metatext/Data Sources/IdentitiesDataSource.swift

128 lines
4.9 KiB
Swift
Raw Normal View History

2021-01-28 23:15:22 +00:00
// Copyright © 2021 Metabolist. All rights reserved.
import Combine
import UIKit
import ViewModels
enum IdentitiesSection: Hashable {
case add
case identities(String)
}
enum IdentitiesItem: Hashable {
case add
case identitiy(Identity)
}
final class IdentitiesDataSource: UITableViewDiffableDataSource<IdentitiesSection, IdentitiesItem> {
private let updateQueue =
DispatchQueue(label: "com.metabolist.metatext.identities-data-source.update-queue")
private var cancellables = Set<AnyCancellable>()
private let deleteAction: (Identity) -> Void
init(tableView: UITableView,
publisher: AnyPublisher<[Identity], Never>,
viewModelProvider: @escaping (Identity) -> IdentityViewModel,
deleteAction: @escaping (Identity) -> Void) {
self.deleteAction = deleteAction
tableView.register(UITableViewCell.self,
forCellReuseIdentifier: String(describing: UITableViewCell.self))
tableView.register(IdentityTableViewCell.self,
forCellReuseIdentifier: String(describing: IdentityTableViewCell.self))
super.init(tableView: tableView) { tableView, indexPath, item in
let cell = tableView.dequeueReusableCell(withIdentifier: item.cellReuseIdentifier,
for: indexPath)
switch item {
case .add:
var configuration = cell.defaultContentConfiguration()
configuration.text = NSLocalizedString("add", comment: "")
configuration.image = UIImage(systemName: "plus.circle.fill")
cell.contentConfiguration = configuration
case let .identitiy(identity):
let viewModel = viewModelProvider(identity)
(cell as? IdentityTableViewCell)?.viewModel = viewModel
cell.accessoryType = identity.id == viewModel.identityContext.identity.id ? .checkmark : .none
}
return cell
}
publisher
.sink { [weak self] in self?.update(identities: $0) }
.store(in: &cancellables)
}
override func apply(_ snapshot: NSDiffableDataSourceSnapshot<IdentitiesSection, IdentitiesItem>,
animatingDifferences: Bool = true,
completion: (() -> Void)? = nil) {
updateQueue.async {
super.apply(snapshot, animatingDifferences: animatingDifferences, completion: completion)
}
}
override func tableView(_ tableView: UITableView, titleForHeaderInSection section: Int) -> String? {
let currentSnapshot = snapshot()
let section = currentSnapshot.sectionIdentifiers[section]
if currentSnapshot.numberOfItems(inSection: section) > 0, case let .identities(title) = section {
return title
}
return nil
}
override func tableView(_ tableView: UITableView, canEditRowAt indexPath: IndexPath) -> Bool {
itemIdentifier(for: indexPath) != .add
}
override func tableView(_ tableView: UITableView,
commit editingStyle: UITableViewCell.EditingStyle,
forRowAt indexPath: IndexPath) {
guard editingStyle == .delete,
case let .identitiy(identity) = itemIdentifier(for: indexPath)
else { return }
deleteAction(identity)
}
}
private extension IdentitiesDataSource {
private func update(identities: [Identity]) {
var newSnapshot = NSDiffableDataSourceSnapshot<IdentitiesSection, IdentitiesItem>()
let sections = [
(section: IdentitiesSection.identities(NSLocalizedString("identities.accounts", comment: "")),
identities: identities.filter { $0.authenticated && !$0.pending }.map(IdentitiesItem.identitiy)),
(section: IdentitiesSection.identities(NSLocalizedString("identities.browsing", comment: "")),
identities: identities.filter { !$0.authenticated && !$0.pending }.map(IdentitiesItem.identitiy)),
(section: IdentitiesSection.identities(NSLocalizedString("identities.pending", comment: "")),
identities: identities.filter { $0.pending }.map(IdentitiesItem.identitiy))
]
.filter { !$0.identities.isEmpty }
newSnapshot.appendSections([.add] + sections.map(\.section))
newSnapshot.appendItems([.add], toSection: .add)
for section in sections {
newSnapshot.appendItems(section.identities, toSection: section.section)
}
apply(newSnapshot, animatingDifferences: !snapshot().sectionIdentifiers.filter { $0 != .add }.isEmpty)
}
}
private extension IdentitiesItem {
var cellReuseIdentifier: String {
switch self {
case .add:
return String(describing: UITableViewCell.self)
case .identitiy:
return String(describing: IdentityTableViewCell.self)
}
}
}