From 5451053195ca9635d261068561b41a5851d3ae32 Mon Sep 17 00:00:00 2001 From: Ilya Laktyushin Date: Sun, 22 Dec 2024 17:16:14 +0400 Subject: [PATCH] Various improvements --- .../Telegram-iOS/en.lproj/Localizable.strings | 74 ++ .../Sources/AccountContext.swift | 11 +- .../Sources/ContactSelectionController.swift | 6 +- .../Sources/GalleryController.swift | 5 + .../Sources/AnimatedCountLabelNode.swift | 16 +- .../Sources/AttachmentController.swift | 13 +- submodules/Camera/Sources/CameraOutput.swift | 14 + .../Sources/ChatListSearchItemHeader.swift | 11 +- submodules/ChatListUI/BUILD | 1 + .../Sources/ChatListController.swift | 2 + .../Sources/ChatListSearchListPaneNode.swift | 306 +++++-- .../Sources/ChatListSearchMediaNode.swift | 2 +- .../Sources/ChatListShimmerNode.swift | 2 +- .../Sources/Node/ChatListEmptyInfoItem.swift | 2 +- .../Sources/Node/ChatListItem.swift | 174 +++- .../Sources/Node/ChatListNode.swift | 4 +- .../ChatPresentationInterfaceState.swift | 164 ++-- .../Sources/LottieAnimationComponent.swift | 9 +- .../Sources/ContactListActionItem.swift | 10 +- .../Sources/ContactListNode.swift | 8 +- .../Sources/ContactsPeerItem.swift | 95 ++- ...tControllerExtractedPresentationNode.swift | 42 +- submodules/Display/Source/TextNode.swift | 47 +- submodules/DrawingUI/BUILD | 1 - submodules/FeaturedStickersScreen/BUILD | 2 +- .../Sources/ChatMediaInputTrendingPane.swift | 29 +- .../Sources/FeaturedStickersScreen.swift | 108 ++- .../Sources/StickerPaneSearchGlobaltem.swift | 2 +- .../GalleryUI/Sources/GalleryController.swift | 2 +- .../Sources/ItemListPeerItem.swift | 83 +- .../Sources/ListSectionHeaderNode.swift | 10 +- submodules/MediaPickerUI/BUILD | 2 + .../Sources/AvatarEditorPreviewView.swift | 191 +++++ .../Sources/MediaPickerGridItem.swift | 11 +- .../Sources/MediaPickerScreen.swift | 339 +++++++- .../Sources/PremiumBoostLevelsScreen.swift | 2 +- .../Sources/PremiumBoostScreen.swift | 2 +- .../Sources/PremiumIntroScreen.swift | 2 +- .../Sources/ReplaceBoostScreen.swift | 2 +- .../Sources/PremiumStarsNode.swift | 2 +- .../Sources/Search/SettingsSearchItem.swift | 3 +- submodules/ShareController/BUILD | 1 + .../StickerPackPreviewController.swift | 75 -- .../Sources/StickerResources.swift | 75 ++ .../TelegramNotices/Sources/Notices.swift | 25 + .../Resources/PresentationResourceKey.swift | 1 + .../PresentationResourcesChatList.swift | 18 + .../Components/AvatarBackground/BUILD | 20 + .../Sources/AvatarBackground.swift | 57 ++ .../Components/AvatarEditorScreen/BUILD | 1 + .../Sources/AvatarEditorScreen.swift | 60 +- .../Sources/AvatarPreviewComponent.swift | 1 + .../Sources/BackgroundColorComponent.swift | 1 + .../TelegramUI/Components/CameraScreen/BUILD | 2 +- .../Sources/CameraCodeFrameView.swift | 177 ++++ .../Sources/CameraCodeResultComponent.swift | 235 ++++++ .../CameraScreen/Sources/CameraCollage.swift | 211 ++++- .../CameraScreen/Sources/CameraScreen.swift | 362 ++++++-- .../Sources/CaptureControlsComponent.swift | 76 +- .../Sources/ChatMessageBubbleItemNode.swift | 24 +- .../ChatMessageCommentFooterContentNode.swift | 4 +- .../ChatMessageGiftBubbleContentNode.swift | 6 +- .../Sources/ChatMessageItemCommon.swift | 13 +- .../Components/Chat/ChatSendStarsScreen/BUILD | 1 + .../Sources/ChatSendStarsScreen.swift | 95 +-- .../ChatEntityKeyboardInputNode/BUILD | 3 +- .../Sources/ChatEntityKeyboardInputNode.swift | 30 +- .../StickerPaneSearchContentNode.swift | 28 +- .../ChatTitleView/Sources/ChatTitleView.swift | 26 +- .../Components/CheckComponent/BUILD | 21 + .../Sources/CheckComponent.swift | 105 +++ .../Sources/EmojiStatusComponent.swift | 2 +- .../Sources/EmojiTextAttachmentView.swift | 42 +- .../MediaEditor/Sources/MediaEditor.swift | 80 +- .../Sources/MediaEditorComposer.swift | 28 +- .../Sources/MediaEditorRenderer.swift | 18 +- .../Sources/MediaEditorValues.swift | 24 + .../Sources/MediaEditorVideoExport.swift | 125 ++- .../Sources/UniversalTextureSource.swift | 24 +- .../MediaEditor/Sources/VideoFinishPass.swift | 70 +- .../Sources/MediaEditorDrafts.swift | 6 +- .../Sources/MediaEditorScreen.swift | 290 ++++++- .../Sources/MediaScrubberComponent.swift | 86 +- .../Sources/PeerInfoCoverComponent.swift | 94 ++- .../Components/PeerInfo/PeerInfoScreen/BUILD | 4 + .../ListItems/PeerInfoScreenCommentItem.swift | 35 +- .../Sources/PeerInfoHeaderNode.swift | 79 +- .../Sources/PeerInfoScreen.swift | 788 ++++++------------ .../Sources/PeerInfoScreenAvatarSetup.swift | 607 ++++++++++++++ .../Sources/PeerInfoGiftsPaneNode.swift | 2 +- .../PeerInfo/VerifyAlertController/BUILD | 31 + .../Sources/VerifyAlertController.swift | 489 +++++++++++ .../PremiumPeerShortcutComponent.swift | 21 +- .../PeerNameColorProfilePreviewItem.swift | 2 +- .../Sources/ThemeGridSearchContentNode.swift | 2 +- .../Components/StickerPickerScreen/BUILD | 1 - .../Sources/StickerPickerScreen.swift | 7 +- .../Sources/StoryContainerScreen.swift | 5 +- ...StoryItemSetContainerViewSendMessage.swift | 77 +- .../Contents.json | 2 +- .../ver (2).pdf | Bin 0 -> 1417 bytes .../verifybadge1_16 (1).pdf | 107 --- .../Contents.json | 12 + .../menu.pdf | Bin 0 -> 3809 bytes .../Item List/Ton.imageset/Contents.json | 12 + .../Item List/Ton.imageset/ton_symbol.pdf | Bin 0 -> 3930 bytes .../Mirror.imageset/Contents.json | 21 + .../PhotoEditorMirrorIcon@3x.png | Bin 0 -> 903 bytes .../Rotate.imageset/Contents.json | 21 + .../PhotoEditorRotateIcon@3x.png | Bin 0 -> 539 bytes .../BotVerify.imageset/Contents.json | 12 + .../BotVerify.imageset/verify_30.pdf | Bin 0 -> 7905 bytes .../Premium/Collectible/Contents.json | 9 + .../Tradable.imageset/Contents.json | 12 + .../Collectible/Tradable.imageset/au_30.pdf | Bin 0 -> 4735 bytes .../Transferable.imageset/Contents.json | 12 + .../Transferable.imageset/replace_30.pdf | Bin 0 -> 5687 bytes .../Collectible/Unique.imageset/Contents.json | 12 + .../Unique.imageset/diamond_30.pdf | Bin 0 -> 4776 bytes .../TelegramUI/Sources/ChatController.swift | 26 +- .../ChatInterfaceStateContextMenus.swift | 5 +- .../ChatInterfaceTitlePanelNodes.swift | 11 + .../ChatPinnedMessageTitlePanelNode.swift | 2 +- .../ChatVerifiedPeerTitlePanelNode.swift | 160 ++++ .../TelegramUI/Sources/OpenResolvedUrl.swift | 2 +- .../Sources/SharedAccountContext.swift | 54 +- .../Sources/TelegramRootController.swift | 7 +- .../Sources/OngoingCallContext.swift | 1 + .../Sources/ChatTextInputAttributes.swift | 1 + .../Sources/WebSearchControllerNode.swift | 2 +- submodules/WebUI/BUILD | 1 + .../WebAppEmojiStatusAlertController.swift | 1 + .../Public/Animation/AnimationView.swift | 2 +- 133 files changed, 5419 insertions(+), 1692 deletions(-) create mode 100644 submodules/MediaPickerUI/Sources/AvatarEditorPreviewView.swift create mode 100644 submodules/TelegramUI/Components/AvatarBackground/BUILD create mode 100644 submodules/TelegramUI/Components/AvatarBackground/Sources/AvatarBackground.swift create mode 100644 submodules/TelegramUI/Components/CameraScreen/Sources/CameraCodeFrameView.swift create mode 100644 submodules/TelegramUI/Components/CameraScreen/Sources/CameraCodeResultComponent.swift create mode 100644 submodules/TelegramUI/Components/CheckComponent/BUILD create mode 100644 submodules/TelegramUI/Components/CheckComponent/Sources/CheckComponent.swift create mode 100644 submodules/TelegramUI/Components/PeerInfo/PeerInfoScreen/Sources/PeerInfoScreenAvatarSetup.swift create mode 100644 submodules/TelegramUI/Components/PeerInfo/VerifyAlertController/BUILD create mode 100644 submodules/TelegramUI/Components/PeerInfo/VerifyAlertController/Sources/VerifyAlertController.swift create mode 100644 submodules/TelegramUI/Images.xcassets/Chat List/PeerVerifiedIconBackground.imageset/ver (2).pdf delete mode 100644 submodules/TelegramUI/Images.xcassets/Chat List/PeerVerifiedIconBackground.imageset/verifybadge1_16 (1).pdf create mode 100644 submodules/TelegramUI/Images.xcassets/Item List/HeaderContextDisclosureArrow.imageset/Contents.json create mode 100644 submodules/TelegramUI/Images.xcassets/Item List/HeaderContextDisclosureArrow.imageset/menu.pdf create mode 100644 submodules/TelegramUI/Images.xcassets/Item List/Ton.imageset/Contents.json create mode 100644 submodules/TelegramUI/Images.xcassets/Item List/Ton.imageset/ton_symbol.pdf create mode 100644 submodules/TelegramUI/Images.xcassets/Media Editor/Mirror.imageset/Contents.json create mode 100644 submodules/TelegramUI/Images.xcassets/Media Editor/Mirror.imageset/PhotoEditorMirrorIcon@3x.png create mode 100644 submodules/TelegramUI/Images.xcassets/Media Editor/Rotate.imageset/Contents.json create mode 100644 submodules/TelegramUI/Images.xcassets/Media Editor/Rotate.imageset/PhotoEditorRotateIcon@3x.png create mode 100644 submodules/TelegramUI/Images.xcassets/Peer Info/BotVerify.imageset/Contents.json create mode 100644 submodules/TelegramUI/Images.xcassets/Peer Info/BotVerify.imageset/verify_30.pdf create mode 100644 submodules/TelegramUI/Images.xcassets/Premium/Collectible/Contents.json create mode 100644 submodules/TelegramUI/Images.xcassets/Premium/Collectible/Tradable.imageset/Contents.json create mode 100644 submodules/TelegramUI/Images.xcassets/Premium/Collectible/Tradable.imageset/au_30.pdf create mode 100644 submodules/TelegramUI/Images.xcassets/Premium/Collectible/Transferable.imageset/Contents.json create mode 100644 submodules/TelegramUI/Images.xcassets/Premium/Collectible/Transferable.imageset/replace_30.pdf create mode 100644 submodules/TelegramUI/Images.xcassets/Premium/Collectible/Unique.imageset/Contents.json create mode 100644 submodules/TelegramUI/Images.xcassets/Premium/Collectible/Unique.imageset/diamond_30.pdf create mode 100644 submodules/TelegramUI/Sources/ChatVerifiedPeerTitlePanelNode.swift diff --git a/Telegram/Telegram-iOS/en.lproj/Localizable.strings b/Telegram/Telegram-iOS/en.lproj/Localizable.strings index ffc39f986f..406c5d0d9a 100644 --- a/Telegram/Telegram-iOS/en.lproj/Localizable.strings +++ b/Telegram/Telegram-iOS/en.lproj/Localizable.strings @@ -13447,3 +13447,77 @@ Sorry for the inconvenience."; "StarsTransaction.StarRefReason.Referred" = "Referred User"; "Gallery.SaveToGallery.cached" = "cached"; + +"ChatList.Search.Messages.AllChats" = "From All Chats"; +"ChatList.Search.Messages.PrivateChats" = "From Private Chats"; +"ChatList.Search.Messages.GroupChats" = "From Group Chats"; +"ChatList.Search.Messages.Channels" = "From Channels"; + +"ChatList.Search.Messages.Menu.AllChats" = "All Chats"; +"ChatList.Search.Messages.Menu.PrivateChats" = "Private Chats"; +"ChatList.Search.Messages.Menu.GroupChats" = "Group Chats"; +"ChatList.Search.Messages.Menu.Channels" = "Channels"; + +"MediaEditor.Timeline" = "Timeline"; + +"Conversation.ContextMenuSendGiftTo" = "Send Gift to %@"; +"Conversation.ContextMenuSendAnotherGift" = "Send Another Gift"; + +"Gift.View.Status" = "Status"; +"Gift.View.Status.NonUnique" = "Non-Unique"; +"Gift.View.Status.Upgrade" = "upgrade"; +"Gift.View.DisplayedInfoHide" = "The gift is visible on your Page. [Hide >]()"; + +"Gift.Upgrade.Title" = "Upgrade Gift"; +"Gift.Upgrade.Description" = "Turn your gift into a unique collectible that you can transfer or auction."; +"Gift.Upgrade.Unique.Title" = "Unique"; +"Gift.Upgrade.Unique.Description" = "Get a unique number, model backdrop and and symbol for your gift."; +"Gift.Upgrade.Transferable.Title" = "Transferable"; +"Gift.Upgrade.Transferable.Description" = "Send your upgraded gift to any of your friends on Telegram."; +"Gift.Upgrade.Tradable.Title" = "Tradable"; +"Gift.Upgrade.Tradable.Description" = "Sell or auction your gift on third-party NFT marketplaces."; +"Gift.Upgrade.Soon" = "SOON"; +"Gift.Upgrade.AddName" = "Add sender's name"; +"Gift.Upgrade.AddNameAndComment" = "Add sender's name and comment"; +"Gift.Upgrade.Upgrade" = "Upgrade"; + +"Gift.Upgrade.Succeed.Title" = "The gift is now collectible"; +"Gift.Upgrade.Succeed.Text" = "You have received a unique number, model, backdrop and symbol for your gift."; + +"Gift.Unique.Collectible" = "Collectible"; +"Gift.Unique.Owner" = "Owner"; +"Gift.Unique.Transfer" = "transfer"; +"Gift.Unique.Model" = "Model"; +"Gift.Unique.Backdrop" = "Backdrop"; +"Gift.Unique.Symbol" = "Symbol"; +"Gift.Unique.Availability" = "Availability"; +"Gift.Unique.Issued" = "%@ issued"; + +"Gift.Unique.AttributeDescription" = "Only %@ of such collectibles have this attribute."; + +"Gift.Transfer.Title" = "Transfer"; +"Gift.Transfer.SendViaBlockchain" = "Send via Blockchain"; +"Gift.Transfer.SendUnlocks" = "unlocks in %@"; +"Gift.Transfer.SendUnlocks.Days_1" = "%@ day"; +"Gift.Transfer.SendUnlocks.Days_any" = "%@ days"; + +"Gift.Transfer.UnlockPending.Title" = "Unlocking In Progress"; +"Gift.Transfer.UnlockPending.Text" = "In %@, you'll be able to send this collectible to any TON blockchain address outside Telegram for sale or auction."; +"Gift.Transfer.UnlockPending.Text.Days_1" = "%@ day"; +"Gift.Transfer.UnlockPending.Text.Days_any" = "%@ days"; + +"Gift.Transfer.UpdateRequired.Title" = "Update Required"; +"Gift.Transfer.UpdateRequired.Text" = "Please update your Telegram application to the latest version."; + +"Gift.View.KeepUpgradeOrConvertDescription" = "You can keep this gift, upgrade it, or sell it for %@. [More About Stars >]()"; + + +"PeerInfo.VerificationInfo.Custom.User" = "This user is verified by %@."; +"PeerInfo.VerificationInfo.Custom.Bot" = "This bot is verified by %@."; +"PeerInfo.VerificationInfo.Custom.Channel" = "This channel is verified by %@."; +"PeerInfo.VerificationInfo.Custom.Group" = "This group is verified by %@."; + +"PeerInfo.VerificationInfo.Bot" = "This bot is verified as official by the representatives of Telegram."; +"PeerInfo.VerificationInfo.Channel" = "This channel is verified as official by the representatives of Telegram."; +"PeerInfo.VerificationInfo.Group" = "This group is verified as official by the representatives of Telegram."; +"PeerInfo.VerificationInfo.URL" = "https://telegram.org/verify"; diff --git a/submodules/AccountContext/Sources/AccountContext.swift b/submodules/AccountContext/Sources/AccountContext.swift index 4823335c21..4688286bcb 100644 --- a/submodules/AccountContext/Sources/AccountContext.swift +++ b/submodules/AccountContext/Sources/AccountContext.swift @@ -1050,12 +1050,12 @@ public protocol SharedAccountContext: AnyObject { func makePremiumLimitController(context: AccountContext, subject: PremiumLimitSubject, count: Int32, forceDark: Bool, cancel: @escaping () -> Void, action: @escaping () -> Bool) -> ViewController func makeStarsGiftController(context: AccountContext, birthdays: [EnginePeer.Id: TelegramBirthday]?, completion: @escaping (([EnginePeer.Id]) -> Void)) -> ViewController - func makePremiumGiftController(context: AccountContext, source: PremiumGiftSource, completion: (([EnginePeer.Id]) -> Void)?) -> ViewController + func makePremiumGiftController(context: AccountContext, source: PremiumGiftSource, transfer: Bool, completion: (([EnginePeer.Id]) -> Void)?) -> ViewController func makeGiftOptionsController(context: AccountContext, peerId: EnginePeer.Id, premiumOptions: [CachedPremiumGiftOption], hasBirthday: Bool) -> ViewController func makePremiumPrivacyControllerController(context: AccountContext, subject: PremiumPrivacySubject, peerId: EnginePeer.Id) -> ViewController func makePremiumBoostLevelsController(context: AccountContext, peerId: EnginePeer.Id, subject: BoostSubject, boostStatus: ChannelBoostStatus, myBoostStatus: MyBoostStatus, forceDark: Bool, openStats: (() -> Void)?) -> ViewController - func makeStickerPackScreen(context: AccountContext, updatedPresentationData: (initial: PresentationData, signal: Signal)?, mainStickerPack: StickerPackReference, stickerPacks: [StickerPackReference], loadedStickerPacks: [LoadedStickerPack], isEditing: Bool, expandIfNeeded: Bool, parentNavigationController: NavigationController?, sendSticker: ((FileMediaReference, UIView, CGRect) -> Bool)?, actionPerformed: ((Bool) -> Void)?) -> ViewController + func makeStickerPackScreen(context: AccountContext, updatedPresentationData: (initial: PresentationData, signal: Signal)?, mainStickerPack: StickerPackReference, stickerPacks: [StickerPackReference], loadedStickerPacks: [LoadedStickerPack], actionTitle: String?, isEditing: Bool, expandIfNeeded: Bool, parentNavigationController: NavigationController?, sendSticker: ((FileMediaReference, UIView, CGRect) -> Bool)?, actionPerformed: ((Bool) -> Void)?) -> ViewController func makeMediaPickerScreen(context: AccountContext, hasSearch: Bool, completion: @escaping (Any) -> Void) -> ViewController @@ -1066,7 +1066,10 @@ public protocol SharedAccountContext: AnyObject { func makeStickerEditorScreen(context: AccountContext, source: Any?, intro: Bool, transitionArguments: (UIView, CGRect, UIImage?)?, completion: @escaping (TelegramMediaFile, [String], @escaping () -> Void) -> Void, cancelled: @escaping () -> Void) -> ViewController func makeStickerMediaPickerScreen(context: AccountContext, getSourceRect: @escaping () -> CGRect?, completion: @escaping (Any?, UIView?, CGRect, UIImage?, Bool, @escaping (Bool?) -> (UIView, CGRect)?, @escaping () -> Void) -> Void, dismissed: @escaping () -> Void) -> ViewController - func makeStoryMediaPickerScreen(context: AccountContext, isDark: Bool, forCollage: Bool, getSourceRect: @escaping () -> CGRect, completion: @escaping (Any, UIView, CGRect, UIImage?, @escaping (Bool?) -> (UIView, CGRect)?, @escaping () -> Void) -> Void, dismissed: @escaping () -> Void, groupsPresented: @escaping () -> Void) -> ViewController + + func makeAvatarMediaPickerScreen(context: AccountContext, getSourceRect: @escaping () -> CGRect?, canDelete: Bool, performDelete: @escaping () -> Void, completion: @escaping (Any?, UIView?, CGRect, UIImage?, Bool, @escaping (Bool?) -> (UIView, CGRect)?, @escaping () -> Void) -> Void, dismissed: @escaping () -> Void) -> ViewController + + func makeStoryMediaPickerScreen(context: AccountContext, isDark: Bool, forCollage: Bool, selectionLimit: Int?, getSourceRect: @escaping () -> CGRect, completion: @escaping (Any, UIView, CGRect, UIImage?, @escaping (Bool?) -> (UIView, CGRect)?, @escaping () -> Void) -> Void, multipleCompletion: @escaping ([Any]) -> Void, dismissed: @escaping () -> Void, groupsPresented: @escaping () -> Void) -> ViewController func makeStickerPickerScreen(context: AccountContext, inputData: Promise, completion: @escaping (FileMediaReference) -> Void) -> ViewController @@ -1108,6 +1111,8 @@ public protocol SharedAccountContext: AnyObject { func makeAffiliateProgramJoinScreen(context: AccountContext, sourcePeer: EnginePeer, commissionPermille: Int32, programDuration: Int32?, revenuePerUser: Double, mode: JoinAffiliateProgramScreenMode) -> ViewController + func makeGalleryController(context: AccountContext, source: GalleryControllerItemSource, streamSingleVideo: Bool, isPreview: Bool) -> ViewController + func makeDebugSettingsController(context: AccountContext?) -> ViewController? func navigateToCurrentCall() diff --git a/submodules/AccountContext/Sources/ContactSelectionController.swift b/submodules/AccountContext/Sources/ContactSelectionController.swift index 243145a8b7..2d6269a073 100644 --- a/submodules/AccountContext/Sources/ContactSelectionController.swift +++ b/submodules/AccountContext/Sources/ContactSelectionController.swift @@ -21,19 +21,21 @@ public enum ContactSelectionControllerMode { public struct ContactListAdditionalOption: Equatable { public let title: String + public let subtitle: String? public let icon: ContactListActionItemIcon public let action: () -> Void public let clearHighlightAutomatically: Bool - public init(title: String, icon: ContactListActionItemIcon, action: @escaping () -> Void, clearHighlightAutomatically: Bool = false) { + public init(title: String, subtitle: String? = nil, icon: ContactListActionItemIcon, action: @escaping () -> Void, clearHighlightAutomatically: Bool = false) { self.title = title + self.subtitle = subtitle self.icon = icon self.action = action self.clearHighlightAutomatically = clearHighlightAutomatically } public static func ==(lhs: ContactListAdditionalOption, rhs: ContactListAdditionalOption) -> Bool { - return lhs.title == rhs.title && lhs.icon == rhs.icon + return lhs.title == rhs.title && lhs.subtitle == rhs.subtitle && lhs.icon == rhs.icon } } diff --git a/submodules/AccountContext/Sources/GalleryController.swift b/submodules/AccountContext/Sources/GalleryController.swift index 3342beb0a8..f51b02e584 100644 --- a/submodules/AccountContext/Sources/GalleryController.swift +++ b/submodules/AccountContext/Sources/GalleryController.swift @@ -1,5 +1,6 @@ import Foundation import UIKit +import Display import Postbox import SwiftSignalKit import TelegramCore @@ -50,6 +51,10 @@ public final class GalleryControllerActionInteraction { } } +public protocol GalleryControllerProtocol: ViewController { + +} + public protocol StickerPackScreen { } diff --git a/submodules/AnimatedCountLabelNode/Sources/AnimatedCountLabelNode.swift b/submodules/AnimatedCountLabelNode/Sources/AnimatedCountLabelNode.swift index ae9341d834..7240457859 100644 --- a/submodules/AnimatedCountLabelNode/Sources/AnimatedCountLabelNode.swift +++ b/submodules/AnimatedCountLabelNode/Sources/AnimatedCountLabelNode.swift @@ -85,7 +85,7 @@ public class AnimatedCountLabelNode: ASDisplayNode { super.init() } - public func asyncLayout() -> (CGSize, [Segment]) -> (Layout, (Bool) -> Void) { + public func asyncLayout() -> (CGSize, UIEdgeInsets, [Segment]) -> (Layout, (Bool) -> Void) { var segmentLayouts: [ResolvedSegment.Key: (TextNodeLayoutArguments) -> (TextNodeLayout, () -> TextNode)] = [:] let wasEmpty = self.resolvedSegments.isEmpty for (segmentKey, segmentAndTextNode) in self.resolvedSegments { @@ -94,7 +94,7 @@ public class AnimatedCountLabelNode: ASDisplayNode { let reverseAnimationDirection = self.reverseAnimationDirection let alwaysOneDirection = self.alwaysOneDirection - return { [weak self] size, initialSegments in + return { [weak self] size, insets, initialSegments in var segments: [ResolvedSegment] = [] loop: for segment in initialSegments { switch segment { @@ -165,7 +165,7 @@ public class AnimatedCountLabelNode: ASDisplayNode { transition = .immediate } - var currentOffset = CGPoint() + var currentOffset = CGPoint(x: insets.left, y: 0.0) for segment in segments { var animation: (CGFloat, Double)? if let (currentSegment, currentTextNode) = strongSelf.resolvedSegments[segment.key] { @@ -254,12 +254,14 @@ public final class ImmediateAnimatedCountLabelNode: AnimatedCountLabelNode { public var segments: [AnimatedCountLabelNode.Segment] = [] private var constrainedSize: CGSize? + private var insets: UIEdgeInsets? - public func updateLayout(size: CGSize, animated: Bool) -> CGSize { + public func updateLayout(size: CGSize, insets: UIEdgeInsets = .zero, animated: Bool) -> CGSize { self.constrainedSize = size + self.insets = insets let makeLayout = self.asyncLayout() - let (layout, apply) = makeLayout(size, self.segments) + let (layout, apply) = makeLayout(size, insets, self.segments) let _ = apply(animated) return layout.size } @@ -282,8 +284,8 @@ public final class ImmediateAnimatedCountLabelNode: AnimatedCountLabelNode { } } } - if let constrainedSize = self.constrainedSize { - let _ = node.updateLayout(size: constrainedSize, animated: false) + if let constrainedSize = self.constrainedSize, let insets = self.insets { + let _ = node.updateLayout(size: constrainedSize, insets: insets, animated: false) } return node } diff --git a/submodules/AttachmentUI/Sources/AttachmentController.swift b/submodules/AttachmentUI/Sources/AttachmentController.swift index b5b09d3777..bd0202fb71 100644 --- a/submodules/AttachmentUI/Sources/AttachmentController.swift +++ b/submodules/AttachmentUI/Sources/AttachmentController.swift @@ -1208,7 +1208,18 @@ public class AttachmentController: ViewController, MinimizableController { public var shouldMinimizeOnSwipe: ((AttachmentButtonType?) -> Bool)? - public init(context: AccountContext, updatedPresentationData: (initial: PresentationData, signal: Signal)? = nil, chatLocation: ChatLocation?, isScheduledMessages: Bool = false, buttons: [AttachmentButtonType], initialButton: AttachmentButtonType = .gallery, fromMenu: Bool = false, hasTextInput: Bool = true, isFullSize: Bool = false, makeEntityInputView: @escaping () -> AttachmentTextInputPanelInputView? = { return nil}) { + public init( + context: AccountContext, + updatedPresentationData: (initial: PresentationData, signal: Signal)? = nil, + chatLocation: ChatLocation?, + isScheduledMessages: Bool = false, + buttons: [AttachmentButtonType], + initialButton: AttachmentButtonType = .gallery, + fromMenu: Bool = false, + hasTextInput: Bool = true, + isFullSize: Bool = false, + makeEntityInputView: @escaping () -> AttachmentTextInputPanelInputView? = { return nil }) + { self.context = context self.updatedPresentationData = updatedPresentationData self.chatLocation = chatLocation diff --git a/submodules/Camera/Sources/CameraOutput.swift b/submodules/Camera/Sources/CameraOutput.swift index d1fbadf7f2..df644f0da2 100644 --- a/submodules/Camera/Sources/CameraOutput.swift +++ b/submodules/Camera/Sources/CameraOutput.swift @@ -64,6 +64,20 @@ public struct CameraCode: Equatable { return CGRect.null } + public var rotation: CGFloat { + guard self.corners.count == 4 else { + return 0.0 + } + + let topLeft = self.corners[1] + let topRight = self.corners[2] + + let dx = topRight.x - topLeft.x + let dy = topRight.y - topLeft.y + + return atan2(dy, dx) - .pi / 2.0 + } + public static func == (lhs: CameraCode, rhs: CameraCode) -> Bool { if lhs.type != rhs.type { return false diff --git a/submodules/ChatListSearchItemHeader/Sources/ChatListSearchItemHeader.swift b/submodules/ChatListSearchItemHeader/Sources/ChatListSearchItemHeader.swift index 3a51304be1..463f70fe57 100644 --- a/submodules/ChatListSearchItemHeader/Sources/ChatListSearchItemHeader.swift +++ b/submodules/ChatListSearchItemHeader/Sources/ChatListSearchItemHeader.swift @@ -1,5 +1,6 @@ import Foundation import UIKit +import AsyncDisplayKit import Display import TelegramPresentationData import ListSectionHeaderNode @@ -209,11 +210,11 @@ public final class ChatListSearchItemHeader: ListViewItemHeader { public let theme: PresentationTheme public let strings: PresentationStrings public let actionTitle: String? - public let action: (() -> Void)? + public let action: ((ASDisplayNode) -> Void)? public let height: CGFloat = 28.0 - public init(type: ChatListSearchItemHeaderType, theme: PresentationTheme, strings: PresentationStrings, actionTitle: String? = nil, action: (() -> Void)? = nil) { + public init(type: ChatListSearchItemHeaderType, theme: PresentationTheme, strings: PresentationStrings, actionTitle: String? = nil, action: ((ASDisplayNode) -> Void)? = nil) { self.type = type self.id = ListViewItemNode.HeaderId(space: 0, id: Int64(self.type.id.hashValue)) self.theme = theme @@ -244,13 +245,13 @@ public final class ChatListSearchItemHeaderNode: ListViewItemHeaderNode { private var theme: PresentationTheme private var strings: PresentationStrings private var actionTitle: String? - private var action: (() -> Void)? + private var action: ((ASDisplayNode) -> Void)? private var validLayout: (size: CGSize, leftInset: CGFloat, rightInset: CGFloat)? private let sectionHeaderNode: ListSectionHeaderNode - public init(type: ChatListSearchItemHeaderType, theme: PresentationTheme, strings: PresentationStrings, actionTitle: String?, action: (() -> Void)?) { + public init(type: ChatListSearchItemHeaderType, theme: PresentationTheme, strings: PresentationStrings, actionTitle: String?, action: ((ASDisplayNode) -> Void)?) { self.type = type self.theme = theme self.strings = strings @@ -273,7 +274,7 @@ public final class ChatListSearchItemHeaderNode: ListViewItemHeaderNode { self.sectionHeaderNode.updateTheme(theme: theme) } - public func update(type: ChatListSearchItemHeaderType, actionTitle: String?, action: (() -> Void)?) { + public func update(type: ChatListSearchItemHeaderType, actionTitle: String?, action: ((ASDisplayNode) -> Void)?) { self.actionTitle = actionTitle self.action = action diff --git a/submodules/ChatListUI/BUILD b/submodules/ChatListUI/BUILD index 1eaca712b2..b26a832fbb 100644 --- a/submodules/ChatListUI/BUILD +++ b/submodules/ChatListUI/BUILD @@ -109,6 +109,7 @@ swift_library( "//submodules/TelegramUI/Components/ChatEntityKeyboardInputNode", "//submodules/ComposePollUI", "//submodules/ChatPresentationInterfaceState", + "//submodules/ShimmerEffect:ShimmerEffect", ], visibility = [ "//visibility:public", diff --git a/submodules/ChatListUI/Sources/ChatListController.swift b/submodules/ChatListUI/Sources/ChatListController.swift index 809952921f..68cc968256 100644 --- a/submodules/ChatListUI/Sources/ChatListController.swift +++ b/submodules/ChatListUI/Sources/ChatListController.swift @@ -1395,6 +1395,8 @@ public class ChatListControllerImpl: TelegramBaseController, ChatListController } switch item.content { + case .loading: + break case let .groupReference(groupReference): let chatListController = ChatListControllerImpl(context: strongSelf.context, location: .chatList(groupId: groupReference.groupId), controlsHistoryPreload: false, hideNetworkActivityStatus: true, previewing: true, enableDebugActions: false) chatListController.navigationPresentation = .master diff --git a/submodules/ChatListUI/Sources/ChatListSearchListPaneNode.swift b/submodules/ChatListUI/Sources/ChatListSearchListPaneNode.swift index 0000544baa..50f850e96e 100644 --- a/submodules/ChatListUI/Sources/ChatListSearchListPaneNode.swift +++ b/submodules/ChatListUI/Sources/ChatListSearchListPaneNode.swift @@ -261,7 +261,7 @@ private enum ChatListRecentEntry: Comparable, Identifiable { header = ChatListSearchItemHeader(type: .text(presentationData.strings.ChatList_Search_SectionRecommendedChannels, 1), theme: theme, strings: strings) } else { if let isChannelsTabExpanded { - header = ChatListSearchItemHeader(type: .text(presentationData.strings.ChatList_Search_SectionLocalChannels, 0), theme: theme, strings: strings, actionTitle: isChannelsTabExpanded ? presentationData.strings.ChatList_Search_SectionActionShowLess : presentationData.strings.ChatList_Search_SectionActionShowMore, action: { + header = ChatListSearchItemHeader(type: .text(presentationData.strings.ChatList_Search_SectionLocalChannels, 0), theme: theme, strings: strings, actionTitle: isChannelsTabExpanded ? presentationData.strings.ChatList_Search_SectionActionShowLess : presentationData.strings.ChatList_Search_SectionActionShowMore, action: { _ in toggleChannelsTabExpanded() }) } else { @@ -273,7 +273,7 @@ private enum ChatListRecentEntry: Comparable, Identifiable { header = ChatListSearchItemHeader(type: .text(presentationData.strings.ChatList_Search_SectionPopularApps, 1), theme: theme, strings: strings) } else { if let isChannelsTabExpanded { - header = ChatListSearchItemHeader(type: .text(presentationData.strings.ChatList_Search_SectionRecentApps, 0), theme: theme, strings: strings, actionTitle: isChannelsTabExpanded ? presentationData.strings.ChatList_Search_SectionActionShowLess : presentationData.strings.ChatList_Search_SectionActionShowMore, action: { + header = ChatListSearchItemHeader(type: .text(presentationData.strings.ChatList_Search_SectionRecentApps, 0), theme: theme, strings: strings, actionTitle: isChannelsTabExpanded ? presentationData.strings.ChatList_Search_SectionActionShowLess : presentationData.strings.ChatList_Search_SectionActionShowMore, action: { _ in toggleChannelsTabExpanded() }) } else { @@ -281,7 +281,7 @@ private enum ChatListRecentEntry: Comparable, Identifiable { } } } else { - header = ChatListSearchItemHeader(type: .recentPeers, theme: theme, strings: strings, actionTitle: strings.WebSearch_RecentSectionClear, action: { + header = ChatListSearchItemHeader(type: .recentPeers, theme: theme, strings: strings, actionTitle: strings.WebSearch_RecentSectionClear, action: { _ in clearRecentlySearchedPeers() }) } @@ -379,6 +379,7 @@ public enum ChatListSearchEntryStableId: Hashable { case localPeerId(EnginePeer.Id) case globalPeerId(EnginePeer.Id) case messageId(EngineMessage.Id, ChatListSearchEntry.MessageSection) + case messagePlaceholder(Int32) case addContact } @@ -439,7 +440,8 @@ public enum ChatListSearchEntry: Comparable, Identifiable { case recentlySearchedPeer(EnginePeer, EnginePeer?, (Int32, Bool)?, Int, PresentationTheme, PresentationStrings, PresentationPersonNameOrder, PresentationPersonNameOrder, PeerStoryStats?, Bool) case localPeer(EnginePeer, EnginePeer?, (Int32, Bool)?, Int, PresentationTheme, PresentationStrings, PresentationPersonNameOrder, PresentationPersonNameOrder, ChatListSearchSectionExpandType, PeerStoryStats?, Bool) case globalPeer(FoundPeer, (Int32, Bool)?, Int, PresentationTheme, PresentationStrings, PresentationPersonNameOrder, PresentationPersonNameOrder, ChatListSearchSectionExpandType, PeerStoryStats?, Bool, String?) - case message(EngineMessage, EngineRenderedPeer, EnginePeerReadCounters?, EngineMessageHistoryThread.Info?, ChatListPresentationData, Int32, Bool?, Bool, MessageOrderingKey, (id: String, size: Int64, isFirstInList: Bool)?, MessageSection, Bool, PeerStoryStats?, Bool) + case message(EngineMessage, EngineRenderedPeer, EnginePeerReadCounters?, EngineMessageHistoryThread.Info?, ChatListPresentationData, Int32, Bool?, Bool, MessageOrderingKey, (id: String, size: Int64, isFirstInList: Bool)?, MessageSection, Bool, PeerStoryStats?, Bool, TelegramSearchPeersScope) + case messagePlaceholder(Int32, ChatListPresentationData, TelegramSearchPeersScope) case addContact(String, PresentationTheme, PresentationStrings) public var stableId: ChatListSearchEntryStableId { @@ -452,8 +454,10 @@ public enum ChatListSearchEntry: Comparable, Identifiable { return .localPeerId(peer.id) case let .globalPeer(peer, _, _, _, _, _, _, _, _, _, _): return .globalPeerId(peer.peer.id) - case let .message(message, _, _, _, _, _, _, _, _, _, section, _, _, _): + case let .message(message, _, _, _, _, _, _, _, _, _, section, _, _, _, _): return .messageId(message.id, section) + case let .messagePlaceholder(index, _, _): + return .messagePlaceholder(index) case .addContact: return .addContact } @@ -485,8 +489,8 @@ public enum ChatListSearchEntry: Comparable, Identifiable { } else { return false } - case let .message(lhsMessage, lhsPeer, lhsCombinedPeerReadState, lhsThreadInfo, lhsPresentationData, lhsTotalCount, lhsSelected, lhsDisplayCustomHeader, lhsKey, lhsResourceId, lhsSection, lhsAllPaused, lhsStoryStats, lhsRequiresPremiumForMessaging): - if case let .message(rhsMessage, rhsPeer, rhsCombinedPeerReadState, rhsThreadInfo, rhsPresentationData, rhsTotalCount, rhsSelected, rhsDisplayCustomHeader, rhsKey, rhsResourceId, rhsSection, rhsAllPaused, rhsStoryStats, rhsRequiresPremiumForMessaging) = rhs { + case let .message(lhsMessage, lhsPeer, lhsCombinedPeerReadState, lhsThreadInfo, lhsPresentationData, lhsTotalCount, lhsSelected, lhsDisplayCustomHeader, lhsKey, lhsResourceId, lhsSection, lhsAllPaused, lhsStoryStats, lhsRequiresPremiumForMessaging, lhsSearchScope): + if case let .message(rhsMessage, rhsPeer, rhsCombinedPeerReadState, rhsThreadInfo, rhsPresentationData, rhsTotalCount, rhsSelected, rhsDisplayCustomHeader, rhsKey, rhsResourceId, rhsSection, rhsAllPaused, rhsStoryStats, rhsRequiresPremiumForMessaging, rhsSearchScope) = rhs { if lhsMessage.id != rhsMessage.id { return false } @@ -535,6 +539,24 @@ public enum ChatListSearchEntry: Comparable, Identifiable { if lhsRequiresPremiumForMessaging != rhsRequiresPremiumForMessaging { return false } + if lhsSearchScope != rhsSearchScope { + return false + } + return true + } else { + return false + } + case let .messagePlaceholder(lhsIndex, lhsPresentationData, lhsSearchScope): + if case let .messagePlaceholder(rhsIndex, rhsPresentationData, rhsSearchScope) = rhs { + if lhsIndex != rhsIndex { + return false + } + if lhsPresentationData !== rhsPresentationData { + return false + } + if lhsSearchScope != rhsSearchScope { + return false + } return true } else { return false @@ -579,7 +601,7 @@ public enum ChatListSearchEntry: Comparable, Identifiable { return false case let .localPeer(_, _, _, rhsIndex, _, _, _, _, _, _, _): return lhsIndex <= rhsIndex - case .globalPeer, .message, .addContact: + case .globalPeer, .message, .messagePlaceholder, .addContact: return true } case let .globalPeer(_, _, lhsIndex, _, _, _, _, _, _, _, _): @@ -588,12 +610,22 @@ public enum ChatListSearchEntry: Comparable, Identifiable { return false case let .globalPeer(_, _, rhsIndex, _, _, _, _, _, _, _, _): return lhsIndex <= rhsIndex - case .message, .addContact: + case .message, .messagePlaceholder, .addContact: return true } - case let .message(_, _, _, _, _, _, _, _, lhsKey, _, _, _, _, _): - if case let .message(_, _, _, _, _, _, _, _, rhsKey, _, _, _, _, _) = rhs { + case let .message(_, _, _, _, _, _, _, _, lhsKey, _, _, _, _, _, _): + if case let .message(_, _, _, _, _, _, _, _, rhsKey, _, _, _, _, _, _) = rhs { return lhsKey < rhsKey + } else if case .messagePlaceholder = rhs { + return true + } else if case .addContact = rhs { + return true + } else { + return false + } + case let .messagePlaceholder(lhsIndex, _, _): + if case let .messagePlaceholder(rhsIndex, _, _) = rhs { + return lhsIndex < rhsIndex } else if case .addContact = rhs { return true } else { @@ -604,7 +636,30 @@ public enum ChatListSearchEntry: Comparable, Identifiable { } } - public func item(context: AccountContext, presentationData: PresentationData, enableHeaders: Bool, filter: ChatListNodePeersFilter, requestPeerType: [ReplyMarkupButtonRequestPeerType]?, location: ChatListControllerLocation, key: ChatListSearchPaneKey, tagMask: EngineMessage.Tags?, interaction: ChatListNodeInteraction, listInteraction: ListMessageItemInteraction, peerContextAction: ((EnginePeer, ChatListSearchContextActionSource, ASDisplayNode, ContextGesture?, CGPoint?) -> Void)?, toggleExpandLocalResults: @escaping () -> Void, toggleExpandGlobalResults: @escaping () -> Void, searchPeer: @escaping (EnginePeer) -> Void, searchQuery: String?, searchOptions: ChatListSearchOptions?, messageContextAction: ((EngineMessage, ASDisplayNode?, CGRect?, UIGestureRecognizer?, ChatListSearchPaneKey, (id: String, size: Int64, isFirstInList: Bool)?) -> Void)?, openClearRecentlyDownloaded: @escaping () -> Void, toggleAllPaused: @escaping () -> Void, openStories: @escaping (EnginePeer.Id, AvatarNode) -> Void, openPublicPosts: @escaping () -> Void) -> ListViewItem { + public func item( + context: AccountContext, + presentationData: PresentationData, + enableHeaders: Bool, + filter: ChatListNodePeersFilter, + requestPeerType: [ReplyMarkupButtonRequestPeerType]?, + location: ChatListControllerLocation, + key: ChatListSearchPaneKey, + tagMask: EngineMessage.Tags?, + interaction: ChatListNodeInteraction, + listInteraction: ListMessageItemInteraction, + peerContextAction: ((EnginePeer, ChatListSearchContextActionSource, ASDisplayNode, ContextGesture?, CGPoint?) -> Void)?, + toggleExpandLocalResults: @escaping () -> Void, + toggleExpandGlobalResults: @escaping () -> Void, + searchPeer: @escaping (EnginePeer) -> Void, + searchQuery: String?, + searchOptions: ChatListSearchOptions?, + messageContextAction: ((EngineMessage, ASDisplayNode?, CGRect?, UIGestureRecognizer?, ChatListSearchPaneKey, (id: String, size: Int64, isFirstInList: Bool)?) -> Void)?, + openClearRecentlyDownloaded: @escaping () -> Void, + toggleAllPaused: @escaping () -> Void, + openStories: @escaping (EnginePeer.Id, AvatarNode) -> Void, + openPublicPosts: @escaping () -> Void, + openMessagesFilter: @escaping (ASDisplayNode) -> Void + ) -> ListViewItem { switch self { case let .topic(peer, threadInfo, _, theme, strings, expandType): let actionTitle: String? @@ -616,7 +671,7 @@ public enum ChatListSearchEntry: Comparable, Identifiable { case .collapse: actionTitle = strings.ChatList_Search_ShowLess } - let header = ChatListSearchItemHeader(type: .topics, theme: theme, strings: strings, actionTitle: actionTitle, action: actionTitle == nil ? nil : { + let header = ChatListSearchItemHeader(type: .topics, theme: theme, strings: strings, actionTitle: actionTitle, action: actionTitle == nil ? nil : { _ in toggleExpandGlobalResults() }) @@ -803,7 +858,7 @@ public enum ChatListSearchEntry: Comparable, Identifiable { } } } - header = ChatListSearchItemHeader(type: headerType, theme: theme, strings: strings, actionTitle: actionTitle, action: actionTitle == nil ? nil : { + header = ChatListSearchItemHeader(type: headerType, theme: theme, strings: strings, actionTitle: actionTitle, action: actionTitle == nil ? nil : { _ in toggleExpandLocalResults() }) } @@ -907,7 +962,7 @@ public enum ChatListSearchEntry: Comparable, Identifiable { case .collapse: actionTitle = strings.ChatList_Search_ShowLess } - header = ChatListSearchItemHeader(type: .globalPeers, theme: theme, strings: strings, actionTitle: actionTitle, action: actionTitle == nil ? nil : { + header = ChatListSearchItemHeader(type: .globalPeers, theme: theme, strings: strings, actionTitle: actionTitle, action: actionTitle == nil ? nil : { _ in toggleExpandGlobalResults() }) } @@ -935,21 +990,21 @@ public enum ChatListSearchEntry: Comparable, Identifiable { openStories(peer.id, sourceNode.avatarNode) } }) - case let .message(message, peer, readState, threadInfo, presentationData, _, selected, displayCustomHeader, orderingKey, _, section, allPaused, storyStats, requiresPremiumForMessaging): + case let .message(message, peer, readState, threadInfo, presentationData, _, selected, displayCustomHeader, orderingKey, _, section, allPaused, storyStats, requiresPremiumForMessaging, searchScope): let header: ChatListSearchItemHeader switch orderingKey { case .downloading: if allPaused { - header = ChatListSearchItemHeader(type: .downloading, theme: presentationData.theme, strings: presentationData.strings, actionTitle: presentationData.strings.DownloadList_ResumeAll, action: { + header = ChatListSearchItemHeader(type: .downloading, theme: presentationData.theme, strings: presentationData.strings, actionTitle: presentationData.strings.DownloadList_ResumeAll, action: { _ in toggleAllPaused() }) } else { - header = ChatListSearchItemHeader(type: .downloading, theme: presentationData.theme, strings: presentationData.strings, actionTitle: presentationData.strings.DownloadList_PauseAll, action: { + header = ChatListSearchItemHeader(type: .downloading, theme: presentationData.theme, strings: presentationData.strings, actionTitle: presentationData.strings.DownloadList_PauseAll, action: { _ in toggleAllPaused() }) } case .downloaded: - header = ChatListSearchItemHeader(type: .recentDownloads, theme: presentationData.theme, strings: presentationData.strings, actionTitle: presentationData.strings.DownloadList_Clear, action: { + header = ChatListSearchItemHeader(type: .recentDownloads, theme: presentationData.theme, strings: presentationData.strings, actionTitle: presentationData.strings.DownloadList_Clear, action: { _ in openClearRecentlyDownloaded() }) case .index: @@ -957,7 +1012,7 @@ public enum ChatListSearchEntry: Comparable, Identifiable { if case .publicPosts = key { header = ChatListSearchItemHeader(type: .publicPosts, theme: presentationData.theme, strings: presentationData.strings, actionTitle: nil, action: nil) } else { - header = ChatListSearchItemHeader(type: .publicPosts, theme: presentationData.theme, strings: presentationData.strings, actionTitle: "\(presentationData.strings.ChatList_Search_ShowMore) >", action: { + header = ChatListSearchItemHeader(type: .publicPosts, theme: presentationData.theme, strings: presentationData.strings, actionTitle: "\(presentationData.strings.ChatList_Search_ShowMore) >", action: { _ in openPublicPosts() }) } @@ -966,7 +1021,24 @@ public enum ChatListSearchEntry: Comparable, Identifiable { if case let .forum(peerId) = location, let peer = peer.peer, peer.id == peerId { headerType = .messages(location: peer.compactDisplayTitle) } - header = ChatListSearchItemHeader(type: headerType, theme: presentationData.theme, strings: presentationData.strings, actionTitle: nil, action: nil) + var actionTitle: String? + if case .generic = section { + let filterTitle: String + switch searchScope { + case .everywhere: + filterTitle = presentationData.strings.ChatList_Search_Messages_AllChats + case .channels: + filterTitle = presentationData.strings.ChatList_Search_Messages_Channels + case .groups: + filterTitle = presentationData.strings.ChatList_Search_Messages_GroupChats + case .privateChats: + filterTitle = presentationData.strings.ChatList_Search_Messages_PrivateChats + } + actionTitle = "\(filterTitle) <" + } + header = ChatListSearchItemHeader(type: headerType, theme: presentationData.theme, strings: presentationData.strings, actionTitle: actionTitle, action: { sourceNode in + openMessagesFilter(sourceNode) + }) } } let selection: ChatHistoryMessageSelection = selected.flatMap { .selectable(selected: $0) } ?? .none @@ -1037,6 +1109,25 @@ public enum ChatListSearchEntry: Comparable, Identifiable { tags: [] )), editing: false, hasActiveRevealControls: false, selected: false, header: tagMask == nil ? header : nil, enableContextActions: false, hiddenOffset: false, interaction: interaction) } + case let .messagePlaceholder(_, presentationData, searchScope): + var actionTitle: String? + let filterTitle: String + switch searchScope { + case .everywhere: + filterTitle = presentationData.strings.ChatList_Search_Messages_AllChats + case .channels: + filterTitle = presentationData.strings.ChatList_Search_Messages_Channels + case .groups: + filterTitle = presentationData.strings.ChatList_Search_Messages_GroupChats + case .privateChats: + filterTitle = presentationData.strings.ChatList_Search_Messages_PrivateChats + } + actionTitle = "\(filterTitle) <" + + let header = ChatListSearchItemHeader(type: .messages(location: nil), theme: presentationData.theme, strings: presentationData.strings, actionTitle: actionTitle, action: { sourceNode in + openMessagesFilter(sourceNode) + }) + return ChatListItem(presentationData: presentationData, context: context, chatListLocation: location, filterData: nil, index: EngineChatList.Item.Index.chatList(ChatListIndex(pinningIndex: nil, messageIndex: MessageIndex(id: MessageId(peerId: PeerId(0), namespace: Namespaces.Message.Cloud, id: 0), timestamp: 0))), content: .loading, editing: false, hasActiveRevealControls: false, selected: false, header: header, enableContextActions: false, hiddenOffset: false, interaction: interaction) case let .addContact(phoneNumber, theme, strings): return ContactsAddItem(context: context, theme: theme, strings: strings, phoneNumber: phoneNumber, header: ChatListSearchItemHeader(type: .phoneNumber, theme: theme, strings: strings, actionTitle: nil, action: nil), action: { interaction.addContact(phoneNumber) @@ -1110,12 +1201,12 @@ private func chatListSearchContainerPreparedRecentTransition( return ChatListSearchContainerRecentTransition(deletions: deletions, insertions: insertions, updates: updates, isEmpty: isEmpty) } -public func chatListSearchContainerPreparedTransition(from fromEntries: [ChatListSearchEntry], to toEntries: [ChatListSearchEntry], displayingResults: Bool, isEmpty: Bool, isLoading: Bool, animated: Bool, context: AccountContext, presentationData: PresentationData, enableHeaders: Bool, filter: ChatListNodePeersFilter, requestPeerType: [ReplyMarkupButtonRequestPeerType]?, location: ChatListControllerLocation, key: ChatListSearchPaneKey, tagMask: EngineMessage.Tags?, interaction: ChatListNodeInteraction, listInteraction: ListMessageItemInteraction, peerContextAction: ((EnginePeer, ChatListSearchContextActionSource, ASDisplayNode, ContextGesture?, CGPoint?) -> Void)?, toggleExpandLocalResults: @escaping () -> Void, toggleExpandGlobalResults: @escaping () -> Void, searchPeer: @escaping (EnginePeer) -> Void, searchQuery: String?, searchOptions: ChatListSearchOptions?, messageContextAction: ((EngineMessage, ASDisplayNode?, CGRect?, UIGestureRecognizer?, ChatListSearchPaneKey, (id: String, size: Int64, isFirstInList: Bool)?) -> Void)?, openClearRecentlyDownloaded: @escaping () -> Void, toggleAllPaused: @escaping () -> Void, openStories: @escaping (EnginePeer.Id, AvatarNode) -> Void, openPublicPosts: @escaping () -> Void) -> ChatListSearchContainerTransition { +public func chatListSearchContainerPreparedTransition(from fromEntries: [ChatListSearchEntry], to toEntries: [ChatListSearchEntry], displayingResults: Bool, isEmpty: Bool, isLoading: Bool, animated: Bool, context: AccountContext, presentationData: PresentationData, enableHeaders: Bool, filter: ChatListNodePeersFilter, requestPeerType: [ReplyMarkupButtonRequestPeerType]?, location: ChatListControllerLocation, key: ChatListSearchPaneKey, tagMask: EngineMessage.Tags?, interaction: ChatListNodeInteraction, listInteraction: ListMessageItemInteraction, peerContextAction: ((EnginePeer, ChatListSearchContextActionSource, ASDisplayNode, ContextGesture?, CGPoint?) -> Void)?, toggleExpandLocalResults: @escaping () -> Void, toggleExpandGlobalResults: @escaping () -> Void, searchPeer: @escaping (EnginePeer) -> Void, searchQuery: String?, searchOptions: ChatListSearchOptions?, messageContextAction: ((EngineMessage, ASDisplayNode?, CGRect?, UIGestureRecognizer?, ChatListSearchPaneKey, (id: String, size: Int64, isFirstInList: Bool)?) -> Void)?, openClearRecentlyDownloaded: @escaping () -> Void, toggleAllPaused: @escaping () -> Void, openStories: @escaping (EnginePeer.Id, AvatarNode) -> Void, openPublicPosts: @escaping () -> Void, openMessagesFilter: @escaping (ASDisplayNode) -> Void) -> ChatListSearchContainerTransition { 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, enableHeaders: enableHeaders, filter: filter, requestPeerType: requestPeerType, location: location, key: key, tagMask: tagMask, interaction: interaction, listInteraction: listInteraction, peerContextAction: peerContextAction, toggleExpandLocalResults: toggleExpandLocalResults, toggleExpandGlobalResults: toggleExpandGlobalResults, searchPeer: searchPeer, searchQuery: searchQuery, searchOptions: searchOptions, messageContextAction: messageContextAction, openClearRecentlyDownloaded: openClearRecentlyDownloaded, toggleAllPaused: toggleAllPaused, openStories: openStories, openPublicPosts: openPublicPosts), directionHint: nil) } - let updates = updateIndices.map { ListViewUpdateItem(index: $0.0, previousIndex: $0.2, item: $0.1.item(context: context, presentationData: presentationData, enableHeaders: enableHeaders, filter: filter, requestPeerType: requestPeerType, location: location, key: key, tagMask: tagMask, interaction: interaction, listInteraction: listInteraction, peerContextAction: peerContextAction, toggleExpandLocalResults: toggleExpandLocalResults, toggleExpandGlobalResults: toggleExpandGlobalResults, searchPeer: searchPeer, searchQuery: searchQuery, searchOptions: searchOptions, messageContextAction: messageContextAction, openClearRecentlyDownloaded: openClearRecentlyDownloaded, toggleAllPaused: toggleAllPaused, openStories: openStories, openPublicPosts: openPublicPosts), directionHint: nil) } + let insertions = indicesAndItems.map { ListViewInsertItem(index: $0.0, previousIndex: $0.2, item: $0.1.item(context: context, presentationData: presentationData, enableHeaders: enableHeaders, filter: filter, requestPeerType: requestPeerType, location: location, key: key, tagMask: tagMask, interaction: interaction, listInteraction: listInteraction, peerContextAction: peerContextAction, toggleExpandLocalResults: toggleExpandLocalResults, toggleExpandGlobalResults: toggleExpandGlobalResults, searchPeer: searchPeer, searchQuery: searchQuery, searchOptions: searchOptions, messageContextAction: messageContextAction, openClearRecentlyDownloaded: openClearRecentlyDownloaded, toggleAllPaused: toggleAllPaused, openStories: openStories, openPublicPosts: openPublicPosts, openMessagesFilter: openMessagesFilter), directionHint: nil) } + let updates = updateIndices.map { ListViewUpdateItem(index: $0.0, previousIndex: $0.2, item: $0.1.item(context: context, presentationData: presentationData, enableHeaders: enableHeaders, filter: filter, requestPeerType: requestPeerType, location: location, key: key, tagMask: tagMask, interaction: interaction, listInteraction: listInteraction, peerContextAction: peerContextAction, toggleExpandLocalResults: toggleExpandLocalResults, toggleExpandGlobalResults: toggleExpandGlobalResults, searchPeer: searchPeer, searchQuery: searchQuery, searchOptions: searchOptions, messageContextAction: messageContextAction, openClearRecentlyDownloaded: openClearRecentlyDownloaded, toggleAllPaused: toggleAllPaused, openStories: openStories, openPublicPosts: openPublicPosts, openMessagesFilter: openMessagesFilter), directionHint: nil) } return ChatListSearchContainerTransition(deletions: deletions, insertions: insertions, updates: updates, displayingResults: displayingResults, isEmpty: isEmpty, isLoading: isLoading, query: searchQuery, animated: animated) } @@ -1215,9 +1306,9 @@ private struct DownloadItem: Equatable { private func filteredPeerSearchQueryResults(value: ([FoundPeer], [FoundPeer]), scope: TelegramSearchPeersScope) -> ([FoundPeer], [FoundPeer]) { switch scope { - case .everywhere, .privateChats: + case .everywhere, .privateChats, .groups: return value - case .channels, .groups: + case .channels: return ( value.0.filter { peer in if let channel = peer.peer as? TelegramChannel, case .broadcast = channel.info { @@ -1422,6 +1513,8 @@ final class ChatListSearchListPaneNode: ASDisplayNode, ChatListSearchPaneNode { private var searchQueryDisposable: Disposable? private var searchOptionsDisposable: Disposable? + private let searchScopePromise = ValuePromise(.everywhere) + init(context: AccountContext, animationCache: AnimationCache, animationRenderer: MultiAnimationRenderer, updatedPresentationData: (initial: PresentationData, signal: Signal)? = nil, interaction: ChatListSearchInteraction, key: ChatListSearchPaneKey, peersFilter: ChatListNodePeersFilter, requestPeerType: [ReplyMarkupButtonRequestPeerType]?, location: ChatListControllerLocation, searchQuery: Signal, searchOptions: Signal, navigationController: NavigationController?, parentController: ViewController?, globalPeerSearchContext: GlobalPeerSearchContext?) { self.context = context self.animationCache = animationCache @@ -1708,8 +1801,8 @@ final class ChatListSearchListPaneNode: ASDisplayNode, ChatListSearchPaneNode { } let previousRecentlySearchedPeersState = Atomic(value: nil) - let foundItems: Signal<([ChatListSearchEntry], Bool)?, NoError> = combineLatest(queue: .mainQueue(), searchQuery, searchOptions, downloadItems) - |> mapToSignal { [weak self] query, options, downloadItems -> Signal<([ChatListSearchEntry], Bool)?, NoError> in + let foundItems: Signal<([ChatListSearchEntry], Bool)?, NoError> = combineLatest(queue: .mainQueue(), searchQuery, searchOptions, self.searchScopePromise.get(), downloadItems) + |> mapToSignal { [weak self] query, options, searchScope, downloadItems -> Signal<([ChatListSearchEntry], Bool)?, NoError> in if query == nil && options == nil && [.chats, .topics, .channels, .apps].contains(key) { let _ = currentRemotePeers.swap(nil) return .single(nil) @@ -1774,7 +1867,7 @@ final class ChatListSearchListPaneNode: ASDisplayNode, ChatListSearchPaneNode { resource = (resourceValue.id.stringRepresentation, size, entries.isEmpty) } - entries.append(.message(message, peer, nil, nil, presentationData, 1, nil, false, .downloading(item.priority), resource, .downloading, allPaused, nil, false)) + entries.append(.message(message, peer, nil, nil, presentationData, 1, nil, false, .downloading(item.priority), resource, .downloading, allPaused, nil, false, .everywhere)) } for item in downloadItems.doneItems.sorted(by: { ChatListSearchEntry.MessageOrderingKey.downloaded(timestamp: $0.timestamp, index: $0.message.index) < ChatListSearchEntry.MessageOrderingKey.downloaded(timestamp: $1.timestamp, index: $1.message.index) }) { if !item.isSeen { @@ -1802,7 +1895,7 @@ final class ChatListSearchListPaneNode: ASDisplayNode, ChatListSearchPaneNode { } } - entries.append(.message(message, peer, nil, nil, presentationData, 1, selectionState?.contains(message.id), false, .downloaded(timestamp: item.timestamp, index: message.index), (item.resourceId, item.size, false), .recentlyDownloaded, false, nil, false)) + entries.append(.message(message, peer, nil, nil, presentationData, 1, selectionState?.contains(message.id), false, .downloaded(timestamp: item.timestamp, index: message.index), (item.resourceId, item.size, false), .recentlyDownloaded, false, nil, false, .everywhere)) } return (entries.sorted(), false) } @@ -2188,7 +2281,7 @@ final class ChatListSearchListPaneNode: ASDisplayNode, ChatListSearchPaneNode { if case let .chatList(groupId) = location, case .archive = groupId { searchLocations = [.group(groupId: groupId._asGroup(), tags: tagMask, minDate: options.date?.0, maxDate: options.date?.1)] } else { - searchLocations = [.general(scope: .everywhere, tags: tagMask, minDate: options.date?.0, maxDate: options.date?.1)] + searchLocations = [.general(scope: searchScope, tags: tagMask, minDate: options.date?.0, maxDate: options.date?.1)] } } } else { @@ -2199,7 +2292,7 @@ final class ChatListSearchListPaneNode: ASDisplayNode, ChatListSearchPaneNode { } else if case let .chatList(groupId) = location, case .archive = groupId { searchLocations = [.group(groupId: groupId._asGroup(), tags: tagMask, minDate: nil, maxDate: nil)] } else { - searchLocations = [.general(scope: .everywhere, tags: tagMask, minDate: nil, maxDate: nil)] + searchLocations = [.general(scope: searchScope, tags: tagMask, minDate: nil, maxDate: nil)] } } @@ -2787,7 +2880,7 @@ final class ChatListSearchListPaneNode: ASDisplayNode, ChatListSearchPaneNode { } } //TODO:requiresPremiumForMessaging - entries.append(.message(message, peer, nil, nil, presentationData, 1, nil, true, .index(message.index), nil, .generic, false, nil, false)) + entries.append(.message(message, peer, nil, nil, presentationData, 1, nil, true, .index(message.index), nil, .generic, false, nil, false, .everywhere)) index += 1 } @@ -2807,39 +2900,46 @@ final class ChatListSearchListPaneNode: ASDisplayNode, ChatListSearchPaneNode { firstHeaderId = headerId } let peer = EngineRenderedPeer(message: message) - entries.append(.message(message, peer, foundPublicMessageSet.readCounters[message.id.peerId], foundPublicMessageSet.threadsData[message.id]?.info, presentationData, foundPublicMessageSet.totalCount, nil, headerId == firstHeaderId, .index(message.index), nil, .publicPosts, false, nil, false)) + entries.append(.message(message, peer, foundPublicMessageSet.readCounters[message.id.peerId], foundPublicMessageSet.threadsData[message.id]?.info, presentationData, foundPublicMessageSet.totalCount, nil, headerId == firstHeaderId, .index(message.index), nil, .publicPosts, false, nil, false, .everywhere)) index += 1 } } var existingMessageIds = Set() - for foundRemoteMessageSet in foundRemoteMessages.0 { - for message in foundRemoteMessageSet.messages { - if existingMessageIds.contains(message.id) { - continue - } - existingMessageIds.insert(message.id) - - if searchState.deletedMessageIds.contains(message.id) { - continue - } else if message.id.namespace == Namespaces.Message.Cloud && searchState.deletedGlobalMessageIds.contains(message.id.id) { - continue - } - let headerId = listMessageDateHeaderId(timestamp: message.timestamp) - if firstHeaderId == nil { - firstHeaderId = headerId - } - var peer = EngineRenderedPeer(message: message) - if let group = message.peers[message.id.peerId] as? TelegramGroup, let migrationReference = group.migrationReference { - if let channelPeer = message.peers[migrationReference.peerId] { - peer = EngineRenderedPeer(peer: EnginePeer(channelPeer)) - } - } - - //TODO:requiresPremiumForMessaging - entries.append(.message(message, peer, foundRemoteMessageSet.readCounters[message.id.peerId], foundRemoteMessageSet.threadsData[message.id]?.info, presentationData, foundRemoteMessageSet.totalCount, selectionState?.contains(message.id), headerId == firstHeaderId, .index(message.index), nil, .generic, false, nil, false)) + if foundRemoteMessages.1 && searchScope != .everywhere { + for i in 0 ..< 5 { + entries.append(.messagePlaceholder(Int32(i), presentationData, searchScope)) index += 1 } + } else { + for foundRemoteMessageSet in foundRemoteMessages.0 { + for message in foundRemoteMessageSet.messages { + if existingMessageIds.contains(message.id) { + continue + } + existingMessageIds.insert(message.id) + + if searchState.deletedMessageIds.contains(message.id) { + continue + } else if message.id.namespace == Namespaces.Message.Cloud && searchState.deletedGlobalMessageIds.contains(message.id.id) { + continue + } + let headerId = listMessageDateHeaderId(timestamp: message.timestamp) + if firstHeaderId == nil { + firstHeaderId = headerId + } + var peer = EngineRenderedPeer(message: message) + if let group = message.peers[message.id.peerId] as? TelegramGroup, let migrationReference = group.migrationReference { + if let channelPeer = message.peers[migrationReference.peerId] { + peer = EngineRenderedPeer(peer: EnginePeer(channelPeer)) + } + } + + //TODO:requiresPremiumForMessaging + entries.append(.message(message, peer, foundRemoteMessageSet.readCounters[message.id.peerId], foundRemoteMessageSet.threadsData[message.id]?.info, presentationData, foundRemoteMessageSet.totalCount, selectionState?.contains(message.id), headerId == firstHeaderId, .index(message.index), nil, .generic, false, nil, false, searchScope)) + index += 1 + } + } } } @@ -2985,6 +3085,8 @@ final class ChatListSearchListPaneNode: ASDisplayNode, ChatListSearchPaneNode { return } switch item.content { + case .loading: + break case let .peer(peerData): if let peer = peerData.peer.peer, let message = peerData.messages.first { peerContextAction(peer, .search(message.id), node, gesture, location) @@ -3096,7 +3198,7 @@ final class ChatListSearchListPaneNode: ASDisplayNode, ChatListSearchPaneNode { var fetchResourceId: (id: String, size: Int64, isFirstInList: Bool)? for entry in currentEntries { switch entry { - case let .message(m, _, _, _, _, _, _, _, _, resource, _, _, _, _): + case let .message(m, _, _, _, _, _, _, _, _, resource, _, _, _, _, _): if m.id == message.id { fetchResourceId = resource } @@ -3165,7 +3267,7 @@ final class ChatListSearchListPaneNode: ASDisplayNode, ChatListSearchPaneNode { if let user = foundPeer.peer as? TelegramUser, user.flags.contains(.requirePremium) { requiresPremiumForMessagingPeerIds.append(foundPeer.peer.id) } - case let .message(_, peer, _, _, _, _, _, _, _, _, _, _, _, _): + case let .message(_, peer, _, _, _, _, _, _, _, _, _, _, _, _, _): if let peer = peer.peer { storyStatsIds.append(peer.id) if case let .user(user) = peer, user.flags.contains(.requirePremium) { @@ -3203,8 +3305,8 @@ final class ChatListSearchListPaneNode: ASDisplayNode, ChatListSearchPaneNode { mappedItems[i] = .localPeer(peer, associatedPeer, unreadBadge, index, theme, strings, sortOrder, displayOrder, expandType, stats[peer.id] ?? nil, requiresPremiumForMessaging[peer.id] ?? false) case let .globalPeer(peer, unreadBadge, index, theme, strings, sortOrder, displayOrder, expandType, _, _, searchQuery): mappedItems[i] = .globalPeer(peer, unreadBadge, index, theme, strings, sortOrder, displayOrder, expandType, stats[peer.peer.id] ?? nil, requiresPremiumForMessaging[peer.peer.id] ?? false, searchQuery) - case let .message(message, peer, combinedPeerReadState, threadInfo, presentationData, totalCount, selected, displayCustomHeader, key, resourceId, section, allPaused, _, _): - mappedItems[i] = .message(message, peer, combinedPeerReadState, threadInfo, presentationData, totalCount, selected, displayCustomHeader, key, resourceId, section, allPaused, stats[peer.peerId] ?? nil, requiresPremiumForMessaging[peer.peerId] ?? false) + case let .message(message, peer, combinedPeerReadState, threadInfo, presentationData, totalCount, selected, displayCustomHeader, key, resourceId, section, allPaused, _, _, searchScope): + mappedItems[i] = .message(message, peer, combinedPeerReadState, threadInfo, presentationData, totalCount, selected, displayCustomHeader, key, resourceId, section, allPaused, stats[peer.peerId] ?? nil, requiresPremiumForMessaging[peer.peerId] ?? false, searchScope) default: break } @@ -3340,6 +3442,8 @@ final class ChatListSearchListPaneNode: ASDisplayNode, ChatListSearchPaneNode { strongSelf.interaction.openStories?(peerId, avatarNode) }, openPublicPosts: { strongSelf.interaction.switchToFilter(.publicPosts) + }, openMessagesFilter: { sourceNode in + strongSelf.openMessagesFilter(sourceNode: sourceNode) }) strongSelf.currentEntries = newEntries if strongSelf.key == .downloads { @@ -3351,7 +3455,7 @@ final class ChatListSearchListPaneNode: ASDisplayNode, ChatListSearchPaneNode { var messages: [EngineMessage] = [] for entry in newEntries { - if case let .message(message, _, _, _, _, _, _, _, _, _, _, _, _, _) = entry { + if case let .message(message, _, _, _, _, _, _, _, _, _, _, _, _, _, _) = entry { messages.append(message) } } @@ -4767,14 +4871,65 @@ final class ChatListSearchListPaneNode: ASDisplayNode, ChatListSearchPaneNode { bounds = selectedItemNode.bounds } switch item.content { - case let .peer(peerData): - return (selectedItemNode.view, bounds, peerData.messages.last?.id ?? peerData.peer.peerId) - case let .groupReference(groupReference): - return (selectedItemNode.view, bounds, groupReference.groupId) + case .loading: + return nil + case let .peer(peerData): + return (selectedItemNode.view, bounds, peerData.messages.last?.id ?? peerData.peer.peerId) + case let .groupReference(groupReference): + return (selectedItemNode.view, bounds, groupReference.groupId) } } return nil } + + func openMessagesFilter(sourceNode: ASDisplayNode) { + self.interaction.dismissInput() + let _ = (self.searchScopePromise.get() + |> take(1)).start(next: { [weak self] scope in + guard let self else { + return + } + var items: [ContextMenuItem] = [] + items.append(.action(ContextMenuActionItem(text: self.presentationData.strings.ChatList_Search_Messages_Menu_AllChats, icon: { theme in + return scope == .everywhere ? generateTintedImage(image: UIImage(bundleImageName: "Chat/Context Menu/Check"), color: theme.contextMenu.primaryColor) : nil + }, action: { [weak self] _, f in + guard let self else { + return + } + f(.default) + self.searchScopePromise.set(.everywhere) + }))) + items.append(.action(ContextMenuActionItem(text: self.presentationData.strings.ChatList_Search_Messages_Menu_PrivateChats, icon: { theme in + return scope == .privateChats ? generateTintedImage(image: UIImage(bundleImageName: "Chat/Context Menu/Check"), color: theme.contextMenu.primaryColor) : nil + }, action: { [weak self] _, f in + guard let self else { + return + } + f(.default) + self.searchScopePromise.set(.privateChats) + }))) + items.append(.action(ContextMenuActionItem(text: self.presentationData.strings.ChatList_Search_Messages_Menu_GroupChats, icon: { theme in + return scope == .groups ? generateTintedImage(image: UIImage(bundleImageName: "Chat/Context Menu/Check"), color: theme.contextMenu.primaryColor) : nil + }, action: { [weak self] _, f in + guard let self else { + return + } + f(.default) + self.searchScopePromise.set(.groups) + }))) + items.append(.action(ContextMenuActionItem(text: self.presentationData.strings.ChatList_Search_Messages_Menu_Channels, icon: { theme in + return scope == .channels ? generateTintedImage(image: UIImage(bundleImageName: "Chat/Context Menu/Check"), color: theme.contextMenu.primaryColor) : nil + }, action: { [weak self] _, f in + guard let self else { + return + } + f(.default) + self.searchScopePromise.set(.channels) + }))) + let contextController = ContextController(presentationData: self.presentationData, source: .reference(ChatListSearchReferenceContentSource(sourceNode: sourceNode)), items: .single(ContextController.Items(content: .list(items))), gesture: nil) + self.interaction.present(contextController, nil) + }) + } } private final class SearchShimmerEffectNode: ASDisplayNode { @@ -5276,3 +5431,18 @@ public final class ChatListSearchShimmerNode: ASDisplayNode { transition.updateFrame(node: self.effectNode, frame: CGRect(origin: CGPoint(x: 0.0, y: 0.0), size: size)) } } + +private final class ChatListSearchReferenceContentSource: ContextReferenceContentSource { + private let sourceNode: ASDisplayNode + var keepInPlace: Bool { + return true + } + + init(sourceNode: ASDisplayNode) { + self.sourceNode = sourceNode + } + + func transitionInfo() -> ContextControllerReferenceViewInfo? { + return ContextControllerReferenceViewInfo(referenceView: self.sourceNode.view, contentAreaInScreenSpace: UIScreen.main.bounds, actionsPosition: .bottom) + } +} diff --git a/submodules/ChatListUI/Sources/ChatListSearchMediaNode.swift b/submodules/ChatListUI/Sources/ChatListSearchMediaNode.swift index 7c02fe7f39..6df813a8bc 100644 --- a/submodules/ChatListUI/Sources/ChatListSearchMediaNode.swift +++ b/submodules/ChatListUI/Sources/ChatListSearchMediaNode.swift @@ -702,7 +702,7 @@ final class ChatListSearchMediaNode: ASDisplayNode, ASScrollViewDelegate { var index: UInt32 = 0 if let entries = entries { for entry in entries { - if case let .message(message, _, _, _, _, _, _, _, _, _, _, _, _, _) = entry { + if case let .message(message, _, _, _, _, _, _, _, _, _, _, _, _, _, _) = entry { self.mediaItems.append(VisualMediaItem(message: message._asMessage(), index: nil)) } index += 1 diff --git a/submodules/ChatListUI/Sources/ChatListShimmerNode.swift b/submodules/ChatListUI/Sources/ChatListShimmerNode.swift index 2c4a09a58d..b7a54b62ce 100644 --- a/submodules/ChatListUI/Sources/ChatListShimmerNode.swift +++ b/submodules/ChatListUI/Sources/ChatListShimmerNode.swift @@ -8,7 +8,7 @@ import AnimationCache import MultiAnimationRenderer import TelegramCore -final class ShimmerEffectNode: ASDisplayNode { +private final class ShimmerEffectNode: ASDisplayNode { private var currentBackgroundColor: UIColor? private var currentForegroundColor: UIColor? private let imageNodeContainer: ASDisplayNode diff --git a/submodules/ChatListUI/Sources/Node/ChatListEmptyInfoItem.swift b/submodules/ChatListUI/Sources/Node/ChatListEmptyInfoItem.swift index d38e931023..21363bb4f0 100644 --- a/submodules/ChatListUI/Sources/Node/ChatListEmptyInfoItem.swift +++ b/submodules/ChatListUI/Sources/Node/ChatListEmptyInfoItem.swift @@ -254,7 +254,7 @@ class ChatListSectionHeaderNode: ListViewItemNode { if item.hide != nil { headerNode.action = item.strings.ChatList_EmptyListContactsHeaderHide headerNode.actionType = .generic - headerNode.activateAction = { + headerNode.activateAction = { _ in guard let self else { return } diff --git a/submodules/ChatListUI/Sources/Node/ChatListItem.swift b/submodules/ChatListUI/Sources/Node/ChatListItem.swift index 05fa9d2acb..3b39041377 100644 --- a/submodules/ChatListUI/Sources/Node/ChatListItem.swift +++ b/submodules/ChatListUI/Sources/Node/ChatListItem.swift @@ -28,6 +28,7 @@ import EmojiStatusComponent import AvatarVideoNode import AppBundle import MultilineTextComponent +import ShimmerEffect public enum ChatListItemContent { public struct ThreadInfo: Equatable { @@ -210,11 +211,14 @@ public enum ChatListItemContent { } } + case loading case peer(PeerData) case groupReference(GroupReferenceData) public var chatLocation: ChatLocation? { switch self { + case .loading: + return nil case let .peer(peerData): return .peer(id: peerData.peer.peerId) case .groupReference: @@ -492,6 +496,8 @@ public class ChatListItem: ListViewItem, ChatListSearchItemNeighbour { public func selected(listView: ListView) { switch self.content { + case .loading: + break case let .peer(peerData): if let message = peerData.messages.last, let peer = peerData.peer.peer { var threadId: Int64? @@ -1234,6 +1240,9 @@ public class ChatListItemNode: ItemListRevealOptionsItemNode { var actionButtonBackgroundView: UIImageView? var actionButtonNode: HighlightableButtonNode? + private var placeholderNode: ShimmerEffectNode? + private var absoluteLocation: (CGRect, CGSize)? + private var hierarchyTrackingLayer: HierarchyTrackingLayer? private var cachedDataDisposable = MetaDisposable() @@ -1293,6 +1302,8 @@ public class ChatListItemNode: ItemListRevealOptionsItemNode { return nil } switch item.content { + case .loading: + return nil case let .groupReference(groupReferenceData): var result = item.presentationData.strings.ChatList_ArchivedChatsTitle let allCount = groupReferenceData.unreadCount @@ -1325,6 +1336,8 @@ public class ChatListItemNode: ItemListRevealOptionsItemNode { return nil } switch item.content { + case .loading: + return nil case let .groupReference(groupReferenceData): let peers = groupReferenceData.peers let messageValue = groupReferenceData.message @@ -1619,6 +1632,9 @@ public class ChatListItemNode: ItemListRevealOptionsItemNode { var displayAsMessage = false var enablePreview = true switch item.content { + case .loading: + displayAsMessage = true + enablePreview = false case let .peer(peerData): displayAsMessage = peerData.displayAsMessage if displayAsMessage, case let .user(author) = peerData.messages.last?.author { @@ -1934,6 +1950,22 @@ public class ChatListItemNode: ItemListRevealOptionsItemNode { var groupHiddenByDefault = false switch item.content { + case .loading: + messages = [] + contentPeer = .group([]) + combinedReadState = nil + unreadCount = (0, false, false, nil, false) + isRemovedFromTotalUnreadCount = false + peerPresence = nil + draftState = nil + mediaDraftContentType = nil + hasUnseenMentions = false + hasUnseenReactions = false + inputActivities = nil + isPeerGroup = false + promoInfo = nil + displayAsMessage = true + hasFailedMessages = false case let .peer(peerData): let messagesValue = peerData.messages let peerValue = peerData.peer @@ -2078,6 +2110,7 @@ public class ChatListItemNode: ItemListRevealOptionsItemNode { var currentForwardedIcon: UIImage? var currentStoryIcon: UIImage? var currentGiftIcon: UIImage? + var currentLocationIcon: UIImage? var selectableControlSizeAndApply: (CGFloat, (CGSize, Bool) -> ItemListSelectableControlNode)? var reorderControlSizeAndApply: (CGFloat, (CGFloat, Bool, ContainedViewLayoutTransition) -> ItemListEditableReorderControlNode)? @@ -2252,6 +2285,7 @@ public class ChatListItemNode: ItemListRevealOptionsItemNode { var displayForwardedIcon = false var displayStoryReplyIcon = false var displayGiftIcon = false + var displayLocationIcon = false var ignoreForwardedIcon = false switch contentData { @@ -2562,7 +2596,9 @@ public class ChatListItemNode: ItemListRevealOptionsItemNode { displayStoryReplyIcon = true } else { for media in message.media { - if let action = media as? TelegramMediaAction { + if let _ = media as? TelegramMediaMap { + displayLocationIcon = true + } else if let action = media as? TelegramMediaAction { switch action.action { case .giftPremium, .giftStars, .starGift: displayGiftIcon = true @@ -2737,6 +2773,10 @@ public class ChatListItemNode: ItemListRevealOptionsItemNode { currentGiftIcon = PresentationResourcesChatList.giftIcon(item.presentationData.theme) } + if displayLocationIcon { + currentLocationIcon = PresentationResourcesChatList.locationIcon(item.presentationData.theme) + } + if let currentForwardedIcon { textLeftCutout += currentForwardedIcon.size.width if !contentImageSpecs.isEmpty { @@ -2764,6 +2804,15 @@ public class ChatListItemNode: ItemListRevealOptionsItemNode { } } + if let currentLocationIcon { + textLeftCutout += currentLocationIcon.size.width + if !contentImageSpecs.isEmpty { + textLeftCutout += forwardedIconSpacing + } else { + textLeftCutout += contentImageTrailingSpace + } + } + for i in 0 ..< contentImageSpecs.count { if i != 0 { textLeftCutout += contentImageSpacing @@ -2824,6 +2873,8 @@ public class ChatListItemNode: ItemListRevealOptionsItemNode { let dateText: String var topIndex: MessageIndex? switch item.content { + case .loading: + break case let .groupReference(groupReferenceData): topIndex = groupReferenceData.message?.index case let .peer(peerData): @@ -2996,6 +3047,7 @@ public class ChatListItemNode: ItemListRevealOptionsItemNode { if case let .chatList(index) = item.index, index.messageIndex.id.peerId == item.context.account.peerId { isAccountPeer = true } + if !isPeerGroup && !isAccountPeer && threadInfo == nil { if displayAsMessage { switch item.content { @@ -3010,16 +3062,16 @@ public class ChatListItemNode: ItemListRevealOptionsItemNode { } else if peer.isFake { currentCredibilityIconContent = .text(color: item.presentationData.theme.chat.message.incoming.scamColor, string: item.presentationData.strings.Message_FakeAccount.uppercased()) } else if let emojiStatus = peer.emojiStatus, !premiumConfiguration.isPremiumDisabled { - if case .channel = peer, peer.isVerified { - currentVerifiedIconContent = .verified(fillColor: item.presentationData.theme.list.itemCheckColors.fillColor, foregroundColor: item.presentationData.theme.list.itemCheckColors.foregroundColor, sizeType: .compact) - } - currentCredibilityIconContent = .animation(content: .customEmoji(fileId: emojiStatus.fileId), size: CGSize(width: 32.0, height: 32.0), placeholderColor: item.presentationData.theme.list.mediaPlaceholderColor, themeColor: item.presentationData.theme.list.itemAccentColor, loopMode: .count(2)) - } else if peer.isVerified { - currentCredibilityIconContent = .verified(fillColor: item.presentationData.theme.list.itemCheckColors.fillColor, foregroundColor: item.presentationData.theme.list.itemCheckColors.foregroundColor, sizeType: .compact) } else if peer.isPremium && !premiumConfiguration.isPremiumDisabled { currentCredibilityIconContent = .premium(color: item.presentationData.theme.list.itemAccentColor) } + + if peer.isVerified { + currentVerifiedIconContent = .verified(fillColor: item.presentationData.theme.list.itemCheckColors.fillColor, foregroundColor: item.presentationData.theme.list.itemCheckColors.foregroundColor, sizeType: .compact) + } else if let verification = peer.verification { + currentVerifiedIconContent = .animation(content: .customEmoji(fileId: verification.iconFileId), size: CGSize(width: 32.0, height: 32.0), placeholderColor: item.presentationData.theme.list.mediaPlaceholderColor, themeColor: item.presentationData.theme.list.itemAccentColor, loopMode: .count(0)) + } } default: break @@ -3037,23 +3089,28 @@ public class ChatListItemNode: ItemListRevealOptionsItemNode { } else if peer.isFake { currentCredibilityIconContent = .text(color: item.presentationData.theme.chat.message.incoming.scamColor, string: item.presentationData.strings.Message_FakeAccount.uppercased()) } else if let emojiStatus = peer.emojiStatus, !premiumConfiguration.isPremiumDisabled { - if case .channel = peer, peer.isVerified { - currentVerifiedIconContent = .verified(fillColor: item.presentationData.theme.list.itemCheckColors.fillColor, foregroundColor: item.presentationData.theme.list.itemCheckColors.foregroundColor, sizeType: .compact) - } - currentCredibilityIconContent = .animation(content: .customEmoji(fileId: emojiStatus.fileId), size: CGSize(width: 32.0, height: 32.0), placeholderColor: item.presentationData.theme.list.mediaPlaceholderColor, themeColor: item.presentationData.theme.list.itemAccentColor, loopMode: .count(2)) - } else if peer.isVerified { - currentCredibilityIconContent = .verified(fillColor: item.presentationData.theme.list.itemCheckColors.fillColor, foregroundColor: item.presentationData.theme.list.itemCheckColors.foregroundColor, sizeType: .compact) } else if peer.isPremium && !premiumConfiguration.isPremiumDisabled { currentCredibilityIconContent = .premium(color: item.presentationData.theme.list.itemAccentColor) } + + if peer.isVerified { + currentVerifiedIconContent = .verified(fillColor: item.presentationData.theme.list.itemCheckColors.fillColor, foregroundColor: item.presentationData.theme.list.itemCheckColors.foregroundColor, sizeType: .compact) + } else if let verification = peer.verification { + currentVerifiedIconContent = .animation(content: .customEmoji(fileId: verification.iconFileId), size: CGSize(width: 32.0, height: 32.0), placeholderColor: item.presentationData.theme.list.mediaPlaceholderColor, themeColor: item.presentationData.theme.list.itemAccentColor, loopMode: .count(0)) + } } } if let currentSecretIconImage = currentSecretIconImage { titleIconsWidth += currentSecretIconImage.size.width + 2.0 } + var titleLeftOffset: CGFloat = 0.0 if let currentVerifiedIconContent { + if titleLeftOffset.isZero, currentVerifiedIconContent != .none { + titleLeftOffset += 20.0 + } + if titleIconsWidth.isZero { titleIconsWidth += 4.0 } else { @@ -3070,6 +3127,10 @@ public class ChatListItemNode: ItemListRevealOptionsItemNode { } if let currentCredibilityIconContent { + if titleLeftOffset.isZero, case .verified = currentCredibilityIconContent { + titleLeftOffset += 20.0 + } + if titleIconsWidth.isZero { titleIconsWidth += 4.0 } else { @@ -3257,6 +3318,9 @@ public class ChatListItemNode: ItemListRevealOptionsItemNode { var peerRevealOptions: [ItemListRevealOption] var peerLeftRevealOptions: [ItemListRevealOption] switch item.content { + case .loading: + peerRevealOptions = [] + peerLeftRevealOptions = [] case let .peer(peerData): let renderedPeer = peerData.peer let presence = peerData.presence @@ -3948,7 +4012,7 @@ public class ChatListItemNode: ItemListRevealOptionsItemNode { } } - var titleOffset: CGFloat = 0.0 + var titleOffset: CGFloat = titleLeftOffset if let currentSecretIconImage = currentSecretIconImage { let iconNode: ASImageNode if let current = strongSelf.secretIconNode { @@ -4293,6 +4357,9 @@ public class ChatListItemNode: ItemListRevealOptionsItemNode { } else if let currentGiftIcon { messageTypeIcon = currentGiftIcon messageTypeIconOffset.y -= 2.0 - UIScreenPixel + } else if let currentLocationIcon { + messageTypeIcon = currentLocationIcon + messageTypeIconOffset.y -= 2.0 - UIScreenPixel } if let messageTypeIcon { @@ -4422,7 +4489,7 @@ public class ChatListItemNode: ItemListRevealOptionsItemNode { strongSelf.credibilityIconView = credibilityIconView strongSelf.mainContentContainerNode.view.addSubview(credibilityIconView) } - + let credibilityIconComponent = EmojiStatusComponent( context: item.context, animationCache: item.interaction.animationCache, @@ -4433,14 +4500,22 @@ public class ChatListItemNode: ItemListRevealOptionsItemNode { ) strongSelf.credibilityIconComponent = credibilityIconComponent + var iconOrigin: CGFloat = nextTitleIconOrigin + let containerSize = CGSize(width: 20.0, height: 20.0) + if case .verified = currentCredibilityIconContent { + iconOrigin = contentRect.origin.x + } let iconSize = credibilityIconView.update( transition: .immediate, component: AnyComponent(credibilityIconComponent), environment: {}, - containerSize: CGSize(width: 20.0, height: 20.0) + containerSize: containerSize ) - transition.updateFrame(view: credibilityIconView, frame: CGRect(origin: CGPoint(x: nextTitleIconOrigin, y: floorToScreenPixels(titleFrame.maxY - lastLineRect.height * 0.5 - iconSize.height / 2.0) - UIScreenPixel), size: iconSize)) - nextTitleIconOrigin += credibilityIconView.bounds.width + 4.0 + transition.updateFrame(view: credibilityIconView, frame: CGRect(origin: CGPoint(x: iconOrigin, y: floorToScreenPixels(titleFrame.maxY - lastLineRect.height * 0.5 - iconSize.height / 2.0) - UIScreenPixel), size: iconSize)) + if case .verified = currentCredibilityIconContent { + } else { + nextTitleIconOrigin += credibilityIconView.bounds.width + 4.0 + } } else if let credibilityIconView = strongSelf.credibilityIconView { strongSelf.credibilityIconView = nil credibilityIconView.removeFromSuperview() @@ -4466,14 +4541,16 @@ public class ChatListItemNode: ItemListRevealOptionsItemNode { ) strongSelf.verifiedIconComponent = verifiedIconComponent + let iconOrigin = contentRect.origin.x + let containerSize = CGSize(width: 16.0, height: 16.0) + let iconSize = verifiedIconView.update( transition: .immediate, component: AnyComponent(verifiedIconComponent), environment: {}, - containerSize: CGSize(width: 20.0, height: 20.0) + containerSize: containerSize ) - transition.updateFrame(view: verifiedIconView, frame: CGRect(origin: CGPoint(x: nextTitleIconOrigin, y: floorToScreenPixels(titleFrame.maxY - lastLineRect.height * 0.5 - iconSize.height / 2.0) - UIScreenPixel), size: iconSize)) - nextTitleIconOrigin += verifiedIconView.bounds.width + 4.0 + transition.updateFrame(view: verifiedIconView, frame: CGRect(origin: CGPoint(x: iconOrigin, y: floorToScreenPixels(titleFrame.maxY - lastLineRect.height * 0.5 - iconSize.height / 2.0) - UIScreenPixel), size: iconSize)) } else if let verifiedIconView = strongSelf.verifiedIconView { strongSelf.verifiedIconView = nil verifiedIconView.removeFromSuperview() @@ -4577,11 +4654,60 @@ public class ChatListItemNode: ItemListRevealOptionsItemNode { } strongSelf.avatarTapRecognizer?.isEnabled = item.interaction.inlineNavigationLocation == nil + + if case .loading = item.content { + let shimmerNode: ShimmerEffectNode + if let current = strongSelf.placeholderNode { + shimmerNode = current + } else { + shimmerNode = ShimmerEffectNode() + strongSelf.placeholderNode = shimmerNode + strongSelf.addSubnode(shimmerNode) + } + shimmerNode.frame = CGRect(origin: CGPoint(), size: CGSize(width: layout.contentSize.width, height: layout.contentSize.height - 1.0)) + if let (rect, size) = strongSelf.absoluteLocation { + shimmerNode.updateAbsoluteRect(rect, within: size) + } + + var shapes: [ShimmerEffectNode.Shape] = [] + + let titleLineWidth: CGFloat = 180.0 + let dateLineWidth: CGFloat = 36.0 + let textFirstLineWidth: CGFloat = 240.0 + let textSecondLineWidth: CGFloat = 200.0 + let lineDiameter: CGFloat = 10.0 + + shapes.append(.circle(avatarFrame)) + + let titleFrame = strongSelf.titleNode.frame + shapes.append(.roundedRectLine(startPoint: CGPoint(x: titleFrame.minX, y: titleFrame.minY + floor((titleFrame.height - lineDiameter) / 2.0)), width: titleLineWidth, diameter: lineDiameter)) + + let textFrame = strongSelf.textNode.textNode.frame + shapes.append(.roundedRectLine(startPoint: CGPoint(x: textFrame.minX, y: textFrame.minY + 7.0), width: textFirstLineWidth, diameter: lineDiameter)) + shapes.append(.roundedRectLine(startPoint: CGPoint(x: textFrame.minX, y: textFrame.minY + 7.0 + lineDiameter + 9.0), width: textSecondLineWidth, diameter: lineDiameter)) + + let dateFrame = strongSelf.dateNode.frame + shapes.append(.roundedRectLine(startPoint: CGPoint(x: dateFrame.maxX - dateLineWidth, y: dateFrame.minY + 3.0), width: dateLineWidth, diameter: lineDiameter)) + + shimmerNode.update(backgroundColor: item.presentationData.theme.list.itemBlocksBackgroundColor, foregroundColor: item.presentationData.theme.list.mediaPlaceholderColor, shimmeringColor: item.presentationData.theme.list.itemBlocksBackgroundColor.withAlphaComponent(0.4), shapes: shapes, size: shimmerNode.frame.size) + } else if let shimmerNode = strongSelf.placeholderNode { + strongSelf.placeholderNode = nil + shimmerNode.removeFromSupernode() + } } }) } } + override public func updateAbsoluteRect(_ rect: CGRect, within containerSize: CGSize) { + var rect = rect + rect.origin.y += self.insets.top + self.absoluteLocation = (rect, containerSize) + if let shimmerNode = self.placeholderNode { + shimmerNode.updateAbsoluteRect(rect, within: containerSize) + } + } + @objc private func compoundTextButtonPressed() { guard let item else { return @@ -4686,6 +4812,8 @@ public class ChatListItemNode: ItemListRevealOptionsItemNode { switch option.key { case RevealOptionKey.pin.rawValue: switch item.content { + case .loading: + break case .peer: let itemId: EngineChatList.PinnedItem.Id = .peer(index.messageIndex.id.peerId) item.interaction.setItemPinned(itemId, true) @@ -4694,6 +4822,8 @@ public class ChatListItemNode: ItemListRevealOptionsItemNode { } case RevealOptionKey.unpin.rawValue: switch item.content { + case .loading: + break case .peer: let itemId: EngineChatList.PinnedItem.Id = .peer(index.messageIndex.id.peerId) item.interaction.setItemPinned(itemId, false) @@ -4892,6 +5022,8 @@ public class ChatListItemNode: ItemListRevealOptionsItemNode { return } switch item.content { + case .loading: + break case let .peer(peerData): item.interaction.openStories(.peer(peerData.peer.peerId), self) case .groupReference: diff --git a/submodules/ChatListUI/Sources/Node/ChatListNode.swift b/submodules/ChatListUI/Sources/Node/ChatListNode.swift index 177841ad47..49dc0ee934 100644 --- a/submodules/ChatListUI/Sources/Node/ChatListNode.swift +++ b/submodules/ChatListUI/Sources/Node/ChatListNode.swift @@ -1747,7 +1747,7 @@ public final class ChatListNode: ListView { self.push?(controller) }) } else { - let controller = self.context.sharedContext.makePremiumGiftController(context: self.context, source: .chatList(birthdays), completion: nil) + let controller = self.context.sharedContext.makePremiumGiftController(context: self.context, source: .chatList(birthdays), transfer: false, completion: nil) controller.navigationPresentation = .modal self.push?(controller) } @@ -3629,6 +3629,8 @@ public final class ChatListNode: ListView { for item in transition.insertItems { if let item = item.item as? ChatListItem { switch item.content { + case .loading: + break case let .peer(peerData): insertedPeerIds.append(peerData.peer.peerId) case .groupReference: diff --git a/submodules/ChatPresentationInterfaceState/Sources/ChatPresentationInterfaceState.swift b/submodules/ChatPresentationInterfaceState/Sources/ChatPresentationInterfaceState.swift index 33a1422a7f..f594c6a3b8 100644 --- a/submodules/ChatPresentationInterfaceState/Sources/ChatPresentationInterfaceState.swift +++ b/submodules/ChatPresentationInterfaceState/Sources/ChatPresentationInterfaceState.swift @@ -532,6 +532,7 @@ public final class ChatPresentationInterfaceState: Equatable { public let businessIntro: TelegramBusinessIntro? public let hasBirthdayToday: Bool public let adMessage: Message? + public let displayVerificationDescription: Bool public init(chatWallpaper: TelegramWallpaper, theme: PresentationTheme, strings: PresentationStrings, dateTimeFormat: PresentationDateTimeFormat, nameDisplayOrder: PresentationPersonNameOrder, limitsConfiguration: LimitsConfiguration, fontSize: PresentationFontSize, bubbleCorners: PresentationChatBubbleCorners, accountPeerId: PeerId, mode: ChatControllerPresentationMode, chatLocation: ChatLocation, subject: ChatControllerSubject?, peerNearbyData: ChatPeerNearbyData?, greetingData: ChatGreetingData?, pendingUnpinnedAllMessages: Bool, activeGroupCallInfo: ChatActiveGroupCallInfo?, hasActiveGroupCall: Bool, importState: ChatPresentationImportState?, threadData: ThreadData?, isGeneralThreadClosed: Bool?, replyMessage: Message?, accountPeerColor: AccountPeerColor?, businessIntro: TelegramBusinessIntro?) { self.interfaceState = ChatInterfaceState() @@ -617,9 +618,10 @@ public final class ChatPresentationInterfaceState: Equatable { self.businessIntro = businessIntro self.hasBirthdayToday = false self.adMessage = nil + self.displayVerificationDescription = false } - public init(interfaceState: ChatInterfaceState, chatLocation: ChatLocation, renderedPeer: RenderedPeer?, isNotAccessible: Bool, explicitelyCanPinMessages: Bool, contactStatus: ChatContactStatus?, hasBots: Bool, isArchived: Bool, inputTextPanelState: ChatTextInputPanelState, editMessageState: ChatEditInterfaceMessageState?, 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?, historyFilter: HistoryFilter?, displayHistoryFilterAsList: Bool, 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: (String, Data, String?)?, showCommands: Bool, hasBotCommands: Bool, showSendAsPeers: Bool, sendAsPeers: [SendAsPeer]?, botMenuButton: BotMenuButton, showWebView: Bool, currentSendAsPeerId: PeerId?, copyProtectionEnabled: Bool, hasAtLeast3Messages: 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?, savedMessagesTopicPeer: EnginePeer?, hasSearchTags: Bool, isPremiumRequiredForMessaging: Bool, hasSavedChats: Bool, appliedBoosts: Int32?, boostsToUnrestrict: Int32?, businessIntro: TelegramBusinessIntro?, hasBirthdayToday: Bool, adMessage: Message?) { + public init(interfaceState: ChatInterfaceState, chatLocation: ChatLocation, renderedPeer: RenderedPeer?, isNotAccessible: Bool, explicitelyCanPinMessages: Bool, contactStatus: ChatContactStatus?, hasBots: Bool, isArchived: Bool, inputTextPanelState: ChatTextInputPanelState, editMessageState: ChatEditInterfaceMessageState?, 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?, historyFilter: HistoryFilter?, displayHistoryFilterAsList: Bool, 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: (String, Data, String?)?, showCommands: Bool, hasBotCommands: Bool, showSendAsPeers: Bool, sendAsPeers: [SendAsPeer]?, botMenuButton: BotMenuButton, showWebView: Bool, currentSendAsPeerId: PeerId?, copyProtectionEnabled: Bool, hasAtLeast3Messages: 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?, savedMessagesTopicPeer: EnginePeer?, hasSearchTags: Bool, isPremiumRequiredForMessaging: Bool, hasSavedChats: Bool, appliedBoosts: Int32?, boostsToUnrestrict: Int32?, businessIntro: TelegramBusinessIntro?, hasBirthdayToday: Bool, adMessage: Message?, displayVerificationDescription: Bool) { self.interfaceState = interfaceState self.chatLocation = chatLocation self.renderedPeer = renderedPeer @@ -703,6 +705,7 @@ public final class ChatPresentationInterfaceState: Equatable { self.businessIntro = businessIntro self.hasBirthdayToday = hasBirthdayToday self.adMessage = adMessage + self.displayVerificationDescription = displayVerificationDescription } public static func ==(lhs: ChatPresentationInterfaceState, rhs: ChatPresentationInterfaceState) -> Bool { @@ -961,35 +964,38 @@ public final class ChatPresentationInterfaceState: Equatable { if lhs.adMessage?.id != rhs.adMessage?.id { return false } + if lhs.displayVerificationDescription != rhs.displayVerificationDescription { + return false + } return true } 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, 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, historyFilter: self.historyFilter, displayHistoryFilterAsList: self.displayHistoryFilterAsList, 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, hasAtLeast3Messages: self.hasAtLeast3Messages, 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, savedMessagesTopicPeer: self.savedMessagesTopicPeer, hasSearchTags: self.hasSearchTags, isPremiumRequiredForMessaging: self.isPremiumRequiredForMessaging, hasSavedChats: self.hasSavedChats, appliedBoosts: self.appliedBoosts, boostsToUnrestrict: self.boostsToUnrestrict, businessIntro: self.businessIntro, hasBirthdayToday: self.hasBirthdayToday, adMessage: self.adMessage) + 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, 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, historyFilter: self.historyFilter, displayHistoryFilterAsList: self.displayHistoryFilterAsList, 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, hasAtLeast3Messages: self.hasAtLeast3Messages, 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, savedMessagesTopicPeer: self.savedMessagesTopicPeer, hasSearchTags: self.hasSearchTags, isPremiumRequiredForMessaging: self.isPremiumRequiredForMessaging, hasSavedChats: self.hasSavedChats, appliedBoosts: self.appliedBoosts, boostsToUnrestrict: self.boostsToUnrestrict, businessIntro: self.businessIntro, hasBirthdayToday: self.hasBirthdayToday, adMessage: self.adMessage, displayVerificationDescription: self.displayVerificationDescription) } 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, 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, historyFilter: self.historyFilter, displayHistoryFilterAsList: self.displayHistoryFilterAsList, 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, hasAtLeast3Messages: self.hasAtLeast3Messages, 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, savedMessagesTopicPeer: self.savedMessagesTopicPeer, hasSearchTags: self.hasSearchTags, isPremiumRequiredForMessaging: self.isPremiumRequiredForMessaging, hasSavedChats: self.hasSavedChats, appliedBoosts: self.appliedBoosts, boostsToUnrestrict: self.boostsToUnrestrict, businessIntro: self.businessIntro, hasBirthdayToday: self.hasBirthdayToday, adMessage: self.adMessage) + 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, 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, historyFilter: self.historyFilter, displayHistoryFilterAsList: self.displayHistoryFilterAsList, 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, hasAtLeast3Messages: self.hasAtLeast3Messages, 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, savedMessagesTopicPeer: self.savedMessagesTopicPeer, hasSearchTags: self.hasSearchTags, isPremiumRequiredForMessaging: self.isPremiumRequiredForMessaging, hasSavedChats: self.hasSavedChats, appliedBoosts: self.appliedBoosts, boostsToUnrestrict: self.boostsToUnrestrict, businessIntro: self.businessIntro, hasBirthdayToday: self.hasBirthdayToday, adMessage: self.adMessage, displayVerificationDescription: self.displayVerificationDescription) } 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, 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, historyFilter: self.historyFilter, displayHistoryFilterAsList: self.displayHistoryFilterAsList, 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, hasAtLeast3Messages: self.hasAtLeast3Messages, 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, savedMessagesTopicPeer: self.savedMessagesTopicPeer, hasSearchTags: self.hasSearchTags, isPremiumRequiredForMessaging: self.isPremiumRequiredForMessaging, hasSavedChats: self.hasSavedChats, appliedBoosts: self.appliedBoosts, boostsToUnrestrict: self.boostsToUnrestrict, businessIntro: self.businessIntro, hasBirthdayToday: self.hasBirthdayToday, adMessage: self.adMessage) + 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, 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, historyFilter: self.historyFilter, displayHistoryFilterAsList: self.displayHistoryFilterAsList, 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, hasAtLeast3Messages: self.hasAtLeast3Messages, 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, savedMessagesTopicPeer: self.savedMessagesTopicPeer, hasSearchTags: self.hasSearchTags, isPremiumRequiredForMessaging: self.isPremiumRequiredForMessaging, hasSavedChats: self.hasSavedChats, appliedBoosts: self.appliedBoosts, boostsToUnrestrict: self.boostsToUnrestrict, businessIntro: self.businessIntro, hasBirthdayToday: self.hasBirthdayToday, adMessage: self.adMessage, displayVerificationDescription: self.displayVerificationDescription) } 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, 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, historyFilter: self.historyFilter, displayHistoryFilterAsList: self.displayHistoryFilterAsList, 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, hasAtLeast3Messages: self.hasAtLeast3Messages, 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, savedMessagesTopicPeer: self.savedMessagesTopicPeer, hasSearchTags: self.hasSearchTags, isPremiumRequiredForMessaging: self.isPremiumRequiredForMessaging, hasSavedChats: self.hasSavedChats, appliedBoosts: self.appliedBoosts, boostsToUnrestrict: self.boostsToUnrestrict, businessIntro: self.businessIntro, hasBirthdayToday: self.hasBirthdayToday, adMessage: self.adMessage) + 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, 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, historyFilter: self.historyFilter, displayHistoryFilterAsList: self.displayHistoryFilterAsList, 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, hasAtLeast3Messages: self.hasAtLeast3Messages, 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, savedMessagesTopicPeer: self.savedMessagesTopicPeer, hasSearchTags: self.hasSearchTags, isPremiumRequiredForMessaging: self.isPremiumRequiredForMessaging, hasSavedChats: self.hasSavedChats, appliedBoosts: self.appliedBoosts, boostsToUnrestrict: self.boostsToUnrestrict, businessIntro: self.businessIntro, hasBirthdayToday: self.hasBirthdayToday, adMessage: self.adMessage, displayVerificationDescription: self.displayVerificationDescription) } 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, 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, historyFilter: self.historyFilter, displayHistoryFilterAsList: self.displayHistoryFilterAsList, 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, hasAtLeast3Messages: self.hasAtLeast3Messages, 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, savedMessagesTopicPeer: self.savedMessagesTopicPeer, hasSearchTags: self.hasSearchTags, isPremiumRequiredForMessaging: self.isPremiumRequiredForMessaging, hasSavedChats: self.hasSavedChats, appliedBoosts: self.appliedBoosts, boostsToUnrestrict: self.boostsToUnrestrict, businessIntro: self.businessIntro, hasBirthdayToday: self.hasBirthdayToday, adMessage: self.adMessage) + 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, 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, historyFilter: self.historyFilter, displayHistoryFilterAsList: self.displayHistoryFilterAsList, 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, hasAtLeast3Messages: self.hasAtLeast3Messages, 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, savedMessagesTopicPeer: self.savedMessagesTopicPeer, hasSearchTags: self.hasSearchTags, isPremiumRequiredForMessaging: self.isPremiumRequiredForMessaging, hasSavedChats: self.hasSavedChats, appliedBoosts: self.appliedBoosts, boostsToUnrestrict: self.boostsToUnrestrict, businessIntro: self.businessIntro, hasBirthdayToday: self.hasBirthdayToday, adMessage: self.adMessage, displayVerificationDescription: self.displayVerificationDescription) } 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, 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, historyFilter: self.historyFilter, displayHistoryFilterAsList: self.displayHistoryFilterAsList, 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, hasAtLeast3Messages: self.hasAtLeast3Messages, 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, savedMessagesTopicPeer: self.savedMessagesTopicPeer, hasSearchTags: self.hasSearchTags, isPremiumRequiredForMessaging: self.isPremiumRequiredForMessaging, hasSavedChats: self.hasSavedChats, appliedBoosts: self.appliedBoosts, boostsToUnrestrict: self.boostsToUnrestrict, businessIntro: self.businessIntro, hasBirthdayToday: self.hasBirthdayToday, adMessage: self.adMessage) + 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, 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, historyFilter: self.historyFilter, displayHistoryFilterAsList: self.displayHistoryFilterAsList, 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, hasAtLeast3Messages: self.hasAtLeast3Messages, 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, savedMessagesTopicPeer: self.savedMessagesTopicPeer, hasSearchTags: self.hasSearchTags, isPremiumRequiredForMessaging: self.isPremiumRequiredForMessaging, hasSavedChats: self.hasSavedChats, appliedBoosts: self.appliedBoosts, boostsToUnrestrict: self.boostsToUnrestrict, businessIntro: self.businessIntro, hasBirthdayToday: self.hasBirthdayToday, adMessage: self.adMessage, displayVerificationDescription: self.displayVerificationDescription) } 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, 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, historyFilter: self.historyFilter, displayHistoryFilterAsList: self.displayHistoryFilterAsList, 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, hasAtLeast3Messages: self.hasAtLeast3Messages, 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, savedMessagesTopicPeer: self.savedMessagesTopicPeer, hasSearchTags: self.hasSearchTags, isPremiumRequiredForMessaging: self.isPremiumRequiredForMessaging, hasSavedChats: self.hasSavedChats, appliedBoosts: self.appliedBoosts, boostsToUnrestrict: self.boostsToUnrestrict, businessIntro: self.businessIntro, hasBirthdayToday: self.hasBirthdayToday, adMessage: self.adMessage) + 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, 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, historyFilter: self.historyFilter, displayHistoryFilterAsList: self.displayHistoryFilterAsList, 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, hasAtLeast3Messages: self.hasAtLeast3Messages, 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, savedMessagesTopicPeer: self.savedMessagesTopicPeer, hasSearchTags: self.hasSearchTags, isPremiumRequiredForMessaging: self.isPremiumRequiredForMessaging, hasSavedChats: self.hasSavedChats, appliedBoosts: self.appliedBoosts, boostsToUnrestrict: self.boostsToUnrestrict, businessIntro: self.businessIntro, hasBirthdayToday: self.hasBirthdayToday, adMessage: self.adMessage, displayVerificationDescription: self.displayVerificationDescription) } public func updatedInputQueryResult(queryKind: ChatPresentationInputQueryKind, _ f: (ChatPresentationInputQueryResult?) -> ChatPresentationInputQueryResult?) -> ChatPresentationInterfaceState { @@ -1001,279 +1007,283 @@ 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, 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, historyFilter: self.historyFilter, displayHistoryFilterAsList: self.displayHistoryFilterAsList, 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, hasAtLeast3Messages: self.hasAtLeast3Messages, 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, savedMessagesTopicPeer: self.savedMessagesTopicPeer, hasSearchTags: self.hasSearchTags, isPremiumRequiredForMessaging: self.isPremiumRequiredForMessaging, hasSavedChats: self.hasSavedChats, appliedBoosts: self.appliedBoosts, boostsToUnrestrict: self.boostsToUnrestrict, businessIntro: self.businessIntro, hasBirthdayToday: self.hasBirthdayToday, adMessage: self.adMessage) + 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, 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, historyFilter: self.historyFilter, displayHistoryFilterAsList: self.displayHistoryFilterAsList, 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, hasAtLeast3Messages: self.hasAtLeast3Messages, 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, savedMessagesTopicPeer: self.savedMessagesTopicPeer, hasSearchTags: self.hasSearchTags, isPremiumRequiredForMessaging: self.isPremiumRequiredForMessaging, hasSavedChats: self.hasSavedChats, appliedBoosts: self.appliedBoosts, boostsToUnrestrict: self.boostsToUnrestrict, businessIntro: self.businessIntro, hasBirthdayToday: self.hasBirthdayToday, adMessage: self.adMessage, displayVerificationDescription: self.displayVerificationDescription) } 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, 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, historyFilter: self.historyFilter, displayHistoryFilterAsList: self.displayHistoryFilterAsList, 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, hasAtLeast3Messages: self.hasAtLeast3Messages, 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, savedMessagesTopicPeer: self.savedMessagesTopicPeer, hasSearchTags: self.hasSearchTags, isPremiumRequiredForMessaging: self.isPremiumRequiredForMessaging, hasSavedChats: self.hasSavedChats, appliedBoosts: self.appliedBoosts, boostsToUnrestrict: self.boostsToUnrestrict, businessIntro: self.businessIntro, hasBirthdayToday: self.hasBirthdayToday, adMessage: self.adMessage) + 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, 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, historyFilter: self.historyFilter, displayHistoryFilterAsList: self.displayHistoryFilterAsList, 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, hasAtLeast3Messages: self.hasAtLeast3Messages, 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, savedMessagesTopicPeer: self.savedMessagesTopicPeer, hasSearchTags: self.hasSearchTags, isPremiumRequiredForMessaging: self.isPremiumRequiredForMessaging, hasSavedChats: self.hasSavedChats, appliedBoosts: self.appliedBoosts, boostsToUnrestrict: self.boostsToUnrestrict, businessIntro: self.businessIntro, hasBirthdayToday: self.hasBirthdayToday, adMessage: self.adMessage, displayVerificationDescription: self.displayVerificationDescription) } 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, 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, historyFilter: self.historyFilter, displayHistoryFilterAsList: self.displayHistoryFilterAsList, 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, hasAtLeast3Messages: self.hasAtLeast3Messages, 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, savedMessagesTopicPeer: self.savedMessagesTopicPeer, hasSearchTags: self.hasSearchTags, isPremiumRequiredForMessaging: self.isPremiumRequiredForMessaging, hasSavedChats: self.hasSavedChats, appliedBoosts: self.appliedBoosts, boostsToUnrestrict: self.boostsToUnrestrict, businessIntro: self.businessIntro, hasBirthdayToday: self.hasBirthdayToday, adMessage: self.adMessage) + 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, 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, historyFilter: self.historyFilter, displayHistoryFilterAsList: self.displayHistoryFilterAsList, 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, hasAtLeast3Messages: self.hasAtLeast3Messages, 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, savedMessagesTopicPeer: self.savedMessagesTopicPeer, hasSearchTags: self.hasSearchTags, isPremiumRequiredForMessaging: self.isPremiumRequiredForMessaging, hasSavedChats: self.hasSavedChats, appliedBoosts: self.appliedBoosts, boostsToUnrestrict: self.boostsToUnrestrict, businessIntro: self.businessIntro, hasBirthdayToday: self.hasBirthdayToday, adMessage: self.adMessage, displayVerificationDescription: self.displayVerificationDescription) } 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, 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, historyFilter: self.historyFilter, displayHistoryFilterAsList: self.displayHistoryFilterAsList, 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, hasAtLeast3Messages: self.hasAtLeast3Messages, 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, savedMessagesTopicPeer: self.savedMessagesTopicPeer, hasSearchTags: self.hasSearchTags, isPremiumRequiredForMessaging: self.isPremiumRequiredForMessaging, hasSavedChats: self.hasSavedChats, appliedBoosts: self.appliedBoosts, boostsToUnrestrict: self.boostsToUnrestrict, businessIntro: self.businessIntro, hasBirthdayToday: self.hasBirthdayToday, adMessage: self.adMessage) + 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, 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, historyFilter: self.historyFilter, displayHistoryFilterAsList: self.displayHistoryFilterAsList, 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, hasAtLeast3Messages: self.hasAtLeast3Messages, 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, savedMessagesTopicPeer: self.savedMessagesTopicPeer, hasSearchTags: self.hasSearchTags, isPremiumRequiredForMessaging: self.isPremiumRequiredForMessaging, hasSavedChats: self.hasSavedChats, appliedBoosts: self.appliedBoosts, boostsToUnrestrict: self.boostsToUnrestrict, businessIntro: self.businessIntro, hasBirthdayToday: self.hasBirthdayToday, adMessage: self.adMessage, displayVerificationDescription: self.displayVerificationDescription) } 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, 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, historyFilter: self.historyFilter, displayHistoryFilterAsList: self.displayHistoryFilterAsList, 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, hasAtLeast3Messages: self.hasAtLeast3Messages, 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, savedMessagesTopicPeer: self.savedMessagesTopicPeer, hasSearchTags: self.hasSearchTags, isPremiumRequiredForMessaging: self.isPremiumRequiredForMessaging, hasSavedChats: self.hasSavedChats, appliedBoosts: self.appliedBoosts, boostsToUnrestrict: self.boostsToUnrestrict, businessIntro: self.businessIntro, hasBirthdayToday: self.hasBirthdayToday, adMessage: self.adMessage) + 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, 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, historyFilter: self.historyFilter, displayHistoryFilterAsList: self.displayHistoryFilterAsList, 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, hasAtLeast3Messages: self.hasAtLeast3Messages, 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, savedMessagesTopicPeer: self.savedMessagesTopicPeer, hasSearchTags: self.hasSearchTags, isPremiumRequiredForMessaging: self.isPremiumRequiredForMessaging, hasSavedChats: self.hasSavedChats, appliedBoosts: self.appliedBoosts, boostsToUnrestrict: self.boostsToUnrestrict, businessIntro: self.businessIntro, hasBirthdayToday: self.hasBirthdayToday, adMessage: self.adMessage, displayVerificationDescription: self.displayVerificationDescription) } 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, 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, historyFilter: self.historyFilter, displayHistoryFilterAsList: self.displayHistoryFilterAsList, 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, hasAtLeast3Messages: self.hasAtLeast3Messages, 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, savedMessagesTopicPeer: self.savedMessagesTopicPeer, hasSearchTags: self.hasSearchTags, isPremiumRequiredForMessaging: self.isPremiumRequiredForMessaging, hasSavedChats: self.hasSavedChats, appliedBoosts: self.appliedBoosts, boostsToUnrestrict: self.boostsToUnrestrict, businessIntro: self.businessIntro, hasBirthdayToday: self.hasBirthdayToday, adMessage: self.adMessage) + 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, 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, historyFilter: self.historyFilter, displayHistoryFilterAsList: self.displayHistoryFilterAsList, 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, hasAtLeast3Messages: self.hasAtLeast3Messages, 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, savedMessagesTopicPeer: self.savedMessagesTopicPeer, hasSearchTags: self.hasSearchTags, isPremiumRequiredForMessaging: self.isPremiumRequiredForMessaging, hasSavedChats: self.hasSavedChats, appliedBoosts: self.appliedBoosts, boostsToUnrestrict: self.boostsToUnrestrict, businessIntro: self.businessIntro, hasBirthdayToday: self.hasBirthdayToday, adMessage: self.adMessage, displayVerificationDescription: self.displayVerificationDescription) } 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, 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, historyFilter: self.historyFilter, displayHistoryFilterAsList: self.displayHistoryFilterAsList, 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, hasAtLeast3Messages: self.hasAtLeast3Messages, 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, savedMessagesTopicPeer: self.savedMessagesTopicPeer, hasSearchTags: self.hasSearchTags, isPremiumRequiredForMessaging: self.isPremiumRequiredForMessaging, hasSavedChats: self.hasSavedChats, appliedBoosts: self.appliedBoosts, boostsToUnrestrict: self.boostsToUnrestrict, businessIntro: self.businessIntro, hasBirthdayToday: self.hasBirthdayToday, adMessage: self.adMessage) + 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, 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, historyFilter: self.historyFilter, displayHistoryFilterAsList: self.displayHistoryFilterAsList, 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, hasAtLeast3Messages: self.hasAtLeast3Messages, 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, savedMessagesTopicPeer: self.savedMessagesTopicPeer, hasSearchTags: self.hasSearchTags, isPremiumRequiredForMessaging: self.isPremiumRequiredForMessaging, hasSavedChats: self.hasSavedChats, appliedBoosts: self.appliedBoosts, boostsToUnrestrict: self.boostsToUnrestrict, businessIntro: self.businessIntro, hasBirthdayToday: self.hasBirthdayToday, adMessage: self.adMessage, displayVerificationDescription: self.displayVerificationDescription) } 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, 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, historyFilter: self.historyFilter, displayHistoryFilterAsList: self.displayHistoryFilterAsList, 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, hasAtLeast3Messages: self.hasAtLeast3Messages, 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, savedMessagesTopicPeer: self.savedMessagesTopicPeer, hasSearchTags: self.hasSearchTags, isPremiumRequiredForMessaging: self.isPremiumRequiredForMessaging, hasSavedChats: self.hasSavedChats, appliedBoosts: self.appliedBoosts, boostsToUnrestrict: self.boostsToUnrestrict, businessIntro: self.businessIntro, hasBirthdayToday: self.hasBirthdayToday, adMessage: self.adMessage) + 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, 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, historyFilter: self.historyFilter, displayHistoryFilterAsList: self.displayHistoryFilterAsList, 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, hasAtLeast3Messages: self.hasAtLeast3Messages, 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, savedMessagesTopicPeer: self.savedMessagesTopicPeer, hasSearchTags: self.hasSearchTags, isPremiumRequiredForMessaging: self.isPremiumRequiredForMessaging, hasSavedChats: self.hasSavedChats, appliedBoosts: self.appliedBoosts, boostsToUnrestrict: self.boostsToUnrestrict, businessIntro: self.businessIntro, hasBirthdayToday: self.hasBirthdayToday, adMessage: self.adMessage, displayVerificationDescription: self.displayVerificationDescription) } 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, 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, historyFilter: self.historyFilter, displayHistoryFilterAsList: self.displayHistoryFilterAsList, 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, hasAtLeast3Messages: self.hasAtLeast3Messages, 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, savedMessagesTopicPeer: self.savedMessagesTopicPeer, hasSearchTags: self.hasSearchTags, isPremiumRequiredForMessaging: self.isPremiumRequiredForMessaging, hasSavedChats: self.hasSavedChats, appliedBoosts: self.appliedBoosts, boostsToUnrestrict: self.boostsToUnrestrict, businessIntro: self.businessIntro, hasBirthdayToday: self.hasBirthdayToday, adMessage: self.adMessage) + 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, 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, historyFilter: self.historyFilter, displayHistoryFilterAsList: self.displayHistoryFilterAsList, 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, hasAtLeast3Messages: self.hasAtLeast3Messages, 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, savedMessagesTopicPeer: self.savedMessagesTopicPeer, hasSearchTags: self.hasSearchTags, isPremiumRequiredForMessaging: self.isPremiumRequiredForMessaging, hasSavedChats: self.hasSavedChats, appliedBoosts: self.appliedBoosts, boostsToUnrestrict: self.boostsToUnrestrict, businessIntro: self.businessIntro, hasBirthdayToday: self.hasBirthdayToday, adMessage: self.adMessage, displayVerificationDescription: self.displayVerificationDescription) } 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, 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, historyFilter: self.historyFilter, displayHistoryFilterAsList: self.displayHistoryFilterAsList, 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, hasAtLeast3Messages: self.hasAtLeast3Messages, 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, savedMessagesTopicPeer: self.savedMessagesTopicPeer, hasSearchTags: self.hasSearchTags, isPremiumRequiredForMessaging: self.isPremiumRequiredForMessaging, hasSavedChats: self.hasSavedChats, appliedBoosts: self.appliedBoosts, boostsToUnrestrict: self.boostsToUnrestrict, businessIntro: self.businessIntro, hasBirthdayToday: self.hasBirthdayToday, adMessage: self.adMessage) + 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, 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, historyFilter: self.historyFilter, displayHistoryFilterAsList: self.displayHistoryFilterAsList, 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, hasAtLeast3Messages: self.hasAtLeast3Messages, 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, savedMessagesTopicPeer: self.savedMessagesTopicPeer, hasSearchTags: self.hasSearchTags, isPremiumRequiredForMessaging: self.isPremiumRequiredForMessaging, hasSavedChats: self.hasSavedChats, appliedBoosts: self.appliedBoosts, boostsToUnrestrict: self.boostsToUnrestrict, businessIntro: self.businessIntro, hasBirthdayToday: self.hasBirthdayToday, adMessage: self.adMessage, displayVerificationDescription: self.displayVerificationDescription) } 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, 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, historyFilter: self.historyFilter, displayHistoryFilterAsList: self.displayHistoryFilterAsList, 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, hasAtLeast3Messages: self.hasAtLeast3Messages, 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, savedMessagesTopicPeer: self.savedMessagesTopicPeer, hasSearchTags: self.hasSearchTags, isPremiumRequiredForMessaging: self.isPremiumRequiredForMessaging, hasSavedChats: self.hasSavedChats, appliedBoosts: self.appliedBoosts, boostsToUnrestrict: self.boostsToUnrestrict, businessIntro: self.businessIntro, hasBirthdayToday: self.hasBirthdayToday, adMessage: self.adMessage) + 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, 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, historyFilter: self.historyFilter, displayHistoryFilterAsList: self.displayHistoryFilterAsList, 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, hasAtLeast3Messages: self.hasAtLeast3Messages, 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, savedMessagesTopicPeer: self.savedMessagesTopicPeer, hasSearchTags: self.hasSearchTags, isPremiumRequiredForMessaging: self.isPremiumRequiredForMessaging, hasSavedChats: self.hasSavedChats, appliedBoosts: self.appliedBoosts, boostsToUnrestrict: self.boostsToUnrestrict, businessIntro: self.businessIntro, hasBirthdayToday: self.hasBirthdayToday, adMessage: self.adMessage, displayVerificationDescription: self.displayVerificationDescription) } 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, 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, historyFilter: self.historyFilter, displayHistoryFilterAsList: self.displayHistoryFilterAsList, 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, hasAtLeast3Messages: self.hasAtLeast3Messages, 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, savedMessagesTopicPeer: self.savedMessagesTopicPeer, hasSearchTags: self.hasSearchTags, isPremiumRequiredForMessaging: self.isPremiumRequiredForMessaging, hasSavedChats: self.hasSavedChats, appliedBoosts: self.appliedBoosts, boostsToUnrestrict: self.boostsToUnrestrict, businessIntro: self.businessIntro, hasBirthdayToday: self.hasBirthdayToday, adMessage: self.adMessage) + 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, 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, historyFilter: self.historyFilter, displayHistoryFilterAsList: self.displayHistoryFilterAsList, 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, hasAtLeast3Messages: self.hasAtLeast3Messages, 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, savedMessagesTopicPeer: self.savedMessagesTopicPeer, hasSearchTags: self.hasSearchTags, isPremiumRequiredForMessaging: self.isPremiumRequiredForMessaging, hasSavedChats: self.hasSavedChats, appliedBoosts: self.appliedBoosts, boostsToUnrestrict: self.boostsToUnrestrict, businessIntro: self.businessIntro, hasBirthdayToday: self.hasBirthdayToday, adMessage: self.adMessage, displayVerificationDescription: self.displayVerificationDescription) } 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, 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, historyFilter: self.historyFilter, displayHistoryFilterAsList: self.displayHistoryFilterAsList, 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, hasAtLeast3Messages: self.hasAtLeast3Messages, 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, savedMessagesTopicPeer: self.savedMessagesTopicPeer, hasSearchTags: self.hasSearchTags, isPremiumRequiredForMessaging: self.isPremiumRequiredForMessaging, hasSavedChats: self.hasSavedChats, appliedBoosts: self.appliedBoosts, boostsToUnrestrict: self.boostsToUnrestrict, businessIntro: self.businessIntro, hasBirthdayToday: self.hasBirthdayToday, adMessage: self.adMessage) + 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, 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, historyFilter: self.historyFilter, displayHistoryFilterAsList: self.displayHistoryFilterAsList, 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, hasAtLeast3Messages: self.hasAtLeast3Messages, 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, savedMessagesTopicPeer: self.savedMessagesTopicPeer, hasSearchTags: self.hasSearchTags, isPremiumRequiredForMessaging: self.isPremiumRequiredForMessaging, hasSavedChats: self.hasSavedChats, appliedBoosts: self.appliedBoosts, boostsToUnrestrict: self.boostsToUnrestrict, businessIntro: self.businessIntro, hasBirthdayToday: self.hasBirthdayToday, adMessage: self.adMessage, displayVerificationDescription: self.displayVerificationDescription) } 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, 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, historyFilter: self.historyFilter, displayHistoryFilterAsList: self.displayHistoryFilterAsList, 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, hasAtLeast3Messages: self.hasAtLeast3Messages, 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, savedMessagesTopicPeer: self.savedMessagesTopicPeer, hasSearchTags: self.hasSearchTags, isPremiumRequiredForMessaging: self.isPremiumRequiredForMessaging, hasSavedChats: self.hasSavedChats, appliedBoosts: self.appliedBoosts, boostsToUnrestrict: self.boostsToUnrestrict, businessIntro: self.businessIntro, hasBirthdayToday: self.hasBirthdayToday, adMessage: self.adMessage) + 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, 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, historyFilter: self.historyFilter, displayHistoryFilterAsList: self.displayHistoryFilterAsList, 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, hasAtLeast3Messages: self.hasAtLeast3Messages, 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, savedMessagesTopicPeer: self.savedMessagesTopicPeer, hasSearchTags: self.hasSearchTags, isPremiumRequiredForMessaging: self.isPremiumRequiredForMessaging, hasSavedChats: self.hasSavedChats, appliedBoosts: self.appliedBoosts, boostsToUnrestrict: self.boostsToUnrestrict, businessIntro: self.businessIntro, hasBirthdayToday: self.hasBirthdayToday, adMessage: self.adMessage, displayVerificationDescription: self.displayVerificationDescription) } 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, 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, historyFilter: self.historyFilter, displayHistoryFilterAsList: self.displayHistoryFilterAsList, 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, hasAtLeast3Messages: self.hasAtLeast3Messages, 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, savedMessagesTopicPeer: self.savedMessagesTopicPeer, hasSearchTags: self.hasSearchTags, isPremiumRequiredForMessaging: self.isPremiumRequiredForMessaging, hasSavedChats: self.hasSavedChats, appliedBoosts: self.appliedBoosts, boostsToUnrestrict: self.boostsToUnrestrict, businessIntro: self.businessIntro, hasBirthdayToday: self.hasBirthdayToday, adMessage: self.adMessage) + 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, 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, historyFilter: self.historyFilter, displayHistoryFilterAsList: self.displayHistoryFilterAsList, 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, hasAtLeast3Messages: self.hasAtLeast3Messages, 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, savedMessagesTopicPeer: self.savedMessagesTopicPeer, hasSearchTags: self.hasSearchTags, isPremiumRequiredForMessaging: self.isPremiumRequiredForMessaging, hasSavedChats: self.hasSavedChats, appliedBoosts: self.appliedBoosts, boostsToUnrestrict: self.boostsToUnrestrict, businessIntro: self.businessIntro, hasBirthdayToday: self.hasBirthdayToday, adMessage: self.adMessage, displayVerificationDescription: self.displayVerificationDescription) } 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, 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, historyFilter: self.historyFilter, displayHistoryFilterAsList: self.displayHistoryFilterAsList, 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, hasAtLeast3Messages: self.hasAtLeast3Messages, 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, savedMessagesTopicPeer: self.savedMessagesTopicPeer, hasSearchTags: self.hasSearchTags, isPremiumRequiredForMessaging: self.isPremiumRequiredForMessaging, hasSavedChats: self.hasSavedChats, appliedBoosts: self.appliedBoosts, boostsToUnrestrict: self.boostsToUnrestrict, businessIntro: self.businessIntro, hasBirthdayToday: self.hasBirthdayToday, adMessage: self.adMessage) + 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, 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, historyFilter: self.historyFilter, displayHistoryFilterAsList: self.displayHistoryFilterAsList, 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, hasAtLeast3Messages: self.hasAtLeast3Messages, 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, savedMessagesTopicPeer: self.savedMessagesTopicPeer, hasSearchTags: self.hasSearchTags, isPremiumRequiredForMessaging: self.isPremiumRequiredForMessaging, hasSavedChats: self.hasSavedChats, appliedBoosts: self.appliedBoosts, boostsToUnrestrict: self.boostsToUnrestrict, businessIntro: self.businessIntro, hasBirthdayToday: self.hasBirthdayToday, adMessage: self.adMessage, displayVerificationDescription: self.displayVerificationDescription) } 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, 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, historyFilter: self.historyFilter, displayHistoryFilterAsList: self.displayHistoryFilterAsList, 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, hasAtLeast3Messages: self.hasAtLeast3Messages, 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, savedMessagesTopicPeer: self.savedMessagesTopicPeer, hasSearchTags: self.hasSearchTags, isPremiumRequiredForMessaging: self.isPremiumRequiredForMessaging, hasSavedChats: self.hasSavedChats, appliedBoosts: self.appliedBoosts, boostsToUnrestrict: self.boostsToUnrestrict, businessIntro: self.businessIntro, hasBirthdayToday: self.hasBirthdayToday, adMessage: self.adMessage) + 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, 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, historyFilter: self.historyFilter, displayHistoryFilterAsList: self.displayHistoryFilterAsList, 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, hasAtLeast3Messages: self.hasAtLeast3Messages, 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, savedMessagesTopicPeer: self.savedMessagesTopicPeer, hasSearchTags: self.hasSearchTags, isPremiumRequiredForMessaging: self.isPremiumRequiredForMessaging, hasSavedChats: self.hasSavedChats, appliedBoosts: self.appliedBoosts, boostsToUnrestrict: self.boostsToUnrestrict, businessIntro: self.businessIntro, hasBirthdayToday: self.hasBirthdayToday, adMessage: self.adMessage, displayVerificationDescription: self.displayVerificationDescription) } 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, 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, historyFilter: self.historyFilter, displayHistoryFilterAsList: self.displayHistoryFilterAsList, 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, hasAtLeast3Messages: self.hasAtLeast3Messages, 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, savedMessagesTopicPeer: self.savedMessagesTopicPeer, hasSearchTags: self.hasSearchTags, isPremiumRequiredForMessaging: self.isPremiumRequiredForMessaging, hasSavedChats: self.hasSavedChats, appliedBoosts: self.appliedBoosts, boostsToUnrestrict: self.boostsToUnrestrict, businessIntro: self.businessIntro, hasBirthdayToday: self.hasBirthdayToday, adMessage: self.adMessage) + 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, 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, historyFilter: self.historyFilter, displayHistoryFilterAsList: self.displayHistoryFilterAsList, 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, hasAtLeast3Messages: self.hasAtLeast3Messages, 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, savedMessagesTopicPeer: self.savedMessagesTopicPeer, hasSearchTags: self.hasSearchTags, isPremiumRequiredForMessaging: self.isPremiumRequiredForMessaging, hasSavedChats: self.hasSavedChats, appliedBoosts: self.appliedBoosts, boostsToUnrestrict: self.boostsToUnrestrict, businessIntro: self.businessIntro, hasBirthdayToday: self.hasBirthdayToday, adMessage: self.adMessage, displayVerificationDescription: self.displayVerificationDescription) } 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, 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, historyFilter: self.historyFilter, displayHistoryFilterAsList: self.displayHistoryFilterAsList, 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, hasAtLeast3Messages: self.hasAtLeast3Messages, 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, savedMessagesTopicPeer: self.savedMessagesTopicPeer, hasSearchTags: self.hasSearchTags, isPremiumRequiredForMessaging: self.isPremiumRequiredForMessaging, hasSavedChats: self.hasSavedChats, appliedBoosts: self.appliedBoosts, boostsToUnrestrict: self.boostsToUnrestrict, businessIntro: self.businessIntro, hasBirthdayToday: self.hasBirthdayToday, adMessage: self.adMessage) + 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, 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, historyFilter: self.historyFilter, displayHistoryFilterAsList: self.displayHistoryFilterAsList, 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, hasAtLeast3Messages: self.hasAtLeast3Messages, 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, savedMessagesTopicPeer: self.savedMessagesTopicPeer, hasSearchTags: self.hasSearchTags, isPremiumRequiredForMessaging: self.isPremiumRequiredForMessaging, hasSavedChats: self.hasSavedChats, appliedBoosts: self.appliedBoosts, boostsToUnrestrict: self.boostsToUnrestrict, businessIntro: self.businessIntro, hasBirthdayToday: self.hasBirthdayToday, adMessage: self.adMessage, displayVerificationDescription: self.displayVerificationDescription) } 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, 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, historyFilter: self.historyFilter, displayHistoryFilterAsList: self.displayHistoryFilterAsList, 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, hasAtLeast3Messages: self.hasAtLeast3Messages, 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, savedMessagesTopicPeer: self.savedMessagesTopicPeer, hasSearchTags: self.hasSearchTags, isPremiumRequiredForMessaging: self.isPremiumRequiredForMessaging, hasSavedChats: self.hasSavedChats, appliedBoosts: self.appliedBoosts, boostsToUnrestrict: self.boostsToUnrestrict, businessIntro: self.businessIntro, hasBirthdayToday: self.hasBirthdayToday, adMessage: self.adMessage) + 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, 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, historyFilter: self.historyFilter, displayHistoryFilterAsList: self.displayHistoryFilterAsList, 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, hasAtLeast3Messages: self.hasAtLeast3Messages, 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, savedMessagesTopicPeer: self.savedMessagesTopicPeer, hasSearchTags: self.hasSearchTags, isPremiumRequiredForMessaging: self.isPremiumRequiredForMessaging, hasSavedChats: self.hasSavedChats, appliedBoosts: self.appliedBoosts, boostsToUnrestrict: self.boostsToUnrestrict, businessIntro: self.businessIntro, hasBirthdayToday: self.hasBirthdayToday, adMessage: self.adMessage, displayVerificationDescription: self.displayVerificationDescription) } 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, 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), historyFilter: self.historyFilter, displayHistoryFilterAsList: self.displayHistoryFilterAsList, 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, hasAtLeast3Messages: self.hasAtLeast3Messages, 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, savedMessagesTopicPeer: self.savedMessagesTopicPeer, hasSearchTags: self.hasSearchTags, isPremiumRequiredForMessaging: self.isPremiumRequiredForMessaging, hasSavedChats: self.hasSavedChats, appliedBoosts: self.appliedBoosts, boostsToUnrestrict: self.boostsToUnrestrict, businessIntro: self.businessIntro, hasBirthdayToday: self.hasBirthdayToday, adMessage: self.adMessage) + 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, 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), historyFilter: self.historyFilter, displayHistoryFilterAsList: self.displayHistoryFilterAsList, 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, hasAtLeast3Messages: self.hasAtLeast3Messages, 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, savedMessagesTopicPeer: self.savedMessagesTopicPeer, hasSearchTags: self.hasSearchTags, isPremiumRequiredForMessaging: self.isPremiumRequiredForMessaging, hasSavedChats: self.hasSavedChats, appliedBoosts: self.appliedBoosts, boostsToUnrestrict: self.boostsToUnrestrict, businessIntro: self.businessIntro, hasBirthdayToday: self.hasBirthdayToday, adMessage: self.adMessage, displayVerificationDescription: self.displayVerificationDescription) } public func updatedHistoryFilter(_ historyFilter: HistoryFilter?) -> 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, 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, historyFilter: historyFilter, displayHistoryFilterAsList: self.displayHistoryFilterAsList, 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, hasAtLeast3Messages: self.hasAtLeast3Messages, 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, savedMessagesTopicPeer: self.savedMessagesTopicPeer, hasSearchTags: self.hasSearchTags, isPremiumRequiredForMessaging: self.isPremiumRequiredForMessaging, hasSavedChats: self.hasSavedChats, appliedBoosts: self.appliedBoosts, boostsToUnrestrict: self.boostsToUnrestrict, businessIntro: self.businessIntro, hasBirthdayToday: self.hasBirthdayToday, adMessage: self.adMessage) + 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, 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, historyFilter: historyFilter, displayHistoryFilterAsList: self.displayHistoryFilterAsList, 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, hasAtLeast3Messages: self.hasAtLeast3Messages, 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, savedMessagesTopicPeer: self.savedMessagesTopicPeer, hasSearchTags: self.hasSearchTags, isPremiumRequiredForMessaging: self.isPremiumRequiredForMessaging, hasSavedChats: self.hasSavedChats, appliedBoosts: self.appliedBoosts, boostsToUnrestrict: self.boostsToUnrestrict, businessIntro: self.businessIntro, hasBirthdayToday: self.hasBirthdayToday, adMessage: self.adMessage, displayVerificationDescription: self.displayVerificationDescription) } public func updatedDisplayHistoryFilterAsList(_ displayHistoryFilterAsList: 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, 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, historyFilter: self.historyFilter, displayHistoryFilterAsList: displayHistoryFilterAsList, 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, hasAtLeast3Messages: self.hasAtLeast3Messages, 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, savedMessagesTopicPeer: self.savedMessagesTopicPeer, hasSearchTags: self.hasSearchTags, isPremiumRequiredForMessaging: self.isPremiumRequiredForMessaging, hasSavedChats: self.hasSavedChats, appliedBoosts: self.appliedBoosts, boostsToUnrestrict: self.boostsToUnrestrict, businessIntro: self.businessIntro, hasBirthdayToday: self.hasBirthdayToday, adMessage: self.adMessage) + 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, 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, historyFilter: self.historyFilter, displayHistoryFilterAsList: displayHistoryFilterAsList, 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, hasAtLeast3Messages: self.hasAtLeast3Messages, 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, savedMessagesTopicPeer: self.savedMessagesTopicPeer, hasSearchTags: self.hasSearchTags, isPremiumRequiredForMessaging: self.isPremiumRequiredForMessaging, hasSavedChats: self.hasSavedChats, appliedBoosts: self.appliedBoosts, boostsToUnrestrict: self.boostsToUnrestrict, businessIntro: self.businessIntro, hasBirthdayToday: self.hasBirthdayToday, adMessage: self.adMessage, displayVerificationDescription: self.displayVerificationDescription) } 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, 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, historyFilter: self.historyFilter, displayHistoryFilterAsList: self.displayHistoryFilterAsList, 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, hasAtLeast3Messages: self.hasAtLeast3Messages, 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, savedMessagesTopicPeer: self.savedMessagesTopicPeer, hasSearchTags: self.hasSearchTags, isPremiumRequiredForMessaging: self.isPremiumRequiredForMessaging, hasSavedChats: self.hasSavedChats, appliedBoosts: self.appliedBoosts, boostsToUnrestrict: self.boostsToUnrestrict, businessIntro: self.businessIntro, hasBirthdayToday: self.hasBirthdayToday, adMessage: self.adMessage) + 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, 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, historyFilter: self.historyFilter, displayHistoryFilterAsList: self.displayHistoryFilterAsList, 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, hasAtLeast3Messages: self.hasAtLeast3Messages, 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, savedMessagesTopicPeer: self.savedMessagesTopicPeer, hasSearchTags: self.hasSearchTags, isPremiumRequiredForMessaging: self.isPremiumRequiredForMessaging, hasSavedChats: self.hasSavedChats, appliedBoosts: self.appliedBoosts, boostsToUnrestrict: self.boostsToUnrestrict, businessIntro: self.businessIntro, hasBirthdayToday: self.hasBirthdayToday, adMessage: self.adMessage, displayVerificationDescription: self.displayVerificationDescription) } 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, 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, historyFilter: self.historyFilter, displayHistoryFilterAsList: self.displayHistoryFilterAsList, 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, hasAtLeast3Messages: self.hasAtLeast3Messages, 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, savedMessagesTopicPeer: self.savedMessagesTopicPeer, hasSearchTags: self.hasSearchTags, isPremiumRequiredForMessaging: self.isPremiumRequiredForMessaging, hasSavedChats: self.hasSavedChats, appliedBoosts: self.appliedBoosts, boostsToUnrestrict: self.boostsToUnrestrict, businessIntro: self.businessIntro, hasBirthdayToday: self.hasBirthdayToday, adMessage: self.adMessage) + 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, 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, historyFilter: self.historyFilter, displayHistoryFilterAsList: self.displayHistoryFilterAsList, 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, hasAtLeast3Messages: self.hasAtLeast3Messages, 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, savedMessagesTopicPeer: self.savedMessagesTopicPeer, hasSearchTags: self.hasSearchTags, isPremiumRequiredForMessaging: self.isPremiumRequiredForMessaging, hasSavedChats: self.hasSavedChats, appliedBoosts: self.appliedBoosts, boostsToUnrestrict: self.boostsToUnrestrict, businessIntro: self.businessIntro, hasBirthdayToday: self.hasBirthdayToday, adMessage: self.adMessage, displayVerificationDescription: self.displayVerificationDescription) } 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, 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, historyFilter: self.historyFilter, displayHistoryFilterAsList: self.displayHistoryFilterAsList, 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, hasAtLeast3Messages: self.hasAtLeast3Messages, 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, savedMessagesTopicPeer: self.savedMessagesTopicPeer, hasSearchTags: self.hasSearchTags, isPremiumRequiredForMessaging: self.isPremiumRequiredForMessaging, hasSavedChats: self.hasSavedChats, appliedBoosts: self.appliedBoosts, boostsToUnrestrict: self.boostsToUnrestrict, businessIntro: self.businessIntro, hasBirthdayToday: self.hasBirthdayToday, adMessage: self.adMessage) + 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, 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, historyFilter: self.historyFilter, displayHistoryFilterAsList: self.displayHistoryFilterAsList, 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, hasAtLeast3Messages: self.hasAtLeast3Messages, 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, savedMessagesTopicPeer: self.savedMessagesTopicPeer, hasSearchTags: self.hasSearchTags, isPremiumRequiredForMessaging: self.isPremiumRequiredForMessaging, hasSavedChats: self.hasSavedChats, appliedBoosts: self.appliedBoosts, boostsToUnrestrict: self.boostsToUnrestrict, businessIntro: self.businessIntro, hasBirthdayToday: self.hasBirthdayToday, adMessage: self.adMessage, displayVerificationDescription: self.displayVerificationDescription) } 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, 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, historyFilter: self.historyFilter, displayHistoryFilterAsList: self.displayHistoryFilterAsList, 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, hasAtLeast3Messages: self.hasAtLeast3Messages, 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, savedMessagesTopicPeer: self.savedMessagesTopicPeer, hasSearchTags: self.hasSearchTags, isPremiumRequiredForMessaging: self.isPremiumRequiredForMessaging, hasSavedChats: self.hasSavedChats, appliedBoosts: self.appliedBoosts, boostsToUnrestrict: self.boostsToUnrestrict, businessIntro: self.businessIntro, hasBirthdayToday: self.hasBirthdayToday, adMessage: self.adMessage) + 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, 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, historyFilter: self.historyFilter, displayHistoryFilterAsList: self.displayHistoryFilterAsList, 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, hasAtLeast3Messages: self.hasAtLeast3Messages, 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, savedMessagesTopicPeer: self.savedMessagesTopicPeer, hasSearchTags: self.hasSearchTags, isPremiumRequiredForMessaging: self.isPremiumRequiredForMessaging, hasSavedChats: self.hasSavedChats, appliedBoosts: self.appliedBoosts, boostsToUnrestrict: self.boostsToUnrestrict, businessIntro: self.businessIntro, hasBirthdayToday: self.hasBirthdayToday, adMessage: self.adMessage, displayVerificationDescription: self.displayVerificationDescription) } 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, 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, historyFilter: self.historyFilter, displayHistoryFilterAsList: self.displayHistoryFilterAsList, 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, hasAtLeast3Messages: self.hasAtLeast3Messages, 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, savedMessagesTopicPeer: self.savedMessagesTopicPeer, hasSearchTags: self.hasSearchTags, isPremiumRequiredForMessaging: self.isPremiumRequiredForMessaging, hasSavedChats: self.hasSavedChats, appliedBoosts: self.appliedBoosts, boostsToUnrestrict: self.boostsToUnrestrict, businessIntro: self.businessIntro, hasBirthdayToday: self.hasBirthdayToday, adMessage: self.adMessage) + 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, 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, historyFilter: self.historyFilter, displayHistoryFilterAsList: self.displayHistoryFilterAsList, 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, hasAtLeast3Messages: self.hasAtLeast3Messages, 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, savedMessagesTopicPeer: self.savedMessagesTopicPeer, hasSearchTags: self.hasSearchTags, isPremiumRequiredForMessaging: self.isPremiumRequiredForMessaging, hasSavedChats: self.hasSavedChats, appliedBoosts: self.appliedBoosts, boostsToUnrestrict: self.boostsToUnrestrict, businessIntro: self.businessIntro, hasBirthdayToday: self.hasBirthdayToday, adMessage: self.adMessage, displayVerificationDescription: self.displayVerificationDescription) } 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, 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, historyFilter: self.historyFilter, displayHistoryFilterAsList: self.displayHistoryFilterAsList, 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, hasAtLeast3Messages: self.hasAtLeast3Messages, 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, savedMessagesTopicPeer: self.savedMessagesTopicPeer, hasSearchTags: self.hasSearchTags, isPremiumRequiredForMessaging: self.isPremiumRequiredForMessaging, hasSavedChats: self.hasSavedChats, appliedBoosts: self.appliedBoosts, boostsToUnrestrict: self.boostsToUnrestrict, businessIntro: self.businessIntro, hasBirthdayToday: self.hasBirthdayToday, adMessage: self.adMessage) + 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, 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, historyFilter: self.historyFilter, displayHistoryFilterAsList: self.displayHistoryFilterAsList, 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, hasAtLeast3Messages: self.hasAtLeast3Messages, 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, savedMessagesTopicPeer: self.savedMessagesTopicPeer, hasSearchTags: self.hasSearchTags, isPremiumRequiredForMessaging: self.isPremiumRequiredForMessaging, hasSavedChats: self.hasSavedChats, appliedBoosts: self.appliedBoosts, boostsToUnrestrict: self.boostsToUnrestrict, businessIntro: self.businessIntro, hasBirthdayToday: self.hasBirthdayToday, adMessage: self.adMessage, displayVerificationDescription: self.displayVerificationDescription) } 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, 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, historyFilter: self.historyFilter, displayHistoryFilterAsList: self.displayHistoryFilterAsList, 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, hasAtLeast3Messages: self.hasAtLeast3Messages, 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, savedMessagesTopicPeer: self.savedMessagesTopicPeer, hasSearchTags: self.hasSearchTags, isPremiumRequiredForMessaging: self.isPremiumRequiredForMessaging, hasSavedChats: self.hasSavedChats, appliedBoosts: self.appliedBoosts, boostsToUnrestrict: self.boostsToUnrestrict, businessIntro: self.businessIntro, hasBirthdayToday: self.hasBirthdayToday, adMessage: self.adMessage) + 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, 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, historyFilter: self.historyFilter, displayHistoryFilterAsList: self.displayHistoryFilterAsList, 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, hasAtLeast3Messages: self.hasAtLeast3Messages, 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, savedMessagesTopicPeer: self.savedMessagesTopicPeer, hasSearchTags: self.hasSearchTags, isPremiumRequiredForMessaging: self.isPremiumRequiredForMessaging, hasSavedChats: self.hasSavedChats, appliedBoosts: self.appliedBoosts, boostsToUnrestrict: self.boostsToUnrestrict, businessIntro: self.businessIntro, hasBirthdayToday: self.hasBirthdayToday, adMessage: self.adMessage, displayVerificationDescription: self.displayVerificationDescription) } 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, 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, historyFilter: self.historyFilter, displayHistoryFilterAsList: self.displayHistoryFilterAsList, 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, hasAtLeast3Messages: self.hasAtLeast3Messages, 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, savedMessagesTopicPeer: self.savedMessagesTopicPeer, hasSearchTags: self.hasSearchTags, isPremiumRequiredForMessaging: self.isPremiumRequiredForMessaging, hasSavedChats: self.hasSavedChats, appliedBoosts: self.appliedBoosts, boostsToUnrestrict: self.boostsToUnrestrict, businessIntro: self.businessIntro, hasBirthdayToday: self.hasBirthdayToday, adMessage: self.adMessage) + 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, 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, historyFilter: self.historyFilter, displayHistoryFilterAsList: self.displayHistoryFilterAsList, 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, hasAtLeast3Messages: self.hasAtLeast3Messages, 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, savedMessagesTopicPeer: self.savedMessagesTopicPeer, hasSearchTags: self.hasSearchTags, isPremiumRequiredForMessaging: self.isPremiumRequiredForMessaging, hasSavedChats: self.hasSavedChats, appliedBoosts: self.appliedBoosts, boostsToUnrestrict: self.boostsToUnrestrict, businessIntro: self.businessIntro, hasBirthdayToday: self.hasBirthdayToday, adMessage: self.adMessage, displayVerificationDescription: self.displayVerificationDescription) } public func updatedSubject(_ subject: ChatControllerSubject?) -> 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, 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, historyFilter: self.historyFilter, displayHistoryFilterAsList: self.displayHistoryFilterAsList, 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: 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, hasAtLeast3Messages: self.hasAtLeast3Messages, 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, savedMessagesTopicPeer: self.savedMessagesTopicPeer, hasSearchTags: self.hasSearchTags, isPremiumRequiredForMessaging: self.isPremiumRequiredForMessaging, hasSavedChats: self.hasSavedChats, appliedBoosts: self.appliedBoosts, boostsToUnrestrict: self.boostsToUnrestrict, businessIntro: self.businessIntro, hasBirthdayToday: self.hasBirthdayToday, adMessage: self.adMessage) + 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, 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, historyFilter: self.historyFilter, displayHistoryFilterAsList: self.displayHistoryFilterAsList, 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: 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, hasAtLeast3Messages: self.hasAtLeast3Messages, 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, savedMessagesTopicPeer: self.savedMessagesTopicPeer, hasSearchTags: self.hasSearchTags, isPremiumRequiredForMessaging: self.isPremiumRequiredForMessaging, hasSavedChats: self.hasSavedChats, appliedBoosts: self.appliedBoosts, boostsToUnrestrict: self.boostsToUnrestrict, businessIntro: self.businessIntro, hasBirthdayToday: self.hasBirthdayToday, adMessage: self.adMessage, displayVerificationDescription: self.displayVerificationDescription) } 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, 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, historyFilter: self.historyFilter, displayHistoryFilterAsList: self.displayHistoryFilterAsList, 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, hasAtLeast3Messages: self.hasAtLeast3Messages, 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, savedMessagesTopicPeer: self.savedMessagesTopicPeer, hasSearchTags: self.hasSearchTags, isPremiumRequiredForMessaging: self.isPremiumRequiredForMessaging, hasSavedChats: self.hasSavedChats, appliedBoosts: self.appliedBoosts, boostsToUnrestrict: self.boostsToUnrestrict, businessIntro: self.businessIntro, hasBirthdayToday: self.hasBirthdayToday, adMessage: self.adMessage) + 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, 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, historyFilter: self.historyFilter, displayHistoryFilterAsList: self.displayHistoryFilterAsList, 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, hasAtLeast3Messages: self.hasAtLeast3Messages, 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, savedMessagesTopicPeer: self.savedMessagesTopicPeer, hasSearchTags: self.hasSearchTags, isPremiumRequiredForMessaging: self.isPremiumRequiredForMessaging, hasSavedChats: self.hasSavedChats, appliedBoosts: self.appliedBoosts, boostsToUnrestrict: self.boostsToUnrestrict, businessIntro: self.businessIntro, hasBirthdayToday: self.hasBirthdayToday, adMessage: self.adMessage, displayVerificationDescription: self.displayVerificationDescription) } 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, 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, historyFilter: self.historyFilter, displayHistoryFilterAsList: self.displayHistoryFilterAsList, 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, hasAtLeast3Messages: self.hasAtLeast3Messages, 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, savedMessagesTopicPeer: self.savedMessagesTopicPeer, hasSearchTags: self.hasSearchTags, isPremiumRequiredForMessaging: self.isPremiumRequiredForMessaging, hasSavedChats: self.hasSavedChats, appliedBoosts: self.appliedBoosts, boostsToUnrestrict: self.boostsToUnrestrict, businessIntro: self.businessIntro, hasBirthdayToday: self.hasBirthdayToday, adMessage: self.adMessage) + 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, 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, historyFilter: self.historyFilter, displayHistoryFilterAsList: self.displayHistoryFilterAsList, 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, hasAtLeast3Messages: self.hasAtLeast3Messages, 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, savedMessagesTopicPeer: self.savedMessagesTopicPeer, hasSearchTags: self.hasSearchTags, isPremiumRequiredForMessaging: self.isPremiumRequiredForMessaging, hasSavedChats: self.hasSavedChats, appliedBoosts: self.appliedBoosts, boostsToUnrestrict: self.boostsToUnrestrict, businessIntro: self.businessIntro, hasBirthdayToday: self.hasBirthdayToday, adMessage: self.adMessage, displayVerificationDescription: self.displayVerificationDescription) } 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, 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, historyFilter: self.historyFilter, displayHistoryFilterAsList: self.displayHistoryFilterAsList, 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, hasAtLeast3Messages: self.hasAtLeast3Messages, 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, savedMessagesTopicPeer: self.savedMessagesTopicPeer, hasSearchTags: self.hasSearchTags, isPremiumRequiredForMessaging: self.isPremiumRequiredForMessaging, hasSavedChats: self.hasSavedChats, appliedBoosts: self.appliedBoosts, boostsToUnrestrict: self.boostsToUnrestrict, businessIntro: self.businessIntro, hasBirthdayToday: self.hasBirthdayToday, adMessage: self.adMessage) + 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, 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, historyFilter: self.historyFilter, displayHistoryFilterAsList: self.displayHistoryFilterAsList, 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, hasAtLeast3Messages: self.hasAtLeast3Messages, 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, savedMessagesTopicPeer: self.savedMessagesTopicPeer, hasSearchTags: self.hasSearchTags, isPremiumRequiredForMessaging: self.isPremiumRequiredForMessaging, hasSavedChats: self.hasSavedChats, appliedBoosts: self.appliedBoosts, boostsToUnrestrict: self.boostsToUnrestrict, businessIntro: self.businessIntro, hasBirthdayToday: self.hasBirthdayToday, adMessage: self.adMessage, displayVerificationDescription: self.displayVerificationDescription) } 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, 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, historyFilter: self.historyFilter, displayHistoryFilterAsList: self.displayHistoryFilterAsList, 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, hasAtLeast3Messages: self.hasAtLeast3Messages, 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, savedMessagesTopicPeer: self.savedMessagesTopicPeer, hasSearchTags: self.hasSearchTags, isPremiumRequiredForMessaging: self.isPremiumRequiredForMessaging, hasSavedChats: self.hasSavedChats, appliedBoosts: self.appliedBoosts, boostsToUnrestrict: self.boostsToUnrestrict, businessIntro: self.businessIntro, hasBirthdayToday: self.hasBirthdayToday, adMessage: self.adMessage) + 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, 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, historyFilter: self.historyFilter, displayHistoryFilterAsList: self.displayHistoryFilterAsList, 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, hasAtLeast3Messages: self.hasAtLeast3Messages, 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, savedMessagesTopicPeer: self.savedMessagesTopicPeer, hasSearchTags: self.hasSearchTags, isPremiumRequiredForMessaging: self.isPremiumRequiredForMessaging, hasSavedChats: self.hasSavedChats, appliedBoosts: self.appliedBoosts, boostsToUnrestrict: self.boostsToUnrestrict, businessIntro: self.businessIntro, hasBirthdayToday: self.hasBirthdayToday, adMessage: self.adMessage, displayVerificationDescription: self.displayVerificationDescription) } 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, 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, historyFilter: self.historyFilter, displayHistoryFilterAsList: self.displayHistoryFilterAsList, 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, hasAtLeast3Messages: self.hasAtLeast3Messages, 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, savedMessagesTopicPeer: self.savedMessagesTopicPeer, hasSearchTags: self.hasSearchTags, isPremiumRequiredForMessaging: self.isPremiumRequiredForMessaging, hasSavedChats: self.hasSavedChats, appliedBoosts: self.appliedBoosts, boostsToUnrestrict: self.boostsToUnrestrict, businessIntro: self.businessIntro, hasBirthdayToday: self.hasBirthdayToday, adMessage: self.adMessage) + 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, 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, historyFilter: self.historyFilter, displayHistoryFilterAsList: self.displayHistoryFilterAsList, 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, hasAtLeast3Messages: self.hasAtLeast3Messages, 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, savedMessagesTopicPeer: self.savedMessagesTopicPeer, hasSearchTags: self.hasSearchTags, isPremiumRequiredForMessaging: self.isPremiumRequiredForMessaging, hasSavedChats: self.hasSavedChats, appliedBoosts: self.appliedBoosts, boostsToUnrestrict: self.boostsToUnrestrict, businessIntro: self.businessIntro, hasBirthdayToday: self.hasBirthdayToday, adMessage: self.adMessage, displayVerificationDescription: self.displayVerificationDescription) } public func updatedReportReason(_ reportReason: (String, Data, 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, 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, historyFilter: self.historyFilter, displayHistoryFilterAsList: self.displayHistoryFilterAsList, 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, hasAtLeast3Messages: self.hasAtLeast3Messages, 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, savedMessagesTopicPeer: self.savedMessagesTopicPeer, hasSearchTags: self.hasSearchTags, isPremiumRequiredForMessaging: self.isPremiumRequiredForMessaging, hasSavedChats: self.hasSavedChats, appliedBoosts: self.appliedBoosts, boostsToUnrestrict: self.boostsToUnrestrict, businessIntro: self.businessIntro, hasBirthdayToday: self.hasBirthdayToday, adMessage: self.adMessage) + 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, 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, historyFilter: self.historyFilter, displayHistoryFilterAsList: self.displayHistoryFilterAsList, 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, hasAtLeast3Messages: self.hasAtLeast3Messages, 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, savedMessagesTopicPeer: self.savedMessagesTopicPeer, hasSearchTags: self.hasSearchTags, isPremiumRequiredForMessaging: self.isPremiumRequiredForMessaging, hasSavedChats: self.hasSavedChats, appliedBoosts: self.appliedBoosts, boostsToUnrestrict: self.boostsToUnrestrict, businessIntro: self.businessIntro, hasBirthdayToday: self.hasBirthdayToday, adMessage: self.adMessage, displayVerificationDescription: self.displayVerificationDescription) } 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, 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, historyFilter: self.historyFilter, displayHistoryFilterAsList: self.displayHistoryFilterAsList, 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, hasAtLeast3Messages: self.hasAtLeast3Messages, 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, savedMessagesTopicPeer: self.savedMessagesTopicPeer, hasSearchTags: self.hasSearchTags, isPremiumRequiredForMessaging: self.isPremiumRequiredForMessaging, hasSavedChats: self.hasSavedChats, appliedBoosts: self.appliedBoosts, boostsToUnrestrict: self.boostsToUnrestrict, businessIntro: self.businessIntro, hasBirthdayToday: self.hasBirthdayToday, adMessage: self.adMessage) + 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, 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, historyFilter: self.historyFilter, displayHistoryFilterAsList: self.displayHistoryFilterAsList, 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, hasAtLeast3Messages: self.hasAtLeast3Messages, 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, savedMessagesTopicPeer: self.savedMessagesTopicPeer, hasSearchTags: self.hasSearchTags, isPremiumRequiredForMessaging: self.isPremiumRequiredForMessaging, hasSavedChats: self.hasSavedChats, appliedBoosts: self.appliedBoosts, boostsToUnrestrict: self.boostsToUnrestrict, businessIntro: self.businessIntro, hasBirthdayToday: self.hasBirthdayToday, adMessage: self.adMessage, displayVerificationDescription: self.displayVerificationDescription) } 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, 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, historyFilter: self.historyFilter, displayHistoryFilterAsList: self.displayHistoryFilterAsList, 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, hasAtLeast3Messages: self.hasAtLeast3Messages, 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, savedMessagesTopicPeer: self.savedMessagesTopicPeer, hasSearchTags: self.hasSearchTags, isPremiumRequiredForMessaging: self.isPremiumRequiredForMessaging, hasSavedChats: self.hasSavedChats, appliedBoosts: self.appliedBoosts, boostsToUnrestrict: self.boostsToUnrestrict, businessIntro: self.businessIntro, hasBirthdayToday: self.hasBirthdayToday, adMessage: self.adMessage) + 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, 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, historyFilter: self.historyFilter, displayHistoryFilterAsList: self.displayHistoryFilterAsList, 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, hasAtLeast3Messages: self.hasAtLeast3Messages, 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, savedMessagesTopicPeer: self.savedMessagesTopicPeer, hasSearchTags: self.hasSearchTags, isPremiumRequiredForMessaging: self.isPremiumRequiredForMessaging, hasSavedChats: self.hasSavedChats, appliedBoosts: self.appliedBoosts, boostsToUnrestrict: self.boostsToUnrestrict, businessIntro: self.businessIntro, hasBirthdayToday: self.hasBirthdayToday, adMessage: self.adMessage, displayVerificationDescription: self.displayVerificationDescription) } 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, 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, historyFilter: self.historyFilter, displayHistoryFilterAsList: self.displayHistoryFilterAsList, 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, hasAtLeast3Messages: self.hasAtLeast3Messages, 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, savedMessagesTopicPeer: self.savedMessagesTopicPeer, hasSearchTags: self.hasSearchTags, isPremiumRequiredForMessaging: self.isPremiumRequiredForMessaging, hasSavedChats: self.hasSavedChats, appliedBoosts: self.appliedBoosts, boostsToUnrestrict: self.boostsToUnrestrict, businessIntro: self.businessIntro, hasBirthdayToday: self.hasBirthdayToday, adMessage: self.adMessage) + 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, 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, historyFilter: self.historyFilter, displayHistoryFilterAsList: self.displayHistoryFilterAsList, 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, hasAtLeast3Messages: self.hasAtLeast3Messages, 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, savedMessagesTopicPeer: self.savedMessagesTopicPeer, hasSearchTags: self.hasSearchTags, isPremiumRequiredForMessaging: self.isPremiumRequiredForMessaging, hasSavedChats: self.hasSavedChats, appliedBoosts: self.appliedBoosts, boostsToUnrestrict: self.boostsToUnrestrict, businessIntro: self.businessIntro, hasBirthdayToday: self.hasBirthdayToday, adMessage: self.adMessage, displayVerificationDescription: self.displayVerificationDescription) } 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, 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, historyFilter: self.historyFilter, displayHistoryFilterAsList: self.displayHistoryFilterAsList, 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, hasAtLeast3Messages: self.hasAtLeast3Messages, 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, savedMessagesTopicPeer: self.savedMessagesTopicPeer, hasSearchTags: self.hasSearchTags, isPremiumRequiredForMessaging: self.isPremiumRequiredForMessaging, hasSavedChats: self.hasSavedChats, appliedBoosts: self.appliedBoosts, boostsToUnrestrict: self.boostsToUnrestrict, businessIntro: self.businessIntro, hasBirthdayToday: self.hasBirthdayToday, adMessage: self.adMessage) + 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, 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, historyFilter: self.historyFilter, displayHistoryFilterAsList: self.displayHistoryFilterAsList, 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, hasAtLeast3Messages: self.hasAtLeast3Messages, 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, savedMessagesTopicPeer: self.savedMessagesTopicPeer, hasSearchTags: self.hasSearchTags, isPremiumRequiredForMessaging: self.isPremiumRequiredForMessaging, hasSavedChats: self.hasSavedChats, appliedBoosts: self.appliedBoosts, boostsToUnrestrict: self.boostsToUnrestrict, businessIntro: self.businessIntro, hasBirthdayToday: self.hasBirthdayToday, adMessage: self.adMessage, displayVerificationDescription: self.displayVerificationDescription) } 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, 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, historyFilter: self.historyFilter, displayHistoryFilterAsList: self.displayHistoryFilterAsList, 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, hasAtLeast3Messages: self.hasAtLeast3Messages, 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, savedMessagesTopicPeer: self.savedMessagesTopicPeer, hasSearchTags: self.hasSearchTags, isPremiumRequiredForMessaging: self.isPremiumRequiredForMessaging, hasSavedChats: self.hasSavedChats, appliedBoosts: self.appliedBoosts, boostsToUnrestrict: self.boostsToUnrestrict, businessIntro: self.businessIntro, hasBirthdayToday: self.hasBirthdayToday, adMessage: self.adMessage) + 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, 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, historyFilter: self.historyFilter, displayHistoryFilterAsList: self.displayHistoryFilterAsList, 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, hasAtLeast3Messages: self.hasAtLeast3Messages, 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, savedMessagesTopicPeer: self.savedMessagesTopicPeer, hasSearchTags: self.hasSearchTags, isPremiumRequiredForMessaging: self.isPremiumRequiredForMessaging, hasSavedChats: self.hasSavedChats, appliedBoosts: self.appliedBoosts, boostsToUnrestrict: self.boostsToUnrestrict, businessIntro: self.businessIntro, hasBirthdayToday: self.hasBirthdayToday, adMessage: self.adMessage, displayVerificationDescription: self.displayVerificationDescription) } 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, 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, historyFilter: self.historyFilter, displayHistoryFilterAsList: self.displayHistoryFilterAsList, 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, hasAtLeast3Messages: self.hasAtLeast3Messages, 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, savedMessagesTopicPeer: self.savedMessagesTopicPeer, hasSearchTags: self.hasSearchTags, isPremiumRequiredForMessaging: self.isPremiumRequiredForMessaging, hasSavedChats: self.hasSavedChats, appliedBoosts: self.appliedBoosts, boostsToUnrestrict: self.boostsToUnrestrict, businessIntro: self.businessIntro, hasBirthdayToday: self.hasBirthdayToday, adMessage: self.adMessage) + 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, 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, historyFilter: self.historyFilter, displayHistoryFilterAsList: self.displayHistoryFilterAsList, 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, hasAtLeast3Messages: self.hasAtLeast3Messages, 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, savedMessagesTopicPeer: self.savedMessagesTopicPeer, hasSearchTags: self.hasSearchTags, isPremiumRequiredForMessaging: self.isPremiumRequiredForMessaging, hasSavedChats: self.hasSavedChats, appliedBoosts: self.appliedBoosts, boostsToUnrestrict: self.boostsToUnrestrict, businessIntro: self.businessIntro, hasBirthdayToday: self.hasBirthdayToday, adMessage: self.adMessage, displayVerificationDescription: self.displayVerificationDescription) } 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, 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, historyFilter: self.historyFilter, displayHistoryFilterAsList: self.displayHistoryFilterAsList, 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, hasAtLeast3Messages: self.hasAtLeast3Messages, 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, savedMessagesTopicPeer: self.savedMessagesTopicPeer, hasSearchTags: self.hasSearchTags, isPremiumRequiredForMessaging: self.isPremiumRequiredForMessaging, hasSavedChats: self.hasSavedChats, appliedBoosts: self.appliedBoosts, boostsToUnrestrict: self.boostsToUnrestrict, businessIntro: self.businessIntro, hasBirthdayToday: self.hasBirthdayToday, adMessage: self.adMessage) + 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, 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, historyFilter: self.historyFilter, displayHistoryFilterAsList: self.displayHistoryFilterAsList, 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, hasAtLeast3Messages: self.hasAtLeast3Messages, 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, savedMessagesTopicPeer: self.savedMessagesTopicPeer, hasSearchTags: self.hasSearchTags, isPremiumRequiredForMessaging: self.isPremiumRequiredForMessaging, hasSavedChats: self.hasSavedChats, appliedBoosts: self.appliedBoosts, boostsToUnrestrict: self.boostsToUnrestrict, businessIntro: self.businessIntro, hasBirthdayToday: self.hasBirthdayToday, adMessage: self.adMessage, displayVerificationDescription: self.displayVerificationDescription) } 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, 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, historyFilter: self.historyFilter, displayHistoryFilterAsList: self.displayHistoryFilterAsList, 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, hasAtLeast3Messages: self.hasAtLeast3Messages, 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, savedMessagesTopicPeer: self.savedMessagesTopicPeer, hasSearchTags: self.hasSearchTags, isPremiumRequiredForMessaging: self.isPremiumRequiredForMessaging, hasSavedChats: self.hasSavedChats, appliedBoosts: self.appliedBoosts, boostsToUnrestrict: self.boostsToUnrestrict, businessIntro: self.businessIntro, hasBirthdayToday: self.hasBirthdayToday, adMessage: self.adMessage) + 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, 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, historyFilter: self.historyFilter, displayHistoryFilterAsList: self.displayHistoryFilterAsList, 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, hasAtLeast3Messages: self.hasAtLeast3Messages, 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, savedMessagesTopicPeer: self.savedMessagesTopicPeer, hasSearchTags: self.hasSearchTags, isPremiumRequiredForMessaging: self.isPremiumRequiredForMessaging, hasSavedChats: self.hasSavedChats, appliedBoosts: self.appliedBoosts, boostsToUnrestrict: self.boostsToUnrestrict, businessIntro: self.businessIntro, hasBirthdayToday: self.hasBirthdayToday, adMessage: self.adMessage, displayVerificationDescription: self.displayVerificationDescription) } public func updatedHasAtLeast3Messages(_ hasAtLeast3Messages: 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, 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, historyFilter: self.historyFilter, displayHistoryFilterAsList: self.displayHistoryFilterAsList, 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, hasAtLeast3Messages: hasAtLeast3Messages, 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, savedMessagesTopicPeer: self.savedMessagesTopicPeer, hasSearchTags: self.hasSearchTags, isPremiumRequiredForMessaging: self.isPremiumRequiredForMessaging, hasSavedChats: self.hasSavedChats, appliedBoosts: self.appliedBoosts, boostsToUnrestrict: self.boostsToUnrestrict, businessIntro: self.businessIntro, hasBirthdayToday: self.hasBirthdayToday, adMessage: self.adMessage) + 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, 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, historyFilter: self.historyFilter, displayHistoryFilterAsList: self.displayHistoryFilterAsList, 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, hasAtLeast3Messages: hasAtLeast3Messages, 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, savedMessagesTopicPeer: self.savedMessagesTopicPeer, hasSearchTags: self.hasSearchTags, isPremiumRequiredForMessaging: self.isPremiumRequiredForMessaging, hasSavedChats: self.hasSavedChats, appliedBoosts: self.appliedBoosts, boostsToUnrestrict: self.boostsToUnrestrict, businessIntro: self.businessIntro, hasBirthdayToday: self.hasBirthdayToday, adMessage: self.adMessage, displayVerificationDescription: self.displayVerificationDescription) } 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, 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, historyFilter: self.historyFilter, displayHistoryFilterAsList: self.displayHistoryFilterAsList, 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, hasAtLeast3Messages: self.hasAtLeast3Messages, 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, savedMessagesTopicPeer: self.savedMessagesTopicPeer, hasSearchTags: self.hasSearchTags, isPremiumRequiredForMessaging: self.isPremiumRequiredForMessaging, hasSavedChats: self.hasSavedChats, appliedBoosts: self.appliedBoosts, boostsToUnrestrict: self.boostsToUnrestrict, businessIntro: self.businessIntro, hasBirthdayToday: self.hasBirthdayToday, adMessage: self.adMessage) + 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, 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, historyFilter: self.historyFilter, displayHistoryFilterAsList: self.displayHistoryFilterAsList, 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, hasAtLeast3Messages: self.hasAtLeast3Messages, 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, savedMessagesTopicPeer: self.savedMessagesTopicPeer, hasSearchTags: self.hasSearchTags, isPremiumRequiredForMessaging: self.isPremiumRequiredForMessaging, hasSavedChats: self.hasSavedChats, appliedBoosts: self.appliedBoosts, boostsToUnrestrict: self.boostsToUnrestrict, businessIntro: self.businessIntro, hasBirthdayToday: self.hasBirthdayToday, adMessage: self.adMessage, displayVerificationDescription: self.displayVerificationDescription) } 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, 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, historyFilter: self.historyFilter, displayHistoryFilterAsList: self.displayHistoryFilterAsList, 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, hasAtLeast3Messages: self.hasAtLeast3Messages, 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, savedMessagesTopicPeer: self.savedMessagesTopicPeer, hasSearchTags: self.hasSearchTags, isPremiumRequiredForMessaging: self.isPremiumRequiredForMessaging, hasSavedChats: self.hasSavedChats, appliedBoosts: self.appliedBoosts, boostsToUnrestrict: self.boostsToUnrestrict, businessIntro: self.businessIntro, hasBirthdayToday: self.hasBirthdayToday, adMessage: self.adMessage) + 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, 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, historyFilter: self.historyFilter, displayHistoryFilterAsList: self.displayHistoryFilterAsList, 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, hasAtLeast3Messages: self.hasAtLeast3Messages, 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, savedMessagesTopicPeer: self.savedMessagesTopicPeer, hasSearchTags: self.hasSearchTags, isPremiumRequiredForMessaging: self.isPremiumRequiredForMessaging, hasSavedChats: self.hasSavedChats, appliedBoosts: self.appliedBoosts, boostsToUnrestrict: self.boostsToUnrestrict, businessIntro: self.businessIntro, hasBirthdayToday: self.hasBirthdayToday, adMessage: self.adMessage, displayVerificationDescription: self.displayVerificationDescription) } 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, 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, historyFilter: self.historyFilter, displayHistoryFilterAsList: self.displayHistoryFilterAsList, 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, hasAtLeast3Messages: self.hasAtLeast3Messages, 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, savedMessagesTopicPeer: self.savedMessagesTopicPeer, hasSearchTags: self.hasSearchTags, isPremiumRequiredForMessaging: self.isPremiumRequiredForMessaging, hasSavedChats: self.hasSavedChats, appliedBoosts: self.appliedBoosts, boostsToUnrestrict: self.boostsToUnrestrict, businessIntro: self.businessIntro, hasBirthdayToday: self.hasBirthdayToday, adMessage: self.adMessage) + 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, 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, historyFilter: self.historyFilter, displayHistoryFilterAsList: self.displayHistoryFilterAsList, 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, hasAtLeast3Messages: self.hasAtLeast3Messages, 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, savedMessagesTopicPeer: self.savedMessagesTopicPeer, hasSearchTags: self.hasSearchTags, isPremiumRequiredForMessaging: self.isPremiumRequiredForMessaging, hasSavedChats: self.hasSavedChats, appliedBoosts: self.appliedBoosts, boostsToUnrestrict: self.boostsToUnrestrict, businessIntro: self.businessIntro, hasBirthdayToday: self.hasBirthdayToday, adMessage: self.adMessage, displayVerificationDescription: self.displayVerificationDescription) } 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, 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, historyFilter: self.historyFilter, displayHistoryFilterAsList: self.displayHistoryFilterAsList, 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, hasAtLeast3Messages: self.hasAtLeast3Messages, 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, savedMessagesTopicPeer: self.savedMessagesTopicPeer, hasSearchTags: self.hasSearchTags, isPremiumRequiredForMessaging: self.isPremiumRequiredForMessaging, hasSavedChats: self.hasSavedChats, appliedBoosts: self.appliedBoosts, boostsToUnrestrict: self.boostsToUnrestrict, businessIntro: self.businessIntro, hasBirthdayToday: self.hasBirthdayToday, adMessage: self.adMessage) + 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, 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, historyFilter: self.historyFilter, displayHistoryFilterAsList: self.displayHistoryFilterAsList, 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, hasAtLeast3Messages: self.hasAtLeast3Messages, 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, savedMessagesTopicPeer: self.savedMessagesTopicPeer, hasSearchTags: self.hasSearchTags, isPremiumRequiredForMessaging: self.isPremiumRequiredForMessaging, hasSavedChats: self.hasSavedChats, appliedBoosts: self.appliedBoosts, boostsToUnrestrict: self.boostsToUnrestrict, businessIntro: self.businessIntro, hasBirthdayToday: self.hasBirthdayToday, adMessage: self.adMessage, displayVerificationDescription: self.displayVerificationDescription) } 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, 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, historyFilter: self.historyFilter, displayHistoryFilterAsList: self.displayHistoryFilterAsList, 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, hasAtLeast3Messages: self.hasAtLeast3Messages, 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, savedMessagesTopicPeer: self.savedMessagesTopicPeer, hasSearchTags: self.hasSearchTags, isPremiumRequiredForMessaging: self.isPremiumRequiredForMessaging, hasSavedChats: self.hasSavedChats, appliedBoosts: self.appliedBoosts, boostsToUnrestrict: self.boostsToUnrestrict, businessIntro: self.businessIntro, hasBirthdayToday: self.hasBirthdayToday, adMessage: self.adMessage) + 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, 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, historyFilter: self.historyFilter, displayHistoryFilterAsList: self.displayHistoryFilterAsList, 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, hasAtLeast3Messages: self.hasAtLeast3Messages, 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, savedMessagesTopicPeer: self.savedMessagesTopicPeer, hasSearchTags: self.hasSearchTags, isPremiumRequiredForMessaging: self.isPremiumRequiredForMessaging, hasSavedChats: self.hasSavedChats, appliedBoosts: self.appliedBoosts, boostsToUnrestrict: self.boostsToUnrestrict, businessIntro: self.businessIntro, hasBirthdayToday: self.hasBirthdayToday, adMessage: self.adMessage, displayVerificationDescription: self.displayVerificationDescription) } 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, 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, historyFilter: self.historyFilter, displayHistoryFilterAsList: self.displayHistoryFilterAsList, 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, hasAtLeast3Messages: self.hasAtLeast3Messages, 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, savedMessagesTopicPeer: self.savedMessagesTopicPeer, hasSearchTags: self.hasSearchTags, isPremiumRequiredForMessaging: self.isPremiumRequiredForMessaging, hasSavedChats: self.hasSavedChats, appliedBoosts: self.appliedBoosts, boostsToUnrestrict: self.boostsToUnrestrict, businessIntro: self.businessIntro, hasBirthdayToday: self.hasBirthdayToday, adMessage: self.adMessage) + 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, 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, historyFilter: self.historyFilter, displayHistoryFilterAsList: self.displayHistoryFilterAsList, 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, hasAtLeast3Messages: self.hasAtLeast3Messages, 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, savedMessagesTopicPeer: self.savedMessagesTopicPeer, hasSearchTags: self.hasSearchTags, isPremiumRequiredForMessaging: self.isPremiumRequiredForMessaging, hasSavedChats: self.hasSavedChats, appliedBoosts: self.appliedBoosts, boostsToUnrestrict: self.boostsToUnrestrict, businessIntro: self.businessIntro, hasBirthdayToday: self.hasBirthdayToday, adMessage: self.adMessage, displayVerificationDescription: self.displayVerificationDescription) } 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, 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, historyFilter: self.historyFilter, displayHistoryFilterAsList: self.displayHistoryFilterAsList, 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, hasAtLeast3Messages: self.hasAtLeast3Messages, 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, savedMessagesTopicPeer: self.savedMessagesTopicPeer, hasSearchTags: self.hasSearchTags, isPremiumRequiredForMessaging: self.isPremiumRequiredForMessaging, hasSavedChats: self.hasSavedChats, appliedBoosts: self.appliedBoosts, boostsToUnrestrict: self.boostsToUnrestrict, businessIntro: self.businessIntro, hasBirthdayToday: self.hasBirthdayToday, adMessage: self.adMessage) + 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, 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, historyFilter: self.historyFilter, displayHistoryFilterAsList: self.displayHistoryFilterAsList, 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, hasAtLeast3Messages: self.hasAtLeast3Messages, 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, savedMessagesTopicPeer: self.savedMessagesTopicPeer, hasSearchTags: self.hasSearchTags, isPremiumRequiredForMessaging: self.isPremiumRequiredForMessaging, hasSavedChats: self.hasSavedChats, appliedBoosts: self.appliedBoosts, boostsToUnrestrict: self.boostsToUnrestrict, businessIntro: self.businessIntro, hasBirthdayToday: self.hasBirthdayToday, adMessage: self.adMessage, displayVerificationDescription: self.displayVerificationDescription) } 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, 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, historyFilter: self.historyFilter, displayHistoryFilterAsList: self.displayHistoryFilterAsList, 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, hasAtLeast3Messages: self.hasAtLeast3Messages, 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, savedMessagesTopicPeer: self.savedMessagesTopicPeer, hasSearchTags: self.hasSearchTags, isPremiumRequiredForMessaging: self.isPremiumRequiredForMessaging, hasSavedChats: self.hasSavedChats, appliedBoosts: self.appliedBoosts, boostsToUnrestrict: self.boostsToUnrestrict, businessIntro: self.businessIntro, hasBirthdayToday: self.hasBirthdayToday, adMessage: self.adMessage) + 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, 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, historyFilter: self.historyFilter, displayHistoryFilterAsList: self.displayHistoryFilterAsList, 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, hasAtLeast3Messages: self.hasAtLeast3Messages, 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, savedMessagesTopicPeer: self.savedMessagesTopicPeer, hasSearchTags: self.hasSearchTags, isPremiumRequiredForMessaging: self.isPremiumRequiredForMessaging, hasSavedChats: self.hasSavedChats, appliedBoosts: self.appliedBoosts, boostsToUnrestrict: self.boostsToUnrestrict, businessIntro: self.businessIntro, hasBirthdayToday: self.hasBirthdayToday, adMessage: self.adMessage, displayVerificationDescription: self.displayVerificationDescription) } 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, 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, historyFilter: self.historyFilter, displayHistoryFilterAsList: self.displayHistoryFilterAsList, 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, hasAtLeast3Messages: self.hasAtLeast3Messages, 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, savedMessagesTopicPeer: self.savedMessagesTopicPeer, hasSearchTags: self.hasSearchTags, isPremiumRequiredForMessaging: self.isPremiumRequiredForMessaging, hasSavedChats: self.hasSavedChats, appliedBoosts: self.appliedBoosts, boostsToUnrestrict: self.boostsToUnrestrict, businessIntro: self.businessIntro, hasBirthdayToday: self.hasBirthdayToday, adMessage: self.adMessage) + 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, 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, historyFilter: self.historyFilter, displayHistoryFilterAsList: self.displayHistoryFilterAsList, 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, hasAtLeast3Messages: self.hasAtLeast3Messages, 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, savedMessagesTopicPeer: self.savedMessagesTopicPeer, hasSearchTags: self.hasSearchTags, isPremiumRequiredForMessaging: self.isPremiumRequiredForMessaging, hasSavedChats: self.hasSavedChats, appliedBoosts: self.appliedBoosts, boostsToUnrestrict: self.boostsToUnrestrict, businessIntro: self.businessIntro, hasBirthdayToday: self.hasBirthdayToday, adMessage: self.adMessage, displayVerificationDescription: self.displayVerificationDescription) } 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, 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, historyFilter: self.historyFilter, displayHistoryFilterAsList: self.displayHistoryFilterAsList, 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, hasAtLeast3Messages: self.hasAtLeast3Messages, 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, savedMessagesTopicPeer: self.savedMessagesTopicPeer, hasSearchTags: self.hasSearchTags, isPremiumRequiredForMessaging: self.isPremiumRequiredForMessaging, hasSavedChats: self.hasSavedChats, appliedBoosts: self.appliedBoosts, boostsToUnrestrict: self.boostsToUnrestrict, businessIntro: self.businessIntro, hasBirthdayToday: self.hasBirthdayToday, adMessage: self.adMessage) + 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, 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, historyFilter: self.historyFilter, displayHistoryFilterAsList: self.displayHistoryFilterAsList, 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, hasAtLeast3Messages: self.hasAtLeast3Messages, 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, savedMessagesTopicPeer: self.savedMessagesTopicPeer, hasSearchTags: self.hasSearchTags, isPremiumRequiredForMessaging: self.isPremiumRequiredForMessaging, hasSavedChats: self.hasSavedChats, appliedBoosts: self.appliedBoosts, boostsToUnrestrict: self.boostsToUnrestrict, businessIntro: self.businessIntro, hasBirthdayToday: self.hasBirthdayToday, adMessage: self.adMessage, displayVerificationDescription: self.displayVerificationDescription) } 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, 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, historyFilter: self.historyFilter, displayHistoryFilterAsList: self.displayHistoryFilterAsList, 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, hasAtLeast3Messages: self.hasAtLeast3Messages, 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, savedMessagesTopicPeer: self.savedMessagesTopicPeer, hasSearchTags: self.hasSearchTags, isPremiumRequiredForMessaging: self.isPremiumRequiredForMessaging, hasSavedChats: self.hasSavedChats, appliedBoosts: self.appliedBoosts, boostsToUnrestrict: self.boostsToUnrestrict, businessIntro: self.businessIntro, hasBirthdayToday: self.hasBirthdayToday, adMessage: self.adMessage) + 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, 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, historyFilter: self.historyFilter, displayHistoryFilterAsList: self.displayHistoryFilterAsList, 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, hasAtLeast3Messages: self.hasAtLeast3Messages, 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, savedMessagesTopicPeer: self.savedMessagesTopicPeer, hasSearchTags: self.hasSearchTags, isPremiumRequiredForMessaging: self.isPremiumRequiredForMessaging, hasSavedChats: self.hasSavedChats, appliedBoosts: self.appliedBoosts, boostsToUnrestrict: self.boostsToUnrestrict, businessIntro: self.businessIntro, hasBirthdayToday: self.hasBirthdayToday, adMessage: self.adMessage, displayVerificationDescription: self.displayVerificationDescription) } 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, 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, historyFilter: self.historyFilter, displayHistoryFilterAsList: self.displayHistoryFilterAsList, 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, hasAtLeast3Messages: self.hasAtLeast3Messages, 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, savedMessagesTopicPeer: self.savedMessagesTopicPeer, hasSearchTags: self.hasSearchTags, isPremiumRequiredForMessaging: self.isPremiumRequiredForMessaging, hasSavedChats: self.hasSavedChats, appliedBoosts: self.appliedBoosts, boostsToUnrestrict: self.boostsToUnrestrict, businessIntro: self.businessIntro, hasBirthdayToday: self.hasBirthdayToday, adMessage: self.adMessage) + 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, 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, historyFilter: self.historyFilter, displayHistoryFilterAsList: self.displayHistoryFilterAsList, 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, hasAtLeast3Messages: self.hasAtLeast3Messages, 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, savedMessagesTopicPeer: self.savedMessagesTopicPeer, hasSearchTags: self.hasSearchTags, isPremiumRequiredForMessaging: self.isPremiumRequiredForMessaging, hasSavedChats: self.hasSavedChats, appliedBoosts: self.appliedBoosts, boostsToUnrestrict: self.boostsToUnrestrict, businessIntro: self.businessIntro, hasBirthdayToday: self.hasBirthdayToday, adMessage: self.adMessage, displayVerificationDescription: self.displayVerificationDescription) } 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, 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, historyFilter: self.historyFilter, displayHistoryFilterAsList: self.displayHistoryFilterAsList, 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, hasAtLeast3Messages: self.hasAtLeast3Messages, 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, savedMessagesTopicPeer: self.savedMessagesTopicPeer, hasSearchTags: self.hasSearchTags, isPremiumRequiredForMessaging: self.isPremiumRequiredForMessaging, hasSavedChats: self.hasSavedChats, appliedBoosts: self.appliedBoosts, boostsToUnrestrict: self.boostsToUnrestrict, businessIntro: self.businessIntro, hasBirthdayToday: self.hasBirthdayToday, adMessage: self.adMessage) + 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, 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, historyFilter: self.historyFilter, displayHistoryFilterAsList: self.displayHistoryFilterAsList, 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, hasAtLeast3Messages: self.hasAtLeast3Messages, 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, savedMessagesTopicPeer: self.savedMessagesTopicPeer, hasSearchTags: self.hasSearchTags, isPremiumRequiredForMessaging: self.isPremiumRequiredForMessaging, hasSavedChats: self.hasSavedChats, appliedBoosts: self.appliedBoosts, boostsToUnrestrict: self.boostsToUnrestrict, businessIntro: self.businessIntro, hasBirthdayToday: self.hasBirthdayToday, adMessage: self.adMessage, displayVerificationDescription: self.displayVerificationDescription) } public func updatedSavedMessagesTopicPeer(_ savedMessagesTopicPeer: EnginePeer?) -> 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, 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, historyFilter: self.historyFilter, displayHistoryFilterAsList: self.displayHistoryFilterAsList, 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, hasAtLeast3Messages: self.hasAtLeast3Messages, 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, savedMessagesTopicPeer: savedMessagesTopicPeer, hasSearchTags: self.hasSearchTags, isPremiumRequiredForMessaging: self.isPremiumRequiredForMessaging, hasSavedChats: self.hasSavedChats, appliedBoosts: self.appliedBoosts, boostsToUnrestrict: self.boostsToUnrestrict, businessIntro: self.businessIntro, hasBirthdayToday: self.hasBirthdayToday, adMessage: self.adMessage) + 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, 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, historyFilter: self.historyFilter, displayHistoryFilterAsList: self.displayHistoryFilterAsList, 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, hasAtLeast3Messages: self.hasAtLeast3Messages, 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, savedMessagesTopicPeer: savedMessagesTopicPeer, hasSearchTags: self.hasSearchTags, isPremiumRequiredForMessaging: self.isPremiumRequiredForMessaging, hasSavedChats: self.hasSavedChats, appliedBoosts: self.appliedBoosts, boostsToUnrestrict: self.boostsToUnrestrict, businessIntro: self.businessIntro, hasBirthdayToday: self.hasBirthdayToday, adMessage: self.adMessage, displayVerificationDescription: self.displayVerificationDescription) } public func updatedHasSearchTags(_ hasSearchTags: 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, 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, historyFilter: self.historyFilter, displayHistoryFilterAsList: self.displayHistoryFilterAsList, 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, hasAtLeast3Messages: self.hasAtLeast3Messages, 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, savedMessagesTopicPeer: self.savedMessagesTopicPeer, hasSearchTags: hasSearchTags, isPremiumRequiredForMessaging: self.isPremiumRequiredForMessaging, hasSavedChats: self.hasSavedChats, appliedBoosts: self.appliedBoosts, boostsToUnrestrict: self.boostsToUnrestrict, businessIntro: self.businessIntro, hasBirthdayToday: self.hasBirthdayToday, adMessage: self.adMessage) + 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, 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, historyFilter: self.historyFilter, displayHistoryFilterAsList: self.displayHistoryFilterAsList, 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, hasAtLeast3Messages: self.hasAtLeast3Messages, 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, savedMessagesTopicPeer: self.savedMessagesTopicPeer, hasSearchTags: hasSearchTags, isPremiumRequiredForMessaging: self.isPremiumRequiredForMessaging, hasSavedChats: self.hasSavedChats, appliedBoosts: self.appliedBoosts, boostsToUnrestrict: self.boostsToUnrestrict, businessIntro: self.businessIntro, hasBirthdayToday: self.hasBirthdayToday, adMessage: self.adMessage, displayVerificationDescription: self.displayVerificationDescription) } public func updatedIsPremiumRequiredForMessaging(_ isPremiumRequiredForMessaging: 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, 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, historyFilter: self.historyFilter, displayHistoryFilterAsList: self.displayHistoryFilterAsList, 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, hasAtLeast3Messages: self.hasAtLeast3Messages, 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, savedMessagesTopicPeer: self.savedMessagesTopicPeer, hasSearchTags: self.hasSearchTags, isPremiumRequiredForMessaging: isPremiumRequiredForMessaging, hasSavedChats: self.hasSavedChats, appliedBoosts: self.appliedBoosts, boostsToUnrestrict: self.boostsToUnrestrict, businessIntro: self.businessIntro, hasBirthdayToday: self.hasBirthdayToday, adMessage: self.adMessage) + 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, 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, historyFilter: self.historyFilter, displayHistoryFilterAsList: self.displayHistoryFilterAsList, 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, hasAtLeast3Messages: self.hasAtLeast3Messages, 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, savedMessagesTopicPeer: self.savedMessagesTopicPeer, hasSearchTags: self.hasSearchTags, isPremiumRequiredForMessaging: isPremiumRequiredForMessaging, hasSavedChats: self.hasSavedChats, appliedBoosts: self.appliedBoosts, boostsToUnrestrict: self.boostsToUnrestrict, businessIntro: self.businessIntro, hasBirthdayToday: self.hasBirthdayToday, adMessage: self.adMessage, displayVerificationDescription: self.displayVerificationDescription) } public func updatedHasSavedChats(_ hasSavedChats: 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, 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, historyFilter: self.historyFilter, displayHistoryFilterAsList: self.displayHistoryFilterAsList, 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, hasAtLeast3Messages: self.hasAtLeast3Messages, 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, savedMessagesTopicPeer: self.savedMessagesTopicPeer, hasSearchTags: self.hasSearchTags, isPremiumRequiredForMessaging: self.isPremiumRequiredForMessaging, hasSavedChats: hasSavedChats, appliedBoosts: self.appliedBoosts, boostsToUnrestrict: self.boostsToUnrestrict, businessIntro: self.businessIntro, hasBirthdayToday: self.hasBirthdayToday, adMessage: self.adMessage) + 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, 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, historyFilter: self.historyFilter, displayHistoryFilterAsList: self.displayHistoryFilterAsList, 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, hasAtLeast3Messages: self.hasAtLeast3Messages, 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, savedMessagesTopicPeer: self.savedMessagesTopicPeer, hasSearchTags: self.hasSearchTags, isPremiumRequiredForMessaging: self.isPremiumRequiredForMessaging, hasSavedChats: hasSavedChats, appliedBoosts: self.appliedBoosts, boostsToUnrestrict: self.boostsToUnrestrict, businessIntro: self.businessIntro, hasBirthdayToday: self.hasBirthdayToday, adMessage: self.adMessage, displayVerificationDescription: self.displayVerificationDescription) } public func updatedAppliedBoosts(_ appliedBoosts: 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, 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, historyFilter: self.historyFilter, displayHistoryFilterAsList: self.displayHistoryFilterAsList, 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, hasAtLeast3Messages: self.hasAtLeast3Messages, 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, savedMessagesTopicPeer: self.savedMessagesTopicPeer, hasSearchTags: self.hasSearchTags, isPremiumRequiredForMessaging: self.isPremiumRequiredForMessaging, hasSavedChats: self.hasSavedChats, appliedBoosts: appliedBoosts, boostsToUnrestrict: self.boostsToUnrestrict, businessIntro: self.businessIntro, hasBirthdayToday: self.hasBirthdayToday, adMessage: self.adMessage) + 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, 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, historyFilter: self.historyFilter, displayHistoryFilterAsList: self.displayHistoryFilterAsList, 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, hasAtLeast3Messages: self.hasAtLeast3Messages, 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, savedMessagesTopicPeer: self.savedMessagesTopicPeer, hasSearchTags: self.hasSearchTags, isPremiumRequiredForMessaging: self.isPremiumRequiredForMessaging, hasSavedChats: self.hasSavedChats, appliedBoosts: appliedBoosts, boostsToUnrestrict: self.boostsToUnrestrict, businessIntro: self.businessIntro, hasBirthdayToday: self.hasBirthdayToday, adMessage: self.adMessage, displayVerificationDescription: self.displayVerificationDescription) } public func updatedBoostsToUnrestrict(_ boostsToUnrestrict: 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, 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, historyFilter: self.historyFilter, displayHistoryFilterAsList: self.displayHistoryFilterAsList, 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, hasAtLeast3Messages: self.hasAtLeast3Messages, 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, savedMessagesTopicPeer: self.savedMessagesTopicPeer, hasSearchTags: self.hasSearchTags, isPremiumRequiredForMessaging: self.isPremiumRequiredForMessaging, hasSavedChats: self.hasSavedChats, appliedBoosts: self.appliedBoosts, boostsToUnrestrict: boostsToUnrestrict, businessIntro: self.businessIntro, hasBirthdayToday: self.hasBirthdayToday, adMessage: self.adMessage) + 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, 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, historyFilter: self.historyFilter, displayHistoryFilterAsList: self.displayHistoryFilterAsList, 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, hasAtLeast3Messages: self.hasAtLeast3Messages, 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, savedMessagesTopicPeer: self.savedMessagesTopicPeer, hasSearchTags: self.hasSearchTags, isPremiumRequiredForMessaging: self.isPremiumRequiredForMessaging, hasSavedChats: self.hasSavedChats, appliedBoosts: self.appliedBoosts, boostsToUnrestrict: boostsToUnrestrict, businessIntro: self.businessIntro, hasBirthdayToday: self.hasBirthdayToday, adMessage: self.adMessage, displayVerificationDescription: self.displayVerificationDescription) } public func updatedBusinessIntro(_ businessIntro: TelegramBusinessIntro?) -> 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, 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, historyFilter: self.historyFilter, displayHistoryFilterAsList: self.displayHistoryFilterAsList, 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, hasAtLeast3Messages: self.hasAtLeast3Messages, 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, savedMessagesTopicPeer: self.savedMessagesTopicPeer, hasSearchTags: self.hasSearchTags, isPremiumRequiredForMessaging: self.isPremiumRequiredForMessaging, hasSavedChats: self.hasSavedChats, appliedBoosts: self.appliedBoosts, boostsToUnrestrict: self.boostsToUnrestrict, businessIntro: businessIntro, hasBirthdayToday: self.hasBirthdayToday, adMessage: self.adMessage) + 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, 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, historyFilter: self.historyFilter, displayHistoryFilterAsList: self.displayHistoryFilterAsList, 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, hasAtLeast3Messages: self.hasAtLeast3Messages, 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, savedMessagesTopicPeer: self.savedMessagesTopicPeer, hasSearchTags: self.hasSearchTags, isPremiumRequiredForMessaging: self.isPremiumRequiredForMessaging, hasSavedChats: self.hasSavedChats, appliedBoosts: self.appliedBoosts, boostsToUnrestrict: self.boostsToUnrestrict, businessIntro: businessIntro, hasBirthdayToday: self.hasBirthdayToday, adMessage: self.adMessage, displayVerificationDescription: self.displayVerificationDescription) } public func updatedHasBirthdayToday(_ hasBirthdayToday: 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, 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, historyFilter: self.historyFilter, displayHistoryFilterAsList: self.displayHistoryFilterAsList, 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, hasAtLeast3Messages: self.hasAtLeast3Messages, 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, savedMessagesTopicPeer: self.savedMessagesTopicPeer, hasSearchTags: self.hasSearchTags, isPremiumRequiredForMessaging: self.isPremiumRequiredForMessaging, hasSavedChats: self.hasSavedChats, appliedBoosts: self.appliedBoosts, boostsToUnrestrict: self.boostsToUnrestrict, businessIntro: self.businessIntro, hasBirthdayToday: hasBirthdayToday, adMessage: self.adMessage) + 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, 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, historyFilter: self.historyFilter, displayHistoryFilterAsList: self.displayHistoryFilterAsList, 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, hasAtLeast3Messages: self.hasAtLeast3Messages, 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, savedMessagesTopicPeer: self.savedMessagesTopicPeer, hasSearchTags: self.hasSearchTags, isPremiumRequiredForMessaging: self.isPremiumRequiredForMessaging, hasSavedChats: self.hasSavedChats, appliedBoosts: self.appliedBoosts, boostsToUnrestrict: self.boostsToUnrestrict, businessIntro: self.businessIntro, hasBirthdayToday: hasBirthdayToday, adMessage: self.adMessage, displayVerificationDescription: self.displayVerificationDescription) } public func updatedAdMessage(_ adMessage: 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, 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, historyFilter: self.historyFilter, displayHistoryFilterAsList: self.displayHistoryFilterAsList, 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, hasAtLeast3Messages: self.hasAtLeast3Messages, 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, savedMessagesTopicPeer: self.savedMessagesTopicPeer, hasSearchTags: self.hasSearchTags, isPremiumRequiredForMessaging: self.isPremiumRequiredForMessaging, hasSavedChats: self.hasSavedChats, appliedBoosts: self.appliedBoosts, boostsToUnrestrict: self.boostsToUnrestrict, businessIntro: self.businessIntro, hasBirthdayToday: self.hasBirthdayToday, adMessage: adMessage) + 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, 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, historyFilter: self.historyFilter, displayHistoryFilterAsList: self.displayHistoryFilterAsList, 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, hasAtLeast3Messages: self.hasAtLeast3Messages, 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, savedMessagesTopicPeer: self.savedMessagesTopicPeer, hasSearchTags: self.hasSearchTags, isPremiumRequiredForMessaging: self.isPremiumRequiredForMessaging, hasSavedChats: self.hasSavedChats, appliedBoosts: self.appliedBoosts, boostsToUnrestrict: self.boostsToUnrestrict, businessIntro: self.businessIntro, hasBirthdayToday: self.hasBirthdayToday, adMessage: adMessage, displayVerificationDescription: self.displayVerificationDescription) + } + + public func updatedDisplayVerificationDescription(_ displayVerificationDescription: 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, 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, historyFilter: self.historyFilter, displayHistoryFilterAsList: self.displayHistoryFilterAsList, 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, hasAtLeast3Messages: self.hasAtLeast3Messages, 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, savedMessagesTopicPeer: self.savedMessagesTopicPeer, hasSearchTags: self.hasSearchTags, isPremiumRequiredForMessaging: self.isPremiumRequiredForMessaging, hasSavedChats: self.hasSavedChats, appliedBoosts: self.appliedBoosts, boostsToUnrestrict: self.boostsToUnrestrict, businessIntro: self.businessIntro, hasBirthdayToday: self.hasBirthdayToday, adMessage: self.adMessage, displayVerificationDescription: displayVerificationDescription) } } diff --git a/submodules/Components/LottieAnimationComponent/Sources/LottieAnimationComponent.swift b/submodules/Components/LottieAnimationComponent/Sources/LottieAnimationComponent.swift index b13b4e4e92..67b747f790 100644 --- a/submodules/Components/LottieAnimationComponent/Sources/LottieAnimationComponent.swift +++ b/submodules/Components/LottieAnimationComponent/Sources/LottieAnimationComponent.swift @@ -22,12 +22,14 @@ public final class LottieAnimationComponent: Component { public var name: String public var mode: Mode public var range: (CGFloat, CGFloat)? + public var speed: CGFloat public var waitForCompletion: Bool - public init(name: String, mode: Mode, range: (CGFloat, CGFloat)? = nil, waitForCompletion: Bool = true) { + public init(name: String, mode: Mode, range: (CGFloat, CGFloat)? = nil, speed: CGFloat = 1.0, waitForCompletion: Bool = true) { self.name = name self.mode = mode self.range = range + self.speed = speed self.waitForCompletion = waitForCompletion } @@ -38,6 +40,9 @@ public final class LottieAnimationComponent: Component { if lhs.mode != rhs.mode { return false } + if lhs.speed != rhs.speed { + return false + } if let lhsRange = lhs.range, let rhsRange = rhs.range, lhsRange != rhsRange { return false } else if (lhs.range == nil) != (rhs.range == nil) { @@ -203,7 +208,7 @@ public final class LottieAnimationComponent: Component { view.loopMode = .playOnce } } - view.animationSpeed = 1.0 + view.animationSpeed = component.animation.speed view.backgroundColor = .clear view.isOpaque = false diff --git a/submodules/ContactListUI/Sources/ContactListActionItem.swift b/submodules/ContactListUI/Sources/ContactListActionItem.swift index 4588b5a5bf..48de2a2ac2 100644 --- a/submodules/ContactListUI/Sources/ContactListActionItem.swift +++ b/submodules/ContactListUI/Sources/ContactListActionItem.swift @@ -17,6 +17,7 @@ public enum ContactListActionItemHighlight { public class ContactListActionItem: ListViewItem, ListViewItemWithHeader { let presentationData: ItemListPresentationData let title: String + let subtitle: String? let icon: ContactListActionItemIcon let highlight: ContactListActionItemHighlight let clearHighlightAutomatically: Bool @@ -24,9 +25,10 @@ public class ContactListActionItem: ListViewItem, ListViewItemWithHeader { let action: () -> Void public let header: ListViewItemHeader? - public init(presentationData: ItemListPresentationData, title: String, icon: ContactListActionItemIcon, highlight: ContactListActionItemHighlight = .cell, clearHighlightAutomatically: Bool = true, accessible: Bool = true, header: ListViewItemHeader?, action: @escaping () -> Void) { + public init(presentationData: ItemListPresentationData, title: String, subtitle: String? = nil, icon: ContactListActionItemIcon, highlight: ContactListActionItemHighlight = .cell, clearHighlightAutomatically: Bool = true, accessible: Bool = true, header: ListViewItemHeader?, action: @escaping () -> Void) { self.presentationData = presentationData self.title = title + self.subtitle = subtitle self.icon = icon self.highlight = highlight self.header = header @@ -204,7 +206,11 @@ class ContactListActionItemNode: ListViewItemNode { strongSelf.backgroundNode.backgroundColor = item.presentationData.theme.list.plainBackgroundColor strongSelf.highlightedBackgroundNode.backgroundColor = item.presentationData.theme.list.itemHighlightedBackgroundColor - strongSelf.iconNode.image = generateTintedImage(image: item.icon.image, color: item.presentationData.theme.list.itemAccentColor) + if item.subtitle != nil { + strongSelf.iconNode.image = item.icon.image + } else { + strongSelf.iconNode.image = generateTintedImage(image: item.icon.image, color: item.presentationData.theme.list.itemAccentColor) + } } if item.accessible && strongSelf.activateArea.supernode == nil { diff --git a/submodules/ContactListUI/Sources/ContactListNode.swift b/submodules/ContactListUI/Sources/ContactListNode.swift index 9ba3d82ee1..77a44a2be6 100644 --- a/submodules/ContactListUI/Sources/ContactListNode.swift +++ b/submodules/ContactListUI/Sources/ContactListNode.swift @@ -144,7 +144,7 @@ private enum ContactListNodeEntry: Comparable, Identifiable { interaction.authorize() }) case let .option(_, option, header, _, _): - return ContactListActionItem(presentationData: ItemListPresentationData(presentationData), title: option.title, icon: option.icon, clearHighlightAutomatically: option.clearHighlightAutomatically, header: header, action: option.action) + return ContactListActionItem(presentationData: ItemListPresentationData(presentationData), title: option.title, subtitle: option.subtitle, icon: option.icon, clearHighlightAutomatically: option.clearHighlightAutomatically, header: header, action: option.action) case let .peer(_, peer, presence, header, selection, _, strings, dateTimeFormat, nameSortOrder, nameDisplayOrder, displayCallIcons, hasMoreButton, enabled, storyData, requiresPremiumForMessaging): var status: ContactsPeerItemStatus let itemPeer: ContactsPeerItemPeer @@ -541,7 +541,7 @@ private func contactListNodeEntries(accountPeer: EnginePeer?, peers: [ContactLis if !topPeers.isEmpty { let hasDeselectAll = !(selectionState?.selectedPeerIndices ?? [:]).isEmpty - let header: ListViewItemHeader? = ChatListSearchItemHeader(type: .text(strings.Premium_Gift_ContactSelection_FrequentContacts.uppercased(), AnyHashable(hasDeselectAll ? 1 : 0)), theme: theme, strings: strings, actionTitle: hasDeselectAll ? strings.Premium_Gift_ContactSelection_DeselectAll.uppercased() : nil, action: { + let header: ListViewItemHeader? = ChatListSearchItemHeader(type: .text(strings.Premium_Gift_ContactSelection_FrequentContacts.uppercased(), AnyHashable(hasDeselectAll ? 1 : 0)), theme: theme, strings: strings, actionTitle: hasDeselectAll ? strings.Premium_Gift_ContactSelection_DeselectAll.uppercased() : nil, action: { _ in interaction.deselectAll() }) @@ -585,7 +585,7 @@ private func contactListNodeEntries(accountPeer: EnginePeer?, peers: [ContactLis if peerIds.count > 1 { actionTitle = allSelected ? strings.Premium_Gift_ContactSelection_DeselectAll.uppercased() : strings.Premium_Gift_ContactSelection_SelectAll.uppercased() } - let header: ListViewItemHeader? = ChatListSearchItemHeader(type: .text(title.uppercased(), AnyHashable(10 * sectionId + (allSelected ? 1 : 0))), theme: theme, strings: strings, actionTitle: actionTitle, action: { + let header: ListViewItemHeader? = ChatListSearchItemHeader(type: .text(title.uppercased(), AnyHashable(10 * sectionId + (allSelected ? 1 : 0))), theme: theme, strings: strings, actionTitle: actionTitle, action: { _ in var existingPeerIds = Set() var peers: [EnginePeer] = [] for peer in topPeers { @@ -645,7 +645,7 @@ private func contactListNodeEntries(accountPeer: EnginePeer?, peers: [ContactLis } } - let header: ListViewItemHeader? = ChatListSearchItemHeader(type: .text(strings.Premium_Gift_ContactSelection_FrequentContacts.uppercased(), AnyHashable(hasDeselectAll ? 1 : 0)), theme: theme, strings: strings, actionTitle: hasDeselectAll ? strings.Premium_Gift_ContactSelection_DeselectAll.uppercased() : nil, action: { + let header: ListViewItemHeader? = ChatListSearchItemHeader(type: .text(strings.Premium_Gift_ContactSelection_FrequentContacts.uppercased(), AnyHashable(hasDeselectAll ? 1 : 0)), theme: theme, strings: strings, actionTitle: hasDeselectAll ? strings.Premium_Gift_ContactSelection_DeselectAll.uppercased() : nil, action: { _ in interaction.deselectAll() }) diff --git a/submodules/ContactsPeerItem/Sources/ContactsPeerItem.swift b/submodules/ContactsPeerItem/Sources/ContactsPeerItem.swift index 856da22908..43847bb2a9 100644 --- a/submodules/ContactsPeerItem/Sources/ContactsPeerItem.swift +++ b/submodules/ContactsPeerItem/Sources/ContactsPeerItem.swift @@ -789,15 +789,16 @@ public class ContactsPeerItemNode: ItemListRevealOptionsItemNode { } else if peer.isFake { credibilityIcon = .text(color: item.presentationData.theme.chat.message.incoming.scamColor, string: item.presentationData.strings.Message_FakeAccount.uppercased()) } else if let emojiStatus = peer.emojiStatus { - if case .channel = peer, peer.isVerified { - verifiedIcon = .verified(fillColor: item.presentationData.theme.list.itemCheckColors.fillColor, foregroundColor: item.presentationData.theme.list.itemCheckColors.foregroundColor, sizeType: .compact) - } credibilityIcon = .animation(content: .customEmoji(fileId: emojiStatus.fileId), size: CGSize(width: 20.0, height: 20.0), placeholderColor: item.presentationData.theme.list.mediaPlaceholderColor, themeColor: item.presentationData.theme.list.itemAccentColor, loopMode: .count(2)) - } else if peer.isVerified { - credibilityIcon = .verified(fillColor: item.presentationData.theme.list.itemCheckColors.fillColor, foregroundColor: item.presentationData.theme.list.itemCheckColors.foregroundColor, sizeType: .compact) } else if peer.isPremium && !premiumConfiguration.isPremiumDisabled { credibilityIcon = .premium(color: item.presentationData.theme.list.itemAccentColor) } + + if peer.isVerified { + verifiedIcon = .verified(fillColor: item.presentationData.theme.list.itemCheckColors.fillColor, foregroundColor: item.presentationData.theme.list.itemCheckColors.foregroundColor, sizeType: .compact) + } else if let verification = peer.verification { + verifiedIcon = .animation(content: .customEmoji(fileId: verification.iconFileId), size: CGSize(width: 32.0, height: 32.0), placeholderColor: item.presentationData.theme.list.mediaPlaceholderColor, themeColor: item.presentationData.theme.list.itemAccentColor, loopMode: .count(0)) + } } case .deviceContact: break @@ -1349,7 +1350,48 @@ public class ContactsPeerItemNode: ItemListRevealOptionsItemNode { } let _ = titleApply() - let titleFrame = titleFrame.offsetBy(dx: revealOffset, dy: 0.0) + + var titleLeftOffset: CGFloat = 0.0 + if let verifiedIcon { + let animationCache = item.context.animationCache + let animationRenderer = item.context.animationRenderer + + let verifiedIconView: ComponentHostView + if let current = strongSelf.verifiedIconView { + verifiedIconView = current + } else { + verifiedIconView = ComponentHostView() + strongSelf.offsetContainerNode.view.addSubview(verifiedIconView) + strongSelf.verifiedIconView = verifiedIconView + } + + let verifiedIconComponent = EmojiStatusComponent( + context: item.context, + animationCache: animationCache, + animationRenderer: animationRenderer, + content: verifiedIcon, + isVisibleForAnimations: strongSelf.visibilityStatus, + action: nil, + emojiFileUpdated: nil + ) + strongSelf.verifiedIconComponent = verifiedIconComponent + + let iconSize = verifiedIconView.update( + transition: .immediate, + component: AnyComponent(verifiedIconComponent), + environment: {}, + containerSize: CGSize(width: 16.0, height: 16.0) + ) + + transition.updateFrame(view: verifiedIconView, frame: CGRect(origin: CGPoint(x: titleFrame.minX, y: floorToScreenPixels(titleFrame.midY - iconSize.height / 2.0)), size: iconSize)) + + titleLeftOffset += iconSize.width + 4.0 + } else if let verifiedIconView = strongSelf.verifiedIconView { + strongSelf.verifiedIconView = nil + verifiedIconView.removeFromSuperview() + } + + let titleFrame = titleFrame.offsetBy(dx: revealOffset + titleLeftOffset, dy: 0.0) transition.updateFrame(node: strongSelf.titleNode, frame: titleFrame) strongSelf.titleNode.alpha = item.enabled ? 1.0 : 0.4 @@ -1421,46 +1463,7 @@ public class ContactsPeerItemNode: ItemListRevealOptionsItemNode { strongSelf.credibilityIconView = nil credibilityIconView.removeFromSuperview() } - - if let verifiedIcon { - let animationCache = item.context.animationCache - let animationRenderer = item.context.animationRenderer - - let verifiedIconView: ComponentHostView - if let current = strongSelf.verifiedIconView { - verifiedIconView = current - } else { - verifiedIconView = ComponentHostView() - strongSelf.offsetContainerNode.view.addSubview(verifiedIconView) - strongSelf.verifiedIconView = verifiedIconView - } - - let verifiedIconComponent = EmojiStatusComponent( - context: item.context, - animationCache: animationCache, - animationRenderer: animationRenderer, - content: verifiedIcon, - isVisibleForAnimations: strongSelf.visibilityStatus, - action: nil, - emojiFileUpdated: nil - ) - strongSelf.verifiedIconComponent = verifiedIconComponent - - let iconSize = verifiedIconView.update( - transition: .immediate, - component: AnyComponent(verifiedIconComponent), - environment: {}, - containerSize: CGSize(width: 20.0, height: 20.0) - ) - - nextIconX += 4.0 - transition.updateFrame(view: verifiedIconView, frame: CGRect(origin: CGPoint(x: nextIconX, y: floorToScreenPixels(titleFrame.midY - iconSize.height / 2.0)), size: iconSize)) - nextIconX += iconSize.width - } else if let verifiedIconView = strongSelf.verifiedIconView { - strongSelf.verifiedIconView = nil - verifiedIconView.removeFromSuperview() - } - + var additionalRightInset: CGFloat = 0.0 if let (titleLayout, titleApply) = actionButtonTitleLayoutAndApply { let actionButtonTitleNode = titleApply() diff --git a/submodules/ContextUI/Sources/ContextControllerExtractedPresentationNode.swift b/submodules/ContextUI/Sources/ContextControllerExtractedPresentationNode.swift index f4d50da3b1..d229d6ddba 100644 --- a/submodules/ContextUI/Sources/ContextControllerExtractedPresentationNode.swift +++ b/submodules/ContextUI/Sources/ContextControllerExtractedPresentationNode.swift @@ -1495,8 +1495,32 @@ final class ContextControllerExtractedPresentationNode: ASDisplayNode, ContextCo } } - let completeWithActionStack = itemContentNode == nil && controllerContentNode == nil + var restoreOverlayViews: [() -> Void] = [] + if let overlayViews = self.getController()?.getOverlayViews?(), !overlayViews.isEmpty, let itemContentNode, let contentNodeSupernode = itemContentNode.supernode { + for view in overlayViews { + let originalFrame = view.frame + let originalSuperview = view.superview + let originalIndex = view.superview?.subviews.firstIndex(of: view) + let originalGroupOpacity = view.layer.allowsGroupOpacity + + contentNodeSupernode.view.insertSubview(view, aboveSubview: itemContentNode.view) + view.frame = view.convert(view.bounds, to: contentNodeSupernode.view) + view.layer.allowsGroupOpacity = true + view.layer.animateAlpha(from: 0.0, to: view.alpha, duration: 0.2) + + restoreOverlayViews.append({ + view.frame = originalFrame + view.layer.allowsGroupOpacity = originalGroupOpacity + if let originalIndex { + originalSuperview?.insertSubview(view, at: originalIndex) + } else { + originalSuperview?.addSubview(view) + } + }) + } + } + let completeWithActionStack = itemContentNode == nil && controllerContentNode == nil if let contentNode = itemContentNode { contentNode.containingItem.willUpdateIsExtractedToContextPreview?(false, transition) @@ -1542,7 +1566,6 @@ final class ContextControllerExtractedPresentationNode: ASDisplayNode, ContextCo additive: true, completion: { [weak self] _ in Queue.mainQueue().after(reactionContextNodeIsAnimatingOut ? 0.2 * UIView.animationDurationFactor() : 0.0, { - if let strongSelf = self, let contentNode = strongSelf.itemContentNode { switch contentNode.containingItem { case let .node(containingNode): @@ -1555,6 +1578,7 @@ final class ContextControllerExtractedPresentationNode: ASDisplayNode, ContextCo contentNode.containingItem.isExtractedToContextPreview = false contentNode.containingItem.isExtractedToContextPreviewUpdated?(false) + restoreOverlayViews.forEach({ $0() }) completion() }) } @@ -1595,6 +1619,7 @@ final class ContextControllerExtractedPresentationNode: ASDisplayNode, ContextCo } contentNode.layer.animateAlpha(from: 1.0, to: 0.0, duration: duration * 0.8, removeOnCompletion: false, completion: { _ in + restoreOverlayViews.forEach({ $0() }) completion() }) contentNode.layer.animate( @@ -1630,6 +1655,7 @@ final class ContextControllerExtractedPresentationNode: ASDisplayNode, ContextCo removeOnCompletion: false, completion: { _ in if completeWithActionStack { + restoreOverlayViews.forEach({ $0() }) completion() } } @@ -1657,18 +1683,6 @@ final class ContextControllerExtractedPresentationNode: ASDisplayNode, ContextCo if let reactionContextNode = self.reactionContextNode { reactionContextNode.animateOut(to: currentContentScreenFrame, animatingOutToReaction: self.reactionContextNodeIsAnimatingOut) } - - if let overlayViews = self.getController()?.getOverlayViews?(), !overlayViews.isEmpty { - for view in overlayViews { - if let snapshotView = view.snapshotView(afterScreenUpdates: false) { - snapshotView.frame = view.convert(view.bounds, to: nil) - self.view.addSubview(snapshotView) - snapshotView.layer.animateAlpha(from: 0.0, to: 1.0, duration: 0.2, removeOnCompletion: false, completion: { [weak snapshotView] _ in - snapshotView?.removeFromSuperview() - }) - } - } - } case .none: if animateReactionsIn, let reactionContextNode = self.reactionContextNode { reactionContextNode.animateIn(from: contentRect) diff --git a/submodules/Display/Source/TextNode.swift b/submodules/Display/Source/TextNode.swift index 8008042739..e10fadff69 100644 --- a/submodules/Display/Source/TextNode.swift +++ b/submodules/Display/Source/TextNode.swift @@ -142,13 +142,14 @@ private final class TextNodeLine { let range: NSRange? let isRTL: Bool var strikethroughs: [TextNodeStrikethrough] + var underlines: [TextNodeStrikethrough] var spoilers: [TextNodeSpoiler] var spoilerWords: [TextNodeSpoiler] var embeddedItems: [TextNodeEmbeddedItem] var attachments: [TextNodeAttachment] let additionalTrailingLine: (CTLine, Double)? - init(line: CTLine, frame: CGRect, ascent: CGFloat, descent: CGFloat, range: NSRange?, isRTL: Bool, strikethroughs: [TextNodeStrikethrough], spoilers: [TextNodeSpoiler], spoilerWords: [TextNodeSpoiler], embeddedItems: [TextNodeEmbeddedItem], attachments: [TextNodeAttachment], additionalTrailingLine: (CTLine, Double)?) { + init(line: CTLine, frame: CGRect, ascent: CGFloat, descent: CGFloat, range: NSRange?, isRTL: Bool, strikethroughs: [TextNodeStrikethrough], underlines: [TextNodeStrikethrough], spoilers: [TextNodeSpoiler], spoilerWords: [TextNodeSpoiler], embeddedItems: [TextNodeEmbeddedItem], attachments: [TextNodeAttachment], additionalTrailingLine: (CTLine, Double)?) { self.line = line self.frame = frame self.ascent = ascent @@ -156,6 +157,7 @@ private final class TextNodeLine { self.range = range self.isRTL = isRTL self.strikethroughs = strikethroughs + self.underlines = underlines self.spoilers = spoilers self.spoilerWords = spoilerWords self.embeddedItems = embeddedItems @@ -200,6 +202,14 @@ public struct TextNodeCutout: Equatable { } } +private let drawUnderlinesManually: Bool = { + if #available(iOS 18.0, *) { + return true + } else { + return false + } +}() + private func displayLineFrame(frame: CGRect, isRTL: Bool, boundingRect: CGRect, cutout: TextNodeCutout?) -> CGRect { if frame.width.isEqual(to: boundingRect.width) { return frame @@ -1439,6 +1449,7 @@ open class TextNode: ASDisplayNode, TextNodeProtocol { range: nil, isRTL: false, strikethroughs: [], + underlines: [], spoilers: [], spoilerWords: [], embeddedItems: [], @@ -1476,6 +1487,7 @@ open class TextNode: ASDisplayNode, TextNodeProtocol { range: NSRange(location: currentLineStartIndex, length: lineCharacterCount), isRTL: isRTL && segment.blockQuote == nil, strikethroughs: [], + underlines: [], spoilers: [], spoilerWords: [], embeddedItems: [], @@ -1746,6 +1758,7 @@ open class TextNode: ASDisplayNode, TextNodeProtocol { var first = true while true { var strikethroughs: [TextNodeStrikethrough] = [] + var underlines: [TextNodeStrikethrough] = [] var spoilers: [TextNodeSpoiler] = [] var spoilerWords: [TextNodeSpoiler] = [] var embeddedItems: [TextNodeEmbeddedItem] = [] @@ -2017,6 +2030,11 @@ open class TextNode: ASDisplayNode, TextNodeProtocol { let upperX = ceil(CTLineGetOffsetForStringIndex(coreTextLine, range.location + range.length, nil)) let x = lowerX < upperX ? lowerX : upperX strikethroughs.append(TextNodeStrikethrough(range: range, frame: CGRect(x: x, y: 0.0, width: abs(upperX - lowerX), height: fontLineHeight))) + } else if let _ = attributes[NSAttributedString.Key.underlineStyle] { + let lowerX = floor(CTLineGetOffsetForStringIndex(coreTextLine, range.location, nil)) + let upperX = ceil(CTLineGetOffsetForStringIndex(coreTextLine, range.location + range.length, nil)) + let x = lowerX < upperX ? lowerX : upperX + underlines.append(TextNodeStrikethrough(range: range, frame: CGRect(x: x, y: 0.0, width: abs(upperX - lowerX), height: fontLineHeight))) } else if let paragraphStyle = attributes[NSAttributedString.Key.paragraphStyle] as? NSParagraphStyle { headIndent = paragraphStyle.headIndent } @@ -2070,6 +2088,7 @@ open class TextNode: ASDisplayNode, TextNodeProtocol { range: NSMakeRange(effectiveLineRange.location, effectiveLineRange.length), isRTL: isRTL, strikethroughs: strikethroughs, + underlines: underlines, spoilers: spoilers, spoilerWords: spoilerWords, embeddedItems: embeddedItems, @@ -2132,6 +2151,11 @@ open class TextNode: ASDisplayNode, TextNodeProtocol { let upperX = ceil(CTLineGetOffsetForStringIndex(coreTextLine, range.location + range.length, nil)) let x = lowerX < upperX ? lowerX : upperX strikethroughs.append(TextNodeStrikethrough(range: range, frame: CGRect(x: x, y: 0.0, width: abs(upperX - lowerX), height: fontLineHeight))) + } else if let _ = attributes[NSAttributedString.Key.underlineStyle] { + let lowerX = floor(CTLineGetOffsetForStringIndex(coreTextLine, range.location, nil)) + let upperX = ceil(CTLineGetOffsetForStringIndex(coreTextLine, range.location + range.length, nil)) + let x = lowerX < upperX ? lowerX : upperX + underlines.append(TextNodeStrikethrough(range: range, frame: CGRect(x: x, y: 0.0, width: abs(upperX - lowerX), height: fontLineHeight))) } else if let paragraphStyle = attributes[NSAttributedString.Key.paragraphStyle] as? NSParagraphStyle { headIndent = paragraphStyle.headIndent } @@ -2179,6 +2203,7 @@ open class TextNode: ASDisplayNode, TextNodeProtocol { range: NSMakeRange(lineRange.location, lineRange.length), isRTL: isRTL, strikethroughs: strikethroughs, + underlines: underlines, spoilers: spoilers, spoilerWords: spoilerWords, embeddedItems: embeddedItems, @@ -2552,6 +2577,26 @@ open class TextNode: ASDisplayNode, TextNodeProtocol { } } + if drawUnderlinesManually { + if !line.underlines.isEmpty { + for strikethrough in line.underlines { + guard let lineRange = line.range else { + continue + } + var textColor: UIColor? + layout.attributedString?.enumerateAttributes(in: NSMakeRange(lineRange.location, lineRange.length), options: []) { attributes, range, _ in + if range == strikethrough.range, let color = attributes[NSAttributedString.Key.foregroundColor] as? UIColor { + textColor = color + } + } + if let textColor = textColor { + context.setFillColor(textColor.cgColor) + } + let frame = strikethrough.frame.offsetBy(dx: lineFrame.minX, dy: lineFrame.minY) + context.fill(CGRect(x: frame.minX, y: frame.minY + 1.0, width: frame.width, height: 1.0)) + } + } + } if !line.strikethroughs.isEmpty { for strikethrough in line.strikethroughs { guard let lineRange = line.range else { diff --git a/submodules/DrawingUI/BUILD b/submodules/DrawingUI/BUILD index 68824329eb..9eb89f2f76 100644 --- a/submodules/DrawingUI/BUILD +++ b/submodules/DrawingUI/BUILD @@ -95,7 +95,6 @@ swift_library( "//submodules/FastBlur:FastBlur", "//submodules/TelegramUI/Components/MediaEditor", "//submodules/ChatPresentationInterfaceState:ChatPresentationInterfaceState", - "//submodules/StickerPackPreviewUI:StickerPackPreviewUI", "//submodules/TelegramUI/Components/LottieComponent", "//submodules/TelegramUI/Components/LottieComponentResourceContent", "//submodules/ImageTransparency", diff --git a/submodules/FeaturedStickersScreen/BUILD b/submodules/FeaturedStickersScreen/BUILD index 450df08ea2..bf593d7323 100644 --- a/submodules/FeaturedStickersScreen/BUILD +++ b/submodules/FeaturedStickersScreen/BUILD @@ -19,7 +19,6 @@ swift_library( "//submodules/TelegramPresentationData:TelegramPresentationData", "//submodules/TelegramUIPreferences:TelegramUIPreferences", "//submodules/MergeLists:MergeLists", - "//submodules/StickerPackPreviewUI:StickerPackPreviewUI", "//submodules/StickerPeekUI:StickerPeekUI", "//submodules/OverlayStatusController:OverlayStatusController", "//submodules/PresentationDataUtils:PresentationDataUtils", @@ -33,6 +32,7 @@ swift_library( "//submodules/TelegramAnimatedStickerNode:TelegramAnimatedStickerNode", "//submodules/TelegramUI/Components/EmojiTextAttachmentView", "//submodules/TextFormat", + "//submodules/ListSectionHeaderNode", ], visibility = [ "//visibility:public", diff --git a/submodules/FeaturedStickersScreen/Sources/ChatMediaInputTrendingPane.swift b/submodules/FeaturedStickersScreen/Sources/ChatMediaInputTrendingPane.swift index 79991c26d8..35d2f53be1 100644 --- a/submodules/FeaturedStickersScreen/Sources/ChatMediaInputTrendingPane.swift +++ b/submodules/FeaturedStickersScreen/Sources/ChatMediaInputTrendingPane.swift @@ -9,9 +9,9 @@ import TelegramPresentationData import MergeLists import OverlayStatusController import AccountContext -import StickerPackPreviewUI import PresentationDataUtils import UndoUI +import StickerResources public final class TrendingPaneInteraction { public let installPack: (ItemCollectionInfo) -> Void @@ -355,13 +355,26 @@ public final class ChatMediaInputTrendingPane: ChatMediaInputPane { let presentationData = strongSelf.context.sharedContext.currentPresentationData.with { $0 }.withUpdated(theme: forceTheme) updatedPresentationData = (presentationData, .single(presentationData)) } - let controller = StickerPackScreen(context: strongSelf.context, updatedPresentationData: updatedPresentationData, mainStickerPack: packReference, stickerPacks: [packReference], actionTitle: strongSelf.stickerActionTitle, parentNavigationController: strongSelf.interaction.getNavigationController(), sendSticker: { fileReference, sourceNode, sourceRect in - if let strongSelf = self { - return strongSelf.interaction.sendSticker(fileReference, false, false, nil, false, sourceNode, sourceRect, nil, []) - } else { - return false - } - }) + + let controller = strongSelf.context.sharedContext.makeStickerPackScreen( + context: strongSelf.context, + updatedPresentationData: updatedPresentationData, + mainStickerPack: packReference, + stickerPacks: [packReference], + loadedStickerPacks: [], + actionTitle: strongSelf.stickerActionTitle, + isEditing: false, + expandIfNeeded: false, + parentNavigationController: strongSelf.interaction.getNavigationController(), + sendSticker: { fileReference, sourceNode, sourceRect in + if let strongSelf = self { + return strongSelf.interaction.sendSticker(fileReference, false, false, nil, false, sourceNode, sourceRect, nil, []) + } else { + return false + } + }, + actionPerformed: nil + ) strongSelf.interaction.presentController(controller, nil) } }, getItemIsPreviewed: self.getItemIsPreviewed, diff --git a/submodules/FeaturedStickersScreen/Sources/FeaturedStickersScreen.swift b/submodules/FeaturedStickersScreen/Sources/FeaturedStickersScreen.swift index 1cce8c5c0b..9dc397a850 100644 --- a/submodules/FeaturedStickersScreen/Sources/FeaturedStickersScreen.swift +++ b/submodules/FeaturedStickersScreen/Sources/FeaturedStickersScreen.swift @@ -9,7 +9,6 @@ import AccountContext import TelegramPresentationData import TelegramUIPreferences import MergeLists -import StickerPackPreviewUI import StickerPeekUI import OverlayStatusController import PresentationDataUtils @@ -319,13 +318,25 @@ 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, 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 { - return false - } - }) + let controller = strongSelf.context.sharedContext.makeStickerPackScreen( + context: strongSelf.context, + updatedPresentationData: strongSelf.updatedPresentationData, + mainStickerPack: packReference, + stickerPacks: [packReference], + loadedStickerPacks: [], + actionTitle: strongSelf.controller?.stickerActionTitle, + isEditing: false, + expandIfNeeded: false, + parentNavigationController: strongSelf.controller?.navigationController as? NavigationController, + sendSticker: { file, sourceNode, sourceRect in + if let strongSelf = self { + return strongSelf.sendSticker?(file, sourceNode, sourceRect) ?? false + } else { + return false + } + }, + actionPerformed: nil + ) strongSelf.controller?.present(controller, in: .window(.root)) } }, @@ -552,14 +563,25 @@ private final class FeaturedStickersScreenNode: ViewControllerTracingNode { switch attribute { case let .Sticker(_, packReference, _): if let packReference = packReference { - 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 { - return false - } - }) - + let controller = strongSelf.context.sharedContext.makeStickerPackScreen( + context: strongSelf.context, + updatedPresentationData: strongSelf.updatedPresentationData, + mainStickerPack: packReference, + stickerPacks: [packReference], + loadedStickerPacks: [], + actionTitle: strongSelf.controller?.stickerActionTitle, + isEditing: false, + expandIfNeeded: false, + parentNavigationController: strongSelf.controller?.navigationController as? NavigationController, + sendSticker: { file, sourceNode, sourceRect in + if let strongSelf = self { + return strongSelf.sendSticker?(file, sourceNode, sourceRect) ?? false + } else { + return false + } + }, + actionPerformed: nil + ) strongSelf.controller?.view.endEditing(true) strongSelf.controller?.present(controller, in: .window(.root)) } @@ -640,14 +662,25 @@ private final class FeaturedStickersScreenNode: ViewControllerTracingNode { switch attribute { case let .Sticker(_, packReference, _): if let packReference = packReference { - 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 { - return false - } - }) - + let controller = strongSelf.context.sharedContext.makeStickerPackScreen( + context: strongSelf.context, + updatedPresentationData: strongSelf.updatedPresentationData, + mainStickerPack: packReference, + stickerPacks: [packReference], + loadedStickerPacks: [], + actionTitle: strongSelf.controller?.stickerActionTitle, + isEditing: false, + expandIfNeeded: false, + parentNavigationController: strongSelf.controller?.navigationController as? NavigationController, + sendSticker: { file, sourceNode, sourceRect in + if let strongSelf = self { + return strongSelf.sendSticker?(file, sourceNode, sourceRect) ?? false + } else { + return false + } + }, + actionPerformed: nil + ) strongSelf.controller?.view.endEditing(true) strongSelf.controller?.present(controller, in: .window(.root)) } @@ -1192,13 +1225,26 @@ 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, 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 { - return false - } - }) + + let controller = strongSelf.context.sharedContext.makeStickerPackScreen( + context: strongSelf.context, + updatedPresentationData: strongSelf.updatedPresentationData, + mainStickerPack: packReference, + stickerPacks: [packReference], + loadedStickerPacks: [], + actionTitle: strongSelf.controller?.stickerActionTitle, + isEditing: false, + expandIfNeeded: false, + parentNavigationController: strongSelf.controller?.navigationController as? NavigationController, + sendSticker: { file, sourceNode, sourceRect in + if let strongSelf = self { + return strongSelf.sendSticker?(file, sourceNode, sourceRect) ?? false + } else { + return false + } + }, + actionPerformed: nil + ) strongSelf.controller?.present(controller, in: .window(.root)) } }, install: { [weak self] info, items, install in diff --git a/submodules/FeaturedStickersScreen/Sources/StickerPaneSearchGlobaltem.swift b/submodules/FeaturedStickersScreen/Sources/StickerPaneSearchGlobaltem.swift index c6342832f8..5431703c70 100644 --- a/submodules/FeaturedStickersScreen/Sources/StickerPaneSearchGlobaltem.swift +++ b/submodules/FeaturedStickersScreen/Sources/StickerPaneSearchGlobaltem.swift @@ -6,9 +6,9 @@ import TelegramCore import SwiftSignalKit import Postbox import TelegramPresentationData -import StickerPackPreviewUI import ListSectionHeaderNode import AccountContext +import StickerResources final class StickerPaneSearchGlobalSection: GridSection { let title: String? diff --git a/submodules/GalleryUI/Sources/GalleryController.swift b/submodules/GalleryUI/Sources/GalleryController.swift index 2d9fcd0b2c..a3df6c7ceb 100644 --- a/submodules/GalleryUI/Sources/GalleryController.swift +++ b/submodules/GalleryUI/Sources/GalleryController.swift @@ -566,7 +566,7 @@ private func galleryEntriesForMessageHistoryEntries(_ entries: [MessageHistoryEn return results } -public class GalleryController: ViewController, StandalonePresentableController, KeyShortcutResponder { +public class GalleryController: ViewController, StandalonePresentableController, KeyShortcutResponder, GalleryControllerProtocol { public static let darkNavigationTheme = NavigationBarTheme(buttonColor: .white, disabledButtonColor: UIColor(rgb: 0x525252), primaryTextColor: .white, backgroundColor: UIColor(white: 0.0, alpha: 0.6), enableBackgroundBlur: false, separatorColor: UIColor(white: 0.0, alpha: 0.8), badgeBackgroundColor: .clear, badgeStrokeColor: .clear, badgeTextColor: .clear) public static let lightNavigationTheme = NavigationBarTheme(buttonColor: UIColor(rgb: 0x007aff), disabledButtonColor: UIColor(rgb: 0xd0d0d0), primaryTextColor: .black, backgroundColor: UIColor(red: 0.968626451, green: 0.968626451, blue: 0.968626451, alpha: 1.0), enableBackgroundBlur: false, separatorColor: UIColor(red: 0.6953125, green: 0.6953125, blue: 0.6953125, alpha: 1.0), badgeBackgroundColor: .clear, badgeStrokeColor: .clear, badgeTextColor: .clear) diff --git a/submodules/ItemListPeerItem/Sources/ItemListPeerItem.swift b/submodules/ItemListPeerItem/Sources/ItemListPeerItem.swift index 51b612227f..70f3cd07e1 100644 --- a/submodules/ItemListPeerItem/Sources/ItemListPeerItem.swift +++ b/submodules/ItemListPeerItem/Sources/ItemListPeerItem.swift @@ -735,6 +735,8 @@ public class ItemListPeerItemNode: ItemListRevealOptionsItemNode, ItemListItemNo private let statusNode: TextNode private var credibilityIconComponent: EmojiStatusComponent? private var credibilityIconView: ComponentHostView? + private var verifiedIconComponent: EmojiStatusComponent? + private var verifiedIconView: ComponentHostView? private var switchNode: SwitchNode? private var checkNode: ASImageNode? private var leftCheckNode: CheckNode? @@ -775,6 +777,14 @@ public class ItemListPeerItemNode: ItemListRevealOptionsItemNode, ItemListItemNo containerSize: credibilityIconView.bounds.size ) } + if let verifiedIconView = self.verifiedIconView, let verifiedIconComponent = self.verifiedIconComponent { + let _ = verifiedIconView.update( + transition: .immediate, + component: AnyComponent(verifiedIconComponent.withVisibleForAnimations(self.visibilityStatus)), + environment: {}, + containerSize: verifiedIconView.bounds.size + ) + } if let avatarIconView = self.avatarIconView, let avatarIconComponentView = avatarIconView.view, let avatarIconComponent = self.avatarIconComponent { let _ = avatarIconView.update( transition: .immediate, @@ -912,6 +922,7 @@ public class ItemListPeerItemNode: ItemListRevealOptionsItemNode, ItemListItemNo var updatedLabelBadgeImage: UIImage? var credibilityIcon: EmojiStatusComponent.Content? + var verifiedIcon: EmojiStatusComponent.Content? if case .threatSelfAsSaved = item.aliasHandling, item.peer.id == item.context.accountPeerId { } else { @@ -921,14 +932,29 @@ public class ItemListPeerItemNode: ItemListRevealOptionsItemNode, ItemListItemNo credibilityIcon = .text(color: item.presentationData.theme.chat.message.incoming.scamColor, string: item.presentationData.strings.Message_FakeAccount.uppercased()) } else if let emojiStatus = item.peer.emojiStatus { credibilityIcon = .animation(content: .customEmoji(fileId: emojiStatus.fileId), size: CGSize(width: 20.0, height: 20.0), placeholderColor: item.presentationData.theme.list.mediaPlaceholderColor, themeColor: item.presentationData.theme.list.itemAccentColor, loopMode: .count(2)) - } else if item.peer.isVerified { - credibilityIcon = .verified(fillColor: item.presentationData.theme.list.itemCheckColors.fillColor, foregroundColor: item.presentationData.theme.list.itemCheckColors.foregroundColor, sizeType: .compact) } else if item.peer.isPremium && !item.context.isPremiumDisabled { credibilityIcon = .premium(color: item.presentationData.theme.list.itemAccentColor) } + + if item.peer.isVerified { + verifiedIcon = .verified(fillColor: item.presentationData.theme.list.itemCheckColors.fillColor, foregroundColor: item.presentationData.theme.list.itemCheckColors.foregroundColor, sizeType: .compact) + } else if let verification = item.peer.verification { + verifiedIcon = .animation(content: .customEmoji(fileId: verification.iconFileId), size: CGSize(width: 32.0, height: 32.0), placeholderColor: item.presentationData.theme.list.mediaPlaceholderColor, themeColor: item.presentationData.theme.list.itemAccentColor, loopMode: .count(0)) + } } var titleIconsWidth: CGFloat = 0.0 + if let verifiedIcon = verifiedIcon { + titleIconsWidth += 4.0 + switch verifiedIcon { + case let .text(_, string): + let textString = NSAttributedString(string: string, font: Font.bold(10.0), textColor: .black, paragraphAlignment: .center) + let stringRect = textString.boundingRect(with: CGSize(width: 100.0, height: 16.0), options: .usesLineFragmentOrigin, context: nil) + titleIconsWidth += floor(stringRect.width) + 11.0 + default: + titleIconsWidth += 16.0 + } + } if let credibilityIcon = credibilityIcon { titleIconsWidth += 4.0 switch credibilityIcon { @@ -1407,7 +1433,50 @@ public class ItemListPeerItemNode: ItemListRevealOptionsItemNode, ItemListItemNo transition.updateFrame(node: strongSelf.topStripeNode, frame: CGRect(origin: CGPoint(x: 0.0, y: -min(insets.top, separatorHeight)), size: CGSize(width: layoutSize.width, height: separatorHeight))) transition.updateFrame(node: strongSelf.bottomStripeNode, frame: CGRect(origin: CGPoint(x: bottomStripeInset, y: contentSize.height + bottomStripeOffset), size: CGSize(width: layoutSize.width - bottomStripeInset, height: separatorHeight))) - let titleFrame = CGRect(origin: CGPoint(x: leftInset + revealOffset + editingOffset, y: verticalInset + verticalOffset), size: titleLayout.size) + var titleFrame = CGRect(origin: CGPoint(x: leftInset + revealOffset + editingOffset, y: verticalInset + verticalOffset), size: titleLayout.size) + + var titleLeftOffset: CGFloat = 0.0 + if let verifiedIcon = verifiedIcon { + let animationCache = item.context.animationCache + let animationRenderer = item.context.animationRenderer + + let verifiedIconView: ComponentHostView + if let current = strongSelf.verifiedIconView { + verifiedIconView = current + } else { + verifiedIconView = ComponentHostView() + strongSelf.containerNode.view.addSubview(verifiedIconView) + strongSelf.verifiedIconView = verifiedIconView + } + + let verifiedIconComponent = EmojiStatusComponent( + postbox: item.context.postbox, + energyUsageSettings: item.context.energyUsageSettings, + resolveInlineStickers: item.context.resolveInlineStickers, + animationCache: animationCache, + animationRenderer: animationRenderer, + content: verifiedIcon, + isVisibleForAnimations: strongSelf.visibilityStatus, + action: nil, + emojiFileUpdated: nil + ) + strongSelf.verifiedIconComponent = verifiedIconComponent + let iconSize = verifiedIconView.update( + transition: .immediate, + component: AnyComponent(verifiedIconComponent), + environment: {}, + containerSize: CGSize(width: 20.0, height: 20.0) + ) + + transition.updateFrame(view: verifiedIconView, frame: CGRect(origin: CGPoint(x: titleFrame.maxX + 4.0, y: floorToScreenPixels(titleFrame.midY - iconSize.height / 2.0)), size: iconSize)) + + titleLeftOffset += iconSize.width + 4.0 + } else if let verifiedIconView = strongSelf.verifiedIconView { + strongSelf.verifiedIconView = nil + verifiedIconView.removeFromSuperview() + } + titleFrame = titleFrame.offsetBy(dx: titleLeftOffset, dy: 0.0) + transition.updateFrame(node: strongSelf.titleNode, frame: titleFrame) transition.updateFrame(node: strongSelf.statusNode, frame: CGRect(origin: CGPoint(x: leftInset + revealOffset + editingOffset, y: strongSelf.titleNode.frame.maxY + titleSpacing), size: statusLayout.size)) @@ -1843,7 +1912,13 @@ public class ItemListPeerItemNode: ItemListRevealOptionsItemNode, ItemListItemNo editingOffset = 0.0 } - transition.updateFrame(node: self.titleNode, frame: CGRect(origin: CGPoint(x: leftInset + revealOffset + editingOffset, y: self.titleNode.frame.minY), size: self.titleNode.bounds.size)) + var titleLeftOffset: CGFloat = 0.0 + if let verifiedIconView = self.verifiedIconView { + transition.updateFrame(view: verifiedIconView, frame: CGRect(origin: CGPoint(x: leftInset + revealOffset + editingOffset, y: verifiedIconView.frame.minY), size: verifiedIconView.bounds.size)) + titleLeftOffset += verifiedIconView.bounds.size.width + 4.0 + } + + transition.updateFrame(node: self.titleNode, frame: CGRect(origin: CGPoint(x: leftInset + revealOffset + editingOffset + titleLeftOffset, y: self.titleNode.frame.minY), size: self.titleNode.bounds.size)) transition.updateFrame(node: self.statusNode, frame: CGRect(origin: CGPoint(x: leftInset + revealOffset + editingOffset, y: self.statusNode.frame.minY), size: self.statusNode.bounds.size)) if let credibilityIconView = self.credibilityIconView { diff --git a/submodules/ListSectionHeaderNode/Sources/ListSectionHeaderNode.swift b/submodules/ListSectionHeaderNode/Sources/ListSectionHeaderNode.swift index 7b4b0095c4..204443f6e1 100644 --- a/submodules/ListSectionHeaderNode/Sources/ListSectionHeaderNode.swift +++ b/submodules/ListSectionHeaderNode/Sources/ListSectionHeaderNode.swift @@ -77,7 +77,7 @@ public final class ListSectionHeaderNode: ASDisplayNode { } } - public var activateAction: (() -> Void)? + public var activateAction: ((ASDisplayNode) -> Void)? public init(theme: PresentationTheme) { self.theme = theme @@ -120,6 +120,10 @@ public final class ListSectionHeaderNode: ASDisplayNode { actionColor = self.theme.list.itemDestructiveColor } let attributedText = NSMutableAttributedString(string: action, font: actionFont, textColor: actionColor) + if let range = attributedText.string.range(of: "<"), let arrowImage = UIImage(bundleImageName: "Item List/HeaderContextDisclosureArrow") { + attributedText.addAttribute(.attachment, value: arrowImage, range: NSRange(range, in: attributedText.string)) + attributedText.addAttribute(.baselineOffset, value: 2.0, range: NSRange(range, in: attributedText.string)) + } if let range = attributedText.string.range(of: ">"), let arrowImage = UIImage(bundleImageName: "Item List/InlineTextRightArrow") { attributedText.addAttribute(.attachment, value: arrowImage, range: NSRange(range, in: attributedText.string)) attributedText.addAttribute(.baselineOffset, value: 1.0, range: NSRange(range, in: attributedText.string)) @@ -158,6 +162,8 @@ public final class ListSectionHeaderNode: ASDisplayNode { } @objc private func actionButtonPressed() { - self.activateAction?() + if let actionButton = self.actionButton { + self.activateAction?(actionButton) + } } } diff --git a/submodules/MediaPickerUI/BUILD b/submodules/MediaPickerUI/BUILD index 22bd024349..ce1515accc 100644 --- a/submodules/MediaPickerUI/BUILD +++ b/submodules/MediaPickerUI/BUILD @@ -52,6 +52,8 @@ swift_library( "//submodules/Components/ComponentDisplayAdapters", "//submodules/AnimatedCountLabelNode", "//submodules/TelegramUI/Components/MediaAssetsContext", + "//submodules/TelegramUI/Components/AvatarBackground", + "//submodules/TelegramUI/Components/EmojiTextAttachmentView", ], visibility = [ "//visibility:public", diff --git a/submodules/MediaPickerUI/Sources/AvatarEditorPreviewView.swift b/submodules/MediaPickerUI/Sources/AvatarEditorPreviewView.swift new file mode 100644 index 0000000000..b03ed229e2 --- /dev/null +++ b/submodules/MediaPickerUI/Sources/AvatarEditorPreviewView.swift @@ -0,0 +1,191 @@ +import Foundation +import UIKit +import Display +import SwiftSignalKit +import TelegramCore +import Postbox +import AvatarBackground +import AccountContext +import EmojiTextAttachmentView +import TextFormat +import ComponentFlow +import MultilineTextComponent + +final class AvatarEditorPreviewView: UIView { + private let context: AccountContext + private var disposable: Disposable? + private var files: [TelegramMediaFile] = [] + private var currentIndex = 0 + private var currentBackgroundIndex = 0 + private var switchingToNext = false + + private let backgroundView = UIImageView() + private let label = ComponentView() + private var animationLayer: InlineStickerItemLayer? + private var preloadDisposableSet = DisposableSet() + + private var timer: SwiftSignalKit.Timer? + + private var currentSize: CGSize? + + var tapped: () -> Void = {} + + init(context: AccountContext) { + self.context = context + + super.init(frame: .zero) + + self.addSubview(self.backgroundView) + + let stickersKey: PostboxViewKey = .orderedItemList(id: Namespaces.OrderedItemList.CloudFeaturedProfilePhotoEmoji) + self.disposable = (context.account.postbox.combinedView(keys: [stickersKey]) + |> runOn(Queue.concurrentDefaultQueue()) + |> deliverOnMainQueue).start(next: { [weak self] views in + guard let self else { + return + } + if let view = views.views[stickersKey] as? OrderedItemListView { + var files: [TelegramMediaFile] = [] + for item in view.items.prefix(8) { + if let mediaItem = item.contents.get(RecentMediaItem.self) { + let file = mediaItem.media + files.append(file) + + self.preloadDisposableSet.add(freeMediaFileResourceInteractiveFetched(account: context.account, userLocation: .other, fileReference: .standalone(media: file), resource: file.resource).start()) + } + } + self.files = files + if let size = self.currentSize { + self.updateLayout(size: size) + } + } + }) + + let tapRecognizer = UITapGestureRecognizer(target: self, action: #selector(self.handleTap)) + self.addGestureRecognizer(tapRecognizer) + } + + required init?(coder: NSCoder) { + preconditionFailure() + } + + deinit { + self.disposable?.dispose() + self.preloadDisposableSet.dispose() + self.timer?.invalidate() + } + + @objc private func handleTap() { + self.tapped() + } + + func updateLayout(size: CGSize) { + self.currentSize = size + self.backgroundView.frame = CGRect(origin: .zero, size: size) + + //TODO:localize + let labelSize = self.label.update( + transition: .immediate, + component: AnyComponent( + MultilineTextComponent( + text: .plain(NSAttributedString( + string: "Use an Emoji", + font: Font.semibold(14.0), + textColor: .white + )), + textShadowColor: UIColor(white: 0.0, alpha: 0.4), + textShadowBlur: 4.0 + ) + ), + environment: {}, + containerSize: size + ) + if let view = self.label.view { + if view.superview == nil { + self.addSubview(view) + } + view.frame = CGRect(origin: CGPoint(x: floor((size.width - labelSize.width) / 2.0), y: size.height - labelSize.height - 20.0), size: labelSize) + } + + guard !self.files.isEmpty else { + if self.backgroundView.image == nil { + self.backgroundView.image = AvatarBackground.defaultBackgrounds[self.currentBackgroundIndex].generateImage(size: size) + } + return + } + + if self.timer == nil { + self.timer = SwiftSignalKit.Timer(timeout: 2.0, repeat: true, completion: { [weak self] in + guard let self else { + return + } + self.switchingToNext = true + if let size = self.currentSize { + self.updateLayout(size: size) + } + }, queue: Queue.mainQueue()) + self.timer?.start() + } + + let iconSize = CGSize(width: 64.0, height: 64.0) + let animationLayer: InlineStickerItemLayer + var disappearingAnimationLayer: InlineStickerItemLayer? + if let current = self.animationLayer, !self.switchingToNext { + animationLayer = current + } else { + if self.switchingToNext { + self.currentIndex = (self.currentIndex + 1) % self.files.count + self.currentBackgroundIndex = (self.currentBackgroundIndex + 1) % AvatarBackground.defaultBackgrounds.count + disappearingAnimationLayer = self.animationLayer + self.switchingToNext = false + } + + if let image = self.backgroundView.image { + let snapshotView = UIImageView(image: image) + self.insertSubview(snapshotView, aboveSubview: self.backgroundView) + snapshotView.layer.animateAlpha(from: 1.0, to: 0.0, duration: 0.2, removeOnCompletion: false, completion: { _ in + snapshotView.removeFromSuperview() + }) + } + self.backgroundView.image = AvatarBackground.defaultBackgrounds[self.currentBackgroundIndex].generateImage(size: size) + + let file = self.files[self.currentIndex] + let emoji = ChatTextInputTextCustomEmojiAttribute( + interactivelySelectedFromPackId: nil, + fileId: file.fileId.id, + file: file + ) + animationLayer = InlineStickerItemLayer( + context: .account(self.context), + userLocation: .other, + attemptSynchronousLoad: false, + emoji: emoji, + file: file, + cache: self.context.animationCache, + renderer: self.context.animationRenderer, + unique: true, + placeholderColor: UIColor(white: 1.0, alpha: 0.1), + pointSize: iconSize, + loopCount: 1 + ) + animationLayer.isVisibleForAnimations = true + self.layer.addSublayer(animationLayer) + self.animationLayer = animationLayer + + animationLayer.animateAlpha(from: 0.0, to: 1.0, duration: 0.2) + animationLayer.animatePosition(from: CGPoint(x: 0.0, y: 10.0), to: .zero, duration: 0.2, additive: true) + animationLayer.animateScale(from: 0.01, to: 1.0, duration: 0.2) + } + + + animationLayer.frame = CGRect(origin: CGPoint(x: floor((size.width - iconSize.width) / 2.0), y: floor((size.height - iconSize.height) / 2.0) - 10.0), size: iconSize) + + if let disappearingAnimationLayer { + disappearingAnimationLayer.animateAlpha(from: 1.0, to: 0.0, duration: 0.2, removeOnCompletion: false, completion: { _ in + disappearingAnimationLayer.removeFromSuperlayer() + }) + disappearingAnimationLayer.animatePosition(from: .zero, to: CGPoint(x: 0.0, y: -10.0), duration: 0.2, removeOnCompletion: false, additive: true) + disappearingAnimationLayer.animateScale(from: 1.0, to: 0.01, duration: 0.2, removeOnCompletion: false) + } + } +} diff --git a/submodules/MediaPickerUI/Sources/MediaPickerGridItem.swift b/submodules/MediaPickerUI/Sources/MediaPickerGridItem.swift index b26687edc4..77d4a01edd 100644 --- a/submodules/MediaPickerUI/Sources/MediaPickerGridItem.swift +++ b/submodules/MediaPickerUI/Sources/MediaPickerGridItem.swift @@ -241,7 +241,7 @@ final class MediaPickerGridItemNode: GridItemNode { } } - func updateSelectionState(animated: Bool = false) { + func updateSelectionState(isFirstTime: Bool = false, animated: Bool = false) { if self.checkNode == nil, let _ = self.interaction?.selectionState, self.selectable, let theme = self.theme { let checkNode = InteractiveCheckNode(theme: CheckNodeTheme(theme: theme, style: .overlay)) checkNode.valueChanged = { [weak self] value in @@ -254,6 +254,11 @@ final class MediaPickerGridItemNode: GridItemNode { self.addSubnode(checkNode) self.checkNode = checkNode self.setNeedsLayout() + + if !isFirstTime { + checkNode.layer.animateScale(from: 0.2, to: 1.0, duration: 0.5, timingFunction: kCAMediaTimingFunctionSpring) + checkNode.layer.animateAlpha(from: 0.0, to: 1.0, duration: 0.5, timingFunction: kCAMediaTimingFunctionSpring) + } } if let interaction = self.interaction, let selectionState = interaction.selectionState { @@ -429,6 +434,8 @@ final class MediaPickerGridItemNode: GridItemNode { } func setup(interaction: MediaPickerInteraction, fetchResult: PHFetchResult, index: Int, theme: PresentationTheme, selectable: Bool, enableAnimations: Bool, stories: Bool) { + let isFirstTime = self.currentAssetState == nil + self.interaction = interaction self.theme = theme self.selectable = selectable @@ -639,7 +646,7 @@ final class MediaPickerGridItemNode: GridItemNode { self.setNeedsLayout() } - self.updateSelectionState() + self.updateSelectionState(isFirstTime: isFirstTime) self.updateHiddenMedia() } diff --git a/submodules/MediaPickerUI/Sources/MediaPickerScreen.swift b/submodules/MediaPickerUI/Sources/MediaPickerScreen.swift index 3dddffaa39..c63354c340 100644 --- a/submodules/MediaPickerUI/Sources/MediaPickerScreen.swift +++ b/submodules/MediaPickerUI/Sources/MediaPickerScreen.swift @@ -161,6 +161,7 @@ public final class MediaPickerScreenImpl: ViewController, MediaPickerScreen, Att case story case addImage case createSticker + case createAvatar } case assets(PHAssetCollection?, AssetsMode) @@ -183,8 +184,11 @@ public final class MediaPickerScreenImpl: ViewController, MediaPickerScreen, Att private let canBoostToUnrestrict: Bool fileprivate let paidMediaAllowed: Bool private let subject: Subject + fileprivate let forCollage: Bool private let saveEditedPhotos: Bool + private var explicitMultipleSelection = false + private let titleView: MediaPickerTitleView private let cancelButtonNode: WebAppCancelButtonNode private let moreButtonNode: MoreButtonNode @@ -203,6 +207,7 @@ public final class MediaPickerScreenImpl: ViewController, MediaPickerScreen, Att public var createFromScratch: () -> Void = {} public var presentFilePicker: () -> Void = {} + public var openAvatarEditor: () -> Void = {} private var completed = false public var legacyCompletion: (_ signals: [Any], _ silently: Bool, _ scheduleTime: Int32?, ChatSendMessageActionSheetController.SendParameters?, @escaping (String) -> UIView?, @escaping () -> Void) -> Void = { _, _, _, _, _, _ in } @@ -260,6 +265,8 @@ public final class MediaPickerScreenImpl: ViewController, MediaPickerScreen, Att fileprivate var modernCameraView: CameraSimplePreviewView? fileprivate var modernCameraTapGestureRecognizer: UITapGestureRecognizer? + fileprivate var avatarEditorPreviewView: AvatarEditorPreviewView? + private var cameraActivateAreaNode: AccessibilityAreaNode private var placeholderNode: MediaPickerPlaceholderNode? private var manageNode: MediaPickerManageNode? @@ -271,7 +278,7 @@ public final class MediaPickerScreenImpl: ViewController, MediaPickerScreen, Att private var nextStableId: Int = 1 private var currentEntries: [MediaPickerGridEntry] = [] private var enqueuedTransactions: [MediaPickerGridTransaction] = [] - private var state: State? + fileprivate var state: State? private var preloadPromise = ValuePromise(true) @@ -515,7 +522,7 @@ public final class MediaPickerScreenImpl: ViewController, MediaPickerScreen, Att self.gridNode.scrollView.alwaysBounceVertical = true self.gridNode.scrollView.showsVerticalScrollIndicator = false - if case let .assets(_, mode) = controller.subject, [.wallpaper, .story, .addImage, .createSticker].contains(mode) { + if case let .assets(_, mode) = controller.subject, [.wallpaper, .story, .addImage, .createSticker, .createAvatar].contains(mode) { } else { let selectionGesture = MediaPickerGridSelectionGesture() @@ -602,7 +609,24 @@ public final class MediaPickerScreenImpl: ViewController, MediaPickerScreen, Att } }) - if let controller = self.controller, case .assets(nil, .default) = controller.subject { + if case .assets(nil, .createAvatar) = controller.subject { + let avatarEditorPreviewView = AvatarEditorPreviewView(context: controller.context) + avatarEditorPreviewView.tapped = { [weak self] in + self?.controller?.openAvatarEditor() + } + self.gridNode.view.addSubview(avatarEditorPreviewView) + self.avatarEditorPreviewView = avatarEditorPreviewView + } + + var useLegacyCamera = false + var useModernCamera = false + if case .assets(nil, .default) = controller.subject { + useLegacyCamera = true + } else if case .assets(nil, let mode) = controller.subject, [.createSticker, .createAvatar].contains(mode) { + useModernCamera = true + } + + if useLegacyCamera { let enableAnimations = self.controller?.context.sharedContext.energyUsageSettings.fullTranslucency ?? true let cameraView = TGAttachmentCameraView(forSelfPortrait: false, videoModeByDefault: controller.bannedSendPhotos != nil && controller.bannedSendVideos == nil)! @@ -626,9 +650,14 @@ public final class MediaPickerScreenImpl: ViewController, MediaPickerScreen, Att self.gridNode.scrollView.addSubview(cameraView) self.gridNode.addSubnode(self.cameraActivateAreaNode) - } else if let controller = self.controller, case .assets(nil, .createSticker) = controller.subject, !Camera.isIpad { + } else if useModernCamera, !Camera.isIpad { + var cameraPosition: Camera.Position = .back + if case .assets(nil, .createAvatar) = controller.subject { + cameraPosition = .front + } + let cameraPreviewView = CameraSimplePreviewView(frame: .zero, main: true) - cameraPreviewView.resetPlaceholder(front: false) + cameraPreviewView.resetPlaceholder(front: cameraPosition == .front) self.modernCameraView = cameraPreviewView let tapGestureRecognizer = UITapGestureRecognizer(target: self, action: #selector(self.cameraTapped)) @@ -650,21 +679,30 @@ public final class MediaPickerScreenImpl: ViewController, MediaPickerScreen, Att self.cameraWrapperView.addSubview(cameraPreviewView) - let camera = Camera( - configuration: Camera.Configuration( - preset: .hd1920x1080, - position: .back, - isDualEnabled: false, - audio: false, - photo: true, - metadata: false - ), - previewView: cameraPreviewView, - secondaryPreviewView: nil - ) - self.modernCamera = camera + let setupCamera = { + let camera = Camera( + configuration: Camera.Configuration( + preset: .hd1920x1080, + position: cameraPosition, + isDualEnabled: false, + audio: false, + photo: true, + metadata: false + ), + previewView: cameraPreviewView, + secondaryPreviewView: nil + ) + self.modernCamera = camera + camera.startCapture() + } - camera.startCapture() + if case .assets(nil, .createAvatar) = controller.subject { + Queue.mainQueue().after(0.4, { + setupCamera() + }) + } else { + setupCamera() + } } else { self.containerNode.clipsToBounds = true } @@ -767,7 +805,7 @@ public final class MediaPickerScreenImpl: ViewController, MediaPickerScreen, Att } fileprivate var resetOnUpdate = false - private func updateState(_ state: State) { + fileprivate func updateState(_ state: State) { guard let controller = self.controller, let interaction = controller.interaction else { return } @@ -783,7 +821,7 @@ public final class MediaPickerScreenImpl: ViewController, MediaPickerScreen, Att var stories = false var selectable = true if case let .assets(_, mode) = controller.subject, mode != .default { - selectable = false + selectable = controller.explicitMultipleSelection if mode == .story { stories = true } @@ -1114,6 +1152,15 @@ public final class MediaPickerScreenImpl: ViewController, MediaPickerScreen, Att Queue.mainQueue().justDispatch { self.dismissInput() } + + if controller.explicitMultipleSelection { + let asset = fetchResult[index] + if let selectableItem = TGMediaAsset(phAsset: asset), let selectionContext = interaction.selectionState { + let value = !selectionContext.isIdentifierSelected(selectableItem.uniqueIdentifier) + let _ = interaction.toggleSelection(selectableItem, value, false) + } + return + } if let customSelection = controller.customSelection { self.openingMedia = true @@ -1489,11 +1536,15 @@ public final class MediaPickerScreenImpl: ViewController, MediaPickerScreen, Att let itemSpacing: CGFloat = 1.0 let itemWidth = floorToScreenPixels((width - itemSpacing * CGFloat(itemsPerRow - 1)) / CGFloat(itemsPerRow)) + var cutoutRect: CGRect? var cameraRect: CGRect? = CGRect(origin: CGPoint(x: layout.safeInsets.left, y: 0.0), size: CGSize(width: itemWidth, height: itemWidth * 2.0 + 1.0)) + if case .assets(nil, .createAvatar) = controller.subject { + cameraRect = CGRect(origin: CGPoint(x: layout.safeInsets.left, y: 0.0), size: CGSize(width: itemWidth, height: itemWidth)) + } if self.cameraView == nil && self.modernCameraView == nil { cameraRect = nil } - + var manageHeight: CGFloat = 0.0 if case let .assets(_, _, _, mediaAccess, cameraAccess) = self.state { if cameraAccess == nil { @@ -1578,7 +1629,7 @@ public final class MediaPickerScreenImpl: ViewController, MediaPickerScreen, Att } else { cameraRect = nil } - + let cleanGridInsets = UIEdgeInsets(top: insets.top, left: layout.safeInsets.left, bottom: layout.intrinsicInsets.bottom, right: layout.safeInsets.right) let gridInsets = UIEdgeInsets(top: insets.top + manageHeight, left: layout.safeInsets.left, bottom: layout.intrinsicInsets.bottom, right: layout.safeInsets.right) transition.updateFrame(node: self.gridNode, frame: innerBounds) @@ -1589,13 +1640,17 @@ public final class MediaPickerScreenImpl: ViewController, MediaPickerScreen, Att transition.updateFrame(node: self.containerNode, frame: CGRect(origin: CGPoint(), size: CGSize(width: bounds.width, height: bounds.height))) + cutoutRect = cameraRect + if let _ = self.avatarEditorPreviewView { + cutoutRect = CGRect(origin: CGPoint(x: layout.safeInsets.left, y: 0.0), size: CGSize(width: cameraRect != nil ? itemWidth * 2.0 : itemWidth, height: itemWidth)) + } + var itemHeight = itemWidth if case let .assets(_, mode) = controller.subject, case .story = mode { itemHeight = floor(itemWidth * 1.227) } - let preloadSize: CGFloat = itemHeight// * 3.0 - self.gridNode.transaction(GridNodeTransaction(deleteItems: [], insertItems: [], updateItems: [], scrollToItem: nil, updateLayout: GridNodeUpdateLayout(layout: GridNodeLayout(size: bounds.size, insets: gridInsets, scrollIndicatorInsets: nil, preloadSize: preloadSize, type: .fixed(itemSize: CGSize(width: itemWidth, height: itemHeight), fillWidth: true, lineSpacing: itemSpacing, itemSpacing: itemSpacing), cutout: cameraRect), transition: transition), itemTransition: .immediate, stationaryItems: .none, updateFirstIndexInSectionOffset: nil, updateOpaqueState: nil, synchronousLoads: false), completion: { [weak self] _ in + self.gridNode.transaction(GridNodeTransaction(deleteItems: [], insertItems: [], updateItems: [], scrollToItem: nil, updateLayout: GridNodeUpdateLayout(layout: GridNodeLayout(size: bounds.size, insets: gridInsets, scrollIndicatorInsets: nil, preloadSize: preloadSize, type: .fixed(itemSize: CGSize(width: itemWidth, height: itemHeight), fillWidth: true, lineSpacing: itemSpacing, itemSpacing: itemSpacing), cutout: cutoutRect), transition: transition), itemTransition: .immediate, stationaryItems: .none, updateFirstIndexInSectionOffset: nil, updateOpaqueState: nil, synchronousLoads: false), completion: { [weak self] _ in guard let strongSelf = self else { return } @@ -1612,6 +1667,14 @@ public final class MediaPickerScreenImpl: ViewController, MediaPickerScreen, Att } }) + if let avatarEditorPreviewView = self.avatarEditorPreviewView { + avatarEditorPreviewView.frame = CGRect(origin: CGPoint(x: cameraRect != nil ? cameraRect!.maxX + itemSpacing : layout.safeInsets.left, y: 0.0), size: CGSize(width: itemWidth, height: itemWidth)) + avatarEditorPreviewView.updateLayout(size: CGSize(width: itemWidth, height: itemWidth)) + if self.gridNode.view.subviews.last !== avatarEditorPreviewView { + self.gridNode.view.bringSubviewToFront(avatarEditorPreviewView) + } + } + if let selectionNode = self.selectionNode, let controller = self.controller { let selectedItems = controller.interaction?.selectionState?.selectedItems() as? [TGMediaSelectableItem] ?? [] let updateSelectionNode = { @@ -1642,7 +1705,7 @@ public final class MediaPickerScreenImpl: ViewController, MediaPickerScreen, Att let screenWidth = min(layout.deviceMetrics.screenSize.width, layout.deviceMetrics.screenSize.height) let cameraFullSize = CGSize(width: screenWidth, height: floorToScreenPixels(layout.size.width * 1.77778)) - let cameraScale = cameraRect.height / cameraFullSize.height + let cameraScale = max(cameraRect.width / cameraFullSize.width, cameraRect.height / cameraFullSize.height) cameraView.bounds = CGRect(origin: .zero, size: cameraFullSize) cameraView.center = CGPoint(x: cameraRect.size.width / 2.0, y: cameraRect.size.height / 2.0) @@ -1681,6 +1744,8 @@ public final class MediaPickerScreenImpl: ViewController, MediaPickerScreen, Att self.controller?.navigationItem.rightBarButtonItem = nil } else if case .assets(_, .createSticker) = subject { hasCamera = false + } else if case .assets(_, .createAvatar) = subject { + hasCamera = false } } @@ -1743,7 +1808,8 @@ public final class MediaPickerScreenImpl: ViewController, MediaPickerScreen, Att private var isDismissing = false - fileprivate let mainButtonState: AttachmentMainButtonState? + fileprivate let mainButtonStatePromise = Promise(nil) + private let mainButtonAction: (() -> Void)? public init( @@ -1758,6 +1824,7 @@ public final class MediaPickerScreenImpl: ViewController, MediaPickerScreen, Att canBoostToUnrestrict: Bool = false, paidMediaAllowed: Bool = false, subject: Subject, + forCollage: Bool = false, editingContext: TGMediaEditingContext? = nil, selectionContext: TGMediaSelectionContext? = nil, saveEditedPhotos: Bool = false, @@ -1778,8 +1845,9 @@ public final class MediaPickerScreenImpl: ViewController, MediaPickerScreen, Att self.canBoostToUnrestrict = canBoostToUnrestrict self.paidMediaAllowed = paidMediaAllowed self.subject = subject + self.forCollage = forCollage self.saveEditedPhotos = saveEditedPhotos - self.mainButtonState = mainButtonState + self.mainButtonStatePromise.set(.single(mainButtonState)) self.mainButtonAction = mainButtonAction let selectionContext = selectionContext ?? TGMediaSelectionContext() @@ -1798,6 +1866,11 @@ public final class MediaPickerScreenImpl: ViewController, MediaPickerScreen, Att self.titleView.title = presentationData.strings.MediaPicker_Recents self.titleView.subtitle = presentationData.strings.MediaPicker_CreateSticker self.titleView.isEnabled = true + case .createAvatar: + //TODO:localize + self.titleView.title = presentationData.strings.MediaPicker_Recents + self.titleView.subtitle = "Set new profile photo" + self.titleView.isEnabled = true case .story: self.titleView.title = presentationData.strings.MediaPicker_Recents self.titleView.isEnabled = true @@ -1823,7 +1896,7 @@ public final class MediaPickerScreenImpl: ViewController, MediaPickerScreen, Att super.init(navigationBarPresentationData: NavigationBarPresentationData(presentationData: presentationData)) self.statusBar.statusBarStyle = .Ignore - + selectionContext.attemptSelectingItem = { [weak self] item in guard let self else { return false @@ -1908,10 +1981,14 @@ public final class MediaPickerScreenImpl: ViewController, MediaPickerScreen, Att } else if collection == nil { self.navigationItem.leftBarButtonItem = UIBarButtonItem(title: self.presentationData.strings.Common_Cancel, style: .plain, target: self, action: #selector(self.cancelPressed)) - if [.createSticker].contains(mode) { - self.navigationItem.rightBarButtonItem = UIBarButtonItem(customDisplayNode: self.moreButtonNode) - self.navigationItem.rightBarButtonItem?.action = #selector(self.rightButtonPressed) - self.navigationItem.rightBarButtonItem?.target = self + if forCollage { + self.navigationItem.rightBarButtonItem = UIBarButtonItem(backButtonAppearanceWithTitle: self.presentationData.strings.Common_Select, target: self, action: #selector(self.selectPressed)) + } else { + if [.createSticker].contains(mode) { + self.navigationItem.rightBarButtonItem = UIBarButtonItem(customDisplayNode: self.moreButtonNode) + self.navigationItem.rightBarButtonItem?.action = #selector(self.rightButtonPressed) + self.navigationItem.rightBarButtonItem?.target = self + } } } else { self.navigationItem.leftBarButtonItem = UIBarButtonItem(backButtonAppearanceWithTitle: self.presentationData.strings.Common_Back, target: self, action: #selector(self.backPressed)) @@ -1923,8 +2000,6 @@ public final class MediaPickerScreenImpl: ViewController, MediaPickerScreen, Att self.navigationItem.leftBarButtonItem = UIBarButtonItem(customDisplayNode: self.cancelButtonNode) self.navigationItem.leftBarButtonItem?.action = #selector(self.cancelPressed) self.navigationItem.leftBarButtonItem?.target = self - -// self.navigationItem.leftBarButtonItem = UIBarButtonItem(title: self.presentationData.strings.Common_Cancel, style: .plain, target: self, action: #selector(self.cancelPressed)) } if self.bannedSendPhotos != nil && self.bannedSendVideos != nil { @@ -2286,6 +2361,11 @@ public final class MediaPickerScreenImpl: ViewController, MediaPickerScreen, Att transition.updateAlpha(node: self.moreButtonNode.iconNode, alpha: moreIsVisible ? 1.0 : 0.0) transition.updateTransformScale(node: self.moreButtonNode.iconNode, scale: moreIsVisible ? 1.0 : 0.1) + + //if self. { + //TODO:localize + self.mainButtonStatePromise.set(.single(AttachmentMainButtonState(text: "Add", badge: "\(count)", font: .bold, background: .color(self.presentationData.theme.actionSheet.controlAccentColor), textColor: self.presentationData.theme.list.itemCheckColors.foregroundColor, isVisible: count > 0, progress: .none, isEnabled: true, hasShimmer: false))) + //} } private func updateThemeAndStrings() { @@ -2458,6 +2538,15 @@ public final class MediaPickerScreenImpl: ViewController, MediaPickerScreen, Att } } + @objc private func selectPressed() { + self.navigationItem.setRightBarButton(nil, animated: true) + self.explicitMultipleSelection = true + + if let state = self.controllerNode.state { + self.controllerNode.updateState(state) + } + } + @objc private func selectedPressed() { self.controllerNode.updateDisplayMode(.selected, animated: true) } @@ -2689,14 +2778,18 @@ final class MediaPickerContext: AttachmentMediaPickerContext { private weak var controller: MediaPickerScreenImpl? var selectionCount: Signal { - return Signal { [weak self] subscriber in - let disposable = self?.controller?.interaction?.selectionState?.selectionChangedSignal().start(next: { [weak self] value in - subscriber.putNext(Int(self?.controller?.interaction?.selectionState?.count() ?? 0)) - }, error: { _ in }, completed: { }) - return ActionDisposable { - disposable?.dispose() - } - } + //if self.controller?.forCollage == true { + return .single(0) +// } else { +// return Signal { [weak self] subscriber in +// let disposable = self?.controller?.interaction?.selectionState?.selectionChangedSignal().start(next: { [weak self] value in +// subscriber.putNext(Int(self?.controller?.interaction?.selectionState?.count() ?? 0)) +// }, error: { _ in }, completed: { }) +// return ActionDisposable { +// disposable?.dispose() +// } +// } +// } } var caption: Signal { @@ -2820,7 +2913,7 @@ final class MediaPickerContext: AttachmentMediaPickerContext { } public var mainButtonState: Signal { - return .single(self.controller?.mainButtonState) + return self.controller?.mainButtonStatePromise.get() ?? .single(nil) } init(controller: MediaPickerScreenImpl) { @@ -3026,8 +3119,10 @@ public func storyMediaPickerController( context: AccountContext, isDark: Bool, forCollage: Bool, + selectionLimit: Int?, getSourceRect: @escaping () -> CGRect, completion: @escaping (Any, UIView, CGRect, UIImage?, @escaping (Bool?) -> (UIView, CGRect)?, @escaping () -> Void) -> Void, + multipleCompletion: @escaping ([Any]) -> Void, dismissed: @escaping () -> Void, groupsPresented: @escaping () -> Void ) -> ViewController { @@ -3036,13 +3131,46 @@ public func storyMediaPickerController( presentationData = presentationData.withUpdated(theme: defaultDarkColorPresentationTheme) } let updatedPresentationData: (PresentationData, Signal) = (presentationData, .single(presentationData)) + + var selectionContext: TGMediaSelectionContext? + if let selectionLimit { + selectionContext = TGMediaSelectionContext() + selectionContext?.selectionLimit = Int32(selectionLimit) + selectionContext?.selectionLimitExceeded = { + HapticFeedback().error() + } + } + let controller = AttachmentController(context: context, updatedPresentationData: updatedPresentationData, chatLocation: nil, buttons: [.standalone], initialButton: .standalone, fromMenu: false, hasTextInput: false, makeEntityInputView: { return nil }) controller.forceSourceRect = true controller.getSourceRect = getSourceRect controller.requestController = { _, present in - let mediaPickerController = MediaPickerScreenImpl(context: context, updatedPresentationData: updatedPresentationData, peer: nil, threadTitle: nil, chatLocation: nil, bannedSendPhotos: nil, bannedSendVideos: nil, subject: .assets(nil, .story), mainButtonState: nil, mainButtonAction: nil) + let mediaPickerController = MediaPickerScreenImpl( + context: context, + updatedPresentationData: updatedPresentationData, + peer: nil, + threadTitle: nil, + chatLocation: nil, + bannedSendPhotos: nil, + bannedSendVideos: nil, + subject: .assets(nil, .story), + forCollage: forCollage, + selectionContext: selectionContext, + mainButtonState: nil, + mainButtonAction: { [weak selectionContext] in + if let selectionContext, let selectedItems = selectionContext.selectedItems() { + var results: [Any] = [] + for item in selectedItems { + if let item = item as? TGMediaAsset, let asset = item.backingAsset { + results.append(asset) + } + } + multipleCompletion(results) + } + } + ) mediaPickerController.groupsPresented = groupsPresented mediaPickerController.customSelection = { controller, result in if let result = result as? MediaEditorDraft { @@ -3093,8 +3221,9 @@ public func storyMediaPickerController( } present(mediaPickerController, mediaPickerController.mediaPickerContext) } - controller.willDismiss = { + controller.willDismiss = { [weak selectionContext] in dismissed() + selectionContext?.clear() } controller.navigationPresentation = .flatModal controller.supportedOrientations = ViewControllerSupportedOrientations(regularSize: .all, compactSize: .portrait) @@ -3228,6 +3357,124 @@ public func stickerMediaPickerController( return controller } +public func avatarMediaPickerController( + context: AccountContext, + getSourceRect: @escaping () -> CGRect?, + canDelete: Bool, + performDelete: @escaping () -> Void, + completion: @escaping (Any?, UIView?, CGRect, UIImage?, Bool, @escaping (Bool?) -> (UIView, CGRect)?, @escaping () -> Void) -> Void, + dismissed: @escaping () -> Void +) -> ViewController { + let presentationData = context.sharedContext.currentPresentationData.with({ $0 }) + let updatedPresentationData: (PresentationData, Signal) = (presentationData, .single(presentationData)) + let controller = AttachmentController(context: context, updatedPresentationData: updatedPresentationData, chatLocation: nil, buttons: [.standalone], initialButton: .standalone, fromMenu: false, hasTextInput: false, makeEntityInputView: { + return nil + }) + controller.forceSourceRect = true + controller.getSourceRect = getSourceRect + controller.requestController = { [weak controller] _, present in + + var mainButtonState: AttachmentMainButtonState? + + if canDelete { + //TODO:localize + mainButtonState = AttachmentMainButtonState(text: "Remove Photo", font: .regular, background: .color(.clear), textColor: presentationData.theme.actionSheet.destructiveActionTextColor, isVisible: true, progress: .none, isEnabled: true, hasShimmer: false) + } + + let mediaPickerController = MediaPickerScreenImpl( + context: context, + updatedPresentationData: updatedPresentationData, + peer: nil, + threadTitle: nil, + chatLocation: nil, + bannedSendPhotos: nil, + bannedSendVideos: nil, + subject: .assets(nil, .createAvatar), + mainButtonState: mainButtonState, + mainButtonAction: { [weak controller] in + controller?.dismiss(animated: true) + performDelete() + } + ) + mediaPickerController.customSelection = { controller, result in + if let result = result as? PHAsset { + controller.updateHiddenMediaId(result.localIdentifier) + if let transitionView = controller.transitionView(for: result.localIdentifier, snapshot: false) { + let transitionOut: (Bool?) -> (UIView, CGRect)? = { isNew in + if let isNew { + if isNew { + controller.updateHiddenMediaId(nil) + if let transitionView = controller.defaultTransitionView() { + return (transitionView, transitionView.bounds) + } + } else if let transitionView = controller.transitionView(for: result.localIdentifier, snapshot: false) { + return (transitionView, transitionView.bounds) + } + } + return nil + } + completion(result, transitionView, transitionView.bounds, controller.transitionImage(for: result.localIdentifier), false, transitionOut, { [weak controller] in + controller?.updateHiddenMediaId(nil) + }) + } + } + } + mediaPickerController.openAvatarEditor = { [weak controller] in + completion(nil, nil, .zero, nil, false, { _ in return nil }, { + }) + controller?.dismiss(animated: true) + } + mediaPickerController.openCamera = { [weak controller] cameraHolder in + guard let cameraHolder = cameraHolder as? CameraHolder else { + return + } + + var returnToCameraImpl: (() -> Void)? + let cameraScreen = CameraScreenImpl( + context: context, + mode: .avatar, + holder: cameraHolder, + transitionIn: CameraScreenImpl.TransitionIn( + sourceView: cameraHolder.parentView, + sourceRect: cameraHolder.parentView.bounds, + sourceCornerRadius: 0.0 + ), + transitionOut: { _ in + return CameraScreenImpl.TransitionOut( + destinationView: cameraHolder.parentView, + destinationRect: cameraHolder.parentView.bounds, + destinationCornerRadius: 0.0 + ) + }, + completion: { result, _, commit in + completion(result, nil, .zero, nil, true, { _ in return nil }, { + returnToCameraImpl?() + }) + } + ) + cameraScreen.transitionedOut = { [weak cameraHolder] in + if let cameraHolder { + cameraHolder.restore() + } + } + controller?.push(cameraScreen) + + returnToCameraImpl = { [weak cameraScreen] in + if let cameraScreen { + cameraScreen.returnFromEditor() + } + } + } + present(mediaPickerController, mediaPickerController.mediaPickerContext) + } + controller.willDismiss = { + dismissed() + } + controller.navigationPresentation = .flatModal + controller.supportedOrientations = ViewControllerSupportedOrientations(regularSize: .all, compactSize: .portrait) + return controller +} + private class SelectedButtonNode: HighlightableButtonNode { private let background = ASImageNode() private let icon = ASImageNode() diff --git a/submodules/PremiumUI/Sources/PremiumBoostLevelsScreen.swift b/submodules/PremiumUI/Sources/PremiumBoostLevelsScreen.swift index 98a3a1f2db..5eee39d92e 100644 --- a/submodules/PremiumUI/Sources/PremiumBoostLevelsScreen.swift +++ b/submodules/PremiumUI/Sources/PremiumBoostLevelsScreen.swift @@ -2413,7 +2413,7 @@ public class PremiumBoostLevelsScreen: ViewController { controller?.dismiss(animated: true, completion: nil) Queue.mainQueue().after(0.4) { - let giftController = context.sharedContext.makePremiumGiftController(context: context, source: .channelBoost, completion: nil) + let giftController = context.sharedContext.makePremiumGiftController(context: context, source: .channelBoost, transfer: false, completion: nil) navigationController.pushViewController(giftController, animated: true) } } diff --git a/submodules/PremiumUI/Sources/PremiumBoostScreen.swift b/submodules/PremiumUI/Sources/PremiumBoostScreen.swift index 00a0a4caa5..a18b55572f 100644 --- a/submodules/PremiumUI/Sources/PremiumBoostScreen.swift +++ b/submodules/PremiumUI/Sources/PremiumBoostScreen.swift @@ -239,7 +239,7 @@ public func PremiumBoostScreen( dismissImpl?() Queue.mainQueue().after(0.4) { - let controller = context.sharedContext.makePremiumGiftController(context: context, source: .channelBoost, completion: nil) + let controller = context.sharedContext.makePremiumGiftController(context: context, source: .channelBoost, transfer: false, completion: nil) pushController(controller) } }), diff --git a/submodules/PremiumUI/Sources/PremiumIntroScreen.swift b/submodules/PremiumUI/Sources/PremiumIntroScreen.swift index 4f7791d7f7..83b6a6eb2f 100644 --- a/submodules/PremiumUI/Sources/PremiumIntroScreen.swift +++ b/submodules/PremiumUI/Sources/PremiumIntroScreen.swift @@ -3417,7 +3417,7 @@ private final class PremiumIntroScreenComponent: CombinedComponent { loadedPack = .result(info: info, items: items, installed: updatedInstalled ?? installed) } - let controller = accountContext.sharedContext.makeStickerPackScreen(context: accountContext, updatedPresentationData: nil, mainStickerPack: packReference, stickerPacks: [packReference], loadedStickerPacks: loadedPack.flatMap { [$0] } ?? [], isEditing: false, expandIfNeeded: false, parentNavigationController: navigationController, sendSticker: { _, _, _ in + let controller = accountContext.sharedContext.makeStickerPackScreen(context: accountContext, updatedPresentationData: nil, mainStickerPack: packReference, stickerPacks: [packReference], loadedStickerPacks: loadedPack.flatMap { [$0] } ?? [], actionTitle: nil, isEditing: false, expandIfNeeded: false, parentNavigationController: navigationController, sendSticker: { _, _, _ in return false }, actionPerformed: { added in updatedInstalled = added diff --git a/submodules/PremiumUI/Sources/ReplaceBoostScreen.swift b/submodules/PremiumUI/Sources/ReplaceBoostScreen.swift index 1aefe3e225..a961c6b91d 100644 --- a/submodules/PremiumUI/Sources/ReplaceBoostScreen.swift +++ b/submodules/PremiumUI/Sources/ReplaceBoostScreen.swift @@ -882,7 +882,7 @@ public class ReplaceBoostScreen: ViewController { } let navigationController = self.navigationController self.dismiss(animated: true, completion: { - let giftController = context.sharedContext.makePremiumGiftController(context: context, source: .channelBoost, completion: nil) + let giftController = context.sharedContext.makePremiumGiftController(context: context, source: .channelBoost, transfer: false, completion: nil) navigationController?.pushViewController(giftController, animated: true) }) } diff --git a/submodules/ReactionSelectionNode/Sources/PremiumStarsNode.swift b/submodules/ReactionSelectionNode/Sources/PremiumStarsNode.swift index bdab92ec61..d368fcad17 100644 --- a/submodules/ReactionSelectionNode/Sources/PremiumStarsNode.swift +++ b/submodules/ReactionSelectionNode/Sources/PremiumStarsNode.swift @@ -47,7 +47,7 @@ public final class PremiumStarsNode: ASDisplayNode { if self.frame.width > 0.0 { size = self.frame.size } else { - size = CGSize(width: 32.0, height: 32.0) + size = CGSize(width: 72.0, height: 32.0) } let starSize = CGSize(width: 6.0, height: 8.0) diff --git a/submodules/SettingsUI/Sources/Search/SettingsSearchItem.swift b/submodules/SettingsUI/Sources/Search/SettingsSearchItem.swift index 956d5a70a5..651320a3e5 100644 --- a/submodules/SettingsUI/Sources/Search/SettingsSearchItem.swift +++ b/submodules/SettingsUI/Sources/Search/SettingsSearchItem.swift @@ -456,7 +456,7 @@ public final class SettingsSearchContainerNode: SearchDisplayControllerContentNo self.recentDisposable = (combineLatest(recentSearchItems, faqItems.get(), self.presentationDataPromise.get()) |> deliverOnMainQueue).start(next: { [weak self] recentSearchItems, faqItems, presentationData in if let strongSelf = self { - let recentHeader = ChatListSearchItemHeader(type: .recentPeers, theme: presentationData.theme, strings: presentationData.strings, actionTitle: presentationData.strings.WebSearch_RecentSectionClear, action: { + let recentHeader = ChatListSearchItemHeader(type: .recentPeers, theme: presentationData.theme, strings: presentationData.strings, actionTitle: presentationData.strings.WebSearch_RecentSectionClear, action: { _ in clearRecentSettingsSearchItems(engine: context.engine) }) let faqHeader = ChatListSearchItemHeader(type: .faq, theme: presentationData.theme, strings: presentationData.strings) @@ -470,7 +470,6 @@ public final class SettingsSearchContainerNode: SearchDisplayControllerContentNo entries.append(.faq(i, faqItems[i], faqHeader)) } - let previousEntries = previousRecentItems.swap(entries) let transition = preparedSettingsSearchContainerRecentTransition(from: previousEntries ?? [], to: entries, account: context.account, theme: presentationData.theme, strings: presentationData.strings, interaction: interaction) strongSelf.enqueueRecentTransition(transition, firstTime: previousEntries == nil) diff --git a/submodules/ShareController/BUILD b/submodules/ShareController/BUILD index 439be0fa1f..096622d8c8 100644 --- a/submodules/ShareController/BUILD +++ b/submodules/ShareController/BUILD @@ -43,6 +43,7 @@ swift_library( "//submodules/Components/MultilineTextComponent", "//submodules/Components/BundleIconComponent", "//submodules/TelegramUI/Components/LottieComponent", + #"//submodules/TelegramUI/Components/MessageInputPanelComponent", ], visibility = [ "//visibility:public", diff --git a/submodules/StickerPackPreviewUI/Sources/StickerPackPreviewController.swift b/submodules/StickerPackPreviewUI/Sources/StickerPackPreviewController.swift index e1d98f6fef..c788f58895 100644 --- a/submodules/StickerPackPreviewUI/Sources/StickerPackPreviewController.swift +++ b/submodules/StickerPackPreviewUI/Sources/StickerPackPreviewController.swift @@ -291,78 +291,3 @@ public final class StickerPackPreviewController: ViewController, StandalonePrese self.controllerNode.containerLayoutUpdated(layout, navigationBarHeight: self.navigationLayout(layout: layout).navigationFrame.maxY, transition: transition) } } - -public func preloadedStickerPackThumbnail(account: Account, info: StickerPackCollectionInfo, items: [ItemCollectionItem]) -> Signal { - if let thumbnail = info.thumbnail { - let signal = Signal { subscriber in - let fetched = fetchedMediaResource(mediaBox: account.postbox.mediaBox, userLocation: .other, userContentType: .sticker, reference: .stickerPackThumbnail(stickerPack: .id(id: info.id.id, accessHash: info.accessHash), resource: thumbnail.resource)).start() - let dataDisposable: Disposable - if thumbnail.typeHint != .generic { - dataDisposable = chatMessageAnimationData(mediaBox: account.postbox.mediaBox, resource: thumbnail.resource, isVideo: thumbnail.typeHint == .video, width: 80, height: 80, synchronousLoad: false).start(next: { data in - if data.complete { - subscriber.putNext(true) - subscriber.putCompletion() - } else { - subscriber.putNext(false) - } - }) - } else { - dataDisposable = account.postbox.mediaBox.resourceData(thumbnail.resource, option: .incremental(waitUntilFetchStatus: false)).start(next: { data in - if data.complete { - subscriber.putNext(true) - subscriber.putCompletion() - } else { - subscriber.putNext(false) - } - }) - } - return ActionDisposable { - fetched.dispose() - dataDisposable.dispose() - } - } - return signal - } - - if let item = items.first as? StickerPackItem { - if item.file.isAnimatedSticker { - let signal = Signal { subscriber in - let fetched = fetchedMediaResource(mediaBox: account.postbox.mediaBox, userLocation: .other, userContentType: .sticker, reference: FileMediaReference.standalone(media: item.file).resourceReference(item.file.resource)).start() - let data = account.postbox.mediaBox.resourceData(item.file.resource).start() - let dimensions = item.file.dimensions ?? PixelDimensions(width: 512, height: 512) - let fetchedRepresentation = chatMessageAnimatedStickerDatas(postbox: account.postbox, userLocation: .other, file: item.file, small: false, size: dimensions.cgSize.aspectFitted(CGSize(width: 160.0, height: 160.0)), fetched: true, onlyFullSize: false, synchronousLoad: false).start(next: { next in - let hasContent = next._0 != nil || next._1 != nil - subscriber.putNext(hasContent) - if hasContent { - subscriber.putCompletion() - } - }) - return ActionDisposable { - fetched.dispose() - data.dispose() - fetchedRepresentation.dispose() - } - } - return signal - } else { - let signal = Signal { subscriber in - let data = account.postbox.mediaBox.resourceData(item.file.resource).start() - let dimensions = item.file.dimensions ?? PixelDimensions(width: 512, height: 512) - let fetchedRepresentation = chatMessageAnimatedStickerDatas(postbox: account.postbox, userLocation: .other, file: item.file, small: true, size: dimensions.cgSize.aspectFitted(CGSize(width: 160.0, height: 160.0)), fetched: true, onlyFullSize: false, synchronousLoad: false).start(next: { next in - let hasContent = next._0 != nil || next._1 != nil - subscriber.putNext(hasContent) - if hasContent { - subscriber.putCompletion() - } - }) - return ActionDisposable { - data.dispose() - fetchedRepresentation.dispose() - } - } - return signal - } - } - - return .single(true) -} diff --git a/submodules/StickerResources/Sources/StickerResources.swift b/submodules/StickerResources/Sources/StickerResources.swift index 04745ac252..b2ed2535d7 100644 --- a/submodules/StickerResources/Sources/StickerResources.swift +++ b/submodules/StickerResources/Sources/StickerResources.swift @@ -585,3 +585,78 @@ public func chatMessageAnimatedSticker(postbox: Postbox, userLocation: MediaReso } } } + +public func preloadedStickerPackThumbnail(account: Account, info: StickerPackCollectionInfo, items: [ItemCollectionItem]) -> Signal { + if let thumbnail = info.thumbnail { + let signal = Signal { subscriber in + let fetched = fetchedMediaResource(mediaBox: account.postbox.mediaBox, userLocation: .other, userContentType: .sticker, reference: .stickerPackThumbnail(stickerPack: .id(id: info.id.id, accessHash: info.accessHash), resource: thumbnail.resource)).start() + let dataDisposable: Disposable + if thumbnail.typeHint != .generic { + dataDisposable = chatMessageAnimationData(mediaBox: account.postbox.mediaBox, resource: thumbnail.resource, isVideo: thumbnail.typeHint == .video, width: 80, height: 80, synchronousLoad: false).start(next: { data in + if data.complete { + subscriber.putNext(true) + subscriber.putCompletion() + } else { + subscriber.putNext(false) + } + }) + } else { + dataDisposable = account.postbox.mediaBox.resourceData(thumbnail.resource, option: .incremental(waitUntilFetchStatus: false)).start(next: { data in + if data.complete { + subscriber.putNext(true) + subscriber.putCompletion() + } else { + subscriber.putNext(false) + } + }) + } + return ActionDisposable { + fetched.dispose() + dataDisposable.dispose() + } + } + return signal + } + + if let item = items.first as? StickerPackItem { + if item.file.isAnimatedSticker { + let signal = Signal { subscriber in + let fetched = fetchedMediaResource(mediaBox: account.postbox.mediaBox, userLocation: .other, userContentType: .sticker, reference: FileMediaReference.standalone(media: item.file).resourceReference(item.file.resource)).start() + let data = account.postbox.mediaBox.resourceData(item.file.resource).start() + let dimensions = item.file.dimensions ?? PixelDimensions(width: 512, height: 512) + let fetchedRepresentation = chatMessageAnimatedStickerDatas(postbox: account.postbox, userLocation: .other, file: item.file, small: false, size: dimensions.cgSize.aspectFitted(CGSize(width: 160.0, height: 160.0)), fetched: true, onlyFullSize: false, synchronousLoad: false).start(next: { next in + let hasContent = next._0 != nil || next._1 != nil + subscriber.putNext(hasContent) + if hasContent { + subscriber.putCompletion() + } + }) + return ActionDisposable { + fetched.dispose() + data.dispose() + fetchedRepresentation.dispose() + } + } + return signal + } else { + let signal = Signal { subscriber in + let data = account.postbox.mediaBox.resourceData(item.file.resource).start() + let dimensions = item.file.dimensions ?? PixelDimensions(width: 512, height: 512) + let fetchedRepresentation = chatMessageAnimatedStickerDatas(postbox: account.postbox, userLocation: .other, file: item.file, small: true, size: dimensions.cgSize.aspectFitted(CGSize(width: 160.0, height: 160.0)), fetched: true, onlyFullSize: false, synchronousLoad: false).start(next: { next in + let hasContent = next._0 != nil || next._1 != nil + subscriber.putNext(hasContent) + if hasContent { + subscriber.putCompletion() + } + }) + return ActionDisposable { + data.dispose() + fetchedRepresentation.dispose() + } + } + return signal + } + } + + return .single(true) +} diff --git a/submodules/TelegramNotices/Sources/Notices.swift b/submodules/TelegramNotices/Sources/Notices.swift index 266fed0d21..76a99a8cac 100644 --- a/submodules/TelegramNotices/Sources/Notices.swift +++ b/submodules/TelegramNotices/Sources/Notices.swift @@ -235,6 +235,7 @@ private struct ApplicationSpecificNoticeKeys { private static let dismissedPremiumGiftNamespace: Int32 = 9 private static let groupEmojiPackNamespace: Int32 = 9 private static let dismissedBirthdayPremiumGiftTipNamespace: Int32 = 10 + private static let displayedPeerVerificationNamespace: Int32 = 11 static func inlineBotLocationRequestNotice(peerId: PeerId) -> NoticeEntryKey { return NoticeEntryKey(namespace: noticeNamespace(namespace: inlineBotLocationRequestNamespace), key: noticeKey(peerId: peerId, key: 0)) @@ -516,6 +517,10 @@ private struct ApplicationSpecificNoticeKeys { return NoticeEntryKey(namespace: noticeNamespace(namespace: dismissedBirthdayPremiumGiftTipNamespace), key: noticeKey(peerId: peerId, key: 0)) } + static func displayedPeerVerification(peerId: PeerId) -> NoticeEntryKey { + return NoticeEntryKey(namespace: noticeNamespace(namespace: displayedPeerVerificationNamespace), key: noticeKey(peerId: peerId, key: 0)) + } + static func monetizationIntroDismissed() -> NoticeEntryKey { return NoticeEntryKey(namespace: noticeNamespace(namespace: globalNamespace), key: ApplicationSpecificGlobalNotice.monetizationIntroDismissed.key) } @@ -2141,6 +2146,26 @@ public struct ApplicationSpecificNotice { |> ignoreValues } + public static func displayedPeerVerification(accountManager: AccountManager, peerId: PeerId) -> Signal { + return accountManager.noticeEntry(key: ApplicationSpecificNoticeKeys.displayedPeerVerification(peerId: peerId)) + |> map { view -> Bool in + if let _ = view.value?.get(ApplicationSpecificBoolNotice.self) { + return true + } else { + return false + } + } + } + + public static func setDisplayedPeerVerification(accountManager: AccountManager, peerId: PeerId) -> Signal { + return accountManager.transaction { transaction -> Void in + if let entry = CodableEntry(ApplicationSpecificBoolNotice()) { + transaction.setNotice(ApplicationSpecificNoticeKeys.displayedPeerVerification(peerId: peerId), entry) + } + } + |> ignoreValues + } + public static func setMonetizationIntroDismissed(accountManager: AccountManager) -> Signal { return accountManager.transaction { transaction -> Void in if let entry = CodableEntry(ApplicationSpecificBoolNotice()) { diff --git a/submodules/TelegramPresentationData/Sources/Resources/PresentationResourceKey.swift b/submodules/TelegramPresentationData/Sources/Resources/PresentationResourceKey.swift index 71a20e585e..8cf36e5679 100644 --- a/submodules/TelegramPresentationData/Sources/Resources/PresentationResourceKey.swift +++ b/submodules/TelegramPresentationData/Sources/Resources/PresentationResourceKey.swift @@ -120,6 +120,7 @@ public enum PresentationResourceKey: Int32 { case chatListForwardedIcon case chatListStoryReplyIcon case chatListGiftIcon + case chatListLocationIcon case chatListGeneralTopicIcon case chatListGeneralTopicSmallIcon diff --git a/submodules/TelegramPresentationData/Sources/Resources/PresentationResourcesChatList.swift b/submodules/TelegramPresentationData/Sources/Resources/PresentationResourcesChatList.swift index 44dbd95eba..ac0ec604d1 100644 --- a/submodules/TelegramPresentationData/Sources/Resources/PresentationResourcesChatList.swift +++ b/submodules/TelegramPresentationData/Sources/Resources/PresentationResourcesChatList.swift @@ -265,6 +265,24 @@ public struct PresentationResourcesChatList { }) } + public static func locationIcon(_ theme: PresentationTheme) -> UIImage? { + return theme.image(PresentationResourceKey.chatListLocationIcon.rawValue, { theme in + if let image = UIImage(bundleImageName: "Chat/Attach Menu/Location") { + return generateImage(image.size, contextGenerator: { size, context in + if let cgImage = image.cgImage { + context.clear(CGRect(origin: CGPoint(), size: size)) + + context.clip(to: CGRect(origin: .zero, size: size).insetBy(dx: 5.0, dy: 5.0), mask: cgImage) + context.setFillColor(theme.chatList.muteIconColor.cgColor) + context.fill(CGRect(origin: CGPoint(), size: size)) + } + }) + } else { + return nil + } + }) + } + public static func verifiedIcon(_ theme: PresentationTheme) -> UIImage? { return theme.image(PresentationResourceKey.chatListVerifiedIcon.rawValue, { theme in if let backgroundImage = UIImage(bundleImageName: "Chat List/PeerVerifiedIconBackground"), let foregroundImage = UIImage(bundleImageName: "Chat List/PeerVerifiedIconForeground") { diff --git a/submodules/TelegramUI/Components/AvatarBackground/BUILD b/submodules/TelegramUI/Components/AvatarBackground/BUILD new file mode 100644 index 0000000000..1ec536ea05 --- /dev/null +++ b/submodules/TelegramUI/Components/AvatarBackground/BUILD @@ -0,0 +1,20 @@ +load("@build_bazel_rules_swift//swift:swift.bzl", "swift_library") + +swift_library( + name = "AvatarBackground", + module_name = "AvatarBackground", + srcs = glob([ + "Sources/**/*.swift", + ]), + copts = [ + "-warnings-as-errors", + ], + deps = [ + "//submodules/AsyncDisplayKit:AsyncDisplayKit", + "//submodules/Display:Display", + "//submodules/GradientBackground:GradientBackground", + ], + visibility = [ + "//visibility:public", + ], +) diff --git a/submodules/TelegramUI/Components/AvatarBackground/Sources/AvatarBackground.swift b/submodules/TelegramUI/Components/AvatarBackground/Sources/AvatarBackground.swift new file mode 100644 index 0000000000..e7b6743ce9 --- /dev/null +++ b/submodules/TelegramUI/Components/AvatarBackground/Sources/AvatarBackground.swift @@ -0,0 +1,57 @@ +import Foundation +import UIKit +import Display +import GradientBackground + +public enum AvatarBackground: Equatable { + public static let defaultBackgrounds: [AvatarBackground] = [ + .gradient([0xFF5A7FFF, 0xFF2CA0F2, 0xFF4DFF89, 0xFF6BFCEB]), + .gradient([0xFFFF011D, 0xFFFF530D, 0xFFFE64DC, 0xFFFFDC61]), + .gradient([0xFFFE64DC, 0xFFFF6847, 0xFFFFDD02, 0xFFFFAE10]), + .gradient([0xFF84EC00, 0xFF00B7C2, 0xFF00C217, 0xFFFFE600]), + .gradient([0xFF86B0FF, 0xFF35FFCF, 0xFF69FFFF, 0xFF76DEFF]), + .gradient([0xFFFAE100, 0xFFFF54EE, 0xFFFC2B78, 0xFFFF52D9]), + .gradient([0xFF73A4FF, 0xFF5F55FF, 0xFFFF49F8, 0xFFEC76FF]), + ] + + case gradient([UInt32]) + + public var colors: [UInt32] { + switch self { + case let .gradient(colors): + return colors + } + } + + public var isLight: Bool { + switch self { + case let .gradient(colors): + if colors.count == 1 { + return UIColor(rgb: colors.first!).lightness > 0.99 + } else if colors.count == 2 { + return UIColor(rgb: colors.first!).lightness > 0.99 || UIColor(rgb: colors.last!).lightness > 0.99 + } else { + var lightCount = 0 + for color in colors { + if UIColor(rgb: color).lightness > 0.99 { + lightCount += 1 + } + } + return lightCount >= 2 + } + } + } + + public func generateImage(size: CGSize) -> UIImage { + switch self { + case let .gradient(colors): + if colors.count == 1 { + return generateSingleColorImage(size: size, color: UIColor(rgb: colors.first!))! + } else if colors.count == 2 { + return generateGradientImage(size: size, colors: colors.map { UIColor(rgb: $0) }, locations: [0.0, 1.0])! + } else { + return GradientBackgroundNode.generatePreview(size: size, colors: colors.map { UIColor(rgb: $0) }) + } + } + } +} diff --git a/submodules/TelegramUI/Components/AvatarEditorScreen/BUILD b/submodules/TelegramUI/Components/AvatarEditorScreen/BUILD index 92230690fd..034b92f5ce 100644 --- a/submodules/TelegramUI/Components/AvatarEditorScreen/BUILD +++ b/submodules/TelegramUI/Components/AvatarEditorScreen/BUILD @@ -39,6 +39,7 @@ swift_library( "//submodules/TelegramUI/Components/AnimationCache:AnimationCache", "//submodules/TelegramUI/Components/EmojiTextAttachmentView:EmojiTextAttachmentView", "//submodules/TelegramUI/Components/MediaEditor", + "//submodules/TelegramUI/Components/AvatarBackground", ], visibility = [ "//visibility:public", diff --git a/submodules/TelegramUI/Components/AvatarEditorScreen/Sources/AvatarEditorScreen.swift b/submodules/TelegramUI/Components/AvatarEditorScreen/Sources/AvatarEditorScreen.swift index ccb2d384fe..6035d1ca59 100644 --- a/submodules/TelegramUI/Components/AvatarEditorScreen/Sources/AvatarEditorScreen.swift +++ b/submodules/TelegramUI/Components/AvatarEditorScreen/Sources/AvatarEditorScreen.swift @@ -24,59 +24,7 @@ import SolidRoundedButtonComponent import AnimationCache import EmojiTextAttachmentView import MediaEditor - -enum AvatarBackground: Equatable { - case gradient([UInt32]) - - var colors: [UInt32] { - switch self { - case let .gradient(colors): - return colors - } - } - - var isLight: Bool { - switch self { - case let .gradient(colors): - if colors.count == 1 { - return UIColor(rgb: colors.first!).lightness > 0.99 - } else if colors.count == 2 { - return UIColor(rgb: colors.first!).lightness > 0.99 || UIColor(rgb: colors.last!).lightness > 0.99 - } else { - var lightCount = 0 - for color in colors { - if UIColor(rgb: color).lightness > 0.99 { - lightCount += 1 - } - } - return lightCount >= 2 - } - } - } - - func generateImage(size: CGSize) -> UIImage { - switch self { - case let .gradient(colors): - if colors.count == 1 { - return generateSingleColorImage(size: size, color: UIColor(rgb: colors.first!))! - } else if colors.count == 2 { - return generateGradientImage(size: size, colors: colors.map { UIColor(rgb: $0) }, locations: [0.0, 1.0])! - } else { - return GradientBackgroundNode.generatePreview(size: size, colors: colors.map { UIColor(rgb: $0) }) - } - } - } -} - -private let defaultBackgrounds: [AvatarBackground] = [ - .gradient([0xFF5A7FFF, 0xFF2CA0F2, 0xFF4DFF89, 0xFF6BFCEB]), - .gradient([0xFFFF011D, 0xFFFF530D, 0xFFFE64DC, 0xFFFFDC61]), - .gradient([0xFFFE64DC, 0xFFFF6847, 0xFFFFDD02, 0xFFFFAE10]), - .gradient([0xFF84EC00, 0xFF00B7C2, 0xFF00C217, 0xFFFFE600]), - .gradient([0xFF86B0FF, 0xFF35FFCF, 0xFF69FFFF, 0xFF76DEFF]), - .gradient([0xFFFAE100, 0xFFFF54EE, 0xFFFC2B78, 0xFFFF52D9]), - .gradient([0xFF73A4FF, 0xFF5F55FF, 0xFFFF49F8, 0xFFEC76FF]), -] +import AvatarBackground public struct AvatarKeyboardInputData: Equatable { var emoji: EmojiPagerContentComponent @@ -147,7 +95,7 @@ final class AvatarEditorScreenComponent: Component { self.context = context self.ready = ready - self.selectedBackground = defaultBackgrounds.first! + self.selectedBackground = AvatarBackground.defaultBackgrounds.first! self.previousColor = self.selectedBackground super.init() @@ -181,7 +129,7 @@ final class AvatarEditorScreenComponent: Component { self.selectedBackground = .gradient(markup.backgroundColors.map { UInt32(bitPattern: $0) }) self.previousColor = self.selectedBackground } else { - self.selectedBackground = defaultBackgrounds.first! + self.selectedBackground = AvatarBackground.defaultBackgrounds.first! } self.previousColor = self.selectedBackground @@ -1046,7 +994,7 @@ final class AvatarEditorScreenComponent: Component { transition: transition, component: AnyComponent(BackgroundColorComponent( theme: environment.theme, - values: defaultBackgrounds, + values: AvatarBackground.defaultBackgrounds, selectedValue: state.selectedBackground, customValue: state.customColor, updateValue: { [weak state] value in diff --git a/submodules/TelegramUI/Components/AvatarEditorScreen/Sources/AvatarPreviewComponent.swift b/submodules/TelegramUI/Components/AvatarEditorScreen/Sources/AvatarPreviewComponent.swift index cc76d661fb..42c959c8a7 100644 --- a/submodules/TelegramUI/Components/AvatarEditorScreen/Sources/AvatarPreviewComponent.swift +++ b/submodules/TelegramUI/Components/AvatarEditorScreen/Sources/AvatarPreviewComponent.swift @@ -15,6 +15,7 @@ import Postbox import AnimatedStickerNode import TelegramAnimatedStickerNode import StickerResources +import AvatarBackground final class AvatarPreviewComponent: Component { typealias EnvironmentType = Empty diff --git a/submodules/TelegramUI/Components/AvatarEditorScreen/Sources/BackgroundColorComponent.swift b/submodules/TelegramUI/Components/AvatarEditorScreen/Sources/BackgroundColorComponent.swift index 169030cb08..43a84b6a12 100644 --- a/submodules/TelegramUI/Components/AvatarEditorScreen/Sources/BackgroundColorComponent.swift +++ b/submodules/TelegramUI/Components/AvatarEditorScreen/Sources/BackgroundColorComponent.swift @@ -6,6 +6,7 @@ import ComponentFlow import ViewControllerComponent import ComponentDisplayAdapters import TelegramPresentationData +import AvatarBackground final class BackgroundColorComponent: Component { let theme: PresentationTheme diff --git a/submodules/TelegramUI/Components/CameraScreen/BUILD b/submodules/TelegramUI/Components/CameraScreen/BUILD index 951d4aed83..33964a446c 100644 --- a/submodules/TelegramUI/Components/CameraScreen/BUILD +++ b/submodules/TelegramUI/Components/CameraScreen/BUILD @@ -85,7 +85,7 @@ swift_library( "//submodules/TelegramUI/Components/MediaAssetsContext", "//submodules/UndoUI", "//submodules/ContextUI", - + "//submodules/AvatarNode", ], visibility = [ "//visibility:public", diff --git a/submodules/TelegramUI/Components/CameraScreen/Sources/CameraCodeFrameView.swift b/submodules/TelegramUI/Components/CameraScreen/Sources/CameraCodeFrameView.swift new file mode 100644 index 0000000000..513c3a7feb --- /dev/null +++ b/submodules/TelegramUI/Components/CameraScreen/Sources/CameraCodeFrameView.swift @@ -0,0 +1,177 @@ +import Foundation +import UIKit +import Display +import ComponentFlow +import Camera + +final class CameraCodeFrameView: UIView { + private var cornerLayers: [SimpleShapeLayer] = [] + private let cornerRadius: CGFloat = 12.0 + private let focusedCornerRadius: CGFloat = 6.0 + private let cornerShort: CGFloat = 16.0 + + private var currentSize: CGSize? + private var currentRect: CGRect? + + override init(frame: CGRect) { + super.init(frame: frame) + + self.isUserInteractionEnabled = false + + for _ in 0..<4 { + let layer = SimpleShapeLayer() + layer.fillColor = UIColor.clear.cgColor + layer.strokeColor = UIColor.white.cgColor + layer.lineWidth = 2.0 + layer.lineCap = .round + layer.lineJoin = .round + self.layer.addSublayer(layer) + self.cornerLayers.append(layer) + } + } + + required init?(coder: NSCoder) { + preconditionFailure() + } + + func update(size: CGSize, code: CameraCode?) { + let isFirstTime = self.currentSize == nil + self.currentSize = size + + var duration: Double = 0.0 + + let bounds = CGRect(origin: .zero, size: size) + let rect: CGRect + if let code { + let codeRect = code.boundingBox + let side = max(codeRect.width * bounds.width, codeRect.height * bounds.height) * 0.7 + let center = CGPoint(x: (1.0 - codeRect.center.y) * bounds.width, y: codeRect.center.x * bounds.height) + rect = CGSize(width: side, height: side).centered(around: center) + + if !isFirstTime { + if let currentRect = self.currentRect { + if rect.center.distance(to: currentRect.center) > 40.0 || abs(rect.size.width - currentRect.size.width) > 40.0 { + duration = 0.35 + } else { + duration = 0.2 + } + } else { + duration = 0.4 + } + } + self.currentRect = rect + } else { + rect = bounds.insetBy(dx: -2.0, dy: -2.0) + if !isFirstTime { + duration = 0.4 + } + self.currentRect = nil + } + + let focused = code != nil + self.applyPaths(to: self.cornerPaths(for: rect, focused: focused, rotation: 0.0), focused: focused, duration: duration) + } + + private func cornerPaths(for rect: CGRect, focused: Bool, rotation: Double) -> [UIBezierPath] { + let effectiveCornerRadius = focused ? self.focusedCornerRadius : self.cornerRadius + let center = CGPoint(x: rect.midX, y: rect.midY) + let transform = CGAffineTransform(translationX: center.x, y: center.y).rotated(by: rotation).translatedBy(x: -center.x, y: -center.y) + + let topLeftPath = UIBezierPath() + topLeftPath.move(to: CGPoint(x: rect.minX, y: focused ? rect.minY + self.cornerShort : rect.midY)) + topLeftPath.addLine(to: CGPoint(x: rect.minX, y: rect.minY + effectiveCornerRadius)) + topLeftPath.addQuadCurve( + to: CGPoint(x: rect.minX + effectiveCornerRadius, y: rect.minY), + controlPoint: CGPoint(x: rect.minX, y: rect.minY) + ) + topLeftPath.addLine(to: CGPoint(x: focused ? rect.minX + self.cornerShort : rect.midX, y: rect.minY)) + topLeftPath.apply(transform) + + let topRightPath = UIBezierPath() + topRightPath.move(to: CGPoint(x: rect.maxX, y: focused ? rect.minY + self.cornerShort : rect.midY)) + topRightPath.addLine(to: CGPoint(x: rect.maxX, y: rect.minY + effectiveCornerRadius)) + topRightPath.addQuadCurve( + to: CGPoint(x: rect.maxX - effectiveCornerRadius, y: rect.minY), + controlPoint: CGPoint(x: rect.maxX, y: rect.minY) + ) + topRightPath.addLine(to: CGPoint(x: focused ? rect.maxX - self.cornerShort : rect.midX, y: rect.minY)) + topRightPath.apply(transform) + + let bottomRightPath = UIBezierPath() + bottomRightPath.move(to: CGPoint(x: rect.maxX, y: focused ? rect.maxY - self.cornerShort : rect.midY)) + bottomRightPath.addLine(to: CGPoint(x: rect.maxX, y: rect.maxY - effectiveCornerRadius)) + bottomRightPath.addQuadCurve( + to: CGPoint(x: rect.maxX - effectiveCornerRadius, y: rect.maxY), + controlPoint: CGPoint(x: rect.maxX, y: rect.maxY) + ) + bottomRightPath.addLine(to: CGPoint(x: focused ? rect.maxX - self.cornerShort : rect.midX, y: rect.maxY)) + bottomRightPath.apply(transform) + + let bottomLeftPath = UIBezierPath() + bottomLeftPath.move(to: CGPoint(x: rect.minX, y: focused ? rect.maxY - self.cornerShort : rect.midY)) + bottomLeftPath.addLine(to: CGPoint(x: rect.minX, y: rect.maxY - effectiveCornerRadius)) + bottomLeftPath.addQuadCurve( + to: CGPoint(x: rect.minX + effectiveCornerRadius, y: rect.maxY), + controlPoint: CGPoint(x: rect.minX, y: rect.maxY) + ) + bottomLeftPath.addLine(to: CGPoint(x: focused ? rect.minX + self.cornerShort : rect.midX, y: rect.maxY)) + bottomLeftPath.apply(transform) + + return [topLeftPath, topRightPath, bottomRightPath, bottomLeftPath] + } + + private var animatingAppearance = false + private func applyPaths(to paths: [UIBezierPath], focused: Bool, duration: Double) { + let animatingAppearance = self.animatingAppearance + for (index, path) in paths.enumerated() { + let layer = self.cornerLayers[index] + let previousPath = layer.path + let previousAlpha = layer.opacity + let previousColor = layer.strokeColor ?? UIColor.clear.cgColor + let previousLineWidth = layer.lineWidth + + if duration > 0.0 && !focused { + + } else { + layer.path = path.cgPath + } + layer.opacity = focused ? 1.0 : 0.0 + layer.strokeColor = focused ? UIColor(rgb: 0xf8d74a).cgColor : UIColor.white.cgColor + layer.lineWidth = focused ? 5.0 : 2.0 + layer.shadowOffset = .zero + layer.shadowRadius = 1.0 + layer.shadowColor = UIColor.black.cgColor + layer.shadowOpacity = 0.2 + + if duration > 0.0 && !animatingAppearance { + if focused && previousAlpha.isZero && index == 0 { + self.animatingAppearance = true + } + if focused { + var currentPath = previousPath + var duration = duration + if let presentationPath = layer.presentation()?.path { + currentPath = presentationPath + duration *= 0.5 + } + layer.animate(from: currentPath, to: path.cgPath, keyPath: "path", timingFunction: duration > 0.35 ? kCAMediaTimingFunctionSpring : CAMediaTimingFunctionName.linear.rawValue, duration: duration, completion: { _ in + if focused && index == 0 { + self.animatingAppearance = false + } + }) + } + layer.animateAlpha(from: CGFloat(previousAlpha), to: CGFloat(layer.opacity), duration: focused ? 0.4 : 0.2, timingFunction: CAMediaTimingFunctionName.easeInEaseOut.rawValue, completion: !focused ? { finished in + layer.path = path.cgPath + } : nil) + layer.animate(from: previousColor, to: layer.strokeColor ?? UIColor.white.cgColor, keyPath: "strokeColor", timingFunction: CAMediaTimingFunctionName.easeInEaseOut.rawValue, duration: 0.3, delay: 0.15) + layer.animate(from: previousLineWidth, to: layer.lineWidth, keyPath: "lineWidth", timingFunction: CAMediaTimingFunctionName.easeInEaseOut.rawValue, duration: 0.3) + } + } + } +} + +private extension CGPoint { + func distance(to point: CGPoint) -> CGFloat { + return sqrt(pow((point.x - self.x), 2) + pow((point.y - self.y), 2)) + } +} diff --git a/submodules/TelegramUI/Components/CameraScreen/Sources/CameraCodeResultComponent.swift b/submodules/TelegramUI/Components/CameraScreen/Sources/CameraCodeResultComponent.swift new file mode 100644 index 0000000000..a3b2b29885 --- /dev/null +++ b/submodules/TelegramUI/Components/CameraScreen/Sources/CameraCodeResultComponent.swift @@ -0,0 +1,235 @@ +import Foundation +import UIKit +import Display +import ComponentFlow +import SwiftSignalKit +import TelegramCore +import MultilineTextComponent +import LottieAnimationComponent +import AvatarNode +import AccountContext + +final class CameraCodeResultComponent: Component { + let context: AccountContext + let peer: EnginePeer + let pressed: (EnginePeer) -> Void + + init( + context: AccountContext, + peer: EnginePeer, + pressed: @escaping (EnginePeer) -> Void + ) { + self.context = context + self.peer = peer + self.pressed = pressed + } + + static func ==(lhs: CameraCodeResultComponent, rhs: CameraCodeResultComponent) -> Bool { + if lhs.context !== rhs.context { + return false + } + if lhs.peer != rhs.peer { + return false + } + return true + } + + final class View: UIView { + private var component: CameraCodeResultComponent? + + private let wrapperView = UIView() + private let backgroundView = BlurredBackgroundView(color: UIColor(rgb: 0x2a2a2a, alpha: 0.65)) + private let highlightedBackgroundView = UIView() + private let contentView = UIView() + private let contentWrapperView = UIView() + private let avatarNode = AvatarNode(font: avatarPlaceholderFont(size: 14.0)) + private let title = ComponentView() + private let subtitle = ComponentView() + private let button = HighlightTrackingButton() + + private let contentMaskView = UIView() + private let animationClippingView = UIView() + private let maskAnimation = ComponentView() + private let maskBackgroundView = UIView() + + init() { + self.animationClippingView.clipsToBounds = true + self.maskBackgroundView.backgroundColor = .white + self.maskBackgroundView.layer.cornerRadius = 12.0 + + self.highlightedBackgroundView.alpha = 0.0 + self.highlightedBackgroundView.backgroundColor = UIColor(rgb: 0xffffff, alpha: 0.5) + self.highlightedBackgroundView.isUserInteractionEnabled = false + + super.init(frame: CGRect()) + + self.addSubview(self.wrapperView) + + self.wrapperView.mask = self.contentMaskView + self.wrapperView.addSubview(self.backgroundView) + self.wrapperView.addSubview(self.highlightedBackgroundView) + self.wrapperView.addSubview(self.contentView) + self.wrapperView.addSubview(self.button) + + self.contentMaskView.addSubview(self.animationClippingView) + self.contentMaskView.addSubview(self.maskBackgroundView) + + self.contentView.addSubview(self.contentWrapperView) + self.contentWrapperView.addSubview(self.avatarNode.view) + + self.button.highligthedChanged = { [weak self] highlighted in + guard let self else { + return + } + if highlighted { + self.highlightedBackgroundView.layer.removeAnimation(forKey: "opacity") + self.highlightedBackgroundView.layer.opacity = 1.0 + } else { + self.highlightedBackgroundView.layer.opacity = 0.0 + self.highlightedBackgroundView.layer.animateAlpha(from: 1.0, to: 0.0, duration: 0.2) + } + } + self.button.addTarget(self, action: #selector(self.buttonPressed), for: .touchUpInside) + } + + required init?(coder aDecoder: NSCoder) { + preconditionFailure() + } + + @objc private func buttonPressed() { + if let component = self.component { + component.pressed(component.peer) + } + } + + func animateIn() { + if let view = self.maskAnimation.view as? LottieAnimationComponent.View { + view.playOnce() + Queue.mainQueue().after(0.016) { + view.alpha = 1.0 + } + view.layer.animatePosition(from: CGPoint(x: 0.0, y: 20.0), to: .zero, duration: 0.5, timingFunction: kCAMediaTimingFunctionSpring, additive: true) + + self.maskBackgroundView.layer.animateAlpha(from: 0.0, to: 1.0, duration: 0.001, delay: 0.29, completion: { _ in + view.alpha = 0.0 + }) + } + + let overlayLayer = SimpleLayer() + overlayLayer.frame = CGRect(origin: .zero, size: self.wrapperView.bounds.size) + overlayLayer.backgroundColor = UIColor.white.cgColor + overlayLayer.animateAlpha(from: 1.0, to: 0.0, duration: 0.3, delay: 0.15, removeOnCompletion: false, completion: { _ in + overlayLayer.removeFromSuperlayer() + }) + self.wrapperView.layer.insertSublayer(overlayLayer, at: 2) + + self.maskBackgroundView.layer.animate(from: 27.5, to: 12.0, keyPath: "cornerRadius", timingFunction: kCAMediaTimingFunctionSpring, duration: 0.4, delay: 0.35) + + self.maskBackgroundView.layer.animateSpring(from: NSValue(cgPoint: CGPoint(x: 0.0, y: 66.0)), to: NSValue(cgPoint: .zero), keyPath: "position", duration: 0.8, delay: 0.2, initialVelocity: 0.0, damping: 64.0, removeOnCompletion: true, additive: true, completion: nil) + self.maskBackgroundView.layer.animateSpring(from: 0.1 as NSNumber, to: 1.0 as NSNumber, keyPath: "transform.scale", duration: 1.0, delay: 0.0, initialVelocity: 0.0, damping: 64.0, removeOnCompletion: true, completion: nil) + self.maskBackgroundView.layer.animateSpring(from: NSValue(cgRect: CGRect(origin: .zero, size: CGSize(width: 30.0, height: 55.0))), to: NSValue(cgRect: self.maskBackgroundView.bounds), keyPath: "bounds", duration: 0.85, delay: 0.26, initialVelocity: 0.0, damping: 90.0, removeOnCompletion: true, completion: nil) + + self.contentView.layer.animateSpring(from: NSValue(cgPoint: CGPoint(x: 0.0, y: 66.0)), to: NSValue(cgPoint: .zero), keyPath: "position", duration: 0.8, delay: 0.2, initialVelocity: 0.0, damping: 64.0, removeOnCompletion: true, additive: true, completion: nil) + self.contentView.layer.animateSpring(from: 0.1 as NSNumber, to: 1.0 as NSNumber, keyPath: "transform.scale", duration: 1.0, delay: 0.0, initialVelocity: 0.0, damping: 64.0, removeOnCompletion: true, completion: nil) + + self.contentWrapperView.layer.animateAlpha(from: 0.0, to: 1.0, duration: 0.25, delay: 0.4) + self.contentWrapperView.layer.animateScale(from: 0.2, to: 1.0, duration: 0.25, delay: 0.35) + } + + func update(component: CameraCodeResultComponent, availableSize: CGSize, transition: ComponentTransition) -> CGSize { + self.component = component + + let backgroundSize = CGSize(width: 220.0, height: 55.0) + + let animationSize = self.maskAnimation.update( + transition: .immediate, + component: AnyComponent( + LottieAnimationComponent( + animation: LottieAnimationComponent.AnimationItem( + name: "UserAvatarMask", + mode: .still(position: .end), + range: (1.0, 0.0), + speed: 30.0 + ), + colors: ["__allcolors__": .white], + size: CGSize(width: 94.0, height: 120.0) + ) + ), + environment: {}, + containerSize: availableSize + ) + if let view = self.maskAnimation.view { + if view.superview == nil { + view.alpha = 0.0 + view.transform = CGAffineTransformMakeScale(1.0, -1.0) + self.animationClippingView.addSubview(view) + } + self.animationClippingView.frame = CGRect(origin: CGPoint(x: floor((availableSize.width - animationSize.width) / 2.0), y: 29.0), size: CGSize(width: animationSize.width, height: animationSize.height - 13.0)) + view.frame = CGRect(origin: .zero, size: animationSize) + } + + let presentationData = component.context.sharedContext.currentPresentationData.with { $0 } + + let avatarSize = CGSize(width: 30.0, height: 30.0) + self.avatarNode.setPeer(context: component.context, theme: presentationData.theme, peer: component.peer) + self.avatarNode.frame = CGRect(origin: CGPoint(x: 12.0, y: floorToScreenPixels((backgroundSize.height - avatarSize.height) / 2.0)), size: avatarSize) + + let titleSize = self.title.update( + transition: .immediate, + component: AnyComponent( + MultilineTextComponent(text: .plain(NSAttributedString(string: component.peer.displayTitle(strings: presentationData.strings, displayOrder: presentationData.nameDisplayOrder), font: Font.regular(17.0), textColor: .white))) + ), + environment: {}, + containerSize: CGSize(width: 140.0, height: availableSize.height) + ) + if let view = self.title.view { + if view.superview == nil { + self.contentWrapperView.addSubview(view) + } + view.frame = CGRect(origin: CGPoint(x: 54.0, y: 9.0), size: titleSize) + } + + //TODO:localize + let subtitleString = NSMutableAttributedString(string: "Open Chat >", font: Font.regular(15.0), textColor: UIColor(rgb: 0xffffff, alpha: 0.7)) + if let range = subtitleString.string.range(of: ">"), let arrowImage = UIImage(bundleImageName: "Item List/InlineTextRightArrow") { + subtitleString.addAttribute(.attachment, value: arrowImage, range: NSRange(range, in: subtitleString.string)) + subtitleString.addAttribute(.baselineOffset, value: 1.0, range: NSRange(range, in: subtitleString.string)) + } + let subtitleSize = self.subtitle.update( + transition: .immediate, + component: AnyComponent( + MultilineTextComponent(text: .plain(subtitleString)) + ), + environment: {}, + containerSize: CGSize(width: 140.0, height: availableSize.height) + ) + if let view = self.subtitle.view { + if view.superview == nil { + self.contentWrapperView.addSubview(view) + } + view.frame = CGRect(origin: CGPoint(x: 54.0, y: 29.0), size: subtitleSize) + } + + self.contentView.frame = CGRect(origin: CGPoint(x: floor((availableSize.width - backgroundSize.width) / 2.0), y: 54.0 + UIScreenPixel), size: backgroundSize) + self.contentWrapperView.frame = self.contentView.bounds + self.maskBackgroundView.frame = self.contentView.frame + self.button.frame = self.contentView.frame + self.highlightedBackgroundView.frame = self.contentView.frame + + self.wrapperView.frame = CGRect(origin: .zero, size: CGSize(width: availableSize.width, height: animationSize.height + 17.0)) + self.backgroundView.frame = self.wrapperView.bounds + self.backgroundView.update(size: self.wrapperView.bounds.size, transition: .immediate) + self.contentMaskView.frame = self.wrapperView.bounds + + return CGSize(width: availableSize.width, height: 120.0) + } + } + + func makeView() -> View { + return View() + } + + func update(view: View, availableSize: CGSize, state: EmptyComponentState, environment: Environment, transition: ComponentTransition) -> CGSize { + return view.update(component: self, availableSize: availableSize, transition: transition) + } +} diff --git a/submodules/TelegramUI/Components/CameraScreen/Sources/CameraCollage.swift b/submodules/TelegramUI/Components/CameraScreen/Sources/CameraCollage.swift index e82638ba2f..e630e84e18 100644 --- a/submodules/TelegramUI/Components/CameraScreen/Sources/CameraCollage.swift +++ b/submodules/TelegramUI/Components/CameraScreen/Sources/CameraCollage.swift @@ -162,6 +162,9 @@ final class CameraCollage { didSet { if self.grid != oldValue { self._state.grid = self.grid + if let cameraIndex = self.cameraIndex, cameraIndex > self.grid.count - 1 { + self.cameraIndex = self.grid.count - 1 + } self.updateState() } } @@ -202,6 +205,19 @@ final class CameraCollage { self.updateState() } + func addResults(signals: [Signal]) { + guard self.results.count < self.grid.count else { + return + } + self.results.append(contentsOf: signals.map { + CaptureResult(result: $0, snapshotView: nil, contentUpdated: { [weak self] in + self?.checkResults() + self?.updateState() + }) + }) + self.updateState() + } + func moveItem(fromId: Int64, toId: Int64) { guard let fromIndex = self.uniqueIds.firstIndex(where: { $0 == fromId }), let toIndex = self.uniqueIds.firstIndex(where: { $0 == toId }), toIndex < self.results.count else { return @@ -310,7 +326,7 @@ final class CameraCollage { return self._state.progress > 1.0 - .ulpOfOne } - var result: Signal { + func result(itemViews: [Int64 : CameraCollageView.ItemView]) -> Signal { guard self.isComplete else { return .complete() } @@ -337,6 +353,9 @@ outer: for row in state.rows { let columnWidth: CGFloat = floor(size.width / CGFloat(row.items.count)) itemFrame = CGRect(origin: itemFrame.origin, size: CGSize(width: columnWidth, height: rowHeight)) for item in row.items { + let scale = itemViews[item.uniqueId]?.contentScale ?? 1.0 + let offset = itemViews[item.uniqueId]?.contentOffset ?? .zero + let content: CameraScreenImpl.Result.VideoCollage.Item.Content switch item.content { case let .image(image): @@ -351,7 +370,12 @@ outer: for row in state.rows { default: fatalError() } - items.append(CameraScreenImpl.Result.VideoCollage.Item(content: content, frame: itemFrame)) + items.append(CameraScreenImpl.Result.VideoCollage.Item( + content: content, + frame: itemFrame, + contentScale: scale, + contentOffset: offset + )) itemFrame.origin.x += columnWidth } itemFrame.origin.x = 0.0 @@ -362,14 +386,19 @@ outer: for row in state.rows { let image = generateImage(size, contextGenerator: { size, context in var itemFrame: CGRect = .zero for row in state.rows { - let columnWidth: CGFloat = floor(size.width / CGFloat(row.items.count)) + let columnWidth: CGFloat = ceil(size.width / CGFloat(row.items.count)) itemFrame = CGRect(origin: itemFrame.origin, size: CGSize(width: columnWidth, height: rowHeight)) for item in row.items { + let scale = itemViews[item.uniqueId]?.contentScale ?? 1.0 + let offset = itemViews[item.uniqueId]?.contentOffset ?? .zero + let mappedItemFrame = CGRect(origin: CGPoint(x: itemFrame.minX, y: size.height - itemFrame.origin.y - rowHeight), size: CGSize(width: columnWidth, height: rowHeight)) if case let .image(image) = item.content { context.clip(to: mappedItemFrame) let drawingSize = image.size.aspectFilled(mappedItemFrame.size) - let imageFrame = drawingSize.centered(around: mappedItemFrame.center) + let center = mappedItemFrame.center.offsetBy(dx: offset.x * mappedItemFrame.width, dy: offset.y * mappedItemFrame.height) + + let imageFrame = CGSize(width: drawingSize.width * scale, height: drawingSize.height * scale).centered(around: center) if let cgImage = image.cgImage { context.draw(cgImage, in: imageFrame, byTiling: false) } @@ -411,10 +440,12 @@ final class CameraCollageView: UIView, UIGestureRecognizerDelegate { } } - final class ItemView: ContextControllerSourceView { + final class ItemView: ContextControllerSourceView, UIScrollViewDelegate { private let extractedContainerView = ContextExtractedContentContainingView() + private let scrollView = UIScrollView() private let clippingView = UIView() + private let contentView = UIView() private var snapshotView: UIView? private var cameraContainerView: UIView? private var imageView: UIImageView? @@ -429,6 +460,14 @@ final class CameraCollageView: UIView, UIGestureRecognizerDelegate { var contextAction: ((Int64, ContextExtractedContentContainingView, ContextGesture?) -> Void)? + var contentScale: CGFloat { + return self.scrollView.zoomScale + } + + var contentOffset: CGPoint { + return self.scrollView.offsetFromCenter + } + var isCamera: Bool { if case .camera = self.item?.content { return true @@ -459,8 +498,17 @@ final class CameraCollageView: UIView, UIGestureRecognizerDelegate { override init(frame: CGRect) { super.init(frame: frame) - self.clippingView.clipsToBounds = true + self.scrollView.delegate = self + self.scrollView.contentInsetAdjustmentBehavior = .never + self.scrollView.contentInset = .zero + self.scrollView.showsHorizontalScrollIndicator = false + self.scrollView.showsVerticalScrollIndicator = false + self.scrollView.decelerationRate = .fast + //self.scrollView.panGestureRecognizer.minimumNumberOfTouches = 2 + self.clippingView.clipsToBounds = true + self.clippingView.isUserInteractionEnabled = false + self.addSubview(self.extractedContainerView) self.isGestureEnabled = false @@ -468,8 +516,11 @@ final class CameraCollageView: UIView, UIGestureRecognizerDelegate { self.clipsToBounds = true self.extractedContainerView.contentView.clipsToBounds = true + self.extractedContainerView.contentView.addSubview(self.scrollView) self.extractedContainerView.contentView.addSubview(self.clippingView) + self.scrollView.addSubview(self.contentView) + self.extractedContainerView.willUpdateIsExtractedToContextPreview = { [weak self] value, _ in guard let self else { return @@ -477,10 +528,13 @@ final class CameraCollageView: UIView, UIGestureRecognizerDelegate { let transition = ContainedViewLayoutTransition.animated(duration: 0.2, curve: .easeInOut) if value { self.clippingView.layer.cornerRadius = 12.0 + self.scrollView.layer.cornerRadius = 12.0 transition.updateSublayerTransformScale(layer: self.extractedContainerView.contentView.layer, scale: CGPoint(x: 0.9, y: 0.9)) } else { self.clippingView.layer.cornerRadius = 0.0 self.clippingView.layer.animate(from: NSNumber(value: Float(12.0)), to: NSNumber(value: Float(0.0)), keyPath: "cornerRadius", timingFunction: CAMediaTimingFunctionName.easeInEaseOut.rawValue, duration: 0.2) + self.scrollView.layer.cornerRadius = 0.0 + self.scrollView.layer.animate(from: NSNumber(value: Float(12.0)), to: NSNumber(value: Float(0.0)), keyPath: "cornerRadius", timingFunction: CAMediaTimingFunctionName.easeInEaseOut.rawValue, duration: 0.2) transition.updateSublayerTransformScale(layer: self.extractedContainerView.contentView.layer, scale: CGPoint(x: 1.0, y: 1.0)) } } @@ -521,6 +575,13 @@ final class CameraCollageView: UIView, UIGestureRecognizerDelegate { let center = CGPoint(x: size.width / 2.0, y: size.height / 2.0) + let bounds = CGRect(origin: .zero, size: size) + var sizeUpdated = false + if self.scrollView.frame.size.width > 0.0 && self.scrollView.frame.size != size { + sizeUpdated = true + } + transition.setFrame(view: self.scrollView, frame: CGRect(origin: .zero, size: size)) + switch item.content { case let .pending(placeholder): if let placeholder { @@ -659,7 +720,8 @@ final class CameraCollageView: UIView, UIGestureRecognizerDelegate { previewLayer.removeFromSuperlayer() self.previewLayer = nil } - + + var added = false var imageTransition = transition var imageView: UIImageView if let current = self.imageView { @@ -669,10 +731,19 @@ final class CameraCollageView: UIView, UIGestureRecognizerDelegate { imageView = UIImageView() imageView.contentMode = .scaleAspectFill self.imageView = imageView - self.clippingView.addSubview(imageView) + self.contentView.addSubview(imageView) + added = true } imageView.image = image - imageTransition.setFrame(view: imageView, frame: CGRect(origin: .zero, size: size)) + + let dimensions = image.size.aspectFilled(size) + imageTransition.setFrame(view: imageView, frame: CGRect(origin: .zero, size: dimensions)) + + if added || sizeUpdated { + self.contentView.bounds = CGRect(origin: .zero, size: dimensions) + self.scrollView.contentSize = dimensions + self.scrollView.resetZooming() + } case let .video(asset, _, _, _): if let cameraContainerView = self.cameraContainerView { cameraContainerView.removeFromSuperview() @@ -696,6 +767,7 @@ final class CameraCollageView: UIView, UIGestureRecognizerDelegate { self.previewLayer = nil } + var added = false var imageTransition = transition if self.videoLayer == nil { imageTransition = .immediate @@ -723,17 +795,27 @@ final class CameraCollageView: UIView, UIGestureRecognizerDelegate { self.videoLayer = videoLayer self.videoPlayer = player - self.clippingView.layer.addSublayer(videoLayer) + self.contentView.layer.addSublayer(videoLayer) player.playImmediately(atRate: 1.0) + + added = true } + let dimensions = (asset.videoDimensions ?? CGSize(width: 1.0, height: 1.0)).aspectFilled(size) if let videoLayer = self.videoLayer { - imageTransition.setFrame(layer: videoLayer, frame: CGRect(origin: .zero, size: size)) + imageTransition.setFrame(layer: videoLayer, frame: CGRect(origin: .zero, size: dimensions)) + } + + if added || sizeUpdated { + self.contentView.bounds = CGRect(origin: .zero, size: dimensions) + self.scrollView.contentSize = dimensions + self.scrollView.resetZooming() } } - - let bounds = CGRect(origin: .zero, size: size) + + self.adjustPreviewZoom(updating: true) + transition.setFrame(view: self.extractedContainerView, frame: bounds) transition.setFrame(view: self.extractedContainerView.contentView, frame: bounds) transition.setBounds(view: self.clippingView, bounds: bounds) @@ -774,6 +856,49 @@ final class CameraCollageView: UIView, UIGestureRecognizerDelegate { completion() }) } + + private func adjustPreviewZoom(updating: Bool = false) { + let minScale: CGFloat = 1.0 + let maxScale: CGFloat = 3.5 + + if self.scrollView.minimumZoomScale != minScale { + self.scrollView.minimumZoomScale = minScale + } + if self.scrollView.maximumZoomScale != maxScale { + self.scrollView.maximumZoomScale = maxScale + } + + let boundsSize = self.scrollView.frame.size + var contentFrame = self.contentView.frame + if boundsSize.width > contentFrame.size.width { + contentFrame.origin.x = (boundsSize.width - contentFrame.size.width) / 2.0 + } else { + contentFrame.origin.x = 0.0 + } + + if boundsSize.height > contentFrame.size.height { + contentFrame.origin.y = (boundsSize.height - contentFrame.size.height) / 2.0 + } else { + contentFrame.origin.y = 0.0 + } + self.contentView.frame = contentFrame + } + + func scrollViewDidZoom(_ scrollView: UIScrollView) { + self.adjustPreviewZoom() + } + + func scrollViewDidEndZooming(_ scrollView: UIScrollView, with view: UIView?, atScale scale: CGFloat) { + self.adjustPreviewZoom() + + if scrollView.zoomScale < 1.0 { + scrollView.setZoomScale(1.0, animated: true) + } + } + + func viewForZooming(in scrollView: UIScrollView) -> UIView? { + return self.contentView + } } private let context: AccountContext @@ -805,6 +930,10 @@ final class CameraCollageView: UIView, UIGestureRecognizerDelegate { var isEnabled: Bool = true + var result: Signal { + return self.collage.result(itemViews: self.itemViews) + } + init(context: AccountContext, collage: CameraCollage, camera: Camera?, cameraContainerView: UIView?) { self.context = context self.collage = collage @@ -934,6 +1063,15 @@ final class CameraCollageView: UIView, UIGestureRecognizerDelegate { if otherGestureRecognizer is UITapGestureRecognizer { return true } + if otherGestureRecognizer is UIPanGestureRecognizer { + if gestureRecognizer === self.reorderRecognizer, ![.began, .changed].contains(gestureRecognizer.state) { + gestureRecognizer.isEnabled = false + gestureRecognizer.isEnabled = true + return true + } else { + return false + } + } return false } @@ -1074,7 +1212,9 @@ final class CameraCollageView: UIView, UIGestureRecognizerDelegate { } if self.itemViews.count > 2 { - itemList.append(.separator) + if itemList.count > 0 { + itemList.append(.separator) + } itemList.append(.action(ContextMenuActionItem(text: presentationData.strings.Camera_CollageDelete, textColor: .destructive, icon: { theme in return generateTintedImage(image: UIImage(bundleImageName: "Chat/Context Menu/Delete"), color: theme.contextMenu.destructiveColor) @@ -1418,3 +1558,46 @@ private final class CollageContextExtractedContentSource: ContextExtractedConten return ContextControllerPutBackViewInfo(contentAreaInScreenSpace: UIScreen.main.bounds) } } + +private extension AVAsset { + var videoDimensions: CGSize? { + if let videoTrack = self.tracks(withMediaType: .video).first { + let size = videoTrack.naturalSize + let transform = videoTrack.preferredTransform + let isPortrait = transform.a == 0 && abs(transform.b) == 1 && abs(transform.c) == 1 && transform.d == 0 + return isPortrait ? CGSize(width: size.height, height: size.width) : size + } + return nil + } +} + +private extension UIScrollView { + func resetZooming() { + guard let contentView = self.delegate?.viewForZooming?(in: self) else { return } + + let scrollViewSize = self.bounds.size + let contentSize = contentView.frame.size + + let offsetX = (scrollViewSize.width - contentSize.width) * 0.5 + let offsetY = (scrollViewSize.height - contentSize.height) * 0.5 + + contentView.center = CGPoint( + x: scrollViewSize.width / 2.0 + self.contentOffset.x, + y: scrollViewSize.height / 2.0 + self.contentOffset.y + ) + + self.contentOffset = CGPoint(x: -offsetX, y: -offsetY) + } + + var offsetFromCenter: CGPoint { + let contentCenterX = (self.contentSize.width - self.bounds.width) / 2.0 + let contentCenterY = (self.contentSize.height - self.bounds.height) / 2.0 + let contentCenter = CGPoint(x: contentCenterX, y: contentCenterY) + + let currentOffset = self.contentOffset + let deltaX = currentOffset.x - contentCenter.x + let deltaY = currentOffset.y - contentCenter.y + + return CGPoint(x: -(deltaX / self.bounds.width), y: (deltaY / self.bounds.height)) + } +} diff --git a/submodules/TelegramUI/Components/CameraScreen/Sources/CameraScreen.swift b/submodules/TelegramUI/Components/CameraScreen/Sources/CameraScreen.swift index 82fd580a32..9886fb1adb 100644 --- a/submodules/TelegramUI/Components/CameraScreen/Sources/CameraScreen.swift +++ b/submodules/TelegramUI/Components/CameraScreen/Sources/CameraScreen.swift @@ -154,6 +154,7 @@ private final class CameraScreenComponent: CombinedComponent { let hasAppeared: Bool let isVisible: Bool let panelWidth: CGFloat + let resolvedCodePeer: EnginePeer? let animateFlipAction: ActionSlot let animateShutter: () -> Void let toggleCameraPositionAction: ActionSlot @@ -162,6 +163,7 @@ private final class CameraScreenComponent: CombinedComponent { let present: (ViewController) -> Void let push: (ViewController) -> Void let completion: ActionSlot> + let openResolvedPeer: (EnginePeer) -> Void init( context: AccountContext, @@ -171,6 +173,7 @@ private final class CameraScreenComponent: CombinedComponent { hasAppeared: Bool, isVisible: Bool, panelWidth: CGFloat, + resolvedCodePeer: EnginePeer?, animateFlipAction: ActionSlot, animateShutter: @escaping () -> Void, toggleCameraPositionAction: ActionSlot, @@ -178,7 +181,8 @@ private final class CameraScreenComponent: CombinedComponent { getController: @escaping () -> CameraScreenImpl?, present: @escaping (ViewController) -> Void, push: @escaping (ViewController) -> Void, - completion: ActionSlot> + completion: ActionSlot>, + openResolvedPeer: @escaping (EnginePeer) -> Void ) { self.context = context self.cameraState = cameraState @@ -187,6 +191,7 @@ private final class CameraScreenComponent: CombinedComponent { self.hasAppeared = hasAppeared self.isVisible = isVisible self.panelWidth = panelWidth + self.resolvedCodePeer = resolvedCodePeer self.animateFlipAction = animateFlipAction self.animateShutter = animateShutter self.toggleCameraPositionAction = toggleCameraPositionAction @@ -195,6 +200,7 @@ private final class CameraScreenComponent: CombinedComponent { self.present = present self.push = push self.completion = completion + self.openResolvedPeer = openResolvedPeer } static func ==(lhs: CameraScreenComponent, rhs: CameraScreenComponent) -> Bool { @@ -219,6 +225,9 @@ private final class CameraScreenComponent: CombinedComponent { if lhs.panelWidth != rhs.panelWidth { return false } + if lhs.resolvedCodePeer != rhs.resolvedCodePeer { + return false + } return true } @@ -630,6 +639,10 @@ private final class CameraScreenComponent: CombinedComponent { self.updated(transition: .easeInOut(duration: 0.2)) } + var isRecording: Bool { + return self.cameraState?.recording != CameraState.Recording.none + } + var isTakingPhoto = false func takePhoto() { guard let controller = self.getController(), let camera = controller.camera, let cameraState = self.cameraState else { @@ -890,11 +903,14 @@ private final class CameraScreenComponent: CombinedComponent { state.cameraState = component.cameraState state.volumeButtonsListenerActive = component.hasAppeared && component.isVisible - let isSticker: Bool - if let controller = controller() as? CameraScreenImpl, case .sticker = controller.mode { - isSticker = true - } else { - isSticker = false + var isSticker = false + var isAvatar = false + if let controller = controller() as? CameraScreenImpl { + if case .sticker = controller.mode { + isSticker = true + } else if case .avatar = controller.mode { + isAvatar = true + } } let isTablet: Bool @@ -1028,8 +1044,10 @@ private final class CameraScreenComponent: CombinedComponent { let captureControls = captureControls.update( component: CaptureControlsComponent( + context: component.context, isTablet: isTablet, isSticker: isSticker, + hasGallery: !isSticker && !isAvatar, hasAppeared: component.hasAppeared && hasAllRequiredAccess, hasAccess: hasAllRequiredAccess, hideControls: component.cameraState.collageProgress > 1.0 - .ulpOfOne, @@ -1038,6 +1056,7 @@ private final class CameraScreenComponent: CombinedComponent { tintColor: controlsTintColor, shutterState: shutterState, lastGalleryAsset: state.lastGalleryAsset, + resolvedCodePeer: state.isTakingPhoto || state.isRecording ? nil : component.resolvedCodePeer, tag: captureControlsTag, galleryButtonTag: galleryButtonTag, shutterTapped: { [weak state] in @@ -1096,7 +1115,8 @@ private final class CameraScreenComponent: CombinedComponent { state.updateZoom(fraction: fraction) } }, - flipAnimationAction: animateFlipAction + flipAnimationAction: animateFlipAction, + openResolvedPeer: component.openResolvedPeer ), availableSize: captureControlsAvailableSize, transition: context.transition @@ -1259,7 +1279,7 @@ private final class CameraScreenComponent: CombinedComponent { rightMostButtonWidth = flashButton.size.width } - if !isSticker && !isTablet { + if !isSticker && !isAvatar && !isTablet { var nextButtonX = availableSize.width - topControlInset - rightMostButtonWidth / 2.0 - 58.0 if Camera.isDualCameraSupported(forRoundVideo: false) && !component.cameraState.isCollageEnabled { let dualButton = dualButton.update( @@ -1581,6 +1601,7 @@ public class CameraScreenImpl: ViewController, CameraScreen { public enum Mode { case story case sticker + case avatar } public enum PIPPosition: Int32 { @@ -1618,6 +1639,8 @@ public class CameraScreenImpl: ViewController, CameraScreen { } public let content: Content public let frame: CGRect + public let contentScale: CGFloat + public let contentOffset: CGPoint } public let items: [Item] } @@ -1687,6 +1710,7 @@ public class CameraScreenImpl: ViewController, CameraScreen { fileprivate let backgroundView: UIView fileprivate let containerView: UIView fileprivate let componentHost: ComponentView + fileprivate let codeFrameView: CameraCodeFrameView private let previewContainerView: UIView private let collageContainerView: UIView @@ -1821,7 +1845,7 @@ public class CameraScreenImpl: ViewController, CameraScreen { isDualCameraEnabled = isDualCameraEnabledValue.boolValue } } - if case .sticker = controller.mode { + if [.sticker, .avatar].contains(controller.mode) { isDualCameraEnabled = false } @@ -1836,6 +1860,8 @@ public class CameraScreenImpl: ViewController, CameraScreen { cameraFrontPosition = true } + self.codeFrameView = CameraCodeFrameView(frame: .zero) + self.collageContainerView = UIView() self.collageContainerView.clipsToBounds = true @@ -1901,6 +1927,7 @@ public class CameraScreenImpl: ViewController, CameraScreen { self.previewContainerView.addSubview(self.previewBlurView) self.previewContainerView.addSubview(self.previewFrameLeftDimView) self.previewContainerView.addSubview(self.previewFrameRightDimView) + self.previewContainerView.addSubview(self.codeFrameView) self.containerView.addSubview(self.transitionDimView) self.view.addSubview(self.transitionCornersView) @@ -1912,21 +1939,16 @@ public class CameraScreenImpl: ViewController, CameraScreen { if let self { let pipPosition = self.pipPosition if self.cameraState.isCollageEnabled { - if let collage = self.collage { + if let collage = self.collage, let collageView = self.collageView { if collage.isComplete { self.animateOutToEditor() self.controller?.completion( - collage.result + collageView.result |> beforeNext { [weak self] value in guard let self else { return } Queue.mainQueue().async { - if case .image = value { - Queue.mainQueue().after(0.3) { - self.previewBlurPromise.set(true) - } - } self.mainPreviewView.isEnabled = false self.additionalPreviewView.isEnabled = false self.camera?.stopCapture() @@ -2099,7 +2121,7 @@ public class CameraScreenImpl: ViewController, CameraScreen { isDualEnabled: self.cameraState.isDualCameraEnabled, audio: true, photo: true, - metadata: false + metadata: true ), previewView: self.mainPreviewView, secondaryPreviewView: self.additionalPreviewView @@ -2216,6 +2238,29 @@ public class CameraScreenImpl: ViewController, CameraScreen { camera.focus(at: CGPoint(x: 0.5, y: 0.5), autoFocus: true) if isNew { + let throttledSignal = camera.detectedCodes + |> mapToThrottled { next -> Signal<[CameraCode], NoError> in + return .single(next) |> then(.complete() |> delay(0.1, queue: Queue.concurrentDefaultQueue())) + } + self.controller?.codeDisposable = (throttledSignal + |> deliverOnMainQueue).start(next: { [weak self] codes in + guard let self else { + return + } + let filteredCodes = codes.filter { + let message = $0.message.replacingOccurrences(of: "https://", with: "") + if message.hasPrefix("t.me/c/") || message.hasPrefix("t.me/+") || message.hasPrefix("t.me/contact/") || message.hasPrefix("t.me/") { + return true + } else { + return false + } + } + if let code = filteredCodes.first, !self.cameraState.isCollageEnabled && self.cameraState.recording == CameraState.Recording.none { + self.controller?.updateFocusedCode(code) + } else { + self.controller?.updateFocusedCode(nil) + } + }) camera.startCapture() } self.captureStartTimestamp = CACurrentMediaTime() @@ -2287,7 +2332,7 @@ public class CameraScreenImpl: ViewController, CameraScreen { gestureRecognizer.isEnabled = false gestureRecognizer.isEnabled = true } - case .sticker: + case .sticker, .avatar: if (abs(translation.y) > 10.0 || self.isDismissing) && self.hasAppeared { self.containerView.layer.sublayerTransform = CATransform3DMakeTranslation(0.0, translation.y, 0.0) if !self.isDismissing { @@ -2308,7 +2353,7 @@ public class CameraScreenImpl: ViewController, CameraScreen { controller.completeWithTransitionProgress(transitionFraction, velocity: abs(velocity.x), dismissing: true) self.isDismissing = false - case .sticker: + case .sticker, .avatar: let velocity = gestureRecognizer.velocity(in: self.view) let transitionFraction = translation.y / self.frame.height if abs(transitionFraction) > 0.3 || abs(velocity.y) > 1000.0 { @@ -2495,7 +2540,7 @@ public class CameraScreenImpl: ViewController, CameraScreen { } self.mainPreviewAnimationWrapperView.layer.animateBounds(from: sourceBounds, to: self.mainPreviewView.bounds, duration: 0.3, timingFunction: kCAMediaTimingFunctionSpring) - let sourceScale = sourceInnerFrame.height / self.previewContainerView.frame.height + let sourceScale = max(sourceInnerFrame.width / self.previewContainerView.frame.width, sourceInnerFrame.height / self.previewContainerView.frame.height) self.mainPreviewView.transform = CGAffineTransform.identity self.mainPreviewAnimationWrapperView.layer.animateScale(from: sourceScale, to: 1.0, duration: 0.3, timingFunction: kCAMediaTimingFunctionSpring, completion: { _ in self.mainPreviewContainerView.addSubview(self.mainPreviewView) @@ -2562,10 +2607,14 @@ public class CameraScreenImpl: ViewController, CameraScreen { if let holder = controller.holder { targetBounds = CGRect(origin: .zero, size: holder.parentView.frame.size.aspectFitted(targetBounds.size)) } - self.mainPreviewView.center = self.mainPreviewView.center.offsetBy(dx: (targetBounds.width - self.mainPreviewView.bounds.width) / 2.0, dy: 0.0) + + let previousPosition = self.mainPreviewView.center + self.mainPreviewView.center = self.mainPreviewView.center.offsetBy(dx: (targetBounds.width - self.mainPreviewView.bounds.width) / 2.0, dy: (targetBounds.height - self.mainPreviewView.bounds.height) / 2.0) + self.mainPreviewView.layer.animatePosition(from: previousPosition, to: self.mainPreviewView.center, duration: 0.3, timingFunction: kCAMediaTimingFunctionSpring) + self.mainPreviewAnimationWrapperView.layer.animateBounds(from: self.mainPreviewView.bounds, to: targetBounds, duration: 0.3, timingFunction: kCAMediaTimingFunctionSpring, removeOnCompletion: false) - let targetScale = destinationInnerFrame.height / self.previewContainerView.frame.height + let targetScale = max(destinationInnerFrame.width / self.previewContainerView.frame.width, destinationInnerFrame.height / self.previewContainerView.frame.height) self.mainPreviewAnimationWrapperView.layer.animateScale(from: 1.0, to: targetScale, duration: 0.3, timingFunction: kCAMediaTimingFunctionSpring, removeOnCompletion: false) } @@ -2616,6 +2665,8 @@ public class CameraScreenImpl: ViewController, CameraScreen { } Queue.mainQueue().after(1.5, { + self.controller?.updateFocusedCode(nil) + if let collageView = self.collageView { collageView.stopPlayback() } @@ -2833,7 +2884,11 @@ public class CameraScreenImpl: ViewController, CameraScreen { if self.additionalPreviewContainerView.bounds.contains(self.view.convert(point, to: self.additionalPreviewContainerView)) { return self.additionalPreviewContainerView } else { - return self.collageView ?? self.mainPreviewView + if let collageView = self.collageView { + return collageView.hitTest(self.view.convert(point, to: collageView), with: event) + } else { + return self.mainPreviewView + } } } return result @@ -2926,7 +2981,8 @@ public class CameraScreenImpl: ViewController, CameraScreen { } self.didAppear() } - + + let componentSize = self.componentHost.update( transition: transition, component: AnyComponent( @@ -2938,6 +2994,7 @@ public class CameraScreenImpl: ViewController, CameraScreen { hasAppeared: self.hasAppeared, isVisible: self.cameraIsActive && !self.hasGallery && self.postingAvailable, panelWidth: panelWidth, + resolvedCodePeer: controller.resolvedCodePeer, animateFlipAction: self.animateFlipAction, animateShutter: { [weak self] in guard let self else { @@ -2963,7 +3020,21 @@ public class CameraScreenImpl: ViewController, CameraScreen { push: { [weak self] c in self?.controller?.push(c) }, - completion: self.completion + completion: self.completion, + openResolvedPeer: { [weak self] peer in + guard let self, let controller = self.controller else { + return + } + let context = self.context + let navigationController = controller.navigationController as? NavigationController + controller.requestDismiss(animated: true, interactive: false) + Queue.mainQueue().after(0.4) { + guard let navigationController else { + return + } + context.sharedContext.navigateToChatController(NavigateToChatControllerParams(navigationController: navigationController, context: context, chatLocation: .peer(peer))) + } + } ) ), environment: { @@ -3002,6 +3073,9 @@ public class CameraScreenImpl: ViewController, CameraScreen { transition.setFrame(view: self.previewContainerView, frame: previewContainerFrame) transition.setFrame(view: self.collageContainerView, frame: CGRect(origin: .zero, size: previewContainerFrame.size)) + transition.setFrame(view: self.codeFrameView, frame: CGRect(origin: .zero, size: previewContainerFrame.size)) + self.codeFrameView.update(size: previewContainerFrame.size, code: controller.focusedCode) + if self.cameraState.isCollageEnabled { let collage: CameraCollage if let current = self.collage { @@ -3045,7 +3119,7 @@ public class CameraScreenImpl: ViewController, CameraScreen { self.controller?.updateCameraState({ $0.updatedIsCollageEnabled(false).updatedCollageProgress(0.0) }, transition: .spring(duration: 0.3)) } else { let currentCount = self.cameraState.collageGrid.count - for grid in collageGrids.reversed() { + for grid in collageGrids { if grid.count == currentCount - 1 { self.controller?.updateCameraState({ $0.updatedCollageGrid(grid) }, transition: .spring(duration: 0.3)) break @@ -3164,7 +3238,7 @@ public class CameraScreenImpl: ViewController, CameraScreen { let additionalPreviewInnerFrame = CGRect(origin: CGPoint(x: floorToScreenPixels((circleSide - additionalPreviewInnerSize.width) / 2.0), y: floorToScreenPixels((circleSide - additionalPreviewInnerSize.height) / 2.0)), size: additionalPreviewInnerSize) if mainPreviewView.superview != self.mainPreviewContainerView { - if case .sticker = controller.mode, !self.animatedIn { + if [.sticker, .avatar].contains(controller.mode), !self.animatedIn { } else { self.mainPreviewContainerView.insertSubview(mainPreviewView, at: 0) @@ -3174,7 +3248,7 @@ public class CameraScreenImpl: ViewController, CameraScreen { self.additionalPreviewContainerView.insertSubview(additionalPreviewView, at: 0) } - if case .sticker = controller.mode { + if [.sticker, .avatar].contains(controller.mode) { if self.animatedIn { mainPreviewView.frame = mainPreviewInnerFrame } @@ -3207,7 +3281,7 @@ public class CameraScreenImpl: ViewController, CameraScreen { transition.setPosition(view: self.transitionCornersView, position: CGPoint(x: layout.size.width + screenCornerRadius / 2.0, y: layout.size.height / 2.0)) transition.setBounds(view: self.transitionCornersView, bounds: CGRect(origin: .zero, size: CGSize(width: screenCornerRadius, height: layout.size.height))) - if (controller.mode == .sticker || isTablet) && isFirstTime { + if ([.sticker, .avatar].contains(controller.mode) || isTablet) && isFirstTime { self.animateIn() } @@ -3256,6 +3330,17 @@ public class CameraScreenImpl: ViewController, CameraScreen { private let postingAvailabilityPromise = Promise() private var postingAvailabilityDisposable: Disposable? + private var codeDisposable: Disposable? + private var resolveCodeDisposable: Disposable? + private var focusedCodePromise = ValuePromise() + var focusedCode: CameraCode? { + didSet { + self.focusedCodePromise.set(self.focusedCode) + } + } + private var resolvePeerDisposable = MetaDisposable() + private var resolvedCodePeer: EnginePeer? + private let hapticFeedback = HapticFeedback() private var validLayout: ContainerViewLayout? @@ -3312,6 +3397,9 @@ public class CameraScreenImpl: ViewController, CameraScreen { deinit { self.audioSessionDisposable?.dispose() self.postingAvailabilityDisposable?.dispose() + self.codeDisposable?.dispose() + self.resolveCodeDisposable?.dispose() + self.resolvePeerDisposable.dispose() if #available(iOS 13.0, *) { try? AVAudioSession.sharedInstance().setAllowHapticsAndSystemSoundsDuringRecording(false) } @@ -3378,6 +3466,54 @@ public class CameraScreenImpl: ViewController, CameraScreen { } }) } + + self.resolveCodeDisposable = (self.focusedCodePromise.get() + |> map { code in + return code?.message + } + |> distinctUntilChanged + |> mapToSignal { code -> Signal in + if let _ = code { + return .single(code) + } else { + return .single(code) + |> delay(1.0, queue: Queue.mainQueue()) + } + }).start(next: { [weak self] code in + guard let self else { + return + } + if let code { + self.resolvePeerDisposable.set( + (self.context.sharedContext.resolveUrl(context: self.context, peerId: nil, url: code, skipUrlAuth: false) + |> deliverOnMainQueue).start(next: { [weak self] resolvedUrl in + guard let self else { + return + } + if case let .peer(peer, _) = resolvedUrl, let peer { + self.resolvedCodePeer = EnginePeer(peer) + self.requestLayout(transition: .animated(duration: 0.4, curve: .spring)) + } + }) + ) + } else { + self.resolvedCodePeer = nil + self.requestLayout(transition: .animated(duration: 0.4, curve: .spring)) + } + }) + } + + private func updateFocusedCode(_ code: CameraCode?) { + if self.focusedCode != code { + self.focusedCode = code + if code == nil { + Queue.mainQueue().after(1.0, { + self.requestLayout(transition: .animated(duration: 0.4, curve: .spring)) + }) + } else { + self.requestLayout(transition: .animated(duration: 0.4, curve: .spring)) + } + } } private func requestAudioSession() { @@ -3439,24 +3575,106 @@ public class CameraScreenImpl: ViewController, CameraScreen { if let current = self.galleryController { controller = current } else { - controller = self.context.sharedContext.makeStoryMediaPickerScreen(context: self.context, isDark: true, forCollage: self.cameraState.isCollageEnabled, getSourceRect: { [weak self] in - if let self { - if let galleryButton = self.node.componentHost.findTaggedView(tag: galleryButtonTag) { - return galleryButton.convert(galleryButton.bounds, to: self.view).offsetBy(dx: 0.0, dy: -15.0) + var selectionLimit: Int? + if self.cameraState.isCollageEnabled, let collage = self.node.collage { + selectionLimit = collage.grid.count - collage.results.count + } else { + selectionLimit = 6 + } + controller = self.context.sharedContext.makeStoryMediaPickerScreen( + context: self.context, + isDark: true, + forCollage: self.cameraState.isCollageEnabled, + selectionLimit: selectionLimit, + getSourceRect: { [weak self] in + if let self { + if let galleryButton = self.node.componentHost.findTaggedView(tag: galleryButtonTag) { + return galleryButton.convert(galleryButton.bounds, to: self.view).offsetBy(dx: 0.0, dy: -15.0) + } else { + return .zero + } } else { return .zero } - } else { - return .zero - } - }, completion: { [weak self] result, transitionView, transitionRect, transitionImage, transitionOut, dismissed in - if let self { - if self.cameraState.isCollageEnabled { - if let asset = result as? PHAsset { + }, completion: { [weak self] result, transitionView, transitionRect, transitionImage, transitionOut, dismissed in + if let self { + if self.cameraState.isCollageEnabled { + if let asset = result as? PHAsset { + if asset.mediaType == .video && asset.duration > 1.0 { + self.node.collage?.addResult(.single(.asset(asset)), snapshotView: nil) + } else { + self.node.collage?.addResult( + assetImage(asset: asset, targetSize: CGSize(width: 1080, height: 1080), exact: false, deliveryMode: .highQualityFormat) + |> runOn(Queue.concurrentDefaultQueue()) + |> mapToSignal { image -> Signal in + if let image { + return .single(.image(Result.Image(image: image, additionalImage: nil, additionalImagePosition: .topLeft))) + } else { + return .complete() + } + }, + snapshotView: nil + ) + } + } + + dismissControllerImpl?() + } else { + stopCameraCapture() + + let resultTransition = ResultTransition( + sourceView: transitionView, + sourceRect: transitionRect, + sourceImage: transitionImage, + transitionOut: transitionOut + ) + if let asset = result as? PHAsset { + if asset.mediaType == .video && asset.duration < 1.0 { + let presentationData = self.context.sharedContext.currentPresentationData.with { $0 } + let alertController = textAlertController( + context: self.context, + forceTheme: defaultDarkColorPresentationTheme, + title: nil, + text: presentationData.strings.Story_Editor_VideoTooShort, + actions: [ + TextAlertAction(type: .defaultAction, title: presentationData.strings.Common_OK, action: {}) + ], + actionLayout: .vertical + ) + self.present(alertController, in: .window(.root)) + } else { + self.completion(.single(.asset(asset)), resultTransition, dismissed) + } + } else if let draft = result as? MediaEditorDraft { + self.completion(.single(.draft(draft)), resultTransition, dismissed) + } + } + } + }, multipleCompletion: { [weak self] results in + guard let self else { + return + } + + if !self.cameraState.isCollageEnabled { + var selectedGrid: Camera.CollageGrid = collageGrids.first! + for grid in collageGrids { + if grid.count == results.count { + selectedGrid = grid + break + } + } + self.updateCameraState({ + $0.updatedIsCollageEnabled(true).updatedCollageProgress(0.0).updatedIsDualCameraEnabled(false).updatedCollageGrid(selectedGrid) + }, transition: .spring(duration: 0.3)) + } + + if let assets = results as? [PHAsset] { + var results: [Signal] = [] + for asset in assets { if asset.mediaType == .video && asset.duration > 1.0 { - self.node.collage?.addResult(.single(.asset(asset)), snapshotView: nil) + results.append(.single(.asset(asset))) } else { - self.node.collage?.addResult( + results.append( assetImage(asset: asset, targetSize: CGSize(width: 1080, height: 1080), exact: false, deliveryMode: .highQualityFormat) |> runOn(Queue.concurrentDefaultQueue()) |> mapToSignal { image -> Signal in @@ -3465,55 +3683,25 @@ public class CameraScreenImpl: ViewController, CameraScreen { } else { return .complete() } - }, - snapshotView: nil + } ) } } - - - - dismissControllerImpl?() - } else { - stopCameraCapture() - - let resultTransition = ResultTransition( - sourceView: transitionView, - sourceRect: transitionRect, - sourceImage: transitionImage, - transitionOut: transitionOut - ) - if let asset = result as? PHAsset { - if asset.mediaType == .video && asset.duration < 1.0 { - let presentationData = self.context.sharedContext.currentPresentationData.with { $0 } - let alertController = textAlertController( - context: self.context, - forceTheme: defaultDarkColorPresentationTheme, - title: nil, - text: presentationData.strings.Story_Editor_VideoTooShort, - actions: [ - TextAlertAction(type: .defaultAction, title: presentationData.strings.Common_OK, action: {}) - ], - actionLayout: .vertical - ) - self.present(alertController, in: .window(.root)) - } else { - self.completion(.single(.asset(asset)), resultTransition, dismissed) - } - } else if let draft = result as? MediaEditorDraft { - self.completion(.single(.draft(draft)), resultTransition, dismissed) - } + self.node.collage?.addResults(signals: results) } + self.galleryController = nil + + dismissControllerImpl?() + }, dismissed: { [weak self] in + resumeCameraCapture() + if let self { + self.node.hasGallery = false + self.node.requestUpdateLayout(transition: .immediate) + } + }, groupsPresented: { + stopCameraCapture() } - }, dismissed: { [weak self] in - resumeCameraCapture() - if let self { - self.node.hasGallery = false - self.node.requestUpdateLayout(transition: .immediate) - } - }, groupsPresented: { - stopCameraCapture() - }) + ) self.galleryController = controller dismissControllerImpl = { [weak controller] in @@ -3557,7 +3745,7 @@ public class CameraScreenImpl: ViewController, CameraScreen { self.isDismissed = true if animated { self.ignoreStatusBar = true - if let layout = self.validLayout, layout.metrics.isTablet || self.mode == .sticker { + if let layout = self.validLayout, layout.metrics.isTablet || [.sticker, .avatar].contains(self.mode) { self.node.animateOut(completion: { self.dismiss(animated: false) self.transitionedOut() diff --git a/submodules/TelegramUI/Components/CameraScreen/Sources/CaptureControlsComponent.swift b/submodules/TelegramUI/Components/CameraScreen/Sources/CaptureControlsComponent.swift index 4266369a88..841d286538 100644 --- a/submodules/TelegramUI/Components/CameraScreen/Sources/CaptureControlsComponent.swift +++ b/submodules/TelegramUI/Components/CameraScreen/Sources/CaptureControlsComponent.swift @@ -3,10 +3,12 @@ import UIKit import Display import ComponentFlow import SwiftSignalKit +import TelegramCore import Photos import LocalMediaResources import CameraButtonComponent import UIKitRuntimeUtils +import AccountContext enum ShutterButtonState: Equatable { case disabled @@ -560,8 +562,10 @@ final class CaptureControlsComponent: Component { case flip } + let context: AccountContext let isTablet: Bool let isSticker: Bool + let hasGallery: Bool let hasAppeared: Bool let hasAccess: Bool let hideControls: Bool @@ -570,6 +574,7 @@ final class CaptureControlsComponent: Component { let tintColor: UIColor let shutterState: ShutterButtonState let lastGalleryAsset: PHAsset? + let resolvedCodePeer: EnginePeer? let tag: AnyObject? let galleryButtonTag: AnyObject? let shutterTapped: () -> Void @@ -581,10 +586,13 @@ final class CaptureControlsComponent: Component { let swipeHintUpdated: (SwipeHint) -> Void let zoomUpdated: (CGFloat) -> Void let flipAnimationAction: ActionSlot + let openResolvedPeer: (EnginePeer) -> Void init( + context: AccountContext, isTablet: Bool, isSticker: Bool, + hasGallery: Bool, hasAppeared: Bool, hasAccess: Bool, hideControls: Bool, @@ -593,6 +601,7 @@ final class CaptureControlsComponent: Component { tintColor: UIColor, shutterState: ShutterButtonState, lastGalleryAsset: PHAsset?, + resolvedCodePeer: EnginePeer?, tag: AnyObject?, galleryButtonTag: AnyObject?, shutterTapped: @escaping () -> Void, @@ -603,10 +612,13 @@ final class CaptureControlsComponent: Component { galleryTapped: @escaping () -> Void, swipeHintUpdated: @escaping (SwipeHint) -> Void, zoomUpdated: @escaping (CGFloat) -> Void, - flipAnimationAction: ActionSlot + flipAnimationAction: ActionSlot, + openResolvedPeer: @escaping (EnginePeer) -> Void ) { + self.context = context self.isTablet = isTablet self.isSticker = isSticker + self.hasGallery = hasGallery self.hasAppeared = hasAppeared self.hasAccess = hasAccess self.hideControls = hideControls @@ -615,6 +627,7 @@ final class CaptureControlsComponent: Component { self.tintColor = tintColor self.shutterState = shutterState self.lastGalleryAsset = lastGalleryAsset + self.resolvedCodePeer = resolvedCodePeer self.tag = tag self.galleryButtonTag = galleryButtonTag self.shutterTapped = shutterTapped @@ -626,15 +639,22 @@ final class CaptureControlsComponent: Component { self.swipeHintUpdated = swipeHintUpdated self.zoomUpdated = zoomUpdated self.flipAnimationAction = flipAnimationAction + self.openResolvedPeer = openResolvedPeer } static func ==(lhs: CaptureControlsComponent, rhs: CaptureControlsComponent) -> Bool { + if lhs.context !== rhs.context { + return false + } if lhs.isTablet != rhs.isTablet { return false } if lhs.isSticker != rhs.isSticker { return false } + if lhs.hasGallery != rhs.hasGallery { + return false + } if lhs.hasAppeared != rhs.hasAppeared { return false } @@ -659,6 +679,9 @@ final class CaptureControlsComponent: Component { if lhs.lastGalleryAsset?.localIdentifier != rhs.lastGalleryAsset?.localIdentifier { return false } + if lhs.resolvedCodePeer != rhs.resolvedCodePeer { + return false + } return true } @@ -710,6 +733,8 @@ final class CaptureControlsComponent: Component { private var state: State? private var availableSize: CGSize? + private var codeResultView: ComponentView? + private let zoomView = ComponentView() private let lockView = ComponentView() private let galleryButtonView = ComponentView() @@ -1050,7 +1075,7 @@ final class CaptureControlsComponent: Component { let galleryButtonFrame: CGRect let gallerySize: CGSize - if !component.isSticker { + if component.hasGallery { let galleryCornerRadius: CGFloat if component.isTablet { gallerySize = CGSize(width: 72.0, height: 72.0) @@ -1276,6 +1301,46 @@ final class CaptureControlsComponent: Component { } } + if let resolvedCodePeer = component.resolvedCodePeer { + let codeResultView: ComponentView + if let current = self.codeResultView { + codeResultView = current + } else { + codeResultView = ComponentView() + self.codeResultView = codeResultView + } + + let codeResultSize = codeResultView.update( + transition: .immediate, + component: AnyComponent( + CameraCodeResultComponent( + context: component.context, + peer: resolvedCodePeer, + pressed: component.openResolvedPeer + ) + ), + environment: {}, + containerSize: availableSize + ) + + if let view = codeResultView.view { + if view.superview == nil { + self.insertSubview(view, at: 0) + if let view = view as? CameraCodeResultComponent.View { + view.animateIn() + } + } + view.frame = CGRect(origin: CGPoint(x: (availableSize.width - codeResultSize.width) / 2.0, y: (size.height - shutterButtonSize.height) / 2.0 - codeResultSize.height), size: codeResultSize) + } + } else if let codeResultView = self.codeResultView { + self.codeResultView = nil + codeResultView.view?.layer.animateAlpha(from: 1.0, to: 0.0, duration: 0.25, removeOnCompletion: false, completion: { _ in + codeResultView.view?.removeFromSuperview() + }) + codeResultView.view?.layer.animateScale(from: 1.0, to: 0.2, duration: 0.25, removeOnCompletion: false) + codeResultView.view?.layer.animatePosition(from: .zero, to: CGPoint(x: 0.0, y: 64.0), duration: 0.4, timingFunction: kCAMediaTimingFunctionSpring, removeOnCompletion: false, additive: true) + } + let _ = self.lockView.update( transition: .immediate, component: AnyComponent( @@ -1353,6 +1418,13 @@ final class CaptureControlsComponent: Component { return size } + + override func hitTest(_ point: CGPoint, with event: UIEvent?) -> UIView? { + if let codeResultView = self.codeResultView?.view, codeResultView.frame.contains(point) { + return codeResultView.hitTest(self.convert(point, to: codeResultView), with: event) + } + return super.hitTest(point, with: event) + } } func makeView() -> View { diff --git a/submodules/TelegramUI/Components/Chat/ChatMessageBubbleItemNode/Sources/ChatMessageBubbleItemNode.swift b/submodules/TelegramUI/Components/Chat/ChatMessageBubbleItemNode/Sources/ChatMessageBubbleItemNode.swift index a9b3e6bde7..abb33902c2 100644 --- a/submodules/TelegramUI/Components/Chat/ChatMessageBubbleItemNode/Sources/ChatMessageBubbleItemNode.swift +++ b/submodules/TelegramUI/Components/Chat/ChatMessageBubbleItemNode/Sources/ChatMessageBubbleItemNode.swift @@ -228,10 +228,7 @@ private func contentNodeMessagesAndClassesForItem(_ item: ChatMessageItem) -> ([ result.append((message, ChatMessageJoinedChannelBubbleContentNode.self, itemAttributes, BubbleItemAttributes(isAttachment: false, neighborType: .text, neighborSpacing: .default))) needReactions = false } else { - switch action.action { - case .photoUpdated: - break - default: + if !canAddMessageReactions(message: message) { needReactions = false } result.append((message, ChatMessageActionBubbleContentNode.self, itemAttributes, BubbleItemAttributes(isAttachment: false, neighborType: .text, neighborSpacing: .default))) @@ -2717,7 +2714,7 @@ public class ChatMessageBubbleItemNode: ChatMessageItemView, ChatMessagePreviewI var maximumNodeWidth = maximumNodeWidth if hasInstantVideo { - maximumNodeWidth = min(309, baseWidth - 84) + maximumNodeWidth = min(309.0, baseWidth - 84.0) } let (minWidth, buttonsLayout) = reactionButtonsLayout(ChatMessageReactionButtonsNode.Arguments( context: item.context, @@ -3055,7 +3052,7 @@ public class ChatMessageBubbleItemNode: ChatMessageItemView, ChatMessagePreviewI if let reactionButtonsFinalize = reactionButtonsFinalize { var maxContentWidth = maxContentWidth if hasInstantVideo { - maxContentWidth = min(310, baseWidth - 84.0) + maxContentWidth = min(310.0, baseWidth - 84.0) } reactionButtonsSizeAndApply = reactionButtonsFinalize(maxContentWidth) } @@ -3185,7 +3182,8 @@ public class ChatMessageBubbleItemNode: ChatMessageItemView, ChatMessagePreviewI shareButtonOffset: shareButtonOffset, avatarOffset: avatarOffset, hidesHeaders: hidesHeaders, - disablesComments: disablesComments + disablesComments: disablesComments, + alignment: alignment ) }) } @@ -3244,7 +3242,8 @@ public class ChatMessageBubbleItemNode: ChatMessageItemView, ChatMessagePreviewI shareButtonOffset: CGPoint?, avatarOffset: CGFloat?, hidesHeaders: Bool, - disablesComments: Bool + disablesComments: Bool, + alignment: ChatMessageBubbleContentAlignment ) -> Void { guard let strongSelf = selfReference.value else { return @@ -4367,7 +4366,14 @@ public class ChatMessageBubbleItemNode: ChatMessageItemView, ChatMessagePreviewI if let reactionButtonsSizeAndApply = reactionButtonsSizeAndApply { let reactionButtonsNode = reactionButtonsSizeAndApply.1(animation) - var reactionButtonsFrame = CGRect(origin: CGPoint(x: backgroundFrame.minX + (incoming ? (layoutConstants.bubble.contentInsets.left + 2.0) : (layoutConstants.bubble.contentInsets.right - 2.0)), y: backgroundFrame.maxY + reactionButtonsOffset + 4.0), size: reactionButtonsSizeAndApply.0) + + var reactionButtonsOriginX: CGFloat + if case .center = alignment { + reactionButtonsOriginX = backgroundFrame.minX + 3.0 + } else { + reactionButtonsOriginX = backgroundFrame.minX + (incoming ? (layoutConstants.bubble.contentInsets.left + 2.0) : (layoutConstants.bubble.contentInsets.right - 2.0)) + } + var reactionButtonsFrame = CGRect(origin: CGPoint(x: reactionButtonsOriginX, y: backgroundFrame.maxY + reactionButtonsOffset + 4.0), size: reactionButtonsSizeAndApply.0) if !disablesComments && !incoming { reactionButtonsFrame.origin.x = backgroundFrame.maxX - reactionButtonsSizeAndApply.0.width - layoutConstants.bubble.contentInsets.left } diff --git a/submodules/TelegramUI/Components/Chat/ChatMessageCommentFooterContentNode/Sources/ChatMessageCommentFooterContentNode.swift b/submodules/TelegramUI/Components/Chat/ChatMessageCommentFooterContentNode/Sources/ChatMessageCommentFooterContentNode.swift index 926429e4a9..5bd1a66e68 100644 --- a/submodules/TelegramUI/Components/Chat/ChatMessageCommentFooterContentNode/Sources/ChatMessageCommentFooterContentNode.swift +++ b/submodules/TelegramUI/Components/Chat/ChatMessageCommentFooterContentNode/Sources/ChatMessageCommentFooterContentNode.swift @@ -219,8 +219,8 @@ public final class ChatMessageCommentFooterContentNode: ChatMessageBubbleContent let textInsets = UIEdgeInsets()//(top: 2.0, left: 2.0, bottom: 5.0, right: 2.0) - let (countLayout, countApply) = makeCountLayout(textConstrainedSize, rawSegments) - let (alternativeCountLayout, alternativeCountApply) = makeAlternativeCountLayout(textConstrainedSize, rawAlternativeSegments) + let (countLayout, countApply) = makeCountLayout(textConstrainedSize, .zero, rawSegments) + let (alternativeCountLayout, alternativeCountApply) = makeAlternativeCountLayout(textConstrainedSize, .zero, rawAlternativeSegments) var textFrame = CGRect(origin: CGPoint(x: -textInsets.left + textLeftInset - 2.0, y: -textInsets.top + 5.0 + topOffset), size: countLayout.size) var textFrameWithoutInsets = CGRect(origin: CGPoint(x: textFrame.origin.x + textInsets.left, y: textFrame.origin.y + textInsets.top), size: CGSize(width: textFrame.width - textInsets.left - textInsets.right, height: textFrame.height - textInsets.top - textInsets.bottom)) diff --git a/submodules/TelegramUI/Components/Chat/ChatMessageGiftBubbleContentNode/Sources/ChatMessageGiftBubbleContentNode.swift b/submodules/TelegramUI/Components/Chat/ChatMessageGiftBubbleContentNode/Sources/ChatMessageGiftBubbleContentNode.swift index 2c93fa33a5..b3d2892a6d 100644 --- a/submodules/TelegramUI/Components/Chat/ChatMessageGiftBubbleContentNode/Sources/ChatMessageGiftBubbleContentNode.swift +++ b/submodules/TelegramUI/Components/Chat/ChatMessageGiftBubbleContentNode/Sources/ChatMessageGiftBubbleContentNode.swift @@ -442,7 +442,11 @@ public class ChatMessageGiftBubbleContentNode: ChatMessageBubbleContentNode { text = item.presentationData.strings.Notification_StarGift_Bot_Subtitle } } else { - text = item.presentationData.strings.Notification_StarGift_Subtitle_Other(peerName, item.presentationData.strings.Notification_StarGift_Subtitle_Other_Stars(Int32(convertStars ?? 0))).string + let formattedString = item.presentationData.strings.Notification_StarGift_Subtitle_Other(peerName, item.presentationData.strings.Notification_StarGift_Subtitle_Other_Stars(Int32(convertStars ?? 0))) + text = formattedString.string + if let starsRange = formattedString.ranges.last { + entities.append(MessageTextEntity(range: starsRange.range.lowerBound ..< starsRange.range.upperBound, type: .Bold)) + } } } } diff --git a/submodules/TelegramUI/Components/Chat/ChatMessageItemCommon/Sources/ChatMessageItemCommon.swift b/submodules/TelegramUI/Components/Chat/ChatMessageItemCommon/Sources/ChatMessageItemCommon.swift index be8d60aaa3..05e9ee7afa 100644 --- a/submodules/TelegramUI/Components/Chat/ChatMessageItemCommon/Sources/ChatMessageItemCommon.swift +++ b/submodules/TelegramUI/Components/Chat/ChatMessageItemCommon/Sources/ChatMessageItemCommon.swift @@ -284,8 +284,17 @@ public func canAddMessageReactions(message: Message) -> Bool { return false } for media in message.media { - if let _ = media as? TelegramMediaAction { - return message.flags.contains(.ReactionsArePossible) + if let action = media as? TelegramMediaAction { + if message.flags.contains(.ReactionsArePossible) { + return true + } else { + switch action.action { + case .unknown, .groupCreated, .channelMigratedFromGroup, .groupMigratedToChannel, .historyCleared, .customText, .botDomainAccessGranted, .botAppAccessGranted, .botSentSecureValues, .phoneNumberRequest, .webViewData, .topicCreated, .attachMenuBotAllowed, .requestedPeer: + return false + default: + return true + } + } } else if let story = media as? TelegramMediaStory { if story.isMention { return false diff --git a/submodules/TelegramUI/Components/Chat/ChatSendStarsScreen/BUILD b/submodules/TelegramUI/Components/Chat/ChatSendStarsScreen/BUILD index 1cbadf2f42..a76f46670b 100644 --- a/submodules/TelegramUI/Components/Chat/ChatSendStarsScreen/BUILD +++ b/submodules/TelegramUI/Components/Chat/ChatSendStarsScreen/BUILD @@ -29,6 +29,7 @@ swift_library( "//submodules/TelegramUI/Components/EmojiStatusComponent", "//submodules/TelegramUI/Components/SliderComponent", "//submodules/TelegramUI/Components/Utils/RoundedRectWithTailPath", + "//submodules/TelegramUI/Components/CheckComponent", "//submodules/AvatarNode", "//submodules/Components/BundleIconComponent", "//submodules/CheckNode", diff --git a/submodules/TelegramUI/Components/Chat/ChatSendStarsScreen/Sources/ChatSendStarsScreen.swift b/submodules/TelegramUI/Components/Chat/ChatSendStarsScreen/Sources/ChatSendStarsScreen.swift index 9a9d574d72..aa3ec3194d 100644 --- a/submodules/TelegramUI/Components/Chat/ChatSendStarsScreen/Sources/ChatSendStarsScreen.swift +++ b/submodules/TelegramUI/Components/Chat/ChatSendStarsScreen/Sources/ChatSendStarsScreen.swift @@ -20,6 +20,7 @@ import AvatarNode import BundleIconComponent import CheckNode import TextFormat +import CheckComponent private final class BalanceComponent: CombinedComponent { let context: AccountContext @@ -2569,97 +2570,3 @@ private final class SliderStarsView: UIView { self.emitterLayer.emitterSize = size } } - -private final class CheckComponent: Component { - struct Theme: Equatable { - public let backgroundColor: UIColor - public let strokeColor: UIColor - public let borderColor: UIColor - public let overlayBorder: Bool - public let hasInset: Bool - public let hasShadow: Bool - public let filledBorder: Bool - public let borderWidth: CGFloat? - - public init(backgroundColor: UIColor, strokeColor: UIColor, borderColor: UIColor, overlayBorder: Bool, hasInset: Bool, hasShadow: Bool, filledBorder: Bool = false, borderWidth: CGFloat? = nil) { - self.backgroundColor = backgroundColor - self.strokeColor = strokeColor - self.borderColor = borderColor - self.overlayBorder = overlayBorder - self.hasInset = hasInset - self.hasShadow = hasShadow - self.filledBorder = filledBorder - self.borderWidth = borderWidth - } - - var checkNodeTheme: CheckNodeTheme { - return CheckNodeTheme( - backgroundColor: self.backgroundColor, - strokeColor: self.strokeColor, - borderColor: self.borderColor, - overlayBorder: self.overlayBorder, - hasInset: self.hasInset, - hasShadow: self.hasShadow, - filledBorder: self.filledBorder, - borderWidth: self.borderWidth - ) - } - } - - let theme: Theme - let selected: Bool - - init( - theme: Theme, - selected: Bool - ) { - self.theme = theme - self.selected = selected - } - - static func ==(lhs: CheckComponent, rhs: CheckComponent) -> Bool { - if lhs.theme != rhs.theme { - return false - } - if lhs.selected != rhs.selected { - return false - } - return true - } - - final class View: UIView { - private var currentValue: CGFloat? - private var animator: DisplayLinkAnimator? - - private var checkLayer: CheckLayer { - return self.layer as! CheckLayer - } - - override class var layerClass: AnyClass { - return CheckLayer.self - } - - init() { - super.init(frame: CGRect()) - } - - required init?(coder aDecoder: NSCoder) { - preconditionFailure() - } - - func update(component: CheckComponent, availableSize: CGSize, transition: ComponentTransition) -> CGSize { - self.checkLayer.setSelected(component.selected, animated: true) - self.checkLayer.theme = component.theme.checkNodeTheme - - return CGSize(width: 22.0, height: 22.0) - } - } - - func makeView() -> View { - return View() - } - - func update(view: View, availableSize: CGSize, state: EmptyComponentState, environment: Environment, transition: ComponentTransition) -> CGSize { - return view.update(component: self, availableSize: availableSize, transition: transition) - } -} diff --git a/submodules/TelegramUI/Components/ChatEntityKeyboardInputNode/BUILD b/submodules/TelegramUI/Components/ChatEntityKeyboardInputNode/BUILD index 59374b8162..9258ec1822 100644 --- a/submodules/TelegramUI/Components/ChatEntityKeyboardInputNode/BUILD +++ b/submodules/TelegramUI/Components/ChatEntityKeyboardInputNode/BUILD @@ -29,7 +29,7 @@ swift_library( "//submodules/PremiumUI:PremiumUI", "//submodules/UndoUI:UndoUI", "//submodules/ContextUI:ContextUI", - "//submodules/GalleryUI:GalleryUI", + "//submodules/StickerResources", "//submodules/TelegramPresentationData:TelegramPresentationData", "//submodules/TelegramNotices:TelegramNotices", "//submodules/StickerPeekUI:StickerPeekUI", @@ -37,7 +37,6 @@ swift_library( "//submodules/TelegramUI/Components/MultiplexedVideoNode:MultiplexedVideoNode", "//submodules/TelegramUI/Components/ChatControllerInteraction:ChatControllerInteraction", "//submodules/FeaturedStickersScreen:FeaturedStickersScreen", - "//submodules/StickerPackPreviewUI", "//submodules/TelegramUI/Components/EntityKeyboardGifContent:EntityKeyboardGifContent", "//submodules/TelegramUI/Components/LegacyMessageInputPanelInputView:LegacyMessageInputPanelInputView", "//submodules/AttachmentTextInputPanelNode", diff --git a/submodules/TelegramUI/Components/ChatEntityKeyboardInputNode/Sources/ChatEntityKeyboardInputNode.swift b/submodules/TelegramUI/Components/ChatEntityKeyboardInputNode/Sources/ChatEntityKeyboardInputNode.swift index 0a3349ff6b..88fed4e4ab 100644 --- a/submodules/TelegramUI/Components/ChatEntityKeyboardInputNode/Sources/ChatEntityKeyboardInputNode.swift +++ b/submodules/TelegramUI/Components/ChatEntityKeyboardInputNode/Sources/ChatEntityKeyboardInputNode.swift @@ -19,7 +19,6 @@ import PremiumUI import AudioToolbox import UndoUI import ContextUI -import GalleryUI import TelegramPresentationData import TelegramNotices import StickerPeekUI @@ -29,7 +28,6 @@ import MultiplexedVideoNode import ChatControllerInteraction import FeaturedStickersScreen import Pasteboard -import StickerPackPreviewUI import EntityKeyboardGifContent import LegacyMessageInputPanelInputView import AttachmentTextInputPanelNode @@ -647,25 +645,6 @@ public final class ChatEntityKeyboardInputNode: ChatInputNode { parentInputInteraction: emojiInputInteraction )) } - - /*let controller = StickerPackScreen( - context: context, - updatedPresentationData: controllerInteraction.updatedPresentationData, - mode: .default, - mainStickerPack: .id(id: featuredStickerPack.info.id.id, accessHash: featuredStickerPack.info.accessHash), - stickerPacks: [.id(id: featuredStickerPack.info.id.id, accessHash: featuredStickerPack.info.accessHash)], - loadedStickerPacks: [.result(info: featuredStickerPack.info, items: featuredStickerPack.topItems, installed: false)], - parentNavigationController: controllerInteraction.navigationController(), - sendSticker: nil, - sendEmoji: { [weak interfaceInteraction] text, emojiAttribute in - guard let interfaceInteraction else { - return - } - interfaceInteraction.insertText(NSAttributedString(string: text, attributes: [ChatTextInputAttributes.customEmoji: emojiAttribute])) - } - ) - controllerInteraction.presentController(controller, nil)*/ - break } } @@ -1395,6 +1374,7 @@ public final class ChatEntityKeyboardInputNode: ChatInputNode { mainStickerPack: packReference, stickerPacks: [packReference], loadedStickerPacks: [], + actionTitle: nil, isEditing: true, expandIfNeeded: true, parentNavigationController: interaction.getNavigationController(), @@ -2131,9 +2111,7 @@ public final class ChatEntityKeyboardInputNode: ChatInputNode { let message = Message(stableId: 0, stableVersion: 0, id: MessageId(peerId: PeerId(0), namespace: Namespaces.Message.Local, id: 0), globallyUniqueId: nil, groupingKey: nil, groupInfo: nil, threadId: nil, timestamp: 0, flags: [], tags: [], globalTags: [], localTags: [], customTags: [], forwardInfo: nil, author: nil, text: "", attributes: [], media: [file.media], peers: SimpleDictionary(), associatedMessages: SimpleDictionary(), associatedMessageIds: [], associatedMedia: [:], associatedThreadInfo: nil, associatedStories: [:]) - let gallery = GalleryController(context: strongSelf.context, source: .standaloneMessage(message, nil), streamSingleVideo: true, replaceRootController: { _, _ in - }, baseNavigationController: nil) - gallery.setHintWillBePresentedInPreviewingContext(true) + let gallery = strongSelf.context.sharedContext.makeGalleryController(context: strongSelf.context, source: .standaloneMessage(message, nil), streamSingleVideo: true, isPreview: true) var items: [ContextMenuItem] = [] items.append(.action(ContextMenuActionItem(text: presentationData.strings.MediaPicker_Send, icon: { theme in @@ -2282,7 +2260,7 @@ private final class ContextControllerContentSourceImpl: ContextControllerContent } func animatedIn() { - if let controller = self.controller as? GalleryController { + if let controller = self.controller as? GalleryControllerProtocol { controller.viewDidAppear(false) } } @@ -2883,7 +2861,7 @@ public final class EmojiContentPeekBehaviorImpl: EmojiContentPeekBehavior { return } - let controller = strongSelf.context.sharedContext.makeStickerPackScreen(context: context, updatedPresentationData: nil, mainStickerPack: packReference, stickerPacks: [packReference], loadedStickerPacks: [], isEditing: false, expandIfNeeded: false, parentNavigationController: interaction.navigationController(), sendSticker: { file, sourceView, sourceRect in + let controller = strongSelf.context.sharedContext.makeStickerPackScreen(context: context, updatedPresentationData: nil, mainStickerPack: packReference, stickerPacks: [packReference], loadedStickerPacks: [], actionTitle: nil, isEditing: false, expandIfNeeded: false, parentNavigationController: interaction.navigationController(), sendSticker: { file, sourceView, sourceRect in sendSticker(file, false, false, nil, false, sourceView, sourceRect, nil) return true }, actionPerformed: nil) diff --git a/submodules/TelegramUI/Components/ChatEntityKeyboardInputNode/Sources/StickerPaneSearchContentNode.swift b/submodules/TelegramUI/Components/ChatEntityKeyboardInputNode/Sources/StickerPaneSearchContentNode.swift index ec10321a04..23230d48ba 100644 --- a/submodules/TelegramUI/Components/ChatEntityKeyboardInputNode/Sources/StickerPaneSearchContentNode.swift +++ b/submodules/TelegramUI/Components/ChatEntityKeyboardInputNode/Sources/StickerPaneSearchContentNode.swift @@ -10,7 +10,6 @@ import PresentationDataUtils import LegacyComponents import MergeLists import AccountContext -import StickerPackPreviewUI import StickerPeekUI import Emoji import AppBundle @@ -19,6 +18,7 @@ import UndoUI import ChatControllerInteraction import FeaturedStickersScreen import ChatPresentationInterfaceState +import StickerResources private enum StickerSearchEntryId: Equatable, Hashable { case sticker(String?, Int64) @@ -224,13 +224,25 @@ final class StickerPaneSearchContentNode: ASDisplayNode, PaneSearchContentNode { let presentationData = strongSelf.context.sharedContext.currentPresentationData.with { $0 }.withUpdated(theme: theme) - let controller = StickerPackScreen(context: strongSelf.context, updatedPresentationData: (presentationData, .single(presentationData)), mainStickerPack: packReference, stickerPacks: [packReference], actionTitle: stickerActionTitle, parentNavigationController: strongSelf.interaction.getNavigationController(), sendSticker: { [weak self] fileReference, sourceNode, sourceRect in - if let strongSelf = self { - return strongSelf.interaction.sendSticker(fileReference, false, false, nil, false, sourceNode, sourceRect, nil, []) - } else { - return false - } - }) + let controller = strongSelf.context.sharedContext.makeStickerPackScreen( + context: strongSelf.context, + updatedPresentationData: (presentationData, .single(presentationData)), + mainStickerPack: packReference, + stickerPacks: [packReference], + loadedStickerPacks: [], + actionTitle: stickerActionTitle, + isEditing: false, + expandIfNeeded: false, + parentNavigationController: strongSelf.interaction.getNavigationController(), + sendSticker: { [weak self] fileReference, sourceView, sourceRect in + if let strongSelf = self { + return strongSelf.interaction.sendSticker(fileReference, false, false, nil, false, sourceView, sourceRect, nil, []) + } else { + return false + } + }, + actionPerformed: nil + ) strongSelf.interaction.presentController(controller, nil) } }, install: { [weak self] info, items, install in diff --git a/submodules/TelegramUI/Components/ChatTitleView/Sources/ChatTitleView.swift b/submodules/TelegramUI/Components/ChatTitleView/Sources/ChatTitleView.swift index 5d07518d22..7d20299d03 100644 --- a/submodules/TelegramUI/Components/ChatTitleView/Sources/ChatTitleView.swift +++ b/submodules/TelegramUI/Components/ChatTitleView/Sources/ChatTitleView.swift @@ -277,9 +277,11 @@ public final class ChatTitleView: UIView, NavigationBarTitleView { } titleCredibilityIcon = .emojiStatus(emojiStatus) } else if peer.isVerified { - titleCredibilityIcon = .verified + titleVerifiedIcon = .verified } else if peer.isPremium && !premiumConfiguration.isPremiumDisabled { titleCredibilityIcon = .premium + } else if let verification = peer.verification { + titleVerifiedIcon = .emojiStatus(PeerEmojiStatus(fileId: verification.iconFileId, expirationDate: nil)) } } } @@ -399,7 +401,7 @@ public final class ChatTitleView: UIView, NavigationBarTitleView { } updated = true } - + if titleCredibilityIcon != self.titleCredibilityIcon { self.titleCredibilityIcon = titleCredibilityIcon updated = true @@ -906,9 +908,15 @@ public final class ChatTitleView: UIView, NavigationBarTitleView { let titleSideInset: CGFloat = 6.0 var titleFrame: CGRect if size.height > 40.0 { - var titleSize = self.titleTextNode.updateLayout(size: CGSize(width: clearBounds.width - leftIconWidth - credibilityIconWidth - verifiedIconWidth - rightIconWidth - titleSideInset * 2.0, height: size.height), animated: titleTransition.isAnimated) + var titleInsets: UIEdgeInsets = .zero + if verifiedIconWidth > 0.0 { + titleInsets.left = verifiedIconWidth + 2.0 + } + + var titleSize = self.titleTextNode.updateLayout(size: CGSize(width: clearBounds.width - leftIconWidth - credibilityIconWidth - verifiedIconWidth - rightIconWidth - titleSideInset * 2.0, height: size.height), insets: titleInsets, animated: titleTransition.isAnimated) titleSize.width += credibilityIconWidth titleSize.width += verifiedIconWidth + let activitySize = self.activityNode.updateLayout(CGSize(width: clearBounds.size.width - titleSideInset * 2.0, height: clearBounds.size.height), alignment: .center) let titleInfoSpacing: CGFloat = 0.0 @@ -943,11 +951,7 @@ public final class ChatTitleView: UIView, NavigationBarTitleView { var nextIconX: CGFloat = titleFrame.width - self.titleVerifiedIconView.frame = CGRect(origin: CGPoint(x: titleFrame.width - titleVerifiedSize.width, y: floor((titleFrame.height - titleVerifiedSize.height) / 2.0)), size: titleVerifiedSize) - nextIconX -= titleVerifiedSize.width - if !titleVerifiedSize.width.isZero { - nextIconX -= 2.0 - } + self.titleVerifiedIconView.frame = CGRect(origin: CGPoint(x: 0.0, y: floor((titleFrame.height - titleVerifiedSize.height) / 2.0)), size: titleVerifiedSize) self.titleCredibilityIconView.frame = CGRect(origin: CGPoint(x: nextIconX - titleCredibilitySize.width, y: floor((titleFrame.height - titleCredibilitySize.height) / 2.0)), size: titleCredibilitySize) nextIconX -= titleCredibilitySize.width @@ -975,11 +979,7 @@ public final class ChatTitleView: UIView, NavigationBarTitleView { var nextIconX: CGFloat = titleFrame.maxX - self.titleVerifiedIconView.frame = CGRect(origin: CGPoint(x: nextIconX - titleVerifiedSize.width, y: floor((titleFrame.height - titleVerifiedSize.height) / 2.0)), size: titleVerifiedSize) - nextIconX -= titleVerifiedSize.width - if !titleVerifiedSize.width.isZero { - nextIconX -= 2.0 - } + self.titleVerifiedIconView.frame = CGRect(origin: CGPoint(x: 0.0, y: floor((titleFrame.height - titleVerifiedSize.height) / 2.0)), size: titleVerifiedSize) self.titleCredibilityIconView.frame = CGRect(origin: CGPoint(x: nextIconX - titleCredibilitySize.width, y: floor((titleFrame.height - titleCredibilitySize.height) / 2.0)), size: titleCredibilitySize) nextIconX -= titleCredibilitySize.width diff --git a/submodules/TelegramUI/Components/CheckComponent/BUILD b/submodules/TelegramUI/Components/CheckComponent/BUILD new file mode 100644 index 0000000000..9e410d0404 --- /dev/null +++ b/submodules/TelegramUI/Components/CheckComponent/BUILD @@ -0,0 +1,21 @@ +load("@build_bazel_rules_swift//swift:swift.bzl", "swift_library") + +swift_library( + name = "CheckComponent", + module_name = "CheckComponent", + srcs = glob([ + "Sources/**/*.swift", + ]), + copts = [ + "-warnings-as-errors", + ], + deps = [ + "//submodules/AsyncDisplayKit:AsyncDisplayKit", + "//submodules/Display:Display", + "//submodules/ComponentFlow:ComponentFlow", + "//submodules/CheckNode:CheckNode", + ], + visibility = [ + "//visibility:public", + ], +) diff --git a/submodules/TelegramUI/Components/CheckComponent/Sources/CheckComponent.swift b/submodules/TelegramUI/Components/CheckComponent/Sources/CheckComponent.swift new file mode 100644 index 0000000000..33db3b4933 --- /dev/null +++ b/submodules/TelegramUI/Components/CheckComponent/Sources/CheckComponent.swift @@ -0,0 +1,105 @@ +import Foundation +import UIKit +import Display +import ComponentFlow +import CheckNode + +public final class CheckComponent: Component { + public struct Theme: Equatable { + public let backgroundColor: UIColor + public let strokeColor: UIColor + public let borderColor: UIColor + public let overlayBorder: Bool + public let hasInset: Bool + public let hasShadow: Bool + public let filledBorder: Bool + public let borderWidth: CGFloat? + + public init(backgroundColor: UIColor, strokeColor: UIColor, borderColor: UIColor, overlayBorder: Bool, hasInset: Bool, hasShadow: Bool, filledBorder: Bool = false, borderWidth: CGFloat? = nil) { + self.backgroundColor = backgroundColor + self.strokeColor = strokeColor + self.borderColor = borderColor + self.overlayBorder = overlayBorder + self.hasInset = hasInset + self.hasShadow = hasShadow + self.filledBorder = filledBorder + self.borderWidth = borderWidth + } + + var checkNodeTheme: CheckNodeTheme { + return CheckNodeTheme( + backgroundColor: self.backgroundColor, + strokeColor: self.strokeColor, + borderColor: self.borderColor, + overlayBorder: self.overlayBorder, + hasInset: self.hasInset, + hasShadow: self.hasShadow, + filledBorder: self.filledBorder, + borderWidth: self.borderWidth + ) + } + } + + let theme: Theme + let size: CGSize + let selected: Bool + + public init( + theme: Theme, + size: CGSize = CGSize(width: 22.0, height: 22.0), + selected: Bool + ) { + self.theme = theme + self.size = size + self.selected = selected + } + + public static func ==(lhs: CheckComponent, rhs: CheckComponent) -> Bool { + if lhs.theme != rhs.theme { + return false + } + if lhs.size != rhs.size { + return false + } + if lhs.selected != rhs.selected { + return false + } + return true + } + + public final class View: UIView { + private var currentValue: CGFloat? + private var animator: DisplayLinkAnimator? + + private var checkLayer: CheckLayer { + return self.layer as! CheckLayer + } + + public override class var layerClass: AnyClass { + return CheckLayer.self + } + + init() { + super.init(frame: CGRect()) + } + + required init?(coder aDecoder: NSCoder) { + preconditionFailure() + } + + public func update(component: CheckComponent, availableSize: CGSize, transition: ComponentTransition) -> CGSize { + self.checkLayer.setSelected(component.selected, animated: true) + self.checkLayer.theme = component.theme.checkNodeTheme + + return component.size + } + } + + public func makeView() -> View { + return View() + } + + public func update(view: View, availableSize: CGSize, state: EmptyComponentState, environment: Environment, transition: ComponentTransition) -> CGSize { + return view.update(component: self, availableSize: availableSize, transition: transition) + } +} diff --git a/submodules/TelegramUI/Components/EmojiStatusComponent/Sources/EmojiStatusComponent.swift b/submodules/TelegramUI/Components/EmojiStatusComponent/Sources/EmojiStatusComponent.swift index b8a2912289..8976049463 100644 --- a/submodules/TelegramUI/Components/EmojiStatusComponent/Sources/EmojiStatusComponent.swift +++ b/submodules/TelegramUI/Components/EmojiStatusComponent/Sources/EmojiStatusComponent.swift @@ -299,7 +299,7 @@ public final class EmojiStatusComponent: Component { imageNamePrefix = "Peer Info/VerifiedIcon" } - if let backgroundImage = UIImage(bundleImageName: "\(imageNamePrefix)Background"), let foregroundImage = UIImage(bundleImageName: "\(imageNamePrefix)Foreground") { + if let backgroundImage = UIImage(bundleImageName: "\(imageNamePrefix)Background"), let foregroundImage = UIImage(bundleImageName: "Share/QrPlaneIcon") { iconImage = generateImage(backgroundImage.size, contextGenerator: { size, context in if let backgroundCgImage = backgroundImage.cgImage, let foregroundCgImage = foregroundImage.cgImage { context.clear(CGRect(origin: CGPoint(), size: size)) diff --git a/submodules/TelegramUI/Components/EmojiTextAttachmentView/Sources/EmojiTextAttachmentView.swift b/submodules/TelegramUI/Components/EmojiTextAttachmentView/Sources/EmojiTextAttachmentView.swift index b3b89b68ef..dd5215544e 100644 --- a/submodules/TelegramUI/Components/EmojiTextAttachmentView/Sources/EmojiTextAttachmentView.swift +++ b/submodules/TelegramUI/Components/EmojiTextAttachmentView/Sources/EmojiTextAttachmentView.swift @@ -473,6 +473,9 @@ public final class InlineStickerItemLayer: MultiAnimationRenderTarget { self.updateTon() case let .animation(name): self.updateLocalAnimation(name: name, attemptSynchronousLoad: attemptSynchronousLoad) + case .verification: + self.updateVerification() + self.updateTintColor() } } else if let file = file { self.updateFile(file: file, attemptSynchronousLoad: attemptSynchronousLoad) @@ -558,8 +561,13 @@ public final class InlineStickerItemLayer: MultiAnimationRenderTarget { if file.isCustomTemplateEmoji { customColor = self.dynamicColor } - } else if let emoji = self.arguments?.emoji, let custom = emoji.custom, case .stars = custom { - customColor = self.dynamicColor + } else if let emoji = self.arguments?.emoji, let custom = emoji.custom { + switch custom { + case .stars(true), .verification: + customColor = self.dynamicColor + default: + break + } } if customColor != nil { @@ -662,6 +670,10 @@ public final class InlineStickerItemLayer: MultiAnimationRenderTarget { self.contents = tonImage?.cgImage } + private func updateVerification() { + self.contents = verificationImage?.cgImage + } + private func updateLocalAnimation(name: String, attemptSynchronousLoad: Bool) { guard let arguments = self.arguments else { return @@ -992,7 +1004,6 @@ private let tintedStarImage: UIImage? = { })?.withRenderingMode(.alwaysTemplate) }() - private let starImage: UIImage? = { generateImage(CGSize(width: 32.0, height: 32.0), contextGenerator: { size, context in context.clear(CGRect(origin: .zero, size: size)) @@ -1012,3 +1023,28 @@ private let tonImage: UIImage? = { } })?.withRenderingMode(.alwaysTemplate) }() + +private let verificationImage: UIImage? = { + if let backgroundImage = UIImage(bundleImageName: "Peer Info/VerifiedIconBackground"), let foregroundImage = UIImage(bundleImageName: "Share/QrPlaneIcon") { + return generateImage(backgroundImage.size, contextGenerator: { size, context in + let fittedRect = CGRect(origin: .zero, size: size).insetBy(dx: 2.0 + UIScreenPixel, dy: 2.0 + UIScreenPixel) + if let backgroundCgImage = backgroundImage.cgImage, let foregroundCgImage = foregroundImage.cgImage { + context.clear(CGRect(origin: CGPoint(), size: size)) + + context.saveGState() + context.clip(to: fittedRect, mask: backgroundCgImage) + + context.setFillColor(UIColor.white.cgColor) + context.fill(CGRect(origin: CGPoint(), size: size)) + context.restoreGState() + + context.clip(to: fittedRect, mask: foregroundCgImage) + context.setBlendMode(.clear) + context.setFillColor(UIColor.clear.cgColor) + context.fill(CGRect(origin: CGPoint(), size: size)) + } + }, opaque: false)?.withRenderingMode(.alwaysTemplate) + } else { + return nil + } +}() diff --git a/submodules/TelegramUI/Components/MediaEditor/Sources/MediaEditor.swift b/submodules/TelegramUI/Components/MediaEditor/Sources/MediaEditor.swift index ce4e02034f..38d839bb1c 100644 --- a/submodules/TelegramUI/Components/MediaEditor/Sources/MediaEditor.swift +++ b/submodules/TelegramUI/Components/MediaEditor/Sources/MediaEditor.swift @@ -119,6 +119,7 @@ public final class MediaEditor { public enum Mode { case `default` case sticker + case avatar } public enum Subject { @@ -130,6 +131,8 @@ public final class MediaEditor { } public let content: Content public let frame: CGRect + public let contentScale: CGFloat + public let contentOffset: CGPoint var isVideo: Bool { return self.duration > 0.0 @@ -146,9 +149,16 @@ public final class MediaEditor { } } - public init(content: Content, frame: CGRect) { + public init( + content: Content, + frame: CGRect, + contentScale: CGFloat, + contentOffset: CGPoint + ) { self.content = content self.frame = frame + self.contentScale = contentScale + self.contentOffset = contentOffset } } @@ -599,7 +609,7 @@ public final class MediaEditor { self.renderer.videoFinishPass.additionalTextureRotation = .rotate0DegreesMirrored } let hasTransparency = imageHasTransparency(image) - self.renderer.consume(main: .texture(texture, time, hasTransparency, nil), additionals: additionalTexture.flatMap { [.texture($0, time, false, nil)] } ?? [], render: true, displayEnabled: false) + self.renderer.consume(main: .texture(texture, time, hasTransparency, nil, 1.0, .zero), additionals: additionalTexture.flatMap { [.texture($0, time, false, nil, 1.0, .zero)] } ?? [], render: true, displayEnabled: false) } private func setupSource(andPlay: Bool) { @@ -619,6 +629,8 @@ public final class MediaEditor { let stickerEntity: MediaEditorComposerStickerEntity? let playerIsReference: Bool let rect: CGRect? + let scale: CGFloat + let offset: CGPoint let gradientColors: GradientColors init( @@ -628,6 +640,8 @@ public final class MediaEditor { stickerEntity: MediaEditorComposerStickerEntity? = nil, playerIsReference: Bool = false, rect: CGRect? = nil, + scale: CGFloat = 1.0, + offset: CGPoint = .zero, gradientColors: GradientColors ) { self.image = image @@ -636,11 +650,13 @@ public final class MediaEditor { self.stickerEntity = stickerEntity self.playerIsReference = playerIsReference self.rect = rect + self.scale = scale + self.offset = offset self.gradientColors = gradientColors } } - func textureSourceResult(for asset: AVAsset, gradientColors: GradientColors? = nil, rect: CGRect? = nil) -> Signal { + func textureSourceResult(for asset: AVAsset, gradientColors: GradientColors? = nil, rect: CGRect? = nil, scale: CGFloat = 1.0, offset: CGPoint = .zero) -> Signal { return Signal { [weak self] subscriber in guard let self else { subscriber.putCompletion() @@ -648,7 +664,13 @@ public final class MediaEditor { } let player = self.makePlayer(asset: asset) if let gradientColors { - subscriber.putNext(TextureSourceResult(player: player, rect: rect, gradientColors: gradientColors)) + subscriber.putNext(TextureSourceResult( + player: player, + rect: rect, + scale: scale, + offset: offset, + gradientColors: gradientColors + )) subscriber.putCompletion() return EmptyDisposable } else { @@ -657,7 +679,13 @@ public final class MediaEditor { imageGenerator.maximumSize = CGSize(width: 72, height: 128) imageGenerator.generateCGImagesAsynchronously(forTimes: [NSValue(time: CMTime(seconds: 0, preferredTimescale: CMTimeScale(30.0)))]) { _, image, _, _, _ in let gradientColors: GradientColors = image.flatMap({ mediaEditorGetGradientColors(from: UIImage(cgImage: $0)) }) ?? GradientColors(top: .black, bottom: .black) - subscriber.putNext(TextureSourceResult(player: player, rect: rect, gradientColors: gradientColors)) + subscriber.putNext(TextureSourceResult( + player: player, + rect: rect, + scale: scale, + offset: offset, + gradientColors: gradientColors + )) subscriber.putCompletion() } return ActionDisposable { @@ -667,7 +695,7 @@ public final class MediaEditor { } } - func textureSourceResult(for asset: PHAsset, rect: CGRect? = nil) -> Signal { + func textureSourceResult(for asset: PHAsset, rect: CGRect? = nil, scale: CGFloat = 1.0, offset: CGPoint = .zero) -> Signal { return Signal { [weak self] subscriber in let isVideo = asset.mediaType == .video @@ -700,6 +728,8 @@ public final class MediaEditor { TextureSourceResult( player: player, rect: rect, + scale: scale, + offset: offset, gradientColors: mediaEditorGetGradientColors(from: image) ) ) @@ -712,6 +742,8 @@ public final class MediaEditor { TextureSourceResult( image: image, rect: rect, + scale: scale, + offset: offset, gradientColors: mediaEditorGetGradientColors(from: image) ) ) @@ -764,9 +796,9 @@ public final class MediaEditor { switch longestItem.content { case let .video(path, _): let asset = AVURLAsset(url: URL(fileURLWithPath: path)) - textureSource = textureSourceResult(for: asset, rect: longestItem.frame) + textureSource = textureSourceResult(for: asset, rect: longestItem.frame, scale: longestItem.contentScale, offset: longestItem.contentOffset) case let .asset(asset): - textureSource = textureSourceResult(for: asset, rect: longestItem.frame) + textureSource = textureSourceResult(for: asset, rect: longestItem.frame, scale: longestItem.contentScale, offset: longestItem.contentOffset) default: textureSource = .complete() } @@ -838,9 +870,9 @@ public final class MediaEditor { if let image = textureSourceResult.image { if self.values.nightTheme, let nightImage = textureSourceResult.nightImage { - textureSource.setMainInput(.image(nightImage, nil)) + textureSource.setMainInput(.image(nightImage, nil, 1.0, .zero)) } else { - textureSource.setMainInput(.image(image, nil)) + textureSource.setMainInput(.image(image, nil, 1.0, .zero)) } if case .sticker = self.mode { @@ -880,10 +912,10 @@ public final class MediaEditor { } } if let player = self.player, let playerItem = player.currentItem, !textureSourceResult.playerIsReference { - textureSource.setMainInput(.video(playerItem, textureSourceResult.rect)) + textureSource.setMainInput(.video(playerItem, textureSourceResult.rect, textureSourceResult.scale, textureSourceResult.offset)) } if self.values.collage.isEmpty, let additionalPlayer = self.additionalPlayers.first, let playerItem = additionalPlayer.currentItem { - textureSource.setAdditionalInputs([.video(playerItem, nil)]) + textureSource.setAdditionalInputs([.video(playerItem, nil, 1.0, .zero)]) } if let entity = textureSourceResult.stickerEntity { textureSource.setMainInput(.entity(entity)) @@ -898,7 +930,7 @@ public final class MediaEditor { switch self.mode { case .default: self.setGradientColors(textureSourceResult.gradientColors) - case .sticker: + case .sticker, .avatar: self.setGradientColors(GradientColors(top: .clear, bottom: .clear)) } @@ -1202,9 +1234,9 @@ public final class MediaEditor { if let textureSource = self.renderer.textureSource as? UniversalTextureSource { if nightTheme { - textureSource.setMainInput(.image(nightImage, nil)) + textureSource.setMainInput(.image(nightImage, nil, 1.0, .zero)) } else { - textureSource.setMainInput(.image(dayImage, nil)) + textureSource.setMainInput(.image(dayImage, nil, 1.0, .zero)) } } } @@ -1221,6 +1253,10 @@ public final class MediaEditor { public var onPlaybackAction: (PlaybackAction) -> Void = { _ in } + public var currentPosition: CMTime { + return self.player?.currentTime() ?? .zero + } + private var initialSeekPosition: Double? private var targetTimePosition: (CMTime, Bool)? private var updatingTimePosition = false @@ -1716,6 +1752,8 @@ public final class MediaEditor { let item = MediaEditorValues.VideoCollageItem( content: content, frame: item.frame, + contentScale: item.contentScale, + contentOffset: item.contentOffset, videoTrimRange: 0 ..< item.duration, videoOffset: nil, videoVolume: passedFirstVideo ? 0.0 : nil @@ -1736,7 +1774,9 @@ public final class MediaEditor { } if mainVideoIsMuted { - self.setVideoVolume(0.0) + Queue.mainQueue().after(0.3) { + self.setVideoVolume(0.0) + } } self.setupAdditionalVideoPlayback() @@ -1795,13 +1835,13 @@ public final class MediaEditor { break case let .imageFile(path): if let image = UIImage(contentsOfFile: path) { - signals.append(.single((.image(image, item.frame), nil, nil))) + signals.append(.single((.image(image, item.frame, item.contentScale, item.contentOffset), nil, nil))) } case let .videoFile(path): let asset = AVURLAsset(url: URL(fileURLWithPath: path)) let player = self.makePlayer(asset: asset) if let playerItem = player.currentItem { - signals.append(.single((.video(playerItem, item.frame), player, item.videoVolume))) + signals.append(.single((.video(playerItem, item.frame, item.contentScale, item.contentOffset), player, item.videoVolume))) } case let .asset(localIdentifier, _): let fetchResult = PHAsset.fetchAssets(withLocalIdentifiers: [localIdentifier], options: nil) @@ -1819,7 +1859,7 @@ public final class MediaEditor { } let player = self.makePlayer(asset: avAsset) if let playerItem = player.currentItem { - subscriber.putNext((.video(playerItem, item.frame), player, item.videoVolume)) + subscriber.putNext((.video(playerItem, item.frame, item.contentScale, item.contentOffset), player, item.videoVolume)) } subscriber.putCompletion() }) @@ -1890,7 +1930,7 @@ public final class MediaEditor { self.additionalPlayersPromise.set(.single([player])) self.additionalPlayerAudioMixes = [audioMix] - (self.renderer.textureSource as? UniversalTextureSource)?.setAdditionalInputs([.video(playerItem, nil)]) + (self.renderer.textureSource as? UniversalTextureSource)?.setAdditionalInputs([.video(playerItem, nil, 1.0, .zero)]) } } diff --git a/submodules/TelegramUI/Components/MediaEditor/Sources/MediaEditorComposer.swift b/submodules/TelegramUI/Components/MediaEditor/Sources/MediaEditorComposer.swift index 782fa83366..1a837bf841 100644 --- a/submodules/TelegramUI/Components/MediaEditor/Sources/MediaEditorComposer.swift +++ b/submodules/TelegramUI/Components/MediaEditor/Sources/MediaEditorComposer.swift @@ -50,17 +50,25 @@ private func roundedCornersMaskImage(size: CGSize) -> CIImage { return CIImage(cgImage: image!) } +private func rectangleMaskImage(size: CGSize) -> CIImage { + let image = generateImage(size, opaque: true, scale: 1.0) { size, context in + context.setFillColor(UIColor.white.cgColor) + context.fill(CGRect(origin: .zero, size: size)) + }?.cgImage + return CIImage(cgImage: image!) +} + final class MediaEditorComposer { enum Input { - case texture(MTLTexture, CMTime, Bool, CGRect?) - case videoBuffer(VideoPixelBuffer, CGRect?) + case texture(MTLTexture, CMTime, Bool, CGRect?, CGFloat, CGPoint) + case videoBuffer(VideoPixelBuffer, CGRect?, CGFloat, CGPoint) case ciImage(CIImage, CMTime) var timestamp: CMTime { switch self { - case let .texture(_, timestamp, _, _): + case let .texture(_, timestamp, _, _, _, _): return timestamp - case let .videoBuffer(videoBuffer, _): + case let .videoBuffer(videoBuffer, _, _, _): return videoBuffer.timestamp case let .ciImage(_, timestamp): return timestamp @@ -69,10 +77,10 @@ final class MediaEditorComposer { var rendererInput: MediaEditorRenderer.Input { switch self { - case let .texture(texture, timestamp, hasTransparency, rect): - return .texture(texture, timestamp, hasTransparency, rect) - case let .videoBuffer(videoBuffer, rect): - return .videoBuffer(videoBuffer, rect) + case let .texture(texture, timestamp, hasTransparency, rect, scale, offset): + return .texture(texture, timestamp, hasTransparency, rect, scale, offset) + case let .videoBuffer(videoBuffer, rect, scale, offset): + return .videoBuffer(videoBuffer, rect, scale, offset) case let .ciImage(image, timestamp): return .ciImage(image, timestamp) } @@ -216,6 +224,8 @@ public func makeEditorImageComposition(context: CIContext, postbox: Postbox, inp var maskImage: CIImage? if values.isSticker { maskImage = roundedCornersMaskImage(size: CGSize(width: floor(1080.0 * 0.97), height: floor(1080.0 * 0.97))) + } else if let _ = outputDimensions { + maskImage = rectangleMaskImage(size: CGSize(width: 1080.0, height: 1080.0)) } if let drawing = values.drawing, let image = CIImage(image: drawing, options: [.colorSpace: colorSpace]) { @@ -285,7 +295,7 @@ private func makeEditorImageFrameComposition(context: CIContext, inputImage: CII } resultImage = resultImage.transformed(by: CGAffineTransform(translationX: dimensions.width / 2.0, y: dimensions.height / 2.0)) - if values.isSticker { + if values.isSticker || values.isAvatar { let minSize = min(dimensions.width, dimensions.height) let scaledSize = CGSize(width: floor(minSize * 0.97), height: floor(minSize * 0.97)) resultImage = resultImage.transformed(by: CGAffineTransform(translationX: -(dimensions.width - scaledSize.width) / 2.0, y: -(dimensions.height - scaledSize.height) / 2.0)).cropped(to: CGRect(origin: .zero, size: scaledSize)) diff --git a/submodules/TelegramUI/Components/MediaEditor/Sources/MediaEditorRenderer.swift b/submodules/TelegramUI/Components/MediaEditor/Sources/MediaEditorRenderer.swift index 3892925a85..4792af7622 100644 --- a/submodules/TelegramUI/Components/MediaEditor/Sources/MediaEditorRenderer.swift +++ b/submodules/TelegramUI/Components/MediaEditor/Sources/MediaEditorRenderer.swift @@ -59,15 +59,15 @@ protocol RenderTarget: AnyObject { final class MediaEditorRenderer { enum Input { - case texture(MTLTexture, CMTime, Bool, CGRect?) - case videoBuffer(VideoPixelBuffer, CGRect?) + case texture(MTLTexture, CMTime, Bool, CGRect?, CGFloat, CGPoint) + case videoBuffer(VideoPixelBuffer, CGRect?, CGFloat, CGPoint) case ciImage(CIImage, CMTime) var timestamp: CMTime { switch self { - case let .texture(_, timestamp, _, _): + case let .texture(_, timestamp, _, _, _, _): return timestamp - case let .videoBuffer(videoBuffer, _): + case let .videoBuffer(videoBuffer, _, _, _): return videoBuffer.timestamp case let .ciImage(_, timestamp): return timestamp @@ -193,17 +193,17 @@ final class MediaEditorRenderer { func textureFromInput(_ input: MediaEditorRenderer.Input, videoInputPass: VideoInputPass) -> VideoFinishPass.Input? { switch input { - case let .texture(texture, _, hasTransparency, rect): - return VideoFinishPass.Input(texture: texture, hasTransparency: hasTransparency, rect: rect) - case let .videoBuffer(videoBuffer, rect): + case let .texture(texture, _, hasTransparency, rect, scale, offset): + return VideoFinishPass.Input(texture: texture, hasTransparency: hasTransparency, rect: rect, scale: scale, offset: offset) + case let .videoBuffer(videoBuffer, rect, scale, offset): if let texture = videoInputPass.processPixelBuffer(videoBuffer, textureCache: textureCache, device: device, commandBuffer: commandBuffer) { - return VideoFinishPass.Input(texture: texture, hasTransparency: false, rect: rect) + return VideoFinishPass.Input(texture: texture, hasTransparency: false, rect: rect, scale: scale, offset: offset) } else { return nil } case let .ciImage(image, _): if let texture = self.ciInputPass.processCIImage(image, device: device, commandBuffer: commandBuffer) { - return VideoFinishPass.Input(texture: texture, hasTransparency: true, rect: nil) + return VideoFinishPass.Input(texture: texture, hasTransparency: true, rect: nil, scale: 1.0, offset: .zero) } else { return nil } diff --git a/submodules/TelegramUI/Components/MediaEditor/Sources/MediaEditorValues.swift b/submodules/TelegramUI/Components/MediaEditor/Sources/MediaEditorValues.swift index 5fbfe5950a..b287ed2d45 100644 --- a/submodules/TelegramUI/Components/MediaEditor/Sources/MediaEditorValues.swift +++ b/submodules/TelegramUI/Components/MediaEditor/Sources/MediaEditorValues.swift @@ -418,6 +418,8 @@ public final class MediaEditorValues: Codable, Equatable { case contentValue case isVideo case frame + case contentScale + case contentOffset case videoTrimRange case videoOffset case videoVolume @@ -441,6 +443,8 @@ public final class MediaEditorValues: Codable, Equatable { public let content: Content public let frame: CGRect + public let contentScale: CGFloat + public let contentOffset: CGPoint public let videoTrimRange: Range? public let videoOffset: Double? @@ -449,12 +453,16 @@ public final class MediaEditorValues: Codable, Equatable { public init( content: Content, frame: CGRect, + contentScale: CGFloat, + contentOffset: CGPoint, videoTrimRange: Range?, videoOffset: Double?, videoVolume: CGFloat? ) { self.content = content self.frame = frame + self.contentScale = contentScale + self.contentOffset = contentOffset self.videoTrimRange = videoTrimRange self.videoOffset = videoOffset self.videoVolume = videoVolume @@ -475,6 +483,10 @@ public final class MediaEditorValues: Codable, Equatable { throw DecodingError.generic } self.frame = try container.decode(CGRect.self, forKey: .frame) + + self.contentScale = try container.decodeIfPresent(CGFloat.self, forKey: .contentScale) ?? 1.0 + self.contentOffset = try container.decodeIfPresent(CGPoint.self, forKey: .contentOffset) ?? .zero + self.videoTrimRange = try container.decodeIfPresent(Range.self, forKey: .videoTrimRange) self.videoOffset = try container.decodeIfPresent(Double.self, forKey: .videoOffset) self.videoVolume = try container.decodeIfPresent(CGFloat.self, forKey: .videoVolume) @@ -497,6 +509,8 @@ public final class MediaEditorValues: Codable, Equatable { try container.encode(isVideo, forKey: .isVideo) } try container.encode(self.frame, forKey: .frame) + try container.encode(self.contentScale, forKey: .contentScale) + try container.encode(self.contentOffset, forKey: .contentOffset) try container.encodeIfPresent(self.videoTrimRange, forKey: .videoTrimRange) try container.encodeIfPresent(self.videoOffset, forKey: .videoOffset) try container.encodeIfPresent(self.videoVolume, forKey: .videoVolume) @@ -506,6 +520,8 @@ public final class MediaEditorValues: Codable, Equatable { return VideoCollageItem( content: self.content, frame: self.frame, + contentScale: self.contentScale, + contentOffset: self.contentOffset, videoTrimRange: videoTrimRange, videoOffset: self.videoOffset, videoVolume: self.videoVolume @@ -516,6 +532,8 @@ public final class MediaEditorValues: Codable, Equatable { return VideoCollageItem( content: self.content, frame: self.frame, + contentScale: self.contentScale, + contentOffset: self.contentOffset, videoTrimRange: self.videoTrimRange, videoOffset: videoOffset, videoVolume: self.videoVolume @@ -526,6 +544,8 @@ public final class MediaEditorValues: Codable, Equatable { return VideoCollageItem( content: self.content, frame: self.frame, + contentScale: self.contentScale, + contentOffset: self.contentOffset, videoTrimRange: self.videoTrimRange, videoOffset: self.videoOffset, videoVolume: videoVolume @@ -590,6 +610,10 @@ public final class MediaEditorValues: Codable, Equatable { return self.qualityPreset == .sticker } + var isAvatar: Bool { + return [.profile].contains(self.qualityPreset) + } + public var cropValues: (offset: CGPoint, rotation: CGFloat, scale: CGFloat) { return (self.cropOffset, self.cropRotation, self.cropScale) } diff --git a/submodules/TelegramUI/Components/MediaEditor/Sources/MediaEditorVideoExport.swift b/submodules/TelegramUI/Components/MediaEditor/Sources/MediaEditorVideoExport.swift index dc05f0e4c0..4e6fb85b34 100644 --- a/submodules/TelegramUI/Components/MediaEditor/Sources/MediaEditorVideoExport.swift +++ b/submodules/TelegramUI/Components/MediaEditor/Sources/MediaEditorVideoExport.swift @@ -184,9 +184,12 @@ public final class MediaEditorVideoExport { private var reader: AVAssetReader? private var videoOutput: AVAssetReaderOutput? private var textureRotation: TextureRotation = .rotate0Degrees - private var videoRect: CGRect? private var frameRate: Float? + private var mainVideoRect: CGRect? + private var mainVideoScale: CGFloat = 1.0 + private var mainVideoOffset: CGPoint = .zero + class VideoOutput { enum Output { case videoOutput(AVAssetReaderOutput) @@ -194,6 +197,8 @@ public final class MediaEditorVideoExport { } let output: Output let rect: CGRect? + let scale: CGFloat + let offset: CGPoint let textureRotation: TextureRotation let duration: Double let frameRate: Float @@ -202,6 +207,8 @@ public final class MediaEditorVideoExport { init( output: Output, rect: CGRect?, + scale: CGFloat, + offset: CGPoint, textureRotation: TextureRotation, duration: Double, frameRate: Float, @@ -209,6 +216,8 @@ public final class MediaEditorVideoExport { ) { self.output = output self.rect = rect + self.scale = scale + self.offset = offset self.textureRotation = textureRotation self.duration = duration self.frameRate = frameRate @@ -275,8 +284,8 @@ public final class MediaEditorVideoExport { } enum Input { - case image(image: UIImage, rect: CGRect?) - case video(asset: AVAsset, rect: CGRect?, rotation: TextureRotation, duration: Double, trimRange: Range?, offset: Double?, volume: CGFloat?) + case image(image: UIImage, rect: CGRect?, scale: CGFloat, offset: CGPoint) + case video(asset: AVAsset, rect: CGRect?, scale: CGFloat, offset: CGPoint, rotation: TextureRotation, duration: Double, trimRange: Range?, trimOffset: Double?, volume: CGFloat?) case sticker(TelegramMediaFile) var isVideo: Bool { @@ -293,19 +302,23 @@ public final class MediaEditorVideoExport { var signals: [Signal] = [] var mainRect: CGRect? + var mainScale: CGFloat = 1.0 + var mainOffset: CGPoint = .zero var additionalAsset: AVAsset? if !self.configuration.values.collage.isEmpty { for item in self.configuration.values.collage { switch item.content { case .main: mainRect = item.frame + mainScale = item.contentScale + mainOffset = item.contentOffset case let .imageFile(path): if let image = UIImage(contentsOfFile: path) { - signals.append(.single(.image(image: image, rect: item.frame))) + signals.append(.single(.image(image: image, rect: item.frame, scale: item.contentScale, offset: item.contentOffset))) } case let .videoFile(path): let asset = AVURLAsset(url: URL(fileURLWithPath: path)) - signals.append(.single(.video(asset: asset, rect: item.frame, rotation: textureRotatonForAVAsset(asset, mirror: false), duration: asset.duration.seconds, trimRange: item.videoTrimRange, offset: item.videoOffset, volume: item.videoVolume))) + signals.append(.single(.video(asset: asset, rect: item.frame, scale: item.contentScale, offset: item.contentOffset, rotation: textureRotatonForAVAsset(asset, mirror: false), duration: asset.duration.seconds, trimRange: item.videoTrimRange, trimOffset: item.videoOffset, volume: item.videoVolume))) case let .asset(localIdentifier, _): let fetchResult = PHAsset.fetchAssets(withLocalIdentifiers: [localIdentifier], options: nil) if fetchResult.count != 0 { @@ -321,7 +334,7 @@ public final class MediaEditorVideoExport { subscriber.putCompletion() return } - subscriber.putNext(.video(asset: avAsset, rect: item.frame, rotation: textureRotatonForAVAsset(avAsset, mirror: false), duration: avAsset.duration.seconds, trimRange: item.videoTrimRange, offset: item.videoOffset, volume: item.videoVolume)) + subscriber.putNext(.video(asset: avAsset, rect: item.frame, scale: item.contentScale, offset: item.contentOffset, rotation: textureRotatonForAVAsset(avAsset, mirror: false), duration: avAsset.duration.seconds, trimRange: item.videoTrimRange, trimOffset: item.videoOffset, volume: item.videoVolume)) subscriber.putCompletion() }) @@ -335,7 +348,7 @@ public final class MediaEditorVideoExport { } else if let additionalPath = self.configuration.values.additionalVideoPath { let asset = AVURLAsset(url: URL(fileURLWithPath: additionalPath)) additionalAsset = asset - signals = [.single(.video(asset: asset, rect: nil, rotation: textureRotatonForAVAsset(asset, mirror: true), duration: asset.duration.seconds, trimRange: nil, offset: nil, volume: nil))] + signals = [.single(.video(asset: asset, rect: nil, scale: 1.0, offset: .zero, rotation: textureRotatonForAVAsset(asset, mirror: true), duration: asset.duration.seconds, trimRange: nil, trimOffset: nil, volume: nil))] } var audioAsset: AVAsset? @@ -349,10 +362,10 @@ public final class MediaEditorVideoExport { switch self.subject { case let .video(asset, isStoryValue): mainAsset = asset - mainInput = .video(asset: asset, rect: mainRect, rotation: textureRotatonForAVAsset(asset), duration: asset.duration.seconds, trimRange: nil, offset: nil, volume: nil) + mainInput = .video(asset: asset, rect: mainRect, scale: mainScale, offset: mainOffset, rotation: textureRotatonForAVAsset(asset), duration: asset.duration.seconds, trimRange: nil, trimOffset: nil, volume: nil) isStory = isStoryValue case let .image(image): - mainInput = .image(image: image, rect: nil) + mainInput = .image(image: image, rect: nil, scale: 1.0, offset: .zero) case let .sticker(file): mainInput = .sticker(file) } @@ -436,8 +449,8 @@ public final class MediaEditorVideoExport { } enum AdditionalTrack { - case image(image: UIImage, rect: CGRect?) - case video(track: AVMutableCompositionTrack, rect: CGRect?, rotation: TextureRotation, duration: Double, frameRate: Float, startTime: CMTime?) + case image(image: UIImage, rect: CGRect?, scale: CGFloat, offset: CGPoint) + case video(track: AVMutableCompositionTrack, rect: CGRect?, scale: CGFloat, offset: CGPoint, rotation: TextureRotation, duration: Double, frameRate: Float, startTime: CMTime?) } func frameRate(for track: AVCompositionTrack) -> Float { @@ -471,8 +484,10 @@ public final class MediaEditorVideoExport { } var readerRange = wholeRange - if case let .video(asset, rect, rotation, _, _, _, _) = main { - self.videoRect = rect + if case let .video(asset, rect, scale, offset, rotation, _, _, _, _) = main { + self.mainVideoRect = rect + self.mainVideoScale = scale + self.mainVideoOffset = offset self.textureRotation = rotation if let videoAssetTrack = asset.tracks(withMediaType: .video).first { if let compositionTrack = composition?.addMutableTrack(withMediaType: .video, preferredTrackID: kCMPersistentTrackID_Invalid) { @@ -502,15 +517,15 @@ public final class MediaEditorVideoExport { if !self.configuration.values.collage.isEmpty { for input in additional { switch input { - case let .image(image, rect): - additionalTracks.append(.image(image: image, rect: rect)) - case let .video(asset, rect, rotation, duration, trimRange, offset, volume): - let startTime = videoStartTime(trimRange: trimRange, offset: offset) + case let .image(image, rect, scale, offset): + additionalTracks.append(.image(image: image, rect: rect, scale: scale, offset: offset)) + case let .video(asset, rect, scale, offset, rotation, duration, trimRange, trimOffset, volume): + let startTime = videoStartTime(trimRange: trimRange, offset: trimOffset) let timeRange = clampedRange(trackDuration: asset.duration, trackTrimRange: videoTimeRange(trimRange: trimRange), trackStart: startTime, maxDuration: readerRange.end) if let videoAssetTrack = asset.tracks(withMediaType: .video).first { if let compositionTrack = composition?.addMutableTrack(withMediaType: .video, preferredTrackID: kCMPersistentTrackID_Invalid) { - additionalTracks.append(.video(track: compositionTrack, rect: rect, rotation: rotation, duration: duration, frameRate: frameRate(for: compositionTrack), startTime: startTime)) + additionalTracks.append(.video(track: compositionTrack, rect: rect, scale: scale, offset: offset, rotation: rotation, duration: duration, frameRate: frameRate(for: compositionTrack), startTime: startTime)) compositionTrack.preferredTransform = videoAssetTrack.preferredTransform @@ -533,7 +548,7 @@ public final class MediaEditorVideoExport { break } } - } else if let additional = additional.first, case let .video(asset, _, rotation, duration, _, _, _) = additional { + } else if let additional = additional.first, case let .video(asset, _, _, _, rotation, duration, _, _, _) = additional { let startTime: CMTime let timeRange: CMTimeRange if mainVideoTrack == nil { @@ -546,7 +561,7 @@ public final class MediaEditorVideoExport { if let videoAssetTrack = asset.tracks(withMediaType: .video).first { if let compositionTrack = composition?.addMutableTrack(withMediaType: .video, preferredTrackID: kCMPersistentTrackID_Invalid) { - additionalTracks.append(.video(track: compositionTrack, rect: nil, rotation: rotation, duration: duration, frameRate: frameRate(for: compositionTrack), startTime: self.configuration.additionalVideoStartTime)) + additionalTracks.append(.video(track: compositionTrack, rect: nil, scale: 1.0, offset: .zero, rotation: rotation, duration: duration, frameRate: frameRate(for: compositionTrack), startTime: self.configuration.additionalVideoStartTime)) compositionTrack.preferredTransform = videoAssetTrack.preferredTransform @@ -644,16 +659,18 @@ public final class MediaEditorVideoExport { var additionalIndex = 0 for track in additionalTracks { switch track { - case let .image(image, rect): + case let .image(image, rect, scale, offset): self.additionalVideoOutput[additionalIndex] = VideoOutput( output: .image(image), rect: rect, + scale: scale, + offset: offset, textureRotation: .rotate0Degrees, duration: 0.0, frameRate: 0.0, startTime: .zero ) - case let .video(track, rect, rotation, duration, frameRate, startTime): + case let .video(track, rect, scale, offset, rotation, duration, frameRate, startTime): let videoOutput = AVAssetReaderTrackOutput(track: track, outputSettings: outputSettings) videoOutput.alwaysCopiesSampleData = true if reader.canAdd(videoOutput) { @@ -666,6 +683,8 @@ public final class MediaEditorVideoExport { self.additionalVideoOutput[additionalIndex] = VideoOutput( output: .videoOutput(videoOutput), rect: rect, + scale: scale, + offset: offset, textureRotation: rotation, duration: duration, frameRate: frameRate, @@ -740,11 +759,16 @@ public final class MediaEditorVideoExport { if let pixelBuffer = CMSampleBufferGetImageBuffer(sampleBuffer) { let timestamp = CMSampleBufferGetPresentationTimeStamp(sampleBuffer) mainTimestamp = timestamp - mainInput = .videoBuffer(VideoPixelBuffer( - pixelBuffer: pixelBuffer, - rotation: self.textureRotation, - timestamp: timestamp - ), self.videoRect) + mainInput = .videoBuffer( + VideoPixelBuffer( + pixelBuffer: pixelBuffer, + rotation: self.textureRotation, + timestamp: timestamp + ), + self.mainVideoRect, + self.mainVideoScale, + self.mainVideoOffset + ) if let duration = self.durationValue { let startTime = self.reader?.timeRange.start.seconds ?? 0.0 @@ -767,11 +791,18 @@ public final class MediaEditorVideoExport { if case let .videoOutput(videoOutput) = additionalVideoOutput.output { if let _ = videoOutput.copyNextSampleBuffer(), let sampleBuffer = videoOutput.copyNextSampleBuffer() { if let pixelBuffer = CMSampleBufferGetImageBuffer(sampleBuffer) { - additionalInput.append(.videoBuffer(VideoPixelBuffer( - pixelBuffer: pixelBuffer, - rotation: additionalVideoOutput.textureRotation, - timestamp: .zero - ), additionalVideoOutput.rect)) + additionalInput.append( + .videoBuffer( + VideoPixelBuffer( + pixelBuffer: pixelBuffer, + rotation: additionalVideoOutput.textureRotation, + timestamp: .zero + ), + additionalVideoOutput.rect, + additionalVideoOutput.scale, + additionalVideoOutput.offset + ) + ) } else { additionalInput.append(nil) } @@ -792,17 +823,33 @@ public final class MediaEditorVideoExport { switch additionalVideoOutput.output { case let .image(image): if let texture = self.composer?.textureForImage(index: i, image: image) { - additionalInput.append(.texture(texture, .zero, false, additionalVideoOutput.rect)) + additionalInput.append( + .texture( + texture, + .zero, + false, + additionalVideoOutput.rect, + additionalVideoOutput.scale, + additionalVideoOutput.offset + ) + ) } case let .videoOutput(videoOutput): if let sampleBuffer = videoOutput.copyNextSampleBuffer() { if let pixelBuffer = CMSampleBufferGetImageBuffer(sampleBuffer) { let timestamp = CMSampleBufferGetPresentationTimeStamp(sampleBuffer) - additionalInput.append(.videoBuffer(VideoPixelBuffer( - pixelBuffer: pixelBuffer, - rotation: additionalVideoOutput.textureRotation, - timestamp: timestamp - ), additionalVideoOutput.rect)) + additionalInput.append( + .videoBuffer( + VideoPixelBuffer( + pixelBuffer: pixelBuffer, + rotation: additionalVideoOutput.textureRotation, + timestamp: timestamp + ), + additionalVideoOutput.rect, + additionalVideoOutput.scale, + additionalVideoOutput.offset + ) + ) if !updatedProgress, let duration = self.durationValue { let startTime = self.reader?.timeRange.start.seconds ?? 0.0 @@ -830,7 +877,7 @@ public final class MediaEditorVideoExport { if case let .image(image) = self.subject, let texture = self.composer?.textureForImage(index: -1, image: image) { - mainInput = .texture(texture, self.imageArguments?.position ?? .zero, imageHasTransparency(image), nil) + mainInput = .texture(texture, self.imageArguments?.position ?? .zero, imageHasTransparency(image), nil, 1.0, .zero) if !updatedProgress, let imageArguments = self.imageArguments, let duration = self.durationValue { let progress = imageArguments.position.seconds / duration.seconds diff --git a/submodules/TelegramUI/Components/MediaEditor/Sources/UniversalTextureSource.swift b/submodules/TelegramUI/Components/MediaEditor/Sources/UniversalTextureSource.swift index b9a2b9c19f..bf17eb05e1 100644 --- a/submodules/TelegramUI/Components/MediaEditor/Sources/UniversalTextureSource.swift +++ b/submodules/TelegramUI/Components/MediaEditor/Sources/UniversalTextureSource.swift @@ -7,8 +7,8 @@ import SwiftSignalKit final class UniversalTextureSource: TextureSource { enum Input { - case image(UIImage, CGRect?) - case video(AVPlayerItem, CGRect?) + case image(UIImage, CGRect?, CGFloat, CGPoint) + case video(AVPlayerItem, CGRect?, CGFloat, CGPoint) case entity(MediaEditorComposerEntity) fileprivate func createContext(renderTarget: RenderTarget, queue: DispatchQueue, additional: Bool) -> InputContext { @@ -48,7 +48,7 @@ final class UniversalTextureSource: TextureSource { } var mainImage: UIImage? { - if let mainInput = self.mainInputContext?.input, case let .image(image, _) = mainInput { + if let mainInput = self.mainInputContext?.input, case let .image(image, _, _, _) = mainInput { return image } return nil @@ -208,13 +208,17 @@ private class ImageInputContext: InputContext { private var texture: MTLTexture? private var hasTransparency = false fileprivate var rect: CGRect? + fileprivate var scale: CGFloat + fileprivate var offset: CGPoint init(input: Input, renderTarget: RenderTarget, queue: DispatchQueue) { - guard case let .image(image, rect) = input else { + guard case let .image(image, rect, scale, offset) = input else { fatalError() } self.input = input self.rect = rect + self.scale = scale + self.offset = offset if let device = renderTarget.mtlDevice { self.texture = loadTexture(image: image, device: device) } @@ -222,7 +226,7 @@ private class ImageInputContext: InputContext { } func output(time: Double) -> Output? { - return self.texture.flatMap { .texture($0, .zero, self.hasTransparency, self.rect) } + return self.texture.flatMap { .texture($0, .zero, self.hasTransparency, self.rect, self.scale, self.offset) } } func invalidate() { @@ -239,20 +243,24 @@ private class VideoInputContext: NSObject, InputContext, AVPlayerItemOutputPullD private var videoOutput: AVPlayerItemVideoOutput? private var textureRotation: TextureRotation = .rotate0Degrees fileprivate var rect: CGRect? + fileprivate var scale: CGFloat + fileprivate var offset: CGPoint var playerItem: AVPlayerItem { - guard case let .video(playerItem, _) = self.input else { + guard case let .video(playerItem, _, _, _) = self.input else { fatalError() } return playerItem } init(input: Input, renderTarget: RenderTarget, queue: DispatchQueue, additional: Bool) { - guard case let .video(_, rect) = input else { + guard case let .video(_, rect, scale, offset) = input else { fatalError() } self.input = input self.rect = rect + self.scale = scale + self.offset = offset super.init() @@ -291,7 +299,7 @@ private class VideoInputContext: NSObject, InputContext, AVPlayerItemOutputPullD if let pixelBuffer = videoOutput.copyPixelBuffer(forItemTime: requestTime, itemTimeForDisplay: &presentationTime) { videoPixelBuffer = VideoPixelBuffer(pixelBuffer: pixelBuffer, rotation: self.textureRotation, timestamp: presentationTime) } - return videoPixelBuffer.flatMap { .videoBuffer($0, self.rect) } + return videoPixelBuffer.flatMap { .videoBuffer($0, self.rect, self.scale, self.offset) } } func invalidate() { diff --git a/submodules/TelegramUI/Components/MediaEditor/Sources/VideoFinishPass.swift b/submodules/TelegramUI/Components/MediaEditor/Sources/VideoFinishPass.swift index c0b2df1182..efbc32684a 100644 --- a/submodules/TelegramUI/Components/MediaEditor/Sources/VideoFinishPass.swift +++ b/submodules/TelegramUI/Components/MediaEditor/Sources/VideoFinishPass.swift @@ -10,12 +10,13 @@ private func verticesData( position: CGPoint, size: CGSize, rotation: CGFloat, + mirror: Bool = false, z: Float = 0.0 ) -> [VertexData] { - let topLeft: simd_float2 - let topRight: simd_float2 - let bottomLeft: simd_float2 - let bottomRight: simd_float2 + var topLeft: simd_float2 + var topRight: simd_float2 + var bottomLeft: simd_float2 + var bottomRight: simd_float2 switch textureRotation { case .rotate0Degrees: @@ -50,6 +51,13 @@ private func verticesData( bottomRight = simd_float2(1.0, 1.0) } + if mirror { + topLeft = simd_float2(1.0 - topLeft.x, topLeft.y) + topRight = simd_float2(1.0 - topRight.x, topRight.y) + bottomLeft = simd_float2(1.0 - bottomLeft.x, bottomLeft.y) + bottomRight = simd_float2(1.0 - bottomRight.x, bottomRight.y) + } + let containerSize = CGSize(width: containerSize.width, height: containerSize.height) let angle = Float(.pi - rotation) @@ -111,6 +119,8 @@ private func verticesData( textureRotation: TextureRotation, containerSize: CGSize, textureRect: CGRect, + scale: simd_float1, + offset: simd_float2, z: Float = 0.0 ) -> [VertexData] { let textureRect = CGRect(origin: CGPoint(x: textureRect.origin.x, y: containerSize.height - textureRect.maxY ), size: textureRect.size) @@ -118,17 +128,24 @@ private func verticesData( let containerAspect = textureRect.width / textureRect.height let imageAspect = size.width / size.height - let texCoordScale: simd_float2 + var texCoordScale: simd_float2 if imageAspect > containerAspect { texCoordScale = simd_float2(Float(containerAspect / imageAspect), 1.0) } else { texCoordScale = simd_float2(1.0, Float(imageAspect / containerAspect)) } - let scaledTopLeft = simd_float2(0.5 - texCoordScale.x * 0.5, 0.5 + texCoordScale.y * 0.5) - let scaledTopRight = simd_float2(0.5 + texCoordScale.x * 0.5, 0.5 + texCoordScale.y * 0.5) - let scaledBottomLeft = simd_float2(0.5 - texCoordScale.x * 0.5, 0.5 - texCoordScale.y * 0.5) - let scaledBottomRight = simd_float2(0.5 + texCoordScale.x * 0.5, 0.5 - texCoordScale.y * 0.5) + let adjustedOffset = simd_float2( + offset.x / texCoordScale.x, + offset.y / texCoordScale.y + ) + + texCoordScale *= 1.0 / scale + + let scaledTopLeft = simd_float2(0.5 - texCoordScale.x * 0.5, 0.5 + texCoordScale.y * 0.5) - adjustedOffset + let scaledTopRight = simd_float2(0.5 + texCoordScale.x * 0.5, 0.5 + texCoordScale.y * 0.5) - adjustedOffset + let scaledBottomLeft = simd_float2(0.5 - texCoordScale.x * 0.5, 0.5 - texCoordScale.y * 0.5) - adjustedOffset + let scaledBottomRight = simd_float2(0.5 + texCoordScale.x * 0.5, 0.5 - texCoordScale.y * 0.5) - adjustedOffset let topLeft: simd_float2 let topRight: simd_float2 @@ -322,6 +339,8 @@ final class VideoFinishPass: RenderPass { texture: MTLTexture, textureRotation: TextureRotation, rect: CGRect, + scale: CGFloat, + offset: CGPoint, zPosition: Float, device: MTLDevice ) { @@ -333,6 +352,8 @@ final class VideoFinishPass: RenderPass { textureRotation: textureRotation, containerSize: containerSize, textureRect: rect, + scale: simd_float1(scale), + offset: simd_float2(Float(offset.x / scale), Float(-offset.y / scale)), z: zPosition ) let buffer = device.makeBuffer( @@ -387,6 +408,7 @@ final class VideoFinishPass: RenderPass { position: center, size: size, rotation: position.rotation, + mirror: position.mirroring, z: zPosition ) let buffer = device.makeBuffer( @@ -415,10 +437,10 @@ final class VideoFinishPass: RenderPass { self.isStory = values.isStory || values.isSticker self.isSticker = values.gradientColors?.first?.alpha == 0.0 - self.mainPosition = VideoFinishPass.VideoPosition(position: position, size: self.mainPosition.size, scale: values.cropScale, rotation: values.cropRotation, baseScale: self.mainPosition.baseScale) + self.mainPosition = VideoFinishPass.VideoPosition(position: position, size: self.mainPosition.size, scale: values.cropScale, rotation: values.cropRotation, mirroring: values.cropMirroring, baseScale: self.mainPosition.baseScale) if let position = values.additionalVideoPosition, let scale = values.additionalVideoScale, let rotation = values.additionalVideoRotation { - 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) + self.additionalPosition = VideoFinishPass.VideoPosition(position: position, size: CGSize(width: 1080.0 / 4.0, height: 1440.0 / 4.0), scale: scale, rotation: rotation, mirroring: false, baseScale: self.additionalPosition.baseScale) } if !values.additionalVideoPositionChanges.isEmpty { self.videoPositionChanges = values.additionalVideoPositionChanges @@ -451,6 +473,7 @@ final class VideoFinishPass: RenderPass { size: CGSize(width: 1080.0, height: 1920.0), scale: 1.0, rotation: 0.0, + mirroring: false, baseScale: 1.0 ) @@ -459,6 +482,7 @@ final class VideoFinishPass: RenderPass { size: CGSize(width: 1440.0, height: 1920.0), scale: 0.5, rotation: 0.0, + mirroring: false, baseScale: 1.0 ) @@ -482,10 +506,11 @@ final class VideoFinishPass: RenderPass { let size: CGSize let scale: CGFloat let rotation: CGFloat + let mirroring: Bool 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) + return VideoPosition(position: self.position, size: size, scale: self.scale, rotation: self.rotation, mirroring: self.mirroring, baseScale: baseScale) } func mixed(with other: VideoPosition, fraction: CGFloat) -> VideoPosition { @@ -505,6 +530,7 @@ final class VideoFinishPass: RenderPass { size: size, scale: scale, rotation: rotation, + mirroring: self.mirroring, baseScale: self.baseScale ) } @@ -558,13 +584,13 @@ final class VideoFinishPass: 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, 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) + mainPosition = VideoPosition(position: mainPosition.position, size: CGSize(width: 1440.0, height: 1920.0), scale: mainPosition.scale, rotation: mainPosition.rotation, mirroring: mainPosition.mirroring, 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, mirroring: additionalPosition.mirroring, 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, baseScale: mainPosition.baseScale) + disappearingPosition = VideoPosition(position: mainPosition.position, size: CGSize(width: 1440.0, height: 1920.0), scale: mainPosition.scale, rotation: mainPosition.rotation, mirroring: mainPosition.mirroring, baseScale: mainPosition.baseScale) } if previousChange.timestamp > 0.0 && timestamp < previousChange.timestamp + transitionDuration { transitionFraction = (timestamp - previousChange.timestamp) / transitionDuration @@ -582,8 +608,8 @@ final class VideoFinishPass: 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, 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) + let appearingPosition = VideoPosition(position: additionalPosition.position, size: additionalPosition.size, scale: 0.01, rotation: self.additionalPosition.rotation, mirroring: self.additionalPosition.mirroring, 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, mirroring: additionalPosition.mirroring, baseScale: additionalPosition.baseScale) foregroundPosition = appearingPosition.mixed(with: additionalPosition, fraction: springFraction) @@ -612,7 +638,7 @@ final class VideoFinishPass: RenderPass { } 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 disappearingPosition = VideoPosition(position: foregroundPosition.position, size: foregroundPosition.size, scale: 0.01, rotation: foregroundPosition.rotation, mirroring: foregroundPosition.mirroring, baseScale: foregroundPosition.baseScale) let mainLowerBound = self.videoRange?.lowerBound ?? 0.0 @@ -635,7 +661,7 @@ final class VideoFinishPass: RenderPass { if isVisible { if let additionalVideoRemovalStartTimestamp { - let disappearingPosition = VideoPosition(position: foregroundPosition.position, size: foregroundPosition.size, scale: 0.01, rotation: foregroundPosition.rotation, baseScale: foregroundPosition.baseScale) + let disappearingPosition = VideoPosition(position: foregroundPosition.position, size: foregroundPosition.size, scale: 0.01, rotation: foregroundPosition.rotation, mirroring: foregroundPosition.mirroring, baseScale: foregroundPosition.baseScale) let visibilityFraction = max(0.0, min(1.0, 1.0 - (CACurrentMediaTime() - additionalVideoRemovalStartTimestamp) / videoRemovalDuration)) if visibilityFraction.isZero { @@ -655,6 +681,8 @@ final class VideoFinishPass: RenderPass { let texture: MTLTexture let hasTransparency: Bool let rect: CGRect? + let scale: CGFloat + let offset: CGPoint } func process( @@ -739,6 +767,8 @@ final class VideoFinishPass: RenderPass { texture: input.texture, textureRotation: self.mainTextureRotation, rect: rect, + scale: input.scale, + offset: input.offset, zPosition: 0.0, device: device ) @@ -751,6 +781,8 @@ final class VideoFinishPass: RenderPass { texture: input.texture, textureRotation: self.mainTextureRotation, rect: rect, + scale: input.scale, + offset: input.offset, zPosition: 0.0, device: device ) diff --git a/submodules/TelegramUI/Components/MediaEditorScreen/Sources/MediaEditorDrafts.swift b/submodules/TelegramUI/Components/MediaEditorScreen/Sources/MediaEditorDrafts.swift index 225b5fb565..1cba0fc422 100644 --- a/submodules/TelegramUI/Components/MediaEditorScreen/Sources/MediaEditorDrafts.swift +++ b/submodules/TelegramUI/Components/MediaEditorScreen/Sources/MediaEditorDrafts.swift @@ -21,6 +21,10 @@ extension MediaEditorScreenImpl { let codableEntities = DrawingEntitiesView.encodeEntities(entities, entitiesView: self.node.entitiesView) mediaEditor.setDrawingAndEntities(data: nil, image: mediaEditor.values.drawing, entities: codableEntities) + if case .avatarEditor = self.mode { + return false + } + let filteredEntities = self.node.entitiesView.entities.filter { entity in if entity is DrawingMediaEntity { return false @@ -49,7 +53,7 @@ extension MediaEditorScreenImpl { } func saveDraft(id: Int64?, edit: Bool = false) { - guard let subject = self.node.subject, let actualSubject = self.node.actualSubject, let mediaEditor = self.node.mediaEditor else { + guard case .storyEditor = self.mode, let subject = self.node.subject, let actualSubject = self.node.actualSubject, let mediaEditor = self.node.mediaEditor else { return } try? FileManager.default.createDirectory(atPath: draftPath(engine: self.context.engine), withIntermediateDirectories: true) diff --git a/submodules/TelegramUI/Components/MediaEditorScreen/Sources/MediaEditorScreen.swift b/submodules/TelegramUI/Components/MediaEditorScreen/Sources/MediaEditorScreen.swift index 8ed605dde4..fe43aa0481 100644 --- a/submodules/TelegramUI/Components/MediaEditorScreen/Sources/MediaEditorScreen.swift +++ b/submodules/TelegramUI/Components/MediaEditorScreen/Sources/MediaEditorScreen.swift @@ -171,6 +171,8 @@ final class MediaEditorScreenComponent: Component { case text case sticker case tools + case rotate + case flip case done case cutout case undo @@ -193,6 +195,10 @@ final class MediaEditorScreenComponent: Component { image = generateTintedImage(image: UIImage(bundleImageName: "Media Editor/AddSticker"), color: .white)! case .tools: image = generateTintedImage(image: UIImage(bundleImageName: "Media Editor/Tools"), color: .white)! + case .rotate: + image = generateTintedImage(image: UIImage(bundleImageName: "Media Editor/Rotate"), color: .white)! + case .flip: + image = generateTintedImage(image: UIImage(bundleImageName: "Media Editor/Mirror"), color: .white)! case .cutout: image = UIImage(bundleImageName: "Media Editor/Cutout")!.withRenderingMode(.alwaysTemplate) case .undo: @@ -289,11 +295,14 @@ final class MediaEditorScreenComponent: Component { public final class View: UIView { private let cancelButton = ComponentView() + private let doneButton = ComponentView() private let drawButton = ComponentView() private let textButton = ComponentView() private let stickerButton = ComponentView() private let toolsButton = ComponentView() - private let doneButton = ComponentView() + + private let rotateButton = ComponentView() + private let flipButton = ComponentView() private let cutoutButton = ComponentView() private let undoButton = ComponentView() @@ -308,6 +317,7 @@ final class MediaEditorScreenComponent: Component { private let inputPanelBackground = ComponentView() private var scrubber: ComponentView? + private var scrubberLabel: ComponentView? private let playbackButton = ComponentView() private let muteButton = ComponentView() @@ -332,6 +342,9 @@ final class MediaEditorScreenComponent: Component { private var inputMediaNodeStateContext = ChatEntityKeyboardInputNode.StateContext() private var inputMediaInteraction: ChatEntityKeyboardInputNode.Interaction? private var inputMediaNode: ChatEntityKeyboardInputNode? + + private var cover: (position: Double, image: UIImage)? + private var coverApplyTimer: SwiftSignalKit.Timer? private var videoRecorder: EntityVideoRecorder? @@ -367,6 +380,7 @@ final class MediaEditorScreenComponent: Component { deinit { self.inputMediaNodeDataDisposable?.dispose() + self.coverApplyTimer?.invalidate() } private func setupIfNeeded() { @@ -724,6 +738,22 @@ final class MediaEditorScreenComponent: Component { return inputText } + private func updateCoverPosition() { + guard let controller = self.environment?.controller() as? MediaEditorScreenImpl, let mediaEditor = controller.node.mediaEditor else { + return + } + + if let image = mediaEditor.resultImage { + self.cover = (mediaEditor.currentPosition.seconds, image) + } + controller.node.requestLayout(forceUpdate: true, transition: .immediate) + + self.coverApplyTimer = SwiftSignalKit.Timer(timeout: 1.0, repeat: false, completion: { [weak mediaEditor] in + mediaEditor?.play() + }, queue: Queue.mainQueue()) + self.coverApplyTimer?.start() + } + func update(component: MediaEditorScreenComponent, availableSize: CGSize, state: State, environment: Environment, transition: ComponentTransition) -> CGSize { guard !self.isDismissed else { return availableSize @@ -757,6 +787,8 @@ final class MediaEditorScreenComponent: Component { self.setupIfNeeded() + let mediaEditor = controller.node.mediaEditor + let isTablet = environment.metrics.isTablet let openDrawing = component.openDrawing @@ -833,7 +865,7 @@ final class MediaEditorScreenComponent: Component { case .storyEditor: doneButtonTitle = isEditingStory ? environment.strings.Story_Editor_Done.uppercased() : environment.strings.Story_Editor_Next.uppercased() doneButtonIcon = UIImage(bundleImageName: "Media Editor/Next")! - case .stickerEditor: + case .stickerEditor, .avatarEditor: doneButtonTitle = nil doneButtonIcon = generateTintedImage(image: UIImage(bundleImageName: "Media Editor/Apply"), color: .white)! case .botPreview: @@ -964,14 +996,104 @@ final class MediaEditorScreenComponent: Component { size: stickerButtonSize ) - if let subject = controller.node.subject, case .empty = subject { + let rotateButtonSize = self.rotateButton.update( + transition: transition, + component: AnyComponent(ContextReferenceButtonComponent( + content: AnyComponent(Image( + image: state.image(.rotate), + size: CGSize(width: 30.0, height: 30.0) + )), + tag: textButtonTag, + minSize: CGSize(width: 30.0, height: 30.0), + action: { [weak controller, weak mediaEditor] _, _ in + guard let controller, let mediaEditor else { + return + } + guard !controller.node.recording.isActive else { + return + } + mediaEditor.setCrop( + offset: mediaEditor.values.cropOffset, + scale: mediaEditor.values.cropScale, + rotation: mediaEditor.values.cropRotation - .pi / 2.0, + mirroring: mediaEditor.values.cropMirroring + ) + } + )), + environment: {}, + containerSize: CGSize(width: 40.0, height: 40.0) + ) + let rotateButtonFrame = CGRect( + origin: CGPoint(x: drawButtonFrame.origin.x, y: availableSize.height - environment.safeInsets.bottom + buttonBottomInset + controlsBottomInset + 2.0), + size: rotateButtonSize + ) + + let flipButtonSize = self.flipButton.update( + transition: transition, + component: AnyComponent(ContextReferenceButtonComponent( + content: AnyComponent(Image( + image: state.image(.flip), + size: CGSize(width: 30.0, height: 30.0) + )), + tag: textButtonTag, + minSize: CGSize(width: 30.0, height: 30.0), + action: { [weak controller, weak mediaEditor] _, _ in + guard let controller, let mediaEditor else { + return + } + guard !controller.node.recording.isActive else { + return + } + mediaEditor.setCrop( + offset: mediaEditor.values.cropOffset, + scale: mediaEditor.values.cropScale, + rotation: mediaEditor.values.cropRotation, + mirroring: !mediaEditor.values.cropMirroring + ) + } + )), + environment: {}, + containerSize: CGSize(width: 40.0, height: 40.0) + ) + let flipButtonFrame = CGRect( + origin: CGPoint(x: textButtonFrame.origin.x, y: availableSize.height - environment.safeInsets.bottom + buttonBottomInset + controlsBottomInset + 2.0), + size: flipButtonSize + ) + + var isAvatarEditor = false + if case .avatarEditor = controller.mode { + isAvatarEditor = true + + drawButtonFrame.origin.x = stickerButtonFrame.origin.x + + if let rotateButtonView = self.rotateButton.view { + if rotateButtonView.superview == nil { + self.addSubview(rotateButtonView) + } + transition.setPosition(view: rotateButtonView, position: rotateButtonFrame.center) + transition.setBounds(view: rotateButtonView, bounds: CGRect(origin: .zero, size: rotateButtonFrame.size)) + if !self.animatingButtons { + transition.setAlpha(view: rotateButtonView, alpha: buttonsAreHidden ? 0.0 : bottomButtonsAlpha) + } + } + + if let flipButtonView = self.flipButton.view { + if flipButtonView.superview == nil { + self.addSubview(flipButtonView) + } + transition.setPosition(view: flipButtonView, position: flipButtonFrame.center) + transition.setBounds(view: flipButtonView, bounds: CGRect(origin: .zero, size: flipButtonFrame.size)) + if !self.animatingButtons { + transition.setAlpha(view: flipButtonView, alpha: buttonsAreHidden ? 0.0 : bottomButtonsAlpha) + } + } + } else if let subject = controller.node.subject, case .empty = subject { let distance = floor((stickerButtonFrame.minX - textButtonFrame.minX) * 1.2) textButtonFrame.origin.x = availableSize.width / 2.0 - textButtonFrame.width / 2.0 drawButtonFrame.origin.x = textButtonFrame.origin.x - distance stickerButtonFrame.origin.x = textButtonFrame.origin.x + distance } - if let drawButtonView = self.drawButton.view { if drawButtonView.superview == nil { self.addSubview(drawButtonView) @@ -983,7 +1105,7 @@ final class MediaEditorScreenComponent: Component { } } - if let textButtonView = self.textButton.view { + if !isAvatarEditor, let textButtonView = self.textButton.view { if textButtonView.superview == nil { self.addSubview(textButtonView) } @@ -994,7 +1116,7 @@ final class MediaEditorScreenComponent: Component { } } - if let stickerButtonView = self.stickerButton.view { + if !isAvatarEditor, let stickerButtonView = self.stickerButton.view { if stickerButtonView.superview == nil { self.addSubview(stickerButtonView) } @@ -1180,7 +1302,6 @@ final class MediaEditorScreenComponent: Component { ) } - let mediaEditor = controller.node.mediaEditor var isOutlineActive = false if let value = mediaEditor?.values.toolValues[.stickerOutline] as? Float, value > 0.0 { isOutlineActive = true @@ -1869,7 +1990,11 @@ final class MediaEditorScreenComponent: Component { maxDuration = 15.0 } else { minDuration = 1.0 - maxDuration = storyMaxVideoDuration + if case .avatarEditor = controller.mode { + maxDuration = 10.0 + } else { + maxDuration = storyMaxVideoDuration + } } let previousTrackCount = self.currentVisibleTracks?.count @@ -1895,6 +2020,36 @@ final class MediaEditorScreenComponent: Component { isCollage = videoCount > 1 } + var scrubberBottomOffset: CGFloat = 0.0 + if case .avatarEditor = controller.mode { + let scrubberLabel: ComponentView + if let current = self.scrubberLabel { + scrubberLabel = current + } else { + scrubberLabel = ComponentView() + self.scrubberLabel = scrubberLabel + } + + let scrubberLabelSize = scrubberLabel.update( + transition: scrubberTransition, + component: AnyComponent(MultilineTextComponent( + text: .plain(NSAttributedString(string: environment.strings.PhotoEditor_SelectCoverFrame, font: Font.regular(14.0), textColor: UIColor(rgb: 0xffffff, alpha: 0.7))) + )), + environment: {}, + containerSize: availableSize + ) + if let view = scrubberLabel.view { + if view.superview == nil { + self.addSubview(view) + } + + let scrubberLabelFrame = CGRect(origin: CGPoint(x: floorToScreenPixels((availableSize.width - scrubberLabelSize.width) / 2.0), y: availableSize.height - environment.safeInsets.bottom - scrubberLabelSize.height + controlsBottomInset - inputPanelSize.height + 2.0), size: scrubberLabelSize) + view.frame = scrubberLabelFrame + + scrubberBottomOffset += scrubberLabelSize.height + 12.0 + } + } + let scrubber: ComponentView if let current = self.scrubber { scrubber = current @@ -1918,8 +2073,21 @@ final class MediaEditorScreenComponent: Component { isCollage: isCollage, isCollageSelected: component.isCollageTimelineOpen, collageSamples: playerState.collageSamples, - positionUpdated: { [weak mediaEditor] position, apply in - if let mediaEditor { + cover: self.cover, + getCoverSourceView: { [weak controller] in + return controller?.node.stickerBackgroundView + }, + positionUpdated: { [weak self, weak controller, weak mediaEditor] position, apply in + if let self, let mediaEditor { + var apply = apply + if !apply, self.coverApplyTimer != nil { + self.coverApplyTimer?.invalidate() + self.coverApplyTimer = nil + } + if apply, let controller, case .avatarEditor = controller.mode { + apply = false + self.updateCoverPosition() + } mediaEditor.seek(position, andPlay: apply) } }, @@ -2038,7 +2206,7 @@ final class MediaEditorScreenComponent: Component { component.externalState.timelineHeight = 0.0 } - let scrubberFrame = CGRect(origin: CGPoint(x: floorToScreenPixels((availableSize.width - scrubberSize.width) / 2.0), y: availableSize.height - environment.safeInsets.bottom - scrubberSize.height + controlsBottomInset - inputPanelSize.height + 3.0), size: scrubberSize) + let scrubberFrame = CGRect(origin: CGPoint(x: floorToScreenPixels((availableSize.width - scrubberSize.width) / 2.0), y: availableSize.height - environment.safeInsets.bottom - scrubberSize.height + controlsBottomInset - inputPanelSize.height + 3.0 - scrubberBottomOffset), size: scrubberSize) if let scrubberView = scrubber.view { var animateIn = false if scrubberView.superview == nil { @@ -2055,11 +2223,20 @@ final class MediaEditorScreenComponent: Component { scrubberTransition.setFrame(view: scrubberView, frame: scrubberFrame) } if !self.animatingButtons && !(!hasMainVideoTrack && animateIn) { - transition.setAlpha(view: scrubberView, alpha: component.isDisplayingTool != nil || component.isDismissing || component.isInteractingWithEntities || isEditingCaption || isRecordingAdditionalVideo || isEditingTextEntity ? 0.0 : 1.0) + let scrubberAlpha = component.isDisplayingTool != nil || component.isDismissing || component.isInteractingWithEntities || isEditingCaption || isRecordingAdditionalVideo || isEditingTextEntity ? 0.0 : 1.0 + transition.setAlpha(view: scrubberView, alpha: scrubberAlpha) + if let scrubberLabelView = self.scrubberLabel?.view { + transition.setAlpha(view: scrubberLabelView, alpha: scrubberAlpha) + } } else if animateIn { scrubberView.layer.animatePosition(from: CGPoint(x: 0.0, y: 44.0), to: .zero, duration: 0.3, timingFunction: kCAMediaTimingFunctionSpring, additive: true) scrubberView.layer.animateAlpha(from: 0.0, to: 1.0, duration: 0.2) scrubberView.layer.animateScale(from: 0.6, to: 1.0, duration: 0.2) + + if let scrubberLabelView = self.scrubberLabel?.view { + scrubberLabelView.layer.animatePosition(from: CGPoint(x: 0.0, y: 44.0), to: .zero, duration: 0.3, timingFunction: kCAMediaTimingFunctionSpring, additive: true) + scrubberLabelView.layer.animateAlpha(from: 0.0, to: 1.0, duration: 0.2) + } } } } else { @@ -2501,6 +2678,7 @@ public final class MediaEditorScreenImpl: ViewController, MediaEditorScreen, UID case storyEditor case stickerEditor(mode: StickerEditorMode) case botPreview + case avatarEditor } public enum TransitionIn { @@ -2589,7 +2767,7 @@ public final class MediaEditorScreenImpl: ViewController, MediaEditorScreen, UID private let gradientView: UIImageView private var gradientColorsDisposable: Disposable? - private var stickerBackgroundView: UIImageView? + fileprivate var stickerBackgroundView: UIImageView? private var stickerOverlayLayer: SimpleShapeLayer? private var stickerFrameLayer: SimpleShapeLayer? @@ -2642,7 +2820,7 @@ public final class MediaEditorScreenImpl: ViewController, MediaEditorScreen, UID private var previousDrawingData: Data? private var previousDrawingEntities: [DrawingEntity]? - + private var weatherPromise: Promise? private var playbackPositionDisposable: Disposable? @@ -2694,12 +2872,15 @@ public final class MediaEditorScreenImpl: ViewController, MediaEditorScreen, UID self.gradientView = UIImageView() var isStickerEditor = false + var isAvatarEditor = false if case .stickerEditor = controller.mode { isStickerEditor = true + } else if case .avatarEditor = controller.mode { + isAvatarEditor = true } self.entitiesContainerView = UIView(frame: CGRect(origin: .zero, size: storyDimensions)) - self.entitiesView = DrawingEntitiesView(context: controller.context, size: storyDimensions, hasBin: !isStickerEditor, isStickerEditor: isStickerEditor) + self.entitiesView = DrawingEntitiesView(context: controller.context, size: storyDimensions, hasBin: !isStickerEditor && !isAvatarEditor, isStickerEditor: isStickerEditor) self.entitiesView.getEntityCenterPosition = { return CGPoint(x: storyDimensions.width / 2.0, y: storyDimensions.height / 2.0) } @@ -2739,7 +2920,8 @@ public final class MediaEditorScreenImpl: ViewController, MediaEditorScreen, UID self.containerView.addSubview(self.previewContainerView) - if case .stickerEditor = controller.mode { + switch controller.mode { + case .stickerEditor: let rowsCount = 40 let stickerBackgroundView = UIImageView() stickerBackgroundView.clipsToBounds = true @@ -2762,7 +2944,11 @@ public final class MediaEditorScreenImpl: ViewController, MediaEditorScreen, UID stickerBackgroundView.layer.rasterizationScale = UIScreenScale self.stickerBackgroundView = stickerBackgroundView self.previewContainerView.addSubview(stickerBackgroundView) - } else { + case .avatarEditor: + let stickerBackgroundView = UIImageView() + self.stickerBackgroundView = stickerBackgroundView + self.previewContainerView.addSubview(stickerBackgroundView) + default: self.previewContainerView.addSubview(self.gradientView) } @@ -2774,7 +2960,8 @@ public final class MediaEditorScreenImpl: ViewController, MediaEditorScreen, UID self.entitiesContainerView.addSubview(self.entitiesView) self.entitiesView.addSubview(self.drawingView) - if case .stickerEditor = controller.mode { + switch controller.mode { + case .stickerEditor, .avatarEditor: let stickerOverlayLayer = SimpleShapeLayer() stickerOverlayLayer.fillColor = UIColor(rgb: 0x000000, alpha: 0.7).cgColor stickerOverlayLayer.fillRule = .evenOdd @@ -2789,6 +2976,8 @@ public final class MediaEditorScreenImpl: ViewController, MediaEditorScreen, UID self.stickerFrameLayer = stickerFrameLayer self.previewContainerView.layer.addSublayer(stickerFrameLayer) + default: + break } self.previewContainerView.addSubview(self.selectionContainerView) @@ -2974,7 +3163,7 @@ public final class MediaEditorScreenImpl: ViewController, MediaEditorScreen, UID } else { mediaEntity.scale = storyDimensions.width / fittedSize.width } - case .stickerEditor: + case .stickerEditor, .avatarEditor: if fittedSize.height > fittedSize.width { mediaEntity.scale = storyDimensions.width / fittedSize.width } else { @@ -3011,9 +3200,11 @@ public final class MediaEditorScreenImpl: ViewController, MediaEditorScreen, UID initialValues = nil } - var isStickerEditor = false + var mediaEditorMode: MediaEditor.Mode = .default if case .stickerEditor = controller.mode { - isStickerEditor = true + mediaEditorMode = .sticker + } else if case .avatarEditor = controller.mode { + mediaEditorMode = .avatar } if let mediaEntityView = self.entitiesView.add(mediaEntity, announce: false) as? DrawingMediaEntityView { @@ -3038,7 +3229,7 @@ public final class MediaEditorScreenImpl: ViewController, MediaEditorScreen, UID } } - let mediaEditor = MediaEditor(context: self.context, mode: isStickerEditor ? .sticker : .default, subject: effectiveSubject.editorSubject, values: initialValues, hasHistogram: true) + let mediaEditor = MediaEditor(context: self.context, mode: mediaEditorMode, subject: effectiveSubject.editorSubject, values: initialValues, hasHistogram: true) if let initialVideoPosition = controller.initialVideoPosition { if controller.isEditingStoryCover { mediaEditor.setCoverImageTimestamp(initialVideoPosition) @@ -3583,6 +3774,9 @@ public final class MediaEditorScreenImpl: ViewController, MediaEditorScreen, UID if case .stickerEditor = controller.mode { hasSwipeToDismiss = false hasSwipeToEnhance = false + } else if case .avatarEditor = controller.mode { + hasSwipeToDismiss = false + hasSwipeToEnhance = false } else if self.isCollageTimelineOpen { hasSwipeToEnhance = false } @@ -3772,7 +3966,7 @@ public final class MediaEditorScreenImpl: ViewController, MediaEditorScreen, UID } else { initialScale = self.previewContainerView.bounds.width / image.size.width } - case .stickerEditor: + case .stickerEditor, .avatarEditor: if image.size.height > image.size.width { initialScale = self.previewContainerView.bounds.width / image.size.width } else { @@ -4845,6 +5039,8 @@ public final class MediaEditorScreenImpl: ViewController, MediaEditorScreen, UID controller.requestStickerCompletion(animated: true) case .botPreview: controller.requestStoryCompletion(animated: true) + case .avatarEditor: + controller.requestStoryCompletion(animated: true) } } @@ -5110,7 +5306,7 @@ public final class MediaEditorScreenImpl: ViewController, MediaEditorScreen, UID var hasInteractiveStickers = true if let controller = self.controller { switch controller.mode { - case .stickerEditor, .botPreview: + case .stickerEditor, .botPreview, .avatarEditor: hasInteractiveStickers = false default: break @@ -5597,7 +5793,16 @@ public final class MediaEditorScreenImpl: ViewController, MediaEditorScreen, UID let stickerFrameRect = CGRect(origin: CGPoint(x: floorToScreenPixels((previewSize.width - stickerFrameWidth) / 2.0), y: floorToScreenPixels((previewSize.height - stickerFrameWidth) / 2.0)), size: CGSize(width: stickerFrameWidth, height: stickerFrameWidth)) let overlayOuterRect = UIBezierPath(rect: CGRect(origin: .zero, size: previewSize)) - let overlayInnerRect = UIBezierPath(cgPath: CGPath(roundedRect: stickerFrameRect, cornerWidth: stickerFrameWidth / 8.0, cornerHeight: stickerFrameWidth / 8.0, transform: nil)) + let overlayInnerRect: UIBezierPath + + switch controller.mode { + case .avatarEditor: + overlayInnerRect = UIBezierPath(cgPath: CGPath(ellipseIn: stickerFrameRect, transform: nil)) + stickerFrameLayer.isHidden = true + default: + overlayInnerRect = UIBezierPath(cgPath: CGPath(roundedRect: stickerFrameRect, cornerWidth: stickerFrameWidth / 8.0, cornerHeight: stickerFrameWidth / 8.0, transform: nil)) + } + let overlayLineWidth: CGFloat = 2.0 - UIScreenPixel overlayOuterRect.append(overlayInnerRect) overlayOuterRect.usesEvenOddFillRule = true @@ -5687,14 +5892,28 @@ public final class MediaEditorScreenImpl: ViewController, MediaEditorScreen, UID } public let content: Content public let frame: CGRect + public let contentScale: CGFloat + public let contentOffset: CGPoint var editorItem: MediaEditor.Subject.VideoCollageItem { - return MediaEditor.Subject.VideoCollageItem(content: self.content.editorContent, frame: self.frame) + return MediaEditor.Subject.VideoCollageItem( + content: self.content.editorContent, + frame: self.frame, + contentScale: self.contentScale, + contentOffset: self.contentOffset + ) } - public init(content: Content, frame: CGRect) { + public init( + content: Content, + frame: CGRect, + contentScale: CGFloat, + contentOffset: CGPoint + ) { self.content = content self.frame = frame + self.contentScale = contentScale + self.contentOffset = contentOffset } } @@ -6559,7 +6778,7 @@ public final class MediaEditorScreenImpl: ViewController, MediaEditorScreen, UID save = presentationData.strings.Story_Editor_DraftKeepMedia } text = presentationData.strings.Story_Editor_DraftDiscaedText - case .stickerEditor, .botPreview: + case .stickerEditor, .botPreview, .avatarEditor: title = presentationData.strings.Story_Editor_DraftDiscardMedia text = presentationData.strings.Story_Editor_DiscardText } @@ -7063,7 +7282,13 @@ public final class MediaEditorScreenImpl: ViewController, MediaEditorScreen, UID if let image = mediaEditor.resultImage { self.saveDraft(id: randomId) - 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 + var values = mediaEditor.values + var outputDimensions: CGSize? + if case .avatarEditor = self.mode { + outputDimensions = CGSize(width: 640.0, height: 640.0) + values = values.withUpdatedQualityPreset(.profile) + } + makeEditorImageComposition(context: self.node.ciContext, postbox: self.context.account.postbox, inputImage: image, dimensions: storyDimensions, outputDimensions: outputDimensions, values: 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(MediaEditorScreenImpl.Result(media: .image(image: resultImage, dimensions: PixelDimensions(resultImage.size)), mediaAreas: mediaAreas, caption: caption, coverTimestamp: nil, options: self.state.privacy, stickers: stickers, randomId: randomId), { [weak self] finished in @@ -7649,7 +7874,7 @@ public final class MediaEditorScreenImpl: ViewController, MediaEditorScreen, UID case .addToStickerPack, .createStickerPack: if let (packReference, packTitle) = packReferenceAndTitle, let navigationController = self.navigationController as? NavigationController { Queue.mainQueue().after(0.2) { - let controller = self.context.sharedContext.makeStickerPackScreen(context: self.context, updatedPresentationData: nil, mainStickerPack: packReference, stickerPacks: [packReference], loadedStickerPacks: [], isEditing: false, expandIfNeeded: true, parentNavigationController: navigationController, sendSticker: self.sendSticker, actionPerformed: nil) + let controller = self.context.sharedContext.makeStickerPackScreen(context: self.context, updatedPresentationData: nil, mainStickerPack: packReference, stickerPacks: [packReference], loadedStickerPacks: [], actionTitle: nil, isEditing: false, expandIfNeeded: true, parentNavigationController: navigationController, sendSticker: self.sendSticker, actionPerformed: nil) (navigationController.viewControllers.last as? ViewController)?.present(controller, in: .window(.root)) Queue.mainQueue().after(0.1) { @@ -7865,7 +8090,10 @@ public final class MediaEditorScreenImpl: ViewController, MediaEditorScreen, UID let videoExport = MediaEditorVideoExport(postbox: self.context.account.postbox, subject: exportSubject, configuration: configuration, outputPath: outputPath, textScale: 2.0) self.videoExport = videoExport - self.exportDisposable.set((videoExport.status + let status: Signal = .single(.progress(0.0)) + |> then(videoExport.status) + + self.exportDisposable.set((status |> deliverOnMainQueue).start(next: { [weak self] status in if let self { switch status { diff --git a/submodules/TelegramUI/Components/MediaScrubberComponent/Sources/MediaScrubberComponent.swift b/submodules/TelegramUI/Components/MediaScrubberComponent/Sources/MediaScrubberComponent.swift index f9da6fbe0b..14190505c4 100644 --- a/submodules/TelegramUI/Components/MediaScrubberComponent/Sources/MediaScrubberComponent.swift +++ b/submodules/TelegramUI/Components/MediaScrubberComponent/Sources/MediaScrubberComponent.swift @@ -91,6 +91,9 @@ public final class MediaScrubberComponent: Component { let isCollageSelected: Bool let collageSamples: (samples: Data, peak: Int32)? + let cover: (position: Double, image: UIImage)? + let getCoverSourceView: () -> UIView? + let portalView: PortalView? let positionUpdated: (Double, Bool) -> Void @@ -114,6 +117,8 @@ public final class MediaScrubberComponent: Component { isCollage: Bool, isCollageSelected: Bool = false, collageSamples: (samples: Data, peak: Int32)? = nil, + cover: (position: Double, image: UIImage)? = nil, + getCoverSourceView: @escaping () -> UIView? = { return nil }, portalView: PortalView? = nil, positionUpdated: @escaping (Double, Bool) -> Void, coverPositionUpdated: @escaping (Double, Bool, @escaping () -> Void) -> Void = { _, _, _ in }, @@ -135,6 +140,8 @@ public final class MediaScrubberComponent: Component { self.isCollage = isCollage self.isCollageSelected = isCollageSelected self.collageSamples = collageSamples + self.cover = cover + self.getCoverSourceView = getCoverSourceView self.portalView = portalView self.positionUpdated = positionUpdated self.coverPositionUpdated = coverPositionUpdated @@ -179,6 +186,9 @@ public final class MediaScrubberComponent: Component { if lhs.collageSamples?.samples != rhs.collageSamples?.samples || lhs.collageSamples?.peak != rhs.collageSamples?.peak { return false } + if lhs.cover?.position != rhs.cover?.position { + return false + } return true } @@ -192,6 +202,10 @@ public final class MediaScrubberComponent: Component { private let cursorView: HandleView private let cursorImageView: UIImageView + private let coverDotWrapper: UIView + private let coverDotView: UIImageView + private let coverImageView: UIImageView + private var cursorDisplayLink: SharedDisplayLinkDriver.Link? private var cursorPositionAnimation: (start: Double, from: Double, to: Double, ended: Bool)? @@ -213,6 +227,16 @@ public final class MediaScrubberComponent: Component { self.cursorView = HandleView() self.cursorImageView = UIImageView() + self.coverDotWrapper = UIView() + self.coverDotWrapper.isUserInteractionEnabled = false + self.coverDotWrapper.isHidden = true + + self.coverDotView = UIImageView(image: generateFilledCircleImage(diameter: 7.0, color: UIColor(rgb: 0x007aff))) + + self.coverImageView = UIImageView() + self.coverImageView.clipsToBounds = true + self.coverImageView.contentMode = .scaleAspectFill + super.init(frame: frame) self.clipsToBounds = false @@ -246,6 +270,10 @@ public final class MediaScrubberComponent: Component { self.addSubview(self.cursorView) self.cursorView.addSubview(self.cursorImageView) + self.addSubview(self.coverDotWrapper) + self.coverDotWrapper.addSubview(self.coverDotView) + self.coverDotWrapper.addSubview(self.coverImageView) + self.cursorView.addGestureRecognizer(UIPanGestureRecognizer(target: self, action: #selector(self.handleCursorPan(_:)))) self.cursorDisplayLink = SharedDisplayLinkDriver.shared.add { [weak self] _ in @@ -632,12 +660,13 @@ public final class MediaScrubberComponent: Component { self.collageTrackView = trackView } + let strings = component.context.sharedContext.currentPresentationData.with { $0 }.strings let trackSize = trackView.update( context: component.context, style: component.style, track: MediaScrubberComponent.Track( id: 1024, - content: .audio(artist: nil, title: "Timeline", samples: component.collageSamples?.samples, peak: component.collageSamples?.peak ?? 0, isTimeline: true), + content: .audio(artist: nil, title: strings.MediaEditor_Timeline, samples: component.collageSamples?.samples, peak: component.collageSamples?.peak ?? 0, isTimeline: true), duration: component.maxDuration, trimRange: nil, offset: nil, @@ -808,7 +837,7 @@ public final class MediaScrubberComponent: Component { if let offset = self.mainAudioTrackOffset { cursorPosition -= offset } - let cursorFrame = cursorFrame(size: scrubberSize, height: self.effectiveCursorHeight, position: cursorPosition, duration: trimDuration) + let cursorFrame = cursorFrame(size: scrubberSize, height: self.effectiveCursorHeight, position: cursorPosition, duration: self.trimDuration) transition.setFrame(view: self.cursorView, frame: cursorFrame) transition.setFrame(view: self.cursorContentView, frame: cursorFrame.insetBy(dx: 6.0, dy: 2.0).offsetBy(dx: -1.0 - UIScreenPixel, dy: 0.0)) } @@ -826,6 +855,42 @@ public final class MediaScrubberComponent: Component { transition.setFrame(view: self.cursorImageView, frame: CGRect(origin: .zero, size: self.cursorView.frame.size)) + + if let (coverPosition, coverImage) = component.cover { + let imageSize = CGSize(width: 36.0, height: 36.0) + var animateFrame = false + if previousComponent?.cover?.position != coverPosition { + self.coverDotWrapper.isHidden = false + if let _ = previousComponent?.cover { + if let snapshotView = self.coverDotWrapper.layer.snapshotContentTreeAsView() { + snapshotView.frame = self.coverDotWrapper.frame + self.addSubview(snapshotView) + snapshotView.layer.animateScale(from: 1.0, to: 0.01, duration: 0.2, removeOnCompletion: false) + snapshotView.layer.animateAlpha(from: 1.0, to: 0.0, duration: 0.2, removeOnCompletion: false, completion: { _ in + snapshotView.removeFromSuperview() + }) + } + } + self.coverDotView.layer.animateScale(from: 0.01, to: 1.0, duration: 0.2) + self.coverImageView.image = coverImage + self.coverImageView.layer.cornerRadius = imageSize.width / 2.0 + + animateFrame = true + } + + let dotSize = self.coverDotView.bounds.size + let dotFrame = cursorFrame(size: scrubberSize, height: dotSize.height, position: coverPosition, duration: self.trimDuration) + self.coverDotWrapper.frame = CGRect(origin: CGPoint(x: floor(dotFrame.center.x - dotSize.width / 2.0), y: -18.0), size: dotSize) + self.coverDotView.frame = CGRect(origin: .zero, size: dotSize) + self.coverImageView.frame = CGRect(origin: CGPoint(x: floorToScreenPixels((dotSize.width - imageSize.width) / 2.0), y: -42.0), size: imageSize) + + if animateFrame, let sourceView = component.getCoverSourceView() { + let sourceFrame = sourceView.convert(sourceView.bounds, to: self.coverDotWrapper) + self.coverImageView.layer.animate(from: sourceFrame.width as NSNumber, to: self.coverImageView.layer.cornerRadius as NSNumber, keyPath: "cornerRadius", timingFunction: kCAMediaTimingFunctionSpring, duration: 0.4) + self.coverImageView.layer.animateFrame(from: sourceFrame, to: self.coverImageView.frame, duration: 0.4, timingFunction: kCAMediaTimingFunctionSpring) + } + } + if component.isCollage { transition.setAlpha(view: self.trackContainerView, alpha: component.isCollageSelected ? 1.0 : 0.0) } @@ -1114,7 +1179,10 @@ private class TrackView: UIView, UIScrollViewDelegate, UIGestureRecognizerDelega var deselectedClipWidth: CGFloat = 0.0 var deselectedClipOrigin: CGFloat = 0.0 - if !track.isMain, duration > 0.0 { + if track.isTimeline { + deselectedClipWidth = clipWidth + deselectedClipOrigin = clipOrigin + } else if !track.isMain, duration > 0.0 { let trackDuration: Double if let trimRange = track.trimRange { trackDuration = trimRange.upperBound - trimRange.lowerBound @@ -1184,12 +1252,7 @@ private class TrackView: UIView, UIScrollViewDelegate, UIGestureRecognizerDelega 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)) @@ -1402,6 +1465,11 @@ private class TrackView: UIView, UIScrollViewDelegate, UIGestureRecognizerDelega transition.setFrame(layer: self.waveformCloneLayer, frame: audioWaveformFrame) } } + + 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)) return scrubberSize } diff --git a/submodules/TelegramUI/Components/PeerInfo/PeerInfoCoverComponent/Sources/PeerInfoCoverComponent.swift b/submodules/TelegramUI/Components/PeerInfo/PeerInfoCoverComponent/Sources/PeerInfoCoverComponent.swift index b41f278d4e..db42285b0d 100644 --- a/submodules/TelegramUI/Components/PeerInfo/PeerInfoCoverComponent/Sources/PeerInfoCoverComponent.swift +++ b/submodules/TelegramUI/Components/PeerInfo/PeerInfoCoverComponent/Sources/PeerInfoCoverComponent.swift @@ -59,8 +59,44 @@ private func patternScaleValueAt(fraction: CGFloat, t: CGFloat, reverse: Bool) - } public final class PeerInfoCoverComponent: Component { + public enum Subject: Equatable { + case peer(EnginePeer) + case custom(UIColor?, UIColor?, Int64?) + + func colors(context: AccountContext, isDark: Bool) -> (UIColor, UIColor)? { + switch self { + case let .peer(peer): + if let colors = peer._asPeer().profileColor.flatMap({ context.peerNameColors.getProfile($0, dark: isDark) }) { + let backgroundColor = colors.main + let secondaryBackgroundColor = colors.secondary ?? colors.main + return (backgroundColor, secondaryBackgroundColor) + } else { + return nil + } + case let .custom(color, secondColor, _): + if let color { + if let secondColor { + return (color, secondColor) + } else { + return (color, color) + } + } else { + return nil + } + } + } + + var fileId: Int64? { + switch self { + case let .peer(peer): + return peer.profileBackgroundEmojiId + case let .custom(_, _, fileId): + return fileId + } + } + } public let context: AccountContext - public let peer: EnginePeer? + public let subject: Subject? public let files: [Int64: TelegramMediaFile] public let isDark: Bool public let avatarCenter: CGPoint @@ -71,7 +107,7 @@ public final class PeerInfoCoverComponent: Component { public init( context: AccountContext, - peer: EnginePeer?, + subject: Subject?, files: [Int64: TelegramMediaFile], isDark: Bool, avatarCenter: CGPoint, @@ -81,7 +117,7 @@ public final class PeerInfoCoverComponent: Component { patternTransitionFraction: CGFloat ) { self.context = context - self.peer = peer + self.subject = subject self.files = files self.isDark = isDark self.avatarCenter = avatarCenter @@ -95,7 +131,7 @@ public final class PeerInfoCoverComponent: Component { if lhs.context !== rhs.context { return false } - if lhs.peer != rhs.peer { + if lhs.subject != rhs.subject { return false } if lhs.files != rhs.files { @@ -185,6 +221,32 @@ public final class PeerInfoCoverComponent: Component { self.patternImageDisposable?.dispose() } + public func animateTransition() { + if let snapshotLayer = self.backgroundView.layer.snapshotContentTree() { + self.layer.insertSublayer(snapshotLayer, above: self.backgroundGradientLayer) + if let gradientSnapshotLayer = self.backgroundGradientLayer.snapshotContentTree() { + self.layer.insertSublayer(gradientSnapshotLayer, above: snapshotLayer) + snapshotLayer.animateAlpha(from: 1.0, to: 0.0, duration: 0.25, removeOnCompletion: false, completion: { _ in + snapshotLayer.removeFromSuperlayer() + }) + gradientSnapshotLayer.animateAlpha(from: 1.0, to: 0.0, duration: 0.25, removeOnCompletion: false, completion: { _ in + gradientSnapshotLayer.removeFromSuperlayer() + }) + } + } + for layer in self.avatarPatternContentLayers { + if let snapshot = layer.snapshotContentTree() { + layer.superlayer?.addSublayer(snapshot) + snapshot.animateAlpha(from: 1.0, to: 0.0, duration: 0.25, removeOnCompletion: false, completion: { _ in + snapshot.removeFromSuperlayer() + }) + layer.animateAlpha(from: 0.0, to: 1.0, duration: 0.25) + } + } + let values: [NSNumber] = [1.0, 1.08, 1.0] + self.avatarBackgroundPatternContentsLayer.animateKeyframes(values: values, duration: 0.25, keyPath: "transform.scale") + } + private func loadPatternFromFile() { guard let component = self.component else { return @@ -236,8 +298,8 @@ public final class PeerInfoCoverComponent: Component { } func update(component: PeerInfoCoverComponent, availableSize: CGSize, state: EmptyComponentState, environment: Environment, transition: ComponentTransition) -> CGSize { - if self.component?.peer?.profileBackgroundEmojiId != component.peer?.profileBackgroundEmojiId { - if let profileBackgroundEmojiId = component.peer?.profileBackgroundEmojiId, profileBackgroundEmojiId != 0 { + if self.component?.subject?.fileId != component.subject?.fileId { + if let fileId = component.subject?.fileId, fileId != 0 { if self.patternContentsTarget == nil { self.patternContentsTarget = PatternContentsTarget(imageUpdated: { [weak self] hadContents in guard let self else { @@ -252,7 +314,6 @@ public final class PeerInfoCoverComponent: Component { self.patternFileDisposable = nil self.patternImageDisposable?.dispose() - let fileId = profileBackgroundEmojiId if let file = component.files[fileId] { self.patternFile = file self.loadPatternFromFile() @@ -283,10 +344,9 @@ public final class PeerInfoCoverComponent: Component { 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 + if let subject = component.subject, let colors = subject.colors(context: component.context, isDark: component.isDark) { + backgroundColor = colors.0 + secondaryBackgroundColor = colors.1 } else { backgroundColor = .clear secondaryBackgroundColor = .clear @@ -324,7 +384,7 @@ public final class PeerInfoCoverComponent: Component { 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 { + if component.subject?.colors(context: component.context, isDark: component.isDark) != nil { self.avatarBackgroundPatternContentsLayer.compositingFilter = "overlayBlendMode" self.avatarBackgroundPatternContentsLayer.colors = [ UIColor(white: 0.0, alpha: 0.6).cgColor, @@ -341,17 +401,17 @@ public final class PeerInfoCoverComponent: Component { ] } - self.avatarBackgroundGradientLayer.isHidden = component.peer?.profileColor == nil + self.avatarBackgroundGradientLayer.isHidden = component.subject?.colors(context: component.context, isDark: component.isDark) == 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 { +// 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) diff --git a/submodules/TelegramUI/Components/PeerInfo/PeerInfoScreen/BUILD b/submodules/TelegramUI/Components/PeerInfo/PeerInfoScreen/BUILD index 15fb7ee5c1..0c1f1d805e 100644 --- a/submodules/TelegramUI/Components/PeerInfo/PeerInfoScreen/BUILD +++ b/submodules/TelegramUI/Components/PeerInfo/PeerInfoScreen/BUILD @@ -150,6 +150,10 @@ swift_library( "//submodules/TelegramUI/Components/TextNodeWithEntities", "//submodules/UrlHandling", "//submodules/TelegramUI/Components/EmojiTextAttachmentView", + "//submodules/TelegramUI/Components/MediaEditor", + "//submodules/TelegramUI/Components/MediaEditorScreen", + "//submodules/TelegramUI/Components/CameraScreen", + "//submodules/TelegramUI/Components/PeerInfo/VerifyAlertController", ], visibility = [ "//visibility:public", diff --git a/submodules/TelegramUI/Components/PeerInfo/PeerInfoScreen/Sources/ListItems/PeerInfoScreenCommentItem.swift b/submodules/TelegramUI/Components/PeerInfo/PeerInfoScreen/Sources/ListItems/PeerInfoScreenCommentItem.swift index ec69eaef71..f6a26f160a 100644 --- a/submodules/TelegramUI/Components/PeerInfo/PeerInfoScreen/Sources/ListItems/PeerInfoScreenCommentItem.swift +++ b/submodules/TelegramUI/Components/PeerInfo/PeerInfoScreen/Sources/ListItems/PeerInfoScreenCommentItem.swift @@ -4,6 +4,8 @@ import TelegramPresentationData import TextFormat import Markdown import AccountContext +import TextNodeWithEntities +import TextFormat final class PeerInfoScreenCommentItem: PeerInfoScreenItem { enum LinkAction { @@ -12,11 +14,15 @@ final class PeerInfoScreenCommentItem: PeerInfoScreenItem { let id: AnyHashable let text: String + let attributedPrefix: NSAttributedString? + let useAccentLinkColor: Bool let linkAction: ((LinkAction) -> Void)? - init(id: AnyHashable, text: String, linkAction: ((LinkAction) -> Void)? = nil) { + init(id: AnyHashable, text: String, attributedPrefix: NSAttributedString? = nil, useAccentLinkColor: Bool = true, linkAction: ((LinkAction) -> Void)? = nil) { self.id = id self.text = text + self.attributedPrefix = attributedPrefix + self.useAccentLinkColor = useAccentLinkColor self.linkAction = linkAction } @@ -26,7 +32,7 @@ final class PeerInfoScreenCommentItem: PeerInfoScreenItem { } private final class PeerInfoScreenCommentItemNode: PeerInfoScreenItemNode { - private let textNode: ImmediateTextNode + private let textNode: ImmediateTextNodeWithEntities private var linkHighlightingNode: LinkHighlightingNode? private let activateArea: AccessibilityAreaNode @@ -36,7 +42,7 @@ private final class PeerInfoScreenCommentItemNode: PeerInfoScreenItemNode { private var chevronImage: UIImage? override init() { - self.textNode = ImmediateTextNode() + self.textNode = ImmediateTextNodeWithEntities() self.textNode.displaysAsynchronously = false self.textNode.isUserInteractionEnabled = false @@ -77,15 +83,30 @@ private final class PeerInfoScreenCommentItemNode: PeerInfoScreenItemNode { let verticalInset: CGFloat = 7.0 self.textNode.maximumNumberOfLines = 0 + self.textNode.arguments = TextNodeWithEntities.Arguments( + context: context, + cache: context.animationCache, + renderer: context.animationRenderer, + placeholderColor: presentationData.theme.list.mediaPlaceholderColor, + attemptSynchronous: false + ) let textFont = Font.regular(presentationData.listsFontSize.itemListBaseHeaderFontSize) let textColor = presentationData.theme.list.freeTextColor var text = item.text text = text.replacingOccurrences(of: " >]", with: "\u{00A0}>]") - let attributedText = parseMarkdownIntoAttributedString(text, attributes: MarkdownAttributes(body: MarkdownAttributeSet(font: textFont, textColor: textColor), bold: MarkdownAttributeSet(font: textFont, textColor: textColor), link: MarkdownAttributeSet(font: textFont, textColor: presentationData.theme.list.itemAccentColor), linkAttribute: { contents in + + let attributedText = parseMarkdownIntoAttributedString(text, attributes: MarkdownAttributes(body: MarkdownAttributeSet(font: textFont, textColor: textColor), bold: MarkdownAttributeSet(font: textFont, textColor: textColor), link: MarkdownAttributeSet(font: textFont, textColor: item.useAccentLinkColor ? presentationData.theme.list.itemAccentColor : textColor, additionalAttributes: item.useAccentLinkColor ? [:] : [NSAttributedString.Key.underlineStyle.rawValue: NSUnderlineStyle.single.rawValue as NSNumber]), linkAttribute: { contents in return (TelegramTextAttributes.URL, contents) })).mutableCopy() as! NSMutableAttributedString + + if let attributedPrefix = item.attributedPrefix { + attributedText.insert(attributedPrefix, at: 0) + attributedText.addAttribute(NSAttributedString.Key.font, value: textFont, range: NSRange(location: 0, length: attributedPrefix.length)) + attributedText.addAttribute(NSAttributedString.Key.foregroundColor, value: textColor, range: NSRange(location: 0, length: attributedPrefix.length)) + } + if let _ = item.text.range(of: ">]"), let range = attributedText.string.range(of: ">") { if themeUpdated || self.chevronImage == nil { self.chevronImage = generateTintedImage(image: UIImage(bundleImageName: "Contact List/SubtitleArrow"), color: presentationData.theme.list.itemAccentColor) @@ -96,6 +117,8 @@ private final class PeerInfoScreenCommentItemNode: PeerInfoScreenItemNode { } self.textNode.attributedText = attributedText + self.textNode.visibility = true + self.textNode.lineSpacing = 0.12 self.activateArea.accessibilityLabel = attributedText.string let textSize = self.textNode.updateLayout(CGSize(width: width - sideInset * 2.0, height: .greatestFiniteMagnitude)) @@ -135,7 +158,7 @@ private final class PeerInfoScreenCommentItemNode: PeerInfoScreenItemNode { } private func updateTouchesAtPoint(_ point: CGPoint?) { - if let _ = self.item, let presentationData = self.presentationData { + if let item = self.item, let presentationData = self.presentationData { var rects: [CGRect]? if let point = point { let textNodeFrame = self.textNode.frame @@ -161,7 +184,7 @@ private final class PeerInfoScreenCommentItemNode: PeerInfoScreenItemNode { if let current = self.linkHighlightingNode { linkHighlightingNode = current } else { - linkHighlightingNode = LinkHighlightingNode(color: presentationData.theme.list.itemAccentColor.withAlphaComponent(0.2)) + linkHighlightingNode = LinkHighlightingNode(color: item.useAccentLinkColor ? presentationData.theme.list.itemAccentColor.withAlphaComponent(0.2) : presentationData.theme.list.freeTextColor.withAlphaComponent(0.2)) self.linkHighlightingNode = linkHighlightingNode self.insertSubnode(linkHighlightingNode, belowSubnode: self.textNode) } diff --git a/submodules/TelegramUI/Components/PeerInfo/PeerInfoScreen/Sources/PeerInfoHeaderNode.swift b/submodules/TelegramUI/Components/PeerInfo/PeerInfoScreen/Sources/PeerInfoHeaderNode.swift index f6e23c1cd9..e6fa323dd6 100644 --- a/submodules/TelegramUI/Components/PeerInfo/PeerInfoScreen/Sources/PeerInfoHeaderNode.swift +++ b/submodules/TelegramUI/Components/PeerInfo/PeerInfoScreen/Sources/PeerInfoHeaderNode.swift @@ -531,17 +531,17 @@ final class PeerInfoHeaderNode: ASDisplayNode { } else if peer.isScam { credibilityIcon = .scam } else if let emojiStatus = peer.emojiStatus, !premiumConfiguration.isPremiumDisabled { - if peer is TelegramChannel, peer.isVerified { - verifiedIcon = .verified - } credibilityIcon = .emojiStatus(emojiStatus) - } else if peer.isVerified { - credibilityIcon = .verified } else if peer.isPremium && !premiumConfiguration.isPremiumDisabled && (peer.id != self.context.account.peerId || self.isSettings || self.isMyProfile) { credibilityIcon = .premium } else { credibilityIcon = .none } + if peer.isVerified { + verifiedIcon = .verified + } else if let verification = peer.verification { + verifiedIcon = .emojiStatus(PeerEmojiStatus(fileId: verification.iconFileId, expirationDate: nil)) + } } else { credibilityIcon = .none } @@ -780,8 +780,8 @@ final class PeerInfoHeaderNode: ASDisplayNode { 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) + emojiRegularStatusContent = .none + emojiExpandedStatusContent = .none case .fake: emojiRegularStatusContent = .text(color: presentationData.theme.chat.message.incoming.scamColor, string: presentationData.strings.Message_FakeAccount.uppercased()) emojiExpandedStatusContent = emojiRegularStatusContent @@ -793,7 +793,7 @@ final class PeerInfoHeaderNode: ASDisplayNode { 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 iconSize = self.titleCredibilityIconView.update( transition: ComponentTransition(navigationTransition), component: AnyComponent(EmojiStatusComponent( @@ -848,7 +848,7 @@ final class PeerInfoHeaderNode: ASDisplayNode { } )), environment: {}, - containerSize: CGSize(width: 34.0, height: 34.0) + containerSize: CGSize(width: 26.0, height: 26.0) ) let expandedIconSize = self.titleExpandedCredibilityIconView.update( transition: ComponentTransition(navigationTransition), @@ -867,7 +867,7 @@ final class PeerInfoHeaderNode: ASDisplayNode { } )), environment: {}, - containerSize: CGSize(width: 34.0, height: 34.0) + containerSize: CGSize(width: 26.0, height: 26.0) ) self.credibilityIconSize = iconSize @@ -877,29 +877,18 @@ final class PeerInfoHeaderNode: ASDisplayNode { do { self.currentVerifiedIcon = verifiedIcon - var currentEmojiStatus: PeerEmojiStatus? let emojiRegularStatusContent: EmojiStatusComponent.Content let emojiExpandedStatusContent: EmojiStatusComponent.Content switch verifiedIcon { - 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) + default: + emojiRegularStatusContent = .none + emojiExpandedStatusContent = .none } let iconSize = self.titleVerifiedIconView.update( @@ -915,7 +904,7 @@ final class PeerInfoHeaderNode: ASDisplayNode { emojiFileUpdated: nil )), environment: {}, - containerSize: CGSize(width: 34.0, height: 34.0) + containerSize: CGSize(width: 26.0, height: 26.0) ) let expandedIconSize = self.titleExpandedVerifiedIconView.update( transition: ComponentTransition(navigationTransition), @@ -926,15 +915,10 @@ final class PeerInfoHeaderNode: ASDisplayNode { content: emojiExpandedStatusContent, isVisibleForAnimations: true, useSharedAnimation: true, - action: { [weak self] in - guard let strongSelf = self else { - return - } - strongSelf.displayPremiumIntro?(strongSelf.titleExpandedVerifiedIconView, currentEmojiStatus, strongSelf.emojiStatusFileAndPackTitle.get(), true) - } + action: {} )), environment: {}, - containerSize: CGSize(width: 34.0, height: 34.0) + containerSize: CGSize(width: 26.0, height: 26.0) ) self.verifiedIconSize = iconSize @@ -1318,35 +1302,44 @@ final class PeerInfoHeaderNode: ASDisplayNode { } var titleHorizontalOffset: CGFloat = 0.0 + var titleExpandedHorizontalOffset: CGFloat = 0.0 var nextIconX: CGFloat = titleSize.width var nextExpandedIconX: CGFloat = titleExpandedSize.width if let credibilityIconSize = self.credibilityIconSize, let titleExpandedCredibilityIconSize = self.titleExpandedCredibilityIconSize { - titleHorizontalOffset += -(credibilityIconSize.width + 4.0) / 2.0 + let offset = (credibilityIconSize.width + 4.0) / 2.0 + + let leftOffset: CGFloat = nextIconX + 4.0 + let leftExpandedOffset: CGFloat = nextExpandedIconX + 4.0 + titleHorizontalOffset -= offset 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: nextIconX + 4.0 + collapsedTransitionOffset, y: floor((titleSize.height - credibilityIconSize.height) / 2.0)), size: credibilityIconSize)) + transition.updateFrame(view: self.titleCredibilityIconView, frame: CGRect(origin: CGPoint(x: leftOffset + collapsedTransitionOffset, y: floor((titleSize.height - credibilityIconSize.height) / 2.0)), size: credibilityIconSize)) + transition.updateFrame(view: self.titleExpandedCredibilityIconView, frame: CGRect(origin: CGPoint(x: leftExpandedOffset, y: floor((titleExpandedSize.height - titleExpandedCredibilityIconSize.height) / 2.0) + 1.0), size: titleExpandedCredibilityIconSize)) + nextIconX += 4.0 + credibilityIconSize.width - transition.updateFrame(view: self.titleExpandedCredibilityIconView, frame: CGRect(origin: CGPoint(x: nextExpandedIconX + 4.0, y: floor((titleExpandedSize.height - titleExpandedCredibilityIconSize.height) / 2.0) + 1.0), size: titleExpandedCredibilityIconSize)) nextExpandedIconX += 4.0 + titleExpandedCredibilityIconSize.width } if let verifiedIconSize = self.verifiedIconSize, let titleExpandedVerifiedIconSize = self.titleExpandedVerifiedIconSize { - titleHorizontalOffset += -(verifiedIconSize.width + 4.0) / 2.0 + let offset = (verifiedIconSize.width + 4.0) / 2.0 + titleHorizontalOffset += offset + titleExpandedHorizontalOffset += offset + let leftOffset: CGFloat = -verifiedIconSize.width - 4.0 + let leftExpandedOffset: CGFloat = -titleExpandedVerifiedIconSize.width - 4.0 + var collapsedTransitionOffset: CGFloat = 0.0 if let navigationTransition = self.navigationTransition { collapsedTransitionOffset = -10.0 * navigationTransition.fraction } - transition.updateFrame(view: self.titleVerifiedIconView, frame: CGRect(origin: CGPoint(x: nextIconX + 4.0 + collapsedTransitionOffset, y: floor((titleSize.height - verifiedIconSize.height) / 2.0)), size: verifiedIconSize)) - nextIconX += 4.0 + verifiedIconSize.width - transition.updateFrame(view: self.titleExpandedVerifiedIconView, frame: CGRect(origin: CGPoint(x: nextExpandedIconX + 4.0, y: floor((titleExpandedSize.height - titleExpandedVerifiedIconSize.height) / 2.0) + 1.0), size: titleExpandedVerifiedIconSize)) - nextExpandedIconX += 4.0 + titleExpandedVerifiedIconSize.width + transition.updateFrame(view: self.titleVerifiedIconView, frame: CGRect(origin: CGPoint(x: leftOffset + collapsedTransitionOffset, y: floor((titleSize.height - verifiedIconSize.height) / 2.0)), size: verifiedIconSize)) + transition.updateFrame(view: self.titleExpandedVerifiedIconView, frame: CGRect(origin: CGPoint(x: leftExpandedOffset, y: floor((titleExpandedSize.height - titleExpandedVerifiedIconSize.height) / 2.0) + 1.0), size: titleExpandedVerifiedIconSize)) } var titleFrame: CGRect @@ -1766,7 +1759,7 @@ final class PeerInfoHeaderNode: ASDisplayNode { var titleFrame = titleFrame if !self.isAvatarExpanded { - titleFrame = titleFrame.offsetBy(dx: self.isAvatarExpanded ? 0.0 : titleHorizontalOffset * titleScale, dy: 0.0) + titleFrame = titleFrame.offsetBy(dx: self.isAvatarExpanded ? titleExpandedHorizontalOffset : 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) @@ -1807,7 +1800,7 @@ final class PeerInfoHeaderNode: ASDisplayNode { subtitleBadgeFraction = (1.0 - titleCollapseFraction) } - let rawTitleFrame = titleFrame.offsetBy(dx: self.isAvatarExpanded ? 0.0 : titleHorizontalOffset * titleScale, dy: 0.0) + let rawTitleFrame = titleFrame.offsetBy(dx: self.isAvatarExpanded ? titleExpandedHorizontalOffset : titleHorizontalOffset * titleScale, dy: 0.0) self.titleNodeRawContainer.frame = rawTitleFrame transition.updateFrame(node: self.titleNode, frame: CGRect(origin: CGPoint(), size: CGSize())) let rawSubtitleFrame = subtitleFrame @@ -2119,7 +2112,7 @@ final class PeerInfoHeaderNode: ASDisplayNode { transition: ComponentTransition(transition), component: AnyComponent(PeerInfoCoverComponent( context: self.context, - peer: peer.flatMap(EnginePeer.init), + subject: peer.flatMap { .peer(EnginePeer($0)) }, files: [:], isDark: presentationData.theme.overallDarkAppearance, avatarCenter: apparentAvatarFrame.center, diff --git a/submodules/TelegramUI/Components/PeerInfo/PeerInfoScreen/Sources/PeerInfoScreen.swift b/submodules/TelegramUI/Components/PeerInfo/PeerInfoScreen/Sources/PeerInfoScreen.swift index d51aea4c8e..3ad9d687aa 100644 --- a/submodules/TelegramUI/Components/PeerInfo/PeerInfoScreen/Sources/PeerInfoScreen.swift +++ b/submodules/TelegramUI/Components/PeerInfo/PeerInfoScreen/Sources/PeerInfoScreen.swift @@ -110,6 +110,7 @@ import PeerSelectionScreen import UIKitRuntimeUtils import OldChannelsController import UrlHandling +import VerifyAlertController public enum PeerInfoAvatarEditingMode { case generic @@ -608,6 +609,7 @@ private final class PeerInfoInteraction { let openBusinessLocationContextMenu: (ASDisplayNode, ContextGesture?) -> Void let openBirthdayContextMenu: (ASDisplayNode, ContextGesture?) -> Void let editingOpenAffiliateProgram: () -> Void + let editingOpenVerifyAccounts: () -> Void let getController: () -> ViewController? init( @@ -677,6 +679,7 @@ private final class PeerInfoInteraction { openBusinessLocationContextMenu: @escaping (ASDisplayNode, ContextGesture?) -> Void, openBirthdayContextMenu: @escaping (ASDisplayNode, ContextGesture?) -> Void, editingOpenAffiliateProgram: @escaping () -> Void, + editingOpenVerifyAccounts: @escaping () -> Void, getController: @escaping () -> ViewController? ) { self.openUsername = openUsername @@ -745,6 +748,7 @@ private final class PeerInfoInteraction { self.openBusinessLocationContextMenu = openBusinessLocationContextMenu self.openBirthdayContextMenu = openBirthdayContextMenu self.editingOpenAffiliateProgram = editingOpenAffiliateProgram + self.editingOpenVerifyAccounts = editingOpenVerifyAccounts self.getController = getController } } @@ -1618,7 +1622,41 @@ private func infoItems(data: PeerInfoScreenData?, context: AccountContext, prese })) } - if let botInfo = user.botInfo, botInfo.flags.contains(.worksWithGroups) { + if user.isVerified { + let description = presentationData.strings.PeerInfo_VerificationInfo_Bot + + let attributedPrefix = NSMutableAttributedString(string: " ") + attributedPrefix.addAttribute(ChatTextInputAttributes.customEmoji, value: ChatTextInputTextCustomEmojiAttribute(interactivelySelectedFromPackId: nil, fileId: 0, file: nil, custom: .verification), range: NSMakeRange(0, 1)) + + items[currentPeerInfoSection]!.append(PeerInfoScreenCommentItem(id: 800, text: description, attributedPrefix: attributedPrefix, useAccentLinkColor: false, linkAction: { action in + if case .tap = action, let navigationController = interaction.getController()?.navigationController as? NavigationController { + context.sharedContext.openExternalUrl(context: context, urlContext: .generic, url: presentationData.strings.PeerInfo_VerificationInfo_URL, forceExternal: false, presentationData: presentationData, navigationController: navigationController, dismissInput: {}) + } + })) + } else if let verification = user.verification { + let description: String + if let customDescription = verification.customDescription { + let entities = generateTextEntities(customDescription, enabledTypes: [.allUrl]) + if let entity = entities.first { + let range = NSRange(location: entity.range.lowerBound, length: entity.range.upperBound - entity.range.lowerBound) + let url = (customDescription as NSString).substring(with: range) + description = customDescription.replacingOccurrences(of: url, with: "[\(url)](\(url))") + } else { + description = customDescription + } + } else { + description = presentationData.strings.PeerInfo_VerificationInfo_Custom_Bot(verification.companyName).string + } + + let attributedPrefix = NSMutableAttributedString(string: " ") + attributedPrefix.addAttribute(ChatTextInputAttributes.customEmoji, value: ChatTextInputTextCustomEmojiAttribute(interactivelySelectedFromPackId: nil, fileId: verification.iconFileId, file: nil), range: NSMakeRange(0, 1)) + + items[currentPeerInfoSection]!.append(PeerInfoScreenCommentItem(id: 800, text: description, attributedPrefix: attributedPrefix, useAccentLinkColor: false, linkAction: { action in + if case let .tap(url) = action, let navigationController = interaction.getController()?.navigationController as? NavigationController { + context.sharedContext.openExternalUrl(context: context, urlContext: .generic, url: url, forceExternal: false, presentationData: presentationData, navigationController: navigationController, dismissInput: {}) + } + })) + } else if let botInfo = user.botInfo, botInfo.flags.contains(.worksWithGroups) { items[currentPeerInfoSection]!.append(PeerInfoScreenActionItem(id: 7, text: presentationData.strings.Bot_AddToChat, color: .accent, action: { interaction.openAddBotToGroup() })) @@ -1767,6 +1805,51 @@ private func infoItems(data: PeerInfoScreenData?, context: AccountContext, prese })) } + if channel.isVerified { + let description: String + if case .group = channel.info { + description = presentationData.strings.PeerInfo_VerificationInfo_Group + } else { + description = presentationData.strings.PeerInfo_VerificationInfo_Channel + } + + let attributedPrefix = NSMutableAttributedString(string: " ") + attributedPrefix.addAttribute(ChatTextInputAttributes.customEmoji, value: ChatTextInputTextCustomEmojiAttribute(interactivelySelectedFromPackId: nil, fileId: 0, file: nil, custom: .verification), range: NSMakeRange(0, 1)) + + items[currentPeerInfoSection]!.append(PeerInfoScreenCommentItem(id: 800, text: description, attributedPrefix: attributedPrefix, useAccentLinkColor: false, linkAction: { action in + if case .tap = action, let navigationController = interaction.getController()?.navigationController as? NavigationController { + context.sharedContext.openExternalUrl(context: context, urlContext: .generic, url: presentationData.strings.PeerInfo_VerificationInfo_URL, forceExternal: false, presentationData: presentationData, navigationController: navigationController, dismissInput: {}) + } + })) + } else if let verification = channel.verification { + let description: String + if let customDescription = verification.customDescription { + let entities = generateTextEntities(customDescription, enabledTypes: [.allUrl]) + if let entity = entities.first { + let range = NSRange(location: entity.range.lowerBound, length: entity.range.upperBound - entity.range.lowerBound) + let url = (customDescription as NSString).substring(with: range) + description = customDescription.replacingOccurrences(of: url, with: "[\(url)](\(url))") + } else { + description = customDescription + } + } else { + if case .group = channel.info { + description = presentationData.strings.PeerInfo_VerificationInfo_Custom_Group(verification.companyName).string + } else { + description = presentationData.strings.PeerInfo_VerificationInfo_Custom_Channel(verification.companyName).string + } + } + + let attributedPrefix = NSMutableAttributedString(string: " ") + attributedPrefix.addAttribute(ChatTextInputAttributes.customEmoji, value: ChatTextInputTextCustomEmojiAttribute(interactivelySelectedFromPackId: nil, fileId: verification.iconFileId, file: nil), range: NSMakeRange(0, 1)) + + items[currentPeerInfoSection]!.append(PeerInfoScreenCommentItem(id: 800, text: description, attributedPrefix: attributedPrefix, useAccentLinkColor: false, linkAction: { action in + if case let .tap(url) = action, let navigationController = interaction.getController()?.navigationController as? NavigationController { + context.sharedContext.openExternalUrl(context: context, urlContext: .generic, url: url, forceExternal: false, presentationData: presentationData, navigationController: navigationController, dismissInput: {}) + } + })) + } + if case .broadcast = channel.info { var canEditMembers = false if channel.hasPermission(.banMembers) { @@ -1918,6 +2001,7 @@ private func editingItems(data: PeerInfoScreenData?, state: PeerInfoState, chatL case groupLocation case peerPublicSettings case peerDataSettings + case peerVerifySettings case peerSettings case peerAdditionalSettings case peerActions @@ -1938,10 +2022,12 @@ private func editingItems(data: PeerInfoScreenData?, state: PeerInfoState, chatL let ItemUsername = 5 let ItemAffiliateProgram = 6 - let ItemIntro = 7 - let ItemCommands = 8 - let ItemBotSettings = 9 - let ItemBotInfo = 10 + let ItemVerify = 8 + + let ItemIntro = 8 + let ItemCommands = 9 + let ItemBotSettings = 10 + let ItemBotInfo = 11 if let botInfo = user.botInfo, botInfo.flags.contains(.canEdit) { items[.peerDataSettings]!.append(PeerInfoScreenDisclosureItem(id: ItemUsername, label: .text("@\(user.addressName ?? "")"), text: presentationData.strings.PeerInfo_Bot_Username, icon: PresentationResourcesSettings.bot, action: { @@ -1968,6 +2054,12 @@ private func editingItems(data: PeerInfoScreenData?, state: PeerInfoState, chatL interaction.editingOpenAffiliateProgram() })) } + + if let cachedUserData = data.cachedData as? CachedUserData, let _ = cachedUserData.botInfo?.verifierSettings { + items[.peerVerifySettings]!.append(PeerInfoScreenActionItem(id: ItemVerify, text: "Verify Accounts", icon: UIImage(bundleImageName: "Peer Info/BotVerify"), action: { + interaction.editingOpenVerifyAccounts() + })) + } items[.peerSettings]!.append(PeerInfoScreenActionItem(id: ItemIntro, text: presentationData.strings.PeerInfo_Bot_EditIntro, icon: UIImage(bundleImageName: "Peer Info/BotIntro"), action: { interaction.openPeerMention("botfather", .withBotStartPayload(ChatControllerInitialBotStart(payload: "\(user.addressName ?? "")-intro", behavior: .interactive))) @@ -2724,7 +2816,7 @@ final class PeerInfoScreenNode: ViewControllerTracingNode, PeerInfoScreenNodePro private(set) var validLayout: (ContainerViewLayout, CGFloat)? private(set) var data: PeerInfoScreenData? - private(set) var state = PeerInfoState( + var state = PeerInfoState( isEditing: false, selectedMessageIds: nil, selectedStoryIds: nil, @@ -2752,8 +2844,7 @@ final class PeerInfoScreenNode: ViewControllerTracingNode, PeerInfoScreenNodePro private var shareStatusDisposable: MetaDisposable? private let joinChannelDisposable = MetaDisposable() - private let editAvatarDisposable = MetaDisposable() - private let updateAvatarDisposable = MetaDisposable() + let updateAvatarDisposable = MetaDisposable() private let currentAvatarMixin = Atomic(value: nil) private var groupMembersSearchContext: GroupMembersSearchContext? @@ -3087,6 +3178,11 @@ final class PeerInfoScreenNode: ViewControllerTracingNode, PeerInfoScreenNodePro return } self.editingOpenAffiliateProgram() + }, editingOpenVerifyAccounts: { [weak self] in + guard let self else { + return + } + self.editingOpenVerifyAccounts() }, getController: { [weak self] in return self?.controller @@ -3893,15 +3989,16 @@ final class PeerInfoScreenNode: ViewControllerTracingNode, PeerInfoScreenNodePro let galleryController = AvatarGalleryController(context: strongSelf.context, peer: EnginePeer(peer), sourceCorners: .round, remoteEntries: entriesPromise, skipInitial: true, centralEntryIndex: centralEntry.flatMap { entries.firstIndex(of: $0) }, replaceRootController: { controller, ready in }) galleryController.openAvatarSetup = { [weak self] completion in - self?.openAvatarForEditing(fromGallery: true, completion: { _ in + self?.controller?.openAvatarForEditing(fromGallery: true, completion: { _ in completion() }) } galleryController.avatarPhotoEditCompletion = { [weak self] image in - self?.updateProfilePhoto(image, mode: .generic) + self?.controller?.updateProfilePhoto(image, mode: .generic) } galleryController.avatarVideoEditCompletion = { [weak self] image, asset, adjustments in - self?.updateProfileVideo(image, asset: asset, adjustments: adjustments, mode: .generic) + let _ = self + //self?.controller?.updateProfileVideo(image, asset: asset, adjustments: adjustments, mode: .generic) } galleryController.removedEntry = { [weak self] entry in if let item = PeerInfoAvatarListItem(entry: entry) { @@ -3959,7 +4056,7 @@ final class PeerInfoScreenNode: ViewControllerTracingNode, PeerInfoScreenNodePro proceed() } } else { - strongSelf.openAvatarForEditing() + strongSelf.controller?.openAvatarForEditing() } } @@ -4850,7 +4947,6 @@ final class PeerInfoScreenNode: ViewControllerTracingNode, PeerInfoScreenNodePro self.hiddenAvatarRepresentationDisposable.dispose() self.toggleShouldChannelMessagesSignaturesDisposable.dispose() self.toggleMessageCopyProtectionDisposable.dispose() - self.editAvatarDisposable.dispose() self.selectAddMemberDisposable.dispose() self.addMemberDisposable.dispose() self.preloadHistoryDisposable.dispose() @@ -8723,6 +8819,92 @@ final class PeerInfoScreenNode: ViewControllerTracingNode, PeerInfoScreenNodePro } } + private func editingOpenVerifyAccounts() { + guard let cachedUserData = self.data?.cachedData as? CachedUserData, let verifierSettings = cachedUserData.botInfo?.verifierSettings else { + return + } + let iconPromise = Promise() + iconPromise.set(self.context.engine.stickers.resolveInlineStickers(fileIds: [verifierSettings.iconFileId]) + |> map { $0.first?.value }) + + let controller = self.context.sharedContext.makePeerSelectionController( + PeerSelectionControllerParams( + context: self.context, + filter: [.excludeSecretChats, .excludeRecent, .excludeSavedMessages], + hasContactSelector: false, + title: "Choose Chat to Verify" + ) + ) + controller.peerSelected = { [weak self, weak controller] peer, _ in + guard let self else { + return + } + let _ = (iconPromise.get() + |> take(1) + |> deliverOnMainQueue).start(next: { verifierIcon in + if let _ = peer.verification { + let removeController = removeVerificationAlertController( + context: self.context, + peer: peer, + verifierSettings: verifierSettings, + verifierIcon: verifierIcon, + completion: { [weak self, weak controller] in + guard let self else { + return + } + controller?.dismiss(animated: true) + + let _ = (self.context.engine.peers.updateCustomVerification(botId: self.peerId, peerId: peer.id, value: .disabled) + |> deliverOnMainQueue).start(completed: { [weak self] in + guard let self else { + return + } + let undoController = UndoOverlayController( + presentationData: self.presentationData, + content: .invitedToVoiceChat(context: self.context, peer: peer, title: nil, text: "You have removed **\(peer.compactDisplayTitle)'s** verification.", action: nil, duration: 5.0), + elevatedLayout: false, + action: { _ in return true } + ) + self.controller?.present(undoController, in: .window(.root)) + }) + } + ) + controller?.present(removeController, in: .window(.root)) + } else { + let verifyController = verifyAlertController( + context: self.context, + updatedPresentationData: nil, + peer: peer, + verifierSettings: verifierSettings, + verifierIcon: verifierIcon, + apply: { [weak self, weak controller] value in + guard let self else { + return + } + controller?.dismiss(animated: true) + + let _ = (self.context.engine.peers.updateCustomVerification(botId: self.peerId, peerId: peer.id, value: .enabled(description: value)) + |> deliverOnMainQueue).start(completed: { [weak self] in + guard let self else { + return + } + let undoController = UndoOverlayController( + presentationData: self.presentationData, + content: .invitedToVoiceChat(context: self.context, peer: peer, title: nil, text: "**\(peer.compactDisplayTitle)** has been notified and will receive your verification mark and description upon accepting.", action: nil, duration: 5.0), + elevatedLayout: false, + action: { _ in return true } + ) + self.controller?.present(undoController, in: .window(.root)) + }) + } + ) + controller?.present(verifyController, in: .window(.root)) + } + }) + } + self.controller?.push(controller) + } + private func editingOpenNameColorSetup() { if self.peerId == self.context.account.peerId { let controller = PeerNameColorScreen(context: self.context, updatedPresentationData: self.controller?.updatedPresentationData, subject: .account) @@ -9420,7 +9602,7 @@ final class PeerInfoScreenNode: ViewControllerTracingNode, PeerInfoScreenNodePro } } - private func deleteProfilePhoto(_ item: PeerInfoAvatarListItem) { + func deleteProfilePhoto(_ item: PeerInfoAvatarListItem) { let dismiss = self.headerNode.avatarListNode.listContainerNode.deleteItem(item) if dismiss { if self.headerNode.isAvatarExpanded { @@ -9434,367 +9616,13 @@ final class PeerInfoScreenNode: ViewControllerTracingNode, PeerInfoScreenNodePro } } - fileprivate func updateProfilePhoto(_ image: UIImage, mode: PeerInfoAvatarEditingMode) { - guard let data = image.jpegData(compressionQuality: 0.6) else { - return - } - - if self.headerNode.isAvatarExpanded { - self.headerNode.ignoreCollapse = true - self.headerNode.updateIsAvatarExpanded(false, transition: .immediate) - self.updateNavigationExpansionPresentation(isExpanded: false, animated: true) - } - self.scrollNode.view.setContentOffset(CGPoint(), animated: false) - - let resource = LocalFileMediaResource(fileId: Int64.random(in: Int64.min ... Int64.max)) - self.context.account.postbox.mediaBox.storeResourceData(resource.id, data: data) - let representation = TelegramMediaImageRepresentation(dimensions: PixelDimensions(width: 640, height: 640), resource: resource, progressiveSizes: [], immediateThumbnailData: nil, hasVideo: false, isPersonal: mode == .custom ? true : false) - - if [.suggest, .fallback].contains(mode) { - } else { - self.state = self.state.withUpdatingAvatar(.image(representation)) - } - - if let (layout, navigationHeight) = self.validLayout { - self.containerLayoutUpdated(layout: layout, navigationHeight: navigationHeight, transition: mode == .custom ? .animated(duration: 0.2, curve: .easeInOut) : .immediate, additive: false) - } - self.headerNode.ignoreCollapse = false - - let postbox = self.context.account.postbox - let signal: Signal - if self.isSettings || self.isMyProfile { - if case .fallback = mode { - signal = self.context.engine.accountData.updateFallbackPhoto(resource: resource, videoResource: nil, videoStartTimestamp: nil, markup: nil, mapResourceToAvatarSizes: { resource, representations in - return mapResourceToAvatarSizes(postbox: postbox, resource: resource, representations: representations) - }) - } else { - signal = self.context.engine.accountData.updateAccountPhoto(resource: resource, videoResource: nil, videoStartTimestamp: nil, markup: nil, mapResourceToAvatarSizes: { resource, representations in - return mapResourceToAvatarSizes(postbox: postbox, resource: resource, representations: representations) - }) - } - } else if case .custom = mode { - signal = self.context.engine.contacts.updateContactPhoto(peerId: self.peerId, resource: resource, videoResource: nil, videoStartTimestamp: nil, markup: nil, mode: .custom, mapResourceToAvatarSizes: { resource, representations in - return mapResourceToAvatarSizes(postbox: postbox, resource: resource, representations: representations) - }) - } else if case .suggest = mode { - signal = self.context.engine.contacts.updateContactPhoto(peerId: self.peerId, resource: resource, videoResource: nil, videoStartTimestamp: nil, markup: nil, mode: .suggest, mapResourceToAvatarSizes: { resource, representations in - return mapResourceToAvatarSizes(postbox: postbox, resource: resource, representations: representations) - }) - } else { - signal = self.context.engine.peers.updatePeerPhoto(peerId: self.peerId, photo: self.context.engine.peers.uploadedPeerPhoto(resource: resource), mapResourceToAvatarSizes: { resource, representations in - return mapResourceToAvatarSizes(postbox: postbox, resource: resource, representations: representations) - }) - } - - var dismissStatus: (() -> Void)? - if [.suggest, .fallback, .accept].contains(mode) { - let statusController = OverlayStatusController(theme: self.presentationData.theme, type: .loading(cancelled: { [weak self] in - self?.updateAvatarDisposable.set(nil) - dismissStatus?() - })) - dismissStatus = { [weak statusController] in - statusController?.dismiss() - } - if let topController = self.controller?.navigationController?.topViewController as? ViewController { - topController.presentInGlobalOverlay(statusController) - } else if let topController = self.controller?.parentController?.topViewController as? ViewController { - topController.presentInGlobalOverlay(statusController) - } else { - self.controller?.presentInGlobalOverlay(statusController) - } - } - - self.updateAvatarDisposable.set((signal - |> deliverOnMainQueue).startStrict(next: { [weak self] result in - guard let strongSelf = self else { - return - } - switch result { - case .complete: - strongSelf.state = strongSelf.state.withUpdatingAvatar(nil).withAvatarUploadProgress(nil) - case let .progress(value): - strongSelf.state = strongSelf.state.withAvatarUploadProgress(.value(CGFloat(value))) - } - if let (layout, navigationHeight) = strongSelf.validLayout { - strongSelf.containerLayoutUpdated(layout: layout, navigationHeight: navigationHeight, transition: .immediate, additive: false) - } - - if case .complete = result { - dismissStatus?() - - let _ = (strongSelf.context.engine.data.get(TelegramEngine.EngineData.Item.Peer.Peer(id: strongSelf.peerId)) - |> deliverOnMainQueue).startStandalone(next: { [weak self] peer in - if let strongSelf = self, let peer { - switch mode { - case .fallback: - (strongSelf.controller?.parentController?.topViewController as? ViewController)?.present(UndoOverlayController(presentationData: strongSelf.presentationData, content: .image(image: image, title: nil, text: strongSelf.presentationData.strings.Privacy_ProfilePhoto_PublicPhotoSuccess, round: true, undoText: nil), elevatedLayout: false, animateInAsReplacement: true, action: { _ in return false }), in: .current) - case .custom: - strongSelf.controller?.present(UndoOverlayController(presentationData: strongSelf.presentationData, content: .invitedToVoiceChat(context: strongSelf.context, peer: peer, title: nil, text: strongSelf.presentationData.strings.UserInfo_SetCustomPhoto_SuccessPhotoText(peer.compactDisplayTitle).string, action: nil, duration: 5), elevatedLayout: false, animateInAsReplacement: true, action: { _ in return false }), in: .current) - - let _ = (strongSelf.context.peerChannelMemberCategoriesContextsManager.profilePhotos(postbox: strongSelf.context.account.postbox, network: strongSelf.context.account.network, peerId: strongSelf.peerId, fetch: peerInfoProfilePhotos(context: strongSelf.context, peerId: strongSelf.peerId)) |> ignoreValues).startStandalone() - case .suggest: - if let navigationController = (strongSelf.controller?.navigationController as? NavigationController) { - strongSelf.context.sharedContext.navigateToChatController(NavigateToChatControllerParams(navigationController: navigationController, context: strongSelf.context, chatLocation: .peer(peer), keepStack: .default, completion: { _ in - })) - } - case .accept: - (strongSelf.controller?.parentController?.topViewController as? ViewController)?.present(UndoOverlayController(presentationData: strongSelf.presentationData, content: .image(image: image, title: strongSelf.presentationData.strings.Conversation_SuggestedPhotoSuccess, text: strongSelf.presentationData.strings.Conversation_SuggestedPhotoSuccessText, round: true, undoText: nil), elevatedLayout: false, animateInAsReplacement: true, action: { [weak self] action in - if case .info = action { - self?.controller?.parentController?.openSettings() - } - return false - }), in: .current) - default: - break - } - } - }) - } - })) - } - - fileprivate func updateProfileVideo(_ image: UIImage, asset: Any?, adjustments: TGVideoEditAdjustments?, mode: PeerInfoAvatarEditingMode) { - guard let data = image.jpegData(compressionQuality: 0.6) else { - return - } - - if self.headerNode.isAvatarExpanded { - self.headerNode.ignoreCollapse = true - self.headerNode.updateIsAvatarExpanded(false, transition: .immediate) - self.updateNavigationExpansionPresentation(isExpanded: false, animated: true) - } - self.scrollNode.view.setContentOffset(CGPoint(), animated: false) - - let photoResource = LocalFileMediaResource(fileId: Int64.random(in: Int64.min ... Int64.max)) - self.context.account.postbox.mediaBox.storeResourceData(photoResource.id, data: data) - let representation = TelegramMediaImageRepresentation(dimensions: PixelDimensions(width: 640, height: 640), resource: photoResource, progressiveSizes: [], immediateThumbnailData: nil, hasVideo: false, isPersonal: mode == .custom ? true : false) - - var markup: UploadPeerPhotoMarkup? = nil - if let fileId = adjustments?.documentId, let backgroundColors = adjustments?.colors as? [Int32], fileId != 0 { - if let packId = adjustments?.stickerPackId, let accessHash = adjustments?.stickerPackAccessHash, packId != 0 { - markup = .sticker(packReference: .id(id: packId, accessHash: accessHash), fileId: fileId, backgroundColors: backgroundColors) - } else { - markup = .emoji(fileId: fileId, backgroundColors: backgroundColors) - } - } - - var uploadVideo = true - if let _ = markup { - if let data = self.context.currentAppConfiguration.with({ $0 }).data, let uploadVideoValue = data["upload_markup_video"] as? Bool, uploadVideoValue { - uploadVideo = true - } else { - uploadVideo = false - } - } - - if [.suggest, .fallback].contains(mode) { - } else { - self.state = self.state.withUpdatingAvatar(.image(representation)) - if !uploadVideo { - self.state = self.state.withAvatarUploadProgress(.indefinite) - } - } - - if let (layout, navigationHeight) = self.validLayout { - self.containerLayoutUpdated(layout: layout, navigationHeight: navigationHeight, transition: mode == .custom ? .animated(duration: 0.2, curve: .easeInOut) : .immediate, additive: false) - } - self.headerNode.ignoreCollapse = false - - var videoStartTimestamp: Double? = nil - if let adjustments = adjustments, adjustments.videoStartValue > 0.0 { - videoStartTimestamp = adjustments.videoStartValue - adjustments.trimStartValue - } - - let account = self.context.account - let context = self.context - - let videoResource: Signal - if uploadVideo { - videoResource = Signal { [weak self] subscriber in - let entityRenderer: LegacyPaintEntityRenderer? = adjustments.flatMap { adjustments in - if let paintingData = adjustments.paintingData, paintingData.hasAnimation { - return LegacyPaintEntityRenderer(postbox: account.postbox, adjustments: adjustments) - } else { - return nil - } - } - - let tempFile = EngineTempBox.shared.tempFile(fileName: "video.mp4") - let uploadInterface = LegacyLiveUploadInterface(context: context) - let signal: SSignal - if let url = asset as? URL, url.absoluteString.hasSuffix(".jpg"), let data = try? Data(contentsOf: url, options: [.mappedRead]), let image = UIImage(data: data), let entityRenderer = entityRenderer { - let durationSignal: SSignal = SSignal(generator: { subscriber in - let disposable = (entityRenderer.duration()).start(next: { duration in - subscriber.putNext(duration) - subscriber.putCompletion() - }) - - return SBlockDisposable(block: { - disposable.dispose() - }) - }) - signal = durationSignal.map(toSignal: { duration -> SSignal in - if let duration = duration as? Double { - return TGMediaVideoConverter.renderUIImage(image, duration: duration, adjustments: adjustments, path: tempFile.path, watcher: nil, entityRenderer: entityRenderer)! - } else { - return SSignal.single(nil) - } - }) - } else if let asset = asset as? AVAsset { - signal = TGMediaVideoConverter.convert(asset, adjustments: adjustments, path: tempFile.path, watcher: uploadInterface, entityRenderer: entityRenderer)! - } else { - signal = SSignal.complete() - } - - let signalDisposable = signal.start(next: { next in - if let result = next as? TGMediaVideoConversionResult { - if let image = result.coverImage, let data = image.jpegData(compressionQuality: 0.7) { - account.postbox.mediaBox.storeResourceData(photoResource.id, data: data) - } - - if let timestamp = videoStartTimestamp { - videoStartTimestamp = max(0.0, min(timestamp, result.duration - 0.05)) - } - - var value = stat() - if stat(result.fileURL.path, &value) == 0 { - if let data = try? Data(contentsOf: result.fileURL) { - let resource: TelegramMediaResource - if let liveUploadData = result.liveUploadData as? LegacyLiveUploadInterfaceResult { - resource = LocalFileMediaResource(fileId: liveUploadData.id) - } else { - resource = LocalFileMediaResource(fileId: Int64.random(in: Int64.min ... Int64.max)) - } - account.postbox.mediaBox.storeResourceData(resource.id, data: data, synchronous: true) - subscriber.putNext(resource) - - EngineTempBox.shared.dispose(tempFile) - } - } - subscriber.putCompletion() - } else if let strongSelf = self, let progress = next as? NSNumber { - Queue.mainQueue().async { - strongSelf.state = strongSelf.state.withAvatarUploadProgress(.value(CGFloat(progress.floatValue * 0.45))) - if let (layout, navigationHeight) = strongSelf.validLayout { - strongSelf.containerLayoutUpdated(layout: layout, navigationHeight: navigationHeight, transition: .immediate, additive: false) - } - } - } - }, error: { _ in - }, completed: nil) - - let disposable = ActionDisposable { - signalDisposable?.dispose() - } - - return ActionDisposable { - disposable.dispose() - } - } - } else { - videoResource = .single(nil) - } - - var dismissStatus: (() -> Void)? - if [.suggest, .fallback, .accept].contains(mode) { - let statusController = OverlayStatusController(theme: self.presentationData.theme, type: .loading(cancelled: { [weak self] in - self?.updateAvatarDisposable.set(nil) - dismissStatus?() - })) - dismissStatus = { [weak statusController] in - statusController?.dismiss() - } - if let topController = self.controller?.navigationController?.topViewController as? ViewController { - topController.presentInGlobalOverlay(statusController) - } else if let topController = self.controller?.parentController?.topViewController as? ViewController { - topController.presentInGlobalOverlay(statusController) - } else { - self.controller?.presentInGlobalOverlay(statusController) - } - } - - let peerId = self.peerId - let isSettings = self.isSettings - let isMyProfile = self.isMyProfile - self.updateAvatarDisposable.set((videoResource - |> mapToSignal { videoResource -> Signal in - if isSettings || isMyProfile { - if case .fallback = mode { - return context.engine.accountData.updateFallbackPhoto(resource: photoResource, videoResource: videoResource, videoStartTimestamp: videoStartTimestamp, markup: markup, mapResourceToAvatarSizes: { resource, representations in - return mapResourceToAvatarSizes(postbox: account.postbox, resource: resource, representations: representations) - }) - } else { - return context.engine.accountData.updateAccountPhoto(resource: photoResource, videoResource: videoResource, videoStartTimestamp: videoStartTimestamp, markup: markup, mapResourceToAvatarSizes: { resource, representations in - return mapResourceToAvatarSizes(postbox: account.postbox, resource: resource, representations: representations) - }) - } - } else if case .custom = mode { - return context.engine.contacts.updateContactPhoto(peerId: peerId, resource: photoResource, videoResource: videoResource, videoStartTimestamp: videoStartTimestamp, markup: markup, mode: .custom, mapResourceToAvatarSizes: { resource, representations in - return mapResourceToAvatarSizes(postbox: account.postbox, resource: resource, representations: representations) - }) - } else if case .suggest = mode { - return context.engine.contacts.updateContactPhoto(peerId: peerId, resource: photoResource, videoResource: videoResource, videoStartTimestamp: videoStartTimestamp, markup: markup, mode: .suggest, mapResourceToAvatarSizes: { resource, representations in - return mapResourceToAvatarSizes(postbox: account.postbox, resource: resource, representations: representations) - }) - } else { - return context.engine.peers.updatePeerPhoto(peerId: peerId, photo: context.engine.peers.uploadedPeerPhoto(resource: photoResource), video: videoResource.flatMap { context.engine.peers.uploadedPeerVideo(resource: $0) |> map(Optional.init) }, videoStartTimestamp: videoStartTimestamp, markup: markup, mapResourceToAvatarSizes: { resource, representations in - return mapResourceToAvatarSizes(postbox: account.postbox, resource: resource, representations: representations) - }) - } - } - |> deliverOnMainQueue).startStrict(next: { [weak self] result in - guard let strongSelf = self else { - return - } - switch result { - case .complete: - strongSelf.state = strongSelf.state.withUpdatingAvatar(nil).withAvatarUploadProgress(nil) - case let .progress(value): - strongSelf.state = strongSelf.state.withAvatarUploadProgress(.value(CGFloat(0.45 + value * 0.55))) - } - if let (layout, navigationHeight) = strongSelf.validLayout { - strongSelf.containerLayoutUpdated(layout: layout, navigationHeight: navigationHeight, transition: .immediate, additive: false) - } - - if case .complete = result { - dismissStatus?() - - let _ = (strongSelf.context.engine.data.get(TelegramEngine.EngineData.Item.Peer.Peer(id: strongSelf.peerId)) - |> deliverOnMainQueue).startStandalone(next: { [weak self] peer in - if let strongSelf = self, let peer { - switch mode { - case .fallback: - (strongSelf.controller?.parentController?.topViewController as? ViewController)?.present(UndoOverlayController(presentationData: strongSelf.presentationData, content: .image(image: image, title: nil, text: strongSelf.presentationData.strings.Privacy_ProfilePhoto_PublicVideoSuccess, round: true, undoText: nil), elevatedLayout: false, animateInAsReplacement: true, action: { _ in return false }), in: .current) - case .custom: - strongSelf.controller?.present(UndoOverlayController(presentationData: strongSelf.presentationData, content: .invitedToVoiceChat(context: strongSelf.context, peer: peer, title: nil, text: strongSelf.presentationData.strings.UserInfo_SetCustomPhoto_SuccessVideoText(peer.compactDisplayTitle).string, action: nil, duration: 5), elevatedLayout: false, animateInAsReplacement: true, action: { _ in return false }), in: .current) - - let _ = (strongSelf.context.peerChannelMemberCategoriesContextsManager.profilePhotos(postbox: strongSelf.context.account.postbox, network: strongSelf.context.account.network, peerId: strongSelf.peerId, fetch: peerInfoProfilePhotos(context: strongSelf.context, peerId: strongSelf.peerId)) |> ignoreValues).startStandalone() - case .suggest: - if let navigationController = (strongSelf.controller?.navigationController as? NavigationController) { - strongSelf.context.sharedContext.navigateToChatController(NavigateToChatControllerParams(navigationController: navigationController, context: strongSelf.context, chatLocation: .peer(peer), keepStack: .default, completion: { _ in - })) - } - case .accept: - (strongSelf.controller?.parentController?.topViewController as? ViewController)?.present(UndoOverlayController(presentationData: strongSelf.presentationData, content: .image(image: image, title: strongSelf.presentationData.strings.Conversation_SuggestedVideoSuccess, text: strongSelf.presentationData.strings.Conversation_SuggestedVideoSuccessText, round: true, undoText: nil), elevatedLayout: false, animateInAsReplacement: true, action: { [weak self] action in - if case .info = action { - self?.controller?.parentController?.openSettings() - } - return false - }), in: .current) - default: - break - } - } - }) - } - })) - } - - fileprivate func openAvatarForEditing(mode: PeerInfoAvatarEditingMode = .generic, fromGallery: Bool = false, completion: @escaping (UIImage?) -> Void = { _ in }) { + fileprivate func oldOpenAvatarForEditing(mode: PeerInfoAvatarEditingMode = .generic, fromGallery: Bool = false, completion: @escaping (UIImage?) -> Void = { _ in }) { guard let peer = self.data?.peer, mode != .generic || canEditPeerInfo(context: self.context, peer: peer, chatLocation: self.chatLocation, threadData: self.data?.threadData) else { return } + self.view.endEditing(true) + var currentIsVideo = false var emojiMarkup: TelegramMediaImage.EmojiMarkup? let item = self.headerNode.avatarListNode.listContainerNode.currentItemNode?.item @@ -9814,23 +9642,7 @@ final class PeerInfoScreenNode: ViewControllerTracingNode, PeerInfoScreenNodePro } let presentationData = strongSelf.presentationData - - let legacyController = LegacyController(presentation: .custom, theme: presentationData.theme) - legacyController.statusBar.statusBarStyle = .Ignore - - let emptyController = LegacyEmptyController(context: legacyController.context)! - let navigationController = makeLegacyNavigationController(rootController: emptyController) - navigationController.setNavigationBarHidden(true, animated: false) - navigationController.navigationBar.transform = CGAffineTransform(translationX: -1000.0, y: 0.0) - - legacyController.bind(controller: navigationController) - - strongSelf.view.endEditing(true) - - let parentController = (strongSelf.context.sharedContext.mainWindow?.viewController as? NavigationController)?.topViewController as? ViewController - - parentController?.present(legacyController, in: .window(.root)) - + var hasPhotos = false if !peer.profileImageRepresentations.isEmpty { hasPhotos = true @@ -9877,24 +9689,37 @@ final class PeerInfoScreenNode: ViewControllerTracingNode, PeerInfoScreenNodePro let keyboardInputData = Promise() keyboardInputData.set(AvatarEditorScreen.inputData(context: strongSelf.context, isGroup: peer.id.namespace != Namespaces.Peer.CloudUser)) + let legacyController = LegacyController(presentation: .custom, theme: presentationData.theme) + legacyController.statusBar.statusBarStyle = .Ignore + + let emptyController = LegacyEmptyController(context: legacyController.context)! + let navigationController = makeLegacyNavigationController(rootController: emptyController) + navigationController.setNavigationBarHidden(true, animated: false) + navigationController.navigationBar.transform = CGAffineTransform(translationX: -1000.0, y: 0.0) + + legacyController.bind(controller: navigationController) + + let parentController = (strongSelf.context.sharedContext.mainWindow?.viewController as? NavigationController)?.topViewController as? ViewController + parentController?.present(legacyController, in: .window(.root)) + let mixin = TGMediaAvatarMenuMixin(context: legacyController.context, parentController: emptyController, hasSearchButton: true, hasDeleteButton: hasDeleteButton, hasViewButton: false, personalPhoto: strongSelf.isSettings || strongSelf.isMyProfile, isVideo: currentIsVideo, saveEditedPhotos: false, saveCapturedMedia: false, signup: false, forum: isForum, title: title, isSuggesting: [.custom, .suggest].contains(mode))! mixin.stickersContext = LegacyPaintStickersContext(context: strongSelf.context) let _ = strongSelf.currentAvatarMixin.swap(mixin) - mixin.requestSearchController = { [weak self, weak parentController] assetsController in - guard let strongSelf = self else { - return - } - let controller = WebSearchController(context: strongSelf.context, updatedPresentationData: strongSelf.controller?.updatedPresentationData, peer: peer, chatLocation: nil, configuration: searchBotsConfiguration, mode: .avatar(initialQuery: (strongSelf.isSettings || strongSelf.isMyProfile) ? nil : peer.compactDisplayTitle, completion: { [weak self] result in - assetsController?.dismiss() - self?.updateProfilePhoto(result, mode: mode) - })) - controller.navigationPresentation = .modal - parentController?.push(controller) - - if fromGallery { - completion(nil) - } - } +// mixin.requestSearchController = { [weak self, weak parentController] assetsController in +// guard let strongSelf = self else { +// return +// } +// let controller = WebSearchController(context: strongSelf.context, updatedPresentationData: strongSelf.controller?.updatedPresentationData, peer: peer, chatLocation: nil, configuration: searchBotsConfiguration, mode: .avatar(initialQuery: (strongSelf.isSettings || strongSelf.isMyProfile) ? nil : peer.compactDisplayTitle, completion: { [weak self] result in +// assetsController?.dismiss() +// self?.updateProfilePhoto(result, mode: mode) +// })) +// controller.navigationPresentation = .modal +// parentController?.push(controller) +// +// if fromGallery { +// completion(nil) +// } +// } var isFromEditor = false mixin.requestAvatarEditor = { [weak self, weak parentController] imageCompletion, videoCompletion in guard let strongSelf = self, let imageCompletion, let videoCompletion else { @@ -9919,12 +9744,6 @@ final class PeerInfoScreenNode: ViewControllerTracingNode, PeerInfoScreenNodePro controller.videoCompletion = videoCompletion parentController?.push(controller) isFromEditor = true - - Queue.mainQueue().after(1.0) { - if let rootController = strongSelf.context.sharedContext.mainWindow?.viewController as? TelegramRootControllerInterface { - rootController.openSettings() - } - } } if let confirmationTextPhoto, let confirmationAction { @@ -9947,24 +9766,24 @@ final class PeerInfoScreenNode: ViewControllerTracingNode, PeerInfoScreenNodePro } } } - mixin.didFinishWithImage = { [weak self] image in - if let image = image { - completion(image) - self?.updateProfilePhoto(image, mode: mode) - } - } - mixin.didFinishWithVideo = { [weak self] image, asset, adjustments in - if let image = image, let asset = asset { - completion(image) - self?.updateProfileVideo(image, asset: asset, adjustments: adjustments, mode: mode) - } - } +// mixin.didFinishWithImage = { [weak self] image in +// if let image = image { +// completion(image) +// self?.updateProfilePhoto(image, mode: mode) +// } +// } +// mixin.didFinishWithVideo = { [weak self] image, asset, adjustments in +// if let image = image, let asset = asset { +// completion(image) +// self?.updateProfileVideo(image, asset: asset, adjustments: adjustments, mode: mode) +// } +// } mixin.didFinishWithDelete = { guard let strongSelf = self else { return } - strongSelf.openAvatarRemoval(mode: mode, peer: peer, item: item) + strongSelf.controller?.openAvatarRemoval(mode: mode, peer: peer, item: item) } mixin.didDismiss = { [weak legacyController] in guard let strongSelf = self else { @@ -9982,81 +9801,6 @@ final class PeerInfoScreenNode: ViewControllerTracingNode, PeerInfoScreenNodePro }) } - fileprivate func openAvatarRemoval(mode: PeerInfoAvatarEditingMode, peer: EnginePeer? = nil, item: PeerInfoAvatarListItem? = nil, completion: @escaping () -> Void = {}) { - let proceed = { [weak self] in - guard let strongSelf = self else { - return - } - - completion() - - if let item = item { - strongSelf.deleteProfilePhoto(item) - } - - let _ = strongSelf.currentAvatarMixin.swap(nil) - if mode != .fallback { - if let peer = peer, let _ = peer.smallProfileImage { - strongSelf.state = strongSelf.state.withUpdatingAvatar(nil) - if let (layout, navigationHeight) = strongSelf.validLayout { - strongSelf.containerLayoutUpdated(layout: layout, navigationHeight: navigationHeight, transition: .immediate, additive: false) - } - } - } - let postbox = strongSelf.context.account.postbox - let signal: Signal - if case .custom = mode { - signal = strongSelf.context.engine.contacts.updateContactPhoto(peerId: strongSelf.peerId, resource: nil, videoResource: nil, videoStartTimestamp: nil, markup: nil, mode: .custom, mapResourceToAvatarSizes: { resource, representations in - return mapResourceToAvatarSizes(postbox: postbox, resource: resource, representations: representations) - }) - } else if case .fallback = mode { - signal = strongSelf.context.engine.accountData.removeFallbackPhoto(reference: nil) - |> castError(UploadPeerPhotoError.self) - |> map { _ in - return .complete([]) - } - } else { - signal = strongSelf.context.engine.peers.updatePeerPhoto(peerId: strongSelf.peerId, photo: nil, mapResourceToAvatarSizes: { resource, representations in - return mapResourceToAvatarSizes(postbox: postbox, resource: resource, representations: representations) - }) - } - strongSelf.updateAvatarDisposable.set((signal - |> deliverOnMainQueue).startStrict(next: { result in - guard let strongSelf = self else { - return - } - switch result { - case .complete: - strongSelf.state = strongSelf.state.withUpdatingAvatar(nil) - if let (layout, navigationHeight) = strongSelf.validLayout { - strongSelf.containerLayoutUpdated(layout: layout, navigationHeight: navigationHeight, transition: .immediate, additive: false) - } - case .progress: - break - } - })) - } - - let presentationData = self.presentationData - let actionSheet = ActionSheetController(presentationData: presentationData) - let items: [ActionSheetItem] = [ - ActionSheetButtonItem(title: presentationData.strings.Settings_RemoveConfirmation, color: .destructive, action: { [weak actionSheet] in - actionSheet?.dismissAnimated() - proceed() - }) - ] - - actionSheet.setItemGroups([ - ActionSheetItemGroup(items: items), - ActionSheetItemGroup(items: [ - ActionSheetButtonItem(title: presentationData.strings.Common_Cancel, color: .accent, font: .bold, action: { [weak actionSheet] in - actionSheet?.dismissAnimated() - }) - ]) - ]) - (self.controller?.navigationController?.topViewController as? ViewController)?.present(actionSheet, in: .window(.root)) - } - private func openAddMember() { guard let data = self.data, let groupPeer = data.peer, let controller = self.controller else { return @@ -10156,6 +9900,7 @@ final class PeerInfoScreenNode: ViewControllerTracingNode, PeerInfoScreenNodePro context: self.context, isDark: false, forCollage: false, + selectionLimit: nil, getSourceRect: { return .zero }, completion: { [weak self] result, transitionView, transitionRect, transitionImage, transitionOut, dismissed in guard let self else { @@ -10168,6 +9913,7 @@ final class PeerInfoScreenNode: ViewControllerTracingNode, PeerInfoScreenNodePro self.openBotPreviewEditor(target: .botPreview(id: self.peerId, language: pane.currentBotPreviewLanguage?.id), source: result, transitionIn: (transitionView, transitionRect, transitionImage)) }, + multipleCompletion: { _ in }, dismissed: {}, groupsPresented: {} ) @@ -10320,7 +10066,7 @@ final class PeerInfoScreenNode: ViewControllerTracingNode, PeerInfoScreenNodePro } switch section { case .avatar: - self.openAvatarForEditing() + self.controller?.openAvatarForEditing() case .edit: self.headerNode.navigationButtonContainer.performAction?(.edit, nil, nil) case .proxy: @@ -10393,12 +10139,12 @@ final class PeerInfoScreenNode: ViewControllerTracingNode, PeerInfoScreenNodePro ) } }, requestPublicPhotoSetup: { [weak self] completion in - if let strongSelf = self { - strongSelf.openAvatarForEditing(mode: .fallback, completion: completion) + if let self { + self.controller?.openAvatarForEditing(mode: .fallback, completion: completion) } }, requestPublicPhotoRemove: { [weak self] completion in - if let strongSelf = self { - strongSelf.openAvatarRemoval(mode: .fallback, completion: completion) + if let self { + self.controller?.openAvatarRemoval(mode: .fallback, completion: completion) } })) } @@ -10430,7 +10176,7 @@ final class PeerInfoScreenNode: ViewControllerTracingNode, PeerInfoScreenNodePro guard let self else { return } - let controller = self.context.sharedContext.makePremiumGiftController(context: self.context, source: .settings(birthdays), completion: nil) + let controller = self.context.sharedContext.makePremiumGiftController(context: self.context, source: .settings(birthdays), transfer: false, completion: nil) self.controller?.push(controller) }) case .stickers: @@ -11593,11 +11339,11 @@ final class PeerInfoScreenNode: ViewControllerTracingNode, PeerInfoScreenNodePro } private func suggestPhoto() { - self.openAvatarForEditing(mode: .suggest) + self.controller?.openAvatarForEditing(mode: .suggest) } private func setCustomPhoto() { - self.openAvatarForEditing(mode: .custom) + self.controller?.openAvatarForEditing(mode: .custom) } private func resetCustomPhoto() { @@ -12286,7 +12032,7 @@ final class PeerInfoScreenNode: ViewControllerTracingNode, PeerInfoScreenNodePro } } - private func updateNavigationExpansionPresentation(isExpanded: Bool, animated: Bool) { + func updateNavigationExpansionPresentation(isExpanded: Bool, animated: Bool) { /*if let controller = self.controller { if animated { UIView.transition(with: controller.controllerNode.headerNode.navigationButtonContainer.view, duration: 0.3, options: [.transitionCrossDissolve], animations: { @@ -12521,7 +12267,7 @@ final class PeerInfoScreenNode: ViewControllerTracingNode, PeerInfoScreenNodePro } public final class PeerInfoScreenImpl: ViewController, PeerInfoScreen, KeyShortcutResponder { - private let context: AccountContext + let context: AccountContext fileprivate let updatedPresentationData: (initial: PresentationData, signal: Signal)? public let peerId: PeerId private let avatarInitiallyExpanded: Bool @@ -12529,19 +12275,19 @@ public final class PeerInfoScreenImpl: ViewController, PeerInfoScreen, KeyShortc private let nearbyPeerDistance: Int32? private let reactionSourceMessageId: MessageId? private let callMessages: [Message] - private let isSettings: Bool - private let isMyProfile: Bool + let isSettings: Bool + let isMyProfile: Bool private let hintGroupInCommon: PeerId? private weak var requestsContext: PeerInvitationImportersContext? fileprivate let starsContext: StarsContext? private let switchToRecommendedChannels: Bool private let switchToGifts: Bool - private let chatLocation: ChatLocation + let chatLocation: ChatLocation private let chatLocationContextHolder = Atomic(value: nil) public weak var parentController: TelegramRootControllerInterface? - fileprivate var presentationData: PresentationData + private(set) var presentationData: PresentationData private var presentationDataDisposable: Disposable? private let cachedDataPromise = Promise() @@ -12553,7 +12299,7 @@ public final class PeerInfoScreenImpl: ViewController, PeerInfoScreen, KeyShortc private var tabBarItemDisposable: Disposable? - fileprivate var controllerNode: PeerInfoScreenNode { + var controllerNode: PeerInfoScreenNode { return self.displayNode as! PeerInfoScreenNode } @@ -13020,7 +12766,7 @@ public final class PeerInfoScreenImpl: ViewController, PeerInfoScreen, KeyShortc public func openAvatarSetup() { let proceed = { [weak self] in - self?.controllerNode.openAvatarForEditing() + self?.openAvatarForEditing() } if !self.isNodeLoaded { self.loadDisplayNode() @@ -13031,21 +12777,7 @@ public final class PeerInfoScreenImpl: ViewController, PeerInfoScreen, KeyShortc proceed() } } - - public func updateProfilePhoto(_ image: UIImage, mode: PeerInfoAvatarEditingMode) { - if !self.isNodeLoaded { - self.loadDisplayNode() - } - self.controllerNode.updateProfilePhoto(image, mode: mode) - } - - 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 openPeer(context: AccountContext, peerId: PeerId, navigation: ChatControllerInteractionNavigateToPeer, navigationController: NavigationController) { let _ = (context.engine.data.get(TelegramEngine.EngineData.Item.Peer.Peer(id: peerId)) |> deliverOnMainQueue).startStandalone(next: { peer in diff --git a/submodules/TelegramUI/Components/PeerInfo/PeerInfoScreen/Sources/PeerInfoScreenAvatarSetup.swift b/submodules/TelegramUI/Components/PeerInfo/PeerInfoScreen/Sources/PeerInfoScreenAvatarSetup.swift new file mode 100644 index 0000000000..49826fe63e --- /dev/null +++ b/submodules/TelegramUI/Components/PeerInfo/PeerInfoScreen/Sources/PeerInfoScreenAvatarSetup.swift @@ -0,0 +1,607 @@ +import Foundation +import UIKit +import Display +import SwiftSignalKit +import TelegramCore +import AccountContext +import MediaEditor +import MediaEditorScreen +import CameraScreen +import Photos +import PeerInfoAvatarListNode +import MapResourceToAvatarSizes +import AvatarEditorScreen +import OverlayStatusController +import UndoUI +import PeerAvatarGalleryUI +import PresentationDataUtils + +extension PeerInfoScreenImpl { + func openAvatarForEditing(mode: PeerInfoAvatarEditingMode = .generic, fromGallery: Bool = false, completion: @escaping (UIImage?) -> Void = { _ in }) { + guard let data = self.controllerNode.data, let peer = data.peer, mode != .generic || canEditPeerInfo(context: self.context, peer: peer, chatLocation: self.chatLocation, threadData: data.threadData) else { + return + } + self.view.endEditing(true) + + let peerId = self.peerId + var isForum = false + if let peer = peer as? TelegramChannel, peer.flags.contains(.isForum) { + isForum = true + } + + var currentIsVideo = false + var emojiMarkup: TelegramMediaImage.EmojiMarkup? + let item = self.controllerNode.headerNode.avatarListNode.listContainerNode.currentItemNode?.item + if let item = item, case let .image(_, _, videoRepresentations, _, _, emojiMarkupValue) = item { + currentIsVideo = !videoRepresentations.isEmpty + emojiMarkup = emojiMarkupValue + } + + let _ = isForum + let _ = currentIsVideo + + let _ = (self.context.engine.data.get( + TelegramEngine.EngineData.Item.Peer.Peer(id: peerId) + ) + |> deliverOnMainQueue).startStandalone(next: { [weak self] peer in + guard let self, let peer else { + return + } + + let keyboardInputData = Promise() + keyboardInputData.set(AvatarEditorScreen.inputData(context: self.context, isGroup: peer.id.namespace != Namespaces.Peer.CloudUser)) + + var hasPhotos = false + if !peer.profileImageRepresentations.isEmpty { + hasPhotos = true + } + + var hasDeleteButton = false + if case .generic = mode { + hasDeleteButton = hasPhotos && !fromGallery + } else if case .custom = mode { + hasDeleteButton = peer.profileImageRepresentations.first?.isPersonal == true + } else if case .fallback = mode { + if let cachedData = data.cachedData as? CachedUserData, case let .known(photo) = cachedData.fallbackPhoto { + hasDeleteButton = photo != nil + } + } + + let _ = hasDeleteButton + + let parentController = (self.context.sharedContext.mainWindow?.viewController as? NavigationController)?.topViewController as? ViewController + + var dismissImpl: (() -> Void)? + let mainController = self.context.sharedContext.makeAvatarMediaPickerScreen(context: self.context, getSourceRect: { return nil }, canDelete: hasDeleteButton, performDelete: { [weak self] in + self?.openAvatarRemoval(mode: mode, peer: peer, item: item) + }, completion: { result, transitionView, transitionRect, transitionImage, fromCamera, transitionOut, cancelled in + let subject: Signal + if let asset = result as? PHAsset { + subject = .single(.asset(asset)) + } else if let image = result as? UIImage { + subject = .single(.image(image: image, dimensions: PixelDimensions(image.size), additionalImage: nil, additionalImagePosition: .bottomRight)) + } else if let result = result as? Signal { + subject = result + |> map { value -> MediaEditorScreenImpl.Subject? in + switch value { + case .pendingImage: + return nil + case let .image(image): + return .image(image: image.image, dimensions: PixelDimensions(image.image.size), additionalImage: nil, additionalImagePosition: .topLeft) + case let .video(video): + return .video(videoPath: video.videoPath, thumbnail: video.coverImage, mirror: video.mirror, additionalVideoPath: nil, additionalThumbnail: nil, dimensions: video.dimensions, duration: video.duration, videoPositionChanges: [], additionalVideoPosition: .topLeft) + default: + return nil + } + } + } else { + let peerType: AvatarEditorScreen.PeerType + if mode == .suggest { + peerType = .suggest + } else if case .legacyGroup = peer { + peerType = .group + } else if case let .channel(channel) = peer { + if case .group = channel.info { + peerType = channel.flags.contains(.isForum) ? .forum : .group + } else { + peerType = .channel + } + } else { + peerType = .user + } + let controller = AvatarEditorScreen(context: self.context, inputData: keyboardInputData.get(), peerType: peerType, markup: emojiMarkup) + //controller.imageCompletion = imageCompletion + //controller.videoCompletion = videoCompletion + parentController?.push(controller) + //isFromEditor = true + return + } + + let editorController = MediaEditorScreenImpl( + context: self.context, + mode: .avatarEditor, + subject: subject, + transitionIn: fromCamera ? .camera : transitionView.flatMap({ .gallery( + MediaEditorScreenImpl.TransitionIn.GalleryTransitionIn( + sourceView: $0, + sourceRect: transitionRect, + sourceImage: transitionImage + ) + ) }), + transitionOut: { finished, isNew in + if !finished, let transitionView { + return MediaEditorScreenImpl.TransitionOut( + destinationView: transitionView, + destinationRect: transitionView.bounds, + destinationCornerRadius: 0.0 + ) + } + return nil + }, completion: { [weak self] result, commit in + dismissImpl?() + + switch result.media { + case let .image(image, _): + self?.updateProfilePhoto(image, mode: mode) + commit({}) + case let .video(video, coverImage, values, _, _): + if let coverImage { + self?.updateProfileVideo(coverImage, asset: video, adjustments: values, mode: mode) + } + commit({}) + default: + break + } + } as (MediaEditorScreenImpl.Result, @escaping (@escaping () -> Void) -> Void) -> Void + ) + editorController.cancelled = { _ in + cancelled() + } + self.push(editorController) + }, dismissed: { + + }) + dismissImpl = { [weak mainController] in + if let mainController, let navigationController = mainController.navigationController { + var viewControllers = navigationController.viewControllers + viewControllers = viewControllers.filter { c in + return !(c is CameraScreen) && c !== mainController + } + navigationController.setViewControllers(viewControllers, animated: false) + } + } + mainController.navigationPresentation = .flatModal + mainController.supportedOrientations = ViewControllerSupportedOrientations(regularSize: .all, compactSize: .portrait) + self.push(mainController) + }) + } + + func openAvatarRemoval(mode: PeerInfoAvatarEditingMode, peer: EnginePeer? = nil, item: PeerInfoAvatarListItem? = nil, completion: @escaping () -> Void = {}) { + let proceed = { [weak self] in + guard let strongSelf = self else { + return + } + + completion() + + if let item = item { + strongSelf.controllerNode.deleteProfilePhoto(item) + } + if mode != .fallback { + if let peer = peer, let _ = peer.smallProfileImage { + strongSelf.controllerNode.state = strongSelf.controllerNode.state.withUpdatingAvatar(nil) + if let (layout, navigationHeight) = strongSelf.controllerNode.validLayout { + strongSelf.controllerNode.containerLayoutUpdated(layout: layout, navigationHeight: navigationHeight, transition: .immediate, additive: false) + } + } + } + let postbox = strongSelf.context.account.postbox + let signal: Signal + if case .custom = mode { + signal = strongSelf.context.engine.contacts.updateContactPhoto(peerId: strongSelf.peerId, resource: nil, videoResource: nil, videoStartTimestamp: nil, markup: nil, mode: .custom, mapResourceToAvatarSizes: { resource, representations in + return mapResourceToAvatarSizes(postbox: postbox, resource: resource, representations: representations) + }) + } else if case .fallback = mode { + signal = strongSelf.context.engine.accountData.removeFallbackPhoto(reference: nil) + |> castError(UploadPeerPhotoError.self) + |> map { _ in + return .complete([]) + } + } else { + signal = strongSelf.context.engine.peers.updatePeerPhoto(peerId: strongSelf.peerId, photo: nil, mapResourceToAvatarSizes: { resource, representations in + return mapResourceToAvatarSizes(postbox: postbox, resource: resource, representations: representations) + }) + } + strongSelf.controllerNode.updateAvatarDisposable.set((signal + |> deliverOnMainQueue).startStrict(next: { result in + guard let strongSelf = self else { + return + } + switch result { + case .complete: + strongSelf.controllerNode.state = strongSelf.controllerNode.state.withUpdatingAvatar(nil) + if let (layout, navigationHeight) = strongSelf.controllerNode.validLayout { + strongSelf.controllerNode.containerLayoutUpdated(layout: layout, navigationHeight: navigationHeight, transition: .immediate, additive: false) + } + case .progress: + break + } + })) + } + + let presentationData = self.presentationData + let actionSheet = ActionSheetController(presentationData: presentationData) + let items: [ActionSheetItem] = [ + ActionSheetButtonItem(title: presentationData.strings.Settings_RemoveConfirmation, color: .destructive, action: { [weak actionSheet] in + actionSheet?.dismissAnimated() + proceed() + }) + ] + + actionSheet.setItemGroups([ + ActionSheetItemGroup(items: items), + ActionSheetItemGroup(items: [ + ActionSheetButtonItem(title: presentationData.strings.Common_Cancel, color: .accent, font: .bold, action: { [weak actionSheet] in + actionSheet?.dismissAnimated() + }) + ]) + ]) + (self.navigationController?.topViewController as? ViewController)?.present(actionSheet, in: .window(.root)) + } + + public func updateProfilePhoto(_ image: UIImage, mode: PeerInfoAvatarEditingMode) { + guard let data = image.jpegData(compressionQuality: 0.6) else { + return + } + + if self.controllerNode.headerNode.isAvatarExpanded { + self.controllerNode.headerNode.ignoreCollapse = true + self.controllerNode.headerNode.updateIsAvatarExpanded(false, transition: .immediate) + self.controllerNode.updateNavigationExpansionPresentation(isExpanded: false, animated: true) + } + self.controllerNode.scrollNode.view.setContentOffset(CGPoint(), animated: false) + + let resource = LocalFileMediaResource(fileId: Int64.random(in: Int64.min ... Int64.max)) + self.context.account.postbox.mediaBox.storeResourceData(resource.id, data: data) + let representation = TelegramMediaImageRepresentation(dimensions: PixelDimensions(width: 640, height: 640), resource: resource, progressiveSizes: [], immediateThumbnailData: nil, hasVideo: false, isPersonal: mode == .custom ? true : false) + + if [.suggest, .fallback].contains(mode) { + } else { + self.controllerNode.state = self.controllerNode.state.withUpdatingAvatar(.image(representation)) + } + + if let (layout, navigationHeight) = self.controllerNode.validLayout { + self.controllerNode.containerLayoutUpdated(layout: layout, navigationHeight: navigationHeight, transition: mode == .custom ? .animated(duration: 0.2, curve: .easeInOut) : .immediate, additive: false) + } + self.controllerNode.headerNode.ignoreCollapse = false + + let postbox = self.context.account.postbox + let signal: Signal + if self.isSettings || self.isMyProfile { + if case .fallback = mode { + signal = self.context.engine.accountData.updateFallbackPhoto(resource: resource, videoResource: nil, videoStartTimestamp: nil, markup: nil, mapResourceToAvatarSizes: { resource, representations in + return mapResourceToAvatarSizes(postbox: postbox, resource: resource, representations: representations) + }) + } else { + signal = self.context.engine.accountData.updateAccountPhoto(resource: resource, videoResource: nil, videoStartTimestamp: nil, markup: nil, mapResourceToAvatarSizes: { resource, representations in + return mapResourceToAvatarSizes(postbox: postbox, resource: resource, representations: representations) + }) + } + } else if case .custom = mode { + signal = self.context.engine.contacts.updateContactPhoto(peerId: self.peerId, resource: resource, videoResource: nil, videoStartTimestamp: nil, markup: nil, mode: .custom, mapResourceToAvatarSizes: { resource, representations in + return mapResourceToAvatarSizes(postbox: postbox, resource: resource, representations: representations) + }) + } else if case .suggest = mode { + signal = self.context.engine.contacts.updateContactPhoto(peerId: self.peerId, resource: resource, videoResource: nil, videoStartTimestamp: nil, markup: nil, mode: .suggest, mapResourceToAvatarSizes: { resource, representations in + return mapResourceToAvatarSizes(postbox: postbox, resource: resource, representations: representations) + }) + } else { + signal = self.context.engine.peers.updatePeerPhoto(peerId: self.peerId, photo: self.context.engine.peers.uploadedPeerPhoto(resource: resource), mapResourceToAvatarSizes: { resource, representations in + return mapResourceToAvatarSizes(postbox: postbox, resource: resource, representations: representations) + }) + } + + var dismissStatus: (() -> Void)? + if [.suggest, .fallback, .accept].contains(mode) { + let statusController = OverlayStatusController(theme: self.presentationData.theme, type: .loading(cancelled: { [weak self] in + self?.controllerNode.updateAvatarDisposable.set(nil) + dismissStatus?() + })) + dismissStatus = { [weak statusController] in + statusController?.dismiss() + } + if let topController = self.navigationController?.topViewController as? ViewController { + topController.presentInGlobalOverlay(statusController) + } else if let topController = self.parentController?.topViewController as? ViewController { + topController.presentInGlobalOverlay(statusController) + } else { + self.presentInGlobalOverlay(statusController) + } + } + + self.controllerNode.updateAvatarDisposable.set((signal + |> deliverOnMainQueue).startStrict(next: { [weak self] result in + guard let strongSelf = self else { + return + } + switch result { + case .complete: + strongSelf.controllerNode.state = strongSelf.controllerNode.state.withUpdatingAvatar(nil).withAvatarUploadProgress(nil) + case let .progress(value): + strongSelf.controllerNode.state = strongSelf.controllerNode.state.withAvatarUploadProgress(.value(CGFloat(value))) + } + if let (layout, navigationHeight) = strongSelf.controllerNode.validLayout { + strongSelf.controllerNode.containerLayoutUpdated(layout: layout, navigationHeight: navigationHeight, transition: .immediate, additive: false) + } + + if case .complete = result { + dismissStatus?() + + let _ = (strongSelf.context.engine.data.get(TelegramEngine.EngineData.Item.Peer.Peer(id: strongSelf.peerId)) + |> deliverOnMainQueue).startStandalone(next: { [weak self] peer in + if let strongSelf = self, let peer { + switch mode { + case .fallback: + (strongSelf.parentController?.topViewController as? ViewController)?.present(UndoOverlayController(presentationData: strongSelf.presentationData, content: .image(image: image, title: nil, text: strongSelf.presentationData.strings.Privacy_ProfilePhoto_PublicPhotoSuccess, round: true, undoText: nil), elevatedLayout: false, animateInAsReplacement: true, action: { _ in return false }), in: .current) + case .custom: + strongSelf.present(UndoOverlayController(presentationData: strongSelf.presentationData, content: .invitedToVoiceChat(context: strongSelf.context, peer: peer, title: nil, text: strongSelf.presentationData.strings.UserInfo_SetCustomPhoto_SuccessPhotoText(peer.compactDisplayTitle).string, action: nil, duration: 5), elevatedLayout: false, animateInAsReplacement: true, action: { _ in return false }), in: .current) + + let _ = (strongSelf.context.peerChannelMemberCategoriesContextsManager.profilePhotos(postbox: strongSelf.context.account.postbox, network: strongSelf.context.account.network, peerId: strongSelf.peerId, fetch: peerInfoProfilePhotos(context: strongSelf.context, peerId: strongSelf.peerId)) |> ignoreValues).startStandalone() + case .suggest: + if let navigationController = (strongSelf.navigationController as? NavigationController) { + strongSelf.context.sharedContext.navigateToChatController(NavigateToChatControllerParams(navigationController: navigationController, context: strongSelf.context, chatLocation: .peer(peer), keepStack: .default, completion: { _ in + })) + } + case .accept: + (strongSelf.parentController?.topViewController as? ViewController)?.present(UndoOverlayController(presentationData: strongSelf.presentationData, content: .image(image: image, title: strongSelf.presentationData.strings.Conversation_SuggestedPhotoSuccess, text: strongSelf.presentationData.strings.Conversation_SuggestedPhotoSuccessText, round: true, undoText: nil), elevatedLayout: false, animateInAsReplacement: true, action: { [weak self] action in + if case .info = action { + self?.parentController?.openSettings() + } + return false + }), in: .current) + default: + break + } + } + }) + } + })) + } + + public func updateProfileVideo(_ image: UIImage, asset: Any?, adjustments: MediaEditorValues?, mode: PeerInfoAvatarEditingMode) { + guard let data = image.jpegData(compressionQuality: 0.6) else { + return + } + + if self.controllerNode.headerNode.isAvatarExpanded { + self.controllerNode.headerNode.ignoreCollapse = true + self.controllerNode.headerNode.updateIsAvatarExpanded(false, transition: .immediate) + self.controllerNode.updateNavigationExpansionPresentation(isExpanded: false, animated: true) + } + self.controllerNode.scrollNode.view.setContentOffset(CGPoint(), animated: false) + + let photoResource = LocalFileMediaResource(fileId: Int64.random(in: Int64.min ... Int64.max)) + self.context.account.postbox.mediaBox.storeResourceData(photoResource.id, data: data) +// let representation = TelegramMediaImageRepresentation(dimensions: PixelDimensions(width: 640, height: 640), resource: photoResource, progressiveSizes: [], immediateThumbnailData: nil, hasVideo: false, isPersonal: mode == .custom ? true : false) +// +// var markup: UploadPeerPhotoMarkup? = nil +// if let fileId = adjustments?.documentId, let backgroundColors = adjustments?.colors as? [Int32], fileId != 0 { +// if let packId = adjustments?.stickerPackId, let accessHash = adjustments?.stickerPackAccessHash, packId != 0 { +// markup = .sticker(packReference: .id(id: packId, accessHash: accessHash), fileId: fileId, backgroundColors: backgroundColors) +// } else { +// markup = .emoji(fileId: fileId, backgroundColors: backgroundColors) +// } +// } +// +// var uploadVideo = true +// if let _ = markup { +// if let data = self.context.currentAppConfiguration.with({ $0 }).data, let uploadVideoValue = data["upload_markup_video"] as? Bool, uploadVideoValue { +// uploadVideo = true +// } else { +// uploadVideo = false +// } +// } +// +// if [.suggest, .fallback].contains(mode) { +// } else { +// self.controllerNode.state = self.controllerNode.state.withUpdatingAvatar(.image(representation)) +// if !uploadVideo { +// self.controllerNode.state = self.controllerNode.state.withAvatarUploadProgress(.indefinite) +// } +// } +// +// if let (layout, navigationHeight) = self.controllerNode.validLayout { +// self.controllerNode.containerLayoutUpdated(layout: layout, navigationHeight: navigationHeight, transition: mode == .custom ? .animated(duration: 0.2, curve: .easeInOut) : .immediate, additive: false) +// } +// self.controllerNode.headerNode.ignoreCollapse = false +// +// var videoStartTimestamp: Double? = nil +// if let adjustments = adjustments, adjustments.videoStartValue > 0.0 { +// videoStartTimestamp = adjustments.videoStartValue - adjustments.trimStartValue +// } +// +// let account = self.context.account +// let context = self.context +// +// let videoResource: Signal +// if uploadVideo { +// videoResource = Signal { [weak self] subscriber in +// let entityRenderer: LegacyPaintEntityRenderer? = adjustments.flatMap { adjustments in +// if let paintingData = adjustments.paintingData, paintingData.hasAnimation { +// return LegacyPaintEntityRenderer(postbox: account.postbox, adjustments: adjustments) +// } else { +// return nil +// } +// } +// +// let tempFile = EngineTempBox.shared.tempFile(fileName: "video.mp4") +// let uploadInterface = LegacyLiveUploadInterface(context: context) +// let signal: SSignal +// if let url = asset as? URL, url.absoluteString.hasSuffix(".jpg"), let data = try? Data(contentsOf: url, options: [.mappedRead]), let image = UIImage(data: data), let entityRenderer = entityRenderer { +// let durationSignal: SSignal = SSignal(generator: { subscriber in +// let disposable = (entityRenderer.duration()).start(next: { duration in +// subscriber.putNext(duration) +// subscriber.putCompletion() +// }) +// +// return SBlockDisposable(block: { +// disposable.dispose() +// }) +// }) +// signal = durationSignal.map(toSignal: { duration -> SSignal in +// if let duration = duration as? Double { +// return TGMediaVideoConverter.renderUIImage(image, duration: duration, adjustments: adjustments, path: tempFile.path, watcher: nil, entityRenderer: entityRenderer)! +// } else { +// return SSignal.single(nil) +// } +// }) +// } else if let asset = asset as? AVAsset { +// signal = TGMediaVideoConverter.convert(asset, adjustments: adjustments, path: tempFile.path, watcher: uploadInterface, entityRenderer: entityRenderer)! +// } else { +// signal = SSignal.complete() +// } +// +// let signalDisposable = signal.start(next: { next in +// if let result = next as? TGMediaVideoConversionResult { +// if let image = result.coverImage, let data = image.jpegData(compressionQuality: 0.7) { +// account.postbox.mediaBox.storeResourceData(photoResource.id, data: data) +// } +// +// if let timestamp = videoStartTimestamp { +// videoStartTimestamp = max(0.0, min(timestamp, result.duration - 0.05)) +// } +// +// var value = stat() +// if stat(result.fileURL.path, &value) == 0 { +// if let data = try? Data(contentsOf: result.fileURL) { +// let resource: TelegramMediaResource +// if let liveUploadData = result.liveUploadData as? LegacyLiveUploadInterfaceResult { +// resource = LocalFileMediaResource(fileId: liveUploadData.id) +// } else { +// resource = LocalFileMediaResource(fileId: Int64.random(in: Int64.min ... Int64.max)) +// } +// account.postbox.mediaBox.storeResourceData(resource.id, data: data, synchronous: true) +// subscriber.putNext(resource) +// +// EngineTempBox.shared.dispose(tempFile) +// } +// } +// subscriber.putCompletion() +// } else if let strongSelf = self, let progress = next as? NSNumber { +// Queue.mainQueue().async { +// strongSelf.state = strongSelf.state.withAvatarUploadProgress(.value(CGFloat(progress.floatValue * 0.45))) +// if let (layout, navigationHeight) = strongSelf.validLayout { +// strongSelf.containerLayoutUpdated(layout: layout, navigationHeight: navigationHeight, transition: .immediate, additive: false) +// } +// } +// } +// }, error: { _ in +// }, completed: nil) +// +// let disposable = ActionDisposable { +// signalDisposable?.dispose() +// } +// +// return ActionDisposable { +// disposable.dispose() +// } +// } +// } else { +// videoResource = .single(nil) +// } +// +// var dismissStatus: (() -> Void)? +// if [.suggest, .fallback, .accept].contains(mode) { +// let statusController = OverlayStatusController(theme: self.presentationData.theme, type: .loading(cancelled: { [weak self] in +// self?.controllerNode.updateAvatarDisposable.set(nil) +// dismissStatus?() +// })) +// dismissStatus = { [weak statusController] in +// statusController?.dismiss() +// } +// if let topController = self.navigationController?.topViewController as? ViewController { +// topController.presentInGlobalOverlay(statusController) +// } else if let topController = self.parentController?.topViewController as? ViewController { +// topController.presentInGlobalOverlay(statusController) +// } else { +// self.presentInGlobalOverlay(statusController) +// } +// } +// +// let peerId = self.peerId +// let isSettings = self.isSettings +// let isMyProfile = self.isMyProfile +// self.controllerNode.updateAvatarDisposable.set((videoResource +// |> mapToSignal { videoResource -> Signal in +// if isSettings || isMyProfile { +// if case .fallback = mode { +// return context.engine.accountData.updateFallbackPhoto(resource: photoResource, videoResource: videoResource, videoStartTimestamp: videoStartTimestamp, markup: markup, mapResourceToAvatarSizes: { resource, representations in +// return mapResourceToAvatarSizes(postbox: account.postbox, resource: resource, representations: representations) +// }) +// } else { +// return context.engine.accountData.updateAccountPhoto(resource: photoResource, videoResource: videoResource, videoStartTimestamp: videoStartTimestamp, markup: markup, mapResourceToAvatarSizes: { resource, representations in +// return mapResourceToAvatarSizes(postbox: account.postbox, resource: resource, representations: representations) +// }) +// } +// } else if case .custom = mode { +// return context.engine.contacts.updateContactPhoto(peerId: peerId, resource: photoResource, videoResource: videoResource, videoStartTimestamp: videoStartTimestamp, markup: markup, mode: .custom, mapResourceToAvatarSizes: { resource, representations in +// return mapResourceToAvatarSizes(postbox: account.postbox, resource: resource, representations: representations) +// }) +// } else if case .suggest = mode { +// return context.engine.contacts.updateContactPhoto(peerId: peerId, resource: photoResource, videoResource: videoResource, videoStartTimestamp: videoStartTimestamp, markup: markup, mode: .suggest, mapResourceToAvatarSizes: { resource, representations in +// return mapResourceToAvatarSizes(postbox: account.postbox, resource: resource, representations: representations) +// }) +// } else { +// return context.engine.peers.updatePeerPhoto(peerId: peerId, photo: context.engine.peers.uploadedPeerPhoto(resource: photoResource), video: videoResource.flatMap { context.engine.peers.uploadedPeerVideo(resource: $0) |> map(Optional.init) }, videoStartTimestamp: videoStartTimestamp, markup: markup, mapResourceToAvatarSizes: { resource, representations in +// return mapResourceToAvatarSizes(postbox: account.postbox, resource: resource, representations: representations) +// }) +// } +// } +// |> deliverOnMainQueue).startStrict(next: { [weak self] result in +// guard let strongSelf = self else { +// return +// } +// switch result { +// case .complete: +// strongSelf.controllerNode.state = strongSelf.controllerNode.state.withUpdatingAvatar(nil).withAvatarUploadProgress(nil) +// case let .progress(value): +// strongSelf.controllerNode.state = strongSelf.controllerNode.state.withAvatarUploadProgress(.value(CGFloat(0.45 + value * 0.55))) +// } +// if let (layout, navigationHeight) = strongSelf.controllerNode.validLayout { +// strongSelf.controllerNode.containerLayoutUpdated(layout: layout, navigationHeight: navigationHeight, transition: .immediate, additive: false) +// } +// +// if case .complete = result { +// dismissStatus?() +// +// let _ = (strongSelf.context.engine.data.get(TelegramEngine.EngineData.Item.Peer.Peer(id: strongSelf.peerId)) +// |> deliverOnMainQueue).startStandalone(next: { [weak self] peer in +// if let strongSelf = self, let peer { +// switch mode { +// case .fallback: +// (strongSelf.parentController?.topViewController as? ViewController)?.present(UndoOverlayController(presentationData: strongSelf.presentationData, content: .image(image: image, title: nil, text: strongSelf.presentationData.strings.Privacy_ProfilePhoto_PublicVideoSuccess, round: true, undoText: nil), elevatedLayout: false, animateInAsReplacement: true, action: { _ in return false }), in: .current) +// case .custom: +// strongSelf.present(UndoOverlayController(presentationData: strongSelf.presentationData, content: .invitedToVoiceChat(context: strongSelf.context, peer: peer, title: nil, text: strongSelf.presentationData.strings.UserInfo_SetCustomPhoto_SuccessVideoText(peer.compactDisplayTitle).string, action: nil, duration: 5), elevatedLayout: false, animateInAsReplacement: true, action: { _ in return false }), in: .current) +// +// let _ = (strongSelf.context.peerChannelMemberCategoriesContextsManager.profilePhotos(postbox: strongSelf.context.account.postbox, network: strongSelf.context.account.network, peerId: strongSelf.peerId, fetch: peerInfoProfilePhotos(context: strongSelf.context, peerId: strongSelf.peerId)) |> ignoreValues).startStandalone() +// case .suggest: +// if let navigationController = (strongSelf.navigationController as? NavigationController) { +// strongSelf.context.sharedContext.navigateToChatController(NavigateToChatControllerParams(navigationController: navigationController, context: strongSelf.context, chatLocation: .peer(peer), keepStack: .default, completion: { _ in +// })) +// } +// case .accept: +// (strongSelf.parentController?.topViewController as? ViewController)?.present(UndoOverlayController(presentationData: strongSelf.presentationData, content: .image(image: image, title: strongSelf.presentationData.strings.Conversation_SuggestedVideoSuccess, text: strongSelf.presentationData.strings.Conversation_SuggestedVideoSuccessText, round: true, undoText: nil), elevatedLayout: false, animateInAsReplacement: true, action: { [weak self] action in +// if case .info = action { +// self?.parentController?.openSettings() +// } +// return false +// }), in: .current) +// default: +// break +// } +// } +// }) +// } +// })) + } +} diff --git a/submodules/TelegramUI/Components/PeerInfo/PeerInfoVisualMediaPaneNode/Sources/PeerInfoGiftsPaneNode.swift b/submodules/TelegramUI/Components/PeerInfo/PeerInfoVisualMediaPaneNode/Sources/PeerInfoGiftsPaneNode.swift index 6cdd4a7c86..f281cf27b6 100644 --- a/submodules/TelegramUI/Components/PeerInfo/PeerInfoVisualMediaPaneNode/Sources/PeerInfoGiftsPaneNode.swift +++ b/submodules/TelegramUI/Components/PeerInfo/PeerInfoVisualMediaPaneNode/Sources/PeerInfoGiftsPaneNode.swift @@ -381,7 +381,7 @@ public final class PeerInfoGiftsPaneNode: ASDisplayNode, PeerInfoPaneNode, UIScr guard let self else { return } - let controller = self.context.sharedContext.makePremiumGiftController(context: self.context, source: .settings(birthdays), completion: nil) + let controller = self.context.sharedContext.makePremiumGiftController(context: self.context, source: .settings(birthdays), transfer: false, completion: nil) controller.navigationPresentation = .modal self.chatControllerInteraction.navigationController()?.pushViewController(controller) }) diff --git a/submodules/TelegramUI/Components/PeerInfo/VerifyAlertController/BUILD b/submodules/TelegramUI/Components/PeerInfo/VerifyAlertController/BUILD new file mode 100644 index 0000000000..788a843cf0 --- /dev/null +++ b/submodules/TelegramUI/Components/PeerInfo/VerifyAlertController/BUILD @@ -0,0 +1,31 @@ +load("@build_bazel_rules_swift//swift:swift.bzl", "swift_library") + +swift_library( + name = "VerifyAlertController", + module_name = "VerifyAlertController", + srcs = glob([ + "Sources/**/*.swift", + ]), + copts = [ + "-warnings-as-errors", + ], + deps = [ + "//submodules/SSignalKit/SwiftSignalKit:SwiftSignalKit", + "//submodules/AsyncDisplayKit:AsyncDisplayKit", + "//submodules/Display:Display", + "//submodules/Postbox:Postbox", + "//submodules/TelegramCore:TelegramCore", + "//submodules/AccountContext:AccountContext", + "//submodules/TelegramPresentationData:TelegramPresentationData", + "//submodules/ComponentFlow", + "//submodules/Components/MultilineTextComponent", + "//submodules/Components/BalancedTextComponent", + "//submodules/Components/ComponentDisplayAdapters", + "//submodules/TelegramUI/Components/TextFieldComponent", + "//submodules/TextFormat", + "//submodules/TelegramUI/Components/PremiumPeerShortcutComponent", + ], + visibility = [ + "//visibility:public", + ], +) diff --git a/submodules/TelegramUI/Components/PeerInfo/VerifyAlertController/Sources/VerifyAlertController.swift b/submodules/TelegramUI/Components/PeerInfo/VerifyAlertController/Sources/VerifyAlertController.swift new file mode 100644 index 0000000000..71e9066557 --- /dev/null +++ b/submodules/TelegramUI/Components/PeerInfo/VerifyAlertController/Sources/VerifyAlertController.swift @@ -0,0 +1,489 @@ +import Foundation +import UIKit +import SwiftSignalKit +import AsyncDisplayKit +import Display +import Postbox +import TelegramCore +import TelegramPresentationData +import AccountContext +import ComponentFlow +import MultilineTextComponent +import BalancedTextComponent +import TextFieldComponent +import ComponentDisplayAdapters +import TextFormat +import PremiumPeerShortcutComponent + +private final class VerifyAlertContentNode: AlertContentNode { + private let context: AccountContext + private var theme: AlertControllerTheme + private var presentationTheme: PresentationTheme + private let strings: PresentationStrings + private let title: String + private let text: String + private let peer: EnginePeer + private let verifierSettings: BotVerifierSettings + private let verifierIcon: TelegramMediaFile? + private let hasInput: Bool + + private let titleView = ComponentView() + private let textView = ComponentView() + private let shortcut = ComponentView() + + private let state = ComponentState() + + private let inputBackgroundNode = ASImageNode() + private let inputField = ComponentView() + private let inputFieldExternalState = TextFieldComponent.ExternalState() + private let inputPlaceholderView = ComponentView() + + private let actionNodesSeparator: ASDisplayNode + private let actionNodes: [TextAlertContentActionNode] + private let actionVerticalSeparators: [ASDisplayNode] + + private let disposable = MetaDisposable() + + private var validLayout: CGSize? + + private let hapticFeedback = HapticFeedback() + + var present: (ViewController) -> () = { _ in } + + var complete: (() -> Void)? { + didSet { +// self.inputFieldNode.complete = self.complete + } + } + + override var dismissOnOutsideTap: Bool { + return self.isUserInteractionEnabled + } + + init(context: AccountContext, theme: AlertControllerTheme, presentationTheme: PresentationTheme, strings: PresentationStrings, actions: [TextAlertAction], title: String, text: String, peer: EnginePeer, verifierSettings: BotVerifierSettings, verifierIcon: TelegramMediaFile?, hasInput: Bool) { + self.context = context + self.theme = theme + self.presentationTheme = presentationTheme + self.strings = strings + self.title = title + self.text = text + self.peer = peer + self.verifierSettings = verifierSettings + self.verifierIcon = verifierIcon + self.hasInput = hasInput + + self.actionNodesSeparator = ASDisplayNode() + self.actionNodesSeparator.isLayerBacked = true + + self.actionNodes = actions.map { action -> TextAlertContentActionNode in + return TextAlertContentActionNode(theme: theme, action: action) + } + + var actionVerticalSeparators: [ASDisplayNode] = [] + if actions.count > 1 { + for _ in 0 ..< actions.count - 1 { + let separatorNode = ASDisplayNode() + separatorNode.isLayerBacked = true + actionVerticalSeparators.append(separatorNode) + } + } + self.actionVerticalSeparators = actionVerticalSeparators + + super.init() + + self.inputBackgroundNode.displaysAsynchronously = false + self.inputBackgroundNode.image = generateStretchableFilledCircleImage(diameter: 16.0, color: presentationTheme.actionSheet.inputHollowBackgroundColor, strokeColor: presentationTheme.actionSheet.inputBorderColor, strokeWidth: UIScreenPixel) + + self.addSubnode(self.actionNodesSeparator) + + if self.hasInput { + self.addSubnode(self.inputBackgroundNode) + } + + for actionNode in self.actionNodes { + self.addSubnode(actionNode) + } + + for separatorNode in self.actionVerticalSeparators { + self.addSubnode(separatorNode) + } + + self.updateTheme(theme) + + self.state._updated = { [weak self] transition, _ in + guard let self, let _ = self.validLayout else { + return + } + self.requestLayout?(transition.containedViewLayoutTransition) + } + } + + deinit { + self.disposable.dispose() + } + + var textAndEntities: (String, [MessageTextEntity]) { + let text = self.inputFieldExternalState.text.string + let entities = generateChatInputTextEntities(self.inputFieldExternalState.text) + return (text, entities) + } + + override func updateTheme(_ theme: AlertControllerTheme) { + self.theme = theme + + self.actionNodesSeparator.backgroundColor = theme.separatorColor + for actionNode in self.actionNodes { + actionNode.updateTheme(theme) + } + for separatorNode in self.actionVerticalSeparators { + separatorNode.backgroundColor = theme.separatorColor + } + + if let size = self.validLayout { + _ = self.updateLayout(size: size, transition: .immediate) + } + } + + override func updateLayout(size: CGSize, transition: ContainedViewLayoutTransition) -> CGSize { + var size = size + size.width = min(size.width, 270.0) + let measureSize = CGSize(width: size.width - 16.0 * 2.0, height: CGFloat.greatestFiniteMagnitude) + + let hadValidLayout = self.validLayout != nil + + self.validLayout = size + + var origin: CGPoint = CGPoint(x: 0.0, y: 16.0) + let spacing: CGFloat = 5.0 + + + let titleSize = self.titleView.update( + transition: .immediate, + component: AnyComponent(MultilineTextComponent( + text: .plain(NSAttributedString(string: self.title, font: Font.semibold(17.0), textColor: self.theme.primaryColor)), + horizontalAlignment: .center, + maximumNumberOfLines: 0 + )), + environment: {}, + containerSize: CGSize(width: measureSize.width, height: 1000.0) + ) + let titleFrame = CGRect(origin: CGPoint(x: floor((size.width - titleSize.width) * 0.5), y: origin.y), size: titleSize) + if let titleComponentView = self.titleView.view { + if titleComponentView.superview == nil { + self.view.addSubview(titleComponentView) + } + titleComponentView.frame = titleFrame + } + origin.y += titleSize.height + 5.0 + + let textSize = self.textView.update( + transition: .immediate, + component: AnyComponent(MultilineTextComponent( + text: .plain(NSAttributedString(string: self.text, font: Font.regular(13.0), textColor: self.theme.primaryColor)), + horizontalAlignment: .center, + maximumNumberOfLines: 0 + )), + environment: {}, + containerSize: CGSize(width: measureSize.width, height: 1000.0) + ) + let textFrame = CGRect(origin: CGPoint(x: floor((size.width - textSize.width) * 0.5), y: origin.y), size: textSize) + if let textComponentView = self.textView.view { + if textComponentView.superview == nil { + self.view.addSubview(textComponentView) + } + textComponentView.frame = textFrame + } + origin.y += textSize.height + 17.0 + + let shortcutSize = self.shortcut.update( + transition: .immediate, + component: AnyComponent(PremiumPeerShortcutComponent( + context: self.context, + theme: self.presentationTheme, + peer: self.peer, + icon: self.verifierIcon, + iconPosition: .left + )), + environment: {}, + containerSize: CGSize(width: measureSize.width, height: 1000.0) + ) + let shortcutFrame = CGRect(origin: CGPoint(x: floor((size.width - shortcutSize.width) * 0.5), y: origin.y), size: shortcutSize) + if let shortcutComponentView = self.shortcut.view { + if shortcutComponentView.superview == nil { + self.view.addSubview(shortcutComponentView) + } + shortcutComponentView.frame = shortcutFrame + } + origin.y += shortcutSize.height + 17.0 + + let actionButtonHeight: CGFloat = 44.0 + var minActionsWidth: CGFloat = 0.0 + let maxActionWidth: CGFloat = floor(size.width / CGFloat(self.actionNodes.count)) + let actionTitleInsets: CGFloat = 8.0 + + var effectiveActionLayout = TextAlertContentActionLayout.horizontal + for actionNode in self.actionNodes { + let actionTitleSize = actionNode.titleNode.updateLayout(CGSize(width: maxActionWidth, height: actionButtonHeight)) + if case .horizontal = effectiveActionLayout, actionTitleSize.height > actionButtonHeight * 0.6667 { + effectiveActionLayout = .vertical + } + switch effectiveActionLayout { + case .horizontal: + minActionsWidth += actionTitleSize.width + actionTitleInsets + case .vertical: + minActionsWidth = max(minActionsWidth, actionTitleSize.width + actionTitleInsets) + } + } + + let insets = UIEdgeInsets(top: 18.0, left: 18.0, bottom: 9.0, right: 18.0) + + var contentWidth = max(titleSize.width, minActionsWidth) + contentWidth = max(contentWidth, 234.0) + + var actionsHeight: CGFloat = 0.0 + switch effectiveActionLayout { + case .horizontal: + actionsHeight = actionButtonHeight + case .vertical: + actionsHeight = actionButtonHeight * CGFloat(self.actionNodes.count) + } + + let resultWidth = contentWidth + insets.left + insets.right + var resultInputHeight: CGFloat = 0.0 + + if self.hasInput { + let inputInset: CGFloat = 16.0 + let inputWidth = resultWidth - inputInset * 2.0 + + var characterLimit: Int = 70 + if let data = self.context.currentAppConfiguration.with({ $0 }).data, let value = data["bot_verification_description_length_limit"] as? Double { + characterLimit = Int(value) + } + + let inputFieldSize = self.inputField.update( + transition: .immediate, + component: AnyComponent(TextFieldComponent( + context: self.context, + theme: self.presentationTheme, + strings: self.strings, + externalState: self.inputFieldExternalState, + fontSize: 14.0, + textColor: self.presentationTheme.actionSheet.inputTextColor, + accentColor: self.presentationTheme.actionSheet.controlAccentColor, + insets: UIEdgeInsets(top: 8.0, left: 2.0, bottom: 8.0, right: 2.0), + hideKeyboard: false, + customInputView: nil, + resetText: nil, + isOneLineWhenUnfocused: false, + characterLimit: characterLimit, + emptyLineHandling: .oneConsecutive, + formatMenuAvailability: .none, + returnKeyType: .default, + lockedFormatAction: { + }, + present: { [weak self] c in + self?.present(c) + }, + paste: { _ in + }, + returnKeyAction: nil, + backspaceKeyAction: nil + )), + environment: {}, + containerSize: CGSize(width: inputWidth, height: 270.0) + ) + self.inputField.parentState = self.state + let inputFieldFrame = CGRect(origin: CGPoint(x: inputInset, y: origin.y), size: inputFieldSize) + if let inputFieldView = self.inputField.view as? TextFieldComponent.View { + if inputFieldView.superview == nil { + self.view.addSubview(inputFieldView) + } + transition.updateFrame(view: inputFieldView, frame: inputFieldFrame) + transition.updateFrame(node: self.inputBackgroundNode, frame: inputFieldFrame) + + if !hadValidLayout { + inputFieldView.activateInput() + } + } + + let placeholderText = self.verifierSettings.customDescription ?? "This page is verified by \(self.verifierSettings.companyName)" + + //TODO:localize + let inputPlaceholderSize = self.inputPlaceholderView.update( + transition: .immediate, + component: AnyComponent( + MultilineTextComponent(text: .plain(NSAttributedString( + string: placeholderText, + font: Font.regular(14.0), + textColor: self.presentationTheme.actionSheet.inputPlaceholderColor + ))) + ), + environment: {}, + containerSize: CGSize(width: inputWidth - 32.0, height: 240.0) + ) + let inputPlaceholderFrame = CGRect(origin: CGPoint(x: inputInset + 10.0, y: floorToScreenPixels(inputFieldFrame.midY - inputPlaceholderSize.height / 2.0)), size: inputPlaceholderSize) + if let inputPlaceholderView = self.inputPlaceholderView.view { + if inputPlaceholderView.superview == nil { + inputPlaceholderView.isUserInteractionEnabled = false + self.view.addSubview(inputPlaceholderView) + } + inputPlaceholderView.frame = inputPlaceholderFrame + inputPlaceholderView.isHidden = self.inputFieldExternalState.hasText + } + resultInputHeight = inputFieldSize.height + 17.0 + } + + let resultSize = CGSize(width: resultWidth, height: titleSize.height + textSize.height + shortcutSize.height + spacing + resultInputHeight + 22.0 + actionsHeight + insets.top + insets.bottom) + + transition.updateFrame(node: self.actionNodesSeparator, frame: CGRect(origin: CGPoint(x: 0.0, y: resultSize.height - actionsHeight - UIScreenPixel), size: CGSize(width: resultSize.width, height: UIScreenPixel))) + + var actionOffset: CGFloat = 0.0 + let actionWidth: CGFloat = floor(resultSize.width / CGFloat(self.actionNodes.count)) + var separatorIndex = -1 + var nodeIndex = 0 + for actionNode in self.actionNodes { + if separatorIndex >= 0 { + let separatorNode = self.actionVerticalSeparators[separatorIndex] + switch effectiveActionLayout { + case .horizontal: + transition.updateFrame(node: separatorNode, frame: CGRect(origin: CGPoint(x: actionOffset - UIScreenPixel, y: resultSize.height - actionsHeight), size: CGSize(width: UIScreenPixel, height: actionsHeight - UIScreenPixel))) + case .vertical: + transition.updateFrame(node: separatorNode, frame: CGRect(origin: CGPoint(x: 0.0, y: resultSize.height - actionsHeight + actionOffset - UIScreenPixel), size: CGSize(width: resultSize.width, height: UIScreenPixel))) + } + } + separatorIndex += 1 + + let currentActionWidth: CGFloat + switch effectiveActionLayout { + case .horizontal: + if nodeIndex == self.actionNodes.count - 1 { + currentActionWidth = resultSize.width - actionOffset + } else { + currentActionWidth = actionWidth + } + case .vertical: + currentActionWidth = resultSize.width + } + + let actionNodeFrame: CGRect + switch effectiveActionLayout { + case .horizontal: + actionNodeFrame = CGRect(origin: CGPoint(x: actionOffset, y: resultSize.height - actionsHeight), size: CGSize(width: currentActionWidth, height: actionButtonHeight)) + actionOffset += currentActionWidth + case .vertical: + actionNodeFrame = CGRect(origin: CGPoint(x: 0.0, y: resultSize.height - actionsHeight + actionOffset), size: CGSize(width: currentActionWidth, height: actionButtonHeight)) + actionOffset += actionButtonHeight + } + + transition.updateFrame(node: actionNode, frame: actionNodeFrame) + + nodeIndex += 1 + } + + return resultSize + } + + func deactivateInput() { + if let inputFieldView = self.inputField.view as? TextFieldComponent.View { + inputFieldView.deactivateInput() + } + } + + func animateError() { + if let inputFieldView = self.inputField.view as? TextFieldComponent.View { + inputFieldView.layer.addShakeAnimation() + } + + self.hapticFeedback.error() + } +} + +public func verifyAlertController(context: AccountContext, updatedPresentationData: (initial: PresentationData, signal: Signal)? = nil, peer: EnginePeer, verifierSettings: BotVerifierSettings, verifierIcon: TelegramMediaFile?, apply: @escaping (String) -> Void) -> AlertController { + let presentationData = updatedPresentationData?.initial ?? context.sharedContext.currentPresentationData.with { $0 } + + var dismissImpl: ((Bool) -> Void)? + var applyImpl: (() -> Void)? + + //TODO:localize + let actions: [TextAlertAction] = [TextAlertAction(type: .genericAction, title: presentationData.strings.Common_Cancel, action: { + dismissImpl?(true) + }), TextAlertAction(type: .defaultAction, title: "Verify", action: { + dismissImpl?(true) + applyImpl?() + })] + + //TODO:localize + let contentNode = VerifyAlertContentNode(context: context, theme: AlertControllerTheme(presentationData: presentationData), presentationTheme: presentationData.theme, strings: presentationData.strings, actions: actions, title: "Verify Account", text: "Do you want to verify this account with your verification mark and description?", peer: peer, verifierSettings: verifierSettings, verifierIcon: verifierIcon, hasInput: true) + contentNode.complete = { + applyImpl?() + } + applyImpl = { [weak contentNode] in + guard let contentNode = contentNode else { + return + } + let (text, _) = contentNode.textAndEntities + apply(text) + } + + let controller = AlertController(theme: AlertControllerTheme(presentationData: presentationData), contentNode: contentNode) + let presentationDataDisposable = (updatedPresentationData?.signal ?? context.sharedContext.presentationData).start(next: { [weak controller] presentationData in + controller?.theme = AlertControllerTheme(presentationData: presentationData) + }) + controller.dismissed = { _ in + presentationDataDisposable.dispose() + } + dismissImpl = { [weak controller] animated in + contentNode.deactivateInput() + if animated { + controller?.dismissAnimated() + } else { + controller?.dismiss() + } + } + + contentNode.present = { [weak controller] c in + controller?.present(c, in: .window(.root)) + } + + return controller +} + +public func removeVerificationAlertController(context: AccountContext, updatedPresentationData: (initial: PresentationData, signal: Signal)? = nil, peer: EnginePeer, verifierSettings: BotVerifierSettings, verifierIcon: TelegramMediaFile?, completion: @escaping () -> Void) -> AlertController { + let presentationData = updatedPresentationData?.initial ?? context.sharedContext.currentPresentationData.with { $0 } + + var dismissImpl: ((Bool) -> Void)? + var applyImpl: (() -> Void)? + + //TODO:localize + let actions: [TextAlertAction] = [TextAlertAction(type: .genericAction, title: presentationData.strings.Common_Cancel, action: { + dismissImpl?(true) + }), TextAlertAction(type: .defaultDestructiveAction, title: "Remove", action: { + dismissImpl?(true) + applyImpl?() + })] + + //TODO:localize + let contentNode = VerifyAlertContentNode(context: context, theme: AlertControllerTheme(presentationData: presentationData), presentationTheme: presentationData.theme, strings: presentationData.strings, actions: actions, title: "Remove Verification", text: "This account is already verified by you. Do you want to remove verification?", peer: peer, verifierSettings: verifierSettings, verifierIcon: verifierIcon, hasInput: false) + applyImpl = { + completion() + } + + let controller = AlertController(theme: AlertControllerTheme(presentationData: presentationData), contentNode: contentNode) + let presentationDataDisposable = (updatedPresentationData?.signal ?? context.sharedContext.presentationData).start(next: { [weak controller] presentationData in + controller?.theme = AlertControllerTheme(presentationData: presentationData) + }) + controller.dismissed = { _ in + presentationDataDisposable.dispose() + } + dismissImpl = { [weak controller] animated in + if animated { + controller?.dismissAnimated() + } else { + controller?.dismiss() + } + } + contentNode.present = { [weak controller] c in + controller?.present(c, in: .window(.root)) + } + return controller +} diff --git a/submodules/TelegramUI/Components/PremiumPeerShortcutComponent/Sources/PremiumPeerShortcutComponent.swift b/submodules/TelegramUI/Components/PremiumPeerShortcutComponent/Sources/PremiumPeerShortcutComponent.swift index 8cd41127c4..6a5bbbf324 100644 --- a/submodules/TelegramUI/Components/PremiumPeerShortcutComponent/Sources/PremiumPeerShortcutComponent.swift +++ b/submodules/TelegramUI/Components/PremiumPeerShortcutComponent/Sources/PremiumPeerShortcutComponent.swift @@ -11,21 +11,29 @@ import EmojiTextAttachmentView import TextFormat public final class PremiumPeerShortcutComponent: Component { + public enum IconPosition { + case left + case right + } + let context: AccountContext let theme: PresentationTheme let peer: EnginePeer let icon: TelegramMediaFile? + let iconPosition: IconPosition public init( context: AccountContext, theme: PresentationTheme, peer: EnginePeer, - icon: TelegramMediaFile? = nil + icon: TelegramMediaFile? = nil, + iconPosition: IconPosition = .right ) { self.context = context self.theme = theme self.peer = peer self.icon = icon + self.iconPosition = iconPosition } public static func ==(lhs: PremiumPeerShortcutComponent, rhs: PremiumPeerShortcutComponent) -> Bool { @@ -38,6 +46,9 @@ public final class PremiumPeerShortcutComponent: Component { if lhs.peer != rhs.peer { return false } + if lhs.iconPosition != rhs.iconPosition { + return false + } return true } @@ -91,18 +102,18 @@ public final class PremiumPeerShortcutComponent: Component { containerSize: CGSize(width: availableSize.width - 50.0, height: availableSize.height) ) + let iconSize = CGSize(width: 20.0, height: 20.0) + let iconSpacing: CGFloat = 2.0 var size = CGSize(width: 30.0 + textSize.width + 20.0, height: 32.0) if let view = self.text.view { if view.superview == nil { self.addSubview(view) } - let textFrame = CGRect(origin: CGPoint(x: 38.0, y: floorToScreenPixels((size.height - textSize.height) / 2.0)), size: textSize) + let textFrame = CGRect(origin: CGPoint(x: component.iconPosition == .left ? 38.0 + iconSize.width + iconSpacing : 38.0, y: floorToScreenPixels((size.height - textSize.height) / 2.0)), size: textSize) view.frame = textFrame } if let icon = component.icon { - let iconSize = CGSize(width: 20.0, height: 20.0) - let iconSpacing: CGFloat = 2.0 let animationLayer: InlineStickerItemLayer if let current = self.animationLayer { animationLayer = current @@ -132,7 +143,7 @@ public final class PremiumPeerShortcutComponent: Component { self.layer.addSublayer(animationLayer) self.animationLayer = animationLayer } - animationLayer.frame = CGRect(origin: CGPoint(x: size.width - 7.0, y: floorToScreenPixels((size.height - iconSize.height) / 2.0)), size: iconSize) + animationLayer.frame = CGRect(origin: CGPoint(x: component.iconPosition == .left ? 38.0 : size.width - 7.0, y: floorToScreenPixels((size.height - iconSize.height) / 2.0)), size: iconSize) size.width += iconSize.width + iconSpacing } else if let animationLayer = self.animationLayer { self.animationLayer = nil diff --git a/submodules/TelegramUI/Components/Settings/PeerNameColorScreen/Sources/PeerNameColorProfilePreviewItem.swift b/submodules/TelegramUI/Components/Settings/PeerNameColorScreen/Sources/PeerNameColorProfilePreviewItem.swift index 346979e7e5..5b4358da3c 100644 --- a/submodules/TelegramUI/Components/Settings/PeerNameColorScreen/Sources/PeerNameColorProfilePreviewItem.swift +++ b/submodules/TelegramUI/Components/Settings/PeerNameColorScreen/Sources/PeerNameColorProfilePreviewItem.swift @@ -223,7 +223,7 @@ final class PeerNameColorProfilePreviewItemNode: ListViewItemNode { transition: .immediate, component: AnyComponent(PeerInfoCoverComponent( context: item.context, - peer: item.peer, + subject: item.peer.flatMap { .peer($0) }, files: item.files, isDark: item.theme.overallDarkAppearance, avatarCenter: avatarFrame.center, diff --git a/submodules/TelegramUI/Components/Settings/WallpaperGridScreen/Sources/ThemeGridSearchContentNode.swift b/submodules/TelegramUI/Components/Settings/WallpaperGridScreen/Sources/ThemeGridSearchContentNode.swift index fdfe787849..fea9ef8c23 100644 --- a/submodules/TelegramUI/Components/Settings/WallpaperGridScreen/Sources/ThemeGridSearchContentNode.swift +++ b/submodules/TelegramUI/Components/Settings/WallpaperGridScreen/Sources/ThemeGridSearchContentNode.swift @@ -585,7 +585,7 @@ final class ThemeGridSearchContentNode: SearchDisplayControllerContentNode { entries.append(.query(i, queries[i])) } - let header = ChatListSearchItemHeader(type: .recentPeers, theme: presentationData.theme, strings: presentationData.strings, actionTitle: presentationData.strings.WebSearch_RecentSectionClear, action: { + let header = ChatListSearchItemHeader(type: .recentPeers, theme: presentationData.theme, strings: presentationData.strings, actionTitle: presentationData.strings.WebSearch_RecentSectionClear, action: { _ in let _ = clearRecentWallpaperSearchQueries(engine: strongSelf.context.engine).start() }) diff --git a/submodules/TelegramUI/Components/StickerPickerScreen/BUILD b/submodules/TelegramUI/Components/StickerPickerScreen/BUILD index 5ad856e097..d1aaf76aac 100644 --- a/submodules/TelegramUI/Components/StickerPickerScreen/BUILD +++ b/submodules/TelegramUI/Components/StickerPickerScreen/BUILD @@ -40,7 +40,6 @@ swift_library( "//submodules/TelegramUI/Components/CameraButtonComponent", "//submodules/ContextUI", "//submodules/UndoUI", - "//submodules/GalleryUI", "//submodules/TelegramUI/Components/TextLoadingEffect", ], visibility = [ diff --git a/submodules/TelegramUI/Components/StickerPickerScreen/Sources/StickerPickerScreen.swift b/submodules/TelegramUI/Components/StickerPickerScreen/Sources/StickerPickerScreen.swift index 9f0f028890..e69fd70984 100644 --- a/submodules/TelegramUI/Components/StickerPickerScreen/Sources/StickerPickerScreen.swift +++ b/submodules/TelegramUI/Components/StickerPickerScreen/Sources/StickerPickerScreen.swift @@ -23,7 +23,6 @@ import BundleIconComponent import LottieComponent import LottieComponentResourceContent import UndoUI -import GalleryUI import TextLoadingEffect import TelegramStringFormatting @@ -752,9 +751,7 @@ public class StickerPickerScreen: ViewController { let message = Message(stableId: 0, stableVersion: 0, id: MessageId(peerId: PeerId(0), namespace: Namespaces.Message.Local, id: 0), globallyUniqueId: nil, groupingKey: nil, groupInfo: nil, threadId: nil, timestamp: 0, flags: [], tags: [], globalTags: [], localTags: [], customTags: [], forwardInfo: nil, author: nil, text: "", attributes: [], media: [file.media], peers: SimpleDictionary(), associatedMessages: SimpleDictionary(), associatedMessageIds: [], associatedMedia: [:], associatedThreadInfo: nil, associatedStories: [:]) - let gallery = GalleryController(context: context, source: .standaloneMessage(message, nil), streamSingleVideo: true, replaceRootController: { _, _ in - }, baseNavigationController: nil) - gallery.setHintWillBePresentedInPreviewingContext(true) + let gallery = context.sharedContext.makeGalleryController(context: context, source: .standaloneMessage(message, nil), streamSingleVideo: true, isPreview: true) var items: [ContextMenuItem] = [] items.append(.action(ContextMenuActionItem(text: presentationData.strings.MediaEditor_AddGif, icon: { theme in @@ -2886,7 +2883,7 @@ private final class ContextControllerContentSourceImpl: ContextControllerContent } func animatedIn() { - if let controller = self.controller as? GalleryController { + if let controller = self.controller as? GalleryControllerProtocol { controller.viewDidAppear(false) } } diff --git a/submodules/TelegramUI/Components/Stories/StoryContainerScreen/Sources/StoryContainerScreen.swift b/submodules/TelegramUI/Components/Stories/StoryContainerScreen/Sources/StoryContainerScreen.swift index d0e5e368a2..12c31e1e8a 100644 --- a/submodules/TelegramUI/Components/Stories/StoryContainerScreen/Sources/StoryContainerScreen.swift +++ b/submodules/TelegramUI/Components/Stories/StoryContainerScreen/Sources/StoryContainerScreen.swift @@ -2098,13 +2098,13 @@ public class StoryContainerScreen: ViewControllerComponentContainer { } } - func dismissWithoutTransitionOut() { + func dismissWithoutTransitionOut(completion: (() -> Void)? = nil) { self.focusedItemPromise.set(.single(nil)) if let componentView = self.node.hostView.componentView as? StoryContainerScreenComponent.View { componentView.dismissWithoutTransitionOut = true } - self.dismiss() + self.dismiss(completion: completion) } override public func dismiss(completion: (() -> Void)? = nil) { @@ -2121,6 +2121,7 @@ public class StoryContainerScreen: ViewControllerComponentContainer { self?.dismiss(animated: false) }) } else { + completion?() self.dismiss(animated: false) } } diff --git a/submodules/TelegramUI/Components/Stories/StoryContainerScreen/Sources/StoryItemSetContainerViewSendMessage.swift b/submodules/TelegramUI/Components/Stories/StoryContainerScreen/Sources/StoryItemSetContainerViewSendMessage.swift index e5a94b82a8..a175b7bbcb 100644 --- a/submodules/TelegramUI/Components/Stories/StoryContainerScreen/Sources/StoryItemSetContainerViewSendMessage.swift +++ b/submodules/TelegramUI/Components/Stories/StoryContainerScreen/Sources/StoryItemSetContainerViewSendMessage.swift @@ -2677,64 +2677,43 @@ final class StoryItemSetContainerSendMessage { navigationController: navigationController, forceExternal: forceExternal, forceUpdate: false, - openPeer: { [weak self, weak view] peerId, navigation in - guard let self, let view, let component = view.component, let controller = component.controller() as? StoryContainerScreen else { + openPeer: { [weak view] peerId, navigation in + guard let view, let component = view.component, let controller = component.controller() as? StoryContainerScreen, let navigationController = controller.navigationController as? NavigationController else { return } - - switch navigation { - case let .chat(_, subject, peekData): - if let navigationController = controller.navigationController as? NavigationController { + let context = component.context + controller.dismissWithoutTransitionOut(completion: { + switch navigation { + case let .chat(_, subject, peekData): if case let .channel(channel) = peerId, channel.flags.contains(.isForum) { - controller.dismissWithoutTransitionOut() - component.context.sharedContext.navigateToForumChannel(context: component.context, peerId: peerId.id, navigationController: navigationController) + context.sharedContext.navigateToForumChannel(context: context, peerId: peerId.id, navigationController: navigationController) } else { - component.context.sharedContext.navigateToChatController(NavigateToChatControllerParams(navigationController: navigationController, context: component.context, chatLocation: .peer(peerId), subject: subject, keepStack: .always, peekData: peekData, pushController: { [weak controller, weak navigationController] chatController, animated, completion in - guard let controller, let navigationController else { - return - } - if "".isEmpty { - navigationController.pushViewController(chatController) - } else { - var viewControllers = navigationController.viewControllers - if let index = viewControllers.firstIndex(where: { $0 === controller }) { - viewControllers.insert(chatController, at: index) - } else { - viewControllers.append(chatController) - } - navigationController.setViewControllers(viewControllers, animated: animated) + context.sharedContext.navigateToChatController(NavigateToChatControllerParams(navigationController: navigationController, context: context, chatLocation: .peer(peerId), subject: subject, keepStack: .always, peekData: peekData, pushController: { [weak navigationController] chatController, animated, completion in + Queue.mainQueue().justDispatch { + navigationController?.pushViewController(chatController) } })) } - } - case .info: - self.navigationActionDisposable.set((component.context.account.postbox.loadedPeerWithId(peerId.id) - |> take(1) - |> deliverOnMainQueue).start(next: { [weak view] peer in - guard let view, let component = view.component else { - return - } - if peer.restrictionText(platform: "ios", contentSettings: component.context.currentContentSettings.with { $0 }) == nil { - if let infoController = component.context.sharedContext.makePeerInfoController(context: component.context, updatedPresentationData: nil, peer: peer, mode: .generic, avatarInitiallyExpanded: false, fromChat: false, requestsContext: nil) { - component.controller()?.push(infoController) + case .info: + let _ = (context.account.postbox.loadedPeerWithId(peerId.id) + |> take(1) + |> deliverOnMainQueue).start(next: { [weak navigationController] peer in + if peer.restrictionText(platform: "ios", contentSettings: context.currentContentSettings.with { $0 }) == nil { + if let infoController = context.sharedContext.makePeerInfoController(context: context, updatedPresentationData: nil, peer: peer, mode: .generic, avatarInitiallyExpanded: false, fromChat: false, requestsContext: nil) { + navigationController?.pushViewController(infoController) + } } - } - })) - case let .withBotStartPayload(startPayload): - if let navigationController = controller.navigationController as? NavigationController { - component.context.sharedContext.navigateToChatController(NavigateToChatControllerParams(navigationController: navigationController, context: component.context, chatLocation: .peer(peerId), botStart: startPayload, keepStack: .always)) + }) + case let .withBotStartPayload(startPayload): + context.sharedContext.navigateToChatController(NavigateToChatControllerParams(navigationController: navigationController, context: context, chatLocation: .peer(peerId), botStart: startPayload, keepStack: .always)) + case let .withAttachBot(attachBotStart): + context.sharedContext.navigateToChatController(NavigateToChatControllerParams(navigationController: navigationController, context: context, chatLocation: .peer(peerId), attachBotStart: attachBotStart)) + case let .withBotApp(botAppStart): + context.sharedContext.navigateToChatController(NavigateToChatControllerParams(navigationController: navigationController, context: context, chatLocation: .peer(peerId), botAppStart: botAppStart)) + default: + break } - case let .withAttachBot(attachBotStart): - if let navigationController = controller.navigationController as? NavigationController { - component.context.sharedContext.navigateToChatController(NavigateToChatControllerParams(navigationController: navigationController, context: component.context, chatLocation: .peer(peerId), attachBotStart: attachBotStart)) - } - case let .withBotApp(botAppStart): - if let navigationController = controller.navigationController as? NavigationController { - component.context.sharedContext.navigateToChatController(NavigateToChatControllerParams(navigationController: navigationController, context: component.context, chatLocation: .peer(peerId), botAppStart: botAppStart)) - } - default: - break - } + }) }, sendFile: nil, sendSticker: nil, diff --git a/submodules/TelegramUI/Images.xcassets/Chat List/PeerVerifiedIconBackground.imageset/Contents.json b/submodules/TelegramUI/Images.xcassets/Chat List/PeerVerifiedIconBackground.imageset/Contents.json index 0b9329e460..2f281563e6 100644 --- a/submodules/TelegramUI/Images.xcassets/Chat List/PeerVerifiedIconBackground.imageset/Contents.json +++ b/submodules/TelegramUI/Images.xcassets/Chat List/PeerVerifiedIconBackground.imageset/Contents.json @@ -1,7 +1,7 @@ { "images" : [ { - "filename" : "verifybadge1_16 (1).pdf", + "filename" : "ver (2).pdf", "idiom" : "universal" } ], diff --git a/submodules/TelegramUI/Images.xcassets/Chat List/PeerVerifiedIconBackground.imageset/ver (2).pdf b/submodules/TelegramUI/Images.xcassets/Chat List/PeerVerifiedIconBackground.imageset/ver (2).pdf new file mode 100644 index 0000000000000000000000000000000000000000..55b9dd56c5381490d27784d2b8406c323bfb9e64 GIT binary patch literal 1417 zcmY!laBvK;I`dFTEr~!5FAK2q*+Jp}3?dH8Gc~f-!dL={^xd9@p<*MYp;wQagA0 zo7jX1=J^+-SS}d|Np-$|zGRZu^7OEomn(04zqM|;{`voP_vh`e|GTCB-$vWJ|99`r ztv9zk^mBWA_Q%3?)9!b%oSo=;ZI18v+w*htvM&@(`u%VF?d;z>rv#*4cMh>Vb+6B3 zlhEZjeg}=WheA}Nl&|VmDT}RY<>*vhb?mR(gfL_FZ$%x`Lr(uIN`HAl;8eAk$pQmkWirvADw9j)@d!A#-6nDT7cBfbCRc94sR&jE$f-(lY7160N>g_GZd81 z-G0{Isd!{oKZm=#u>Hd4GAAdwwl`fly7)Hpsdw{guS%^@GIdq>Y3A*a=GmF|i>=80 z&bI7Z+Ak-_ah3OI&EPohwo)e}3(Rq_a`*^1~G+ z3A&okS|S$9IxI77xw7PaEc2&B7c%-20`v7Wx73?ti}muwT+F!g^~Ia?2Ng}94%*L> zGhteI=2s3wZ^L!PO-st=oAG~;@)9=tIq|7rFC+V`SITwsn{OE8EK=v3m&m#6&JvAt zGr4x%uIr6^5GpUcRd{cYn8~YL%dXm`8b8t|%MeeaRH22i$vrh8Zh0_6xu z#xggtgl8)gxF|5Y={x4-<(CvIM8`riQ%Ge&s)Bw%Vmd5$`KG31COYL;C`2n5=ox?k zf?;R|6GE_HCIzJy=a&{Grxq)KGB_w_f- z2wpbmduOHqovHxxL68E>9_Rd0pk6}^_ZLIT0$5;}p_l~nU@_c6ki#GzcTOxx%*jtj z)ml-Mn#N_IV9o{cK8RE>Gc`3fRR9V@fuVr{n5B>h7cvIA69f=K7G~xcLKc>2LWTxr zz_3BlXlQ5*bUsw5q$n{nC$)$R6nma7z~Im*&d*KNRM5yw(S!tweo%gXi2}&O-~iSS c&a6rWx*l9GCKi=|y<=!-Y09Ol>gw+X0J%&KumAu6 literal 0 HcmV?d00001 diff --git a/submodules/TelegramUI/Images.xcassets/Chat List/PeerVerifiedIconBackground.imageset/verifybadge1_16 (1).pdf b/submodules/TelegramUI/Images.xcassets/Chat List/PeerVerifiedIconBackground.imageset/verifybadge1_16 (1).pdf deleted file mode 100644 index f0c6ef9330..0000000000 --- a/submodules/TelegramUI/Images.xcassets/Chat List/PeerVerifiedIconBackground.imageset/verifybadge1_16 (1).pdf +++ /dev/null @@ -1,107 +0,0 @@ -%PDF-1.7 - -1 0 obj - << >> -endobj - -2 0 obj - << /Length 3 0 R >> -stream -/DeviceRGB CS -/DeviceRGB cs -q -1.000000 0.000000 -0.000000 1.000000 3.000000 3.000000 cm -0.000000 0.000000 0.000000 scn -0.000000 8.400000 m -0.000000 8.960052 0.000000 9.240079 0.108993 9.453991 c -0.204867 9.642153 0.357847 9.795134 0.546009 9.891006 c -0.759921 10.000000 1.039948 10.000000 1.600000 10.000000 c -8.400000 10.000000 l -8.960052 10.000000 9.240079 10.000000 9.453991 9.891006 c -9.642153 9.795134 9.795134 9.642153 9.891006 9.453991 c -10.000000 9.240079 10.000000 8.960052 10.000000 8.400000 c -10.000000 1.600000 l -10.000000 1.039948 10.000000 0.759921 9.891006 0.546009 c -9.795134 0.357847 9.642153 0.204866 9.453991 0.108994 c -9.240079 0.000000 8.960052 0.000000 8.400000 0.000000 c -1.600000 0.000000 l -1.039948 0.000000 0.759921 0.000000 0.546009 0.108994 c -0.357847 0.204866 0.204867 0.357847 0.108993 0.546009 c -0.000000 0.759921 0.000000 1.039948 0.000000 1.600000 c -0.000000 8.400000 l -h -f -n -Q -q -0.707107 0.707107 -0.707107 0.707107 10.928923 -1.999968 cm -0.000000 0.000000 0.000000 scn -0.000000 12.542089 m -0.000000 13.102142 0.000000 13.382169 0.108993 13.596081 c -0.204867 13.784243 0.357847 13.937223 0.546009 14.033096 c -0.759921 14.142090 1.039948 14.142090 1.600000 14.142090 c -8.400000 14.142090 l -8.960052 14.142090 9.240079 14.142090 9.453991 14.033096 c -9.642153 13.937223 9.795134 13.784243 9.891006 13.596081 c -10.000000 13.382169 10.000000 13.102142 10.000000 12.542089 c -10.000000 5.742090 l -10.000000 5.182037 10.000000 4.902011 9.891006 4.688099 c -9.795134 4.499937 9.642153 4.346956 9.453991 4.251083 c -9.240079 4.142090 8.960052 4.142090 8.400000 4.142090 c -1.600000 4.142090 l -1.039948 4.142090 0.759921 4.142090 0.546009 4.251083 c -0.357847 4.346956 0.204867 4.499937 0.108993 4.688099 c -0.000000 4.902011 0.000000 5.182037 0.000000 5.742090 c -0.000000 12.542089 l -h -f -n -Q - -endstream -endobj - -3 0 obj - 1811 -endobj - -4 0 obj - << /Annots [] - /Type /Page - /MediaBox [ 0.000000 0.000000 16.000000 16.000000 ] - /Resources 1 0 R - /Contents 2 0 R - /Parent 5 0 R - >> -endobj - -5 0 obj - << /Kids [ 4 0 R ] - /Count 1 - /Type /Pages - >> -endobj - -6 0 obj - << /Pages 5 0 R - /Type /Catalog - >> -endobj - -xref -0 7 -0000000000 65535 f -0000000010 00000 n -0000000034 00000 n -0000001901 00000 n -0000001924 00000 n -0000002097 00000 n -0000002171 00000 n -trailer -<< /ID [ (some) (id) ] - /Root 6 0 R - /Size 7 ->> -startxref -2230 -%%EOF \ No newline at end of file diff --git a/submodules/TelegramUI/Images.xcassets/Item List/HeaderContextDisclosureArrow.imageset/Contents.json b/submodules/TelegramUI/Images.xcassets/Item List/HeaderContextDisclosureArrow.imageset/Contents.json new file mode 100644 index 0000000000..b360a235bc --- /dev/null +++ b/submodules/TelegramUI/Images.xcassets/Item List/HeaderContextDisclosureArrow.imageset/Contents.json @@ -0,0 +1,12 @@ +{ + "images" : [ + { + "filename" : "menu.pdf", + "idiom" : "universal" + } + ], + "info" : { + "author" : "xcode", + "version" : 1 + } +} diff --git a/submodules/TelegramUI/Images.xcassets/Item List/HeaderContextDisclosureArrow.imageset/menu.pdf b/submodules/TelegramUI/Images.xcassets/Item List/HeaderContextDisclosureArrow.imageset/menu.pdf new file mode 100644 index 0000000000000000000000000000000000000000..8811223960a06e20c49074f89cea9fb65cc8deef GIT binary patch literal 3809 zcmai1c{r497dQ4MQiu>9RF*IXGfa!@k!>v5x53ER#xj@~Aqt6#?6M}ZIqZkCcH4izbcB_K#lrqF2l$1m>NO#mDkf3fXQRGytE#cb zWla&`pL2#kQI(pCs4W`ADj>|_$E?Zzv-%DksD8sT3rfEj5_@Uc`nR zs%}ac$!zD`+=sJxCbYuGZPUDi+-&B>Z*j<1=kT|VSWiu|=c#A#2?=&-)(bP7Li{+% z7v4_d72#rNJ%1$P!j`=pRL?S7R{sFdmEsT1ONsJ>w{8&iF)lI3(f(Az4~#&<`UrDO z(}80b&aLSQo5ItDZCq^Bq&3sXMm(LW0w^|LMz&6|KEPY;-A)d>KnH_QMTO4}Id{ja z^Oalf(hWu+Z?F2I+ym{~2vIGH?;&+<g=AQX+|ECI7zE4TryQx2&r%beFb=K<`9^~jIPN-gkeJQKg)^c6 zb~aN$<}NEni}j{}8xvH_szb<@kwxnkl7p9PkJz3X&=BiE_2)>01xugC8LdzG2|ch< zu12mzb)Ful9?3&2DJa2;JwmN%3Op;3OyfxW!NXi7GeNCLeBobNt z-2gAhf;@5?e2(q8I@DCw0+0b8jO8wX7c7_-mwH+-m2vh(H%2EH9y1lCS}PwkI;+(4 z%HzxuSegG$(pZeVc6yOXel~S<`sDP1CEktWS1(haWT0{sybYO;iKJZVB;R)Ngc@Z~ zGC*C2J__?CoPR2a68`JbfS$K_@aek!VeG?kU!Sd@&Jtg%9yaE@a5eBL%X?K-s61E5 z{t$2osQ-o#Is8e=_aVFxiAxO(dG=}CU|w=wc!LrjJ{F;4y&(nZ|=IPv;`I?0frN6-?#h0{;E`{4)(v-kdWTox`>S#v|rQan4^ zX3C>FhLlan8PRVsxujKqM93!<(*@`6)Q%d=5b*>!?a|l5~=Zu>6s>b(c}&DKe_~iRr*yWPzVCN)KgwtIVVFP-#Y?bt&F#KI?rw zw{a)3(=NW`b~(;)Cb?JYpiW;=b*cSlxF_ebV$k)g#RAlU8f{z9S4mrl&p8|YK}&=o z!mdGcom6eXQtVRNYN>B2ZH6dPEM34vePq1Bvc=NGrz|_|^xZM2psY#83FcI58l=9Q zBIxkaPEOya$~Iw0BB-YE{zkyE>@xXtdY>eJFW(8slzf*H-hG0J)0%7{VeQ)722IQs ze4S;R)sltDVuL|2#-8^*8oZ8rrg{W<7@D=sRS zERmqz@15z(q{4a~3bKoaOE>jyl(xS~czv8Y+ne3n|JJGOvBqP^ohZ=LNX$)VzbExR0gU7u+_cZt_Y(n)xJ+#sAHMQKhuo#s^e zr6Re~v*K!HYQ=VCV8w97PBpZ4V#s1(#6jC;>Y>MC;ai<5pO@b2b+JC*22H=F4SoG< zwtCa$n=_;u`&XV{Pe`19DejO{E^U{1^frh z2k8ax1wwa#i;r7SXaR}PywDuloJz97r-z+cp)SX--e1jJs9kjUHartMzxVs_tje>u zZh?bAU!)`0b`pP#f~F#GMlM8_7#OCX;A|*P_N;ZPeTbLwPFBv0PeQe6DqY2sFQ`j?cuIEBvtr z60#C--H|wp8@$?ex)X)1g`P$CtU{|s9tX${$)A9P`lV9KzMSv3EUz+l48N4PCHN}q zAv7c7dqxU04-u8)`3fg{vQ9Dh8s)vnc*|77;yFAIf&UV8=VFe5z5fWFWU$q;7SH@B zrZ+|(WPp^%-@ys*RU$&hQ1IAX!0w;7(a;)}5hr_q4S1V@4t8 z-sPzs1@G^3R~IN(w0*8TbvD4x(Jg{R^jiVu9;kov?Qdp1J#UzQ7)RQbH!$RbSg8cgX364LO8OtyCUhcj!(mH#_dIz~e z{xVlT+N`sp6T9lX%D&1m7c?AJXidaB%ulxzZ)O=xA6-9+U9B37sC-km)B$=A>I7j1 zldIoGZ?LT%-m=d{sv#$AsMhyQ8_E~9#l{MLkmq8Bk~~DDRTpMT$ETZR-j#jyB!BL8 z9dONT&+UupTiWW_B0jKeuFGGc_)qxH#ErH%0-a zS2@l%1l(^g`qX+pz5Hu`&VxHmuSjD7eQ)2{|q(*P^KODVgw! z`TC9c&J^AI%}ZNx{T(k$#7tt(HWWc3y4SWBt>=_pJna26)IHng(>5$gIOhAX?Ar=FoT6CAwdkB710$|jnEj+V~MZ|In?0h z=+*wn+7rgmg<(L3DKgIoLM{EIwwn+Nm)HFX2b|)(VTMn-x!EUgZSTFvNCeL7@w3lF z2)*8ES$sTem-miD%*d0=omGPQRO;=|1MWS%;?knmaVzMVe(p)(TNUlsEpAx@`p^;y z%PyI!?GnHj7YpQINK4rZ1O4njI)kz@wkxgRWv>{JOb+SlzT zBcw@m)(KYpO+R<(<1bk0H*5z$^tH4!QGsYTfX-FT00+98U1R0nyXf3i3+s&yF!w{b zqIapR78Ia+F{9B?Iure61N+0~|B_lb-Nm0=7WCDO;zL&8X!Zij?bj7GtA3T4Kh4f~ zP2@d;&I;SPA?|Wp)@pSEbpGzD*z?(!!^0&F3sq__Z_JO^bvHWN*9g9fi?HWa=NB)m zET?(}&R1}e6>o`~x(OT+B|R*UjG?^eiH<5A7Z2tPK)S@oGkaRF2~e9n%8{q#3h=B~ zBF(xwq=`|2Q@$-m)2DUvlX;FN@jDMu1 z{OB179o;Qb?X0{XiH`n71 znrjdRrj5*471*+(6KdN`XKhUETe`>u(!dDhQn%h=-uP0_Uf(RX@X{~kM(6jXjUN98xVJB;}HVsU|hox{(m z!s4PIZ7xBgF8mrijL>cvlqMDr*Z~Ui3cKqc0saets{WFGIWj{BVuJ!)(SZQ{2GQF~ zA8@p=zBsfm?Lhh!`2`|T0W=Iy{R#coMWRFh3{D0ZH(I0sJpuX+{pB%;W()mS_<=O! zzdRxS@I=oq@b`iA!2Zr#3xz{@V=w$^iVr}$gB1X%0{HLr>0ba2gDJrP8vb)Z6@Xm{ z@cjuX!V$DU>FXazMe$#|RA96m|Ji+O$GS1!8^B(ts4kP5SS0Ld>vO96&GCZfhXsm{2r5@7*C26@c5V Qgc3|q6@2ulj*0Gn09NRdbpQYW literal 0 HcmV?d00001 diff --git a/submodules/TelegramUI/Images.xcassets/Item List/Ton.imageset/Contents.json b/submodules/TelegramUI/Images.xcassets/Item List/Ton.imageset/Contents.json new file mode 100644 index 0000000000..37df253109 --- /dev/null +++ b/submodules/TelegramUI/Images.xcassets/Item List/Ton.imageset/Contents.json @@ -0,0 +1,12 @@ +{ + "images" : [ + { + "filename" : "ton_symbol.pdf", + "idiom" : "universal" + } + ], + "info" : { + "author" : "xcode", + "version" : 1 + } +} diff --git a/submodules/TelegramUI/Images.xcassets/Item List/Ton.imageset/ton_symbol.pdf b/submodules/TelegramUI/Images.xcassets/Item List/Ton.imageset/ton_symbol.pdf new file mode 100644 index 0000000000000000000000000000000000000000..702e30a928f63db6e13e384f73690324e31dfbf5 GIT binary patch literal 3930 zcmai1c{r4B_cvLal*qo+AjvugGYlpBlBFR@_F)EN8_Qs_WKR-_>^sR4i6VQLY>i?p z*^6jFlznOBH`BM}?e~4J>%ISYo^yT9ea?N(xzF=lpCf|M)RuxsD*%BIkPHZG?*asY z&YS^()!cAsZx1964FYST{V)z_6CHJmin74k7^;r)cB7~@(GFM?`lrSK?cs!T2FXHU z3O^AekQ_x)Q33kkywON^AU>-m1%o&?#@U7S-;9$20IsyRTsKUislG0i7arA-{nE)0C$g0kMAs}+QjZnXo8$$|O$zgbweMExVeBI%&|ajV?--ugok_aL zM%GhK=eKnQjA5w)SM0B-h^eO#43G6F@*o*K4lwoz4ujlO-tK2H@pRL;mzTTm6LR)l zdtRY()^8sG^NWD3r6Gwlfsq7E0^Mu-wYG9ie&@DAeZC zN`DYUe?ygXl%_WP41#7ebT5ye9{|leA|EQ3NB<-ub{bd*@Q47&FyFIdZlb$5Esz!l zVq!D~iQi+uXfP!3plBh2m%I5a4$y1dK`^tk927kG1TfCPq1u6fo6--fozwW37uydl zVQFBAQ$5yi+b_yZpM>NsKgidfB6Dmrly(|{=ip~4CM-@{;T5bHE)$BU9k+#oXpe^$ zebvYF(j!jZ1)gW@P=y#vnu5fE9MNp~u>4iyq7oNV+OosL5e*TE1*eQpkj&*d40YuC zUpb#!2g-A0$G?e`)=VwDke5lC`lS4cb)9`z;(9o#H4T|1<7Pm2>UdIA58JRh>jNIlU1h*!A9mn%Ev8VvS&p%G{LC4=-BX)HIYKaRbExzdE(4b=ufD5mCX|_Y zWzM-evMDntYf`V}!ZnS21hd%k-P1SIsxwV=>NT$#?0|HVA162vSLR^EOWjc72C+F& zw41Lxu)8wFR@7EJ#O!+t`Q8DwHbhuatMTYPM82mHQWt4)r_8y6yCki^yaaEulJTyN z&8P>_V;NI?_W{meK5^hQht_amb&1si%;oTgtnaPsMLeX@Cz=+3B~c5o`}r$+V`gvz zxMjWiPE)lheUW`hyP2Mums>`$lAV2>4V^F6Sl7^xi;QzUvF%TuKfaC%;AzfODJsmL zEfyw~4$Kdylb{3E`I&_iCEs*!mvjxq4oZ*~2QmjnUfY&EQ+sB!9|mX(#U$8yw&rzB zH|O6$*3BFta1y+PFA3L@lnMh1qdVCh6lQU=F0+lN%#YpIOY9L$nz;X{G#e9+iJF&^ z8|{5#VCUCe-pB8->?|!`2|Hn*z;pbTf%aL4=jt8yF7v}t5tEN2v=B$t(xbwTMi z7-rFHYxNjEq%{&XYw}*mx2UnR?znGq!+f3BWZ6_s!(#o~r0Y~}U>g2pK-Mb$J?6dG zs@rN%pSQg`$~PdtDX4k1IiNYY=`ubw@Z2V8BW9~~D}A+Q&HBs4eDunpuM>+3&tIc_ z#(Y1Eg)r{NeV+o%g(ifqh8F7^q~1t*A{CXo3!ZwqJ|i1}ZbwUnff5Ajz+a6Mj1d9- zmBcDyM9O8!5KcFNCc!$CG_nc47DwiNmGKyo zmi9F*36cvB%W`>zlT@yi^}9)YcYL~Ku72%2{#X##HOT(eEPX4lNqmz&xn(zGyEwCe71>ds*I1PT)xKg;_!@(}H~}=EtpFb1T=bY|RF& z2Q2SCJgmf5>%l$tsjAB5*b_0oizANBFY7bAYPUV$RmpWw%pw+U2Je7lpGIL_iNn29 ziOLOA{(-?cRyn7!&e3b}!pbCNdgVZWsc~P=k&z&q?;8gmgb#%GMNPIZo-^M^Y!W^% z*G)BRZE8ht*=;dxF)#a01QwWA;jLFbwG@5J(ElW|BZA$k91E!!s$K5}yaV(AFk^|; zuOoIDxA@6cIS3WR%oUP(sd4>-)jh#C`QHi4(R}gF$Hf#^=S!wPHH*J3``|)Y7;qSM zNbkxSjvQVmcay6gnl;zvZ4$j^yyj!3k4^a=@%0sj8gUL2HWGJZE|Q8p_UAS(y3!Wr zml+pTP0K4v!S~*MOv`2Lqsy9CBA%J}8rud=HXOQ7b}YH-zGkYg9{U7ZUGpVtzy2ZY zW9@xgscUE7JRG#~ve^kTW~>r^>af|~aA+_albysh3y$}znCER__nP_4N& ziRnE}=h*j4g3#oJ)N@n&O>hnfbkkxzV6(Da!37)ttR{E@Dj>hNZT+G#eQW2$kV@8e zH+ghTj6ArOUfx&Nhqg6nc@lhhd)E_&_S;luv2WXSBrR14EG_ZCm|YvV&czGsaSozi z#V2QEiL;V41{AGSvei=4llL<(#GGYPCHm2ro(PH+1ZITv23Et>rgP~-F8g!WqikHe zo|?NrX?igwtR}ic14L7JkYabia?47+sR}WNyF6nYH&4=&} z?zgNxTeQr5+f%;|2WFNMYsEo?{mEzWq~{8UAIi{n1l2n#ll7d zM?!DaX~BH-)lkoc9ddeKwLs`-E+1%3Ef^b1FB=;JcS|A&G`REM>EoD4!a%eK>L(+l zNL1Ddl>JRVf6&KYu-tFh5(L)M&`?MEpiv+yS2Y1yQ{DV9mjAtr%3U?EZdmV&o=6Au z4=Sqx0a3k}P-rNXiT<*I{$=w&NiB@(;%_bsh%-_1n3Q2|TIBey%5jHXZzYS1Jvcn9 zfDs!iw9lx7^A`~v(+GORsNti?KmR_tcEt8QeePZ}S;}-`$88C4&0xI-)^<(e*wZrP zOGecM%dMs05Q(7D**K?0jxf|^QEODL^HBdQ4e$`#7}5TOxGZTcr(@~8^fH%sc=Sz4 zmzdA4q{iziq36DZgg2ALo)Il~cBbwY#?KT&<~jl$dhR-HNOa%raareVo6pOw`6 z_}vr=ztJB0o;m5_tU!+|0qS{Jnc52JX^M*si- literal 0 HcmV?d00001 diff --git a/submodules/TelegramUI/Images.xcassets/Media Editor/Mirror.imageset/Contents.json b/submodules/TelegramUI/Images.xcassets/Media Editor/Mirror.imageset/Contents.json new file mode 100644 index 0000000000..6c0c1d16cb --- /dev/null +++ b/submodules/TelegramUI/Images.xcassets/Media Editor/Mirror.imageset/Contents.json @@ -0,0 +1,21 @@ +{ + "images" : [ + { + "idiom" : "universal", + "scale" : "1x" + }, + { + "idiom" : "universal", + "scale" : "2x" + }, + { + "filename" : "PhotoEditorMirrorIcon@3x.png", + "idiom" : "universal", + "scale" : "3x" + } + ], + "info" : { + "author" : "xcode", + "version" : 1 + } +} diff --git a/submodules/TelegramUI/Images.xcassets/Media Editor/Mirror.imageset/PhotoEditorMirrorIcon@3x.png b/submodules/TelegramUI/Images.xcassets/Media Editor/Mirror.imageset/PhotoEditorMirrorIcon@3x.png new file mode 100644 index 0000000000000000000000000000000000000000..06abfe2818461c4836ef1a31a320f7b367e44e50 GIT binary patch literal 903 zcmV;219<$2P)pdbMeB!yUrg`^Q1NhLO#Mop!HB54wAG?kb_V)hLc9Fz?mod3>C>je<2>cqbPp#NEXW-4UEK1uEcp@^f@ZyMbC1|l_&aP#%!GAMnd}FiJl;D{e@ASWJlHv#oho;{i|^pZQe;~*E~kORIU*B3e$oBZ?;#ro0X9nT)k80PrEC93tJ=gCvIDiFGgJlW@UlAFn!JnF=A8pSBNyIB`G z58Hgx1Ed0@Qv|(@JU!?Evgs=~p3|uCp+2C4T#949;aB<*A&EXlY4qqx@;&6a7taS8 zn2efS!+Q+iv*nsB`WiLSq8IsAzwuNN8d!{y+(I^bmvvPh4YWi>&c_blCnYCA&)y~>YHP^KO|#h1TDy)tw?Zu#+)YH}TJ_~O$J zb0pA=VVa-ZpiV7%g)e=Ia;4~UobuBZmE<<^h7LN;l0q|vL9%+ADl)0dvFrS3l1Ara zS9$f0A@WMJy3CVAGlo{O!;lIxt;?5PXG)_P!!jTGrJ#Dp3}5aKY9m9}Dy-gxy8NTZ z+z@Et*&VieCw2KxJkf*>SG}W?EcDuQwmLM>siH3b?)?Q+DO$FA7wYmCKIW@L%T@0} zUGD2Y+ltXL)w@uaKZ^kkdRqVI zH<7CyEmOT?%vzW0MO&)(G0D1IH#%_jp83rY09@SHqC4FFnEJ3K{)j4e^c}X)$4pF2OiWBn dOiccR`~@9yWvw}X%mx4e002ovPDHLkV1lRZuJ`}| literal 0 HcmV?d00001 diff --git a/submodules/TelegramUI/Images.xcassets/Media Editor/Rotate.imageset/Contents.json b/submodules/TelegramUI/Images.xcassets/Media Editor/Rotate.imageset/Contents.json new file mode 100644 index 0000000000..cedf76ea94 --- /dev/null +++ b/submodules/TelegramUI/Images.xcassets/Media Editor/Rotate.imageset/Contents.json @@ -0,0 +1,21 @@ +{ + "images" : [ + { + "idiom" : "universal", + "scale" : "1x" + }, + { + "idiom" : "universal", + "scale" : "2x" + }, + { + "filename" : "PhotoEditorRotateIcon@3x.png", + "idiom" : "universal", + "scale" : "3x" + } + ], + "info" : { + "author" : "xcode", + "version" : 1 + } +} diff --git a/submodules/TelegramUI/Images.xcassets/Media Editor/Rotate.imageset/PhotoEditorRotateIcon@3x.png b/submodules/TelegramUI/Images.xcassets/Media Editor/Rotate.imageset/PhotoEditorRotateIcon@3x.png new file mode 100644 index 0000000000000000000000000000000000000000..937885fcaa4a2b7dadd6e65bc91849873e6d5b8c GIT binary patch literal 539 zcmV+$0_6RPP)a-<^f6f)jjzYao@mV!x|c=_Z1*Y(nTIPU&;m$R%t2qj9Eq_DyT9v%mB$lE;flZg!QGG}=B zkwD(!j;Z7wyhMk05QJuf8|Ea>F?VIubEGvs+rn|sPRFL`XzVKme1g@&oc>0n$j>e zDPt|;w0N27?XtP5eI9+oM1_Z`v0XG*1cR;V>#uEX2H{F#`VjDe$6_kDC*4!Y-~kK~}c!Fea{qg7lcq>~9Q)y&bH@I|`uILQ=EHN1gOkzLIa?UGI0 zEYXvU(2+i_g>ojwu|+)p}TR7Mx)Va dG#btS%QxTKT#kgUnc)Bc002ovPDHLkV1lIQ4ln=! literal 0 HcmV?d00001 diff --git a/submodules/TelegramUI/Images.xcassets/Peer Info/BotVerify.imageset/Contents.json b/submodules/TelegramUI/Images.xcassets/Peer Info/BotVerify.imageset/Contents.json new file mode 100644 index 0000000000..133b9230e0 --- /dev/null +++ b/submodules/TelegramUI/Images.xcassets/Peer Info/BotVerify.imageset/Contents.json @@ -0,0 +1,12 @@ +{ + "images" : [ + { + "filename" : "verify_30.pdf", + "idiom" : "universal" + } + ], + "info" : { + "author" : "xcode", + "version" : 1 + } +} diff --git a/submodules/TelegramUI/Images.xcassets/Peer Info/BotVerify.imageset/verify_30.pdf b/submodules/TelegramUI/Images.xcassets/Peer Info/BotVerify.imageset/verify_30.pdf new file mode 100644 index 0000000000000000000000000000000000000000..6f37febc61c1eb504addc98ece4a1aced36c671d GIT binary patch literal 7905 zcmaiZWl&sOvo;PvlHg7T2?S^G0fGegL4*6?J`C;}Ah-vI;I4sS!QI_8!2${H@WFYH z-1EJ+>aHJqcRj27S*v^3u6nBbHCRl78OXwejt&H{0ql${(E$Jf0RXFrwG+hA*3bz8 zU=@QnTNp!>CEq=6JuX%Wi>JNExAo(;7{u7l1oG#O9K_bl$sEAW#m)04q5$A{v;+mw zp-zqvLmPD0w7NtKFz!4-ubsav6cL= z#l?_xgi*xF?42}BUScaPp&%ox>iA=#5AgDakup>uzH-`=pDuY{vC>xDf zll=e}ZHp+z=9q`ponxS{=q_f0M}C3ytUmzc&_HalwuGUGW4#298Lgq&rjgh+l)u?0ebdhZ!iPp2{SR4H$}ZK+lZxv*+%(u9CJe3S@*hjCM zN;I(vB9QWZAE_1svS z=!lTx3`t6#k#;1q;hy^%^5~$v?<`m|y`=I)r0~uAE$d2x45o=k zmqG0o1}ZVB0vOQoLvXUWvyYYX3N2L;i?OByz6QkR&?>ze*WkpLm*f~5F&8>T=Oj#z zTnS_mOUhNuN*!O`;oreN#k*z<_8lusT^h94sOK~4~^ z)=pwbUg2%@ugZLi55(H)Urdf!5CF-80ggzc%2a9jiH0;%3%Wk=YDvq>aVGT=mf zVCe!IQ8_SHeVh|+mh+VI6s3xBl8*fRod(_lqv)g4C#M9?ch^$tRH?C=Tjte)&8ePg zOVX{1-lExHO!~w4H(|-usmhX#Vs3I*0Lg@!Xyd`7E$%_JKCZ#D!IoIMKGHsqzKTSB zI(-Ho_4~xz&j=#zV86U?N^_sV+4c&CQifVF#pY#1g~>S@g|5m+pEeqB6#Bva+MxyU zB~Eg?u@i6b#iw(t3w8FmEwRqnVIPC@h{xw@#kAlL=(JdEWHhDc)j@I~?Z$Uk&DE;N zc}9gD>eA};%Aj2K!ea}+9|%#%x5!NdoJ>3Vzx80*#Z3(QT>5oNFyXT}65U~KW@(!W zt?&hESnb!MYsWLDv(CMwX*$9Q0zTF)mLYvt({)5FjZRf+4da#`U_>g(nZWC|DMmH(I^6OfZL;%bTmz>DD1Hyj~!z)gMdPr8qTlCvHg<%*)N*ETA4Qn%JFA z8RweN%}&i-EWDA5D(syKpJW{0pGcjUozX9D6KT_X@PlvnwTL#b|CZIe){-4#*s%V* zlc3XqT8+AXoG;fsH>3y8m}k>z({i(kRs%O*I<}uOZZW^IDBZ%}B50SHV{T|g&cL~^ zbd=2a(42*{g8P+GG%>|TIf=K%UGKV$Ld7G+!@C(}y$0V5wj{ny)Gzy48e3*r8eEo8 zdRGQ5T`Ya52G*@FsLn0vifL}un4jd#h*#JQTVK_O*!-GTI!IhNsNAo&2Wa!-wHOY^;1)HY5&b?6z=L$vz911>s9BFZBihNP5+*(8WIyJ6S z;*7_1r$NPN%a zLtstbOxZveNK#F^WzyFK9v#aWTgUHVq+$HpvDKN=x!180;t@m`B1_Fg%`LI?K{X0b ztX^V0rz6KQH~+nR#ZsFi(*g@0tGj)|c=1oQS@n_%1wAkCh+C48Pc^{gxt&O@)a| z*tNPjw#28thEU=^;XfL*)Uhw5@c=&W{CU{0+#-H19&%xD zfp&p;2wU{X(Wr9OJ=$r_yZI!$^ZM$w-9^Q`PuW!cX&?Lsd_TO!d~Eehz%}Xx*{x0n zSP;CfIj&Kp)L3$SN4b)H-+3598fi{J&wIRExVF>6uv+}XvU6|3c+NPbH)A?*`t-K% zwyIpcr9SI?&|%$SH*^hm8TK3oqvKK_nC?7_y$)3wFR*>sI#aPi%*`%V%Bx!A+Z}32 zB?q%<<>^f$%`1+b_Yx=aO{wy2^LH~-R;!DigX`-1Hoc2BUg0-<*UQ(eD@R|U-&t-B zBm5wUUIC0Kzp%t#NdG!Ij967{eYh6+5sFtNv zzTpB&#SqEHTvnga%exbeLym!(iS32a{T`d1MLJJf+cu3Wqo0t4!Ib`HA-sn|Psr_F zlF;%)GYB8Zb*^>leqPbRV`&%JR_C?IMb3G5sXNe=a&h%)N-*uR?{@Bl{&w;trF67m z6rwNJTI+>%d2P=PaX#mNVbp$ZI(|?_esDm{jcN6jP$-gGnqU$#5}EKRjRAXHbb?n$ zFkK`mCE+1eG4w5(@SroC>MPH1O7u@YLmt&2k+n=@pZ6|AjV5|lywdm4hd?{s(I=xo@L5Xqz#27;Xqv05vH>v|S(4$%odHwtel))#j?? zOy-Pz4lbKAsh636{F)%6R;j)iSeJALKY2`P??=@b4S+PTfLdYVx$nmYac-!rh)EZ< zamLU{D-a3pJ;Jx7ocZ~b;`w>jJ|=J{oRiEwas=8qH9ExB#r&V5mivj~-&z(vVnI5RrYQk=%&nY?8_cqwTBQyyd`$x$TPuJQxj}Ve zkv2-}ZseaVB3H`#tY z)BfzK#*86BcCAmuq;YbA0}11-L&^(u#VCuFFI5gLh(djxF!!G~0?N0i7rSYl zPTpDG?e|}eCfyab``J&NF;Cz%^Qmb+y_#enokY=`b98MxGe+pZgUNSjta2<3*lSMS71!dGTLxRg)5&1u&}( z@Z0bnM%k?98-_&QxXE!u_8RMOy9C-W(gj_+Voo-2Y;P1}Sos_nnQC2Nb+Gi-3iDM2 z-?py5DjI4^+w}hMMZTfzSvun8<|)+u<4L`INgP&c#T=atFKw7P?x;}W=9)W`U$1~d z-8U zJn#6zJC>)M@nV!&v!La2>ep-JXAR{DOx;ND{kYR{?Nh`yu95i?y*??7)lw{$dh2HO zM5R}{;o{84DRVK2BIL{#2({%ORUJ|{p~%yx=G^1H&DbHj`^DSrKtQpW|G5<^SU;{< zAOsc+Ua^0Z1`8FXS8nYdW@oI3sL3ho5*}6J%#*qO;BAk#ZATrkXiS8x3AL};=F##z z5_{hk?2uTzEL3UpYD8tK+66{qty^!wYUn{-WHI*6WC9e}@GZaniWl0DRfD${X*B3FW*Z zL*Le8qdTzVd=>uz{5Ao7{(#dfn9^N9sqP5>ZVk+Pf$$KqIiwyp9!SAdCs$zInVa3o zf89=*hL6DW(sb%K_4_NtrCum;SP#jzb8zR2HB`5T`qrXB*yMv7d}`7?!k*v0LfP|r zQ(b|j<`sAMXH^6gEg(W~c`1`7y>z^9%)hUbMMkklq2B-;;_yIM_{U!;Aug~W6lDoV zoLK4CaImM;q?y7UoOks5axEL$irBdl=U}T><1$zpUzCG(wCF%tMnI_hs|(>?PxO3@ zsVm){COfw3Hc1Wox}jpa`3<{*8g*d!Ks<_>58Na9^w|O$qqHTlce<}^_eg~I6LzK@ zFz#7xx!mBmAi(ktwZT5Vc&9@2lW5if+5G&J56fboPL{DyM6ukv^L`wu?qlvS;ce$} z0}}X%MQh*ZQ(R2UOiE@C8ncvmhP-uE&n?e1La0p1qlSnoE%Z|a=W35uiLK{OjRflO zw}0R(!k~JR#Eh~RcT3MYM$^*sMOW;Ld9tcM>=2HaVTbY^UfHG)mG(x@DD~dX?f)=0 z*4o0$r^k5bnjHDIs3BrfPv@q;YQQk7Ylb!|N`zYamRpX)qa4?Ipi0Ofom-jY069GV z0-sv>RE!br<4K;5zkZz=yG0FuX1WpZdvY*MA20c*9y=$Ru7p^_h>vOR18EnX0B3PW ze}euTMH|++9N#F0XD0el#5qcta+uip`O|0Z(fi521Lj9RIAc>K*i%yjqtU#{a72}EBO?3OKbZDbk)Z^2!=mH~jEf?P%0Cf+CTl;K ziZ5OutFVW;3CT5=`b{{~a1qJ8Pei_^mqHO40`>UsWTg(A82Bst(Cnnma-;zo<=dc= z-m7zzGZt@|2`zRq7vsqH$tGn;56GRf~1lfF+)itKsHaPFuk;>mqi)ZNg8FPcG@%2Rmz zo!dz4^RNhm-^MQ>RVg?`JZ~4L0-IRZTL{LjqyziGJ(-u0&8<-h9%(|Cqzuib8+uua z>uJH&)n!+3I*V9G)LdJr1tQ1QbRYgoYat9lNfmZRVm>all>X*R|87BP=bn=O;hxx`ji>tJHiooZHjkc z3u#$GNrCXyVpHn9v?_6+l1tGd1UD!@9zRlZB8xm`9HahllL~(BaV7I_Vq`L4da-9# zmkcHIK)i|)?Ex-PU5s4|{Ovt{A%S;evr=6o4h{nx(W>+>r zdSZ@}x0!~-FRUFWhHSxgxN5KYqFc4U8!`Q;s4QgaLGR1JXEMvw!M%#GLhUw3?nhET zW*nuGQBoZ_i_hrOo!X!GC?^SqbUAzVUy){r$E)|z3@w*nC`Fna4B$#5Vg#VXUX{wP zZhct`N-L~43ASR~6oYRpIt^4m5XoI`&x&ZS4;&Ax(w1|SNuC;kUn-4pe_P2-G&Y3k z?vh1yB|-d|yt%kcbs<0wN{(dHlu{|OiH#@t-q@r{WQ{3Gtu$<^1-TtteWikk)93)DS;-&hVx$XS2$>DaWo4XHrHM5R>Jh zOWWexDQHRM1wcATlysRD-k%~un=N6NTAgM&DK@Q1kHbo6T!_v;RU}>X%ZMYB5;q>) z|AxBMB1fS#zZTP2DXh8D7Sf9mZv})?TiUSjMQJ4|k(+YM@!(Py^5{H6cXnhs*diQ@ zEf$Kh3e|_zusk;b<&Lr`5g=5+<$)_x=gd&X^94&vOp0v$U+~bQx4ucc)+~_tb%63c%VI0`hD^&&cv8-c3LJ4BEo5 z4<(jkGVuyDJSkSmZk=p9z97}&=8W*QEh!Qg-YeSB-}W7@R%rZIW9@mwFlE%{!4Oox z{4!BJUW{s7IuFg+HIOJ=;+aDU(9*}}%lNdjNw&&6-kdqx)XaKWT=0&5_M1j%H8&)FdiD)HV9r6Sr5f|2I$>)5t@wSCko3s(xfMfZG zAM^zYouDxB*$pM{`ck*@b!`pL%yI?wYms8OQ{va|nR0eK2jeE{W5XUvV6nU_~{eaeECZ&zUy)9RUftU<)Bh+L=2oGNe zj!P(6cY~m0Zfn(ew{z%n*ojn??%3e2#&slA5_cQ_3;oLMxYm}X5Db@%T5xOLBYvH}hOD*Zzy1EmSg_J37)|yWrpw>bGzfwDqnUA&fvtsC_ z!^@8=r4aqXx&_2KwKDxQNfoT@*&1^w+pU?|1AqC?j3A%YQI&l$P`pA%z&hS1wc^7c zUEO)`w`{{$W=M3#1%<3`kgDI{3|Scps;Fhu`H8eRmpM`ptX%7OM{v@6YKaCCr=JvS z9-bKHn)1h-UrT4rMJDxbp|>7Ns%WIj*z$E}MdLQZRF)^ef%~O%gHJ z7|3VbLu4+68&5bZM5dwU+BI?}KK#h>etits_^X987mt}vRrw&6Cd2eoL4}SP%}^$- z`bLiAW230{xI>sQmSyYoFGJ60P`$xfS%a58C;q&)8yE zlssrsQYlK{6T1%W%bib}Ypeijt% z&8KM;4hFCIvRh}{Y)dIZy?!hC%>hrHx+s(Vhd#Ee>J2j`cO(*ZPO3($&3jE9dG0gN zi3g9WTvc*jLvyCRN-G<4d#kRio}EQRgdP0ccB_aMA!n^;d_gKc**<7hM?U$HuLDE9 zVWL%qP*WW7+68MV!{?+y#OJL#psz=2loYM3JlQmE@0rzsi7_24O;{F4ISRFuVV1bml6nQd2Cg^Kng|E|@uHJg# z3+1sNZ>AI4RPetFB`6eFw`Lu>*3}65!)phgCb36rlfO~jInR3P~|+>OiXhxc>)eC-%X|~Dw8}jt1E#7`Ifc~nRV-KJpC?|m zRxGWWcX@At1Q||jSa7o2qq6>*d=K0RjjieM>%qodc=Jq?5YoUs$c%Ppi8gyGbfej|_{6AJ|n!SRu3DgpfrSJ*UC>*#!XQW)lKy z9YcL>6lmr%`opp?aVBt1%Rhn+R1TIjer)ApR}NdqINep_ZE%n527X7x+wnLwrc!Gv zR7UC2$oCp@1YT+U=);)81lx{djeUD>4B1lfY#}bmY*}|l=67rD7wBir;-(^*_YD#Y z$@#H@J>9Bkc}z_$-aB*hWa)|L=-lZdY&Bk<5d8rE8nyGmoe z{|o}h_G{Y>WrQMW_rW%pQtD^U*HbL#CE|WO8!k)D(bjr0P=`)dHz-J<)|aV zY7~a<<}~bsDNlYAk5t>wLW;sJ;C4`b>SeIumecVFJHh4Dj>^epP7xIQuAf!e()9uL zOTMVy|I+n&>Z<%_=*Y>*2K>7>!}B-%$1Kv;*3JnE(ANEPsHo~@|2UQe8=Cz!ca(>i zSQx&ua|LJv*jU*9SpOIte?$LRe=#XTpms1vV+a)R)IoY`?>r4SMeS^zAhwSuKDCJc z0>Orkj~IaWPw0O%lAfS{2B)$XCXZJIJZ13Iq56vv_81HNulqwEk^jL2`WMqvg3!MY ze7e|w5*IaeGPJfc`**0TBg7P)4FF_A|4;r;PXIR;7Y7&M5&v@m*#Li70NXzyc5cw) zMW5C`5D)u*dGT;P=J;t9|x9KioSkcSiW*u49<7Z>+GfliKw7S<3) z^r!DGE%sQ*H&8np2tDA9g$ezC%E_v1XXgZXD%BsKhE>JF9rBd-A46tCN2fn3!NbkY MiT?VvxT3`W120*x1^@s6 literal 0 HcmV?d00001 diff --git a/submodules/TelegramUI/Images.xcassets/Premium/Collectible/Contents.json b/submodules/TelegramUI/Images.xcassets/Premium/Collectible/Contents.json new file mode 100644 index 0000000000..6e965652df --- /dev/null +++ b/submodules/TelegramUI/Images.xcassets/Premium/Collectible/Contents.json @@ -0,0 +1,9 @@ +{ + "info" : { + "author" : "xcode", + "version" : 1 + }, + "properties" : { + "provides-namespace" : true + } +} diff --git a/submodules/TelegramUI/Images.xcassets/Premium/Collectible/Tradable.imageset/Contents.json b/submodules/TelegramUI/Images.xcassets/Premium/Collectible/Tradable.imageset/Contents.json new file mode 100644 index 0000000000..3a897bad59 --- /dev/null +++ b/submodules/TelegramUI/Images.xcassets/Premium/Collectible/Tradable.imageset/Contents.json @@ -0,0 +1,12 @@ +{ + "images" : [ + { + "filename" : "au_30.pdf", + "idiom" : "universal" + } + ], + "info" : { + "author" : "xcode", + "version" : 1 + } +} diff --git a/submodules/TelegramUI/Images.xcassets/Premium/Collectible/Tradable.imageset/au_30.pdf b/submodules/TelegramUI/Images.xcassets/Premium/Collectible/Tradable.imageset/au_30.pdf new file mode 100644 index 0000000000000000000000000000000000000000..d22e0fcc62a5a370e1f8dceb4571ebf7d62c6072 GIT binary patch literal 4735 zcmai2c{r5s*Eca}Qc{)>CMvrz%*asoHA`bjmc(E%7-KiK>``_y_C48&kUgOg*<}~m zLb7I;*YvHvzuw<_UGM$J^PKB*pL3pb&V60y{ygUzQci&%DsTe?f`SFXI1?)n7<}_4 z7$R-sgm$n+IibN2IkXGL6s@T!bFy`^APN}b-pSkMWLpkxiZet1+EGQ@S~yvPh2SDL zej(~$;S)(h0_5oAfJR|K_~Z%#26=IWz7^-X9mWp;=ruJwQX`O*J>pOI^Rdai5KV$Q zef~nDA&rJFjl3M$eJOJJ%XUt>X6I=%#>s&|?fZUIp!cGpPnda1NZFj0)CLRgl}x_Q zul4p?N*-HCJ;*pFiL@n^6LiR?e4A-(bjMxJjFx4G>~y<7!oG?TGxZ7R)$HoOiF3VF zRvR*&(0caZoRfA7^EUkjz4~M1K|L-0kV>E|nW1T1cXoy{O)8$AnWX^e-K*XfhxhrRN;*k=$~5547<*7!jIW(Y(|)Nk#w36jZQtwp1H%}yH%=B% zOLN`g_O23(hDaQXo{63Ww+x|K?P9wqBZ|WI6h-@$L9mU)r{iQw#x@dcVIlUoIrZ4O zy~`|hE8-M5?TpkWf3e(C1e~E|Z zfv3U1U@7`xl1jguNRn-zqx8#{0r2#5FdyM`V7Y(D6sQVd>kkm5dS*;jN2WE!67LJ9 zq|gBKJR`@*l1DO{kwUNBYh%_w1(c0KQqfYMzH+)8FiK7*^%jZH1`bL~$}Xgb^uTke zYpBDdF7_Dpa9#q&pqL6zGdB?gFK+vgP9gDhm#K4_m#6OIZ`>!p*PJtDzRT`O%IcH7 zrh;bzBCkFL-KKae1=Zlw2J?XEf-Yo;WNc_;=UQo#=ARw(uknx0ysp94s|%x3Qxxv$ zvXtBc!5C7)Cj$iJ;uz!v}~gkOZ!DHtDm)aS&@#rZSaoo9re+i zZOm;RZ6yRFP9q*~ogak5XQ!m!AbqnNHHM!dGwjq+N+|uPe9NLsx$&90xp>X>#Lrb1 z)Z3Bm29I-|7C5OcM)z^i$q#0g<=$Tsu{!%*$T{>;He>H_xtu;=g;O7by{)G_qJvOH z7*xyb)s<-jvrTfFbd+_tH4#}txf>YY`BM?VLtrgEjPHxl+5_i|{8}C(xKV|Mv()z( zCc_T~{L0u8{g6=(=kl7oeTVOS-200n}=*1wj0-Z*9^J&tvy3N`gx~BE-pkYZ& z{fYXC4T+dU3OEF#Zk1k(4!-d!Q7N+d2@^&)d7)m((}((J245^y>%5zJNDAm`G#0#`M;y z`iv-4)%3Y$`eu8MyBw9hVp;B4LGNfyZ_GH&Sk2U4*S+{$IlBEy%-Hi6c_|n_OyDBF z@Nnm(sA2)jC>u|gPKd9hq} zV6tiqDZ{-;v+HpUw$F9CWYQNe-6=DFS1~jzXU4qMr8Po*Tiql_SlwGpI5{Tyj#val zM8DJM{!9FT{7~SG<{Z0ocI~?=R_EB7#VY&p{E5_>xndvc>GoO^rAI%&` zoWF8=Qyho=m;lWBMEY#_E2V) zQQF~8xX0&BZ^Ke|rHV6vsqFf!Sz9f#yC<_}n(iI%RojnrcA4>=l9%8#$P+|jS7fC-0fm}^S|C5(kUoWf8g~n?2xG|@g+1q zel0!*nuhRAw(4@?6R#9udh<5^q9?;c=&@U}%6SQwb^&Hp0`!sBQl}|m4{REV-r(GrUE5=-DduilLX{pu4a&8x`A@lkV)k&?DyS9kZ*eW&qb20JIB z#cL*9J-kxyr*h#egEqrC#Cyeo;vTO2qt13iL!J+Qd_Ps-*XP$AINr1@se6pvZvOhM zYNB3#TRv#Vc!zR_>YMYJN2YEm-f;a(L-s+U%9m?<*Kj)}Bi=;=m0N9q&wzFSW+b}o zqyIj|&gH}VsYnUrv|g`noN6cS zO=wLW3>e%xY&$G1)~T;d-|n!VwqJZab#cP^oU=10T%CTf`Fr&KW3ApC+vC~qTGpgl z8TlI7rBg6be#Ft|h4?gzZnES>v5uQ#YawqSv1$X)56yGku$$T{G9l&gvWnH@26%X%20A~N^Mu&q(k!$g-x~EB(>KgM;`~QKaF{IOzSLTTgR|oAqQgn z6Z??K^%}=HfrD>hzGzY}e_ry{v+~r+tLxvwKHX_J-j|;DPnhPXhEi{`ao^j5D=Um5RMs^NlHnZ6^!@0r z@Yuv;9-3a+KGFLUDbjHXvBybw9^ayr>Tn^^X7dcW0!sAm^e96}Po)99@3~&8HhW;* z`byUdPSS}XL3#+0tvf<>?6{6<5rTeu_MS9G!8UN!#_kK0Gf_up+)kB(?BMlRW-qeL^nT;VSluU+*KB5gLBZbLz$v~ zQdwCjnCM0GgoYBC=x-bNUpD_QsTCo*_?ycDR^pYzXGPA-?}*78Hd;k7aTOhqJ&K6* z(Fo}z4{eZ~V#se}i@G){u$mOYGk?*$#lo&_dTeK7`zT_X`Pf!6==CEZbqkNA)Ra#H z{V(@M@@6Vo6pL`LTGLTOUEO05zJgL3nt5(*Y!wNF#kK(PjvQFzg z7v|Mmel6p;OT($fTPLs z?O#Zh0c1~2FO0_)MU*|GqYg5i7`9OON;9j!YEbCgH@aWq3^6!OKPKl$d6mIsS3-F6 zU}4R4H-$H#Nxeul=cC6^ml&&uIFI%1ZT+2kujzBWXXM7plejSN#T z4cI>QDd^}4Tcf&<5BI&cTETWL9*qvN^itYZ_G->0C?*VGnK1J3RIlIs8C}6R?zyO% z)@ac=Zl=WN7X|k^;UG7L>w5=1!`?Z)xeFZW@#qc4@4CUOSGoj?B5!7gaS}YQC-rUX z_UX@!M>!bKTZ|wJk!jb5O^@AmJnF?FIyB!`)_>R>4$s5(GlW|+@vegxJ7au}fiTNIK=inw;E+K;n;a3CROuw&X)cYiiDc<|IDwj=Jg3W6rrcTxl|Bi)jzsyOo-x$IY5hU7HfYkzFXxGW5X0JLC!1a*FO) zsjcFfclmYd#>z?pwp?>f343?X!B~4lLdVWQ{O0DfDeE_}{2rQIAZ z`noi?;ZjRFot016Ojm4@m){)%O`Utw^16lRRayHF3UtLICHapB&}N zu^WY5{{R~2;Z@L4#dlRLN(hfkqT8;mj6l)>kkTuS082wc zOLxlJjd4Bcwg00h6MNo2BLS$8F!WF7yzvMAIVrHU#W^{G4Ge$%@@u=s-H&#(ZaZ+iFyAV0ihh6epUhogTp}AuF2m~ F_#Z#`ZlVAH literal 0 HcmV?d00001 diff --git a/submodules/TelegramUI/Images.xcassets/Premium/Collectible/Transferable.imageset/Contents.json b/submodules/TelegramUI/Images.xcassets/Premium/Collectible/Transferable.imageset/Contents.json new file mode 100644 index 0000000000..822f9cb994 --- /dev/null +++ b/submodules/TelegramUI/Images.xcassets/Premium/Collectible/Transferable.imageset/Contents.json @@ -0,0 +1,12 @@ +{ + "images" : [ + { + "filename" : "replace_30.pdf", + "idiom" : "universal" + } + ], + "info" : { + "author" : "xcode", + "version" : 1 + } +} diff --git a/submodules/TelegramUI/Images.xcassets/Premium/Collectible/Transferable.imageset/replace_30.pdf b/submodules/TelegramUI/Images.xcassets/Premium/Collectible/Transferable.imageset/replace_30.pdf new file mode 100644 index 0000000000000000000000000000000000000000..5c497e01678e212aeb8f87a113c0665217d5fff1 GIT binary patch literal 5687 zcmai&byQSc+r~jsBt$wLLFpP`fB}`E8;9-~V347P7+@r%K}EW|Q$o5MB_%~91wkaF z8;K8me4gj^d%v~5{l_`?y7s=Wv+sS*TIaX!2T*BQZXk~^0Ra%e2SA!x6951Y9|CwK zY*BD0dl(81;FX5EBFy3Ha*|h5SA$m;aXoi+x4oK{hMOZT;J;^-;r35aRsepGknnFs z6(DdGJ$_8!jBITxNRRe)5b8KBS3sBraKar&JTDM5Aty7~0ga?A zPZf{l8PtU08T8Eb9~G-p-WvzlKK^o+`Nx!X!; zS{&&87y?DScMzZ&mnMLNfFk%#rcmatT5f^0CRP#AxPOCxR2GZc-4R_u3Kcnl;Xy0$ zeF8zM)Ude#9_je(CmG2jvnygNr2FKjoI!pgZ3(axK3ipM7RKnn-p&{^YoJO(e*)$J z^)drxh*mo-jN$F`&+@iRu8(SNd*DyM*yuWh$(4_Z(5n(X4RUVB`6?m;D3m!J%y%gB6&=}+x2~3X@_-(;rRKP^BJLpuPMZV3HuH@pgMp_k|;COA%ssh z;U34G&=)*iP%|b+&hpovAx+Aj@}5#mktl_*oNxCr5w{9oqmoduf|955+Dyq&dP`On z0ZqxCDKiQ!Po7IVkwU-2$}wPSV%9JF30V zUW1qUF>g@Ht5KtD6f)!46$OTCLe@kF{4TG9a%o0BS4rz*Zm{a}+9~QOOlpIb!3On` zCruTaIJss89oh=o?CRib{(@bE@A8dsoHLw8N7M+HwJlNVijyS=YSzJuozxb}UIh zsU-=Kga_h9s9G0V)!RIQOJB@(A4G(yLt94od~;?b ztZsq0ld{v1S&O-5L^RtyJNP}hx$q)t(R#6wMVBm3A*%OY^mN|a!c>GGB5;*k;Pb$o zvZ-rN@esZFrWKE1nb2Lc2pY!M%Ce8lyCmP4y_5-)33U?Ki*QJak>NDN3WYstON&R?|bcI^&VJybQz7f|&U6-bFrc_JFlE2KYQ zVpxg(C^HebsQ!(~CAaZ?9ivNZ!)l%5Ownvw!+QPRjLmGiM*{l3d&(~Q8{!-LuI;Yp zkdv96g^PP;lV|g8vwL%FlMXuGL;P^$;N?-_QQ~g(p3&CyYVgjj?df&lu1^-ulP*8l zz40zXFJ>{9d?I{yee#u*<6p#8aR<-fguAHK_Y=S$}!I zf1D1NH>E8@)4e*@0NM(cC5xU$=+JQ1@B+nq&ikAV9ZQ{AoogL)!5)EB!Ai_r%tEp= zFEqo+rE6ptvO2P?v-5P^%Vt`gxTbhSdEFgiM~Z%EO=!O+J3Tw*o+uCMyPGS0Uw)$b{y$vml^123PYC>c7=pqrG=T8>|0*9MFRC}1i< zdC;jV@#yc-X$Mv_IohDV)kMpCZj4o`Jdt) z(VrQnK_5dG^hR_G)#~5up5L3xyy)BvrVF!TWEa_8Etp?v=J-;yY~8swYW~?gu{&)% zV0{0q=d8R`ySXOgu-|dParNaq*{ln(iwi4Am2$lEAnNp`#z?;X<u~ zyr2j-`26elgmk`eDj)OCEDN65?P)4WhE#zns<%=u>q~{c*W{US zKYuh=I%e!>eB!BwSI%5(e%R4)Yb+I!8cnsx8|GTFO4~y2xN3j2yE)g78DD0+Q}0yR zo&CK-EB?*KL`rFD<6zUAQ|E>3o=Rh~O6%nLr;j#Yrak)?wAbyrr|rB#eu|#Xp7PG^ zG&q0b`MDYD3&-;E=fvG2k|9yp+Sv^K@}%YRRASjbaehHTBmUReQBg0y&y{177g6GFZeT|7nCwD(SPC4#5`@F|~ zHnx{oJXALXH&Je>@*+Aubr6EP9*U8ewI5oJY?Lr;Y|sc1+B8s!hcPQqj==}RVv|xh zNJpebMGPOOO2j9|UM4?z`3N7EjQb-eHh%D}Ytr@8cPypsLtBw-;ysncG za#oVC=wdcc8yIW>Vq@ssXp7I9oJ=g5oaF7{f_7q{6fbZ>@kf{m;Pw{3iSR1ArcMI> zfAI4cKK?BW{39Cxcon3iBw@~Q3&1t2ssoI!ZGI&S{k@R>>#W=6Cw)^s~qs~~X`erj)@RnG$n0=5@ zkjMCoaWjDf-3a#fl{w+F2jd&UD&qAHy8@gJmqe&MPjQT)FoVohqYMRWVl*?#?7ixm z(QK{AyRFZ{uaiGoZr2(Jo17@TQlpat!_4|W(7mGw?{y;eg0lfHJ1R9f%D9PVYl9ymjit;7SL^M4$LxP3BUM2G#w5IK3eX_UY1Xew8%E z9%;!upoV`_t;NX*1*XuUlb04fFK&)PM=!@9!gE0<*vs#51hY{gd&YV*>OJF9tIoS% zpM6NA7K%+!=mt(yBR@nG`!toa!`+cvy(!maVi6Ki;Pl>f(3k7{&TQ_)a_XDAv8-g- z7LW8s6o?~!#P-$4Y5GSDTkk&(uSDB-%FWc z(Ny2;9@i`QwmdoSjDcUwO;FDLyiC^cysl{(5_RL*+NFf};dec`_;KcvY$O7lZ*lo! zfL*q6o4Ut0quus*!Etv@197GNOeL5Z1j%lO(J^$@e+o_>q*Umbbb^=f9oPC)OQOYM z61lokQQU^Qg6>20*`A{75A2SfpM`*olIXle)}y`Fi1rZand0`n6TqCJM?+mWiT-0N zY)p#5G+9=u-P;rI9UW%~RJLxYs5|?}e(ZXk`mwPA>Db3VScdxspVd-bc6!PxG(Cs9 z+A)TVrDmo{&2T4yi)utC=+534jp~Q+z5U228kh4$!dj}hfD(^VrC?u3<8R#(J?rRGQ)2;Ga7 z@Qf@qK8etn?al>1>=gPip9}X3A{4g#9(=!Gqj1k)x^a%ITF-_NT8T4HpTK|H$N%Lr zMRs#1rT(f2=*dIE4^&&+pA1Wk`LbiVqg~rIT_7n^HMWUL!LX`UyH)BJO4;W~CA^*% zRF8uBD#mnNSOYHuVYl6?X&oRn?)?hImL2_2y99@=zZBL}9tOe`yH%fw8q|FK)e58B zT2i(MuT_YKKBE%e=Nc3rZel4HgJeX3Y-oE5)=0*Uc>wOMz+N#OagHY}xV<$fLTM#D z?xzN#keXco3YNr#sJ{7n)NpZ4E|!IXXkQD;&>2eeP)=~!o>{*oG($dQz=X^rud%cbDT!cPUMvpVEu#E7yOkc z(q!wUyL8&=wAzM`1!ZNg)?0~^rTRHCm*;`hv80$2EJYO7qBgmhMTa@)iKggEjzcT0 zLtP=^pwz>&%g)1;S=qgnmJuLNf+1aZv{c3M4rC3< zY_5pOfS54x`6KRM>OPXYNYy`T*rnxwA+!n`N&0?f=A{@$4@+xMWXTtbkB5V?`N!R(@1&CN@8432m!j~O z#YzJ>32WsD`HJcI^fBR3@zFV~72tdn#(k3!dd9-~C1Nw9SNl+uK4_8T_S0H@4yL>Z z)D9(LJB=%>)mqQefBL)2nge~l4Fs!(+j8SI#6AdJahOmqH$S0QyU?8SVWwMBd2E*P zMlU>NL>1^{$yLZ)8yA-oyI-6v*doN?qI;O|zK>5^$)tMo*f3#3EX(D)p{hPjDZIO) zoqn-j1AQ`zrPnJwFIg1GunPG8zTk`Epu56!I-Opz^4U0<*SU=a11!E$_2gb)siHBB z;%nXKgWBWEDiSj>gSS<~BARTBP4pCp)vC4otc`^k7BNau)OIvacx>F+yEf(%y7703to@yG<)ej&)D2ohCD2vP`_ORDndwUrc8Q9s~u5p0zuwBS4+W^+KH4i`u<>Q7-4OWo)8hH{ z(*paTuomfc*`S<=1p&91gFPr)>jenj#r8trRJ)7%KKGG%q+)uxN7t^wKZzG6b8l zq$F++VqQoUZc|N))>!@@EOpH)f3@=Xfg%F`5RdRb@~;k`y*(1;3@|YI-Kx`k=5W>Q zgTkKv-OW>hTOeSPNHo9zz{kV)Yy2As{3HDv{q3Xf@|*k+tKAJ7x-WMcfL~o!wLL{ z({+K)|2*(|V}BJc1w+AXkx&0jMLWSQ3HSg&K7zmMzrF&5Kp+7S;7b2JfP8>o5y1Yp z#4iNC+UWK8R}$v`uT{dJs~Z1YCGuZhAU@&$?F9l}8UL9G;upR`+&{cP0)qc0qMTp| zTeuU!^?O&4zB))YXQUmR9l(aLVE^kldDW3f6yW-(e!UuA4TL-Vy6#`CTbL8-*GYf` N`Gg1_Jdk-J`#(rOB&GlW literal 0 HcmV?d00001 diff --git a/submodules/TelegramUI/Images.xcassets/Premium/Collectible/Unique.imageset/Contents.json b/submodules/TelegramUI/Images.xcassets/Premium/Collectible/Unique.imageset/Contents.json new file mode 100644 index 0000000000..b3a84e599e --- /dev/null +++ b/submodules/TelegramUI/Images.xcassets/Premium/Collectible/Unique.imageset/Contents.json @@ -0,0 +1,12 @@ +{ + "images" : [ + { + "filename" : "diamond_30.pdf", + "idiom" : "universal" + } + ], + "info" : { + "author" : "xcode", + "version" : 1 + } +} diff --git a/submodules/TelegramUI/Images.xcassets/Premium/Collectible/Unique.imageset/diamond_30.pdf b/submodules/TelegramUI/Images.xcassets/Premium/Collectible/Unique.imageset/diamond_30.pdf new file mode 100644 index 0000000000000000000000000000000000000000..9a4aa524fb00a8149873c201d3fc02f286e3d95e GIT binary patch literal 4776 zcmai2c{r4B_qRraWE;Ci_T6AGNcNqEEMrT9DPtMS3?}=UBxT9IWh+D?`yR4H)?^n^ zw#dFOFMX@;+x7mg>v#Tmo^yT9ea>^A`+lx-KG)%bsi+Bog{1%hFh~^SY;6w!f#l>s zB8rY!q#GK6MS?_BkRB)-q=CB9ndwYK)KKTPv$x}!Sp{k1Y>WJ5(Lti`V(ma;5J{Td9f_PY!w_3-oQT zqh+JSYc}Hazk6gM1lBN45!9ptb;r5_GGYT=B%2RPG*Q+e0!Y_>pA!^g#Qp?PNG%oj zU9CM0R(;6?R#R(Jc|N817G0W7X+{JInt-H}eF)?z|M@hPl(7TPsj$%Lv?cx2p|i&} zed`GUD1%I4Q|Oh-Q|J}tE;NIJir%9O9|M4Wglz%%jsb)&7gn2?_ktBJ1FpWcZz8@3 zB7UF%9Kovyl7r#x1ROtOVZnzyyCe}H{*1URIAR)5g^v!#7bVZRO`!+QIztRtvpwm?s@9VL< zz6Fq=PkTHSBCL{-edAe5|Kx(~0@W7nfx!Ks{-#7ks;Hw55jR_GXlKh)YkRP6Vpk%5 zH^VnpV1&_ICIstizYz_`s~*=XE_;)XKUitqL8zAuO0(!u+`W%^OZ-Jz8Z1oddD#=- zi9h^=xy83Bc5MvDTt_Jn5NVyC)?VjXXWjEV!W@w+2HsY1xk^Nh3n>q|szi|)=@KET zmdMUe9r-}G6=r?aRiNb2h(eu?kA{!()o85dh8up$nJ{v`)u+4<6H8MJ)T>qQ>Fk5la$uP*dI$}KS#*J25kp>L7CaAUu%7&-idwG| z)@dI8;^|AQ&SJ~}FP-X8c4?l)lB7MwwwU{)`?-w$BV{UP_$xeSB2HSSnxn>09jJM= z(tcg3A#tvCUbC^LF`of6TP$w_75I(d3GoqeEl@&m!D{W6duBl`zZJx)T;E+`JC@1v zgSn8VQ?XgZ7>9dVP5yz~w%~TlQo;}q{Qyu#WKOu-3U_CQkOJOf$N{&hZv#iBFbyV~ zB{w9al1U&UC_VdpyJ`n6`#3vyJ6*e*<(B2dV_akOWmbK0i)>rAUX1lA^10cWvoARM z^9L4(lKLS7mYFHp<9UY~Px9J7Mhpt{FAt;)41cmJXjE*xbsC8OHUJfM+okDQ`*eL~ zG@@$eQVX!fmBWamqF*N4D?6->)<$X;J8M5%%MGV_t{KzG9y|X0b$%Ku2o<_0BtFtT zrE}Y(qp+97X4OtuqFC~Zbrd7pBOSGCHmyqUtix3wt46#N(Dv=(?W&Kz7H?Jby)dT8 zzVLogT;XvMrf|IQv=m%EGiEq4VX0y|_r`8B>yv7+(+9`>iZG`iqxviHV=J$hOAoDo z+!iTyex2dc=lQ@j56fUZkf_+L^vy{9QTB`3JMA9rPxN;5tY3)h`OAo>#-`qoc_Jcd z)@^0+2KP~QICR$F>s9yM+O{e-_qdwHD%XjE$@H4#>dgs<$qesA9H&?62JS2BE8m9W zhEK1XwUe#8S7x10{YJf4eO#RhF2VcyPXBiJZvJl4M){`YkMYH@b+WbbWvSLrwwO`( z?|l9wr;#U<_;UeK0UH4?v~?05#Fq($CLD-NR&LFR1tXi0LV=(t)+&)T{V08yS6^{g zNmp>ZiJ(8wk+qJ!iYJ7rlzYy$qZZcNm(@2z*CxOzP}4lulGU=*JQe00N*|`pA;=-A zHu1pl39U+n+Duk+mVNef6R+ZlMmNDRVHpuGm$?3d??%JMFN^hV`T9j3G4&+B0VgJ| zCB}j?pn<9OJy=263Nep|U0>Ly8|JDvwQw{(^nT#ed#TzMt`oRA?W2ahaH7hPfe=l6 zZJ01FZ6N{2x$bLldtg0hbZ%{~jn+p?=RGttMpSbotspKrZ7ktVe0&18uBX=L40$fV z<)J$Yj4#E^-bu`TD7J-o4{Z_^SB(Hv{uyy~}M*ZR0#X+~`L5zV&zHSXWYK z-F4d2JRjuIQo{Ag(>f>}7_wuw<+W4XEM@QfxUt-K9KtGbyl45oHfeYN%18Osy^f=i zO}?YS&7{KKs$Qg(PD7b5#omF7B+_F?meTs|&Yk|1BG#1^MoDsq8v5&xIW&QT$ezb> z$*KHQ{mKK<7V>F|2}yCMDL2Bekt%d~;2B==iC_mL`*(YnLKUYoi2Y4G8LDk>IkZ>8 z?IC#GC_I?A7|}WgZ0y2&TniNq+IRJ#vU=bM(P`?XCY_BwCc8&a0y5H|CtdVs9z1Q> zY+N?a_*_?#m?4zDEDmuh(zu)fdj2NVxFx?G#sP}e!-zPa4FzX zm8v90ThX?a!zR7ErvXfaXF|}FkTp7*R4_U!(jf?I!NY2u5J!^sa{!QN+h2_EOgm?t z0I@&x^Cx}$4U7MQ%|RlX%F0Rz4AK^K&Q%RSmgix9I!pXnb;Vl+UVWsb}FcYiipUx ziiGsri9&<5=gm?T4)HO#AGcNgK|-VN@ZoMF`tx4Naow@^AZWI_>+tK7eaX{1Kjvez1W#bbZ4jp&92j40KzL-#V!>1yt`^WM^|5mrq@fzk3~hN^Vg$?=wuT zQyw?R0)5KM?K0Pw<3Xnve5*%B9iUkj?^ns-W^V+b1(#jr0+M)l1Je+$0~a~VrZ`#e zP42_JR!HSF;Hg-Gck`bLtc7x@9X1v4w4x1}ZVQyFJt3Ulp98^Ec33F(+<`$+g>W9y z(44vFk#Dq3AHD*Hn$LlE@A$dBh3Zfd29D6IYxM>#gt!ybEltE~&3Vw?XZ=uxw-h*fF@AbRK2AkwoTvr~o&(V;u5FZsdRF-$mR(iyYHpPB)`nXcP(3ZvV1+a2HC3{Gc=g7!tXc_`xqv3d$Z!)nv6b*-GEDd7R!Ih5w)K(|i)tcoZ)K3xGFX|j0puf+{xy-(4k1L^ii zHM$+NSQq@EV$G+H{se;`*SIPt_f`=Fa2!!Gaq#YtO*axdzUCm+l@f1buW;?Am3@ zjwA=AbUqS_L2Oa5$z`v6)`zb^#Ehr63UPSg#*WWH8_31)0p3Vs4BOj^OGQ(G2A$zO z-Lyt`UGOa#)Sn1H3I=l1(h!zK*k>t*9Cxr~@qk%Tj#I$s@hD9H!a&$ibbk`h57I$L zAHh|S_$zU;b|_6`fkMJ-=Vc;rvJwAaG5}n`*ql_9ZhG#NIPW$6 zrm%_(_ehCr5ihw)r_X;FF<-L!x2W;jp$+&ISyC`rpgyI99DDlk$#|7l|9mN`X)|wa zUmv5hb!|HDVe7Q5Bqt9KUGLb;QnhEL$QTQ4kzt24Gc&pTyBqIp9$UWeminrit=;%o zm{R)m^6PI@CR(4yG+r{Dn(w{G=Fml9F!v0Kbrb6?Tgie9Uk6^(hc61w^8~dF>n}#j zFT0gDmiNFzW*M#U2L!l|PnR7}#N={o!bz-BKytDjcPEKPIgg2Z#s$8+?5}kCPgQJ_ z{U62qyg2?dI{=GGiTx?DQh(r|BLy_t8H)j#TmG627~XR^n=Zf*cYlu$bdk0wgpxB3 zWDXJ)7X2yzD&l{jf3@F{43HRScQ+d(26WyC&a3qKoI=?djYXo*zIa~ge}gcD+ZhIu z{ssM4KRAc}oquSfY|pj|I?v#|(fp3+e&!4QpZ#ObkpGGV{VURWf|x%aJm1(qi7O+p z2uJ6;f4kz`kaqy0Ah0OlpZw3CAV~;B90EGSe+gI=^iu<&e?elB(6f!6%fFD6*#E4O zf}G{}_bTcCi6sG+JiE=mR!M-R&r + if let peerId = self.chatLocation.peerId { + displayedPeerVerification = ApplicationSpecificNotice.displayedPeerVerification(accountManager: context.sharedContext.accountManager, peerId: peerId) + |> take(1) + } else { + displayedPeerVerification = .single(false) + } self.peerDisposable.set(combineLatest( queue: Queue.mainQueue(), @@ -5593,8 +5601,9 @@ public final class ChatControllerImpl: TelegramBaseController, ChatController, G hasSavedChats, isPremiumRequiredForMessaging, managingBot, - adMessage - ).startStrict(next: { [weak self] peerView, globalNotificationSettings, onlineMemberCount, hasScheduledMessages, peerReportNotice, pinnedCount, threadInfo, hasSearchTags, hasSavedChats, isPremiumRequiredForMessaging, managingBot, adMessage in + adMessage, + displayedPeerVerification + ).startStrict(next: { [weak self] peerView, globalNotificationSettings, onlineMemberCount, hasScheduledMessages, peerReportNotice, pinnedCount, threadInfo, hasSearchTags, hasSavedChats, isPremiumRequiredForMessaging, managingBot, adMessage, displayedPeerVerification in if let strongSelf = self { if strongSelf.peerView === peerView && strongSelf.reportIrrelvantGeoNotice == peerReportNotice && strongSelf.hasScheduledMessages == hasScheduledMessages && strongSelf.threadInfo == threadInfo && strongSelf.presentationInterfaceState.hasSearchTags == hasSearchTags && strongSelf.presentationInterfaceState.hasSavedChats == hasSavedChats && strongSelf.presentationInterfaceState.isPremiumRequiredForMessaging == isPremiumRequiredForMessaging && managingBot == strongSelf.presentationInterfaceState.contactStatus?.managingBot && adMessage?.id == strongSelf.presentationInterfaceState.adMessage?.id { return @@ -5745,7 +5754,11 @@ public final class ChatControllerImpl: TelegramBaseController, ChatController, G var autoremoveTimeout: Int32? var copyProtectionEnabled: Bool = false var hasBirthdayToday = false + var displayVerificationDescription = false if let peer = peerView.peers[peerView.peerId] { + if let _ = peer.verification, !displayedPeerVerification { + displayVerificationDescription = true + } copyProtectionEnabled = peer.isCopyProtectionEnabled if let cachedGroupData = peerView.cachedData as? CachedGroupData { if !cachedGroupData.botInfos.isEmpty { @@ -5916,6 +5929,7 @@ public final class ChatControllerImpl: TelegramBaseController, ChatController, G .updatedHasBirthdayToday(hasBirthdayToday) .updatedBusinessIntro(businessIntro) .updatedAdMessage(adMessage) + .updatedDisplayVerificationDescription(displayVerificationDescription) .updatedInterfaceState { interfaceState in var interfaceState = interfaceState diff --git a/submodules/TelegramUI/Sources/ChatInterfaceStateContextMenus.swift b/submodules/TelegramUI/Sources/ChatInterfaceStateContextMenus.swift index 96546f93c8..330644db6b 100644 --- a/submodules/TelegramUI/Sources/ChatInterfaceStateContextMenus.swift +++ b/submodules/TelegramUI/Sources/ChatInterfaceStateContextMenus.swift @@ -1122,13 +1122,12 @@ func contextMenuForChatPresentationInterfaceState(chatPresentationInterfaceState } if data.messageActions.options.contains(.sendGift) { - //TODO:localize let sendGiftTitle: String if message.effectivelyIncoming(context.account.peerId) { let peerName = message.peers[message.id.peerId].flatMap(EnginePeer.init)?.compactDisplayTitle ?? "" - sendGiftTitle = "Send Gift to \(peerName)" + sendGiftTitle = chatPresentationInterfaceState.strings.Conversation_ContextMenuSendGiftTo(peerName).string } else { - sendGiftTitle = "Send Another Gift" + sendGiftTitle = chatPresentationInterfaceState.strings.Conversation_ContextMenuSendAnotherGift } actions.append(.action(ContextMenuActionItem(text: sendGiftTitle, icon: { theme in return generateTintedImage(image: UIImage(bundleImageName: "Chat/Context Menu/Gift"), color: theme.actionSheet.primaryTextColor) diff --git a/submodules/TelegramUI/Sources/ChatInterfaceTitlePanelNodes.swift b/submodules/TelegramUI/Sources/ChatInterfaceTitlePanelNodes.swift index 67d0684389..5bd39a5836 100644 --- a/submodules/TelegramUI/Sources/ChatInterfaceTitlePanelNodes.swift +++ b/submodules/TelegramUI/Sources/ChatInterfaceTitlePanelNodes.swift @@ -106,6 +106,17 @@ func titlePanelForChatPresentationInterfaceState(_ chatPresentationInterfaceStat } } + + if let peer = chatPresentationInterfaceState.renderedPeer?.peer, let _ = peer.verification, chatPresentationInterfaceState.displayVerificationDescription { + if let currentPanel = currentPanel as? ChatVerifiedPeerTitlePanelNode { + return currentPanel + } else if let controllerInteraction = controllerInteraction { + let panel = ChatVerifiedPeerTitlePanelNode(context: context, animationCache: controllerInteraction.presentationContext.animationCache, animationRenderer: controllerInteraction.presentationContext.animationRenderer) + panel.interfaceInteraction = interfaceInteraction + return panel + } + } + if let channel = chatPresentationInterfaceState.renderedPeer?.peer as? TelegramChannel, channel.flags.contains(.isForum) { if let threadData = chatPresentationInterfaceState.threadData { if threadData.isClosed { diff --git a/submodules/TelegramUI/Sources/ChatPinnedMessageTitlePanelNode.swift b/submodules/TelegramUI/Sources/ChatPinnedMessageTitlePanelNode.swift index 103bd38194..9262bd3f0d 100644 --- a/submodules/TelegramUI/Sources/ChatPinnedMessageTitlePanelNode.swift +++ b/submodules/TelegramUI/Sources/ChatPinnedMessageTitlePanelNode.swift @@ -718,7 +718,7 @@ final class ChatPinnedMessageTitlePanelNode: ChatTitleAccessoryPanelNode { updateImageSignal = .single({ _ in return nil }) } } - let (titleLayout, titleApply) = makeTitleLayout(CGSize(width: width - textLineInset - contentLeftInset - rightInset - textRightInset, height: CGFloat.greatestFiniteMagnitude), titleStrings) + let (titleLayout, titleApply) = makeTitleLayout(CGSize(width: width - textLineInset - contentLeftInset - rightInset - textRightInset, height: CGFloat.greatestFiniteMagnitude), .zero, titleStrings) let (textString, _, isText) = descriptionStringForMessage(contentSettings: context.currentContentSettings.with { $0 }, message: EngineMessage(message), strings: strings, nameDisplayOrder: nameDisplayOrder, dateTimeFormat: dateTimeFormat, accountPeerId: accountPeerId) diff --git a/submodules/TelegramUI/Sources/ChatVerifiedPeerTitlePanelNode.swift b/submodules/TelegramUI/Sources/ChatVerifiedPeerTitlePanelNode.swift new file mode 100644 index 0000000000..f0f042ef01 --- /dev/null +++ b/submodules/TelegramUI/Sources/ChatVerifiedPeerTitlePanelNode.swift @@ -0,0 +1,160 @@ +import Foundation +import UIKit +import Display +import AsyncDisplayKit +import Postbox +import TelegramCore +import TelegramPresentationData +import LocalizedPeerData +import TelegramStringFormatting +import TextFormat +import Markdown +import ChatPresentationInterfaceState +import TextNodeWithEntities +import AnimationCache +import MultiAnimationRenderer +import AccountContext +import TelegramNotices + +final class ChatVerifiedPeerTitlePanelNode: ChatTitleAccessoryPanelNode { + private let context: AccountContext + private let animationCache: AnimationCache + private let animationRenderer: MultiAnimationRenderer + + private let separatorNode: ASDisplayNode + private let emojiStatusTextNode: TextNodeWithEntities + + private var presentationInterfaceState: ChatPresentationInterfaceState? + + private var theme: PresentationTheme? + + private var tapGestureRecognizer: UITapGestureRecognizer? + + init(context: AccountContext, animationCache: AnimationCache, animationRenderer: MultiAnimationRenderer) { + self.context = context + self.animationCache = animationCache + self.animationRenderer = animationRenderer + + self.separatorNode = ASDisplayNode() + self.separatorNode.isLayerBacked = true + + self.emojiStatusTextNode = TextNodeWithEntities() + + super.init() + + self.addSubnode(self.separatorNode) + self.addSubnode(self.emojiStatusTextNode.textNode) + } + + override func didLoad() { + super.didLoad() + + let tapRecognizer = UITapGestureRecognizer(target: self, action: #selector(self.tapped)) + self.view.addGestureRecognizer(tapRecognizer) + } + + @objc private func tapped() { + guard let navigationController = self.interfaceInteraction?.getNavigationController(), let interfaceState = self.presentationInterfaceState else { + return + } + if let verification = interfaceState.renderedPeer?.peer?.verification, let description = verification.customDescription { + let entities = generateTextEntities(description, enabledTypes: [.allUrl]) + if let entity = entities.first { + let range = NSRange(location: entity.range.lowerBound, length: entity.range.upperBound - entity.range.lowerBound) + let url = (description as NSString).substring(with: range) + self.context.sharedContext.openExternalUrl(context: self.context, urlContext: .generic, url: url, forceExternal: false, presentationData: self.context.sharedContext.currentPresentationData.with { $0 }, navigationController: navigationController, dismissInput: {}) + } + } + } + + override func updateLayout(width: CGFloat, leftInset: CGFloat, rightInset: CGFloat, transition: ContainedViewLayoutTransition, interfaceState: ChatPresentationInterfaceState) -> LayoutResult { + let isFirstTime = self.presentationInterfaceState == nil + self.presentationInterfaceState = interfaceState + + if interfaceState.theme !== self.theme { + self.theme = interfaceState.theme + + self.separatorNode.backgroundColor = interfaceState.theme.rootController.navigationBar.separatorColor + } + + var panelHeight: CGFloat = 8.0 + + if let peer = interfaceState.renderedPeer?.peer, let verification = peer.verification { + if isFirstTime { + let _ = ApplicationSpecificNotice.setDisplayedPeerVerification(accountManager: self.context.sharedContext.accountManager, peerId: peer.id).start() + } + + let emojiStatus = PeerEmojiStatus(fileId: verification.iconFileId, expirationDate: nil) + let emojiStatusTextNode = self.emojiStatusTextNode + + let description: String + if let customDescription = verification.customDescription { + description = customDescription + } else { + switch peer { + case let user as TelegramUser: + if let _ = user.botInfo { + description = interfaceState.strings.PeerInfo_VerificationInfo_Custom_Bot(verification.companyName).string + } else { + description = interfaceState.strings.PeerInfo_VerificationInfo_Custom_User(verification.companyName).string + } + case let channel as TelegramChannel: + switch channel.info { + case .group: + description = interfaceState.strings.PeerInfo_VerificationInfo_Custom_Group(verification.companyName).string + case .broadcast: + description = interfaceState.strings.PeerInfo_VerificationInfo_Custom_Channel(verification.companyName).string + } + default: + description = "" + } + } + + let plainText = " \(description)" + let entities = generateTextEntities(plainText, enabledTypes: [.allUrl]) + + let attributedText = NSMutableAttributedString(attributedString: NSAttributedString(string: plainText, font: Font.regular(12.0), textColor: interfaceState.theme.rootController.navigationBar.secondaryTextColor, paragraphAlignment: .center)) + attributedText.addAttribute(ChatTextInputAttributes.customEmoji, value: ChatTextInputTextCustomEmojiAttribute(interactivelySelectedFromPackId: nil, fileId: emojiStatus.fileId, file: nil), range: NSMakeRange(0, 1)) + if let entity = entities.first { + let range = NSRange(location: entity.range.lowerBound, length: entity.range.upperBound - entity.range.lowerBound) + attributedText.addAttribute(NSAttributedString.Key.foregroundColor, value: interfaceState.theme.rootController.navigationBar.accentTextColor, range: range) + } + + let makeEmojiStatusLayout = TextNodeWithEntities.asyncLayout(emojiStatusTextNode) + let (emojiStatusLayout, emojiStatusApply) = makeEmojiStatusLayout(TextNodeLayoutArguments( + attributedString: attributedText, + backgroundColor: nil, + minimumNumberOfLines: 0, + maximumNumberOfLines: 0, + truncationType: .end, + constrainedSize: CGSize(width: width - leftInset * 2.0 - 16.0 * 2.0, height: CGFloat.greatestFiniteMagnitude), + alignment: .center, + verticalAlignment: .top, + lineSpacing: 0.2, + cutout: nil, + insets: UIEdgeInsets(), + lineColor: nil, + textShadowColor: nil, + textStroke: nil, + displaySpoilers: false, + displayEmbeddedItemsUnderSpoilers: false + )) + let _ = emojiStatusApply(TextNodeWithEntities.Arguments( + context: self.context, + cache: self.animationCache, + renderer: self.animationRenderer, + placeholderColor: interfaceState.theme.list.mediaPlaceholderColor, + attemptSynchronous: false + )) + transition.updateFrame(node: emojiStatusTextNode.textNode, frame: CGRect(origin: CGPoint(x: floor((width - emojiStatusLayout.size.width) / 2.0), y: panelHeight), size: emojiStatusLayout.size)) + panelHeight += emojiStatusLayout.size.height + 8.0 + + emojiStatusTextNode.visibilityRect = .infinite + } + + let initialPanelHeight = panelHeight + transition.updateFrame(node: self.separatorNode, frame: CGRect(origin: CGPoint(x: 0.0, y: 0.0), size: CGSize(width: width, height: UIScreenPixel))) + + return LayoutResult(backgroundHeight: initialPanelHeight, insetHeight: panelHeight, hitTestSlop: .zero) + } +} diff --git a/submodules/TelegramUI/Sources/OpenResolvedUrl.swift b/submodules/TelegramUI/Sources/OpenResolvedUrl.swift index 0ae780833f..2f06d8b22e 100644 --- a/submodules/TelegramUI/Sources/OpenResolvedUrl.swift +++ b/submodules/TelegramUI/Sources/OpenResolvedUrl.swift @@ -712,7 +712,7 @@ func openResolvedUrlImpl( } case let .premiumMultiGift(reference): dismissInput() - let controller = context.sharedContext.makePremiumGiftController(context: context, source: .deeplink(reference), completion: nil) + let controller = context.sharedContext.makePremiumGiftController(context: context, source: .deeplink(reference), transfer: false, completion: nil) if let navigationController = navigationController { navigationController.pushViewController(controller, animated: true) } diff --git a/submodules/TelegramUI/Sources/SharedAccountContext.swift b/submodules/TelegramUI/Sources/SharedAccountContext.swift index c1f138311b..2120a018e2 100644 --- a/submodules/TelegramUI/Sources/SharedAccountContext.swift +++ b/submodules/TelegramUI/Sources/SharedAccountContext.swift @@ -75,6 +75,7 @@ import GiftViewScreen import StarsIntroScreen import ContentReportScreen import AffiliateProgramSetupScreen +import GalleryUI private final class AccountUserInterfaceInUseContext { let subscribers = Bag<(Bool) -> Void>() @@ -2277,9 +2278,11 @@ public final class SharedAccountContextImpl: SharedAccountContext { return controller } - public func makePremiumGiftController(context: AccountContext, source: PremiumGiftSource, completion: (([EnginePeer.Id]) -> Void)?) -> ViewController { + public func makePremiumGiftController(context: AccountContext, source: PremiumGiftSource, transfer: Bool, completion: (([EnginePeer.Id]) -> Void)?) -> ViewController { let presentationData = context.sharedContext.currentPresentationData.with { $0 } + var presentTransferAlertImpl: (() -> Void)? + var presentBirthdayPickerImpl: (() -> Void)? var mode: ContactSelectionControllerMode = .generic var currentBirthdays: [EnginePeer.Id: TelegramBirthday]? @@ -2295,7 +2298,20 @@ public final class SharedAccountContextImpl: SharedAccountContext { } let contactOptions: Signal<[ContactListAdditionalOption], NoError> - if currentBirthdays != nil || "".isEmpty { + if transfer { + //TODO:localize + contactOptions = .single([ + ContactListAdditionalOption( + title: presentationData.strings.Gift_Transfer_SendViaBlockchain, + subtitle: presentationData.strings.Gift_Transfer_SendUnlocks("21 days").string, + icon: .generic(UIImage(bundleImageName: "Item List/Ton")!), + action: { + presentTransferAlertImpl?() + }, + clearHighlightAutomatically: true + ) + ]) + } else if currentBirthdays != nil || "".isEmpty { contactOptions = context.engine.data.subscribe(TelegramEngine.EngineData.Item.Peer.Birthday(id: context.account.peerId)) |> map { birthday in if birthday == nil { @@ -2325,7 +2341,7 @@ public final class SharedAccountContextImpl: SharedAccountContext { context: context, mode: mode, autoDismiss: false, - title: { strings in return presentationData.strings.Gift_PremiumOrStars_Title }, + title: { strings in return transfer ? presentationData.strings.Gift_Transfer_Title : presentationData.strings.Gift_PremiumOrStars_Title }, options: contactOptions, openProfile: { peer in openProfileImpl?(peer) @@ -2403,6 +2419,13 @@ public final class SharedAccountContextImpl: SharedAccountContext { controller.push(birthdayController) } + presentTransferAlertImpl = { [weak controller] in + let alertController = textAlertController(context: context, title: presentationData.strings.Gift_Transfer_UnlockPending_Title, text: presentationData.strings.Gift_Transfer_UnlockPending_Text("21 days").string, actions: [ + TextAlertAction(type: .defaultAction, title: presentationData.strings.Common_OK, action: {}) + ]) + controller?.present(alertController, in: .window(.root)) + } + return controller } @@ -2431,6 +2454,7 @@ public final class SharedAccountContextImpl: SharedAccountContext { var actionImpl: (() -> Void)? var openPremiumIntroImpl: (() -> Void)? + let presentationData = context.sharedContext.currentPresentationData.with { $0 } let controller = PremiumPrivacyScreen( context: context, peerId: peerId, @@ -2449,7 +2473,6 @@ public final class SharedAccountContextImpl: SharedAccountContext { let currentPrivacy = Promise() currentPrivacy.set(context.engine.privacy.requestAccountPrivacySettings()) - let presentationData = context.sharedContext.currentPresentationData.with { $0 } let tooltipText: String switch subject { @@ -2506,7 +2529,7 @@ public final class SharedAccountContextImpl: SharedAccountContext { let controller = context.sharedContext.makePremiumIntroController(context: context, source: introSource, forceDark: false, dismissed: nil) parentController.push(controller) } - + return controller } @@ -2548,8 +2571,8 @@ public final class SharedAccountContextImpl: SharedAccountContext { return controller } - public func makeStickerPackScreen(context: AccountContext, updatedPresentationData: (initial: PresentationData, signal: Signal)?, mainStickerPack: StickerPackReference, stickerPacks: [StickerPackReference], loadedStickerPacks: [LoadedStickerPack], isEditing: Bool, expandIfNeeded: Bool, parentNavigationController: NavigationController?, sendSticker: ((FileMediaReference, UIView, CGRect) -> Bool)?, actionPerformed: ((Bool) -> Void)?) -> ViewController { - return StickerPackScreen(context: context, updatedPresentationData: updatedPresentationData, mainStickerPack: mainStickerPack, stickerPacks: stickerPacks, loadedStickerPacks: loadedStickerPacks, isEditing: isEditing, expandIfNeeded: expandIfNeeded, parentNavigationController: parentNavigationController, sendSticker: sendSticker, actionPerformed: { actions in + public func makeStickerPackScreen(context: AccountContext, updatedPresentationData: (initial: PresentationData, signal: Signal)?, mainStickerPack: StickerPackReference, stickerPacks: [StickerPackReference], loadedStickerPacks: [LoadedStickerPack], actionTitle: String?, isEditing: Bool, expandIfNeeded: Bool, parentNavigationController: NavigationController?, sendSticker: ((FileMediaReference, UIView, CGRect) -> Bool)?, actionPerformed: ((Bool) -> Void)?) -> ViewController { + return StickerPackScreen(context: context, updatedPresentationData: updatedPresentationData, mainStickerPack: mainStickerPack, stickerPacks: stickerPacks, loadedStickerPacks: loadedStickerPacks, actionTitle: actionTitle, isEditing: isEditing, expandIfNeeded: expandIfNeeded, parentNavigationController: parentNavigationController, sendSticker: sendSticker, actionPerformed: { actions in if let (_, _, action) = actions.first { switch action { case .add: @@ -2709,14 +2732,18 @@ public final class SharedAccountContextImpl: SharedAccountContext { return mediaPickerController(context: context, hasSearch: hasSearch, completion: completion) } - public func makeStoryMediaPickerScreen(context: AccountContext, isDark: Bool, forCollage: Bool, getSourceRect: @escaping () -> CGRect, completion: @escaping (Any, UIView, CGRect, UIImage?, @escaping (Bool?) -> (UIView, CGRect)?, @escaping () -> Void) -> Void, dismissed: @escaping () -> Void, groupsPresented: @escaping () -> Void) -> ViewController { - return storyMediaPickerController(context: context, isDark: isDark, forCollage: forCollage, getSourceRect: getSourceRect, completion: completion, dismissed: dismissed, groupsPresented: groupsPresented) + public func makeStoryMediaPickerScreen(context: AccountContext, isDark: Bool, forCollage: Bool, selectionLimit: Int?, getSourceRect: @escaping () -> CGRect, completion: @escaping (Any, UIView, CGRect, UIImage?, @escaping (Bool?) -> (UIView, CGRect)?, @escaping () -> Void) -> Void, multipleCompletion: @escaping ([Any]) -> Void, dismissed: @escaping () -> Void, groupsPresented: @escaping () -> Void) -> ViewController { + return storyMediaPickerController(context: context, isDark: isDark, forCollage: forCollage, selectionLimit: selectionLimit, getSourceRect: getSourceRect, completion: completion, multipleCompletion: multipleCompletion, dismissed: dismissed, groupsPresented: groupsPresented) } public func makeStickerMediaPickerScreen(context: AccountContext, getSourceRect: @escaping () -> CGRect?, completion: @escaping (Any?, UIView?, CGRect, UIImage?, Bool, @escaping (Bool?) -> (UIView, CGRect)?, @escaping () -> Void) -> Void, dismissed: @escaping () -> Void) -> ViewController { return stickerMediaPickerController(context: context, getSourceRect: getSourceRect, completion: completion, dismissed: dismissed) } + public func makeAvatarMediaPickerScreen(context: AccountContext, getSourceRect: @escaping () -> CGRect?, canDelete: Bool, performDelete: @escaping () -> Void, completion: @escaping (Any?, UIView?, CGRect, UIImage?, Bool, @escaping (Bool?) -> (UIView, CGRect)?, @escaping () -> Void) -> Void, dismissed: @escaping () -> Void) -> ViewController { + return avatarMediaPickerController(context: context, getSourceRect: getSourceRect, canDelete: canDelete, performDelete: performDelete, completion: completion, dismissed: dismissed) + } + public func makeStickerPickerScreen(context: AccountContext, inputData: Promise, completion: @escaping (FileMediaReference) -> Void) -> ViewController { let controller = StickerPickerScreen(context: context, inputData: inputData.get(), expanded: true, hasGifs: false, hasInteractiveStickers: false) controller.completion = { content in @@ -2844,6 +2871,15 @@ public final class SharedAccountContextImpl: SharedAccountContext { public func makeAffiliateProgramJoinScreen(context: AccountContext, sourcePeer: EnginePeer, commissionPermille: Int32, programDuration: Int32?, revenuePerUser: Double, mode: JoinAffiliateProgramScreenMode) -> ViewController { return JoinAffiliateProgramScreen(context: context, sourcePeer: sourcePeer, commissionPermille: commissionPermille, programDuration: programDuration, revenuePerUser: revenuePerUser, mode: mode) } + + public func makeGalleryController(context: AccountContext, source: GalleryControllerItemSource, streamSingleVideo: Bool, isPreview: Bool) -> ViewController { + let controller = GalleryController(context: context, source: source, streamSingleVideo: streamSingleVideo, replaceRootController: { _, _ in + }, baseNavigationController: nil) + if isPreview { + controller.setHintWillBePresentedInPreviewingContext(true) + } + return controller + } } private func peerInfoControllerImpl(context: AccountContext, updatedPresentationData: (PresentationData, Signal)?, peer: Peer, mode: PeerInfoControllerMode, avatarInitiallyExpanded: Bool, isOpenedFromChat: Bool, requestsContext: PeerInvitationImportersContext? = nil) -> ViewController? { diff --git a/submodules/TelegramUI/Sources/TelegramRootController.swift b/submodules/TelegramUI/Sources/TelegramRootController.swift index 95a0038bfd..d9979b4cd1 100644 --- a/submodules/TelegramUI/Sources/TelegramRootController.swift +++ b/submodules/TelegramUI/Sources/TelegramRootController.swift @@ -378,7 +378,12 @@ public final class TelegramRootController: NavigationController, TelegramRootCon case let .asset(asset): content = .asset(asset) } - return MediaEditorScreenImpl.Subject.VideoCollageItem(content: content, frame: item.frame) + return MediaEditorScreenImpl.Subject.VideoCollageItem( + content: content, + frame: item.frame, + contentScale: item.contentScale, + contentOffset: item.contentOffset + ) } return .videoCollage(items: collage.items.map { editorCollageItem($0) }) case let .asset(asset): diff --git a/submodules/TelegramVoip/Sources/OngoingCallContext.swift b/submodules/TelegramVoip/Sources/OngoingCallContext.swift index c48dd4537f..a5a20e20c4 100644 --- a/submodules/TelegramVoip/Sources/OngoingCallContext.swift +++ b/submodules/TelegramVoip/Sources/OngoingCallContext.swift @@ -2,6 +2,7 @@ import Foundation import SwiftSignalKit import TelegramCore import Network +import TelegramUIPreferences import TgVoip import TgVoipWebrtc diff --git a/submodules/TextFormat/Sources/ChatTextInputAttributes.swift b/submodules/TextFormat/Sources/ChatTextInputAttributes.swift index 86aa5d1b9a..91266ba908 100644 --- a/submodules/TextFormat/Sources/ChatTextInputAttributes.swift +++ b/submodules/TextFormat/Sources/ChatTextInputAttributes.swift @@ -410,6 +410,7 @@ public final class ChatTextInputTextCustomEmojiAttribute: NSObject, Codable { case stars(tinted: Bool) case ton case animation(name: String) + case verification } public let interactivelySelectedFromPackId: ItemCollectionId? diff --git a/submodules/WebSearchUI/Sources/WebSearchControllerNode.swift b/submodules/WebSearchUI/Sources/WebSearchControllerNode.swift index b6ca23e4bb..b1fcd23e6e 100644 --- a/submodules/WebSearchUI/Sources/WebSearchControllerNode.swift +++ b/submodules/WebSearchUI/Sources/WebSearchControllerNode.swift @@ -289,7 +289,7 @@ class WebSearchControllerNode: ASDisplayNode { entries.append(WebSearchRecentQueryEntry(index: i, query: queries[i])) } - let header = ChatListSearchItemHeader(type: .recentPeers, theme: interfaceState.presentationData.theme, strings: interfaceState.presentationData.strings, actionTitle: interfaceState.presentationData.strings.WebSearch_RecentSectionClear, action: { + let header = ChatListSearchItemHeader(type: .recentPeers, theme: interfaceState.presentationData.theme, strings: interfaceState.presentationData.strings, actionTitle: interfaceState.presentationData.strings.WebSearch_RecentSectionClear, action: { _ in let _ = clearRecentWebSearchQueries(engine: strongSelf.context.engine).start() }) diff --git a/submodules/WebUI/BUILD b/submodules/WebUI/BUILD index 3dbe57365e..74e5f438e4 100644 --- a/submodules/WebUI/BUILD +++ b/submodules/WebUI/BUILD @@ -48,6 +48,7 @@ swift_library( "//submodules/TelegramUI/Components/ListItemComponentAdaptor", "//submodules/TelegramUI/Components/ListSectionComponent", "//submodules/TelegramUI/Components/Chat/ChatMessageItemImpl", + "//submodules/TelegramUI/Components/PremiumPeerShortcutComponent", "//submodules/DeviceLocationManager", "//submodules/DeviceAccess", "//submodules/TelegramUI/Components/Utils/GenerateStickerPlaceholderImage", diff --git a/submodules/WebUI/Sources/WebAppEmojiStatusAlertController.swift b/submodules/WebUI/Sources/WebAppEmojiStatusAlertController.swift index 9c461b0dc7..45039c81c9 100644 --- a/submodules/WebUI/Sources/WebAppEmojiStatusAlertController.swift +++ b/submodules/WebUI/Sources/WebAppEmojiStatusAlertController.swift @@ -61,6 +61,7 @@ private final class IconsNode: ASDisplayNode { if self.switchingToNext { self.currentIndex = (self.currentIndex + 1) % self.files.count disappearingAnimationLayer = self.animationLayer + self.switchingToNext = false } let file = self.files[self.currentIndex] let emoji = ChatTextInputTextCustomEmojiAttribute( diff --git a/submodules/lottie-ios/Sources/Public/Animation/AnimationView.swift b/submodules/lottie-ios/Sources/Public/Animation/AnimationView.swift index c5a8656e86..5d823c5e19 100644 --- a/submodules/lottie-ios/Sources/Public/Animation/AnimationView.swift +++ b/submodules/lottie-ios/Sources/Public/Animation/AnimationView.swift @@ -1290,7 +1290,7 @@ final public class AnimationView: AnimationViewBase { layerAnimation.fillMode = CAMediaTimingFillMode.both layerAnimation.repeatCount = loopMode.caAnimationConfiguration.repeatCount layerAnimation.autoreverses = loopMode.caAnimationConfiguration.autoreverses - + layerAnimation.isRemovedOnCompletion = false if timeOffset != 0 { let currentLayerTime = viewLayer?.convertTime(CACurrentMediaTime(), from: nil) ?? 0