Use async/await for conversation loading

This commit is contained in:
Shadowfacts 2022-05-11 19:10:38 -04:00
parent 1a02319894
commit 21e9ca990d
3 changed files with 65 additions and 47 deletions

View File

@ -87,6 +87,16 @@ class MastodonCachePersistentStore: NSPersistentContainer {
self.statusSubject.send(status.id) self.statusSubject.send(status.id)
} }
} }
@MainActor
func addOrUpdateOnViewContext(status: Status) -> StatusMO {
let statusMO = self.upsert(status: status, context: viewContext)
if viewContext.hasChanges {
try! viewContext.save()
}
statusSubject.send(status.id)
return statusMO
}
func addAll(statuses: [Status], completion: (() -> Void)? = nil) { func addAll(statuses: [Status], completion: (() -> Void)? = nil) {
backgroundContext.perform { backgroundContext.perform {
@ -98,6 +108,14 @@ class MastodonCachePersistentStore: NSPersistentContainer {
completion?() completion?()
} }
} }
func addAll(statuses: [Status]) async {
return await withCheckedContinuation { continuation in
addAll(statuses: statuses) {
continuation.resume()
}
}
}
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

View File

@ -131,41 +131,38 @@ class ConversationTableViewController: EnhancedTableViewController {
} }
navigationItem.rightBarButtonItem = visibilityBarButtonItem navigationItem.rightBarButtonItem = visibilityBarButtonItem
loadMainStatus() Task {
await loadMainStatus()
}
} }
private func loadMainStatus() { @MainActor
private func loadMainStatus() async {
guard loadingState == .unloaded else { return } guard loadingState == .unloaded else { return }
if let mainStatus = mastodonController.persistentContainer.status(for: mainStatusID) { if let mainStatus = mastodonController.persistentContainer.status(for: mainStatusID) {
self.mainStatusLoaded(mainStatus) await mainStatusLoaded(mainStatus)
} else { } else {
loadingState = .loadingMain loadingState = .loadingMain
let request = Client.getStatus(id: mainStatusID) let req = Client.getStatus(id: mainStatusID)
mastodonController.run(request) { (response) in do {
switch response { let (status, _) = try await mastodonController.run(req)
case let .success(status, _): let statusMO = mastodonController.persistentContainer.addOrUpdateOnViewContext(status: status)
let viewContext = self.mastodonController.persistentContainer.viewContext await mainStatusLoaded(statusMO)
self.mastodonController.persistentContainer.addOrUpdate(status: status, context: viewContext) { (statusMO) in } catch {
self.mainStatusLoaded(statusMO) let error = error as! Client.Error
} loadingState = .unloaded
let config = ToastConfiguration(from: error, with: "Error Loading Status", in: self) { [weak self] toast in
case let .failure(error): toast.dismissToast(animated: true)
DispatchQueue.main.async { await self?.loadMainStatus()
self.loadingState = .unloaded
let config = ToastConfiguration(from: error, with: "Error Loading Status", in: self) { [weak self] (toast) in
toast.dismissToast(animated: true)
self?.loadMainStatus()
}
self.showToast(configuration: config, animated: true)
}
} }
showToast(configuration: config, animated: true)
return
} }
} }
} }
private func mainStatusLoaded(_ mainStatus: StatusMO) { private func mainStatusLoaded(_ mainStatus: StatusMO) async {
let mainStatusItem = Item.status(id: mainStatusID, state: mainStatusState) let mainStatusItem = Item.status(id: mainStatusID, state: mainStatusState)
var snapshot = NSDiffableDataSourceSnapshot<Section, Item>() var snapshot = NSDiffableDataSourceSnapshot<Section, Item>()
@ -175,10 +172,11 @@ class ConversationTableViewController: EnhancedTableViewController {
loadingState = .loadedMain loadingState = .loadedMain
loadContext(for: mainStatus) await loadContext(for: mainStatus)
} }
private func loadContext(for mainStatus: StatusMO) { @MainActor
private func loadContext(for mainStatus: StatusMO) async {
guard loadingState == .loadedMain else { return } guard loadingState == .loadedMain else { return }
loadingState = .loadingContext loadingState = .loadingContext
@ -188,30 +186,24 @@ class ConversationTableViewController: EnhancedTableViewController {
// todo: it would be nice to cache these contexts // todo: it would be nice to cache these contexts
let request = Status.getContext(mainStatusID) let request = Status.getContext(mainStatusID)
mastodonController.run(request) { response in do {
switch response { let (context, _) = try await mastodonController.run(request)
case let .success(context, _): let parentIDs = self.getDirectParents(inReplyTo: mainStatusInReplyToID, from: context.ancestors)
let parentIDs = self.getDirectParents(inReplyTo: mainStatusInReplyToID, from: context.ancestors) let parentStatuses = context.ancestors.filter { parentIDs.contains($0.id) }
let parentStatuses = context.ancestors.filter { parentIDs.contains($0.id) }
// todo: should this really be blindly adding all the descendants?
await mastodonController.persistentContainer.addAll(statuses: parentStatuses + context.descendants)
self.contextLoaded(mainStatus: mainStatus, context: context, parentIDs: parentIDs)
} catch {
let error = error as! Client.Error
self.loadingState = .loadedMain
// todo: should this really be blindly adding all the descendants? let config = ToastConfiguration(from: error, with: "Error Loading Content", in: self) { [weak self] (toast) in
self.mastodonController.persistentContainer.addAll(statuses: parentStatuses + context.descendants) { toast.dismissToast(animated: true)
DispatchQueue.main.async { await self?.loadContext(for: mainStatus)
self.contextLoaded(mainStatus: mainStatus, context: context, parentIDs: parentIDs)
}
}
case let .failure(error):
DispatchQueue.main.async {
self.loadingState = .loadedMain
let config = ToastConfiguration(from: error, with: "Error Loading Content", in: self) { [weak self] (toast) in
toast.dismissToast(animated: true)
self?.loadContext(for: mainStatus)
}
self.showToast(configuration: config, animated: true)
}
} }
self.showToast(configuration: config, animated: true)
} }
} }

View File

@ -57,6 +57,14 @@ extension ToastConfiguration {
viewController.present(reporter, animated: true) viewController.present(reporter, animated: true)
} }
} }
init(from error: Client.Error, with title: String, in viewController: UIViewController, retryAction: @escaping @MainActor (ToastView) async -> Void) {
self.init(from: error, with: title, in: viewController) { toast in
Task {
await retryAction(toast)
}
}
}
} }
fileprivate extension Client.Error { fileprivate extension Client.Error {