Swiftgram/TelegramCore/AccountIntermediateState.swift
2017-08-29 17:20:33 +03:00

394 lines
17 KiB
Swift

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<PeerId>
let messageIds: Set<MessageId>
let channelStates: [PeerId: ChannelState]
let peerNotificationSettings: [PeerId: PeerNotificationSettings]
let peerIdsWithNewMessages: Set<PeerId>
let locallyGeneratedMessageTimestamps: [PeerId: [(MessageId.Namespace, Int32)]]
let cloudReadStates: [PeerId: PeerReadState]
init(state: AuthorizedAccountState.State, peerIds: Set<PeerId>, messageIds: Set<MessageId>, peerIdsWithNewMessages: Set<PeerId>, 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<MessageId>
var readInboxMaxIds: [PeerId: MessageId]
var storedMessagesByPeerIdAndTimestamp: [PeerId: Set<MessageIndex>]
var insertedPeers: [PeerId: Peer] = [:]
var preCachedResources: [(MediaResource, Data)] = []
init(initialState: AccountInitialState, initialPeers: [PeerId: Peer], initialStoredMessages: Set<MessageId>, initialReadInboxMaxIds: [PeerId: MessageId], storedMessagesByPeerIdAndTimestamp: [PeerId: Set<MessageIndex>]) {
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<MessageId>, readInboxMaxIds: [PeerId: MessageId], storedMessagesByPeerIdAndTimestamp: [PeerId: Set<MessageIndex>], 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)
}
}