mirror of
https://github.com/TelegramMessenger/Telegram-iOS.git
synced 2026-07-06 03:33:41 +02:00
Merge branch 'master' of gitlab.com:peter-iakovlev/telegram-ios
This commit is contained in:
commit
bf4da379b9
60 changed files with 847 additions and 157 deletions
|
|
@ -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()
|
||||
|
|
|
|||
|
|
@ -22,6 +22,7 @@ swift_library(
|
|||
"//submodules/OverlayStatusController:OverlayStatusController",
|
||||
"//submodules/AccountContext:AccountContext",
|
||||
"//submodules/AppBundle:AppBundle",
|
||||
"//submodules/GZip:GZip"
|
||||
],
|
||||
visibility = [
|
||||
"//visibility:public",
|
||||
|
|
|
|||
|
|
@ -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))
|
||||
|
|
|
|||
|
|
@ -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)
|
||||
|
|
|
|||
|
|
@ -14,6 +14,10 @@ final class MutableAdditionalChatListItemsView: MutablePostboxView {
|
|||
}
|
||||
return false
|
||||
}
|
||||
|
||||
func refreshDueToExternalTransaction(postbox: PostboxImpl) -> Bool {
|
||||
return false
|
||||
}
|
||||
|
||||
func immutableView() -> PostboxView {
|
||||
return AdditionalChatListItemsView(self)
|
||||
|
|
|
|||
|
|
@ -48,6 +48,10 @@ final class MutableAllChatListHolesView: MutablePostboxView {
|
|||
return false
|
||||
}
|
||||
}
|
||||
|
||||
func refreshDueToExternalTransaction(postbox: PostboxImpl) -> Bool {
|
||||
return false
|
||||
}
|
||||
|
||||
func immutableView() -> PostboxView {
|
||||
return AllChatListHolesView(self)
|
||||
|
|
|
|||
|
|
@ -16,6 +16,10 @@ final class MutableCachedItemView: MutablePostboxView {
|
|||
}
|
||||
return false
|
||||
}
|
||||
|
||||
func refreshDueToExternalTransaction(postbox: PostboxImpl) -> Bool {
|
||||
return false
|
||||
}
|
||||
|
||||
func immutableView() -> PostboxView {
|
||||
return CachedItemView(self)
|
||||
|
|
|
|||
|
|
@ -17,6 +17,10 @@ final class MutableCachedPeerDataView: MutablePostboxView {
|
|||
return false
|
||||
}
|
||||
}
|
||||
|
||||
func refreshDueToExternalTransaction(postbox: PostboxImpl) -> Bool {
|
||||
return false
|
||||
}
|
||||
|
||||
func immutableView() -> PostboxView {
|
||||
return CachedPeerDataView(self)
|
||||
|
|
|
|||
|
|
@ -70,6 +70,10 @@ final class MutableContactPeersView: MutablePostboxView {
|
|||
return updated
|
||||
}
|
||||
|
||||
func refreshDueToExternalTransaction(postbox: PostboxImpl) -> Bool {
|
||||
return false
|
||||
}
|
||||
|
||||
func immutableView() -> PostboxView {
|
||||
return ContactPeersView(self)
|
||||
}
|
||||
|
|
|
|||
|
|
@ -33,6 +33,10 @@ final class MutableDeletedMessagesView: MutablePostboxView {
|
|||
return updated
|
||||
}
|
||||
|
||||
func refreshDueToExternalTransaction(postbox: PostboxImpl) -> Bool {
|
||||
return false
|
||||
}
|
||||
|
||||
func immutableView() -> PostboxView {
|
||||
return DeletedMessagesView(self)
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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 {
|
||||
|
|
|
|||
|
|
@ -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)
|
||||
|
|
|
|||
|
|
@ -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)
|
||||
|
|
|
|||
|
|
@ -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)
|
||||
|
|
|
|||
|
|
@ -48,6 +48,10 @@ final class MutableItemCollectionInfoView: MutablePostboxView {
|
|||
}
|
||||
return updated
|
||||
}
|
||||
|
||||
func refreshDueToExternalTransaction(postbox: PostboxImpl) -> Bool {
|
||||
return false
|
||||
}
|
||||
|
||||
func immutableView() -> PostboxView {
|
||||
return ItemCollectionInfoView(self)
|
||||
|
|
|
|||
|
|
@ -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 {
|
||||
|
|
|
|||
|
|
@ -50,6 +50,10 @@ final class MutableLocalMessageTagsView: MutablePostboxView {
|
|||
}
|
||||
return updated
|
||||
}
|
||||
|
||||
func refreshDueToExternalTransaction(postbox: PostboxImpl) -> Bool {
|
||||
return false
|
||||
}
|
||||
|
||||
func immutableView() -> PostboxView {
|
||||
return LocalMessageTagsView(self)
|
||||
|
|
|
|||
|
|
@ -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)
|
||||
|
|
|
|||
|
|
@ -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:
|
||||
|
|
|
|||
|
|
@ -171,6 +171,10 @@ final class MutableMessageOfInterestHolesView: MutablePostboxView {
|
|||
return false
|
||||
}
|
||||
}
|
||||
|
||||
func refreshDueToExternalTransaction(postbox: PostboxImpl) -> Bool {
|
||||
return false
|
||||
}
|
||||
|
||||
func immutableView() -> PostboxView {
|
||||
return MessageOfInterestHolesView(self)
|
||||
|
|
|
|||
|
|
@ -51,6 +51,10 @@ final class MutableMessagesView: MutablePostboxView {
|
|||
return false
|
||||
}
|
||||
}
|
||||
|
||||
func refreshDueToExternalTransaction(postbox: PostboxImpl) -> Bool {
|
||||
return false
|
||||
}
|
||||
|
||||
func immutableView() -> PostboxView {
|
||||
return MessagesView(self)
|
||||
|
|
|
|||
|
|
@ -42,6 +42,10 @@ final class MutableBasicPeerView: MutablePostboxView {
|
|||
|
||||
return updated
|
||||
}
|
||||
|
||||
func refreshDueToExternalTransaction(postbox: PostboxImpl) -> Bool {
|
||||
return false
|
||||
}
|
||||
|
||||
func immutableView() -> PostboxView {
|
||||
return BasicPeerView(self)
|
||||
|
|
|
|||
|
|
@ -22,6 +22,10 @@ final class MutablePeerChatInclusionView: MutablePostboxView {
|
|||
|
||||
return updated
|
||||
}
|
||||
|
||||
func refreshDueToExternalTransaction(postbox: PostboxImpl) -> Bool {
|
||||
return false
|
||||
}
|
||||
|
||||
func immutableView() -> PostboxView {
|
||||
return PeerChatInclusionView(self)
|
||||
|
|
|
|||
|
|
@ -51,6 +51,10 @@ final class MutableOrderedItemListView: MutablePostboxView {
|
|||
|
||||
return updated
|
||||
}
|
||||
|
||||
func refreshDueToExternalTransaction(postbox: PostboxImpl) -> Bool {
|
||||
return false
|
||||
}
|
||||
|
||||
func immutableView() -> PostboxView {
|
||||
return OrderedItemListView(self)
|
||||
|
|
|
|||
|
|
@ -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)
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
|
|
|||
|
|
@ -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)
|
||||
|
|
|
|||
|
|
@ -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)
|
||||
|
|
|
|||
|
|
@ -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)
|
||||
|
|
|
|||
|
|
@ -253,6 +253,10 @@ final class MutablePeerView: MutablePostboxView {
|
|||
|
||||
return updated
|
||||
}
|
||||
|
||||
func refreshDueToExternalTransaction(postbox: PostboxImpl) -> Bool {
|
||||
return false
|
||||
}
|
||||
|
||||
func immutableView() -> PostboxView {
|
||||
return PeerView(self)
|
||||
|
|
|
|||
|
|
@ -16,6 +16,10 @@ final class MutablePendingMessageActionsSummaryView: MutablePostboxView {
|
|||
}
|
||||
return updated
|
||||
}
|
||||
|
||||
func refreshDueToExternalTransaction(postbox: PostboxImpl) -> Bool {
|
||||
return false
|
||||
}
|
||||
|
||||
func immutableView() -> PostboxView {
|
||||
return PendingMessageActionsSummaryView(self)
|
||||
|
|
|
|||
|
|
@ -38,6 +38,10 @@ final class MutablePendingMessageActionsView: MutablePostboxView {
|
|||
}
|
||||
return updated
|
||||
}
|
||||
|
||||
func refreshDueToExternalTransaction(postbox: PostboxImpl) -> Bool {
|
||||
return false
|
||||
}
|
||||
|
||||
func immutableView() -> PostboxView {
|
||||
return PendingMessageActionsView(self)
|
||||
|
|
|
|||
|
|
@ -25,6 +25,10 @@ final class MutablePendingPeerNotificationSettingsView: MutablePostboxView {
|
|||
}
|
||||
return updated
|
||||
}
|
||||
|
||||
func refreshDueToExternalTransaction(postbox: PostboxImpl) -> Bool {
|
||||
return false
|
||||
}
|
||||
|
||||
func immutableView() -> PostboxView {
|
||||
return PendingPeerNotificationSettingsView(self)
|
||||
|
|
|
|||
|
|
@ -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)
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -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] = [:]
|
||||
|
|
|
|||
|
|
@ -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
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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)
|
||||
|
|
|
|||
|
|
@ -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)
|
||||
|
|
|
|||
|
|
@ -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)
|
||||
|
|
|
|||
|
|
@ -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)
|
||||
|
|
|
|||
|
|
@ -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()
|
||||
|
|
|
|||
|
|
@ -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)
|
||||
|
|
|
|||
|
|
@ -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()
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
|
|
|||
|
|
@ -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)
|
||||
|
||||
|
|
|
|||
|
|
@ -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)
|
||||
|
|
|
|||
|
|
@ -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)
|
||||
|
|
|
|||
|
|
@ -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()
|
||||
|
|
|
|||
|
|
@ -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)
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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()
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
|
|
|||
|
|
@ -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)
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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)
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -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)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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)
|
||||
|
|
|
|||
|
|
@ -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)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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() {
|
||||
|
|
|
|||
|
|
@ -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)
|
||||
|
|
|
|||
|
|
@ -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)
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -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)
|
||||
}
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue