diff --git a/CHANGELOG.md b/CHANGELOG.md index 0416395749..77a73c4c42 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -10,7 +10,9 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 - Improved background syncing #833 - Update to Xcode 14. #839 +- Fixed the Share Database button in the debug settings. #918 - Fixed incorrect aspect ratio of avatar images. #753 +- Added a button to delete the SQL database in the debug settings. #738 ## [1.3.5] 2022-10-03 diff --git a/Shared/Extensions/UIViewController+Alert.swift b/Shared/Extensions/UIViewController+Alert.swift index e6176cb801..a1e3b6819b 100644 --- a/Shared/Extensions/UIViewController+Alert.swift +++ b/Shared/Extensions/UIViewController+Alert.swift @@ -20,15 +20,20 @@ protocol AlertRouter { extension UIViewController: AlertRouter { + @MainActor func alert(error: Error) { - let controller = UIAlertController(title: Localized.error.text, - message: error.localizedDescription, - preferredStyle: .alert) + let controller = UIAlertController( + title: Localized.error.text, + message: error.localizedDescription, + preferredStyle: .alert + ) controller.view.tintColor = UIColor.tint.system - let cancelAction = UIAlertAction(title: Localized.cancel.text, - style: .cancel) { _ in - controller.dismiss(animated: true) + let cancelAction = UIAlertAction( + title: Localized.cancel.text, + style: .cancel + ) { _ in + controller.dismiss(animated: true) } controller.addAction(cancelAction) @@ -45,11 +50,14 @@ extension UIViewController: AlertRouter { /// - message: A description shown below the title. /// - cancelTitle: A button that closes the alert. /// - cancelClosure: A closure that will be called with the alert is dismissed. - func alert(from sourceView: AnyObject? = nil, - title: String? = nil, - message: String, - cancelTitle: String = Localized.cancel.text, - cancelClosure: (() -> Void)? = nil) { + @MainActor + func alert( + from sourceView: AnyObject? = nil, + title: String? = nil, + message: String, + cancelTitle: String = Localized.cancel.text, + cancelClosure: (() -> Void)? = nil + ) { let cancel = UIAlertAction(title: cancelTitle, style: .cancel) { _ in cancelClosure?() @@ -61,6 +69,7 @@ extension UIViewController: AlertRouter { /// An async alternative to `alert(from:title:message:cancelTitle:cancelClosure)`. /// It's name includes async because otherwise Swift gets confused and thinks calls to the completion handler /// version are calls to the async version. + @MainActor func showAsyncAlert( from sourceView: AnyObject? = nil, title: String? = nil, @@ -88,17 +97,22 @@ extension UIViewController: AlertRouter { /// - cancelClosure: A closure that will be performed if the cancel button is pressed. /// - confirmTitle: The title of the confirmation button. /// - confirmClosure: A closure that will be performed if the confirmation button is pressed. - func confirm(from sourceView: UIView? = nil, - title: String? = nil, - message: String, - isDestructive: Bool = false, - cancelTitle: String = Localized.cancel.text, - cancelClosure: (() -> Void)? = nil, - confirmTitle: String = Localized.ok.text, - confirmClosure: @escaping (() -> Void)) { + @MainActor + func confirm( + from sourceView: UIView? = nil, + title: String? = nil, + message: String, + isDestructive: Bool = false, + cancelTitle: String = Localized.cancel.text, + cancelClosure: (() -> Void)? = nil, + confirmTitle: String = Localized.ok.text, + confirmClosure: @escaping (() -> Void) + ) { - let confirm = UIAlertAction(title: confirmTitle, - style: isDestructive ? .destructive : .default) { _ in + let confirm = UIAlertAction( + title: confirmTitle, + style: isDestructive ? .destructive : .default + ) { _ in confirmClosure() } @@ -150,15 +164,20 @@ extension UIViewController: AlertRouter { /// - sourceView: The view that the popover arrow should point to on large devices if you would like to display /// the choices as an `.actionSheet`. If this parameter is nil the choices will be displayed in a `.alert` /// style. Should be a subclass of either `UIView` or `UIBarButtonItem`. - func choose(from actions: [UIAlertAction], - title: String? = nil, - message: String? = nil, - sourceView: AnyObject? = nil) { + @MainActor + func choose( + from actions: [UIAlertAction], + title: String? = nil, + message: String? = nil, + sourceView: AnyObject? = nil + ) { let style: UIAlertController.Style = sourceView != nil ? .actionSheet : .alert - let controller = UIAlertController(title: title, - message: message, - preferredStyle: style) + let controller = UIAlertController( + title: title, + message: message, + preferredStyle: style + ) controller.view.tintColor = UIColor.tint.system for action in actions { controller.addAction(action) } self.present(alertController: controller, sourceView: sourceView) @@ -174,10 +193,13 @@ extension UIViewController: AlertRouter { /// - sourceRect: The rectangle in the coordinate space of sourceView that the popover should point at. This /// property is ignored if sourceView is a `UIBarButtonItem`. /// - animated: Whether or not the transition should be animated. - func present(alertController controller: UIAlertController, - sourceView: AnyObject? = nil, - sourceRect: CGRect? = nil, - animated: Bool = true) { + @MainActor + func present( + alertController controller: UIAlertController, + sourceView: AnyObject? = nil, + sourceRect: CGRect? = nil, + animated: Bool = true + ) { if controller.preferredStyle == .actionSheet && sourceView == nil { let errorMessage = "sourceView is required to present a popover controller" @@ -195,6 +217,7 @@ extension UIViewController: AlertRouter { /// `UIView` or `UIBarButtonItem` /// - Parameter rect: The rectangle in the coordinate space of sourceView that the popover should point at. This /// property is ignored if sourceView is a `UIBarButtonItem`. + @MainActor func configurePopover(from sourceView: AnyObject?, rect: CGRect? = nil) { guard let popover = self.popoverPresentationController, let sourceView = sourceView else { diff --git a/Source/Debug/DebugViewController.swift b/Source/Debug/DebugViewController.swift index eef6536bdc..44a734d3bd 100644 --- a/Source/Debug/DebugViewController.swift +++ b/Source/Debug/DebugViewController.swift @@ -307,10 +307,8 @@ class DebugViewController: DebugTableViewController { title: "Export database", cellReuseIdentifier: DebugValueTableViewCell.className, valueClosure: nil, - actionClosure: { [weak self] _ in - Task { - self?.shareDatabase(cell:) - } + actionClosure: { cell in + Task { await self.shareDatabase(cell: cell) } } ) ] @@ -467,8 +465,10 @@ class DebugViewController: DebugTableViewController { /// Allows the user to export the go-ssb log and SQLite database in a zip file. This function will zip up the files /// and present a share sheet as a popover on the given cell. - private func shareDatabase(cell: UITableViewCell) async { + @MainActor + func shareDatabase(cell: UITableViewCell) async { cell.showActivityIndicator() + cell.isUserInteractionEnabled = false let presentShareSheet = { [weak self] (activityItems: [Any]) in let activityController = UIActivityViewController( @@ -481,40 +481,36 @@ class DebugViewController: DebugTableViewController { self?.present(activityController, animated: true) } - let databaseDirectory = URL(fileURLWithPath: await Bots.current.statistics().repo.path).deletingLastPathComponent() + let databasePath = await Bots.current.statistics().repo.path + let databaseDirectory = URL(fileURLWithPath: databasePath).deletingLastPathComponent() let temporaryDirectory = URL(fileURLWithPath: NSTemporaryDirectory()) let url = temporaryDirectory.appendingPathComponent(UUID().uuidString) - DispatchQueue.global(qos: .background).async { [weak self] in - defer { - DispatchQueue.main.sync { - cell.hideActivityIndicator() - } - } - do { - try FileManager.default.createDirectory(at: url, withIntermediateDirectories: false) - let zipFileURL = temporaryDirectory.appendingPathComponent("\(UUID().uuidString).zip") - let destFileURL = url.appendingPathComponent(databaseDirectory.lastPathComponent) - try FileManager.default.copyItem(at: databaseDirectory, to: destFileURL) - - let coord = NSFileCoordinator() - var readError: NSError? - coord.coordinate(readingItemAt: url, options: .forUploading, error: &readError) { (zippedURL: URL) -> Void in - do { - try FileManager.default.copyItem(at: zippedURL, to: zipFileURL) - } catch { - DispatchQueue.main.async { [weak self] in - self?.alert(error: error) - } - } - DispatchQueue.main.async { - presentShareSheet([zipFileURL]) - } - } - } catch { - DispatchQueue.main.async { [weak self] in - self?.alert(error: error) + defer { + cell.hideActivityIndicator() + cell.isUserInteractionEnabled = false + } + do { + try FileManager.default.createDirectory(at: url, withIntermediateDirectories: false) + let zipFileURL = temporaryDirectory.appendingPathComponent("\(UUID().uuidString).zip") + let destFileURL = url.appendingPathComponent(databaseDirectory.lastPathComponent) + try FileManager.default.copyItem(at: databaseDirectory, to: destFileURL) + + let coord = NSFileCoordinator() + var readError: NSError? + coord.coordinate( + readingItemAt: url, + options: .forUploading, + error: &readError + ) { (zippedURL: URL) -> Void in + do { + try FileManager.default.copyItem(at: zippedURL, to: zipFileURL) + } catch { + self.alert(error: error) } + presentShareSheet([zipFileURL]) } + } catch { + self.alert(error: error) } }