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)
}
}
@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) {
backgroundContext.perform {
@ -98,6 +108,14 @@ class MastodonCachePersistentStore: NSPersistentContainer {
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? {
let context = context ?? viewContext

View File

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

View File

@ -57,6 +57,14 @@ extension ToastConfiguration {
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 {