forked from shadowfacts/Tusker
Store lists on MastodonController
This commit is contained in:
parent
7178473f34
commit
ab47fa776e
|
@ -17,11 +17,12 @@ public class List: Decodable, Equatable, Hashable {
|
|||
}
|
||||
|
||||
public static func ==(lhs: List, rhs: List) -> Bool {
|
||||
return lhs.id == rhs.id
|
||||
return lhs.id == rhs.id && lhs.title == rhs.title
|
||||
}
|
||||
|
||||
public func hash(into hasher: inout Hasher) {
|
||||
hasher.combine(id)
|
||||
hasher.combine(title)
|
||||
}
|
||||
|
||||
public static func getAccounts(_ list: List, range: RequestRange = .default) -> Request<[Account]> {
|
||||
|
|
|
@ -49,7 +49,7 @@ class CreateListService {
|
|||
do {
|
||||
let request = Client.createList(title: title)
|
||||
let (list, _) = try await mastodonController.run(request)
|
||||
NotificationCenter.default.post(name: .listsChanged, object: nil)
|
||||
mastodonController.addedList(list)
|
||||
self.didCreateList?(list)
|
||||
} catch {
|
||||
let alert = UIAlertController(title: "Error Creating List", message: error.localizedDescription, preferredStyle: .alert)
|
||||
|
@ -64,7 +64,3 @@ class CreateListService {
|
|||
}
|
||||
|
||||
}
|
||||
|
||||
extension Foundation.Notification.Name {
|
||||
static let listsChanged = Notification.Name("listsChanged")
|
||||
}
|
||||
|
|
|
@ -50,7 +50,7 @@ class DeleteListService {
|
|||
do {
|
||||
let request = List.delete(list)
|
||||
_ = try await mastodonController.run(request)
|
||||
NotificationCenter.default.post(name: .listsChanged, object: nil)
|
||||
mastodonController.deletedList(list)
|
||||
} catch {
|
||||
let alert = UIAlertController(title: "Error Deleting List", message: error.localizedDescription, preferredStyle: .alert)
|
||||
alert.addAction(UIAlertAction(title: "Cancel", style: .cancel))
|
||||
|
|
|
@ -46,6 +46,7 @@ class MastodonController: ObservableObject {
|
|||
@Published private(set) var instance: Instance!
|
||||
@Published private(set) var nodeInfo: NodeInfo!
|
||||
@Published private(set) var instanceFeatures = InstanceFeatures()
|
||||
@Published private(set) var lists: [List] = []
|
||||
private(set) var customEmojis: [Emoji]?
|
||||
|
||||
private var pendingOwnInstanceRequestCallbacks = [(Result<Instance, Client.Error>) -> Void]()
|
||||
|
@ -119,6 +120,15 @@ class MastodonController: ObservableObject {
|
|||
})
|
||||
}
|
||||
|
||||
func initialize() async throws {
|
||||
async let ownAccount = try getOwnAccount()
|
||||
async let ownInstance = try getOwnInstance()
|
||||
|
||||
_ = try await (ownAccount, ownInstance)
|
||||
|
||||
loadLists()
|
||||
}
|
||||
|
||||
func getOwnAccount(completion: ((Result<Account, Client.Error>) -> Void)? = nil) {
|
||||
if account != nil {
|
||||
completion?(.success(account))
|
||||
|
@ -264,4 +274,53 @@ class MastodonController: ObservableObject {
|
|||
}
|
||||
}
|
||||
|
||||
private func loadLists() {
|
||||
let req = Client.getLists()
|
||||
run(req) { response in
|
||||
if case .success(let lists, _) = response {
|
||||
DispatchQueue.main.async {
|
||||
self.lists = lists.sorted(using: ListComparator())
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@MainActor
|
||||
func addedList(_ list: List) {
|
||||
var new = self.lists
|
||||
new.append(list)
|
||||
new.sort { $0.title < $1.title }
|
||||
self.lists = new
|
||||
}
|
||||
|
||||
@MainActor
|
||||
func deletedList(_ list: List) {
|
||||
self.lists.removeAll(where: { $0.id == list.id })
|
||||
}
|
||||
|
||||
@MainActor
|
||||
func renamedList(_ list: List) {
|
||||
var new = self.lists
|
||||
if let index = new.firstIndex(where: { $0.id == list.id }) {
|
||||
new[index] = list
|
||||
}
|
||||
new.sort(using: ListComparator())
|
||||
self.lists = new
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
private struct ListComparator: SortComparator {
|
||||
typealias Compared = List
|
||||
|
||||
var underlying = String.Comparator(options: .caseInsensitive)
|
||||
|
||||
var order: SortOrder {
|
||||
get { underlying.order }
|
||||
set { underlying.order = newValue }
|
||||
}
|
||||
|
||||
func compare(_ lhs: List, _ rhs: List) -> ComparisonResult {
|
||||
return underlying.compare(lhs.title, rhs.title)
|
||||
}
|
||||
}
|
||||
|
|
|
@ -49,7 +49,7 @@ class RenameListService {
|
|||
do {
|
||||
let req = List.update(list, title: title)
|
||||
let (list, _) = try await mastodonController.run(req)
|
||||
NotificationCenter.default.post(name: .listRenamed, object: list.id, userInfo: ["list": list])
|
||||
mastodonController.renamedList(list)
|
||||
} catch {
|
||||
let alert = UIAlertController(title: "Error Updating List", message: error.localizedDescription, preferredStyle: .alert)
|
||||
alert.addAction(UIAlertAction(title: "Cancel", style: .cancel))
|
||||
|
@ -63,7 +63,3 @@ class RenameListService {
|
|||
}
|
||||
|
||||
}
|
||||
|
||||
extension Foundation.Notification.Name {
|
||||
static let listRenamed = Notification.Name("listRenamed")
|
||||
}
|
||||
|
|
|
@ -42,9 +42,9 @@ class AuxiliarySceneDelegate: UIResponder, UIWindowSceneDelegate {
|
|||
|
||||
let controller = MastodonController.getForAccount(account)
|
||||
session.mastodonController = controller
|
||||
|
||||
controller.getOwnAccount()
|
||||
controller.getOwnInstance()
|
||||
Task {
|
||||
try? await controller.initialize()
|
||||
}
|
||||
|
||||
guard let rootVC = viewController(for: activity, mastodonController: controller) else {
|
||||
UIApplication.shared.requestSceneSessionDestruction(session, options: nil, errorHandler: nil)
|
||||
|
|
|
@ -50,8 +50,9 @@ class ComposeSceneDelegate: UIResponder, UIWindowSceneDelegate {
|
|||
}
|
||||
|
||||
session.mastodonController = controller
|
||||
controller.getOwnAccount()
|
||||
controller.getOwnInstance()
|
||||
Task {
|
||||
try? await controller.initialize()
|
||||
}
|
||||
|
||||
let composeVC = ComposeHostingController(draft: draft, mastodonController: controller)
|
||||
composeVC.delegate = self
|
||||
|
|
|
@ -203,8 +203,9 @@ class MainSceneDelegate: UIResponder, UIWindowSceneDelegate, TuskerSceneDelegate
|
|||
|
||||
func createAppUI() -> TuskerRootViewController {
|
||||
let mastodonController = window!.windowScene!.session.mastodonController!
|
||||
mastodonController.getOwnAccount()
|
||||
mastodonController.getOwnInstance()
|
||||
Task {
|
||||
try? await mastodonController.initialize()
|
||||
}
|
||||
|
||||
let split = MainSplitViewController(mastodonController: mastodonController)
|
||||
if UIDevice.current.userInterfaceIdiom == .phone,
|
||||
|
|
|
@ -24,6 +24,8 @@ class ExploreViewController: UIViewController, UICollectionViewDelegate {
|
|||
|
||||
var searchControllerStatusOnAppearance: Bool? = nil
|
||||
|
||||
private var listsCancellable: AnyCancellable?
|
||||
|
||||
init(mastodonController: MastodonController) {
|
||||
self.mastodonController = mastodonController
|
||||
|
||||
|
@ -70,9 +72,10 @@ 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(listRenamed(_:)), name: .listRenamed, object: nil)
|
||||
NotificationCenter.default.addObserver(self, selector: #selector(preferencesChanged), name: .preferencesChanged, object: nil)
|
||||
|
||||
listsCancellable = mastodonController.$lists
|
||||
.sink { [unowned self] in self.reloadLists($0) }
|
||||
}
|
||||
|
||||
override func viewWillAppear(_ animated: Bool) {
|
||||
|
@ -158,7 +161,7 @@ class ExploreViewController: UIViewController, UICollectionViewDelegate {
|
|||
snapshot.appendItems([.findInstance], toSection: .savedInstances)
|
||||
dataSource.apply(snapshot, animatingDifferences: false)
|
||||
|
||||
reloadLists()
|
||||
reloadLists(mastodonController.lists)
|
||||
}
|
||||
|
||||
private func addDiscoverSection(to snapshot: inout NSDiffableDataSourceSnapshot<Section, Item>) {
|
||||
|
@ -180,39 +183,13 @@ class ExploreViewController: UIViewController, UICollectionViewDelegate {
|
|||
self.dataSource.apply(snapshot)
|
||||
}
|
||||
|
||||
@objc private func reloadLists() {
|
||||
let request = Client.getLists()
|
||||
mastodonController.run(request) { (response) in
|
||||
guard case let .success(lists, _) = response else {
|
||||
return
|
||||
}
|
||||
|
||||
var snapshot = self.dataSource.snapshot()
|
||||
snapshot.deleteItems(snapshot.itemIdentifiers(inSection: .lists))
|
||||
snapshot.appendItems(lists.map { .list($0) }, toSection: .lists)
|
||||
snapshot.appendItems([.addList], toSection: .lists)
|
||||
|
||||
DispatchQueue.main.async {
|
||||
self.dataSource.apply(snapshot)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@objc private func listRenamed(_ notification: Foundation.Notification) {
|
||||
let list = notification.userInfo!["list"] as! List
|
||||
var snapshot = dataSource.snapshot()
|
||||
let existing = snapshot.itemIdentifiers(inSection: .lists).first(where: {
|
||||
if case .list(let existingList) = $0, existingList.id == list.id {
|
||||
return true
|
||||
} else {
|
||||
return false
|
||||
}
|
||||
})
|
||||
if let existing {
|
||||
snapshot.insertItems([.list(list)], afterItem: existing)
|
||||
snapshot.deleteItems([existing])
|
||||
dataSource.apply(snapshot)
|
||||
}
|
||||
private func reloadLists(_ lists: [List]) {
|
||||
var snapshot = self.dataSource.snapshot()
|
||||
snapshot.deleteItems(snapshot.itemIdentifiers(inSection: .lists))
|
||||
snapshot.appendItems(lists.map { .list($0) }, toSection: .lists)
|
||||
snapshot.appendItems([.addList], toSection: .lists)
|
||||
|
||||
self.dataSource.apply(snapshot)
|
||||
}
|
||||
|
||||
@MainActor
|
||||
|
|
|
@ -8,6 +8,7 @@
|
|||
|
||||
import UIKit
|
||||
import Pachyderm
|
||||
import Combine
|
||||
|
||||
class EditListAccountsViewController: EnhancedTableViewController {
|
||||
|
||||
|
@ -22,6 +23,8 @@ class EditListAccountsViewController: EnhancedTableViewController {
|
|||
var searchResultsController: EditListSearchResultsContainerViewController!
|
||||
var searchController: UISearchController!
|
||||
|
||||
private var listRenamedCancellable: AnyCancellable?
|
||||
|
||||
init(list: List, mastodonController: MastodonController) {
|
||||
self.list = list
|
||||
self.mastodonController = mastodonController
|
||||
|
@ -30,7 +33,13 @@ class EditListAccountsViewController: EnhancedTableViewController {
|
|||
|
||||
listChanged()
|
||||
|
||||
NotificationCenter.default.addObserver(self, selector: #selector(listRenamed(_:)), name: .listRenamed, object: list.id)
|
||||
listRenamedCancellable = mastodonController.$lists
|
||||
.compactMap { $0.first { $0.id == list.id } }
|
||||
.removeDuplicates(by: { $0.title == $1.title })
|
||||
.sink { [unowned self] in
|
||||
self.list = $0
|
||||
self.listChanged()
|
||||
}
|
||||
}
|
||||
|
||||
required init?(coder: NSCoder) {
|
||||
|
@ -88,12 +97,6 @@ class EditListAccountsViewController: EnhancedTableViewController {
|
|||
title = String(format: NSLocalizedString("Edit %@", comment: "edit list screen title"), list.title)
|
||||
}
|
||||
|
||||
@objc private func listRenamed(_ notification: Foundation.Notification) {
|
||||
let list = notification.userInfo!["list"] as! List
|
||||
self.list = list
|
||||
self.listChanged()
|
||||
}
|
||||
|
||||
func loadAccounts() async {
|
||||
do {
|
||||
let request = List.getAccounts(list)
|
||||
|
|
|
@ -8,6 +8,7 @@
|
|||
|
||||
import UIKit
|
||||
import Pachyderm
|
||||
import Combine
|
||||
|
||||
class ListTimelineViewController: TimelineViewController {
|
||||
|
||||
|
@ -15,6 +16,8 @@ class ListTimelineViewController: TimelineViewController {
|
|||
|
||||
var presentEditOnAppear = false
|
||||
|
||||
private var listRenamedCancellable: AnyCancellable?
|
||||
|
||||
init(for list: List, mastodonController: MastodonController) {
|
||||
self.list = list
|
||||
|
||||
|
@ -22,7 +25,13 @@ class ListTimelineViewController: TimelineViewController {
|
|||
|
||||
listChanged()
|
||||
|
||||
NotificationCenter.default.addObserver(self, selector: #selector(listRenamed(_:)), name: .listRenamed, object: list.id)
|
||||
listRenamedCancellable = mastodonController.$lists
|
||||
.compactMap { $0.first { $0.id == list.id } }
|
||||
.removeDuplicates(by: { $0.title == $1.title })
|
||||
.sink { [unowned self] in
|
||||
self.list = $0
|
||||
self.listChanged()
|
||||
}
|
||||
}
|
||||
|
||||
required init?(coder aDecoder: NSCoder) {
|
||||
|
@ -47,12 +56,6 @@ class ListTimelineViewController: TimelineViewController {
|
|||
title = list.title
|
||||
}
|
||||
|
||||
@objc private func listRenamed(_ notification: Foundation.Notification) {
|
||||
let list = notification.userInfo!["list"] as! List
|
||||
self.list = list
|
||||
self.listChanged()
|
||||
}
|
||||
|
||||
func presentEdit(animated: Bool) {
|
||||
let editListAccountsController = EditListAccountsViewController(list: list, mastodonController: mastodonController)
|
||||
editListAccountsController.navigationItem.leftBarButtonItem = UIBarButtonItem(barButtonSystemItem: .done, target: self, action: #selector(editListDoneButtonPressed))
|
||||
|
|
|
@ -8,6 +8,7 @@
|
|||
|
||||
import UIKit
|
||||
import Pachyderm
|
||||
import Combine
|
||||
|
||||
protocol MainSidebarViewControllerDelegate: AnyObject {
|
||||
func sidebarRequestPresentCompose(_ sidebarViewController: MainSidebarViewController)
|
||||
|
@ -28,6 +29,8 @@ class MainSidebarViewController: UIViewController {
|
|||
private var collectionView: UICollectionView!
|
||||
private var dataSource: UICollectionViewDiffableDataSource<Section, Item>!
|
||||
|
||||
private var listsCancellable: AnyCancellable?
|
||||
|
||||
var allItems: [Item] {
|
||||
[
|
||||
.tab(.timelines),
|
||||
|
@ -99,10 +102,11 @@ 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(listRenamed(_:)), name: .listRenamed, object: nil)
|
||||
NotificationCenter.default.addObserver(self, selector: #selector(preferencesChanged), name: .preferencesChanged, object: nil)
|
||||
|
||||
listsCancellable = mastodonController.$lists
|
||||
.sink { [unowned self] in self.reloadLists($0) }
|
||||
|
||||
onViewDidLoad?()
|
||||
}
|
||||
|
||||
|
@ -170,7 +174,7 @@ class MainSidebarViewController: UIViewController {
|
|||
dataSource.apply(snapshot, animatingDifferences: false)
|
||||
|
||||
applyDiscoverSectionSnapshot()
|
||||
reloadLists()
|
||||
reloadLists(mastodonController.lists)
|
||||
reloadSavedHashtags()
|
||||
reloadSavedInstances()
|
||||
}
|
||||
|
@ -203,42 +207,28 @@ class MainSidebarViewController: UIViewController {
|
|||
}
|
||||
}
|
||||
|
||||
@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 }
|
||||
|
||||
var exploreSnapshot = NSDiffableDataSourceSectionSnapshot<Item>()
|
||||
exploreSnapshot.append([.listsHeader])
|
||||
exploreSnapshot.expand([.listsHeader])
|
||||
exploreSnapshot.append(lists.map { .list($0) }, to: .listsHeader)
|
||||
exploreSnapshot.append([.addList], to: .listsHeader)
|
||||
DispatchQueue.main.async {
|
||||
let selected = self.collectionView.indexPathsForSelectedItems?.first
|
||||
|
||||
self.dataSource.apply(exploreSnapshot, to: .lists) {
|
||||
if let selected = selected {
|
||||
self.collectionView.selectItem(at: selected, animated: false, scrollPosition: .centeredVertically)
|
||||
}
|
||||
}
|
||||
private func reloadLists(_ lists: [List]) {
|
||||
var exploreSnapshot = NSDiffableDataSourceSectionSnapshot<Item>()
|
||||
exploreSnapshot.append([.listsHeader])
|
||||
exploreSnapshot.expand([.listsHeader])
|
||||
exploreSnapshot.append(lists.map { .list($0) }, to: .listsHeader)
|
||||
exploreSnapshot.append([.addList], to: .listsHeader)
|
||||
var selectedItem: Item?
|
||||
if let selectedIndexPath = collectionView.indexPathsForSelectedItems?.first,
|
||||
let item = dataSource.itemIdentifier(for: selectedIndexPath) {
|
||||
if case .list(let list) = item,
|
||||
let newList = lists.first(where: { $0.id == list.id }) {
|
||||
selectedItem = .list(newList)
|
||||
} else {
|
||||
selectedItem = item
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@objc private func listRenamed(_ notification: Foundation.Notification) {
|
||||
let list = notification.userInfo!["list"] as! List
|
||||
var snapshot = dataSource.snapshot()
|
||||
let existing = snapshot.itemIdentifiers(inSection: .lists).first(where: {
|
||||
if case .list(let existingList) = $0, existingList.id == list.id {
|
||||
return true
|
||||
} else {
|
||||
return false
|
||||
|
||||
self.dataSource.apply(exploreSnapshot, to: .lists) {
|
||||
if let selectedItem,
|
||||
let indexPath = self.dataSource.indexPath(for: selectedItem) {
|
||||
self.collectionView.selectItem(at: indexPath, animated: false, scrollPosition: .centeredVertically)
|
||||
}
|
||||
})
|
||||
if let existing {
|
||||
snapshot.insertItems([.list(list)], afterItem: existing)
|
||||
snapshot.deleteItems([existing])
|
||||
dataSource.apply(snapshot)
|
||||
}
|
||||
}
|
||||
|
||||
|
|
Loading…
Reference in New Issue