Compare commits

..

No commits in common. "2c8ba878b78d7680369f7db111ba1723dc0bf856" and "102fe6ed912165950295d8083aa435b540d6650b" have entirely different histories.

25 changed files with 124 additions and 173 deletions

View File

@ -29,10 +29,18 @@ public class Attachment: Codable {
let container = try decoder.container(keyedBy: CodingKeys.self)
self.id = try container.decode(String.self, forKey: .id)
self.kind = try container.decode(Kind.self, forKey: .kind)
self.url = try container.decode(URL.self, forKey: .url)
self.previewURL = try container.decode(URL.self, forKey: .previewURL)
self.remoteURL = try? container.decode(URL.self, forKey: .remoteURL)
self.textURL = try? container.decode(URL.self, forKey: .textURL)
self.url = URL(string: try container.decode(String.self, forKey: .url))!
self.previewURL = URL(string: try container.decode(String.self, forKey: .previewURL))!
if let remote = try? container.decode(String.self, forKey: .remoteURL) {
self.remoteURL = URL(string: remote)!
} else {
self.remoteURL = nil
}
if let text = try? container.decode(String.self, forKey: .textURL) {
self.textURL = URL(string: text)!
} else {
self.textURL = nil
}
self.meta = try? container.decode(Metadata.self, forKey: .meta)
self.description = try? container.decode(String.self, forKey: .description)
}

View File

@ -33,9 +33,9 @@ class MastodonController {
private(set) lazy var cache = MastodonCache(mastodonController: self)
private(set) lazy var persistentContainer = MastodonCachePersistentStore(for: self)
let instanceURL: URL
var accountInfo: LocalData.UserAccountInfo?
private(set) var accountInfo: LocalData.UserAccountInfo?
let client: Client!

View File

@ -58,7 +58,7 @@ extension AccountMO {
}
self.acct = account.acct
self.avatar = account.avatarStatic // we don't animate avatars
self.avatar = account.avatar
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.headerStatic // we don't animate headers
self.header = account.header
self.id = account.id
self.locked = account.locked
self.moved = account.moved ?? false

View File

@ -37,33 +37,19 @@ 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 {
self.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)
}
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> = AccountMO.fetchRequest()
@ -76,31 +62,17 @@ 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 {
self.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)
}
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?()
}
}
}

View File

@ -36,7 +36,6 @@ 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
@ -96,7 +95,6 @@ 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)

View File

@ -44,7 +44,6 @@
<attribute name="reblogged" optional="YES" attributeType="Boolean" usesScalarValueType="YES"/>
<attribute name="reblogsCount" attributeType="Integer 64" defaultValueString="0" usesScalarValueType="YES"/>
<attribute name="sensitive" attributeType="Boolean" usesScalarValueType="YES"/>
<attribute name="spoilerText" attributeType="String"/>
<attribute name="uri" attributeType="String"/>
<attribute name="url" optional="YES" attributeType="URI"/>
<attribute name="visibilityString" attributeType="String"/>
@ -58,6 +57,6 @@
</entity>
<elements>
<element name="Account" positionX="169.21875" positionY="78.9609375" width="128" height="313"/>
<element name="Status" positionX="-63" positionY="-18" width="128" height="403"/>
<element name="Status" positionX="-63" positionY="-18" width="128" height="388"/>
</elements>
</model>

View File

