Merge commit 'ed86e369ebda242bb9cf0f38df3821586bd7d307' into macos-9.5-release

This commit is contained in:
Mike Renoir 2023-03-08 12:03:01 +04:00
commit 080eba2533
43 changed files with 622 additions and 1392 deletions

View File

@ -38,7 +38,7 @@ class NotificationViewController: UIViewController, UNNotificationContentExtensi
let appVersion = (Bundle.main.infoDictionary?["CFBundleShortVersionString"] as? String) ?? "unknown" let appVersion = (Bundle.main.infoDictionary?["CFBundleShortVersionString"] as? String) ?? "unknown"
self.impl = NotificationViewControllerImpl(initializationData: NotificationViewControllerInitializationData(appBundleId: baseAppBundleId, appBuildType: buildConfig.isAppStoreBuild ? .public : .internal, appGroupPath: appGroupUrl.path, apiId: buildConfig.apiId, apiHash: buildConfig.apiHash, languagesCategory: languagesCategory, encryptionParameters: encryptionParameters, appVersion: appVersion, bundleData: buildConfig.bundleData(withAppToken: nil, signatureDict: nil)), setPreferredContentSize: { [weak self] size in self.impl = NotificationViewControllerImpl(initializationData: NotificationViewControllerInitializationData(appBundleId: baseAppBundleId, appBuildType: buildConfig.isAppStoreBuild ? .public : .internal, appGroupPath: appGroupUrl.path, apiId: buildConfig.apiId, apiHash: buildConfig.apiHash, languagesCategory: languagesCategory, encryptionParameters: encryptionParameters, appVersion: appVersion, bundleData: buildConfig.bundleData(withAppToken: nil, signatureDict: nil), useBetaFeatures: !buildConfig.isAppStoreBuild), setPreferredContentSize: { [weak self] size in
self?.preferredContentSize = size self?.preferredContentSize = size
}) })
} }

View File

@ -680,7 +680,7 @@ private final class NotificationServiceHandler {
Logger.shared.logToConsole = loggingSettings.logToConsole Logger.shared.logToConsole = loggingSettings.logToConsole
Logger.shared.redactSensitiveData = loggingSettings.redactSensitiveData Logger.shared.redactSensitiveData = loggingSettings.redactSensitiveData
let networkArguments = NetworkInitializationArguments(apiId: apiId, apiHash: apiHash, languagesCategory: languagesCategory, appVersion: appVersion, voipMaxLayer: 0, voipVersions: [], appData: .single(buildConfig.bundleData(withAppToken: nil, signatureDict: nil)), autolockDeadine: .single(nil), encryptionProvider: OpenSSLEncryptionProvider(), deviceModelName: nil) let networkArguments = NetworkInitializationArguments(apiId: apiId, apiHash: apiHash, languagesCategory: languagesCategory, appVersion: appVersion, voipMaxLayer: 0, voipVersions: [], appData: .single(buildConfig.bundleData(withAppToken: nil, signatureDict: nil)), autolockDeadine: .single(nil), encryptionProvider: OpenSSLEncryptionProvider(), deviceModelName: nil, useBetaFeatures: !buildConfig.isAppStoreBuild)
let isLockedMessage: String? let isLockedMessage: String?
if let data = try? Data(contentsOf: URL(fileURLWithPath: appLockStatePath(rootPath: rootPath))), let state = try? JSONDecoder().decode(LockState.self, from: data), isAppLocked(state: state) { if let data = try? Data(contentsOf: URL(fileURLWithPath: appLockStatePath(rootPath: rootPath))), let state = try? JSONDecoder().decode(LockState.self, from: data), isAppLocked(state: state) {

View File

@ -45,7 +45,7 @@ class ShareRootController: UIViewController {
let appVersion = (Bundle.main.infoDictionary?["CFBundleShortVersionString"] as? String) ?? "unknown" let appVersion = (Bundle.main.infoDictionary?["CFBundleShortVersionString"] as? String) ?? "unknown"
self.impl = ShareRootControllerImpl(initializationData: ShareRootControllerInitializationData(appBundleId: baseAppBundleId, appBuildType: buildConfig.isAppStoreBuild ? .public : .internal, appGroupPath: appGroupUrl.path, apiId: buildConfig.apiId, apiHash: buildConfig.apiHash, languagesCategory: languagesCategory, encryptionParameters: encryptionParameters, appVersion: appVersion, bundleData: buildConfig.bundleData(withAppToken: nil, signatureDict: nil)), getExtensionContext: { [weak self] in self.impl = ShareRootControllerImpl(initializationData: ShareRootControllerInitializationData(appBundleId: baseAppBundleId, appBuildType: buildConfig.isAppStoreBuild ? .public : .internal, appGroupPath: appGroupUrl.path, apiId: buildConfig.apiId, apiHash: buildConfig.apiHash, languagesCategory: languagesCategory, encryptionParameters: encryptionParameters, appVersion: appVersion, bundleData: buildConfig.bundleData(withAppToken: nil, signatureDict: nil), useBetaFeatures: !buildConfig.isAppStoreBuild), getExtensionContext: { [weak self] in
return self?.extensionContext return self?.extensionContext
}) })
} }

View File

@ -174,7 +174,7 @@ class DefaultIntentHandler: INExtension, INSendMessageIntentHandling, INSearchFo
if let accountCache = accountCache { if let accountCache = accountCache {
account = .single(accountCache) account = .single(accountCache)
} else { } else {
account = currentAccount(allocateIfNotExists: false, networkArguments: NetworkInitializationArguments(apiId: apiId, apiHash: apiHash, languagesCategory: languagesCategory, appVersion: appVersion, voipMaxLayer: 0, voipVersions: [], appData: .single(buildConfig.bundleData(withAppToken: nil, signatureDict: nil)), autolockDeadine: .single(nil), encryptionProvider: OpenSSLEncryptionProvider(), deviceModelName: nil), supplementary: true, manager: accountManager, rootPath: rootPath, auxiliaryMethods: accountAuxiliaryMethods, encryptionParameters: encryptionParameters) account = currentAccount(allocateIfNotExists: false, networkArguments: NetworkInitializationArguments(apiId: apiId, apiHash: apiHash, languagesCategory: languagesCategory, appVersion: appVersion, voipMaxLayer: 0, voipVersions: [], appData: .single(buildConfig.bundleData(withAppToken: nil, signatureDict: nil)), autolockDeadine: .single(nil), encryptionProvider: OpenSSLEncryptionProvider(), deviceModelName: nil, useBetaFeatures: !buildConfig.isAppStoreBuild), supplementary: true, manager: accountManager, rootPath: rootPath, auxiliaryMethods: accountAuxiliaryMethods, encryptionParameters: encryptionParameters)
|> mapToSignal { account -> Signal<Account?, NoError> in |> mapToSignal { account -> Signal<Account?, NoError> in
if let account = account { if let account = account {
switch account { switch account {

View File

@ -9041,3 +9041,14 @@ Sorry for the inconvenience.";
"Premium.Gift.TitleShort" = "Telegram Premium"; "Premium.Gift.TitleShort" = "Telegram Premium";
"VoiceOver.GiftPremium" = "Gift Telegram Premium"; "VoiceOver.GiftPremium" = "Gift Telegram Premium";
"Login.Email.CantAccess" = "Can't access this email?";
"Login.Email.ResetTitle" = "Reset Email";
"Login.Email.ResetText" = "You can change your login email if you are logged into Telegram from another device. Otherwise, if you don't have access to email %@, you can reset this email with an SMS code in 7 days.";
"Login.Email.Reset" = "Reset";
"Login.Email.ResetNowViaSMS" = "Reset now via SMS";
"Login.Email.WillBeResetIn" = "Email will be reset in %@";
"Login.Email.PremiumRequiredTitle" = "Telegram Premium Required";
"Login.Email.PremiumRequiredText" = "Due to high cost of SMS in your country, you need to have a **Telegram Premium** account to reset this email via an SMS code. You can ask a friend to a gift a Premium subscription for your account %@";
"ChatList.StartMessaging" = "Select a chat to start messaging";

View File

@ -433,7 +433,7 @@ final class AttachmentContainer: ASDisplayNode, UIGestureRecognizerDelegate {
let containerFrame: CGRect let containerFrame: CGRect
let clipFrame: CGRect let clipFrame: CGRect
let containerScale: CGFloat let containerScale: CGFloat
if layout.metrics.widthClass == .compact { if case .compact = layout.metrics.widthClass {
self.clipNode.clipsToBounds = true self.clipNode.clipsToBounds = true
if isLandscape { if isLandscape {

View File

@ -4811,7 +4811,7 @@ private final class ChatListLocationContext {
strings: presentationData.strings, strings: presentationData.strings,
dateTimeFormat: presentationData.dateTimeFormat, dateTimeFormat: presentationData.dateTimeFormat,
nameDisplayOrder: presentationData.nameDisplayOrder, nameDisplayOrder: presentationData.nameDisplayOrder,
content: .peer(peerView: peerView, customTitle: nil, onlineMemberCount: onlineMemberCount, isScheduledMessages: false, isMuted: nil, customMessageCount: nil), content: .peer(peerView: peerView, customTitle: nil, onlineMemberCount: onlineMemberCount, isScheduledMessages: false, isMuted: nil, customMessageCount: nil, isEnabled: true),
tapped: { [weak self] in tapped: { [weak self] in
guard let self else { guard let self else {
return return

View File

@ -89,7 +89,6 @@ private enum DebugControllerEntry: ItemListNodeEntry {
case experimentalCompatibility(Bool) case experimentalCompatibility(Bool)
case enableDebugDataDisplay(Bool) case enableDebugDataDisplay(Bool)
case acceleratedStickers(Bool) case acceleratedStickers(Bool)
case experimentalBackground(Bool)
case inlineForums(Bool) case inlineForums(Bool)
case localTranscription(Bool) case localTranscription(Bool)
case enableReactionOverrides(Bool) case enableReactionOverrides(Bool)
@ -118,7 +117,7 @@ private enum DebugControllerEntry: ItemListNodeEntry {
return DebugControllerSection.logging.rawValue return DebugControllerSection.logging.rawValue
case .keepChatNavigationStack, .skipReadHistory, .crashOnSlowQueries: case .keepChatNavigationStack, .skipReadHistory, .crashOnSlowQueries:
return DebugControllerSection.experiments.rawValue return DebugControllerSection.experiments.rawValue
case .clearTips, .resetNotifications, .crash, .resetData, .resetDatabase, .resetDatabaseAndCache, .resetHoles, .reindexUnread, .resetCacheIndex, .reindexCache, .resetBiometricsData, .resetWebViewCache, .optimizeDatabase, .photoPreview, .knockoutWallpaper, .playerEmbedding, .playlistPlayback, .enableQuickReactionSwitch, .voiceConference, .experimentalCompatibility, .enableDebugDataDisplay, .acceleratedStickers, .experimentalBackground, .inlineForums, .localTranscription, .enableReactionOverrides, .restorePurchases: case .clearTips, .resetNotifications, .crash, .resetData, .resetDatabase, .resetDatabaseAndCache, .resetHoles, .reindexUnread, .resetCacheIndex, .reindexCache, .resetBiometricsData, .resetWebViewCache, .optimizeDatabase, .photoPreview, .knockoutWallpaper, .playerEmbedding, .playlistPlayback, .enableQuickReactionSwitch, .voiceConference, .experimentalCompatibility, .enableDebugDataDisplay, .acceleratedStickers, .inlineForums, .localTranscription, .enableReactionOverrides, .restorePurchases:
return DebugControllerSection.experiments.rawValue return DebugControllerSection.experiments.rawValue
case .logTranslationRecognition, .resetTranslationStates: case .logTranslationRecognition, .resetTranslationStates:
return DebugControllerSection.translation.rawValue return DebugControllerSection.translation.rawValue
@ -201,8 +200,6 @@ private enum DebugControllerEntry: ItemListNodeEntry {
return 34 return 34
case .acceleratedStickers: case .acceleratedStickers:
return 35 return 35
case .experimentalBackground:
return 36
case .inlineForums: case .inlineForums:
return 37 return 37
case .localTranscription: case .localTranscription:
@ -1186,16 +1183,6 @@ private enum DebugControllerEntry: ItemListNodeEntry {
}) })
}).start() }).start()
}) })
case let .experimentalBackground(value):
return ItemListSwitchItem(presentationData: presentationData, title: "Background Experiment", value: value, sectionId: self.section, style: .blocks, updated: { value in
let _ = arguments.sharedContext.accountManager.transaction ({ transaction in
transaction.updateSharedData(ApplicationSpecificSharedDataKeys.experimentalUISettings, { settings in
var settings = settings?.get(ExperimentalUISettings.self) ?? ExperimentalUISettings.defaultSettings
settings.experimentalBackground = value
return PreferencesEntry(settings)
})
}).start()
})
case let .inlineForums(value): case let .inlineForums(value):
return ItemListSwitchItem(presentationData: presentationData, title: "Inline Forums", value: value, sectionId: self.section, style: .blocks, updated: { value in return ItemListSwitchItem(presentationData: presentationData, title: "Inline Forums", value: value, sectionId: self.section, style: .blocks, updated: { value in
let _ = arguments.sharedContext.accountManager.transaction ({ transaction in let _ = arguments.sharedContext.accountManager.transaction ({ transaction in
@ -1324,7 +1311,7 @@ private enum DebugControllerEntry: ItemListNodeEntry {
} }
} }
private func debugControllerEntries(sharedContext: SharedAccountContext, presentationData: PresentationData, loggingSettings: LoggingSettings, mediaInputSettings: MediaInputSettings, experimentalSettings: ExperimentalUISettings, networkSettings: NetworkSettings?, hasLegacyAppData: Bool) -> [DebugControllerEntry] { private func debugControllerEntries(sharedContext: SharedAccountContext, presentationData: PresentationData, loggingSettings: LoggingSettings, mediaInputSettings: MediaInputSettings, experimentalSettings: ExperimentalUISettings, networkSettings: NetworkSettings?, hasLegacyAppData: Bool, useBetaFeatures: Bool) -> [DebugControllerEntry] {
var entries: [DebugControllerEntry] = [] var entries: [DebugControllerEntry] = []
let isMainApp = sharedContext.applicationBindings.isMainApp let isMainApp = sharedContext.applicationBindings.isMainApp
@ -1374,7 +1361,6 @@ private func debugControllerEntries(sharedContext: SharedAccountContext, present
entries.append(.experimentalCompatibility(experimentalSettings.experimentalCompatibility)) entries.append(.experimentalCompatibility(experimentalSettings.experimentalCompatibility))
entries.append(.enableDebugDataDisplay(experimentalSettings.enableDebugDataDisplay)) entries.append(.enableDebugDataDisplay(experimentalSettings.enableDebugDataDisplay))
entries.append(.acceleratedStickers(experimentalSettings.acceleratedStickers)) entries.append(.acceleratedStickers(experimentalSettings.acceleratedStickers))
entries.append(.experimentalBackground(experimentalSettings.experimentalBackground))
entries.append(.inlineForums(experimentalSettings.inlineForums)) entries.append(.inlineForums(experimentalSettings.inlineForums))
entries.append(.localTranscription(experimentalSettings.localTranscription)) entries.append(.localTranscription(experimentalSettings.localTranscription))
if case .internal = sharedContext.applicationBindings.appBuildType { if case .internal = sharedContext.applicationBindings.appBuildType {
@ -1404,7 +1390,7 @@ private func debugControllerEntries(sharedContext: SharedAccountContext, present
if isMainApp { if isMainApp {
entries.append(.disableVideoAspectScaling(experimentalSettings.disableVideoAspectScaling)) entries.append(.disableVideoAspectScaling(experimentalSettings.disableVideoAspectScaling))
entries.append(.enableNetworkFramework(networkSettings?.useNetworkFramework ?? false)) entries.append(.enableNetworkFramework(networkSettings?.useNetworkFramework ?? useBetaFeatures))
} }
if let backupHostOverride = networkSettings?.backupHostOverride { if let backupHostOverride = networkSettings?.backupHostOverride {
@ -1476,8 +1462,13 @@ public func debugController(sharedContext: SharedAccountContext, context: Accoun
}) })
} }
var useBetaFeatures: Bool = false
if let context {
useBetaFeatures = context.account.network.useBetaFeatures
}
let controllerState = ItemListControllerState(presentationData: ItemListPresentationData(presentationData), title: .text("Debug"), leftNavigationButton: leftNavigationButton, rightNavigationButton: nil, backNavigationButton: ItemListBackButton(title: presentationData.strings.Common_Back)) let controllerState = ItemListControllerState(presentationData: ItemListPresentationData(presentationData), title: .text("Debug"), leftNavigationButton: leftNavigationButton, rightNavigationButton: nil, backNavigationButton: ItemListBackButton(title: presentationData.strings.Common_Back))
let listState = ItemListNodeState(presentationData: ItemListPresentationData(presentationData), entries: debugControllerEntries(sharedContext: sharedContext, presentationData: presentationData, loggingSettings: loggingSettings, mediaInputSettings: mediaInputSettings, experimentalSettings: experimentalSettings, networkSettings: networkSettings, hasLegacyAppData: hasLegacyAppData), style: .blocks) let listState = ItemListNodeState(presentationData: ItemListPresentationData(presentationData), entries: debugControllerEntries(sharedContext: sharedContext, presentationData: presentationData, loggingSettings: loggingSettings, mediaInputSettings: mediaInputSettings, experimentalSettings: experimentalSettings, networkSettings: networkSettings, hasLegacyAppData: hasLegacyAppData, useBetaFeatures: useBetaFeatures), style: .blocks)
return (controllerState, (listState, arguments)) return (controllerState, (listState, arguments))
} }

View File

@ -34,11 +34,6 @@ public struct NavigationAnimationOptions : OptionSet {
public static let removeOnMasterDetails = NavigationAnimationOptions(rawValue: 1 << 0) public static let removeOnMasterDetails = NavigationAnimationOptions(rawValue: 1 << 0)
} }
public enum NavigationEmptyDetailsBackgoundMode {
case image(UIImage)
case wallpaper(UIImage)
}
private enum ControllerTransition { private enum ControllerTransition {
case none case none
case appearance case appearance
@ -120,6 +115,10 @@ public final class NavigationControllerDropContent {
} }
} }
public protocol NavigationDetailsPlaceholderNode: ASDisplayNode {
func updateLayout(size: CGSize, needsTiling: Bool, transition: ContainedViewLayoutTransition)
}
open class NavigationController: UINavigationController, ContainableController, UIGestureRecognizerDelegate { open class NavigationController: UINavigationController, ContainableController, UIGestureRecognizerDelegate {
public var isOpaqueWhenInOverlay: Bool = true public var isOpaqueWhenInOverlay: Bool = true
public var blocksBackgroundWhenInOverlay: Bool = true public var blocksBackgroundWhenInOverlay: Bool = true
@ -131,7 +130,6 @@ open class NavigationController: UINavigationController, ContainableController,
} }
private var masterDetailsBlackout: MasterDetailLayoutBlackout? private var masterDetailsBlackout: MasterDetailLayoutBlackout?
private var backgroundDetailsMode: NavigationEmptyDetailsBackgoundMode?
public var lockOrientation: Bool = false public var lockOrientation: Bool = false
@ -232,16 +230,21 @@ open class NavigationController: UINavigationController, ContainableController,
self.requestLayout(transition: transition) self.requestLayout(transition: transition)
} }
public func updateBackgroundDetailsMode(_ mode: NavigationEmptyDetailsBackgoundMode?, transition: ContainedViewLayoutTransition) { private weak var detailsPlaceholderNode: NavigationDetailsPlaceholderNode?
self.backgroundDetailsMode = mode public func updateDetailsPlaceholderNode(_ node: NavigationDetailsPlaceholderNode?) {
self.requestLayout(transition: transition) if self.detailsPlaceholderNode !== node {
self.detailsPlaceholderNode?.removeFromSupernode()
self.detailsPlaceholderNode = node
if let node {
self.displayNode.insertSubnode(node, at: 0)
}
}
} }
public init(mode: NavigationControllerMode, theme: NavigationControllerTheme, isFlat: Bool = false, backgroundDetailsMode: NavigationEmptyDetailsBackgoundMode? = nil) { public init(mode: NavigationControllerMode, theme: NavigationControllerTheme, isFlat: Bool = false) {
self.mode = mode self.mode = mode
self.theme = theme self.theme = theme
self.isFlat = isFlat self.isFlat = isFlat
self.backgroundDetailsMode = backgroundDetailsMode
super.init(nibName: nil, bundle: nil) super.init(nibName: nil, bundle: nil)
} }
@ -340,7 +343,7 @@ open class NavigationController: UINavigationController, ContainableController,
return nil return nil
} }
public func containerLayoutUpdated(_ layout: ContainerViewLayout, transition: ContainedViewLayoutTransition) { open func containerLayoutUpdated(_ layout: ContainerViewLayout, transition: ContainedViewLayoutTransition) {
if !self.isViewLoaded { if !self.isViewLoaded {
self.loadView() self.loadView()
} }
@ -836,7 +839,11 @@ open class NavigationController: UINavigationController, ContainableController,
flatContainer.keyboardViewManager = nil flatContainer.keyboardViewManager = nil
flatContainer.canHaveKeyboardFocus = false flatContainer.canHaveKeyboardFocus = false
} }
self.displayNode.insertSubnode(flatContainer, at: 0) if let detailsPlaceholderNode = self.detailsPlaceholderNode {
self.displayNode.insertSubnode(flatContainer, aboveSubnode: detailsPlaceholderNode)
} else {
self.displayNode.insertSubnode(flatContainer, at: 0)
}
self.rootContainer = .flat(flatContainer) self.rootContainer = .flat(flatContainer)
flatContainer.frame = CGRect(origin: CGPoint(), size: layout.size) flatContainer.frame = CGRect(origin: CGPoint(), size: layout.size)
flatContainer.update(layout: layout, canBeClosed: false, controllers: controllers, transition: .immediate) flatContainer.update(layout: layout, canBeClosed: false, controllers: controllers, transition: .immediate)
@ -859,7 +866,11 @@ open class NavigationController: UINavigationController, ContainableController,
flatContainer.keyboardViewManager = nil flatContainer.keyboardViewManager = nil
flatContainer.canHaveKeyboardFocus = false flatContainer.canHaveKeyboardFocus = false
} }
self.displayNode.insertSubnode(flatContainer, at: 0) if let detailsPlaceholderNode = self.detailsPlaceholderNode {
self.displayNode.insertSubnode(flatContainer, aboveSubnode: detailsPlaceholderNode)
} else {
self.displayNode.insertSubnode(flatContainer, at: 0)
}
self.rootContainer = .flat(flatContainer) self.rootContainer = .flat(flatContainer)
flatContainer.frame = CGRect(origin: CGPoint(), size: layout.size) flatContainer.frame = CGRect(origin: CGPoint(), size: layout.size)
flatContainer.update(layout: layout, canBeClosed: false, controllers: controllers, transition: .immediate) flatContainer.update(layout: layout, canBeClosed: false, controllers: controllers, transition: .immediate)
@ -873,7 +884,11 @@ open class NavigationController: UINavigationController, ContainableController,
}, scrollToTop: { [weak self] subject in }, scrollToTop: { [weak self] subject in
self?.scrollToTop(subject) self?.scrollToTop(subject)
}) })
self.displayNode.insertSubnode(splitContainer, at: 0) if let detailsPlaceholderNode = self.detailsPlaceholderNode {
self.displayNode.insertSubnode(splitContainer, aboveSubnode: detailsPlaceholderNode)
} else {
self.displayNode.insertSubnode(splitContainer, at: 0)
}
self.rootContainer = .split(splitContainer) self.rootContainer = .split(splitContainer)
if previousModalContainer == nil { if previousModalContainer == nil {
splitContainer.canHaveKeyboardFocus = true splitContainer.canHaveKeyboardFocus = true
@ -881,7 +896,7 @@ open class NavigationController: UINavigationController, ContainableController,
splitContainer.canHaveKeyboardFocus = false splitContainer.canHaveKeyboardFocus = false
} }
splitContainer.frame = CGRect(origin: CGPoint(), size: layout.size) splitContainer.frame = CGRect(origin: CGPoint(), size: layout.size)
splitContainer.update(layout: layout, masterControllers: masterControllers, detailControllers: detailControllers, transition: .immediate) splitContainer.update(layout: layout, masterControllers: masterControllers, detailControllers: detailControllers, detailsPlaceholderNode: self.detailsPlaceholderNode, transition: .immediate)
flatContainer.statusBarStyleUpdated = nil flatContainer.statusBarStyleUpdated = nil
flatContainer.removeFromSupernode() flatContainer.removeFromSupernode()
case let .split(splitContainer): case let .split(splitContainer):
@ -891,7 +906,7 @@ open class NavigationController: UINavigationController, ContainableController,
splitContainer.canHaveKeyboardFocus = false splitContainer.canHaveKeyboardFocus = false
} }
transition.updateFrame(node: splitContainer, frame: CGRect(origin: CGPoint(), size: layout.size)) transition.updateFrame(node: splitContainer, frame: CGRect(origin: CGPoint(), size: layout.size))
splitContainer.update(layout: layout, masterControllers: masterControllers, detailControllers: detailControllers, transition: transition) splitContainer.update(layout: layout, masterControllers: masterControllers, detailControllers: detailControllers, detailsPlaceholderNode: self.detailsPlaceholderNode, transition: transition)
} }
} else { } else {
let splitContainer = NavigationSplitContainer(theme: self.theme, controllerRemoved: { [weak self] controller in let splitContainer = NavigationSplitContainer(theme: self.theme, controllerRemoved: { [weak self] controller in
@ -899,7 +914,11 @@ open class NavigationController: UINavigationController, ContainableController,
}, scrollToTop: { [weak self] subject in }, scrollToTop: { [weak self] subject in
self?.scrollToTop(subject) self?.scrollToTop(subject)
}) })
self.displayNode.insertSubnode(splitContainer, at: 0) if let detailsPlaceholderNode = self.detailsPlaceholderNode {
self.displayNode.insertSubnode(splitContainer, aboveSubnode: detailsPlaceholderNode)
} else {
self.displayNode.insertSubnode(splitContainer, at: 0)
}
self.rootContainer = .split(splitContainer) self.rootContainer = .split(splitContainer)
if previousModalContainer == nil { if previousModalContainer == nil {
splitContainer.canHaveKeyboardFocus = true splitContainer.canHaveKeyboardFocus = true
@ -907,7 +926,7 @@ open class NavigationController: UINavigationController, ContainableController,
splitContainer.canHaveKeyboardFocus = false splitContainer.canHaveKeyboardFocus = false
} }
splitContainer.frame = CGRect(origin: CGPoint(), size: layout.size) splitContainer.frame = CGRect(origin: CGPoint(), size: layout.size)
splitContainer.update(layout: layout, masterControllers: masterControllers, detailControllers: detailControllers, transition: .immediate) splitContainer.update(layout: layout, masterControllers: masterControllers, detailControllers: detailControllers, detailsPlaceholderNode: self.detailsPlaceholderNode, transition: .immediate)
} }
} }

