Merge commit 'ed86e369ebda242bb9cf0f38df3821586bd7d307' into macos-9.5-release

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

View File

@ -38,7 +38,7 @@ class NotificationViewController: UIViewController, UNNotificationContentExtensi
let appVersion = (Bundle.main.infoDictionary?["CFBundleShortVersionString"] as? String) ?? "unknown"
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
})
}

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

@ -26,7 +26,10 @@ func applyMediaResourceChanges(from: Media, to: Media, postbox: Postbox, force:
}
}
if let fromLargestRepresentation = largestImageRepresentation(fromImage.representations), let toLargestRepresentation = largestImageRepresentation(toImage.representations) {
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) {

View File

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

View File

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

View File

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

View File

@ -379,7 +379,7 @@ func _internal_renderStorageUsageStatsMessages(account: Account, stats: StorageU
}
}
func _internal_clearStorage(account: Account, peerId: EnginePeer.Id?, categories: [StorageUsageStats.CategoryKey], includeMessages: [Message], excludeMessages: [Message]) -> Signal<Never, NoError> {
func _internal_clearStorage(account: Account, peerId: EnginePeer.Id?, categories: [StorageUsageStats.CategoryKey], includeMessages: [Message], excludeMessages: [Message]) -> Signal<Float, NoError> {
let mediaBox = account.postbox.mediaBox
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()
})
})

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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