Tusker/Tusker/API/ReblogService.swift

118 lines
4.4 KiB
Swift

//
// ReblogService.swift
// Tusker
//
// Created by Shadowfacts on 10/8/22.
// Copyright © 2022 Shadowfacts. All rights reserved.
//
import UIKit
import Pachyderm
@MainActor
class ReblogService {
private let mastodonController: MastodonController
private let presenter: any TuskerNavigationDelegate
private let status: StatusMO
var hapticFeedback = true
var requireConfirmation = Preferences.shared.confirmBeforeReblog
init(status: StatusMO, mastodonController: MastodonController, presenter: any TuskerNavigationDelegate) {
self.status = status
self.mastodonController = mastodonController
self.presenter = presenter
}
func toggleReblog() async {
if !status.reblogged,
requireConfirmation {
presentConfirmationAlert()
} else {
await doToggleReblog(visibility: nil)
}
}
private func presentConfirmationAlert() {
let image: UIImage?
let reblogVisibilityActions: [CustomAlertController.MenuAction]
let maximumVisibility = status.visibility
if mastodonController.instanceFeatures.reblogVisibility {
image = UIImage(systemName: maximumVisibility.unfilledImageName)
reblogVisibilityActions = [Visibility.unlisted, .private].compactMap { visibility in
guard visibility < maximumVisibility else {
return nil
}
return CustomAlertController.MenuAction(title: "Reblog as \(visibility.displayName)", subtitle: visibility.subtitle, image: UIImage(systemName: visibility.unfilledImageName)) {
// deliberately retain a strong reference to self
Task {
await self.doToggleReblog(visibility: visibility)
}
}
}
} else {
image = nil
reblogVisibilityActions = []
}
let preview = ConfirmReblogStatusPreviewView(status: status)
var config = CustomAlertController.Configuration(title: "Are you sure you want to reblog this post?", content: preview, actions: [
CustomAlertController.Action(title: "Cancel", style: .cancel, handler: nil),
CustomAlertController.Action(title: "Reblog", image: image, style: .default, handler: {
// deliberately retain a strong reference to self
Task {
await self.doToggleReblog(visibility: nil)
}
})
])
if !reblogVisibilityActions.isEmpty {
var menuAction = CustomAlertController.Action(title: nil, image: UIImage(systemName: "chevron.down"), style: .menu(reblogVisibilityActions), handler: nil)
menuAction.isSecondaryMenu = true
config.actions.append(menuAction)
}
let alert = CustomAlertController(config: config)
presenter.present(alert, animated: true)
}
private func doToggleReblog(visibility: Visibility?) async {
let oldValue = status.reblogged
status.reblogged.toggle()
mastodonController.persistentContainer.statusSubject.send(status.id)
#if !os(visionOS)
if hapticFeedback {
UIImpactFeedbackGenerator(style: .light).impactOccurred()
}
#endif
let request: Request<Status>
if status.reblogged {
request = Status.reblog(status.id, visibility: visibility)
} else {
request = Status.unreblog(status.id)
}
do {
let (newStatus, _) = try await mastodonController.run(request)
mastodonController.persistentContainer.addOrUpdate(status: newStatus)
} catch {
status.favourited = oldValue
mastodonController.persistentContainer.statusSubject.send(status.id)
let title = oldValue ? "Error Unreblogging" : "Error Reblogging"
let config = ToastConfiguration(from: error, with: title, in: presenter) { toast in
toast.dismissToast(animated: true)
await self.toggleReblog()
}
presenter.showToast(configuration: config, animated: true)
#if !os(visionOS)
if hapticFeedback {
UINotificationFeedbackGenerator().notificationOccurred(.error)
}
#endif
}
}
}