mirror of
https://github.com/Swiftgram/Telegram-iOS.git
synced 2025-06-16 05:55:20 +00:00
Merge commit 'ed86e369ebda242bb9cf0f38df3821586bd7d307' into macos-9.5-release
This commit is contained in:
commit
080eba2533
@ -38,7 +38,7 @@ class NotificationViewController: UIViewController, UNNotificationContentExtensi
|
||||
|
||||
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
|
||||
})
|
||||
}
|
||||
|
@ -680,7 +680,7 @@ private final class NotificationServiceHandler {
|
||||
Logger.shared.logToConsole = loggingSettings.logToConsole
|
||||
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?
|
||||
if let data = try? Data(contentsOf: URL(fileURLWithPath: appLockStatePath(rootPath: rootPath))), let state = try? JSONDecoder().decode(LockState.self, from: data), isAppLocked(state: state) {
|
||||
|
@ -45,7 +45,7 @@ class ShareRootController: UIViewController {
|
||||
|
||||
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
|
||||
})
|
||||
}
|
||||
|
@ -174,7 +174,7 @@ class DefaultIntentHandler: INExtension, INSendMessageIntentHandling, INSearchFo
|
||||
if let accountCache = accountCache {
|
||||
account = .single(accountCache)
|
||||
} 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
|
||||
if let account = account {
|
||||
switch account {
|
||||
|
@ -9041,3 +9041,14 @@ Sorry for the inconvenience.";
|
||||
"Premium.Gift.TitleShort" = "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";
|
||||
|
@ -433,7 +433,7 @@ final class AttachmentContainer: ASDisplayNode, UIGestureRecognizerDelegate {
|
||||
let containerFrame: CGRect
|
||||
let clipFrame: CGRect
|
||||
let containerScale: CGFloat
|
||||
if layout.metrics.widthClass == .compact {
|
||||
if case .compact = layout.metrics.widthClass {
|
||||
self.clipNode.clipsToBounds = true
|
||||
|
||||
if isLandscape {
|
||||
|
@ -4811,7 +4811,7 @@ private final class ChatListLocationContext {
|
||||
strings: presentationData.strings,
|
||||
dateTimeFormat: presentationData.dateTimeFormat,
|
||||
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
|
||||
guard let self else {
|
||||
return
|
||||
|
@ -89,7 +89,6 @@ private enum DebugControllerEntry: ItemListNodeEntry {
|
||||
case experimentalCompatibility(Bool)
|
||||
case enableDebugDataDisplay(Bool)
|
||||
case acceleratedStickers(Bool)
|
||||
case experimentalBackground(Bool)
|
||||
case inlineForums(Bool)
|
||||
case localTranscription(Bool)
|
||||
case enableReactionOverrides(Bool)
|
||||
@ -118,7 +117,7 @@ private enum DebugControllerEntry: ItemListNodeEntry {
|
||||
return DebugControllerSection.logging.rawValue
|
||||
case .keepChatNavigationStack, .skipReadHistory, .crashOnSlowQueries:
|
||||
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
|
||||
case .logTranslationRecognition, .resetTranslationStates:
|
||||
return DebugControllerSection.translation.rawValue
|
||||
@ -201,8 +200,6 @@ private enum DebugControllerEntry: ItemListNodeEntry {
|
||||
return 34
|
||||
case .acceleratedStickers:
|
||||
return 35
|
||||
case .experimentalBackground:
|
||||
return 36
|
||||
case .inlineForums:
|
||||
return 37
|
||||
case .localTranscription:
|
||||
@ -1186,16 +1183,6 @@ private enum DebugControllerEntry: ItemListNodeEntry {
|
||||
})
|
||||
}).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):
|
||||
return ItemListSwitchItem(presentationData: presentationData, title: "Inline Forums", value: value, sectionId: self.section, style: .blocks, updated: { value 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] = []
|
||||
|
||||
let isMainApp = sharedContext.applicationBindings.isMainApp
|
||||
@ -1374,7 +1361,6 @@ private func debugControllerEntries(sharedContext: SharedAccountContext, present
|
||||
entries.append(.experimentalCompatibility(experimentalSettings.experimentalCompatibility))
|
||||
entries.append(.enableDebugDataDisplay(experimentalSettings.enableDebugDataDisplay))
|
||||
entries.append(.acceleratedStickers(experimentalSettings.acceleratedStickers))
|
||||
entries.append(.experimentalBackground(experimentalSettings.experimentalBackground))
|
||||
entries.append(.inlineForums(experimentalSettings.inlineForums))
|
||||
entries.append(.localTranscription(experimentalSettings.localTranscription))
|
||||
if case .internal = sharedContext.applicationBindings.appBuildType {
|
||||
@ -1404,7 +1390,7 @@ private func debugControllerEntries(sharedContext: SharedAccountContext, present
|
||||
|
||||
if isMainApp {
|
||||
entries.append(.disableVideoAspectScaling(experimentalSettings.disableVideoAspectScaling))
|
||||
entries.append(.enableNetworkFramework(networkSettings?.useNetworkFramework ?? false))
|
||||
entries.append(.enableNetworkFramework(networkSettings?.useNetworkFramework ?? useBetaFeatures))
|
||||
}
|
||||
|
||||
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 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))
|
||||
}
|
||||
|
@ -34,11 +34,6 @@ public struct NavigationAnimationOptions : OptionSet {
|
||||
public static let removeOnMasterDetails = NavigationAnimationOptions(rawValue: 1 << 0)
|
||||
}
|
||||
|
||||
public enum NavigationEmptyDetailsBackgoundMode {
|
||||
case image(UIImage)
|
||||
case wallpaper(UIImage)
|
||||
}
|
||||
|
||||
private enum ControllerTransition {
|
||||
case none
|
||||
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 {
|
||||
public var isOpaqueWhenInOverlay: Bool = true
|
||||
public var blocksBackgroundWhenInOverlay: Bool = true
|
||||
@ -131,7 +130,6 @@ open class NavigationController: UINavigationController, ContainableController,
|
||||
}
|
||||
|
||||
private var masterDetailsBlackout: MasterDetailLayoutBlackout?
|
||||
private var backgroundDetailsMode: NavigationEmptyDetailsBackgoundMode?
|
||||
|
||||
public var lockOrientation: Bool = false
|
||||
|
||||
@ -232,16 +230,21 @@ open class NavigationController: UINavigationController, ContainableController,
|
||||
self.requestLayout(transition: transition)
|
||||
}
|
||||
|
||||
public func updateBackgroundDetailsMode(_ mode: NavigationEmptyDetailsBackgoundMode?, transition: ContainedViewLayoutTransition) {
|
||||
self.backgroundDetailsMode = mode
|
||||
self.requestLayout(transition: transition)
|
||||
private weak var detailsPlaceholderNode: NavigationDetailsPlaceholderNode?
|
||||
public func updateDetailsPlaceholderNode(_ node: NavigationDetailsPlaceholderNode?) {
|
||||
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.theme = theme
|
||||
self.isFlat = isFlat
|
||||
self.backgroundDetailsMode = backgroundDetailsMode
|
||||
|
||||
super.init(nibName: nil, bundle: nil)
|
||||
}
|
||||
@ -340,7 +343,7 @@ open class NavigationController: UINavigationController, ContainableController,
|
||||
return nil
|
||||
}
|
||||
|
||||
public func containerLayoutUpdated(_ layout: ContainerViewLayout, transition: ContainedViewLayoutTransition) {
|
||||
open func containerLayoutUpdated(_ layout: ContainerViewLayout, transition: ContainedViewLayoutTransition) {
|
||||
if !self.isViewLoaded {
|
||||
self.loadView()
|
||||
}
|
||||
@ -836,7 +839,11 @@ open class NavigationController: UINavigationController, ContainableController,
|
||||
flatContainer.keyboardViewManager = nil
|
||||
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)
|
||||
flatContainer.frame = CGRect(origin: CGPoint(), size: layout.size)
|
||||
flatContainer.update(layout: layout, canBeClosed: false, controllers: controllers, transition: .immediate)
|
||||
@ -859,7 +866,11 @@ open class NavigationController: UINavigationController, ContainableController,
|
||||
flatContainer.keyboardViewManager = nil
|
||||
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)
|
||||
flatContainer.frame = CGRect(origin: CGPoint(), size: layout.size)
|
||||
flatContainer.update(layout: layout, canBeClosed: false, controllers: controllers, transition: .immediate)
|
||||
@ -873,7 +884,11 @@ open class NavigationController: UINavigationController, ContainableController,
|
||||
}, scrollToTop: { [weak self] subject in
|
||||
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)
|
||||
if previousModalContainer == nil {
|
||||
splitContainer.canHaveKeyboardFocus = true
|
||||
@ -881,7 +896,7 @@ open class NavigationController: UINavigationController, ContainableController,
|
||||
splitContainer.canHaveKeyboardFocus = false
|
||||
}
|
||||
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.removeFromSupernode()
|
||||
case let .split(splitContainer):
|
||||
@ -891,7 +906,7 @@ open class NavigationController: UINavigationController, ContainableController,
|
||||
splitContainer.canHaveKeyboardFocus = false
|
||||
}
|
||||
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 {
|
||||
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
|
||||
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)
|
||||
if previousModalContainer == nil {
|
||||
splitContainer.canHaveKeyboardFocus = true
|
||||
@ -907,7 +926,7 @@ open class NavigationController: UINavigationController, ContainableController,
|
||||
splitContainer.canHaveKeyboardFocus = false
|
||||
}
|
||||
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)
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -83,7 +83,7 @@ final class NavigationSplitContainer: ASDisplayNode {
|
||||
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 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.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.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)
|
||||
|
||||
|
@ -271,6 +271,22 @@ public final class GradientBackgroundNode: ASDisplayNode {
|
||||
|
||||
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) {
|
||||
self.useSharedAnimationPhase = useSharedAnimationPhase
|
||||
self.saturation = adjustSaturation ? 1.7 : 1.0
|
||||
@ -289,12 +305,26 @@ public final class GradientBackgroundNode: ASDisplayNode {
|
||||
|
||||
if useSharedAnimationPhase {
|
||||
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 {
|
||||
self.phase = 0
|
||||
}
|
||||
}
|
||||
|
||||
deinit {
|
||||
self.sharedAnimationSyncDisposable?.dispose()
|
||||
}
|
||||
|
||||
public func setPatternOverlay(layer: GradientBackgroundPatternOverlayLayer?) {
|
||||
@ -422,8 +452,7 @@ public final class GradientBackgroundNode: ASDisplayNode {
|
||||
animation.fillMode = .backwards
|
||||
animation.beginTime = self.contentView.layer.convertTime(CACurrentMediaTime(), from: nil) + 0.25
|
||||
}
|
||||
|
||||
|
||||
|
||||
self.isAnimating = true
|
||||
if let patternOverlayLayer = self.patternOverlayLayer {
|
||||
patternOverlayLayer.isAnimating = true
|
||||
@ -542,6 +571,8 @@ public final class GradientBackgroundNode: ASDisplayNode {
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
public func animateEvent(transition: ContainedViewLayoutTransition, extendAnimation: Bool, backwards: Bool, completion: @escaping () -> Void) {
|
||||
guard case let .animated(duration, _) = transition, duration > 0.001 else {
|
||||
@ -560,6 +591,7 @@ public final class GradientBackgroundNode: ASDisplayNode {
|
||||
}
|
||||
if self.useSharedAnimationPhase {
|
||||
GradientBackgroundNode.sharedPhase = self.phase
|
||||
GradientBackgroundNode.sharedAnimationSyncPipe.putNext(SharedAnimationUpdate(phase: self.phase, sender: self))
|
||||
}
|
||||
if let size = self.validLayout {
|
||||
self.updateLayout(size: size, transition: transition, extendAnimation: extendAnimation, backwards: backwards, completion: completion)
|
||||
|
@ -287,9 +287,7 @@ public class InvisibleInkDustNode: ASDisplayNode {
|
||||
return
|
||||
}
|
||||
self.staticParams = (size, color, lineRects)
|
||||
|
||||
let start = CACurrentMediaTime()
|
||||
|
||||
|
||||
var combinedRect: CGRect?
|
||||
var combinedRects: [CGRect] = []
|
||||
for rect in lineRects {
|
||||
@ -308,7 +306,6 @@ public class InvisibleInkDustNode: ASDisplayNode {
|
||||
combinedRects.append(combinedRect.insetBy(dx: 0.0, dy: -1.0))
|
||||
}
|
||||
|
||||
print("combining \(CACurrentMediaTime() - start)")
|
||||
Queue.concurrentDefaultQueue().async {
|
||||
var generator = ArbitraryRandomNumberGenerator(seed: 1)
|
||||
let image = generateImage(size, rotatedContext: { size, context in
|
||||
@ -331,8 +328,6 @@ public class InvisibleInkDustNode: ASDisplayNode {
|
||||
}
|
||||
}
|
||||
self.staticNode?.frame = CGRect(origin: CGPoint(), size: size)
|
||||
|
||||
print("total draw \(CACurrentMediaTime() - start)")
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -523,7 +523,7 @@ final class MediaPickerSelectedListNode: ASDisplayNode, UIScrollViewDelegate, UI
|
||||
self.context = context
|
||||
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.scrollNode = ASScrollNode()
|
||||
|
||||
|
@ -1732,9 +1732,14 @@ public final class MediaBox {
|
||||
return
|
||||
}
|
||||
|
||||
var lastReportValue = 0
|
||||
|
||||
let reportProgress: (Int) -> Void = { count in
|
||||
Queue.mainQueue().async {
|
||||
subscriber.putNext(min(1.0, Float(count) / Float(totalCount)))
|
||||
let currentProgress = 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())
|
||||
}
|
||||
|
||||
subscriber.putNext(1.0)
|
||||
subscriber.putCompletion()
|
||||
}
|
||||
return EmptyDisposable
|
||||
|
@ -416,11 +416,19 @@ private final class PremiumGiftScreenContentComponent: CombinedComponent {
|
||||
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)?
|
||||
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)
|
||||
controller.action = {
|
||||
let controller = PremiumLimitsListScreen(context: accountContext, subject: demoSubject, source: .gift(state?.price), order: state?.configuration.perks, buttonText: buttonText, isPremium: false)
|
||||
controller.action = { [weak state] in
|
||||
dismissImpl?()
|
||||
buy()
|
||||
if let _ = state?.price {
|
||||
buy()
|
||||
}
|
||||
}
|
||||
controller.disposed = {
|
||||
// updateIsFocused(false)
|
||||
|
@ -23,11 +23,10 @@ extension ReactionsMessageAttribute {
|
||||
parsedRecentReactions = recentReactions.compactMap { recentReaction -> ReactionsMessageAttribute.RecentPeer? in
|
||||
switch recentReaction {
|
||||
case let .messagePeerReaction(flags, peerId, date, reaction):
|
||||
let _ = date
|
||||
let isLarge = (flags & (1 << 0)) != 0
|
||||
let isUnseen = (flags & (1 << 1)) != 0
|
||||
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 {
|
||||
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 }) {
|
||||
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() {
|
||||
@ -186,11 +185,10 @@ extension ReactionsMessageAttribute {
|
||||
parsedRecentReactions = recentReactions.compactMap { recentReaction -> ReactionsMessageAttribute.RecentPeer? in
|
||||
switch recentReaction {
|
||||
case let .messagePeerReaction(flags, peerId, date, reaction):
|
||||
let _ = date
|
||||
let isLarge = (flags & (1 << 0)) != 0
|
||||
let isUnseen = (flags & (1 << 1)) != 0
|
||||
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 {
|
||||
return nil
|
||||
}
|
||||
|
@ -433,7 +433,9 @@ public struct NetworkInitializationArguments {
|
||||
public let autolockDeadine: Signal<Int32?, NoError>
|
||||
public let encryptionProvider: EncryptionProvider
|
||||
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.apiHash = apiHash
|
||||
self.languagesCategory = languagesCategory
|
||||
@ -444,6 +446,7 @@ public struct NetworkInitializationArguments {
|
||||
self.autolockDeadine = autolockDeadine
|
||||
self.encryptionProvider = encryptionProvider
|
||||
self.deviceModelName = deviceModelName
|
||||
self.useBetaFeatures = useBetaFeatures
|
||||
}
|
||||
}
|
||||
#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)
|
||||
|
||||
if let networkSettings = networkSettings, networkSettings.useNetworkFramework {
|
||||
if #available(iOS 12.0, macOS 10.14, *) {
|
||||
context.makeTcpConnectionInterface = { delegate, delegateQueue in
|
||||
return NetworkFrameworkTcpConnectionInterface(delegate: delegate, delegateQueue: delegateQueue)
|
||||
if let networkSettings = networkSettings {
|
||||
let useNetworkFramework: Bool
|
||||
if let customValue = networkSettings.useNetworkFramework {
|
||||
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.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
|
||||
guard let data = data else {
|
||||
return
|
||||
@ -720,6 +734,7 @@ public final class Network: NSObject, MTRequestMessageServiceDelegate {
|
||||
let basePath: String
|
||||
private let connectionStatusDelegate: MTProtoConnectionStatusDelegate
|
||||
private let useRequestTimeoutTimers: Bool
|
||||
public let useBetaFeatures: Bool
|
||||
|
||||
private let appDataDisposable: Disposable
|
||||
|
||||
@ -763,7 +778,7 @@ public final class Network: NSObject, MTRequestMessageServiceDelegate {
|
||||
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.queue = queue
|
||||
@ -777,6 +792,7 @@ public final class Network: NSObject, MTRequestMessageServiceDelegate {
|
||||
self.appDataDisposable = appDataDisposable
|
||||
self.basePath = basePath
|
||||
self.useRequestTimeoutTimers = useRequestTimeoutTimers
|
||||
self.useBetaFeatures = useBetaFeatures
|
||||
|
||||
super.init()
|
||||
|
||||
|
@ -26,7 +26,10 @@ func applyMediaResourceChanges(from: Media, to: Media, postbox: Postbox, force:
|
||||
}
|
||||
}
|
||||
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 {
|
||||
if let fromPreview = smallestImageRepresentation(fromFile.previewRepresentations), let toPreview = smallestImageRepresentation(toFile.previewRepresentations) {
|
||||
|
@ -338,7 +338,7 @@ public extension EngineMessageReactionListContext.State {
|
||||
for recentPeer in reactionsAttribute.recentPeers {
|
||||
if let peer = message.peers[recentPeer.peerId] {
|
||||
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]))
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -4,13 +4,13 @@ public struct NetworkSettings: Codable {
|
||||
public var reducedBackupDiscoveryTimeout: Bool
|
||||
public var applicationUpdateUrlPrefix: String?
|
||||
public var backupHostOverride: String?
|
||||
public var useNetworkFramework: Bool
|
||||
public var useNetworkFramework: Bool?
|
||||
|
||||
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.applicationUpdateUrlPrefix = applicationUpdateUrlPrefix
|
||||
self.backupHostOverride = backupHostOverride
|
||||
@ -23,7 +23,7 @@ public struct NetworkSettings: Codable {
|
||||
self.reducedBackupDiscoveryTimeout = ((try? container.decode(Int32.self, forKey: "reducedBackupDiscoveryTimeout")) ?? 0) != 0
|
||||
self.applicationUpdateUrlPrefix = try? container.decodeIfPresent(String.self, forKey: "applicationUpdateUrlPrefix")
|
||||
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 {
|
||||
@ -32,6 +32,6 @@ public struct NetworkSettings: Codable {
|
||||
try container.encode((self.reducedBackupDiscoveryTimeout ? 1 : 0) as Int32, forKey: "reducedBackupDiscoveryTimeout")
|
||||
try container.encodeIfPresent(self.applicationUpdateUrlPrefix, forKey: "applicationUpdateUrlPrefix")
|
||||
try container.encodeIfPresent(self.backupHostOverride, forKey: "backupHostOverride")
|
||||
try container.encode(self.useNetworkFramework, forKey: "useNetworkFramework")
|
||||
try container.encodeIfPresent(self.useNetworkFramework, forKey: "useNetworkFramework_v2")
|
||||
}
|
||||
}
|
||||
|
@ -119,12 +119,14 @@ public final class ReactionsMessageAttribute: Equatable, MessageAttribute {
|
||||
public var isLarge: Bool
|
||||
public var isUnseen: Bool
|
||||
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.isLarge = isLarge
|
||||
self.isUnseen = isUnseen
|
||||
self.peerId = peerId
|
||||
self.timestamp = timestamp
|
||||
}
|
||||
|
||||
public init(decoder: PostboxDecoder) {
|
||||
@ -136,6 +138,7 @@ public final class ReactionsMessageAttribute: Equatable, MessageAttribute {
|
||||
self.isLarge = decoder.decodeInt32ForKey("l", orElse: 0) != 0
|
||||
self.isUnseen = decoder.decodeInt32ForKey("u", orElse: 0) != 0
|
||||
self.peerId = PeerId(decoder.decodeInt64ForKey("p", orElse: 0))
|
||||
self.timestamp = decoder.decodeOptionalInt32ForKey("ts")
|
||||
}
|
||||
|
||||
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.isUnseen ? 1 : 0, forKey: "u")
|
||||
encoder.encodeInt64(self.peerId.toInt64(), forKey: "p")
|
||||
if let timestamp = self.timestamp {
|
||||
encoder.encodeInt32(timestamp, forKey: "ts")
|
||||
} else {
|
||||
encoder.encodeNil(forKey: "ts")
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -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
|
||||
return Signal { subscriber in
|
||||
var includeResourceIds = Set<MediaResourceId>()
|
||||
@ -435,7 +435,9 @@ func _internal_clearStorage(account: Account, peerId: EnginePeer.Id?, categories
|
||||
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) {
|
||||
let additionalPaths: [String] = [
|
||||
"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
|
||||
return Signal { subscriber in
|
||||
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
|
||||
var resourceIds: [MediaResourceId] = []
|
||||
|
||||
for id in ids {
|
||||
if let value = String(data: id, encoding: .utf8) {
|
||||
resourceIds.append(MediaResourceId(value))
|
||||
}
|
||||
}
|
||||
let _ = mediaBox.removeCachedResources(resourceIds).start(completed: {
|
||||
let _ = mediaBox.removeCachedResources(resourceIds).start(next: { progress in
|
||||
subscriber.putNext(progress)
|
||||
}, completed: {
|
||||
subscriber.putCompletion()
|
||||
})
|
||||
})
|
||||
|
@ -233,11 +233,11 @@ public extension TelegramEngine {
|
||||
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)
|
||||
}
|
||||
|
||||
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)
|
||||
}
|
||||
|
||||
|
@ -1838,7 +1838,7 @@ public final class ChatEntityKeyboardInputNode: ChatInputNode {
|
||||
strongSelf.reorderItems(category: category, items: items)
|
||||
},
|
||||
makeSearchContainerNode: { [weak self, weak controllerInteraction] content in
|
||||
guard let controllerInteraction = controllerInteraction else {
|
||||
guard let self, let controllerInteraction = controllerInteraction else {
|
||||
return nil
|
||||
}
|
||||
|
||||
@ -1860,9 +1860,10 @@ public final class ChatEntityKeyboardInputNode: ChatInputNode {
|
||||
mode: mappedMode,
|
||||
trendingGifsPromise: trendingGifsPromise,
|
||||
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 {
|
||||
return
|
||||
}
|
||||
@ -2502,7 +2503,7 @@ public final class EmojiContentPeekBehaviorImpl: EmojiContentPeekBehavior {
|
||||
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 })
|
||||
|
||||
let viewRecord = self.viewRecords.first(where: { $0.view === view })
|
||||
|
@ -12,6 +12,7 @@ import EntityKeyboard
|
||||
import ChatControllerInteraction
|
||||
import MultiplexedVideoNode
|
||||
import FeaturedStickersScreen
|
||||
import StickerPeekUI
|
||||
|
||||
private let searchBarHeight: CGFloat = 52.0
|
||||
|
||||
@ -37,6 +38,7 @@ public final class PaneSearchContainerNode: ASDisplayNode, EntitySearchContainer
|
||||
public private(set) var contentNode: PaneSearchContentNode & ASDisplayNode
|
||||
private let controllerInteraction: ChatControllerInteraction
|
||||
private let inputNodeInteraction: ChatMediaInputNodeInteraction
|
||||
private let peekBehavior: EmojiContentPeekBehavior?
|
||||
|
||||
private let backgroundNode: ASDisplayNode
|
||||
private let searchBar: PaneSearchBarNode
|
||||
@ -51,11 +53,12 @@ public final class PaneSearchContainerNode: ASDisplayNode, EntitySearchContainer
|
||||
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.mode = mode
|
||||
self.controllerInteraction = controllerInteraction
|
||||
self.inputNodeInteraction = inputNodeInteraction
|
||||
self.peekBehavior = peekBehavior
|
||||
switch mode {
|
||||
case .gif:
|
||||
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)
|
||||
}
|
||||
}
|
||||
|
||||
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) {
|
||||
|
@ -32,14 +32,14 @@ public enum ChatTitleContent: Equatable {
|
||||
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 custom(String, String?, Bool)
|
||||
|
||||
public static func ==(lhs: ChatTitleContent, rhs: ChatTitleContent) -> Bool {
|
||||
switch lhs {
|
||||
case let .peer(peerView, customTitle, onlineMemberCount, isScheduledMessages, isMuted, customMessageCount):
|
||||
if case let .peer(rhsPeerView, rhsCustomTitle, rhsOnlineMemberCount, rhsIsScheduledMessages, rhsIsMuted, rhsCustomMessageCount) = rhs {
|
||||
case let .peer(peerView, customTitle, onlineMemberCount, isScheduledMessages, isMuted, customMessageCount, isEnabled):
|
||||
if case let .peer(rhsPeerView, rhsCustomTitle, rhsOnlineMemberCount, rhsIsScheduledMessages, rhsIsMuted, rhsCustomMessageCount, rhsIsEnabled) = rhs {
|
||||
if peerView !== rhsPeerView {
|
||||
return false
|
||||
}
|
||||
@ -58,7 +58,9 @@ public enum ChatTitleContent: Equatable {
|
||||
if customMessageCount != rhsCustomMessageCount {
|
||||
return false
|
||||
}
|
||||
|
||||
if isEnabled != rhsIsEnabled {
|
||||
return false
|
||||
}
|
||||
return true
|
||||
} else {
|
||||
return false
|
||||
@ -169,7 +171,7 @@ public final class ChatTitleView: UIView, NavigationBarTitleView {
|
||||
var titleCredibilityIcon: ChatTitleCredibilityIcon = .none
|
||||
var isEnabled = true
|
||||
switch titleContent {
|
||||
case let .peer(peerView, customTitle, _, isScheduledMessages, isMuted, _):
|
||||
case let .peer(peerView, customTitle, _, isScheduledMessages, isMuted, _, isEnabledValue):
|
||||
if peerView.peerId.isReplies {
|
||||
let typeText: String = self.strings.DialogList_Replies
|
||||
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):
|
||||
let textFont = titleFont
|
||||
@ -365,7 +368,7 @@ public final class ChatTitleView: UIView, NavigationBarTitleView {
|
||||
var inputActivitiesAllowed = true
|
||||
if let titleContent = self.titleContent {
|
||||
switch titleContent {
|
||||
case let .peer(peerView, _, _, isScheduledMessages, _, customMessageCount):
|
||||
case let .peer(peerView, _, _, isScheduledMessages, _, customMessageCount, _):
|
||||
if let peer = peerViewMainPeer(peerView) {
|
||||
if peer.id == self.context.account.peerId || isScheduledMessages || peer.id.isReplies {
|
||||
inputActivitiesAllowed = false
|
||||
@ -469,7 +472,7 @@ public final class ChatTitleView: UIView, NavigationBarTitleView {
|
||||
} else {
|
||||
if let titleContent = self.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 {
|
||||
let string = NSAttributedString(string: self.strings.Conversation_Messages(Int32(customMessageCount)), font: subtitleFont, textColor: titleTheme.rootController.navigationBar.secondaryTextColor)
|
||||
state = .info(string, .generic)
|
||||
|
@ -2222,7 +2222,7 @@ private final class EmptySearchResultsView: UIView {
|
||||
}
|
||||
|
||||
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 {
|
||||
|
@ -2880,7 +2880,12 @@ final class StorageUsageScreenComponent: Component {
|
||||
let totalSize = aggregatedData.selectedSize
|
||||
|
||||
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 {
|
||||
return
|
||||
}
|
||||
@ -2921,39 +2926,45 @@ final class StorageUsageScreenComponent: Component {
|
||||
self.isClearing = true
|
||||
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: [])
|
||||
|> deliverOnMainQueue).start(completed: { [weak self] in
|
||||
guard let self, let _ = self.component, let aggregatedData = self.aggregatedData else {
|
||||
|> deliverOnMainQueue).start(next: { [weak self] progress in
|
||||
guard let self else {
|
||||
return
|
||||
}
|
||||
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
|
||||
}
|
||||
self.updateClearProgress(progress: progress)
|
||||
}, completed: { [weak self] in
|
||||
guard let self else {
|
||||
return
|
||||
}
|
||||
|
||||
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)
|
||||
|> 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 {
|
||||
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) {
|
||||
guard let component = self.component else {
|
||||
return
|
||||
@ -3507,8 +3529,8 @@ private class StorageUsageClearProgressOverlayNode: ASDisplayNode {
|
||||
self.addSubnode(self.animationNode)
|
||||
self.addSubnode(self.progressTextNode)
|
||||
self.addSubnode(self.descriptionTextNode)
|
||||
//self.addSubnode(self.progressBackgroundNode)
|
||||
//self.addSubnode(self.progressForegroundNode)
|
||||
self.addSubnode(self.progressBackgroundNode)
|
||||
self.addSubnode(self.progressForegroundNode)
|
||||
}
|
||||
|
||||
deinit {
|
||||
@ -3525,7 +3547,7 @@ private class StorageUsageClearProgressOverlayNode: ASDisplayNode {
|
||||
}
|
||||
|
||||
private var progress: Float = 0.0
|
||||
private func setProgress(_ progress: Float) {
|
||||
func setProgress(_ progress: Float) {
|
||||
self.progress = progress
|
||||
|
||||
if let size = self.validLayout {
|
||||
@ -3562,8 +3584,10 @@ private class StorageUsageClearProgressOverlayNode: ASDisplayNode {
|
||||
self.descriptionTextNode.attributedText = NSAttributedString(string: self.presentationData.strings.ClearCache_KeepOpenedDescription, font: Font.regular(15.0), textColor: self.presentationData.theme.actionSheet.secondaryTextColor)
|
||||
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)
|
||||
|
||||
let progressText: String = "\(Int(self.progress * 100.0))%"
|
||||
|
||||
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)
|
||||
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)
|
||||
var progressTextFrame = CGRect(origin: CGPoint(x: floorToScreenPixels((size.width - progressTextSize.width) / 2.0), y: descriptionTextFrame.minY - spacing - progressTextSize.height), size: progressTextSize)
|
||||
|
||||
|
@ -483,7 +483,7 @@ private func extractAccountManagerState(records: AccountRecordsView<TelegramAcco
|
||||
Logger.shared.log("data", "can't deserialize")
|
||||
}
|
||||
return data
|
||||
}, autolockDeadine: autolockDeadine, encryptionProvider: OpenSSLEncryptionProvider(), deviceModelName: nil)
|
||||
}, autolockDeadine: autolockDeadine, encryptionProvider: OpenSSLEncryptionProvider(), deviceModelName: nil, useBetaFeatures: !buildConfig.isAppStoreBuild)
|
||||
|
||||
guard let appGroupUrl = maybeAppGroupUrl else {
|
||||
self.mainWindow?.presentNative(UIAlertController(title: nil, message: "Error 2", preferredStyle: .alert))
|
||||
|
@ -573,7 +573,7 @@ public final class ChatControllerImpl: TelegramBaseController, ChatController, G
|
||||
default:
|
||||
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)
|
||||
|
||||
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())
|
||||
|> deliverOnMainQueue).start(next: { [weak self] peerView, onlineMemberCount, displayedCount, subtitleText, presentationInterfaceState in
|
||||
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, hasPeerInfo in
|
||||
if let strongSelf = self {
|
||||
var isScheduledMessages = false
|
||||
if case .scheduledMessages = presentationInterfaceState.subject {
|
||||
@ -4723,7 +4732,7 @@ public final class ChatControllerImpl: TelegramBaseController, ChatController, G
|
||||
if case .pinnedMessages = presentationInterfaceState.subject {
|
||||
strongSelf.chatTitleView?.titleContent = .custom(presentationInterfaceState.strings.Chat_TitlePinnedMessages(Int32(displayedCount ?? 1)), nil, false)
|
||||
} 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?
|
||||
if strongSelf.context.account.peerId == peer.id {
|
||||
imageOverride = .savedMessagesIcon
|
||||
@ -5278,7 +5287,7 @@ public final class ChatControllerImpl: TelegramBaseController, ChatController, G
|
||||
}
|
||||
|
||||
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
|
||||
if strongSelf.chatLocation.threadId == 1 {
|
||||
|
@ -801,7 +801,7 @@ class ChatControllerNode: ASDisplayNode, UIScrollViewDelegate {
|
||||
self.emptyNode = emptyNode
|
||||
self.historyNodeContainer.supernode?.insertSubnode(emptyNode, aboveSubnode: self.historyNodeContainer)
|
||||
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 {
|
||||
emptyNode.layer.animateAlpha(from: 0.0, to: 1.0, duration: 0.2)
|
||||
@ -1622,7 +1622,7 @@ class ChatControllerNode: ASDisplayNode, UIScrollViewDelegate {
|
||||
emptyNodeInsets.bottom += inputPanelsHeight
|
||||
self.validEmptyNodeLayout = (contentBounds.size, emptyNodeInsets)
|
||||
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)
|
||||
emptyNode.update(rect: contentBounds, within: contentBounds.size, transition: transition)
|
||||
}
|
||||
|
@ -16,7 +16,7 @@ import ComponentFlow
|
||||
import EmojiStatusComponent
|
||||
|
||||
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)
|
||||
@ -36,7 +36,7 @@ private final class ChatEmptyNodeRegularChatContent: ASDisplayNode, ChatEmptyNod
|
||||
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 {
|
||||
self.currentTheme = interfaceState.theme
|
||||
self.currentStrings = interfaceState.strings
|
||||
@ -44,12 +44,16 @@ private final class ChatEmptyNodeRegularChatContent: ASDisplayNode, ChatEmptyNod
|
||||
let serviceColor = serviceMessageColorComponents(theme: interfaceState.theme, wallpaper: interfaceState.chatWallpaper)
|
||||
|
||||
let text: String
|
||||
switch interfaceState.chatLocation {
|
||||
case .peer, .replyThread, .feed:
|
||||
if case .scheduledMessages = interfaceState.subject {
|
||||
text = interfaceState.strings.ScheduledMessages_EmptyPlaceholder
|
||||
} else {
|
||||
text = interfaceState.strings.Conversation_EmptyPlaceholder
|
||||
if case .detailsPlaceholder = subject {
|
||||
text = interfaceState.strings.ChatList_StartMessaging
|
||||
} else {
|
||||
switch interfaceState.chatLocation {
|
||||
case .peer, .replyThread, .feed:
|
||||
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, [])
|
||||
}
|
||||
|
||||
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 {
|
||||
self.currentTheme = interfaceState.theme
|
||||
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, [])
|
||||
}
|
||||
|
||||
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 {
|
||||
self.currentTheme = interfaceState.theme
|
||||
self.currentStrings = interfaceState.strings
|
||||
@ -442,7 +446,7 @@ private final class ChatEmptyNodeSecretChatContent: ASDisplayNode, ChatEmptyNode
|
||||
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 {
|
||||
self.currentTheme = interfaceState.theme
|
||||
self.currentStrings = interfaceState.strings
|
||||
@ -576,7 +580,7 @@ private final class ChatEmptyNodeGroupChatContent: ASDisplayNode, ChatEmptyNodeC
|
||||
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 {
|
||||
self.currentTheme = interfaceState.theme
|
||||
self.currentStrings = interfaceState.strings
|
||||
@ -691,7 +695,7 @@ private final class ChatEmptyNodeCloudChatContent: ASDisplayNode, ChatEmptyNodeC
|
||||
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 {
|
||||
self.currentTheme = interfaceState.theme
|
||||
self.currentStrings = interfaceState.strings
|
||||
@ -813,7 +817,7 @@ final class ChatEmptyNodeTopicChatContent: ASDisplayNode, ChatEmptyNodeContent,
|
||||
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)
|
||||
if self.currentTheme !== interfaceState.theme || self.currentStrings !== interfaceState.strings {
|
||||
self.currentTheme = interfaceState.theme
|
||||
@ -900,6 +904,10 @@ private enum ChatEmptyNodeContentType: Equatable {
|
||||
}
|
||||
|
||||
final class ChatEmptyNode: ASDisplayNode {
|
||||
enum Subject {
|
||||
case emptyChat(ChatHistoryNodeLoadState.EmptyType)
|
||||
case detailsPlaceholder
|
||||
}
|
||||
private let context: AccountContext
|
||||
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
|
||||
|
||||
if self.currentTheme !== interfaceState.theme || self.currentStrings !== interfaceState.strings {
|
||||
@ -969,38 +977,43 @@ final class ChatEmptyNode: ASDisplayNode {
|
||||
}
|
||||
|
||||
let contentType: ChatEmptyNodeContentType
|
||||
if case .replyThread = interfaceState.chatLocation {
|
||||
if case .topic = emptyType {
|
||||
contentType = .topic
|
||||
} else {
|
||||
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
|
||||
switch subject {
|
||||
case .detailsPlaceholder:
|
||||
contentType = .regular
|
||||
case let .emptyChat(emptyType):
|
||||
if case .replyThread = interfaceState.chatLocation {
|
||||
if case .topic = emptyType {
|
||||
contentType = .topic
|
||||
} 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 {
|
||||
contentType = .regular
|
||||
}
|
||||
} else {
|
||||
contentType = .regular
|
||||
}
|
||||
|
||||
|
||||
var updateGreetingSticker = false
|
||||
var contentTransition = transition
|
||||
if self.content?.0 != contentType {
|
||||
@ -1044,7 +1057,7 @@ final class ChatEmptyNode: ASDisplayNode {
|
||||
|
||||
var contentSize = CGSize()
|
||||
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 {
|
||||
self.context.prefetchManager?.prepareNextGreetingSticker()
|
||||
|
@ -1531,7 +1531,7 @@ private class QrContentNode: ASDisplayNode, ContentNode {
|
||||
|
||||
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.backgroundColor = .white
|
||||
@ -2022,7 +2022,7 @@ private class MessageContentNode: ASDisplayNode, ContentNode {
|
||||
|
||||
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.backgroundImageNode = ASImageNode()
|
||||
|
@ -497,11 +497,13 @@ public final class MediaManagerImpl: NSObject, MediaManager {
|
||||
strongSelf.voiceMediaPlayer?.stop()
|
||||
if let (account, playlist, settings, storedState) = inputData {
|
||||
var continueInstantVideoLoopAfterFinish: Bool = true
|
||||
var controlPlaybackWithProximity: Bool = true
|
||||
if let playlist = playlist as? PeerMessagesMediaPlaylist {
|
||||
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
|
||||
voiceMediaPlayer.playedToEnd = { [weak voiceMediaPlayer] in
|
||||
if let strongSelf = self, let voiceMediaPlayer = voiceMediaPlayer, voiceMediaPlayer === strongSelf.voiceMediaPlayer {
|
||||
|
@ -41,8 +41,9 @@ public struct NotificationViewControllerInitializationData {
|
||||
public let encryptionParameters: (Data, Data)
|
||||
public let appVersion: String
|
||||
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.appBuildType = appBuildType
|
||||
self.appGroupPath = appGroupPath
|
||||
@ -52,6 +53,7 @@ public struct NotificationViewControllerInitializationData {
|
||||
self.encryptionParameters = encryptionParameters
|
||||
self.appVersion = appVersion
|
||||
self.bundleData = bundleData
|
||||
self.useBetaFeatures = useBetaFeatures
|
||||
}
|
||||
}
|
||||
|
||||
@ -138,7 +140,7 @@ public final class NotificationViewControllerImpl {
|
||||
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)
|
||||
}
|
||||
|
@ -257,6 +257,18 @@ private enum PeerInfoScreenInputData: Equatable {
|
||||
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> {
|
||||
let tags: [(MessageTags, PeerInfoPaneKey)] = [
|
||||
(.photoOrVideo, .media),
|
||||
|
@ -66,8 +66,9 @@ public struct ShareRootControllerInitializationData {
|
||||
public let encryptionParameters: (Data, Data)
|
||||
public let appVersion: String
|
||||
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.appBuildType = appBuildType
|
||||
self.appGroupPath = appGroupPath
|
||||
@ -77,6 +78,7 @@ public struct ShareRootControllerInitializationData {
|
||||
self.encryptionParameters = encryptionParameters
|
||||
self.appVersion = appVersion
|
||||
self.bundleData = bundleData
|
||||
self.useBetaFeatures = useBetaFeatures
|
||||
}
|
||||
}
|
||||
|
||||
@ -239,7 +241,7 @@ public class ShareRootControllerImpl {
|
||||
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)
|
||||
internalContext = InternalContext(sharedContext: sharedContext)
|
||||
globalInternalContext = internalContext
|
||||
|
@ -15,6 +15,46 @@ import AppBundle
|
||||
import DatePickerNode
|
||||
import DebugSettingsUI
|
||||
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 {
|
||||
private let context: AccountContext
|
||||
@ -30,6 +70,8 @@ public final class TelegramRootController: NavigationController {
|
||||
private var presentationDataDisposable: Disposable?
|
||||
private var presentationData: PresentationData
|
||||
|
||||
private var detailsPlaceholderNode: DetailsChatPlaceholderNode?
|
||||
|
||||
private var applicationInFocusDisposable: Disposable?
|
||||
|
||||
public init(context: AccountContext) {
|
||||
@ -37,33 +79,13 @@ public final class TelegramRootController: NavigationController {
|
||||
|
||||
self.presentationData = context.sharedContext.currentPresentationData.with { $0 }
|
||||
|
||||
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:
|
||||
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)
|
||||
super.init(mode: .automaticMasterDetail, theme: NavigationControllerTheme(presentationTheme: self.presentationData.theme))
|
||||
|
||||
self.presentationDataDisposable = (context.sharedContext.presentationData
|
||||
|> deliverOnMainQueue).start(next: { [weak self] presentationData in
|
||||
if let strongSelf = self {
|
||||
if presentationData.chatWallpaper != strongSelf.presentationData.chatWallpaper {
|
||||
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)
|
||||
}
|
||||
|
||||
strongSelf.detailsPlaceholderNode?.updatePresentationData(presentationData)
|
||||
|
||||
let previousTheme = strongSelf.presentationData.theme
|
||||
strongSelf.presentationData = presentationData
|
||||
if previousTheme !== presentationData.theme {
|
||||
@ -92,6 +114,32 @@ public final class TelegramRootController: NavigationController {
|
||||
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) {
|
||||
let tabBarController = TabBarControllerImpl(navigationBarPresentationData: NavigationBarPresentationData(presentationData: self.presentationData), theme: TabBarControllerTheme(rootControllerTheme: self.presentationData.theme))
|
||||
tabBarController.navigationPresentation = .master
|
||||
|
@ -40,7 +40,6 @@ public struct ExperimentalUISettings: Codable, Equatable {
|
||||
public var experimentalCompatibility: Bool
|
||||
public var enableDebugDataDisplay: Bool
|
||||
public var acceleratedStickers: Bool
|
||||
public var experimentalBackground: Bool
|
||||
public var inlineStickers: Bool
|
||||
public var localTranscription: Bool
|
||||
public var enableReactionOverrides: Bool
|
||||
@ -69,7 +68,6 @@ public struct ExperimentalUISettings: Codable, Equatable {
|
||||
experimentalCompatibility: false,
|
||||
enableDebugDataDisplay: false,
|
||||
acceleratedStickers: false,
|
||||
experimentalBackground: false,
|
||||
inlineStickers: false,
|
||||
localTranscription: false,
|
||||
enableReactionOverrides: false,
|
||||
@ -99,7 +97,6 @@ public struct ExperimentalUISettings: Codable, Equatable {
|
||||
experimentalCompatibility: Bool,
|
||||
enableDebugDataDisplay: Bool,
|
||||
acceleratedStickers: Bool,
|
||||
experimentalBackground: Bool,
|
||||
inlineStickers: Bool,
|
||||
localTranscription: Bool,
|
||||
enableReactionOverrides: Bool,
|
||||
@ -126,7 +123,6 @@ public struct ExperimentalUISettings: Codable, Equatable {
|
||||
self.experimentalCompatibility = experimentalCompatibility
|
||||
self.enableDebugDataDisplay = enableDebugDataDisplay
|
||||
self.acceleratedStickers = acceleratedStickers
|
||||
self.experimentalBackground = experimentalBackground
|
||||
self.inlineStickers = inlineStickers
|
||||
self.localTranscription = localTranscription
|
||||
self.enableReactionOverrides = enableReactionOverrides
|
||||
@ -157,7 +153,6 @@ public struct ExperimentalUISettings: Codable, Equatable {
|
||||
self.experimentalCompatibility = (try container.decodeIfPresent(Int32.self, forKey: "experimentalCompatibility") ?? 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.experimentalBackground = (try container.decodeIfPresent(Int32.self, forKey: "experimentalBackground") ?? 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.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.enableDebugDataDisplay ? 1 : 0) as Int32, forKey: "enableDebugDataDisplay")
|
||||
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.localTranscription ? 1 : 0) as Int32, forKey: "localTranscription")
|
||||
try container.encode(self.enableReactionOverrides, forKey: "enableReactionOverrides")
|
||||
|
@ -282,8 +282,8 @@ public struct EnergyUsageSettings: Codable, Equatable {
|
||||
autoplayVideo: true,
|
||||
autoplayGif: true,
|
||||
loopStickers: true,
|
||||
loopEmoji: isCapable ? false : true,
|
||||
fullTranslucency: isCapable ? false : true,
|
||||
loopEmoji: isCapable,
|
||||
fullTranslucency: isCapable,
|
||||
extendBackgroundWork: true,
|
||||
autodownloadInBackground: true
|
||||
)
|
||||
|
@ -1 +1 @@
|
||||
Subproject commit af88317c7529c5b69c4d574d1293fb385abb3f02
|
||||
Subproject commit 35d5d408cee8c69b61a32bc96dbfccc37980a5ae
|
@ -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
|
||||
}
|
||||
}
|
||||
|
File diff suppressed because it is too large
Load Diff
Loading…
x
Reference in New Issue
Block a user