Compare commits
No commits in common. "2c8ba878b78d7680369f7db111ba1723dc0bf856" and "102fe6ed912165950295d8083aa435b540d6650b" have entirely different histories.
2c8ba878b7
...
102fe6ed91
|
@ -29,10 +29,18 @@ public class Attachment: Codable {
|
||||||
let container = try decoder.container(keyedBy: CodingKeys.self)
|
let container = try decoder.container(keyedBy: CodingKeys.self)
|
||||||
self.id = try container.decode(String.self, forKey: .id)
|
self.id = try container.decode(String.self, forKey: .id)
|
||||||
self.kind = try container.decode(Kind.self, forKey: .kind)
|
self.kind = try container.decode(Kind.self, forKey: .kind)
|
||||||
self.url = try container.decode(URL.self, forKey: .url)
|
self.url = URL(string: try container.decode(String.self, forKey: .url))!
|
||||||
self.previewURL = try container.decode(URL.self, forKey: .previewURL)
|
self.previewURL = URL(string: try container.decode(String.self, forKey: .previewURL))!
|
||||||
self.remoteURL = try? container.decode(URL.self, forKey: .remoteURL)
|
if let remote = try? container.decode(String.self, forKey: .remoteURL) {
|
||||||
self.textURL = try? container.decode(URL.self, forKey: .textURL)
|
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.meta = try? container.decode(Metadata.self, forKey: .meta)
|
||||||
self.description = try? container.decode(String.self, forKey: .description)
|
self.description = try? container.decode(String.self, forKey: .description)
|
||||||
}
|
}
|
||||||
|
|
|
@ -35,7 +35,7 @@ class MastodonController {
|
||||||
private(set) lazy var persistentContainer = MastodonCachePersistentStore(for: self)
|
private(set) lazy var persistentContainer = MastodonCachePersistentStore(for: self)
|
||||||
|
|
||||||
let instanceURL: URL
|
let instanceURL: URL
|
||||||
var accountInfo: LocalData.UserAccountInfo?
|
private(set) var accountInfo: LocalData.UserAccountInfo?
|
||||||
|
|
||||||
let client: Client!
|
let client: Client!
|
||||||
|
|
||||||
|
|
|
@ -58,7 +58,7 @@ extension AccountMO {
|
||||||
}
|
}
|
||||||
|
|
||||||
self.acct = account.acct
|
self.acct = account.acct
|
||||||
self.avatar = account.avatarStatic // we don't animate avatars
|
self.avatar = account.avatar
|
||||||
self.bot = account.bot ?? false
|
self.bot = account.bot ?? false
|
||||||
self.createdAt = account.createdAt
|
self.createdAt = account.createdAt
|
||||||
self.displayName = account.displayName
|
self.displayName = account.displayName
|
||||||
|
@ -66,7 +66,7 @@ extension AccountMO {
|
||||||
self.fields = account.fields ?? []
|
self.fields = account.fields ?? []
|
||||||
self.followersCount = account.followersCount
|
self.followersCount = account.followersCount
|
||||||
self.followingCount = account.followingCount
|
self.followingCount = account.followingCount
|
||||||
self.header = account.headerStatic // we don't animate headers
|
self.header = account.header
|
||||||
self.id = account.id
|
self.id = account.id
|
||||||
self.locked = account.locked
|
self.locked = account.locked
|
||||||
self.moved = account.moved ?? false
|
self.moved = account.moved ?? false
|
||||||
|
|
|
@ -37,33 +37,19 @@ class MastodonCachePersistentStore: NSPersistentContainer {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private func upsert(status: Status) {
|
func addOrUpdate(status: Status, save: Bool = true) {
|
||||||
|
backgroundContext.perform {
|
||||||
if let statusMO = self.status(for: status.id, in: self.backgroundContext) {
|
if let statusMO = self.status(for: status.id, in: self.backgroundContext) {
|
||||||
statusMO.updateFrom(apiStatus: status, container: self)
|
statusMO.updateFrom(apiStatus: status, container: self)
|
||||||
} else {
|
} else {
|
||||||
_ = StatusMO(apiStatus: status, container: self, context: self.backgroundContext)
|
_ = StatusMO(apiStatus: status, container: self, context: self.backgroundContext)
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
|
||||||
func addOrUpdate(status: Status, save: Bool = true) {
|
|
||||||
backgroundContext.perform {
|
|
||||||
self.upsert(status: status)
|
|
||||||
if save, self.backgroundContext.hasChanges {
|
if save, self.backgroundContext.hasChanges {
|
||||||
try! self.backgroundContext.save()
|
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? {
|
func account(for id: String, in context: NSManagedObjectContext? = nil) -> AccountMO? {
|
||||||
let context = context ?? viewContext
|
let context = context ?? viewContext
|
||||||
let request: NSFetchRequest<AccountMO> = AccountMO.fetchRequest()
|
let request: NSFetchRequest<AccountMO> = AccountMO.fetchRequest()
|
||||||
|
@ -76,31 +62,17 @@ class MastodonCachePersistentStore: NSPersistentContainer {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private func upsert(account: Account) {
|
func addOrUpdate(account: Account, save: Bool = true) {
|
||||||
|
backgroundContext.perform {
|
||||||
if let accountMO = self.account(for: account.id, in: self.backgroundContext) {
|
if let accountMO = self.account(for: account.id, in: self.backgroundContext) {
|
||||||
accountMO.updateFrom(apiAccount: account, container: self)
|
accountMO.updateFrom(apiAccount: account, container: self)
|
||||||
} else {
|
} else {
|
||||||
_ = AccountMO(apiAccount: account, container: self, context: self.backgroundContext)
|
_ = AccountMO(apiAccount: account, container: self, context: self.backgroundContext)
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
|
||||||
func addOrUpdate(account: Account, save: Bool = true) {
|
|
||||||
backgroundContext.perform {
|
|
||||||
self.upsert(account: account)
|
|
||||||
if save, self.backgroundContext.hasChanges {
|
if save, self.backgroundContext.hasChanges {
|
||||||
try! self.backgroundContext.save()
|
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?()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -36,7 +36,6 @@ public final class StatusMO: NSManagedObject {
|
||||||
@NSManaged public var reblogged: Bool
|
@NSManaged public var reblogged: Bool
|
||||||
@NSManaged public var reblogsCount: Int
|
@NSManaged public var reblogsCount: Int
|
||||||
@NSManaged public var sensitive: Bool
|
@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 uri: String // todo: are both uri and url necessary?
|
||||||
@NSManaged public var url: URL?
|
@NSManaged public var url: URL?
|
||||||
@NSManaged private var visibilityString: String
|
@NSManaged private var visibilityString: String
|
||||||
|
@ -96,7 +95,6 @@ extension StatusMO {
|
||||||
self.reblogged = status.reblogged ?? false
|
self.reblogged = status.reblogged ?? false
|
||||||
self.reblogsCount = status.reblogsCount
|
self.reblogsCount = status.reblogsCount
|
||||||
self.sensitive = status.sensitive
|
self.sensitive = status.sensitive
|
||||||
self.spoilerText = status.spoilerText
|
|
||||||
self.uri = status.uri
|
self.uri = status.uri
|
||||||
self.visibility = status.visibility
|
self.visibility = status.visibility
|
||||||
self.account = container.account(for: status.account.id, in: context) ?? AccountMO(apiAccount: status.account, container: container, context: context)
|
self.account = container.account(for: status.account.id, in: context) ?? AccountMO(apiAccount: status.account, container: container, context: context)
|
||||||
|
|
|
@ -44,7 +44,6 @@
|
||||||
<attribute name="reblogged" optional="YES" attributeType="Boolean" usesScalarValueType="YES"/>
|
<attribute name="reblogged" optional="YES" attributeType="Boolean" usesScalarValueType="YES"/>
|
||||||
<attribute name="reblogsCount" attributeType="Integer 64" defaultValueString="0" usesScalarValueType="YES"/>
|
<attribute name="reblogsCount" attributeType="Integer 64" defaultValueString="0" usesScalarValueType="YES"/>
|
||||||
<attribute name="sensitive" attributeType="Boolean" usesScalarValueType="YES"/>
|
<attribute name="sensitive" attributeType="Boolean" usesScalarValueType="YES"/>
|
||||||
<attribute name="spoilerText" attributeType="String"/>
|
|
||||||
<attribute name="uri" attributeType="String"/>
|
<attribute name="uri" attributeType="String"/>
|
||||||
<attribute name="url" optional="YES" attributeType="URI"/>
|
<attribute name="url" optional="YES" attributeType="URI"/>
|
||||||
<attribute name="visibilityString" attributeType="String"/>
|
<attribute name="visibilityString" attributeType="String"/>
|
||||||
|
@ -58,6 +57,6 @@
|
||||||
</entity>
|
</entity>
|
||||||
<elements>
|
<elements>
|
||||||
<element name="Account" positionX="169.21875" positionY="78.9609375" width="128" height="313"/>
|
<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>
|
</elements>
|
||||||
</model>
|
</model>
|
|
@ -9,7 +9,7 @@
|
||||||
import Foundation
|
import Foundation
|
||||||
import Pachyderm
|
import Pachyderm
|
||||||
|
|
||||||
extension AccountMO {
|
extension Account {
|
||||||
|
|
||||||
var displayOrUserName: String {
|
var displayOrUserName: String {
|
||||||
if displayName.isEmpty {
|
if displayName.isEmpty {
|
||||||
|
@ -31,7 +31,7 @@ extension AccountMO {
|
||||||
|
|
||||||
private func stripCustomEmoji(from string: String) -> String {
|
private func stripCustomEmoji(from string: String) -> String {
|
||||||
let range = NSRange(location: 0, length: string.utf16.count)
|
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: "")
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -11,7 +11,6 @@ import Foundation
|
||||||
private let decoder = PropertyListDecoder()
|
private let decoder = PropertyListDecoder()
|
||||||
private let encoder = PropertyListEncoder()
|
private let encoder = PropertyListEncoder()
|
||||||
|
|
||||||
// todo: invalidate cache on underlying data change using KVO?
|
|
||||||
@propertyWrapper
|
@propertyWrapper
|
||||||
struct LazilyDecoding<Enclosing, Value: Codable> {
|
struct LazilyDecoding<Enclosing, Value: Codable> {
|
||||||
|
|
||||||
|
|
|
@ -44,10 +44,11 @@ class LocalData: ObservableObject {
|
||||||
let url = URL(string: instanceURL),
|
let url = URL(string: instanceURL),
|
||||||
let clientId = info["clientID"],
|
let clientId = info["clientID"],
|
||||||
let secret = info["clientSecret"],
|
let secret = info["clientSecret"],
|
||||||
|
let username = info["username"],
|
||||||
let accessToken = info["accessToken"] else {
|
let accessToken = info["accessToken"] else {
|
||||||
return nil
|
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 {
|
} else {
|
||||||
return []
|
return []
|
||||||
|
@ -55,18 +56,15 @@ class LocalData: ObservableObject {
|
||||||
}
|
}
|
||||||
set {
|
set {
|
||||||
objectWillChange.send()
|
objectWillChange.send()
|
||||||
let array = newValue.map { (info) -> [String: String] in
|
let array = newValue.map { (info) in
|
||||||
var res = [
|
return [
|
||||||
"id": info.id,
|
"id": info.id,
|
||||||
"instanceURL": info.instanceURL.absoluteString,
|
"instanceURL": info.instanceURL.absoluteString,
|
||||||
"clientID": info.clientID,
|
"clientID": info.clientID,
|
||||||
"clientSecret": info.clientSecret,
|
"clientSecret": info.clientSecret,
|
||||||
|
"username": info.username,
|
||||||
"accessToken": info.accessToken
|
"accessToken": info.accessToken
|
||||||
]
|
]
|
||||||
if let username = info.username {
|
|
||||||
res["username"] = username
|
|
||||||
}
|
|
||||||
return res
|
|
||||||
}
|
}
|
||||||
defaults.set(array, forKey: accountsKey)
|
defaults.set(array, forKey: accountsKey)
|
||||||
}
|
}
|
||||||
|
@ -87,7 +85,7 @@ class LocalData: ObservableObject {
|
||||||
return !accounts.isEmpty
|
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
|
var accounts = self.accounts
|
||||||
if let index = accounts.firstIndex(where: { $0.instanceURL == url && $0.username == username }) {
|
if let index = accounts.firstIndex(where: { $0.instanceURL == url && $0.username == username }) {
|
||||||
accounts.remove(at: index)
|
accounts.remove(at: index)
|
||||||
|
@ -99,13 +97,6 @@ class LocalData: ObservableObject {
|
||||||
return info
|
return info
|
||||||
}
|
}
|
||||||
|
|
||||||
func setUsername(for info: UserAccountInfo, username: String) {
|
|
||||||
var info = info
|
|
||||||
info.username = username
|
|
||||||
removeAccount(info)
|
|
||||||
accounts.append(info)
|
|
||||||
}
|
|
||||||
|
|
||||||
func removeAccount(_ info: UserAccountInfo) {
|
func removeAccount(_ info: UserAccountInfo) {
|
||||||
accounts.removeAll(where: { $0.id == info.id })
|
accounts.removeAll(where: { $0.id == info.id })
|
||||||
}
|
}
|
||||||
|
@ -137,7 +128,7 @@ extension LocalData {
|
||||||
let instanceURL: URL
|
let instanceURL: URL
|
||||||
let clientID: String
|
let clientID: String
|
||||||
let clientSecret: String
|
let clientSecret: String
|
||||||
fileprivate(set) var username: String!
|
let username: String
|
||||||
let accessToken: String
|
let accessToken: String
|
||||||
|
|
||||||
func hash(into hasher: inout Hasher) {
|
func hash(into hasher: inout Hasher) {
|
||||||
|
|
|
@ -64,7 +64,12 @@ class MastodonCache {
|
||||||
|
|
||||||
func addAll(statuses: [Status]) {
|
func addAll(statuses: [Status]) {
|
||||||
statuses.forEach(add)
|
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
|
// MARK: - Accounts
|
||||||
|
@ -99,7 +104,12 @@ class MastodonCache {
|
||||||
|
|
||||||
func addAll(accounts: [Account]) {
|
func addAll(accounts: [Account]) {
|
||||||
accounts.forEach(add)
|
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
|
// MARK: - Relationships
|
||||||
|
|
|
@ -213,8 +213,7 @@ class ComposeViewController: UIViewController {
|
||||||
replyAvatarImageViewTopConstraint!.isActive = true
|
replyAvatarImageViewTopConstraint!.isActive = true
|
||||||
|
|
||||||
inReplyToContainer.isHidden = false
|
inReplyToContainer.isHidden = false
|
||||||
// todo: update to use managed objects
|
inReplyToLabel.text = "In reply to \(inReplyTo.account.displayOrUserName)"
|
||||||
inReplyToLabel.text = "In reply to \(inReplyTo.account.displayName)"
|
|
||||||
}
|
}
|
||||||
|
|
||||||
override func viewWillAppear(_ animated: Bool) {
|
override func viewWillAppear(_ animated: Bool) {
|
||||||
|
|
|
@ -68,13 +68,10 @@ extension OnboardingViewController: InstanceSelectorTableViewControllerDelegate
|
||||||
let authCode = item.value else { return }
|
let authCode = item.value else { return }
|
||||||
|
|
||||||
mastodonController.authorize(authorizationCode: authCode) { (accessToken) in
|
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
|
mastodonController.getOwnAccount { (account) in
|
||||||
LocalData.shared.setUsername(for: accountInfo, username: account.username)
|
|
||||||
|
|
||||||
DispatchQueue.main.async {
|
DispatchQueue.main.async {
|
||||||
|
let accountInfo = LocalData.shared.addAccount(instanceURL: instanceURL, clientID: clientID, clientSecret: clientSecret, username: account.username, accessToken: accessToken)
|
||||||
|
|
||||||
self.onboardingDelegate?.didFinishOnboarding(account: accountInfo)
|
self.onboardingDelegate?.didFinishOnboarding(account: accountInfo)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -128,7 +128,7 @@ class ProfileTableViewController: EnhancedTableViewController {
|
||||||
}
|
}
|
||||||
|
|
||||||
@objc func updateUIForPreferences() {
|
@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
|
navigationItem.title = account.displayNameWithoutCustomEmoji
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -59,18 +59,15 @@ class TimelineTableViewController: EnhancedTableViewController {
|
||||||
let request = Client.getStatuses(timeline: timeline)
|
let request = Client.getStatuses(timeline: timeline)
|
||||||
mastodonController.run(request) { response in
|
mastodonController.run(request) { response in
|
||||||
guard case let .success(statuses, pagination) = response else { fatalError() }
|
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.timelineSegments.insert(statuses.map { ($0.id, .unknown) }, at: 0)
|
||||||
self.newer = pagination?.newer
|
self.newer = pagination?.newer
|
||||||
self.older = pagination?.older
|
self.older = pagination?.older
|
||||||
self.mastodonController.persistentContainer.addAll(statuses: statuses) {
|
|
||||||
DispatchQueue.main.async {
|
DispatchQueue.main.async {
|
||||||
self.tableView.reloadData()
|
self.tableView.reloadData()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
|
||||||
// MARK: - Table view data source
|
// MARK: - Table view data source
|
||||||
|
|
||||||
|
@ -104,11 +101,10 @@ class TimelineTableViewController: EnhancedTableViewController {
|
||||||
mastodonController.run(request) { response in
|
mastodonController.run(request) { response in
|
||||||
guard case let .success(newStatuses, pagination) = response else { fatalError() }
|
guard case let .success(newStatuses, pagination) = response else { fatalError() }
|
||||||
self.older = pagination?.older
|
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 newRows = self.timelineSegments.last!.count..<(self.timelineSegments.last!.count + newStatuses.count)
|
||||||
let newIndexPaths = newRows.map { IndexPath(row: $0, section: self.timelineSegments.count - 1) }
|
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.timelineSegments[self.timelineSegments.count - 1].append(contentsOf: newStatuses.map { ($0.id, .unknown) })
|
||||||
self.mastodonController.persistentContainer.addAll(statuses: newStatuses) {
|
|
||||||
DispatchQueue.main.async {
|
DispatchQueue.main.async {
|
||||||
UIView.performWithoutAnimation {
|
UIView.performWithoutAnimation {
|
||||||
self.tableView.insertRows(at: newIndexPaths, with: .none)
|
self.tableView.insertRows(at: newIndexPaths, with: .none)
|
||||||
|
@ -117,7 +113,6 @@ class TimelineTableViewController: EnhancedTableViewController {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
|
||||||
override func tableView(_ tableView: UITableView, canEditRowAt indexPath: IndexPath) -> Bool {
|
override func tableView(_ tableView: UITableView, canEditRowAt indexPath: IndexPath) -> Bool {
|
||||||
return true
|
return true
|
||||||
|
@ -138,14 +133,13 @@ class TimelineTableViewController: EnhancedTableViewController {
|
||||||
mastodonController.run(request) { response in
|
mastodonController.run(request) { response in
|
||||||
guard case let .success(newStatuses, pagination) = response else { fatalError() }
|
guard case let .success(newStatuses, pagination) = response else { fatalError() }
|
||||||
self.newer = pagination?.newer
|
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)
|
self.timelineSegments[0].insert(contentsOf: newStatuses.map { ($0.id, .unknown) }, at: 0)
|
||||||
|
|
||||||
if let newer = pagination?.newer {
|
if let newer = pagination?.newer {
|
||||||
self.newer = newer
|
self.newer = newer
|
||||||
}
|
}
|
||||||
|
|
||||||
self.mastodonController.persistentContainer.addAll(statuses: newStatuses) {
|
|
||||||
DispatchQueue.main.async {
|
DispatchQueue.main.async {
|
||||||
let newIndexPaths = (0..<newStatuses.count).map {
|
let newIndexPaths = (0..<newStatuses.count).map {
|
||||||
IndexPath(row: $0, section: 0)
|
IndexPath(row: $0, section: 0)
|
||||||
|
@ -161,7 +155,6 @@ class TimelineTableViewController: EnhancedTableViewController {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
|
||||||
@objc func composePressed(_ sender: Any) {
|
@objc func composePressed(_ sender: Any) {
|
||||||
compose()
|
compose()
|
||||||
|
@ -182,7 +175,7 @@ extension TimelineTableViewController: StatusTableViewCellDelegate {
|
||||||
extension TimelineTableViewController: UITableViewDataSourcePrefetching {
|
extension TimelineTableViewController: UITableViewDataSourcePrefetching {
|
||||||
func tableView(_ tableView: UITableView, prefetchRowsAt indexPaths: [IndexPath]) {
|
func tableView(_ tableView: UITableView, prefetchRowsAt indexPaths: [IndexPath]) {
|
||||||
for indexPath in indexPaths {
|
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)
|
_ = ImageCache.avatars.get(status.account.avatar, completion: nil)
|
||||||
for attachment in status.attachments {
|
for attachment in status.attachments {
|
||||||
_ = ImageCache.attachments.get(attachment.url, completion: nil)
|
_ = ImageCache.attachments.get(attachment.url, completion: nil)
|
||||||
|
@ -192,7 +185,7 @@ extension TimelineTableViewController: UITableViewDataSourcePrefetching {
|
||||||
|
|
||||||
func tableView(_ tableView: UITableView, cancelPrefetchingForRowsAt indexPaths: [IndexPath]) {
|
func tableView(_ tableView: UITableView, cancelPrefetchingForRowsAt indexPaths: [IndexPath]) {
|
||||||
for indexPath in indexPaths {
|
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)
|
ImageCache.avatars.cancelWithoutCallback(status.account.avatar)
|
||||||
for attachment in status.attachments {
|
for attachment in status.attachments {
|
||||||
ImageCache.attachments.cancelWithoutCallback(attachment.url)
|
ImageCache.attachments.cancelWithoutCallback(attachment.url)
|
||||||
|
|
|
@ -32,13 +32,12 @@ class UserActivityManager {
|
||||||
|
|
||||||
// MARK: - New Post
|
// MARK: - New Post
|
||||||
static func newPostActivity(mentioning: Account? = nil) -> NSUserActivity {
|
static func newPostActivity(mentioning: Account? = nil) -> NSUserActivity {
|
||||||
// todo: update to use managed objects
|
|
||||||
let activity = NSUserActivity(type: .newPost)
|
let activity = NSUserActivity(type: .newPost)
|
||||||
activity.isEligibleForPrediction = true
|
activity.isEligibleForPrediction = true
|
||||||
if let mentioning = mentioning {
|
if let mentioning = mentioning {
|
||||||
activity.userInfo = ["mentioning": mentioning.acct]
|
activity.userInfo = ["mentioning": mentioning.acct]
|
||||||
activity.title = "Send a message to \(mentioning.displayName)"
|
activity.title = "Send a message to \(mentioning.displayOrUserName)"
|
||||||
activity.suggestedInvocationPhrase = "Send a message to \(mentioning.displayName)"
|
activity.suggestedInvocationPhrase = "Send a message to \(mentioning.displayOrUserName)"
|
||||||
} else {
|
} else {
|
||||||
activity.userInfo = [:]
|
activity.userInfo = [:]
|
||||||
activity.title = "New Post"
|
activity.title = "New Post"
|
||||||
|
|
|
@ -48,7 +48,7 @@ class AttachmentsContainerView: UIView {
|
||||||
|
|
||||||
// MARK: - User Interaface
|
// MARK: - User Interaface
|
||||||
|
|
||||||
func updateUI(status: StatusMO) {
|
func updateUI(status: Status) {
|
||||||
self.statusID = status.id
|
self.statusID = status.id
|
||||||
attachments = status.attachments.filter { AttachmentsContainerView.supportedAttachmentTypes.contains($0.kind) }
|
attachments = status.attachments.filter { AttachmentsContainerView.supportedAttachmentTypes.contains($0.kind) }
|
||||||
|
|
||||||
|
|
|
@ -83,15 +83,6 @@ class EmojiLabel: UILabel {
|
||||||
|
|
||||||
extension EmojiLabel {
|
extension EmojiLabel {
|
||||||
func updateForAccountDisplayName(account: Account) {
|
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 {
|
if Preferences.shared.hideCustomEmojiInUsernames {
|
||||||
self.text = account.displayNameWithoutCustomEmoji
|
self.text = account.displayNameWithoutCustomEmoji
|
||||||
self.removeEmojis()
|
self.removeEmojis()
|
||||||
|
|
|
@ -138,14 +138,13 @@ class ActionNotificationGroupTableViewCell: UITableViewCell {
|
||||||
}
|
}
|
||||||
let peopleStr: String
|
let peopleStr: String
|
||||||
// todo: figure out how to localize this
|
// todo: figure out how to localize this
|
||||||
// todo: update to use managed objects
|
|
||||||
switch people.count {
|
switch people.count {
|
||||||
case 1:
|
case 1:
|
||||||
peopleStr = people.first!.displayName
|
peopleStr = people.first!.displayOrUserName
|
||||||
case 2:
|
case 2:
|
||||||
peopleStr = people.first!.displayName + " and " + people.last!.displayName
|
peopleStr = people.first!.displayOrUserName + " and " + people.last!.displayOrUserName
|
||||||
default:
|
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)"
|
actionLabel.text = "\(verb) by \(peopleStr)"
|
||||||
}
|
}
|
||||||
|
|
|
@ -72,16 +72,15 @@ class FollowNotificationGroupTableViewCell: UITableViewCell {
|
||||||
}
|
}
|
||||||
|
|
||||||
func updateActionLabel(people: [Account]) {
|
func updateActionLabel(people: [Account]) {
|
||||||
// todo: update to use managed objects
|
|
||||||
// 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 {
|
||||||
case 1:
|
case 1:
|
||||||
peopleStr = people.first!.displayName
|
peopleStr = people.first!.displayOrUserName
|
||||||
case 2:
|
case 2:
|
||||||
peopleStr = people.first!.displayName + " and " + people.last!.displayName
|
peopleStr = people.first!.displayOrUserName + " and " + people.last!.displayOrUserName
|
||||||
default:
|
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)"
|
actionLabel.text = "Followed by \(peopleStr)"
|
||||||
|
|
|
@ -52,13 +52,12 @@ class FollowRequestNotificationTableViewCell: UITableViewCell {
|
||||||
}
|
}
|
||||||
|
|
||||||
func updateUI(account: Account) {
|
func updateUI(account: Account) {
|
||||||
// todo: update to use managed objects
|
|
||||||
self.account = account
|
self.account = account
|
||||||
if Preferences.shared.hideCustomEmojiInUsernames {
|
if Preferences.shared.hideCustomEmojiInUsernames {
|
||||||
actionLabel.text = "Request to follow from \(account.displayName)"
|
actionLabel.text = "Request to follow from \(account.displayNameWithoutCustomEmoji)"
|
||||||
actionLabel.removeEmojis()
|
actionLabel.removeEmojis()
|
||||||
} else {
|
} else {
|
||||||
actionLabel.text = "Request to follow from \(account.displayName)"
|
actionLabel.text = "Request to follow from \(account.displayOrUserName)"
|
||||||
actionLabel.setEmojis(account.emojis, identifier: account.id)
|
actionLabel.setEmojis(account.emojis, identifier: account.id)
|
||||||
}
|
}
|
||||||
avatarRequest = ImageCache.avatars.get(account.avatar) { [weak self] (data) in
|
avatarRequest = ImageCache.avatars.get(account.avatar) { [weak self] (data) in
|
||||||
|
|
|
@ -95,26 +95,25 @@ class BaseStatusTableViewCell: UITableViewCell {
|
||||||
}
|
}
|
||||||
|
|
||||||
open func createObserversIfNecessary() {
|
open func createObserversIfNecessary() {
|
||||||
// todo: KVO on StatusMO for this?
|
if statusUpdater == nil {
|
||||||
// if statusUpdater == nil {
|
statusUpdater = mastodonController.cache.statusSubject
|
||||||
// statusUpdater = mastodonController.cache.statusSubject
|
.filter { [unowned self] in $0.id == self.statusID }
|
||||||
// .filter { [unowned self] in $0.id == self.statusID }
|
.receive(on: DispatchQueue.main)
|
||||||
// .receive(on: DispatchQueue.main)
|
.sink { [unowned self] in self.updateStatusState(status: $0) }
|
||||||
// .sink { [unowned self] in self.updateStatusState(status: $0) }
|
}
|
||||||
// }
|
|
||||||
//
|
if accountUpdater == nil {
|
||||||
// if accountUpdater == nil {
|
accountUpdater = mastodonController.cache.accountSubject
|
||||||
// accountUpdater = mastodonController.cache.accountSubject
|
.filter { [unowned self] in $0.id == self.accountID }
|
||||||
// .filter { [unowned self] in $0.id == self.accountID }
|
.receive(on: DispatchQueue.main)
|
||||||
// .receive(on: DispatchQueue.main)
|
.sink { [unowned self] in self.updateUI(account: $0) }
|
||||||
// .sink { [unowned self] in self.updateUI(account: $0) }
|
}
|
||||||
// }
|
|
||||||
}
|
}
|
||||||
|
|
||||||
func updateUI(statusID: String, state: StatusState) {
|
func updateUI(statusID: String, state: StatusState) {
|
||||||
createObserversIfNecessary()
|
createObserversIfNecessary()
|
||||||
|
|
||||||
guard let status = mastodonController.persistentContainer.status(for: statusID) else {
|
guard let status = mastodonController.cache.status(for: statusID) else {
|
||||||
fatalError("Missing cached status")
|
fatalError("Missing cached status")
|
||||||
}
|
}
|
||||||
self.statusID = statusID
|
self.statusID = statusID
|
||||||
|
@ -162,9 +161,9 @@ class BaseStatusTableViewCell: UITableViewCell {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func updateStatusState(status: StatusMO) {
|
func updateStatusState(status: Status) {
|
||||||
favorited = status.favourited
|
favorited = status.favourited ?? false
|
||||||
reblogged = status.reblogged
|
reblogged = status.reblogged ?? false
|
||||||
|
|
||||||
if favorited {
|
if favorited {
|
||||||
favoriteButton.accessibilityLabel = NSLocalizedString("Undo Favorite", comment: "undo favorite button accessibility label")
|
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)"
|
usernameLabel.text = "@\(account.acct)"
|
||||||
avatarImageView.image = nil
|
avatarImageView.image = nil
|
||||||
avatarRequest = ImageCache.avatars.get(account.avatar) { [weak self] (data) in
|
avatarRequest = ImageCache.avatars.get(account.avatar) { [weak self] (data) in
|
||||||
DispatchQueue.main.async {
|
|
||||||
guard let self = self, let data = data, self.accountID == account.id else { return }
|
guard let self = self, let data = data, self.accountID == account.id else { return }
|
||||||
|
DispatchQueue.main.async {
|
||||||
self.avatarImageView.image = UIImage(data: data)
|
self.avatarImageView.image = UIImage(data: data)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@objc func updateUIForPreferences() {
|
@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)
|
avatarImageView.layer.cornerRadius = Preferences.shared.avatarStyle.cornerRadius(for: avatarImageView)
|
||||||
displayNameLabel.updateForAccountDisplayName(account: account)
|
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() {
|
override func prepareForReuse() {
|
||||||
|
@ -312,7 +311,7 @@ class BaseStatusTableViewCell: UITableViewCell {
|
||||||
|
|
||||||
extension BaseStatusTableViewCell: AttachmentViewDelegate {
|
extension BaseStatusTableViewCell: AttachmentViewDelegate {
|
||||||
func attachmentViewGallery(startingAt index: Int) -> UIViewController {
|
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:))
|
let sourceViews = status.attachments.map(attachmentsView.getAttachmentView(for:))
|
||||||
return delegate!.gallery(attachments: status.attachments, sourceViews: sourceViews, startIndex: index)
|
return delegate!.gallery(attachments: status.attachments, sourceViews: sourceViews, startIndex: index)
|
||||||
}
|
}
|
||||||
|
|
|
@ -49,7 +49,7 @@ class ConversationMainStatusTableViewCell: BaseStatusTableViewCell {
|
||||||
timestampAndClientLabel.text = timestampAndClientText
|
timestampAndClientLabel.text = timestampAndClientText
|
||||||
}
|
}
|
||||||
|
|
||||||
override func updateStatusState(status: StatusMO) {
|
override func updateStatusState(status: Status) {
|
||||||
super.updateStatusState(status: status)
|
super.updateStatusState(status: status)
|
||||||
|
|
||||||
// todo: localize me
|
// todo: localize me
|
||||||
|
@ -57,7 +57,7 @@ class ConversationMainStatusTableViewCell: BaseStatusTableViewCell {
|
||||||
totalReblogsButton.setTitle("\(status.reblogsCount) Reblog\(status.reblogsCount == 1 ? "" : "s")", for: .normal)
|
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)
|
super.updateUI(account: account)
|
||||||
|
|
||||||
profileAccessibilityElement.accessibilityLabel = account.displayNameWithoutCustomEmoji
|
profileAccessibilityElement.accessibilityLabel = account.displayNameWithoutCustomEmoji
|
||||||
|
|
|
@ -47,20 +47,20 @@ class TimelineStatusTableViewCell: BaseStatusTableViewCell {
|
||||||
override func createObserversIfNecessary() {
|
override func createObserversIfNecessary() {
|
||||||
super.createObserversIfNecessary()
|
super.createObserversIfNecessary()
|
||||||
|
|
||||||
// todo: use KVO on reblogger account?
|
if rebloggerAccountUpdater == nil {
|
||||||
// if rebloggerAccountUpdater == nil {
|
rebloggerAccountUpdater = mastodonController.cache.accountSubject
|
||||||
// rebloggerAccountUpdater = mastodonController.cache.accountSubject
|
.filter { [unowned self] in $0.id == self.rebloggerID }
|
||||||
// .filter { [unowned self] in $0.id == self.rebloggerID }
|
.receive(on: DispatchQueue.main)
|
||||||
// .receive(on: DispatchQueue.main)
|
.sink { [unowned self] in self.updateRebloggerLabel(reblogger: $0) }
|
||||||
// .sink { [unowned self] in self.updateRebloggerLabel(reblogger: $0) }
|
}
|
||||||
// }
|
|
||||||
}
|
}
|
||||||
|
|
||||||
override func updateUI(statusID: String, state: StatusState) {
|
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
|
let realStatusID: String
|
||||||
if let rebloggedStatus = status.reblog {
|
if let rebloggedStatusID = status.reblog?.id,
|
||||||
|
let rebloggedStatus = mastodonController.cache.status(for: rebloggedStatusID) {
|
||||||
reblogStatusID = statusID
|
reblogStatusID = statusID
|
||||||
rebloggerID = status.account.id
|
rebloggerID = status.account.id
|
||||||
status = rebloggedStatus
|
status = rebloggedStatus
|
||||||
|
@ -77,7 +77,7 @@ class TimelineStatusTableViewCell: BaseStatusTableViewCell {
|
||||||
|
|
||||||
updateTimestamp()
|
updateTimestamp()
|
||||||
|
|
||||||
let pinned = status.pinned
|
let pinned = status.pinned ?? false
|
||||||
pinImageView.isHidden = !(pinned && showPinned)
|
pinImageView.isHidden = !(pinned && showPinned)
|
||||||
timestampLabel.isHidden = !pinImageView.isHidden
|
timestampLabel.isHidden = !pinImageView.isHidden
|
||||||
}
|
}
|
||||||
|
@ -85,12 +85,12 @@ class TimelineStatusTableViewCell: BaseStatusTableViewCell {
|
||||||
@objc override func updateUIForPreferences() {
|
@objc override func updateUIForPreferences() {
|
||||||
super.updateUIForPreferences()
|
super.updateUIForPreferences()
|
||||||
if let rebloggerID = rebloggerID,
|
if let rebloggerID = rebloggerID,
|
||||||
let reblogger = mastodonController.persistentContainer.account(for: rebloggerID) {
|
let reblogger = mastodonController.cache.account(for: rebloggerID) {
|
||||||
updateRebloggerLabel(reblogger: reblogger)
|
updateRebloggerLabel(reblogger: reblogger)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private func updateRebloggerLabel(reblogger: AccountMO) {
|
private func updateRebloggerLabel(reblogger: Account) {
|
||||||
if Preferences.shared.hideCustomEmojiInUsernames {
|
if Preferences.shared.hideCustomEmojiInUsernames {
|
||||||
reblogLabel.text = "Reblogged by \(reblogger.displayNameWithoutCustomEmoji)"
|
reblogLabel.text = "Reblogged by \(reblogger.displayNameWithoutCustomEmoji)"
|
||||||
reblogLabel.removeEmojis()
|
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
|
// 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
|
// so we bail out immediately, since there's nothing to update
|
||||||
guard let mastodonController = mastodonController else { return }
|
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.text = status.createdAt.timeAgoString()
|
||||||
timestampLabel.accessibilityLabel = TimelineStatusTableViewCell.relativeDateFormatter.localizedString(for: status.createdAt, relativeTo: Date())
|
timestampLabel.accessibilityLabel = TimelineStatusTableViewCell.relativeDateFormatter.localizedString(for: status.createdAt, relativeTo: Date())
|
||||||
|
|
|
@ -15,7 +15,7 @@ class StatusContentTextView: ContentTextView {
|
||||||
didSet {
|
didSet {
|
||||||
guard let statusID = statusID else { return }
|
guard let statusID = statusID else { return }
|
||||||
guard let mastodonController = mastodonController,
|
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)")
|
fatalError("Can't set StatusContentTextView text without cached status for \(statusID)")
|
||||||
}
|
}
|
||||||
setTextFromHtml(status.content)
|
setTextFromHtml(status.content)
|
||||||
|
|
|
@ -314,8 +314,7 @@ struct XCBActions {
|
||||||
DispatchQueue.main.async {
|
DispatchQueue.main.async {
|
||||||
show(vc)
|
show(vc)
|
||||||
}
|
}
|
||||||
// todo: update to use managed objects
|
let alertController = UIAlertController(title: "Follow \(account.displayNameWithoutCustomEmoji)?", message: nil, preferredStyle: .alert)
|
||||||
let alertController = UIAlertController(title: "Follow \(account.displayName)?", message: nil, preferredStyle: .alert)
|
|
||||||
alertController.addAction(UIAlertAction(title: "Ok", style: .default, handler: { (_) in
|
alertController.addAction(UIAlertAction(title: "Ok", style: .default, handler: { (_) in
|
||||||
performAction(account)
|
performAction(account)
|
||||||
}))
|
}))
|
||||||
|
|
Loading…
Reference in New Issue