2021-01-30 22:27:49 +00:00
|
|
|
// Copyright © 2021 Metabolist. All rights reserved.
|
|
|
|
|
|
|
|
import Combine
|
|
|
|
import Mastodon
|
|
|
|
import UIKit
|
|
|
|
import ViewModels
|
|
|
|
|
|
|
|
final class ExploreDataSource: UICollectionViewDiffableDataSource<ExploreViewModel.Section, ExploreViewModel.Item> {
|
|
|
|
private let updateQueue =
|
|
|
|
DispatchQueue(label: "com.metabolist.metatext.explore-data-source.update-queue")
|
2021-01-31 05:43:40 +00:00
|
|
|
private weak var collectionView: UICollectionView?
|
2021-01-30 22:27:49 +00:00
|
|
|
private var cancellables = Set<AnyCancellable>()
|
|
|
|
|
|
|
|
init(collectionView: UICollectionView, viewModel: ExploreViewModel) {
|
2021-01-31 05:43:40 +00:00
|
|
|
self.collectionView = collectionView
|
|
|
|
|
2022-05-06 03:11:08 +00:00
|
|
|
let tagRegistration = UICollectionView.CellRegistration<TagCollectionViewCell, TagViewModel> {
|
|
|
|
$0.viewModel = $2
|
|
|
|
}
|
|
|
|
|
|
|
|
let instanceRegistration = UICollectionView.CellRegistration<InstanceCollectionViewCell, InstanceViewModel> {
|
|
|
|
$0.viewModel = $2
|
|
|
|
}
|
|
|
|
|
|
|
|
let itemRegistration = UICollectionView.CellRegistration
|
|
|
|
<SeparatorConfiguredCollectionViewListCell, ExploreViewModel.Item> {
|
|
|
|
var configuration = $0.defaultContentConfiguration()
|
|
|
|
|
|
|
|
switch $2 {
|
|
|
|
case .profileDirectory:
|
|
|
|
configuration.text = NSLocalizedString("explore.profile-directory", comment: "")
|
|
|
|
configuration.image = UIImage(systemName: "person.crop.square.fill.and.at.rectangle")
|
|
|
|
default:
|
|
|
|
break
|
|
|
|
}
|
|
|
|
|
|
|
|
$0.contentConfiguration = configuration
|
|
|
|
$0.accessories = [.disclosureIndicator()]
|
|
|
|
}
|
|
|
|
|
2021-01-30 22:27:49 +00:00
|
|
|
super.init(collectionView: collectionView) {
|
|
|
|
switch $2 {
|
|
|
|
case let .tag(tag):
|
|
|
|
return $0.dequeueConfiguredReusableCell(
|
2022-05-06 03:11:08 +00:00
|
|
|
using: tagRegistration,
|
2021-01-30 22:27:49 +00:00
|
|
|
for: $1,
|
|
|
|
item: viewModel.viewModel(tag: tag))
|
2021-01-31 05:43:40 +00:00
|
|
|
case .instance:
|
|
|
|
return $0.dequeueConfiguredReusableCell(
|
2022-05-06 03:11:08 +00:00
|
|
|
using: instanceRegistration,
|
2021-01-31 05:43:40 +00:00
|
|
|
for: $1,
|
|
|
|
item: viewModel.instanceViewModel)
|
|
|
|
default:
|
2022-05-06 03:11:08 +00:00
|
|
|
return $0.dequeueConfiguredReusableCell(using: itemRegistration, for: $1, item: $2)
|
2021-01-30 22:27:49 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
let headerRegistration = UICollectionView.SupplementaryRegistration
|
2022-05-06 03:11:08 +00:00
|
|
|
<ExploreSectionHeaderView>(elementKind: UICollectionView.elementKindSectionHeader) { [weak self] in
|
2021-01-30 22:27:49 +00:00
|
|
|
$0.label.text = self?.snapshot().sectionIdentifiers[$2.section].displayName
|
|
|
|
}
|
|
|
|
|
|
|
|
supplementaryViewProvider = {
|
|
|
|
$0.dequeueConfiguredReusableSupplementary(using: headerRegistration, for: $2)
|
|
|
|
}
|
|
|
|
|
2021-01-31 05:43:40 +00:00
|
|
|
viewModel.$trends.combineLatest(viewModel.$instanceViewModel)
|
|
|
|
.sink { [weak self] in self?.update(tags: $0, instanceViewModel: $1) }
|
|
|
|
.store(in: &cancellables)
|
2021-01-30 22:27:49 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
override func apply(_ snapshot: NSDiffableDataSourceSnapshot<ExploreViewModel.Section, ExploreViewModel.Item>,
|
|
|
|
animatingDifferences: Bool = true,
|
|
|
|
completion: (() -> Void)? = nil) {
|
|
|
|
updateQueue.async {
|
|
|
|
super.apply(snapshot, animatingDifferences: animatingDifferences, completion: completion)
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2021-01-31 05:43:40 +00:00
|
|
|
private extension ExploreDataSource {
|
|
|
|
func update(tags: [Tag], instanceViewModel: InstanceViewModel?) {
|
2021-01-31 06:30:12 +00:00
|
|
|
var newsnapshot = NSDiffableDataSourceSnapshot<ExploreViewModel.Section, ExploreViewModel.Item>()
|
2021-01-31 05:43:40 +00:00
|
|
|
|
|
|
|
if !tags.isEmpty {
|
2021-01-31 06:30:12 +00:00
|
|
|
newsnapshot.appendSections([.trending])
|
|
|
|
newsnapshot.appendItems(tags.map(ExploreViewModel.Item.tag), toSection: .trending)
|
2021-01-31 05:43:40 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
if let instanceViewModel = instanceViewModel {
|
2021-01-31 06:30:12 +00:00
|
|
|
newsnapshot.appendSections([.instance])
|
|
|
|
newsnapshot.appendItems([.instance], toSection: .instance)
|
2021-01-31 05:43:40 +00:00
|
|
|
|
|
|
|
if instanceViewModel.instance.canShowProfileDirectory {
|
2021-01-31 06:30:12 +00:00
|
|
|
newsnapshot.appendItems([.profileDirectory], toSection: .instance)
|
2021-01-31 05:43:40 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
let wasEmpty = self.snapshot().itemIdentifiers.isEmpty
|
|
|
|
let contentOffset = collectionView?.contentOffset
|
|
|
|
|
2021-01-31 06:30:12 +00:00
|
|
|
apply(newsnapshot, animatingDifferences: false) {
|
2021-01-31 05:43:40 +00:00
|
|
|
if let contentOffset = contentOffset, !wasEmpty {
|
|
|
|
self.collectionView?.contentOffset = contentOffset
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2021-01-30 22:27:49 +00:00
|
|
|
private extension ExploreViewModel.Section {
|
|
|
|
var displayName: String {
|
|
|
|
switch self {
|
|
|
|
case .trending:
|
|
|
|
return NSLocalizedString("explore.trending", comment: "")
|
|
|
|
case .instance:
|
|
|
|
return NSLocalizedString("explore.instance", comment: "")
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|