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 logsPath = rootPath + "/broadcast-logs"
let logsPath = rootPath + "/logs/broadcast-logs"
let _ = try? FileManager.default.createDirectory(atPath: logsPath, withIntermediateDirectories: true, attributes: nil)
let screencastBufferClientContext = IpcGroupCallBufferBroadcastContext(basePath: rootPath + "/broadcast-coordination")

View File

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

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

View File

@ -388,7 +388,7 @@
"Tour.Text5" = "**Telegram** lets you access your\nmessages from multiple devices.";
"Tour.Title6" = "Free";
"Tour.Text6" = "**Telegram** isprovides free unlimited cloud storage\nfor chats and media.";
"Tour.Text6" = "**Telegram** provides free unlimited cloud storage\nfor chats and media.";
"Tour.StartButton" = "Start Messaging";
@ -7550,12 +7550,15 @@ Sorry for the inconvenience.";
"Premium.AppIcons.Proceed" = "Unlock Premium Icons";
"Premium.NoAds.Proceed" = "About Telegram Premium";
"AccessDenied.LocationPreciseDenied" = "To share your specific location in this chat, please go to Settings > Privacy > Location Services > Telegram and set Precise Location to On.";
"Chat.MultipleTypingPair" = "%@ and %@";
"Chat.MultipleTypingMore" = "%@ and %@ others";
"Group.Username.RemoveExistingUsernamesOrExtendInfo" = "You have reserved too many public links. Try revoking a link from an older group or channel, or upgrade to **Telegram Premium** to double the limit to **%@** public links.";
"Group.Username.RemoveExistingUsernamesNoPremiumInfo" = "You have reserved too many public links. Try revoking the link from an older group or channel. We are working to let you increase this limit in the future.";
"Group.Username.RemoveExistingUsernamesFinalInfo" = "You have reserved too many public links. Try revoking the link from an older group or channel, or create a private one instead.";
"OldChannels.TooManyCommunitiesText" = "You are a member of **%@** groups and channels. Please leave some before joining a new one or upgrade to **Telegram Premium** to double the limit to **%@** groups and channels.";
@ -7623,11 +7626,13 @@ Sorry for the inconvenience.";
"Premium.NoAds" = "No Ads";
"Premium.NoAdsInfo" = "No more ads in public channels where Telegram sometimes shows ads.";
"Premium.NoAdsStandaloneInfo" = "Remove ads such as this by subscribing to **Telegram Premium**.";
"Premium.Reactions" = "Unique Reactions";
"Premium.ReactionsInfo" = "Additional animated reactions on messages, available only to the Premium subscribers.";
"Premium.ReactionsStandalone" = "Additional Reactions";
"Premium.ReactionsStandaloneInfo" = "Unlock a wider range of reactions by subscribing to **Telegram Premium**.";
"Premium.ReactionsStandaloneInfo" = "Unlock a wider range of reactions on messages by subscribing to **Telegram Premium**.";
"Premium.Stickers" = "Premium Stickers";
"Premium.StickersInfo" = "Exclusive enlarged stickers featuring additional effects, updated monthly.";

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

View File

