// // BookmarksTableViewController.swift // Tusker // // Created by Shadowfacts on 12/15/19. // Copyright © 2019 Shadowfacts. All rights reserved. // import UIKit import Pachyderm class BookmarksTableViewController: EnhancedTableViewController { private let statusCell = "statusCell" let mastodonController: MastodonController var statuses: [(id: String, state: StatusState)] = [] { didSet { DispatchQueue.main.async { self.tableView.reloadData() } } } var newer: RequestRange? var older: RequestRange? init(mastodonController: MastodonController) { self.mastodonController = mastodonController super.init(style: .plain) title = NSLocalizedString("Bookmarks", comment: "bookmarks screen title") } required init?(coder: NSCoder) { fatalError("init(coder:) has not been implemented") } override func viewDidLoad() { super.viewDidLoad() tableView.rowHeight = UITableView.automaticDimension tableView.estimatedRowHeight = 140 tableView.register(UINib(nibName: "TimelineStatusTableViewCell", bundle: .main), forCellReuseIdentifier: statusCell) tableView.prefetchDataSource = self let request = Client.getBookmarks() mastodonController.run(request) { (response) in guard case let .success(statuses, pagination) = response else { fatalError() } self.mastodonController.cache.addAll(statuses: statuses) self.statuses.append(contentsOf: statuses.map { ($0.id, .unknown) }) self.newer = pagination?.newer self.older = pagination?.older } userActivity = UserActivityManager.bookmarksActivity() } // MARK: - Table view data source override func numberOfSections(in tableView: UITableView) -> Int { return 1 } override func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int { return statuses.count } override func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell { let cell = tableView.dequeueReusableCell(withIdentifier: statusCell, for: indexPath) as! TimelineStatusTableViewCell cell.delegate = self let (id, state) = statuses[indexPath.row] cell.updateUI(statusID: id, state: state) return cell } // MARK: - Table view delegate override func tableView(_ tableView: UITableView, willDisplay cell: UITableViewCell, forRowAt indexPath: IndexPath) { guard indexPath.row == statuses.count, let older = older else { return } let request = Client.getBookmarks(range: older) mastodonController.run(request) { (response) in guard case let .success(newStatuses, pagination) = response else { fatalError() } self.older = pagination?.older self.mastodonController.cache.addAll(statuses: newStatuses) self.statuses.append(contentsOf: newStatuses.map { ($0.id, .unknown) }) } } override func tableView(_ tableView: UITableView, canEditRowAt indexPath: IndexPath) -> Bool { return true } override func tableView(_ tableView: UITableView, leadingSwipeActionsConfigurationForRowAt indexPath: IndexPath) -> UISwipeActionsConfiguration? { return (tableView.cellForRow(at: indexPath) as? TableViewSwipeActionProvider)?.leadingSwipeActionsConfiguration() } override func tableView(_ tableView: UITableView, trailingSwipeActionsConfigurationForRowAt indexPath: IndexPath) -> UISwipeActionsConfiguration? { let cellConfig = (tableView.cellForRow(at: indexPath) as? TableViewSwipeActionProvider)?.trailingSwipeActionsConfiguration() guard let status = mastodonController.cache.status(for: statuses[indexPath.row].id) else { return cellConfig } let unbookmarkAction = UIContextualAction(style: .destructive, title: NSLocalizedString("Unbookmark", comment: "unbookmark action title")) { (action, view, completion) in let request = Status.unbookmark(status) self.mastodonController.run(request) { (response) in guard case let .success(newStatus, _) = response else { fatalError() } self.mastodonController.cache.add(status: newStatus) self.statuses.remove(at: indexPath.row) } } unbookmarkAction.image = UIImage(systemName: "bookmark.fill") let config: UISwipeActionsConfiguration if let cellConfig = cellConfig { config = UISwipeActionsConfiguration(actions: cellConfig.actions + [unbookmarkAction]) config.performsFirstActionWithFullSwipe = cellConfig.performsFirstActionWithFullSwipe } else { config = UISwipeActionsConfiguration(actions: [unbookmarkAction]) config.performsFirstActionWithFullSwipe = false } return config } override func getSuggestedContextMenuActions(tableView: UITableView, indexPath: IndexPath, point: CGPoint) -> [UIAction] { guard let status = mastodonController.cache.status(for: statuses[indexPath.row].id) else { return [] } return [ UIAction(title: NSLocalizedString("Unbookmark", comment: "unbookmark action title"), image: UIImage(systemName: "bookmark.fill"), identifier: .init("unbookmark"), discoverabilityTitle: nil, attributes: [], state: .off, handler: { (_) in let request = Status.unbookmark(status) self.mastodonController.run(request) { (response) in guard case let .success(newStatus, _) = response else { fatalError() } self.mastodonController.cache.add(status: newStatus) self.statuses.remove(at: indexPath.row) } }) ] } } extension BookmarksTableViewController: StatusTableViewCellDelegate { var apiController: MastodonController { mastodonController } func statusCellCollapsedStateChanged(_ cell: BaseStatusTableViewCell) { tableView.beginUpdates() tableView.endUpdates() } } extension BookmarksTableViewController: UITableViewDataSourcePrefetching { func tableView(_ tableView: UITableView, prefetchRowsAt indexPaths: [IndexPath]) { for indexPath in indexPaths { guard let status = mastodonController.cache.status(for: statuses[indexPath.row].id) else { continue } ImageCache.avatars.get(status.account.avatar, completion: nil) for attachment in status.attachments where attachment.kind == .image { ImageCache.attachments.get(attachment.url, completion: nil) } } } func tableView(_ tableView: UITableView, cancelPrefetchingForRowsAt indexPaths: [IndexPath]) { for indexPath in indexPaths { guard let status = mastodonController.cache.status(for: statuses[indexPath.row].id) else { continue } ImageCache.avatars.cancel(status.account.avatar) for attachment in status.attachments where attachment.kind == .image { ImageCache.attachments.cancel(attachment.url) } } } }