From 53aa40353a3667c66a1cbef70ec30c7445a7dfeb Mon Sep 17 00:00:00 2001 From: Ilya Laktyushin Date: Thu, 22 Jun 2023 05:01:19 +0400 Subject: [PATCH] Stories --- .../Sources/AccountContext.swift | 2 + .../Sources/ChatListController.swift | 17 +- .../Sources/ChatListControllerNode.swift | 7 +- .../Sources/ContactsControllerNode.swift | 18 + .../PremiumUI/Sources/PremiumDemoScreen.swift | 26 +- .../PremiumUI/Sources/PremiumGiftScreen.swift | 2 + .../Sources/PremiumIntroScreen.swift | 353 ++++++++++-------- .../Sources/MediaEditorScreen.swift | 30 +- .../Sources/SharedAccountContext.swift | 4 + 9 files changed, 282 insertions(+), 177 deletions(-) diff --git a/submodules/AccountContext/Sources/AccountContext.swift b/submodules/AccountContext/Sources/AccountContext.swift index 8c21c05b03..f57cd9b3f5 100644 --- a/submodules/AccountContext/Sources/AccountContext.swift +++ b/submodules/AccountContext/Sources/AccountContext.swift @@ -939,6 +939,7 @@ public enum PremiumIntroSource { case voiceToText case fasterDownload case translation + case stories } public enum PremiumDemoSubject { @@ -956,6 +957,7 @@ public enum PremiumDemoSubject { case animatedEmoji case emojiStatus case translation + case stories } public enum PremiumLimitSubject { diff --git a/submodules/ChatListUI/Sources/ChatListController.swift b/submodules/ChatListUI/Sources/ChatListController.swift index b27ec858db..6059deefc2 100644 --- a/submodules/ChatListUI/Sources/ChatListController.swift +++ b/submodules/ChatListUI/Sources/ChatListController.swift @@ -158,7 +158,7 @@ public class ChatListControllerImpl: TelegramBaseController, ChatListController private var activeDownloadsDisposable: Disposable? private var clearUnseenDownloadsTimer: SwiftSignalKit.Timer? - private var isPremium: Bool = false + private(set) var isPremium: Bool = false private var didSetupTabs = false @@ -2341,6 +2341,19 @@ public class ChatListControllerImpl: TelegramBaseController, ChatListController } fileprivate func openStoryCamera() { + guard self.isPremium else { + let context = self.context + var replaceImpl: ((ViewController) -> Void)? + let controller = context.sharedContext.makePremiumDemoController(context: self.context, subject: .stories, action: { + let controller = context.sharedContext.makePremiumIntroController(context: context, source: .stories) + replaceImpl?(controller) + }) + replaceImpl = { [weak controller] c in + controller?.replace(with: c) + } + self.push(controller) + return + } var cameraTransitionIn: StoryCameraTransitionIn? if let componentView = self.chatListHeaderView() { if let (transitionView, _) = componentView.storyPeerListView()?.transitionViewForItem(peerId: self.context.account.peerId) { @@ -2501,7 +2514,7 @@ public class ChatListControllerImpl: TelegramBaseController, ChatListController }))) let isMuted = notificationSettings.storiesMuted == true - items.append(.action(ContextMenuActionItem(text: isMuted ? "Unmute" : "Mute", icon: { theme in + items.append(.action(ContextMenuActionItem(text: isMuted ? "Notify" : "Not Notify", icon: { theme in return generateTintedImage(image: UIImage(bundleImageName: isMuted ? "Chat/Context Menu/Muted": "Chat/Context Menu/Unmute"), color: theme.contextMenu.primaryColor) }, action: { [weak self] _, f in f(.default) diff --git a/submodules/ChatListUI/Sources/ChatListControllerNode.swift b/submodules/ChatListUI/Sources/ChatListControllerNode.swift index 7c2950ded0..63e90da60b 100644 --- a/submodules/ChatListUI/Sources/ChatListControllerNode.swift +++ b/submodules/ChatListUI/Sources/ChatListControllerNode.swift @@ -1191,7 +1191,7 @@ public final class ChatListContainerNode: ASDisplayNode, UIGestureRecognizerDele return bandingStart + (1.0 - (1.0 / ((bandedOffset * coefficient / range) + 1.0))) * range } - if case .compact = layout.metrics.widthClass { + if case .compact = layout.metrics.widthClass, self.controller?.isPremium == true { let cameraIsAlreadyOpened = self.controller?.hasStoryCameraTransition ?? false if selectedIndex <= 0 && translation.x > 0.0 { transitionFraction = 0.0 @@ -1204,6 +1204,11 @@ public final class ChatListContainerNode: ASDisplayNode, UIGestureRecognizerDele transitionFraction = 0.0 return } + } else { + if selectedIndex <= 0 && translation.x > 0.0 { + let overscroll = translation.x + transitionFraction = rubberBandingOffset(offset: overscroll, bandingStart: 0.0) / layout.size.width + } } if selectedIndex >= maxFilterIndex && translation.x < 0.0 { diff --git a/submodules/ContactListUI/Sources/ContactsControllerNode.swift b/submodules/ContactListUI/Sources/ContactsControllerNode.swift index 4aeb14a6de..c39e246b29 100644 --- a/submodules/ContactListUI/Sources/ContactsControllerNode.swift +++ b/submodules/ContactListUI/Sources/ContactsControllerNode.swift @@ -68,6 +68,9 @@ final class ContactsControllerNode: ASDisplayNode, UIGestureRecognizerDelegate { private var presentationDataDisposable: Disposable? private let stringsPromise = Promise() + private var isPremium = false + private var isPremiumDisposable: Disposable? + weak var controller: ContactsController? private var initialScrollingOffset: CGFloat? @@ -258,11 +261,22 @@ final class ContactsControllerNode: ASDisplayNode, UIGestureRecognizerDelegate { self.storiesReady.set(.single(true)) }) + + self.isPremiumDisposable = (self.context.engine.data.subscribe(TelegramEngine.EngineData.Item.Peer.Peer(id: self.context.account.peerId)) + |> map { + return $0?.isPremium ?? false + } + |> deliverOnMainQueue).start(next: { [weak self] isPremium in + if let self { + self.isPremium = isPremium + } + }) } deinit { self.presentationDataDisposable?.dispose() self.storySubscriptionsDisposable?.dispose() + self.isPremiumDisposable?.dispose() } override func didLoad() { @@ -293,6 +307,10 @@ final class ContactsControllerNode: ASDisplayNode, UIGestureRecognizerDelegate { return false } + override func gestureRecognizerShouldBegin(_ gestureRecognizer: UIGestureRecognizer) -> Bool { + return self.isPremium + } + private func updateThemeAndStrings() { self.backgroundColor = self.presentationData.theme.chatList.backgroundColor self.searchDisplayController?.updatePresentationData(self.presentationData) diff --git a/submodules/PremiumUI/Sources/PremiumDemoScreen.swift b/submodules/PremiumUI/Sources/PremiumDemoScreen.swift index 2ef5236446..e153a645b6 100644 --- a/submodules/PremiumUI/Sources/PremiumDemoScreen.swift +++ b/submodules/PremiumUI/Sources/PremiumDemoScreen.swift @@ -471,7 +471,7 @@ private final class DemoSheetContent: CombinedComponent { self.context = context self.subject = subject self.source = source - self.order = order ?? [.moreUpload, .fasterDownload, .voiceToText, .noAds, .uniqueReactions, .premiumStickers, .animatedEmoji, .advancedChatManagement, .profileBadge, .animatedUserpics, .appIcons, .translation] + self.order = order ?? [.moreUpload, .fasterDownload, .voiceToText, .noAds, .uniqueReactions, .premiumStickers, .animatedEmoji, .advancedChatManagement, .profileBadge, .animatedUserpics, .appIcons, .translation, .stories] self.action = action self.dismiss = dismiss } @@ -939,6 +939,25 @@ private final class DemoSheetContent: CombinedComponent { ) ) ) + //TODO:localize + availableItems[.stories] = DemoPagerComponent.Item( + AnyComponentWithIdentity( + id: PremiumDemoScreen.Subject.stories, + component: AnyComponent( + PageComponent( + content: AnyComponent(PhoneDemoComponent( + context: component.context, + position: .top, + videoFile: configuration.videos["voice_to_text"], + decoration: .badgeStars + )), + title: "Story Posting", + text: "Be one of the first to share your stories with your contacts or an unlimited audience.", + textColor: textColor + ) + ) + ) + ) var items: [DemoPagerComponent.Item] = component.order.compactMap { availableItems[$0] } let index: Int @@ -1029,6 +1048,10 @@ private final class DemoSheetContent: CombinedComponent { buttonAnimationName = "premium_unlock" case .translation: buttonText = strings.Premium_Translation_Proceed + case .stories: + //TODO:localize + buttonText = "Unlock Story Posting" + buttonAnimationName = "premium_unlock" default: buttonText = strings.Common_OK } @@ -1210,6 +1233,7 @@ public class PremiumDemoScreen: ViewControllerComponentContainer { case animatedEmoji case emojiStatus case translation + case stories } public enum Source: Equatable { diff --git a/submodules/PremiumUI/Sources/PremiumGiftScreen.swift b/submodules/PremiumUI/Sources/PremiumGiftScreen.swift index 283828e28b..79c67b5d25 100644 --- a/submodules/PremiumUI/Sources/PremiumGiftScreen.swift +++ b/submodules/PremiumUI/Sources/PremiumGiftScreen.swift @@ -397,6 +397,8 @@ private final class PremiumGiftScreenContentComponent: CombinedComponent { demoSubject = .emojiStatus case .translation: demoSubject = .translation + case .stories: + demoSubject = .stories } let buttonText: String diff --git a/submodules/PremiumUI/Sources/PremiumIntroScreen.swift b/submodules/PremiumUI/Sources/PremiumIntroScreen.swift index 15bc133974..f18d56967b 100644 --- a/submodules/PremiumUI/Sources/PremiumIntroScreen.swift +++ b/submodules/PremiumUI/Sources/PremiumIntroScreen.swift @@ -184,6 +184,12 @@ public enum PremiumSource: Equatable { } else { return false } + case .stories: + if case .stories = rhs { + return true + } else { + return false + } } } @@ -213,63 +219,66 @@ public enum PremiumSource: Equatable { case translation case linksPerSharedFolder case membershipInSharedFolders + case stories var identifier: String? { switch self { - case .settings: - return "settings" - case .stickers: - return "premium_stickers" - case .reactions: - return "infinite_reactions" - case .ads: - return "no_ads" - case .upload: - return "more_upload" - case .appIcons: - return "app_icons" - case .groupsAndChannels: - return "double_limits__channels" - case .pinnedChats: - return "double_limits__dialog_pinned" - case .publicLinks: - return "double_limits__channels_public" - case .savedGifs: - return "double_limits__saved_gifs" - case .savedStickers: - return "double_limits__stickers_faved" - case .folders: - return "double_limits__dialog_filters" - case .chatsPerFolder: - return "double_limits__dialog_filters_chats" - case .accounts: - return "double_limits__accounts" - case .about: - return "double_limits__about" - case .animatedEmoji: - return "animated_emoji" - case let .profile(id): - return "profile__\(id.id._internalGetInt64Value())" - case .emojiStatus: - return "emoji_status" - case .voiceToText: - return "voice_to_text" - case .fasterDownload: - return "faster_download" - case .gift, .giftTerms: - return nil - case let .deeplink(reference): - if let reference = reference { - return "deeplink_\(reference)" - } else { - return "deeplink" - } - case .translation: - return "translations" - case .linksPerSharedFolder: - return "double_limits__community_invites" - case .membershipInSharedFolders: - return "double_limits__communities_joined" + case .settings: + return "settings" + case .stickers: + return "premium_stickers" + case .reactions: + return "infinite_reactions" + case .ads: + return "no_ads" + case .upload: + return "more_upload" + case .appIcons: + return "app_icons" + case .groupsAndChannels: + return "double_limits__channels" + case .pinnedChats: + return "double_limits__dialog_pinned" + case .publicLinks: + return "double_limits__channels_public" + case .savedGifs: + return "double_limits__saved_gifs" + case .savedStickers: + return "double_limits__stickers_faved" + case .folders: + return "double_limits__dialog_filters" + case .chatsPerFolder: + return "double_limits__dialog_filters_chats" + case .accounts: + return "double_limits__accounts" + case .about: + return "double_limits__about" + case .animatedEmoji: + return "animated_emoji" + case let .profile(id): + return "profile__\(id.id._internalGetInt64Value())" + case .emojiStatus: + return "emoji_status" + case .voiceToText: + return "voice_to_text" + case .fasterDownload: + return "faster_download" + case .gift, .giftTerms: + return nil + case let .deeplink(reference): + if let reference = reference { + return "deeplink_\(reference)" + } else { + return "deeplink" + } + case .translation: + return "translations" + case .linksPerSharedFolder: + return "double_limits__community_invites" + case .membershipInSharedFolders: + return "double_limits__communities_joined" + case .stories: + return "stories" } } } @@ -289,6 +298,7 @@ enum PremiumPerk: CaseIterable { case animatedEmoji case emojiStatus case translation + case stories static var allCases: [PremiumPerk] { return [ @@ -321,133 +331,142 @@ enum PremiumPerk: CaseIterable { var identifier: String { switch self { - case .doubleLimits: - return "double_limits" - case .moreUpload: - return "more_upload" - case .fasterDownload: - return "faster_download" - case .voiceToText: - return "voice_to_text" - case .noAds: - return "no_ads" - case .uniqueReactions: - return "infinite_reactions" - case .premiumStickers: - return "premium_stickers" - case .advancedChatManagement: - return "advanced_chat_management" - case .profileBadge: - return "profile_badge" - case .animatedUserpics: - return "animated_userpics" - case .appIcons: - return "app_icons" - case .animatedEmoji: - return "animated_emoji" - case .emojiStatus: - return "emoji_status" - case .translation: - return "translations" + case .doubleLimits: + return "double_limits" + case .moreUpload: + return "more_upload" + case .fasterDownload: + return "faster_download" + case .voiceToText: + return "voice_to_text" + case .noAds: + return "no_ads" + case .uniqueReactions: + return "infinite_reactions" + case .premiumStickers: + return "premium_stickers" + case .advancedChatManagement: + return "advanced_chat_management" + case .profileBadge: + return "profile_badge" + case .animatedUserpics: + return "animated_userpics" + case .appIcons: + return "app_icons" + case .animatedEmoji: + return "animated_emoji" + case .emojiStatus: + return "emoji_status" + case .translation: + return "translations" + case .stories: + return "stories" } } func title(strings: PresentationStrings) -> String { switch self { - case .doubleLimits: - return strings.Premium_DoubledLimits - case .moreUpload: - return strings.Premium_UploadSize - case .fasterDownload: - return strings.Premium_FasterSpeed - case .voiceToText: - return strings.Premium_VoiceToText - case .noAds: - return strings.Premium_NoAds - case .uniqueReactions: - return strings.Premium_InfiniteReactions - case .premiumStickers: - return strings.Premium_Stickers - case .advancedChatManagement: - return strings.Premium_ChatManagement - case .profileBadge: - return strings.Premium_Badge - case .animatedUserpics: - return strings.Premium_Avatar - case .appIcons: - return strings.Premium_AppIcon - case .animatedEmoji: - return strings.Premium_AnimatedEmoji - case .emojiStatus: - return strings.Premium_EmojiStatus - case .translation: - return strings.Premium_Translation + case .doubleLimits: + return strings.Premium_DoubledLimits + case .moreUpload: + return strings.Premium_UploadSize + case .fasterDownload: + return strings.Premium_FasterSpeed + case .voiceToText: + return strings.Premium_VoiceToText + case .noAds: + return strings.Premium_NoAds + case .uniqueReactions: + return strings.Premium_InfiniteReactions + case .premiumStickers: + return strings.Premium_Stickers + case .advancedChatManagement: + return strings.Premium_ChatManagement + case .profileBadge: + return strings.Premium_Badge + case .animatedUserpics: + return strings.Premium_Avatar + case .appIcons: + return strings.Premium_AppIcon + case .animatedEmoji: + return strings.Premium_AnimatedEmoji + case .emojiStatus: + return strings.Premium_EmojiStatus + case .translation: + return strings.Premium_Translation + case .stories: + //TODO:localize + return "Story Posting" } } func subtitle(strings: PresentationStrings) -> String { switch self { - case .doubleLimits: - return strings.Premium_DoubledLimitsInfo - case .moreUpload: - return strings.Premium_UploadSizeInfo - case .fasterDownload: - return strings.Premium_FasterSpeedInfo - case .voiceToText: - return strings.Premium_VoiceToTextInfo - case .noAds: - return strings.Premium_NoAdsInfo - case .uniqueReactions: - return strings.Premium_InfiniteReactionsInfo - case .premiumStickers: - return strings.Premium_StickersInfo - case .advancedChatManagement: - return strings.Premium_ChatManagementInfo - case .profileBadge: - return strings.Premium_BadgeInfo - case .animatedUserpics: - return strings.Premium_AvatarInfo - case .appIcons: - return strings.Premium_AppIconInfo - case .animatedEmoji: - return strings.Premium_AnimatedEmojiInfo - case .emojiStatus: - return strings.Premium_EmojiStatusInfo - case .translation: - return strings.Premium_TranslationInfo + case .doubleLimits: + return strings.Premium_DoubledLimitsInfo + case .moreUpload: + return strings.Premium_UploadSizeInfo + case .fasterDownload: + return strings.Premium_FasterSpeedInfo + case .voiceToText: + return strings.Premium_VoiceToTextInfo + case .noAds: + return strings.Premium_NoAdsInfo + case .uniqueReactions: + return strings.Premium_InfiniteReactionsInfo + case .premiumStickers: + return strings.Premium_StickersInfo + case .advancedChatManagement: + return strings.Premium_ChatManagementInfo + case .profileBadge: + return strings.Premium_BadgeInfo + case .animatedUserpics: + return strings.Premium_AvatarInfo + case .appIcons: + return strings.Premium_AppIconInfo + case .animatedEmoji: + return strings.Premium_AnimatedEmojiInfo + case .emojiStatus: + return strings.Premium_EmojiStatusInfo + case .translation: + return strings.Premium_TranslationInfo + case .stories: + return "Be one of the first to share your stories with your contacts or an unlimited audience." } } var iconName: String { switch self { - case .doubleLimits: - return "Premium/Perk/Limits" - case .moreUpload: - return "Premium/Perk/Upload" - case .fasterDownload: - return "Premium/Perk/Speed" - case .voiceToText: - return "Premium/Perk/Voice" - case .noAds: - return "Premium/Perk/NoAds" - case .uniqueReactions: - return "Premium/Perk/Reactions" - case .premiumStickers: - return "Premium/Perk/Stickers" - case .advancedChatManagement: - return "Premium/Perk/Chat" - case .profileBadge: - return "Premium/Perk/Badge" - case .animatedUserpics: - return "Premium/Perk/Avatar" - case .appIcons: - return "Premium/Perk/AppIcon" - case .animatedEmoji: - return "Premium/Perk/Emoji" - case .emojiStatus: - return "Premium/Perk/Status" - case .translation: - return "Premium/Perk/Translation" + case .doubleLimits: + return "Premium/Perk/Limits" + case .moreUpload: + return "Premium/Perk/Upload" + case .fasterDownload: + return "Premium/Perk/Speed" + case .voiceToText: + return "Premium/Perk/Voice" + case .noAds: + return "Premium/Perk/NoAds" + case .uniqueReactions: + return "Premium/Perk/Reactions" + case .premiumStickers: + return "Premium/Perk/Stickers" + case .advancedChatManagement: + return "Premium/Perk/Chat" + case .profileBadge: + return "Premium/Perk/Badge" + case .animatedUserpics: + return "Premium/Perk/Avatar" + case .appIcons: + return "Premium/Perk/AppIcon" + case .animatedEmoji: + return "Premium/Perk/Emoji" + case .emojiStatus: + return "Premium/Perk/Status" + case .translation: + return "Premium/Perk/Translation" + case .stories: + return "Premium/Perk/Translation" } } } @@ -1714,6 +1733,8 @@ private final class PremiumIntroScreenContentComponent: CombinedComponent { demoSubject = .emojiStatus case .translation: demoSubject = .translation + case .stories: + demoSubject = .stories } let isPremium = state?.isPremium == true diff --git a/submodules/TelegramUI/Components/MediaEditorScreen/Sources/MediaEditorScreen.swift b/submodules/TelegramUI/Components/MediaEditorScreen/Sources/MediaEditorScreen.swift index 963c6cf81a..0d1140af3c 100644 --- a/submodules/TelegramUI/Components/MediaEditorScreen/Sources/MediaEditorScreen.swift +++ b/submodules/TelegramUI/Components/MediaEditorScreen/Sources/MediaEditorScreen.swift @@ -888,7 +888,7 @@ final class MediaEditorScreenComponent: Component { timeoutSelected = false var inputPanelAvailableWidth = previewSize.width - var inputPanelAvailableHeight = 115.0 + var inputPanelAvailableHeight = 103.0 if case .regular = environment.metrics.widthClass { if (self.inputPanelExternalState.isEditing || self.inputPanelExternalState.hasText) { inputPanelAvailableWidth += 200.0 @@ -1547,6 +1547,7 @@ public final class MediaEditorScreen: ViewController, UIDropInteractionDelegate private var isDismissing = false private var dismissOffset: CGFloat = 0.0 private var isDismissed = false + private var isDismissBySwipeSuppressed = false private var presentationData: PresentationData private var validLayout: ContainerViewLayout? @@ -1955,6 +1956,7 @@ public final class MediaEditorScreen: ViewController, UIDropInteractionDelegate if abs(translation.y) > 10.0 && !self.isEnhancing && hasSwipeToDismiss { if !self.isDismissing { self.isDismissing = true + self.isDismissBySwipeSuppressed = controller.isEligibleForDraft() controller.requestLayout(transition: .animated(duration: 0.25, curve: .easeInOut)) } } else if abs(translation.x) > 10.0 && !self.isDismissing { @@ -1965,6 +1967,12 @@ public final class MediaEditorScreen: ViewController, UIDropInteractionDelegate if self.isDismissing { self.dismissOffset = translation.y controller.requestLayout(transition: .immediate) + + if abs(self.dismissOffset) > 20.0, controller.isEligibleForDraft() { + gestureRecognizer.isEnabled = false + gestureRecognizer.isEnabled = true + controller.maybePresentDiscardAlert() + } } else if self.isEnhancing { if let mediaEditor = self.mediaEditor { let value = mediaEditor.getToolValue(.enhance) as? Float ?? 0.0 @@ -1977,7 +1985,7 @@ public final class MediaEditorScreen: ViewController, UIDropInteractionDelegate } case .ended, .cancelled: if self.isDismissing { - if abs(translation.y) > self.view.frame.height * 0.33 || abs(velocity.y) > 1000.0 { + if abs(translation.y) > self.view.frame.height * 0.33 || abs(velocity.y) > 1000.0, !controller.isEligibleForDraft() { controller.requestDismiss(saveDraft: false, animated: true) } else { self.dismissOffset = 0.0 @@ -2532,7 +2540,7 @@ public final class MediaEditorScreen: ViewController, UIDropInteractionDelegate isInteractingWithEntities: self.isInteractingWithEntities, isSavingAvailable: controller.isSavingAvailable, hasAppeared: self.hasAppeared, - isDismissing: self.isDismissing, + isDismissing: self.isDismissing && !self.isDismissBySwipeSuppressed, bottomSafeInset: layout.intrinsicInsets.bottom, mediaEditor: self.mediaEditor, privacy: controller.state.privacy, @@ -2697,7 +2705,7 @@ public final class MediaEditorScreen: ViewController, UIDropInteractionDelegate } transition.setFrame(view: self.backgroundDimView, frame: CGRect(origin: .zero, size: layout.size)) - transition.setAlpha(view: self.backgroundDimView, alpha: self.isDismissing ? 0.0 : 1.0) + transition.setAlpha(view: self.backgroundDimView, alpha: self.isDismissing && !self.isDismissBySwipeSuppressed ? 0.0 : 1.0) var bottomInputOffset: CGFloat = 0.0 if inputHeight > 0.0 { @@ -3042,19 +3050,27 @@ public final class MediaEditorScreen: ViewController, UIDropInteractionDelegate self.present(controller, in: .current) } - func maybePresentDiscardAlert() { + func isEligibleForDraft() -> Bool { guard let mediaEditor = self.node.mediaEditor else { - return + return false } let entities = self.node.entitiesView.entities.filter { !($0 is DrawingMediaEntity) } let codableEntities = DrawingEntitiesView.encodeEntities(entities, entitiesView: self.node.entitiesView) mediaEditor.setDrawingAndEntities(data: nil, image: mediaEditor.values.drawing, entities: codableEntities) - self.hapticFeedback.impact(.light) if let subject = self.node.subject, case .asset = subject, self.node.mediaEditor?.values.hasChanges == false { + return false + } + return true + } + + func maybePresentDiscardAlert() { + self.hapticFeedback.impact(.light) + if !self.isEligibleForDraft() { self.requestDismiss(saveDraft: false, animated: true) return } + let title: String let save: String if case .draft = self.node.subject { diff --git a/submodules/TelegramUI/Sources/SharedAccountContext.swift b/submodules/TelegramUI/Sources/SharedAccountContext.swift index e502b41523..481470f812 100644 --- a/submodules/TelegramUI/Sources/SharedAccountContext.swift +++ b/submodules/TelegramUI/Sources/SharedAccountContext.swift @@ -1768,6 +1768,8 @@ public final class SharedAccountContextImpl: SharedAccountContext { mappedSource = .fasterDownload case .translation: mappedSource = .translation + case .stories: + mappedSource = .stories } return PremiumIntroScreen(context: context, source: mappedSource) } @@ -1803,6 +1805,8 @@ public final class SharedAccountContextImpl: SharedAccountContext { mappedSubject = .emojiStatus case .translation: mappedSubject = .translation + case .stories: + mappedSubject = .stories } return PremiumDemoScreen(context: context, subject: mappedSubject, action: action) }