Various improvements

This commit is contained in:
Ilya Laktyushin 2024-01-08 17:03:26 +04:00
parent 049c7f9c0c
commit 107a48b53d
45 changed files with 1912 additions and 701 deletions

View File

@ -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
}
}

View File

@ -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
}
}

View 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
}
}

View 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
}
}
}

View File

@ -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
}

View File

@ -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

View File

@ -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())
})
}

View File

@ -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);

View File

@ -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]

View File

@ -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
}

View File

@ -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

View 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()
}
}
}

View File

@ -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 {

View File

@ -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 {

View File

@ -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)."))
}
}

View File

@ -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 {

View File

@ -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

View File

@ -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] = []

View File

@ -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! {

View File

@ -14,6 +14,7 @@ swift_library(
"//submodules/SSignalKit/SwiftSignalKit",
"//submodules/Display",
"//submodules/MediaPlayer:UniversalMediaPlayer",
"//submodules/AnimatedCountLabelNode",
],
visibility = [
"//visibility:public",

View File

@ -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])

View File

@ -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 {

View File

@ -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))
}
}
}
}

View File

@ -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

View File

@ -20,7 +20,6 @@ swift_library(
"//submodules/AccountContext",
"//submodules/PhotoResources",
"//submodules/TelegramStringFormatting",
"//submodules/RadialStatusNode",
"//submodules/SemanticStatusNode",
"//submodules/FileMediaResourceStatus",
"//submodules/CheckNode",

View File

@ -10,7 +10,6 @@ import TelegramPresentationData
import AccountContext
import PhotoResources
import TelegramStringFormatting
import RadialStatusNode
import SemanticStatusNode
import FileMediaResourceStatus
import CheckNode

View File

@ -20,6 +20,7 @@ swift_library(
"//submodules/TelegramPresentationData",
"//submodules/AccountContext",
"//submodules/RadialStatusNode",
"//submodules/SemanticStatusNode",
"//submodules/PhotoResources",
"//submodules/TelegramUniversalVideoContent",
"//submodules/FileMediaResourceStatus",

View File

@ -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

View File

@ -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))

View File

@ -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

View File

@ -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] = []

View File

@ -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 {

View File

@ -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
}

View File

@ -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)

View File

@ -0,0 +1,12 @@
{
"images" : [
{
"filename" : "LastSeen.pdf",
"idiom" : "universal"
}
],
"info" : {
"author" : "xcode",
"version" : 1
}
}

View 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

View File

@ -0,0 +1,12 @@
{
"images" : [
{
"filename" : "ReadTime.pdf",
"idiom" : "universal"
}
],
"info" : {
"author" : "xcode",
"version" : 1
}
}

View 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

View File

@ -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

View File

@ -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))
}

View File

@ -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

View File

@ -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
}

View File

@ -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)
}

View File

@ -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) {