mirror of
https://github.com/Swiftgram/Telegram-iOS.git
synced 2025-11-27 10:32:37 +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, *)
|
@available(iOSApplicationExtension 10.0, iOS 10.0, *)
|
||||||
private struct NotificationContent {
|
private struct NotificationContent: CustomStringConvertible {
|
||||||
var title: String?
|
var title: String?
|
||||||
var subtitle: String?
|
var subtitle: String?
|
||||||
var body: String?
|
var body: String?
|
||||||
@ -438,6 +438,21 @@ private struct NotificationContent {
|
|||||||
var senderPerson: INPerson?
|
var senderPerson: INPerson?
|
||||||
var senderImage: INImage?
|
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) {
|
mutating func addSenderInfo(mediaBox: MediaBox, accountPeerId: PeerId, peer: Peer) {
|
||||||
if #available(iOS 15.0, *) {
|
if #available(iOS 15.0, *) {
|
||||||
let image = peerAvatar(mediaBox: mediaBox, accountPeerId: accountPeerId, peer: peer)
|
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]) {
|
init?(queue: Queue, updateCurrentContent: @escaping (NotificationContent) -> Void, completed: @escaping () -> Void, payload: [AnyHashable: Any]) {
|
||||||
self.queue = queue
|
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 {
|
guard let appBundleIdentifier = Bundle.main.bundleIdentifier, let lastDotRange = appBundleIdentifier.range(of: ".", options: [.backwards]) else {
|
||||||
return nil
|
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)
|
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 {
|
guard var encryptedPayload = payload["p"] as? String else {
|
||||||
|
Logger.shared.log("NotificationService \(episode)", "Invalid payload 1")
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
encryptedPayload = encryptedPayload.replacingOccurrences(of: "-", with: "+")
|
encryptedPayload = encryptedPayload.replacingOccurrences(of: "-", with: "+")
|
||||||
@ -595,12 +615,16 @@ private final class NotificationServiceHandler {
|
|||||||
encryptedPayload.append("=")
|
encryptedPayload.append("=")
|
||||||
}
|
}
|
||||||
guard let payloadData = Data(base64Encoded: encryptedPayload) else {
|
guard let payloadData = Data(base64Encoded: encryptedPayload) else {
|
||||||
|
Logger.shared.log("NotificationService \(episode)", "Invalid payload 2")
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
let _ = (self.accountManager.accountRecords()
|
let _ = (combineLatest(queue: self.queue,
|
||||||
|
self.accountManager.accountRecords(),
|
||||||
|
self.accountManager.sharedData(keys: [ApplicationSpecificSharedDataKeys.inAppNotificationSettings])
|
||||||
|
)
|
||||||
|> take(1)
|
|> 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 recordId: AccountRecordId?
|
||||||
var isCurrentAccount: Bool = false
|
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 {
|
guard let strongSelf = self, let recordId = recordId else {
|
||||||
|
Logger.shared.log("NotificationService \(episode)", "Couldn't find a matching decryption key")
|
||||||
|
|
||||||
let content = NotificationContent()
|
let content = NotificationContent()
|
||||||
updateCurrentContent(content)
|
updateCurrentContent(content)
|
||||||
completed()
|
completed()
|
||||||
@ -641,6 +669,8 @@ private final class NotificationServiceHandler {
|
|||||||
return
|
return
|
||||||
}
|
}
|
||||||
guard let stateManager = stateManager else {
|
guard let stateManager = stateManager else {
|
||||||
|
Logger.shared.log("NotificationService \(episode)", "Didn't receive stateManager")
|
||||||
|
|
||||||
let content = NotificationContent()
|
let content = NotificationContent()
|
||||||
updateCurrentContent(content)
|
updateCurrentContent(content)
|
||||||
completed()
|
completed()
|
||||||
@ -658,6 +688,8 @@ private final class NotificationServiceHandler {
|
|||||||
return
|
return
|
||||||
}
|
}
|
||||||
guard let notificationsKey = notificationsKey else {
|
guard let notificationsKey = notificationsKey else {
|
||||||
|
Logger.shared.log("NotificationService \(episode)", "Didn't receive decryption key")
|
||||||
|
|
||||||
let content = NotificationContent()
|
let content = NotificationContent()
|
||||||
updateCurrentContent(content)
|
updateCurrentContent(content)
|
||||||
completed()
|
completed()
|
||||||
@ -665,6 +697,8 @@ private final class NotificationServiceHandler {
|
|||||||
return
|
return
|
||||||
}
|
}
|
||||||
guard let decryptedPayload = decryptedNotificationPayload(key: notificationsKey, data: payloadData) else {
|
guard let decryptedPayload = decryptedNotificationPayload(key: notificationsKey, data: payloadData) else {
|
||||||
|
Logger.shared.log("NotificationService \(episode)", "Couldn't decrypt payload")
|
||||||
|
|
||||||
let content = NotificationContent()
|
let content = NotificationContent()
|
||||||
updateCurrentContent(content)
|
updateCurrentContent(content)
|
||||||
completed()
|
completed()
|
||||||
@ -672,6 +706,8 @@ private final class NotificationServiceHandler {
|
|||||||
return
|
return
|
||||||
}
|
}
|
||||||
guard let payloadJson = try? JSONSerialization.jsonObject(with: decryptedPayload, options: []) as? [String: Any] else {
|
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()
|
let content = NotificationContent()
|
||||||
updateCurrentContent(content)
|
updateCurrentContent(content)
|
||||||
completed()
|
completed()
|
||||||
@ -679,6 +715,8 @@ private final class NotificationServiceHandler {
|
|||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
|
Logger.shared.log("NotificationService \(episode)", "Decrypted payload: \(payloadJson)")
|
||||||
|
|
||||||
var peerId: PeerId?
|
var peerId: PeerId?
|
||||||
var messageId: MessageId.Id?
|
var messageId: MessageId.Id?
|
||||||
var mediaAttachment: Media?
|
var mediaAttachment: Media?
|
||||||
@ -842,8 +880,10 @@ private final class NotificationServiceHandler {
|
|||||||
if let action = action {
|
if let action = action {
|
||||||
switch action {
|
switch action {
|
||||||
case .logout:
|
case .logout:
|
||||||
|
Logger.shared.log("NotificationService \(episode)", "Will logout")
|
||||||
completed()
|
completed()
|
||||||
case let .poll(peerId, initialContent):
|
case let .poll(peerId, initialContent):
|
||||||
|
Logger.shared.log("NotificationService \(episode)", "Will poll")
|
||||||
if let stateManager = strongSelf.stateManager {
|
if let stateManager = strongSelf.stateManager {
|
||||||
let pollCompletion: (NotificationContent) -> Void = { content in
|
let pollCompletion: (NotificationContent) -> Void = { content in
|
||||||
var content = content
|
var content = content
|
||||||
@ -912,6 +952,7 @@ private final class NotificationServiceHandler {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
Logger.shared.log("NotificationService \(episode)", "Will fetch media")
|
||||||
let _ = (fetchMediaSignal
|
let _ = (fetchMediaSignal
|
||||||
|> timeout(10.0, queue: queue, alternate: .single(nil))
|
|> timeout(10.0, queue: queue, alternate: .single(nil))
|
||||||
|> deliverOn(queue)).start(next: { mediaData in
|
|> deliverOn(queue)).start(next: { mediaData in
|
||||||
@ -920,6 +961,9 @@ private final class NotificationServiceHandler {
|
|||||||
return
|
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(
|
let _ = (getCurrentRenderedTotalUnreadCount(
|
||||||
accountManager: strongSelf.accountManager,
|
accountManager: strongSelf.accountManager,
|
||||||
postbox: stateManager.postbox
|
postbox: stateManager.postbox
|
||||||
@ -934,6 +978,8 @@ private final class NotificationServiceHandler {
|
|||||||
content.badge = Int(value.0)
|
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 image = mediaAttachment as? TelegramMediaImage, let resource = largestImageRepresentation(image.representations)?.resource {
|
||||||
if let mediaData = mediaData {
|
if let mediaData = mediaData {
|
||||||
stateManager.postbox.mediaBox.storeResourceData(resource.id, data: mediaData, synchronous: true)
|
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)
|
updateCurrentContent(content)
|
||||||
|
|
||||||
completed()
|
completed()
|
||||||
@ -1000,6 +1048,8 @@ private final class NotificationServiceHandler {
|
|||||||
|
|
||||||
stateManager.network.shouldKeepConnection.set(.single(true))
|
stateManager.network.shouldKeepConnection.set(.single(true))
|
||||||
if peerId.namespace == Namespaces.Peer.CloudChannel {
|
if peerId.namespace == Namespaces.Peer.CloudChannel {
|
||||||
|
Logger.shared.log("NotificationService \(episode)", "Will poll channel \(peerId)")
|
||||||
|
|
||||||
pollSignal = standalonePollChannelOnce(
|
pollSignal = standalonePollChannelOnce(
|
||||||
postbox: stateManager.postbox,
|
postbox: stateManager.postbox,
|
||||||
network: stateManager.network,
|
network: stateManager.network,
|
||||||
@ -1007,6 +1057,7 @@ private final class NotificationServiceHandler {
|
|||||||
stateManager: stateManager
|
stateManager: stateManager
|
||||||
)
|
)
|
||||||
} else {
|
} else {
|
||||||
|
Logger.shared.log("NotificationService \(episode)", "Will perform non-specific getDifference")
|
||||||
enum ControlError {
|
enum ControlError {
|
||||||
case restart
|
case restart
|
||||||
}
|
}
|
||||||
@ -1029,7 +1080,7 @@ private final class NotificationServiceHandler {
|
|||||||
pollWithUpdatedContent = stateManager.postbox.transaction { transaction -> NotificationContent in
|
pollWithUpdatedContent = stateManager.postbox.transaction { transaction -> NotificationContent in
|
||||||
var content = initialContent
|
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)
|
content.addSenderInfo(mediaBox: stateManager.postbox.mediaBox, accountPeerId: stateManager.accountPeerId, peer: peer)
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -1054,6 +1105,7 @@ private final class NotificationServiceHandler {
|
|||||||
completed()
|
completed()
|
||||||
}
|
}
|
||||||
case let .deleteMessage(ids):
|
case let .deleteMessage(ids):
|
||||||
|
Logger.shared.log("NotificationService \(episode)", "Will delete messages \(ids)")
|
||||||
let mediaBox = stateManager.postbox.mediaBox
|
let mediaBox = stateManager.postbox.mediaBox
|
||||||
let _ = (stateManager.postbox.transaction { transaction -> Void in
|
let _ = (stateManager.postbox.transaction { transaction -> Void in
|
||||||
_internal_deleteMessages(transaction: transaction, mediaBox: mediaBox, ids: ids, deleteMedia: true)
|
_internal_deleteMessages(transaction: transaction, mediaBox: mediaBox, ids: ids, deleteMedia: true)
|
||||||
@ -1084,6 +1136,8 @@ private final class NotificationServiceHandler {
|
|||||||
if isCurrentAccount {
|
if isCurrentAccount {
|
||||||
content.badge = Int(value.0)
|
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)
|
updateCurrentContent(content)
|
||||||
|
|
||||||
@ -1092,6 +1146,7 @@ private final class NotificationServiceHandler {
|
|||||||
}
|
}
|
||||||
|
|
||||||
if !removeIdentifiers.isEmpty {
|
if !removeIdentifiers.isEmpty {
|
||||||
|
Logger.shared.log("NotificationService \(episode)", "Will try to remove \(removeIdentifiers.count) notifications")
|
||||||
UNUserNotificationCenter.current().removeDeliveredNotifications(withIdentifiers: removeIdentifiers)
|
UNUserNotificationCenter.current().removeDeliveredNotifications(withIdentifiers: removeIdentifiers)
|
||||||
queue.after(1.0, {
|
queue.after(1.0, {
|
||||||
completeRemoval()
|
completeRemoval()
|
||||||
@ -1102,6 +1157,7 @@ private final class NotificationServiceHandler {
|
|||||||
})
|
})
|
||||||
})
|
})
|
||||||
case let .readMessage(id):
|
case let .readMessage(id):
|
||||||
|
Logger.shared.log("NotificationService \(episode)", "Will read message \(id)")
|
||||||
let _ = (stateManager.postbox.transaction { transaction -> Void in
|
let _ = (stateManager.postbox.transaction { transaction -> Void in
|
||||||
transaction.applyIncomingReadMaxId(id)
|
transaction.applyIncomingReadMaxId(id)
|
||||||
}
|
}
|
||||||
@ -1130,6 +1186,9 @@ private final class NotificationServiceHandler {
|
|||||||
content.badge = Int(value.0)
|
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)
|
updateCurrentContent(content)
|
||||||
|
|
||||||
completed()
|
completed()
|
||||||
@ -1137,6 +1196,7 @@ private final class NotificationServiceHandler {
|
|||||||
}
|
}
|
||||||
|
|
||||||
if !removeIdentifiers.isEmpty {
|
if !removeIdentifiers.isEmpty {
|
||||||
|
Logger.shared.log("NotificationService \(episode)", "Will try to remove \(removeIdentifiers.count) notifications")
|
||||||
UNUserNotificationCenter.current().removeDeliveredNotifications(withIdentifiers: removeIdentifiers)
|
UNUserNotificationCenter.current().removeDeliveredNotifications(withIdentifiers: removeIdentifiers)
|
||||||
queue.after(1.0, {
|
queue.after(1.0, {
|
||||||
completeRemoval()
|
completeRemoval()
|
||||||
|
|||||||
@ -81,6 +81,7 @@ public final class AnimatedStickerFrame {
|
|||||||
self.type = type
|
self.type = type
|
||||||
self.width = width
|
self.width = width
|
||||||
self.height = height
|
self.height = height
|
||||||
|
assert(bytesPerRow > 0)
|
||||||
self.bytesPerRow = bytesPerRow
|
self.bytesPerRow = bytesPerRow
|
||||||
self.index = index
|
self.index = index
|
||||||
self.isLastFrame = isLastFrame
|
self.isLastFrame = isLastFrame
|
||||||
@ -687,7 +688,7 @@ private final class AnimatedStickerDirectFrameSource: AnimatedStickerFrameSource
|
|||||||
self.currentFrame += 1
|
self.currentFrame += 1
|
||||||
if draw {
|
if draw {
|
||||||
if let cache = self.cache, let yuvData = cache.readUncompressedYuvFrame(index: frameIndex) {
|
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 {
|
} else {
|
||||||
var frameData = Data(count: self.bytesPerRow * self.height)
|
var frameData = Data(count: self.bytesPerRow * self.height)
|
||||||
frameData.withUnsafeMutableBytes { buffer -> Void in
|
frameData.withUnsafeMutableBytes { buffer -> Void in
|
||||||
@ -1135,6 +1136,8 @@ public final class AnimatedStickerNode: ASDisplayNode {
|
|||||||
return
|
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: {
|
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 {
|
guard let strongSelf = self else {
|
||||||
return
|
return
|
||||||
|
|||||||
@ -10,6 +10,7 @@ final class SoftwareAnimationRenderer: ASDisplayNode, AnimationRenderer {
|
|||||||
private var highlightedColor: UIColor?
|
private var highlightedColor: UIColor?
|
||||||
|
|
||||||
func render(queue: Queue, width: Int, height: Int, bytesPerRow: Int, data: Data, type: AnimationRendererFrameType, completion: @escaping () -> Void) {
|
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
|
queue.async { [weak self] in
|
||||||
switch type {
|
switch type {
|
||||||
case .argb:
|
case .argb:
|
||||||
@ -26,7 +27,7 @@ final class SoftwareAnimationRenderer: ASDisplayNode, AnimationRenderer {
|
|||||||
guard let baseAddress = bytes.baseAddress else {
|
guard let baseAddress = bytes.baseAddress else {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
if bytesPerRow * height > bytes.count {
|
if bytesPerRow <= 0 || height <= 0 || width <= 0 || bytesPerRow * height > bytes.count {
|
||||||
assert(false)
|
assert(false)
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|||||||
@ -22,6 +22,7 @@ swift_library(
|
|||||||
"//submodules/OverlayStatusController:OverlayStatusController",
|
"//submodules/OverlayStatusController:OverlayStatusController",
|
||||||
"//submodules/AccountContext:AccountContext",
|
"//submodules/AccountContext:AccountContext",
|
||||||
"//submodules/AppBundle:AppBundle",
|
"//submodules/AppBundle:AppBundle",
|
||||||
|
"//submodules/GZip:GZip"
|
||||||
],
|
],
|
||||||
visibility = [
|
visibility = [
|
||||||
"//visibility:public",
|
"//visibility:public",
|
||||||
|
|||||||
@ -13,6 +13,7 @@ import PresentationDataUtils
|
|||||||
import OverlayStatusController
|
import OverlayStatusController
|
||||||
import AccountContext
|
import AccountContext
|
||||||
import AppBundle
|
import AppBundle
|
||||||
|
import GZip
|
||||||
|
|
||||||
@objc private final class DebugControllerMailComposeDelegate: NSObject, MFMailComposeViewControllerDelegate {
|
@objc private final class DebugControllerMailComposeDelegate: NSObject, MFMailComposeViewControllerDelegate {
|
||||||
public func mailComposeController(_ controller: MFMailComposeViewController, didFinishWith result: MFMailComposeResult, error: Error?) {
|
public func mailComposeController(_ controller: MFMailComposeViewController, didFinishWith result: MFMailComposeResult, error: Error?) {
|
||||||
@ -79,7 +80,7 @@ private enum DebugControllerEntry: ItemListNodeEntry {
|
|||||||
case experimentalCompatibility(Bool)
|
case experimentalCompatibility(Bool)
|
||||||
case enableDebugDataDisplay(Bool)
|
case enableDebugDataDisplay(Bool)
|
||||||
case acceleratedStickers(Bool)
|
case acceleratedStickers(Bool)
|
||||||
case mockICE(Bool)
|
case experimentalBackground(Bool)
|
||||||
case playerEmbedding(Bool)
|
case playerEmbedding(Bool)
|
||||||
case playlistPlayback(Bool)
|
case playlistPlayback(Bool)
|
||||||
case voiceConference
|
case voiceConference
|
||||||
@ -101,7 +102,7 @@ private enum DebugControllerEntry: ItemListNodeEntry {
|
|||||||
return DebugControllerSection.logging.rawValue
|
return DebugControllerSection.logging.rawValue
|
||||||
case .enableRaiseToSpeak, .keepChatNavigationStack, .skipReadHistory, .crashOnSlowQueries:
|
case .enableRaiseToSpeak, .keepChatNavigationStack, .skipReadHistory, .crashOnSlowQueries:
|
||||||
return DebugControllerSection.experiments.rawValue
|
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
|
return DebugControllerSection.experiments.rawValue
|
||||||
case .preferredVideoCodec:
|
case .preferredVideoCodec:
|
||||||
return DebugControllerSection.videoExperiments.rawValue
|
return DebugControllerSection.videoExperiments.rawValue
|
||||||
@ -170,7 +171,7 @@ private enum DebugControllerEntry: ItemListNodeEntry {
|
|||||||
return 27
|
return 27
|
||||||
case .acceleratedStickers:
|
case .acceleratedStickers:
|
||||||
return 29
|
return 29
|
||||||
case .mockICE:
|
case .experimentalBackground:
|
||||||
return 30
|
return 30
|
||||||
case .playerEmbedding:
|
case .playerEmbedding:
|
||||||
return 31
|
return 31
|
||||||
@ -234,25 +235,27 @@ private enum DebugControllerEntry: ItemListNodeEntry {
|
|||||||
strongController.dismiss()
|
strongController.dismiss()
|
||||||
|
|
||||||
let lineFeed = "\n".data(using: .utf8)!
|
let lineFeed = "\n".data(using: .utf8)!
|
||||||
var logData: Data = Data()
|
var rawLogData: Data = Data()
|
||||||
for (name, path) in logs {
|
for (name, path) in logs {
|
||||||
if !logData.isEmpty {
|
if !rawLogData.isEmpty {
|
||||||
logData.append(lineFeed)
|
rawLogData.append(lineFeed)
|
||||||
logData.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)) {
|
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 id = Int64.random(in: Int64.min ... Int64.max)
|
||||||
let fileResource = LocalFileMediaResource(fileId: id, size: logData.count, isSecretRelated: false)
|
let fileResource = LocalFileMediaResource(fileId: id, size: gzippedData.count, isSecretRelated: false)
|
||||||
context.account.postbox.mediaBox.storeResourceData(fileResource.id, data: logData)
|
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: logData.count, attributes: [.FileName(fileName: "Log-iOS-Full.txt")])
|
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 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: [message]).start()
|
||||||
@ -386,25 +389,27 @@ private enum DebugControllerEntry: ItemListNodeEntry {
|
|||||||
strongController.dismiss()
|
strongController.dismiss()
|
||||||
|
|
||||||
let lineFeed = "\n".data(using: .utf8)!
|
let lineFeed = "\n".data(using: .utf8)!
|
||||||
var logData: Data = Data()
|
var rawLogData: Data = Data()
|
||||||
for (name, path) in logs {
|
for (name, path) in logs {
|
||||||
if !logData.isEmpty {
|
if !rawLogData.isEmpty {
|
||||||
logData.append(lineFeed)
|
rawLogData.append(lineFeed)
|
||||||
logData.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)) {
|
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 id = Int64.random(in: Int64.min ... Int64.max)
|
||||||
let fileResource = LocalFileMediaResource(fileId: id, size: logData.count, isSecretRelated: false)
|
let fileResource = LocalFileMediaResource(fileId: id, size: gzippedData.count, isSecretRelated: false)
|
||||||
context.account.postbox.mediaBox.storeResourceData(fileResource.id, data: logData)
|
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: logData.count, attributes: [.FileName(fileName: "Log-iOS-Full.txt")])
|
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 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: [message]).start()
|
||||||
@ -436,12 +441,18 @@ private enum DebugControllerEntry: ItemListNodeEntry {
|
|||||||
})
|
})
|
||||||
})
|
})
|
||||||
case .sendNotificationLogs:
|
case .sendNotificationLogs:
|
||||||
return ItemListDisclosureItem(presentationData: presentationData, title: "Send Notification Logs", label: "", sectionId: self.section, style: .blocks, action: {
|
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 + "/notificationServiceLogs").collectLogs()
|
let _ = (Logger(rootPath: arguments.sharedContext.basePath, basePath: arguments.sharedContext.basePath + "/notification-logs").collectLogs()
|
||||||
|> deliverOnMainQueue).start(next: { logs in
|
|> deliverOnMainQueue).start(next: { logs in
|
||||||
guard let context = arguments.context else {
|
let presentationData = arguments.sharedContext.currentPresentationData.with { $0 }
|
||||||
return
|
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]))
|
let controller = context.sharedContext.makePeerSelectionController(PeerSelectionControllerParams(context: context, filter: [.onlyWriteable, .excludeDisabled]))
|
||||||
controller.peerSelected = { [weak controller] peer in
|
controller.peerSelected = { [weak controller] peer in
|
||||||
let peerId = peer.id
|
let peerId = peer.id
|
||||||
@ -449,15 +460,56 @@ private enum DebugControllerEntry: ItemListNodeEntry {
|
|||||||
if let strongController = controller {
|
if let strongController = controller {
|
||||||
strongController.dismiss()
|
strongController.dismiss()
|
||||||
|
|
||||||
let messages = logs.map { (name, path) -> EnqueueMessage in
|
let lineFeed = "\n".data(using: .utf8)!
|
||||||
let id = Int64.random(in: Int64.min ... Int64.max)
|
var rawLogData: Data = Data()
|
||||||
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)])
|
for (name, path) in logs {
|
||||||
return .message(text: "", attributes: [], mediaReference: .standalone(media: file), replyToMessageId: nil, localGroupingKey: nil, correlationId: nil)
|
if !rawLogData.isEmpty {
|
||||||
|
rawLogData.append(lineFeed)
|
||||||
|
rawLogData.append(lineFeed)
|
||||||
}
|
}
|
||||||
let _ = enqueueMessages(account: context.account, peerId: peerId, messages: messages).start()
|
|
||||||
|
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 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()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
arguments.pushController(controller)
|
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.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:
|
case .sendCriticalLogs:
|
||||||
@ -752,12 +804,12 @@ private enum DebugControllerEntry: ItemListNodeEntry {
|
|||||||
})
|
})
|
||||||
}).start()
|
}).start()
|
||||||
})
|
})
|
||||||
case let .mockICE(value):
|
case let .experimentalBackground(value):
|
||||||
return ItemListSwitchItem(presentationData: presentationData, title: "mockICE", value: value, sectionId: self.section, style: .blocks, updated: { value in
|
return ItemListSwitchItem(presentationData: presentationData, title: "Background Experiment", value: value, sectionId: self.section, style: .blocks, updated: { value in
|
||||||
let _ = arguments.sharedContext.accountManager.transaction ({ transaction in
|
let _ = arguments.sharedContext.accountManager.transaction ({ transaction in
|
||||||
transaction.updateSharedData(ApplicationSpecificSharedDataKeys.experimentalUISettings, { settings in
|
transaction.updateSharedData(ApplicationSpecificSharedDataKeys.experimentalUISettings, { settings in
|
||||||
var settings = settings?.get(ExperimentalUISettings.self) ?? ExperimentalUISettings.defaultSettings
|
var settings = settings?.get(ExperimentalUISettings.self) ?? ExperimentalUISettings.defaultSettings
|
||||||
settings.mockICE = value
|
settings.experimentalBackground = value
|
||||||
return PreferencesEntry(settings)
|
return PreferencesEntry(settings)
|
||||||
})
|
})
|
||||||
}).start()
|
}).start()
|
||||||
@ -837,7 +889,7 @@ private func debugControllerEntries(sharedContext: SharedAccountContext, present
|
|||||||
|
|
||||||
// entries.append(.testStickerImport(presentationData.theme))
|
// entries.append(.testStickerImport(presentationData.theme))
|
||||||
entries.append(.sendLogs(presentationData.theme))
|
entries.append(.sendLogs(presentationData.theme))
|
||||||
entries.append(.sendOneLog(presentationData.theme))
|
//entries.append(.sendOneLog(presentationData.theme))
|
||||||
entries.append(.sendShareLogs)
|
entries.append(.sendShareLogs)
|
||||||
entries.append(.sendNotificationLogs(presentationData.theme))
|
entries.append(.sendNotificationLogs(presentationData.theme))
|
||||||
entries.append(.sendCriticalLogs(presentationData.theme))
|
entries.append(.sendCriticalLogs(presentationData.theme))
|
||||||
@ -874,7 +926,7 @@ private func debugControllerEntries(sharedContext: SharedAccountContext, present
|
|||||||
entries.append(.experimentalCompatibility(experimentalSettings.experimentalCompatibility))
|
entries.append(.experimentalCompatibility(experimentalSettings.experimentalCompatibility))
|
||||||
entries.append(.enableDebugDataDisplay(experimentalSettings.enableDebugDataDisplay))
|
entries.append(.enableDebugDataDisplay(experimentalSettings.enableDebugDataDisplay))
|
||||||
entries.append(.acceleratedStickers(experimentalSettings.acceleratedStickers))
|
entries.append(.acceleratedStickers(experimentalSettings.acceleratedStickers))
|
||||||
entries.append(.mockICE(experimentalSettings.mockICE))
|
entries.append(.experimentalBackground(experimentalSettings.experimentalBackground))
|
||||||
entries.append(.playerEmbedding(experimentalSettings.playerEmbedding))
|
entries.append(.playerEmbedding(experimentalSettings.playerEmbedding))
|
||||||
entries.append(.playlistPlayback(experimentalSettings.playlistPlayback))
|
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) {
|
public func pictureInPictureController(_ pictureInPictureController: AVPictureInPictureController, skipByInterval skipInterval: CMTime, completion completionHandler: @escaping () -> Void) {
|
||||||
|
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()
|
completionHandler()
|
||||||
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
public func pictureInPictureControllerShouldProhibitBackgroundAudioPlayback(_ pictureInPictureController: AVPictureInPictureController) -> Bool {
|
public func pictureInPictureControllerShouldProhibitBackgroundAudioPlayback(_ pictureInPictureController: AVPictureInPictureController) -> Bool {
|
||||||
@ -539,7 +554,9 @@ private final class PictureInPictureContentImpl: NSObject, PictureInPictureConte
|
|||||||
|
|
||||||
private var hiddenMediaManagerIndex: Int?
|
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.overlayController = overlayController
|
||||||
self.mediaManager = mediaManager
|
self.mediaManager = mediaManager
|
||||||
self.node = videoNode
|
self.node = videoNode
|
||||||
@ -559,7 +576,7 @@ private final class PictureInPictureContentImpl: NSObject, PictureInPictureConte
|
|||||||
contentDelegate.pictureInPictureController = pictureInPictureController
|
contentDelegate.pictureInPictureController = pictureInPictureController
|
||||||
|
|
||||||
pictureInPictureController.canStartPictureInPictureAutomaticallyFromInline = false
|
pictureInPictureController.canStartPictureInPictureAutomaticallyFromInline = false
|
||||||
pictureInPictureController.requiresLinearPlayback = true
|
pictureInPictureController.requiresLinearPlayback = !canSkip
|
||||||
pictureInPictureController.delegate = self
|
pictureInPictureController.delegate = self
|
||||||
self.pictureInPictureController = pictureInPictureController
|
self.pictureInPictureController = pictureInPictureController
|
||||||
let timer = SwiftSignalKit.Timer(timeout: 0.005, repeat: true, completion: { [weak self] in
|
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 {
|
deinit {
|
||||||
|
self.messageRemovedDisposable?.dispose()
|
||||||
self.pictureInPictureTimer?.invalidate()
|
self.pictureInPictureTimer?.invalidate()
|
||||||
self.node.setCanPlaybackWithoutHierarchy(false)
|
self.node.setCanPlaybackWithoutHierarchy(false)
|
||||||
|
|
||||||
@ -699,6 +738,8 @@ final class UniversalVideoGalleryItemNode: ZoomableContentGalleryItemNode {
|
|||||||
private var keepSoundOnDismiss = false
|
private var keepSoundOnDismiss = false
|
||||||
private var hasPictureInPicture = false
|
private var hasPictureInPicture = false
|
||||||
|
|
||||||
|
private var pictureInPictureButton: UIBarButtonItem?
|
||||||
|
|
||||||
private var requiresDownload = false
|
private var requiresDownload = false
|
||||||
|
|
||||||
private var item: UniversalVideoGalleryItem?
|
private var item: UniversalVideoGalleryItem?
|
||||||
@ -1328,6 +1369,7 @@ final class UniversalVideoGalleryItemNode: ZoomableContentGalleryItemNode {
|
|||||||
}
|
}
|
||||||
if forceEnablePiP || (!isAnimated && !disablePlayerControls && !disablePictureInPicture) {
|
if forceEnablePiP || (!isAnimated && !disablePlayerControls && !disablePictureInPicture) {
|
||||||
let rightBarButtonItem = UIBarButtonItem(image: pictureInPictureButtonImage, style: .plain, target: self, action: #selector(self.pictureInPictureButtonPressed))
|
let rightBarButtonItem = UIBarButtonItem(image: pictureInPictureButtonImage, style: .plain, target: self, action: #selector(self.pictureInPictureButtonPressed))
|
||||||
|
self.pictureInPictureButton = rightBarButtonItem
|
||||||
barButtonItems.append(rightBarButtonItem)
|
barButtonItems.append(rightBarButtonItem)
|
||||||
self.hasPictureInPicture = true
|
self.hasPictureInPicture = true
|
||||||
} else {
|
} else {
|
||||||
@ -1427,6 +1469,8 @@ final class UniversalVideoGalleryItemNode: ZoomableContentGalleryItemNode {
|
|||||||
pictureInPictureNode.removeFromSupernode()
|
pictureInPictureNode.removeFromSupernode()
|
||||||
self.videoNode?.backgroundColor = .black
|
self.videoNode?.backgroundColor = .black
|
||||||
}
|
}
|
||||||
|
|
||||||
|
self.pictureInPictureButton?.isEnabled = self.pictureInPictureNode == nil
|
||||||
}
|
}
|
||||||
|
|
||||||
private func shouldAutoplayOnCentrality() -> Bool {
|
private func shouldAutoplayOnCentrality() -> Bool {
|
||||||
@ -2064,6 +2108,20 @@ final class UniversalVideoGalleryItemNode: ZoomableContentGalleryItemNode {
|
|||||||
}
|
}
|
||||||
|
|
||||||
@objc func pictureInPictureButtonPressed() {
|
@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 {
|
if let item = self.item, let videoNode = self.videoNode, let overlayController = self.context.sharedContext.mediaManager.overlayMediaManager.controller {
|
||||||
videoNode.setContinuePlayingWithoutSoundOnLostAudioSession(false)
|
videoNode.setContinuePlayingWithoutSoundOnLostAudioSession(false)
|
||||||
|
|
||||||
@ -2071,7 +2129,7 @@ final class UniversalVideoGalleryItemNode: ZoomableContentGalleryItemNode {
|
|||||||
let baseNavigationController = self.baseNavigationController()
|
let baseNavigationController = self.baseNavigationController()
|
||||||
let playbackRate = self.playbackRate
|
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
|
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)
|
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
|
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 {
|
guard let strongSelf = self else {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|||||||
@ -275,7 +275,7 @@ public final class GradientBackgroundNode: ASDisplayNode {
|
|||||||
deinit {
|
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
|
let sizeUpdated = self.validLayout != size
|
||||||
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.beginTime = self.contentView.layer.convertTime(CACurrentMediaTime(), from: nil) + 0.25
|
||||||
}
|
}
|
||||||
|
|
||||||
|
animation.completion = { _ in
|
||||||
|
completion()
|
||||||
|
}
|
||||||
|
|
||||||
self.contentView.layer.removeAnimation(forKey: "contents")
|
self.contentView.layer.removeAnimation(forKey: "contents")
|
||||||
self.contentView.layer.add(animation, forKey: "contents")
|
self.contentView.layer.add(animation, forKey: "contents")
|
||||||
|
|
||||||
@ -405,7 +409,11 @@ public final class GradientBackgroundNode: ASDisplayNode {
|
|||||||
for cloneNode in self.cloneNodes {
|
for cloneNode in self.cloneNodes {
|
||||||
cloneNode.value?.image = dimmedImage
|
cloneNode.value?.image = dimmedImage
|
||||||
}
|
}
|
||||||
|
|
||||||
|
completion()
|
||||||
}
|
}
|
||||||
|
} else {
|
||||||
|
completion()
|
||||||
}
|
}
|
||||||
} else if sizeUpdated {
|
} else if sizeUpdated {
|
||||||
let image = generateGradient(size: imageSize, colors: self.colors, positions: positions)
|
let image = generateGradient(size: imageSize, colors: self.colors, positions: positions)
|
||||||
@ -419,6 +427,10 @@ public final class GradientBackgroundNode: ASDisplayNode {
|
|||||||
}
|
}
|
||||||
|
|
||||||
self.validPhase = self.phase
|
self.validPhase = self.phase
|
||||||
|
|
||||||
|
completion()
|
||||||
|
} else {
|
||||||
|
completion()
|
||||||
}
|
}
|
||||||
|
|
||||||
transition.updateFrame(view: self.contentView, frame: CGRect(origin: CGPoint(), size: size))
|
transition.updateFrame(view: self.contentView, frame: CGRect(origin: CGPoint(), size: size))
|
||||||
@ -440,13 +452,14 @@ public final class GradientBackgroundNode: ASDisplayNode {
|
|||||||
self.colors = colors
|
self.colors = colors
|
||||||
self.invalidated = true
|
self.invalidated = true
|
||||||
if let size = self.validLayout {
|
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 {
|
guard case let .animated(duration, _) = transition, duration > 0.001 else {
|
||||||
|
completion()
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -463,7 +476,9 @@ public final class GradientBackgroundNode: ASDisplayNode {
|
|||||||
GradientBackgroundNode.sharedPhase = self.phase
|
GradientBackgroundNode.sharedPhase = self.phase
|
||||||
}
|
}
|
||||||
if let size = self.validLayout {
|
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):
|
case .sameSection(false):
|
||||||
bottomStripeInset = leftInset + editingOffset
|
bottomStripeInset = leftInset + editingOffset
|
||||||
bottomStripeOffset = -separatorHeight
|
bottomStripeOffset = -separatorHeight
|
||||||
|
strongSelf.bottomStripeNode.isHidden = !item.hasSeparator
|
||||||
default:
|
default:
|
||||||
bottomStripeInset = 0.0
|
bottomStripeInset = 0.0
|
||||||
bottomStripeOffset = 0.0
|
bottomStripeOffset = 0.0
|
||||||
hasBottomCorners = true
|
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.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)))
|
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) bool isAnimation;
|
||||||
@property (nonatomic, readonly) TGMediaAsset *originalAsset;
|
@property (nonatomic, readonly) TGMediaAsset *originalAsset;
|
||||||
@property (nonatomic, readonly) CGSize dimensions;
|
@property (nonatomic, readonly) CGSize dimensions;
|
||||||
|
@property (nonatomic, readonly) NSString *uniformTypeIdentifier;
|
||||||
|
|
||||||
- (instancetype)initWithURL:(NSURL *)url;
|
- (instancetype)initWithURL:(NSURL *)url;
|
||||||
- (instancetype)initWithAsset:(TGMediaAsset *)asset livePhoto:(bool)livePhoto;
|
- (instancetype)initWithAsset:(TGMediaAsset *)asset livePhoto:(bool)livePhoto;
|
||||||
|
|||||||
@ -88,6 +88,11 @@
|
|||||||
return _cachedAVAsset;
|
return _cachedAVAsset;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
- (NSString *)uniformTypeIdentifier
|
||||||
|
{
|
||||||
|
return nil;
|
||||||
|
}
|
||||||
|
|
||||||
- (SSignal *)avAsset {
|
- (SSignal *)avAsset {
|
||||||
if (_originalAsset != nil) {
|
if (_originalAsset != nil) {
|
||||||
if (_cachedAVAsset != nil) {
|
if (_cachedAVAsset != nil) {
|
||||||
|
|||||||
@ -1107,7 +1107,11 @@
|
|||||||
}
|
}
|
||||||
else if (self.item.avAsset != nil) {
|
else if (self.item.avAsset != nil) {
|
||||||
itemSignal = [self.item.avAsset mapToSignal:^SSignal *(AVAsset *avAsset) {
|
itemSignal = [self.item.avAsset mapToSignal:^SSignal *(AVAsset *avAsset) {
|
||||||
|
if ([avAsset isKindOfClass:[AVAsset class]]) {
|
||||||
return [SSignal single:[AVPlayerItem playerItemWithAsset:avAsset]];
|
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
|
+ (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];
|
SQueue *queue = [[SQueue alloc] init];
|
||||||
|
|
||||||
return [[SSignal alloc] initWithGenerator:^id<SDisposable>(SSubscriber *subscriber)
|
return [[SSignal alloc] initWithGenerator:^id<SDisposable>(SSubscriber *subscriber)
|
||||||
|
|||||||
@ -94,7 +94,7 @@ final class PasscodeEntryControllerNode: ASDisplayNode {
|
|||||||
if let strongSelf = self {
|
if let strongSelf = self {
|
||||||
strongSelf.inputFieldNode.append(character)
|
strongSelf.inputFieldNode.append(character)
|
||||||
if let gradientNode = strongSelf.backgroundCustomNode as? GradientBackgroundNode {
|
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 {
|
if let strongSelf = self {
|
||||||
let _ = strongSelf.inputFieldNode.delete()
|
let _ = strongSelf.inputFieldNode.delete()
|
||||||
if let gradientNode = strongSelf.backgroundCustomNode as? GradientBackgroundNode {
|
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()
|
self.hapticFeedback.tap()
|
||||||
let result = self.inputFieldNode.delete()
|
let result = self.inputFieldNode.delete()
|
||||||
if result, let gradientNode = self.backgroundCustomNode as? GradientBackgroundNode {
|
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 {
|
if let gradientNode = self.backgroundCustomNode as? GradientBackgroundNode {
|
||||||
gradientNode.layer.animateAlpha(from: 0.0, to: 1.0, duration: 0.3)
|
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)
|
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)
|
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)
|
self.backgroundImageNode.layer.animateAlpha(from: 0.0, to: 1.0, duration: 0.3)
|
||||||
if let gradientNode = self.backgroundCustomNode as? GradientBackgroundNode {
|
if let gradientNode = self.backgroundCustomNode as? GradientBackgroundNode {
|
||||||
gradientNode.layer.animateAlpha(from: 0.0, to: 1.0, duration: 0.3)
|
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)
|
self.backgroundDimNode.layer.animateAlpha(from: 0.0, to: 1.0, duration: 0.2)
|
||||||
}
|
}
|
||||||
if !iconFrame.isEmpty {
|
if !iconFrame.isEmpty {
|
||||||
@ -385,7 +385,7 @@ final class PasscodeEntryControllerNode: ASDisplayNode {
|
|||||||
self.subtitleNode.layer.animateAlpha(from: 0.0, to: 1.0, duration: 0.25)
|
self.subtitleNode.layer.animateAlpha(from: 0.0, to: 1.0, duration: 0.25)
|
||||||
|
|
||||||
if let gradientNode = self.backgroundCustomNode as? GradientBackgroundNode {
|
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.inputFieldNode.animateIn()
|
||||||
self.keyboardNode.animateIn()
|
self.keyboardNode.animateIn()
|
||||||
@ -425,7 +425,7 @@ final class PasscodeEntryControllerNode: ASDisplayNode {
|
|||||||
self.hapticFeedback.error()
|
self.hapticFeedback.error()
|
||||||
|
|
||||||
if let gradientNode = self.backgroundCustomNode as? GradientBackgroundNode {
|
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 {
|
if let backgroundCustomNode = self.backgroundCustomNode {
|
||||||
transition.updateFrame(node: backgroundCustomNode, frame: bounds)
|
transition.updateFrame(node: backgroundCustomNode, frame: bounds)
|
||||||
if let gradientBackgroundNode = backgroundCustomNode as? GradientBackgroundNode {
|
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)
|
transition.updateFrame(view: self.effectView, frame: bounds)
|
||||||
|
|||||||
@ -178,7 +178,7 @@ private enum ChannelAdminsEntry: ItemListNodeEntry {
|
|||||||
let arguments = arguments as! ChannelAdminsControllerArguments
|
let arguments = arguments as! ChannelAdminsControllerArguments
|
||||||
switch self {
|
switch self {
|
||||||
case let .recentActions(_, text):
|
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()
|
arguments.openRecentActions()
|
||||||
})
|
})
|
||||||
case let .adminsHeader(_, title):
|
case let .adminsHeader(_, title):
|
||||||
|
|||||||
@ -12,6 +12,7 @@ import AccountContext
|
|||||||
import AlertUI
|
import AlertUI
|
||||||
import PresentationDataUtils
|
import PresentationDataUtils
|
||||||
import ItemListPeerItem
|
import ItemListPeerItem
|
||||||
|
import ItemListPeerActionItem
|
||||||
import InviteLinksUI
|
import InviteLinksUI
|
||||||
|
|
||||||
private final class ChannelMembersControllerArguments {
|
private final class ChannelMembersControllerArguments {
|
||||||
@ -22,6 +23,7 @@ private final class ChannelMembersControllerArguments {
|
|||||||
let removePeer: (PeerId) -> Void
|
let removePeer: (PeerId) -> Void
|
||||||
let openPeer: (Peer) -> Void
|
let openPeer: (Peer) -> Void
|
||||||
let inviteViaLink: () -> 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) {
|
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.context = context
|
||||||
self.addMember = addMember
|
self.addMember = addMember
|
||||||
@ -155,12 +157,12 @@ private enum ChannelMembersEntry: ItemListNodeEntry {
|
|||||||
func item(presentationData: ItemListPresentationData, arguments: Any) -> ListViewItem {
|
func item(presentationData: ItemListPresentationData, arguments: Any) -> ListViewItem {
|
||||||
let arguments = arguments as! ChannelMembersControllerArguments
|
let arguments = arguments as! ChannelMembersControllerArguments
|
||||||
switch self {
|
switch self {
|
||||||
case let .addMember(_, text):
|
case let .addMember(theme, text):
|
||||||
return ItemListActionItem(presentationData: presentationData, title: text, kind: .generic, alignment: .natural, sectionId: self.section, style: .blocks, action: {
|
return ItemListPeerActionItem(presentationData: presentationData, icon: PresentationResourcesItemList.addPersonIcon(theme), title: text, alwaysPlain: false, sectionId: self.section, height: .generic, editing: false, action: {
|
||||||
arguments.addMember()
|
arguments.addMember()
|
||||||
})
|
})
|
||||||
case let .inviteLink(_, text):
|
case let .inviteLink(theme, text):
|
||||||
return ItemListActionItem(presentationData: presentationData, title: text, kind: .generic, alignment: .natural, sectionId: self.section, style: .blocks, action: {
|
return ItemListPeerActionItem(presentationData: presentationData, icon: PresentationResourcesItemList.linkIcon(theme), title: text, alwaysPlain: false, sectionId: self.section, height: .generic, editing: false, action: {
|
||||||
arguments.inviteViaLink()
|
arguments.inviteViaLink()
|
||||||
})
|
})
|
||||||
case let .addMemberInfo(_, text):
|
case let .addMemberInfo(_, text):
|
||||||
|
|||||||
@ -15,6 +15,10 @@ final class MutableAdditionalChatListItemsView: MutablePostboxView {
|
|||||||
return false
|
return false
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func refreshDueToExternalTransaction(postbox: PostboxImpl) -> Bool {
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
|
||||||
func immutableView() -> PostboxView {
|
func immutableView() -> PostboxView {
|
||||||
return AdditionalChatListItemsView(self)
|
return AdditionalChatListItemsView(self)
|
||||||
}
|
}
|
||||||
|
|||||||
@ -49,6 +49,10 @@ final class MutableAllChatListHolesView: MutablePostboxView {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func refreshDueToExternalTransaction(postbox: PostboxImpl) -> Bool {
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
|
||||||
func immutableView() -> PostboxView {
|
func immutableView() -> PostboxView {
|
||||||
return AllChatListHolesView(self)
|
return AllChatListHolesView(self)
|
||||||
}
|
}
|
||||||
|
|||||||
@ -17,6 +17,10 @@ final class MutableCachedItemView: MutablePostboxView {
|
|||||||
return false
|
return false
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func refreshDueToExternalTransaction(postbox: PostboxImpl) -> Bool {
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
|
||||||
func immutableView() -> PostboxView {
|
func immutableView() -> PostboxView {
|
||||||
return CachedItemView(self)
|
return CachedItemView(self)
|
||||||
}
|
}
|
||||||
|
|||||||
@ -18,6 +18,10 @@ final class MutableCachedPeerDataView: MutablePostboxView {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func refreshDueToExternalTransaction(postbox: PostboxImpl) -> Bool {
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
|
||||||
func immutableView() -> PostboxView {
|
func immutableView() -> PostboxView {
|
||||||
return CachedPeerDataView(self)
|
return CachedPeerDataView(self)
|
||||||
}
|
}
|
||||||
|
|||||||
@ -70,6 +70,10 @@ final class MutableContactPeersView: MutablePostboxView {
|
|||||||
return updated
|
return updated
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func refreshDueToExternalTransaction(postbox: PostboxImpl) -> Bool {
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
|
||||||
func immutableView() -> PostboxView {
|
func immutableView() -> PostboxView {
|
||||||
return ContactPeersView(self)
|
return ContactPeersView(self)
|
||||||
}
|
}
|
||||||
|
|||||||
@ -33,6 +33,10 @@ final class MutableDeletedMessagesView: MutablePostboxView {
|
|||||||
return updated
|
return updated
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func refreshDueToExternalTransaction(postbox: PostboxImpl) -> Bool {
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
|
||||||
func immutableView() -> PostboxView {
|
func immutableView() -> PostboxView {
|
||||||
return DeletedMessagesView(self)
|
return DeletedMessagesView(self)
|
||||||
}
|
}
|
||||||
|
|||||||
@ -203,6 +203,26 @@ final class MutableGlobalMessageTagsView: MutablePostboxView {
|
|||||||
return hasChanges
|
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 {
|
private func add(_ entry: InternalGlobalMessageTagsEntry) -> Bool {
|
||||||
if self.entries.count == 0 {
|
if self.entries.count == 0 {
|
||||||
self.entries.append(entry)
|
self.entries.append(entry)
|
||||||
|
|||||||
@ -62,6 +62,23 @@ final class MutableHistoryTagInfoView: MutablePostboxView {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
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 {
|
func immutableView() -> PostboxView {
|
||||||
return HistoryTagInfoView(self)
|
return HistoryTagInfoView(self)
|
||||||
}
|
}
|
||||||
|
|||||||
@ -38,6 +38,20 @@ final class MutableInvalidatedMessageHistoryTagSummariesView: MutablePostboxView
|
|||||||
return updated
|
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 {
|
func immutableView() -> PostboxView {
|
||||||
return InvalidatedMessageHistoryTagSummariesView(self)
|
return InvalidatedMessageHistoryTagSummariesView(self)
|
||||||
}
|
}
|
||||||
|
|||||||
@ -41,6 +41,21 @@ final class MutableItemCollectionIdsView: MutablePostboxView {
|
|||||||
return updated
|
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 {
|
func immutableView() -> PostboxView {
|
||||||
return ItemCollectionIdsView(self)
|
return ItemCollectionIdsView(self)
|
||||||
}
|
}
|
||||||
|
|||||||
@ -49,6 +49,10 @@ final class MutableItemCollectionInfoView: MutablePostboxView {
|
|||||||
return updated
|
return updated
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func refreshDueToExternalTransaction(postbox: PostboxImpl) -> Bool {
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
|
||||||
func immutableView() -> PostboxView {
|
func immutableView() -> PostboxView {
|
||||||
return ItemCollectionInfoView(self)
|
return ItemCollectionInfoView(self)
|
||||||
}
|
}
|
||||||
|
|||||||
@ -91,11 +91,13 @@ final class MutableItemCollectionInfosView: MutablePostboxView {
|
|||||||
return updated
|
return updated
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func refreshDueToExternalTransaction(postbox: PostboxImpl) -> Bool {
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
|
||||||
func immutableView() -> PostboxView {
|
func immutableView() -> PostboxView {
|
||||||
return ItemCollectionInfosView(self)
|
return ItemCollectionInfosView(self)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public final class ItemCollectionInfosView: PostboxView {
|
public final class ItemCollectionInfosView: PostboxView {
|
||||||
|
|||||||
@ -51,6 +51,10 @@ final class MutableLocalMessageTagsView: MutablePostboxView {
|
|||||||
return updated
|
return updated
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func refreshDueToExternalTransaction(postbox: PostboxImpl) -> Bool {
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
|
||||||
func immutableView() -> PostboxView {
|
func immutableView() -> PostboxView {
|
||||||
return LocalMessageTagsView(self)
|
return LocalMessageTagsView(self)
|
||||||
}
|
}
|
||||||
|
|||||||
@ -26,6 +26,17 @@ final class MutableMessageHistoryTagSummaryView: MutablePostboxView {
|
|||||||
return hasChanges
|
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 {
|
func immutableView() -> PostboxView {
|
||||||
return MessageHistoryTagSummaryView(self)
|
return MessageHistoryTagSummaryView(self)
|
||||||
}
|
}
|
||||||
|
|||||||
@ -739,7 +739,7 @@ final class MutableMessageHistoryView {
|
|||||||
}
|
}
|
||||||
case let .peerChatState(peerId, _):
|
case let .peerChatState(peerId, _):
|
||||||
if transaction.currentUpdatedPeerChatStates.contains(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
|
hasChanges = true
|
||||||
}
|
}
|
||||||
case .totalUnreadState:
|
case .totalUnreadState:
|
||||||
|
|||||||
@ -172,6 +172,10 @@ final class MutableMessageOfInterestHolesView: MutablePostboxView {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func refreshDueToExternalTransaction(postbox: PostboxImpl) -> Bool {
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
|
||||||
func immutableView() -> PostboxView {
|
func immutableView() -> PostboxView {
|
||||||
return MessageOfInterestHolesView(self)
|
return MessageOfInterestHolesView(self)
|
||||||
}
|
}
|
||||||
|
|||||||
@ -52,6 +52,10 @@ final class MutableMessagesView: MutablePostboxView {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func refreshDueToExternalTransaction(postbox: PostboxImpl) -> Bool {
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
|
||||||
func immutableView() -> PostboxView {
|
func immutableView() -> PostboxView {
|
||||||
return MessagesView(self)
|
return MessagesView(self)
|
||||||
}
|
}
|
||||||
|
|||||||
@ -43,6 +43,10 @@ final class MutableBasicPeerView: MutablePostboxView {
|
|||||||
return updated
|
return updated
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func refreshDueToExternalTransaction(postbox: PostboxImpl) -> Bool {
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
|
||||||
func immutableView() -> PostboxView {
|
func immutableView() -> PostboxView {
|
||||||
return BasicPeerView(self)
|
return BasicPeerView(self)
|
||||||
}
|
}
|
||||||
|
|||||||
@ -23,6 +23,10 @@ final class MutablePeerChatInclusionView: MutablePostboxView {
|
|||||||
return updated
|
return updated
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func refreshDueToExternalTransaction(postbox: PostboxImpl) -> Bool {
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
|
||||||
func immutableView() -> PostboxView {
|
func immutableView() -> PostboxView {
|
||||||
return PeerChatInclusionView(self)
|
return PeerChatInclusionView(self)
|
||||||
}
|
}
|
||||||
|
|||||||
@ -52,6 +52,10 @@ final class MutableOrderedItemListView: MutablePostboxView {
|
|||||||
return updated
|
return updated
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func refreshDueToExternalTransaction(postbox: PostboxImpl) -> Bool {
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
|
||||||
func immutableView() -> PostboxView {
|
func immutableView() -> PostboxView {
|
||||||
return OrderedItemListView(self)
|
return OrderedItemListView(self)
|
||||||
}
|
}
|
||||||
|
|||||||
@ -5,7 +5,7 @@ final class PeerChatStateTable: Table {
|
|||||||
return ValueBoxTable(id: id, keyType: .int64, compactValuesOnCreation: false)
|
return ValueBoxTable(id: id, keyType: .int64, compactValuesOnCreation: false)
|
||||||
}
|
}
|
||||||
|
|
||||||
private var cachedPeerChatStates: [PeerId: PostboxCoding?] = [:]
|
private var cachedPeerChatStates: [PeerId: CodableEntry?] = [:]
|
||||||
private var updatedPeerIds = Set<PeerId>()
|
private var updatedPeerIds = Set<PeerId>()
|
||||||
|
|
||||||
private let sharedKey = ValueBoxKey(length: 8)
|
private let sharedKey = ValueBoxKey(length: 8)
|
||||||
@ -15,11 +15,12 @@ final class PeerChatStateTable: Table {
|
|||||||
return self.sharedKey
|
return self.sharedKey
|
||||||
}
|
}
|
||||||
|
|
||||||
func get(_ id: PeerId) -> PostboxCoding? {
|
func get(_ id: PeerId) -> CodableEntry? {
|
||||||
if let state = self.cachedPeerChatStates[id] {
|
if let state = self.cachedPeerChatStates[id] {
|
||||||
return state
|
return state
|
||||||
} else {
|
} 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
|
self.cachedPeerChatStates[id] = state
|
||||||
return state
|
return state
|
||||||
} else {
|
} 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.cachedPeerChatStates[id] = state
|
||||||
self.updatedPeerIds.insert(id)
|
self.updatedPeerIds.insert(id)
|
||||||
}
|
}
|
||||||
@ -41,12 +42,9 @@ final class PeerChatStateTable: Table {
|
|||||||
|
|
||||||
override func beforeCommit() {
|
override func beforeCommit() {
|
||||||
if !self.updatedPeerIds.isEmpty {
|
if !self.updatedPeerIds.isEmpty {
|
||||||
let sharedEncoder = PostboxEncoder()
|
|
||||||
for id in self.updatedPeerIds {
|
for id in self.updatedPeerIds {
|
||||||
if let wrappedState = self.cachedPeerChatStates[id], let state = wrappedState {
|
if let wrappedState = self.cachedPeerChatStates[id], let state = wrappedState {
|
||||||
sharedEncoder.reset()
|
self.valueBox.set(self.table, key: self.key(id), value: ReadBuffer(data: state.data))
|
||||||
sharedEncoder.encodeRootObject(state)
|
|
||||||
self.valueBox.set(self.table, key: self.key(id), value: sharedEncoder.readBufferNoCopy())
|
|
||||||
} else {
|
} else {
|
||||||
self.valueBox.remove(self.table, key: self.key(id), secure: false)
|
self.valueBox.remove(self.table, key: self.key(id), secure: false)
|
||||||
}
|
}
|
||||||
|
|||||||
@ -2,7 +2,7 @@ import Foundation
|
|||||||
|
|
||||||
final class MutablePeerChatStateView: MutablePostboxView {
|
final class MutablePeerChatStateView: MutablePostboxView {
|
||||||
let peerId: PeerId
|
let peerId: PeerId
|
||||||
var chatState: PostboxCoding?
|
var chatState: CodableEntry?
|
||||||
|
|
||||||
init(postbox: PostboxImpl, peerId: PeerId) {
|
init(postbox: PostboxImpl, peerId: PeerId) {
|
||||||
self.peerId = peerId
|
self.peerId = peerId
|
||||||
@ -18,6 +18,17 @@ final class MutablePeerChatStateView: MutablePostboxView {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
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 {
|
func immutableView() -> PostboxView {
|
||||||
return PeerChatStateView(self)
|
return PeerChatStateView(self)
|
||||||
}
|
}
|
||||||
@ -25,7 +36,7 @@ final class MutablePeerChatStateView: MutablePostboxView {
|
|||||||
|
|
||||||
public final class PeerChatStateView: PostboxView {
|
public final class PeerChatStateView: PostboxView {
|
||||||
public let peerId: PeerId
|
public let peerId: PeerId
|
||||||
public let chatState: PostboxCoding?
|
public let chatState: CodableEntry?
|
||||||
|
|
||||||
init(_ view: MutablePeerChatStateView) {
|
init(_ view: MutablePeerChatStateView) {
|
||||||
self.peerId = view.peerId
|
self.peerId = view.peerId
|
||||||
|
|||||||
@ -20,6 +20,17 @@ final class MutablePeerNotificationSettingsBehaviorTimestampView: MutablePostbox
|
|||||||
return updated
|
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 {
|
func immutableView() -> PostboxView {
|
||||||
return PeerNotificationSettingsBehaviorTimestampView(self)
|
return PeerNotificationSettingsBehaviorTimestampView(self)
|
||||||
}
|
}
|
||||||
|
|||||||
@ -37,6 +37,44 @@ final class MutablePeerNotificationSettingsView: MutablePostboxView {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
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 {
|
func immutableView() -> PostboxView {
|
||||||
return PeerNotificationSettingsView(self)
|
return PeerNotificationSettingsView(self)
|
||||||
}
|
}
|
||||||
|
|||||||
@ -27,6 +27,41 @@ final class MutablePeerPresencesView: MutablePostboxView {
|
|||||||
return updated
|
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 {
|
func immutableView() -> PostboxView {
|
||||||
return PeerPresencesView(self)
|
return PeerPresencesView(self)
|
||||||
}
|
}
|
||||||
|
|||||||
@ -254,6 +254,10 @@ final class MutablePeerView: MutablePostboxView {
|
|||||||
return updated
|
return updated
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func refreshDueToExternalTransaction(postbox: PostboxImpl) -> Bool {
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
|
||||||
func immutableView() -> PostboxView {
|
func immutableView() -> PostboxView {
|
||||||
return PeerView(self)
|
return PeerView(self)
|
||||||
}
|
}
|
||||||
|
|||||||
@ -17,6 +17,10 @@ final class MutablePendingMessageActionsSummaryView: MutablePostboxView {
|
|||||||
return updated
|
return updated
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func refreshDueToExternalTransaction(postbox: PostboxImpl) -> Bool {
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
|
||||||
func immutableView() -> PostboxView {
|
func immutableView() -> PostboxView {
|
||||||
return PendingMessageActionsSummaryView(self)
|
return PendingMessageActionsSummaryView(self)
|
||||||
}
|
}
|
||||||
|
|||||||
@ -39,6 +39,10 @@ final class MutablePendingMessageActionsView: MutablePostboxView {
|
|||||||
return updated
|
return updated
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func refreshDueToExternalTransaction(postbox: PostboxImpl) -> Bool {
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
|
||||||
func immutableView() -> PostboxView {
|
func immutableView() -> PostboxView {
|
||||||
return PendingMessageActionsView(self)
|
return PendingMessageActionsView(self)
|
||||||
}
|
}
|
||||||
|
|||||||
@ -26,6 +26,10 @@ final class MutablePendingPeerNotificationSettingsView: MutablePostboxView {
|
|||||||
return updated
|
return updated
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func refreshDueToExternalTransaction(postbox: PostboxImpl) -> Bool {
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
|
||||||
func immutableView() -> PostboxView {
|
func immutableView() -> PostboxView {
|
||||||
return PendingPeerNotificationSettingsView(self)
|
return PendingPeerNotificationSettingsView(self)
|
||||||
}
|
}
|
||||||
|
|||||||
@ -272,7 +272,7 @@ public final class Transaction {
|
|||||||
|
|
||||||
public func getPeerChatState(_ id: PeerId) -> PeerChatState? {
|
public func getPeerChatState(_ id: PeerId) -> PeerChatState? {
|
||||||
assert(!self.disposed)
|
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) {
|
public func setPeerChatState(_ id: PeerId, state: PeerChatState) {
|
||||||
@ -2212,7 +2212,7 @@ final class PostboxImpl {
|
|||||||
}
|
}
|
||||||
|
|
||||||
fileprivate func setPeerChatState(_ id: PeerId, state: PeerChatState) {
|
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)
|
self.currentUpdatedPeerChatStates.insert(id)
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -2715,7 +2715,7 @@ final class PostboxImpl {
|
|||||||
let messages = self.getMessageGroup(at: id)
|
let messages = self.getMessageGroup(at: id)
|
||||||
additionalDataEntries.append(.message(id, messages ?? []))
|
additionalDataEntries.append(.message(id, messages ?? []))
|
||||||
case let .peerChatState(peerId):
|
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:
|
case .totalUnreadState:
|
||||||
additionalDataEntries.append(.totalUnreadState(self.messageHistoryMetadataTable.getTotalUnreadState(groupId: .root)))
|
additionalDataEntries.append(.totalUnreadState(self.messageHistoryMetadataTable.getTotalUnreadState(groupId: .root)))
|
||||||
case let .peerNotificationSettings(peerId):
|
case let .peerNotificationSettings(peerId):
|
||||||
|
|||||||
@ -5,6 +5,7 @@ public protocol PostboxView {
|
|||||||
|
|
||||||
protocol MutablePostboxView {
|
protocol MutablePostboxView {
|
||||||
func replay(postbox: PostboxImpl, transaction: PostboxTransaction) -> Bool
|
func replay(postbox: PostboxImpl, transaction: PostboxTransaction) -> Bool
|
||||||
|
func refreshDueToExternalTransaction(postbox: PostboxImpl) -> Bool
|
||||||
func immutableView() -> PostboxView
|
func immutableView() -> PostboxView
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -25,6 +26,16 @@ final class CombinedMutableView {
|
|||||||
return updated
|
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 {
|
func immutableView() -> CombinedView {
|
||||||
var result: [PostboxViewKey: PostboxView] = [:]
|
var result: [PostboxViewKey: PostboxView] = [:]
|
||||||
for (key, view) in self.views {
|
for (key, view) in self.views {
|
||||||
|
|||||||
@ -13,11 +13,37 @@ public final class CodableEntry: Equatable {
|
|||||||
self.data = encoder.makeData()
|
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? {
|
public func get<T: Decodable>(_ type: T.Type) -> T? {
|
||||||
let decoder = PostboxDecoder(buffer: MemoryBuffer(data: self.data))
|
let decoder = PostboxDecoder(buffer: MemoryBuffer(data: self.data))
|
||||||
return decoder.decode(T.self, forKey: "_")
|
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 {
|
public static func ==(lhs: CodableEntry, rhs: CodableEntry) -> Bool {
|
||||||
return lhs.data == rhs.data
|
return lhs.data == rhs.data
|
||||||
}
|
}
|
||||||
|
|||||||
@ -45,6 +45,22 @@ final class MutablePreferencesView: MutablePostboxView {
|
|||||||
return updated
|
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 {
|
func immutableView() -> PostboxView {
|
||||||
return PreferencesView(self)
|
return PreferencesView(self)
|
||||||
}
|
}
|
||||||
|
|||||||
@ -27,6 +27,17 @@ final class MutableSynchronizeGroupMessageStatsView: MutablePostboxView {
|
|||||||
return updated
|
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 {
|
func immutableView() -> PostboxView {
|
||||||
return SynchronizeGroupMessageStatsView(self)
|
return SynchronizeGroupMessageStatsView(self)
|
||||||
}
|
}
|
||||||
|
|||||||
@ -30,6 +30,42 @@ final class MutableTopChatMessageView: MutablePostboxView {
|
|||||||
return updated
|
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 {
|
func immutableView() -> PostboxView {
|
||||||
return TopChatMessageView(self)
|
return TopChatMessageView(self)
|
||||||
}
|
}
|
||||||
|
|||||||
@ -6,10 +6,42 @@ public enum UnreadMessageCountsItem: Equatable {
|
|||||||
case peer(PeerId)
|
case peer(PeerId)
|
||||||
}
|
}
|
||||||
|
|
||||||
private enum MutableUnreadMessageCountsItemEntry {
|
private enum MutableUnreadMessageCountsItemEntry: Equatable {
|
||||||
case total((ValueBoxKey, PreferencesEntry?)?, ChatListTotalUnreadState)
|
case total((ValueBoxKey, PreferencesEntry?)?, ChatListTotalUnreadState)
|
||||||
case totalInGroup(PeerGroupId, ChatListTotalUnreadState)
|
case totalInGroup(PeerGroupId, ChatListTotalUnreadState)
|
||||||
case peer(PeerId, CombinedPeerReadState?)
|
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 {
|
public enum UnreadMessageCountsItemEntry {
|
||||||
@ -19,9 +51,12 @@ public enum UnreadMessageCountsItemEntry {
|
|||||||
}
|
}
|
||||||
|
|
||||||
final class MutableUnreadMessageCountsView: MutablePostboxView {
|
final class MutableUnreadMessageCountsView: MutablePostboxView {
|
||||||
|
private let items: [UnreadMessageCountsItem]
|
||||||
fileprivate var entries: [MutableUnreadMessageCountsItemEntry]
|
fileprivate var entries: [MutableUnreadMessageCountsItemEntry]
|
||||||
|
|
||||||
init(postbox: PostboxImpl, items: [UnreadMessageCountsItem]) {
|
init(postbox: PostboxImpl, items: [UnreadMessageCountsItem]) {
|
||||||
|
self.items = items
|
||||||
|
|
||||||
self.entries = items.map { item in
|
self.entries = items.map { item in
|
||||||
switch item {
|
switch item {
|
||||||
case let .total(preferencesKey):
|
case let .total(preferencesKey):
|
||||||
@ -81,6 +116,25 @@ final class MutableUnreadMessageCountsView: MutablePostboxView {
|
|||||||
return updated
|
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 {
|
func immutableView() -> PostboxView {
|
||||||
return UnreadMessageCountsView(self)
|
return UnreadMessageCountsView(self)
|
||||||
}
|
}
|
||||||
@ -152,6 +206,16 @@ final class MutableCombinedReadStateView: MutablePostboxView {
|
|||||||
return updated
|
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 {
|
func immutableView() -> PostboxView {
|
||||||
return CombinedReadStateView(self)
|
return CombinedReadStateView(self)
|
||||||
}
|
}
|
||||||
|
|||||||
@ -62,7 +62,7 @@ private final class BubbleSettingsControllerNode: ASDisplayNode, UIScrollViewDel
|
|||||||
|
|
||||||
self.scrollNode = ASScrollNode()
|
self.scrollNode = ASScrollNode()
|
||||||
|
|
||||||
self.chatBackgroundNode = WallpaperBackgroundNode(context: context)
|
self.chatBackgroundNode = createWallpaperBackgroundNode(context: context, forChatDisplay: false)
|
||||||
self.chatBackgroundNode.displaysAsynchronously = false
|
self.chatBackgroundNode.displaysAsynchronously = false
|
||||||
|
|
||||||
self.messagesContainerNode = ASDisplayNode()
|
self.messagesContainerNode = ASDisplayNode()
|
||||||
|
|||||||
@ -132,7 +132,7 @@ class ForwardPrivacyChatPreviewItemNode: ListViewItemNode {
|
|||||||
|
|
||||||
return { item, params, neighbors in
|
return { item, params, neighbors in
|
||||||
if currentBackgroundNode == nil {
|
if currentBackgroundNode == nil {
|
||||||
currentBackgroundNode = WallpaperBackgroundNode(context: item.context)
|
currentBackgroundNode = createWallpaperBackgroundNode(context: item.context, forChatDisplay: false)
|
||||||
}
|
}
|
||||||
currentBackgroundNode?.update(wallpaper: item.wallpaper)
|
currentBackgroundNode?.update(wallpaper: item.wallpaper)
|
||||||
currentBackgroundNode?.updateBubbleTheme(bubbleTheme: item.theme, bubbleCorners: item.chatBubbleCorners)
|
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.pageControlNode = PageControlNode(dotSpacing: 7.0, dotColor: .white, inactiveDotColor: UIColor.white.withAlphaComponent(0.4))
|
||||||
|
|
||||||
self.chatListBackgroundNode = ASDisplayNode()
|
self.chatListBackgroundNode = ASDisplayNode()
|
||||||
self.chatBackgroundNode = WallpaperBackgroundNode(context: context)
|
self.chatBackgroundNode = createWallpaperBackgroundNode(context: context, forChatDisplay: false)
|
||||||
self.chatBackgroundNode.displaysAsynchronously = false
|
self.chatBackgroundNode.displaysAsynchronously = false
|
||||||
|
|
||||||
self.messagesContainerNode = ASDisplayNode()
|
self.messagesContainerNode = ASDisplayNode()
|
||||||
|
|||||||
@ -174,7 +174,7 @@ final class SettingsThemeWallpaperNode: ASDisplayNode {
|
|||||||
|
|
||||||
if let gradientNode = self.gradientNode {
|
if let gradientNode = self.gradientNode {
|
||||||
gradientNode.frame = CGRect(origin: CGPoint(), size: size)
|
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
|
let progressDiameter: CGFloat = 50.0
|
||||||
|
|||||||
@ -285,7 +285,7 @@ final class ThemeAccentColorControllerNode: ASDisplayNode, UIScrollViewDelegate
|
|||||||
self.backgroundContainerNode = ASDisplayNode()
|
self.backgroundContainerNode = ASDisplayNode()
|
||||||
self.backgroundContainerNode.clipsToBounds = true
|
self.backgroundContainerNode.clipsToBounds = true
|
||||||
self.backgroundWrapperNode = ASDisplayNode()
|
self.backgroundWrapperNode = ASDisplayNode()
|
||||||
self.backgroundNode = WallpaperBackgroundNode(context: context)
|
self.backgroundNode = createWallpaperBackgroundNode(context: context, forChatDisplay: false)
|
||||||
|
|
||||||
self.messagesContainerNode = ASDisplayNode()
|
self.messagesContainerNode = ASDisplayNode()
|
||||||
self.messagesContainerNode.clipsToBounds = true
|
self.messagesContainerNode.clipsToBounds = true
|
||||||
@ -1354,7 +1354,7 @@ final class ThemeAccentColorControllerNode: ASDisplayNode, UIScrollViewDelegate
|
|||||||
|
|
||||||
@objc private func playPressed() {
|
@objc private func playPressed() {
|
||||||
if self.state.backgroundColors.count >= 3 || self.state.messagesColors.count >= 3 {
|
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 {
|
} else {
|
||||||
self.updateState({ state in
|
self.updateState({ state in
|
||||||
var state = state
|
var state = state
|
||||||
|
|||||||
@ -107,7 +107,7 @@ final class ThemePreviewControllerNode: ASDisplayNode, UIScrollViewDelegate {
|
|||||||
self.messagesContainerNode.clipsToBounds = true
|
self.messagesContainerNode.clipsToBounds = true
|
||||||
self.messagesContainerNode.transform = CATransform3DMakeScale(1.0, -1.0, 1.0)
|
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.instantChatBackgroundNode.displaysAsynchronously = false
|
||||||
|
|
||||||
self.ready.set(.single(true))
|
self.ready.set(.single(true))
|
||||||
@ -121,7 +121,7 @@ final class ThemePreviewControllerNode: ASDisplayNode, UIScrollViewDelegate {
|
|||||||
self.blurredNode = BlurredImageNode()
|
self.blurredNode = BlurredImageNode()
|
||||||
self.blurredNode.blurView.contentMode = .scaleAspectFill
|
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)
|
self.toolbarNode = WallpaperGalleryToolbarNode(theme: self.previewTheme, strings: self.presentationData.strings, doneButtonType: .set)
|
||||||
|
|
||||||
|
|||||||
@ -138,7 +138,7 @@ class ThemeSettingsChatPreviewItemNode: ListViewItemNode {
|
|||||||
|
|
||||||
return { item, params, neighbors in
|
return { item, params, neighbors in
|
||||||
if currentBackgroundNode == nil {
|
if currentBackgroundNode == nil {
|
||||||
currentBackgroundNode = WallpaperBackgroundNode(context: item.context)
|
currentBackgroundNode = createWallpaperBackgroundNode(context: item.context, forChatDisplay: false)
|
||||||
}
|
}
|
||||||
currentBackgroundNode?.update(wallpaper: item.wallpaper)
|
currentBackgroundNode?.update(wallpaper: item.wallpaper)
|
||||||
currentBackgroundNode?.updateBubbleTheme(bubbleTheme: item.componentTheme, bubbleCorners: item.chatBubbleCorners)
|
currentBackgroundNode?.updateBubbleTheme(bubbleTheme: item.componentTheme, bubbleCorners: item.chatBubbleCorners)
|
||||||
|
|||||||
@ -136,7 +136,7 @@ final class WallpaperGalleryItemNode: GalleryItemNode {
|
|||||||
self.wrapperNode = ASDisplayNode()
|
self.wrapperNode = ASDisplayNode()
|
||||||
self.imageNode = TransformImageNode()
|
self.imageNode = TransformImageNode()
|
||||||
self.imageNode.contentAnimations = .subsequentUpdates
|
self.imageNode.contentAnimations = .subsequentUpdates
|
||||||
self.nativeNode = WallpaperBackgroundNode(context: context)
|
self.nativeNode = createWallpaperBackgroundNode(context: context, forChatDisplay: false)
|
||||||
self.cropNode = WallpaperCropNode()
|
self.cropNode = WallpaperCropNode()
|
||||||
self.statusNode = RadialStatusNode(backgroundNodeColor: UIColor(white: 0.0, alpha: 0.6))
|
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)
|
self.statusNode.frame = CGRect(x: 0.0, y: 0.0, width: progressDiameter, height: progressDiameter)
|
||||||
@ -812,7 +812,7 @@ final class WallpaperGalleryItemNode: GalleryItemNode {
|
|||||||
switch wallpaper {
|
switch wallpaper {
|
||||||
case let .gradient(gradient):
|
case let .gradient(gradient):
|
||||||
if gradient.colors.count >= 3 {
|
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 {
|
} else {
|
||||||
let rotation = gradient.settings.rotation ?? 0
|
let rotation = gradient.settings.rotation ?? 0
|
||||||
self.requestRotateGradient?((rotation + 90) % 360)
|
self.requestRotateGradient?((rotation + 90) % 360)
|
||||||
@ -820,7 +820,7 @@ final class WallpaperGalleryItemNode: GalleryItemNode {
|
|||||||
case let .file(file):
|
case let .file(file):
|
||||||
if file.isPattern {
|
if file.isPattern {
|
||||||
if file.settings.colors.count >= 3 {
|
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 {
|
} else {
|
||||||
let rotation = file.settings.rotation ?? 0
|
let rotation = file.settings.rotation ?? 0
|
||||||
self.requestRotateGradient?((rotation + 90) % 360)
|
self.requestRotateGradient?((rotation + 90) % 360)
|
||||||
|
|||||||
@ -964,7 +964,7 @@ final class MessageStoryRenderer {
|
|||||||
|
|
||||||
self.containerNode = ASDisplayNode()
|
self.containerNode = ASDisplayNode()
|
||||||
|
|
||||||
self.instantChatBackgroundNode = WallpaperBackgroundNode(context: context)
|
self.instantChatBackgroundNode = createWallpaperBackgroundNode(context: context, forChatDisplay: false)
|
||||||
self.instantChatBackgroundNode.displaysAsynchronously = false
|
self.instantChatBackgroundNode.displaysAsynchronously = false
|
||||||
|
|
||||||
self.messagesContainerNode = ASDisplayNode()
|
self.messagesContainerNode = ASDisplayNode()
|
||||||
|
|||||||
@ -7,6 +7,6 @@
|
|||||||
NSData * _Nullable prepareSvgImage(NSData * _Nonnull data);
|
NSData * _Nullable prepareSvgImage(NSData * _Nonnull data);
|
||||||
UIImage * _Nullable renderPreparedImage(NSData * _Nonnull data, CGSize size);
|
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 */
|
#endif /* Lottie_h */
|
||||||
|
|||||||
@ -83,7 +83,7 @@ CGSize aspectFillSize(CGSize size, CGSize bounds) {
|
|||||||
|
|
||||||
@end
|
@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];
|
NSDate *startTime = [NSDate date];
|
||||||
|
|
||||||
NSXMLParser *parser = [[NSXMLParser alloc] initWithData:data];
|
NSXMLParser *parser = [[NSXMLParser alloc] initWithData:data];
|
||||||
@ -116,7 +116,7 @@ UIImage * _Nullable drawSvgImage(NSData * _Nonnull data, CGSize size, UIColor *b
|
|||||||
|
|
||||||
startTime = [NSDate date];
|
startTime = [NSDate date];
|
||||||
|
|
||||||
UIGraphicsBeginImageContextWithOptions(size, true, 1.0);
|
UIGraphicsBeginImageContextWithOptions(size, opaque, 1.0);
|
||||||
CGContextRef context = UIGraphicsGetCurrentContext();
|
CGContextRef context = UIGraphicsGetCurrentContext();
|
||||||
CGContextSetFillColorWithColor(context, backgroundColor.CGColor);
|
CGContextSetFillColorWithColor(context, backgroundColor.CGColor);
|
||||||
CGContextFillRect(context, CGRectMake(0.0f, 0.0f, size.width, size.height));
|
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 {
|
} else {
|
||||||
pollPts = 1
|
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)
|
|> map(Optional.init)
|
||||||
|> `catch` { error -> Signal<Api.updates.ChannelDifference?, MTRpcError> in
|
|> `catch` { error -> Signal<Api.updates.ChannelDifference?, MTRpcError> in
|
||||||
switch error.errorDescription {
|
switch error.errorDescription {
|
||||||
|
|||||||
@ -181,8 +181,12 @@ private func synchronizeMarkAllUnseen(transaction: Transaction, postbox: Postbox
|
|||||||
)
|
)
|
||||||
|> mapToSignal { resultId -> Signal<Void, GetUnseenIdsError> in
|
|> mapToSignal { resultId -> Signal<Void, GetUnseenIdsError> in
|
||||||
if let resultId = resultId {
|
if let resultId = resultId {
|
||||||
let _ = currentMaxId.swap(resultId)
|
let previous = currentMaxId.swap(resultId)
|
||||||
|
if previous == resultId {
|
||||||
|
return .fail(.done)
|
||||||
|
} else {
|
||||||
return .complete()
|
return .complete()
|
||||||
|
}
|
||||||
} else {
|
} else {
|
||||||
return .fail(.done)
|
return .fail(.done)
|
||||||
}
|
}
|
||||||
|
|||||||
@ -74,11 +74,11 @@ public struct Namespaces {
|
|||||||
public static let cachedPollResults: Int8 = 9
|
public static let cachedPollResults: Int8 = 9
|
||||||
public static let cachedContextResults: Int8 = 10
|
public static let cachedContextResults: Int8 = 10
|
||||||
public static let proximityNotificationStoredState: Int8 = 11
|
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 cachedGroupCallDisplayAsPeers: Int8 = 14
|
||||||
public static let cachedAdMessageStates: Int8 = 15
|
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 {
|
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 contactSettings: ContactSynchronizationSettings = transaction.getSharedData(ApplicationSpecificSharedDataKeys.contactSynchronizationSettings)?.get(ContactSynchronizationSettings.self) ?? ContactSynchronizationSettings.defaultSettings
|
||||||
|
|
||||||
let effectiveTheme: PresentationThemeReference
|
let effectiveTheme: PresentationThemeReference
|
||||||
|
var preferredBaseTheme: TelegramBaseTheme?
|
||||||
let parameters = AutomaticThemeSwitchParameters(settings: themeSettings.automaticThemeSwitchSetting)
|
let parameters = AutomaticThemeSwitchParameters(settings: themeSettings.automaticThemeSwitchSetting)
|
||||||
let autoNightModeTriggered: Bool
|
let autoNightModeTriggered: Bool
|
||||||
if automaticThemeShouldSwitchNow(parameters, systemUserInterfaceStyle: systemUserInterfaceStyle) {
|
if automaticThemeShouldSwitchNow(parameters, systemUserInterfaceStyle: systemUserInterfaceStyle) {
|
||||||
effectiveTheme = themeSettings.automaticThemeSwitchSetting.theme
|
effectiveTheme = themeSettings.automaticThemeSwitchSetting.theme
|
||||||
autoNightModeTriggered = true
|
autoNightModeTriggered = true
|
||||||
|
|
||||||
|
if let baseTheme = themeSettings.themePreferredBaseTheme[effectiveTheme.index], [.night, .tinted].contains(baseTheme) {
|
||||||
|
preferredBaseTheme = baseTheme
|
||||||
|
} else {
|
||||||
|
preferredBaseTheme = .night
|
||||||
|
}
|
||||||
} else {
|
} else {
|
||||||
effectiveTheme = themeSettings.theme
|
effectiveTheme = themeSettings.theme
|
||||||
autoNightModeTriggered = false
|
autoNightModeTriggered = false
|
||||||
|
|
||||||
|
if let baseTheme = themeSettings.themePreferredBaseTheme[effectiveTheme.index], [.classic, .day].contains(baseTheme) {
|
||||||
|
preferredBaseTheme = baseTheme
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
let effectiveColors = themeSettings.themeSpecificAccentColors[effectiveTheme.index]
|
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
|
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
|
self.pushRegistry = pushRegistry
|
||||||
pushRegistry.delegate = self
|
pushRegistry.delegate = self
|
||||||
|
|
||||||
self.badgeDisposable.set((self.context.get()
|
self.resetBadge()
|
||||||
|> 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)
|
|
||||||
}))
|
|
||||||
|
|
||||||
if #available(iOS 9.1, *) {
|
if #available(iOS 9.1, *) {
|
||||||
self.quickActionsDisposable.set((self.context.get()
|
self.quickActionsDisposable.set((self.context.get()
|
||||||
@ -1281,6 +1271,20 @@ private func extractAccountManagerState(records: AccountRecordsView<TelegramAcco
|
|||||||
return true
|
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) {
|
func applicationWillResignActive(_ application: UIApplication) {
|
||||||
self.isActiveValue = false
|
self.isActiveValue = false
|
||||||
self.isActivePromise.set(false)
|
self.isActivePromise.set(false)
|
||||||
@ -1362,6 +1366,8 @@ private func extractAccountManagerState(records: AccountRecordsView<TelegramAcco
|
|||||||
self.isActiveValue = true
|
self.isActiveValue = true
|
||||||
self.isActivePromise.set(true)
|
self.isActivePromise.set(true)
|
||||||
|
|
||||||
|
self.resetBadge()
|
||||||
|
|
||||||
self.maybeCheckForUpdates()
|
self.maybeCheckForUpdates()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@ -508,7 +508,7 @@ public final class ChatControllerImpl: TelegramBaseController, ChatController, G
|
|||||||
default:
|
default:
|
||||||
break
|
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)
|
self.wallpaperReady.set(self.chatBackgroundNode.isReady)
|
||||||
|
|
||||||
var locationBroadcastPanelSource: LocationBroadcastPanelSource
|
var locationBroadcastPanelSource: LocationBroadcastPanelSource
|
||||||
@ -4998,8 +4998,17 @@ public final class ChatControllerImpl: TelegramBaseController, ChatController, G
|
|||||||
var minOffsetForNavigation: CGFloat = 40.0
|
var minOffsetForNavigation: CGFloat = 40.0
|
||||||
strongSelf.chatDisplayNode.historyNode.enumerateItemNodes { itemNode in
|
strongSelf.chatDisplayNode.historyNode.enumerateItemNodes { itemNode in
|
||||||
if let itemNode = itemNode as? ChatMessageBubbleItemNode {
|
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
|
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
|
return false
|
||||||
|
|||||||
@ -370,7 +370,7 @@ class ChatControllerNode: ASDisplayNode, UIScrollViewDelegate {
|
|||||||
if (strongSelf.context.sharedContext.currentPresentationData.with({ $0 })).reduceMotion {
|
if (strongSelf.context.sharedContext.currentPresentationData.with({ $0 })).reduceMotion {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
strongSelf.backgroundNode.animateEvent(transition: transition)
|
strongSelf.backgroundNode.animateEvent(transition: transition, extendAnimation: false)
|
||||||
}
|
}
|
||||||
|
|
||||||
getMessageTransitionNode = { [weak self] in
|
getMessageTransitionNode = { [weak self] in
|
||||||
@ -1648,7 +1648,7 @@ class ChatControllerNode: ASDisplayNode, UIScrollViewDelegate {
|
|||||||
if (self.context.sharedContext.currentPresentationData.with({ $0 })).reduceMotion {
|
if (self.context.sharedContext.currentPresentationData.with({ $0 })).reduceMotion {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
self.backgroundNode.animateEvent(transition: transition)
|
self.backgroundNode.animateEvent(transition: transition, extendAnimation: false)
|
||||||
}
|
}
|
||||||
//self.historyNode.didScrollWithOffset?(listBottomInset - previousListBottomInset, transition, nil)
|
//self.historyNode.didScrollWithOffset?(listBottomInset - previousListBottomInset, transition, nil)
|
||||||
}
|
}
|
||||||
|
|||||||
@ -469,7 +469,7 @@ public final class ChatHistoryListNode: ListView, ChatHistoryNode {
|
|||||||
private let galleryHiddenMesageAndMediaDisposable = MetaDisposable()
|
private let galleryHiddenMesageAndMediaDisposable = MetaDisposable()
|
||||||
|
|
||||||
private let messageProcessingManager = ChatMessageThrottledProcessingManager()
|
private let messageProcessingManager = ChatMessageThrottledProcessingManager()
|
||||||
private let adSeenProcessingManager = ChatMessageThrottledProcessingManager()
|
let adSeenProcessingManager = ChatMessageThrottledProcessingManager()
|
||||||
private let seenLiveLocationProcessingManager = ChatMessageThrottledProcessingManager()
|
private let seenLiveLocationProcessingManager = ChatMessageThrottledProcessingManager()
|
||||||
private let unsupportedMessageProcessingManager = ChatMessageThrottledProcessingManager()
|
private let unsupportedMessageProcessingManager = ChatMessageThrottledProcessingManager()
|
||||||
private let refreshMediaProcessingManager = ChatMessageThrottledProcessingManager()
|
private let refreshMediaProcessingManager = ChatMessageThrottledProcessingManager()
|
||||||
@ -611,7 +611,7 @@ public final class ChatHistoryListNode: ListView, ChatHistoryNode {
|
|||||||
return
|
return
|
||||||
}
|
}
|
||||||
var adPeerId: PeerId?
|
var adPeerId: PeerId?
|
||||||
adPeerId = nil
|
adPeerId = messages.first?.author?.id
|
||||||
|
|
||||||
if strongSelf.preloadAdPeerId != adPeerId {
|
if strongSelf.preloadAdPeerId != adPeerId {
|
||||||
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)
|
let toLaterRange = (historyView.filteredEntries.count - 1 - (visible.firstIndex - 1), historyView.filteredEntries.count - 1)
|
||||||
|
|
||||||
var messageIdsWithViewCount: [MessageId] = []
|
var messageIdsWithViewCount: [MessageId] = []
|
||||||
var messageIdsWithAds: [MessageId] = []
|
|
||||||
var messageIdsWithLiveLocation: [MessageId] = []
|
var messageIdsWithLiveLocation: [MessageId] = []
|
||||||
var messageIdsWithUnsupportedMedia: [MessageId] = []
|
var messageIdsWithUnsupportedMedia: [MessageId] = []
|
||||||
var messageIdsWithRefreshMedia: [MessageId] = []
|
var messageIdsWithRefreshMedia: [MessageId] = []
|
||||||
@ -1589,8 +1588,6 @@ public final class ChatHistoryListNode: ListView, ChatHistoryNode {
|
|||||||
if message.id.namespace == Namespaces.Message.Cloud {
|
if message.id.namespace == Namespaces.Message.Cloud {
|
||||||
messageIdsWithViewCount.append(message.id)
|
messageIdsWithViewCount.append(message.id)
|
||||||
}
|
}
|
||||||
} else if attribute is AdMessageAttribute {
|
|
||||||
messageIdsWithAds.append(message.id)
|
|
||||||
} else if attribute is ReplyThreadMessageAttribute {
|
} else if attribute is ReplyThreadMessageAttribute {
|
||||||
if message.id.namespace == Namespaces.Message.Cloud {
|
if message.id.namespace == Namespaces.Message.Cloud {
|
||||||
messageIdsWithViewCount.append(message.id)
|
messageIdsWithViewCount.append(message.id)
|
||||||
@ -1754,9 +1751,6 @@ public final class ChatHistoryListNode: ListView, ChatHistoryNode {
|
|||||||
if !messageIdsWithViewCount.isEmpty {
|
if !messageIdsWithViewCount.isEmpty {
|
||||||
self.messageProcessingManager.add(messageIdsWithViewCount)
|
self.messageProcessingManager.add(messageIdsWithViewCount)
|
||||||
}
|
}
|
||||||
if !messageIdsWithAds.isEmpty {
|
|
||||||
self.adSeenProcessingManager.add(messageIdsWithAds)
|
|
||||||
}
|
|
||||||
if !messageIdsWithLiveLocation.isEmpty {
|
if !messageIdsWithLiveLocation.isEmpty {
|
||||||
self.seenLiveLocationProcessingManager.add(messageIdsWithLiveLocation)
|
self.seenLiveLocationProcessingManager.add(messageIdsWithLiveLocation)
|
||||||
}
|
}
|
||||||
|
|||||||
@ -24,7 +24,7 @@ private func attributedServiceMessageString(theme: ChatPresentationThemeData, st
|
|||||||
|
|
||||||
class ChatMessageActionBubbleContentNode: ChatMessageBubbleContentNode {
|
class ChatMessageActionBubbleContentNode: ChatMessageBubbleContentNode {
|
||||||
let labelNode: TextNode
|
let labelNode: TextNode
|
||||||
var backgroundNode: WallpaperBackgroundNode.BubbleBackgroundNode?
|
var backgroundNode: WallpaperBubbleBackgroundNode?
|
||||||
var backgroundColorNode: ASDisplayNode
|
var backgroundColorNode: ASDisplayNode
|
||||||
let backgroundMaskNode: ASImageNode
|
let backgroundMaskNode: ASImageNode
|
||||||
var linkHighlightingNode: LinkHighlightingNode?
|
var linkHighlightingNode: LinkHighlightingNode?
|
||||||
@ -327,7 +327,7 @@ class ChatMessageActionBubbleContentNode: ChatMessageBubbleContentNode {
|
|||||||
var backgroundFrame = backgroundNode.frame
|
var backgroundFrame = backgroundNode.frame
|
||||||
backgroundFrame.origin.x += rect.minX
|
backgroundFrame.origin.x += rect.minX
|
||||||
backgroundFrame.origin.y += rect.minY
|
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
|
private let containerNode: ContextControllerSourceNode
|
||||||
let imageNode: TransformImageNode
|
let imageNode: TransformImageNode
|
||||||
private var enableSynchronousImageApply: Bool = false
|
private var enableSynchronousImageApply: Bool = false
|
||||||
private var backgroundNode: WallpaperBackgroundNode.BubbleBackgroundNode?
|
private var backgroundNode: WallpaperBubbleBackgroundNode?
|
||||||
private(set) var placeholderNode: StickerShimmerEffectNode
|
private(set) var placeholderNode: StickerShimmerEffectNode
|
||||||
private(set) var animationNode: GenericAnimatedStickerNode?
|
private(set) var animationNode: GenericAnimatedStickerNode?
|
||||||
private var animationSize: CGSize?
|
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)
|
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 {
|
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 {
|
final class ChatMessageBubbleBackdrop: ASDisplayNode {
|
||||||
private var backgroundContent: WallpaperBackgroundNode.BubbleBackgroundNode?
|
private var backgroundContent: WallpaperBubbleBackgroundNode?
|
||||||
|
|
||||||
private var currentType: ChatMessageBackgroundType?
|
private var currentType: ChatMessageBackgroundType?
|
||||||
private var currentMaskMode: Bool?
|
private var currentMaskMode: Bool?
|
||||||
@ -86,7 +86,7 @@ final class ChatMessageBubbleBackdrop: ASDisplayNode {
|
|||||||
var backgroundFrame = backgroundContent.frame
|
var backgroundFrame = backgroundContent.frame
|
||||||
backgroundFrame.origin.x += rect.minX
|
backgroundFrame.origin.x += rect.minX
|
||||||
backgroundFrame.origin.y += rect.minY
|
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
|
var backgroundFrame = backgroundContent.frame
|
||||||
backgroundFrame.origin.x += rect.minX
|
backgroundFrame.origin.x += rect.minX
|
||||||
backgroundFrame.origin.y += rect.minY
|
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
|
var backgroundFrame = backgroundContent.frame
|
||||||
backgroundFrame.origin.x += rect.minX
|
backgroundFrame.origin.x += rect.minX
|
||||||
backgroundFrame.origin.y += rect.minY
|
backgroundFrame.origin.y += rect.minY
|
||||||
backgroundContent.update(rect: backgroundFrame, within: containerSize)
|
backgroundContent.update(rect: backgroundFrame, within: containerSize, transition: .immediate)
|
||||||
}
|
}
|
||||||
self.backgroundContent = backgroundContent
|
self.backgroundContent = backgroundContent
|
||||||
self.insertSubnode(backgroundContent, at: 0)
|
self.insertSubnode(backgroundContent, at: 0)
|
||||||
@ -174,7 +174,7 @@ final class ChatMessageBubbleBackdrop: ASDisplayNode {
|
|||||||
var backgroundFrame = backgroundContent.frame
|
var backgroundFrame = backgroundContent.frame
|
||||||
backgroundFrame.origin.x += rect.minX
|
backgroundFrame.origin.x += rect.minX
|
||||||
backgroundFrame.origin.y += rect.minY
|
backgroundFrame.origin.y += rect.minY
|
||||||
backgroundContent.update(rect: backgroundFrame, within: containerSize)
|
backgroundContent.update(rect: backgroundFrame, within: containerSize, transition: .immediate)
|
||||||
}
|
}
|
||||||
self.backgroundContent = backgroundContent
|
self.backgroundContent = backgroundContent
|
||||||
self.insertSubnode(backgroundContent, at: 0)
|
self.insertSubnode(backgroundContent, at: 0)
|
||||||
|
|||||||
@ -3321,6 +3321,9 @@ class ChatMessageBubbleItemNode: ChatMessageItemView, ChatMessagePreviewItemNode
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
if message.adAttribute != nil {
|
||||||
|
canHaveSelection = false
|
||||||
|
}
|
||||||
default:
|
default:
|
||||||
break
|
break
|
||||||
}
|
}
|
||||||
|
|||||||
@ -22,7 +22,7 @@ class ChatMessageStickerItemNode: ChatMessageItemView {
|
|||||||
let contextSourceNode: ContextExtractedContentContainingNode
|
let contextSourceNode: ContextExtractedContentContainingNode
|
||||||
private let containerNode: ContextControllerSourceNode
|
private let containerNode: ContextControllerSourceNode
|
||||||
let imageNode: TransformImageNode
|
let imageNode: TransformImageNode
|
||||||
private var backgroundNode: WallpaperBackgroundNode.BubbleBackgroundNode?
|
private var backgroundNode: WallpaperBubbleBackgroundNode?
|
||||||
private var placeholderNode: StickerShimmerEffectNode
|
private var placeholderNode: StickerShimmerEffectNode
|
||||||
var textNode: TextNode?
|
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)
|
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 {
|
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 {
|
if lhs.withinSize != rhs.withinSize {
|
||||||
return false
|
return false
|
||||||
}
|
}
|
||||||
if lhs.wallpaperNode != rhs.wallpaperNode {
|
if lhs.wallpaperNode !== rhs.wallpaperNode {
|
||||||
return false
|
return false
|
||||||
}
|
}
|
||||||
return true
|
return true
|
||||||
@ -564,7 +564,7 @@ final class AvatarComponent: Component {
|
|||||||
}
|
}
|
||||||
|
|
||||||
private final class WallpaperBlurNode: ASDisplayNode {
|
private final class WallpaperBlurNode: ASDisplayNode {
|
||||||
private var backgroundNode: WallpaperBackgroundNode.BubbleBackgroundNode?
|
private var backgroundNode: WallpaperBubbleBackgroundNode?
|
||||||
private let colorNode: ASDisplayNode
|
private let colorNode: ASDisplayNode
|
||||||
|
|
||||||
override init() {
|
override init() {
|
||||||
|
|||||||
@ -99,7 +99,7 @@ final class ChatRecentActionsControllerNode: ViewControllerTracingNode {
|
|||||||
|
|
||||||
self.automaticMediaDownloadSettings = context.sharedContext.currentAutomaticMediaDownloadSettings.with { $0 }
|
self.automaticMediaDownloadSettings = context.sharedContext.currentAutomaticMediaDownloadSettings.with { $0 }
|
||||||
|
|
||||||
self.backgroundNode = WallpaperBackgroundNode(context: context)
|
self.backgroundNode = createWallpaperBackgroundNode(context: context, forChatDisplay: true)
|
||||||
self.backgroundNode.isUserInteractionEnabled = false
|
self.backgroundNode.isUserInteractionEnabled = false
|
||||||
|
|
||||||
self.panelBackgroundNode = NavigationBackgroundNode(color: self.presentationData.theme.chat.inputPanel.panelBackgroundColor)
|
self.panelBackgroundNode = NavigationBackgroundNode(color: self.presentationData.theme.chat.inputPanel.panelBackgroundColor)
|
||||||
|
|||||||
@ -64,7 +64,7 @@ class ChatReplyCountItem: ListViewItem {
|
|||||||
class ChatReplyCountItemNode: ListViewItemNode {
|
class ChatReplyCountItemNode: ListViewItemNode {
|
||||||
var item: ChatReplyCountItem?
|
var item: ChatReplyCountItem?
|
||||||
private let labelNode: TextNode
|
private let labelNode: TextNode
|
||||||
private var backgroundNode: WallpaperBackgroundNode.BubbleBackgroundNode?
|
private var backgroundNode: WallpaperBubbleBackgroundNode?
|
||||||
private let backgroundColorNode: ASDisplayNode
|
private let backgroundColorNode: ASDisplayNode
|
||||||
|
|
||||||
private var theme: ChatPresentationThemeData?
|
private var theme: ChatPresentationThemeData?
|
||||||
@ -201,7 +201,7 @@ class ChatReplyCountItemNode: ListViewItemNode {
|
|||||||
backgroundFrame.origin.x += rect.minX
|
backgroundFrame.origin.x += rect.minX
|
||||||
backgroundFrame.origin.y += rect.minY
|
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 experimentalCompatibility: Bool
|
||||||
public var enableDebugDataDisplay: Bool
|
public var enableDebugDataDisplay: Bool
|
||||||
public var acceleratedStickers: Bool
|
public var acceleratedStickers: Bool
|
||||||
public var mockICE: Bool
|
public var experimentalBackground: Bool
|
||||||
|
|
||||||
public static var defaultSettings: ExperimentalUISettings {
|
public static var defaultSettings: ExperimentalUISettings {
|
||||||
return ExperimentalUISettings(
|
return ExperimentalUISettings(
|
||||||
@ -36,7 +36,7 @@ public struct ExperimentalUISettings: Codable, Equatable {
|
|||||||
experimentalCompatibility: false,
|
experimentalCompatibility: false,
|
||||||
enableDebugDataDisplay: false,
|
enableDebugDataDisplay: false,
|
||||||
acceleratedStickers: false,
|
acceleratedStickers: false,
|
||||||
mockICE: false
|
experimentalBackground: false
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -55,7 +55,7 @@ public struct ExperimentalUISettings: Codable, Equatable {
|
|||||||
experimentalCompatibility: Bool,
|
experimentalCompatibility: Bool,
|
||||||
enableDebugDataDisplay: Bool,
|
enableDebugDataDisplay: Bool,
|
||||||
acceleratedStickers: Bool,
|
acceleratedStickers: Bool,
|
||||||
mockICE: Bool
|
experimentalBackground: Bool
|
||||||
) {
|
) {
|
||||||
self.keepChatNavigationStack = keepChatNavigationStack
|
self.keepChatNavigationStack = keepChatNavigationStack
|
||||||
self.skipReadHistory = skipReadHistory
|
self.skipReadHistory = skipReadHistory
|
||||||
@ -71,7 +71,7 @@ public struct ExperimentalUISettings: Codable, Equatable {
|
|||||||
self.experimentalCompatibility = experimentalCompatibility
|
self.experimentalCompatibility = experimentalCompatibility
|
||||||
self.enableDebugDataDisplay = enableDebugDataDisplay
|
self.enableDebugDataDisplay = enableDebugDataDisplay
|
||||||
self.acceleratedStickers = acceleratedStickers
|
self.acceleratedStickers = acceleratedStickers
|
||||||
self.mockICE = mockICE
|
self.experimentalBackground = experimentalBackground
|
||||||
}
|
}
|
||||||
|
|
||||||
public init(from decoder: Decoder) throws {
|
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.experimentalCompatibility = (try container.decodeIfPresent(Int32.self, forKey: "experimentalCompatibility") ?? 0) != 0
|
||||||
self.enableDebugDataDisplay = (try container.decodeIfPresent(Int32.self, forKey: "enableDebugDataDisplay") ?? 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.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 {
|
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.experimentalCompatibility ? 1 : 0) as Int32, forKey: "experimentalCompatibility")
|
||||||
try container.encode((self.enableDebugDataDisplay ? 1 : 0) as Int32, forKey: "enableDebugDataDisplay")
|
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.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/SSignalKit/SwiftSignalKit:SwiftSignalKit",
|
||||||
"//submodules/WallpaperResources:WallpaperResources",
|
"//submodules/WallpaperResources:WallpaperResources",
|
||||||
"//submodules/FastBlur:FastBlur",
|
"//submodules/FastBlur:FastBlur",
|
||||||
|
"//submodules/Svg:Svg",
|
||||||
|
"//submodules/GZip:GZip",
|
||||||
|
"//submodules/AppBundle:AppBundle",
|
||||||
],
|
],
|
||||||
visibility = [
|
visibility = [
|
||||||
"//visibility:public",
|
"//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):
|
case let .pattern(data, colors, intensity):
|
||||||
let wallpaperImage = generateImage(arguments.drawingSize, rotatedContext: { size, context in
|
let wallpaperImage = generateImage(arguments.drawingSize, rotatedContext: { size, context in
|
||||||
drawWallpaperGradientImage(colors.map(UIColor.init(rgb:)), context: context, size: size)
|
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.setBlendMode(.softLight)
|
||||||
context.setAlpha(abs(CGFloat(intensity)) / 100.0)
|
context.setAlpha(abs(CGFloat(intensity)) / 100.0)
|
||||||
context.draw(image.cgImage!, in: CGRect(origin: CGPoint(), size: arguments.drawingSize))
|
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",
|
"bazel": "4.0.0",
|
||||||
"xcode": "13.0"
|
"xcode": "13.0"
|
||||||
}
|
}
|
||||||
|
|||||||
Loading…
x
Reference in New Issue
Block a user