diff --git a/submodules/AccountContext/Sources/AccountContext.swift b/submodules/AccountContext/Sources/AccountContext.swift index 4bfb363aae..576a2010d4 100644 --- a/submodules/AccountContext/Sources/AccountContext.swift +++ b/submodules/AccountContext/Sources/AccountContext.swift @@ -905,7 +905,7 @@ public protocol SharedAccountContext: AnyObject { selectedMessages: Signal?, NoError>, mode: ChatHistoryListMode ) -> ChatHistoryListNode - func makeChatMessagePreviewItem(context: AccountContext, messages: [Message], theme: PresentationTheme, strings: PresentationStrings, wallpaper: TelegramWallpaper, fontSize: PresentationFontSize, chatBubbleCorners: PresentationChatBubbleCorners, dateTimeFormat: PresentationDateTimeFormat, nameOrder: PresentationPersonNameOrder, forcedResourceStatus: FileMediaResourceStatus?, tapMessage: ((Message) -> Void)?, clickThroughMessage: (() -> Void)?, backgroundNode: ASDisplayNode?, availableReactions: AvailableReactions?, accountPeer: Peer?, isCentered: Bool, isPreview: Bool) -> ListViewItem + func makeChatMessagePreviewItem(context: AccountContext, messages: [Message], theme: PresentationTheme, strings: PresentationStrings, wallpaper: TelegramWallpaper, fontSize: PresentationFontSize, chatBubbleCorners: PresentationChatBubbleCorners, dateTimeFormat: PresentationDateTimeFormat, nameOrder: PresentationPersonNameOrder, forcedResourceStatus: FileMediaResourceStatus?, tapMessage: ((Message) -> Void)?, clickThroughMessage: (() -> Void)?, backgroundNode: ASDisplayNode?, availableReactions: AvailableReactions?, accountPeer: Peer?, isCentered: Bool, isPreview: Bool, isStandalone: Bool) -> ListViewItem func makeChatMessageDateHeaderItem(context: AccountContext, timestamp: Int32, theme: PresentationTheme, strings: PresentationStrings, wallpaper: TelegramWallpaper, fontSize: PresentationFontSize, chatBubbleCorners: PresentationChatBubbleCorners, dateTimeFormat: PresentationDateTimeFormat, nameOrder: PresentationPersonNameOrder) -> ListViewItemHeader func makeChatMessageAvatarHeaderItem(context: AccountContext, timestamp: Int32, peer: Peer, message: Message, theme: PresentationTheme, strings: PresentationStrings, wallpaper: TelegramWallpaper, fontSize: PresentationFontSize, chatBubbleCorners: PresentationChatBubbleCorners, dateTimeFormat: PresentationDateTimeFormat, nameOrder: PresentationPersonNameOrder) -> ListViewItemHeader func makePeerSharedMediaController(context: AccountContext, peerId: PeerId) -> ViewController? @@ -952,6 +952,7 @@ public protocol SharedAccountContext: AnyObject { func makePremiumDemoController(context: AccountContext, subject: PremiumDemoSubject, action: @escaping () -> 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) -> ViewController + func makePremiumPrivacyControllerController(context: AccountContext, subject: PremiumPrivacySubject) -> ViewController func makeStickerPackScreen(context: AccountContext, updatedPresentationData: (initial: PresentationData, signal: Signal)?, mainStickerPack: StickerPackReference, stickerPacks: [StickerPackReference], loadedStickerPacks: [LoadedStickerPack], parentNavigationController: NavigationController?, sendSticker: ((FileMediaReference, UIView, CGRect) -> Bool)?) -> ViewController @@ -983,86 +984,6 @@ public protocol SharedAccountContext: AnyObject { func beginNewAuth(testingEnvironment: Bool) } -public enum PremiumIntroSource { - case settings - case stickers - case reactions - case ads - case upload - case groupsAndChannels - case pinnedChats - case publicLinks - case savedGifs - case savedStickers - case folders - case chatsPerFolder - case accounts - case appIcons - case about - case deeplink(String?) - case profile(PeerId) - case emojiStatus(PeerId, Int64, TelegramMediaFile?, LoadedStickerPack?) - case voiceToText - case fasterDownload - case translation - case stories - case storiesDownload - case storiesStealthMode - case storiesPermanentViews - case storiesFormatting - case storiesExpirationDurations - case storiesSuggestedReactions - case channelBoost(EnginePeer.Id) - case nameColor - case similarChannels - case wallpapers - case presence -} - -public enum PremiumGiftSource: Equatable { - case profile - case attachMenu - case settings - case chatList - case channelBoost - case deeplink(String?) -} - -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 - case translation - case stories - case colors - case wallpapers -} - -public enum PremiumLimitSubject { - case folders - case chatsPerFolder - case pins - case files - case accounts - case linksPerSharedFolder - case membershipInSharedFolders - case channels - case expiringStories - case storiesWeekly - case storiesMonthly - case storiesChannelBoost(peer: EnginePeer, isCurrent: Bool, level: Int32, currentLevelBoosts: Int32, nextLevelBoosts: Int32?, link: String?, myBoostCount: Int32, canBoostAgain: Bool) -} - public protocol ComposeController: ViewController { } @@ -1121,102 +1042,6 @@ public protocol AccountContext: AnyObject { func requestCall(peerId: PeerId, isVideo: Bool, completion: @escaping () -> Void) } -public struct PremiumConfiguration { - public static var defaultValue: PremiumConfiguration { - return PremiumConfiguration( - isPremiumDisabled: false, - showPremiumGiftInAttachMenu: false, - showPremiumGiftInTextField: false, - giveawayGiftsPurchaseAvailable: false, - boostsPerGiftCount: 3, - audioTransciptionTrialMaxDuration: 300, - audioTransciptionTrialCount: 2, - minChannelNameColorLevel: 1, - minChannelNameIconLevel: 4, - minChannelProfileColorLevel: 5, - minChannelProfileIconLevel: 7, - minChannelEmojiStatusLevel: 8, - minChannelWallpaperLevel: 9, - minChannelCustomWallpaperLevel: 10 - ) - } - - public let isPremiumDisabled: Bool - public let showPremiumGiftInAttachMenu: Bool - public let showPremiumGiftInTextField: Bool - public let giveawayGiftsPurchaseAvailable: Bool - public let boostsPerGiftCount: Int32 - public let audioTransciptionTrialMaxDuration: Int32 - public let audioTransciptionTrialCount: Int32 - public let minChannelNameColorLevel: Int32 - public let minChannelNameIconLevel: Int32 - public let minChannelProfileColorLevel: Int32 - public let minChannelProfileIconLevel: Int32 - public let minChannelEmojiStatusLevel: Int32 - public let minChannelWallpaperLevel: Int32 - public let minChannelCustomWallpaperLevel: Int32 - - fileprivate init( - isPremiumDisabled: Bool, - showPremiumGiftInAttachMenu: Bool, - showPremiumGiftInTextField: Bool, - giveawayGiftsPurchaseAvailable: Bool, - boostsPerGiftCount: Int32, - audioTransciptionTrialMaxDuration: Int32, - audioTransciptionTrialCount: Int32, - minChannelNameColorLevel: Int32, - minChannelNameIconLevel: Int32, - minChannelProfileColorLevel: Int32, - minChannelProfileIconLevel: Int32, - minChannelEmojiStatusLevel: Int32, - minChannelWallpaperLevel: Int32, - minChannelCustomWallpaperLevel: Int32 - - ) { - self.isPremiumDisabled = isPremiumDisabled - self.showPremiumGiftInAttachMenu = showPremiumGiftInAttachMenu - self.showPremiumGiftInTextField = showPremiumGiftInTextField - self.giveawayGiftsPurchaseAvailable = giveawayGiftsPurchaseAvailable - self.boostsPerGiftCount = boostsPerGiftCount - self.audioTransciptionTrialMaxDuration = audioTransciptionTrialMaxDuration - self.audioTransciptionTrialCount = audioTransciptionTrialCount - self.minChannelNameColorLevel = minChannelNameColorLevel - self.minChannelNameIconLevel = minChannelNameIconLevel - self.minChannelProfileColorLevel = minChannelProfileColorLevel - self.minChannelProfileIconLevel = minChannelProfileIconLevel - self.minChannelEmojiStatusLevel = minChannelEmojiStatusLevel - self.minChannelWallpaperLevel = minChannelWallpaperLevel - self.minChannelCustomWallpaperLevel = minChannelCustomWallpaperLevel - } - - public static func with(appConfiguration: AppConfiguration) -> PremiumConfiguration { - let defaultValue = self.defaultValue - if let data = appConfiguration.data { - func get(_ value: Any?) -> Int32? { - return (value as? Double).flatMap(Int32.init) - } - return PremiumConfiguration( - isPremiumDisabled: data["premium_purchase_blocked"] as? Bool ?? defaultValue.isPremiumDisabled, - showPremiumGiftInAttachMenu: data["premium_gift_attach_menu_icon"] as? Bool ?? defaultValue.showPremiumGiftInAttachMenu, - showPremiumGiftInTextField: data["premium_gift_text_field_icon"] as? Bool ?? defaultValue.showPremiumGiftInTextField, - giveawayGiftsPurchaseAvailable: data["giveaway_gifts_purchase_available"] as? Bool ?? defaultValue.giveawayGiftsPurchaseAvailable, - boostsPerGiftCount: get(data["boosts_per_sent_gift"]) ?? defaultValue.boostsPerGiftCount, - audioTransciptionTrialMaxDuration: get(data["transcribe_audio_trial_duration_max"]) ?? defaultValue.audioTransciptionTrialMaxDuration, - audioTransciptionTrialCount: get(data["transcribe_audio_trial_weekly_number"]) ?? defaultValue.audioTransciptionTrialCount, - minChannelNameColorLevel: get(data["channel_color_level_min"]) ?? defaultValue.minChannelNameColorLevel, - minChannelNameIconLevel: get(data["channel_bg_icon_level_min"]) ?? defaultValue.minChannelNameIconLevel, - minChannelProfileColorLevel: get(data["channel_profile_color_level_min"]) ?? defaultValue.minChannelProfileColorLevel, - minChannelProfileIconLevel: get(data["channel_profile_bg_icon_level_min"]) ?? defaultValue.minChannelProfileIconLevel, - minChannelEmojiStatusLevel: get(data["channel_emoji_status_level_min"]) ?? defaultValue.minChannelEmojiStatusLevel, - minChannelWallpaperLevel: get(data["channel_wallpaper_level_min"]) ?? defaultValue.minChannelWallpaperLevel, - minChannelCustomWallpaperLevel: get(data["channel_custom_wallpaper_level_min"]) ?? defaultValue.minChannelCustomWallpaperLevel - ) - } else { - return defaultValue - } - } -} - public struct AntiSpamBotConfiguration { public static var defaultValue: AntiSpamBotConfiguration { return AntiSpamBotConfiguration(antiSpamBotId: nil, minimumGroupParticipants: 100) @@ -1323,289 +1148,3 @@ public struct StickersSearchConfiguration { } } } - -private extension PeerNameColors.Colors { - init?(colors: EngineAvailableColorOptions.MultiColorPack) { - if colors.colors.isEmpty { - return nil - } - self.main = UIColor(rgb: colors.colors[0]) - if colors.colors.count > 1 { - self.secondary = UIColor(rgb: colors.colors[1]) - } else { - self.secondary = nil - } - if colors.colors.count > 2 { - self.tertiary = UIColor(rgb: colors.colors[2]) - } else { - self.tertiary = nil - } - } -} - -public class PeerNameColors: Equatable { - public enum Subject { - case background - case palette - case stories - } - - public struct Colors: Equatable { - public let main: UIColor - public let secondary: UIColor? - public let tertiary: UIColor? - - public init(main: UIColor, secondary: UIColor?, tertiary: UIColor?) { - self.main = main - self.secondary = secondary - self.tertiary = tertiary - } - - public init(main: UIColor) { - self.main = main - self.secondary = nil - self.tertiary = nil - } - - public init?(colors: [UIColor]) { - guard let first = colors.first else { - return nil - } - self.main = first - if colors.count == 3 { - self.secondary = colors[1] - self.tertiary = colors[2] - } else if colors.count == 2, let second = colors.last { - self.secondary = second - self.tertiary = nil - } else { - self.secondary = nil - self.tertiary = nil - } - } - } - - public static var defaultSingleColors: [Int32: Colors] { - return [ - 0: Colors(main: UIColor(rgb: 0xcc5049)), - 1: Colors(main: UIColor(rgb: 0xd67722)), - 2: Colors(main: UIColor(rgb: 0x955cdb)), - 3: Colors(main: UIColor(rgb: 0x40a920)), - 4: Colors(main: UIColor(rgb: 0x309eba)), - 5: Colors(main: UIColor(rgb: 0x368ad1)), - 6: Colors(main: UIColor(rgb: 0xc7508b)) - ] - } - - public static var defaultValue: PeerNameColors { - return PeerNameColors( - colors: defaultSingleColors, - darkColors: [:], - displayOrder: [5, 3, 1, 0, 2, 4, 6], - profileColors: [:], - profileDarkColors: [:], - profilePaletteColors: [:], - profilePaletteDarkColors: [:], - profileStoryColors: [:], - profileStoryDarkColors: [:], - profileDisplayOrder: [], - nameColorsChannelMinRequiredBoostLevel: [:] - ) - } - - public let colors: [Int32: Colors] - public let darkColors: [Int32: Colors] - public let displayOrder: [Int32] - - public let profileColors: [Int32: Colors] - public let profileDarkColors: [Int32: Colors] - public let profilePaletteColors: [Int32: Colors] - public let profilePaletteDarkColors: [Int32: Colors] - public let profileStoryColors: [Int32: Colors] - public let profileStoryDarkColors: [Int32: Colors] - public let profileDisplayOrder: [Int32] - - public let nameColorsChannelMinRequiredBoostLevel: [Int32: Int32] - - public func get(_ color: PeerNameColor, dark: Bool = false) -> Colors { - if dark, let colors = self.darkColors[color.rawValue] { - return colors - } else if let colors = self.colors[color.rawValue] { - return colors - } else { - return PeerNameColors.defaultSingleColors[5]! - } - } - - public func getProfile(_ color: PeerNameColor, dark: Bool = false, subject: Subject = .background) -> Colors { - switch subject { - case .background: - if dark, let colors = self.profileDarkColors[color.rawValue] { - return colors - } else if let colors = self.profileColors[color.rawValue] { - return colors - } else { - return Colors(main: UIColor(rgb: 0xcc5049)) - } - case .palette: - if dark, let colors = self.profilePaletteDarkColors[color.rawValue] { - return colors - } else if let colors = self.profilePaletteColors[color.rawValue] { - return colors - } else { - return self.getProfile(color, dark: dark, subject: .background) - } - case .stories: - if dark, let colors = self.profileStoryDarkColors[color.rawValue] { - return colors - } else if let colors = self.profileStoryColors[color.rawValue] { - return colors - } else { - return self.getProfile(color, dark: dark, subject: .background) - } - } - } - - fileprivate init( - colors: [Int32: Colors], - darkColors: [Int32: Colors], - displayOrder: [Int32], - profileColors: [Int32: Colors], - profileDarkColors: [Int32: Colors], - profilePaletteColors: [Int32: Colors], - profilePaletteDarkColors: [Int32: Colors], - profileStoryColors: [Int32: Colors], - profileStoryDarkColors: [Int32: Colors], - profileDisplayOrder: [Int32], - nameColorsChannelMinRequiredBoostLevel: [Int32: Int32] - ) { - self.colors = colors - self.darkColors = darkColors - self.displayOrder = displayOrder - self.profileColors = profileColors - self.profileDarkColors = profileDarkColors - self.profilePaletteColors = profilePaletteColors - self.profilePaletteDarkColors = profilePaletteDarkColors - self.profileStoryColors = profileStoryColors - self.profileStoryDarkColors = profileStoryDarkColors - self.profileDisplayOrder = profileDisplayOrder - self.nameColorsChannelMinRequiredBoostLevel = nameColorsChannelMinRequiredBoostLevel - } - - public static func with(availableReplyColors: EngineAvailableColorOptions, availableProfileColors: EngineAvailableColorOptions) -> PeerNameColors { - var colors: [Int32: Colors] = [:] - var darkColors: [Int32: Colors] = [:] - var displayOrder: [Int32] = [] - var profileColors: [Int32: Colors] = [:] - var profileDarkColors: [Int32: Colors] = [:] - var profilePaletteColors: [Int32: Colors] = [:] - var profilePaletteDarkColors: [Int32: Colors] = [:] - var profileStoryColors: [Int32: Colors] = [:] - var profileStoryDarkColors: [Int32: Colors] = [:] - var profileDisplayOrder: [Int32] = [] - - var nameColorsChannelMinRequiredBoostLevel: [Int32: Int32] = [:] - - if !availableReplyColors.options.isEmpty { - for option in availableReplyColors.options { - if let requiredChannelMinBoostLevel = option.value.requiredChannelMinBoostLevel { - nameColorsChannelMinRequiredBoostLevel[option.key] = requiredChannelMinBoostLevel - } - - if let parsedLight = PeerNameColors.Colors(colors: option.value.light.background) { - colors[option.key] = parsedLight - } - if let parsedDark = (option.value.dark?.background).flatMap(PeerNameColors.Colors.init(colors:)) { - darkColors[option.key] = parsedDark - } - - for option in availableReplyColors.options { - if !displayOrder.contains(option.key) { - displayOrder.append(option.key) - } - } - } - } else { - let defaultValue = PeerNameColors.defaultValue - colors = defaultValue.colors - darkColors = defaultValue.darkColors - displayOrder = defaultValue.displayOrder - } - - if !availableProfileColors.options.isEmpty { - for option in availableProfileColors.options { - if let parsedLight = PeerNameColors.Colors(colors: option.value.light.background) { - profileColors[option.key] = parsedLight - } - if let parsedDark = (option.value.dark?.background).flatMap(PeerNameColors.Colors.init(colors:)) { - profileDarkColors[option.key] = parsedDark - } - if let parsedPaletteLight = PeerNameColors.Colors(colors: option.value.light.palette) { - profilePaletteColors[option.key] = parsedPaletteLight - } - if let parsedPaletteDark = (option.value.dark?.palette).flatMap(PeerNameColors.Colors.init(colors:)) { - profilePaletteDarkColors[option.key] = parsedPaletteDark - } - if let parsedStoryLight = (option.value.light.stories).flatMap(PeerNameColors.Colors.init(colors:)) { - profileStoryColors[option.key] = parsedStoryLight - } - if let parsedStoryDark = (option.value.dark?.stories).flatMap(PeerNameColors.Colors.init(colors:)) { - profileStoryDarkColors[option.key] = parsedStoryDark - } - for option in availableProfileColors.options { - if !profileDisplayOrder.contains(option.key) { - profileDisplayOrder.append(option.key) - } - } - } - } - - return PeerNameColors( - colors: colors, - darkColors: darkColors, - displayOrder: displayOrder, - profileColors: profileColors, - profileDarkColors: profileDarkColors, - profilePaletteColors: profilePaletteColors, - profilePaletteDarkColors: profilePaletteDarkColors, - profileStoryColors: profileStoryColors, - profileStoryDarkColors: profileStoryDarkColors, - profileDisplayOrder: profileDisplayOrder, - nameColorsChannelMinRequiredBoostLevel: nameColorsChannelMinRequiredBoostLevel - ) - } - - public static func == (lhs: PeerNameColors, rhs: PeerNameColors) -> Bool { - if lhs.colors != rhs.colors { - return false - } - if lhs.darkColors != rhs.darkColors { - return false - } - if lhs.displayOrder != rhs.displayOrder { - return false - } - if lhs.profileColors != rhs.profileColors { - return false - } - if lhs.profileDarkColors != rhs.profileDarkColors { - return false - } - if lhs.profilePaletteColors != rhs.profilePaletteColors { - return false - } - if lhs.profilePaletteDarkColors != rhs.profilePaletteDarkColors { - return false - } - if lhs.profileStoryColors != rhs.profileStoryColors { - return false - } - if lhs.profileStoryDarkColors != rhs.profileStoryDarkColors { - return false - } - if lhs.profileDisplayOrder != rhs.profileDisplayOrder { - return false - } - return true - } -} diff --git a/submodules/AccountContext/Sources/ChatController.swift b/submodules/AccountContext/Sources/ChatController.swift index 8605c86494..c2e25b59a1 100644 --- a/submodules/AccountContext/Sources/ChatController.swift +++ b/submodules/AccountContext/Sources/ChatController.swift @@ -53,6 +53,7 @@ public final class ChatMessageItemAssociatedData: Equatable { public let recommendedChannels: RecommendedChannels? public let audioTranscriptionTrial: AudioTranscription.TrialState public let chatThemes: [TelegramTheme] + public let isStandalone: Bool public init( automaticDownloadPeerType: MediaAutoDownloadPeerType, @@ -79,7 +80,8 @@ public final class ChatMessageItemAssociatedData: Equatable { maxReadStoryId: Int32? = nil, recommendedChannels: RecommendedChannels? = nil, audioTranscriptionTrial: AudioTranscription.TrialState = .defaultValue, - chatThemes: [TelegramTheme] = [] + chatThemes: [TelegramTheme] = [], + isStandalone: Bool = false ) { self.automaticDownloadPeerType = automaticDownloadPeerType self.automaticDownloadPeerId = automaticDownloadPeerId @@ -106,6 +108,7 @@ public final class ChatMessageItemAssociatedData: Equatable { self.recommendedChannels = recommendedChannels self.audioTranscriptionTrial = audioTranscriptionTrial self.chatThemes = chatThemes + self.isStandalone = isStandalone } public static func == (lhs: ChatMessageItemAssociatedData, rhs: ChatMessageItemAssociatedData) -> Bool { @@ -181,6 +184,9 @@ public final class ChatMessageItemAssociatedData: Equatable { if lhs.chatThemes != rhs.chatThemes { return false } + if lhs.isStandalone != rhs.isStandalone { + return false + } return true } } diff --git a/submodules/AccountContext/Sources/PeerNameColors.swift b/submodules/AccountContext/Sources/PeerNameColors.swift new file mode 100644 index 0000000000..e8bce68a49 --- /dev/null +++ b/submodules/AccountContext/Sources/PeerNameColors.swift @@ -0,0 +1,289 @@ +import Foundation +import UIKit +import TelegramCore + +private extension PeerNameColors.Colors { + init?(colors: EngineAvailableColorOptions.MultiColorPack) { + if colors.colors.isEmpty { + return nil + } + self.main = UIColor(rgb: colors.colors[0]) + if colors.colors.count > 1 { + self.secondary = UIColor(rgb: colors.colors[1]) + } else { + self.secondary = nil + } + if colors.colors.count > 2 { + self.tertiary = UIColor(rgb: colors.colors[2]) + } else { + self.tertiary = nil + } + } +} + +public class PeerNameColors: Equatable { + public enum Subject { + case background + case palette + case stories + } + + public struct Colors: Equatable { + public let main: UIColor + public let secondary: UIColor? + public let tertiary: UIColor? + + public init(main: UIColor, secondary: UIColor?, tertiary: UIColor?) { + self.main = main + self.secondary = secondary + self.tertiary = tertiary + } + + public init(main: UIColor) { + self.main = main + self.secondary = nil + self.tertiary = nil + } + + public init?(colors: [UIColor]) { + guard let first = colors.first else { + return nil + } + self.main = first + if colors.count == 3 { + self.secondary = colors[1] + self.tertiary = colors[2] + } else if colors.count == 2, let second = colors.last { + self.secondary = second + self.tertiary = nil + } else { + self.secondary = nil + self.tertiary = nil + } + } + } + + public static var defaultSingleColors: [Int32: Colors] { + return [ + 0: Colors(main: UIColor(rgb: 0xcc5049)), + 1: Colors(main: UIColor(rgb: 0xd67722)), + 2: Colors(main: UIColor(rgb: 0x955cdb)), + 3: Colors(main: UIColor(rgb: 0x40a920)), + 4: Colors(main: UIColor(rgb: 0x309eba)), + 5: Colors(main: UIColor(rgb: 0x368ad1)), + 6: Colors(main: UIColor(rgb: 0xc7508b)) + ] + } + + public static var defaultValue: PeerNameColors { + return PeerNameColors( + colors: defaultSingleColors, + darkColors: [:], + displayOrder: [5, 3, 1, 0, 2, 4, 6], + profileColors: [:], + profileDarkColors: [:], + profilePaletteColors: [:], + profilePaletteDarkColors: [:], + profileStoryColors: [:], + profileStoryDarkColors: [:], + profileDisplayOrder: [], + nameColorsChannelMinRequiredBoostLevel: [:] + ) + } + + public let colors: [Int32: Colors] + public let darkColors: [Int32: Colors] + public let displayOrder: [Int32] + + public let profileColors: [Int32: Colors] + public let profileDarkColors: [Int32: Colors] + public let profilePaletteColors: [Int32: Colors] + public let profilePaletteDarkColors: [Int32: Colors] + public let profileStoryColors: [Int32: Colors] + public let profileStoryDarkColors: [Int32: Colors] + public let profileDisplayOrder: [Int32] + + public let nameColorsChannelMinRequiredBoostLevel: [Int32: Int32] + + public func get(_ color: PeerNameColor, dark: Bool = false) -> Colors { + if dark, let colors = self.darkColors[color.rawValue] { + return colors + } else if let colors = self.colors[color.rawValue] { + return colors + } else { + return PeerNameColors.defaultSingleColors[5]! + } + } + + public func getProfile(_ color: PeerNameColor, dark: Bool = false, subject: Subject = .background) -> Colors { + switch subject { + case .background: + if dark, let colors = self.profileDarkColors[color.rawValue] { + return colors + } else if let colors = self.profileColors[color.rawValue] { + return colors + } else { + return Colors(main: UIColor(rgb: 0xcc5049)) + } + case .palette: + if dark, let colors = self.profilePaletteDarkColors[color.rawValue] { + return colors + } else if let colors = self.profilePaletteColors[color.rawValue] { + return colors + } else { + return self.getProfile(color, dark: dark, subject: .background) + } + case .stories: + if dark, let colors = self.profileStoryDarkColors[color.rawValue] { + return colors + } else if let colors = self.profileStoryColors[color.rawValue] { + return colors + } else { + return self.getProfile(color, dark: dark, subject: .background) + } + } + } + + fileprivate init( + colors: [Int32: Colors], + darkColors: [Int32: Colors], + displayOrder: [Int32], + profileColors: [Int32: Colors], + profileDarkColors: [Int32: Colors], + profilePaletteColors: [Int32: Colors], + profilePaletteDarkColors: [Int32: Colors], + profileStoryColors: [Int32: Colors], + profileStoryDarkColors: [Int32: Colors], + profileDisplayOrder: [Int32], + nameColorsChannelMinRequiredBoostLevel: [Int32: Int32] + ) { + self.colors = colors + self.darkColors = darkColors + self.displayOrder = displayOrder + self.profileColors = profileColors + self.profileDarkColors = profileDarkColors + self.profilePaletteColors = profilePaletteColors + self.profilePaletteDarkColors = profilePaletteDarkColors + self.profileStoryColors = profileStoryColors + self.profileStoryDarkColors = profileStoryDarkColors + self.profileDisplayOrder = profileDisplayOrder + self.nameColorsChannelMinRequiredBoostLevel = nameColorsChannelMinRequiredBoostLevel + } + + public static func with(availableReplyColors: EngineAvailableColorOptions, availableProfileColors: EngineAvailableColorOptions) -> PeerNameColors { + var colors: [Int32: Colors] = [:] + var darkColors: [Int32: Colors] = [:] + var displayOrder: [Int32] = [] + var profileColors: [Int32: Colors] = [:] + var profileDarkColors: [Int32: Colors] = [:] + var profilePaletteColors: [Int32: Colors] = [:] + var profilePaletteDarkColors: [Int32: Colors] = [:] + var profileStoryColors: [Int32: Colors] = [:] + var profileStoryDarkColors: [Int32: Colors] = [:] + var profileDisplayOrder: [Int32] = [] + + var nameColorsChannelMinRequiredBoostLevel: [Int32: Int32] = [:] + + if !availableReplyColors.options.isEmpty { + for option in availableReplyColors.options { + if let requiredChannelMinBoostLevel = option.value.requiredChannelMinBoostLevel { + nameColorsChannelMinRequiredBoostLevel[option.key] = requiredChannelMinBoostLevel + } + + if let parsedLight = PeerNameColors.Colors(colors: option.value.light.background) { + colors[option.key] = parsedLight + } + if let parsedDark = (option.value.dark?.background).flatMap(PeerNameColors.Colors.init(colors:)) { + darkColors[option.key] = parsedDark + } + + for option in availableReplyColors.options { + if !displayOrder.contains(option.key) { + displayOrder.append(option.key) + } + } + } + } else { + let defaultValue = PeerNameColors.defaultValue + colors = defaultValue.colors + darkColors = defaultValue.darkColors + displayOrder = defaultValue.displayOrder + } + + if !availableProfileColors.options.isEmpty { + for option in availableProfileColors.options { + if let parsedLight = PeerNameColors.Colors(colors: option.value.light.background) { + profileColors[option.key] = parsedLight + } + if let parsedDark = (option.value.dark?.background).flatMap(PeerNameColors.Colors.init(colors:)) { + profileDarkColors[option.key] = parsedDark + } + if let parsedPaletteLight = PeerNameColors.Colors(colors: option.value.light.palette) { + profilePaletteColors[option.key] = parsedPaletteLight + } + if let parsedPaletteDark = (option.value.dark?.palette).flatMap(PeerNameColors.Colors.init(colors:)) { + profilePaletteDarkColors[option.key] = parsedPaletteDark + } + if let parsedStoryLight = (option.value.light.stories).flatMap(PeerNameColors.Colors.init(colors:)) { + profileStoryColors[option.key] = parsedStoryLight + } + if let parsedStoryDark = (option.value.dark?.stories).flatMap(PeerNameColors.Colors.init(colors:)) { + profileStoryDarkColors[option.key] = parsedStoryDark + } + for option in availableProfileColors.options { + if !profileDisplayOrder.contains(option.key) { + profileDisplayOrder.append(option.key) + } + } + } + } + + return PeerNameColors( + colors: colors, + darkColors: darkColors, + displayOrder: displayOrder, + profileColors: profileColors, + profileDarkColors: profileDarkColors, + profilePaletteColors: profilePaletteColors, + profilePaletteDarkColors: profilePaletteDarkColors, + profileStoryColors: profileStoryColors, + profileStoryDarkColors: profileStoryDarkColors, + profileDisplayOrder: profileDisplayOrder, + nameColorsChannelMinRequiredBoostLevel: nameColorsChannelMinRequiredBoostLevel + ) + } + + public static func == (lhs: PeerNameColors, rhs: PeerNameColors) -> Bool { + if lhs.colors != rhs.colors { + return false + } + if lhs.darkColors != rhs.darkColors { + return false + } + if lhs.displayOrder != rhs.displayOrder { + return false + } + if lhs.profileColors != rhs.profileColors { + return false + } + if lhs.profileDarkColors != rhs.profileDarkColors { + return false + } + if lhs.profilePaletteColors != rhs.profilePaletteColors { + return false + } + if lhs.profilePaletteDarkColors != rhs.profilePaletteDarkColors { + return false + } + if lhs.profileStoryColors != rhs.profileStoryColors { + return false + } + if lhs.profileStoryDarkColors != rhs.profileStoryDarkColors { + return false + } + if lhs.profileDisplayOrder != rhs.profileDisplayOrder { + return false + } + return true + } +} diff --git a/submodules/AccountContext/Sources/Premium.swift b/submodules/AccountContext/Sources/Premium.swift new file mode 100644 index 0000000000..fed5a7b462 --- /dev/null +++ b/submodules/AccountContext/Sources/Premium.swift @@ -0,0 +1,183 @@ +import Foundation +import TelegramCore + +public enum PremiumIntroSource { + case settings + case stickers + case reactions + case ads + case upload + case groupsAndChannels + case pinnedChats + case publicLinks + case savedGifs + case savedStickers + case folders + case chatsPerFolder + case accounts + case appIcons + case about + case deeplink(String?) + case profile(EnginePeer.Id) + case emojiStatus(EnginePeer.Id, Int64, TelegramMediaFile?, LoadedStickerPack?) + case voiceToText + case fasterDownload + case translation + case stories + case storiesDownload + case storiesStealthMode + case storiesPermanentViews + case storiesFormatting + case storiesExpirationDurations + case storiesSuggestedReactions + case channelBoost(EnginePeer.Id) + case nameColor + case similarChannels + case wallpapers + case presence +} + +public enum PremiumGiftSource: Equatable { + case profile + case attachMenu + case settings + case chatList + case channelBoost + case deeplink(String?) +} + +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 + case translation + case stories + case colors + case wallpapers +} + +public enum PremiumLimitSubject { + case folders + case chatsPerFolder + case pins + case files + case accounts + case linksPerSharedFolder + case membershipInSharedFolders + case channels + case expiringStories + case storiesWeekly + case storiesMonthly + case storiesChannelBoost(peer: EnginePeer, isCurrent: Bool, level: Int32, currentLevelBoosts: Int32, nextLevelBoosts: Int32?, link: String?, myBoostCount: Int32, canBoostAgain: Bool) +} + +public enum PremiumPrivacySubject { + case presence + case readTime +} + +public struct PremiumConfiguration { + public static var defaultValue: PremiumConfiguration { + return PremiumConfiguration( + isPremiumDisabled: false, + showPremiumGiftInAttachMenu: false, + showPremiumGiftInTextField: false, + giveawayGiftsPurchaseAvailable: false, + boostsPerGiftCount: 3, + audioTransciptionTrialMaxDuration: 300, + audioTransciptionTrialCount: 2, + minChannelNameColorLevel: 1, + minChannelNameIconLevel: 4, + minChannelProfileColorLevel: 5, + minChannelProfileIconLevel: 7, + minChannelEmojiStatusLevel: 8, + minChannelWallpaperLevel: 9, + minChannelCustomWallpaperLevel: 10 + ) + } + + public let isPremiumDisabled: Bool + public let showPremiumGiftInAttachMenu: Bool + public let showPremiumGiftInTextField: Bool + public let giveawayGiftsPurchaseAvailable: Bool + public let boostsPerGiftCount: Int32 + public let audioTransciptionTrialMaxDuration: Int32 + public let audioTransciptionTrialCount: Int32 + public let minChannelNameColorLevel: Int32 + public let minChannelNameIconLevel: Int32 + public let minChannelProfileColorLevel: Int32 + public let minChannelProfileIconLevel: Int32 + public let minChannelEmojiStatusLevel: Int32 + public let minChannelWallpaperLevel: Int32 + public let minChannelCustomWallpaperLevel: Int32 + + fileprivate init( + isPremiumDisabled: Bool, + showPremiumGiftInAttachMenu: Bool, + showPremiumGiftInTextField: Bool, + giveawayGiftsPurchaseAvailable: Bool, + boostsPerGiftCount: Int32, + audioTransciptionTrialMaxDuration: Int32, + audioTransciptionTrialCount: Int32, + minChannelNameColorLevel: Int32, + minChannelNameIconLevel: Int32, + minChannelProfileColorLevel: Int32, + minChannelProfileIconLevel: Int32, + minChannelEmojiStatusLevel: Int32, + minChannelWallpaperLevel: Int32, + minChannelCustomWallpaperLevel: Int32 + + ) { + self.isPremiumDisabled = isPremiumDisabled + self.showPremiumGiftInAttachMenu = showPremiumGiftInAttachMenu + self.showPremiumGiftInTextField = showPremiumGiftInTextField + self.giveawayGiftsPurchaseAvailable = giveawayGiftsPurchaseAvailable + self.boostsPerGiftCount = boostsPerGiftCount + self.audioTransciptionTrialMaxDuration = audioTransciptionTrialMaxDuration + self.audioTransciptionTrialCount = audioTransciptionTrialCount + self.minChannelNameColorLevel = minChannelNameColorLevel + self.minChannelNameIconLevel = minChannelNameIconLevel + self.minChannelProfileColorLevel = minChannelProfileColorLevel + self.minChannelProfileIconLevel = minChannelProfileIconLevel + self.minChannelEmojiStatusLevel = minChannelEmojiStatusLevel + self.minChannelWallpaperLevel = minChannelWallpaperLevel + self.minChannelCustomWallpaperLevel = minChannelCustomWallpaperLevel + } + + public static func with(appConfiguration: AppConfiguration) -> PremiumConfiguration { + let defaultValue = self.defaultValue + if let data = appConfiguration.data { + func get(_ value: Any?) -> Int32? { + return (value as? Double).flatMap(Int32.init) + } + return PremiumConfiguration( + isPremiumDisabled: data["premium_purchase_blocked"] as? Bool ?? defaultValue.isPremiumDisabled, + showPremiumGiftInAttachMenu: data["premium_gift_attach_menu_icon"] as? Bool ?? defaultValue.showPremiumGiftInAttachMenu, + showPremiumGiftInTextField: data["premium_gift_text_field_icon"] as? Bool ?? defaultValue.showPremiumGiftInTextField, + giveawayGiftsPurchaseAvailable: data["giveaway_gifts_purchase_available"] as? Bool ?? defaultValue.giveawayGiftsPurchaseAvailable, + boostsPerGiftCount: get(data["boosts_per_sent_gift"]) ?? defaultValue.boostsPerGiftCount, + audioTransciptionTrialMaxDuration: get(data["transcribe_audio_trial_duration_max"]) ?? defaultValue.audioTransciptionTrialMaxDuration, + audioTransciptionTrialCount: get(data["transcribe_audio_trial_weekly_number"]) ?? defaultValue.audioTransciptionTrialCount, + minChannelNameColorLevel: get(data["channel_color_level_min"]) ?? defaultValue.minChannelNameColorLevel, + minChannelNameIconLevel: get(data["channel_bg_icon_level_min"]) ?? defaultValue.minChannelNameIconLevel, + minChannelProfileColorLevel: get(data["channel_profile_color_level_min"]) ?? defaultValue.minChannelProfileColorLevel, + minChannelProfileIconLevel: get(data["channel_profile_bg_icon_level_min"]) ?? defaultValue.minChannelProfileIconLevel, + minChannelEmojiStatusLevel: get(data["channel_emoji_status_level_min"]) ?? defaultValue.minChannelEmojiStatusLevel, + minChannelWallpaperLevel: get(data["channel_wallpaper_level_min"]) ?? defaultValue.minChannelWallpaperLevel, + minChannelCustomWallpaperLevel: get(data["channel_custom_wallpaper_level_min"]) ?? defaultValue.minChannelCustomWallpaperLevel + ) + } else { + return defaultValue + } + } +} diff --git a/submodules/ContextUI/Sources/ContextController.swift b/submodules/ContextUI/Sources/ContextController.swift index c93fd1fbb0..c6b2decb95 100644 --- a/submodules/ContextUI/Sources/ContextController.swift +++ b/submodules/ContextUI/Sources/ContextController.swift @@ -2142,6 +2142,7 @@ public enum ContextActionsHorizontalAlignment { } public protocol ContextExtractedContentSource: AnyObject { + var initialAppearanceOffset: CGPoint { get } var centerVertically: Bool { get } var keepInPlace: Bool { get } var ignoreContentTouches: Bool { get } @@ -2155,6 +2156,10 @@ public protocol ContextExtractedContentSource: AnyObject { } public extension ContextExtractedContentSource { + var initialAppearanceOffset: CGPoint { + return .zero + } + var centerVertically: Bool { return false } diff --git a/submodules/ContextUI/Sources/ContextControllerExtractedPresentationNode.swift b/submodules/ContextUI/Sources/ContextControllerExtractedPresentationNode.swift index fbeb50e9f8..5f55aa26c1 100644 --- a/submodules/ContextUI/Sources/ContextControllerExtractedPresentationNode.swift +++ b/submodules/ContextUI/Sources/ContextControllerExtractedPresentationNode.swift @@ -1135,8 +1135,11 @@ final class ContextControllerExtractedPresentationNode: ASDisplayNode, ContextCo let contentHeight = contentNode.containingItem.view.bounds.size.height if case let .extracted(extracted) = self.source, extracted.centerVertically { if actionsSize.height.isZero { + var initialContentRect = contentRect + initialContentRect.origin.y += extracted.initialAppearanceOffset.y + let fixedContentY = floorToScreenPixels((layout.size.height - contentHeight) / 2.0) - animationInContentYDistance = fixedContentY - contentRect.minY + animationInContentYDistance = fixedContentY - initialContentRect.minY } else if contentX + contentWidth > layout.size.width / 2.0, actionsSize.height > 0.0 { let fixedContentX = layout.size.width - (contentX + contentWidth) animationInContentXDistance = fixedContentX - contentX diff --git a/submodules/Display/Source/GenerateImage.swift b/submodules/Display/Source/GenerateImage.swift index 610628a96e..5618a7ea7d 100644 --- a/submodules/Display/Source/GenerateImage.swift +++ b/submodules/Display/Source/GenerateImage.swift @@ -398,18 +398,22 @@ public func generateGradientImage(size: CGSize, scale: CGFloat = 0.0, colors: [U return image } -public func generateGradientFilledCircleImage(diameter: CGFloat, colors: NSArray) -> UIImage? { +public func generateGradientFilledCircleImage(diameter: CGFloat, colors: NSArray, direction: GradientImageDirection = .vertical) -> UIImage? { return generateImage(CGSize(width: diameter, height: diameter), contextGenerator: { size, context in let bounds = CGRect(origin: CGPoint(), size: size) context.clear(bounds) context.addEllipse(in: bounds) context.clip() - var locations: [CGFloat] = [0.0, 1.0] + var locations: [CGFloat] = [] + for i in 0 ..< colors.count { + let t = CGFloat(i) / CGFloat(colors.count - 1) + locations.append(t) + } let colorSpace = DeviceGraphicsContextSettings.shared.colorSpace let gradient = CGGradient(colorsSpace: colorSpace, colors: colors, locations: &locations)! - context.drawLinearGradient(gradient, start: CGPoint(), end: CGPoint(x: 0.0, y: bounds.size.height), options: CGGradientDrawingOptions()) + context.drawLinearGradient(gradient, start: CGPoint(), end: direction == .horizontal ? CGPoint(x: size.width, y: 0.0) : CGPoint(x: 0.0, y: size.height), options: CGGradientDrawingOptions()) }) } diff --git a/submodules/LegacyComponents/Sources/TGModernConversationInputMicButton.m b/submodules/LegacyComponents/Sources/TGModernConversationInputMicButton.m index 9e570cdd40..501866d959 100644 --- a/submodules/LegacyComponents/Sources/TGModernConversationInputMicButton.m +++ b/submodules/LegacyComponents/Sources/TGModernConversationInputMicButton.m @@ -146,6 +146,8 @@ static const CGFloat outerCircleMinScale = innerCircleRadius / outerCircleRadius BOOL _xFeedbackOccured; BOOL _yFeedbackOccured; + + bool _skipCancelUpdate; } @end @@ -507,7 +509,9 @@ static const CGFloat outerCircleMinScale = innerCircleRadius / outerCircleRadius [self displayLink].paused = false; if (_locked) { + _skipCancelUpdate = true; [self animateLock]; + _skipCancelUpdate = false; } } @@ -644,7 +648,7 @@ static const CGFloat outerCircleMinScale = innerCircleRadius / outerCircleRadius _currentScale = 1; _cancelTargetTranslation = 0; id delegate = _delegate; - if ([delegate respondsToSelector:@selector(micButtonInteractionUpdateCancelTranslation:)]) + if ([delegate respondsToSelector:@selector(micButtonInteractionUpdateCancelTranslation:)] && !_skipCancelUpdate) [delegate micButtonInteractionUpdateCancelTranslation:-_cancelTargetTranslation]; _innerIconView.transform = CGAffineTransformMakeScale(0.3f, 0.3f); diff --git a/submodules/MediaPickerUI/Sources/MediaPickerSelectedListNode.swift b/submodules/MediaPickerUI/Sources/MediaPickerSelectedListNode.swift index f48f1b2b86..7c0e0a343d 100644 --- a/submodules/MediaPickerUI/Sources/MediaPickerSelectedListNode.swift +++ b/submodules/MediaPickerUI/Sources/MediaPickerSelectedListNode.swift @@ -859,10 +859,10 @@ final class MediaPickerSelectedListNode: ASDisplayNode, UIScrollViewDelegate, UI let previewText = groupLayouts.count > 1 ? presentationData.strings.Attachment_MessagesPreview : presentationData.strings.Attachment_MessagePreview let previewMessage = Message(stableId: 0, stableVersion: 0, id: MessageId(peerId: peerId, namespace: 0, id: 0), globallyUniqueId: nil, groupingKey: nil, groupInfo: nil, threadId: nil, timestamp: 0, flags: [], tags: [], globalTags: [], localTags: [], forwardInfo: nil, author: peers[peerId], text: "", attributes: [], media: [TelegramMediaAction(action: .customText(text: previewText, entities: [], additionalAttributes: nil))], peers: peers, associatedMessages: SimpleDictionary(), associatedMessageIds: [], associatedMedia: [:], associatedThreadInfo: nil, associatedStories: [:]) - let previewItem = self.context.sharedContext.makeChatMessagePreviewItem(context: context, messages: [previewMessage], theme: theme, strings: presentationData.strings, wallpaper: wallpaper, fontSize: presentationData.chatFontSize, chatBubbleCorners: bubbleCorners, dateTimeFormat: presentationData.dateTimeFormat, nameOrder: presentationData.nameDisplayOrder, forcedResourceStatus: nil, tapMessage: nil, clickThroughMessage: nil, backgroundNode: self.wallpaperBackgroundNode, availableReactions: nil, accountPeer: nil, isCentered: true, isPreview: true) + let previewItem = self.context.sharedContext.makeChatMessagePreviewItem(context: context, messages: [previewMessage], theme: theme, strings: presentationData.strings, wallpaper: wallpaper, fontSize: presentationData.chatFontSize, chatBubbleCorners: bubbleCorners, dateTimeFormat: presentationData.dateTimeFormat, nameOrder: presentationData.nameDisplayOrder, forcedResourceStatus: nil, tapMessage: nil, clickThroughMessage: nil, backgroundNode: self.wallpaperBackgroundNode, availableReactions: nil, accountPeer: nil, isCentered: true, isPreview: true, isStandalone: false) let dragMessage = Message(stableId: 0, stableVersion: 0, id: MessageId(peerId: peerId, namespace: 0, id: 0), globallyUniqueId: nil, groupingKey: nil, groupInfo: nil, threadId: nil, timestamp: 0, flags: [], tags: [], globalTags: [], localTags: [], forwardInfo: nil, author: peers[peerId], text: "", attributes: [], media: [TelegramMediaAction(action: .customText(text: presentationData.strings.Attachment_DragToReorder, entities: [], additionalAttributes: nil))], peers: peers, associatedMessages: SimpleDictionary(), associatedMessageIds: [], associatedMedia: [:], associatedThreadInfo: nil, associatedStories: [:]) - let dragItem = self.context.sharedContext.makeChatMessagePreviewItem(context: context, messages: [dragMessage], theme: theme, strings: presentationData.strings, wallpaper: wallpaper, fontSize: presentationData.chatFontSize, chatBubbleCorners: bubbleCorners, dateTimeFormat: presentationData.dateTimeFormat, nameOrder: presentationData.nameDisplayOrder, forcedResourceStatus: nil, tapMessage: nil, clickThroughMessage: nil, backgroundNode: self.wallpaperBackgroundNode, availableReactions: nil, accountPeer: nil, isCentered: true, isPreview: true) + let dragItem = self.context.sharedContext.makeChatMessagePreviewItem(context: context, messages: [dragMessage], theme: theme, strings: presentationData.strings, wallpaper: wallpaper, fontSize: presentationData.chatFontSize, chatBubbleCorners: bubbleCorners, dateTimeFormat: presentationData.dateTimeFormat, nameOrder: presentationData.nameDisplayOrder, forcedResourceStatus: nil, tapMessage: nil, clickThroughMessage: nil, backgroundNode: self.wallpaperBackgroundNode, availableReactions: nil, accountPeer: nil, isCentered: true, isPreview: true, isStandalone: false) let headerItems: [ListViewItem] = [previewItem, dragItem] diff --git a/submodules/PhotoResources/Sources/PhotoResources.swift b/submodules/PhotoResources/Sources/PhotoResources.swift index 450d9bb9ad..38378ab4f5 100644 --- a/submodules/PhotoResources/Sources/PhotoResources.swift +++ b/submodules/PhotoResources/Sources/PhotoResources.swift @@ -2154,7 +2154,7 @@ public func chatSecretMessageVideo(account: Account, userLocation: MediaResource thumbnailContext2.withFlippedContext { c in c.interpolationQuality = .none if let image = thumbnailContext.generateImage()?.cgImage { - c.draw(image, in: CGRect(origin: CGPoint(), size: thumbnailContext2Size)) + c.draw(image, in: CGRect(origin: CGPoint(), size: thumbnailContext2Size).insetBy(dx: -4.0, dy: -4.0)) } } imageFastBlur(Int32(thumbnailContext2Size.width), Int32(thumbnailContext2Size.height), Int32(thumbnailContext2.bytesPerRow), thumbnailContext2.bytes) @@ -2185,7 +2185,7 @@ public func chatSecretMessageVideo(account: Account, userLocation: MediaResource } } - addCorners(context, arguments: arguments) +// addCorners(context, arguments: arguments) return context } diff --git a/submodules/PremiumUI/Sources/PremiumBoostLevelsScreen.swift b/submodules/PremiumUI/Sources/PremiumBoostLevelsScreen.swift index 8425a7586a..ef521840b1 100644 --- a/submodules/PremiumUI/Sources/PremiumBoostLevelsScreen.swift +++ b/submodules/PremiumUI/Sources/PremiumBoostLevelsScreen.swift @@ -361,8 +361,6 @@ private final class LevelSectionComponent: CombinedComponent { } } - - private final class LimitSheetContent: CombinedComponent { typealias EnvironmentType = (Empty, ScrollChildEnvironment) @@ -821,8 +819,6 @@ private final class LimitSheetContent: CombinedComponent { } } - - private final class BoostLevelsContainerComponent: CombinedComponent { let context: AccountContext let theme: PresentationTheme diff --git a/submodules/PremiumUI/Sources/PremiumPrivacyScreen.swift b/submodules/PremiumUI/Sources/PremiumPrivacyScreen.swift new file mode 100644 index 0000000000..d8be589bc7 --- /dev/null +++ b/submodules/PremiumUI/Sources/PremiumPrivacyScreen.swift @@ -0,0 +1,507 @@ +import Foundation +import UIKit +import Display +import ComponentFlow +import Markdown +import TextFormat +import TelegramPresentationData +import ViewControllerComponent +import SheetComponent +import BundleIconComponent +import BalancedTextComponent +import MultilineTextComponent +import SolidRoundedButtonComponent +import AccountContext + +private final class SheetContent: CombinedComponent { + typealias EnvironmentType = ViewControllerComponentContainer.Environment + + let context: AccountContext + let subject: PremiumPrivacyScreen.Subject + + let action: () -> Void + let openPremiumIntro: () -> Void + let dismiss: () -> Void + + init(context: AccountContext, + subject: PremiumPrivacyScreen.Subject, + action: @escaping () -> Void, + openPremiumIntro: @escaping () -> Void, + dismiss: @escaping () -> Void + ) { + self.context = context + self.subject = subject + self.action = action + self.openPremiumIntro = openPremiumIntro + self.dismiss = dismiss + } + + static func ==(lhs: SheetContent, rhs: SheetContent) -> Bool { + if lhs.context !== rhs.context { + return false + } + if lhs.subject != rhs.subject { + return false + } + return true + } + + final class State: ComponentState { + var cachedCloseImage: (UIImage, PresentationTheme)? + var cachedIconImage: UIImage? + } + + func makeState() -> State { + return State() + } + + static var body: Body { + let closeButton = Child(Button.self) + + let iconBackground = Child(Image.self) + let icon = Child(BundleIconComponent.self) + + let title = Child(BalancedTextComponent.self) + let text = Child(BalancedTextComponent.self) + let actionButton = Child(SolidRoundedButtonComponent.self) + + let orLeftLine = Child(Rectangle.self) + let orRightLine = Child(Rectangle.self) + let orText = Child(MultilineTextComponent.self) + + let premiumTitle = Child(BalancedTextComponent.self) + let premiumText = Child(BalancedTextComponent.self) + let premiumButton = Child(SolidRoundedButtonComponent.self) + + return { context in + let environment = context.environment[EnvironmentType.self] + let component = context.component + let state = context.state + + let theme = environment.theme + let strings = environment.strings + + let sideInset: CGFloat = 16.0 + environment.safeInsets.left + let textSideInset: CGFloat = 32.0 + environment.safeInsets.left + + let titleFont = Font.semibold(20.0) + let textFont = Font.regular(15.0) + let boldTextFont = Font.semibold(15.0) + let textColor = theme.actionSheet.primaryTextColor + let secondaryTextColor = theme.actionSheet.secondaryTextColor + let linkColor = theme.actionSheet.controlAccentColor + let markdownAttributes = MarkdownAttributes(body: MarkdownAttributeSet(font: textFont, textColor: textColor), bold: MarkdownAttributeSet(font: boldTextFont, textColor: textColor), link: MarkdownAttributeSet(font: textFont, textColor: linkColor), linkAttribute: { contents in + return (TelegramTextAttributes.URL, contents) + }) + + let iconName: String + let titleString: String + let textString: String + let buttonTitle: String + let premiumString: String + + let premiumTitleString = "Upgrade to Premium" + let premiumButtonTitle = "Subscribe to Telegram Premium" + + let peerName = "Name" + switch component.subject { + case .presence: + iconName = "Premium/PrivacyPresence" + titleString = "Show Your Last Seen" + textString = "To see **\(peerName)'s** Last Seen time, either start showing your own Last Seen Time..." + buttonTitle = "Show My Last Seen to Everyone" + premiumString = "Subscription will let you see **\(peerName)'s** Last Seen status without showing yours." + case .readTime: + iconName = "Premium/PrivacyReadTime" + titleString = "Show Your Read Date" + textString = "To see when **\(peerName)** read the message, either start showing your own read time:" + buttonTitle = "Show My Read Time" + premiumString = "Subscription will let you see **\(peerName)'s** read time without showing yours." + } + + let spacing: CGFloat = 8.0 + var contentSize = CGSize(width: context.availableSize.width, height: 32.0) + + let closeImage: UIImage + if let (image, theme) = state.cachedCloseImage, theme === environment.theme { + closeImage = image + } else { + closeImage = generateCloseButtonImage(backgroundColor: UIColor(rgb: 0x808084, alpha: 0.1), foregroundColor: theme.actionSheet.inputClearButtonColor)! + state.cachedCloseImage = (closeImage, theme) + } + + let closeButton = closeButton.update( + component: Button( + content: AnyComponent(Image(image: closeImage)), + action: { [weak component] in + component?.dismiss() + } + ), + availableSize: CGSize(width: 30.0, height: 30.0), + transition: .immediate + ) + context.add(closeButton + .position(CGPoint(x: context.availableSize.width - environment.safeInsets.left - closeButton.size.width, y: 28.0)) + ) + + let iconSize = CGSize(width: 90.0, height: 90.0) + let gradientImage: UIImage + if let current = state.cachedIconImage { + gradientImage = current + } else { + gradientImage = generateFilledCircleImage(diameter: iconSize.width, color: theme.actionSheet.controlAccentColor)! + context.state.cachedIconImage = gradientImage + } + + let iconBackground = iconBackground.update( + component: Image(image: gradientImage), + availableSize: iconSize, + transition: .immediate + ) + context.add(iconBackground + .position(CGPoint(x: context.availableSize.width / 2.0, y: contentSize.height + iconBackground.size.height / 2.0)) + ) + + let icon = icon.update( + component: BundleIconComponent(name: iconName, tintColor: .white), + availableSize: CGSize(width: 70.0, height: 70.0), + transition: .immediate + ) + context.add(icon + .position(CGPoint(x: context.availableSize.width / 2.0, y: contentSize.height + iconBackground.size.height / 2.0)) + ) + contentSize.height += iconSize.height + contentSize.height += spacing + 5.0 + + let title = title.update( + component: BalancedTextComponent( + text: .plain(NSAttributedString(string: titleString, font: titleFont, textColor: textColor)), + horizontalAlignment: .center, + maximumNumberOfLines: 0, + lineSpacing: 0.1 + ), + availableSize: CGSize(width: context.availableSize.width - textSideInset * 2.0, height: context.availableSize.height), + transition: .immediate + ) + context.add(title + .position(CGPoint(x: context.availableSize.width / 2.0, y: contentSize.height + title.size.height / 2.0)) + ) + contentSize.height += title.size.height + contentSize.height += spacing + + let text = text.update( + component: BalancedTextComponent( + text: .markdown(text: textString, attributes: markdownAttributes), + horizontalAlignment: .center, + maximumNumberOfLines: 0, + lineSpacing: 0.2 + ), + availableSize: CGSize(width: context.availableSize.width - textSideInset * 2.0, height: context.availableSize.height), + transition: .immediate + ) + context.add(text + .position(CGPoint(x: context.availableSize.width / 2.0, y: contentSize.height + text.size.height / 2.0)) + ) + contentSize.height += text.size.height + contentSize.height += spacing + 5.0 + + let actionButton = actionButton.update( + component: SolidRoundedButtonComponent( + title: buttonTitle, + theme: SolidRoundedButtonComponent.Theme( + backgroundColor: theme.list.itemCheckColors.fillColor, + backgroundColors: [], + foregroundColor: theme.list.itemCheckColors.foregroundColor + ), + font: .bold, + fontSize: 17.0, + height: 50.0, + cornerRadius: 10.0, + gloss: false, + iconName: nil, + animationName: nil, + iconPosition: .left, + action: { + component.action() + component.dismiss() + } + ), + availableSize: CGSize(width: context.availableSize.width - sideInset * 2.0, height: 50.0), + transition: context.transition + ) + context.add(actionButton + .position(CGPoint(x: context.availableSize.width / 2.0, y: contentSize.height + actionButton.size.height / 2.0)) + ) + contentSize.height += actionButton.size.height + contentSize.height += 22.0 + + let orText = orText.update( + component: MultilineTextComponent(text: .plain(NSAttributedString(string: strings.ChannelBoost_Or, font: Font.regular(15.0), textColor: secondaryTextColor, paragraphAlignment: .center))), + availableSize: CGSize(width: context.availableSize.width - sideInset * 2.0, height: context.availableSize.height), + transition: .immediate + ) + context.add(orText + .position(CGPoint(x: context.availableSize.width / 2.0, y: contentSize.height + orText.size.height / 2.0)) + ) + + let orLeftLine = orLeftLine.update( + component: Rectangle(color: theme.list.itemBlocksSeparatorColor.withAlphaComponent(0.3)), + availableSize: CGSize(width: 90.0, height: 1.0 - UIScreenPixel), + transition: .immediate + ) + context.add(orLeftLine + .position(CGPoint(x: context.availableSize.width / 2.0 - orText.size.width / 2.0 - 11.0 - 45.0, y: contentSize.height + orText.size.height / 2.0)) + ) + + let orRightLine = orRightLine.update( + component: Rectangle(color: theme.list.itemBlocksSeparatorColor.withAlphaComponent(0.3)), + availableSize: CGSize(width: 90.0, height: 1.0 - UIScreenPixel), + transition: .immediate + ) + context.add(orRightLine + .position(CGPoint(x: context.availableSize.width / 2.0 + orText.size.width / 2.0 + 11.0 + 45.0, y: contentSize.height + orText.size.height / 2.0)) + ) + contentSize.height += orText.size.height + contentSize.height += 18.0 + + let premiumTitle = premiumTitle.update( + component: BalancedTextComponent( + text: .plain(NSAttributedString(string: premiumTitleString, font: titleFont, textColor: textColor)), + horizontalAlignment: .center, + maximumNumberOfLines: 0, + lineSpacing: 0.1 + ), + availableSize: CGSize(width: context.availableSize.width - textSideInset * 2.0, height: context.availableSize.height), + transition: .immediate + ) + context.add(premiumTitle + .position(CGPoint(x: context.availableSize.width / 2.0, y: contentSize.height + premiumTitle.size.height / 2.0)) + ) + contentSize.height += premiumTitle.size.height + contentSize.height += spacing + + let premiumText = premiumText.update( + component: BalancedTextComponent( + text: .markdown(text: premiumString, attributes: markdownAttributes), + horizontalAlignment: .center, + maximumNumberOfLines: 0, + lineSpacing: 0.2 + ), + availableSize: CGSize(width: context.availableSize.width - textSideInset * 2.0, height: context.availableSize.height), + transition: .immediate + ) + context.add(premiumText + .position(CGPoint(x: context.availableSize.width / 2.0, y: contentSize.height + premiumText.size.height / 2.0)) + ) + contentSize.height += premiumText.size.height + contentSize.height += spacing + 5.0 + + let premiumButton = premiumButton.update( + component: SolidRoundedButtonComponent( + title: premiumButtonTitle, + theme: SolidRoundedButtonComponent.Theme( + backgroundColor: .black, + backgroundColors: [ + UIColor(rgb: 0x0077ff), + UIColor(rgb: 0x6b93ff), + UIColor(rgb: 0x8878ff), + UIColor(rgb: 0xe46ace) + ], + foregroundColor: .white + ), + font: .bold, + fontSize: 17.0, + height: 50.0, + cornerRadius: 10.0, + gloss: false, + iconName: nil, + animationName: nil, + iconPosition: .left, + action: { + component.openPremiumIntro() + component.dismiss() + } + ), + availableSize: CGSize(width: context.availableSize.width - sideInset * 2.0, height: 50.0), + transition: context.transition + ) + context.add(premiumButton + .position(CGPoint(x: context.availableSize.width / 2.0, y: contentSize.height + premiumButton.size.height / 2.0)) + ) + contentSize.height += premiumButton.size.height + contentSize.height += 14.0 + + contentSize.height += environment.safeInsets.bottom + + return contentSize + } + } +} + +private final class SheetContainerComponent: CombinedComponent { + typealias EnvironmentType = ViewControllerComponentContainer.Environment + + let context: AccountContext + let subject: PremiumPrivacyScreen.Subject + let action: () -> Void + let openPremiumIntro: () -> Void + + init( + context: AccountContext, + subject: PremiumPrivacyScreen.Subject, + action: @escaping () -> Void, + openPremiumIntro: @escaping () -> Void + ) { + self.context = context + self.subject = subject + self.action = action + self.openPremiumIntro = openPremiumIntro + } + + static func ==(lhs: SheetContainerComponent, rhs: SheetContainerComponent) -> Bool { + if lhs.context !== rhs.context { + return false + } + if lhs.subject != rhs.subject { + return false + } + return true + } + + static var body: Body { + let sheet = Child(SheetComponent.self) + let animateOut = StoredActionSlot(Action.self) + + let sheetExternalState = SheetComponent.ExternalState() + + return { context in + let environment = context.environment[EnvironmentType.self] + + let controller = environment.controller + + let sheet = sheet.update( + component: SheetComponent( + content: AnyComponent(SheetContent( + context: context.component.context, + subject: context.component.subject, + action: context.component.action, + openPremiumIntro: context.component.openPremiumIntro, + dismiss: { + animateOut.invoke(Action { _ in + if let controller = controller() { + controller.dismiss(completion: nil) + } + }) + } + )), + backgroundColor: .color(environment.theme.actionSheet.opaqueItemBackgroundColor), + followContentSizeChanges: true, + externalState: sheetExternalState, + animateOut: animateOut + ), + environment: { + environment + SheetComponentEnvironment( + isDisplaying: environment.value.isVisible, + isCentered: environment.metrics.widthClass == .regular, + hasInputHeight: !environment.inputHeight.isZero, + regularMetricsSize: CGSize(width: 430.0, height: 900.0), + dismiss: { animated in + if animated { + animateOut.invoke(Action { _ in + if let controller = controller() { + controller.dismiss(completion: nil) + } + }) + } else { + if let controller = controller() { + controller.dismiss(completion: nil) + } + } + } + ) + }, + availableSize: context.availableSize, + transition: context.transition + ) + + context.add(sheet + .position(CGPoint(x: context.availableSize.width / 2.0, y: context.availableSize.height / 2.0)) + ) + + if let controller = controller(), !controller.automaticallyControlPresentationContextLayout { + let layout = ContainerViewLayout( + size: context.availableSize, + metrics: environment.metrics, + deviceMetrics: environment.deviceMetrics, + intrinsicInsets: UIEdgeInsets(top: 0.0, left: 0.0, bottom: max(environment.safeInsets.bottom, sheetExternalState.contentHeight), right: 0.0), + safeInsets: UIEdgeInsets(top: 0.0, left: environment.safeInsets.left, bottom: 0.0, right: environment.safeInsets.right), + additionalInsets: .zero, + statusBarHeight: environment.statusBarHeight, + inputHeight: nil, + inputHeightIsInteractivellyChanging: false, + inVoiceOver: false + ) + controller.presentationContext.containerLayoutUpdated(layout, transition: context.transition.containedViewLayoutTransition) + } + + return context.availableSize + } + } +} + + +public class PremiumPrivacyScreen: ViewControllerComponentContainer { + public enum Subject: Equatable { + case presence + case readTime + } + + private let context: AccountContext + private let subject: PremiumPrivacyScreen.Subject + private var action: (() -> Void)? + private var openPremiumIntro: (() -> Void)? + + public init( + context: AccountContext, + subject: PremiumPrivacyScreen.Subject, + action: @escaping () -> Void, + openPremiumIntro: @escaping () -> Void + ) { + self.context = context + self.subject = subject + self.action = action + self.openPremiumIntro = openPremiumIntro + + super.init( + context: context, + component: SheetContainerComponent( + context: context, + subject: subject, + action: action, + openPremiumIntro: openPremiumIntro + ), + navigationBarAppearance: .none, + statusBarStyle: .ignore, + theme: .default + ) + + self.navigationPresentation = .flatModal + } + + required public init(coder aDecoder: NSCoder) { + fatalError("init(coder:) has not been implemented") + } + + public override func viewDidLoad() { + super.viewDidLoad() + + self.view.disablesInteractiveModalDismiss = true + } + + public func dismissAnimated() { + if let view = self.node.hostView.findTaggedView(tag: SheetComponent.View.Tag()) as? SheetComponent.View { + view.dismissAnimated() + } + } +} diff --git a/submodules/SettingsUI/Sources/BubbleSettings/BubbleSettingsController.swift b/submodules/SettingsUI/Sources/BubbleSettings/BubbleSettingsController.swift index 75b5b852b3..2b338c21b5 100644 --- a/submodules/SettingsUI/Sources/BubbleSettings/BubbleSettingsController.swift +++ b/submodules/SettingsUI/Sources/BubbleSettings/BubbleSettingsController.swift @@ -170,20 +170,20 @@ private final class BubbleSettingsControllerNode: ASDisplayNode, UIScrollViewDel messages[replyMessageId] = Message(stableId: 3, stableVersion: 0, id: replyMessageId, globallyUniqueId: nil, groupingKey: nil, groupInfo: nil, threadId: nil, timestamp: 66000, flags: [], tags: [], globalTags: [], localTags: [], forwardInfo: nil, author: peers[otherPeerId], text: self.presentationData.strings.Appearance_ThemePreview_Chat_1_Text, attributes: [], media: [], peers: peers, associatedMessages: SimpleDictionary(), associatedMessageIds: [], associatedMedia: [:], associatedThreadInfo: nil, associatedStories: [:]) let message1 = Message(stableId: 4, stableVersion: 0, id: MessageId(peerId: otherPeerId, namespace: 0, id: 4), globallyUniqueId: nil, groupingKey: nil, groupInfo: nil, threadId: nil, timestamp: 66003, flags: [], tags: [], globalTags: [], localTags: [], forwardInfo: nil, author: peers[otherPeerId], text: self.presentationData.strings.Appearance_ThemePreview_Chat_3_Text, attributes: [], media: [], peers: peers, associatedMessages: messages, associatedMessageIds: [], associatedMedia: [:], associatedThreadInfo: nil, associatedStories: [:]) - items.append(self.context.sharedContext.makeChatMessagePreviewItem(context: self.context, messages: [message1], theme: self.presentationData.theme, strings: self.presentationData.strings, wallpaper: self.presentationData.chatWallpaper, fontSize: self.presentationData.chatFontSize, chatBubbleCorners: self.presentationData.chatBubbleCorners, dateTimeFormat: self.presentationData.dateTimeFormat, nameOrder: self.presentationData.nameDisplayOrder, forcedResourceStatus: nil, tapMessage: nil, clickThroughMessage: nil, backgroundNode: self.chatBackgroundNode, availableReactions: nil, accountPeer: nil, isCentered: false, isPreview: true)) + items.append(self.context.sharedContext.makeChatMessagePreviewItem(context: self.context, messages: [message1], theme: self.presentationData.theme, strings: self.presentationData.strings, wallpaper: self.presentationData.chatWallpaper, fontSize: self.presentationData.chatFontSize, chatBubbleCorners: self.presentationData.chatBubbleCorners, dateTimeFormat: self.presentationData.dateTimeFormat, nameOrder: self.presentationData.nameDisplayOrder, forcedResourceStatus: nil, tapMessage: nil, clickThroughMessage: nil, backgroundNode: self.chatBackgroundNode, availableReactions: nil, accountPeer: nil, isCentered: false, isPreview: true, isStandalone: false)) let message2 = Message(stableId: 3, stableVersion: 0, id: MessageId(peerId: peerId, namespace: 0, id: 3), globallyUniqueId: nil, groupingKey: nil, groupInfo: nil, threadId: nil, timestamp: 66002, flags: [.Incoming], tags: [], globalTags: [], localTags: [], forwardInfo: nil, author: peers[peerId], text: self.presentationData.strings.Appearance_ThemePreview_Chat_2_Text, attributes: [ReplyMessageAttribute(messageId: replyMessageId, threadMessageId: nil, quote: nil, isQuote: false)], media: [], peers: peers, associatedMessages: messages, associatedMessageIds: [], associatedMedia: [:], associatedThreadInfo: nil, associatedStories: [:]) - items.append(self.context.sharedContext.makeChatMessagePreviewItem(context: self.context, messages: [message2], theme: self.presentationData.theme, strings: self.presentationData.strings, wallpaper: self.presentationData.chatWallpaper, fontSize: self.presentationData.chatFontSize, chatBubbleCorners: self.presentationData.chatBubbleCorners, dateTimeFormat: self.presentationData.dateTimeFormat, nameOrder: self.presentationData.nameDisplayOrder, forcedResourceStatus: nil, tapMessage: nil, clickThroughMessage: nil, backgroundNode: self.chatBackgroundNode, availableReactions: nil, accountPeer: nil, isCentered: false, isPreview: true)) + items.append(self.context.sharedContext.makeChatMessagePreviewItem(context: self.context, messages: [message2], theme: self.presentationData.theme, strings: self.presentationData.strings, wallpaper: self.presentationData.chatWallpaper, fontSize: self.presentationData.chatFontSize, chatBubbleCorners: self.presentationData.chatBubbleCorners, dateTimeFormat: self.presentationData.dateTimeFormat, nameOrder: self.presentationData.nameDisplayOrder, forcedResourceStatus: nil, tapMessage: nil, clickThroughMessage: nil, backgroundNode: self.chatBackgroundNode, availableReactions: nil, accountPeer: nil, isCentered: false, isPreview: true, isStandalone: false)) let waveformBase64 = "DAAOAAkACQAGAAwADwAMABAADQAPABsAGAALAA0AGAAfABoAHgATABgAGQAYABQADAAVABEAHwANAA0ACQAWABkACQAOAAwACQAfAAAAGQAVAAAAEwATAAAACAAfAAAAHAAAABwAHwAAABcAGQAAABQADgAAABQAHwAAAB8AHwAAAAwADwAAAB8AEwAAABoAFwAAAB8AFAAAAAAAHwAAAAAAHgAAAAAAHwAAAAAAHwAAAAAAHwAAAAAAHwAAAAAAHwAAAAAAAAA=" let voiceAttributes: [TelegramMediaFileAttribute] = [.Audio(isVoice: true, duration: 23, title: nil, performer: nil, waveform: Data(base64Encoded: waveformBase64)!)] let voiceMedia = TelegramMediaFile(fileId: MediaId(namespace: 0, id: 0), partialReference: nil, resource: LocalFileMediaResource(fileId: 0), previewRepresentations: [], videoThumbnails: [], immediateThumbnailData: nil, mimeType: "audio/ogg", size: 0, attributes: voiceAttributes) let message3 = Message(stableId: 1, stableVersion: 0, id: MessageId(peerId: peerId, namespace: 0, id: 1), globallyUniqueId: nil, groupingKey: nil, groupInfo: nil, threadId: nil, timestamp: 66001, flags: [.Incoming], tags: [], globalTags: [], localTags: [], forwardInfo: nil, author: peers[peerId], text: "", attributes: [], media: [voiceMedia], peers: peers, associatedMessages: messages, associatedMessageIds: [], associatedMedia: [:], associatedThreadInfo: nil, associatedStories: [:]) - items.append(self.context.sharedContext.makeChatMessagePreviewItem(context: self.context, messages: [message3], theme: self.presentationData.theme, strings: self.presentationData.strings, wallpaper: self.presentationData.chatWallpaper, fontSize: self.presentationData.chatFontSize, chatBubbleCorners: self.presentationData.chatBubbleCorners, dateTimeFormat: self.presentationData.dateTimeFormat, nameOrder: self.presentationData.nameDisplayOrder, forcedResourceStatus: FileMediaResourceStatus(mediaStatus: .playbackStatus(.paused), fetchStatus: .Local), tapMessage: nil, clickThroughMessage: nil, backgroundNode: self.chatBackgroundNode, availableReactions: nil, accountPeer: nil, isCentered: false, isPreview: true)) + items.append(self.context.sharedContext.makeChatMessagePreviewItem(context: self.context, messages: [message3], theme: self.presentationData.theme, strings: self.presentationData.strings, wallpaper: self.presentationData.chatWallpaper, fontSize: self.presentationData.chatFontSize, chatBubbleCorners: self.presentationData.chatBubbleCorners, dateTimeFormat: self.presentationData.dateTimeFormat, nameOrder: self.presentationData.nameDisplayOrder, forcedResourceStatus: FileMediaResourceStatus(mediaStatus: .playbackStatus(.paused), fetchStatus: .Local), tapMessage: nil, clickThroughMessage: nil, backgroundNode: self.chatBackgroundNode, availableReactions: nil, accountPeer: nil, isCentered: false, isPreview: true, isStandalone: false)) let message4 = Message(stableId: 2, stableVersion: 0, id: MessageId(peerId: otherPeerId, namespace: 0, id: 2), globallyUniqueId: nil, groupingKey: nil, groupInfo: nil, threadId: nil, timestamp: 66001, flags: [], tags: [], globalTags: [], localTags: [], forwardInfo: nil, author: peers[otherPeerId], text: self.presentationData.strings.Appearance_ThemePreview_Chat_1_Text, attributes: [], media: [], peers: peers, associatedMessages: messages, associatedMessageIds: [], associatedMedia: [:], associatedThreadInfo: nil, associatedStories: [:]) - items.append(self.context.sharedContext.makeChatMessagePreviewItem(context: self.context, messages: [message4], theme: self.presentationData.theme, strings: self.presentationData.strings, wallpaper: self.presentationData.chatWallpaper, fontSize: self.presentationData.chatFontSize, chatBubbleCorners: self.presentationData.chatBubbleCorners, dateTimeFormat: self.presentationData.dateTimeFormat, nameOrder: self.presentationData.nameDisplayOrder, forcedResourceStatus: nil, tapMessage: nil, clickThroughMessage: nil, backgroundNode: self.chatBackgroundNode, availableReactions: nil, accountPeer: nil, isCentered: false, isPreview: true)) + items.append(self.context.sharedContext.makeChatMessagePreviewItem(context: self.context, messages: [message4], theme: self.presentationData.theme, strings: self.presentationData.strings, wallpaper: self.presentationData.chatWallpaper, fontSize: self.presentationData.chatFontSize, chatBubbleCorners: self.presentationData.chatBubbleCorners, dateTimeFormat: self.presentationData.dateTimeFormat, nameOrder: self.presentationData.nameDisplayOrder, forcedResourceStatus: nil, tapMessage: nil, clickThroughMessage: nil, backgroundNode: self.chatBackgroundNode, availableReactions: nil, accountPeer: nil, isCentered: false, isPreview: true, isStandalone: false)) let width: CGFloat if case .regular = layout.metrics.widthClass { diff --git a/submodules/SettingsUI/Sources/Privacy and Security/ForwardPrivacyChatPreviewItem.swift b/submodules/SettingsUI/Sources/Privacy and Security/ForwardPrivacyChatPreviewItem.swift index c4190b3f5a..09cf8cd621 100644 --- a/submodules/SettingsUI/Sources/Privacy and Security/ForwardPrivacyChatPreviewItem.swift +++ b/submodules/SettingsUI/Sources/Privacy and Security/ForwardPrivacyChatPreviewItem.swift @@ -149,7 +149,7 @@ class ForwardPrivacyChatPreviewItemNode: ListViewItemNode { let forwardInfo = MessageForwardInfo(author: item.linkEnabled ? peers[peerId] : nil, source: nil, sourceMessageId: nil, date: 0, authorSignature: item.linkEnabled ? nil : item.peerName, psaType: nil, flags: []) - let messageItem = item.context.sharedContext.makeChatMessagePreviewItem(context: item.context, messages: [Message(stableId: 1, stableVersion: 0, id: MessageId(peerId: peerId, namespace: 0, id: 1), globallyUniqueId: nil, groupingKey: nil, groupInfo: nil, threadId: nil, timestamp: 66000, flags: [.Incoming], tags: [], globalTags: [], localTags: [], forwardInfo: forwardInfo, author: nil, text: item.strings.Privacy_Forwards_PreviewMessageText, attributes: [], media: [], peers: peers, associatedMessages: messages, associatedMessageIds: [], associatedMedia: [:], associatedThreadInfo: nil, associatedStories: [:])], theme: item.theme, strings: item.strings, wallpaper: item.wallpaper, fontSize: item.fontSize, chatBubbleCorners: item.chatBubbleCorners, dateTimeFormat: item.dateTimeFormat, nameOrder: item.nameDisplayOrder, forcedResourceStatus: nil, tapMessage: nil, clickThroughMessage: nil, backgroundNode: currentBackgroundNode, availableReactions: nil, accountPeer: nil, isCentered: false, isPreview: true) + let messageItem = item.context.sharedContext.makeChatMessagePreviewItem(context: item.context, messages: [Message(stableId: 1, stableVersion: 0, id: MessageId(peerId: peerId, namespace: 0, id: 1), globallyUniqueId: nil, groupingKey: nil, groupInfo: nil, threadId: nil, timestamp: 66000, flags: [.Incoming], tags: [], globalTags: [], localTags: [], forwardInfo: forwardInfo, author: nil, text: item.strings.Privacy_Forwards_PreviewMessageText, attributes: [], media: [], peers: peers, associatedMessages: messages, associatedMessageIds: [], associatedMedia: [:], associatedThreadInfo: nil, associatedStories: [:])], theme: item.theme, strings: item.strings, wallpaper: item.wallpaper, fontSize: item.fontSize, chatBubbleCorners: item.chatBubbleCorners, dateTimeFormat: item.dateTimeFormat, nameOrder: item.nameDisplayOrder, forcedResourceStatus: nil, tapMessage: nil, clickThroughMessage: nil, backgroundNode: currentBackgroundNode, availableReactions: nil, accountPeer: nil, isCentered: false, isPreview: true, isStandalone: false) var node: ListViewItemNode? if let current = currentNode { diff --git a/submodules/SettingsUI/Sources/Privacy and Security/SelectivePrivacySettingsController.swift b/submodules/SettingsUI/Sources/Privacy and Security/SelectivePrivacySettingsController.swift index f5f440f7a3..2e4fe1d215 100644 --- a/submodules/SettingsUI/Sources/Privacy and Security/SelectivePrivacySettingsController.swift +++ b/submodules/SettingsUI/Sources/Privacy and Security/SelectivePrivacySettingsController.swift @@ -849,11 +849,11 @@ private func selectivePrivacySettingsControllerEntries(presentationData: Present if case .presence = kind, let peer { //TODO:localize entries.append(.hideReadTime(presentationData.theme, "Hide Read Time", state.hideReadTimeEnabled == true)) - entries.append(.hideReadTimeInfo(presentationData.theme, "Do not show the time when you read a message to people you hid your last seen from.")) + entries.append(.hideReadTimeInfo(presentationData.theme, "Do not show the time when you read a message to people you hid your last seen from. If you turn this on, their read time will also be hidden from you.\nThis does not affect group chats.")) if !peer.isPremium { entries.append(.subscribeToPremium(presentationData.theme, "Subscribe to Telegram Premium")) - entries.append(.subscribeToPremiumInfo(presentationData.theme, "If you subscribe to Telegram Premium, you will still see other users' last seen and read time even if you hid yours from them (unless they specifically restricted it).")) + entries.append(.subscribeToPremiumInfo(presentationData.theme, "It you subscribe to Telegram Premium, you will still see other users' last seen and read time even if you hid yours from them (unless they specifically restricted it).")) } } diff --git a/submodules/SettingsUI/Sources/Text Size/TextSizeSelectionController.swift b/submodules/SettingsUI/Sources/Text Size/TextSizeSelectionController.swift index 1e9f8351ef..161b91026a 100644 --- a/submodules/SettingsUI/Sources/Text Size/TextSizeSelectionController.swift +++ b/submodules/SettingsUI/Sources/Text Size/TextSizeSelectionController.swift @@ -435,20 +435,20 @@ private final class TextSizeSelectionControllerNode: ASDisplayNode, UIScrollView messages[replyMessageId] = Message(stableId: 3, stableVersion: 0, id: replyMessageId, globallyUniqueId: nil, groupingKey: nil, groupInfo: nil, threadId: nil, timestamp: 66000, flags: [], tags: [], globalTags: [], localTags: [], forwardInfo: nil, author: peers[otherPeerId], text: self.presentationData.strings.Appearance_ThemePreview_Chat_1_Text, attributes: [], media: [], peers: peers, associatedMessages: SimpleDictionary(), associatedMessageIds: [], associatedMedia: [:], associatedThreadInfo: nil, associatedStories: [:]) let message1 = Message(stableId: 4, stableVersion: 0, id: MessageId(peerId: otherPeerId, namespace: 0, id: 4), globallyUniqueId: nil, groupingKey: nil, groupInfo: nil, threadId: nil, timestamp: 66003, flags: [], tags: [], globalTags: [], localTags: [], forwardInfo: nil, author: peers[otherPeerId], text: self.presentationData.strings.Appearance_ThemePreview_Chat_3_Text, attributes: [], media: [], peers: peers, associatedMessages: messages, associatedMessageIds: [], associatedMedia: [:], associatedThreadInfo: nil, associatedStories: [:]) - items.append(self.context.sharedContext.makeChatMessagePreviewItem(context: self.context, messages: [message1], theme: self.presentationData.theme, strings: self.presentationData.strings, wallpaper: self.presentationData.chatWallpaper, fontSize: self.presentationData.chatFontSize, chatBubbleCorners: self.presentationData.chatBubbleCorners, dateTimeFormat: self.presentationData.dateTimeFormat, nameOrder: self.presentationData.nameDisplayOrder, forcedResourceStatus: nil, tapMessage: nil, clickThroughMessage: nil, backgroundNode: self.chatBackgroundNode, availableReactions: nil, accountPeer: nil, isCentered: false, isPreview: true)) + items.append(self.context.sharedContext.makeChatMessagePreviewItem(context: self.context, messages: [message1], theme: self.presentationData.theme, strings: self.presentationData.strings, wallpaper: self.presentationData.chatWallpaper, fontSize: self.presentationData.chatFontSize, chatBubbleCorners: self.presentationData.chatBubbleCorners, dateTimeFormat: self.presentationData.dateTimeFormat, nameOrder: self.presentationData.nameDisplayOrder, forcedResourceStatus: nil, tapMessage: nil, clickThroughMessage: nil, backgroundNode: self.chatBackgroundNode, availableReactions: nil, accountPeer: nil, isCentered: false, isPreview: true, isStandalone: false)) let message2 = Message(stableId: 3, stableVersion: 0, id: MessageId(peerId: peerId, namespace: 0, id: 3), globallyUniqueId: nil, groupingKey: nil, groupInfo: nil, threadId: nil, timestamp: 66002, flags: [.Incoming], tags: [], globalTags: [], localTags: [], forwardInfo: nil, author: peers[peerId], text: self.presentationData.strings.Appearance_ThemePreview_Chat_2_Text, attributes: [ReplyMessageAttribute(messageId: replyMessageId, threadMessageId: nil, quote: nil, isQuote: false)], media: [], peers: peers, associatedMessages: messages, associatedMessageIds: [], associatedMedia: [:], associatedThreadInfo: nil, associatedStories: [:]) - items.append(self.context.sharedContext.makeChatMessagePreviewItem(context: self.context, messages: [message2], theme: self.presentationData.theme, strings: self.presentationData.strings, wallpaper: self.presentationData.chatWallpaper, fontSize: self.presentationData.chatFontSize, chatBubbleCorners: self.presentationData.chatBubbleCorners, dateTimeFormat: self.presentationData.dateTimeFormat, nameOrder: self.presentationData.nameDisplayOrder, forcedResourceStatus: nil, tapMessage: nil, clickThroughMessage: nil, backgroundNode: self.chatBackgroundNode, availableReactions: nil, accountPeer: nil, isCentered: false, isPreview: true)) + items.append(self.context.sharedContext.makeChatMessagePreviewItem(context: self.context, messages: [message2], theme: self.presentationData.theme, strings: self.presentationData.strings, wallpaper: self.presentationData.chatWallpaper, fontSize: self.presentationData.chatFontSize, chatBubbleCorners: self.presentationData.chatBubbleCorners, dateTimeFormat: self.presentationData.dateTimeFormat, nameOrder: self.presentationData.nameDisplayOrder, forcedResourceStatus: nil, tapMessage: nil, clickThroughMessage: nil, backgroundNode: self.chatBackgroundNode, availableReactions: nil, accountPeer: nil, isCentered: false, isPreview: true, isStandalone: false)) let waveformBase64 = "DAAOAAkACQAGAAwADwAMABAADQAPABsAGAALAA0AGAAfABoAHgATABgAGQAYABQADAAVABEAHwANAA0ACQAWABkACQAOAAwACQAfAAAAGQAVAAAAEwATAAAACAAfAAAAHAAAABwAHwAAABcAGQAAABQADgAAABQAHwAAAB8AHwAAAAwADwAAAB8AEwAAABoAFwAAAB8AFAAAAAAAHwAAAAAAHgAAAAAAHwAAAAAAHwAAAAAAHwAAAAAAHwAAAAAAHwAAAAAAAAA=" let voiceAttributes: [TelegramMediaFileAttribute] = [.Audio(isVoice: true, duration: 23, title: nil, performer: nil, waveform: Data(base64Encoded: waveformBase64)!)] let voiceMedia = TelegramMediaFile(fileId: MediaId(namespace: 0, id: 0), partialReference: nil, resource: LocalFileMediaResource(fileId: 0), previewRepresentations: [], videoThumbnails: [], immediateThumbnailData: nil, mimeType: "audio/ogg", size: 0, attributes: voiceAttributes) let message3 = Message(stableId: 1, stableVersion: 0, id: MessageId(peerId: peerId, namespace: 0, id: 1), globallyUniqueId: nil, groupingKey: nil, groupInfo: nil, threadId: nil, timestamp: 66001, flags: [.Incoming], tags: [], globalTags: [], localTags: [], forwardInfo: nil, author: peers[peerId], text: "", attributes: [], media: [voiceMedia], peers: peers, associatedMessages: messages, associatedMessageIds: [], associatedMedia: [:], associatedThreadInfo: nil, associatedStories: [:]) - items.append(self.context.sharedContext.makeChatMessagePreviewItem(context: self.context, messages: [message3], theme: self.presentationData.theme, strings: self.presentationData.strings, wallpaper: self.presentationData.chatWallpaper, fontSize: self.presentationData.chatFontSize, chatBubbleCorners: self.presentationData.chatBubbleCorners, dateTimeFormat: self.presentationData.dateTimeFormat, nameOrder: self.presentationData.nameDisplayOrder, forcedResourceStatus: FileMediaResourceStatus(mediaStatus: .playbackStatus(.paused), fetchStatus: .Local), tapMessage: nil, clickThroughMessage: nil, backgroundNode: self.chatBackgroundNode, availableReactions: nil, accountPeer: nil, isCentered: false, isPreview: true)) + items.append(self.context.sharedContext.makeChatMessagePreviewItem(context: self.context, messages: [message3], theme: self.presentationData.theme, strings: self.presentationData.strings, wallpaper: self.presentationData.chatWallpaper, fontSize: self.presentationData.chatFontSize, chatBubbleCorners: self.presentationData.chatBubbleCorners, dateTimeFormat: self.presentationData.dateTimeFormat, nameOrder: self.presentationData.nameDisplayOrder, forcedResourceStatus: FileMediaResourceStatus(mediaStatus: .playbackStatus(.paused), fetchStatus: .Local), tapMessage: nil, clickThroughMessage: nil, backgroundNode: self.chatBackgroundNode, availableReactions: nil, accountPeer: nil, isCentered: false, isPreview: true, isStandalone: false)) let message4 = Message(stableId: 2, stableVersion: 0, id: MessageId(peerId: otherPeerId, namespace: 0, id: 2), globallyUniqueId: nil, groupingKey: nil, groupInfo: nil, threadId: nil, timestamp: 66001, flags: [], tags: [], globalTags: [], localTags: [], forwardInfo: nil, author: peers[otherPeerId], text: self.presentationData.strings.Appearance_ThemePreview_Chat_1_Text, attributes: [], media: [], peers: peers, associatedMessages: messages, associatedMessageIds: [], associatedMedia: [:], associatedThreadInfo: nil, associatedStories: [:]) - items.append(self.context.sharedContext.makeChatMessagePreviewItem(context: self.context, messages: [message4], theme: self.presentationData.theme, strings: self.presentationData.strings, wallpaper: self.presentationData.chatWallpaper, fontSize: self.presentationData.chatFontSize, chatBubbleCorners: self.presentationData.chatBubbleCorners, dateTimeFormat: self.presentationData.dateTimeFormat, nameOrder: self.presentationData.nameDisplayOrder, forcedResourceStatus: nil, tapMessage: nil, clickThroughMessage: nil, backgroundNode: self.chatBackgroundNode, availableReactions: nil, accountPeer: nil, isCentered: false, isPreview: true)) + items.append(self.context.sharedContext.makeChatMessagePreviewItem(context: self.context, messages: [message4], theme: self.presentationData.theme, strings: self.presentationData.strings, wallpaper: self.presentationData.chatWallpaper, fontSize: self.presentationData.chatFontSize, chatBubbleCorners: self.presentationData.chatBubbleCorners, dateTimeFormat: self.presentationData.dateTimeFormat, nameOrder: self.presentationData.nameDisplayOrder, forcedResourceStatus: nil, tapMessage: nil, clickThroughMessage: nil, backgroundNode: self.chatBackgroundNode, availableReactions: nil, accountPeer: nil, isCentered: false, isPreview: true, isStandalone: false)) let width: CGFloat if case .regular = layout.metrics.widthClass { diff --git a/submodules/SettingsUI/Sources/Themes/ThemePreviewControllerNode.swift b/submodules/SettingsUI/Sources/Themes/ThemePreviewControllerNode.swift index c258319120..8de914010c 100644 --- a/submodules/SettingsUI/Sources/Themes/ThemePreviewControllerNode.swift +++ b/submodules/SettingsUI/Sources/Themes/ThemePreviewControllerNode.swift @@ -619,7 +619,7 @@ final class ThemePreviewControllerNode: ASDisplayNode, UIScrollViewDelegate { sampleMessages.append(message8) items = sampleMessages.reversed().map { message in - self.context.sharedContext.makeChatMessagePreviewItem(context: self.context, messages: [message], theme: self.previewTheme, strings: self.presentationData.strings, wallpaper: self.wallpaper, fontSize: self.presentationData.chatFontSize, chatBubbleCorners: self.presentationData.chatBubbleCorners, dateTimeFormat: self.presentationData.dateTimeFormat, nameOrder: self.presentationData.nameDisplayOrder, forcedResourceStatus: !message.media.isEmpty ? FileMediaResourceStatus(mediaStatus: .playbackStatus(.paused), fetchStatus: .Local) : nil, tapMessage: nil, clickThroughMessage: nil, backgroundNode: self.wallpaperNode, availableReactions: nil, accountPeer: nil, isCentered: false, isPreview: true) + self.context.sharedContext.makeChatMessagePreviewItem(context: self.context, messages: [message], theme: self.previewTheme, strings: self.presentationData.strings, wallpaper: self.wallpaper, fontSize: self.presentationData.chatFontSize, chatBubbleCorners: self.presentationData.chatBubbleCorners, dateTimeFormat: self.presentationData.dateTimeFormat, nameOrder: self.presentationData.nameDisplayOrder, forcedResourceStatus: !message.media.isEmpty ? FileMediaResourceStatus(mediaStatus: .playbackStatus(.paused), fetchStatus: .Local) : nil, tapMessage: nil, clickThroughMessage: nil, backgroundNode: self.wallpaperNode, availableReactions: nil, accountPeer: nil, isCentered: false, isPreview: true, isStandalone: false) } let width: CGFloat diff --git a/submodules/SettingsUI/Sources/Themes/ThemeSettingsChatPreviewItem.swift b/submodules/SettingsUI/Sources/Themes/ThemeSettingsChatPreviewItem.swift index 8e4c892102..ee1a3bb395 100644 --- a/submodules/SettingsUI/Sources/Themes/ThemeSettingsChatPreviewItem.swift +++ b/submodules/SettingsUI/Sources/Themes/ThemeSettingsChatPreviewItem.swift @@ -168,7 +168,7 @@ class ThemeSettingsChatPreviewItemNode: ListViewItemNode { } let message = Message(stableId: 1, stableVersion: 0, id: MessageId(peerId: messageItem.outgoing ? otherPeerId : peerId, namespace: 0, id: 1), globallyUniqueId: nil, groupingKey: nil, groupInfo: nil, threadId: nil, timestamp: 66000, flags: messageItem.outgoing ? [] : [.Incoming], tags: [], globalTags: [], localTags: [], forwardInfo: nil, author: messageItem.outgoing ? TelegramUser(id: otherPeerId, accessHash: nil, firstName: "", lastName: "", username: nil, phone: nil, photo: [], botInfo: nil, restrictionInfo: nil, flags: [], emojiStatus: nil, usernames: [], storiesHidden: nil, nameColor: nil, backgroundEmojiId: nil, profileColor: nil, profileBackgroundEmojiId: nil) : nil, text: messageItem.text, attributes: messageItem.reply != nil ? [ReplyMessageAttribute(messageId: replyMessageId, threadMessageId: nil, quote: nil, isQuote: false)] : [], media: [], peers: peers, associatedMessages: messages, associatedMessageIds: [], associatedMedia: [:], associatedThreadInfo: nil, associatedStories: [:]) - items.append(item.context.sharedContext.makeChatMessagePreviewItem(context: item.context, messages: [message], theme: item.componentTheme, strings: item.strings, wallpaper: item.wallpaper, fontSize: item.fontSize, chatBubbleCorners: item.chatBubbleCorners, dateTimeFormat: item.dateTimeFormat, nameOrder: item.nameDisplayOrder, forcedResourceStatus: nil, tapMessage: nil, clickThroughMessage: nil, backgroundNode: currentBackgroundNode, availableReactions: nil, accountPeer: nil, isCentered: false, isPreview: true)) + items.append(item.context.sharedContext.makeChatMessagePreviewItem(context: item.context, messages: [message], theme: item.componentTheme, strings: item.strings, wallpaper: item.wallpaper, fontSize: item.fontSize, chatBubbleCorners: item.chatBubbleCorners, dateTimeFormat: item.dateTimeFormat, nameOrder: item.nameDisplayOrder, forcedResourceStatus: nil, tapMessage: nil, clickThroughMessage: nil, backgroundNode: currentBackgroundNode, availableReactions: nil, accountPeer: nil, isCentered: false, isPreview: true, isStandalone: false)) } var nodes: [ListViewItemNode] = [] diff --git a/submodules/TelegramCore/Sources/State/Serialization.swift b/submodules/TelegramCore/Sources/State/Serialization.swift index d02ad2476c..b54867d1ff 100644 --- a/submodules/TelegramCore/Sources/State/Serialization.swift +++ b/submodules/TelegramCore/Sources/State/Serialization.swift @@ -210,7 +210,7 @@ public class BoxedMessage: NSObject { public class Serialization: NSObject, MTSerialization { public func currentLayer() -> UInt { - return 170 + return 172 } public func parseMessage(_ data: Data!) -> Any! { diff --git a/submodules/TelegramUI/Components/Chat/ChatInstantVideoMessageDurationNode/BUILD b/submodules/TelegramUI/Components/Chat/ChatInstantVideoMessageDurationNode/BUILD index d3250e2fad..ef0bf22fb1 100644 --- a/submodules/TelegramUI/Components/Chat/ChatInstantVideoMessageDurationNode/BUILD +++ b/submodules/TelegramUI/Components/Chat/ChatInstantVideoMessageDurationNode/BUILD @@ -14,6 +14,7 @@ swift_library( "//submodules/SSignalKit/SwiftSignalKit", "//submodules/Display", "//submodules/MediaPlayer:UniversalMediaPlayer", + "//submodules/AnimatedCountLabelNode", ], visibility = [ "//visibility:public", diff --git a/submodules/TelegramUI/Components/Chat/ChatInstantVideoMessageDurationNode/Sources/ChatInstantVideoMessageDurationNode.swift b/submodules/TelegramUI/Components/Chat/ChatInstantVideoMessageDurationNode/Sources/ChatInstantVideoMessageDurationNode.swift index e75e529677..b33ad62baa 100644 --- a/submodules/TelegramUI/Components/Chat/ChatInstantVideoMessageDurationNode/Sources/ChatInstantVideoMessageDurationNode.swift +++ b/submodules/TelegramUI/Components/Chat/ChatInstantVideoMessageDurationNode/Sources/ChatInstantVideoMessageDurationNode.swift @@ -4,6 +4,7 @@ import AsyncDisplayKit import SwiftSignalKit import Display import UniversalMediaPlayer +import AnimatedCountLabelNode private let textFont = Font.with(size: 11.0, design: .regular, weight: .regular, traits: [.monospacedNumbers]) diff --git a/submodules/TelegramUI/Components/Chat/ChatMessageBubbleItemNode/Sources/ChatMessageBubbleItemNode.swift b/submodules/TelegramUI/Components/Chat/ChatMessageBubbleItemNode/Sources/ChatMessageBubbleItemNode.swift index 51edc4886e..ce8b88165d 100644 --- a/submodules/TelegramUI/Components/Chat/ChatMessageBubbleItemNode/Sources/ChatMessageBubbleItemNode.swift +++ b/submodules/TelegramUI/Components/Chat/ChatMessageBubbleItemNode/Sources/ChatMessageBubbleItemNode.swift @@ -2913,13 +2913,8 @@ public class ChatMessageBubbleItemNode: ChatMessageItemView, ChatMessagePreviewI } var legacyTransition: ContainedViewLayoutTransition = .immediate - var useDisplayLinkAnimations = false if case let .System(duration, _) = animation { legacyTransition = .animated(duration: duration, curve: .spring) - - if let subject = item.associatedData.subject, case .messageOptions = subject, !"".isEmpty { - useDisplayLinkAnimations = true - } } var forceBackgroundSide = false @@ -3230,9 +3225,7 @@ public class ChatMessageBubbleItemNode: ChatMessageItemView, ChatMessagePreviewI strongSelf.credibilityHighlightNode = nil } } - - let beginAt = applyInfo.timestamp ?? CACurrentMediaTime() - + let timingFunction = kCAMediaTimingFunctionSpring if let forwardInfoNode = forwardInfoSizeApply.1(bubbleContentWidth) { strongSelf.forwardInfoNode = forwardInfoNode @@ -3255,15 +3248,8 @@ public class ChatMessageBubbleItemNode: ChatMessageItemView, ChatMessagePreviewI let forwardInfoFrame = CGRect(origin: CGPoint(x: contentOrigin.x + layoutConstants.text.bubbleInsets.left, y: layoutConstants.bubble.contentInsets.top + forwardInfoOriginY), size: CGSize(width: bubbleContentWidth, height: forwardInfoSizeApply.0.height)) if case let .System(duration, _) = animation { if animateFrame { - if useDisplayLinkAnimations { - let animation = ListViewAnimation(from: previousForwardInfoNodeFrame, to: forwardInfoFrame, duration: duration * UIView.animationDurationFactor(), curve: strongSelf.preferredAnimationCurve, beginAt: beginAt, update: { _, frame in - forwardInfoNode.frame = frame - }) - strongSelf.setAnimationForKey("forwardFrame", animation: animation) - } else { - forwardInfoNode.frame = forwardInfoFrame - forwardInfoNode.layer.animateFrame(from: previousForwardInfoNodeFrame, to: forwardInfoFrame, duration: duration, timingFunction: timingFunction) - } + forwardInfoNode.frame = forwardInfoFrame + forwardInfoNode.layer.animateFrame(from: previousForwardInfoNodeFrame, to: forwardInfoFrame, duration: duration, timingFunction: timingFunction) } else { forwardInfoNode.frame = forwardInfoFrame } @@ -3637,7 +3623,6 @@ public class ChatMessageBubbleItemNode: ChatMessageItemView, ChatMessagePreviewI } let contentNodeFrame = relativeFrame.offsetBy(dx: effectiveContentOriginX, dy: effectiveContentOriginY) - let previousContentNodeFrame = contentNode.frame if case let .System(duration, _) = animation { var animateFrame = false @@ -3653,58 +3638,51 @@ public class ChatMessageBubbleItemNode: ChatMessageItemView, ChatMessagePreviewI } if animateFrame { - if useDisplayLinkAnimations { - let animation = ListViewAnimation(from: previousContentNodeFrame, to: contentNodeFrame, duration: duration * UIView.animationDurationFactor(), curve: strongSelf.preferredAnimationCurve, beginAt: beginAt, update: { _, frame in - contentNode.frame = frame + var useExpensiveSnapshot = false + if case .messageOptions = item.associatedData.subject { + useExpensiveSnapshot = true + } + + if let animateTextAndWebpagePositionSwap, let contentNode = contentNode as? ChatMessageTextBubbleContentNode, let snapshotView = useExpensiveSnapshot ? contentNode.view.snapshotView(afterScreenUpdates: false) : contentNode.layer.snapshotContentTreeAsView() { + let clippingView = UIView() + clippingView.clipsToBounds = true + clippingView.frame = contentNode.frame + + clippingView.addSubview(snapshotView) + snapshotView.frame = CGRect(origin: CGPoint(), size: contentNode.bounds.size) + + contentNode.view.superview?.insertSubview(clippingView, belowSubview: contentNode.view) + + animation.animator.updateAlpha(layer: clippingView.layer, alpha: 0.0, completion: { [weak clippingView] _ in + clippingView?.removeFromSuperview() }) - strongSelf.setAnimationForKey("contentNode\(contentNodeIndex)Frame", animation: animation) - } else { - var useExpensiveSnapshot = false - if case .messageOptions = item.associatedData.subject { - useExpensiveSnapshot = true + + let positionOffset: CGFloat = animateTextAndWebpagePositionSwap ? -1.0 : 1.0 + + animation.animator.updatePosition(layer: snapshotView.layer, position: CGPoint(x: snapshotView.center.x, y: snapshotView.center.y + positionOffset * contentNode.frame.height), completion: nil) + + contentNode.frame = contentNodeFrame + + if let statusNode = contentNode.statusNode, let contentSuperview = contentNode.view.superview, statusNode.view.isDescendant(of: contentSuperview), let bottomStatusNodeAnimationSourcePosition { + let localSourcePosition = statusNode.view.convert(bottomStatusNodeAnimationSourcePosition, from: contentSuperview) + let offset = CGPoint(x: statusNode.bounds.width - localSourcePosition.x, y: statusNode.bounds.height - localSourcePosition.y) + animation.animator.animatePosition(layer: statusNode.layer, from: statusNode.layer.position.offsetBy(dx: -offset.x, dy: -offset.y), to: statusNode.layer.position, completion: nil) } - if let animateTextAndWebpagePositionSwap, let contentNode = contentNode as? ChatMessageTextBubbleContentNode, let snapshotView = useExpensiveSnapshot ? contentNode.view.snapshotView(afterScreenUpdates: false) : contentNode.layer.snapshotContentTreeAsView() { - let clippingView = UIView() - clippingView.clipsToBounds = true - clippingView.frame = contentNode.frame - - clippingView.addSubview(snapshotView) - snapshotView.frame = CGRect(origin: CGPoint(), size: contentNode.bounds.size) - - contentNode.view.superview?.insertSubview(clippingView, belowSubview: contentNode.view) - - animation.animator.updateAlpha(layer: clippingView.layer, alpha: 0.0, completion: { [weak clippingView] _ in - clippingView?.removeFromSuperview() - }) - - let positionOffset: CGFloat = animateTextAndWebpagePositionSwap ? -1.0 : 1.0 - - animation.animator.updatePosition(layer: snapshotView.layer, position: CGPoint(x: snapshotView.center.x, y: snapshotView.center.y + positionOffset * contentNode.frame.height), completion: nil) - - contentNode.frame = contentNodeFrame - - if let statusNode = contentNode.statusNode, let contentSuperview = contentNode.view.superview, statusNode.view.isDescendant(of: contentSuperview), let bottomStatusNodeAnimationSourcePosition { - let localSourcePosition = statusNode.view.convert(bottomStatusNodeAnimationSourcePosition, from: contentSuperview) - let offset = CGPoint(x: statusNode.bounds.width - localSourcePosition.x, y: statusNode.bounds.height - localSourcePosition.y) - animation.animator.animatePosition(layer: statusNode.layer, from: statusNode.layer.position.offsetBy(dx: -offset.x, dy: -offset.y), to: statusNode.layer.position, completion: nil) - } - - contentNode.animateClippingTransition(offset: positionOffset * contentNodeFrame.height, animation: animation) - - contentNode.alpha = 0.0 - animation.animator.updateAlpha(layer: contentNode.layer, alpha: 1.0, completion: nil) - } else if animateTextAndWebpagePositionSwap != nil, let contentNode = contentNode as? ChatMessageWebpageBubbleContentNode { - if let statusNode = contentNode.contentNode.statusNode, let contentSuperview = contentNode.view.superview, statusNode.view.isDescendant(of: contentSuperview), let bottomStatusNodeAnimationSourcePosition { - let localSourcePosition = statusNode.view.convert(bottomStatusNodeAnimationSourcePosition, from: contentSuperview) - let offset = CGPoint(x: statusNode.bounds.width - localSourcePosition.x, y: statusNode.bounds.height - localSourcePosition.y) - animation.animator.animatePosition(layer: statusNode.layer, from: statusNode.layer.position.offsetBy(dx: -offset.x, dy: -offset.y), to: statusNode.layer.position, completion: nil) - } - - animation.animator.updateFrame(layer: contentNode.layer, frame: contentNodeFrame, completion: nil) - } else { - animation.animator.updateFrame(layer: contentNode.layer, frame: contentNodeFrame, completion: nil) + contentNode.animateClippingTransition(offset: positionOffset * contentNodeFrame.height, animation: animation) + + contentNode.alpha = 0.0 + animation.animator.updateAlpha(layer: contentNode.layer, alpha: 1.0, completion: nil) + } else if animateTextAndWebpagePositionSwap != nil, let contentNode = contentNode as? ChatMessageWebpageBubbleContentNode { + if let statusNode = contentNode.contentNode.statusNode, let contentSuperview = contentNode.view.superview, statusNode.view.isDescendant(of: contentSuperview), let bottomStatusNodeAnimationSourcePosition { + let localSourcePosition = statusNode.view.convert(bottomStatusNodeAnimationSourcePosition, from: contentSuperview) + let offset = CGPoint(x: statusNode.bounds.width - localSourcePosition.x, y: statusNode.bounds.height - localSourcePosition.y) + animation.animator.animatePosition(layer: statusNode.layer, from: statusNode.layer.position.offsetBy(dx: -offset.x, dy: -offset.y), to: statusNode.layer.position, completion: nil) } + + animation.animator.updateFrame(layer: contentNode.layer, frame: contentNodeFrame, completion: nil) + } else { + animation.animator.updateFrame(layer: contentNode.layer, frame: contentNodeFrame, completion: nil) } } else if animateAlpha { contentNode.frame = contentNodeFrame @@ -3857,48 +3835,23 @@ public class ChatMessageBubbleItemNode: ChatMessageItemView, ChatMessagePreviewI isCurrentlyPlayingMedia = true } - if case let .System(duration, _) = animation/*, !strongSelf.mainContextSourceNode.isExtractedToContextPreview*/ { + if case .System = animation/*, !strongSelf.mainContextSourceNode.isExtractedToContextPreview*/ { if !strongSelf.backgroundNode.frame.equalTo(backgroundFrame) { - if useDisplayLinkAnimations { - strongSelf.clippingNode.clipsToBounds = shouldClipOnTransitions - let backgroundAnimation = ListViewAnimation(from: strongSelf.backgroundNode.frame, to: backgroundFrame, duration: duration * UIView.animationDurationFactor(), curve: strongSelf.preferredAnimationCurve, beginAt: beginAt, update: { [weak strongSelf] _, frame in - if let strongSelf = strongSelf { - strongSelf.backgroundNode.frame = frame - if let backgroundHighlightNode = strongSelf.backgroundHighlightNode { - backgroundHighlightNode.frame = frame - backgroundHighlightNode.updateLayout(size: frame.size, transition: .immediate) - } - strongSelf.clippingNode.position = CGPoint(x: frame.midX, y: frame.midY) - strongSelf.clippingNode.bounds = CGRect(origin: CGPoint(x: frame.minX, y: frame.minY), size: frame.size) - - strongSelf.backgroundNode.updateLayout(size: frame.size, transition: .immediate) - strongSelf.backgroundWallpaperNode.updateFrame(frame, transition: .immediate) - strongSelf.shadowNode.updateLayout(backgroundFrame: frame, transition: .immediate) - } - }, completed: { [weak strongSelf] _ in - guard let strongSelf else { - return - } - strongSelf.clippingNode.clipsToBounds = false - }) - strongSelf.setAnimationForKey("backgroundNodeFrame", animation: backgroundAnimation) - } else { - animation.animator.updateFrame(layer: strongSelf.backgroundNode.layer, frame: backgroundFrame, completion: nil) - if let backgroundHighlightNode = strongSelf.backgroundHighlightNode { - animation.animator.updateFrame(layer: backgroundHighlightNode.layer, frame: backgroundFrame, completion: nil) - backgroundHighlightNode.updateLayout(size: backgroundFrame.size, transition: animation) - } - animation.animator.updatePosition(layer: strongSelf.clippingNode.layer, position: backgroundFrame.center, completion: nil) - strongSelf.clippingNode.clipsToBounds = shouldClipOnTransitions - animation.animator.updateBounds(layer: strongSelf.clippingNode.layer, bounds: CGRect(origin: CGPoint(x: backgroundFrame.minX, y: backgroundFrame.minY), size: backgroundFrame.size), completion: { [weak strongSelf] _ in - strongSelf?.clippingNode.clipsToBounds = false - }) - - strongSelf.backgroundNode.updateLayout(size: backgroundFrame.size, transition: animation) - animation.animator.updateFrame(layer: strongSelf.backgroundWallpaperNode.layer, frame: backgroundFrame, completion: nil) - strongSelf.shadowNode.updateLayout(backgroundFrame: backgroundFrame, animator: animation.animator) - strongSelf.backgroundWallpaperNode.updateFrame(backgroundFrame, animator: animation.animator) + animation.animator.updateFrame(layer: strongSelf.backgroundNode.layer, frame: backgroundFrame, completion: nil) + if let backgroundHighlightNode = strongSelf.backgroundHighlightNode { + animation.animator.updateFrame(layer: backgroundHighlightNode.layer, frame: backgroundFrame, completion: nil) + backgroundHighlightNode.updateLayout(size: backgroundFrame.size, transition: animation) } + animation.animator.updatePosition(layer: strongSelf.clippingNode.layer, position: backgroundFrame.center, completion: nil) + strongSelf.clippingNode.clipsToBounds = shouldClipOnTransitions + animation.animator.updateBounds(layer: strongSelf.clippingNode.layer, bounds: CGRect(origin: CGPoint(x: backgroundFrame.minX, y: backgroundFrame.minY), size: backgroundFrame.size), completion: { [weak strongSelf] _ in + strongSelf?.clippingNode.clipsToBounds = false + }) + + strongSelf.backgroundNode.updateLayout(size: backgroundFrame.size, transition: animation) + animation.animator.updateFrame(layer: strongSelf.backgroundWallpaperNode.layer, frame: backgroundFrame, completion: nil) + strongSelf.shadowNode.updateLayout(backgroundFrame: backgroundFrame, animator: animation.animator) + strongSelf.backgroundWallpaperNode.updateFrame(backgroundFrame, animator: animation.animator) if let _ = strongSelf.backgroundNode.type { if !strongSelf.mainContextSourceNode.isExtractedToContextPreview { diff --git a/submodules/TelegramUI/Components/Chat/ChatMessageContactBubbleContentNode/Sources/ChatMessageContactBubbleContentNode.swift b/submodules/TelegramUI/Components/Chat/ChatMessageContactBubbleContentNode/Sources/ChatMessageContactBubbleContentNode.swift index 2f9134dc60..7d580c43b7 100644 --- a/submodules/TelegramUI/Components/Chat/ChatMessageContactBubbleContentNode/Sources/ChatMessageContactBubbleContentNode.swift +++ b/submodules/TelegramUI/Components/Chat/ChatMessageContactBubbleContentNode/Sources/ChatMessageContactBubbleContentNode.swift @@ -367,7 +367,7 @@ public class ChatMessageContactBubbleContentNode: ChatMessageBubbleContentNode { strongSelf.messageButtonNode.frame = messageButtonFrame let backgroundInsets = layoutConstants.text.bubbleInsets - let backgroundFrame = CGRect(origin: CGPoint(x: backgroundInsets.left, y: backgroundInsets.top + 5.0), size: CGSize(width: contentWidth - layoutConstants.text.bubbleInsets.right * 2.0, height: 94.0)) + let backgroundFrame = CGRect(origin: CGPoint(x: backgroundInsets.left, y: backgroundInsets.top + 5.0), size: CGSize(width: contentWidth - layoutConstants.text.bubbleInsets.right * 2.0, height: layoutSize.height - 34.0)) if let statusSizeAndApply = statusSizeAndApply { strongSelf.dateAndStatusNode.frame = CGRect(origin: CGPoint(x: layoutConstants.text.bubbleInsets.left, y: backgroundFrame.maxY + 3.0), size: statusSizeAndApply.0) @@ -479,7 +479,7 @@ public class ChatMessageContactBubbleContentNode: ChatMessageBubbleContentNode { return ChatMessageBubbleContentTapAction(content: .ignore) } if self.addButtonNode.frame.contains(point) { - return ChatMessageBubbleContentTapAction(content: .openMessage) + return ChatMessageBubbleContentTapAction(content: .ignore) } if self.dateAndStatusNode.supernode != nil, let _ = self.dateAndStatusNode.hitTest(self.view.convert(point, to: self.dateAndStatusNode.view), with: nil) { return ChatMessageBubbleContentTapAction(content: .ignore) @@ -490,7 +490,17 @@ public class ChatMessageContactBubbleContentNode: ChatMessageBubbleContentNode { @objc private func contactTap(_ recognizer: UITapGestureRecognizer) { if case .ended = recognizer.state { if let item = self.item { - let _ = item.controllerInteraction.openMessage(item.message, OpenMessageParams(mode: .default)) + var selectedContact: TelegramMediaContact? + for media in item.message.media { + if let media = media as? TelegramMediaContact { + selectedContact = media + } + } + if let peerId = selectedContact?.peerId, let peer = item.message.peers[peerId] { + item.controllerInteraction.openPeer(EnginePeer(peer), .info(nil), nil, .default) + } else { + let _ = item.controllerInteraction.openMessage(item.message, OpenMessageParams(mode: .default)) + } } } } diff --git a/submodules/TelegramUI/Components/Chat/ChatMessageInstantVideoBubbleContentNode/Sources/ChatMessageInstantVideoBubbleContentNode.swift b/submodules/TelegramUI/Components/Chat/ChatMessageInstantVideoBubbleContentNode/Sources/ChatMessageInstantVideoBubbleContentNode.swift index 8806ca16f8..71bcca0c73 100644 --- a/submodules/TelegramUI/Components/Chat/ChatMessageInstantVideoBubbleContentNode/Sources/ChatMessageInstantVideoBubbleContentNode.swift +++ b/submodules/TelegramUI/Components/Chat/ChatMessageInstantVideoBubbleContentNode/Sources/ChatMessageInstantVideoBubbleContentNode.swift @@ -192,6 +192,9 @@ public class ChatMessageInstantVideoBubbleContentNode: ChatMessageBubbleContentN } } + let isViewOnceMessage = item.message.minAutoremoveOrClearTimeout == viewOnceTimeout + let forceIsPlaying = isViewOnceMessage && didSetupFileNode + var incoming = item.message.effectivelyIncoming(item.context.account.peerId) if let subject = item.associatedData.subject, case let .messageOptions(_, _, info) = subject, case .forward = info { incoming = false @@ -200,19 +203,19 @@ public class ChatMessageInstantVideoBubbleContentNode: ChatMessageBubbleContentN let statusType: ChatMessageDateAndStatusType? switch preparePosition { case .linear(_, .None), .linear(_, .Neighbour(true, _, _)): - if incoming { - statusType = .BubbleIncoming + if incoming { + statusType = .BubbleIncoming + } else { + if item.message.flags.contains(.Failed) { + statusType = .BubbleOutgoing(.Failed) + } else if (item.message.flags.isSending && !item.message.isSentOrAcknowledged) || item.attributes.updatingMedia != nil { + statusType = .BubbleOutgoing(.Sending) } else { - if item.message.flags.contains(.Failed) { - statusType = .BubbleOutgoing(.Failed) - } else if (item.message.flags.isSending && !item.message.isSentOrAcknowledged) || item.attributes.updatingMedia != nil { - statusType = .BubbleOutgoing(.Sending) - } else { - statusType = .BubbleOutgoing(.Sent(read: item.read)) - } + statusType = .BubbleOutgoing(.Sent(read: item.read)) } - default: - statusType = nil + } + default: + statusType = nil } let automaticDownload = shouldDownloadMediaAutomatically(settings: item.controllerInteraction.automaticMediaDownloadSettings, peerType: item.associatedData.automaticDownloadPeerType, networkType: item.associatedData.automaticDownloadNetworkType, authorPeerId: item.message.author?.id, contactsPeerIds: item.associatedData.contactsPeerIds, media: selectedFile!) @@ -256,7 +259,7 @@ public class ChatMessageInstantVideoBubbleContentNode: ChatMessageBubbleContentN let normalDisplaySize = layoutConstants.instantVideo.dimensions var displaySize = normalDisplaySize let maximumDisplaySize = CGSize(width: min(404, constrainedSize.width - 2.0), height: min(404, constrainedSize.width - 2.0)) - if item.associatedData.currentlyPlayingMessageId == item.message.index { + if (item.associatedData.currentlyPlayingMessageId == item.message.index || forceIsPlaying) && (!isViewOnceMessage || item.associatedData.isStandalone) { isPlaying = true if !isExpanded { displaySize = maximumDisplaySize diff --git a/submodules/TelegramUI/Components/Chat/ChatMessageInteractiveFileNode/BUILD b/submodules/TelegramUI/Components/Chat/ChatMessageInteractiveFileNode/BUILD index 874b586334..ac61001594 100644 --- a/submodules/TelegramUI/Components/Chat/ChatMessageInteractiveFileNode/BUILD +++ b/submodules/TelegramUI/Components/Chat/ChatMessageInteractiveFileNode/BUILD @@ -20,7 +20,6 @@ swift_library( "//submodules/AccountContext", "//submodules/PhotoResources", "//submodules/TelegramStringFormatting", - "//submodules/RadialStatusNode", "//submodules/SemanticStatusNode", "//submodules/FileMediaResourceStatus", "//submodules/CheckNode", diff --git a/submodules/TelegramUI/Components/Chat/ChatMessageInteractiveFileNode/Sources/ChatMessageInteractiveFileNode.swift b/submodules/TelegramUI/Components/Chat/ChatMessageInteractiveFileNode/Sources/ChatMessageInteractiveFileNode.swift index 0aca23b715..d924d74da3 100644 --- a/submodules/TelegramUI/Components/Chat/ChatMessageInteractiveFileNode/Sources/ChatMessageInteractiveFileNode.swift +++ b/submodules/TelegramUI/Components/Chat/ChatMessageInteractiveFileNode/Sources/ChatMessageInteractiveFileNode.swift @@ -10,7 +10,6 @@ import TelegramPresentationData import AccountContext import PhotoResources import TelegramStringFormatting -import RadialStatusNode import SemanticStatusNode import FileMediaResourceStatus import CheckNode diff --git a/submodules/TelegramUI/Components/Chat/ChatMessageInteractiveInstantVideoNode/BUILD b/submodules/TelegramUI/Components/Chat/ChatMessageInteractiveInstantVideoNode/BUILD index 2e541b8c8d..63595c29a2 100644 --- a/submodules/TelegramUI/Components/Chat/ChatMessageInteractiveInstantVideoNode/BUILD +++ b/submodules/TelegramUI/Components/Chat/ChatMessageInteractiveInstantVideoNode/BUILD @@ -20,6 +20,7 @@ swift_library( "//submodules/TelegramPresentationData", "//submodules/AccountContext", "//submodules/RadialStatusNode", + "//submodules/SemanticStatusNode", "//submodules/PhotoResources", "//submodules/TelegramUniversalVideoContent", "//submodules/FileMediaResourceStatus", diff --git a/submodules/TelegramUI/Components/Chat/ChatMessageInteractiveInstantVideoNode/Sources/ChatMessageInteractiveInstantVideoNode.swift b/submodules/TelegramUI/Components/Chat/ChatMessageInteractiveInstantVideoNode/Sources/ChatMessageInteractiveInstantVideoNode.swift index 6144637ec1..27ac32af5e 100644 --- a/submodules/TelegramUI/Components/Chat/ChatMessageInteractiveInstantVideoNode/Sources/ChatMessageInteractiveInstantVideoNode.swift +++ b/submodules/TelegramUI/Components/Chat/ChatMessageInteractiveInstantVideoNode/Sources/ChatMessageInteractiveInstantVideoNode.swift @@ -8,7 +8,7 @@ import TelegramCore import UniversalMediaPlayer import TelegramPresentationData import AccountContext -import RadialStatusNode +import SemanticStatusNode import PhotoResources import TelegramUniversalVideoContent import FileMediaResourceStatus @@ -91,8 +91,10 @@ public class ChatMessageInteractiveInstantVideoNode: ASDisplayNode { public var audioTranscriptionButton: ComponentHostView? private var dustNode: MediaDustNode? - private var statusNode: RadialStatusNode? - private var disappearingStatusNode: RadialStatusNode? + private var statusNode: SemanticStatusNode? + private var disappearingStatusNode: SemanticStatusNode? + private var streamingStatusNode: SemanticStatusNode? + private var playbackStatusNode: InstantVideoRadialStatusNode? public private(set) var videoFrame: CGRect? private var imageScale: CGFloat = 1.0 @@ -129,8 +131,12 @@ public class ChatMessageInteractiveInstantVideoNode: ASDisplayNode { private let playerStatusDisposable = MetaDisposable() private let fetchedThumbnailDisposable = MetaDisposable() + private var viewOnceIconImage: UIImage? + private var shouldAcquireVideoContext: Bool { - if self.visibility && self.trackingIsInHierarchy && !self.canAttachContent { + if let item = self.item, item.associatedData.isStandalone { + return true + } else if self.visibility && self.trackingIsInHierarchy && !self.canAttachContent { return true } else { return false @@ -172,6 +178,7 @@ public class ChatMessageInteractiveInstantVideoNode: ASDisplayNode { self.secretVideoPlaceholderBackground.displaysAsynchronously = false self.secretVideoPlaceholderBackground.displayWithoutProcessing = true self.secretVideoPlaceholder = TransformImageNode() + self.secretVideoPlaceholder.clipsToBounds = true self.infoBackgroundNode = ASImageNode() self.infoBackgroundNode.isLayerBacked = true @@ -620,12 +627,16 @@ public class ChatMessageInteractiveInstantVideoNode: ASDisplayNode { } let effectiveAudioTranscriptionState = updatedAudioTranscriptionState ?? audioTranscriptionState + + let principalGraphics = PresentationResourcesChat.principalGraphics(theme: item.presentationData.theme.theme, wallpaper: item.presentationData.theme.wallpaper, bubbleCorners: item.presentationData.chatBubbleCorners) + let viewOnceIconImage = principalGraphics.radialIndicatorViewOnceIcon return (result, { [weak self] layoutData, animation in if let strongSelf = self { strongSelf.item = item strongSelf.videoFrame = displayVideoFrame strongSelf.appliedForwardInfo = (forwardSource, forwardAuthorSignature) + strongSelf.viewOnceIconImage = viewOnceIconImage strongSelf.automaticDownload = automaticDownload @@ -756,7 +767,7 @@ public class ChatMessageInteractiveInstantVideoNode: ASDisplayNode { } } } - }), content: NativeVideoContent(id: .message(item.message.stableId, telegramFile.fileId), userLocation: .peer(item.message.id.peerId), fileReference: .message(message: MessageReference(item.message), media: telegramFile), streamVideo: streamVideo ? .conservative : .none, enableSound: false, fetchAutomatically: false, isAudioVideoMessage: true, captureProtected: item.message.isCopyProtected(), storeAfterDownload: nil), priority: .embedded, autoplay: item.context.sharedContext.energyUsageSettings.autoplayVideo) + }), content: NativeVideoContent(id: .message(item.message.stableId, telegramFile.fileId), userLocation: .peer(item.message.id.peerId), fileReference: .message(message: MessageReference(item.message), media: telegramFile), streamVideo: streamVideo ? .conservative : .none, enableSound: false, fetchAutomatically: false, isAudioVideoMessage: true, captureProtected: item.message.isCopyProtected(), storeAfterDownload: nil), priority: item.associatedData.isStandalone ? .overlay : .embedded, autoplay: item.context.sharedContext.energyUsageSettings.autoplayVideo && !isViewOnceMessage) if let previousVideoNode = previousVideoNode { videoNode.bounds = previousVideoNode.bounds videoNode.position = previousVideoNode.position @@ -953,6 +964,7 @@ public class ChatMessageInteractiveInstantVideoNode: ASDisplayNode { let placeholderFrame = videoFrame.insetBy(dx: 2.0, dy: 2.0) strongSelf.secretVideoPlaceholder.bounds = CGRect(origin: CGPoint(), size: placeholderFrame.size) + animation.animator.updateCornerRadius(layer: strongSelf.secretVideoPlaceholder.layer, cornerRadius: placeholderFrame.size.width / 2.0, completion: nil) animation.animator.updateScale(layer: strongSelf.secretVideoPlaceholder.layer, scale: imageScale, completion: nil) animation.animator.updatePosition(layer: strongSelf.secretVideoPlaceholder.layer, position: displayVideoFrame.center, completion: nil) @@ -1151,24 +1163,23 @@ public class ChatMessageInteractiveInstantVideoNode: ASDisplayNode { guard let item = self.item, let status = self.status, let videoFrame = self.videoFrame else { return } - let messageTheme = item.presentationData.theme.theme.chat.message - + let isViewOnceMessage = item.message.minAutoremoveOrClearTimeout == viewOnceTimeout let isSecretMedia = item.message.containsSecretMedia - var secretBeginTimeAndTimeout: (Double, Double)? - if isSecretMedia { - if let attribute = item.message.autoclearAttribute { - if let countdownBeginTime = attribute.countdownBeginTime { - secretBeginTimeAndTimeout = (Double(countdownBeginTime), Double(attribute.timeout)) - } - } else if let attribute = item.message.autoremoveAttribute { - if let countdownBeginTime = attribute.countdownBeginTime { - secretBeginTimeAndTimeout = (Double(countdownBeginTime), Double(attribute.timeout)) - } - } - } +// var secretBeginTimeAndTimeout: (Double, Double)? +// if isSecretMedia { +// if let attribute = item.message.autoclearAttribute { +// if let countdownBeginTime = attribute.countdownBeginTime { +// secretBeginTimeAndTimeout = (Double(countdownBeginTime), Double(attribute.timeout)) +// } +// } else if let attribute = item.message.autoremoveAttribute { +// if let countdownBeginTime = attribute.countdownBeginTime { +// secretBeginTimeAndTimeout = (Double(countdownBeginTime), Double(attribute.timeout)) +// } +// } +// } var selectedMedia: TelegramMediaFile? for media in item.message.media { @@ -1234,13 +1245,19 @@ public class ChatMessageInteractiveInstantVideoNode: ASDisplayNode { } else if isBuffering ?? false { progressRequired = true } - if item.presentationData.isPreview { + if item.associatedData.isStandalone { + progressRequired = false + } else if item.presentationData.isPreview { progressRequired = true } if progressRequired { if self.statusNode == nil { - let statusNode = RadialStatusNode(backgroundNodeColor: item.presentationData.theme.theme.chat.message.mediaOverlayControlColors.fillColor, isPreview: item.presentationData.isPreview) + let statusNode = SemanticStatusNode( + backgroundNodeColor: item.presentationData.theme.theme.chat.message.mediaOverlayControlColors.fillColor, + foregroundNodeColor: item.presentationData.theme.theme.chat.message.mediaOverlayControlColors.foregroundColor, + overlayForegroundNodeColor: item.presentationData.theme.theme.chat.message.mediaOverlayControlColors.foregroundColor + ) statusNode.displaysAsynchronously = !item.presentationData.isPreview self.isUserInteractionEnabled = false self.statusNode = statusNode @@ -1249,7 +1266,7 @@ public class ChatMessageInteractiveInstantVideoNode: ASDisplayNode { } else { if let statusNode = self.statusNode { self.disappearingStatusNode = statusNode - statusNode.transitionToState(.none, completion: { [weak statusNode, weak self] in + statusNode.transitionToState(.none, animated: true, synchronous: item.presentationData.isPreview, completion: { [weak statusNode, weak self] in statusNode?.removeFromSupernode() if self?.disappearingStatusNode === statusNode { self?.disappearingStatusNode = nil @@ -1259,7 +1276,7 @@ public class ChatMessageInteractiveInstantVideoNode: ASDisplayNode { } } - let statusFrame = CGRect(origin: CGPoint(x: videoFrame.origin.x + floorToScreenPixels((videoFrame.size.width - 50.0) / 2.0), y: videoFrame.origin.y + floorToScreenPixels((videoFrame.size.height - 50.0) / 2.0)), size: CGSize(width: 50.0, height: 50.0)) + let statusFrame = CGRect(origin: CGPoint(x: videoFrame.origin.x + floorToScreenPixels((videoFrame.size.width - 54.0) / 2.0), y: videoFrame.origin.y + floorToScreenPixels((videoFrame.size.height - 54.0) / 2.0)), size: CGSize(width: 54.0, height: 54.0)) if let animator = animator { if let statusNode = self.statusNode { animator.updateFrame(layer: statusNode.layer, frame: statusFrame, completion: nil) @@ -1272,7 +1289,9 @@ public class ChatMessageInteractiveInstantVideoNode: ASDisplayNode { self.disappearingStatusNode?.frame = statusFrame } - var state: RadialStatusNodeState + var state: SemanticStatusNodeState + var streamingState: SemanticStatusNodeState = .none + switch status.mediaStatus { case var .fetchStatus(fetchStatus): if item.message.forwardInfo != nil { @@ -1283,28 +1302,31 @@ public class ChatMessageInteractiveInstantVideoNode: ASDisplayNode { case let .Fetching(_, progress): if let isBuffering = isBuffering { if isBuffering { - state = .progress(color: messageTheme.mediaOverlayControlColors.foregroundColor, lineWidth: nil, value: nil, cancelEnabled: true, animateRotation: true) + state = .progress(value: nil, cancelEnabled: true, appearance: nil) } else { state = .none } } else { let adjustedProgress = max(progress, 0.027) - state = .progress(color: messageTheme.mediaOverlayControlColors.foregroundColor, lineWidth: nil, value: CGFloat(adjustedProgress), cancelEnabled: true, animateRotation: true) + state = .progress(value: CGFloat(adjustedProgress), cancelEnabled: true, appearance: nil) } case .Local: if isViewOnceMessage { - state = .play(messageTheme.mediaOverlayControlColors.foregroundColor) + state = .play } else if isSecretMedia { - if let (beginTime, timeout) = secretBeginTimeAndTimeout { - state = .secretTimeout(color: messageTheme.mediaOverlayControlColors.foregroundColor, icon: .flame, beginTime: beginTime, timeout: timeout, sparks: true) - } else { - state = .staticTimeout - } + //TODO: + state = .play +// if let (beginTime, timeout) = secretBeginTimeAndTimeout { +// state = .secretTimeout(position: , duration: , generationTimestamp: , appearance: nil) +// state = .secretTimeout(color: messageTheme.mediaOverlayControlColors.foregroundColor, icon: .flame, beginTime: beginTime, timeout: timeout, sparks: true) +// } else { +// state = .staticTimeout +// } } else { state = .none } case .Remote, .Paused: - state = .download(messageTheme.mediaOverlayControlColors.foregroundColor) + state = .download } default: var isLocal = false @@ -1312,13 +1334,18 @@ public class ChatMessageInteractiveInstantVideoNode: ASDisplayNode { isLocal = true } if (isBuffering ?? false) && !isLocal { - state = .progress(color: messageTheme.mediaOverlayControlColors.foregroundColor, lineWidth: nil, value: nil, cancelEnabled: true, animateRotation: true) + state = .progress(value: nil, cancelEnabled: true, appearance: nil) } else { state = .none } } + + if isViewOnceMessage && progressRequired, let viewOnceIconImage = self.viewOnceIconImage, state == .play { + streamingState = .customIcon(viewOnceIconImage) + } + if item.presentationData.isPreview { - state = .play(messageTheme.mediaOverlayControlColors.foregroundColor) + state = .play } if let statusNode = self.statusNode { if state == .none { @@ -1331,12 +1358,63 @@ public class ChatMessageInteractiveInstantVideoNode: ASDisplayNode { }) } - if case .playbackStatus = status.mediaStatus { + let streamingProgressDiameter: CGFloat = 20.0 + let streamingCacheStatusFrame = CGRect(origin: statusFrame.origin.offsetBy(dx: 37.0, dy: 37.0), size: CGSize(width: streamingProgressDiameter, height: streamingProgressDiameter)) + if streamingState != .none && self.streamingStatusNode == nil { + let streamingStatusNode = SemanticStatusNode( + backgroundNodeColor: item.presentationData.theme.theme.chat.message.mediaOverlayControlColors.fillColor, + foregroundNodeColor: item.presentationData.theme.theme.chat.message.mediaOverlayControlColors.foregroundColor, + overlayForegroundNodeColor: item.presentationData.theme.theme.chat.message.mediaOverlayControlColors.foregroundColor + ) + self.streamingStatusNode = streamingStatusNode + streamingStatusNode.frame = streamingCacheStatusFrame + self.addSubnode(streamingStatusNode) + + if isViewOnceMessage { + streamingStatusNode.layer.animateScale(from: 0.1, to: 1.0, duration: 0.2, timingFunction: CAMediaTimingFunctionName.linear.rawValue) + streamingStatusNode.layer.animateAlpha(from: 0.1, to: 1.0, duration: 0.2, timingFunction: CAMediaTimingFunctionName.linear.rawValue) + } + } else if let streamingStatusNode = self.streamingStatusNode { + streamingStatusNode.backgroundNodeColor = item.presentationData.theme.theme.chat.message.mediaOverlayControlColors.fillColor + } + + if let streamingStatusNode = self.streamingStatusNode { + if let animator = animator { + animator.updateFrame(layer: streamingStatusNode.layer, frame: streamingCacheStatusFrame, completion: nil) + } else { + streamingStatusNode.frame = streamingCacheStatusFrame + } + if streamingState == .none { + self.streamingStatusNode = nil + if isViewOnceMessage { + streamingStatusNode.layer.animateScale(from: 1.0, to: 0.1, duration: 0.2, timingFunction: CAMediaTimingFunctionName.linear.rawValue, removeOnCompletion: false) + } + streamingStatusNode.layer.animateAlpha(from: 1.0, to: 0.0, duration: 0.2, timingFunction: CAMediaTimingFunctionName.linear.rawValue, removeOnCompletion: false, completion: { [weak streamingStatusNode] _ in + if streamingState == .none { + streamingStatusNode?.removeFromSupernode() + } + }) + } else { + streamingStatusNode.transitionToState(streamingState) + } + } + + if let statusNode = self.statusNode, streamingState != .none { + let cutoutFrame = streamingCacheStatusFrame.offsetBy(dx: -statusFrame.minX, dy: -statusFrame.minY).insetBy(dx: -2.0 + UIScreenPixel, dy: -2.0 + UIScreenPixel) + statusNode.setCutout(cutoutFrame, animated: true) + } + + if case .playbackStatus = status.mediaStatus, !isViewOnceMessage || item.associatedData.isStandalone { let playbackStatusNode: InstantVideoRadialStatusNode if let current = self.playbackStatusNode { playbackStatusNode = current } else { playbackStatusNode = InstantVideoRadialStatusNode(color: UIColor(white: 1.0, alpha: 0.6), hasSeek: !isViewOnceMessage, sparks: isViewOnceMessage) + playbackStatusNode.alpha = 0.0 + Queue.mainQueue().after(0.15) { + playbackStatusNode.alpha = 1.0 + playbackStatusNode.layer.animateAlpha(from: 0.0, to: 1.0, duration: 0.2) + } playbackStatusNode.isUserInteractionEnabled = !isViewOnceMessage playbackStatusNode.seekTo = { [weak self] position, play in guard let strongSelf = self else { @@ -1355,7 +1433,16 @@ public class ChatMessageInteractiveInstantVideoNode: ASDisplayNode { self.playbackStatusNode = playbackStatusNode } - playbackStatusNode.frame = videoFrame.insetBy(dx: 1.5, dy: 1.5) + let playbackStatusFrame = videoFrame.insetBy(dx: 1.5, dy: 1.5) + if playbackStatusNode.bounds.width > 0.0 && playbackStatusNode.bounds.width != playbackStatusFrame.width, let animator { + animator.animateScale(layer: playbackStatusNode.layer, from: playbackStatusNode.bounds.width / playbackStatusFrame.width, to: 1.0, completion: nil) + } + playbackStatusNode.bounds = playbackStatusFrame + if let animator { + animator.updatePosition(layer: playbackStatusNode.layer, position: playbackStatusFrame.center, completion: nil) + } else { + playbackStatusNode.position = playbackStatusFrame.center + } let status = messageFileMediaPlaybackStatus(context: item.context, file: file, message: EngineMessage(item.message), isRecentActions: item.associatedData.isRecentActions, isGlobalSearch: false, isDownloadList: false) playbackStatusNode.status = status diff --git a/submodules/TelegramUI/Components/ChatTextInputMediaRecordingButton/Sources/LockView.swift b/submodules/TelegramUI/Components/ChatTextInputMediaRecordingButton/Sources/LockView.swift index 06a736e3f5..31977edeb8 100644 --- a/submodules/TelegramUI/Components/ChatTextInputMediaRecordingButton/Sources/LockView.swift +++ b/submodules/TelegramUI/Components/ChatTextInputMediaRecordingButton/Sources/LockView.swift @@ -19,7 +19,7 @@ final class LockView: UIButton, TGModernConversationInputMicButtonLock { }() private let lockingView: AnimationView = { - guard let url = getAppBundle().url(forResource: "Lock", withExtension: "json"), let animation = Animation.filepath(url.path) + guard let url = getAppBundle().url(forResource: "LockPause", withExtension: "json"), let animation = Animation.filepath(url.path) else { return AnimationView() } let view = AnimationView(animation: animation, configuration: LottieConfiguration(renderingEngine: .mainThread, decodingStrategy: .codable)) diff --git a/submodules/TelegramUI/Components/MediaEditor/Sources/DrawingMessageRenderer.swift b/submodules/TelegramUI/Components/MediaEditor/Sources/DrawingMessageRenderer.swift index cd0ba03359..9a37d1bb59 100644 --- a/submodules/TelegramUI/Components/MediaEditor/Sources/DrawingMessageRenderer.swift +++ b/submodules/TelegramUI/Components/MediaEditor/Sources/DrawingMessageRenderer.swift @@ -187,7 +187,7 @@ public final class DrawingMessageRenderer { let avatarHeaderItem = self.context.sharedContext.makeChatMessageAvatarHeaderItem(context: self.context, timestamp: self.messages.first?.timestamp ?? 0, peer: self.messages.first!.peers[self.messages.first!.author!.id]!, message: self.messages.first!, theme: theme, strings: presentationData.strings, wallpaper: presentationData.chatWallpaper, fontSize: presentationData.chatFontSize, chatBubbleCorners: presentationData.chatBubbleCorners, dateTimeFormat: presentationData.dateTimeFormat, nameOrder: presentationData.nameDisplayOrder) - let items: [ListViewItem] = [self.context.sharedContext.makeChatMessagePreviewItem(context: self.context, messages: self.messages, theme: theme, strings: presentationData.strings, wallpaper: presentationData.theme.chat.defaultWallpaper, fontSize: presentationData.chatFontSize, chatBubbleCorners: presentationData.chatBubbleCorners, dateTimeFormat: presentationData.dateTimeFormat, nameOrder: presentationData.nameDisplayOrder, forcedResourceStatus: nil, tapMessage: nil, clickThroughMessage: nil, backgroundNode: nil, availableReactions: nil, accountPeer: nil, isCentered: false, isPreview: true)] + let items: [ListViewItem] = [self.context.sharedContext.makeChatMessagePreviewItem(context: self.context, messages: self.messages, theme: theme, strings: presentationData.strings, wallpaper: presentationData.theme.chat.defaultWallpaper, fontSize: presentationData.chatFontSize, chatBubbleCorners: presentationData.chatBubbleCorners, dateTimeFormat: presentationData.dateTimeFormat, nameOrder: presentationData.nameDisplayOrder, forcedResourceStatus: nil, tapMessage: nil, clickThroughMessage: nil, backgroundNode: nil, availableReactions: nil, accountPeer: nil, isCentered: false, isPreview: true, isStandalone: false)] let inset: CGFloat = 16.0 let leftInset: CGFloat = 37.0 diff --git a/submodules/TelegramUI/Components/Settings/PeerNameColorScreen/Sources/PeerNameColorChatPreviewItem.swift b/submodules/TelegramUI/Components/Settings/PeerNameColorScreen/Sources/PeerNameColorChatPreviewItem.swift index 90a0f7db35..c4b9146312 100644 --- a/submodules/TelegramUI/Components/Settings/PeerNameColorScreen/Sources/PeerNameColorChatPreviewItem.swift +++ b/submodules/TelegramUI/Components/Settings/PeerNameColorScreen/Sources/PeerNameColorChatPreviewItem.swift @@ -236,7 +236,7 @@ final class PeerNameColorChatPreviewItemNode: ListViewItemNode { } let message = Message(stableId: 1, stableVersion: 0, id: MessageId(peerId: peerId, namespace: 0, id: 1), globallyUniqueId: nil, groupingKey: nil, groupInfo: nil, threadId: nil, timestamp: 66000, flags: messageItem.outgoing ? [] : [.Incoming], tags: [], globalTags: [], localTags: [], forwardInfo: nil, author: peers[authorPeerId], text: messageItem.text, attributes: messageItem.reply != nil ? [ReplyMessageAttribute(messageId: replyMessageId, threadMessageId: nil, quote: nil, isQuote: false)] : [], media: media, peers: peers, associatedMessages: messages, associatedMessageIds: [], associatedMedia: [:], associatedThreadInfo: nil, associatedStories: [:]) - items.append(item.context.sharedContext.makeChatMessagePreviewItem(context: item.context, messages: [message], theme: item.componentTheme, strings: item.strings, wallpaper: item.wallpaper, fontSize: item.fontSize, chatBubbleCorners: item.chatBubbleCorners, dateTimeFormat: item.dateTimeFormat, nameOrder: item.nameDisplayOrder, forcedResourceStatus: nil, tapMessage: nil, clickThroughMessage: nil, backgroundNode: currentBackgroundNode, availableReactions: nil, accountPeer: nil, isCentered: false, isPreview: true)) + items.append(item.context.sharedContext.makeChatMessagePreviewItem(context: item.context, messages: [message], theme: item.componentTheme, strings: item.strings, wallpaper: item.wallpaper, fontSize: item.fontSize, chatBubbleCorners: item.chatBubbleCorners, dateTimeFormat: item.dateTimeFormat, nameOrder: item.nameDisplayOrder, forcedResourceStatus: nil, tapMessage: nil, clickThroughMessage: nil, backgroundNode: currentBackgroundNode, availableReactions: nil, accountPeer: nil, isCentered: false, isPreview: true, isStandalone: false)) } var nodes: [ListViewItemNode] = [] diff --git a/submodules/TelegramUI/Components/Settings/QuickReactionSetupController/Sources/ReactionChatPreviewItem.swift b/submodules/TelegramUI/Components/Settings/QuickReactionSetupController/Sources/ReactionChatPreviewItem.swift index cac1f69208..cde4c65bc1 100644 --- a/submodules/TelegramUI/Components/Settings/QuickReactionSetupController/Sources/ReactionChatPreviewItem.swift +++ b/submodules/TelegramUI/Components/Settings/QuickReactionSetupController/Sources/ReactionChatPreviewItem.swift @@ -292,7 +292,7 @@ class ReactionChatPreviewItemNode: ListViewItemNode { attributes.append(ReactionsMessageAttribute(canViewList: false, reactions: [MessageReaction(value: reaction, count: 1, chosenOrder: 0)], recentPeers: recentPeers)) } - let messageItem = item.context.sharedContext.makeChatMessagePreviewItem(context: item.context, messages: [Message(stableId: 1, stableVersion: 0, id: MessageId(peerId: chatPeerId, namespace: 0, id: 1), globallyUniqueId: nil, groupingKey: nil, groupInfo: nil, threadId: nil, timestamp: 66000, flags: [.Incoming], tags: [], globalTags: [], localTags: [], forwardInfo: nil, author: peers[userPeerId], text: messageText, attributes: attributes, media: [], peers: peers, associatedMessages: messages, associatedMessageIds: [], associatedMedia: [:], associatedThreadInfo: nil, associatedStories: [:])], theme: item.theme, strings: item.strings, wallpaper: item.wallpaper, fontSize: item.fontSize, chatBubbleCorners: item.chatBubbleCorners, dateTimeFormat: item.dateTimeFormat, nameOrder: item.nameDisplayOrder, forcedResourceStatus: nil, tapMessage: nil, clickThroughMessage: nil, backgroundNode: currentBackgroundNode, availableReactions: item.availableReactions, accountPeer: item.accountPeer, isCentered: true, isPreview: true) + let messageItem = item.context.sharedContext.makeChatMessagePreviewItem(context: item.context, messages: [Message(stableId: 1, stableVersion: 0, id: MessageId(peerId: chatPeerId, namespace: 0, id: 1), globallyUniqueId: nil, groupingKey: nil, groupInfo: nil, threadId: nil, timestamp: 66000, flags: [.Incoming], tags: [], globalTags: [], localTags: [], forwardInfo: nil, author: peers[userPeerId], text: messageText, attributes: attributes, media: [], peers: peers, associatedMessages: messages, associatedMessageIds: [], associatedMedia: [:], associatedThreadInfo: nil, associatedStories: [:])], theme: item.theme, strings: item.strings, wallpaper: item.wallpaper, fontSize: item.fontSize, chatBubbleCorners: item.chatBubbleCorners, dateTimeFormat: item.dateTimeFormat, nameOrder: item.nameDisplayOrder, forcedResourceStatus: nil, tapMessage: nil, clickThroughMessage: nil, backgroundNode: currentBackgroundNode, availableReactions: item.availableReactions, accountPeer: item.accountPeer, isCentered: true, isPreview: true, isStandalone: false) var node: ListViewItemNode? if let current = currentNode { diff --git a/submodules/TelegramUI/Components/Settings/ThemeAccentColorScreen/Sources/ThemeAccentColorControllerNode.swift b/submodules/TelegramUI/Components/Settings/ThemeAccentColorScreen/Sources/ThemeAccentColorControllerNode.swift index 52018357b5..5b96fd2ddb 100644 --- a/submodules/TelegramUI/Components/Settings/ThemeAccentColorScreen/Sources/ThemeAccentColorControllerNode.swift +++ b/submodules/TelegramUI/Components/Settings/ThemeAccentColorScreen/Sources/ThemeAccentColorControllerNode.swift @@ -1087,7 +1087,7 @@ final class ThemeAccentColorControllerNode: ASDisplayNode, UIScrollViewDelegate return state }, animated: true) }, clickThroughMessage: { - }, backgroundNode: self.backgroundNode, availableReactions: nil, accountPeer: nil, isCentered: false, isPreview: true) + }, backgroundNode: self.backgroundNode, availableReactions: nil, accountPeer: nil, isCentered: false, isPreview: true, isStandalone: false) return item } diff --git a/submodules/TelegramUI/Components/Settings/WallpaperGalleryScreen/Sources/WallpaperGalleryItem.swift b/submodules/TelegramUI/Components/Settings/WallpaperGalleryScreen/Sources/WallpaperGalleryItem.swift index 2a423e6c83..a64dbbb8eb 100644 --- a/submodules/TelegramUI/Components/Settings/WallpaperGalleryScreen/Sources/WallpaperGalleryItem.swift +++ b/submodules/TelegramUI/Components/Settings/WallpaperGalleryScreen/Sources/WallpaperGalleryItem.swift @@ -1622,19 +1622,19 @@ final class WallpaperGalleryItemNode: GalleryItemNode { if !bottomMessageText.isEmpty { let message1 = Message(stableId: 2, stableVersion: 0, id: MessageId(peerId: peerId, namespace: 0, id: 2), globallyUniqueId: nil, groupingKey: nil, groupInfo: nil, threadId: nil, timestamp: 66001, flags: [], tags: [], globalTags: [], localTags: [], forwardInfo: nil, author: peers[otherPeerId], text: bottomMessageText, attributes: [], media: [], peers: peers, associatedMessages: messages, associatedMessageIds: [], associatedMedia: [:], associatedThreadInfo: nil, associatedStories: [:]) - items.append(self.context.sharedContext.makeChatMessagePreviewItem(context: self.context, messages: [message1], theme: theme, strings: self.presentationData.strings, wallpaper: currentWallpaper, fontSize: self.presentationData.chatFontSize, chatBubbleCorners: self.presentationData.chatBubbleCorners, dateTimeFormat: self.presentationData.dateTimeFormat, nameOrder: self.presentationData.nameDisplayOrder, forcedResourceStatus: nil, tapMessage: nil, clickThroughMessage: nil, backgroundNode: self.nativeNode, availableReactions: nil, accountPeer: nil, isCentered: false, isPreview: true)) + items.append(self.context.sharedContext.makeChatMessagePreviewItem(context: self.context, messages: [message1], theme: theme, strings: self.presentationData.strings, wallpaper: currentWallpaper, fontSize: self.presentationData.chatFontSize, chatBubbleCorners: self.presentationData.chatBubbleCorners, dateTimeFormat: self.presentationData.dateTimeFormat, nameOrder: self.presentationData.nameDisplayOrder, forcedResourceStatus: nil, tapMessage: nil, clickThroughMessage: nil, backgroundNode: self.nativeNode, availableReactions: nil, accountPeer: nil, isCentered: false, isPreview: true, isStandalone: false)) } let message2 = Message(stableId: 1, stableVersion: 0, id: MessageId(peerId: peerId, namespace: 0, id: 1), globallyUniqueId: nil, groupingKey: nil, groupInfo: nil, threadId: nil, timestamp: 66000, flags: [.Incoming], tags: [], globalTags: [], localTags: [], forwardInfo: nil, author: peers[peerId], text: topMessageText, attributes: messageAttributes, media: [], peers: peers, associatedMessages: messages, associatedMessageIds: [], associatedMedia: [:], associatedThreadInfo: nil, associatedStories: [:]) - items.append(self.context.sharedContext.makeChatMessagePreviewItem(context: self.context, messages: [message2], theme: theme, strings: self.presentationData.strings, wallpaper: currentWallpaper, fontSize: self.presentationData.chatFontSize, chatBubbleCorners: self.presentationData.chatBubbleCorners, dateTimeFormat: self.presentationData.dateTimeFormat, nameOrder: self.presentationData.nameDisplayOrder, forcedResourceStatus: nil, tapMessage: nil, clickThroughMessage: nil, backgroundNode: self.nativeNode, availableReactions: nil, accountPeer: nil, isCentered: false, isPreview: true)) + items.append(self.context.sharedContext.makeChatMessagePreviewItem(context: self.context, messages: [message2], theme: theme, strings: self.presentationData.strings, wallpaper: currentWallpaper, fontSize: self.presentationData.chatFontSize, chatBubbleCorners: self.presentationData.chatBubbleCorners, dateTimeFormat: self.presentationData.dateTimeFormat, nameOrder: self.presentationData.nameDisplayOrder, forcedResourceStatus: nil, tapMessage: nil, clickThroughMessage: nil, backgroundNode: self.nativeNode, availableReactions: nil, accountPeer: nil, isCentered: false, isPreview: true, isStandalone: false)) if let serviceMessageText { let attributedText = convertMarkdownToAttributes(NSAttributedString(string: serviceMessageText)) let entities = generateChatInputTextEntities(attributedText) let message3 = Message(stableId: 0, stableVersion: 0, id: MessageId(peerId: peerId, namespace: 0, id: 0), globallyUniqueId: nil, groupingKey: nil, groupInfo: nil, threadId: nil, timestamp: 66002, flags: [.Incoming], tags: [], globalTags: [], localTags: [], forwardInfo: nil, author: peers[peerId], text: "", attributes: [], media: [TelegramMediaAction(action: .customText(text: attributedText.string, entities: entities, additionalAttributes: nil))], peers: peers, associatedMessages: messages, associatedMessageIds: [], associatedMedia: [:], associatedThreadInfo: nil, associatedStories: [:]) - items.append(self.context.sharedContext.makeChatMessagePreviewItem(context: self.context, messages: [message3], theme: theme, strings: self.presentationData.strings, wallpaper: currentWallpaper, fontSize: self.presentationData.chatFontSize, chatBubbleCorners: self.presentationData.chatBubbleCorners, dateTimeFormat: self.presentationData.dateTimeFormat, nameOrder: self.presentationData.nameDisplayOrder, forcedResourceStatus: nil, tapMessage: nil, clickThroughMessage: nil, backgroundNode: self.nativeNode, availableReactions: nil, accountPeer: nil, isCentered: false, isPreview: true)) + items.append(self.context.sharedContext.makeChatMessagePreviewItem(context: self.context, messages: [message3], theme: theme, strings: self.presentationData.strings, wallpaper: currentWallpaper, fontSize: self.presentationData.chatFontSize, chatBubbleCorners: self.presentationData.chatBubbleCorners, dateTimeFormat: self.presentationData.dateTimeFormat, nameOrder: self.presentationData.nameDisplayOrder, forcedResourceStatus: nil, tapMessage: nil, clickThroughMessage: nil, backgroundNode: self.nativeNode, availableReactions: nil, accountPeer: nil, isCentered: false, isPreview: true, isStandalone: false)) } let params = ListViewItemLayoutParams(width: layout.size.width, leftInset: layout.safeInsets.left, rightInset: layout.safeInsets.right, availableHeight: layout.size.height) diff --git a/submodules/TelegramUI/Images.xcassets/Premium/PrivacyPresence.imageset/Contents.json b/submodules/TelegramUI/Images.xcassets/Premium/PrivacyPresence.imageset/Contents.json new file mode 100644 index 0000000000..d4072b508f --- /dev/null +++ b/submodules/TelegramUI/Images.xcassets/Premium/PrivacyPresence.imageset/Contents.json @@ -0,0 +1,12 @@ +{ + "images" : [ + { + "filename" : "LastSeen.pdf", + "idiom" : "universal" + } + ], + "info" : { + "author" : "xcode", + "version" : 1 + } +} diff --git a/submodules/TelegramUI/Images.xcassets/Premium/PrivacyPresence.imageset/LastSeen.pdf b/submodules/TelegramUI/Images.xcassets/Premium/PrivacyPresence.imageset/LastSeen.pdf new file mode 100644 index 0000000000..77e71ede93 --- /dev/null +++ b/submodules/TelegramUI/Images.xcassets/Premium/PrivacyPresence.imageset/LastSeen.pdf @@ -0,0 +1,236 @@ +%PDF-1.7 + +1 0 obj + << /Type /XObject + /Length 2 0 R + /Group << /Type /Group + /S /Transparency + >> + /Subtype /Form + /Resources << >> + /BBox [ 0.000000 0.000000 90.000000 90.000000 ] + >> +stream +/DeviceRGB CS +/DeviceRGB cs +q +-0.573576 0.819152 -0.819152 -0.573576 116.549232 55.611801 cm +0.000000 0.000000 0.000000 scn +11.201888 42.536640 m +10.293921 43.394623 8.862335 43.354103 8.004352 42.446136 c +7.146368 41.538170 7.186888 40.106583 8.094854 39.248600 c +11.201888 42.536640 l +h +0.104302 67.332283 m +2.359326 67.155991 l +2.359326 67.155991 l +0.104302 67.332283 l +h +-2.261905 57.367592 m +-2.261905 56.118378 -1.249215 55.105686 0.000000 55.105686 c +1.249215 55.105686 2.261905 56.118378 2.261905 57.367592 c +-2.261905 57.367592 l +h +8.094854 39.248600 m +14.413891 33.277420 22.963470 29.611862 32.367256 29.611862 c +32.367256 34.135670 l +24.160690 34.135670 16.711905 37.329945 11.201888 42.536640 c +8.094854 39.248600 l +h +32.367256 29.611862 m +51.816689 29.611862 67.607117 45.286694 67.607117 64.651543 c +63.083305 64.651543 l +63.083305 47.811054 49.344265 34.135670 32.367256 34.135670 c +32.367256 29.611862 l +h +67.607117 64.651543 m +67.607117 84.016388 51.816689 99.691223 32.367256 99.691223 c +32.367256 95.167419 l +49.344265 95.167419 63.083305 81.492027 63.083305 64.651543 c +67.607117 64.651543 l +h +32.367256 99.691223 m +13.838978 99.691223 -0.744848 85.491989 -2.150723 67.508575 c +2.359326 67.155991 l +3.587800 82.870171 16.285238 95.167419 32.367256 95.167419 c +32.367256 99.691223 l +h +-2.150723 67.508575 m +-2.228836 66.509377 -2.261905 58.196545 -2.261905 57.367592 c +2.261905 57.367592 l +2.261905 57.810497 2.270653 60.074085 2.288070 62.339462 c +2.296773 63.471542 2.307606 64.599052 2.320517 65.496704 c +2.326979 65.946014 2.333896 66.332962 2.341205 66.632019 c +2.344865 66.781738 2.348513 66.904846 2.352073 67.000267 c +2.353848 67.047859 2.355492 67.085579 2.356944 67.114319 c +2.358485 67.144806 2.359401 67.156944 2.359326 67.155991 c +-2.150723 67.508575 l +h +f +n +Q +q +-0.573576 0.819152 -0.819152 -0.573576 88.913589 26.117556 cm +0.000000 0.000000 0.000000 scn +9.281114 9.781655 m +16.382307 17.498228 l +16.941557 18.105942 16.902267 19.051954 16.294554 19.611202 c +16.008730 19.874233 15.631773 20.015724 15.243468 20.005730 c +0.971281 19.638401 l +0.420877 19.624233 -0.013829 19.166559 0.000337 18.616156 c +0.006554 18.374573 0.100313 18.143475 0.264171 17.965847 c +7.814779 9.780768 l +8.188101 9.376076 8.818807 9.350644 9.223498 9.723966 c +9.243483 9.742400 9.262703 9.761649 9.281114 9.781655 c +h +f* +n +Q +q +0.997564 0.069756 -0.069756 0.997564 27.183931 22.657665 cm +0.000000 0.000000 0.000000 scn +1.126237 13.390795 m +2.459934 13.920820 9.052311 11.488453 9.899677 10.654730 c +10.747043 9.821009 8.165665 2.188360 3.458126 3.582010 c +-1.249412 4.975660 -0.207460 12.860769 1.126237 13.390795 c +h +11.387338 14.554115 m +10.053641 14.024090 3.339256 16.886160 2.491890 17.719881 c +2.073576 18.131460 2.036059 22.762421 3.879909 27.093227 c +5.771091 31.535206 11.220125 34.641033 13.845622 33.894619 c +22.144812 31.535206 12.721035 15.084141 11.387338 14.554115 c +h +f* +n +Q +q +1.000000 0.000000 -0.000000 1.000000 44.495422 29.013916 cm +0.000000 0.000000 0.000000 scn +3.598538 14.091810 m +5.121493 13.880739 10.383765 10.189638 10.959850 8.824841 c +11.535934 7.460043 4.756379 0.930214 1.484518 3.851572 c +0.240279 4.962521 -0.215860 6.791552 0.093658 8.620932 c +0.598051 11.602104 2.654739 14.222616 3.598538 14.091810 c +h +5.710229 17.638847 m +7.863484 29.044964 15.632494 32.943184 18.918007 31.831261 c +20.597502 31.262865 22.614096 27.443319 21.877079 23.943005 c +20.639967 18.067585 14.851414 12.199171 13.275784 12.765230 c +10.189638 13.873955 5.710229 17.084599 5.710229 17.638847 c +h +f* +n +Q + +endstream +endobj + +2 0 obj + 3525 +endobj + +3 0 obj + << /Type /XObject + /Length 4 0 R + /Group << /Type /Group + /S /Transparency + >> + /Subtype /Form + /Resources << >> + /BBox [ 0.000000 0.000000 90.000000 90.000000 ] + >> +stream +/DeviceRGB CS +/DeviceRGB cs +q +1.000000 0.000000 -0.000000 1.000000 5.000000 5.000000 cm +0.000000 0.000000 0.000000 scn +0.000000 80.000000 m +80.000000 80.000000 l +80.000000 0.000000 l +0.000000 0.000000 l +0.000000 80.000000 l +h +f +n +Q + +endstream +endobj + +4 0 obj + 232 +endobj + +5 0 obj + << /XObject << /X1 1 0 R >> + /ExtGState << /E1 << /SMask << /Type /Mask + /G 3 0 R + /S /Alpha + >> + /Type /ExtGState + >> >> + >> +endobj + +6 0 obj + << /Length 7 0 R >> +stream +/DeviceRGB CS +/DeviceRGB cs +q +/E1 gs +/X1 Do +Q + +endstream +endobj + +7 0 obj + 46 +endobj + +8 0 obj + << /Annots [] + /Type /Page + /MediaBox [ 0.000000 0.000000 90.000000 90.000000 ] + /Resources 5 0 R + /Contents 6 0 R + /Parent 9 0 R + >> +endobj + +9 0 obj + << /Kids [ 8 0 R ] + /Count 1 + /Type /Pages + >> +endobj + +10 0 obj + << /Pages 9 0 R + /Type /Catalog + >> +endobj + +xref +0 11 +0000000000 65535 f +0000000010 00000 n +0000003783 00000 n +0000003806 00000 n +0000004286 00000 n +0000004308 00000 n +0000004606 00000 n +0000004708 00000 n +0000004729 00000 n +0000004902 00000 n +0000004976 00000 n +trailer +<< /ID [ (some) (id) ] + /Root 10 0 R + /Size 11 +>> +startxref +5036 +%%EOF \ No newline at end of file diff --git a/submodules/TelegramUI/Images.xcassets/Premium/PrivacyReadTime.imageset/Contents.json b/submodules/TelegramUI/Images.xcassets/Premium/PrivacyReadTime.imageset/Contents.json new file mode 100644 index 0000000000..5898847ff8 --- /dev/null +++ b/submodules/TelegramUI/Images.xcassets/Premium/PrivacyReadTime.imageset/Contents.json @@ -0,0 +1,12 @@ +{ + "images" : [ + { + "filename" : "ReadTime.pdf", + "idiom" : "universal" + } + ], + "info" : { + "author" : "xcode", + "version" : 1 + } +} diff --git a/submodules/TelegramUI/Images.xcassets/Premium/PrivacyReadTime.imageset/ReadTime.pdf b/submodules/TelegramUI/Images.xcassets/Premium/PrivacyReadTime.imageset/ReadTime.pdf new file mode 100644 index 0000000000..25f40eeac6 --- /dev/null +++ b/submodules/TelegramUI/Images.xcassets/Premium/PrivacyReadTime.imageset/ReadTime.pdf @@ -0,0 +1,277 @@ +%PDF-1.7 + +1 0 obj + << /Type /XObject + /Length 2 0 R + /Group << /Type /Group + /S /Transparency + >> + /Subtype /Form + /Resources << >> + /BBox [ 0.000000 0.000000 90.000000 90.000000 ] + >> +stream +/DeviceRGB CS +/DeviceRGB cs +q +0.707107 0.707107 -0.707107 0.707107 67.034660 -18.373894 cm +0.000000 0.000000 0.000000 scn +10.453167 41.350018 m +9.512874 42.238548 8.030319 42.196587 7.141789 41.256294 c +6.253258 40.316002 6.295220 38.833447 7.235513 37.944916 c +10.453167 41.350018 l +h +0.095610 63.883827 m +-2.239701 64.066391 l +0.095610 63.883827 l +h +-2.342436 54.749527 m +-2.342436 53.455833 -1.293692 52.407089 0.000000 52.407089 c +1.293692 52.407089 2.342436 53.455833 2.342436 54.749527 c +-2.342436 54.749527 l +h +7.235513 37.944916 m +13.076076 32.425873 20.978649 29.037750 29.669981 29.037750 c +29.669981 33.722622 l +22.218493 33.722622 15.455904 36.622681 10.453167 41.350018 c +7.235513 37.944916 l +h +29.669981 29.037750 m +47.645660 29.037750 62.242207 43.525253 62.242207 61.426483 c +57.557335 61.426483 l +57.557335 46.139488 45.085209 33.722622 29.669981 33.722622 c +29.669981 29.037750 l +h +62.242207 61.426483 m +62.242207 79.327713 47.645660 93.815216 29.669981 93.815216 c +29.669981 89.130341 l +45.085209 89.130341 57.557335 76.713478 57.557335 61.426483 c +62.242207 61.426483 l +h +29.669981 93.815216 m +12.540254 93.815216 -0.940433 80.686134 -2.239701 64.066391 c +2.430921 63.701260 l +3.546472 77.970970 15.073609 89.130341 29.669981 89.130341 c +29.669981 93.815216 l +h +-2.239701 64.066391 m +-2.312374 63.136772 -2.342436 55.500645 -2.342436 54.749527 c +2.342436 54.749527 l +2.342436 55.154526 2.350449 57.228703 2.366412 59.305004 c +2.374389 60.342529 2.384313 61.375381 2.396135 62.197342 c +2.402054 62.608826 2.408379 62.962509 2.415047 63.235344 c +2.418387 63.371964 2.421699 63.483559 2.424903 63.569450 c +2.426500 63.612274 2.427957 63.645615 2.429212 63.670444 c +2.430558 63.697090 2.431254 63.705528 2.430921 63.701260 c +-2.239701 64.066391 l +h +f +n +Q +q +0.707107 0.707107 -0.707107 0.707107 35.092972 1.912642 cm +0.000000 0.000000 0.000000 scn +9.400195 9.052296 m +16.491901 18.012711 l +17.065374 18.737297 16.942873 19.789585 16.218287 20.363058 c +15.921938 20.597603 15.554913 20.724905 15.176980 20.724241 c +1.113485 20.699522 l +0.497442 20.698439 -0.001081 20.198158 0.000002 19.582117 c +0.000426 19.340685 0.079173 19.105904 0.224414 18.913044 c +7.634510 9.073509 l +8.005109 8.581406 8.704469 8.482906 9.196571 8.853506 c +9.272655 8.910804 9.341086 8.977612 9.400195 9.052296 c +h +f* +n +Q +q +1.000000 0.000000 -0.000000 1.000000 37.394165 30.785156 cm +0.000000 0.000000 0.000000 scn +25.237368 22.854145 m +26.030727 23.803909 25.903934 25.216991 24.954170 26.010351 c +24.004406 26.803711 22.591324 26.676918 21.797964 25.727154 c +25.237368 22.854145 l +h +7.466430 5.075024 m +9.186129 3.638515 l +9.186131 3.638519 l +7.466430 5.075024 l +h +7.322968 5.062153 m +5.886413 3.342493 l +5.886472 3.342443 l +7.322968 5.062153 l +h +7.306202 5.079990 m +5.500865 3.752708 l +5.500870 3.752703 l +7.306202 5.079990 l +h +1.805337 16.344992 m +1.072299 17.342052 -0.330222 17.556084 -1.327282 16.823048 c +-2.324342 16.090010 -2.538375 14.687489 -1.805337 13.690428 c +1.805337 16.344992 l +h +21.797964 25.727154 m +5.746728 6.511530 l +9.186131 3.638519 l +25.237368 22.854145 l +21.797964 25.727154 l +h +5.746732 6.511532 m +6.504047 7.418142 7.852895 7.539131 8.759465 6.781860 c +5.886472 3.342443 l +6.879384 2.513050 8.356690 2.645563 9.186129 3.638515 c +5.746732 6.511532 l +h +8.759524 6.781811 m +8.891400 6.671646 9.009609 6.545914 9.111534 6.407280 c +5.500870 3.752703 l +5.612496 3.600872 5.741967 3.463160 5.886413 3.342493 c +8.759524 6.781811 l +h +9.111539 6.407272 m +1.805337 16.344992 l +-1.805337 13.690428 l +5.500865 3.752708 l +9.111539 6.407272 l +h +f +n +Q +q +1.000000 0.000000 -0.000000 1.000000 29.174744 29.887207 cm +0.000000 0.000000 0.000000 scn +5.315279 3.728403 m +6.056771 2.737616 7.461063 2.535522 8.451851 3.277015 c +9.442638 4.018508 9.644732 5.422799 8.903239 6.413588 c +5.315279 3.728403 l +h +1.793980 15.913027 m +1.052487 16.903814 -0.351804 17.105907 -1.342592 16.364414 c +-2.333380 15.622922 -2.535474 14.218631 -1.793980 13.227842 c +1.793980 15.913027 l +h +8.903239 6.413588 m +1.793980 15.913027 l +-1.793980 13.227842 l +5.315279 3.728403 l +8.903239 6.413588 l +h +f +n +Q + +endstream +endobj + +2 0 obj + 4078 +endobj + +3 0 obj + << /Type /XObject + /Length 4 0 R + /Group << /Type /Group + /S /Transparency + >> + /Subtype /Form + /Resources << >> + /BBox [ 0.000000 0.000000 90.000000 90.000000 ] + >> +stream +/DeviceRGB CS +/DeviceRGB cs +q +1.000000 0.000000 -0.000000 1.000000 5.000000 5.000000 cm +0.000000 0.000000 0.000000 scn +0.000000 80.000000 m +80.000000 80.000000 l +80.000000 0.000000 l +0.000000 0.000000 l +0.000000 80.000000 l +h +f +n +Q + +endstream +endobj + +4 0 obj + 232 +endobj + +5 0 obj + << /XObject << /X1 1 0 R >> + /ExtGState << /E1 << /SMask << /Type /Mask + /G 3 0 R + /S /Alpha + >> + /Type /ExtGState + >> >> + >> +endobj + +6 0 obj + << /Length 7 0 R >> +stream +/DeviceRGB CS +/DeviceRGB cs +q +/E1 gs +/X1 Do +Q + +endstream +endobj + +7 0 obj + 46 +endobj + +8 0 obj + << /Annots [] + /Type /Page + /MediaBox [ 0.000000 0.000000 90.000000 90.000000 ] + /Resources 5 0 R + /Contents 6 0 R + /Parent 9 0 R + >> +endobj + +9 0 obj + << /Kids [ 8 0 R ] + /Count 1 + /Type /Pages + >> +endobj + +10 0 obj + << /Pages 9 0 R + /Type /Catalog + >> +endobj + +xref +0 11 +0000000000 65535 f +0000000010 00000 n +0000004336 00000 n +0000004359 00000 n +0000004839 00000 n +0000004861 00000 n +0000005159 00000 n +0000005261 00000 n +0000005282 00000 n +0000005455 00000 n +0000005529 00000 n +trailer +<< /ID [ (some) (id) ] + /Root 10 0 R + /Size 11 +>> +startxref +5589 +%%EOF \ No newline at end of file diff --git a/submodules/TelegramUI/Resources/Animations/LockPause.json b/submodules/TelegramUI/Resources/Animations/LockPause.json new file mode 100644 index 0000000000..3997067fef --- /dev/null +++ b/submodules/TelegramUI/Resources/Animations/LockPause.json @@ -0,0 +1 @@ +{"v":"5.12.1","fr":60,"ip":0,"op":60,"w":240,"h":360,"nm":"nLock3","ddd":0,"assets":[],"layers":[{"ddd":0,"ind":1,"ty":4,"nm":"Rectangle 3","sr":1,"ks":{"o":{"a":0,"k":100,"ix":11},"r":{"a":1,"k":[{"i":{"x":[0.667],"y":[1]},"o":{"x":[0.333],"y":[0]},"t":26,"s":[0]},{"t":46,"s":[90]}],"ix":10},"p":{"s":true,"x":{"a":1,"k":[{"i":{"x":[0.667],"y":[1]},"o":{"x":[0.333],"y":[0]},"t":13,"s":[120]},{"i":{"x":[0.667],"y":[1]},"o":{"x":[0.333],"y":[0]},"t":26,"s":[120]},{"t":46,"s":[120]}],"ix":3},"y":{"a":1,"k":[{"i":{"x":[0.667],"y":[1]},"o":{"x":[0.333],"y":[0]},"t":13,"s":[150]},{"i":{"x":[0.667],"y":[1]},"o":{"x":[0.333],"y":[0]},"t":26,"s":[200]},{"t":46,"s":[180]}],"ix":4}},"a":{"a":0,"k":[0,0,0],"ix":1,"l":2},"s":{"a":0,"k":[100,100,100],"ix":6,"l":2}},"ao":0,"shapes":[{"ty":"gr","it":[{"ind":0,"ty":"sh","ix":1,"ks":{"a":1,"k":[{"i":{"x":0.833,"y":1},"o":{"x":0.333,"y":0},"t":26,"s":[{"i":[[-1.66,0],[0,0],[0,-1.66],[0,0],[1.66,0],[0,0],[0,1.66],[0,0]],"o":[[0,0],[1.66,0],[0,0],[0,1.66],[0,0],[-1.66,0],[0,0],[0,-1.66]],"v":[[-5,-7],[5,-7],[8,-4],[8,4],[5,7],[-5,7],[-8,4],[-8,-4]],"c":true}]},{"t":46,"s":[{"i":[[-1.66,0],[0,0],[0,-1.66],[0,0],[1.66,0],[0,0],[0,1.66],[0,0]],"o":[[0,0],[1.66,0],[0,0],[0,1.66],[0,0],[-1.66,0],[0,0],[0,-1.66]],"v":[[-6.25,-5.75],[6.25,-5.75],[9.25,-2.75],[9.25,2.75],[6.25,5.75],[-6.25,5.75],[-9.25,2.75],[-9.25,-2.75]],"c":true}]}],"ix":2},"nm":"Path 1","mn":"ADBE Vector Shape - Group","hd":false},{"ty":"tm","s":{"a":1,"k":[{"i":{"x":[0.4],"y":[1]},"o":{"x":[0.6],"y":[0]},"t":26,"s":[0]},{"t":46,"s":[42]}],"ix":1},"e":{"a":1,"k":[{"i":{"x":[0.4],"y":[1]},"o":{"x":[0.6],"y":[0]},"t":26,"s":[100]},{"t":46,"s":[63]}],"ix":2},"o":{"a":0,"k":212,"ix":3},"m":1,"ix":2,"nm":"Trim Paths 1","mn":"ADBE Vector Filter - Trim","hd":false},{"ty":"st","c":{"a":0,"k":[0,0.494117647409,0.898039221764,1],"ix":3},"o":{"a":0,"k":100,"ix":4},"w":{"a":1,"k":[{"i":{"x":[0.4],"y":[1]},"o":{"x":[0.6],"y":[0]},"t":26,"s":[1.33]},{"t":46,"s":[6]}],"ix":5},"lc":2,"lj":1,"ml":4,"bm":0,"nm":"Обводка 1","mn":"ADBE Vector Graphic - Stroke","hd":false},{"ty":"tr","p":{"a":0,"k":[0,0],"ix":2},"a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[600,600],"ix":3},"r":{"a":0,"k":0,"ix":6},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"Transform"}],"nm":"Rectangle","np":3,"cix":2,"bm":0,"ix":1,"mn":"ADBE Vector Group","hd":false}],"ip":26,"op":60,"st":13,"ct":1,"bm":0},{"ddd":0,"ind":2,"ty":4,"nm":"Rectangle 2","sr":1,"ks":{"o":{"a":0,"k":100,"ix":11},"r":{"a":1,"k":[{"i":{"x":[0.667],"y":[1]},"o":{"x":[0.333],"y":[0]},"t":26,"s":[0]},{"t":46,"s":[90]}],"ix":10},"p":{"s":true,"x":{"a":1,"k":[{"i":{"x":[0.667],"y":[1]},"o":{"x":[0.333],"y":[0]},"t":13,"s":[120]},{"i":{"x":[0.667],"y":[1]},"o":{"x":[0.333],"y":[0]},"t":26,"s":[120]},{"t":46,"s":[120]}],"ix":3},"y":{"a":1,"k":[{"i":{"x":[0.667],"y":[1]},"o":{"x":[0.333],"y":[0]},"t":13,"s":[150]},{"i":{"x":[0.667],"y":[1]},"o":{"x":[0.333],"y":[0]},"t":26,"s":[200]},{"t":46,"s":[180]}],"ix":4}},"a":{"a":0,"k":[0,0,0],"ix":1,"l":2},"s":{"a":0,"k":[100,100,100],"ix":6,"l":2}},"ao":0,"shapes":[{"ty":"gr","it":[{"ind":0,"ty":"sh","ix":1,"ks":{"a":1,"k":[{"i":{"x":0.833,"y":1},"o":{"x":0.333,"y":0},"t":26,"s":[{"i":[[-1.66,0],[0,0],[0,-1.66],[0,0],[1.66,0],[0,0],[0,1.66],[0,0]],"o":[[0,0],[1.66,0],[0,0],[0,1.66],[0,0],[-1.66,0],[0,0],[0,-1.66]],"v":[[-5,-7],[5,-7],[8,-4],[8,4],[5,7],[-5,7],[-8,4],[-8,-4]],"c":true}]},{"t":46,"s":[{"i":[[-1.66,0],[0,0],[0,-1.66],[0,0],[1.66,0],[0,0],[0,1.66],[0,0]],"o":[[0,0],[1.66,0],[0,0],[0,1.66],[0,0],[-1.66,0],[0,0],[0,-1.66]],"v":[[-6.25,-5.75],[6.25,-5.75],[9.25,-2.75],[9.25,2.75],[6.25,5.75],[-6.25,5.75],[-9.25,2.75],[-9.25,-2.75]],"c":true}]}],"ix":2},"nm":"Path 1","mn":"ADBE Vector Shape - Group","hd":false},{"ty":"tm","s":{"a":1,"k":[{"i":{"x":[0.4],"y":[1]},"o":{"x":[0.6],"y":[0]},"t":26,"s":[0]},{"t":46,"s":[42]}],"ix":1},"e":{"a":1,"k":[{"i":{"x":[0.4],"y":[1]},"o":{"x":[0.6],"y":[0]},"t":26,"s":[100]},{"t":46,"s":[63]}],"ix":2},"o":{"a":0,"k":32,"ix":3},"m":1,"ix":2,"nm":"Trim Paths 1","mn":"ADBE Vector Filter - Trim","hd":false},{"ty":"st","c":{"a":0,"k":[0,0.494117647409,0.898039221764,1],"ix":3},"o":{"a":0,"k":100,"ix":4},"w":{"a":1,"k":[{"i":{"x":[0.4],"y":[1]},"o":{"x":[0.6],"y":[0]},"t":26,"s":[1.33]},{"t":46,"s":[6]}],"ix":5},"lc":2,"lj":1,"ml":4,"bm":0,"nm":"Обводка 1","mn":"ADBE Vector Graphic - Stroke","hd":false},{"ty":"tr","p":{"a":0,"k":[0,0],"ix":2},"a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[600,600],"ix":3},"r":{"a":0,"k":0,"ix":6},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"Transform"}],"nm":"Rectangle","np":3,"cix":2,"bm":0,"ix":1,"mn":"ADBE Vector Group","hd":false}],"ip":26,"op":60,"st":13,"ct":1,"bm":0},{"ddd":0,"ind":3,"ty":4,"nm":"Rectangle","sr":1,"ks":{"o":{"a":0,"k":100,"ix":11},"r":{"a":1,"k":[{"i":{"x":[0.667],"y":[1]},"o":{"x":[0.333],"y":[0]},"t":26,"s":[0]},{"t":46,"s":[90]}],"ix":10},"p":{"s":true,"x":{"a":1,"k":[{"i":{"x":[0.667],"y":[1]},"o":{"x":[0.333],"y":[0]},"t":0,"s":[120]},{"i":{"x":[0.667],"y":[1]},"o":{"x":[0.333],"y":[0]},"t":26,"s":[120]},{"t":46,"s":[120]}],"ix":3},"y":{"a":1,"k":[{"i":{"x":[0.667],"y":[1]},"o":{"x":[0.333],"y":[0]},"t":0,"s":[150]},{"i":{"x":[0.667],"y":[1]},"o":{"x":[0.333],"y":[0]},"t":26,"s":[200]},{"t":46,"s":[180]}],"ix":4}},"a":{"a":0,"k":[0,0,0],"ix":1,"l":2},"s":{"a":0,"k":[100,100,100],"ix":6,"l":2}},"ao":0,"shapes":[{"ty":"gr","it":[{"ind":0,"ty":"sh","ix":1,"ks":{"a":1,"k":[{"i":{"x":0.667,"y":1},"o":{"x":0.333,"y":0},"t":26,"s":[{"i":[[-1.66,0],[0,0],[0,-1.66],[0,0],[1.66,0],[0,0],[0,1.66],[0,0]],"o":[[0,0],[1.66,0],[0,0],[0,1.66],[0,0],[-1.66,0],[0,0],[0,-1.66]],"v":[[-5,-7],[5,-7],[8,-4],[8,4],[5,7],[-5,7],[-8,4],[-8,-4]],"c":true}]},{"t":46,"s":[{"i":[[-1.66,0],[0,0],[0,-1.66],[0,0],[1.66,0],[0,0],[0,1.66],[0,0]],"o":[[0,0],[1.66,0],[0,0],[0,1.66],[0,0],[-1.66,0],[0,0],[0,-1.66]],"v":[[-5,-8],[5,-8],[8,-5],[8,5],[5,8],[-5,8],[-8,5],[-8,-5]],"c":true}]}],"ix":2},"nm":"Path 1","mn":"ADBE Vector Shape - Group","hd":false},{"ty":"st","c":{"a":0,"k":[0,0.494117647409,0.898039221764,1],"ix":3},"o":{"a":0,"k":100,"ix":4},"w":{"a":0,"k":1.33,"ix":5},"lc":1,"lj":1,"ml":4,"bm":0,"nm":"Обводка 1","mn":"ADBE Vector Graphic - Stroke","hd":false},{"ty":"tr","p":{"a":0,"k":[0,0],"ix":2},"a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[600,600],"ix":3},"r":{"a":0,"k":0,"ix":6},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"Transform"}],"nm":"Rectangle","np":2,"cix":2,"bm":0,"ix":1,"mn":"ADBE Vector Group","hd":false},{"ty":"fl","c":{"a":0,"k":[0,0.494117647409,0.898039221764,1],"ix":4},"o":{"a":0,"k":0,"ix":5},"r":1,"bm":0,"nm":"Заливка 1","mn":"ADBE Vector Graphic - Fill","hd":false}],"ip":0,"op":26,"st":0,"ct":1,"bm":0},{"ddd":0,"ind":4,"ty":4,"nm":"Path","parent":3,"sr":1,"ks":{"o":{"a":0,"k":100,"ix":11},"r":{"a":1,"k":[{"i":{"x":[0.667],"y":[1]},"o":{"x":[0.333],"y":[0]},"t":0,"s":[10]},{"t":10,"s":[0]}],"ix":10},"p":{"a":1,"k":[{"i":{"x":0.899,"y":0.642},"o":{"x":0.865,"y":0},"t":0,"s":[30,-32,0],"to":[0.011,8.859,0],"ti":[0,0,0]},{"i":{"x":0.619,"y":0.469},"o":{"x":0.298,"y":0.682},"t":25,"s":[30.07,26.297,0],"to":[0,0,0],"ti":[0,0,0]},{"i":{"x":0.515,"y":1},"o":{"x":0.218,"y":0.434},"t":26,"s":[30.079,29.902,0],"to":[0,0,0],"ti":[-0.001,-0.956,0]},{"t":30,"s":[30.088,39.998,0]}],"ix":2,"l":2},"a":{"a":0,"k":[30,36,0],"ix":1,"l":2},"s":{"a":0,"k":[100,100,100],"ix":6,"l":2}},"ao":0,"shapes":[{"ty":"gr","it":[{"ind":0,"ty":"sh","ix":1,"ks":{"a":0,"k":{"i":[[0,0],[0,0],[-2.76,0],[0,-2.76],[0,0]],"o":[[0,0],[0,-2.76],[2.76,0],[0,0],[0,0]],"v":[[-5,2],[-5,-1],[0,-6],[5,-1],[5,6]],"c":false},"ix":2},"nm":"Path 1","mn":"ADBE Vector Shape - Group","hd":false},{"ty":"tm","s":{"a":1,"k":[{"i":{"x":[0.833],"y":[0.833]},"o":{"x":[0.167],"y":[0.167]},"t":22,"s":[14]},{"i":{"x":[0.833],"y":[0.833]},"o":{"x":[0.167],"y":[0.167]},"t":25,"s":[33]},{"t":26,"s":[41]}],"ix":1},"e":{"a":1,"k":[{"i":{"x":[0.4],"y":[1]},"o":{"x":[0.167],"y":[0]},"t":0,"s":[93]},{"i":{"x":[0.686],"y":[0.698]},"o":{"x":[0.353],"y":[0]},"t":6,"s":[93]},{"i":{"x":[0.697],"y":[0.578]},"o":{"x":[0.365],"y":[0.403]},"t":9,"s":[90.956]},{"i":{"x":[0.741],"y":[0.419]},"o":{"x":[0.419],"y":[0.214]},"t":13,"s":[88.581]},{"i":{"x":[0.713],"y":[0.507]},"o":{"x":[0.387],"y":[0.398]},"t":20,"s":[77.219]},{"i":{"x":[0.697],"y":[0.921]},"o":{"x":[0.37],"y":[0.27]},"t":23,"s":[66.609]},{"i":{"x":[0.833],"y":[0.833]},"o":{"x":[0.167],"y":[0.167]},"t":25,"s":[50]},{"t":26,"s":[43]}],"ix":2},"o":{"a":0,"k":0,"ix":3},"m":1,"ix":2,"nm":"Trim Paths 1","mn":"ADBE Vector Filter - Trim","hd":false},{"ty":"st","c":{"a":0,"k":[0,0.494117647409,0.898039221764,1],"ix":3},"o":{"a":0,"k":100,"ix":4},"w":{"a":0,"k":1.33,"ix":5},"lc":2,"lj":1,"ml":4,"bm":0,"nm":"Обводка 1","mn":"ADBE Vector Graphic - Stroke","hd":false},{"ty":"tr","p":{"a":0,"k":[0,0],"ix":2},"a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[600,600],"ix":3},"r":{"a":0,"k":0,"ix":6},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"Transform"}],"nm":"Path","np":3,"cix":2,"bm":0,"ix":1,"mn":"ADBE Vector Group","hd":false}],"ip":0,"op":26,"st":0,"ct":1,"bm":0},{"ddd":0,"ind":5,"ty":4,"nm":"Path 4","parent":3,"sr":1,"ks":{"o":{"a":0,"k":100,"ix":11},"r":{"a":0,"k":0,"ix":10},"p":{"a":1,"k":[{"i":{"x":0.498,"y":1},"o":{"x":0.921,"y":0},"t":0,"s":[0,132,0],"to":[-0.018,-13.122,0],"ti":[0.001,0.644,0]},{"t":32,"s":[-0.62,8.043,0]}],"ix":2,"l":2},"a":{"a":0,"k":[0,0,0],"ix":1,"l":2},"s":{"a":0,"k":[100,100,100],"ix":6,"l":2}},"ao":0,"shapes":[{"ty":"gr","it":[{"ind":0,"ty":"sh","ix":1,"ks":{"a":1,"k":[{"i":{"x":0.667,"y":1},"o":{"x":1,"y":0},"t":0,"s":[{"i":[[0,0],[0,0],[0,0]],"o":[[0,0],[0,0],[0,0]],"v":[[-8,3],[0,-3],[8,3]],"c":false}]},{"t":24,"s":[{"i":[[0,0],[0,0],[0,0]],"o":[[0,0],[0,0],[0,0]],"v":[[-0.753,3],[0,-3],[0.753,3]],"c":false}]}],"ix":2},"nm":"Path 1","mn":"ADBE Vector Shape - Group","hd":false},{"ty":"tm","s":{"a":0,"k":0,"ix":1},"e":{"a":1,"k":[{"i":{"x":[0.833],"y":[0.833]},"o":{"x":[0.167],"y":[0.167]},"t":21,"s":[100]},{"i":{"x":[0.833],"y":[0.833]},"o":{"x":[0.167],"y":[0.167]},"t":22,"s":[43]},{"i":{"x":[0.833],"y":[0.833]},"o":{"x":[0.167],"y":[0.167]},"t":23,"s":[20]},{"i":{"x":[0.833],"y":[0.833]},"o":{"x":[0.167],"y":[0.167]},"t":24,"s":[6]},{"t":25,"s":[0]}],"ix":2},"o":{"a":0,"k":0,"ix":3},"m":1,"ix":2,"nm":"Trim Paths 1","mn":"ADBE Vector Filter - Trim","hd":false},{"ty":"st","c":{"a":0,"k":[0,0.494117647409,0.898039221764,1],"ix":3},"o":{"a":0,"k":100,"ix":4},"w":{"a":0,"k":1.33,"ix":5},"lc":2,"lj":2,"bm":0,"nm":"Обводка 1","mn":"ADBE Vector Graphic - Stroke","hd":false},{"ty":"tr","p":{"a":0,"k":[0,0],"ix":2},"a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[600,600],"ix":3},"r":{"a":0,"k":0,"ix":6},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"Transform"}],"nm":"Path 5","np":3,"cix":2,"bm":0,"ix":1,"mn":"ADBE Vector Group","hd":false},{"ty":"gr","it":[{"ind":0,"ty":"sh","ix":1,"ks":{"a":1,"k":[{"i":{"x":0.667,"y":1},"o":{"x":1,"y":0},"t":0,"s":[{"i":[[0,0],[0,0],[0,0]],"o":[[0,0],[0,0],[0,0]],"v":[[-8,3],[0,-3],[8,3]],"c":false}]},{"t":24,"s":[{"i":[[0,0],[0,0],[0,0]],"o":[[0,0],[0,0],[0,0]],"v":[[-0.753,3],[0,-3],[0.753,3]],"c":false}]}],"ix":2},"nm":"Path 1","mn":"ADBE Vector Shape - Group","hd":false},{"ty":"tm","s":{"a":1,"k":[{"i":{"x":[0.833],"y":[0.833]},"o":{"x":[0.167],"y":[0.167]},"t":21,"s":[0]},{"i":{"x":[0.833],"y":[0.833]},"o":{"x":[0.167],"y":[0.167]},"t":22,"s":[60]},{"i":{"x":[0.833],"y":[0.833]},"o":{"x":[0.167],"y":[0.167]},"t":23,"s":[80]},{"i":{"x":[0.833],"y":[0.833]},"o":{"x":[0.167],"y":[0.167]},"t":24,"s":[94]},{"t":25,"s":[100]}],"ix":1},"e":{"a":0,"k":100,"ix":2},"o":{"a":0,"k":0,"ix":3},"m":1,"ix":2,"nm":"Trim Paths 1","mn":"ADBE Vector Filter - Trim","hd":false},{"ty":"st","c":{"a":0,"k":[0,0.494117647409,0.898039221764,1],"ix":3},"o":{"a":0,"k":100,"ix":4},"w":{"a":0,"k":1.33,"ix":5},"lc":2,"lj":2,"bm":0,"nm":"Обводка 1","mn":"ADBE Vector Graphic - Stroke","hd":false},{"ty":"tr","p":{"a":0,"k":[0,0],"ix":2},"a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[600,600],"ix":3},"r":{"a":0,"k":0,"ix":6},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"Transform"}],"nm":"Path 4","np":3,"cix":2,"bm":0,"ix":2,"mn":"ADBE Vector Group","hd":false}],"ip":0,"op":25,"st":0,"ct":1,"bm":0}],"markers":[],"props":{}} \ No newline at end of file diff --git a/submodules/TelegramUI/Sources/ChatController.swift b/submodules/TelegramUI/Sources/ChatController.swift index 446926061b..9396e53808 100644 --- a/submodules/TelegramUI/Sources/ChatController.swift +++ b/submodules/TelegramUI/Sources/ChatController.swift @@ -12973,12 +12973,7 @@ public final class ChatControllerImpl: TelegramBaseController, ChatController, G strongSelf.push(controller) if justInstalled { - let content: UndoOverlayContent -// if bot.flags.contains(.showInSettings) { - content = .succeed(text: strongSelf.presentationData.strings.WebApp_ShortcutsSettingsAdded(botPeer.compactDisplayTitle).string, timeout: 5.0, customUndoText: nil) -// } else { -// content = .succeed(text: strongSelf.presentationData.strings.WebApp_ShortcutsAdded(bot.shortName).string, timeout: 5.0) -// } + let content: UndoOverlayContent = .succeed(text: strongSelf.presentationData.strings.WebApp_ShortcutsSettingsAdded(botPeer.compactDisplayTitle).string, timeout: 5.0, customUndoText: nil) controller.present(UndoOverlayController(presentationData: strongSelf.presentationData, content: content, elevatedLayout: false, position: .top, action: { _ in return false }), in: .current) } }, error: { [weak self] error in @@ -15489,13 +15484,13 @@ public final class ChatControllerImpl: TelegramBaseController, ChatController, G let updatedMessage = message .withUpdatedReplyToMessageId(replyMessageSubject?.subjectModel) .withUpdatedCorrelationId(correlationId) -// .withUpdatedAttributes({ attributes in -// var attributes = attributes -//#if DEBUG -// attributes.append(AutoremoveTimeoutMessageAttribute(timeout: viewOnceTimeout, countdownBeginTime: nil)) -//#endif -// return attributes -// }) + .withUpdatedAttributes({ attributes in + var attributes = attributes +#if DEBUG + attributes.append(AutoremoveTimeoutMessageAttribute(timeout: viewOnceTimeout, countdownBeginTime: nil)) +#endif + return attributes + }) var usedCorrelationId = false diff --git a/submodules/TelegramUI/Sources/ChatHistoryListNode.swift b/submodules/TelegramUI/Sources/ChatHistoryListNode.swift index 2a054b7901..d14860c1d7 100644 --- a/submodules/TelegramUI/Sources/ChatHistoryListNode.swift +++ b/submodules/TelegramUI/Sources/ChatHistoryListNode.swift @@ -4110,8 +4110,12 @@ public final class ChatHistoryListNodeImpl: ListView, ChatHistoryNode, ChatHisto func voicePlaylistItemChanged(_ previousItem: SharedMediaPlaylistItem?, _ currentItem: SharedMediaPlaylistItem?) -> Void { if let currentItemId = currentItem?.id as? PeerMessagesMediaPlaylistItemId { - let isVideo = currentItem?.playbackData?.type == .instantVideo - self.currentlyPlayingMessageIdPromise.set(.single((currentItemId.messageIndex, isVideo))) + if let source = currentItem?.playbackData?.source, case let .telegramFile(_, _, isViewOnce) = source, isViewOnce { + self.currentlyPlayingMessageIdPromise.set(.single(nil)) + } else { + let isVideo = currentItem?.playbackData?.type == .instantVideo + self.currentlyPlayingMessageIdPromise.set(.single((currentItemId.messageIndex, isVideo))) + } } else { self.currentlyPlayingMessageIdPromise.set(.single(nil)) } diff --git a/submodules/TelegramUI/Sources/ChatMessageContextControllerContentSource.swift b/submodules/TelegramUI/Sources/ChatMessageContextControllerContentSource.swift index 61fa9d1913..cc8b2b0eb8 100644 --- a/submodules/TelegramUI/Sources/ChatMessageContextControllerContentSource.swift +++ b/submodules/TelegramUI/Sources/ChatMessageContextControllerContentSource.swift @@ -110,6 +110,8 @@ final class ChatViewOnceMessageContextExtractedContentSource: ContextExtractedCo let blurBackground: Bool = true let centerVertically: Bool = true + var initialAppearanceOffset: CGPoint = .zero + private let context: AccountContext private let presentationData: PresentationData private weak var chatNode: ChatControllerNode? @@ -169,6 +171,9 @@ final class ChatViewOnceMessageContextExtractedContentSource: ContextExtractedCo self.idleTimerExtensionDisposable.set(self.context.sharedContext.applicationBindings.pushIdleTimerExtension()) + let isIncoming = self.message.effectivelyIncoming(self.context.account.peerId) + let isVideo = (self.message.media.first(where: { $0 is TelegramMediaFile }) as? TelegramMediaFile)?.isInstantVideo ?? false + var result: ContextControllerTakeViewInfo? var sourceNode: ContextExtractedContentContainingNode? var sourceRect: CGRect = .zero @@ -181,19 +186,19 @@ final class ChatViewOnceMessageContextExtractedContentSource: ContextExtractedCo } if item.content.contains(where: { $0.0.stableId == self.message.stableId }), let contentNode = itemNode.getMessageContextSourceNode(stableId: self.message.stableId) { sourceNode = contentNode - sourceRect = itemNode.frame + sourceRect = contentNode.view.convert(contentNode.bounds, to: chatNode.view) + if !isVideo { + sourceRect.origin.y -= 2.0 + UIScreenPixel + } } } - let isIncoming = self.message.effectivelyIncoming(self.context.account.peerId) - let isVideo = (self.message.media.first(where: { $0 is TelegramMediaFile }) as? TelegramMediaFile)?.isInstantVideo ?? false - var tooltipSourceRect: CGRect = .zero if let sourceNode { var bubbleWidth: CGFloat = 0.0 - if (isIncoming || "".isEmpty) && !isVideo { + if (isIncoming || "".isEmpty) { let messageItem = self.context.sharedContext.makeChatMessagePreviewItem( context: self.context, messages: [self.message], @@ -211,10 +216,12 @@ final class ChatViewOnceMessageContextExtractedContentSource: ContextExtractedCo availableReactions: nil, accountPeer: nil, isCentered: false, - isPreview: false + isPreview: false, + isStandalone: true ) - let params = ListViewItemLayoutParams(width: chatNode.historyNode.frame.width, leftInset: validLayout.safeInsets.left, rightInset: validLayout.safeInsets.right, availableHeight: chatNode.historyNode.frame.height, isStandalone: false) + let width = chatNode.historyNode.frame.width + let params = ListViewItemLayoutParams(width: width, leftInset: validLayout.safeInsets.left, rightInset: validLayout.safeInsets.right, availableHeight: chatNode.historyNode.frame.height, isStandalone: false) var node: ListViewItemNode? messageItem.nodeConfiguredForParams(async: { $0() }, params: params, synchronousLoads: false, previousItem: nil, nextItem: nil, completion: { messageNode, apply in @@ -223,11 +230,22 @@ final class ChatViewOnceMessageContextExtractedContentSource: ContextExtractedCo }) if let messageNode = node as? ChatMessageItemView, let copyContentNode = messageNode.getMessageContextSourceNode(stableId: self.message.stableId) { - messageNode.frame.origin.y = chatNode.frame.height - sourceRect.origin.y - sourceRect.size.height + let delta: CGFloat = 0.0// (width - 20.0 - messageNode.frame.height) + self.initialAppearanceOffset = CGPoint(x: 0.0, y: -delta) + + copyContentNode.contentNode.backgroundColor = UIColor.red.withAlphaComponent(0.5) + + messageNode.frame.origin.y = sourceRect.origin.y// chatNode.frame.height - sourceRect.origin.y - sourceRect.size.height chatNode.addSubnode(messageNode) result = ContextControllerTakeViewInfo(containingItem: .node(copyContentNode), contentAreaInScreenSpace: chatNode.convert(chatNode.frameForVisibleArea(), to: nil)) bubbleWidth = copyContentNode.contentNode.subnodes?.first?.frame.width ?? messageNode.frame.width + + if isVideo { + messageItem.updateNode(async: { $0() }, node: { return messageNode }, params: params, previousItem: nil, nextItem: nil, animation: .System(duration: 0.4, transition: ControlledTransition(duration: 0.4, curve: .spring, interactive: false)), completion: { (layout, apply) in + apply(ListViewItemApply(isOnScreen: true)) + }) + } } self.messageNodeCopy = node as? ChatMessageItemView diff --git a/submodules/TelegramUI/Sources/ChatTextInputPanelNode.swift b/submodules/TelegramUI/Sources/ChatTextInputPanelNode.swift index 40c20b121e..961325f02e 100644 --- a/submodules/TelegramUI/Sources/ChatTextInputPanelNode.swift +++ b/submodules/TelegramUI/Sources/ChatTextInputPanelNode.swift @@ -2164,7 +2164,7 @@ class ChatTextInputPanelNode: ChatInputPanelNode, ASEditableTextNodeDelegate, Ch } audioRecordingDotNode.frame = CGRect(origin: CGPoint(x: leftInset + 2.0 - UIScreenPixel, y: audioRecordingTimeNode.frame.midY - 20), size: CGSize(width: 40.0, height: 40)) - if animateDotAppearing { + if animateDotAppearing || animateCancelSlideIn { audioRecordingDotNode.layer.animateScale(from: 0.3, to: 1, duration: 0.15, delay: 0, removeOnCompletion: false) audioRecordingTimeNode.started = { [weak audioRecordingDotNode] in if let audioRecordingDotNode = audioRecordingDotNode, audioRecordingDotNode.layer.animation(forKey: "recording") == nil { @@ -2546,6 +2546,15 @@ class ChatTextInputPanelNode: ChatInputPanelNode, ASEditableTextNodeDelegate, Ch self.updateActionButtons(hasText: inputHasText, hideMicButton: hideMicButton, animated: transition.isAnimated) + var viewOnceIsVisible = false + if let recordingState = interfaceState.inputTextPanelState.mediaRecordingState { + if case let .audio(_, isLocked) = recordingState { + viewOnceIsVisible = isLocked + } else if case let .video(_, isLocked) = recordingState { + viewOnceIsVisible = isLocked + } + } + if let prevInputPanelNode = self.prevInputPanelNode { prevInputPanelNode.frame = CGRect(origin: .zero, size: prevInputPanelNode.frame.size) } @@ -2592,26 +2601,37 @@ class ChatTextInputPanelNode: ChatInputPanelNode, ASEditableTextNodeDelegate, Ch animateAlpha(for: prevPreviewInputPanelNode.waveformScubberNode) animateAlpha(for: prevPreviewInputPanelNode.durationLabel) animateAlpha(for: prevPreviewInputPanelNode.playButton) - + let binNode = prevPreviewInputPanelNode.binNode self.animatingBinNode = binNode let dismissBin = { [weak self, weak prevPreviewInputPanelNode, weak binNode] in if binNode?.supernode != nil { - prevPreviewInputPanelNode?.deleteButton.layer.animateAlpha(from: 1.0, to: 0.0, duration: 0.15, delay: 0, removeOnCompletion: false) { [weak prevPreviewInputPanelNode] _ in + prevPreviewInputPanelNode?.deleteButton.layer.animateAlpha(from: 1.0, to: 0.0, duration: 0.15, delay: 0.0, removeOnCompletion: false) { [weak prevPreviewInputPanelNode] _ in if prevPreviewInputPanelNode?.supernode === self { prevPreviewInputPanelNode?.removeFromSupernode() } } - prevPreviewInputPanelNode?.deleteButton.layer.animateScale(from: 1.0, to: 0.3, duration: 0.15, delay: 0, removeOnCompletion: false) + prevPreviewInputPanelNode?.deleteButton.layer.animateScale(from: 1.0, to: 0.3, duration: 0.15, delay: 0.0, removeOnCompletion: false) - self?.attachmentButton.layer.animateAlpha(from: 0.0, to: 1.0, duration: 0.15, delay: 0, removeOnCompletion: false) - self?.attachmentButton.layer.animateScale(from: 0.3, to: 1.0, duration: 0.15, delay: 0, removeOnCompletion: false) + if isRecording { + self?.attachmentButton.layer.animateAlpha(from: 0.0, to: 0, duration: 0.01, delay: 0.0, removeOnCompletion: false) + self?.attachmentButton.layer.animateScale(from: 1, to: 0.3, duration: 0.01, delay: 0.0, removeOnCompletion: false) + } else { + self?.attachmentButton.layer.animateAlpha(from: 0.0, to: 1.0, duration: 0.15, delay: 0.0, removeOnCompletion: false) + self?.attachmentButton.layer.animateScale(from: 0.3, to: 1.0, duration: 0.15, delay: 0.0, removeOnCompletion: false) + } } else if prevPreviewInputPanelNode?.supernode === self { prevPreviewInputPanelNode?.removeFromSupernode() } } if self.isMediaDeleted { + Queue.mainQueue().after(0.5, { + self.isMediaDeleted = false + }) + } + + if self.isMediaDeleted && !isRecording { binNode.completion = dismissBin binNode.play() } else { @@ -2638,16 +2658,18 @@ class ChatTextInputPanelNode: ChatInputPanelNode, ASEditableTextNodeDelegate, Ch } } + var clippingDelta: CGFloat = 0.0 + if case let .media(_, _, focused) = interfaceState.inputMode, focused { + clippingDelta = -panelHeight + } + transition.updateFrame(node: self.clippingNode, frame: CGRect(origin: CGPoint(), size: CGSize(width: width, height: panelHeight))) + transition.updateSublayerTransformOffset(layer: self.clippingNode.layer, offset: CGPoint(x: 0.0, y: clippingDelta)) let viewOnceSize = self.viewOnceButton.update(theme: interfaceState.theme) let viewOnceButtonFrame = CGRect(origin: CGPoint(x: width - rightInset - 44.0 - UIScreenPixel, y: -152.0), size: viewOnceSize) self.viewOnceButton.bounds = CGRect(origin: .zero, size: viewOnceButtonFrame.size) transition.updatePosition(node: self.viewOnceButton, position: viewOnceButtonFrame.center) - var viewOnceIsVisible = false - if let recordingState = interfaceState.inputTextPanelState.mediaRecordingState, case let .audio(_, isLocked) = recordingState, isLocked { - viewOnceIsVisible = true - } if self.viewOnceButton.alpha.isZero && viewOnceIsVisible { self.viewOnceButton.update(isSelected: self.viewOnce, animated: false) } @@ -2659,13 +2681,6 @@ class ChatTextInputPanelNode: ChatInputPanelNode, ASEditableTextNodeDelegate, Ch self.viewOnceButton.isHidden = true } - var clippingDelta: CGFloat = 0.0 - if case let .media(_, _, focused) = interfaceState.inputMode, focused { - clippingDelta = -panelHeight - } - transition.updateFrame(node: self.clippingNode, frame: CGRect(origin: CGPoint(), size: CGSize(width: width, height: panelHeight))) - transition.updateSublayerTransformOffset(layer: self.clippingNode.layer, offset: CGPoint(x: 0.0, y: clippingDelta)) - return panelHeight } diff --git a/submodules/TelegramUI/Sources/SharedAccountContext.swift b/submodules/TelegramUI/Sources/SharedAccountContext.swift index ad4b538a5b..d03f61828d 100644 --- a/submodules/TelegramUI/Sources/SharedAccountContext.swift +++ b/submodules/TelegramUI/Sources/SharedAccountContext.swift @@ -1675,7 +1675,7 @@ public final class SharedAccountContextImpl: SharedAccountContext { return presentAddMembersImpl(context: context, updatedPresentationData: updatedPresentationData, parentController: parentController, groupPeer: groupPeer, selectAddMemberDisposable: selectAddMemberDisposable, addMemberDisposable: addMemberDisposable) } - public func makeChatMessagePreviewItem(context: AccountContext, messages: [Message], theme: PresentationTheme, strings: PresentationStrings, wallpaper: TelegramWallpaper, fontSize: PresentationFontSize, chatBubbleCorners: PresentationChatBubbleCorners, dateTimeFormat: PresentationDateTimeFormat, nameOrder: PresentationPersonNameOrder, forcedResourceStatus: FileMediaResourceStatus?, tapMessage: ((Message) -> Void)?, clickThroughMessage: (() -> Void)? = nil, backgroundNode: ASDisplayNode?, availableReactions: AvailableReactions?, accountPeer: Peer?, isCentered: Bool, isPreview: Bool) -> ListViewItem { + public func makeChatMessagePreviewItem(context: AccountContext, messages: [Message], theme: PresentationTheme, strings: PresentationStrings, wallpaper: TelegramWallpaper, fontSize: PresentationFontSize, chatBubbleCorners: PresentationChatBubbleCorners, dateTimeFormat: PresentationDateTimeFormat, nameOrder: PresentationPersonNameOrder, forcedResourceStatus: FileMediaResourceStatus?, tapMessage: ((Message) -> Void)?, clickThroughMessage: (() -> Void)? = nil, backgroundNode: ASDisplayNode?, availableReactions: AvailableReactions?, accountPeer: Peer?, isCentered: Bool, isPreview: Bool, isStandalone: Bool) -> ListViewItem { let controllerInteraction: ChatControllerInteraction controllerInteraction = ChatControllerInteraction(openMessage: { _, _ in @@ -1770,7 +1770,7 @@ public final class SharedAccountContextImpl: SharedAccountContext { chatLocation = .peer(id: messages.first!.id.peerId) } - return ChatMessageItemImpl(presentationData: ChatPresentationData(theme: ChatPresentationThemeData(theme: theme, wallpaper: wallpaper), fontSize: fontSize, strings: strings, dateTimeFormat: dateTimeFormat, nameDisplayOrder: nameOrder, disableAnimations: false, largeEmoji: false, chatBubbleCorners: chatBubbleCorners, animatedEmojiScale: 1.0, isPreview: isPreview), context: context, chatLocation: chatLocation, associatedData: ChatMessageItemAssociatedData(automaticDownloadPeerType: .contact, automaticDownloadPeerId: nil, automaticDownloadNetworkType: .cellular, isRecentActions: false, subject: nil, contactsPeerIds: Set(), animatedEmojiStickers: [:], forcedResourceStatus: forcedResourceStatus, availableReactions: availableReactions, defaultReaction: nil, isPremium: false, accountPeer: accountPeer.flatMap(EnginePeer.init), forceInlineReactions: true), controllerInteraction: controllerInteraction, content: content, disableDate: true, additionalContent: nil) + return ChatMessageItemImpl(presentationData: ChatPresentationData(theme: ChatPresentationThemeData(theme: theme, wallpaper: wallpaper), fontSize: fontSize, strings: strings, dateTimeFormat: dateTimeFormat, nameDisplayOrder: nameOrder, disableAnimations: false, largeEmoji: false, chatBubbleCorners: chatBubbleCorners, animatedEmojiScale: 1.0, isPreview: isPreview), context: context, chatLocation: chatLocation, associatedData: ChatMessageItemAssociatedData(automaticDownloadPeerType: .contact, automaticDownloadPeerId: nil, automaticDownloadNetworkType: .cellular, isRecentActions: false, subject: nil, contactsPeerIds: Set(), animatedEmojiStickers: [:], forcedResourceStatus: forcedResourceStatus, availableReactions: availableReactions, defaultReaction: nil, isPremium: false, accountPeer: accountPeer.flatMap(EnginePeer.init), forceInlineReactions: true, isStandalone: isStandalone), controllerInteraction: controllerInteraction, content: content, disableDate: true, additionalContent: nil) } public func makeChatMessageDateHeaderItem(context: AccountContext, timestamp: Int32, theme: PresentationTheme, strings: PresentationStrings, wallpaper: TelegramWallpaper, fontSize: PresentationFontSize, chatBubbleCorners: PresentationChatBubbleCorners, dateTimeFormat: PresentationDateTimeFormat, nameOrder: PresentationPersonNameOrder) -> ListViewItemHeader { @@ -2094,6 +2094,50 @@ public final class SharedAccountContextImpl: SharedAccountContext { return controller } + public func makePremiumPrivacyControllerController(context: AccountContext, subject: PremiumPrivacySubject) -> ViewController { + let mappedSubject: PremiumPrivacyScreen.Subject + let introSource: PremiumIntroSource + switch subject { + case .presence: + mappedSubject = .presence + introSource = .presence + case .readTime: + mappedSubject = .readTime + introSource = .presence + } + + var presentTooltipImpl: (() -> Void)? + var openPremiumIntroImpl: (() -> Void)? + + let controller = PremiumPrivacyScreen( + context: context, + subject: mappedSubject, + action: { + presentTooltipImpl?() + }, openPremiumIntro: { + openPremiumIntroImpl?() + } + ) + presentTooltipImpl = { [weak controller] in + guard let parentController = controller else { + return + } + let presentationData = context.sharedContext.currentPresentationData.with { $0 } + parentController.present(UndoOverlayController(presentationData: presentationData, content: .info(title: nil, text: "Your last seen time is now visible.", timeout: 5.0, customUndoText: nil), elevatedLayout: true, action: { _ in + return true + }), in: .window(.root)) + } + openPremiumIntroImpl = { [weak controller] in + guard let parentController = controller else { + return + } + let controller = context.sharedContext.makePremiumIntroController(context: context, source: introSource, forceDark: false, dismissed: nil) + parentController.push(controller) + } + + return controller + } + 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/TelegramUI/Sources/TelegramRootController.swift b/submodules/TelegramUI/Sources/TelegramRootController.swift index cabf003a6c..2c5a9f5ec7 100644 --- a/submodules/TelegramUI/Sources/TelegramRootController.swift +++ b/submodules/TelegramUI/Sources/TelegramRootController.swift @@ -28,6 +28,8 @@ import ImageCompression import TextFormat import MediaEditor import PeerInfoScreen +import PremiumUI +import UndoUI private class DetailsChatPlaceholderNode: ASDisplayNode, NavigationDetailsPlaceholderNode { private var presentationData: PresentationData @@ -219,6 +221,11 @@ public final class TelegramRootController: NavigationController, TelegramRootCon self.accountSettingsController = accountSettingsController self.rootTabController = tabBarController self.pushViewController(tabBarController, animated: false) + + Queue.mainQueue().after(0.1) { + let controller = self.context.sharedContext.makePremiumPrivacyControllerController(context: self.context, subject: .presence) + self.chatListController?.push(controller) + } } public func updateRootControllers(showCallsTab: Bool) {