mirror of
https://github.com/Swiftgram/Telegram-iOS.git
synced 2025-09-01 10:23:15 +00:00
Merge commit '1462366056b085c59b4ed5dbe88f33a86c7e96b8' into layer-135
This commit is contained in:
commit
7b7a4203c4
@ -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()
|
||||
|
@ -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 {
|
||||
|
@ -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
|
||||
}
|
||||
|
@ -22,6 +22,7 @@ swift_library(
|
||||
"//submodules/OverlayStatusController:OverlayStatusController",
|
||||
"//submodules/AccountContext:AccountContext",
|
||||
"//submodules/AppBundle:AppBundle",
|
||||
"//submodules/GZip:GZip"
|
||||
],
|
||||
visibility = [
|
||||
"//visibility:public",
|
||||
|
@ -13,6 +13,7 @@ import PresentationDataUtils
|
||||
import OverlayStatusController
|
||||
import AccountContext
|
||||
import AppBundle
|
||||
import GZip
|
||||
|
||||
@objc private final class DebugControllerMailComposeDelegate: NSObject, MFMailComposeViewControllerDelegate {
|
||||
public func mailComposeController(_ controller: MFMailComposeViewController, didFinishWith result: MFMailComposeResult, error: Error?) {
|
||||
@ -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))
|
||||
}
|
||||
|
@ -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
|
||||
}
|
||||
|
@ -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()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -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)))
|
||||
|
@ -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;
|
||||
|
@ -88,6 +88,11 @@
|
||||
return _cachedAVAsset;
|
||||
}
|
||||
|
||||
- (NSString *)uniformTypeIdentifier
|
||||
{
|
||||
return nil;
|
||||
}
|
||||
|
||||
- (SSignal *)avAsset {
|
||||
if (_originalAsset != nil) {
|
||||
if (_cachedAVAsset != nil) {
|
||||
|
@ -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];
|
||||
}
|
||||
}];
|
||||
}
|
||||
|
||||
|
@ -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)
|
||||
|
@ -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)
|
||||
|
@ -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):
|
||||
|
@ -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):
|
||||
|
@ -14,6 +14,10 @@ final class MutableAdditionalChatListItemsView: MutablePostboxView {
|
||||
}
|
||||
return false
|
||||
}
|
||||
|
||||
func refreshDueToExternalTransaction(postbox: PostboxImpl) -> Bool {
|
||||
return false
|
||||
}
|
||||
|
||||
func immutableView() -> PostboxView {
|
||||
return AdditionalChatListItemsView(self)
|
||||
|
@ -48,6 +48,10 @@ final class MutableAllChatListHolesView: MutablePostboxView {
|
||||
return false
|
||||
}
|
||||
}
|
||||
|
||||
func refreshDueToExternalTransaction(postbox: PostboxImpl) -> Bool {
|
||||
return false
|
||||
}
|
||||
|
||||
func immutableView() -> PostboxView {
|
||||
return AllChatListHolesView(self)
|
||||
|
@ -16,6 +16,10 @@ final class MutableCachedItemView: MutablePostboxView {
|
||||
}
|
||||
return false
|
||||
}
|
||||
|
||||
func refreshDueToExternalTransaction(postbox: PostboxImpl) -> Bool {
|
||||
return false
|
||||
}
|
||||
|
||||
func immutableView() -> PostboxView {
|
||||
return CachedItemView(self)
|
||||
|
@ -17,6 +17,10 @@ final class MutableCachedPeerDataView: MutablePostboxView {
|
||||
return false
|
||||
}
|
||||
}
|
||||
|
||||
func refreshDueToExternalTransaction(postbox: PostboxImpl) -> Bool {
|
||||
return false
|
||||
}
|
||||
|
||||
func immutableView() -> PostboxView {
|
||||
return CachedPeerDataView(self)
|
||||
|
@ -70,6 +70,10 @@ final class MutableContactPeersView: MutablePostboxView {
|
||||
return updated
|
||||
}
|
||||
|
||||
func refreshDueToExternalTransaction(postbox: PostboxImpl) -> Bool {
|
||||
return false
|
||||
}
|
||||
|
||||
func immutableView() -> PostboxView {
|
||||
return ContactPeersView(self)
|
||||
}
|
||||
|
@ -33,6 +33,10 @@ final class MutableDeletedMessagesView: MutablePostboxView {
|
||||
return updated
|
||||
}
|
||||
|
||||
func refreshDueToExternalTransaction(postbox: PostboxImpl) -> Bool {
|
||||
return false
|
||||
}
|
||||
|
||||
func immutableView() -> PostboxView {
|
||||
return DeletedMessagesView(self)
|
||||
}
|
||||
|
@ -202,6 +202,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 {
|
||||
|
@ -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)
|
||||
|
@ -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)
|
||||
|
@ -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)
|
||||
|
@ -48,6 +48,10 @@ final class MutableItemCollectionInfoView: MutablePostboxView {
|
||||
}
|
||||
return updated
|
||||
}
|
||||
|
||||
func refreshDueToExternalTransaction(postbox: PostboxImpl) -> Bool {
|
||||
return false
|
||||
}
|
||||
|
||||
func immutableView() -> PostboxView {
|
||||
return ItemCollectionInfoView(self)
|
||||
|
@ -90,12 +90,14 @@ final class MutableItemCollectionInfosView: MutablePostboxView {
|
||||
}
|
||||
return updated
|
||||
}
|
||||
|
||||
func refreshDueToExternalTransaction(postbox: PostboxImpl) -> Bool {
|
||||
return false
|
||||
}
|
||||
|
||||
func immutableView() -> PostboxView {
|
||||
return ItemCollectionInfosView(self)
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
|
||||
public final class ItemCollectionInfosView: PostboxView {
|
||||
|
@ -50,6 +50,10 @@ final class MutableLocalMessageTagsView: MutablePostboxView {
|
||||
}
|
||||
return updated
|
||||
}
|
||||
|
||||
func refreshDueToExternalTransaction(postbox: PostboxImpl) -> Bool {
|
||||
return false
|
||||
}
|
||||
|
||||
func immutableView() -> PostboxView {
|
||||
return LocalMessageTagsView(self)
|
||||
|
@ -25,6 +25,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)
|
||||
|
@ -739,7 +739,7 @@ final class MutableMessageHistoryView {
|
||||
}
|
||||
case let .peerChatState(peerId, _):
|
||||
if transaction.currentUpdatedPeerChatStates.contains(peerId) {
|
||||
updated[i] = .peerChatState(peerId, postbox.peerChatStateTable.get(peerId) as? PeerChatState)
|
||||
updated[i] = .peerChatState(peerId, postbox.peerChatStateTable.get(peerId)?.getLegacy() as? PeerChatState)
|
||||
hasChanges = true
|
||||
}
|
||||
case .totalUnreadState:
|
||||
|
@ -171,6 +171,10 @@ final class MutableMessageOfInterestHolesView: MutablePostboxView {
|
||||
return false
|
||||
}
|
||||
}
|
||||
|
||||
func refreshDueToExternalTransaction(postbox: PostboxImpl) -> Bool {
|
||||
return false
|
||||
}
|
||||
|
||||
func immutableView() -> PostboxView {
|
||||
return MessageOfInterestHolesView(self)
|
||||
|
@ -51,6 +51,10 @@ final class MutableMessagesView: MutablePostboxView {
|
||||
return false
|
||||
}
|
||||
}
|
||||
|
||||
func refreshDueToExternalTransaction(postbox: PostboxImpl) -> Bool {
|
||||
return false
|
||||
}
|
||||
|
||||
func immutableView() -> PostboxView {
|
||||
return MessagesView(self)
|
||||
|
@ -42,6 +42,10 @@ final class MutableBasicPeerView: MutablePostboxView {
|
||||
|
||||
return updated
|
||||
}
|
||||
|
||||
func refreshDueToExternalTransaction(postbox: PostboxImpl) -> Bool {
|
||||
return false
|
||||
}
|
||||
|
||||
func immutableView() -> PostboxView {
|
||||
return BasicPeerView(self)
|
||||
|
@ -22,6 +22,10 @@ final class MutablePeerChatInclusionView: MutablePostboxView {
|
||||
|
||||
return updated
|
||||
}
|
||||
|
||||
func refreshDueToExternalTransaction(postbox: PostboxImpl) -> Bool {
|
||||
return false
|
||||
}
|
||||
|
||||
func immutableView() -> PostboxView {
|
||||
return PeerChatInclusionView(self)
|
||||
|
@ -51,6 +51,10 @@ final class MutableOrderedItemListView: MutablePostboxView {
|
||||
|
||||
return updated
|
||||
}
|
||||
|
||||
func refreshDueToExternalTransaction(postbox: PostboxImpl) -> Bool {
|
||||
return false
|
||||
}
|
||||
|
||||
func immutableView() -> PostboxView {
|
||||
return OrderedItemListView(self)
|
||||
|
@ -5,7 +5,7 @@ final class PeerChatStateTable: Table {
|
||||
return ValueBoxTable(id: id, keyType: .int64, compactValuesOnCreation: false)
|
||||
}
|
||||
|
||||
private var cachedPeerChatStates: [PeerId: PostboxCoding?] = [:]
|
||||
private var cachedPeerChatStates: [PeerId: CodableEntry?] = [:]
|
||||
private var updatedPeerIds = Set<PeerId>()
|
||||
|
||||
private let sharedKey = ValueBoxKey(length: 8)
|
||||
@ -15,11 +15,12 @@ final class PeerChatStateTable: Table {
|
||||
return self.sharedKey
|
||||
}
|
||||
|
||||
func get(_ id: PeerId) -> PostboxCoding? {
|
||||
func get(_ id: PeerId) -> CodableEntry? {
|
||||
if let state = self.cachedPeerChatStates[id] {
|
||||
return state
|
||||
} else {
|
||||
if let value = self.valueBox.get(self.table, key: self.key(id)), let state = PostboxDecoder(buffer: value).decodeRootObject() {
|
||||
if let value = self.valueBox.get(self.table, key: self.key(id)) {
|
||||
let state = CodableEntry(data: value.makeData())
|
||||
self.cachedPeerChatStates[id] = state
|
||||
return state
|
||||
} else {
|
||||
@ -29,7 +30,7 @@ final class PeerChatStateTable: Table {
|
||||
}
|
||||
}
|
||||
|
||||
func set(_ id: PeerId, state: PostboxCoding?) {
|
||||
func set(_ id: PeerId, state: CodableEntry?) {
|
||||
self.cachedPeerChatStates[id] = state
|
||||
self.updatedPeerIds.insert(id)
|
||||
}
|
||||
@ -41,12 +42,9 @@ final class PeerChatStateTable: Table {
|
||||
|
||||
override func beforeCommit() {
|
||||
if !self.updatedPeerIds.isEmpty {
|
||||
let sharedEncoder = PostboxEncoder()
|
||||
for id in self.updatedPeerIds {
|
||||
if let wrappedState = self.cachedPeerChatStates[id], let state = wrappedState {
|
||||
sharedEncoder.reset()
|
||||
sharedEncoder.encodeRootObject(state)
|
||||
self.valueBox.set(self.table, key: self.key(id), value: sharedEncoder.readBufferNoCopy())
|
||||
self.valueBox.set(self.table, key: self.key(id), value: ReadBuffer(data: state.data))
|
||||
} else {
|
||||
self.valueBox.remove(self.table, key: self.key(id), secure: false)
|
||||
}
|
||||
|
@ -2,7 +2,7 @@ import Foundation
|
||||
|
||||
final class MutablePeerChatStateView: MutablePostboxView {
|
||||
let peerId: PeerId
|
||||
var chatState: PostboxCoding?
|
||||
var chatState: CodableEntry?
|
||||
|
||||
init(postbox: PostboxImpl, peerId: PeerId) {
|
||||
self.peerId = peerId
|
||||
@ -17,6 +17,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
|
||||
|
@ -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)
|
||||
|
@ -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)
|
||||
|
@ -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)
|
||||
|
@ -253,6 +253,10 @@ final class MutablePeerView: MutablePostboxView {
|
||||
|
||||
return updated
|
||||
}
|
||||
|
||||
func refreshDueToExternalTransaction(postbox: PostboxImpl) -> Bool {
|
||||
return false
|
||||
}
|
||||
|
||||
func immutableView() -> PostboxView {
|
||||
return PeerView(self)
|
||||
|
@ -16,6 +16,10 @@ final class MutablePendingMessageActionsSummaryView: MutablePostboxView {
|
||||
}
|
||||
return updated
|
||||
}
|
||||
|
||||
func refreshDueToExternalTransaction(postbox: PostboxImpl) -> Bool {
|
||||
return false
|
||||
}
|
||||
|
||||
func immutableView() -> PostboxView {
|
||||
return PendingMessageActionsSummaryView(self)
|
||||
|
@ -38,6 +38,10 @@ final class MutablePendingMessageActionsView: MutablePostboxView {
|
||||
}
|
||||
return updated
|
||||
}
|
||||
|
||||
func refreshDueToExternalTransaction(postbox: PostboxImpl) -> Bool {
|
||||
return false
|
||||
}
|
||||
|
||||
func immutableView() -> PostboxView {
|
||||
return PendingMessageActionsView(self)
|
||||
|
@ -25,6 +25,10 @@ final class MutablePendingPeerNotificationSettingsView: MutablePostboxView {
|
||||
}
|
||||
return updated
|
||||
}
|
||||
|
||||
func refreshDueToExternalTransaction(postbox: PostboxImpl) -> Bool {
|
||||
return false
|
||||
}
|
||||
|
||||
func immutableView() -> PostboxView {
|
||||
return PendingPeerNotificationSettingsView(self)
|
||||
|
@ -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):
|
||||
|
@ -5,6 +5,7 @@ public protocol PostboxView {
|
||||
|
||||
protocol MutablePostboxView {
|
||||
func replay(postbox: PostboxImpl, transaction: PostboxTransaction) -> Bool
|
||||
func refreshDueToExternalTransaction(postbox: PostboxImpl) -> Bool
|
||||
func immutableView() -> PostboxView
|
||||
}
|
||||
|
||||
@ -24,6 +25,16 @@ final class CombinedMutableView {
|
||||
}
|
||||
return updated
|
||||
}
|
||||
|
||||
func refreshDueToExternalTransaction(postbox: PostboxImpl) -> Bool {
|
||||
var updated = false
|
||||
for (_, view) in self.views {
|
||||
if view.refreshDueToExternalTransaction(postbox: postbox) {
|
||||
updated = true
|
||||
}
|
||||
}
|
||||
return updated
|
||||
}
|
||||
|
||||
func immutableView() -> CombinedView {
|
||||
var result: [PostboxViewKey: PostboxView] = [:]
|
||||
|
@ -13,11 +13,37 @@ public final class CodableEntry: Equatable {
|
||||
self.data = encoder.makeData()
|
||||
}
|
||||
|
||||
public init(legacyValue: PostboxCoding) {
|
||||
let encoder = PostboxEncoder()
|
||||
encoder.encodeRootObject(legacyValue)
|
||||
self.data = encoder.makeData()
|
||||
}
|
||||
|
||||
public func get<T: Decodable>(_ type: T.Type) -> T? {
|
||||
let decoder = PostboxDecoder(buffer: MemoryBuffer(data: self.data))
|
||||
return decoder.decode(T.self, forKey: "_")
|
||||
}
|
||||
|
||||
public func getLegacy<T: PostboxCoding>(_ type: T.Type) -> T? {
|
||||
let decoder = PostboxDecoder(buffer: MemoryBuffer(data: self.data))
|
||||
let object = decoder.decodeRootObject()
|
||||
if let object = object as? T {
|
||||
return object
|
||||
} else {
|
||||
return nil
|
||||
}
|
||||
}
|
||||
|
||||
public func getLegacy() -> PostboxCoding? {
|
||||
let decoder = PostboxDecoder(buffer: MemoryBuffer(data: self.data))
|
||||
let object = decoder.decodeRootObject()
|
||||
if let object = object {
|
||||
return object
|
||||
} else {
|
||||
return nil
|
||||
}
|
||||
}
|
||||
|
||||
public static func ==(lhs: CodableEntry, rhs: CodableEntry) -> Bool {
|
||||
return lhs.data == rhs.data
|
||||
}
|
||||
|
@ -44,6 +44,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)
|
||||
|
@ -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)
|
||||
|
@ -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)
|
||||
|
@ -6,10 +6,42 @@ public enum UnreadMessageCountsItem: Equatable {
|
||||
case peer(PeerId)
|
||||
}
|
||||
|
||||
private enum MutableUnreadMessageCountsItemEntry {
|
||||
private enum MutableUnreadMessageCountsItemEntry: Equatable {
|
||||
case total((ValueBoxKey, PreferencesEntry?)?, ChatListTotalUnreadState)
|
||||
case totalInGroup(PeerGroupId, ChatListTotalUnreadState)
|
||||
case peer(PeerId, CombinedPeerReadState?)
|
||||
|
||||
static func ==(lhs: MutableUnreadMessageCountsItemEntry, rhs: MutableUnreadMessageCountsItemEntry) -> Bool {
|
||||
switch lhs {
|
||||
case let .total(lhsKeyAndEntry, lhsUnreadState):
|
||||
if case let .total(rhsKeyAndEntry, rhsUnreadState) = rhs {
|
||||
if lhsKeyAndEntry?.0 != rhsKeyAndEntry?.0 {
|
||||
return false
|
||||
}
|
||||
if lhsKeyAndEntry?.1 != rhsKeyAndEntry?.1 {
|
||||
return false
|
||||
}
|
||||
if lhsUnreadState != rhsUnreadState {
|
||||
return false
|
||||
}
|
||||
return true
|
||||
} else {
|
||||
return false
|
||||
}
|
||||
case let .totalInGroup(groupId, state):
|
||||
if case .totalInGroup(groupId, state) = rhs {
|
||||
return true
|
||||
} else {
|
||||
return false
|
||||
}
|
||||
case let .peer(peerId, readState):
|
||||
if case .peer(peerId, readState) = rhs {
|
||||
return true
|
||||
} else {
|
||||
return false
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public enum UnreadMessageCountsItemEntry {
|
||||
@ -19,9 +51,12 @@ public enum UnreadMessageCountsItemEntry {
|
||||
}
|
||||
|
||||
final class MutableUnreadMessageCountsView: MutablePostboxView {
|
||||
private let items: [UnreadMessageCountsItem]
|
||||
fileprivate var entries: [MutableUnreadMessageCountsItemEntry]
|
||||
|
||||
init(postbox: PostboxImpl, items: [UnreadMessageCountsItem]) {
|
||||
self.items = items
|
||||
|
||||
self.entries = items.map { item in
|
||||
switch item {
|
||||
case let .total(preferencesKey):
|
||||
@ -80,6 +115,25 @@ final class MutableUnreadMessageCountsView: MutablePostboxView {
|
||||
|
||||
return updated
|
||||
}
|
||||
|
||||
func refreshDueToExternalTransaction(postbox: PostboxImpl) -> Bool {
|
||||
let entries: [MutableUnreadMessageCountsItemEntry] = self.items.map { item -> MutableUnreadMessageCountsItemEntry in
|
||||
switch item {
|
||||
case let .total(preferencesKey):
|
||||
return .total(preferencesKey.flatMap({ ($0, postbox.preferencesTable.get(key: $0)) }), postbox.messageHistoryMetadataTable.getTotalUnreadState(groupId: .root))
|
||||
case let .totalInGroup(groupId):
|
||||
return .totalInGroup(groupId, postbox.messageHistoryMetadataTable.getTotalUnreadState(groupId: groupId))
|
||||
case let .peer(peerId):
|
||||
return .peer(peerId, postbox.readStateTable.getCombinedState(peerId))
|
||||
}
|
||||
}
|
||||
if self.entries != entries {
|
||||
self.entries = entries
|
||||
return true
|
||||
} else {
|
||||
return false
|
||||
}
|
||||
}
|
||||
|
||||
func immutableView() -> PostboxView {
|
||||
return UnreadMessageCountsView(self)
|
||||
@ -151,6 +205,16 @@ final class MutableCombinedReadStateView: MutablePostboxView {
|
||||
|
||||
return updated
|
||||
}
|
||||
|
||||
func refreshDueToExternalTransaction(postbox: PostboxImpl) -> Bool {
|
||||
let state = postbox.readStateTable.getCombinedState(self.peerId)
|
||||
if state != self.state {
|
||||
self.state = state
|
||||
return true
|
||||
} else {
|
||||
return false
|
||||
}
|
||||
}
|
||||
|
||||
func immutableView() -> PostboxView {
|
||||
return CombinedReadStateView(self)
|
||||
|
@ -62,7 +62,7 @@ private final class BubbleSettingsControllerNode: ASDisplayNode, UIScrollViewDel
|
||||
|
||||
self.scrollNode = ASScrollNode()
|
||||
|
||||
self.chatBackgroundNode = WallpaperBackgroundNode(context: context)
|
||||
self.chatBackgroundNode = createWallpaperBackgroundNode(context: context, forChatDisplay: false)
|
||||
self.chatBackgroundNode.displaysAsynchronously = false
|
||||
|
||||
self.messagesContainerNode = ASDisplayNode()
|
||||
|
@ -132,7 +132,7 @@ class ForwardPrivacyChatPreviewItemNode: ListViewItemNode {
|
||||
|
||||
return { item, params, neighbors in
|
||||
if currentBackgroundNode == nil {
|
||||
currentBackgroundNode = WallpaperBackgroundNode(context: item.context)
|
||||
currentBackgroundNode = createWallpaperBackgroundNode(context: item.context, forChatDisplay: false)
|
||||
}
|
||||
currentBackgroundNode?.update(wallpaper: item.wallpaper)
|
||||
currentBackgroundNode?.updateBubbleTheme(bubbleTheme: item.theme, bubbleCorners: item.chatBubbleCorners)
|
||||
|
@ -74,7 +74,7 @@ private final class TextSizeSelectionControllerNode: ASDisplayNode, UIScrollView
|
||||
self.pageControlNode = PageControlNode(dotSpacing: 7.0, dotColor: .white, inactiveDotColor: UIColor.white.withAlphaComponent(0.4))
|
||||
|
||||
self.chatListBackgroundNode = ASDisplayNode()
|
||||
self.chatBackgroundNode = WallpaperBackgroundNode(context: context)
|
||||
self.chatBackgroundNode = createWallpaperBackgroundNode(context: context, forChatDisplay: false)
|
||||
self.chatBackgroundNode.displaysAsynchronously = false
|
||||
|
||||
self.messagesContainerNode = ASDisplayNode()
|
||||
|
@ -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
|
||||
|
@ -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
|
||||
|
@ -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)
|
||||
|
||||
|
@ -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)
|
||||
|
@ -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)
|
||||
|
@ -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()
|
||||
|
@ -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 */
|
||||
|
@ -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));
|
||||
|
@ -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 {
|
||||
|
@ -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)
|
||||
}
|
||||
|
@ -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 {
|
||||
|
@ -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
|
||||
|
12
submodules/TelegramUI/Images.xcassets/Chat/Info/RecentActionsIcon.imageset/Contents.json
vendored
Normal file
12
submodules/TelegramUI/Images.xcassets/Chat/Info/RecentActionsIcon.imageset/Contents.json
vendored
Normal file
@ -0,0 +1,12 @@
|
||||
{
|
||||
"images" : [
|
||||
{
|
||||
"filename" : "Icon-38.pdf",
|
||||
"idiom" : "universal"
|
||||
}
|
||||
],
|
||||
"info" : {
|
||||
"author" : "xcode",
|
||||
"version" : 1
|
||||
}
|
||||
}
|
113
submodules/TelegramUI/Images.xcassets/Chat/Info/RecentActionsIcon.imageset/Icon-38.pdf
vendored
Normal file
113
submodules/TelegramUI/Images.xcassets/Chat/Info/RecentActionsIcon.imageset/Icon-38.pdf
vendored
Normal 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
|
@ -1174,17 +1174,7 @@ private func extractAccountManagerState(records: AccountRecordsView<TelegramAcco
|
||||
self.pushRegistry = pushRegistry
|
||||
pushRegistry.delegate = self
|
||||
|
||||
self.badgeDisposable.set((self.context.get()
|
||||
|> mapToSignal { context -> Signal<Int32, NoError> in
|
||||
if let context = context {
|
||||
return context.applicationBadge
|
||||
} else {
|
||||
return .single(0)
|
||||
}
|
||||
}
|
||||
|> deliverOnMainQueue).start(next: { count in
|
||||
UIApplication.shared.applicationIconBadgeNumber = Int(count)
|
||||
}))
|
||||
self.resetBadge()
|
||||
|
||||
if #available(iOS 9.1, *) {
|
||||
self.quickActionsDisposable.set((self.context.get()
|
||||
@ -1281,6 +1271,20 @@ private func extractAccountManagerState(records: AccountRecordsView<TelegramAcco
|
||||
return true
|
||||
}
|
||||
|
||||
private func resetBadge() {
|
||||
self.badgeDisposable.set((self.context.get()
|
||||
|> mapToSignal { context -> Signal<Int32, NoError> in
|
||||
if let context = context {
|
||||
return context.applicationBadge
|
||||
} else {
|
||||
return .single(0)
|
||||
}
|
||||
}
|
||||
|> deliverOnMainQueue).start(next: { count in
|
||||
UIApplication.shared.applicationIconBadgeNumber = Int(count)
|
||||
}))
|
||||
}
|
||||
|
||||
func applicationWillResignActive(_ application: UIApplication) {
|
||||
self.isActiveValue = false
|
||||
self.isActivePromise.set(false)
|
||||
@ -1361,6 +1365,8 @@ private func extractAccountManagerState(records: AccountRecordsView<TelegramAcco
|
||||
self.isInForegroundPromise.set(true)
|
||||
self.isActiveValue = true
|
||||
self.isActivePromise.set(true)
|
||||
|
||||
self.resetBadge()
|
||||
|
||||
self.maybeCheckForUpdates()
|
||||
}
|
||||
|
@ -508,7 +508,7 @@ public final class ChatControllerImpl: TelegramBaseController, ChatController, G
|
||||
default:
|
||||
break
|
||||
}
|
||||
self.chatBackgroundNode = WallpaperBackgroundNode(context: context, useSharedAnimationPhase: useSharedAnimationPhase)
|
||||
self.chatBackgroundNode = createWallpaperBackgroundNode(context: context, 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
|
||||
|
@ -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)
|
||||
}
|
||||
|
@ -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)
|
||||
}
|
||||
|
@ -24,7 +24,7 @@ private func attributedServiceMessageString(theme: ChatPresentationThemeData, st
|
||||
|
||||
class ChatMessageActionBubbleContentNode: ChatMessageBubbleContentNode {
|
||||
let labelNode: TextNode
|
||||
var backgroundNode: WallpaperBackgroundNode.BubbleBackgroundNode?
|
||||
var backgroundNode: WallpaperBubbleBackgroundNode?
|
||||
var backgroundColorNode: ASDisplayNode
|
||||
let backgroundMaskNode: ASImageNode
|
||||
var linkHighlightingNode: LinkHighlightingNode?
|
||||
@ -327,7 +327,7 @@ class ChatMessageActionBubbleContentNode: ChatMessageBubbleContentNode {
|
||||
var backgroundFrame = backgroundNode.frame
|
||||
backgroundFrame.origin.x += rect.minX
|
||||
backgroundFrame.origin.y += rect.minY
|
||||
backgroundNode.update(rect: backgroundFrame, within: containerSize)
|
||||
backgroundNode.update(rect: backgroundFrame, within: containerSize, transition: .immediate)
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -165,7 +165,7 @@ class ChatMessageAnimatedStickerItemNode: ChatMessageItemView {
|
||||
private let containerNode: ContextControllerSourceNode
|
||||
let imageNode: TransformImageNode
|
||||
private var enableSynchronousImageApply: Bool = false
|
||||
private var backgroundNode: WallpaperBackgroundNode.BubbleBackgroundNode?
|
||||
private var backgroundNode: WallpaperBubbleBackgroundNode?
|
||||
private(set) var placeholderNode: StickerShimmerEffectNode
|
||||
private(set) var animationNode: GenericAnimatedStickerNode?
|
||||
private var animationSize: CGSize?
|
||||
@ -650,7 +650,7 @@ class ChatMessageAnimatedStickerItemNode: ChatMessageItemView {
|
||||
self.placeholderNode.updateAbsoluteRect(CGRect(origin: CGPoint(x: rect.minX + self.placeholderNode.frame.minX, y: rect.minY + self.placeholderNode.frame.minY), size: self.placeholderNode.frame.size), within: containerSize)
|
||||
|
||||
if let backgroundNode = self.backgroundNode {
|
||||
backgroundNode.update(rect: CGRect(origin: CGPoint(x: rect.minX + self.placeholderNode.frame.minX, y: rect.minY + self.placeholderNode.frame.minY), size: self.placeholderNode.frame.size), within: containerSize)
|
||||
backgroundNode.update(rect: CGRect(origin: CGPoint(x: rect.minX + self.placeholderNode.frame.minX, y: rect.minY + self.placeholderNode.frame.minY), size: self.placeholderNode.frame.size), within: containerSize, transition: .immediate)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -55,7 +55,7 @@ func bubbleMaskForType(_ type: ChatMessageBackgroundType, graphics: PrincipalThe
|
||||
}
|
||||
|
||||
final class ChatMessageBubbleBackdrop: ASDisplayNode {
|
||||
private var backgroundContent: WallpaperBackgroundNode.BubbleBackgroundNode?
|
||||
private var backgroundContent: WallpaperBubbleBackgroundNode?
|
||||
|
||||
private var currentType: ChatMessageBackgroundType?
|
||||
private var currentMaskMode: Bool?
|
||||
@ -86,7 +86,7 @@ final class ChatMessageBubbleBackdrop: ASDisplayNode {
|
||||
var backgroundFrame = backgroundContent.frame
|
||||
backgroundFrame.origin.x += rect.minX
|
||||
backgroundFrame.origin.y += rect.minY
|
||||
backgroundContent.update(rect: backgroundFrame, within: containerSize)
|
||||
backgroundContent.update(rect: backgroundFrame, within: containerSize, transition: .immediate)
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -142,7 +142,7 @@ final class ChatMessageBubbleBackdrop: ASDisplayNode {
|
||||
var backgroundFrame = backgroundContent.frame
|
||||
backgroundFrame.origin.x += rect.minX
|
||||
backgroundFrame.origin.y += rect.minY
|
||||
backgroundContent.update(rect: backgroundFrame, within: containerSize)
|
||||
backgroundContent.update(rect: backgroundFrame, within: containerSize, transition: .immediate)
|
||||
}
|
||||
}
|
||||
|
||||
@ -162,7 +162,7 @@ final class ChatMessageBubbleBackdrop: ASDisplayNode {
|
||||
var backgroundFrame = backgroundContent.frame
|
||||
backgroundFrame.origin.x += rect.minX
|
||||
backgroundFrame.origin.y += rect.minY
|
||||
backgroundContent.update(rect: backgroundFrame, within: containerSize)
|
||||
backgroundContent.update(rect: backgroundFrame, within: containerSize, transition: .immediate)
|
||||
}
|
||||
self.backgroundContent = backgroundContent
|
||||
self.insertSubnode(backgroundContent, at: 0)
|
||||
@ -174,7 +174,7 @@ final class ChatMessageBubbleBackdrop: ASDisplayNode {
|
||||
var backgroundFrame = backgroundContent.frame
|
||||
backgroundFrame.origin.x += rect.minX
|
||||
backgroundFrame.origin.y += rect.minY
|
||||
backgroundContent.update(rect: backgroundFrame, within: containerSize)
|
||||
backgroundContent.update(rect: backgroundFrame, within: containerSize, transition: .immediate)
|
||||
}
|
||||
self.backgroundContent = backgroundContent
|
||||
self.insertSubnode(backgroundContent, at: 0)
|
||||
|
@ -3321,6 +3321,9 @@ class ChatMessageBubbleItemNode: ChatMessageItemView, ChatMessagePreviewItemNode
|
||||
}
|
||||
}
|
||||
}
|
||||
if message.adAttribute != nil {
|
||||
canHaveSelection = false
|
||||
}
|
||||
default:
|
||||
break
|
||||
}
|
||||
|
@ -22,7 +22,7 @@ class ChatMessageStickerItemNode: ChatMessageItemView {
|
||||
let contextSourceNode: ContextExtractedContentContainingNode
|
||||
private let containerNode: ContextControllerSourceNode
|
||||
let imageNode: TransformImageNode
|
||||
private var backgroundNode: WallpaperBackgroundNode.BubbleBackgroundNode?
|
||||
private var backgroundNode: WallpaperBubbleBackgroundNode?
|
||||
private var placeholderNode: StickerShimmerEffectNode
|
||||
var textNode: TextNode?
|
||||
|
||||
@ -250,7 +250,7 @@ class ChatMessageStickerItemNode: ChatMessageItemView {
|
||||
self.placeholderNode.updateAbsoluteRect(CGRect(origin: CGPoint(x: rect.minX + placeholderNode.frame.minX, y: rect.minY + placeholderNode.frame.minY), size: placeholderNode.frame.size), within: containerSize)
|
||||
|
||||
if let backgroundNode = self.backgroundNode {
|
||||
backgroundNode.update(rect: CGRect(origin: CGPoint(x: rect.minX + self.placeholderNode.frame.minX, y: rect.minY + self.placeholderNode.frame.minY), size: self.placeholderNode.frame.size), within: containerSize)
|
||||
backgroundNode.update(rect: CGRect(origin: CGPoint(x: rect.minX + self.placeholderNode.frame.minX, y: rect.minY + self.placeholderNode.frame.minY), size: self.placeholderNode.frame.size), within: containerSize, transition: .immediate)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -353,7 +353,7 @@ final class BadgeComponent: CombinedComponent {
|
||||
if lhs.withinSize != rhs.withinSize {
|
||||
return false
|
||||
}
|
||||
if lhs.wallpaperNode != rhs.wallpaperNode {
|
||||
if lhs.wallpaperNode !== rhs.wallpaperNode {
|
||||
return false
|
||||
}
|
||||
return true
|
||||
@ -564,7 +564,7 @@ final class AvatarComponent: Component {
|
||||
}
|
||||
|
||||
private final class WallpaperBlurNode: ASDisplayNode {
|
||||
private var backgroundNode: WallpaperBackgroundNode.BubbleBackgroundNode?
|
||||
private var backgroundNode: WallpaperBubbleBackgroundNode?
|
||||
private let colorNode: ASDisplayNode
|
||||
|
||||
override init() {
|
||||
|
@ -99,7 +99,7 @@ final class ChatRecentActionsControllerNode: ViewControllerTracingNode {
|
||||
|
||||
self.automaticMediaDownloadSettings = context.sharedContext.currentAutomaticMediaDownloadSettings.with { $0 }
|
||||
|
||||
self.backgroundNode = WallpaperBackgroundNode(context: context)
|
||||
self.backgroundNode = createWallpaperBackgroundNode(context: context, forChatDisplay: true)
|
||||
self.backgroundNode.isUserInteractionEnabled = false
|
||||
|
||||
self.panelBackgroundNode = NavigationBackgroundNode(color: self.presentationData.theme.chat.inputPanel.panelBackgroundColor)
|
||||
|
@ -64,7 +64,7 @@ class ChatReplyCountItem: ListViewItem {
|
||||
class ChatReplyCountItemNode: ListViewItemNode {
|
||||
var item: ChatReplyCountItem?
|
||||
private let labelNode: TextNode
|
||||
private var backgroundNode: WallpaperBackgroundNode.BubbleBackgroundNode?
|
||||
private var backgroundNode: WallpaperBubbleBackgroundNode?
|
||||
private let backgroundColorNode: ASDisplayNode
|
||||
|
||||
private var theme: ChatPresentationThemeData?
|
||||
@ -201,7 +201,7 @@ class ChatReplyCountItemNode: ListViewItemNode {
|
||||
backgroundFrame.origin.x += rect.minX
|
||||
backgroundFrame.origin.y += rect.minY
|
||||
|
||||
backgroundNode.update(rect: backgroundFrame, within: containerSize)
|
||||
backgroundNode.update(rect: backgroundFrame, within: containerSize, transition: .immediate)
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -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")
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -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",
|
||||
|
File diff suppressed because it is too large
Load Diff
@ -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))
|
||||
|
@ -1,5 +1,5 @@
|
||||
{
|
||||
"app": "8.2",
|
||||
"app": "8.2.1",
|
||||
"bazel": "4.0.0",
|
||||
"xcode": "13.0"
|
||||
}
|
||||
|
Loading…
x
Reference in New Issue
Block a user