Tusker/Tusker/Screens/Explore/ExploreViewController.swift

229 lines
8.7 KiB
Swift

//
// ExploreViewController.swift
// Tusker
//
// Created by Shadowfacts on 12/14/19.
// Copyright © 2019 Shadowfacts. All rights reserved.
//
import UIKit
import Combine
import Pachyderm
class ExploreViewController: EnhancedTableViewController {
var dataSource: DataSource!
var resultsController: SearchResultsViewController!
var searchController: UISearchController!
init() {
super.init(style: .insetGrouped)
title = NSLocalizedString("Explore", comment: "explore tab title")
tabBarItem.image = UIImage(systemName: "magnifyingglass")
}
required init?(coder: NSCoder) {
fatalError("init(coder:) has not been implemented")
}
override func viewDidLoad() {
super.viewDidLoad()
tableView.register(UINib(nibName: "BasicTableViewCell", bundle: .main), forCellReuseIdentifier: "basicCell")
dataSource = DataSource(tableView: tableView, cellProvider: { (tableView, indexPath, item) -> UITableViewCell? in
let cell = tableView.dequeueReusableCell(withIdentifier: "basicCell", for: indexPath)
switch item {
case .bookmarks:
cell.imageView!.image = UIImage(systemName: "bookmark.fill")
cell.textLabel!.text = NSLocalizedString("Bookmarks", comment: "bookmarks nav item title")
cell.accessoryType = .disclosureIndicator
case let .list(list):
cell.imageView!.image = nil
cell.textLabel!.text = list.title
cell.accessoryType = .disclosureIndicator
case .addList:
cell.imageView!.image = UIImage(systemName: "plus")
cell.textLabel!.text = NSLocalizedString("New List...", comment: "new list nav item title")
cell.accessoryType = .none
}
return cell
})
dataSource.exploreController = self
var snapshot = NSDiffableDataSourceSnapshot<Section, Item>()
snapshot.appendSections([.bookmarks, .lists])
snapshot.appendItems([.bookmarks], toSection: .bookmarks)
snapshot.appendItems([.addList], toSection: .lists)
// the initial, static items should not be displayed with an animation
UIView.performWithoutAnimation {
dataSource.apply(snapshot)
}
resultsController = SearchResultsViewController()
resultsController.exploreNavigationController = self.navigationController!
searchController = UISearchController(searchResultsController: resultsController)
searchController.searchResultsUpdater = resultsController
searchController.searchBar.autocapitalizationType = .none
searchController.searchBar.delegate = resultsController
definesPresentationContext = true
navigationItem.searchController = searchController
navigationItem.hidesSearchBarWhenScrolling = false
reloadLists()
}
func reloadLists() {
let request = MastodonController.client.getLists()
MastodonController.client.run(request) { (response) in
guard case let .success(lists, _) = response else {
fatalError()
}
var snapshot = self.dataSource.snapshot()
snapshot.deleteSections([.lists])
snapshot.appendSections([.lists])
snapshot.appendItems(lists.map { .list($0) } + [.addList], toSection: .lists)
DispatchQueue.main.async {
self.dataSource.apply(snapshot)
}
}
}
// MARK: - Table view delegate
override func tableView(_ tableView: UITableView, didSelectRowAt indexPath: IndexPath) {
switch dataSource.itemIdentifier(for: indexPath) {
case nil:
return
case .bookmarks:
show(BookmarksTableViewController(), sender: nil)
case let .list(list):
show(ListTimelineViewController(for: list), sender: nil)
case .addList:
tableView.selectRow(at: nil, animated: true, scrollPosition: .none)
let alert = UIAlertController(title: NSLocalizedString("New List", comment: "new list alert title"), message: NSLocalizedString("Choose a title for your new list", comment: "new list alert message"), preferredStyle: .alert)
alert.addTextField(configurationHandler: nil)
alert.addAction(UIAlertAction(title: NSLocalizedString("Cancel", comment: "new list alert cancel button"), style: .cancel, handler: nil))
alert.addAction(UIAlertAction(title: NSLocalizedString("Create List", comment: "new list create button"), style: .default, handler: { (_) in
guard let title = alert.textFields?.first?.text else {
fatalError()
}
let request = MastodonController.client.createList(title: title)
MastodonController.client.run(request) { (response) in
guard case let .success(list, _) = response else { fatalError() }
self.reloadLists()
DispatchQueue.main.async {
self.show(ListTimelineViewController(for: list), sender: nil)
}
}
}))
present(alert, animated: true)
}
}
override func tableView(_ tableView: UITableView, editingStyleForRowAt indexPath: IndexPath) -> UITableViewCell.EditingStyle {
return .delete
}
}
extension ExploreViewController {
enum Section: CaseIterable {
case bookmarks
case lists
}
enum Item: Hashable {
case bookmarks
case list(List)
case addList
static func == (lhs: ExploreViewController.Item, rhs: ExploreViewController.Item) -> Bool {
switch (lhs, rhs) {
case (.bookmarks, .bookmarks):
return true
case let (.list(a), .list(b)):
return a.id == b.id
case (.addList, .addList):
return true
default:
return false
}
}
func hash(into hasher: inout Hasher) {
switch self {
case .bookmarks:
hasher.combine("bookmarks")
case let .list(list):
hasher.combine("list")
hasher.combine(list.id)
case .addList:
hasher.combine("addList")
}
}
}
class DataSource: UITableViewDiffableDataSource<Section, Item> {
weak var exploreController: ExploreViewController?
override func tableView(_ tableView: UITableView, titleForHeaderInSection section: Int) -> String? {
switch section {
case 1:
return NSLocalizedString("Lists", comment: "explore lists section title")
default:
return nil
}
}
override func tableView(_ tableView: UITableView, canEditRowAt indexPath: IndexPath) -> Bool {
if case .list(_) = itemIdentifier(for: indexPath) {
return true
}
return false
}
override func tableView(_ tableView: UITableView, commit editingStyle: UITableViewCell.EditingStyle, forRowAt indexPath: IndexPath) {
guard editingStyle == .delete,
case let .list(list) = itemIdentifier(for: indexPath) else {
return
}
let title = String(format: NSLocalizedString("Are you sure want to delete the '%@' list?", comment: "delete list alert title"), list.title)
let alert = UIAlertController(title: title, message: nil, preferredStyle: .alert)
alert.addAction(UIAlertAction(title: NSLocalizedString("Cancel", comment: "delete list alert cancel button"), style: .cancel, handler: nil))
alert.addAction(UIAlertAction(title: NSLocalizedString("Delete List", comment: "delete list alert confirm button"), style: .destructive, handler: { (_) in
let request = List.delete(list)
MastodonController.client.run(request) { (response) in
guard case .success(_, _) = response else {
fatalError()
}
var snapshot = self.snapshot()
snapshot.deleteItems([.list(list)])
DispatchQueue.main.async {
self.apply(snapshot)
}
}
}))
self.exploreController?.present(alert, animated: true)
}
}
}