forked from shadowfacts/Tusker
Use CoreData for notifications screen
This commit is contained in:
parent
fa1daa682f
commit
f53474ac90
|
@ -46,14 +46,14 @@ public class Status: Decodable {
|
||||||
return Request<Card>(method: .get, path: "/api/v1/statuses/\(status.id)/card")
|
return Request<Card>(method: .get, path: "/api/v1/statuses/\(status.id)/card")
|
||||||
}
|
}
|
||||||
|
|
||||||
public static func getFavourites(_ status: Status, range: RequestRange = .default) -> Request<[Account]> {
|
public static func getFavourites(_ statusID: String, range: RequestRange = .default) -> Request<[Account]> {
|
||||||
var request = Request<[Account]>(method: .get, path: "/api/v1/statuses/\(status.id)/favourited_by")
|
var request = Request<[Account]>(method: .get, path: "/api/v1/statuses/\(statusID)/favourited_by")
|
||||||
request.range = range
|
request.range = range
|
||||||
return request
|
return request
|
||||||
}
|
}
|
||||||
|
|
||||||
public static func getReblogs(_ status: Status, range: RequestRange = .default) -> Request<[Account]> {
|
public static func getReblogs(_ statusID: String, range: RequestRange = .default) -> Request<[Account]> {
|
||||||
var request = Request<[Account]>(method: .get, path: "/api/v1/statuses/\(status.id)/reblogged_by")
|
var request = Request<[Account]>(method: .get, path: "/api/v1/statuses/\(statusID)/reblogged_by")
|
||||||
request.range = range
|
request.range = range
|
||||||
return request
|
return request
|
||||||
}
|
}
|
||||||
|
|
|
@ -9,14 +9,14 @@
|
||||||
import Foundation
|
import Foundation
|
||||||
|
|
||||||
public class NotificationGroup {
|
public class NotificationGroup {
|
||||||
public let notificationIDs: [String]
|
public let notifications: [Notification]
|
||||||
public let id: String
|
public let id: String
|
||||||
public let kind: Notification.Kind
|
public let kind: Notification.Kind
|
||||||
public let statusState: StatusState?
|
public let statusState: StatusState?
|
||||||
|
|
||||||
init?(notifications: [Notification]) {
|
init?(notifications: [Notification]) {
|
||||||
guard !notifications.isEmpty else { return nil }
|
guard !notifications.isEmpty else { return nil }
|
||||||
self.notificationIDs = notifications.map { $0.id }
|
self.notifications = notifications
|
||||||
self.id = notifications.first!.id
|
self.id = notifications.first!.id
|
||||||
self.kind = notifications.first!.kind
|
self.kind = notifications.first!.kind
|
||||||
if kind == .mention {
|
if kind == .mention {
|
||||||
|
|
|
@ -92,6 +92,12 @@
|
||||||
ReferencedContainer = "container:Tusker.xcodeproj">
|
ReferencedContainer = "container:Tusker.xcodeproj">
|
||||||
</BuildableReference>
|
</BuildableReference>
|
||||||
</BuildableProductRunnable>
|
</BuildableProductRunnable>
|
||||||
|
<CommandLineArguments>
|
||||||
|
<CommandLineArgument
|
||||||
|
argument = "-com.apple.CoreData.ConcurrencyDebug 1"
|
||||||
|
isEnabled = "YES">
|
||||||
|
</CommandLineArgument>
|
||||||
|
</CommandLineArguments>
|
||||||
</LaunchAction>
|
</LaunchAction>
|
||||||
<ProfileAction
|
<ProfileAction
|
||||||
buildConfiguration = "Release"
|
buildConfiguration = "Release"
|
||||||
|
|
|
@ -84,7 +84,7 @@ class MastodonController {
|
||||||
run(request) { response in
|
run(request) { response in
|
||||||
guard case let .success(account, _) = response else { fatalError() }
|
guard case let .success(account, _) = response else { fatalError() }
|
||||||
self.account = account
|
self.account = account
|
||||||
self.cache.add(account: account)
|
self.persistentContainer.addOrUpdate(account: account)
|
||||||
completion?(account)
|
completion?(account)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -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 {
|
backgroundContext.perform {
|
||||||
self.upsert(status: status, incrementReferenceCount: incrementReferenceCount)
|
self.upsert(status: status, incrementReferenceCount: incrementReferenceCount)
|
||||||
if self.backgroundContext.hasChanges {
|
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 {
|
backgroundContext.perform {
|
||||||
self.upsert(account: account)
|
self.upsert(account: account)
|
||||||
if self.backgroundContext.hasChanges {
|
if self.backgroundContext.hasChanges {
|
||||||
|
|
|
@ -63,9 +63,8 @@ class NotificationsTableViewController: EnhancedTableViewController {
|
||||||
|
|
||||||
self.groups.append(contentsOf: groups)
|
self.groups.append(contentsOf: groups)
|
||||||
|
|
||||||
self.mastodonController.cache.addAll(notifications: notifications)
|
self.mastodonController.persistentContainer.addAll(statuses: notifications.compactMap { $0.status })
|
||||||
self.mastodonController.cache.addAll(statuses: notifications.compactMap { $0.status })
|
self.mastodonController.persistentContainer.addAll(accounts: notifications.map { $0.account })
|
||||||
self.mastodonController.cache.addAll(accounts: notifications.map { $0.account })
|
|
||||||
|
|
||||||
self.newer = pagination?.newer
|
self.newer = pagination?.newer
|
||||||
self.older = pagination?.older
|
self.older = pagination?.older
|
||||||
|
@ -92,7 +91,7 @@ class NotificationsTableViewController: EnhancedTableViewController {
|
||||||
|
|
||||||
switch group.kind {
|
switch group.kind {
|
||||||
case .mention:
|
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 {
|
let cell = tableView.dequeueReusableCell(withIdentifier: statusCell, for: indexPath) as? TimelineStatusTableViewCell else {
|
||||||
fatalError()
|
fatalError()
|
||||||
}
|
}
|
||||||
|
@ -113,7 +112,7 @@ class NotificationsTableViewController: EnhancedTableViewController {
|
||||||
return cell
|
return cell
|
||||||
|
|
||||||
case .followRequest:
|
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() }
|
let cell = tableView.dequeueReusableCell(withIdentifier: followRequestCell, for: indexPath) as? FollowRequestNotificationTableViewCell else { fatalError() }
|
||||||
cell.delegate = self
|
cell.delegate = self
|
||||||
cell.updateUI(notification: notification)
|
cell.updateUI(notification: notification)
|
||||||
|
@ -195,8 +194,8 @@ class NotificationsTableViewController: EnhancedTableViewController {
|
||||||
|
|
||||||
func dismissNotificationsInGroup(at indexPath: IndexPath, completion: (() -> Void)? = nil) {
|
func dismissNotificationsInGroup(at indexPath: IndexPath, completion: (() -> Void)? = nil) {
|
||||||
let group = DispatchGroup()
|
let group = DispatchGroup()
|
||||||
groups[indexPath.row].notificationIDs
|
groups[indexPath.row].notifications
|
||||||
.map(Pachyderm.Notification.dismiss(id:))
|
.map { Pachyderm.Notification.dismiss(id: $0.id) }
|
||||||
.forEach { (request) in
|
.forEach { (request) in
|
||||||
group.enter()
|
group.enter()
|
||||||
mastodonController.run(request) { (response) in
|
mastodonController.run(request) { (response) in
|
||||||
|
@ -259,8 +258,8 @@ extension NotificationsTableViewController: StatusTableViewCellDelegate {
|
||||||
extension NotificationsTableViewController: UITableViewDataSourcePrefetching {
|
extension NotificationsTableViewController: UITableViewDataSourcePrefetching {
|
||||||
func tableView(_ tableView: UITableView, prefetchRowsAt indexPaths: [IndexPath]) {
|
func tableView(_ tableView: UITableView, prefetchRowsAt indexPaths: [IndexPath]) {
|
||||||
for indexPath in indexPaths {
|
for indexPath in indexPaths {
|
||||||
for notificationID in groups[indexPath.row].notificationIDs {
|
for notification in groups[indexPath.row].notifications {
|
||||||
guard let notification = mastodonController.cache.notification(for: notificationID) else { continue }
|
// todo: this account object could be stale
|
||||||
_ = ImageCache.avatars.get(notification.account.avatar, completion: nil)
|
_ = ImageCache.avatars.get(notification.account.avatar, completion: nil)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -268,8 +267,7 @@ extension NotificationsTableViewController: UITableViewDataSourcePrefetching {
|
||||||
|
|
||||||
func tableView(_ tableView: UITableView, cancelPrefetchingForRowsAt indexPaths: [IndexPath]) {
|
func tableView(_ tableView: UITableView, cancelPrefetchingForRowsAt indexPaths: [IndexPath]) {
|
||||||
for indexPath in indexPaths {
|
for indexPath in indexPaths {
|
||||||
for notificationID in groups[indexPath.row].notificationIDs {
|
for notification in groups[indexPath.row].notifications {
|
||||||
guard let notification = mastodonController.cache.notification(for: notificationID) else { continue }
|
|
||||||
ImageCache.avatars.cancelWithoutCallback(notification.account.avatar)
|
ImageCache.avatars.cancelWithoutCallback(notification.account.avatar)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -73,13 +73,13 @@ class StatusActionAccountListTableViewController: EnhancedTableViewController {
|
||||||
|
|
||||||
if accountIDs == nil {
|
if accountIDs == nil {
|
||||||
// account IDs haven't been set, so perform a request to load them
|
// 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)")
|
fatalError("Missing cached status \(statusID)")
|
||||||
}
|
}
|
||||||
|
|
||||||
tableView.tableFooterView = UIActivityIndicatorView(style: .large)
|
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
|
mastodonController.run(request) { (response) in
|
||||||
guard case let .success(accounts, _) = response else { fatalError() }
|
guard case let .success(accounts, _) = response else { fatalError() }
|
||||||
self.mastodonController.cache.addAll(accounts: accounts)
|
self.mastodonController.cache.addAll(accounts: accounts)
|
||||||
|
|
|
@ -38,7 +38,8 @@ class ActionNotificationGroupTableViewCell: UITableViewCell {
|
||||||
}
|
}
|
||||||
|
|
||||||
@objc func updateUIForPreferences() {
|
@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)
|
updateActionLabel(people: people)
|
||||||
|
|
||||||
for case let imageView as UIImageView in actionAvatarStackView.arrangedSubviews {
|
for case let imageView as UIImageView in actionAvatarStackView.arrangedSubviews {
|
||||||
|
@ -52,7 +53,7 @@ class ActionNotificationGroupTableViewCell: UITableViewCell {
|
||||||
}
|
}
|
||||||
self.group = group
|
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!
|
let status = firstNotification.status!
|
||||||
self.statusID = status.id
|
self.statusID = status.id
|
||||||
|
|
||||||
|
@ -67,7 +68,7 @@ class ActionNotificationGroupTableViewCell: UITableViewCell {
|
||||||
fatalError()
|
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() }
|
actionAvatarStackView.arrangedSubviews.forEach { $0.removeFromSuperview() }
|
||||||
for account in people {
|
for account in people {
|
||||||
|
@ -98,8 +99,7 @@ class ActionNotificationGroupTableViewCell: UITableViewCell {
|
||||||
}
|
}
|
||||||
|
|
||||||
func updateTimestamp() {
|
func updateTimestamp() {
|
||||||
guard let id = group.notificationIDs.first,
|
guard let notification = group.notifications.first else {
|
||||||
let notification = mastodonController.cache.notification(for: id) else {
|
|
||||||
fatalError("Missing cached notification")
|
fatalError("Missing cached notification")
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -126,7 +126,7 @@ class ActionNotificationGroupTableViewCell: UITableViewCell {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func updateActionLabel(people: [Account]) {
|
func updateActionLabel(people: [AccountMO]) {
|
||||||
let verb: String
|
let verb: String
|
||||||
switch group.kind {
|
switch group.kind {
|
||||||
case .favourite:
|
case .favourite:
|
||||||
|
@ -163,7 +163,7 @@ class ActionNotificationGroupTableViewCell: UITableViewCell {
|
||||||
extension ActionNotificationGroupTableViewCell: SelectableTableViewCell {
|
extension ActionNotificationGroupTableViewCell: SelectableTableViewCell {
|
||||||
func didSelectCell() {
|
func didSelectCell() {
|
||||||
guard let delegate = delegate else { return }
|
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 accountIDs = notifications.map { $0.account.id }
|
||||||
let action: StatusActionAccountListTableViewController.ActionType
|
let action: StatusActionAccountListTableViewController.ActionType
|
||||||
switch notifications.first!.kind {
|
switch notifications.first!.kind {
|
||||||
|
@ -184,7 +184,7 @@ extension ActionNotificationGroupTableViewCell: MenuPreviewProvider {
|
||||||
|
|
||||||
func getPreviewProviders(for location: CGPoint, sourceViewController: UIViewController) -> PreviewProviders? {
|
func getPreviewProviders(for location: CGPoint, sourceViewController: UIViewController) -> PreviewProviders? {
|
||||||
return (content: {
|
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 accountIDs = notifications.map { $0.account.id }
|
||||||
let action: StatusActionAccountListTableViewController.ActionType
|
let action: StatusActionAccountListTableViewController.ActionType
|
||||||
switch notifications.first!.kind {
|
switch notifications.first!.kind {
|
||||||
|
|
|
@ -34,7 +34,7 @@ class FollowNotificationGroupTableViewCell: UITableViewCell {
|
||||||
}
|
}
|
||||||
|
|
||||||
@objc func updateUIForPreferences() {
|
@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)
|
updateActionLabel(people: people)
|
||||||
|
|
||||||
for case let imageView as UIImageView in avatarStackView.arrangedSubviews {
|
for case let imageView as UIImageView in avatarStackView.arrangedSubviews {
|
||||||
|
@ -45,7 +45,7 @@ class FollowNotificationGroupTableViewCell: UITableViewCell {
|
||||||
func updateUI(group: NotificationGroup) {
|
func updateUI(group: NotificationGroup) {
|
||||||
self.group = group
|
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)
|
updateActionLabel(people: people)
|
||||||
updateTimestamp()
|
updateTimestamp()
|
||||||
|
@ -71,8 +71,8 @@ class FollowNotificationGroupTableViewCell: UITableViewCell {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func updateActionLabel(people: [Account]) {
|
func updateActionLabel(people: [AccountMO]) {
|
||||||
// todo: update to use managed objects
|
// todo: custom emoji in people display names
|
||||||
// todo: figure out how to localize this
|
// todo: figure out how to localize this
|
||||||
let peopleStr: String
|
let peopleStr: String
|
||||||
switch people.count {
|
switch people.count {
|
||||||
|
@ -88,8 +88,7 @@ class FollowNotificationGroupTableViewCell: UITableViewCell {
|
||||||
}
|
}
|
||||||
|
|
||||||
func updateTimestamp() {
|
func updateTimestamp() {
|
||||||
guard let id = group.notificationIDs.first,
|
guard let notification = group.notifications.first else {
|
||||||
let notification = mastodonController.cache.notification(for: id) else {
|
|
||||||
fatalError("Missing cached notification")
|
fatalError("Missing cached notification")
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -128,14 +127,14 @@ class FollowNotificationGroupTableViewCell: UITableViewCell {
|
||||||
|
|
||||||
extension FollowNotificationGroupTableViewCell: SelectableTableViewCell {
|
extension FollowNotificationGroupTableViewCell: SelectableTableViewCell {
|
||||||
func didSelectCell() {
|
func didSelectCell() {
|
||||||
let people = group.notificationIDs.compactMap(mastodonController.cache.notification(for:)).map { $0.account.id }
|
let accountIDs = group.notifications.map { $0.account.id }
|
||||||
switch people.count {
|
switch accountIDs.count {
|
||||||
case 0:
|
case 0:
|
||||||
return
|
return
|
||||||
case 1:
|
case 1:
|
||||||
delegate?.selected(account: people.first!)
|
delegate?.selected(account: accountIDs.first!)
|
||||||
default:
|
default:
|
||||||
delegate?.showFollowedByList(accountIDs: people)
|
delegate?.showFollowedByList(accountIDs: accountIDs)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -145,7 +144,7 @@ extension FollowNotificationGroupTableViewCell: MenuPreviewProvider {
|
||||||
|
|
||||||
func getPreviewProviders(for location: CGPoint, sourceViewController: UIViewController) -> PreviewProviders? {
|
func getPreviewProviders(for location: CGPoint, sourceViewController: UIViewController) -> PreviewProviders? {
|
||||||
guard let mastodonController = mastodonController else { return nil }
|
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: {
|
return (content: {
|
||||||
if accountIDs.count == 1 {
|
if accountIDs.count == 1 {
|
||||||
return ProfileTableViewController(accountID: accountIDs.first!, mastodonController: mastodonController)
|
return ProfileTableViewController(accountID: accountIDs.first!, mastodonController: mastodonController)
|
||||||
|
|
Loading…
Reference in New Issue