@ -9,7 +9,7 @@
import Foundation
import Pachyderm
extension AccountMO {
extension Account {
var displayOrUserName: String {
if displayName.isEmpty {
@ -31,7 +31,7 @@ extension AccountMO {
private func stripCustomEmoji(from string: String) -> String {
let range = NSRange(location: 0, length: string.utf16.count)
return AccountMO.customEmojiRegex.stringByReplacingMatches(in: string, options: [], range: range, withTemplate: "")
return Account.customEmojiRegex.stringByReplacingMatches(in: string, options: [], range: range, withTemplate: "")
}
}

View File

@ -11,7 +11,6 @@ import Foundation
private let decoder = PropertyListDecoder()
private let encoder = PropertyListEncoder()
// todo: invalidate cache on underlying data change using KVO?
@propertyWrapper
struct LazilyDecoding<Enclosing, Value: Codable> {

View File

@ -44,10 +44,11 @@ class LocalData: ObservableObject {
let url = URL(string: instanceURL),
let clientId = info["clientID"],
let secret = info["clientSecret"],
let username = info["username"],
let accessToken = info["accessToken"] else {
return nil
}
return UserAccountInfo(id: id, instanceURL: url, clientID: clientId, clientSecret: secret, username: info["username"], accessToken: accessToken)
return UserAccountInfo(id: id, instanceURL: url, clientID: clientId, clientSecret: secret, username: username, accessToken: accessToken)
}
} else {
return []
@ -55,18 +56,15 @@ class LocalData: ObservableObject {
}
set {
objectWillChange.send()
let array = newValue.map { (info) -> [String: String] in
var res = [
let array = newValue.map { (info) in
return [
"id": info.id,
"instanceURL": info.instanceURL.absoluteString,
"clientID": info.clientID,
"clientSecret": info.clientSecret,
"username": info.username,
"accessToken": info.accessToken
]
if let username = info.username {
res["username"] = username
}
return res
}
defaults.set(array, forKey: accountsKey)
}
@ -87,7 +85,7 @@ class LocalData: ObservableObject {
return !accounts.isEmpty
}
func addAccount(instanceURL url: URL, clientID: String, clientSecret secret: String, username: String?, accessToken: String) -> UserAccountInfo {
func addAccount(instanceURL url: URL, clientID: String, clientSecret secret: String, username: String, accessToken: String) -> UserAccountInfo {
var accounts = self.accounts
if let index = accounts.firstIndex(where: { $0.instanceURL == url && $0.username == username }) {
accounts.remove(at: index)
@ -99,13 +97,6 @@ class LocalData: ObservableObject {
return info
}
func setUsername(for info: UserAccountInfo, username: String) {
var info = info
info.username = username
removeAccount(info)
accounts.append(info)
}
func removeAccount(_ info: UserAccountInfo) {
accounts.removeAll(where: { $0.id == info.id })
}
@ -137,7 +128,7 @@ extension LocalData {
let instanceURL: URL
let clientID: String
let clientSecret: String
fileprivate(set) var username: String!
let username: String
let accessToken: String
func hash(into hasher: inout Hasher) {

View File

@ -64,7 +64,12 @@ class MastodonCache {
func addAll(statuses: [Status]) {
statuses.forEach(add)
mastodonController?.persistentContainer.addAll(statuses: statuses)
if let container = mastodonController?.persistentContainer {
statuses.forEach { container.addOrUpdate(status: $0, save: false) }
container.backgroundContext.perform {
try! container.backgroundContext.save()
}
}
}
// MARK: - Accounts
@ -99,7 +104,12 @@ class MastodonCache {
func addAll(accounts: [Account]) {
accounts.forEach(add)
mastodonController?.persistentContainer.addAll(accounts: accounts)
if let container = mastodonController?.persistentContainer {
accounts.forEach { container.addOrUpdate(account: $0, save: false) }
container.backgroundContext.perform {
try! container.backgroundContext.save()
}
}
}
// MARK: - Relationships

View File

@ -213,8 +213,7 @@ class ComposeViewController: UIViewController {
replyAvatarImageViewTopConstraint!.isActive = true
inReplyToContainer.isHidden = false
// todo: update to use managed objects
inReplyToLabel.text = "In reply to \(inReplyTo.account.displayName)"
inReplyToLabel.text = "In reply to \(inReplyTo.account.displayOrUserName)"
}
override func viewWillAppear(_ animated: Bool) {

View File

@ -68,13 +68,10 @@ extension OnboardingViewController: InstanceSelectorTableViewControllerDelegate
let authCode = item.value else { return }
mastodonController.authorize(authorizationCode: authCode) { (accessToken) in
let accountInfo = LocalData.shared.addAccount(instanceURL: instanceURL, clientID: clientID, clientSecret: clientSecret, username: nil, accessToken: accessToken)
mastodonController.accountInfo = accountInfo
mastodonController.getOwnAccount { (account) in
LocalData.shared.setUsername(for: accountInfo, username: account.username)
DispatchQueue.main.async {
let accountInfo = LocalData.shared.addAccount(instanceURL: instanceURL, clientID: clientID, clientSecret: clientSecret, username: account.username, accessToken: accessToken)
self.onboardingDelegate?.didFinishOnboarding(account: accountInfo)
}
}

View File

@ -128,7 +128,7 @@ class ProfileTableViewController: EnhancedTableViewController {
}
@objc func updateUIForPreferences() {
guard let accountID = accountID, let account = mastodonController.persistentContainer.account(for: accountID) else { return }
guard let accountID = accountID, let account = mastodonController.cache.account(for: accountID) else { return }
navigationItem.title = account.displayNameWithoutCustomEmoji
}

View File

@ -59,15 +59,12 @@ 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)
// todo: possible race condition here? we update the underlying data before waiting to reload the table view
self.mastodonController.cache.addAll(statuses: statuses)
self.timelineSegments.insert(statuses.map { ($0.id, .unknown) }, at: 0)
self.newer = pagination?.newer
self.older = pagination?.older
self.mastodonController.persistentContainer.addAll(statuses: statuses) {
DispatchQueue.main.async {
self.tableView.reloadData()
}
DispatchQueue.main.async {
self.tableView.reloadData()
}
}
}
@ -104,15 +101,13 @@ 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) })
self.mastodonController.persistentContainer.addAll(statuses: newStatuses) {
DispatchQueue.main.async {
UIView.performWithoutAnimation {
self.tableView.insertRows(at: newIndexPaths, with: .none)
}
DispatchQueue.main.async {
UIView.performWithoutAnimation {
self.tableView.insertRows(at: newIndexPaths, with: .none)
}
}
}
@ -138,27 +133,25 @@ 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
}
self.mastodonController.persistentContainer.addAll(statuses: newStatuses) {
DispatchQueue.main.async {
let newIndexPaths = (0..<newStatuses.count).map {
IndexPath(row: $0, section: 0)
}
UIView.performWithoutAnimation {
self.tableView.insertRows(at: newIndexPaths, with: .automatic)
}
self.refreshControl?.endRefreshing()
// maintain the current position in the list (don't scroll to the top)
self.tableView.scrollToRow(at: IndexPath(row: newStatuses.count, section: 0), at: .top, animated: false)
DispatchQueue.main.async {
let newIndexPaths = (0..<newStatuses.count).map {
IndexPath(row: $0, section: 0)
}
UIView.performWithoutAnimation {
self.tableView.insertRows(at: newIndexPaths, with: .automatic)
}
self.refreshControl?.endRefreshing()
// maintain the current position in the list (don't scroll to the top)
self.tableView.scrollToRow(at: IndexPath(row: newStatuses.count, section: 0), at: .top, animated: false)
}
}
}
@ -182,7 +175,7 @@ extension TimelineTableViewController: StatusTableViewCellDelegate {
extension TimelineTableViewController: UITableViewDataSourcePrefetching {
func tableView(_ tableView: UITableView, prefetchRowsAt indexPaths: [IndexPath]) {
for indexPath in indexPaths {
guard let status = mastodonController.persistentContainer.status(for: statusID(for: indexPath)) else { continue }
guard let status = mastodonController.cache.status(for: statusID(for: indexPath)) else { continue }
_ = ImageCache.avatars.get(status.account.avatar, completion: nil)
for attachment in status.attachments {
_ = ImageCache.attachments.get(attachment.url, completion: nil)
@ -192,7 +185,7 @@ extension TimelineTableViewController: UITableViewDataSourcePrefetching {
func tableView(_ tableView: UITableView, cancelPrefetchingForRowsAt indexPaths: [IndexPath]) {
for indexPath in indexPaths {
guard let status = mastodonController.persistentContainer.status(for: statusID(for: indexPath)) else { continue }
guard let status = mastodonController.cache.status(for: statusID(for: indexPath)) else { continue }
ImageCache.avatars.cancelWithoutCallback(status.account.avatar)
for attachment in status.attachments {
ImageCache.attachments.cancelWithoutCallback(attachment.url)

View File

@ -32,13 +32,12 @@ class UserActivityManager {
// MARK: - New Post
static func newPostActivity(mentioning: Account? = nil) -> 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.displayName)"
activity.suggestedInvocationPhrase = "Send a message to \(mentioning.displayName)"
activity.title = "Send a message to \(mentioning.displayOrUserName)"
activity.suggestedInvocationPhrase = "Send a message to \(mentioning.displayOrUserName)"
} else {
activity.userInfo = [:]
activity.title = "New Post"

View File

@ -48,7 +48,7 @@ class AttachmentsContainerView: UIView {
// MARK: - User Interaface
func updateUI(status: StatusMO) {
func updateUI(status: Status) {
self.statusID = status.id
attachments = status.attachments.filter { AttachmentsContainerView.supportedAttachmentTypes.contains($0.kind) }

View File

@ -83,15 +83,6 @@ 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()

View File

@ -138,14 +138,13 @@ 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!.displayName
peopleStr = people.first!.displayOrUserName
case 2:
peopleStr = people.first!.displayName + " and " + people.last!.displayName
peopleStr = people.first!.displayOrUserName + " and " + people.last!.displayOrUserName
default:
peopleStr = people.dropLast().map { $0.displayName }.joined(separator: ", ") + ", and " + people.last!.displayName
peopleStr = people.dropLast().map { $0.displayOrUserName }.joined(separator: ", ") + ", and " + people.last!.displayOrUserName
}
actionLabel.text = "\(verb) by \(peopleStr)"
}

View File

@ -72,16 +72,15 @@ 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!.displayName
peopleStr = people.first!.displayOrUserName
case 2:
peopleStr = people.first!.displayName + " and " + people.last!.displayName
peopleStr = people.first!.displayOrUserName + " and " + people.last!.displayOrUserName
default:
peopleStr = people.dropLast().map { $0.displayName }.joined(separator: ", ") + ", and " + people.last!.displayName
peopleStr = people.dropLast().map { $0.displayOrUserName }.joined(separator: ", ") + ", and " + people.last!.displayOrUserName
}
actionLabel.text = "Followed by \(peopleStr)"

View File

@ -52,13 +52,12 @@ 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.displayName)"
actionLabel.text = "Request to follow from \(account.displayNameWithoutCustomEmoji)"
actionLabel.removeEmojis()
} else {
actionLabel.text = "Request to follow from \(account.displayName)"
actionLabel.text = "Request to follow from \(account.displayOrUserName)"
actionLabel.setEmojis(account.emojis, identifier: account.id)
}
avatarRequest = ImageCache.avatars.get(account.avatar) { [weak self] (data) in

View File

@ -95,26 +95,25 @@ class BaseStatusTableViewCell: UITableViewCell {
}
open func createObserversIfNecessary() {
// 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) }
// }
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.persistentContainer.status(for: statusID) else {
guard let status = mastodonController.cache.status(for: statusID) else {
fatalError("Missing cached status")
}
self.statusID = statusID
@ -162,9 +161,9 @@ class BaseStatusTableViewCell: UITableViewCell {
}
}
func updateStatusState(status: StatusMO) {
favorited = status.favourited
reblogged = status.reblogged
func updateStatusState(status: Status) {
favorited = status.favourited ?? false
reblogged = status.reblogged ?? false
if favorited {
favoriteButton.accessibilityLabel = NSLocalizedString("Undo Favorite", comment: "undo favorite button accessibility label")
@ -178,22 +177,22 @@ class BaseStatusTableViewCell: UITableViewCell {
}
}
func updateUI(account: AccountMO) {
func updateUI(account: Account) {
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.persistentContainer.account(for: accountID) else { return }
guard let mastodonController = mastodonController, let account = mastodonController.cache.account(for: accountID) else { return }
avatarImageView.layer.cornerRadius = Preferences.shared.avatarStyle.cornerRadius(for: avatarImageView)
displayNameLabel.updateForAccountDisplayName(account: account)
attachmentsView.contentHidden = Preferences.shared.blurAllMedia || (mastodonController.persistentContainer.status(for: statusID)?.sensitive ?? false)
attachmentsView.contentHidden = Preferences.shared.blurAllMedia || (mastodonController.cache.status(for: statusID)?.sensitive ?? false)
}
override func prepareForReuse() {
@ -312,7 +311,7 @@ class BaseStatusTableViewCell: UITableViewCell {
extension BaseStatusTableViewCell: AttachmentViewDelegate {
func attachmentViewGallery(startingAt index: Int) -> UIViewController {
guard let status = mastodonController.persistentContainer.status(for: statusID) else { fatalError("Missing cached status \(statusID!)") }
guard let status = mastodonController.cache.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)
}

View File

@ -49,7 +49,7 @@ class ConversationMainStatusTableViewCell: BaseStatusTableViewCell {
timestampAndClientLabel.text = timestampAndClientText
}
override func updateStatusState(status: StatusMO) {
override func updateStatusState(status: Status) {
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: AccountMO) {
override func updateUI(account: Account) {
super.updateUI(account: account)
profileAccessibilityElement.accessibilityLabel = account.displayNameWithoutCustomEmoji

View File

@ -47,20 +47,20 @@ class TimelineStatusTableViewCell: BaseStatusTableViewCell {
override func createObserversIfNecessary() {
super.createObserversIfNecessary()
// 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) }
// }
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.persistentContainer.status(for: statusID) else { fatalError("Missing cached status \(statusID)") }
guard var status = mastodonController.cache.status(for: statusID) else { fatalError("Missing cached status \(statusID)") }
let realStatusID: String
if let rebloggedStatus = status.reblog {
if let rebloggedStatusID = status.reblog?.id,
let rebloggedStatus = mastodonController.cache.status(for: rebloggedStatusID) {
reblogStatusID = statusID
rebloggerID = status.account.id
status = rebloggedStatus
@ -77,7 +77,7 @@ class TimelineStatusTableViewCell: BaseStatusTableViewCell {
updateTimestamp()
let pinned = status.pinned
let pinned = status.pinned ?? false
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.persistentContainer.account(for: rebloggerID) {
let reblogger = mastodonController.cache.account(for: rebloggerID) {
updateRebloggerLabel(reblogger: reblogger)
}
}
private func updateRebloggerLabel(reblogger: AccountMO) {
private func updateRebloggerLabel(reblogger: Account) {
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.persistentContainer.status(for: statusID) else { fatalError("Missing cached status \(statusID!)") }
guard let status = mastodonController.cache.status(for: statusID) else { fatalError("Missing cached status \(statusID!)") }
timestampLabel.text = status.createdAt.timeAgoString()
timestampLabel.accessibilityLabel = TimelineStatusTableViewCell.relativeDateFormatter.localizedString(for: status.createdAt, relativeTo: Date())

View File

@ -15,7 +15,7 @@ class StatusContentTextView: ContentTextView {
didSet {
guard let statusID = statusID else { return }
guard let mastodonController = mastodonController,
let status = mastodonController.persistentContainer.status(for: statusID) else {
let status = mastodonController.cache.status(for: statusID) else {
fatalError("Can't set StatusContentTextView text without cached status for \(statusID)")
}
setTextFromHtml(status.content)

View File

@ -314,8 +314,7 @@ struct XCBActions {
DispatchQueue.main.async {
show(vc)
}
// todo: update to use managed objects
let alertController = UIAlertController(title: "Follow \(account.displayName)?", message: nil, preferredStyle: .alert)
let alertController = UIAlertController(title: "Follow \(account.displayNameWithoutCustomEmoji)?", message: nil, preferredStyle: .alert)
alertController.addAction(UIAlertAction(title: "Ok", style: .default, handler: { (_) in
performAction(account)
}))