Merge commit '1462366056b085c59b4ed5dbe88f33a86c7e96b8' into layer-135

This commit is contained in:
Ilya Laktyushin 2021-11-05 21:46:33 +04:00
commit 7b7a4203c4
85 changed files with 1834 additions and 232 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,12 +615,16 @@ private final class NotificationServiceHandler {
encryptedPayload.append("=")
}
guard let payloadData = Data(base64Encoded: encryptedPayload) else {
Logger.shared.log("NotificationService \(episode)", "Invalid payload 2")
return nil
}
let _ = (self.accountManager.accountRecords()
let _ = (combineLatest(queue: self.queue,
self.accountManager.accountRecords(),
self.accountManager.sharedData(keys: [ApplicationSpecificSharedDataKeys.inAppNotificationSettings])
)
|> take(1)
|> deliverOn(self.queue)).start(next: { [weak self] records in
|> deliverOn(self.queue)).start(next: { [weak self] records, sharedData in
var recordId: AccountRecordId?
var isCurrentAccount: Bool = false
@ -620,7 +644,11 @@ private final class NotificationServiceHandler {
}
}
let inAppNotificationSettings = sharedData.entries[ApplicationSpecificSharedDataKeys.inAppNotificationSettings]?.get(InAppNotificationSettings.self) ?? InAppNotificationSettings.defaultSettings
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 +669,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 +688,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 +697,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 +706,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 +715,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 +880,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 +952,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 +961,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 +978,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 +1034,8 @@ private final class NotificationServiceHandler {
}
}
Logger.shared.log("NotificationService \(episode)", "Updating content to \(content)")
updateCurrentContent(content)
completed()
@ -1000,6 +1048,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 +1057,7 @@ private final class NotificationServiceHandler {
stateManager: stateManager
)
} else {
Logger.shared.log("NotificationService \(episode)", "Will perform non-specific getDifference")
enum ControlError {
case restart
}
@ -1029,7 +1080,7 @@ private final class NotificationServiceHandler {
pollWithUpdatedContent = stateManager.postbox.transaction { transaction -> NotificationContent in
var content = initialContent
if let peer = transaction.getPeer(interactionAuthorId) {
if inAppNotificationSettings.displayNameOnLockscreen, let peer = transaction.getPeer(interactionAuthorId) {
content.addSenderInfo(mediaBox: stateManager.postbox.mediaBox, accountPeerId: stateManager.accountPeerId, peer: peer)
}
@ -1054,6 +1105,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 +1136,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 +1146,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 +1157,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 +1186,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 +1196,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

@ -81,6 +81,7 @@ public final class AnimatedStickerFrame {
self.type = type
self.width = width
self.height = height
assert(bytesPerRow > 0)
self.bytesPerRow = bytesPerRow
self.index = index
self.isLastFrame = isLastFrame
@ -687,7 +688,7 @@ private final class AnimatedStickerDirectFrameSource: AnimatedStickerFrameSource
self.currentFrame += 1
if draw {
if let cache = self.cache, let yuvData = cache.readUncompressedYuvFrame(index: frameIndex) {
return AnimatedStickerFrame(data: yuvData, type: .yuva, width: self.width, height: self.height, bytesPerRow: 0, index: frameIndex, isLastFrame: frameIndex == self.frameCount - 1, totalFrames: self.frameCount)
return AnimatedStickerFrame(data: yuvData, type: .yuva, width: self.width, height: self.height, bytesPerRow: self.width * 2, index: frameIndex, isLastFrame: frameIndex == self.frameCount - 1, totalFrames: self.frameCount)
} else {
var frameData = Data(count: self.bytesPerRow * self.height)
frameData.withUnsafeMutableBytes { buffer -> Void in
@ -1134,6 +1135,8 @@ public final class AnimatedStickerNode: ASDisplayNode {
guard let strongSelf = self else {
return
}
assert(frame.bytesPerRow != 0)
strongSelf.renderer?.render(queue: strongSelf.queue, width: frame.width, height: frame.height, bytesPerRow: frame.bytesPerRow, data: frame.data, type: frame.type, completion: {
guard let strongSelf = self else {

View File

@ -10,6 +10,7 @@ final class SoftwareAnimationRenderer: ASDisplayNode, AnimationRenderer {
private var highlightedColor: UIColor?
func render(queue: Queue, width: Int, height: Int, bytesPerRow: Int, data: Data, type: AnimationRendererFrameType, completion: @escaping () -> Void) {
assert(bytesPerRow > 0)
queue.async { [weak self] in
switch type {
case .argb:
@ -26,7 +27,7 @@ final class SoftwareAnimationRenderer: ASDisplayNode, AnimationRenderer {
guard let baseAddress = bytes.baseAddress else {
return
}
if bytesPerRow * height > bytes.count {
if bytesPerRow <= 0 || height <= 0 || width <= 0 || bytesPerRow * height > bytes.count {
assert(false)
return
}

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?) {
@ -79,7 +80,7 @@ private enum DebugControllerEntry: ItemListNodeEntry {
case experimentalCompatibility(Bool)
case enableDebugDataDisplay(Bool)
case acceleratedStickers(Bool)
case mockICE(Bool)
case experimentalBackground(Bool)
case playerEmbedding(Bool)
case playlistPlayback(Bool)
case voiceConference
@ -101,7 +102,7 @@ private enum DebugControllerEntry: ItemListNodeEntry {
return DebugControllerSection.logging.rawValue
case .enableRaiseToSpeak, .keepChatNavigationStack, .skipReadHistory, .crashOnSlowQueries:
return DebugControllerSection.experiments.rawValue
case .clearTips, .crash, .resetData, .resetDatabase, .resetDatabaseAndCache, .resetHoles, .reindexUnread, .resetBiometricsData, .optimizeDatabase, .photoPreview, .knockoutWallpaper, .playerEmbedding, .playlistPlayback, .voiceConference, .experimentalCompatibility, .enableDebugDataDisplay, .acceleratedStickers, .mockICE:
case .clearTips, .crash, .resetData, .resetDatabase, .resetDatabaseAndCache, .resetHoles, .reindexUnread, .resetBiometricsData, .optimizeDatabase, .photoPreview, .knockoutWallpaper, .playerEmbedding, .playlistPlayback, .voiceConference, .experimentalCompatibility, .enableDebugDataDisplay, .acceleratedStickers, .experimentalBackground:
return DebugControllerSection.experiments.rawValue
case .preferredVideoCodec:
return DebugControllerSection.videoExperiments.rawValue
@ -170,7 +171,7 @@ private enum DebugControllerEntry: ItemListNodeEntry {
return 27
case .acceleratedStickers:
return 29
case .mockICE:
case .experimentalBackground:
return 30
case .playerEmbedding:
return 31
@ -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: {
@ -752,12 +804,12 @@ private enum DebugControllerEntry: ItemListNodeEntry {
})
}).start()
})
case let .mockICE(value):
return ItemListSwitchItem(presentationData: presentationData, title: "mockICE", value: value, sectionId: self.section, style: .blocks, updated: { value in
case let .experimentalBackground(value):
return ItemListSwitchItem(presentationData: presentationData, title: "Background Experiment", value: value, sectionId: self.section, style: .blocks, updated: { value in
let _ = arguments.sharedContext.accountManager.transaction ({ transaction in
transaction.updateSharedData(ApplicationSpecificSharedDataKeys.experimentalUISettings, { settings in
var settings = settings?.get(ExperimentalUISettings.self) ?? ExperimentalUISettings.defaultSettings
settings.mockICE = value
settings.experimentalBackground = value
return PreferencesEntry(settings)
})
}).start()
@ -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))
@ -874,7 +926,7 @@ private func debugControllerEntries(sharedContext: SharedAccountContext, present
entries.append(.experimentalCompatibility(experimentalSettings.experimentalCompatibility))
entries.append(.enableDebugDataDisplay(experimentalSettings.enableDebugDataDisplay))
entries.append(.acceleratedStickers(experimentalSettings.acceleratedStickers))
entries.append(.mockICE(experimentalSettings.mockICE))
entries.append(.experimentalBackground(experimentalSettings.experimentalBackground))
entries.append(.playerEmbedding(experimentalSettings.playerEmbedding))
entries.append(.playlistPlayback(experimentalSettings.playlistPlayback))
}

View File

@ -518,7 +518,22 @@ private final class PictureInPictureContentImpl: NSObject, PictureInPictureConte
}
public func pictureInPictureController(_ pictureInPictureController: AVPictureInPictureController, skipByInterval skipInterval: CMTime, completion completionHandler: @escaping () -> Void) {
completionHandler()
let node = self.node
let _ = (self.node.status
|> take(1)
|> deliverOnMainQueue).start(next: { [weak node] status in
if let node = node, let timestamp = status?.timestamp, let duration = status?.duration {
let nextTimestamp = timestamp + skipInterval.seconds
if nextTimestamp > duration {
node.seek(0.0)
node.pause()
} else {
node.seek(min(duration, nextTimestamp))
}
}
completionHandler()
})
}
public func pictureInPictureControllerShouldProhibitBackgroundAudioPlayback(_ pictureInPictureController: AVPictureInPictureController) -> Bool {
@ -539,7 +554,9 @@ private final class PictureInPictureContentImpl: NSObject, PictureInPictureConte
private var hiddenMediaManagerIndex: Int?
init(overlayController: OverlayMediaController, mediaManager: MediaManager, accountId: AccountRecordId, hiddenMedia: (MessageId, Media)?, videoNode: UniversalVideoNode, willBegin: @escaping (PictureInPictureContentImpl) -> Void, didEnd: @escaping (PictureInPictureContentImpl) -> Void, expand: @escaping (@escaping () -> Void) -> Void) {
private var messageRemovedDisposable: Disposable?
init(context: AccountContext, overlayController: OverlayMediaController, mediaManager: MediaManager, accountId: AccountRecordId, hiddenMedia: (MessageId, Media)?, videoNode: UniversalVideoNode, canSkip: Bool, willBegin: @escaping (PictureInPictureContentImpl) -> Void, didEnd: @escaping (PictureInPictureContentImpl) -> Void, expand: @escaping (@escaping () -> Void) -> Void) {
self.overlayController = overlayController
self.mediaManager = mediaManager
self.node = videoNode
@ -559,7 +576,7 @@ private final class PictureInPictureContentImpl: NSObject, PictureInPictureConte
contentDelegate.pictureInPictureController = pictureInPictureController
pictureInPictureController.canStartPictureInPictureAutomaticallyFromInline = false
pictureInPictureController.requiresLinearPlayback = true
pictureInPictureController.requiresLinearPlayback = !canSkip
pictureInPictureController.delegate = self
self.pictureInPictureController = pictureInPictureController
let timer = SwiftSignalKit.Timer(timeout: 0.005, repeat: true, completion: { [weak self] in
@ -586,9 +603,31 @@ private final class PictureInPictureContentImpl: NSObject, PictureInPictureConte
}
})
}
if let (messageId, _) = hiddenMedia {
self.messageRemovedDisposable = (context.account.postbox.combinedView(keys: [PostboxViewKey.messages([messageId])])
|> map { views -> Bool in
if let view = views.views[PostboxViewKey.messages([messageId])] as? MessagesView {
if view.messages[messageId] == nil {
return true
}
}
return false
}
|> filter { $0 }
|> take(1)
|> deliverOnMainQueue).start(next: { [weak self] _ in
guard let strongSelf = self else {
return
}
overlayController.removePictureInPictureContent(content: strongSelf)
strongSelf.node.canAttachContent = false
})
}
}
deinit {
self.messageRemovedDisposable?.dispose()
self.pictureInPictureTimer?.invalidate()
self.node.setCanPlaybackWithoutHierarchy(false)
@ -698,6 +737,8 @@ final class UniversalVideoGalleryItemNode: ZoomableContentGalleryItemNode {
private var dismissOnOrientationChange = false
private var keepSoundOnDismiss = false
private var hasPictureInPicture = false
private var pictureInPictureButton: UIBarButtonItem?
private var requiresDownload = false
@ -1328,6 +1369,7 @@ final class UniversalVideoGalleryItemNode: ZoomableContentGalleryItemNode {
}
if forceEnablePiP || (!isAnimated && !disablePlayerControls && !disablePictureInPicture) {
let rightBarButtonItem = UIBarButtonItem(image: pictureInPictureButtonImage, style: .plain, target: self, action: #selector(self.pictureInPictureButtonPressed))
self.pictureInPictureButton = rightBarButtonItem
barButtonItems.append(rightBarButtonItem)
self.hasPictureInPicture = true
} else {
@ -1427,6 +1469,8 @@ final class UniversalVideoGalleryItemNode: ZoomableContentGalleryItemNode {
pictureInPictureNode.removeFromSupernode()
self.videoNode?.backgroundColor = .black
}
self.pictureInPictureButton?.isEnabled = self.pictureInPictureNode == nil
}
private func shouldAutoplayOnCentrality() -> Bool {
@ -2064,6 +2108,20 @@ 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 {
if message.id.namespace == Namespaces.Message.Cloud {
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 +2129,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)
@ -2094,7 +2152,7 @@ final class UniversalVideoGalleryItemNode: ZoomableContentGalleryItemNode {
break
}
let content = PictureInPictureContentImpl(overlayController: overlayController, mediaManager: self.context.sharedContext.mediaManager, accountId: self.context.account.id, hiddenMedia: hiddenMedia, videoNode: overlayVideoNode, willBegin: { [weak self] content in
let content = PictureInPictureContentImpl(context: self.context, overlayController: overlayController, mediaManager: self.context.sharedContext.mediaManager, accountId: self.context.account.id, hiddenMedia: hiddenMedia, videoNode: overlayVideoNode, canSkip: true, willBegin: { [weak self] content in
guard let strongSelf = self else {
return
}

View File

@ -275,7 +275,7 @@ public final class GradientBackgroundNode: ASDisplayNode {
deinit {
}
public func updateLayout(size: CGSize, transition: ContainedViewLayoutTransition, extendAnimation: Bool = false, backwards: Bool = false) {
public func updateLayout(size: CGSize, transition: ContainedViewLayoutTransition, extendAnimation: Bool, backwards: Bool, completion: @escaping () -> Void) {
let sizeUpdated = self.validLayout != size
self.validLayout = size
@ -372,6 +372,10 @@ public final class GradientBackgroundNode: ASDisplayNode {
animation.beginTime = self.contentView.layer.convertTime(CACurrentMediaTime(), from: nil) + 0.25
}
animation.completion = { _ in
completion()
}
self.contentView.layer.removeAnimation(forKey: "contents")
self.contentView.layer.add(animation, forKey: "contents")
@ -405,7 +409,11 @@ public final class GradientBackgroundNode: ASDisplayNode {
for cloneNode in self.cloneNodes {
cloneNode.value?.image = dimmedImage
}
completion()
}
} else {
completion()
}
} else if sizeUpdated {
let image = generateGradient(size: imageSize, colors: self.colors, positions: positions)
@ -419,6 +427,10 @@ public final class GradientBackgroundNode: ASDisplayNode {
}
self.validPhase = self.phase
completion()
} else {
completion()
}
transition.updateFrame(view: self.contentView, frame: CGRect(origin: CGPoint(), size: size))
@ -440,13 +452,14 @@ public final class GradientBackgroundNode: ASDisplayNode {
self.colors = colors
self.invalidated = true
if let size = self.validLayout {
self.updateLayout(size: size, transition: .immediate)
self.updateLayout(size: size, transition: .immediate, extendAnimation: false, backwards: false, completion: {})
}
}
}
public func animateEvent(transition: ContainedViewLayoutTransition, extendAnimation: Bool = false, backwards: Bool = false) {
public func animateEvent(transition: ContainedViewLayoutTransition, extendAnimation: Bool, backwards: Bool, completion: @escaping () -> Void) {
guard case let .animated(duration, _) = transition, duration > 0.001 else {
completion()
return
}
@ -463,7 +476,9 @@ public final class GradientBackgroundNode: ASDisplayNode {
GradientBackgroundNode.sharedPhase = self.phase
}
if let size = self.validLayout {
self.updateLayout(size: size, transition: transition, extendAnimation: extendAnimation, backwards: backwards)
self.updateLayout(size: size, transition: transition, extendAnimation: extendAnimation, backwards: backwards, completion: completion)
} else {
completion()
}
}
}

View File

@ -255,15 +255,14 @@ class ItemListPeerActionItemNode: ListViewItemNode {
case .sameSection(false):
bottomStripeInset = leftInset + editingOffset
bottomStripeOffset = -separatorHeight
strongSelf.bottomStripeNode.isHidden = !item.hasSeparator
default:
bottomStripeInset = 0.0
bottomStripeOffset = 0.0
hasBottomCorners = true
strongSelf.bottomStripeNode.isHidden = hasCorners
strongSelf.bottomStripeNode.isHidden = hasCorners || !item.hasSeparator
}
strongSelf.bottomStripeNode.isHidden = strongSelf.bottomStripeNode.isHidden || !item.hasSeparator
strongSelf.maskNode.image = hasCorners ? PresentationResourcesItemList.cornersImage(item.presentationData.theme, top: hasTopCorners, bottom: hasBottomCorners) : nil
strongSelf.backgroundNode.frame = CGRect(origin: CGPoint(x: 0.0, y: -min(insets.top, separatorHeight)), size: CGSize(width: params.width, height: contentSize.height + min(insets.top, separatorHeight) + min(insets.bottom, separatorHeight)))

View File

@ -13,7 +13,7 @@
@property (nonatomic, readonly) bool isAnimation;
@property (nonatomic, readonly) TGMediaAsset *originalAsset;
@property (nonatomic, readonly) CGSize dimensions;
@property (nonatomic, readonly) NSString *uniformTypeIdentifier;
- (instancetype)initWithURL:(NSURL *)url;
- (instancetype)initWithAsset:(TGMediaAsset *)asset livePhoto:(bool)livePhoto;

View File

@ -88,6 +88,11 @@
return _cachedAVAsset;
}
- (NSString *)uniformTypeIdentifier
{
return nil;
}
- (SSignal *)avAsset {
if (_originalAsset != nil) {
if (_cachedAVAsset != nil) {

View File

@ -1107,7 +1107,11 @@
}
else if (self.item.avAsset != nil) {
itemSignal = [self.item.avAsset mapToSignal:^SSignal *(AVAsset *avAsset) {
return [SSignal single:[AVPlayerItem playerItemWithAsset:avAsset]];
if ([avAsset isKindOfClass:[AVAsset class]]) {
return [SSignal single:[AVPlayerItem playerItemWithAsset:avAsset]];
} else {
return [SSignal never];
}
}];
}

View File

@ -108,6 +108,9 @@
+ (SSignal *)convertAVAsset:(AVAsset *)avAsset adjustments:(TGMediaVideoEditAdjustments *)adjustments watcher:(TGMediaVideoFileWatcher *)watcher inhibitAudio:(bool)inhibitAudio entityRenderer:(id<TGPhotoPaintEntityRenderer>)entityRenderer
{
if ([avAsset isKindOfClass:[NSURL class]]) {
avAsset = [[AVURLAsset alloc] initWithURL:(NSURL *)avAsset options:nil];
}
SQueue *queue = [[SQueue alloc] init];
return [[SSignal alloc] initWithGenerator:^id<SDisposable>(SSubscriber *subscriber)

View File

@ -94,7 +94,7 @@ final class PasscodeEntryControllerNode: ASDisplayNode {
if let strongSelf = self {
strongSelf.inputFieldNode.append(character)
if let gradientNode = strongSelf.backgroundCustomNode as? GradientBackgroundNode {
gradientNode.animateEvent(transition: .animated(duration: 0.55, curve: .spring))
gradientNode.animateEvent(transition: .animated(duration: 0.55, curve: .spring), extendAnimation: false, backwards: false, completion: {})
}
}
}
@ -102,7 +102,7 @@ final class PasscodeEntryControllerNode: ASDisplayNode {
if let strongSelf = self {
let _ = strongSelf.inputFieldNode.delete()
if let gradientNode = strongSelf.backgroundCustomNode as? GradientBackgroundNode {
gradientNode.animateEvent(transition: .animated(duration: 0.55, curve: .spring), backwards: true)
gradientNode.animateEvent(transition: .animated(duration: 0.55, curve: .spring), extendAnimation: false, backwards: true, completion: {})
}
}
}
@ -167,7 +167,7 @@ final class PasscodeEntryControllerNode: ASDisplayNode {
self.hapticFeedback.tap()
let result = self.inputFieldNode.delete()
if result, let gradientNode = self.backgroundCustomNode as? GradientBackgroundNode {
gradientNode.animateEvent(transition: .animated(duration: 0.55, curve: .spring), backwards: true)
gradientNode.animateEvent(transition: .animated(duration: 0.55, curve: .spring), extendAnimation: false, backwards: true, completion: {})
}
}
@ -337,7 +337,7 @@ final class PasscodeEntryControllerNode: ASDisplayNode {
if let gradientNode = self.backgroundCustomNode as? GradientBackgroundNode {
gradientNode.layer.animateAlpha(from: 0.0, to: 1.0, duration: 0.3)
self.backgroundDimNode.layer.animateAlpha(from: 0.0, to: 1.0, duration: 0.3)
gradientNode.animateEvent(transition: .animated(duration: 1.0, curve: .spring), extendAnimation: true)
gradientNode.animateEvent(transition: .animated(duration: 1.0, curve: .spring), extendAnimation: true, backwards: false, completion: {})
}
}
self.titleNode.setAttributedText(NSAttributedString(string: self.strings.EnterPasscode_EnterPasscode, font: titleFont, textColor: .white), animation: .none)
@ -355,7 +355,7 @@ final class PasscodeEntryControllerNode: ASDisplayNode {
self.backgroundImageNode.layer.animateAlpha(from: 0.0, to: 1.0, duration: 0.3)
if let gradientNode = self.backgroundCustomNode as? GradientBackgroundNode {
gradientNode.layer.animateAlpha(from: 0.0, to: 1.0, duration: 0.3)
gradientNode.animateEvent(transition: .animated(duration: 0.35, curve: .spring))
gradientNode.animateEvent(transition: .animated(duration: 0.35, curve: .spring), extendAnimation: false, backwards: false, completion: {})
self.backgroundDimNode.layer.animateAlpha(from: 0.0, to: 1.0, duration: 0.2)
}
if !iconFrame.isEmpty {
@ -385,7 +385,7 @@ final class PasscodeEntryControllerNode: ASDisplayNode {
self.subtitleNode.layer.animateAlpha(from: 0.0, to: 1.0, duration: 0.25)
if let gradientNode = self.backgroundCustomNode as? GradientBackgroundNode {
gradientNode.animateEvent(transition: .animated(duration: 1.0, curve: .spring))
gradientNode.animateEvent(transition: .animated(duration: 1.0, curve: .spring), extendAnimation: false, backwards: false, completion: {})
}
self.inputFieldNode.animateIn()
self.keyboardNode.animateIn()
@ -425,7 +425,7 @@ final class PasscodeEntryControllerNode: ASDisplayNode {
self.hapticFeedback.error()
if let gradientNode = self.backgroundCustomNode as? GradientBackgroundNode {
gradientNode.animateEvent(transition: .animated(duration: 1.5, curve: .spring), extendAnimation: true, backwards: true)
gradientNode.animateEvent(transition: .animated(duration: 1.5, curve: .spring), extendAnimation: true, backwards: true, completion: {})
}
}
@ -440,7 +440,7 @@ final class PasscodeEntryControllerNode: ASDisplayNode {
if let backgroundCustomNode = self.backgroundCustomNode {
transition.updateFrame(node: backgroundCustomNode, frame: bounds)
if let gradientBackgroundNode = backgroundCustomNode as? GradientBackgroundNode {
gradientBackgroundNode.updateLayout(size: bounds.size, transition: transition)
gradientBackgroundNode.updateLayout(size: bounds.size, transition: transition, extendAnimation: false, backwards: false, completion: {})
}
}
transition.updateFrame(view: self.effectView, frame: bounds)

View File

@ -178,7 +178,7 @@ private enum ChannelAdminsEntry: ItemListNodeEntry {
let arguments = arguments as! ChannelAdminsControllerArguments
switch self {
case let .recentActions(_, text):
return ItemListDisclosureItem(presentationData: presentationData, title: text, label: "", sectionId: self.section, style: .blocks, action: {
return ItemListDisclosureItem(presentationData: presentationData, icon: UIImage(bundleImageName: "Chat/Info/RecentActionsIcon"), title: text, label: "", sectionId: self.section, style: .blocks, action: {
arguments.openRecentActions()
})
case let .adminsHeader(_, title):

View File

@ -12,6 +12,7 @@ import AccountContext
import AlertUI
import PresentationDataUtils
import ItemListPeerItem
import ItemListPeerActionItem
import InviteLinksUI
private final class ChannelMembersControllerArguments {
@ -21,8 +22,9 @@ private final class ChannelMembersControllerArguments {
let setPeerIdWithRevealedOptions: (PeerId?, PeerId?) -> Void
let removePeer: (PeerId) -> Void
let openPeer: (Peer) -> Void
let inviteViaLink: ()->Void
init(context: AccountContext, addMember: @escaping () -> Void, setPeerIdWithRevealedOptions: @escaping (PeerId?, PeerId?) -> Void, removePeer: @escaping (PeerId) -> Void, openPeer: @escaping (Peer) -> Void, inviteViaLink: @escaping()->Void) {
let inviteViaLink: () -> Void
init(context: AccountContext, addMember: @escaping () -> Void, setPeerIdWithRevealedOptions: @escaping (PeerId?, PeerId?) -> Void, removePeer: @escaping (PeerId) -> Void, openPeer: @escaping (Peer) -> Void, inviteViaLink: @escaping () -> Void) {
self.context = context
self.addMember = addMember
self.setPeerIdWithRevealedOptions = setPeerIdWithRevealedOptions
@ -155,12 +157,12 @@ private enum ChannelMembersEntry: ItemListNodeEntry {
func item(presentationData: ItemListPresentationData, arguments: Any) -> ListViewItem {
let arguments = arguments as! ChannelMembersControllerArguments
switch self {
case let .addMember(_, text):
return ItemListActionItem(presentationData: presentationData, title: text, kind: .generic, alignment: .natural, sectionId: self.section, style: .blocks, action: {
case let .addMember(theme, text):
return ItemListPeerActionItem(presentationData: presentationData, icon: PresentationResourcesItemList.addPersonIcon(theme), title: text, alwaysPlain: false, sectionId: self.section, height: .generic, editing: false, action: {
arguments.addMember()
})
case let .inviteLink(_, text):
return ItemListActionItem(presentationData: presentationData, title: text, kind: .generic, alignment: .natural, sectionId: self.section, style: .blocks, action: {
case let .inviteLink(theme, text):
return ItemListPeerActionItem(presentationData: presentationData, icon: PresentationResourcesItemList.linkIcon(theme), title: text, alwaysPlain: false, sectionId: self.section, height: .generic, editing: false, action: {
arguments.inviteViaLink()
})
case let .addMemberInfo(_, text):

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,26 @@ 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*/
return false
}
private func add(_ entry: InternalGlobalMessageTagsEntry) -> Bool {
if self.entries.count == 0 {

View File

@ -61,6 +61,23 @@ 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
}*/
return false
}
func immutableView() -> PostboxView {
return HistoryTagInfoView(self)

View File

@ -37,6 +37,20 @@ 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
}*/
return false
}
func immutableView() -> PostboxView {
return InvalidatedMessageHistoryTagSummariesView(self)

View File

@ -40,6 +40,21 @@ 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
}*/
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,17 @@ 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
}*/
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,17 @@ 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
}*/
return false
}
func immutableView() -> PostboxView {
return PeerChatStateView(self)
@ -25,7 +36,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,17 @@ 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
}*/
return false
}
func immutableView() -> PostboxView {
return PeerNotificationSettingsBehaviorTimestampView(self)

View File

@ -36,6 +36,44 @@ 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
}*/
return false
}
func immutableView() -> PostboxView {
return PeerNotificationSettingsView(self)

View File

@ -26,6 +26,41 @@ 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
}*/
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

@ -272,7 +272,7 @@ public final class Transaction {
public func getPeerChatState(_ id: PeerId) -> PeerChatState? {
assert(!self.disposed)
return self.postbox?.peerChatStateTable.get(id) as? PeerChatState
return self.postbox?.peerChatStateTable.get(id)?.getLegacy() as? PeerChatState
}
public func setPeerChatState(_ id: PeerId, state: PeerChatState) {
@ -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)
}
@ -2715,7 +2715,7 @@ final class PostboxImpl {
let messages = self.getMessageGroup(at: id)
additionalDataEntries.append(.message(id, messages ?? []))
case let .peerChatState(peerId):
additionalDataEntries.append(.peerChatState(peerId, self.peerChatStateTable.get(peerId) as? PeerChatState))
additionalDataEntries.append(.peerChatState(peerId, self.peerChatStateTable.get(peerId)?.getLegacy() as? PeerChatState))
case .totalUnreadState:
additionalDataEntries.append(.totalUnreadState(self.messageHistoryMetadataTable.getTotalUnreadState(groupId: .root)))
case let .peerNotificationSettings(peerId):

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,22 @@ 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
}*/
return false
}
func immutableView() -> PostboxView {
return PreferencesView(self)

View File

@ -26,6 +26,17 @@ 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
}*/
return false
}
func immutableView() -> PostboxView {
return SynchronizeGroupMessageStatsView(self)

View File

@ -29,6 +29,42 @@ 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
}*/
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, forChatDisplay: false)
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, forChatDisplay: false)
}
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, forChatDisplay: false)
self.chatBackgroundNode.displaysAsynchronously = false
self.messagesContainerNode = ASDisplayNode()

View File

@ -174,7 +174,7 @@ final class SettingsThemeWallpaperNode: ASDisplayNode {
if let gradientNode = self.gradientNode {
gradientNode.frame = CGRect(origin: CGPoint(), size: size)
gradientNode.updateLayout(size: size, transition: .immediate)
gradientNode.updateLayout(size: size, transition: .immediate, extendAnimation: false, backwards: false, completion: {})
}
let progressDiameter: CGFloat = 50.0

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, forChatDisplay: false)
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, forChatDisplay: false)
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, forChatDisplay: false)
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, forChatDisplay: false)
}
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, forChatDisplay: false)
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, forChatDisplay: false)
self.instantChatBackgroundNode.displaysAsynchronously = false
self.messagesContainerNode = ASDisplayNode()

View File

@ -7,6 +7,6 @@
NSData * _Nullable prepareSvgImage(NSData * _Nonnull data);
UIImage * _Nullable renderPreparedImage(NSData * _Nonnull data, CGSize size);
UIImage * _Nullable drawSvgImage(NSData * _Nonnull data, CGSize size, UIColor * _Nullable backgroundColor, UIColor * _Nullable foregroundColor);
UIImage * _Nullable drawSvgImage(NSData * _Nonnull data, CGSize size, UIColor * _Nullable backgroundColor, UIColor * _Nullable foregroundColor, bool opaque);
#endif /* Lottie_h */

View File

@ -83,7 +83,7 @@ CGSize aspectFillSize(CGSize size, CGSize bounds) {
@end
UIImage * _Nullable drawSvgImage(NSData * _Nonnull data, CGSize size, UIColor *backgroundColor, UIColor *foregroundColor) {
UIImage * _Nullable drawSvgImage(NSData * _Nonnull data, CGSize size, UIColor *backgroundColor, UIColor *foregroundColor, bool opaque) {
NSDate *startTime = [NSDate date];
NSXMLParser *parser = [[NSXMLParser alloc] initWithData:data];
@ -115,8 +115,8 @@ UIImage * _Nullable drawSvgImage(NSData * _Nonnull data, CGSize size, UIColor *b
printf("parseTime = %f\n", deltaTime);
startTime = [NSDate date];
UIGraphicsBeginImageContextWithOptions(size, true, 1.0);
UIGraphicsBeginImageContextWithOptions(size, opaque, 1.0);
CGContextRef context = UIGraphicsGetCurrentContext();
CGContextSetFillColorWithColor(context, backgroundColor.CGColor);
CGContextFillRect(context, CGRectMake(0.0f, 0.0f, size.width, size.height));

View File

@ -2001,7 +2001,7 @@ private func pollChannel(network: Network, peer: Peer, state: AccountMutableStat
} else {
pollPts = 1
}
return (network.request(Api.functions.updates.getChannelDifference(flags: 0, channel: inputChannel, filter: .channelMessagesFilterEmpty, pts: pollPts, limit: limit))
return (network.request(Api.functions.updates.getChannelDifference(flags: 0, channel: inputChannel, filter: .channelMessagesFilterEmpty, pts: max(pollPts, 1), limit: limit))
|> map(Optional.init)
|> `catch` { error -> Signal<Api.updates.ChannelDifference?, MTRpcError> in
switch error.errorDescription {

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

@ -74,11 +74,11 @@ public struct Namespaces {
public static let cachedPollResults: Int8 = 9
public static let cachedContextResults: Int8 = 10
public static let proximityNotificationStoredState: Int8 = 11
public static let cachedPeerInvitationImporters: Int8 = 12
public static let cachedPeerExportedInvitations: Int8 = 13
public static let cachedGroupCallDisplayAsPeers: Int8 = 14
public static let cachedAdMessageStates: Int8 = 15
public static let cachedSendAsPeers: Int8 = 16
public static let cachedPeerInvitationImporters: Int8 = 16
public static let cachedPeerExportedInvitations: Int8 = 17
public static let cachedSendAsPeers: Int8 = 18
}
public struct UnorderedItemList {

View File

@ -285,18 +285,29 @@ public func currentPresentationDataAndSettings(accountManager: AccountManager<Te
let contactSettings: ContactSynchronizationSettings = transaction.getSharedData(ApplicationSpecificSharedDataKeys.contactSynchronizationSettings)?.get(ContactSynchronizationSettings.self) ?? ContactSynchronizationSettings.defaultSettings
let effectiveTheme: PresentationThemeReference
var preferredBaseTheme: TelegramBaseTheme?
let parameters = AutomaticThemeSwitchParameters(settings: themeSettings.automaticThemeSwitchSetting)
let autoNightModeTriggered: Bool
if automaticThemeShouldSwitchNow(parameters, systemUserInterfaceStyle: systemUserInterfaceStyle) {
effectiveTheme = themeSettings.automaticThemeSwitchSetting.theme
autoNightModeTriggered = true
if let baseTheme = themeSettings.themePreferredBaseTheme[effectiveTheme.index], [.night, .tinted].contains(baseTheme) {
preferredBaseTheme = baseTheme
} else {
preferredBaseTheme = .night
}
} else {
effectiveTheme = themeSettings.theme
autoNightModeTriggered = false
if let baseTheme = themeSettings.themePreferredBaseTheme[effectiveTheme.index], [.classic, .day].contains(baseTheme) {
preferredBaseTheme = baseTheme
}
}
let effectiveColors = themeSettings.themeSpecificAccentColors[effectiveTheme.index]
let theme = makePresentationTheme(mediaBox: accountManager.mediaBox, themeReference: effectiveTheme, accentColor: effectiveColors?.color, bubbleColors: effectiveColors?.customBubbleColors ?? [], baseColor: effectiveColors?.baseColor) ?? defaultPresentationTheme
let theme = makePresentationTheme(mediaBox: accountManager.mediaBox, themeReference: effectiveTheme, baseTheme: preferredBaseTheme, accentColor: effectiveColors?.color, bubbleColors: effectiveColors?.customBubbleColors ?? [], baseColor: effectiveColors?.baseColor) ?? defaultPresentationTheme
let effectiveChatWallpaper: TelegramWallpaper = (themeSettings.themeSpecificChatWallpapers[coloredThemeIndex(reference: effectiveTheme, accentColor: effectiveColors)] ?? themeSettings.themeSpecificChatWallpapers[effectiveTheme.index]) ?? theme.chat.defaultWallpaper

View File

@ -0,0 +1,12 @@
{
"images" : [
{
"filename" : "Icon-38.pdf",
"idiom" : "universal"
}
],
"info" : {
"author" : "xcode",
"version" : 1
}
}

View File

@ -0,0 +1,113 @@
%PDF-1.7
1 0 obj
<< >>
endobj
2 0 obj
<< /Length 3 0 R >>
stream
/DeviceRGB CS
/DeviceRGB cs
q
1.000000 0.000000 -0.000000 1.000000 0.000000 0.000000 cm
1.000000 0.584314 0.000000 scn
0.000000 18.799999 m
0.000000 22.720367 0.000000 24.680552 0.762954 26.177933 c
1.434068 27.495068 2.504932 28.565931 3.822066 29.237045 c
5.319448 30.000000 7.279633 30.000000 11.200000 30.000000 c
18.799999 30.000000 l
22.720367 30.000000 24.680552 30.000000 26.177933 29.237045 c
27.495068 28.565931 28.565931 27.495068 29.237045 26.177933 c
30.000000 24.680552 30.000000 22.720367 30.000000 18.799999 c
30.000000 11.200001 l
30.000000 7.279633 30.000000 5.319448 29.237045 3.822067 c
28.565931 2.504932 27.495068 1.434069 26.177933 0.762955 c
24.680552 0.000000 22.720367 0.000000 18.799999 0.000000 c
11.200000 0.000000 l
7.279633 0.000000 5.319448 0.000000 3.822066 0.762955 c
2.504932 1.434069 1.434068 2.504932 0.762954 3.822067 c
0.000000 5.319448 0.000000 7.279633 0.000000 11.200001 c
0.000000 18.799999 l
h
f
n
Q
q
1.000000 0.000000 -0.000000 1.000000 4.267334 8.000000 cm
1.000000 1.000000 1.000000 scn
10.732674 14.000000 m
5.052428 14.000000 1.569850 9.951639 0.290127 8.129647 c
-0.083405 7.597835 -0.096524 6.911736 0.252348 6.363431 c
1.487609 4.422029 4.937309 0.000000 10.732674 0.000000 c
16.528038 0.000000 19.977737 4.422029 21.212999 6.363431 c
21.561871 6.911736 21.548754 7.597834 21.175220 8.129646 c
19.895500 9.951638 16.412922 14.000000 10.732674 14.000000 c
h
15.732676 7.000000 m
15.732676 4.238577 13.494099 2.000000 10.732676 2.000000 c
7.971252 2.000000 5.732676 4.238577 5.732676 7.000000 c
5.732676 9.761423 7.971252 12.000000 10.732676 12.000000 c
13.494099 12.000000 15.732676 9.761423 15.732676 7.000000 c
h
10.732676 4.000000 m
12.389530 4.000000 13.732676 5.343145 13.732676 7.000000 c
13.732676 8.656855 12.389530 10.000000 10.732676 10.000000 c
10.644694 10.000000 10.557597 9.996212 10.471539 9.988792 c
10.637726 9.697166 10.732672 9.359671 10.732672 9.000002 c
10.732672 7.895432 9.837242 7.000001 8.732672 7.000001 c
8.373003 7.000001 8.035508 7.094942 7.743883 7.261129 c
7.736463 7.175073 7.732676 7.087979 7.732676 7.000000 c
7.732676 5.343145 9.075821 4.000000 10.732676 4.000000 c
h
f*
n
Q
endstream
endobj
3 0 obj
2162
endobj
4 0 obj
<< /Annots []
/Type /Page
/MediaBox [ 0.000000 0.000000 30.000000 30.000000 ]
/Resources 1 0 R
/Contents 2 0 R
/Parent 5 0 R
>>
endobj
5 0 obj
<< /Kids [ 4 0 R ]
/Count 1
/Type /Pages
>>
endobj
6 0 obj
<< /Type /Catalog
/Pages 5 0 R
>>
endobj
xref
0 7
0000000000 65535 f
0000000010 00000 n
0000000034 00000 n
0000002252 00000 n
0000002275 00000 n
0000002448 00000 n
0000002522 00000 n
trailer
<< /ID [ (some) (id) ]
/Root 6 0 R
/Size 7
>>
startxref
2581
%%EOF

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, forChatDisplay: true, useSharedAnimationPhase: useSharedAnimationPhase, useExperimentalImplementation: self.context.sharedContext.immediateExperimentalUISettings.experimentalBackground)
self.wallpaperReady.set(self.chatBackgroundNode.isReady)
var locationBroadcastPanelSource: LocationBroadcastPanelSource
@ -4998,8 +4998,17 @@ public final class ChatControllerImpl: TelegramBaseController, ChatController, G
var minOffsetForNavigation: CGFloat = 40.0
strongSelf.chatDisplayNode.historyNode.enumerateItemNodes { itemNode in
if let itemNode = itemNode as? ChatMessageBubbleItemNode {
if itemNode.item?.content.firstMessage.adAttribute != nil {
if let message = itemNode.item?.content.firstMessage, message.adAttribute != nil {
minOffsetForNavigation += itemNode.bounds.height
switch offset {
case let .known(offset):
if offset <= itemNode.bounds.height / 2.0 {
strongSelf.chatDisplayNode.historyNode.adSeenProcessingManager.add([message.id])
}
default:
break
}
}
}
return false

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

@ -469,7 +469,7 @@ public final class ChatHistoryListNode: ListView, ChatHistoryNode {
private let galleryHiddenMesageAndMediaDisposable = MetaDisposable()
private let messageProcessingManager = ChatMessageThrottledProcessingManager()
private let adSeenProcessingManager = ChatMessageThrottledProcessingManager()
let adSeenProcessingManager = ChatMessageThrottledProcessingManager()
private let seenLiveLocationProcessingManager = ChatMessageThrottledProcessingManager()
private let unsupportedMessageProcessingManager = ChatMessageThrottledProcessingManager()
private let refreshMediaProcessingManager = ChatMessageThrottledProcessingManager()
@ -611,7 +611,7 @@ public final class ChatHistoryListNode: ListView, ChatHistoryNode {
return
}
var adPeerId: PeerId?
adPeerId = nil
adPeerId = messages.first?.author?.id
if strongSelf.preloadAdPeerId != adPeerId {
strongSelf.preloadAdPeerId = adPeerId
@ -1563,7 +1563,6 @@ public final class ChatHistoryListNode: ListView, ChatHistoryNode {
let toLaterRange = (historyView.filteredEntries.count - 1 - (visible.firstIndex - 1), historyView.filteredEntries.count - 1)
var messageIdsWithViewCount: [MessageId] = []
var messageIdsWithAds: [MessageId] = []
var messageIdsWithLiveLocation: [MessageId] = []
var messageIdsWithUnsupportedMedia: [MessageId] = []
var messageIdsWithRefreshMedia: [MessageId] = []
@ -1589,8 +1588,6 @@ public final class ChatHistoryListNode: ListView, ChatHistoryNode {
if message.id.namespace == Namespaces.Message.Cloud {
messageIdsWithViewCount.append(message.id)
}
} else if attribute is AdMessageAttribute {
messageIdsWithAds.append(message.id)
} else if attribute is ReplyThreadMessageAttribute {
if message.id.namespace == Namespaces.Message.Cloud {
messageIdsWithViewCount.append(message.id)
@ -1754,9 +1751,6 @@ public final class ChatHistoryListNode: ListView, ChatHistoryNode {
if !messageIdsWithViewCount.isEmpty {
self.messageProcessingManager.add(messageIdsWithViewCount)
}
if !messageIdsWithAds.isEmpty {
self.adSeenProcessingManager.add(messageIdsWithAds)
}
if !messageIdsWithLiveLocation.isEmpty {
self.seenLiveLocationProcessingManager.add(messageIdsWithLiveLocation)
}

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

@ -3321,6 +3321,9 @@ class ChatMessageBubbleItemNode: ChatMessageItemView, ChatMessagePreviewItemNode
}
}
}
if message.adAttribute != nil {
canHaveSelection = false
}
default:
break
}

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, forChatDisplay: true)
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

@ -18,7 +18,7 @@ public struct ExperimentalUISettings: Codable, Equatable {
public var experimentalCompatibility: Bool
public var enableDebugDataDisplay: Bool
public var acceleratedStickers: Bool
public var mockICE: Bool
public var experimentalBackground: Bool
public static var defaultSettings: ExperimentalUISettings {
return ExperimentalUISettings(
@ -36,7 +36,7 @@ public struct ExperimentalUISettings: Codable, Equatable {
experimentalCompatibility: false,
enableDebugDataDisplay: false,
acceleratedStickers: false,
mockICE: false
experimentalBackground: false
)
}
@ -55,7 +55,7 @@ public struct ExperimentalUISettings: Codable, Equatable {
experimentalCompatibility: Bool,
enableDebugDataDisplay: Bool,
acceleratedStickers: Bool,
mockICE: Bool
experimentalBackground: Bool
) {
self.keepChatNavigationStack = keepChatNavigationStack
self.skipReadHistory = skipReadHistory
@ -71,7 +71,7 @@ public struct ExperimentalUISettings: Codable, Equatable {
self.experimentalCompatibility = experimentalCompatibility
self.enableDebugDataDisplay = enableDebugDataDisplay
self.acceleratedStickers = acceleratedStickers
self.mockICE = mockICE
self.experimentalBackground = experimentalBackground
}
public init(from decoder: Decoder) throws {
@ -91,7 +91,7 @@ public struct ExperimentalUISettings: Codable, Equatable {
self.experimentalCompatibility = (try container.decodeIfPresent(Int32.self, forKey: "experimentalCompatibility") ?? 0) != 0
self.enableDebugDataDisplay = (try container.decodeIfPresent(Int32.self, forKey: "enableDebugDataDisplay") ?? 0) != 0
self.acceleratedStickers = (try container.decodeIfPresent(Int32.self, forKey: "acceleratedStickers") ?? 0) != 0
self.mockICE = (try container.decodeIfPresent(Int32.self, forKey: "mockICE") ?? 0) != 0
self.experimentalBackground = (try container.decodeIfPresent(Int32.self, forKey: "experimentalBackground") ?? 0) != 0
}
public func encode(to encoder: Encoder) throws {
@ -111,7 +111,7 @@ public struct ExperimentalUISettings: Codable, Equatable {
try container.encode((self.experimentalCompatibility ? 1 : 0) as Int32, forKey: "experimentalCompatibility")
try container.encode((self.enableDebugDataDisplay ? 1 : 0) as Int32, forKey: "enableDebugDataDisplay")
try container.encode((self.acceleratedStickers ? 1 : 0) as Int32, forKey: "acceleratedStickers")
try container.encode((self.mockICE ? 1 : 0) as Int32, forKey: "mockICE")
try container.encode((self.experimentalBackground ? 1 : 0) as Int32, forKey: "experimentalBackground")
}
}

View File

@ -19,6 +19,9 @@ swift_library(
"//submodules/SSignalKit/SwiftSignalKit:SwiftSignalKit",
"//submodules/WallpaperResources:WallpaperResources",
"//submodules/FastBlur:FastBlur",
"//submodules/Svg:Svg",
"//submodules/GZip:GZip",
"//submodules/AppBundle:AppBundle",
],
visibility = [
"//visibility:public",

View File

@ -1272,7 +1272,7 @@ public func themeImage(account: Account, accountManager: AccountManager<Telegram
case let .pattern(data, colors, intensity):
let wallpaperImage = generateImage(arguments.drawingSize, rotatedContext: { size, context in
drawWallpaperGradientImage(colors.map(UIColor.init(rgb:)), context: context, size: size)
if let unpackedData = TGGUnzipData(data, 2 * 1024 * 1024), let image = drawSvgImage(unpackedData, arguments.drawingSize, .clear, .black) {
if let unpackedData = TGGUnzipData(data, 2 * 1024 * 1024), let image = drawSvgImage(unpackedData, arguments.drawingSize, .clear, .black, true) {
context.setBlendMode(.softLight)
context.setAlpha(abs(CGFloat(intensity)) / 100.0)
context.draw(image.cgImage!, in: CGRect(origin: CGPoint(), size: arguments.drawingSize))

View File

@ -1,5 +1,5 @@
{
"app": "8.2",
"app": "8.2.1",
"bazel": "4.0.0",
"xcode": "13.0"
}