// // 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 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 } } }