forked from shadowfacts/Tusker
168 lines
6.6 KiB
Swift
168 lines
6.6 KiB
Swift
//
|
|
// NotificationLoadingViewController.swift
|
|
// Tusker
|
|
//
|
|
// Created by Shadowfacts on 4/12/24.
|
|
// Copyright © 2024 Shadowfacts. All rights reserved.
|
|
//
|
|
|
|
import UIKit
|
|
import Pachyderm
|
|
|
|
class NotificationLoadingViewController: UIViewController {
|
|
|
|
private let notificationID: String
|
|
private let mastodonController: MastodonController
|
|
|
|
init(notificationID: String, mastodonController: MastodonController) {
|
|
self.notificationID = notificationID
|
|
self.mastodonController = mastodonController
|
|
|
|
super.init(nibName: nil, bundle: nil)
|
|
}
|
|
|
|
required init?(coder: NSCoder) {
|
|
fatalError("init(coder:) has not been implemented")
|
|
}
|
|
|
|
override func viewDidLoad() {
|
|
super.viewDidLoad()
|
|
|
|
view.backgroundColor = .secondarySystemBackground
|
|
|
|
let indicator = UIActivityIndicatorView(style: .medium)
|
|
indicator.startAnimating()
|
|
indicator.translatesAutoresizingMaskIntoConstraints = false
|
|
view.addSubview(indicator)
|
|
NSLayoutConstraint.activate([
|
|
indicator.centerXAnchor.constraint(equalTo: view.safeAreaLayoutGuide.centerXAnchor),
|
|
indicator.centerYAnchor.constraint(equalTo: view.safeAreaLayoutGuide.centerYAnchor),
|
|
])
|
|
}
|
|
|
|
override func viewWillAppear(_ animated: Bool) {
|
|
super.viewWillAppear(animated)
|
|
|
|
Task {
|
|
let request = Client.getNotification(id: notificationID)
|
|
do {
|
|
let (notification, _) = try await mastodonController.run(request)
|
|
await withCheckedContinuation { continuation in
|
|
let container = mastodonController.persistentContainer
|
|
let context = container.viewContext
|
|
container.addAll(notifications: [notification], in: context) {
|
|
continuation.resume()
|
|
}
|
|
}
|
|
showNotification(notification)
|
|
} catch {
|
|
showLoadingError(error)
|
|
}
|
|
}
|
|
}
|
|
|
|
private func showNotification(_ notification: Pachyderm.Notification) {
|
|
let vc: UIViewController
|
|
switch notification.kind {
|
|
case .mention, .status, .poll, .update:
|
|
guard let statusID = notification.status?.id else {
|
|
showLoadingError(Error.missingStatus)
|
|
return
|
|
}
|
|
vc = ConversationViewController(for: statusID, state: .unknown, mastodonController: mastodonController)
|
|
case .reblog, .favourite:
|
|
guard let statusID = notification.status?.id else {
|
|
showLoadingError(Error.missingStatus)
|
|
return
|
|
}
|
|
let actionType = notification.kind == .reblog ? StatusActionAccountListViewController.ActionType.reblog : .favorite
|
|
vc = StatusActionAccountListViewController(actionType: actionType, statusID: statusID, statusState: .unknown, accountIDs: [notification.account.id], mastodonController: mastodonController)
|
|
case .emojiReaction:
|
|
guard let statusID = notification.status?.id else {
|
|
showLoadingError(Error.missingStatus)
|
|
return
|
|
}
|
|
guard let emoji = notification.emoji else {
|
|
showLoadingError(Error.unknownType)
|
|
return
|
|
}
|
|
let actionType = StatusActionAccountListViewController.ActionType.emojiReaction(emoji, notification.emojiURL)
|
|
vc = StatusActionAccountListViewController(actionType: actionType, statusID: statusID, statusState: .unknown, accountIDs: [notification.account.id], mastodonController: mastodonController)
|
|
case .follow:
|
|
vc = ProfileViewController(accountID: notification.account.id, mastodonController: mastodonController)
|
|
case .followRequest:
|
|
vc = FollowRequestNotificationViewController(notification: notification, mastodonController: mastodonController)
|
|
case .unknown:
|
|
showLoadingError(Error.unknownType)
|
|
return
|
|
}
|
|
guard let navigationController else {
|
|
fatalError("Don't know how to show notification VC outside of navigation controller: parent is \(parent?.description ?? "<nil>")")
|
|
}
|
|
navigationController.viewControllers[navigationController.viewControllers.count - 1] = vc
|
|
}
|
|
|
|
private func showLoadingError(_ error: any Swift.Error) {
|
|
let image = UIImageView(image: UIImage(systemName: "exclamationmark.triangle.fill")!)
|
|
image.tintColor = .secondaryLabel
|
|
image.contentMode = .scaleAspectFit
|
|
|
|
let title = UILabel()
|
|
title.textColor = .secondaryLabel
|
|
title.font = .preferredFont(forTextStyle: .title1).withTraits(.traitBold)!
|
|
title.adjustsFontForContentSizeCategory = true
|
|
title.text = "Couldn't Load Notification"
|
|
|
|
let subtitle = UILabel()
|
|
subtitle.textColor = .secondaryLabel
|
|
subtitle.font = .preferredFont(forTextStyle: .body)
|
|
subtitle.adjustsFontForContentSizeCategory = true
|
|
subtitle.numberOfLines = 0
|
|
subtitle.textAlignment = .center
|
|
if let error = error as? Error {
|
|
subtitle.text = error.localizedDescription
|
|
} else if let error = error as? Client.Error {
|
|
subtitle.text = error.localizedDescription
|
|
} else {
|
|
subtitle.text = error.localizedDescription
|
|
}
|
|
|
|
let stack = UIStackView(arrangedSubviews: [
|
|
image,
|
|
title,
|
|
subtitle,
|
|
])
|
|
stack.axis = .vertical
|
|
stack.alignment = .center
|
|
stack.spacing = 8
|
|
stack.isAccessibilityElement = true
|
|
stack.accessibilityLabel = "\(title.text!). \(subtitle.text!)"
|
|
|
|
stack.translatesAutoresizingMaskIntoConstraints = false
|
|
view.addSubview(stack)
|
|
NSLayoutConstraint.activate([
|
|
image.widthAnchor.constraint(equalToConstant: 64),
|
|
image.heightAnchor.constraint(equalToConstant: 64),
|
|
|
|
stack.leadingAnchor.constraint(equalToSystemSpacingAfter: view.safeAreaLayoutGuide.leadingAnchor, multiplier: 1),
|
|
view.safeAreaLayoutGuide.trailingAnchor.constraint(equalToSystemSpacingAfter: stack.trailingAnchor, multiplier: 1),
|
|
stack.centerYAnchor.constraint(equalTo: view.safeAreaLayoutGuide.centerYAnchor),
|
|
])
|
|
}
|
|
|
|
}
|
|
|
|
private enum Error: LocalizedError {
|
|
case missingStatus
|
|
case unknownType
|
|
|
|
var errorDescription: String? {
|
|
switch self {
|
|
case .missingStatus:
|
|
"Missing status for mention/status notification"
|
|
case .unknownType:
|
|
"Unknown notification type"
|
|
}
|
|
}
|
|
}
|