Update bookmarks VC on bookmarked state changes

Closes #318
This commit is contained in:
Shadowfacts 2023-01-28 15:15:40 -05:00
parent 2e64500c35
commit 5e2b551045
1 changed files with 69 additions and 8 deletions

View File

@ -8,6 +8,7 @@
import UIKit import UIKit
import Pachyderm import Pachyderm
import CoreData
class BookmarksViewController: UIViewController, CollectionViewController { class BookmarksViewController: UIViewController, CollectionViewController {
@ -83,7 +84,7 @@ class BookmarksViewController: UIViewController, CollectionViewController {
} }
return UICollectionViewDiffableDataSource(collectionView: collectionView) { collectionView, indexPath, itemIdentifier in return UICollectionViewDiffableDataSource(collectionView: collectionView) { collectionView, indexPath, itemIdentifier in
switch itemIdentifier { 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)) return collectionView.dequeueConfiguredReusableCell(using: statusCell, for: indexPath, item: (id, state))
case .loadingIndicator: case .loadingIndicator:
return collectionView.dequeueConfiguredReusableCell(using: loadingCell, for: indexPath, item: ()) return collectionView.dequeueConfiguredReusableCell(using: loadingCell, for: indexPath, item: ())
@ -93,6 +94,9 @@ class BookmarksViewController: UIViewController, CollectionViewController {
override func viewDidLoad() { override func viewDidLoad() {
super.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) { override func viewWillAppear(_ animated: Bool) {
@ -132,7 +136,7 @@ class BookmarksViewController: UIViewController, CollectionViewController {
var snapshot = NSDiffableDataSourceSnapshot<Section, Item>() var snapshot = NSDiffableDataSourceSnapshot<Section, Item>()
snapshot.appendSections([.bookmarks]) 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) await apply(snapshot: snapshot, animatingDifferences: true)
state = .loaded state = .loaded
@ -170,7 +174,7 @@ class BookmarksViewController: UIViewController, CollectionViewController {
await mastodonController.persistentContainer.addAll(statuses: statuses) await mastodonController.persistentContainer.addAll(statuses: statuses)
snapshot.deleteItems([.loadingIndicator]) 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) await apply(snapshot: snapshot, animatingDifferences: true)
state = .loaded state = .loaded
@ -188,6 +192,63 @@ class BookmarksViewController: UIViewController, CollectionViewController {
} }
} }
@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<NSManagedObject> {
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<NSManagedObject> {
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)
}
}
}
} }
extension BookmarksViewController { extension BookmarksViewController {
@ -195,7 +256,7 @@ extension BookmarksViewController {
case bookmarks case bookmarks
} }
enum Item: Equatable, Hashable { enum Item: Equatable, Hashable {
case status(id: String, state: CollapseState) case status(id: String, state: CollapseState, addedLocally: Bool)
case loadingIndicator case loadingIndicator
var hideIndicators: Bool { var hideIndicators: Bool {
@ -209,7 +270,7 @@ extension BookmarksViewController {
static func ==(lhs: Item, rhs: Item) -> Bool { static func ==(lhs: Item, rhs: Item) -> Bool {
switch (lhs, rhs) { 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 return a == b
case (.loadingIndicator, .loadingIndicator): case (.loadingIndicator, .loadingIndicator):
return true return true
@ -220,7 +281,7 @@ extension BookmarksViewController {
func hash(into hasher: inout Hasher) { func hash(into hasher: inout Hasher) {
switch self { switch self {
case .status(id: let id, state: _): case .status(id: let id, _, _):
hasher.combine(0) hasher.combine(0)
hasher.combine(id) hasher.combine(id)
case .loadingIndicator: case .loadingIndicator:
@ -250,7 +311,7 @@ extension BookmarksViewController: UICollectionViewDelegate {
} }
func collectionView(_ collectionView: UICollectionView, shouldSelectItemAt indexPath: IndexPath) -> Bool { 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 return true
} else { } else {
return false return false
@ -258,7 +319,7 @@ extension BookmarksViewController: UICollectionViewDelegate {
} }
func collectionView(_ collectionView: UICollectionView, didSelectItemAt indexPath: IndexPath) { 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()) selected(status: id, state: state.copy())
} }
} }