import Foundation #if os(macOS) import PostboxMac import SwiftSignalKitMac import MtProtoKitMac #else import Postbox import SwiftSignalKit import MtProtoKitDynamic #endif final class AccountInitialState { let state: AuthorizedAccountState.State let peerIds: Set let messageIds: Set let channelStates: [PeerId: ChannelState] let peerNotificationSettings: [PeerId: PeerNotificationSettings] let peerIdsWithNewMessages: Set let locallyGeneratedMessageTimestamps: [PeerId: [(MessageId.Namespace, Int32)]] let cloudReadStates: [PeerId: PeerReadState] init(state: AuthorizedAccountState.State, peerIds: Set, messageIds: Set, peerIdsWithNewMessages: Set, channelStates: [PeerId: ChannelState], peerNotificationSettings: [PeerId: PeerNotificationSettings], locallyGeneratedMessageTimestamps: [PeerId: [(MessageId.Namespace, Int32)]], cloudReadStates: [PeerId: PeerReadState]) { self.state = state self.peerIds = peerIds self.messageIds = messageIds self.channelStates = channelStates self.peerIdsWithNewMessages = peerIdsWithNewMessages self.peerNotificationSettings = peerNotificationSettings self.locallyGeneratedMessageTimestamps = locallyGeneratedMessageTimestamps self.cloudReadStates = cloudReadStates } } enum AccountStateUpdatePinnerPeerIdsOperation { case pin(PeerId) case unpin(PeerId) case reorder([PeerId]) case sync } enum AccountStateUpdateStickerPacksOperation { case add(Api.messages.StickerSet) case reorder(SynchronizeInstalledStickerPacksOperationNamespace, [Int64]) case sync } enum AccountStateMutationOperation { case AddMessages([StoreMessage], AddMessagesLocation) case DeleteMessagesWithGlobalIds([Int32]) case DeleteMessages([MessageId]) case EditMessage(MessageId, StoreMessage) case UpdateMedia(MediaId, Media?) case ReadInbox(MessageId) case ReadOutbox(MessageId) case ResetReadState(PeerId, MessageId.Namespace, MessageId.Id, MessageId.Id, MessageId.Id, Int32) case ResetMessageTagSummary(PeerId, MessageId.Namespace, Int32, MessageHistoryTagNamespaceCountValidityRange) case UpdateState(AuthorizedAccountState.State) case UpdateChannelState(PeerId, ChannelState) case UpdatePeerNotificationSettings(PeerId, PeerNotificationSettings) case AddHole(MessageId) case MergeApiChats([Api.Chat]) case UpdatePeer(PeerId, (Peer?) -> Peer?) case UpdateCachedPeerData(PeerId, (CachedPeerData?) -> CachedPeerData?) case MergeApiUsers([Api.User]) case MergePeerPresences([PeerId: PeerPresence]) case UpdateSecretChat(chat: Api.EncryptedChat, timestamp: Int32) case AddSecretMessages([Api.EncryptedMessage]) case ReadSecretOutbox(peerId: PeerId, maxTimestamp: Int32, actionTimestamp: Int32) case AddPeerInputActivity(chatPeerId: PeerId, peerId: PeerId?, activity: PeerInputActivity?) case UpdatePinnedPeerIds(AccountStateUpdatePinnerPeerIdsOperation) case ReadMessageContents((PeerId?, [Int32])) case UpdateMessageImpressionCount(MessageId, Int32) case UpdateInstalledStickerPacks(AccountStateUpdateStickerPacksOperation) case UpdateChatInputState(PeerId, SynchronizeableChatInputState?) case UpdateCall(Api.PhoneCall) case UpdateLangPack(Api.LangPackDifference?) } struct AccountMutableState { let initialState: AccountInitialState let branchOperationIndex: Int var operations: [AccountStateMutationOperation] = [] var state: AuthorizedAccountState.State var peers: [PeerId: Peer] var channelStates: [PeerId: ChannelState] var peerNotificationSettings: [PeerId: PeerNotificationSettings] var storedMessages: Set var readInboxMaxIds: [PeerId: MessageId] var storedMessagesByPeerIdAndTimestamp: [PeerId: Set] var insertedPeers: [PeerId: Peer] = [:] var preCachedResources: [(MediaResource, Data)] = [] init(initialState: AccountInitialState, initialPeers: [PeerId: Peer], initialStoredMessages: Set, initialReadInboxMaxIds: [PeerId: MessageId], storedMessagesByPeerIdAndTimestamp: [PeerId: Set]) { self.initialState = initialState self.state = initialState.state self.peers = initialPeers self.storedMessages = initialStoredMessages self.readInboxMaxIds = initialReadInboxMaxIds self.channelStates = initialState.channelStates self.peerNotificationSettings = initialState.peerNotificationSettings self.storedMessagesByPeerIdAndTimestamp = storedMessagesByPeerIdAndTimestamp self.branchOperationIndex = 0 } init(initialState: AccountInitialState, operations: [AccountStateMutationOperation], state: AuthorizedAccountState.State, peers: [PeerId: Peer], channelStates: [PeerId: ChannelState], peerNotificationSettings: [PeerId: PeerNotificationSettings], storedMessages: Set, readInboxMaxIds: [PeerId: MessageId], storedMessagesByPeerIdAndTimestamp: [PeerId: Set], branchOperationIndex: Int) { self.initialState = initialState self.operations = operations self.state = state self.peers = peers self.channelStates = channelStates self.storedMessages = storedMessages self.peerNotificationSettings = peerNotificationSettings self.readInboxMaxIds = readInboxMaxIds self.storedMessagesByPeerIdAndTimestamp = storedMessagesByPeerIdAndTimestamp self.branchOperationIndex = branchOperationIndex } func branch() -> AccountMutableState { return AccountMutableState(initialState: self.initialState, operations: self.operations, state: self.state, peers: self.peers, channelStates: self.channelStates, peerNotificationSettings: self.peerNotificationSettings, storedMessages: self.storedMessages, readInboxMaxIds: self.readInboxMaxIds, storedMessagesByPeerIdAndTimestamp: self.storedMessagesByPeerIdAndTimestamp, branchOperationIndex: self.operations.count) } mutating func merge(_ other: AccountMutableState) { for i in other.branchOperationIndex ..< other.operations.count { self.addOperation(other.operations[i]) } for (_, peer) in other.insertedPeers { self.peers[peer.id] = peer } self.preCachedResources.append(contentsOf: other.preCachedResources) } mutating func addPreCachedResource(_ resource: MediaResource, data: Data) { self.preCachedResources.append((resource, data)) } mutating func addMessages(_ messages: [StoreMessage], location: AddMessagesLocation) { self.addOperation(.AddMessages(messages, location)) } mutating func deleteMessagesWithGlobalIds(_ globalIds: [Int32]) { self.addOperation(.DeleteMessagesWithGlobalIds(globalIds)) } mutating func deleteMessages(_ messageIds: [MessageId]) { self.addOperation(.DeleteMessages(messageIds)) } mutating func editMessage(_ id: MessageId, message: StoreMessage) { self.addOperation(.EditMessage(id, message)) } mutating func updateMedia(_ id: MediaId, media: Media?) { self.addOperation(.UpdateMedia(id, media)) } mutating func readInbox(_ messageId: MessageId) { self.addOperation(.ReadInbox(messageId)) } mutating func readOutbox(_ messageId: MessageId) { self.addOperation(.ReadOutbox(messageId)) } mutating func resetReadState(_ peerId: PeerId, namespace: MessageId.Namespace, maxIncomingReadId: MessageId.Id, maxOutgoingReadId: MessageId.Id, maxKnownId: MessageId.Id, count: Int32) { self.addOperation(.ResetReadState(peerId, namespace, maxIncomingReadId, maxOutgoingReadId, maxKnownId, count)) } mutating func resetMessageTagSummary(_ peerId: PeerId, namespace: MessageId.Namespace, count: Int32, range: MessageHistoryTagNamespaceCountValidityRange) { self.addOperation(.ResetMessageTagSummary(peerId, namespace, count, range)) } mutating func updateState(_ state: AuthorizedAccountState.State) { self.addOperation(.UpdateState(state)) } mutating func updateChannelState(_ peerId: PeerId, state: ChannelState) { self.addOperation(.UpdateChannelState(peerId, state)) } mutating func updatePeerNotificationSettings(_ peerId: PeerId, notificationSettings: PeerNotificationSettings) { self.addOperation(.UpdatePeerNotificationSettings(peerId, notificationSettings)) } mutating func addHole(_ messageId: MessageId) { self.addOperation(.AddHole(messageId)) } mutating func mergeChats(_ chats: [Api.Chat]) { self.addOperation(.MergeApiChats(chats)) } mutating func updatePeer(_ id: PeerId, _ f: @escaping (Peer?) -> Peer?) { self.addOperation(.UpdatePeer(id, f)) } mutating func updateCachedPeerData(_ id: PeerId, _ f: @escaping (CachedPeerData?) -> CachedPeerData?) { self.addOperation(.UpdateCachedPeerData(id, f)) } mutating func updateLangPack(_ difference: Api.LangPackDifference?) { self.addOperation(.UpdateLangPack(difference)) } mutating func mergeUsers(_ users: [Api.User]) { self.addOperation(.MergeApiUsers(users)) var presences: [PeerId: PeerPresence] = [:] for user in users { switch user { case let .user(_, id, _, _, _, _, _, _, status, _, _, _, _): if let status = status { presences[PeerId(namespace: Namespaces.Peer.CloudUser, id: id)] = TelegramUserPresence(apiStatus: status) } break case .userEmpty: break } } if !presences.isEmpty { self.addOperation(.MergePeerPresences(presences)) } } mutating func mergePeerPresences(_ presences: [PeerId: PeerPresence]) { self.addOperation(.MergePeerPresences(presences)) } mutating func updateSecretChat(chat: Api.EncryptedChat, timestamp: Int32) { self.addOperation(.UpdateSecretChat(chat: chat, timestamp: timestamp)) } mutating func addSecretMessages(_ messages: [Api.EncryptedMessage]) { self.addOperation(.AddSecretMessages(messages)) } mutating func readSecretOutbox(peerId: PeerId, timestamp: Int32, actionTimestamp: Int32) { self.addOperation(.ReadSecretOutbox(peerId: peerId, maxTimestamp: timestamp, actionTimestamp: actionTimestamp)) } mutating func addPeerInputActivity(chatPeerId: PeerId, peerId: PeerId?, activity: PeerInputActivity?) { self.addOperation(.AddPeerInputActivity(chatPeerId: chatPeerId, peerId: peerId, activity: activity)) } mutating func addUpdatePinnedPeerIds(_ operation: AccountStateUpdatePinnerPeerIdsOperation) { self.addOperation(.UpdatePinnedPeerIds(operation)) } mutating func addReadMessagesContents(_ peerIdsAndMessageIds: (PeerId?, [Int32])) { self.addOperation(.ReadMessageContents(peerIdsAndMessageIds)) } mutating func addUpdateMessageImpressionCount(id: MessageId, count: Int32) { self.addOperation(.UpdateMessageImpressionCount(id, count)) } mutating func addUpdateInstalledStickerPacks(_ operation: AccountStateUpdateStickerPacksOperation) { self.addOperation(.UpdateInstalledStickerPacks(operation)) } mutating func addUpdateChatInputState(peerId: PeerId, state: SynchronizeableChatInputState?) { self.addOperation(.UpdateChatInputState(peerId, state)) } mutating func addUpdateCall(_ call: Api.PhoneCall) { self.addOperation(.UpdateCall(call)) } mutating func addOperation(_ operation: AccountStateMutationOperation) { switch operation { case .AddHole, .DeleteMessages, .DeleteMessagesWithGlobalIds, .EditMessage, .UpdateMedia, .ReadOutbox, .MergePeerPresences, .UpdateSecretChat, .AddSecretMessages, .ReadSecretOutbox, .AddPeerInputActivity, .UpdateCachedPeerData, .UpdatePinnedPeerIds, .ReadMessageContents, .UpdateMessageImpressionCount, .UpdateInstalledStickerPacks, .UpdateChatInputState, .UpdateCall, .UpdateLangPack: break case let .AddMessages(messages, _): for message in messages { if case let .Id(id) = message.id { self.storedMessages.insert(id) } } case let .UpdateState(state): self.state = state case let .UpdateChannelState(peerId, channelState): self.channelStates[peerId] = channelState case let .UpdatePeerNotificationSettings(peerId, notificationSettings): self.peerNotificationSettings[peerId] = notificationSettings case let .MergeApiChats(chats): for chat in chats { if let groupOrChannel = mergeGroupOrChannel(lhs: peers[chat.peerId], rhs: chat) { peers[groupOrChannel.id] = groupOrChannel insertedPeers[groupOrChannel.id] = groupOrChannel } } case let .MergeApiUsers(users): for apiUser in users { if let user = TelegramUser.merge(peers[apiUser.peerId] as? TelegramUser, rhs: apiUser) { peers[user.id] = user insertedPeers[user.id] = user } } case let .UpdatePeer(id, f): let peer = self.peers[id] if let updatedPeer = f(peer) { peers[id] = updatedPeer insertedPeers[id] = updatedPeer } case let .ReadInbox(messageId): let current = self.readInboxMaxIds[messageId.peerId] if current == nil || current! < messageId { self.readInboxMaxIds[messageId.peerId] = messageId } case let .ResetReadState(peerId, namespace, maxIncomingReadId, _, _, _): let current = self.readInboxMaxIds[peerId] if namespace == Namespaces.Message.Cloud { if current == nil || current!.id < maxIncomingReadId { self.readInboxMaxIds[peerId] = MessageId(peerId: peerId, namespace: namespace, id: maxIncomingReadId) } } case let .ResetMessageTagSummary(peerId, namespace, count, range): break } self.operations.append(operation) } } struct AccountFinalState { let state: AccountMutableState let shouldPoll: Bool let incomplete: Bool } struct AccountReplayedFinalState { let state: AccountFinalState let addedSecretMessageIds: [MessageId] let updatedTypingActivities: [PeerId: [PeerId: PeerInputActivity?]] let updatedWebpages: [MediaId: TelegramMediaWebpage] let updatedCalls: [Api.PhoneCall] } struct AccountFinalStateEvents { let addedIncomingMessageIds: [MessageId] let updatedTypingActivities: [PeerId: [PeerId: PeerInputActivity?]] let updatedWebpages: [MediaId: TelegramMediaWebpage] let updatedCalls: [Api.PhoneCall] var isEmpty: Bool { return self.addedIncomingMessageIds.isEmpty && self.updatedTypingActivities.isEmpty && self.updatedWebpages.isEmpty && self.updatedCalls.isEmpty } init() { self.addedIncomingMessageIds = [] self.updatedTypingActivities = [:] self.updatedWebpages = [:] self.updatedCalls = [] } init(addedIncomingMessageIds: [MessageId], updatedTypingActivities: [PeerId: [PeerId: PeerInputActivity?]], updatedWebpages: [MediaId: TelegramMediaWebpage], updatedCalls: [Api.PhoneCall]) { self.addedIncomingMessageIds = addedIncomingMessageIds self.updatedTypingActivities = updatedTypingActivities self.updatedWebpages = updatedWebpages self.updatedCalls = updatedCalls } init(state: AccountReplayedFinalState) { var addedIncomingMessageIds: [MessageId] = [] for operation in state.state.state.operations { switch operation { case let .AddMessages(messages, location): if case .UpperHistoryBlock = location { for message in messages { if case let .Id(id) = message.id, message.flags.contains(.Incoming) { addedIncomingMessageIds.append(id) } } } default: break } } addedIncomingMessageIds.append(contentsOf: state.addedSecretMessageIds) self.addedIncomingMessageIds = addedIncomingMessageIds self.updatedTypingActivities = state.updatedTypingActivities self.updatedWebpages = state.updatedWebpages self.updatedCalls = state.updatedCalls } func union(with other: AccountFinalStateEvents) -> AccountFinalStateEvents { return AccountFinalStateEvents(addedIncomingMessageIds: self.addedIncomingMessageIds + other.addedIncomingMessageIds, updatedTypingActivities: self.updatedTypingActivities, updatedWebpages: self.updatedWebpages, updatedCalls: self.updatedCalls + other.updatedCalls) } }