Skip to content

Commit

Permalink
fix: task error handling (#28)
Browse files Browse the repository at this point in the history
  • Loading branch information
bouassaba authored Nov 26, 2024
1 parent 4a23cc1 commit c1024a7
Show file tree
Hide file tree
Showing 4 changed files with 116 additions and 109 deletions.
4 changes: 3 additions & 1 deletion Sources/Screens/File/FileOverview.swift
Original file line number Diff line number Diff line change
Expand Up @@ -48,7 +48,9 @@ struct FileOverview: View, ViewDataProvider, LoadStateProvider, TimerLifecycle,
.onChange(of: searchText) {
fileStore.searchPublisher.send($1)
}
.refreshable { fileStore.fetchNextPage(replace: true) }
.refreshable {
fileStore.fetchNextPage(replace: true)
}
}
}
}
Expand Down
170 changes: 93 additions & 77 deletions Sources/Screens/Task/TaskList.swift
Original file line number Diff line number Diff line change
Expand Up @@ -11,76 +11,75 @@
import SwiftUI
import VoltaserveCore

struct TaskList: View {
struct TaskList: View, ViewDataProvider, LoadStateProvider, TimerLifecycle, TokenDistributing, ListItemScrollable,
ErrorPresentable
{
@EnvironmentObject private var tokenStore: TokenStore
@ObservedObject private var fileStore: FileStore
@StateObject private var taskStore = TaskStore()
@Environment(\.dismiss) private var dismiss
@State private var isDismissingAll = false
@State private var showError = false
@State private var errorTitle: String?
@State private var errorMessage: String?

init(fileStore: FileStore) {
self.fileStore = fileStore
}

var body: some View {
NavigationStack {
if let entities = taskStore.entities {
Group {
if entities.count == 0 {
Text("There are no tasks.")
} else {
List {
ForEach(entities, id: \.id) { task in
NavigationLink {
TaskOverview(task, taskStore: taskStore, fileStore: fileStore)
} label: {
TaskRow(task)
.onAppear {
onListItemAppear(task.id)
}
if isLoading {
ProgressView()
} else if let error {
VOErrorMessage(error)
} else {
if let entities = taskStore.entities {
Group {
if entities.count == 0 {
Text("There are no tasks.")
} else {
List {
ForEach(entities, id: \.id) { task in
NavigationLink {
TaskOverview(task, taskStore: taskStore, fileStore: fileStore)
} label: {
TaskRow(task)
.onAppear {
onListItemAppear(task.id)
}
}
}
}
}
}
}
.navigationBarTitleDisplayMode(.inline)
.navigationTitle("Tasks")
.refreshable {
taskStore.fetchNextPage(replace: true)
}
.toolbar {
ToolbarItem(placement: .topBarLeading) {
if isDismissingAll {
ProgressView()
} else {
Button("Dismiss All") {
performDismissAll()
.navigationBarTitleDisplayMode(.inline)
.navigationTitle("Tasks")
.refreshable {
taskStore.fetchNextPage(replace: true)
}
.toolbar {
ToolbarItem(placement: .topBarLeading) {
if isDismissingAll {
ProgressView()
} else {
Button("Dismiss All") {
performDismissAll()
}
}
}
}
ToolbarItem(placement: .topBarTrailing) {
Button("Done") {
dismiss()
ToolbarItem(placement: .topBarTrailing) {
Button("Done") {
dismiss()
}
}
}
ToolbarItem(placement: .topBarLeading) {
if taskStore.isLoading, taskStore.entities != nil {
ProgressView()
ToolbarItem(placement: .topBarLeading) {
if taskStore.entitiesIsLoading {
ProgressView()
}
}
}
}
} else {
ProgressView()
}
}
.voErrorAlert(
isPresented: $showError,
title: taskStore.errorTitle,
message: taskStore.errorMessage
)
.voErrorSheet(isPresented: $errorIsPresented, message: errorMessage)
.onAppear {
if let token = tokenStore.token {
assignTokenToStores(token)
Expand All @@ -97,54 +96,71 @@ struct TaskList: View {
onAppearOrChange()
}
}
.sync($taskStore.showError, with: $showError)
}

private func onAppearOrChange() {
private func performDismissAll() {
withErrorHandling {
_ = try await taskStore.dismiss()
return true
} before: {
isDismissingAll = true
} success: {
taskStore.fetchNextPage(replace: true)
dismiss()
} failure: { message in
errorMessage = message
errorIsPresented = true
} anyways: {
isDismissingAll = false
}
}

// MARK: - LoadStateProvider

var isLoading: Bool {
taskStore.entitiesIsLoadingFirstTime
}

var error: String? {
taskStore.entitiesError
}

// MARK: - ErrorPresentable

@State var errorIsPresented: Bool = false
@State var errorMessage: String?

// MARK: - ViewDataProvider

func onAppearOrChange() {
fetchData()
}

private func fetchData() {
func fetchData() {
taskStore.fetchNextPage(replace: true)
}

private func assignTokenToStores(_ token: VOToken.Value) {
taskStore.token = token
}
// MARK: - TimerLifecycle

private func startTimers() {
func startTimers() {
taskStore.startTimer()
}

private func stopTimers() {
func stopTimers() {
taskStore.stopTimer()
}

private func onListItemAppear(_ id: String) {
if taskStore.isEntityThreshold(id) {
taskStore.fetchNextPage()
}
// MARK: - TokenDistributing

func assignTokenToStores(_ token: VOToken.Value) {
taskStore.token = token
}

private func performDismissAll() {
isDismissingAll = true
withErrorHandling {
let result = try await taskStore.dismiss()
if let result {
if !result.succeeded.isEmpty {
fileStore.fetchTaskCount()
}
}
return true
} success: {
taskStore.fetchNextPage(replace: true)
dismiss()
} failure: { message in
errorTitle = "Error: Dismissing All Tasks"
errorMessage = message
showError = true
} anyways: {
isDismissingAll = false
// MARK: - ListItemScrollable

func onListItemAppear(_ id: String) {
if taskStore.isEntityThreshold(id) {
taskStore.fetchNextPage()
}
}
}
30 changes: 13 additions & 17 deletions Sources/Screens/Task/TaskOverview.swift
Original file line number Diff line number Diff line change
Expand Up @@ -11,14 +11,11 @@
import SwiftUI
import VoltaserveCore

struct TaskOverview: View {
struct TaskOverview: View, ErrorPresentable {
@ObservedObject private var taskStore: TaskStore
@ObservedObject private var fileStore: FileStore
@Environment(\.dismiss) private var dismiss
@State private var showDismissConfirmation = false
@State private var showError = false
@State private var errorTitle: String?
@State private var errorMessage: String?
@State private var dismissConfirmationIsPresented = false
@State private var isDismissing = false
private let task: VOTask.Entity

Expand Down Expand Up @@ -87,7 +84,7 @@ struct TaskOverview: View {
if task.status == .error {
Section(header: VOSectionHeader("Actions")) {
Button(role: .destructive) {
showDismissConfirmation = true
dismissConfirmationIsPresented = true
} label: {
HStack {
Text("Dismiss Task")
Expand All @@ -100,7 +97,7 @@ struct TaskOverview: View {
.disabled(isDismissing)
.confirmationDialog(
"Dismiss Task",
isPresented: $showDismissConfirmation,
isPresented: $dismissConfirmationIsPresented,
titleVisibility: .visible
) {
Button("Dismiss", role: .destructive) {
Expand All @@ -114,28 +111,27 @@ struct TaskOverview: View {
}
.navigationBarTitleDisplayMode(.inline)
.navigationTitle("#\(task.id)")
.voErrorAlert(
isPresented: $showError,
title: taskStore.errorTitle,
message: taskStore.errorMessage
)
.sync($taskStore.showError, with: $showError)
.voErrorSheet(isPresented: $errorIsPresented, message: errorMessage)
}

private func performDismiss() {
isDismissing = true
withErrorHandling {
try await taskStore.dismiss(task.id)
fileStore.fetchTaskCount()
return true
} before: {
isDismissing = true
} success: {
dismiss()
} failure: { message in
errorTitle = "Error: Dismissing Task"
errorMessage = message
showError = true
errorIsPresented = true
} anyways: {
isDismissing = false
}
}

// MARK: - ErrorPresentable

@State var errorIsPresented: Bool = false
@State var errorMessage: String?
}
21 changes: 7 additions & 14 deletions Sources/Screens/Task/TaskStore.swift
Original file line number Diff line number Diff line change
Expand Up @@ -14,10 +14,9 @@ import VoltaserveCore

class TaskStore: ObservableObject {
@Published var entities: [VOTask.Entity]?
@Published var showError = false
@Published var errorTitle: String?
@Published var errorMessage: String?
@Published var isLoading = false
@Published var entitiesIsLoading: Bool = false
var entitiesIsLoadingFirstTime: Bool { entitiesIsLoading && entities == nil }
@Published var entitiesError: String?
private var list: VOTask.List?
private var timer: Timer?
private var taskClient: VOTask?
Expand All @@ -35,10 +34,6 @@ class TaskStore: ObservableObject {

// MARK: - Fetch

private func fetch(id: String) async throws -> VOTask.Entity? {
try await taskClient?.fetch(id)
}

private func fetchProbe(size: Int = Constants.pageSize) async throws -> VOTask.Probe? {
try await taskClient?.fetchProbe(.init(size: size))
}
Expand All @@ -48,7 +43,7 @@ class TaskStore: ObservableObject {
}

func fetchNextPage(replace: Bool = false) {
guard !isLoading else { return }
guard !entitiesIsLoading else { return }

var nextPage = -1
var list: VOTask.List?
Expand All @@ -71,7 +66,7 @@ class TaskStore: ObservableObject {
list = try await self.fetchList(page: nextPage)
return true
} before: {
self.isLoading = true
self.entitiesIsLoading = true
} success: {
self.list = list
if let list {
Expand All @@ -82,11 +77,9 @@ class TaskStore: ObservableObject {
}
}
} failure: { message in
self.errorTitle = "Error: Fetching Tasks"
self.errorMessage = message
self.showError = true
self.entitiesError = message
} anyways: {
self.isLoading = false
self.entitiesIsLoading = false
}
}

Expand Down

0 comments on commit c1024a7

Please sign in to comment.