mirror of
https://github.com/Swiftgram/Telegram-iOS.git
synced 2025-06-16 05:55:20 +00:00
931 lines
42 KiB
Swift
931 lines
42 KiB
Swift
import Foundation
|
|
|
|
public struct ChatListEntryMessageTagSummaryComponent {
|
|
public let tag: MessageTags
|
|
public let namespace: MessageId.Namespace
|
|
|
|
public init(tag: MessageTags, namespace: MessageId.Namespace) {
|
|
self.tag = tag
|
|
self.namespace = namespace
|
|
}
|
|
}
|
|
|
|
public struct ChatListEntryPendingMessageActionsSummaryComponent {
|
|
public let type: PendingMessageActionType
|
|
public let namespace: MessageId.Namespace
|
|
|
|
public init(type: PendingMessageActionType, namespace: MessageId.Namespace) {
|
|
self.type = type
|
|
self.namespace = namespace
|
|
}
|
|
}
|
|
|
|
public struct ChatListEntrySummaryComponents {
|
|
public let tagSummary: ChatListEntryMessageTagSummaryComponent?
|
|
public let actionsSummary: ChatListEntryPendingMessageActionsSummaryComponent?
|
|
|
|
public init(tagSummary: ChatListEntryMessageTagSummaryComponent? = nil, actionsSummary: ChatListEntryPendingMessageActionsSummaryComponent? = nil) {
|
|
self.tagSummary = tagSummary
|
|
self.actionsSummary = actionsSummary
|
|
}
|
|
}
|
|
|
|
public struct ChatListMessageTagSummaryInfo: Equatable {
|
|
public let tagSummaryCount: Int32?
|
|
public let actionsSummaryCount: Int32?
|
|
|
|
public init(tagSummaryCount: Int32? = nil, actionsSummaryCount: Int32? = nil) {
|
|
self.tagSummaryCount = tagSummaryCount
|
|
self.actionsSummaryCount = actionsSummaryCount
|
|
}
|
|
|
|
public static func ==(lhs: ChatListMessageTagSummaryInfo, rhs: ChatListMessageTagSummaryInfo) -> Bool {
|
|
return lhs.tagSummaryCount == rhs.tagSummaryCount && lhs.actionsSummaryCount == rhs.actionsSummaryCount
|
|
}
|
|
}
|
|
|
|
public final class ChatListGroupReferencePeer: Equatable {
|
|
public let peer: RenderedPeer
|
|
public let isUnread: Bool
|
|
|
|
init(peer: RenderedPeer, isUnread: Bool) {
|
|
self.peer = peer
|
|
self.isUnread = isUnread
|
|
}
|
|
|
|
public static func ==(lhs: ChatListGroupReferencePeer, rhs: ChatListGroupReferencePeer) -> Bool {
|
|
if lhs.peer != rhs.peer {
|
|
return false
|
|
}
|
|
if lhs.isUnread != rhs.isUnread {
|
|
return false
|
|
}
|
|
return true
|
|
}
|
|
}
|
|
|
|
public struct ChatListGroupReferenceEntry: Equatable {
|
|
public let groupId: PeerGroupId
|
|
public let message: Message?
|
|
public let renderedPeers: [ChatListGroupReferencePeer]
|
|
public let unreadState: PeerGroupUnreadCountersCombinedSummary
|
|
|
|
public static func ==(lhs: ChatListGroupReferenceEntry, rhs: ChatListGroupReferenceEntry) -> Bool {
|
|
if lhs.groupId != rhs.groupId {
|
|
return false
|
|
}
|
|
if lhs.unreadState != rhs.unreadState {
|
|
return false
|
|
}
|
|
if lhs.message?.stableVersion != rhs.message?.stableVersion {
|
|
return false
|
|
}
|
|
if lhs.renderedPeers != rhs.renderedPeers {
|
|
return false
|
|
}
|
|
return true
|
|
}
|
|
}
|
|
|
|
public enum ChatListEntry: Comparable {
|
|
case MessageEntry(ChatListIndex, Message?, CombinedPeerReadState?, PeerNotificationSettings?, PeerChatListEmbeddedInterfaceState?, RenderedPeer, PeerPresence?, ChatListMessageTagSummaryInfo)
|
|
case HoleEntry(ChatListHole)
|
|
|
|
public var index: ChatListIndex {
|
|
switch self {
|
|
case let .MessageEntry(index, _, _, _, _, _, _, _):
|
|
return index
|
|
case let .HoleEntry(hole):
|
|
return ChatListIndex(pinningIndex: nil, messageIndex: hole.index)
|
|
}
|
|
}
|
|
|
|
public static func ==(lhs: ChatListEntry, rhs: ChatListEntry) -> Bool {
|
|
switch lhs {
|
|
case let .MessageEntry(lhsIndex, lhsMessage, lhsReadState, lhsSettings, lhsEmbeddedState, lhsPeer, lhsPresence, lhsInfo):
|
|
switch rhs {
|
|
case let .MessageEntry(rhsIndex, rhsMessage, rhsReadState, rhsSettings, rhsEmbeddedState, rhsPeer, rhsPresence, rhsInfo):
|
|
if lhsIndex != rhsIndex {
|
|
return false
|
|
}
|
|
if lhsReadState != rhsReadState {
|
|
return false
|
|
}
|
|
if lhsMessage?.stableVersion != rhsMessage?.stableVersion {
|
|
return false
|
|
}
|
|
if let lhsSettings = lhsSettings, let rhsSettings = rhsSettings {
|
|
if !lhsSettings.isEqual(to: rhsSettings) {
|
|
return false
|
|
}
|
|
} else if (lhsSettings != nil) != (rhsSettings != nil) {
|
|
return false
|
|
}
|
|
if let lhsEmbeddedState = lhsEmbeddedState, let rhsEmbeddedState = rhsEmbeddedState {
|
|
if !lhsEmbeddedState.isEqual(to: rhsEmbeddedState) {
|
|
return false
|
|
}
|
|
} else if (lhsEmbeddedState != nil) != (rhsEmbeddedState != nil) {
|
|
return false
|
|
}
|
|
if lhsPeer != rhsPeer {
|
|
return false
|
|
}
|
|
if let lhsPresence = lhsPresence, let rhsPresence = rhsPresence {
|
|
if !lhsPresence.isEqual(to: rhsPresence) {
|
|
return false
|
|
}
|
|
} else if (lhsPresence != nil) != (rhsPresence != nil) {
|
|
return false
|
|
}
|
|
if lhsInfo != rhsInfo {
|
|
return false
|
|
}
|
|
return true
|
|
default:
|
|
return false
|
|
}
|
|
case let .HoleEntry(hole):
|
|
if case .HoleEntry(hole) = rhs {
|
|
return true
|
|
} else {
|
|
return false
|
|
}
|
|
}
|
|
}
|
|
|
|
public static func <(lhs: ChatListEntry, rhs: ChatListEntry) -> Bool {
|
|
return lhs.index < rhs.index
|
|
}
|
|
}
|
|
|
|
private func processedChatListEntry(_ entry: MutableChatListEntry, cachedDataTable: CachedPeerDataTable, readStateTable: MessageHistoryReadStateTable, messageHistoryTable: MessageHistoryTable) -> MutableChatListEntry {
|
|
switch entry {
|
|
case let .IntermediateMessageEntry(index, message, readState, embeddedState):
|
|
var updatedMessage = message
|
|
if let message = message, let cachedData = cachedDataTable.get(message.id.peerId), let associatedHistoryMessageId = cachedData.associatedHistoryMessageId, message.id.id == 1 {
|
|
if let messageIndex = messageHistoryTable.messageHistoryIndexTable.earlierEntries(id: associatedHistoryMessageId, count: 1).first {
|
|
if let associatedMessage = messageHistoryTable.getMessage(messageIndex) {
|
|
updatedMessage = associatedMessage
|
|
}
|
|
}
|
|
}
|
|
return .IntermediateMessageEntry(index, updatedMessage, readState, embeddedState)
|
|
default:
|
|
return entry
|
|
}
|
|
}
|
|
|
|
enum MutableChatListEntry: Equatable {
|
|
case IntermediateMessageEntry(ChatListIndex, IntermediateMessage?, CombinedPeerReadState?, PeerChatListEmbeddedInterfaceState?)
|
|
case MessageEntry(ChatListIndex, Message?, CombinedPeerReadState?, PeerNotificationSettings?, PeerChatListEmbeddedInterfaceState?, RenderedPeer, PeerPresence?, ChatListMessageTagSummaryInfo)
|
|
case HoleEntry(ChatListHole)
|
|
|
|
init(_ intermediateEntry: ChatListIntermediateEntry, cachedDataTable: CachedPeerDataTable, readStateTable: MessageHistoryReadStateTable, messageHistoryTable: MessageHistoryTable) {
|
|
switch intermediateEntry {
|
|
case let .message(index, message, embeddedState):
|
|
self = processedChatListEntry(.IntermediateMessageEntry(index, message, readStateTable.getCombinedState(index.messageIndex.id.peerId), embeddedState), cachedDataTable: cachedDataTable, readStateTable: readStateTable, messageHistoryTable: messageHistoryTable)
|
|
case let .hole(hole):
|
|
self = .HoleEntry(hole)
|
|
}
|
|
}
|
|
|
|
var index: ChatListIndex {
|
|
switch self {
|
|
case let .IntermediateMessageEntry(index, _, _, _):
|
|
return index
|
|
case let .MessageEntry(index, _, _, _, _, _, _, _):
|
|
return index
|
|
case let .HoleEntry(hole):
|
|
return ChatListIndex(pinningIndex: nil, messageIndex: hole.index)
|
|
}
|
|
}
|
|
|
|
static func ==(lhs: MutableChatListEntry, rhs: MutableChatListEntry) -> Bool {
|
|
if lhs.index != rhs.index {
|
|
return false
|
|
}
|
|
|
|
switch lhs {
|
|
case .IntermediateMessageEntry:
|
|
switch rhs {
|
|
case .IntermediateMessageEntry:
|
|
return true
|
|
default:
|
|
return false
|
|
}
|
|
case .MessageEntry:
|
|
switch rhs {
|
|
case .MessageEntry:
|
|
return true
|
|
default:
|
|
return false
|
|
}
|
|
case .HoleEntry:
|
|
switch rhs {
|
|
case .HoleEntry:
|
|
return true
|
|
default:
|
|
return false
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
final class MutableChatListViewReplayContext {
|
|
var invalidEarlier: Bool = false
|
|
var invalidLater: Bool = false
|
|
var removedEntries: Bool = false
|
|
|
|
func empty() -> Bool {
|
|
return !self.removedEntries && !invalidEarlier && !invalidLater
|
|
}
|
|
}
|
|
|
|
private enum ChatListEntryType {
|
|
case message
|
|
case hole
|
|
case groupReference
|
|
}
|
|
|
|
private func updateMessagePeers(_ message: Message, updatedPeers: [PeerId: Peer]) -> Message? {
|
|
var updated = false
|
|
for (peerId, currentPeer) in message.peers {
|
|
if let updatedPeer = updatedPeers[peerId], !arePeersEqual(currentPeer, updatedPeer) {
|
|
updated = true
|
|
break
|
|
}
|
|
}
|
|
if updated {
|
|
var peers = SimpleDictionary<PeerId, Peer>()
|
|
for (peerId, currentPeer) in message.peers {
|
|
if let updatedPeer = updatedPeers[peerId] {
|
|
peers[peerId] = updatedPeer
|
|
} else {
|
|
peers[peerId] = currentPeer
|
|
}
|
|
}
|
|
return Message(stableId: message.stableId, stableVersion: message.stableVersion, id: message.id, globallyUniqueId: message.globallyUniqueId, groupingKey: message.groupingKey, groupInfo: message.groupInfo, timestamp: message.timestamp, flags: message.flags, tags: message.tags, globalTags: message.globalTags, localTags: message.localTags, forwardInfo: message.forwardInfo, author: message.author, text: message.text, attributes: message.attributes, media: message.media, peers: peers, associatedMessages: message.associatedMessages, associatedMessageIds: message.associatedMessageIds)
|
|
}
|
|
return nil
|
|
}
|
|
|
|
private func updatedRenderedPeer(_ renderedPeer: RenderedPeer, updatedPeers: [PeerId: Peer]) -> RenderedPeer? {
|
|
var updated = false
|
|
for (peerId, currentPeer) in renderedPeer.peers {
|
|
if let updatedPeer = updatedPeers[peerId], !arePeersEqual(currentPeer, updatedPeer) {
|
|
updated = true
|
|
break
|
|
}
|
|
}
|
|
if updated {
|
|
var peers = SimpleDictionary<PeerId, Peer>()
|
|
for (peerId, currentPeer) in renderedPeer.peers {
|
|
if let updatedPeer = updatedPeers[peerId] {
|
|
peers[peerId] = updatedPeer
|
|
} else {
|
|
peers[peerId] = currentPeer
|
|
}
|
|
}
|
|
return RenderedPeer(peerId: renderedPeer.peerId, peers: peers)
|
|
}
|
|
return nil
|
|
}
|
|
|
|
final class MutableChatListView {
|
|
let groupId: PeerGroupId
|
|
private let summaryComponents: ChatListEntrySummaryComponents
|
|
fileprivate var additionalItemIds: Set<PeerId>
|
|
fileprivate var additionalItemEntries: [MutableChatListEntry]
|
|
fileprivate var earlier: MutableChatListEntry?
|
|
fileprivate var later: MutableChatListEntry?
|
|
fileprivate var entries: [MutableChatListEntry]
|
|
fileprivate var groupEntries: [ChatListGroupReferenceEntry]
|
|
private var count: Int
|
|
|
|
init(postbox: Postbox, groupId: PeerGroupId, earlier: MutableChatListEntry?, entries: [MutableChatListEntry], later: MutableChatListEntry?, count: Int, summaryComponents: ChatListEntrySummaryComponents) {
|
|
self.groupId = groupId
|
|
self.earlier = earlier
|
|
self.entries = entries
|
|
self.later = later
|
|
self.count = count
|
|
self.summaryComponents = summaryComponents
|
|
self.additionalItemEntries = []
|
|
if case .root = groupId {
|
|
let itemIds = postbox.additionalChatListItemsTable.get()
|
|
self.additionalItemIds = Set(itemIds)
|
|
for peerId in itemIds {
|
|
if let entry = postbox.chatListTable.getStandalone(peerId: peerId, messageHistoryTable: postbox.messageHistoryTable) {
|
|
self.additionalItemEntries.append(MutableChatListEntry(entry, cachedDataTable: postbox.cachedPeerDataTable, readStateTable: postbox.readStateTable, messageHistoryTable: postbox.messageHistoryTable))
|
|
}
|
|
}
|
|
self.groupEntries = []
|
|
self.reloadGroups(postbox: postbox)
|
|
} else {
|
|
self.additionalItemIds = Set()
|
|
self.groupEntries = []
|
|
}
|
|
}
|
|
|
|
private func reloadGroups(postbox: Postbox) {
|
|
self.groupEntries.removeAll()
|
|
if case .root = self.groupId {
|
|
for groupId in postbox.chatListTable.existingGroups() {
|
|
var foundIndices: [(ChatListIndex, MessageIndex)] = []
|
|
var unpinnedCount = 0
|
|
let maxCount = 8
|
|
|
|
var upperBound: (ChatListIndex, Bool)?
|
|
inner: while true {
|
|
if let entry = postbox.chatListTable.earlierEntryInfos(groupId: groupId, index: upperBound, messageHistoryTable: postbox.messageHistoryTable, peerChatInterfaceStateTable: postbox.peerChatInterfaceStateTable, count: 1).first {
|
|
switch entry {
|
|
case let .message(index, messageIndex):
|
|
if let messageIndex = messageIndex {
|
|
foundIndices.append((index, messageIndex))
|
|
if index.pinningIndex == nil {
|
|
unpinnedCount += 1
|
|
}
|
|
|
|
if unpinnedCount >= maxCount {
|
|
break inner
|
|
}
|
|
|
|
upperBound = (entry.index, true)
|
|
} else {
|
|
upperBound = (entry.index.predecessor, true)
|
|
}
|
|
case .hole:
|
|
upperBound = (entry.index, false)
|
|
}
|
|
} else {
|
|
break inner
|
|
}
|
|
}
|
|
|
|
foundIndices.sort(by: { $0.1 > $1.1 })
|
|
if foundIndices.count > maxCount {
|
|
foundIndices.removeSubrange(maxCount...)
|
|
}
|
|
|
|
if !foundIndices.isEmpty {
|
|
var message: Message?
|
|
var renderedPeers: [ChatListGroupReferencePeer] = []
|
|
for (index, messageIndex) in foundIndices {
|
|
if let peer = postbox.peerTable.get(index.messageIndex.id.peerId) {
|
|
var peers = SimpleDictionary<PeerId, Peer>()
|
|
peers[peer.id] = peer
|
|
if let associatedPeerId = peer.associatedPeerId {
|
|
if let associatedPeer = postbox.peerTable.get(associatedPeerId) {
|
|
peers[associatedPeer.id] = associatedPeer
|
|
}
|
|
}
|
|
|
|
let renderedPeer = RenderedPeer(peerId: peer.id, peers: peers)
|
|
let isUnread = postbox.readStateTable.getCombinedState(peer.id)?.isUnread ?? false
|
|
renderedPeers.append(ChatListGroupReferencePeer(peer: renderedPeer, isUnread: isUnread))
|
|
|
|
if foundIndices.count == 1 && message == nil {
|
|
message = postbox.messageHistoryTable.getMessage(messageIndex).flatMap({ postbox.messageHistoryTable.renderMessage($0, peerTable: postbox.peerTable) })
|
|
}
|
|
}
|
|
}
|
|
|
|
self.groupEntries.append(ChatListGroupReferenceEntry(groupId: groupId, message: message, renderedPeers: renderedPeers, unreadState: postbox.groupMessageStatsTable.get(groupId: groupId)))
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
func refreshDueToExternalTransaction(postbox: Postbox) -> Bool {
|
|
var index = ChatListIndex.absoluteUpperBound
|
|
if !self.entries.isEmpty && self.later != nil {
|
|
index = self.entries[self.entries.count / 2].index
|
|
}
|
|
|
|
let (entries, earlier, later) = postbox.fetchAroundChatEntries(groupId: self.groupId, index: index, count: self.count)
|
|
let currentGroupEntries = self.groupEntries
|
|
|
|
self.reloadGroups(postbox: postbox)
|
|
|
|
var updated = false
|
|
|
|
if self.groupEntries != currentGroupEntries {
|
|
updated = true
|
|
}
|
|
|
|
if entries != self.entries || earlier != self.earlier || later != self.later {
|
|
self.entries = entries
|
|
self.earlier = earlier
|
|
self.later = later
|
|
updated = true
|
|
}
|
|
|
|
return updated
|
|
}
|
|
|
|
func replay(postbox: Postbox, operations: [PeerGroupId: [ChatListOperation]], updatedPeerNotificationSettings: [PeerId: PeerNotificationSettings], updatedPeers: [PeerId: Peer], updatedPeerPresences: [PeerId: PeerPresence], transaction: PostboxTransaction, context: MutableChatListViewReplayContext) -> Bool {
|
|
var hasChanges = false
|
|
|
|
if let groupOperations = operations[self.groupId] {
|
|
for operation in groupOperations {
|
|
switch operation {
|
|
case let .InsertEntry(index, message, combinedReadState, embeddedState):
|
|
if self.add(.IntermediateMessageEntry(index, message, combinedReadState, embeddedState), postbox: postbox) {
|
|
hasChanges = true
|
|
}
|
|
case let .InsertHole(index):
|
|
if self.add(.HoleEntry(index), postbox: postbox) {
|
|
hasChanges = true
|
|
}
|
|
case let .RemoveEntry(indices):
|
|
if self.remove(Set(indices), type: .message, context: context) {
|
|
hasChanges = true
|
|
}
|
|
case let .RemoveHoles(indices):
|
|
if self.remove(Set(indices), type: .hole, context: context) {
|
|
hasChanges = true
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
if case .root = self.groupId {
|
|
var invalidatedGroups = false
|
|
for (groupId, groupOperations) in operations {
|
|
if case .group = groupId, !groupOperations.isEmpty {
|
|
invalidatedGroups = true
|
|
}
|
|
}
|
|
|
|
if invalidatedGroups {
|
|
self.reloadGroups(postbox: postbox)
|
|
hasChanges = true
|
|
} else {
|
|
for i in 0 ..< self.groupEntries.count {
|
|
if let updatedState = transaction.currentUpdatedTotalUnreadSummaries[self.groupEntries[i].groupId] {
|
|
self.groupEntries[i] = ChatListGroupReferenceEntry(groupId: self.groupEntries[i].groupId, message: self.groupEntries[i].message, renderedPeers: self.groupEntries[i].renderedPeers, unreadState: updatedState)
|
|
hasChanges = true
|
|
}
|
|
}
|
|
|
|
if !transaction.alteredInitialPeerCombinedReadStates.isEmpty {
|
|
for i in 0 ..< self.groupEntries.count {
|
|
for j in 0 ..< groupEntries[i].renderedPeers.count {
|
|
if transaction.alteredInitialPeerCombinedReadStates[groupEntries[i].renderedPeers[j].peer.peerId] != nil {
|
|
let isUnread = postbox.readStateTable.getCombinedState(groupEntries[i].renderedPeers[j].peer.peerId)?.isUnread ?? false
|
|
if isUnread != groupEntries[i].renderedPeers[j].isUnread {
|
|
var renderedPeers = self.groupEntries[i].renderedPeers
|
|
renderedPeers[j] = ChatListGroupReferencePeer(peer: groupEntries[i].renderedPeers[j].peer, isUnread: isUnread)
|
|
self.groupEntries[i] = ChatListGroupReferenceEntry(groupId: self.groupEntries[i].groupId, message: self.groupEntries[i].message, renderedPeers: renderedPeers, unreadState: self.groupEntries[i].unreadState)
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
if !updatedPeerNotificationSettings.isEmpty {
|
|
for i in 0 ..< self.entries.count {
|
|
switch self.entries[i] {
|
|
case let .MessageEntry(index, message, readState, _, embeddedState, peer, peerPresence, summaryInfo):
|
|
var notificationSettingsPeerId = peer.peerId
|
|
if let peer = peer.peers[peer.peerId], let associatedPeerId = peer.associatedPeerId {
|
|
notificationSettingsPeerId = associatedPeerId
|
|
}
|
|
if let settings = updatedPeerNotificationSettings[notificationSettingsPeerId] {
|
|
self.entries[i] = .MessageEntry(index, message, readState, settings, embeddedState, peer, peerPresence, summaryInfo)
|
|
hasChanges = true
|
|
}
|
|
default:
|
|
continue
|
|
}
|
|
}
|
|
}
|
|
if !updatedPeers.isEmpty {
|
|
for i in 0 ..< self.entries.count {
|
|
switch self.entries[i] {
|
|
case let .MessageEntry(index, message, readState, settings, embeddedState, peer, peerPresence, summaryInfo):
|
|
var updatedMessage: Message?
|
|
if let message = message {
|
|
updatedMessage = updateMessagePeers(message, updatedPeers: updatedPeers)
|
|
}
|
|
let updatedPeer = updatedRenderedPeer(peer, updatedPeers: updatedPeers)
|
|
if updatedMessage != nil || updatedPeer != nil {
|
|
self.entries[i] = .MessageEntry(index, updatedMessage ?? message, readState, settings, embeddedState, updatedPeer ?? peer, peerPresence, summaryInfo)
|
|
hasChanges = true
|
|
}
|
|
default:
|
|
continue
|
|
}
|
|
}
|
|
}
|
|
if !updatedPeerPresences.isEmpty {
|
|
for i in 0 ..< self.entries.count {
|
|
switch self.entries[i] {
|
|
case let .MessageEntry(index, message, readState, settings, embeddedState, peer, _, summaryInfo):
|
|
var presencePeerId = peer.peerId
|
|
if let peer = peer.peers[peer.peerId], let associatedPeerId = peer.associatedPeerId {
|
|
presencePeerId = associatedPeerId
|
|
}
|
|
if let presence = updatedPeerPresences[presencePeerId] {
|
|
self.entries[i] = .MessageEntry(index, message, readState, settings, embeddedState, peer, presence, summaryInfo)
|
|
hasChanges = true
|
|
}
|
|
default:
|
|
continue
|
|
}
|
|
}
|
|
}
|
|
if !transaction.currentUpdatedMessageTagSummaries.isEmpty || !transaction.currentUpdatedMessageActionsSummaries.isEmpty {
|
|
for i in 0 ..< self.entries.count {
|
|
switch self.entries[i] {
|
|
case let .MessageEntry(index, message, readState, settings, embeddedState, peer, peerPresence, currentSummary):
|
|
var updatedTagSummaryCount: Int32?
|
|
var updatedActionsSummaryCount: Int32?
|
|
|
|
if let tagSummary = self.summaryComponents.tagSummary {
|
|
let key = MessageHistoryTagsSummaryKey(tag: tagSummary.tag, peerId: index.messageIndex.id.peerId, namespace: tagSummary.namespace)
|
|
if let summary = transaction.currentUpdatedMessageTagSummaries[key] {
|
|
updatedTagSummaryCount = summary.count
|
|
}
|
|
}
|
|
|
|
if let actionsSummary = self.summaryComponents.actionsSummary {
|
|
let key = PendingMessageActionsSummaryKey(type: actionsSummary.type, peerId: index.messageIndex.id.peerId, namespace: actionsSummary.namespace)
|
|
if let count = transaction.currentUpdatedMessageActionsSummaries[key] {
|
|
updatedActionsSummaryCount = count
|
|
}
|
|
}
|
|
|
|
if updatedTagSummaryCount != nil || updatedActionsSummaryCount != nil {
|
|
let summaryInfo = ChatListMessageTagSummaryInfo(tagSummaryCount: updatedTagSummaryCount ?? currentSummary.tagSummaryCount, actionsSummaryCount: updatedActionsSummaryCount ?? currentSummary.actionsSummaryCount)
|
|
|
|
self.entries[i] = .MessageEntry(index, message, readState, settings, embeddedState, peer, peerPresence, summaryInfo)
|
|
hasChanges = true
|
|
}
|
|
default:
|
|
continue
|
|
}
|
|
}
|
|
}
|
|
var updateAdditionalItems = false
|
|
if let itemIds = transaction.replacedAdditionalChatListItems {
|
|
self.additionalItemIds = Set(itemIds)
|
|
updateAdditionalItems = true
|
|
}
|
|
for peerId in self.additionalItemIds {
|
|
if transaction.currentOperationsByPeerId[peerId] != nil {
|
|
updateAdditionalItems = true
|
|
}
|
|
if transaction.currentUpdatedPeers[peerId] != nil {
|
|
updateAdditionalItems = true
|
|
}
|
|
if transaction.currentUpdatedChatListInclusions[peerId] != nil {
|
|
updateAdditionalItems = true
|
|
}
|
|
}
|
|
if updateAdditionalItems {
|
|
self.additionalItemEntries.removeAll()
|
|
for peerId in postbox.additionalChatListItemsTable.get() {
|
|
if let entry = postbox.chatListTable.getStandalone(peerId: peerId, messageHistoryTable: postbox.messageHistoryTable) {
|
|
self.additionalItemEntries.append(MutableChatListEntry(entry, cachedDataTable: postbox.cachedPeerDataTable, readStateTable: postbox.readStateTable, messageHistoryTable: postbox.messageHistoryTable))
|
|
}
|
|
}
|
|
hasChanges = true
|
|
}
|
|
return hasChanges
|
|
}
|
|
|
|
func add(_ initialEntry: MutableChatListEntry, postbox: Postbox) -> Bool {
|
|
let entry = processedChatListEntry(initialEntry, cachedDataTable: postbox.cachedPeerDataTable, readStateTable: postbox.readStateTable, messageHistoryTable: postbox.messageHistoryTable)
|
|
if self.entries.count == 0 {
|
|
self.entries.append(entry)
|
|
return true
|
|
} else {
|
|
let first = self.entries[self.entries.count - 1]
|
|
let last = self.entries[0]
|
|
|
|
let next = self.later
|
|
|
|
if entry.index < last.index {
|
|
if self.earlier == nil || self.earlier!.index < entry.index {
|
|
if self.entries.count < self.count {
|
|
self.entries.insert(entry, at: 0)
|
|
} else {
|
|
self.earlier = entry
|
|
}
|
|
return true
|
|
} else {
|
|
return false
|
|
}
|
|
} else if entry.index > first.index {
|
|
if next != nil && entry.index > next!.index {
|
|
if self.later == nil || self.later!.index > entry.index {
|
|
if self.entries.count < self.count {
|
|
self.entries.append(entry)
|
|
} else {
|
|
self.later = entry
|
|
}
|
|
return true
|
|
} else {
|
|
return false
|
|
}
|
|
} else {
|
|
self.entries.append(entry)
|
|
if self.entries.count > self.count {
|
|
self.earlier = self.entries[0]
|
|
self.entries.remove(at: 0)
|
|
}
|
|
return true
|
|
}
|
|
} else if entry != last && entry != first {
|
|
var i = self.entries.count
|
|
while i >= 1 {
|
|
if self.entries[i - 1].index < entry.index {
|
|
break
|
|
}
|
|
i -= 1
|
|
}
|
|
self.entries.insert(entry, at: i)
|
|
if self.entries.count > self.count {
|
|
self.earlier = self.entries[0]
|
|
self.entries.remove(at: 0)
|
|
}
|
|
return true
|
|
} else {
|
|
return false
|
|
}
|
|
}
|
|
}
|
|
|
|
private func remove(_ indices: Set<ChatListIndex>, type: ChatListEntryType, context: MutableChatListViewReplayContext) -> Bool {
|
|
var hasChanges = false
|
|
if let earlier = self.earlier, indices.contains(earlier.index) {
|
|
var match = false
|
|
switch earlier {
|
|
case .HoleEntry:
|
|
match = type == .hole
|
|
case .IntermediateMessageEntry, .MessageEntry:
|
|
match = type == .message
|
|
/*case .IntermediateGroupReferenceEntry, .GroupReferenceEntry:
|
|
match = type == .groupReference*/
|
|
}
|
|
if match {
|
|
context.invalidEarlier = true
|
|
hasChanges = true
|
|
}
|
|
}
|
|
|
|
if let later = self.later, indices.contains(later.index) {
|
|
var match = false
|
|
switch later {
|
|
case .HoleEntry:
|
|
match = type == .hole
|
|
case .IntermediateMessageEntry, .MessageEntry:
|
|
match = type == .message
|
|
/*case .IntermediateGroupReferenceEntry, .GroupReferenceEntry:
|
|
match = type == .groupReference*/
|
|
}
|
|
if match {
|
|
context.invalidLater = true
|
|
hasChanges = true
|
|
}
|
|
}
|
|
|
|
if self.entries.count != 0 {
|
|
var i = self.entries.count - 1
|
|
while i >= 0 {
|
|
if indices.contains(self.entries[i].index) {
|
|
var match = false
|
|
switch self.entries[i] {
|
|
case .HoleEntry:
|
|
match = type == .hole
|
|
case .IntermediateMessageEntry, .MessageEntry:
|
|
match = type == .message
|
|
/*case .IntermediateGroupReferenceEntry, .GroupReferenceEntry:
|
|
match = type == .groupReference*/
|
|
}
|
|
if match {
|
|
self.entries.remove(at: i)
|
|
context.removedEntries = true
|
|
hasChanges = true
|
|
}
|
|
}
|
|
i -= 1
|
|
}
|
|
}
|
|
|
|
return hasChanges
|
|
}
|
|
|
|
func complete(postbox: Postbox, context: MutableChatListViewReplayContext) {
|
|
if context.removedEntries {
|
|
var addedEntries: [MutableChatListEntry] = []
|
|
|
|
var latestAnchor: ChatListIndex?
|
|
if let last = self.entries.last {
|
|
latestAnchor = last.index
|
|
}
|
|
|
|
if latestAnchor == nil {
|
|
if let later = self.later {
|
|
latestAnchor = later.index
|
|
}
|
|
}
|
|
|
|
if let later = self.later {
|
|
addedEntries += postbox.fetchLaterChatEntries(groupId: self.groupId, index: later.index.predecessor, count: self.count)
|
|
}
|
|
if let earlier = self.earlier {
|
|
addedEntries += postbox.fetchEarlierChatEntries(groupId: self.groupId, index: earlier.index.successor, count: self.count)
|
|
}
|
|
|
|
addedEntries += self.entries
|
|
addedEntries.sort(by: { $0.index < $1.index })
|
|
var i = addedEntries.count - 1
|
|
while i >= 1 {
|
|
if addedEntries[i].index.messageIndex.id == addedEntries[i - 1].index.messageIndex.id {
|
|
addedEntries.remove(at: i)
|
|
}
|
|
i -= 1
|
|
}
|
|
self.entries = []
|
|
|
|
var anchorIndex = addedEntries.count - 1
|
|
if let latestAnchor = latestAnchor {
|
|
var i = addedEntries.count - 1
|
|
while i >= 0 {
|
|
if addedEntries[i].index <= latestAnchor {
|
|
anchorIndex = i
|
|
break
|
|
}
|
|
i -= 1
|
|
}
|
|
}
|
|
|
|
self.later = nil
|
|
if anchorIndex + 1 < addedEntries.count {
|
|
self.later = addedEntries[anchorIndex + 1]
|
|
}
|
|
|
|
i = anchorIndex
|
|
while i >= 0 && i > anchorIndex - self.count {
|
|
self.entries.insert(addedEntries[i], at: 0)
|
|
i -= 1
|
|
}
|
|
|
|
self.earlier = nil
|
|
if anchorIndex - self.count >= 0 {
|
|
self.earlier = addedEntries[anchorIndex - self.count]
|
|
}
|
|
} else {
|
|
if context.invalidEarlier {
|
|
var earlyId: ChatListIndex?
|
|
let i = 0
|
|
if i < self.entries.count {
|
|
earlyId = self.entries[i].index
|
|
}
|
|
|
|
let earlierEntries = postbox.fetchEarlierChatEntries(groupId: self.groupId, index: earlyId, count: 1)
|
|
self.earlier = earlierEntries.first
|
|
}
|
|
|
|
if context.invalidLater {
|
|
var laterId: ChatListIndex?
|
|
let i = self.entries.count - 1
|
|
if i >= 0 {
|
|
laterId = self.entries[i].index
|
|
}
|
|
|
|
let laterEntries = postbox.fetchLaterChatEntries(groupId: self.groupId, index: laterId, count: 1)
|
|
self.later = laterEntries.first
|
|
}
|
|
}
|
|
}
|
|
|
|
func firstHole() -> ChatListHole? {
|
|
for entry in self.entries {
|
|
if case let .HoleEntry(hole) = entry {
|
|
return hole
|
|
}
|
|
}
|
|
|
|
return nil
|
|
}
|
|
|
|
private func renderEntry(_ entry: MutableChatListEntry, postbox: Postbox, renderMessage: (IntermediateMessage) -> Message, getPeer: (PeerId) -> Peer?, getPeerNotificationSettings: (PeerId) -> PeerNotificationSettings?, getPeerPresence: (PeerId) -> PeerPresence?) -> MutableChatListEntry? {
|
|
switch entry {
|
|
case let .IntermediateMessageEntry(index, message, combinedReadState, embeddedState):
|
|
let renderedMessage: Message?
|
|
if let message = message {
|
|
renderedMessage = renderMessage(message)
|
|
} else {
|
|
renderedMessage = nil
|
|
}
|
|
var peers = SimpleDictionary<PeerId, Peer>()
|
|
var notificationSettings: PeerNotificationSettings?
|
|
var presence: PeerPresence?
|
|
if let peer = getPeer(index.messageIndex.id.peerId) {
|
|
peers[peer.id] = peer
|
|
if let associatedPeerId = peer.associatedPeerId {
|
|
if let associatedPeer = getPeer(associatedPeerId) {
|
|
peers[associatedPeer.id] = associatedPeer
|
|
}
|
|
notificationSettings = getPeerNotificationSettings(associatedPeerId)
|
|
presence = getPeerPresence(associatedPeerId)
|
|
} else {
|
|
notificationSettings = getPeerNotificationSettings(index.messageIndex.id.peerId)
|
|
presence = getPeerPresence(index.messageIndex.id.peerId)
|
|
}
|
|
}
|
|
|
|
var tagSummaryCount: Int32?
|
|
var actionsSummaryCount: Int32?
|
|
|
|
if let tagSummary = self.summaryComponents.tagSummary {
|
|
let key = MessageHistoryTagsSummaryKey(tag: tagSummary.tag, peerId: index.messageIndex.id.peerId, namespace: tagSummary.namespace)
|
|
if let summary = postbox.messageHistoryTagsSummaryTable.get(key) {
|
|
tagSummaryCount = summary.count
|
|
}
|
|
}
|
|
|
|
if let actionsSummary = self.summaryComponents.actionsSummary {
|
|
let key = PendingMessageActionsSummaryKey(type: actionsSummary.type, peerId: index.messageIndex.id.peerId, namespace: actionsSummary.namespace)
|
|
actionsSummaryCount = postbox.pendingMessageActionsMetadataTable.getCount(.peerNamespaceAction(key.peerId, key.namespace, key.type))
|
|
}
|
|
|
|
return .MessageEntry(index, renderedMessage, combinedReadState, notificationSettings, embeddedState, RenderedPeer(peerId: index.messageIndex.id.peerId, peers: peers), presence, ChatListMessageTagSummaryInfo(tagSummaryCount: tagSummaryCount, actionsSummaryCount: actionsSummaryCount))
|
|
/*case let .IntermediateGroupReferenceEntry(groupId, index, counters):
|
|
let message = postbox.messageHistoryTable.getMessage(index.messageIndex).flatMap(postbox.renderIntermediateMessage)
|
|
return .GroupReferenceEntry(groupId, index, message, ChatListGroupReferenceTopPeers(postbox: postbox, groupId: groupId), counters ?? ChatListGroupReferenceUnreadCounters(postbox: postbox, groupId: groupId))*/
|
|
default:
|
|
return nil
|
|
}
|
|
}
|
|
|
|
func render(postbox: Postbox, renderMessage: (IntermediateMessage) -> Message, getPeer: (PeerId) -> Peer?, getPeerNotificationSettings: (PeerId) -> PeerNotificationSettings?, getPeerPresence: (PeerId) -> PeerPresence?) {
|
|
for i in 0 ..< self.entries.count {
|
|
if let updatedEntry = self.renderEntry(self.entries[i], postbox: postbox, renderMessage: renderMessage, getPeer: getPeer, getPeerNotificationSettings: getPeerNotificationSettings, getPeerPresence: getPeerPresence) {
|
|
self.entries[i] = updatedEntry
|
|
}
|
|
}
|
|
for i in 0 ..< self.additionalItemEntries.count {
|
|
if let updatedEntry = self.renderEntry(self.additionalItemEntries[i], postbox: postbox, renderMessage: renderMessage, getPeer: getPeer, getPeerNotificationSettings: getPeerNotificationSettings, getPeerPresence: getPeerPresence) {
|
|
self.additionalItemEntries[i] = updatedEntry
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
public final class ChatListView {
|
|
public let groupId: PeerGroupId
|
|
public let additionalItemEntries: [ChatListEntry]
|
|
public let entries: [ChatListEntry]
|
|
public let groupEntries: [ChatListGroupReferenceEntry]
|
|
public let earlierIndex: ChatListIndex?
|
|
public let laterIndex: ChatListIndex?
|
|
|
|
init(_ mutableView: MutableChatListView) {
|
|
self.groupId = mutableView.groupId
|
|
|
|
var entries: [ChatListEntry] = []
|
|
for entry in mutableView.entries {
|
|
switch entry {
|
|
case let .MessageEntry(index, message, combinedReadState, notificationSettings, embeddedState, peer, peerPresence, summaryInfo):
|
|
entries.append(.MessageEntry(index, message, combinedReadState, notificationSettings, embeddedState, peer, peerPresence, summaryInfo))
|
|
case let .HoleEntry(hole):
|
|
entries.append(.HoleEntry(hole))
|
|
/*case let .GroupReferenceEntry(groupId, index, message, topPeers, counters):
|
|
entries.append(.GroupReferenceEntry(groupId, index, message, topPeers.getPeers(), GroupReferenceUnreadCounters(counters)))*/
|
|
case .IntermediateMessageEntry:
|
|
assertionFailure()
|
|
/*case .IntermediateGroupReferenceEntry:
|
|
assertionFailure()*/
|
|
}
|
|
}
|
|
self.groupEntries = mutableView.groupEntries
|
|
self.entries = entries
|
|
self.earlierIndex = mutableView.earlier?.index
|
|
self.laterIndex = mutableView.later?.index
|
|
|
|
var additionalItemEntries: [ChatListEntry] = []
|
|
for entry in mutableView.additionalItemEntries {
|
|
switch entry {
|
|
case let .MessageEntry(index, message, combinedReadState, notificationSettings, embeddedState, peer, peerPresence, summaryInfo):
|
|
additionalItemEntries.append(.MessageEntry(index, message, combinedReadState, notificationSettings, embeddedState, peer, peerPresence, summaryInfo))
|
|
case .HoleEntry:
|
|
assertionFailure()
|
|
/*case .GroupReferenceEntry:
|
|
assertionFailure()*/
|
|
case .IntermediateMessageEntry:
|
|
assertionFailure()
|
|
/*case .IntermediateGroupReferenceEntry:
|
|
assertionFailure()*/
|
|
}
|
|
}
|
|
|
|
self.additionalItemEntries = additionalItemEntries
|
|
}
|
|
}
|