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

This commit is contained in:
Ilya Laktyushin 2021-11-04 21:44:08 +04:00
commit bf4da379b9
60 changed files with 847 additions and 157 deletions

View file

@ -424,7 +424,7 @@ private func peerAvatar(mediaBox: MediaBox, accountPeerId: PeerId, peer: Peer) -
}
@available(iOSApplicationExtension 10.0, iOS 10.0, *)
private struct NotificationContent {
private struct NotificationContent: CustomStringConvertible {
var title: String?
var subtitle: String?
var body: String?
@ -438,6 +438,21 @@ private struct NotificationContent {
var senderPerson: INPerson?
var senderImage: INImage?
var description: String {
var string = "{"
string += " title: \(String(describing: self.title))\n"
string += " subtitle: \(String(describing: self.subtitle))\n"
string += " body: \(String(describing: self.body)),\n"
string += " threadId: \(String(describing: self.threadId)),\n"
string += " sound: \(String(describing: self.sound)),\n"
string += " badge: \(String(describing: self.badge)),\n"
string += " category: \(String(describing: self.category)),\n"
string += " userInfo: \(String(describing: self.userInfo)),\n"
string += " senderImage: \(self.senderImage != nil ? "non-empty" : "empty"),\n"
string += "}"
return string
}
mutating func addSenderInfo(mediaBox: MediaBox, accountPeerId: PeerId, peer: Peer) {
if #available(iOS 15.0, *) {
let image = peerAvatar(mediaBox: mediaBox, accountPeerId: accountPeerId, peer: peer)
@ -548,6 +563,8 @@ private final class NotificationServiceHandler {
init?(queue: Queue, updateCurrentContent: @escaping (NotificationContent) -> Void, completed: @escaping () -> Void, payload: [AnyHashable: Any]) {
self.queue = queue
let episode = String(UInt32.random(in: 0 ..< UInt32.max), radix: 16)
guard let appBundleIdentifier = Bundle.main.bundleIdentifier, let lastDotRange = appBundleIdentifier.range(of: ".", options: [.backwards]) else {
return nil
}
@ -586,7 +603,10 @@ private final class NotificationServiceHandler {
let networkArguments = NetworkInitializationArguments(apiId: apiId, apiHash: apiHash, languagesCategory: languagesCategory, appVersion: appVersion, voipMaxLayer: 0, voipVersions: [], appData: .single(buildConfig.bundleData(withAppToken: nil, signatureDict: nil)), autolockDeadine: .single(nil), encryptionProvider: OpenSSLEncryptionProvider(), resolvedDeviceName: nil)
Logger.shared.log("NotificationService \(episode)", "Begin processing payload \(payload)")
guard var encryptedPayload = payload["p"] as? String else {
Logger.shared.log("NotificationService \(episode)", "Invalid payload 1")
return nil
}
encryptedPayload = encryptedPayload.replacingOccurrences(of: "-", with: "+")
@ -595,6 +615,7 @@ private final class NotificationServiceHandler {
encryptedPayload.append("=")
}
guard let payloadData = Data(base64Encoded: encryptedPayload) else {
Logger.shared.log("NotificationService \(episode)", "Invalid payload 2")
return nil
}
@ -621,6 +642,8 @@ private final class NotificationServiceHandler {
}
guard let strongSelf = self, let recordId = recordId else {
Logger.shared.log("NotificationService \(episode)", "Couldn't find a matching decryption key")
let content = NotificationContent()
updateCurrentContent(content)
completed()
@ -641,6 +664,8 @@ private final class NotificationServiceHandler {
return
}
guard let stateManager = stateManager else {
Logger.shared.log("NotificationService \(episode)", "Didn't receive stateManager")
let content = NotificationContent()
updateCurrentContent(content)
completed()
@ -658,6 +683,8 @@ private final class NotificationServiceHandler {
return
}
guard let notificationsKey = notificationsKey else {
Logger.shared.log("NotificationService \(episode)", "Didn't receive decryption key")
let content = NotificationContent()
updateCurrentContent(content)
completed()
@ -665,6 +692,8 @@ private final class NotificationServiceHandler {
return
}
guard let decryptedPayload = decryptedNotificationPayload(key: notificationsKey, data: payloadData) else {
Logger.shared.log("NotificationService \(episode)", "Couldn't decrypt payload")
let content = NotificationContent()
updateCurrentContent(content)
completed()
@ -672,6 +701,8 @@ private final class NotificationServiceHandler {
return
}
guard let payloadJson = try? JSONSerialization.jsonObject(with: decryptedPayload, options: []) as? [String: Any] else {
Logger.shared.log("NotificationService \(episode)", "Couldn't process payload as JSON")
let content = NotificationContent()
updateCurrentContent(content)
completed()
@ -679,6 +710,8 @@ private final class NotificationServiceHandler {
return
}
Logger.shared.log("NotificationService \(episode)", "Decrypted payload: \(payloadJson)")
var peerId: PeerId?
var messageId: MessageId.Id?
var mediaAttachment: Media?
@ -842,8 +875,10 @@ private final class NotificationServiceHandler {
if let action = action {
switch action {
case .logout:
Logger.shared.log("NotificationService \(episode)", "Will logout")
completed()
case let .poll(peerId, initialContent):
Logger.shared.log("NotificationService \(episode)", "Will poll")
if let stateManager = strongSelf.stateManager {
let pollCompletion: (NotificationContent) -> Void = { content in
var content = content
@ -912,6 +947,7 @@ private final class NotificationServiceHandler {
}
}
Logger.shared.log("NotificationService \(episode)", "Will fetch media")
let _ = (fetchMediaSignal
|> timeout(10.0, queue: queue, alternate: .single(nil))
|> deliverOn(queue)).start(next: { mediaData in
@ -920,6 +956,9 @@ private final class NotificationServiceHandler {
return
}
Logger.shared.log("NotificationService \(episode)", "Did fetch media \(mediaData == nil ? "Non-empty" : "Empty")")
Logger.shared.log("NotificationService \(episode)", "Will get unread count")
let _ = (getCurrentRenderedTotalUnreadCount(
accountManager: strongSelf.accountManager,
postbox: stateManager.postbox
@ -934,6 +973,8 @@ private final class NotificationServiceHandler {
content.badge = Int(value.0)
}
Logger.shared.log("NotificationService \(episode)", "Unread count: \(value.0), isCurrentAccount: \(isCurrentAccount)")
if let image = mediaAttachment as? TelegramMediaImage, let resource = largestImageRepresentation(image.representations)?.resource {
if let mediaData = mediaData {
stateManager.postbox.mediaBox.storeResourceData(resource.id, data: mediaData, synchronous: true)
@ -988,6 +1029,8 @@ private final class NotificationServiceHandler {
}
}
Logger.shared.log("NotificationService \(episode)", "Updating content to \(content)")
updateCurrentContent(content)
completed()
@ -1000,6 +1043,8 @@ private final class NotificationServiceHandler {
stateManager.network.shouldKeepConnection.set(.single(true))
if peerId.namespace == Namespaces.Peer.CloudChannel {
Logger.shared.log("NotificationService \(episode)", "Will poll channel \(peerId)")
pollSignal = standalonePollChannelOnce(
postbox: stateManager.postbox,
network: stateManager.network,
@ -1007,6 +1052,7 @@ private final class NotificationServiceHandler {
stateManager: stateManager
)
} else {
Logger.shared.log("NotificationService \(episode)", "Will perform non-specific getDifference")
enum ControlError {
case restart
}
@ -1054,6 +1100,7 @@ private final class NotificationServiceHandler {
completed()
}
case let .deleteMessage(ids):
Logger.shared.log("NotificationService \(episode)", "Will delete messages \(ids)")
let mediaBox = stateManager.postbox.mediaBox
let _ = (stateManager.postbox.transaction { transaction -> Void in
_internal_deleteMessages(transaction: transaction, mediaBox: mediaBox, ids: ids, deleteMedia: true)
@ -1084,6 +1131,8 @@ private final class NotificationServiceHandler {
if isCurrentAccount {
content.badge = Int(value.0)
}
Logger.shared.log("NotificationService \(episode)", "Unread count: \(value.0), isCurrentAccount: \(isCurrentAccount)")
Logger.shared.log("NotificationService \(episode)", "Updating content to \(content)")
updateCurrentContent(content)
@ -1092,6 +1141,7 @@ private final class NotificationServiceHandler {
}
if !removeIdentifiers.isEmpty {
Logger.shared.log("NotificationService \(episode)", "Will try to remove \(removeIdentifiers.count) notifications")
UNUserNotificationCenter.current().removeDeliveredNotifications(withIdentifiers: removeIdentifiers)
queue.after(1.0, {
completeRemoval()
@ -1102,6 +1152,7 @@ private final class NotificationServiceHandler {
})
})
case let .readMessage(id):
Logger.shared.log("NotificationService \(episode)", "Will read message \(id)")
let _ = (stateManager.postbox.transaction { transaction -> Void in
transaction.applyIncomingReadMaxId(id)
}
@ -1130,6 +1181,9 @@ private final class NotificationServiceHandler {
content.badge = Int(value.0)
}
Logger.shared.log("NotificationService \(episode)", "Unread count: \(value.0), isCurrentAccount: \(isCurrentAccount)")
Logger.shared.log("NotificationService \(episode)", "Updating content to \(content)")
updateCurrentContent(content)
completed()
@ -1137,6 +1191,7 @@ private final class NotificationServiceHandler {
}
if !removeIdentifiers.isEmpty {
Logger.shared.log("NotificationService \(episode)", "Will try to remove \(removeIdentifiers.count) notifications")
UNUserNotificationCenter.current().removeDeliveredNotifications(withIdentifiers: removeIdentifiers)
queue.after(1.0, {
completeRemoval()

View file

@ -22,6 +22,7 @@ swift_library(
"//submodules/OverlayStatusController:OverlayStatusController",
"//submodules/AccountContext:AccountContext",
"//submodules/AppBundle:AppBundle",
"//submodules/GZip:GZip"
],
visibility = [
"//visibility:public",

View file

@ -13,6 +13,7 @@ import PresentationDataUtils
import OverlayStatusController
import AccountContext
import AppBundle
import GZip
@objc private final class DebugControllerMailComposeDelegate: NSObject, MFMailComposeViewControllerDelegate {
public func mailComposeController(_ controller: MFMailComposeViewController, didFinishWith result: MFMailComposeResult, error: Error?) {
@ -219,42 +220,44 @@ private enum DebugControllerEntry: ItemListNodeEntry {
|> deliverOnMainQueue).start(next: { logs 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 logData: Data = Data()
var rawLogData: Data = Data()
for (name, path) in logs {
if !logData.isEmpty {
logData.append(lineFeed)
logData.append(lineFeed)
if !rawLogData.isEmpty {
rawLogData.append(lineFeed)
rawLogData.append(lineFeed)
}
logData.append("------ File: \(name) ------\n".data(using: .utf8)!)
rawLogData.append("------ File: \(name) ------\n".data(using: .utf8)!)
if let data = try? Data(contentsOf: URL(fileURLWithPath: path)) {
logData.append(data)
rawLogData.append(data)
}
}
let gzippedData = TGGZipData(rawLogData, 1.0)
let id = Int64.random(in: Int64.min ... Int64.max)
let fileResource = LocalFileMediaResource(fileId: id, size: logData.count, isSecretRelated: false)
context.account.postbox.mediaBox.storeResourceData(fileResource.id, data: logData)
let file = TelegramMediaFile(fileId: MediaId(namespace: Namespaces.Media.LocalFile, id: id), partialReference: nil, resource: fileResource, previewRepresentations: [], videoThumbnails: [], immediateThumbnailData: nil, mimeType: "application/text", size: logData.count, attributes: [.FileName(fileName: "Log-iOS-Full.txt")])
let fileResource = LocalFileMediaResource(fileId: id, size: 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/text", size: gzippedData.count, attributes: [.FileName(fileName: "Log-iOS-Full.txt.gz")])
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()
}
}
@ -263,7 +266,7 @@ private enum DebugControllerEntry: ItemListNodeEntry {
}
items.append(ActionSheetButtonItem(title: "Via Email", color: .accent, action: { [weak actionSheet] in
actionSheet?.dismissAnimated()
let composeController = MFMailComposeViewController()
composeController.mailComposeDelegate = arguments.mailComposeDelegate
composeController.setSubject("Telegram Logs")
@ -274,12 +277,12 @@ private enum DebugControllerEntry: ItemListNodeEntry {
}
arguments.getRootController()?.present(composeController, animated: true, completion: nil)
}))
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)
})
})
@ -371,42 +374,44 @@ private enum DebugControllerEntry: ItemListNodeEntry {
|> deliverOnMainQueue).start(next: { logs 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 logData: Data = Data()
var rawLogData: Data = Data()
for (name, path) in logs {
if !logData.isEmpty {
logData.append(lineFeed)
logData.append(lineFeed)
if !rawLogData.isEmpty {
rawLogData.append(lineFeed)
rawLogData.append(lineFeed)
}
logData.append("------ File: \(name) ------\n".data(using: .utf8)!)
rawLogData.append("------ File: \(name) ------\n".data(using: .utf8)!)
if let data = try? Data(contentsOf: URL(fileURLWithPath: path)) {
logData.append(data)
rawLogData.append(data)
}
}
let gzippedData = TGGZipData(rawLogData, 1.0)
let id = Int64.random(in: Int64.min ... Int64.max)
let fileResource = LocalFileMediaResource(fileId: id, size: logData.count, isSecretRelated: false)
context.account.postbox.mediaBox.storeResourceData(fileResource.id, data: logData)
let file = TelegramMediaFile(fileId: MediaId(namespace: Namespaces.Media.LocalFile, id: id), partialReference: nil, resource: fileResource, previewRepresentations: [], videoThumbnails: [], immediateThumbnailData: nil, mimeType: "application/text", size: logData.count, attributes: [.FileName(fileName: "Log-iOS-Full.txt")])
let fileResource = LocalFileMediaResource(fileId: id, size: 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/text", size: gzippedData.count, attributes: [.FileName(fileName: "Log-iOS-Full.txt.gz")])
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()
}
}
@ -415,7 +420,7 @@ private enum DebugControllerEntry: ItemListNodeEntry {
}
items.append(ActionSheetButtonItem(title: "Via Email", color: .accent, action: { [weak actionSheet] in
actionSheet?.dismissAnimated()
let composeController = MFMailComposeViewController()
composeController.mailComposeDelegate = arguments.mailComposeDelegate
composeController.setSubject("Telegram Logs")
@ -426,39 +431,86 @@ private enum DebugControllerEntry: ItemListNodeEntry {
}
arguments.getRootController()?.present(composeController, animated: true, completion: nil)
}))
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 .sendNotificationLogs:
return ItemListDisclosureItem(presentationData: presentationData, title: "Send Notification Logs", label: "", sectionId: self.section, style: .blocks, action: {
let _ = (Logger(rootPath: arguments.sharedContext.basePath, basePath: arguments.sharedContext.basePath + "/notificationServiceLogs").collectLogs()
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()
|> deliverOnMainQueue).start(next: { logs in
guard let context = arguments.context else {
return
}
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 messages = logs.map { (name, path) -> EnqueueMessage 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 rawLogData: Data = Data()
for (name, path) in logs {
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 gzippedData = TGGZipData(rawLogData, 1.0)
let id = Int64.random(in: Int64.min ... Int64.max)
let file = TelegramMediaFile(fileId: MediaId(namespace: Namespaces.Media.LocalFile, id: id), partialReference: nil, resource: LocalFileReferenceMediaResource(localFilePath: path, randomId: id), previewRepresentations: [], videoThumbnails: [], immediateThumbnailData: nil, mimeType: "application/text", size: nil, attributes: [.FileName(fileName: name)])
return .message(text: "", attributes: [], mediaReference: .standalone(media: file), replyToMessageId: nil, localGroupingKey: nil, correlationId: nil)
let fileResource = LocalFileMediaResource(fileId: id, size: 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/text", size: gzippedData.count, attributes: [.FileName(fileName: "Log-iOS-Full.txt.gz")])
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()
}
let _ = enqueueMessages(account: context.account, peerId: peerId, messages: messages).start()
}
arguments.pushController(controller)
}))
}
items.append(ActionSheetButtonItem(title: "Via Email", color: .accent, action: { [weak actionSheet] in
actionSheet?.dismissAnimated()
let composeController = MFMailComposeViewController()
composeController.mailComposeDelegate = arguments.mailComposeDelegate
composeController.setSubject("Telegram Logs")
for (name, path) in logs {
if let data = try? Data(contentsOf: URL(fileURLWithPath: path), options: .mappedIfSafe) {
composeController.addAttachmentData(data, mimeType: "application/text", fileName: name)
}
}
arguments.pushController(controller)
})
arguments.getRootController()?.present(composeController, animated: true, completion: nil)
}))
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 .sendCriticalLogs:
return ItemListDisclosureItem(presentationData: presentationData, title: "Send Critical Logs", label: "", sectionId: self.section, style: .blocks, action: {
@ -837,7 +889,7 @@ private func debugControllerEntries(sharedContext: SharedAccountContext, present
// entries.append(.testStickerImport(presentationData.theme))
entries.append(.sendLogs(presentationData.theme))
entries.append(.sendOneLog(presentationData.theme))
//entries.append(.sendOneLog(presentationData.theme))
entries.append(.sendShareLogs)
entries.append(.sendNotificationLogs(presentationData.theme))
entries.append(.sendCriticalLogs(presentationData.theme))

View file

@ -2064,6 +2064,18 @@ final class UniversalVideoGalleryItemNode: ZoomableContentGalleryItemNode {
}
@objc func pictureInPictureButtonPressed() {
var isNativePictureInPictureSupported = false
switch self.item?.contentInfo {
case let .message(message):
for media in message.media {
if let media = media as? TelegramMediaFile, media.isVideo {
isNativePictureInPictureSupported = true
}
}
default:
break
}
if let item = self.item, let videoNode = self.videoNode, let overlayController = self.context.sharedContext.mediaManager.overlayMediaManager.controller {
videoNode.setContinuePlayingWithoutSoundOnLostAudioSession(false)
@ -2071,7 +2083,7 @@ final class UniversalVideoGalleryItemNode: ZoomableContentGalleryItemNode {
let baseNavigationController = self.baseNavigationController()
let playbackRate = self.playbackRate
if #available(iOSApplicationExtension 15.0, iOS 15.0, *), AVPictureInPictureController.isPictureInPictureSupported() {
if #available(iOSApplicationExtension 15.0, iOS 15.0, *), AVPictureInPictureController.isPictureInPictureSupported(), isNativePictureInPictureSupported {
self.disablePictureInPicturePlaceholder = true
let overlayVideoNode = UniversalVideoNode(postbox: self.context.account.postbox, audioSession: self.context.sharedContext.mediaManager.audioSession, manager: self.context.sharedContext.mediaManager.universalVideoManager, decoration: GalleryVideoDecoration(), content: item.content, priority: .overlay)

View file

@ -14,6 +14,10 @@ final class MutableAdditionalChatListItemsView: MutablePostboxView {
}
return false
}
func refreshDueToExternalTransaction(postbox: PostboxImpl) -> Bool {
return false
}
func immutableView() -> PostboxView {
return AdditionalChatListItemsView(self)

View file

@ -48,6 +48,10 @@ final class MutableAllChatListHolesView: MutablePostboxView {
return false
}
}
func refreshDueToExternalTransaction(postbox: PostboxImpl) -> Bool {
return false
}
func immutableView() -> PostboxView {
return AllChatListHolesView(self)

View file

@ -16,6 +16,10 @@ final class MutableCachedItemView: MutablePostboxView {
}
return false
}
func refreshDueToExternalTransaction(postbox: PostboxImpl) -> Bool {
return false
}
func immutableView() -> PostboxView {
return CachedItemView(self)

View file

@ -17,6 +17,10 @@ final class MutableCachedPeerDataView: MutablePostboxView {
return false
}
}
func refreshDueToExternalTransaction(postbox: PostboxImpl) -> Bool {
return false
}
func immutableView() -> PostboxView {
return CachedPeerDataView(self)

View file

@ -70,6 +70,10 @@ final class MutableContactPeersView: MutablePostboxView {
return updated
}
func refreshDueToExternalTransaction(postbox: PostboxImpl) -> Bool {
return false
}
func immutableView() -> PostboxView {
return ContactPeersView(self)
}

View file

@ -33,6 +33,10 @@ final class MutableDeletedMessagesView: MutablePostboxView {
return updated
}
func refreshDueToExternalTransaction(postbox: PostboxImpl) -> Bool {
return false
}
func immutableView() -> PostboxView {
return DeletedMessagesView(self)
}

View file

@ -202,6 +202,25 @@ final class MutableGlobalMessageTagsView: MutablePostboxView {
return hasChanges
}
func refreshDueToExternalTransaction(postbox: PostboxImpl) -> Bool {
let (entries, lower, upper) = postbox.messageHistoryTable.entriesAround(globalTagMask: globalTag, index: position, count: count)
self.entries = entries.map { entry -> InternalGlobalMessageTagsEntry in
switch entry {
case let .message(message):
return .intermediateMessage(message)
case let .hole(index):
return .hole(index)
}
}
self.earlier = lower
self.later = upper
self.render(postbox: postbox)
return true
}
private func add(_ entry: InternalGlobalMessageTagsEntry) -> Bool {
if self.entries.count == 0 {

View file

@ -61,6 +61,22 @@ final class MutableHistoryTagInfoView: MutablePostboxView {
return false
}
}
func refreshDueToExternalTransaction(postbox: PostboxImpl) -> Bool {
var currentIndex: MessageIndex?
for namespace in postbox.messageHistoryIndexTable.existingNamespaces(peerId: self.peerId) {
if let index = postbox.messageHistoryTagsTable.latestIndex(tag: self.tag, peerId: self.peerId, namespace: namespace) {
currentIndex = index
break
}
}
if self.currentIndex != currentIndex {
self.currentIndex = currentIndex
return true
} else {
return false
}
}
func immutableView() -> PostboxView {
return HistoryTagInfoView(self)

View file

@ -37,6 +37,19 @@ final class MutableInvalidatedMessageHistoryTagSummariesView: MutablePostboxView
}
return updated
}
func refreshDueToExternalTransaction(postbox: PostboxImpl) -> Bool {
var entries = Set<InvalidatedMessageHistoryTagsSummaryEntry>()
for entry in postbox.invalidatedMessageHistoryTagsSummaryTable.get(tagMask: tagMask, namespace: namespace) {
entries.insert(entry)
}
if self.entries != entries {
self.entries = entries
return true
} else {
return false
}
}
func immutableView() -> PostboxView {
return InvalidatedMessageHistoryTagSummariesView(self)

View file

@ -40,6 +40,20 @@ final class MutableItemCollectionIdsView: MutablePostboxView {
}
return updated
}
func refreshDueToExternalTransaction(postbox: PostboxImpl) -> Bool {
var idsByNamespace: [ItemCollectionId.Namespace: Set<ItemCollectionId>] = [:]
for namespace in namespaces {
let ids = postbox.itemCollectionInfoTable.getIds(namespace: namespace)
idsByNamespace[namespace] = Set(ids)
}
if self.idsByNamespace != idsByNamespace {
self.idsByNamespace = idsByNamespace
return true
} else {
return false
}
}
func immutableView() -> PostboxView {
return ItemCollectionIdsView(self)

View file

@ -48,6 +48,10 @@ final class MutableItemCollectionInfoView: MutablePostboxView {
}
return updated
}
func refreshDueToExternalTransaction(postbox: PostboxImpl) -> Bool {
return false
}
func immutableView() -> PostboxView {
return ItemCollectionInfoView(self)

View file

@ -90,12 +90,14 @@ final class MutableItemCollectionInfosView: MutablePostboxView {
}
return updated
}
func refreshDueToExternalTransaction(postbox: PostboxImpl) -> Bool {
return false
}
func immutableView() -> PostboxView {
return ItemCollectionInfosView(self)
}
}
public final class ItemCollectionInfosView: PostboxView {

View file

@ -50,6 +50,10 @@ final class MutableLocalMessageTagsView: MutablePostboxView {
}
return updated
}
func refreshDueToExternalTransaction(postbox: PostboxImpl) -> Bool {
return false
}
func immutableView() -> PostboxView {
return LocalMessageTagsView(self)

View file

@ -25,6 +25,16 @@ final class MutableMessageHistoryTagSummaryView: MutablePostboxView {
return hasChanges
}
func refreshDueToExternalTransaction(postbox: PostboxImpl) -> Bool {
let count = postbox.messageHistoryTagsSummaryTable.get(MessageHistoryTagsSummaryKey(tag: self.tag, peerId: self.peerId, namespace: self.namespace))?.count
if self.count != count {
self.count = count
return true
} else {
return false
}
}
func immutableView() -> PostboxView {
return MessageHistoryTagSummaryView(self)

View file

@ -739,7 +739,7 @@ final class MutableMessageHistoryView {
}
case let .peerChatState(peerId, _):
if transaction.currentUpdatedPeerChatStates.contains(peerId) {
updated[i] = .peerChatState(peerId, postbox.peerChatStateTable.get(peerId) as? PeerChatState)
updated[i] = .peerChatState(peerId, postbox.peerChatStateTable.get(peerId)?.getLegacy() as? PeerChatState)
hasChanges = true
}
case .totalUnreadState:

View file

@ -171,6 +171,10 @@ final class MutableMessageOfInterestHolesView: MutablePostboxView {
return false
}
}
func refreshDueToExternalTransaction(postbox: PostboxImpl) -> Bool {
return false
}
func immutableView() -> PostboxView {
return MessageOfInterestHolesView(self)

View file

@ -51,6 +51,10 @@ final class MutableMessagesView: MutablePostboxView {
return false
}
}
func refreshDueToExternalTransaction(postbox: PostboxImpl) -> Bool {
return false
}
func immutableView() -> PostboxView {
return MessagesView(self)

View file

@ -42,6 +42,10 @@ final class MutableBasicPeerView: MutablePostboxView {
return updated
}
func refreshDueToExternalTransaction(postbox: PostboxImpl) -> Bool {
return false
}
func immutableView() -> PostboxView {
return BasicPeerView(self)

View file

@ -22,6 +22,10 @@ final class MutablePeerChatInclusionView: MutablePostboxView {
return updated
}
func refreshDueToExternalTransaction(postbox: PostboxImpl) -> Bool {
return false
}
func immutableView() -> PostboxView {
return PeerChatInclusionView(self)

View file

@ -51,6 +51,10 @@ final class MutableOrderedItemListView: MutablePostboxView {
return updated
}
func refreshDueToExternalTransaction(postbox: PostboxImpl) -> Bool {
return false
}
func immutableView() -> PostboxView {
return OrderedItemListView(self)

View file

@ -5,7 +5,7 @@ final class PeerChatStateTable: Table {
return ValueBoxTable(id: id, keyType: .int64, compactValuesOnCreation: false)
}
private var cachedPeerChatStates: [PeerId: PostboxCoding?] = [:]
private var cachedPeerChatStates: [PeerId: CodableEntry?] = [:]
private var updatedPeerIds = Set<PeerId>()
private let sharedKey = ValueBoxKey(length: 8)
@ -15,11 +15,12 @@ final class PeerChatStateTable: Table {
return self.sharedKey
}
func get(_ id: PeerId) -> PostboxCoding? {
func get(_ id: PeerId) -> CodableEntry? {
if let state = self.cachedPeerChatStates[id] {
return state
} else {
if let value = self.valueBox.get(self.table, key: self.key(id)), let state = PostboxDecoder(buffer: value).decodeRootObject() {
if let value = self.valueBox.get(self.table, key: self.key(id)) {
let state = CodableEntry(data: value.makeData())
self.cachedPeerChatStates[id] = state
return state
} else {
@ -29,7 +30,7 @@ final class PeerChatStateTable: Table {
}
}
func set(_ id: PeerId, state: PostboxCoding?) {
func set(_ id: PeerId, state: CodableEntry?) {
self.cachedPeerChatStates[id] = state
self.updatedPeerIds.insert(id)
}
@ -41,12 +42,9 @@ final class PeerChatStateTable: Table {
override func beforeCommit() {
if !self.updatedPeerIds.isEmpty {
let sharedEncoder = PostboxEncoder()
for id in self.updatedPeerIds {
if let wrappedState = self.cachedPeerChatStates[id], let state = wrappedState {
sharedEncoder.reset()
sharedEncoder.encodeRootObject(state)
self.valueBox.set(self.table, key: self.key(id), value: sharedEncoder.readBufferNoCopy())
self.valueBox.set(self.table, key: self.key(id), value: ReadBuffer(data: state.data))
} else {
self.valueBox.remove(self.table, key: self.key(id), secure: false)
}

View file

@ -2,7 +2,7 @@ import Foundation
final class MutablePeerChatStateView: MutablePostboxView {
let peerId: PeerId
var chatState: PostboxCoding?
var chatState: CodableEntry?
init(postbox: PostboxImpl, peerId: PeerId) {
self.peerId = peerId
@ -17,6 +17,16 @@ final class MutablePeerChatStateView: MutablePostboxView {
return false
}
}
func refreshDueToExternalTransaction(postbox: PostboxImpl) -> Bool {
let chatState = postbox.peerChatStateTable.get(self.peerId)
if self.chatState != chatState {
self.chatState = chatState
return true
} else {
return false
}
}
func immutableView() -> PostboxView {
return PeerChatStateView(self)
@ -25,7 +35,7 @@ final class MutablePeerChatStateView: MutablePostboxView {
public final class PeerChatStateView: PostboxView {
public let peerId: PeerId
public let chatState: PostboxCoding?
public let chatState: CodableEntry?
init(_ view: MutablePeerChatStateView) {
self.peerId = view.peerId

View file

@ -19,6 +19,16 @@ final class MutablePeerNotificationSettingsBehaviorTimestampView: MutablePostbox
return updated
}
func refreshDueToExternalTransaction(postbox: PostboxImpl) -> Bool {
let earliestTimestamp = postbox.peerNotificationSettingsBehaviorTable.getEarliest()?.1
if self.earliestTimestamp != earliestTimestamp {
self.earliestTimestamp = earliestTimestamp
return true
} else {
return false
}
}
func immutableView() -> PostboxView {
return PeerNotificationSettingsBehaviorTimestampView(self)

View file

@ -36,6 +36,43 @@ final class MutablePeerNotificationSettingsView: MutablePostboxView {
return false
}
}
func refreshDueToExternalTransaction(postbox: PostboxImpl) -> Bool {
var notificationSettings: [PeerId: PeerNotificationSettings] = [:]
for peerId in self.peerIds {
var notificationPeerId = peerId
if let peer = postbox.peerTable.get(peerId), let associatedPeerId = peer.associatedPeerId {
notificationPeerId = associatedPeerId
}
if let settings = postbox.peerNotificationSettingsTable.getEffective(notificationPeerId) {
notificationSettings[peerId] = settings
}
}
var updated = false
if self.notificationSettings.count != notificationSettings.count {
updated = true
} else {
for (key, value) in self.notificationSettings {
if let other = notificationSettings[key] {
if !other.isEqual(to: value) {
updated = true
break
}
} else {
updated = true
break
}
}
}
if updated {
self.notificationSettings = notificationSettings
return true
} else {
return false
}
}
func immutableView() -> PostboxView {
return PeerNotificationSettingsView(self)

View file

@ -26,6 +26,40 @@ final class MutablePeerPresencesView: MutablePostboxView {
return updated
}
func refreshDueToExternalTransaction(postbox: PostboxImpl) -> Bool {
var presences: [PeerId: PeerPresence] = [:]
for id in self.ids {
if let presence = postbox.peerPresenceTable.get(id) {
presences[id] = presence
}
}
var updated = false
if self.presences.count != presences.count {
updated = true
} else {
for (key, value) in self.presences {
if let other = presences[key] {
if !other.isEqual(to: value) {
updated = true
break
}
} else {
updated = true
break
}
}
}
if updated {
self.presences = presences
return true
} else {
return false
}
}
func immutableView() -> PostboxView {
return PeerPresencesView(self)

View file

@ -253,6 +253,10 @@ final class MutablePeerView: MutablePostboxView {
return updated
}
func refreshDueToExternalTransaction(postbox: PostboxImpl) -> Bool {
return false
}
func immutableView() -> PostboxView {
return PeerView(self)

View file

@ -16,6 +16,10 @@ final class MutablePendingMessageActionsSummaryView: MutablePostboxView {
}
return updated
}
func refreshDueToExternalTransaction(postbox: PostboxImpl) -> Bool {
return false
}
func immutableView() -> PostboxView {
return PendingMessageActionsSummaryView(self)

View file

@ -38,6 +38,10 @@ final class MutablePendingMessageActionsView: MutablePostboxView {
}
return updated
}
func refreshDueToExternalTransaction(postbox: PostboxImpl) -> Bool {
return false
}
func immutableView() -> PostboxView {
return PendingMessageActionsView(self)

View file

@ -25,6 +25,10 @@ final class MutablePendingPeerNotificationSettingsView: MutablePostboxView {
}
return updated
}
func refreshDueToExternalTransaction(postbox: PostboxImpl) -> Bool {
return false
}
func immutableView() -> PostboxView {
return PendingPeerNotificationSettingsView(self)

View file

@ -2212,7 +2212,7 @@ final class PostboxImpl {
}
fileprivate func setPeerChatState(_ id: PeerId, state: PeerChatState) {
self.peerChatStateTable.set(id, state: state)
self.peerChatStateTable.set(id, state: CodableEntry(legacyValue: state))
self.currentUpdatedPeerChatStates.insert(id)
}

View file

@ -5,6 +5,7 @@ public protocol PostboxView {
protocol MutablePostboxView {
func replay(postbox: PostboxImpl, transaction: PostboxTransaction) -> Bool
func refreshDueToExternalTransaction(postbox: PostboxImpl) -> Bool
func immutableView() -> PostboxView
}
@ -24,6 +25,16 @@ final class CombinedMutableView {
}
return updated
}
func refreshDueToExternalTransaction(postbox: PostboxImpl) -> Bool {
var updated = false
for (_, view) in self.views {
if view.refreshDueToExternalTransaction(postbox: postbox) {
updated = true
}
}
return updated
}
func immutableView() -> CombinedView {
var result: [PostboxViewKey: PostboxView] = [:]

View file

@ -13,11 +13,37 @@ public final class CodableEntry: Equatable {
self.data = encoder.makeData()
}
public init(legacyValue: PostboxCoding) {
let encoder = PostboxEncoder()
encoder.encodeRootObject(legacyValue)
self.data = encoder.makeData()
}
public func get<T: Decodable>(_ type: T.Type) -> T? {
let decoder = PostboxDecoder(buffer: MemoryBuffer(data: self.data))
return decoder.decode(T.self, forKey: "_")
}
public func getLegacy<T: PostboxCoding>(_ type: T.Type) -> T? {
let decoder = PostboxDecoder(buffer: MemoryBuffer(data: self.data))
let object = decoder.decodeRootObject()
if let object = object as? T {
return object
} else {
return nil
}
}
public func getLegacy() -> PostboxCoding? {
let decoder = PostboxDecoder(buffer: MemoryBuffer(data: self.data))
let object = decoder.decodeRootObject()
if let object = object {
return object
} else {
return nil
}
}
public static func ==(lhs: CodableEntry, rhs: CodableEntry) -> Bool {
return lhs.data == rhs.data
}

View file

@ -44,6 +44,21 @@ final class MutablePreferencesView: MutablePostboxView {
return updated
}
func refreshDueToExternalTransaction(postbox: PostboxImpl) -> Bool {
var values: [ValueBoxKey: PreferencesEntry] = [:]
for key in self.keys {
if let value = postbox.preferencesTable.get(key: key) {
values[key] = value
}
}
if self.values != values {
self.values = values
return true
} else {
return false
}
}
func immutableView() -> PostboxView {
return PreferencesView(self)

View file

@ -26,6 +26,16 @@ final class MutableSynchronizeGroupMessageStatsView: MutablePostboxView {
}
return updated
}
func refreshDueToExternalTransaction(postbox: PostboxImpl) -> Bool {
let groupsAndNamespaces = postbox.synchronizeGroupMessageStatsTable.get()
if self.groupsAndNamespaces != groupsAndNamespaces {
self.groupsAndNamespaces = groupsAndNamespaces
return true
} else {
return false
}
}
func immutableView() -> PostboxView {
return SynchronizeGroupMessageStatsView(self)

View file

@ -29,6 +29,41 @@ final class MutableTopChatMessageView: MutablePostboxView {
return updated
}
func refreshDueToExternalTransaction(postbox: PostboxImpl) -> Bool {
var messages: [PeerId: Message] = [:]
for peerId in self.peerIds {
if let index = postbox.chatListIndexTable.get(peerId: peerId).topMessageIndex {
messages[peerId] = postbox.getMessage(index.id)
}
}
var updated = false
if self.messages.count != messages.count {
updated = true
} else {
for (key, value) in self.messages {
if let other = messages[key] {
if other.stableId != value.stableId || other.stableVersion != value.stableVersion {
updated = true
break
}
} else {
updated = true
break
}
}
}
if updated {
self.messages = messages
return true
} else {
return false
}
}
func immutableView() -> PostboxView {
return TopChatMessageView(self)

View file

@ -6,10 +6,42 @@ public enum UnreadMessageCountsItem: Equatable {
case peer(PeerId)
}
private enum MutableUnreadMessageCountsItemEntry {
private enum MutableUnreadMessageCountsItemEntry: Equatable {
case total((ValueBoxKey, PreferencesEntry?)?, ChatListTotalUnreadState)
case totalInGroup(PeerGroupId, ChatListTotalUnreadState)
case peer(PeerId, CombinedPeerReadState?)
static func ==(lhs: MutableUnreadMessageCountsItemEntry, rhs: MutableUnreadMessageCountsItemEntry) -> Bool {
switch lhs {
case let .total(lhsKeyAndEntry, lhsUnreadState):
if case let .total(rhsKeyAndEntry, rhsUnreadState) = rhs {
if lhsKeyAndEntry?.0 != rhsKeyAndEntry?.0 {
return false
}
if lhsKeyAndEntry?.1 != rhsKeyAndEntry?.1 {
return false
}
if lhsUnreadState != rhsUnreadState {
return false
}
return true
} else {
return false
}
case let .totalInGroup(groupId, state):
if case .totalInGroup(groupId, state) = rhs {
return true
} else {
return false
}
case let .peer(peerId, readState):
if case .peer(peerId, readState) = rhs {
return true
} else {
return false
}
}
}
}
public enum UnreadMessageCountsItemEntry {
@ -19,9 +51,12 @@ public enum UnreadMessageCountsItemEntry {
}
final class MutableUnreadMessageCountsView: MutablePostboxView {
private let items: [UnreadMessageCountsItem]
fileprivate var entries: [MutableUnreadMessageCountsItemEntry]
init(postbox: PostboxImpl, items: [UnreadMessageCountsItem]) {
self.items = items
self.entries = items.map { item in
switch item {
case let .total(preferencesKey):
@ -80,6 +115,25 @@ final class MutableUnreadMessageCountsView: MutablePostboxView {
return updated
}
func refreshDueToExternalTransaction(postbox: PostboxImpl) -> Bool {
let entries: [MutableUnreadMessageCountsItemEntry] = self.items.map { item -> MutableUnreadMessageCountsItemEntry in
switch item {
case let .total(preferencesKey):
return .total(preferencesKey.flatMap({ ($0, postbox.preferencesTable.get(key: $0)) }), postbox.messageHistoryMetadataTable.getTotalUnreadState(groupId: .root))
case let .totalInGroup(groupId):
return .totalInGroup(groupId, postbox.messageHistoryMetadataTable.getTotalUnreadState(groupId: groupId))
case let .peer(peerId):
return .peer(peerId, postbox.readStateTable.getCombinedState(peerId))
}
}
if self.entries != entries {
self.entries = entries
return true
} else {
return false
}
}
func immutableView() -> PostboxView {
return UnreadMessageCountsView(self)
@ -151,6 +205,16 @@ final class MutableCombinedReadStateView: MutablePostboxView {
return updated
}
func refreshDueToExternalTransaction(postbox: PostboxImpl) -> Bool {
let state = postbox.readStateTable.getCombinedState(self.peerId)
if state != self.state {
self.state = state
return true
} else {
return false
}
}
func immutableView() -> PostboxView {
return CombinedReadStateView(self)

View file

@ -62,7 +62,7 @@ private final class BubbleSettingsControllerNode: ASDisplayNode, UIScrollViewDel
self.scrollNode = ASScrollNode()
self.chatBackgroundNode = WallpaperBackgroundNode(context: context)
self.chatBackgroundNode = createWallpaperBackgroundNode(context: context)
self.chatBackgroundNode.displaysAsynchronously = false
self.messagesContainerNode = ASDisplayNode()

View file

@ -132,7 +132,7 @@ class ForwardPrivacyChatPreviewItemNode: ListViewItemNode {
return { item, params, neighbors in
if currentBackgroundNode == nil {
currentBackgroundNode = WallpaperBackgroundNode(context: item.context)
currentBackgroundNode = createWallpaperBackgroundNode(context: item.context)
}
currentBackgroundNode?.update(wallpaper: item.wallpaper)
currentBackgroundNode?.updateBubbleTheme(bubbleTheme: item.theme, bubbleCorners: item.chatBubbleCorners)

View file

@ -74,7 +74,7 @@ private final class TextSizeSelectionControllerNode: ASDisplayNode, UIScrollView
self.pageControlNode = PageControlNode(dotSpacing: 7.0, dotColor: .white, inactiveDotColor: UIColor.white.withAlphaComponent(0.4))
self.chatListBackgroundNode = ASDisplayNode()
self.chatBackgroundNode = WallpaperBackgroundNode(context: context)
self.chatBackgroundNode = createWallpaperBackgroundNode(context: context)
self.chatBackgroundNode.displaysAsynchronously = false
self.messagesContainerNode = ASDisplayNode()

View file

@ -285,7 +285,7 @@ final class ThemeAccentColorControllerNode: ASDisplayNode, UIScrollViewDelegate
self.backgroundContainerNode = ASDisplayNode()
self.backgroundContainerNode.clipsToBounds = true
self.backgroundWrapperNode = ASDisplayNode()
self.backgroundNode = WallpaperBackgroundNode(context: context)
self.backgroundNode = createWallpaperBackgroundNode(context: context)
self.messagesContainerNode = ASDisplayNode()
self.messagesContainerNode.clipsToBounds = true
@ -1354,7 +1354,7 @@ final class ThemeAccentColorControllerNode: ASDisplayNode, UIScrollViewDelegate
@objc private func playPressed() {
if self.state.backgroundColors.count >= 3 || self.state.messagesColors.count >= 3 {
self.backgroundNode.animateEvent(transition: .animated(duration: 0.5, curve: .spring))
self.backgroundNode.animateEvent(transition: .animated(duration: 0.5, curve: .spring), extendAnimation: false)
} else {
self.updateState({ state in
var state = state

View file

@ -107,7 +107,7 @@ final class ThemePreviewControllerNode: ASDisplayNode, UIScrollViewDelegate {
self.messagesContainerNode.clipsToBounds = true
self.messagesContainerNode.transform = CATransform3DMakeScale(1.0, -1.0, 1.0)
self.instantChatBackgroundNode = WallpaperBackgroundNode(context: context)
self.instantChatBackgroundNode = createWallpaperBackgroundNode(context: context)
self.instantChatBackgroundNode.displaysAsynchronously = false
self.ready.set(.single(true))
@ -121,7 +121,7 @@ final class ThemePreviewControllerNode: ASDisplayNode, UIScrollViewDelegate {
self.blurredNode = BlurredImageNode()
self.blurredNode.blurView.contentMode = .scaleAspectFill
self.wallpaperNode = WallpaperBackgroundNode(context: context)
self.wallpaperNode = createWallpaperBackgroundNode(context: context)
self.toolbarNode = WallpaperGalleryToolbarNode(theme: self.previewTheme, strings: self.presentationData.strings, doneButtonType: .set)

View file

@ -138,7 +138,7 @@ class ThemeSettingsChatPreviewItemNode: ListViewItemNode {
return { item, params, neighbors in
if currentBackgroundNode == nil {
currentBackgroundNode = WallpaperBackgroundNode(context: item.context)
currentBackgroundNode = createWallpaperBackgroundNode(context: item.context)
}
currentBackgroundNode?.update(wallpaper: item.wallpaper)
currentBackgroundNode?.updateBubbleTheme(bubbleTheme: item.componentTheme, bubbleCorners: item.chatBubbleCorners)

View file

@ -136,7 +136,7 @@ final class WallpaperGalleryItemNode: GalleryItemNode {
self.wrapperNode = ASDisplayNode()
self.imageNode = TransformImageNode()
self.imageNode.contentAnimations = .subsequentUpdates
self.nativeNode = WallpaperBackgroundNode(context: context)
self.nativeNode = createWallpaperBackgroundNode(context: context)
self.cropNode = WallpaperCropNode()
self.statusNode = RadialStatusNode(backgroundNodeColor: UIColor(white: 0.0, alpha: 0.6))
self.statusNode.frame = CGRect(x: 0.0, y: 0.0, width: progressDiameter, height: progressDiameter)
@ -812,7 +812,7 @@ final class WallpaperGalleryItemNode: GalleryItemNode {
switch wallpaper {
case let .gradient(gradient):
if gradient.colors.count >= 3 {
self.nativeNode.animateEvent(transition: .animated(duration: 0.5, curve: .spring))
self.nativeNode.animateEvent(transition: .animated(duration: 0.5, curve: .spring), extendAnimation: false)
} else {
let rotation = gradient.settings.rotation ?? 0
self.requestRotateGradient?((rotation + 90) % 360)
@ -820,7 +820,7 @@ final class WallpaperGalleryItemNode: GalleryItemNode {
case let .file(file):
if file.isPattern {
if file.settings.colors.count >= 3 {
self.nativeNode.animateEvent(transition: .animated(duration: 0.5, curve: .spring))
self.nativeNode.animateEvent(transition: .animated(duration: 0.5, curve: .spring), extendAnimation: false)
} else {
let rotation = file.settings.rotation ?? 0
self.requestRotateGradient?((rotation + 90) % 360)

View file

@ -964,7 +964,7 @@ final class MessageStoryRenderer {
self.containerNode = ASDisplayNode()
self.instantChatBackgroundNode = WallpaperBackgroundNode(context: context)
self.instantChatBackgroundNode = createWallpaperBackgroundNode(context: context)
self.instantChatBackgroundNode.displaysAsynchronously = false
self.messagesContainerNode = ASDisplayNode()

View file

@ -181,8 +181,12 @@ private func synchronizeMarkAllUnseen(transaction: Transaction, postbox: Postbox
)
|> mapToSignal { resultId -> Signal<Void, GetUnseenIdsError> in
if let resultId = resultId {
let _ = currentMaxId.swap(resultId)
return .complete()
let previous = currentMaxId.swap(resultId)
if previous == resultId {
return .fail(.done)
} else {
return .complete()
}
} else {
return .fail(.done)
}

View file

@ -1174,17 +1174,7 @@ private func extractAccountManagerState(records: AccountRecordsView<TelegramAcco
self.pushRegistry = pushRegistry
pushRegistry.delegate = self
self.badgeDisposable.set((self.context.get()
|> mapToSignal { context -> Signal<Int32, NoError> in
if let context = context {
return context.applicationBadge
} else {
return .single(0)
}
}
|> deliverOnMainQueue).start(next: { count in
UIApplication.shared.applicationIconBadgeNumber = Int(count)
}))
self.resetBadge()
if #available(iOS 9.1, *) {
self.quickActionsDisposable.set((self.context.get()
@ -1281,6 +1271,20 @@ private func extractAccountManagerState(records: AccountRecordsView<TelegramAcco
return true
}
private func resetBadge() {
self.badgeDisposable.set((self.context.get()
|> mapToSignal { context -> Signal<Int32, NoError> in
if let context = context {
return context.applicationBadge
} else {
return .single(0)
}
}
|> deliverOnMainQueue).start(next: { count in
UIApplication.shared.applicationIconBadgeNumber = Int(count)
}))
}
func applicationWillResignActive(_ application: UIApplication) {
self.isActiveValue = false
self.isActivePromise.set(false)
@ -1361,6 +1365,8 @@ private func extractAccountManagerState(records: AccountRecordsView<TelegramAcco
self.isInForegroundPromise.set(true)
self.isActiveValue = true
self.isActivePromise.set(true)
self.resetBadge()
self.maybeCheckForUpdates()
}

View file

@ -508,7 +508,7 @@ public final class ChatControllerImpl: TelegramBaseController, ChatController, G
default:
break
}
self.chatBackgroundNode = WallpaperBackgroundNode(context: context, useSharedAnimationPhase: useSharedAnimationPhase)
self.chatBackgroundNode = createWallpaperBackgroundNode(context: context, useSharedAnimationPhase: useSharedAnimationPhase)
self.wallpaperReady.set(self.chatBackgroundNode.isReady)
var locationBroadcastPanelSource: LocationBroadcastPanelSource

View file

@ -370,7 +370,7 @@ class ChatControllerNode: ASDisplayNode, UIScrollViewDelegate {
if (strongSelf.context.sharedContext.currentPresentationData.with({ $0 })).reduceMotion {
return
}
strongSelf.backgroundNode.animateEvent(transition: transition)
strongSelf.backgroundNode.animateEvent(transition: transition, extendAnimation: false)
}
getMessageTransitionNode = { [weak self] in
@ -1648,7 +1648,7 @@ class ChatControllerNode: ASDisplayNode, UIScrollViewDelegate {
if (self.context.sharedContext.currentPresentationData.with({ $0 })).reduceMotion {
return
}
self.backgroundNode.animateEvent(transition: transition)
self.backgroundNode.animateEvent(transition: transition, extendAnimation: false)
}
//self.historyNode.didScrollWithOffset?(listBottomInset - previousListBottomInset, transition, nil)
}

View file

@ -24,7 +24,7 @@ private func attributedServiceMessageString(theme: ChatPresentationThemeData, st
class ChatMessageActionBubbleContentNode: ChatMessageBubbleContentNode {
let labelNode: TextNode
var backgroundNode: WallpaperBackgroundNode.BubbleBackgroundNode?
var backgroundNode: WallpaperBubbleBackgroundNode?
var backgroundColorNode: ASDisplayNode
let backgroundMaskNode: ASImageNode
var linkHighlightingNode: LinkHighlightingNode?
@ -327,7 +327,7 @@ class ChatMessageActionBubbleContentNode: ChatMessageBubbleContentNode {
var backgroundFrame = backgroundNode.frame
backgroundFrame.origin.x += rect.minX
backgroundFrame.origin.y += rect.minY
backgroundNode.update(rect: backgroundFrame, within: containerSize)
backgroundNode.update(rect: backgroundFrame, within: containerSize, transition: .immediate)
}
}

View file

@ -165,7 +165,7 @@ class ChatMessageAnimatedStickerItemNode: ChatMessageItemView {
private let containerNode: ContextControllerSourceNode
let imageNode: TransformImageNode
private var enableSynchronousImageApply: Bool = false
private var backgroundNode: WallpaperBackgroundNode.BubbleBackgroundNode?
private var backgroundNode: WallpaperBubbleBackgroundNode?
private(set) var placeholderNode: StickerShimmerEffectNode
private(set) var animationNode: GenericAnimatedStickerNode?
private var animationSize: CGSize?
@ -650,7 +650,7 @@ class ChatMessageAnimatedStickerItemNode: ChatMessageItemView {
self.placeholderNode.updateAbsoluteRect(CGRect(origin: CGPoint(x: rect.minX + self.placeholderNode.frame.minX, y: rect.minY + self.placeholderNode.frame.minY), size: self.placeholderNode.frame.size), within: containerSize)
if let backgroundNode = self.backgroundNode {
backgroundNode.update(rect: CGRect(origin: CGPoint(x: rect.minX + self.placeholderNode.frame.minX, y: rect.minY + self.placeholderNode.frame.minY), size: self.placeholderNode.frame.size), within: containerSize)
backgroundNode.update(rect: CGRect(origin: CGPoint(x: rect.minX + self.placeholderNode.frame.minX, y: rect.minY + self.placeholderNode.frame.minY), size: self.placeholderNode.frame.size), within: containerSize, transition: .immediate)
}
}
}

View file

@ -55,7 +55,7 @@ func bubbleMaskForType(_ type: ChatMessageBackgroundType, graphics: PrincipalThe
}
final class ChatMessageBubbleBackdrop: ASDisplayNode {
private var backgroundContent: WallpaperBackgroundNode.BubbleBackgroundNode?
private var backgroundContent: WallpaperBubbleBackgroundNode?
private var currentType: ChatMessageBackgroundType?
private var currentMaskMode: Bool?
@ -86,7 +86,7 @@ final class ChatMessageBubbleBackdrop: ASDisplayNode {
var backgroundFrame = backgroundContent.frame
backgroundFrame.origin.x += rect.minX
backgroundFrame.origin.y += rect.minY
backgroundContent.update(rect: backgroundFrame, within: containerSize)
backgroundContent.update(rect: backgroundFrame, within: containerSize, transition: .immediate)
}
}
}
@ -142,7 +142,7 @@ final class ChatMessageBubbleBackdrop: ASDisplayNode {
var backgroundFrame = backgroundContent.frame
backgroundFrame.origin.x += rect.minX
backgroundFrame.origin.y += rect.minY
backgroundContent.update(rect: backgroundFrame, within: containerSize)
backgroundContent.update(rect: backgroundFrame, within: containerSize, transition: .immediate)
}
}
@ -162,7 +162,7 @@ final class ChatMessageBubbleBackdrop: ASDisplayNode {
var backgroundFrame = backgroundContent.frame
backgroundFrame.origin.x += rect.minX
backgroundFrame.origin.y += rect.minY
backgroundContent.update(rect: backgroundFrame, within: containerSize)
backgroundContent.update(rect: backgroundFrame, within: containerSize, transition: .immediate)
}
self.backgroundContent = backgroundContent
self.insertSubnode(backgroundContent, at: 0)
@ -174,7 +174,7 @@ final class ChatMessageBubbleBackdrop: ASDisplayNode {
var backgroundFrame = backgroundContent.frame
backgroundFrame.origin.x += rect.minX
backgroundFrame.origin.y += rect.minY
backgroundContent.update(rect: backgroundFrame, within: containerSize)
backgroundContent.update(rect: backgroundFrame, within: containerSize, transition: .immediate)
}
self.backgroundContent = backgroundContent
self.insertSubnode(backgroundContent, at: 0)

View file

@ -22,7 +22,7 @@ class ChatMessageStickerItemNode: ChatMessageItemView {
let contextSourceNode: ContextExtractedContentContainingNode
private let containerNode: ContextControllerSourceNode
let imageNode: TransformImageNode
private var backgroundNode: WallpaperBackgroundNode.BubbleBackgroundNode?
private var backgroundNode: WallpaperBubbleBackgroundNode?
private var placeholderNode: StickerShimmerEffectNode
var textNode: TextNode?
@ -250,7 +250,7 @@ class ChatMessageStickerItemNode: ChatMessageItemView {
self.placeholderNode.updateAbsoluteRect(CGRect(origin: CGPoint(x: rect.minX + placeholderNode.frame.minX, y: rect.minY + placeholderNode.frame.minY), size: placeholderNode.frame.size), within: containerSize)
if let backgroundNode = self.backgroundNode {
backgroundNode.update(rect: CGRect(origin: CGPoint(x: rect.minX + self.placeholderNode.frame.minX, y: rect.minY + self.placeholderNode.frame.minY), size: self.placeholderNode.frame.size), within: containerSize)
backgroundNode.update(rect: CGRect(origin: CGPoint(x: rect.minX + self.placeholderNode.frame.minX, y: rect.minY + self.placeholderNode.frame.minY), size: self.placeholderNode.frame.size), within: containerSize, transition: .immediate)
}
}
}

View file

@ -353,7 +353,7 @@ final class BadgeComponent: CombinedComponent {
if lhs.withinSize != rhs.withinSize {
return false
}
if lhs.wallpaperNode != rhs.wallpaperNode {
if lhs.wallpaperNode !== rhs.wallpaperNode {
return false
}
return true
@ -564,7 +564,7 @@ final class AvatarComponent: Component {
}
private final class WallpaperBlurNode: ASDisplayNode {
private var backgroundNode: WallpaperBackgroundNode.BubbleBackgroundNode?
private var backgroundNode: WallpaperBubbleBackgroundNode?
private let colorNode: ASDisplayNode
override init() {

View file

@ -99,7 +99,7 @@ final class ChatRecentActionsControllerNode: ViewControllerTracingNode {
self.automaticMediaDownloadSettings = context.sharedContext.currentAutomaticMediaDownloadSettings.with { $0 }
self.backgroundNode = WallpaperBackgroundNode(context: context)
self.backgroundNode = createWallpaperBackgroundNode(context: context)
self.backgroundNode.isUserInteractionEnabled = false
self.panelBackgroundNode = NavigationBackgroundNode(color: self.presentationData.theme.chat.inputPanel.panelBackgroundColor)

View file

@ -64,7 +64,7 @@ class ChatReplyCountItem: ListViewItem {
class ChatReplyCountItemNode: ListViewItemNode {
var item: ChatReplyCountItem?
private let labelNode: TextNode
private var backgroundNode: WallpaperBackgroundNode.BubbleBackgroundNode?
private var backgroundNode: WallpaperBubbleBackgroundNode?
private let backgroundColorNode: ASDisplayNode
private var theme: ChatPresentationThemeData?
@ -201,7 +201,7 @@ class ChatReplyCountItemNode: ListViewItemNode {
backgroundFrame.origin.x += rect.minX
backgroundFrame.origin.y += rect.minY
backgroundNode.update(rect: backgroundFrame, within: containerSize)
backgroundNode.update(rect: backgroundFrame, within: containerSize, transition: .immediate)
}
}

View file

@ -27,25 +27,47 @@ private func generateBlurredContents(image: UIImage) -> UIImage? {
return context.generateImage()
}
public final class WallpaperBackgroundNode: ASDisplayNode {
public final class BubbleBackgroundNode: ASDisplayNode {
public enum BubbleType {
case incoming
case outgoing
case free
}
public enum WallpaperBubbleType {
case incoming
case outgoing
case free
}
private let bubbleType: BubbleType
public protocol WallpaperBubbleBackgroundNode: ASDisplayNode {
var frame: CGRect { get set }
func update(rect: CGRect, within containerSize: CGSize, transition: ContainedViewLayoutTransition)
func update(rect: CGRect, within containerSize: CGSize, transition: CombinedTransition)
func offset(value: CGPoint, animationCurve: ContainedViewLayoutTransitionCurve, duration: Double)
func offsetSpring(value: CGFloat, duration: Double, damping: CGFloat)
}
public protocol WallpaperBackgroundNode: ASDisplayNode {
var isReady: Signal<Bool, NoError> { get }
var rotation: CGFloat { get set }
func update(wallpaper: TelegramWallpaper)
func _internalUpdateIsSettingUpWallpaper()
func updateLayout(size: CGSize, transition: ContainedViewLayoutTransition)
func animateEvent(transition: ContainedViewLayoutTransition, extendAnimation: Bool)
func updateBubbleTheme(bubbleTheme: PresentationTheme, bubbleCorners: PresentationChatBubbleCorners)
func hasBubbleBackground(for type: WallpaperBubbleType) -> Bool
func makeBubbleBackground(for type: WallpaperBubbleType) -> WallpaperBubbleBackgroundNode?
}
final class WallpaperBackgroundNodeImpl: ASDisplayNode, WallpaperBackgroundNode {
final class BubbleBackgroundNodeImpl: ASDisplayNode, WallpaperBubbleBackgroundNode {
private let bubbleType: WallpaperBubbleType
private let contentNode: ASImageNode
private var cleanWallpaperNode: ASDisplayNode?
private var gradientWallpaperNode: GradientBackgroundNode.CloneNode?
private weak var backgroundNode: WallpaperBackgroundNode?
private var index: SparseBag<BubbleBackgroundNode>.Index?
private weak var backgroundNode: WallpaperBackgroundNodeImpl?
private var index: SparseBag<BubbleBackgroundNodeImpl>.Index?
private var currentLayout: (rect: CGRect, containerSize: CGSize)?
public override var frame: CGRect {
override var frame: CGRect {
didSet {
if oldValue.size != self.bounds.size {
self.contentNode.frame = self.bounds
@ -59,7 +81,7 @@ public final class WallpaperBackgroundNode: ASDisplayNode {
}
}
init(backgroundNode: WallpaperBackgroundNode, bubbleType: BubbleType) {
init(backgroundNode: WallpaperBackgroundNodeImpl, bubbleType: WallpaperBubbleType) {
self.backgroundNode = backgroundNode
self.bubbleType = bubbleType
@ -210,7 +232,7 @@ public final class WallpaperBackgroundNode: ASDisplayNode {
}
}
public func update(rect: CGRect, within containerSize: CGSize, transition: ContainedViewLayoutTransition = .immediate) {
func update(rect: CGRect, within containerSize: CGSize, transition: ContainedViewLayoutTransition = .immediate) {
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))
@ -233,7 +255,7 @@ public final class WallpaperBackgroundNode: ASDisplayNode {
}
}
public func update(rect: CGRect, within containerSize: CGSize, transition: CombinedTransition) {
func update(rect: CGRect, within containerSize: CGSize, transition: CombinedTransition) {
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))
@ -250,7 +272,7 @@ public final class WallpaperBackgroundNode: ASDisplayNode {
}
}
public func offset(value: CGPoint, animationCurve: ContainedViewLayoutTransitionCurve, duration: Double) {
func offset(value: CGPoint, animationCurve: ContainedViewLayoutTransitionCurve, duration: Double) {
guard let (_, containerSize) = self.currentLayout else {
return
}
@ -267,7 +289,7 @@ public final class WallpaperBackgroundNode: ASDisplayNode {
}
}
public func offsetSpring(value: CGFloat, duration: Double, damping: CGFloat) {
func offsetSpring(value: CGFloat, duration: Double, damping: CGFloat) {
guard let (_, containerSize) = self.currentLayout else {
return
}
@ -285,9 +307,9 @@ public final class WallpaperBackgroundNode: ASDisplayNode {
}
private final class BubbleBackgroundNodeReference {
weak var node: BubbleBackgroundNode?
weak var node: BubbleBackgroundNodeImpl?
init(node: BubbleBackgroundNode) {
init(node: BubbleBackgroundNodeImpl) {
self.node = node
}
}
@ -303,6 +325,8 @@ public final class WallpaperBackgroundNode: ASDisplayNode {
private let patternImageNode: ASImageNode
private var isGeneratingPatternImage: Bool = false
private let bakedBackgroundView: UIImageView
private var validLayout: CGSize?
private var wallpaper: TelegramWallpaper?
private var isSettingUpWallpaper: Bool = false
@ -367,7 +391,7 @@ public final class WallpaperBackgroundNode: ASDisplayNode {
}
}
public var rotation: CGFloat = 0.0 {
var rotation: CGFloat = 0.0 {
didSet {
var fromValue: CGFloat = 0.0
if let value = (self.layer.value(forKeyPath: "transform.rotation.z") as? NSNumber)?.floatValue {
@ -400,11 +424,11 @@ public final class WallpaperBackgroundNode: ASDisplayNode {
private static var cachedSharedPattern: (PatternKey, UIImage)?
private let _isReady = ValuePromise<Bool>(false, ignoreRepeated: true)
public var isReady: Signal<Bool, NoError> {
var isReady: Signal<Bool, NoError> {
return self._isReady.get()
}
public init(context: AccountContext, useSharedAnimationPhase: Bool = false) {
init(context: AccountContext, useSharedAnimationPhase: Bool) {
self.context = context
self.useSharedAnimationPhase = useSharedAnimationPhase
self.imageContentMode = .scaleAspectFill
@ -413,6 +437,9 @@ public final class WallpaperBackgroundNode: ASDisplayNode {
self.contentNode.contentMode = self.imageContentMode
self.patternImageNode = ASImageNode()
self.bakedBackgroundView = UIImageView()
self.bakedBackgroundView.isHidden = true
super.init()
@ -420,6 +447,8 @@ public final class WallpaperBackgroundNode: ASDisplayNode {
self.contentNode.frame = self.bounds
self.addSubnode(self.contentNode)
self.addSubnode(self.patternImageNode)
//self.view.addSubview(self.bakedBackgroundView)
}
deinit {
@ -428,7 +457,7 @@ public final class WallpaperBackgroundNode: ASDisplayNode {
self.imageDisposable.dispose()
}
public func update(wallpaper: TelegramWallpaper) {
func update(wallpaper: TelegramWallpaper) {
if self.wallpaper == wallpaper {
return
}
@ -539,7 +568,7 @@ public final class WallpaperBackgroundNode: ASDisplayNode {
}
}
public func _internalUpdateIsSettingUpWallpaper() {
func _internalUpdateIsSettingUpWallpaper() {
self.isSettingUpWallpaper = true
}
@ -622,7 +651,7 @@ public final class WallpaperBackgroundNode: ASDisplayNode {
self.validPatternGeneratedImage = nil
self.validPatternImage = nil
if let cachedValidPatternImage = WallpaperBackgroundNode.cachedValidPatternImage, cachedValidPatternImage.generated.wallpaper == wallpaper {
if let cachedValidPatternImage = WallpaperBackgroundNodeImpl.cachedValidPatternImage, cachedValidPatternImage.generated.wallpaper == wallpaper {
self.validPatternImage = ValidPatternImage(wallpaper: cachedValidPatternImage.generated.wallpaper, generate: cachedValidPatternImage.generate)
} else {
func reference(for resource: EngineMediaResource, media: EngineMedia) -> MediaResourceReference {
@ -688,7 +717,7 @@ public final class WallpaperBackgroundNode: ASDisplayNode {
if self.validPatternGeneratedImage != updatedGeneratedImage {
self.validPatternGeneratedImage = updatedGeneratedImage
if let cachedValidPatternImage = WallpaperBackgroundNode.cachedValidPatternImage, cachedValidPatternImage.generated == updatedGeneratedImage {
if let cachedValidPatternImage = WallpaperBackgroundNodeImpl.cachedValidPatternImage, cachedValidPatternImage.generated == updatedGeneratedImage {
self.patternImageNode.image = cachedValidPatternImage.image
self.updatePatternPresentation()
} else {
@ -700,7 +729,7 @@ public final class WallpaperBackgroundNode: ASDisplayNode {
self.updatePatternPresentation()
if self.useSharedAnimationPhase {
WallpaperBackgroundNode.cachedValidPatternImage = CachedValidPatternImage(generate: validPatternImage.generate, generated: updatedGeneratedImage, image: image)
WallpaperBackgroundNodeImpl.cachedValidPatternImage = CachedValidPatternImage(generate: validPatternImage.generate, generated: updatedGeneratedImage, image: image)
}
} else {
self.updatePatternPresentation()
@ -721,7 +750,7 @@ public final class WallpaperBackgroundNode: ASDisplayNode {
strongSelf.updatePatternPresentation()
if let image = image, strongSelf.useSharedAnimationPhase {
WallpaperBackgroundNode.cachedValidPatternImage = CachedValidPatternImage(generate: validPatternImage.generate, generated: updatedGeneratedImage, image: image)
WallpaperBackgroundNodeImpl.cachedValidPatternImage = CachedValidPatternImage(generate: validPatternImage.generate, generated: updatedGeneratedImage, image: image)
}
}
}
@ -743,7 +772,7 @@ public final class WallpaperBackgroundNode: ASDisplayNode {
transition.updateFrame(node: self.patternImageNode, frame: CGRect(origin: CGPoint(), size: size))
}
public func updateLayout(size: CGSize, transition: ContainedViewLayoutTransition) {
func updateLayout(size: CGSize, transition: ContainedViewLayoutTransition) {
let isFirstLayout = self.validLayout == nil
self.validLayout = size
@ -767,12 +796,25 @@ public final class WallpaperBackgroundNode: ASDisplayNode {
}
}
public func animateEvent(transition: ContainedViewLayoutTransition, extendAnimation: Bool = false) {
func animateEvent(transition: ContainedViewLayoutTransition, extendAnimation: Bool) {
self.gradientBackgroundNode?.animateEvent(transition: transition, extendAnimation: extendAnimation)
self.outgoingBubbleGradientBackgroundNode?.animateEvent(transition: transition, extendAnimation: extendAnimation)
}
public func updateBubbleTheme(bubbleTheme: PresentationTheme, bubbleCorners: PresentationChatBubbleCorners) {
private func updateBakedBackground() {
guard let size = self.validLayout else {
return
}
let context = DrawingContext(size: size, scale: UIScreenScale, opaque: true)
context.withContext { context in
context.clear(CGRect(origin: CGPoint(), size: size))
}
self.bakedBackgroundView.image = context.generateImage()
}
func updateBubbleTheme(bubbleTheme: PresentationTheme, bubbleCorners: PresentationChatBubbleCorners) {
if self.bubbleTheme !== bubbleTheme || self.bubbleCorners != bubbleCorners {
self.bubbleTheme = bubbleTheme
self.bubbleCorners = bubbleCorners
@ -801,7 +843,7 @@ public final class WallpaperBackgroundNode: ASDisplayNode {
}
}
public func hasBubbleBackground(for type: WallpaperBackgroundNode.BubbleBackgroundNode.BubbleType) -> Bool {
func hasBubbleBackground(for type: WallpaperBubbleType) -> Bool {
guard let bubbleTheme = self.bubbleTheme, let bubbleCorners = self.bubbleCorners else {
return false
}
@ -846,12 +888,138 @@ public final class WallpaperBackgroundNode: ASDisplayNode {
return false
}
public func makeBubbleBackground(for type: WallpaperBackgroundNode.BubbleBackgroundNode.BubbleType) -> WallpaperBackgroundNode.BubbleBackgroundNode? {
func makeBubbleBackground(for type: WallpaperBubbleType) -> WallpaperBubbleBackgroundNode? {
if !self.hasBubbleBackground(for: type) {
return nil
}
let node = WallpaperBackgroundNode.BubbleBackgroundNode(backgroundNode: self, bubbleType: type)
let node = WallpaperBackgroundNodeImpl.BubbleBackgroundNodeImpl(backgroundNode: self, bubbleType: type)
node.updateContents()
return node
}
}
final class WallpaperBackgroundNodeMergedImpl: ASDisplayNode, WallpaperBackgroundNode {
final class SharedStorage {
}
private class WallpaperComponentView: UIView {
let updated: () -> Void
init(updated: @escaping () -> Void) {
self.updated = updated
super.init(frame: CGRect())
}
required init?(coder: NSCoder) {
fatalError("init(coder:) has not been implemented")
}
}
private final class WallpaperGradiendComponentView: WallpaperComponentView {
struct Spec {
var colors: [UInt32]
}
}
private final class WallpaperPatternComponentView: WallpaperComponentView {
struct Spec {
}
}
private let context: AccountContext
private let storage: SharedStorage
private let staticView: UIImageView
private var gradient: WallpaperGradiendComponentView?
private var pattern: WallpaperPatternComponentView?
private let _isReady = ValuePromise<Bool>(false, ignoreRepeated: true)
var isReady: Signal<Bool, NoError> {
return self._isReady.get()
}
var rotation: CGFloat = 0.0 {
didSet {
}
}
init(context: AccountContext, storage: SharedStorage?) {
self.context = context
self.storage = storage ?? SharedStorage()
self.staticView = UIImageView()
super.init()
self.view.addSubview(self.staticView)
}
func update(wallpaper: TelegramWallpaper) {
var gradientSpec: WallpaperGradiendComponentView.Spec?
switch wallpaper {
case let .builtin(wallpaperSettings):
let _ = wallpaperSettings
case let .color(color):
let _ = color
case let .gradient(gradient):
if gradient.colors.count >= 3 {
gradientSpec = WallpaperGradiendComponentView.Spec(colors: gradient.colors)
}
case let .image(representations, settings):
let _ = representations
let _ = settings
case let .file(file):
if file.settings.colors.count >= 3 {
gradientSpec = WallpaperGradiendComponentView.Spec(colors: file.settings.colors)
}
}
if let gradientSpec = gradientSpec {
let gradient: WallpaperGradiendComponentView
if let current = self.gradient {
gradient = current
} else {
gradient = WallpaperGradiendComponentView(updated: { [weak self] in
self?.componentsUpdated()
})
}
let _ = gradient
let _ = gradientSpec
} else if let gradient = self.gradient {
self.gradient = nil
gradient.removeFromSuperview()
}
}
private func componentsUpdated() {
}
func _internalUpdateIsSettingUpWallpaper() {
}
func updateLayout(size: CGSize, transition: ContainedViewLayoutTransition) {
}
func animateEvent(transition: ContainedViewLayoutTransition, extendAnimation: Bool) {
}
func updateBubbleTheme(bubbleTheme: PresentationTheme, bubbleCorners: PresentationChatBubbleCorners) {
}
func hasBubbleBackground(for type: WallpaperBubbleType) -> Bool {
return false
}
func makeBubbleBackground(for type: WallpaperBubbleType) -> WallpaperBubbleBackgroundNode? {
return nil
}
}
public func createWallpaperBackgroundNode(context: AccountContext, useSharedAnimationPhase: Bool = false) -> WallpaperBackgroundNode {
return WallpaperBackgroundNodeImpl(context: context, useSharedAnimationPhase: useSharedAnimationPhase)
}