mirror of
https://github.com/Swiftgram/Telegram-iOS.git
synced 2025-06-16 05:55:20 +00:00
Various improvements
This commit is contained in:
parent
049c7f9c0c
commit
107a48b53d
@ -905,7 +905,7 @@ public protocol SharedAccountContext: AnyObject {
|
||||
selectedMessages: Signal<Set<MessageId>?, 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<PresentationData, NoError>)?, 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
|
||||
}
|
||||
}
|
||||
|
@ -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
|
||||
}
|
||||
}
|
||||
|
289
submodules/AccountContext/Sources/PeerNameColors.swift
Normal file
289
submodules/AccountContext/Sources/PeerNameColors.swift
Normal file
@ -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
|
||||
}
|
||||
}
|
183
submodules/AccountContext/Sources/Premium.swift
Normal file
183
submodules/AccountContext/Sources/Premium.swift
Normal file
@ -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
|
||||
}
|
||||
}
|
||||
}
|
@ -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
|
||||
}
|
||||
|
@ -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
|
||||
|
@ -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())
|
||||
})
|
||||
}
|
||||
|
||||
|
@ -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<TGModernConversationInputMicButtonDelegate> delegate = _delegate;
|
||||
if ([delegate respondsToSelector:@selector(micButtonInteractionUpdateCancelTranslation:)])
|
||||
if ([delegate respondsToSelector:@selector(micButtonInteractionUpdateCancelTranslation:)] && !_skipCancelUpdate)
|
||||
[delegate micButtonInteractionUpdateCancelTranslation:-_cancelTargetTranslation];
|
||||
|
||||
_innerIconView.transform = CGAffineTransformMakeScale(0.3f, 0.3f);
|
||||
|
@ -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]
|
||||
|
||||
|
@ -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
|
||||
}
|
||||
|
@ -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
|
||||
|
507
submodules/PremiumUI/Sources/PremiumPrivacyScreen.swift
Normal file
507
submodules/PremiumUI/Sources/PremiumPrivacyScreen.swift
Normal file
@ -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<EnvironmentType>.self)
|
||||
let animateOut = StoredActionSlot(Action<Void>.self)
|
||||
|
||||
let sheetExternalState = SheetComponent<EnvironmentType>.ExternalState()
|
||||
|
||||
return { context in
|
||||
let environment = context.environment[EnvironmentType.self]
|
||||
|
||||
let controller = environment.controller
|
||||
|
||||
let sheet = sheet.update(
|
||||
component: SheetComponent<EnvironmentType>(
|
||||
content: AnyComponent<EnvironmentType>(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<ViewControllerComponentContainer.Environment>.View.Tag()) as? SheetComponent<ViewControllerComponentContainer.Environment>.View {
|
||||
view.dismissAnimated()
|
||||
}
|
||||
}
|
||||
}
|
@ -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 {
|
||||
|
@ -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 {
|
||||
|
@ -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)."))
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -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 {
|
||||
|
@ -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
|
||||
|
@ -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] = []
|
||||
|
@ -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! {
|
||||
|
@ -14,6 +14,7 @@ swift_library(
|
||||
"//submodules/SSignalKit/SwiftSignalKit",
|
||||
"//submodules/Display",
|
||||
"//submodules/MediaPlayer:UniversalMediaPlayer",
|
||||
"//submodules/AnimatedCountLabelNode",
|
||||
],
|
||||
visibility = [
|
||||
"//visibility:public",
|
||||
|
@ -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])
|
||||
|
||||
|
@ -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 {
|
||||
|
@ -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))
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -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
|
||||
|
@ -20,7 +20,6 @@ swift_library(
|
||||
"//submodules/AccountContext",
|
||||
"//submodules/PhotoResources",
|
||||
"//submodules/TelegramStringFormatting",
|
||||
"//submodules/RadialStatusNode",
|
||||
"//submodules/SemanticStatusNode",
|
||||
"//submodules/FileMediaResourceStatus",
|
||||
"//submodules/CheckNode",
|
||||
|
@ -10,7 +10,6 @@ import TelegramPresentationData
|
||||
import AccountContext
|
||||
import PhotoResources
|
||||
import TelegramStringFormatting
|
||||
import RadialStatusNode
|
||||
import SemanticStatusNode
|
||||
import FileMediaResourceStatus
|
||||
import CheckNode
|
||||
|
@ -20,6 +20,7 @@ swift_library(
|
||||
"//submodules/TelegramPresentationData",
|
||||
"//submodules/AccountContext",
|
||||
"//submodules/RadialStatusNode",
|
||||
"//submodules/SemanticStatusNode",
|
||||
"//submodules/PhotoResources",
|
||||
"//submodules/TelegramUniversalVideoContent",
|
||||
"//submodules/FileMediaResourceStatus",
|
||||
|
@ -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<Empty>?
|
||||
|
||||
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
|
||||
|
@ -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))
|
||||
|
@ -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
|
||||
|
@ -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] = []
|
||||
|
@ -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 {
|
||||
|
@ -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
|
||||
}
|
||||
|
||||
|
@ -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)
|
||||
|
12
submodules/TelegramUI/Images.xcassets/Premium/PrivacyPresence.imageset/Contents.json
vendored
Normal file
12
submodules/TelegramUI/Images.xcassets/Premium/PrivacyPresence.imageset/Contents.json
vendored
Normal file
@ -0,0 +1,12 @@
|
||||
{
|
||||
"images" : [
|
||||
{
|
||||
"filename" : "LastSeen.pdf",
|
||||
"idiom" : "universal"
|
||||
}
|
||||
],
|
||||
"info" : {
|
||||
"author" : "xcode",
|
||||
"version" : 1
|
||||
}
|
||||
}
|
236
submodules/TelegramUI/Images.xcassets/Premium/PrivacyPresence.imageset/LastSeen.pdf
vendored
Normal file
236
submodules/TelegramUI/Images.xcassets/Premium/PrivacyPresence.imageset/LastSeen.pdf
vendored
Normal file
@ -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
|
12
submodules/TelegramUI/Images.xcassets/Premium/PrivacyReadTime.imageset/Contents.json
vendored
Normal file
12
submodules/TelegramUI/Images.xcassets/Premium/PrivacyReadTime.imageset/Contents.json
vendored
Normal file
@ -0,0 +1,12 @@
|
||||
{
|
||||
"images" : [
|
||||
{
|
||||
"filename" : "ReadTime.pdf",
|
||||
"idiom" : "universal"
|
||||
}
|
||||
],
|
||||
"info" : {
|
||||
"author" : "xcode",
|
||||
"version" : 1
|
||||
}
|
||||
}
|
277
submodules/TelegramUI/Images.xcassets/Premium/PrivacyReadTime.imageset/ReadTime.pdf
vendored
Normal file
277
submodules/TelegramUI/Images.xcassets/Premium/PrivacyReadTime.imageset/ReadTime.pdf
vendored
Normal file
@ -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
|
File diff suppressed because one or more lines are too long
@ -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
|
||||
|
||||
|
@ -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))
|
||||
}
|
||||
|
@ -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
|
||||
|
@ -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
|
||||
}
|
||||
|
||||
|
@ -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<PresentationData, NoError>)?, 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)
|
||||
}
|
||||
|
@ -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) {
|
||||
|
Loading…
x
Reference in New Issue
Block a user