mastodon-ios/Mastodon/Scene/Root/Sidebar/SidebarViewModel.swift

192 lines
7.4 KiB
Swift
Raw Normal View History

2021-09-22 13:08:09 +02:00
//
// SidebarViewModel.swift
// Mastodon
//
// Created by Cirno MainasuK on 2021-9-22.
//
import UIKit
import Combine
import CoreData
import CoreDataStack
2021-09-24 13:58:50 +02:00
import Meta
import MastodonMeta
2021-09-22 13:08:09 +02:00
final class SidebarViewModel {
var disposeBag = Set<AnyCancellable>()
// input
let context: AppContext
// output
var diffableDataSource: UICollectionViewDiffableDataSource<Section, Item>!
init(context: AppContext) {
self.context = context
}
}
extension SidebarViewModel {
enum Section: Hashable, CaseIterable {
case tab
case account
}
enum Item: Hashable {
case tab(MainTabBarController.Tab)
case header(HeaderViewModel)
case account(AccountViewModel)
case addAccount
}
struct HeaderViewModel: Hashable {
let title: String
}
struct AccountViewModel: Hashable {
let authenticationObjectID: NSManagedObjectID
}
struct AddAccountViewModel: Hashable {
let id = UUID()
}
}
extension SidebarViewModel {
func setupDiffableDataSource(
collectionView: UICollectionView
) {
2021-09-24 13:58:50 +02:00
let tabCellRegistration = UICollectionView.CellRegistration<SidebarListCollectionViewCell, MainTabBarController.Tab> { (cell, indexPath, item) in
let imageURL: URL? = {
switch item {
case .me:
let authentication = self.context.authenticationService.activeMastodonAuthentication.value
return authentication?.user.avatarImageURL()
default:
return nil
}
}()
let headline: MetaContent = {
switch item {
case .me:
return PlaintextMetaContent(string: item.title)
// TODO:
// return PlaintextMetaContent(string: "Myself")
default:
return PlaintextMetaContent(string: item.title)
}
}()
cell.item = SidebarListContentView.Item(
image: item.sidebarImage,
imageURL: imageURL,
headline: headline,
subheadline: nil
)
cell.setNeedsUpdateConfiguration()
2021-09-22 13:08:09 +02:00
}
let headerRegistration = UICollectionView.CellRegistration<UICollectionViewListCell, HeaderViewModel> { (cell, indexPath, item) in
2021-09-24 13:58:50 +02:00
var content = UIListContentConfiguration.sidebarHeader()
2021-09-22 13:08:09 +02:00
content.text = item.title
cell.contentConfiguration = content
cell.accessories = [.outlineDisclosure()]
}
2021-09-24 13:58:50 +02:00
let accountRegistration = UICollectionView.CellRegistration<SidebarListCollectionViewCell, AccountViewModel> { (cell, indexPath, item) in
2021-09-22 13:08:09 +02:00
let authentication = AppContext.shared.managedObjectContext.object(with: item.authenticationObjectID) as! MastodonAuthentication
2021-09-24 13:58:50 +02:00
let user = authentication.user
let imageURL = user.avatarImageURL()
let headline: MetaContent = {
do {
let content = MastodonContent(content: user.displayNameWithFallback, emojis: user.emojiMeta)
return try MastodonMetaContent.convert(document: content)
} catch {
return PlaintextMetaContent(string: user.displayNameWithFallback)
}
}()
cell.item = SidebarListContentView.Item(
image: .placeholder(color: .systemFill),
imageURL: imageURL,
headline: headline,
subheadline: PlaintextMetaContent(string: "@" + user.acctWithDomain)
)
cell.setNeedsUpdateConfiguration()
2021-09-22 13:08:09 +02:00
}
let addAccountRegistration = UICollectionView.CellRegistration<UICollectionViewListCell, AddAccountViewModel> { (cell, indexPath, item) in
var content = cell.defaultContentConfiguration()
content.text = L10n.Scene.AccountList.addAccount
content.image = nil
cell.contentConfiguration = content
cell.accessories = []
}
diffableDataSource = UICollectionViewDiffableDataSource(collectionView: collectionView) { collectionView, indexPath, item in
switch item {
case .tab(let tab):
2021-09-24 13:58:50 +02:00
return collectionView.dequeueConfiguredReusableCell(using: tabCellRegistration, for: indexPath, item: tab)
2021-09-22 13:08:09 +02:00
case .header(let viewModel):
return collectionView.dequeueConfiguredReusableCell(using: headerRegistration, for: indexPath, item: viewModel)
case .account(let viewModel):
return collectionView.dequeueConfiguredReusableCell(using: accountRegistration, for: indexPath, item: viewModel)
case .addAccount:
return collectionView.dequeueConfiguredReusableCell(using: addAccountRegistration, for: indexPath, item: AddAccountViewModel())
}
}
var snapshot = NSDiffableDataSourceSnapshot<Section, Item>()
snapshot.appendSections(Section.allCases)
diffableDataSource.apply(snapshot)
for section in Section.allCases {
switch section {
case .tab:
var sectionSnapshot = NSDiffableDataSourceSectionSnapshot<Item>()
let items: [Item] = [
.tab(.home),
.tab(.search),
.tab(.notification),
.tab(.me),
]
sectionSnapshot.append(items, to: nil)
diffableDataSource.apply(sectionSnapshot, to: section)
case .account:
var sectionSnapshot = NSDiffableDataSourceSectionSnapshot<Item>()
let headerItem = Item.header(HeaderViewModel(title: "Accounts"))
sectionSnapshot.append([headerItem], to: nil)
sectionSnapshot.append([], to: headerItem)
sectionSnapshot.append([.addAccount], to: headerItem)
sectionSnapshot.expand([headerItem])
diffableDataSource.apply(sectionSnapshot, to: section)
}
}
context.authenticationService.mastodonAuthentications
.receive(on: DispatchQueue.main)
.sink { [weak self] authentications in
guard let self = self else { return }
2021-09-24 13:58:50 +02:00
// tab
var snapshot = self.diffableDataSource.snapshot()
snapshot.reloadItems([.tab(.me)])
self.diffableDataSource.apply(snapshot)
// account
var accountSectionSnapshot = NSDiffableDataSourceSectionSnapshot<Item>()
2021-09-22 13:08:09 +02:00
let headerItem = Item.header(HeaderViewModel(title: "Accounts"))
2021-09-24 13:58:50 +02:00
accountSectionSnapshot.append([headerItem], to: nil)
let accountItems = authentications.map { authentication in
2021-09-22 13:08:09 +02:00
Item.account(AccountViewModel(authenticationObjectID: authentication.objectID))
}
2021-09-24 13:58:50 +02:00
accountSectionSnapshot.append(accountItems, to: headerItem)
accountSectionSnapshot.append([.addAccount], to: headerItem)
accountSectionSnapshot.expand([headerItem])
self.diffableDataSource.apply(accountSectionSnapshot, to: .account)
2021-09-22 13:08:09 +02:00
}
.store(in: &disposeBag)
}
}