150 lines
5.1 KiB
Swift
150 lines
5.1 KiB
Swift
//
|
|
// 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 {
|
|
|
|
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: DisappearingMessagesConfigurationRecord {
|
|
threadViewModel.disappearingMessagesConfiguration
|
|
}
|
|
|
|
let indexPathOfUnreadIndicator: IndexPath?
|
|
|
|
private let interactionIdToIndexPathMap: [String: IndexPath]
|
|
/// Maps each collapsed interaction's uniqueId to the uniqueId of its parent CollapseSetInteraction.
|
|
private let collapsedInteractionToParentUniqueIdMap: [String: String]
|
|
|
|
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 collapsedInteractionToParentUniqueIdMap = [String: String]()
|
|
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
|
|
}
|
|
if
|
|
item.interactionType == .collapseSet,
|
|
let collapseSet = item.interaction as? CollapseSetInteraction
|
|
{
|
|
for collapsed in collapseSet.collapsedInteractions {
|
|
collapsedInteractionToParentUniqueIdMap[collapsed.uniqueId] = collapseSet.uniqueId
|
|
}
|
|
}
|
|
}
|
|
self.indexPathOfUnreadIndicator = indexPathOfUnreadIndicator
|
|
self.interactionIdToIndexPathMap = interactionIdToIndexPathMap
|
|
self.collapsedInteractionToParentUniqueIdMap = collapsedInteractionToParentUniqueIdMap
|
|
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,
|
|
)
|
|
}
|
|
|
|
func indexPath(forInteractionUniqueId interactionUniqueId: String) -> IndexPath? {
|
|
interactionIdToIndexPathMap[interactionUniqueId]
|
|
}
|
|
|
|
/// Returns the uniqueId of the CollapseSetInteraction that contains
|
|
/// the given interaction if it is inside a collapse set.
|
|
func collapseSetUniqueId(forCollapsedInteractionId interactionId: String) -> String? {
|
|
collapsedInteractionToParentUniqueIdMap[interactionId]
|
|
}
|
|
|
|
var debugDescription: String {
|
|
isEmptyInitialState ? "empty" : "[items: \(items.count)]"
|
|
}
|
|
}
|