2022-11-22 20:21:36 +00:00
|
|
|
//
|
|
|
|
// StatusActionAccountListViewController.swift
|
|
|
|
// Tusker
|
|
|
|
//
|
2023-01-18 01:02:03 +00:00
|
|
|
// Created by Shadowfacts on 1/17/23.
|
|
|
|
// Copyright © 2023 Shadowfacts. All rights reserved.
|
2022-11-22 20:21:36 +00:00
|
|
|
//
|
|
|
|
|
|
|
|
import UIKit
|
|
|
|
import Pachyderm
|
|
|
|
|
2023-01-18 01:02:03 +00:00
|
|
|
class StatusActionAccountListViewController: UIViewController {
|
2022-11-22 20:21:36 +00:00
|
|
|
|
|
|
|
private let mastodonController: MastodonController
|
2023-01-18 01:02:03 +00:00
|
|
|
private let actionType: StatusActionAccountListViewController.ActionType
|
2022-11-22 20:21:36 +00:00
|
|
|
private let statusID: String
|
2022-12-03 23:21:49 +00:00
|
|
|
private let statusState: CollapseState
|
2022-11-22 20:21:36 +00:00
|
|
|
private var accountIDs: [String]?
|
|
|
|
|
|
|
|
/// If `true`, a warning will be shown below the account list describing that the total favs/reblogs may be innacurate.
|
2023-02-23 03:00:12 +00:00
|
|
|
var showInaccurateCountWarning = false
|
2022-11-22 20:21:36 +00:00
|
|
|
|
2023-01-18 01:02:03 +00:00
|
|
|
private var state: State = .unloaded {
|
|
|
|
didSet {
|
|
|
|
switch oldValue {
|
|
|
|
case .loading(let indicator):
|
|
|
|
indicator.removeFromSuperview()
|
|
|
|
case .displaying(let vc):
|
|
|
|
vc.removeViewAndController()
|
|
|
|
default:
|
|
|
|
break
|
|
|
|
}
|
|
|
|
|
|
|
|
switch state {
|
|
|
|
case .unloaded:
|
|
|
|
break
|
|
|
|
case .loading(let indicator):
|
|
|
|
indicator.translatesAutoresizingMaskIntoConstraints = false
|
|
|
|
view.addSubview(indicator)
|
|
|
|
NSLayoutConstraint.activate([
|
|
|
|
indicator.centerXAnchor.constraint(equalTo: view.safeAreaLayoutGuide.centerXAnchor),
|
|
|
|
indicator.centerYAnchor.constraint(equalTo: view.safeAreaLayoutGuide.centerYAnchor),
|
|
|
|
])
|
|
|
|
case .displaying(let vc):
|
2023-01-29 21:02:47 +00:00
|
|
|
vc.view.translatesAutoresizingMaskIntoConstraints = false
|
2023-01-18 01:02:03 +00:00
|
|
|
embedChild(vc)
|
|
|
|
case .notFound:
|
|
|
|
showStatusNotFound()
|
|
|
|
}
|
|
|
|
}
|
2022-11-22 20:21:36 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
|
|
Creates a new view controller showing the accounts that performed the given action on the given status.
|
|
|
|
|
|
|
|
- Parameter actionType The action that this VC is for.
|
|
|
|
- Parameter statusID The ID of the status to show.
|
|
|
|
- Parameter accountIDs The accounts that will be shown. If `nil` is passed, a request will be performed to load the accounts.
|
|
|
|
- Parameter mastodonController The `MastodonController` instance this view controller uses.
|
|
|
|
*/
|
2023-01-18 01:02:03 +00:00
|
|
|
init(actionType: StatusActionAccountListViewController.ActionType, statusID: String, statusState: CollapseState, accountIDs: [String]?, mastodonController: MastodonController) {
|
2022-11-22 20:21:36 +00:00
|
|
|
self.mastodonController = mastodonController
|
|
|
|
self.actionType = actionType
|
|
|
|
self.statusID = statusID
|
|
|
|
self.statusState = statusState
|
|
|
|
self.accountIDs = accountIDs
|
2023-01-18 01:02:03 +00:00
|
|
|
|
2022-11-22 20:21:36 +00:00
|
|
|
super.init(nibName: nil, bundle: nil)
|
2023-01-18 01:02:03 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
required init?(coder: NSCoder) {
|
|
|
|
fatalError("init(coder:) has not been implemented")
|
|
|
|
}
|
|
|
|
|
|
|
|
override func viewDidLoad() {
|
|
|
|
super.viewDidLoad()
|
2022-11-22 20:21:36 +00:00
|
|
|
|
|
|
|
switch actionType {
|
|
|
|
case .favorite:
|
|
|
|
title = NSLocalizedString("Favorited By", comment: "status favorited by accounts list title")
|
|
|
|
case .reblog:
|
|
|
|
title = NSLocalizedString("Reblogged By", comment: "status reblogged by accounts list title")
|
|
|
|
}
|
2023-01-18 01:02:03 +00:00
|
|
|
|
2023-02-03 04:02:11 +00:00
|
|
|
view.backgroundColor = .appBackground
|
2023-01-18 01:02:03 +00:00
|
|
|
|
|
|
|
NotificationCenter.default.addObserver(self, selector: #selector(handleStatusDeleted), name: .statusDeleted, object: nil)
|
2022-11-22 20:21:36 +00:00
|
|
|
}
|
|
|
|
|
2023-01-18 01:02:03 +00:00
|
|
|
override func viewWillAppear(_ animated: Bool) {
|
|
|
|
super.viewWillAppear(animated)
|
|
|
|
|
2023-01-18 19:56:13 +00:00
|
|
|
if case .unloaded = state {
|
|
|
|
Task {
|
|
|
|
await loadStatus()
|
|
|
|
}
|
2022-11-22 20:21:36 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2023-01-18 01:02:03 +00:00
|
|
|
@objc private func handleStatusDeleted(_ notification: Foundation.Notification) {
|
|
|
|
guard let userInfo = notification.userInfo,
|
|
|
|
let accountID = mastodonController.accountInfo?.id,
|
|
|
|
userInfo["accountID"] as? String == accountID,
|
|
|
|
let statusIDs = userInfo["statusIDs"] as? [String] else {
|
|
|
|
return
|
2022-11-22 20:21:36 +00:00
|
|
|
}
|
2023-01-18 01:02:03 +00:00
|
|
|
if statusIDs.contains(statusID) {
|
|
|
|
state = .notFound
|
2022-11-22 20:21:36 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2023-01-18 01:02:03 +00:00
|
|
|
// MARK: Loading
|
|
|
|
|
|
|
|
private func loadStatus() async {
|
|
|
|
@MainActor
|
|
|
|
func doLoadStatus() async -> StatusMO? {
|
|
|
|
switch await FetchStatusService(statusID: statusID, mastodonController: mastodonController).run() {
|
|
|
|
case .loaded(let status):
|
|
|
|
return mastodonController.persistentContainer.addOrUpdateOnViewContext(status: status)
|
|
|
|
case .notFound:
|
|
|
|
state = .notFound
|
|
|
|
return nil
|
|
|
|
case .error(let error):
|
|
|
|
self.showStatusError(error)
|
|
|
|
return nil
|
|
|
|
}
|
|
|
|
}
|
2022-11-22 20:21:36 +00:00
|
|
|
|
2023-01-18 01:02:03 +00:00
|
|
|
if let cached = mastodonController.persistentContainer.status(for: statusID) {
|
|
|
|
await statusLoaded(cached)
|
|
|
|
} else {
|
|
|
|
let indicator = UIActivityIndicatorView(style: .medium)
|
|
|
|
indicator.startAnimating()
|
|
|
|
state = .loading(indicator)
|
|
|
|
|
|
|
|
if let status = await doLoadStatus() {
|
|
|
|
await statusLoaded(status)
|
|
|
|
}
|
2022-11-22 20:21:36 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2023-01-18 01:02:03 +00:00
|
|
|
private func statusLoaded(_ status: StatusMO) async {
|
2023-01-18 19:56:13 +00:00
|
|
|
let vc = StatusActionAccountListCollectionViewController(statusID: statusID, actionType: actionType, mastodonController: mastodonController)
|
2023-02-23 03:00:12 +00:00
|
|
|
vc.addStatus(status, state: statusState, showInaccurateCountWarning: showInaccurateCountWarning)
|
2023-01-18 01:02:03 +00:00
|
|
|
if let accountIDs {
|
2023-01-18 19:56:13 +00:00
|
|
|
vc.setAccounts(accountIDs, animated: false)
|
2022-11-22 20:21:36 +00:00
|
|
|
}
|
2023-01-18 19:56:13 +00:00
|
|
|
state = .displaying(vc)
|
2022-11-22 20:21:36 +00:00
|
|
|
}
|
|
|
|
|
2023-01-18 01:02:03 +00:00
|
|
|
private func showStatusNotFound() {
|
|
|
|
let notFoundView = StatusNotFoundView(frame: .zero)
|
|
|
|
notFoundView.translatesAutoresizingMaskIntoConstraints = false
|
|
|
|
view.addSubview(notFoundView)
|
|
|
|
NSLayoutConstraint.activate([
|
|
|
|
notFoundView.leadingAnchor.constraint(equalToSystemSpacingAfter: view.safeAreaLayoutGuide.leadingAnchor, multiplier: 1),
|
|
|
|
view.safeAreaLayoutGuide.trailingAnchor.constraint(equalToSystemSpacingAfter: notFoundView.trailingAnchor, multiplier: 1),
|
|
|
|
notFoundView.centerYAnchor.constraint(equalTo: view.safeAreaLayoutGuide.centerYAnchor),
|
|
|
|
])
|
2022-11-22 20:21:36 +00:00
|
|
|
}
|
|
|
|
|
2023-01-18 01:02:03 +00:00
|
|
|
private func showStatusError(_ error: Client.Error) {
|
|
|
|
let config = ToastConfiguration(from: error, with: "Error Loading Status", in: self) { [weak self] toast in
|
|
|
|
toast.dismissToast(animated: true)
|
|
|
|
await self?.loadStatus()
|
2022-11-22 20:21:36 +00:00
|
|
|
}
|
2023-01-18 01:02:03 +00:00
|
|
|
self.showToast(configuration: config, animated: true)
|
2022-11-22 20:21:36 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
}
|
|
|
|
|
2023-01-18 01:02:03 +00:00
|
|
|
extension StatusActionAccountListViewController {
|
|
|
|
enum State {
|
|
|
|
case unloaded
|
|
|
|
case loading(UIActivityIndicatorView)
|
|
|
|
case displaying(StatusActionAccountListCollectionViewController)
|
|
|
|
case notFound
|
|
|
|
}
|
2022-11-22 20:21:36 +00:00
|
|
|
}
|
|
|
|
|
2023-01-18 01:02:03 +00:00
|
|
|
extension StatusActionAccountListViewController {
|
|
|
|
enum ActionType {
|
|
|
|
case favorite, reblog
|
2022-12-04 04:20:19 +00:00
|
|
|
}
|
2022-11-22 20:21:36 +00:00
|
|
|
}
|
|
|
|
|
2023-01-18 01:02:03 +00:00
|
|
|
extension StatusActionAccountListViewController: ToastableViewController {
|
|
|
|
var toastScrollView: UIScrollView? {
|
|
|
|
if case .displaying(let vc) = state {
|
|
|
|
return vc.toastScrollView
|
|
|
|
} else {
|
|
|
|
return nil
|
|
|
|
}
|
2022-11-22 20:21:36 +00:00
|
|
|
}
|
|
|
|
}
|