diff --git a/ViewModels/Sources/ViewModels/AccountStatusesViewModel.swift b/ViewModels/Sources/ViewModels/AccountStatusesViewModel.swift index f98208c..9aa34d5 100644 --- a/ViewModels/Sources/ViewModels/AccountStatusesViewModel.swift +++ b/ViewModels/Sources/ViewModels/AccountStatusesViewModel.swift @@ -28,6 +28,18 @@ public class AccountStatusesViewModel: StatusListViewModel { .assign(to: &$accountViewModel) } + public override var collectionItems: AnyPublisher<[[CollectionItem]], Never> { + // The pinned key is added to the info of collection items in the first section + // so a diffable data source can potentially render it in both sections + super.collectionItems + .map { + $0.enumerated().map { + $0 == 0 ? $1.map { .init(id: $0.id, kind: $0.kind, info: [.pinned: true]) } : $1 + } + } + .eraseToAnyPublisher() + } + public override var navigationEvents: AnyPublisher { $accountViewModel.compactMap { $0 } .flatMap(\.events) @@ -50,10 +62,6 @@ public class AccountStatusesViewModel: StatusListViewModel { super.request(maxID: maxID, minID: minID) } - override func isPinned(status: Status) -> Bool { - collection == .statuses && items.first?.contains(CollectionItem(id: status.id, kind: .status)) ?? false - } - public override var title: AnyPublisher { $accountViewModel.map { $0?.accountName }.eraseToAnyPublisher() } diff --git a/ViewModels/Sources/ViewModels/Entities/CollectionItem.swift b/ViewModels/Sources/ViewModels/Entities/CollectionItem.swift index d22f8b8..c1627ec 100644 --- a/ViewModels/Sources/ViewModels/Entities/CollectionItem.swift +++ b/ViewModels/Sources/ViewModels/Entities/CollectionItem.swift @@ -3,6 +3,13 @@ public struct CollectionItem: Hashable { public let id: String public let kind: Kind + public let info: [InfoKey: AnyHashable] + + init(id: String, kind: Kind, info: [InfoKey: AnyHashable]? = nil) { + self.id = id + self.kind = kind + self.info = info ?? [InfoKey: AnyHashable]() + } } public extension CollectionItem { @@ -10,4 +17,8 @@ public extension CollectionItem { case status case account } + + enum InfoKey { + case pinned + } } diff --git a/ViewModels/Sources/ViewModels/StatusListViewModel.swift b/ViewModels/Sources/ViewModels/StatusListViewModel.swift index 599b596..4bb1e93 100644 --- a/ViewModels/Sources/ViewModels/StatusListViewModel.swift +++ b/ViewModels/Sources/ViewModels/StatusListViewModel.swift @@ -41,6 +41,8 @@ public class StatusListViewModel: ObservableObject { .store(in: &cancellables) } + public var collectionItems: AnyPublisher<[[CollectionItem]], Never> { $items.eraseToAnyPublisher() } + public var navigationEvents: AnyPublisher { navigationEventsSubject.eraseToAnyPublisher() } public var title: AnyPublisher { Just(statusListService.title).eraseToAnyPublisher() } @@ -55,13 +57,9 @@ public class StatusListViewModel: ObservableObject { .sink { _ in } .store(in: &cancellables) } - - func isPinned(status: Status) -> Bool { false } } extension StatusListViewModel: CollectionViewModel { - public var collectionItems: AnyPublisher<[[CollectionItem]], Never> { $items.eraseToAnyPublisher() } - public var alertItems: AnyPublisher { $alertItem.compactMap { $0 }.eraseToAnyPublisher() } public var loading: AnyPublisher { loadingSubject.eraseToAnyPublisher() } @@ -93,7 +91,7 @@ extension StatusListViewModel: CollectionViewModel { public func viewModel(item: CollectionItem) -> Any? { switch item.kind { case .status: - return statusViewModel(id: item.id) + return statusViewModel(item: item) default: return nil } @@ -111,8 +109,8 @@ private extension StatusListViewModel { var contextParentID: String? { statusListService.contextParentID } - func statusViewModel(id: String) -> StatusViewModel? { - guard let status = statuses[id] else { return nil } + func statusViewModel(item: CollectionItem) -> StatusViewModel? { + guard let status = statuses[item.id] else { return nil } var statusViewModel: StatusViewModel @@ -136,7 +134,7 @@ private extension StatusListViewModel { } statusViewModel.isContextParent = status.id == statusListService.contextParentID - statusViewModel.isPinned = isPinned(status: status) + statusViewModel.isPinned = item.info[.pinned] != nil statusViewModel.isReplyInContext = isReplyInContext(status: status) statusViewModel.hasReplyFollowing = hasReplyFollowing(status: status)