diff --git a/submodules/AccountContext/Sources/AccountContext.swift b/submodules/AccountContext/Sources/AccountContext.swift index 870e25d981..fa7aec6e2b 100644 --- a/submodules/AccountContext/Sources/AccountContext.swift +++ b/submodules/AccountContext/Sources/AccountContext.swift @@ -793,6 +793,7 @@ public protocol SharedAccountContext: AnyObject { func makeChatQrCodeScreen(context: AccountContext, peer: Peer, threadId: Int64?) -> ViewController func makePremiumIntroController(context: AccountContext, source: PremiumIntroSource) -> ViewController + func makePremiumDemoController(context: AccountContext, subject: PremiumDemoSubject, action: @escaping () -> Void) -> ViewController func makeStickerPackScreen(context: AccountContext, updatedPresentationData: (initial: PresentationData, signal: Signal)?, mainStickerPack: StickerPackReference, stickerPacks: [StickerPackReference], loadedStickerPacks: [LoadedStickerPack], parentNavigationController: NavigationController?, sendSticker: ((FileMediaReference, UIView, CGRect) -> Bool)?) -> ViewController @@ -832,6 +833,22 @@ public enum PremiumIntroSource { case fasterDownload } +public enum PremiumDemoSubject { + case doubleLimits + case moreUpload + case fasterDownload + case voiceToText + case noAds + case uniqueReactions + case premiumStickers + case advancedChatManagement + case profileBadge + case animatedUserpics + case appIcons + case animatedEmoji + case emojiStatus +} + public protocol ComposeController: ViewController { } diff --git a/submodules/PremiumUI/Sources/PremiumLimitScreen.swift b/submodules/PremiumUI/Sources/PremiumLimitScreen.swift index 39abd2487e..fadd9ff184 100644 --- a/submodules/PremiumUI/Sources/PremiumLimitScreen.swift +++ b/submodules/PremiumUI/Sources/PremiumLimitScreen.swift @@ -941,7 +941,11 @@ private final class LimitSheetContent: CombinedComponent { contentSize = CGSize(width: context.availableSize.width, height: buttonFrame.maxY + 5.0 + environment.safeInsets.bottom) } else { - contentSize = CGSize(width: context.availableSize.width, height: 351.0 + environment.safeInsets.bottom) + var height: CGFloat = 351.0 + if isPremiumDisabled { + height -= 78.0 + } + contentSize = CGSize(width: context.availableSize.width, height: height + environment.safeInsets.bottom) } return contentSize diff --git a/submodules/TelegramUI/Sources/ChatMessageBubbleItemNode.swift b/submodules/TelegramUI/Sources/ChatMessageBubbleItemNode.swift index dbe19b64e6..e11b2da899 100644 --- a/submodules/TelegramUI/Sources/ChatMessageBubbleItemNode.swift +++ b/submodules/TelegramUI/Sources/ChatMessageBubbleItemNode.swift @@ -2822,7 +2822,7 @@ class ChatMessageBubbleItemNode: ChatMessageItemView, ChatMessagePreviewItemNode } } let previousReplyInfoNodeFrame = replyInfoNode.frame - replyInfoNode.frame = CGRect(origin: CGPoint(x: contentOrigin.x + layoutConstants.text.bubbleInsets.left, y: layoutConstants.bubble.contentInsets.top + replyInfoOriginY), size: replyInfoSizeApply.0) + replyInfoNode.frame = CGRect(origin: CGPoint(x: contentOrigin.x + layoutConstants.text.bubbleInsets.left, y: layoutConstants.bubble.contentInsets.top + replyInfoOriginY), size: CGSize(width: backgroundFrame.width - layoutConstants.text.bubbleInsets.left - layoutConstants.text.bubbleInsets.right, height: replyInfoSizeApply.0.height)) if case let .System(duration, _) = animation { if animateFrame { replyInfoNode.layer.animateFrame(from: previousReplyInfoNodeFrame, to: replyInfoNode.frame, duration: duration, timingFunction: timingFunction) diff --git a/submodules/TelegramUI/Sources/ChatMessageInteractiveFileNode.swift b/submodules/TelegramUI/Sources/ChatMessageInteractiveFileNode.swift index 4e86f3447a..55078a1eb3 100644 --- a/submodules/TelegramUI/Sources/ChatMessageInteractiveFileNode.swift +++ b/submodules/TelegramUI/Sources/ChatMessageInteractiveFileNode.swift @@ -224,6 +224,8 @@ final class ChatMessageInteractiveFileNode: ASDisplayNode { } private var isWaitingForCollapse: Bool = false + private var hapticFeedback: HapticFeedback? + override init() { self.titleNode = TextNode() self.titleNode.displaysAsynchronously = false @@ -355,11 +357,23 @@ final class ChatMessageInteractiveFileNode: ASDisplayNode { } guard arguments.associatedData.isPremium else { + if self.hapticFeedback == nil { + self.hapticFeedback = HapticFeedback() + } + self.hapticFeedback?.impact(.medium) + let presentationData = context.sharedContext.currentPresentationData.with { $0 } 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), elevatedLayout: false, position: .top, animateInAsReplacement: false, action: { action in if case .undo = action { - let introController = context.sharedContext.makePremiumIntroController(context: context, source: .settings) - arguments.controllerInteraction.navigationController()?.pushViewController(introController, animated: true) + var replaceImpl: ((ViewController) -> Void)? + let controller = context.sharedContext.makePremiumDemoController(context: context, subject: .voiceToText, action: { + let controller = context.sharedContext.makePremiumIntroController(context: context, source: .settings) + replaceImpl?(controller) + }) + replaceImpl = { [weak controller] c in + controller?.replace(with: c) + } + arguments.controllerInteraction.navigationController()?.pushViewController(controller, animated: true) let _ = ApplicationSpecificNotice.incrementAudioTranscriptionSuggestion(accountManager: context.sharedContext.accountManager).start() } diff --git a/submodules/TelegramUI/Sources/ChatMessageInteractiveInstantVideoNode.swift b/submodules/TelegramUI/Sources/ChatMessageInteractiveInstantVideoNode.swift index 40bfcab65c..a18c097ab0 100644 --- a/submodules/TelegramUI/Sources/ChatMessageInteractiveInstantVideoNode.swift +++ b/submodules/TelegramUI/Sources/ChatMessageInteractiveInstantVideoNode.swift @@ -141,6 +141,8 @@ class ChatMessageInteractiveInstantVideoNode: ASDisplayNode { } private var isWaitingForCollapse: Bool = false + private var hapticFeedback: HapticFeedback? + var requestUpdateLayout: (Bool) -> Void = { _ in } override init() { @@ -1486,11 +1488,24 @@ class ChatMessageInteractiveInstantVideoNode: ASDisplayNode { } guard item.associatedData.isPremium else { + if self.hapticFeedback == nil { + self.hapticFeedback = HapticFeedback() + } + self.hapticFeedback?.impact(.medium) + let presentationData = item.context.sharedContext.currentPresentationData.with { $0 } 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), elevatedLayout: false, position: .top, animateInAsReplacement: false, action: { action in if case .undo = action { - let introController = item.context.sharedContext.makePremiumIntroController(context: item.context, source: .settings) - item.controllerInteraction.navigationController()?.pushViewController(introController, animated: true) + let context = item.context + var replaceImpl: ((ViewController) -> Void)? + let controller = context.sharedContext.makePremiumDemoController(context: context, subject: .voiceToText, action: { + let controller = context.sharedContext.makePremiumIntroController(context: context, source: .settings) + replaceImpl?(controller) + }) + replaceImpl = { [weak controller] c in + controller?.replace(with: c) + } + item.controllerInteraction.navigationController()?.pushViewController(controller, animated: true) let _ = ApplicationSpecificNotice.incrementAudioTranscriptionSuggestion(accountManager: item.context.sharedContext.accountManager).start() } diff --git a/submodules/TelegramUI/Sources/SharedAccountContext.swift b/submodules/TelegramUI/Sources/SharedAccountContext.swift index e8efa5558a..0c96050740 100644 --- a/submodules/TelegramUI/Sources/SharedAccountContext.swift +++ b/submodules/TelegramUI/Sources/SharedAccountContext.swift @@ -1424,50 +1424,83 @@ public final class SharedAccountContextImpl: SharedAccountContext { public func makePremiumIntroController(context: AccountContext, source: PremiumIntroSource) -> ViewController { let mappedSource: PremiumSource switch source { - case .settings: - mappedSource = .settings - case .stickers: - mappedSource = .stickers - case .reactions: - mappedSource = .reactions - case .ads: - mappedSource = .ads - case .upload: - mappedSource = .upload - case .groupsAndChannels: - mappedSource = .groupsAndChannels - case .pinnedChats: - mappedSource = .pinnedChats - case .publicLinks: - mappedSource = .publicLinks - case .savedGifs: - mappedSource = .savedGifs - case .savedStickers: - mappedSource = .savedStickers - case .folders: - mappedSource = .folders - case .chatsPerFolder: - mappedSource = .chatsPerFolder - case .appIcons: - mappedSource = .appIcons - case .accounts: - mappedSource = .accounts - case .about: - mappedSource = .about - case let .deeplink(reference): - mappedSource = .deeplink(reference) - case let .profile(peerId): - mappedSource = .profile(peerId) - case let .emojiStatus(peerId, fileId, file, packTitle): - mappedSource = .emojiStatus(peerId, fileId, file, packTitle) - case .voiceToText: - mappedSource = .voiceToText - case .fasterDownload: - mappedSource = .fasterDownload + case .settings: + mappedSource = .settings + case .stickers: + mappedSource = .stickers + case .reactions: + mappedSource = .reactions + case .ads: + mappedSource = .ads + case .upload: + mappedSource = .upload + case .groupsAndChannels: + mappedSource = .groupsAndChannels + case .pinnedChats: + mappedSource = .pinnedChats + case .publicLinks: + mappedSource = .publicLinks + case .savedGifs: + mappedSource = .savedGifs + case .savedStickers: + mappedSource = .savedStickers + case .folders: + mappedSource = .folders + case .chatsPerFolder: + mappedSource = .chatsPerFolder + case .appIcons: + mappedSource = .appIcons + case .accounts: + mappedSource = .accounts + case .about: + mappedSource = .about + case let .deeplink(reference): + mappedSource = .deeplink(reference) + case let .profile(peerId): + mappedSource = .profile(peerId) + case let .emojiStatus(peerId, fileId, file, packTitle): + mappedSource = .emojiStatus(peerId, fileId, file, packTitle) + case .voiceToText: + mappedSource = .voiceToText + case .fasterDownload: + mappedSource = .fasterDownload } return PremiumIntroScreen(context: context, source: mappedSource) } + public func makePremiumDemoController(context: AccountContext, subject: PremiumDemoSubject, action: @escaping () -> Void) -> ViewController { + let mappedSubject: PremiumDemoScreen.Subject + switch subject { + case .doubleLimits: + mappedSubject = .doubleLimits + case .moreUpload: + mappedSubject = .moreUpload + case .fasterDownload: + mappedSubject = .fasterDownload + case .voiceToText: + mappedSubject = .voiceToText + case .noAds: + mappedSubject = .noAds + case .uniqueReactions: + mappedSubject = .uniqueReactions + case .premiumStickers: + mappedSubject = .premiumStickers + case .advancedChatManagement: + mappedSubject = .advancedChatManagement + case .profileBadge: + mappedSubject = .profileBadge + case .animatedUserpics: + mappedSubject = .animatedUserpics + case .appIcons: + mappedSubject = .appIcons + case .animatedEmoji: + mappedSubject = .animatedEmoji + case .emojiStatus: + mappedSubject = .emojiStatus + } + return PremiumDemoScreen(context: context, subject: mappedSubject, action: action) + } + public func makeStickerPackScreen(context: AccountContext, updatedPresentationData: (initial: PresentationData, signal: Signal)?, mainStickerPack: StickerPackReference, stickerPacks: [StickerPackReference], loadedStickerPacks: [LoadedStickerPack], parentNavigationController: NavigationController?, sendSticker: ((FileMediaReference, UIView, CGRect) -> Bool)?) -> ViewController { return StickerPackScreen(context: context, updatedPresentationData: updatedPresentationData, mainStickerPack: mainStickerPack, stickerPacks: stickerPacks, loadedStickerPacks: loadedStickerPacks, parentNavigationController: parentNavigationController, sendSticker: sendSticker) } diff --git a/submodules/TelegramUniversalVideoContent/Sources/YoutubeEmbedImplementation.swift b/submodules/TelegramUniversalVideoContent/Sources/YoutubeEmbedImplementation.swift index 2bd15c231a..d58ba3931b 100644 --- a/submodules/TelegramUniversalVideoContent/Sources/YoutubeEmbedImplementation.swift +++ b/submodules/TelegramUniversalVideoContent/Sources/YoutubeEmbedImplementation.swift @@ -287,7 +287,6 @@ final class YoutubeEmbedImplementation: WebEmbedImplementation { } let _ = download - let _ = failed if let position = position { if self.ignoreEarlierTimestamps { @@ -307,8 +306,14 @@ final class YoutubeEmbedImplementation: WebEmbedImplementation { } if let updateStatus = self.updateStatus, let playback = playback, let duration = duration { - let playbackStatus: MediaPlayerPlaybackStatus - switch playback { + if let failed, failed { + self.status = MediaPlayerStatus(generationTimestamp: self.status.generationTimestamp, duration: 0.0, dimensions: self.status.dimensions, timestamp: 0.0, baseRate: 0.0, seekId: self.status.seekId, status: .playing, soundEnabled: false) + updateStatus(self.status) + + self.onPlaybackStarted?() + } else { + let playbackStatus: MediaPlayerPlaybackStatus + switch playback { case 0: if newTimestamp > Double(duration) - 1.0 { self.isPlaying = false @@ -325,17 +330,18 @@ final class YoutubeEmbedImplementation: WebEmbedImplementation { playbackStatus = .buffering(initial: !self.started, whilePlaying: self.isPlaying, progress: 0.0, display: false) default: playbackStatus = .buffering(initial: true, whilePlaying: true, progress: 0.0, display: false) - } - - if case .playing = playbackStatus, !self.started { - self.started = true - print("YT started in \(CFAbsoluteTimeGetCurrent() - self.benchmarkStartTime)") + } - self.onPlaybackStarted?() + if case .playing = playbackStatus, !self.started { + self.started = true + print("YT started in \(CFAbsoluteTimeGetCurrent() - self.benchmarkStartTime)") + + self.onPlaybackStarted?() + } + + self.status = MediaPlayerStatus(generationTimestamp: self.status.generationTimestamp, duration: Double(duration), dimensions: self.status.dimensions, timestamp: newTimestamp, baseRate: self.status.baseRate, seekId: self.status.seekId, status: playbackStatus, soundEnabled: true) + updateStatus(self.status) } - - self.status = MediaPlayerStatus(generationTimestamp: self.status.generationTimestamp, duration: Double(duration), dimensions: self.status.dimensions, timestamp: newTimestamp, baseRate: self.status.baseRate, seekId: self.status.seekId, status: playbackStatus, soundEnabled: true) - updateStatus(self.status) } }