forked from shadowfacts/Tusker
Use async/await for conversation loading
This commit is contained in:
parent
1a02319894
commit
21e9ca990d
|
@ -88,6 +88,16 @@ class MastodonCachePersistentStore: NSPersistentContainer {
|
|||
}
|
||||
}
|
||||
|
||||
@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 {
|
||||
statuses.forEach { self.upsert(status: $0, context: self.backgroundContext) }
|
||||
|
@ -99,6 +109,14 @@ class MastodonCachePersistentStore: NSPersistentContainer {
|
|||
}
|
||||
}
|
||||
|
||||
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
|
||||
let request: NSFetchRequest<AccountMO> = AccountMO.fetchRequest()
|
||||
|
|
|
@ -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
|
||||
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)
|
||||
self?.loadMainStatus()
|
||||
}
|
||||
self.showToast(configuration: config, 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,32 +186,26 @@ 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, _):
|
||||
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?
|
||||
self.mastodonController.persistentContainer.addAll(statuses: parentStatuses + context.descendants) {
|
||||
DispatchQueue.main.async {
|
||||
await mastodonController.persistentContainer.addAll(statuses: parentStatuses + context.descendants)
|
||||
self.contextLoaded(mainStatus: mainStatus, context: context, parentIDs: parentIDs)
|
||||
}
|
||||
}
|
||||
|
||||
case let .failure(error):
|
||||
DispatchQueue.main.async {
|
||||
} catch {
|
||||
let error = error as! Client.Error
|
||||
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)
|
||||
await self?.loadContext(for: mainStatus)
|
||||
}
|
||||
self.showToast(configuration: config, animated: true)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private func contextLoaded(mainStatus: StatusMO, context: ConversationContext, parentIDs: [String]) {
|
||||
let mainStatusItem = Item.status(id: mainStatusID, state: mainStatusState)
|
||||
|
|
|
@ -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 {
|
||||
|
|
Loading…
Reference in New Issue