Implement error handling with ApiResultHandler on key screens
Applied the new error handling utilities to ResidencesScreen and ResidenceDetailScreen, replacing inline error displays with popup dialogs. Changes: - ResidencesScreen: Replaced inline error UI with ApiResultHandler - Shows error dialog popup instead of error message in center of screen - Maintains all functionality while improving UX - ResidenceDetailScreen: Comprehensive error handling implementation - Main residence loading uses ApiResultHandler with custom loading content - Generate report operation uses HandleErrors() extension - Delete residence operation uses HandleErrors() extension - All operations now show retry/cancel dialogs on error Benefits: - Consistent error handling UX across screens - Users can retry failed operations without navigating away - Cleaner code with less boilerplate error handling - Error dialogs are non-blocking and dismissible These screens now demonstrate the pattern for implementing error handling across the rest of the app using the error handling utilities. 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude <noreply@anthropic.com>
This commit is contained in:
@@ -15,7 +15,9 @@ import androidx.compose.ui.text.font.FontWeight
|
|||||||
import androidx.compose.ui.unit.dp
|
import androidx.compose.ui.unit.dp
|
||||||
import androidx.lifecycle.viewmodel.compose.viewModel
|
import androidx.lifecycle.viewmodel.compose.viewModel
|
||||||
import com.mycrib.android.ui.components.AddNewTaskDialog
|
import com.mycrib.android.ui.components.AddNewTaskDialog
|
||||||
|
import com.mycrib.android.ui.components.ApiResultHandler
|
||||||
import com.mycrib.android.ui.components.CompleteTaskDialog
|
import com.mycrib.android.ui.components.CompleteTaskDialog
|
||||||
|
import com.mycrib.android.ui.components.HandleErrors
|
||||||
import com.mycrib.android.ui.components.ManageUsersDialog
|
import com.mycrib.android.ui.components.ManageUsersDialog
|
||||||
import com.mycrib.android.ui.components.common.InfoCard
|
import com.mycrib.android.ui.components.common.InfoCard
|
||||||
import com.mycrib.android.ui.components.residence.PropertyDetailItem
|
import com.mycrib.android.ui.components.residence.PropertyDetailItem
|
||||||
@@ -116,6 +118,12 @@ fun ResidenceDetailScreen(
|
|||||||
}
|
}
|
||||||
|
|
||||||
// Handle generate report state
|
// Handle generate report state
|
||||||
|
// Handle errors for generate report
|
||||||
|
generateReportState.HandleErrors(
|
||||||
|
onRetry = { residenceViewModel.generateMaintenanceReport(residenceId) },
|
||||||
|
errorTitle = "Failed to Generate Report"
|
||||||
|
)
|
||||||
|
|
||||||
LaunchedEffect(generateReportState) {
|
LaunchedEffect(generateReportState) {
|
||||||
when (generateReportState) {
|
when (generateReportState) {
|
||||||
is ApiResult.Success -> {
|
is ApiResult.Success -> {
|
||||||
@@ -124,15 +132,16 @@ fun ResidenceDetailScreen(
|
|||||||
showReportSnackbar = true
|
showReportSnackbar = true
|
||||||
residenceViewModel.resetGenerateReportState()
|
residenceViewModel.resetGenerateReportState()
|
||||||
}
|
}
|
||||||
is ApiResult.Error -> {
|
|
||||||
reportMessage = (generateReportState as ApiResult.Error).message
|
|
||||||
showReportSnackbar = true
|
|
||||||
residenceViewModel.resetGenerateReportState()
|
|
||||||
}
|
|
||||||
else -> {}
|
else -> {}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Handle errors for delete residence
|
||||||
|
deleteState.HandleErrors(
|
||||||
|
onRetry = { residenceViewModel.deleteResidence(residenceId) },
|
||||||
|
errorTitle = "Failed to Delete Property"
|
||||||
|
)
|
||||||
|
|
||||||
// Handle delete residence state
|
// Handle delete residence state
|
||||||
LaunchedEffect(deleteState) {
|
LaunchedEffect(deleteState) {
|
||||||
when (deleteState) {
|
when (deleteState) {
|
||||||
@@ -140,11 +149,6 @@ fun ResidenceDetailScreen(
|
|||||||
residenceViewModel.resetDeleteResidenceState()
|
residenceViewModel.resetDeleteResidenceState()
|
||||||
onNavigateBack()
|
onNavigateBack()
|
||||||
}
|
}
|
||||||
is ApiResult.Error -> {
|
|
||||||
reportMessage = (deleteState as ApiResult.Error).message
|
|
||||||
showReportSnackbar = true
|
|
||||||
residenceViewModel.resetDeleteResidenceState()
|
|
||||||
}
|
|
||||||
else -> {}
|
else -> {}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -403,14 +407,16 @@ fun ResidenceDetailScreen(
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
) { paddingValues ->
|
) { paddingValues ->
|
||||||
when (residenceState) {
|
ApiResultHandler(
|
||||||
is ApiResult.Idle, is ApiResult.Loading -> {
|
state = residenceState,
|
||||||
Box(
|
onRetry = {
|
||||||
modifier = Modifier
|
residenceViewModel.getResidence(residenceId) { result ->
|
||||||
.fillMaxSize()
|
residenceState = result
|
||||||
.padding(paddingValues),
|
}
|
||||||
contentAlignment = Alignment.Center
|
},
|
||||||
) {
|
modifier = Modifier.padding(paddingValues),
|
||||||
|
errorTitle = "Failed to Load Property",
|
||||||
|
loadingContent = {
|
||||||
Column(
|
Column(
|
||||||
horizontalAlignment = Alignment.CenterHorizontally,
|
horizontalAlignment = Alignment.CenterHorizontally,
|
||||||
verticalArrangement = Arrangement.spacedBy(16.dp)
|
verticalArrangement = Arrangement.spacedBy(16.dp)
|
||||||
@@ -423,43 +429,7 @@ fun ResidenceDetailScreen(
|
|||||||
)
|
)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
) { residence ->
|
||||||
is ApiResult.Error -> {
|
|
||||||
Box(
|
|
||||||
modifier = Modifier
|
|
||||||
.fillMaxSize()
|
|
||||||
.padding(paddingValues),
|
|
||||||
contentAlignment = Alignment.Center
|
|
||||||
) {
|
|
||||||
Column(
|
|
||||||
horizontalAlignment = Alignment.CenterHorizontally,
|
|
||||||
verticalArrangement = Arrangement.spacedBy(12.dp)
|
|
||||||
) {
|
|
||||||
Icon(
|
|
||||||
Icons.Default.Error,
|
|
||||||
contentDescription = null,
|
|
||||||
modifier = Modifier.size(64.dp),
|
|
||||||
tint = MaterialTheme.colorScheme.error
|
|
||||||
)
|
|
||||||
Text(
|
|
||||||
text = "Error: ${(residenceState as ApiResult.Error).message}",
|
|
||||||
color = MaterialTheme.colorScheme.error
|
|
||||||
)
|
|
||||||
Button(
|
|
||||||
onClick = {
|
|
||||||
residenceViewModel.getResidence(residenceId) { result ->
|
|
||||||
residenceState = result
|
|
||||||
}
|
|
||||||
},
|
|
||||||
shape = RoundedCornerShape(12.dp)
|
|
||||||
) {
|
|
||||||
Text("Retry")
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
is ApiResult.Success -> {
|
|
||||||
val residence = (residenceState as ApiResult.Success).data
|
|
||||||
LazyColumn(
|
LazyColumn(
|
||||||
modifier = Modifier
|
modifier = Modifier
|
||||||
.fillMaxSize()
|
.fillMaxSize()
|
||||||
@@ -720,8 +690,6 @@ fun ResidenceDetailScreen(
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
else -> {}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -114,50 +114,12 @@ fun ResidencesScreen(
|
|||||||
},
|
},
|
||||||
floatingActionButtonPosition = FabPosition.End
|
floatingActionButtonPosition = FabPosition.End
|
||||||
) { paddingValues ->
|
) { paddingValues ->
|
||||||
when (myResidencesState) {
|
ApiResultHandler(
|
||||||
is ApiResult.Idle, is ApiResult.Loading -> {
|
state = myResidencesState,
|
||||||
Box(
|
onRetry = { viewModel.loadMyResidences() },
|
||||||
modifier = Modifier
|
modifier = Modifier.padding(paddingValues),
|
||||||
.fillMaxSize()
|
errorTitle = "Failed to Load Properties"
|
||||||
.padding(paddingValues),
|
) { response ->
|
||||||
contentAlignment = Alignment.Center
|
|
||||||
) {
|
|
||||||
CircularProgressIndicator()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
is ApiResult.Error -> {
|
|
||||||
Box(
|
|
||||||
modifier = Modifier
|
|
||||||
.fillMaxSize()
|
|
||||||
.padding(paddingValues),
|
|
||||||
contentAlignment = Alignment.Center
|
|
||||||
) {
|
|
||||||
Column(
|
|
||||||
horizontalAlignment = Alignment.CenterHorizontally,
|
|
||||||
verticalArrangement = Arrangement.spacedBy(12.dp)
|
|
||||||
) {
|
|
||||||
Icon(
|
|
||||||
Icons.Default.Error,
|
|
||||||
contentDescription = null,
|
|
||||||
modifier = Modifier.size(64.dp),
|
|
||||||
tint = MaterialTheme.colorScheme.error
|
|
||||||
)
|
|
||||||
Text(
|
|
||||||
text = "Error: ${(myResidencesState as ApiResult.Error).message}",
|
|
||||||
color = MaterialTheme.colorScheme.error,
|
|
||||||
style = MaterialTheme.typography.bodyLarge
|
|
||||||
)
|
|
||||||
Button(
|
|
||||||
onClick = { viewModel.loadMyResidences() },
|
|
||||||
shape = RoundedCornerShape(12.dp)
|
|
||||||
) {
|
|
||||||
Text("Retry")
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
is ApiResult.Success -> {
|
|
||||||
val response = (myResidencesState as ApiResult.Success).data
|
|
||||||
if (response.residences.isEmpty()) {
|
if (response.residences.isEmpty()) {
|
||||||
Box(
|
Box(
|
||||||
modifier = Modifier
|
modifier = Modifier
|
||||||
@@ -436,8 +398,6 @@ fun ResidencesScreen(
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
else -> {}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user