From 5e2b5510452884ba9d10ab392bcd9f6daa0f90ae Mon Sep 17 00:00:00 2001 From: Shadowfacts Date: Sat, 28 Jan 2023 15:15:40 -0500 Subject: [PATCH] Update bookmarks VC on bookmarked state changes Closes #318 --- .../Bookmarks/BookmarksViewController.swift | 77 +++++++++++++++++-- 1 file changed, 69 insertions(+), 8 deletions(-) diff --git a/Tusker/Screens/Bookmarks/BookmarksViewController.swift b/Tusker/Screens/Bookmarks/BookmarksViewController.swift index 19eeaad6..30a7cd77 100644 --- a/Tusker/Screens/Bookmarks/BookmarksViewController.swift +++ b/Tusker/Screens/Bookmarks/BookmarksViewController.swift @@ -8,6 +8,7 @@ import UIKit import Pachyderm +import CoreData class BookmarksViewController: UIViewController, CollectionViewController { @@ -83,7 +84,7 @@ class BookmarksViewController: UIViewController, CollectionViewController { } return UICollectionViewDiffableDataSource(collectionView: collectionView) { collectionView, indexPath, itemIdentifier in switch itemIdentifier { - case .status(id: let id, state: let state): + case .status(id: let id, state: let state, addedLocally: _): return collectionView.dequeueConfiguredReusableCell(using: statusCell, for: indexPath, item: (id, state)) case .loadingIndicator: return collectionView.dequeueConfiguredReusableCell(using: loadingCell, for: indexPath, item: ()) @@ -93,6 +94,9 @@ class BookmarksViewController: UIViewController, CollectionViewController { override func viewDidLoad() { super.viewDidLoad() + + NotificationCenter.default.addObserver(self, selector: #selector(handleStatusDeleted), name: .statusDeleted, object: nil) + NotificationCenter.default.addObserver(self, selector: #selector(managedObjectsDidChange), name: .NSManagedObjectContextObjectsDidChange, object: mastodonController.persistentContainer.viewContext) } override func viewWillAppear(_ animated: Bool) { @@ -132,7 +136,7 @@ class BookmarksViewController: UIViewController, CollectionViewController { var snapshot = NSDiffableDataSourceSnapshot() snapshot.appendSections([.bookmarks]) - snapshot.appendItems(statuses.map { .status(id: $0.id, state: .unknown) }) + snapshot.appendItems(statuses.map { .status(id: $0.id, state: .unknown, addedLocally: false) }) await apply(snapshot: snapshot, animatingDifferences: true) state = .loaded @@ -170,7 +174,7 @@ class BookmarksViewController: UIViewController, CollectionViewController { await mastodonController.persistentContainer.addAll(statuses: statuses) snapshot.deleteItems([.loadingIndicator]) - snapshot.appendItems(statuses.map { .status(id: $0.id, state: .unknown) }) + snapshot.appendItems(statuses.map { .status(id: $0.id, state: .unknown, addedLocally: false) }) await apply(snapshot: snapshot, animatingDifferences: true) state = .loaded @@ -187,6 +191,63 @@ class BookmarksViewController: UIViewController, CollectionViewController { state = .loaded } } + + @objc private func handleStatusDeleted(_ notification: Foundation.Notification) { + guard let userInfo = notification.userInfo, + let accountID = mastodonController.accountInfo?.id, + userInfo["accountID"] as? String == accountID, + let statusIDs = userInfo["statusIDs"] as? [String] else { + return + } + var snapshot = dataSource.snapshot() + let toDelete = statusIDs.map { id in + Item.status(id: id, state: .unknown, addedLocally: false) + }.filter { item in + snapshot.itemIdentifiers.contains(item) + } + if !toDelete.isEmpty { + snapshot.deleteItems(toDelete) + Task { + await apply(snapshot: snapshot, animatingDifferences: true) + } + } + } + + @objc private func managedObjectsDidChange(_ notification: Foundation.Notification) { + var snapshot = dataSource.snapshot() + func prepend(item: Item) { + if let first = snapshot.itemIdentifiers.first { + snapshot.insertItems([item], beforeItem: first) + } else { + snapshot.appendItems([item]) + } + } + var hasChanges = false + if let inserted = notification.userInfo?[NSInsertedObjectsKey] as? Set { + for case let status as StatusMO in inserted where status.bookmarked == true { + prepend(item: .status(id: status.id, state: .unknown, addedLocally: true)) + hasChanges = true + } + } + if let updated = notification.userInfo?[NSUpdatedObjectsKey] as? Set { + for case let status as StatusMO in updated { + let item = Item.status(id: status.id, state: .unknown, addedLocally: true) + var exists = snapshot.itemIdentifiers.contains(item) + if status.bookmarked == true && !exists { + prepend(item: item) + hasChanges = true + } else if status.bookmarked == false && exists { + snapshot.deleteItems([item]) + hasChanges = true + } + } + } + if hasChanges { + Task { + await apply(snapshot: snapshot, animatingDifferences: true) + } + } + } } @@ -195,7 +256,7 @@ extension BookmarksViewController { case bookmarks } enum Item: Equatable, Hashable { - case status(id: String, state: CollapseState) + case status(id: String, state: CollapseState, addedLocally: Bool) case loadingIndicator var hideIndicators: Bool { @@ -209,7 +270,7 @@ extension BookmarksViewController { static func ==(lhs: Item, rhs: Item) -> Bool { switch (lhs, rhs) { - case (.status(id: let a, state: _), .status(id: let b, state: _)): + case (.status(id: let a, _, _), .status(id: let b, _, _)): return a == b case (.loadingIndicator, .loadingIndicator): return true @@ -220,7 +281,7 @@ extension BookmarksViewController { func hash(into hasher: inout Hasher) { switch self { - case .status(id: let id, state: _): + case .status(id: let id, _, _): hasher.combine(0) hasher.combine(id) case .loadingIndicator: @@ -250,7 +311,7 @@ extension BookmarksViewController: UICollectionViewDelegate { } func collectionView(_ collectionView: UICollectionView, shouldSelectItemAt indexPath: IndexPath) -> Bool { - if case .status(id: _, state: _) = dataSource.itemIdentifier(for: indexPath) { + if case .status(_, _, _) = dataSource.itemIdentifier(for: indexPath) { return true } else { return false @@ -258,7 +319,7 @@ extension BookmarksViewController: UICollectionViewDelegate { } func collectionView(_ collectionView: UICollectionView, didSelectItemAt indexPath: IndexPath) { - if case .status(id: let id, state: let state) = dataSource.itemIdentifier(for: indexPath) { + if case .status(id: let id, state: let state, _) = dataSource.itemIdentifier(for: indexPath) { selected(status: id, state: state.copy()) } }