diff --git a/submodules/PhoneNumberFormat/Sources/InteractivePhoneFormatter.swift b/submodules/PhoneNumberFormat/Sources/InteractivePhoneFormatter.swift index 24405329bb..ff36a6587e 100644 --- a/submodules/PhoneNumberFormat/Sources/InteractivePhoneFormatter.swift +++ b/submodules/PhoneNumberFormat/Sources/InteractivePhoneFormatter.swift @@ -10,6 +10,11 @@ public final class InteractivePhoneFormatter { public func updateText(_ text: String) -> (String?, String) { self.formatter.clear() let string = self.formatter.inputString(text) - return (self.formatter.regionPrefix, string ?? "") + + var regionPrefix = self.formatter.regionPrefix + if let string = string, string.hasPrefix("+383") { + regionPrefix = "+383" + } + return (regionPrefix, string ?? "") } } diff --git a/submodules/TelegramCore/Sources/AccountIntermediateState.swift b/submodules/TelegramCore/Sources/AccountIntermediateState.swift index d3434c8689..bf19ff8002 100644 --- a/submodules/TelegramCore/Sources/AccountIntermediateState.swift +++ b/submodules/TelegramCore/Sources/AccountIntermediateState.swift @@ -10,20 +10,24 @@ struct PeerChatInfo { var notificationSettings: PeerNotificationSettings } +struct AccountStateChannelState: Equatable { + var pts: Int32 +} + final class AccountInitialState { let state: AuthorizedAccountState.State let peerIds: Set - let chatStates: [PeerId: PeerChatState] + let channelStates: [PeerId: AccountStateChannelState] let peerChatInfos: [PeerId: PeerChatInfo] let peerIdsRequiringLocalChatState: Set let locallyGeneratedMessageTimestamps: [PeerId: [(MessageId.Namespace, Int32)]] let cloudReadStates: [PeerId: PeerReadState] let channelsToPollExplicitely: Set - init(state: AuthorizedAccountState.State, peerIds: Set, peerIdsRequiringLocalChatState: Set, chatStates: [PeerId: PeerChatState], peerChatInfos: [PeerId: PeerChatInfo], locallyGeneratedMessageTimestamps: [PeerId: [(MessageId.Namespace, Int32)]], cloudReadStates: [PeerId: PeerReadState], channelsToPollExplicitely: Set) { + init(state: AuthorizedAccountState.State, peerIds: Set, peerIdsRequiringLocalChatState: Set, channelStates: [PeerId: AccountStateChannelState], peerChatInfos: [PeerId: PeerChatInfo], locallyGeneratedMessageTimestamps: [PeerId: [(MessageId.Namespace, Int32)]], cloudReadStates: [PeerId: PeerReadState], channelsToPollExplicitely: Set) { self.state = state self.peerIds = peerIds - self.chatStates = chatStates + self.channelStates = channelStates self.peerIdsRequiringLocalChatState = peerIdsRequiringLocalChatState self.peerChatInfos = peerChatInfos self.locallyGeneratedMessageTimestamps = locallyGeneratedMessageTimestamps @@ -72,7 +76,9 @@ enum AccountStateMutationOperation { case ResetMessageTagSummary(PeerId, MessageId.Namespace, Int32, MessageHistoryTagNamespaceCountValidityRange) case ReadGroupFeedInbox(PeerGroupId, MessageIndex) case UpdateState(AuthorizedAccountState.State) - case UpdateChannelState(PeerId, ChannelState) + case UpdateChannelState(PeerId, Int32) + case UpdateChannelInvalidationPts(PeerId, Int32) + case UpdateChannelSynchronizedUntilMessage(PeerId, MessageId.Id) case UpdateNotificationSettings(AccountStateNotificationSettingsSubject, PeerNotificationSettings) case UpdateGlobalNotificationSettings(AccountStateGlobalNotificationSettingsSubject, MessageNotificationSettings) case MergeApiChats([Api.Chat]) @@ -110,7 +116,7 @@ struct AccountMutableState { var state: AuthorizedAccountState.State var peers: [PeerId: Peer] - var chatStates: [PeerId: PeerChatState] + var channelStates: [PeerId: AccountStateChannelState] var peerChatInfos: [PeerId: PeerChatInfo] var referencedMessageIds: Set var storedMessages: Set @@ -139,7 +145,7 @@ struct AccountMutableState { self.referencedMessageIds = initialReferencedMessageIds self.storedMessages = initialStoredMessages self.readInboxMaxIds = initialReadInboxMaxIds - self.chatStates = initialState.chatStates + self.channelStates = initialState.channelStates self.peerChatInfos = initialState.peerChatInfos self.storedMessagesByPeerIdAndTimestamp = storedMessagesByPeerIdAndTimestamp self.branchOperationIndex = 0 @@ -147,12 +153,12 @@ struct AccountMutableState { self.updatedOutgoingUniqueMessageIds = [:] } - init(initialState: AccountInitialState, operations: [AccountStateMutationOperation], state: AuthorizedAccountState.State, peers: [PeerId: Peer], chatStates: [PeerId: PeerChatState], peerChatInfos: [PeerId: PeerChatInfo], referencedMessageIds: Set, storedMessages: Set, readInboxMaxIds: [PeerId: MessageId], storedMessagesByPeerIdAndTimestamp: [PeerId: Set], namespacesWithHolesFromPreviousState: [PeerId: Set], updatedOutgoingUniqueMessageIds: [Int64: Int32], displayAlerts: [(text: String, isDropAuth: Bool)], branchOperationIndex: Int) { + init(initialState: AccountInitialState, operations: [AccountStateMutationOperation], state: AuthorizedAccountState.State, peers: [PeerId: Peer], channelStates: [PeerId: AccountStateChannelState], peerChatInfos: [PeerId: PeerChatInfo], referencedMessageIds: Set, storedMessages: Set, readInboxMaxIds: [PeerId: MessageId], storedMessagesByPeerIdAndTimestamp: [PeerId: Set], namespacesWithHolesFromPreviousState: [PeerId: Set], updatedOutgoingUniqueMessageIds: [Int64: Int32], displayAlerts: [(text: String, isDropAuth: Bool)], branchOperationIndex: Int) { self.initialState = initialState self.operations = operations self.state = state self.peers = peers - self.chatStates = chatStates + self.channelStates = channelStates self.referencedMessageIds = referencedMessageIds self.storedMessages = storedMessages self.peerChatInfos = peerChatInfos @@ -165,7 +171,7 @@ struct AccountMutableState { } func branch() -> AccountMutableState { - return AccountMutableState(initialState: self.initialState, operations: self.operations, state: self.state, peers: self.peers, chatStates: self.chatStates, peerChatInfos: self.peerChatInfos, referencedMessageIds: self.referencedMessageIds, storedMessages: self.storedMessages, readInboxMaxIds: self.readInboxMaxIds, storedMessagesByPeerIdAndTimestamp: self.storedMessagesByPeerIdAndTimestamp, namespacesWithHolesFromPreviousState: self.namespacesWithHolesFromPreviousState, updatedOutgoingUniqueMessageIds: self.updatedOutgoingUniqueMessageIds, displayAlerts: self.displayAlerts, branchOperationIndex: self.operations.count) + return AccountMutableState(initialState: self.initialState, operations: self.operations, state: self.state, peers: self.peers, channelStates: self.channelStates, peerChatInfos: self.peerChatInfos, referencedMessageIds: self.referencedMessageIds, storedMessages: self.storedMessages, readInboxMaxIds: self.readInboxMaxIds, storedMessagesByPeerIdAndTimestamp: self.storedMessagesByPeerIdAndTimestamp, namespacesWithHolesFromPreviousState: self.namespacesWithHolesFromPreviousState, updatedOutgoingUniqueMessageIds: self.updatedOutgoingUniqueMessageIds, displayAlerts: self.displayAlerts, branchOperationIndex: self.operations.count) } mutating func merge(_ other: AccountMutableState) { @@ -269,8 +275,16 @@ struct AccountMutableState { self.addOperation(.UpdateState(state)) } - mutating func updateChannelState(_ peerId: PeerId, state: ChannelState) { - self.addOperation(.UpdateChannelState(peerId, state)) + mutating func updateChannelState(_ peerId: PeerId, pts: Int32) { + self.addOperation(.UpdateChannelState(peerId, pts)) + } + + mutating func updateChannelInvalidationPts(_ peerId: PeerId, invalidationPts: Int32) { + self.addOperation(.UpdateChannelInvalidationPts(peerId, invalidationPts)) + } + + mutating func updateChannelSynchronizedUntilMessage(_ peerId: PeerId, id: MessageId.Id) { + self.addOperation(.UpdateChannelSynchronizedUntilMessage(peerId, id)) } mutating func updateNotificationSettings(_ subject: AccountStateNotificationSettingsSubject, notificationSettings: PeerNotificationSettings) { @@ -464,8 +478,12 @@ struct AccountMutableState { } case let .UpdateState(state): self.state = state - case let .UpdateChannelState(peerId, channelState): - self.chatStates[peerId] = channelState + case let .UpdateChannelState(peerId, pts): + self.channelStates[peerId] = AccountStateChannelState(pts: pts) + case .UpdateChannelInvalidationPts: + break + case .UpdateChannelSynchronizedUntilMessage: + break case let .UpdateNotificationSettings(subject, notificationSettings): if case let .peer(peerId) = subject { if var currentInfo = self.peerChatInfos[peerId] { diff --git a/submodules/TelegramCore/Sources/AccountStateManagementUtils.swift b/submodules/TelegramCore/Sources/AccountStateManagementUtils.swift index f25fa4e3c2..56b3502092 100644 --- a/submodules/TelegramCore/Sources/AccountStateManagementUtils.swift +++ b/submodules/TelegramCore/Sources/AccountStateManagementUtils.swift @@ -356,7 +356,7 @@ private func locallyGeneratedMessageTimestampsFromDifference(_ difference: Api.u private func initialStateWithPeerIds(_ transaction: Transaction, peerIds: Set, activeChannelIds: Set, associatedMessageIds: Set, peerIdsRequiringLocalChatState: Set, locallyGeneratedMessageTimestamps: [PeerId: [(MessageId.Namespace, Int32)]]) -> AccountMutableState { var peers: [PeerId: Peer] = [:] - var chatStates: [PeerId: PeerChatState] = [:] + var channelStates: [PeerId: AccountStateChannelState] = [:] var channelsToPollExplicitely = Set() @@ -367,11 +367,11 @@ private func initialStateWithPeerIds(_ transaction: Transaction, peerIds: Set Signal { @@ -772,7 +772,7 @@ private func finalStateWithUpdatesAndServerTime(postbox: Postbox, network: Netwo case let .updateChannelTooLong(_, channelId, channelPts): let peerId = PeerId(namespace: Namespaces.Peer.CloudChannel, id: channelId) if !channelsToPoll.contains(peerId) { - if let channelPts = channelPts, let channelState = state.chatStates[peerId] as? ChannelState, channelState.pts >= channelPts { + if let channelPts = channelPts, let channelState = state.channelStates[peerId], channelState.pts >= channelPts { Logger.shared.log("State", "channel \(peerId) (\((updatedState.peers[peerId] as? TelegramChannel)?.title ?? "nil")) skip updateChannelTooLong by pts") } else { channelsToPoll.insert(peerId) @@ -780,12 +780,12 @@ private func finalStateWithUpdatesAndServerTime(postbox: Postbox, network: Netwo } case let .updateDeleteChannelMessages(channelId, messages, pts: pts, ptsCount): let peerId = PeerId(namespace: Namespaces.Peer.CloudChannel, id: channelId) - if let previousState = updatedState.chatStates[peerId] as? ChannelState { + if let previousState = updatedState.channelStates[peerId] { if previousState.pts >= pts { Logger.shared.log("State", "channel \(peerId) (\((updatedState.peers[peerId] as? TelegramChannel)?.title ?? "nil")) skip old delete update") } else if previousState.pts + ptsCount == pts { updatedState.deleteMessages(messages.map({ MessageId(peerId: peerId, namespace: Namespaces.Message.Cloud, id: $0) })) - updatedState.updateChannelState(peerId, state: previousState.withUpdatedPts(pts)) + updatedState.updateChannelState(peerId, pts: pts) } else { if !channelsToPoll.contains(peerId) { Logger.shared.log("State", "channel \(peerId) (\((updatedState.peers[peerId] as? TelegramChannel)?.title ?? "nil")) delete pts hole") @@ -802,7 +802,7 @@ private func finalStateWithUpdatesAndServerTime(postbox: Postbox, network: Netwo case let .updateEditChannelMessage(apiMessage, pts, ptsCount): if let message = StoreMessage(apiMessage: apiMessage), case let .Id(messageId) = message.id { let peerId = messageId.peerId - if let previousState = updatedState.chatStates[peerId] as? ChannelState { + if let previousState = updatedState.channelStates[peerId] { if previousState.pts >= pts { Logger.shared.log("State", "channel \(peerId) (\((updatedState.peers[peerId] as? TelegramChannel)?.title ?? "nil")) skip old edit update") } else if previousState.pts + ptsCount == pts { @@ -814,7 +814,7 @@ private func finalStateWithUpdatesAndServerTime(postbox: Postbox, network: Netwo var attributes = message.attributes attributes.append(ChannelMessageStateVersionAttribute(pts: pts)) updatedState.editMessage(messageId, message: message.withUpdatedAttributes(attributes)) - updatedState.updateChannelState(peerId, state: previousState.withUpdatedPts(pts)) + updatedState.updateChannelState(peerId, pts: pts) } else { if !channelsToPoll.contains(peerId) { Logger.shared.log("State", "channel \(peerId) (\((updatedState.peers[peerId] as? TelegramChannel)?.title ?? "nil")) edit message pts hole") @@ -833,7 +833,7 @@ private func finalStateWithUpdatesAndServerTime(postbox: Postbox, network: Netwo } case let .updateChannelWebPage(channelId, apiWebpage, pts, ptsCount): let peerId = PeerId(namespace: Namespaces.Peer.CloudChannel, id: channelId) - if let previousState = updatedState.chatStates[peerId] as? ChannelState { + if let previousState = updatedState.channelStates[peerId] { if previousState.pts >= pts { } else if previousState.pts + ptsCount == pts { switch apiWebpage { @@ -845,7 +845,7 @@ private func finalStateWithUpdatesAndServerTime(postbox: Postbox, network: Netwo } } - updatedState.updateChannelState(peerId, state: previousState.withUpdatedPts(pts)) + updatedState.updateChannelState(peerId, pts: pts) } else { if !channelsToPoll.contains(peerId) { Logger.shared.log("State", "channel \(peerId) (\((updatedState.peers[peerId] as? TelegramChannel)?.title ?? "nil")) updateWebPage pts hole") @@ -880,7 +880,7 @@ private func finalStateWithUpdatesAndServerTime(postbox: Postbox, network: Netwo } case let .updateNewChannelMessage(apiMessage, pts, ptsCount): if let message = StoreMessage(apiMessage: apiMessage) { - if let previousState = updatedState.chatStates[message.id.peerId] as? ChannelState { + if let previousState = updatedState.channelStates[message.id.peerId] { if previousState.pts >= pts { let messageText: String if Logger.shared.redactSensitiveData { @@ -898,7 +898,10 @@ private func finalStateWithUpdatesAndServerTime(postbox: Postbox, network: Netwo var attributes = message.attributes attributes.append(ChannelMessageStateVersionAttribute(pts: pts)) updatedState.addMessages([message.withUpdatedAttributes(attributes)], location: .UpperHistoryBlock) - updatedState.updateChannelState(message.id.peerId, state: previousState.withUpdatedPts(pts)) + updatedState.updateChannelState(message.id.peerId, pts: pts) + if case let .Id(id) = message.id { + updatedState.updateChannelSynchronizedUntilMessage(id.peerId, id: id.id) + } } else { if !channelsToPoll.contains(message.id.peerId) { Logger.shared.log("State", "channel \(message.id.peerId) (\((updatedState.peers[message.id.peerId] as? TelegramChannel)?.title ?? "nil")) message pts hole") @@ -1587,9 +1590,9 @@ private func resolveMissingPeerChatInfos(network: Network, state: AccountMutable func keepPollingChannel(postbox: Postbox, network: Network, peerId: PeerId, stateManager: AccountStateManager) -> Signal { return postbox.transaction { transaction -> Signal in if let accountState = (transaction.getState() as? AuthorizedAccountState)?.state, let peer = transaction.getPeer(peerId) { - var chatStates: [PeerId: PeerChatState] = [:] + var channelStates: [PeerId: AccountStateChannelState] = [:] if let channelState = transaction.getPeerChatState(peerId) as? ChannelState { - chatStates[peerId] = channelState + channelStates[peerId] = AccountStateChannelState(pts: channelState.pts) } let initialPeers: [PeerId: Peer] = [peerId: peer] var peerChatInfos: [PeerId: PeerChatInfo] = [:] @@ -1606,7 +1609,7 @@ func keepPollingChannel(postbox: Postbox, network: Network, peerId: PeerId, stat peerChatInfos[peerId] = PeerChatInfo(notificationSettings: notificationSettings) } } - let initialState = AccountMutableState(initialState: AccountInitialState(state: accountState, peerIds: Set(), peerIdsRequiringLocalChatState: Set(), chatStates: chatStates, peerChatInfos: peerChatInfos, locallyGeneratedMessageTimestamps: [:], cloudReadStates: [:], channelsToPollExplicitely: Set()), initialPeers: initialPeers, initialReferencedMessageIds: Set(), initialStoredMessages: Set(), initialReadInboxMaxIds: [:], storedMessagesByPeerIdAndTimestamp: [:]) + let initialState = AccountMutableState(initialState: AccountInitialState(state: accountState, peerIds: Set(), peerIdsRequiringLocalChatState: Set(), channelStates: channelStates, peerChatInfos: peerChatInfos, locallyGeneratedMessageTimestamps: [:], cloudReadStates: [:], channelsToPollExplicitely: Set()), initialPeers: initialPeers, initialReferencedMessageIds: Set(), initialStoredMessages: Set(), initialReadInboxMaxIds: [:], storedMessagesByPeerIdAndTimestamp: [:]) return pollChannel(network: network, peer: peer, state: initialState) |> mapToSignal { (finalState, _, timeout) -> Signal in return resolveAssociatedMessages(network: network, state: finalState) @@ -1657,7 +1660,9 @@ private func resetChannels(network: Network, peers: [Peer], state: AccountMutabl var storeMessages: [StoreMessage] = [] var readStates: [PeerId: [MessageId.Namespace: PeerReadState]] = [:] var mentionTagSummaries: [PeerId: MessageHistoryTagNamespaceSummary] = [:] - var channelStates: [PeerId: ChannelState] = [:] + var channelStates: [PeerId: AccountStateChannelState] = [:] + var invalidateChannelStates: [PeerId: Int32] = [:] + var channelSynchronizedUntilMessage: [PeerId: MessageId.Id] = [:] var notificationSettings: [PeerId: PeerNotificationSettings] = [:] if let result = result { @@ -1714,7 +1719,8 @@ private func resetChannels(network: Network, peers: [Peer], state: AccountMutabl } if let apiChannelPts = apiChannelPts { - channelStates[peerId] = ChannelState(pts: apiChannelPts, invalidatedPts: apiChannelPts, synchronizedUntilMessageId: nil) + channelStates[peerId] = AccountStateChannelState(pts: apiChannelPts) + invalidateChannelStates[peerId] = apiChannelPts } notificationSettings[peerId] = TelegramPeerNotificationSettings(apiSettings: apiNotificationSettings) @@ -1744,6 +1750,7 @@ private func resetChannels(network: Network, peers: [Peer], state: AccountMutabl for message in storeMessages { if case let .Id(id) = message.id, id.namespace == Namespaces.Message.Cloud { updatedState.setNeedsHoleFromPreviousState(peerId: id.peerId, namespace: id.namespace) + channelSynchronizedUntilMessage[id.peerId] = id.id } } @@ -1766,7 +1773,13 @@ private func resetChannels(network: Network, peers: [Peer], state: AccountMutabl } for (peerId, channelState) in channelStates { - updatedState.updateChannelState(peerId, state: channelState) + updatedState.updateChannelState(peerId, pts: channelState.pts) + } + for (peerId, pts) in invalidateChannelStates { + updatedState.updateChannelInvalidationPts(peerId, invalidationPts: pts) + } + for (peerId, id) in channelSynchronizedUntilMessage { + updatedState.updateChannelSynchronizedUntilMessage(peerId, id: id) } for (peerId, settings) in notificationSettings { @@ -1792,7 +1805,7 @@ private func pollChannel(network: Network, peer: Peer, state: AccountMutableStat #endif let pollPts: Int32 - if let channelState = state.chatStates[peer.id] as? ChannelState { + if let channelState = state.channelStates[peer.id] { pollPts = channelState.pts } else { pollPts = 1 @@ -1814,13 +1827,13 @@ private func pollChannel(network: Network, peer: Peer, state: AccountMutableStat switch difference { case let .channelDifference(_, pts, timeout, newMessages, otherUpdates, chats, users): apiTimeout = timeout - let channelState: ChannelState - if let previousState = updatedState.chatStates[peer.id] as? ChannelState { - channelState = previousState.withUpdatedPts(pts) + let channelPts: Int32 + if let _ = updatedState.channelStates[peer.id] { + channelPts = pts } else { - channelState = ChannelState(pts: pts, invalidatedPts: nil, synchronizedUntilMessageId: nil) + channelPts = pts } - updatedState.updateChannelState(peer.id, state: channelState) + updatedState.updateChannelState(peer.id, pts: channelPts) updatedState.mergeChats(chats) updatedState.mergeUsers(users) @@ -1833,6 +1846,9 @@ private func pollChannel(network: Network, peer: Peer, state: AccountMutableStat } } updatedState.addMessages([message], location: .UpperHistoryBlock) + if case let .Id(id) = message.id { + updatedState.updateChannelSynchronizedUntilMessage(id.peerId, id: id.id) + } } } for update in otherUpdates { @@ -1895,13 +1911,13 @@ private func pollChannel(network: Network, peer: Peer, state: AccountMutableStat case let .channelDifferenceEmpty(_, pts, timeout): apiTimeout = timeout - let channelState: ChannelState - if let previousState = updatedState.chatStates[peer.id] as? ChannelState { - channelState = previousState.withUpdatedPts(pts) + let channelPts: Int32 + if let previousState = updatedState.channelStates[peer.id] { + channelPts = pts } else { - channelState = ChannelState(pts: pts, invalidatedPts: nil, synchronizedUntilMessageId: nil) + channelPts = pts } - updatedState.updateChannelState(peer.id, state: channelState) + updatedState.updateChannelState(peer.id, pts: channelPts) case let .channelDifferenceTooLong(_, timeout, dialog, messages, chats, users): apiTimeout = timeout @@ -1917,8 +1933,8 @@ private func pollChannel(network: Network, peer: Peer, state: AccountMutableStat } if let (peer, pts, topMessage, readInboxMaxId, readOutboxMaxId, unreadCount, unreadMentionsCount) = parameters { - let channelState = ChannelState(pts: pts, invalidatedPts: pts, synchronizedUntilMessageId: nil) - updatedState.updateChannelState(peer.peerId, state: channelState) + updatedState.updateChannelState(peer.peerId, pts: pts) + updatedState.updateChannelInvalidationPts(peer.peerId, invalidationPts: pts) updatedState.mergeChats(chats) updatedState.mergeUsers(users) @@ -1936,6 +1952,7 @@ private func pollChannel(network: Network, peer: Peer, state: AccountMutableStat let location: AddMessagesLocation if case let .Id(id) = message.id, id.id == topMessage { location = .UpperHistoryBlock + updatedState.updateChannelSynchronizedUntilMessage(id.peerId, id: id.id) } else { location = .Random } @@ -2007,9 +2024,9 @@ private func verifyTransaction(_ transaction: Transaction, finalState: AccountMu for peerId in channelsWithUpdatedStates { let currentState = transaction.getPeerChatState(peerId) var previousStateMatches = false - let previousState = finalState.initialState.chatStates[peerId] as? ChannelState - if let currentState = currentState, let previousState = previousState { - if currentState.equals(previousState) { + let previousState = finalState.initialState.channelStates[peerId] + if let currentState = currentState as? ChannelState, let previousState = previousState { + if currentState.pts == previousState.pts { previousStateMatches = true } } else if currentState == nil && previousState == nil { @@ -2048,7 +2065,9 @@ private func optimizedOperations(_ operations: [AccountStateMutationOperation]) var result: [AccountStateMutationOperation] = [] var updatedState: AuthorizedAccountState.State? - var updatedChannelStates: [PeerId: ChannelState] = [:] + var updatedChannelStates: [PeerId: AccountStateChannelState] = [:] + var invalidateChannelPts: [PeerId: Int32] = [:] + var updateChannelSynchronizedUntilMessage: [PeerId: MessageId.Id] = [:] var currentAddMessages: OptimizeAddMessagesState? var currentAddScheduledMessages: OptimizeAddMessagesState? @@ -2065,8 +2084,12 @@ private func optimizedOperations(_ operations: [AccountStateMutationOperation]) result.append(operation) case let .UpdateState(state): updatedState = state - case let .UpdateChannelState(peerId, state): - updatedChannelStates[peerId] = state + case let .UpdateChannelState(peerId, pts): + updatedChannelStates[peerId] = AccountStateChannelState(pts: pts) + case let .UpdateChannelInvalidationPts(peerId, pts): + invalidateChannelPts[peerId] = pts + case let .UpdateChannelSynchronizedUntilMessage(peerId, id): + updateChannelSynchronizedUntilMessage[peerId] = id case let .AddMessages(messages, location): if let currentAddMessages = currentAddMessages, currentAddMessages.location == location { currentAddMessages.messages.append(contentsOf: messages) @@ -2097,7 +2120,15 @@ private func optimizedOperations(_ operations: [AccountStateMutationOperation]) } for (peerId, state) in updatedChannelStates { - result.append(.UpdateChannelState(peerId, state)) + result.append(.UpdateChannelState(peerId, state.pts)) + } + + for (peerId, pts) in invalidateChannelPts { + result.append(.UpdateChannelInvalidationPts(peerId, pts)) + } + + for (peerId, id) in updateChannelSynchronizedUntilMessage { + result.append(.UpdateChannelSynchronizedUntilMessage(peerId, id)) } return result @@ -2144,8 +2175,18 @@ func replayFinalState(accountManager: AccountManager, postbox: Postbox, accountP for (peerId, namespaces) in finalState.state.namespacesWithHolesFromPreviousState { for namespace in namespaces { - if let id = transaction.getTopPeerMessageId(peerId: peerId, namespace: namespace) { - holesFromPreviousStateMessageIds.append(MessageId(peerId: id.peerId, namespace: id.namespace, id: id.id + 1)) + var topId: Int32? + if namespace == Namespaces.Message.Cloud, let channelState = transaction.getPeerChatState(peerId) as? ChannelState { + if let synchronizedUntilMessageId = channelState.synchronizedUntilMessageId { + topId = synchronizedUntilMessageId + 1 + } + } + if topId == nil { + topId = transaction.getTopPeerMessageId(peerId: peerId, namespace: namespace)?.id + } + + if let id = topId { + holesFromPreviousStateMessageIds.append(MessageId(peerId: peerId, namespace: namespace, id: id + 1)) } else { holesFromPreviousStateMessageIds.append(MessageId(peerId: peerId, namespace: namespace, id: 1)) } @@ -2494,9 +2535,21 @@ func replayFinalState(accountManager: AccountManager, postbox: Postbox, accountP let currentState = transaction.getState() as! AuthorizedAccountState transaction.setState(currentState.changedState(state)) Logger.shared.log("State", "apply state \(state)") - case let .UpdateChannelState(peerId, channelState): - transaction.setPeerChatState(peerId, state: channelState) - Logger.shared.log("State", "apply channel state \(peerId): \(channelState)") + case let .UpdateChannelState(peerId, pts): + var state = (transaction.getPeerChatState(peerId) as? ChannelState) ?? ChannelState(pts: pts, invalidatedPts: nil, synchronizedUntilMessageId: nil) + state = state.withUpdatedPts(pts) + transaction.setPeerChatState(peerId, state: state) + Logger.shared.log("State", "apply channel state \(peerId): \(state)") + case let .UpdateChannelInvalidationPts(peerId, pts): + var state = (transaction.getPeerChatState(peerId) as? ChannelState) ?? ChannelState(pts: 0, invalidatedPts: pts, synchronizedUntilMessageId: nil) + state = state.withUpdatedInvalidatedPts(pts) + transaction.setPeerChatState(peerId, state: state) + Logger.shared.log("State", "apply channel invalidation pts \(peerId): \(state)") + case let .UpdateChannelSynchronizedUntilMessage(peerId, id): + var state = (transaction.getPeerChatState(peerId) as? ChannelState) ?? ChannelState(pts: 0, invalidatedPts: nil, synchronizedUntilMessageId: id) + state = state.withUpdatedSynchronizedUntilMessageId(id) + transaction.setPeerChatState(peerId, state: state) + Logger.shared.log("State", "apply channel synchronized until message \(peerId): \(state)") case let .UpdateNotificationSettings(subject, notificationSettings): switch subject { case let .peer(peerId): diff --git a/submodules/TelegramCore/Sources/ChatListFiltering.swift b/submodules/TelegramCore/Sources/ChatListFiltering.swift index 0cc46fd73e..094ad569fc 100644 --- a/submodules/TelegramCore/Sources/ChatListFiltering.swift +++ b/submodules/TelegramCore/Sources/ChatListFiltering.swift @@ -564,7 +564,7 @@ private func loadAndStorePeerChatInfos(accountPeerId: PeerId, postbox: Postbox, var peers: [Peer] = [] var peerPresences: [PeerId: PeerPresence] = [:] var notificationSettings: [PeerId: PeerNotificationSettings] = [:] - var channelStates: [PeerId: ChannelState] = [:] + var channelStates: [PeerId: Int32] = [:] switch result { case let .peerDialogs(dialogs, messages, chats, users, _): @@ -644,9 +644,10 @@ private func loadAndStorePeerChatInfos(accountPeerId: PeerId, postbox: Postbox, transaction.replaceMessageTagSummary(peerId: peerId, tagMask: .unseenPersonalMessage, namespace: Namespaces.Message.Cloud, count: unreadMentionsCount, maxId: topMessage) if let pts = pts { - let channelState = ChannelState(pts: pts, invalidatedPts: pts, synchronizedUntilMessageId: nil) - transaction.setPeerChatState(peerId, state: channelState) - channelStates[peer.peerId] = channelState + if transaction.getPeerChatState(peerId) == nil { + transaction.setPeerChatState(peerId, state: ChannelState(pts: pts, invalidatedPts: nil, synchronizedUntilMessageId: nil)) + } + channelStates[peer.peerId] = pts } case .dialogFolder: assertionFailure() @@ -659,9 +660,9 @@ private func loadAndStorePeerChatInfos(accountPeerId: PeerId, postbox: Postbox, if let storeMessage = StoreMessage(apiMessage: message) { var updatedStoreMessage = storeMessage if case let .Id(id) = storeMessage.id { - if let channelState = channelStates[id.peerId] { + if let channelPts = channelStates[id.peerId] { var updatedAttributes = storeMessage.attributes - updatedAttributes.append(ChannelMessageStateVersionAttribute(pts: channelState.pts)) + updatedAttributes.append(ChannelMessageStateVersionAttribute(pts: channelPts)) updatedStoreMessage = updatedStoreMessage.withUpdatedAttributes(updatedAttributes) } } diff --git a/submodules/TelegramCore/Sources/FetchChatList.swift b/submodules/TelegramCore/Sources/FetchChatList.swift index 3d7f923cd7..9da9b60c2c 100644 --- a/submodules/TelegramCore/Sources/FetchChatList.swift +++ b/submodules/TelegramCore/Sources/FetchChatList.swift @@ -19,7 +19,7 @@ struct ParsedDialogs { let notificationSettings: [PeerId: PeerNotificationSettings] let readStates: [PeerId: [MessageId.Namespace: PeerReadState]] let mentionTagSummaries: [PeerId: MessageHistoryTagNamespaceSummary] - let chatStates: [PeerId: PeerChatState] + let channelStates: [PeerId: Int32] let topMessageIds: [PeerId: MessageId] let storeMessages: [StoreMessage] @@ -50,7 +50,7 @@ private func parseDialogs(apiDialogs: [Api.Dialog], apiMessages: [Api.Message], var notificationSettings: [PeerId: PeerNotificationSettings] = [:] var readStates: [PeerId: [MessageId.Namespace: PeerReadState]] = [:] var mentionTagSummaries: [PeerId: MessageHistoryTagNamespaceSummary] = [:] - var chatStates: [PeerId: PeerChatState] = [:] + var channelStates: [PeerId: Int32] = [:] var topMessageIds: [PeerId: MessageId] = [:] var storeMessages: [StoreMessage] = [] @@ -131,7 +131,7 @@ private func parseDialogs(apiDialogs: [Api.Dialog], apiMessages: [Api.Message], } if let apiChannelPts = apiChannelPts { - chatStates[peerId] = ChannelState(pts: apiChannelPts, invalidatedPts: nil, synchronizedUntilMessageId: nil) + channelStates[peerId] = apiChannelPts } notificationSettings[peerId] = TelegramPeerNotificationSettings(apiSettings: apiNotificationSettings) @@ -149,9 +149,9 @@ private func parseDialogs(apiDialogs: [Api.Dialog], apiMessages: [Api.Message], if let storeMessage = StoreMessage(apiMessage: message) { var updatedStoreMessage = storeMessage if case let .Id(id) = storeMessage.id { - if let channelState = chatStates[id.peerId] as? ChannelState { + if let channelPts = channelStates[id.peerId] { var updatedAttributes = storeMessage.attributes - updatedAttributes.append(ChannelMessageStateVersionAttribute(pts: channelState.pts)) + updatedAttributes.append(ChannelMessageStateVersionAttribute(pts: channelPts)) updatedStoreMessage = updatedStoreMessage.withUpdatedAttributes(updatedAttributes) } @@ -174,7 +174,7 @@ private func parseDialogs(apiDialogs: [Api.Dialog], apiMessages: [Api.Message], notificationSettings: notificationSettings, readStates: readStates, mentionTagSummaries: mentionTagSummaries, - chatStates: chatStates, + channelStates: channelStates, topMessageIds: topMessageIds, storeMessages: storeMessages, @@ -190,7 +190,7 @@ struct FetchedChatList { let notificationSettings: [PeerId: PeerNotificationSettings] let readStates: [PeerId: [MessageId.Namespace: PeerReadState]] let mentionTagSummaries: [PeerId: MessageHistoryTagNamespaceSummary] - let chatStates: [PeerId: PeerChatState] + let channelStates: [PeerId: Int32] let storeMessages: [StoreMessage] let topMessageIds: [PeerId: MessageId] @@ -295,7 +295,7 @@ func fetchChatList(postbox: Postbox, network: Network, location: FetchChatListLo var notificationSettings: [PeerId: PeerNotificationSettings] = [:] var readStates: [PeerId: [MessageId.Namespace: PeerReadState]] = [:] var mentionTagSummaries: [PeerId: MessageHistoryTagNamespaceSummary] = [:] - var chatStates: [PeerId: PeerChatState] = [:] + var channelStates: [PeerId: Int32] = [:] var storeMessages: [StoreMessage] = [] var topMessageIds: [PeerId: MessageId] = [:] @@ -304,7 +304,7 @@ func fetchChatList(postbox: Postbox, network: Network, location: FetchChatListLo notificationSettings.merge(parsedRemoteChats.notificationSettings, uniquingKeysWith: { _, updated in updated }) readStates.merge(parsedRemoteChats.readStates, uniquingKeysWith: { _, updated in updated }) mentionTagSummaries.merge(parsedRemoteChats.mentionTagSummaries, uniquingKeysWith: { _, updated in updated }) - chatStates.merge(parsedRemoteChats.chatStates, uniquingKeysWith: { _, updated in updated }) + channelStates.merge(parsedRemoteChats.channelStates, uniquingKeysWith: { _, updated in updated }) storeMessages.append(contentsOf: parsedRemoteChats.storeMessages) topMessageIds.merge(parsedRemoteChats.topMessageIds, uniquingKeysWith: { _, updated in updated }) @@ -314,7 +314,7 @@ func fetchChatList(postbox: Postbox, network: Network, location: FetchChatListLo notificationSettings.merge(parsedPinnedChats.notificationSettings, uniquingKeysWith: { _, updated in updated }) readStates.merge(parsedPinnedChats.readStates, uniquingKeysWith: { _, updated in updated }) mentionTagSummaries.merge(parsedPinnedChats.mentionTagSummaries, uniquingKeysWith: { _, updated in updated }) - chatStates.merge(parsedPinnedChats.chatStates, uniquingKeysWith: { _, updated in updated }) + channelStates.merge(parsedPinnedChats.channelStates, uniquingKeysWith: { _, updated in updated }) storeMessages.append(contentsOf: parsedPinnedChats.storeMessages) topMessageIds.merge(parsedPinnedChats.topMessageIds, uniquingKeysWith: { _, updated in updated }) } @@ -336,7 +336,7 @@ func fetchChatList(postbox: Postbox, network: Network, location: FetchChatListLo notificationSettings.merge(folderChats.notificationSettings, uniquingKeysWith: { _, updated in updated }) readStates.merge(folderChats.readStates, uniquingKeysWith: { _, updated in updated }) mentionTagSummaries.merge(folderChats.mentionTagSummaries, uniquingKeysWith: { _, updated in updated }) - chatStates.merge(folderChats.chatStates, uniquingKeysWith: { _, updated in updated }) + channelStates.merge(folderChats.channelStates, uniquingKeysWith: { _, updated in updated }) storeMessages.append(contentsOf: folderChats.storeMessages) } @@ -369,7 +369,7 @@ func fetchChatList(postbox: Postbox, network: Network, location: FetchChatListLo notificationSettings: notificationSettings, readStates: readStates, mentionTagSummaries: mentionTagSummaries, - chatStates: chatStates, + channelStates: channelStates, storeMessages: storeMessages, topMessageIds: topMessageIds, diff --git a/submodules/TelegramCore/Sources/Holes.swift b/submodules/TelegramCore/Sources/Holes.swift index bb699b1d0b..8a358707a7 100644 --- a/submodules/TelegramCore/Sources/Holes.swift +++ b/submodules/TelegramCore/Sources/Holes.swift @@ -479,15 +479,11 @@ func fetchChatListHole(postbox: Postbox, network: Network, accountPeerId: PeerId } } - for (peerId, chatState) in fetchedChats.chatStates { - if let chatState = chatState as? ChannelState { - if let current = transaction.getPeerChatState(peerId) as? ChannelState { - transaction.setPeerChatState(peerId, state: current.withUpdatedPts(chatState.pts)) - } else { - transaction.setPeerChatState(peerId, state: chatState) - } + 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: chatState) + transaction.setPeerChatState(peerId, state: ChannelState(pts: pts, invalidatedPts: nil, synchronizedUntilMessageId: nil)) } } diff --git a/submodules/TelegramCore/Sources/ManagedSynchronizePeerReadStates.swift b/submodules/TelegramCore/Sources/ManagedSynchronizePeerReadStates.swift index ba0b8de09e..babafd68c6 100644 --- a/submodules/TelegramCore/Sources/ManagedSynchronizePeerReadStates.swift +++ b/submodules/TelegramCore/Sources/ManagedSynchronizePeerReadStates.swift @@ -2,70 +2,134 @@ import Foundation import Postbox import SwiftSignalKit -private final class ManagedSynchronizePeerReadStatesState { - private var synchronizeDisposables: [PeerId: (PeerReadStateSynchronizationOperation, Disposable)] = [:] - - func clearDisposables() -> [Disposable] { - let disposables = Array(self.synchronizeDisposables.values.map({ $0.1 })) - self.synchronizeDisposables.removeAll() - return disposables +private final class SynchronizePeerReadStatesContextImpl { + private final class Operation { + let operation: PeerReadStateSynchronizationOperation + let disposable: Disposable + + init( + operation: PeerReadStateSynchronizationOperation, + disposable: Disposable + ) { + self.operation = operation + self.disposable = disposable + } + + deinit { + self.disposable.dispose() + } } - func update(operations: [PeerId: PeerReadStateSynchronizationOperation]) -> (removed: [Disposable], added: [(PeerId, PeerReadStateSynchronizationOperation, MetaDisposable)]) { - var removed: [Disposable] = [] - var added: [(PeerId, PeerReadStateSynchronizationOperation, MetaDisposable)] = [] + private let queue: Queue + private let network: Network + private let postbox: Postbox + private let stateManager: AccountStateManager + + private var disposable: Disposable? + + private var currentState: [PeerId : PeerReadStateSynchronizationOperation] = [:] + private var activeOperations: [PeerId: Operation] = [:] + private var pendingOperations: [PeerId: PeerReadStateSynchronizationOperation] = [:] + + init(queue: Queue, network: Network, postbox: Postbox, stateManager: AccountStateManager) { + self.queue = queue + self.network = network + self.postbox = postbox + self.stateManager = stateManager - for (peerId, (operation, disposable)) in self.synchronizeDisposables { - if operations[peerId] != operation { - removed.append(disposable) - self.synchronizeDisposables.removeValue(forKey: peerId) + self.disposable = (postbox.synchronizePeerReadStatesView() + |> deliverOn(self.queue)).start(next: { [weak self] view in + guard let strongSelf = self else { + return + } + strongSelf.currentState = view.operations + strongSelf.update() + }) + } + + deinit { + self.disposable?.dispose() + } + + func dispose() { + } + + private func update() { + let peerIds = Set(self.currentState.keys).union(Set(self.pendingOperations.keys)) + + for peerId in peerIds { + var maybeOperation: PeerReadStateSynchronizationOperation? + if let operation = self.currentState[peerId] { + maybeOperation = operation + } else if let operation = self.pendingOperations[peerId] { + maybeOperation = operation + self.pendingOperations.removeValue(forKey: peerId) + } + + if let operation = maybeOperation { + if let current = self.activeOperations[peerId] { + if current.operation != operation { + self.pendingOperations[peerId] = operation + } + } else { + let operationDisposable = MetaDisposable() + let activeOperation = Operation( + operation: operation, + disposable: operationDisposable + ) + self.activeOperations[peerId] = activeOperation + let signal: Signal + switch operation { + case .Validate: + signal = synchronizePeerReadState(network: self.network, postbox: self.postbox, stateManager: self.stateManager, peerId: peerId, push: false, validate: true) + |> ignoreValues + case let .Push(_, thenSync): + signal = synchronizePeerReadState(network: self.network, postbox: self.postbox, stateManager: stateManager, peerId: peerId, push: true, validate: thenSync) + |> ignoreValues + } + operationDisposable.set((signal + |> deliverOn(self.queue)).start(completed: { [weak self, weak activeOperation] in + guard let strongSelf = self else { + return + } + if let activeOperation = activeOperation { + if let current = strongSelf.activeOperations[peerId], current === activeOperation { + strongSelf.activeOperations.removeValue(forKey: peerId) + strongSelf.update() + } + } + })) + } } } - - for (peerId, operation) in operations { - if self.synchronizeDisposables[peerId] == nil { - let disposable = MetaDisposable() - self.synchronizeDisposables[peerId] = (operation, disposable) - added.append((peerId, operation, disposable)) - } + } +} + +private final class SynchronizePeerReadStatesStatesContext { + private let queue: Queue + private let impl: QueueLocalObject + + init(network: Network, postbox: Postbox, stateManager: AccountStateManager) { + self.queue = Queue() + let queue = self.queue + self.impl = QueueLocalObject(queue: queue, generate: { + return SynchronizePeerReadStatesContextImpl(queue: queue, network: network, postbox: postbox, stateManager: stateManager) + }) + } + + func dispose() { + self.impl.with { impl in + impl.dispose() } - - return (removed, added) } } func managedSynchronizePeerReadStates(network: Network, postbox: Postbox, stateManager: AccountStateManager) -> Signal { return Signal { _ in - let state = Atomic(value: ManagedSynchronizePeerReadStatesState()) - - let disposable = postbox.synchronizePeerReadStatesView().start(next: { view in - let (removed, added) = state.with { state -> (removed: [Disposable], added: [(PeerId, PeerReadStateSynchronizationOperation, MetaDisposable)]) in - return state.update(operations: view.operations) - } - - for disposable in removed { - disposable.dispose() - } - - for (peerId, operation, disposable) in added { - let synchronizeOperation: Signal - switch operation { - case .Validate: - synchronizeOperation = synchronizePeerReadState(network: network, postbox: postbox, stateManager: stateManager, peerId: peerId, push: false, validate: true) - case let .Push(_, thenSync): - synchronizeOperation = synchronizePeerReadState(network: network, postbox: postbox, stateManager: stateManager, peerId: peerId, push: true, validate: thenSync) - } - disposable.set(synchronizeOperation.start()) - } - }) + let context = SynchronizePeerReadStatesStatesContext(network: network, postbox: postbox, stateManager: stateManager) return ActionDisposable { - disposable.dispose() - for disposable in state.with({ state -> [Disposable] in - state.clearDisposables() - }) { - disposable.dispose() - } + context.dispose() } } } diff --git a/submodules/TelegramCore/Sources/ManagedSynchronizePinnedChatsOperations.swift b/submodules/TelegramCore/Sources/ManagedSynchronizePinnedChatsOperations.swift index d726df5b03..b5ff3b474e 100644 --- a/submodules/TelegramCore/Sources/ManagedSynchronizePinnedChatsOperations.swift +++ b/submodules/TelegramCore/Sources/ManagedSynchronizePinnedChatsOperations.swift @@ -135,7 +135,7 @@ private func synchronizePinnedChats(transaction: Transaction, postbox: Postbox, |> mapToSignal { dialogs -> Signal in var storeMessages: [StoreMessage] = [] var readStates: [PeerId: [MessageId.Namespace: PeerReadState]] = [:] - var chatStates: [PeerId: PeerChatState] = [:] + var channelStates: [PeerId: Int32] = [:] var notificationSettings: [PeerId: PeerNotificationSettings] = [:] var remoteItemIds: [PinnedItemId] = [] @@ -200,7 +200,7 @@ private func synchronizePinnedChats(transaction: Transaction, postbox: Postbox, readStates[peerId]![Namespaces.Message.Cloud] = .idBased(maxIncomingReadId: apiReadInboxMaxId, maxOutgoingReadId: apiReadOutboxMaxId, maxKnownId: apiTopMessage, count: apiUnreadCount, markedUnread: apiMarkedUnread) if let apiChannelPts = apiChannelPts { - chatStates[peerId] = ChannelState(pts: apiChannelPts, invalidatedPts: nil, synchronizedUntilMessageId: nil) + channelStates[peerId] = apiChannelPts } notificationSettings[peerId] = TelegramPeerNotificationSettings(apiSettings: apiNotificationSettings) @@ -245,15 +245,11 @@ private func synchronizePinnedChats(transaction: Transaction, postbox: Postbox, transaction.resetIncomingReadStates(readStates) - for (peerId, chatState) in chatStates { - if let chatState = chatState as? ChannelState { - if let _ = transaction.getPeerChatState(peerId) as? ChannelState { - // skip changing state - } else { - transaction.setPeerChatState(peerId, state: chatState) - } + for (peerId, pts) in channelStates { + if let _ = transaction.getPeerChatState(peerId) as? ChannelState { + // skip changing state } else { - transaction.setPeerChatState(peerId, state: chatState) + transaction.setPeerChatState(peerId, state: ChannelState(pts: pts, invalidatedPts: nil, synchronizedUntilMessageId: nil)) } } diff --git a/submodules/TelegramUI/Sources/ChatInterfaceStateContextMenus.swift b/submodules/TelegramUI/Sources/ChatInterfaceStateContextMenus.swift index 38630e8e75..9b3ad9d2ff 100644 --- a/submodules/TelegramUI/Sources/ChatInterfaceStateContextMenus.swift +++ b/submodules/TelegramUI/Sources/ChatInterfaceStateContextMenus.swift @@ -49,8 +49,20 @@ private func canEditMessage(accountPeerId: PeerId, limitsConfiguration: LimitsCo } } else if message.id.peerId.namespace == Namespaces.Peer.SecretChat || message.id.namespace != Namespaces.Message.Cloud { hasEditRights = false - } else if let author = message.author, author.id == accountPeerId { + } else if let author = message.author, author.id == accountPeerId, let peer = message.peers[message.id.peerId] { hasEditRights = true + if let peer = peer as? TelegramChannel { + switch peer.info { + case .broadcast: + if peer.hasPermission(.editAllMessages) || !message.flags.contains(.Incoming) { + unlimitedInterval = true + } + case .group: + if peer.hasPermission(.pinMessages) { + unlimitedInterval = true + } + } + } } else if message.author?.id == message.id.peerId, let peer = message.peers[message.id.peerId] { if let peer = peer as? TelegramChannel { switch peer.info {