Add error dialogs with retry/cancel for network failures
Implemented user-friendly error handling for network call failures: Android (Compose): - Created reusable ErrorDialog component with retry and cancel buttons - Updated TasksScreen to show error dialog popup instead of inline error - Error dialog appears when network calls fail with option to retry iOS (SwiftUI): - Created ErrorAlertModifier and ErrorAlertInfo helpers - Added .errorAlert() view modifier for consistent error handling - Updated TaskFormView to show error alerts with retry/cancel options - Error alerts appear when task creation or other network calls fail Both platforms now provide a consistent user experience when network errors occur, giving users the choice to retry the operation or cancel. 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude <noreply@anthropic.com>
This commit is contained in:
58
iosApp/iosApp/Helpers/ErrorAlertModifier.swift
Normal file
58
iosApp/iosApp/Helpers/ErrorAlertModifier.swift
Normal file
@@ -0,0 +1,58 @@
|
||||
import SwiftUI
|
||||
|
||||
/// A view modifier that shows error alerts with retry/cancel options
|
||||
struct ErrorAlertModifier: ViewModifier {
|
||||
let error: ErrorAlertInfo?
|
||||
let onRetry: () -> Void
|
||||
let onDismiss: () -> Void
|
||||
|
||||
func body(content: Content) -> some View {
|
||||
content
|
||||
.alert(
|
||||
error?.title ?? "Network Error",
|
||||
isPresented: Binding(
|
||||
get: { error != nil },
|
||||
set: { if !$0 { onDismiss() } }
|
||||
)
|
||||
) {
|
||||
Button("Try Again", role: .none) {
|
||||
onRetry()
|
||||
}
|
||||
Button("Cancel", role: .cancel) {
|
||||
onDismiss()
|
||||
}
|
||||
} message: {
|
||||
if let error = error {
|
||||
Text(error.message)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// Information about an error to display in an alert
|
||||
struct ErrorAlertInfo: Identifiable {
|
||||
let id = UUID()
|
||||
let title: String
|
||||
let message: String
|
||||
|
||||
init(title: String = "Network Error", message: String) {
|
||||
self.title = title
|
||||
self.message = message
|
||||
}
|
||||
}
|
||||
|
||||
extension View {
|
||||
/// Shows an error alert with retry and cancel buttons
|
||||
///
|
||||
/// - Parameters:
|
||||
/// - error: The error to display, or nil to hide the alert
|
||||
/// - onRetry: Closure called when user taps "Try Again"
|
||||
/// - onDismiss: Closure called when user taps "Cancel" or dismisses alert
|
||||
func errorAlert(
|
||||
error: ErrorAlertInfo?,
|
||||
onRetry: @escaping () -> Void,
|
||||
onDismiss: @escaping () -> Void
|
||||
) -> some View {
|
||||
modifier(ErrorAlertModifier(error: error, onRetry: onRetry, onDismiss: onDismiss))
|
||||
}
|
||||
}
|
||||
@@ -260,10 +260,10 @@ struct TaskCard: View {
|
||||
residence: 1,
|
||||
title: "Clean Gutters",
|
||||
description: "Remove all debris from gutters",
|
||||
category: TaskCategory(id: 1, name: "maintenance", description: ""),
|
||||
priority: TaskPriority(id: 2, name: "medium", displayName: "", description: ""),
|
||||
category: TaskCategory(id: 1, name: "maintenance", orderId: 0, description: ""),
|
||||
priority: TaskPriority(id: 2, name: "medium", displayName: "", orderId: 0, description: ""),
|
||||
frequency: TaskFrequency(id: 1, name: "monthly", lookupName: "", displayName: "30", daySpan: 0, notifyDays: 0),
|
||||
status: TaskStatus(id: 1, name: "pending", displayName: "", description: ""),
|
||||
status: TaskStatus(id: 1, name: "pending", displayName: "", orderId: 0, description: ""),
|
||||
dueDate: "2024-12-15",
|
||||
estimatedCost: "150.00",
|
||||
actualCost: nil,
|
||||
|
||||
@@ -83,10 +83,10 @@ struct TasksSection: View {
|
||||
residence: 1,
|
||||
title: "Clean Gutters",
|
||||
description: "Remove all debris",
|
||||
category: TaskCategory(id: 1, name: "maintenance", description: ""),
|
||||
priority: TaskPriority(id: 2, name: "medium", displayName: "Medium", description: ""),
|
||||
category: TaskCategory(id: 1, name: "maintenance", orderId: 1, description: ""),
|
||||
priority: TaskPriority(id: 2, name: "medium", displayName: "Medium", orderId: 1, description: ""),
|
||||
frequency: TaskFrequency(id: 1, name: "monthly", lookupName: "", displayName: "Monthly", daySpan: 0, notifyDays: 0),
|
||||
status: TaskStatus(id: 1, name: "pending", displayName: "Pending", description: ""),
|
||||
status: TaskStatus(id: 1, name: "pending", displayName: "Pending", orderId: 1, description: ""),
|
||||
dueDate: "2024-12-15",
|
||||
estimatedCost: "150.00",
|
||||
actualCost: nil,
|
||||
@@ -113,10 +113,10 @@ struct TasksSection: View {
|
||||
residence: 1,
|
||||
title: "Fix Leaky Faucet",
|
||||
description: "Kitchen sink fixed",
|
||||
category: TaskCategory(id: 2, name: "plumbing", description: ""),
|
||||
priority: TaskPriority(id: 3, name: "high", displayName: "High", description: ""),
|
||||
category: TaskCategory(id: 2, name: "plumbing", orderId: 1, description: ""),
|
||||
priority: TaskPriority(id: 3, name: "high", displayName: "High", orderId: 1, description: ""),
|
||||
frequency: TaskFrequency(id: 6, name: "once", lookupName: "", displayName: "One Time", daySpan: 0, notifyDays: 0),
|
||||
status: TaskStatus(id: 3, name: "completed", displayName: "Completed", description: ""),
|
||||
status: TaskStatus(id: 3, name: "completed", displayName: "Completed", orderId: 1, description: ""),
|
||||
dueDate: "2024-11-01",
|
||||
estimatedCost: "200.00",
|
||||
actualCost: nil,
|
||||
|
||||
@@ -41,6 +41,9 @@ struct TaskFormView: View {
|
||||
@State private var titleError: String = ""
|
||||
@State private var residenceError: String = ""
|
||||
|
||||
// Error alert state
|
||||
@State private var errorAlert: ErrorAlertInfo? = nil
|
||||
|
||||
var body: some View {
|
||||
NavigationStack {
|
||||
ZStack {
|
||||
@@ -173,6 +176,21 @@ struct TaskFormView: View {
|
||||
isPresented = false
|
||||
}
|
||||
}
|
||||
.onChange(of: viewModel.errorMessage) { errorMessage in
|
||||
if let errorMessage = errorMessage, !errorMessage.isEmpty {
|
||||
errorAlert = ErrorAlertInfo(message: errorMessage)
|
||||
}
|
||||
}
|
||||
.errorAlert(
|
||||
error: errorAlert,
|
||||
onRetry: {
|
||||
errorAlert = nil
|
||||
submitForm()
|
||||
},
|
||||
onDismiss: {
|
||||
errorAlert = nil
|
||||
}
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user