Add comprehensive error handling utilities for all screens
Created reusable error handling components that can be used across all screens in both Android and iOS apps to show retry/cancel dialogs when network calls fail. Android Components: - ApiResultHandler: Composable that automatically handles ApiResult states with loading indicators and error dialogs - HandleErrors(): Extension function for ApiResult to show error dialogs for operations that don't return display data - Updated ResidencesScreen to import ApiResultHandler iOS Components: - ViewStateHandler: SwiftUI view that handles loading/error/success states with automatic error alerts - handleErrors(): View modifier for automatic error monitoring - Both use the existing ErrorAlertModifier for consistent alerts Documentation: - Created ERROR_HANDLING.md with comprehensive usage guide - Includes examples for data loading and create/update/delete operations - Migration guide for updating existing screens - Best practices and testing guidelines These utilities make it easy to add consistent error handling with retry functionality to any screen that makes network calls, improving the user experience across the entire app. 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude <noreply@anthropic.com>
This commit is contained in:
@@ -0,0 +1,146 @@
|
||||
package com.mycrib.android.ui.components
|
||||
|
||||
import androidx.compose.foundation.layout.Box
|
||||
import androidx.compose.foundation.layout.fillMaxSize
|
||||
import androidx.compose.material3.CircularProgressIndicator
|
||||
import androidx.compose.runtime.*
|
||||
import androidx.compose.ui.Alignment
|
||||
import androidx.compose.ui.Modifier
|
||||
import com.mycrib.shared.network.ApiResult
|
||||
|
||||
/**
|
||||
* Handles ApiResult states automatically with loading, error dialogs, and success content.
|
||||
*
|
||||
* Example usage:
|
||||
* ```
|
||||
* val state by viewModel.dataState.collectAsState()
|
||||
*
|
||||
* ApiResultHandler(
|
||||
* state = state,
|
||||
* onRetry = { viewModel.loadData() }
|
||||
* ) { data ->
|
||||
* // Success content using the data
|
||||
* Text("Data: ${data.name}")
|
||||
* }
|
||||
* ```
|
||||
*
|
||||
* @param T The type of data in the ApiResult.Success
|
||||
* @param state The current ApiResult state
|
||||
* @param onRetry Callback to retry the operation when error occurs
|
||||
* @param modifier Modifier for the container
|
||||
* @param loadingContent Custom loading content (default: CircularProgressIndicator)
|
||||
* @param errorTitle Custom error dialog title (default: "Network Error")
|
||||
* @param content Content to show when state is Success
|
||||
*/
|
||||
@Composable
|
||||
fun <T> ApiResultHandler(
|
||||
state: ApiResult<T>,
|
||||
onRetry: () -> Unit,
|
||||
modifier: Modifier = Modifier,
|
||||
loadingContent: @Composable (() -> Unit)? = null,
|
||||
errorTitle: String = "Network Error",
|
||||
content: @Composable (T) -> Unit
|
||||
) {
|
||||
var showErrorDialog by remember { mutableStateOf(false) }
|
||||
var errorMessage by remember { mutableStateOf("") }
|
||||
|
||||
// Show error dialog when state changes to Error
|
||||
LaunchedEffect(state) {
|
||||
if (state is ApiResult.Error) {
|
||||
errorMessage = state.message
|
||||
showErrorDialog = true
|
||||
}
|
||||
}
|
||||
|
||||
when (state) {
|
||||
is ApiResult.Idle, is ApiResult.Loading -> {
|
||||
Box(
|
||||
modifier = modifier.fillMaxSize(),
|
||||
contentAlignment = Alignment.Center
|
||||
) {
|
||||
if (loadingContent != null) {
|
||||
loadingContent()
|
||||
} else {
|
||||
CircularProgressIndicator()
|
||||
}
|
||||
}
|
||||
}
|
||||
is ApiResult.Error -> {
|
||||
// Show loading indicator while error dialog is displayed
|
||||
Box(
|
||||
modifier = modifier.fillMaxSize(),
|
||||
contentAlignment = Alignment.Center
|
||||
) {
|
||||
CircularProgressIndicator()
|
||||
}
|
||||
}
|
||||
is ApiResult.Success -> {
|
||||
content(state.data)
|
||||
}
|
||||
}
|
||||
|
||||
// Error dialog
|
||||
if (showErrorDialog) {
|
||||
ErrorDialog(
|
||||
title = errorTitle,
|
||||
message = errorMessage,
|
||||
onRetry = {
|
||||
showErrorDialog = false
|
||||
onRetry()
|
||||
},
|
||||
onDismiss = {
|
||||
showErrorDialog = false
|
||||
}
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Extension function to observe ApiResult state and show error dialog
|
||||
* Use this for operations that don't return data to display (like create/update/delete)
|
||||
*
|
||||
* Example usage:
|
||||
* ```
|
||||
* val createState by viewModel.createState.collectAsState()
|
||||
*
|
||||
* createState.HandleErrors(
|
||||
* onRetry = { viewModel.createItem() }
|
||||
* )
|
||||
*
|
||||
* LaunchedEffect(createState) {
|
||||
* if (createState is ApiResult.Success) {
|
||||
* // Handle success
|
||||
* navController.popBackStack()
|
||||
* }
|
||||
* }
|
||||
* ```
|
||||
*/
|
||||
@Composable
|
||||
fun <T> ApiResult<T>.HandleErrors(
|
||||
onRetry: () -> Unit,
|
||||
errorTitle: String = "Network Error"
|
||||
) {
|
||||
var showErrorDialog by remember { mutableStateOf(false) }
|
||||
var errorMessage by remember { mutableStateOf("") }
|
||||
|
||||
LaunchedEffect(this) {
|
||||
if (this@HandleErrors is ApiResult.Error) {
|
||||
errorMessage = (this@HandleErrors as ApiResult.Error).message
|
||||
showErrorDialog = true
|
||||
}
|
||||
}
|
||||
|
||||
if (showErrorDialog) {
|
||||
ErrorDialog(
|
||||
title = errorTitle,
|
||||
message = errorMessage,
|
||||
onRetry = {
|
||||
showErrorDialog = false
|
||||
onRetry()
|
||||
},
|
||||
onDismiss = {
|
||||
showErrorDialog = false
|
||||
}
|
||||
)
|
||||
}
|
||||
}
|
||||
@@ -20,6 +20,7 @@ import androidx.compose.ui.graphics.Color
|
||||
import androidx.compose.ui.text.font.FontWeight
|
||||
import androidx.compose.ui.unit.dp
|
||||
import androidx.lifecycle.viewmodel.compose.viewModel
|
||||
import com.mycrib.android.ui.components.ApiResultHandler
|
||||
import com.mycrib.android.ui.components.JoinResidenceDialog
|
||||
import com.mycrib.android.ui.components.common.StatItem
|
||||
import com.mycrib.android.ui.components.residence.TaskStatChip
|
||||
|
||||
Reference in New Issue
Block a user