View File

@ -83,7 +83,7 @@ final class NavigationSplitContainer: ASDisplayNode {
self.separator.backgroundColor = theme.navigationBar.separatorColor self.separator.backgroundColor = theme.navigationBar.separatorColor
} }
func update(layout: ContainerViewLayout, masterControllers: [ViewController], detailControllers: [ViewController], transition: ContainedViewLayoutTransition) { func update(layout: ContainerViewLayout, masterControllers: [ViewController], detailControllers: [ViewController], detailsPlaceholderNode: NavigationDetailsPlaceholderNode?, transition: ContainedViewLayoutTransition) {
let masterWidth: CGFloat = min(max(320.0, floor(layout.size.width / 3.0)), floor(layout.size.width / 2.0)) let masterWidth: CGFloat = min(max(320.0, floor(layout.size.width / 3.0)), floor(layout.size.width / 2.0))
let detailWidth = layout.size.width - masterWidth let detailWidth = layout.size.width - masterWidth
@ -94,6 +94,12 @@ final class NavigationSplitContainer: ASDisplayNode {
transition.updateFrame(node: self.detailContainer, frame: CGRect(origin: CGPoint(x: masterWidth, y: 0.0), size: CGSize(width: detailWidth, height: layout.size.height))) transition.updateFrame(node: self.detailContainer, frame: CGRect(origin: CGPoint(x: masterWidth, y: 0.0), size: CGSize(width: detailWidth, height: layout.size.height)))
transition.updateFrame(node: self.separator, frame: CGRect(origin: CGPoint(x: masterWidth, y: 0.0), size: CGSize(width: UIScreenPixel, height: layout.size.height))) transition.updateFrame(node: self.separator, frame: CGRect(origin: CGPoint(x: masterWidth, y: 0.0), size: CGSize(width: UIScreenPixel, height: layout.size.height)))
if let detailsPlaceholderNode {
let needsTiling = layout.size.width > layout.size.height
detailsPlaceholderNode.updateLayout(size: CGSize(width: detailWidth, height: layout.size.height), needsTiling: needsTiling, transition: transition)
transition.updateFrame(node: detailsPlaceholderNode, frame: CGRect(origin: CGPoint(x: masterWidth, y: 0.0), size: CGSize(width: detailWidth, height: layout.size.height)))
}
self.masterContainer.update(layout: ContainerViewLayout(size: CGSize(width: masterWidth, height: layout.size.height), metrics: layout.metrics, deviceMetrics: layout.deviceMetrics, intrinsicInsets: layout.intrinsicInsets, safeInsets: layout.safeInsets, additionalInsets: UIEdgeInsets(), statusBarHeight: layout.statusBarHeight, inputHeight: layout.inputHeight, inputHeightIsInteractivellyChanging: layout.inputHeightIsInteractivellyChanging, inVoiceOver: layout.inVoiceOver), canBeClosed: false, controllers: masterControllers, transition: transition) self.masterContainer.update(layout: ContainerViewLayout(size: CGSize(width: masterWidth, height: layout.size.height), metrics: layout.metrics, deviceMetrics: layout.deviceMetrics, intrinsicInsets: layout.intrinsicInsets, safeInsets: layout.safeInsets, additionalInsets: UIEdgeInsets(), statusBarHeight: layout.statusBarHeight, inputHeight: layout.inputHeight, inputHeightIsInteractivellyChanging: layout.inputHeightIsInteractivellyChanging, inVoiceOver: layout.inVoiceOver), canBeClosed: false, controllers: masterControllers, transition: transition)
self.detailContainer.update(layout: ContainerViewLayout(size: CGSize(width: detailWidth, height: layout.size.height), metrics: layout.metrics, deviceMetrics: layout.deviceMetrics, intrinsicInsets: layout.intrinsicInsets, safeInsets: layout.safeInsets, additionalInsets: layout.additionalInsets, statusBarHeight: layout.statusBarHeight, inputHeight: layout.inputHeight, inputHeightIsInteractivellyChanging: layout.inputHeightIsInteractivellyChanging, inVoiceOver: layout.inVoiceOver), canBeClosed: true, controllers: detailControllers, transition: transition) self.detailContainer.update(layout: ContainerViewLayout(size: CGSize(width: detailWidth, height: layout.size.height), metrics: layout.metrics, deviceMetrics: layout.deviceMetrics, intrinsicInsets: layout.intrinsicInsets, safeInsets: layout.safeInsets, additionalInsets: layout.additionalInsets, statusBarHeight: layout.statusBarHeight, inputHeight: layout.inputHeight, inputHeightIsInteractivellyChanging: layout.inputHeightIsInteractivellyChanging, inVoiceOver: layout.inVoiceOver), canBeClosed: true, controllers: detailControllers, transition: transition)

View File

@ -271,6 +271,22 @@ public final class GradientBackgroundNode: ASDisplayNode {
private var patternOverlayLayer: GradientBackgroundPatternOverlayLayer? private var patternOverlayLayer: GradientBackgroundPatternOverlayLayer?
private class SharedAnimationUpdate {
let phase: Int
let sender: AnyObject
init(
phase: Int,
sender: AnyObject
) {
self.phase = phase
self.sender = sender
}
}
private static let sharedAnimationSyncPipe = ValuePipe<SharedAnimationUpdate>()
private var sharedAnimationSyncDisposable: Disposable?
public init(colors: [UIColor]? = nil, useSharedAnimationPhase: Bool = false, adjustSaturation: Bool = true) { public init(colors: [UIColor]? = nil, useSharedAnimationPhase: Bool = false, adjustSaturation: Bool = true) {
self.useSharedAnimationPhase = useSharedAnimationPhase self.useSharedAnimationPhase = useSharedAnimationPhase
self.saturation = adjustSaturation ? 1.7 : 1.0 self.saturation = adjustSaturation ? 1.7 : 1.0
@ -289,12 +305,26 @@ public final class GradientBackgroundNode: ASDisplayNode {
if useSharedAnimationPhase { if useSharedAnimationPhase {
self.phase = GradientBackgroundNode.sharedPhase self.phase = GradientBackgroundNode.sharedPhase
self.sharedAnimationSyncDisposable = (GradientBackgroundNode.sharedAnimationSyncPipe.signal()
|> filter { [weak self] update in
return update.sender !== self
}
|> deliverOnMainQueue).start(next: { [weak self] update in
if let self {
self.phase = update.phase
if let size = self.validLayout {
self.updateLayout(size: size, transition: .immediate, extendAnimation: false, backwards: false, completion: {})
}
}
})
} else { } else {
self.phase = 0 self.phase = 0
} }
} }
deinit { deinit {
self.sharedAnimationSyncDisposable?.dispose()
} }
public func setPatternOverlay(layer: GradientBackgroundPatternOverlayLayer?) { public func setPatternOverlay(layer: GradientBackgroundPatternOverlayLayer?) {
@ -423,7 +453,6 @@ public final class GradientBackgroundNode: ASDisplayNode {
animation.beginTime = self.contentView.layer.convertTime(CACurrentMediaTime(), from: nil) + 0.25 animation.beginTime = self.contentView.layer.convertTime(CACurrentMediaTime(), from: nil) + 0.25
} }
self.isAnimating = true self.isAnimating = true
if let patternOverlayLayer = self.patternOverlayLayer { if let patternOverlayLayer = self.patternOverlayLayer {
patternOverlayLayer.isAnimating = true patternOverlayLayer.isAnimating = true
@ -543,6 +572,8 @@ public final class GradientBackgroundNode: ASDisplayNode {
} }
} }
public func animateEvent(transition: ContainedViewLayoutTransition, extendAnimation: Bool, backwards: Bool, completion: @escaping () -> Void) { public func animateEvent(transition: ContainedViewLayoutTransition, extendAnimation: Bool, backwards: Bool, completion: @escaping () -> Void) {
guard case let .animated(duration, _) = transition, duration > 0.001 else { guard case let .animated(duration, _) = transition, duration > 0.001 else {
completion() completion()
@ -560,6 +591,7 @@ public final class GradientBackgroundNode: ASDisplayNode {
} }
if self.useSharedAnimationPhase { if self.useSharedAnimationPhase {
GradientBackgroundNode.sharedPhase = self.phase GradientBackgroundNode.sharedPhase = self.phase
GradientBackgroundNode.sharedAnimationSyncPipe.putNext(SharedAnimationUpdate(phase: self.phase, sender: self))
} }
if let size = self.validLayout { if let size = self.validLayout {
self.updateLayout(size: size, transition: transition, extendAnimation: extendAnimation, backwards: backwards, completion: completion) self.updateLayout(size: size, transition: transition, extendAnimation: extendAnimation, backwards: backwards, completion: completion)

View File

@ -288,8 +288,6 @@ public class InvisibleInkDustNode: ASDisplayNode {
} }
self.staticParams = (size, color, lineRects) self.staticParams = (size, color, lineRects)
let start = CACurrentMediaTime()
var combinedRect: CGRect? var combinedRect: CGRect?
var combinedRects: [CGRect] = [] var combinedRects: [CGRect] = []
for rect in lineRects { for rect in lineRects {
@ -308,7 +306,6 @@ public class InvisibleInkDustNode: ASDisplayNode {
combinedRects.append(combinedRect.insetBy(dx: 0.0, dy: -1.0)) combinedRects.append(combinedRect.insetBy(dx: 0.0, dy: -1.0))
} }
print("combining \(CACurrentMediaTime() - start)")
Queue.concurrentDefaultQueue().async { Queue.concurrentDefaultQueue().async {
var generator = ArbitraryRandomNumberGenerator(seed: 1) var generator = ArbitraryRandomNumberGenerator(seed: 1)
let image = generateImage(size, rotatedContext: { size, context in let image = generateImage(size, rotatedContext: { size, context in
@ -331,8 +328,6 @@ public class InvisibleInkDustNode: ASDisplayNode {
} }
} }
self.staticNode?.frame = CGRect(origin: CGPoint(), size: size) self.staticNode?.frame = CGRect(origin: CGPoint(), size: size)
print("total draw \(CACurrentMediaTime() - start)")
} }
} }

View File

@ -523,7 +523,7 @@ final class MediaPickerSelectedListNode: ASDisplayNode, UIScrollViewDelegate, UI
self.context = context self.context = context
self.persistentItems = persistentItems self.persistentItems = persistentItems
self.wallpaperBackgroundNode = createWallpaperBackgroundNode(context: context, forChatDisplay: true, useSharedAnimationPhase: false, useExperimentalImplementation: context.sharedContext.immediateExperimentalUISettings.experimentalBackground) self.wallpaperBackgroundNode = createWallpaperBackgroundNode(context: context, forChatDisplay: true, useSharedAnimationPhase: false)
self.wallpaperBackgroundNode.backgroundColor = .black self.wallpaperBackgroundNode.backgroundColor = .black
self.scrollNode = ASScrollNode() self.scrollNode = ASScrollNode()

View File

@ -1732,9 +1732,14 @@ public final class MediaBox {
return return
} }
var lastReportValue = 0
let reportProgress: (Int) -> Void = { count in let reportProgress: (Int) -> Void = { count in
Queue.mainQueue().async { let currentProgress = min(1.0, Float(count) / Float(totalCount))
subscriber.putNext(min(1.0, Float(count) / Float(totalCount))) let currentInteger = Int(currentProgress * 100.0)
if lastReportValue != currentInteger {
lastReportValue = currentInteger
subscriber.putNext(currentProgress)
} }
} }
@ -1781,6 +1786,7 @@ public final class MediaBox {
self.didRemoveResourcesPipe.putNext(Void()) self.didRemoveResourcesPipe.putNext(Void())
} }
subscriber.putNext(1.0)
subscriber.putCompletion() subscriber.putCompletion()
} }
return EmptyDisposable return EmptyDisposable

View File

@ -416,11 +416,19 @@ private final class PremiumGiftScreenContentComponent: CombinedComponent {
demoSubject = .translation demoSubject = .translation
} }
let buttonText: String
if let price = state?.price {
buttonText = strings.Premium_Gift_GiftSubscription(price).string
} else {
buttonText = strings.Common_OK
}
var dismissImpl: (() -> Void)? var dismissImpl: (() -> Void)?
let controller = PremiumLimitsListScreen(context: accountContext, subject: demoSubject, source: .gift(state?.price), order: state?.configuration.perks, buttonText: strings.Premium_Gift_GiftSubscription(state?.price ?? "").string, isPremium: false) let controller = PremiumLimitsListScreen(context: accountContext, subject: demoSubject, source: .gift(state?.price), order: state?.configuration.perks, buttonText: buttonText, isPremium: false)
controller.action = { controller.action = { [weak state] in
dismissImpl?() dismissImpl?()
buy() if let _ = state?.price {
buy()
}
} }
controller.disposed = { controller.disposed = {
// updateIsFocused(false) // updateIsFocused(false)

View File

@ -23,11 +23,10 @@ extension ReactionsMessageAttribute {
parsedRecentReactions = recentReactions.compactMap { recentReaction -> ReactionsMessageAttribute.RecentPeer? in parsedRecentReactions = recentReactions.compactMap { recentReaction -> ReactionsMessageAttribute.RecentPeer? in
switch recentReaction { switch recentReaction {
case let .messagePeerReaction(flags, peerId, date, reaction): case let .messagePeerReaction(flags, peerId, date, reaction):
let _ = date
let isLarge = (flags & (1 << 0)) != 0 let isLarge = (flags & (1 << 0)) != 0
let isUnseen = (flags & (1 << 1)) != 0 let isUnseen = (flags & (1 << 1)) != 0
if let reaction = MessageReaction.Reaction(apiReaction: reaction) { if let reaction = MessageReaction.Reaction(apiReaction: reaction) {
return ReactionsMessageAttribute.RecentPeer(value: reaction, isLarge: isLarge, isUnseen: isUnseen, peerId: peerId.peerId) return ReactionsMessageAttribute.RecentPeer(value: reaction, isLarge: isLarge, isUnseen: isUnseen, peerId: peerId.peerId, timestamp: date)
} else { } else {
return nil return nil
} }
@ -118,7 +117,7 @@ private func mergeReactions(reactions: [MessageReaction], recentPeers: [Reaction
if let index = recentPeers.firstIndex(where: { $0.value == pendingReaction.value && $0.peerId == accountPeerId }) { if let index = recentPeers.firstIndex(where: { $0.value == pendingReaction.value && $0.peerId == accountPeerId }) {
recentPeers.remove(at: index) recentPeers.remove(at: index)
} }
recentPeers.append(ReactionsMessageAttribute.RecentPeer(value: pendingReaction.value, isLarge: false, isUnseen: false, peerId: accountPeerId)) recentPeers.append(ReactionsMessageAttribute.RecentPeer(value: pendingReaction.value, isLarge: false, isUnseen: false, peerId: accountPeerId, timestamp: Int32(CFAbsoluteTimeGetCurrent() + kCFAbsoluteTimeIntervalSince1970)))
} }
for i in (0 ..< result.count).reversed() { for i in (0 ..< result.count).reversed() {
@ -186,11 +185,10 @@ extension ReactionsMessageAttribute {
parsedRecentReactions = recentReactions.compactMap { recentReaction -> ReactionsMessageAttribute.RecentPeer? in parsedRecentReactions = recentReactions.compactMap { recentReaction -> ReactionsMessageAttribute.RecentPeer? in
switch recentReaction { switch recentReaction {
case let .messagePeerReaction(flags, peerId, date, reaction): case let .messagePeerReaction(flags, peerId, date, reaction):
let _ = date
let isLarge = (flags & (1 << 0)) != 0 let isLarge = (flags & (1 << 0)) != 0
let isUnseen = (flags & (1 << 1)) != 0 let isUnseen = (flags & (1 << 1)) != 0
if let reaction = MessageReaction.Reaction(apiReaction: reaction) { if let reaction = MessageReaction.Reaction(apiReaction: reaction) {
return ReactionsMessageAttribute.RecentPeer(value: reaction, isLarge: isLarge, isUnseen: isUnseen, peerId: peerId.peerId) return ReactionsMessageAttribute.RecentPeer(value: reaction, isLarge: isLarge, isUnseen: isUnseen, peerId: peerId.peerId, timestamp: date)
} else { } else {
return nil return nil
} }

View File

@ -433,7 +433,9 @@ public struct NetworkInitializationArguments {
public let autolockDeadine: Signal<Int32?, NoError> public let autolockDeadine: Signal<Int32?, NoError>
public let encryptionProvider: EncryptionProvider public let encryptionProvider: EncryptionProvider
public let deviceModelName:String? public let deviceModelName:String?
public init(apiId: Int32, apiHash: String, languagesCategory: String, appVersion: String, voipMaxLayer: Int32, voipVersions: [CallSessionManagerImplementationVersion], appData: Signal<Data?, NoError>, autolockDeadine: Signal<Int32?, NoError>, encryptionProvider: EncryptionProvider, deviceModelName:String?) { public let useBetaFeatures: Bool
public init(apiId: Int32, apiHash: String, languagesCategory: String, appVersion: String, voipMaxLayer: Int32, voipVersions: [CallSessionManagerImplementationVersion], appData: Signal<Data?, NoError>, autolockDeadine: Signal<Int32?, NoError>, encryptionProvider: EncryptionProvider, deviceModelName: String?, useBetaFeatures: Bool) {
self.apiId = apiId self.apiId = apiId
self.apiHash = apiHash self.apiHash = apiHash
self.languagesCategory = languagesCategory self.languagesCategory = languagesCategory
@ -444,6 +446,7 @@ public struct NetworkInitializationArguments {
self.autolockDeadine = autolockDeadine self.autolockDeadine = autolockDeadine
self.encryptionProvider = encryptionProvider self.encryptionProvider = encryptionProvider
self.deviceModelName = deviceModelName self.deviceModelName = deviceModelName
self.useBetaFeatures = useBetaFeatures
} }
} }
#if os(iOS) #if os(iOS)
@ -494,10 +497,21 @@ func initializedNetwork(accountId: AccountRecordId, arguments: NetworkInitializa
let context = MTContext(serialization: serialization, encryptionProvider: arguments.encryptionProvider, apiEnvironment: apiEnvironment, isTestingEnvironment: testingEnvironment, useTempAuthKeys: useTempAuthKeys) let context = MTContext(serialization: serialization, encryptionProvider: arguments.encryptionProvider, apiEnvironment: apiEnvironment, isTestingEnvironment: testingEnvironment, useTempAuthKeys: useTempAuthKeys)
if let networkSettings = networkSettings, networkSettings.useNetworkFramework { if let networkSettings = networkSettings {
if #available(iOS 12.0, macOS 10.14, *) { let useNetworkFramework: Bool
context.makeTcpConnectionInterface = { delegate, delegateQueue in if let customValue = networkSettings.useNetworkFramework {
return NetworkFrameworkTcpConnectionInterface(delegate: delegate, delegateQueue: delegateQueue) useNetworkFramework = customValue
} else if arguments.useBetaFeatures {
useNetworkFramework = true
} else {
useNetworkFramework = false
}
if useNetworkFramework {
if #available(iOS 12.0, macOS 10.14, *) {
context.makeTcpConnectionInterface = { delegate, delegateQueue in
return NetworkFrameworkTcpConnectionInterface(delegate: delegate, delegateQueue: delegateQueue)
}
} }
} }
} }
@ -589,7 +603,7 @@ func initializedNetwork(accountId: AccountRecordId, arguments: NetworkInitializa
mtProto.delegate = connectionStatusDelegate mtProto.delegate = connectionStatusDelegate
mtProto.add(requestService) mtProto.add(requestService)
let network = Network(queue: queue, datacenterId: datacenterId, context: context, mtProto: mtProto, requestService: requestService, connectionStatusDelegate: connectionStatusDelegate, _connectionStatus: connectionStatus, basePath: basePath, appDataDisposable: appDataDisposable, encryptionProvider: arguments.encryptionProvider, useRequestTimeoutTimers: useRequestTimeoutTimers) let network = Network(queue: queue, datacenterId: datacenterId, context: context, mtProto: mtProto, requestService: requestService, connectionStatusDelegate: connectionStatusDelegate, _connectionStatus: connectionStatus, basePath: basePath, appDataDisposable: appDataDisposable, encryptionProvider: arguments.encryptionProvider, useRequestTimeoutTimers: useRequestTimeoutTimers, useBetaFeatures: arguments.useBetaFeatures)
appDataUpdatedImpl = { [weak network] data in appDataUpdatedImpl = { [weak network] data in
guard let data = data else { guard let data = data else {
return return
@ -720,6 +734,7 @@ public final class Network: NSObject, MTRequestMessageServiceDelegate {
let basePath: String let basePath: String
private let connectionStatusDelegate: MTProtoConnectionStatusDelegate private let connectionStatusDelegate: MTProtoConnectionStatusDelegate
private let useRequestTimeoutTimers: Bool private let useRequestTimeoutTimers: Bool
public let useBetaFeatures: Bool
private let appDataDisposable: Disposable private let appDataDisposable: Disposable
@ -763,7 +778,7 @@ public final class Network: NSObject, MTRequestMessageServiceDelegate {
return "Network context: \(self.context)" return "Network context: \(self.context)"
} }
fileprivate init(queue: Queue, datacenterId: Int, context: MTContext, mtProto: MTProto, requestService: MTRequestMessageService, connectionStatusDelegate: MTProtoConnectionStatusDelegate, _connectionStatus: Promise<ConnectionStatus>, basePath: String, appDataDisposable: Disposable, encryptionProvider: EncryptionProvider, useRequestTimeoutTimers: Bool) { fileprivate init(queue: Queue, datacenterId: Int, context: MTContext, mtProto: MTProto, requestService: MTRequestMessageService, connectionStatusDelegate: MTProtoConnectionStatusDelegate, _connectionStatus: Promise<ConnectionStatus>, basePath: String, appDataDisposable: Disposable, encryptionProvider: EncryptionProvider, useRequestTimeoutTimers: Bool, useBetaFeatures: Bool) {
self.encryptionProvider = encryptionProvider self.encryptionProvider = encryptionProvider
self.queue = queue self.queue = queue
@ -777,6 +792,7 @@ public final class Network: NSObject, MTRequestMessageServiceDelegate {
self.appDataDisposable = appDataDisposable self.appDataDisposable = appDataDisposable
self.basePath = basePath self.basePath = basePath
self.useRequestTimeoutTimers = useRequestTimeoutTimers self.useRequestTimeoutTimers = useRequestTimeoutTimers
self.useBetaFeatures = useBetaFeatures
super.init() super.init()

View File

@ -26,7 +26,10 @@ func applyMediaResourceChanges(from: Media, to: Media, postbox: Postbox, force:
} }
} }
if let fromLargestRepresentation = largestImageRepresentation(fromImage.representations), let toLargestRepresentation = largestImageRepresentation(toImage.representations) { if let fromLargestRepresentation = largestImageRepresentation(fromImage.representations), let toLargestRepresentation = largestImageRepresentation(toImage.representations) {
copyOrMoveResourceData(from: fromLargestRepresentation.resource, to: toLargestRepresentation.resource, mediaBox: postbox.mediaBox) if fromLargestRepresentation.progressiveSizes != toLargestRepresentation.progressiveSizes {
} else {
copyOrMoveResourceData(from: fromLargestRepresentation.resource, to: toLargestRepresentation.resource, mediaBox: postbox.mediaBox)
}
} }
} else if let fromFile = from as? TelegramMediaFile, let toFile = to as? TelegramMediaFile { } else if let fromFile = from as? TelegramMediaFile, let toFile = to as? TelegramMediaFile {
if let fromPreview = smallestImageRepresentation(fromFile.previewRepresentations), let toPreview = smallestImageRepresentation(toFile.previewRepresentations) { if let fromPreview = smallestImageRepresentation(fromFile.previewRepresentations), let toPreview = smallestImageRepresentation(toFile.previewRepresentations) {

View File

@ -338,7 +338,7 @@ public extension EngineMessageReactionListContext.State {
for recentPeer in reactionsAttribute.recentPeers { for recentPeer in reactionsAttribute.recentPeers {
if let peer = message.peers[recentPeer.peerId] { if let peer = message.peers[recentPeer.peerId] {
if reaction == nil || recentPeer.value == reaction { if reaction == nil || recentPeer.value == reaction {
items.append(EngineMessageReactionListContext.Item(peer: EnginePeer(peer), reaction: recentPeer.value, timestamp: readStats?.readTimestamps[peer.id])) items.append(EngineMessageReactionListContext.Item(peer: EnginePeer(peer), reaction: recentPeer.value, timestamp: recentPeer.timestamp ?? readStats?.readTimestamps[peer.id]))
} }
} }
} }

View File

@ -4,13 +4,13 @@ public struct NetworkSettings: Codable {
public var reducedBackupDiscoveryTimeout: Bool public var reducedBackupDiscoveryTimeout: Bool
public var applicationUpdateUrlPrefix: String? public var applicationUpdateUrlPrefix: String?
public var backupHostOverride: String? public var backupHostOverride: String?
public var useNetworkFramework: Bool public var useNetworkFramework: Bool?
public static var defaultSettings: NetworkSettings { public static var defaultSettings: NetworkSettings {
return NetworkSettings(reducedBackupDiscoveryTimeout: false, applicationUpdateUrlPrefix: nil, backupHostOverride: nil, useNetworkFramework: false) return NetworkSettings(reducedBackupDiscoveryTimeout: false, applicationUpdateUrlPrefix: nil, backupHostOverride: nil, useNetworkFramework: nil)
} }
public init(reducedBackupDiscoveryTimeout: Bool, applicationUpdateUrlPrefix: String?, backupHostOverride: String?, useNetworkFramework: Bool) { public init(reducedBackupDiscoveryTimeout: Bool, applicationUpdateUrlPrefix: String?, backupHostOverride: String?, useNetworkFramework: Bool?) {
self.reducedBackupDiscoveryTimeout = reducedBackupDiscoveryTimeout self.reducedBackupDiscoveryTimeout = reducedBackupDiscoveryTimeout
self.applicationUpdateUrlPrefix = applicationUpdateUrlPrefix self.applicationUpdateUrlPrefix = applicationUpdateUrlPrefix
self.backupHostOverride = backupHostOverride self.backupHostOverride = backupHostOverride
@ -23,7 +23,7 @@ public struct NetworkSettings: Codable {
self.reducedBackupDiscoveryTimeout = ((try? container.decode(Int32.self, forKey: "reducedBackupDiscoveryTimeout")) ?? 0) != 0 self.reducedBackupDiscoveryTimeout = ((try? container.decode(Int32.self, forKey: "reducedBackupDiscoveryTimeout")) ?? 0) != 0
self.applicationUpdateUrlPrefix = try? container.decodeIfPresent(String.self, forKey: "applicationUpdateUrlPrefix") self.applicationUpdateUrlPrefix = try? container.decodeIfPresent(String.self, forKey: "applicationUpdateUrlPrefix")
self.backupHostOverride = try? container.decodeIfPresent(String.self, forKey: "backupHostOverride") self.backupHostOverride = try? container.decodeIfPresent(String.self, forKey: "backupHostOverride")
self.useNetworkFramework = try container.decodeIfPresent(Bool.self, forKey: "useNetworkFramework") ?? NetworkSettings.defaultSettings.useNetworkFramework self.useNetworkFramework = try container.decodeIfPresent(Bool.self, forKey: "useNetworkFramework_v2")
} }
public func encode(to encoder: Encoder) throws { public func encode(to encoder: Encoder) throws {
@ -32,6 +32,6 @@ public struct NetworkSettings: Codable {
try container.encode((self.reducedBackupDiscoveryTimeout ? 1 : 0) as Int32, forKey: "reducedBackupDiscoveryTimeout") try container.encode((self.reducedBackupDiscoveryTimeout ? 1 : 0) as Int32, forKey: "reducedBackupDiscoveryTimeout")
try container.encodeIfPresent(self.applicationUpdateUrlPrefix, forKey: "applicationUpdateUrlPrefix") try container.encodeIfPresent(self.applicationUpdateUrlPrefix, forKey: "applicationUpdateUrlPrefix")
try container.encodeIfPresent(self.backupHostOverride, forKey: "backupHostOverride") try container.encodeIfPresent(self.backupHostOverride, forKey: "backupHostOverride")
try container.encode(self.useNetworkFramework, forKey: "useNetworkFramework") try container.encodeIfPresent(self.useNetworkFramework, forKey: "useNetworkFramework_v2")
} }
} }

View File

@ -119,12 +119,14 @@ public final class ReactionsMessageAttribute: Equatable, MessageAttribute {
public var isLarge: Bool public var isLarge: Bool
public var isUnseen: Bool public var isUnseen: Bool
public var peerId: PeerId public var peerId: PeerId
public var timestamp: Int32?
public init(value: MessageReaction.Reaction, isLarge: Bool, isUnseen: Bool, peerId: PeerId) { public init(value: MessageReaction.Reaction, isLarge: Bool, isUnseen: Bool, peerId: PeerId, timestamp: Int32?) {
self.value = value self.value = value
self.isLarge = isLarge self.isLarge = isLarge
self.isUnseen = isUnseen self.isUnseen = isUnseen
self.peerId = peerId self.peerId = peerId
self.timestamp = timestamp
} }
public init(decoder: PostboxDecoder) { public init(decoder: PostboxDecoder) {
@ -136,6 +138,7 @@ public final class ReactionsMessageAttribute: Equatable, MessageAttribute {
self.isLarge = decoder.decodeInt32ForKey("l", orElse: 0) != 0 self.isLarge = decoder.decodeInt32ForKey("l", orElse: 0) != 0
self.isUnseen = decoder.decodeInt32ForKey("u", orElse: 0) != 0 self.isUnseen = decoder.decodeInt32ForKey("u", orElse: 0) != 0
self.peerId = PeerId(decoder.decodeInt64ForKey("p", orElse: 0)) self.peerId = PeerId(decoder.decodeInt64ForKey("p", orElse: 0))
self.timestamp = decoder.decodeOptionalInt32ForKey("ts")
} }
public func encode(_ encoder: PostboxEncoder) { public func encode(_ encoder: PostboxEncoder) {
@ -148,6 +151,11 @@ public final class ReactionsMessageAttribute: Equatable, MessageAttribute {
encoder.encodeInt32(self.isLarge ? 1 : 0, forKey: "l") encoder.encodeInt32(self.isLarge ? 1 : 0, forKey: "l")
encoder.encodeInt32(self.isUnseen ? 1 : 0, forKey: "u") encoder.encodeInt32(self.isUnseen ? 1 : 0, forKey: "u")
encoder.encodeInt64(self.peerId.toInt64(), forKey: "p") encoder.encodeInt64(self.peerId.toInt64(), forKey: "p")
if let timestamp = self.timestamp {
encoder.encodeInt32(timestamp, forKey: "ts")
} else {
encoder.encodeNil(forKey: "ts")
}
} }
} }

View File

@ -379,7 +379,7 @@ func _internal_renderStorageUsageStatsMessages(account: Account, stats: StorageU
} }
} }
func _internal_clearStorage(account: Account, peerId: EnginePeer.Id?, categories: [StorageUsageStats.CategoryKey], includeMessages: [Message], excludeMessages: [Message]) -> Signal<Never, NoError> { func _internal_clearStorage(account: Account, peerId: EnginePeer.Id?, categories: [StorageUsageStats.CategoryKey], includeMessages: [Message], excludeMessages: [Message]) -> Signal<Float, NoError> {
let mediaBox = account.postbox.mediaBox let mediaBox = account.postbox.mediaBox
return Signal { subscriber in return Signal { subscriber in
var includeResourceIds = Set<MediaResourceId>() var includeResourceIds = Set<MediaResourceId>()
@ -435,7 +435,9 @@ func _internal_clearStorage(account: Account, peerId: EnginePeer.Id?, categories
resourceIds.append(MediaResourceId(value)) resourceIds.append(MediaResourceId(value))
} }
} }
let _ = mediaBox.removeCachedResources(resourceIds).start(completed: { let _ = mediaBox.removeCachedResources(resourceIds).start(next: { progress in
subscriber.putNext(progress)
}, completed: {
if peerId == nil && categories.contains(.misc) { if peerId == nil && categories.contains(.misc) {
let additionalPaths: [String] = [ let additionalPaths: [String] = [
"cache", "cache",
@ -469,7 +471,7 @@ func _internal_clearStorage(account: Account, peerId: EnginePeer.Id?, categories
} }
} }
func _internal_clearStorage(account: Account, peerIds: Set<EnginePeer.Id>, includeMessages: [Message], excludeMessages: [Message]) -> Signal<Never, NoError> { func _internal_clearStorage(account: Account, peerIds: Set<EnginePeer.Id>, includeMessages: [Message], excludeMessages: [Message]) -> Signal<Float, NoError> {
let mediaBox = account.postbox.mediaBox let mediaBox = account.postbox.mediaBox
return Signal { subscriber in return Signal { subscriber in
var includeResourceIds = Set<MediaResourceId>() var includeResourceIds = Set<MediaResourceId>()
@ -496,12 +498,15 @@ func _internal_clearStorage(account: Account, peerIds: Set<EnginePeer.Id>, inclu
mediaBox.storageBox.remove(peerIds: peerIds, includeIds: includeIds, excludeIds: excludeIds, completion: { ids in mediaBox.storageBox.remove(peerIds: peerIds, includeIds: includeIds, excludeIds: excludeIds, completion: { ids in
var resourceIds: [MediaResourceId] = [] var resourceIds: [MediaResourceId] = []
for id in ids { for id in ids {
if let value = String(data: id, encoding: .utf8) { if let value = String(data: id, encoding: .utf8) {
resourceIds.append(MediaResourceId(value)) resourceIds.append(MediaResourceId(value))
} }
} }
let _ = mediaBox.removeCachedResources(resourceIds).start(completed: { let _ = mediaBox.removeCachedResources(resourceIds).start(next: { progress in
subscriber.putNext(progress)
}, completed: {
subscriber.putCompletion() subscriber.putCompletion()
}) })
}) })

View File

@ -233,11 +233,11 @@ public extension TelegramEngine {
return _internal_renderStorageUsageStatsMessages(account: self.account, stats: stats, categories: categories, existingMessages: existingMessages) return _internal_renderStorageUsageStatsMessages(account: self.account, stats: stats, categories: categories, existingMessages: existingMessages)
} }
public func clearStorage(peerId: EnginePeer.Id?, categories: [StorageUsageStats.CategoryKey], includeMessages: [Message], excludeMessages: [Message]) -> Signal<Never, NoError> { public func clearStorage(peerId: EnginePeer.Id?, categories: [StorageUsageStats.CategoryKey], includeMessages: [Message], excludeMessages: [Message]) -> Signal<Float, NoError> {
return _internal_clearStorage(account: self.account, peerId: peerId, categories: categories, includeMessages: includeMessages, excludeMessages: excludeMessages) return _internal_clearStorage(account: self.account, peerId: peerId, categories: categories, includeMessages: includeMessages, excludeMessages: excludeMessages)
} }
public func clearStorage(peerIds: Set<EnginePeer.Id>, includeMessages: [Message], excludeMessages: [Message]) -> Signal<Never, NoError> { public func clearStorage(peerIds: Set<EnginePeer.Id>, includeMessages: [Message], excludeMessages: [Message]) -> Signal<Float, NoError> {
_internal_clearStorage(account: self.account, peerIds: peerIds, includeMessages: includeMessages, excludeMessages: excludeMessages) _internal_clearStorage(account: self.account, peerIds: peerIds, includeMessages: includeMessages, excludeMessages: excludeMessages)
} }

View File

@ -1838,7 +1838,7 @@ public final class ChatEntityKeyboardInputNode: ChatInputNode {
strongSelf.reorderItems(category: category, items: items) strongSelf.reorderItems(category: category, items: items)
}, },
makeSearchContainerNode: { [weak self, weak controllerInteraction] content in makeSearchContainerNode: { [weak self, weak controllerInteraction] content in
guard let controllerInteraction = controllerInteraction else { guard let self, let controllerInteraction = controllerInteraction else {
return nil return nil
} }
@ -1860,9 +1860,10 @@ public final class ChatEntityKeyboardInputNode: ChatInputNode {
mode: mappedMode, mode: mappedMode,
trendingGifsPromise: trendingGifsPromise, trendingGifsPromise: trendingGifsPromise,
cancel: { cancel: {
} },
peekBehavior: self.emojiInputInteraction?.peekBehavior
) )
searchContainerNode.openGifContextMenu = { item, sourceNode, sourceRect, gesture, isSaved in searchContainerNode.openGifContextMenu = { [weak self] item, sourceNode, sourceRect, gesture, isSaved in
guard let self else { guard let self else {
return return
} }
@ -2502,7 +2503,7 @@ public final class EmojiContentPeekBehaviorImpl: EmojiContentPeekBehavior {
self.present = present self.present = present
} }
public func setGestureRecognizerEnabled(view: UIView, isEnabled: Bool, itemAtPoint: @escaping (CGPoint) -> (AnyHashable, EmojiPagerContentComponent.View.ItemLayer, TelegramMediaFile)?) { public func setGestureRecognizerEnabled(view: UIView, isEnabled: Bool, itemAtPoint: @escaping (CGPoint) -> (AnyHashable, CALayer, TelegramMediaFile)?) {
self.viewRecords = self.viewRecords.filter({ $0.view != nil }) self.viewRecords = self.viewRecords.filter({ $0.view != nil })
let viewRecord = self.viewRecords.first(where: { $0.view === view }) let viewRecord = self.viewRecords.first(where: { $0.view === view })

View File

@ -12,6 +12,7 @@ import EntityKeyboard
import ChatControllerInteraction import ChatControllerInteraction
import MultiplexedVideoNode import MultiplexedVideoNode
import FeaturedStickersScreen import FeaturedStickersScreen
import StickerPeekUI
private let searchBarHeight: CGFloat = 52.0 private let searchBarHeight: CGFloat = 52.0
@ -37,6 +38,7 @@ public final class PaneSearchContainerNode: ASDisplayNode, EntitySearchContainer
public private(set) var contentNode: PaneSearchContentNode & ASDisplayNode public private(set) var contentNode: PaneSearchContentNode & ASDisplayNode
private let controllerInteraction: ChatControllerInteraction private let controllerInteraction: ChatControllerInteraction
private let inputNodeInteraction: ChatMediaInputNodeInteraction private let inputNodeInteraction: ChatMediaInputNodeInteraction
private let peekBehavior: EmojiContentPeekBehavior?
private let backgroundNode: ASDisplayNode private let backgroundNode: ASDisplayNode
private let searchBar: PaneSearchBarNode private let searchBar: PaneSearchBarNode
@ -51,11 +53,12 @@ public final class PaneSearchContainerNode: ASDisplayNode, EntitySearchContainer
return self.contentNode.ready return self.contentNode.ready
} }
public init(context: AccountContext, theme: PresentationTheme, strings: PresentationStrings, controllerInteraction: ChatControllerInteraction, inputNodeInteraction: ChatMediaInputNodeInteraction, mode: ChatMediaInputSearchMode, trendingGifsPromise: Promise<ChatMediaInputGifPaneTrendingState?>, cancel: @escaping () -> Void) { public init(context: AccountContext, theme: PresentationTheme, strings: PresentationStrings, controllerInteraction: ChatControllerInteraction, inputNodeInteraction: ChatMediaInputNodeInteraction, mode: ChatMediaInputSearchMode, trendingGifsPromise: Promise<ChatMediaInputGifPaneTrendingState?>, cancel: @escaping () -> Void, peekBehavior: EmojiContentPeekBehavior?) {
self.context = context self.context = context
self.mode = mode self.mode = mode
self.controllerInteraction = controllerInteraction self.controllerInteraction = controllerInteraction
self.inputNodeInteraction = inputNodeInteraction self.inputNodeInteraction = inputNodeInteraction
self.peekBehavior = peekBehavior
switch mode { switch mode {
case .gif: case .gif:
self.contentNode = GifPaneSearchContentNode(context: context, theme: theme, strings: strings, controllerInteraction: controllerInteraction, inputNodeInteraction: inputNodeInteraction, trendingPromise: trendingGifsPromise) self.contentNode = GifPaneSearchContentNode(context: context, theme: theme, strings: strings, controllerInteraction: controllerInteraction, inputNodeInteraction: inputNodeInteraction, trendingPromise: trendingGifsPromise)
@ -103,6 +106,41 @@ public final class PaneSearchContainerNode: ASDisplayNode, EntitySearchContainer
self?.openGifContextMenu?(file, node, rect, gesture, isSaved) self?.openGifContextMenu?(file, node, rect, gesture, isSaved)
} }
} }
if let contentNode = self.contentNode as? StickerPaneSearchContentNode, let peekBehavior = self.peekBehavior {
peekBehavior.setGestureRecognizerEnabled(view: self.contentNode.view, isEnabled: true, itemAtPoint: { [weak contentNode] point in
guard let contentNode else {
return nil
}
guard let (itemNode, item) = contentNode.itemAt(point: point) else {
return nil
}
var maybeFile: TelegramMediaFile?
if let item = item as? StickerPreviewPeekItem {
switch item {
case let .found(foundItem):
maybeFile = foundItem.file
case let .pack(fileValue):
maybeFile = fileValue
}
}
guard let file = maybeFile else {
return nil
}
var groupId: AnyHashable = AnyHashable("search")
for attribute in file.attributes {
if case let .Sticker(_, packReference, _) = attribute {
if case let .id(id, _) = packReference {
groupId = AnyHashable(ItemCollectionId(namespace: Namespaces.ItemCollection.CloudStickerPacks, id: id))
}
}
}
return (groupId, itemNode.layer, file)
})
}
} }
public func updateThemeAndStrings(theme: PresentationTheme, strings: PresentationStrings) { public func updateThemeAndStrings(theme: PresentationTheme, strings: PresentationStrings) {

View File

@ -32,14 +32,14 @@ public enum ChatTitleContent: Equatable {
case replies case replies
} }
case peer(peerView: PeerView, customTitle: String?, onlineMemberCount: Int32?, isScheduledMessages: Bool, isMuted: Bool?, customMessageCount: Int?) case peer(peerView: PeerView, customTitle: String?, onlineMemberCount: Int32?, isScheduledMessages: Bool, isMuted: Bool?, customMessageCount: Int?, isEnabled: Bool)
case replyThread(type: ReplyThreadType, count: Int) case replyThread(type: ReplyThreadType, count: Int)
case custom(String, String?, Bool) case custom(String, String?, Bool)
public static func ==(lhs: ChatTitleContent, rhs: ChatTitleContent) -> Bool { public static func ==(lhs: ChatTitleContent, rhs: ChatTitleContent) -> Bool {
switch lhs { switch lhs {
case let .peer(peerView, customTitle, onlineMemberCount, isScheduledMessages, isMuted, customMessageCount): case let .peer(peerView, customTitle, onlineMemberCount, isScheduledMessages, isMuted, customMessageCount, isEnabled):
if case let .peer(rhsPeerView, rhsCustomTitle, rhsOnlineMemberCount, rhsIsScheduledMessages, rhsIsMuted, rhsCustomMessageCount) = rhs { if case let .peer(rhsPeerView, rhsCustomTitle, rhsOnlineMemberCount, rhsIsScheduledMessages, rhsIsMuted, rhsCustomMessageCount, rhsIsEnabled) = rhs {
if peerView !== rhsPeerView { if peerView !== rhsPeerView {
return false return false
} }
@ -58,7 +58,9 @@ public enum ChatTitleContent: Equatable {
if customMessageCount != rhsCustomMessageCount { if customMessageCount != rhsCustomMessageCount {
return false return false
} }
if isEnabled != rhsIsEnabled {
return false
}
return true return true
} else { } else {
return false return false
@ -169,7 +171,7 @@ public final class ChatTitleView: UIView, NavigationBarTitleView {
var titleCredibilityIcon: ChatTitleCredibilityIcon = .none var titleCredibilityIcon: ChatTitleCredibilityIcon = .none
var isEnabled = true var isEnabled = true
switch titleContent { switch titleContent {
case let .peer(peerView, customTitle, _, isScheduledMessages, isMuted, _): case let .peer(peerView, customTitle, _, isScheduledMessages, isMuted, _, isEnabledValue):
if peerView.peerId.isReplies { if peerView.peerId.isReplies {
let typeText: String = self.strings.DialogList_Replies let typeText: String = self.strings.DialogList_Replies
segments = [.text(0, NSAttributedString(string: typeText, font: titleFont, textColor: titleTheme.rootController.navigationBar.primaryTextColor))] segments = [.text(0, NSAttributedString(string: typeText, font: titleFont, textColor: titleTheme.rootController.navigationBar.primaryTextColor))]
@ -225,6 +227,7 @@ public final class ChatTitleView: UIView, NavigationBarTitleView {
} }
} }
} }
isEnabled = isEnabledValue
} }
case let .replyThread(type, count): case let .replyThread(type, count):
let textFont = titleFont let textFont = titleFont
@ -365,7 +368,7 @@ public final class ChatTitleView: UIView, NavigationBarTitleView {
var inputActivitiesAllowed = true var inputActivitiesAllowed = true
if let titleContent = self.titleContent { if let titleContent = self.titleContent {
switch titleContent { switch titleContent {
case let .peer(peerView, _, _, isScheduledMessages, _, customMessageCount): case let .peer(peerView, _, _, isScheduledMessages, _, customMessageCount, _):
if let peer = peerViewMainPeer(peerView) { if let peer = peerViewMainPeer(peerView) {
if peer.id == self.context.account.peerId || isScheduledMessages || peer.id.isReplies { if peer.id == self.context.account.peerId || isScheduledMessages || peer.id.isReplies {
inputActivitiesAllowed = false inputActivitiesAllowed = false
@ -469,7 +472,7 @@ public final class ChatTitleView: UIView, NavigationBarTitleView {
} else { } else {
if let titleContent = self.titleContent { if let titleContent = self.titleContent {
switch titleContent { switch titleContent {
case let .peer(peerView, customTitle, onlineMemberCount, isScheduledMessages, _, customMessageCount): case let .peer(peerView, customTitle, onlineMemberCount, isScheduledMessages, _, customMessageCount, _):
if let customMessageCount = customMessageCount, customMessageCount != 0 { if let customMessageCount = customMessageCount, customMessageCount != 0 {
let string = NSAttributedString(string: self.strings.Conversation_Messages(Int32(customMessageCount)), font: subtitleFont, textColor: titleTheme.rootController.navigationBar.secondaryTextColor) let string = NSAttributedString(string: self.strings.Conversation_Messages(Int32(customMessageCount)), font: subtitleFont, textColor: titleTheme.rootController.navigationBar.secondaryTextColor)
state = .info(string, .generic) state = .info(string, .generic)

View File

@ -2222,7 +2222,7 @@ private final class EmptySearchResultsView: UIView {
} }
public protocol EmojiContentPeekBehavior: AnyObject { public protocol EmojiContentPeekBehavior: AnyObject {
func setGestureRecognizerEnabled(view: UIView, isEnabled: Bool, itemAtPoint: @escaping (CGPoint) -> (AnyHashable, EmojiPagerContentComponent.View.ItemLayer, TelegramMediaFile)?) func setGestureRecognizerEnabled(view: UIView, isEnabled: Bool, itemAtPoint: @escaping (CGPoint) -> (AnyHashable, CALayer, TelegramMediaFile)?)
} }
public final class EmojiPagerContentComponent: Component { public final class EmojiPagerContentComponent: Component {

View File

@ -2880,7 +2880,12 @@ final class StorageUsageScreenComponent: Component {
let totalSize = aggregatedData.selectedSize let totalSize = aggregatedData.selectedSize
let _ = (component.context.engine.resources.clearStorage(peerId: component.peer?.id, categories: mappedCategories, includeMessages: aggregatedData.clearIncludeMessages, excludeMessages: aggregatedData.clearExcludeMessages) let _ = (component.context.engine.resources.clearStorage(peerId: component.peer?.id, categories: mappedCategories, includeMessages: aggregatedData.clearIncludeMessages, excludeMessages: aggregatedData.clearExcludeMessages)
|> deliverOnMainQueue).start(completed: { [weak self] in |> deliverOnMainQueue).start(next: { [weak self] progress in
guard let self else {
return
}
self.updateClearProgress(progress: progress)
}, completed: { [weak self] in
guard let self, let _ = self.component else { guard let self, let _ = self.component else {
return return
} }
@ -2921,39 +2926,45 @@ final class StorageUsageScreenComponent: Component {
self.isClearing = true self.isClearing = true
self.state?.updated(transition: .immediate) self.state?.updated(transition: .immediate)
var totalSize: Int64 = 0
let contextStats = aggregatedData.contextStats
for category in aggregatedData.selectedCategories {
let mappedCategory: StorageUsageStats.CategoryKey
switch category {
case .photos:
mappedCategory = .photos
case .videos:
mappedCategory = .videos
case .files:
mappedCategory = .files
case .music:
mappedCategory = .music
case .other:
continue
case .stickers:
mappedCategory = .stickers
case .avatars:
mappedCategory = .avatars
case .misc:
mappedCategory = .misc
}
if let value = contextStats.categories[mappedCategory] {
totalSize += value.size
}
}
let _ = (component.context.engine.resources.clearStorage(peerId: component.peer?.id, categories: mappedCategories, includeMessages: [], excludeMessages: []) let _ = (component.context.engine.resources.clearStorage(peerId: component.peer?.id, categories: mappedCategories, includeMessages: [], excludeMessages: [])
|> deliverOnMainQueue).start(completed: { [weak self] in |> deliverOnMainQueue).start(next: { [weak self] progress in
guard let self, let _ = self.component, let aggregatedData = self.aggregatedData else { guard let self else {
return return
} }
var totalSize: Int64 = 0 self.updateClearProgress(progress: progress)
}, completed: { [weak self] in
let contextStats = aggregatedData.contextStats guard let self else {
return
for category in aggregatedData.selectedCategories {
let mappedCategory: StorageUsageStats.CategoryKey
switch category {
case .photos:
mappedCategory = .photos
case .videos:
mappedCategory = .videos
case .files:
mappedCategory = .files
case .music:
mappedCategory = .music
case .other:
continue
case .stickers:
mappedCategory = .stickers
case .avatars:
mappedCategory = .avatars
case .misc:
mappedCategory = .misc
}
if let value = contextStats.categories[mappedCategory] {
totalSize += value.size
}
} }
self.reloadStats(firstTime: false, completion: { [weak self] in self.reloadStats(firstTime: false, completion: { [weak self] in
@ -2994,7 +3005,12 @@ final class StorageUsageScreenComponent: Component {
} }
let _ = (component.context.engine.resources.clearStorage(peerIds: aggregatedData.selectionState.selectedPeers, includeMessages: includeMessages, excludeMessages: excludeMessages) let _ = (component.context.engine.resources.clearStorage(peerIds: aggregatedData.selectionState.selectedPeers, includeMessages: includeMessages, excludeMessages: excludeMessages)
|> deliverOnMainQueue).start(completed: { [weak self] in |> deliverOnMainQueue).start(next: { [weak self] progress in
guard let self else {
return
}
self.updateClearProgress(progress: progress)
}, completed: { [weak self] in
guard let self else { guard let self else {
return return
} }
@ -3012,6 +3028,12 @@ final class StorageUsageScreenComponent: Component {
} }
} }
private func updateClearProgress(progress: Float) {
if let clearingNode = self.clearingNode {
clearingNode.setProgress(progress)
}
}
private func openKeepMediaCategory(mappedCategory: CacheStorageSettings.PeerStorageCategory, sourceView: StoragePeerTypeItemComponent.View) { private func openKeepMediaCategory(mappedCategory: CacheStorageSettings.PeerStorageCategory, sourceView: StoragePeerTypeItemComponent.View) {
guard let component = self.component else { guard let component = self.component else {
return return
@ -3507,8 +3529,8 @@ private class StorageUsageClearProgressOverlayNode: ASDisplayNode {
self.addSubnode(self.animationNode) self.addSubnode(self.animationNode)
self.addSubnode(self.progressTextNode) self.addSubnode(self.progressTextNode)
self.addSubnode(self.descriptionTextNode) self.addSubnode(self.descriptionTextNode)
//self.addSubnode(self.progressBackgroundNode) self.addSubnode(self.progressBackgroundNode)
//self.addSubnode(self.progressForegroundNode) self.addSubnode(self.progressForegroundNode)
} }
deinit { deinit {
@ -3525,7 +3547,7 @@ private class StorageUsageClearProgressOverlayNode: ASDisplayNode {
} }
private var progress: Float = 0.0 private var progress: Float = 0.0
private func setProgress(_ progress: Float) { func setProgress(_ progress: Float) {
self.progress = progress self.progress = progress
if let size = self.validLayout { if let size = self.validLayout {
@ -3563,7 +3585,9 @@ private class StorageUsageClearProgressOverlayNode: ASDisplayNode {
let descriptionTextSize = self.descriptionTextNode.updateLayout(CGSize(width: size.width - inset * 3.0, height: size.height)) let descriptionTextSize = self.descriptionTextNode.updateLayout(CGSize(width: size.width - inset * 3.0, height: size.height))
var descriptionTextFrame = CGRect(origin: CGPoint(x: floorToScreenPixels((size.width - descriptionTextSize.width) / 2.0), y: animationFrame.maxY + 52.0), size: descriptionTextSize) var descriptionTextFrame = CGRect(origin: CGPoint(x: floorToScreenPixels((size.width - descriptionTextSize.width) / 2.0), y: animationFrame.maxY + 52.0), size: descriptionTextSize)
self.progressTextNode.attributedText = NSAttributedString(string: self.presentationData.strings.ClearCache_NoProgress, font: Font.with(size: 17.0, design: .regular, weight: .semibold, traits: [.monospacedNumbers]), textColor: self.presentationData.theme.actionSheet.primaryTextColor) let progressText: String = "\(Int(self.progress * 100.0))%"
self.progressTextNode.attributedText = NSAttributedString(string: progressText, font: Font.with(size: 17.0, design: .regular, weight: .semibold, traits: [.monospacedNumbers]), textColor: self.presentationData.theme.actionSheet.primaryTextColor)
let progressTextSize = self.progressTextNode.updateLayout(size) let progressTextSize = self.progressTextNode.updateLayout(size)
var progressTextFrame = CGRect(origin: CGPoint(x: floorToScreenPixels((size.width - progressTextSize.width) / 2.0), y: descriptionTextFrame.minY - spacing - progressTextSize.height), size: progressTextSize) var progressTextFrame = CGRect(origin: CGPoint(x: floorToScreenPixels((size.width - progressTextSize.width) / 2.0), y: descriptionTextFrame.minY - spacing - progressTextSize.height), size: progressTextSize)

View File

@ -483,7 +483,7 @@ private func extractAccountManagerState(records: AccountRecordsView<TelegramAcco
Logger.shared.log("data", "can't deserialize") Logger.shared.log("data", "can't deserialize")
} }
return data return data
}, autolockDeadine: autolockDeadine, encryptionProvider: OpenSSLEncryptionProvider(), deviceModelName: nil) }, autolockDeadine: autolockDeadine, encryptionProvider: OpenSSLEncryptionProvider(), deviceModelName: nil, useBetaFeatures: !buildConfig.isAppStoreBuild)
guard let appGroupUrl = maybeAppGroupUrl else { guard let appGroupUrl = maybeAppGroupUrl else {
self.mainWindow?.presentNative(UIAlertController(title: nil, message: "Error 2", preferredStyle: .alert)) self.mainWindow?.presentNative(UIAlertController(title: nil, message: "Error 2", preferredStyle: .alert))

View File

@ -573,7 +573,7 @@ public final class ChatControllerImpl: TelegramBaseController, ChatController, G
default: default:
break break
} }
self.chatBackgroundNode = createWallpaperBackgroundNode(context: context, forChatDisplay: true, useSharedAnimationPhase: useSharedAnimationPhase, useExperimentalImplementation: self.context.sharedContext.immediateExperimentalUISettings.experimentalBackground) self.chatBackgroundNode = createWallpaperBackgroundNode(context: context, forChatDisplay: true, useSharedAnimationPhase: useSharedAnimationPhase)
self.wallpaperReady.set(self.chatBackgroundNode.isReady) self.wallpaperReady.set(self.chatBackgroundNode.isReady)
var locationBroadcastPanelSource: LocationBroadcastPanelSource var locationBroadcastPanelSource: LocationBroadcastPanelSource
@ -4670,9 +4670,18 @@ public final class ChatControllerImpl: TelegramBaseController, ChatController, G
} }
} }
let hasPeerInfo: Signal<Bool, NoError>
if peerId == context.account.peerId {
hasPeerInfo = .single(true)
|> then(
hasAvailablePeerInfoMediaPanes(context: context, peerId: peerId)
)
} else {
hasPeerInfo = .single(true)
}
self.titleDisposable.set((combineLatest(queue: Queue.mainQueue(), peerView.get(), onlineMemberCount, displayedCountSignal, subtitleTextSignal, self.presentationInterfaceStatePromise.get()) self.titleDisposable.set((combineLatest(queue: Queue.mainQueue(), peerView.get(), onlineMemberCount, displayedCountSignal, subtitleTextSignal, self.presentationInterfaceStatePromise.get(), hasPeerInfo)
|> deliverOnMainQueue).start(next: { [weak self] peerView, onlineMemberCount, displayedCount, subtitleText, presentationInterfaceState in |> deliverOnMainQueue).start(next: { [weak self] peerView, onlineMemberCount, displayedCount, subtitleText, presentationInterfaceState, hasPeerInfo in
if let strongSelf = self { if let strongSelf = self {
var isScheduledMessages = false var isScheduledMessages = false
if case .scheduledMessages = presentationInterfaceState.subject { if case .scheduledMessages = presentationInterfaceState.subject {
@ -4723,7 +4732,7 @@ public final class ChatControllerImpl: TelegramBaseController, ChatController, G
if case .pinnedMessages = presentationInterfaceState.subject { if case .pinnedMessages = presentationInterfaceState.subject {
strongSelf.chatTitleView?.titleContent = .custom(presentationInterfaceState.strings.Chat_TitlePinnedMessages(Int32(displayedCount ?? 1)), nil, false) strongSelf.chatTitleView?.titleContent = .custom(presentationInterfaceState.strings.Chat_TitlePinnedMessages(Int32(displayedCount ?? 1)), nil, false)
} else { } else {
strongSelf.chatTitleView?.titleContent = .peer(peerView: peerView, customTitle: nil, onlineMemberCount: onlineMemberCount, isScheduledMessages: isScheduledMessages, isMuted: nil, customMessageCount: nil) strongSelf.chatTitleView?.titleContent = .peer(peerView: peerView, customTitle: nil, onlineMemberCount: onlineMemberCount, isScheduledMessages: isScheduledMessages, isMuted: nil, customMessageCount: nil, isEnabled: hasPeerInfo)
let imageOverride: AvatarNodeImageOverride? let imageOverride: AvatarNodeImageOverride?
if strongSelf.context.account.peerId == peer.id { if strongSelf.context.account.peerId == peer.id {
imageOverride = .savedMessagesIcon imageOverride = .savedMessagesIcon
@ -5278,7 +5287,7 @@ public final class ChatControllerImpl: TelegramBaseController, ChatController, G
} }
if let threadInfo = messageAndTopic.threadData?.info { if let threadInfo = messageAndTopic.threadData?.info {
strongSelf.chatTitleView?.titleContent = .peer(peerView: peerView, customTitle: threadInfo.title, onlineMemberCount: onlineMemberCount, isScheduledMessages: false, isMuted: peerIsMuted, customMessageCount: messageAndTopic.messageCount == 0 ? nil : messageAndTopic.messageCount) strongSelf.chatTitleView?.titleContent = .peer(peerView: peerView, customTitle: threadInfo.title, onlineMemberCount: onlineMemberCount, isScheduledMessages: false, isMuted: peerIsMuted, customMessageCount: messageAndTopic.messageCount == 0 ? nil : messageAndTopic.messageCount, isEnabled: true)
let avatarContent: EmojiStatusComponent.Content let avatarContent: EmojiStatusComponent.Content
if strongSelf.chatLocation.threadId == 1 { if strongSelf.chatLocation.threadId == 1 {

View File

@ -801,7 +801,7 @@ class ChatControllerNode: ASDisplayNode, UIScrollViewDelegate {
self.emptyNode = emptyNode self.emptyNode = emptyNode
self.historyNodeContainer.supernode?.insertSubnode(emptyNode, aboveSubnode: self.historyNodeContainer) self.historyNodeContainer.supernode?.insertSubnode(emptyNode, aboveSubnode: self.historyNodeContainer)
if let (size, insets) = self.validEmptyNodeLayout { if let (size, insets) = self.validEmptyNodeLayout {
emptyNode.updateLayout(interfaceState: self.chatPresentationInterfaceState, emptyType: emptyType, loadingNode: wasLoading && self.loadingNode.supernode != nil ? self.loadingNode : nil, backgroundNode: self.backgroundNode, size: size, insets: insets, transition: .immediate) emptyNode.updateLayout(interfaceState: self.chatPresentationInterfaceState, subject: .emptyChat(emptyType), loadingNode: wasLoading && self.loadingNode.supernode != nil ? self.loadingNode : nil, backgroundNode: self.backgroundNode, size: size, insets: insets, transition: .immediate)
} }
if animated { if animated {
emptyNode.layer.animateAlpha(from: 0.0, to: 1.0, duration: 0.2) emptyNode.layer.animateAlpha(from: 0.0, to: 1.0, duration: 0.2)
@ -1622,7 +1622,7 @@ class ChatControllerNode: ASDisplayNode, UIScrollViewDelegate {
emptyNodeInsets.bottom += inputPanelsHeight emptyNodeInsets.bottom += inputPanelsHeight
self.validEmptyNodeLayout = (contentBounds.size, emptyNodeInsets) self.validEmptyNodeLayout = (contentBounds.size, emptyNodeInsets)
if let emptyNode = self.emptyNode, let emptyType = self.emptyType { if let emptyNode = self.emptyNode, let emptyType = self.emptyType {
emptyNode.updateLayout(interfaceState: self.chatPresentationInterfaceState, emptyType: emptyType, loadingNode: nil, backgroundNode: self.backgroundNode, size: contentBounds.size, insets: emptyNodeInsets, transition: transition) emptyNode.updateLayout(interfaceState: self.chatPresentationInterfaceState, subject: .emptyChat(emptyType), loadingNode: nil, backgroundNode: self.backgroundNode, size: contentBounds.size, insets: emptyNodeInsets, transition: transition)
transition.updateFrame(node: emptyNode, frame: contentBounds) transition.updateFrame(node: emptyNode, frame: contentBounds)
emptyNode.update(rect: contentBounds, within: contentBounds.size, transition: transition) emptyNode.update(rect: contentBounds, within: contentBounds.size, transition: transition)
} }

View File

@ -16,7 +16,7 @@ import ComponentFlow
import EmojiStatusComponent import EmojiStatusComponent
private protocol ChatEmptyNodeContent { private protocol ChatEmptyNodeContent {
func updateLayout(interfaceState: ChatPresentationInterfaceState, size: CGSize, transition: ContainedViewLayoutTransition) -> CGSize func updateLayout(interfaceState: ChatPresentationInterfaceState, subject: ChatEmptyNode.Subject, size: CGSize, transition: ContainedViewLayoutTransition) -> CGSize
} }
private let titleFont = Font.medium(15.0) private let titleFont = Font.medium(15.0)
@ -36,7 +36,7 @@ private final class ChatEmptyNodeRegularChatContent: ASDisplayNode, ChatEmptyNod
self.addSubnode(self.textNode) self.addSubnode(self.textNode)
} }
func updateLayout(interfaceState: ChatPresentationInterfaceState, size: CGSize, transition: ContainedViewLayoutTransition) -> CGSize { func updateLayout(interfaceState: ChatPresentationInterfaceState, subject: ChatEmptyNode.Subject, size: CGSize, transition: ContainedViewLayoutTransition) -> CGSize {
if self.currentTheme !== interfaceState.theme || self.currentStrings !== interfaceState.strings { if self.currentTheme !== interfaceState.theme || self.currentStrings !== interfaceState.strings {
self.currentTheme = interfaceState.theme self.currentTheme = interfaceState.theme
self.currentStrings = interfaceState.strings self.currentStrings = interfaceState.strings
@ -44,12 +44,16 @@ private final class ChatEmptyNodeRegularChatContent: ASDisplayNode, ChatEmptyNod
let serviceColor = serviceMessageColorComponents(theme: interfaceState.theme, wallpaper: interfaceState.chatWallpaper) let serviceColor = serviceMessageColorComponents(theme: interfaceState.theme, wallpaper: interfaceState.chatWallpaper)
let text: String let text: String
switch interfaceState.chatLocation { if case .detailsPlaceholder = subject {
case .peer, .replyThread, .feed: text = interfaceState.strings.ChatList_StartMessaging
if case .scheduledMessages = interfaceState.subject { } else {
text = interfaceState.strings.ScheduledMessages_EmptyPlaceholder switch interfaceState.chatLocation {
} else { case .peer, .replyThread, .feed:
text = interfaceState.strings.Conversation_EmptyPlaceholder if case .scheduledMessages = interfaceState.subject {
text = interfaceState.strings.ScheduledMessages_EmptyPlaceholder
} else {
text = interfaceState.strings.Conversation_EmptyPlaceholder
}
} }
} }
@ -140,7 +144,7 @@ final class ChatEmptyNodeGreetingChatContent: ASDisplayNode, ChatEmptyNodeSticke
let _ = self.interaction?.sendSticker(.standalone(media: stickerItem.stickerItem.file), false, self.view, self.stickerNode.bounds, nil, []) let _ = self.interaction?.sendSticker(.standalone(media: stickerItem.stickerItem.file), false, self.view, self.stickerNode.bounds, nil, [])
} }
func updateLayout(interfaceState: ChatPresentationInterfaceState, size: CGSize, transition: ContainedViewLayoutTransition) -> CGSize { func updateLayout(interfaceState: ChatPresentationInterfaceState, subject: ChatEmptyNode.Subject, size: CGSize, transition: ContainedViewLayoutTransition) -> CGSize {
if self.currentTheme !== interfaceState.theme || self.currentStrings !== interfaceState.strings { if self.currentTheme !== interfaceState.theme || self.currentStrings !== interfaceState.strings {
self.currentTheme = interfaceState.theme self.currentTheme = interfaceState.theme
self.currentStrings = interfaceState.strings self.currentStrings = interfaceState.strings
@ -309,7 +313,7 @@ final class ChatEmptyNodeNearbyChatContent: ASDisplayNode, ChatEmptyNodeStickerC
let _ = self.interaction?.sendSticker(.standalone(media: stickerItem.stickerItem.file), false, self.view, self.stickerNode.bounds, nil, []) let _ = self.interaction?.sendSticker(.standalone(media: stickerItem.stickerItem.file), false, self.view, self.stickerNode.bounds, nil, [])
} }
func updateLayout(interfaceState: ChatPresentationInterfaceState, size: CGSize, transition: ContainedViewLayoutTransition) -> CGSize { func updateLayout(interfaceState: ChatPresentationInterfaceState, subject: ChatEmptyNode.Subject, size: CGSize, transition: ContainedViewLayoutTransition) -> CGSize {
if self.currentTheme !== interfaceState.theme || self.currentStrings !== interfaceState.strings { if self.currentTheme !== interfaceState.theme || self.currentStrings !== interfaceState.strings {
self.currentTheme = interfaceState.theme self.currentTheme = interfaceState.theme
self.currentStrings = interfaceState.strings self.currentStrings = interfaceState.strings
@ -442,7 +446,7 @@ private final class ChatEmptyNodeSecretChatContent: ASDisplayNode, ChatEmptyNode
self.addSubnode(self.subtitleNode) self.addSubnode(self.subtitleNode)
} }
func updateLayout(interfaceState: ChatPresentationInterfaceState, size: CGSize, transition: ContainedViewLayoutTransition) -> CGSize { func updateLayout(interfaceState: ChatPresentationInterfaceState, subject: ChatEmptyNode.Subject, size: CGSize, transition: ContainedViewLayoutTransition) -> CGSize {
if self.currentTheme !== interfaceState.theme || self.currentStrings !== interfaceState.strings { if self.currentTheme !== interfaceState.theme || self.currentStrings !== interfaceState.strings {
self.currentTheme = interfaceState.theme self.currentTheme = interfaceState.theme
self.currentStrings = interfaceState.strings self.currentStrings = interfaceState.strings
@ -576,7 +580,7 @@ private final class ChatEmptyNodeGroupChatContent: ASDisplayNode, ChatEmptyNodeC
self.addSubnode(self.subtitleNode) self.addSubnode(self.subtitleNode)
} }
func updateLayout(interfaceState: ChatPresentationInterfaceState, size: CGSize, transition: ContainedViewLayoutTransition) -> CGSize { func updateLayout(interfaceState: ChatPresentationInterfaceState, subject: ChatEmptyNode.Subject, size: CGSize, transition: ContainedViewLayoutTransition) -> CGSize {
if self.currentTheme !== interfaceState.theme || self.currentStrings !== interfaceState.strings { if self.currentTheme !== interfaceState.theme || self.currentStrings !== interfaceState.strings {
self.currentTheme = interfaceState.theme self.currentTheme = interfaceState.theme
self.currentStrings = interfaceState.strings self.currentStrings = interfaceState.strings
@ -691,7 +695,7 @@ private final class ChatEmptyNodeCloudChatContent: ASDisplayNode, ChatEmptyNodeC
self.addSubnode(self.titleNode) self.addSubnode(self.titleNode)
} }
func updateLayout(interfaceState: ChatPresentationInterfaceState, size: CGSize, transition: ContainedViewLayoutTransition) -> CGSize { func updateLayout(interfaceState: ChatPresentationInterfaceState, subject: ChatEmptyNode.Subject, size: CGSize, transition: ContainedViewLayoutTransition) -> CGSize {
if self.currentTheme !== interfaceState.theme || self.currentStrings !== interfaceState.strings { if self.currentTheme !== interfaceState.theme || self.currentStrings !== interfaceState.strings {
self.currentTheme = interfaceState.theme self.currentTheme = interfaceState.theme
self.currentStrings = interfaceState.strings self.currentStrings = interfaceState.strings
@ -813,7 +817,7 @@ final class ChatEmptyNodeTopicChatContent: ASDisplayNode, ChatEmptyNodeContent,
self.addSubnode(self.textNode) self.addSubnode(self.textNode)
} }
func updateLayout(interfaceState: ChatPresentationInterfaceState, size: CGSize, transition: ContainedViewLayoutTransition) -> CGSize { func updateLayout(interfaceState: ChatPresentationInterfaceState, subject: ChatEmptyNode.Subject, size: CGSize, transition: ContainedViewLayoutTransition) -> CGSize {
let serviceColor = serviceMessageColorComponents(theme: interfaceState.theme, wallpaper: interfaceState.chatWallpaper) let serviceColor = serviceMessageColorComponents(theme: interfaceState.theme, wallpaper: interfaceState.chatWallpaper)
if self.currentTheme !== interfaceState.theme || self.currentStrings !== interfaceState.strings { if self.currentTheme !== interfaceState.theme || self.currentStrings !== interfaceState.strings {
self.currentTheme = interfaceState.theme self.currentTheme = interfaceState.theme
@ -900,6 +904,10 @@ private enum ChatEmptyNodeContentType: Equatable {
} }
final class ChatEmptyNode: ASDisplayNode { final class ChatEmptyNode: ASDisplayNode {
enum Subject {
case emptyChat(ChatHistoryNodeLoadState.EmptyType)
case detailsPlaceholder
}
private let context: AccountContext private let context: AccountContext
private let interaction: ChatPanelInterfaceInteraction? private let interaction: ChatPanelInterfaceInteraction?
@ -953,7 +961,7 @@ final class ChatEmptyNode: ASDisplayNode {
} }
} }
func updateLayout(interfaceState: ChatPresentationInterfaceState, emptyType: ChatHistoryNodeLoadState.EmptyType, loadingNode: ChatLoadingNode?, backgroundNode: WallpaperBackgroundNode?, size: CGSize, insets: UIEdgeInsets, transition: ContainedViewLayoutTransition) { func updateLayout(interfaceState: ChatPresentationInterfaceState, subject: Subject, loadingNode: ChatLoadingNode?, backgroundNode: WallpaperBackgroundNode?, size: CGSize, insets: UIEdgeInsets, transition: ContainedViewLayoutTransition) {
self.wallpaperBackgroundNode = backgroundNode self.wallpaperBackgroundNode = backgroundNode
if self.currentTheme !== interfaceState.theme || self.currentStrings !== interfaceState.strings { if self.currentTheme !== interfaceState.theme || self.currentStrings !== interfaceState.strings {
@ -969,36 +977,41 @@ final class ChatEmptyNode: ASDisplayNode {
} }
let contentType: ChatEmptyNodeContentType let contentType: ChatEmptyNodeContentType
if case .replyThread = interfaceState.chatLocation { switch subject {
if case .topic = emptyType { case .detailsPlaceholder:
contentType = .topic contentType = .regular
} else { case let .emptyChat(emptyType):
contentType = .regular if case .replyThread = interfaceState.chatLocation {
} if case .topic = emptyType {
} else if let peer = interfaceState.renderedPeer?.peer, !isScheduledMessages { contentType = .topic
if peer.id == self.context.account.peerId {
contentType = .cloud
} else if let _ = peer as? TelegramSecretChat {
contentType = .secret
} else if let group = peer as? TelegramGroup, case .creator = group.role {
contentType = .group
} else if let channel = peer as? TelegramChannel, case .group = channel.info, channel.flags.contains(.isCreator) && !channel.flags.contains(.isGigagroup) {
contentType = .group
} else if let _ = interfaceState.peerNearbyData {
contentType = .peerNearby
} else if let peer = peer as? TelegramUser {
if peer.isDeleted || peer.botInfo != nil || peer.flags.contains(.isSupport) || peer.isScam || interfaceState.peerIsBlocked {
contentType = .regular
} else if case .clearedHistory = emptyType {
contentType = .regular
} else { } else {
contentType = .greeting contentType = .regular
}
} else if let peer = interfaceState.renderedPeer?.peer, !isScheduledMessages {
if peer.id == self.context.account.peerId {
contentType = .cloud
} else if let _ = peer as? TelegramSecretChat {
contentType = .secret
} else if let group = peer as? TelegramGroup, case .creator = group.role {
contentType = .group
} else if let channel = peer as? TelegramChannel, case .group = channel.info, channel.flags.contains(.isCreator) && !channel.flags.contains(.isGigagroup) {
contentType = .group
} else if let _ = interfaceState.peerNearbyData {
contentType = .peerNearby
} else if let peer = peer as? TelegramUser {
if peer.isDeleted || peer.botInfo != nil || peer.flags.contains(.isSupport) || peer.isScam || interfaceState.peerIsBlocked {
contentType = .regular
} else if case .clearedHistory = emptyType {
contentType = .regular
} else {
contentType = .greeting
}
} else {
contentType = .regular
} }
} else { } else {
contentType = .regular contentType = .regular
} }
} else {
contentType = .regular
} }
var updateGreetingSticker = false var updateGreetingSticker = false
@ -1044,7 +1057,7 @@ final class ChatEmptyNode: ASDisplayNode {
var contentSize = CGSize() var contentSize = CGSize()
if let contentNode = self.content?.1 { if let contentNode = self.content?.1 {
contentSize = contentNode.updateLayout(interfaceState: interfaceState, size: displayRect.size, transition: contentTransition) contentSize = contentNode.updateLayout(interfaceState: interfaceState, subject: subject, size: displayRect.size, transition: contentTransition)
if updateGreetingSticker { if updateGreetingSticker {
self.context.prefetchManager?.prepareNextGreetingSticker() self.context.prefetchManager?.prepareNextGreetingSticker()

View File

@ -1531,7 +1531,7 @@ private class QrContentNode: ASDisplayNode, ContentNode {
self.containerNode = ASDisplayNode() self.containerNode = ASDisplayNode()
self.wallpaperBackgroundNode = createWallpaperBackgroundNode(context: context, forChatDisplay: true, useSharedAnimationPhase: false, useExperimentalImplementation: context.sharedContext.immediateExperimentalUISettings.experimentalBackground) self.wallpaperBackgroundNode = createWallpaperBackgroundNode(context: context, forChatDisplay: true, useSharedAnimationPhase: false)
self.codeBackgroundNode = ASDisplayNode() self.codeBackgroundNode = ASDisplayNode()
self.codeBackgroundNode.backgroundColor = .white self.codeBackgroundNode.backgroundColor = .white
@ -2022,7 +2022,7 @@ private class MessageContentNode: ASDisplayNode, ContentNode {
self.containerNode = ASDisplayNode() self.containerNode = ASDisplayNode()
self.wallpaperBackgroundNode = createWallpaperBackgroundNode(context: context, forChatDisplay: true, useSharedAnimationPhase: false, useExperimentalImplementation: context.sharedContext.immediateExperimentalUISettings.experimentalBackground) self.wallpaperBackgroundNode = createWallpaperBackgroundNode(context: context, forChatDisplay: true, useSharedAnimationPhase: false)
self.backgroundNode = ASDisplayNode() self.backgroundNode = ASDisplayNode()
self.backgroundImageNode = ASImageNode() self.backgroundImageNode = ASImageNode()

View File

@ -497,11 +497,13 @@ public final class MediaManagerImpl: NSObject, MediaManager {
strongSelf.voiceMediaPlayer?.stop() strongSelf.voiceMediaPlayer?.stop()
if let (account, playlist, settings, storedState) = inputData { if let (account, playlist, settings, storedState) = inputData {
var continueInstantVideoLoopAfterFinish: Bool = true var continueInstantVideoLoopAfterFinish: Bool = true
var controlPlaybackWithProximity: Bool = true
if let playlist = playlist as? PeerMessagesMediaPlaylist { if let playlist = playlist as? PeerMessagesMediaPlaylist {
continueInstantVideoLoopAfterFinish = playlist.context.sharedContext.energyUsageSettings.autoplayVideo continueInstantVideoLoopAfterFinish = playlist.context.sharedContext.energyUsageSettings.autoplayVideo
controlPlaybackWithProximity = playlist.context.sharedContext.currentMediaInputSettings.with({ $0.enableRaiseToSpeak })
} }
let voiceMediaPlayer = SharedMediaPlayer(mediaManager: strongSelf, inForeground: strongSelf.inForeground, account: account, audioSession: strongSelf.audioSession, overlayMediaManager: strongSelf.overlayMediaManager, playlist: playlist, initialOrder: .reversed, initialLooping: .none, initialPlaybackRate: settings.voicePlaybackRate, playerIndex: nextPlayerIndex, controlPlaybackWithProximity: true, type: type, continueInstantVideoLoopAfterFinish: continueInstantVideoLoopAfterFinish) let voiceMediaPlayer = SharedMediaPlayer(mediaManager: strongSelf, inForeground: strongSelf.inForeground, account: account, audioSession: strongSelf.audioSession, overlayMediaManager: strongSelf.overlayMediaManager, playlist: playlist, initialOrder: .reversed, initialLooping: .none, initialPlaybackRate: settings.voicePlaybackRate, playerIndex: nextPlayerIndex, controlPlaybackWithProximity: controlPlaybackWithProximity, type: type, continueInstantVideoLoopAfterFinish: continueInstantVideoLoopAfterFinish)
strongSelf.voiceMediaPlayer = voiceMediaPlayer strongSelf.voiceMediaPlayer = voiceMediaPlayer
voiceMediaPlayer.playedToEnd = { [weak voiceMediaPlayer] in voiceMediaPlayer.playedToEnd = { [weak voiceMediaPlayer] in
if let strongSelf = self, let voiceMediaPlayer = voiceMediaPlayer, voiceMediaPlayer === strongSelf.voiceMediaPlayer { if let strongSelf = self, let voiceMediaPlayer = voiceMediaPlayer, voiceMediaPlayer === strongSelf.voiceMediaPlayer {

View File

@ -41,8 +41,9 @@ public struct NotificationViewControllerInitializationData {
public let encryptionParameters: (Data, Data) public let encryptionParameters: (Data, Data)
public let appVersion: String public let appVersion: String
public let bundleData: Data? public let bundleData: Data?
public let useBetaFeatures: Bool
public init(appBundleId: String, appBuildType: TelegramAppBuildType, appGroupPath: String, apiId: Int32, apiHash: String, languagesCategory: String, encryptionParameters: (Data, Data), appVersion: String, bundleData: Data?) { public init(appBundleId: String, appBuildType: TelegramAppBuildType, appGroupPath: String, apiId: Int32, apiHash: String, languagesCategory: String, encryptionParameters: (Data, Data), appVersion: String, bundleData: Data?, useBetaFeatures: Bool) {
self.appBundleId = appBundleId self.appBundleId = appBundleId
self.appBuildType = appBuildType self.appBuildType = appBuildType
self.appGroupPath = appGroupPath self.appGroupPath = appGroupPath
@ -52,6 +53,7 @@ public struct NotificationViewControllerInitializationData {
self.encryptionParameters = encryptionParameters self.encryptionParameters = encryptionParameters
self.appVersion = appVersion self.appVersion = appVersion
self.bundleData = bundleData self.bundleData = bundleData
self.useBetaFeatures = useBetaFeatures
} }
} }
@ -138,7 +140,7 @@ public final class NotificationViewControllerImpl {
return nil return nil
}) })
sharedAccountContext = SharedAccountContextImpl(mainWindow: nil, sharedContainerPath: self.initializationData.appGroupPath, basePath: rootPath, encryptionParameters: ValueBoxEncryptionParameters(forceEncryptionIfNoSet: false, key: ValueBoxEncryptionParameters.Key(data: self.initializationData.encryptionParameters.0)!, salt: ValueBoxEncryptionParameters.Salt(data: self.initializationData.encryptionParameters.1)!), accountManager: accountManager, appLockContext: appLockContext, applicationBindings: applicationBindings, initialPresentationDataAndSettings: initialPresentationDataAndSettings!, networkArguments: NetworkInitializationArguments(apiId: self.initializationData.apiId, apiHash: self.initializationData.apiHash, languagesCategory: self.initializationData.languagesCategory, appVersion: self.initializationData.appVersion, voipMaxLayer: 0, voipVersions: [], appData: .single(self.initializationData.bundleData), autolockDeadine: .single(nil), encryptionProvider: OpenSSLEncryptionProvider(), deviceModelName: nil), hasInAppPurchases: false, rootPath: rootPath, legacyBasePath: nil, apsNotificationToken: .never(), voipNotificationToken: .never(), firebaseSecretStream: .never(), setNotificationCall: { _ in }, navigateToChat: { _, _, _ in }, appDelegate: nil) sharedAccountContext = SharedAccountContextImpl(mainWindow: nil, sharedContainerPath: self.initializationData.appGroupPath, basePath: rootPath, encryptionParameters: ValueBoxEncryptionParameters(forceEncryptionIfNoSet: false, key: ValueBoxEncryptionParameters.Key(data: self.initializationData.encryptionParameters.0)!, salt: ValueBoxEncryptionParameters.Salt(data: self.initializationData.encryptionParameters.1)!), accountManager: accountManager, appLockContext: appLockContext, applicationBindings: applicationBindings, initialPresentationDataAndSettings: initialPresentationDataAndSettings!, networkArguments: NetworkInitializationArguments(apiId: self.initializationData.apiId, apiHash: self.initializationData.apiHash, languagesCategory: self.initializationData.languagesCategory, appVersion: self.initializationData.appVersion, voipMaxLayer: 0, voipVersions: [], appData: .single(self.initializationData.bundleData), autolockDeadine: .single(nil), encryptionProvider: OpenSSLEncryptionProvider(), deviceModelName: nil, useBetaFeatures: self.initializationData.useBetaFeatures), hasInAppPurchases: false, rootPath: rootPath, legacyBasePath: nil, apsNotificationToken: .never(), voipNotificationToken: .never(), firebaseSecretStream: .never(), setNotificationCall: { _ in }, navigateToChat: { _, _, _ in }, appDelegate: nil)
presentationDataPromise.set(sharedAccountContext!.presentationData) presentationDataPromise.set(sharedAccountContext!.presentationData)
} }

View File

@ -257,6 +257,18 @@ private enum PeerInfoScreenInputData: Equatable {
case group(groupId: PeerId) case group(groupId: PeerId)
} }
public func hasAvailablePeerInfoMediaPanes(context: AccountContext, peerId: PeerId) -> Signal<Bool, NoError> {
let chatLocationContextHolder = Atomic<ChatLocationContextHolder?>(value: nil)
return peerInfoAvailableMediaPanes(context: context, peerId: peerId, chatLocation: .peer(id: peerId), chatLocationContextHolder: chatLocationContextHolder)
|> map { panes -> Bool in
if let panes {
return !panes.isEmpty
} else {
return false
}
}
}
private func peerInfoAvailableMediaPanes(context: AccountContext, peerId: PeerId, chatLocation: ChatLocation, chatLocationContextHolder: Atomic<ChatLocationContextHolder?>) -> Signal<[PeerInfoPaneKey]?, NoError> { private func peerInfoAvailableMediaPanes(context: AccountContext, peerId: PeerId, chatLocation: ChatLocation, chatLocationContextHolder: Atomic<ChatLocationContextHolder?>) -> Signal<[PeerInfoPaneKey]?, NoError> {
let tags: [(MessageTags, PeerInfoPaneKey)] = [ let tags: [(MessageTags, PeerInfoPaneKey)] = [
(.photoOrVideo, .media), (.photoOrVideo, .media),

View File

@ -66,8 +66,9 @@ public struct ShareRootControllerInitializationData {
public let encryptionParameters: (Data, Data) public let encryptionParameters: (Data, Data)
public let appVersion: String public let appVersion: String
public let bundleData: Data? public let bundleData: Data?
public let useBetaFeatures: Bool
public init(appBundleId: String, appBuildType: TelegramAppBuildType, appGroupPath: String, apiId: Int32, apiHash: String, languagesCategory: String, encryptionParameters: (Data, Data), appVersion: String, bundleData: Data?) { public init(appBundleId: String, appBuildType: TelegramAppBuildType, appGroupPath: String, apiId: Int32, apiHash: String, languagesCategory: String, encryptionParameters: (Data, Data), appVersion: String, bundleData: Data?, useBetaFeatures: Bool) {
self.appBundleId = appBundleId self.appBundleId = appBundleId
self.appBuildType = appBuildType self.appBuildType = appBuildType
self.appGroupPath = appGroupPath self.appGroupPath = appGroupPath
@ -77,6 +78,7 @@ public struct ShareRootControllerInitializationData {
self.encryptionParameters = encryptionParameters self.encryptionParameters = encryptionParameters
self.appVersion = appVersion self.appVersion = appVersion
self.bundleData = bundleData self.bundleData = bundleData
self.useBetaFeatures = useBetaFeatures
} }
} }
@ -239,7 +241,7 @@ public class ShareRootControllerImpl {
return nil return nil
}) })
let sharedContext = SharedAccountContextImpl(mainWindow: nil, sharedContainerPath: self.initializationData.appGroupPath, basePath: rootPath, encryptionParameters: ValueBoxEncryptionParameters(forceEncryptionIfNoSet: false, key: ValueBoxEncryptionParameters.Key(data: self.initializationData.encryptionParameters.0)!, salt: ValueBoxEncryptionParameters.Salt(data: self.initializationData.encryptionParameters.1)!), accountManager: accountManager, appLockContext: appLockContext, applicationBindings: applicationBindings, initialPresentationDataAndSettings: initialPresentationDataAndSettings!, networkArguments: NetworkInitializationArguments(apiId: self.initializationData.apiId, apiHash: self.initializationData.apiHash, languagesCategory: self.initializationData.languagesCategory, appVersion: self.initializationData.appVersion, voipMaxLayer: 0, voipVersions: [], appData: .single(self.initializationData.bundleData), autolockDeadine: .single(nil), encryptionProvider: OpenSSLEncryptionProvider(), deviceModelName: nil), hasInAppPurchases: false, rootPath: rootPath, legacyBasePath: nil, apsNotificationToken: .never(), voipNotificationToken: .never(), firebaseSecretStream: .never(), setNotificationCall: { _ in }, navigateToChat: { _, _, _ in }, appDelegate: nil) let sharedContext = SharedAccountContextImpl(mainWindow: nil, sharedContainerPath: self.initializationData.appGroupPath, basePath: rootPath, encryptionParameters: ValueBoxEncryptionParameters(forceEncryptionIfNoSet: false, key: ValueBoxEncryptionParameters.Key(data: self.initializationData.encryptionParameters.0)!, salt: ValueBoxEncryptionParameters.Salt(data: self.initializationData.encryptionParameters.1)!), accountManager: accountManager, appLockContext: appLockContext, applicationBindings: applicationBindings, initialPresentationDataAndSettings: initialPresentationDataAndSettings!, networkArguments: NetworkInitializationArguments(apiId: self.initializationData.apiId, apiHash: self.initializationData.apiHash, languagesCategory: self.initializationData.languagesCategory, appVersion: self.initializationData.appVersion, voipMaxLayer: 0, voipVersions: [], appData: .single(self.initializationData.bundleData), autolockDeadine: .single(nil), encryptionProvider: OpenSSLEncryptionProvider(), deviceModelName: nil, useBetaFeatures: self.initializationData.useBetaFeatures), hasInAppPurchases: false, rootPath: rootPath, legacyBasePath: nil, apsNotificationToken: .never(), voipNotificationToken: .never(), firebaseSecretStream: .never(), setNotificationCall: { _ in }, navigateToChat: { _, _, _ in }, appDelegate: nil)
presentationDataPromise.set(sharedContext.presentationData) presentationDataPromise.set(sharedContext.presentationData)
internalContext = InternalContext(sharedContext: sharedContext) internalContext = InternalContext(sharedContext: sharedContext)
globalInternalContext = internalContext globalInternalContext = internalContext

View File

@ -15,6 +15,46 @@ import AppBundle
import DatePickerNode import DatePickerNode
import DebugSettingsUI import DebugSettingsUI
import TabBarUI import TabBarUI
import WallpaperBackgroundNode
import ChatPresentationInterfaceState
private class DetailsChatPlaceholderNode: ASDisplayNode, NavigationDetailsPlaceholderNode {
private var presentationData: PresentationData
private var presentationInterfaceState: ChatPresentationInterfaceState
let wallpaperBackgroundNode: WallpaperBackgroundNode
let emptyNode: ChatEmptyNode
init(context: AccountContext) {
self.presentationData = context.sharedContext.currentPresentationData.with { $0 }
self.presentationInterfaceState = ChatPresentationInterfaceState(chatWallpaper: self.presentationData.chatWallpaper, theme: self.presentationData.theme, strings: self.presentationData.strings, dateTimeFormat: self.presentationData.dateTimeFormat, nameDisplayOrder: self.presentationData.nameDisplayOrder, limitsConfiguration: context.currentLimitsConfiguration.with { $0 }, fontSize: self.presentationData.chatFontSize, bubbleCorners: self.presentationData.chatBubbleCorners, accountPeerId: context.account.peerId, mode: .standard(previewing: false), chatLocation: .peer(id: context.account.peerId), subject: nil, peerNearbyData: nil, greetingData: nil, pendingUnpinnedAllMessages: false, activeGroupCallInfo: nil, hasActiveGroupCall: false, importState: nil, threadData: nil, isGeneralThreadClosed: nil)
self.wallpaperBackgroundNode = createWallpaperBackgroundNode(context: context, forChatDisplay: true, useSharedAnimationPhase: true)
self.emptyNode = ChatEmptyNode(context: context, interaction: nil)
super.init()
self.addSubnode(self.wallpaperBackgroundNode)
self.addSubnode(self.emptyNode)
}
func updatePresentationData(_ presentationData: PresentationData) {
self.presentationData = presentationData
self.presentationInterfaceState = ChatPresentationInterfaceState(chatWallpaper: self.presentationData.chatWallpaper, theme: self.presentationData.theme, strings: self.presentationData.strings, dateTimeFormat: self.presentationData.dateTimeFormat, nameDisplayOrder: self.presentationData.nameDisplayOrder, limitsConfiguration: self.presentationInterfaceState.limitsConfiguration, fontSize: self.presentationData.chatFontSize, bubbleCorners: self.presentationData.chatBubbleCorners, accountPeerId: self.presentationInterfaceState.accountPeerId, mode: .standard(previewing: false), chatLocation: self.presentationInterfaceState.chatLocation, subject: nil, peerNearbyData: nil, greetingData: nil, pendingUnpinnedAllMessages: false, activeGroupCallInfo: nil, hasActiveGroupCall: false, importState: nil, threadData: nil, isGeneralThreadClosed: nil)
self.wallpaperBackgroundNode.update(wallpaper: presentationData.chatWallpaper)
}
func updateLayout(size: CGSize, needsTiling: Bool, transition: ContainedViewLayoutTransition) {
let contentBounds = CGRect(origin: .zero, size: size)
self.wallpaperBackgroundNode.updateLayout(size: size, displayMode: needsTiling ? .aspectFit : .aspectFill, transition: transition)
transition.updateFrame(node: self.wallpaperBackgroundNode, frame: contentBounds)
self.emptyNode.updateLayout(interfaceState: self.presentationInterfaceState, subject: .detailsPlaceholder, loadingNode: nil, backgroundNode: self.wallpaperBackgroundNode, size: contentBounds.size, insets: .zero, transition: transition)
transition.updateFrame(node: self.emptyNode, frame: CGRect(origin: .zero, size: size))
self.emptyNode.update(rect: contentBounds, within: contentBounds.size, transition: transition)
}
}
public final class TelegramRootController: NavigationController { public final class TelegramRootController: NavigationController {
private let context: AccountContext private let context: AccountContext
@ -30,6 +70,8 @@ public final class TelegramRootController: NavigationController {
private var presentationDataDisposable: Disposable? private var presentationDataDisposable: Disposable?
private var presentationData: PresentationData private var presentationData: PresentationData
private var detailsPlaceholderNode: DetailsChatPlaceholderNode?
private var applicationInFocusDisposable: Disposable? private var applicationInFocusDisposable: Disposable?
public init(context: AccountContext) { public init(context: AccountContext) {
@ -37,32 +79,12 @@ public final class TelegramRootController: NavigationController {
self.presentationData = context.sharedContext.currentPresentationData.with { $0 } self.presentationData = context.sharedContext.currentPresentationData.with { $0 }
let navigationDetailsBackgroundMode: NavigationEmptyDetailsBackgoundMode? super.init(mode: .automaticMasterDetail, theme: NavigationControllerTheme(presentationTheme: self.presentationData.theme))
switch presentationData.chatWallpaper {
case .color:
let image = generateTintedImage(image: UIImage(bundleImageName: "Chat List/EmptyMasterDetailIcon"), color: presentationData.theme.chatList.messageTextColor.withAlphaComponent(0.2))
navigationDetailsBackgroundMode = image != nil ? .image(image!) : nil
default:
let image = chatControllerBackgroundImage(theme: presentationData.theme, wallpaper: presentationData.chatWallpaper, mediaBox: context.account.postbox.mediaBox, knockoutMode: context.sharedContext.immediateExperimentalUISettings.knockoutWallpaper)
navigationDetailsBackgroundMode = image != nil ? .wallpaper(image!) : nil
}
super.init(mode: .automaticMasterDetail, theme: NavigationControllerTheme(presentationTheme: self.presentationData.theme), backgroundDetailsMode: navigationDetailsBackgroundMode)
self.presentationDataDisposable = (context.sharedContext.presentationData self.presentationDataDisposable = (context.sharedContext.presentationData
|> deliverOnMainQueue).start(next: { [weak self] presentationData in |> deliverOnMainQueue).start(next: { [weak self] presentationData in
if let strongSelf = self { if let strongSelf = self {
if presentationData.chatWallpaper != strongSelf.presentationData.chatWallpaper { strongSelf.detailsPlaceholderNode?.updatePresentationData(presentationData)
let navigationDetailsBackgroundMode: NavigationEmptyDetailsBackgoundMode?
switch presentationData.chatWallpaper {
case .color:
let image = generateTintedImage(image: UIImage(bundleImageName: "Chat List/EmptyMasterDetailIcon"), color: presentationData.theme.chatList.messageTextColor.withAlphaComponent(0.2))
navigationDetailsBackgroundMode = image != nil ? .image(image!) : nil
default:
navigationDetailsBackgroundMode = chatControllerBackgroundImage(theme: presentationData.theme, wallpaper: presentationData.chatWallpaper, mediaBox: strongSelf.context.sharedContext.accountManager.mediaBox, knockoutMode: strongSelf.context.sharedContext.immediateExperimentalUISettings.knockoutWallpaper).flatMap(NavigationEmptyDetailsBackgoundMode.wallpaper)
}
strongSelf.updateBackgroundDetailsMode(navigationDetailsBackgroundMode, transition: .immediate)
}
let previousTheme = strongSelf.presentationData.theme let previousTheme = strongSelf.presentationData.theme
strongSelf.presentationData = presentationData strongSelf.presentationData = presentationData
@ -92,6 +114,32 @@ public final class TelegramRootController: NavigationController {
self.applicationInFocusDisposable?.dispose() self.applicationInFocusDisposable?.dispose()
} }
override public func containerLayoutUpdated(_ layout: ContainerViewLayout, transition: ContainedViewLayoutTransition) {
let needsRootWallpaperBackgroundNode: Bool
if case .regular = layout.metrics.widthClass {
needsRootWallpaperBackgroundNode = true
} else {
needsRootWallpaperBackgroundNode = false
}
if needsRootWallpaperBackgroundNode {
let detailsPlaceholderNode: DetailsChatPlaceholderNode
if let current = self.detailsPlaceholderNode {
detailsPlaceholderNode = current
} else {
detailsPlaceholderNode = DetailsChatPlaceholderNode(context: self.context)
detailsPlaceholderNode.wallpaperBackgroundNode.update(wallpaper: self.presentationData.chatWallpaper)
self.detailsPlaceholderNode = detailsPlaceholderNode
}
self.updateDetailsPlaceholderNode(detailsPlaceholderNode)
} else if let _ = self.detailsPlaceholderNode {
self.detailsPlaceholderNode = nil
self.updateDetailsPlaceholderNode(nil)
}
super.containerLayoutUpdated(layout, transition: transition)
}
public func addRootControllers(showCallsTab: Bool) { public func addRootControllers(showCallsTab: Bool) {
let tabBarController = TabBarControllerImpl(navigationBarPresentationData: NavigationBarPresentationData(presentationData: self.presentationData), theme: TabBarControllerTheme(rootControllerTheme: self.presentationData.theme)) let tabBarController = TabBarControllerImpl(navigationBarPresentationData: NavigationBarPresentationData(presentationData: self.presentationData), theme: TabBarControllerTheme(rootControllerTheme: self.presentationData.theme))
tabBarController.navigationPresentation = .master tabBarController.navigationPresentation = .master

View File

@ -40,7 +40,6 @@ public struct ExperimentalUISettings: Codable, Equatable {
public var experimentalCompatibility: Bool public var experimentalCompatibility: Bool
public var enableDebugDataDisplay: Bool public var enableDebugDataDisplay: Bool
public var acceleratedStickers: Bool public var acceleratedStickers: Bool
public var experimentalBackground: Bool
public var inlineStickers: Bool public var inlineStickers: Bool
public var localTranscription: Bool public var localTranscription: Bool
public var enableReactionOverrides: Bool public var enableReactionOverrides: Bool
@ -69,7 +68,6 @@ public struct ExperimentalUISettings: Codable, Equatable {
experimentalCompatibility: false, experimentalCompatibility: false,
enableDebugDataDisplay: false, enableDebugDataDisplay: false,
acceleratedStickers: false, acceleratedStickers: false,
experimentalBackground: false,
inlineStickers: false, inlineStickers: false,
localTranscription: false, localTranscription: false,
enableReactionOverrides: false, enableReactionOverrides: false,
@ -99,7 +97,6 @@ public struct ExperimentalUISettings: Codable, Equatable {
experimentalCompatibility: Bool, experimentalCompatibility: Bool,
enableDebugDataDisplay: Bool, enableDebugDataDisplay: Bool,
acceleratedStickers: Bool, acceleratedStickers: Bool,
experimentalBackground: Bool,
inlineStickers: Bool, inlineStickers: Bool,
localTranscription: Bool, localTranscription: Bool,
enableReactionOverrides: Bool, enableReactionOverrides: Bool,
@ -126,7 +123,6 @@ public struct ExperimentalUISettings: Codable, Equatable {
self.experimentalCompatibility = experimentalCompatibility self.experimentalCompatibility = experimentalCompatibility
self.enableDebugDataDisplay = enableDebugDataDisplay self.enableDebugDataDisplay = enableDebugDataDisplay
self.acceleratedStickers = acceleratedStickers self.acceleratedStickers = acceleratedStickers
self.experimentalBackground = experimentalBackground
self.inlineStickers = inlineStickers self.inlineStickers = inlineStickers
self.localTranscription = localTranscription self.localTranscription = localTranscription
self.enableReactionOverrides = enableReactionOverrides self.enableReactionOverrides = enableReactionOverrides
@ -157,7 +153,6 @@ public struct ExperimentalUISettings: Codable, Equatable {
self.experimentalCompatibility = (try container.decodeIfPresent(Int32.self, forKey: "experimentalCompatibility") ?? 0) != 0 self.experimentalCompatibility = (try container.decodeIfPresent(Int32.self, forKey: "experimentalCompatibility") ?? 0) != 0
self.enableDebugDataDisplay = (try container.decodeIfPresent(Int32.self, forKey: "enableDebugDataDisplay") ?? 0) != 0 self.enableDebugDataDisplay = (try container.decodeIfPresent(Int32.self, forKey: "enableDebugDataDisplay") ?? 0) != 0
self.acceleratedStickers = (try container.decodeIfPresent(Int32.self, forKey: "acceleratedStickers") ?? 0) != 0 self.acceleratedStickers = (try container.decodeIfPresent(Int32.self, forKey: "acceleratedStickers") ?? 0) != 0
self.experimentalBackground = (try container.decodeIfPresent(Int32.self, forKey: "experimentalBackground") ?? 0) != 0
self.inlineStickers = (try container.decodeIfPresent(Int32.self, forKey: "inlineStickers") ?? 0) != 0 self.inlineStickers = (try container.decodeIfPresent(Int32.self, forKey: "inlineStickers") ?? 0) != 0
self.localTranscription = (try container.decodeIfPresent(Int32.self, forKey: "localTranscription") ?? 0) != 0 self.localTranscription = (try container.decodeIfPresent(Int32.self, forKey: "localTranscription") ?? 0) != 0
self.enableReactionOverrides = try container.decodeIfPresent(Bool.self, forKey: "enableReactionOverrides") ?? false self.enableReactionOverrides = try container.decodeIfPresent(Bool.self, forKey: "enableReactionOverrides") ?? false
@ -188,7 +183,6 @@ public struct ExperimentalUISettings: Codable, Equatable {
try container.encode((self.experimentalCompatibility ? 1 : 0) as Int32, forKey: "experimentalCompatibility") try container.encode((self.experimentalCompatibility ? 1 : 0) as Int32, forKey: "experimentalCompatibility")
try container.encode((self.enableDebugDataDisplay ? 1 : 0) as Int32, forKey: "enableDebugDataDisplay") try container.encode((self.enableDebugDataDisplay ? 1 : 0) as Int32, forKey: "enableDebugDataDisplay")
try container.encode((self.acceleratedStickers ? 1 : 0) as Int32, forKey: "acceleratedStickers") try container.encode((self.acceleratedStickers ? 1 : 0) as Int32, forKey: "acceleratedStickers")
try container.encode((self.experimentalBackground ? 1 : 0) as Int32, forKey: "experimentalBackground")
try container.encode((self.inlineStickers ? 1 : 0) as Int32, forKey: "inlineStickers") try container.encode((self.inlineStickers ? 1 : 0) as Int32, forKey: "inlineStickers")
try container.encode((self.localTranscription ? 1 : 0) as Int32, forKey: "localTranscription") try container.encode((self.localTranscription ? 1 : 0) as Int32, forKey: "localTranscription")
try container.encode(self.enableReactionOverrides, forKey: "enableReactionOverrides") try container.encode(self.enableReactionOverrides, forKey: "enableReactionOverrides")

View File

@ -282,8 +282,8 @@ public struct EnergyUsageSettings: Codable, Equatable {
autoplayVideo: true, autoplayVideo: true,
autoplayGif: true, autoplayGif: true,
loopStickers: true, loopStickers: true,
loopEmoji: isCapable ? false : true, loopEmoji: isCapable,
fullTranslucency: isCapable ? false : true, fullTranslucency: isCapable,
extendBackgroundWork: true, extendBackgroundWork: true,
autodownloadInBackground: true autodownloadInBackground: true
) )

@ -1 +1 @@
Subproject commit af88317c7529c5b69c4d574d1293fb385abb3f02 Subproject commit 35d5d408cee8c69b61a32bc96dbfccc37980a5ae

View File

@ -1,313 +1 @@
import Foundation
import UIKit
import AsyncDisplayKit
import Display
import GradientBackground
import TelegramPresentationData
import TelegramCore
import AccountContext
import SwiftSignalKit
import WallpaperResources
import FastBlur
import Svg
import GZip
import AppBundle
import AnimatedStickerNode
import TelegramAnimatedStickerNode
import HierarchyTrackingLayer
import MetalKit
import HierarchyTrackingLayer
import simd
private final class NullActionClass: NSObject, CAAction {
static let shared = NullActionClass()
@objc public func run(forKey event: String, object anObject: Any, arguments dict: [AnyHashable : Any]?) {
}
}
@available(iOS 13.0, *)
open class SimpleMetalLayer: CAMetalLayer {
override open func action(forKey event: String) -> CAAction? {
return nullAction
}
override public init() {
super.init()
}
override public init(layer: Any) {
super.init(layer: layer)
}
required public init?(coder: NSCoder) {
fatalError("init(coder:) has not been implemented")
}
}
private func makePipelineState(device: MTLDevice, library: MTLLibrary, vertexProgram: String, fragmentProgram: String) -> MTLRenderPipelineState? {
guard let loadedVertexProgram = library.makeFunction(name: vertexProgram) else {
return nil
}
guard let loadedFragmentProgram = library.makeFunction(name: fragmentProgram) else {
return nil
}
let pipelineStateDescriptor = MTLRenderPipelineDescriptor()
pipelineStateDescriptor.vertexFunction = loadedVertexProgram
pipelineStateDescriptor.fragmentFunction = loadedFragmentProgram
pipelineStateDescriptor.colorAttachments[0].pixelFormat = .bgra8Unorm
guard let pipelineState = try? device.makeRenderPipelineState(descriptor: pipelineStateDescriptor) else {
return nil
}
return pipelineState
}
@available(iOS 13.0, *)
final class MetalWallpaperBackgroundNode: ASDisplayNode, WallpaperBackgroundNode {
private let device: MTLDevice
private let metalLayer: SimpleMetalLayer
private let commandQueue: MTLCommandQueue
private let renderPipelineState: MTLRenderPipelineState
private let hierarchyTrackingLayer = HierarchyTrackingLayer()
var isReady: Signal<Bool, NoError> {
return .single(true)
}
var rotation: CGFloat = 0.0
private var animationPhase: Int = 0
private var animationThread: Thread?
private var displayLink: CADisplayLink?
override init() {
self.device = MTLCreateSystemDefaultDevice()!
self.metalLayer = SimpleMetalLayer()
self.metalLayer.maximumDrawableCount = 3
self.metalLayer.presentsWithTransaction = true
self.metalLayer.contentsScale = UIScreenScale
self.commandQueue = self.device.makeCommandQueue()!
let mainBundle = Bundle(for: MetalWallpaperBackgroundNode.self)
guard let path = mainBundle.path(forResource: "WallpaperBackgroundNodeBundle", ofType: "bundle") else {
preconditionFailure()
}
guard let bundle = Bundle(path: path) else {
preconditionFailure()
}
guard let defaultLibrary = try? self.device.makeDefaultLibrary(bundle: bundle) else {
preconditionFailure()
}
guard let renderPipelineState = makePipelineState(device: self.device, library: defaultLibrary, vertexProgram: "wallpaperVertex", fragmentProgram: "wallpaperFragment") else {
preconditionFailure()
}
self.renderPipelineState = renderPipelineState
super.init()
self.metalLayer.device = self.device
self.metalLayer.pixelFormat = .bgra8Unorm
self.metalLayer.framebufferOnly = true
self.metalLayer.allowsNextDrawableTimeout = true
self.metalLayer.isOpaque = true
self.layer.addSublayer(self.metalLayer)
self.layer.addSublayer(self.hierarchyTrackingLayer)
self.hierarchyTrackingLayer.opacity = 0.0
self.hierarchyTrackingLayer.didEnterHierarchy = { [weak self] in
self?.updateIsVisible(true)
}
self.hierarchyTrackingLayer.didExitHierarchy = { [weak self] in
self?.updateIsVisible(false)
}
}
func update(wallpaper: TelegramWallpaper) {
}
func _internalUpdateIsSettingUpWallpaper() {
}
func updateLayout(size: CGSize, displayMode: WallpaperDisplayMode, transition: ContainedViewLayoutTransition) {
if self.metalLayer.drawableSize != size {
self.metalLayer.drawableSize = size
transition.updateFrame(layer: self.metalLayer, frame: CGRect(origin: CGPoint(), size: size))
self.redraw()
}
}
private func updateIsVisible(_ isVisible: Bool) {
if isVisible {
if self.displayLink == nil {
let displayLink = CADisplayLink(target: DisplayLinkTarget { [weak self] in
guard let strongSelf = self else {
return
}
strongSelf.redraw()
}, selector: #selector(DisplayLinkTarget.event))
self.displayLink = displayLink
if #available(iOS 15.0, iOSApplicationExtension 15.0, *) {
if "".isEmpty {
displayLink.preferredFrameRateRange = CAFrameRateRange(minimum: 60.0, maximum: 60.0, preferred: 60.0)
} else {
displayLink.preferredFrameRateRange = CAFrameRateRange(minimum: Float(UIScreen.main.maximumFramesPerSecond), maximum: Float(UIScreen.main.maximumFramesPerSecond), preferred: Float(UIScreen.main.maximumFramesPerSecond))
}
}
displayLink.isPaused = false
if !"".isEmpty {
self.animationThread = Thread(block: {
displayLink.add(to: .current, forMode: .common)
while true {
if Thread.current.isCancelled {
break
}
RunLoop.current.run(until: .init(timeIntervalSinceNow: 1.0))
}
})
self.animationThread?.name = "MetalWallpaperBackgroundNode"
self.animationThread?.qualityOfService = .userInteractive
self.animationThread?.start()
} else {
displayLink.add(to: .current, forMode: .common)
}
}
} else {
if let displayLink = self.displayLink {
self.displayLink = nil
displayLink.invalidate()
}
if let animationThread = self.animationThread {
self.animationThread = nil
animationThread.cancel()
}
}
}
private var previousDrawTime: Double?
private func redraw() {
let timestamp = CACurrentMediaTime()
if let previousDrawTime = self.previousDrawTime {
let _ = previousDrawTime
//print("frame time \((timestamp - previousDrawTime) * 1000.0)")
}
self.previousDrawTime = timestamp
self.animationPhase += 1
let animationOffset = Float(self.animationPhase % 200) / 200.0
let _ = animationOffset
guard let commandBuffer = self.commandQueue.makeCommandBuffer() else {
return
}
guard let drawable = self.metalLayer.nextDrawable() else {
return
}
let drawTime = CACurrentMediaTime() - timestamp
if drawTime > 9.0 / 1000.0 {
print("get time \(drawTime * 1000.0)")
}
let renderPassDescriptor = MTLRenderPassDescriptor()
renderPassDescriptor.colorAttachments[0].texture = drawable.texture
renderPassDescriptor.colorAttachments[0].loadAction = .clear
renderPassDescriptor.colorAttachments[0].clearColor = MTLClearColor(
red: 0.0,
green: 0.0,
blue: 0.0,
alpha: 1.0
)
guard let renderEncoder = commandBuffer.makeRenderCommandEncoder(descriptor: renderPassDescriptor) else {
return
}
var vertices: [Float] = [
-1.0, -1.0,
1.0, -1.0,
-1.0, 1.0,
1.0, 1.0
]
renderEncoder.setRenderPipelineState(self.renderPipelineState)
renderEncoder.setVertexBytes(&vertices, length: 4 * vertices.count, index: 0)
var resolution = simd_uint2(UInt32(drawable.texture.width), UInt32(drawable.texture.height))
renderEncoder.setFragmentBytes(&resolution, length: MemoryLayout<simd_uint2>.size * 2, index: 0)
var time = Float(timestamp) * 0.25
renderEncoder.setFragmentBytes(&time, length: 4, index: 1)
renderEncoder.drawPrimitives(type: .triangleStrip, vertexStart: 0, vertexCount: 4, instanceCount: 1)
renderEncoder.endEncoding()
if self.metalLayer.presentsWithTransaction {
if Thread.isMainThread {
commandBuffer.commit()
commandBuffer.waitUntilScheduled()
drawable.present()
} else {
CATransaction.begin()
commandBuffer.commit()
commandBuffer.waitUntilScheduled()
drawable.present()
CATransaction.commit()
}
} else {
commandBuffer.addScheduledHandler { _ in
drawable.present()
}
commandBuffer.commit()
}
}
func animateEvent(transition: ContainedViewLayoutTransition, extendAnimation: Bool) {
}
func updateIsLooping(_ isLooping: Bool) {
}
func updateBubbleTheme(bubbleTheme: PresentationTheme, bubbleCorners: PresentationChatBubbleCorners) {
}
func hasBubbleBackground(for type: WallpaperBubbleType) -> Bool {
return false
}
func hasExtraBubbleBackground() -> Bool {
return false
}
func makeBubbleBackground(for type: WallpaperBubbleType) -> WallpaperBubbleBackgroundNode? {
return nil
}
func makeFreeBackground() -> PortalView? {
return nil
}
func makeDimmedNode() -> ASDisplayNode? {
return nil
}
}