// // Copyright 2020 Signal Messenger, LLC // SPDX-License-Identifier: AGPL-3.0-only // import SignalServiceKit import SignalUI // This captures all of the state loaded by the CVLoadCoordinator. // // It is intended to be an immutable, comprehensive snapshot of all // loaded state needed to render CVC at a given state of time. class CVRenderState { public static let renderStateId_unknown: UInt = 0 private static let idCounter = AtomicUInt(1, lock: .sharedGlobal) let renderStateId: UInt let threadViewModel: ThreadViewModel let prevThreadViewModel: ThreadViewModel? var isEmptyInitialState: Bool { prevThreadViewModel == nil } let conversationViewModel: ConversationViewModel var isFirstLoad: Bool { if let loadType = self.loadType, case CVLoadType.loadInitialMapping = loadType { return true } return false } // All of CVRenderState's state should be immutable. let items: [CVRenderItem] let canLoadOlderItems: Bool let canLoadNewerItems: Bool // The "view state" values that were used when loading this render state. let viewStateSnapshot: CVViewStateSnapshot let loadType: CVLoadType? let loadDate = Date() // These values reflect the style and view state at the time // was load began. They may be obsolete. var conversationStyle: ConversationStyle { viewStateSnapshot.conversationStyle } // TODO: We might want to precompute: interactionIndexMap, focusItemIndex, unreadIndicatorIndex var disappearingMessagesConfiguration: OWSDisappearingMessagesConfiguration { threadViewModel.disappearingMessagesConfiguration } public let indexPathOfUnreadIndicator: IndexPath? private let interactionIdToIndexPathMap: [String: IndexPath] public let allIndexPaths: [IndexPath] init( threadViewModel: ThreadViewModel, prevThreadViewModel: ThreadViewModel?, conversationViewModel: ConversationViewModel, items: [CVRenderItem], canLoadOlderItems: Bool, canLoadNewerItems: Bool, viewStateSnapshot: CVViewStateSnapshot, loadType: CVLoadType? ) { self.threadViewModel = threadViewModel self.prevThreadViewModel = prevThreadViewModel self.conversationViewModel = conversationViewModel self.items = items self.canLoadOlderItems = canLoadOlderItems self.canLoadNewerItems = canLoadNewerItems self.viewStateSnapshot = viewStateSnapshot self.loadType = loadType self.renderStateId = Self.idCounter.increment() let messageSection = CVLoadCoordinator.messageSection var indexPathOfUnreadIndicator: IndexPath? var interactionIdToIndexPathMap = [String: IndexPath]() var allIndexPaths = [IndexPath]() for (index, item) in items.enumerated() { let indexPath = IndexPath(row: index, section: messageSection) interactionIdToIndexPathMap[item.interaction.uniqueId] = indexPath allIndexPaths.append(indexPath) if item.interactionType == .unreadIndicator { indexPathOfUnreadIndicator = indexPath } } self.indexPathOfUnreadIndicator = indexPathOfUnreadIndicator self.interactionIdToIndexPathMap = interactionIdToIndexPathMap self.allIndexPaths = allIndexPaths } static func defaultRenderState( threadViewModel: ThreadViewModel, conversationViewModel: ConversationViewModel, viewStateSnapshot: CVViewStateSnapshot ) -> CVRenderState { CVRenderState( threadViewModel: threadViewModel, prevThreadViewModel: nil, conversationViewModel: conversationViewModel, items: [], canLoadOlderItems: false, canLoadNewerItems: false, viewStateSnapshot: viewStateSnapshot, loadType: nil ) } public func indexPath(forInteractionUniqueId interactionUniqueId: String) -> IndexPath? { interactionIdToIndexPathMap[interactionUniqueId] } public var debugDescription: String { isEmptyInitialState ? "empty" : "[items: \(items.count)]" } }