From 3d4a2ebe4ba19f6224fa05a96fe8ada093871ea7 Mon Sep 17 00:00:00 2001 From: Ilya Laktyushin Date: Mon, 6 Jun 2022 14:46:30 +0400 Subject: [PATCH 1/3] Various improvements --- .../Telegram-iOS/en.lproj/Localizable.strings | 2 + .../Sources/AccountContext.swift | 24 ++++++- .../ChatItemGalleryFooterContentNode.swift | 6 +- .../Sources/AppIconsDemoComponent.swift | 13 +++- .../Themes/ThemeSettingsController.swift | 7 +- .../Sources/StickerPackScreen.swift | 62 ++++++++++-------- .../Sources/StickerPreviewPeekContent.swift | 2 +- .../Icons/Black.imageset/Contents.json | 21 ++++++ .../Icons/Black.imageset/black@270x270.png | Bin 0 -> 35809 bytes .../Premium/Icons/Contents.json | 9 +++ .../Icons/Premium.imageset/Contents.json | 21 ++++++ .../Premium.imageset/premium@270x270.png | Bin 0 -> 18470 bytes .../Icons/Turbo.imageset/Contents.json | 21 ++++++ .../Icons/Turbo.imageset/turbo@270x270.png | Bin 0 -> 31903 bytes .../TelegramUI/Sources/AppDelegate.swift | 9 +++ .../TelegramUI/Sources/ChatController.swift | 15 ++++- .../ChatInterfaceStateContextMenus.swift | 16 ++++- .../Sources/ChatMediaInputNode.swift | 44 +++++++++---- .../Sources/DrawingStickersScreen.swift | 4 +- .../Sources/FeaturedStickersScreen.swift | 6 +- ...textResultsChatInputContextPanelNode.swift | 3 +- ...rizontalStickersChatContextPanelNode.swift | 3 +- .../Sources/InlineReactionSearchPanel.swift | 3 +- .../Sources/NotificationContentContext.swift | 2 +- .../Sources/PeerInfo/PeerInfoScreen.swift | 22 ++++--- .../Sources/ShareExtensionContext.swift | 5 +- .../StickersChatInputContextPanelNode.swift | 3 +- 27 files changed, 254 insertions(+), 69 deletions(-) create mode 100644 submodules/TelegramUI/Images.xcassets/Premium/Icons/Black.imageset/Contents.json create mode 100644 submodules/TelegramUI/Images.xcassets/Premium/Icons/Black.imageset/black@270x270.png create mode 100644 submodules/TelegramUI/Images.xcassets/Premium/Icons/Contents.json create mode 100644 submodules/TelegramUI/Images.xcassets/Premium/Icons/Premium.imageset/Contents.json create mode 100644 submodules/TelegramUI/Images.xcassets/Premium/Icons/Premium.imageset/premium@270x270.png create mode 100644 submodules/TelegramUI/Images.xcassets/Premium/Icons/Turbo.imageset/Contents.json create mode 100644 submodules/TelegramUI/Images.xcassets/Premium/Icons/Turbo.imageset/turbo@270x270.png diff --git a/Telegram/Telegram-iOS/en.lproj/Localizable.strings b/Telegram/Telegram-iOS/en.lproj/Localizable.strings index b01cdd2105..1ca3aacb9a 100644 --- a/Telegram/Telegram-iOS/en.lproj/Localizable.strings +++ b/Telegram/Telegram-iOS/en.lproj/Localizable.strings @@ -7705,3 +7705,5 @@ Sorry for the inconvenience."; "Premium.Purchase.ErrorUnknown" = "An error occurred. Please try again."; "Premium.Purchase.ErrorNetwork" = "Please check your internet connection and try again."; "Premium.Purchase.ErrorNotAllowed" = "The device is not not allowed to make the payment."; + +"Settings.Premium" = "Telegram Premium"; diff --git a/submodules/AccountContext/Sources/AccountContext.swift b/submodules/AccountContext/Sources/AccountContext.swift index 8e1cd29158..3a7df00c3d 100644 --- a/submodules/AccountContext/Sources/AccountContext.swift +++ b/submodules/AccountContext/Sources/AccountContext.swift @@ -51,6 +51,7 @@ public final class TelegramApplicationBindings { public let pushIdleTimerExtension: () -> Disposable public let openSettings: () -> Void public let openAppStorePage: () -> Void + public let openSubscriptions: () -> Void public let registerForNotifications: (@escaping (Bool) -> Void) -> Void public let requestSiriAuthorization: (@escaping (Bool) -> Void) -> Void public let siriAuthorization: () -> AccessType @@ -62,7 +63,7 @@ public final class TelegramApplicationBindings { public let requestSetAlternateIconName: (String?, @escaping (Bool) -> Void) -> Void public let forceOrientation: (UIInterfaceOrientation) -> Void - public init(isMainApp: Bool, appBundleId: String, appBuildType: TelegramAppBuildType, containerPath: String, appSpecificScheme: String, openUrl: @escaping (String) -> Void, openUniversalUrl: @escaping (String, TelegramApplicationOpenUrlCompletion) -> Void, canOpenUrl: @escaping (String) -> Bool, getTopWindow: @escaping () -> UIWindow?, displayNotification: @escaping (String) -> Void, applicationInForeground: Signal, applicationIsActive: Signal, clearMessageNotifications: @escaping ([MessageId]) -> Void, pushIdleTimerExtension: @escaping () -> Disposable, openSettings: @escaping () -> Void, openAppStorePage: @escaping () -> Void, registerForNotifications: @escaping (@escaping (Bool) -> Void) -> Void, requestSiriAuthorization: @escaping (@escaping (Bool) -> Void) -> Void, siriAuthorization: @escaping () -> AccessType, getWindowHost: @escaping () -> WindowHost?, presentNativeController: @escaping (UIViewController) -> Void, dismissNativeController: @escaping () -> Void, getAvailableAlternateIcons: @escaping () -> [PresentationAppIcon], getAlternateIconName: @escaping () -> String?, requestSetAlternateIconName: @escaping (String?, @escaping (Bool) -> Void) -> Void, forceOrientation: @escaping (UIInterfaceOrientation) -> Void) { + public init(isMainApp: Bool, appBundleId: String, appBuildType: TelegramAppBuildType, containerPath: String, appSpecificScheme: String, openUrl: @escaping (String) -> Void, openUniversalUrl: @escaping (String, TelegramApplicationOpenUrlCompletion) -> Void, canOpenUrl: @escaping (String) -> Bool, getTopWindow: @escaping () -> UIWindow?, displayNotification: @escaping (String) -> Void, applicationInForeground: Signal, applicationIsActive: Signal, clearMessageNotifications: @escaping ([MessageId]) -> Void, pushIdleTimerExtension: @escaping () -> Disposable, openSettings: @escaping () -> Void, openAppStorePage: @escaping () -> Void, openSubscriptions: @escaping () -> Void, registerForNotifications: @escaping (@escaping (Bool) -> Void) -> Void, requestSiriAuthorization: @escaping (@escaping (Bool) -> Void) -> Void, siriAuthorization: @escaping () -> AccessType, getWindowHost: @escaping () -> WindowHost?, presentNativeController: @escaping (UIViewController) -> Void, dismissNativeController: @escaping () -> Void, getAvailableAlternateIcons: @escaping () -> [PresentationAppIcon], getAlternateIconName: @escaping () -> String?, requestSetAlternateIconName: @escaping (String?, @escaping (Bool) -> Void) -> Void, forceOrientation: @escaping (UIInterfaceOrientation) -> Void) { self.isMainApp = isMainApp self.appBundleId = appBundleId self.appBuildType = appBuildType @@ -79,6 +80,7 @@ public final class TelegramApplicationBindings { self.pushIdleTimerExtension = pushIdleTimerExtension self.openSettings = openSettings self.openAppStorePage = openAppStorePage + self.openSubscriptions = openSubscriptions self.registerForNotifications = registerForNotifications self.requestSiriAuthorization = requestSiriAuthorization self.siriAuthorization = siriAuthorization @@ -885,3 +887,23 @@ public protocol AccountContext: AnyObject { func joinGroupCall(peerId: PeerId, invite: String?, requestJoinAsPeerId: ((@escaping (PeerId?) -> Void) -> Void)?, activeCall: EngineGroupCallDescription) func requestCall(peerId: PeerId, isVideo: Bool, completion: @escaping () -> Void) } + +public struct PremiumConfiguration { + public static var defaultValue: PremiumConfiguration { + return PremiumConfiguration(isPremiumDisabled: false) + } + + public let isPremiumDisabled: Bool + + fileprivate init(isPremiumDisabled: Bool) { + self.isPremiumDisabled = isPremiumDisabled + } + + public static func with(appConfiguration: AppConfiguration) -> PremiumConfiguration { + if let data = appConfiguration.data, let value = data["premium_purchase_blocked"] as? Bool { + return PremiumConfiguration(isPremiumDisabled: value) + } else { + return .defaultValue + } + } +} diff --git a/submodules/GalleryUI/Sources/ChatItemGalleryFooterContentNode.swift b/submodules/GalleryUI/Sources/ChatItemGalleryFooterContentNode.swift index 79d844ba08..2a85754ef1 100644 --- a/submodules/GalleryUI/Sources/ChatItemGalleryFooterContentNode.swift +++ b/submodules/GalleryUI/Sources/ChatItemGalleryFooterContentNode.swift @@ -1259,8 +1259,9 @@ final class ChatItemGalleryFooterContentNode: GalleryFooterContentNode, UIScroll case .generic: controllerInteraction?.presentController(UndoOverlayController(presentationData: presentationData, content: .universal(animation: "anim_gif", scale: 0.075, colors: [:], title: nil, text: presentationData.strings.Gallery_GifSaved), elevatedLayout: true, animateInAsReplacement: false, action: { _ in return false }), nil) case let .limitExceeded(limit, premiumLimit): + let premiumConfiguration = PremiumConfiguration.with(appConfiguration: context.currentAppConfiguration.with { $0 }) let text: String - if limit == premiumLimit { + if limit == premiumLimit || premiumConfiguration.isPremiumDisabled { text = presentationData.strings.Premium_MaxSavedGifsFinalText } else { text = presentationData.strings.Premium_MaxSavedGifsText("\(premiumLimit)").string @@ -1453,8 +1454,9 @@ final class ChatItemGalleryFooterContentNode: GalleryFooterContentNode, UIScroll case .generic: controllerInteraction?.presentController(UndoOverlayController(presentationData: presentationData, content: .universal(animation: "anim_gif", scale: 0.075, colors: [:], title: nil, text: presentationData.strings.Gallery_GifSaved), elevatedLayout: true, animateInAsReplacement: false, action: { _ in return false }), nil) case let .limitExceeded(limit, premiumLimit): + let premiumConfiguration = PremiumConfiguration.with(appConfiguration: context.currentAppConfiguration.with { $0 }) let text: String - if limit == premiumLimit { + if limit == premiumLimit || premiumConfiguration.isPremiumDisabled { text = presentationData.strings.Premium_MaxSavedGifsFinalText } else { text = presentationData.strings.Premium_MaxSavedGifsText("\(premiumLimit)").string diff --git a/submodules/PremiumUI/Sources/AppIconsDemoComponent.swift b/submodules/PremiumUI/Sources/AppIconsDemoComponent.swift index 75eb32f78f..1892884991 100644 --- a/submodules/PremiumUI/Sources/AppIconsDemoComponent.swift +++ b/submodules/PremiumUI/Sources/AppIconsDemoComponent.swift @@ -64,7 +64,18 @@ final class AppIconsDemoComponent: Component { if self.imageViews.isEmpty { for icon in component.appIcons { - if let image = UIImage(named: icon.imageName, in: getAppBundle(), compatibleWith: nil) { + let image: UIImage? + switch icon.imageName { + case "Premium": + image = UIImage(bundleImageName: "Premium/Icons/Premium") + case "PremiumBlack": + image = UIImage(bundleImageName: "Premium/Icons/Black") + case "PremiumTurbo": + image = UIImage(bundleImageName: "Premium/Icons/Turbo") + default: + image = nil + } + if let image = image { let imageView = UIImageView(frame: CGRect(origin: .zero, size: CGSize(width: 90.0, height: 90.0))) imageView.clipsToBounds = true imageView.layer.cornerRadius = 24.0 diff --git a/submodules/SettingsUI/Sources/Themes/ThemeSettingsController.swift b/submodules/SettingsUI/Sources/Themes/ThemeSettingsController.swift index ee06a4a8e9..38403b25a6 100644 --- a/submodules/SettingsUI/Sources/Themes/ThemeSettingsController.swift +++ b/submodules/SettingsUI/Sources/Themes/ThemeSettingsController.swift @@ -416,13 +416,18 @@ public func themeSettingsController(context: AccountContext, focusOnItemTag: The let _ = telegramWallpapers(postbox: context.account.postbox, network: context.account.network).start() let currentAppIcon: PresentationAppIcon? - let appIcons = context.sharedContext.applicationBindings.getAvailableAlternateIcons() + var appIcons = context.sharedContext.applicationBindings.getAvailableAlternateIcons() if let alternateIconName = context.sharedContext.applicationBindings.getAlternateIconName() { currentAppIcon = appIcons.filter { $0.name == alternateIconName }.first } else { currentAppIcon = appIcons.filter { $0.isDefault }.first } + let premiumConfiguration = PremiumConfiguration.with(appConfiguration: context.currentAppConfiguration.with { $0 }) + if premiumConfiguration.isPremiumDisabled { + appIcons = appIcons.filter { !$0.isPremium } + } + let availableAppIcons: Signal<[PresentationAppIcon], NoError> = .single(appIcons) let currentAppIconName = ValuePromise() currentAppIconName.set(currentAppIcon?.name ?? "Blue") diff --git a/submodules/StickerPackPreviewUI/Sources/StickerPackScreen.swift b/submodules/StickerPackPreviewUI/Sources/StickerPackScreen.swift index 443f256dda..04b7b114c5 100644 --- a/submodules/StickerPackPreviewUI/Sources/StickerPackScreen.swift +++ b/submodules/StickerPackPreviewUI/Sources/StickerPackScreen.swift @@ -640,31 +640,6 @@ private final class StickerPackContainer: ASDisplayNode { self.currentStickerPack = (info, items, installed) - if installed { - let text: String - if info.id.namespace == Namespaces.ItemCollection.CloudStickerPacks { - text = self.presentationData.strings.StickerPack_RemoveStickerCount(info.count) - } else { - text = self.presentationData.strings.StickerPack_RemoveMaskCount(info.count) - } - self.buttonNode.setTitle(text, with: Font.regular(17.0), with: self.presentationData.theme.list.itemDestructiveColor, for: .normal) - self.buttonNode.setBackgroundImage(nil, for: []) - } else { - let text: String - if info.id.namespace == Namespaces.ItemCollection.CloudStickerPacks { - text = self.presentationData.strings.StickerPack_AddStickerCount(info.count) - } else { - text = self.presentationData.strings.StickerPack_AddMaskCount(info.count) - } - self.buttonNode.setTitle(text, with: Font.semibold(17.0), with: self.presentationData.theme.list.itemCheckColors.foregroundColor, for: .normal) - let roundedAccentBackground = generateImage(CGSize(width: 22.0, height: 22.0), rotatedContext: { size, context in - context.clear(CGRect(origin: CGPoint(), size: size)) - context.setFillColor(self.presentationData.theme.list.itemCheckColors.fillColor.cgColor) - context.fillEllipse(in: CGRect(origin: CGPoint(), size: CGSize(width: size.width, height: size.height))) - })?.stretchableImage(withLeftCapWidth: 11, topCapHeight: 11) - self.buttonNode.setBackgroundImage(roundedAccentBackground, for: []) - } - if self.titleNode.attributedText == nil { if let titlePlaceholderNode = self.titlePlaceholderNode { self.titlePlaceholderNode = nil @@ -678,6 +653,9 @@ private final class StickerPackContainer: ASDisplayNode { updateLayout = true + + let premiumConfiguration = PremiumConfiguration.with(appConfiguration: self.context.currentAppConfiguration.with { $0 }) + var generalItems: [StickerPackItem] = [] var premiumItems: [StickerPackItem] = [] @@ -711,11 +689,39 @@ private final class StickerPackContainer: ASDisplayNode { addItem(item, false, false) } - if !premiumItems.isEmpty { - for item in premiumItems { - addItem(item, true, !hasPremium) + if !premiumConfiguration.isPremiumDisabled { + if !premiumItems.isEmpty { + for item in premiumItems { + addItem(item, true, !hasPremium) + } } } + + if installed { + let text: String + if info.id.namespace == Namespaces.ItemCollection.CloudStickerPacks { + text = self.presentationData.strings.StickerPack_RemoveStickerCount(Int32(entries.count)) + } else { + text = self.presentationData.strings.StickerPack_RemoveMaskCount(Int32(entries.count)) + } + self.buttonNode.setTitle(text, with: Font.regular(17.0), with: self.presentationData.theme.list.itemDestructiveColor, for: .normal) + self.buttonNode.setBackgroundImage(nil, for: []) + } else { + let text: String + if info.id.namespace == Namespaces.ItemCollection.CloudStickerPacks { + text = self.presentationData.strings.StickerPack_AddStickerCount(Int32(entries.count)) + } else { + text = self.presentationData.strings.StickerPack_AddMaskCount(Int32(entries.count)) + } + self.buttonNode.setTitle(text, with: Font.semibold(17.0), with: self.presentationData.theme.list.itemCheckColors.foregroundColor, for: .normal) + let roundedAccentBackground = generateImage(CGSize(width: 22.0, height: 22.0), rotatedContext: { size, context in + context.clear(CGRect(origin: CGPoint(), size: size)) + context.setFillColor(self.presentationData.theme.list.itemCheckColors.fillColor.cgColor) + context.fillEllipse(in: CGRect(origin: CGPoint(), size: CGSize(width: size.width, height: size.height))) + })?.stretchableImage(withLeftCapWidth: 11, topCapHeight: 11) + self.buttonNode.setBackgroundImage(roundedAccentBackground, for: []) + } + } let previousEntries = self.currentEntries self.currentEntries = entries diff --git a/submodules/StickerPackPreviewUI/Sources/StickerPreviewPeekContent.swift b/submodules/StickerPackPreviewUI/Sources/StickerPreviewPeekContent.swift index 855859bbe1..e81333433e 100644 --- a/submodules/StickerPackPreviewUI/Sources/StickerPreviewPeekContent.swift +++ b/submodules/StickerPackPreviewUI/Sources/StickerPreviewPeekContent.swift @@ -209,7 +209,7 @@ public final class StickerPreviewPeekContentNode: ASDisplayNode, PeekControllerC animationNode.updateLayout(size: imageSize) if let additionalAnimationNode = self.additionalAnimationNode { - additionalAnimationNode.frame = imageFrame.offsetBy(dx: -imageFrame.width * 0.245 + 21, dy: -1.0).insetBy(dx: -imageFrame.width * 0.245, dy: -imageFrame.height * 0.245) + additionalAnimationNode.frame = imageFrame.offsetBy(dx: -imageFrame.width * 0.245 + 21.0, dy: -1.0).insetBy(dx: -imageFrame.width * 0.245, dy: -imageFrame.height * 0.245) additionalAnimationNode.updateLayout(size: additionalAnimationNode.frame.size) } } diff --git a/submodules/TelegramUI/Images.xcassets/Premium/Icons/Black.imageset/Contents.json b/submodules/TelegramUI/Images.xcassets/Premium/Icons/Black.imageset/Contents.json new file mode 100644 index 0000000000..0e16260166 --- /dev/null +++ b/submodules/TelegramUI/Images.xcassets/Premium/Icons/Black.imageset/Contents.json @@ -0,0 +1,21 @@ +{ + "images" : [ + { + "idiom" : "universal", + "scale" : "1x" + }, + { + "idiom" : "universal", + "scale" : "2x" + }, + { + "filename" : "black@270x270.png", + "idiom" : "universal", + "scale" : "3x" + } + ], + "info" : { + "author" : "xcode", + "version" : 1 + } +} diff --git a/submodules/TelegramUI/Images.xcassets/Premium/Icons/Black.imageset/black@270x270.png b/submodules/TelegramUI/Images.xcassets/Premium/Icons/Black.imageset/black@270x270.png new file mode 100644 index 0000000000000000000000000000000000000000..93c4d590cee41c59d4984ff42bc5098b240aafa1 GIT binary patch literal 35809 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 P00000NkvXXu0mjfP#yN4 literal 0 HcmV?d00001 diff --git a/submodules/TelegramUI/Images.xcassets/Premium/Icons/Contents.json b/submodules/TelegramUI/Images.xcassets/Premium/Icons/Contents.json new file mode 100644 index 0000000000..6e965652df --- /dev/null +++ b/submodules/TelegramUI/Images.xcassets/Premium/Icons/Contents.json @@ -0,0 +1,9 @@ +{ + "info" : { + "author" : "xcode", + "version" : 1 + }, + "properties" : { + "provides-namespace" : true + } +} diff --git a/submodules/TelegramUI/Images.xcassets/Premium/Icons/Premium.imageset/Contents.json b/submodules/TelegramUI/Images.xcassets/Premium/Icons/Premium.imageset/Contents.json new file mode 100644 index 0000000000..687a0cb1b4 --- /dev/null +++ b/submodules/TelegramUI/Images.xcassets/Premium/Icons/Premium.imageset/Contents.json @@ -0,0 +1,21 @@ +{ + "images" : [ + { + "idiom" : "universal", + "scale" : "1x" + }, + { + "idiom" : "universal", + "scale" : "2x" + }, + { + "filename" : "premium@270x270.png", + "idiom" : "universal", + "scale" : "3x" + } + ], + "info" : { + "author" : "xcode", + "version" : 1 + } +} diff --git a/submodules/TelegramUI/Images.xcassets/Premium/Icons/Premium.imageset/premium@270x270.png b/submodules/TelegramUI/Images.xcassets/Premium/Icons/Premium.imageset/premium@270x270.png new file mode 100644 index 0000000000000000000000000000000000000000..e7b6b683ed95fffad2d9bb6d077545264e6910c5 GIT binary patch literal 18470 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%0DfEmqwFu{)b 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 literal 0 HcmV?d00001 diff --git a/submodules/TelegramUI/Sources/AppDelegate.swift b/submodules/TelegramUI/Sources/AppDelegate.swift index 87c29fc9c5..4d6e67446b 100644 --- a/submodules/TelegramUI/Sources/AppDelegate.swift +++ b/submodules/TelegramUI/Sources/AppDelegate.swift @@ -33,6 +33,7 @@ import TelegramAudio import DebugSettingsUI import BackgroundTasks import UIKitRuntimeUtils +import StoreKit #if canImport(AppCenter) import AppCenter @@ -622,6 +623,14 @@ private func extractAccountManagerState(records: AccountRecordsView take(1) diff --git a/submodules/TelegramUI/Sources/ChatController.swift b/submodules/TelegramUI/Sources/ChatController.swift index 06871648d3..d6290331ff 100644 --- a/submodules/TelegramUI/Sources/ChatController.swift +++ b/submodules/TelegramUI/Sources/ChatController.swift @@ -1029,7 +1029,9 @@ public final class ChatControllerImpl: TelegramBaseController, ChatController, G } actions.context = strongSelf.context - + + let premiumConfiguration = PremiumConfiguration.with(appConfiguration: strongSelf.context.currentAppConfiguration.with { $0 }) + if canAddMessageReactions(message: topMessage), let availableReactions = availableReactions, let allowedReactions = allowedReactions { var hasPremiumPlaceholder = false filterReactions: for reaction in availableReactions.reactions { @@ -1067,7 +1069,8 @@ public final class ChatControllerImpl: TelegramBaseController, ChatController, G largeApplicationAnimation: reaction.effectAnimation ))) } - if hasPremiumPlaceholder { + + if hasPremiumPlaceholder && !premiumConfiguration.isPremiumDisabled { actions.reactionItems.append(.premium) } } @@ -7973,8 +7976,9 @@ public final class ChatControllerImpl: TelegramBaseController, ChatController, G case .generic: strongSelf.presentInGlobalOverlay(UndoOverlayController(presentationData: strongSelf.presentationData, content: .sticker(context: strongSelf.context, file: stickerFile, title: nil, text: added ? strongSelf.presentationData.strings.Conversation_StickerAddedToFavorites : strongSelf.presentationData.strings.Conversation_StickerRemovedFromFavorites, undoText: nil), elevatedLayout: true, action: { _ in return false }), with: nil) case let .limitExceeded(limit, premiumLimit): + let premiumConfiguration = PremiumConfiguration.with(appConfiguration: context.currentAppConfiguration.with { $0 }) let text: String - if limit == premiumLimit { + if limit == premiumLimit || premiumConfiguration.isPremiumDisabled { text = strongSelf.presentationData.strings.Premium_MaxFavedStickersFinalText } else { text = strongSelf.presentationData.strings.Premium_MaxFavedStickersText("\(premiumLimit)").string @@ -12391,6 +12395,11 @@ public final class ChatControllerImpl: TelegramBaseController, ChatController, G } private func displayPremiumStickerTooltip(file: TelegramMediaFile, message: Message) { + let premiumConfiguration = PremiumConfiguration.with(appConfiguration: self.context.currentAppConfiguration.with { $0 }) + guard !premiumConfiguration.isPremiumDisabled else { + return + } + var currentOverlayController: UndoOverlayController? self.window?.forEachController({ controller in diff --git a/submodules/TelegramUI/Sources/ChatInterfaceStateContextMenus.swift b/submodules/TelegramUI/Sources/ChatInterfaceStateContextMenus.swift index 471987ee10..179dbe35d0 100644 --- a/submodules/TelegramUI/Sources/ChatInterfaceStateContextMenus.swift +++ b/submodules/TelegramUI/Sources/ChatInterfaceStateContextMenus.swift @@ -394,12 +394,21 @@ func contextMenuForChatPresentationInterfaceState(chatPresentationInterfaceState }) }))) - if !chatPresentationInterfaceState.isPremium { + let premiumConfiguration = PremiumConfiguration.with(appConfiguration: context.currentAppConfiguration.with { $0 }) + if !chatPresentationInterfaceState.isPremium && !premiumConfiguration.isPremiumDisabled { actions.append(.action(ContextMenuActionItem(text: presentationData.strings.SponsoredMessageMenu_Hide, textColor: .primary, textLayout: .twoLinesMax, textFont: .custom(Font.regular(presentationData.listsFontSize.baseDisplaySize - 1.0)), badge: nil, icon: { theme in return generateTintedImage(image: UIImage(bundleImageName: "Chat/Context Menu/Restrict"), color: theme.actionSheet.primaryTextColor) }, iconSource: nil, action: { c, _ in c.dismiss(completion: { - controllerInteraction.navigationController()?.pushViewController(PremiumIntroScreen(context: context, source: .ads)) + var replaceImpl: ((ViewController) -> Void)? + let controller = PremiumDemoScreen(context: context, subject: .noAds, action: { + let controller = PremiumIntroScreen(context: context, source: .ads) + replaceImpl?(controller) + }) + replaceImpl = { [weak controller] c in + controller?.replace(with: c) + } + controllerInteraction.navigationController()?.pushViewController(controller) }) }))) } @@ -1225,8 +1234,9 @@ func contextMenuForChatPresentationInterfaceState(chatPresentationInterfaceState case .generic: controllerInteraction.presentControllerInCurrent(UndoOverlayController(presentationData: presentationData, content: .universal(animation: "anim_gif", scale: 0.075, colors: [:], title: nil, text: presentationData.strings.Gallery_GifSaved), elevatedLayout: false, animateInAsReplacement: false, action: { _ in return false }), nil) case let .limitExceeded(limit, premiumLimit): + let premiumConfiguration = PremiumConfiguration.with(appConfiguration: context.currentAppConfiguration.with { $0 }) let text: String - if limit == premiumLimit { + if limit == premiumLimit || premiumConfiguration.isPremiumDisabled { text = presentationData.strings.Premium_MaxSavedGifsFinalText } else { text = presentationData.strings.Premium_MaxSavedGifsText("\(premiumLimit)").string diff --git a/submodules/TelegramUI/Sources/ChatMediaInputNode.swift b/submodules/TelegramUI/Sources/ChatMediaInputNode.swift index a600739cf9..147a4da640 100644 --- a/submodules/TelegramUI/Sources/ChatMediaInputNode.swift +++ b/submodules/TelegramUI/Sources/ChatMediaInputNode.swift @@ -261,7 +261,7 @@ func chatMediaInputPanelGifModeEntries(theme: PresentationTheme, strings: Presen return entries } -func chatMediaInputGridEntries(view: ItemCollectionsView, savedStickers: OrderedItemListView?, recentStickers: OrderedItemListView?, peerSpecificPack: PeerSpecificPackData?, canInstallPeerSpecificPack: CanInstallPeerSpecificPack, trendingPacks: [FeaturedStickerPackItem], installedPacks: Set, premiumStickers: OrderedItemListView? = nil, trendingIsDismissed: Bool = false, hasSearch: Bool = true, hasAccessories: Bool = true, strings: PresentationStrings, theme: PresentationTheme, hasPremium: Bool) -> [ChatMediaInputGridEntry] { +func chatMediaInputGridEntries(view: ItemCollectionsView, savedStickers: OrderedItemListView?, recentStickers: OrderedItemListView?, peerSpecificPack: PeerSpecificPackData?, canInstallPeerSpecificPack: CanInstallPeerSpecificPack, trendingPacks: [FeaturedStickerPackItem], installedPacks: Set, premiumStickers: OrderedItemListView? = nil, trendingIsDismissed: Bool = false, hasSearch: Bool = true, hasAccessories: Bool = true, strings: PresentationStrings, theme: PresentationTheme, hasPremium: Bool, isPremiumDisabled: Bool) -> [ChatMediaInputGridEntry] { var entries: [ChatMediaInputGridEntry] = [] if hasSearch && view.lower == nil { @@ -284,7 +284,11 @@ func chatMediaInputGridEntries(view: ItemCollectionsView, savedStickers: Ordered savedStickerIds.insert(item.file.fileId.id) let index = ItemCollectionItemIndex(index: Int32(i), id: item.file.fileId.id) let stickerItem = StickerPackItem(index: index, file: item.file, indexKeys: []) - entries.append(.sticker(index: ItemCollectionViewEntryIndex(collectionIndex: -3, collectionId: packInfo.id, itemIndex: index), stickerItem: stickerItem, stickerPackInfo: packInfo, canManagePeerSpecificPack: nil, maybeManageable: hasAccessories, theme: theme, isLocked: stickerItem.file.isPremiumSticker && !hasPremium)) + if isPremiumDisabled && item.file.isPremiumSticker { + + } else { + entries.append(.sticker(index: ItemCollectionViewEntryIndex(collectionIndex: -3, collectionId: packInfo.id, itemIndex: index), stickerItem: stickerItem, stickerPackInfo: packInfo, canManagePeerSpecificPack: nil, maybeManageable: hasAccessories, theme: theme, isLocked: stickerItem.file.isPremiumSticker && !hasPremium)) + } } } } @@ -307,8 +311,13 @@ func chatMediaInputGridEntries(view: ItemCollectionsView, savedStickers: Ordered if !savedStickerIds.contains(mediaId.id) { let index = ItemCollectionItemIndex(index: Int32(i), id: mediaId.id) let stickerItem = StickerPackItem(index: index, file: file, indexKeys: []) - entries.append(.sticker(index: ItemCollectionViewEntryIndex(collectionIndex: -2, collectionId: packInfo.id, itemIndex: index), stickerItem: stickerItem, stickerPackInfo: packInfo, canManagePeerSpecificPack: nil, maybeManageable: hasAccessories, theme: theme, isLocked: stickerItem.file.isPremiumSticker && !hasPremium)) - addedCount += 1 + + if isPremiumDisabled && file.isPremiumSticker { + + } else { + entries.append(.sticker(index: ItemCollectionViewEntryIndex(collectionIndex: -2, collectionId: packInfo.id, itemIndex: index), stickerItem: stickerItem, stickerPackInfo: packInfo, canManagePeerSpecificPack: nil, maybeManageable: hasAccessories, theme: theme, isLocked: stickerItem.file.isPremiumSticker && !hasPremium)) + addedCount += 1 + } } } } @@ -330,12 +339,16 @@ func chatMediaInputGridEntries(view: ItemCollectionsView, savedStickers: Ordered if let item = peerSpecificPack.items[i] as? StickerPackItem { let index = ItemCollectionItemIndex(index: Int32(i), id: item.file.fileId.id) let stickerItem = StickerPackItem(index: index, file: item.file, indexKeys: []) - entries.append(.sticker(index: ItemCollectionViewEntryIndex(collectionIndex: -1, collectionId: packInfo.id, itemIndex: index), stickerItem: stickerItem, stickerPackInfo: packInfo, canManagePeerSpecificPack: canManagePeerSpecificPack, maybeManageable: hasAccessories, theme: theme, isLocked: stickerItem.file.isPremiumSticker && !hasPremium)) + if isPremiumDisabled && item.file.isPremiumSticker { + + } else { + entries.append(.sticker(index: ItemCollectionViewEntryIndex(collectionIndex: -1, collectionId: packInfo.id, itemIndex: index), stickerItem: stickerItem, stickerPackInfo: packInfo, canManagePeerSpecificPack: canManagePeerSpecificPack, maybeManageable: hasAccessories, theme: theme, isLocked: stickerItem.file.isPremiumSticker && !hasPremium)) + } } } } - if let premiumStickers = premiumStickers, !premiumStickers.items.isEmpty && hasPremium { + if let premiumStickers = premiumStickers, !premiumStickers.items.isEmpty && hasPremium && !isPremiumDisabled { let packInfo = StickerPackCollectionInfo(id: ItemCollectionId(namespace: ChatMediaInputPanelAuxiliaryNamespace.premium.rawValue, id: 0), flags: [], accessHash: 0, title: strings.Stickers_PremiumStickers.uppercased(), shortName: "", thumbnail: nil, immediateThumbnailData: nil, hash: 0, count: 0) for i in 0 ..< premiumStickers.items.count { if let item = premiumStickers.items[i].contents.get(RecentMediaItem.self) { @@ -351,7 +364,11 @@ func chatMediaInputGridEntries(view: ItemCollectionsView, savedStickers: Ordered for entry in view.entries { if let item = entry.item as? StickerPackItem { - entries.append(.sticker(index: entry.index, stickerItem: item, stickerPackInfo: stickerPackInfos[entry.index.collectionId], canManagePeerSpecificPack: false, maybeManageable: hasAccessories, theme: theme, isLocked: item.file.isPremiumSticker && !hasPremium)) + if isPremiumDisabled && item.file.isPremiumSticker { + + } else { + entries.append(.sticker(index: entry.index, stickerItem: item, stickerPackInfo: stickerPackInfos[entry.index.collectionId], canManagePeerSpecificPack: false, maybeManageable: hasAccessories, theme: theme, isLocked: item.file.isPremiumSticker && !hasPremium)) + } } } @@ -1102,6 +1119,8 @@ final class ChatMediaInputNode: ChatInputNode { return animatedEmojiStickers } + let premiumConfiguration = PremiumConfiguration.with(appConfiguration: context.currentAppConfiguration.with { $0 }) + let previousView = Atomic(value: nil) let transitionQueue = Queue() let transitions = combineLatest(queue: transitionQueue, itemCollectionsView, peerSpecificPack, context.account.viewTracker.featuredStickerPacks(), self.themeAndStringsPromise.get(), reactions, self.panelIsFocusedPromise.get(), ApplicationSpecificNotice.dismissedTrendingStickerPacks(accountManager: context.sharedContext.accountManager), temporaryPackOrder.get(), animatedEmojiStickers, context.account.postbox.peerView(id: context.account.peerId)) @@ -1142,7 +1161,7 @@ final class ChatMediaInputNode: ChatInputNode { let panelEntries = chatMediaInputPanelEntries(view: view, savedStickers: savedStickers, recentStickers: recentStickers, temporaryPackOrder: temporaryPackOrder, trendingIsDismissed: trendingIsDismissed, peerSpecificPack: peerSpecificPack.0, canInstallPeerSpecificPack: peerSpecificPack.1, theme: theme, strings: strings, premiumStickers: hasPremium ? premiumStickers : nil, expanded: panelExpanded, reorderable: true) let gifPaneEntries = chatMediaInputPanelGifModeEntries(theme: theme, strings: strings, reactions: reactions, animatedEmojiStickers: animatedEmojiStickers, expanded: panelExpanded) - var gridEntries = chatMediaInputGridEntries(view: view, savedStickers: savedStickers, recentStickers: recentStickers, peerSpecificPack: peerSpecificPack.0, canInstallPeerSpecificPack: peerSpecificPack.1, trendingPacks: trendingPacks, installedPacks: installedPacks, premiumStickers: premiumStickers, trendingIsDismissed: trendingIsDismissed, strings: strings, theme: theme, hasPremium: hasPremium) + var gridEntries = chatMediaInputGridEntries(view: view, savedStickers: savedStickers, recentStickers: recentStickers, peerSpecificPack: peerSpecificPack.0, canInstallPeerSpecificPack: peerSpecificPack.1, trendingPacks: trendingPacks, installedPacks: installedPacks, premiumStickers: premiumStickers, trendingIsDismissed: trendingIsDismissed, strings: strings, theme: theme, hasPremium: hasPremium, isPremiumDisabled: premiumConfiguration.isPremiumDisabled) if view.higher == nil { var hasTopSeparator = true @@ -1471,8 +1490,9 @@ final class ChatMediaInputNode: ChatInputNode { case .generic: controllerInteraction.presentController(UndoOverlayController(presentationData: presentationData, content: .universal(animation: "anim_gif", scale: 0.075, colors: [:], title: nil, text: presentationData.strings.Gallery_GifSaved), elevatedLayout: false, animateInAsReplacement: false, action: { _ in return false }), nil) case let .limitExceeded(limit, premiumLimit): + let premiumConfiguration = PremiumConfiguration.with(appConfiguration: context.currentAppConfiguration.with { $0 }) let text: String - if limit == premiumLimit { + if limit == premiumLimit || premiumConfiguration.isPremiumDisabled { text = presentationData.strings.Premium_MaxSavedGifsFinalText } else { text = presentationData.strings.Premium_MaxSavedGifsText("\(premiumLimit)").string @@ -1578,8 +1598,9 @@ final class ChatMediaInputNode: ChatInputNode { case .generic: strongSelf.controllerInteraction.presentGlobalOverlayController(UndoOverlayController(presentationData: presentationData, content: .sticker(context: strongSelf.context, file: item.file, title: nil, text: !isStarred ? strongSelf.strings.Conversation_StickerAddedToFavorites : strongSelf.strings.Conversation_StickerRemovedFromFavorites, undoText: nil), elevatedLayout: false, action: { _ in return false }), nil) case let .limitExceeded(limit, premiumLimit): + let premiumConfiguration = PremiumConfiguration.with(appConfiguration: strongSelf.context.currentAppConfiguration.with { $0 }) let text: String - if limit == premiumLimit { + if limit == premiumLimit || premiumConfiguration.isPremiumDisabled { text = strongSelf.strings.Premium_MaxFavedStickersFinalText } else { text = strongSelf.strings.Premium_MaxFavedStickersText("\(premiumLimit)").string @@ -1730,8 +1751,9 @@ final class ChatMediaInputNode: ChatInputNode { case .generic: strongSelf.controllerInteraction.presentGlobalOverlayController(UndoOverlayController(presentationData: presentationData, content: .sticker(context: strongSelf.context, file: item.file, title: nil, text: !isStarred ? strongSelf.strings.Conversation_StickerAddedToFavorites : strongSelf.strings.Conversation_StickerRemovedFromFavorites, undoText: nil), elevatedLayout: false, action: { _ in return false }), nil) case let .limitExceeded(limit, premiumLimit): + let premiumConfiguration = PremiumConfiguration.with(appConfiguration: strongSelf.context.currentAppConfiguration.with { $0 }) let text: String - if limit == premiumLimit { + if limit == premiumLimit || premiumConfiguration.isPremiumDisabled { text = strongSelf.strings.Premium_MaxFavedStickersFinalText } else { text = strongSelf.strings.Premium_MaxFavedStickersText("\(premiumLimit)").string diff --git a/submodules/TelegramUI/Sources/DrawingStickersScreen.swift b/submodules/TelegramUI/Sources/DrawingStickersScreen.swift index cc0a0dbc80..be902da0ea 100644 --- a/submodules/TelegramUI/Sources/DrawingStickersScreen.swift +++ b/submodules/TelegramUI/Sources/DrawingStickersScreen.swift @@ -594,7 +594,7 @@ private final class DrawingStickersScreenNode: ViewControllerTracingNode { } let panelEntries = chatMediaInputPanelEntries(view: view, savedStickers: savedStickers, recentStickers: recentStickers, peerSpecificPack: nil, canInstallPeerSpecificPack: .none, theme: theme, strings: strings, hasGifs: false, hasSettings: false) - let gridEntries = chatMediaInputGridEntries(view: view, savedStickers: savedStickers, recentStickers: recentStickers, peerSpecificPack: nil, canInstallPeerSpecificPack: .none, trendingPacks: [], installedPacks: installedPacks, hasSearch: false, hasAccessories: false, strings: strings, theme: theme, hasPremium: false) + let gridEntries = chatMediaInputGridEntries(view: view, savedStickers: savedStickers, recentStickers: recentStickers, peerSpecificPack: nil, canInstallPeerSpecificPack: .none, trendingPacks: [], installedPacks: installedPacks, hasSearch: false, hasAccessories: false, strings: strings, theme: theme, hasPremium: false, isPremiumDisabled: true) let (previousPanelEntries, previousGridEntries) = previousStickerEntries.swap((panelEntries, gridEntries)) return (view, preparedChatMediaInputPanelEntryTransition(context: context, from: previousPanelEntries, to: panelEntries, inputNodeInteraction: stickersInputNodeInteraction, scrollToItem: nil), previousPanelEntries.isEmpty, preparedChatMediaInputGridEntryTransition(account: context.account, view: view, from: previousGridEntries, to: gridEntries, update: update, interfaceInteraction: controllerInteraction, inputNodeInteraction: stickersInputNodeInteraction, trendingInteraction: trendingInteraction), previousGridEntries.isEmpty) @@ -629,7 +629,7 @@ private final class DrawingStickersScreenNode: ViewControllerTracingNode { } let panelEntries = chatMediaInputPanelEntries(view: view, savedStickers: nil, recentStickers: nil, peerSpecificPack: nil, canInstallPeerSpecificPack: .none, theme: theme, strings: strings, hasGifs: false, hasSettings: false) - let gridEntries = chatMediaInputGridEntries(view: view, savedStickers: nil, recentStickers: nil, peerSpecificPack: nil, canInstallPeerSpecificPack: .none, trendingPacks: [], installedPacks: installedPacks, hasSearch: false, hasAccessories: false, strings: strings, theme: theme, hasPremium: false) + let gridEntries = chatMediaInputGridEntries(view: view, savedStickers: nil, recentStickers: nil, peerSpecificPack: nil, canInstallPeerSpecificPack: .none, trendingPacks: [], installedPacks: installedPacks, hasSearch: false, hasAccessories: false, strings: strings, theme: theme, hasPremium: false, isPremiumDisabled: true) let (previousPanelEntries, previousGridEntries) = previousMaskEntries.swap((panelEntries, gridEntries)) return (view, preparedChatMediaInputPanelEntryTransition(context: context, from: previousPanelEntries, to: panelEntries, inputNodeInteraction: masksInputNodeInteraction, scrollToItem: nil), previousPanelEntries.isEmpty, preparedChatMediaInputGridEntryTransition(account: context.account, view: view, from: previousGridEntries, to: gridEntries, update: update, interfaceInteraction: controllerInteraction, inputNodeInteraction: masksInputNodeInteraction, trendingInteraction: trendingInteraction), previousGridEntries.isEmpty) diff --git a/submodules/TelegramUI/Sources/FeaturedStickersScreen.swift b/submodules/TelegramUI/Sources/FeaturedStickersScreen.swift index acd504c0df..39986aa3ae 100644 --- a/submodules/TelegramUI/Sources/FeaturedStickersScreen.swift +++ b/submodules/TelegramUI/Sources/FeaturedStickersScreen.swift @@ -502,8 +502,9 @@ private final class FeaturedStickersScreenNode: ViewControllerTracingNode { case .generic: strongSelf.controller?.presentInGlobalOverlay(UndoOverlayController(presentationData: strongSelf.presentationData, content: .sticker(context: strongSelf.context, file: item.file, title: nil, text: !isStarred ? strongSelf.presentationData.strings.Conversation_StickerAddedToFavorites : strongSelf.presentationData.strings.Conversation_StickerRemovedFromFavorites, undoText: nil), elevatedLayout: false, action: { _ in return false }), with: nil) case let .limitExceeded(limit, premiumLimit): + let premiumConfiguration = PremiumConfiguration.with(appConfiguration: strongSelf.context.currentAppConfiguration.with { $0 }) let text: String - if limit == premiumLimit { + if limit == premiumLimit || premiumConfiguration.isPremiumDisabled { text = strongSelf.presentationData.strings.Premium_MaxFavedStickersFinalText } else { text = strongSelf.presentationData.strings.Premium_MaxFavedStickersText("\(premiumLimit)").string @@ -589,8 +590,9 @@ private final class FeaturedStickersScreenNode: ViewControllerTracingNode { case .generic: strongSelf.controller?.presentInGlobalOverlay(UndoOverlayController(presentationData: strongSelf.presentationData, content: .sticker(context: strongSelf.context, file: item.file, title: nil, text: !isStarred ? strongSelf.presentationData.strings.Conversation_StickerAddedToFavorites : strongSelf.presentationData.strings.Conversation_StickerRemovedFromFavorites, undoText: nil), elevatedLayout: false, action: { _ in return false }), with: nil) case let .limitExceeded(limit, premiumLimit): + let premiumConfiguration = PremiumConfiguration.with(appConfiguration: strongSelf.context.currentAppConfiguration.with { $0 }) let text: String - if limit == premiumLimit { + if limit == premiumLimit || premiumConfiguration.isPremiumDisabled { text = strongSelf.presentationData.strings.Premium_MaxFavedStickersFinalText } else { text = strongSelf.presentationData.strings.Premium_MaxFavedStickersText("\(premiumLimit)").string diff --git a/submodules/TelegramUI/Sources/HorizontalListContextResultsChatInputContextPanelNode.swift b/submodules/TelegramUI/Sources/HorizontalListContextResultsChatInputContextPanelNode.swift index f66fd69d11..23a4da53d9 100644 --- a/submodules/TelegramUI/Sources/HorizontalListContextResultsChatInputContextPanelNode.swift +++ b/submodules/TelegramUI/Sources/HorizontalListContextResultsChatInputContextPanelNode.swift @@ -203,8 +203,9 @@ final class HorizontalListContextResultsChatInputContextPanelNode: ChatInputCont case .generic: interfaceInteraction?.presentController(UndoOverlayController(presentationData: presentationData, content: .universal(animation: "anim_gif", scale: 0.075, colors: [:], title: nil, text: presentationData.strings.Gallery_GifSaved), elevatedLayout: false, animateInAsReplacement: false, action: { _ in return false }), nil) case let .limitExceeded(limit, premiumLimit): + let premiumConfiguration = PremiumConfiguration.with(appConfiguration: context.currentAppConfiguration.with { $0 }) let text: String - if limit == premiumLimit { + if limit == premiumLimit || premiumConfiguration.isPremiumDisabled { text = presentationData.strings.Premium_MaxSavedGifsFinalText } else { text = presentationData.strings.Premium_MaxSavedGifsText("\(premiumLimit)").string diff --git a/submodules/TelegramUI/Sources/HorizontalStickersChatContextPanelNode.swift b/submodules/TelegramUI/Sources/HorizontalStickersChatContextPanelNode.swift index d13163808d..4cbfdca401 100755 --- a/submodules/TelegramUI/Sources/HorizontalStickersChatContextPanelNode.swift +++ b/submodules/TelegramUI/Sources/HorizontalStickersChatContextPanelNode.swift @@ -193,8 +193,9 @@ final class HorizontalStickersChatContextPanelNode: ChatInputContextPanelNode { case .generic: strongSelf.interfaceInteraction?.presentGlobalOverlayController(UndoOverlayController(presentationData: presentationData, content: .sticker(context: strongSelf.context, file: item.file, title: nil, text: !isStarred ? strongSelf.strings.Conversation_StickerAddedToFavorites : strongSelf.strings.Conversation_StickerRemovedFromFavorites, undoText: nil), elevatedLayout: false, action: { _ in return false }), nil) case let .limitExceeded(limit, premiumLimit): + let premiumConfiguration = PremiumConfiguration.with(appConfiguration: strongSelf.context.currentAppConfiguration.with { $0 }) let text: String - if limit == premiumLimit { + if limit == premiumLimit || premiumConfiguration.isPremiumDisabled { text = strongSelf.strings.Premium_MaxFavedStickersFinalText } else { text = strongSelf.strings.Premium_MaxFavedStickersText("\(premiumLimit)").string diff --git a/submodules/TelegramUI/Sources/InlineReactionSearchPanel.swift b/submodules/TelegramUI/Sources/InlineReactionSearchPanel.swift index 7c9dfcfae3..74f9b3eca9 100644 --- a/submodules/TelegramUI/Sources/InlineReactionSearchPanel.swift +++ b/submodules/TelegramUI/Sources/InlineReactionSearchPanel.swift @@ -145,8 +145,9 @@ private final class InlineReactionSearchStickersNode: ASDisplayNode, UIScrollVie case .generic: strongSelf.getControllerInteraction?()?.presentGlobalOverlayController(UndoOverlayController(presentationData: presentationData, content: .sticker(context: strongSelf.context, file: item.file, title: nil, text: !isStarred ? strongSelf.strings.Conversation_StickerAddedToFavorites : strongSelf.strings.Conversation_StickerRemovedFromFavorites, undoText: nil), elevatedLayout: false, action: { _ in return false }), nil) case let .limitExceeded(limit, premiumLimit): + let premiumConfiguration = PremiumConfiguration.with(appConfiguration: strongSelf.context.currentAppConfiguration.with { $0 }) let text: String - if limit == premiumLimit { + if limit == premiumLimit || premiumConfiguration.isPremiumDisabled { text = strongSelf.strings.Premium_MaxFavedStickersFinalText } else { text = strongSelf.strings.Premium_MaxFavedStickersText("\(premiumLimit)").string diff --git a/submodules/TelegramUI/Sources/NotificationContentContext.swift b/submodules/TelegramUI/Sources/NotificationContentContext.swift index f4730c6517..e01e712ec4 100644 --- a/submodules/TelegramUI/Sources/NotificationContentContext.swift +++ b/submodules/TelegramUI/Sources/NotificationContentContext.swift @@ -119,7 +119,7 @@ public final class NotificationViewControllerImpl { }, applicationInForeground: .single(false), applicationIsActive: .single(false), clearMessageNotifications: { _ in }, pushIdleTimerExtension: { return EmptyDisposable - }, openSettings: {}, openAppStorePage: {}, registerForNotifications: { _ in }, requestSiriAuthorization: { _ in }, siriAuthorization: { return .notDetermined }, getWindowHost: { + }, openSettings: {}, openAppStorePage: {}, openSubscriptions: {}, registerForNotifications: { _ in }, requestSiriAuthorization: { _ in }, siriAuthorization: { return .notDetermined }, getWindowHost: { return nil }, presentNativeController: { _ in }, dismissNativeController: { diff --git a/submodules/TelegramUI/Sources/PeerInfo/PeerInfoScreen.swift b/submodules/TelegramUI/Sources/PeerInfo/PeerInfoScreen.swift index dece83c726..e7cd0e07c8 100644 --- a/submodules/TelegramUI/Sources/PeerInfo/PeerInfoScreen.swift +++ b/submodules/TelegramUI/Sources/PeerInfo/PeerInfoScreen.swift @@ -724,9 +724,12 @@ private func settingsItems(data: PeerInfoScreenData?, context: AccountContext, p interaction.openSettings(.language) })) - items[.payment]!.append(PeerInfoScreenDisclosureItem(id: 100, label: .text(""), text: "Telegram Premium", icon: PresentationResourcesSettings.premium, action: { - interaction.openSettings(.premium) - })) + let premiumConfiguration = PremiumConfiguration.with(appConfiguration: context.currentAppConfiguration.with { $0 }) + if !premiumConfiguration.isPremiumDisabled { + items[.payment]!.append(PeerInfoScreenDisclosureItem(id: 100, label: .text(""), text: presentationData.strings.Settings_Premium, icon: PresentationResourcesSettings.premium, action: { + interaction.openSettings(.premium) + })) + } /*items[.payment]!.append(PeerInfoScreenDisclosureItem(id: 100, label: .text(""), text: "Payment Method", icon: PresentationResourcesSettings.language, action: { interaction.openPaymentMethod() @@ -2980,11 +2983,14 @@ final class PeerInfoScreenNode: ViewControllerTracingNode, UIScrollViewDelegate return } - let controller = PremiumIntroScreen(context: strongSelf.context, source: .profile(strongSelf.peerId)) - controller.sourceView = sourceView - controller.containerView = strongSelf.controller?.navigationController?.view - controller.animationColor = white ? .white : strongSelf.presentationData.theme.list.itemAccentColor - strongSelf.controller?.push(controller) + let premiumConfiguration = PremiumConfiguration.with(appConfiguration: strongSelf.context.currentAppConfiguration.with { $0 }) + if !premiumConfiguration.isPremiumDisabled { + let controller = PremiumIntroScreen(context: strongSelf.context, source: .profile(strongSelf.peerId)) + controller.sourceView = sourceView + controller.containerView = strongSelf.controller?.navigationController?.view + controller.animationColor = white ? .white : strongSelf.presentationData.theme.list.itemAccentColor + strongSelf.controller?.push(controller) + } } self.headerNode.displayAvatarContextMenu = { [weak self] node, gesture in diff --git a/submodules/TelegramUI/Sources/ShareExtensionContext.swift b/submodules/TelegramUI/Sources/ShareExtensionContext.swift index 3181503f85..aa7280daf8 100644 --- a/submodules/TelegramUI/Sources/ShareExtensionContext.swift +++ b/submodules/TelegramUI/Sources/ShareExtensionContext.swift @@ -193,7 +193,10 @@ public class ShareRootControllerImpl { }, applicationInForeground: .single(false), applicationIsActive: .single(false), clearMessageNotifications: { _ in }, pushIdleTimerExtension: { return EmptyDisposable - }, openSettings: {}, openAppStorePage: {}, registerForNotifications: { _ in }, requestSiriAuthorization: { _ in }, siriAuthorization: { return .notDetermined }, getWindowHost: { + }, openSettings: { + }, openAppStorePage: { + }, openSubscriptions: { + }, registerForNotifications: { _ in }, requestSiriAuthorization: { _ in }, siriAuthorization: { return .notDetermined }, getWindowHost: { return nil }, presentNativeController: { _ in }, dismissNativeController: { diff --git a/submodules/TelegramUI/Sources/StickersChatInputContextPanelNode.swift b/submodules/TelegramUI/Sources/StickersChatInputContextPanelNode.swift index 2808ad948f..b31262461c 100644 --- a/submodules/TelegramUI/Sources/StickersChatInputContextPanelNode.swift +++ b/submodules/TelegramUI/Sources/StickersChatInputContextPanelNode.swift @@ -149,8 +149,9 @@ final class StickersChatInputContextPanelNode: ChatInputContextPanelNode { case .generic: strongSelf.interfaceInteraction?.presentGlobalOverlayController(UndoOverlayController(presentationData: presentationData, content: .sticker(context: strongSelf.context, file: item.file, title: nil, text: !isStarred ? strongSelf.strings.Conversation_StickerAddedToFavorites : strongSelf.strings.Conversation_StickerRemovedFromFavorites, undoText: nil), elevatedLayout: false, action: { _ in return false }), nil) case let .limitExceeded(limit, premiumLimit): + let premiumConfiguration = PremiumConfiguration.with(appConfiguration: strongSelf.context.currentAppConfiguration.with { $0 }) let text: String - if limit == premiumLimit { + if limit == premiumLimit || premiumConfiguration.isPremiumDisabled { text = strongSelf.strings.Premium_MaxFavedStickersFinalText } else { text = strongSelf.strings.Premium_MaxFavedStickersText("\(premiumLimit)").string From 9ad1b7bf86de8d2ebb4c482f8bbd00986941405a Mon Sep 17 00:00:00 2001 From: Ilya Laktyushin Date: Mon, 6 Jun 2022 16:28:23 +0400 Subject: [PATCH 2/3] Various improvements --- .../Sources/IncreaseLimitHeaderItem.swift | 3 +- .../Sources/PremiumIntroScreen.swift | 59 +-- .../Sources/PremiumLimitScreen.swift | 348 +++++++++++------- .../Sources/PremiumLimitsListScreen.swift | 3 +- .../PremiumUI/Sources/RollingCountLabel.swift | 21 +- .../Sources/StickersCarouselComponent.swift | 4 +- 6 files changed, 263 insertions(+), 175 deletions(-) diff --git a/submodules/PeerInfoUI/Sources/IncreaseLimitHeaderItem.swift b/submodules/PeerInfoUI/Sources/IncreaseLimitHeaderItem.swift index e0bb7bfddc..5eb7619454 100644 --- a/submodules/PeerInfoUI/Sources/IncreaseLimitHeaderItem.swift +++ b/submodules/PeerInfoUI/Sources/IncreaseLimitHeaderItem.swift @@ -160,7 +160,8 @@ class IncreaseLimitHeaderItemNode: ListViewItemNode { activeTitleColor: .white, badgeIconName: badgeIconName, badgeText: "\(item.count)", - badgePosition: CGFloat(item.count) / CGFloat(item.premiumCount) + badgePosition: CGFloat(item.count) / CGFloat(item.premiumCount), + isPremiumDisabled: false )), environment: {}, containerSize: CGSize(width: layout.size.width - params.leftInset - params.rightInset, height: 200.0) diff --git a/submodules/PremiumUI/Sources/PremiumIntroScreen.swift b/submodules/PremiumUI/Sources/PremiumIntroScreen.swift index b443345f23..49f1158882 100644 --- a/submodules/PremiumUI/Sources/PremiumIntroScreen.swift +++ b/submodules/PremiumUI/Sources/PremiumIntroScreen.swift @@ -1320,35 +1320,40 @@ private final class PremiumIntroScreenComponent: CombinedComponent { super.init() + let availableProducts: Signal<[InAppPurchaseManager.Product], NoError> if let inAppPurchaseManager = context.inAppPurchaseManager { - let otherPeerName: Signal - if case let .profile(peerId) = source { - let presentationData = context.sharedContext.currentPresentationData.with { $0 } - otherPeerName = context.engine.data.get(TelegramEngine.EngineData.Item.Peer.Peer(id: peerId)) - |> map { peer -> String? in - return peer?.displayTitle(strings: presentationData.strings, displayOrder: presentationData.nameDisplayOrder) - } - } else { - otherPeerName = .single(nil) - } - - self.disposable = combineLatest( - queue: Queue.mainQueue(), - inAppPurchaseManager.availableProducts, - context.account.postbox.peerView(id: context.account.peerId) - |> map { view -> Bool in - return view.peers[view.peerId]?.isPremium ?? false - }, - otherPeerName - ).start(next: { [weak self] products, isPremium, otherPeerName in - if let strongSelf = self { - strongSelf.premiumProduct = products.first - strongSelf.isPremium = isPremium - strongSelf.otherPeerName = otherPeerName - strongSelf.updated(transition: .immediate) - } - }) + availableProducts = inAppPurchaseManager.availableProducts + } else { + availableProducts = .single([]) } + + let otherPeerName: Signal + if case let .profile(peerId) = source { + let presentationData = context.sharedContext.currentPresentationData.with { $0 } + otherPeerName = context.engine.data.get(TelegramEngine.EngineData.Item.Peer.Peer(id: peerId)) + |> map { peer -> String? in + return peer?.displayTitle(strings: presentationData.strings, displayOrder: presentationData.nameDisplayOrder) + } + } else { + otherPeerName = .single(nil) + } + + self.disposable = combineLatest( + queue: Queue.mainQueue(), + availableProducts, + context.account.postbox.peerView(id: context.account.peerId) + |> map { view -> Bool in + return view.peers[view.peerId]?.isPremium ?? false + }, + otherPeerName + ).start(next: { [weak self] products, isPremium, otherPeerName in + if let strongSelf = self { + strongSelf.premiumProduct = products.first + strongSelf.isPremium = isPremium + strongSelf.otherPeerName = otherPeerName + strongSelf.updated(transition: .immediate) + } + }) } deinit { diff --git a/submodules/PremiumUI/Sources/PremiumLimitScreen.swift b/submodules/PremiumUI/Sources/PremiumLimitScreen.swift index 985f3fe5d5..a1f2ba2808 100644 --- a/submodules/PremiumUI/Sources/PremiumLimitScreen.swift +++ b/submodules/PremiumUI/Sources/PremiumLimitScreen.swift @@ -44,6 +44,7 @@ private class PremiumLimitAnimationComponent: Component { private let textColor: UIColor private let badgeText: String? private let badgePosition: CGFloat + private let isPremiumDisabled: Bool init( iconName: String?, @@ -51,7 +52,8 @@ private class PremiumLimitAnimationComponent: Component { activeColors: [UIColor], textColor: UIColor, badgeText: String?, - badgePosition: CGFloat + badgePosition: CGFloat, + isPremiumDisabled: Bool ) { self.iconName = iconName self.inactiveColor = inactiveColor @@ -59,6 +61,7 @@ private class PremiumLimitAnimationComponent: Component { self.textColor = textColor self.badgeText = badgeText self.badgePosition = badgePosition + self.isPremiumDisabled = isPremiumDisabled } static func ==(lhs: PremiumLimitAnimationComponent, rhs: PremiumLimitAnimationComponent) -> Bool { @@ -80,6 +83,9 @@ private class PremiumLimitAnimationComponent: Component { if lhs.badgePosition != rhs.badgePosition { return false } + if lhs.isPremiumDisabled != rhs.isPremiumDisabled { + return false + } return true } @@ -182,44 +188,41 @@ private class PremiumLimitAnimationComponent: Component { private var didPlayAppearanceAnimation = false func playAppearanceAnimation(component: PremiumLimitAnimationComponent, availableSize: CGSize) { self.badgeView.layer.animateScale(from: 0.1, to: 1.0, duration: 0.4, timingFunction: CAMediaTimingFunctionName.easeInEaseOut.rawValue) - - let now = self.badgeView.layer.convertTime(CACurrentMediaTime(), from: nil) - + let positionAnimation = CABasicAnimation(keyPath: "position.x") positionAnimation.fromValue = NSValue(cgPoint: CGPoint(x: 0.0, y: 0.0)) positionAnimation.toValue = NSValue(cgPoint: self.badgeView.center) positionAnimation.duration = 0.5 positionAnimation.fillMode = .forwards - positionAnimation.beginTime = now - - let rotateAnimation = CABasicAnimation(keyPath: "transform.rotation.z") - rotateAnimation.fromValue = 0.0 as NSNumber - rotateAnimation.toValue = 0.2 as NSNumber - rotateAnimation.isAdditive = true - rotateAnimation.duration = 0.2 - rotateAnimation.beginTime = now + 0.5 - rotateAnimation.fillMode = .forwards - rotateAnimation.timingFunction = CAMediaTimingFunction(name: .easeOut) + self.badgeView.layer.add(positionAnimation, forKey: "appearance1") + Queue.mainQueue().after(0.5, { + let rotateAnimation = CABasicAnimation(keyPath: "transform.rotation.z") + rotateAnimation.fromValue = 0.0 as NSNumber + rotateAnimation.toValue = 0.2 as NSNumber + rotateAnimation.duration = 0.2 + rotateAnimation.fillMode = .forwards + rotateAnimation.timingFunction = CAMediaTimingFunction(name: .easeOut) + rotateAnimation.isRemovedOnCompletion = false + self.badgeView.layer.add(rotateAnimation, forKey: "appearance2") + if !self.badgeView.isHidden { self.hapticFeedback.impact(.light) } + + Queue.mainQueue().after(0.2) { + let returnAnimation = CABasicAnimation(keyPath: "transform.rotation.z") + returnAnimation.fromValue = 0.2 as NSNumber + returnAnimation.toValue = 0.0 as NSNumber + returnAnimation.duration = 0.18 + returnAnimation.fillMode = .forwards + returnAnimation.timingFunction = CAMediaTimingFunction(name: .easeIn) + self.badgeView.layer.add(returnAnimation, forKey: "appearance3") + self.badgeView.layer.removeAnimation(forKey: "appearance2") + } }) - let returnAnimation = CABasicAnimation(keyPath: "transform.rotation.z") - returnAnimation.fromValue = 0.2 as NSNumber - returnAnimation.toValue = 0.0 as NSNumber - returnAnimation.isAdditive = true - returnAnimation.duration = 0.18 - returnAnimation.beginTime = now + 0.5 + 0.2 - returnAnimation.fillMode = .forwards - returnAnimation.timingFunction = CAMediaTimingFunction(name: .easeIn) - - self.badgeView.layer.add(positionAnimation, forKey: "appearance1") - self.badgeView.layer.add(rotateAnimation, forKey: "appearance2") - self.badgeView.layer.add(returnAnimation, forKey: "appearance3") - self.badgeView.alpha = 1.0 self.badgeView.layer.animateAlpha(from: 0.0, to: 1.0, duration: 0.1) @@ -241,12 +244,14 @@ private class PremiumLimitAnimationComponent: Component { let containerFrame = CGRect(origin: CGPoint(x: 0.0, y: availableSize.height - lineHeight), size: CGSize(width: availableSize.width, height: lineHeight)) self.container.frame = containerFrame - self.inactiveBackground.frame = CGRect(origin: .zero, size: CGSize(width: containerFrame.width / 2.0, height: lineHeight)) - self.activeContainer.frame = CGRect(origin: CGPoint(x: containerFrame.width / 2.0, y: 0.0), size: CGSize(width: containerFrame.width / 2.0, height: lineHeight)) - - self.activeBackground.bounds = CGRect(origin: .zero, size: CGSize(width: containerFrame.width * 3.0 / 2.0, height: lineHeight)) - if self.activeBackground.animation(forKey: "movement") == nil { - self.activeBackground.position = CGPoint(x: containerFrame.width * 3.0 / 4.0 - self.activeBackground.frame.width * 0.35, y: lineHeight / 2.0) + if !component.isPremiumDisabled { + self.inactiveBackground.frame = CGRect(origin: .zero, size: CGSize(width: containerFrame.width / 2.0, height: lineHeight)) + self.activeContainer.frame = CGRect(origin: CGPoint(x: containerFrame.width / 2.0, y: 0.0), size: CGSize(width: containerFrame.width / 2.0, height: lineHeight)) + + self.activeBackground.bounds = CGRect(origin: .zero, size: CGSize(width: containerFrame.width * 3.0 / 2.0, height: lineHeight)) + if self.activeBackground.animation(forKey: "movement") == nil { + self.activeBackground.position = CGPoint(x: containerFrame.width * 3.0 / 4.0 - self.activeBackground.frame.width * 0.35, y: lineHeight / 2.0) + } } let countWidth: CGFloat @@ -276,24 +281,41 @@ private class PremiumLimitAnimationComponent: Component { self.badgeMaskTailView.frame = CGRect(origin: CGPoint(x: badgeSize.width - 44.0, y: badgeSize.height - 36.0), size: CGSize(width: 44.0, height: 36.0)) self.badgeView.bounds = CGRect(origin: .zero, size: badgeSize) - if component.badgePosition > 1.0 - .ulpOfOne { + + var badgePosition = component.badgePosition + if component.isPremiumDisabled { + badgePosition = 0.5 + } + if badgePosition > 1.0 - .ulpOfOne { self.badgeView.layer.anchorPoint = CGPoint(x: 1.0, y: 1.0) self.badgeMaskTailView.isHidden = false self.badgeMaskArrowView.isHidden = true - self.badgeView.center = CGPoint(x: 3.0 + (availableSize.width - 6.0) * component.badgePosition + 3.0, y: 82.0) + if let _ = self.badgeView.layer.animation(forKey: "appearance1") { + + } else { + self.badgeView.center = CGPoint(x: 3.0 + (availableSize.width - 6.0) * badgePosition + 3.0, y: 82.0) + } } else { self.badgeView.layer.anchorPoint = CGPoint(x: 0.5, y: 1.0) self.badgeMaskTailView.isHidden = true - self.badgeMaskArrowView.isHidden = false + self.badgeMaskArrowView.isHidden = component.isPremiumDisabled - self.badgeView.center = CGPoint(x: 3.0 + (availableSize.width - 6.0) * component.badgePosition, y: 82.0) + if let _ = self.badgeView.layer.animation(forKey: "appearance1") { + + } else { + self.badgeView.center = CGPoint(x: 3.0 + (availableSize.width - 6.0) * badgePosition, y: 82.0) + } if self.badgeView.frame.maxX > availableSize.width { let delta = self.badgeView.frame.maxX - availableSize.width - 6.0 - self.badgeView.center = self.badgeView.center.offsetBy(dx: -delta, dy: 0.0) + if let _ = self.badgeView.layer.animation(forKey: "appearance1") { + + } else { + self.badgeView.center = self.badgeView.center.offsetBy(dx: -delta, dy: 0.0) + } } } self.badgeForeground.bounds = CGRect(origin: CGPoint(), size: CGSize(width: badgeSize.width * 3.0, height: badgeSize.height)) @@ -304,7 +326,16 @@ private class PremiumLimitAnimationComponent: Component { self.badgeIcon.frame = CGRect(x: 15.0, y: 9.0, width: 30.0, height: 30.0) self.badgeCountLabel.frame = CGRect(x: badgeSize.width - countWidth - 11.0, y: 10.0, width: countWidth, height: 48.0) - if !self.didPlayAppearanceAnimation { + if component.isPremiumDisabled { + if !self.didPlayAppearanceAnimation { + self.didPlayAppearanceAnimation = true + + self.badgeView.alpha = 1.0 + if let badgeText = component.badgeText { + self.badgeCountLabel.configure(with: badgeText, duration: 0.3) + } + } + } else if !self.didPlayAppearanceAnimation { self.didPlayAppearanceAnimation = true self.playAppearanceAnimation(component: component, availableSize: availableSize) } @@ -396,6 +427,7 @@ public final class PremiumLimitDisplayComponent: CombinedComponent { let badgeIconName: String? let badgeText: String? let badgePosition: CGFloat + let isPremiumDisabled: Bool public init( inactiveColor: UIColor, @@ -408,7 +440,8 @@ public final class PremiumLimitDisplayComponent: CombinedComponent { activeTitleColor: UIColor, badgeIconName: String?, badgeText: String?, - badgePosition: CGFloat + badgePosition: CGFloat, + isPremiumDisabled: Bool ) { self.inactiveColor = inactiveColor self.activeColors = activeColors @@ -421,6 +454,7 @@ public final class PremiumLimitDisplayComponent: CombinedComponent { self.badgeIconName = badgeIconName self.badgeText = badgeText self.badgePosition = badgePosition + self.isPremiumDisabled = isPremiumDisabled } public static func ==(lhs: PremiumLimitDisplayComponent, rhs: PremiumLimitDisplayComponent) -> Bool { @@ -457,6 +491,9 @@ public final class PremiumLimitDisplayComponent: CombinedComponent { if lhs.badgePosition != rhs.badgePosition { return false } + if lhs.isPremiumDisabled != rhs.isPremiumDisabled { + return false + } return true } @@ -473,62 +510,6 @@ public final class PremiumLimitDisplayComponent: CombinedComponent { let height: CGFloat = 120.0 let lineHeight: CGFloat = 30.0 - let inactiveTitle = inactiveTitle.update( - component: MultilineTextComponent( - text: .plain( - NSAttributedString( - string: component.inactiveTitle, - font: Font.semibold(15.0), - textColor: component.inactiveTitleColor - ) - ) - ), - availableSize: context.availableSize, - transition: context.transition - ) - - let inactiveValue = inactiveValue.update( - component: MultilineTextComponent( - text: .plain( - NSAttributedString( - string: component.inactiveValue, - font: Font.semibold(15.0), - textColor: component.inactiveTitleColor - ) - ) - ), - availableSize: context.availableSize, - transition: context.transition - ) - - let activeTitle = activeTitle.update( - component: MultilineTextComponent( - text: .plain( - NSAttributedString( - string: component.activeTitle, - font: Font.semibold(15.0), - textColor: component.activeTitleColor - ) - ) - ), - availableSize: context.availableSize, - transition: context.transition - ) - - let activeValue = activeValue.update( - component: MultilineTextComponent( - text: .plain( - NSAttributedString( - string: component.activeValue, - font: Font.semibold(15.0), - textColor: component.activeTitleColor - ) - ) - ), - availableSize: context.availableSize, - transition: context.transition - ) - let animation = animation.update( component: PremiumLimitAnimationComponent( iconName: component.badgeIconName, @@ -536,7 +517,8 @@ public final class PremiumLimitDisplayComponent: CombinedComponent { activeColors: component.activeColors, textColor: component.activeTitleColor, badgeText: component.badgeText, - badgePosition: component.badgePosition + badgePosition: component.badgePosition, + isPremiumDisabled: component.isPremiumDisabled ), availableSize: CGSize(width: context.availableSize.width, height: height), transition: context.transition @@ -546,22 +528,80 @@ public final class PremiumLimitDisplayComponent: CombinedComponent { .position(CGPoint(x: context.availableSize.width / 2.0, y: height / 2.0)) ) - context.add(inactiveTitle - .position(CGPoint(x: inactiveTitle.size.width / 2.0 + 12.0, y: height - lineHeight / 2.0)) - ) - - context.add(inactiveValue - .position(CGPoint(x: context.availableSize.width / 2.0 - inactiveValue.size.width / 2.0 - 12.0, y: height - lineHeight / 2.0)) - ) - - context.add(activeTitle - .position(CGPoint(x: context.availableSize.width / 2.0 + activeTitle.size.width / 2.0 + 12.0, y: height - lineHeight / 2.0)) - ) - - context.add(activeValue - .position(CGPoint(x: context.availableSize.width - activeValue.size.width / 2.0 - 12.0, y: height - lineHeight / 2.0)) - ) - + if !component.isPremiumDisabled { + let inactiveTitle = inactiveTitle.update( + component: MultilineTextComponent( + text: .plain( + NSAttributedString( + string: component.inactiveTitle, + font: Font.semibold(15.0), + textColor: component.inactiveTitleColor + ) + ) + ), + availableSize: context.availableSize, + transition: context.transition + ) + + let inactiveValue = inactiveValue.update( + component: MultilineTextComponent( + text: .plain( + NSAttributedString( + string: component.inactiveValue, + font: Font.semibold(15.0), + textColor: component.inactiveTitleColor + ) + ) + ), + availableSize: context.availableSize, + transition: context.transition + ) + + let activeTitle = activeTitle.update( + component: MultilineTextComponent( + text: .plain( + NSAttributedString( + string: component.activeTitle, + font: Font.semibold(15.0), + textColor: component.activeTitleColor + ) + ) + ), + availableSize: context.availableSize, + transition: context.transition + ) + + let activeValue = activeValue.update( + component: MultilineTextComponent( + text: .plain( + NSAttributedString( + string: component.activeValue, + font: Font.semibold(15.0), + textColor: component.activeTitleColor + ) + ) + ), + availableSize: context.availableSize, + transition: context.transition + ) + + context.add(inactiveTitle + .position(CGPoint(x: inactiveTitle.size.width / 2.0 + 12.0, y: height - lineHeight / 2.0)) + ) + + context.add(inactiveValue + .position(CGPoint(x: context.availableSize.width / 2.0 - inactiveValue.size.width / 2.0 - 12.0, y: height - lineHeight / 2.0)) + ) + + context.add(activeTitle + .position(CGPoint(x: context.availableSize.width / 2.0 + activeTitle.size.width / 2.0 + 12.0, y: height - lineHeight / 2.0)) + ) + + context.add(activeValue + .position(CGPoint(x: context.availableSize.width - activeValue.size.width / 2.0 - 12.0, y: height - lineHeight / 2.0)) + ) + } + return CGSize(width: context.availableSize.width, height: height) } } @@ -656,6 +696,9 @@ private final class LimitSheetContent: CombinedComponent { let state = context.state let subject = component.subject + let premiumConfiguration = PremiumConfiguration.with(appConfiguration: component.context.currentAppConfiguration.with { $0 }) + let isPremiumDisabled = premiumConfiguration.isPremiumDisabled + let sideInset: CGFloat = 16.0 + environment.safeInsets.left let textSideInset: CGFloat = 24.0 + environment.safeInsets.left @@ -684,7 +727,7 @@ private final class LimitSheetContent: CombinedComponent { var titleText = strings.Premium_LimitReached var buttonAnimationName = "premium_x2" let iconName: String - let badgeText: String + var badgeText: String var string: String let defaultValue: String let premiumValue: String @@ -703,6 +746,11 @@ private final class LimitSheetContent: CombinedComponent { if !state.isPremium && badgePosition > 0.5 { string = strings.Premium_MaxFoldersCountText("\(limit)", "\(premiumLimit)").string } + + if isPremiumDisabled { + badgeText = "\(limit)" + string = strings.Premium_MaxFoldersCountNoPremiumText("\(limit)").string + } case .chatsPerFolder: let limit = state.limits.maxFolderChatsCount let premiumLimit = state.premiumLimits.maxFolderChatsCount @@ -712,6 +760,11 @@ private final class LimitSheetContent: CombinedComponent { defaultValue = component.count > limit ? "\(limit)" : "" premiumValue = component.count >= premiumLimit ? "" : "\(premiumLimit)" badgePosition = CGFloat(component.count) / CGFloat(premiumLimit) + + if isPremiumDisabled { + badgeText = "\(limit)" + string = strings.Premium_MaxChatsInFolderNoPremiumText("\(limit)").string + } case .pins: let limit = state.limits.maxPinnedChatCount let premiumLimit = state.premiumLimits.maxPinnedChatCount @@ -721,6 +774,11 @@ private final class LimitSheetContent: CombinedComponent { defaultValue = component.count > limit ? "\(limit)" : "" premiumValue = component.count >= premiumLimit ? "" : "\(premiumLimit)" badgePosition = CGFloat(component.count) / CGFloat(premiumLimit) + + if isPremiumDisabled { + badgeText = "\(limit)" + string = strings.Premium_MaxPinsNoPremiumText("\(limit)").string + } case .files: let limit = Int64(state.limits.maxUploadFileParts) * 512 * 1024 + 1024 * 1024 * 100 let premiumLimit = Int64(state.premiumLimits.maxUploadFileParts) * 512 * 1024 + 1024 * 1024 * 100 @@ -731,6 +789,11 @@ private final class LimitSheetContent: CombinedComponent { premiumValue = component.count != 4 ? dataSizeString(premiumLimit, formatting: DataSizeStringFormatting(strings: environment.strings, decimalSeparator: environment.dateTimeFormat.decimalSeparator)) : "" badgePosition = component.count == 4 ? 1.0 : 0.5 titleText = strings.Premium_FileTooLarge + + if isPremiumDisabled { + badgeText = dataSizeString(limit, formatting: DataSizeStringFormatting(strings: environment.strings, decimalSeparator: environment.dateTimeFormat.decimalSeparator)) + string = strings.Premium_MaxFileSizeNoPremiumText(dataSizeString(premiumLimit, formatting: DataSizeStringFormatting(strings: environment.strings, decimalSeparator: environment.dateTimeFormat.decimalSeparator))).string + } case .accounts: let limit = 3 let premiumLimit = component.count + 1 @@ -745,6 +808,11 @@ private final class LimitSheetContent: CombinedComponent { badgePosition = CGFloat(component.count) / CGFloat(premiumLimit) } buttonAnimationName = "premium_addone" + + if isPremiumDisabled { + badgeText = "\(limit)" + string = strings.Premium_MaxAccountsNoPremiumText("\(limit)").string + } } var reachedMaximumLimit = badgePosition >= 1.0 if case .folders = subject, !state.isPremium { @@ -784,16 +852,26 @@ private final class LimitSheetContent: CombinedComponent { transition: .immediate ) + let gradientColors: [UIColor] + if isPremiumDisabled { + gradientColors = [ + UIColor(rgb: 0x007afe), + UIColor(rgb: 0x5494ff) + ] + } else { + gradientColors = [ + UIColor(rgb: 0x0077ff), + UIColor(rgb: 0x6b93ff), + UIColor(rgb: 0x8878ff), + UIColor(rgb: 0xe46ace) + ] + } + if state.initialized { let limit = limit.update( component: PremiumLimitDisplayComponent( inactiveColor: theme.list.itemBlocksSeparatorColor.withAlphaComponent(0.5), - activeColors: [ - UIColor(rgb: 0x0077ff), - UIColor(rgb: 0x6b93ff), - UIColor(rgb: 0x8878ff), - UIColor(rgb: 0xe46ace) - ], + activeColors: gradientColors, inactiveTitle: strings.Premium_Free, inactiveValue: defaultValue, inactiveTitleColor: theme.list.itemPrimaryTextColor, @@ -802,7 +880,8 @@ private final class LimitSheetContent: CombinedComponent { activeTitleColor: .white, badgeIconName: iconName, badgeText: badgeText, - badgePosition: badgePosition + badgePosition: badgePosition, + isPremiumDisabled: isPremiumDisabled ), availableSize: CGSize(width: context.availableSize.width - sideInset * 2.0, height: context.availableSize.height), transition: .immediate @@ -812,47 +891,50 @@ private final class LimitSheetContent: CombinedComponent { ) } + let isIncreaseButton = !reachedMaximumLimit && !isPremiumDisabled let button = button.update( component: SolidRoundedButtonComponent( - title: !reachedMaximumLimit ? strings.Premium_IncreaseLimit : strings.Common_OK, + title: isIncreaseButton ? strings.Premium_IncreaseLimit : strings.Common_OK, theme: SolidRoundedButtonComponent.Theme( backgroundColor: .black, - backgroundColors: [ - UIColor(rgb: 0x0077ff), - UIColor(rgb: 0x6b93ff), - UIColor(rgb: 0x8878ff), - UIColor(rgb: 0xe46ace) - ], + backgroundColors: gradientColors, foregroundColor: .white ), font: .bold, fontSize: 17.0, height: 50.0, cornerRadius: 10.0, - gloss: !reachedMaximumLimit, - animationName: !reachedMaximumLimit ? buttonAnimationName : nil, + gloss: isIncreaseButton, + animationName: isIncreaseButton ? buttonAnimationName : nil, iconPosition: .right, action: { [weak component] in guard let component = component else { return } component.dismiss() - component.action() + if isIncreaseButton { + component.action() + } } ), availableSize: CGSize(width: context.availableSize.width - sideInset * 2.0, height: 50.0), transition: context.transition ) + var textOffset: CGFloat = 228.0 + if isPremiumDisabled { + textOffset -= 68.0 + } + context.add(title .position(CGPoint(x: context.availableSize.width / 2.0, y: 28.0)) ) context.add(text - .position(CGPoint(x: context.availableSize.width / 2.0, y: 228.0)) + .position(CGPoint(x: context.availableSize.width / 2.0, y: textOffset)) ) - let buttonFrame = CGRect(origin: CGPoint(x: sideInset, y: 228.0 + ceil(text.size.height / 2.0) + 38.0), size: button.size) + let buttonFrame = CGRect(origin: CGPoint(x: sideInset, y: textOffset + ceil(text.size.height / 2.0) + 38.0), size: button.size) context.add(button .position(CGPoint(x: buttonFrame.midX, y: buttonFrame.midY)) ) diff --git a/submodules/PremiumUI/Sources/PremiumLimitsListScreen.swift b/submodules/PremiumUI/Sources/PremiumLimitsListScreen.swift index 0d35423853..e4f47cf261 100644 --- a/submodules/PremiumUI/Sources/PremiumLimitsListScreen.swift +++ b/submodules/PremiumUI/Sources/PremiumLimitsListScreen.swift @@ -167,7 +167,8 @@ private final class LimitComponent: CombinedComponent { activeTitleColor: component.activeTextColor, badgeIconName: "", badgeText: nil, - badgePosition: 0.0 + badgePosition: 0.0, + isPremiumDisabled: false ), availableSize: CGSize(width: context.availableSize.width - sideInset * 2.0, height: context.availableSize.height), transition: .immediate diff --git a/submodules/PremiumUI/Sources/RollingCountLabel.swift b/submodules/PremiumUI/Sources/RollingCountLabel.swift index 48195fe479..5191a5e775 100644 --- a/submodules/PremiumUI/Sources/RollingCountLabel.swift +++ b/submodules/PremiumUI/Sources/RollingCountLabel.swift @@ -30,7 +30,6 @@ open class RollingLabel: UILabel { open var showSymbol = false private var scrollLayers: [CAScrollLayer] = [] private var scrollLabels: [UILabel] = [] - private let duration = 1.12 private let durationOffset = 0.2 private let textsNotAnimated = [","] @@ -38,26 +37,26 @@ open class RollingLabel: UILabel { self.suffix = suffix } - func configure(with string: String) { - fullText = string + func configure(with string: String, duration: Double = 0.9) { + self.fullText = string - clean() - setupSubviews() + self.clean() + self.setupSubviews() self.text = " " - self.animate() + self.animate(duration: duration) } - private func animate(ascending: Bool = true) { - createAnimations(ascending: ascending) + private func animate(ascending: Bool = true, duration: Double) { + self.createAnimations(ascending: ascending, duration: duration) } private func clean() { self.text = nil self.subviews.forEach { $0.removeFromSuperview() } self.layer.sublayers?.forEach { $0.removeFromSuperlayer() } - scrollLayers.removeAll() - scrollLabels.removeAll() + self.scrollLayers.removeAll() + self.scrollLabels.removeAll() } private func setupSubviews() { @@ -168,7 +167,7 @@ open class RollingLabel: UILabel { } } - private func createAnimations(ascending: Bool) { + private func createAnimations(ascending: Bool, duration: Double) { var offset: CFTimeInterval = 0.0 for scrollLayer in scrollLayers { diff --git a/submodules/PremiumUI/Sources/StickersCarouselComponent.swift b/submodules/PremiumUI/Sources/StickersCarouselComponent.swift index b4535bdb2d..33ff458566 100644 --- a/submodules/PremiumUI/Sources/StickersCarouselComponent.swift +++ b/submodules/PremiumUI/Sources/StickersCarouselComponent.swift @@ -257,7 +257,7 @@ private class StickerNode: ASDisplayNode { } } - let placeholderFrame = CGRect(origin: .zero, size: imageSize) + let placeholderFrame = CGRect(origin: CGPoint(x: -10.0, y: 0.0), size: imageSize) let thumbnailDimensions = PixelDimensions(width: 512, height: 512) self.placeholderNode.update(backgroundColor: nil, foregroundColor: UIColor(rgb: 0xffffff, alpha: 0.2), shimmeringColor: UIColor(rgb: 0xffffff, alpha: 0.3), data: self.file.immediateThumbnailData, size: placeholderFrame.size, imageSize: thumbnailDimensions.cgSize) self.placeholderNode.frame = placeholderFrame @@ -297,7 +297,7 @@ private class StickersCarouselNode: ASDisplayNode, UIScrollViewDelegate { super.init() - self.clipsToBounds = true +// self.clipsToBounds = true self.addSubnode(self.scrollNode) self.scrollNode.addSubnode(self.tapNode) From dd20006e40bc9aa72bac52c14c83c5380474a3f4 Mon Sep 17 00:00:00 2001 From: Ilya Laktyushin Date: Mon, 6 Jun 2022 16:57:25 +0400 Subject: [PATCH 3/3] Various fixes --- .../Items/ItemListMultilineInputItem.swift | 11 ++++++++++ .../TelegramUI/Sources/AccountContext.swift | 9 ++++++++- .../Sources/PeerInfo/PeerInfoScreen.swift | 20 ++++++++++++++++++- .../PeerInfoScreenMultilineInputtem.swift | 6 +++++- 4 files changed, 43 insertions(+), 3 deletions(-) diff --git a/submodules/ItemListUI/Sources/Items/ItemListMultilineInputItem.swift b/submodules/ItemListUI/Sources/Items/ItemListMultilineInputItem.swift index d1cea4832c..138f9d20fd 100644 --- a/submodules/ItemListUI/Sources/Items/ItemListMultilineInputItem.swift +++ b/submodules/ItemListUI/Sources/Items/ItemListMultilineInputItem.swift @@ -127,6 +127,8 @@ public class ItemListMultilineInputItemNode: ListViewItemNode, ASEditableTextNod return self.item?.tag } + private var exceededLimit = false + public init() { self.backgroundNode = ASDisplayNode() self.backgroundNode.isLayerBacked = true @@ -197,6 +199,7 @@ public class ItemListMultilineInputItemNode: ListViewItemNode, ASEditableTextNod var limitTextString: NSAttributedString? var rightInset: CGFloat = params.rightInset + var exceededLimit = false if let maxLength = item.maxLength, maxLength.display { let textLength: Int switch maxLength.mode { @@ -210,6 +213,7 @@ public class ItemListMultilineInputItemNode: ListViewItemNode, ASEditableTextNod if displayTextLimit { limitTextString = NSAttributedString(string: "\(remainingCount)", font: Font.regular(13.0), textColor: remainingCount < 0 ? item.presentationData.theme.list.itemDestructiveColor : item.presentationData.theme.list.itemSecondaryTextColor) } + exceededLimit = remainingCount < 0 rightInset += 30.0 + 4.0 } @@ -254,6 +258,7 @@ public class ItemListMultilineInputItemNode: ListViewItemNode, ASEditableTextNod if let strongSelf = self { strongSelf.item = item strongSelf.layoutParams = params + strongSelf.exceededLimit = exceededLimit if let _ = updatedTheme { strongSelf.topStripeNode.backgroundColor = itemSeparatorColor @@ -470,6 +475,12 @@ public class ItemListMultilineInputItemNode: ListViewItemNode, ASEditableTextNod self.textNode.layer.addShakeAnimation() } + public func animateErrorIfNeeded() { + if self.exceededLimit { + self.animateError() + } + } + @objc private func inlineActionPressed() { if let action = self.item?.inlineAction?.action { action() diff --git a/submodules/TelegramUI/Sources/AccountContext.swift b/submodules/TelegramUI/Sources/AccountContext.swift index c9dc7f0b23..944d5bcb33 100644 --- a/submodules/TelegramUI/Sources/AccountContext.swift +++ b/submodules/TelegramUI/Sources/AccountContext.swift @@ -286,7 +286,14 @@ public final class AccountContextImpl: AccountContext { strongSelf.animatedEmojiStickers = stickers }) - self.userLimitsConfigurationDisposable = (self.engine.data.subscribe(TelegramEngine.EngineData.Item.Configuration.UserLimits(isPremium: false)) + self.userLimitsConfigurationDisposable = (self.account.postbox.peerView(id: self.account.peerId) + |> mapToSignal { peerView -> Signal in + if let peer = peerView.peers[peerView.peerId] { + return self.engine.data.subscribe(TelegramEngine.EngineData.Item.Configuration.UserLimits(isPremium: peer.isPremium)) + } else { + return .complete() + } + } |> deliverOnMainQueue).start(next: { [weak self] value in guard let strongSelf = self else { return diff --git a/submodules/TelegramUI/Sources/PeerInfo/PeerInfoScreen.swift b/submodules/TelegramUI/Sources/PeerInfo/PeerInfoScreen.swift index e7cd0e07c8..c028aea13b 100644 --- a/submodules/TelegramUI/Sources/PeerInfo/PeerInfoScreen.swift +++ b/submodules/TelegramUI/Sources/PeerInfo/PeerInfoScreen.swift @@ -219,6 +219,14 @@ private final class PeerInfoScreenItemSectionContainerNode: ASDisplayNode { return contentHeight } + + func animateErrorIfNeeded() { + for (_, itemNode) in self.itemNodes { + if let itemNode = itemNode as? PeerInfoScreenMultilineInputItemNode { + itemNode.animateErrorIfNeeded() + } + } + } } final class PeerInfoSelectionPanelNode: ASDisplayNode { @@ -2636,7 +2644,6 @@ final class PeerInfoScreenNode: ViewControllerTracingNode, UIScrollViewDelegate }) strongSelf.controller?.navigationItem.setLeftBarButton(UIBarButtonItem(title: strongSelf.presentationData.strings.Common_Cancel, style: .plain, target: strongSelf, action: #selector(strongSelf.editingCancelPressed)), animated: true) case .done, .cancel: - (strongSelf.controller?.parent as? TabBarController)?.updateIsTabBarHidden(false, transition: .animated(duration: 0.3, curve: .linear)) strongSelf.view.endEditing(true) if case .done = key { guard let data = strongSelf.data else { @@ -2649,6 +2656,16 @@ final class PeerInfoScreenNode: ViewControllerTracingNode, UIScrollViewDelegate let lastName = strongSelf.headerNode.editingContentNode.editingTextForKey(.lastName) ?? "" let bio = strongSelf.state.updatingBio + if let bio = bio { + if Int32(bio.count) > strongSelf.context.userLimits.maxAboutLength { + for (_, section) in strongSelf.editingSections { + section.animateErrorIfNeeded() + } + strongSelf.hapticFeedback?.error() + return + } + } + if peer.firstName != firstName || peer.lastName != lastName || (bio != nil && bio != cachedData.about) { var updateNameSignal: Signal = .complete() var hasProgress = false @@ -2889,6 +2906,7 @@ final class PeerInfoScreenNode: ViewControllerTracingNode, UIScrollViewDelegate }, completion: nil) strongSelf.controller?.navigationItem.setLeftBarButton(nil, animated: true) } + (strongSelf.controller?.parent as? TabBarController)?.updateIsTabBarHidden(false, transition: .animated(duration: 0.3, curve: .linear)) case .select: strongSelf.state = strongSelf.state.withSelectedMessageIds(Set()) if let (layout, navigationHeight) = strongSelf.validLayout { diff --git a/submodules/TelegramUI/Sources/PeerInfoScreenMultilineInputtem.swift b/submodules/TelegramUI/Sources/PeerInfoScreenMultilineInputtem.swift index 41c7c682c4..8127aa0509 100644 --- a/submodules/TelegramUI/Sources/PeerInfoScreenMultilineInputtem.swift +++ b/submodules/TelegramUI/Sources/PeerInfoScreenMultilineInputtem.swift @@ -29,7 +29,7 @@ final class PeerInfoScreenMultilineInputItem: PeerInfoScreenItem { } } -private final class PeerInfoScreenMultilineInputItemNode: PeerInfoScreenItemNode { +final class PeerInfoScreenMultilineInputItemNode: PeerInfoScreenItemNode { private let bottomSeparatorNode: ASDisplayNode private let maskNode: ASImageNode @@ -118,4 +118,8 @@ private final class PeerInfoScreenMultilineInputItemNode: PeerInfoScreenItemNode return height } + + func animateErrorIfNeeded() { + self.itemNode?.animateErrorIfNeeded() + } }