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) {
|
func addAll(statuses: [Status], completion: (() -> Void)? = nil) {
|
||||||
backgroundContext.perform {
|
backgroundContext.perform {
|
||||||
statuses.forEach { self.upsert(status: $0, context: self.backgroundContext) }
|
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? {
|
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()
|
||||||
|
|
|
@ -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?
|
// todo: should this really be blindly adding all the descendants?
|
||||||
self.mastodonController.persistentContainer.addAll(statuses: parentStatuses + context.descendants) {
|
await mastodonController.persistentContainer.addAll(statuses: parentStatuses + context.descendants)
|
||||||
DispatchQueue.main.async {
|
self.contextLoaded(mainStatus: mainStatus, context: context, parentIDs: parentIDs)
|
||||||
self.contextLoaded(mainStatus: mainStatus, context: context, parentIDs: parentIDs)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
case let .failure(error):
|
} catch {
|
||||||
DispatchQueue.main.async {
|
let error = error as! Client.Error
|
||||||
self.loadingState = .loadedMain
|
self.loadingState = .loadedMain
|
||||||
|
|
||||||
let config = ToastConfiguration(from: error, with: "Error Loading Content", in: self) { [weak self] (toast) in
|
let config = ToastConfiguration(from: error, with: "Error Loading Content", in: self) { [weak self] (toast) in
|
||||||
toast.dismissToast(animated: true)
|
toast.dismissToast(animated: true)
|
||||||
self?.loadContext(for: mainStatus)
|
await self?.loadContext(for: mainStatus)
|
||||||
}
|
|
||||||
self.showToast(configuration: config, animated: true)
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
self.showToast(configuration: config, animated: true)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -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 {
|
||||||
|
|
Loading…
Reference in New Issue