179 lines
6.1 KiB
Swift
179 lines
6.1 KiB
Swift
//
|
|
// EditListSearchFollowingViewController.swift
|
|
// Tusker
|
|
//
|
|
// Created by Shadowfacts on 11/11/22.
|
|
// Copyright © 2022 Shadowfacts. All rights reserved.
|
|
//
|
|
|
|
import UIKit
|
|
import Pachyderm
|
|
|
|
class EditListSearchFollowingViewController: EnhancedTableViewController {
|
|
|
|
private let mastodonController: MastodonController
|
|
private let didSelectAccount: (String) -> Void
|
|
|
|
private var dataSource: UITableViewDiffableDataSource<Section, String>!
|
|
|
|
private var query: String?
|
|
private var accountIDs: [String] = []
|
|
private var nextRange: RequestRange?
|
|
|
|
init(mastodonController: MastodonController, didSelectAccount: @escaping (String) -> Void) {
|
|
self.mastodonController = mastodonController
|
|
self.didSelectAccount = didSelectAccount
|
|
|
|
super.init(style: .grouped)
|
|
}
|
|
|
|
required init?(coder: NSCoder) {
|
|
fatalError("init(coder:) has not been implemented")
|
|
}
|
|
|
|
override func viewDidLoad() {
|
|
super.viewDidLoad()
|
|
|
|
tableView.register(UINib(nibName: "AccountTableViewCell", bundle: .main), forCellReuseIdentifier: "accountCell")
|
|
dataSource = UITableViewDiffableDataSource(tableView: tableView) { tableView, indexPath, itemIdentifier in
|
|
let cell = tableView.dequeueReusableCell(withIdentifier: "accountCell", for: indexPath) as! AccountTableViewCell
|
|
cell.delegate = self
|
|
cell.updateUI(accountID: itemIdentifier)
|
|
return cell
|
|
}
|
|
}
|
|
|
|
override func viewWillAppear(_ animated: Bool) {
|
|
super.viewWillAppear(animated)
|
|
|
|
if dataSource.snapshot().numberOfItems == 0 {
|
|
Task {
|
|
await load()
|
|
}
|
|
}
|
|
}
|
|
|
|
override func tableView(_ tableView: UITableView, willDisplay cell: UITableViewCell, forRowAt indexPath: IndexPath) {
|
|
print("will display: \(indexPath)")
|
|
if indexPath.row == tableView.numberOfRows(inSection: indexPath.section) - 1 {
|
|
Task {
|
|
await load()
|
|
}
|
|
}
|
|
}
|
|
|
|
override func tableView(_ tableView: UITableView, didSelectRowAt indexPath: IndexPath) {
|
|
guard let id = dataSource.itemIdentifier(for: indexPath) else {
|
|
return
|
|
}
|
|
didSelectAccount(id)
|
|
}
|
|
|
|
private func load() async {
|
|
do {
|
|
let ownAccount = try await mastodonController.getOwnAccount()
|
|
let req = Account.getFollowing(ownAccount.id, range: nextRange ?? .default)
|
|
let (following, pagination) = try await mastodonController.run(req)
|
|
await withCheckedContinuation { continuation in
|
|
mastodonController.persistentContainer.addAll(accounts: following) {
|
|
continuation.resume()
|
|
}
|
|
}
|
|
accountIDs.append(contentsOf: following.lazy.map(\.id))
|
|
nextRange = pagination?.older
|
|
updateDataSource(appending: following.map(\.id))
|
|
} catch {
|
|
let config = ToastConfiguration(from: error, with: "Error Loading Following", in: self) { toast in
|
|
toast.dismissToast(animated: true)
|
|
await self.load()
|
|
}
|
|
self.showToast(configuration: config, animated: true)
|
|
}
|
|
}
|
|
|
|
private func updateDataSourceForQueryChanged() {
|
|
guard let query, !query.isEmpty else {
|
|
let snapshot = NSDiffableDataSourceSnapshot<Section, String>()
|
|
dataSource.apply(snapshot, animatingDifferences: true)
|
|
return
|
|
}
|
|
|
|
let ids = filterAccounts(ids: accountIDs, with: query)
|
|
|
|
var snapshot = dataSource.snapshot()
|
|
if snapshot.indexOfSection(.accounts) == nil {
|
|
snapshot.appendSections([.accounts])
|
|
} else {
|
|
snapshot.deleteItems(snapshot.itemIdentifiers(inSection: .accounts))
|
|
}
|
|
snapshot.appendItems(ids)
|
|
dataSource.apply(snapshot, animatingDifferences: true)
|
|
|
|
// if there aren't any results for the current query, try to load more
|
|
if ids.isEmpty {
|
|
Task {
|
|
await load()
|
|
}
|
|
}
|
|
}
|
|
|
|
private func updateDataSource(appending ids: [String]) {
|
|
guard let query, !query.isEmpty else {
|
|
let snapshot = NSDiffableDataSourceSnapshot<Section, String>()
|
|
dataSource.apply(snapshot, animatingDifferences: true)
|
|
return
|
|
}
|
|
|
|
let ids = filterAccounts(ids: ids, with: query)
|
|
|
|
var snapshot = dataSource.snapshot()
|
|
if snapshot.indexOfSection(.accounts) == nil {
|
|
snapshot.appendSections([.accounts])
|
|
}
|
|
let existing = snapshot.itemIdentifiers(inSection: .accounts)
|
|
snapshot.appendItems(ids.filter { !existing.contains($0) })
|
|
dataSource.apply(snapshot, animatingDifferences: true)
|
|
|
|
// if there aren't any results for the current query, try to load more
|
|
if ids.isEmpty {
|
|
Task {
|
|
await load()
|
|
}
|
|
}
|
|
}
|
|
|
|
private func filterAccounts(ids: [String], with query: String) -> [String] {
|
|
let req = AccountMO.fetchRequest()
|
|
req.predicate = NSPredicate(format: "id in %@", ids)
|
|
let accounts = try! mastodonController.persistentContainer.viewContext.fetch(req)
|
|
|
|
return accounts
|
|
.map { (account) -> (AccountMO, Bool) in
|
|
let displayNameMatch = FuzzyMatcher.match(pattern: query, str: account.displayNameWithoutCustomEmoji)
|
|
let usernameMatch = FuzzyMatcher.match(pattern: query, str: account.acct)
|
|
return (account, displayNameMatch.matched || usernameMatch.matched)
|
|
}
|
|
.filter(\.1)
|
|
.map(\.0.id)
|
|
}
|
|
|
|
func updateQuery(_ query: String) {
|
|
self.query = query
|
|
updateDataSourceForQueryChanged()
|
|
}
|
|
|
|
}
|
|
|
|
extension EditListSearchFollowingViewController {
|
|
enum Section {
|
|
case accounts
|
|
}
|
|
}
|
|
|
|
extension EditListSearchFollowingViewController: TuskerNavigationDelegate {
|
|
var apiController: MastodonController! { mastodonController }
|
|
}
|
|
|
|
extension EditListSearchFollowingViewController: MenuActionProvider {
|
|
}
|