From 21354c783df8738b488e3753d3506909775d78e5 Mon Sep 17 00:00:00 2001 From: Isaac <> Date: Wed, 13 Dec 2023 23:28:40 +0400 Subject: [PATCH 1/5] Limit possible lottie parameters --- .../Sources/AnimatedStickerFrameSource.swift | 4 ++-- submodules/rlottie/LottieInstance.mm | 5 +++++ 2 files changed, 7 insertions(+), 2 deletions(-) diff --git a/submodules/AnimatedStickerNode/Sources/AnimatedStickerFrameSource.swift b/submodules/AnimatedStickerNode/Sources/AnimatedStickerFrameSource.swift index 9d815f9921..81aae9de5b 100644 --- a/submodules/AnimatedStickerNode/Sources/AnimatedStickerFrameSource.swift +++ b/submodules/AnimatedStickerNode/Sources/AnimatedStickerFrameSource.swift @@ -592,9 +592,9 @@ public final class AnimatedStickerDirectFrameSource: AnimatedStickerFrameSource return nil } self.animation = animation - let frameCount = Int(animation.frameCount) + let frameCount = max(1, Int(animation.frameCount)) self.frameCount = frameCount - self.frameRate = Int(animation.frameRate) + self.frameRate = max(1, Int(animation.frameRate)) self.cache = cachePathPrefix.flatMap { cachePathPrefix in AnimatedStickerDirectFrameSourceCache(queue: queue, pathPrefix: cachePathPrefix, width: width, height: height, frameCount: frameCount, fitzModifier: fitzModifier, useHardware: useMetalCache) diff --git a/submodules/rlottie/LottieInstance.mm b/submodules/rlottie/LottieInstance.mm index 1d40bc2aab..4989491f50 100755 --- a/submodules/rlottie/LottieInstance.mm +++ b/submodules/rlottie/LottieInstance.mm @@ -49,7 +49,9 @@ } _frameCount = (int32_t)_animation->totalFrame(); + _frameCount = MAX(1, _frameCount); _frameRate = (int32_t)_animation->frameRate(); + _frameRate = MAX(1, _frameRate); size_t width = 0; size_t height = 0; @@ -59,6 +61,9 @@ return nil; } + width = MAX(1, width); + height = MAX(1, height); + _dimensions = CGSizeMake(width, height); if ((_frameRate > 360) || _animation->duration() > 9.0) { From 67f1443daa7b84a35d2fe37b1c7f38394f7873fe Mon Sep 17 00:00:00 2001 From: Isaac <> Date: Wed, 13 Dec 2023 23:29:02 +0400 Subject: [PATCH 2/5] Fill in default secret chat timer for standalone messages --- .../State/ManagedSecretChatOutgoingOperations.swift | 10 ++++++++-- 1 file changed, 8 insertions(+), 2 deletions(-) diff --git a/submodules/TelegramCore/Sources/State/ManagedSecretChatOutgoingOperations.swift b/submodules/TelegramCore/Sources/State/ManagedSecretChatOutgoingOperations.swift index daf468b03b..bcaf3dbe7e 100644 --- a/submodules/TelegramCore/Sources/State/ManagedSecretChatOutgoingOperations.swift +++ b/submodules/TelegramCore/Sources/State/ManagedSecretChatOutgoingOperations.swift @@ -1876,6 +1876,12 @@ private func sendStandaloneMessage(auxiliaryMethods: AccountAuxiliaryMethods, po if let value = contents.media { media.append(value) } + + var attributes = contents.attributes + if !attributes.contains(where: { $0 is AutoremoveTimeoutMessageAttribute }), let messageAutoremoveTimeout = state.messageAutoremoveTimeout { + attributes.append(AutoclearTimeoutMessageAttribute(timeout: messageAutoremoveTimeout, countdownBeginTime: nil)) + } + let message = Message( stableId: 1, stableVersion: 0, @@ -1892,7 +1898,7 @@ private func sendStandaloneMessage(auxiliaryMethods: AccountAuxiliaryMethods, po forwardInfo: nil, author: nil, text: contents.text, - attributes: contents.attributes, + attributes: attributes, media: media, peers: SimpleDictionary(), associatedMessages: SimpleDictionary(), @@ -1967,7 +1973,7 @@ private func sendStandaloneMessage(auxiliaryMethods: AccountAuxiliaryMethods, po } let entitiesAttribute = message.textEntitiesAttribute - let (tags, globalTags) = tagsForStoreMessage(incoming: false, attributes: contents.attributes, media: updatedMedia, textEntities: entitiesAttribute?.entities, isPinned: false) + let (tags, globalTags) = tagsForStoreMessage(incoming: false, attributes: attributes, media: updatedMedia, textEntities: entitiesAttribute?.entities, isPinned: false) let storedMessage = StoreMessage( peerId: peerId, From 9c818a240d6c6320089f903a1e5a5ac59b2ce6f5 Mon Sep 17 00:00:00 2001 From: Isaac <> Date: Wed, 13 Dec 2023 23:55:42 +0400 Subject: [PATCH 3/5] Disable title accessory panel screenshots --- submodules/TelegramUI/Sources/ChatControllerNode.swift | 8 +++++++- 1 file changed, 7 insertions(+), 1 deletion(-) diff --git a/submodules/TelegramUI/Sources/ChatControllerNode.swift b/submodules/TelegramUI/Sources/ChatControllerNode.swift index eb4fed6df6..4fd10e2b54 100644 --- a/submodules/TelegramUI/Sources/ChatControllerNode.swift +++ b/submodules/TelegramUI/Sources/ChatControllerNode.swift @@ -368,6 +368,8 @@ class ChatControllerNode: ASDisplayNode, UIScrollViewDelegate { self.titleAccessoryPanelContainer = ChatControllerTitlePanelNodeContainer() self.titleAccessoryPanelContainer.clipsToBounds = true + setLayerDisableScreenshots(self.titleAccessoryPanelContainer.layer, chatLocation.peerId?.namespace == Namespaces.Peer.SecretChat) + self.inputContextPanelContainer = ChatControllerTitlePanelNodeContainer() self.inputContextOverTextPanelContainer = ChatControllerTitlePanelNodeContainer() @@ -1012,7 +1014,11 @@ class ChatControllerNode: ASDisplayNode, UIScrollViewDelegate { } if let historyNodeContainer = self.historyNodeContainer as? HistoryNodeContainer { - historyNodeContainer.isSecret = self.chatPresentationInterfaceState.copyProtectionEnabled || self.chatLocation.peerId?.namespace == Namespaces.Peer.SecretChat + let isSecret = self.chatPresentationInterfaceState.copyProtectionEnabled || self.chatLocation.peerId?.namespace == Namespaces.Peer.SecretChat + if historyNodeContainer.isSecret != isSecret { + historyNodeContainer.isSecret = isSecret + setLayerDisableScreenshots(self.titleAccessoryPanelContainer.layer, isSecret) + } } var previousListBottomInset: CGFloat? From b66f748969be61520b154f9f4dea091b874b389f Mon Sep 17 00:00:00 2001 From: Isaac <> Date: Fri, 15 Dec 2023 15:13:40 +0400 Subject: [PATCH 4/5] [WIP] Channel appearance --- .../Sources/BotCheckoutControllerNode.swift | 9 +- ...BotCheckoutNativeCardEntryController.swift | 2 +- ...heckoutNativeCardEntryControllerNode.swift | 6 +- .../Sources/ViewControllerComponent.swift | 20 +- submodules/Display/Source/GenerateImage.swift | 1 + .../Display/Source/ListViewItemNode.swift | 6 +- .../ItemListUI/Sources/ItemListItem.swift | 4 + .../Sources/PeerInfoAvatarListNode.swift | 7 +- .../Sources/ReactionContextNode.swift | 2 + submodules/SettingsUI/BUILD | 2 + .../TextSizeSelectionController.swift | 4 +- .../Sources/ThemePickerController.swift | 1 + .../Sources/ThemePickerGridItem.swift | 2 +- .../ThemeAccentColorControllerNode.swift | 2 +- .../ThemeAutoNightSettingsController.swift | 1 + .../Themes/ThemePreviewControllerNode.swift | 4 +- .../Themes/ThemeSettingsController.swift | 27 +- .../Sources/ApiUtils/ApiGroupOrChannel.swift | 10 +- .../SyncCore/SyncCore_TelegramChannel.swift | 64 +- .../TelegramEngine/Messages/AdMessages.swift | 6 +- .../Sources/TelegramEngine/Peers/Peer.swift | 4 + .../Sources/Utils/PeerUtils.swift | 11 + .../Sources/PresentationData.swift | 26 + .../ChatRecentActionsHistoryTransition.swift | 2 +- .../Components/DynamicCornerRadiusView/BUILD | 18 + .../Sources/DynamicCornerRadiusView.swift | 90 ++ .../EmojiStatusSelectionComponent.swift | 54 +- .../Components/ListActionItemComponent/BUILD | 21 + .../Sources/ListActionItemComponent.swift | 187 +++ .../Components/ListItemComponentAdaptor/BUILD | 21 + .../Sources/ListItemComponentAdaptor.swift | 131 ++ .../Components/ListSectionComponent/BUILD | 21 + .../Sources/ListSectionComponent.swift | 288 ++++ .../NotificationExceptionsScreen.swift | 2 +- .../PeerAllowedReactionsScreen/BUILD | 1 + .../Sources/PeerAllowedReactionsScreen.swift | 79 +- .../PeerInfoScreenDisclosureItem.swift | 28 +- .../Sources/PeerInfoScreen.swift | 8 +- .../PremiumLockButtonSubtitleComponent/BUILD | 22 + .../PremiumLockButtonSubtitleComponent.swift | 83 + .../Settings/PeerNameColorScreen/BUILD | 14 + .../Sources/ChannelAppearanceScreen.swift | 1355 +++++++++++++++++ .../PeerNameColorChatPreviewItem.swift | 94 +- .../Sources/PeerNameColorItem.swift | 70 +- .../PeerNameColorProfilePreviewItem.swift | 207 ++- .../Settings/ThemeCarouselItem/BUILD | 37 + .../Sources/ThemeCarouselItem.swift | 154 +- .../Settings/ThemeSettingsThemeItem/BUILD | 33 + .../Sources}/ThemeSettingsThemeItem.swift | 125 +- .../Sources/SharedAccountContext.swift | 24 + 50 files changed, 3063 insertions(+), 327 deletions(-) create mode 100644 submodules/TelegramUI/Components/DynamicCornerRadiusView/BUILD create mode 100644 submodules/TelegramUI/Components/DynamicCornerRadiusView/Sources/DynamicCornerRadiusView.swift create mode 100644 submodules/TelegramUI/Components/ListActionItemComponent/BUILD create mode 100644 submodules/TelegramUI/Components/ListActionItemComponent/Sources/ListActionItemComponent.swift create mode 100644 submodules/TelegramUI/Components/ListItemComponentAdaptor/BUILD create mode 100644 submodules/TelegramUI/Components/ListItemComponentAdaptor/Sources/ListItemComponentAdaptor.swift create mode 100644 submodules/TelegramUI/Components/ListSectionComponent/BUILD create mode 100644 submodules/TelegramUI/Components/ListSectionComponent/Sources/ListSectionComponent.swift create mode 100644 submodules/TelegramUI/Components/PremiumLockButtonSubtitleComponent/BUILD create mode 100644 submodules/TelegramUI/Components/PremiumLockButtonSubtitleComponent/Sources/PremiumLockButtonSubtitleComponent.swift create mode 100644 submodules/TelegramUI/Components/Settings/PeerNameColorScreen/Sources/ChannelAppearanceScreen.swift create mode 100644 submodules/TelegramUI/Components/Settings/ThemeCarouselItem/BUILD rename submodules/{SettingsUI => TelegramUI/Components/Settings/ThemeCarouselItem}/Sources/ThemeCarouselItem.swift (86%) create mode 100644 submodules/TelegramUI/Components/Settings/ThemeSettingsThemeItem/BUILD rename submodules/{SettingsUI/Sources/Themes => TelegramUI/Components/Settings/ThemeSettingsThemeItem/Sources}/ThemeSettingsThemeItem.swift (85%) diff --git a/submodules/BotPaymentsUI/Sources/BotCheckoutControllerNode.swift b/submodules/BotPaymentsUI/Sources/BotCheckoutControllerNode.swift index bdc23e1a58..0b323ae9fc 100644 --- a/submodules/BotPaymentsUI/Sources/BotCheckoutControllerNode.swift +++ b/submodules/BotPaymentsUI/Sources/BotCheckoutControllerNode.swift @@ -903,10 +903,17 @@ final class BotCheckoutControllerNode: ItemListControllerNode, PKPaymentAuthoriz guard let publicToken = nativeParams["public_token"] as? String else { return } + + var customTokenizeUrl: String? + if let value = nativeParams["public_token"] as? String, let url = URL(string: value), let host = url.host { + if url.scheme == "https" && (host == "smart-glocal.com" || host.hasSuffix(".smart-glocal.com")) { + customTokenizeUrl = value + } + } var dismissImpl: (() -> Void)? let canSave = paymentForm.canSaveCredentials || paymentForm.passwordMissing - let controller = BotCheckoutNativeCardEntryController(context: strongSelf.context, provider: .smartglobal(isTesting: paymentForm.invoice.isTest, publicToken: publicToken), completion: { method in + let controller = BotCheckoutNativeCardEntryController(context: strongSelf.context, provider: .smartglobal(isTesting: paymentForm.invoice.isTest, publicToken: publicToken, customTokenizeUrl: customTokenizeUrl), completion: { method in guard let strongSelf = self else { return } diff --git a/submodules/BotPaymentsUI/Sources/BotCheckoutNativeCardEntryController.swift b/submodules/BotPaymentsUI/Sources/BotCheckoutNativeCardEntryController.swift index ab4254bd9f..74af758711 100644 --- a/submodules/BotPaymentsUI/Sources/BotCheckoutNativeCardEntryController.swift +++ b/submodules/BotPaymentsUI/Sources/BotCheckoutNativeCardEntryController.swift @@ -30,7 +30,7 @@ struct BotCheckoutNativeCardEntryAdditionalFields: OptionSet { final class BotCheckoutNativeCardEntryController: ViewController { enum Provider { case stripe(additionalFields: BotCheckoutNativeCardEntryAdditionalFields, publishableKey: String) - case smartglobal(isTesting: Bool, publicToken: String) + case smartglobal(isTesting: Bool, publicToken: String, customTokenizeUrl: String?) } private var controllerNode: BotCheckoutNativeCardEntryControllerNode { diff --git a/submodules/BotPaymentsUI/Sources/BotCheckoutNativeCardEntryControllerNode.swift b/submodules/BotPaymentsUI/Sources/BotCheckoutNativeCardEntryControllerNode.swift index 0602b878ef..9f497c90b9 100644 --- a/submodules/BotPaymentsUI/Sources/BotCheckoutNativeCardEntryControllerNode.swift +++ b/submodules/BotPaymentsUI/Sources/BotCheckoutNativeCardEntryControllerNode.swift @@ -310,9 +310,11 @@ final class BotCheckoutNativeCardEntryControllerNode: ViewControllerTracingNode, })) self.updateDone() - case let .smartglobal(isTesting, publicToken): + case let .smartglobal(isTesting, publicToken, customTokenizeUrl): let url: String - if isTesting { + if let customTokenizeUrl { + url = customTokenizeUrl + } else if isTesting { url = "https://tgb-playground.smart-glocal.com/cds/v1/tokenize/card" } else { url = "https://tgb.smart-glocal.com/cds/v1/tokenize/card" diff --git a/submodules/Components/ViewControllerComponent/Sources/ViewControllerComponent.swift b/submodules/Components/ViewControllerComponent/Sources/ViewControllerComponent.swift index d24d1e2a7c..0a5f0ae820 100644 --- a/submodules/Components/ViewControllerComponent/Sources/ViewControllerComponent.swift +++ b/submodules/Components/ViewControllerComponent/Sources/ViewControllerComponent.swift @@ -225,12 +225,26 @@ open class ViewControllerComponentContainer: ViewController { public var wasDismissed: (() -> Void)? - public init(context: AccountContext, component: C, navigationBarAppearance: NavigationBarAppearance, statusBarStyle: StatusBarStyle = .default, presentationMode: PresentationMode = .default, theme: Theme = .default) where C.EnvironmentType == ViewControllerComponentContainer.Environment { + public init( + context: AccountContext, + component: C, + navigationBarAppearance: NavigationBarAppearance, + statusBarStyle: StatusBarStyle = .default, + presentationMode: PresentationMode = .default, + theme: Theme = .default, + updatedPresentationData: (initial: PresentationData, signal: Signal)? = nil + ) where C.EnvironmentType == ViewControllerComponentContainer.Environment { self.context = context self.component = AnyComponent(component) self.theme = theme - let presentationData = context.sharedContext.currentPresentationData.with { $0 } + let presentationData: PresentationData + if let updatedPresentationData { + presentationData = updatedPresentationData.initial + } else { + presentationData = self.context.sharedContext.currentPresentationData.with { $0 } + } + let navigationBarPresentationData: NavigationBarPresentationData? switch navigationBarAppearance { @@ -243,7 +257,7 @@ open class ViewControllerComponentContainer: ViewController { } super.init(navigationBarPresentationData: navigationBarPresentationData) - self.presentationDataDisposable = (self.context.sharedContext.presentationData + self.presentationDataDisposable = ((updatedPresentationData?.signal ?? self.context.sharedContext.presentationData) |> deliverOnMainQueue).start(next: { [weak self] presentationData in if let strongSelf = self { var theme = presentationData.theme diff --git a/submodules/Display/Source/GenerateImage.swift b/submodules/Display/Source/GenerateImage.swift index 36c9486c3e..fabcda9f32 100644 --- a/submodules/Display/Source/GenerateImage.swift +++ b/submodules/Display/Source/GenerateImage.swift @@ -428,6 +428,7 @@ public func generateScaledImage(image: UIImage?, size: CGSize, opaque: Bool = tr public func generateSingleColorImage(size: CGSize, color: UIColor) -> UIImage? { return generateImage(size, contextGenerator: { size, context in + context.clear(CGRect(origin: CGPoint(), size: size)) context.setFillColor(color.cgColor) context.fill(CGRect(origin: CGPoint(), size: size)) }) diff --git a/submodules/Display/Source/ListViewItemNode.swift b/submodules/Display/Source/ListViewItemNode.swift index 7386ec3f17..bec5fbf86c 100644 --- a/submodules/Display/Source/ListViewItemNode.swift +++ b/submodules/Display/Source/ListViewItemNode.swift @@ -52,17 +52,19 @@ public enum ListViewItemNodeVisibility: Equatable { case visible(CGFloat, CGRect) } -public struct ListViewItemLayoutParams { +public struct ListViewItemLayoutParams: Equatable { public let width: CGFloat public let leftInset: CGFloat public let rightInset: CGFloat public let availableHeight: CGFloat + public let isStandalone: Bool - public init(width: CGFloat, leftInset: CGFloat, rightInset: CGFloat, availableHeight: CGFloat) { + public init(width: CGFloat, leftInset: CGFloat, rightInset: CGFloat, availableHeight: CGFloat, isStandalone: Bool = false) { self.width = width self.leftInset = leftInset self.rightInset = rightInset self.availableHeight = availableHeight + self.isStandalone = isStandalone } } diff --git a/submodules/ItemListUI/Sources/ItemListItem.swift b/submodules/ItemListUI/Sources/ItemListItem.swift index 7cede4afcb..1cadb4b7d7 100644 --- a/submodules/ItemListUI/Sources/ItemListItem.swift +++ b/submodules/ItemListUI/Sources/ItemListItem.swift @@ -122,6 +122,10 @@ public func itemListNeighborsPlainInsets(_ neighbors: ItemListNeighbors) -> UIEd } public func itemListNeighborsGroupedInsets(_ neighbors: ItemListNeighbors, _ params: ListViewItemLayoutParams) -> UIEdgeInsets { + if params.isStandalone { + return UIEdgeInsets() + } + let topInset: CGFloat switch neighbors.top { case .none: diff --git a/submodules/PeerInfoAvatarListNode/Sources/PeerInfoAvatarListNode.swift b/submodules/PeerInfoAvatarListNode/Sources/PeerInfoAvatarListNode.swift index 5b03157cac..6059880d4b 100644 --- a/submodules/PeerInfoAvatarListNode/Sources/PeerInfoAvatarListNode.swift +++ b/submodules/PeerInfoAvatarListNode/Sources/PeerInfoAvatarListNode.swift @@ -599,7 +599,12 @@ private final class VariableBlurView: UIVisualEffectView { fatalError("init(coder:) has not been implemented") } - override func updateTraitsIfNeeded() { + override func traitCollectionDidChange(_ previousTraitCollection: UITraitCollection?) { + if #available(iOS 13.0, *) { + if self.traitCollection.hasDifferentColorAppearance(comparedTo: previousTraitCollection) { + self.resetEffect() + } + } } private func resetEffect() { diff --git a/submodules/ReactionSelectionNode/Sources/ReactionContextNode.swift b/submodules/ReactionSelectionNode/Sources/ReactionContextNode.swift index a61a4d9ec1..0a2304340b 100644 --- a/submodules/ReactionSelectionNode/Sources/ReactionContextNode.swift +++ b/submodules/ReactionSelectionNode/Sources/ReactionContextNode.swift @@ -589,6 +589,7 @@ public final class ReactionContextNode: ASDisplayNode, UIScrollViewDelegate { strings: strongSelf.presentationData.strings, deviceMetrics: DeviceMetrics.iPhone13, emojiContent: emojiContent, + color: nil, backgroundColor: .clear, separatorColor: strongSelf.presentationData.theme.list.itemPlainSeparatorColor.withMultipliedAlpha(0.5), hideTopPanel: hideTopPanel, @@ -1215,6 +1216,7 @@ public final class ReactionContextNode: ASDisplayNode, UIScrollViewDelegate { strings: self.presentationData.strings, deviceMetrics: DeviceMetrics.iPhone13, emojiContent: emojiContent, + color: nil, backgroundColor: .clear, separatorColor: self.presentationData.theme.list.itemPlainSeparatorColor.withMultipliedAlpha(0.5), hideTopPanel: hideTopPanel, diff --git a/submodules/SettingsUI/BUILD b/submodules/SettingsUI/BUILD index 471983403b..373ff7b42e 100644 --- a/submodules/SettingsUI/BUILD +++ b/submodules/SettingsUI/BUILD @@ -117,6 +117,8 @@ swift_library( "//submodules/TelegramUI/Components/Settings/PeerNameColorScreen", "//submodules/ManagedAnimationNode:ManagedAnimationNode", "//submodules/TelegramUI/Components/Settings/QuickReactionSetupController", + "//submodules/TelegramUI/Components/Settings/ThemeCarouselItem", + "//submodules/TelegramUI/Components/Settings/ThemeSettingsThemeItem", ], visibility = [ "//visibility:public", diff --git a/submodules/SettingsUI/Sources/Text Size/TextSizeSelectionController.swift b/submodules/SettingsUI/Sources/Text Size/TextSizeSelectionController.swift index 06dad8b58c..0ff9343614 100644 --- a/submodules/SettingsUI/Sources/Text Size/TextSizeSelectionController.swift +++ b/submodules/SettingsUI/Sources/Text Size/TextSizeSelectionController.swift @@ -307,10 +307,10 @@ private final class TextSizeSelectionControllerNode: ASDisplayNode, UIScrollView let selfPeer: EnginePeer = .user(TelegramUser(id: self.context.account.peerId, accessHash: nil, firstName: nil, lastName: nil, username: nil, phone: nil, photo: [], botInfo: nil, restrictionInfo: nil, flags: [], emojiStatus: nil, usernames: [], storiesHidden: nil, nameColor: nil, backgroundEmojiId: nil, profileColor: nil, profileBackgroundEmojiId: nil)) let peer1: EnginePeer = .user(TelegramUser(id: PeerId(namespace: Namespaces.Peer.CloudUser, id: PeerId.Id._internalFromInt64Value(1)), accessHash: nil, firstName: self.presentationData.strings.Appearance_ThemePreview_ChatList_1_Name, lastName: nil, username: nil, phone: nil, photo: [], botInfo: nil, restrictionInfo: nil, flags: [], emojiStatus: nil, usernames: [], storiesHidden: nil, nameColor: nil, backgroundEmojiId: nil, profileColor: nil, profileBackgroundEmojiId: nil)) let peer2: EnginePeer = .user(TelegramUser(id: PeerId(namespace: Namespaces.Peer.CloudUser, id: PeerId.Id._internalFromInt64Value(2)), accessHash: nil, firstName: self.presentationData.strings.Appearance_ThemePreview_ChatList_2_Name, lastName: nil, username: nil, phone: nil, photo: [], botInfo: nil, restrictionInfo: nil, flags: [], emojiStatus: nil, usernames: [], storiesHidden: nil, nameColor: nil, backgroundEmojiId: nil, profileColor: nil, profileBackgroundEmojiId: nil)) - let peer3: EnginePeer = .channel(TelegramChannel(id: PeerId(namespace: Namespaces.Peer.CloudChannel, id: PeerId.Id._internalFromInt64Value(3)), accessHash: nil, title: self.presentationData.strings.Appearance_ThemePreview_ChatList_3_Name, username: nil, photo: [], creationDate: 0, version: 0, participationStatus: .member, info: .group(.init(flags: [])), flags: [], restrictionInfo: nil, adminRights: nil, bannedRights: nil, defaultBannedRights: nil, usernames: [], storiesHidden: nil, nameColor: nil, backgroundEmojiId: nil, profileColor: nil, profileBackgroundEmojiId: nil)) + let peer3: EnginePeer = .channel(TelegramChannel(id: PeerId(namespace: Namespaces.Peer.CloudChannel, id: PeerId.Id._internalFromInt64Value(3)), accessHash: nil, title: self.presentationData.strings.Appearance_ThemePreview_ChatList_3_Name, username: nil, photo: [], creationDate: 0, version: 0, participationStatus: .member, info: .group(.init(flags: [])), flags: [], restrictionInfo: nil, adminRights: nil, bannedRights: nil, defaultBannedRights: nil, usernames: [], storiesHidden: nil, nameColor: nil, backgroundEmojiId: nil, profileColor: nil, profileBackgroundEmojiId: nil, emojiStatus: nil)) let peer3Author: EnginePeer = .user(TelegramUser(id: PeerId(namespace: Namespaces.Peer.CloudUser, id: PeerId.Id._internalFromInt64Value(4)), accessHash: nil, firstName: self.presentationData.strings.Appearance_ThemePreview_ChatList_3_AuthorName, lastName: nil, username: nil, phone: nil, photo: [], botInfo: nil, restrictionInfo: nil, flags: [], emojiStatus: nil, usernames: [], storiesHidden: nil, nameColor: nil, backgroundEmojiId: nil, profileColor: nil, profileBackgroundEmojiId: nil)) let peer4: EnginePeer = .user(TelegramUser(id: PeerId(namespace: Namespaces.Peer.CloudUser, id: PeerId.Id._internalFromInt64Value(4)), accessHash: nil, firstName: self.presentationData.strings.Appearance_ThemePreview_ChatList_4_Name, lastName: nil, username: nil, phone: nil, photo: [], botInfo: nil, restrictionInfo: nil, flags: [], emojiStatus: nil, usernames: [], storiesHidden: nil, nameColor: nil, backgroundEmojiId: nil, profileColor: nil, profileBackgroundEmojiId: nil)) - let peer5: EnginePeer = .channel(TelegramChannel(id: PeerId(namespace: Namespaces.Peer.CloudChannel, id: PeerId.Id._internalFromInt64Value(5)), accessHash: nil, title: self.presentationData.strings.Appearance_ThemePreview_ChatList_5_Name, username: nil, photo: [], creationDate: 0, version: 0, participationStatus: .member, info: .broadcast(.init(flags: [])), flags: [], restrictionInfo: nil, adminRights: nil, bannedRights: nil, defaultBannedRights: nil, usernames: [], storiesHidden: nil, nameColor: nil, backgroundEmojiId: nil, profileColor: nil, profileBackgroundEmojiId: nil)) + let peer5: EnginePeer = .channel(TelegramChannel(id: PeerId(namespace: Namespaces.Peer.CloudChannel, id: PeerId.Id._internalFromInt64Value(5)), accessHash: nil, title: self.presentationData.strings.Appearance_ThemePreview_ChatList_5_Name, username: nil, photo: [], creationDate: 0, version: 0, participationStatus: .member, info: .broadcast(.init(flags: [])), flags: [], restrictionInfo: nil, adminRights: nil, bannedRights: nil, defaultBannedRights: nil, usernames: [], storiesHidden: nil, nameColor: nil, backgroundEmojiId: nil, profileColor: nil, profileBackgroundEmojiId: nil, emojiStatus: nil)) let peer6: EnginePeer = .user(TelegramUser(id: PeerId(namespace: Namespaces.Peer.SecretChat, id: PeerId.Id._internalFromInt64Value(5)), accessHash: nil, firstName: self.presentationData.strings.Appearance_ThemePreview_ChatList_6_Name, lastName: nil, username: nil, phone: nil, photo: [], botInfo: nil, restrictionInfo: nil, flags: [], emojiStatus: nil, usernames: [], storiesHidden: nil, nameColor: nil, backgroundEmojiId: nil, profileColor: nil, profileBackgroundEmojiId: nil)) let timestamp = self.referenceTimestamp diff --git a/submodules/SettingsUI/Sources/ThemePickerController.swift b/submodules/SettingsUI/Sources/ThemePickerController.swift index ea45e2a190..97198c6691 100644 --- a/submodules/SettingsUI/Sources/ThemePickerController.swift +++ b/submodules/SettingsUI/Sources/ThemePickerController.swift @@ -19,6 +19,7 @@ import ContextUI import UndoUI import ItemListPeerActionItem import AnimationUI +import ThemeSettingsThemeItem private final class ThemePickerControllerArguments { let context: AccountContext diff --git a/submodules/SettingsUI/Sources/ThemePickerGridItem.swift b/submodules/SettingsUI/Sources/ThemePickerGridItem.swift index b2d3d19c4d..2f00529fac 100644 --- a/submodules/SettingsUI/Sources/ThemePickerGridItem.swift +++ b/submodules/SettingsUI/Sources/ThemePickerGridItem.swift @@ -18,7 +18,7 @@ import AnimatedStickerNode import TelegramAnimatedStickerNode import ShimmerEffect import StickerResources - +import ThemeCarouselItem private var cachedBorderImages: [String: UIImage] = [:] private func generateBorderImage(theme: PresentationTheme, bordered: Bool, selected: Bool) -> UIImage? { diff --git a/submodules/SettingsUI/Sources/Themes/ThemeAccentColorControllerNode.swift b/submodules/SettingsUI/Sources/Themes/ThemeAccentColorControllerNode.swift index aff01e5072..1d9f103428 100644 --- a/submodules/SettingsUI/Sources/Themes/ThemeAccentColorControllerNode.swift +++ b/submodules/SettingsUI/Sources/Themes/ThemeAccentColorControllerNode.swift @@ -948,7 +948,7 @@ final class ThemeAccentColorControllerNode: ASDisplayNode, UIScrollViewDelegate let selfPeer: EnginePeer = .user(TelegramUser(id: self.context.account.peerId, accessHash: nil, firstName: nil, lastName: nil, username: nil, phone: nil, photo: [], botInfo: nil, restrictionInfo: nil, flags: [], emojiStatus: nil, usernames: [], storiesHidden: nil, nameColor: nil, backgroundEmojiId: nil, profileColor: nil, profileBackgroundEmojiId: nil)) let peer1: EnginePeer = .user(TelegramUser(id: PeerId(namespace: Namespaces.Peer.CloudUser, id: PeerId.Id._internalFromInt64Value(1)), accessHash: nil, firstName: self.presentationData.strings.Appearance_ThemePreview_ChatList_1_Name, lastName: nil, username: nil, phone: nil, photo: [], botInfo: nil, restrictionInfo: nil, flags: [], emojiStatus: nil, usernames: [], storiesHidden: nil, nameColor: nil, backgroundEmojiId: nil, profileColor: nil, profileBackgroundEmojiId: nil)) let peer2: EnginePeer = .user(TelegramUser(id: PeerId(namespace: Namespaces.Peer.CloudUser, id: PeerId.Id._internalFromInt64Value(2)), accessHash: nil, firstName: self.presentationData.strings.Appearance_ThemePreview_ChatList_2_Name, lastName: nil, username: nil, phone: nil, photo: [], botInfo: nil, restrictionInfo: nil, flags: [], emojiStatus: nil, usernames: [], storiesHidden: nil, nameColor: nil, backgroundEmojiId: nil, profileColor: nil, profileBackgroundEmojiId: nil)) - let peer3: EnginePeer = .channel(TelegramChannel(id: PeerId(namespace: Namespaces.Peer.CloudChannel, id: PeerId.Id._internalFromInt64Value(3)), accessHash: nil, title: self.presentationData.strings.Appearance_ThemePreview_ChatList_3_Name, username: nil, photo: [], creationDate: 0, version: 0, participationStatus: .member, info: .group(.init(flags: [])), flags: [], restrictionInfo: nil, adminRights: nil, bannedRights: nil, defaultBannedRights: nil, usernames: [], storiesHidden: nil, nameColor: nil, backgroundEmojiId: nil, profileColor: nil, profileBackgroundEmojiId: nil)) + let peer3: EnginePeer = .channel(TelegramChannel(id: PeerId(namespace: Namespaces.Peer.CloudChannel, id: PeerId.Id._internalFromInt64Value(3)), accessHash: nil, title: self.presentationData.strings.Appearance_ThemePreview_ChatList_3_Name, username: nil, photo: [], creationDate: 0, version: 0, participationStatus: .member, info: .group(.init(flags: [])), flags: [], restrictionInfo: nil, adminRights: nil, bannedRights: nil, defaultBannedRights: nil, usernames: [], storiesHidden: nil, nameColor: nil, backgroundEmojiId: nil, profileColor: nil, profileBackgroundEmojiId: nil, emojiStatus: nil)) let peer3Author: EnginePeer = .user(TelegramUser(id: PeerId(namespace: Namespaces.Peer.CloudUser, id: PeerId.Id._internalFromInt64Value(4)), accessHash: nil, firstName: self.presentationData.strings.Appearance_ThemePreview_ChatList_3_AuthorName, lastName: nil, username: nil, phone: nil, photo: [], botInfo: nil, restrictionInfo: nil, flags: [], emojiStatus: nil, usernames: [], storiesHidden: nil, nameColor: nil, backgroundEmojiId: nil, profileColor: nil, profileBackgroundEmojiId: nil)) let peer4: EnginePeer = .user(TelegramUser(id: PeerId(namespace: Namespaces.Peer.CloudUser, id: PeerId.Id._internalFromInt64Value(4)), accessHash: nil, firstName: self.presentationData.strings.Appearance_ThemePreview_ChatList_4_Name, lastName: nil, username: nil, phone: nil, photo: [], botInfo: nil, restrictionInfo: nil, flags: [], emojiStatus: nil, usernames: [], storiesHidden: nil, nameColor: nil, backgroundEmojiId: nil, profileColor: nil, profileBackgroundEmojiId: nil)) diff --git a/submodules/SettingsUI/Sources/Themes/ThemeAutoNightSettingsController.swift b/submodules/SettingsUI/Sources/Themes/ThemeAutoNightSettingsController.swift index e1b9d76ff0..2f8ca0cdab 100644 --- a/submodules/SettingsUI/Sources/Themes/ThemeAutoNightSettingsController.swift +++ b/submodules/SettingsUI/Sources/Themes/ThemeAutoNightSettingsController.swift @@ -14,6 +14,7 @@ import DeviceLocationManager import Geocoding import WallpaperResources import Sunrise +import ThemeSettingsThemeItem private enum TriggerMode { case system diff --git a/submodules/SettingsUI/Sources/Themes/ThemePreviewControllerNode.swift b/submodules/SettingsUI/Sources/Themes/ThemePreviewControllerNode.swift index 59ebed6a4b..20657800eb 100644 --- a/submodules/SettingsUI/Sources/Themes/ThemePreviewControllerNode.swift +++ b/submodules/SettingsUI/Sources/Themes/ThemePreviewControllerNode.swift @@ -455,10 +455,10 @@ final class ThemePreviewControllerNode: ASDisplayNode, UIScrollViewDelegate { let selfPeer: EnginePeer = .user(TelegramUser(id: self.context.account.peerId, accessHash: nil, firstName: nil, lastName: nil, username: nil, phone: nil, photo: [], botInfo: nil, restrictionInfo: nil, flags: [], emojiStatus: nil, usernames: [], storiesHidden: nil, nameColor: nil, backgroundEmojiId: nil, profileColor: nil, profileBackgroundEmojiId: nil)) let peer1: EnginePeer = .user(TelegramUser(id: PeerId(namespace: Namespaces.Peer.CloudUser, id: PeerId.Id._internalFromInt64Value(1)), accessHash: nil, firstName: self.presentationData.strings.Appearance_ThemePreview_ChatList_1_Name, lastName: nil, username: nil, phone: nil, photo: [], botInfo: nil, restrictionInfo: nil, flags: [], emojiStatus: nil, usernames: [], storiesHidden: nil, nameColor: nil, backgroundEmojiId: nil, profileColor: nil, profileBackgroundEmojiId: nil)) let peer2: EnginePeer = .user(TelegramUser(id: PeerId(namespace: Namespaces.Peer.CloudUser, id: PeerId.Id._internalFromInt64Value(2)), accessHash: nil, firstName: self.presentationData.strings.Appearance_ThemePreview_ChatList_2_Name, lastName: nil, username: nil, phone: nil, photo: [], botInfo: nil, restrictionInfo: nil, flags: [], emojiStatus: nil, usernames: [], storiesHidden: nil, nameColor: nil, backgroundEmojiId: nil, profileColor: nil, profileBackgroundEmojiId: nil)) - let peer3: EnginePeer = .channel(TelegramChannel(id: PeerId(namespace: Namespaces.Peer.CloudChannel, id: PeerId.Id._internalFromInt64Value(3)), accessHash: nil, title: self.presentationData.strings.Appearance_ThemePreview_ChatList_3_Name, username: nil, photo: [], creationDate: 0, version: 0, participationStatus: .member, info: .group(.init(flags: [])), flags: [], restrictionInfo: nil, adminRights: nil, bannedRights: nil, defaultBannedRights: nil, usernames: [], storiesHidden: nil, nameColor: nil, backgroundEmojiId: nil, profileColor: nil, profileBackgroundEmojiId: nil)) + let peer3: EnginePeer = .channel(TelegramChannel(id: PeerId(namespace: Namespaces.Peer.CloudChannel, id: PeerId.Id._internalFromInt64Value(3)), accessHash: nil, title: self.presentationData.strings.Appearance_ThemePreview_ChatList_3_Name, username: nil, photo: [], creationDate: 0, version: 0, participationStatus: .member, info: .group(.init(flags: [])), flags: [], restrictionInfo: nil, adminRights: nil, bannedRights: nil, defaultBannedRights: nil, usernames: [], storiesHidden: nil, nameColor: nil, backgroundEmojiId: nil, profileColor: nil, profileBackgroundEmojiId: nil, emojiStatus: nil)) let peer3Author: EnginePeer = .user(TelegramUser(id: PeerId(namespace: Namespaces.Peer.CloudUser, id: PeerId.Id._internalFromInt64Value(4)), accessHash: nil, firstName: self.presentationData.strings.Appearance_ThemePreview_ChatList_3_AuthorName, lastName: nil, username: nil, phone: nil, photo: [], botInfo: nil, restrictionInfo: nil, flags: [], emojiStatus: nil, usernames: [], storiesHidden: nil, nameColor: nil, backgroundEmojiId: nil, profileColor: nil, profileBackgroundEmojiId: nil)) let peer4: EnginePeer = .user(TelegramUser(id: PeerId(namespace: Namespaces.Peer.CloudUser, id: PeerId.Id._internalFromInt64Value(4)), accessHash: nil, firstName: self.presentationData.strings.Appearance_ThemePreview_ChatList_4_Name, lastName: nil, username: nil, phone: nil, photo: [], botInfo: nil, restrictionInfo: nil, flags: [], emojiStatus: nil, usernames: [], storiesHidden: nil, nameColor: nil, backgroundEmojiId: nil, profileColor: nil, profileBackgroundEmojiId: nil)) - let peer5: EnginePeer = .channel(TelegramChannel(id: PeerId(namespace: Namespaces.Peer.CloudChannel, id: PeerId.Id._internalFromInt64Value(5)), accessHash: nil, title: self.presentationData.strings.Appearance_ThemePreview_ChatList_5_Name, username: nil, photo: [], creationDate: 0, version: 0, participationStatus: .member, info: .broadcast(.init(flags: [])), flags: [], restrictionInfo: nil, adminRights: nil, bannedRights: nil, defaultBannedRights: nil, usernames: [], storiesHidden: nil, nameColor: nil, backgroundEmojiId: nil, profileColor: nil, profileBackgroundEmojiId: nil)) + let peer5: EnginePeer = .channel(TelegramChannel(id: PeerId(namespace: Namespaces.Peer.CloudChannel, id: PeerId.Id._internalFromInt64Value(5)), accessHash: nil, title: self.presentationData.strings.Appearance_ThemePreview_ChatList_5_Name, username: nil, photo: [], creationDate: 0, version: 0, participationStatus: .member, info: .broadcast(.init(flags: [])), flags: [], restrictionInfo: nil, adminRights: nil, bannedRights: nil, defaultBannedRights: nil, usernames: [], storiesHidden: nil, nameColor: nil, backgroundEmojiId: nil, profileColor: nil, profileBackgroundEmojiId: nil, emojiStatus: nil)) let peer6: EnginePeer = .user(TelegramUser(id: PeerId(namespace: Namespaces.Peer.SecretChat, id: PeerId.Id._internalFromInt64Value(5)), accessHash: nil, firstName: self.presentationData.strings.Appearance_ThemePreview_ChatList_6_Name, lastName: nil, username: nil, phone: nil, photo: [], botInfo: nil, restrictionInfo: nil, flags: [], emojiStatus: nil, usernames: [], storiesHidden: nil, nameColor: nil, backgroundEmojiId: nil, profileColor: nil, profileBackgroundEmojiId: nil)) let peer7: EnginePeer = .user(TelegramUser(id: PeerId(namespace: Namespaces.Peer.CloudUser, id: PeerId.Id._internalFromInt64Value(6)), accessHash: nil, firstName: self.presentationData.strings.Appearance_ThemePreview_ChatList_7_Name, lastName: nil, username: nil, phone: nil, photo: [], botInfo: nil, restrictionInfo: nil, flags: [], emojiStatus: nil, usernames: [], storiesHidden: nil, nameColor: nil, backgroundEmojiId: nil, profileColor: nil, profileBackgroundEmojiId: nil)) diff --git a/submodules/SettingsUI/Sources/Themes/ThemeSettingsController.swift b/submodules/SettingsUI/Sources/Themes/ThemeSettingsController.swift index a7740847bd..08e5dc5254 100644 --- a/submodules/SettingsUI/Sources/Themes/ThemeSettingsController.swift +++ b/submodules/SettingsUI/Sources/Themes/ThemeSettingsController.swift @@ -19,32 +19,7 @@ import ContextUI import UndoUI import PremiumUI import PeerNameColorScreen - -func themeDisplayName(strings: PresentationStrings, reference: PresentationThemeReference) -> String { - let name: String - switch reference { - case let .builtin(theme): - switch theme { - case .dayClassic: - name = strings.Appearance_ThemeCarouselClassic - case .day: - name = strings.Appearance_ThemeCarouselDay - case .night: - name = strings.Appearance_ThemeCarouselNewNight - case .nightAccent: - name = strings.Appearance_ThemeCarouselTintedNight - } - case let .local(theme): - name = theme.title - case let .cloud(theme): - if let emoticon = theme.theme.emoticon { - name = emoticon - } else { - name = theme.theme.title - } - } - return name -} +import ThemeCarouselItem private final class ThemeSettingsControllerArguments { let context: AccountContext diff --git a/submodules/TelegramCore/Sources/ApiUtils/ApiGroupOrChannel.swift b/submodules/TelegramCore/Sources/ApiUtils/ApiGroupOrChannel.swift index a94d824e56..1cf7db0a2d 100644 --- a/submodules/TelegramCore/Sources/ApiUtils/ApiGroupOrChannel.swift +++ b/submodules/TelegramCore/Sources/ApiUtils/ApiGroupOrChannel.swift @@ -173,7 +173,7 @@ func parseTelegramGroupOrChannel(chat: Api.Chat) -> Peer? { } } - return TelegramChannel(id: PeerId(namespace: Namespaces.Peer.CloudChannel, id: PeerId.Id._internalFromInt64Value(id)), accessHash: accessHashValue, title: title, username: username, photo: imageRepresentationsForApiChatPhoto(photo), creationDate: date, version: 0, participationStatus: participationStatus, info: info, flags: channelFlags, restrictionInfo: restrictionInfo, adminRights: adminRights.flatMap(TelegramChatAdminRights.init), bannedRights: bannedRights.flatMap(TelegramChatBannedRights.init), defaultBannedRights: defaultBannedRights.flatMap(TelegramChatBannedRights.init), usernames: usernames?.map(TelegramPeerUsername.init(apiUsername:)) ?? [], storiesHidden: storiesHidden, nameColor: nameColorIndex.flatMap { PeerNameColor(rawValue: $0) }, backgroundEmojiId: backgroundEmojiId, profileColor: profileColorIndex.flatMap { PeerNameColor(rawValue: $0) }, profileBackgroundEmojiId: profileBackgroundEmojiId) + return TelegramChannel(id: PeerId(namespace: Namespaces.Peer.CloudChannel, id: PeerId.Id._internalFromInt64Value(id)), accessHash: accessHashValue, title: title, username: username, photo: imageRepresentationsForApiChatPhoto(photo), creationDate: date, version: 0, participationStatus: participationStatus, info: info, flags: channelFlags, restrictionInfo: restrictionInfo, adminRights: adminRights.flatMap(TelegramChatAdminRights.init), bannedRights: bannedRights.flatMap(TelegramChatBannedRights.init), defaultBannedRights: defaultBannedRights.flatMap(TelegramChatBannedRights.init), usernames: usernames?.map(TelegramPeerUsername.init(apiUsername:)) ?? [], storiesHidden: storiesHidden, nameColor: nameColorIndex.flatMap { PeerNameColor(rawValue: $0) }, backgroundEmojiId: backgroundEmojiId, profileColor: profileColorIndex.flatMap { PeerNameColor(rawValue: $0) }, profileBackgroundEmojiId: profileBackgroundEmojiId, emojiStatus: nil) case let .channelForbidden(flags, id, accessHash, title, untilDate): let info: TelegramChannelInfo if (flags & Int32(1 << 8)) != 0 { @@ -182,7 +182,7 @@ func parseTelegramGroupOrChannel(chat: Api.Chat) -> Peer? { info = .broadcast(TelegramChannelBroadcastInfo(flags: [])) } - return TelegramChannel(id: PeerId(namespace: Namespaces.Peer.CloudChannel, id: PeerId.Id._internalFromInt64Value(id)), accessHash: .personal(accessHash), title: title, username: nil, photo: [], creationDate: 0, version: 0, participationStatus: .kicked, info: info, flags: TelegramChannelFlags(), restrictionInfo: nil, adminRights: nil, bannedRights: TelegramChatBannedRights(flags: [.banReadMessages], untilDate: untilDate ?? Int32.max), defaultBannedRights: nil, usernames: [], storiesHidden: nil, nameColor: nil, backgroundEmojiId: nil, profileColor: nil, profileBackgroundEmojiId: nil) + return TelegramChannel(id: PeerId(namespace: Namespaces.Peer.CloudChannel, id: PeerId.Id._internalFromInt64Value(id)), accessHash: .personal(accessHash), title: title, username: nil, photo: [], creationDate: 0, version: 0, participationStatus: .kicked, info: info, flags: TelegramChannelFlags(), restrictionInfo: nil, adminRights: nil, bannedRights: TelegramChatBannedRights(flags: [.banReadMessages], untilDate: untilDate ?? Int32.max), defaultBannedRights: nil, usernames: [], storiesHidden: nil, nameColor: nil, backgroundEmojiId: nil, profileColor: nil, profileBackgroundEmojiId: nil, emojiStatus: nil) } } @@ -252,7 +252,9 @@ func mergeGroupOrChannel(lhs: Peer?, rhs: Api.Chat) -> Peer? { } } - return TelegramChannel(id: lhs.id, accessHash: lhs.accessHash, title: title, username: username, photo: imageRepresentationsForApiChatPhoto(photo), creationDate: lhs.creationDate, version: lhs.version, participationStatus: lhs.participationStatus, info: info, flags: channelFlags, restrictionInfo: lhs.restrictionInfo, adminRights: lhs.adminRights, bannedRights: lhs.bannedRights, defaultBannedRights: defaultBannedRights.flatMap(TelegramChatBannedRights.init), usernames: usernames?.map(TelegramPeerUsername.init(apiUsername:)) ?? [], storiesHidden: storiesHidden, nameColor: nameColorIndex.flatMap { PeerNameColor(rawValue: $0) }, backgroundEmojiId: backgroundEmojiId, profileColor: profileColorIndex.flatMap { PeerNameColor(rawValue: $0) }, profileBackgroundEmojiId: profileBackgroundEmojiId) + let emojiStatus = lhs.emojiStatus + + return TelegramChannel(id: lhs.id, accessHash: lhs.accessHash, title: title, username: username, photo: imageRepresentationsForApiChatPhoto(photo), creationDate: lhs.creationDate, version: lhs.version, participationStatus: lhs.participationStatus, info: info, flags: channelFlags, restrictionInfo: lhs.restrictionInfo, adminRights: lhs.adminRights, bannedRights: lhs.bannedRights, defaultBannedRights: defaultBannedRights.flatMap(TelegramChatBannedRights.init), usernames: usernames?.map(TelegramPeerUsername.init(apiUsername:)) ?? [], storiesHidden: storiesHidden, nameColor: nameColorIndex.flatMap { PeerNameColor(rawValue: $0) }, backgroundEmojiId: backgroundEmojiId, profileColor: profileColorIndex.flatMap { PeerNameColor(rawValue: $0) }, profileBackgroundEmojiId: profileBackgroundEmojiId, emojiStatus: emojiStatus) } else { return parseTelegramGroupOrChannel(chat: rhs) } @@ -306,6 +308,6 @@ func mergeChannel(lhs: TelegramChannel?, rhs: TelegramChannel) -> TelegramChanne let storiesHidden: Bool? = rhs.storiesHidden ?? lhs.storiesHidden - return TelegramChannel(id: lhs.id, accessHash: accessHash, title: rhs.title, username: rhs.username, photo: rhs.photo, creationDate: rhs.creationDate, version: rhs.version, participationStatus: lhs.participationStatus, info: info, flags: channelFlags, restrictionInfo: rhs.restrictionInfo, adminRights: rhs.adminRights, bannedRights: rhs.bannedRights, defaultBannedRights: rhs.defaultBannedRights, usernames: rhs.usernames, storiesHidden: storiesHidden, nameColor: rhs.nameColor, backgroundEmojiId: rhs.backgroundEmojiId, profileColor: rhs.profileColor, profileBackgroundEmojiId: rhs.profileBackgroundEmojiId) + return TelegramChannel(id: lhs.id, accessHash: accessHash, title: rhs.title, username: rhs.username, photo: rhs.photo, creationDate: rhs.creationDate, version: rhs.version, participationStatus: lhs.participationStatus, info: info, flags: channelFlags, restrictionInfo: rhs.restrictionInfo, adminRights: rhs.adminRights, bannedRights: rhs.bannedRights, defaultBannedRights: rhs.defaultBannedRights, usernames: rhs.usernames, storiesHidden: storiesHidden, nameColor: rhs.nameColor, backgroundEmojiId: rhs.backgroundEmojiId, profileColor: rhs.profileColor, profileBackgroundEmojiId: rhs.profileBackgroundEmojiId, emojiStatus: rhs.emojiStatus) } diff --git a/submodules/TelegramCore/Sources/SyncCore/SyncCore_TelegramChannel.swift b/submodules/TelegramCore/Sources/SyncCore/SyncCore_TelegramChannel.swift index 08abf2fdab..4f0b779a0c 100644 --- a/submodules/TelegramCore/Sources/SyncCore/SyncCore_TelegramChannel.swift +++ b/submodules/TelegramCore/Sources/SyncCore/SyncCore_TelegramChannel.swift @@ -171,6 +171,7 @@ public final class TelegramChannel: Peer, Equatable { public let backgroundEmojiId: Int64? public let profileColor: PeerNameColor? public let profileBackgroundEmojiId: Int64? + public let emojiStatus: PeerEmojiStatus? public var indexName: PeerIndexNameRepresentation { var addressNames = self.usernames.map { $0.username } @@ -181,8 +182,19 @@ public final class TelegramChannel: Peer, Equatable { } public var associatedMediaIds: [MediaId]? { - if let backgroundEmojiId = self.backgroundEmojiId { - return [MediaId(namespace: Namespaces.Media.CloudFile, id: backgroundEmojiId)] + if let emojiStatus = self.emojiStatus, let backgroundEmojiId = self.backgroundEmojiId { + return [ + MediaId(namespace: Namespaces.Media.CloudFile, id: emojiStatus.fileId), + MediaId(namespace: Namespaces.Media.CloudFile, id: backgroundEmojiId) + ] + } else if let emojiStatus = self.emojiStatus { + return [ + MediaId(namespace: Namespaces.Media.CloudFile, id: emojiStatus.fileId) + ] + } else if let backgroundEmojiId = self.backgroundEmojiId { + return [ + MediaId(namespace: Namespaces.Media.CloudFile, id: backgroundEmojiId) + ] } else { return nil } @@ -191,7 +203,17 @@ public final class TelegramChannel: Peer, Equatable { public let associatedPeerId: PeerId? = nil public let notificationSettingsPeerId: PeerId? = nil - public var timeoutAttribute: UInt32? { return nil } + public var timeoutAttribute: UInt32? { + if let emojiStatus = self.emojiStatus { + if let expirationDate = emojiStatus.expirationDate { + return UInt32(max(0, expirationDate)) + } else { + return nil + } + } else { + return nil + } + } public init( id: PeerId, @@ -213,7 +235,8 @@ public final class TelegramChannel: Peer, Equatable { nameColor: PeerNameColor?, backgroundEmojiId: Int64?, profileColor: PeerNameColor?, - profileBackgroundEmojiId: Int64? + profileBackgroundEmojiId: Int64?, + emojiStatus: PeerEmojiStatus? ) { self.id = id self.accessHash = accessHash @@ -235,6 +258,7 @@ public final class TelegramChannel: Peer, Equatable { self.backgroundEmojiId = backgroundEmojiId self.profileColor = profileColor self.profileBackgroundEmojiId = profileBackgroundEmojiId + self.emojiStatus = emojiStatus } public init(decoder: PostboxDecoder) { @@ -268,6 +292,7 @@ public final class TelegramChannel: Peer, Equatable { self.backgroundEmojiId = decoder.decodeOptionalInt64ForKey("bgem") self.profileColor = decoder.decodeOptionalInt32ForKey("pclr").flatMap { PeerNameColor(rawValue: $0) } self.profileBackgroundEmojiId = decoder.decodeOptionalInt64ForKey("pgem") + self.emojiStatus = decoder.decode(PeerEmojiStatus.self, forKey: "emjs") } public func encode(_ encoder: PostboxEncoder) { @@ -347,6 +372,12 @@ public final class TelegramChannel: Peer, Equatable { } else { encoder.encodeNil(forKey: "pgem") } + + if let emojiStatus = self.emojiStatus { + encoder.encode(emojiStatus, forKey: "emjs") + } else { + encoder.encodeNil(forKey: "emjs") + } } public func isEqual(_ other: Peer) -> Bool { @@ -399,43 +430,50 @@ public final class TelegramChannel: Peer, Equatable { if lhs.profileBackgroundEmojiId != rhs.profileBackgroundEmojiId { return false } + if lhs.emojiStatus != rhs.emojiStatus { + return false + } return true } public func withUpdatedAddressName(_ addressName: String?) -> TelegramChannel { - return TelegramChannel(id: self.id, accessHash: self.accessHash, title: self.title, username: addressName, photo: self.photo, creationDate: self.creationDate, version: self.version, participationStatus: self.participationStatus, info: self.info, flags: self.flags, restrictionInfo: self.restrictionInfo, adminRights: self.adminRights, bannedRights: self.bannedRights, defaultBannedRights: self.defaultBannedRights, usernames: self.usernames, storiesHidden: self.storiesHidden, nameColor: self.nameColor, backgroundEmojiId: self.backgroundEmojiId, profileColor: self.profileColor, profileBackgroundEmojiId: self.profileBackgroundEmojiId) + return TelegramChannel(id: self.id, accessHash: self.accessHash, title: self.title, username: addressName, photo: self.photo, creationDate: self.creationDate, version: self.version, participationStatus: self.participationStatus, info: self.info, flags: self.flags, restrictionInfo: self.restrictionInfo, adminRights: self.adminRights, bannedRights: self.bannedRights, defaultBannedRights: self.defaultBannedRights, usernames: self.usernames, storiesHidden: self.storiesHidden, nameColor: self.nameColor, backgroundEmojiId: self.backgroundEmojiId, profileColor: self.profileColor, profileBackgroundEmojiId: self.profileBackgroundEmojiId, emojiStatus: self.emojiStatus) } public func withUpdatedAddressNames(_ addressNames: [TelegramPeerUsername]) -> TelegramChannel { - return TelegramChannel(id: self.id, accessHash: self.accessHash, title: self.title, username: self.username, photo: self.photo, creationDate: self.creationDate, version: self.version, participationStatus: self.participationStatus, info: self.info, flags: self.flags, restrictionInfo: self.restrictionInfo, adminRights: self.adminRights, bannedRights: self.bannedRights, defaultBannedRights: self.defaultBannedRights, usernames: addressNames, storiesHidden: self.storiesHidden, nameColor: self.nameColor, backgroundEmojiId: self.backgroundEmojiId, profileColor: self.profileColor, profileBackgroundEmojiId: self.profileBackgroundEmojiId) + return TelegramChannel(id: self.id, accessHash: self.accessHash, title: self.title, username: self.username, photo: self.photo, creationDate: self.creationDate, version: self.version, participationStatus: self.participationStatus, info: self.info, flags: self.flags, restrictionInfo: self.restrictionInfo, adminRights: self.adminRights, bannedRights: self.bannedRights, defaultBannedRights: self.defaultBannedRights, usernames: addressNames, storiesHidden: self.storiesHidden, nameColor: self.nameColor, backgroundEmojiId: self.backgroundEmojiId, profileColor: self.profileColor, profileBackgroundEmojiId: self.profileBackgroundEmojiId, emojiStatus: self.emojiStatus) } public func withUpdatedDefaultBannedRights(_ defaultBannedRights: TelegramChatBannedRights?) -> TelegramChannel { - return TelegramChannel(id: self.id, accessHash: self.accessHash, title: self.title, username: self.username, photo: self.photo, creationDate: self.creationDate, version: self.version, participationStatus: self.participationStatus, info: self.info, flags: self.flags, restrictionInfo: self.restrictionInfo, adminRights: self.adminRights, bannedRights: self.bannedRights, defaultBannedRights: defaultBannedRights, usernames: self.usernames, storiesHidden: self.storiesHidden, nameColor: self.nameColor, backgroundEmojiId: self.backgroundEmojiId, profileColor: self.profileColor, profileBackgroundEmojiId: self.profileBackgroundEmojiId) + return TelegramChannel(id: self.id, accessHash: self.accessHash, title: self.title, username: self.username, photo: self.photo, creationDate: self.creationDate, version: self.version, participationStatus: self.participationStatus, info: self.info, flags: self.flags, restrictionInfo: self.restrictionInfo, adminRights: self.adminRights, bannedRights: self.bannedRights, defaultBannedRights: defaultBannedRights, usernames: self.usernames, storiesHidden: self.storiesHidden, nameColor: self.nameColor, backgroundEmojiId: self.backgroundEmojiId, profileColor: self.profileColor, profileBackgroundEmojiId: self.profileBackgroundEmojiId, emojiStatus: self.emojiStatus) } public func withUpdatedFlags(_ flags: TelegramChannelFlags) -> TelegramChannel { - return TelegramChannel(id: self.id, accessHash: self.accessHash, title: self.title, username: self.username, photo: self.photo, creationDate: self.creationDate, version: self.version, participationStatus: self.participationStatus, info: self.info, flags: flags, restrictionInfo: self.restrictionInfo, adminRights: self.adminRights, bannedRights: self.bannedRights, defaultBannedRights: self.defaultBannedRights, usernames: self.usernames, storiesHidden: self.storiesHidden, nameColor: self.nameColor, backgroundEmojiId: self.backgroundEmojiId, profileColor: self.profileColor, profileBackgroundEmojiId: self.profileBackgroundEmojiId) + return TelegramChannel(id: self.id, accessHash: self.accessHash, title: self.title, username: self.username, photo: self.photo, creationDate: self.creationDate, version: self.version, participationStatus: self.participationStatus, info: self.info, flags: flags, restrictionInfo: self.restrictionInfo, adminRights: self.adminRights, bannedRights: self.bannedRights, defaultBannedRights: self.defaultBannedRights, usernames: self.usernames, storiesHidden: self.storiesHidden, nameColor: self.nameColor, backgroundEmojiId: self.backgroundEmojiId, profileColor: self.profileColor, profileBackgroundEmojiId: self.profileBackgroundEmojiId, emojiStatus: self.emojiStatus) } public func withUpdatedStoriesHidden(_ storiesHidden: Bool?) -> TelegramChannel { - return TelegramChannel(id: self.id, accessHash: self.accessHash, title: self.title, username: self.username, photo: self.photo, creationDate: self.creationDate, version: self.version, participationStatus: self.participationStatus, info: self.info, flags: self.flags, restrictionInfo: self.restrictionInfo, adminRights: self.adminRights, bannedRights: self.bannedRights, defaultBannedRights: self.defaultBannedRights, usernames: self.usernames, storiesHidden: storiesHidden, nameColor: self.nameColor, backgroundEmojiId: self.backgroundEmojiId, profileColor: self.profileColor, profileBackgroundEmojiId: self.profileBackgroundEmojiId) + return TelegramChannel(id: self.id, accessHash: self.accessHash, title: self.title, username: self.username, photo: self.photo, creationDate: self.creationDate, version: self.version, participationStatus: self.participationStatus, info: self.info, flags: self.flags, restrictionInfo: self.restrictionInfo, adminRights: self.adminRights, bannedRights: self.bannedRights, defaultBannedRights: self.defaultBannedRights, usernames: self.usernames, storiesHidden: storiesHidden, nameColor: self.nameColor, backgroundEmojiId: self.backgroundEmojiId, profileColor: self.profileColor, profileBackgroundEmojiId: self.profileBackgroundEmojiId, emojiStatus: self.emojiStatus) } public func withUpdatedNameColor(_ nameColor: PeerNameColor?) -> TelegramChannel { - return TelegramChannel(id: self.id, accessHash: self.accessHash, title: self.title, username: self.username, photo: self.photo, creationDate: self.creationDate, version: self.version, participationStatus: self.participationStatus, info: self.info, flags: self.flags, restrictionInfo: self.restrictionInfo, adminRights: self.adminRights, bannedRights: self.bannedRights, defaultBannedRights: self.defaultBannedRights, usernames: self.usernames, storiesHidden: self.storiesHidden, nameColor: nameColor, backgroundEmojiId: self.backgroundEmojiId, profileColor: self.profileColor, profileBackgroundEmojiId: self.profileBackgroundEmojiId) + return TelegramChannel(id: self.id, accessHash: self.accessHash, title: self.title, username: self.username, photo: self.photo, creationDate: self.creationDate, version: self.version, participationStatus: self.participationStatus, info: self.info, flags: self.flags, restrictionInfo: self.restrictionInfo, adminRights: self.adminRights, bannedRights: self.bannedRights, defaultBannedRights: self.defaultBannedRights, usernames: self.usernames, storiesHidden: self.storiesHidden, nameColor: nameColor, backgroundEmojiId: self.backgroundEmojiId, profileColor: self.profileColor, profileBackgroundEmojiId: self.profileBackgroundEmojiId, emojiStatus: self.emojiStatus) } public func withUpdatedBackgroundEmojiId(_ backgroundEmojiId: Int64?) -> TelegramChannel { - return TelegramChannel(id: self.id, accessHash: self.accessHash, title: self.title, username: self.username, photo: self.photo, creationDate: self.creationDate, version: self.version, participationStatus: self.participationStatus, info: self.info, flags: self.flags, restrictionInfo: self.restrictionInfo, adminRights: self.adminRights, bannedRights: self.bannedRights, defaultBannedRights: self.defaultBannedRights, usernames: self.usernames, storiesHidden: self.storiesHidden, nameColor: self.nameColor, backgroundEmojiId: backgroundEmojiId, profileColor: self.profileColor, profileBackgroundEmojiId: self.profileBackgroundEmojiId) + return TelegramChannel(id: self.id, accessHash: self.accessHash, title: self.title, username: self.username, photo: self.photo, creationDate: self.creationDate, version: self.version, participationStatus: self.participationStatus, info: self.info, flags: self.flags, restrictionInfo: self.restrictionInfo, adminRights: self.adminRights, bannedRights: self.bannedRights, defaultBannedRights: self.defaultBannedRights, usernames: self.usernames, storiesHidden: self.storiesHidden, nameColor: self.nameColor, backgroundEmojiId: backgroundEmojiId, profileColor: self.profileColor, profileBackgroundEmojiId: self.profileBackgroundEmojiId, emojiStatus: self.emojiStatus) } public func withUpdatedProfileColor(_ profileColor: PeerNameColor?) -> TelegramChannel { - return TelegramChannel(id: self.id, accessHash: self.accessHash, title: self.title, username: self.username, photo: self.photo, creationDate: self.creationDate, version: self.version, participationStatus: self.participationStatus, info: self.info, flags: self.flags, restrictionInfo: self.restrictionInfo, adminRights: self.adminRights, bannedRights: self.bannedRights, defaultBannedRights: self.defaultBannedRights, usernames: self.usernames, storiesHidden: self.storiesHidden, nameColor: self.nameColor, backgroundEmojiId: self.backgroundEmojiId, profileColor: profileColor, profileBackgroundEmojiId: self.profileBackgroundEmojiId) + return TelegramChannel(id: self.id, accessHash: self.accessHash, title: self.title, username: self.username, photo: self.photo, creationDate: self.creationDate, version: self.version, participationStatus: self.participationStatus, info: self.info, flags: self.flags, restrictionInfo: self.restrictionInfo, adminRights: self.adminRights, bannedRights: self.bannedRights, defaultBannedRights: self.defaultBannedRights, usernames: self.usernames, storiesHidden: self.storiesHidden, nameColor: self.nameColor, backgroundEmojiId: self.backgroundEmojiId, profileColor: profileColor, profileBackgroundEmojiId: self.profileBackgroundEmojiId, emojiStatus: self.emojiStatus) } public func withUpdatedProfileBackgroundEmojiId(_ profileBackgroundEmojiId: Int64?) -> TelegramChannel { - return TelegramChannel(id: self.id, accessHash: self.accessHash, title: self.title, username: self.username, photo: self.photo, creationDate: self.creationDate, version: self.version, participationStatus: self.participationStatus, info: self.info, flags: self.flags, restrictionInfo: self.restrictionInfo, adminRights: self.adminRights, bannedRights: self.bannedRights, defaultBannedRights: self.defaultBannedRights, usernames: self.usernames, storiesHidden: self.storiesHidden, nameColor: self.nameColor, backgroundEmojiId: self.backgroundEmojiId, profileColor: self.profileColor, profileBackgroundEmojiId: profileBackgroundEmojiId) + return TelegramChannel(id: self.id, accessHash: self.accessHash, title: self.title, username: self.username, photo: self.photo, creationDate: self.creationDate, version: self.version, participationStatus: self.participationStatus, info: self.info, flags: self.flags, restrictionInfo: self.restrictionInfo, adminRights: self.adminRights, bannedRights: self.bannedRights, defaultBannedRights: self.defaultBannedRights, usernames: self.usernames, storiesHidden: self.storiesHidden, nameColor: self.nameColor, backgroundEmojiId: self.backgroundEmojiId, profileColor: self.profileColor, profileBackgroundEmojiId: profileBackgroundEmojiId, emojiStatus: self.emojiStatus) + } + + public func withUpdatedEmojiStatus(_ emojiStatus: PeerEmojiStatus?) -> TelegramChannel { + return TelegramChannel(id: self.id, accessHash: self.accessHash, title: self.title, username: self.username, photo: self.photo, creationDate: self.creationDate, version: self.version, participationStatus: self.participationStatus, info: self.info, flags: self.flags, restrictionInfo: self.restrictionInfo, adminRights: self.adminRights, bannedRights: self.bannedRights, defaultBannedRights: self.defaultBannedRights, usernames: self.usernames, storiesHidden: self.storiesHidden, nameColor: self.nameColor, backgroundEmojiId: self.backgroundEmojiId, profileColor: self.profileColor, profileBackgroundEmojiId: self.profileBackgroundEmojiId, emojiStatus: emojiStatus) } } diff --git a/submodules/TelegramCore/Sources/TelegramEngine/Messages/AdMessages.swift b/submodules/TelegramCore/Sources/TelegramEngine/Messages/AdMessages.swift index 493c924e6c..38eb51b49e 100644 --- a/submodules/TelegramCore/Sources/TelegramEngine/Messages/AdMessages.swift +++ b/submodules/TelegramCore/Sources/TelegramEngine/Messages/AdMessages.swift @@ -315,7 +315,8 @@ private class AdMessagesHistoryContextImpl { nameColor: invite.nameColor, backgroundEmojiId: nil, profileColor: nil, - profileBackgroundEmojiId: nil + profileBackgroundEmojiId: nil, + emojiStatus: nil ) case let .webPage(webPage): author = TelegramChannel( @@ -338,7 +339,8 @@ private class AdMessagesHistoryContextImpl { nameColor: .blue, backgroundEmojiId: nil, profileColor: nil, - profileBackgroundEmojiId: nil + profileBackgroundEmojiId: nil, + emojiStatus: nil ) } diff --git a/submodules/TelegramCore/Sources/TelegramEngine/Peers/Peer.swift b/submodules/TelegramCore/Sources/TelegramEngine/Peers/Peer.swift index 8f6fca61b8..06cbf99f62 100644 --- a/submodules/TelegramCore/Sources/TelegramEngine/Peers/Peer.swift +++ b/submodules/TelegramCore/Sources/TelegramEngine/Peers/Peer.swift @@ -514,6 +514,10 @@ public extension EnginePeer { return self._asPeer().profileColor } + var emojiStatus: PeerEmojiStatus? { + return self._asPeer().emojiStatus + } + var backgroundEmojiId: Int64? { return self._asPeer().backgroundEmojiId } diff --git a/submodules/TelegramCore/Sources/Utils/PeerUtils.swift b/submodules/TelegramCore/Sources/Utils/PeerUtils.swift index 53aaa0e3bb..9a07ce5ac0 100644 --- a/submodules/TelegramCore/Sources/Utils/PeerUtils.swift +++ b/submodules/TelegramCore/Sources/Utils/PeerUtils.swift @@ -255,6 +255,17 @@ public extension Peer { return false } + var emojiStatus: PeerEmojiStatus? { + switch self { + case let user as TelegramUser: + return user.emojiStatus + case let channel as TelegramChannel: + return channel.emojiStatus + default: + return nil + } + } + var backgroundEmojiId: Int64? { switch self { case let user as TelegramUser: diff --git a/submodules/TelegramPresentationData/Sources/PresentationData.swift b/submodules/TelegramPresentationData/Sources/PresentationData.swift index 002043cccf..bcbb763eee 100644 --- a/submodules/TelegramPresentationData/Sources/PresentationData.swift +++ b/submodules/TelegramPresentationData/Sources/PresentationData.swift @@ -875,3 +875,29 @@ public extension PresentationData { return PresentationData(strings: strings, theme: self.theme, autoNightModeTriggered: self.autoNightModeTriggered, chatWallpaper: self.chatWallpaper, chatFontSize: self.chatFontSize, chatBubbleCorners: chatBubbleCorners, listsFontSize: self.listsFontSize, dateTimeFormat: self.dateTimeFormat, nameDisplayOrder: self.nameDisplayOrder, nameSortOrder: self.nameSortOrder, reduceMotion: self.reduceMotion, largeEmoji: self.largeEmoji) } } + +public func themeDisplayName(strings: PresentationStrings, reference: PresentationThemeReference) -> String { + let name: String + switch reference { + case let .builtin(theme): + switch theme { + case .dayClassic: + name = strings.Appearance_ThemeCarouselClassic + case .day: + name = strings.Appearance_ThemeCarouselDay + case .night: + name = strings.Appearance_ThemeCarouselNewNight + case .nightAccent: + name = strings.Appearance_ThemeCarouselTintedNight + } + case let .local(theme): + name = theme.title + case let .cloud(theme): + if let emoticon = theme.theme.emoticon { + name = emoticon + } else { + name = theme.theme.title + } + } + return name +} diff --git a/submodules/TelegramUI/Components/Chat/ChatRecentActionsController/Sources/ChatRecentActionsHistoryTransition.swift b/submodules/TelegramUI/Components/Chat/ChatRecentActionsController/Sources/ChatRecentActionsHistoryTransition.swift index 4f62d1263b..949929da9e 100644 --- a/submodules/TelegramUI/Components/Chat/ChatRecentActionsController/Sources/ChatRecentActionsHistoryTransition.swift +++ b/submodules/TelegramUI/Components/Chat/ChatRecentActionsController/Sources/ChatRecentActionsHistoryTransition.swift @@ -69,7 +69,7 @@ private func filterOriginalMessageFlags(_ message: Message) -> Message { private func filterMessageChannelPeer(_ peer: Peer) -> Peer { if let peer = peer as? TelegramChannel { - return TelegramChannel(id: peer.id, accessHash: peer.accessHash, title: peer.title, username: peer.username, photo: peer.photo, creationDate: peer.creationDate, version: peer.version, participationStatus: peer.participationStatus, info: .group(TelegramChannelGroupInfo(flags: [])), flags: peer.flags, restrictionInfo: peer.restrictionInfo, adminRights: peer.adminRights, bannedRights: peer.bannedRights, defaultBannedRights: peer.defaultBannedRights, usernames: peer.usernames, storiesHidden: peer.storiesHidden, nameColor: peer.nameColor, backgroundEmojiId: peer.backgroundEmojiId, profileColor: peer.profileColor, profileBackgroundEmojiId: peer.profileBackgroundEmojiId) + return TelegramChannel(id: peer.id, accessHash: peer.accessHash, title: peer.title, username: peer.username, photo: peer.photo, creationDate: peer.creationDate, version: peer.version, participationStatus: peer.participationStatus, info: .group(TelegramChannelGroupInfo(flags: [])), flags: peer.flags, restrictionInfo: peer.restrictionInfo, adminRights: peer.adminRights, bannedRights: peer.bannedRights, defaultBannedRights: peer.defaultBannedRights, usernames: peer.usernames, storiesHidden: peer.storiesHidden, nameColor: peer.nameColor, backgroundEmojiId: peer.backgroundEmojiId, profileColor: peer.profileColor, profileBackgroundEmojiId: peer.profileBackgroundEmojiId, emojiStatus: peer.emojiStatus) } return peer } diff --git a/submodules/TelegramUI/Components/DynamicCornerRadiusView/BUILD b/submodules/TelegramUI/Components/DynamicCornerRadiusView/BUILD new file mode 100644 index 0000000000..a6c83e6f0e --- /dev/null +++ b/submodules/TelegramUI/Components/DynamicCornerRadiusView/BUILD @@ -0,0 +1,18 @@ +load("@build_bazel_rules_swift//swift:swift.bzl", "swift_library") + +swift_library( + name = "DynamicCornerRadiusView", + module_name = "DynamicCornerRadiusView", + srcs = glob([ + "Sources/**/*.swift", + ]), + copts = [ + "-warnings-as-errors", + ], + deps = [ + "//submodules/ComponentFlow" + ], + visibility = [ + "//visibility:public", + ], +) diff --git a/submodules/TelegramUI/Components/DynamicCornerRadiusView/Sources/DynamicCornerRadiusView.swift b/submodules/TelegramUI/Components/DynamicCornerRadiusView/Sources/DynamicCornerRadiusView.swift new file mode 100644 index 0000000000..531a3bb4a8 --- /dev/null +++ b/submodules/TelegramUI/Components/DynamicCornerRadiusView/Sources/DynamicCornerRadiusView.swift @@ -0,0 +1,90 @@ +import Foundation +import UIKit +import ComponentFlow + +private func generatePath(size: CGSize, corners: DynamicCornerRadiusView.Corners) -> CGPath { + let path = CGMutablePath() + + var corners = corners + corners.minXMinY = max(0.01, corners.minXMinY) + corners.maxXMinY = max(0.01, corners.maxXMinY) + corners.minXMaxY = max(0.01, corners.minXMaxY) + corners.maxXMaxY = max(0.01, corners.maxXMaxY) + + path.move(to: CGPoint(x: 0.0, y: corners.minXMinY)) + path.addArc(tangent1End: CGPoint(x: 0.0, y: 0.0), tangent2End: CGPoint(x: corners.minXMinY, y: 0.0), radius: corners.minXMinY) + path.addLine(to: CGPoint(x: size.width - corners.maxXMinY, y: 0.0)) + path.addArc(tangent1End: CGPoint(x: size.width, y: 0.0), tangent2End: CGPoint(x: size.width, y: corners.maxXMinY), radius: corners.maxXMinY) + path.addLine(to: CGPoint(x: size.width, y: size.height - corners.maxXMaxY)) + path.addArc(tangent1End: CGPoint(x: size.width, y: size.height), tangent2End: CGPoint(x: size.width - corners.maxXMaxY, y: size.height), radius: corners.maxXMaxY) + path.addLine(to: CGPoint(x: corners.minXMaxY, y: size.height)) + path.addArc(tangent1End: CGPoint(x: 0.0, y: size.height), tangent2End: CGPoint(x: 0.0, y: size.height - corners.minXMaxY), radius: corners.minXMaxY) + path.closeSubpath() + + return path +} + +open class DynamicCornerRadiusView: UIView { + override public static var layerClass: AnyClass { + return CAShapeLayer.self + } + + public struct Corners: Equatable { + public var minXMinY: CGFloat + public var maxXMinY: CGFloat + public var minXMaxY: CGFloat + public var maxXMaxY: CGFloat + + public init(minXMinY: CGFloat, maxXMinY: CGFloat, minXMaxY: CGFloat, maxXMaxY: CGFloat) { + self.minXMinY = minXMinY + self.maxXMinY = maxXMinY + self.minXMaxY = minXMaxY + self.maxXMaxY = maxXMaxY + } + } + + private struct Params: Equatable { + var size: CGSize + var corners: Corners + + init(size: CGSize, corners: Corners) { + self.size = size + self.corners = corners + } + } + + private var params: Params? + + override public init(frame: CGRect) { + super.init(frame: frame) + + if let shapeLayer = self.layer as? CAShapeLayer { + shapeLayer.strokeColor = nil + } + } + + required public init?(coder: NSCoder) { + fatalError("init(coder:) has not been implemented") + } + + public func update(size: CGSize, corners: Corners, transition: Transition) { + let params = Params(size: size, corners: corners) + if self.params == params { + return + } + self.params = params + self.update(params: params, transition: transition) + } + + public func updateColor(color: UIColor, transition: Transition) { + if let shapeLayer = self.layer as? CAShapeLayer { + transition.setShapeLayerFillColor(layer: shapeLayer, color: color) + } + } + + private func update(params: Params, transition: Transition) { + if let shapeLayer = self.layer as? CAShapeLayer { + transition.setShapeLayerPath(layer: shapeLayer, path: generatePath(size: params.size, corners: params.corners)) + } + } +} diff --git a/submodules/TelegramUI/Components/EmojiStatusSelectionComponent/Sources/EmojiStatusSelectionComponent.swift b/submodules/TelegramUI/Components/EmojiStatusSelectionComponent/Sources/EmojiStatusSelectionComponent.swift index 8df436290e..4be7f89a58 100644 --- a/submodules/TelegramUI/Components/EmojiStatusSelectionComponent/Sources/EmojiStatusSelectionComponent.swift +++ b/submodules/TelegramUI/Components/EmojiStatusSelectionComponent/Sources/EmojiStatusSelectionComponent.swift @@ -64,6 +64,7 @@ public final class EmojiStatusSelectionComponent: Component { public let emojiContent: EmojiPagerContentComponent public let backgroundColor: UIColor public let separatorColor: UIColor + public let color: UIColor? public let hideTopPanel: Bool public let disableTopPanel: Bool public let hideTopPanelUpdated: (Bool, Transition) -> Void @@ -73,6 +74,7 @@ public final class EmojiStatusSelectionComponent: Component { strings: PresentationStrings, deviceMetrics: DeviceMetrics, emojiContent: EmojiPagerContentComponent, + color: UIColor?, backgroundColor: UIColor, separatorColor: UIColor, hideTopPanel: Bool, @@ -83,6 +85,7 @@ public final class EmojiStatusSelectionComponent: Component { self.strings = strings self.deviceMetrics = deviceMetrics self.emojiContent = emojiContent + self.color = color self.backgroundColor = backgroundColor self.separatorColor = separatorColor self.hideTopPanel = hideTopPanel @@ -103,6 +106,9 @@ public final class EmojiStatusSelectionComponent: Component { if lhs.emojiContent != rhs.emojiContent { return false } + if lhs.color != rhs.color { + return false + } if lhs.backgroundColor != rhs.backgroundColor { return false } @@ -169,7 +175,7 @@ public final class EmojiStatusSelectionComponent: Component { isContentInFocus: true, containerInsets: UIEdgeInsets(top: topPanelHeight - 34.0, left: 0.0, bottom: 0.0, right: 0.0), topPanelInsets: UIEdgeInsets(top: 0.0, left: 4.0, bottom: 0.0, right: 4.0), - emojiContent: component.emojiContent, + emojiContent: component.emojiContent.withCustomTintColor(component.color), stickerContent: nil, maskContent: nil, gifContent: nil, @@ -200,7 +206,8 @@ public final class EmojiStatusSelectionComponent: Component { isExpanded: false, clipContentToTopPanel: false, useExternalSearchContainer: false, - hidePanels: component.disableTopPanel + hidePanels: component.disableTopPanel, + customTintColor: component.color )), environment: {}, containerSize: availableSize @@ -277,6 +284,7 @@ public final class EmojiStatusSelectionController: ViewController { private var validLayout: ContainerViewLayout? private let currentSelection: Int64? + private let color: UIColor? private var emojiContentDisposable: Disposable? private var emojiContent: EmojiPagerContentComponent? @@ -312,10 +320,11 @@ public final class EmojiStatusSelectionController: ViewController { private var isReactionSearchActive: Bool = false - init(controller: EmojiStatusSelectionController, context: AccountContext, sourceView: UIView?, emojiContent: Signal, currentSelection: Int64?) { + init(controller: EmojiStatusSelectionController, context: AccountContext, sourceView: UIView?, emojiContent: Signal, currentSelection: Int64?, color: UIColor?) { self.controller = controller self.context = context self.currentSelection = currentSelection + self.color = color if let sourceView = sourceView { self.globalSourceRect = sourceView.convert(sourceView.bounds, to: nil) @@ -826,17 +835,26 @@ public final class EmojiStatusSelectionController: ViewController { renderer: animationRenderer, placeholderColor: UIColor(white: 0.0, alpha: 0.0), pointSize: CGSize(width: 32.0, height: 32.0), - dynamicColor: self.presentationData.theme.list.itemAccentColor + dynamicColor: self.color ?? self.presentationData.theme.list.itemAccentColor ) - switch item.tintMode { - case let .custom(color): - baseItemLayer.contentTintColor = color - case .accent: - baseItemLayer.contentTintColor = self.presentationData.theme.list.itemAccentColor - case .primary: - baseItemLayer.contentTintColor = self.presentationData.theme.list.itemPrimaryTextColor - case .none: - break + if let color = self.color { + switch item.tintMode { + case .none: + break + default: + baseItemLayer.contentTintColor = color + } + } else { + switch item.tintMode { + case let .custom(color): + baseItemLayer.contentTintColor = color + case .accent: + baseItemLayer.contentTintColor = self.presentationData.theme.list.itemAccentColor + case .primary: + baseItemLayer.contentTintColor = self.presentationData.theme.list.itemPrimaryTextColor + case .none: + break + } } if let sublayers = animationLayer.sublayers { @@ -988,6 +1006,7 @@ public final class EmojiStatusSelectionController: ViewController { strings: self.presentationData.strings, deviceMetrics: layout.deviceMetrics, emojiContent: emojiContent, + color: self.color, backgroundColor: listBackgroundColor, separatorColor: separatorColor, hideTopPanel: self.isReactionSearchActive, @@ -1279,6 +1298,8 @@ public final class EmojiStatusSelectionController: ViewController { case .statusSelection: let _ = (self.context.engine.accountData.setEmojiStatus(file: item?.itemFile, expirationDate: nil) |> deliverOnMainQueue).start() + case let .backgroundSelection(completion): + completion(item?.itemFile?.fileId.id) case let .quickReactionSelection(completion): if let item = item, let itemFile = item.itemFile { var selectedReaction: MessageReaction.Reaction? @@ -1364,6 +1385,7 @@ public final class EmojiStatusSelectionController: ViewController { public enum Mode { case statusSelection + case backgroundSelection(completion: (Int64?) -> Void) case quickReactionSelection(completion: () -> Void) } @@ -1371,6 +1393,7 @@ public final class EmojiStatusSelectionController: ViewController { private weak var sourceView: UIView? private let emojiContent: Signal private let currentSelection: Int64? + private let color: UIColor? private let mode: Mode private let destinationItemView: () -> UIView? @@ -1383,12 +1406,13 @@ public final class EmojiStatusSelectionController: ViewController { return true } - public init(context: AccountContext, mode: Mode, sourceView: UIView, emojiContent: Signal, currentSelection: Int64?, destinationItemView: @escaping () -> UIView?) { + public init(context: AccountContext, mode: Mode, sourceView: UIView, emojiContent: Signal, currentSelection: Int64?, color: UIColor? = nil, destinationItemView: @escaping () -> UIView?) { self.context = context self.mode = mode self.sourceView = sourceView self.emojiContent = emojiContent self.currentSelection = currentSelection + self.color = color self.destinationItemView = destinationItemView super.init(navigationBarPresentationData: nil) @@ -1418,7 +1442,7 @@ public final class EmojiStatusSelectionController: ViewController { } override public func loadDisplayNode() { - self.displayNode = Node(controller: self, context: self.context, sourceView: self.sourceView, emojiContent: self.emojiContent, currentSelection: self.currentSelection) + self.displayNode = Node(controller: self, context: self.context, sourceView: self.sourceView, emojiContent: self.emojiContent, currentSelection: self.currentSelection, color: self.color) super.displayNodeDidLoad() } diff --git a/submodules/TelegramUI/Components/ListActionItemComponent/BUILD b/submodules/TelegramUI/Components/ListActionItemComponent/BUILD new file mode 100644 index 0000000000..dae202b12d --- /dev/null +++ b/submodules/TelegramUI/Components/ListActionItemComponent/BUILD @@ -0,0 +1,21 @@ +load("@build_bazel_rules_swift//swift:swift.bzl", "swift_library") + +swift_library( + name = "ListActionItemComponent", + module_name = "ListActionItemComponent", + srcs = glob([ + "Sources/**/*.swift", + ]), + copts = [ + "-warnings-as-errors", + ], + deps = [ + "//submodules/Display", + "//submodules/ComponentFlow", + "//submodules/TelegramPresentationData", + "//submodules/TelegramUI/Components/ListSectionComponent", + ], + visibility = [ + "//visibility:public", + ], +) diff --git a/submodules/TelegramUI/Components/ListActionItemComponent/Sources/ListActionItemComponent.swift b/submodules/TelegramUI/Components/ListActionItemComponent/Sources/ListActionItemComponent.swift new file mode 100644 index 0000000000..fb5467ad0a --- /dev/null +++ b/submodules/TelegramUI/Components/ListActionItemComponent/Sources/ListActionItemComponent.swift @@ -0,0 +1,187 @@ +import Foundation +import UIKit +import Display +import ComponentFlow +import TelegramPresentationData +import ListSectionComponent + +public final class ListActionItemComponent: Component { + public let theme: PresentationTheme + public let title: AnyComponent + public let icon: AnyComponentWithIdentity? + public let hasArrow: Bool + public let action: ((UIView) -> Void)? + + public init( + theme: PresentationTheme, + title: AnyComponent, + icon: AnyComponentWithIdentity?, + hasArrow: Bool = true, + action: ((UIView) -> Void)? + ) { + self.theme = theme + self.title = title + self.icon = icon + self.hasArrow = hasArrow + self.action = action + } + + public static func ==(lhs: ListActionItemComponent, rhs: ListActionItemComponent) -> Bool { + if lhs.theme !== rhs.theme { + return false + } + if lhs.title != rhs.title { + return false + } + if lhs.icon != rhs.icon { + return false + } + if lhs.hasArrow != rhs.hasArrow { + return false + } + if (lhs.action == nil) != (rhs.action == nil) { + return false + } + return true + } + + public final class View: HighlightTrackingButton, ListSectionComponent.ChildView { + private let title = ComponentView() + private var icon: ComponentView? + + private let arrowView: UIImageView + + private var component: ListActionItemComponent? + + public var iconView: UIView? { + return self.icon?.view + } + + public var customUpdateIsHighlighted: ((Bool) -> Void)? + + public override init(frame: CGRect) { + self.arrowView = UIImageView() + + super.init(frame: CGRect()) + + self.addSubview(self.arrowView) + + self.addTarget(self, action: #selector(self.pressed), for: .touchUpInside) + self.internalHighligthedChanged = { [weak self] isHighlighted in + guard let self else { + return + } + if let customUpdateIsHighlighted = self.customUpdateIsHighlighted { + customUpdateIsHighlighted(isHighlighted) + } + } + } + + required public init?(coder: NSCoder) { + preconditionFailure() + } + + @objc private func pressed() { + self.component?.action?(self) + } + + func update(component: ListActionItemComponent, availableSize: CGSize, state: EmptyComponentState, environment: Environment, transition: Transition) -> CGSize { + let previousComponent = self.component + self.component = component + + self.isEnabled = component.action != nil + + let verticalInset: CGFloat = 11.0 + + let contentLeftInset: CGFloat = 16.0 + let contentRightInset: CGFloat = component.hasArrow ? 30.0 : 16.0 + + var contentHeight: CGFloat = 0.0 + contentHeight += verticalInset + + let titleSize = self.title.update( + transition: transition, + component: component.title, + environment: {}, + containerSize: CGSize(width: availableSize.width - contentLeftInset, height: availableSize.height) + ) + let titleFrame = CGRect(origin: CGPoint(x: contentLeftInset, y: verticalInset), size: titleSize) + if let titleView = self.title.view { + if titleView.superview == nil { + titleView.isUserInteractionEnabled = false + self.addSubview(titleView) + } + transition.setFrame(view: titleView, frame: titleFrame) + } + contentHeight += titleSize.height + + contentHeight += verticalInset + + if let iconValue = component.icon { + if previousComponent?.icon?.id != iconValue.id, let icon = self.icon { + self.icon = nil + if let iconView = icon.view { + transition.setAlpha(view: iconView, alpha: 0.0, completion: { [weak iconView] _ in + iconView?.removeFromSuperview() + }) + } + } + + var iconTransition = transition + let icon: ComponentView + if let current = self.icon { + icon = current + } else { + iconTransition = iconTransition.withAnimation(.none) + icon = ComponentView() + self.icon = icon + } + + let iconSize = icon.update( + transition: iconTransition, + component: iconValue.component, + environment: {}, + containerSize: CGSize(width: availableSize.width, height: availableSize.height) + ) + let iconFrame = CGRect(origin: CGPoint(x: availableSize.width - contentRightInset - iconSize.width, y: floor((contentHeight - iconSize.height) * 0.5)), size: iconSize) + if let iconView = icon.view { + if iconView.superview == nil { + iconView.isUserInteractionEnabled = false + self.addSubview(iconView) + transition.animateAlpha(view: iconView, from: 0.0, to: 1.0) + } + iconTransition.setFrame(view: iconView, frame: iconFrame) + } + } else { + if let icon = self.icon { + self.icon = nil + if let iconView = icon.view { + transition.setAlpha(view: iconView, alpha: 0.0, completion: { [weak iconView] _ in + iconView?.removeFromSuperview() + }) + } + } + } + + if self.arrowView.image == nil { + self.arrowView.image = PresentationResourcesItemList.disclosureArrowImage(component.theme)?.withRenderingMode(.alwaysTemplate) + } + self.arrowView.tintColor = component.theme.list.disclosureArrowColor + if let image = self.arrowView.image { + let arrowFrame = CGRect(origin: CGPoint(x: availableSize.width - 7.0 - image.size.width, y: floor((contentHeight - image.size.height) * 0.5)), size: image.size) + transition.setFrame(view: self.arrowView, frame: arrowFrame) + } + transition.setAlpha(view: self.arrowView, alpha: component.hasArrow ? 1.0 : 0.0) + + return CGSize(width: availableSize.width, height: contentHeight) + } + } + + public func makeView() -> View { + return View(frame: CGRect()) + } + + public func update(view: View, availableSize: CGSize, state: EmptyComponentState, environment: Environment, transition: Transition) -> CGSize { + return view.update(component: self, availableSize: availableSize, state: state, environment: environment, transition: transition) + } +} diff --git a/submodules/TelegramUI/Components/ListItemComponentAdaptor/BUILD b/submodules/TelegramUI/Components/ListItemComponentAdaptor/BUILD new file mode 100644 index 0000000000..1ebd9ea2ec --- /dev/null +++ b/submodules/TelegramUI/Components/ListItemComponentAdaptor/BUILD @@ -0,0 +1,21 @@ +load("@build_bazel_rules_swift//swift:swift.bzl", "swift_library") + +swift_library( + name = "ListItemComponentAdaptor", + module_name = "ListItemComponentAdaptor", + srcs = glob([ + "Sources/**/*.swift", + ]), + copts = [ + "-warnings-as-errors", + ], + deps = [ + "//submodules/AsyncDisplayKit", + "//submodules/Display", + "//submodules/ComponentFlow", + "//submodules/Components/ComponentDisplayAdapters", + ], + visibility = [ + "//visibility:public", + ], +) diff --git a/submodules/TelegramUI/Components/ListItemComponentAdaptor/Sources/ListItemComponentAdaptor.swift b/submodules/TelegramUI/Components/ListItemComponentAdaptor/Sources/ListItemComponentAdaptor.swift new file mode 100644 index 0000000000..ba4e12d70e --- /dev/null +++ b/submodules/TelegramUI/Components/ListItemComponentAdaptor/Sources/ListItemComponentAdaptor.swift @@ -0,0 +1,131 @@ +import Foundation +import UIKit +import AsyncDisplayKit +import Display +import ComponentFlow +import ComponentDisplayAdapters + +public protocol _ListItemComponentAdaptorItemGenerator: AnyObject, Equatable { + func item() -> ListViewItem +} + +public final class ListItemComponentAdaptor: Component { + public typealias ItemGenerator = _ListItemComponentAdaptorItemGenerator + + private let itemGenerator: AnyObject + private let isEqualImpl: (AnyObject) -> Bool + private let itemImpl: () -> ListViewItem + private let params: ListViewItemLayoutParams + + public init( + itemGenerator: ItemGeneratorType, + params: ListViewItemLayoutParams + ) { + self.itemGenerator = itemGenerator + self.isEqualImpl = { other in + if let other = other as? ItemGeneratorType, itemGenerator == other { + return true + } else { + return false + } + } + self.itemImpl = { + return itemGenerator.item() + } + self.params = params + } + + public static func ==(lhs: ListItemComponentAdaptor, rhs: ListItemComponentAdaptor) -> Bool { + if !lhs.isEqualImpl(rhs.itemGenerator) { + return false + } + if lhs.params != rhs.params { + return false + } + return true + } + + public final class View: UIView { + private var itemNode: ListViewItemNode? + + func update(component: ListItemComponentAdaptor, availableSize: CGSize, state: EmptyComponentState, environment: Environment, transition: Transition) -> CGSize { + let item = component.itemImpl() + + if let itemNode = self.itemNode { + let mappedAnimation: ListViewItemUpdateAnimation + switch transition.animation { + case .none: + mappedAnimation = .None + case let .curve(duration, curve): + mappedAnimation = .System(duration: duration, transition: ControlledTransition(duration: duration, curve: curve.containedViewLayoutTransitionCurve, interactive: false)) + } + + var resultSize: CGSize? + item.updateNode( + async: { f in f() }, + node: { return itemNode }, + params: component.params, + previousItem: nil, + nextItem: nil, + animation: mappedAnimation, + completion: { [weak itemNode] layout, apply in + resultSize = layout.size + + guard let itemNode else { + return + } + + let nodeFrame = CGRect(origin: CGPoint(), size: CGSize(width: layout.size.width, height: layout.size.height)) + + itemNode.contentSize = layout.contentSize + itemNode.insets = layout.insets + itemNode.frame = nodeFrame + + apply(ListViewItemApply(isOnScreen: true)) + } + ) + if let resultSize { + transition.setFrame(view: itemNode.view, frame: CGRect(origin: CGPoint(), size: resultSize)) + return resultSize + } else { + #if DEBUG + assertionFailure() + #endif + return self.bounds.size + } + } else { + var itemNode: ListViewItemNode? + item.nodeConfiguredForParams( + async: { f in f() }, + params: component.params, + synchronousLoads: true, + previousItem: nil, + nextItem: nil, + completion: { result, apply in + itemNode = result + apply().1(ListViewItemApply(isOnScreen: true)) + } + ) + if let itemNode { + self.itemNode = itemNode + self.addSubnode(itemNode) + + return itemNode.bounds.size + } else { + #if DEBUG + assertionFailure() + #endif + return self.bounds.size + } + } + } + } + + public func makeView() -> View { + return View(frame: CGRect()) + } + + public func update(view: View, availableSize: CGSize, state: EmptyComponentState, environment: Environment, transition: Transition) -> CGSize { + return view.update(component: self, availableSize: availableSize, state: state, environment: environment, transition: transition) + } +} diff --git a/submodules/TelegramUI/Components/ListSectionComponent/BUILD b/submodules/TelegramUI/Components/ListSectionComponent/BUILD new file mode 100644 index 0000000000..e216e0bb5d --- /dev/null +++ b/submodules/TelegramUI/Components/ListSectionComponent/BUILD @@ -0,0 +1,21 @@ +load("@build_bazel_rules_swift//swift:swift.bzl", "swift_library") + +swift_library( + name = "ListSectionComponent", + module_name = "ListSectionComponent", + srcs = glob([ + "Sources/**/*.swift", + ]), + copts = [ + "-warnings-as-errors", + ], + deps = [ + "//submodules/Display", + "//submodules/ComponentFlow", + "//submodules/TelegramPresentationData", + "//submodules/TelegramUI/Components/DynamicCornerRadiusView", + ], + visibility = [ + "//visibility:public", + ], +) diff --git a/submodules/TelegramUI/Components/ListSectionComponent/Sources/ListSectionComponent.swift b/submodules/TelegramUI/Components/ListSectionComponent/Sources/ListSectionComponent.swift new file mode 100644 index 0000000000..5612d0f692 --- /dev/null +++ b/submodules/TelegramUI/Components/ListSectionComponent/Sources/ListSectionComponent.swift @@ -0,0 +1,288 @@ +import Foundation +import UIKit +import Display +import ComponentFlow +import TelegramPresentationData +import DynamicCornerRadiusView + +public protocol ListSectionComponentChildView: AnyObject { + var customUpdateIsHighlighted: ((Bool) -> Void)? { get set } +} + +public final class ListSectionComponent: Component { + public typealias ChildView = ListSectionComponentChildView + + public enum Background: Equatable { + case none + case all + case range(from: AnyHashable, corners: DynamicCornerRadiusView.Corners) + } + + public let theme: PresentationTheme + public let background: Background + public let header: AnyComponent? + public let footer: AnyComponent? + public let items: [AnyComponentWithIdentity] + + public init( + theme: PresentationTheme, + background: Background = .all, + header: AnyComponent?, + footer: AnyComponent?, + items: [AnyComponentWithIdentity] + ) { + self.theme = theme + self.background = background + self.header = header + self.footer = footer + self.items = items + } + + public static func ==(lhs: ListSectionComponent, rhs: ListSectionComponent) -> Bool { + if lhs.theme !== rhs.theme { + return false + } + if lhs.background != rhs.background { + return false + } + if lhs.header != rhs.header { + return false + } + if lhs.footer != rhs.footer { + return false + } + if lhs.items != rhs.items { + return false + } + return true + } + + public final class View: UIView { + private let contentView: UIView + private let contentBackgroundView: DynamicCornerRadiusView + + private var header: ComponentView? + private var footer: ComponentView? + private var itemViews: [AnyHashable: ComponentView] = [:] + + private var isHighlighted: Bool = false + + private var component: ListSectionComponent? + + public override init(frame: CGRect) { + self.contentView = UIView() + self.contentView.layer.cornerRadius = 11.0 + self.contentView.clipsToBounds = true + + self.contentBackgroundView = DynamicCornerRadiusView() + + super.init(frame: CGRect()) + + self.addSubview(self.contentBackgroundView) + self.addSubview(self.contentView) + } + + required public init?(coder: NSCoder) { + preconditionFailure() + } + + private func updateIsHighlighted(isHighlighted: Bool) { + if self.isHighlighted == isHighlighted { + return + } + self.isHighlighted = isHighlighted + + guard let component = self.component else { + return + } + + let transition: Transition + let backgroundColor: UIColor + if isHighlighted { + transition = .immediate + backgroundColor = component.theme.list.itemHighlightedBackgroundColor + } else { + transition = .easeInOut(duration: 0.2) + backgroundColor = component.theme.list.itemBlocksBackgroundColor + } + self.contentBackgroundView.updateColor(color: backgroundColor, transition: transition) + } + + func update(component: ListSectionComponent, availableSize: CGSize, state: EmptyComponentState, environment: Environment, transition: Transition) -> CGSize { + self.component = component + + let backgroundColor: UIColor + if self.isHighlighted { + backgroundColor = component.theme.list.itemHighlightedBackgroundColor + } else { + backgroundColor = component.theme.list.itemBlocksBackgroundColor + } + self.contentBackgroundView.updateColor(color: backgroundColor, transition: transition) + + let headerSideInset: CGFloat = 16.0 + + var contentHeight: CGFloat = 0.0 + + if let headerValue = component.header { + let header: ComponentView + var headerTransition = transition + if let current = self.header { + header = current + } else { + headerTransition = headerTransition.withAnimation(.none) + header = ComponentView() + self.header = header + } + + let headerSize = header.update( + transition: headerTransition, + component: headerValue, + environment: {}, + containerSize: CGSize(width: availableSize.width - headerSideInset * 2.0, height: availableSize.height) + ) + if let headerView = header.view { + if headerView.superview == nil { + self.addSubview(headerView) + } + headerTransition.setFrame(view: headerView, frame: CGRect(origin: CGPoint(x: headerSideInset, y: contentHeight), size: headerSize)) + } + contentHeight += headerSize.height + } else { + if let header = self.header { + self.header = nil + header.view?.removeFromSuperview() + } + } + + var innerContentHeight: CGFloat = 0.0 + var validItemIds: [AnyHashable] = [] + for item in component.items { + validItemIds.append(item.id) + + let itemView: ComponentView + var itemTransition = transition + if let current = self.itemViews[item.id] { + itemView = current + } else { + itemTransition = itemTransition.withAnimation(.none) + itemView = ComponentView() + self.itemViews[item.id] = itemView + } + + let itemSize = itemView.update( + transition: itemTransition, + component: item.component, + environment: {}, + containerSize: CGSize(width: availableSize.width, height: availableSize.height) + ) + let itemFrame = CGRect(origin: CGPoint(x: 0.0, y: innerContentHeight), size: itemSize) + if let itemComponentView = itemView.view { + if itemComponentView.superview == nil { + self.contentView.addSubview(itemComponentView) + transition.animateAlpha(view: itemComponentView, from: 0.0, to: 1.0) + + if let itemComponentView = itemComponentView as? ChildView { + itemComponentView.customUpdateIsHighlighted = { [weak self] isHighlighted in + guard let self else { + return + } + self.updateIsHighlighted(isHighlighted: isHighlighted) + } + } + } + itemTransition.setFrame(view: itemComponentView, frame: itemFrame) + } + innerContentHeight += itemSize.height + } + var removedItemIds: [AnyHashable] = [] + for (id, itemView) in self.itemViews { + if !validItemIds.contains(id) { + removedItemIds.append(id) + + if let itemComponentView = itemView.view { + transition.setAlpha(view: itemComponentView, alpha: 0.0, completion: { [weak itemComponentView] _ in + itemComponentView?.removeFromSuperview() + }) + } + } + } + for id in removedItemIds { + self.itemViews.removeValue(forKey: id) + } + + if innerContentHeight != 0.0 && contentHeight != 0.0 { + contentHeight += 7.0 + } + + let contentFrame = CGRect(origin: CGPoint(x: 0.0, y: contentHeight), size: CGSize(width: availableSize.width, height: innerContentHeight)) + transition.setFrame(view: self.contentView, frame: contentFrame) + + let backgroundFrame: CGRect + var backgroundAlpha: CGFloat = 1.0 + switch component.background { + case .none: + backgroundFrame = contentFrame + backgroundAlpha = 0.0 + self.contentBackgroundView.update(size: backgroundFrame.size, corners: DynamicCornerRadiusView.Corners(minXMinY: 11.0, maxXMinY: 11.0, minXMaxY: 11.0, maxXMaxY: 11.0), transition: transition) + case .all: + backgroundFrame = contentFrame + self.contentBackgroundView.update(size: backgroundFrame.size, corners: DynamicCornerRadiusView.Corners(minXMinY: 11.0, maxXMinY: 11.0, minXMaxY: 11.0, maxXMaxY: 11.0), transition: transition) + case let .range(from, corners): + if let itemComponentView = self.itemViews[from]?.view, itemComponentView.frame.minY < contentFrame.height { + backgroundFrame = CGRect(origin: CGPoint(x: 0.0, y: contentFrame.minY + itemComponentView.frame.minY), size: CGSize(width: contentFrame.width, height: contentFrame.height - itemComponentView.frame.minY)) + } else { + backgroundFrame = CGRect(origin: CGPoint(x: contentFrame.minY, y: contentFrame.height), size: CGSize(width: contentFrame.width, height: 0.0)) + } + self.contentBackgroundView.update(size: backgroundFrame.size, corners: corners, transition: transition) + } + transition.setFrame(view: self.contentBackgroundView, frame: backgroundFrame) + transition.setAlpha(view: self.contentBackgroundView, alpha: backgroundAlpha) + + contentHeight += innerContentHeight + + if let footerValue = component.footer { + let footer: ComponentView + var footerTransition = transition + if let current = self.footer { + footer = current + } else { + footerTransition = footerTransition.withAnimation(.none) + footer = ComponentView() + self.footer = footer + } + + let footerSize = footer.update( + transition: footerTransition, + component: footerValue, + environment: {}, + containerSize: CGSize(width: availableSize.width - headerSideInset * 2.0, height: availableSize.height) + ) + if contentHeight != 0.0 { + contentHeight += 7.0 + } + if let footerView = footer.view { + if footerView.superview == nil { + self.addSubview(footerView) + } + footerTransition.setFrame(view: footerView, frame: CGRect(origin: CGPoint(x: headerSideInset, y: contentHeight), size: footerSize)) + } + contentHeight += footerSize.height + } else { + if let footer = self.footer { + self.footer = nil + footer.view?.removeFromSuperview() + } + } + + return CGSize(width: availableSize.width, height: contentHeight) + } + } + + public func makeView() -> View { + return View(frame: CGRect()) + } + + public func update(view: View, availableSize: CGSize, state: EmptyComponentState, environment: Environment, transition: Transition) -> CGSize { + return view.update(component: self, availableSize: availableSize, state: state, environment: environment, transition: transition) + } +} diff --git a/submodules/TelegramUI/Components/NotificationExceptionsScreen/Sources/NotificationExceptionsScreen.swift b/submodules/TelegramUI/Components/NotificationExceptionsScreen/Sources/NotificationExceptionsScreen.swift index 3f7c37cfd7..258049808f 100644 --- a/submodules/TelegramUI/Components/NotificationExceptionsScreen/Sources/NotificationExceptionsScreen.swift +++ b/submodules/TelegramUI/Components/NotificationExceptionsScreen/Sources/NotificationExceptionsScreen.swift @@ -332,7 +332,7 @@ private func notificationsPeerCategoryEntries(peerId: EnginePeer.Id, notificatio } } existingThreadIds.insert(value.threadId) - entries.append(.exception(Int32(index), presentationData.dateTimeFormat, presentationData.nameDisplayOrder, .channel(TelegramChannel(id: peerId, accessHash: nil, title: "", username: nil, photo: [], creationDate: 0, version: 0, participationStatus: .member, info: .group(TelegramChannelGroupInfo(flags: [])), flags: [.isForum], restrictionInfo: nil, adminRights: nil, bannedRights: nil, defaultBannedRights: nil, usernames: [], storiesHidden: nil, nameColor: nil, backgroundEmojiId: nil, profileColor: nil, profileBackgroundEmojiId: nil)), value.threadId, value.info, title, value.notificationSettings._asNotificationSettings(), state.editing, state.revealedThreadId == value.threadId)) + entries.append(.exception(Int32(index), presentationData.dateTimeFormat, presentationData.nameDisplayOrder, .channel(TelegramChannel(id: peerId, accessHash: nil, title: "", username: nil, photo: [], creationDate: 0, version: 0, participationStatus: .member, info: .group(TelegramChannelGroupInfo(flags: [])), flags: [.isForum], restrictionInfo: nil, adminRights: nil, bannedRights: nil, defaultBannedRights: nil, usernames: [], storiesHidden: nil, nameColor: nil, backgroundEmojiId: nil, profileColor: nil, profileBackgroundEmojiId: nil, emojiStatus: nil)), value.threadId, value.info, title, value.notificationSettings._asNotificationSettings(), state.editing, state.revealedThreadId == value.threadId)) index += 1 } diff --git a/submodules/TelegramUI/Components/PeerAllowedReactionsScreen/BUILD b/submodules/TelegramUI/Components/PeerAllowedReactionsScreen/BUILD index 631aee7b7d..da94a4fdb1 100644 --- a/submodules/TelegramUI/Components/PeerAllowedReactionsScreen/BUILD +++ b/submodules/TelegramUI/Components/PeerAllowedReactionsScreen/BUILD @@ -26,6 +26,7 @@ swift_library( "//submodules/Markdown", "//submodules/TelegramUI/Components/ButtonComponent", "//submodules/TelegramUI/Components/AnimatedTextComponent", + "//submodules/TelegramUI/Components/PremiumLockButtonSubtitleComponent", "//submodules/Components/BundleIconComponent", "//submodules/Components/PagerComponent", "//submodules/PremiumUI", diff --git a/submodules/TelegramUI/Components/PeerAllowedReactionsScreen/Sources/PeerAllowedReactionsScreen.swift b/submodules/TelegramUI/Components/PeerAllowedReactionsScreen/Sources/PeerAllowedReactionsScreen.swift index a3c415cb82..031539d733 100644 --- a/submodules/TelegramUI/Components/PeerAllowedReactionsScreen/Sources/PeerAllowedReactionsScreen.swift +++ b/submodules/TelegramUI/Components/PeerAllowedReactionsScreen/Sources/PeerAllowedReactionsScreen.swift @@ -20,82 +20,7 @@ import BundleIconComponent import AnimatedTextComponent import TextFormat import AudioToolbox - -private final class ButtonSubtitleComponent: CombinedComponent { - let count: Int - let theme: PresentationTheme - let strings: PresentationStrings - - init(count: Int, theme: PresentationTheme, strings: PresentationStrings) { - self.count = count - self.theme = theme - self.strings = strings - } - - static func ==(lhs: ButtonSubtitleComponent, rhs: ButtonSubtitleComponent) -> Bool { - if lhs.count != rhs.count { - return false - } - if lhs.theme !== rhs.theme { - return false - } - if lhs.strings !== rhs.strings { - return false - } - return true - } - - static var body: Body { - let icon = Child(BundleIconComponent.self) - let text = Child(AnimatedTextComponent.self) - - return { context in - let icon = icon.update( - component: BundleIconComponent( - name: "Chat/Input/Accessory Panels/TextLockIcon", - tintColor: context.component.theme.list.itemCheckColors.foregroundColor.withMultipliedAlpha(0.7), - maxSize: CGSize(width: 10.0, height: 10.0) - ), - availableSize: CGSize(width: 100.0, height: 100.0), - transition: context.transition - ) - var textItems: [AnimatedTextComponent.Item] = [] - - let levelString = context.component.strings.ChannelReactions_LevelRequiredLabel("") - var previousIndex = 0 - let nsLevelString = levelString.string as NSString - for range in levelString.ranges.sorted(by: { $0.range.lowerBound < $1.range.lowerBound }) { - if range.range.lowerBound > previousIndex { - textItems.append(AnimatedTextComponent.Item(id: AnyHashable(range.index), content: .text(nsLevelString.substring(with: NSRange(location: previousIndex, length: range.range.lowerBound - previousIndex))))) - } - if range.index == 0 { - textItems.append(AnimatedTextComponent.Item(id: AnyHashable(range.index), content: .number(context.component.count, minDigits: 1))) - } - previousIndex = range.range.upperBound - } - if nsLevelString.length > previousIndex { - textItems.append(AnimatedTextComponent.Item(id: AnyHashable(100), content: .text(nsLevelString.substring(with: NSRange(location: previousIndex, length: nsLevelString.length - previousIndex))))) - } - - let text = text.update( - component: AnimatedTextComponent(font: Font.medium(11.0), color: context.component.theme.list.itemCheckColors.foregroundColor.withMultipliedAlpha(0.7), items: textItems), - availableSize: CGSize(width: context.availableSize.width - 20.0, height: 100.0), - transition: context.transition - ) - - let spacing: CGFloat = 3.0 - let size = CGSize(width: icon.size.width + spacing + text.size.width, height: text.size.height) - context.add(icon - .position(icon.size.centered(in: CGRect(origin: CGPoint(x: 0.0, y: -UIScreenPixel), size: CGSize(width: icon.size.width, height: size.height))).center) - ) - context.add(text - .position(text.size.centered(in: CGRect(origin: CGPoint(x: icon.size.width + spacing, y: 0.0), size: text.size)).center) - ) - - return size - } - } -} +import PremiumLockButtonSubtitleComponent final class PeerAllowedReactionsScreenComponent: Component { typealias EnvironmentType = ViewControllerComponentContainer.Environment @@ -904,7 +829,7 @@ final class PeerAllowedReactionsScreenComponent: Component { }).count : 0 if let boostStatus = self.boostStatus, customReactionCount > boostStatus.level { - buttonContents.append(AnyComponentWithIdentity(id: AnyHashable(1 as Int), component: AnyComponent(ButtonSubtitleComponent( + buttonContents.append(AnyComponentWithIdentity(id: AnyHashable(1 as Int), component: AnyComponent(PremiumLockButtonSubtitleComponent( count: customReactionCount, theme: environment.theme, strings: environment.strings diff --git a/submodules/TelegramUI/Components/PeerInfo/PeerInfoScreen/Sources/ListItems/PeerInfoScreenDisclosureItem.swift b/submodules/TelegramUI/Components/PeerInfo/PeerInfoScreen/Sources/ListItems/PeerInfoScreenDisclosureItem.swift index dcf1946b95..369f19bb3e 100644 --- a/submodules/TelegramUI/Components/PeerInfo/PeerInfoScreen/Sources/ListItems/PeerInfoScreenDisclosureItem.swift +++ b/submodules/TelegramUI/Components/PeerInfo/PeerInfoScreen/Sources/ListItems/PeerInfoScreenDisclosureItem.swift @@ -34,15 +34,17 @@ final class PeerInfoScreenDisclosureItem: PeerInfoScreenItem { let id: AnyHashable let label: Label let additionalBadgeLabel: String? + let additionalBadgeIcon: UIImage? let text: String let icon: UIImage? let iconSignal: Signal? let action: (() -> Void)? - init(id: AnyHashable, label: Label = .none, additionalBadgeLabel: String? = nil, text: String, icon: UIImage? = nil, iconSignal: Signal? = nil, action: (() -> Void)?) { + init(id: AnyHashable, label: Label = .none, additionalBadgeLabel: String? = nil, additionalBadgeIcon: UIImage? = nil, text: String, icon: UIImage? = nil, iconSignal: Signal? = nil, action: (() -> Void)?) { self.id = id self.label = label self.additionalBadgeLabel = additionalBadgeLabel + self.additionalBadgeIcon = additionalBadgeIcon self.text = text self.icon = icon self.iconSignal = iconSignal @@ -61,6 +63,7 @@ private final class PeerInfoScreenDisclosureItemNode: PeerInfoScreenItemNode { private let labelBadgeNode: ASImageNode private let labelNode: ImmediateTextNode private var additionalLabelNode: ImmediateTextNode? + private var additionalLabelBadgeNode: ASImageNode? private let textNode: ImmediateTextNode private let arrowNode: ASImageNode private let bottomSeparatorNode: ASDisplayNode @@ -249,6 +252,24 @@ private final class PeerInfoScreenDisclosureItemNode: PeerInfoScreenItemNode { self.labelBadgeNode.removeFromSupernode() } + if let additionalBadgeIcon = item.additionalBadgeIcon { + let additionalLabelBadgeNode: ASImageNode + if let current = self.additionalLabelBadgeNode { + additionalLabelBadgeNode = current + } else { + additionalLabelBadgeNode = ASImageNode() + additionalLabelBadgeNode.isUserInteractionEnabled = false + self.additionalLabelBadgeNode = additionalLabelBadgeNode + self.insertSubnode(additionalLabelBadgeNode, belowSubnode: self.labelNode) + } + additionalLabelBadgeNode.image = additionalBadgeIcon + } else { + if let additionalLabelBadgeNode = self.additionalLabelBadgeNode { + self.additionalLabelBadgeNode = nil + additionalLabelBadgeNode.removeFromSupernode() + } + } + var badgeWidth = max(badgeDiameter, labelSize.width + 10.0) if case .semitransparentBadge = item.label { badgeWidth += 2.0 @@ -283,6 +304,11 @@ private final class PeerInfoScreenDisclosureItemNode: PeerInfoScreenItemNode { additionalLabelNode.removeFromSupernode() } + if let additionalLabelBadgeNode = self.additionalLabelBadgeNode, let image = additionalLabelBadgeNode.image { + let additionalLabelSize = image.size + additionalLabelBadgeNode.frame = CGRect(origin: CGPoint(x: textFrame.maxX + 6.0, y: floor((height - additionalLabelSize.height) / 2.0) + 1.0), size: additionalLabelSize) + } + let labelBadgeNodeFrame: CGRect if case let .image(_, imageSize) = item.label { labelBadgeNodeFrame = CGRect(origin: CGPoint(x: width - rightInset - imageSize.width, y: floorToScreenPixels(textFrame.midY - imageSize.height / 2.0)), size:imageSize) diff --git a/submodules/TelegramUI/Components/PeerInfo/PeerInfoScreen/Sources/PeerInfoScreen.swift b/submodules/TelegramUI/Components/PeerInfo/PeerInfoScreen/Sources/PeerInfoScreen.swift index 8161639e43..7e301f2207 100644 --- a/submodules/TelegramUI/Components/PeerInfo/PeerInfoScreen/Sources/PeerInfoScreen.swift +++ b/submodules/TelegramUI/Components/PeerInfo/PeerInfoScreen/Sources/PeerInfoScreen.swift @@ -1675,7 +1675,8 @@ private func editingItems(data: PeerInfoScreenData?, state: PeerInfoState, chatL } let colorImage = generateSettingsMenuPeerColorsLabelIcon(colors: colors) - items[.peerSettings]!.append(PeerInfoScreenDisclosureItem(id: ItemPeerColor, label: .image(colorImage, colorImage.size), text: presentationData.strings.Channel_ChannelColor, icon: UIImage(bundleImageName: "Chat/Info/NameColorIcon"), action: { + //TODO:localize + items[.peerSettings]!.append(PeerInfoScreenDisclosureItem(id: ItemPeerColor, label: .image(colorImage, colorImage.size), additionalBadgeIcon: generateDisclosureActionBoostLevelBadgeImage(text: "Level 1+"), text: "Appearance", icon: UIImage(bundleImageName: "Chat/Info/NameColorIcon"), action: { interaction.editingOpenNameColorSetup() })) @@ -7200,9 +7201,8 @@ final class PeerInfoScreenNode: ViewControllerTracingNode, PeerInfoScreenNodePro if self.peerId == self.context.account.peerId { let controller = PeerNameColorScreen(context: self.context, updatedPresentationData: self.controller?.updatedPresentationData, subject: .account) self.controller?.push(controller) - } else { - let controller = PeerNameColorScreen(context: self.context, updatedPresentationData: self.controller?.updatedPresentationData, subject: .channel(self.peerId)) - self.controller?.push(controller) + } else if let peer = self.data?.peer, peer is TelegramChannel { + self.controller?.push(ChannelAppearanceScreen(context: self.context, updatedPresentationData: self.controller?.updatedPresentationData, peerId: self.peerId)) } } diff --git a/submodules/TelegramUI/Components/PremiumLockButtonSubtitleComponent/BUILD b/submodules/TelegramUI/Components/PremiumLockButtonSubtitleComponent/BUILD new file mode 100644 index 0000000000..b98e6369ae --- /dev/null +++ b/submodules/TelegramUI/Components/PremiumLockButtonSubtitleComponent/BUILD @@ -0,0 +1,22 @@ +load("@build_bazel_rules_swift//swift:swift.bzl", "swift_library") + +swift_library( + name = "PremiumLockButtonSubtitleComponent", + module_name = "PremiumLockButtonSubtitleComponent", + srcs = glob([ + "Sources/**/*.swift", + ]), + copts = [ + "-warnings-as-errors", + ], + deps = [ + "//submodules/Display", + "//submodules/ComponentFlow", + "//submodules/TelegramPresentationData", + "//submodules/Components/BundleIconComponent", + "//submodules/TelegramUI/Components/AnimatedTextComponent", + ], + visibility = [ + "//visibility:public", + ], +) diff --git a/submodules/TelegramUI/Components/PremiumLockButtonSubtitleComponent/Sources/PremiumLockButtonSubtitleComponent.swift b/submodules/TelegramUI/Components/PremiumLockButtonSubtitleComponent/Sources/PremiumLockButtonSubtitleComponent.swift new file mode 100644 index 0000000000..8e868f52f7 --- /dev/null +++ b/submodules/TelegramUI/Components/PremiumLockButtonSubtitleComponent/Sources/PremiumLockButtonSubtitleComponent.swift @@ -0,0 +1,83 @@ +import Foundation +import UIKit +import Display +import ComponentFlow +import TelegramPresentationData +import BundleIconComponent +import AnimatedTextComponent + +public final class PremiumLockButtonSubtitleComponent: CombinedComponent { + public let count: Int + public let theme: PresentationTheme + public let strings: PresentationStrings + + public init(count: Int, theme: PresentationTheme, strings: PresentationStrings) { + self.count = count + self.theme = theme + self.strings = strings + } + + public static func ==(lhs: PremiumLockButtonSubtitleComponent, rhs: PremiumLockButtonSubtitleComponent) -> Bool { + if lhs.count != rhs.count { + return false + } + if lhs.theme !== rhs.theme { + return false + } + if lhs.strings !== rhs.strings { + return false + } + return true + } + + public static var body: Body { + let icon = Child(BundleIconComponent.self) + let text = Child(AnimatedTextComponent.self) + + return { context in + let icon = icon.update( + component: BundleIconComponent( + name: "Chat/Input/Accessory Panels/TextLockIcon", + tintColor: context.component.theme.list.itemCheckColors.foregroundColor.withMultipliedAlpha(0.7), + maxSize: CGSize(width: 10.0, height: 10.0) + ), + availableSize: CGSize(width: 100.0, height: 100.0), + transition: context.transition + ) + var textItems: [AnimatedTextComponent.Item] = [] + + let levelString = context.component.strings.ChannelReactions_LevelRequiredLabel("") + var previousIndex = 0 + let nsLevelString = levelString.string as NSString + for range in levelString.ranges.sorted(by: { $0.range.lowerBound < $1.range.lowerBound }) { + if range.range.lowerBound > previousIndex { + textItems.append(AnimatedTextComponent.Item(id: AnyHashable(range.index), content: .text(nsLevelString.substring(with: NSRange(location: previousIndex, length: range.range.lowerBound - previousIndex))))) + } + if range.index == 0 { + textItems.append(AnimatedTextComponent.Item(id: AnyHashable(range.index), content: .number(context.component.count, minDigits: 1))) + } + previousIndex = range.range.upperBound + } + if nsLevelString.length > previousIndex { + textItems.append(AnimatedTextComponent.Item(id: AnyHashable(100), content: .text(nsLevelString.substring(with: NSRange(location: previousIndex, length: nsLevelString.length - previousIndex))))) + } + + let text = text.update( + component: AnimatedTextComponent(font: Font.medium(11.0), color: context.component.theme.list.itemCheckColors.foregroundColor.withMultipliedAlpha(0.7), items: textItems), + availableSize: CGSize(width: context.availableSize.width - 20.0, height: 100.0), + transition: context.transition + ) + + let spacing: CGFloat = 3.0 + let size = CGSize(width: icon.size.width + spacing + text.size.width, height: text.size.height) + context.add(icon + .position(icon.size.centered(in: CGRect(origin: CGPoint(x: 0.0, y: -UIScreenPixel), size: CGSize(width: icon.size.width, height: size.height))).center) + ) + context.add(text + .position(text.size.centered(in: CGRect(origin: CGPoint(x: icon.size.width + spacing, y: 0.0), size: text.size)).center) + ) + + return size + } + } +} diff --git a/submodules/TelegramUI/Components/Settings/PeerNameColorScreen/BUILD b/submodules/TelegramUI/Components/Settings/PeerNameColorScreen/BUILD index dd8d1627db..4af595503c 100644 --- a/submodules/TelegramUI/Components/Settings/PeerNameColorScreen/BUILD +++ b/submodules/TelegramUI/Components/Settings/PeerNameColorScreen/BUILD @@ -21,6 +21,7 @@ swift_library( "//submodules/PresentationDataUtils", "//submodules/UndoUI", "//submodules/WallpaperBackgroundNode", + "//submodules/ComponentFlow", "//submodules/TelegramUI/Components/EmojiStatusComponent", "//submodules/TelegramUI/Components/EntityKeyboard", "//submodules/SolidRoundedButtonNode", @@ -28,6 +29,19 @@ swift_library( "//submodules/PremiumUI", "//submodules/TelegramUI/Components/PeerInfo/PeerInfoCoverComponent", "//submodules/AvatarNode", + "//submodules/Components/ViewControllerComponent", + "//submodules/Components/BundleIconComponent", + "//submodules/TelegramUI/Components/AnimatedTextComponent", + "//submodules/TelegramUI/Components/ButtonComponent", + "//submodules/TelegramUI/Components/PremiumLockButtonSubtitleComponent", + "//submodules/TelegramUI/Components/ListItemComponentAdaptor", + "//submodules/TelegramUI/Components/ListSectionComponent", + "//submodules/TelegramUI/Components/ListActionItemComponent", + "//submodules/TelegramUI/Components/Settings/ThemeCarouselItem", + "//submodules/Components/MultilineTextComponent", + "//submodules/TelegramUI/Components/EmojiStatusSelectionComponent", + "//submodules/TelegramUI/Components/DynamicCornerRadiusView", + "//submodules/Components/ComponentDisplayAdapters", ], visibility = [ "//visibility:public", diff --git a/submodules/TelegramUI/Components/Settings/PeerNameColorScreen/Sources/ChannelAppearanceScreen.swift b/submodules/TelegramUI/Components/Settings/PeerNameColorScreen/Sources/ChannelAppearanceScreen.swift new file mode 100644 index 0000000000..1637c96238 --- /dev/null +++ b/submodules/TelegramUI/Components/Settings/PeerNameColorScreen/Sources/ChannelAppearanceScreen.swift @@ -0,0 +1,1355 @@ +import Foundation +import UIKit +import Display +import AsyncDisplayKit +import SwiftSignalKit +import Postbox +import TelegramCore +import TelegramPresentationData +import TelegramUIPreferences +import ItemListUI +import PresentationDataUtils +import AccountContext +import UndoUI +import EntityKeyboard +import PremiumUI +import ComponentFlow +import BundleIconComponent +import AnimatedTextComponent +import ViewControllerComponent +import ButtonComponent +import PremiumLockButtonSubtitleComponent +import ListItemComponentAdaptor +import ListSectionComponent +import MultilineTextComponent +import ThemeCarouselItem +import ListActionItemComponent +import EmojiStatusSelectionComponent +import EmojiStatusComponent +import DynamicCornerRadiusView +import ComponentDisplayAdapters + +private final class EmojiActionIconComponent: Component { + let context: AccountContext + let color: UIColor + let fileId: Int64? + let file: TelegramMediaFile? + + init( + context: AccountContext, + color: UIColor, + fileId: Int64?, + file: TelegramMediaFile? + ) { + self.context = context + self.color = color + self.fileId = fileId + self.file = file + } + + static func ==(lhs: EmojiActionIconComponent, rhs: EmojiActionIconComponent) -> Bool { + if lhs.context !== rhs.context { + return false + } + if lhs.color != rhs.color { + return false + } + if lhs.fileId != rhs.fileId { + return false + } + if lhs.file != rhs.file { + return false + } + return true + } + + final class View: UIView { + private var icon: ComponentView? + + func update(component: EmojiActionIconComponent, availableSize: CGSize, state: EmptyComponentState, environment: Environment, transition: Transition) -> CGSize { + let size = CGSize(width: 24.0, height: 24.0) + + if let fileId = component.fileId { + let icon: ComponentView + if let current = self.icon { + icon = current + } else { + icon = ComponentView() + self.icon = icon + } + let _ = icon.update( + transition: .immediate, + component: AnyComponent(EmojiStatusComponent( + context: component.context, + animationCache: component.context.animationCache, + animationRenderer: component.context.animationRenderer, + content: .animation( + content: .customEmoji(fileId: fileId), + size: size, + placeholderColor: .lightGray, + themeColor: component.color, + loopMode: .forever + ), + isVisibleForAnimations: false, + action: nil + )), + environment: {}, + containerSize: size + ) + let iconFrame = CGRect(origin: CGPoint(), size: size) + if let iconView = icon.view { + if iconView.superview == nil { + self.addSubview(iconView) + } + iconView.frame = iconFrame + } + } else { + if let icon = self.icon { + self.icon = nil + icon.view?.removeFromSuperview() + } + } + + return size + } + } + + func makeView() -> View { + return View(frame: CGRect()) + } + + func update(view: View, availableSize: CGSize, state: EmptyComponentState, environment: Environment, transition: Transition) -> CGSize { + return view.update(component: self, availableSize: availableSize, state: state, environment: environment, transition: transition) + } +} + +public func generateDisclosureActionBoostLevelBadgeImage(text: String) -> UIImage { + let attributedText = NSAttributedString(string: text, attributes: [ + .font: Font.medium(12.0), + .foregroundColor: UIColor.white + ]) + let bounds = attributedText.boundingRect(with: CGSize(width: 100.0, height: 100.0), options: .usesLineFragmentOrigin, context: nil) + let leftInset: CGFloat = 16.0 + let rightInset: CGFloat = 4.0 + let size = CGSize(width: leftInset + rightInset + ceil(bounds.width), height: 20.0) + return generateImage(size, rotatedContext: { size, context in + context.clear(CGRect(origin: CGPoint(), size: size)) + context.addPath(UIBezierPath(roundedRect: CGRect(origin: CGPoint(), size: size), cornerRadius: 6.0).cgPath) + context.clip() + + var locations: [CGFloat] = [0.0, 1.0] + let colors: [CGColor] = [UIColor(rgb: 0x9076FF).cgColor, UIColor(rgb: 0xB86DEA).cgColor] + let colorSpace = CGColorSpaceCreateDeviceRGB() + let gradient = CGGradient(colorsSpace: colorSpace, colors: colors as CFArray, locations: &locations)! + context.drawLinearGradient(gradient, start: CGPoint(x: 0.0, y: 0.0), end: CGPoint(x: size.width, y: 0.0), options: CGGradientDrawingOptions()) + + context.resetClip() + + UIGraphicsPushContext(context) + + if let image = generateTintedImage(image: UIImage(bundleImageName: "Chat/Input/Media/PanelBadgeLock"), color: .white) { + let imageFit: CGFloat = 14.0 + let imageSize = image.size.aspectFitted(CGSize(width: imageFit, height: imageFit)) + let imageRect = CGRect(origin: CGPoint(x: 2.0, y: UIScreenPixel + floorToScreenPixels((size.height - imageSize.height) * 0.5)), size: imageSize) + image.draw(in: imageRect) + } + + attributedText.draw(at: CGPoint(x: leftInset, y: floorToScreenPixels((size.height - bounds.height) * 0.5))) + + UIGraphicsPopContext() + })! +} + +private final class BoostLevelIconComponent: Component { + let strings: PresentationStrings + let level: Int + + init( + strings: PresentationStrings, + level: Int + ) { + self.strings = strings + self.level = level + } + + static func ==(lhs: BoostLevelIconComponent, rhs: BoostLevelIconComponent) -> Bool { + if lhs.strings !== rhs.strings { + return false + } + if lhs.level != rhs.level { + return false + } + return true + } + + final class View: UIView { + private let imageView: UIImageView + + private var component: BoostLevelIconComponent? + + override init(frame: CGRect) { + self.imageView = UIImageView() + + super.init(frame: frame) + + self.addSubview(self.imageView) + } + + required init?(coder: NSCoder) { + fatalError("init(coder:) has not been implemented") + } + + func update(component: BoostLevelIconComponent, availableSize: CGSize, state: EmptyComponentState, environment: Environment, transition: Transition) -> CGSize { + if self.component != component { + //TODO:localize + self.imageView.image = generateDisclosureActionBoostLevelBadgeImage(text: "Level \(component.level)") + } + self.component = component + + if let image = self.imageView.image { + self.imageView.frame = CGRect(origin: CGPoint(), size: image.size) + return image.size + } else { + return CGSize(width: 1.0, height: 20.0) + } + } + } + + func makeView() -> View { + return View(frame: CGRect()) + } + + func update(view: View, availableSize: CGSize, state: EmptyComponentState, environment: Environment, transition: Transition) -> CGSize { + return view.update(component: self, availableSize: availableSize, state: state, environment: environment, transition: transition) + } +} + +final class ChannelAppearanceScreenComponent: Component { + typealias EnvironmentType = ViewControllerComponentContainer.Environment + + let context: AccountContext + let peerId: EnginePeer.Id + + init( + context: AccountContext, + peerId: EnginePeer.Id + ) { + self.context = context + self.peerId = peerId + } + + static func ==(lhs: ChannelAppearanceScreenComponent, rhs: ChannelAppearanceScreenComponent) -> Bool { + if lhs.context !== rhs.context { + return false + } + if lhs.peerId != rhs.peerId { + return false + } + + return true + } + + private final class ContentsData { + let peer: EnginePeer? + let subscriberCount: Int? + let availableThemes: [TelegramTheme] + + init(peer: EnginePeer?, subscriberCount: Int?, availableThemes: [TelegramTheme]) { + self.peer = peer + self.subscriberCount = subscriberCount + self.availableThemes = availableThemes + } + + static func get(context: AccountContext, peerId: EnginePeer.Id) -> Signal { + return combineLatest( + context.engine.data.subscribe( + TelegramEngine.EngineData.Item.Peer.Peer(id: peerId), + TelegramEngine.EngineData.Item.Peer.ParticipantCount(id: peerId) + ), + telegramThemes(postbox: context.account.postbox, network: context.account.network, accountManager: context.sharedContext.accountManager) + ) + |> map { peerData, cloudThemes -> ContentsData in + let (peer, subscriberCount) = peerData + return ContentsData( + peer: peer, + subscriberCount: subscriberCount, + availableThemes: cloudThemes + ) + } + } + } + + private final class ScrollView: UIScrollView { + override func touchesShouldCancel(in view: UIView) -> Bool { + return true + } + } + + final class View: UIView, UIScrollViewDelegate { + private let scrollView: ScrollView + private let actionButton = ComponentView() + private let bottomPanelBackgroundView: BlurredBackgroundView + private let bottomPanelSeparator: SimpleLayer + + private let replySection = ComponentView() + private let wallpaperSection = ComponentView() + private let bannerSection = ComponentView() + private let resetColorSection = ComponentView() + private let emojiStatusSection = ComponentView() + + private var chatPreviewItemNode: PeerNameColorChatPreviewItemNode? + + private var isUpdating: Bool = false + + private var component: ChannelAppearanceScreenComponent? + private(set) weak var state: EmptyComponentState? + private var environment: EnvironmentType? + + let isReady = ValuePromise(false, ignoreRepeated: true) + private var contentsData: ContentsData? + private var contentsDataDisposable: Disposable? + + private var cachedIconFiles: [Int64: TelegramMediaFile] = [:] + + private var updatedPeerNameColor: PeerNameColor? + private var updatedPeerNameEmoji: Int64?? + private var updatedPeerProfileColor: PeerNameColor?? + private var updatedPeerProfileEmoji: Int64?? + private var updatedPeerStatusEmoji: Int64?? + + private var requiredLevel: Int? + + private var currentTheme: PresentationThemeReference? + + private var boostStatus: ChannelBoostStatus? + private var boostStatusDisposable: Disposable? + + private var isApplyingSettings: Bool = false + private var applyDisposable: Disposable? + + private weak var emojiStatusSelectionController: ViewController? + private weak var currentUndoController: UndoOverlayController? + + override init(frame: CGRect) { + self.scrollView = ScrollView() + self.scrollView.showsVerticalScrollIndicator = true + self.scrollView.showsHorizontalScrollIndicator = false + self.scrollView.scrollsToTop = false + self.scrollView.delaysContentTouches = false + self.scrollView.canCancelContentTouches = true + self.scrollView.contentInsetAdjustmentBehavior = .never + if #available(iOS 13.0, *) { + self.scrollView.automaticallyAdjustsScrollIndicatorInsets = false + } + self.scrollView.alwaysBounceVertical = true + + self.bottomPanelBackgroundView = BlurredBackgroundView(color: .clear, enableBlur: true) + self.bottomPanelSeparator = SimpleLayer() + + super.init(frame: frame) + + self.scrollView.delegate = self + self.addSubview(self.scrollView) + + self.addSubview(self.bottomPanelBackgroundView) + self.layer.addSublayer(self.bottomPanelSeparator) + } + + required init?(coder: NSCoder) { + fatalError("init(coder:) has not been implemented") + } + + deinit { + self.contentsDataDisposable?.dispose() + self.applyDisposable?.dispose() + self.boostStatusDisposable?.dispose() + } + + func scrollToTop() { + self.scrollView.setContentOffset(CGPoint(), animated: true) + } + + func attemptNavigation(complete: @escaping () -> Void) -> Bool { + return true + } + + func scrollViewDidScroll(_ scrollView: UIScrollView) { + self.updateScrolling(transition: .immediate) + } + + private func updateScrolling(transition: Transition) { + let navigationAlphaDistance: CGFloat = 16.0 + let navigationAlpha: CGFloat = max(0.0, min(1.0, self.scrollView.contentOffset.y / navigationAlphaDistance)) + if let controller = self.environment?.controller(), let navigationBar = controller.navigationBar { + transition.setAlpha(layer: navigationBar.backgroundNode.layer, alpha: navigationAlpha) + transition.setAlpha(layer: navigationBar.stripeNode.layer, alpha: navigationAlpha) + } + + let bottomNavigationAlphaDistance: CGFloat = 16.0 + let bottomNavigationAlpha: CGFloat = max(0.0, min(1.0, (self.scrollView.contentSize.height - self.scrollView.bounds.maxY) / bottomNavigationAlphaDistance)) + + transition.setAlpha(view: self.bottomPanelBackgroundView, alpha: bottomNavigationAlpha) + transition.setAlpha(layer: self.bottomPanelSeparator, alpha: bottomNavigationAlpha) + } + + private func applySettings() { + guard let component = self.component, let contentsData = self.contentsData, let peer = contentsData.peer, let requiredLevel = self.requiredLevel else { + return + } + if self.isApplyingSettings { + return + } + + if let boostStatus = self.boostStatus, requiredLevel > boostStatus.level { + self.displayPremiumScreen(requiredLevel: requiredLevel) + return + } + + self.isApplyingSettings = true + self.state?.updated(transition: .immediate) + + self.applyDisposable?.dispose() + + let nameColor: PeerNameColor + if let updatedPeerNameColor = self.updatedPeerNameColor { + nameColor = updatedPeerNameColor + } else if let peerNameColor = peer.nameColor { + nameColor = peerNameColor + } else { + nameColor = .blue + } + + let profileColor: PeerNameColor? + if case let .some(value) = self.updatedPeerProfileColor { + profileColor = value + } else if let peerProfileColor = peer.profileColor { + profileColor = peerProfileColor + } else { + profileColor = nil + } + + let replyFileId: Int64? + if case let .some(value) = self.updatedPeerNameEmoji { + replyFileId = value + } else { + replyFileId = contentsData.peer?.backgroundEmojiId + } + + let backgroundFileId: Int64? + if case let .some(value) = self.updatedPeerProfileEmoji { + backgroundFileId = value + } else { + backgroundFileId = contentsData.peer?.profileBackgroundEmojiId + } + + let emojiStatus: PeerEmojiStatus? + if case let .some(value) = self.updatedPeerStatusEmoji { + if let value { + emojiStatus = PeerEmojiStatus(fileId: value, expirationDate: nil) + } else { + emojiStatus = nil + } + } else { + emojiStatus = contentsData.peer?.emojiStatus + } + let statusFileId = emojiStatus?.fileId + + let _ = statusFileId + + enum ApplyError { + case generic + } + + self.applyDisposable = (combineLatest([ + component.context.engine.peers.updatePeerNameColorAndEmoji(peerId: component.peerId, nameColor: nameColor, backgroundEmojiId: replyFileId, profileColor: profileColor, profileBackgroundEmojiId: backgroundFileId) + |> mapError { _ -> ApplyError in + return .generic + } + ]) + |> deliverOnMainQueue).start(error: { [weak self] _ in + guard let self else { + return + } + self.isApplyingSettings = false + self.state?.updated(transition: .immediate) + }, completed: { [weak self] in + guard let self else { + return + } + self.environment?.controller()?.dismiss() + }) + } + + private func displayPremiumScreen(requiredLevel: Int) { + guard let component = self.component else { + return + } + + let _ = (component.context.engine.data.get(TelegramEngine.EngineData.Item.Peer.Peer(id: component.peerId)) + |> deliverOnMainQueue).startStandalone(next: { [weak self] peer in + guard let self, let component = self.component, let peer, let status = self.boostStatus else { + return + } + + let premiumConfiguration = PremiumConfiguration.with(appConfiguration: component.context.currentAppConfiguration.with { $0 }) + + let link = status.url + let controller = PremiumLimitScreen(context: component.context, subject: .storiesChannelBoost(peer: peer, boostSubject: .channelReactions(reactionCount: requiredLevel), isCurrent: true, level: Int32(status.level), currentLevelBoosts: Int32(status.currentLevelBoosts), nextLevelBoosts: status.nextLevelBoosts.flatMap(Int32.init), link: link, myBoostCount: 0, canBoostAgain: false), count: Int32(status.boosts), action: { [weak self] in + guard let self, let component = self.component else { + return true + } + + UIPasteboard.general.string = link + let presentationData = component.context.sharedContext.currentPresentationData.with { $0 } + self.environment?.controller()?.present(UndoOverlayController(presentationData: presentationData, content: .linkCopied(text: presentationData.strings.ChannelBoost_BoostLinkCopied), elevatedLayout: false, position: .bottom, animateInAsReplacement: false, action: { _ in return false }), in: .current) + return true + }, openStats: { [weak self] in + guard let self else { + return + } + self.openBoostStats() + }, openGift: premiumConfiguration.giveawayGiftsPurchaseAvailable ? { [weak self] in + guard let self, let component = self.component else { + return + } + let controller = createGiveawayController(context: component.context, peerId: component.peerId, subject: .generic) + self.environment?.controller()?.push(controller) + } : nil) + self.environment?.controller()?.push(controller) + + HapticFeedback().impact(.light) + }) + } + + private func openBoostStats() { + guard let component = self.component, let boostStatus = self.boostStatus else { + return + } + let statsController = component.context.sharedContext.makeChannelStatsController(context: component.context, updatedPresentationData: nil, peerId: component.peerId, boosts: true, boostStatus: boostStatus) + self.environment?.controller()?.push(statsController) + } + + private enum EmojiSetupSubject { + case reply + case profile + case status + } + + private var previousEmojiSetupTimestamp: Double? + private func openEmojiSetup(sourceView: UIView, currentFileId: Int64?, color: UIColor?, subject: EmojiSetupSubject) { + guard let component = self.component, let environment = self.environment else { + return + } + + let currentTimestamp = CACurrentMediaTime() + if let previousTimestamp = self.previousEmojiSetupTimestamp, currentTimestamp < previousTimestamp + 1.0 { + return + } + self.previousEmojiSetupTimestamp = currentTimestamp + + self.emojiStatusSelectionController?.dismiss() + var selectedItems = Set() + if let currentFileId { + selectedItems.insert(MediaId(namespace: Namespaces.Media.CloudFile, id: currentFileId)) + } + + let controller = EmojiStatusSelectionController( + context: component.context, + mode: .backgroundSelection(completion: { [weak self] result in + guard let self else { + return + } + switch subject { + case .reply: + self.updatedPeerNameEmoji = result + case .profile: + self.updatedPeerProfileEmoji = result + case .status: + self.updatedPeerStatusEmoji = result + } + self.state?.updated(transition: .spring(duration: 0.4)) + }), + sourceView: sourceView, + emojiContent: EmojiPagerContentComponent.emojiInputData( + context: component.context, + animationCache: component.context.animationCache, + animationRenderer: component.context.animationRenderer, + isStandalone: false, + subject: subject == .status ? .status : .backgroundIcon, + hasTrending: false, + topReactionItems: [], + areUnicodeEmojiEnabled: false, + areCustomEmojiEnabled: true, + chatPeerId: component.context.account.peerId, + selectedItems: selectedItems, + topStatusTitle: nil, + backgroundIconColor: color + ), + currentSelection: currentFileId, + color: color, + destinationItemView: { [weak sourceView] in + return sourceView + } + ) + self.emojiStatusSelectionController = controller + environment.controller()?.present(controller, in: .window(.root)) + } + + func update(component: ChannelAppearanceScreenComponent, availableSize: CGSize, state: EmptyComponentState, environment: Environment, transition: Transition) -> CGSize { + self.isUpdating = true + defer { + self.isUpdating = false + } + + let environment = environment[EnvironmentType.self].value + let themeUpdated = self.environment?.theme !== environment.theme + self.environment = environment + + self.component = component + self.state = state + + if themeUpdated { + self.backgroundColor = environment.theme.list.blocksBackgroundColor + } + + if self.contentsDataDisposable == nil { + self.contentsDataDisposable = (ContentsData.get(context: component.context, peerId: component.peerId) + |> deliverOnMainQueue).start(next: { [weak self] contentsData in + guard let self else { + return + } + self.contentsData = contentsData + if !self.isUpdating { + self.state?.updated(transition: .immediate) + } + self.isReady.set(true) + }) + } + if self.boostStatusDisposable == nil { + self.boostStatusDisposable = (component.context.engine.peers.getChannelBoostStatus(peerId: component.peerId) + |> deliverOnMainQueue).start(next: { [weak self] boostStatus in + guard let self else { + return + } + self.boostStatus = boostStatus + if !self.isUpdating { + self.state?.updated(transition: .immediate) + } + }) + } + + guard let contentsData = self.contentsData, var peer = contentsData.peer else { + return availableSize + } + + var requiredLevel = 1 + + let replyIconLevel = 5 + let profileIconLevel = 7 + let emojiStatusLevel = 8 + let themeLevel = 9 + + let nameColor: PeerNameColor + if let updatedPeerNameColor = self.updatedPeerNameColor { + nameColor = updatedPeerNameColor + } else if let peerNameColor = peer.nameColor { + nameColor = peerNameColor + } else { + nameColor = .blue + } + + let profileColor: PeerNameColor? + if case let .some(value) = self.updatedPeerProfileColor { + profileColor = value + } else if let peerProfileColor = peer.profileColor { + profileColor = peerProfileColor + } else { + profileColor = nil + } + + let replyFileId: Int64? + if case let .some(value) = self.updatedPeerNameEmoji { + replyFileId = value + } else { + replyFileId = contentsData.peer?.backgroundEmojiId + } + if replyFileId != nil { + requiredLevel = max(requiredLevel, replyIconLevel) + } + + let backgroundFileId: Int64? + if case let .some(value) = self.updatedPeerProfileEmoji { + backgroundFileId = value + } else { + backgroundFileId = contentsData.peer?.profileBackgroundEmojiId + } + if backgroundFileId != nil { + requiredLevel = max(requiredLevel, profileIconLevel) + } + + let emojiStatus: PeerEmojiStatus? + if case let .some(value) = self.updatedPeerStatusEmoji { + if let value { + emojiStatus = PeerEmojiStatus(fileId: value, expirationDate: nil) + } else { + emojiStatus = nil + } + } else { + emojiStatus = contentsData.peer?.emojiStatus + } + if emojiStatus != nil { + requiredLevel = max(requiredLevel, emojiStatusLevel) + } + let statusFileId = emojiStatus?.fileId + + let cloudThemes: [PresentationThemeReference] = contentsData.availableThemes.map { .cloud(PresentationCloudTheme(theme: $0, resolvedWallpaper: nil, creatorAccountId: $0.isCreator ? component.context.account.id : nil)) } + var chatThemes = cloudThemes.filter { $0.emoticon != nil } + chatThemes.insert(.builtin(.dayClassic), at: 0) + + if !chatThemes.isEmpty { + if self.currentTheme == nil { + self.currentTheme = chatThemes[0] + } + } + + if self.currentTheme != nil && self.currentTheme != chatThemes.first { + requiredLevel = max(requiredLevel, themeLevel) + } + + if case let .user(user) = peer { + peer = .user(user + .withUpdatedNameColor(nameColor) + .withUpdatedProfileColor(profileColor) + .withUpdatedEmojiStatus(emojiStatus) + .withUpdatedBackgroundEmojiId(replyFileId) + .withUpdatedProfileBackgroundEmojiId(backgroundFileId) + ) + } else if case let .channel(channel) = peer { + peer = .channel(channel + .withUpdatedNameColor(nameColor) + .withUpdatedProfileColor(profileColor) + .withUpdatedEmojiStatus(emojiStatus) + .withUpdatedBackgroundEmojiId(replyFileId) + .withUpdatedProfileBackgroundEmojiId(backgroundFileId) + ) + } + + self.requiredLevel = requiredLevel + + let topInset: CGFloat = 24.0 + let bottomContentInset: CGFloat = 24.0 + let bottomInset: CGFloat = 8.0 + let sideInset: CGFloat = 16.0 + environment.safeInsets.left + let sectionSpacing: CGFloat = 32.0 + + let listItemParams = ListViewItemLayoutParams(width: availableSize.width - sideInset * 2.0, leftInset: 0.0, rightInset: 0.0, availableHeight: 10000.0, isStandalone: true) + + var contentHeight: CGFloat = 0.0 + contentHeight += environment.navigationHeight + contentHeight += topInset + + let presentationData = component.context.sharedContext.currentPresentationData.with { $0 } + + let messageItem = PeerNameColorChatPreviewItem.MessageItem( + outgoing: false, + peerId: EnginePeer.Id(namespace: peer.id.namespace, id: PeerId.Id._internalFromInt64Value(0)), + author: peer.compactDisplayTitle, + photo: peer.profileImageRepresentations, + nameColor: nameColor, + backgroundEmojiId: replyFileId, + reply: (peer.compactDisplayTitle, environment.strings.NameColor_ChatPreview_ReplyText_Channel), + linkPreview: (environment.strings.NameColor_ChatPreview_LinkSite, environment.strings.NameColor_ChatPreview_LinkTitle, environment.strings.NameColor_ChatPreview_LinkText), + text: environment.strings.NameColor_ChatPreview_MessageText_Channel + ) + + var replyLogoContents: [AnyComponentWithIdentity] = [] + replyLogoContents.append(AnyComponentWithIdentity(id: 0, component: AnyComponent(MultilineTextComponent( + text: .plain(NSAttributedString( + string: "Replies Logo", //TODO:localize + font: Font.regular(presentationData.listsFontSize.baseDisplaySize), + textColor: environment.theme.list.itemPrimaryTextColor + )), + maximumNumberOfLines: 0 + )))) + if replyFileId != nil, let boostStatus = self.boostStatus, boostStatus.level < replyIconLevel { + replyLogoContents.append(AnyComponentWithIdentity(id: 1, component: AnyComponent(BoostLevelIconComponent( + strings: environment.strings, + level: replyIconLevel + )))) + } + + let replySectionSize = self.replySection.update( + transition: transition, + component: AnyComponent(ListSectionComponent( + theme: environment.theme, + header: nil, + footer: AnyComponent(MultilineTextComponent( + text: .plain(NSAttributedString( + string: "Choose a color for the name of your channel, the link it sends, and replies to its messages.", //TODO:localize + font: Font.regular(presentationData.listsFontSize.itemListBaseHeaderFontSize), + textColor: environment.theme.list.freeTextColor + )), + maximumNumberOfLines: 0 + )), + items: [ + AnyComponentWithIdentity(id: 0, component: AnyComponent(ListItemComponentAdaptor( + itemGenerator: PeerNameColorChatPreviewItem( + context: component.context, + theme: environment.theme, + componentTheme: environment.theme, + strings: environment.strings, + sectionId: 0, + fontSize: presentationData.chatFontSize, + chatBubbleCorners: presentationData.chatBubbleCorners, + wallpaper: presentationData.chatWallpaper, + dateTimeFormat: environment.dateTimeFormat, + nameDisplayOrder: presentationData.nameDisplayOrder, + messageItems: [messageItem] + ), + params: listItemParams + ))), + AnyComponentWithIdentity(id: 1, component: AnyComponent(ListItemComponentAdaptor( + itemGenerator: PeerNameColorItem( + theme: environment.theme, + colors: component.context.peerNameColors, + isProfile: false, + currentColor: nameColor, + updated: { [weak self] value in + guard let self else { + return + } + self.updatedPeerNameColor = value + self.state?.updated(transition: .spring(duration: 0.4)) + }, + sectionId: 0 + ), + params: listItemParams + ))), + AnyComponentWithIdentity(id: 2, component: AnyComponent(ListActionItemComponent( + theme: environment.theme, + title: AnyComponent(HStack(replyLogoContents, spacing: 6.0)), + icon: AnyComponentWithIdentity(id: 0, component: AnyComponent(EmojiActionIconComponent( + context: component.context, + color: component.context.peerNameColors.get(nameColor, dark: environment.theme.overallDarkAppearance).main, + fileId: replyFileId, + file: replyFileId.flatMap { self.cachedIconFiles[$0] } + ))), + action: { [weak self] view in + guard let self, let contentsData = self.contentsData, let view = view as? ListActionItemComponent.View, let iconView = view.iconView else { + return + } + + let nameColor: PeerNameColor + if let updatedPeerNameColor = self.updatedPeerNameColor { + nameColor = updatedPeerNameColor + } else if let peerNameColor = peer.nameColor { + nameColor = peerNameColor + } else { + nameColor = .blue + } + + let currentFileId: Int64? + if case let .some(value) = self.updatedPeerNameEmoji { + currentFileId = value + } else { + currentFileId = contentsData.peer?.backgroundEmojiId + } + + self.openEmojiSetup(sourceView: iconView, currentFileId: currentFileId, color: component.context.peerNameColors.get(nameColor, dark: environment.theme.overallDarkAppearance).main, subject: .reply) + } + ))) + ] + )), + environment: {}, + containerSize: CGSize(width: availableSize.width - sideInset * 2.0, height: 1000.0) + ) + let replySectionFrame = CGRect(origin: CGPoint(x: sideInset, y: contentHeight), size: replySectionSize) + if let replySectionView = self.replySection.view { + if replySectionView.superview == nil { + self.scrollView.addSubview(replySectionView) + } + transition.setFrame(view: replySectionView, frame: replySectionFrame) + } + contentHeight += replySectionSize.height + + contentHeight += sectionSpacing + + if !chatThemes.isEmpty, let currentTheme { + var wallpaperLogoContents: [AnyComponentWithIdentity] = [] + wallpaperLogoContents.append(AnyComponentWithIdentity(id: 0, component: AnyComponent(MultilineTextComponent( + text: .plain(NSAttributedString( + string: "Channel Wallpaper", //TODO:localize + font: Font.regular(presentationData.listsFontSize.baseDisplaySize), + textColor: environment.theme.list.itemPrimaryTextColor + )), + maximumNumberOfLines: 0 + )))) + if currentTheme != chatThemes[0], let boostStatus = self.boostStatus, boostStatus.level < themeLevel { + wallpaperLogoContents.append(AnyComponentWithIdentity(id: 1, component: AnyComponent(BoostLevelIconComponent( + strings: environment.strings, + level: themeLevel + )))) + } + + let wallpaperSectionSize = self.wallpaperSection.update( + transition: transition, + component: AnyComponent(ListSectionComponent( + theme: environment.theme, + header: nil, + footer: AnyComponent(MultilineTextComponent( + text: .plain(NSAttributedString( + string: "Set a wallpaper that will be visible to everyone reading your channel.", //TODO:localize + font: Font.regular(presentationData.listsFontSize.itemListBaseHeaderFontSize), + textColor: environment.theme.list.freeTextColor + )), + maximumNumberOfLines: 0 + )), + items: [ + AnyComponentWithIdentity(id: 0, component: AnyComponent(ListItemComponentAdaptor( + itemGenerator: ThemeCarouselThemeItem( + context: component.context, + theme: environment.theme, + strings: environment.strings, + sectionId: 0, + themes: chatThemes, + animatedEmojiStickers: component.context.animatedEmojiStickers, + themeSpecificAccentColors: [:], + themeSpecificChatWallpapers: [:], + nightMode: false, + currentTheme: currentTheme, + updatedTheme: { [weak self] value in + guard let self else { + return + } + self.currentTheme = value + self.state?.updated(transition: .spring(duration: 0.4)) + }, + contextAction: nil + ), + params: listItemParams + ))), + AnyComponentWithIdentity(id: 1, component: AnyComponent(ListActionItemComponent( + theme: environment.theme, + title: AnyComponent(HStack(wallpaperLogoContents, spacing: 6.0)), + icon: nil, + action: { [weak self] view in + guard let self else { + return + } + let _ = self + } + ))) + ] + )), + environment: {}, + containerSize: CGSize(width: availableSize.width - sideInset * 2.0, height: 1000.0) + ) + let wallpaperSectionFrame = CGRect(origin: CGPoint(x: sideInset, y: contentHeight), size: wallpaperSectionSize) + if let wallpaperSectionView = self.wallpaperSection.view { + if wallpaperSectionView.superview == nil { + self.scrollView.addSubview(wallpaperSectionView) + } + transition.setFrame(view: wallpaperSectionView, frame: wallpaperSectionFrame) + } + contentHeight += wallpaperSectionSize.height + contentHeight += sectionSpacing + } + + var profileLogoContents: [AnyComponentWithIdentity] = [] + profileLogoContents.append(AnyComponentWithIdentity(id: 0, component: AnyComponent(MultilineTextComponent( + text: .plain(NSAttributedString( + string: "Profile Logo", //TODO:localize + font: Font.regular(presentationData.listsFontSize.baseDisplaySize), + textColor: environment.theme.list.itemPrimaryTextColor + )), + maximumNumberOfLines: 0 + )))) + if backgroundFileId != nil, let boostStatus = self.boostStatus, boostStatus.level < profileIconLevel { + profileLogoContents.append(AnyComponentWithIdentity(id: 1, component: AnyComponent(BoostLevelIconComponent( + strings: environment.strings, + level: profileIconLevel + )))) + } + + let bannerBackground: ListSectionComponent.Background + if profileColor != nil { + bannerBackground = .range(from: 1, corners: DynamicCornerRadiusView.Corners(minXMinY: 0.0, maxXMinY: 0.0, minXMaxY: 11.0, maxXMaxY: 11.0)) + } else { + bannerBackground = .range(from: 1, corners: DynamicCornerRadiusView.Corners(minXMinY: 11.0, maxXMinY: 11.0, minXMaxY: 11.0, maxXMaxY: 11.0)) + } + let bannerSectionSize = self.bannerSection.update( + transition: transition, + component: AnyComponent(ListSectionComponent( + theme: environment.theme, + background: bannerBackground, + header: AnyComponent(MultilineTextComponent( + text: .plain(NSAttributedString( + string: "PROFILE PAGE COLOR", //TODO:localize + font: Font.regular(presentationData.listsFontSize.itemListBaseHeaderFontSize), + textColor: environment.theme.list.freeTextColor + )), + maximumNumberOfLines: 0 + )), + footer: AnyComponent(MultilineTextComponent( + text: .plain(NSAttributedString( + string: "Choose a color and a logo for the channel's profile.", //TODO:localize + font: Font.regular(presentationData.listsFontSize.itemListBaseHeaderFontSize), + textColor: environment.theme.list.freeTextColor + )), + maximumNumberOfLines: 0 + )), + items: [ + AnyComponentWithIdentity(id: 0, component: AnyComponent(ListItemComponentAdaptor( + itemGenerator: PeerNameColorProfilePreviewItem( + context: component.context, + theme: environment.theme, + componentTheme: environment.theme, + strings: environment.strings, + sectionId: 0, + peer: peer, + subtitleString: contentsData.subscriberCount.flatMap { environment.strings.Conversation_StatusSubscribers(Int32($0)) }, + files: self.cachedIconFiles, + nameDisplayOrder: presentationData.nameDisplayOrder + ), + params: listItemParams + ))), + AnyComponentWithIdentity(id: 1, component: AnyComponent(ListItemComponentAdaptor( + itemGenerator: PeerNameColorItem( + theme: environment.theme, + colors: component.context.peerNameColors, + isProfile: true, + currentColor: profileColor, + updated: { [weak self] value in + guard let self else { + return + } + self.updatedPeerProfileColor = value + self.state?.updated(transition: .spring(duration: 0.4)) + }, + sectionId: 0 + ), + params: listItemParams + ))), + AnyComponentWithIdentity(id: 2, component: AnyComponent(ListActionItemComponent( + theme: environment.theme, + title: AnyComponent(HStack(profileLogoContents, spacing: 6.0)), + icon: AnyComponentWithIdentity(id: 0, component: AnyComponent(EmojiActionIconComponent( + context: component.context, + color: profileColor.flatMap { profileColor in + component.context.peerNameColors.getProfile(profileColor, dark: environment.theme.overallDarkAppearance, subject: .palette).main + } ?? environment.theme.list.itemAccentColor, + fileId: backgroundFileId, + file: backgroundFileId.flatMap { self.cachedIconFiles[$0] } + ))), + action: { [weak self] view in + guard let self, let contentsData = self.contentsData, let view = view as? ListActionItemComponent.View, let iconView = view.iconView else { + return + } + + let currentFileId: Int64? + if case let .some(value) = self.updatedPeerProfileEmoji { + currentFileId = value + } else { + currentFileId = contentsData.peer?.profileBackgroundEmojiId + } + + let profileColor: PeerNameColor? + if case let .some(value) = self.updatedPeerProfileColor { + profileColor = value + } else if let peerProfileColor = peer.profileColor { + profileColor = peerProfileColor + } else { + profileColor = nil + } + + self.openEmojiSetup(sourceView: iconView, currentFileId: currentFileId, color: profileColor.flatMap { profileColor in + component.context.peerNameColors.getProfile(profileColor, dark: environment.theme.overallDarkAppearance, subject: .palette).main + } ?? environment.theme.list.itemAccentColor, subject: .profile) + } + ))) + ] + )), + environment: {}, + containerSize: CGSize(width: availableSize.width - sideInset * 2.0, height: 1000.0) + ) + let bannerSectionFrame = CGRect(origin: CGPoint(x: sideInset, y: contentHeight), size: bannerSectionSize) + if let bannerSectionView = self.bannerSection.view { + if bannerSectionView.superview == nil { + self.scrollView.addSubview(bannerSectionView) + } + transition.setFrame(view: bannerSectionView, frame: bannerSectionFrame) + } + contentHeight += bannerSectionSize.height + contentHeight += sectionSpacing + + var emojiStatusContents: [AnyComponentWithIdentity] = [] + emojiStatusContents.append(AnyComponentWithIdentity(id: 0, component: AnyComponent(MultilineTextComponent( + text: .plain(NSAttributedString( + string: "Channel Emoji Status", //TODO:localize + font: Font.regular(presentationData.listsFontSize.baseDisplaySize), + textColor: environment.theme.list.itemPrimaryTextColor + )), + maximumNumberOfLines: 0 + )))) + if emojiStatus != nil, let boostStatus = self.boostStatus, boostStatus.level < emojiStatusLevel { + emojiStatusContents.append(AnyComponentWithIdentity(id: 1, component: AnyComponent(BoostLevelIconComponent( + strings: environment.strings, + level: emojiStatusLevel + )))) + } + + let resetColorSectionSize = self.resetColorSection.update( + transition: transition, + component: AnyComponent(ListSectionComponent( + theme: environment.theme, + header: nil, + footer: nil, + items: [ + AnyComponentWithIdentity(id: 0, component: AnyComponent(ListActionItemComponent( + theme: environment.theme, + title: AnyComponent(MultilineTextComponent( + text: .plain(NSAttributedString( + string: "Reset Profile Color", //TODO:localize + font: Font.regular(presentationData.listsFontSize.baseDisplaySize), + textColor: environment.theme.list.itemAccentColor + )), + maximumNumberOfLines: 0 + )), + icon: nil, + hasArrow: false, + action: { [weak self] view in + guard let self else { + return + } + + self.updatedPeerProfileColor = .some(nil) + self.updatedPeerProfileEmoji = .some(nil) + self.state?.updated(transition: .spring(duration: 0.4)) + } + ))) + ] + )), + environment: {}, + containerSize: CGSize(width: availableSize.width - sideInset * 2.0, height: 1000.0) + ) + + let displayResetProfileColor = profileColor != nil || backgroundFileId != nil + + let resetColorSectionFrame = CGRect(origin: CGPoint(x: sideInset, y: contentHeight), size: resetColorSectionSize) + if let resetColorSectionView = self.resetColorSection.view { + if resetColorSectionView.superview == nil { + self.scrollView.addSubview(resetColorSectionView) + } + transition.setPosition(view: resetColorSectionView, position: resetColorSectionFrame.center) + transition.setBounds(view: resetColorSectionView, bounds: CGRect(origin: CGPoint(), size: resetColorSectionFrame.size)) + transition.setScale(view: resetColorSectionView, scale: displayResetProfileColor ? 1.0 : 0.001) + transition.setAlpha(view: resetColorSectionView, alpha: displayResetProfileColor ? 1.0 : 0.0) + } + if displayResetProfileColor { + contentHeight += resetColorSectionSize.height + contentHeight += sectionSpacing + } + + let emojiStatusSectionSize = self.emojiStatusSection.update( + transition: transition, + component: AnyComponent(ListSectionComponent( + theme: environment.theme, + header: nil, + footer: AnyComponent(MultilineTextComponent( + text: .plain(NSAttributedString( + string: "Choose a status that will be shown next to the channel's name.", //TODO:localize + font: Font.regular(presentationData.listsFontSize.itemListBaseHeaderFontSize), + textColor: environment.theme.list.freeTextColor + )), + maximumNumberOfLines: 0 + )), + items: [ + AnyComponentWithIdentity(id: 0, component: AnyComponent(ListActionItemComponent( + theme: environment.theme, + title: AnyComponent(HStack(emojiStatusContents, spacing: 6.0)), + icon: AnyComponentWithIdentity(id: 0, component: AnyComponent(EmojiActionIconComponent( + context: component.context, + color: environment.theme.list.itemAccentColor, + fileId: statusFileId, + file: statusFileId.flatMap { self.cachedIconFiles[$0] } + ))), + action: { [weak self] view in + guard let self, let contentsData = self.contentsData, let view = view as? ListActionItemComponent.View, let iconView = view.iconView else { + return + } + + let currentFileId: Int64? + if case let .some(value) = self.updatedPeerStatusEmoji { + currentFileId = value + } else { + currentFileId = contentsData.peer?.emojiStatus?.fileId + } + + self.openEmojiSetup(sourceView: iconView, currentFileId: currentFileId, color: nil, subject: .status) + } + ))) + ] + )), + environment: {}, + containerSize: CGSize(width: availableSize.width - sideInset * 2.0, height: 1000.0) + ) + let emojiStatusSectionFrame = CGRect(origin: CGPoint(x: sideInset, y: contentHeight), size: emojiStatusSectionSize) + if let emojiStatusSectionView = self.emojiStatusSection.view { + if emojiStatusSectionView.superview == nil { + self.scrollView.addSubview(emojiStatusSectionView) + } + transition.setFrame(view: emojiStatusSectionView, frame: emojiStatusSectionFrame) + } + contentHeight += emojiStatusSectionSize.height + + contentHeight += bottomContentInset + + var buttonContents: [AnyComponentWithIdentity] = [] + //TODO:localize + buttonContents.append(AnyComponentWithIdentity(id: AnyHashable(0 as Int), component: AnyComponent( + Text(text: "Apply Changes", font: Font.semibold(17.0), color: environment.theme.list.itemCheckColors.foregroundColor) + ))) + + if let boostStatus = self.boostStatus, requiredLevel > boostStatus.level { + buttonContents.append(AnyComponentWithIdentity(id: AnyHashable(1 as Int), component: AnyComponent(PremiumLockButtonSubtitleComponent( + count: requiredLevel, + theme: environment.theme, + strings: environment.strings + )))) + } + + let buttonSize = self.actionButton.update( + transition: transition, + component: AnyComponent(ButtonComponent( + background: ButtonComponent.Background( + color: environment.theme.list.itemCheckColors.fillColor, + foreground: environment.theme.list.itemCheckColors.foregroundColor, + pressedColor: environment.theme.list.itemCheckColors.fillColor.withMultipliedAlpha(0.8) + ), + content: AnyComponentWithIdentity(id: AnyHashable(0 as Int), component: AnyComponent( + VStack(buttonContents, spacing: 3.0) + )), + isEnabled: true, + tintWhenDisabled: false, + displaysProgress: self.isApplyingSettings, + action: { [weak self] in + guard let self else { + return + } + self.applySettings() + } + )), + environment: {}, + containerSize: CGSize(width: availableSize.width - sideInset * 2.0, height: 50.0) + ) + contentHeight += buttonSize.height + + contentHeight += bottomInset + contentHeight += environment.safeInsets.bottom + + let buttonY = availableSize.height - bottomInset - environment.safeInsets.bottom - buttonSize.height + + let buttonFrame = CGRect(origin: CGPoint(x: sideInset, y: buttonY), size: buttonSize) + if let buttonView = self.actionButton.view { + if buttonView.superview == nil { + self.addSubview(buttonView) + } + transition.setFrame(view: buttonView, frame: buttonFrame) + transition.setAlpha(view: buttonView, alpha: 1.0) + } + + let bottomPanelFrame = CGRect(origin: CGPoint(x: 0.0, y: buttonY - 8.0), size: CGSize(width: availableSize.width, height: availableSize.height - buttonY + 8.0)) + transition.setFrame(view: self.bottomPanelBackgroundView, frame: bottomPanelFrame) + self.bottomPanelBackgroundView.updateColor(color: environment.theme.rootController.navigationBar.blurredBackgroundColor, transition: .immediate) + self.bottomPanelBackgroundView.update(size: bottomPanelFrame.size, transition: transition.containedViewLayoutTransition) + + self.bottomPanelSeparator.backgroundColor = environment.theme.rootController.navigationBar.separatorColor.cgColor + transition.setFrame(layer: self.bottomPanelSeparator, frame: CGRect(origin: CGPoint(x: bottomPanelFrame.minX, y: bottomPanelFrame.minY), size: CGSize(width: bottomPanelFrame.width, height: UIScreenPixel))) + + let contentSize = CGSize(width: availableSize.width, height: contentHeight) + if self.scrollView.frame != CGRect(origin: CGPoint(), size: availableSize) { + self.scrollView.frame = CGRect(origin: CGPoint(), size: availableSize) + } + if self.scrollView.contentSize != contentSize { + self.scrollView.contentSize = contentSize + } + let scrollInsets = UIEdgeInsets(top: environment.navigationHeight, left: 0.0, bottom: availableSize.height - bottomPanelFrame.minY, right: 0.0) + if self.scrollView.scrollIndicatorInsets != scrollInsets { + self.scrollView.scrollIndicatorInsets = scrollInsets + } + + self.updateScrolling(transition: transition) + + return availableSize + } + } + + func makeView() -> View { + return View() + } + + func update(view: View, availableSize: CGSize, state: EmptyComponentState, environment: Environment, transition: Transition) -> CGSize { + return view.update(component: self, availableSize: availableSize, state: state, environment: environment, transition: transition) + } +} + +public class ChannelAppearanceScreen: ViewControllerComponentContainer { + private let context: AccountContext + + private var didSetReady: Bool = false + + public init( + context: AccountContext, + updatedPresentationData: (initial: PresentationData, signal: Signal)?, + peerId: EnginePeer.Id + ) { + self.context = context + + super.init(context: context, component: ChannelAppearanceScreenComponent( + context: context, + peerId: peerId + ), navigationBarAppearance: .default, theme: .default, updatedPresentationData: updatedPresentationData) + + //TODO:localize + self.title = "Appearance" + + self.ready.set(.never()) + + self.scrollToTop = { [weak self] in + guard let self, let componentView = self.node.hostView.componentView as? ChannelAppearanceScreenComponent.View else { + return + } + componentView.scrollToTop() + } + + self.attemptNavigation = { [weak self] complete in + guard let self, let componentView = self.node.hostView.componentView as? ChannelAppearanceScreenComponent.View else { + return true + } + + return componentView.attemptNavigation(complete: complete) + } + } + + required public init(coder aDecoder: NSCoder) { + fatalError("init(coder:) has not been implemented") + } + + deinit { + } + + @objc private func cancelPressed() { + self.dismiss() + } + + override public func containerLayoutUpdated(_ layout: ContainerViewLayout, transition: ContainedViewLayoutTransition) { + super.containerLayoutUpdated(layout, transition: transition) + + if let componentView = self.node.hostView.componentView as? ChannelAppearanceScreenComponent.View { + if !self.didSetReady { + self.didSetReady = true + self.ready.set(componentView.isReady.get()) + } + } + } +} diff --git a/submodules/TelegramUI/Components/Settings/PeerNameColorScreen/Sources/PeerNameColorChatPreviewItem.swift b/submodules/TelegramUI/Components/Settings/PeerNameColorScreen/Sources/PeerNameColorChatPreviewItem.swift index 920bbc1b46..ec0d99e68c 100644 --- a/submodules/TelegramUI/Components/Settings/PeerNameColorScreen/Sources/PeerNameColorChatPreviewItem.swift +++ b/submodules/TelegramUI/Components/Settings/PeerNameColorScreen/Sources/PeerNameColorChatPreviewItem.swift @@ -11,10 +11,11 @@ import ItemListUI import PresentationDataUtils import AccountContext import WallpaperBackgroundNode +import ListItemComponentAdaptor -final class PeerNameColorChatPreviewItem: ListViewItem, ItemListItem { +final class PeerNameColorChatPreviewItem: ListViewItem, ItemListItem, ListItemComponentAdaptor.ItemGenerator { struct MessageItem: Equatable { - static func == (lhs: MessageItem, rhs: MessageItem) -> Bool { + static func ==(lhs: MessageItem, rhs: MessageItem) -> Bool { if lhs.outgoing != rhs.outgoing { return false } @@ -118,6 +119,44 @@ final class PeerNameColorChatPreviewItem: ListViewItem, ItemListItem { } } } + + public func item() -> ListViewItem { + return self + } + + public static func ==(lhs: PeerNameColorChatPreviewItem, rhs: PeerNameColorChatPreviewItem) -> Bool { + if lhs.context !== rhs.context { + return false + } + if lhs.theme !== rhs.theme { + return false + } + if lhs.componentTheme !== rhs.componentTheme { + return false + } + if lhs.strings !== rhs.strings { + return false + } + if lhs.fontSize != rhs.fontSize { + return false + } + if lhs.chatBubbleCorners != rhs.chatBubbleCorners { + return false + } + if lhs.wallpaper != rhs.wallpaper { + return false + } + if lhs.dateTimeFormat != rhs.dateTimeFormat { + return false + } + if lhs.nameDisplayOrder != rhs.nameDisplayOrder { + return false + } + if lhs.messageItems != rhs.messageItems { + return false + } + return true + } } final class PeerNameColorChatPreviewItemNode: ListViewItemNode { @@ -329,31 +368,42 @@ final class PeerNameColorChatPreviewItemNode: ListViewItemNode { strongSelf.insertSubnode(strongSelf.maskNode, at: 3) } - let hasCorners = itemListHasRoundedBlockLayout(params) - var hasTopCorners = false - var hasBottomCorners = false - switch neighbors.top { + if params.isStandalone { + strongSelf.topStripeNode.isHidden = true + strongSelf.bottomStripeNode.isHidden = true + strongSelf.maskNode.isHidden = true + } else { + let hasCorners = itemListHasRoundedBlockLayout(params) + + var hasTopCorners = false + var hasBottomCorners = false + + switch neighbors.top { case .sameSection(false): strongSelf.topStripeNode.isHidden = true default: hasTopCorners = true strongSelf.topStripeNode.isHidden = hasCorners + } + let bottomStripeInset: CGFloat + let bottomStripeOffset: CGFloat + switch neighbors.bottom { + case .sameSection(false): + bottomStripeInset = 0.0 + bottomStripeOffset = -separatorHeight + strongSelf.bottomStripeNode.isHidden = false + default: + bottomStripeInset = 0.0 + bottomStripeOffset = 0.0 + hasBottomCorners = true + strongSelf.bottomStripeNode.isHidden = hasCorners + } + + strongSelf.maskNode.image = hasCorners ? PresentationResourcesItemList.cornersImage(item.componentTheme, top: hasTopCorners, bottom: hasBottomCorners) : nil + + strongSelf.topStripeNode.frame = CGRect(origin: CGPoint(x: 0.0, y: -min(insets.top, separatorHeight)), size: CGSize(width: layoutSize.width, height: separatorHeight)) + strongSelf.bottomStripeNode.frame = CGRect(origin: CGPoint(x: bottomStripeInset, y: contentSize.height + bottomStripeOffset), size: CGSize(width: layoutSize.width - bottomStripeInset, height: separatorHeight)) } - let bottomStripeInset: CGFloat - let bottomStripeOffset: CGFloat - switch neighbors.bottom { - case .sameSection(false): - bottomStripeInset = 0.0 - bottomStripeOffset = -separatorHeight - strongSelf.bottomStripeNode.isHidden = false - default: - bottomStripeInset = 0.0 - bottomStripeOffset = 0.0 - hasBottomCorners = true - strongSelf.bottomStripeNode.isHidden = hasCorners - } - - strongSelf.maskNode.image = hasCorners ? PresentationResourcesItemList.cornersImage(item.componentTheme, top: hasTopCorners, bottom: hasBottomCorners) : nil let backgroundFrame = CGRect(origin: CGPoint(x: 0.0, y: 0.0), size: CGSize(width: params.width, height: contentSize.height + min(insets.top, separatorHeight) + min(insets.bottom, separatorHeight))) @@ -377,8 +427,6 @@ final class PeerNameColorChatPreviewItemNode: ListViewItemNode { backgroundNode.updateLayout(size: backgroundNode.bounds.size, displayMode: displayMode, transition: .immediate) } strongSelf.maskNode.frame = backgroundFrame.insetBy(dx: params.leftInset, dy: 0.0) - strongSelf.topStripeNode.frame = CGRect(origin: CGPoint(x: 0.0, y: -min(insets.top, separatorHeight)), size: CGSize(width: layoutSize.width, height: separatorHeight)) - strongSelf.bottomStripeNode.frame = CGRect(origin: CGPoint(x: bottomStripeInset, y: contentSize.height + bottomStripeOffset), size: CGSize(width: layoutSize.width - bottomStripeInset, height: separatorHeight)) } }) } diff --git a/submodules/TelegramUI/Components/Settings/PeerNameColorScreen/Sources/PeerNameColorItem.swift b/submodules/TelegramUI/Components/Settings/PeerNameColorScreen/Sources/PeerNameColorItem.swift index 25c36e87b9..8d10567b47 100644 --- a/submodules/TelegramUI/Components/Settings/PeerNameColorScreen/Sources/PeerNameColorItem.swift +++ b/submodules/TelegramUI/Components/Settings/PeerNameColorScreen/Sources/PeerNameColorItem.swift @@ -10,6 +10,7 @@ import MergeLists import ItemListUI import PresentationDataUtils import AccountContext +import ListItemComponentAdaptor private enum PeerNameColorEntryId: Hashable { case color(Int32) @@ -313,7 +314,7 @@ private final class PeerNameColorIconItemNode : ListViewItemNode { } } -final class PeerNameColorItem: ListViewItem, ItemListItem { +final class PeerNameColorItem: ListViewItem, ItemListItem, ListItemComponentAdaptor.ItemGenerator { var sectionId: ItemListSectionId let theme: PresentationTheme @@ -365,6 +366,27 @@ final class PeerNameColorItem: ListViewItem, ItemListItem { } } } + + public func item() -> ListViewItem { + return self + } + + public static func ==(lhs: PeerNameColorItem, rhs: PeerNameColorItem) -> Bool { + if lhs.theme !== rhs.theme { + return false + } + if lhs.colors != rhs.colors { + return false + } + if lhs.isProfile != rhs.isProfile { + return false + } + if lhs.currentColor != rhs.currentColor { + return false + } + + return true + } } private struct PeerNameColorItemNodeTransition { @@ -528,24 +550,30 @@ final class PeerNameColorItemNode: ListViewItemNode, ItemListItemNode { strongSelf.containerNode.insertSubnode(strongSelf.maskNode, at: 3) } - let hasCorners = itemListHasRoundedBlockLayout(params) - var hasTopCorners = false - var hasBottomCorners = false - if item.currentColor != nil { - switch neighbors.top { - case .sameSection(false): - strongSelf.topStripeNode.isHidden = true - default: - hasTopCorners = true - strongSelf.topStripeNode.isHidden = hasCorners - } - } else { + if params.isStandalone { + strongSelf.backgroundNode.isHidden = true strongSelf.topStripeNode.isHidden = true - hasTopCorners = true - } - let bottomStripeInset: CGFloat - let bottomStripeOffset: CGFloat - switch neighbors.bottom { + strongSelf.bottomStripeNode.isHidden = true + strongSelf.maskNode.isHidden = true + } else { + let hasCorners = itemListHasRoundedBlockLayout(params) + var hasTopCorners = false + var hasBottomCorners = false + if item.currentColor != nil { + switch neighbors.top { + case .sameSection(false): + strongSelf.topStripeNode.isHidden = true + default: + hasTopCorners = true + strongSelf.topStripeNode.isHidden = hasCorners + } + } else { + strongSelf.topStripeNode.isHidden = true + hasTopCorners = true + } + let bottomStripeInset: CGFloat + let bottomStripeOffset: CGFloat + switch neighbors.bottom { case .sameSection(false): bottomStripeInset = params.leftInset + 16.0 bottomStripeOffset = -separatorHeight @@ -555,15 +583,17 @@ final class PeerNameColorItemNode: ListViewItemNode, ItemListItemNode { bottomStripeOffset = 0.0 hasBottomCorners = true strongSelf.bottomStripeNode.isHidden = hasCorners + } + strongSelf.maskNode.image = hasCorners ? PresentationResourcesItemList.cornersImage(item.theme, top: hasTopCorners, bottom: hasBottomCorners) : nil + + strongSelf.bottomStripeNode.frame = CGRect(origin: CGPoint(x: bottomStripeInset, y: contentSize.height + bottomStripeOffset), size: CGSize(width: layoutSize.width - bottomStripeInset, height: separatorHeight)) } strongSelf.containerNode.frame = CGRect(x: 0.0, y: 0.0, width: contentSize.width, height: contentSize.height) - strongSelf.maskNode.image = hasCorners ? PresentationResourcesItemList.cornersImage(item.theme, top: hasTopCorners, bottom: hasBottomCorners) : nil strongSelf.backgroundNode.frame = CGRect(origin: CGPoint(x: 0.0, y: -min(insets.top, separatorHeight)), size: CGSize(width: params.width, height: contentSize.height + min(insets.top, separatorHeight) + min(insets.bottom, separatorHeight))) strongSelf.maskNode.frame = strongSelf.backgroundNode.frame.insetBy(dx: params.leftInset, dy: 0.0) strongSelf.topStripeNode.frame = CGRect(origin: CGPoint(x: 0.0, y: -min(insets.top, separatorHeight)), size: CGSize(width: layoutSize.width, height: separatorHeight)) - strongSelf.bottomStripeNode.frame = CGRect(origin: CGPoint(x: bottomStripeInset, y: contentSize.height + bottomStripeOffset), size: CGSize(width: layoutSize.width - bottomStripeInset, height: separatorHeight)) var listInsets = UIEdgeInsets() listInsets.top += params.leftInset + 8.0 diff --git a/submodules/TelegramUI/Components/Settings/PeerNameColorScreen/Sources/PeerNameColorProfilePreviewItem.swift b/submodules/TelegramUI/Components/Settings/PeerNameColorScreen/Sources/PeerNameColorProfilePreviewItem.swift index 9db8346dd7..32ef02063c 100644 --- a/submodules/TelegramUI/Components/Settings/PeerNameColorScreen/Sources/PeerNameColorProfilePreviewItem.swift +++ b/submodules/TelegramUI/Components/Settings/PeerNameColorScreen/Sources/PeerNameColorProfilePreviewItem.swift @@ -14,24 +14,28 @@ import ComponentFlow import PeerInfoCoverComponent import AvatarNode import EmojiStatusComponent +import ListItemComponentAdaptor +import ComponentDisplayAdapters -final class PeerNameColorProfilePreviewItem: ListViewItem, ItemListItem { +final class PeerNameColorProfilePreviewItem: ListViewItem, ItemListItem, ListItemComponentAdaptor.ItemGenerator { let context: AccountContext let theme: PresentationTheme let componentTheme: PresentationTheme let strings: PresentationStrings let sectionId: ItemListSectionId let peer: EnginePeer? + let subtitleString: String? let files: [Int64: TelegramMediaFile] let nameDisplayOrder: PresentationPersonNameOrder - init(context: AccountContext, theme: PresentationTheme, componentTheme: PresentationTheme, strings: PresentationStrings, sectionId: ItemListSectionId, peer: EnginePeer?, files: [Int64: TelegramMediaFile], nameDisplayOrder: PresentationPersonNameOrder) { + init(context: AccountContext, theme: PresentationTheme, componentTheme: PresentationTheme, strings: PresentationStrings, sectionId: ItemListSectionId, peer: EnginePeer?, subtitleString: String? = nil, files: [Int64: TelegramMediaFile], nameDisplayOrder: PresentationPersonNameOrder) { self.context = context self.theme = theme self.componentTheme = componentTheme self.strings = strings self.sectionId = sectionId self.peer = peer + self.subtitleString = subtitleString self.files = files self.nameDisplayOrder = nameDisplayOrder } @@ -46,7 +50,7 @@ final class PeerNameColorProfilePreviewItem: ListViewItem, ItemListItem { Queue.mainQueue().async { completion(node, { - return (nil, { _ in apply() }) + return (nil, { _ in apply(.None) }) }) } } @@ -61,13 +65,43 @@ final class PeerNameColorProfilePreviewItem: ListViewItem, ItemListItem { let (layout, apply) = makeLayout(self, params, itemListNeighbors(item: self, topItem: previousItem as? ItemListItem, bottomItem: nextItem as? ItemListItem)) Queue.mainQueue().async { completion(layout, { _ in - apply() + apply(animation) }) } } } } } + + func item() -> ListViewItem { + return self + } + + static func ==(lhs: PeerNameColorProfilePreviewItem, rhs: PeerNameColorProfilePreviewItem) -> Bool { + if lhs.context !== rhs.context { + return false + } + if lhs.theme !== rhs.theme { + return false + } + if lhs.componentTheme !== rhs.componentTheme { + return false + } + if lhs.strings !== rhs.strings { + return false + } + if lhs.peer != rhs.peer { + return false + } + if lhs.files != rhs.files { + return false + } + if lhs.nameDisplayOrder != rhs.nameDisplayOrder { + return false + } + + return true + } } final class PeerNameColorProfilePreviewItemNode: ListViewItemNode { @@ -104,7 +138,7 @@ final class PeerNameColorProfilePreviewItemNode: ListViewItemNode { deinit { } - func asyncLayout() -> (_ item: PeerNameColorProfilePreviewItem, _ params: ListViewItemLayoutParams, _ neighbors: ItemListNeighbors) -> (ListViewItemNodeLayout, () -> Void) { + func asyncLayout() -> (_ item: PeerNameColorProfilePreviewItem, _ params: ListViewItemLayoutParams, _ neighbors: ItemListNeighbors) -> (ListViewItemNodeLayout, (ListViewItemUpdateAnimation) -> Void) { return { [weak self] item, params, neighbors in let separatorHeight = UIScreenPixel @@ -117,7 +151,7 @@ final class PeerNameColorProfilePreviewItemNode: ListViewItemNode { let layout = ListViewItemNodeLayout(contentSize: contentSize, insets: insets) let layoutSize = layout.size - return (layout, { [weak self] in + return (layout, { [weak self] animation in guard let self else { return } @@ -139,32 +173,41 @@ final class PeerNameColorProfilePreviewItemNode: ListViewItemNode { if self.maskNode.supernode == nil { self.addSubnode(self.maskNode) } - - let hasCorners = itemListHasRoundedBlockLayout(params) - var hasTopCorners = false - var hasBottomCorners = false - switch neighbors.top { + + if params.isStandalone { + self.topStripeNode.isHidden = true + self.bottomStripeNode.isHidden = true + self.maskNode.isHidden = true + } else { + let hasCorners = itemListHasRoundedBlockLayout(params) + var hasTopCorners = false + var hasBottomCorners = false + switch neighbors.top { case .sameSection(false): self.topStripeNode.isHidden = true default: hasTopCorners = true self.topStripeNode.isHidden = hasCorners + } + let bottomStripeInset: CGFloat + let bottomStripeOffset: CGFloat + switch neighbors.bottom { + case .sameSection(false): + bottomStripeInset = 0.0 + bottomStripeOffset = -separatorHeight + self.bottomStripeNode.isHidden = item.peer?.profileColor == nil + default: + bottomStripeInset = 0.0 + bottomStripeOffset = 0.0 + hasBottomCorners = true + self.bottomStripeNode.isHidden = hasCorners + } + + self.maskNode.image = hasCorners ? PresentationResourcesItemList.cornersImage(item.componentTheme, top: hasTopCorners, bottom: hasBottomCorners) : nil + + self.topStripeNode.frame = CGRect(origin: CGPoint(x: 0.0, y: -min(insets.top, separatorHeight)), size: CGSize(width: layoutSize.width, height: separatorHeight)) + self.bottomStripeNode.frame = CGRect(origin: CGPoint(x: bottomStripeInset, y: contentSize.height + bottomStripeOffset), size: CGSize(width: layoutSize.width - bottomStripeInset, height: separatorHeight)) } - let bottomStripeInset: CGFloat - let bottomStripeOffset: CGFloat - switch neighbors.bottom { - case .sameSection(false): - bottomStripeInset = 0.0 - bottomStripeOffset = -separatorHeight - self.bottomStripeNode.isHidden = item.peer?.profileColor == nil - default: - bottomStripeInset = 0.0 - bottomStripeOffset = 0.0 - hasBottomCorners = true - self.bottomStripeNode.isHidden = hasCorners - } - - self.maskNode.image = hasCorners ? PresentationResourcesItemList.cornersImage(item.componentTheme, top: hasTopCorners, bottom: hasBottomCorners) : nil let backgroundFrame = CGRect(origin: CGPoint(x: 0.0, y: 0.0), size: CGSize(width: params.width, height: contentSize.height + min(insets.top, separatorHeight) + min(insets.bottom, separatorHeight))) @@ -217,6 +260,61 @@ final class PeerNameColorProfilePreviewItemNode: ListViewItemNode { } self.avatarNode.frame = avatarFrame.offsetBy(dx: coverFrame.minX, dy: coverFrame.minY) + let premiumConfiguration = PremiumConfiguration.with(appConfiguration: item.context.currentAppConfiguration.with { $0 }) + + enum CredibilityIcon { + case none + case premium + case verified + case fake + case scam + case emojiStatus(PeerEmojiStatus) + } + + let credibilityIcon: CredibilityIcon + if let peer = item.peer { + if peer.isFake { + credibilityIcon = .fake + } else if peer.isScam { + credibilityIcon = .scam + } else if case let .user(user) = peer, let emojiStatus = user.emojiStatus, !premiumConfiguration.isPremiumDisabled { + credibilityIcon = .emojiStatus(emojiStatus) + } else if case let .channel(channel) = peer, let emojiStatus = channel.emojiStatus, !premiumConfiguration.isPremiumDisabled { + credibilityIcon = .emojiStatus(emojiStatus) + } else if peer.isVerified { + credibilityIcon = .verified + } else if peer.isPremium && !premiumConfiguration.isPremiumDisabled && (peer.id != item.context.account.peerId) { + credibilityIcon = .premium + } else { + credibilityIcon = .none + } + } else { + credibilityIcon = .none + } + + let statusColor: UIColor + if let peer = item.peer, peer.profileColor != nil { + statusColor = .white + } else { + statusColor = item.theme.list.itemCheckColors.fillColor + } + + let emojiStatusContent: EmojiStatusComponent.Content + switch credibilityIcon { + case .none: + emojiStatusContent = .none + case .premium: + emojiStatusContent = .premium(color: statusColor) + case .verified: + emojiStatusContent = .verified(fillColor: statusColor, foregroundColor: .clear, sizeType: .large) + case .fake: + emojiStatusContent = .text(color: item.theme.chat.message.incoming.scamColor, string: item.strings.Message_FakeAccount.uppercased()) + case .scam: + emojiStatusContent = .text(color: item.theme.chat.message.incoming.scamColor, string: item.strings.Message_ScamAccount.uppercased()) + case let .emojiStatus(emojiStatus): + emojiStatusContent = .animation(content: .customEmoji(fileId: emojiStatus.fileId), size: CGSize(width: 80.0, height: 80.0), placeholderColor: item.theme.list.mediaPlaceholderColor, themeColor: statusColor, loopMode: .forever) + } + let backgroundColor: UIColor let titleColor: UIColor let subtitleColor: UIColor @@ -239,15 +337,64 @@ final class PeerNameColorProfilePreviewItemNode: ListViewItemNode { environment: {}, containerSize: CGSize(width: coverFrame.width - 16.0, height: 100.0) ) - let titleFrame = CGRect(origin: CGPoint(x: coverFrame.minX + floor((coverFrame.width - titleSize.width) * 0.5), y: avatarFrame.maxY + 10.0), size: titleSize) + + var titleContentWidth = titleSize.width + var hasStatusIcon = false + if case .none = emojiStatusContent { + } else { + hasStatusIcon = true + titleContentWidth += 4.0 + 34.0 + } + let _ = hasStatusIcon + + let titleFrame = CGRect(origin: CGPoint(x: coverFrame.minX + floor((coverFrame.width - titleContentWidth) * 0.5), y: avatarFrame.maxY + 10.0), size: titleSize) if let titleView = self.title.view { if titleView.superview == nil { + titleView.layer.anchorPoint = CGPoint(x: 0.0, y: 0.0) self.view.addSubview(titleView) } - titleView.frame = titleFrame + titleView.bounds = CGRect(origin: CGPoint(), size: titleFrame.size) + animation.animator.updatePosition(layer: titleView.layer, position: titleFrame.origin, completion: nil) } - let subtitleString: String = item.strings.LastSeen_JustNow + let icon: ComponentView + if let current = self.icon { + icon = current + } else { + icon = ComponentView() + self.icon = icon + } + let iconSize = CGSize(width: 34.0, height: 34.0) + let _ = icon.update( + transition: Transition(animation.transition), + component: AnyComponent(EmojiStatusComponent( + context: item.context, + animationCache: item.context.animationCache, + animationRenderer: item.context.animationRenderer, + content: emojiStatusContent, + isVisibleForAnimations: true, + action: nil + )), + environment: {}, + containerSize: iconSize + ) + if let iconView = icon.view { + if iconView.superview == nil { + self.view.addSubview(iconView) + } + let iconFrame = CGRect(origin: CGPoint(x: titleFrame.maxX + 4.0, y: titleFrame.minY + floorToScreenPixels((titleFrame.height - iconSize.height) * 0.5)), size: iconSize) + iconView.bounds = CGRect(origin: CGPoint(), size: iconFrame.size) + animation.animator.updatePosition(layer: iconView.layer, position: iconFrame.center, completion: nil) + } + + let subtitleString: String + if let value = item.subtitleString { + subtitleString = value + } else if case .channel = item.peer { + subtitleString = item.strings.Channel_Status + } else { + subtitleString = item.strings.LastSeen_JustNow + } let subtitleSize = self.subtitle.update( transition: .immediate, component: AnyComponent(Text( @@ -265,8 +412,6 @@ final class PeerNameColorProfilePreviewItemNode: ListViewItemNode { } self.maskNode.frame = backgroundFrame.insetBy(dx: params.leftInset, dy: 0.0) - self.topStripeNode.frame = CGRect(origin: CGPoint(x: 0.0, y: -min(insets.top, separatorHeight)), size: CGSize(width: layoutSize.width, height: separatorHeight)) - self.bottomStripeNode.frame = CGRect(origin: CGPoint(x: bottomStripeInset, y: contentSize.height + bottomStripeOffset), size: CGSize(width: layoutSize.width - bottomStripeInset, height: separatorHeight)) }) } } diff --git a/submodules/TelegramUI/Components/Settings/ThemeCarouselItem/BUILD b/submodules/TelegramUI/Components/Settings/ThemeCarouselItem/BUILD new file mode 100644 index 0000000000..7e7c767224 --- /dev/null +++ b/submodules/TelegramUI/Components/Settings/ThemeCarouselItem/BUILD @@ -0,0 +1,37 @@ +load("@build_bazel_rules_swift//swift:swift.bzl", "swift_library") + +swift_library( + name = "ThemeCarouselItem", + module_name = "ThemeCarouselItem", + srcs = glob([ + "Sources/**/*.swift", + ]), + copts = [ + "-warnings-as-errors", + ], + deps = [ + "//submodules/Display", + "//submodules/AsyncDisplayKit", + "//submodules/SSignalKit/SwiftSignalKit", + "//submodules/Postbox", + "//submodules/TelegramCore", + "//submodules/TelegramPresentationData", + "//submodules/MergeLists", + "//submodules/TelegramUIPreferences", + "//submodules/ItemListUI", + "//submodules/PresentationDataUtils", + "//submodules/WallpaperResources", + "//submodules/AccountContext", + "//submodules/AppBundle", + "//submodules/ContextUI", + "//submodules/AnimatedStickerNode", + "//submodules/TelegramAnimatedStickerNode", + "//submodules/ShimmerEffect", + "//submodules/StickerResources", + "//submodules/TelegramUI/Components/ListItemComponentAdaptor", + "//submodules/HexColor", + ], + visibility = [ + "//visibility:public", + ], +) diff --git a/submodules/SettingsUI/Sources/ThemeCarouselItem.swift b/submodules/TelegramUI/Components/Settings/ThemeCarouselItem/Sources/ThemeCarouselItem.swift similarity index 86% rename from submodules/SettingsUI/Sources/ThemeCarouselItem.swift rename to submodules/TelegramUI/Components/Settings/ThemeCarouselItem/Sources/ThemeCarouselItem.swift index 7744e5a771..9b30e4247e 100644 --- a/submodules/SettingsUI/Sources/ThemeCarouselItem.swift +++ b/submodules/TelegramUI/Components/Settings/ThemeCarouselItem/Sources/ThemeCarouselItem.swift @@ -18,6 +18,8 @@ import AnimatedStickerNode import TelegramAnimatedStickerNode import ShimmerEffect import StickerResources +import ListItemComponentAdaptor +import HexColor private struct ThemeCarouselThemeEntry: Comparable, Identifiable { let index: Int @@ -79,19 +81,19 @@ private struct ThemeCarouselThemeEntry: Comparable, Identifiable { } -class ThemeCarouselThemeIconItem: ListViewItem { - let context: AccountContext - let emojiFile: TelegramMediaFile? - let themeReference: PresentationThemeReference - let nightMode: Bool - let themeSpecificAccentColors: [Int64: PresentationThemeAccentColor] - let themeSpecificChatWallpapers: [Int64: TelegramWallpaper] - let selected: Bool - let theme: PresentationTheme - let strings: PresentationStrings - let wallpaper: TelegramWallpaper? - let action: (PresentationThemeReference) -> Void - let contextAction: ((PresentationThemeReference, ASDisplayNode, ContextGesture?) -> Void)? +public class ThemeCarouselThemeIconItem: ListViewItem { + public let context: AccountContext + public let emojiFile: TelegramMediaFile? + public let themeReference: PresentationThemeReference + public let nightMode: Bool + public let themeSpecificAccentColors: [Int64: PresentationThemeAccentColor] + public let themeSpecificChatWallpapers: [Int64: TelegramWallpaper] + public let selected: Bool + public let theme: PresentationTheme + public let strings: PresentationStrings + public let wallpaper: TelegramWallpaper? + public let action: (PresentationThemeReference) -> Void + public let contextAction: ((PresentationThemeReference, ASDisplayNode, ContextGesture?) -> Void)? public init(context: AccountContext, emojiFile: TelegramMediaFile?, themeReference: PresentationThemeReference, nightMode: Bool, themeSpecificAccentColors: [Int64: PresentationThemeAccentColor], themeSpecificChatWallpapers: [Int64: TelegramWallpaper], selected: Bool, theme: PresentationTheme, strings: PresentationStrings, wallpaper: TelegramWallpaper?, action: @escaping (PresentationThemeReference) -> Void, contextAction: ((PresentationThemeReference, ASDisplayNode, ContextGesture?) -> Void)?) { self.context = context @@ -148,7 +150,6 @@ class ThemeCarouselThemeIconItem: ListViewItem { } } - private let textFont = Font.regular(12.0) private let selectedTextFont = Font.bold(12.0) @@ -507,23 +508,23 @@ private final class ThemeCarouselThemeItemIconNode : ListViewItemNode { } } -class ThemeCarouselThemeItem: ListViewItem, ItemListItem { - var sectionId: ItemListSectionId +public class ThemeCarouselThemeItem: ListViewItem, ItemListItem, ListItemComponentAdaptor.ItemGenerator { + public var sectionId: ItemListSectionId - let context: AccountContext - let theme: PresentationTheme - let strings: PresentationStrings - let themes: [PresentationThemeReference] - let animatedEmojiStickers: [String: [StickerPackItem]] - let themeSpecificAccentColors: [Int64: PresentationThemeAccentColor] - let themeSpecificChatWallpapers: [Int64: TelegramWallpaper] - let nightMode: Bool - let currentTheme: PresentationThemeReference - let updatedTheme: (PresentationThemeReference) -> Void - let contextAction: ((PresentationThemeReference, ASDisplayNode, ContextGesture?) -> Void)? - let tag: ItemListItemTag? + public let context: AccountContext + public let theme: PresentationTheme + public let strings: PresentationStrings + public let themes: [PresentationThemeReference] + public let animatedEmojiStickers: [String: [StickerPackItem]] + public let themeSpecificAccentColors: [Int64: PresentationThemeAccentColor] + public let themeSpecificChatWallpapers: [Int64: TelegramWallpaper] + public let nightMode: Bool + public let currentTheme: PresentationThemeReference + public let updatedTheme: (PresentationThemeReference) -> Void + public let contextAction: ((PresentationThemeReference, ASDisplayNode, ContextGesture?) -> Void)? + public let tag: ItemListItemTag? - init(context: AccountContext, theme: PresentationTheme, strings: PresentationStrings, sectionId: ItemListSectionId, themes: [PresentationThemeReference], animatedEmojiStickers: [String: [StickerPackItem]], themeSpecificAccentColors: [Int64: PresentationThemeAccentColor], themeSpecificChatWallpapers: [Int64: TelegramWallpaper], nightMode: Bool, currentTheme: PresentationThemeReference, updatedTheme: @escaping (PresentationThemeReference) -> Void, contextAction: ((PresentationThemeReference, ASDisplayNode, ContextGesture?) -> Void)?, tag: ItemListItemTag? = nil) { + public init(context: AccountContext, theme: PresentationTheme, strings: PresentationStrings, sectionId: ItemListSectionId, themes: [PresentationThemeReference], animatedEmojiStickers: [String: [StickerPackItem]], themeSpecificAccentColors: [Int64: PresentationThemeAccentColor], themeSpecificChatWallpapers: [Int64: TelegramWallpaper], nightMode: Bool, currentTheme: PresentationThemeReference, updatedTheme: @escaping (PresentationThemeReference) -> Void, contextAction: ((PresentationThemeReference, ASDisplayNode, ContextGesture?) -> Void)?, tag: ItemListItemTag? = nil) { self.context = context self.theme = theme self.strings = strings @@ -539,7 +540,7 @@ class ThemeCarouselThemeItem: ListViewItem, ItemListItem { self.sectionId = sectionId } - func nodeConfiguredForParams(async: @escaping (@escaping () -> Void) -> Void, params: ListViewItemLayoutParams, synchronousLoads: Bool, previousItem: ListViewItem?, nextItem: ListViewItem?, completion: @escaping (ListViewItemNode, @escaping () -> (Signal?, (ListViewItemApply) -> Void)) -> Void) { + public func nodeConfiguredForParams(async: @escaping (@escaping () -> Void) -> Void, params: ListViewItemLayoutParams, synchronousLoads: Bool, previousItem: ListViewItem?, nextItem: ListViewItem?, completion: @escaping (ListViewItemNode, @escaping () -> (Signal?, (ListViewItemApply) -> Void)) -> Void) { async { let node = ThemeCarouselThemeItemNode() let (layout, apply) = node.asyncLayout()(self, params, itemListNeighbors(item: self, topItem: previousItem as? ItemListItem, bottomItem: nextItem as? ItemListItem)) @@ -555,7 +556,7 @@ class ThemeCarouselThemeItem: ListViewItem, ItemListItem { } } - func updateNode(async: @escaping (@escaping () -> Void) -> Void, node: @escaping () -> ListViewItemNode, params: ListViewItemLayoutParams, previousItem: ListViewItem?, nextItem: ListViewItem?, animation: ListViewItemUpdateAnimation, completion: @escaping (ListViewItemNodeLayout, @escaping (ListViewItemApply) -> Void) -> Void) { + public func updateNode(async: @escaping (@escaping () -> Void) -> Void, node: @escaping () -> ListViewItemNode, params: ListViewItemLayoutParams, previousItem: ListViewItem?, nextItem: ListViewItem?, animation: ListViewItemUpdateAnimation, completion: @escaping (ListViewItemNodeLayout, @escaping (ListViewItemApply) -> Void) -> Void) { Queue.mainQueue().async { if let nodeValue = node() as? ThemeCarouselThemeItemNode { let makeLayout = nodeValue.asyncLayout() @@ -571,6 +572,42 @@ class ThemeCarouselThemeItem: ListViewItem, ItemListItem { } } } + + public func item() -> ListViewItem { + return self + } + + public static func ==(lhs: ThemeCarouselThemeItem, rhs: ThemeCarouselThemeItem) -> Bool { + if lhs.context !== rhs.context { + return false + } + if lhs.theme !== rhs.theme { + return false + } + if lhs.strings !== rhs.strings { + return false + } + if lhs.themes != rhs.themes { + return false + } + if lhs.animatedEmojiStickers != rhs.animatedEmojiStickers { + return false + } + if lhs.themeSpecificAccentColors != rhs.themeSpecificAccentColors { + return false + } + if lhs.themeSpecificChatWallpapers != rhs.themeSpecificChatWallpapers { + return false + } + if lhs.nightMode != rhs.nightMode { + return false + } + if lhs.currentTheme != rhs.currentTheme { + return false + } + + return true + } } private struct ThemeCarouselThemeItemNodeTransition { @@ -609,7 +646,7 @@ private func ensureThemeVisible(listNode: ListView, themeReference: Presentation } } -class ThemeCarouselThemeItemNode: ListViewItemNode, ItemListItemNode { +public class ThemeCarouselThemeItemNode: ListViewItemNode, ItemListItemNode { private let containerNode: ASDisplayNode private let backgroundNode: ASDisplayNode private let topStripeNode: ASDisplayNode @@ -627,11 +664,11 @@ class ThemeCarouselThemeItemNode: ListViewItemNode, ItemListItemNode { private var tapping = false - var tag: ItemListItemTag? { + public var tag: ItemListItemTag? { return self.item?.tag } - init() { + public init() { self.containerNode = ASDisplayNode() self.backgroundNode = ASDisplayNode() @@ -654,7 +691,7 @@ class ThemeCarouselThemeItemNode: ListViewItemNode, ItemListItemNode { self.addSubnode(self.listNode) } - override func didLoad() { + override public func didLoad() { super.didLoad() self.listNode.view.disablesInteractiveTransitionGestureRecognizer = true } @@ -695,7 +732,7 @@ class ThemeCarouselThemeItemNode: ListViewItemNode, ItemListItemNode { }) } - func asyncLayout() -> (_ item: ThemeCarouselThemeItem, _ params: ListViewItemLayoutParams, _ neighbors: ItemListNeighbors) -> (ListViewItemNodeLayout, () -> Void) { + public func asyncLayout() -> (_ item: ThemeCarouselThemeItem, _ params: ListViewItemLayoutParams, _ neighbors: ItemListNeighbors) -> (ListViewItemNodeLayout, () -> Void) { return { item, params, neighbors in let contentSize: CGSize let insets: UIEdgeInsets @@ -728,20 +765,26 @@ class ThemeCarouselThemeItemNode: ListViewItemNode, ItemListItemNode { if strongSelf.maskNode.supernode == nil { strongSelf.containerNode.insertSubnode(strongSelf.maskNode, at: 3) } - - let hasCorners = itemListHasRoundedBlockLayout(params) - var hasTopCorners = false - var hasBottomCorners = false - switch neighbors.top { + + if params.isStandalone { + strongSelf.topStripeNode.isHidden = true + strongSelf.bottomStripeNode.isHidden = true + strongSelf.maskNode.isHidden = true + strongSelf.backgroundNode.isHidden = true + } else { + let hasCorners = itemListHasRoundedBlockLayout(params) + var hasTopCorners = false + var hasBottomCorners = false + switch neighbors.top { case .sameSection(false): strongSelf.topStripeNode.isHidden = true default: hasTopCorners = true strongSelf.topStripeNode.isHidden = hasCorners - } - let bottomStripeInset: CGFloat - let bottomStripeOffset: CGFloat - switch neighbors.bottom { + } + let bottomStripeInset: CGFloat + let bottomStripeOffset: CGFloat + switch neighbors.bottom { case .sameSection(false): bottomStripeInset = params.leftInset + 16.0 bottomStripeOffset = -separatorHeight @@ -751,17 +794,20 @@ class ThemeCarouselThemeItemNode: ListViewItemNode, ItemListItemNode { bottomStripeOffset = 0.0 hasBottomCorners = true strongSelf.bottomStripeNode.isHidden = hasCorners + } + + strongSelf.bottomStripeNode.isHidden = true + + strongSelf.maskNode.image = hasCorners ? PresentationResourcesItemList.cornersImage(item.theme, top: hasTopCorners, bottom: hasBottomCorners) : nil + + strongSelf.topStripeNode.frame = CGRect(origin: CGPoint(x: 0.0, y: -min(insets.top, separatorHeight)), size: CGSize(width: layoutSize.width, height: separatorHeight)) + strongSelf.bottomStripeNode.frame = CGRect(origin: CGPoint(x: bottomStripeInset, y: contentSize.height + bottomStripeOffset), size: CGSize(width: layoutSize.width - bottomStripeInset, height: separatorHeight)) } - - strongSelf.bottomStripeNode.isHidden = true strongSelf.containerNode.frame = CGRect(x: 0.0, y: 0.0, width: contentSize.width, height: contentSize.height) - strongSelf.maskNode.image = hasCorners ? PresentationResourcesItemList.cornersImage(item.theme, top: hasTopCorners, bottom: hasBottomCorners) : nil strongSelf.backgroundNode.frame = CGRect(origin: CGPoint(x: 0.0, y: -min(insets.top, separatorHeight)), size: CGSize(width: params.width, height: contentSize.height + min(insets.top, separatorHeight) + min(insets.bottom, separatorHeight))) strongSelf.maskNode.frame = strongSelf.backgroundNode.frame.insetBy(dx: params.leftInset, dy: 0.0) - strongSelf.topStripeNode.frame = CGRect(origin: CGPoint(x: 0.0, y: -min(insets.top, separatorHeight)), size: CGSize(width: layoutSize.width, height: separatorHeight)) - strongSelf.bottomStripeNode.frame = CGRect(origin: CGPoint(x: bottomStripeInset, y: contentSize.height + bottomStripeOffset), size: CGSize(width: layoutSize.width - bottomStripeInset, height: separatorHeight)) var listInsets = UIEdgeInsets() listInsets.top += params.leftInset + 12.0 @@ -810,15 +856,15 @@ class ThemeCarouselThemeItemNode: ListViewItemNode, ItemListItemNode { } } - override func animateInsertion(_ currentTimestamp: Double, duration: Double, short: Bool) { + override public func animateInsertion(_ currentTimestamp: Double, duration: Double, short: Bool) { self.layer.animateAlpha(from: 0.0, to: 1.0, duration: 0.4) } - override func animateRemoved(_ currentTimestamp: Double, duration: Double) { + override public func animateRemoved(_ currentTimestamp: Double, duration: Double) { self.layer.animateAlpha(from: 1.0, to: 0.0, duration: 0.15, removeOnCompletion: false) } - func prepareCrossfadeTransition() { + public func prepareCrossfadeTransition() { guard self.snapshotView == nil else { return } @@ -836,7 +882,7 @@ class ThemeCarouselThemeItemNode: ListViewItemNode, ItemListItemNode { } } - func animateCrossfadeTransition() { + public func animateCrossfadeTransition() { guard self.snapshotView?.layer.animationKeys()?.isEmpty ?? true else { return } diff --git a/submodules/TelegramUI/Components/Settings/ThemeSettingsThemeItem/BUILD b/submodules/TelegramUI/Components/Settings/ThemeSettingsThemeItem/BUILD new file mode 100644 index 0000000000..f0350af4f8 --- /dev/null +++ b/submodules/TelegramUI/Components/Settings/ThemeSettingsThemeItem/BUILD @@ -0,0 +1,33 @@ +load("@build_bazel_rules_swift//swift:swift.bzl", "swift_library") + +swift_library( + name = "ThemeSettingsThemeItem", + module_name = "ThemeSettingsThemeItem", + srcs = glob([ + "Sources/**/*.swift", + ]), + copts = [ + "-warnings-as-errors", + ], + deps = [ + "//submodules/Display", + "//submodules/AsyncDisplayKit", + "//submodules/SSignalKit/SwiftSignalKit", + "//submodules/Postbox", + "//submodules/TelegramCore", + "//submodules/TelegramPresentationData", + "//submodules/MergeLists", + "//submodules/TelegramUIPreferences", + "//submodules/ItemListUI", + "//submodules/PresentationDataUtils", + "//submodules/WallpaperResources", + "//submodules/AccountContext", + "//submodules/AppBundle", + "//submodules/ContextUI", + "//submodules/HexColor", + "//submodules/TelegramUI/Components/ListItemComponentAdaptor", + ], + visibility = [ + "//visibility:public", + ], +) diff --git a/submodules/SettingsUI/Sources/Themes/ThemeSettingsThemeItem.swift b/submodules/TelegramUI/Components/Settings/ThemeSettingsThemeItem/Sources/ThemeSettingsThemeItem.swift similarity index 85% rename from submodules/SettingsUI/Sources/Themes/ThemeSettingsThemeItem.swift rename to submodules/TelegramUI/Components/Settings/ThemeSettingsThemeItem/Sources/ThemeSettingsThemeItem.swift index 6fa79773ef..64b0b365d3 100644 --- a/submodules/SettingsUI/Sources/Themes/ThemeSettingsThemeItem.swift +++ b/submodules/TelegramUI/Components/Settings/ThemeSettingsThemeItem/Sources/ThemeSettingsThemeItem.swift @@ -14,6 +14,8 @@ import WallpaperResources import AccountContext import AppBundle import ContextUI +import ListItemComponentAdaptor +import HexColor private struct ThemeSettingsThemeEntry: Comparable, Identifiable { let index: Int @@ -369,24 +371,24 @@ private final class ThemeSettingsThemeItemIconNode : ListViewItemNode { } } -class ThemeSettingsThemeItem: ListViewItem, ItemListItem { - var sectionId: ItemListSectionId +public class ThemeSettingsThemeItem: ListViewItem, ItemListItem, ListItemComponentAdaptor.ItemGenerator { + public var sectionId: ItemListSectionId - let context: AccountContext - let theme: PresentationTheme - let strings: PresentationStrings - let themes: [PresentationThemeReference] - let allThemes: [PresentationThemeReference] - let displayUnsupported: Bool - let themeSpecificAccentColors: [Int64: PresentationThemeAccentColor] - let themeSpecificChatWallpapers: [Int64: TelegramWallpaper] - let themePreferredBaseTheme: [Int64: TelegramBaseTheme] - let currentTheme: PresentationThemeReference - let updatedTheme: (PresentationThemeReference) -> Void - let contextAction: ((PresentationThemeReference, ASDisplayNode, ContextGesture?) -> Void)? - let tag: ItemListItemTag? + public let context: AccountContext + public let theme: PresentationTheme + public let strings: PresentationStrings + public let themes: [PresentationThemeReference] + public let allThemes: [PresentationThemeReference] + public let displayUnsupported: Bool + public let themeSpecificAccentColors: [Int64: PresentationThemeAccentColor] + public let themeSpecificChatWallpapers: [Int64: TelegramWallpaper] + public let themePreferredBaseTheme: [Int64: TelegramBaseTheme] + public let currentTheme: PresentationThemeReference + public let updatedTheme: (PresentationThemeReference) -> Void + public let contextAction: ((PresentationThemeReference, ASDisplayNode, ContextGesture?) -> Void)? + public let tag: ItemListItemTag? - init(context: AccountContext, theme: PresentationTheme, strings: PresentationStrings, sectionId: ItemListSectionId, themes: [PresentationThemeReference], allThemes: [PresentationThemeReference], displayUnsupported: Bool, themeSpecificAccentColors: [Int64: PresentationThemeAccentColor], themeSpecificChatWallpapers: [Int64: TelegramWallpaper], themePreferredBaseTheme: [Int64: TelegramBaseTheme], currentTheme: PresentationThemeReference, updatedTheme: @escaping (PresentationThemeReference) -> Void, contextAction: ((PresentationThemeReference, ASDisplayNode, ContextGesture?) -> Void)?, tag: ItemListItemTag? = nil) { + public init(context: AccountContext, theme: PresentationTheme, strings: PresentationStrings, sectionId: ItemListSectionId, themes: [PresentationThemeReference], allThemes: [PresentationThemeReference], displayUnsupported: Bool, themeSpecificAccentColors: [Int64: PresentationThemeAccentColor], themeSpecificChatWallpapers: [Int64: TelegramWallpaper], themePreferredBaseTheme: [Int64: TelegramBaseTheme], currentTheme: PresentationThemeReference, updatedTheme: @escaping (PresentationThemeReference) -> Void, contextAction: ((PresentationThemeReference, ASDisplayNode, ContextGesture?) -> Void)?, tag: ItemListItemTag? = nil) { self.context = context self.theme = theme self.strings = strings @@ -403,7 +405,7 @@ class ThemeSettingsThemeItem: ListViewItem, ItemListItem { self.sectionId = sectionId } - func nodeConfiguredForParams(async: @escaping (@escaping () -> Void) -> Void, params: ListViewItemLayoutParams, synchronousLoads: Bool, previousItem: ListViewItem?, nextItem: ListViewItem?, completion: @escaping (ListViewItemNode, @escaping () -> (Signal?, (ListViewItemApply) -> Void)) -> Void) { + public func nodeConfiguredForParams(async: @escaping (@escaping () -> Void) -> Void, params: ListViewItemLayoutParams, synchronousLoads: Bool, previousItem: ListViewItem?, nextItem: ListViewItem?, completion: @escaping (ListViewItemNode, @escaping () -> (Signal?, (ListViewItemApply) -> Void)) -> Void) { async { let node = ThemeSettingsThemeItemNode() let (layout, apply) = node.asyncLayout()(self, params, itemListNeighbors(item: self, topItem: previousItem as? ItemListItem, bottomItem: nextItem as? ItemListItem)) @@ -419,7 +421,7 @@ class ThemeSettingsThemeItem: ListViewItem, ItemListItem { } } - func updateNode(async: @escaping (@escaping () -> Void) -> Void, node: @escaping () -> ListViewItemNode, params: ListViewItemLayoutParams, previousItem: ListViewItem?, nextItem: ListViewItem?, animation: ListViewItemUpdateAnimation, completion: @escaping (ListViewItemNodeLayout, @escaping (ListViewItemApply) -> Void) -> Void) { + public func updateNode(async: @escaping (@escaping () -> Void) -> Void, node: @escaping () -> ListViewItemNode, params: ListViewItemLayoutParams, previousItem: ListViewItem?, nextItem: ListViewItem?, animation: ListViewItemUpdateAnimation, completion: @escaping (ListViewItemNodeLayout, @escaping (ListViewItemApply) -> Void) -> Void) { Queue.mainQueue().async { if let nodeValue = node() as? ThemeSettingsThemeItemNode { let makeLayout = nodeValue.asyncLayout() @@ -435,6 +437,42 @@ class ThemeSettingsThemeItem: ListViewItem, ItemListItem { } } } + + public func item() -> ListViewItem { + return self + } + + public static func ==(lhs: ThemeSettingsThemeItem, rhs: ThemeSettingsThemeItem) -> Bool { + if lhs.context !== rhs.context { + return false + } + if lhs.theme !== rhs.theme { + return false + } + if lhs.strings !== rhs.strings { + return false + } + if lhs.themes != rhs.themes { + return false + } + if lhs.displayUnsupported != rhs.displayUnsupported { + return false + } + if lhs.themeSpecificAccentColors != rhs.themeSpecificAccentColors { + return false + } + if lhs.themeSpecificChatWallpapers != rhs.themeSpecificChatWallpapers { + return false + } + if lhs.themePreferredBaseTheme != rhs.themePreferredBaseTheme { + return false + } + if lhs.currentTheme != rhs.currentTheme { + return false + } + + return true + } } private struct ThemeSettingsThemeItemNodeTransition { @@ -472,7 +510,7 @@ private func ensureThemeVisible(listNode: ListView, themeReference: Presentation } } -class ThemeSettingsThemeItemNode: ListViewItemNode, ItemListItemNode { +public class ThemeSettingsThemeItemNode: ListViewItemNode, ItemListItemNode { private let containerNode: ASDisplayNode private let backgroundNode: ASDisplayNode private let topStripeNode: ASDisplayNode @@ -488,13 +526,13 @@ class ThemeSettingsThemeItemNode: ListViewItemNode, ItemListItemNode { private var item: ThemeSettingsThemeItem? private var layoutParams: ListViewItemLayoutParams? - var tag: ItemListItemTag? { + public var tag: ItemListItemTag? { return self.item?.tag } private var tapping = false - init() { + public init() { self.containerNode = ASDisplayNode() self.backgroundNode = ASDisplayNode() @@ -517,7 +555,7 @@ class ThemeSettingsThemeItemNode: ListViewItemNode, ItemListItemNode { self.addSubnode(self.listNode) } - override func didLoad() { + override public func didLoad() { super.didLoad() self.listNode.view.disablesInteractiveTransitionGestureRecognizer = true } @@ -558,7 +596,7 @@ class ThemeSettingsThemeItemNode: ListViewItemNode, ItemListItemNode { }) } - func asyncLayout() -> (_ item: ThemeSettingsThemeItem, _ params: ListViewItemLayoutParams, _ neighbors: ItemListNeighbors) -> (ListViewItemNodeLayout, () -> Void) { + public func asyncLayout() -> (_ item: ThemeSettingsThemeItem, _ params: ListViewItemLayoutParams, _ neighbors: ItemListNeighbors) -> (ListViewItemNodeLayout, () -> Void) { return { item, params, neighbors in let contentSize: CGSize let insets: UIEdgeInsets @@ -591,20 +629,26 @@ class ThemeSettingsThemeItemNode: ListViewItemNode, ItemListItemNode { if strongSelf.maskNode.supernode == nil { strongSelf.containerNode.insertSubnode(strongSelf.maskNode, at: 3) } - - let hasCorners = itemListHasRoundedBlockLayout(params) - var hasTopCorners = false - var hasBottomCorners = false - switch neighbors.top { + + if params.isStandalone { + strongSelf.topStripeNode.isHidden = true + strongSelf.bottomStripeNode.isHidden = true + strongSelf.maskNode.isHidden = true + strongSelf.backgroundNode.isHidden = true + } else { + let hasCorners = itemListHasRoundedBlockLayout(params) + var hasTopCorners = false + var hasBottomCorners = false + switch neighbors.top { case .sameSection(false): strongSelf.topStripeNode.isHidden = true default: hasTopCorners = true strongSelf.topStripeNode.isHidden = hasCorners - } - let bottomStripeInset: CGFloat - let bottomStripeOffset: CGFloat - switch neighbors.bottom { + } + let bottomStripeInset: CGFloat + let bottomStripeOffset: CGFloat + switch neighbors.bottom { case .sameSection(false): bottomStripeInset = params.leftInset + 16.0 bottomStripeOffset = -separatorHeight @@ -614,15 +658,18 @@ class ThemeSettingsThemeItemNode: ListViewItemNode, ItemListItemNode { bottomStripeOffset = 0.0 hasBottomCorners = true strongSelf.bottomStripeNode.isHidden = hasCorners + } + + strongSelf.maskNode.image = hasCorners ? PresentationResourcesItemList.cornersImage(item.theme, top: hasTopCorners, bottom: hasBottomCorners) : nil + + strongSelf.topStripeNode.frame = CGRect(origin: CGPoint(x: 0.0, y: -min(insets.top, separatorHeight)), size: CGSize(width: layoutSize.width, height: separatorHeight)) + strongSelf.bottomStripeNode.frame = CGRect(origin: CGPoint(x: bottomStripeInset, y: contentSize.height + bottomStripeOffset), size: CGSize(width: layoutSize.width - bottomStripeInset, height: separatorHeight)) } strongSelf.containerNode.frame = CGRect(x: 0.0, y: 0.0, width: contentSize.width, height: contentSize.height) - strongSelf.maskNode.image = hasCorners ? PresentationResourcesItemList.cornersImage(item.theme, top: hasTopCorners, bottom: hasBottomCorners) : nil strongSelf.backgroundNode.frame = CGRect(origin: CGPoint(x: 0.0, y: -min(insets.top, separatorHeight)), size: CGSize(width: params.width, height: contentSize.height + min(insets.top, separatorHeight) + min(insets.bottom, separatorHeight))) strongSelf.maskNode.frame = strongSelf.backgroundNode.frame.insetBy(dx: params.leftInset, dy: 0.0) - strongSelf.topStripeNode.frame = CGRect(origin: CGPoint(x: 0.0, y: -min(insets.top, separatorHeight)), size: CGSize(width: layoutSize.width, height: separatorHeight)) - strongSelf.bottomStripeNode.frame = CGRect(origin: CGPoint(x: bottomStripeInset, y: contentSize.height + bottomStripeOffset), size: CGSize(width: layoutSize.width - bottomStripeInset, height: separatorHeight)) var listInsets = UIEdgeInsets() listInsets.top += params.leftInset + 4.0 @@ -694,15 +741,15 @@ class ThemeSettingsThemeItemNode: ListViewItemNode, ItemListItemNode { } } - override func animateInsertion(_ currentTimestamp: Double, duration: Double, short: Bool) { + override public func animateInsertion(_ currentTimestamp: Double, duration: Double, short: Bool) { self.layer.animateAlpha(from: 0.0, to: 1.0, duration: 0.4) } - override func animateRemoved(_ currentTimestamp: Double, duration: Double) { + override public func animateRemoved(_ currentTimestamp: Double, duration: Double) { self.layer.animateAlpha(from: 1.0, to: 0.0, duration: 0.15, removeOnCompletion: false) } - func prepareCrossfadeTransition() { + public func prepareCrossfadeTransition() { guard self.snapshotView == nil else { return } @@ -719,7 +766,7 @@ class ThemeSettingsThemeItemNode: ListViewItemNode, ItemListItemNode { } } - func animateCrossfadeTransition() { + public func animateCrossfadeTransition() { guard self.snapshotView?.layer.animationKeys()?.isEmpty ?? true else { return } diff --git a/submodules/TelegramUI/Sources/SharedAccountContext.swift b/submodules/TelegramUI/Sources/SharedAccountContext.swift index 7784c9f693..d626bdb977 100644 --- a/submodules/TelegramUI/Sources/SharedAccountContext.swift +++ b/submodules/TelegramUI/Sources/SharedAccountContext.swift @@ -363,6 +363,18 @@ public final class SharedAccountContextImpl: SharedAccountContext { } if themeUpdated { updateLegacyTheme() + + /*if #available(iOS 13.0, *) { + let userInterfaceStyle: UIUserInterfaceStyle + if strongSelf.currentPresentationData.with({ $0 }).theme.overallDarkAppearance { + userInterfaceStyle = .dark + } else { + userInterfaceStyle = .light + } + if let eventView = strongSelf.mainWindow?.hostView.eventView, eventView.overrideUserInterfaceStyle != userInterfaceStyle { + eventView.overrideUserInterfaceStyle = userInterfaceStyle + } + }*/ } if themeNameUpdated { strongSelf.presentCrossfadeController() @@ -962,6 +974,18 @@ public final class SharedAccountContextImpl: SharedAccountContext { } }) } + + /*if #available(iOS 13.0, *) { + let userInterfaceStyle: UIUserInterfaceStyle + if self.currentPresentationData.with({ $0 }).theme.overallDarkAppearance { + userInterfaceStyle = .dark + } else { + userInterfaceStyle = .light + } + if let eventView = self.mainWindow?.hostView.eventView, eventView.overrideUserInterfaceStyle != userInterfaceStyle { + eventView.overrideUserInterfaceStyle = userInterfaceStyle + } + }*/ } deinit { From 09e025e98596d8d74081820a2e31c3936bc602bf Mon Sep 17 00:00:00 2001 From: Isaac <> Date: Fri, 15 Dec 2023 22:59:33 +0400 Subject: [PATCH 5/5] [WIP] Channel appearance --- .../Sources/Node/ChatListItem.swift | 4 +- .../ReactionListContextMenuContent.swift | 2 +- .../Sources/ViewControllerComponent.swift | 14 +- .../Sources/ContactsPeerItem.swift | 2 +- .../Sources/ItemListPeerItem.swift | 2 +- .../Sources/PremiumGiftCodeScreen.swift | 10 +- .../TextSizeSelectionController.swift | 4 +- .../Sources/ThemePickerGridItem.swift | 2 +- .../ThemeAccentColorControllerNode.swift | 2 +- .../Themes/ThemePreviewControllerNode.swift | 4 +- submodules/TelegramApi/Sources/Api0.swift | 13 +- submodules/TelegramApi/Sources/Api12.swift | 64 ++- submodules/TelegramApi/Sources/Api2.swift | 28 ++ submodules/TelegramApi/Sources/Api22.swift | 104 +++++ submodules/TelegramApi/Sources/Api29.swift | 14 +- submodules/TelegramApi/Sources/Api3.swift | 24 +- submodules/TelegramApi/Sources/Api30.swift | 34 +- submodules/TelegramApi/Sources/Api32.swift | 45 +- .../Sources/VoiceChatParticipantItem.swift | 2 +- .../Account/AccountIntermediateState.swift | 2 +- .../Sources/ApiUtils/ApiGroupOrChannel.swift | 14 +- .../ApiUtils/StoreMessage_Telegram.swift | 4 +- .../Sources/State/UpdatesApiUtils.swift | 2 +- .../SyncCore_StickerPackCollectionInfo.swift | 4 + .../SyncCore/SyncCore_TelegramChannel.swift | 39 +- .../TelegramEngine/Calls/GroupCalls.swift | 2 +- .../TelegramEngine/Messages/AdMessages.swift | 6 +- .../Messages/EngineStoryViewListContext.swift | 4 +- .../Messages/SearchMessages.swift | 8 +- .../TelegramEngine/Messages/SendAsPeers.swift | 2 +- .../Messages/StoryListContext.swift | 7 + .../TelegramEngine/Payments/GiftCodes.swift | 6 +- .../TelegramEngine/Peers/AddressNames.swift | 2 +- .../Peers/ChannelAdminEventLogs.swift | 4 + .../Peers/ChannelRecommendation.swift | 2 +- .../TelegramEngine/Peers/Communities.swift | 6 +- .../Peers/InactiveChannels.swift | 2 +- .../TelegramEngine/Peers/SearchPeers.swift | 2 +- .../Peers/TelegramEnginePeers.swift | 4 + .../TelegramEngine/Peers/UpdatePeerInfo.swift | 46 ++- .../UpdatedAccountPrivacySettings.swift | 2 +- .../TelegramEngine/Stickers/StickerPack.swift | 3 + .../TelegramCore/Sources/UpdatePeers.swift | 2 +- .../Sources/ChatMessageBubbleItemNode.swift | 5 +- .../ChatRecentActionsHistoryTransition.swift | 2 +- .../MessageInlineBlockBackgroundView.swift | 5 +- .../ChatTitleView/Sources/ChatTitleView.swift | 2 +- .../EmojiStatusSelectionComponent.swift | 52 ++- .../Sources/EmojiPagerContentComponent.swift | 184 ++++++++- .../NotificationExceptionsScreen.swift | 2 +- .../Sources/PeerInfoHeaderNode.swift | 2 +- .../Sources/PeerInfoScreen.swift | 14 +- .../Settings/PeerNameColorScreen/BUILD | 1 + .../Sources/ChannelAppearanceScreen.swift | 386 +++++++++++------- .../PeerNameColorChatPreviewItem.swift | 2 +- .../Sources/ThemeCarouselItem.swift | 29 +- .../Sources/PeerListItemComponent.swift | 2 +- .../ChatReportPeerTitlePanelNode.swift | 5 + .../Sources/WallpaperResources.swift | 7 +- 59 files changed, 973 insertions(+), 276 deletions(-) diff --git a/submodules/ChatListUI/Sources/Node/ChatListItem.swift b/submodules/ChatListUI/Sources/Node/ChatListItem.swift index 1aacf0f996..7cac497f41 100644 --- a/submodules/ChatListUI/Sources/Node/ChatListItem.swift +++ b/submodules/ChatListUI/Sources/Node/ChatListItem.swift @@ -2477,7 +2477,7 @@ class ChatListItemNode: ItemListRevealOptionsItemNode { currentCredibilityIconContent = .text(color: item.presentationData.theme.chat.message.incoming.scamColor, string: item.presentationData.strings.Message_ScamAccount.uppercased()) } else if peer.isFake { currentCredibilityIconContent = .text(color: item.presentationData.theme.chat.message.incoming.scamColor, string: item.presentationData.strings.Message_FakeAccount.uppercased()) - } else if case let .user(user) = peer, let emojiStatus = user.emojiStatus, !premiumConfiguration.isPremiumDisabled { + } else if let emojiStatus = peer.emojiStatus, !premiumConfiguration.isPremiumDisabled { 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) @@ -2493,7 +2493,7 @@ class ChatListItemNode: ItemListRevealOptionsItemNode { currentCredibilityIconContent = .text(color: item.presentationData.theme.chat.message.incoming.scamColor, string: item.presentationData.strings.Message_ScamAccount.uppercased()) } else if peer.isFake { currentCredibilityIconContent = .text(color: item.presentationData.theme.chat.message.incoming.scamColor, string: item.presentationData.strings.Message_FakeAccount.uppercased()) - } else if case let .user(user) = peer, let emojiStatus = user.emojiStatus, !premiumConfiguration.isPremiumDisabled { + } else if let emojiStatus = peer.emojiStatus, !premiumConfiguration.isPremiumDisabled { 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) diff --git a/submodules/Components/ReactionListContextMenuContent/Sources/ReactionListContextMenuContent.swift b/submodules/Components/ReactionListContextMenuContent/Sources/ReactionListContextMenuContent.swift index 6c1e332766..1b70a3a8b6 100644 --- a/submodules/Components/ReactionListContextMenuContent/Sources/ReactionListContextMenuContent.swift +++ b/submodules/Components/ReactionListContextMenuContent/Sources/ReactionListContextMenuContent.swift @@ -609,7 +609,7 @@ public final class ReactionListContextMenuContent: ContextControllerItemsContent currentCredibilityIcon = .text(color: presentationData.theme.chat.message.incoming.scamColor, string: presentationData.strings.Message_ScamAccount.uppercased()) } else if item.peer.isFake { currentCredibilityIcon = .text(color: presentationData.theme.chat.message.incoming.scamColor, string: presentationData.strings.Message_FakeAccount.uppercased()) - } else if case let .user(user) = item.peer, let emojiStatus = user.emojiStatus { + } else if let emojiStatus = item.peer.emojiStatus { currentCredibilityIcon = .animation(content: .customEmoji(fileId: emojiStatus.fileId), size: CGSize(width: 32.0, height: 32.0), placeholderColor: UIColor(white: 0.0, alpha: 0.1), themeColor: presentationData.theme.list.itemAccentColor, loopMode: .count(2)) } else if item.peer.isVerified { currentCredibilityIcon = .verified(fillColor: presentationData.theme.list.itemCheckColors.fillColor, foregroundColor: presentationData.theme.list.itemCheckColors.foregroundColor, sizeType: .compact) diff --git a/submodules/Components/ViewControllerComponent/Sources/ViewControllerComponent.swift b/submodules/Components/ViewControllerComponent/Sources/ViewControllerComponent.swift index 0a5f0ae820..aedc76242e 100644 --- a/submodules/Components/ViewControllerComponent/Sources/ViewControllerComponent.swift +++ b/submodules/Components/ViewControllerComponent/Sources/ViewControllerComponent.swift @@ -244,7 +244,6 @@ open class ViewControllerComponentContainer: ViewController { } else { presentationData = self.context.sharedContext.currentPresentationData.with { $0 } } - let navigationBarPresentationData: NavigationBarPresentationData? switch navigationBarAppearance { @@ -280,6 +279,19 @@ open class ViewControllerComponentContainer: ViewController { strongSelf.statusBar.statusBarStyle = presentationData.theme.rootController.statusBarStyle.style } + let navigationBarPresentationData: NavigationBarPresentationData? + switch navigationBarAppearance { + case .none: + navigationBarPresentationData = nil + case .transparent: + navigationBarPresentationData = NavigationBarPresentationData(presentationData: presentationData, hideBackground: true, hideBadge: false, hideSeparator: true) + case .default: + navigationBarPresentationData = NavigationBarPresentationData(presentationData: presentationData) + } + if let navigationBarPresentationData { + strongSelf.navigationBar?.updatePresentationData(navigationBarPresentationData) + } + if let layout = strongSelf.validLayout { strongSelf.containerLayoutUpdated(layout, transition: .immediate) } diff --git a/submodules/ContactsPeerItem/Sources/ContactsPeerItem.swift b/submodules/ContactsPeerItem/Sources/ContactsPeerItem.swift index 54165ebd7d..4361932dd8 100644 --- a/submodules/ContactsPeerItem/Sources/ContactsPeerItem.swift +++ b/submodules/ContactsPeerItem/Sources/ContactsPeerItem.swift @@ -699,7 +699,7 @@ public class ContactsPeerItemNode: ItemListRevealOptionsItemNode { credibilityIcon = .text(color: item.presentationData.theme.chat.message.incoming.scamColor, string: item.presentationData.strings.Message_ScamAccount.uppercased()) } else if peer.isFake { credibilityIcon = .text(color: item.presentationData.theme.chat.message.incoming.scamColor, string: item.presentationData.strings.Message_FakeAccount.uppercased()) - } else if case let .user(user) = peer, let emojiStatus = user.emojiStatus { + } else if let emojiStatus = 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 peer.isVerified { credibilityIcon = .verified(fillColor: item.presentationData.theme.list.itemCheckColors.fillColor, foregroundColor: item.presentationData.theme.list.itemCheckColors.foregroundColor, sizeType: .compact) diff --git a/submodules/ItemListPeerItem/Sources/ItemListPeerItem.swift b/submodules/ItemListPeerItem/Sources/ItemListPeerItem.swift index 093c11e33e..7a17a43cf3 100644 --- a/submodules/ItemListPeerItem/Sources/ItemListPeerItem.swift +++ b/submodules/ItemListPeerItem/Sources/ItemListPeerItem.swift @@ -913,7 +913,7 @@ public class ItemListPeerItemNode: ItemListRevealOptionsItemNode, ItemListItemNo credibilityIcon = .text(color: item.presentationData.theme.chat.message.incoming.scamColor, string: item.presentationData.strings.Message_ScamAccount.uppercased()) } else if item.peer.isFake { credibilityIcon = .text(color: item.presentationData.theme.chat.message.incoming.scamColor, string: item.presentationData.strings.Message_FakeAccount.uppercased()) - } else if case let .user(user) = item.peer, let emojiStatus = user.emojiStatus { + } 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) diff --git a/submodules/PremiumUI/Sources/PremiumGiftCodeScreen.swift b/submodules/PremiumUI/Sources/PremiumGiftCodeScreen.swift index 36236cfefe..c35df0ffa4 100644 --- a/submodules/PremiumUI/Sources/PremiumGiftCodeScreen.swift +++ b/submodules/PremiumUI/Sources/PremiumGiftCodeScreen.swift @@ -86,7 +86,9 @@ private final class PremiumGiftCodeSheetContent: CombinedComponent { var peerIds: [EnginePeer.Id] = [] switch subject { case let .giftCode(giftCode): - peerIds.append(giftCode.fromPeerId) + if let fromPeerId = giftCode.fromPeerId { + peerIds.append(fromPeerId) + } if let toPeerId = giftCode.toPeerId { peerIds.append(toPeerId) } @@ -201,7 +203,11 @@ private final class PremiumGiftCodeSheetContent: CombinedComponent { } link = "https://t.me/giftcode/\(giftCode.slug)" date = giftCode.date - fromPeer = state.peerMap[giftCode.fromPeerId] + if let fromPeerId = giftCode.fromPeerId { + fromPeer = state.peerMap[fromPeerId] + } else { + fromPeer = nil + } toPeerId = giftCode.toPeerId if let toPeerId = giftCode.toPeerId { toPeer = state.peerMap[toPeerId] diff --git a/submodules/SettingsUI/Sources/Text Size/TextSizeSelectionController.swift b/submodules/SettingsUI/Sources/Text Size/TextSizeSelectionController.swift index 0ff9343614..a0591b67c4 100644 --- a/submodules/SettingsUI/Sources/Text Size/TextSizeSelectionController.swift +++ b/submodules/SettingsUI/Sources/Text Size/TextSizeSelectionController.swift @@ -307,10 +307,10 @@ private final class TextSizeSelectionControllerNode: ASDisplayNode, UIScrollView let selfPeer: EnginePeer = .user(TelegramUser(id: self.context.account.peerId, accessHash: nil, firstName: nil, lastName: nil, username: nil, phone: nil, photo: [], botInfo: nil, restrictionInfo: nil, flags: [], emojiStatus: nil, usernames: [], storiesHidden: nil, nameColor: nil, backgroundEmojiId: nil, profileColor: nil, profileBackgroundEmojiId: nil)) let peer1: EnginePeer = .user(TelegramUser(id: PeerId(namespace: Namespaces.Peer.CloudUser, id: PeerId.Id._internalFromInt64Value(1)), accessHash: nil, firstName: self.presentationData.strings.Appearance_ThemePreview_ChatList_1_Name, lastName: nil, username: nil, phone: nil, photo: [], botInfo: nil, restrictionInfo: nil, flags: [], emojiStatus: nil, usernames: [], storiesHidden: nil, nameColor: nil, backgroundEmojiId: nil, profileColor: nil, profileBackgroundEmojiId: nil)) let peer2: EnginePeer = .user(TelegramUser(id: PeerId(namespace: Namespaces.Peer.CloudUser, id: PeerId.Id._internalFromInt64Value(2)), accessHash: nil, firstName: self.presentationData.strings.Appearance_ThemePreview_ChatList_2_Name, lastName: nil, username: nil, phone: nil, photo: [], botInfo: nil, restrictionInfo: nil, flags: [], emojiStatus: nil, usernames: [], storiesHidden: nil, nameColor: nil, backgroundEmojiId: nil, profileColor: nil, profileBackgroundEmojiId: nil)) - let peer3: EnginePeer = .channel(TelegramChannel(id: PeerId(namespace: Namespaces.Peer.CloudChannel, id: PeerId.Id._internalFromInt64Value(3)), accessHash: nil, title: self.presentationData.strings.Appearance_ThemePreview_ChatList_3_Name, username: nil, photo: [], creationDate: 0, version: 0, participationStatus: .member, info: .group(.init(flags: [])), flags: [], restrictionInfo: nil, adminRights: nil, bannedRights: nil, defaultBannedRights: nil, usernames: [], storiesHidden: nil, nameColor: nil, backgroundEmojiId: nil, profileColor: nil, profileBackgroundEmojiId: nil, emojiStatus: nil)) + let peer3: EnginePeer = .channel(TelegramChannel(id: PeerId(namespace: Namespaces.Peer.CloudChannel, id: PeerId.Id._internalFromInt64Value(3)), accessHash: nil, title: self.presentationData.strings.Appearance_ThemePreview_ChatList_3_Name, username: nil, photo: [], creationDate: 0, version: 0, participationStatus: .member, info: .group(.init(flags: [])), flags: [], restrictionInfo: nil, adminRights: nil, bannedRights: nil, defaultBannedRights: nil, usernames: [], storiesHidden: nil, nameColor: nil, backgroundEmojiId: nil, profileColor: nil, profileBackgroundEmojiId: nil, emojiStatus: nil, approximateBoostLevel: nil)) let peer3Author: EnginePeer = .user(TelegramUser(id: PeerId(namespace: Namespaces.Peer.CloudUser, id: PeerId.Id._internalFromInt64Value(4)), accessHash: nil, firstName: self.presentationData.strings.Appearance_ThemePreview_ChatList_3_AuthorName, lastName: nil, username: nil, phone: nil, photo: [], botInfo: nil, restrictionInfo: nil, flags: [], emojiStatus: nil, usernames: [], storiesHidden: nil, nameColor: nil, backgroundEmojiId: nil, profileColor: nil, profileBackgroundEmojiId: nil)) let peer4: EnginePeer = .user(TelegramUser(id: PeerId(namespace: Namespaces.Peer.CloudUser, id: PeerId.Id._internalFromInt64Value(4)), accessHash: nil, firstName: self.presentationData.strings.Appearance_ThemePreview_ChatList_4_Name, lastName: nil, username: nil, phone: nil, photo: [], botInfo: nil, restrictionInfo: nil, flags: [], emojiStatus: nil, usernames: [], storiesHidden: nil, nameColor: nil, backgroundEmojiId: nil, profileColor: nil, profileBackgroundEmojiId: nil)) - let peer5: EnginePeer = .channel(TelegramChannel(id: PeerId(namespace: Namespaces.Peer.CloudChannel, id: PeerId.Id._internalFromInt64Value(5)), accessHash: nil, title: self.presentationData.strings.Appearance_ThemePreview_ChatList_5_Name, username: nil, photo: [], creationDate: 0, version: 0, participationStatus: .member, info: .broadcast(.init(flags: [])), flags: [], restrictionInfo: nil, adminRights: nil, bannedRights: nil, defaultBannedRights: nil, usernames: [], storiesHidden: nil, nameColor: nil, backgroundEmojiId: nil, profileColor: nil, profileBackgroundEmojiId: nil, emojiStatus: nil)) + let peer5: EnginePeer = .channel(TelegramChannel(id: PeerId(namespace: Namespaces.Peer.CloudChannel, id: PeerId.Id._internalFromInt64Value(5)), accessHash: nil, title: self.presentationData.strings.Appearance_ThemePreview_ChatList_5_Name, username: nil, photo: [], creationDate: 0, version: 0, participationStatus: .member, info: .broadcast(.init(flags: [])), flags: [], restrictionInfo: nil, adminRights: nil, bannedRights: nil, defaultBannedRights: nil, usernames: [], storiesHidden: nil, nameColor: nil, backgroundEmojiId: nil, profileColor: nil, profileBackgroundEmojiId: nil, emojiStatus: nil, approximateBoostLevel: nil)) let peer6: EnginePeer = .user(TelegramUser(id: PeerId(namespace: Namespaces.Peer.SecretChat, id: PeerId.Id._internalFromInt64Value(5)), accessHash: nil, firstName: self.presentationData.strings.Appearance_ThemePreview_ChatList_6_Name, lastName: nil, username: nil, phone: nil, photo: [], botInfo: nil, restrictionInfo: nil, flags: [], emojiStatus: nil, usernames: [], storiesHidden: nil, nameColor: nil, backgroundEmojiId: nil, profileColor: nil, profileBackgroundEmojiId: nil)) let timestamp = self.referenceTimestamp diff --git a/submodules/SettingsUI/Sources/ThemePickerGridItem.swift b/submodules/SettingsUI/Sources/ThemePickerGridItem.swift index 2f00529fac..a1dec15db2 100644 --- a/submodules/SettingsUI/Sources/ThemePickerGridItem.swift +++ b/submodules/SettingsUI/Sources/ThemePickerGridItem.swift @@ -500,7 +500,7 @@ class ThemeGridThemeItemNode: ListViewItemNode, ItemListItemNode { for theme in item.themes { let selected = item.currentTheme.index == theme.index - let iconItem = ThemeCarouselThemeIconItem(context: item.context, emojiFile: theme.emoticon.flatMap { item.animatedEmojiStickers[$0]?.first?.file }, themeReference: theme, nightMode: item.nightMode, themeSpecificAccentColors: item.themeSpecificAccentColors, themeSpecificChatWallpapers: item.themeSpecificChatWallpapers, selected: selected, theme: item.theme, strings: item.strings, wallpaper: nil, action: { theme in + let iconItem = ThemeCarouselThemeIconItem(context: item.context, emojiFile: theme.emoticon.flatMap { item.animatedEmojiStickers[$0]?.first?.file }, themeReference: theme, nightMode: item.nightMode, channelMode: false, themeSpecificAccentColors: item.themeSpecificAccentColors, themeSpecificChatWallpapers: item.themeSpecificChatWallpapers, selected: selected, theme: item.theme, strings: item.strings, wallpaper: nil, action: { theme in item.updatedTheme(theme) }, contextAction: nil) diff --git a/submodules/SettingsUI/Sources/Themes/ThemeAccentColorControllerNode.swift b/submodules/SettingsUI/Sources/Themes/ThemeAccentColorControllerNode.swift index 1d9f103428..59ab83e09a 100644 --- a/submodules/SettingsUI/Sources/Themes/ThemeAccentColorControllerNode.swift +++ b/submodules/SettingsUI/Sources/Themes/ThemeAccentColorControllerNode.swift @@ -948,7 +948,7 @@ final class ThemeAccentColorControllerNode: ASDisplayNode, UIScrollViewDelegate let selfPeer: EnginePeer = .user(TelegramUser(id: self.context.account.peerId, accessHash: nil, firstName: nil, lastName: nil, username: nil, phone: nil, photo: [], botInfo: nil, restrictionInfo: nil, flags: [], emojiStatus: nil, usernames: [], storiesHidden: nil, nameColor: nil, backgroundEmojiId: nil, profileColor: nil, profileBackgroundEmojiId: nil)) let peer1: EnginePeer = .user(TelegramUser(id: PeerId(namespace: Namespaces.Peer.CloudUser, id: PeerId.Id._internalFromInt64Value(1)), accessHash: nil, firstName: self.presentationData.strings.Appearance_ThemePreview_ChatList_1_Name, lastName: nil, username: nil, phone: nil, photo: [], botInfo: nil, restrictionInfo: nil, flags: [], emojiStatus: nil, usernames: [], storiesHidden: nil, nameColor: nil, backgroundEmojiId: nil, profileColor: nil, profileBackgroundEmojiId: nil)) let peer2: EnginePeer = .user(TelegramUser(id: PeerId(namespace: Namespaces.Peer.CloudUser, id: PeerId.Id._internalFromInt64Value(2)), accessHash: nil, firstName: self.presentationData.strings.Appearance_ThemePreview_ChatList_2_Name, lastName: nil, username: nil, phone: nil, photo: [], botInfo: nil, restrictionInfo: nil, flags: [], emojiStatus: nil, usernames: [], storiesHidden: nil, nameColor: nil, backgroundEmojiId: nil, profileColor: nil, profileBackgroundEmojiId: nil)) - let peer3: EnginePeer = .channel(TelegramChannel(id: PeerId(namespace: Namespaces.Peer.CloudChannel, id: PeerId.Id._internalFromInt64Value(3)), accessHash: nil, title: self.presentationData.strings.Appearance_ThemePreview_ChatList_3_Name, username: nil, photo: [], creationDate: 0, version: 0, participationStatus: .member, info: .group(.init(flags: [])), flags: [], restrictionInfo: nil, adminRights: nil, bannedRights: nil, defaultBannedRights: nil, usernames: [], storiesHidden: nil, nameColor: nil, backgroundEmojiId: nil, profileColor: nil, profileBackgroundEmojiId: nil, emojiStatus: nil)) + let peer3: EnginePeer = .channel(TelegramChannel(id: PeerId(namespace: Namespaces.Peer.CloudChannel, id: PeerId.Id._internalFromInt64Value(3)), accessHash: nil, title: self.presentationData.strings.Appearance_ThemePreview_ChatList_3_Name, username: nil, photo: [], creationDate: 0, version: 0, participationStatus: .member, info: .group(.init(flags: [])), flags: [], restrictionInfo: nil, adminRights: nil, bannedRights: nil, defaultBannedRights: nil, usernames: [], storiesHidden: nil, nameColor: nil, backgroundEmojiId: nil, profileColor: nil, profileBackgroundEmojiId: nil, emojiStatus: nil, approximateBoostLevel: nil)) let peer3Author: EnginePeer = .user(TelegramUser(id: PeerId(namespace: Namespaces.Peer.CloudUser, id: PeerId.Id._internalFromInt64Value(4)), accessHash: nil, firstName: self.presentationData.strings.Appearance_ThemePreview_ChatList_3_AuthorName, lastName: nil, username: nil, phone: nil, photo: [], botInfo: nil, restrictionInfo: nil, flags: [], emojiStatus: nil, usernames: [], storiesHidden: nil, nameColor: nil, backgroundEmojiId: nil, profileColor: nil, profileBackgroundEmojiId: nil)) let peer4: EnginePeer = .user(TelegramUser(id: PeerId(namespace: Namespaces.Peer.CloudUser, id: PeerId.Id._internalFromInt64Value(4)), accessHash: nil, firstName: self.presentationData.strings.Appearance_ThemePreview_ChatList_4_Name, lastName: nil, username: nil, phone: nil, photo: [], botInfo: nil, restrictionInfo: nil, flags: [], emojiStatus: nil, usernames: [], storiesHidden: nil, nameColor: nil, backgroundEmojiId: nil, profileColor: nil, profileBackgroundEmojiId: nil)) diff --git a/submodules/SettingsUI/Sources/Themes/ThemePreviewControllerNode.swift b/submodules/SettingsUI/Sources/Themes/ThemePreviewControllerNode.swift index 20657800eb..168b3ca003 100644 --- a/submodules/SettingsUI/Sources/Themes/ThemePreviewControllerNode.swift +++ b/submodules/SettingsUI/Sources/Themes/ThemePreviewControllerNode.swift @@ -455,10 +455,10 @@ final class ThemePreviewControllerNode: ASDisplayNode, UIScrollViewDelegate { let selfPeer: EnginePeer = .user(TelegramUser(id: self.context.account.peerId, accessHash: nil, firstName: nil, lastName: nil, username: nil, phone: nil, photo: [], botInfo: nil, restrictionInfo: nil, flags: [], emojiStatus: nil, usernames: [], storiesHidden: nil, nameColor: nil, backgroundEmojiId: nil, profileColor: nil, profileBackgroundEmojiId: nil)) let peer1: EnginePeer = .user(TelegramUser(id: PeerId(namespace: Namespaces.Peer.CloudUser, id: PeerId.Id._internalFromInt64Value(1)), accessHash: nil, firstName: self.presentationData.strings.Appearance_ThemePreview_ChatList_1_Name, lastName: nil, username: nil, phone: nil, photo: [], botInfo: nil, restrictionInfo: nil, flags: [], emojiStatus: nil, usernames: [], storiesHidden: nil, nameColor: nil, backgroundEmojiId: nil, profileColor: nil, profileBackgroundEmojiId: nil)) let peer2: EnginePeer = .user(TelegramUser(id: PeerId(namespace: Namespaces.Peer.CloudUser, id: PeerId.Id._internalFromInt64Value(2)), accessHash: nil, firstName: self.presentationData.strings.Appearance_ThemePreview_ChatList_2_Name, lastName: nil, username: nil, phone: nil, photo: [], botInfo: nil, restrictionInfo: nil, flags: [], emojiStatus: nil, usernames: [], storiesHidden: nil, nameColor: nil, backgroundEmojiId: nil, profileColor: nil, profileBackgroundEmojiId: nil)) - let peer3: EnginePeer = .channel(TelegramChannel(id: PeerId(namespace: Namespaces.Peer.CloudChannel, id: PeerId.Id._internalFromInt64Value(3)), accessHash: nil, title: self.presentationData.strings.Appearance_ThemePreview_ChatList_3_Name, username: nil, photo: [], creationDate: 0, version: 0, participationStatus: .member, info: .group(.init(flags: [])), flags: [], restrictionInfo: nil, adminRights: nil, bannedRights: nil, defaultBannedRights: nil, usernames: [], storiesHidden: nil, nameColor: nil, backgroundEmojiId: nil, profileColor: nil, profileBackgroundEmojiId: nil, emojiStatus: nil)) + let peer3: EnginePeer = .channel(TelegramChannel(id: PeerId(namespace: Namespaces.Peer.CloudChannel, id: PeerId.Id._internalFromInt64Value(3)), accessHash: nil, title: self.presentationData.strings.Appearance_ThemePreview_ChatList_3_Name, username: nil, photo: [], creationDate: 0, version: 0, participationStatus: .member, info: .group(.init(flags: [])), flags: [], restrictionInfo: nil, adminRights: nil, bannedRights: nil, defaultBannedRights: nil, usernames: [], storiesHidden: nil, nameColor: nil, backgroundEmojiId: nil, profileColor: nil, profileBackgroundEmojiId: nil, emojiStatus: nil, approximateBoostLevel: nil)) let peer3Author: EnginePeer = .user(TelegramUser(id: PeerId(namespace: Namespaces.Peer.CloudUser, id: PeerId.Id._internalFromInt64Value(4)), accessHash: nil, firstName: self.presentationData.strings.Appearance_ThemePreview_ChatList_3_AuthorName, lastName: nil, username: nil, phone: nil, photo: [], botInfo: nil, restrictionInfo: nil, flags: [], emojiStatus: nil, usernames: [], storiesHidden: nil, nameColor: nil, backgroundEmojiId: nil, profileColor: nil, profileBackgroundEmojiId: nil)) let peer4: EnginePeer = .user(TelegramUser(id: PeerId(namespace: Namespaces.Peer.CloudUser, id: PeerId.Id._internalFromInt64Value(4)), accessHash: nil, firstName: self.presentationData.strings.Appearance_ThemePreview_ChatList_4_Name, lastName: nil, username: nil, phone: nil, photo: [], botInfo: nil, restrictionInfo: nil, flags: [], emojiStatus: nil, usernames: [], storiesHidden: nil, nameColor: nil, backgroundEmojiId: nil, profileColor: nil, profileBackgroundEmojiId: nil)) - let peer5: EnginePeer = .channel(TelegramChannel(id: PeerId(namespace: Namespaces.Peer.CloudChannel, id: PeerId.Id._internalFromInt64Value(5)), accessHash: nil, title: self.presentationData.strings.Appearance_ThemePreview_ChatList_5_Name, username: nil, photo: [], creationDate: 0, version: 0, participationStatus: .member, info: .broadcast(.init(flags: [])), flags: [], restrictionInfo: nil, adminRights: nil, bannedRights: nil, defaultBannedRights: nil, usernames: [], storiesHidden: nil, nameColor: nil, backgroundEmojiId: nil, profileColor: nil, profileBackgroundEmojiId: nil, emojiStatus: nil)) + let peer5: EnginePeer = .channel(TelegramChannel(id: PeerId(namespace: Namespaces.Peer.CloudChannel, id: PeerId.Id._internalFromInt64Value(5)), accessHash: nil, title: self.presentationData.strings.Appearance_ThemePreview_ChatList_5_Name, username: nil, photo: [], creationDate: 0, version: 0, participationStatus: .member, info: .broadcast(.init(flags: [])), flags: [], restrictionInfo: nil, adminRights: nil, bannedRights: nil, defaultBannedRights: nil, usernames: [], storiesHidden: nil, nameColor: nil, backgroundEmojiId: nil, profileColor: nil, profileBackgroundEmojiId: nil, emojiStatus: nil, approximateBoostLevel: nil)) let peer6: EnginePeer = .user(TelegramUser(id: PeerId(namespace: Namespaces.Peer.SecretChat, id: PeerId.Id._internalFromInt64Value(5)), accessHash: nil, firstName: self.presentationData.strings.Appearance_ThemePreview_ChatList_6_Name, lastName: nil, username: nil, phone: nil, photo: [], botInfo: nil, restrictionInfo: nil, flags: [], emojiStatus: nil, usernames: [], storiesHidden: nil, nameColor: nil, backgroundEmojiId: nil, profileColor: nil, profileBackgroundEmojiId: nil)) let peer7: EnginePeer = .user(TelegramUser(id: PeerId(namespace: Namespaces.Peer.CloudUser, id: PeerId.Id._internalFromInt64Value(6)), accessHash: nil, firstName: self.presentationData.strings.Appearance_ThemePreview_ChatList_7_Name, lastName: nil, username: nil, phone: nil, photo: [], botInfo: nil, restrictionInfo: nil, flags: [], emojiStatus: nil, usernames: [], storiesHidden: nil, nameColor: nil, backgroundEmojiId: nil, profileColor: nil, profileBackgroundEmojiId: nil)) diff --git a/submodules/TelegramApi/Sources/Api0.swift b/submodules/TelegramApi/Sources/Api0.swift index e29cf34d61..861fb565c5 100644 --- a/submodules/TelegramApi/Sources/Api0.swift +++ b/submodules/TelegramApi/Sources/Api0.swift @@ -104,6 +104,7 @@ fileprivate let parsers: [Int32 : (BufferReader) -> Any?] = { dict[531458253] = { return Api.ChannelAdminLogEvent.parse_channelAdminLogEvent($0) } dict[1427671598] = { return Api.ChannelAdminLogEventAction.parse_channelAdminLogEventActionChangeAbout($0) } dict[-1102180616] = { return Api.ChannelAdminLogEventAction.parse_channelAdminLogEventActionChangeAvailableReactions($0) } + dict[1051328177] = { return Api.ChannelAdminLogEventAction.parse_channelAdminLogEventActionChangeEmojiStatus($0) } dict[1855199800] = { return Api.ChannelAdminLogEventAction.parse_channelAdminLogEventActionChangeHistoryTTL($0) } dict[84703944] = { return Api.ChannelAdminLogEventAction.parse_channelAdminLogEventActionChangeLinkedChat($0) } dict[241923758] = { return Api.ChannelAdminLogEventAction.parse_channelAdminLogEventActionChangeLocation($0) } @@ -167,7 +168,7 @@ fileprivate let parsers: [Int32 : (BufferReader) -> Any?] = { dict[-531931925] = { return Api.ChannelParticipantsFilter.parse_channelParticipantsMentions($0) } dict[-566281095] = { return Api.ChannelParticipantsFilter.parse_channelParticipantsRecent($0) } dict[106343499] = { return Api.ChannelParticipantsFilter.parse_channelParticipantsSearch($0) } - dict[-1506368542] = { return Api.Chat.parse_channel($0) } + dict[179174543] = { return Api.Chat.parse_channel($0) } dict[399807445] = { return Api.Chat.parse_channelForbidden($0) } dict[1103884886] = { return Api.Chat.parse_chat($0) } dict[693512293] = { return Api.Chat.parse_chatEmpty($0) } @@ -463,7 +464,9 @@ fileprivate let parsers: [Int32 : (BufferReader) -> Any?] = { dict[695856818] = { return Api.LangPackString.parse_langPackStringDeleted($0) } dict[1816636575] = { return Api.LangPackString.parse_langPackStringPluralized($0) } dict[-1361650766] = { return Api.MaskCoords.parse_maskCoords($0) } + dict[577893055] = { return Api.MediaArea.parse_inputMediaAreaChannelPost($0) } dict[-1300094593] = { return Api.MediaArea.parse_inputMediaAreaVenue($0) } + dict[1996756655] = { return Api.MediaArea.parse_mediaAreaChannelPost($0) } dict[-544523486] = { return Api.MediaArea.parse_mediaAreaGeoPoint($0) } dict[340088945] = { return Api.MediaArea.parse_mediaAreaSuggestedReaction($0) } dict[-1098720356] = { return Api.MediaArea.parse_mediaAreaVenue($0) } @@ -856,6 +859,8 @@ fileprivate let parsers: [Int32 : (BufferReader) -> Any?] = { dict[1232025500] = { return Api.Update.parse_updateBotInlineQuery($0) } dict[317794823] = { return Api.Update.parse_updateBotInlineSend($0) } dict[347625491] = { return Api.Update.parse_updateBotMenuButton($0) } + dict[-1407069234] = { return Api.Update.parse_updateBotMessageReaction($0) } + dict[164329305] = { return Api.Update.parse_updateBotMessageReactions($0) } dict[-1934976362] = { return Api.Update.parse_updateBotPrecheckoutQuery($0) } dict[-1246823043] = { return Api.Update.parse_updateBotShippingQuery($0) } dict[-997782967] = { return Api.Update.parse_updateBotStopped($0) } @@ -1180,7 +1185,7 @@ fileprivate let parsers: [Int32 : (BufferReader) -> Any?] = { dict[1218005070] = { return Api.messages.VotesList.parse_votesList($0) } dict[-44166467] = { return Api.messages.WebPage.parse_webPage($0) } dict[1042605427] = { return Api.payments.BankCardData.parse_bankCardData($0) } - dict[-1222446760] = { return Api.payments.CheckedGiftCode.parse_checkedGiftCode($0) } + dict[675942550] = { return Api.payments.CheckedGiftCode.parse_checkedGiftCode($0) } dict[-1362048039] = { return Api.payments.ExportedInvoice.parse_exportedInvoice($0) } dict[1130879648] = { return Api.payments.GiveawayInfo.parse_giveawayInfo($0) } dict[13456752] = { return Api.payments.GiveawayInfo.parse_giveawayInfoResults($0) } @@ -1225,7 +1230,7 @@ fileprivate let parsers: [Int32 : (BufferReader) -> Any?] = { dict[1574486984] = { return Api.stories.Stories.parse_stories($0) } dict[-1436583780] = { return Api.stories.StoryReactionsList.parse_storyReactionsList($0) } dict[-560009955] = { return Api.stories.StoryViews.parse_storyViews($0) } - dict[430008454] = { return Api.stories.StoryViewsList.parse_storyViewsList($0) } + dict[1507299269] = { return Api.stories.StoryViewsList.parse_storyViewsList($0) } dict[543450958] = { return Api.updates.ChannelDifference.parse_channelDifference($0) } dict[1041346555] = { return Api.updates.ChannelDifference.parse_channelDifferenceEmpty($0) } dict[-1531132162] = { return Api.updates.ChannelDifference.parse_channelDifferenceTooLong($0) } @@ -1257,7 +1262,7 @@ public extension Api { return parser(reader) } else { - telegramApiLog("Type constructor \(String(signature, radix: 16, uppercase: false)) not found") + telegramApiLog("Type constructor \(String(UInt32(bitPattern: signature), radix: 16, uppercase: false)) not found") return nil } } diff --git a/submodules/TelegramApi/Sources/Api12.swift b/submodules/TelegramApi/Sources/Api12.swift index d7131cd5dc..70197dfd31 100644 --- a/submodules/TelegramApi/Sources/Api12.swift +++ b/submodules/TelegramApi/Sources/Api12.swift @@ -155,14 +155,24 @@ public extension Api { } } public extension Api { - enum MediaArea: TypeConstructorDescription { + indirect enum MediaArea: TypeConstructorDescription { + case inputMediaAreaChannelPost(coordinates: Api.MediaAreaCoordinates, channel: Api.InputChannel, msgId: Int32) case inputMediaAreaVenue(coordinates: Api.MediaAreaCoordinates, queryId: Int64, resultId: String) + case mediaAreaChannelPost(coordinates: Api.MediaAreaCoordinates, channelId: Int64, msgId: Int32) case mediaAreaGeoPoint(coordinates: Api.MediaAreaCoordinates, geo: Api.GeoPoint) case mediaAreaSuggestedReaction(flags: Int32, coordinates: Api.MediaAreaCoordinates, reaction: Api.Reaction) case mediaAreaVenue(coordinates: Api.MediaAreaCoordinates, geo: Api.GeoPoint, title: String, address: String, provider: String, venueId: String, venueType: String) public func serialize(_ buffer: Buffer, _ boxed: Swift.Bool) { switch self { + case .inputMediaAreaChannelPost(let coordinates, let channel, let msgId): + if boxed { + buffer.appendInt32(577893055) + } + coordinates.serialize(buffer, true) + channel.serialize(buffer, true) + serializeInt32(msgId, buffer: buffer, boxed: false) + break case .inputMediaAreaVenue(let coordinates, let queryId, let resultId): if boxed { buffer.appendInt32(-1300094593) @@ -171,6 +181,14 @@ public extension Api { serializeInt64(queryId, buffer: buffer, boxed: false) serializeString(resultId, buffer: buffer, boxed: false) break + case .mediaAreaChannelPost(let coordinates, let channelId, let msgId): + if boxed { + buffer.appendInt32(1996756655) + } + coordinates.serialize(buffer, true) + serializeInt64(channelId, buffer: buffer, boxed: false) + serializeInt32(msgId, buffer: buffer, boxed: false) + break case .mediaAreaGeoPoint(let coordinates, let geo): if boxed { buffer.appendInt32(-544523486) @@ -203,8 +221,12 @@ public extension Api { public func descriptionFields() -> (String, [(String, Any)]) { switch self { + case .inputMediaAreaChannelPost(let coordinates, let channel, let msgId): + return ("inputMediaAreaChannelPost", [("coordinates", coordinates as Any), ("channel", channel as Any), ("msgId", msgId as Any)]) case .inputMediaAreaVenue(let coordinates, let queryId, let resultId): return ("inputMediaAreaVenue", [("coordinates", coordinates as Any), ("queryId", queryId as Any), ("resultId", resultId as Any)]) + case .mediaAreaChannelPost(let coordinates, let channelId, let msgId): + return ("mediaAreaChannelPost", [("coordinates", coordinates as Any), ("channelId", channelId as Any), ("msgId", msgId as Any)]) case .mediaAreaGeoPoint(let coordinates, let geo): return ("mediaAreaGeoPoint", [("coordinates", coordinates as Any), ("geo", geo as Any)]) case .mediaAreaSuggestedReaction(let flags, let coordinates, let reaction): @@ -214,6 +236,27 @@ public extension Api { } } + public static func parse_inputMediaAreaChannelPost(_ reader: BufferReader) -> MediaArea? { + var _1: Api.MediaAreaCoordinates? + if let signature = reader.readInt32() { + _1 = Api.parse(reader, signature: signature) as? Api.MediaAreaCoordinates + } + var _2: Api.InputChannel? + if let signature = reader.readInt32() { + _2 = Api.parse(reader, signature: signature) as? Api.InputChannel + } + var _3: Int32? + _3 = reader.readInt32() + let _c1 = _1 != nil + let _c2 = _2 != nil + let _c3 = _3 != nil + if _c1 && _c2 && _c3 { + return Api.MediaArea.inputMediaAreaChannelPost(coordinates: _1!, channel: _2!, msgId: _3!) + } + else { + return nil + } + } public static func parse_inputMediaAreaVenue(_ reader: BufferReader) -> MediaArea? { var _1: Api.MediaAreaCoordinates? if let signature = reader.readInt32() { @@ -233,6 +276,25 @@ public extension Api { return nil } } + public static func parse_mediaAreaChannelPost(_ reader: BufferReader) -> MediaArea? { + var _1: Api.MediaAreaCoordinates? + if let signature = reader.readInt32() { + _1 = Api.parse(reader, signature: signature) as? Api.MediaAreaCoordinates + } + var _2: Int64? + _2 = reader.readInt64() + var _3: Int32? + _3 = reader.readInt32() + let _c1 = _1 != nil + let _c2 = _2 != nil + let _c3 = _3 != nil + if _c1 && _c2 && _c3 { + return Api.MediaArea.mediaAreaChannelPost(coordinates: _1!, channelId: _2!, msgId: _3!) + } + else { + return nil + } + } public static func parse_mediaAreaGeoPoint(_ reader: BufferReader) -> MediaArea? { var _1: Api.MediaAreaCoordinates? if let signature = reader.readInt32() { diff --git a/submodules/TelegramApi/Sources/Api2.swift b/submodules/TelegramApi/Sources/Api2.swift index 0131fb4065..e8bb5e4205 100644 --- a/submodules/TelegramApi/Sources/Api2.swift +++ b/submodules/TelegramApi/Sources/Api2.swift @@ -724,6 +724,7 @@ public extension Api { indirect enum ChannelAdminLogEventAction: TypeConstructorDescription { case channelAdminLogEventActionChangeAbout(prevValue: String, newValue: String) case channelAdminLogEventActionChangeAvailableReactions(prevValue: Api.ChatReactions, newValue: Api.ChatReactions) + case channelAdminLogEventActionChangeEmojiStatus(prevValue: Api.EmojiStatus, newValue: Api.EmojiStatus) case channelAdminLogEventActionChangeHistoryTTL(prevValue: Int32, newValue: Int32) case channelAdminLogEventActionChangeLinkedChat(prevValue: Int64, newValue: Int64) case channelAdminLogEventActionChangeLocation(prevValue: Api.ChannelLocation, newValue: Api.ChannelLocation) @@ -785,6 +786,13 @@ public extension Api { prevValue.serialize(buffer, true) newValue.serialize(buffer, true) break + case .channelAdminLogEventActionChangeEmojiStatus(let prevValue, let newValue): + if boxed { + buffer.appendInt32(1051328177) + } + prevValue.serialize(buffer, true) + newValue.serialize(buffer, true) + break case .channelAdminLogEventActionChangeHistoryTTL(let prevValue, let newValue): if boxed { buffer.appendInt32(1855199800) @@ -1088,6 +1096,8 @@ public extension Api { return ("channelAdminLogEventActionChangeAbout", [("prevValue", prevValue as Any), ("newValue", newValue as Any)]) case .channelAdminLogEventActionChangeAvailableReactions(let prevValue, let newValue): return ("channelAdminLogEventActionChangeAvailableReactions", [("prevValue", prevValue as Any), ("newValue", newValue as Any)]) + case .channelAdminLogEventActionChangeEmojiStatus(let prevValue, let newValue): + return ("channelAdminLogEventActionChangeEmojiStatus", [("prevValue", prevValue as Any), ("newValue", newValue as Any)]) case .channelAdminLogEventActionChangeHistoryTTL(let prevValue, let newValue): return ("channelAdminLogEventActionChangeHistoryTTL", [("prevValue", prevValue as Any), ("newValue", newValue as Any)]) case .channelAdminLogEventActionChangeLinkedChat(let prevValue, let newValue): @@ -1211,6 +1221,24 @@ public extension Api { return nil } } + public static func parse_channelAdminLogEventActionChangeEmojiStatus(_ reader: BufferReader) -> ChannelAdminLogEventAction? { + var _1: Api.EmojiStatus? + if let signature = reader.readInt32() { + _1 = Api.parse(reader, signature: signature) as? Api.EmojiStatus + } + var _2: Api.EmojiStatus? + if let signature = reader.readInt32() { + _2 = Api.parse(reader, signature: signature) as? Api.EmojiStatus + } + let _c1 = _1 != nil + let _c2 = _2 != nil + if _c1 && _c2 { + return Api.ChannelAdminLogEventAction.channelAdminLogEventActionChangeEmojiStatus(prevValue: _1!, newValue: _2!) + } + else { + return nil + } + } public static func parse_channelAdminLogEventActionChangeHistoryTTL(_ reader: BufferReader) -> ChannelAdminLogEventAction? { var _1: Int32? _1 = reader.readInt32() diff --git a/submodules/TelegramApi/Sources/Api22.swift b/submodules/TelegramApi/Sources/Api22.swift index 1f91f7133b..2ed34c1d5b 100644 --- a/submodules/TelegramApi/Sources/Api22.swift +++ b/submodules/TelegramApi/Sources/Api22.swift @@ -471,6 +471,8 @@ public extension Api { case updateBotInlineQuery(flags: Int32, queryId: Int64, userId: Int64, query: String, geo: Api.GeoPoint?, peerType: Api.InlineQueryPeerType?, offset: String) case updateBotInlineSend(flags: Int32, userId: Int64, query: String, geo: Api.GeoPoint?, id: String, msgId: Api.InputBotInlineMessageID?) case updateBotMenuButton(botId: Int64, button: Api.BotMenuButton) + case updateBotMessageReaction(peer: Api.Peer, msgId: Int32, date: Int32, actor: Api.Peer, oldReactions: [Api.Reaction], newReactions: [Api.Reaction], qts: Int32) + case updateBotMessageReactions(peer: Api.Peer, msgId: Int32, date: Int32, reactions: [Api.ReactionCount], qts: Int32) case updateBotPrecheckoutQuery(flags: Int32, queryId: Int64, userId: Int64, payload: Buffer, info: Api.PaymentRequestedInfo?, shippingOptionId: String?, currency: String, totalAmount: Int64) case updateBotShippingQuery(queryId: Int64, userId: Int64, payload: Buffer, shippingAddress: Api.PostAddress) case updateBotStopped(userId: Int64, date: Int32, stopped: Api.Bool, qts: Int32) @@ -671,6 +673,40 @@ public extension Api { serializeInt64(botId, buffer: buffer, boxed: false) button.serialize(buffer, true) break + case .updateBotMessageReaction(let peer, let msgId, let date, let actor, let oldReactions, let newReactions, let qts): + if boxed { + buffer.appendInt32(-1407069234) + } + peer.serialize(buffer, true) + serializeInt32(msgId, buffer: buffer, boxed: false) + serializeInt32(date, buffer: buffer, boxed: false) + actor.serialize(buffer, true) + buffer.appendInt32(481674261) + buffer.appendInt32(Int32(oldReactions.count)) + for item in oldReactions { + item.serialize(buffer, true) + } + buffer.appendInt32(481674261) + buffer.appendInt32(Int32(newReactions.count)) + for item in newReactions { + item.serialize(buffer, true) + } + serializeInt32(qts, buffer: buffer, boxed: false) + break + case .updateBotMessageReactions(let peer, let msgId, let date, let reactions, let qts): + if boxed { + buffer.appendInt32(164329305) + } + peer.serialize(buffer, true) + serializeInt32(msgId, buffer: buffer, boxed: false) + serializeInt32(date, buffer: buffer, boxed: false) + buffer.appendInt32(481674261) + buffer.appendInt32(Int32(reactions.count)) + for item in reactions { + item.serialize(buffer, true) + } + serializeInt32(qts, buffer: buffer, boxed: false) + break case .updateBotPrecheckoutQuery(let flags, let queryId, let userId, let payload, let info, let shippingOptionId, let currency, let totalAmount): if boxed { buffer.appendInt32(-1934976362) @@ -1634,6 +1670,10 @@ public extension Api { return ("updateBotInlineSend", [("flags", flags as Any), ("userId", userId as Any), ("query", query as Any), ("geo", geo as Any), ("id", id as Any), ("msgId", msgId as Any)]) case .updateBotMenuButton(let botId, let button): return ("updateBotMenuButton", [("botId", botId as Any), ("button", button as Any)]) + case .updateBotMessageReaction(let peer, let msgId, let date, let actor, let oldReactions, let newReactions, let qts): + return ("updateBotMessageReaction", [("peer", peer as Any), ("msgId", msgId as Any), ("date", date as Any), ("actor", actor as Any), ("oldReactions", oldReactions as Any), ("newReactions", newReactions as Any), ("qts", qts as Any)]) + case .updateBotMessageReactions(let peer, let msgId, let date, let reactions, let qts): + return ("updateBotMessageReactions", [("peer", peer as Any), ("msgId", msgId as Any), ("date", date as Any), ("reactions", reactions as Any), ("qts", qts as Any)]) case .updateBotPrecheckoutQuery(let flags, let queryId, let userId, let payload, let info, let shippingOptionId, let currency, let totalAmount): return ("updateBotPrecheckoutQuery", [("flags", flags as Any), ("queryId", queryId as Any), ("userId", userId as Any), ("payload", payload as Any), ("info", info as Any), ("shippingOptionId", shippingOptionId as Any), ("currency", currency as Any), ("totalAmount", totalAmount as Any)]) case .updateBotShippingQuery(let queryId, let userId, let payload, let shippingAddress): @@ -2050,6 +2090,70 @@ public extension Api { return nil } } + public static func parse_updateBotMessageReaction(_ reader: BufferReader) -> Update? { + var _1: Api.Peer? + if let signature = reader.readInt32() { + _1 = Api.parse(reader, signature: signature) as? Api.Peer + } + var _2: Int32? + _2 = reader.readInt32() + var _3: Int32? + _3 = reader.readInt32() + var _4: Api.Peer? + if let signature = reader.readInt32() { + _4 = Api.parse(reader, signature: signature) as? Api.Peer + } + var _5: [Api.Reaction]? + if let _ = reader.readInt32() { + _5 = Api.parseVector(reader, elementSignature: 0, elementType: Api.Reaction.self) + } + var _6: [Api.Reaction]? + if let _ = reader.readInt32() { + _6 = Api.parseVector(reader, elementSignature: 0, elementType: Api.Reaction.self) + } + var _7: Int32? + _7 = reader.readInt32() + let _c1 = _1 != nil + let _c2 = _2 != nil + let _c3 = _3 != nil + let _c4 = _4 != nil + let _c5 = _5 != nil + let _c6 = _6 != nil + let _c7 = _7 != nil + if _c1 && _c2 && _c3 && _c4 && _c5 && _c6 && _c7 { + return Api.Update.updateBotMessageReaction(peer: _1!, msgId: _2!, date: _3!, actor: _4!, oldReactions: _5!, newReactions: _6!, qts: _7!) + } + else { + return nil + } + } + public static func parse_updateBotMessageReactions(_ reader: BufferReader) -> Update? { + var _1: Api.Peer? + if let signature = reader.readInt32() { + _1 = Api.parse(reader, signature: signature) as? Api.Peer + } + var _2: Int32? + _2 = reader.readInt32() + var _3: Int32? + _3 = reader.readInt32() + var _4: [Api.ReactionCount]? + if let _ = reader.readInt32() { + _4 = Api.parseVector(reader, elementSignature: 0, elementType: Api.ReactionCount.self) + } + var _5: Int32? + _5 = reader.readInt32() + let _c1 = _1 != nil + let _c2 = _2 != nil + let _c3 = _3 != nil + let _c4 = _4 != nil + let _c5 = _5 != nil + if _c1 && _c2 && _c3 && _c4 && _c5 { + return Api.Update.updateBotMessageReactions(peer: _1!, msgId: _2!, date: _3!, reactions: _4!, qts: _5!) + } + else { + return nil + } + } public static func parse_updateBotPrecheckoutQuery(_ reader: BufferReader) -> Update? { var _1: Int32? _1 = reader.readInt32() diff --git a/submodules/TelegramApi/Sources/Api29.swift b/submodules/TelegramApi/Sources/Api29.swift index 40fbe24d59..0ff2e96975 100644 --- a/submodules/TelegramApi/Sources/Api29.swift +++ b/submodules/TelegramApi/Sources/Api29.swift @@ -792,16 +792,16 @@ public extension Api.payments { } public extension Api.payments { enum CheckedGiftCode: TypeConstructorDescription { - case checkedGiftCode(flags: Int32, fromId: Api.Peer, giveawayMsgId: Int32?, toId: Int64?, date: Int32, months: Int32, usedDate: Int32?, chats: [Api.Chat], users: [Api.User]) + case checkedGiftCode(flags: Int32, fromId: Api.Peer?, giveawayMsgId: Int32?, toId: Int64?, date: Int32, months: Int32, usedDate: Int32?, chats: [Api.Chat], users: [Api.User]) public func serialize(_ buffer: Buffer, _ boxed: Swift.Bool) { switch self { case .checkedGiftCode(let flags, let fromId, let giveawayMsgId, let toId, let date, let months, let usedDate, let chats, let users): if boxed { - buffer.appendInt32(-1222446760) + buffer.appendInt32(675942550) } serializeInt32(flags, buffer: buffer, boxed: false) - fromId.serialize(buffer, true) + if Int(flags) & Int(1 << 4) != 0 {fromId!.serialize(buffer, true)} if Int(flags) & Int(1 << 3) != 0 {serializeInt32(giveawayMsgId!, buffer: buffer, boxed: false)} if Int(flags) & Int(1 << 0) != 0 {serializeInt64(toId!, buffer: buffer, boxed: false)} serializeInt32(date, buffer: buffer, boxed: false) @@ -832,9 +832,9 @@ public extension Api.payments { var _1: Int32? _1 = reader.readInt32() var _2: Api.Peer? - if let signature = reader.readInt32() { + if Int(_1!) & Int(1 << 4) != 0 {if let signature = reader.readInt32() { _2 = Api.parse(reader, signature: signature) as? Api.Peer - } + } } var _3: Int32? if Int(_1!) & Int(1 << 3) != 0 {_3 = reader.readInt32() } var _4: Int64? @@ -854,7 +854,7 @@ public extension Api.payments { _9 = Api.parseVector(reader, elementSignature: 0, elementType: Api.User.self) } let _c1 = _1 != nil - let _c2 = _2 != nil + let _c2 = (Int(_1!) & Int(1 << 4) == 0) || _2 != nil let _c3 = (Int(_1!) & Int(1 << 3) == 0) || _3 != nil let _c4 = (Int(_1!) & Int(1 << 0) == 0) || _4 != nil let _c5 = _5 != nil @@ -863,7 +863,7 @@ public extension Api.payments { let _c8 = _8 != nil let _c9 = _9 != nil if _c1 && _c2 && _c3 && _c4 && _c5 && _c6 && _c7 && _c8 && _c9 { - return Api.payments.CheckedGiftCode.checkedGiftCode(flags: _1!, fromId: _2!, giveawayMsgId: _3, toId: _4, date: _5!, months: _6!, usedDate: _7, chats: _8!, users: _9!) + return Api.payments.CheckedGiftCode.checkedGiftCode(flags: _1!, fromId: _2, giveawayMsgId: _3, toId: _4, date: _5!, months: _6!, usedDate: _7, chats: _8!, users: _9!) } else { return nil diff --git a/submodules/TelegramApi/Sources/Api3.swift b/submodules/TelegramApi/Sources/Api3.swift index 3a0e0def85..3baff622ff 100644 --- a/submodules/TelegramApi/Sources/Api3.swift +++ b/submodules/TelegramApi/Sources/Api3.swift @@ -522,7 +522,7 @@ public extension Api { } public extension Api { indirect enum Chat: TypeConstructorDescription { - case channel(flags: Int32, flags2: Int32, id: Int64, accessHash: Int64?, title: String, username: String?, photo: Api.ChatPhoto, date: Int32, restrictionReason: [Api.RestrictionReason]?, adminRights: Api.ChatAdminRights?, bannedRights: Api.ChatBannedRights?, defaultBannedRights: Api.ChatBannedRights?, participantsCount: Int32?, usernames: [Api.Username]?, storiesMaxId: Int32?, color: Api.PeerColor?, profileColor: Api.PeerColor?) + case channel(flags: Int32, flags2: Int32, id: Int64, accessHash: Int64?, title: String, username: String?, photo: Api.ChatPhoto, date: Int32, restrictionReason: [Api.RestrictionReason]?, adminRights: Api.ChatAdminRights?, bannedRights: Api.ChatBannedRights?, defaultBannedRights: Api.ChatBannedRights?, participantsCount: Int32?, usernames: [Api.Username]?, storiesMaxId: Int32?, color: Api.PeerColor?, profileColor: Api.PeerColor?, emojiStatus: Api.EmojiStatus?, level: Int32?) case channelForbidden(flags: Int32, id: Int64, accessHash: Int64, title: String, untilDate: Int32?) case chat(flags: Int32, id: Int64, title: String, photo: Api.ChatPhoto, participantsCount: Int32, date: Int32, version: Int32, migratedTo: Api.InputChannel?, adminRights: Api.ChatAdminRights?, defaultBannedRights: Api.ChatBannedRights?) case chatEmpty(id: Int64) @@ -530,9 +530,9 @@ public extension Api { public func serialize(_ buffer: Buffer, _ boxed: Swift.Bool) { switch self { - case .channel(let flags, let flags2, let id, let accessHash, let title, let username, let photo, let date, let restrictionReason, let adminRights, let bannedRights, let defaultBannedRights, let participantsCount, let usernames, let storiesMaxId, let color, let profileColor): + case .channel(let flags, let flags2, let id, let accessHash, let title, let username, let photo, let date, let restrictionReason, let adminRights, let bannedRights, let defaultBannedRights, let participantsCount, let usernames, let storiesMaxId, let color, let profileColor, let emojiStatus, let level): if boxed { - buffer.appendInt32(-1506368542) + buffer.appendInt32(179174543) } serializeInt32(flags, buffer: buffer, boxed: false) serializeInt32(flags2, buffer: buffer, boxed: false) @@ -559,6 +559,8 @@ public extension Api { if Int(flags2) & Int(1 << 4) != 0 {serializeInt32(storiesMaxId!, buffer: buffer, boxed: false)} if Int(flags2) & Int(1 << 7) != 0 {color!.serialize(buffer, true)} if Int(flags2) & Int(1 << 8) != 0 {profileColor!.serialize(buffer, true)} + if Int(flags2) & Int(1 << 9) != 0 {emojiStatus!.serialize(buffer, true)} + if Int(flags2) & Int(1 << 10) != 0 {serializeInt32(level!, buffer: buffer, boxed: false)} break case .channelForbidden(let flags, let id, let accessHash, let title, let untilDate): if boxed { @@ -603,8 +605,8 @@ public extension Api { public func descriptionFields() -> (String, [(String, Any)]) { switch self { - case .channel(let flags, let flags2, let id, let accessHash, let title, let username, let photo, let date, let restrictionReason, let adminRights, let bannedRights, let defaultBannedRights, let participantsCount, let usernames, let storiesMaxId, let color, let profileColor): - return ("channel", [("flags", flags as Any), ("flags2", flags2 as Any), ("id", id as Any), ("accessHash", accessHash as Any), ("title", title as Any), ("username", username as Any), ("photo", photo as Any), ("date", date as Any), ("restrictionReason", restrictionReason as Any), ("adminRights", adminRights as Any), ("bannedRights", bannedRights as Any), ("defaultBannedRights", defaultBannedRights as Any), ("participantsCount", participantsCount as Any), ("usernames", usernames as Any), ("storiesMaxId", storiesMaxId as Any), ("color", color as Any), ("profileColor", profileColor as Any)]) + case .channel(let flags, let flags2, let id, let accessHash, let title, let username, let photo, let date, let restrictionReason, let adminRights, let bannedRights, let defaultBannedRights, let participantsCount, let usernames, let storiesMaxId, let color, let profileColor, let emojiStatus, let level): + return ("channel", [("flags", flags as Any), ("flags2", flags2 as Any), ("id", id as Any), ("accessHash", accessHash as Any), ("title", title as Any), ("username", username as Any), ("photo", photo as Any), ("date", date as Any), ("restrictionReason", restrictionReason as Any), ("adminRights", adminRights as Any), ("bannedRights", bannedRights as Any), ("defaultBannedRights", defaultBannedRights as Any), ("participantsCount", participantsCount as Any), ("usernames", usernames as Any), ("storiesMaxId", storiesMaxId as Any), ("color", color as Any), ("profileColor", profileColor as Any), ("emojiStatus", emojiStatus as Any), ("level", level as Any)]) case .channelForbidden(let flags, let id, let accessHash, let title, let untilDate): return ("channelForbidden", [("flags", flags as Any), ("id", id as Any), ("accessHash", accessHash as Any), ("title", title as Any), ("untilDate", untilDate as Any)]) case .chat(let flags, let id, let title, let photo, let participantsCount, let date, let version, let migratedTo, let adminRights, let defaultBannedRights): @@ -667,6 +669,12 @@ public extension Api { if Int(_2!) & Int(1 << 8) != 0 {if let signature = reader.readInt32() { _17 = Api.parse(reader, signature: signature) as? Api.PeerColor } } + var _18: Api.EmojiStatus? + if Int(_2!) & Int(1 << 9) != 0 {if let signature = reader.readInt32() { + _18 = Api.parse(reader, signature: signature) as? Api.EmojiStatus + } } + var _19: Int32? + if Int(_2!) & Int(1 << 10) != 0 {_19 = reader.readInt32() } let _c1 = _1 != nil let _c2 = _2 != nil let _c3 = _3 != nil @@ -684,8 +692,10 @@ public extension Api { let _c15 = (Int(_2!) & Int(1 << 4) == 0) || _15 != nil let _c16 = (Int(_2!) & Int(1 << 7) == 0) || _16 != nil let _c17 = (Int(_2!) & Int(1 << 8) == 0) || _17 != nil - if _c1 && _c2 && _c3 && _c4 && _c5 && _c6 && _c7 && _c8 && _c9 && _c10 && _c11 && _c12 && _c13 && _c14 && _c15 && _c16 && _c17 { - return Api.Chat.channel(flags: _1!, flags2: _2!, id: _3!, accessHash: _4, title: _5!, username: _6, photo: _7!, date: _8!, restrictionReason: _9, adminRights: _10, bannedRights: _11, defaultBannedRights: _12, participantsCount: _13, usernames: _14, storiesMaxId: _15, color: _16, profileColor: _17) + let _c18 = (Int(_2!) & Int(1 << 9) == 0) || _18 != nil + let _c19 = (Int(_2!) & Int(1 << 10) == 0) || _19 != nil + if _c1 && _c2 && _c3 && _c4 && _c5 && _c6 && _c7 && _c8 && _c9 && _c10 && _c11 && _c12 && _c13 && _c14 && _c15 && _c16 && _c17 && _c18 && _c19 { + return Api.Chat.channel(flags: _1!, flags2: _2!, id: _3!, accessHash: _4, title: _5!, username: _6, photo: _7!, date: _8!, restrictionReason: _9, adminRights: _10, bannedRights: _11, defaultBannedRights: _12, participantsCount: _13, usernames: _14, storiesMaxId: _15, color: _16, profileColor: _17, emojiStatus: _18, level: _19) } else { return nil diff --git a/submodules/TelegramApi/Sources/Api30.swift b/submodules/TelegramApi/Sources/Api30.swift index fc8015a365..0f744475ba 100644 --- a/submodules/TelegramApi/Sources/Api30.swift +++ b/submodules/TelegramApi/Sources/Api30.swift @@ -1548,13 +1548,13 @@ public extension Api.stories { } public extension Api.stories { enum StoryViewsList: TypeConstructorDescription { - case storyViewsList(flags: Int32, count: Int32, viewsCount: Int32, forwardsCount: Int32, reactionsCount: Int32, views: [Api.StoryView], users: [Api.User], nextOffset: String?) + case storyViewsList(flags: Int32, count: Int32, viewsCount: Int32, forwardsCount: Int32, reactionsCount: Int32, views: [Api.StoryView], chats: [Api.Chat], users: [Api.User], nextOffset: String?) public func serialize(_ buffer: Buffer, _ boxed: Swift.Bool) { switch self { - case .storyViewsList(let flags, let count, let viewsCount, let forwardsCount, let reactionsCount, let views, let users, let nextOffset): + case .storyViewsList(let flags, let count, let viewsCount, let forwardsCount, let reactionsCount, let views, let chats, let users, let nextOffset): if boxed { - buffer.appendInt32(430008454) + buffer.appendInt32(1507299269) } serializeInt32(flags, buffer: buffer, boxed: false) serializeInt32(count, buffer: buffer, boxed: false) @@ -1567,6 +1567,11 @@ public extension Api.stories { item.serialize(buffer, true) } buffer.appendInt32(481674261) + buffer.appendInt32(Int32(chats.count)) + for item in chats { + item.serialize(buffer, true) + } + buffer.appendInt32(481674261) buffer.appendInt32(Int32(users.count)) for item in users { item.serialize(buffer, true) @@ -1578,8 +1583,8 @@ public extension Api.stories { public func descriptionFields() -> (String, [(String, Any)]) { switch self { - case .storyViewsList(let flags, let count, let viewsCount, let forwardsCount, let reactionsCount, let views, let users, let nextOffset): - return ("storyViewsList", [("flags", flags as Any), ("count", count as Any), ("viewsCount", viewsCount as Any), ("forwardsCount", forwardsCount as Any), ("reactionsCount", reactionsCount as Any), ("views", views as Any), ("users", users as Any), ("nextOffset", nextOffset as Any)]) + case .storyViewsList(let flags, let count, let viewsCount, let forwardsCount, let reactionsCount, let views, let chats, let users, let nextOffset): + return ("storyViewsList", [("flags", flags as Any), ("count", count as Any), ("viewsCount", viewsCount as Any), ("forwardsCount", forwardsCount as Any), ("reactionsCount", reactionsCount as Any), ("views", views as Any), ("chats", chats as Any), ("users", users as Any), ("nextOffset", nextOffset as Any)]) } } @@ -1598,12 +1603,16 @@ public extension Api.stories { if let _ = reader.readInt32() { _6 = Api.parseVector(reader, elementSignature: 0, elementType: Api.StoryView.self) } - var _7: [Api.User]? + var _7: [Api.Chat]? if let _ = reader.readInt32() { - _7 = Api.parseVector(reader, elementSignature: 0, elementType: Api.User.self) + _7 = Api.parseVector(reader, elementSignature: 0, elementType: Api.Chat.self) } - var _8: String? - if Int(_1!) & Int(1 << 0) != 0 {_8 = parseString(reader) } + var _8: [Api.User]? + if let _ = reader.readInt32() { + _8 = Api.parseVector(reader, elementSignature: 0, elementType: Api.User.self) + } + var _9: String? + if Int(_1!) & Int(1 << 0) != 0 {_9 = parseString(reader) } let _c1 = _1 != nil let _c2 = _2 != nil let _c3 = _3 != nil @@ -1611,9 +1620,10 @@ public extension Api.stories { let _c5 = _5 != nil let _c6 = _6 != nil let _c7 = _7 != nil - let _c8 = (Int(_1!) & Int(1 << 0) == 0) || _8 != nil - if _c1 && _c2 && _c3 && _c4 && _c5 && _c6 && _c7 && _c8 { - return Api.stories.StoryViewsList.storyViewsList(flags: _1!, count: _2!, viewsCount: _3!, forwardsCount: _4!, reactionsCount: _5!, views: _6!, users: _7!, nextOffset: _8) + let _c8 = _8 != nil + let _c9 = (Int(_1!) & Int(1 << 0) == 0) || _9 != nil + if _c1 && _c2 && _c3 && _c4 && _c5 && _c6 && _c7 && _c8 && _c9 { + return Api.stories.StoryViewsList.storyViewsList(flags: _1!, count: _2!, viewsCount: _3!, forwardsCount: _4!, reactionsCount: _5!, views: _6!, chats: _7!, users: _8!, nextOffset: _9) } else { return nil diff --git a/submodules/TelegramApi/Sources/Api32.swift b/submodules/TelegramApi/Sources/Api32.swift index bbd7d3331c..9ce47f6456 100644 --- a/submodules/TelegramApi/Sources/Api32.swift +++ b/submodules/TelegramApi/Sources/Api32.swift @@ -328,6 +328,21 @@ public extension Api.functions.account { }) } } +public extension Api.functions.account { + static func getChannelRestrictedStatusEmojis(hash: Int64) -> (FunctionDescription, Buffer, DeserializeFunctionResponse) { + let buffer = Buffer() + buffer.appendInt32(900325589) + serializeInt64(hash, buffer: buffer, boxed: false) + return (FunctionDescription(name: "account.getChannelRestrictedStatusEmojis", parameters: [("hash", String(describing: hash))]), buffer, DeserializeFunctionResponse { (buffer: Buffer) -> Api.EmojiList? in + let reader = BufferReader(buffer) + var result: Api.EmojiList? + if let signature = reader.readInt32() { + result = Api.parse(reader, signature: signature) as? Api.EmojiList + } + return result + }) + } +} public extension Api.functions.account { static func getChatThemes(hash: Int64) -> (FunctionDescription, Buffer, DeserializeFunctionResponse) { let buffer = Buffer() @@ -3067,6 +3082,22 @@ public extension Api.functions.channels { }) } } +public extension Api.functions.channels { + static func updateEmojiStatus(channel: Api.InputChannel, emojiStatus: Api.EmojiStatus) -> (FunctionDescription, Buffer, DeserializeFunctionResponse) { + let buffer = Buffer() + buffer.appendInt32(-254548312) + channel.serialize(buffer, true) + emojiStatus.serialize(buffer, true) + return (FunctionDescription(name: "channels.updateEmojiStatus", parameters: [("channel", String(describing: channel)), ("emojiStatus", String(describing: emojiStatus))]), buffer, DeserializeFunctionResponse { (buffer: Buffer) -> Api.Updates? in + let reader = BufferReader(buffer) + var result: Api.Updates? + if let signature = reader.readInt32() { + result = Api.parse(reader, signature: signature) as? Api.Updates + } + return result + }) + } +} public extension Api.functions.channels { static func updatePinnedForumTopic(channel: Api.InputChannel, topicId: Int32, pinned: Api.Bool) -> (FunctionDescription, Buffer, DeserializeFunctionResponse) { let buffer = Buffer() @@ -8557,20 +8588,18 @@ public extension Api.functions.stats { } } public extension Api.functions.stats { - static func getMessagePublicForwards(channel: Api.InputChannel, msgId: Int32, offsetRate: Int32, offsetPeer: Api.InputPeer, offsetId: Int32, limit: Int32) -> (FunctionDescription, Buffer, DeserializeFunctionResponse) { + static func getMessagePublicForwards(channel: Api.InputChannel, msgId: Int32, offset: String, limit: Int32) -> (FunctionDescription, Buffer, DeserializeFunctionResponse) { let buffer = Buffer() - buffer.appendInt32(1445996571) + buffer.appendInt32(1595212100) channel.serialize(buffer, true) serializeInt32(msgId, buffer: buffer, boxed: false) - serializeInt32(offsetRate, buffer: buffer, boxed: false) - offsetPeer.serialize(buffer, true) - serializeInt32(offsetId, buffer: buffer, boxed: false) + serializeString(offset, buffer: buffer, boxed: false) serializeInt32(limit, buffer: buffer, boxed: false) - return (FunctionDescription(name: "stats.getMessagePublicForwards", parameters: [("channel", String(describing: channel)), ("msgId", String(describing: msgId)), ("offsetRate", String(describing: offsetRate)), ("offsetPeer", String(describing: offsetPeer)), ("offsetId", String(describing: offsetId)), ("limit", String(describing: limit))]), buffer, DeserializeFunctionResponse { (buffer: Buffer) -> Api.messages.Messages? in + return (FunctionDescription(name: "stats.getMessagePublicForwards", parameters: [("channel", String(describing: channel)), ("msgId", String(describing: msgId)), ("offset", String(describing: offset)), ("limit", String(describing: limit))]), buffer, DeserializeFunctionResponse { (buffer: Buffer) -> Api.stats.PublicForwards? in let reader = BufferReader(buffer) - var result: Api.messages.Messages? + var result: Api.stats.PublicForwards? if let signature = reader.readInt32() { - result = Api.parse(reader, signature: signature) as? Api.messages.Messages + result = Api.parse(reader, signature: signature) as? Api.stats.PublicForwards } return result }) diff --git a/submodules/TelegramCallsUI/Sources/VoiceChatParticipantItem.swift b/submodules/TelegramCallsUI/Sources/VoiceChatParticipantItem.swift index 1ed491fde8..3ab4562186 100644 --- a/submodules/TelegramCallsUI/Sources/VoiceChatParticipantItem.swift +++ b/submodules/TelegramCallsUI/Sources/VoiceChatParticipantItem.swift @@ -839,7 +839,7 @@ class VoiceChatParticipantItemNode: ItemListRevealOptionsItemNode { credibilityIcon = .text(color: item.presentationData.theme.chat.message.incoming.scamColor, string: item.presentationData.strings.Message_ScamAccount.uppercased()) } else if item.peer.isFake { credibilityIcon = .text(color: item.presentationData.theme.chat.message.incoming.scamColor, string: item.presentationData.strings.Message_FakeAccount.uppercased()) - } else if case let .user(user) = item.peer, let emojiStatus = user.emojiStatus, !premiumConfiguration.isPremiumDisabled { + } else if let emojiStatus = item.peer.emojiStatus, !premiumConfiguration.isPremiumDisabled { 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) diff --git a/submodules/TelegramCore/Sources/Account/AccountIntermediateState.swift b/submodules/TelegramCore/Sources/Account/AccountIntermediateState.swift index 4ab1e3a94c..310b8b36de 100644 --- a/submodules/TelegramCore/Sources/Account/AccountIntermediateState.swift +++ b/submodules/TelegramCore/Sources/Account/AccountIntermediateState.swift @@ -469,7 +469,7 @@ struct AccountMutableState { for chat in chats { switch chat { - case let .channel(_, _, _, _, _, _, _, _, _, _, _, _, participantsCount, _, _, _, _): + case let .channel(_, _, _, _, _, _, _, _, _, _, _, _, participantsCount, _, _, _, _, _, _): if let participantsCount = participantsCount { self.addOperation(.UpdateCachedPeerData(chat.peerId, { current in var previous: CachedChannelData diff --git a/submodules/TelegramCore/Sources/ApiUtils/ApiGroupOrChannel.swift b/submodules/TelegramCore/Sources/ApiUtils/ApiGroupOrChannel.swift index 1cf7db0a2d..a288585d95 100644 --- a/submodules/TelegramCore/Sources/ApiUtils/ApiGroupOrChannel.swift +++ b/submodules/TelegramCore/Sources/ApiUtils/ApiGroupOrChannel.swift @@ -61,7 +61,7 @@ func parseTelegramGroupOrChannel(chat: Api.Chat) -> Peer? { return TelegramGroup(id: PeerId(namespace: Namespaces.Peer.CloudGroup, id: PeerId.Id._internalFromInt64Value(id)), title: "", photo: [], participantCount: 0, role: .member, membership: .Removed, flags: [], defaultBannedRights: nil, migrationReference: nil, creationDate: 0, version: 0) case let .chatForbidden(id, title): return TelegramGroup(id: PeerId(namespace: Namespaces.Peer.CloudGroup, id: PeerId.Id._internalFromInt64Value(id)), title: title, photo: [], participantCount: 0, role: .member, membership: .Removed, flags: [], defaultBannedRights: nil, migrationReference: nil, creationDate: 0, version: 0) - case let .channel(flags, flags2, id, accessHash, title, username, photo, date, restrictionReason, adminRights, bannedRights, defaultBannedRights, _, usernames, _, color, profileColor): + case let .channel(flags, flags2, id, accessHash, title, username, photo, date, restrictionReason, adminRights, bannedRights, defaultBannedRights, _, usernames, _, color, profileColor, emojiStatus, boostLevel): let isMin = (flags & (1 << 12)) != 0 let participationStatus: TelegramChannelParticipationStatus @@ -173,7 +173,7 @@ func parseTelegramGroupOrChannel(chat: Api.Chat) -> Peer? { } } - return TelegramChannel(id: PeerId(namespace: Namespaces.Peer.CloudChannel, id: PeerId.Id._internalFromInt64Value(id)), accessHash: accessHashValue, title: title, username: username, photo: imageRepresentationsForApiChatPhoto(photo), creationDate: date, version: 0, participationStatus: participationStatus, info: info, flags: channelFlags, restrictionInfo: restrictionInfo, adminRights: adminRights.flatMap(TelegramChatAdminRights.init), bannedRights: bannedRights.flatMap(TelegramChatBannedRights.init), defaultBannedRights: defaultBannedRights.flatMap(TelegramChatBannedRights.init), usernames: usernames?.map(TelegramPeerUsername.init(apiUsername:)) ?? [], storiesHidden: storiesHidden, nameColor: nameColorIndex.flatMap { PeerNameColor(rawValue: $0) }, backgroundEmojiId: backgroundEmojiId, profileColor: profileColorIndex.flatMap { PeerNameColor(rawValue: $0) }, profileBackgroundEmojiId: profileBackgroundEmojiId, emojiStatus: nil) + return TelegramChannel(id: PeerId(namespace: Namespaces.Peer.CloudChannel, id: PeerId.Id._internalFromInt64Value(id)), accessHash: accessHashValue, title: title, username: username, photo: imageRepresentationsForApiChatPhoto(photo), creationDate: date, version: 0, participationStatus: participationStatus, info: info, flags: channelFlags, restrictionInfo: restrictionInfo, adminRights: adminRights.flatMap(TelegramChatAdminRights.init), bannedRights: bannedRights.flatMap(TelegramChatBannedRights.init), defaultBannedRights: defaultBannedRights.flatMap(TelegramChatBannedRights.init), usernames: usernames?.map(TelegramPeerUsername.init(apiUsername:)) ?? [], storiesHidden: storiesHidden, nameColor: nameColorIndex.flatMap { PeerNameColor(rawValue: $0) }, backgroundEmojiId: backgroundEmojiId, profileColor: profileColorIndex.flatMap { PeerNameColor(rawValue: $0) }, profileBackgroundEmojiId: profileBackgroundEmojiId, emojiStatus: emojiStatus.flatMap(PeerEmojiStatus.init(apiStatus:)), approximateBoostLevel: boostLevel) case let .channelForbidden(flags, id, accessHash, title, untilDate): let info: TelegramChannelInfo if (flags & Int32(1 << 8)) != 0 { @@ -182,7 +182,7 @@ func parseTelegramGroupOrChannel(chat: Api.Chat) -> Peer? { info = .broadcast(TelegramChannelBroadcastInfo(flags: [])) } - return TelegramChannel(id: PeerId(namespace: Namespaces.Peer.CloudChannel, id: PeerId.Id._internalFromInt64Value(id)), accessHash: .personal(accessHash), title: title, username: nil, photo: [], creationDate: 0, version: 0, participationStatus: .kicked, info: info, flags: TelegramChannelFlags(), restrictionInfo: nil, adminRights: nil, bannedRights: TelegramChatBannedRights(flags: [.banReadMessages], untilDate: untilDate ?? Int32.max), defaultBannedRights: nil, usernames: [], storiesHidden: nil, nameColor: nil, backgroundEmojiId: nil, profileColor: nil, profileBackgroundEmojiId: nil, emojiStatus: nil) + return TelegramChannel(id: PeerId(namespace: Namespaces.Peer.CloudChannel, id: PeerId.Id._internalFromInt64Value(id)), accessHash: .personal(accessHash), title: title, username: nil, photo: [], creationDate: 0, version: 0, participationStatus: .kicked, info: info, flags: TelegramChannelFlags(), restrictionInfo: nil, adminRights: nil, bannedRights: TelegramChatBannedRights(flags: [.banReadMessages], untilDate: untilDate ?? Int32.max), defaultBannedRights: nil, usernames: [], storiesHidden: nil, nameColor: nil, backgroundEmojiId: nil, profileColor: nil, profileBackgroundEmojiId: nil, emojiStatus: nil, approximateBoostLevel: nil) } } @@ -190,7 +190,7 @@ func mergeGroupOrChannel(lhs: Peer?, rhs: Api.Chat) -> Peer? { switch rhs { case .chat, .chatEmpty, .chatForbidden, .channelForbidden: return parseTelegramGroupOrChannel(chat: rhs) - case let .channel(flags, flags2, _, accessHash, title, username, photo, _, _, _, _, defaultBannedRights, _, usernames, _, color, profileColor): + case let .channel(flags, flags2, _, accessHash, title, username, photo, _, _, _, _, defaultBannedRights, _, usernames, _, color, profileColor, emojiStatus, boostLevel): let isMin = (flags & (1 << 12)) != 0 if accessHash != nil && !isMin { return parseTelegramGroupOrChannel(chat: rhs) @@ -252,9 +252,9 @@ func mergeGroupOrChannel(lhs: Peer?, rhs: Api.Chat) -> Peer? { } } - let emojiStatus = lhs.emojiStatus + let parsedEmojiStatus = emojiStatus.flatMap(PeerEmojiStatus.init(apiStatus:)) - return TelegramChannel(id: lhs.id, accessHash: lhs.accessHash, title: title, username: username, photo: imageRepresentationsForApiChatPhoto(photo), creationDate: lhs.creationDate, version: lhs.version, participationStatus: lhs.participationStatus, info: info, flags: channelFlags, restrictionInfo: lhs.restrictionInfo, adminRights: lhs.adminRights, bannedRights: lhs.bannedRights, defaultBannedRights: defaultBannedRights.flatMap(TelegramChatBannedRights.init), usernames: usernames?.map(TelegramPeerUsername.init(apiUsername:)) ?? [], storiesHidden: storiesHidden, nameColor: nameColorIndex.flatMap { PeerNameColor(rawValue: $0) }, backgroundEmojiId: backgroundEmojiId, profileColor: profileColorIndex.flatMap { PeerNameColor(rawValue: $0) }, profileBackgroundEmojiId: profileBackgroundEmojiId, emojiStatus: emojiStatus) + return TelegramChannel(id: lhs.id, accessHash: lhs.accessHash, title: title, username: username, photo: imageRepresentationsForApiChatPhoto(photo), creationDate: lhs.creationDate, version: lhs.version, participationStatus: lhs.participationStatus, info: info, flags: channelFlags, restrictionInfo: lhs.restrictionInfo, adminRights: lhs.adminRights, bannedRights: lhs.bannedRights, defaultBannedRights: defaultBannedRights.flatMap(TelegramChatBannedRights.init), usernames: usernames?.map(TelegramPeerUsername.init(apiUsername:)) ?? [], storiesHidden: storiesHidden, nameColor: nameColorIndex.flatMap { PeerNameColor(rawValue: $0) }, backgroundEmojiId: backgroundEmojiId, profileColor: profileColorIndex.flatMap { PeerNameColor(rawValue: $0) }, profileBackgroundEmojiId: profileBackgroundEmojiId, emojiStatus: parsedEmojiStatus, approximateBoostLevel: boostLevel) } else { return parseTelegramGroupOrChannel(chat: rhs) } @@ -308,6 +308,6 @@ func mergeChannel(lhs: TelegramChannel?, rhs: TelegramChannel) -> TelegramChanne let storiesHidden: Bool? = rhs.storiesHidden ?? lhs.storiesHidden - return TelegramChannel(id: lhs.id, accessHash: accessHash, title: rhs.title, username: rhs.username, photo: rhs.photo, creationDate: rhs.creationDate, version: rhs.version, participationStatus: lhs.participationStatus, info: info, flags: channelFlags, restrictionInfo: rhs.restrictionInfo, adminRights: rhs.adminRights, bannedRights: rhs.bannedRights, defaultBannedRights: rhs.defaultBannedRights, usernames: rhs.usernames, storiesHidden: storiesHidden, nameColor: rhs.nameColor, backgroundEmojiId: rhs.backgroundEmojiId, profileColor: rhs.profileColor, profileBackgroundEmojiId: rhs.profileBackgroundEmojiId, emojiStatus: rhs.emojiStatus) + return TelegramChannel(id: lhs.id, accessHash: accessHash, title: rhs.title, username: rhs.username, photo: rhs.photo, creationDate: rhs.creationDate, version: rhs.version, participationStatus: lhs.participationStatus, info: info, flags: channelFlags, restrictionInfo: rhs.restrictionInfo, adminRights: rhs.adminRights, bannedRights: rhs.bannedRights, defaultBannedRights: rhs.defaultBannedRights, usernames: rhs.usernames, storiesHidden: storiesHidden, nameColor: rhs.nameColor, backgroundEmojiId: rhs.backgroundEmojiId, profileColor: rhs.profileColor, profileBackgroundEmojiId: rhs.profileBackgroundEmojiId, emojiStatus: rhs.emojiStatus, approximateBoostLevel: rhs.approximateBoostLevel) } diff --git a/submodules/TelegramCore/Sources/ApiUtils/StoreMessage_Telegram.swift b/submodules/TelegramCore/Sources/ApiUtils/StoreMessage_Telegram.swift index 5f6a22b9a1..90d04dffe1 100644 --- a/submodules/TelegramCore/Sources/ApiUtils/StoreMessage_Telegram.swift +++ b/submodules/TelegramCore/Sources/ApiUtils/StoreMessage_Telegram.swift @@ -451,7 +451,7 @@ func mediaAreaFromApiMediaArea(_ mediaArea: Api.MediaArea) -> MediaArea? { } } switch mediaArea { - case .inputMediaAreaVenue: + case .inputMediaAreaVenue, .inputMediaAreaChannelPost: return nil case let .mediaAreaGeoPoint(coordinates, geo): let latitude: Double @@ -490,6 +490,8 @@ func mediaAreaFromApiMediaArea(_ mediaArea: Api.MediaArea) -> MediaArea? { } else { return nil } + case .mediaAreaChannelPost: + return nil } } diff --git a/submodules/TelegramCore/Sources/State/UpdatesApiUtils.swift b/submodules/TelegramCore/Sources/State/UpdatesApiUtils.swift index fbd58bf03b..534baeba6b 100644 --- a/submodules/TelegramCore/Sources/State/UpdatesApiUtils.swift +++ b/submodules/TelegramCore/Sources/State/UpdatesApiUtils.swift @@ -182,7 +182,7 @@ extension Api.Chat { return PeerId(namespace: Namespaces.Peer.CloudGroup, id: PeerId.Id._internalFromInt64Value(id)) case let .chatForbidden(id, _): return PeerId(namespace: Namespaces.Peer.CloudGroup, id: PeerId.Id._internalFromInt64Value(id)) - case let .channel(_, _, id, _, _, _, _, _, _, _, _, _, _, _, _, _, _): + case let .channel(_, _, id, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _): return PeerId(namespace: Namespaces.Peer.CloudChannel, id: PeerId.Id._internalFromInt64Value(id)) case let .channelForbidden(_, id, _, _, _): return PeerId(namespace: Namespaces.Peer.CloudChannel, id: PeerId.Id._internalFromInt64Value(id)) diff --git a/submodules/TelegramCore/Sources/SyncCore/SyncCore_StickerPackCollectionInfo.swift b/submodules/TelegramCore/Sources/SyncCore/SyncCore_StickerPackCollectionInfo.swift index f0898df6e3..2c40edf798 100644 --- a/submodules/TelegramCore/Sources/SyncCore/SyncCore_StickerPackCollectionInfo.swift +++ b/submodules/TelegramCore/Sources/SyncCore/SyncCore_StickerPackCollectionInfo.swift @@ -30,6 +30,9 @@ public struct StickerPackCollectionInfoFlags: OptionSet { if flags.contains(StickerPackCollectionInfoFlags.isEmoji) { rawValue |= StickerPackCollectionInfoFlags.isEmoji.rawValue } + if flags.contains(StickerPackCollectionInfoFlags.isAvailableAsChannelStatus) { + rawValue |= StickerPackCollectionInfoFlags.isAvailableAsChannelStatus.rawValue + } self.rawValue = rawValue } @@ -39,6 +42,7 @@ public struct StickerPackCollectionInfoFlags: OptionSet { public static let isAnimated = StickerPackCollectionInfoFlags(rawValue: 1 << 2) public static let isVideo = StickerPackCollectionInfoFlags(rawValue: 1 << 3) public static let isEmoji = StickerPackCollectionInfoFlags(rawValue: 1 << 4) + public static let isAvailableAsChannelStatus = StickerPackCollectionInfoFlags(rawValue: 1 << 5) } diff --git a/submodules/TelegramCore/Sources/SyncCore/SyncCore_TelegramChannel.swift b/submodules/TelegramCore/Sources/SyncCore/SyncCore_TelegramChannel.swift index 4f0b779a0c..717c902f4e 100644 --- a/submodules/TelegramCore/Sources/SyncCore/SyncCore_TelegramChannel.swift +++ b/submodules/TelegramCore/Sources/SyncCore/SyncCore_TelegramChannel.swift @@ -172,6 +172,7 @@ public final class TelegramChannel: Peer, Equatable { public let profileColor: PeerNameColor? public let profileBackgroundEmojiId: Int64? public let emojiStatus: PeerEmojiStatus? + public let approximateBoostLevel: Int32? public var indexName: PeerIndexNameRepresentation { var addressNames = self.usernames.map { $0.username } @@ -236,7 +237,8 @@ public final class TelegramChannel: Peer, Equatable { backgroundEmojiId: Int64?, profileColor: PeerNameColor?, profileBackgroundEmojiId: Int64?, - emojiStatus: PeerEmojiStatus? + emojiStatus: PeerEmojiStatus?, + approximateBoostLevel: Int32? ) { self.id = id self.accessHash = accessHash @@ -259,6 +261,7 @@ public final class TelegramChannel: Peer, Equatable { self.profileColor = profileColor self.profileBackgroundEmojiId = profileBackgroundEmojiId self.emojiStatus = emojiStatus + self.approximateBoostLevel = approximateBoostLevel } public init(decoder: PostboxDecoder) { @@ -293,6 +296,7 @@ public final class TelegramChannel: Peer, Equatable { self.profileColor = decoder.decodeOptionalInt32ForKey("pclr").flatMap { PeerNameColor(rawValue: $0) } self.profileBackgroundEmojiId = decoder.decodeOptionalInt64ForKey("pgem") self.emojiStatus = decoder.decode(PeerEmojiStatus.self, forKey: "emjs") + self.approximateBoostLevel = decoder.decodeOptionalInt32ForKey("abl") } public func encode(_ encoder: PostboxEncoder) { @@ -378,6 +382,12 @@ public final class TelegramChannel: Peer, Equatable { } else { encoder.encodeNil(forKey: "emjs") } + + if let approximateBoostLevel = self.approximateBoostLevel { + encoder.encodeInt32(approximateBoostLevel, forKey: "abl") + } else { + encoder.encodeNil(forKey: "abl") + } } public func isEqual(_ other: Peer) -> Bool { @@ -433,47 +443,54 @@ public final class TelegramChannel: Peer, Equatable { if lhs.emojiStatus != rhs.emojiStatus { return false } + if lhs.approximateBoostLevel != rhs.approximateBoostLevel { + return false + } return true } public func withUpdatedAddressName(_ addressName: String?) -> TelegramChannel { - return TelegramChannel(id: self.id, accessHash: self.accessHash, title: self.title, username: addressName, photo: self.photo, creationDate: self.creationDate, version: self.version, participationStatus: self.participationStatus, info: self.info, flags: self.flags, restrictionInfo: self.restrictionInfo, adminRights: self.adminRights, bannedRights: self.bannedRights, defaultBannedRights: self.defaultBannedRights, usernames: self.usernames, storiesHidden: self.storiesHidden, nameColor: self.nameColor, backgroundEmojiId: self.backgroundEmojiId, profileColor: self.profileColor, profileBackgroundEmojiId: self.profileBackgroundEmojiId, emojiStatus: self.emojiStatus) + return TelegramChannel(id: self.id, accessHash: self.accessHash, title: self.title, username: addressName, photo: self.photo, creationDate: self.creationDate, version: self.version, participationStatus: self.participationStatus, info: self.info, flags: self.flags, restrictionInfo: self.restrictionInfo, adminRights: self.adminRights, bannedRights: self.bannedRights, defaultBannedRights: self.defaultBannedRights, usernames: self.usernames, storiesHidden: self.storiesHidden, nameColor: self.nameColor, backgroundEmojiId: self.backgroundEmojiId, profileColor: self.profileColor, profileBackgroundEmojiId: self.profileBackgroundEmojiId, emojiStatus: self.emojiStatus, approximateBoostLevel: self.approximateBoostLevel) } public func withUpdatedAddressNames(_ addressNames: [TelegramPeerUsername]) -> TelegramChannel { - return TelegramChannel(id: self.id, accessHash: self.accessHash, title: self.title, username: self.username, photo: self.photo, creationDate: self.creationDate, version: self.version, participationStatus: self.participationStatus, info: self.info, flags: self.flags, restrictionInfo: self.restrictionInfo, adminRights: self.adminRights, bannedRights: self.bannedRights, defaultBannedRights: self.defaultBannedRights, usernames: addressNames, storiesHidden: self.storiesHidden, nameColor: self.nameColor, backgroundEmojiId: self.backgroundEmojiId, profileColor: self.profileColor, profileBackgroundEmojiId: self.profileBackgroundEmojiId, emojiStatus: self.emojiStatus) + return TelegramChannel(id: self.id, accessHash: self.accessHash, title: self.title, username: self.username, photo: self.photo, creationDate: self.creationDate, version: self.version, participationStatus: self.participationStatus, info: self.info, flags: self.flags, restrictionInfo: self.restrictionInfo, adminRights: self.adminRights, bannedRights: self.bannedRights, defaultBannedRights: self.defaultBannedRights, usernames: addressNames, storiesHidden: self.storiesHidden, nameColor: self.nameColor, backgroundEmojiId: self.backgroundEmojiId, profileColor: self.profileColor, profileBackgroundEmojiId: self.profileBackgroundEmojiId, emojiStatus: self.emojiStatus, approximateBoostLevel: self.approximateBoostLevel) } public func withUpdatedDefaultBannedRights(_ defaultBannedRights: TelegramChatBannedRights?) -> TelegramChannel { - return TelegramChannel(id: self.id, accessHash: self.accessHash, title: self.title, username: self.username, photo: self.photo, creationDate: self.creationDate, version: self.version, participationStatus: self.participationStatus, info: self.info, flags: self.flags, restrictionInfo: self.restrictionInfo, adminRights: self.adminRights, bannedRights: self.bannedRights, defaultBannedRights: defaultBannedRights, usernames: self.usernames, storiesHidden: self.storiesHidden, nameColor: self.nameColor, backgroundEmojiId: self.backgroundEmojiId, profileColor: self.profileColor, profileBackgroundEmojiId: self.profileBackgroundEmojiId, emojiStatus: self.emojiStatus) + return TelegramChannel(id: self.id, accessHash: self.accessHash, title: self.title, username: self.username, photo: self.photo, creationDate: self.creationDate, version: self.version, participationStatus: self.participationStatus, info: self.info, flags: self.flags, restrictionInfo: self.restrictionInfo, adminRights: self.adminRights, bannedRights: self.bannedRights, defaultBannedRights: defaultBannedRights, usernames: self.usernames, storiesHidden: self.storiesHidden, nameColor: self.nameColor, backgroundEmojiId: self.backgroundEmojiId, profileColor: self.profileColor, profileBackgroundEmojiId: self.profileBackgroundEmojiId, emojiStatus: self.emojiStatus, approximateBoostLevel: self.approximateBoostLevel) } public func withUpdatedFlags(_ flags: TelegramChannelFlags) -> TelegramChannel { - return TelegramChannel(id: self.id, accessHash: self.accessHash, title: self.title, username: self.username, photo: self.photo, creationDate: self.creationDate, version: self.version, participationStatus: self.participationStatus, info: self.info, flags: flags, restrictionInfo: self.restrictionInfo, adminRights: self.adminRights, bannedRights: self.bannedRights, defaultBannedRights: self.defaultBannedRights, usernames: self.usernames, storiesHidden: self.storiesHidden, nameColor: self.nameColor, backgroundEmojiId: self.backgroundEmojiId, profileColor: self.profileColor, profileBackgroundEmojiId: self.profileBackgroundEmojiId, emojiStatus: self.emojiStatus) + return TelegramChannel(id: self.id, accessHash: self.accessHash, title: self.title, username: self.username, photo: self.photo, creationDate: self.creationDate, version: self.version, participationStatus: self.participationStatus, info: self.info, flags: flags, restrictionInfo: self.restrictionInfo, adminRights: self.adminRights, bannedRights: self.bannedRights, defaultBannedRights: self.defaultBannedRights, usernames: self.usernames, storiesHidden: self.storiesHidden, nameColor: self.nameColor, backgroundEmojiId: self.backgroundEmojiId, profileColor: self.profileColor, profileBackgroundEmojiId: self.profileBackgroundEmojiId, emojiStatus: self.emojiStatus, approximateBoostLevel: self.approximateBoostLevel) } public func withUpdatedStoriesHidden(_ storiesHidden: Bool?) -> TelegramChannel { - return TelegramChannel(id: self.id, accessHash: self.accessHash, title: self.title, username: self.username, photo: self.photo, creationDate: self.creationDate, version: self.version, participationStatus: self.participationStatus, info: self.info, flags: self.flags, restrictionInfo: self.restrictionInfo, adminRights: self.adminRights, bannedRights: self.bannedRights, defaultBannedRights: self.defaultBannedRights, usernames: self.usernames, storiesHidden: storiesHidden, nameColor: self.nameColor, backgroundEmojiId: self.backgroundEmojiId, profileColor: self.profileColor, profileBackgroundEmojiId: self.profileBackgroundEmojiId, emojiStatus: self.emojiStatus) + return TelegramChannel(id: self.id, accessHash: self.accessHash, title: self.title, username: self.username, photo: self.photo, creationDate: self.creationDate, version: self.version, participationStatus: self.participationStatus, info: self.info, flags: self.flags, restrictionInfo: self.restrictionInfo, adminRights: self.adminRights, bannedRights: self.bannedRights, defaultBannedRights: self.defaultBannedRights, usernames: self.usernames, storiesHidden: storiesHidden, nameColor: self.nameColor, backgroundEmojiId: self.backgroundEmojiId, profileColor: self.profileColor, profileBackgroundEmojiId: self.profileBackgroundEmojiId, emojiStatus: self.emojiStatus, approximateBoostLevel: self.approximateBoostLevel) } public func withUpdatedNameColor(_ nameColor: PeerNameColor?) -> TelegramChannel { - return TelegramChannel(id: self.id, accessHash: self.accessHash, title: self.title, username: self.username, photo: self.photo, creationDate: self.creationDate, version: self.version, participationStatus: self.participationStatus, info: self.info, flags: self.flags, restrictionInfo: self.restrictionInfo, adminRights: self.adminRights, bannedRights: self.bannedRights, defaultBannedRights: self.defaultBannedRights, usernames: self.usernames, storiesHidden: self.storiesHidden, nameColor: nameColor, backgroundEmojiId: self.backgroundEmojiId, profileColor: self.profileColor, profileBackgroundEmojiId: self.profileBackgroundEmojiId, emojiStatus: self.emojiStatus) + return TelegramChannel(id: self.id, accessHash: self.accessHash, title: self.title, username: self.username, photo: self.photo, creationDate: self.creationDate, version: self.version, participationStatus: self.participationStatus, info: self.info, flags: self.flags, restrictionInfo: self.restrictionInfo, adminRights: self.adminRights, bannedRights: self.bannedRights, defaultBannedRights: self.defaultBannedRights, usernames: self.usernames, storiesHidden: self.storiesHidden, nameColor: nameColor, backgroundEmojiId: self.backgroundEmojiId, profileColor: self.profileColor, profileBackgroundEmojiId: self.profileBackgroundEmojiId, emojiStatus: self.emojiStatus, approximateBoostLevel: self.approximateBoostLevel) } public func withUpdatedBackgroundEmojiId(_ backgroundEmojiId: Int64?) -> TelegramChannel { - return TelegramChannel(id: self.id, accessHash: self.accessHash, title: self.title, username: self.username, photo: self.photo, creationDate: self.creationDate, version: self.version, participationStatus: self.participationStatus, info: self.info, flags: self.flags, restrictionInfo: self.restrictionInfo, adminRights: self.adminRights, bannedRights: self.bannedRights, defaultBannedRights: self.defaultBannedRights, usernames: self.usernames, storiesHidden: self.storiesHidden, nameColor: self.nameColor, backgroundEmojiId: backgroundEmojiId, profileColor: self.profileColor, profileBackgroundEmojiId: self.profileBackgroundEmojiId, emojiStatus: self.emojiStatus) + return TelegramChannel(id: self.id, accessHash: self.accessHash, title: self.title, username: self.username, photo: self.photo, creationDate: self.creationDate, version: self.version, participationStatus: self.participationStatus, info: self.info, flags: self.flags, restrictionInfo: self.restrictionInfo, adminRights: self.adminRights, bannedRights: self.bannedRights, defaultBannedRights: self.defaultBannedRights, usernames: self.usernames, storiesHidden: self.storiesHidden, nameColor: self.nameColor, backgroundEmojiId: backgroundEmojiId, profileColor: self.profileColor, profileBackgroundEmojiId: self.profileBackgroundEmojiId, emojiStatus: self.emojiStatus, approximateBoostLevel: self.approximateBoostLevel) } public func withUpdatedProfileColor(_ profileColor: PeerNameColor?) -> TelegramChannel { - return TelegramChannel(id: self.id, accessHash: self.accessHash, title: self.title, username: self.username, photo: self.photo, creationDate: self.creationDate, version: self.version, participationStatus: self.participationStatus, info: self.info, flags: self.flags, restrictionInfo: self.restrictionInfo, adminRights: self.adminRights, bannedRights: self.bannedRights, defaultBannedRights: self.defaultBannedRights, usernames: self.usernames, storiesHidden: self.storiesHidden, nameColor: self.nameColor, backgroundEmojiId: self.backgroundEmojiId, profileColor: profileColor, profileBackgroundEmojiId: self.profileBackgroundEmojiId, emojiStatus: self.emojiStatus) + return TelegramChannel(id: self.id, accessHash: self.accessHash, title: self.title, username: self.username, photo: self.photo, creationDate: self.creationDate, version: self.version, participationStatus: self.participationStatus, info: self.info, flags: self.flags, restrictionInfo: self.restrictionInfo, adminRights: self.adminRights, bannedRights: self.bannedRights, defaultBannedRights: self.defaultBannedRights, usernames: self.usernames, storiesHidden: self.storiesHidden, nameColor: self.nameColor, backgroundEmojiId: self.backgroundEmojiId, profileColor: profileColor, profileBackgroundEmojiId: self.profileBackgroundEmojiId, emojiStatus: self.emojiStatus, approximateBoostLevel: self.approximateBoostLevel) } public func withUpdatedProfileBackgroundEmojiId(_ profileBackgroundEmojiId: Int64?) -> TelegramChannel { - return TelegramChannel(id: self.id, accessHash: self.accessHash, title: self.title, username: self.username, photo: self.photo, creationDate: self.creationDate, version: self.version, participationStatus: self.participationStatus, info: self.info, flags: self.flags, restrictionInfo: self.restrictionInfo, adminRights: self.adminRights, bannedRights: self.bannedRights, defaultBannedRights: self.defaultBannedRights, usernames: self.usernames, storiesHidden: self.storiesHidden, nameColor: self.nameColor, backgroundEmojiId: self.backgroundEmojiId, profileColor: self.profileColor, profileBackgroundEmojiId: profileBackgroundEmojiId, emojiStatus: self.emojiStatus) + return TelegramChannel(id: self.id, accessHash: self.accessHash, title: self.title, username: self.username, photo: self.photo, creationDate: self.creationDate, version: self.version, participationStatus: self.participationStatus, info: self.info, flags: self.flags, restrictionInfo: self.restrictionInfo, adminRights: self.adminRights, bannedRights: self.bannedRights, defaultBannedRights: self.defaultBannedRights, usernames: self.usernames, storiesHidden: self.storiesHidden, nameColor: self.nameColor, backgroundEmojiId: self.backgroundEmojiId, profileColor: self.profileColor, profileBackgroundEmojiId: profileBackgroundEmojiId, emojiStatus: self.emojiStatus, approximateBoostLevel: self.approximateBoostLevel) } public func withUpdatedEmojiStatus(_ emojiStatus: PeerEmojiStatus?) -> TelegramChannel { - return TelegramChannel(id: self.id, accessHash: self.accessHash, title: self.title, username: self.username, photo: self.photo, creationDate: self.creationDate, version: self.version, participationStatus: self.participationStatus, info: self.info, flags: self.flags, restrictionInfo: self.restrictionInfo, adminRights: self.adminRights, bannedRights: self.bannedRights, defaultBannedRights: self.defaultBannedRights, usernames: self.usernames, storiesHidden: self.storiesHidden, nameColor: self.nameColor, backgroundEmojiId: self.backgroundEmojiId, profileColor: self.profileColor, profileBackgroundEmojiId: self.profileBackgroundEmojiId, emojiStatus: emojiStatus) + return TelegramChannel(id: self.id, accessHash: self.accessHash, title: self.title, username: self.username, photo: self.photo, creationDate: self.creationDate, version: self.version, participationStatus: self.participationStatus, info: self.info, flags: self.flags, restrictionInfo: self.restrictionInfo, adminRights: self.adminRights, bannedRights: self.bannedRights, defaultBannedRights: self.defaultBannedRights, usernames: self.usernames, storiesHidden: self.storiesHidden, nameColor: self.nameColor, backgroundEmojiId: self.backgroundEmojiId, profileColor: self.profileColor, profileBackgroundEmojiId: self.profileBackgroundEmojiId, emojiStatus: emojiStatus, approximateBoostLevel: self.approximateBoostLevel) + } + + public func withUpdatedApproximateBoostLevel(_ approximateBoostLevel: Int32?) -> TelegramChannel { + return TelegramChannel(id: self.id, accessHash: self.accessHash, title: self.title, username: self.username, photo: self.photo, creationDate: self.creationDate, version: self.version, participationStatus: self.participationStatus, info: self.info, flags: self.flags, restrictionInfo: self.restrictionInfo, adminRights: self.adminRights, bannedRights: self.bannedRights, defaultBannedRights: self.defaultBannedRights, usernames: self.usernames, storiesHidden: self.storiesHidden, nameColor: self.nameColor, backgroundEmojiId: self.backgroundEmojiId, profileColor: self.profileColor, profileBackgroundEmojiId: self.profileBackgroundEmojiId, emojiStatus: self.emojiStatus, approximateBoostLevel: approximateBoostLevel) } } diff --git a/submodules/TelegramCore/Sources/TelegramEngine/Calls/GroupCalls.swift b/submodules/TelegramCore/Sources/TelegramEngine/Calls/GroupCalls.swift index 3d43d551d3..1531576a48 100644 --- a/submodules/TelegramCore/Sources/TelegramEngine/Calls/GroupCalls.swift +++ b/submodules/TelegramCore/Sources/TelegramEngine/Calls/GroupCalls.swift @@ -2254,7 +2254,7 @@ func _internal_groupCallDisplayAsAvailablePeers(accountPeerId: PeerId, network: for chat in chats { if let groupOrChannel = parseTelegramGroupOrChannel(chat: chat) { switch chat { - case let .channel(_, _, _, _, _, _, _, _, _, _, _, _, participantsCount, _, _, _, _): + case let .channel(_, _, _, _, _, _, _, _, _, _, _, _, participantsCount, _, _, _, _, _, _): if let participantsCount = participantsCount { subscribers[groupOrChannel.id] = participantsCount } diff --git a/submodules/TelegramCore/Sources/TelegramEngine/Messages/AdMessages.swift b/submodules/TelegramCore/Sources/TelegramEngine/Messages/AdMessages.swift index 38eb51b49e..f35211a3e9 100644 --- a/submodules/TelegramCore/Sources/TelegramEngine/Messages/AdMessages.swift +++ b/submodules/TelegramCore/Sources/TelegramEngine/Messages/AdMessages.swift @@ -316,7 +316,8 @@ private class AdMessagesHistoryContextImpl { backgroundEmojiId: nil, profileColor: nil, profileBackgroundEmojiId: nil, - emojiStatus: nil + emojiStatus: nil, + approximateBoostLevel: nil ) case let .webPage(webPage): author = TelegramChannel( @@ -340,7 +341,8 @@ private class AdMessagesHistoryContextImpl { backgroundEmojiId: nil, profileColor: nil, profileBackgroundEmojiId: nil, - emojiStatus: nil + emojiStatus: nil, + approximateBoostLevel: nil ) } diff --git a/submodules/TelegramCore/Sources/TelegramEngine/Messages/EngineStoryViewListContext.swift b/submodules/TelegramCore/Sources/TelegramEngine/Messages/EngineStoryViewListContext.swift index ebe34d8d56..46e111e301 100644 --- a/submodules/TelegramCore/Sources/TelegramEngine/Messages/EngineStoryViewListContext.swift +++ b/submodules/TelegramCore/Sources/TelegramEngine/Messages/EngineStoryViewListContext.swift @@ -465,8 +465,8 @@ public final class EngineStoryViewListContext { |> mapToSignal { result -> Signal in return account.postbox.transaction { transaction -> InternalState in switch result { - case let .storyViewsList(_, count, viewsCount, forwardsCount, reactionsCount, views, users, nextOffset): - let peers = AccumulatedPeers(users: users) + case let .storyViewsList(_, count, viewsCount, forwardsCount, reactionsCount, views, chats, users, nextOffset): + let peers = AccumulatedPeers(chats: chats, users: users) updatePeers(transaction: transaction, accountPeerId: accountPeerId, peers: peers) var items: [Item] = [] diff --git a/submodules/TelegramCore/Sources/TelegramEngine/Messages/SearchMessages.swift b/submodules/TelegramCore/Sources/TelegramEngine/Messages/SearchMessages.swift index 09740d2c94..73440ad37b 100644 --- a/submodules/TelegramCore/Sources/TelegramEngine/Messages/SearchMessages.swift +++ b/submodules/TelegramCore/Sources/TelegramEngine/Messages/SearchMessages.swift @@ -375,7 +375,11 @@ func _internal_searchMessages(account: Account, location: SearchMessagesLocation return .complete() } - let request = Api.functions.stats.getMessagePublicForwards(channel: inputChannel, msgId: messageId.id, offsetRate: nextRate, offsetPeer: inputPeer, offsetId: lowerBound?.id.id ?? 0, limit: limit) + //TODO + let _ = inputChannel + return .complete() + + /*let request = Api.functions.stats.getMessagePublicForwards(channel: inputChannel, msgId: messageId.id, offsetRate: nextRate, offsetPeer: inputPeer, offsetId: lowerBound?.id.id ?? 0, limit: limit) let signal: Signal if let statsDatacenterId = statsDatacenterId, account.network.datacenterId != statsDatacenterId { signal = account.network.download(datacenterId: Int(statsDatacenterId), isMedia: false, tag: nil) @@ -392,7 +396,7 @@ func _internal_searchMessages(account: Account, location: SearchMessagesLocation } |> `catch` { _ -> Signal<(Api.messages.Messages?, Api.messages.Messages?), NoError> in return .single((nil, nil)) - } + }*/ } case let .sentMedia(tags): let filter: Api.MessagesFilter = tags.flatMap { messageFilterForTagMask($0) } ?? .inputMessagesFilterEmpty diff --git a/submodules/TelegramCore/Sources/TelegramEngine/Messages/SendAsPeers.swift b/submodules/TelegramCore/Sources/TelegramEngine/Messages/SendAsPeers.swift index 52dec1effb..0c24717543 100644 --- a/submodules/TelegramCore/Sources/TelegramEngine/Messages/SendAsPeers.swift +++ b/submodules/TelegramCore/Sources/TelegramEngine/Messages/SendAsPeers.swift @@ -138,7 +138,7 @@ func _internal_peerSendAsAvailablePeers(accountPeerId: PeerId, network: Network, for chat in chats { if let groupOrChannel = parsedPeers.get(chat.peerId) { switch chat { - case let .channel(_, _, _, _, _, _, _, _, _, _, _, _, participantsCount, _, _, _, _): + case let .channel(_, _, _, _, _, _, _, _, _, _, _, _, participantsCount, _, _, _, _, _, _): if let participantsCount = participantsCount { subscribers[groupOrChannel.id] = participantsCount } diff --git a/submodules/TelegramCore/Sources/TelegramEngine/Messages/StoryListContext.swift b/submodules/TelegramCore/Sources/TelegramEngine/Messages/StoryListContext.swift index 07ed846288..07d8b77419 100644 --- a/submodules/TelegramCore/Sources/TelegramEngine/Messages/StoryListContext.swift +++ b/submodules/TelegramCore/Sources/TelegramEngine/Messages/StoryListContext.swift @@ -1423,6 +1423,13 @@ public func _internal_pollPeerStories(postbox: Postbox, network: Network, accoun guard let inputPeer = inputPeer else { return .complete() } + + #if DEBUG + if "".isEmpty { + return .complete() + } + #endif + return network.request(Api.functions.stories.getPeerStories(peer: inputPeer)) |> map(Optional.init) |> `catch` { _ -> Signal in diff --git a/submodules/TelegramCore/Sources/TelegramEngine/Payments/GiftCodes.swift b/submodules/TelegramCore/Sources/TelegramEngine/Payments/GiftCodes.swift index 374593993a..6ac4a4fad3 100644 --- a/submodules/TelegramCore/Sources/TelegramEngine/Payments/GiftCodes.swift +++ b/submodules/TelegramCore/Sources/TelegramEngine/Payments/GiftCodes.swift @@ -5,7 +5,7 @@ import TelegramApi public struct PremiumGiftCodeInfo: Equatable { public let slug: String - public let fromPeerId: EnginePeer.Id + public let fromPeerId: EnginePeer.Id? public let messageId: EngineMessage.Id? public let toPeerId: EnginePeer.Id? public let date: Int32 @@ -259,8 +259,8 @@ extension PremiumGiftCodeInfo { switch apiCheckedGiftCode { case let .checkedGiftCode(flags, fromId, giveawayMsgId, toId, date, months, usedDate, _, _): self.slug = slug - self.fromPeerId = fromId.peerId - self.messageId = giveawayMsgId.flatMap { EngineMessage.Id(peerId: fromId.peerId, namespace: Namespaces.Message.Cloud, id: $0) } + self.fromPeerId = fromId?.peerId + self.messageId = giveawayMsgId.flatMap { giveawayMsgId in fromId.flatMap { fromId in EngineMessage.Id(peerId: fromId.peerId, namespace: Namespaces.Message.Cloud, id: giveawayMsgId) } } self.toPeerId = toId.flatMap { EnginePeer.Id(namespace: Namespaces.Peer.CloudUser, id: EnginePeer.Id.Id._internalFromInt64Value($0)) } self.date = date self.months = months diff --git a/submodules/TelegramCore/Sources/TelegramEngine/Peers/AddressNames.swift b/submodules/TelegramCore/Sources/TelegramEngine/Peers/AddressNames.swift index 6edfce7e37..50b317f946 100644 --- a/submodules/TelegramCore/Sources/TelegramEngine/Peers/AddressNames.swift +++ b/submodules/TelegramCore/Sources/TelegramEngine/Peers/AddressNames.swift @@ -595,7 +595,7 @@ func _internal_channelsForStories(account: Account) -> Signal<[Peer], NoError> { if let peer = transaction.getPeer(chat.peerId) { peers.append(peer) - if case let .channel(_, _, _, _, _, _, _, _, _, _, _, _, participantsCount, _, _, _, _) = chat, let participantsCount = participantsCount { + if case let .channel(_, _, _, _, _, _, _, _, _, _, _, _, participantsCount, _, _, _, _, _, _) = chat, let participantsCount = participantsCount { transaction.updatePeerCachedData(peerIds: Set([peer.id]), update: { _, current in var current = current as? CachedChannelData ?? CachedChannelData() var participantsSummary = current.participantsSummary diff --git a/submodules/TelegramCore/Sources/TelegramEngine/Peers/ChannelAdminEventLogs.swift b/submodules/TelegramCore/Sources/TelegramEngine/Peers/ChannelAdminEventLogs.swift index 6f755509a9..fb98934964 100644 --- a/submodules/TelegramCore/Sources/TelegramEngine/Peers/ChannelAdminEventLogs.swift +++ b/submodules/TelegramCore/Sources/TelegramEngine/Peers/ChannelAdminEventLogs.swift @@ -360,6 +360,10 @@ func channelAdminLogEvents(accountPeerId: PeerId, postbox: Postbox, network: Net let _ = prevValue let _ = newValue action = nil + case let .channelAdminLogEventActionChangeEmojiStatus(prevValue, newValue): + let _ = prevValue + let _ = newValue + action = nil } let peerId = PeerId(namespace: Namespaces.Peer.CloudUser, id: PeerId.Id._internalFromInt64Value(userId)) if let action = action { diff --git a/submodules/TelegramCore/Sources/TelegramEngine/Peers/ChannelRecommendation.swift b/submodules/TelegramCore/Sources/TelegramEngine/Peers/ChannelRecommendation.swift index d7d33a256d..b66146f16d 100644 --- a/submodules/TelegramCore/Sources/TelegramEngine/Peers/ChannelRecommendation.swift +++ b/submodules/TelegramCore/Sources/TelegramEngine/Peers/ChannelRecommendation.swift @@ -74,7 +74,7 @@ func _internal_requestRecommendedChannels(account: Account, peerId: EnginePeer.I for chat in chats { if let peer = transaction.getPeer(chat.peerId) { peers.append(EnginePeer(peer)) - if case let .channel(_, _, _, _, _, _, _, _, _, _, _, _, participantsCount, _, _, _, _) = chat, let participantsCount = participantsCount { + if case let .channel(_, _, _, _, _, _, _, _, _, _, _, _, participantsCount, _, _, _, _, _, _) = chat, let participantsCount = participantsCount { transaction.updatePeerCachedData(peerIds: Set([peer.id]), update: { _, current in var current = current as? CachedChannelData ?? CachedChannelData() var participantsSummary = current.participantsSummary diff --git a/submodules/TelegramCore/Sources/TelegramEngine/Peers/Communities.swift b/submodules/TelegramCore/Sources/TelegramEngine/Peers/Communities.swift index c42442928e..96c9eec56f 100644 --- a/submodules/TelegramCore/Sources/TelegramEngine/Peers/Communities.swift +++ b/submodules/TelegramCore/Sources/TelegramEngine/Peers/Communities.swift @@ -280,7 +280,7 @@ func _internal_checkChatFolderLink(account: Account, slug: String) -> Signal Signal S var memberCounts: [ChatListFiltersState.ChatListFilterUpdates.MemberCount] = [] for chat in chats { - if case let .channel(_, _, _, _, _, _, _, _, _, _, _, _, participantsCount, _, _, _, _) = chat { + if case let .channel(_, _, _, _, _, _, _, _, _, _, _, _, participantsCount, _, _, _, _, _, _) = chat { if let participantsCount = participantsCount { memberCounts.append(ChatListFiltersState.ChatListFilterUpdates.MemberCount(id: chat.peerId, count: participantsCount)) } diff --git a/submodules/TelegramCore/Sources/TelegramEngine/Peers/InactiveChannels.swift b/submodules/TelegramCore/Sources/TelegramEngine/Peers/InactiveChannels.swift index a24d8954ff..9837949190 100644 --- a/submodules/TelegramCore/Sources/TelegramEngine/Peers/InactiveChannels.swift +++ b/submodules/TelegramCore/Sources/TelegramEngine/Peers/InactiveChannels.swift @@ -31,7 +31,7 @@ func _internal_inactiveChannelList(network: Network) -> Signal<[InactiveChannel] var participantsCounts: [PeerId: Int32] = [:] for chat in chats { switch chat { - case let .channel(_, _, _, _, _, _, _, _, _, _, _, _, participantsCountValue, _, _, _, _): + case let .channel(_, _, _, _, _, _, _, _, _, _, _, _, participantsCountValue, _, _, _, _, _, _): if let participantsCountValue = participantsCountValue { participantsCounts[chat.peerId] = participantsCountValue } diff --git a/submodules/TelegramCore/Sources/TelegramEngine/Peers/SearchPeers.swift b/submodules/TelegramCore/Sources/TelegramEngine/Peers/SearchPeers.swift index bb04ad9024..ccdb358a1f 100644 --- a/submodules/TelegramCore/Sources/TelegramEngine/Peers/SearchPeers.swift +++ b/submodules/TelegramCore/Sources/TelegramEngine/Peers/SearchPeers.swift @@ -38,7 +38,7 @@ public func _internal_searchPeers(accountPeerId: PeerId, postbox: Postbox, netwo for chat in chats { if let groupOrChannel = parseTelegramGroupOrChannel(chat: chat) { switch chat { - case let .channel(_, _, _, _, _, _, _, _, _, _, _, _, participantsCount, _, _, _, _): + case let .channel(_, _, _, _, _, _, _, _, _, _, _, _, participantsCount, _, _, _, _, _, _): if let participantsCount = participantsCount { subscribers[groupOrChannel.id] = participantsCount } diff --git a/submodules/TelegramCore/Sources/TelegramEngine/Peers/TelegramEnginePeers.swift b/submodules/TelegramCore/Sources/TelegramEngine/Peers/TelegramEnginePeers.swift index 7bde5e753a..c9ada3511e 100644 --- a/submodules/TelegramCore/Sources/TelegramEngine/Peers/TelegramEnginePeers.swift +++ b/submodules/TelegramCore/Sources/TelegramEngine/Peers/TelegramEnginePeers.swift @@ -718,6 +718,10 @@ public extension TelegramEngine { return _internal_updatePeerNameColorAndEmoji(account: self.account, peerId: peerId, nameColor: nameColor, backgroundEmojiId: backgroundEmojiId, profileColor: profileColor, profileBackgroundEmojiId: profileBackgroundEmojiId) } + public func updatePeerEmojiStatus(peerId: EnginePeer.Id, fileId: Int64?, expirationDate: Int32?) -> Signal { + return _internal_updatePeerEmojiStatus(account: self.account, peerId: peerId, fileId: fileId, expirationDate: expirationDate) + } + public func getChatListPeers(filterPredicate: ChatListFilterPredicate) -> Signal<[EnginePeer], NoError> { return self.account.postbox.transaction { transaction -> [EnginePeer] in return transaction.getChatListPeers(groupId: .root, filterPredicate: filterPredicate, additionalFilter: nil).map(EnginePeer.init) diff --git a/submodules/TelegramCore/Sources/TelegramEngine/Peers/UpdatePeerInfo.swift b/submodules/TelegramCore/Sources/TelegramEngine/Peers/UpdatePeerInfo.swift index 9a76f169a4..74a3fcb96d 100644 --- a/submodules/TelegramCore/Sources/TelegramEngine/Peers/UpdatePeerInfo.swift +++ b/submodules/TelegramCore/Sources/TelegramEngine/Peers/UpdatePeerInfo.swift @@ -100,9 +100,9 @@ func _internal_updatePeerNameColorAndEmoji(account: Account, peerId: EnginePeer. if let peer = peer as? TelegramChannel, let inputChannel = apiInputChannel(peer) { let flagsReplies: Int32 = (1 << 0) | (1 << 2) - var flagsProfile: Int32 = (1 << 0) | (1 << 2) + var flagsProfile: Int32 = (1 << 0) | (1 << 1) if profileColor != nil { - flagsProfile |= (1 << 1) + flagsProfile |= (1 << 2) } return combineLatest( @@ -161,3 +161,45 @@ func _internal_updatePeerNameColorAndEmoji(account: Account, peerId: EnginePeer. |> castError(UpdatePeerNameColorAndEmojiError.self) |> switchToLatest } + +public enum UpdatePeerEmojiStatusError { + case generic +} + +func _internal_updatePeerEmojiStatus(account: Account, peerId: PeerId, fileId: Int64?, expirationDate: Int32?) -> Signal { + return account.postbox.transaction { transaction -> Api.InputChannel? in + let updatedStatus = fileId.flatMap { + PeerEmojiStatus(fileId: $0, expirationDate: expirationDate) + } + if let peer = transaction.getPeer(peerId) as? TelegramChannel { + updatePeersCustom(transaction: transaction, peers: [peer.withUpdatedEmojiStatus(updatedStatus)], update: { _, updated in updated }) + } + + return transaction.getPeer(peerId).flatMap(apiInputChannel) + } + |> castError(UpdatePeerEmojiStatusError.self) + |> mapToSignal { inputChannel -> Signal in + guard let inputChannel = inputChannel else { + return .fail(.generic) + } + let mappedStatus: Api.EmojiStatus + if let fileId = fileId { + if let expirationDate = expirationDate { + mappedStatus = .emojiStatusUntil(documentId: fileId, until: expirationDate) + } else { + mappedStatus = .emojiStatus(documentId: fileId) + } + } else { + mappedStatus = .emojiStatusEmpty + } + return account.network.request(Api.functions.channels.updateEmojiStatus(channel: inputChannel, emojiStatus: mappedStatus)) + |> ignoreValues + |> `catch` { error -> Signal in + if error.errorDescription == "CHAT_NOT_MODIFIED" { + return .complete() + } else { + return .fail(.generic) + } + } + } +} diff --git a/submodules/TelegramCore/Sources/TelegramEngine/Privacy/UpdatedAccountPrivacySettings.swift b/submodules/TelegramCore/Sources/TelegramEngine/Privacy/UpdatedAccountPrivacySettings.swift index baf71084d1..e0609dc2eb 100644 --- a/submodules/TelegramCore/Sources/TelegramEngine/Privacy/UpdatedAccountPrivacySettings.swift +++ b/submodules/TelegramCore/Sources/TelegramEngine/Privacy/UpdatedAccountPrivacySettings.swift @@ -166,7 +166,7 @@ func _internal_requestAccountPrivacySettings(account: Account) -> Signal Message { private func filterMessageChannelPeer(_ peer: Peer) -> Peer { if let peer = peer as? TelegramChannel { - return TelegramChannel(id: peer.id, accessHash: peer.accessHash, title: peer.title, username: peer.username, photo: peer.photo, creationDate: peer.creationDate, version: peer.version, participationStatus: peer.participationStatus, info: .group(TelegramChannelGroupInfo(flags: [])), flags: peer.flags, restrictionInfo: peer.restrictionInfo, adminRights: peer.adminRights, bannedRights: peer.bannedRights, defaultBannedRights: peer.defaultBannedRights, usernames: peer.usernames, storiesHidden: peer.storiesHidden, nameColor: peer.nameColor, backgroundEmojiId: peer.backgroundEmojiId, profileColor: peer.profileColor, profileBackgroundEmojiId: peer.profileBackgroundEmojiId, emojiStatus: peer.emojiStatus) + return TelegramChannel(id: peer.id, accessHash: peer.accessHash, title: peer.title, username: peer.username, photo: peer.photo, creationDate: peer.creationDate, version: peer.version, participationStatus: peer.participationStatus, info: .group(TelegramChannelGroupInfo(flags: [])), flags: peer.flags, restrictionInfo: peer.restrictionInfo, adminRights: peer.adminRights, bannedRights: peer.bannedRights, defaultBannedRights: peer.defaultBannedRights, usernames: peer.usernames, storiesHidden: peer.storiesHidden, nameColor: peer.nameColor, backgroundEmojiId: peer.backgroundEmojiId, profileColor: peer.profileColor, profileBackgroundEmojiId: peer.profileBackgroundEmojiId, emojiStatus: peer.emojiStatus, approximateBoostLevel: peer.approximateBoostLevel) } return peer } diff --git a/submodules/TelegramUI/Components/Chat/MessageInlineBlockBackgroundView/Sources/MessageInlineBlockBackgroundView.swift b/submodules/TelegramUI/Components/Chat/MessageInlineBlockBackgroundView/Sources/MessageInlineBlockBackgroundView.swift index 14f5bb3d83..e09b349b29 100644 --- a/submodules/TelegramUI/Components/Chat/MessageInlineBlockBackgroundView/Sources/MessageInlineBlockBackgroundView.swift +++ b/submodules/TelegramUI/Components/Chat/MessageInlineBlockBackgroundView/Sources/MessageInlineBlockBackgroundView.swift @@ -334,7 +334,6 @@ private final class LineView: UIView { let _ = previousParams - self.backgroundView.tintColor = primaryColor if let secondaryColor { @@ -406,6 +405,10 @@ private final class LineView: UIView { self.dashBackgroundView = nil dashBackgroundView.removeFromSuperview() } + if let dashThirdBackgroundView = self.dashThirdBackgroundView { + self.dashThirdBackgroundView = nil + dashThirdBackgroundView.removeFromSuperview() + } self.backgroundView.alpha = 1.0 } diff --git a/submodules/TelegramUI/Components/ChatTitleView/Sources/ChatTitleView.swift b/submodules/TelegramUI/Components/ChatTitleView/Sources/ChatTitleView.swift index f708e2baa0..98e17f2e40 100644 --- a/submodules/TelegramUI/Components/ChatTitleView/Sources/ChatTitleView.swift +++ b/submodules/TelegramUI/Components/ChatTitleView/Sources/ChatTitleView.swift @@ -257,7 +257,7 @@ public final class ChatTitleView: UIView, NavigationBarTitleView { titleCredibilityIcon = .fake } else if peer.isScam { titleCredibilityIcon = .scam - } else if let user = peer as? TelegramUser, let emojiStatus = user.emojiStatus, !premiumConfiguration.isPremiumDisabled { + } else if let emojiStatus = peer.emojiStatus, !premiumConfiguration.isPremiumDisabled { titleCredibilityIcon = .emojiStatus(emojiStatus) } else if peer.isVerified { titleCredibilityIcon = .verified diff --git a/submodules/TelegramUI/Components/EmojiStatusSelectionComponent/Sources/EmojiStatusSelectionComponent.swift b/submodules/TelegramUI/Components/EmojiStatusSelectionComponent/Sources/EmojiStatusSelectionComponent.swift index 4be7f89a58..f2057cc3e8 100644 --- a/submodules/TelegramUI/Components/EmojiStatusSelectionComponent/Sources/EmojiStatusSelectionComponent.swift +++ b/submodules/TelegramUI/Components/EmojiStatusSelectionComponent/Sources/EmojiStatusSelectionComponent.swift @@ -1043,11 +1043,18 @@ public final class EmojiStatusSelectionController: ViewController { sourceOrigin = CGPoint(x: layout.size.width / 2.0, y: floor(layout.size.height / 2.0 - componentSize.height)) } + var componentFrame: CGRect + let pointsToTop: Bool if sourceOrigin.y + 5.0 + componentSize.height > layout.size.height - layout.insets(options: []).bottom { - sourceOrigin.y = layout.size.height - layout.insets(options: []).bottom - componentSize.height - 5.0 + componentFrame = CGRect(origin: CGPoint(x: floor((layout.size.width - componentSize.width) / 2.0), y: sourceOrigin.y - 25.0 - componentSize.height), size: componentSize) + pointsToTop = false + } else { + componentFrame = CGRect(origin: CGPoint(x: floor((layout.size.width - componentSize.width) / 2.0), y: sourceOrigin.y + 5.0), size: componentSize) + pointsToTop = true + } + if componentFrame.minY < layout.insets(options: [.statusBar]).top { + componentFrame.origin.y = layout.insets(options: [.statusBar]).top } - - let componentFrame = CGRect(origin: CGPoint(x: floor((layout.size.width - componentSize.width) / 2.0), y: sourceOrigin.y + 5.0), size: componentSize) if self.componentShadowLayer.bounds.size != componentFrame.size { let componentShadowPath = UIBezierPath(roundedRect: CGRect(origin: CGPoint(), size: componentFrame.size), cornerRadius: 24.0).cgPath @@ -1057,7 +1064,12 @@ public final class EmojiStatusSelectionController: ViewController { let cloudOffset0: CGFloat = 30.0 let cloudSize0: CGFloat = 16.0 - var cloudFrame0 = CGRect(origin: CGPoint(x: floor(sourceOrigin.x + cloudOffset0 - cloudSize0 / 2.0), y: componentFrame.minY - cloudSize0 / 2.0), size: CGSize(width: cloudSize0, height: cloudSize0)) + var cloudFrame0: CGRect + if pointsToTop { + cloudFrame0 = CGRect(origin: CGPoint(x: floor(sourceOrigin.x + cloudOffset0 - cloudSize0 / 2.0), y: componentFrame.minY - cloudSize0 / 2.0), size: CGSize(width: cloudSize0, height: cloudSize0)) + } else { + cloudFrame0 = CGRect(origin: CGPoint(x: floor(sourceOrigin.x + cloudOffset0 - cloudSize0 / 2.0), y: componentFrame.maxY - cloudSize0 / 2.0), size: CGSize(width: cloudSize0, height: cloudSize0)) + } var invertX = false if cloudFrame0.maxX >= layout.size.width - layout.safeInsets.right - 32.0 { cloudFrame0.origin.x = floor(sourceOrigin.x - cloudSize0 - cloudOffset0 + cloudSize0 / 2.0) @@ -1074,7 +1086,12 @@ public final class EmojiStatusSelectionController: ViewController { let cloudOffset1 = CGPoint(x: -9.0, y: -14.0) let cloudSize1: CGFloat = 8.0 - var cloudFrame1 = CGRect(origin: CGPoint(x: floor(cloudFrame0.midX + cloudOffset1.x - cloudSize1 / 2.0), y: floor(cloudFrame0.midY + cloudOffset1.y - cloudSize1 / 2.0)), size: CGSize(width: cloudSize1, height: cloudSize1)) + var cloudFrame1: CGRect + if pointsToTop { + cloudFrame1 = CGRect(origin: CGPoint(x: floor(cloudFrame0.midX + cloudOffset1.x - cloudSize1 / 2.0), y: floor(cloudFrame0.midY + cloudOffset1.y - cloudSize1 / 2.0)), size: CGSize(width: cloudSize1, height: cloudSize1)) + } else { + cloudFrame1 = CGRect(origin: CGPoint(x: floor(cloudFrame0.midX + cloudOffset1.x - cloudSize1 / 2.0), y: floor(cloudFrame0.midY - cloudOffset1.y - cloudSize1 / 2.0)), size: CGSize(width: cloudSize1, height: cloudSize1)) + } if invertX { cloudFrame1.origin.x = floor(cloudFrame0.midX - cloudSize1 - cloudOffset1.x + cloudSize1 / 2.0) } @@ -1095,7 +1112,7 @@ public final class EmojiStatusSelectionController: ViewController { let contentDuration: Double = 0.3 let contentDelay: Double = 0.14 - let initialContentFrame = CGRect(origin: CGPoint(x: cloudFrame0.midX - 24.0, y: componentFrame.minY), size: CGSize(width: 24.0 * 2.0, height: 24.0 * 2.0)) + let initialContentFrame = CGRect(origin: CGPoint(x: cloudFrame0.midX - 24.0, y: pointsToTop ? componentFrame.minY : (componentFrame.maxY - 24.0 * 2.0)), size: CGSize(width: 24.0 * 2.0, height: 24.0 * 2.0)) if let emojiView = self.componentHost.findTaggedView(tag: EmojiPagerContentComponent.Tag(id: AnyHashable("emoji"))) as? EmojiPagerContentComponent.View { emojiView.animateIn(fromLocation: self.view.convert(initialContentFrame.center, to: emojiView)) @@ -1103,7 +1120,7 @@ public final class EmojiStatusSelectionController: ViewController { componentView.layer.animatePosition(from: initialContentFrame.center, to: componentFrame.center, duration: contentDuration, delay: contentDelay, timingFunction: kCAMediaTimingFunctionSpring) componentView.layer.animateBounds(from: CGRect(origin: CGPoint(x: -(componentFrame.minX - initialContentFrame.minX), y: -(componentFrame.minY - initialContentFrame.minY)), size: initialContentFrame.size), to: CGRect(origin: CGPoint(), size: componentFrame.size), duration: contentDuration, delay: contentDelay, timingFunction: kCAMediaTimingFunctionSpring) - self.componentShadowLayer.animateFrame(from: CGRect(origin: CGPoint(x: cloudFrame0.midX - 24.0, y: componentFrame.minY), size: CGSize(width: 24.0 * 2.0, height: 24.0 * 2.0)), to: componentView.frame, duration: contentDuration, delay: contentDelay, timingFunction: kCAMediaTimingFunctionSpring) + self.componentShadowLayer.animateFrame(from: CGRect(origin: CGPoint(x: cloudFrame0.midX - 24.0, y: pointsToTop ? componentFrame.minY : (componentFrame.maxY - 24.0 * 2.0)), size: CGSize(width: 24.0 * 2.0, height: 24.0 * 2.0)), to: componentView.frame, duration: contentDuration, delay: contentDelay, timingFunction: kCAMediaTimingFunctionSpring) componentView.layer.animateAlpha(from: 0.0, to: 1.0, duration: 0.04, delay: contentDelay) self.componentShadowLayer.animateAlpha(from: 0.0, to: 1.0, duration: 0.04, delay: contentDelay) @@ -1283,7 +1300,17 @@ public final class EmojiStatusSelectionController: ViewController { return } - if let _ = item, let destinationView = controller.destinationItemView() { + var animateOutToView = false + switch controller.mode { + case .backgroundSelection, .customStatusSelection, .quickReactionSelection: + if let itemFile = item?.itemFile, itemFile.fileId.id != 0 { + animateOutToView = true + } + case .statusSelection: + animateOutToView = true + } + + if animateOutToView, item != nil, let destinationView = controller.destinationItemView() { if let snapshotView = destinationView.snapshotView(afterScreenUpdates: false) { snapshotView.frame = destinationView.frame destinationView.superview?.insertSubview(snapshotView, belowSubview: destinationView) @@ -1299,7 +1326,9 @@ public final class EmojiStatusSelectionController: ViewController { let _ = (self.context.engine.accountData.setEmojiStatus(file: item?.itemFile, expirationDate: nil) |> deliverOnMainQueue).start() case let .backgroundSelection(completion): - completion(item?.itemFile?.fileId.id) + completion(item?.itemFile) + case let .customStatusSelection(completion): + completion(item?.itemFile, nil) case let .quickReactionSelection(completion): if let item = item, let itemFile = item.itemFile { var selectedReaction: MessageReaction.Reaction? @@ -1325,7 +1354,7 @@ public final class EmojiStatusSelectionController: ViewController { completion() } - if let item = item, let destinationView = controller.destinationItemView() { + if animateOutToView, let item = item, let destinationView = controller.destinationItemView() { var emojiString: String? if let itemFile = item.itemFile { attributeLoop: for attribute in itemFile.attributes { @@ -1385,7 +1414,8 @@ public final class EmojiStatusSelectionController: ViewController { public enum Mode { case statusSelection - case backgroundSelection(completion: (Int64?) -> Void) + case backgroundSelection(completion: (TelegramMediaFile?) -> Void) + case customStatusSelection(completion: (TelegramMediaFile?, Int32?) -> Void) case quickReactionSelection(completion: () -> Void) } diff --git a/submodules/TelegramUI/Components/EntityKeyboard/Sources/EmojiPagerContentComponent.swift b/submodules/TelegramUI/Components/EntityKeyboard/Sources/EmojiPagerContentComponent.swift index e661064f2b..ffd8d3fb03 100644 --- a/submodules/TelegramUI/Components/EntityKeyboard/Sources/EmojiPagerContentComponent.swift +++ b/submodules/TelegramUI/Components/EntityKeyboard/Sources/EmojiPagerContentComponent.swift @@ -7188,6 +7188,7 @@ public final class EmojiPagerContentComponent: Component { public enum Subject: Equatable { case generic case status + case channelStatus case reaction(onlyTop: Bool) case emoji case topicIcon @@ -7239,6 +7240,19 @@ public final class EmojiPagerContentComponent: Component { orderedItemListCollectionIds.append(Namespaces.OrderedItemList.CloudFeaturedStatusEmoji) orderedItemListCollectionIds.append(Namespaces.OrderedItemList.CloudRecentStatusEmoji) + iconStatusEmoji = context.engine.stickers.loadedStickerPack(reference: .iconStatusEmoji, forceActualized: false) + |> map { result -> [TelegramMediaFile] in + switch result { + case let .result(_, items, _): + return items.map(\.file) + default: + return [] + } + } + |> take(1) + } else if case .channelStatus = subject { + orderedItemListCollectionIds.append(Namespaces.OrderedItemList.CloudFeaturedStatusEmoji) + iconStatusEmoji = context.engine.stickers.loadedStickerPack(reference: .iconStatusEmoji, forceActualized: false) |> map { result -> [TelegramMediaFile] in switch result { @@ -7283,6 +7297,8 @@ public final class EmojiPagerContentComponent: Component { searchCategories = context.engine.stickers.emojiSearchCategories(kind: .emoji) } else if case .status = subject { searchCategories = context.engine.stickers.emojiSearchCategories(kind: .status) + } else if case .channelStatus = subject { + searchCategories = context.engine.stickers.emojiSearchCategories(kind: .status) } else if [.profilePhoto, .groupPhoto].contains(subject) { searchCategories = context.engine.stickers.emojiSearchCategories(kind: .avatar) } else { @@ -7564,6 +7580,166 @@ public final class EmojiPagerContentComponent: Component { } } + if let recentStatusEmoji = recentStatusEmoji { + for item in recentStatusEmoji.items { + guard let item = item.contents.get(RecentMediaItem.self) else { + continue + } + + let file = item.media + if existingIds.contains(file.fileId) { + continue + } + existingIds.insert(file.fileId) + + var tintMode: Item.TintMode = .none + if file.isCustomTemplateEmoji { + tintMode = .accent + } + for attribute in file.attributes { + if case let .CustomEmoji(_, _, _, packReference) = attribute { + switch packReference { + case let .id(id, _): + if id == 773947703670341676 || id == 2964141614563343 { + tintMode = .accent + } + default: + break + } + } + } + + let resultItem: EmojiPagerContentComponent.Item + + let animationData = EntityKeyboardAnimationData(file: file) + resultItem = EmojiPagerContentComponent.Item( + animationData: animationData, + content: .animation(animationData), + itemFile: file, + subgroupId: nil, + icon: .none, + tintMode: tintMode + ) + + if let groupIndex = itemGroupIndexById[groupId] { + if itemGroups[groupIndex].items.count >= (5 + 8) * 8 { + break + } + + itemGroups[groupIndex].items.append(resultItem) + } + } + } + if let featuredStatusEmoji = featuredStatusEmoji { + for item in featuredStatusEmoji.items { + guard let item = item.contents.get(RecentMediaItem.self) else { + continue + } + + let file = item.media + if existingIds.contains(file.fileId) { + continue + } + existingIds.insert(file.fileId) + + let resultItem: EmojiPagerContentComponent.Item + + var tintMode: Item.TintMode = .none + if file.isCustomTemplateEmoji { + tintMode = .accent + } + for attribute in file.attributes { + if case let .CustomEmoji(_, _, _, packReference) = attribute { + switch packReference { + case let .id(id, _): + if id == 773947703670341676 || id == 2964141614563343 { + tintMode = .accent + } + default: + break + } + } + } + + let animationData = EntityKeyboardAnimationData(file: file) + resultItem = EmojiPagerContentComponent.Item( + animationData: animationData, + content: .animation(animationData), + itemFile: file, + subgroupId: nil, + icon: .none, + tintMode: tintMode + ) + + if let groupIndex = itemGroupIndexById[groupId] { + if itemGroups[groupIndex].items.count >= (5 + 8) * 8 { + break + } + + itemGroups[groupIndex].items.append(resultItem) + } + } + } + } else if case .channelStatus = subject { + let resultItem = EmojiPagerContentComponent.Item( + animationData: nil, + content: .icon(.stop), + itemFile: nil, + subgroupId: nil, + icon: .none, + tintMode: .accent + ) + + let groupId = "recent" + if let groupIndex = itemGroupIndexById[groupId] { + itemGroups[groupIndex].items.append(resultItem) + } else { + itemGroupIndexById[groupId] = itemGroups.count + itemGroups.append(ItemGroup(supergroupId: groupId, id: groupId, title: topStatusTitle?.uppercased(), subtitle: nil, isPremiumLocked: false, isFeatured: false, collapsedLineCount: 5, isClearable: false, headerItem: nil, items: [resultItem])) + } + + var existingIds = Set() + + for file in iconStatusEmoji.prefix(7) { + if existingIds.contains(file.fileId) { + continue + } + existingIds.insert(file.fileId) + + var tintMode: Item.TintMode = .none + if file.isCustomTemplateEmoji { + tintMode = .accent + } + for attribute in file.attributes { + if case let .CustomEmoji(_, _, _, packReference) = attribute { + switch packReference { + case let .id(id, _): + if id == 773947703670341676 || id == 2964141614563343 { + tintMode = .accent + } + default: + break + } + } + } + + let resultItem: EmojiPagerContentComponent.Item + + let animationData = EntityKeyboardAnimationData(file: file) + resultItem = EmojiPagerContentComponent.Item( + animationData: animationData, + content: .animation(animationData), + itemFile: file, + subgroupId: nil, + icon: .none, + tintMode: tintMode + ) + + if let groupIndex = itemGroupIndexById[groupId] { + itemGroups[groupIndex].items.append(resultItem) + } + } + if let recentStatusEmoji = recentStatusEmoji { for item in recentStatusEmoji.items { guard let item = item.contents.get(RecentMediaItem.self) else { @@ -8143,7 +8319,7 @@ public final class EmojiPagerContentComponent: Component { var isTemplate = false var tintMode: Item.TintMode = .none if item.file.isCustomTemplateEmoji { - if [.status, .backgroundIcon].contains(subject) { + if [.status, .channelStatus, .backgroundIcon].contains(subject) { if let backgroundIconColor { tintMode = .custom(backgroundIconColor) } else { @@ -8229,7 +8405,7 @@ public final class EmojiPagerContentComponent: Component { for item in featuredEmojiPack.topItems { var tintMode: Item.TintMode = .none if item.file.isCustomTemplateEmoji { - if [.status, .backgroundIcon].contains(subject) { + if [.status, .channelStatus, .backgroundIcon].contains(subject) { if let backgroundIconColor { tintMode = .custom(backgroundIconColor) } else { @@ -8361,8 +8537,8 @@ public final class EmojiPagerContentComponent: Component { ) } - let warpContentsOnEdges = [.reaction(onlyTop: true), .reaction(onlyTop: false), .quickReaction, .status, .profilePhoto, .groupPhoto, .backgroundIcon].contains(subject) - let enableLongPress = [.reaction(onlyTop: true), .reaction(onlyTop: false), .status].contains(subject) + let warpContentsOnEdges = [.reaction(onlyTop: true), .reaction(onlyTop: false), .quickReaction, .status, .channelStatus, .profilePhoto, .groupPhoto, .backgroundIcon].contains(subject) + let enableLongPress = [.reaction(onlyTop: true), .reaction(onlyTop: false), .status, .channelStatus].contains(subject) return EmojiPagerContentComponent( id: "emoji", diff --git a/submodules/TelegramUI/Components/NotificationExceptionsScreen/Sources/NotificationExceptionsScreen.swift b/submodules/TelegramUI/Components/NotificationExceptionsScreen/Sources/NotificationExceptionsScreen.swift index 258049808f..472def405a 100644 --- a/submodules/TelegramUI/Components/NotificationExceptionsScreen/Sources/NotificationExceptionsScreen.swift +++ b/submodules/TelegramUI/Components/NotificationExceptionsScreen/Sources/NotificationExceptionsScreen.swift @@ -332,7 +332,7 @@ private func notificationsPeerCategoryEntries(peerId: EnginePeer.Id, notificatio } } existingThreadIds.insert(value.threadId) - entries.append(.exception(Int32(index), presentationData.dateTimeFormat, presentationData.nameDisplayOrder, .channel(TelegramChannel(id: peerId, accessHash: nil, title: "", username: nil, photo: [], creationDate: 0, version: 0, participationStatus: .member, info: .group(TelegramChannelGroupInfo(flags: [])), flags: [.isForum], restrictionInfo: nil, adminRights: nil, bannedRights: nil, defaultBannedRights: nil, usernames: [], storiesHidden: nil, nameColor: nil, backgroundEmojiId: nil, profileColor: nil, profileBackgroundEmojiId: nil, emojiStatus: nil)), value.threadId, value.info, title, value.notificationSettings._asNotificationSettings(), state.editing, state.revealedThreadId == value.threadId)) + entries.append(.exception(Int32(index), presentationData.dateTimeFormat, presentationData.nameDisplayOrder, .channel(TelegramChannel(id: peerId, accessHash: nil, title: "", username: nil, photo: [], creationDate: 0, version: 0, participationStatus: .member, info: .group(TelegramChannelGroupInfo(flags: [])), flags: [.isForum], restrictionInfo: nil, adminRights: nil, bannedRights: nil, defaultBannedRights: nil, usernames: [], storiesHidden: nil, nameColor: nil, backgroundEmojiId: nil, profileColor: nil, profileBackgroundEmojiId: nil, emojiStatus: nil, approximateBoostLevel: nil)), value.threadId, value.info, title, value.notificationSettings._asNotificationSettings(), state.editing, state.revealedThreadId == value.threadId)) index += 1 } diff --git a/submodules/TelegramUI/Components/PeerInfo/PeerInfoScreen/Sources/PeerInfoHeaderNode.swift b/submodules/TelegramUI/Components/PeerInfo/PeerInfoScreen/Sources/PeerInfoHeaderNode.swift index 0903e86ae9..3571c93393 100644 --- a/submodules/TelegramUI/Components/PeerInfo/PeerInfoScreen/Sources/PeerInfoHeaderNode.swift +++ b/submodules/TelegramUI/Components/PeerInfo/PeerInfoScreen/Sources/PeerInfoHeaderNode.swift @@ -476,7 +476,7 @@ final class PeerInfoHeaderNode: ASDisplayNode { credibilityIcon = .fake } else if peer.isScam { credibilityIcon = .scam - } else if let user = peer as? TelegramUser, let emojiStatus = user.emojiStatus, !premiumConfiguration.isPremiumDisabled { + } else if let emojiStatus = peer.emojiStatus, !premiumConfiguration.isPremiumDisabled { credibilityIcon = .emojiStatus(emojiStatus) } else if peer.isVerified { credibilityIcon = .verified diff --git a/submodules/TelegramUI/Components/PeerInfo/PeerInfoScreen/Sources/PeerInfoScreen.swift b/submodules/TelegramUI/Components/PeerInfo/PeerInfoScreen/Sources/PeerInfoScreen.swift index 7e301f2207..939028915a 100644 --- a/submodules/TelegramUI/Components/PeerInfo/PeerInfoScreen/Sources/PeerInfoScreen.swift +++ b/submodules/TelegramUI/Components/PeerInfo/PeerInfoScreen/Sources/PeerInfoScreen.swift @@ -1676,7 +1676,11 @@ private func editingItems(data: PeerInfoScreenData?, state: PeerInfoState, chatL let colorImage = generateSettingsMenuPeerColorsLabelIcon(colors: colors) //TODO:localize - items[.peerSettings]!.append(PeerInfoScreenDisclosureItem(id: ItemPeerColor, label: .image(colorImage, colorImage.size), additionalBadgeIcon: generateDisclosureActionBoostLevelBadgeImage(text: "Level 1+"), text: "Appearance", icon: UIImage(bundleImageName: "Chat/Info/NameColorIcon"), action: { + var boostIcon: UIImage? + if let approximateBoostLevel = channel.approximateBoostLevel, approximateBoostLevel < 1 { + boostIcon = generateDisclosureActionBoostLevelBadgeImage(text: "Level 1+") + } + items[.peerSettings]!.append(PeerInfoScreenDisclosureItem(id: ItemPeerColor, label: .image(colorImage, colorImage.size), additionalBadgeIcon: boostIcon, text: "Appearance", icon: UIImage(bundleImageName: "Chat/Info/NameColorIcon"), action: { interaction.editingOpenNameColorSetup() })) @@ -3836,7 +3840,7 @@ final class PeerInfoScreenNode: ViewControllerTracingNode, PeerInfoScreenNodePro var currentSelectedFileId: Int64? var topStatusTitle = strongSelf.presentationData.strings.PeerStatusSetup_NoTimerTitle if let peer = strongSelf.data?.peer { - if let user = peer as? TelegramUser, let emojiStatus = user.emojiStatus { + if let emojiStatus = peer.emojiStatus { selectedItems.insert(MediaId(namespace: Namespaces.Media.CloudFile, id: emojiStatus.fileId)) currentSelectedFileId = emojiStatus.fileId @@ -12214,7 +12218,7 @@ private final class AccountPeerContextItemNode: ASDisplayNode, ContextMenuCustom self.avatarNode.setPeer(context: self.item.context, account: self.item.account, theme: self.presentationData.theme, peer: self.item.peer) - if case let .user(user) = self.item.peer, let _ = user.emojiStatus { + if self.item.peer.emojiStatus != nil { rightTextInset += 32.0 } @@ -12232,6 +12236,10 @@ private final class AccountPeerContextItemNode: ASDisplayNode, ContextMenuCustom } else if user.isPremium { iconContent = .premium(color: self.presentationData.theme.list.itemAccentColor) } + } else if case let .channel(channel) = self.item.peer { + if let emojiStatus = channel.emojiStatus { + iconContent = .animation(content: .customEmoji(fileId: emojiStatus.fileId), size: CGSize(width: 28.0, height: 28.0), placeholderColor: self.presentationData.theme.list.mediaPlaceholderColor, themeColor: self.presentationData.theme.list.itemAccentColor, loopMode: .forever) + } } if let iconContent { let emojiStatusSize = self.emojiStatusView.update( diff --git a/submodules/TelegramUI/Components/Settings/PeerNameColorScreen/BUILD b/submodules/TelegramUI/Components/Settings/PeerNameColorScreen/BUILD index 4af595503c..778642e431 100644 --- a/submodules/TelegramUI/Components/Settings/PeerNameColorScreen/BUILD +++ b/submodules/TelegramUI/Components/Settings/PeerNameColorScreen/BUILD @@ -42,6 +42,7 @@ swift_library( "//submodules/TelegramUI/Components/EmojiStatusSelectionComponent", "//submodules/TelegramUI/Components/DynamicCornerRadiusView", "//submodules/Components/ComponentDisplayAdapters", + "//submodules/WallpaperResources", ], visibility = [ "//visibility:public", diff --git a/submodules/TelegramUI/Components/Settings/PeerNameColorScreen/Sources/ChannelAppearanceScreen.swift b/submodules/TelegramUI/Components/Settings/PeerNameColorScreen/Sources/ChannelAppearanceScreen.swift index 1637c96238..52caa07c99 100644 --- a/submodules/TelegramUI/Components/Settings/PeerNameColorScreen/Sources/ChannelAppearanceScreen.swift +++ b/submodules/TelegramUI/Components/Settings/PeerNameColorScreen/Sources/ChannelAppearanceScreen.swift @@ -28,6 +28,7 @@ import EmojiStatusSelectionComponent import EmojiStatusComponent import DynamicCornerRadiusView import ComponentDisplayAdapters +import WallpaperResources private final class EmojiActionIconComponent: Component { let context: AccountContext @@ -285,6 +286,25 @@ final class ChannelAppearanceScreenComponent: Component { } } + private struct ResolvedState { + var nameColor: PeerNameColor + var profileColor: PeerNameColor? + var replyFileId: Int64? + var backgroundFileId: Int64? + var emojiStatus: PeerEmojiStatus? + + var hasChanges: Bool + + init(nameColor: PeerNameColor, profileColor: PeerNameColor?, replyFileId: Int64?, backgroundFileId: Int64?, emojiStatus: PeerEmojiStatus?, hasChanges: Bool) { + self.nameColor = nameColor + self.profileColor = profileColor + self.replyFileId = replyFileId + self.backgroundFileId = backgroundFileId + self.emojiStatus = emojiStatus + self.hasChanges = hasChanges + } + } + final class View: UIView, UIScrollViewDelegate { private let scrollView: ScrollView private let actionButton = ComponentView() @@ -315,12 +335,15 @@ final class ChannelAppearanceScreenComponent: Component { private var updatedPeerNameEmoji: Int64?? private var updatedPeerProfileColor: PeerNameColor?? private var updatedPeerProfileEmoji: Int64?? - private var updatedPeerStatusEmoji: Int64?? + private var updatedPeerStatus: PeerEmojiStatus?? private var requiredLevel: Int? private var currentTheme: PresentationThemeReference? + private var resolvedCurrentTheme: (reference: PresentationThemeReference, isDark: Bool, theme: PresentationTheme, wallpaper: TelegramWallpaper?)? + private var resolvingCurrentTheme: (reference: PresentationThemeReference, isDark: Bool, disposable: Disposable)? + private var boostLevel: Int? private var boostStatus: ChannelBoostStatus? private var boostStatusDisposable: Disposable? @@ -363,6 +386,7 @@ final class ChannelAppearanceScreenComponent: Component { self.contentsDataDisposable?.dispose() self.applyDisposable?.dispose() self.boostStatusDisposable?.dispose() + self.resolvingCurrentTheme?.disposable.dispose() } func scrollToTop() { @@ -392,23 +416,12 @@ final class ChannelAppearanceScreenComponent: Component { transition.setAlpha(layer: self.bottomPanelSeparator, alpha: bottomNavigationAlpha) } - private func applySettings() { - guard let component = self.component, let contentsData = self.contentsData, let peer = contentsData.peer, let requiredLevel = self.requiredLevel else { - return - } - if self.isApplyingSettings { - return + private func resolveState() -> ResolvedState? { + guard let contentsData = self.contentsData, let peer = contentsData.peer else { + return nil } - if let boostStatus = self.boostStatus, requiredLevel > boostStatus.level { - self.displayPremiumScreen(requiredLevel: requiredLevel) - return - } - - self.isApplyingSettings = true - self.state?.updated(transition: .immediate) - - self.applyDisposable?.dispose() + var hasChanges = false let nameColor: PeerNameColor if let updatedPeerNameColor = self.updatedPeerNameColor { @@ -418,6 +431,9 @@ final class ChannelAppearanceScreenComponent: Component { } else { nameColor = .blue } + if nameColor != peer.nameColor { + hasChanges = true + } let profileColor: PeerNameColor? if case let .some(value) = self.updatedPeerProfileColor { @@ -427,49 +443,98 @@ final class ChannelAppearanceScreenComponent: Component { } else { profileColor = nil } + if profileColor != peer.profileColor { + hasChanges = true + } let replyFileId: Int64? if case let .some(value) = self.updatedPeerNameEmoji { replyFileId = value } else { - replyFileId = contentsData.peer?.backgroundEmojiId + replyFileId = peer.backgroundEmojiId + } + if replyFileId != peer.backgroundEmojiId { + hasChanges = true } let backgroundFileId: Int64? if case let .some(value) = self.updatedPeerProfileEmoji { backgroundFileId = value } else { - backgroundFileId = contentsData.peer?.profileBackgroundEmojiId + backgroundFileId = peer.profileBackgroundEmojiId + } + if backgroundFileId != peer.profileBackgroundEmojiId { + hasChanges = true } let emojiStatus: PeerEmojiStatus? - if case let .some(value) = self.updatedPeerStatusEmoji { - if let value { - emojiStatus = PeerEmojiStatus(fileId: value, expirationDate: nil) - } else { - emojiStatus = nil - } + if case let .some(value) = self.updatedPeerStatus { + emojiStatus = value } else { - emojiStatus = contentsData.peer?.emojiStatus + emojiStatus = peer.emojiStatus + } + if emojiStatus != peer.emojiStatus { + hasChanges = true } - let statusFileId = emojiStatus?.fileId - let _ = statusFileId + return ResolvedState( + nameColor: nameColor, + profileColor: profileColor, + replyFileId: replyFileId, + backgroundFileId: backgroundFileId, + emojiStatus: emojiStatus, + hasChanges: hasChanges + ) + } + + private func applySettings() { + guard let component = self.component, let resolvedState = self.resolveState(), let requiredLevel = self.requiredLevel else { + return + } + if self.isApplyingSettings { + return + } + + if let boostLevel = self.boostLevel, requiredLevel > boostLevel { + self.displayPremiumScreen(requiredLevel: requiredLevel) + return + } + + if !resolvedState.hasChanges { + self.environment?.controller()?.dismiss() + return + } + + self.isApplyingSettings = true + self.state?.updated(transition: .immediate) + + self.applyDisposable?.dispose() + + let statusFileId = resolvedState.emojiStatus?.fileId enum ApplyError { case generic } self.applyDisposable = (combineLatest([ - component.context.engine.peers.updatePeerNameColorAndEmoji(peerId: component.peerId, nameColor: nameColor, backgroundEmojiId: replyFileId, profileColor: profileColor, profileBackgroundEmojiId: backgroundFileId) + component.context.engine.peers.updatePeerNameColorAndEmoji(peerId: component.peerId, nameColor: resolvedState.nameColor, backgroundEmojiId: resolvedState.replyFileId, profileColor: resolvedState.profileColor, profileBackgroundEmojiId: resolvedState.backgroundFileId) + |> ignoreValues + |> mapError { _ -> ApplyError in + return .generic + }, + component.context.engine.peers.updatePeerEmojiStatus(peerId: component.peerId, fileId: statusFileId, expirationDate: nil) |> mapError { _ -> ApplyError in return .generic } ]) |> deliverOnMainQueue).start(error: { [weak self] _ in - guard let self else { + guard let self, let component = self.component else { return } + + let presentationData = component.context.sharedContext.currentPresentationData.with { $0 } + self.environment?.controller()?.present(standardTextAlertController(theme: AlertControllerTheme(presentationData: presentationData), title: nil, text: presentationData.strings.Login_UnknownError, actions: [TextAlertAction(type: .defaultAction, title: presentationData.strings.Common_OK, action: {})]), in: .window(.root)) + self.isApplyingSettings = false self.state?.updated(transition: .immediate) }, completed: { [weak self] in @@ -553,29 +618,62 @@ final class ChannelAppearanceScreenComponent: Component { selectedItems.insert(MediaId(namespace: Namespaces.Media.CloudFile, id: currentFileId)) } - let controller = EmojiStatusSelectionController( - context: component.context, - mode: .backgroundSelection(completion: { [weak self] result in + let mappedSubject: EmojiPagerContentComponent.Subject + switch subject { + case .reply, .profile: + mappedSubject = .backgroundIcon + case .status: + mappedSubject = .channelStatus + } + + let mappedMode: EmojiStatusSelectionController.Mode + switch subject { + case .status: + mappedMode = .customStatusSelection(completion: { [weak self] result, timestamp in guard let self else { return } + if let result { + self.cachedIconFiles[result.fileId.id] = result + } switch subject { - case .reply: - self.updatedPeerNameEmoji = result - case .profile: - self.updatedPeerProfileEmoji = result case .status: - self.updatedPeerStatusEmoji = result + self.updatedPeerStatus = (result?.fileId.id).flatMap { PeerEmojiStatus(fileId: $0, expirationDate: timestamp) } + default: + break } self.state?.updated(transition: .spring(duration: 0.4)) - }), + }) + default: + mappedMode = .backgroundSelection(completion: { [weak self] result in + guard let self else { + return + } + if let result { + self.cachedIconFiles[result.fileId.id] = result + } + switch subject { + case .reply: + self.updatedPeerNameEmoji = (result?.fileId.id) + case .profile: + self.updatedPeerProfileEmoji = (result?.fileId.id) + case .status: + self.updatedPeerStatus = (result?.fileId.id).flatMap { PeerEmojiStatus(fileId: $0, expirationDate: nil) } + } + self.state?.updated(transition: .spring(duration: 0.4)) + }) + } + + let controller = EmojiStatusSelectionController( + context: component.context, + mode: mappedMode, sourceView: sourceView, emojiContent: EmojiPagerContentComponent.emojiInputData( context: component.context, animationCache: component.context.animationCache, animationRenderer: component.context.animationRenderer, isStandalone: false, - subject: subject == .status ? .status : .backgroundIcon, + subject: mappedSubject, hasTrending: false, topReactionItems: [], areUnicodeEmojiEnabled: false, @@ -588,6 +686,9 @@ final class ChannelAppearanceScreenComponent: Component { currentSelection: currentFileId, color: color, destinationItemView: { [weak sourceView] in + guard let sourceView else { + return nil + } return sourceView } ) @@ -618,6 +719,9 @@ final class ChannelAppearanceScreenComponent: Component { guard let self else { return } + if self.contentsData == nil, case let .channel(channel) = contentsData.peer { + self.boostLevel = channel.approximateBoostLevel.flatMap(Int.init) + } self.contentsData = contentsData if !self.isUpdating { self.state?.updated(transition: .immediate) @@ -631,6 +735,7 @@ final class ChannelAppearanceScreenComponent: Component { guard let self else { return } + self.boostLevel = boostStatus?.level self.boostStatus = boostStatus if !self.isUpdating { self.state?.updated(transition: .immediate) @@ -638,65 +743,40 @@ final class ChannelAppearanceScreenComponent: Component { }) } - guard let contentsData = self.contentsData, var peer = contentsData.peer else { + guard let contentsData = self.contentsData, var peer = contentsData.peer, let resolvedState = self.resolveState() else { return availableSize } var requiredLevel = 1 let replyIconLevel = 5 - let profileIconLevel = 7 - let emojiStatusLevel = 8 + var profileIconLevel = 7 + var emojiStatusLevel = 8 let themeLevel = 9 - let nameColor: PeerNameColor - if let updatedPeerNameColor = self.updatedPeerNameColor { - nameColor = updatedPeerNameColor - } else if let peerNameColor = peer.nameColor { - nameColor = peerNameColor - } else { - nameColor = .blue + if let data = component.context.currentAppConfiguration.with({ $0 }).data { + if let value = data["channel_profile_color_level_min"] as? Double { + profileIconLevel = Int(value) + } + if let value = data["channel_emoji_status_level_min"] as? Double { + emojiStatusLevel = Int(value) + } } - let profileColor: PeerNameColor? - if case let .some(value) = self.updatedPeerProfileColor { - profileColor = value - } else if let peerProfileColor = peer.profileColor { - profileColor = peerProfileColor - } else { - profileColor = nil - } + let profileColor = resolvedState.profileColor - let replyFileId: Int64? - if case let .some(value) = self.updatedPeerNameEmoji { - replyFileId = value - } else { - replyFileId = contentsData.peer?.backgroundEmojiId - } + let replyFileId = resolvedState.replyFileId if replyFileId != nil { requiredLevel = max(requiredLevel, replyIconLevel) } - let backgroundFileId: Int64? - if case let .some(value) = self.updatedPeerProfileEmoji { - backgroundFileId = value - } else { - backgroundFileId = contentsData.peer?.profileBackgroundEmojiId - } - if backgroundFileId != nil { + let backgroundFileId = resolvedState.backgroundFileId + if profileColor != nil || backgroundFileId != nil { requiredLevel = max(requiredLevel, profileIconLevel) } - let emojiStatus: PeerEmojiStatus? - if case let .some(value) = self.updatedPeerStatusEmoji { - if let value { - emojiStatus = PeerEmojiStatus(fileId: value, expirationDate: nil) - } else { - emojiStatus = nil - } - } else { - emojiStatus = contentsData.peer?.emojiStatus - } + let emojiStatus = resolvedState.emojiStatus + if emojiStatus != nil { requiredLevel = max(requiredLevel, emojiStatusLevel) } @@ -712,13 +792,51 @@ final class ChannelAppearanceScreenComponent: Component { } } + if let currentTheme = self.currentTheme, (self.resolvedCurrentTheme?.reference != currentTheme || self.resolvedCurrentTheme?.isDark != environment.theme.overallDarkAppearance), (self.resolvingCurrentTheme?.reference != currentTheme || self.resolvingCurrentTheme?.isDark != environment.theme.overallDarkAppearance) { + self.resolvingCurrentTheme?.disposable.dispose() + + let disposable = MetaDisposable() + self.resolvingCurrentTheme = (currentTheme, environment.theme.overallDarkAppearance, disposable) + + var presentationTheme: PresentationTheme? + switch currentTheme { + case .builtin: + presentationTheme = makePresentationTheme(mediaBox: component.context.sharedContext.accountManager.mediaBox, themeReference: .builtin(environment.theme.overallDarkAppearance ? .night : .dayClassic)) + case let .cloud(cloudTheme): + presentationTheme = makePresentationTheme(cloudTheme: cloudTheme.theme, dark: environment.theme.overallDarkAppearance) + default: + presentationTheme = makePresentationTheme(mediaBox: component.context.sharedContext.accountManager.mediaBox, themeReference: currentTheme) + } + if let presentationTheme { + let resolvedWallpaper: Signal + if case let .file(file) = presentationTheme.chat.defaultWallpaper, file.id == 0 { + resolvedWallpaper = cachedWallpaper(account: component.context.account, slug: file.slug, settings: file.settings) + |> map { wallpaper -> TelegramWallpaper? in + return wallpaper?.wallpaper + } + } else { + resolvedWallpaper = .single(presentationTheme.chat.defaultWallpaper) + } + disposable.set((resolvedWallpaper + |> deliverOnMainQueue).startStrict(next: { [weak self] resolvedWallpaper in + guard let self, let environment = self.environment else { + return + } + self.resolvedCurrentTheme = (currentTheme, environment.theme.overallDarkAppearance, presentationTheme, resolvedWallpaper) + if !self.isUpdating { + self.state?.updated(transition: .immediate) + } + })) + } + } + if self.currentTheme != nil && self.currentTheme != chatThemes.first { requiredLevel = max(requiredLevel, themeLevel) } if case let .user(user) = peer { peer = .user(user - .withUpdatedNameColor(nameColor) + .withUpdatedNameColor(resolvedState.nameColor) .withUpdatedProfileColor(profileColor) .withUpdatedEmojiStatus(emojiStatus) .withUpdatedBackgroundEmojiId(replyFileId) @@ -726,7 +844,7 @@ final class ChannelAppearanceScreenComponent: Component { ) } else if case let .channel(channel) = peer { peer = .channel(channel - .withUpdatedNameColor(nameColor) + .withUpdatedNameColor(resolvedState.nameColor) .withUpdatedProfileColor(profileColor) .withUpdatedEmojiStatus(emojiStatus) .withUpdatedBackgroundEmojiId(replyFileId) @@ -750,16 +868,17 @@ final class ChannelAppearanceScreenComponent: Component { let presentationData = component.context.sharedContext.currentPresentationData.with { $0 } + //TODO:localize let messageItem = PeerNameColorChatPreviewItem.MessageItem( outgoing: false, peerId: EnginePeer.Id(namespace: peer.id.namespace, id: PeerId.Id._internalFromInt64Value(0)), author: peer.compactDisplayTitle, photo: peer.profileImageRepresentations, - nameColor: nameColor, + nameColor: resolvedState.nameColor, backgroundEmojiId: replyFileId, - reply: (peer.compactDisplayTitle, environment.strings.NameColor_ChatPreview_ReplyText_Channel), - linkPreview: (environment.strings.NameColor_ChatPreview_LinkSite, environment.strings.NameColor_ChatPreview_LinkTitle, environment.strings.NameColor_ChatPreview_LinkText), - text: environment.strings.NameColor_ChatPreview_MessageText_Channel + reply: (peer.compactDisplayTitle, "Reply to your channel"), + linkPreview: ("Telegram", "Link Preview", "This preview will also be tinted."), + text: "The color you select will be used for the channel's name" ) var replyLogoContents: [AnyComponentWithIdentity] = [] @@ -771,13 +890,22 @@ final class ChannelAppearanceScreenComponent: Component { )), maximumNumberOfLines: 0 )))) - if replyFileId != nil, let boostStatus = self.boostStatus, boostStatus.level < replyIconLevel { + if replyFileId != nil, let boostLevel = self.boostLevel, boostLevel < replyIconLevel { replyLogoContents.append(AnyComponentWithIdentity(id: 1, component: AnyComponent(BoostLevelIconComponent( strings: environment.strings, level: replyIconLevel )))) } + var chatPreviewTheme: PresentationTheme = environment.theme + var chatPreviewWallpaper: TelegramWallpaper = presentationData.chatWallpaper + if let resolvedCurrentTheme = self.resolvedCurrentTheme { + chatPreviewTheme = resolvedCurrentTheme.theme + if let wallpaper = resolvedCurrentTheme.wallpaper { + chatPreviewWallpaper = wallpaper + } + } + let replySectionSize = self.replySection.update( transition: transition, component: AnyComponent(ListSectionComponent( @@ -795,13 +923,13 @@ final class ChannelAppearanceScreenComponent: Component { AnyComponentWithIdentity(id: 0, component: AnyComponent(ListItemComponentAdaptor( itemGenerator: PeerNameColorChatPreviewItem( context: component.context, - theme: environment.theme, - componentTheme: environment.theme, + theme: chatPreviewTheme, + componentTheme: chatPreviewTheme, strings: environment.strings, sectionId: 0, fontSize: presentationData.chatFontSize, chatBubbleCorners: presentationData.chatBubbleCorners, - wallpaper: presentationData.chatWallpaper, + wallpaper: chatPreviewWallpaper, dateTimeFormat: environment.dateTimeFormat, nameDisplayOrder: presentationData.nameDisplayOrder, messageItems: [messageItem] @@ -813,7 +941,7 @@ final class ChannelAppearanceScreenComponent: Component { theme: environment.theme, colors: component.context.peerNameColors, isProfile: false, - currentColor: nameColor, + currentColor: resolvedState.nameColor, updated: { [weak self] value in guard let self else { return @@ -830,32 +958,16 @@ final class ChannelAppearanceScreenComponent: Component { title: AnyComponent(HStack(replyLogoContents, spacing: 6.0)), icon: AnyComponentWithIdentity(id: 0, component: AnyComponent(EmojiActionIconComponent( context: component.context, - color: component.context.peerNameColors.get(nameColor, dark: environment.theme.overallDarkAppearance).main, + color: component.context.peerNameColors.get(resolvedState.nameColor, dark: environment.theme.overallDarkAppearance).main, fileId: replyFileId, file: replyFileId.flatMap { self.cachedIconFiles[$0] } ))), action: { [weak self] view in - guard let self, let contentsData = self.contentsData, let view = view as? ListActionItemComponent.View, let iconView = view.iconView else { + guard let self, let resolvedState = self.resolveState(), let view = view as? ListActionItemComponent.View, let iconView = view.iconView else { return } - let nameColor: PeerNameColor - if let updatedPeerNameColor = self.updatedPeerNameColor { - nameColor = updatedPeerNameColor - } else if let peerNameColor = peer.nameColor { - nameColor = peerNameColor - } else { - nameColor = .blue - } - - let currentFileId: Int64? - if case let .some(value) = self.updatedPeerNameEmoji { - currentFileId = value - } else { - currentFileId = contentsData.peer?.backgroundEmojiId - } - - self.openEmojiSetup(sourceView: iconView, currentFileId: currentFileId, color: component.context.peerNameColors.get(nameColor, dark: environment.theme.overallDarkAppearance).main, subject: .reply) + self.openEmojiSetup(sourceView: iconView, currentFileId: resolvedState.replyFileId, color: component.context.peerNameColors.get(resolvedState.nameColor, dark: environment.theme.overallDarkAppearance).main, subject: .reply) } ))) ] @@ -884,7 +996,7 @@ final class ChannelAppearanceScreenComponent: Component { )), maximumNumberOfLines: 0 )))) - if currentTheme != chatThemes[0], let boostStatus = self.boostStatus, boostStatus.level < themeLevel { + if currentTheme != chatThemes[0], let boostLevel = self.boostLevel, boostLevel < themeLevel { wallpaperLogoContents.append(AnyComponentWithIdentity(id: 1, component: AnyComponent(BoostLevelIconComponent( strings: environment.strings, level: themeLevel @@ -915,7 +1027,8 @@ final class ChannelAppearanceScreenComponent: Component { animatedEmojiStickers: component.context.animatedEmojiStickers, themeSpecificAccentColors: [:], themeSpecificChatWallpapers: [:], - nightMode: false, + nightMode: environment.theme.overallDarkAppearance, + channelMode: true, currentTheme: currentTheme, updatedTheme: { [weak self] value in guard let self else { @@ -964,7 +1077,7 @@ final class ChannelAppearanceScreenComponent: Component { )), maximumNumberOfLines: 0 )))) - if backgroundFileId != nil, let boostStatus = self.boostStatus, boostStatus.level < profileIconLevel { + if backgroundFileId != nil, let boostLevel = self.boostLevel, boostLevel < profileIconLevel { profileLogoContents.append(AnyComponentWithIdentity(id: 1, component: AnyComponent(BoostLevelIconComponent( strings: environment.strings, level: profileIconLevel @@ -1042,28 +1155,12 @@ final class ChannelAppearanceScreenComponent: Component { file: backgroundFileId.flatMap { self.cachedIconFiles[$0] } ))), action: { [weak self] view in - guard let self, let contentsData = self.contentsData, let view = view as? ListActionItemComponent.View, let iconView = view.iconView else { + guard let self, let resolvedState = self.resolveState(), let view = view as? ListActionItemComponent.View, let iconView = view.iconView else { return } - let currentFileId: Int64? - if case let .some(value) = self.updatedPeerProfileEmoji { - currentFileId = value - } else { - currentFileId = contentsData.peer?.profileBackgroundEmojiId - } - - let profileColor: PeerNameColor? - if case let .some(value) = self.updatedPeerProfileColor { - profileColor = value - } else if let peerProfileColor = peer.profileColor { - profileColor = peerProfileColor - } else { - profileColor = nil - } - - self.openEmojiSetup(sourceView: iconView, currentFileId: currentFileId, color: profileColor.flatMap { profileColor in - component.context.peerNameColors.getProfile(profileColor, dark: environment.theme.overallDarkAppearance, subject: .palette).main + self.openEmojiSetup(sourceView: iconView, currentFileId: resolvedState.backgroundFileId, color: resolvedState.profileColor.flatMap { + component.context.peerNameColors.getProfile($0, dark: environment.theme.overallDarkAppearance, subject: .palette).main } ?? environment.theme.list.itemAccentColor, subject: .profile) } ))) @@ -1091,7 +1188,7 @@ final class ChannelAppearanceScreenComponent: Component { )), maximumNumberOfLines: 0 )))) - if emojiStatus != nil, let boostStatus = self.boostStatus, boostStatus.level < emojiStatusLevel { + if emojiStatus != nil, let boostLevel = self.boostLevel, boostLevel < emojiStatusLevel { emojiStatusContents.append(AnyComponentWithIdentity(id: 1, component: AnyComponent(BoostLevelIconComponent( strings: environment.strings, level: emojiStatusLevel @@ -1174,18 +1271,11 @@ final class ChannelAppearanceScreenComponent: Component { file: statusFileId.flatMap { self.cachedIconFiles[$0] } ))), action: { [weak self] view in - guard let self, let contentsData = self.contentsData, let view = view as? ListActionItemComponent.View, let iconView = view.iconView else { + guard let self, let resolvedState = self.resolveState(), let view = view as? ListActionItemComponent.View, let iconView = view.iconView else { return } - let currentFileId: Int64? - if case let .some(value) = self.updatedPeerStatusEmoji { - currentFileId = value - } else { - currentFileId = contentsData.peer?.emojiStatus?.fileId - } - - self.openEmojiSetup(sourceView: iconView, currentFileId: currentFileId, color: nil, subject: .status) + self.openEmojiSetup(sourceView: iconView, currentFileId: resolvedState.emojiStatus?.fileId, color: nil, subject: .status) } ))) ] @@ -1210,7 +1300,7 @@ final class ChannelAppearanceScreenComponent: Component { Text(text: "Apply Changes", font: Font.semibold(17.0), color: environment.theme.list.itemCheckColors.foregroundColor) ))) - if let boostStatus = self.boostStatus, requiredLevel > boostStatus.level { + if let boostLevel = self.boostLevel, requiredLevel > boostLevel { buttonContents.append(AnyComponentWithIdentity(id: AnyHashable(1 as Int), component: AnyComponent(PremiumLockButtonSubtitleComponent( count: requiredLevel, theme: environment.theme, @@ -1266,6 +1356,8 @@ final class ChannelAppearanceScreenComponent: Component { self.bottomPanelSeparator.backgroundColor = environment.theme.rootController.navigationBar.separatorColor.cgColor transition.setFrame(layer: self.bottomPanelSeparator, frame: CGRect(origin: CGPoint(x: bottomPanelFrame.minX, y: bottomPanelFrame.minY), size: CGSize(width: bottomPanelFrame.width, height: UIScreenPixel))) + let previousBounds = self.scrollView.bounds + let contentSize = CGSize(width: availableSize.width, height: contentHeight) if self.scrollView.frame != CGRect(origin: CGPoint(), size: availableSize) { self.scrollView.frame = CGRect(origin: CGPoint(), size: availableSize) @@ -1278,6 +1370,14 @@ final class ChannelAppearanceScreenComponent: Component { self.scrollView.scrollIndicatorInsets = scrollInsets } + if !previousBounds.isEmpty, !transition.animation.isImmediate { + let bounds = self.scrollView.bounds + if bounds.maxY != previousBounds.maxY { + let offsetY = previousBounds.maxY - bounds.maxY + transition.animateBoundsOrigin(view: self.scrollView, from: CGPoint(x: 0.0, y: offsetY), to: CGPoint(), additive: true) + } + } + self.updateScrolling(transition: transition) return availableSize diff --git a/submodules/TelegramUI/Components/Settings/PeerNameColorScreen/Sources/PeerNameColorChatPreviewItem.swift b/submodules/TelegramUI/Components/Settings/PeerNameColorScreen/Sources/PeerNameColorChatPreviewItem.swift index ec0d99e68c..3133579374 100644 --- a/submodules/TelegramUI/Components/Settings/PeerNameColorScreen/Sources/PeerNameColorChatPreviewItem.swift +++ b/submodules/TelegramUI/Components/Settings/PeerNameColorScreen/Sources/PeerNameColorChatPreviewItem.swift @@ -299,7 +299,7 @@ final class PeerNameColorChatPreviewItemNode: ListViewItemNode { strongSelf.containerNode.frame = CGRect(origin: CGPoint(), size: contentSize) - if let currentItem, currentItem.messageItems.first?.nameColor != item.messageItems.first?.nameColor || currentItem.messageItems.first?.backgroundEmojiId != item.messageItems.first?.backgroundEmojiId { + if let currentItem, currentItem.messageItems.first?.nameColor != item.messageItems.first?.nameColor || currentItem.messageItems.first?.backgroundEmojiId != item.messageItems.first?.backgroundEmojiId || currentItem.theme !== item.theme || currentItem.wallpaper != item.wallpaper { if let snapshot = strongSelf.view.snapshotView(afterScreenUpdates: false) { snapshot.frame = CGRect(origin: CGPoint(x: 0.0, y: -insets.top), size: snapshot.frame.size) strongSelf.view.addSubview(snapshot) diff --git a/submodules/TelegramUI/Components/Settings/ThemeCarouselItem/Sources/ThemeCarouselItem.swift b/submodules/TelegramUI/Components/Settings/ThemeCarouselItem/Sources/ThemeCarouselItem.swift index 9b30e4247e..2b0dc7d7f4 100644 --- a/submodules/TelegramUI/Components/Settings/ThemeCarouselItem/Sources/ThemeCarouselItem.swift +++ b/submodules/TelegramUI/Components/Settings/ThemeCarouselItem/Sources/ThemeCarouselItem.swift @@ -26,6 +26,7 @@ private struct ThemeCarouselThemeEntry: Comparable, Identifiable { let emojiFile: TelegramMediaFile? let themeReference: PresentationThemeReference let nightMode: Bool + let channelMode: Bool let themeSpecificAccentColors: [Int64: PresentationThemeAccentColor] let themeSpecificChatWallpapers: [Int64: TelegramWallpaper] var selected: Bool @@ -50,6 +51,9 @@ private struct ThemeCarouselThemeEntry: Comparable, Identifiable { if lhs.nightMode != rhs.nightMode { return false } + if lhs.channelMode != rhs.channelMode { + return false + } if lhs.themeSpecificAccentColors != rhs.themeSpecificAccentColors { return false } @@ -76,7 +80,7 @@ private struct ThemeCarouselThemeEntry: Comparable, Identifiable { } func item(context: AccountContext, action: @escaping (PresentationThemeReference) -> Void, contextAction: ((PresentationThemeReference, ASDisplayNode, ContextGesture?) -> Void)?) -> ListViewItem { - return ThemeCarouselThemeIconItem(context: context, emojiFile: self.emojiFile, themeReference: self.themeReference, nightMode: self.nightMode, themeSpecificAccentColors: self.themeSpecificAccentColors, themeSpecificChatWallpapers: self.themeSpecificChatWallpapers, selected: self.selected, theme: self.theme, strings: self.strings, wallpaper: self.wallpaper, action: action, contextAction: contextAction) + return ThemeCarouselThemeIconItem(context: context, emojiFile: self.emojiFile, themeReference: self.themeReference, nightMode: self.nightMode, channelMode: self.channelMode, themeSpecificAccentColors: self.themeSpecificAccentColors, themeSpecificChatWallpapers: self.themeSpecificChatWallpapers, selected: self.selected, theme: self.theme, strings: self.strings, wallpaper: self.wallpaper, action: action, contextAction: contextAction) } } @@ -86,6 +90,7 @@ public class ThemeCarouselThemeIconItem: ListViewItem { public let emojiFile: TelegramMediaFile? public let themeReference: PresentationThemeReference public let nightMode: Bool + public let channelMode: Bool public let themeSpecificAccentColors: [Int64: PresentationThemeAccentColor] public let themeSpecificChatWallpapers: [Int64: TelegramWallpaper] public let selected: Bool @@ -95,11 +100,12 @@ public class ThemeCarouselThemeIconItem: ListViewItem { public let action: (PresentationThemeReference) -> Void public let contextAction: ((PresentationThemeReference, ASDisplayNode, ContextGesture?) -> Void)? - public init(context: AccountContext, emojiFile: TelegramMediaFile?, themeReference: PresentationThemeReference, nightMode: Bool, themeSpecificAccentColors: [Int64: PresentationThemeAccentColor], themeSpecificChatWallpapers: [Int64: TelegramWallpaper], selected: Bool, theme: PresentationTheme, strings: PresentationStrings, wallpaper: TelegramWallpaper?, action: @escaping (PresentationThemeReference) -> Void, contextAction: ((PresentationThemeReference, ASDisplayNode, ContextGesture?) -> Void)?) { + public init(context: AccountContext, emojiFile: TelegramMediaFile?, themeReference: PresentationThemeReference, nightMode: Bool, channelMode: Bool, themeSpecificAccentColors: [Int64: PresentationThemeAccentColor], themeSpecificChatWallpapers: [Int64: TelegramWallpaper], selected: Bool, theme: PresentationTheme, strings: PresentationStrings, wallpaper: TelegramWallpaper?, action: @escaping (PresentationThemeReference) -> Void, contextAction: ((PresentationThemeReference, ASDisplayNode, ContextGesture?) -> Void)?) { self.context = context self.emojiFile = emojiFile self.themeReference = themeReference self.nightMode = nightMode + self.channelMode = channelMode self.themeSpecificAccentColors = themeSpecificAccentColors self.themeSpecificChatWallpapers = themeSpecificChatWallpapers self.selected = selected @@ -339,6 +345,7 @@ private final class ThemeCarouselThemeItemIconNode : ListViewItemNode { var updatedThemeReference = false var updatedTheme = false var updatedNightMode = false + var updatedChannelMode = false var updatedWallpaper = false var updatedSelected = false @@ -348,6 +355,9 @@ private final class ThemeCarouselThemeItemIconNode : ListViewItemNode { if currentItem?.nightMode != item.nightMode { updatedNightMode = true } + if currentItem?.channelMode != item.channelMode { + updatedChannelMode = true + } if currentItem?.wallpaper != item.wallpaper { updatedWallpaper = true } @@ -373,7 +383,7 @@ private final class ThemeCarouselThemeItemIconNode : ListViewItemNode { if let strongSelf = self { strongSelf.item = item - if updatedThemeReference || updatedWallpaper || updatedNightMode { + if updatedThemeReference || updatedWallpaper || updatedNightMode || updatedChannelMode { var themeReference = item.themeReference if case .builtin = themeReference, item.nightMode { themeReference = .builtin(.night) @@ -382,7 +392,7 @@ private final class ThemeCarouselThemeItemIconNode : ListViewItemNode { let color = item.themeSpecificAccentColors[themeReference.index] let wallpaper = item.themeSpecificChatWallpapers[themeReference.index] - strongSelf.imageNode.setSignal(themeIconImage(account: item.context.account, accountManager: item.context.sharedContext.accountManager, theme: themeReference, color: color, wallpaper: wallpaper ?? item.wallpaper, nightMode: item.nightMode, emoticon: true)) + strongSelf.imageNode.setSignal(themeIconImage(account: item.context.account, accountManager: item.context.sharedContext.accountManager, theme: themeReference, color: color, wallpaper: wallpaper ?? item.wallpaper, nightMode: item.nightMode, channelMode: item.channelMode, emoticon: true)) strongSelf.imageNode.backgroundColor = nil } @@ -519,12 +529,13 @@ public class ThemeCarouselThemeItem: ListViewItem, ItemListItem, ListItemCompone public let themeSpecificAccentColors: [Int64: PresentationThemeAccentColor] public let themeSpecificChatWallpapers: [Int64: TelegramWallpaper] public let nightMode: Bool + public let channelMode: Bool public let currentTheme: PresentationThemeReference public let updatedTheme: (PresentationThemeReference) -> Void public let contextAction: ((PresentationThemeReference, ASDisplayNode, ContextGesture?) -> Void)? public let tag: ItemListItemTag? - public init(context: AccountContext, theme: PresentationTheme, strings: PresentationStrings, sectionId: ItemListSectionId, themes: [PresentationThemeReference], animatedEmojiStickers: [String: [StickerPackItem]], themeSpecificAccentColors: [Int64: PresentationThemeAccentColor], themeSpecificChatWallpapers: [Int64: TelegramWallpaper], nightMode: Bool, currentTheme: PresentationThemeReference, updatedTheme: @escaping (PresentationThemeReference) -> Void, contextAction: ((PresentationThemeReference, ASDisplayNode, ContextGesture?) -> Void)?, tag: ItemListItemTag? = nil) { + public init(context: AccountContext, theme: PresentationTheme, strings: PresentationStrings, sectionId: ItemListSectionId, themes: [PresentationThemeReference], animatedEmojiStickers: [String: [StickerPackItem]], themeSpecificAccentColors: [Int64: PresentationThemeAccentColor], themeSpecificChatWallpapers: [Int64: TelegramWallpaper], nightMode: Bool, channelMode: Bool = false, currentTheme: PresentationThemeReference, updatedTheme: @escaping (PresentationThemeReference) -> Void, contextAction: ((PresentationThemeReference, ASDisplayNode, ContextGesture?) -> Void)?, tag: ItemListItemTag? = nil) { self.context = context self.theme = theme self.strings = strings @@ -533,6 +544,7 @@ public class ThemeCarouselThemeItem: ListViewItem, ItemListItem, ListItemCompone self.themeSpecificAccentColors = themeSpecificAccentColors self.themeSpecificChatWallpapers = themeSpecificChatWallpapers self.nightMode = nightMode + self.channelMode = channelMode self.currentTheme = currentTheme self.updatedTheme = updatedTheme self.contextAction = contextAction @@ -602,6 +614,9 @@ public class ThemeCarouselThemeItem: ListViewItem, ItemListItem, ListItemCompone if lhs.nightMode != rhs.nightMode { return false } + if lhs.channelMode != rhs.channelMode { + return false + } if lhs.currentTheme != rhs.currentTheme { return false } @@ -827,12 +842,12 @@ public class ThemeCarouselThemeItemNode: ListViewItemNode, ItemListItemNode { hasCurrentTheme = true } let emojiFile = theme.emoticon.flatMap { item.animatedEmojiStickers[$0]?.first?.file } - entries.append(ThemeCarouselThemeEntry(index: index, emojiFile: emojiFile, themeReference: theme, nightMode: item.nightMode, themeSpecificAccentColors: item.themeSpecificAccentColors, themeSpecificChatWallpapers: item.themeSpecificChatWallpapers, selected: selected, theme: item.theme, strings: item.strings, wallpaper: nil)) + entries.append(ThemeCarouselThemeEntry(index: index, emojiFile: emojiFile, themeReference: theme, nightMode: item.nightMode, channelMode: item.channelMode, themeSpecificAccentColors: item.themeSpecificAccentColors, themeSpecificChatWallpapers: item.themeSpecificChatWallpapers, selected: selected, theme: item.theme, strings: item.strings, wallpaper: nil)) index += 1 } if !hasCurrentTheme { - entries.append(ThemeCarouselThemeEntry(index: index, emojiFile: nil, themeReference: item.currentTheme, nightMode: false, themeSpecificAccentColors: item.themeSpecificAccentColors, themeSpecificChatWallpapers: item.themeSpecificChatWallpapers, selected: true, theme: item.theme, strings: item.strings, wallpaper: nil)) + entries.append(ThemeCarouselThemeEntry(index: index, emojiFile: nil, themeReference: item.currentTheme, nightMode: false, channelMode: item.channelMode, themeSpecificAccentColors: item.themeSpecificAccentColors, themeSpecificChatWallpapers: item.themeSpecificChatWallpapers, selected: true, theme: item.theme, strings: item.strings, wallpaper: nil)) } let action: (PresentationThemeReference) -> Void = { [weak self] themeReference in diff --git a/submodules/TelegramUI/Components/Stories/PeerListItemComponent/Sources/PeerListItemComponent.swift b/submodules/TelegramUI/Components/Stories/PeerListItemComponent/Sources/PeerListItemComponent.swift index e69c895c20..3deab23b96 100644 --- a/submodules/TelegramUI/Components/Stories/PeerListItemComponent/Sources/PeerListItemComponent.swift +++ b/submodules/TelegramUI/Components/Stories/PeerListItemComponent/Sources/PeerListItemComponent.swift @@ -604,7 +604,7 @@ public final class PeerListItemComponent: Component { statusIcon = .text(color: component.theme.chat.message.incoming.scamColor, string: component.strings.Message_ScamAccount.uppercased()) } else if peer.isFake { statusIcon = .text(color: component.theme.chat.message.incoming.scamColor, string: component.strings.Message_FakeAccount.uppercased()) - } else if case let .user(user) = peer, let emojiStatus = user.emojiStatus { + } else if let emojiStatus = peer.emojiStatus { statusIcon = .animation(content: .customEmoji(fileId: emojiStatus.fileId), size: CGSize(width: 20.0, height: 20.0), placeholderColor: component.theme.list.mediaPlaceholderColor, themeColor: component.theme.list.itemAccentColor, loopMode: .count(2)) } else if peer.isVerified { statusIcon = .verified(fillColor: component.theme.list.itemCheckColors.fillColor, foregroundColor: component.theme.list.itemCheckColors.foregroundColor, sizeType: .compact) diff --git a/submodules/TelegramUI/Sources/ChatReportPeerTitlePanelNode.swift b/submodules/TelegramUI/Sources/ChatReportPeerTitlePanelNode.swift index d04a66cebb..5f758642c5 100644 --- a/submodules/TelegramUI/Sources/ChatReportPeerTitlePanelNode.swift +++ b/submodules/TelegramUI/Sources/ChatReportPeerTitlePanelNode.swift @@ -540,6 +540,11 @@ final class ChatReportPeerTitlePanelNode: ChatTitleAccessoryPanelNode { } else { emojiStatus = emojiStatusValue } + } else if let channel = interfaceState.renderedPeer?.peer as? TelegramChannel, let emojiStatusValue = channel.emojiStatus { + if channel.isFake || channel.isScam { + } else { + emojiStatus = emojiStatusValue + } } /*#if DEBUG diff --git a/submodules/WallpaperResources/Sources/WallpaperResources.swift b/submodules/WallpaperResources/Sources/WallpaperResources.swift index fac8add04e..d3a39df2eb 100644 --- a/submodules/WallpaperResources/Sources/WallpaperResources.swift +++ b/submodules/WallpaperResources/Sources/WallpaperResources.swift @@ -1398,7 +1398,7 @@ private let messageImage: UIImage = { return messageBubbleImage(maxCornerRadius: 16.0, minCornerRadius: 16.0, incoming: true, fillColor: .white, strokeColor: .clear, neighbors: .none, shadow: nil, wallpaper: .color(0x000000), knockout: false) }() -public func themeIconImage(account: Account, accountManager: AccountManager, theme: PresentationThemeReference, color: PresentationThemeAccentColor?, wallpaper: TelegramWallpaper? = nil, nightMode: Bool? = nil, emoticon: Bool = false, large: Bool = false, qr: Bool = false, message: Bool = false) -> Signal<(TransformImageArguments) -> DrawingContext?, NoError> { +public func themeIconImage(account: Account, accountManager: AccountManager, theme: PresentationThemeReference, color: PresentationThemeAccentColor?, wallpaper: TelegramWallpaper? = nil, nightMode: Bool? = nil, channelMode: Bool? = nil, emoticon: Bool = false, large: Bool = false, qr: Bool = false, message: Bool = false) -> Signal<(TransformImageArguments) -> DrawingContext?, NoError> { let colorsSignal: Signal<((UIColor, UIColor?, [UInt32]), [UIColor], [UIColor], UIImage?, Bool, Bool, CGFloat, Int32?), NoError> var reference: MediaResourceReference? @@ -1693,6 +1693,7 @@ public func themeIconImage(account: Account, accountManager: AccountManager 1 { c.clip() @@ -1748,7 +1749,7 @@ public func themeIconImage(account: Account, accountManager: AccountManager