From 891fed3189938c572412e04a02592fca7d037cab Mon Sep 17 00:00:00 2001 From: Ilya Laktyushin Date: Tue, 23 Apr 2024 02:51:25 +0400 Subject: [PATCH] Add story stealth mode shortcut in chat list --- .../Telegram-iOS/en.lproj/Localizable.strings | 3 + .../Sources/AccountContext.swift | 2 +- .../AccountContext/Sources/Premium.swift | 1 + submodules/ChatListUI/BUILD | 1 + .../Sources/ChatListController.swift | 46 +++-- .../ChatListControllerStoryStealthMode.swift | 162 ++++++++++++++++++ .../ChatListFilterPresetController.swift | 4 +- .../ChatListFilterPresetListController.swift | 4 +- .../PremiumUI/Sources/PremiumDemoScreen.swift | 65 +++++++ .../Sources/RecentActionsSettingsSheet.swift | 19 +- .../Sources/AdsReportScreen.swift | 4 +- .../ChatMessageInteractiveFileNode.swift | 8 +- ...atMessageInteractiveInstantVideoNode.swift | 8 +- .../ChatRecentActionsHistoryTransition.swift | 4 +- .../Sources/PeerNameColorScreen.swift | 4 +- .../Sources/WallpaperGalleryController.swift | 4 +- .../Sources/OpenStories.swift | 4 +- .../Sources/StoryContainerScreen.swift | 16 ++ .../StoryItemSetContainerComponent.swift | 23 ++- ...StoryItemSetContainerViewSendMessage.swift | 4 +- .../Sources/StoryStealthModeSheetScreen.swift | 13 +- .../TelegramUI/Sources/ChatController.swift | 4 +- .../ChatInterfaceStateContextMenus.swift | 8 +- .../Sources/SharedAccountContext.swift | 21 ++- 24 files changed, 366 insertions(+), 66 deletions(-) create mode 100644 submodules/ChatListUI/Sources/ChatListControllerStoryStealthMode.swift diff --git a/Telegram/Telegram-iOS/en.lproj/Localizable.strings b/Telegram/Telegram-iOS/en.lproj/Localizable.strings index 7a950aff16..3247f7e7aa 100644 --- a/Telegram/Telegram-iOS/en.lproj/Localizable.strings +++ b/Telegram/Telegram-iOS/en.lproj/Localizable.strings @@ -12134,3 +12134,6 @@ Sorry for the inconvenience."; "Channel.AdminLogFilter.Section.Messages" = "Messages"; "Premium.Gift.ContactSelection.AddBirthday" = "Add Your Birthday"; + + +"Story.StealthMode.EnableAndOpenAction" = "Enable and Open the Story"; diff --git a/submodules/AccountContext/Sources/AccountContext.swift b/submodules/AccountContext/Sources/AccountContext.swift index c05f275d35..7ff9730b3c 100644 --- a/submodules/AccountContext/Sources/AccountContext.swift +++ b/submodules/AccountContext/Sources/AccountContext.swift @@ -1012,7 +1012,7 @@ public protocol SharedAccountContext: AnyObject { func makeChatQrCodeScreen(context: AccountContext, peer: Peer, threadId: Int64?, temporary: Bool) -> ViewController func makePremiumIntroController(context: AccountContext, source: PremiumIntroSource, forceDark: Bool, dismissed: (() -> Void)?) -> ViewController - func makePremiumDemoController(context: AccountContext, subject: PremiumDemoSubject, action: @escaping () -> Void) -> ViewController + func makePremiumDemoController(context: AccountContext, subject: PremiumDemoSubject, forceDark: Bool, action: @escaping () -> Void, dismissed: (() -> Void)?) -> ViewController func makePremiumLimitController(context: AccountContext, subject: PremiumLimitSubject, count: Int32, forceDark: Bool, cancel: @escaping () -> Void, action: @escaping () -> Bool) -> ViewController func makePremiumGiftController(context: AccountContext, source: PremiumGiftSource, completion: (() -> Void)?) -> ViewController func makePremiumPrivacyControllerController(context: AccountContext, subject: PremiumPrivacySubject, peerId: EnginePeer.Id) -> ViewController diff --git a/submodules/AccountContext/Sources/Premium.swift b/submodules/AccountContext/Sources/Premium.swift index 02dfa7dec2..a2edb10793 100644 --- a/submodules/AccountContext/Sources/Premium.swift +++ b/submodules/AccountContext/Sources/Premium.swift @@ -72,6 +72,7 @@ public enum PremiumDemoSubject { case lastSeen case messagePrivacy case folderTags + case business case businessLocation case businessHours diff --git a/submodules/ChatListUI/BUILD b/submodules/ChatListUI/BUILD index 1e40ba5992..4cfac4cabe 100644 --- a/submodules/ChatListUI/BUILD +++ b/submodules/ChatListUI/BUILD @@ -103,6 +103,7 @@ swift_library( "//submodules/TelegramUI/Components/Settings/PeerNameColorItem", "//submodules/TelegramUI/Components/Settings/BirthdayPickerScreen", "//submodules/Components/MultilineTextComponent", + "//submodules/TelegramUI/Components/Stories/StoryStealthModeSheetScreen", ], visibility = [ "//visibility:public", diff --git a/submodules/ChatListUI/Sources/ChatListController.swift b/submodules/ChatListUI/Sources/ChatListController.swift index e105a43bd2..fd94633a75 100644 --- a/submodules/ChatListUI/Sources/ChatListController.swift +++ b/submodules/ChatListUI/Sources/ChatListController.swift @@ -210,6 +210,8 @@ public class ChatListControllerImpl: TelegramBaseController, ChatListController private var sharedOpenStoryProgressDisposable = MetaDisposable() + var currentTooltipUpdateTimer: Foundation.Timer? + private var fullScreenEffectView: RippleEffectView? public override func updateNavigationCustomData(_ data: Any?, progress: CGFloat, transition: ContainedViewLayoutTransition) { @@ -3082,20 +3084,29 @@ public class ChatListControllerImpl: TelegramBaseController, ChatListController } }))) -// items.append(.action(ContextMenuActionItem(text: presentationData.strings.StoryFeed_ViewAnonymously, icon: { theme in -// return generateTintedImage(image: UIImage(bundleImageName: self.context.isPremium ? "Chat/Context Menu/Eye" : "Chat/Context Menu/EyeLocked"), color: theme.contextMenu.primaryColor) -// }, action: { [weak self] _, a in -// a(.default) -// -// guard let self else { -// return -// } -// if self.context.isPremium { -//// self.sendMessageContext.requestStealthMode(view: self) -// } else { -//// self.presentStealthModeUpgradeScreen() -// } -// }))) + items.append(.action(ContextMenuActionItem(text: presentationData.strings.StoryFeed_ViewAnonymously, icon: { theme in + return generateTintedImage(image: UIImage(bundleImageName: self.context.isPremium ? "Chat/Context Menu/Eye" : "Chat/Context Menu/EyeLocked"), color: theme.contextMenu.primaryColor) + }, action: { [weak self] _, a in + a(.default) + + guard let self else { + return + } + if self.context.isPremium { + self.requestStealthMode(openStory: { [weak self] presentTooltip in + guard let self else { + return + } + self.openStories(peerId: peer.id, completion: { storyController in + presentTooltip(storyController) + }) + }) + } else { + self.presentStealthModeUpgrade(action: { [weak self] in + self?.presentUpgradeStoriesScreen() + }) + } + }))) let hideText: String if self.location == .chatList(groupId: .archive) { @@ -3985,6 +3996,10 @@ public class ChatListControllerImpl: TelegramBaseController, ChatListController } public func openStories(peerId: EnginePeer.Id) { + self.openStories(peerId: peerId, completion: { _ in }) + } + + public func openStories(peerId: EnginePeer.Id, completion: @escaping (StoryContainerScreen) -> Void = { _ in }) { if let navigationBarView = self.chatListDisplayNode.navigationBarView.view as? ChatListNavigationBar.View { if navigationBarView.storiesUnlocked { self.shouldFixStorySubscriptionOrder = true @@ -4087,7 +4102,8 @@ public class ChatListControllerImpl: TelegramBaseController, ChatListController self.sharedOpenStoryProgressDisposable.set(nil) componentView.storyPeerListView()?.setLoadingItem(peerId: peerId, signal: signal) } - } + }, + completion: completion ) return diff --git a/submodules/ChatListUI/Sources/ChatListControllerStoryStealthMode.swift b/submodules/ChatListUI/Sources/ChatListControllerStoryStealthMode.swift new file mode 100644 index 0000000000..e50b7668d6 --- /dev/null +++ b/submodules/ChatListUI/Sources/ChatListControllerStoryStealthMode.swift @@ -0,0 +1,162 @@ +import Foundation +import Display +import SwiftSignalKit +import TelegramCore +import TelegramPresentationData +import AccountContext +import StoryContainerScreen +import StoryStealthModeSheetScreen +import UndoUI + +extension ChatListControllerImpl { + func requestStealthMode(openStory: @escaping (@escaping (StoryContainerScreen) -> Void) -> Void) { + let context = self.context + + let _ = (context.engine.data.get( + TelegramEngine.EngineData.Item.Configuration.StoryConfigurationState(), + TelegramEngine.EngineData.Item.Configuration.App() + ) + |> deliverOnMainQueue).start(next: { [weak self] config, appConfig in + guard let self else { + return + } + + let timestamp = Int32(Date().timeIntervalSince1970) + if let activeUntilTimestamp = config.stealthModeState.actualizedNow().activeUntilTimestamp, activeUntilTimestamp > timestamp { + let remainingActiveSeconds = activeUntilTimestamp - timestamp + + let presentationData = context.sharedContext.currentPresentationData.with { $0 }.withUpdated(theme: defaultDarkPresentationTheme) + let text = presentationData.strings.Story_ToastStealthModeActiveText(timeIntervalString(strings: presentationData.strings, value: remainingActiveSeconds)).string + let tooltipScreen = UndoOverlayController( + presentationData: presentationData, + content: .actionSucceeded(title: presentationData.strings.Story_ToastStealthModeActiveTitle, text: text, cancel: "", destructive: false), + elevatedLayout: false, + animateInAsReplacement: false, + action: { _ in + return false + } + ) + tooltipScreen.tag = "no_auto_dismiss" + weak var tooltipScreenValue: UndoOverlayController? = tooltipScreen + self.currentTooltipUpdateTimer?.invalidate() + self.currentTooltipUpdateTimer = Foundation.Timer.scheduledTimer(withTimeInterval: 0.5, repeats: true, block: { [weak self] _ in + guard let self else { + return + } + guard let tooltipScreenValue else { + self.currentTooltipUpdateTimer?.invalidate() + self.currentTooltipUpdateTimer = nil + return + } + + let timestamp = Int32(Date().timeIntervalSince1970) + let remainingActiveSeconds = max(1, activeUntilTimestamp - timestamp) + + let presentationData = context.sharedContext.currentPresentationData.with { $0 }.withUpdated(theme: defaultDarkPresentationTheme) + let text = presentationData.strings.Story_ToastStealthModeActiveText(timeIntervalString(strings: presentationData.strings, value: remainingActiveSeconds)).string + tooltipScreenValue.content = .actionSucceeded(title: presentationData.strings.Story_ToastStealthModeActiveTitle, text: text, cancel: "", destructive: false) + }) + + openStory({ storyController in + storyController.presentExternalTooltip(tooltipScreen) + }) + + return + } + + let pastPeriod: Int32 + let futurePeriod: Int32 + if let data = appConfig.data, let futurePeriodF = data["stories_stealth_future_period"] as? Double, let pastPeriodF = data["stories_stealth_past_period"] as? Double { + futurePeriod = Int32(futurePeriodF) + pastPeriod = Int32(pastPeriodF) + } else { + pastPeriod = 5 * 60 + futurePeriod = 25 * 60 + } + + let sheet = StoryStealthModeSheetScreen( + context: context, + mode: .control(external: true, cooldownUntilTimestamp: config.stealthModeState.actualizedNow().cooldownUntilTimestamp), + forceDark: false, + backwardDuration: pastPeriod, + forwardDuration: futurePeriod, + buttonAction: { + let _ = (context.engine.messages.enableStoryStealthMode() + |> deliverOnMainQueue).start(completed: { + let presentationData = context.sharedContext.currentPresentationData.with { $0 }.withUpdated(theme: defaultDarkPresentationTheme) + let text = presentationData.strings.Story_ToastStealthModeActivatedText(timeIntervalString(strings: presentationData.strings, value: pastPeriod), timeIntervalString(strings: presentationData.strings, value: futurePeriod)).string + let tooltipScreen = UndoOverlayController( + presentationData: presentationData, + content: .actionSucceeded(title: presentationData.strings.Story_ToastStealthModeActivatedTitle, text: text, cancel: "", destructive: false), + elevatedLayout: false, + animateInAsReplacement: false, + action: { _ in + return false + } + ) + + openStory({ storyController in + storyController.presentExternalTooltip(tooltipScreen) + }) + + HapticFeedback().success() + }) + } + ) + self.push(sheet) + }) + } + + func presentStealthModeUpgrade(action: @escaping () -> Void) { + let context = self.context + let _ = (context.engine.data.get( + TelegramEngine.EngineData.Item.Configuration.StoryConfigurationState(), + TelegramEngine.EngineData.Item.Configuration.App() + ) + |> deliverOnMainQueue).start(next: { [weak self] config, appConfig in + guard let self else { + return + } + + let pastPeriod: Int32 + let futurePeriod: Int32 + if let data = appConfig.data, let futurePeriodF = data["stories_stealth_future_period"] as? Double, let pastPeriodF = data["stories_stealth_past_period"] as? Double { + futurePeriod = Int32(futurePeriodF) + pastPeriod = Int32(pastPeriodF) + } else { + pastPeriod = 5 * 60 + futurePeriod = 25 * 60 + } + + let sheet = StoryStealthModeSheetScreen( + context: context, + mode: .upgrade, + forceDark: false, + backwardDuration: pastPeriod, + forwardDuration: futurePeriod, + buttonAction: { + action() + } + ) + self.push(sheet) + }) + } + + func presentUpgradeStoriesScreen() { + let context = self.context + var replaceImpl: ((ViewController) -> Void)? + let controller = context.sharedContext.makePremiumDemoController(context: context, subject: .stories, forceDark: false, action: { + let controller = context.sharedContext.makePremiumIntroController(context: context, source: .storiesStealthMode, forceDark: false, dismissed: nil) + replaceImpl?(controller) + }, dismissed: nil) + replaceImpl = { [weak self, weak controller] c in + controller?.dismiss(animated: true, completion: { + guard let self else { + return + } + self.push(c) + }) + } + self.push(controller) + } +} diff --git a/submodules/ChatListUI/Sources/ChatListFilterPresetController.swift b/submodules/ChatListUI/Sources/ChatListFilterPresetController.swift index cdb77cad55..b45661aca5 100644 --- a/submodules/ChatListUI/Sources/ChatListFilterPresetController.swift +++ b/submodules/ChatListUI/Sources/ChatListFilterPresetController.swift @@ -1579,10 +1579,10 @@ func chatListFilterPresetController(context: AccountContext, currentPreset initi }, openTagColorPremium: { var replaceImpl: ((ViewController) -> Void)? - let controller = context.sharedContext.makePremiumDemoController(context: context, subject: .folderTags, action: { + let controller = context.sharedContext.makePremiumDemoController(context: context, subject: .folderTags, forceDark: false, action: { let controller = context.sharedContext.makePremiumIntroController(context: context, source: .folderTags, forceDark: false, dismissed: nil) replaceImpl?(controller) - }) + }, dismissed: nil) replaceImpl = { [weak controller] c in controller?.replace(with: c) } diff --git a/submodules/ChatListUI/Sources/ChatListFilterPresetListController.swift b/submodules/ChatListUI/Sources/ChatListFilterPresetListController.swift index c398146a21..a6e9fe1af9 100644 --- a/submodules/ChatListUI/Sources/ChatListFilterPresetListController.swift +++ b/submodules/ChatListUI/Sources/ChatListFilterPresetListController.swift @@ -559,10 +559,10 @@ public func chatListFilterPresetListController(context: AccountContext, mode: Ch context.engine.peers.updateChatListFiltersDisplayTags(isEnabled: value) }, updateDisplayTagsLocked: { var replaceImpl: ((ViewController) -> Void)? - let controller = context.sharedContext.makePremiumDemoController(context: context, subject: .folderTags, action: { + let controller = context.sharedContext.makePremiumDemoController(context: context, subject: .folderTags, forceDark: false, action: { let controller = context.sharedContext.makePremiumIntroController(context: context, source: .folderTags, forceDark: false, dismissed: nil) replaceImpl?(controller) - }) + }, dismissed: nil) replaceImpl = { [weak controller] c in controller?.replace(with: c) } diff --git a/submodules/PremiumUI/Sources/PremiumDemoScreen.swift b/submodules/PremiumUI/Sources/PremiumDemoScreen.swift index c66d950c82..de2053e80c 100644 --- a/submodules/PremiumUI/Sources/PremiumDemoScreen.swift +++ b/submodules/PremiumUI/Sources/PremiumDemoScreen.swift @@ -1425,6 +1425,71 @@ public class PremiumDemoScreen: ViewControllerComponentContainer { case businessChatBots case businessIntro case businessLinks + + public var perk: PremiumPerk { + switch self { + case .doubleLimits: + return .doubleLimits + case .moreUpload: + return .moreUpload + case .fasterDownload: + return .fasterDownload + case .voiceToText: + return .voiceToText + case .noAds: + return .noAds + case .uniqueReactions: + return .uniqueReactions + case .premiumStickers: + return .premiumStickers + case .advancedChatManagement: + return .advancedChatManagement + case .profileBadge: + return .profileBadge + case .animatedUserpics: + return .animatedUserpics + case .appIcons: + return .appIcons + case .animatedEmoji: + return .animatedEmoji + case .emojiStatus: + return .emojiStatus + case .translation: + return .translation + case .stories: + return .stories + case .colors: + return .colors + case .wallpapers: + return .wallpapers + case .messageTags: + return .messageTags + case .lastSeen: + return .lastSeen + case .messagePrivacy: + return .messagePrivacy + case .business: + return .business + case .folderTags: + return .folderTags + case .businessLocation: + return .businessLocation + case .businessHours: + return .businessHours + case .businessGreetingMessage: + return .businessGreetingMessage + case .businessQuickReplies: + return .businessQuickReplies + case .businessAwayMessage: + return .businessAwayMessage + case .businessChatBots: + return .businessChatBots + case .businessIntro: + return .businessIntro + case .businessLinks: + return .businessLinks + } + } } public enum Source: Equatable { diff --git a/submodules/TelegramUI/Components/AdminUserActionsSheet/Sources/RecentActionsSettingsSheet.swift b/submodules/TelegramUI/Components/AdminUserActionsSheet/Sources/RecentActionsSettingsSheet.swift index c31ae3256c..24df544b7f 100644 --- a/submodules/TelegramUI/Components/AdminUserActionsSheet/Sources/RecentActionsSettingsSheet.swift +++ b/submodules/TelegramUI/Components/AdminUserActionsSheet/Sources/RecentActionsSettingsSheet.swift @@ -369,7 +369,7 @@ private final class RecentActionsSettingsSheetComponent: Component { ) } - private func updateScrolling(transition: Transition) { + private func updateScrolling(isFirstTime: Bool = false, transition: Transition) { guard let environment = self.environment, let controller = environment.controller(), let itemLayout = self.itemLayout else { return } @@ -389,16 +389,23 @@ private final class RecentActionsSettingsSheetComponent: Component { var topOffsetFraction = topOffset / topOffsetDistance topOffsetFraction = max(0.0, min(1.0, topOffsetFraction)) + let modalStyleOverlayTransition: ContainedViewLayoutTransition + if isFirstTime { + modalStyleOverlayTransition = .animated(duration: 0.4, curve: .spring) + } else { + modalStyleOverlayTransition = transition.containedViewLayoutTransition + } + let transitionFactor: CGFloat = 1.0 - topOffsetFraction if self.isUpdating { DispatchQueue.main.async { [weak controller] in guard let controller else { return } - controller.updateModalStyleOverlayTransitionFactor(transitionFactor, transition: transition.containedViewLayoutTransition) + controller.updateModalStyleOverlayTransitionFactor(transitionFactor, transition: modalStyleOverlayTransition) } } else { - controller.updateModalStyleOverlayTransitionFactor(transitionFactor, transition: transition.containedViewLayoutTransition) + controller.updateModalStyleOverlayTransitionFactor(transitionFactor, transition: modalStyleOverlayTransition) } } @@ -443,8 +450,10 @@ private final class RecentActionsSettingsSheetComponent: Component { let resetScrolling = self.scrollView.bounds.width != availableSize.width let sideInset: CGFloat = 16.0 + environment.safeInsets.left - + + var isFirstTime = false if self.component == nil { + isFirstTime = true self.selectedMembersActions = Set(MembersActionType.actionTypesFromFlags(component.initialValue.events)) self.selectedSettingsActions = Set(SettingsActionType.actionTypesFromFlags(component.initialValue.events)) self.selectedMessagesActions = Set(MessagesActionType.actionTypesFromFlags(component.initialValue.events)) @@ -924,7 +933,7 @@ private final class RecentActionsSettingsSheetComponent: Component { } } self.ignoreScrolling = false - self.updateScrolling(transition: transition) + self.updateScrolling(isFirstTime: isFirstTime, transition: transition) return availableSize } diff --git a/submodules/TelegramUI/Components/Ads/AdsReportScreen/Sources/AdsReportScreen.swift b/submodules/TelegramUI/Components/Ads/AdsReportScreen/Sources/AdsReportScreen.swift index 5e6410d81d..cb1bcc02fb 100644 --- a/submodules/TelegramUI/Components/Ads/AdsReportScreen/Sources/AdsReportScreen.swift +++ b/submodules/TelegramUI/Components/Ads/AdsReportScreen/Sources/AdsReportScreen.swift @@ -625,10 +625,10 @@ public final class AdsReportScreen: ViewControllerComponentContainer { }) case .premiumRequired: var replaceImpl: ((ViewController) -> Void)? - let controller = context.sharedContext.makePremiumDemoController(context: context, subject: .noAds, action: { + let controller = context.sharedContext.makePremiumDemoController(context: context, subject: .noAds, forceDark: false, action: { let controller = context.sharedContext.makePremiumIntroController(context: context, source: .ads, forceDark: false, dismissed: nil) replaceImpl?(controller) - }) + }, dismissed: nil) replaceImpl = { [weak controller] c in controller?.replace(with: c) } diff --git a/submodules/TelegramUI/Components/Chat/ChatMessageInteractiveFileNode/Sources/ChatMessageInteractiveFileNode.swift b/submodules/TelegramUI/Components/Chat/ChatMessageInteractiveFileNode/Sources/ChatMessageInteractiveFileNode.swift index f10ee1d73b..b9f82bab5c 100644 --- a/submodules/TelegramUI/Components/Chat/ChatMessageInteractiveFileNode/Sources/ChatMessageInteractiveFileNode.swift +++ b/submodules/TelegramUI/Components/Chat/ChatMessageInteractiveFileNode/Sources/ChatMessageInteractiveFileNode.swift @@ -370,10 +370,10 @@ public final class ChatMessageInteractiveFileNode: ASDisplayNode { let tipController = UndoOverlayController(presentationData: presentationData, content: .universal(animation: "anim_voiceToText", scale: 0.065, colors: [:], title: nil, text: presentationData.strings.Message_AudioTranscription_SubscribeToPremium, customUndoText: presentationData.strings.Message_AudioTranscription_SubscribeToPremiumAction, timeout: nil), elevatedLayout: false, position: .top, animateInAsReplacement: false, action: { action in if case .undo = action { var replaceImpl: ((ViewController) -> Void)? - let controller = context.sharedContext.makePremiumDemoController(context: context, subject: .voiceToText, action: { + let controller = context.sharedContext.makePremiumDemoController(context: context, subject: .voiceToText, forceDark: false, action: { let controller = context.sharedContext.makePremiumIntroController(context: context, source: .settings, forceDark: false, dismissed: nil) replaceImpl?(controller) - }) + }, dismissed: nil) replaceImpl = { [weak controller] c in controller?.replace(with: c) } @@ -537,10 +537,10 @@ public final class ChatMessageInteractiveFileNode: ASDisplayNode { let tipController = UndoOverlayController(presentationData: presentationData, content: .universal(animation: "Transcribe", scale: 0.06, colors: [:], title: nil, text: text, customUndoText: nil, timeout: timeout), elevatedLayout: false, position: .top, animateInAsReplacement: false, action: { action in if case .info = action { var replaceImpl: ((ViewController) -> Void)? - let controller = context.sharedContext.makePremiumDemoController(context: context, subject: .voiceToText, action: { + let controller = context.sharedContext.makePremiumDemoController(context: context, subject: .voiceToText, forceDark: false, action: { let controller = context.sharedContext.makePremiumIntroController(context: context, source: .settings, forceDark: false, dismissed: nil) replaceImpl?(controller) - }) + }, dismissed: nil) replaceImpl = { [weak controller] c in controller?.replace(with: c) } diff --git a/submodules/TelegramUI/Components/Chat/ChatMessageInteractiveInstantVideoNode/Sources/ChatMessageInteractiveInstantVideoNode.swift b/submodules/TelegramUI/Components/Chat/ChatMessageInteractiveInstantVideoNode/Sources/ChatMessageInteractiveInstantVideoNode.swift index e10b3d40c7..b6ab4de49f 100644 --- a/submodules/TelegramUI/Components/Chat/ChatMessageInteractiveInstantVideoNode/Sources/ChatMessageInteractiveInstantVideoNode.swift +++ b/submodules/TelegramUI/Components/Chat/ChatMessageInteractiveInstantVideoNode/Sources/ChatMessageInteractiveInstantVideoNode.swift @@ -1829,10 +1829,10 @@ public class ChatMessageInteractiveInstantVideoNode: ASDisplayNode { if case .undo = action { let context = item.context var replaceImpl: ((ViewController) -> Void)? - let controller = context.sharedContext.makePremiumDemoController(context: context, subject: .voiceToText, action: { + let controller = context.sharedContext.makePremiumDemoController(context: context, subject: .voiceToText, forceDark: false, action: { let controller = context.sharedContext.makePremiumIntroController(context: context, source: .settings, forceDark: false, dismissed: nil) replaceImpl?(controller) - }) + }, dismissed: nil) replaceImpl = { [weak controller] c in controller?.replace(with: c) } @@ -1941,10 +1941,10 @@ public class ChatMessageInteractiveInstantVideoNode: ASDisplayNode { let tipController = UndoOverlayController(presentationData: presentationData, content: .universal(animation: "Transcribe", scale: 0.06, colors: [:], title: nil, text: text, customUndoText: nil, timeout: timeout), elevatedLayout: false, position: .top, animateInAsReplacement: false, action: { action in if case .info = action { var replaceImpl: ((ViewController) -> Void)? - let controller = context.sharedContext.makePremiumDemoController(context: context, subject: .voiceToText, action: { + let controller = context.sharedContext.makePremiumDemoController(context: context, subject: .voiceToText, forceDark: false, action: { let controller = context.sharedContext.makePremiumIntroController(context: context, source: .settings, forceDark: false, dismissed: nil) replaceImpl?(controller) - }) + }, dismissed: nil) replaceImpl = { [weak controller] c in controller?.replace(with: c) } diff --git a/submodules/TelegramUI/Components/Chat/ChatRecentActionsController/Sources/ChatRecentActionsHistoryTransition.swift b/submodules/TelegramUI/Components/Chat/ChatRecentActionsController/Sources/ChatRecentActionsHistoryTransition.swift index e4576c18a6..5044049b0b 100644 --- a/submodules/TelegramUI/Components/Chat/ChatRecentActionsController/Sources/ChatRecentActionsHistoryTransition.swift +++ b/submodules/TelegramUI/Components/Chat/ChatRecentActionsController/Sources/ChatRecentActionsHistoryTransition.swift @@ -2244,7 +2244,7 @@ struct ChatRecentActionsEntry: Comparable, Identifiable { } } -private let deletedMessagesDisplayedLimit = 5 +private let deletedMessagesDisplayedLimit = 4 func chatRecentActionsEntries(entries: [ChannelAdminEventLogEntry], presentationData: ChatPresentationData, expandedDeletedMessages: Set) -> [ChatRecentActionsEntry] { var result: [ChatRecentActionsEntry] = [] @@ -2252,7 +2252,7 @@ func chatRecentActionsEntries(entries: [ChannelAdminEventLogEntry], presentation func appendCurrentDeleteEntries() { if !deleteMessageEntries.isEmpty, let lastEntry = deleteMessageEntries.last, let lastMessageId = lastEntry.event.action.messageId { - let isExpandable = deleteMessageEntries.count > deletedMessagesDisplayedLimit + let isExpandable = deleteMessageEntries.count >= deletedMessagesDisplayedLimit let isExpanded = expandedDeletedMessages.contains(lastMessageId) || !isExpandable let isGroup = deleteMessageEntries.count > 1 diff --git a/submodules/TelegramUI/Components/Settings/PeerNameColorScreen/Sources/PeerNameColorScreen.swift b/submodules/TelegramUI/Components/Settings/PeerNameColorScreen/Sources/PeerNameColorScreen.swift index c233426027..3740225ec6 100644 --- a/submodules/TelegramUI/Components/Settings/PeerNameColorScreen/Sources/PeerNameColorScreen.swift +++ b/submodules/TelegramUI/Components/Settings/PeerNameColorScreen/Sources/PeerNameColorScreen.swift @@ -726,10 +726,10 @@ public func PeerNameColorScreen( action: { action in if case .info = action { var replaceImpl: ((ViewController) -> Void)? - let controller = context.sharedContext.makePremiumDemoController(context: context, subject: .colors, action: { + let controller = context.sharedContext.makePremiumDemoController(context: context, subject: .colors, forceDark: false, action: { let controller = context.sharedContext.makePremiumIntroController(context: context, source: .settings, forceDark: false, dismissed: nil) replaceImpl?(controller) - }) + }, dismissed: nil) replaceImpl = { [weak controller] c in controller?.replace(with: c) } diff --git a/submodules/TelegramUI/Components/Settings/WallpaperGalleryScreen/Sources/WallpaperGalleryController.swift b/submodules/TelegramUI/Components/Settings/WallpaperGalleryScreen/Sources/WallpaperGalleryController.swift index 25faa1b007..18a158a3d4 100644 --- a/submodules/TelegramUI/Components/Settings/WallpaperGalleryScreen/Sources/WallpaperGalleryController.swift +++ b/submodules/TelegramUI/Components/Settings/WallpaperGalleryScreen/Sources/WallpaperGalleryController.swift @@ -527,10 +527,10 @@ public class WallpaperGalleryController: ViewController { if forBoth && !strongSelf.context.isPremium { let context = strongSelf.context var replaceImpl: ((ViewController) -> Void)? - let controller = context.sharedContext.makePremiumDemoController(context: context, subject: .wallpapers, action: { + let controller = context.sharedContext.makePremiumDemoController(context: context, subject: .wallpapers, forceDark: false, action: { let controller = context.sharedContext.makePremiumIntroController(context: context, source: .wallpapers, forceDark: false, dismissed: nil) replaceImpl?(controller) - }) + }, dismissed: nil) replaceImpl = { [weak controller] c in controller?.replace(with: c) } diff --git a/submodules/TelegramUI/Components/Stories/StoryContainerScreen/Sources/OpenStories.swift b/submodules/TelegramUI/Components/Stories/StoryContainerScreen/Sources/OpenStories.swift index f32358946b..c8e338546a 100644 --- a/submodules/TelegramUI/Components/Stories/StoryContainerScreen/Sources/OpenStories.swift +++ b/submodules/TelegramUI/Components/Stories/StoryContainerScreen/Sources/OpenStories.swift @@ -170,7 +170,8 @@ public extension StoryContainerScreen { transitionIn: @escaping () -> StoryContainerScreen.TransitionIn?, transitionOut: @escaping (EnginePeer.Id) -> StoryContainerScreen.TransitionOut?, setFocusedItem: @escaping (Signal) -> Void, - setProgress: @escaping (Signal) -> Void + setProgress: @escaping (Signal) -> Void, + completion: @escaping (StoryContainerScreen) -> Void = { _ in } ) { let storyContent = StoryContentContextImpl(context: context, isHidden: isHidden, focusedPeerId: peerId, singlePeer: singlePeer, fixedOrder: initialOrder) let signal = storyContent.state @@ -211,6 +212,7 @@ public extension StoryContainerScreen { ) setFocusedItem(storyContainerScreen.focusedItem) parentController?.push(storyContainerScreen) + completion(storyContainerScreen) } |> ignoreValues diff --git a/submodules/TelegramUI/Components/Stories/StoryContainerScreen/Sources/StoryContainerScreen.swift b/submodules/TelegramUI/Components/Stories/StoryContainerScreen/Sources/StoryContainerScreen.swift index 0fd9947382..9d200b7a99 100644 --- a/submodules/TelegramUI/Components/Stories/StoryContainerScreen/Sources/StoryContainerScreen.swift +++ b/submodules/TelegramUI/Components/Stories/StoryContainerScreen/Sources/StoryContainerScreen.swift @@ -1223,6 +1223,16 @@ private final class StoryContainerScreenComponent: Component { } } + func presentExternalTooltip(_ tooltipScreen: UndoOverlayController) { + guard let stateValue = self.stateValue, let slice = stateValue.slice, let itemSetView = self.visibleItemSetViews[slice.peer.id], let itemSetComponentView = itemSetView.view.view as? StoryItemSetContainerComponent.View else { + return + } + itemSetComponentView.sendMessageContext.tooltipScreen = tooltipScreen + itemSetComponentView.updateIsProgressPaused() + + self.environment?.controller()?.present(tooltipScreen, in: .current) + } + func update(component: StoryContainerScreenComponent, availableSize: CGSize, state: EmptyComponentState, environment: Environment, transition: Transition) -> CGSize { if self.didAnimateOut { return availableSize @@ -2057,6 +2067,12 @@ public class StoryContainerScreen: ViewControllerComponentContainer { } } + public func presentExternalTooltip(_ tooltipScreen: UndoOverlayController) { + if let componentView = self.node.hostView.componentView as? StoryContainerScreenComponent.View { + componentView.presentExternalTooltip(tooltipScreen) + } + } + func dismissWithoutTransitionOut() { self.focusedItemPromise.set(.single(nil)) diff --git a/submodules/TelegramUI/Components/Stories/StoryContainerScreen/Sources/StoryItemSetContainerComponent.swift b/submodules/TelegramUI/Components/Stories/StoryContainerScreen/Sources/StoryItemSetContainerComponent.swift index d4970771d5..6f931b1bca 100644 --- a/submodules/TelegramUI/Components/Stories/StoryContainerScreen/Sources/StoryItemSetContainerComponent.swift +++ b/submodules/TelegramUI/Components/Stories/StoryContainerScreen/Sources/StoryItemSetContainerComponent.swift @@ -5757,22 +5757,27 @@ public final class StoryItemSetContainerComponent: Component { }) } - private func presentStoriesUpgradeScreen(source: PremiumSource) { + private func presentStoriesUpgradeScreen(source: PremiumIntroSource) { guard let component = self.component else { return } let context = component.context var replaceImpl: ((ViewController) -> Void)? - let controller = PremiumLimitsListScreen(context: context, subject: .stories, source: .other, order: [.stories], buttonText: component.strings.Story_PremiumUpgradeStoriesButton, isPremium: false, forceDark: true) - controller.action = { [weak self] in + var dismissedImpl: (() -> Void)? + let controller = context.sharedContext.makePremiumDemoController(context: context, subject: .stories, forceDark: true, action: { [weak self] in guard let self else { return } - let controller = PremiumIntroScreen(context: context, source: source, forceDark: true) + var dismissedImpl: (() -> Void)? + let controller = context.sharedContext.makePremiumIntroController(context: context, source: source, forceDark: true, dismissed: { + dismissedImpl?() + }) self.sendMessageContext.actionSheet = controller - controller.wasDismissed = { [weak self, weak controller]in + replaceImpl?(controller) + + dismissedImpl = { [weak self, weak controller] in guard let self else { return } @@ -5782,10 +5787,10 @@ public final class StoryItemSetContainerComponent: Component { } self.updateIsProgressPaused() } - - replaceImpl?(controller) - } - controller.disposed = { [weak self, weak controller] in + }, dismissed: { + dismissedImpl?() + }) + dismissedImpl = { [weak self, weak controller] in guard let self else { return } diff --git a/submodules/TelegramUI/Components/Stories/StoryContainerScreen/Sources/StoryItemSetContainerViewSendMessage.swift b/submodules/TelegramUI/Components/Stories/StoryContainerScreen/Sources/StoryItemSetContainerViewSendMessage.swift index e9409d5f8b..0f5ea43c4b 100644 --- a/submodules/TelegramUI/Components/Stories/StoryContainerScreen/Sources/StoryItemSetContainerViewSendMessage.swift +++ b/submodules/TelegramUI/Components/Stories/StoryContainerScreen/Sources/StoryItemSetContainerViewSendMessage.swift @@ -3186,7 +3186,8 @@ final class StoryItemSetContainerSendMessage { let sheet = StoryStealthModeSheetScreen( context: component.context, - mode: .control(cooldownUntilTimestamp: config.stealthModeState.actualizedNow().cooldownUntilTimestamp), + mode: .control(external: false, cooldownUntilTimestamp: config.stealthModeState.actualizedNow().cooldownUntilTimestamp), + forceDark: true, backwardDuration: pastPeriod, forwardDuration: futurePeriod, buttonAction: { [weak self, weak view] in @@ -3261,6 +3262,7 @@ final class StoryItemSetContainerSendMessage { let sheet = StoryStealthModeSheetScreen( context: component.context, mode: .upgrade, + forceDark: true, backwardDuration: pastPeriod, forwardDuration: futurePeriod, buttonAction: { diff --git a/submodules/TelegramUI/Components/Stories/StoryStealthModeSheetScreen/Sources/StoryStealthModeSheetScreen.swift b/submodules/TelegramUI/Components/Stories/StoryStealthModeSheetScreen/Sources/StoryStealthModeSheetScreen.swift index 8103308393..61ea6561a8 100644 --- a/submodules/TelegramUI/Components/Stories/StoryStealthModeSheetScreen/Sources/StoryStealthModeSheetScreen.swift +++ b/submodules/TelegramUI/Components/Stories/StoryStealthModeSheetScreen/Sources/StoryStealthModeSheetScreen.swift @@ -100,7 +100,7 @@ private final class StoryStealthModeSheetContentComponent: Component { self.state = state var remainingCooldownSeconds: Int32 = 0 - if case let .control(cooldownUntilTimestamp) = component.mode { + if case let .control(_, cooldownUntilTimestamp) = component.mode { if let cooldownUntilTimestamp { remainingCooldownSeconds = cooldownUntilTimestamp - Int32(Date().timeIntervalSince1970) remainingCooldownSeconds = max(0, remainingCooldownSeconds) @@ -243,9 +243,9 @@ private final class StoryStealthModeSheetContentComponent: Component { let buttonText: String let content: AnyComponentWithIdentity switch component.mode { - case .control: + case let .control(external, _): if remainingCooldownSeconds <= 0 { - buttonText = environment.strings.Story_StealthMode_EnableAction + buttonText = external ? environment.strings.Story_StealthMode_EnableAndOpenAction : environment.strings.Story_StealthMode_EnableAction } else { buttonText = environment.strings.Story_StealthMode_CooldownAction(stringForDuration(remainingCooldownSeconds)).string } @@ -283,7 +283,7 @@ private final class StoryStealthModeSheetContentComponent: Component { } switch component.mode { - case let .control(cooldownUntilTimestamp): + case let .control(_, cooldownUntilTimestamp): var remainingCooldownSeconds: Int32 = 0 if let cooldownUntilTimestamp { remainingCooldownSeconds = cooldownUntilTimestamp - Int32(Date().timeIntervalSince1970) @@ -468,13 +468,14 @@ private final class StoryStealthModeSheetScreenComponent: Component { public class StoryStealthModeSheetScreen: ViewControllerComponentContainer { public enum Mode: Equatable { - case control(cooldownUntilTimestamp: Int32?) + case control(external: Bool, cooldownUntilTimestamp: Int32?) case upgrade } public init( context: AccountContext, mode: Mode, + forceDark: Bool, backwardDuration: Int32, forwardDuration: Int32, buttonAction: (() -> Void)? = nil @@ -485,7 +486,7 @@ public class StoryStealthModeSheetScreen: ViewControllerComponentContainer { backwardDuration: backwardDuration, forwardDuration: forwardDuration, buttonAction: buttonAction - ), navigationBarAppearance: .none, theme: .dark) + ), navigationBarAppearance: .none, theme: forceDark ? .dark : .default) self.statusBar.statusBarStyle = .Ignore self.navigationPresentation = .flatModal diff --git a/submodules/TelegramUI/Sources/ChatController.swift b/submodules/TelegramUI/Sources/ChatController.swift index 32619bdc59..a31dc472f4 100644 --- a/submodules/TelegramUI/Sources/ChatController.swift +++ b/submodules/TelegramUI/Sources/ChatController.swift @@ -11930,10 +11930,10 @@ public final class ChatControllerImpl: TelegramBaseController, ChatController, G case .info: let context = self.context var replaceImpl: ((ViewController) -> Void)? - let controller = context.sharedContext.makePremiumDemoController(context: context, subject: .fasterDownload, action: { + let controller = context.sharedContext.makePremiumDemoController(context: context, subject: .fasterDownload, forceDark: false, action: { let controller = context.sharedContext.makePremiumIntroController(context: context, source: .fasterDownload, forceDark: false, dismissed: nil) replaceImpl?(controller) - }) + }, dismissed: nil) replaceImpl = { [weak controller] c in controller?.replace(with: c) } diff --git a/submodules/TelegramUI/Sources/ChatInterfaceStateContextMenus.swift b/submodules/TelegramUI/Sources/ChatInterfaceStateContextMenus.swift index 4a559c44a4..2b8c603fc7 100644 --- a/submodules/TelegramUI/Sources/ChatInterfaceStateContextMenus.swift +++ b/submodules/TelegramUI/Sources/ChatInterfaceStateContextMenus.swift @@ -591,10 +591,10 @@ func contextMenuForChatPresentationInterfaceState(chatPresentationInterfaceState }, iconSource: nil, action: { c, _ in c.dismiss(completion: { var replaceImpl: ((ViewController) -> Void)? - let controller = context.sharedContext.makePremiumDemoController(context: context, subject: .noAds, action: { + let controller = context.sharedContext.makePremiumDemoController(context: context, subject: .noAds, forceDark: false, action: { let controller = context.sharedContext.makePremiumIntroController(context: context, source: .ads, forceDark: false, dismissed: nil) replaceImpl?(controller) - }) + }, dismissed: nil) replaceImpl = { [weak controller] c in controller?.replace(with: c) } @@ -1088,10 +1088,10 @@ func contextMenuForChatPresentationInterfaceState(chatPresentationInterfaceState }, action: { _, f in let context = context var replaceImpl: ((ViewController) -> Void)? - let controller = context.sharedContext.makePremiumDemoController(context: context, subject: .fasterDownload, action: { + let controller = context.sharedContext.makePremiumDemoController(context: context, subject: .fasterDownload, forceDark: false, action: { let controller = context.sharedContext.makePremiumIntroController(context: context, source: .fasterDownload, forceDark: false, dismissed: nil) replaceImpl?(controller) - }) + }, dismissed: nil) replaceImpl = { [weak controller] c in controller?.replace(with: c) } diff --git a/submodules/TelegramUI/Sources/SharedAccountContext.swift b/submodules/TelegramUI/Sources/SharedAccountContext.swift index ff9467d3dd..8315e7e9a6 100644 --- a/submodules/TelegramUI/Sources/SharedAccountContext.swift +++ b/submodules/TelegramUI/Sources/SharedAccountContext.swift @@ -2062,7 +2062,9 @@ public final class SharedAccountContextImpl: SharedAccountContext { return controller } - public func makePremiumDemoController(context: AccountContext, subject: PremiumDemoSubject, action: @escaping () -> Void) -> ViewController { + public func makePremiumDemoController(context: AccountContext, subject: PremiumDemoSubject, forceDark: Bool, action: @escaping () -> Void, dismissed: (() -> Void)?) -> ViewController { + let presentationData = context.sharedContext.currentPresentationData.with { $0 } + var buttonText: String = presentationData.strings.Common_OK let mappedSubject: PremiumDemoScreen.Subject switch subject { case .doubleLimits: @@ -2095,6 +2097,7 @@ public final class SharedAccountContextImpl: SharedAccountContext { mappedSubject = .translation case .stories: mappedSubject = .stories + buttonText = presentationData.strings.Story_PremiumUpgradeStoriesButton case .colors: mappedSubject = .colors case .wallpapers: @@ -2107,10 +2110,24 @@ public final class SharedAccountContextImpl: SharedAccountContext { mappedSubject = .messagePrivacy case .folderTags: mappedSubject = .folderTags + case .business: + mappedSubject = .business + buttonText = presentationData.strings.Chat_EmptyStateIntroFooterPremiumActionButton default: mappedSubject = .doubleLimits } - return PremiumDemoScreen(context: context, subject: mappedSubject, action: action) + + switch mappedSubject { + case .stories, .business, .doubleLimits: + let controller = PremiumLimitsListScreen(context: context, subject: mappedSubject, source: .other, order: [mappedSubject.perk], buttonText: buttonText, isPremium: false, forceDark: forceDark) + controller.action = action + if let dismissed { + controller.disposed = dismissed + } + return controller + default: + return PremiumDemoScreen(context: context, subject: mappedSubject, action: action) + } } public func makePremiumLimitController(context: AccountContext, subject: PremiumLimitSubject, count: Int32, forceDark: Bool, cancel: @escaping () -> Void, action: @escaping () -> Bool) -> ViewController {