diff --git a/Tusker.xcodeproj/project.pbxproj b/Tusker.xcodeproj/project.pbxproj index b1b13b81..a98112c2 100644 --- a/Tusker.xcodeproj/project.pbxproj +++ b/Tusker.xcodeproj/project.pbxproj @@ -75,6 +75,7 @@ D620483423D3801D008A63EF /* LinkTextView.swift in Sources */ = {isa = PBXBuildFile; fileRef = D620483323D3801D008A63EF /* LinkTextView.swift */; }; D620483623D38075008A63EF /* ContentTextView.swift in Sources */ = {isa = PBXBuildFile; fileRef = D620483523D38075008A63EF /* ContentTextView.swift */; }; D620483823D38190008A63EF /* StatusContentTextView.swift in Sources */ = {isa = PBXBuildFile; fileRef = D620483723D38190008A63EF /* StatusContentTextView.swift */; }; + D6210D762C0B924F009BB569 /* RemoveProfileSuggestionService.swift in Sources */ = {isa = PBXBuildFile; fileRef = D6210D752C0B924F009BB569 /* RemoveProfileSuggestionService.swift */; }; D621733328F1D5ED004C7DB1 /* ReblogService.swift in Sources */ = {isa = PBXBuildFile; fileRef = D621733228F1D5ED004C7DB1 /* ReblogService.swift */; }; D62275A824F1CA2800B82A16 /* ComposeReplyContentView.swift in Sources */ = {isa = PBXBuildFile; fileRef = D62275A724F1CA2800B82A16 /* ComposeReplyContentView.swift */; }; D623A53D2635F5590095BD04 /* StatusPollView.swift in Sources */ = {isa = PBXBuildFile; fileRef = D623A53C2635F5590095BD04 /* StatusPollView.swift */; }; @@ -506,6 +507,7 @@ D620483323D3801D008A63EF /* LinkTextView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = LinkTextView.swift; sourceTree = ""; }; D620483523D38075008A63EF /* ContentTextView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ContentTextView.swift; sourceTree = ""; }; D620483723D38190008A63EF /* StatusContentTextView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = StatusContentTextView.swift; sourceTree = ""; }; + D6210D752C0B924F009BB569 /* RemoveProfileSuggestionService.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = RemoveProfileSuggestionService.swift; sourceTree = ""; }; D621733228F1D5ED004C7DB1 /* ReblogService.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ReblogService.swift; sourceTree = ""; }; D62275A724F1CA2800B82A16 /* ComposeReplyContentView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ComposeReplyContentView.swift; sourceTree = ""; }; D623A53C2635F5590095BD04 /* StatusPollView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = StatusPollView.swift; sourceTree = ""; }; @@ -1757,6 +1759,7 @@ D6DD8FFC298495A8002AD3FD /* LogoutService.swift */, D6D79F252A0C8D2700AB2315 /* FetchStatusSourceService.swift */, D630C3CB2BC5FD4600208903 /* GetAuthorizationTokenService.swift */, + D6210D752C0B924F009BB569 /* RemoveProfileSuggestionService.swift */, ); path = API; sourceTree = ""; @@ -2362,6 +2365,7 @@ D698F46D2BD0B8310054DB14 /* AnnouncementsCollection.swift in Sources */, D61F75AF293AF50C00C0B37F /* EditedFilter.swift in Sources */, D691771929A7B8820054D7EF /* ProfileHeaderMovedOverlayView.swift in Sources */, + D6210D762C0B924F009BB569 /* RemoveProfileSuggestionService.swift in Sources */, D61F75B9293C15A000C0B37F /* ZeroHeightCollectionViewCell.swift in Sources */, D68232F72464F4FD00325FB8 /* ComposeDrawingViewController.swift in Sources */, 04586B4122B2FFB10021BD04 /* PreferencesView.swift in Sources */, diff --git a/Tusker/API/RemoveProfileSuggestionService.swift b/Tusker/API/RemoveProfileSuggestionService.swift new file mode 100644 index 00000000..5932c697 --- /dev/null +++ b/Tusker/API/RemoveProfileSuggestionService.swift @@ -0,0 +1,39 @@ +// +// RemoveProfileSuggestionService.swift +// Tusker +// +// Created by Shadowfacts on 6/1/24. +// Copyright © 2024 Shadowfacts. All rights reserved. +// + +import Foundation +import Pachyderm + +@MainActor +class RemoveProfileSuggestionService { + private let accountID: String + private let mastodonController: MastodonController + private let presenter: any TuskerNavigationDelegate + private let completionHandler: @MainActor () -> Void + + init(accountID: String, mastodonController: MastodonController, presenter: any TuskerNavigationDelegate, completionHandler: @MainActor @escaping () -> Void) { + self.accountID = accountID + self.mastodonController = mastodonController + self.presenter = presenter + self.completionHandler = completionHandler + } + + func run() async { + let req = Suggestion.remove(accountID: accountID) + do { + _ = try await mastodonController.run(req) + completionHandler() + } catch { + let config = ToastConfiguration(from: error, with: "Error Removing Suggestion", in: presenter) { toast in + toast.dismissToast(animated: true) + await self.run() + } + self.presenter.showToast(configuration: config, animated: true) + } + } +} diff --git a/Tusker/Screens/Explore/SuggestedProfilesViewController.swift b/Tusker/Screens/Explore/SuggestedProfilesViewController.swift index 8330d0cc..b9c07e77 100644 --- a/Tusker/Screens/Explore/SuggestedProfilesViewController.swift +++ b/Tusker/Screens/Explore/SuggestedProfilesViewController.swift @@ -184,7 +184,19 @@ extension SuggestedProfilesViewController: UICollectionViewDelegate { return UIContextMenuConfiguration { ProfileViewController(accountID: id, mastodonController: self.mastodonController) } actionProvider: { _ in - UIMenu(children: self.actionsForProfile(accountID: id, source: .view(cell))) + let dismiss = UIAction(title: "Remove Suggestion", image: UIImage(systemName: "trash"), attributes: .destructive) { [unowned self] _ in + let service = RemoveProfileSuggestionService(accountID: id, mastodonController: self.mastodonController, presenter: self) { [weak self] in + guard let self else { return } + var snapshot = self.dataSource.snapshot() + // the source here doesn't matter, since it's ignored by the equatable and hashable impls + snapshot.deleteItems([.account(id, .global)]) + self.dataSource.apply(snapshot, animatingDifferences: true) + } + Task { + await service.run() + } + } + return UIMenu(children: [UIMenu(options: .displayInline, children: [dismiss])] + self.actionsForProfile(accountID: id, source: .view(cell))) } } diff --git a/Tusker/Screens/Explore/TrendsViewController.swift b/Tusker/Screens/Explore/TrendsViewController.swift index d275cff7..e280d046 100644 --- a/Tusker/Screens/Explore/TrendsViewController.swift +++ b/Tusker/Screens/Explore/TrendsViewController.swift @@ -368,20 +368,14 @@ class TrendsViewController: UIViewController, CollectionViewController { @MainActor private func removeProfileSuggestion(accountID: String) async { - let req = Suggestion.remove(accountID: accountID) - do { - _ = try await mastodonController.run(req) - var snapshot = dataSource.snapshot() + let service = RemoveProfileSuggestionService(accountID: accountID, mastodonController: mastodonController, presenter: self) { [weak self] in + guard let self else { return } + var snapshot = self.dataSource.snapshot() // the source here doesn't matter, since it's ignored by the equatable and hashable impls snapshot.deleteItems([.account(accountID, .global)]) - await apply(snapshot: snapshot) - } catch { - let config = ToastConfiguration(from: error, with: "Error Removing Suggestion", in: self) { [unowned self] toast in - toast.dismissToast(animated: true) - _ = await self.removeProfileSuggestion(accountID: accountID) - } - self.showToast(configuration: config, animated: true) + self.dataSource.apply(snapshot, animatingDifferences: true) } + await service.run() } }