From 60bf3b2e3359e2c1d7b57c85f68cecb2b157455c Mon Sep 17 00:00:00 2001 From: Shadowfacts Date: Fri, 11 Nov 2022 18:16:44 -0500 Subject: [PATCH] Fix potential crash when deleting list --- Tusker.xcodeproj/project.pbxproj | 4 ++ Tusker/API/DeleteListService.swift | 65 +++++++++++++++++++ .../Explore/ExploreViewController.swift | 30 +++------ 3 files changed, 78 insertions(+), 21 deletions(-) create mode 100644 Tusker/API/DeleteListService.swift diff --git a/Tusker.xcodeproj/project.pbxproj b/Tusker.xcodeproj/project.pbxproj index 075de3e3..64c0b402 100644 --- a/Tusker.xcodeproj/project.pbxproj +++ b/Tusker.xcodeproj/project.pbxproj @@ -314,6 +314,7 @@ D6F6A54E291EF7E100F496A8 /* EditListSearchFollowingViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = D6F6A54D291EF7E100F496A8 /* EditListSearchFollowingViewController.swift */; }; D6F6A550291F058600F496A8 /* CreateListService.swift in Sources */ = {isa = PBXBuildFile; fileRef = D6F6A54F291F058600F496A8 /* CreateListService.swift */; }; D6F6A552291F098700F496A8 /* RenameListService.swift in Sources */ = {isa = PBXBuildFile; fileRef = D6F6A551291F098700F496A8 /* RenameListService.swift */; }; + D6F6A554291F0D9600F496A8 /* DeleteListService.swift in Sources */ = {isa = PBXBuildFile; fileRef = D6F6A553291F0D9600F496A8 /* DeleteListService.swift */; }; D6F953F021251A2900CF0F2B /* MastodonController.swift in Sources */ = {isa = PBXBuildFile; fileRef = D6F953EF21251A2900CF0F2B /* MastodonController.swift */; }; D6FF9860255C717400845181 /* AccountSwitchingContainerViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = D6FF985F255C717400845181 /* AccountSwitchingContainerViewController.swift */; }; /* End PBXBuildFile section */ @@ -683,6 +684,7 @@ D6F6A54D291EF7E100F496A8 /* EditListSearchFollowingViewController.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = EditListSearchFollowingViewController.swift; sourceTree = ""; }; D6F6A54F291F058600F496A8 /* CreateListService.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = CreateListService.swift; sourceTree = ""; }; D6F6A551291F098700F496A8 /* RenameListService.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = RenameListService.swift; sourceTree = ""; }; + D6F6A553291F0D9600F496A8 /* DeleteListService.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = DeleteListService.swift; sourceTree = ""; }; D6F953EF21251A2900CF0F2B /* MastodonController.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = MastodonController.swift; sourceTree = ""; }; D6FF985F255C717400845181 /* AccountSwitchingContainerViewController.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AccountSwitchingContainerViewController.swift; sourceTree = ""; }; /* End PBXFileReference section */ @@ -1494,6 +1496,7 @@ D621733228F1D5ED004C7DB1 /* ReblogService.swift */, D6F6A54F291F058600F496A8 /* CreateListService.swift */, D6F6A551291F098700F496A8 /* RenameListService.swift */, + D6F6A553291F0D9600F496A8 /* DeleteListService.swift */, ); path = API; sourceTree = ""; @@ -1994,6 +1997,7 @@ D6C1B2082545D1EC00DAAA66 /* StatusCardView.swift in Sources */, D64BC18A23C16487000D0238 /* UnpinStatusActivity.swift in Sources */, D64D8CA92463B494006B0BAA /* MultiThreadDictionary.swift in Sources */, + D6F6A554291F0D9600F496A8 /* DeleteListService.swift in Sources */, D68E6F5F253C9B2D001A1B4C /* BaseEmojiLabel.swift in Sources */, D6F0B12B24A3071C001E48C3 /* MainSplitViewController.swift in Sources */, D6AEBB3E2321638100E5038B /* UIActivity+Types.swift in Sources */, diff --git a/Tusker/API/DeleteListService.swift b/Tusker/API/DeleteListService.swift new file mode 100644 index 00000000..95e22c37 --- /dev/null +++ b/Tusker/API/DeleteListService.swift @@ -0,0 +1,65 @@ +// +// DeleteListService.swift +// Tusker +// +// Created by Shadowfacts on 11/11/22. +// Copyright © 2022 Shadowfacts. All rights reserved. +// + +import UIKit +import Pachyderm + +@MainActor +class DeleteListService { + private let list: List + private let mastodonController: MastodonController + private let present: (UIViewController) -> Void + + init(list: List, mastodonController: MastodonController, present: @escaping (UIViewController) -> Void) { + self.list = list + self.mastodonController = mastodonController + self.present = present + } + + @discardableResult + func run() async -> Bool { + if await presentConfirmationAlert() { + await deleteList() + return true + } else { + return false + } + } + + private func presentConfirmationAlert() async -> Bool { + await withCheckedContinuation { continuation in + let titleFormat = NSLocalizedString("Are you sure you want to delete the '%@' list?", comment: "delete list alert title") + let title = String(format: titleFormat, list.title) + let alert = UIAlertController(title: title, message: nil, preferredStyle: .alert) + alert.addAction(UIAlertAction(title: NSLocalizedString("Cancel", comment: "delete list alert cancel button"), style: .cancel, handler: { (_) in + continuation.resume(returning: false) + })) + alert.addAction(UIAlertAction(title: NSLocalizedString("Delete List", comment: "delete list alert confirm button"), style: .destructive, handler: { (_) in + continuation.resume(returning: true) + })) + present(alert) + } + } + + private func deleteList() async { + do { + let request = List.delete(list) + _ = try await mastodonController.run(request) + NotificationCenter.default.post(name: .listsChanged, object: nil) + } catch { + let alert = UIAlertController(title: "Error Deleting List", message: error.localizedDescription, preferredStyle: .alert) + alert.addAction(UIAlertAction(title: "Cancel", style: .cancel)) + alert.addAction(UIAlertAction(title: "Retry", style: .default, handler: { _ in + Task { + await self.deleteList() + } + })) + present(alert) + } + } +} diff --git a/Tusker/Screens/Explore/ExploreViewController.swift b/Tusker/Screens/Explore/ExploreViewController.swift index 6d24fb75..602ac7c9 100644 --- a/Tusker/Screens/Explore/ExploreViewController.swift +++ b/Tusker/Screens/Explore/ExploreViewController.swift @@ -274,29 +274,17 @@ class ExploreViewController: UIViewController, UICollectionViewDelegate { } private func deleteList(_ list: List, completion: @escaping (Bool) -> Void) { - let titleFormat = NSLocalizedString("Are you sure you want to delete the '%@' list?", comment: "delete list alert title") - let title = String(format: titleFormat, list.title) - let alert = UIAlertController(title: title, message: nil, preferredStyle: .alert) - alert.addAction(UIAlertAction(title: NSLocalizedString("Cancel", comment: "delete list alert cancel button"), style: .cancel, handler: { (_) in - completion(false) - })) - alert.addAction(UIAlertAction(title: NSLocalizedString("Delete List", comment: "delete list alert confirm button"), style: .destructive, handler: { (_) in - - let request = List.delete(list) - self.mastodonController.run(request) { (response) in - guard case .success(_, _) = response else { - fatalError() - } - - var snapshot = self.dataSource.snapshot() + Task { @MainActor in + let service = DeleteListService(list: list, mastodonController: mastodonController, present: { self.present($0, animated: true) }) + if await service.run() { + var snapshot = dataSource.snapshot() snapshot.deleteItems([.list(list)]) - DispatchQueue.main.async { - self.dataSource.apply(snapshot) - completion(true) - } + await dataSource.apply(snapshot) + completion(true) + } else { + completion(false) } - })) - present(alert, animated: true) + } } func removeSavedHashtag(_ hashtag: Hashtag) {