mirror of
https://github.com/Swiftgram/Telegram-iOS.git
synced 2025-06-16 05:55:20 +00:00
Merge branch 'master' of gitlab.com:peter-iakovlev/telegram-ios
This commit is contained in:
commit
4dfa591968
@ -64,7 +64,7 @@ private func rootPathForBasePath(_ appGroupPath: String) -> String {
|
||||
|
||||
let rootPath = rootPathForBasePath(appGroupUrl.path)
|
||||
|
||||
let logsPath = rootPath + "/broadcast-logs"
|
||||
let logsPath = rootPath + "/logs/broadcast-logs"
|
||||
let _ = try? FileManager.default.createDirectory(atPath: logsPath, withIntermediateDirectories: true, attributes: nil)
|
||||
|
||||
let screencastBufferClientContext = IpcGroupCallBufferBroadcastContext(basePath: rootPath + "/broadcast-coordination")
|
||||
|
@ -639,7 +639,7 @@ private final class NotificationServiceHandler {
|
||||
|
||||
TempBox.initializeShared(basePath: rootPath, processType: "notification", launchSpecificId: Int64.random(in: Int64.min ... Int64.max))
|
||||
|
||||
let logsPath = rootPath + "/notification-logs"
|
||||
let logsPath = rootPath + "/logs/notification-logs"
|
||||
let _ = try? FileManager.default.createDirectory(atPath: logsPath, withIntermediateDirectories: true, attributes: nil)
|
||||
|
||||
setupSharedLogger(rootPath: rootPath, path: logsPath)
|
||||
@ -673,7 +673,7 @@ private final class NotificationServiceHandler {
|
||||
incomingCallMessage = "is calling you"
|
||||
}
|
||||
|
||||
Logger.shared.log("NotificationService \(episode)", "Begin processing payload \(payload)")
|
||||
Logger.shared.log("NotificationService \(episode)", "Begin processing payload")
|
||||
|
||||
guard var encryptedPayload = payload["p"] as? String else {
|
||||
Logger.shared.log("NotificationService \(episode)", "Invalid payload 1")
|
||||
@ -691,13 +691,16 @@ private final class NotificationServiceHandler {
|
||||
|
||||
let _ = (combineLatest(queue: self.queue,
|
||||
self.accountManager.accountRecords(),
|
||||
self.accountManager.sharedData(keys: [ApplicationSpecificSharedDataKeys.inAppNotificationSettings, ApplicationSpecificSharedDataKeys.voiceCallSettings])
|
||||
self.accountManager.sharedData(keys: [ApplicationSpecificSharedDataKeys.inAppNotificationSettings, ApplicationSpecificSharedDataKeys.voiceCallSettings, SharedDataKeys.loggingSettings])
|
||||
)
|
||||
|> take(1)
|
||||
|> deliverOn(self.queue)).start(next: { [weak self] records, sharedData in
|
||||
var recordId: AccountRecordId?
|
||||
var isCurrentAccount: Bool = false
|
||||
var customSoundPath: String?
|
||||
|
||||
let loggingSettings = sharedData.entries[SharedDataKeys.loggingSettings]?.get(LoggingSettings.self) ?? LoggingSettings.defaultSettings
|
||||
Logger.shared.logToFile = loggingSettings.logToFile
|
||||
Logger.shared.logToConsole = loggingSettings.logToConsole
|
||||
|
||||
if let keyId = notificationPayloadKeyId(data: payloadData) {
|
||||
outer: for listRecord in records.records {
|
||||
@ -717,8 +720,6 @@ private final class NotificationServiceHandler {
|
||||
|
||||
let inAppNotificationSettings = sharedData.entries[ApplicationSpecificSharedDataKeys.inAppNotificationSettings]?.get(InAppNotificationSettings.self) ?? InAppNotificationSettings.defaultSettings
|
||||
|
||||
customSoundPath = inAppNotificationSettings.customSound
|
||||
|
||||
let voiceCallSettings: VoiceCallSettings
|
||||
if let value = sharedData.entries[ApplicationSpecificSharedDataKeys.voiceCallSettings]?.get(VoiceCallSettings.self) {
|
||||
voiceCallSettings = value
|
||||
|
@ -113,7 +113,7 @@ class DefaultIntentHandler: INExtension, INSendMessageIntentHandling, INSearchFo
|
||||
|
||||
TempBox.initializeShared(basePath: rootPath, processType: "siri", launchSpecificId: Int64.random(in: Int64.min ... Int64.max))
|
||||
|
||||
let logsPath = rootPath + "/siri-logs"
|
||||
let logsPath = rootPath + "/logs/siri-logs"
|
||||
let _ = try? FileManager.default.createDirectory(atPath: logsPath, withIntermediateDirectories: true, attributes: nil)
|
||||
|
||||
setupSharedLogger(rootPath: rootPath, path: logsPath)
|
||||
@ -880,7 +880,7 @@ private final class WidgetIntentHandler {
|
||||
|
||||
TempBox.initializeShared(basePath: rootPath, processType: "siri", launchSpecificId: Int64.random(in: Int64.min ... Int64.max))
|
||||
|
||||
let logsPath = rootPath + "/siri-logs"
|
||||
let logsPath = rootPath + "/logs/siri-logs"
|
||||
let _ = try? FileManager.default.createDirectory(atPath: logsPath, withIntermediateDirectories: true, attributes: nil)
|
||||
|
||||
setupSharedLogger(rootPath: rootPath, path: logsPath)
|
||||
|
@ -388,7 +388,7 @@
|
||||
"Tour.Text5" = "**Telegram** lets you access your\nmessages from multiple devices.";
|
||||
|
||||
"Tour.Title6" = "Free";
|
||||
"Tour.Text6" = "**Telegram** isprovides free unlimited cloud storage\nfor chats and media.";
|
||||
"Tour.Text6" = "**Telegram** provides free unlimited cloud storage\nfor chats and media.";
|
||||
|
||||
"Tour.StartButton" = "Start Messaging";
|
||||
|
||||
@ -7550,12 +7550,15 @@ Sorry for the inconvenience.";
|
||||
|
||||
"Premium.AppIcons.Proceed" = "Unlock Premium Icons";
|
||||
|
||||
"Premium.NoAds.Proceed" = "About Telegram Premium";
|
||||
|
||||
"AccessDenied.LocationPreciseDenied" = "To share your specific location in this chat, please go to Settings > Privacy > Location Services > Telegram and set Precise Location to On.";
|
||||
|
||||
"Chat.MultipleTypingPair" = "%@ and %@";
|
||||
"Chat.MultipleTypingMore" = "%@ and %@ others";
|
||||
|
||||
"Group.Username.RemoveExistingUsernamesOrExtendInfo" = "You have reserved too many public links. Try revoking a link from an older group or channel, or upgrade to **Telegram Premium** to double the limit to **%@** public links.";
|
||||
"Group.Username.RemoveExistingUsernamesNoPremiumInfo" = "You have reserved too many public links. Try revoking the link from an older group or channel. We are working to let you increase this limit in the future.";
|
||||
"Group.Username.RemoveExistingUsernamesFinalInfo" = "You have reserved too many public links. Try revoking the link from an older group or channel, or create a private one instead.";
|
||||
|
||||
"OldChannels.TooManyCommunitiesText" = "You are a member of **%@** groups and channels. Please leave some before joining a new one or upgrade to **Telegram Premium** to double the limit to **%@** groups and channels.";
|
||||
@ -7623,11 +7626,13 @@ Sorry for the inconvenience.";
|
||||
"Premium.NoAds" = "No Ads";
|
||||
"Premium.NoAdsInfo" = "No more ads in public channels where Telegram sometimes shows ads.";
|
||||
|
||||
"Premium.NoAdsStandaloneInfo" = "Remove ads such as this by subscribing to **Telegram Premium**.";
|
||||
|
||||
"Premium.Reactions" = "Unique Reactions";
|
||||
"Premium.ReactionsInfo" = "Additional animated reactions on messages, available only to the Premium subscribers.";
|
||||
|
||||
"Premium.ReactionsStandalone" = "Additional Reactions";
|
||||
"Premium.ReactionsStandaloneInfo" = "Unlock a wider range of reactions by subscribing to **Telegram Premium**.";
|
||||
"Premium.ReactionsStandaloneInfo" = "Unlock a wider range of reactions on messages by subscribing to **Telegram Premium**.";
|
||||
|
||||
"Premium.Stickers" = "Premium Stickers";
|
||||
"Premium.StickersInfo" = "Exclusive enlarged stickers featuring additional effects, updated monthly.";
|
||||
|
@ -91,7 +91,7 @@ private func getCommonTimeline(friends: [Friend]?, in context: TimelineProviderC
|
||||
|
||||
TempBox.initializeShared(basePath: rootPath, processType: "widget", launchSpecificId: Int64.random(in: Int64.min ... Int64.max))
|
||||
|
||||
let logsPath = rootPath + "/widget-logs"
|
||||
let logsPath = rootPath + "/logs/widget-logs"
|
||||
let _ = try? FileManager.default.createDirectory(atPath: logsPath, withIntermediateDirectories: true, attributes: nil)
|
||||
|
||||
setupSharedLogger(rootPath: rootPath, path: logsPath)
|
||||
|
@ -147,7 +147,41 @@ public protocol AnimatedStickerNodeSource {
|
||||
func directDataPath(attemptSynchronously: Bool) -> Signal<String?, NoError>
|
||||
}
|
||||
|
||||
public final class AnimatedStickerNode: ASDisplayNode {
|
||||
public protocol AnimatedStickerNode: ASDisplayNode {
|
||||
var automaticallyLoadFirstFrame: Bool { get set }
|
||||
var automaticallyLoadLastFrame: Bool { get set }
|
||||
var playToCompletionOnStop: Bool { get set }
|
||||
var started: () -> Void { get set }
|
||||
|
||||
var completed: (Bool) -> Void { get set }
|
||||
var frameUpdated: (Int, Int) -> Void { get set }
|
||||
var currentFrameIndex: Int { get }
|
||||
var currentFrameCount: Int { get }
|
||||
var isPlaying: Bool { get }
|
||||
var stopAtNearestLoop: Bool { get set }
|
||||
|
||||
var status: Signal<AnimatedStickerStatus, NoError> { get }
|
||||
|
||||
var autoplay: Bool { get set }
|
||||
|
||||
var visibility: Bool { get set }
|
||||
|
||||
var isPlayingChanged: (Bool) -> Void { get }
|
||||
|
||||
func cloneCurrentFrame(from otherNode: AnimatedStickerNode?)
|
||||
func setup(source: AnimatedStickerNodeSource, width: Int, height: Int, playbackMode: AnimatedStickerPlaybackMode, mode: AnimatedStickerMode)
|
||||
func reset()
|
||||
func playOnce()
|
||||
func play(firstFrame: Bool, fromIndex: Int?)
|
||||
func pause()
|
||||
func stop()
|
||||
func seekTo(_ position: AnimatedStickerPlaybackPosition)
|
||||
func playIfNeeded() -> Bool
|
||||
func updateLayout(size: CGSize)
|
||||
func setOverlayColor(_ color: UIColor?, replace: Bool, animated: Bool)
|
||||
}
|
||||
|
||||
public final class DefaultAnimatedStickerNodeImpl: ASDisplayNode, AnimatedStickerNode {
|
||||
private let queue: Queue
|
||||
private let disposable = MetaDisposable()
|
||||
private let fetchDisposable = MetaDisposable()
|
||||
@ -245,14 +279,14 @@ public final class AnimatedStickerNode: ASDisplayNode {
|
||||
return SoftwareAnimationRenderer()
|
||||
})
|
||||
|
||||
private weak var nodeToCopyFrameFrom: AnimatedStickerNode?
|
||||
private weak var nodeToCopyFrameFrom: DefaultAnimatedStickerNodeImpl?
|
||||
override public func didLoad() {
|
||||
super.didLoad()
|
||||
|
||||
if #available(iOS 10.0, *), (self.useMetalCache/* || "".isEmpty*/) {
|
||||
self.renderer = AnimatedStickerNode.hardwareRendererPool.take()
|
||||
self.renderer = DefaultAnimatedStickerNodeImpl.hardwareRendererPool.take()
|
||||
} else {
|
||||
self.renderer = AnimatedStickerNode.softwareRendererPool.take()
|
||||
self.renderer = DefaultAnimatedStickerNodeImpl.softwareRendererPool.take()
|
||||
if let contents = self.nodeToCopyFrameFrom?.renderer?.renderer.contents {
|
||||
self.renderer?.renderer.contents = contents
|
||||
}
|
||||
@ -267,7 +301,12 @@ public final class AnimatedStickerNode: ASDisplayNode {
|
||||
}
|
||||
|
||||
public func cloneCurrentFrame(from otherNode: AnimatedStickerNode?) {
|
||||
if let renderer = self.renderer?.renderer as? SoftwareAnimationRenderer, let otherRenderer = otherNode?.renderer?.renderer as? SoftwareAnimationRenderer {
|
||||
guard let otherNode = otherNode as? DefaultAnimatedStickerNodeImpl else {
|
||||
self.nodeToCopyFrameFrom = nil
|
||||
return
|
||||
}
|
||||
|
||||
if let renderer = self.renderer?.renderer as? SoftwareAnimationRenderer, let otherRenderer = otherNode.renderer?.renderer as? SoftwareAnimationRenderer {
|
||||
if let contents = otherRenderer.contents {
|
||||
renderer.contents = contents
|
||||
}
|
||||
|
@ -250,7 +250,7 @@ final class CallListControllerNode: ASDisplayNode {
|
||||
self.emptyTextNode.textAlignment = .center
|
||||
self.emptyTextNode.maximumNumberOfLines = 3
|
||||
|
||||
self.emptyAnimationNode = AnimatedStickerNode()
|
||||
self.emptyAnimationNode = DefaultAnimatedStickerNodeImpl()
|
||||
self.emptyAnimationNode.alpha = 0.0
|
||||
self.emptyAnimationNode.isUserInteractionEnabled = false
|
||||
|
||||
|
@ -337,8 +337,8 @@ public final class ChatImportActivityScreen: ViewController {
|
||||
|
||||
self.presentationData = self.context.sharedContext.currentPresentationData.with { $0 }
|
||||
|
||||
self.animationNode = AnimatedStickerNode()
|
||||
self.doneAnimationNode = AnimatedStickerNode()
|
||||
self.animationNode = DefaultAnimatedStickerNodeImpl()
|
||||
self.doneAnimationNode = DefaultAnimatedStickerNodeImpl()
|
||||
self.doneAnimationNode.isHidden = true
|
||||
|
||||
self.radialStatus = RadialStatusNode(backgroundNodeColor: .clear)
|
||||
|
@ -84,7 +84,7 @@ class ChatListFilterSettingsHeaderItemNode: ListViewItemNode {
|
||||
self.titleNode.contentMode = .left
|
||||
self.titleNode.contentsScale = UIScreen.main.scale
|
||||
|
||||
self.animationNode = AnimatedStickerNode()
|
||||
self.animationNode = DefaultAnimatedStickerNodeImpl()
|
||||
|
||||
super.init(layerBacked: false, dynamicBounce: false)
|
||||
|
||||
@ -101,7 +101,7 @@ class ChatListFilterSettingsHeaderItemNode: ListViewItemNode {
|
||||
@objc private func animationTapGesture(_ recognizer: UITapGestureRecognizer) {
|
||||
if case .ended = recognizer.state {
|
||||
if !self.animationNode.isPlaying {
|
||||
self.animationNode.play()
|
||||
self.animationNode.play(firstFrame: false, fromIndex: nil)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -31,7 +31,7 @@ final class ChatListEmptyNode: ASDisplayNode {
|
||||
self.isFilter = isFilter
|
||||
self.isLoading = isLoading
|
||||
|
||||
self.animationNode = AnimatedStickerNode()
|
||||
self.animationNode = DefaultAnimatedStickerNodeImpl()
|
||||
|
||||
self.textNode = ImmediateTextNode()
|
||||
self.textNode.displaysAsynchronously = false
|
||||
@ -107,13 +107,13 @@ final class ChatListEmptyNode: ASDisplayNode {
|
||||
@objc private func animationTapGesture(_ recognizer: UITapGestureRecognizer) {
|
||||
if case .ended = recognizer.state {
|
||||
if !self.animationNode.isPlaying {
|
||||
self.animationNode.play()
|
||||
self.animationNode.play(firstFrame: false, fromIndex: nil)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func restartAnimation() {
|
||||
self.animationNode.play()
|
||||
self.animationNode.play(firstFrame: false, fromIndex: nil)
|
||||
}
|
||||
|
||||
func updateThemeAndStrings(theme: PresentationTheme, strings: PresentationStrings) {
|
||||
|
@ -1467,7 +1467,7 @@ final class ActionSheetAnimationAndTextItemNode: ActionSheetItemNode {
|
||||
self.theme = theme
|
||||
self.defaultFont = Font.regular(floor(theme.baseFontSize * 13.0 / 17.0))
|
||||
|
||||
self.animationNode = AnimatedStickerNode()
|
||||
self.animationNode = DefaultAnimatedStickerNodeImpl()
|
||||
self.animationNode.setup(source: AnimatedStickerNodeLocalFileSource(name: "ClearDownloadList"), width: 256, height: 256, playbackMode: .loop, mode: .direct(cachePathPrefix: nil))
|
||||
self.animationNode.visibility = true
|
||||
|
||||
|
@ -971,7 +971,7 @@ final class ChatListSearchListPaneNode: ASDisplayNode, ChatListSearchPaneNode {
|
||||
self.emptyResultsTextNode.textAlignment = .center
|
||||
self.emptyResultsTextNode.isHidden = true
|
||||
|
||||
self.emptyResultsAnimationNode = AnimatedStickerNode()
|
||||
self.emptyResultsAnimationNode = DefaultAnimatedStickerNodeImpl()
|
||||
self.emptyResultsAnimationNode.isHidden = true
|
||||
|
||||
super.init()
|
||||
|
@ -335,8 +335,12 @@ public final class ChatMessageShadowNode: ASDisplayNode {
|
||||
self.contentNode.image = shadowImage
|
||||
}
|
||||
|
||||
public func updateLayout(backgroundFrame: CGRect, animator: ControlledTransitionAnimator) {
|
||||
animator.updateFrame(layer: self.contentNode.layer, frame: CGRect(origin: CGPoint(x: backgroundFrame.minX - 10.0, y: backgroundFrame.minY - 10.0), size: CGSize(width: backgroundFrame.width + 20.0, height: backgroundFrame.height + 20.0)), completion: nil)
|
||||
}
|
||||
|
||||
public func updateLayout(backgroundFrame: CGRect, transition: ContainedViewLayoutTransition) {
|
||||
transition.updateFrame(node: self.contentNode, frame: CGRect(origin: CGPoint(x: backgroundFrame.minX - 10.0, y: backgroundFrame.minY - 10.0), size: CGSize(width: backgroundFrame.width + 20.0, height: backgroundFrame.height + 20.0)))
|
||||
transition.updateFrame(layer: self.contentNode.layer, frame: CGRect(origin: CGPoint(x: backgroundFrame.minX - 10.0, y: backgroundFrame.minY - 10.0), size: CGSize(width: backgroundFrame.width + 20.0, height: backgroundFrame.height + 20.0)), completion: nil)
|
||||
}
|
||||
}
|
||||
|
||||
@ -552,7 +556,7 @@ public final class ChatMessageBubbleBackdrop: ASDisplayNode {
|
||||
var backgroundFrame = backgroundContent.frame
|
||||
backgroundFrame.origin.x += rect.minX
|
||||
backgroundFrame.origin.y += rect.minY
|
||||
backgroundContent.update(rect: backgroundFrame, within: containerSize, transition: .animated(duration: animator.duration, curve: .spring))
|
||||
backgroundContent.update(rect: backgroundFrame, within: containerSize, animator: animator)
|
||||
}
|
||||
}
|
||||
animator.updateFrame(layer: self.layer, frame: value, completion: { _ in
|
||||
|
@ -95,7 +95,7 @@ public final class AnimatedStickerComponent: Component {
|
||||
if self.component?.animation != component.animation {
|
||||
self.animationNode?.view.removeFromSuperview()
|
||||
|
||||
let animationNode = AnimatedStickerNode()
|
||||
let animationNode = DefaultAnimatedStickerNodeImpl()
|
||||
let source: AnimatedStickerNodeSource
|
||||
switch component.animation.source {
|
||||
case let .bundle(name):
|
||||
|
@ -370,18 +370,17 @@ public final class ReactionListContextMenuContent: ContextControllerItemsContent
|
||||
}
|
||||
|
||||
let premiumConfiguration = PremiumConfiguration.with(appConfiguration: self.context.currentAppConfiguration.with { $0 })
|
||||
var currentCredibilityIconImage: UIImage?
|
||||
if item.peer.id != self.context.account.peerId {
|
||||
if item.peer.isScam {
|
||||
currentCredibilityIconImage = PresentationResourcesChatList.scamIcon(presentationData.theme, strings: presentationData.strings, type: .regular)
|
||||
} else if item.peer.isFake {
|
||||
currentCredibilityIconImage = PresentationResourcesChatList.fakeIcon(presentationData.theme, strings: presentationData.strings, type: .regular)
|
||||
} else if item.peer.isVerified {
|
||||
currentCredibilityIconImage = PresentationResourcesChatList.verifiedIcon(presentationData.theme)
|
||||
} else if item.peer.isPremium && !premiumConfiguration.isPremiumDisabled {
|
||||
currentCredibilityIconImage = PresentationResourcesChatList.premiumIcon(presentationData.theme)
|
||||
}
|
||||
var currentCredibilityIconImage: UIImage?
|
||||
if item.peer.isScam {
|
||||
currentCredibilityIconImage = PresentationResourcesChatList.scamIcon(presentationData.theme, strings: presentationData.strings, type: .regular)
|
||||
} else if item.peer.isFake {
|
||||
currentCredibilityIconImage = PresentationResourcesChatList.fakeIcon(presentationData.theme, strings: presentationData.strings, type: .regular)
|
||||
} else if item.peer.isVerified {
|
||||
currentCredibilityIconImage = PresentationResourcesChatList.verifiedIcon(presentationData.theme)
|
||||
} else if item.peer.isPremium && !premiumConfiguration.isPremiumDisabled {
|
||||
currentCredibilityIconImage = PresentationResourcesChatList.premiumIcon(presentationData.theme)
|
||||
}
|
||||
|
||||
var additionalTitleInset: CGFloat = 0.0
|
||||
if let currentCredibilityIconImage = currentCredibilityIconImage {
|
||||
additionalTitleInset += 3.0 + currentCredibilityIconImage.size.width
|
||||
|
@ -261,7 +261,7 @@ public final class ContactsSearchContainerNode: SearchDisplayControllerContentNo
|
||||
self.emptyResultsTextNode.textAlignment = .center
|
||||
self.emptyResultsTextNode.isHidden = true
|
||||
|
||||
self.emptyResultsAnimationNode = AnimatedStickerNode()
|
||||
self.emptyResultsAnimationNode = DefaultAnimatedStickerNodeImpl()
|
||||
self.emptyResultsAnimationNode.isHidden = true
|
||||
|
||||
super.init()
|
||||
|
@ -61,6 +61,7 @@ private enum DebugControllerEntry: ItemListNodeEntry {
|
||||
case sendGroupCallLogs
|
||||
case sendNotificationLogs(PresentationTheme)
|
||||
case sendCriticalLogs(PresentationTheme)
|
||||
case sendAllLogs
|
||||
case accounts(PresentationTheme)
|
||||
case logToFile(PresentationTheme, Bool)
|
||||
case logToConsole(PresentationTheme, Bool)
|
||||
@ -94,7 +95,7 @@ private enum DebugControllerEntry: ItemListNodeEntry {
|
||||
case preferredVideoCodec(Int, String, String?, Bool)
|
||||
case disableVideoAspectScaling(Bool)
|
||||
case enableVoipTcp(Bool)
|
||||
case resetInAppPurchases(PresentationTheme)
|
||||
case restorePurchases(PresentationTheme)
|
||||
case hostInfo(PresentationTheme, String)
|
||||
case versionInfo(PresentationTheme)
|
||||
|
||||
@ -102,7 +103,7 @@ private enum DebugControllerEntry: ItemListNodeEntry {
|
||||
switch self {
|
||||
case .testStickerImport:
|
||||
return DebugControllerSection.sticker.rawValue
|
||||
case .sendLogs, .sendOneLog, .sendShareLogs, .sendGroupCallLogs, .sendNotificationLogs, .sendCriticalLogs:
|
||||
case .sendLogs, .sendOneLog, .sendShareLogs, .sendGroupCallLogs, .sendNotificationLogs, .sendCriticalLogs, .sendAllLogs:
|
||||
return DebugControllerSection.logs.rawValue
|
||||
case .accounts:
|
||||
return DebugControllerSection.logs.rawValue
|
||||
@ -110,7 +111,7 @@ private enum DebugControllerEntry: ItemListNodeEntry {
|
||||
return DebugControllerSection.logging.rawValue
|
||||
case .enableRaiseToSpeak, .keepChatNavigationStack, .skipReadHistory, .crashOnSlowQueries:
|
||||
return DebugControllerSection.experiments.rawValue
|
||||
case .clearTips, .crash, .resetData, .resetDatabase, .resetDatabaseAndCache, .resetHoles, .reindexUnread, .resetBiometricsData, .resetWebViewCache, .optimizeDatabase, .photoPreview, .knockoutWallpaper, .playerEmbedding, .playlistPlayback, .voiceConference, .experimentalCompatibility, .enableDebugDataDisplay, .acceleratedStickers, .experimentalBackground, .inlineStickers, .localTranscription, . enableReactionOverrides, .resetInAppPurchases:
|
||||
case .clearTips, .crash, .resetData, .resetDatabase, .resetDatabaseAndCache, .resetHoles, .reindexUnread, .resetBiometricsData, .resetWebViewCache, .optimizeDatabase, .photoPreview, .knockoutWallpaper, .playerEmbedding, .playlistPlayback, .voiceConference, .experimentalCompatibility, .enableDebugDataDisplay, .acceleratedStickers, .experimentalBackground, .inlineStickers, .localTranscription, . enableReactionOverrides, .restorePurchases:
|
||||
return DebugControllerSection.experiments.rawValue
|
||||
case .preferredVideoCodec:
|
||||
return DebugControllerSection.videoExperiments.rawValue
|
||||
@ -137,70 +138,72 @@ private enum DebugControllerEntry: ItemListNodeEntry {
|
||||
return 5
|
||||
case .sendCriticalLogs:
|
||||
return 6
|
||||
case .accounts:
|
||||
case .sendAllLogs:
|
||||
return 7
|
||||
case .logToFile:
|
||||
case .accounts:
|
||||
return 8
|
||||
case .logToConsole:
|
||||
case .logToFile:
|
||||
return 9
|
||||
case .redactSensitiveData:
|
||||
case .logToConsole:
|
||||
return 10
|
||||
case .enableRaiseToSpeak:
|
||||
case .redactSensitiveData:
|
||||
return 11
|
||||
case .keepChatNavigationStack:
|
||||
case .enableRaiseToSpeak:
|
||||
return 12
|
||||
case .skipReadHistory:
|
||||
case .keepChatNavigationStack:
|
||||
return 13
|
||||
case .crashOnSlowQueries:
|
||||
case .skipReadHistory:
|
||||
return 14
|
||||
case .clearTips:
|
||||
case .crashOnSlowQueries:
|
||||
return 15
|
||||
case .crash:
|
||||
case .clearTips:
|
||||
return 16
|
||||
case .resetData:
|
||||
case .crash:
|
||||
return 17
|
||||
case .resetDatabase:
|
||||
case .resetData:
|
||||
return 18
|
||||
case .resetDatabaseAndCache:
|
||||
case .resetDatabase:
|
||||
return 19
|
||||
case .resetHoles:
|
||||
case .resetDatabaseAndCache:
|
||||
return 20
|
||||
case .reindexUnread:
|
||||
case .resetHoles:
|
||||
return 21
|
||||
case .resetBiometricsData:
|
||||
case .reindexUnread:
|
||||
return 22
|
||||
case .resetWebViewCache:
|
||||
case .resetBiometricsData:
|
||||
return 23
|
||||
case .optimizeDatabase:
|
||||
case .resetWebViewCache:
|
||||
return 24
|
||||
case .photoPreview:
|
||||
case .optimizeDatabase:
|
||||
return 25
|
||||
case .knockoutWallpaper:
|
||||
case .photoPreview:
|
||||
return 26
|
||||
case .experimentalCompatibility:
|
||||
case .knockoutWallpaper:
|
||||
return 27
|
||||
case .enableDebugDataDisplay:
|
||||
case .experimentalCompatibility:
|
||||
return 28
|
||||
case .acceleratedStickers:
|
||||
case .enableDebugDataDisplay:
|
||||
return 29
|
||||
case .experimentalBackground:
|
||||
case .acceleratedStickers:
|
||||
return 30
|
||||
case .inlineStickers:
|
||||
case .experimentalBackground:
|
||||
return 31
|
||||
case .localTranscription:
|
||||
case .inlineStickers:
|
||||
return 32
|
||||
case .enableReactionOverrides:
|
||||
case .localTranscription:
|
||||
return 33
|
||||
case .resetInAppPurchases:
|
||||
case .enableReactionOverrides:
|
||||
return 34
|
||||
case .playerEmbedding:
|
||||
case .restorePurchases:
|
||||
return 35
|
||||
case .playlistPlayback:
|
||||
case .playerEmbedding:
|
||||
return 36
|
||||
case .voiceConference:
|
||||
case .playlistPlayback:
|
||||
return 37
|
||||
case .voiceConference:
|
||||
return 38
|
||||
case let .preferredVideoCodec(index, _, _, _):
|
||||
return 38 + index
|
||||
return 39 + index
|
||||
case .disableVideoAspectScaling:
|
||||
return 100
|
||||
case .enableVoipTcp:
|
||||
@ -402,7 +405,7 @@ private enum DebugControllerEntry: ItemListNodeEntry {
|
||||
})
|
||||
case .sendShareLogs:
|
||||
return ItemListDisclosureItem(presentationData: presentationData, title: "Send Share Logs (Up to 40 MB)", label: "", sectionId: self.section, style: .blocks, action: {
|
||||
let _ = (Logger.shared.collectLogs(prefix: "/share-logs")
|
||||
let _ = (Logger.shared.collectLogs(prefix: "/logs/share-logs")
|
||||
|> deliverOnMainQueue).start(next: { logs in
|
||||
let presentationData = arguments.sharedContext.currentPresentationData.with { $0 }
|
||||
let actionSheet = ActionSheetController(presentationData: presentationData)
|
||||
@ -570,7 +573,7 @@ private enum DebugControllerEntry: ItemListNodeEntry {
|
||||
})
|
||||
case .sendNotificationLogs:
|
||||
return ItemListDisclosureItem(presentationData: presentationData, title: "Send Notification Logs (Up to 40 MB)", label: "", sectionId: self.section, style: .blocks, action: {
|
||||
let _ = (Logger(rootPath: arguments.sharedContext.basePath, basePath: arguments.sharedContext.basePath + "/notification-logs").collectLogs()
|
||||
let _ = (Logger(rootPath: arguments.sharedContext.basePath, basePath: arguments.sharedContext.basePath + "/logs/notification-logs").collectLogs()
|
||||
|> deliverOnMainQueue).start(next: { logs in
|
||||
let presentationData = arguments.sharedContext.currentPresentationData.with { $0 }
|
||||
let actionSheet = ActionSheetController(presentationData: presentationData)
|
||||
@ -705,6 +708,101 @@ private enum DebugControllerEntry: ItemListNodeEntry {
|
||||
arguments.presentController(actionSheet, nil)
|
||||
})
|
||||
})
|
||||
case .sendAllLogs:
|
||||
return ItemListDisclosureItem(presentationData: presentationData, title: "Send All Logs", label: "", sectionId: self.section, style: .blocks, action: {
|
||||
let logTypes: [String] = [
|
||||
"app-logs",
|
||||
"broadcast-logs",
|
||||
"siri-logs",
|
||||
"widget-logs",
|
||||
"notificationcontent-logs",
|
||||
"notification-logs"
|
||||
]
|
||||
|
||||
var logByType: [Signal<(type: String, logs: [(String, String)]), NoError>] = []
|
||||
for type in logTypes {
|
||||
logByType.append(Logger(rootPath: arguments.sharedContext.basePath, basePath: arguments.sharedContext.basePath + "/logs/\(type)").collectLogs()
|
||||
|> map { result -> (type: String, logs: [(String, String)]) in
|
||||
return (type, result)
|
||||
})
|
||||
}
|
||||
|
||||
let allLogs = combineLatest(logByType)
|
||||
|
||||
let _ = (allLogs
|
||||
|> deliverOnMainQueue).start(next: { allLogs in
|
||||
let presentationData = arguments.sharedContext.currentPresentationData.with { $0 }
|
||||
let actionSheet = ActionSheetController(presentationData: presentationData)
|
||||
|
||||
var items: [ActionSheetButtonItem] = []
|
||||
|
||||
if let context = arguments.context, context.sharedContext.applicationBindings.isMainApp {
|
||||
items.append(ActionSheetButtonItem(title: "Via Telegram", color: .accent, action: { [weak actionSheet] in
|
||||
actionSheet?.dismissAnimated()
|
||||
|
||||
let controller = context.sharedContext.makePeerSelectionController(PeerSelectionControllerParams(context: context, filter: [.onlyWriteable, .excludeDisabled]))
|
||||
controller.peerSelected = { [weak controller] peer in
|
||||
let peerId = peer.id
|
||||
|
||||
if let strongController = controller {
|
||||
strongController.dismiss()
|
||||
|
||||
let lineFeed = "\n".data(using: .utf8)!
|
||||
|
||||
var tempSources: [TempBoxFile] = []
|
||||
for (type, logItems) in allLogs {
|
||||
let tempSource = TempBox.shared.tempFile(fileName: "Log-\(type).txt")
|
||||
|
||||
var rawLogData: Data = Data()
|
||||
for (name, path) in logItems {
|
||||
if !rawLogData.isEmpty {
|
||||
rawLogData.append(lineFeed)
|
||||
rawLogData.append(lineFeed)
|
||||
}
|
||||
|
||||
rawLogData.append("------ File: \(name) ------\n".data(using: .utf8)!)
|
||||
|
||||
if let data = try? Data(contentsOf: URL(fileURLWithPath: path)) {
|
||||
rawLogData.append(data)
|
||||
}
|
||||
}
|
||||
|
||||
let _ = try? rawLogData.write(to: URL(fileURLWithPath: tempSource.path))
|
||||
tempSources.append(tempSource)
|
||||
}
|
||||
|
||||
let tempZip = TempBox.shared.tempFile(fileName: "destination.zip")
|
||||
SSZipArchive.createZipFile(atPath: tempZip.path, withFilesAtPaths: tempSources.map(\.path))
|
||||
|
||||
guard let gzippedData = try? Data(contentsOf: URL(fileURLWithPath: tempZip.path)) else {
|
||||
return
|
||||
}
|
||||
|
||||
tempSources.forEach(TempBox.shared.dispose)
|
||||
TempBox.shared.dispose(tempZip)
|
||||
|
||||
let id = Int64.random(in: Int64.min ... Int64.max)
|
||||
let fileResource = LocalFileMediaResource(fileId: id, size: Int64(gzippedData.count), isSecretRelated: false)
|
||||
context.account.postbox.mediaBox.storeResourceData(fileResource.id, data: gzippedData)
|
||||
|
||||
let file = TelegramMediaFile(fileId: MediaId(namespace: Namespaces.Media.LocalFile, id: id), partialReference: nil, resource: fileResource, previewRepresentations: [], videoThumbnails: [], immediateThumbnailData: nil, mimeType: "application/zip", size: Int64(gzippedData.count), attributes: [.FileName(fileName: "Log-iOS-All.txt.zip")])
|
||||
let message: EnqueueMessage = .message(text: "", attributes: [], mediaReference: .standalone(media: file), replyToMessageId: nil, localGroupingKey: nil, correlationId: nil)
|
||||
|
||||
let _ = enqueueMessages(account: context.account, peerId: peerId, messages: [message]).start()
|
||||
}
|
||||
}
|
||||
arguments.pushController(controller)
|
||||
}))
|
||||
}
|
||||
|
||||
actionSheet.setItemGroups([ActionSheetItemGroup(items: items), ActionSheetItemGroup(items: [
|
||||
ActionSheetButtonItem(title: presentationData.strings.Common_Cancel, color: .accent, font: .bold, action: { [weak actionSheet] in
|
||||
actionSheet?.dismissAnimated()
|
||||
})
|
||||
])])
|
||||
arguments.presentController(actionSheet, nil)
|
||||
})
|
||||
})
|
||||
case .accounts:
|
||||
return ItemListDisclosureItem(presentationData: presentationData, title: "Accounts", label: "", sectionId: self.section, style: .blocks, action: {
|
||||
guard let context = arguments.context else {
|
||||
@ -1043,9 +1141,21 @@ private enum DebugControllerEntry: ItemListNodeEntry {
|
||||
})
|
||||
}).start()
|
||||
})
|
||||
case .resetInAppPurchases:
|
||||
return ItemListActionItem(presentationData: presentationData, title: "Reset IAP Transactions", kind: .destructive, alignment: .natural, sectionId: self.section, style: .blocks, action: {
|
||||
arguments.context?.inAppPurchaseManager?.finishAllTransactions()
|
||||
case .restorePurchases:
|
||||
return ItemListActionItem(presentationData: presentationData, title: "Restore Purchases", kind: .generic, alignment: .natural, sectionId: self.section, style: .blocks, action: {
|
||||
arguments.context?.inAppPurchaseManager?.restorePurchases(completion: { state in
|
||||
let text: String
|
||||
switch state {
|
||||
case .succeed:
|
||||
text = "Done"
|
||||
case .failed:
|
||||
text = "Failed"
|
||||
}
|
||||
if let context = arguments.context {
|
||||
let controller = textAlertController(context: context, title: nil, text: text, actions: [TextAlertAction(type: .genericAction, title: "OK", action: {})])
|
||||
arguments.presentController(controller, nil)
|
||||
}
|
||||
})
|
||||
})
|
||||
case let .hostInfo(_, string):
|
||||
return ItemListTextItem(presentationData: presentationData, text: .plain(string), sectionId: self.section)
|
||||
@ -1071,6 +1181,7 @@ private func debugControllerEntries(sharedContext: SharedAccountContext, present
|
||||
entries.append(.sendGroupCallLogs)
|
||||
entries.append(.sendNotificationLogs(presentationData.theme))
|
||||
entries.append(.sendCriticalLogs(presentationData.theme))
|
||||
entries.append(.sendAllLogs)
|
||||
if isMainApp {
|
||||
entries.append(.accounts(presentationData.theme))
|
||||
}
|
||||
@ -1111,7 +1222,7 @@ private func debugControllerEntries(sharedContext: SharedAccountContext, present
|
||||
if case .internal = sharedContext.applicationBindings.appBuildType {
|
||||
entries.append(.enableReactionOverrides(experimentalSettings.enableReactionOverrides))
|
||||
}
|
||||
entries.append(.resetInAppPurchases(presentationData.theme))
|
||||
entries.append(.restorePurchases(presentationData.theme))
|
||||
entries.append(.playerEmbedding(experimentalSettings.playerEmbedding))
|
||||
entries.append(.playlistPlayback(experimentalSettings.playlistPlayback))
|
||||
}
|
||||
|
@ -830,6 +830,31 @@ public extension ContainedViewLayoutTransition {
|
||||
}
|
||||
}
|
||||
|
||||
func updateContentsRect(layer: CALayer, contentsRect: CGRect, completion: ((Bool) -> Void)? = nil) {
|
||||
if layer.contentsRect == contentsRect {
|
||||
if let completion = completion {
|
||||
completion(true)
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
switch self {
|
||||
case .immediate:
|
||||
layer.contentsRect = contentsRect
|
||||
if let completion = completion {
|
||||
completion(true)
|
||||
}
|
||||
case let .animated(duration, curve):
|
||||
let previousContentsRect = layer.contentsRect
|
||||
layer.contentsRect = contentsRect
|
||||
layer.animate(from: NSValue(cgRect: previousContentsRect), to: NSValue(cgRect: contentsRect), keyPath: "contentsRect", timingFunction: curve.timingFunction, duration: duration, mediaTimingFunction: curve.mediaTimingFunction, completion: { result in
|
||||
if let completion = completion {
|
||||
completion(result)
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
func animateTransformScale(node: ASDisplayNode, from fromScale: CGFloat, additive: Bool = false, completion: ((Bool) -> Void)? = nil) {
|
||||
let t = node.layer.transform
|
||||
let currentScale = sqrt((t.m11 * t.m11) + (t.m12 * t.m12) + (t.m13 * t.m13))
|
||||
@ -1512,6 +1537,7 @@ public protocol ControlledTransitionAnimator: AnyObject {
|
||||
func updateBounds(layer: CALayer, bounds: CGRect, completion: ((Bool) -> Void)?)
|
||||
func updateFrame(layer: CALayer, frame: CGRect, completion: ((Bool) -> Void)?)
|
||||
func updateCornerRadius(layer: CALayer, cornerRadius: CGFloat, completion: ((Bool) -> Void)?)
|
||||
func updateContentsRect(layer: CALayer, contentsRect: CGRect, completion: ((Bool) -> Void)?)
|
||||
}
|
||||
|
||||
protocol AnyValueProviding {
|
||||
@ -1908,6 +1934,21 @@ public final class ControlledTransition {
|
||||
completion: completion
|
||||
))
|
||||
}
|
||||
|
||||
public func updateContentsRect(layer: CALayer, contentsRect: CGRect, completion: ((Bool) -> Void)?) {
|
||||
if layer.contentsRect == contentsRect {
|
||||
return
|
||||
}
|
||||
let fromValue = layer.presentation()?.contentsRect ?? layer.contentsRect
|
||||
layer.contentsRect = contentsRect
|
||||
self.add(animation: ControlledTransitionProperty(
|
||||
layer: layer,
|
||||
path: "contentsRect",
|
||||
fromValue: fromValue,
|
||||
toValue: contentsRect,
|
||||
completion: completion
|
||||
))
|
||||
}
|
||||
}
|
||||
|
||||
public final class LegacyAnimator: ControlledTransitionAnimator {
|
||||
@ -1963,6 +2004,10 @@ public final class ControlledTransition {
|
||||
public func updateCornerRadius(layer: CALayer, cornerRadius: CGFloat, completion: ((Bool) -> Void)?) {
|
||||
self.transition.updateCornerRadius(layer: layer, cornerRadius: cornerRadius, completion: completion)
|
||||
}
|
||||
|
||||
public func updateContentsRect(layer: CALayer, contentsRect: CGRect, completion: ((Bool) -> Void)?) {
|
||||
self.transition.updateContentsRect(layer: layer, contentsRect: contentsRect, completion: completion)
|
||||
}
|
||||
}
|
||||
|
||||
public let animator: ControlledTransitionAnimator
|
||||
|
@ -910,7 +910,6 @@ open class NavigationController: UINavigationController, ContainableController,
|
||||
visibleRootModalDismissProgress = effectiveRootModalDismissProgress
|
||||
additionalModalFrameProgress = 1.0 - topModalDismissProgress
|
||||
} else {
|
||||
// effectiveRootModalDismissProgress = ((topModalIsFlat && !topFlatModalHasProgress) || isLandscape) ? 1.0 : topModalDismissProgress
|
||||
effectiveRootModalDismissProgress = topModalDismissProgress
|
||||
visibleRootModalDismissProgress = effectiveRootModalDismissProgress
|
||||
additionalModalFrameProgress = 0.0
|
||||
|
@ -123,7 +123,7 @@ final class StickerPackPreviewGridItemNode: GridItemNode {
|
||||
self.imageNode.isHidden = true
|
||||
|
||||
if isVerified {
|
||||
let animationNode = AnimatedStickerNode()
|
||||
let animationNode = DefaultAnimatedStickerNodeImpl()
|
||||
self.animationNode = animationNode
|
||||
|
||||
if let placeholderNode = self.placeholderNode {
|
||||
|
@ -75,7 +75,7 @@ private final class StickerPreviewPeekContentNode: ASDisplayNode, PeekController
|
||||
case let .image(data):
|
||||
self.imageNode.image = UIImage(data: data)
|
||||
case .animation, .video:
|
||||
let animationNode = AnimatedStickerNode()
|
||||
let animationNode = DefaultAnimatedStickerNodeImpl()
|
||||
self.animationNode = animationNode
|
||||
let dimensions = PixelDimensions(width: 512, height: 512)
|
||||
let fittedDimensions = dimensions.cgSize.aspectFitted(CGSize(width: 400.0, height: 400.0))
|
||||
@ -84,7 +84,7 @@ private final class StickerPreviewPeekContentNode: ASDisplayNode, PeekController
|
||||
if case .video = item.content {
|
||||
isVideo = true
|
||||
}
|
||||
self.animationNode?.setup(source: AnimatedStickerResourceSource(account: account, resource: resource, isVideo: isVideo), width: Int(fittedDimensions.width), height: Int(fittedDimensions.height), mode: .direct(cachePathPrefix: nil))
|
||||
self.animationNode?.setup(source: AnimatedStickerResourceSource(account: account, resource: resource, isVideo: isVideo), width: Int(fittedDimensions.width), height: Int(fittedDimensions.height), playbackMode: .loop, mode: .direct(cachePathPrefix: nil))
|
||||
}
|
||||
self.animationNode?.visibility = true
|
||||
}
|
||||
|
@ -32,6 +32,11 @@ public final class InAppPurchaseManager: NSObject {
|
||||
case notAllowed
|
||||
}
|
||||
|
||||
public enum RestoreState {
|
||||
case succeed
|
||||
case failed
|
||||
}
|
||||
|
||||
private final class PaymentTransactionContext {
|
||||
var state: SKPaymentTransactionState?
|
||||
let subscriber: (TransactionState) -> Void
|
||||
@ -59,6 +64,8 @@ public final class InAppPurchaseManager: NSObject {
|
||||
private let stateQueue = Queue()
|
||||
private var paymentContexts: [String: PaymentTransactionContext] = [:]
|
||||
|
||||
private var onRestoreCompletion: ((RestoreState) -> Void)?
|
||||
|
||||
private let disposableSet = DisposableDict<String>()
|
||||
|
||||
public init(engine: TelegramEngine, premiumProductId: String) {
|
||||
@ -79,6 +86,7 @@ public final class InAppPurchaseManager: NSObject {
|
||||
guard !self.premiumProductId.isEmpty else {
|
||||
return
|
||||
}
|
||||
Logger.shared.log("InAppPurchaseManager", "Requesting products")
|
||||
let productRequest = SKProductsRequest(productIdentifiers: Set([self.premiumProductId]))
|
||||
productRequest.delegate = self
|
||||
productRequest.start()
|
||||
@ -93,7 +101,17 @@ public final class InAppPurchaseManager: NSObject {
|
||||
return self.productsPromise.get()
|
||||
}
|
||||
|
||||
public func restorePurchases(completion: @escaping (RestoreState) -> Void) {
|
||||
Logger.shared.log("InAppPurchaseManager", "Restoring purchases")
|
||||
self.onRestoreCompletion = completion
|
||||
|
||||
let paymentQueue = SKPaymentQueue.default()
|
||||
paymentQueue.restoreCompletedTransactions()
|
||||
}
|
||||
|
||||
public func finishAllTransactions() {
|
||||
Logger.shared.log("InAppPurchaseManager", "Finishing all transactions")
|
||||
|
||||
let paymentQueue = SKPaymentQueue.default()
|
||||
let transactions = paymentQueue.transactions
|
||||
for transaction in transactions {
|
||||
@ -102,6 +120,8 @@ public final class InAppPurchaseManager: NSObject {
|
||||
}
|
||||
|
||||
public func buyProduct(_ product: Product, account: Account) -> Signal<PurchaseState, PurchaseError> {
|
||||
Logger.shared.log("InAppPurchaseManager", "Buying product: \(product.skProduct.productIdentifier), price \(product.price)")
|
||||
|
||||
let payment = SKPayment(product: product.skProduct)
|
||||
SKPaymentQueue.default().add(payment)
|
||||
|
||||
@ -162,7 +182,10 @@ extension InAppPurchaseManager: SKProductsRequestDelegate {
|
||||
self.productRequest = nil
|
||||
|
||||
Queue.mainQueue().async {
|
||||
self.productsPromise.set(.single(response.products.map { Product(skProduct: $0) }))
|
||||
let products = response.products.map { Product(skProduct: $0) }
|
||||
|
||||
Logger.shared.log("InAppPurchaseManager", "Received products \(products.map({ $0.skProduct.productIdentifier }).joined(separator: ", "))")
|
||||
self.productsPromise.set(.single(products))
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -187,37 +210,34 @@ extension InAppPurchaseManager: SKPaymentTransactionObserver {
|
||||
let transactionState: TransactionState?
|
||||
switch transaction.transactionState {
|
||||
case .purchased:
|
||||
Logger.shared.log("InAppPurchaseManager", "Transaction \(transaction.transactionIdentifier ?? ""), original transaction \(transaction.original?.transactionIdentifier ?? "none") purchased")
|
||||
let transactionIdentifier = transaction.transactionIdentifier
|
||||
transactionState = .purchased(transactionId: transactionIdentifier)
|
||||
if let transactionIdentifier = transactionIdentifier {
|
||||
self.disposableSet.set(
|
||||
self.engine.payments.assignAppStoreTransaction(transactionId: transactionIdentifier, receipt: getReceiptData() ?? Data(), restore: false).start(error: { _ in
|
||||
self.engine.payments.sendAppStoreReceipt(receipt: getReceiptData() ?? Data(), restore: false).start(error: { _ in
|
||||
Logger.shared.log("InAppPurchaseManager", "Transaction \(transaction.transactionIdentifier ?? "") failed to assign AppStore transaction")
|
||||
queue.finishTransaction(transaction)
|
||||
}, completed: {
|
||||
Logger.shared.log("InAppPurchaseManager", "Transaction \(transaction.transactionIdentifier ?? "") successfully assigned AppStore transaction")
|
||||
queue.finishTransaction(transaction)
|
||||
}),
|
||||
forKey: transactionIdentifier
|
||||
)
|
||||
}
|
||||
case .restored:
|
||||
Logger.shared.log("InAppPurchaseManager", "Transaction \(transaction.transactionIdentifier ?? ""), original transaction \(transaction.original?.transactionIdentifier ?? "") restroring")
|
||||
let transactionIdentifier = transaction.transactionIdentifier
|
||||
transactionState = .restored(transactionId: transactionIdentifier)
|
||||
if let transactionIdentifier = transactionIdentifier {
|
||||
self.disposableSet.set(
|
||||
self.engine.payments.assignAppStoreTransaction(transactionId: transactionIdentifier, receipt: getReceiptData() ?? Data(), restore: true).start(error: { _ in
|
||||
queue.finishTransaction(transaction)
|
||||
}, completed: {
|
||||
queue.finishTransaction(transaction)
|
||||
}),
|
||||
forKey: transactionIdentifier
|
||||
)
|
||||
}
|
||||
case .failed:
|
||||
Logger.shared.log("InAppPurchaseManager", "Transaction \(transaction.transactionIdentifier ?? "") failed \((transaction.error as? SKError)?.localizedDescription ?? "")")
|
||||
transactionState = .failed(error: transaction.error as? SKError)
|
||||
queue.finishTransaction(transaction)
|
||||
case .purchasing:
|
||||
Logger.shared.log("InAppPurchaseManager", "Transaction \(transaction.transactionIdentifier ?? "") purchasing")
|
||||
transactionState = .purchasing
|
||||
case .deferred:
|
||||
Logger.shared.log("InAppPurchaseManager", "Transaction \(transaction.transactionIdentifier ?? "") deferred")
|
||||
transactionState = .deferred
|
||||
default:
|
||||
transactionState = nil
|
||||
@ -230,4 +250,29 @@ extension InAppPurchaseManager: SKPaymentTransactionObserver {
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public func paymentQueueRestoreCompletedTransactionsFinished(_ queue: SKPaymentQueue) {
|
||||
if let onRestoreCompletion = self.onRestoreCompletion {
|
||||
Logger.shared.log("InAppPurchaseManager", "Transactions restoration finished")
|
||||
onRestoreCompletion(.succeed)
|
||||
self.onRestoreCompletion = nil
|
||||
|
||||
if let receiptData = getReceiptData() {
|
||||
self.disposableSet.set(
|
||||
self.engine.payments.sendAppStoreReceipt(receipt: receiptData, restore: true).start(completed: {
|
||||
Logger.shared.log("InAppPurchaseManager", "Sent restored receipt")
|
||||
}),
|
||||
forKey: "restore"
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public func paymentQueue(_ queue: SKPaymentQueue, restoreCompletedTransactionsFailedWithError error: Error) {
|
||||
if let onRestoreCompletion = self.onRestoreCompletion {
|
||||
Logger.shared.log("InAppPurchaseManager", "Transactions restoration failed with error \((error as? SKError)?.localizedDescription ?? "")")
|
||||
onRestoreCompletion(.failed)
|
||||
self.onRestoreCompletion = nil
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -80,7 +80,7 @@ class InviteLinkHeaderItemNode: ListViewItemNode {
|
||||
self.titleNode.contentMode = .left
|
||||
self.titleNode.contentsScale = UIScreen.main.scale
|
||||
|
||||
self.animationNode = AnimatedStickerNode()
|
||||
self.animationNode = DefaultAnimatedStickerNodeImpl()
|
||||
|
||||
super.init(layerBacked: false, dynamicBounce: false)
|
||||
|
||||
|
@ -58,7 +58,7 @@ final class InviteRequestsEmptyStateItemNode: ItemListControllerEmptyStateItemNo
|
||||
init(item: InviteRequestsEmptyStateItem) {
|
||||
self.item = item
|
||||
|
||||
self.animationNode = AnimatedStickerNode()
|
||||
self.animationNode = DefaultAnimatedStickerNodeImpl()
|
||||
self.animationNode.setup(source: AnimatedStickerNodeLocalFileSource(name: "TwoFactorSetupRememberSuccess"), width: 192, height: 192, playbackMode: .once, mode: .direct(cachePathPrefix: nil))
|
||||
self.animationNode.visibility = true
|
||||
|
||||
|
@ -761,14 +761,14 @@ class ItemListStickerPackItemNode: ItemListRevealOptionsItemNode {
|
||||
if let current = strongSelf.animationNode {
|
||||
animationNode = current
|
||||
} else {
|
||||
animationNode = AnimatedStickerNode()
|
||||
animationNode = DefaultAnimatedStickerNodeImpl()
|
||||
animationNode.started = { [weak self] in
|
||||
self?.removePlaceholder(animated: false)
|
||||
}
|
||||
strongSelf.animationNode = animationNode
|
||||
strongSelf.addSubnode(animationNode)
|
||||
|
||||
animationNode.setup(source: AnimatedStickerResourceSource(account: item.account, resource: resource, isVideo: isVideo), width: 80, height: 80, mode: .cached)
|
||||
animationNode.setup(source: AnimatedStickerResourceSource(account: item.account, resource: resource, isVideo: isVideo), width: 80, height: 80, playbackMode: .loop, mode: .cached)
|
||||
}
|
||||
animationNode.visibility = strongSelf.visibility != .none && item.playAnimatedStickers
|
||||
animationNode.isHidden = !item.playAnimatedStickers
|
||||
|
@ -15,7 +15,6 @@
|
||||
|
||||
+ (TGMenuSheetController *)presentInParentController:(TGViewController *)parentController context:(id<LegacyComponentsContext>)context images:(NSArray *)images allowGrouping:(bool)allowGrouping hasCaption:(bool)hasCaption hasTimer:(bool)hasTimer hasSilentPosting:(bool)hasSilentPosting hasSchedule:(bool)hasSchedule reminder:(bool)reminder recipientName:(NSString *)recipientName stickersContext:(id<TGPhotoPaintStickersContext>)stickersContext presentScheduleController:(void (^)(void(^)(int32_t)))presentScheduleController presentTimerController:(void (^)(void(^)(int32_t)))presentTimerController completed:(void (^)(TGMediaSelectionContext *selectionContext, TGMediaEditingContext *editingContext, id<TGMediaSelectableItem> currentItem, bool silentPosting, int32_t scheduleTime))completed dismissed:(void (^)(void))dismissed sourceView:(UIView *)sourceView sourceRect:(CGRect (^)(void))sourceRect;
|
||||
|
||||
+ (NSArray *)resultSignalsForSelectionContext:(TGMediaSelectionContext *)selectionContext editingContext:(TGMediaEditingContext *)editingContext currentItem:(id<TGMediaSelectableItem>)currentItem descriptionGenerator:(id (^)(id, NSAttributedString *,
|
||||
NSString *))descriptionGenerator;
|
||||
+ (NSArray *)resultSignalsForSelectionContext:(TGMediaSelectionContext *)selectionContext editingContext:(TGMediaEditingContext *)editingContext currentItem:(id<TGMediaSelectableItem>)currentItem descriptionGenerator:(id (^)(id, NSAttributedString *, NSString *))descriptionGenerator;
|
||||
|
||||
@end
|
||||
|
@ -62,7 +62,7 @@ class LegacyPaintStickerView: UIView, TGPhotoPaintStickerRenderView {
|
||||
if let dimensions = self.file.dimensions {
|
||||
if self.file.isAnimatedSticker || self.file.isVideoSticker {
|
||||
if self.animationNode == nil {
|
||||
let animationNode = AnimatedStickerNode()
|
||||
let animationNode = DefaultAnimatedStickerNodeImpl()
|
||||
animationNode.autoplay = false
|
||||
self.animationNode = animationNode
|
||||
animationNode.started = { [weak self, weak animationNode] in
|
||||
@ -115,7 +115,7 @@ class LegacyPaintStickerView: UIView, TGPhotoPaintStickerRenderView {
|
||||
let dimensions = self.file.dimensions ?? PixelDimensions(width: 512, height: 512)
|
||||
let fittedDimensions = dimensions.cgSize.aspectFitted(CGSize(width: 384.0, height: 384.0))
|
||||
let source = AnimatedStickerResourceSource(account: self.context.account, resource: self.file.resource, isVideo: self.file.isVideoSticker)
|
||||
self.animationNode?.setup(source: source, width: Int(fittedDimensions.width), height: Int(fittedDimensions.height), mode: .direct(cachePathPrefix: nil))
|
||||
self.animationNode?.setup(source: source, width: Int(fittedDimensions.width), height: Int(fittedDimensions.height), playbackMode: .loop, mode: .direct(cachePathPrefix: nil))
|
||||
|
||||
self.cachedDisposable.set((source.cachedDataPath(width: 384, height: 384)
|
||||
|> deliverOn(Queue.concurrentDefaultQueue())).start())
|
||||
@ -150,7 +150,7 @@ class LegacyPaintStickerView: UIView, TGPhotoPaintStickerRenderView {
|
||||
func play(fromFrame frameIndex: Int) {
|
||||
self.isVisible = true
|
||||
self.updateVisibility()
|
||||
self.animationNode?.play(fromIndex: frameIndex)
|
||||
self.animationNode?.play(firstFrame: false, fromIndex: frameIndex)
|
||||
}
|
||||
|
||||
func copyStickerView(_ view: TGPhotoPaintStickerRenderView!) {
|
||||
@ -158,7 +158,7 @@ class LegacyPaintStickerView: UIView, TGPhotoPaintStickerRenderView {
|
||||
return
|
||||
}
|
||||
self.animationNode?.cloneCurrentFrame(from: animationNode)
|
||||
self.animationNode?.play(fromIndex: animationNode.currentFrameIndex)
|
||||
self.animationNode?.play(firstFrame: false, fromIndex: animationNode.currentFrameIndex)
|
||||
self.updateVisibility()
|
||||
}
|
||||
|
||||
|
@ -37,7 +37,7 @@ final class LocationPlaceholderNode: ASDisplayNode {
|
||||
playbackMode = .loop
|
||||
}
|
||||
|
||||
self.animationNode = AnimatedStickerNode()
|
||||
self.animationNode = DefaultAnimatedStickerNodeImpl()
|
||||
self.animationNode.setup(source: AnimatedStickerNodeLocalFileSource(name: name), width: 320, height: 320, playbackMode: playbackMode, mode: .direct(cachePathPrefix: nil))
|
||||
self.animationNode.visibility = true
|
||||
|
||||
|
@ -43,7 +43,7 @@ final class MediaPickerPlaceholderNode: ASDisplayNode {
|
||||
playbackMode = .once
|
||||
}
|
||||
|
||||
self.animationNode = AnimatedStickerNode()
|
||||
self.animationNode = DefaultAnimatedStickerNodeImpl()
|
||||
self.animationNode.setup(source: AnimatedStickerNodeLocalFileSource(name: name), width: 320, height: 320, playbackMode: playbackMode, mode: .direct(cachePathPrefix: nil))
|
||||
self.animationNode.visibility = true
|
||||
|
||||
|
@ -1269,17 +1269,17 @@ private final class TwoFactorDataInputScreenNode: ViewControllerTracingNode, UIS
|
||||
case .password, .passwordRecovery, .emailAddress, .updateEmailAddress:
|
||||
self.monkeyNode = ManagedMonkeyAnimationNode()
|
||||
case .emailConfirmation, .passwordRecoveryEmail:
|
||||
let animatedStickerNode = AnimatedStickerNode()
|
||||
let animatedStickerNode = DefaultAnimatedStickerNodeImpl()
|
||||
animatedStickerNode.setup(source: AnimatedStickerNodeLocalFileSource(name: "TwoFactorSetupMail"), width: 272, height: 272, playbackMode: .once, mode: .direct(cachePathPrefix: nil))
|
||||
animatedStickerNode.visibility = true
|
||||
self.animatedStickerNode = animatedStickerNode
|
||||
case .passwordHint:
|
||||
let animatedStickerNode = AnimatedStickerNode()
|
||||
let animatedStickerNode = DefaultAnimatedStickerNodeImpl()
|
||||
animatedStickerNode.setup(source: AnimatedStickerNodeLocalFileSource(name: "TwoFactorSetupHint"), width: 272, height: 272, playbackMode: .once, mode: .direct(cachePathPrefix: nil))
|
||||
animatedStickerNode.visibility = true
|
||||
self.animatedStickerNode = animatedStickerNode
|
||||
case .rememberPassword:
|
||||
let animatedStickerNode = AnimatedStickerNode()
|
||||
let animatedStickerNode = DefaultAnimatedStickerNodeImpl()
|
||||
animatedStickerNode.setup(source: AnimatedStickerNodeLocalFileSource(name: "TwoFactorSetupRemember"), width: 272, height: 272, playbackMode: .count(3), mode: .direct(cachePathPrefix: nil))
|
||||
animatedStickerNode.visibility = true
|
||||
self.animatedStickerNode = animatedStickerNode
|
||||
|
@ -155,7 +155,7 @@ private final class TwoFactorAuthSplashScreenNode: ViewControllerTracingNode {
|
||||
self.presentationData = presentationData
|
||||
self.mode = mode
|
||||
|
||||
self.animationNode = AnimatedStickerNode()
|
||||
self.animationNode = DefaultAnimatedStickerNodeImpl()
|
||||
|
||||
let title: String
|
||||
let texts: [NSAttributedString]
|
||||
@ -178,7 +178,7 @@ private final class TwoFactorAuthSplashScreenNode: ViewControllerTracingNode {
|
||||
texts = [NSAttributedString(string: self.presentationData.strings.TwoFactorSetup_Done_Text, font: textFont, textColor: textColor)]
|
||||
buttonText = doneText
|
||||
|
||||
self.animationNode.setup(source: AnimatedStickerNodeLocalFileSource(name: "TwoFactorSetupDone"), width: 248, height: 248, mode: .direct(cachePathPrefix: nil))
|
||||
self.animationNode.setup(source: AnimatedStickerNodeLocalFileSource(name: "TwoFactorSetupDone"), width: 248, height: 248, playbackMode: .loop, mode: .direct(cachePathPrefix: nil))
|
||||
self.animationSize = CGSize(width: 124.0, height: 124.0)
|
||||
self.animationNode.visibility = true
|
||||
case let .recoveryDone(_, _, isPasswordSet):
|
||||
@ -209,7 +209,7 @@ private final class TwoFactorAuthSplashScreenNode: ViewControllerTracingNode {
|
||||
texts = [NSAttributedString(string: self.presentationData.strings.TwoFactorRemember_Done_Text, font: textFont, textColor: textColor)]
|
||||
buttonText = self.presentationData.strings.TwoFactorRemember_Done_Action
|
||||
|
||||
self.animationNode.setup(source: AnimatedStickerNodeLocalFileSource(name: "TwoFactorSetupRememberSuccess"), width: 248, height: 248, mode: .direct(cachePathPrefix: nil))
|
||||
self.animationNode.setup(source: AnimatedStickerNodeLocalFileSource(name: "TwoFactorSetupRememberSuccess"), width: 248, height: 248, playbackMode: .loop, mode: .direct(cachePathPrefix: nil))
|
||||
self.animationSize = CGSize(width: 124.0, height: 124.0)
|
||||
self.animationNode.visibility = true
|
||||
}
|
||||
|
@ -730,7 +730,19 @@ private func channelVisibilityControllerEntries(presentationData: PresentationDa
|
||||
|
||||
if case .revokeNames = mode {
|
||||
let count = Int32(publicChannelsToRevoke?.count ?? 0)
|
||||
entries.append(.linksLimitInfo(presentationData.theme, presentationData.strings.Group_Username_RemoveExistingUsernamesOrExtendInfo("\(premiumLimits.maxPublicLinksCount)").string, count, limits.maxPublicLinksCount, premiumLimits.maxPublicLinksCount, isPremiumDisabled))
|
||||
|
||||
let text: String
|
||||
if count >= premiumLimits.maxPublicLinksCount {
|
||||
text = presentationData.strings.Group_Username_RemoveExistingUsernamesFinalInfo
|
||||
} else {
|
||||
if isPremiumDisabled {
|
||||
text = presentationData.strings.Group_Username_RemoveExistingUsernamesNoPremiumInfo
|
||||
} else {
|
||||
text = presentationData.strings.Group_Username_RemoveExistingUsernamesOrExtendInfo("\(premiumLimits.maxPublicLinksCount)").string
|
||||
}
|
||||
}
|
||||
|
||||
entries.append(.linksLimitInfo(presentationData.theme, text, count, limits.maxPublicLinksCount, premiumLimits.maxPublicLinksCount, isPremiumDisabled))
|
||||
|
||||
if let publicChannelsToRevoke = publicChannelsToRevoke {
|
||||
var index: Int32 = 0
|
||||
@ -1419,7 +1431,8 @@ public func channelVisibilityController(context: AccountContext, updatedPresenta
|
||||
|
||||
var rightNavigationButton: ItemListNavigationButton?
|
||||
if case .revokeNames = mode {
|
||||
if !premiumConfiguration.isPremiumDisabled {
|
||||
let count = Int32(publicChannelsToRevoke?.count ?? 0)
|
||||
if !premiumConfiguration.isPremiumDisabled && count < premiumLimits.maxPublicLinksCount {
|
||||
footerItem = IncreaseLimitFooterItem(theme: presentationData.theme, title: presentationData.strings.Premium_IncreaseLimit, colorful: true, action: {
|
||||
let controller = PremiumIntroScreen(context: context, source: .publicLinks)
|
||||
pushControllerImpl?(controller)
|
||||
|
@ -39,7 +39,7 @@ private final class ReportPeerHeaderActionSheetItemNode: ActionSheetItemNode {
|
||||
|
||||
let textFont = Font.regular(floor(theme.baseFontSize * 13.0 / 17.0))
|
||||
|
||||
self.animationNode = AnimatedStickerNode()
|
||||
self.animationNode = DefaultAnimatedStickerNodeImpl()
|
||||
self.animationNode.setup(source: AnimatedStickerNodeLocalFileSource(name: "Cop"), width: 192, height: 192, playbackMode: .count(2), mode: .direct(cachePathPrefix: nil))
|
||||
self.animationNode.visibility = true
|
||||
|
||||
|
@ -74,7 +74,7 @@ class PeersNearbyHeaderItemNode: ListViewItemNode {
|
||||
self.titleNode.contentMode = .left
|
||||
self.titleNode.contentsScale = UIScreen.main.scale
|
||||
|
||||
self.animationNode = AnimatedStickerNode()
|
||||
self.animationNode = DefaultAnimatedStickerNodeImpl()
|
||||
|
||||
super.init(layerBacked: false, dynamicBounce: false)
|
||||
|
||||
|
@ -49,7 +49,7 @@ private final class MediaBoxFileMap {
|
||||
return nil
|
||||
}
|
||||
|
||||
var truncationSizeValue: Int32 = 0
|
||||
var truncationSizeValue: Int64 = 0
|
||||
|
||||
var data = Data(count: Int(8 + count * 2 * 8))
|
||||
let dataCount = data.count
|
||||
@ -89,8 +89,10 @@ private final class MediaBoxFileMap {
|
||||
self.ranges = ranges
|
||||
if truncationSizeValue == -1 {
|
||||
self.truncationSize = nil
|
||||
} else if truncationSizeValue < 0 {
|
||||
self.truncationSize = nil
|
||||
} else {
|
||||
self.truncationSize = Int64(truncationSizeValue)
|
||||
self.truncationSize = truncationSizeValue
|
||||
}
|
||||
} else {
|
||||
let crc: UInt32 = firstUInt32
|
||||
@ -215,7 +217,8 @@ private final class MediaBoxFileMap {
|
||||
} else {
|
||||
maxValue = Int64.max
|
||||
}
|
||||
let clippedRange: Range<Int64> = range.lowerBound ..< min(maxValue, range.upperBound)
|
||||
let clippedUpperBound = min(maxValue, range.upperBound)
|
||||
let clippedRange: Range<Int64> = min(range.lowerBound, clippedUpperBound) ..< clippedUpperBound
|
||||
let clippedRangeSet = RangeSet<Int64>(clippedRange)
|
||||
|
||||
if self.ranges.isSuperset(of: clippedRangeSet) {
|
||||
|
@ -780,7 +780,7 @@ private final class DemoSheetContent: CombinedComponent {
|
||||
videoFile: configuration.videos["no_ads"]
|
||||
)),
|
||||
title: strings.Premium_NoAds,
|
||||
text: strings.Premium_NoAdsInfo,
|
||||
text: isStandalone ? strings.Premium_NoAdsStandaloneInfo : strings.Premium_NoAdsInfo,
|
||||
textColor: textColor
|
||||
)
|
||||
)
|
||||
@ -950,6 +950,7 @@ private final class DemoSheetContent: CombinedComponent {
|
||||
)
|
||||
|
||||
let buttonText: String
|
||||
var buttonAnimationName: String?
|
||||
if state.isPremium == true {
|
||||
buttonText = strings.Common_OK
|
||||
} else {
|
||||
@ -957,16 +958,21 @@ private final class DemoSheetContent: CombinedComponent {
|
||||
case let .intro(price):
|
||||
buttonText = strings.Premium_SubscribeFor(price ?? "–").string
|
||||
case .other:
|
||||
switch component.subject {
|
||||
case .uniqueReactions:
|
||||
buttonText = strings.Premium_Reactions_Proceed
|
||||
case .premiumStickers:
|
||||
buttonText = strings.Premium_Stickers_Proceed
|
||||
case .appIcons:
|
||||
buttonText = strings.Premium_AppIcons_Proceed
|
||||
default:
|
||||
buttonText = strings.Common_OK
|
||||
}
|
||||
switch component.subject {
|
||||
case .uniqueReactions:
|
||||
buttonText = strings.Premium_Reactions_Proceed
|
||||
buttonAnimationName = "premium_unlock"
|
||||
case .premiumStickers:
|
||||
buttonText = strings.Premium_Stickers_Proceed
|
||||
buttonAnimationName = "premium_unlock"
|
||||
case .appIcons:
|
||||
buttonText = strings.Premium_AppIcons_Proceed
|
||||
buttonAnimationName = "premium_unlock"
|
||||
case .noAds:
|
||||
buttonText = strings.Premium_NoAds_Proceed
|
||||
default:
|
||||
buttonText = strings.Common_OK
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@ -988,7 +994,7 @@ private final class DemoSheetContent: CombinedComponent {
|
||||
height: 50.0,
|
||||
cornerRadius: 11.0,
|
||||
gloss: state.isPremium != true,
|
||||
animationName: isStandalone && component.subject == .uniqueReactions ? "premium_unlock" : nil,
|
||||
animationName: isStandalone ? buttonAnimationName : nil,
|
||||
iconPosition: .right,
|
||||
iconSpacing: 4.0,
|
||||
action: { [weak component, weak state] in
|
||||
|
@ -382,6 +382,8 @@ private final class SectionGroupComponent: Component {
|
||||
buttonView = current
|
||||
} else {
|
||||
buttonView = HighlightTrackingButton()
|
||||
buttonView.isMultipleTouchEnabled = false
|
||||
buttonView.isExclusiveTouch = true
|
||||
buttonView.addTarget(self, action: #selector(self.buttonPressed(_:)), for: .touchUpInside)
|
||||
self.buttonViews[item.content.id] = buttonView
|
||||
self.addSubview(buttonView)
|
||||
@ -1622,7 +1624,7 @@ private final class PremiumIntroScreenComponent: CombinedComponent {
|
||||
titleScale = 1.0 - fraction * 0.36
|
||||
|
||||
if state.otherPeerName != nil {
|
||||
titleAlpha = min(1.0, fraction * 1.5)
|
||||
titleAlpha = min(1.0, fraction * 1.1)
|
||||
} else {
|
||||
titleAlpha = 1.0
|
||||
}
|
||||
@ -1656,7 +1658,7 @@ private final class PremiumIntroScreenComponent: CombinedComponent {
|
||||
context.add(secondaryTitle
|
||||
.position(CGPoint(x: context.availableSize.width / 2.0, y: max(topInset + 160.0 - titleOffset, environment.statusBarHeight + (environment.navigationHeight - environment.statusBarHeight) / 2.0)))
|
||||
.scale(titleScale)
|
||||
.opacity(1.0 - titleAlpha)
|
||||
.opacity(max(0.0, 1.0 - titleAlpha * 1.8))
|
||||
)
|
||||
|
||||
if state.isPremium == true {
|
||||
|
@ -281,9 +281,11 @@ class PremiumStarComponent: Component {
|
||||
}
|
||||
|
||||
private func maybeAnimateIn() {
|
||||
guard let scene = self.sceneView.scene, let node = scene.rootNode.childNode(withName: "star", recursively: false), let animateFrom = self.animateFrom, let containerView = self.containerView else {
|
||||
guard let scene = self.sceneView.scene, let node = scene.rootNode.childNode(withName: "star", recursively: false), let animateFrom = self.animateFrom, var containerView = self.containerView else {
|
||||
return
|
||||
}
|
||||
|
||||
containerView = containerView.subviews[2].subviews[1]
|
||||
|
||||
if let animationColor = self.animationColor {
|
||||
let newNode = node.clone()
|
||||
@ -323,6 +325,9 @@ class PremiumStarComponent: Component {
|
||||
self.sceneView.layer.animatePosition(from: sourcePosition, to: targetPosition, duration: 1.0, timingFunction: kCAMediaTimingFunctionSpring, completion: { _ in
|
||||
self.addSubview(self.sceneView)
|
||||
self.sceneView.center = initialPosition
|
||||
})
|
||||
|
||||
Queue.mainQueue().after(0.4, {
|
||||
animateFrom.alpha = 1.0
|
||||
})
|
||||
|
||||
|
@ -104,7 +104,7 @@ private class StickerNode: ASDisplayNode {
|
||||
self.imageNode = TransformImageNode()
|
||||
|
||||
if file.isPremiumSticker || forceIsPremium {
|
||||
let animationNode = AnimatedStickerNode()
|
||||
let animationNode = DefaultAnimatedStickerNodeImpl()
|
||||
animationNode.automaticallyLoadFirstFrame = true
|
||||
self.animationNode = animationNode
|
||||
|
||||
@ -122,7 +122,7 @@ private class StickerNode: ASDisplayNode {
|
||||
self.effectDisposable.set(freeMediaFileResourceInteractiveFetched(account: self.context.account, fileReference: .standalone(media: file), resource: effect.resource).start())
|
||||
|
||||
let source = AnimatedStickerResourceSource(account: self.context.account, resource: effect.resource, fitzModifier: nil)
|
||||
let additionalAnimationNode = AnimatedStickerNode()
|
||||
let additionalAnimationNode = DefaultAnimatedStickerNodeImpl()
|
||||
|
||||
var pathPrefix: String?
|
||||
pathPrefix = context.account.postbox.mediaBox.shortLivedResourceCachePathPrefix(effect.resource.id)
|
||||
@ -232,7 +232,7 @@ private class StickerNode: ASDisplayNode {
|
||||
} else if isVisible {
|
||||
additionalAnimationNode.visibility = isVisible
|
||||
if !wasVisible {
|
||||
additionalAnimationNode.play(fromIndex: 0)
|
||||
additionalAnimationNode.play(firstFrame: false, fromIndex: 0)
|
||||
Queue.mainQueue().after(0.05, {
|
||||
additionalAnimationNode.alpha = 1.0
|
||||
})
|
||||
|
@ -255,8 +255,8 @@ public final class QrCodeScreen: ViewController {
|
||||
self.qrImageNode.clipsToBounds = true
|
||||
self.qrImageNode.cornerRadius = 16.0
|
||||
|
||||
self.qrIconNode = AnimatedStickerNode()
|
||||
self.qrIconNode.setup(source: AnimatedStickerNodeLocalFileSource(name: "PlaneLogo"), width: 240, height: 240, mode: .direct(cachePathPrefix: nil))
|
||||
self.qrIconNode = DefaultAnimatedStickerNodeImpl()
|
||||
self.qrIconNode.setup(source: AnimatedStickerNodeLocalFileSource(name: "PlaneLogo"), width: 240, height: 240, playbackMode: .loop, mode: .direct(cachePathPrefix: nil))
|
||||
self.qrIconNode.visibility = true
|
||||
|
||||
super.init()
|
||||
|
@ -656,7 +656,7 @@ public final class ReactionContextNode: ASDisplayNode, UIScrollViewDelegate {
|
||||
transition.updateBounds(node: itemNode, bounds: CGRect(origin: CGPoint(), size: expandedFrame.size))
|
||||
itemNode.updateLayout(size: expandedFrame.size, isExpanded: true, largeExpanded: self.didTriggerExpandedReaction, isPreviewing: false, transition: transition)
|
||||
|
||||
let additionalAnimationNode = AnimatedStickerNode()
|
||||
let additionalAnimationNode = DefaultAnimatedStickerNodeImpl()
|
||||
|
||||
let additionalAnimation: TelegramMediaFile
|
||||
if self.didTriggerExpandedReaction {
|
||||
@ -1064,7 +1064,7 @@ public final class StandaloneReactionAnimation: ASDisplayNode {
|
||||
|
||||
itemNode.updateLayout(size: expandedFrame.size, isExpanded: true, largeExpanded: isLarge, isPreviewing: false, transition: .immediate)
|
||||
|
||||
let additionalAnimationNode = AnimatedStickerNode()
|
||||
let additionalAnimationNode = DefaultAnimatedStickerNodeImpl()
|
||||
|
||||
let additionalAnimation: TelegramMediaFile
|
||||
if isLarge && !forceSmallEffectAnimation {
|
||||
|
@ -72,11 +72,11 @@ public final class ReactionNode: ASDisplayNode, ReactionItemNode {
|
||||
self.item = item
|
||||
self.hasAppearAnimation = hasAppearAnimation
|
||||
|
||||
self.staticAnimationNode = AnimatedStickerNode()
|
||||
self.staticAnimationNode = DefaultAnimatedStickerNodeImpl()
|
||||
|
||||
if hasAppearAnimation {
|
||||
self.staticAnimationNode.isHidden = true
|
||||
self.animateInAnimationNode = AnimatedStickerNode()
|
||||
self.animateInAnimationNode = DefaultAnimatedStickerNodeImpl()
|
||||
}
|
||||
|
||||
super.init()
|
||||
@ -144,9 +144,9 @@ public final class ReactionNode: ASDisplayNode, ReactionItemNode {
|
||||
self.staticAnimationNode.completed = { [weak self] _ in
|
||||
self?.mainAnimationCompletion?()
|
||||
}
|
||||
self.staticAnimationNode.play(fromIndex: 0)
|
||||
self.staticAnimationNode.play(firstFrame: false, fromIndex: 0)
|
||||
} else if isExpanded, self.animationNode == nil {
|
||||
let animationNode = AnimatedStickerNode()
|
||||
let animationNode = DefaultAnimatedStickerNodeImpl()
|
||||
animationNode.automaticallyLoadFirstFrame = true
|
||||
self.animationNode = animationNode
|
||||
self.addSubnode(animationNode)
|
||||
@ -235,7 +235,7 @@ public final class ReactionNode: ASDisplayNode, ReactionItemNode {
|
||||
if self.animationNode == nil {
|
||||
if isPreviewing {
|
||||
if self.stillAnimationNode == nil {
|
||||
let stillAnimationNode = AnimatedStickerNode()
|
||||
let stillAnimationNode = DefaultAnimatedStickerNodeImpl()
|
||||
self.stillAnimationNode = stillAnimationNode
|
||||
self.addSubnode(stillAnimationNode)
|
||||
|
||||
|
@ -1015,7 +1015,7 @@ private class StorageUsageClearProgressOverlayNode: ASDisplayNode, ActionSheetGr
|
||||
init(presentationData: PresentationData) {
|
||||
self.presentationData = presentationData
|
||||
|
||||
self.animationNode = AnimatedStickerNode()
|
||||
self.animationNode = DefaultAnimatedStickerNodeImpl()
|
||||
self.animationNode.setup(source: AnimatedStickerNodeLocalFileSource(name: "ClearCache"), width: 256, height: 256, playbackMode: .loop, mode: .direct(cachePathPrefix: nil))
|
||||
self.animationNode.visibility = true
|
||||
|
||||
|
@ -58,7 +58,7 @@ final class PrivacyIntroControllerNode: ViewControllerTracingNode {
|
||||
self.proceedAction = proceedAction
|
||||
|
||||
self.iconNode = ASImageNode()
|
||||
self.animationNode = AnimatedStickerNode()
|
||||
self.animationNode = DefaultAnimatedStickerNodeImpl()
|
||||
|
||||
self.titleNode = ASTextNode()
|
||||
self.textNode = ASTextNode()
|
||||
|
@ -84,7 +84,7 @@ class RecentSessionsHeaderItemNode: ListViewItemNode {
|
||||
self.titleNode.contentMode = .left
|
||||
self.titleNode.contentsScale = UIScreen.main.scale
|
||||
|
||||
self.animationNode = AnimatedStickerNode()
|
||||
self.animationNode = DefaultAnimatedStickerNodeImpl()
|
||||
|
||||
self.buttonNode = SolidRoundedButtonNode(theme: SolidRoundedButtonTheme(backgroundColor: .black, foregroundColor: .white), fontSize: 16.0, height: 50.0, cornerRadius: 11.0)
|
||||
|
||||
@ -143,7 +143,7 @@ class RecentSessionsHeaderItemNode: ListViewItemNode {
|
||||
strongSelf.animationNode.setup(source: AnimatedStickerNodeLocalFileSource(name: item.animationName), width: 256, height: 256, playbackMode: .still(.start), mode: .direct(cachePathPrefix: nil))
|
||||
strongSelf.animationNode.visibility = true
|
||||
Queue.mainQueue().after(0.3) {
|
||||
strongSelf.animationNode.play()
|
||||
strongSelf.animationNode.play(firstFrame: false, fromIndex: nil)
|
||||
}
|
||||
}
|
||||
strongSelf.item = item
|
||||
|
@ -134,6 +134,14 @@ class ReactionChatPreviewItemNode: ListViewItemNode {
|
||||
}
|
||||
}
|
||||
|
||||
override func hitTest(_ point: CGPoint, with event: UIEvent?) -> UIView? {
|
||||
if self.bounds.contains(point) {
|
||||
return self.view
|
||||
} else {
|
||||
return nil
|
||||
}
|
||||
}
|
||||
|
||||
private func beginReactionAnimation() {
|
||||
if let item = self.item, let updatedReaction = item.reaction, let availableReactions = item.availableReactions, let messageNode = self.messageNode as? ChatMessageItemNodeProtocol {
|
||||
if let targetView = messageNode.targetReactionView(value: updatedReaction) {
|
||||
|
@ -302,7 +302,7 @@ private final class ThemeCarouselThemeItemIconNode : ListViewItemNode {
|
||||
Queue.mainQueue().after(0.1) {
|
||||
if !wasSelected {
|
||||
animatedStickerNode.seekTo(.frameIndex(0))
|
||||
animatedStickerNode.play()
|
||||
animatedStickerNode.play(firstFrame: false, fromIndex: nil)
|
||||
|
||||
let scale: CGFloat = 2.6
|
||||
animatedStickerNode.transform = CATransform3DMakeScale(scale, scale, 1.0)
|
||||
@ -417,7 +417,7 @@ private final class ThemeCarouselThemeItemIconNode : ListViewItemNode {
|
||||
if let current = strongSelf.animatedStickerNode {
|
||||
animatedStickerNode = current
|
||||
} else {
|
||||
animatedStickerNode = AnimatedStickerNode()
|
||||
animatedStickerNode = DefaultAnimatedStickerNodeImpl()
|
||||
animatedStickerNode.started = { [weak self] in
|
||||
self?.emojiImageNode.isHidden = true
|
||||
}
|
||||
|
@ -258,7 +258,7 @@ private final class ThemeGridThemeItemIconNode : ASDisplayNode {
|
||||
if let current = self.animatedStickerNode {
|
||||
animatedStickerNode = current
|
||||
} else {
|
||||
animatedStickerNode = AnimatedStickerNode()
|
||||
animatedStickerNode = DefaultAnimatedStickerNodeImpl()
|
||||
animatedStickerNode.started = { [weak self] in
|
||||
self?.emojiImageNode.isHidden = true
|
||||
}
|
||||
|
@ -212,11 +212,11 @@ public final class ShareProlongedLoadingContainerNode: ASDisplayNode, ShareConte
|
||||
self.theme = theme
|
||||
self.strings = strings
|
||||
|
||||
self.animationNode = AnimatedStickerNode()
|
||||
self.animationNode = DefaultAnimatedStickerNodeImpl()
|
||||
self.animationNode.setup(source: AnimatedStickerNodeLocalFileSource(name: "ShareProgress"), width: 384, height: 384, playbackMode: .loop, mode: .direct(cachePathPrefix: nil))
|
||||
self.animationNode.visibility = true
|
||||
|
||||
self.doneAnimationNode = AnimatedStickerNode()
|
||||
self.doneAnimationNode = DefaultAnimatedStickerNodeImpl()
|
||||
self.doneAnimationNode.setup(source: AnimatedStickerNodeLocalFileSource(name: "ShareDone"), width: 384, height: 384, playbackMode: .once, mode: .direct(cachePathPrefix: nil))
|
||||
self.doneAnimationNode.visibility = false
|
||||
self.doneAnimationNode.isHidden = true
|
||||
|
@ -264,7 +264,7 @@ class DiceAnimatedStickerNode: ASDisplayNode {
|
||||
self.account = account
|
||||
self.intrinsicSize = size
|
||||
|
||||
self.animationNode = AnimatedStickerNode()
|
||||
self.animationNode = DefaultAnimatedStickerNodeImpl()
|
||||
self.animationNode.autoplay = true
|
||||
|
||||
super.init()
|
||||
|
@ -751,6 +751,8 @@ public final class SolidRoundedButtonView: UIView {
|
||||
}
|
||||
|
||||
self.buttonNode = HighlightTrackingButton()
|
||||
self.buttonNode.isMultipleTouchEnabled = false
|
||||
self.buttonNode.isExclusiveTouch = true
|
||||
|
||||
self.titleNode = ImmediateTextView()
|
||||
self.titleNode.isUserInteractionEnabled = false
|
||||
|
@ -56,7 +56,7 @@ final class StatsEmptyStateItemNode: ItemListControllerEmptyStateItemNode {
|
||||
init(item: StatsEmptyStateItem) {
|
||||
self.item = item
|
||||
|
||||
self.animationNode = AnimatedStickerNode()
|
||||
self.animationNode = DefaultAnimatedStickerNodeImpl()
|
||||
self.animationNode.setup(source: AnimatedStickerNodeLocalFileSource(name: "Charts"), width: 192, height: 192, playbackMode: .loop, mode: .direct(cachePathPrefix: nil))
|
||||
self.animationNode.visibility = true
|
||||
|
||||
|
@ -223,7 +223,7 @@ final class StickerPackPreviewGridItemNode: GridItemNode {
|
||||
}
|
||||
|
||||
if self.animationNode == nil {
|
||||
let animationNode = AnimatedStickerNode()
|
||||
let animationNode = DefaultAnimatedStickerNodeImpl()
|
||||
self.animationNode = animationNode
|
||||
self.insertSubnode(animationNode, aboveSubnode: self.imageNode)
|
||||
animationNode.started = { [weak self] in
|
||||
@ -243,7 +243,7 @@ final class StickerPackPreviewGridItemNode: GridItemNode {
|
||||
}
|
||||
}
|
||||
let fittedDimensions = dimensions.cgSize.aspectFitted(CGSize(width: 160.0, height: 160.0))
|
||||
self.animationNode?.setup(source: AnimatedStickerResourceSource(account: account, resource: stickerItem.file.resource, isVideo: stickerItem.file.isVideoSticker), width: Int(fittedDimensions.width), height: Int(fittedDimensions.height), mode: .cached)
|
||||
self.animationNode?.setup(source: AnimatedStickerResourceSource(account: account, resource: stickerItem.file.resource, isVideo: stickerItem.file.isVideoSticker), width: Int(fittedDimensions.width), height: Int(fittedDimensions.height), playbackMode: .loop, mode: .cached)
|
||||
|
||||
self.animationNode?.visibility = visibility
|
||||
|
||||
|
@ -116,7 +116,7 @@ public final class StickerPreviewPeekContentNode: ASDisplayNode, PeekControllerC
|
||||
let isPremiumSticker = item.file.isPremiumSticker
|
||||
|
||||
if item.file.isAnimatedSticker || item.file.isVideoSticker {
|
||||
let animationNode = AnimatedStickerNode()
|
||||
let animationNode = DefaultAnimatedStickerNodeImpl()
|
||||
self.animationNode = animationNode
|
||||
|
||||
let dimensions = item.file.dimensions ?? PixelDimensions(width: 512, height: 512)
|
||||
@ -130,7 +130,7 @@ public final class StickerPreviewPeekContentNode: ASDisplayNode, PeekControllerC
|
||||
self.effectDisposable.set(freeMediaFileResourceInteractiveFetched(account: account, fileReference: .standalone(media: item.file), resource: effect.resource).start())
|
||||
|
||||
let source = AnimatedStickerResourceSource(account: account, resource: effect.resource, fitzModifier: nil)
|
||||
let additionalAnimationNode = AnimatedStickerNode()
|
||||
let additionalAnimationNode = DefaultAnimatedStickerNodeImpl()
|
||||
additionalAnimationNode.setup(source: source, width: Int(fittedDimensions.width * 2.0), height: Int(fittedDimensions.height * 2.0), playbackMode: .once, mode: .direct(cachePathPrefix: nil))
|
||||
additionalAnimationNode.visibility = true
|
||||
self.additionalAnimationNode = additionalAnimationNode
|
||||
@ -153,8 +153,8 @@ public final class StickerPreviewPeekContentNode: ASDisplayNode, PeekControllerC
|
||||
animationNode.completed = { [weak self] _ in
|
||||
if let strongSelf = self, let animationNode = strongSelf.animationNode, let additionalAnimationNode = strongSelf.additionalAnimationNode {
|
||||
Queue.mainQueue().async {
|
||||
animationNode.play()
|
||||
additionalAnimationNode.play()
|
||||
animationNode.play(firstFrame: false, fromIndex: nil)
|
||||
additionalAnimationNode.play(firstFrame: false, fromIndex: nil)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -125,7 +125,7 @@ private final class TabBarItemNode: ASDisplayNode {
|
||||
|
||||
self.animationContainerNode = ASDisplayNode()
|
||||
|
||||
self.animationNode = AnimatedStickerNode()
|
||||
self.animationNode = DefaultAnimatedStickerNodeImpl()
|
||||
self.animationNode.autoplay = true
|
||||
self.animationNode.automaticallyLoadLastFrame = true
|
||||
|
||||
@ -788,7 +788,7 @@ class TabBarNode: ASDisplayNode {
|
||||
self.itemSelected(closestNode.0, longTap, [container.imageNode.imageNode, container.imageNode.textImageNode, container.badgeContainerNode])
|
||||
if previousSelectedIndex != closestNode.0 {
|
||||
if let selectedIndex = self.selectedIndex, let _ = self.tabBarItems[selectedIndex].item.animationName {
|
||||
container.imageNode.animationNode.play()
|
||||
container.imageNode.animationNode.play(firstFrame: false, fromIndex: nil)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -917,7 +917,7 @@ fileprivate let parsers: [Int32 : (BufferReader) -> Any?] = {
|
||||
dict[415997816] = { return Api.help.InviteText.parse_inviteText($0) }
|
||||
dict[-1600596305] = { return Api.help.PassportConfig.parse_passportConfig($0) }
|
||||
dict[-1078332329] = { return Api.help.PassportConfig.parse_passportConfigNotModified($0) }
|
||||
dict[-533328101] = { return Api.help.PremiumPromo.parse_premiumPromo($0) }
|
||||
dict[-1974518743] = { return Api.help.PremiumPromo.parse_premiumPromo($0) }
|
||||
dict[-1942390465] = { return Api.help.PromoData.parse_promoData($0) }
|
||||
dict[-1728664459] = { return Api.help.PromoData.parse_promoDataEmpty($0) }
|
||||
dict[235081943] = { return Api.help.RecentMeUrls.parse_recentMeUrls($0) }
|
||||
|
@ -212,13 +212,13 @@ public extension Api.help {
|
||||
}
|
||||
public extension Api.help {
|
||||
enum PremiumPromo: TypeConstructorDescription {
|
||||
case premiumPromo(statusText: String, statusEntities: [Api.MessageEntity], videoSections: [String], videos: [Api.Document], currency: String, monthlyAmount: Int64)
|
||||
case premiumPromo(statusText: String, statusEntities: [Api.MessageEntity], videoSections: [String], videos: [Api.Document], currency: String, monthlyAmount: Int64, users: [Api.User])
|
||||
|
||||
public func serialize(_ buffer: Buffer, _ boxed: Swift.Bool) {
|
||||
switch self {
|
||||
case .premiumPromo(let statusText, let statusEntities, let videoSections, let videos, let currency, let monthlyAmount):
|
||||
case .premiumPromo(let statusText, let statusEntities, let videoSections, let videos, let currency, let monthlyAmount, let users):
|
||||
if boxed {
|
||||
buffer.appendInt32(-533328101)
|
||||
buffer.appendInt32(-1974518743)
|
||||
}
|
||||
serializeString(statusText, buffer: buffer, boxed: false)
|
||||
buffer.appendInt32(481674261)
|
||||
@ -238,14 +238,19 @@ public extension Api.help {
|
||||
}
|
||||
serializeString(currency, buffer: buffer, boxed: false)
|
||||
serializeInt64(monthlyAmount, buffer: buffer, boxed: false)
|
||||
buffer.appendInt32(481674261)
|
||||
buffer.appendInt32(Int32(users.count))
|
||||
for item in users {
|
||||
item.serialize(buffer, true)
|
||||
}
|
||||
break
|
||||
}
|
||||
}
|
||||
|
||||
public func descriptionFields() -> (String, [(String, Any)]) {
|
||||
switch self {
|
||||
case .premiumPromo(let statusText, let statusEntities, let videoSections, let videos, let currency, let monthlyAmount):
|
||||
return ("premiumPromo", [("statusText", String(describing: statusText)), ("statusEntities", String(describing: statusEntities)), ("videoSections", String(describing: videoSections)), ("videos", String(describing: videos)), ("currency", String(describing: currency)), ("monthlyAmount", String(describing: monthlyAmount))])
|
||||
case .premiumPromo(let statusText, let statusEntities, let videoSections, let videos, let currency, let monthlyAmount, let users):
|
||||
return ("premiumPromo", [("statusText", String(describing: statusText)), ("statusEntities", String(describing: statusEntities)), ("videoSections", String(describing: videoSections)), ("videos", String(describing: videos)), ("currency", String(describing: currency)), ("monthlyAmount", String(describing: monthlyAmount)), ("users", String(describing: users))])
|
||||
}
|
||||
}
|
||||
|
||||
@ -268,14 +273,19 @@ public extension Api.help {
|
||||
_5 = parseString(reader)
|
||||
var _6: Int64?
|
||||
_6 = reader.readInt64()
|
||||
var _7: [Api.User]?
|
||||
if let _ = reader.readInt32() {
|
||||
_7 = Api.parseVector(reader, elementSignature: 0, elementType: Api.User.self)
|
||||
}
|
||||
let _c1 = _1 != nil
|
||||
let _c2 = _2 != nil
|
||||
let _c3 = _3 != nil
|
||||
let _c4 = _4 != nil
|
||||
let _c5 = _5 != nil
|
||||
let _c6 = _6 != nil
|
||||
if _c1 && _c2 && _c3 && _c4 && _c5 && _c6 {
|
||||
return Api.help.PremiumPromo.premiumPromo(statusText: _1!, statusEntities: _2!, videoSections: _3!, videos: _4!, currency: _5!, monthlyAmount: _6!)
|
||||
let _c7 = _7 != nil
|
||||
if _c1 && _c2 && _c3 && _c4 && _c5 && _c6 && _c7 {
|
||||
return Api.help.PremiumPromo.premiumPromo(statusText: _1!, statusEntities: _2!, videoSections: _3!, videos: _4!, currency: _5!, monthlyAmount: _6!, users: _7!)
|
||||
}
|
||||
else {
|
||||
return nil
|
||||
|
@ -6227,13 +6227,12 @@ public extension Api.functions.messages {
|
||||
}
|
||||
}
|
||||
public extension Api.functions.payments {
|
||||
static func assignAppStoreTransaction(flags: Int32, transactionId: String, receipt: Buffer) -> (FunctionDescription, Buffer, DeserializeFunctionResponse<Api.Updates>) {
|
||||
static func assignAppStoreTransaction(flags: Int32, receipt: Buffer) -> (FunctionDescription, Buffer, DeserializeFunctionResponse<Api.Updates>) {
|
||||
let buffer = Buffer()
|
||||
buffer.appendInt32(267129798)
|
||||
buffer.appendInt32(224186320)
|
||||
serializeInt32(flags, buffer: buffer, boxed: false)
|
||||
serializeString(transactionId, buffer: buffer, boxed: false)
|
||||
serializeBytes(receipt, buffer: buffer, boxed: false)
|
||||
return (FunctionDescription(name: "payments.assignAppStoreTransaction", parameters: [("flags", String(describing: flags)), ("transactionId", String(describing: transactionId)), ("receipt", String(describing: receipt))]), buffer, DeserializeFunctionResponse { (buffer: Buffer) -> Api.Updates? in
|
||||
return (FunctionDescription(name: "payments.assignAppStoreTransaction", parameters: [("flags", String(describing: flags)), ("receipt", String(describing: receipt))]), buffer, DeserializeFunctionResponse { (buffer: Buffer) -> Api.Updates? in
|
||||
let reader = BufferReader(buffer)
|
||||
var result: Api.Updates?
|
||||
if let signature = reader.readInt32() {
|
||||
|
@ -1369,6 +1369,8 @@ public func standaloneStateManager(
|
||||
Logger.shared.log("StandaloneStateManager", "Received settings")
|
||||
|
||||
return postbox.transaction { transaction -> (PostboxCoding?, LocalizationSettings?, ProxySettings?, NetworkSettings?) in
|
||||
Logger.shared.log("StandaloneStateManager", "Getting state")
|
||||
|
||||
let state = transaction.getState()
|
||||
|
||||
return (state, localizationSettings, proxySettings, transaction.getPreferencesEntry(key: PreferencesKeys.networkSettings)?.get(NetworkSettings.self))
|
||||
|
@ -19,6 +19,13 @@ func updatePremiumPromoConfigurationOnce(postbox: Postbox, network: Network) ->
|
||||
return .complete()
|
||||
}
|
||||
return postbox.transaction { transaction -> Void in
|
||||
if case let .premiumPromo(_, _, _, _, _, _, apiUsers) = result {
|
||||
let users = apiUsers.map { TelegramUser(user: $0) }
|
||||
updatePeers(transaction: transaction, peers: users, update: { _, updated -> Peer in
|
||||
return updated
|
||||
})
|
||||
}
|
||||
|
||||
updatePremiumPromoConfiguration(transaction: transaction, { configuration -> PremiumPromoConfiguration in
|
||||
return PremiumPromoConfiguration(apiPremiumPromo: result)
|
||||
})
|
||||
@ -54,7 +61,7 @@ private func updatePremiumPromoConfiguration(transaction: Transaction, _ f: (Pre
|
||||
private extension PremiumPromoConfiguration {
|
||||
init(apiPremiumPromo: Api.help.PremiumPromo) {
|
||||
switch apiPremiumPromo {
|
||||
case let .premiumPromo(statusText, statusEntities, videoSections, videoFiles, currency, monthlyAmount):
|
||||
case let .premiumPromo(statusText, statusEntities, videoSections, videoFiles, currency, monthlyAmount, _):
|
||||
self.status = statusText
|
||||
self.statusEntities = messageTextEntitiesFromApiEntities(statusEntities)
|
||||
self.currency = currency
|
||||
|
@ -8,12 +8,12 @@ public enum AssignAppStoreTransactionError {
|
||||
case generic
|
||||
}
|
||||
|
||||
func _internal_assignAppStoreTransaction(account: Account, transactionId: String, receipt: Data, restore: Bool) -> Signal<Never, AssignAppStoreTransactionError> {
|
||||
func _internal_sendAppStoreReceipt(account: Account, receipt: Data, restore: Bool) -> Signal<Never, AssignAppStoreTransactionError> {
|
||||
var flags: Int32 = 0
|
||||
if restore {
|
||||
flags |= (1 << 0)
|
||||
}
|
||||
return account.network.request(Api.functions.payments.assignAppStoreTransaction(flags: flags, transactionId: transactionId, receipt: Buffer(data: receipt)))
|
||||
return account.network.request(Api.functions.payments.assignAppStoreTransaction(flags: flags, receipt: Buffer(data: receipt)))
|
||||
|> mapError { _ -> AssignAppStoreTransactionError in
|
||||
return .generic
|
||||
}
|
||||
@ -23,6 +23,10 @@ func _internal_assignAppStoreTransaction(account: Account, transactionId: String
|
||||
}
|
||||
}
|
||||
|
||||
public enum RestoreAppStoreReceiptError {
|
||||
case generic
|
||||
}
|
||||
|
||||
func _internal_canPurchasePremium(account: Account) -> Signal<Bool, NoError> {
|
||||
return account.network.request(Api.functions.payments.canPurchasePremium())
|
||||
|> map { result -> Bool in
|
||||
|
@ -38,8 +38,8 @@ public extension TelegramEngine {
|
||||
return _internal_clearBotPaymentInfo(network: self.account.network, info: info)
|
||||
}
|
||||
|
||||
public func assignAppStoreTransaction(transactionId: String, receipt: Data, restore: Bool) -> Signal<Never, AssignAppStoreTransactionError> {
|
||||
return _internal_assignAppStoreTransaction(account: self.account, transactionId: transactionId, receipt: receipt, restore: restore)
|
||||
public func sendAppStoreReceipt(receipt: Data, restore: Bool) -> Signal<Never, AssignAppStoreTransactionError> {
|
||||
return _internal_sendAppStoreReceipt(account: self.account, receipt: receipt, restore: restore)
|
||||
}
|
||||
|
||||
public func canPurchasePremium() -> Signal<Bool, NoError> {
|
||||
|
@ -72,7 +72,7 @@ public final class PermissionContentNode: ASDisplayNode {
|
||||
self.iconNode.displaysAsynchronously = false
|
||||
|
||||
if case let .animation(animation) = icon {
|
||||
self.animationNode = AnimatedStickerNode()
|
||||
self.animationNode = DefaultAnimatedStickerNodeImpl()
|
||||
|
||||
self.animationNode?.setup(source: AnimatedStickerNodeLocalFileSource(name: animation), width: 320, height: 320, playbackMode: .once, mode: .direct(cachePathPrefix: nil))
|
||||
self.animationNode?.visibility = true
|
||||
|
@ -473,7 +473,19 @@ private func extractAccountManagerState(records: AccountRecordsView<TelegramAcco
|
||||
|
||||
TempBox.initializeShared(basePath: rootPath, processType: "app", launchSpecificId: Int64.random(in: Int64.min ... Int64.max))
|
||||
|
||||
let logsPath = rootPath + "/logs"
|
||||
let legacyLogs: [String] = [
|
||||
"logs",
|
||||
"broadcast-logs",
|
||||
"siri-logs",
|
||||
"widget-logs",
|
||||
"notificationcontent-logs",
|
||||
"notification-logs"
|
||||
]
|
||||
for item in legacyLogs {
|
||||
let _ = try? FileManager.default.removeItem(atPath: "\(rootPath)/\(item)")
|
||||
}
|
||||
|
||||
let logsPath = rootPath + "/logs/app-logs"
|
||||
let _ = try? FileManager.default.createDirectory(atPath: logsPath, withIntermediateDirectories: true, attributes: nil)
|
||||
Logger.setSharedLogger(Logger(rootPath: rootPath, basePath: logsPath))
|
||||
|
||||
|
@ -62,7 +62,7 @@ final class AttachmentFileEmptyStateItemNode: ItemListControllerEmptyStateItemNo
|
||||
init(item: AttachmentFileEmptyStateItem) {
|
||||
self.item = item
|
||||
|
||||
self.animationNode = AnimatedStickerNode()
|
||||
self.animationNode = DefaultAnimatedStickerNodeImpl()
|
||||
self.animationNode.setup(source: AnimatedStickerNodeLocalFileSource(name: "Files"), width: 320, height: 320, playbackMode: .loop, mode: .direct(cachePathPrefix: nil))
|
||||
self.animationNode.visibility = true
|
||||
|
||||
|
@ -77,6 +77,7 @@ import ChatSendMessageActionUI
|
||||
import ChatTextLinkEditUI
|
||||
import WebUI
|
||||
import PremiumUI
|
||||
import ImageTransparency
|
||||
|
||||
#if DEBUG
|
||||
import os.signpost
|
||||
@ -15253,6 +15254,18 @@ public final class ChatControllerImpl: TelegramBaseController, ChatController, G
|
||||
}
|
||||
let images = imageItems as! [UIImage]
|
||||
|
||||
strongSelf.chatDisplayNode.updateDropInteraction(isActive: false)
|
||||
if images.count == 1, let image = images.first, let cgImage = image.cgImage {
|
||||
let maxSide = max(image.size.width, image.size.height)
|
||||
if maxSide.isZero {
|
||||
return
|
||||
}
|
||||
let aspectRatio = min(image.size.width, image.size.height) / maxSide
|
||||
if (imageHasTransparency(cgImage) && aspectRatio > 0.2) {
|
||||
strongSelf.enqueueStickerImage(image, isMemoji: false)
|
||||
return
|
||||
}
|
||||
}
|
||||
strongSelf.chatDisplayNode.updateDropInteraction(isActive: false)
|
||||
strongSelf.displayPasteMenu(images)
|
||||
}
|
||||
|
@ -243,10 +243,10 @@ final class ChatMediaInputMetaSectionItemNode: ListViewItemNode {
|
||||
if let current = self.animatedStickerNode {
|
||||
animatedStickerNode = current
|
||||
} else {
|
||||
animatedStickerNode = AnimatedStickerNode()
|
||||
animatedStickerNode = DefaultAnimatedStickerNodeImpl()
|
||||
self.animatedStickerNode = animatedStickerNode
|
||||
self.scalingNode.addSubnode(animatedStickerNode)
|
||||
animatedStickerNode.setup(source: AnimatedStickerResourceSource(account: account, resource: file.resource), width: 128, height: 128, mode: .cached)
|
||||
animatedStickerNode.setup(source: AnimatedStickerResourceSource(account: account, resource: file.resource), width: 128, height: 128, playbackMode: .loop, mode: .cached)
|
||||
}
|
||||
animatedStickerNode.visibility = self.visibilityStatus && loopAnimatedStickers
|
||||
|
||||
|
@ -275,7 +275,7 @@ final class ChatMediaInputStickerGridItemNode: GridItemNode {
|
||||
if let dimensions = item.stickerItem.file.dimensions {
|
||||
if item.stickerItem.file.isAnimatedSticker || item.stickerItem.file.isVideoSticker {
|
||||
if self.animationNode == nil {
|
||||
let animationNode = AnimatedStickerNode()
|
||||
let animationNode = DefaultAnimatedStickerNodeImpl()
|
||||
animationNode.view.addGestureRecognizer(UITapGestureRecognizer(target: self, action: #selector(self.imageNodeTap(_:))))
|
||||
self.animationNode = animationNode
|
||||
animationNode.started = { [weak self] in
|
||||
@ -445,7 +445,7 @@ final class ChatMediaInputStickerGridItemNode: GridItemNode {
|
||||
let dimensions = item.stickerItem.file.dimensions ?? PixelDimensions(width: 512, height: 512)
|
||||
let fitSize = item.large ? CGSize(width: 384.0, height: 384.0) : CGSize(width: 160.0, height: 160.0)
|
||||
let fittedDimensions = dimensions.cgSize.aspectFitted(fitSize)
|
||||
animationNode.setup(source: AnimatedStickerResourceSource(account: item.account, resource: item.stickerItem.file.resource, isVideo: item.stickerItem.file.isVideoSticker), width: Int(fittedDimensions.width), height: Int(fittedDimensions.height), mode: .cached)
|
||||
animationNode.setup(source: AnimatedStickerResourceSource(account: item.account, resource: item.stickerItem.file.resource, isVideo: item.stickerItem.file.isVideoSticker), width: Int(fittedDimensions.width), height: Int(fittedDimensions.height), playbackMode: .loop, mode: .cached)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -233,7 +233,7 @@ final class ChatMediaInputStickerPackItemNode: ListViewItemNode {
|
||||
if let current = self.animatedStickerNode {
|
||||
animatedStickerNode = current
|
||||
} else {
|
||||
animatedStickerNode = AnimatedStickerNode()
|
||||
animatedStickerNode = DefaultAnimatedStickerNodeImpl()
|
||||
animatedStickerNode.started = { [weak self] in
|
||||
self?.imageNode.isHidden = true
|
||||
self?.removePlaceholder(animated: false)
|
||||
@ -244,7 +244,7 @@ final class ChatMediaInputStickerPackItemNode: ListViewItemNode {
|
||||
} else {
|
||||
self.scalingNode.addSubnode(animatedStickerNode)
|
||||
}
|
||||
animatedStickerNode.setup(source: AnimatedStickerResourceSource(account: account, resource: resource, isVideo: isVideo), width: 128, height: 128, mode: .cached)
|
||||
animatedStickerNode.setup(source: AnimatedStickerResourceSource(account: account, resource: resource, isVideo: isVideo), width: 128, height: 128, playbackMode: .loop, mode: .cached)
|
||||
}
|
||||
animatedStickerNode.visibility = self.visibilityStatus && loopAnimatedStickers
|
||||
}
|
||||
@ -367,7 +367,7 @@ final class ChatMediaInputStickerPackItemNode: ListViewItemNode {
|
||||
case let .animated(resource, dimensions, isVideo):
|
||||
imageSize = dimensions.cgSize.aspectFitted(boundingImageSize)
|
||||
|
||||
let animatedStickerNode = AnimatedStickerNode()
|
||||
let animatedStickerNode = DefaultAnimatedStickerNodeImpl()
|
||||
animatedStickerNode.setup(source: AnimatedStickerResourceSource(account: account, resource: resource, isVideo: isVideo), width: 128, height: 128, mode: .cached)
|
||||
animatedStickerNode.visibility = self.visibilityStatus && loopAnimatedStickers
|
||||
scalingNode.addSubnode(animatedStickerNode)
|
||||
|
@ -39,7 +39,7 @@ protocol GenericAnimatedStickerNode: ASDisplayNode {
|
||||
func setFrameIndex(_ frameIndex: Int)
|
||||
}
|
||||
|
||||
extension AnimatedStickerNode: GenericAnimatedStickerNode {
|
||||
extension DefaultAnimatedStickerNodeImpl: GenericAnimatedStickerNode {
|
||||
func setFrameIndex(_ frameIndex: Int) {
|
||||
self.stop()
|
||||
self.play(fromIndex: frameIndex)
|
||||
@ -440,7 +440,7 @@ class ChatMessageAnimatedStickerItemNode: ChatMessageItemView {
|
||||
self.animationNode = animationNode
|
||||
}
|
||||
} else {
|
||||
let animationNode = AnimatedStickerNode(useMetalCache: item.context.sharedContext.immediateExperimentalUISettings.acceleratedStickers)
|
||||
let animationNode = DefaultAnimatedStickerNodeImpl(useMetalCache: item.context.sharedContext.immediateExperimentalUISettings.acceleratedStickers)
|
||||
animationNode.started = { [weak self] in
|
||||
if let strongSelf = self {
|
||||
strongSelf.imageNode.alpha = 0.0
|
||||
@ -1684,7 +1684,7 @@ class ChatMessageAnimatedStickerItemNode: ChatMessageItemView {
|
||||
}
|
||||
} else {
|
||||
let pathPrefix = item.context.account.postbox.mediaBox.shortLivedResourceCachePathPrefix(resource.id)
|
||||
let additionalAnimationNode = AnimatedStickerNode()
|
||||
let additionalAnimationNode = DefaultAnimatedStickerNodeImpl()
|
||||
additionalAnimationNode.setup(source: source, width: Int(animationSize.width * 1.6), height: Int(animationSize.height * 1.6), playbackMode: .once, mode: .direct(cachePathPrefix: pathPrefix))
|
||||
var animationFrame: CGRect
|
||||
if isStickerEffect {
|
||||
@ -1941,7 +1941,7 @@ class ChatMessageAnimatedStickerItemNode: ChatMessageItemView {
|
||||
|
||||
switch status.status {
|
||||
case .playing:
|
||||
animationNode?.play()
|
||||
animationNode?.play(firstFrame: false, fromIndex: nil)
|
||||
strongSelf.mediaStatusDisposable.set(nil)
|
||||
default:
|
||||
break
|
||||
@ -1955,7 +1955,7 @@ class ChatMessageAnimatedStickerItemNode: ChatMessageItemView {
|
||||
if let haptic = haptic, !haptic.active {
|
||||
haptic.start(time: 0.0)
|
||||
}
|
||||
animationNode?.play()
|
||||
animationNode?.play(firstFrame: false, fromIndex: nil)
|
||||
}
|
||||
})
|
||||
}
|
||||
|
@ -2837,8 +2837,8 @@ class ChatMessageBubbleItemNode: ChatMessageItemView, ChatMessagePreviewItemNode
|
||||
|
||||
strongSelf.backgroundNode.updateLayout(size: backgroundFrame.size, transition: animation)
|
||||
animation.animator.updateFrame(layer: strongSelf.backgroundWallpaperNode.layer, frame: backgroundFrame, completion: nil)
|
||||
strongSelf.shadowNode.updateLayout(backgroundFrame: backgroundFrame, transition: animation.transition)
|
||||
strongSelf.backgroundWallpaperNode.updateFrame(backgroundFrame, transition: animation.transition)
|
||||
strongSelf.shadowNode.updateLayout(backgroundFrame: backgroundFrame, animator: animation.animator)
|
||||
strongSelf.backgroundWallpaperNode.updateFrame(backgroundFrame, animator: animation.animator)
|
||||
}
|
||||
|
||||
if let _ = strongSelf.backgroundNode.type {
|
||||
|
@ -659,12 +659,12 @@ final class ChatMessageInteractiveFileNode: ASDisplayNode {
|
||||
case let .success(text, isPending):
|
||||
textString = NSAttributedString(string: text, font: textFont, textColor: messageTheme.primaryTextColor)
|
||||
|
||||
#if DEBUG
|
||||
/*#if DEBUG
|
||||
var isPending = isPending
|
||||
if "".isEmpty {
|
||||
isPending = true
|
||||
}
|
||||
#endif
|
||||
#endif*/
|
||||
|
||||
if isPending {
|
||||
let modifiedString = NSMutableAttributedString(attributedString: textString!)
|
||||
@ -1116,7 +1116,7 @@ final class ChatMessageInteractiveFileNode: ASDisplayNode {
|
||||
let _ = waveformView.update(
|
||||
transition: waveformTransition.withUserData(ComponentHostViewSkipSettingFrame()),
|
||||
component: AnyComponent(AudioWaveformComponent(
|
||||
backgroundColor: waveformColor,
|
||||
backgroundColor: isTranscriptionInProgress ? messageTheme.mediaInactiveControlColor : waveformColor,
|
||||
foregroundColor: messageTheme.mediaActiveControlColor,
|
||||
shimmerColor: isTranscriptionInProgress ? messageTheme.mediaActiveControlColor : nil,
|
||||
samples: audioWaveform?.samples ?? Data(),
|
||||
|
@ -981,7 +981,7 @@ final class ChatMessageInteractiveMediaNode: ASDisplayNode, GalleryItemTransitio
|
||||
}
|
||||
|
||||
if currentReplaceAnimatedStickerNode, let updatedAnimatedStickerFile = updateAnimatedStickerFile {
|
||||
let animatedStickerNode = AnimatedStickerNode()
|
||||
let animatedStickerNode = DefaultAnimatedStickerNodeImpl()
|
||||
animatedStickerNode.isUserInteractionEnabled = false
|
||||
animatedStickerNode.started = {
|
||||
guard let strongSelf = self else {
|
||||
|
@ -112,11 +112,11 @@ public final class ChatMessageTransitionNode: ASDisplayNode {
|
||||
|
||||
final class Sticker {
|
||||
let imageNode: TransformImageNode
|
||||
let animationNode: GenericAnimatedStickerNode?
|
||||
let animationNode: AnimatedStickerNode?
|
||||
let placeholderNode: ASDisplayNode?
|
||||
let relativeSourceRect: CGRect
|
||||
|
||||
init(imageNode: TransformImageNode, animationNode: GenericAnimatedStickerNode?, placeholderNode: ASDisplayNode?, relativeSourceRect: CGRect) {
|
||||
init(imageNode: TransformImageNode, animationNode: AnimatedStickerNode?, placeholderNode: ASDisplayNode?, relativeSourceRect: CGRect) {
|
||||
self.imageNode = imageNode
|
||||
self.animationNode = animationNode
|
||||
self.placeholderNode = placeholderNode
|
||||
|
@ -381,7 +381,7 @@ private final class ThemeSettingsThemeItemIconNode : ListViewItemNode {
|
||||
Queue.mainQueue().after(0.1) {
|
||||
if !wasSelected {
|
||||
animatedStickerNode.seekTo(.frameIndex(0))
|
||||
animatedStickerNode.play()
|
||||
animatedStickerNode.play(firstFrame: false, fromIndex: nil)
|
||||
|
||||
let scale: CGFloat = 1.95
|
||||
animatedStickerNode.transform = CATransform3DMakeScale(scale, scale, 1.0)
|
||||
@ -498,7 +498,7 @@ private final class ThemeSettingsThemeItemIconNode : ListViewItemNode {
|
||||
if let current = strongSelf.animatedStickerNode {
|
||||
animatedStickerNode = current
|
||||
} else {
|
||||
animatedStickerNode = AnimatedStickerNode()
|
||||
animatedStickerNode = DefaultAnimatedStickerNodeImpl()
|
||||
animatedStickerNode.started = { [weak self] in
|
||||
self?.emojiImageNode.isHidden = true
|
||||
}
|
||||
@ -1499,7 +1499,7 @@ private class QrContentNode: ASDisplayNode, ContentNode {
|
||||
self.codeStaticIconNode = codeStaticIconNode
|
||||
self.codeAnimatedIconNode = nil
|
||||
} else {
|
||||
let codeAnimatedIconNode = AnimatedStickerNode()
|
||||
let codeAnimatedIconNode = DefaultAnimatedStickerNodeImpl()
|
||||
codeAnimatedIconNode.setup(source: AnimatedStickerNodeLocalFileSource(name: "PlaneLogoPlain"), width: 120, height: 120, mode: .direct(cachePathPrefix: nil))
|
||||
codeAnimatedIconNode.visibility = true
|
||||
self.codeAnimatedIconNode = codeAnimatedIconNode
|
||||
|
@ -362,7 +362,7 @@ private final class ThemeSettingsThemeItemIconNode : ListViewItemNode {
|
||||
Queue.mainQueue().after(0.1) {
|
||||
if !wasSelected {
|
||||
animatedStickerNode.seekTo(.frameIndex(0))
|
||||
animatedStickerNode.play()
|
||||
animatedStickerNode.play(firstFrame: false, fromIndex: nil)
|
||||
|
||||
let scale: CGFloat = 2.6
|
||||
animatedStickerNode.transform = CATransform3DMakeScale(scale, scale, 1.0)
|
||||
@ -479,7 +479,7 @@ private final class ThemeSettingsThemeItemIconNode : ListViewItemNode {
|
||||
if let current = strongSelf.animatedStickerNode {
|
||||
animatedStickerNode = current
|
||||
} else {
|
||||
animatedStickerNode = AnimatedStickerNode()
|
||||
animatedStickerNode = DefaultAnimatedStickerNodeImpl()
|
||||
animatedStickerNode.started = { [weak self] in
|
||||
self?.emojiImageNode.isHidden = true
|
||||
}
|
||||
|
@ -156,7 +156,9 @@ final class ChatTitleView: UIView, NavigationBarTitleView {
|
||||
}
|
||||
if let notificationSettings = peerView.notificationSettings as? TelegramPeerNotificationSettings {
|
||||
if case let .muted(until) = notificationSettings.muteState, until >= Int32(CFAbsoluteTimeGetCurrent() + NSTimeIntervalSince1970) {
|
||||
titleRightIcon = .mute
|
||||
if titleCredibilityIcon != .verified {
|
||||
titleRightIcon = .mute
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -421,7 +421,7 @@ final class HorizontalListContextResultsChatInputPanelItemNode: ListViewItemNode
|
||||
if let currentAnimationNode = strongSelf.animationNode {
|
||||
animationNode = currentAnimationNode
|
||||
} else {
|
||||
animationNode = AnimatedStickerNode()
|
||||
animationNode = DefaultAnimatedStickerNodeImpl()
|
||||
animationNode.transform = CATransform3DMakeRotation(CGFloat.pi / 2.0, 0.0, 0.0, 1.0)
|
||||
animationNode.visibility = true
|
||||
if let placeholderNode = strongSelf.placeholderNode {
|
||||
@ -437,7 +437,7 @@ final class HorizontalListContextResultsChatInputPanelItemNode: ListViewItemNode
|
||||
let dimensions = animatedStickerFile.dimensions ?? PixelDimensions(width: 512, height: 512)
|
||||
let fittedDimensions = dimensions.cgSize.aspectFitted(CGSize(width: 160.0, height: 160.0))
|
||||
strongSelf.fetchDisposable.set(freeMediaFileResourceInteractiveFetched(account: item.account, fileReference: stickerPackFileReference(animatedStickerFile), resource: animatedStickerFile.resource).start())
|
||||
animationNode.setup(source: AnimatedStickerResourceSource(account: item.account, resource: animatedStickerFile.resource, isVideo: animatedStickerFile.isVideoSticker), width: Int(fittedDimensions.width), height: Int(fittedDimensions.height), mode: .cached)
|
||||
animationNode.setup(source: AnimatedStickerResourceSource(account: item.account, resource: animatedStickerFile.resource, isVideo: animatedStickerFile.isVideoSticker), width: Int(fittedDimensions.width), height: Int(fittedDimensions.height), playbackMode: .loop, mode: .cached)
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -142,7 +142,7 @@ final class HorizontalStickerGridItemNode: GridItemNode {
|
||||
if let currentAnimationNode = self.animationNode {
|
||||
animationNode = currentAnimationNode
|
||||
} else {
|
||||
animationNode = AnimatedStickerNode()
|
||||
animationNode = DefaultAnimatedStickerNodeImpl()
|
||||
animationNode.transform = self.imageNode.transform
|
||||
animationNode.visibility = self.isVisibleInGrid
|
||||
animationNode.view.addGestureRecognizer(UITapGestureRecognizer(target: self, action: #selector(self.imageNodeTap(_:))))
|
||||
@ -178,7 +178,7 @@ final class HorizontalStickerGridItemNode: GridItemNode {
|
||||
strongSelf.removePlaceholder(animated: false)
|
||||
}
|
||||
}
|
||||
animationNode.setup(source: AnimatedStickerResourceSource(account: account, resource: item.file.resource, isVideo: item.file.isVideoSticker), width: Int(fittedDimensions.width), height: Int(fittedDimensions.height), mode: .cached)
|
||||
animationNode.setup(source: AnimatedStickerResourceSource(account: account, resource: item.file.resource, isVideo: item.file.isVideoSticker), width: Int(fittedDimensions.width), height: Int(fittedDimensions.height), playbackMode: .loop, mode: .cached)
|
||||
|
||||
self.stickerFetchedDisposable.set(freeMediaFileResourceInteractiveFetched(account: account, fileReference: stickerPackFileReference(item.file), resource: item.file.resource).start())
|
||||
} else {
|
||||
|
@ -63,7 +63,7 @@ private final class LargeEmojiActionSheetItemNode: ActionSheetItemNode {
|
||||
if let fitz = fitz {
|
||||
fitzModifier = EmojiFitzModifier(emoji: fitz)
|
||||
}
|
||||
self.animationNode = AnimatedStickerNode()
|
||||
self.animationNode = DefaultAnimatedStickerNodeImpl()
|
||||
self.animationNode.setup(source: AnimatedStickerResourceSource(account: context.account, resource: file.resource, fitzModifier: fitzModifier), width: 192, height: 192, playbackMode: .once, mode: .direct(cachePathPrefix: nil))
|
||||
self.animationNode.visibility = true
|
||||
|
||||
|
@ -120,7 +120,7 @@ final class TrendingTopItemNode: ASDisplayNode {
|
||||
if let currentAnimationNode = self.animationNode {
|
||||
animationNode = currentAnimationNode
|
||||
} else {
|
||||
animationNode = AnimatedStickerNode()
|
||||
animationNode = DefaultAnimatedStickerNodeImpl()
|
||||
animationNode.transform = self.imageNode.transform
|
||||
animationNode.visibility = self.visibility
|
||||
self.animationNode = animationNode
|
||||
@ -141,7 +141,7 @@ final class TrendingTopItemNode: ASDisplayNode {
|
||||
animationNode.started = { [weak self] in
|
||||
self?.imageNode.alpha = 0.0
|
||||
}
|
||||
animationNode.setup(source: AnimatedStickerResourceSource(account: account, resource: item.file.resource, isVideo: item.file.isVideoSticker), width: Int(fittedDimensions.width), height: Int(fittedDimensions.height), mode: .cached)
|
||||
animationNode.setup(source: AnimatedStickerResourceSource(account: account, resource: item.file.resource, isVideo: item.file.isVideoSticker), width: Int(fittedDimensions.width), height: Int(fittedDimensions.height), playbackMode: .loop, mode: .cached)
|
||||
self.loadDisposable.set(freeMediaFileResourceInteractiveFetched(account: account, fileReference: stickerPackFileReference(item.file), resource: item.file.resource).start())
|
||||
} else {
|
||||
self.imageNode.setSignal(chatMessageSticker(account: account, file: item.file, small: true, synchronousLoad: synchronousLoads), attemptSynchronously: synchronousLoads)
|
||||
|
@ -87,7 +87,7 @@ public final class NotificationViewControllerImpl {
|
||||
|
||||
TempBox.initializeShared(basePath: rootPath, processType: "notification-content", launchSpecificId: Int64.random(in: Int64.min ... Int64.max))
|
||||
|
||||
let logsPath = rootPath + "/notificationcontent-logs"
|
||||
let logsPath = rootPath + "/logs/notificationcontent-logs"
|
||||
let _ = try? FileManager.default.createDirectory(atPath: logsPath, withIntermediateDirectories: true, attributes: nil)
|
||||
|
||||
setupSharedLogger(rootPath: rootPath, path: logsPath)
|
||||
@ -294,7 +294,7 @@ public final class NotificationViewControllerImpl {
|
||||
if let current = strongSelf.animatedStickerNode {
|
||||
animatedStickerNode = current
|
||||
} else {
|
||||
animatedStickerNode = AnimatedStickerNode()
|
||||
animatedStickerNode = DefaultAnimatedStickerNodeImpl()
|
||||
strongSelf.animatedStickerNode = animatedStickerNode
|
||||
animatedStickerNode.started = {
|
||||
guard let strongSelf = self else {
|
||||
@ -315,7 +315,7 @@ public final class NotificationViewControllerImpl {
|
||||
} else {
|
||||
strongSelf.imageNode.setSignal(chatMessageAnimatedSticker(postbox: accountAndImage.0.postbox, file: fileReference.media, small: false, size: fittedDimensions))
|
||||
}
|
||||
animatedStickerNode.setup(source: AnimatedStickerResourceSource(account: accountAndImage.0, resource: fileReference.media.resource, isVideo: file.isVideoSticker), width: Int(fittedDimensions.width), height: Int(fittedDimensions.height), mode: .direct(cachePathPrefix: nil))
|
||||
animatedStickerNode.setup(source: AnimatedStickerResourceSource(account: accountAndImage.0, resource: fileReference.media.resource, isVideo: file.isVideoSticker), width: Int(fittedDimensions.width), height: Int(fittedDimensions.height), playbackMode: .loop, mode: .direct(cachePathPrefix: nil))
|
||||
animatedStickerNode.visibility = true
|
||||
|
||||
accountAndImage.0.network.shouldExplicitelyKeepWorkerConnections.set(.single(true))
|
||||
|
@ -2609,7 +2609,9 @@ final class PeerInfoHeaderNode: ASDisplayNode {
|
||||
let _ = nextPanelSubtitleNodeLayout[TitleNodeStateRegular]!.size
|
||||
let usernameSize = usernameNodeLayout[TitleNodeStateRegular]!.size
|
||||
|
||||
var titleHorizontalOffset: CGFloat = 0.0
|
||||
if let image = self.titleCredibilityIconNode.image {
|
||||
titleHorizontalOffset = -(image.size.width + 4.0) / 2.0
|
||||
transition.updateFrame(node: self.titleCredibilityIconNode, frame: CGRect(origin: CGPoint(x: titleSize.width + 4.0, y: floor((titleSize.height - image.size.height) / 2.0) + 1.0), size: image.size))
|
||||
transition.updateFrame(node: self.titleExpandedCredibilityIconNode, frame: CGRect(origin: CGPoint(x: titleExpandedSize.width + 4.0, y: floor((titleExpandedSize.height - image.size.height) / 2.0) + 1.0), size: image.size))
|
||||
}
|
||||
@ -2629,14 +2631,14 @@ final class PeerInfoHeaderNode: ASDisplayNode {
|
||||
subtitleFrame = CGRect(origin: CGPoint(x: 16.0, y: minTitleFrame.maxY + 2.0), size: subtitleSize)
|
||||
usernameFrame = CGRect(origin: CGPoint(x: width - usernameSize.width - 16.0, y: minTitleFrame.midY - usernameSize.height / 2.0), size: usernameSize)
|
||||
} else {
|
||||
titleFrame = CGRect(origin: CGPoint(x: floor((width - titleSize.width) / 2.0), y: avatarFrame.maxY + 7.0 + (subtitleSize.height.isZero ? 11.0 : 0.0) + 11.0), size: titleSize)
|
||||
titleFrame = CGRect(origin: CGPoint(x: floorToScreenPixels((width - titleSize.width) / 2.0), y: avatarFrame.maxY + 7.0 + (subtitleSize.height.isZero ? 11.0 : 0.0) + 11.0), size: titleSize)
|
||||
|
||||
let totalSubtitleWidth = subtitleSize.width + usernameSpacing + usernameSize.width
|
||||
if usernameSize.width == 0.0 {
|
||||
subtitleFrame = CGRect(origin: CGPoint(x: floor((width - subtitleSize.width) / 2.0), y: titleFrame.maxY + 1.0), size: subtitleSize)
|
||||
usernameFrame = CGRect(origin: CGPoint(x: floor((width - usernameSize.width) / 2.0), y: subtitleFrame.maxY + 1.0), size: usernameSize)
|
||||
subtitleFrame = CGRect(origin: CGPoint(x: floorToScreenPixels((width - subtitleSize.width) / 2.0), y: titleFrame.maxY + 1.0), size: subtitleSize)
|
||||
usernameFrame = CGRect(origin: CGPoint(x: floorToScreenPixels((width - usernameSize.width) / 2.0), y: subtitleFrame.maxY + 1.0), size: usernameSize)
|
||||
} else {
|
||||
subtitleFrame = CGRect(origin: CGPoint(x: floor((width - totalSubtitleWidth) / 2.0), y: titleFrame.maxY + 1.0), size: subtitleSize)
|
||||
subtitleFrame = CGRect(origin: CGPoint(x: floorToScreenPixels((width - totalSubtitleWidth) / 2.0), y: titleFrame.maxY + 1.0), size: subtitleSize)
|
||||
usernameFrame = CGRect(origin: CGPoint(x: subtitleFrame.maxX + usernameSpacing, y: titleFrame.maxY + 1.0), size: usernameSize)
|
||||
}
|
||||
}
|
||||
@ -2851,10 +2853,15 @@ final class PeerInfoHeaderNode: ASDisplayNode {
|
||||
neutralTitleScale = 0.7
|
||||
neutralSubtitleScale = 1.0
|
||||
}
|
||||
|
||||
|
||||
let titleScale = (transitionFraction * transitionSourceTitleFrame.height + (1.0 - transitionFraction) * titleFrame.height * neutralTitleScale) / (titleFrame.height)
|
||||
let subtitleScale = max(0.01, min(10.0, (transitionFraction * transitionSourceSubtitleFrame.height + (1.0 - transitionFraction) * subtitleFrame.height * neutralSubtitleScale) / (subtitleFrame.height)))
|
||||
|
||||
var titleFrame = titleFrame
|
||||
if !self.isAvatarExpanded {
|
||||
titleFrame = titleFrame.offsetBy(dx: self.isAvatarExpanded ? 0.0 : titleHorizontalOffset * titleScale, dy: 0.0)
|
||||
}
|
||||
|
||||
let titleCenter = CGPoint(x: transitionFraction * transitionSourceTitleFrame.midX + (1.0 - transitionFraction) * titleFrame.midX, y: transitionFraction * transitionSourceTitleFrame.midY + (1.0 - transitionFraction) * titleFrame.midY)
|
||||
let subtitleCenter = CGPoint(x: transitionFraction * transitionSourceSubtitleFrame.midX + (1.0 - transitionFraction) * subtitleFrame.midX, y: transitionFraction * transitionSourceSubtitleFrame.midY + (1.0 - transitionFraction) * subtitleFrame.midY)
|
||||
|
||||
@ -2885,7 +2892,7 @@ final class PeerInfoHeaderNode: ASDisplayNode {
|
||||
subtitleOffset = titleCollapseFraction * -2.0
|
||||
}
|
||||
|
||||
let rawTitleFrame = titleFrame
|
||||
let rawTitleFrame = titleFrame.offsetBy(dx: self.isAvatarExpanded ? 0.0 : titleHorizontalOffset * titleScale, dy: 0.0)
|
||||
self.titleNodeRawContainer.frame = rawTitleFrame
|
||||
transition.updateFrame(node: self.titleNode, frame: CGRect(origin: CGPoint(), size: CGSize()))
|
||||
let rawSubtitleFrame = subtitleFrame
|
||||
|
@ -175,7 +175,7 @@ public class ShareRootControllerImpl {
|
||||
|
||||
TempBox.initializeShared(basePath: rootPath, processType: "share", launchSpecificId: Int64.random(in: Int64.min ... Int64.max))
|
||||
|
||||
let logsPath = rootPath + "/share-logs"
|
||||
let logsPath = rootPath + "/logs/share-logs"
|
||||
let _ = try? FileManager.default.createDirectory(atPath: logsPath, withIntermediateDirectories: true, attributes: nil)
|
||||
|
||||
setupSharedLogger(rootPath: rootPath, path: logsPath)
|
||||
|
@ -158,14 +158,14 @@ final class StickerPaneSearchStickerItemNode: GridItemNode {
|
||||
if let dimensions = stickerItem.file.dimensions {
|
||||
if stickerItem.file.isAnimatedSticker || stickerItem.file.isVideoSticker {
|
||||
if self.animationNode == nil {
|
||||
let animationNode = AnimatedStickerNode()
|
||||
let animationNode = DefaultAnimatedStickerNodeImpl()
|
||||
animationNode.view.addGestureRecognizer(UITapGestureRecognizer(target: self, action: #selector(self.imageNodeTap(_:))))
|
||||
self.animationNode = animationNode
|
||||
self.insertSubnode(animationNode, belowSubnode: self.textNode)
|
||||
}
|
||||
let dimensions = stickerItem.file.dimensions ?? PixelDimensions(width: 512, height: 512)
|
||||
let fittedDimensions = dimensions.cgSize.aspectFitted(CGSize(width: 160.0, height: 160.0))
|
||||
self.animationNode?.setup(source: AnimatedStickerResourceSource(account: account, resource: stickerItem.file.resource, isVideo: stickerItem.file.isVideoSticker), width: Int(fittedDimensions.width), height: Int(fittedDimensions.height), mode: .cached)
|
||||
self.animationNode?.setup(source: AnimatedStickerResourceSource(account: account, resource: stickerItem.file.resource, isVideo: stickerItem.file.isVideoSticker), width: Int(fittedDimensions.width), height: Int(fittedDimensions.height), playbackMode: .loop, mode: .cached)
|
||||
self.animationNode?.visibility = self.isVisibleInGrid
|
||||
self.stickerFetchedDisposable.set(freeMediaFileResourceInteractiveFetched(account: account, fileReference: stickerPackFileReference(stickerItem.file), resource: stickerItem.file.resource).start())
|
||||
} else {
|
||||
|
@ -294,7 +294,7 @@ private final class FeaturedPackItemNode: ListViewItemNode {
|
||||
if let current = self.animatedStickerNode {
|
||||
animatedStickerNode = current
|
||||
} else {
|
||||
animatedStickerNode = AnimatedStickerNode()
|
||||
animatedStickerNode = DefaultAnimatedStickerNodeImpl()
|
||||
animatedStickerNode.started = { [weak self] in
|
||||
self?.imageNode.isHidden = true
|
||||
self?.removePlaceholder(animated: false)
|
||||
@ -305,7 +305,7 @@ private final class FeaturedPackItemNode: ListViewItemNode {
|
||||
} else {
|
||||
self.containerNode.addSubnode(animatedStickerNode)
|
||||
}
|
||||
animatedStickerNode.setup(source: AnimatedStickerResourceSource(account: account, resource: resource, isVideo: isVideo), width: 128, height: 128, mode: .cached)
|
||||
animatedStickerNode.setup(source: AnimatedStickerResourceSource(account: account, resource: resource, isVideo: isVideo), width: 128, height: 128, playbackMode: .loop, mode: .cached)
|
||||
}
|
||||
animatedStickerNode.visibility = self.visibilityStatus && loopAnimatedStickers
|
||||
}
|
||||
|
@ -194,7 +194,7 @@ private final class TooltipScreenNode: ViewControllerTracingNode {
|
||||
|
||||
self.textNode.attributedText = stringWithAppliedEntities(text, entities: textEntities, baseColor: .white, linkColor: .white, baseFont: Font.regular(fontSize), linkFont: Font.regular(fontSize), boldFont: Font.semibold(14.0), italicFont: Font.italic(fontSize), boldItalicFont: Font.semiboldItalic(fontSize), fixedFont: Font.monospace(fontSize), blockQuoteFont: Font.regular(fontSize), underlineLinks: true, external: false)
|
||||
|
||||
self.animatedStickerNode = AnimatedStickerNode()
|
||||
self.animatedStickerNode = DefaultAnimatedStickerNodeImpl()
|
||||
switch icon {
|
||||
case .none:
|
||||
break
|
||||
|
@ -319,7 +319,7 @@ final class UndoOverlayControllerNode: ViewControllerTracingNode {
|
||||
self.iconNode = nil
|
||||
self.iconCheckNode = nil
|
||||
self.animationNode = nil
|
||||
self.animatedStickerNode = AnimatedStickerNode()
|
||||
self.animatedStickerNode = DefaultAnimatedStickerNodeImpl()
|
||||
self.animatedStickerNode?.visibility = true
|
||||
self.animatedStickerNode?.setup(source: AnimatedStickerNodeLocalFileSource(name: name), width: 100, height: 100, playbackMode: .once, mode: .direct(cachePathPrefix: nil))
|
||||
|
||||
@ -429,7 +429,7 @@ final class UndoOverlayControllerNode: ViewControllerTracingNode {
|
||||
case .still:
|
||||
break
|
||||
case let .animated(resource, _, isVideo):
|
||||
let animatedStickerNode = AnimatedStickerNode()
|
||||
let animatedStickerNode = DefaultAnimatedStickerNodeImpl()
|
||||
self.animatedStickerNode = animatedStickerNode
|
||||
animatedStickerNode.setup(source: AnimatedStickerResourceSource(account: context.account, resource: resource._asResource(), isVideo: isVideo), width: 80, height: 80, mode: .direct(cachePathPrefix: nil))
|
||||
}
|
||||
@ -471,7 +471,7 @@ final class UndoOverlayControllerNode: ViewControllerTracingNode {
|
||||
slotMachineNode.setState(.value(value, true))
|
||||
}
|
||||
} else {
|
||||
let animatedStickerNode = AnimatedStickerNode()
|
||||
let animatedStickerNode = DefaultAnimatedStickerNodeImpl()
|
||||
self.animatedStickerNode = animatedStickerNode
|
||||
|
||||
let _ = (context.engine.stickers.loadedStickerPack(reference: .dice(dice.emoji), forceActualized: false)
|
||||
@ -723,7 +723,7 @@ final class UndoOverlayControllerNode: ViewControllerTracingNode {
|
||||
case .still:
|
||||
break
|
||||
case let .animated(resource):
|
||||
let animatedStickerNode = AnimatedStickerNode()
|
||||
let animatedStickerNode = DefaultAnimatedStickerNodeImpl()
|
||||
self.animatedStickerNode = animatedStickerNode
|
||||
animatedStickerNode.setup(source: AnimatedStickerResourceSource(account: context.account, resource: resource._asResource(), isVideo: file.isVideoSticker), width: 80, height: 80, mode: .cached)
|
||||
}
|
||||
@ -749,7 +749,7 @@ final class UndoOverlayControllerNode: ViewControllerTracingNode {
|
||||
self.iconCheckNode = nil
|
||||
self.animationNode = nil
|
||||
|
||||
let animatedStickerNode = AnimatedStickerNode()
|
||||
let animatedStickerNode = DefaultAnimatedStickerNodeImpl()
|
||||
self.animatedStickerNode = animatedStickerNode
|
||||
|
||||
animatedStickerNode.setup(source: AnimatedStickerNodeLocalFileSource(name: "anim_savemedia"), width: 80, height: 80, playbackMode: .once, mode: .direct(cachePathPrefix: nil))
|
||||
|
@ -44,6 +44,7 @@ public protocol WallpaperBubbleBackgroundNode: ASDisplayNode {
|
||||
|
||||
func update(rect: CGRect, within containerSize: CGSize, transition: ContainedViewLayoutTransition)
|
||||
func update(rect: CGRect, within containerSize: CGSize, transition: CombinedTransition)
|
||||
func update(rect: CGRect, within containerSize: CGSize, animator: ControlledTransitionAnimator)
|
||||
func offset(value: CGPoint, animationCurve: ContainedViewLayoutTransitionCurve, duration: Double)
|
||||
func offsetSpring(value: CGFloat, duration: Double, damping: CGFloat)
|
||||
}
|
||||
@ -262,6 +263,23 @@ final class WallpaperBackgroundNodeImpl: ASDisplayNode, WallpaperBackgroundNode
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func update(rect: CGRect, within containerSize: CGSize, animator: ControlledTransitionAnimator) {
|
||||
self.currentLayout = (rect, containerSize)
|
||||
|
||||
let shiftedContentsRect = CGRect(origin: CGPoint(x: rect.minX / containerSize.width, y: rect.minY / containerSize.height), size: CGSize(width: rect.width / containerSize.width, height: rect.height / containerSize.height))
|
||||
|
||||
animator.updateFrame(layer: self.contentNode.layer, frame: self.bounds, completion: nil)
|
||||
animator.updateContentsRect(layer: self.contentNode.layer, contentsRect: shiftedContentsRect, completion: nil)
|
||||
if let cleanWallpaperNode = self.cleanWallpaperNode {
|
||||
animator.updateFrame(layer: cleanWallpaperNode.layer, frame: self.bounds, completion: nil)
|
||||
animator.updateContentsRect(layer: cleanWallpaperNode.layer, contentsRect: shiftedContentsRect, completion: nil)
|
||||
}
|
||||
if let gradientWallpaperNode = self.gradientWallpaperNode {
|
||||
animator.updateFrame(layer: gradientWallpaperNode.layer, frame: self.bounds, completion: nil)
|
||||
animator.updateContentsRect(layer: gradientWallpaperNode.layer, contentsRect: shiftedContentsRect, completion: nil)
|
||||
}
|
||||
}
|
||||
|
||||
func update(rect: CGRect, within containerSize: CGSize, transition: CombinedTransition) {
|
||||
self.currentLayout = (rect, containerSize)
|
||||
@ -467,7 +485,7 @@ final class WallpaperBackgroundNodeImpl: ASDisplayNode, WallpaperBackgroundNode
|
||||
("ptrnSLON_0906_1033", CGPoint(x: 906 - 256, y: 1033 - 256))
|
||||
]
|
||||
for (animation, relativePosition) in animationList {
|
||||
let animationNode = AnimatedStickerNode()
|
||||
let animationNode = DefaultAnimatedStickerNodeImpl()
|
||||
animationNode.automaticallyLoadFirstFrame = true
|
||||
animationNode.autoplay = true
|
||||
//self.inlineAnimationNodes.append((animationNode, relativePosition))
|
||||
@ -1177,6 +1195,23 @@ final class WallpaperBackgroundNodeMergedImpl: ASDisplayNode, WallpaperBackgroun
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func update(rect: CGRect, within containerSize: CGSize, animator: ControlledTransitionAnimator) {
|
||||
self.currentLayout = (rect, containerSize)
|
||||
|
||||
let shiftedContentsRect = CGRect(origin: CGPoint(x: rect.minX / containerSize.width, y: rect.minY / containerSize.height), size: CGSize(width: rect.width / containerSize.width, height: rect.height / containerSize.height))
|
||||
|
||||
animator.updateFrame(layer: self.contentNode.layer, frame: self.bounds, completion: nil)
|
||||
animator.updateContentsRect(layer: self.contentNode.layer, contentsRect: shiftedContentsRect, completion: nil)
|
||||
if let cleanWallpaperNode = self.cleanWallpaperNode {
|
||||
animator.updateFrame(layer: cleanWallpaperNode.layer, frame: self.bounds, completion: nil)
|
||||
animator.updateContentsRect(layer: cleanWallpaperNode.layer, contentsRect: shiftedContentsRect, completion: nil)
|
||||
}
|
||||
if let gradientWallpaperNode = self.gradientWallpaperNode {
|
||||
animator.updateFrame(layer: gradientWallpaperNode.layer, frame: self.bounds, completion: nil)
|
||||
animator.updateContentsRect(layer: gradientWallpaperNode.layer, contentsRect: shiftedContentsRect, completion: nil)
|
||||
}
|
||||
}
|
||||
|
||||
func update(rect: CGRect, within containerSize: CGSize, transition: CombinedTransition) {
|
||||
self.currentLayout = (rect, containerSize)
|
||||
|
@ -4,7 +4,9 @@
|
||||
// enable threading
|
||||
//#define LOTTIE_THREAD_SUPPORT
|
||||
|
||||
#ifndef LOTTIE_THREAD_SAFE
|
||||
#define LOTTIE_THREAD_SAFE
|
||||
#endif
|
||||
|
||||
//enable logging
|
||||
//#define LOTTIE_LOGGING_SUPPORT
|
||||
@ -16,6 +18,8 @@
|
||||
//#define LOTTIE_CACHE_SUPPORT
|
||||
|
||||
// disable image loader
|
||||
#ifndef LOTTIE_IMAGE_MODULE_DISABLED
|
||||
#define LOTTIE_IMAGE_MODULE_DISABLED
|
||||
#endif
|
||||
|
||||
#endif // CONFIG_H
|
||||
|
Loading…
x
Reference in New Issue
Block a user