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:
@@ -0,0 +1,65 @@
|
||||
package com.mycrib.android.ui.components
|
||||
|
||||
import androidx.compose.material3.AlertDialog
|
||||
import androidx.compose.material3.Button
|
||||
import androidx.compose.material3.ButtonDefaults
|
||||
import androidx.compose.material3.MaterialTheme
|
||||
import androidx.compose.material3.Text
|
||||
import androidx.compose.material3.TextButton
|
||||
import androidx.compose.runtime.Composable
|
||||
import androidx.compose.ui.graphics.Color
|
||||
|
||||
/**
|
||||
* Reusable error dialog component that shows network errors with retry/cancel options
|
||||
*
|
||||
* @param title Dialog title (default: "Network Error")
|
||||
* @param message Error message to display
|
||||
* @param onRetry Callback when user clicks Retry button
|
||||
* @param onDismiss Callback when user clicks Cancel or dismisses dialog
|
||||
* @param retryButtonText Text for retry button (default: "Try Again")
|
||||
* @param dismissButtonText Text for dismiss button (default: "Cancel")
|
||||
*/
|
||||
@Composable
|
||||
fun ErrorDialog(
|
||||
title: String = "Network Error",
|
||||
message: String,
|
||||
onRetry: () -> Unit,
|
||||
onDismiss: () -> Unit,
|
||||
retryButtonText: String = "Try Again",
|
||||
dismissButtonText: String = "Cancel"
|
||||
) {
|
||||
AlertDialog(
|
||||
onDismissRequest = onDismiss,
|
||||
title = {
|
||||
Text(
|
||||
text = title,
|
||||
style = MaterialTheme.typography.headlineSmall,
|
||||
color = MaterialTheme.colorScheme.error
|
||||
)
|
||||
},
|
||||
text = {
|
||||
Text(
|
||||
text = message,
|
||||
style = MaterialTheme.typography.bodyMedium
|
||||
)
|
||||
},
|
||||
confirmButton = {
|
||||
Button(
|
||||
onClick = {
|
||||
onDismiss()
|
||||
onRetry()
|
||||
},
|
||||
colors = ButtonDefaults.buttonColors(
|
||||
containerColor = MaterialTheme.colorScheme.primary
|
||||
)
|
||||
) {
|
||||
Text(retryButtonText)
|
||||
}
|
||||
},
|
||||
dismissButton = {
|
||||
TextButton(onClick = onDismiss) {
|
||||
Text(dismissButtonText)
|
||||
}
|
||||
}
|
||||
)
|
||||
}
|
||||
@@ -11,6 +11,7 @@ import androidx.compose.ui.Modifier
|
||||
import androidx.compose.ui.unit.dp
|
||||
import androidx.lifecycle.viewmodel.compose.viewModel
|
||||
import com.mycrib.android.ui.components.CompleteTaskDialog
|
||||
import com.mycrib.android.ui.components.ErrorDialog
|
||||
import com.mycrib.android.ui.components.task.TaskCard
|
||||
import com.mycrib.android.ui.components.task.TaskPill
|
||||
import com.mycrib.android.ui.utils.getIconFromName
|
||||
@@ -32,6 +33,16 @@ fun TasksScreen(
|
||||
var expandedColumns by remember { mutableStateOf(setOf<String>()) }
|
||||
var showCompleteDialog by remember { mutableStateOf(false) }
|
||||
var selectedTask by remember { mutableStateOf<com.mycrib.shared.models.TaskDetail?>(null) }
|
||||
var showErrorDialog by remember { mutableStateOf(false) }
|
||||
var errorMessage by remember { mutableStateOf("") }
|
||||
|
||||
// Show error dialog when tasks fail to load
|
||||
LaunchedEffect(tasksState) {
|
||||
if (tasksState is ApiResult.Error) {
|
||||
errorMessage = (tasksState as ApiResult.Error).message
|
||||
showErrorDialog = true
|
||||
}
|
||||
}
|
||||
|
||||
LaunchedEffect(Unit) {
|
||||
viewModel.loadTasks()
|
||||
@@ -64,7 +75,7 @@ fun TasksScreen(
|
||||
// No FAB on Tasks screen - tasks are added from within residences
|
||||
) { paddingValues ->
|
||||
when (tasksState) {
|
||||
is ApiResult.Idle, is ApiResult.Loading -> {
|
||||
is ApiResult.Idle, is ApiResult.Loading, is ApiResult.Error -> {
|
||||
Box(
|
||||
modifier = Modifier
|
||||
.fillMaxSize()
|
||||
@@ -74,25 +85,6 @@ fun TasksScreen(
|
||||
CircularProgressIndicator()
|
||||
}
|
||||
}
|
||||
is ApiResult.Error -> {
|
||||
Box(
|
||||
modifier = Modifier
|
||||
.fillMaxSize()
|
||||
.padding(paddingValues),
|
||||
contentAlignment = androidx.compose.ui.Alignment.Center
|
||||
) {
|
||||
Column(horizontalAlignment = androidx.compose.ui.Alignment.CenterHorizontally) {
|
||||
Text(
|
||||
text = "Error: ${(tasksState as ApiResult.Error).message}",
|
||||
color = MaterialTheme.colorScheme.error
|
||||
)
|
||||
Spacer(modifier = Modifier.height(8.dp))
|
||||
Button(onClick = { viewModel.loadTasks() }) {
|
||||
Text("Retry")
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
is ApiResult.Success -> {
|
||||
val taskData = (tasksState as ApiResult.Success).data
|
||||
val hasNoTasks = taskData.columns.all { it.tasks.isEmpty() }
|
||||
@@ -276,4 +268,18 @@ fun TasksScreen(
|
||||
}
|
||||
)
|
||||
}
|
||||
|
||||
// Show error dialog when network call fails
|
||||
if (showErrorDialog) {
|
||||
ErrorDialog(
|
||||
message = errorMessage,
|
||||
onRetry = {
|
||||
showErrorDialog = false
|
||||
viewModel.loadTasks()
|
||||
},
|
||||
onDismiss = {
|
||||
showErrorDialog = false
|
||||
}
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user