Use CoreData for notifications screen

This commit is contained in:
Shadowfacts 2020-04-27 19:20:09 -04:00
parent fa1daa682f
commit f53474ac90
Signed by: shadowfacts
GPG Key ID: 94A5AB95422746E5
9 changed files with 46 additions and 43 deletions

View File

@ -46,14 +46,14 @@ public class Status: Decodable {
return Request<Card>(method: .get, path: "/api/v1/statuses/\(status.id)/card")
}
public static func getFavourites(_ status: Status, range: RequestRange = .default) -> Request<[Account]> {
var request = Request<[Account]>(method: .get, path: "/api/v1/statuses/\(status.id)/favourited_by")
public static func getFavourites(_ statusID: String, range: RequestRange = .default) -> Request<[Account]> {
var request = Request<[Account]>(method: .get, path: "/api/v1/statuses/\(statusID)/favourited_by")
request.range = range
return request
}
public static func getReblogs(_ status: Status, range: RequestRange = .default) -> Request<[Account]> {
var request = Request<[Account]>(method: .get, path: "/api/v1/statuses/\(status.id)/reblogged_by")
public static func getReblogs(_ statusID: String, range: RequestRange = .default) -> Request<[Account]> {
var request = Request<[Account]>(method: .get, path: "/api/v1/statuses/\(statusID)/reblogged_by")
request.range = range
return request
}

View File

@ -9,14 +9,14 @@
import Foundation
public class NotificationGroup {
public let notificationIDs: [String]
public let notifications: [Notification]
public let id: String
public let kind: Notification.Kind
public let statusState: StatusState?
init?(notifications: [Notification]) {
guard !notifications.isEmpty else { return nil }
self.notificationIDs = notifications.map { $0.id }
self.notifications = notifications
self.id = notifications.first!.id
self.kind = notifications.first!.kind
if kind == .mention {

View File

@ -92,6 +92,12 @@
ReferencedContainer = "container:Tusker.xcodeproj">
</BuildableReference>
</BuildableProductRunnable>
<CommandLineArguments>
<CommandLineArgument
argument = "-com.apple.CoreData.ConcurrencyDebug 1"
isEnabled = "YES">
</CommandLineArgument>
</CommandLineArguments>
</LaunchAction>
<ProfileAction
buildConfiguration = "Release"

View File

@ -84,7 +84,7 @@ class MastodonController {
run(request) { response in
guard case let .success(account, _) = response else { fatalError() }
self.account = account
self.cache.add(account: account)
self.persistentContainer.addOrUpdate(account: account)
completion?(account)
}
}

View File

@ -51,7 +51,7 @@ class MastodonCachePersistentStore: NSPersistentContainer {
}
}
func addOrUpdate(status: Status, incrementReferenceCount: Bool, completion: (() -> Void)?) {
func addOrUpdate(status: Status, incrementReferenceCount: Bool, completion: (() -> Void)? = nil) {
backgroundContext.perform {
self.upsert(status: status, incrementReferenceCount: incrementReferenceCount)
if self.backgroundContext.hasChanges {
@ -91,7 +91,7 @@ class MastodonCachePersistentStore: NSPersistentContainer {
}
}
func addOrUpdate(account: Account, completion: (() -> Void)?) {
func addOrUpdate(account: Account, completion: (() -> Void)? = nil) {
backgroundContext.perform {
self.upsert(account: account)
if self.backgroundContext.hasChanges {

View File

@ -63,9 +63,8 @@ class NotificationsTableViewController: EnhancedTableViewController {
self.groups.append(contentsOf: groups)
self.mastodonController.cache.addAll(notifications: notifications)
self.mastodonController.cache.addAll(statuses: notifications.compactMap { $0.status })
self.mastodonController.cache.addAll(accounts: notifications.map { $0.account })
self.mastodonController.persistentContainer.addAll(statuses: notifications.compactMap { $0.status })
self.mastodonController.persistentContainer.addAll(accounts: notifications.map { $0.account })
self.newer = pagination?.newer
self.older = pagination?.older
@ -92,7 +91,7 @@ class NotificationsTableViewController: EnhancedTableViewController {
switch group.kind {
case .mention:
guard let notification = mastodonController.cache.notification(for: group.notificationIDs.first!),
guard let notification = group.notifications.first,
let cell = tableView.dequeueReusableCell(withIdentifier: statusCell, for: indexPath) as? TimelineStatusTableViewCell else {
fatalError()
}
@ -113,7 +112,7 @@ class NotificationsTableViewController: EnhancedTableViewController {
return cell
case .followRequest:
guard let notification = mastodonController.cache.notification(for: group.notificationIDs.first!),
guard let notification = group.notifications.first,
let cell = tableView.dequeueReusableCell(withIdentifier: followRequestCell, for: indexPath) as? FollowRequestNotificationTableViewCell else { fatalError() }
cell.delegate = self
cell.updateUI(notification: notification)
@ -195,8 +194,8 @@ class NotificationsTableViewController: EnhancedTableViewController {
func dismissNotificationsInGroup(at indexPath: IndexPath, completion: (() -> Void)? = nil) {
let group = DispatchGroup()
groups[indexPath.row].notificationIDs
.map(Pachyderm.Notification.dismiss(id:))
groups[indexPath.row].notifications
.map { Pachyderm.Notification.dismiss(id: $0.id) }
.forEach { (request) in
group.enter()
mastodonController.run(request) { (response) in
@ -259,8 +258,8 @@ extension NotificationsTableViewController: StatusTableViewCellDelegate {
extension NotificationsTableViewController: UITableViewDataSourcePrefetching {
func tableView(_ tableView: UITableView, prefetchRowsAt indexPaths: [IndexPath]) {
for indexPath in indexPaths {
for notificationID in groups[indexPath.row].notificationIDs {
guard let notification = mastodonController.cache.notification(for: notificationID) else { continue }
for notification in groups[indexPath.row].notifications {
// todo: this account object could be stale
_ = ImageCache.avatars.get(notification.account.avatar, completion: nil)
}
}
@ -268,8 +267,7 @@ extension NotificationsTableViewController: UITableViewDataSourcePrefetching {
func tableView(_ tableView: UITableView, cancelPrefetchingForRowsAt indexPaths: [IndexPath]) {
for indexPath in indexPaths {
for notificationID in groups[indexPath.row].notificationIDs {
guard let notification = mastodonController.cache.notification(for: notificationID) else { continue }
for notification in groups[indexPath.row].notifications {
ImageCache.avatars.cancelWithoutCallback(notification.account.avatar)
}
}

View File

@ -73,13 +73,13 @@ class StatusActionAccountListTableViewController: EnhancedTableViewController {
if accountIDs == nil {
// account IDs haven't been set, so perform a request to load them
guard let status = mastodonController.cache.status(for: statusID) else {
guard let status = mastodonController.persistentContainer.status(for: statusID) else {
fatalError("Missing cached status \(statusID)")
}
tableView.tableFooterView = UIActivityIndicatorView(style: .large)
let request = actionType == .favorite ? Status.getFavourites(status) : Status.getReblogs(status)
let request = actionType == .favorite ? Status.getFavourites(status.id) : Status.getReblogs(status.id)
mastodonController.run(request) { (response) in
guard case let .success(accounts, _) = response else { fatalError() }
self.mastodonController.cache.addAll(accounts: accounts)

View File

@ -38,7 +38,8 @@ class ActionNotificationGroupTableViewCell: UITableViewCell {
}
@objc func updateUIForPreferences() {
let people = group.notificationIDs.compactMap(mastodonController.cache.notification(for:)).map { $0.account }
// todo: is this compactMap necessary?
let people = group.notifications.compactMap { mastodonController.persistentContainer.account(for: $0.account.id) }
updateActionLabel(people: people)
for case let imageView as UIImageView in actionAvatarStackView.arrangedSubviews {
@ -52,7 +53,7 @@ class ActionNotificationGroupTableViewCell: UITableViewCell {
}
self.group = group
guard let firstNotification = mastodonController.cache.notification(for: group.notificationIDs.first!) else { fatalError() }
guard let firstNotification = group.notifications.first else { fatalError() }
let status = firstNotification.status!
self.statusID = status.id
@ -67,7 +68,7 @@ class ActionNotificationGroupTableViewCell: UITableViewCell {
fatalError()
}
let people = group.notificationIDs.compactMap(mastodonController.cache.notification(for:)).map { $0.account }
let people = group.notifications.compactMap { mastodonController.persistentContainer.account(for: $0.account.id) }
actionAvatarStackView.arrangedSubviews.forEach { $0.removeFromSuperview() }
for account in people {
@ -98,8 +99,7 @@ class ActionNotificationGroupTableViewCell: UITableViewCell {
}
func updateTimestamp() {
guard let id = group.notificationIDs.first,
let notification = mastodonController.cache.notification(for: id) else {
guard let notification = group.notifications.first else {
fatalError("Missing cached notification")
}
@ -126,7 +126,7 @@ class ActionNotificationGroupTableViewCell: UITableViewCell {
}
}
func updateActionLabel(people: [Account]) {
func updateActionLabel(people: [AccountMO]) {
let verb: String
switch group.kind {
case .favourite:
@ -163,7 +163,7 @@ class ActionNotificationGroupTableViewCell: UITableViewCell {
extension ActionNotificationGroupTableViewCell: SelectableTableViewCell {
func didSelectCell() {
guard let delegate = delegate else { return }
let notifications = group.notificationIDs.compactMap(mastodonController.cache.notification(for:))
let notifications = group.notifications
let accountIDs = notifications.map { $0.account.id }
let action: StatusActionAccountListTableViewController.ActionType
switch notifications.first!.kind {
@ -184,7 +184,7 @@ extension ActionNotificationGroupTableViewCell: MenuPreviewProvider {
func getPreviewProviders(for location: CGPoint, sourceViewController: UIViewController) -> PreviewProviders? {
return (content: {
let notifications = self.group.notificationIDs.compactMap(self.mastodonController.cache.notification(for:))
let notifications = self.group.notifications
let accountIDs = notifications.map { $0.account.id }
let action: StatusActionAccountListTableViewController.ActionType
switch notifications.first!.kind {

View File

@ -34,7 +34,7 @@ class FollowNotificationGroupTableViewCell: UITableViewCell {
}
@objc func updateUIForPreferences() {
let people = group.notificationIDs.compactMap(mastodonController.cache.notification(for:)).map { $0.account }
let people = group.notifications.compactMap { mastodonController.persistentContainer.account(for: $0.account.id) }
updateActionLabel(people: people)
for case let imageView as UIImageView in avatarStackView.arrangedSubviews {
@ -45,8 +45,8 @@ class FollowNotificationGroupTableViewCell: UITableViewCell {
func updateUI(group: NotificationGroup) {
self.group = group
let people = group.notificationIDs.compactMap(mastodonController.cache.notification(for:)).map { $0.account }
let people = group.notifications.compactMap { mastodonController.persistentContainer.account(for: $0.account.id) }
updateActionLabel(people: people)
updateTimestamp()
@ -71,8 +71,8 @@ class FollowNotificationGroupTableViewCell: UITableViewCell {
}
}
func updateActionLabel(people: [Account]) {
// todo: update to use managed objects
func updateActionLabel(people: [AccountMO]) {
// todo: custom emoji in people display names
// todo: figure out how to localize this
let peopleStr: String
switch people.count {
@ -88,8 +88,7 @@ class FollowNotificationGroupTableViewCell: UITableViewCell {
}
func updateTimestamp() {
guard let id = group.notificationIDs.first,
let notification = mastodonController.cache.notification(for: id) else {
guard let notification = group.notifications.first else {
fatalError("Missing cached notification")
}
@ -128,14 +127,14 @@ class FollowNotificationGroupTableViewCell: UITableViewCell {
extension FollowNotificationGroupTableViewCell: SelectableTableViewCell {
func didSelectCell() {
let people = group.notificationIDs.compactMap(mastodonController.cache.notification(for:)).map { $0.account.id }
switch people.count {
let accountIDs = group.notifications.map { $0.account.id }
switch accountIDs.count {
case 0:
return
case 1:
delegate?.selected(account: people.first!)
delegate?.selected(account: accountIDs.first!)
default:
delegate?.showFollowedByList(accountIDs: people)
delegate?.showFollowedByList(accountIDs: accountIDs)
}
}
}
@ -145,7 +144,7 @@ extension FollowNotificationGroupTableViewCell: MenuPreviewProvider {
func getPreviewProviders(for location: CGPoint, sourceViewController: UIViewController) -> PreviewProviders? {
guard let mastodonController = mastodonController else { return nil }
let accountIDs = self.group.notificationIDs.compactMap(mastodonController.cache.notification(for:)).map { $0.account.id }
let accountIDs = self.group.notifications.map { $0.account.id }
return (content: {
if accountIDs.count == 1 {
return ProfileTableViewController(accountID: accountIDs.first!, mastodonController: mastodonController)