@ -147,7 +147,41 @@ public protocol AnimatedStickerNodeSource {
func directDataPath(attemptSynchronously: Bool) -> Signal<String?, NoError>
}
public final class AnimatedStickerNode: ASDisplayNode {
public protocol AnimatedStickerNode: ASDisplayNode {
var automaticallyLoadFirstFrame: Bool { get set }
var automaticallyLoadLastFrame: Bool { get set }
var playToCompletionOnStop: Bool { get set }
var started: () -> Void { get set }
var completed: (Bool) -> Void { get set }
var frameUpdated: (Int, Int) -> Void { get set }
var currentFrameIndex: Int { get }
var currentFrameCount: Int { get }
var isPlaying: Bool { get }
var stopAtNearestLoop: Bool { get set }
var status: Signal<AnimatedStickerStatus, NoError> { get }
var autoplay: Bool { get set }
var visibility: Bool { get set }
var isPlayingChanged: (Bool) -> Void { get }
func cloneCurrentFrame(from otherNode: AnimatedStickerNode?)
func setup(source: AnimatedStickerNodeSource, width: Int, height: Int, playbackMode: AnimatedStickerPlaybackMode, mode: AnimatedStickerMode)
func reset()
func playOnce()
func play(firstFrame: Bool, fromIndex: Int?)
func pause()
func stop()
func seekTo(_ position: AnimatedStickerPlaybackPosition)
func playIfNeeded() -> Bool
func updateLayout(size: CGSize)
func setOverlayColor(_ color: UIColor?, replace: Bool, animated: Bool)
}
public final class DefaultAnimatedStickerNodeImpl: ASDisplayNode, AnimatedStickerNode {
private let queue: Queue
private let disposable = MetaDisposable()
private let fetchDisposable = MetaDisposable()
@ -245,14 +279,14 @@ public final class AnimatedStickerNode: ASDisplayNode {
return SoftwareAnimationRenderer()
})
private weak var nodeToCopyFrameFrom: AnimatedStickerNode?
private weak var nodeToCopyFrameFrom: DefaultAnimatedStickerNodeImpl?
override public func didLoad() {
super.didLoad()
if #available(iOS 10.0, *), (self.useMetalCache/* || "".isEmpty*/) {
self.renderer = AnimatedStickerNode.hardwareRendererPool.take()
self.renderer = DefaultAnimatedStickerNodeImpl.hardwareRendererPool.take()
} else {
self.renderer = AnimatedStickerNode.softwareRendererPool.take()
self.renderer = DefaultAnimatedStickerNodeImpl.softwareRendererPool.take()
if let contents = self.nodeToCopyFrameFrom?.renderer?.renderer.contents {
self.renderer?.renderer.contents = contents
}
@ -267,7 +301,12 @@ public final class AnimatedStickerNode: ASDisplayNode {
}
public func cloneCurrentFrame(from otherNode: AnimatedStickerNode?) {
if let renderer = self.renderer?.renderer as? SoftwareAnimationRenderer, let otherRenderer = otherNode?.renderer?.renderer as? SoftwareAnimationRenderer {
guard let otherNode = otherNode as? DefaultAnimatedStickerNodeImpl else {
self.nodeToCopyFrameFrom = nil
return
}
if let renderer = self.renderer?.renderer as? SoftwareAnimationRenderer, let otherRenderer = otherNode.renderer?.renderer as? SoftwareAnimationRenderer {
if let contents = otherRenderer.contents {
renderer.contents = contents
}

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

@ -335,8 +335,12 @@ public final class ChatMessageShadowNode: ASDisplayNode {
self.contentNode.image = shadowImage
}
public func updateLayout(backgroundFrame: CGRect, animator: ControlledTransitionAnimator) {
animator.updateFrame(layer: self.contentNode.layer, frame: CGRect(origin: CGPoint(x: backgroundFrame.minX - 10.0, y: backgroundFrame.minY - 10.0), size: CGSize(width: backgroundFrame.width + 20.0, height: backgroundFrame.height + 20.0)), completion: nil)
}
public func updateLayout(backgroundFrame: CGRect, transition: ContainedViewLayoutTransition) {
transition.updateFrame(node: self.contentNode, frame: CGRect(origin: CGPoint(x: backgroundFrame.minX - 10.0, y: backgroundFrame.minY - 10.0), size: CGSize(width: backgroundFrame.width + 20.0, height: backgroundFrame.height + 20.0)))
transition.updateFrame(layer: self.contentNode.layer, frame: CGRect(origin: CGPoint(x: backgroundFrame.minX - 10.0, y: backgroundFrame.minY - 10.0), size: CGSize(width: backgroundFrame.width + 20.0, height: backgroundFrame.height + 20.0)), completion: nil)
}
}
@ -552,7 +556,7 @@ public final class ChatMessageBubbleBackdrop: ASDisplayNode {
var backgroundFrame = backgroundContent.frame
backgroundFrame.origin.x += rect.minX
backgroundFrame.origin.y += rect.minY
backgroundContent.update(rect: backgroundFrame, within: containerSize, transition: .animated(duration: animator.duration, curve: .spring))
backgroundContent.update(rect: backgroundFrame, within: containerSize, animator: animator)
}
}
animator.updateFrame(layer: self.layer, frame: value, completion: { _ in

View File

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

View File

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

View File

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

View File

@ -61,6 +61,7 @@ private enum DebugControllerEntry: ItemListNodeEntry {
case sendGroupCallLogs
case sendNotificationLogs(PresentationTheme)
case sendCriticalLogs(PresentationTheme)
case sendAllLogs
case accounts(PresentationTheme)
case logToFile(PresentationTheme, Bool)
case logToConsole(PresentationTheme, Bool)
@ -94,7 +95,7 @@ private enum DebugControllerEntry: ItemListNodeEntry {
case preferredVideoCodec(Int, String, String?, Bool)
case disableVideoAspectScaling(Bool)
case enableVoipTcp(Bool)
case resetInAppPurchases(PresentationTheme)
case restorePurchases(PresentationTheme)
case hostInfo(PresentationTheme, String)
case versionInfo(PresentationTheme)
@ -102,7 +103,7 @@ private enum DebugControllerEntry: ItemListNodeEntry {
switch self {
case .testStickerImport:
return DebugControllerSection.sticker.rawValue
case .sendLogs, .sendOneLog, .sendShareLogs, .sendGroupCallLogs, .sendNotificationLogs, .sendCriticalLogs:
case .sendLogs, .sendOneLog, .sendShareLogs, .sendGroupCallLogs, .sendNotificationLogs, .sendCriticalLogs, .sendAllLogs:
return DebugControllerSection.logs.rawValue
case .accounts:
return DebugControllerSection.logs.rawValue
@ -110,7 +111,7 @@ private enum DebugControllerEntry: ItemListNodeEntry {
return DebugControllerSection.logging.rawValue
case .enableRaiseToSpeak, .keepChatNavigationStack, .skipReadHistory, .crashOnSlowQueries:
return DebugControllerSection.experiments.rawValue
case .clearTips, .crash, .resetData, .resetDatabase, .resetDatabaseAndCache, .resetHoles, .reindexUnread, .resetBiometricsData, .resetWebViewCache, .optimizeDatabase, .photoPreview, .knockoutWallpaper, .playerEmbedding, .playlistPlayback, .voiceConference, .experimentalCompatibility, .enableDebugDataDisplay, .acceleratedStickers, .experimentalBackground, .inlineStickers, .localTranscription, . enableReactionOverrides, .resetInAppPurchases:
case .clearTips, .crash, .resetData, .resetDatabase, .resetDatabaseAndCache, .resetHoles, .reindexUnread, .resetBiometricsData, .resetWebViewCache, .optimizeDatabase, .photoPreview, .knockoutWallpaper, .playerEmbedding, .playlistPlayback, .voiceConference, .experimentalCompatibility, .enableDebugDataDisplay, .acceleratedStickers, .experimentalBackground, .inlineStickers, .localTranscription, . enableReactionOverrides, .restorePurchases:
return DebugControllerSection.experiments.rawValue
case .preferredVideoCodec:
return DebugControllerSection.videoExperiments.rawValue
@ -137,70 +138,72 @@ private enum DebugControllerEntry: ItemListNodeEntry {
return 5
case .sendCriticalLogs:
return 6
case .accounts:
case .sendAllLogs:
return 7
case .logToFile:
case .accounts:
return 8
case .logToConsole:
case .logToFile:
return 9
case .redactSensitiveData:
case .logToConsole:
return 10
case .enableRaiseToSpeak:
case .redactSensitiveData:
return 11
case .keepChatNavigationStack:
case .enableRaiseToSpeak:
return 12
case .skipReadHistory:
case .keepChatNavigationStack:
return 13
case .crashOnSlowQueries:
case .skipReadHistory:
return 14
case .clearTips:
case .crashOnSlowQueries:
return 15
case .crash:
case .clearTips:
return 16
case .resetData:
case .crash:
return 17
case .resetDatabase:
case .resetData:
return 18
case .resetDatabaseAndCache:
case .resetDatabase:
return 19
case .resetHoles:
case .resetDatabaseAndCache:
return 20
case .reindexUnread:
case .resetHoles:
return 21
case .resetBiometricsData:
case .reindexUnread:
return 22
case .resetWebViewCache:
case .resetBiometricsData:
return 23
case .optimizeDatabase:
case .resetWebViewCache:
return 24
case .photoPreview:
case .optimizeDatabase:
return 25
case .knockoutWallpaper:
case .photoPreview:
return 26
case .experimentalCompatibility:
case .knockoutWallpaper:
return 27
case .enableDebugDataDisplay:
case .experimentalCompatibility:
return 28
case .acceleratedStickers:
case .enableDebugDataDisplay:
return 29
case .experimentalBackground:
case .acceleratedStickers:
return 30
case .inlineStickers:
case .experimentalBackground:
return 31
case .localTranscription:
case .inlineStickers:
return 32
case .enableReactionOverrides:
case .localTranscription:
return 33
case .resetInAppPurchases:
case .enableReactionOverrides:
return 34
case .playerEmbedding:
case .restorePurchases:
return 35
case .playlistPlayback:
case .playerEmbedding:
return 36
case .voiceConference:
case .playlistPlayback:
return 37
case .voiceConference:
return 38
case let .preferredVideoCodec(index, _, _, _):
return 38 + index
return 39 + index
case .disableVideoAspectScaling:
return 100
case .enableVoipTcp:
@ -402,7 +405,7 @@ private enum DebugControllerEntry: ItemListNodeEntry {
})
case .sendShareLogs:
return ItemListDisclosureItem(presentationData: presentationData, title: "Send Share Logs (Up to 40 MB)", label: "", sectionId: self.section, style: .blocks, action: {
let _ = (Logger.shared.collectLogs(prefix: "/share-logs")
let _ = (Logger.shared.collectLogs(prefix: "/logs/share-logs")
|> deliverOnMainQueue).start(next: { logs in
let presentationData = arguments.sharedContext.currentPresentationData.with { $0 }
let actionSheet = ActionSheetController(presentationData: presentationData)
@ -570,7 +573,7 @@ private enum DebugControllerEntry: ItemListNodeEntry {
})
case .sendNotificationLogs:
return ItemListDisclosureItem(presentationData: presentationData, title: "Send Notification Logs (Up to 40 MB)", label: "", sectionId: self.section, style: .blocks, action: {
let _ = (Logger(rootPath: arguments.sharedContext.basePath, basePath: arguments.sharedContext.basePath + "/notification-logs").collectLogs()
let _ = (Logger(rootPath: arguments.sharedContext.basePath, basePath: arguments.sharedContext.basePath + "/logs/notification-logs").collectLogs()
|> deliverOnMainQueue).start(next: { logs in
let presentationData = arguments.sharedContext.currentPresentationData.with { $0 }
let actionSheet = ActionSheetController(presentationData: presentationData)
@ -705,6 +708,101 @@ private enum DebugControllerEntry: ItemListNodeEntry {
arguments.presentController(actionSheet, nil)
})
})
case .sendAllLogs:
return ItemListDisclosureItem(presentationData: presentationData, title: "Send All Logs", label: "", sectionId: self.section, style: .blocks, action: {
let logTypes: [String] = [
"app-logs",
"broadcast-logs",
"siri-logs",
"widget-logs",
"notificationcontent-logs",
"notification-logs"
]
var logByType: [Signal<(type: String, logs: [(String, String)]), NoError>] = []
for type in logTypes {
logByType.append(Logger(rootPath: arguments.sharedContext.basePath, basePath: arguments.sharedContext.basePath + "/logs/\(type)").collectLogs()
|> map { result -> (type: String, logs: [(String, String)]) in
return (type, result)
})
}
let allLogs = combineLatest(logByType)
let _ = (allLogs
|> deliverOnMainQueue).start(next: { allLogs in
let presentationData = arguments.sharedContext.currentPresentationData.with { $0 }
let actionSheet = ActionSheetController(presentationData: presentationData)
var items: [ActionSheetButtonItem] = []
if let context = arguments.context, context.sharedContext.applicationBindings.isMainApp {
items.append(ActionSheetButtonItem(title: "Via Telegram", color: .accent, action: { [weak actionSheet] in
actionSheet?.dismissAnimated()
let controller = context.sharedContext.makePeerSelectionController(PeerSelectionControllerParams(context: context, filter: [.onlyWriteable, .excludeDisabled]))
controller.peerSelected = { [weak controller] peer in
let peerId = peer.id
if let strongController = controller {
strongController.dismiss()
let lineFeed = "\n".data(using: .utf8)!
var tempSources: [TempBoxFile] = []
for (type, logItems) in allLogs {
let tempSource = TempBox.shared.tempFile(fileName: "Log-\(type).txt")
var rawLogData: Data = Data()
for (name, path) in logItems {
if !rawLogData.isEmpty {
rawLogData.append(lineFeed)
rawLogData.append(lineFeed)
}
rawLogData.append("------ File: \(name) ------\n".data(using: .utf8)!)
if let data = try? Data(contentsOf: URL(fileURLWithPath: path)) {
rawLogData.append(data)
}
}
let _ = try? rawLogData.write(to: URL(fileURLWithPath: tempSource.path))
tempSources.append(tempSource)
}
let tempZip = TempBox.shared.tempFile(fileName: "destination.zip")
SSZipArchive.createZipFile(atPath: tempZip.path, withFilesAtPaths: tempSources.map(\.path))
guard let gzippedData = try? Data(contentsOf: URL(fileURLWithPath: tempZip.path)) else {
return
}
tempSources.forEach(TempBox.shared.dispose)
TempBox.shared.dispose(tempZip)
let id = Int64.random(in: Int64.min ... Int64.max)
let fileResource = LocalFileMediaResource(fileId: id, size: Int64(gzippedData.count), isSecretRelated: false)
context.account.postbox.mediaBox.storeResourceData(fileResource.id, data: gzippedData)
let file = TelegramMediaFile(fileId: MediaId(namespace: Namespaces.Media.LocalFile, id: id), partialReference: nil, resource: fileResource, previewRepresentations: [], videoThumbnails: [], immediateThumbnailData: nil, mimeType: "application/zip", size: Int64(gzippedData.count), attributes: [.FileName(fileName: "Log-iOS-All.txt.zip")])
let message: EnqueueMessage = .message(text: "", attributes: [], mediaReference: .standalone(media: file), replyToMessageId: nil, localGroupingKey: nil, correlationId: nil)
let _ = enqueueMessages(account: context.account, peerId: peerId, messages: [message]).start()
}
}
arguments.pushController(controller)
}))
}
actionSheet.setItemGroups([ActionSheetItemGroup(items: items), ActionSheetItemGroup(items: [
ActionSheetButtonItem(title: presentationData.strings.Common_Cancel, color: .accent, font: .bold, action: { [weak actionSheet] in
actionSheet?.dismissAnimated()
})
])])
arguments.presentController(actionSheet, nil)
})
})
case .accounts:
return ItemListDisclosureItem(presentationData: presentationData, title: "Accounts", label: "", sectionId: self.section, style: .blocks, action: {
guard let context = arguments.context else {
@ -1043,9 +1141,21 @@ private enum DebugControllerEntry: ItemListNodeEntry {
})
}).start()
})
case .resetInAppPurchases:
return ItemListActionItem(presentationData: presentationData, title: "Reset IAP Transactions", kind: .destructive, alignment: .natural, sectionId: self.section, style: .blocks, action: {
arguments.context?.inAppPurchaseManager?.finishAllTransactions()
case .restorePurchases:
return ItemListActionItem(presentationData: presentationData, title: "Restore Purchases", kind: .generic, alignment: .natural, sectionId: self.section, style: .blocks, action: {
arguments.context?.inAppPurchaseManager?.restorePurchases(completion: { state in
let text: String
switch state {
case .succeed:
text = "Done"
case .failed:
text = "Failed"
}
if let context = arguments.context {
let controller = textAlertController(context: context, title: nil, text: text, actions: [TextAlertAction(type: .genericAction, title: "OK", action: {})])
arguments.presentController(controller, nil)
}
})
})
case let .hostInfo(_, string):
return ItemListTextItem(presentationData: presentationData, text: .plain(string), sectionId: self.section)
@ -1071,6 +1181,7 @@ private func debugControllerEntries(sharedContext: SharedAccountContext, present
entries.append(.sendGroupCallLogs)
entries.append(.sendNotificationLogs(presentationData.theme))
entries.append(.sendCriticalLogs(presentationData.theme))
entries.append(.sendAllLogs)
if isMainApp {
entries.append(.accounts(presentationData.theme))
}
@ -1111,7 +1222,7 @@ private func debugControllerEntries(sharedContext: SharedAccountContext, present
if case .internal = sharedContext.applicationBindings.appBuildType {
entries.append(.enableReactionOverrides(experimentalSettings.enableReactionOverrides))
}
entries.append(.resetInAppPurchases(presentationData.theme))
entries.append(.restorePurchases(presentationData.theme))
entries.append(.playerEmbedding(experimentalSettings.playerEmbedding))
entries.append(.playlistPlayback(experimentalSettings.playlistPlayback))
}

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) {
let t = node.layer.transform
let currentScale = sqrt((t.m11 * t.m11) + (t.m12 * t.m12) + (t.m13 * t.m13))
@ -1512,6 +1537,7 @@ public protocol ControlledTransitionAnimator: AnyObject {
func updateBounds(layer: CALayer, bounds: CGRect, completion: ((Bool) -> Void)?)
func updateFrame(layer: CALayer, frame: CGRect, completion: ((Bool) -> Void)?)
func updateCornerRadius(layer: CALayer, cornerRadius: CGFloat, completion: ((Bool) -> Void)?)
func updateContentsRect(layer: CALayer, contentsRect: CGRect, completion: ((Bool) -> Void)?)
}
protocol AnyValueProviding {
@ -1908,6 +1934,21 @@ public final class ControlledTransition {
completion: completion
))
}
public func updateContentsRect(layer: CALayer, contentsRect: CGRect, completion: ((Bool) -> Void)?) {
if layer.contentsRect == contentsRect {
return
}
let fromValue = layer.presentation()?.contentsRect ?? layer.contentsRect
layer.contentsRect = contentsRect
self.add(animation: ControlledTransitionProperty(
layer: layer,
path: "contentsRect",
fromValue: fromValue,
toValue: contentsRect,
completion: completion
))
}
}
public final class LegacyAnimator: ControlledTransitionAnimator {
@ -1963,6 +2004,10 @@ public final class ControlledTransition {
public func updateCornerRadius(layer: CALayer, cornerRadius: CGFloat, completion: ((Bool) -> Void)?) {
self.transition.updateCornerRadius(layer: layer, cornerRadius: cornerRadius, completion: completion)
}
public func updateContentsRect(layer: CALayer, contentsRect: CGRect, completion: ((Bool) -> Void)?) {
self.transition.updateContentsRect(layer: layer, contentsRect: contentsRect, completion: completion)
}
}
public let animator: ControlledTransitionAnimator

View File

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

View File

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

View File

@ -75,7 +75,7 @@ private final class StickerPreviewPeekContentNode: ASDisplayNode, PeekController
case let .image(data):
self.imageNode.image = UIImage(data: data)
case .animation, .video:
let animationNode = AnimatedStickerNode()
let animationNode = DefaultAnimatedStickerNodeImpl()
self.animationNode = animationNode
let dimensions = PixelDimensions(width: 512, height: 512)
let fittedDimensions = dimensions.cgSize.aspectFitted(CGSize(width: 400.0, height: 400.0))
@ -84,7 +84,7 @@ private final class StickerPreviewPeekContentNode: ASDisplayNode, PeekController
if case .video = item.content {
isVideo = true
}
self.animationNode?.setup(source: AnimatedStickerResourceSource(account: account, resource: resource, isVideo: isVideo), width: Int(fittedDimensions.width), height: Int(fittedDimensions.height), mode: .direct(cachePathPrefix: nil))
self.animationNode?.setup(source: AnimatedStickerResourceSource(account: account, resource: resource, isVideo: isVideo), width: Int(fittedDimensions.width), height: Int(fittedDimensions.height), playbackMode: .loop, mode: .direct(cachePathPrefix: nil))
}
self.animationNode?.visibility = true
}

View File

@ -32,6 +32,11 @@ public final class InAppPurchaseManager: NSObject {
case notAllowed
}
public enum RestoreState {
case succeed
case failed
}
private final class PaymentTransactionContext {
var state: SKPaymentTransactionState?
let subscriber: (TransactionState) -> Void
@ -59,6 +64,8 @@ public final class InAppPurchaseManager: NSObject {
private let stateQueue = Queue()
private var paymentContexts: [String: PaymentTransactionContext] = [:]
private var onRestoreCompletion: ((RestoreState) -> Void)?
private let disposableSet = DisposableDict<String>()
public init(engine: TelegramEngine, premiumProductId: String) {
@ -79,6 +86,7 @@ public final class InAppPurchaseManager: NSObject {
guard !self.premiumProductId.isEmpty else {
return
}
Logger.shared.log("InAppPurchaseManager", "Requesting products")
let productRequest = SKProductsRequest(productIdentifiers: Set([self.premiumProductId]))
productRequest.delegate = self
productRequest.start()
@ -93,7 +101,17 @@ public final class InAppPurchaseManager: NSObject {
return self.productsPromise.get()
}
public func restorePurchases(completion: @escaping (RestoreState) -> Void) {
Logger.shared.log("InAppPurchaseManager", "Restoring purchases")
self.onRestoreCompletion = completion
let paymentQueue = SKPaymentQueue.default()
paymentQueue.restoreCompletedTransactions()
}
public func finishAllTransactions() {
Logger.shared.log("InAppPurchaseManager", "Finishing all transactions")
let paymentQueue = SKPaymentQueue.default()
let transactions = paymentQueue.transactions
for transaction in transactions {
@ -102,6 +120,8 @@ public final class InAppPurchaseManager: NSObject {
}
public func buyProduct(_ product: Product, account: Account) -> Signal<PurchaseState, PurchaseError> {
Logger.shared.log("InAppPurchaseManager", "Buying product: \(product.skProduct.productIdentifier), price \(product.price)")
let payment = SKPayment(product: product.skProduct)
SKPaymentQueue.default().add(payment)
@ -162,7 +182,10 @@ extension InAppPurchaseManager: SKProductsRequestDelegate {
self.productRequest = nil
Queue.mainQueue().async {
self.productsPromise.set(.single(response.products.map { Product(skProduct: $0) }))
let products = response.products.map { Product(skProduct: $0) }
Logger.shared.log("InAppPurchaseManager", "Received products \(products.map({ $0.skProduct.productIdentifier }).joined(separator: ", "))")
self.productsPromise.set(.single(products))
}
}
}
@ -187,37 +210,34 @@ extension InAppPurchaseManager: SKPaymentTransactionObserver {
let transactionState: TransactionState?
switch transaction.transactionState {
case .purchased:
Logger.shared.log("InAppPurchaseManager", "Transaction \(transaction.transactionIdentifier ?? ""), original transaction \(transaction.original?.transactionIdentifier ?? "none") purchased")
let transactionIdentifier = transaction.transactionIdentifier
transactionState = .purchased(transactionId: transactionIdentifier)
if let transactionIdentifier = transactionIdentifier {
self.disposableSet.set(
self.engine.payments.assignAppStoreTransaction(transactionId: transactionIdentifier, receipt: getReceiptData() ?? Data(), restore: false).start(error: { _ in
self.engine.payments.sendAppStoreReceipt(receipt: getReceiptData() ?? Data(), restore: false).start(error: { _ in
Logger.shared.log("InAppPurchaseManager", "Transaction \(transaction.transactionIdentifier ?? "") failed to assign AppStore transaction")
queue.finishTransaction(transaction)
}, completed: {
Logger.shared.log("InAppPurchaseManager", "Transaction \(transaction.transactionIdentifier ?? "") successfully assigned AppStore transaction")
queue.finishTransaction(transaction)
}),
forKey: transactionIdentifier
)
}
case .restored:
Logger.shared.log("InAppPurchaseManager", "Transaction \(transaction.transactionIdentifier ?? ""), original transaction \(transaction.original?.transactionIdentifier ?? "") restroring")
let transactionIdentifier = transaction.transactionIdentifier
transactionState = .restored(transactionId: transactionIdentifier)
if let transactionIdentifier = transactionIdentifier {
self.disposableSet.set(
self.engine.payments.assignAppStoreTransaction(transactionId: transactionIdentifier, receipt: getReceiptData() ?? Data(), restore: true).start(error: { _ in
queue.finishTransaction(transaction)
}, completed: {
queue.finishTransaction(transaction)
}),
forKey: transactionIdentifier
)
}
case .failed:
Logger.shared.log("InAppPurchaseManager", "Transaction \(transaction.transactionIdentifier ?? "") failed \((transaction.error as? SKError)?.localizedDescription ?? "")")
transactionState = .failed(error: transaction.error as? SKError)
queue.finishTransaction(transaction)
case .purchasing:
Logger.shared.log("InAppPurchaseManager", "Transaction \(transaction.transactionIdentifier ?? "") purchasing")
transactionState = .purchasing
case .deferred:
Logger.shared.log("InAppPurchaseManager", "Transaction \(transaction.transactionIdentifier ?? "") deferred")
transactionState = .deferred
default:
transactionState = nil
@ -230,4 +250,29 @@ extension InAppPurchaseManager: SKPaymentTransactionObserver {
}
}
}
public func paymentQueueRestoreCompletedTransactionsFinished(_ queue: SKPaymentQueue) {
if let onRestoreCompletion = self.onRestoreCompletion {
Logger.shared.log("InAppPurchaseManager", "Transactions restoration finished")
onRestoreCompletion(.succeed)
self.onRestoreCompletion = nil
if let receiptData = getReceiptData() {
self.disposableSet.set(
self.engine.payments.sendAppStoreReceipt(receipt: receiptData, restore: true).start(completed: {
Logger.shared.log("InAppPurchaseManager", "Sent restored receipt")
}),
forKey: "restore"
)
}
}
}
public func paymentQueue(_ queue: SKPaymentQueue, restoreCompletedTransactionsFailedWithError error: Error) {
if let onRestoreCompletion = self.onRestoreCompletion {
Logger.shared.log("InAppPurchaseManager", "Transactions restoration failed with error \((error as? SKError)?.localizedDescription ?? "")")
onRestoreCompletion(.failed)
self.onRestoreCompletion = nil
}
}
}

View File

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

View File

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

View File

@ -761,14 +761,14 @@ class ItemListStickerPackItemNode: ItemListRevealOptionsItemNode {
if let current = strongSelf.animationNode {
animationNode = current
} else {
animationNode = AnimatedStickerNode()
animationNode = DefaultAnimatedStickerNodeImpl()
animationNode.started = { [weak self] in
self?.removePlaceholder(animated: false)
}
strongSelf.animationNode = animationNode
strongSelf.addSubnode(animationNode)
animationNode.setup(source: AnimatedStickerResourceSource(account: item.account, resource: resource, isVideo: isVideo), width: 80, height: 80, mode: .cached)
animationNode.setup(source: AnimatedStickerResourceSource(account: item.account, resource: resource, isVideo: isVideo), width: 80, height: 80, playbackMode: .loop, mode: .cached)
}
animationNode.visibility = strongSelf.visibility != .none && item.playAnimatedStickers
animationNode.isHidden = !item.playAnimatedStickers

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

View File

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

View File

@ -37,7 +37,7 @@ final class LocationPlaceholderNode: ASDisplayNode {
playbackMode = .loop
}
self.animationNode = AnimatedStickerNode()
self.animationNode = DefaultAnimatedStickerNodeImpl()
self.animationNode.setup(source: AnimatedStickerNodeLocalFileSource(name: name), width: 320, height: 320, playbackMode: playbackMode, mode: .direct(cachePathPrefix: nil))
self.animationNode.visibility = true

View File

@ -43,7 +43,7 @@ final class MediaPickerPlaceholderNode: ASDisplayNode {
playbackMode = .once
}
self.animationNode = AnimatedStickerNode()
self.animationNode = DefaultAnimatedStickerNodeImpl()
self.animationNode.setup(source: AnimatedStickerNodeLocalFileSource(name: name), width: 320, height: 320, playbackMode: playbackMode, mode: .direct(cachePathPrefix: nil))
self.animationNode.visibility = true

View File

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

View File

@ -155,7 +155,7 @@ private final class TwoFactorAuthSplashScreenNode: ViewControllerTracingNode {
self.presentationData = presentationData
self.mode = mode
self.animationNode = AnimatedStickerNode()
self.animationNode = DefaultAnimatedStickerNodeImpl()
let title: String
let texts: [NSAttributedString]
@ -178,7 +178,7 @@ private final class TwoFactorAuthSplashScreenNode: ViewControllerTracingNode {
texts = [NSAttributedString(string: self.presentationData.strings.TwoFactorSetup_Done_Text, font: textFont, textColor: textColor)]
buttonText = doneText
self.animationNode.setup(source: AnimatedStickerNodeLocalFileSource(name: "TwoFactorSetupDone"), width: 248, height: 248, mode: .direct(cachePathPrefix: nil))
self.animationNode.setup(source: AnimatedStickerNodeLocalFileSource(name: "TwoFactorSetupDone"), width: 248, height: 248, playbackMode: .loop, mode: .direct(cachePathPrefix: nil))
self.animationSize = CGSize(width: 124.0, height: 124.0)
self.animationNode.visibility = true
case let .recoveryDone(_, _, isPasswordSet):
@ -209,7 +209,7 @@ private final class TwoFactorAuthSplashScreenNode: ViewControllerTracingNode {
texts = [NSAttributedString(string: self.presentationData.strings.TwoFactorRemember_Done_Text, font: textFont, textColor: textColor)]
buttonText = self.presentationData.strings.TwoFactorRemember_Done_Action
self.animationNode.setup(source: AnimatedStickerNodeLocalFileSource(name: "TwoFactorSetupRememberSuccess"), width: 248, height: 248, mode: .direct(cachePathPrefix: nil))
self.animationNode.setup(source: AnimatedStickerNodeLocalFileSource(name: "TwoFactorSetupRememberSuccess"), width: 248, height: 248, playbackMode: .loop, mode: .direct(cachePathPrefix: nil))
self.animationSize = CGSize(width: 124.0, height: 124.0)
self.animationNode.visibility = true
}

View File

@ -730,7 +730,19 @@ private func channelVisibilityControllerEntries(presentationData: PresentationDa
if case .revokeNames = mode {
let count = Int32(publicChannelsToRevoke?.count ?? 0)
entries.append(.linksLimitInfo(presentationData.theme, presentationData.strings.Group_Username_RemoveExistingUsernamesOrExtendInfo("\(premiumLimits.maxPublicLinksCount)").string, count, limits.maxPublicLinksCount, premiumLimits.maxPublicLinksCount, isPremiumDisabled))
let text: String
if count >= premiumLimits.maxPublicLinksCount {
text = presentationData.strings.Group_Username_RemoveExistingUsernamesFinalInfo
} else {
if isPremiumDisabled {
text = presentationData.strings.Group_Username_RemoveExistingUsernamesNoPremiumInfo
} else {
text = presentationData.strings.Group_Username_RemoveExistingUsernamesOrExtendInfo("\(premiumLimits.maxPublicLinksCount)").string
}
}
entries.append(.linksLimitInfo(presentationData.theme, text, count, limits.maxPublicLinksCount, premiumLimits.maxPublicLinksCount, isPremiumDisabled))
if let publicChannelsToRevoke = publicChannelsToRevoke {
var index: Int32 = 0
@ -1419,7 +1431,8 @@ public func channelVisibilityController(context: AccountContext, updatedPresenta
var rightNavigationButton: ItemListNavigationButton?
if case .revokeNames = mode {
if !premiumConfiguration.isPremiumDisabled {
let count = Int32(publicChannelsToRevoke?.count ?? 0)
if !premiumConfiguration.isPremiumDisabled && count < premiumLimits.maxPublicLinksCount {
footerItem = IncreaseLimitFooterItem(theme: presentationData.theme, title: presentationData.strings.Premium_IncreaseLimit, colorful: true, action: {
let controller = PremiumIntroScreen(context: context, source: .publicLinks)
pushControllerImpl?(controller)

View File

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

View File

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

View File

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

View File

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

View File

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

View File

@ -281,9 +281,11 @@ class PremiumStarComponent: Component {
}
private func maybeAnimateIn() {
guard let scene = self.sceneView.scene, let node = scene.rootNode.childNode(withName: "star", recursively: false), let animateFrom = self.animateFrom, let containerView = self.containerView else {
guard let scene = self.sceneView.scene, let node = scene.rootNode.childNode(withName: "star", recursively: false), let animateFrom = self.animateFrom, var containerView = self.containerView else {
return
}
containerView = containerView.subviews[2].subviews[1]
if let animationColor = self.animationColor {
let newNode = node.clone()
@ -323,6 +325,9 @@ class PremiumStarComponent: Component {
self.sceneView.layer.animatePosition(from: sourcePosition, to: targetPosition, duration: 1.0, timingFunction: kCAMediaTimingFunctionSpring, completion: { _ in
self.addSubview(self.sceneView)
self.sceneView.center = initialPosition
})
Queue.mainQueue().after(0.4, {
animateFrom.alpha = 1.0
})

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

@ -84,7 +84,7 @@ class RecentSessionsHeaderItemNode: ListViewItemNode {
self.titleNode.contentMode = .left
self.titleNode.contentsScale = UIScreen.main.scale
self.animationNode = AnimatedStickerNode()
self.animationNode = DefaultAnimatedStickerNodeImpl()
self.buttonNode = SolidRoundedButtonNode(theme: SolidRoundedButtonTheme(backgroundColor: .black, foregroundColor: .white), fontSize: 16.0, height: 50.0, cornerRadius: 11.0)
@ -143,7 +143,7 @@ class RecentSessionsHeaderItemNode: ListViewItemNode {
strongSelf.animationNode.setup(source: AnimatedStickerNodeLocalFileSource(name: item.animationName), width: 256, height: 256, playbackMode: .still(.start), mode: .direct(cachePathPrefix: nil))
strongSelf.animationNode.visibility = true
Queue.mainQueue().after(0.3) {
strongSelf.animationNode.play()
strongSelf.animationNode.play(firstFrame: false, fromIndex: nil)
}
}
strongSelf.item = item

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

@ -223,7 +223,7 @@ final class StickerPackPreviewGridItemNode: GridItemNode {
}
if self.animationNode == nil {
let animationNode = AnimatedStickerNode()
let animationNode = DefaultAnimatedStickerNodeImpl()
self.animationNode = animationNode
self.insertSubnode(animationNode, aboveSubnode: self.imageNode)
animationNode.started = { [weak self] in
@ -243,7 +243,7 @@ final class StickerPackPreviewGridItemNode: GridItemNode {
}
}
let fittedDimensions = dimensions.cgSize.aspectFitted(CGSize(width: 160.0, height: 160.0))
self.animationNode?.setup(source: AnimatedStickerResourceSource(account: account, resource: stickerItem.file.resource, isVideo: stickerItem.file.isVideoSticker), width: Int(fittedDimensions.width), height: Int(fittedDimensions.height), mode: .cached)
self.animationNode?.setup(source: AnimatedStickerResourceSource(account: account, resource: stickerItem.file.resource, isVideo: stickerItem.file.isVideoSticker), width: Int(fittedDimensions.width), height: Int(fittedDimensions.height), playbackMode: .loop, mode: .cached)
self.animationNode?.visibility = visibility

View File

@ -116,7 +116,7 @@ public final class StickerPreviewPeekContentNode: ASDisplayNode, PeekControllerC
let isPremiumSticker = item.file.isPremiumSticker
if item.file.isAnimatedSticker || item.file.isVideoSticker {
let animationNode = AnimatedStickerNode()
let animationNode = DefaultAnimatedStickerNodeImpl()
self.animationNode = animationNode
let dimensions = item.file.dimensions ?? PixelDimensions(width: 512, height: 512)
@ -130,7 +130,7 @@ public final class StickerPreviewPeekContentNode: ASDisplayNode, PeekControllerC
self.effectDisposable.set(freeMediaFileResourceInteractiveFetched(account: account, fileReference: .standalone(media: item.file), resource: effect.resource).start())
let source = AnimatedStickerResourceSource(account: account, resource: effect.resource, fitzModifier: nil)
let additionalAnimationNode = AnimatedStickerNode()
let additionalAnimationNode = DefaultAnimatedStickerNodeImpl()
additionalAnimationNode.setup(source: source, width: Int(fittedDimensions.width * 2.0), height: Int(fittedDimensions.height * 2.0), playbackMode: .once, mode: .direct(cachePathPrefix: nil))
additionalAnimationNode.visibility = true
self.additionalAnimationNode = additionalAnimationNode
@ -153,8 +153,8 @@ public final class StickerPreviewPeekContentNode: ASDisplayNode, PeekControllerC
animationNode.completed = { [weak self] _ in
if let strongSelf = self, let animationNode = strongSelf.animationNode, let additionalAnimationNode = strongSelf.additionalAnimationNode {
Queue.mainQueue().async {
animationNode.play()
additionalAnimationNode.play()
animationNode.play(firstFrame: false, fromIndex: nil)
additionalAnimationNode.play(firstFrame: false, fromIndex: nil)
}
}
}

View File

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

View File

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

View File

@ -212,13 +212,13 @@ public extension Api.help {
}
public extension Api.help {
enum PremiumPromo: TypeConstructorDescription {
case premiumPromo(statusText: String, statusEntities: [Api.MessageEntity], videoSections: [String], videos: [Api.Document], currency: String, monthlyAmount: Int64)
case premiumPromo(statusText: String, statusEntities: [Api.MessageEntity], videoSections: [String], videos: [Api.Document], currency: String, monthlyAmount: Int64, users: [Api.User])
public func serialize(_ buffer: Buffer, _ boxed: Swift.Bool) {
switch self {
case .premiumPromo(let statusText, let statusEntities, let videoSections, let videos, let currency, let monthlyAmount):
case .premiumPromo(let statusText, let statusEntities, let videoSections, let videos, let currency, let monthlyAmount, let users):
if boxed {
buffer.appendInt32(-533328101)
buffer.appendInt32(-1974518743)
}
serializeString(statusText, buffer: buffer, boxed: false)
buffer.appendInt32(481674261)
@ -238,14 +238,19 @@ public extension Api.help {
}
serializeString(currency, buffer: buffer, boxed: false)
serializeInt64(monthlyAmount, buffer: buffer, boxed: false)
buffer.appendInt32(481674261)
buffer.appendInt32(Int32(users.count))
for item in users {
item.serialize(buffer, true)
}
break
}
}
public func descriptionFields() -> (String, [(String, Any)]) {
switch self {
case .premiumPromo(let statusText, let statusEntities, let videoSections, let videos, let currency, let monthlyAmount):
return ("premiumPromo", [("statusText", String(describing: statusText)), ("statusEntities", String(describing: statusEntities)), ("videoSections", String(describing: videoSections)), ("videos", String(describing: videos)), ("currency", String(describing: currency)), ("monthlyAmount", String(describing: monthlyAmount))])
case .premiumPromo(let statusText, let statusEntities, let videoSections, let videos, let currency, let monthlyAmount, let users):
return ("premiumPromo", [("statusText", String(describing: statusText)), ("statusEntities", String(describing: statusEntities)), ("videoSections", String(describing: videoSections)), ("videos", String(describing: videos)), ("currency", String(describing: currency)), ("monthlyAmount", String(describing: monthlyAmount)), ("users", String(describing: users))])
}
}
@ -268,14 +273,19 @@ public extension Api.help {
_5 = parseString(reader)
var _6: Int64?
_6 = reader.readInt64()
var _7: [Api.User]?
if let _ = reader.readInt32() {
_7 = Api.parseVector(reader, elementSignature: 0, elementType: Api.User.self)
}
let _c1 = _1 != nil
let _c2 = _2 != nil
let _c3 = _3 != nil
let _c4 = _4 != nil
let _c5 = _5 != nil
let _c6 = _6 != nil
if _c1 && _c2 && _c3 && _c4 && _c5 && _c6 {
return Api.help.PremiumPromo.premiumPromo(statusText: _1!, statusEntities: _2!, videoSections: _3!, videos: _4!, currency: _5!, monthlyAmount: _6!)
let _c7 = _7 != nil
if _c1 && _c2 && _c3 && _c4 && _c5 && _c6 && _c7 {
return Api.help.PremiumPromo.premiumPromo(statusText: _1!, statusEntities: _2!, videoSections: _3!, videos: _4!, currency: _5!, monthlyAmount: _6!, users: _7!)
}
else {
return nil

View File

@ -6227,13 +6227,12 @@ public extension Api.functions.messages {
}
}
public extension Api.functions.payments {
static func assignAppStoreTransaction(flags: Int32, transactionId: String, receipt: Buffer) -> (FunctionDescription, Buffer, DeserializeFunctionResponse<Api.Updates>) {
static func assignAppStoreTransaction(flags: Int32, receipt: Buffer) -> (FunctionDescription, Buffer, DeserializeFunctionResponse<Api.Updates>) {
let buffer = Buffer()
buffer.appendInt32(267129798)
buffer.appendInt32(224186320)
serializeInt32(flags, buffer: buffer, boxed: false)
serializeString(transactionId, buffer: buffer, boxed: false)
serializeBytes(receipt, buffer: buffer, boxed: false)
return (FunctionDescription(name: "payments.assignAppStoreTransaction", parameters: [("flags", String(describing: flags)), ("transactionId", String(describing: transactionId)), ("receipt", String(describing: receipt))]), buffer, DeserializeFunctionResponse { (buffer: Buffer) -> Api.Updates? in
return (FunctionDescription(name: "payments.assignAppStoreTransaction", parameters: [("flags", String(describing: flags)), ("receipt", String(describing: receipt))]), buffer, DeserializeFunctionResponse { (buffer: Buffer) -> Api.Updates? in
let reader = BufferReader(buffer)
var result: Api.Updates?
if let signature = reader.readInt32() {

View File

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

View File

@ -19,6 +19,13 @@ func updatePremiumPromoConfigurationOnce(postbox: Postbox, network: Network) ->
return .complete()
}
return postbox.transaction { transaction -> Void in
if case let .premiumPromo(_, _, _, _, _, _, apiUsers) = result {
let users = apiUsers.map { TelegramUser(user: $0) }
updatePeers(transaction: transaction, peers: users, update: { _, updated -> Peer in
return updated
})
}
updatePremiumPromoConfiguration(transaction: transaction, { configuration -> PremiumPromoConfiguration in
return PremiumPromoConfiguration(apiPremiumPromo: result)
})
@ -54,7 +61,7 @@ private func updatePremiumPromoConfiguration(transaction: Transaction, _ f: (Pre
private extension PremiumPromoConfiguration {
init(apiPremiumPromo: Api.help.PremiumPromo) {
switch apiPremiumPromo {
case let .premiumPromo(statusText, statusEntities, videoSections, videoFiles, currency, monthlyAmount):
case let .premiumPromo(statusText, statusEntities, videoSections, videoFiles, currency, monthlyAmount, _):
self.status = statusText
self.statusEntities = messageTextEntitiesFromApiEntities(statusEntities)
self.currency = currency

View File

@ -8,12 +8,12 @@ public enum AssignAppStoreTransactionError {
case generic
}
func _internal_assignAppStoreTransaction(account: Account, transactionId: String, receipt: Data, restore: Bool) -> Signal<Never, AssignAppStoreTransactionError> {
func _internal_sendAppStoreReceipt(account: Account, receipt: Data, restore: Bool) -> Signal<Never, AssignAppStoreTransactionError> {
var flags: Int32 = 0
if restore {
flags |= (1 << 0)
}
return account.network.request(Api.functions.payments.assignAppStoreTransaction(flags: flags, transactionId: transactionId, receipt: Buffer(data: receipt)))
return account.network.request(Api.functions.payments.assignAppStoreTransaction(flags: flags, receipt: Buffer(data: receipt)))
|> mapError { _ -> AssignAppStoreTransactionError in
return .generic
}
@ -23,6 +23,10 @@ func _internal_assignAppStoreTransaction(account: Account, transactionId: String
}
}
public enum RestoreAppStoreReceiptError {
case generic
}
func _internal_canPurchasePremium(account: Account) -> Signal<Bool, NoError> {
return account.network.request(Api.functions.payments.canPurchasePremium())
|> map { result -> Bool in

View File

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

View File

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

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))
let logsPath = rootPath + "/logs"
let legacyLogs: [String] = [
"logs",
"broadcast-logs",
"siri-logs",
"widget-logs",
"notificationcontent-logs",
"notification-logs"
]
for item in legacyLogs {
let _ = try? FileManager.default.removeItem(atPath: "\(rootPath)/\(item)")
}
let logsPath = rootPath + "/logs/app-logs"
let _ = try? FileManager.default.createDirectory(atPath: logsPath, withIntermediateDirectories: true, attributes: nil)
Logger.setSharedLogger(Logger(rootPath: rootPath, basePath: logsPath))

View File

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

View File

@ -77,6 +77,7 @@ import ChatSendMessageActionUI
import ChatTextLinkEditUI
import WebUI
import PremiumUI
import ImageTransparency
#if DEBUG
import os.signpost
@ -15253,6 +15254,18 @@ public final class ChatControllerImpl: TelegramBaseController, ChatController, G
}
let images = imageItems as! [UIImage]
strongSelf.chatDisplayNode.updateDropInteraction(isActive: false)
if images.count == 1, let image = images.first, let cgImage = image.cgImage {
let maxSide = max(image.size.width, image.size.height)
if maxSide.isZero {
return
}
let aspectRatio = min(image.size.width, image.size.height) / maxSide
if (imageHasTransparency(cgImage) && aspectRatio > 0.2) {
strongSelf.enqueueStickerImage(image, isMemoji: false)
return
}
}
strongSelf.chatDisplayNode.updateDropInteraction(isActive: false)
strongSelf.displayPasteMenu(images)
}

View File

@ -243,10 +243,10 @@ final class ChatMediaInputMetaSectionItemNode: ListViewItemNode {
if let current = self.animatedStickerNode {
animatedStickerNode = current
} else {
animatedStickerNode = AnimatedStickerNode()
animatedStickerNode = DefaultAnimatedStickerNodeImpl()
self.animatedStickerNode = animatedStickerNode
self.scalingNode.addSubnode(animatedStickerNode)
animatedStickerNode.setup(source: AnimatedStickerResourceSource(account: account, resource: file.resource), width: 128, height: 128, mode: .cached)
animatedStickerNode.setup(source: AnimatedStickerResourceSource(account: account, resource: file.resource), width: 128, height: 128, playbackMode: .loop, mode: .cached)
}
animatedStickerNode.visibility = self.visibilityStatus && loopAnimatedStickers

View File

@ -275,7 +275,7 @@ final class ChatMediaInputStickerGridItemNode: GridItemNode {
if let dimensions = item.stickerItem.file.dimensions {
if item.stickerItem.file.isAnimatedSticker || item.stickerItem.file.isVideoSticker {
if self.animationNode == nil {
let animationNode = AnimatedStickerNode()
let animationNode = DefaultAnimatedStickerNodeImpl()
animationNode.view.addGestureRecognizer(UITapGestureRecognizer(target: self, action: #selector(self.imageNodeTap(_:))))
self.animationNode = animationNode
animationNode.started = { [weak self] in
@ -445,7 +445,7 @@ final class ChatMediaInputStickerGridItemNode: GridItemNode {
let dimensions = item.stickerItem.file.dimensions ?? PixelDimensions(width: 512, height: 512)
let fitSize = item.large ? CGSize(width: 384.0, height: 384.0) : CGSize(width: 160.0, height: 160.0)
let fittedDimensions = dimensions.cgSize.aspectFitted(fitSize)
animationNode.setup(source: AnimatedStickerResourceSource(account: item.account, resource: item.stickerItem.file.resource, isVideo: item.stickerItem.file.isVideoSticker), width: Int(fittedDimensions.width), height: Int(fittedDimensions.height), mode: .cached)
animationNode.setup(source: AnimatedStickerResourceSource(account: item.account, resource: item.stickerItem.file.resource, isVideo: item.stickerItem.file.isVideoSticker), width: Int(fittedDimensions.width), height: Int(fittedDimensions.height), playbackMode: .loop, mode: .cached)
}
}
}

View File

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

View File

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

View File

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

View File

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

View File

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

View File

@ -112,11 +112,11 @@ public final class ChatMessageTransitionNode: ASDisplayNode {
final class Sticker {
let imageNode: TransformImageNode
let animationNode: GenericAnimatedStickerNode?
let animationNode: AnimatedStickerNode?
let placeholderNode: ASDisplayNode?
let relativeSourceRect: CGRect
init(imageNode: TransformImageNode, animationNode: GenericAnimatedStickerNode?, placeholderNode: ASDisplayNode?, relativeSourceRect: CGRect) {
init(imageNode: TransformImageNode, animationNode: AnimatedStickerNode?, placeholderNode: ASDisplayNode?, relativeSourceRect: CGRect) {
self.imageNode = imageNode
self.animationNode = animationNode
self.placeholderNode = placeholderNode

View File

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

View File

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

View File

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

View File

@ -421,7 +421,7 @@ final class HorizontalListContextResultsChatInputPanelItemNode: ListViewItemNode
if let currentAnimationNode = strongSelf.animationNode {
animationNode = currentAnimationNode
} else {
animationNode = AnimatedStickerNode()
animationNode = DefaultAnimatedStickerNodeImpl()
animationNode.transform = CATransform3DMakeRotation(CGFloat.pi / 2.0, 0.0, 0.0, 1.0)
animationNode.visibility = true
if let placeholderNode = strongSelf.placeholderNode {
@ -437,7 +437,7 @@ final class HorizontalListContextResultsChatInputPanelItemNode: ListViewItemNode
let dimensions = animatedStickerFile.dimensions ?? PixelDimensions(width: 512, height: 512)
let fittedDimensions = dimensions.cgSize.aspectFitted(CGSize(width: 160.0, height: 160.0))
strongSelf.fetchDisposable.set(freeMediaFileResourceInteractiveFetched(account: item.account, fileReference: stickerPackFileReference(animatedStickerFile), resource: animatedStickerFile.resource).start())
animationNode.setup(source: AnimatedStickerResourceSource(account: item.account, resource: animatedStickerFile.resource, isVideo: animatedStickerFile.isVideoSticker), width: Int(fittedDimensions.width), height: Int(fittedDimensions.height), mode: .cached)
animationNode.setup(source: AnimatedStickerResourceSource(account: item.account, resource: animatedStickerFile.resource, isVideo: animatedStickerFile.isVideoSticker), width: Int(fittedDimensions.width), height: Int(fittedDimensions.height), playbackMode: .loop, mode: .cached)
}
}

View File

@ -142,7 +142,7 @@ final class HorizontalStickerGridItemNode: GridItemNode {
if let currentAnimationNode = self.animationNode {
animationNode = currentAnimationNode
} else {
animationNode = AnimatedStickerNode()
animationNode = DefaultAnimatedStickerNodeImpl()
animationNode.transform = self.imageNode.transform
animationNode.visibility = self.isVisibleInGrid
animationNode.view.addGestureRecognizer(UITapGestureRecognizer(target: self, action: #selector(self.imageNodeTap(_:))))
@ -178,7 +178,7 @@ final class HorizontalStickerGridItemNode: GridItemNode {
strongSelf.removePlaceholder(animated: false)
}
}
animationNode.setup(source: AnimatedStickerResourceSource(account: account, resource: item.file.resource, isVideo: item.file.isVideoSticker), width: Int(fittedDimensions.width), height: Int(fittedDimensions.height), mode: .cached)
animationNode.setup(source: AnimatedStickerResourceSource(account: account, resource: item.file.resource, isVideo: item.file.isVideoSticker), width: Int(fittedDimensions.width), height: Int(fittedDimensions.height), playbackMode: .loop, mode: .cached)
self.stickerFetchedDisposable.set(freeMediaFileResourceInteractiveFetched(account: account, fileReference: stickerPackFileReference(item.file), resource: item.file.resource).start())
} else {

View File

@ -63,7 +63,7 @@ private final class LargeEmojiActionSheetItemNode: ActionSheetItemNode {
if let fitz = fitz {
fitzModifier = EmojiFitzModifier(emoji: fitz)
}
self.animationNode = AnimatedStickerNode()
self.animationNode = DefaultAnimatedStickerNodeImpl()
self.animationNode.setup(source: AnimatedStickerResourceSource(account: context.account, resource: file.resource, fitzModifier: fitzModifier), width: 192, height: 192, playbackMode: .once, mode: .direct(cachePathPrefix: nil))
self.animationNode.visibility = true

View File

@ -120,7 +120,7 @@ final class TrendingTopItemNode: ASDisplayNode {
if let currentAnimationNode = self.animationNode {
animationNode = currentAnimationNode
} else {
animationNode = AnimatedStickerNode()
animationNode = DefaultAnimatedStickerNodeImpl()
animationNode.transform = self.imageNode.transform
animationNode.visibility = self.visibility
self.animationNode = animationNode
@ -141,7 +141,7 @@ final class TrendingTopItemNode: ASDisplayNode {
animationNode.started = { [weak self] in
self?.imageNode.alpha = 0.0
}
animationNode.setup(source: AnimatedStickerResourceSource(account: account, resource: item.file.resource, isVideo: item.file.isVideoSticker), width: Int(fittedDimensions.width), height: Int(fittedDimensions.height), mode: .cached)
animationNode.setup(source: AnimatedStickerResourceSource(account: account, resource: item.file.resource, isVideo: item.file.isVideoSticker), width: Int(fittedDimensions.width), height: Int(fittedDimensions.height), playbackMode: .loop, mode: .cached)
self.loadDisposable.set(freeMediaFileResourceInteractiveFetched(account: account, fileReference: stickerPackFileReference(item.file), resource: item.file.resource).start())
} else {
self.imageNode.setSignal(chatMessageSticker(account: account, file: item.file, small: true, synchronousLoad: synchronousLoads), attemptSynchronously: synchronousLoads)

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))
let logsPath = rootPath + "/notificationcontent-logs"
let logsPath = rootPath + "/logs/notificationcontent-logs"
let _ = try? FileManager.default.createDirectory(atPath: logsPath, withIntermediateDirectories: true, attributes: nil)
setupSharedLogger(rootPath: rootPath, path: logsPath)
@ -294,7 +294,7 @@ public final class NotificationViewControllerImpl {
if let current = strongSelf.animatedStickerNode {
animatedStickerNode = current
} else {
animatedStickerNode = AnimatedStickerNode()
animatedStickerNode = DefaultAnimatedStickerNodeImpl()
strongSelf.animatedStickerNode = animatedStickerNode
animatedStickerNode.started = {
guard let strongSelf = self else {
@ -315,7 +315,7 @@ public final class NotificationViewControllerImpl {
} else {
strongSelf.imageNode.setSignal(chatMessageAnimatedSticker(postbox: accountAndImage.0.postbox, file: fileReference.media, small: false, size: fittedDimensions))
}
animatedStickerNode.setup(source: AnimatedStickerResourceSource(account: accountAndImage.0, resource: fileReference.media.resource, isVideo: file.isVideoSticker), width: Int(fittedDimensions.width), height: Int(fittedDimensions.height), mode: .direct(cachePathPrefix: nil))
animatedStickerNode.setup(source: AnimatedStickerResourceSource(account: accountAndImage.0, resource: fileReference.media.resource, isVideo: file.isVideoSticker), width: Int(fittedDimensions.width), height: Int(fittedDimensions.height), playbackMode: .loop, mode: .direct(cachePathPrefix: nil))
animatedStickerNode.visibility = true
accountAndImage.0.network.shouldExplicitelyKeepWorkerConnections.set(.single(true))

View File

@ -2609,7 +2609,9 @@ final class PeerInfoHeaderNode: ASDisplayNode {
let _ = nextPanelSubtitleNodeLayout[TitleNodeStateRegular]!.size
let usernameSize = usernameNodeLayout[TitleNodeStateRegular]!.size
var titleHorizontalOffset: CGFloat = 0.0
if let image = self.titleCredibilityIconNode.image {
titleHorizontalOffset = -(image.size.width + 4.0) / 2.0
transition.updateFrame(node: self.titleCredibilityIconNode, frame: CGRect(origin: CGPoint(x: titleSize.width + 4.0, y: floor((titleSize.height - image.size.height) / 2.0) + 1.0), size: image.size))
transition.updateFrame(node: self.titleExpandedCredibilityIconNode, frame: CGRect(origin: CGPoint(x: titleExpandedSize.width + 4.0, y: floor((titleExpandedSize.height - image.size.height) / 2.0) + 1.0), size: image.size))
}
@ -2629,14 +2631,14 @@ final class PeerInfoHeaderNode: ASDisplayNode {
subtitleFrame = CGRect(origin: CGPoint(x: 16.0, y: minTitleFrame.maxY + 2.0), size: subtitleSize)
usernameFrame = CGRect(origin: CGPoint(x: width - usernameSize.width - 16.0, y: minTitleFrame.midY - usernameSize.height / 2.0), size: usernameSize)
} else {
titleFrame = CGRect(origin: CGPoint(x: floor((width - titleSize.width) / 2.0), y: avatarFrame.maxY + 7.0 + (subtitleSize.height.isZero ? 11.0 : 0.0) + 11.0), size: titleSize)
titleFrame = CGRect(origin: CGPoint(x: floorToScreenPixels((width - titleSize.width) / 2.0), y: avatarFrame.maxY + 7.0 + (subtitleSize.height.isZero ? 11.0 : 0.0) + 11.0), size: titleSize)
let totalSubtitleWidth = subtitleSize.width + usernameSpacing + usernameSize.width
if usernameSize.width == 0.0 {
subtitleFrame = CGRect(origin: CGPoint(x: floor((width - subtitleSize.width) / 2.0), y: titleFrame.maxY + 1.0), size: subtitleSize)
usernameFrame = CGRect(origin: CGPoint(x: floor((width - usernameSize.width) / 2.0), y: subtitleFrame.maxY + 1.0), size: usernameSize)
subtitleFrame = CGRect(origin: CGPoint(x: floorToScreenPixels((width - subtitleSize.width) / 2.0), y: titleFrame.maxY + 1.0), size: subtitleSize)
usernameFrame = CGRect(origin: CGPoint(x: floorToScreenPixels((width - usernameSize.width) / 2.0), y: subtitleFrame.maxY + 1.0), size: usernameSize)
} else {
subtitleFrame = CGRect(origin: CGPoint(x: floor((width - totalSubtitleWidth) / 2.0), y: titleFrame.maxY + 1.0), size: subtitleSize)
subtitleFrame = CGRect(origin: CGPoint(x: floorToScreenPixels((width - totalSubtitleWidth) / 2.0), y: titleFrame.maxY + 1.0), size: subtitleSize)
usernameFrame = CGRect(origin: CGPoint(x: subtitleFrame.maxX + usernameSpacing, y: titleFrame.maxY + 1.0), size: usernameSize)
}
}
@ -2851,10 +2853,15 @@ final class PeerInfoHeaderNode: ASDisplayNode {
neutralTitleScale = 0.7
neutralSubtitleScale = 1.0
}
let titleScale = (transitionFraction * transitionSourceTitleFrame.height + (1.0 - transitionFraction) * titleFrame.height * neutralTitleScale) / (titleFrame.height)
let subtitleScale = max(0.01, min(10.0, (transitionFraction * transitionSourceSubtitleFrame.height + (1.0 - transitionFraction) * subtitleFrame.height * neutralSubtitleScale) / (subtitleFrame.height)))
var titleFrame = titleFrame
if !self.isAvatarExpanded {
titleFrame = titleFrame.offsetBy(dx: self.isAvatarExpanded ? 0.0 : titleHorizontalOffset * titleScale, dy: 0.0)
}
let titleCenter = CGPoint(x: transitionFraction * transitionSourceTitleFrame.midX + (1.0 - transitionFraction) * titleFrame.midX, y: transitionFraction * transitionSourceTitleFrame.midY + (1.0 - transitionFraction) * titleFrame.midY)
let subtitleCenter = CGPoint(x: transitionFraction * transitionSourceSubtitleFrame.midX + (1.0 - transitionFraction) * subtitleFrame.midX, y: transitionFraction * transitionSourceSubtitleFrame.midY + (1.0 - transitionFraction) * subtitleFrame.midY)
@ -2885,7 +2892,7 @@ final class PeerInfoHeaderNode: ASDisplayNode {
subtitleOffset = titleCollapseFraction * -2.0
}
let rawTitleFrame = titleFrame
let rawTitleFrame = titleFrame.offsetBy(dx: self.isAvatarExpanded ? 0.0 : titleHorizontalOffset * titleScale, dy: 0.0)
self.titleNodeRawContainer.frame = rawTitleFrame
transition.updateFrame(node: self.titleNode, frame: CGRect(origin: CGPoint(), size: CGSize()))
let rawSubtitleFrame = subtitleFrame

View File

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

View File

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

View File

@ -294,7 +294,7 @@ private final class FeaturedPackItemNode: ListViewItemNode {
if let current = self.animatedStickerNode {
animatedStickerNode = current
} else {
animatedStickerNode = AnimatedStickerNode()
animatedStickerNode = DefaultAnimatedStickerNodeImpl()
animatedStickerNode.started = { [weak self] in
self?.imageNode.isHidden = true
self?.removePlaceholder(animated: false)
@ -305,7 +305,7 @@ private final class FeaturedPackItemNode: ListViewItemNode {
} else {
self.containerNode.addSubnode(animatedStickerNode)
}
animatedStickerNode.setup(source: AnimatedStickerResourceSource(account: account, resource: resource, isVideo: isVideo), width: 128, height: 128, mode: .cached)
animatedStickerNode.setup(source: AnimatedStickerResourceSource(account: account, resource: resource, isVideo: isVideo), width: 128, height: 128, playbackMode: .loop, mode: .cached)
}
animatedStickerNode.visibility = self.visibilityStatus && loopAnimatedStickers
}

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.animatedStickerNode = AnimatedStickerNode()
self.animatedStickerNode = DefaultAnimatedStickerNodeImpl()
switch icon {
case .none:
break

View File

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

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: CombinedTransition)
func update(rect: CGRect, within containerSize: CGSize, animator: ControlledTransitionAnimator)
func offset(value: CGPoint, animationCurve: ContainedViewLayoutTransitionCurve, duration: Double)
func offsetSpring(value: CGFloat, duration: Double, damping: CGFloat)
}
@ -262,6 +263,23 @@ final class WallpaperBackgroundNodeImpl: ASDisplayNode, WallpaperBackgroundNode
}
}
}
func update(rect: CGRect, within containerSize: CGSize, animator: ControlledTransitionAnimator) {
self.currentLayout = (rect, containerSize)
let shiftedContentsRect = CGRect(origin: CGPoint(x: rect.minX / containerSize.width, y: rect.minY / containerSize.height), size: CGSize(width: rect.width / containerSize.width, height: rect.height / containerSize.height))
animator.updateFrame(layer: self.contentNode.layer, frame: self.bounds, completion: nil)
animator.updateContentsRect(layer: self.contentNode.layer, contentsRect: shiftedContentsRect, completion: nil)
if let cleanWallpaperNode = self.cleanWallpaperNode {
animator.updateFrame(layer: cleanWallpaperNode.layer, frame: self.bounds, completion: nil)
animator.updateContentsRect(layer: cleanWallpaperNode.layer, contentsRect: shiftedContentsRect, completion: nil)
}
if let gradientWallpaperNode = self.gradientWallpaperNode {
animator.updateFrame(layer: gradientWallpaperNode.layer, frame: self.bounds, completion: nil)
animator.updateContentsRect(layer: gradientWallpaperNode.layer, contentsRect: shiftedContentsRect, completion: nil)
}
}
func update(rect: CGRect, within containerSize: CGSize, transition: CombinedTransition) {
self.currentLayout = (rect, containerSize)
@ -467,7 +485,7 @@ final class WallpaperBackgroundNodeImpl: ASDisplayNode, WallpaperBackgroundNode
("ptrnSLON_0906_1033", CGPoint(x: 906 - 256, y: 1033 - 256))
]
for (animation, relativePosition) in animationList {
let animationNode = AnimatedStickerNode()
let animationNode = DefaultAnimatedStickerNodeImpl()
animationNode.automaticallyLoadFirstFrame = true
animationNode.autoplay = true
//self.inlineAnimationNodes.append((animationNode, relativePosition))
@ -1177,6 +1195,23 @@ final class WallpaperBackgroundNodeMergedImpl: ASDisplayNode, WallpaperBackgroun
}
}
}
func update(rect: CGRect, within containerSize: CGSize, animator: ControlledTransitionAnimator) {
self.currentLayout = (rect, containerSize)
let shiftedContentsRect = CGRect(origin: CGPoint(x: rect.minX / containerSize.width, y: rect.minY / containerSize.height), size: CGSize(width: rect.width / containerSize.width, height: rect.height / containerSize.height))
animator.updateFrame(layer: self.contentNode.layer, frame: self.bounds, completion: nil)
animator.updateContentsRect(layer: self.contentNode.layer, contentsRect: shiftedContentsRect, completion: nil)
if let cleanWallpaperNode = self.cleanWallpaperNode {
animator.updateFrame(layer: cleanWallpaperNode.layer, frame: self.bounds, completion: nil)
animator.updateContentsRect(layer: cleanWallpaperNode.layer, contentsRect: shiftedContentsRect, completion: nil)
}
if let gradientWallpaperNode = self.gradientWallpaperNode {
animator.updateFrame(layer: gradientWallpaperNode.layer, frame: self.bounds, completion: nil)
animator.updateContentsRect(layer: gradientWallpaperNode.layer, contentsRect: shiftedContentsRect, completion: nil)
}
}
func update(rect: CGRect, within containerSize: CGSize, transition: CombinedTransition) {
self.currentLayout = (rect, containerSize)

View File

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