State management fixes

This commit is contained in:
Ali 2020-04-22 21:37:41 +04:00
parent 4a045feb2f
commit fb07f06a26
9 changed files with 292 additions and 147 deletions

View File

@ -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 ?? "")
}
}

View File

@ -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<PeerId>
let chatStates: [PeerId: PeerChatState]
let channelStates: [PeerId: AccountStateChannelState]
let peerChatInfos: [PeerId: PeerChatInfo]
let peerIdsRequiringLocalChatState: Set<PeerId>
let locallyGeneratedMessageTimestamps: [PeerId: [(MessageId.Namespace, Int32)]]
let cloudReadStates: [PeerId: PeerReadState]
let channelsToPollExplicitely: Set<PeerId>
init(state: AuthorizedAccountState.State, peerIds: Set<PeerId>, peerIdsRequiringLocalChatState: Set<PeerId>, chatStates: [PeerId: PeerChatState], peerChatInfos: [PeerId: PeerChatInfo], locallyGeneratedMessageTimestamps: [PeerId: [(MessageId.Namespace, Int32)]], cloudReadStates: [PeerId: PeerReadState], channelsToPollExplicitely: Set<PeerId>) {
init(state: AuthorizedAccountState.State, peerIds: Set<PeerId>, peerIdsRequiringLocalChatState: Set<PeerId>, channelStates: [PeerId: AccountStateChannelState], peerChatInfos: [PeerId: PeerChatInfo], locallyGeneratedMessageTimestamps: [PeerId: [(MessageId.Namespace, Int32)]], cloudReadStates: [PeerId: PeerReadState], channelsToPollExplicitely: Set<PeerId>) {
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<MessageId>
var storedMessages: Set<MessageId>
@ -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<MessageId>, storedMessages: Set<MessageId>, readInboxMaxIds: [PeerId: MessageId], storedMessagesByPeerIdAndTimestamp: [PeerId: Set<MessageIndex>], namespacesWithHolesFromPreviousState: [PeerId: Set<MessageId.Namespace>], 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<MessageId>, storedMessages: Set<MessageId>, readInboxMaxIds: [PeerId: MessageId], storedMessagesByPeerIdAndTimestamp: [PeerId: Set<MessageIndex>], namespacesWithHolesFromPreviousState: [PeerId: Set<MessageId.Namespace>], 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] {

View File

@ -356,7 +356,7 @@ private func locallyGeneratedMessageTimestampsFromDifference(_ difference: Api.u
private func initialStateWithPeerIds(_ transaction: Transaction, peerIds: Set<PeerId>, activeChannelIds: Set<PeerId>, associatedMessageIds: Set<MessageId>, peerIdsRequiringLocalChatState: Set<PeerId>, locallyGeneratedMessageTimestamps: [PeerId: [(MessageId.Namespace, Int32)]]) -> AccountMutableState {
var peers: [PeerId: Peer] = [:]
var chatStates: [PeerId: PeerChatState] = [:]
var channelStates: [PeerId: AccountStateChannelState] = [:]
var channelsToPollExplicitely = Set<PeerId>()
@ -367,11 +367,11 @@ private func initialStateWithPeerIds(_ transaction: Transaction, peerIds: Set<Pe
if peerId.namespace == Namespaces.Peer.CloudChannel {
if let channelState = transaction.getPeerChatState(peerId) as? ChannelState {
chatStates[peerId] = channelState
channelStates[peerId] = AccountStateChannelState(pts: channelState.pts)
}
} else if peerId.namespace == Namespaces.Peer.CloudUser || peerId.namespace == Namespaces.Peer.CloudGroup {
if let chatState = transaction.getPeerChatState(peerId) as? RegularChatState {
chatStates[peerId] = chatState
if let _ = transaction.getPeerChatState(peerId) as? RegularChatState {
//chatStates[peerId] = chatState
}
}
}
@ -434,7 +434,7 @@ private func initialStateWithPeerIds(_ transaction: Transaction, peerIds: Set<Pe
}
}
return AccountMutableState(initialState: AccountInitialState(state: (transaction.getState() as? AuthorizedAccountState)!.state!, peerIds: peerIds, peerIdsRequiringLocalChatState: peerIdsRequiringLocalChatState, chatStates: chatStates, peerChatInfos: peerChatInfos, locallyGeneratedMessageTimestamps: locallyGeneratedMessageTimestamps, cloudReadStates: cloudReadStates, channelsToPollExplicitely: channelsToPollExplicitely), initialPeers: peers, initialReferencedMessageIds: associatedMessageIds, initialStoredMessages: storedMessages, initialReadInboxMaxIds: readInboxMaxIds, storedMessagesByPeerIdAndTimestamp: storedMessagesByPeerIdAndTimestamp)
return AccountMutableState(initialState: AccountInitialState(state: (transaction.getState() as? AuthorizedAccountState)!.state!, peerIds: peerIds, peerIdsRequiringLocalChatState: peerIdsRequiringLocalChatState, channelStates: channelStates, peerChatInfos: peerChatInfos, locallyGeneratedMessageTimestamps: locallyGeneratedMessageTimestamps, cloudReadStates: cloudReadStates, channelsToPollExplicitely: channelsToPollExplicitely), initialPeers: peers, initialReferencedMessageIds: associatedMessageIds, initialStoredMessages: storedMessages, initialReadInboxMaxIds: readInboxMaxIds, storedMessagesByPeerIdAndTimestamp: storedMessagesByPeerIdAndTimestamp)
}
func initialStateWithUpdateGroups(postbox: Postbox, groups: [UpdateGroup]) -> Signal<AccountMutableState, NoError> {
@ -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<Void, NoError> {
return postbox.transaction { transaction -> Signal<Void, NoError> 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<Void, NoError> 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):

View File

@ -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)
}
}

View File

@ -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,

View File

@ -479,15 +479,11 @@ func fetchChatListHole(postbox: Postbox, network: Network, accountPeerId: PeerId
}
}
for (peerId, chatState) in fetchedChats.chatStates {
if let chatState = chatState as? ChannelState {
for (peerId, pts) in fetchedChats.channelStates {
if let current = transaction.getPeerChatState(peerId) as? ChannelState {
transaction.setPeerChatState(peerId, state: current.withUpdatedPts(chatState.pts))
transaction.setPeerChatState(peerId, state: current.withUpdatedPts(pts))
} else {
transaction.setPeerChatState(peerId, state: chatState)
}
} else {
transaction.setPeerChatState(peerId, state: chatState)
transaction.setPeerChatState(peerId, state: ChannelState(pts: pts, invalidatedPts: nil, synchronizedUntilMessageId: nil))
}
}

View File

@ -2,70 +2,134 @@ import Foundation
import Postbox
import SwiftSignalKit
private final class ManagedSynchronizePeerReadStatesState {
private var synchronizeDisposables: [PeerId: (PeerReadStateSynchronizationOperation, Disposable)] = [:]
private final class SynchronizePeerReadStatesContextImpl {
private final class Operation {
let operation: PeerReadStateSynchronizationOperation
let disposable: Disposable
func clearDisposables() -> [Disposable] {
let disposables = Array(self.synchronizeDisposables.values.map({ $0.1 }))
self.synchronizeDisposables.removeAll()
return disposables
init(
operation: PeerReadStateSynchronizationOperation,
disposable: Disposable
) {
self.operation = operation
self.disposable = disposable
}
func update(operations: [PeerId: PeerReadStateSynchronizationOperation]) -> (removed: [Disposable], added: [(PeerId, PeerReadStateSynchronizationOperation, MetaDisposable)]) {
var removed: [Disposable] = []
var added: [(PeerId, PeerReadStateSynchronizationOperation, MetaDisposable)] = []
for (peerId, (operation, disposable)) in self.synchronizeDisposables {
if operations[peerId] != operation {
removed.append(disposable)
self.synchronizeDisposables.removeValue(forKey: peerId)
deinit {
self.disposable.dispose()
}
}
for (peerId, operation) in operations {
if self.synchronizeDisposables[peerId] == nil {
let disposable = MetaDisposable()
self.synchronizeDisposables[peerId] = (operation, disposable)
added.append((peerId, operation, disposable))
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
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<Never, NoError>
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()
}
}
}))
}
}
}
}
}
return (removed, added)
private final class SynchronizePeerReadStatesStatesContext {
private let queue: Queue
private let impl: QueueLocalObject<SynchronizePeerReadStatesContextImpl>
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()
}
}
}
func managedSynchronizePeerReadStates(network: Network, postbox: Postbox, stateManager: AccountStateManager) -> Signal<Void, NoError> {
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<Void, NoError>
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()
}
}
}

View File

@ -135,7 +135,7 @@ private func synchronizePinnedChats(transaction: Transaction, postbox: Postbox,
|> mapToSignal { dialogs -> Signal<Void, NoError> 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 {
for (peerId, pts) in channelStates {
if let _ = transaction.getPeerChatState(peerId) as? ChannelState {
// skip changing state
} else {
transaction.setPeerChatState(peerId, state: chatState)
}
} else {
transaction.setPeerChatState(peerId, state: chatState)
transaction.setPeerChatState(peerId, state: ChannelState(pts: pts, invalidatedPts: nil, synchronizedUntilMessageId: nil))
}
}

View File

@ -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 {