diff --git a/Tusker.xcodeproj/project.pbxproj b/Tusker.xcodeproj/project.pbxproj index d1376097..48db7a57 100644 --- a/Tusker.xcodeproj/project.pbxproj +++ b/Tusker.xcodeproj/project.pbxproj @@ -312,6 +312,7 @@ D6F2E966249E8BFD005846BB /* IssueReporterViewController.xib in Resources */ = {isa = PBXBuildFile; fileRef = D6F2E964249E8BFD005846BB /* IssueReporterViewController.xib */; }; D6F6A54C291EF6FE00F496A8 /* EditListSearchResultsContainerViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = D6F6A54B291EF6FE00F496A8 /* EditListSearchResultsContainerViewController.swift */; }; D6F6A54E291EF7E100F496A8 /* EditListSearchFollowingViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = D6F6A54D291EF7E100F496A8 /* EditListSearchFollowingViewController.swift */; }; + D6F6A550291F058600F496A8 /* CreateListService.swift in Sources */ = {isa = PBXBuildFile; fileRef = D6F6A54F291F058600F496A8 /* CreateListService.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 */ @@ -679,6 +680,7 @@ D6F2E964249E8BFD005846BB /* IssueReporterViewController.xib */ = {isa = PBXFileReference; lastKnownFileType = file.xib; path = IssueReporterViewController.xib; sourceTree = ""; }; D6F6A54B291EF6FE00F496A8 /* EditListSearchResultsContainerViewController.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = EditListSearchResultsContainerViewController.swift; sourceTree = ""; }; D6F6A54D291EF7E100F496A8 /* EditListSearchFollowingViewController.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = EditListSearchFollowingViewController.swift; sourceTree = ""; }; + D6F6A54F291F058600F496A8 /* CreateListService.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = CreateListService.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 */ @@ -1488,6 +1490,7 @@ D6E9CDA7281A427800BBC98E /* PostService.swift */, D61ABEFD28F1C92600B29151 /* FavoriteService.swift */, D621733228F1D5ED004C7DB1 /* ReblogService.swift */, + D6F6A54F291F058600F496A8 /* CreateListService.swift */, ); path = API; sourceTree = ""; @@ -1915,6 +1918,7 @@ D681E4D3246E2AFF0053414F /* MuteConversationActivity.swift in Sources */, D6969EA0240C8384002843CE /* EmojiLabel.swift in Sources */, D64BC18623C1253A000D0238 /* AssetPreviewViewController.swift in Sources */, + D6F6A550291F058600F496A8 /* CreateListService.swift in Sources */, D6BEA24B291C6A2B002F4D01 /* AlertWithData.swift in Sources */, D61ABEFE28F1C92600B29151 /* FavoriteService.swift in Sources */, D663626221360B1900C9CBA2 /* Preferences.swift in Sources */, diff --git a/Tusker/API/CreateListService.swift b/Tusker/API/CreateListService.swift new file mode 100644 index 00000000..8dbb7d8b --- /dev/null +++ b/Tusker/API/CreateListService.swift @@ -0,0 +1,70 @@ +// +// CreateListService.swift +// Tusker +// +// Created by Shadowfacts on 11/11/22. +// Copyright © 2022 Shadowfacts. All rights reserved. +// + +import UIKit +import Pachyderm + +@MainActor +class CreateListService { + private let mastodonController: MastodonController + private let present: (UIViewController) -> Void + private let didCreateList: (@MainActor (List) -> Void)? + + private var createAction: UIAlertAction? + + init(mastodonController: MastodonController, present: @escaping (UIViewController) -> Void, didCreateList: (@MainActor (List) -> Void)?) { + self.mastodonController = mastodonController + self.present = present + self.didCreateList = didCreateList + } + + func run() { + let alert = UIAlertController(title: NSLocalizedString("New List", comment: "new list alert title"), message: NSLocalizedString("Choose a title for your new list", comment: "new list alert message"), preferredStyle: .alert) + alert.addTextField { textField in + textField.addTarget(self, action: #selector(self.alertTextFieldValueChanged), for: .editingChanged) + } + alert.addAction(UIAlertAction(title: NSLocalizedString("Cancel", comment: "new list alert cancel button"), style: .cancel, handler: nil)) + createAction = UIAlertAction(title: NSLocalizedString("Create List", comment: "new list create button"), style: .default, handler: { (_) in + let textField = alert.textFields!.first! + let title = textField.text ?? "" + Task { + await self.createList(with: title) + } + }) + createAction!.isEnabled = false + alert.addAction(createAction!) + present(alert) + } + + @objc private func alertTextFieldValueChanged(_ textField: UITextField) { + createAction?.isEnabled = textField.text?.trimmingCharacters(in: .whitespacesAndNewlines).isEmpty == false + } + + private func createList(with title: String) async { + do { + let request = Client.createList(title: title) + let (list, _) = try await mastodonController.run(request) + NotificationCenter.default.post(name: .listsChanged, object: nil) + self.didCreateList?(list) + } catch { + let alert = UIAlertController(title: "Error Creating 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.createList(with: title) + } + })) + present(alert) + } + } + +} + +extension Foundation.Notification.Name { + static let listsChanged = Notification.Name("listsChanged") +} diff --git a/Tusker/Screens/Explore/ExploreViewController.swift b/Tusker/Screens/Explore/ExploreViewController.swift index d7cd8632..3aa81180 100644 --- a/Tusker/Screens/Explore/ExploreViewController.swift +++ b/Tusker/Screens/Explore/ExploreViewController.swift @@ -70,6 +70,7 @@ class ExploreViewController: UIViewController, UICollectionViewDelegate { NotificationCenter.default.addObserver(self, selector: #selector(savedHashtagsChanged), name: .savedHashtagsChanged, object: nil) NotificationCenter.default.addObserver(self, selector: #selector(savedInstancesChanged), name: .savedInstancesChanged, object: nil) + NotificationCenter.default.addObserver(self, selector: #selector(reloadLists), name: .listsChanged, object: nil) NotificationCenter.default.addObserver(self, selector: #selector(preferencesChanged), name: .preferencesChanged, object: nil) } @@ -178,7 +179,7 @@ class ExploreViewController: UIViewController, UICollectionViewDelegate { self.dataSource.apply(snapshot) } - private func reloadLists() { + @objc private func reloadLists() { let request = Client.getLists() mastodonController.run(request) { (response) in guard case let .success(lists, _) = response else { @@ -356,28 +357,12 @@ class ExploreViewController: UIViewController, UICollectionViewDelegate { case .addList: collectionView.deselectItem(at: indexPath, animated: true) - let alert = UIAlertController(title: NSLocalizedString("New List", comment: "new list alert title"), message: NSLocalizedString("Choose a title for your new list", comment: "new list alert message"), preferredStyle: .alert) - alert.addTextField(configurationHandler: nil) - alert.addAction(UIAlertAction(title: NSLocalizedString("Cancel", comment: "new list alert cancel button"), style: .cancel, handler: nil)) - alert.addAction(UIAlertAction(title: NSLocalizedString("Create List", comment: "new list create button"), style: .default, handler: { (_) in - guard let title = alert.textFields?.first?.text else { - fatalError() - } - - let request = Client.createList(title: title) - self.mastodonController.run(request) { (response) in - guard case let .success(list, _) = response else { fatalError() } - - self.reloadLists() - - DispatchQueue.main.async { - let listTimelineController = ListTimelineViewController(for: list, mastodonController: self.mastodonController) - listTimelineController.presentEditOnAppear = true - self.show(listTimelineController, sender: nil) - } - } - })) - present(alert, animated: true) + let service = CreateListService(mastodonController: mastodonController, present: { self.present($0, animated: true) }) { list in + let listTimelineController = ListTimelineViewController(for: list, mastodonController: self.mastodonController) + listTimelineController.presentEditOnAppear = true + self.show(listTimelineController, sender: nil) + } + service.run() case let .savedHashtag(hashtag): show(HashtagTimelineViewController(for: hashtag, mastodonController: mastodonController), sender: nil) diff --git a/Tusker/Screens/Main/MainSidebarViewController.swift b/Tusker/Screens/Main/MainSidebarViewController.swift index 817e1edc..53e48fe1 100644 --- a/Tusker/Screens/Main/MainSidebarViewController.swift +++ b/Tusker/Screens/Main/MainSidebarViewController.swift @@ -99,6 +99,7 @@ class MainSidebarViewController: UIViewController { NotificationCenter.default.addObserver(self, selector: #selector(reloadSavedHashtags), name: .savedHashtagsChanged, object: nil) NotificationCenter.default.addObserver(self, selector: #selector(reloadSavedInstances), name: .savedInstancesChanged, object: nil) + NotificationCenter.default.addObserver(self, selector: #selector(reloadLists), name: .listsChanged, object: nil) NotificationCenter.default.addObserver(self, selector: #selector(preferencesChanged), name: .preferencesChanged, object: nil) onViewDidLoad?() @@ -201,7 +202,7 @@ class MainSidebarViewController: UIViewController { } } - private func reloadLists() { + @objc private func reloadLists() { let request = Client.getLists() mastodonController.run(request) { [weak self] (response) in guard let self = self, case let .success(lists, _) = response else { return } @@ -297,28 +298,12 @@ class MainSidebarViewController: UIViewController { } } - // todo: deduplicate with ExploreViewController private func showAddList() { - let alert = UIAlertController(title: "New List", message: "Choose a title for your new list", preferredStyle: .alert) - alert.addTextField(configurationHandler: nil) - alert.addAction(UIAlertAction(title: "Cancel", style: .cancel, handler: nil)) - alert.addAction(UIAlertAction(title: "Create List", style: .default, handler: { (_) in - guard let title = alert.textFields?.first?.text else { - fatalError() - } - - let request = Client.createList(title: title) - self.mastodonController.run(request) { (response) in - guard case let .success(list, _) = response else { fatalError() } - - self.reloadLists() - - DispatchQueue.main.async { - self.sidebarDelegate?.sidebar(self, didSelectItem: .list(list)) - } - } - })) - present(alert, animated: true) + let service = CreateListService(mastodonController: mastodonController, present: { self.present($0, animated: true + ) }) { list in + self.sidebarDelegate?.sidebar(self, didSelectItem: .list(list)) + } + service.run() } // todo: deduplicate with ExploreViewController