2019-12-17 05:22:25 +00:00
|
|
|
//
|
|
|
|
// ExploreViewController.swift
|
|
|
|
// Tusker
|
|
|
|
//
|
|
|
|
// Created by Shadowfacts on 12/14/19.
|
|
|
|
// Copyright © 2019 Shadowfacts. All rights reserved.
|
|
|
|
//
|
|
|
|
|
|
|
|
import UIKit
|
|
|
|
import Combine
|
2019-12-17 23:48:29 +00:00
|
|
|
import Pachyderm
|
2022-05-11 02:57:46 +00:00
|
|
|
import CoreData
|
2022-07-09 15:45:27 +00:00
|
|
|
import WebURLFoundationExtras
|
2019-12-17 05:22:25 +00:00
|
|
|
|
2021-02-06 18:48:31 +00:00
|
|
|
class ExploreViewController: UIViewController, UICollectionViewDelegate {
|
2019-12-17 05:22:25 +00:00
|
|
|
|
2020-05-13 22:57:04 +00:00
|
|
|
weak var mastodonController: MastodonController!
|
2020-01-05 20:25:07 +00:00
|
|
|
|
2021-02-06 18:48:31 +00:00
|
|
|
private var collectionView: UICollectionView!
|
|
|
|
private var dataSource: UICollectionViewDiffableDataSource<Section, Item>!
|
2019-12-17 05:22:25 +00:00
|
|
|
|
2021-02-06 18:48:31 +00:00
|
|
|
private(set) var resultsController: SearchResultsViewController!
|
|
|
|
private(set) var searchController: UISearchController!
|
2019-12-17 05:22:25 +00:00
|
|
|
|
2020-06-30 02:21:03 +00:00
|
|
|
var searchControllerStatusOnAppearance: Bool? = nil
|
|
|
|
|
2020-01-05 20:25:07 +00:00
|
|
|
init(mastodonController: MastodonController) {
|
|
|
|
self.mastodonController = mastodonController
|
|
|
|
|
2021-02-06 18:48:31 +00:00
|
|
|
super.init(nibName: nil, bundle: nil)
|
2020-12-14 23:44:41 +00:00
|
|
|
|
2019-12-17 05:22:25 +00:00
|
|
|
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()
|
2019-12-18 02:18:32 +00:00
|
|
|
|
2021-02-06 18:48:31 +00:00
|
|
|
var configuration = UICollectionLayoutListConfiguration(appearance: .insetGrouped)
|
|
|
|
configuration.trailingSwipeActionsConfigurationProvider = self.trailingSwipeActionsForCell(at:)
|
|
|
|
configuration.headerMode = .supplementary
|
|
|
|
let layout = UICollectionViewCompositionalLayout.list(using: configuration)
|
|
|
|
collectionView = UICollectionView(frame: view.bounds, collectionViewLayout: layout)
|
|
|
|
collectionView.autoresizingMask = [.flexibleWidth, .flexibleHeight]
|
|
|
|
collectionView.delegate = self
|
|
|
|
collectionView.dragDelegate = self
|
|
|
|
view.addSubview(collectionView)
|
2019-12-17 05:22:25 +00:00
|
|
|
|
2021-02-06 18:48:31 +00:00
|
|
|
dataSource = createDataSource()
|
|
|
|
applyInitialSnapshot()
|
2020-01-20 16:48:47 +00:00
|
|
|
|
2021-02-08 00:52:59 +00:00
|
|
|
if mastodonController.instance == nil {
|
|
|
|
mastodonController.getOwnInstance(completion: self.ownInstanceLoaded(_:))
|
|
|
|
}
|
|
|
|
|
2020-01-05 20:25:07 +00:00
|
|
|
resultsController = SearchResultsViewController(mastodonController: mastodonController)
|
2019-12-17 05:22:25 +00:00
|
|
|
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
|
2019-12-17 23:48:29 +00:00
|
|
|
|
2019-12-20 02:20:29 +00:00
|
|
|
NotificationCenter.default.addObserver(self, selector: #selector(savedHashtagsChanged), name: .savedHashtagsChanged, object: nil)
|
2019-12-20 03:41:23 +00:00
|
|
|
NotificationCenter.default.addObserver(self, selector: #selector(savedInstancesChanged), name: .savedInstancesChanged, object: nil)
|
2022-11-11 22:51:23 +00:00
|
|
|
NotificationCenter.default.addObserver(self, selector: #selector(reloadLists), name: .listsChanged, object: nil)
|
2022-11-11 23:08:44 +00:00
|
|
|
NotificationCenter.default.addObserver(self, selector: #selector(listRenamed(_:)), name: .listRenamed, object: nil)
|
2022-04-01 23:23:49 +00:00
|
|
|
NotificationCenter.default.addObserver(self, selector: #selector(preferencesChanged), name: .preferencesChanged, object: nil)
|
2021-02-06 18:48:31 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
override func viewWillAppear(_ animated: Bool) {
|
|
|
|
super.viewWillAppear(animated)
|
2019-12-20 02:20:29 +00:00
|
|
|
|
2021-02-06 18:48:31 +00:00
|
|
|
// Can't use UICollectionViewController's builtin version of this because it requires
|
|
|
|
// the collection view layout be passed into the constructor. Swipe actions for list collection views
|
|
|
|
// are created by passing a closure to the layout's configuration. This closure needs to capture
|
|
|
|
// `self`, so it can't be passed into the super constructor.
|
|
|
|
if let indexPaths = collectionView.indexPathsForSelectedItems {
|
|
|
|
for indexPath in indexPaths {
|
|
|
|
collectionView.deselectItem(at: indexPath, animated: true)
|
|
|
|
}
|
|
|
|
}
|
2019-12-18 02:18:32 +00:00
|
|
|
}
|
|
|
|
|
2020-06-30 02:21:03 +00:00
|
|
|
override func viewDidAppear(_ animated: Bool) {
|
|
|
|
super.viewDidAppear(animated)
|
|
|
|
|
|
|
|
// this is a workaround for the issue that setting isActive on a search controller that is not visible
|
|
|
|
// does not cause it to automatically become active once it becomes visible
|
|
|
|
// see FB7814561
|
|
|
|
if let active = searchControllerStatusOnAppearance {
|
|
|
|
searchController.isActive = active
|
|
|
|
searchControllerStatusOnAppearance = nil
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2021-02-06 18:48:31 +00:00
|
|
|
private func createDataSource() -> UICollectionViewDiffableDataSource<Section, Item> {
|
|
|
|
let sectionHeaderCell = UICollectionView.SupplementaryRegistration<UICollectionViewListCell>(elementKind: UICollectionView.elementKindSectionHeader) { (headerView, collectionView, indexPath) in
|
|
|
|
let section = self.dataSource.snapshot().sectionIdentifiers[indexPath.section]
|
|
|
|
|
|
|
|
var config = headerView.defaultContentConfiguration()
|
|
|
|
config.text = section.label
|
|
|
|
headerView.contentConfiguration = config
|
|
|
|
}
|
|
|
|
|
|
|
|
let listCell = UICollectionView.CellRegistration<UICollectionViewListCell, Item> { (cell, indexPath, item) in
|
|
|
|
var config = cell.defaultContentConfiguration()
|
|
|
|
config.text = item.label
|
|
|
|
config.image = item.image
|
|
|
|
cell.contentConfiguration = config
|
|
|
|
|
|
|
|
switch item {
|
|
|
|
case .addList, .addSavedHashtag, .findInstance:
|
|
|
|
cell.accessories = []
|
|
|
|
default:
|
|
|
|
cell.accessories = [.disclosureIndicator()]
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
let dataSource = UICollectionViewDiffableDataSource<Section, Item>(collectionView: collectionView) { (collectionView, indexPath, item) in
|
|
|
|
return collectionView.dequeueConfiguredReusableCell(using: listCell, for: indexPath, item: item)
|
|
|
|
}
|
|
|
|
dataSource.supplementaryViewProvider = { (collectionView, elementKind, indexPath) in
|
|
|
|
if elementKind == UICollectionView.elementKindSectionHeader {
|
|
|
|
return collectionView.dequeueConfiguredReusableSupplementary(using: sectionHeaderCell, for: indexPath)
|
|
|
|
} else {
|
|
|
|
return nil
|
|
|
|
}
|
|
|
|
}
|
|
|
|
return dataSource
|
|
|
|
}
|
|
|
|
|
|
|
|
private func applyInitialSnapshot() {
|
|
|
|
var snapshot = NSDiffableDataSourceSnapshot<Section, Item>()
|
2021-02-08 00:52:59 +00:00
|
|
|
snapshot.appendSections(Section.allCases.filter { $0 != .discover })
|
2021-02-06 18:48:31 +00:00
|
|
|
snapshot.appendItems([.bookmarks], toSection: .bookmarks)
|
2022-11-15 02:17:08 +00:00
|
|
|
if mastodonController.instanceFeatures.trends,
|
2022-04-01 23:23:49 +00:00
|
|
|
!Preferences.shared.hideDiscover {
|
|
|
|
addDiscoverSection(to: &snapshot)
|
2021-02-08 00:52:59 +00:00
|
|
|
}
|
2021-02-06 18:48:31 +00:00
|
|
|
snapshot.appendItems([.addList], toSection: .lists)
|
2022-05-11 02:57:46 +00:00
|
|
|
let hashtags = fetchSavedHashtags().map {
|
|
|
|
Item.savedHashtag(Hashtag(name: $0.name, url: $0.url))
|
|
|
|
}
|
|
|
|
snapshot.appendItems(hashtags, toSection: .savedHashtags)
|
2021-02-06 18:48:31 +00:00
|
|
|
snapshot.appendItems([.addSavedHashtag], toSection: .savedHashtags)
|
2022-05-11 02:57:46 +00:00
|
|
|
let instances = fetchSavedInstances().map {
|
|
|
|
Item.savedInstance($0.url)
|
|
|
|
}
|
|
|
|
snapshot.appendItems(instances, toSection: .savedInstances)
|
2021-02-06 18:48:31 +00:00
|
|
|
snapshot.appendItems([.findInstance], toSection: .savedInstances)
|
|
|
|
dataSource.apply(snapshot, animatingDifferences: false)
|
|
|
|
|
|
|
|
reloadLists()
|
|
|
|
}
|
|
|
|
|
2022-04-01 23:23:49 +00:00
|
|
|
private func addDiscoverSection(to snapshot: inout NSDiffableDataSourceSnapshot<Section, Item>) {
|
|
|
|
snapshot.insertSections([.discover], afterSection: .bookmarks)
|
2022-04-02 17:18:14 +00:00
|
|
|
snapshot.appendItems([.trendingTags, .profileDirectory], toSection: .discover)
|
|
|
|
if mastodonController.instanceFeatures.trendingStatusesAndLinks {
|
|
|
|
snapshot.insertItems([.trendingStatuses], beforeItem: .trendingTags)
|
|
|
|
snapshot.insertItems([.trendingLinks], afterItem: .trendingTags)
|
|
|
|
}
|
2022-04-01 23:23:49 +00:00
|
|
|
}
|
|
|
|
|
2021-02-08 00:52:59 +00:00
|
|
|
private func ownInstanceLoaded(_ instance: Instance) {
|
|
|
|
var snapshot = self.dataSource.snapshot()
|
2022-11-15 02:17:08 +00:00
|
|
|
if mastodonController.instanceFeatures.trends,
|
2022-01-26 02:01:36 +00:00
|
|
|
!snapshot.sectionIdentifiers.contains(.discover) {
|
2021-02-08 00:52:59 +00:00
|
|
|
snapshot.insertSections([.discover], afterSection: .bookmarks)
|
|
|
|
snapshot.appendItems([.trendingTags, .profileDirectory], toSection: .discover)
|
|
|
|
}
|
|
|
|
self.dataSource.apply(snapshot)
|
|
|
|
}
|
|
|
|
|
2022-11-11 22:51:23 +00:00
|
|
|
@objc private func reloadLists() {
|
2020-01-05 19:00:39 +00:00
|
|
|
let request = Client.getLists()
|
2020-01-05 20:25:07 +00:00
|
|
|
mastodonController.run(request) { (response) in
|
2019-12-17 23:48:29 +00:00
|
|
|
guard case let .success(lists, _) = response else {
|
2021-02-06 18:48:31 +00:00
|
|
|
return
|
2019-12-17 23:48:29 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
var snapshot = self.dataSource.snapshot()
|
2019-12-20 02:20:29 +00:00
|
|
|
snapshot.deleteItems(snapshot.itemIdentifiers(inSection: .lists))
|
2021-02-06 18:48:31 +00:00
|
|
|
snapshot.appendItems(lists.map { .list($0) }, toSection: .lists)
|
|
|
|
snapshot.appendItems([.addList], toSection: .lists)
|
|
|
|
|
2019-12-17 23:48:29 +00:00
|
|
|
DispatchQueue.main.async {
|
|
|
|
self.dataSource.apply(snapshot)
|
|
|
|
}
|
|
|
|
}
|
2019-12-17 05:22:25 +00:00
|
|
|
}
|
2019-12-17 23:48:29 +00:00
|
|
|
|
2022-11-11 23:08:44 +00:00
|
|
|
@objc private func listRenamed(_ notification: Foundation.Notification) {
|
|
|
|
let list = notification.userInfo!["list"] as! List
|
|
|
|
var snapshot = dataSource.snapshot()
|
|
|
|
let existing = snapshot.itemIdentifiers(inSection: .lists).first(where: {
|
|
|
|
if case .list(let existingList) = $0, existingList.id == list.id {
|
|
|
|
return true
|
|
|
|
} else {
|
|
|
|
return false
|
|
|
|
}
|
|
|
|
})
|
|
|
|
if let existing {
|
|
|
|
snapshot.insertItems([.list(list)], afterItem: existing)
|
|
|
|
snapshot.deleteItems([existing])
|
|
|
|
dataSource.apply(snapshot)
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2022-05-11 02:57:46 +00:00
|
|
|
@MainActor
|
|
|
|
private func fetchSavedHashtags() -> [SavedHashtag] {
|
|
|
|
let req = SavedHashtag.fetchRequest()
|
2022-05-15 14:37:38 +00:00
|
|
|
req.sortDescriptors = [NSSortDescriptor(key: "name", ascending: true, selector: #selector(NSString.localizedCompare(_:)))]
|
2022-05-11 02:57:46 +00:00
|
|
|
do {
|
|
|
|
return try mastodonController.persistentContainer.viewContext.fetch(req)
|
|
|
|
} catch {
|
|
|
|
return []
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
@MainActor
|
|
|
|
private func fetchSavedInstances() -> [SavedInstance] {
|
|
|
|
let req = SavedInstance.fetchRequest()
|
|
|
|
req.sortDescriptors = [NSSortDescriptor(key: "url.host", ascending: true)]
|
|
|
|
do {
|
|
|
|
return try mastodonController.persistentContainer.viewContext.fetch(req)
|
|
|
|
} catch {
|
|
|
|
return []
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2021-02-06 18:48:31 +00:00
|
|
|
@objc private func savedHashtagsChanged() {
|
2019-12-20 02:20:29 +00:00
|
|
|
var snapshot = dataSource.snapshot()
|
|
|
|
snapshot.deleteItems(snapshot.itemIdentifiers(inSection: .savedHashtags))
|
2022-05-11 02:57:46 +00:00
|
|
|
let hashtags = fetchSavedHashtags().map {
|
|
|
|
Item.savedHashtag(Hashtag(name: $0.name, url: $0.url))
|
|
|
|
}
|
|
|
|
snapshot.appendItems(hashtags, toSection: .savedHashtags)
|
2021-02-06 18:48:31 +00:00
|
|
|
snapshot.appendItems([.addSavedHashtag], toSection: .savedHashtags)
|
2019-12-20 02:20:29 +00:00
|
|
|
dataSource.apply(snapshot)
|
|
|
|
}
|
|
|
|
|
2021-02-06 18:48:31 +00:00
|
|
|
@objc private func savedInstancesChanged() {
|
2019-12-20 03:41:23 +00:00
|
|
|
var snapshot = dataSource.snapshot()
|
|
|
|
snapshot.deleteItems(snapshot.itemIdentifiers(inSection: .savedInstances))
|
2022-05-11 02:57:46 +00:00
|
|
|
let instances = fetchSavedInstances().map {
|
|
|
|
Item.savedInstance($0.url)
|
|
|
|
}
|
|
|
|
snapshot.appendItems(instances, toSection: .savedInstances)
|
2021-02-06 18:48:31 +00:00
|
|
|
snapshot.appendItems([.findInstance], toSection: .savedInstances)
|
2019-12-20 03:41:23 +00:00
|
|
|
dataSource.apply(snapshot)
|
|
|
|
}
|
|
|
|
|
2022-04-01 23:23:49 +00:00
|
|
|
@objc private func preferencesChanged() {
|
|
|
|
var snapshot = dataSource.snapshot()
|
|
|
|
let hasSection = snapshot.sectionIdentifiers.contains(.discover)
|
|
|
|
let hide = Preferences.shared.hideDiscover
|
|
|
|
if hasSection && hide {
|
|
|
|
snapshot.deleteSections([.discover])
|
|
|
|
} else if !hasSection && !hide {
|
|
|
|
addDiscoverSection(to: &snapshot)
|
|
|
|
} else {
|
|
|
|
return
|
|
|
|
}
|
|
|
|
dataSource.apply(snapshot)
|
|
|
|
}
|
|
|
|
|
2021-02-06 18:48:31 +00:00
|
|
|
private func deleteList(_ list: List, completion: @escaping (Bool) -> Void) {
|
2022-11-11 23:16:44 +00:00
|
|
|
Task { @MainActor in
|
|
|
|
let service = DeleteListService(list: list, mastodonController: mastodonController, present: { self.present($0, animated: true) })
|
|
|
|
if await service.run() {
|
|
|
|
var snapshot = dataSource.snapshot()
|
2019-12-20 02:20:29 +00:00
|
|
|
snapshot.deleteItems([.list(list)])
|
2022-11-11 23:16:44 +00:00
|
|
|
await dataSource.apply(snapshot)
|
|
|
|
completion(true)
|
|
|
|
} else {
|
|
|
|
completion(false)
|
2019-12-20 02:20:29 +00:00
|
|
|
}
|
2022-11-11 23:16:44 +00:00
|
|
|
}
|
2019-12-20 02:20:29 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
func removeSavedHashtag(_ hashtag: Hashtag) {
|
2022-05-11 02:57:46 +00:00
|
|
|
let context = mastodonController.persistentContainer.viewContext
|
|
|
|
if let hashtag = try? context.fetch(SavedHashtag.fetchRequest(name: hashtag.name)).first {
|
|
|
|
context.delete(hashtag)
|
|
|
|
try! context.save()
|
|
|
|
}
|
2019-12-20 02:20:29 +00:00
|
|
|
}
|
|
|
|
|
2019-12-20 03:41:23 +00:00
|
|
|
func removeSavedInstance(_ instanceURL: URL) {
|
2022-05-11 02:57:46 +00:00
|
|
|
let context = mastodonController.persistentContainer.viewContext
|
|
|
|
if let instance = try? context.fetch(SavedInstance.fetchRequest(url: instanceURL)).first {
|
|
|
|
context.delete(instance)
|
|
|
|
try! context.save()
|
|
|
|
}
|
2019-12-20 03:41:23 +00:00
|
|
|
}
|
|
|
|
|
2021-02-06 18:48:31 +00:00
|
|
|
private func trailingSwipeActionsForCell(at indexPath: IndexPath) -> UISwipeActionsConfiguration? {
|
2022-05-11 02:44:23 +00:00
|
|
|
let title: String
|
2021-02-06 18:48:31 +00:00
|
|
|
let handler: UIContextualAction.Handler
|
|
|
|
switch dataSource.itemIdentifier(for: indexPath) {
|
|
|
|
case let .list(list):
|
2022-05-11 02:44:23 +00:00
|
|
|
title = NSLocalizedString("Delete", comment: "delete swipe action title")
|
2021-02-06 18:48:31 +00:00
|
|
|
handler = { (_, _, completion) in
|
|
|
|
self.deleteList(list, completion: completion)
|
|
|
|
}
|
|
|
|
|
|
|
|
case let .savedHashtag(hashtag):
|
2022-05-11 02:44:23 +00:00
|
|
|
title = NSLocalizedString("Unsave", comment: "unsave swipe action title")
|
2021-02-06 18:48:31 +00:00
|
|
|
handler = { (_, _, completion) in
|
|
|
|
self.removeSavedHashtag(hashtag)
|
|
|
|
completion(true)
|
|
|
|
}
|
|
|
|
|
|
|
|
case let .savedInstance(url):
|
2022-05-11 02:44:23 +00:00
|
|
|
title = NSLocalizedString("Unsave", comment: "unsave swipe action title")
|
2021-02-06 18:48:31 +00:00
|
|
|
handler = { (_, _, completion) in
|
|
|
|
self.removeSavedInstance(url)
|
|
|
|
completion(true)
|
|
|
|
}
|
|
|
|
|
|
|
|
default:
|
|
|
|
return nil
|
|
|
|
}
|
|
|
|
|
|
|
|
return UISwipeActionsConfiguration(actions: [
|
2022-05-11 02:44:23 +00:00
|
|
|
UIContextualAction(style: .destructive, title: title, handler: handler)
|
2021-02-06 18:48:31 +00:00
|
|
|
])
|
|
|
|
}
|
|
|
|
|
|
|
|
// MARK: - Collection View Delegate
|
2019-12-17 05:22:25 +00:00
|
|
|
|
2021-02-06 18:48:31 +00:00
|
|
|
func collectionView(_ collectionView: UICollectionView, didSelectItemAt indexPath: IndexPath) {
|
2019-12-17 05:22:25 +00:00
|
|
|
switch dataSource.itemIdentifier(for: indexPath) {
|
|
|
|
case nil:
|
|
|
|
return
|
|
|
|
|
|
|
|
case .bookmarks:
|
2020-01-05 20:25:07 +00:00
|
|
|
show(BookmarksTableViewController(mastodonController: mastodonController), sender: nil)
|
2019-12-17 05:22:25 +00:00
|
|
|
|
2022-04-02 14:39:03 +00:00
|
|
|
case .trendingStatuses:
|
|
|
|
show(TrendingStatusesViewController(mastodonController: mastodonController), sender: nil)
|
|
|
|
|
2021-02-06 19:54:35 +00:00
|
|
|
case .trendingTags:
|
|
|
|
show(TrendingHashtagsViewController(mastodonController: mastodonController), sender: nil)
|
|
|
|
|
2022-04-02 15:36:45 +00:00
|
|
|
case .trendingLinks:
|
|
|
|
show(TrendingLinksViewController(mastodonController: mastodonController), sender: nil)
|
|
|
|
|
2021-02-08 00:39:22 +00:00
|
|
|
case .profileDirectory:
|
|
|
|
show(ProfileDirectoryViewController(mastodonController: mastodonController), sender: nil)
|
|
|
|
|
2019-12-17 23:48:29 +00:00
|
|
|
case let .list(list):
|
2020-01-05 20:25:07 +00:00
|
|
|
show(ListTimelineViewController(for: list, mastodonController: mastodonController), sender: nil)
|
2019-12-18 02:18:32 +00:00
|
|
|
|
|
|
|
case .addList:
|
2021-02-06 18:48:31 +00:00
|
|
|
collectionView.deselectItem(at: indexPath, animated: true)
|
2022-11-11 22:51:23 +00:00
|
|
|
let service = CreateListService(mastodonController: mastodonController, present: { self.present($0, animated: true) }) { list in
|
|
|
|
let listTimelineController = ListTimelineViewController(for: list, mastodonController: self.mastodonController)
|
|
|
|
listTimelineController.presentEditOnAppear = true
|
|
|
|
self.show(listTimelineController, sender: nil)
|
|
|
|
}
|
|
|
|
service.run()
|
2019-12-20 02:20:29 +00:00
|
|
|
|
|
|
|
case let .savedHashtag(hashtag):
|
2020-01-05 20:25:07 +00:00
|
|
|
show(HashtagTimelineViewController(for: hashtag, mastodonController: mastodonController), sender: nil)
|
2019-12-20 02:20:29 +00:00
|
|
|
|
|
|
|
case .addSavedHashtag:
|
2021-02-06 18:48:31 +00:00
|
|
|
collectionView.deselectItem(at: indexPath, animated: true)
|
2020-01-05 20:25:07 +00:00
|
|
|
let navController = UINavigationController(rootViewController: AddSavedHashtagViewController(mastodonController: mastodonController))
|
2019-12-20 02:20:29 +00:00
|
|
|
present(navController, animated: true)
|
2019-12-20 03:41:23 +00:00
|
|
|
|
|
|
|
case let .savedInstance(url):
|
2020-01-20 16:48:47 +00:00
|
|
|
show(InstanceTimelineViewController(for: url, parentMastodonController: mastodonController), sender: nil)
|
2019-12-20 03:41:23 +00:00
|
|
|
|
|
|
|
case .findInstance:
|
2021-02-06 18:48:31 +00:00
|
|
|
collectionView.deselectItem(at: indexPath, animated: true)
|
2020-01-20 16:48:47 +00:00
|
|
|
let findController = FindInstanceViewController(parentMastodonController: mastodonController)
|
2019-12-20 03:41:23 +00:00
|
|
|
findController.instanceTimelineDelegate = self
|
|
|
|
let navController = UINavigationController(rootViewController: findController)
|
|
|
|
present(navController, animated: true)
|
2019-12-17 05:22:25 +00:00
|
|
|
}
|
|
|
|
}
|
2020-03-16 03:54:04 +00:00
|
|
|
|
2019-12-17 05:22:25 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
extension ExploreViewController {
|
|
|
|
enum Section: CaseIterable {
|
|
|
|
case bookmarks
|
2021-02-06 19:54:35 +00:00
|
|
|
case discover
|
2019-12-17 05:22:25 +00:00
|
|
|
case lists
|
2019-12-20 02:20:29 +00:00
|
|
|
case savedHashtags
|
2019-12-20 03:41:23 +00:00
|
|
|
case savedInstances
|
2021-02-06 18:48:31 +00:00
|
|
|
|
|
|
|
var label: String? {
|
|
|
|
switch self {
|
|
|
|
case .bookmarks:
|
|
|
|
return nil
|
2021-02-06 19:54:35 +00:00
|
|
|
case .discover:
|
|
|
|
return NSLocalizedString("Discover", comment: "discover section title")
|
2021-02-06 18:48:31 +00:00
|
|
|
case .lists:
|
|
|
|
return NSLocalizedString("Lists", comment: "explore lists section title")
|
|
|
|
case .savedHashtags:
|
|
|
|
return NSLocalizedString("Saved Hashtags", comment: "explore saved hashtags section title")
|
|
|
|
case .savedInstances:
|
|
|
|
return NSLocalizedString("Instance Timelines", comment: "explore instance timelines section title")
|
|
|
|
}
|
|
|
|
}
|
2019-12-17 05:22:25 +00:00
|
|
|
}
|
2021-02-06 18:48:31 +00:00
|
|
|
|
2019-12-17 05:22:25 +00:00
|
|
|
enum Item: Hashable {
|
|
|
|
case bookmarks
|
2022-04-02 14:39:03 +00:00
|
|
|
case trendingStatuses
|
2021-02-06 19:54:35 +00:00
|
|
|
case trendingTags
|
2022-04-02 15:36:45 +00:00
|
|
|
case trendingLinks
|
2021-02-08 00:39:22 +00:00
|
|
|
case profileDirectory
|
2019-12-17 23:48:29 +00:00
|
|
|
case list(List)
|
2019-12-18 02:18:32 +00:00
|
|
|
case addList
|
2019-12-20 02:20:29 +00:00
|
|
|
case savedHashtag(Hashtag)
|
|
|
|
case addSavedHashtag
|
2019-12-20 03:41:23 +00:00
|
|
|
case savedInstance(URL)
|
|
|
|
case findInstance
|
2021-02-06 18:48:31 +00:00
|
|
|
|
|
|
|
var label: String {
|
|
|
|
switch self {
|
|
|
|
case .bookmarks:
|
|
|
|
return NSLocalizedString("Bookmarks", comment: "bookmarks nav item title")
|
2022-04-02 14:39:03 +00:00
|
|
|
case .trendingStatuses:
|
|
|
|
return NSLocalizedString("Trending Posts", comment: "trending statuses nav item title")
|
2021-02-06 19:54:35 +00:00
|
|
|
case .trendingTags:
|
|
|
|
return NSLocalizedString("Trending Hashtags", comment: "trending hashtags nav item title")
|
2022-04-02 15:36:45 +00:00
|
|
|
case .trendingLinks:
|
|
|
|
return NSLocalizedString("Trending Links", comment: "trending links nav item title")
|
2021-02-08 00:39:22 +00:00
|
|
|
case .profileDirectory:
|
|
|
|
return NSLocalizedString("Profile Directory", comment: "profile directory nav item title")
|
2021-02-06 18:48:31 +00:00
|
|
|
case let .list(list):
|
|
|
|
return list.title
|
|
|
|
case .addList:
|
|
|
|
return NSLocalizedString("New List...", comment: "new list nav item title")
|
|
|
|
case let .savedHashtag(hashtag):
|
|
|
|
return hashtag.name
|
|
|
|
case .addSavedHashtag:
|
|
|
|
return NSLocalizedString("Save Hashtag...", comment: "save hashtag nav item title")
|
|
|
|
case let .savedInstance(url):
|
|
|
|
return url.host!
|
|
|
|
case .findInstance:
|
|
|
|
return NSLocalizedString("Find An Instance...", comment: "find instance nav item title")
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
var image: UIImage {
|
|
|
|
let name: String
|
|
|
|
switch self {
|
|
|
|
case .bookmarks:
|
|
|
|
name = "bookmark.fill"
|
2022-04-02 14:39:03 +00:00
|
|
|
case .trendingStatuses:
|
|
|
|
name = "doc.text.image"
|
2021-02-06 19:54:35 +00:00
|
|
|
case .trendingTags:
|
2022-04-02 15:36:45 +00:00
|
|
|
name = "number"
|
|
|
|
case .trendingLinks:
|
|
|
|
name = "link"
|
2021-02-08 00:39:22 +00:00
|
|
|
case .profileDirectory:
|
|
|
|
name = "person.2.fill"
|
2021-02-06 18:48:31 +00:00
|
|
|
case .list(_):
|
|
|
|
name = "list.bullet"
|
|
|
|
case .addList, .addSavedHashtag:
|
|
|
|
name = "plus"
|
|
|
|
case .savedHashtag(_):
|
|
|
|
name = "number"
|
|
|
|
case .savedInstance(_):
|
|
|
|
name = "globe"
|
|
|
|
case .findInstance:
|
|
|
|
name = "magnifyingglass"
|
|
|
|
}
|
|
|
|
return UIImage(systemName: name)!
|
|
|
|
}
|
|
|
|
|
|
|
|
static func == (lhs: Item, rhs: Item) -> Bool {
|
2019-12-17 23:48:29 +00:00
|
|
|
switch (lhs, rhs) {
|
|
|
|
case (.bookmarks, .bookmarks):
|
|
|
|
return true
|
2022-04-02 14:39:03 +00:00
|
|
|
case (.trendingStatuses, .trendingStatuses):
|
|
|
|
return true
|
2021-02-06 19:54:35 +00:00
|
|
|
case (.trendingTags, .trendingTags):
|
|
|
|
return true
|
2022-04-02 15:36:45 +00:00
|
|
|
case (.trendingLinks, .trendingLinks):
|
|
|
|
return true
|
2021-02-08 00:39:22 +00:00
|
|
|
case (.profileDirectory, .profileDirectory):
|
|
|
|
return true
|
2019-12-17 23:48:29 +00:00
|
|
|
case let (.list(a), .list(b)):
|
2022-11-11 23:08:44 +00:00
|
|
|
return a.id == b.id && a.title == b.title
|
2019-12-18 02:18:32 +00:00
|
|
|
case (.addList, .addList):
|
|
|
|
return true
|
2019-12-20 02:20:29 +00:00
|
|
|
case let (.savedHashtag(a), .savedHashtag(b)):
|
|
|
|
return a == b
|
|
|
|
case (.addSavedHashtag, .addSavedHashtag):
|
|
|
|
return true
|
2019-12-20 03:41:23 +00:00
|
|
|
case let (.savedInstance(a), .savedInstance(b)):
|
|
|
|
return a == b
|
|
|
|
case (.findInstance, .findInstance):
|
|
|
|
return true
|
2019-12-17 23:48:29 +00:00
|
|
|
default:
|
|
|
|
return false
|
|
|
|
}
|
|
|
|
}
|
2021-02-06 18:48:31 +00:00
|
|
|
|
2019-12-17 23:48:29 +00:00
|
|
|
func hash(into hasher: inout Hasher) {
|
|
|
|
switch self {
|
|
|
|
case .bookmarks:
|
|
|
|
hasher.combine("bookmarks")
|
2022-04-02 14:39:03 +00:00
|
|
|
case .trendingStatuses:
|
|
|
|
hasher.combine("trendingStatuses")
|
2021-02-06 19:54:35 +00:00
|
|
|
case .trendingTags:
|
|
|
|
hasher.combine("trendingTags")
|
2022-04-02 15:36:45 +00:00
|
|
|
case .trendingLinks:
|
|
|
|
hasher.combine("trendingLinks")
|
2021-02-08 00:39:22 +00:00
|
|
|
case .profileDirectory:
|
|
|
|
hasher.combine("profileDirectory")
|
2019-12-17 23:48:29 +00:00
|
|
|
case let .list(list):
|
|
|
|
hasher.combine("list")
|
|
|
|
hasher.combine(list.id)
|
2022-11-11 23:08:44 +00:00
|
|
|
hasher.combine(list.title)
|
2019-12-18 02:18:32 +00:00
|
|
|
case .addList:
|
|
|
|
hasher.combine("addList")
|
2019-12-20 02:20:29 +00:00
|
|
|
case let .savedHashtag(hashtag):
|
|
|
|
hasher.combine("savedHashtag")
|
|
|
|
hasher.combine(hashtag.name)
|
|
|
|
case .addSavedHashtag:
|
|
|
|
hasher.combine("addSavedHashtag")
|
2019-12-20 03:41:23 +00:00
|
|
|
case let .savedInstance(url):
|
|
|
|
hasher.combine("savedInstance")
|
|
|
|
hasher.combine(url)
|
|
|
|
case .findInstance:
|
|
|
|
hasher.combine("findInstance")
|
2019-12-17 23:48:29 +00:00
|
|
|
}
|
|
|
|
}
|
2019-12-17 05:22:25 +00:00
|
|
|
}
|
|
|
|
}
|
2019-12-20 03:41:23 +00:00
|
|
|
|
|
|
|
extension ExploreViewController: InstanceTimelineViewControllerDelegate {
|
|
|
|
func didSaveInstance(url: URL) {
|
|
|
|
dismiss(animated: true) {
|
2020-01-20 16:48:47 +00:00
|
|
|
self.show(InstanceTimelineViewController(for: url, parentMastodonController: self.mastodonController), sender: nil)
|
2019-12-20 03:41:23 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
func didUnsaveInstance(url: URL) {
|
|
|
|
dismiss(animated: true)
|
|
|
|
}
|
|
|
|
}
|
2020-12-14 23:44:41 +00:00
|
|
|
|
2021-02-06 18:48:31 +00:00
|
|
|
extension ExploreViewController: UICollectionViewDragDelegate {
|
|
|
|
func collectionView(_ collectionView: UICollectionView, itemsForBeginning session: UIDragSession, at indexPath: IndexPath) -> [UIDragItem] {
|
2020-12-14 23:44:41 +00:00
|
|
|
guard let item = dataSource.itemIdentifier(for: indexPath),
|
|
|
|
let accountID = mastodonController.accountInfo?.id else {
|
|
|
|
return []
|
|
|
|
}
|
2021-02-06 18:48:31 +00:00
|
|
|
|
2020-12-14 23:44:41 +00:00
|
|
|
let provider: NSItemProvider
|
|
|
|
switch item {
|
|
|
|
case .bookmarks:
|
2022-05-13 21:10:18 +00:00
|
|
|
let activity = UserActivityManager.bookmarksActivity()
|
|
|
|
activity.displaysAuxiliaryScene = true
|
|
|
|
provider = NSItemProvider(object: activity)
|
2020-12-14 23:44:41 +00:00
|
|
|
case let .list(list):
|
|
|
|
guard let activity = UserActivityManager.showTimelineActivity(timeline: .list(id: list.id), accountID: accountID) else { return [] }
|
2022-05-13 21:10:18 +00:00
|
|
|
activity.displaysAuxiliaryScene = true
|
2020-12-14 23:44:41 +00:00
|
|
|
provider = NSItemProvider(object: activity)
|
|
|
|
case let .savedHashtag(hashtag):
|
2022-07-09 15:45:27 +00:00
|
|
|
guard let url = URL(hashtag.url) else {
|
|
|
|
return []
|
|
|
|
}
|
|
|
|
provider = NSItemProvider(object: url as NSURL)
|
2020-12-14 23:44:41 +00:00
|
|
|
if let activity = UserActivityManager.showTimelineActivity(timeline: .tag(hashtag: hashtag.name), accountID: accountID) {
|
2022-05-13 21:10:18 +00:00
|
|
|
activity.displaysAuxiliaryScene = true
|
2020-12-14 23:44:41 +00:00
|
|
|
provider.registerObject(activity, visibility: .all)
|
|
|
|
}
|
|
|
|
case let .savedInstance(url):
|
|
|
|
provider = NSItemProvider(object: url as NSURL)
|
|
|
|
// todo: should dragging public timelines into new windows be supported?
|
2022-04-02 14:39:03 +00:00
|
|
|
default:
|
2020-12-14 23:44:41 +00:00
|
|
|
return []
|
|
|
|
}
|
|
|
|
return [UIDragItem(itemProvider: provider)]
|
|
|
|
}
|
|
|
|
}
|