From 2c8ba878b78d7680369f7db111ba1723dc0bf856 Mon Sep 17 00:00:00 2001 From: Shadowfacts Date: Sun, 12 Apr 2020 12:54:27 -0400 Subject: [PATCH] Start converting UI to use CoreData backed objects instead of API objects directly --- Tusker/CoreData/AccountMO.swift | 4 +- .../MastodonCachePersistentStore.swift | 50 +++++++++++++++---- Tusker/CoreData/StatusMO.swift | 2 + .../Tusker.xcdatamodel/contents | 3 +- Tusker/Extensions/Account+Preferences.swift | 4 +- Tusker/LazilyDecoding.swift | 1 + Tusker/MastodonCache.swift | 14 +----- .../Compose/ComposeViewController.swift | 3 +- .../Profile/ProfileTableViewController.swift | 2 +- .../TimelineTableViewController.swift | 49 ++++++++++-------- Tusker/Shortcuts/UserActivityManager.swift | 5 +- .../AttachmentsContainerView.swift | 2 +- Tusker/Views/EmojiLabel.swift | 9 ++++ ...ActionNotificationGroupTableViewCell.swift | 7 +-- ...FollowNotificationGroupTableViewCell.swift | 7 +-- ...llowRequestNotificationTableViewCell.swift | 5 +- .../Status/BaseStatusTableViewCell.swift | 45 +++++++++-------- .../ConversationMainStatusTableViewCell.swift | 4 +- .../Status/TimelineStatusTableViewCell.swift | 26 +++++----- Tusker/Views/StatusContentTextView.swift | 2 +- Tusker/XCallbackURL/XCBActions.swift | 3 +- 21 files changed, 146 insertions(+), 101 deletions(-) diff --git a/Tusker/CoreData/AccountMO.swift b/Tusker/CoreData/AccountMO.swift index 5920781c..db852131 100644 --- a/Tusker/CoreData/AccountMO.swift +++ b/Tusker/CoreData/AccountMO.swift @@ -58,7 +58,7 @@ extension AccountMO { } self.acct = account.acct - self.avatar = account.avatar + self.avatar = account.avatarStatic // we don't animate avatars self.bot = account.bot ?? false self.createdAt = account.createdAt self.displayName = account.displayName @@ -66,7 +66,7 @@ extension AccountMO { self.fields = account.fields ?? [] self.followersCount = account.followersCount self.followingCount = account.followingCount - self.header = account.header + self.header = account.headerStatic // we don't animate headers self.id = account.id self.locked = account.locked self.moved = account.moved ?? false diff --git a/Tusker/CoreData/MastodonCachePersistentStore.swift b/Tusker/CoreData/MastodonCachePersistentStore.swift index 21345974..81dc89e1 100644 --- a/Tusker/CoreData/MastodonCachePersistentStore.swift +++ b/Tusker/CoreData/MastodonCachePersistentStore.swift @@ -37,19 +37,33 @@ class MastodonCachePersistentStore: NSPersistentContainer { } } + private func upsert(status: Status) { + if let statusMO = self.status(for: status.id, in: self.backgroundContext) { + statusMO.updateFrom(apiStatus: status, container: self) + } else { + _ = StatusMO(apiStatus: status, container: self, context: self.backgroundContext) + } + } + func addOrUpdate(status: Status, save: Bool = true) { backgroundContext.perform { - if let statusMO = self.status(for: status.id, in: self.backgroundContext) { - statusMO.updateFrom(apiStatus: status, container: self) - } else { - _ = StatusMO(apiStatus: status, container: self, context: self.backgroundContext) - } + self.upsert(status: status) if save, self.backgroundContext.hasChanges { try! self.backgroundContext.save() } } } - + + func addAll(statuses: [Status], completion: (() -> Void)? = nil) { + backgroundContext.perform { + statuses.forEach(self.upsert(status:)) + if self.backgroundContext.hasChanges { + try! self.backgroundContext.save() + } + completion?() + } + } + func account(for id: String, in context: NSManagedObjectContext? = nil) -> AccountMO? { let context = context ?? viewContext let request: NSFetchRequest = AccountMO.fetchRequest() @@ -62,17 +76,31 @@ class MastodonCachePersistentStore: NSPersistentContainer { } } + private func upsert(account: Account) { + if let accountMO = self.account(for: account.id, in: self.backgroundContext) { + accountMO.updateFrom(apiAccount: account, container: self) + } else { + _ = AccountMO(apiAccount: account, container: self, context: self.backgroundContext) + } + } + func addOrUpdate(account: Account, save: Bool = true) { backgroundContext.perform { - if let accountMO = self.account(for: account.id, in: self.backgroundContext) { - accountMO.updateFrom(apiAccount: account, container: self) - } else { - _ = AccountMO(apiAccount: account, container: self, context: self.backgroundContext) - } + self.upsert(account: account) if save, self.backgroundContext.hasChanges { try! self.backgroundContext.save() } } } + func addAll(accounts: [Account], completion: (() -> Void)? = nil) { + backgroundContext.perform { + accounts.forEach(self.upsert(account:)) + if self.backgroundContext.hasChanges { + try! self.backgroundContext.save() + } + completion?() + } + } + } diff --git a/Tusker/CoreData/StatusMO.swift b/Tusker/CoreData/StatusMO.swift index 182a5dc8..aed68312 100644 --- a/Tusker/CoreData/StatusMO.swift +++ b/Tusker/CoreData/StatusMO.swift @@ -36,6 +36,7 @@ public final class StatusMO: NSManagedObject { @NSManaged public var reblogged: Bool @NSManaged public var reblogsCount: Int @NSManaged public var sensitive: Bool + @NSManaged public var spoilerText: String @NSManaged public var uri: String // todo: are both uri and url necessary? @NSManaged public var url: URL? @NSManaged private var visibilityString: String @@ -95,6 +96,7 @@ extension StatusMO { self.reblogged = status.reblogged ?? false self.reblogsCount = status.reblogsCount self.sensitive = status.sensitive + self.spoilerText = status.spoilerText self.uri = status.uri self.visibility = status.visibility self.account = container.account(for: status.account.id, in: context) ?? AccountMO(apiAccount: status.account, container: container, context: context) diff --git a/Tusker/CoreData/Tusker.xcdatamodeld/Tusker.xcdatamodel/contents b/Tusker/CoreData/Tusker.xcdatamodeld/Tusker.xcdatamodel/contents index c1b1a524..67b5f931 100644 --- a/Tusker/CoreData/Tusker.xcdatamodeld/Tusker.xcdatamodel/contents +++ b/Tusker/CoreData/Tusker.xcdatamodeld/Tusker.xcdatamodel/contents @@ -44,6 +44,7 @@ + @@ -57,6 +58,6 @@ - + \ No newline at end of file diff --git a/Tusker/Extensions/Account+Preferences.swift b/Tusker/Extensions/Account+Preferences.swift index 09d8dff5..9a5811be 100644 --- a/Tusker/Extensions/Account+Preferences.swift +++ b/Tusker/Extensions/Account+Preferences.swift @@ -9,7 +9,7 @@ import Foundation import Pachyderm -extension Account { +extension AccountMO { var displayOrUserName: String { if displayName.isEmpty { @@ -31,7 +31,7 @@ extension Account { private func stripCustomEmoji(from string: String) -> String { let range = NSRange(location: 0, length: string.utf16.count) - return Account.customEmojiRegex.stringByReplacingMatches(in: string, options: [], range: range, withTemplate: "") + return AccountMO.customEmojiRegex.stringByReplacingMatches(in: string, options: [], range: range, withTemplate: "") } } diff --git a/Tusker/LazilyDecoding.swift b/Tusker/LazilyDecoding.swift index 2930df58..9858188e 100644 --- a/Tusker/LazilyDecoding.swift +++ b/Tusker/LazilyDecoding.swift @@ -11,6 +11,7 @@ import Foundation private let decoder = PropertyListDecoder() private let encoder = PropertyListEncoder() +// todo: invalidate cache on underlying data change using KVO? @propertyWrapper struct LazilyDecoding { diff --git a/Tusker/MastodonCache.swift b/Tusker/MastodonCache.swift index 4e22df13..2d50bee4 100644 --- a/Tusker/MastodonCache.swift +++ b/Tusker/MastodonCache.swift @@ -64,12 +64,7 @@ class MastodonCache { func addAll(statuses: [Status]) { statuses.forEach(add) - if let container = mastodonController?.persistentContainer { - statuses.forEach { container.addOrUpdate(status: $0, save: false) } - container.backgroundContext.perform { - try! container.backgroundContext.save() - } - } + mastodonController?.persistentContainer.addAll(statuses: statuses) } // MARK: - Accounts @@ -104,12 +99,7 @@ class MastodonCache { func addAll(accounts: [Account]) { accounts.forEach(add) - if let container = mastodonController?.persistentContainer { - accounts.forEach { container.addOrUpdate(account: $0, save: false) } - container.backgroundContext.perform { - try! container.backgroundContext.save() - } - } + mastodonController?.persistentContainer.addAll(accounts: accounts) } // MARK: - Relationships diff --git a/Tusker/Screens/Compose/ComposeViewController.swift b/Tusker/Screens/Compose/ComposeViewController.swift index edd4fd15..1f274144 100644 --- a/Tusker/Screens/Compose/ComposeViewController.swift +++ b/Tusker/Screens/Compose/ComposeViewController.swift @@ -213,7 +213,8 @@ class ComposeViewController: UIViewController { replyAvatarImageViewTopConstraint!.isActive = true inReplyToContainer.isHidden = false - inReplyToLabel.text = "In reply to \(inReplyTo.account.displayOrUserName)" + // todo: update to use managed objects + inReplyToLabel.text = "In reply to \(inReplyTo.account.displayName)" } override func viewWillAppear(_ animated: Bool) { diff --git a/Tusker/Screens/Profile/ProfileTableViewController.swift b/Tusker/Screens/Profile/ProfileTableViewController.swift index 5a2c6616..24e1224e 100644 --- a/Tusker/Screens/Profile/ProfileTableViewController.swift +++ b/Tusker/Screens/Profile/ProfileTableViewController.swift @@ -128,7 +128,7 @@ class ProfileTableViewController: EnhancedTableViewController { } @objc func updateUIForPreferences() { - guard let accountID = accountID, let account = mastodonController.cache.account(for: accountID) else { return } + guard let accountID = accountID, let account = mastodonController.persistentContainer.account(for: accountID) else { return } navigationItem.title = account.displayNameWithoutCustomEmoji } diff --git a/Tusker/Screens/Timeline/TimelineTableViewController.swift b/Tusker/Screens/Timeline/TimelineTableViewController.swift index b643d066..8d07441e 100644 --- a/Tusker/Screens/Timeline/TimelineTableViewController.swift +++ b/Tusker/Screens/Timeline/TimelineTableViewController.swift @@ -59,12 +59,15 @@ class TimelineTableViewController: EnhancedTableViewController { let request = Client.getStatuses(timeline: timeline) mastodonController.run(request) { response in guard case let .success(statuses, pagination) = response else { fatalError() } - self.mastodonController.cache.addAll(statuses: statuses) +// self.mastodonController.cache.addAll(statuses: statuses) + // todo: possible race condition here? we update the underlying data before waiting to reload the table view self.timelineSegments.insert(statuses.map { ($0.id, .unknown) }, at: 0) self.newer = pagination?.newer self.older = pagination?.older - DispatchQueue.main.async { - self.tableView.reloadData() + self.mastodonController.persistentContainer.addAll(statuses: statuses) { + DispatchQueue.main.async { + self.tableView.reloadData() + } } } } @@ -101,13 +104,15 @@ class TimelineTableViewController: EnhancedTableViewController { mastodonController.run(request) { response in guard case let .success(newStatuses, pagination) = response else { fatalError() } self.older = pagination?.older - self.mastodonController.cache.addAll(statuses: newStatuses) +// self.mastodonController.cache.addAll(statuses: newStatuses) let newRows = self.timelineSegments.last!.count..<(self.timelineSegments.last!.count + newStatuses.count) let newIndexPaths = newRows.map { IndexPath(row: $0, section: self.timelineSegments.count - 1) } self.timelineSegments[self.timelineSegments.count - 1].append(contentsOf: newStatuses.map { ($0.id, .unknown) }) - DispatchQueue.main.async { - UIView.performWithoutAnimation { - self.tableView.insertRows(at: newIndexPaths, with: .none) + self.mastodonController.persistentContainer.addAll(statuses: newStatuses) { + DispatchQueue.main.async { + UIView.performWithoutAnimation { + self.tableView.insertRows(at: newIndexPaths, with: .none) + } } } } @@ -133,25 +138,27 @@ class TimelineTableViewController: EnhancedTableViewController { mastodonController.run(request) { response in guard case let .success(newStatuses, pagination) = response else { fatalError() } self.newer = pagination?.newer - self.mastodonController.cache.addAll(statuses: newStatuses) +// self.mastodonController.cache.addAll(statuses: newStatuses) self.timelineSegments[0].insert(contentsOf: newStatuses.map { ($0.id, .unknown) }, at: 0) if let newer = pagination?.newer { self.newer = newer } - DispatchQueue.main.async { - let newIndexPaths = (0.. NSUserActivity { + // todo: update to use managed objects let activity = NSUserActivity(type: .newPost) activity.isEligibleForPrediction = true if let mentioning = mentioning { activity.userInfo = ["mentioning": mentioning.acct] - activity.title = "Send a message to \(mentioning.displayOrUserName)" - activity.suggestedInvocationPhrase = "Send a message to \(mentioning.displayOrUserName)" + activity.title = "Send a message to \(mentioning.displayName)" + activity.suggestedInvocationPhrase = "Send a message to \(mentioning.displayName)" } else { activity.userInfo = [:] activity.title = "New Post" diff --git a/Tusker/Views/Attachments/AttachmentsContainerView.swift b/Tusker/Views/Attachments/AttachmentsContainerView.swift index 99f372b9..622f8ebc 100644 --- a/Tusker/Views/Attachments/AttachmentsContainerView.swift +++ b/Tusker/Views/Attachments/AttachmentsContainerView.swift @@ -48,7 +48,7 @@ class AttachmentsContainerView: UIView { // MARK: - User Interaface - func updateUI(status: Status) { + func updateUI(status: StatusMO) { self.statusID = status.id attachments = status.attachments.filter { AttachmentsContainerView.supportedAttachmentTypes.contains($0.kind) } diff --git a/Tusker/Views/EmojiLabel.swift b/Tusker/Views/EmojiLabel.swift index cc9d41be..b14ba7a3 100644 --- a/Tusker/Views/EmojiLabel.swift +++ b/Tusker/Views/EmojiLabel.swift @@ -83,6 +83,15 @@ class EmojiLabel: UILabel { extension EmojiLabel { func updateForAccountDisplayName(account: Account) { + if Preferences.shared.hideCustomEmojiInUsernames { + self.text = account.displayName + self.removeEmojis() + } else { + self.text = account.displayName + self.setEmojis(account.emojis, identifier: account.id) + } + } + func updateForAccountDisplayName(account: AccountMO) { if Preferences.shared.hideCustomEmojiInUsernames { self.text = account.displayNameWithoutCustomEmoji self.removeEmojis() diff --git a/Tusker/Views/Notifications/ActionNotificationGroupTableViewCell.swift b/Tusker/Views/Notifications/ActionNotificationGroupTableViewCell.swift index c34650ad..94bfa4dc 100644 --- a/Tusker/Views/Notifications/ActionNotificationGroupTableViewCell.swift +++ b/Tusker/Views/Notifications/ActionNotificationGroupTableViewCell.swift @@ -138,13 +138,14 @@ class ActionNotificationGroupTableViewCell: UITableViewCell { } let peopleStr: String // todo: figure out how to localize this + // todo: update to use managed objects switch people.count { case 1: - peopleStr = people.first!.displayOrUserName + peopleStr = people.first!.displayName case 2: - peopleStr = people.first!.displayOrUserName + " and " + people.last!.displayOrUserName + peopleStr = people.first!.displayName + " and " + people.last!.displayName default: - peopleStr = people.dropLast().map { $0.displayOrUserName }.joined(separator: ", ") + ", and " + people.last!.displayOrUserName + peopleStr = people.dropLast().map { $0.displayName }.joined(separator: ", ") + ", and " + people.last!.displayName } actionLabel.text = "\(verb) by \(peopleStr)" } diff --git a/Tusker/Views/Notifications/FollowNotificationGroupTableViewCell.swift b/Tusker/Views/Notifications/FollowNotificationGroupTableViewCell.swift index d6b59b99..6660711c 100644 --- a/Tusker/Views/Notifications/FollowNotificationGroupTableViewCell.swift +++ b/Tusker/Views/Notifications/FollowNotificationGroupTableViewCell.swift @@ -72,15 +72,16 @@ class FollowNotificationGroupTableViewCell: UITableViewCell { } func updateActionLabel(people: [Account]) { + // todo: update to use managed objects // todo: figure out how to localize this let peopleStr: String switch people.count { case 1: - peopleStr = people.first!.displayOrUserName + peopleStr = people.first!.displayName case 2: - peopleStr = people.first!.displayOrUserName + " and " + people.last!.displayOrUserName + peopleStr = people.first!.displayName + " and " + people.last!.displayName default: - peopleStr = people.dropLast().map { $0.displayOrUserName }.joined(separator: ", ") + ", and " + people.last!.displayOrUserName + peopleStr = people.dropLast().map { $0.displayName }.joined(separator: ", ") + ", and " + people.last!.displayName } actionLabel.text = "Followed by \(peopleStr)" diff --git a/Tusker/Views/Notifications/FollowRequestNotificationTableViewCell.swift b/Tusker/Views/Notifications/FollowRequestNotificationTableViewCell.swift index cce2e8d9..ba6a509f 100644 --- a/Tusker/Views/Notifications/FollowRequestNotificationTableViewCell.swift +++ b/Tusker/Views/Notifications/FollowRequestNotificationTableViewCell.swift @@ -52,12 +52,13 @@ class FollowRequestNotificationTableViewCell: UITableViewCell { } func updateUI(account: Account) { + // todo: update to use managed objects self.account = account if Preferences.shared.hideCustomEmojiInUsernames { - actionLabel.text = "Request to follow from \(account.displayNameWithoutCustomEmoji)" + actionLabel.text = "Request to follow from \(account.displayName)" actionLabel.removeEmojis() } else { - actionLabel.text = "Request to follow from \(account.displayOrUserName)" + actionLabel.text = "Request to follow from \(account.displayName)" actionLabel.setEmojis(account.emojis, identifier: account.id) } avatarRequest = ImageCache.avatars.get(account.avatar) { [weak self] (data) in diff --git a/Tusker/Views/Status/BaseStatusTableViewCell.swift b/Tusker/Views/Status/BaseStatusTableViewCell.swift index 663d581d..12652b10 100644 --- a/Tusker/Views/Status/BaseStatusTableViewCell.swift +++ b/Tusker/Views/Status/BaseStatusTableViewCell.swift @@ -95,25 +95,26 @@ class BaseStatusTableViewCell: UITableViewCell { } open func createObserversIfNecessary() { - if statusUpdater == nil { - statusUpdater = mastodonController.cache.statusSubject - .filter { [unowned self] in $0.id == self.statusID } - .receive(on: DispatchQueue.main) - .sink { [unowned self] in self.updateStatusState(status: $0) } - } - - if accountUpdater == nil { - accountUpdater = mastodonController.cache.accountSubject - .filter { [unowned self] in $0.id == self.accountID } - .receive(on: DispatchQueue.main) - .sink { [unowned self] in self.updateUI(account: $0) } - } + // todo: KVO on StatusMO for this? +// if statusUpdater == nil { +// statusUpdater = mastodonController.cache.statusSubject +// .filter { [unowned self] in $0.id == self.statusID } +// .receive(on: DispatchQueue.main) +// .sink { [unowned self] in self.updateStatusState(status: $0) } +// } +// +// if accountUpdater == nil { +// accountUpdater = mastodonController.cache.accountSubject +// .filter { [unowned self] in $0.id == self.accountID } +// .receive(on: DispatchQueue.main) +// .sink { [unowned self] in self.updateUI(account: $0) } +// } } func updateUI(statusID: String, state: StatusState) { createObserversIfNecessary() - guard let status = mastodonController.cache.status(for: statusID) else { + guard let status = mastodonController.persistentContainer.status(for: statusID) else { fatalError("Missing cached status") } self.statusID = statusID @@ -161,9 +162,9 @@ class BaseStatusTableViewCell: UITableViewCell { } } - func updateStatusState(status: Status) { - favorited = status.favourited ?? false - reblogged = status.reblogged ?? false + func updateStatusState(status: StatusMO) { + favorited = status.favourited + reblogged = status.reblogged if favorited { favoriteButton.accessibilityLabel = NSLocalizedString("Undo Favorite", comment: "undo favorite button accessibility label") @@ -177,22 +178,22 @@ class BaseStatusTableViewCell: UITableViewCell { } } - func updateUI(account: Account) { + func updateUI(account: AccountMO) { usernameLabel.text = "@\(account.acct)" avatarImageView.image = nil avatarRequest = ImageCache.avatars.get(account.avatar) { [weak self] (data) in - guard let self = self, let data = data, self.accountID == account.id else { return } DispatchQueue.main.async { + guard let self = self, let data = data, self.accountID == account.id else { return } self.avatarImageView.image = UIImage(data: data) } } } @objc func updateUIForPreferences() { - guard let mastodonController = mastodonController, let account = mastodonController.cache.account(for: accountID) else { return } + guard let mastodonController = mastodonController, let account = mastodonController.persistentContainer.account(for: accountID) else { return } avatarImageView.layer.cornerRadius = Preferences.shared.avatarStyle.cornerRadius(for: avatarImageView) displayNameLabel.updateForAccountDisplayName(account: account) - attachmentsView.contentHidden = Preferences.shared.blurAllMedia || (mastodonController.cache.status(for: statusID)?.sensitive ?? false) + attachmentsView.contentHidden = Preferences.shared.blurAllMedia || (mastodonController.persistentContainer.status(for: statusID)?.sensitive ?? false) } override func prepareForReuse() { @@ -311,7 +312,7 @@ class BaseStatusTableViewCell: UITableViewCell { extension BaseStatusTableViewCell: AttachmentViewDelegate { func attachmentViewGallery(startingAt index: Int) -> UIViewController { - guard let status = mastodonController.cache.status(for: statusID) else { fatalError("Missing cached status \(statusID!)") } + guard let status = mastodonController.persistentContainer.status(for: statusID) else { fatalError("Missing cached status \(statusID!)") } let sourceViews = status.attachments.map(attachmentsView.getAttachmentView(for:)) return delegate!.gallery(attachments: status.attachments, sourceViews: sourceViews, startIndex: index) } diff --git a/Tusker/Views/Status/ConversationMainStatusTableViewCell.swift b/Tusker/Views/Status/ConversationMainStatusTableViewCell.swift index 370b6fda..e6dcec75 100644 --- a/Tusker/Views/Status/ConversationMainStatusTableViewCell.swift +++ b/Tusker/Views/Status/ConversationMainStatusTableViewCell.swift @@ -49,7 +49,7 @@ class ConversationMainStatusTableViewCell: BaseStatusTableViewCell { timestampAndClientLabel.text = timestampAndClientText } - override func updateStatusState(status: Status) { + override func updateStatusState(status: StatusMO) { super.updateStatusState(status: status) // todo: localize me @@ -57,7 +57,7 @@ class ConversationMainStatusTableViewCell: BaseStatusTableViewCell { totalReblogsButton.setTitle("\(status.reblogsCount) Reblog\(status.reblogsCount == 1 ? "" : "s")", for: .normal) } - override func updateUI(account: Account) { + override func updateUI(account: AccountMO) { super.updateUI(account: account) profileAccessibilityElement.accessibilityLabel = account.displayNameWithoutCustomEmoji diff --git a/Tusker/Views/Status/TimelineStatusTableViewCell.swift b/Tusker/Views/Status/TimelineStatusTableViewCell.swift index 7780957a..1338c59a 100644 --- a/Tusker/Views/Status/TimelineStatusTableViewCell.swift +++ b/Tusker/Views/Status/TimelineStatusTableViewCell.swift @@ -47,20 +47,20 @@ class TimelineStatusTableViewCell: BaseStatusTableViewCell { override func createObserversIfNecessary() { super.createObserversIfNecessary() - if rebloggerAccountUpdater == nil { - rebloggerAccountUpdater = mastodonController.cache.accountSubject - .filter { [unowned self] in $0.id == self.rebloggerID } - .receive(on: DispatchQueue.main) - .sink { [unowned self] in self.updateRebloggerLabel(reblogger: $0) } - } + // todo: use KVO on reblogger account? +// if rebloggerAccountUpdater == nil { +// rebloggerAccountUpdater = mastodonController.cache.accountSubject +// .filter { [unowned self] in $0.id == self.rebloggerID } +// .receive(on: DispatchQueue.main) +// .sink { [unowned self] in self.updateRebloggerLabel(reblogger: $0) } +// } } override func updateUI(statusID: String, state: StatusState) { - guard var status = mastodonController.cache.status(for: statusID) else { fatalError("Missing cached status \(statusID)") } + guard var status = mastodonController.persistentContainer.status(for: statusID) else { fatalError("Missing cached status \(statusID)") } let realStatusID: String - if let rebloggedStatusID = status.reblog?.id, - let rebloggedStatus = mastodonController.cache.status(for: rebloggedStatusID) { + if let rebloggedStatus = status.reblog { reblogStatusID = statusID rebloggerID = status.account.id status = rebloggedStatus @@ -77,7 +77,7 @@ class TimelineStatusTableViewCell: BaseStatusTableViewCell { updateTimestamp() - let pinned = status.pinned ?? false + let pinned = status.pinned pinImageView.isHidden = !(pinned && showPinned) timestampLabel.isHidden = !pinImageView.isHidden } @@ -85,12 +85,12 @@ class TimelineStatusTableViewCell: BaseStatusTableViewCell { @objc override func updateUIForPreferences() { super.updateUIForPreferences() if let rebloggerID = rebloggerID, - let reblogger = mastodonController.cache.account(for: rebloggerID) { + let reblogger = mastodonController.persistentContainer.account(for: rebloggerID) { updateRebloggerLabel(reblogger: reblogger) } } - private func updateRebloggerLabel(reblogger: Account) { + private func updateRebloggerLabel(reblogger: AccountMO) { if Preferences.shared.hideCustomEmojiInUsernames { reblogLabel.text = "Reblogged by \(reblogger.displayNameWithoutCustomEmoji)" reblogLabel.removeEmojis() @@ -104,7 +104,7 @@ class TimelineStatusTableViewCell: BaseStatusTableViewCell { // if the mastodonController is nil (i.e. the delegate is nil), then the screen this cell was a part of has been deallocated // so we bail out immediately, since there's nothing to update guard let mastodonController = mastodonController else { return } - guard let status = mastodonController.cache.status(for: statusID) else { fatalError("Missing cached status \(statusID!)") } + guard let status = mastodonController.persistentContainer.status(for: statusID) else { fatalError("Missing cached status \(statusID!)") } timestampLabel.text = status.createdAt.timeAgoString() timestampLabel.accessibilityLabel = TimelineStatusTableViewCell.relativeDateFormatter.localizedString(for: status.createdAt, relativeTo: Date()) diff --git a/Tusker/Views/StatusContentTextView.swift b/Tusker/Views/StatusContentTextView.swift index c10d838b..813d6347 100644 --- a/Tusker/Views/StatusContentTextView.swift +++ b/Tusker/Views/StatusContentTextView.swift @@ -15,7 +15,7 @@ class StatusContentTextView: ContentTextView { didSet { guard let statusID = statusID else { return } guard let mastodonController = mastodonController, - let status = mastodonController.cache.status(for: statusID) else { + let status = mastodonController.persistentContainer.status(for: statusID) else { fatalError("Can't set StatusContentTextView text without cached status for \(statusID)") } setTextFromHtml(status.content) diff --git a/Tusker/XCallbackURL/XCBActions.swift b/Tusker/XCallbackURL/XCBActions.swift index fc802866..26dbb462 100644 --- a/Tusker/XCallbackURL/XCBActions.swift +++ b/Tusker/XCallbackURL/XCBActions.swift @@ -314,7 +314,8 @@ struct XCBActions { DispatchQueue.main.async { show(vc) } - let alertController = UIAlertController(title: "Follow \(account.displayNameWithoutCustomEmoji)?", message: nil, preferredStyle: .alert) + // todo: update to use managed objects + let alertController = UIAlertController(title: "Follow \(account.displayName)?", message: nil, preferredStyle: .alert) alertController.addAction(UIAlertAction(title: "Ok", style: .default, handler: { (_) in performAction(account) }))