import Foundation import SwiftSignalKit import Postbox import TelegramApi #if canImport(SGDeletedMessages) import SGDeletedMessages #if canImport(SGLogging) import SGLogging #endif #endif #if canImport(SGSimpleSettings) import SGSimpleSettings #endif func _internal_resetAccountState(postbox: Postbox, network: Network, accountPeerId: PeerId) -> Signal { return network.request(Api.functions.updates.getState()) |> retryRequest |> mapToSignal { state -> Signal in let chatList = fetchChatList(accountPeerId: accountPeerId, postbox: postbox, network: network, location: .general, upperBound: .absoluteUpperBound(), hash: 0, limit: 100) return chatList |> mapToSignal { fetchedChats -> Signal in guard let fetchedChats = fetchedChats else { return .never() } return withResolvedAssociatedMessages(postbox: postbox, source: .network(network), accountPeerId: accountPeerId, parsedPeers: fetchedChats.peers, storeMessages: fetchedChats.storeMessages, resolveThreads: false, { transaction, additionalPeers, additionalMessages -> Void in for peerId in transaction.chatListGetAllPeerIds() { if peerId.namespace != Namespaces.Peer.SecretChat { transaction.updatePeerChatListInclusion(peerId, inclusion: .notIncluded) } if peerId.namespace != Namespaces.Peer.SecretChat { transaction.addHole(peerId: peerId, threadId: nil, namespace: Namespaces.Message.Cloud, space: .everywhere, range: 1 ... (Int32.max - 1)) } if peerId.namespace == Namespaces.Peer.CloudChannel { if let channel = transaction.getPeer(peerId) as? TelegramChannel, channel.isForumOrMonoForum { transaction.setPeerPinnedThreads(peerId: peerId, threadIds: []) for threadId in transaction.setMessageHistoryThreads(peerId: peerId) { transaction.setMessageHistoryThreadInfo(peerId: peerId, threadId: threadId, info: nil) transaction.addHole(peerId: peerId, threadId: threadId, namespace: Namespaces.Message.Cloud, space: .everywhere, range: 1 ... (Int32.max - 1)) } } transaction.updatePeerCachedData(peerIds: Set([peerId]), update: { _, _ in nil }) transaction.setPeerThreadCombinedState(peerId: peerId, state: nil) } } transaction.removeAllChatListEntries(groupId: .root, exceptPeerNamespace: Namespaces.Peer.SecretChat) transaction.removeAllChatListEntries(groupId: .group(1), exceptPeerNamespace: Namespaces.Peer.SecretChat) updatePeers(transaction: transaction, accountPeerId: accountPeerId, peers: fetchedChats.peers.union(with: additionalPeers)) for (threadMessageId, data) in fetchedChats.threadInfos { if let entry = StoredMessageHistoryThreadInfo(data.data) { transaction.setMessageHistoryThreadInfo(peerId: threadMessageId.peerId, threadId: threadMessageId.threadId, info: entry) } transaction.replaceMessageTagSummary(peerId: threadMessageId.peerId, threadId: threadMessageId.threadId, tagMask: .unseenPersonalMessage, namespace: Namespaces.Message.Cloud, customTag: nil, count: data.unreadMentionCount, maxId: data.topMessageId) transaction.replaceMessageTagSummary(peerId: threadMessageId.peerId, threadId: threadMessageId.threadId, tagMask: .unseenReaction, namespace: Namespaces.Message.Cloud, customTag: nil, count: data.unreadReactionCount, maxId: data.topMessageId) } transaction.updateCurrentPeerNotificationSettings(fetchedChats.notificationSettings) let _ = transaction.addMessages(fetchedChats.storeMessages, location: .UpperHistoryBlock) let _ = transaction.addMessages(additionalMessages, location: .Random) // MARK: - GLEGram - Bump maxKnownId for saved-deleted tops var readStates = fetchedChats.readStates #if canImport(SGDeletedMessages) if SGDeletedMessages.showDeletedMessages { for (peerId, namespaces) in readStates { guard let cloudState = namespaces[Namespaces.Message.Cloud] else { continue } switch cloudState { case let .idBased(maxIncomingReadId, maxOutgoingReadId, maxKnownId, count, markedUnread): if let localTop = transaction.getTopPeerMessageId(peerId: peerId, namespace: Namespaces.Message.Cloud), localTop.id > maxKnownId, let localTopMessage = transaction.getMessage(localTop), localTopMessage.sgDeletedAttribute.isDeleted { #if canImport(SGLogging) SGLogger.shared.log("SGDeletedMessages", "ResetState.resetIncomingReadStates: bump maxKnownId peerId=\(peerId) \(maxKnownId)→\(localTop.id) (saved deleted top)") #endif var updated = namespaces updated[Namespaces.Message.Cloud] = .idBased( maxIncomingReadId: maxIncomingReadId, maxOutgoingReadId: maxOutgoingReadId, maxKnownId: localTop.id, count: count, markedUnread: markedUnread ) readStates[peerId] = updated } default: break } } } #endif transaction.resetIncomingReadStates(readStates) for (peerId, autoremoveValue) in fetchedChats.ttlPeriods { transaction.updatePeerCachedData(peerIds: Set([peerId]), update: { _, current in if peerId.namespace == Namespaces.Peer.CloudUser { let current = (current as? CachedUserData) ?? CachedUserData() return current.withUpdatedAutoremoveTimeout(autoremoveValue) } else if peerId.namespace == Namespaces.Peer.CloudChannel { let current = (current as? CachedChannelData) ?? CachedChannelData() return current.withUpdatedAutoremoveTimeout(autoremoveValue) } else if peerId.namespace == Namespaces.Peer.CloudGroup { let current = (current as? CachedGroupData) ?? CachedGroupData() return current.withUpdatedAutoremoveTimeout(autoremoveValue) } else { return current } }) } for (peerId, value) in fetchedChats.viewForumAsMessages { if value { transaction.updatePeerCachedData(peerIds: Set([peerId]), update: { _, current in if peerId.namespace == Namespaces.Peer.CloudChannel { let current = (current as? CachedChannelData) ?? CachedChannelData() return current.withUpdatedViewForumAsMessages(.known(value)) } else { return current } }) } } for hole in transaction.allChatListHoles(groupId: .root) { transaction.replaceChatListHole(groupId: .root, index: hole.index, hole: nil) } for hole in transaction.allChatListHoles(groupId: .group(1)) { transaction.replaceChatListHole(groupId: .group(1), index: hole.index, hole: nil) } if let hole = fetchedChats.lowerNonPinnedIndex.flatMap(ChatListHole.init) { transaction.addChatListHole(groupId: .root, hole: hole) } transaction.addChatListHole(groupId: .group(1), hole: ChatListHole(index: MessageIndex(id: MessageId(peerId: PeerId(namespace: Namespaces.Peer.Empty, id: PeerId.Id._internalFromInt64Value(0)), namespace: Namespaces.Message.Cloud, id: 1), timestamp: Int32.max - 1))) for peerId in fetchedChats.chatPeerIds { if let peer = transaction.getPeer(peerId) { transaction.updatePeerChatListInclusion(peerId, inclusion: .ifHasMessagesOrOneOf(groupId: .root, pinningIndex: transaction.getPeerChatListIndex(peerId)?.1.pinningIndex, minTimestamp: minTimestampForPeerInclusion(peer))) } else { assertionFailure() } } for (peerId, peerGroupId) in fetchedChats.peerGroupIds { if let peer = transaction.getPeer(peerId) { transaction.updatePeerChatListInclusion(peerId, inclusion: .ifHasMessagesOrOneOf(groupId: peerGroupId, pinningIndex: nil, minTimestamp: minTimestampForPeerInclusion(peer))) } else { assertionFailure() } } for (peerId, pts) in fetchedChats.channelStates { if let current = transaction.getPeerChatState(peerId) as? ChannelState { transaction.setPeerChatState(peerId, state: current.withUpdatedPts(pts)) } else { transaction.setPeerChatState(peerId, state: ChannelState(pts: pts, invalidatedPts: nil, synchronizedUntilMessageId: nil)) } } if let replacePinnedItemIds = fetchedChats.pinnedItemIds { // MARK: - GLEGram - Unlimited pinned chats with local premium let serverPinned = replacePinnedItemIds.map(PinnedItemId.peer) let unlimitedPinned: Bool #if canImport(SGSimpleSettings) unlimitedPinned = SGSimpleSettings.shared.enableLocalPremium #else unlimitedPinned = false #endif if unlimitedPinned { let currentLocal = transaction.getPinnedItemIds(groupId: .root) let serverIds = Set(serverPinned) let localExtras = currentLocal.filter { !serverIds.contains($0) } let merged = serverPinned + localExtras transaction.setPinnedItemIds(groupId: .root, itemIds: merged) } else { transaction.setPinnedItemIds(groupId: .root, itemIds: serverPinned) } } for (peerId, summary) in fetchedChats.mentionTagSummaries { transaction.replaceMessageTagSummary(peerId: peerId, threadId: nil, tagMask: .unseenPersonalMessage, namespace: Namespaces.Message.Cloud, customTag: nil, count: summary.count, maxId: summary.range.maxId) } for (peerId, summary) in fetchedChats.reactionTagSummaries { transaction.replaceMessageTagSummary(peerId: peerId, threadId: nil, tagMask: .unseenReaction, namespace: Namespaces.Message.Cloud, customTag: nil, count: summary.count, maxId: summary.range.maxId) } for (groupId, summary) in fetchedChats.folderSummaries { transaction.resetPeerGroupSummary(groupId: groupId, namespace: Namespaces.Message.Cloud, summary: summary) } let savedMessageTags = transaction.getMessageTagSummaryCustomTags(peerId: accountPeerId, threadId: nil, tagMask: [], namespace: Namespaces.Message.Cloud) if !savedMessageTags.isEmpty { for tag in savedMessageTags { transaction.replaceMessageTagSummary(peerId: accountPeerId, threadId: nil, tagMask: [], namespace: Namespaces.Message.Cloud, customTag: tag, count: 0, maxId: 1) } transaction.invalidateMessageHistoryTagsSummary(peerId: accountPeerId, threadId: nil, namespace: Namespaces.Message.Cloud, tagMask: [], customTag: savedMessageTags[0]) } transaction.reindexUnreadCounters() if let currentState = transaction.getState() as? AuthorizedAccountState { switch state { case let .state(stateData): let (pts, qts, date, seq) = (stateData.pts, stateData.qts, stateData.date, stateData.seq) transaction.setState(currentState.changedState(AuthorizedAccountState.State(pts: pts, qts: qts, date: date, seq: seq))) } } }) |> ignoreValues } } }