Merge branch 'master' of gitlab.com:peter-iakovlev/telegram-ios

This commit is contained in:
Mike Renoir 2022-06-10 11:14:21 +04:00
commit 4dfa591968
90 changed files with 625 additions and 244 deletions

View File

@ -64,7 +64,7 @@ private func rootPathForBasePath(_ appGroupPath: String) -> String {
let rootPath = rootPathForBasePath(appGroupUrl.path) 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 _ = try? FileManager.default.createDirectory(atPath: logsPath, withIntermediateDirectories: true, attributes: nil)
let screencastBufferClientContext = IpcGroupCallBufferBroadcastContext(basePath: rootPath + "/broadcast-coordination") let screencastBufferClientContext = IpcGroupCallBufferBroadcastContext(basePath: rootPath + "/broadcast-coordination")

View File

@ -639,7 +639,7 @@ private final class NotificationServiceHandler {
TempBox.initializeShared(basePath: rootPath, processType: "notification", launchSpecificId: Int64.random(in: Int64.min ... Int64.max)) 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) let _ = try? FileManager.default.createDirectory(atPath: logsPath, withIntermediateDirectories: true, attributes: nil)
setupSharedLogger(rootPath: rootPath, path: logsPath) setupSharedLogger(rootPath: rootPath, path: logsPath)
@ -673,7 +673,7 @@ private final class NotificationServiceHandler {
incomingCallMessage = "is calling you" 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 { guard var encryptedPayload = payload["p"] as? String else {
Logger.shared.log("NotificationService \(episode)", "Invalid payload 1") Logger.shared.log("NotificationService \(episode)", "Invalid payload 1")
@ -691,13 +691,16 @@ private final class NotificationServiceHandler {
let _ = (combineLatest(queue: self.queue, let _ = (combineLatest(queue: self.queue,
self.accountManager.accountRecords(), self.accountManager.accountRecords(),
self.accountManager.sharedData(keys: [ApplicationSpecificSharedDataKeys.inAppNotificationSettings, ApplicationSpecificSharedDataKeys.voiceCallSettings]) self.accountManager.sharedData(keys: [ApplicationSpecificSharedDataKeys.inAppNotificationSettings, ApplicationSpecificSharedDataKeys.voiceCallSettings, SharedDataKeys.loggingSettings])
) )
|> take(1) |> take(1)
|> deliverOn(self.queue)).start(next: { [weak self] records, sharedData in |> deliverOn(self.queue)).start(next: { [weak self] records, sharedData in
var recordId: AccountRecordId? var recordId: AccountRecordId?
var isCurrentAccount: Bool = false 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) { if let keyId = notificationPayloadKeyId(data: payloadData) {
outer: for listRecord in records.records { 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 let inAppNotificationSettings = sharedData.entries[ApplicationSpecificSharedDataKeys.inAppNotificationSettings]?.get(InAppNotificationSettings.self) ?? InAppNotificationSettings.defaultSettings
customSoundPath = inAppNotificationSettings.customSound
let voiceCallSettings: VoiceCallSettings let voiceCallSettings: VoiceCallSettings
if let value = sharedData.entries[ApplicationSpecificSharedDataKeys.voiceCallSettings]?.get(VoiceCallSettings.self) { if let value = sharedData.entries[ApplicationSpecificSharedDataKeys.voiceCallSettings]?.get(VoiceCallSettings.self) {
voiceCallSettings = value voiceCallSettings = value

View File

@ -113,7 +113,7 @@ class DefaultIntentHandler: INExtension, INSendMessageIntentHandling, INSearchFo
TempBox.initializeShared(basePath: rootPath, processType: "siri", launchSpecificId: Int64.random(in: Int64.min ... Int64.max)) 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) let _ = try? FileManager.default.createDirectory(atPath: logsPath, withIntermediateDirectories: true, attributes: nil)
setupSharedLogger(rootPath: rootPath, path: logsPath) 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)) 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) let _ = try? FileManager.default.createDirectory(atPath: logsPath, withIntermediateDirectories: true, attributes: nil)
setupSharedLogger(rootPath: rootPath, path: logsPath) setupSharedLogger(rootPath: rootPath, path: logsPath)

View File

@ -388,7 +388,7 @@
"Tour.Text5" = "**Telegram** lets you access your\nmessages from multiple devices."; "Tour.Text5" = "**Telegram** lets you access your\nmessages from multiple devices.";
"Tour.Title6" = "Free"; "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"; "Tour.StartButton" = "Start Messaging";
@ -7550,12 +7550,15 @@ Sorry for the inconvenience.";
"Premium.AppIcons.Proceed" = "Unlock Premium Icons"; "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."; "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.MultipleTypingPair" = "%@ and %@";
"Chat.MultipleTypingMore" = "%@ and %@ others"; "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.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."; "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."; "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.NoAds" = "No Ads";
"Premium.NoAdsInfo" = "No more ads in public channels where Telegram sometimes shows 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.Reactions" = "Unique Reactions";
"Premium.ReactionsInfo" = "Additional animated reactions on messages, available only to the Premium subscribers."; "Premium.ReactionsInfo" = "Additional animated reactions on messages, available only to the Premium subscribers.";
"Premium.ReactionsStandalone" = "Additional Reactions"; "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.Stickers" = "Premium Stickers";
"Premium.StickersInfo" = "Exclusive enlarged stickers featuring additional effects, updated monthly."; "Premium.StickersInfo" = "Exclusive enlarged stickers featuring additional effects, updated monthly.";

View File

@ -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)) 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) let _ = try? FileManager.default.createDirectory(atPath: logsPath, withIntermediateDirectories: true, attributes: nil)
setupSharedLogger(rootPath: rootPath, path: logsPath) setupSharedLogger(rootPath: rootPath, path: logsPath)

View File

@ -147,7 +147,41 @@ public protocol AnimatedStickerNodeSource {
func directDataPath(attemptSynchronously: Bool) -> Signal<String?, NoError> 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 queue: Queue
private let disposable = MetaDisposable() private let disposable = MetaDisposable()
private let fetchDisposable = MetaDisposable() private let fetchDisposable = MetaDisposable()
@ -245,14 +279,14 @@ public final class AnimatedStickerNode: ASDisplayNode {
return SoftwareAnimationRenderer() return SoftwareAnimationRenderer()
}) })
private weak var nodeToCopyFrameFrom: AnimatedStickerNode? private weak var nodeToCopyFrameFrom: DefaultAnimatedStickerNodeImpl?
override public func didLoad() { override public func didLoad() {
super.didLoad() super.didLoad()
if #available(iOS 10.0, *), (self.useMetalCache/* || "".isEmpty*/) { if #available(iOS 10.0, *), (self.useMetalCache/* || "".isEmpty*/) {
self.renderer = AnimatedStickerNode.hardwareRendererPool.take() self.renderer = DefaultAnimatedStickerNodeImpl.hardwareRendererPool.take()
} else { } else {
self.renderer = AnimatedStickerNode.softwareRendererPool.take() self.renderer = DefaultAnimatedStickerNodeImpl.softwareRendererPool.take()
if let contents = self.nodeToCopyFrameFrom?.renderer?.renderer.contents { if let contents = self.nodeToCopyFrameFrom?.renderer?.renderer.contents {
self.renderer?.renderer.contents = contents self.renderer?.renderer.contents = contents
} }
@ -267,7 +301,12 @@ public final class AnimatedStickerNode: ASDisplayNode {
} }
public func cloneCurrentFrame(from otherNode: AnimatedStickerNode?) { 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 { if let contents = otherRenderer.contents {
renderer.contents = contents renderer.contents = contents
} }

View File

@ -250,7 +250,7 @@ final class CallListControllerNode: ASDisplayNode {
self.emptyTextNode.textAlignment = .center self.emptyTextNode.textAlignment = .center
self.emptyTextNode.maximumNumberOfLines = 3 self.emptyTextNode.maximumNumberOfLines = 3
self.emptyAnimationNode = AnimatedStickerNode() self.emptyAnimationNode = DefaultAnimatedStickerNodeImpl()
self.emptyAnimationNode.alpha = 0.0 self.emptyAnimationNode.alpha = 0.0
self.emptyAnimationNode.isUserInteractionEnabled = false self.emptyAnimationNode.isUserInteractionEnabled = false

View File

@ -337,8 +337,8 @@ public final class ChatImportActivityScreen: ViewController {
self.presentationData = self.context.sharedContext.currentPresentationData.with { $0 } self.presentationData = self.context.sharedContext.currentPresentationData.with { $0 }
self.animationNode = AnimatedStickerNode() self.animationNode = DefaultAnimatedStickerNodeImpl()
self.doneAnimationNode = AnimatedStickerNode() self.doneAnimationNode = DefaultAnimatedStickerNodeImpl()
self.doneAnimationNode.isHidden = true self.doneAnimationNode.isHidden = true
self.radialStatus = RadialStatusNode(backgroundNodeColor: .clear) self.radialStatus = RadialStatusNode(backgroundNodeColor: .clear)

View File

@ -84,7 +84,7 @@ class ChatListFilterSettingsHeaderItemNode: ListViewItemNode {
self.titleNode.contentMode = .left self.titleNode.contentMode = .left
self.titleNode.contentsScale = UIScreen.main.scale self.titleNode.contentsScale = UIScreen.main.scale
self.animationNode = AnimatedStickerNode() self.animationNode = DefaultAnimatedStickerNodeImpl()
super.init(layerBacked: false, dynamicBounce: false) super.init(layerBacked: false, dynamicBounce: false)
@ -101,7 +101,7 @@ class ChatListFilterSettingsHeaderItemNode: ListViewItemNode {
@objc private func animationTapGesture(_ recognizer: UITapGestureRecognizer) { @objc private func animationTapGesture(_ recognizer: UITapGestureRecognizer) {
if case .ended = recognizer.state { if case .ended = recognizer.state {
if !self.animationNode.isPlaying { if !self.animationNode.isPlaying {
self.animationNode.play() self.animationNode.play(firstFrame: false, fromIndex: nil)
} }
} }
} }

View File

@ -31,7 +31,7 @@ final class ChatListEmptyNode: ASDisplayNode {
self.isFilter = isFilter self.isFilter = isFilter
self.isLoading = isLoading self.isLoading = isLoading
self.animationNode = AnimatedStickerNode() self.animationNode = DefaultAnimatedStickerNodeImpl()
self.textNode = ImmediateTextNode() self.textNode = ImmediateTextNode()
self.textNode.displaysAsynchronously = false self.textNode.displaysAsynchronously = false
@ -107,13 +107,13 @@ final class ChatListEmptyNode: ASDisplayNode {
@objc private func animationTapGesture(_ recognizer: UITapGestureRecognizer) { @objc private func animationTapGesture(_ recognizer: UITapGestureRecognizer) {
if case .ended = recognizer.state { if case .ended = recognizer.state {
if !self.animationNode.isPlaying { if !self.animationNode.isPlaying {
self.animationNode.play() self.animationNode.play(firstFrame: false, fromIndex: nil)
} }
} }
} }
func restartAnimation() { func restartAnimation() {
self.animationNode.play() self.animationNode.play(firstFrame: false, fromIndex: nil)
} }
func updateThemeAndStrings(theme: PresentationTheme, strings: PresentationStrings) { func updateThemeAndStrings(theme: PresentationTheme, strings: PresentationStrings) {

View File

@ -1467,7 +1467,7 @@ final class ActionSheetAnimationAndTextItemNode: ActionSheetItemNode {
self.theme = theme self.theme = theme
self.defaultFont = Font.regular(floor(theme.baseFontSize * 13.0 / 17.0)) 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.setup(source: AnimatedStickerNodeLocalFileSource(name: "ClearDownloadList"), width: 256, height: 256, playbackMode: .loop, mode: .direct(cachePathPrefix: nil))
self.animationNode.visibility = true self.animationNode.visibility = true

View File

@ -971,7 +971,7 @@ final class ChatListSearchListPaneNode: ASDisplayNode, ChatListSearchPaneNode {
self.emptyResultsTextNode.textAlignment = .center self.emptyResultsTextNode.textAlignment = .center
self.emptyResultsTextNode.isHidden = true self.emptyResultsTextNode.isHidden = true
self.emptyResultsAnimationNode = AnimatedStickerNode() self.emptyResultsAnimationNode = DefaultAnimatedStickerNodeImpl()
self.emptyResultsAnimationNode.isHidden = true self.emptyResultsAnimationNode.isHidden = true
super.init() super.init()

View File

@ -335,8 +335,12 @@ public final class ChatMessageShadowNode: ASDisplayNode {
self.contentNode.image = shadowImage 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) { 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 var backgroundFrame = backgroundContent.frame
backgroundFrame.origin.x += rect.minX backgroundFrame.origin.x += rect.minX
backgroundFrame.origin.y += rect.minY 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 animator.updateFrame(layer: self.layer, frame: value, completion: { _ in

View File

@ -95,7 +95,7 @@ public final class AnimatedStickerComponent: Component {
if self.component?.animation != component.animation { if self.component?.animation != component.animation {
self.animationNode?.view.removeFromSuperview() self.animationNode?.view.removeFromSuperview()
let animationNode = AnimatedStickerNode() let animationNode = DefaultAnimatedStickerNodeImpl()
let source: AnimatedStickerNodeSource let source: AnimatedStickerNodeSource
switch component.animation.source { switch component.animation.source {
case let .bundle(name): case let .bundle(name):

View File

@ -370,18 +370,17 @@ public final class ReactionListContextMenuContent: ContextControllerItemsContent
} }
let premiumConfiguration = PremiumConfiguration.with(appConfiguration: self.context.currentAppConfiguration.with { $0 }) let premiumConfiguration = PremiumConfiguration.with(appConfiguration: self.context.currentAppConfiguration.with { $0 })
var currentCredibilityIconImage: UIImage? var currentCredibilityIconImage: UIImage?
if item.peer.id != self.context.account.peerId { if item.peer.isScam {
if item.peer.isScam { currentCredibilityIconImage = PresentationResourcesChatList.scamIcon(presentationData.theme, strings: presentationData.strings, type: .regular)
currentCredibilityIconImage = PresentationResourcesChatList.scamIcon(presentationData.theme, strings: presentationData.strings, type: .regular) } else if item.peer.isFake {
} else if item.peer.isFake { currentCredibilityIconImage = PresentationResourcesChatList.fakeIcon(presentationData.theme, strings: presentationData.strings, type: .regular)
currentCredibilityIconImage = PresentationResourcesChatList.fakeIcon(presentationData.theme, strings: presentationData.strings, type: .regular) } else if item.peer.isVerified {
} else if item.peer.isVerified { currentCredibilityIconImage = PresentationResourcesChatList.verifiedIcon(presentationData.theme)
currentCredibilityIconImage = PresentationResourcesChatList.verifiedIcon(presentationData.theme) } else if item.peer.isPremium && !premiumConfiguration.isPremiumDisabled {
} else if item.peer.isPremium && !premiumConfiguration.isPremiumDisabled { currentCredibilityIconImage = PresentationResourcesChatList.premiumIcon(presentationData.theme)
currentCredibilityIconImage = PresentationResourcesChatList.premiumIcon(presentationData.theme)
}
} }
var additionalTitleInset: CGFloat = 0.0 var additionalTitleInset: CGFloat = 0.0
if let currentCredibilityIconImage = currentCredibilityIconImage { if let currentCredibilityIconImage = currentCredibilityIconImage {
additionalTitleInset += 3.0 + currentCredibilityIconImage.size.width additionalTitleInset += 3.0 + currentCredibilityIconImage.size.width

View File

@ -261,7 +261,7 @@ public final class ContactsSearchContainerNode: SearchDisplayControllerContentNo
self.emptyResultsTextNode.textAlignment = .center self.emptyResultsTextNode.textAlignment = .center
self.emptyResultsTextNode.isHidden = true self.emptyResultsTextNode.isHidden = true
self.emptyResultsAnimationNode = AnimatedStickerNode() self.emptyResultsAnimationNode = DefaultAnimatedStickerNodeImpl()
self.emptyResultsAnimationNode.isHidden = true self.emptyResultsAnimationNode.isHidden = true
super.init() super.init()

View File

@ -61,6 +61,7 @@ private enum DebugControllerEntry: ItemListNodeEntry {
case sendGroupCallLogs case sendGroupCallLogs
case sendNotificationLogs(PresentationTheme) case sendNotificationLogs(PresentationTheme)
case sendCriticalLogs(PresentationTheme) case sendCriticalLogs(PresentationTheme)
case sendAllLogs
case accounts(PresentationTheme) case accounts(PresentationTheme)
case logToFile(PresentationTheme, Bool) case logToFile(PresentationTheme, Bool)
case logToConsole(PresentationTheme, Bool) case logToConsole(PresentationTheme, Bool)
@ -94,7 +95,7 @@ private enum DebugControllerEntry: ItemListNodeEntry {
case preferredVideoCodec(Int, String, String?, Bool) case preferredVideoCodec(Int, String, String?, Bool)
case disableVideoAspectScaling(Bool) case disableVideoAspectScaling(Bool)
case enableVoipTcp(Bool) case enableVoipTcp(Bool)
case resetInAppPurchases(PresentationTheme) case restorePurchases(PresentationTheme)
case hostInfo(PresentationTheme, String) case hostInfo(PresentationTheme, String)
case versionInfo(PresentationTheme) case versionInfo(PresentationTheme)
@ -102,7 +103,7 @@ private enum DebugControllerEntry: ItemListNodeEntry {
switch self { switch self {
case .testStickerImport: case .testStickerImport:
return DebugControllerSection.sticker.rawValue return DebugControllerSection.sticker.rawValue
case .sendLogs, .sendOneLog, .sendShareLogs, .sendGroupCallLogs, .sendNotificationLogs, .sendCriticalLogs: case .sendLogs, .sendOneLog, .sendShareLogs, .sendGroupCallLogs, .sendNotificationLogs, .sendCriticalLogs, .sendAllLogs:
return DebugControllerSection.logs.rawValue return DebugControllerSection.logs.rawValue
case .accounts: case .accounts:
return DebugControllerSection.logs.rawValue return DebugControllerSection.logs.rawValue
@ -110,7 +111,7 @@ private enum DebugControllerEntry: ItemListNodeEntry {
return DebugControllerSection.logging.rawValue return DebugControllerSection.logging.rawValue
case .enableRaiseToSpeak, .keepChatNavigationStack, .skipReadHistory, .crashOnSlowQueries: case .enableRaiseToSpeak, .keepChatNavigationStack, .skipReadHistory, .crashOnSlowQueries:
return DebugControllerSection.experiments.rawValue 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 return DebugControllerSection.experiments.rawValue
case .preferredVideoCodec: case .preferredVideoCodec:
return DebugControllerSection.videoExperiments.rawValue return DebugControllerSection.videoExperiments.rawValue
@ -137,70 +138,72 @@ private enum DebugControllerEntry: ItemListNodeEntry {
return 5 return 5
case .sendCriticalLogs: case .sendCriticalLogs:
return 6 return 6
case .accounts: case .sendAllLogs:
return 7 return 7
case .logToFile: case .accounts:
return 8 return 8
case .logToConsole: case .logToFile:
return 9 return 9
case .redactSensitiveData: case .logToConsole:
return 10 return 10
case .enableRaiseToSpeak: case .redactSensitiveData:
return 11 return 11
case .keepChatNavigationStack: case .enableRaiseToSpeak:
return 12 return 12
case .skipReadHistory: case .keepChatNavigationStack:
return 13 return 13
case .crashOnSlowQueries: case .skipReadHistory:
return 14 return 14
case .clearTips: case .crashOnSlowQueries:
return 15 return 15
case .crash: case .clearTips:
return 16 return 16
case .resetData: case .crash:
return 17 return 17
case .resetDatabase: case .resetData:
return 18 return 18
case .resetDatabaseAndCache: case .resetDatabase:
return 19 return 19
case .resetHoles: case .resetDatabaseAndCache:
return 20 return 20
case .reindexUnread: case .resetHoles:
return 21 return 21
case .resetBiometricsData: case .reindexUnread:
return 22 return 22
case .resetWebViewCache: case .resetBiometricsData:
return 23 return 23
case .optimizeDatabase: case .resetWebViewCache:
return 24 return 24
case .photoPreview: case .optimizeDatabase:
return 25 return 25
case .knockoutWallpaper: case .photoPreview:
return 26 return 26
case .experimentalCompatibility: case .knockoutWallpaper:
return 27 return 27
case .enableDebugDataDisplay: case .experimentalCompatibility:
return 28 return 28
case .acceleratedStickers: case .enableDebugDataDisplay:
return 29 return 29
case .experimentalBackground: case .acceleratedStickers:
return 30 return 30
case .inlineStickers: case .experimentalBackground:
return 31 return 31
case .localTranscription: case .inlineStickers:
return 32 return 32
case .enableReactionOverrides: case .localTranscription:
return 33 return 33
case .resetInAppPurchases: case .enableReactionOverrides:
return 34 return 34
case .playerEmbedding: case .restorePurchases:
return 35 return 35
case .playlistPlayback: case .playerEmbedding:
return 36 return 36
case .voiceConference: case .playlistPlayback:
return 37 return 37
case .voiceConference:
return 38
case let .preferredVideoCodec(index, _, _, _): case let .preferredVideoCodec(index, _, _, _):
return 38 + index return 39 + index
case .disableVideoAspectScaling: case .disableVideoAspectScaling:
return 100 return 100
case .enableVoipTcp: case .enableVoipTcp:
@ -402,7 +405,7 @@ private enum DebugControllerEntry: ItemListNodeEntry {
}) })
case .sendShareLogs: case .sendShareLogs:
return ItemListDisclosureItem(presentationData: presentationData, title: "Send Share Logs (Up to 40 MB)", label: "", sectionId: self.section, style: .blocks, action: { 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 |> deliverOnMainQueue).start(next: { logs in
let presentationData = arguments.sharedContext.currentPresentationData.with { $0 } let presentationData = arguments.sharedContext.currentPresentationData.with { $0 }
let actionSheet = ActionSheetController(presentationData: presentationData) let actionSheet = ActionSheetController(presentationData: presentationData)
@ -570,7 +573,7 @@ private enum DebugControllerEntry: ItemListNodeEntry {
}) })
case .sendNotificationLogs: case .sendNotificationLogs:
return ItemListDisclosureItem(presentationData: presentationData, title: "Send Notification Logs (Up to 40 MB)", label: "", sectionId: self.section, style: .blocks, action: { 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 |> deliverOnMainQueue).start(next: { logs in
let presentationData = arguments.sharedContext.currentPresentationData.with { $0 } let presentationData = arguments.sharedContext.currentPresentationData.with { $0 }
let actionSheet = ActionSheetController(presentationData: presentationData) let actionSheet = ActionSheetController(presentationData: presentationData)
@ -705,6 +708,101 @@ private enum DebugControllerEntry: ItemListNodeEntry {
arguments.presentController(actionSheet, nil) 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: case .accounts:
return ItemListDisclosureItem(presentationData: presentationData, title: "Accounts", label: "", sectionId: self.section, style: .blocks, action: { return ItemListDisclosureItem(presentationData: presentationData, title: "Accounts", label: "", sectionId: self.section, style: .blocks, action: {
guard let context = arguments.context else { guard let context = arguments.context else {
@ -1043,9 +1141,21 @@ private enum DebugControllerEntry: ItemListNodeEntry {
}) })
}).start() }).start()
}) })
case .resetInAppPurchases: case .restorePurchases:
return ItemListActionItem(presentationData: presentationData, title: "Reset IAP Transactions", kind: .destructive, alignment: .natural, sectionId: self.section, style: .blocks, action: { return ItemListActionItem(presentationData: presentationData, title: "Restore Purchases", kind: .generic, alignment: .natural, sectionId: self.section, style: .blocks, action: {
arguments.context?.inAppPurchaseManager?.finishAllTransactions() 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): case let .hostInfo(_, string):
return ItemListTextItem(presentationData: presentationData, text: .plain(string), sectionId: self.section) 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(.sendGroupCallLogs)
entries.append(.sendNotificationLogs(presentationData.theme)) entries.append(.sendNotificationLogs(presentationData.theme))
entries.append(.sendCriticalLogs(presentationData.theme)) entries.append(.sendCriticalLogs(presentationData.theme))
entries.append(.sendAllLogs)
if isMainApp { if isMainApp {
entries.append(.accounts(presentationData.theme)) entries.append(.accounts(presentationData.theme))
} }
@ -1111,7 +1222,7 @@ private func debugControllerEntries(sharedContext: SharedAccountContext, present
if case .internal = sharedContext.applicationBindings.appBuildType { if case .internal = sharedContext.applicationBindings.appBuildType {
entries.append(.enableReactionOverrides(experimentalSettings.enableReactionOverrides)) entries.append(.enableReactionOverrides(experimentalSettings.enableReactionOverrides))
} }
entries.append(.resetInAppPurchases(presentationData.theme)) entries.append(.restorePurchases(presentationData.theme))
entries.append(.playerEmbedding(experimentalSettings.playerEmbedding)) entries.append(.playerEmbedding(experimentalSettings.playerEmbedding))
entries.append(.playlistPlayback(experimentalSettings.playlistPlayback)) entries.append(.playlistPlayback(experimentalSettings.playlistPlayback))
} }

View File

@ -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) { func animateTransformScale(node: ASDisplayNode, from fromScale: CGFloat, additive: Bool = false, completion: ((Bool) -> Void)? = nil) {
let t = node.layer.transform let t = node.layer.transform
let currentScale = sqrt((t.m11 * t.m11) + (t.m12 * t.m12) + (t.m13 * t.m13)) 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 updateBounds(layer: CALayer, bounds: CGRect, completion: ((Bool) -> Void)?)
func updateFrame(layer: CALayer, frame: CGRect, completion: ((Bool) -> Void)?) func updateFrame(layer: CALayer, frame: CGRect, completion: ((Bool) -> Void)?)
func updateCornerRadius(layer: CALayer, cornerRadius: CGFloat, completion: ((Bool) -> Void)?) func updateCornerRadius(layer: CALayer, cornerRadius: CGFloat, completion: ((Bool) -> Void)?)
func updateContentsRect(layer: CALayer, contentsRect: CGRect, completion: ((Bool) -> Void)?)
} }
protocol AnyValueProviding { protocol AnyValueProviding {
@ -1908,6 +1934,21 @@ public final class ControlledTransition {
completion: completion 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 { public final class LegacyAnimator: ControlledTransitionAnimator {
@ -1963,6 +2004,10 @@ public final class ControlledTransition {
public func updateCornerRadius(layer: CALayer, cornerRadius: CGFloat, completion: ((Bool) -> Void)?) { public func updateCornerRadius(layer: CALayer, cornerRadius: CGFloat, completion: ((Bool) -> Void)?) {
self.transition.updateCornerRadius(layer: layer, cornerRadius: cornerRadius, completion: completion) 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 public let animator: ControlledTransitionAnimator

View File

@ -910,7 +910,6 @@ open class NavigationController: UINavigationController, ContainableController,
visibleRootModalDismissProgress = effectiveRootModalDismissProgress visibleRootModalDismissProgress = effectiveRootModalDismissProgress
additionalModalFrameProgress = 1.0 - topModalDismissProgress additionalModalFrameProgress = 1.0 - topModalDismissProgress
} else { } else {
// effectiveRootModalDismissProgress = ((topModalIsFlat && !topFlatModalHasProgress) || isLandscape) ? 1.0 : topModalDismissProgress
effectiveRootModalDismissProgress = topModalDismissProgress effectiveRootModalDismissProgress = topModalDismissProgress
visibleRootModalDismissProgress = effectiveRootModalDismissProgress visibleRootModalDismissProgress = effectiveRootModalDismissProgress
additionalModalFrameProgress = 0.0 additionalModalFrameProgress = 0.0

View File

@ -123,7 +123,7 @@ final class StickerPackPreviewGridItemNode: GridItemNode {
self.imageNode.isHidden = true self.imageNode.isHidden = true
if isVerified { if isVerified {
let animationNode = AnimatedStickerNode() let animationNode = DefaultAnimatedStickerNodeImpl()
self.animationNode = animationNode self.animationNode = animationNode
if let placeholderNode = self.placeholderNode { if let placeholderNode = self.placeholderNode {

View File

@ -75,7 +75,7 @@ private final class StickerPreviewPeekContentNode: ASDisplayNode, PeekController
case let .image(data): case let .image(data):
self.imageNode.image = UIImage(data: data) self.imageNode.image = UIImage(data: data)
case .animation, .video: case .animation, .video:
let animationNode = AnimatedStickerNode() let animationNode = DefaultAnimatedStickerNodeImpl()
self.animationNode = animationNode self.animationNode = animationNode
let dimensions = PixelDimensions(width: 512, height: 512) let dimensions = PixelDimensions(width: 512, height: 512)
let fittedDimensions = dimensions.cgSize.aspectFitted(CGSize(width: 400.0, height: 400.0)) 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 { if case .video = item.content {
isVideo = true 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 self.animationNode?.visibility = true
} }

View File

@ -32,6 +32,11 @@ public final class InAppPurchaseManager: NSObject {
case notAllowed case notAllowed
} }
public enum RestoreState {
case succeed
case failed
}
private final class PaymentTransactionContext { private final class PaymentTransactionContext {
var state: SKPaymentTransactionState? var state: SKPaymentTransactionState?
let subscriber: (TransactionState) -> Void let subscriber: (TransactionState) -> Void
@ -59,6 +64,8 @@ public final class InAppPurchaseManager: NSObject {
private let stateQueue = Queue() private let stateQueue = Queue()
private var paymentContexts: [String: PaymentTransactionContext] = [:] private var paymentContexts: [String: PaymentTransactionContext] = [:]
private var onRestoreCompletion: ((RestoreState) -> Void)?
private let disposableSet = DisposableDict<String>() private let disposableSet = DisposableDict<String>()
public init(engine: TelegramEngine, premiumProductId: String) { public init(engine: TelegramEngine, premiumProductId: String) {
@ -79,6 +86,7 @@ public final class InAppPurchaseManager: NSObject {
guard !self.premiumProductId.isEmpty else { guard !self.premiumProductId.isEmpty else {
return return
} }
Logger.shared.log("InAppPurchaseManager", "Requesting products")
let productRequest = SKProductsRequest(productIdentifiers: Set([self.premiumProductId])) let productRequest = SKProductsRequest(productIdentifiers: Set([self.premiumProductId]))
productRequest.delegate = self productRequest.delegate = self
productRequest.start() productRequest.start()
@ -93,7 +101,17 @@ public final class InAppPurchaseManager: NSObject {
return self.productsPromise.get() 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() { public func finishAllTransactions() {
Logger.shared.log("InAppPurchaseManager", "Finishing all transactions")
let paymentQueue = SKPaymentQueue.default() let paymentQueue = SKPaymentQueue.default()
let transactions = paymentQueue.transactions let transactions = paymentQueue.transactions
for transaction in transactions { for transaction in transactions {
@ -102,6 +120,8 @@ public final class InAppPurchaseManager: NSObject {
} }
public func buyProduct(_ product: Product, account: Account) -> Signal<PurchaseState, PurchaseError> { 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) let payment = SKPayment(product: product.skProduct)
SKPaymentQueue.default().add(payment) SKPaymentQueue.default().add(payment)
@ -162,7 +182,10 @@ extension InAppPurchaseManager: SKProductsRequestDelegate {
self.productRequest = nil self.productRequest = nil
Queue.mainQueue().async { 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? let transactionState: TransactionState?
switch transaction.transactionState { switch transaction.transactionState {
case .purchased: case .purchased:
Logger.shared.log("InAppPurchaseManager", "Transaction \(transaction.transactionIdentifier ?? ""), original transaction \(transaction.original?.transactionIdentifier ?? "none") purchased")
let transactionIdentifier = transaction.transactionIdentifier let transactionIdentifier = transaction.transactionIdentifier
transactionState = .purchased(transactionId: transactionIdentifier) transactionState = .purchased(transactionId: transactionIdentifier)
if let transactionIdentifier = transactionIdentifier { if let transactionIdentifier = transactionIdentifier {
self.disposableSet.set( 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) queue.finishTransaction(transaction)
}, completed: { }, completed: {
Logger.shared.log("InAppPurchaseManager", "Transaction \(transaction.transactionIdentifier ?? "") successfully assigned AppStore transaction")
queue.finishTransaction(transaction) queue.finishTransaction(transaction)
}), }),
forKey: transactionIdentifier forKey: transactionIdentifier
) )
} }
case .restored: case .restored:
Logger.shared.log("InAppPurchaseManager", "Transaction \(transaction.transactionIdentifier ?? ""), original transaction \(transaction.original?.transactionIdentifier ?? "") restroring")
let transactionIdentifier = transaction.transactionIdentifier let transactionIdentifier = transaction.transactionIdentifier
transactionState = .restored(transactionId: 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: case .failed:
Logger.shared.log("InAppPurchaseManager", "Transaction \(transaction.transactionIdentifier ?? "") failed \((transaction.error as? SKError)?.localizedDescription ?? "")")
transactionState = .failed(error: transaction.error as? SKError) transactionState = .failed(error: transaction.error as? SKError)
queue.finishTransaction(transaction) queue.finishTransaction(transaction)
case .purchasing: case .purchasing:
Logger.shared.log("InAppPurchaseManager", "Transaction \(transaction.transactionIdentifier ?? "") purchasing")
transactionState = .purchasing transactionState = .purchasing
case .deferred: case .deferred:
Logger.shared.log("InAppPurchaseManager", "Transaction \(transaction.transactionIdentifier ?? "") deferred")
transactionState = .deferred transactionState = .deferred
default: default:
transactionState = nil 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
}
}
} }

View File

@ -80,7 +80,7 @@ class InviteLinkHeaderItemNode: ListViewItemNode {
self.titleNode.contentMode = .left self.titleNode.contentMode = .left
self.titleNode.contentsScale = UIScreen.main.scale self.titleNode.contentsScale = UIScreen.main.scale
self.animationNode = AnimatedStickerNode() self.animationNode = DefaultAnimatedStickerNodeImpl()
super.init(layerBacked: false, dynamicBounce: false) super.init(layerBacked: false, dynamicBounce: false)

View File

@ -58,7 +58,7 @@ final class InviteRequestsEmptyStateItemNode: ItemListControllerEmptyStateItemNo
init(item: InviteRequestsEmptyStateItem) { init(item: InviteRequestsEmptyStateItem) {
self.item = item 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.setup(source: AnimatedStickerNodeLocalFileSource(name: "TwoFactorSetupRememberSuccess"), width: 192, height: 192, playbackMode: .once, mode: .direct(cachePathPrefix: nil))
self.animationNode.visibility = true self.animationNode.visibility = true

View File

@ -761,14 +761,14 @@ class ItemListStickerPackItemNode: ItemListRevealOptionsItemNode {
if let current = strongSelf.animationNode { if let current = strongSelf.animationNode {
animationNode = current animationNode = current
} else { } else {
animationNode = AnimatedStickerNode() animationNode = DefaultAnimatedStickerNodeImpl()
animationNode.started = { [weak self] in animationNode.started = { [weak self] in
self?.removePlaceholder(animated: false) self?.removePlaceholder(animated: false)
} }
strongSelf.animationNode = animationNode strongSelf.animationNode = animationNode
strongSelf.addSubnode(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.visibility = strongSelf.visibility != .none && item.playAnimatedStickers
animationNode.isHidden = !item.playAnimatedStickers animationNode.isHidden = !item.playAnimatedStickers

View File

@ -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; + (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 *, + (NSArray *)resultSignalsForSelectionContext:(TGMediaSelectionContext *)selectionContext editingContext:(TGMediaEditingContext *)editingContext currentItem:(id<TGMediaSelectableItem>)currentItem descriptionGenerator:(id (^)(id, NSAttributedString *, NSString *))descriptionGenerator;
NSString *))descriptionGenerator;
@end @end

View File

@ -62,7 +62,7 @@ class LegacyPaintStickerView: UIView, TGPhotoPaintStickerRenderView {
if let dimensions = self.file.dimensions { if let dimensions = self.file.dimensions {
if self.file.isAnimatedSticker || self.file.isVideoSticker { if self.file.isAnimatedSticker || self.file.isVideoSticker {
if self.animationNode == nil { if self.animationNode == nil {
let animationNode = AnimatedStickerNode() let animationNode = DefaultAnimatedStickerNodeImpl()
animationNode.autoplay = false animationNode.autoplay = false
self.animationNode = animationNode self.animationNode = animationNode
animationNode.started = { [weak self, weak animationNode] in 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 dimensions = self.file.dimensions ?? PixelDimensions(width: 512, height: 512)
let fittedDimensions = dimensions.cgSize.aspectFitted(CGSize(width: 384.0, height: 384.0)) 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) 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) self.cachedDisposable.set((source.cachedDataPath(width: 384, height: 384)
|> deliverOn(Queue.concurrentDefaultQueue())).start()) |> deliverOn(Queue.concurrentDefaultQueue())).start())
@ -150,7 +150,7 @@ class LegacyPaintStickerView: UIView, TGPhotoPaintStickerRenderView {
func play(fromFrame frameIndex: Int) { func play(fromFrame frameIndex: Int) {
self.isVisible = true self.isVisible = true
self.updateVisibility() self.updateVisibility()
self.animationNode?.play(fromIndex: frameIndex) self.animationNode?.play(firstFrame: false, fromIndex: frameIndex)
} }
func copyStickerView(_ view: TGPhotoPaintStickerRenderView!) { func copyStickerView(_ view: TGPhotoPaintStickerRenderView!) {
@ -158,7 +158,7 @@ class LegacyPaintStickerView: UIView, TGPhotoPaintStickerRenderView {
return return
} }
self.animationNode?.cloneCurrentFrame(from: animationNode) self.animationNode?.cloneCurrentFrame(from: animationNode)
self.animationNode?.play(fromIndex: animationNode.currentFrameIndex) self.animationNode?.play(firstFrame: false, fromIndex: animationNode.currentFrameIndex)
self.updateVisibility() self.updateVisibility()
} }

View File

@ -37,7 +37,7 @@ final class LocationPlaceholderNode: ASDisplayNode {
playbackMode = .loop 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.setup(source: AnimatedStickerNodeLocalFileSource(name: name), width: 320, height: 320, playbackMode: playbackMode, mode: .direct(cachePathPrefix: nil))
self.animationNode.visibility = true self.animationNode.visibility = true

View File

@ -43,7 +43,7 @@ final class MediaPickerPlaceholderNode: ASDisplayNode {
playbackMode = .once 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.setup(source: AnimatedStickerNodeLocalFileSource(name: name), width: 320, height: 320, playbackMode: playbackMode, mode: .direct(cachePathPrefix: nil))
self.animationNode.visibility = true self.animationNode.visibility = true

View File

@ -1269,17 +1269,17 @@ private final class TwoFactorDataInputScreenNode: ViewControllerTracingNode, UIS
case .password, .passwordRecovery, .emailAddress, .updateEmailAddress: case .password, .passwordRecovery, .emailAddress, .updateEmailAddress:
self.monkeyNode = ManagedMonkeyAnimationNode() self.monkeyNode = ManagedMonkeyAnimationNode()
case .emailConfirmation, .passwordRecoveryEmail: 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.setup(source: AnimatedStickerNodeLocalFileSource(name: "TwoFactorSetupMail"), width: 272, height: 272, playbackMode: .once, mode: .direct(cachePathPrefix: nil))
animatedStickerNode.visibility = true animatedStickerNode.visibility = true
self.animatedStickerNode = animatedStickerNode self.animatedStickerNode = animatedStickerNode
case .passwordHint: 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.setup(source: AnimatedStickerNodeLocalFileSource(name: "TwoFactorSetupHint"), width: 272, height: 272, playbackMode: .once, mode: .direct(cachePathPrefix: nil))
animatedStickerNode.visibility = true animatedStickerNode.visibility = true
self.animatedStickerNode = animatedStickerNode self.animatedStickerNode = animatedStickerNode
case .rememberPassword: 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.setup(source: AnimatedStickerNodeLocalFileSource(name: "TwoFactorSetupRemember"), width: 272, height: 272, playbackMode: .count(3), mode: .direct(cachePathPrefix: nil))
animatedStickerNode.visibility = true animatedStickerNode.visibility = true
self.animatedStickerNode = animatedStickerNode self.animatedStickerNode = animatedStickerNode

View File

@ -155,7 +155,7 @@ private final class TwoFactorAuthSplashScreenNode: ViewControllerTracingNode {
self.presentationData = presentationData self.presentationData = presentationData
self.mode = mode self.mode = mode
self.animationNode = AnimatedStickerNode() self.animationNode = DefaultAnimatedStickerNodeImpl()
let title: String let title: String
let texts: [NSAttributedString] 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)] texts = [NSAttributedString(string: self.presentationData.strings.TwoFactorSetup_Done_Text, font: textFont, textColor: textColor)]
buttonText = doneText 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.animationSize = CGSize(width: 124.0, height: 124.0)
self.animationNode.visibility = true self.animationNode.visibility = true
case let .recoveryDone(_, _, isPasswordSet): 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)] texts = [NSAttributedString(string: self.presentationData.strings.TwoFactorRemember_Done_Text, font: textFont, textColor: textColor)]
buttonText = self.presentationData.strings.TwoFactorRemember_Done_Action 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.animationSize = CGSize(width: 124.0, height: 124.0)
self.animationNode.visibility = true self.animationNode.visibility = true
} }

View File

@ -730,7 +730,19 @@ private func channelVisibilityControllerEntries(presentationData: PresentationDa
if case .revokeNames = mode { if case .revokeNames = mode {
let count = Int32(publicChannelsToRevoke?.count ?? 0) 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 { if let publicChannelsToRevoke = publicChannelsToRevoke {
var index: Int32 = 0 var index: Int32 = 0
@ -1419,7 +1431,8 @@ public func channelVisibilityController(context: AccountContext, updatedPresenta
var rightNavigationButton: ItemListNavigationButton? var rightNavigationButton: ItemListNavigationButton?
if case .revokeNames = mode { 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: { footerItem = IncreaseLimitFooterItem(theme: presentationData.theme, title: presentationData.strings.Premium_IncreaseLimit, colorful: true, action: {
let controller = PremiumIntroScreen(context: context, source: .publicLinks) let controller = PremiumIntroScreen(context: context, source: .publicLinks)
pushControllerImpl?(controller) pushControllerImpl?(controller)

View File

@ -39,7 +39,7 @@ private final class ReportPeerHeaderActionSheetItemNode: ActionSheetItemNode {
let textFont = Font.regular(floor(theme.baseFontSize * 13.0 / 17.0)) 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.setup(source: AnimatedStickerNodeLocalFileSource(name: "Cop"), width: 192, height: 192, playbackMode: .count(2), mode: .direct(cachePathPrefix: nil))
self.animationNode.visibility = true self.animationNode.visibility = true

View File

@ -74,7 +74,7 @@ class PeersNearbyHeaderItemNode: ListViewItemNode {
self.titleNode.contentMode = .left self.titleNode.contentMode = .left
self.titleNode.contentsScale = UIScreen.main.scale self.titleNode.contentsScale = UIScreen.main.scale
self.animationNode = AnimatedStickerNode() self.animationNode = DefaultAnimatedStickerNodeImpl()
super.init(layerBacked: false, dynamicBounce: false) super.init(layerBacked: false, dynamicBounce: false)

View File

@ -49,7 +49,7 @@ private final class MediaBoxFileMap {
return nil return nil
} }
var truncationSizeValue: Int32 = 0 var truncationSizeValue: Int64 = 0
var data = Data(count: Int(8 + count * 2 * 8)) var data = Data(count: Int(8 + count * 2 * 8))
let dataCount = data.count let dataCount = data.count
@ -89,8 +89,10 @@ private final class MediaBoxFileMap {
self.ranges = ranges self.ranges = ranges
if truncationSizeValue == -1 { if truncationSizeValue == -1 {
self.truncationSize = nil self.truncationSize = nil
} else if truncationSizeValue < 0 {
self.truncationSize = nil
} else { } else {
self.truncationSize = Int64(truncationSizeValue) self.truncationSize = truncationSizeValue
} }
} else { } else {
let crc: UInt32 = firstUInt32 let crc: UInt32 = firstUInt32
@ -215,7 +217,8 @@ private final class MediaBoxFileMap {
} else { } else {
maxValue = Int64.max 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) let clippedRangeSet = RangeSet<Int64>(clippedRange)
if self.ranges.isSuperset(of: clippedRangeSet) { if self.ranges.isSuperset(of: clippedRangeSet) {

View File

@ -780,7 +780,7 @@ private final class DemoSheetContent: CombinedComponent {
videoFile: configuration.videos["no_ads"] videoFile: configuration.videos["no_ads"]
)), )),
title: strings.Premium_NoAds, title: strings.Premium_NoAds,
text: strings.Premium_NoAdsInfo, text: isStandalone ? strings.Premium_NoAdsStandaloneInfo : strings.Premium_NoAdsInfo,
textColor: textColor textColor: textColor
) )
) )
@ -950,6 +950,7 @@ private final class DemoSheetContent: CombinedComponent {
) )
let buttonText: String let buttonText: String
var buttonAnimationName: String?
if state.isPremium == true { if state.isPremium == true {
buttonText = strings.Common_OK buttonText = strings.Common_OK
} else { } else {
@ -957,16 +958,21 @@ private final class DemoSheetContent: CombinedComponent {
case let .intro(price): case let .intro(price):
buttonText = strings.Premium_SubscribeFor(price ?? "").string buttonText = strings.Premium_SubscribeFor(price ?? "").string
case .other: case .other:
switch component.subject { switch component.subject {
case .uniqueReactions: case .uniqueReactions:
buttonText = strings.Premium_Reactions_Proceed buttonText = strings.Premium_Reactions_Proceed
case .premiumStickers: buttonAnimationName = "premium_unlock"
buttonText = strings.Premium_Stickers_Proceed case .premiumStickers:
case .appIcons: buttonText = strings.Premium_Stickers_Proceed
buttonText = strings.Premium_AppIcons_Proceed buttonAnimationName = "premium_unlock"
default: case .appIcons:
buttonText = strings.Common_OK 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, height: 50.0,
cornerRadius: 11.0, cornerRadius: 11.0,
gloss: state.isPremium != true, gloss: state.isPremium != true,
animationName: isStandalone && component.subject == .uniqueReactions ? "premium_unlock" : nil, animationName: isStandalone ? buttonAnimationName : nil,
iconPosition: .right, iconPosition: .right,
iconSpacing: 4.0, iconSpacing: 4.0,
action: { [weak component, weak state] in action: { [weak component, weak state] in

View File

@ -382,6 +382,8 @@ private final class SectionGroupComponent: Component {
buttonView = current buttonView = current
} else { } else {
buttonView = HighlightTrackingButton() buttonView = HighlightTrackingButton()
buttonView.isMultipleTouchEnabled = false
buttonView.isExclusiveTouch = true
buttonView.addTarget(self, action: #selector(self.buttonPressed(_:)), for: .touchUpInside) buttonView.addTarget(self, action: #selector(self.buttonPressed(_:)), for: .touchUpInside)
self.buttonViews[item.content.id] = buttonView self.buttonViews[item.content.id] = buttonView
self.addSubview(buttonView) self.addSubview(buttonView)
@ -1622,7 +1624,7 @@ private final class PremiumIntroScreenComponent: CombinedComponent {
titleScale = 1.0 - fraction * 0.36 titleScale = 1.0 - fraction * 0.36
if state.otherPeerName != nil { if state.otherPeerName != nil {
titleAlpha = min(1.0, fraction * 1.5) titleAlpha = min(1.0, fraction * 1.1)
} else { } else {
titleAlpha = 1.0 titleAlpha = 1.0
} }
@ -1656,7 +1658,7 @@ private final class PremiumIntroScreenComponent: CombinedComponent {
context.add(secondaryTitle 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))) .position(CGPoint(x: context.availableSize.width / 2.0, y: max(topInset + 160.0 - titleOffset, environment.statusBarHeight + (environment.navigationHeight - environment.statusBarHeight) / 2.0)))
.scale(titleScale) .scale(titleScale)
.opacity(1.0 - titleAlpha) .opacity(max(0.0, 1.0 - titleAlpha * 1.8))
) )
if state.isPremium == true { if state.isPremium == true {

View File

@ -281,9 +281,11 @@ class PremiumStarComponent: Component {
} }
private func maybeAnimateIn() { 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 return
} }
containerView = containerView.subviews[2].subviews[1]
if let animationColor = self.animationColor { if let animationColor = self.animationColor {
let newNode = node.clone() 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.sceneView.layer.animatePosition(from: sourcePosition, to: targetPosition, duration: 1.0, timingFunction: kCAMediaTimingFunctionSpring, completion: { _ in
self.addSubview(self.sceneView) self.addSubview(self.sceneView)
self.sceneView.center = initialPosition self.sceneView.center = initialPosition
})
Queue.mainQueue().after(0.4, {
animateFrom.alpha = 1.0 animateFrom.alpha = 1.0
}) })

View File

@ -104,7 +104,7 @@ private class StickerNode: ASDisplayNode {
self.imageNode = TransformImageNode() self.imageNode = TransformImageNode()
if file.isPremiumSticker || forceIsPremium { if file.isPremiumSticker || forceIsPremium {
let animationNode = AnimatedStickerNode() let animationNode = DefaultAnimatedStickerNodeImpl()
animationNode.automaticallyLoadFirstFrame = true animationNode.automaticallyLoadFirstFrame = true
self.animationNode = animationNode 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()) 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 source = AnimatedStickerResourceSource(account: self.context.account, resource: effect.resource, fitzModifier: nil)
let additionalAnimationNode = AnimatedStickerNode() let additionalAnimationNode = DefaultAnimatedStickerNodeImpl()
var pathPrefix: String? var pathPrefix: String?
pathPrefix = context.account.postbox.mediaBox.shortLivedResourceCachePathPrefix(effect.resource.id) pathPrefix = context.account.postbox.mediaBox.shortLivedResourceCachePathPrefix(effect.resource.id)
@ -232,7 +232,7 @@ private class StickerNode: ASDisplayNode {
} else if isVisible { } else if isVisible {
additionalAnimationNode.visibility = isVisible additionalAnimationNode.visibility = isVisible
if !wasVisible { if !wasVisible {
additionalAnimationNode.play(fromIndex: 0) additionalAnimationNode.play(firstFrame: false, fromIndex: 0)
Queue.mainQueue().after(0.05, { Queue.mainQueue().after(0.05, {
additionalAnimationNode.alpha = 1.0 additionalAnimationNode.alpha = 1.0
}) })

View File

@ -255,8 +255,8 @@ public final class QrCodeScreen: ViewController {
self.qrImageNode.clipsToBounds = true self.qrImageNode.clipsToBounds = true
self.qrImageNode.cornerRadius = 16.0 self.qrImageNode.cornerRadius = 16.0
self.qrIconNode = AnimatedStickerNode() self.qrIconNode = DefaultAnimatedStickerNodeImpl()
self.qrIconNode.setup(source: AnimatedStickerNodeLocalFileSource(name: "PlaneLogo"), width: 240, height: 240, mode: .direct(cachePathPrefix: nil)) self.qrIconNode.setup(source: AnimatedStickerNodeLocalFileSource(name: "PlaneLogo"), width: 240, height: 240, playbackMode: .loop, mode: .direct(cachePathPrefix: nil))
self.qrIconNode.visibility = true self.qrIconNode.visibility = true
super.init() super.init()

View File

@ -656,7 +656,7 @@ public final class ReactionContextNode: ASDisplayNode, UIScrollViewDelegate {
transition.updateBounds(node: itemNode, bounds: CGRect(origin: CGPoint(), size: expandedFrame.size)) 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) itemNode.updateLayout(size: expandedFrame.size, isExpanded: true, largeExpanded: self.didTriggerExpandedReaction, isPreviewing: false, transition: transition)
let additionalAnimationNode = AnimatedStickerNode() let additionalAnimationNode = DefaultAnimatedStickerNodeImpl()
let additionalAnimation: TelegramMediaFile let additionalAnimation: TelegramMediaFile
if self.didTriggerExpandedReaction { 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) itemNode.updateLayout(size: expandedFrame.size, isExpanded: true, largeExpanded: isLarge, isPreviewing: false, transition: .immediate)
let additionalAnimationNode = AnimatedStickerNode() let additionalAnimationNode = DefaultAnimatedStickerNodeImpl()
let additionalAnimation: TelegramMediaFile let additionalAnimation: TelegramMediaFile
if isLarge && !forceSmallEffectAnimation { if isLarge && !forceSmallEffectAnimation {

View File

@ -72,11 +72,11 @@ public final class ReactionNode: ASDisplayNode, ReactionItemNode {
self.item = item self.item = item
self.hasAppearAnimation = hasAppearAnimation self.hasAppearAnimation = hasAppearAnimation
self.staticAnimationNode = AnimatedStickerNode() self.staticAnimationNode = DefaultAnimatedStickerNodeImpl()
if hasAppearAnimation { if hasAppearAnimation {
self.staticAnimationNode.isHidden = true self.staticAnimationNode.isHidden = true
self.animateInAnimationNode = AnimatedStickerNode() self.animateInAnimationNode = DefaultAnimatedStickerNodeImpl()
} }
super.init() super.init()
@ -144,9 +144,9 @@ public final class ReactionNode: ASDisplayNode, ReactionItemNode {
self.staticAnimationNode.completed = { [weak self] _ in self.staticAnimationNode.completed = { [weak self] _ in
self?.mainAnimationCompletion?() self?.mainAnimationCompletion?()
} }
self.staticAnimationNode.play(fromIndex: 0) self.staticAnimationNode.play(firstFrame: false, fromIndex: 0)
} else if isExpanded, self.animationNode == nil { } else if isExpanded, self.animationNode == nil {
let animationNode = AnimatedStickerNode() let animationNode = DefaultAnimatedStickerNodeImpl()
animationNode.automaticallyLoadFirstFrame = true animationNode.automaticallyLoadFirstFrame = true
self.animationNode = animationNode self.animationNode = animationNode
self.addSubnode(animationNode) self.addSubnode(animationNode)
@ -235,7 +235,7 @@ public final class ReactionNode: ASDisplayNode, ReactionItemNode {
if self.animationNode == nil { if self.animationNode == nil {
if isPreviewing { if isPreviewing {
if self.stillAnimationNode == nil { if self.stillAnimationNode == nil {
let stillAnimationNode = AnimatedStickerNode() let stillAnimationNode = DefaultAnimatedStickerNodeImpl()
self.stillAnimationNode = stillAnimationNode self.stillAnimationNode = stillAnimationNode
self.addSubnode(stillAnimationNode) self.addSubnode(stillAnimationNode)

View File

@ -1015,7 +1015,7 @@ private class StorageUsageClearProgressOverlayNode: ASDisplayNode, ActionSheetGr
init(presentationData: PresentationData) { init(presentationData: PresentationData) {
self.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.setup(source: AnimatedStickerNodeLocalFileSource(name: "ClearCache"), width: 256, height: 256, playbackMode: .loop, mode: .direct(cachePathPrefix: nil))
self.animationNode.visibility = true self.animationNode.visibility = true

View File

@ -58,7 +58,7 @@ final class PrivacyIntroControllerNode: ViewControllerTracingNode {
self.proceedAction = proceedAction self.proceedAction = proceedAction
self.iconNode = ASImageNode() self.iconNode = ASImageNode()
self.animationNode = AnimatedStickerNode() self.animationNode = DefaultAnimatedStickerNodeImpl()
self.titleNode = ASTextNode() self.titleNode = ASTextNode()
self.textNode = ASTextNode() self.textNode = ASTextNode()

View File

@ -84,7 +84,7 @@ class RecentSessionsHeaderItemNode: ListViewItemNode {
self.titleNode.contentMode = .left self.titleNode.contentMode = .left
self.titleNode.contentsScale = UIScreen.main.scale 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) 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.setup(source: AnimatedStickerNodeLocalFileSource(name: item.animationName), width: 256, height: 256, playbackMode: .still(.start), mode: .direct(cachePathPrefix: nil))
strongSelf.animationNode.visibility = true strongSelf.animationNode.visibility = true
Queue.mainQueue().after(0.3) { Queue.mainQueue().after(0.3) {
strongSelf.animationNode.play() strongSelf.animationNode.play(firstFrame: false, fromIndex: nil)
} }
} }
strongSelf.item = item strongSelf.item = item

View File

@ -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() { private func beginReactionAnimation() {
if let item = self.item, let updatedReaction = item.reaction, let availableReactions = item.availableReactions, let messageNode = self.messageNode as? ChatMessageItemNodeProtocol { 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) { if let targetView = messageNode.targetReactionView(value: updatedReaction) {

View File

@ -302,7 +302,7 @@ private final class ThemeCarouselThemeItemIconNode : ListViewItemNode {
Queue.mainQueue().after(0.1) { Queue.mainQueue().after(0.1) {
if !wasSelected { if !wasSelected {
animatedStickerNode.seekTo(.frameIndex(0)) animatedStickerNode.seekTo(.frameIndex(0))
animatedStickerNode.play() animatedStickerNode.play(firstFrame: false, fromIndex: nil)
let scale: CGFloat = 2.6 let scale: CGFloat = 2.6
animatedStickerNode.transform = CATransform3DMakeScale(scale, scale, 1.0) animatedStickerNode.transform = CATransform3DMakeScale(scale, scale, 1.0)
@ -417,7 +417,7 @@ private final class ThemeCarouselThemeItemIconNode : ListViewItemNode {
if let current = strongSelf.animatedStickerNode { if let current = strongSelf.animatedStickerNode {
animatedStickerNode = current animatedStickerNode = current
} else { } else {
animatedStickerNode = AnimatedStickerNode() animatedStickerNode = DefaultAnimatedStickerNodeImpl()
animatedStickerNode.started = { [weak self] in animatedStickerNode.started = { [weak self] in
self?.emojiImageNode.isHidden = true self?.emojiImageNode.isHidden = true
} }

View File

@ -258,7 +258,7 @@ private final class ThemeGridThemeItemIconNode : ASDisplayNode {
if let current = self.animatedStickerNode { if let current = self.animatedStickerNode {
animatedStickerNode = current animatedStickerNode = current
} else { } else {
animatedStickerNode = AnimatedStickerNode() animatedStickerNode = DefaultAnimatedStickerNodeImpl()
animatedStickerNode.started = { [weak self] in animatedStickerNode.started = { [weak self] in
self?.emojiImageNode.isHidden = true self?.emojiImageNode.isHidden = true
} }

View File

@ -212,11 +212,11 @@ public final class ShareProlongedLoadingContainerNode: ASDisplayNode, ShareConte
self.theme = theme self.theme = theme
self.strings = strings 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.setup(source: AnimatedStickerNodeLocalFileSource(name: "ShareProgress"), width: 384, height: 384, playbackMode: .loop, mode: .direct(cachePathPrefix: nil))
self.animationNode.visibility = true 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.setup(source: AnimatedStickerNodeLocalFileSource(name: "ShareDone"), width: 384, height: 384, playbackMode: .once, mode: .direct(cachePathPrefix: nil))
self.doneAnimationNode.visibility = false self.doneAnimationNode.visibility = false
self.doneAnimationNode.isHidden = true self.doneAnimationNode.isHidden = true

View File

@ -264,7 +264,7 @@ class DiceAnimatedStickerNode: ASDisplayNode {
self.account = account self.account = account
self.intrinsicSize = size self.intrinsicSize = size
self.animationNode = AnimatedStickerNode() self.animationNode = DefaultAnimatedStickerNodeImpl()
self.animationNode.autoplay = true self.animationNode.autoplay = true
super.init() super.init()

View File

@ -751,6 +751,8 @@ public final class SolidRoundedButtonView: UIView {
} }
self.buttonNode = HighlightTrackingButton() self.buttonNode = HighlightTrackingButton()
self.buttonNode.isMultipleTouchEnabled = false
self.buttonNode.isExclusiveTouch = true
self.titleNode = ImmediateTextView() self.titleNode = ImmediateTextView()
self.titleNode.isUserInteractionEnabled = false self.titleNode.isUserInteractionEnabled = false

View File

@ -56,7 +56,7 @@ final class StatsEmptyStateItemNode: ItemListControllerEmptyStateItemNode {
init(item: StatsEmptyStateItem) { init(item: StatsEmptyStateItem) {
self.item = item 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.setup(source: AnimatedStickerNodeLocalFileSource(name: "Charts"), width: 192, height: 192, playbackMode: .loop, mode: .direct(cachePathPrefix: nil))
self.animationNode.visibility = true self.animationNode.visibility = true

View File

@ -223,7 +223,7 @@ final class StickerPackPreviewGridItemNode: GridItemNode {
} }
if self.animationNode == nil { if self.animationNode == nil {
let animationNode = AnimatedStickerNode() let animationNode = DefaultAnimatedStickerNodeImpl()
self.animationNode = animationNode self.animationNode = animationNode
self.insertSubnode(animationNode, aboveSubnode: self.imageNode) self.insertSubnode(animationNode, aboveSubnode: self.imageNode)
animationNode.started = { [weak self] in 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)) 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 self.animationNode?.visibility = visibility

View File

@ -116,7 +116,7 @@ public final class StickerPreviewPeekContentNode: ASDisplayNode, PeekControllerC
let isPremiumSticker = item.file.isPremiumSticker let isPremiumSticker = item.file.isPremiumSticker
if item.file.isAnimatedSticker || item.file.isVideoSticker { if item.file.isAnimatedSticker || item.file.isVideoSticker {
let animationNode = AnimatedStickerNode() let animationNode = DefaultAnimatedStickerNodeImpl()
self.animationNode = animationNode self.animationNode = animationNode
let dimensions = item.file.dimensions ?? PixelDimensions(width: 512, height: 512) 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()) 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 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.setup(source: source, width: Int(fittedDimensions.width * 2.0), height: Int(fittedDimensions.height * 2.0), playbackMode: .once, mode: .direct(cachePathPrefix: nil))
additionalAnimationNode.visibility = true additionalAnimationNode.visibility = true
self.additionalAnimationNode = additionalAnimationNode self.additionalAnimationNode = additionalAnimationNode
@ -153,8 +153,8 @@ public final class StickerPreviewPeekContentNode: ASDisplayNode, PeekControllerC
animationNode.completed = { [weak self] _ in animationNode.completed = { [weak self] _ in
if let strongSelf = self, let animationNode = strongSelf.animationNode, let additionalAnimationNode = strongSelf.additionalAnimationNode { if let strongSelf = self, let animationNode = strongSelf.animationNode, let additionalAnimationNode = strongSelf.additionalAnimationNode {
Queue.mainQueue().async { Queue.mainQueue().async {
animationNode.play() animationNode.play(firstFrame: false, fromIndex: nil)
additionalAnimationNode.play() additionalAnimationNode.play(firstFrame: false, fromIndex: nil)
} }
} }
} }

View File

@ -125,7 +125,7 @@ private final class TabBarItemNode: ASDisplayNode {
self.animationContainerNode = ASDisplayNode() self.animationContainerNode = ASDisplayNode()
self.animationNode = AnimatedStickerNode() self.animationNode = DefaultAnimatedStickerNodeImpl()
self.animationNode.autoplay = true self.animationNode.autoplay = true
self.animationNode.automaticallyLoadLastFrame = 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]) self.itemSelected(closestNode.0, longTap, [container.imageNode.imageNode, container.imageNode.textImageNode, container.badgeContainerNode])
if previousSelectedIndex != closestNode.0 { if previousSelectedIndex != closestNode.0 {
if let selectedIndex = self.selectedIndex, let _ = self.tabBarItems[selectedIndex].item.animationName { if let selectedIndex = self.selectedIndex, let _ = self.tabBarItems[selectedIndex].item.animationName {
container.imageNode.animationNode.play() container.imageNode.animationNode.play(firstFrame: false, fromIndex: nil)
} }
} }
} }

View File

@ -917,7 +917,7 @@ fileprivate let parsers: [Int32 : (BufferReader) -> Any?] = {
dict[415997816] = { return Api.help.InviteText.parse_inviteText($0) } dict[415997816] = { return Api.help.InviteText.parse_inviteText($0) }
dict[-1600596305] = { return Api.help.PassportConfig.parse_passportConfig($0) } dict[-1600596305] = { return Api.help.PassportConfig.parse_passportConfig($0) }
dict[-1078332329] = { return Api.help.PassportConfig.parse_passportConfigNotModified($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[-1942390465] = { return Api.help.PromoData.parse_promoData($0) }
dict[-1728664459] = { return Api.help.PromoData.parse_promoDataEmpty($0) } dict[-1728664459] = { return Api.help.PromoData.parse_promoDataEmpty($0) }
dict[235081943] = { return Api.help.RecentMeUrls.parse_recentMeUrls($0) } dict[235081943] = { return Api.help.RecentMeUrls.parse_recentMeUrls($0) }

View File

@ -212,13 +212,13 @@ public extension Api.help {
} }
public extension Api.help { public extension Api.help {
enum PremiumPromo: TypeConstructorDescription { 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) { public func serialize(_ buffer: Buffer, _ boxed: Swift.Bool) {
switch self { 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 { if boxed {
buffer.appendInt32(-533328101) buffer.appendInt32(-1974518743)
} }
serializeString(statusText, buffer: buffer, boxed: false) serializeString(statusText, buffer: buffer, boxed: false)
buffer.appendInt32(481674261) buffer.appendInt32(481674261)
@ -238,14 +238,19 @@ public extension Api.help {
} }
serializeString(currency, buffer: buffer, boxed: false) serializeString(currency, buffer: buffer, boxed: false)
serializeInt64(monthlyAmount, 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 break
} }
} }
public func descriptionFields() -> (String, [(String, Any)]) { public func descriptionFields() -> (String, [(String, Any)]) {
switch self { 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):
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))]) 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) _5 = parseString(reader)
var _6: Int64? var _6: Int64?
_6 = reader.readInt64() _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 _c1 = _1 != nil
let _c2 = _2 != nil let _c2 = _2 != nil
let _c3 = _3 != nil let _c3 = _3 != nil
let _c4 = _4 != nil let _c4 = _4 != nil
let _c5 = _5 != nil let _c5 = _5 != nil
let _c6 = _6 != nil let _c6 = _6 != nil
if _c1 && _c2 && _c3 && _c4 && _c5 && _c6 { let _c7 = _7 != nil
return Api.help.PremiumPromo.premiumPromo(statusText: _1!, statusEntities: _2!, videoSections: _3!, videos: _4!, currency: _5!, monthlyAmount: _6!) 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 { else {
return nil return nil

View File

@ -6227,13 +6227,12 @@ public extension Api.functions.messages {
} }
} }
public extension Api.functions.payments { 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() let buffer = Buffer()
buffer.appendInt32(267129798) buffer.appendInt32(224186320)
serializeInt32(flags, buffer: buffer, boxed: false) serializeInt32(flags, buffer: buffer, boxed: false)
serializeString(transactionId, buffer: buffer, boxed: false)
serializeBytes(receipt, 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) let reader = BufferReader(buffer)
var result: Api.Updates? var result: Api.Updates?
if let signature = reader.readInt32() { if let signature = reader.readInt32() {

View File

@ -1369,6 +1369,8 @@ public func standaloneStateManager(
Logger.shared.log("StandaloneStateManager", "Received settings") Logger.shared.log("StandaloneStateManager", "Received settings")
return postbox.transaction { transaction -> (PostboxCoding?, LocalizationSettings?, ProxySettings?, NetworkSettings?) in return postbox.transaction { transaction -> (PostboxCoding?, LocalizationSettings?, ProxySettings?, NetworkSettings?) in
Logger.shared.log("StandaloneStateManager", "Getting state")
let state = transaction.getState() let state = transaction.getState()
return (state, localizationSettings, proxySettings, transaction.getPreferencesEntry(key: PreferencesKeys.networkSettings)?.get(NetworkSettings.self)) return (state, localizationSettings, proxySettings, transaction.getPreferencesEntry(key: PreferencesKeys.networkSettings)?.get(NetworkSettings.self))

View File

@ -19,6 +19,13 @@ func updatePremiumPromoConfigurationOnce(postbox: Postbox, network: Network) ->
return .complete() return .complete()
} }
return postbox.transaction { transaction -> Void in 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 updatePremiumPromoConfiguration(transaction: transaction, { configuration -> PremiumPromoConfiguration in
return PremiumPromoConfiguration(apiPremiumPromo: result) return PremiumPromoConfiguration(apiPremiumPromo: result)
}) })
@ -54,7 +61,7 @@ private func updatePremiumPromoConfiguration(transaction: Transaction, _ f: (Pre
private extension PremiumPromoConfiguration { private extension PremiumPromoConfiguration {
init(apiPremiumPromo: Api.help.PremiumPromo) { init(apiPremiumPromo: Api.help.PremiumPromo) {
switch apiPremiumPromo { switch apiPremiumPromo {
case let .premiumPromo(statusText, statusEntities, videoSections, videoFiles, currency, monthlyAmount): case let .premiumPromo(statusText, statusEntities, videoSections, videoFiles, currency, monthlyAmount, _):
self.status = statusText self.status = statusText
self.statusEntities = messageTextEntitiesFromApiEntities(statusEntities) self.statusEntities = messageTextEntitiesFromApiEntities(statusEntities)
self.currency = currency self.currency = currency

View File

@ -8,12 +8,12 @@ public enum AssignAppStoreTransactionError {
case generic 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 var flags: Int32 = 0
if restore { if restore {
flags |= (1 << 0) 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 |> mapError { _ -> AssignAppStoreTransactionError in
return .generic 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> { func _internal_canPurchasePremium(account: Account) -> Signal<Bool, NoError> {
return account.network.request(Api.functions.payments.canPurchasePremium()) return account.network.request(Api.functions.payments.canPurchasePremium())
|> map { result -> Bool in |> map { result -> Bool in

View File

@ -38,8 +38,8 @@ public extension TelegramEngine {
return _internal_clearBotPaymentInfo(network: self.account.network, info: info) return _internal_clearBotPaymentInfo(network: self.account.network, info: info)
} }
public func assignAppStoreTransaction(transactionId: String, receipt: Data, restore: Bool) -> Signal<Never, AssignAppStoreTransactionError> { public func sendAppStoreReceipt(receipt: Data, restore: Bool) -> Signal<Never, AssignAppStoreTransactionError> {
return _internal_assignAppStoreTransaction(account: self.account, transactionId: transactionId, receipt: receipt, restore: restore) return _internal_sendAppStoreReceipt(account: self.account, receipt: receipt, restore: restore)
} }
public func canPurchasePremium() -> Signal<Bool, NoError> { public func canPurchasePremium() -> Signal<Bool, NoError> {

View File

@ -72,7 +72,7 @@ public final class PermissionContentNode: ASDisplayNode {
self.iconNode.displaysAsynchronously = false self.iconNode.displaysAsynchronously = false
if case let .animation(animation) = icon { 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?.setup(source: AnimatedStickerNodeLocalFileSource(name: animation), width: 320, height: 320, playbackMode: .once, mode: .direct(cachePathPrefix: nil))
self.animationNode?.visibility = true self.animationNode?.visibility = true

View File

@ -473,7 +473,19 @@ private func extractAccountManagerState(records: AccountRecordsView<TelegramAcco
TempBox.initializeShared(basePath: rootPath, processType: "app", launchSpecificId: Int64.random(in: Int64.min ... Int64.max)) 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) let _ = try? FileManager.default.createDirectory(atPath: logsPath, withIntermediateDirectories: true, attributes: nil)
Logger.setSharedLogger(Logger(rootPath: rootPath, basePath: logsPath)) Logger.setSharedLogger(Logger(rootPath: rootPath, basePath: logsPath))

View File

@ -62,7 +62,7 @@ final class AttachmentFileEmptyStateItemNode: ItemListControllerEmptyStateItemNo
init(item: AttachmentFileEmptyStateItem) { init(item: AttachmentFileEmptyStateItem) {
self.item = item 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.setup(source: AnimatedStickerNodeLocalFileSource(name: "Files"), width: 320, height: 320, playbackMode: .loop, mode: .direct(cachePathPrefix: nil))
self.animationNode.visibility = true self.animationNode.visibility = true

View File

@ -77,6 +77,7 @@ import ChatSendMessageActionUI
import ChatTextLinkEditUI import ChatTextLinkEditUI
import WebUI import WebUI
import PremiumUI import PremiumUI
import ImageTransparency
#if DEBUG #if DEBUG
import os.signpost import os.signpost
@ -15253,6 +15254,18 @@ public final class ChatControllerImpl: TelegramBaseController, ChatController, G
} }
let images = imageItems as! [UIImage] 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.chatDisplayNode.updateDropInteraction(isActive: false)
strongSelf.displayPasteMenu(images) strongSelf.displayPasteMenu(images)
} }

View File

@ -243,10 +243,10 @@ final class ChatMediaInputMetaSectionItemNode: ListViewItemNode {
if let current = self.animatedStickerNode { if let current = self.animatedStickerNode {
animatedStickerNode = current animatedStickerNode = current
} else { } else {
animatedStickerNode = AnimatedStickerNode() animatedStickerNode = DefaultAnimatedStickerNodeImpl()
self.animatedStickerNode = animatedStickerNode self.animatedStickerNode = animatedStickerNode
self.scalingNode.addSubnode(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 animatedStickerNode.visibility = self.visibilityStatus && loopAnimatedStickers

View File

@ -275,7 +275,7 @@ final class ChatMediaInputStickerGridItemNode: GridItemNode {
if let dimensions = item.stickerItem.file.dimensions { if let dimensions = item.stickerItem.file.dimensions {
if item.stickerItem.file.isAnimatedSticker || item.stickerItem.file.isVideoSticker { if item.stickerItem.file.isAnimatedSticker || item.stickerItem.file.isVideoSticker {
if self.animationNode == nil { if self.animationNode == nil {
let animationNode = AnimatedStickerNode() let animationNode = DefaultAnimatedStickerNodeImpl()
animationNode.view.addGestureRecognizer(UITapGestureRecognizer(target: self, action: #selector(self.imageNodeTap(_:)))) animationNode.view.addGestureRecognizer(UITapGestureRecognizer(target: self, action: #selector(self.imageNodeTap(_:))))
self.animationNode = animationNode self.animationNode = animationNode
animationNode.started = { [weak self] in 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 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 fitSize = item.large ? CGSize(width: 384.0, height: 384.0) : CGSize(width: 160.0, height: 160.0)
let fittedDimensions = dimensions.cgSize.aspectFitted(fitSize) 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)
} }
} }
} }

View File

@ -233,7 +233,7 @@ final class ChatMediaInputStickerPackItemNode: ListViewItemNode {
if let current = self.animatedStickerNode { if let current = self.animatedStickerNode {
animatedStickerNode = current animatedStickerNode = current
} else { } else {
animatedStickerNode = AnimatedStickerNode() animatedStickerNode = DefaultAnimatedStickerNodeImpl()
animatedStickerNode.started = { [weak self] in animatedStickerNode.started = { [weak self] in
self?.imageNode.isHidden = true self?.imageNode.isHidden = true
self?.removePlaceholder(animated: false) self?.removePlaceholder(animated: false)
@ -244,7 +244,7 @@ final class ChatMediaInputStickerPackItemNode: ListViewItemNode {
} else { } else {
self.scalingNode.addSubnode(animatedStickerNode) 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 animatedStickerNode.visibility = self.visibilityStatus && loopAnimatedStickers
} }
@ -367,7 +367,7 @@ final class ChatMediaInputStickerPackItemNode: ListViewItemNode {
case let .animated(resource, dimensions, isVideo): case let .animated(resource, dimensions, isVideo):
imageSize = dimensions.cgSize.aspectFitted(boundingImageSize) 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.setup(source: AnimatedStickerResourceSource(account: account, resource: resource, isVideo: isVideo), width: 128, height: 128, mode: .cached)
animatedStickerNode.visibility = self.visibilityStatus && loopAnimatedStickers animatedStickerNode.visibility = self.visibilityStatus && loopAnimatedStickers
scalingNode.addSubnode(animatedStickerNode) scalingNode.addSubnode(animatedStickerNode)

View File

@ -39,7 +39,7 @@ protocol GenericAnimatedStickerNode: ASDisplayNode {
func setFrameIndex(_ frameIndex: Int) func setFrameIndex(_ frameIndex: Int)
} }
extension AnimatedStickerNode: GenericAnimatedStickerNode { extension DefaultAnimatedStickerNodeImpl: GenericAnimatedStickerNode {
func setFrameIndex(_ frameIndex: Int) { func setFrameIndex(_ frameIndex: Int) {
self.stop() self.stop()
self.play(fromIndex: frameIndex) self.play(fromIndex: frameIndex)
@ -440,7 +440,7 @@ class ChatMessageAnimatedStickerItemNode: ChatMessageItemView {
self.animationNode = animationNode self.animationNode = animationNode
} }
} else { } else {
let animationNode = AnimatedStickerNode(useMetalCache: item.context.sharedContext.immediateExperimentalUISettings.acceleratedStickers) let animationNode = DefaultAnimatedStickerNodeImpl(useMetalCache: item.context.sharedContext.immediateExperimentalUISettings.acceleratedStickers)
animationNode.started = { [weak self] in animationNode.started = { [weak self] in
if let strongSelf = self { if let strongSelf = self {
strongSelf.imageNode.alpha = 0.0 strongSelf.imageNode.alpha = 0.0
@ -1684,7 +1684,7 @@ class ChatMessageAnimatedStickerItemNode: ChatMessageItemView {
} }
} else { } else {
let pathPrefix = item.context.account.postbox.mediaBox.shortLivedResourceCachePathPrefix(resource.id) 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)) 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 var animationFrame: CGRect
if isStickerEffect { if isStickerEffect {
@ -1941,7 +1941,7 @@ class ChatMessageAnimatedStickerItemNode: ChatMessageItemView {
switch status.status { switch status.status {
case .playing: case .playing:
animationNode?.play() animationNode?.play(firstFrame: false, fromIndex: nil)
strongSelf.mediaStatusDisposable.set(nil) strongSelf.mediaStatusDisposable.set(nil)
default: default:
break break
@ -1955,7 +1955,7 @@ class ChatMessageAnimatedStickerItemNode: ChatMessageItemView {
if let haptic = haptic, !haptic.active { if let haptic = haptic, !haptic.active {
haptic.start(time: 0.0) haptic.start(time: 0.0)
} }
animationNode?.play() animationNode?.play(firstFrame: false, fromIndex: nil)
} }
}) })
} }

View File

@ -2837,8 +2837,8 @@ class ChatMessageBubbleItemNode: ChatMessageItemView, ChatMessagePreviewItemNode
strongSelf.backgroundNode.updateLayout(size: backgroundFrame.size, transition: animation) strongSelf.backgroundNode.updateLayout(size: backgroundFrame.size, transition: animation)
animation.animator.updateFrame(layer: strongSelf.backgroundWallpaperNode.layer, frame: backgroundFrame, completion: nil) animation.animator.updateFrame(layer: strongSelf.backgroundWallpaperNode.layer, frame: backgroundFrame, completion: nil)
strongSelf.shadowNode.updateLayout(backgroundFrame: backgroundFrame, transition: animation.transition) strongSelf.shadowNode.updateLayout(backgroundFrame: backgroundFrame, animator: animation.animator)
strongSelf.backgroundWallpaperNode.updateFrame(backgroundFrame, transition: animation.transition) strongSelf.backgroundWallpaperNode.updateFrame(backgroundFrame, animator: animation.animator)
} }
if let _ = strongSelf.backgroundNode.type { if let _ = strongSelf.backgroundNode.type {

View File

@ -659,12 +659,12 @@ final class ChatMessageInteractiveFileNode: ASDisplayNode {
case let .success(text, isPending): case let .success(text, isPending):
textString = NSAttributedString(string: text, font: textFont, textColor: messageTheme.primaryTextColor) textString = NSAttributedString(string: text, font: textFont, textColor: messageTheme.primaryTextColor)
#if DEBUG /*#if DEBUG
var isPending = isPending var isPending = isPending
if "".isEmpty { if "".isEmpty {
isPending = true isPending = true
} }
#endif #endif*/
if isPending { if isPending {
let modifiedString = NSMutableAttributedString(attributedString: textString!) let modifiedString = NSMutableAttributedString(attributedString: textString!)
@ -1116,7 +1116,7 @@ final class ChatMessageInteractiveFileNode: ASDisplayNode {
let _ = waveformView.update( let _ = waveformView.update(
transition: waveformTransition.withUserData(ComponentHostViewSkipSettingFrame()), transition: waveformTransition.withUserData(ComponentHostViewSkipSettingFrame()),
component: AnyComponent(AudioWaveformComponent( component: AnyComponent(AudioWaveformComponent(
backgroundColor: waveformColor, backgroundColor: isTranscriptionInProgress ? messageTheme.mediaInactiveControlColor : waveformColor,
foregroundColor: messageTheme.mediaActiveControlColor, foregroundColor: messageTheme.mediaActiveControlColor,
shimmerColor: isTranscriptionInProgress ? messageTheme.mediaActiveControlColor : nil, shimmerColor: isTranscriptionInProgress ? messageTheme.mediaActiveControlColor : nil,
samples: audioWaveform?.samples ?? Data(), samples: audioWaveform?.samples ?? Data(),

View File

@ -981,7 +981,7 @@ final class ChatMessageInteractiveMediaNode: ASDisplayNode, GalleryItemTransitio
} }
if currentReplaceAnimatedStickerNode, let updatedAnimatedStickerFile = updateAnimatedStickerFile { if currentReplaceAnimatedStickerNode, let updatedAnimatedStickerFile = updateAnimatedStickerFile {
let animatedStickerNode = AnimatedStickerNode() let animatedStickerNode = DefaultAnimatedStickerNodeImpl()
animatedStickerNode.isUserInteractionEnabled = false animatedStickerNode.isUserInteractionEnabled = false
animatedStickerNode.started = { animatedStickerNode.started = {
guard let strongSelf = self else { guard let strongSelf = self else {

View File

@ -112,11 +112,11 @@ public final class ChatMessageTransitionNode: ASDisplayNode {
final class Sticker { final class Sticker {
let imageNode: TransformImageNode let imageNode: TransformImageNode
let animationNode: GenericAnimatedStickerNode? let animationNode: AnimatedStickerNode?
let placeholderNode: ASDisplayNode? let placeholderNode: ASDisplayNode?
let relativeSourceRect: CGRect 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.imageNode = imageNode
self.animationNode = animationNode self.animationNode = animationNode
self.placeholderNode = placeholderNode self.placeholderNode = placeholderNode

View File

@ -381,7 +381,7 @@ private final class ThemeSettingsThemeItemIconNode : ListViewItemNode {
Queue.mainQueue().after(0.1) { Queue.mainQueue().after(0.1) {
if !wasSelected { if !wasSelected {
animatedStickerNode.seekTo(.frameIndex(0)) animatedStickerNode.seekTo(.frameIndex(0))
animatedStickerNode.play() animatedStickerNode.play(firstFrame: false, fromIndex: nil)
let scale: CGFloat = 1.95 let scale: CGFloat = 1.95
animatedStickerNode.transform = CATransform3DMakeScale(scale, scale, 1.0) animatedStickerNode.transform = CATransform3DMakeScale(scale, scale, 1.0)
@ -498,7 +498,7 @@ private final class ThemeSettingsThemeItemIconNode : ListViewItemNode {
if let current = strongSelf.animatedStickerNode { if let current = strongSelf.animatedStickerNode {
animatedStickerNode = current animatedStickerNode = current
} else { } else {
animatedStickerNode = AnimatedStickerNode() animatedStickerNode = DefaultAnimatedStickerNodeImpl()
animatedStickerNode.started = { [weak self] in animatedStickerNode.started = { [weak self] in
self?.emojiImageNode.isHidden = true self?.emojiImageNode.isHidden = true
} }
@ -1499,7 +1499,7 @@ private class QrContentNode: ASDisplayNode, ContentNode {
self.codeStaticIconNode = codeStaticIconNode self.codeStaticIconNode = codeStaticIconNode
self.codeAnimatedIconNode = nil self.codeAnimatedIconNode = nil
} else { } else {
let codeAnimatedIconNode = AnimatedStickerNode() let codeAnimatedIconNode = DefaultAnimatedStickerNodeImpl()
codeAnimatedIconNode.setup(source: AnimatedStickerNodeLocalFileSource(name: "PlaneLogoPlain"), width: 120, height: 120, mode: .direct(cachePathPrefix: nil)) codeAnimatedIconNode.setup(source: AnimatedStickerNodeLocalFileSource(name: "PlaneLogoPlain"), width: 120, height: 120, mode: .direct(cachePathPrefix: nil))
codeAnimatedIconNode.visibility = true codeAnimatedIconNode.visibility = true
self.codeAnimatedIconNode = codeAnimatedIconNode self.codeAnimatedIconNode = codeAnimatedIconNode

View File

@ -362,7 +362,7 @@ private final class ThemeSettingsThemeItemIconNode : ListViewItemNode {
Queue.mainQueue().after(0.1) { Queue.mainQueue().after(0.1) {
if !wasSelected { if !wasSelected {
animatedStickerNode.seekTo(.frameIndex(0)) animatedStickerNode.seekTo(.frameIndex(0))
animatedStickerNode.play() animatedStickerNode.play(firstFrame: false, fromIndex: nil)
let scale: CGFloat = 2.6 let scale: CGFloat = 2.6
animatedStickerNode.transform = CATransform3DMakeScale(scale, scale, 1.0) animatedStickerNode.transform = CATransform3DMakeScale(scale, scale, 1.0)
@ -479,7 +479,7 @@ private final class ThemeSettingsThemeItemIconNode : ListViewItemNode {
if let current = strongSelf.animatedStickerNode { if let current = strongSelf.animatedStickerNode {
animatedStickerNode = current animatedStickerNode = current
} else { } else {
animatedStickerNode = AnimatedStickerNode() animatedStickerNode = DefaultAnimatedStickerNodeImpl()
animatedStickerNode.started = { [weak self] in animatedStickerNode.started = { [weak self] in
self?.emojiImageNode.isHidden = true self?.emojiImageNode.isHidden = true
} }

View File

@ -156,7 +156,9 @@ final class ChatTitleView: UIView, NavigationBarTitleView {
} }
if let notificationSettings = peerView.notificationSettings as? TelegramPeerNotificationSettings { if let notificationSettings = peerView.notificationSettings as? TelegramPeerNotificationSettings {
if case let .muted(until) = notificationSettings.muteState, until >= Int32(CFAbsoluteTimeGetCurrent() + NSTimeIntervalSince1970) { if case let .muted(until) = notificationSettings.muteState, until >= Int32(CFAbsoluteTimeGetCurrent() + NSTimeIntervalSince1970) {
titleRightIcon = .mute if titleCredibilityIcon != .verified {
titleRightIcon = .mute
}
} }
} }
} }

View File

@ -421,7 +421,7 @@ final class HorizontalListContextResultsChatInputPanelItemNode: ListViewItemNode
if let currentAnimationNode = strongSelf.animationNode { if let currentAnimationNode = strongSelf.animationNode {
animationNode = currentAnimationNode animationNode = currentAnimationNode
} else { } else {
animationNode = AnimatedStickerNode() animationNode = DefaultAnimatedStickerNodeImpl()
animationNode.transform = CATransform3DMakeRotation(CGFloat.pi / 2.0, 0.0, 0.0, 1.0) animationNode.transform = CATransform3DMakeRotation(CGFloat.pi / 2.0, 0.0, 0.0, 1.0)
animationNode.visibility = true animationNode.visibility = true
if let placeholderNode = strongSelf.placeholderNode { if let placeholderNode = strongSelf.placeholderNode {
@ -437,7 +437,7 @@ final class HorizontalListContextResultsChatInputPanelItemNode: ListViewItemNode
let dimensions = animatedStickerFile.dimensions ?? PixelDimensions(width: 512, height: 512) let dimensions = animatedStickerFile.dimensions ?? PixelDimensions(width: 512, height: 512)
let fittedDimensions = dimensions.cgSize.aspectFitted(CGSize(width: 160.0, height: 160.0)) 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()) 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)
} }
} }

View File

@ -142,7 +142,7 @@ final class HorizontalStickerGridItemNode: GridItemNode {
if let currentAnimationNode = self.animationNode { if let currentAnimationNode = self.animationNode {
animationNode = currentAnimationNode animationNode = currentAnimationNode
} else { } else {
animationNode = AnimatedStickerNode() animationNode = DefaultAnimatedStickerNodeImpl()
animationNode.transform = self.imageNode.transform animationNode.transform = self.imageNode.transform
animationNode.visibility = self.isVisibleInGrid animationNode.visibility = self.isVisibleInGrid
animationNode.view.addGestureRecognizer(UITapGestureRecognizer(target: self, action: #selector(self.imageNodeTap(_:)))) animationNode.view.addGestureRecognizer(UITapGestureRecognizer(target: self, action: #selector(self.imageNodeTap(_:))))
@ -178,7 +178,7 @@ final class HorizontalStickerGridItemNode: GridItemNode {
strongSelf.removePlaceholder(animated: false) 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()) self.stickerFetchedDisposable.set(freeMediaFileResourceInteractiveFetched(account: account, fileReference: stickerPackFileReference(item.file), resource: item.file.resource).start())
} else { } else {

View File

@ -63,7 +63,7 @@ private final class LargeEmojiActionSheetItemNode: ActionSheetItemNode {
if let fitz = fitz { if let fitz = fitz {
fitzModifier = EmojiFitzModifier(emoji: 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.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 self.animationNode.visibility = true

View File

@ -120,7 +120,7 @@ final class TrendingTopItemNode: ASDisplayNode {
if let currentAnimationNode = self.animationNode { if let currentAnimationNode = self.animationNode {
animationNode = currentAnimationNode animationNode = currentAnimationNode
} else { } else {
animationNode = AnimatedStickerNode() animationNode = DefaultAnimatedStickerNodeImpl()
animationNode.transform = self.imageNode.transform animationNode.transform = self.imageNode.transform
animationNode.visibility = self.visibility animationNode.visibility = self.visibility
self.animationNode = animationNode self.animationNode = animationNode
@ -141,7 +141,7 @@ final class TrendingTopItemNode: ASDisplayNode {
animationNode.started = { [weak self] in animationNode.started = { [weak self] in
self?.imageNode.alpha = 0.0 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()) self.loadDisposable.set(freeMediaFileResourceInteractiveFetched(account: account, fileReference: stickerPackFileReference(item.file), resource: item.file.resource).start())
} else { } else {
self.imageNode.setSignal(chatMessageSticker(account: account, file: item.file, small: true, synchronousLoad: synchronousLoads), attemptSynchronously: synchronousLoads) self.imageNode.setSignal(chatMessageSticker(account: account, file: item.file, small: true, synchronousLoad: synchronousLoads), attemptSynchronously: synchronousLoads)

View File

@ -87,7 +87,7 @@ public final class NotificationViewControllerImpl {
TempBox.initializeShared(basePath: rootPath, processType: "notification-content", launchSpecificId: Int64.random(in: Int64.min ... Int64.max)) 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) let _ = try? FileManager.default.createDirectory(atPath: logsPath, withIntermediateDirectories: true, attributes: nil)
setupSharedLogger(rootPath: rootPath, path: logsPath) setupSharedLogger(rootPath: rootPath, path: logsPath)
@ -294,7 +294,7 @@ public final class NotificationViewControllerImpl {
if let current = strongSelf.animatedStickerNode { if let current = strongSelf.animatedStickerNode {
animatedStickerNode = current animatedStickerNode = current
} else { } else {
animatedStickerNode = AnimatedStickerNode() animatedStickerNode = DefaultAnimatedStickerNodeImpl()
strongSelf.animatedStickerNode = animatedStickerNode strongSelf.animatedStickerNode = animatedStickerNode
animatedStickerNode.started = { animatedStickerNode.started = {
guard let strongSelf = self else { guard let strongSelf = self else {
@ -315,7 +315,7 @@ public final class NotificationViewControllerImpl {
} else { } else {
strongSelf.imageNode.setSignal(chatMessageAnimatedSticker(postbox: accountAndImage.0.postbox, file: fileReference.media, small: false, size: fittedDimensions)) 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 animatedStickerNode.visibility = true
accountAndImage.0.network.shouldExplicitelyKeepWorkerConnections.set(.single(true)) accountAndImage.0.network.shouldExplicitelyKeepWorkerConnections.set(.single(true))

View File

@ -2609,7 +2609,9 @@ final class PeerInfoHeaderNode: ASDisplayNode {
let _ = nextPanelSubtitleNodeLayout[TitleNodeStateRegular]!.size let _ = nextPanelSubtitleNodeLayout[TitleNodeStateRegular]!.size
let usernameSize = usernameNodeLayout[TitleNodeStateRegular]!.size let usernameSize = usernameNodeLayout[TitleNodeStateRegular]!.size
var titleHorizontalOffset: CGFloat = 0.0
if let image = self.titleCredibilityIconNode.image { 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.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)) 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) 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) usernameFrame = CGRect(origin: CGPoint(x: width - usernameSize.width - 16.0, y: minTitleFrame.midY - usernameSize.height / 2.0), size: usernameSize)
} else { } 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 let totalSubtitleWidth = subtitleSize.width + usernameSpacing + usernameSize.width
if usernameSize.width == 0.0 { if usernameSize.width == 0.0 {
subtitleFrame = CGRect(origin: CGPoint(x: floor((width - subtitleSize.width) / 2.0), y: titleFrame.maxY + 1.0), size: subtitleSize) subtitleFrame = CGRect(origin: CGPoint(x: floorToScreenPixels((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) usernameFrame = CGRect(origin: CGPoint(x: floorToScreenPixels((width - usernameSize.width) / 2.0), y: subtitleFrame.maxY + 1.0), size: usernameSize)
} else { } 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) 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 neutralTitleScale = 0.7
neutralSubtitleScale = 1.0 neutralSubtitleScale = 1.0
} }
let titleScale = (transitionFraction * transitionSourceTitleFrame.height + (1.0 - transitionFraction) * titleFrame.height * neutralTitleScale) / (titleFrame.height) 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))) 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 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) 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 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 self.titleNodeRawContainer.frame = rawTitleFrame
transition.updateFrame(node: self.titleNode, frame: CGRect(origin: CGPoint(), size: CGSize())) transition.updateFrame(node: self.titleNode, frame: CGRect(origin: CGPoint(), size: CGSize()))
let rawSubtitleFrame = subtitleFrame let rawSubtitleFrame = subtitleFrame

View File

@ -175,7 +175,7 @@ public class ShareRootControllerImpl {
TempBox.initializeShared(basePath: rootPath, processType: "share", launchSpecificId: Int64.random(in: Int64.min ... Int64.max)) 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) let _ = try? FileManager.default.createDirectory(atPath: logsPath, withIntermediateDirectories: true, attributes: nil)
setupSharedLogger(rootPath: rootPath, path: logsPath) setupSharedLogger(rootPath: rootPath, path: logsPath)

View File

@ -158,14 +158,14 @@ final class StickerPaneSearchStickerItemNode: GridItemNode {
if let dimensions = stickerItem.file.dimensions { if let dimensions = stickerItem.file.dimensions {
if stickerItem.file.isAnimatedSticker || stickerItem.file.isVideoSticker { if stickerItem.file.isAnimatedSticker || stickerItem.file.isVideoSticker {
if self.animationNode == nil { if self.animationNode == nil {
let animationNode = AnimatedStickerNode() let animationNode = DefaultAnimatedStickerNodeImpl()
animationNode.view.addGestureRecognizer(UITapGestureRecognizer(target: self, action: #selector(self.imageNodeTap(_:)))) animationNode.view.addGestureRecognizer(UITapGestureRecognizer(target: self, action: #selector(self.imageNodeTap(_:))))
self.animationNode = animationNode self.animationNode = animationNode
self.insertSubnode(animationNode, belowSubnode: self.textNode) self.insertSubnode(animationNode, belowSubnode: self.textNode)
} }
let dimensions = stickerItem.file.dimensions ?? PixelDimensions(width: 512, height: 512) let dimensions = stickerItem.file.dimensions ?? PixelDimensions(width: 512, height: 512)
let fittedDimensions = dimensions.cgSize.aspectFitted(CGSize(width: 160.0, height: 160.0)) 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.animationNode?.visibility = self.isVisibleInGrid
self.stickerFetchedDisposable.set(freeMediaFileResourceInteractiveFetched(account: account, fileReference: stickerPackFileReference(stickerItem.file), resource: stickerItem.file.resource).start()) self.stickerFetchedDisposable.set(freeMediaFileResourceInteractiveFetched(account: account, fileReference: stickerPackFileReference(stickerItem.file), resource: stickerItem.file.resource).start())
} else { } else {

View File

@ -294,7 +294,7 @@ private final class FeaturedPackItemNode: ListViewItemNode {
if let current = self.animatedStickerNode { if let current = self.animatedStickerNode {
animatedStickerNode = current animatedStickerNode = current
} else { } else {
animatedStickerNode = AnimatedStickerNode() animatedStickerNode = DefaultAnimatedStickerNodeImpl()
animatedStickerNode.started = { [weak self] in animatedStickerNode.started = { [weak self] in
self?.imageNode.isHidden = true self?.imageNode.isHidden = true
self?.removePlaceholder(animated: false) self?.removePlaceholder(animated: false)
@ -305,7 +305,7 @@ private final class FeaturedPackItemNode: ListViewItemNode {
} else { } else {
self.containerNode.addSubnode(animatedStickerNode) 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 animatedStickerNode.visibility = self.visibilityStatus && loopAnimatedStickers
} }

View File

@ -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.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 { switch icon {
case .none: case .none:
break break

View File

@ -319,7 +319,7 @@ final class UndoOverlayControllerNode: ViewControllerTracingNode {
self.iconNode = nil self.iconNode = nil
self.iconCheckNode = nil self.iconCheckNode = nil
self.animationNode = nil self.animationNode = nil
self.animatedStickerNode = AnimatedStickerNode() self.animatedStickerNode = DefaultAnimatedStickerNodeImpl()
self.animatedStickerNode?.visibility = true self.animatedStickerNode?.visibility = true
self.animatedStickerNode?.setup(source: AnimatedStickerNodeLocalFileSource(name: name), width: 100, height: 100, playbackMode: .once, mode: .direct(cachePathPrefix: nil)) 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: case .still:
break break
case let .animated(resource, _, isVideo): case let .animated(resource, _, isVideo):
let animatedStickerNode = AnimatedStickerNode() let animatedStickerNode = DefaultAnimatedStickerNodeImpl()
self.animatedStickerNode = animatedStickerNode self.animatedStickerNode = animatedStickerNode
animatedStickerNode.setup(source: AnimatedStickerResourceSource(account: context.account, resource: resource._asResource(), isVideo: isVideo), width: 80, height: 80, mode: .direct(cachePathPrefix: nil)) 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)) slotMachineNode.setState(.value(value, true))
} }
} else { } else {
let animatedStickerNode = AnimatedStickerNode() let animatedStickerNode = DefaultAnimatedStickerNodeImpl()
self.animatedStickerNode = animatedStickerNode self.animatedStickerNode = animatedStickerNode
let _ = (context.engine.stickers.loadedStickerPack(reference: .dice(dice.emoji), forceActualized: false) let _ = (context.engine.stickers.loadedStickerPack(reference: .dice(dice.emoji), forceActualized: false)
@ -723,7 +723,7 @@ final class UndoOverlayControllerNode: ViewControllerTracingNode {
case .still: case .still:
break break
case let .animated(resource): case let .animated(resource):
let animatedStickerNode = AnimatedStickerNode() let animatedStickerNode = DefaultAnimatedStickerNodeImpl()
self.animatedStickerNode = animatedStickerNode self.animatedStickerNode = animatedStickerNode
animatedStickerNode.setup(source: AnimatedStickerResourceSource(account: context.account, resource: resource._asResource(), isVideo: file.isVideoSticker), width: 80, height: 80, mode: .cached) 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.iconCheckNode = nil
self.animationNode = nil self.animationNode = nil
let animatedStickerNode = AnimatedStickerNode() let animatedStickerNode = DefaultAnimatedStickerNodeImpl()
self.animatedStickerNode = animatedStickerNode self.animatedStickerNode = animatedStickerNode
animatedStickerNode.setup(source: AnimatedStickerNodeLocalFileSource(name: "anim_savemedia"), width: 80, height: 80, playbackMode: .once, mode: .direct(cachePathPrefix: nil)) animatedStickerNode.setup(source: AnimatedStickerNodeLocalFileSource(name: "anim_savemedia"), width: 80, height: 80, playbackMode: .once, mode: .direct(cachePathPrefix: nil))

View File

@ -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: ContainedViewLayoutTransition)
func update(rect: CGRect, within containerSize: CGSize, transition: CombinedTransition) 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 offset(value: CGPoint, animationCurve: ContainedViewLayoutTransitionCurve, duration: Double)
func offsetSpring(value: CGFloat, duration: Double, damping: CGFloat) 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) { func update(rect: CGRect, within containerSize: CGSize, transition: CombinedTransition) {
self.currentLayout = (rect, containerSize) self.currentLayout = (rect, containerSize)
@ -467,7 +485,7 @@ final class WallpaperBackgroundNodeImpl: ASDisplayNode, WallpaperBackgroundNode
("ptrnSLON_0906_1033", CGPoint(x: 906 - 256, y: 1033 - 256)) ("ptrnSLON_0906_1033", CGPoint(x: 906 - 256, y: 1033 - 256))
] ]
for (animation, relativePosition) in animationList { for (animation, relativePosition) in animationList {
let animationNode = AnimatedStickerNode() let animationNode = DefaultAnimatedStickerNodeImpl()
animationNode.automaticallyLoadFirstFrame = true animationNode.automaticallyLoadFirstFrame = true
animationNode.autoplay = true animationNode.autoplay = true
//self.inlineAnimationNodes.append((animationNode, relativePosition)) //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) { func update(rect: CGRect, within containerSize: CGSize, transition: CombinedTransition) {
self.currentLayout = (rect, containerSize) self.currentLayout = (rect, containerSize)

View File

@ -4,7 +4,9 @@
// enable threading // enable threading
//#define LOTTIE_THREAD_SUPPORT //#define LOTTIE_THREAD_SUPPORT
#ifndef LOTTIE_THREAD_SAFE
#define LOTTIE_THREAD_SAFE #define LOTTIE_THREAD_SAFE
#endif
//enable logging //enable logging
//#define LOTTIE_LOGGING_SUPPORT //#define LOTTIE_LOGGING_SUPPORT
@ -16,6 +18,8 @@
//#define LOTTIE_CACHE_SUPPORT //#define LOTTIE_CACHE_SUPPORT
// disable image loader // disable image loader
#ifndef LOTTIE_IMAGE_MODULE_DISABLED
#define LOTTIE_IMAGE_MODULE_DISABLED #define LOTTIE_IMAGE_MODULE_DISABLED
#endif
#endif // CONFIG_H #endif // CONFIG_H