diff --git a/Telegram/SiriIntents/IntentHandler.swift b/Telegram/SiriIntents/IntentHandler.swift index c71c957c1c..f492ef9884 100644 --- a/Telegram/SiriIntents/IntentHandler.swift +++ b/Telegram/SiriIntents/IntentHandler.swift @@ -22,9 +22,7 @@ private func setupSharedLogger(rootPath: String, path: String) { } } -private let accountAuxiliaryMethods = AccountAuxiliaryMethods(updatePeerChatInputState: { interfaceState, inputState -> PeerChatInterfaceState? in - return interfaceState -}, fetchResource: { account, resource, ranges, _ in +private let accountAuxiliaryMethods = AccountAuxiliaryMethods(fetchResource: { account, resource, ranges, _ in return nil }, fetchResourceMediaReferenceHash: { resource in return .single(nil) diff --git a/Telegram/WidgetKitWidget/TodayViewController.swift b/Telegram/WidgetKitWidget/TodayViewController.swift index 84bc6339c3..11ce25c07c 100644 --- a/Telegram/WidgetKitWidget/TodayViewController.swift +++ b/Telegram/WidgetKitWidget/TodayViewController.swift @@ -50,9 +50,7 @@ private func setupSharedLogger(rootPath: String, path: String) { } } -private let accountAuxiliaryMethods = AccountAuxiliaryMethods(updatePeerChatInputState: { interfaceState, inputState -> PeerChatInterfaceState? in - return interfaceState -}, fetchResource: { account, resource, ranges, _ in +private let accountAuxiliaryMethods = AccountAuxiliaryMethods(fetchResource: { account, resource, ranges, _ in return nil }, fetchResourceMediaReferenceHash: { resource in return .single(nil) diff --git a/submodules/AccountContext/Sources/ChatController.swift b/submodules/AccountContext/Sources/ChatController.swift index af7c095c8e..23a5bbf8b6 100644 --- a/submodules/AccountContext/Sources/ChatController.swift +++ b/submodules/AccountContext/Sources/ChatController.swift @@ -339,37 +339,6 @@ public enum ChatControllerPresentationMode: Equatable { case inline(NavigationController?) } -public final class ChatEmbeddedInterfaceState: PeerChatListEmbeddedInterfaceState { - public let timestamp: Int32 - public let text: NSAttributedString - - public init(timestamp: Int32, text: NSAttributedString) { - self.timestamp = timestamp - self.text = text - } - - public init(from decoder: Decoder) throws { - let container = try decoder.container(keyedBy: StringCodingKey.self) - self.timestamp = (try? container.decode(Int32.self, forKey: "d")) ?? 0 - self.text = ((try? container.decode(ChatTextInputStateText.self, forKey: "at")) ?? ChatTextInputStateText()).attributedText() - } - - public func encode(to encoder: Encoder) throws { - var container = encoder.container(keyedBy: StringCodingKey.self) - - try container.encode(self.timestamp, forKey: "d") - try container.encode(ChatTextInputStateText(attributedText: self.text), forKey: "at") - } - - public func isEqual(to: PeerChatListEmbeddedInterfaceState) -> Bool { - if let to = to as? ChatEmbeddedInterfaceState { - return self.timestamp == to.timestamp && self.text.isEqual(to: to.text) - } else { - return false - } - } -} - public enum ChatPresentationInputQueryResult: Equatable { case stickers([FoundStickerItem]) case hashtags([String]) diff --git a/submodules/ChatHistoryImportTasks/BUILD b/submodules/ChatHistoryImportTasks/BUILD index 6e0b39f4c3..0bb2b8f3b6 100644 --- a/submodules/ChatHistoryImportTasks/BUILD +++ b/submodules/ChatHistoryImportTasks/BUILD @@ -7,9 +7,6 @@ swift_library( "Sources/**/*.swift", ]), deps = [ - "//submodules/SSignalKit/SwiftSignalKit:SwiftSignalKit", - "//submodules/Postbox:Postbox", - "//submodules/TelegramCore:TelegramCore", ], visibility = [ "//visibility:public", diff --git a/submodules/ChatHistoryImportTasks/Sources/ChatHistoryImportTask.swift b/submodules/ChatHistoryImportTasks/Sources/ChatHistoryImportTask.swift index 2a50b6f8cb..66621571a3 100644 --- a/submodules/ChatHistoryImportTasks/Sources/ChatHistoryImportTask.swift +++ b/submodules/ChatHistoryImportTasks/Sources/ChatHistoryImportTask.swift @@ -1,14 +1,4 @@ import Foundation -import Postbox -import TelegramCore -import SwiftSignalKit public enum ChatHistoryImportTasks { - public final class Context { - - } - - public static func importState(peerId: PeerId) -> Signal { - return .single(nil) - } } diff --git a/submodules/ChatInterfaceState/Sources/ChatInterfaceState.swift b/submodules/ChatInterfaceState/Sources/ChatInterfaceState.swift index 9d14791fbc..3a7e8845e0 100644 --- a/submodules/ChatInterfaceState/Sources/ChatInterfaceState.swift +++ b/submodules/ChatInterfaceState/Sources/ChatInterfaceState.swift @@ -4,6 +4,7 @@ import Postbox import TelegramCore import TextFormat import AccountContext +import SwiftSignalKit public enum ChatTextInputMediaRecordingButtonMode: Int32 { case audio = 0 @@ -31,7 +32,6 @@ public struct ChatInterfaceSelectionState: Codable, Equatable { } } - public func encode(to encoder: Encoder) throws { var container = encoder.container(keyedBy: StringCodingKey.self) @@ -40,20 +40,6 @@ public struct ChatInterfaceSelectionState: Codable, Equatable { try container.encode(buffer.makeData(), forKey: "i") } - - /*public init(decoder: PostboxDecoder) { - if let data = decoder.decodeBytesForKeyNoCopy("i") { - self.selectedIds = Set(MessageId.decodeArrayFromBuffer(data)) - } else { - self.selectedIds = Set() - } - } - - public func encode(_ encoder: PostboxEncoder) { - let buffer = WriteBuffer() - MessageId.encodeArrayToBuffer(Array(selectedIds), buffer: buffer) - encoder.encodeBytes(buffer, forKey: "i") - }*/ } public struct ChatEditMessageState: Codable, Equatable { @@ -102,34 +88,6 @@ public struct ChatEditMessageState: Codable, Equatable { try container.encodeIfPresent(self.inputTextMaxLength, forKey: "tl") } - /*public init(decoder: PostboxDecoder) { - self.messageId = MessageId(peerId: PeerId(decoder.decodeInt64ForKey("mp", orElse: 0)), namespace: decoder.decodeInt32ForKey("mn", orElse: 0), id: decoder.decodeInt32ForKey("mi", orElse: 0)) - if let inputState = decoder.decodeObjectForKey("is", decoder: { return ChatTextInputState(decoder: $0) }) as? ChatTextInputState { - self.inputState = inputState - } else { - self.inputState = ChatTextInputState() - } - self.disableUrlPreview = decoder.decodeOptionalStringForKey("dup") - self.inputTextMaxLength = decoder.decodeOptionalInt32ForKey("tl") - } - - public func encode(_ encoder: PostboxEncoder) { - encoder.encodeInt64(self.messageId.peerId.toInt64(), forKey: "mp") - encoder.encodeInt32(self.messageId.namespace, forKey: "mn") - encoder.encodeInt32(self.messageId.id, forKey: "mi") - encoder.encodeObject(self.inputState, forKey: "is") - if let disableUrlPreview = self.disableUrlPreview { - encoder.encodeString(disableUrlPreview, forKey: "dup") - } else { - encoder.encodeNil(forKey: "dup") - } - if let inputTextMaxLength = self.inputTextMaxLength { - encoder.encodeInt32(inputTextMaxLength, forKey: "ml") - } else { - encoder.encodeNil(forKey: "ml") - } - }*/ - public static func ==(lhs: ChatEditMessageState, rhs: ChatEditMessageState) -> Bool { return lhs.messageId == rhs.messageId && lhs.inputState == rhs.inputState && lhs.disableUrlPreview == rhs.disableUrlPreview && lhs.inputTextMaxLength == rhs.inputTextMaxLength } @@ -143,7 +101,7 @@ public struct ChatEditMessageState: Codable, Equatable { } } -public struct ChatInterfaceMessageActionsState: PostboxCoding, Equatable { +public struct ChatInterfaceMessageActionsState: Codable, Equatable { public var closedButtonKeyboardMessageId: MessageId? public var dismissedButtonKeyboardMessageId: MessageId? public var processedSetupReplyMessageId: MessageId? @@ -173,86 +131,92 @@ public struct ChatInterfaceMessageActionsState: PostboxCoding, Equatable { self.dismissedAddContactPhoneNumber = dismissedAddContactPhoneNumber } - public init(decoder: PostboxDecoder) { - if let closedMessageIdPeerId = decoder.decodeOptionalInt64ForKey("cb.p"), let closedMessageIdNamespace = decoder.decodeOptionalInt32ForKey("cb.n"), let closedMessageIdId = decoder.decodeOptionalInt32ForKey("cb.i") { + public init(from decoder: Decoder) throws { + let container = try decoder.container(keyedBy: StringCodingKey.self) + + if let closedMessageIdPeerId = try? container.decodeIfPresent(Int64.self, forKey: "cb.p"), let closedMessageIdNamespace = try? container.decodeIfPresent(Int32.self, forKey: "cb.n"), let closedMessageIdId = try? container.decodeIfPresent(Int32.self, forKey: "cb.i") { self.closedButtonKeyboardMessageId = MessageId(peerId: PeerId(closedMessageIdPeerId), namespace: closedMessageIdNamespace, id: closedMessageIdId) } else { self.closedButtonKeyboardMessageId = nil } - if let messageIdPeerId = decoder.decodeOptionalInt64ForKey("dismissedbuttons.p"), let messageIdNamespace = decoder.decodeOptionalInt32ForKey("dismissedbuttons.n"), let messageIdId = decoder.decodeOptionalInt32ForKey("dismissedbuttons.i") { + if let messageIdPeerId = try? container.decodeIfPresent(Int64.self, forKey: "dismissedbuttons.p"), let messageIdNamespace = try? container.decodeIfPresent(Int32.self, forKey: "dismissedbuttons.n"), let messageIdId = try? container.decodeIfPresent(Int32.self, forKey: "dismissedbuttons.i") { self.dismissedButtonKeyboardMessageId = MessageId(peerId: PeerId(messageIdPeerId), namespace: messageIdNamespace, id: messageIdId) } else { self.dismissedButtonKeyboardMessageId = nil } - if let processedMessageIdPeerId = decoder.decodeOptionalInt64ForKey("pb.p"), let processedMessageIdNamespace = decoder.decodeOptionalInt32ForKey("pb.n"), let processedMessageIdId = decoder.decodeOptionalInt32ForKey("pb.i") { + if let processedMessageIdPeerId = try? container.decodeIfPresent(Int64.self, forKey: "pb.p"), let processedMessageIdNamespace = try? container.decodeIfPresent(Int32.self, forKey: "pb.n"), let processedMessageIdId = try? container.decodeIfPresent(Int32.self, forKey: "pb.i") { self.processedSetupReplyMessageId = MessageId(peerId: PeerId(processedMessageIdPeerId), namespace: processedMessageIdNamespace, id: processedMessageIdId) } else { self.processedSetupReplyMessageId = nil } - if let closedPinnedMessageIdPeerId = decoder.decodeOptionalInt64ForKey("cp.p"), let closedPinnedMessageIdNamespace = decoder.decodeOptionalInt32ForKey("cp.n"), let closedPinnedMessageIdId = decoder.decodeOptionalInt32ForKey("cp.i") { + if let closedPinnedMessageIdPeerId = try? container.decodeIfPresent(Int64.self, forKey: "cp.p"), let closedPinnedMessageIdNamespace = try? container.decodeIfPresent(Int32.self, forKey: "cp.n"), let closedPinnedMessageIdId = try? container.decodeIfPresent(Int32.self, forKey: "cp.i") { self.closedPinnedMessageId = MessageId(peerId: PeerId(closedPinnedMessageIdPeerId), namespace: closedPinnedMessageIdNamespace, id: closedPinnedMessageIdId) } else { self.closedPinnedMessageId = nil } - self.closedPeerSpecificPackSetup = decoder.decodeInt32ForKey("cpss", orElse: 0) != 0 + self.closedPeerSpecificPackSetup = ((try? container.decode(Int32.self, forKey: "cpss")) ?? 0) != 0 + + self.dismissedAddContactPhoneNumber = try? container.decodeIfPresent(String.self, forKey: "dismissedAddContactPhoneNumber") } - public func encode(_ encoder: PostboxEncoder) { + public func encode(to encoder: Encoder) throws { + var container = encoder.container(keyedBy: StringCodingKey.self) + if let closedButtonKeyboardMessageId = self.closedButtonKeyboardMessageId { - encoder.encodeInt64(closedButtonKeyboardMessageId.peerId.toInt64(), forKey: "cb.p") - encoder.encodeInt32(closedButtonKeyboardMessageId.namespace, forKey: "cb.n") - encoder.encodeInt32(closedButtonKeyboardMessageId.id, forKey: "cb.i") + try container.encode(closedButtonKeyboardMessageId.peerId.toInt64(), forKey: "cb.p") + try container.encode(closedButtonKeyboardMessageId.namespace, forKey: "cb.n") + try container.encode(closedButtonKeyboardMessageId.id, forKey: "cb.i") } else { - encoder.encodeNil(forKey: "cb.p") - encoder.encodeNil(forKey: "cb.n") - encoder.encodeNil(forKey: "cb.i") + try container.encodeNil(forKey: "cb.p") + try container.encodeNil(forKey: "cb.n") + try container.encodeNil(forKey: "cb.i") } if let dismissedButtonKeyboardMessageId = self.dismissedButtonKeyboardMessageId { - encoder.encodeInt64(dismissedButtonKeyboardMessageId.peerId.toInt64(), forKey: "dismissedbuttons.p") - encoder.encodeInt32(dismissedButtonKeyboardMessageId.namespace, forKey: "dismissedbuttons.n") - encoder.encodeInt32(dismissedButtonKeyboardMessageId.id, forKey: "dismissedbuttons.i") + try container.encode(dismissedButtonKeyboardMessageId.peerId.toInt64(), forKey: "dismissedbuttons.p") + try container.encode(dismissedButtonKeyboardMessageId.namespace, forKey: "dismissedbuttons.n") + try container.encode(dismissedButtonKeyboardMessageId.id, forKey: "dismissedbuttons.i") } else { - encoder.encodeNil(forKey: "dismissedbuttons.p") - encoder.encodeNil(forKey: "dismissedbuttons.n") - encoder.encodeNil(forKey: "dismissedbuttons.i") + try container.encodeNil(forKey: "dismissedbuttons.p") + try container.encodeNil(forKey: "dismissedbuttons.n") + try container.encodeNil(forKey: "dismissedbuttons.i") } if let processedSetupReplyMessageId = self.processedSetupReplyMessageId { - encoder.encodeInt64(processedSetupReplyMessageId.peerId.toInt64(), forKey: "pb.p") - encoder.encodeInt32(processedSetupReplyMessageId.namespace, forKey: "pb.n") - encoder.encodeInt32(processedSetupReplyMessageId.id, forKey: "pb.i") + try container.encode(processedSetupReplyMessageId.peerId.toInt64(), forKey: "pb.p") + try container.encode(processedSetupReplyMessageId.namespace, forKey: "pb.n") + try container.encode(processedSetupReplyMessageId.id, forKey: "pb.i") } else { - encoder.encodeNil(forKey: "pb.p") - encoder.encodeNil(forKey: "pb.n") - encoder.encodeNil(forKey: "pb.i") + try container.encodeNil(forKey: "pb.p") + try container.encodeNil(forKey: "pb.n") + try container.encodeNil(forKey: "pb.i") } if let closedPinnedMessageId = self.closedPinnedMessageId { - encoder.encodeInt64(closedPinnedMessageId.peerId.toInt64(), forKey: "cp.p") - encoder.encodeInt32(closedPinnedMessageId.namespace, forKey: "cp.n") - encoder.encodeInt32(closedPinnedMessageId.id, forKey: "cp.i") + try container.encode(closedPinnedMessageId.peerId.toInt64(), forKey: "cp.p") + try container.encode(closedPinnedMessageId.namespace, forKey: "cp.n") + try container.encode(closedPinnedMessageId.id, forKey: "cp.i") } else { - encoder.encodeNil(forKey: "cp.p") - encoder.encodeNil(forKey: "cp.n") - encoder.encodeNil(forKey: "cp.i") + try container.encodeNil(forKey: "cp.p") + try container.encodeNil(forKey: "cp.n") + try container.encodeNil(forKey: "cp.i") } - encoder.encodeInt32(self.closedPeerSpecificPackSetup ? 1 : 0, forKey: "cpss") + try container.encode((self.closedPeerSpecificPackSetup ? 1 : 0) as Int32, forKey: "cpss") if let dismissedAddContactPhoneNumber = self.dismissedAddContactPhoneNumber { - encoder.encodeString(dismissedAddContactPhoneNumber, forKey: "dismissedAddContactPhoneNumber") + try container.encode(dismissedAddContactPhoneNumber, forKey: "dismissedAddContactPhoneNumber") } else { - encoder.encodeNil(forKey: "dismissedAddContactPhoneNumber") + try container.encodeNil(forKey: "dismissedAddContactPhoneNumber") } } } -public struct ChatInterfaceHistoryScrollState: PostboxCoding, Equatable { +public struct ChatInterfaceHistoryScrollState: Codable, Equatable { public let messageIndex: MessageIndex public let relativeOffset: Double @@ -261,17 +225,28 @@ public struct ChatInterfaceHistoryScrollState: PostboxCoding, Equatable { self.relativeOffset = relativeOffset } - public init(decoder: PostboxDecoder) { - self.messageIndex = MessageIndex(id: MessageId(peerId: PeerId(decoder.decodeInt64ForKey("m.p", orElse: 0)), namespace: decoder.decodeInt32ForKey("m.n", orElse: 0), id: decoder.decodeInt32ForKey("m.i", orElse: 0)), timestamp: decoder.decodeInt32ForKey("m.t", orElse: 0)) - self.relativeOffset = decoder.decodeDoubleForKey("ro", orElse: 0.0) + public init(from decoder: Decoder) throws { + let container = try decoder.container(keyedBy: StringCodingKey.self) + + self.messageIndex = MessageIndex( + id: MessageId( + peerId: PeerId((try? container.decode(Int64.self, forKey: "m.p")) ?? 0), + namespace: (try? container.decode(Int32.self, forKey: "m.n")) ?? 0, + id: (try? container.decode(Int32.self, forKey: "m.i")) ?? 0 + ), + timestamp: (try? container.decode(Int32.self, forKey: "m.t")) ?? 0 + ) + self.relativeOffset = (try? container.decode(Double.self, forKey: "ro")) ?? 0.0 } - public func encode(_ encoder: PostboxEncoder) { - encoder.encodeInt32(self.messageIndex.timestamp, forKey: "m.t") - encoder.encodeInt64(self.messageIndex.id.peerId.toInt64(), forKey: "m.p") - encoder.encodeInt32(self.messageIndex.id.namespace, forKey: "m.n") - encoder.encodeInt32(self.messageIndex.id.id, forKey: "m.i") - encoder.encodeDouble(self.relativeOffset, forKey: "ro") + public func encode(to encoder: Encoder) throws { + var container = encoder.container(keyedBy: StringCodingKey.self) + + try container.encode(self.messageIndex.timestamp, forKey: "m.t") + try container.encode(self.messageIndex.id.peerId.toInt64(), forKey: "m.p") + try container.encode(self.messageIndex.id.namespace, forKey: "m.n") + try container.encode(self.messageIndex.id.id, forKey: "m.i") + try container.encode(self.relativeOffset, forKey: "ro") } public static func ==(lhs: ChatInterfaceHistoryScrollState, rhs: ChatInterfaceHistoryScrollState) -> Bool { @@ -285,7 +260,7 @@ public struct ChatInterfaceHistoryScrollState: PostboxCoding, Equatable { } } -public final class ChatInterfaceState: SynchronizeableChatInterfaceState, Equatable { +public final class ChatInterfaceState: Codable, Equatable { public let timestamp: Int32 public let composeInputState: ChatTextInputState public let composeDisableUrlPreview: String? @@ -299,35 +274,15 @@ public final class ChatInterfaceState: SynchronizeableChatInterfaceState, Equata public let silentPosting: Bool public let inputLanguage: String? - public var associatedMessageIds: [MessageId] { - var ids: [MessageId] = [] - if let editMessage = self.editMessage { - ids.append(editMessage.messageId) - } - return ids - } - - public var chatListEmbeddedState: PeerChatListEmbeddedInterfaceState? { - if self.composeInputState.inputText.length != 0 && self.timestamp != 0 { - return ChatEmbeddedInterfaceState(timestamp: self.timestamp, text: self.composeInputState.inputText) - } else { - return nil - } - } - public var synchronizeableInputState: SynchronizeableChatInputState? { if self.composeInputState.inputText.length == 0 { return nil } else { - return SynchronizeableChatInputState(replyToMessageId: self.replyMessageId, text: self.composeInputState.inputText.string, entities: generateChatInputTextEntities(self.composeInputState.inputText), timestamp: self.timestamp) + return SynchronizeableChatInputState(replyToMessageId: self.replyMessageId, text: self.composeInputState.inputText.string, entities: generateChatInputTextEntities(self.composeInputState.inputText), timestamp: self.timestamp, textSelection: self.composeInputState.selectionRange) } } - - public var historyScrollMessageIndex: MessageIndex? { - return self.historyScrollState?.messageIndex - } - - public func withUpdatedSynchronizeableInputState(_ state: SynchronizeableChatInputState?) -> SynchronizeableChatInterfaceState { + + public func withUpdatedSynchronizeableInputState(_ state: SynchronizeableChatInputState?) -> ChatInterfaceState { var result = self.withUpdatedComposeInputState(ChatTextInputState(inputText: chatInputStateStringWithAppliedEntities(state?.text ?? "", entities: state?.entities ?? []))).withUpdatedReplyMessageId(state?.replyToMessageId) if let timestamp = state?.timestamp { result = result.withUpdatedTimestamp(timestamp) @@ -335,6 +290,10 @@ public final class ChatInterfaceState: SynchronizeableChatInterfaceState, Equata return result } + public var historyScrollMessageIndex: MessageIndex? { + return self.historyScrollState?.messageIndex + } + public var effectiveInputState: ChatTextInputState { if let editMessage = self.editMessage { return editMessage.inputState @@ -373,114 +332,110 @@ public final class ChatInterfaceState: SynchronizeableChatInterfaceState, Equata self.inputLanguage = inputLanguage } - public init(decoder: PostboxDecoder) { - self.timestamp = decoder.decodeInt32ForKey("ts", orElse: 0) - if let inputState = decoder.decode(ChatTextInputState.self, forKey: "is") { + public init(from decoder: Decoder) throws { + let container = try decoder.container(keyedBy: StringCodingKey.self) + + self.timestamp = (try? container.decode(Int32.self, forKey: "ts")) ?? 0 + if let inputState = try? container.decode(ChatTextInputState.self, forKey: "is") { self.composeInputState = inputState } else { self.composeInputState = ChatTextInputState() } - if let composeDisableUrlPreview = decoder.decodeOptionalStringForKey("dup") { + if let composeDisableUrlPreview = try? container.decodeIfPresent(String.self, forKey: "dup") { self.composeDisableUrlPreview = composeDisableUrlPreview } else { self.composeDisableUrlPreview = nil } - let replyMessageIdPeerId: Int64? = decoder.decodeOptionalInt64ForKey("r.p") - let replyMessageIdNamespace: Int32? = decoder.decodeOptionalInt32ForKey("r.n") - let replyMessageIdId: Int32? = decoder.decodeOptionalInt32ForKey("r.i") + let replyMessageIdPeerId: Int64? = try? container.decodeIfPresent(Int64.self, forKey: "r.p") + let replyMessageIdNamespace: Int32? = try? container.decodeIfPresent(Int32.self, forKey: "r.n") + let replyMessageIdId: Int32? = try? container.decodeIfPresent(Int32.self, forKey: "r.i") if let replyMessageIdPeerId = replyMessageIdPeerId, let replyMessageIdNamespace = replyMessageIdNamespace, let replyMessageIdId = replyMessageIdId { self.replyMessageId = MessageId(peerId: PeerId(replyMessageIdPeerId), namespace: replyMessageIdNamespace, id: replyMessageIdId) } else { self.replyMessageId = nil } - if let forwardMessageIdsData = decoder.decodeBytesForKeyNoCopy("fm") { - self.forwardMessageIds = MessageId.decodeArrayFromBuffer(forwardMessageIdsData) + if let forwardMessageIdsData = try? container.decodeIfPresent(Data.self, forKey: "fm") { + self.forwardMessageIds = MessageId.decodeArrayFromBuffer(ReadBuffer(data: forwardMessageIdsData)) } else { self.forwardMessageIds = nil } - if let editMessage = decoder.decode(ChatEditMessageState.self, forKey: "em") { + if let editMessage = try? container.decodeIfPresent(ChatEditMessageState.self, forKey: "em") { self.editMessage = editMessage } else { self.editMessage = nil } - if let selectionState = decoder.decode(ChatInterfaceSelectionState.self, forKey: "ss") { + if let selectionState = try? container.decodeIfPresent(ChatInterfaceSelectionState.self, forKey: "ss") { self.selectionState = selectionState } else { self.selectionState = nil } - - if let messageActionsState = decoder.decodeObjectForKey("as", decoder: { ChatInterfaceMessageActionsState(decoder: $0) }) as? ChatInterfaceMessageActionsState { + + if let messageActionsState = try? container.decodeIfPresent(ChatInterfaceMessageActionsState.self, forKey: "as") { self.messageActionsState = messageActionsState } else { self.messageActionsState = ChatInterfaceMessageActionsState() } + + self.historyScrollState = try? container.decodeIfPresent(ChatInterfaceHistoryScrollState.self, forKey: "hss") - self.historyScrollState = decoder.decodeObjectForKey("hss", decoder: { ChatInterfaceHistoryScrollState(decoder: $0) }) as? ChatInterfaceHistoryScrollState + self.mediaRecordingMode = ChatTextInputMediaRecordingButtonMode(rawValue: (try? container.decodeIfPresent(Int32.self, forKey: "mrm")) ?? 0) ?? .audio - self.mediaRecordingMode = ChatTextInputMediaRecordingButtonMode(rawValue: decoder.decodeInt32ForKey("mrm", orElse: 0))! - - self.silentPosting = decoder.decodeInt32ForKey("sip", orElse: 0) != 0 - self.inputLanguage = decoder.decodeOptionalStringForKey("inputLanguage") + self.silentPosting = ((try? container.decode(Int32.self, forKey: "sip")) ?? 0) != 0 + self.inputLanguage = try? container.decodeIfPresent(String.self, forKey: "inputLanguage") } - public func encode(_ encoder: PostboxEncoder) { - encoder.encodeInt32(self.timestamp, forKey: "ts") - encoder.encode(self.composeInputState, forKey: "is") + public func encode(to encoder: Encoder) throws { + var container = encoder.container(keyedBy: StringCodingKey.self) + + try container.encode(self.timestamp, forKey: "ts") + try container.encode(self.composeInputState, forKey: "is") if let composeDisableUrlPreview = self.composeDisableUrlPreview { - encoder.encodeString(composeDisableUrlPreview, forKey: "dup") + try container.encode(composeDisableUrlPreview, forKey: "dup") } else { - encoder.encodeNil(forKey: "dup") + try container.encodeNil(forKey: "dup") } if let replyMessageId = self.replyMessageId { - encoder.encodeInt64(replyMessageId.peerId.toInt64(), forKey: "r.p") - encoder.encodeInt32(replyMessageId.namespace, forKey: "r.n") - encoder.encodeInt32(replyMessageId.id, forKey: "r.i") + try container.encode(replyMessageId.peerId.toInt64(), forKey: "r.p") + try container.encode(replyMessageId.namespace, forKey: "r.n") + try container.encode(replyMessageId.id, forKey: "r.i") } else { - encoder.encodeNil(forKey: "r.p") - encoder.encodeNil(forKey: "r.n") - encoder.encodeNil(forKey: "r.i") + try container.encodeNil(forKey: "r.p") + try container.encodeNil(forKey: "r.n") + try container.encodeNil(forKey: "r.i") } if let forwardMessageIds = self.forwardMessageIds { let buffer = WriteBuffer() MessageId.encodeArrayToBuffer(forwardMessageIds, buffer: buffer) - encoder.encodeBytes(buffer, forKey: "fm") + try container.encode(buffer.makeData(), forKey: "fm") } else { - encoder.encodeNil(forKey: "fm") + try container.encodeNil(forKey: "fm") } if let editMessage = self.editMessage { - encoder.encode(editMessage, forKey: "em") + try container.encode(editMessage, forKey: "em") } else { - encoder.encodeNil(forKey: "em") + try container.encodeNil(forKey: "em") } if let selectionState = self.selectionState { - encoder.encode(selectionState, forKey: "ss") + try container.encode(selectionState, forKey: "ss") } else { - encoder.encodeNil(forKey: "ss") + try container.encodeNil(forKey: "ss") } if self.messageActionsState.isEmpty { - encoder.encodeNil(forKey: "as") + try container.encodeNil(forKey: "as") } else { - encoder.encodeObject(self.messageActionsState, forKey: "as") + try container.encode(self.messageActionsState, forKey: "as") } if let historyScrollState = self.historyScrollState { - encoder.encodeObject(historyScrollState, forKey: "hss") + try container.encode(historyScrollState, forKey: "hss") } else { - encoder.encodeNil(forKey: "hss") + try container.encodeNil(forKey: "hss") } - encoder.encodeInt32(self.mediaRecordingMode.rawValue, forKey: "mrm") - encoder.encodeInt32(self.silentPosting ? 1 : 0, forKey: "sip") + try container.encode(self.mediaRecordingMode.rawValue, forKey: "mrm") + try container.encode((self.silentPosting ? 1 : 0) as Int32, forKey: "sip") if let inputLanguage = self.inputLanguage { - encoder.encodeString(inputLanguage, forKey: "inputLanguage") + try container.encode(inputLanguage, forKey: "inputLanguage") } else { - encoder.encodeNil(forKey: "inputLanguage") - } - } - - public func isEqual(to: PeerChatInterfaceState) -> Bool { - if let to = to as? ChatInterfaceState, self == to { - return true - } else { - return false + try container.encodeNil(forKey: "inputLanguage") } } @@ -600,4 +555,34 @@ public final class ChatInterfaceState: SynchronizeableChatInterfaceState, Equata public func withUpdatedInputLanguage(_ inputLanguage: String?) -> ChatInterfaceState { return ChatInterfaceState(timestamp: self.timestamp, composeInputState: self.composeInputState, composeDisableUrlPreview: self.composeDisableUrlPreview, replyMessageId: self.replyMessageId, forwardMessageIds: self.forwardMessageIds, editMessage: self.editMessage, selectionState: self.selectionState, messageActionsState: self.messageActionsState, historyScrollState: self.historyScrollState, mediaRecordingMode: self.mediaRecordingMode, silentPosting: self.silentPosting, inputLanguage: inputLanguage) } + + public static func parse(_ state: OpaqueChatInterfaceState) -> ChatInterfaceState { + guard let opaqueData = state.opaqueData else { + return ChatInterfaceState().withUpdatedSynchronizeableInputState(state.synchronizeableInputState) + } + guard var decodedState = try? AdaptedPostboxDecoder().decode(ChatInterfaceState.self, from: opaqueData) else { + return ChatInterfaceState().withUpdatedSynchronizeableInputState(state.synchronizeableInputState) + } + decodedState = decodedState.withUpdatedSynchronizeableInputState(state.synchronizeableInputState) + return decodedState + } + + public static func update(engine: TelegramEngine, peerId: PeerId, threadId: Int64?, _ f: @escaping (ChatInterfaceState) -> ChatInterfaceState) -> Signal { + return engine.peers.getOpaqueChatInterfaceState(peerId: peerId, threadId: threadId) + |> mapToSignal { previousOpaqueState -> Signal in + let previousState = previousOpaqueState.flatMap(ChatInterfaceState.parse) + let updatedState = f(previousState ?? ChatInterfaceState()) + + let updatedOpaqueData = try? AdaptedPostboxEncoder().encode(updatedState) + + return engine.peers.setOpaqueChatInterfaceState( + peerId: peerId, + threadId: threadId, + state: OpaqueChatInterfaceState( + opaqueData: updatedOpaqueData, + historyScrollMessageIndex: updatedState.historyScrollMessageIndex, + synchronizeableInputState: updatedState.synchronizeableInputState + )) + } + } } diff --git a/submodules/ChatListUI/Sources/ChatListSearchContainerNode.swift b/submodules/ChatListUI/Sources/ChatListSearchContainerNode.swift index 96e6e0f078..53d302d921 100644 --- a/submodules/ChatListUI/Sources/ChatListSearchContainerNode.swift +++ b/submodules/ChatListUI/Sources/ChatListSearchContainerNode.swift @@ -1031,15 +1031,10 @@ public final class ChatListSearchContainerNode: SearchDisplayControllerContentNo strongSelf.containerLayoutUpdated(layout, navigationBarHeight: navigationBarHeight, transition: .immediate) } } else { - let _ = (strongSelf.context.account.postbox.transaction({ transaction -> Void in - transaction.updatePeerChatInterfaceState(peerId, update: { currentState in - if let currentState = currentState as? ChatInterfaceState { - return currentState.withUpdatedForwardMessageIds(Array(messageIds)) - } else { - return ChatInterfaceState().withUpdatedForwardMessageIds(Array(messageIds)) - } - }) - }) |> deliverOnMainQueue).start(completed: { + let _ = (ChatInterfaceState.update(engine: strongSelf.context.engine, peerId: peerId, threadId: nil, { currentState in + return currentState.withUpdatedForwardMessageIds(Array(messageIds)) + }) + |> deliverOnMainQueue).start(completed: { if let strongSelf = self { let controller = strongSelf.context.sharedContext.makeChatController(context: strongSelf.context, chatLocation: .peer(peerId), subject: nil, botStart: nil, mode: .standard(previewing: false)) controller.purposefulAction = { [weak self] in diff --git a/submodules/ChatListUI/Sources/Node/ChatListItem.swift b/submodules/ChatListUI/Sources/Node/ChatListItem.swift index 2329c2e072..ba2e1330de 100644 --- a/submodules/ChatListUI/Sources/Node/ChatListItem.swift +++ b/submodules/ChatListUI/Sources/Node/ChatListItem.swift @@ -17,9 +17,10 @@ import PeerPresenceStatusManager import PhotoResources import ChatListSearchItemNode import ContextUI +import ChatInterfaceState public enum ChatListItemContent { - case peer(messages: [Message], peer: RenderedPeer, combinedReadState: CombinedPeerReadState?, isRemovedFromTotalUnreadCount: Bool, presence: PeerPresence?, summaryInfo: ChatListMessageTagSummaryInfo, embeddedState: PeerChatListEmbeddedInterfaceState?, inputActivities: [(Peer, PeerInputActivity)]?, promoInfo: ChatListNodeEntryPromoInfo?, ignoreUnreadBadge: Bool, displayAsMessage: Bool, hasFailedMessages: Bool) + case peer(messages: [Message], peer: RenderedPeer, combinedReadState: CombinedPeerReadState?, isRemovedFromTotalUnreadCount: Bool, presence: PeerPresence?, summaryInfo: ChatListMessageTagSummaryInfo, embeddedState: StoredPeerChatInterfaceState?, inputActivities: [(Peer, PeerInputActivity)]?, promoInfo: ChatListNodeEntryPromoInfo?, ignoreUnreadBadge: Bool, displayAsMessage: Bool, hasFailedMessages: Bool) case groupReference(groupId: PeerGroupId, peers: [ChatListGroupReferencePeer], message: Message?, unreadState: PeerGroupUnreadCountersCombinedSummary, hiddenByDefault: Bool) public var chatLocation: ChatLocation? { @@ -813,7 +814,7 @@ class ChatListItemNode: ItemListRevealOptionsItemNode { let unreadCount: (count: Int32, unread: Bool, muted: Bool, mutedCount: Int32?) let isRemovedFromTotalUnreadCount: Bool let peerPresence: PeerPresence? - let embeddedState: PeerChatListEmbeddedInterfaceState? + let embeddedState: StoredPeerChatInterfaceState? let summaryInfo: ChatListMessageTagSummaryInfo let inputActivities: [(Peer, PeerInputActivity)]? let isPeerGroup: Bool @@ -1021,11 +1022,15 @@ class ChatListItemNode: ItemListRevealOptionsItemNode { chatListText = (text, messageText) } - if inlineAuthorPrefix == nil, let embeddedState = embeddedState as? ChatEmbeddedInterfaceState { + if inlineAuthorPrefix == nil, let embeddedState = embeddedState, embeddedState.overrideChatTimestamp != nil, let opaqueState = _internal_decodeStoredChatInterfaceState(state: embeddedState) { + let interfaceState = ChatInterfaceState.parse(opaqueState) + hasDraft = true authorAttributedString = NSAttributedString(string: item.presentationData.strings.DialogList_Draft, font: textFont, textColor: theme.messageDraftTextColor) + + let draftText: String = interfaceState.composeInputState.inputText.string - attributedText = NSAttributedString(string: foldLineBreaks(embeddedState.text.string.replacingOccurrences(of: "\n\n", with: " ")), font: textFont, textColor: theme.messageTextColor) + attributedText = NSAttributedString(string: foldLineBreaks(draftText.replacingOccurrences(of: "\n\n", with: " ")), font: textFont, textColor: theme.messageTextColor) } else if let message = messages.last { var composedString: NSMutableAttributedString if let inlineAuthorPrefix = inlineAuthorPrefix { diff --git a/submodules/ChatListUI/Sources/Node/ChatListNodeEntries.swift b/submodules/ChatListUI/Sources/Node/ChatListNodeEntries.swift index 7b00ff204d..99e05d168d 100644 --- a/submodules/ChatListUI/Sources/Node/ChatListNodeEntries.swift +++ b/submodules/ChatListUI/Sources/Node/ChatListNodeEntries.swift @@ -46,7 +46,7 @@ public enum ChatListNodeEntryPromoInfo: Equatable { enum ChatListNodeEntry: Comparable, Identifiable { case HeaderEntry - case PeerEntry(index: ChatListIndex, presentationData: ChatListPresentationData, messages: [Message], readState: CombinedPeerReadState?, isRemovedFromTotalUnreadCount: Bool, embeddedInterfaceState: PeerChatListEmbeddedInterfaceState?, peer: RenderedPeer, presence: PeerPresence?, summaryInfo: ChatListMessageTagSummaryInfo, editing: Bool, hasActiveRevealControls: Bool, selected: Bool, inputActivities: [(Peer, PeerInputActivity)]?, promoInfo: ChatListNodeEntryPromoInfo?, hasFailedMessages: Bool, isContact: Bool) + case PeerEntry(index: ChatListIndex, presentationData: ChatListPresentationData, messages: [Message], readState: CombinedPeerReadState?, isRemovedFromTotalUnreadCount: Bool, embeddedInterfaceState: StoredPeerChatInterfaceState?, peer: RenderedPeer, presence: PeerPresence?, summaryInfo: ChatListMessageTagSummaryInfo, editing: Bool, hasActiveRevealControls: Bool, selected: Bool, inputActivities: [(Peer, PeerInputActivity)]?, promoInfo: ChatListNodeEntryPromoInfo?, hasFailedMessages: Bool, isContact: Bool) case HoleEntry(ChatListHole, theme: PresentationTheme) case GroupReferenceEntry(index: ChatListIndex, presentationData: ChatListPresentationData, groupId: PeerGroupId, peers: [ChatListGroupReferencePeer], message: Message?, editing: Bool, unreadState: PeerGroupUnreadCountersCombinedSummary, revealed: Bool, hiddenByDefault: Bool) case ArchiveIntro(presentationData: ChatListPresentationData) @@ -144,7 +144,7 @@ enum ChatListNodeEntry: Comparable, Identifiable { return false } if let lhsEmbeddedState = lhsEmbeddedState, let rhsEmbeddedState = rhsEmbeddedState { - if !lhsEmbeddedState.isEqual(to: rhsEmbeddedState) { + if lhsEmbeddedState != rhsEmbeddedState { return false } } else if (lhsEmbeddedState != nil) != (rhsEmbeddedState != nil) { diff --git a/submodules/Postbox/Sources/ChatListTable.swift b/submodules/Postbox/Sources/ChatListTable.swift index 92170fe4c2..107c7860d5 100644 --- a/submodules/Postbox/Sources/ChatListTable.swift +++ b/submodules/Postbox/Sources/ChatListTable.swift @@ -110,7 +110,7 @@ private func addOperation(_ operation: ChatListOperation, groupId: PeerGroupId, } public enum ChatListNamespaceEntry { - case peer(index: ChatListIndex, readState: PeerReadState?, topMessageAttributes: [MessageAttribute], tagSummary: MessageHistoryTagNamespaceSummary?, interfaceState: PeerChatInterfaceState?) + case peer(index: ChatListIndex, readState: PeerReadState?, topMessageAttributes: [MessageAttribute], tagSummary: MessageHistoryTagNamespaceSummary?, interfaceState: StoredPeerChatInterfaceState?) case hole(MessageIndex) public var index: ChatListIndex { @@ -294,12 +294,12 @@ final class ChatListTable: Table { return result } - func replay(historyOperationsByPeerId: [PeerId: [MessageHistoryOperation]], updatedPeerChatListEmbeddedStates: [PeerId: PeerChatListEmbeddedInterfaceState?], updatedChatListInclusions: [PeerId: PeerChatListInclusion], messageHistoryTable: MessageHistoryTable, peerChatInterfaceStateTable: PeerChatInterfaceStateTable, operations: inout [PeerGroupId: [ChatListOperation]]) { + func replay(historyOperationsByPeerId: [PeerId: [MessageHistoryOperation]], updatedPeerChatListEmbeddedStates: Set, updatedChatListInclusions: [PeerId: PeerChatListInclusion], messageHistoryTable: MessageHistoryTable, peerChatInterfaceStateTable: PeerChatInterfaceStateTable, operations: inout [PeerGroupId: [ChatListOperation]]) { var changedPeerIds = Set() for peerId in historyOperationsByPeerId.keys { changedPeerIds.insert(peerId) } - for peerId in updatedPeerChatListEmbeddedStates.keys { + for peerId in updatedPeerChatListEmbeddedStates { changedPeerIds.insert(peerId) } for peerId in updatedChatListInclusions.keys { @@ -315,19 +315,19 @@ final class ChatListTable: Table { } let topMessage = messageHistoryTable.topIndex(peerId: peerId) - let embeddedChatState = peerChatInterfaceStateTable.get(peerId)?.chatListEmbeddedState + let embeddedChatStateOverrideTimestamp = peerChatInterfaceStateTable.get(peerId)?.overrideChatTimestamp let rawTopMessageIndex: MessageIndex? let topMessageIndex: MessageIndex? if let topMessage = topMessage { var updatedTimestamp = topMessage.timestamp rawTopMessageIndex = MessageIndex(id: topMessage.id, timestamp: topMessage.timestamp) - if let embeddedChatState = embeddedChatState { - updatedTimestamp = max(updatedTimestamp, embeddedChatState.timestamp) + if let embeddedChatStateOverrideTimestamp = embeddedChatStateOverrideTimestamp { + updatedTimestamp = max(updatedTimestamp, embeddedChatStateOverrideTimestamp) } topMessageIndex = MessageIndex(id: topMessage.id, timestamp: updatedTimestamp) - } else if let embeddedChatState = embeddedChatState, embeddedChatState.timestamp != 0 { - topMessageIndex = MessageIndex(id: MessageId(peerId: peerId, namespace: 0, id: 1), timestamp: embeddedChatState.timestamp) + } else if let embeddedChatStateOverrideTimestamp = embeddedChatStateOverrideTimestamp, embeddedChatStateOverrideTimestamp != 0 { + topMessageIndex = MessageIndex(id: MessageId(peerId: peerId, namespace: 0, id: 1), timestamp: embeddedChatStateOverrideTimestamp) rawTopMessageIndex = nil } else { topMessageIndex = nil diff --git a/submodules/Postbox/Sources/ChatListView.swift b/submodules/Postbox/Sources/ChatListView.swift index 772deea1f9..4c32c8dce4 100644 --- a/submodules/Postbox/Sources/ChatListView.swift +++ b/submodules/Postbox/Sources/ChatListView.swift @@ -88,7 +88,7 @@ public struct ChatListGroupReferenceEntry: Equatable { } public enum ChatListEntry: Comparable { - case MessageEntry(index: ChatListIndex, messages: [Message], readState: CombinedPeerReadState?, isRemovedFromTotalUnreadCount: Bool, embeddedInterfaceState: PeerChatListEmbeddedInterfaceState?, renderedPeer: RenderedPeer, presence: PeerPresence?, summaryInfo: ChatListMessageTagSummaryInfo, hasFailed: Bool, isContact: Bool) + case MessageEntry(index: ChatListIndex, messages: [Message], readState: CombinedPeerReadState?, isRemovedFromTotalUnreadCount: Bool, embeddedInterfaceState: StoredPeerChatInterfaceState?, renderedPeer: RenderedPeer, presence: PeerPresence?, summaryInfo: ChatListMessageTagSummaryInfo, hasFailed: Bool, isContact: Bool) case HoleEntry(ChatListHole) public var index: ChatListIndex { @@ -123,7 +123,7 @@ public enum ChatListEntry: Comparable { return false } if let lhsEmbeddedState = lhsEmbeddedState, let rhsEmbeddedState = rhsEmbeddedState { - if !lhsEmbeddedState.isEqual(to: rhsEmbeddedState) { + if lhsEmbeddedState != rhsEmbeddedState { return false } } else if (lhsEmbeddedState != nil) != (rhsEmbeddedState != nil) { @@ -185,7 +185,7 @@ public enum ChatListEntry: Comparable { enum MutableChatListEntry: Equatable { case IntermediateMessageEntry(index: ChatListIndex, messageIndex: MessageIndex?) - case MessageEntry(index: ChatListIndex, messages: [Message], readState: CombinedPeerReadState?, notificationSettings: PeerNotificationSettings?, isRemovedFromTotalUnreadCount: Bool, embeddedInterfaceState: PeerChatListEmbeddedInterfaceState?, renderedPeer: RenderedPeer, presence: PeerPresence?, tagSummaryInfo: ChatListMessageTagSummaryInfo, hasFailedMessages: Bool, isContact: Bool) + case MessageEntry(index: ChatListIndex, messages: [Message], readState: CombinedPeerReadState?, notificationSettings: PeerNotificationSettings?, isRemovedFromTotalUnreadCount: Bool, embeddedInterfaceState: StoredPeerChatInterfaceState?, renderedPeer: RenderedPeer, presence: PeerPresence?, tagSummaryInfo: ChatListMessageTagSummaryInfo, hasFailedMessages: Bool, isContact: Bool) case HoleEntry(ChatListHole) init(_ intermediateEntry: ChatListIntermediateEntry, cachedDataTable: CachedPeerDataTable, readStateTable: MessageHistoryReadStateTable, messageHistoryTable: MessageHistoryTable) { @@ -591,7 +591,7 @@ final class MutableChatListView { let tagSummaryCount: Int32? = nil let actionsSummaryCount: Int32? = nil - return .MessageEntry(index: index, messages: renderedMessages, readState: postbox.readStateTable.getCombinedState(index.messageIndex.id.peerId), notificationSettings: notificationSettings, isRemovedFromTotalUnreadCount: false, embeddedInterfaceState: postbox.peerChatInterfaceStateTable.get(index.messageIndex.id.peerId)?.chatListEmbeddedState, renderedPeer: RenderedPeer(peerId: index.messageIndex.id.peerId, peers: peers), presence: presence, tagSummaryInfo: ChatListMessageTagSummaryInfo(tagSummaryCount: tagSummaryCount, actionsSummaryCount: actionsSummaryCount), hasFailedMessages: postbox.messageHistoryFailedTable.contains(peerId: index.messageIndex.id.peerId), isContact: isContact) + return .MessageEntry(index: index, messages: renderedMessages, readState: postbox.readStateTable.getCombinedState(index.messageIndex.id.peerId), notificationSettings: notificationSettings, isRemovedFromTotalUnreadCount: false, embeddedInterfaceState: postbox.peerChatInterfaceStateTable.get(index.messageIndex.id.peerId), renderedPeer: RenderedPeer(peerId: index.messageIndex.id.peerId, peers: peers), presence: presence, tagSummaryInfo: ChatListMessageTagSummaryInfo(tagSummaryCount: tagSummaryCount, actionsSummaryCount: actionsSummaryCount), hasFailedMessages: postbox.messageHistoryFailedTable.contains(peerId: index.messageIndex.id.peerId), isContact: isContact) default: return nil } diff --git a/submodules/Postbox/Sources/ChatListViewState.swift b/submodules/Postbox/Sources/ChatListViewState.swift index 7e8d365f44..02b160bae6 100644 --- a/submodules/Postbox/Sources/ChatListViewState.swift +++ b/submodules/Postbox/Sources/ChatListViewState.swift @@ -1394,7 +1394,7 @@ struct ChatListViewState { } } - let updatedEntry: MutableChatListEntry = .MessageEntry(index: index, messages: renderedMessages, readState: postbox.readStateTable.getCombinedState(index.messageIndex.id.peerId), notificationSettings: notificationSettings, isRemovedFromTotalUnreadCount: isRemovedFromTotalUnreadCount, embeddedInterfaceState: postbox.peerChatInterfaceStateTable.get(index.messageIndex.id.peerId)?.chatListEmbeddedState, renderedPeer: renderedPeer, presence: presence, tagSummaryInfo: tagSummaryInfo, hasFailedMessages: false, isContact: postbox.contactsTable.isContact(peerId: index.messageIndex.id.peerId)) + let updatedEntry: MutableChatListEntry = .MessageEntry(index: index, messages: renderedMessages, readState: postbox.readStateTable.getCombinedState(index.messageIndex.id.peerId), notificationSettings: notificationSettings, isRemovedFromTotalUnreadCount: isRemovedFromTotalUnreadCount, embeddedInterfaceState: postbox.peerChatInterfaceStateTable.get(index.messageIndex.id.peerId), renderedPeer: renderedPeer, presence: presence, tagSummaryInfo: tagSummaryInfo, hasFailedMessages: false, isContact: postbox.contactsTable.isContact(peerId: index.messageIndex.id.peerId)) if directionIndex == 0 { self.stateBySpace[space]!.orderedEntries.setLowerOrAtAnchorAtArrayIndex(listIndex, to: updatedEntry) } else { diff --git a/submodules/Postbox/Sources/Coding.swift b/submodules/Postbox/Sources/Coding.swift index 8a4f17e519..33edb55996 100644 --- a/submodules/Postbox/Sources/Coding.swift +++ b/submodules/Postbox/Sources/Coding.swift @@ -589,7 +589,7 @@ public final class PostboxEncoder { let innerEncoder = _AdaptedPostboxEncoder(typeHash: typeHash) try! value.encode(to: innerEncoder) - let (data, valueType) = innerEncoder.makeData() + let (data, valueType) = innerEncoder.makeData(addHeader: true) self.encodeInnerObjectData(data, valueType: valueType, forKey: key) } @@ -1612,6 +1612,29 @@ public final class PostboxDecoder { return nil } } + + static func parseDataRaw(data: Data) -> Data? { + return data.withUnsafeBytes { bytes -> Data? in + guard let baseAddress = bytes.baseAddress else { + return nil + } + if bytes.count < 4 { + return nil + } + + var length: Int32 = 0 + memcpy(&length, baseAddress, 4) + + if length < 0 || length != (bytes.count - 4) { + return nil + } + if length == 0 { + return Data() + } + + return Data(bytes: baseAddress.advanced(by: 4), count: Int(length)) + } + } public func decodeDataForKey(_ key: String) -> Data? { if PostboxDecoder.positionOnKey(self.buffer.memory, offset: &self.offset, maxOffset: self.buffer.length, length: self.buffer.length, key: key, valueType: .Bytes) { diff --git a/submodules/Postbox/Sources/InitialMessageHistoryData.swift b/submodules/Postbox/Sources/InitialMessageHistoryData.swift index 4c3c7d6e83..463e4750c3 100644 --- a/submodules/Postbox/Sources/InitialMessageHistoryData.swift +++ b/submodules/Postbox/Sources/InitialMessageHistoryData.swift @@ -2,6 +2,6 @@ import Foundation public struct InitialMessageHistoryData { public let peer: Peer? - public let chatInterfaceState: PeerChatInterfaceState? + public let storedInterfaceState: StoredPeerChatInterfaceState? public let associatedMessages: [MessageId: Message] } diff --git a/submodules/Postbox/Sources/Message.swift b/submodules/Postbox/Sources/Message.swift index 85b35fefca..37608f64bd 100644 --- a/submodules/Postbox/Sources/Message.swift +++ b/submodules/Postbox/Sources/Message.swift @@ -1,6 +1,6 @@ import Foundation -public struct MessageId: Hashable, Comparable, CustomStringConvertible, PostboxCoding { +public struct MessageId: Hashable, Comparable, CustomStringConvertible, PostboxCoding, Codable { public typealias Namespace = Int32 public typealias Id = Int32 @@ -94,7 +94,7 @@ public struct MessageId: Hashable, Comparable, CustomStringConvertible, PostboxC } } -public struct MessageIndex: Comparable, Hashable { +public struct MessageIndex: Codable, Comparable, Hashable { public let id: MessageId public let timestamp: Int32 @@ -514,7 +514,7 @@ public struct MessageForwardInfo: Equatable { } } -public protocol MessageAttribute: class, PostboxCoding { +public protocol MessageAttribute: AnyObject, PostboxCoding { var associatedPeerIds: [PeerId] { get } var associatedMessageIds: [MessageId] { get } var automaticTimestampBasedAttribute: (UInt16, Int32)? { get } diff --git a/submodules/Postbox/Sources/Peer.swift b/submodules/Postbox/Sources/Peer.swift index 48943109a3..c85b4f1af9 100644 --- a/submodules/Postbox/Sources/Peer.swift +++ b/submodules/Postbox/Sources/Peer.swift @@ -1,6 +1,10 @@ import Foundation public struct PeerId: Hashable, CustomStringConvertible, Comparable, Codable { + enum CodingKeys: String, CodingKey { + case internalValue = "iv" + } + public struct Namespace: Comparable, Hashable, Codable, CustomStringConvertible { public static var max: Namespace { return Namespace(rawValue: 0x7) @@ -156,6 +160,17 @@ public struct PeerId: Hashable, CustomStringConvertible, Comparable, Codable { self.id = Id(rawValue: Int32(bitPattern: UInt32(clamping: idLowBits))) } } + + public init(from decoder: Decoder) throws { + let container = try decoder.container(keyedBy: CodingKeys.self) + let value = try container.decode(Int64.self, forKey: .internalValue) + self.init(value) + } + + public func encode(to encoder: Encoder) throws { + var container = encoder.container(keyedBy: CodingKeys.self) + try container.encode(self.toInt64(), forKey: .internalValue) + } public func toInt64() -> Int64 { let idLowBits = UInt32(bitPattern: self.id.rawValue) diff --git a/submodules/Postbox/Sources/PeerChatInterfaceState.swift b/submodules/Postbox/Sources/PeerChatInterfaceState.swift index be04895995..3e491225f0 100644 --- a/submodules/Postbox/Sources/PeerChatInterfaceState.swift +++ b/submodules/Postbox/Sources/PeerChatInterfaceState.swift @@ -1,14 +1,75 @@ +import Foundation -public protocol PeerChatListEmbeddedInterfaceState: Codable { +/*public protocol PeerChatListEmbeddedInterfaceState: Codable { var timestamp: Int32 { get } func isEqual(to: PeerChatListEmbeddedInterfaceState) -> Bool +}*/ + +public final class StoredPeerChatInterfaceState: Codable, Equatable { + private enum CodingKeys: CodingKey { + case overrideChatTimestamp + case historyScrollMessageIndex + case associatedMessageIds + case data + } + + public let overrideChatTimestamp: Int32? + public let historyScrollMessageIndex: MessageIndex? + public let associatedMessageIds: [MessageId] + public let data: Data? + + public init( + overrideChatTimestamp: Int32?, + historyScrollMessageIndex: MessageIndex?, + associatedMessageIds: [MessageId], + data: Data? + ) { + self.overrideChatTimestamp = overrideChatTimestamp + self.historyScrollMessageIndex = historyScrollMessageIndex + self.associatedMessageIds = associatedMessageIds + self.data = data + } + + public init(from decoder: Decoder) throws { + let container = try decoder.container(keyedBy: CodingKeys.self) + + self.overrideChatTimestamp = try? container.decodeIfPresent(Int32.self, forKey: .overrideChatTimestamp) + self.historyScrollMessageIndex = try? container.decodeIfPresent(MessageIndex.self, forKey: .historyScrollMessageIndex) + self.associatedMessageIds = try container.decode([MessageId].self, forKey: .associatedMessageIds) + self.data = try? container.decodeIfPresent(Data.self, forKey: .data) + } + + public func encode(to encoder: Encoder) throws { + var container = encoder.container(keyedBy: CodingKeys.self) + + try container.encodeIfPresent(self.overrideChatTimestamp, forKey: .overrideChatTimestamp) + try container.encodeIfPresent(self.historyScrollMessageIndex, forKey: .historyScrollMessageIndex) + try container.encodeIfPresent(self.associatedMessageIds, forKey: .associatedMessageIds) + try container.encodeIfPresent(self.data, forKey: .data) + } + + public static func ==(lhs: StoredPeerChatInterfaceState, rhs: StoredPeerChatInterfaceState) -> Bool { + if lhs.overrideChatTimestamp != rhs.overrideChatTimestamp { + return false + } + if lhs.historyScrollMessageIndex != rhs.historyScrollMessageIndex { + return false + } + if lhs.associatedMessageIds != rhs.associatedMessageIds { + return false + } + if lhs.data != rhs.data { + return false + } + return true + } } -public protocol PeerChatInterfaceState: PostboxCoding { +/*public protocol PeerChatInterfaceState: PostboxCoding { var chatListEmbeddedState: PeerChatListEmbeddedInterfaceState? { get } var historyScrollMessageIndex: MessageIndex? { get } var associatedMessageIds: [MessageId] { get } func isEqual(to: PeerChatInterfaceState) -> Bool -} +}*/ diff --git a/submodules/Postbox/Sources/PeerChatInterfaceStateTable.swift b/submodules/Postbox/Sources/PeerChatInterfaceStateTable.swift index 5a39de8b2b..2467e38467 100644 --- a/submodules/Postbox/Sources/PeerChatInterfaceStateTable.swift +++ b/submodules/Postbox/Sources/PeerChatInterfaceStateTable.swift @@ -5,7 +5,7 @@ final class PeerChatInterfaceStateTable: Table { return ValueBoxTable(id: id, keyType: .int64, compactValuesOnCreation: false) } - private var states: [PeerId: PeerChatInterfaceState?] = [:] + private var states: [PeerId: StoredPeerChatInterfaceState?] = [:] private var peerIdsWithUpdatedStates = Set() private let sharedKey = ValueBoxKey(length: 8) @@ -15,10 +15,10 @@ final class PeerChatInterfaceStateTable: Table { return sharedKey } - func get(_ peerId: PeerId) -> PeerChatInterfaceState? { + func get(_ peerId: PeerId) -> StoredPeerChatInterfaceState? { if let cachedValue = self.states[peerId] { return cachedValue - } else if let value = self.valueBox.get(self.table, key: self.key(peerId, sharedKey: self.sharedKey)), let state = PostboxDecoder(buffer: value).decodeRootObject() as? PeerChatInterfaceState { + } else if let value = self.valueBox.get(self.table, key: self.key(peerId, sharedKey: self.sharedKey)), let state = try? AdaptedPostboxDecoder().decode(StoredPeerChatInterfaceState.self, from: value.makeData()) { self.states[peerId] = state return state } else { @@ -27,18 +27,14 @@ final class PeerChatInterfaceStateTable: Table { } } - func set(_ peerId: PeerId, state: PeerChatInterfaceState?) -> (updated: Bool, updatedEmbeddedState: Bool) { + func set(_ peerId: PeerId, state: StoredPeerChatInterfaceState?) -> (updated: Bool, updatedEmbeddedState: Bool) { let currentState = self.get(peerId) var updated = false var updatedEmbeddedState = false if let currentState = currentState, let state = state { - if !currentState.isEqual(to: state) { + if currentState != state { updated = true - if let currentEmbeddedState = currentState.chatListEmbeddedState, let embeddedState = state.chatListEmbeddedState { - if !currentEmbeddedState.isEqual(to: embeddedState) { - updatedEmbeddedState = true - } - } else if (currentState.chatListEmbeddedState != nil) != (state.chatListEmbeddedState != nil) { + if currentState.overrideChatTimestamp != state.overrideChatTimestamp { updatedEmbeddedState = true } } @@ -60,13 +56,10 @@ final class PeerChatInterfaceStateTable: Table { override func beforeCommit() { if !self.peerIdsWithUpdatedStates.isEmpty { - let sharedEncoder = PostboxEncoder() for peerId in self.peerIdsWithUpdatedStates { if let state = self.states[peerId] { - if let state = state { - sharedEncoder.reset() - sharedEncoder.encodeRootObject(state) - self.valueBox.set(self.table, key: self.key(peerId, sharedKey: self.sharedKey), value: sharedEncoder.readBufferNoCopy()) + if let state = state, let data = try? AdaptedPostboxEncoder().encode(state) { + self.valueBox.set(self.table, key: self.key(peerId, sharedKey: self.sharedKey), value: ReadBuffer(data: data)) } else { self.valueBox.remove(self.table, key: self.key(peerId, sharedKey: self.sharedKey), secure: false) } diff --git a/submodules/Postbox/Sources/PeerChatThreadInterfaceStateTable.swift b/submodules/Postbox/Sources/PeerChatThreadInterfaceStateTable.swift index fb75665a23..12f7818461 100644 --- a/submodules/Postbox/Sources/PeerChatThreadInterfaceStateTable.swift +++ b/submodules/Postbox/Sources/PeerChatThreadInterfaceStateTable.swift @@ -15,7 +15,7 @@ final class PeerChatThreadInterfaceStateTable: Table { return ValueBoxTable(id: id, keyType: .binary, compactValuesOnCreation: false) } - private var states: [PeerChatThreadId: PeerChatInterfaceState?] = [:] + private var states: [PeerChatThreadId: StoredPeerChatInterfaceState?] = [:] private var peerIdsWithUpdatedStates = Set() private let sharedKey = ValueBoxKey(length: 8 + 8) @@ -26,10 +26,10 @@ final class PeerChatThreadInterfaceStateTable: Table { return sharedKey } - func get(_ peerId: PeerChatThreadId) -> PeerChatInterfaceState? { + func get(_ peerId: PeerChatThreadId) -> StoredPeerChatInterfaceState? { if let cachedValue = self.states[peerId] { return cachedValue - } else if let value = self.valueBox.get(self.table, key: self.key(peerId, sharedKey: self.sharedKey)), let state = PostboxDecoder(buffer: value).decodeRootObject() as? PeerChatInterfaceState { + } else if let value = self.valueBox.get(self.table, key: self.key(peerId, sharedKey: self.sharedKey)), let state = try? AdaptedPostboxDecoder().decode(StoredPeerChatInterfaceState.self, from: value.makeData()) { self.states[peerId] = state return state } else { @@ -38,11 +38,11 @@ final class PeerChatThreadInterfaceStateTable: Table { } } - func set(_ peerId: PeerChatThreadId, state: PeerChatInterfaceState?) -> Bool { + func set(_ peerId: PeerChatThreadId, state: StoredPeerChatInterfaceState?) -> Bool { let currentState = self.get(peerId) var updated = false if let currentState = currentState, let state = state { - if !currentState.isEqual(to: state) { + if currentState != state { updated = true } } else if (currentState != nil) != (state != nil) { @@ -62,13 +62,10 @@ final class PeerChatThreadInterfaceStateTable: Table { override func beforeCommit() { if !self.peerIdsWithUpdatedStates.isEmpty { - let sharedEncoder = PostboxEncoder() for peerId in self.peerIdsWithUpdatedStates { if let state = self.states[peerId] { - if let state = state { - sharedEncoder.reset() - sharedEncoder.encodeRootObject(state) - self.valueBox.set(self.table, key: self.key(peerId, sharedKey: self.sharedKey), value: sharedEncoder.readBufferNoCopy()) + if let state = state, let data = try? AdaptedPostboxEncoder().encode(state) { + self.valueBox.set(self.table, key: self.key(peerId, sharedKey: self.sharedKey), value: ReadBuffer(data: data)) } else { self.valueBox.remove(self.table, key: self.key(peerId, sharedKey: self.sharedKey), secure: false) } diff --git a/submodules/Postbox/Sources/Postbox.swift b/submodules/Postbox/Sources/Postbox.swift index 45b941354b..dbd3d51a78 100644 --- a/submodules/Postbox/Sources/Postbox.swift +++ b/submodules/Postbox/Sources/Postbox.swift @@ -259,29 +259,24 @@ public final class Transaction { } } - /*public func getPeerGroupState(_ id: PeerGroupId) -> PeerGroupState? { - assert(!self.disposed) - return self.postbox?.peerGroupStateTable.get(id) - } - - public func setPeerGroupState(_ id: PeerGroupId, state: PeerGroupState) { - assert(!self.disposed) - self.postbox?.setPeerGroupState(id, state: state) - }*/ - - public func getPeerChatInterfaceState(_ id: PeerId) -> PeerChatInterfaceState? { + public func getPeerChatInterfaceState(_ id: PeerId) -> StoredPeerChatInterfaceState? { assert(!self.disposed) return self.postbox?.peerChatInterfaceStateTable.get(id) } - - public func updatePeerChatInterfaceState(_ id: PeerId, update: (PeerChatInterfaceState?) -> (PeerChatInterfaceState?)) { + + public func getPeerChatThreadInterfaceState(_ id: PeerId, threadId: Int64) -> StoredPeerChatInterfaceState? { assert(!self.disposed) - self.postbox?.updatePeerChatInterfaceState(id, update: update) + return self.postbox?.peerChatThreadInterfaceStateTable.get(PeerChatThreadId(peerId: id, threadId: threadId)) } - public func updatePeerChatThreadInterfaceState(_ id: PeerId, threadId: Int64, update: (PeerChatInterfaceState?) -> (PeerChatInterfaceState?)) { + public func setPeerChatInterfaceState(_ id: PeerId, state: StoredPeerChatInterfaceState?) { assert(!self.disposed) - self.postbox?.updatePeerChatThreadInterfaceState(id, threadId: threadId, update: update) + self.postbox?.setPeerChatInterfaceState(id, state: state) + } + + public func setPeerChatThreadInterfaceState(_ id: PeerId, threadId: Int64, state: StoredPeerChatInterfaceState?) { + assert(!self.disposed) + self.postbox?.setPeerChatThreadInterfaceState(id, threadId: threadId, state: state) } public func getPeer(_ id: PeerId) -> Peer? { @@ -1306,7 +1301,7 @@ public final class Postbox { private var currentUpdatedPeerNotificationBehaviorTimestamps: [PeerId: PeerNotificationSettingsBehaviorTimestamp] = [:] private var currentUpdatedCachedPeerData: [PeerId: CachedPeerData] = [:] private var currentUpdatedPeerPresences: [PeerId: PeerPresence] = [:] - private var currentUpdatedPeerChatListEmbeddedStates: [PeerId: PeerChatListEmbeddedInterfaceState?] = [:] + private var currentUpdatedPeerChatListEmbeddedStates = Set() private var currentUpdatedTotalUnreadStates: [PeerGroupId: ChatListTotalUnreadState] = [:] private var currentUpdatedGroupTotalUnreadSummaries: [PeerGroupId: PeerGroupUnreadCountersCombinedSummary] = [:] private var currentPeerMergedOperationLogOperations: [PeerMergedOperationLogOperation] = [] @@ -1479,8 +1474,8 @@ public final class Postbox { self.itemCollectionInfoTable = ItemCollectionInfoTable(valueBox: self.valueBox, table: ItemCollectionInfoTable.tableSpec(21)) self.itemCollectionReverseIndexTable = ReverseIndexReferenceTable(valueBox: self.valueBox, table: ReverseIndexReferenceTable.tableSpec(36)) self.itemCollectionItemTable = ItemCollectionItemTable(valueBox: self.valueBox, table: ItemCollectionItemTable.tableSpec(22), reverseIndexTable: self.itemCollectionReverseIndexTable) - self.peerChatInterfaceStateTable = PeerChatInterfaceStateTable(valueBox: self.valueBox, table: PeerChatInterfaceStateTable.tableSpec(23)) - self.peerChatThreadInterfaceStateTable = PeerChatThreadInterfaceStateTable(valueBox: self.valueBox, table: PeerChatThreadInterfaceStateTable.tableSpec(64)) + self.peerChatInterfaceStateTable = PeerChatInterfaceStateTable(valueBox: self.valueBox, table: PeerChatInterfaceStateTable.tableSpec(67)) + self.peerChatThreadInterfaceStateTable = PeerChatThreadInterfaceStateTable(valueBox: self.valueBox, table: PeerChatThreadInterfaceStateTable.tableSpec(68)) self.itemCacheMetaTable = ItemCacheMetaTable(valueBox: self.valueBox, table: ItemCacheMetaTable.tableSpec(24)) self.itemCacheTable = ItemCacheTable(valueBox: self.valueBox, table: ItemCacheTable.tableSpec(25)) self.chatListIndexTable = ChatListIndexTable(valueBox: self.valueBox, table: ChatListIndexTable.tableSpec(8), peerNameIndexTable: self.peerNameIndexTable, metadataTable: self.messageHistoryMetadataTable, readStateTable: self.readStateTable, notificationSettingsTable: self.peerNotificationSettingsTable) @@ -2204,16 +2199,16 @@ public final class Postbox { self.currentUpdatedPeerChatStates.insert(id) } - fileprivate func updatePeerChatInterfaceState(_ id: PeerId, update: (PeerChatInterfaceState?) -> (PeerChatInterfaceState?)) { - let updatedState = update(self.peerChatInterfaceStateTable.get(id)) + fileprivate func setPeerChatInterfaceState(_ id: PeerId, state: StoredPeerChatInterfaceState?) { + let updatedState = state let (_, updatedEmbeddedState) = self.peerChatInterfaceStateTable.set(id, state: updatedState) if updatedEmbeddedState { - self.currentUpdatedPeerChatListEmbeddedStates[id] = updatedState?.chatListEmbeddedState + self.currentUpdatedPeerChatListEmbeddedStates.insert(id) } } - fileprivate func updatePeerChatThreadInterfaceState(_ id: PeerId, threadId: Int64, update: (PeerChatInterfaceState?) -> (PeerChatInterfaceState?)) { - let updatedState = update(self.peerChatThreadInterfaceStateTable.get(PeerChatThreadId(peerId: id, threadId: threadId))) + fileprivate func setPeerChatThreadInterfaceState(_ id: PeerId, threadId: Int64, state: StoredPeerChatInterfaceState?) { + let updatedState = state let _ = self.peerChatThreadInterfaceStateTable.set(PeerChatThreadId(peerId: id, threadId: threadId), state: updatedState) } @@ -2797,7 +2792,7 @@ public final class Postbox { } } } - return InitialMessageHistoryData(peer: self.peerTable.get(peerId), chatInterfaceState: chatInterfaceState, associatedMessages: associatedMessages) + return InitialMessageHistoryData(peer: self.peerTable.get(peerId), storedInterfaceState: chatInterfaceState, associatedMessages: associatedMessages) } else { let chatInterfaceState = self.peerChatInterfaceStateTable.get(peerId) var associatedMessages: [MessageId: Message] = [:] @@ -2808,7 +2803,7 @@ public final class Postbox { } } } - return InitialMessageHistoryData(peer: self.peerTable.get(peerId), chatInterfaceState: chatInterfaceState, associatedMessages: associatedMessages) + return InitialMessageHistoryData(peer: self.peerTable.get(peerId), storedInterfaceState: chatInterfaceState, associatedMessages: associatedMessages) } } diff --git a/submodules/Postbox/Sources/PostboxTransaction.swift b/submodules/Postbox/Sources/PostboxTransaction.swift index 22f57a7c98..03113f3a8f 100644 --- a/submodules/Postbox/Sources/PostboxTransaction.swift +++ b/submodules/Postbox/Sources/PostboxTransaction.swift @@ -11,7 +11,7 @@ final class PostboxTransaction { let currentUpdatedPeerNotificationBehaviorTimestamps: [PeerId: PeerNotificationSettingsBehaviorTimestamp] let currentUpdatedCachedPeerData: [PeerId: CachedPeerData] let currentUpdatedPeerPresences: [PeerId: PeerPresence] - let currentUpdatedPeerChatListEmbeddedStates: [PeerId: PeerChatListEmbeddedInterfaceState?] + let currentUpdatedPeerChatListEmbeddedStates: Set let currentUpdatedTotalUnreadStates: [PeerGroupId: ChatListTotalUnreadState] let currentUpdatedTotalUnreadSummaries: [PeerGroupId: PeerGroupUnreadCountersCombinedSummary] let alteredInitialPeerCombinedReadStates: [PeerId: CombinedPeerReadState] @@ -174,7 +174,7 @@ final class PostboxTransaction { return true } - init(currentUpdatedState: PostboxCoding?, currentPeerHoleOperations: [MessageHistoryIndexHoleOperationKey: [MessageHistoryIndexHoleOperation]] = [:], currentOperationsByPeerId: [PeerId: [MessageHistoryOperation]], chatListOperations: [PeerGroupId: [ChatListOperation]], currentUpdatedChatListInclusions: [PeerId: PeerChatListInclusion], currentUpdatedPeers: [PeerId: Peer], currentUpdatedPeerNotificationSettings: [PeerId: (PeerNotificationSettings?, PeerNotificationSettings)], currentUpdatedPeerNotificationBehaviorTimestamps: [PeerId: PeerNotificationSettingsBehaviorTimestamp], currentUpdatedCachedPeerData: [PeerId: CachedPeerData], currentUpdatedPeerPresences: [PeerId: PeerPresence], currentUpdatedPeerChatListEmbeddedStates: [PeerId: PeerChatListEmbeddedInterfaceState?], currentUpdatedTotalUnreadStates: [PeerGroupId: ChatListTotalUnreadState], currentUpdatedTotalUnreadSummaries: [PeerGroupId: PeerGroupUnreadCountersCombinedSummary], alteredInitialPeerCombinedReadStates: [PeerId: CombinedPeerReadState], currentPeerMergedOperationLogOperations: [PeerMergedOperationLogOperation], currentTimestampBasedMessageAttributesOperations: [TimestampBasedMessageAttributesOperation], unsentMessageOperations: [IntermediateMessageHistoryUnsentOperation], updatedSynchronizePeerReadStateOperations: [PeerId: PeerReadStateSynchronizationOperation?], currentUpdatedGroupSummarySynchronizeOperations: [PeerGroupAndNamespace: Bool], currentPreferencesOperations: [PreferencesOperation], currentOrderedItemListOperations: [Int32: [OrderedItemListOperation]], currentItemCollectionItemsOperations: [ItemCollectionId: [ItemCollectionItemsOperation]], currentItemCollectionInfosOperations: [ItemCollectionInfosOperation], currentUpdatedPeerChatStates: Set, currentGlobalTagsOperations: [GlobalMessageHistoryTagsOperation], currentLocalTagsOperations: [IntermediateMessageHistoryLocalTagsOperation], updatedMedia: [MediaId: Media?], replaceRemoteContactCount: Int32?, replaceContactPeerIds: Set?, currentPendingMessageActionsOperations: [PendingMessageActionsOperation], currentUpdatedMessageActionsSummaries: [PendingMessageActionsSummaryKey: Int32], currentUpdatedMessageTagSummaries: [MessageHistoryTagsSummaryKey: MessageHistoryTagNamespaceSummary], currentInvalidateMessageTagSummaries: [InvalidatedMessageHistoryTagsSummaryEntryOperation], currentUpdatedPendingPeerNotificationSettings: Set, replacedAdditionalChatListItems: [AdditionalChatListItem]?, updatedNoticeEntryKeys: Set, updatedCacheEntryKeys: Set, currentUpdatedMasterClientId: Int64?, updatedFailedMessagePeerIds: Set, updatedFailedMessageIds: Set, updatedGlobalNotificationSettings: Bool) { + init(currentUpdatedState: PostboxCoding?, currentPeerHoleOperations: [MessageHistoryIndexHoleOperationKey: [MessageHistoryIndexHoleOperation]] = [:], currentOperationsByPeerId: [PeerId: [MessageHistoryOperation]], chatListOperations: [PeerGroupId: [ChatListOperation]], currentUpdatedChatListInclusions: [PeerId: PeerChatListInclusion], currentUpdatedPeers: [PeerId: Peer], currentUpdatedPeerNotificationSettings: [PeerId: (PeerNotificationSettings?, PeerNotificationSettings)], currentUpdatedPeerNotificationBehaviorTimestamps: [PeerId: PeerNotificationSettingsBehaviorTimestamp], currentUpdatedCachedPeerData: [PeerId: CachedPeerData], currentUpdatedPeerPresences: [PeerId: PeerPresence], currentUpdatedPeerChatListEmbeddedStates: Set, currentUpdatedTotalUnreadStates: [PeerGroupId: ChatListTotalUnreadState], currentUpdatedTotalUnreadSummaries: [PeerGroupId: PeerGroupUnreadCountersCombinedSummary], alteredInitialPeerCombinedReadStates: [PeerId: CombinedPeerReadState], currentPeerMergedOperationLogOperations: [PeerMergedOperationLogOperation], currentTimestampBasedMessageAttributesOperations: [TimestampBasedMessageAttributesOperation], unsentMessageOperations: [IntermediateMessageHistoryUnsentOperation], updatedSynchronizePeerReadStateOperations: [PeerId: PeerReadStateSynchronizationOperation?], currentUpdatedGroupSummarySynchronizeOperations: [PeerGroupAndNamespace: Bool], currentPreferencesOperations: [PreferencesOperation], currentOrderedItemListOperations: [Int32: [OrderedItemListOperation]], currentItemCollectionItemsOperations: [ItemCollectionId: [ItemCollectionItemsOperation]], currentItemCollectionInfosOperations: [ItemCollectionInfosOperation], currentUpdatedPeerChatStates: Set, currentGlobalTagsOperations: [GlobalMessageHistoryTagsOperation], currentLocalTagsOperations: [IntermediateMessageHistoryLocalTagsOperation], updatedMedia: [MediaId: Media?], replaceRemoteContactCount: Int32?, replaceContactPeerIds: Set?, currentPendingMessageActionsOperations: [PendingMessageActionsOperation], currentUpdatedMessageActionsSummaries: [PendingMessageActionsSummaryKey: Int32], currentUpdatedMessageTagSummaries: [MessageHistoryTagsSummaryKey: MessageHistoryTagNamespaceSummary], currentInvalidateMessageTagSummaries: [InvalidatedMessageHistoryTagsSummaryEntryOperation], currentUpdatedPendingPeerNotificationSettings: Set, replacedAdditionalChatListItems: [AdditionalChatListItem]?, updatedNoticeEntryKeys: Set, updatedCacheEntryKeys: Set, currentUpdatedMasterClientId: Int64?, updatedFailedMessagePeerIds: Set, updatedFailedMessageIds: Set, updatedGlobalNotificationSettings: Bool) { self.currentUpdatedState = currentUpdatedState self.currentPeerHoleOperations = currentPeerHoleOperations self.currentOperationsByPeerId = currentOperationsByPeerId diff --git a/submodules/Postbox/Sources/Utils/Decoder/AdaptedPostboxDecoder.swift b/submodules/Postbox/Sources/Utils/Decoder/AdaptedPostboxDecoder.swift index 69d2074ced..6b83b7b0d5 100644 --- a/submodules/Postbox/Sources/Utils/Decoder/AdaptedPostboxDecoder.swift +++ b/submodules/Postbox/Sources/Utils/Decoder/AdaptedPostboxDecoder.swift @@ -10,7 +10,10 @@ final public class AdaptedPostboxDecoder { case dataArray } - func decode(_ type: T.Type, from data: Data) throws -> T where T : Decodable { + public init() { + } + + public func decode(_ type: T.Type, from data: Data) throws -> T where T : Decodable { return try self.decode(type, from: data, contentType: .object) } diff --git a/submodules/Postbox/Sources/Utils/Decoder/AdaptedPostboxKeyedDecodingContainer.swift b/submodules/Postbox/Sources/Utils/Decoder/AdaptedPostboxKeyedDecodingContainer.swift index 6d4f5e9faa..22e5e62c32 100644 --- a/submodules/Postbox/Sources/Utils/Decoder/AdaptedPostboxKeyedDecodingContainer.swift +++ b/submodules/Postbox/Sources/Utils/Decoder/AdaptedPostboxKeyedDecodingContainer.swift @@ -38,8 +38,22 @@ extension _AdaptedPostboxDecoder.KeyedContainer: KeyedDecodingContainerProtocol if let mappedType = AdaptedPostboxDecoder.ContentType(valueType: valueType) { return try AdaptedPostboxDecoder().decode(T.self, from: data, contentType: mappedType) } else { - decodingErrorBreakpoint() - throw DecodingError.typeMismatch(T.self, DecodingError.Context(codingPath: self.codingPath + [key], debugDescription: "")) + switch valueType { + case .Bytes: + guard let resultData = PostboxDecoder.parseDataRaw(data: data) else { + decodingErrorBreakpoint() + throw DecodingError.typeMismatch(T.self, DecodingError.Context(codingPath: self.codingPath + [key], debugDescription: "")) + } + if let resultData = resultData as? T { + return resultData + } else { + decodingErrorBreakpoint() + throw DecodingError.typeMismatch(T.self, DecodingError.Context(codingPath: self.codingPath + [key], debugDescription: "")) + } + default: + decodingErrorBreakpoint() + throw DecodingError.typeMismatch(T.self, DecodingError.Context(codingPath: self.codingPath + [key], debugDescription: "")) + } } } else { decodingErrorBreakpoint() @@ -82,6 +96,15 @@ extension _AdaptedPostboxDecoder.KeyedContainer: KeyedDecodingContainerProtocol throw DecodingError.keyNotFound(key, DecodingError.Context(codingPath: self.codingPath + [key], debugDescription: "")) } } + + func decode(_ type: Double.Type, forKey key: Key) throws -> Double { + if let value = self.decoder.decodeOptionalDoubleForKey(key.stringValue) { + return value + } else { + decodingErrorBreakpoint() + throw DecodingError.keyNotFound(key, DecodingError.Context(codingPath: self.codingPath + [key], debugDescription: "")) + } + } func nestedUnkeyedContainer(forKey key: Key) throws -> UnkeyedDecodingContainer { preconditionFailure() diff --git a/submodules/Postbox/Sources/Utils/Encoder/AdaptedPostboxEncoder.swift b/submodules/Postbox/Sources/Utils/Encoder/AdaptedPostboxEncoder.swift index 14bf249f28..5c46082034 100644 --- a/submodules/Postbox/Sources/Utils/Encoder/AdaptedPostboxEncoder.swift +++ b/submodules/Postbox/Sources/Utils/Encoder/AdaptedPostboxEncoder.swift @@ -2,12 +2,15 @@ import Foundation import MurMurHash32 public class AdaptedPostboxEncoder { - func encode(_ value: Encodable) throws -> Data { + public init() { + } + + public func encode(_ value: Encodable) throws -> Data { let typeHash: Int32 = murMurHashString32("\(type(of: value))") let encoder = _AdaptedPostboxEncoder(typeHash: typeHash) try value.encode(to: encoder) - return encoder.makeData().0 + return encoder.makeData(addHeader: false).0 } } @@ -24,8 +27,8 @@ final class _AdaptedPostboxEncoder { self.typeHash = typeHash } - func makeData() -> (Data, ValueType) { - return self.container!.makeData() + func makeData(addHeader: Bool) -> (Data, ValueType) { + return self.container!.makeData(addHeader: addHeader) } } @@ -58,5 +61,5 @@ extension _AdaptedPostboxEncoder: Encoder { } protocol AdaptedPostboxEncodingContainer: AnyObject { - func makeData() -> (Data, ValueType) + func makeData(addHeader: Bool) -> (Data, ValueType) } diff --git a/submodules/Postbox/Sources/Utils/Encoder/AdaptedPostboxKeyedEncodingContainer.swift b/submodules/Postbox/Sources/Utils/Encoder/AdaptedPostboxKeyedEncodingContainer.swift index 8f6681d1ed..311d3edcf2 100644 --- a/submodules/Postbox/Sources/Utils/Encoder/AdaptedPostboxKeyedEncodingContainer.swift +++ b/submodules/Postbox/Sources/Utils/Encoder/AdaptedPostboxKeyedEncodingContainer.swift @@ -17,16 +17,21 @@ extension _AdaptedPostboxEncoder { self.encoder = PostboxEncoder() } - func makeData() -> (Data, ValueType) { + func makeData(addHeader: Bool) -> (Data, ValueType) { let buffer = WriteBuffer() - var typeHash: Int32 = self.typeHash - buffer.write(&typeHash, offset: 0, length: 4) + if addHeader { + var typeHash: Int32 = self.typeHash + buffer.write(&typeHash, offset: 0, length: 4) + } let data = self.encoder.makeData() - var length: Int32 = Int32(data.count) - buffer.write(&length, offset: 0, length: 4) + if addHeader { + var length: Int32 = Int32(data.count) + buffer.write(&length, offset: 0, length: 4) + } + buffer.write(data) return (buffer.makeData(), .Object) @@ -43,7 +48,7 @@ extension _AdaptedPostboxEncoder.KeyedContainer: KeyedEncodingContainerProtocol let innerEncoder = _AdaptedPostboxEncoder(typeHash: typeHash) try! value.encode(to: innerEncoder) - let (data, valueType) = innerEncoder.makeData() + let (data, valueType) = innerEncoder.makeData(addHeader: true) self.encoder.encodeInnerObjectData(data, valueType: valueType, forKey: key.stringValue) } } diff --git a/submodules/Postbox/Sources/Utils/Encoder/AdaptedPostboxSingleValueEncodingContainer.swift b/submodules/Postbox/Sources/Utils/Encoder/AdaptedPostboxSingleValueEncodingContainer.swift index 7dd39a4ffa..5ae52729e9 100644 --- a/submodules/Postbox/Sources/Utils/Encoder/AdaptedPostboxSingleValueEncodingContainer.swift +++ b/submodules/Postbox/Sources/Utils/Encoder/AdaptedPostboxSingleValueEncodingContainer.swift @@ -79,7 +79,7 @@ extension _AdaptedPostboxEncoder.SingleValueContainer: SingleValueEncodingContai } extension _AdaptedPostboxEncoder.SingleValueContainer: AdaptedPostboxEncodingContainer { - func makeData() -> (Data, ValueType) { + func makeData(addHeader: Bool) -> (Data, ValueType) { preconditionFailure() } } diff --git a/submodules/Postbox/Sources/Utils/Encoder/AdaptedPostboxUnkeyedEncodingContainer.swift b/submodules/Postbox/Sources/Utils/Encoder/AdaptedPostboxUnkeyedEncodingContainer.swift index 8efa257466..568db0689a 100644 --- a/submodules/Postbox/Sources/Utils/Encoder/AdaptedPostboxUnkeyedEncodingContainer.swift +++ b/submodules/Postbox/Sources/Utils/Encoder/AdaptedPostboxUnkeyedEncodingContainer.swift @@ -25,7 +25,9 @@ extension _AdaptedPostboxEncoder { self.userInfo = userInfo } - func makeData() -> (Data, ValueType) { + func makeData(addHeader: Bool) -> (Data, ValueType) { + precondition(addHeader) + if self.items.isEmpty { let buffer = WriteBuffer() @@ -111,7 +113,7 @@ extension _AdaptedPostboxEncoder.UnkeyedContainer: UnkeyedEncodingContainer { let innerEncoder = _AdaptedPostboxEncoder(typeHash: typeHash) try! value.encode(to: innerEncoder) - let (data, _) = innerEncoder.makeData() + let (data, _) = innerEncoder.makeData(addHeader: true) let buffer = WriteBuffer() diff --git a/submodules/TelegramCore/Sources/Account/Account.swift b/submodules/TelegramCore/Sources/Account/Account.swift index 3eb5a09d43..042a065381 100644 --- a/submodules/TelegramCore/Sources/Account/Account.swift +++ b/submodules/TelegramCore/Sources/Account/Account.swift @@ -698,13 +698,11 @@ public enum AccountNetworkState: Equatable { } public final class AccountAuxiliaryMethods { - public let updatePeerChatInputState: (PeerChatInterfaceState?, SynchronizeableChatInputState?) -> PeerChatInterfaceState? public let fetchResource: (Account, MediaResource, Signal<[(Range, MediaBoxFetchPriority)], NoError>, MediaResourceFetchParameters?) -> Signal? public let fetchResourceMediaReferenceHash: (MediaResource) -> Signal public let prepareSecretThumbnailData: (MediaResourceData) -> (PixelDimensions, Data)? - public init(updatePeerChatInputState: @escaping (PeerChatInterfaceState?, SynchronizeableChatInputState?) -> PeerChatInterfaceState?, fetchResource: @escaping (Account, MediaResource, Signal<[(Range, MediaBoxFetchPriority)], NoError>, MediaResourceFetchParameters?) -> Signal?, fetchResourceMediaReferenceHash: @escaping (MediaResource) -> Signal, prepareSecretThumbnailData: @escaping (MediaResourceData) -> (PixelDimensions, Data)?) { - self.updatePeerChatInputState = updatePeerChatInputState + public init(fetchResource: @escaping (Account, MediaResource, Signal<[(Range, MediaBoxFetchPriority)], NoError>, MediaResourceFetchParameters?) -> Signal?, fetchResourceMediaReferenceHash: @escaping (MediaResource) -> Signal, prepareSecretThumbnailData: @escaping (MediaResourceData) -> (PixelDimensions, Data)?) { self.fetchResource = fetchResource self.fetchResourceMediaReferenceHash = fetchResourceMediaReferenceHash self.prepareSecretThumbnailData = prepareSecretThumbnailData diff --git a/submodules/TelegramCore/Sources/State/AccountStateManagementUtils.swift b/submodules/TelegramCore/Sources/State/AccountStateManagementUtils.swift index e7b767d019..fa457495d4 100644 --- a/submodules/TelegramCore/Sources/State/AccountStateManagementUtils.swift +++ b/submodules/TelegramCore/Sources/State/AccountStateManagementUtils.swift @@ -1353,7 +1353,7 @@ private func finalStateWithUpdatesAndServerTime(postbox: Postbox, network: Netwo if let replyToMsgId = replyToMsgId { replyToMessageId = MessageId(peerId: peer.peerId, namespace: Namespaces.Message.Cloud, id: replyToMsgId) } - inputState = SynchronizeableChatInputState(replyToMessageId: replyToMessageId, text: message, entities: messageTextEntitiesFromApiEntities(entities ?? []), timestamp: date) + inputState = SynchronizeableChatInputState(replyToMessageId: replyToMessageId, text: message, entities: messageTextEntitiesFromApiEntities(entities ?? []), timestamp: date, textSelection: nil) } updatedState.addUpdateChatInputState(peerId: peer.peerId, state: inputState) case let .updatePhoneCall(phoneCall): @@ -2995,9 +2995,7 @@ func replayFinalState(accountManager: AccountManager, postbox: Postbox, accountP case .UpdateRecentGifs: syncRecentGifs = true case let .UpdateChatInputState(peerId, inputState): - transaction.updatePeerChatInterfaceState(peerId, update: { current in - return auxiliaryMethods.updatePeerChatInputState(current, inputState) - }) + _internal_updateChatInputState(transaction: transaction, peerId: peerId, inputState: inputState) case let .UpdateCall(call): updatedCalls.append(call) case let .AddCallSignalingData(callId, data): diff --git a/submodules/TelegramCore/Sources/State/ManagedSynchronizeChatInputStateOperations.swift b/submodules/TelegramCore/Sources/State/ManagedSynchronizeChatInputStateOperations.swift index 817beee648..c6112c68de 100644 --- a/submodules/TelegramCore/Sources/State/ManagedSynchronizeChatInputStateOperations.swift +++ b/submodules/TelegramCore/Sources/State/ManagedSynchronizeChatInputStateOperations.swift @@ -126,7 +126,11 @@ func managedSynchronizeChatInputStateOperations(postbox: Postbox, network: Netwo } private func synchronizeChatInputState(transaction: Transaction, postbox: Postbox, network: Network, peerId: PeerId, operation: SynchronizeChatInputStateOperation) -> Signal { - let inputState = (transaction.getPeerChatInterfaceState(peerId) as? SynchronizeableChatInterfaceState)?.synchronizeableInputState + var inputState: SynchronizeableChatInputState? + if let peerChatInterfaceState = transaction.getPeerChatInterfaceState(peerId), let data = peerChatInterfaceState.data { + inputState = (try? AdaptedPostboxDecoder().decode(InternalChatInterfaceState.self, from: data))?.synchronizeableInputState + } + if let peer = transaction.getPeer(peerId), let inputPeer = apiInputPeer(peer) { var flags: Int32 = 0 if let inputState = inputState { diff --git a/submodules/TelegramCore/Sources/State/SynchronizeChatInputStateOperation.swift b/submodules/TelegramCore/Sources/State/SynchronizeChatInputStateOperation.swift index 7c49544c23..544d797775 100644 --- a/submodules/TelegramCore/Sources/State/SynchronizeChatInputStateOperation.swift +++ b/submodules/TelegramCore/Sources/State/SynchronizeChatInputStateOperation.swift @@ -1,7 +1,6 @@ import Foundation import Postbox - func addSynchronizeChatInputStateOperation(transaction: Transaction, peerId: PeerId) { var updateLocalIndex: Int32? let tag: PeerOperationLogTag = OperationLogTags.SynchronizeChatInputStates @@ -17,8 +16,8 @@ func addSynchronizeChatInputStateOperation(transaction: Transaction, peerId: Pee var previousState: SynchronizeableChatInputState? if let previousOperation = previousOperation { previousState = previousOperation.previousState - } else if let peerChatInterfaceState = transaction.getPeerChatInterfaceState(peerId) as? SynchronizeableChatInterfaceState { - previousState = peerChatInterfaceState.synchronizeableInputState + } else if let peerChatInterfaceState = transaction.getPeerChatInterfaceState(peerId), let data = peerChatInterfaceState.data { + previousState = (try? AdaptedPostboxDecoder().decode(InternalChatInterfaceState.self, from: data))?.synchronizeableInputState } let operationContents = SynchronizeChatInputStateOperation(previousState: previousState) if let updateLocalIndex = updateLocalIndex { diff --git a/submodules/TelegramCore/Sources/SyncCore/SyncCore_SynchronizeChatInputStateOperation.swift b/submodules/TelegramCore/Sources/SyncCore/SyncCore_SynchronizeChatInputStateOperation.swift index 04ddc11ef2..7f3ad4bb48 100644 --- a/submodules/TelegramCore/Sources/SyncCore/SyncCore_SynchronizeChatInputStateOperation.swift +++ b/submodules/TelegramCore/Sources/SyncCore/SyncCore_SynchronizeChatInputStateOperation.swift @@ -8,12 +8,12 @@ public final class SynchronizeChatInputStateOperation: PostboxCoding { } public init(decoder: PostboxDecoder) { - self.previousState = decoder.decodeObjectForKey("p", decoder: { SynchronizeableChatInputState(decoder: $0) }) as? SynchronizeableChatInputState + self.previousState = decoder.decode(SynchronizeableChatInputState.self, forKey: "p") } public func encode(_ encoder: PostboxEncoder) { if let previousState = self.previousState { - encoder.encodeObject(previousState, forKey: "p") + encoder.encode(previousState, forKey: "p") } else { encoder.encodeNil(forKey: "p") } diff --git a/submodules/TelegramCore/Sources/SyncCore/SyncCore_SynchronizeableChatInputState.swift b/submodules/TelegramCore/Sources/SyncCore/SyncCore_SynchronizeableChatInputState.swift index 6211d31c87..f291ba736a 100644 --- a/submodules/TelegramCore/Sources/SyncCore/SyncCore_SynchronizeableChatInputState.swift +++ b/submodules/TelegramCore/Sources/SyncCore/SyncCore_SynchronizeableChatInputState.swift @@ -1,42 +1,49 @@ import Foundation import Postbox -public struct SynchronizeableChatInputState: PostboxCoding, Equatable { +public struct SynchronizeableChatInputState: Codable, Equatable { public let replyToMessageId: MessageId? public let text: String public let entities: [MessageTextEntity] public let timestamp: Int32 + public let textSelection: Range? - public init(replyToMessageId: MessageId?, text: String, entities: [MessageTextEntity], timestamp: Int32) { + public init(replyToMessageId: MessageId?, text: String, entities: [MessageTextEntity], timestamp: Int32, textSelection: Range?) { self.replyToMessageId = replyToMessageId self.text = text self.entities = entities self.timestamp = timestamp + self.textSelection = textSelection } - public init(decoder: PostboxDecoder) { - self.text = decoder.decodeStringForKey("t", orElse: "") - self.entities = decoder.decodeObjectArrayWithDecoderForKey("e") - self.timestamp = decoder.decodeInt32ForKey("s", orElse: 0) - if let messageIdPeerId = decoder.decodeOptionalInt64ForKey("m.p"), let messageIdNamespace = decoder.decodeOptionalInt32ForKey("m.n"), let messageIdId = decoder.decodeOptionalInt32ForKey("m.i") { + public init(from decoder: Decoder) throws { + let container = try decoder.container(keyedBy: StringCodingKey.self) + self.text = (try? container.decode(String.self, forKey: "t")) ?? "" + self.entities = (try? container.decode([MessageTextEntity].self, forKey: "e")) ?? [] + self.timestamp = (try? container.decode(Int32.self, forKey: "s")) ?? 0 + + if let messageIdPeerId = try? container.decodeIfPresent(Int64.self, forKey: "m.p"), let messageIdNamespace = try? container.decodeIfPresent(Int32.self, forKey: "m.n"), let messageIdId = try? container.decodeIfPresent(Int32.self, forKey: "m.i") { self.replyToMessageId = MessageId(peerId: PeerId(messageIdPeerId), namespace: messageIdNamespace, id: messageIdId) } else { self.replyToMessageId = nil } + self.textSelection = nil } - public func encode(_ encoder: PostboxEncoder) { - encoder.encodeString(self.text, forKey: "t") - encoder.encodeObjectArray(self.entities, forKey: "e") - encoder.encodeInt32(self.timestamp, forKey: "s") + public func encode(to encoder: Encoder) throws { + var container = encoder.container(keyedBy: StringCodingKey.self) + + try container.encode(self.text, forKey: "t") + try container.encode(self.entities, forKey: "e") + try container.encode(self.timestamp, forKey: "s") if let replyToMessageId = self.replyToMessageId { - encoder.encodeInt64(replyToMessageId.peerId.toInt64(), forKey: "m.p") - encoder.encodeInt32(replyToMessageId.namespace, forKey: "m.n") - encoder.encodeInt32(replyToMessageId.id, forKey: "m.i") + try container.encode(replyToMessageId.peerId.toInt64(), forKey: "m.p") + try container.encode(replyToMessageId.namespace, forKey: "m.n") + try container.encode(replyToMessageId.id, forKey: "m.i") } else { - encoder.encodeNil(forKey: "m.p") - encoder.encodeNil(forKey: "m.n") - encoder.encodeNil(forKey: "m.i") + try container.encodeNil(forKey: "m.p") + try container.encodeNil(forKey: "m.n") + try container.encodeNil(forKey: "m.i") } } @@ -53,11 +60,46 @@ public struct SynchronizeableChatInputState: PostboxCoding, Equatable { if lhs.timestamp != rhs.timestamp { return false } + if lhs.textSelection != rhs.textSelection { + return false + } return true } } -public protocol SynchronizeableChatInterfaceState: PeerChatInterfaceState { - var synchronizeableInputState: SynchronizeableChatInputState? { get } - func withUpdatedSynchronizeableInputState(_ state: SynchronizeableChatInputState?) -> SynchronizeableChatInterfaceState +class InternalChatInterfaceState: Codable { + let synchronizeableInputState: SynchronizeableChatInputState? + let historyScrollMessageIndex: MessageIndex? + let opaqueData: Data? + + init( + synchronizeableInputState: SynchronizeableChatInputState?, + historyScrollMessageIndex: MessageIndex?, + opaqueData: Data? + ) { + self.synchronizeableInputState = synchronizeableInputState + self.historyScrollMessageIndex = historyScrollMessageIndex + self.opaqueData = opaqueData + } +} + +func _internal_updateChatInputState(transaction: Transaction, peerId: PeerId, inputState: SynchronizeableChatInputState?) { + var previousState: InternalChatInterfaceState? + if let peerChatInterfaceState = transaction.getPeerChatInterfaceState(peerId), let data = peerChatInterfaceState.data { + previousState = (try? AdaptedPostboxDecoder().decode(InternalChatInterfaceState.self, from: data)) + } + + if let updatedStateData = try? AdaptedPostboxEncoder().encode(InternalChatInterfaceState( + synchronizeableInputState: inputState, + historyScrollMessageIndex: previousState?.historyScrollMessageIndex, + opaqueData: previousState?.opaqueData + )) { + let storedState = StoredPeerChatInterfaceState( + overrideChatTimestamp: inputState?.timestamp, + historyScrollMessageIndex: previousState?.historyScrollMessageIndex, + associatedMessageIds: (inputState?.replyToMessageId).flatMap({ [$0] }) ?? [], + data: updatedStateData + ) + transaction.setPeerChatInterfaceState(peerId, state: storedState) + } } diff --git a/submodules/TelegramCore/Sources/SyncCore/SyncCore_TextEntitiesMessageAttribute.swift b/submodules/TelegramCore/Sources/SyncCore/SyncCore_TextEntitiesMessageAttribute.swift index e0c3f565a9..f1503ed736 100644 --- a/submodules/TelegramCore/Sources/SyncCore/SyncCore_TextEntitiesMessageAttribute.swift +++ b/submodules/TelegramCore/Sources/SyncCore/SyncCore_TextEntitiesMessageAttribute.swift @@ -23,7 +23,7 @@ public enum MessageTextEntityType: Equatable { case Custom(type: CustomEntityType) } -public struct MessageTextEntity: PostboxCoding, Equatable { +public struct MessageTextEntity: PostboxCoding, Codable, Equatable { public let range: Range public let type: MessageTextEntityType @@ -74,6 +74,60 @@ public struct MessageTextEntity: PostboxCoding, Equatable { self.type = .Unknown } } + + public init(from decoder: Decoder) throws { + let container = try decoder.container(keyedBy: StringCodingKey.self) + + let rangeStart: Int32 = (try? container.decode(Int32.self, forKey: "start")) ?? 0 + var rangeEnd: Int32 = (try? container.decode(Int32.self, forKey: "end")) ?? 0 + rangeEnd = max(rangeEnd, rangeStart) + + let type: Int32 = (try? container.decode(Int32.self, forKey: "_rawValue")) ?? 0 + + self.range = Int(rangeStart) ..< Int(rangeEnd) + + switch type { + case 1: + self.type = .Mention + case 2: + self.type = .Hashtag + case 3: + self.type = .BotCommand + case 4: + self.type = .Url + case 5: + self.type = .Email + case 6: + self.type = .Bold + case 7: + self.type = .Italic + case 8: + self.type = .Code + case 9: + self.type = .Pre + case 10: + let url = (try? container.decode(String.self, forKey: "url")) ?? "" + self.type = .TextUrl(url: url) + case 11: + let peerId = (try? container.decode(Int64.self, forKey: "peerId")) ?? 0 + self.type = .TextMention(peerId: PeerId(peerId)) + case 12: + self.type = .PhoneNumber + case 13: + self.type = .Strikethrough + case 14: + self.type = .BlockQuote + case 15: + self.type = .Underline + case 16: + self.type = .BankCard + case Int32.max: + let customType: Int32 = (try? container.decode(Int32.self, forKey: "type")) ?? 0 + self.type = .Custom(type: customType) + default: + self.type = .Unknown + } + } public func encode(_ encoder: PostboxEncoder) { encoder.encodeInt32(Int32(self.range.lowerBound), forKey: "start") @@ -120,6 +174,54 @@ public struct MessageTextEntity: PostboxCoding, Equatable { encoder.encodeInt32(type, forKey: "type") } } + + public func encode(to encoder: Encoder) throws { + var container = encoder.container(keyedBy: StringCodingKey.self) + + try container.encode(Int32(self.range.lowerBound), forKey: "start") + try container.encode(Int32(self.range.upperBound), forKey: "end") + switch self.type { + case .Unknown: + try container.encode(0 as Int32, forKey: "_rawValue") + case .Mention: + try container.encode(1 as Int32, forKey: "_rawValue") + case .Hashtag: + try container.encode(2 as Int32, forKey: "_rawValue") + case .BotCommand: + try container.encode(3 as Int32, forKey: "_rawValue") + case .Url: + try container.encode(4 as Int32, forKey: "_rawValue") + case .Email: + try container.encode(5 as Int32, forKey: "_rawValue") + case .Bold: + try container.encode(6 as Int32, forKey: "_rawValue") + case .Italic: + try container.encode(7 as Int32, forKey: "_rawValue") + case .Code: + try container.encode(8 as Int32, forKey: "_rawValue") + case .Pre: + try container.encode(9 as Int32, forKey: "_rawValue") + case let .TextUrl(url): + try container.encode(10 as Int32, forKey: "_rawValue") + try container.encode(url, forKey: "url") + case let .TextMention(peerId): + try container.encode(11 as Int32, forKey: "_rawValue") + try container.encode(peerId.toInt64(), forKey: "peerId") + case .PhoneNumber: + try container.encode(12 as Int32, forKey: "_rawValue") + case .Strikethrough: + try container.encode(13 as Int32, forKey: "_rawValue") + case .BlockQuote: + try container.encode(14 as Int32, forKey: "_rawValue") + case .Underline: + try container.encode(15 as Int32, forKey: "_rawValue") + case .BankCard: + try container.encode(16 as Int32, forKey: "_rawValue") + case let .Custom(type): + try container.encode(Int32.max as Int32, forKey: "_rawValue") + try container.encode(type as Int32, forKey: "type") + } + } public static func ==(lhs: MessageTextEntity, rhs: MessageTextEntity) -> Bool { return lhs.range == rhs.range && lhs.type == rhs.type diff --git a/submodules/TelegramCore/Sources/TelegramEngine/Messages/ClearCloudDrafts.swift b/submodules/TelegramCore/Sources/TelegramEngine/Messages/ClearCloudDrafts.swift index 0205e1f3cb..131cdef8bb 100644 --- a/submodules/TelegramCore/Sources/TelegramEngine/Messages/ClearCloudDrafts.swift +++ b/submodules/TelegramCore/Sources/TelegramEngine/Messages/ClearCloudDrafts.swift @@ -41,13 +41,7 @@ func _internal_clearCloudDraftsInteractively(postbox: Postbox, network: Network, updatePeerPresences(transaction: transaction, accountPeerId: accountPeerId, peerPresences: peerPresences) var signals: [Signal] = [] for peerId in peerIds { - transaction.updatePeerChatInterfaceState(peerId, update: { current in - if let current = current as? SynchronizeableChatInterfaceState { - return current.withUpdatedSynchronizeableInputState(nil) - } else { - return nil - } - }) + _internal_updateChatInputState(transaction: transaction, peerId: peerId, inputState: nil) if let peer = transaction.getPeer(peerId), let inputPeer = apiInputPeer(peer) { signals.append(network.request(Api.functions.messages.saveDraft(flags: 0, replyToMsgId: nil, peer: inputPeer, message: "", entities: nil)) diff --git a/submodules/TelegramCore/Sources/TelegramEngine/Peers/OpaqueChatState.swift b/submodules/TelegramCore/Sources/TelegramEngine/Peers/OpaqueChatState.swift new file mode 100644 index 0000000000..db80a6bd81 --- /dev/null +++ b/submodules/TelegramCore/Sources/TelegramEngine/Peers/OpaqueChatState.swift @@ -0,0 +1,5 @@ +import Postbox + +public protocol EngineOpaqueChatState: AnyObject, Codable { + func isEqual(to other: EngineOpaqueChatState) -> Bool +} diff --git a/submodules/TelegramCore/Sources/TelegramEngine/Peers/RemovePeerChat.swift b/submodules/TelegramCore/Sources/TelegramEngine/Peers/RemovePeerChat.swift index 71e835ee0e..76a96034af 100644 --- a/submodules/TelegramCore/Sources/TelegramEngine/Peers/RemovePeerChat.swift +++ b/submodules/TelegramCore/Sources/TelegramEngine/Peers/RemovePeerChat.swift @@ -25,13 +25,7 @@ func _internal_terminateSecretChat(transaction: Transaction, peerId: PeerId, req func _internal_removePeerChat(account: Account, transaction: Transaction, mediaBox: MediaBox, peerId: PeerId, reportChatSpam: Bool, deleteGloballyIfPossible: Bool) { if let _ = transaction.getPeerChatInterfaceState(peerId) { - transaction.updatePeerChatInterfaceState(peerId, update: { current in - if let current = current { - return account.auxiliaryMethods.updatePeerChatInputState(current, nil) - } else { - return nil - } - }) + transaction.setPeerChatInterfaceState(peerId, state: nil) } _internal_updateChatListFiltersInteractively(transaction: transaction, { filters in var filters = filters diff --git a/submodules/TelegramCore/Sources/TelegramEngine/Peers/TelegramEnginePeers.swift b/submodules/TelegramCore/Sources/TelegramEngine/Peers/TelegramEnginePeers.swift index 6ae77fdf6d..d38064737a 100644 --- a/submodules/TelegramCore/Sources/TelegramEngine/Peers/TelegramEnginePeers.swift +++ b/submodules/TelegramCore/Sources/TelegramEngine/Peers/TelegramEnginePeers.swift @@ -8,6 +8,22 @@ public enum AddressNameValidationStatus: Equatable { case availability(AddressNameAvailability) } +public final class OpaqueChatInterfaceState { + public let opaqueData: Data? + public let historyScrollMessageIndex: MessageIndex? + public let synchronizeableInputState: SynchronizeableChatInputState? + + public init( + opaqueData: Data?, + historyScrollMessageIndex: MessageIndex?, + synchronizeableInputState: SynchronizeableChatInputState? + ) { + self.opaqueData = opaqueData + self.historyScrollMessageIndex = historyScrollMessageIndex + self.synchronizeableInputState = synchronizeableInputState + } +} + public extension TelegramEngine { final class Peers { private let account: Account @@ -508,5 +524,86 @@ public extension TelegramEngine { return results.first?.0 } } + + public func getOpaqueChatInterfaceState(peerId: PeerId, threadId: Int64?) -> Signal { + return self.account.postbox.transaction { transaction -> OpaqueChatInterfaceState? in + let storedState: StoredPeerChatInterfaceState? + if let threadId = threadId { + storedState = transaction.getPeerChatThreadInterfaceState(peerId, threadId: threadId) + } else { + storedState = transaction.getPeerChatInterfaceState(peerId) + } + + guard let state = storedState, let data = state.data else { + return nil + } + guard let internalState = try? AdaptedPostboxDecoder().decode(InternalChatInterfaceState.self, from: data) else { + return nil + } + return OpaqueChatInterfaceState( + opaqueData: internalState.opaqueData, + historyScrollMessageIndex: internalState.historyScrollMessageIndex, + synchronizeableInputState: internalState.synchronizeableInputState + ) + } + } + + public func setOpaqueChatInterfaceState(peerId: PeerId, threadId: Int64?, state: OpaqueChatInterfaceState) -> Signal { + return self.account.postbox.transaction { transaction -> Void in + guard let data = try? AdaptedPostboxEncoder().encode(InternalChatInterfaceState( + synchronizeableInputState: state.synchronizeableInputState, + historyScrollMessageIndex: state.historyScrollMessageIndex, + opaqueData: state.opaqueData + )) else { + return + } + + #if DEBUG + let _ = try! AdaptedPostboxDecoder().decode(InternalChatInterfaceState.self, from: data) + #endif + + let storedState = StoredPeerChatInterfaceState( + overrideChatTimestamp: state.synchronizeableInputState?.timestamp, + historyScrollMessageIndex: state.historyScrollMessageIndex, + associatedMessageIds: (state.synchronizeableInputState?.replyToMessageId).flatMap({ [$0] }) ?? [], + data: data + ) + + if let threadId = threadId { + transaction.setPeerChatThreadInterfaceState(peerId, threadId: threadId, state: storedState) + } else { + var currentInputState: SynchronizeableChatInputState? + if let peerChatInterfaceState = transaction.getPeerChatInterfaceState(peerId), let data = peerChatInterfaceState.data { + currentInputState = (try? AdaptedPostboxDecoder().decode(InternalChatInterfaceState.self, from: data))?.synchronizeableInputState + } + let updatedInputState = state.synchronizeableInputState + + if currentInputState != updatedInputState { + if peerId.namespace == Namespaces.Peer.CloudUser || peerId.namespace == Namespaces.Peer.CloudChannel || peerId.namespace == Namespaces.Peer.CloudGroup { + addSynchronizeChatInputStateOperation(transaction: transaction, peerId: peerId) + } + } + transaction.setPeerChatInterfaceState( + peerId, + state: storedState + ) + } + } + |> ignoreValues + } } } + +public func _internal_decodeStoredChatInterfaceState(state: StoredPeerChatInterfaceState) -> OpaqueChatInterfaceState? { + guard let data = state.data else { + return nil + } + guard let internalState = try? AdaptedPostboxDecoder().decode(InternalChatInterfaceState.self, from: data) else { + return nil + } + return OpaqueChatInterfaceState( + opaqueData: internalState.opaqueData, + historyScrollMessageIndex: internalState.historyScrollMessageIndex, + synchronizeableInputState: internalState.synchronizeableInputState + ) +} diff --git a/submodules/TelegramCore/Sources/UpdatePeerChatInterfaceState.swift b/submodules/TelegramCore/Sources/UpdatePeerChatInterfaceState.swift index 9a1429d590..8b13789179 100644 --- a/submodules/TelegramCore/Sources/UpdatePeerChatInterfaceState.swift +++ b/submodules/TelegramCore/Sources/UpdatePeerChatInterfaceState.swift @@ -1,26 +1 @@ -import Foundation -import Postbox -import SwiftSignalKit - -public func updatePeerChatInterfaceState(account: Account, peerId: PeerId, threadId: Int64?, state: SynchronizeableChatInterfaceState) -> Signal { - return account.postbox.transaction { transaction -> Void in - if let threadId = threadId { - transaction.updatePeerChatThreadInterfaceState(peerId, threadId: threadId, update: { _ in - return state - }) - } else { - let currentInputState = (transaction.getPeerChatInterfaceState(peerId) as? SynchronizeableChatInterfaceState)?.synchronizeableInputState - let updatedInputState = state.synchronizeableInputState - - if currentInputState != updatedInputState { - if peerId.namespace == Namespaces.Peer.CloudUser || peerId.namespace == Namespaces.Peer.CloudChannel || peerId.namespace == Namespaces.Peer.CloudGroup { - addSynchronizeChatInputStateOperation(transaction: transaction, peerId: peerId) - } - } - transaction.updatePeerChatInterfaceState(peerId, update: { _ in - return state - }) - } - } -} diff --git a/submodules/TelegramUI/Sources/ChatController.swift b/submodules/TelegramUI/Sources/ChatController.swift index 396c93fe20..416f1dd16a 100644 --- a/submodules/TelegramUI/Sources/ChatController.swift +++ b/submodules/TelegramUI/Sources/ChatController.swift @@ -3803,7 +3803,7 @@ public final class ChatControllerImpl: TelegramBaseController, ChatController, G } }) - if case let .peer(peerId) = self.chatLocation { + /*if case let .peer(peerId) = self.chatLocation { self.importStateDisposable = (ChatHistoryImportTasks.importState(peerId: peerId) |> distinctUntilChanged |> deliverOnMainQueue).start(next: { [weak self] state in @@ -3817,7 +3817,7 @@ public final class ChatControllerImpl: TelegramBaseController, ChatController, G $0.updatedImportState(mappedState) }) }) - } + }*/ } required public init(coder aDecoder: NSCoder) { @@ -4282,7 +4282,10 @@ public final class ChatControllerImpl: TelegramBaseController, ChatController, G guard let strongSelf = self, let combinedInitialData = combinedInitialData else { return } - if let interfaceState = combinedInitialData.initialData?.chatInterfaceState as? ChatInterfaceState { + + if let opaqueState = (combinedInitialData.initialData?.storedInterfaceState).flatMap(_internal_decodeStoredChatInterfaceState) { + let interfaceState = ChatInterfaceState.parse(opaqueState) + var pinnedMessageId: MessageId? var peerIsBlocked: Bool = false var callsAvailable: Bool = true @@ -7515,7 +7518,9 @@ public final class ChatControllerImpl: TelegramBaseController, ChatController, G interfaceState = interfaceState.withUpdatedHistoryScrollState(scrollState) } interfaceState = interfaceState.withUpdatedInputLanguage(self.chatDisplayNode.currentTextInputLanguage) - let _ = updatePeerChatInterfaceState(account: self.context.account, peerId: peerId, threadId: threadId, state: interfaceState).start() + let _ = ChatInterfaceState.update(engine: self.context.engine, peerId: peerId, threadId: threadId, { _ in + return interfaceState + }).start() } override public func viewDidDisappear(_ animated: Bool) { @@ -11173,16 +11178,11 @@ public final class ChatControllerImpl: TelegramBaseController, ChatController, G } } } - - let _ = (strongSelf.context.account.postbox.transaction({ transaction -> Void in - transaction.updatePeerChatInterfaceState(peerId, update: { currentState in - if let currentState = currentState as? ChatInterfaceState { - return currentState.withUpdatedForwardMessageIds(messages.map { $0.id }) - } else { - return ChatInterfaceState().withUpdatedForwardMessageIds(messages.map { $0.id }) - } - }) - }) |> deliverOnMainQueue).start(completed: { + + let _ = (ChatInterfaceState.update(engine: strongSelf.context.engine, peerId: peerId, threadId: nil, { currentState in + return currentState.withUpdatedForwardMessageIds(messages.map { $0.id }) + }) + |> deliverOnMainQueue).start(completed: { if let strongSelf = self { strongSelf.updateChatPresentationInterfaceState(animated: false, interactive: true, { $0.updatedInterfaceState({ $0.withoutSelectionState() }) }) @@ -11266,14 +11266,8 @@ public final class ChatControllerImpl: TelegramBaseController, ChatController, G })) case let .chat(textInputState, subject, peekData): if let textInputState = textInputState { - let _ = (self.context.account.postbox.transaction({ transaction -> Void in - transaction.updatePeerChatInterfaceState(peerId, update: { currentState in - if let currentState = currentState as? ChatInterfaceState { - return currentState.withUpdatedComposeInputState(textInputState) - } else { - return ChatInterfaceState().withUpdatedComposeInputState(textInputState) - } - }) + let _ = (ChatInterfaceState.update(engine: self.context.engine, peerId: peerId, threadId: nil, { currentState in + return currentState.withUpdatedComposeInputState(textInputState) }) |> deliverOnMainQueue).start(completed: { [weak self] in if let strongSelf = self, let navigationController = strongSelf.effectiveNavigationController { @@ -11308,15 +11302,10 @@ public final class ChatControllerImpl: TelegramBaseController, ChatController, G }) strongController.dismiss() } else { - let _ = (strongSelf.context.account.postbox.transaction({ transaction -> Void in - transaction.updatePeerChatInterfaceState(peerId, update: { currentState in - if let currentState = currentState as? ChatInterfaceState { - return currentState.withUpdatedComposeInputState(textInputState) - } else { - return ChatInterfaceState().withUpdatedComposeInputState(textInputState) - } - }) - }) |> deliverOnMainQueue).start(completed: { + let _ = (ChatInterfaceState.update(engine: strongSelf.context.engine, peerId: peerId, threadId: nil, { currentState in + return currentState.withUpdatedComposeInputState(textInputState) + }) + |> deliverOnMainQueue).start(completed: { if let strongSelf = self { strongSelf.updateChatPresentationInterfaceState(animated: false, interactive: true, { $0.updatedInterfaceState({ $0.withoutSelectionState() }) }) diff --git a/submodules/TelegramUI/Sources/ChatHistoryViewForLocation.swift b/submodules/TelegramUI/Sources/ChatHistoryViewForLocation.swift index e8c9e35068..612fb3f74a 100644 --- a/submodules/TelegramUI/Sources/ChatHistoryViewForLocation.swift +++ b/submodules/TelegramUI/Sources/ChatHistoryViewForLocation.swift @@ -149,7 +149,7 @@ func chatHistoryViewForLocation(_ location: ChatHistoryLocationInput, context: A } } } - } else if view.isAddedToChatList, let historyScrollState = (initialData?.chatInterfaceState as? ChatInterfaceState)?.historyScrollState, tagMask == nil { + } else if view.isAddedToChatList, tagMask == nil, let historyScrollState = (initialData?.storedInterfaceState).flatMap(_internal_decodeStoredChatInterfaceState).flatMap(ChatInterfaceState.parse)?.historyScrollState { scrollPosition = .positionRestoration(index: historyScrollState.messageIndex, relativeOffset: CGFloat(historyScrollState.relativeOffset)) } else { if case .peer = chatLocation, !view.isAddedToChatList { diff --git a/submodules/TelegramUI/Sources/ChatMediaInputNode.swift b/submodules/TelegramUI/Sources/ChatMediaInputNode.swift index 243f33012b..c20077f39a 100644 --- a/submodules/TelegramUI/Sources/ChatMediaInputNode.swift +++ b/submodules/TelegramUI/Sources/ChatMediaInputNode.swift @@ -761,16 +761,20 @@ final class ChatMediaInputNode: ChatInputNode { let inputNodeInteraction = self.inputNodeInteraction! let peerSpecificPack: Signal<(PeerSpecificPackData?, CanInstallPeerSpecificPack), NoError> if let peerId = peerId { - self.dismissedPeerSpecificStickerPack.set(context.account.postbox.transaction { transaction -> Bool in - guard let state = transaction.getPeerChatInterfaceState(peerId) as? ChatInterfaceState else { + self.dismissedPeerSpecificStickerPack.set( + context.engine.peers.getOpaqueChatInterfaceState(peerId: peerId, threadId: nil) + |> map { opaqueState -> Bool in + guard let opaqueState = opaqueState else { + return false + } + let interfaceState = ChatInterfaceState.parse(opaqueState) + + if interfaceState.messageActionsState.closedPeerSpecificPackSetup { + return true + } return false } - if state.messageActionsState.closedPeerSpecificPackSetup { - return true - } - - return false - }) + ) peerSpecificPack = combineLatest(context.engine.peers.peerSpecificStickerPack(peerId: peerId), context.account.postbox.multiplePeersView([peerId]), self.dismissedPeerSpecificStickerPack.get()) |> map { packData, peersView, dismissedPeerSpecificPack -> (PeerSpecificPackData?, CanInstallPeerSpecificPack) in if let peer = peersView.peers[peerId] { @@ -2094,17 +2098,11 @@ final class ChatMediaInputNode: ChatInputNode { return } self.dismissedPeerSpecificStickerPack.set(.single(true)) - let _ = (self.context.account.postbox.transaction { transaction -> Void in - transaction.updatePeerChatInterfaceState(peerId, update: { current in - if let current = current as? ChatInterfaceState { - return current.withUpdatedMessageActionsState({ value in - var value = value - value.closedPeerSpecificPackSetup = true - return value - }) - } else { - return current - } + let _ = ChatInterfaceState.update(engine: self.context.engine, peerId: peerId, threadId: nil, { current in + return current.withUpdatedMessageActionsState({ value in + var value = value + value.closedPeerSpecificPackSetup = true + return value }) }).start() } diff --git a/submodules/TelegramUI/Sources/DeclareEncodables.swift b/submodules/TelegramUI/Sources/DeclareEncodables.swift index 747c363eea..5eaaa38693 100644 --- a/submodules/TelegramUI/Sources/DeclareEncodables.swift +++ b/submodules/TelegramUI/Sources/DeclareEncodables.swift @@ -15,7 +15,6 @@ import ChatInterfaceState private var telegramUIDeclaredEncodables: Void = { declareEncodable(InAppNotificationSettings.self, f: { InAppNotificationSettings(decoder: $0) }) - declareEncodable(ChatInterfaceState.self, f: { ChatInterfaceState(decoder: $0) }) declareEncodable(VideoLibraryMediaResource.self, f: { VideoLibraryMediaResource(decoder: $0) }) declareEncodable(LocalFileVideoMediaResource.self, f: { LocalFileVideoMediaResource(decoder: $0) }) declareEncodable(LocalFileGifMediaResource.self, f: { LocalFileGifMediaResource(decoder: $0) }) diff --git a/submodules/TelegramUI/Sources/OpenResolvedUrl.swift b/submodules/TelegramUI/Sources/OpenResolvedUrl.swift index c0d08dee99..f7a2fe8dfc 100644 --- a/submodules/TelegramUI/Sources/OpenResolvedUrl.swift +++ b/submodules/TelegramUI/Sources/OpenResolvedUrl.swift @@ -222,14 +222,8 @@ func openResolvedUrlImpl(_ resolvedUrl: ResolvedUrl, context: AccountContext, ur } if let textInputState = textInputState { - let _ = (context.account.postbox.transaction({ transaction -> Void in - transaction.updatePeerChatInterfaceState(peerId, update: { currentState in - if let currentState = currentState as? ChatInterfaceState { - return currentState.withUpdatedComposeInputState(textInputState) - } else { - return ChatInterfaceState().withUpdatedComposeInputState(textInputState) - } - }) + let _ = (ChatInterfaceState.update(engine: context.engine, peerId: peerId, threadId: nil, { currentState in + return currentState.withUpdatedComposeInputState(textInputState) }) |> deliverOnMainQueue).start(completed: { navigationController?.pushViewController(ChatControllerImpl(context: context, chatLocation: .peer(peerId))) diff --git a/submodules/TelegramUI/Sources/PeerInfo/PeerInfoScreen.swift b/submodules/TelegramUI/Sources/PeerInfo/PeerInfoScreen.swift index 26de979e7a..fa26a84218 100644 --- a/submodules/TelegramUI/Sources/PeerInfo/PeerInfoScreen.swift +++ b/submodules/TelegramUI/Sources/PeerInfo/PeerInfoScreen.swift @@ -5771,15 +5771,10 @@ private final class PeerInfoScreenNode: ViewControllerTracingNode, UIScrollViewD peerSelectionController.dismiss() } } else { - let _ = (strongSelf.context.account.postbox.transaction({ transaction -> Void in - transaction.updatePeerChatInterfaceState(peerId, update: { currentState in - if let currentState = currentState as? ChatInterfaceState { - return currentState.withUpdatedForwardMessageIds(Array(messageIds)) - } else { - return ChatInterfaceState().withUpdatedForwardMessageIds(Array(messageIds)) - } - }) - }) |> deliverOnMainQueue).start(completed: { + let _ = (ChatInterfaceState.update(engine: strongSelf.context.engine, peerId: peerId, threadId: nil, { currentState in + return currentState.withUpdatedForwardMessageIds(Array(messageIds)) + }) + |> deliverOnMainQueue).start(completed: { if let strongSelf = self { strongSelf.headerNode.navigationButtonContainer.performAction?(.selectionDone) diff --git a/submodules/TelegramUI/Sources/TelegramAccountAuxiliaryMethods.swift b/submodules/TelegramUI/Sources/TelegramAccountAuxiliaryMethods.swift index e0d2a72d05..3e06830a34 100644 --- a/submodules/TelegramUI/Sources/TelegramAccountAuxiliaryMethods.swift +++ b/submodules/TelegramUI/Sources/TelegramAccountAuxiliaryMethods.swift @@ -12,15 +12,7 @@ import WallpaperResources import AppBundle import SwiftSignalKit -public let telegramAccountAuxiliaryMethods = AccountAuxiliaryMethods(updatePeerChatInputState: { interfaceState, inputState -> PeerChatInterfaceState? in - if interfaceState == nil { - return ChatInterfaceState().withUpdatedSynchronizeableInputState(inputState) - } else if let interfaceState = interfaceState as? ChatInterfaceState { - return interfaceState.withUpdatedSynchronizeableInputState(inputState) - } else { - return interfaceState - } -}, fetchResource: { account, resource, ranges, _ in +public let telegramAccountAuxiliaryMethods = AccountAuxiliaryMethods(fetchResource: { account, resource, ranges, _ in if let resource = resource as? VideoLibraryMediaResource { return fetchVideoLibraryMediaResource(account: account, resource: resource) } else if let resource = resource as? LocalFileVideoMediaResource {