improve error handling

This commit is contained in:
2026-05-06 14:25:04 +07:00
parent eb2f543f53
commit 8b5a8b0e48
3 changed files with 156 additions and 101 deletions

View File

@@ -3,6 +3,7 @@ package su.reya.coop
import androidx.compose.foundation.isSystemInDarkTheme
import androidx.compose.material3.ExperimentalMaterial3ExpressiveApi
import androidx.compose.material3.MaterialExpressiveTheme
import androidx.compose.material3.SnackbarHostState
import androidx.compose.material3.darkColorScheme
import androidx.compose.material3.dynamicDarkColorScheme
import androidx.compose.material3.dynamicLightColorScheme
@@ -32,6 +33,10 @@ val LocalNostrViewModel = staticCompositionLocalOf<NostrViewModel> {
error("No NostrViewModel provided")
}
val LocalSnackbarHostState = staticCompositionLocalOf<SnackbarHostState> {
error("No SnackbarHostState provided")
}
@OptIn(ExperimentalMaterial3ExpressiveApi::class)
@Composable
fun App(dbPath: String) {
@@ -53,24 +58,32 @@ fun App(dbPath: String) {
else -> expressiveLightColorScheme()
}
// Snackbar
val snackbarHostState = remember { SnackbarHostState() }
LaunchedEffect(Unit) {
viewModel.initAndConnect(dbPath)
viewModel.startNotificationHandler()
viewModel.getChatRooms()
viewModel.errorEvents.collect { message ->
snackbarHostState.showSnackbar(message)
}
}
MaterialExpressiveTheme(
colorScheme = colorScheme,
) {
CompositionLocalProvider(LocalNostrViewModel provides viewModel) {
CompositionLocalProvider(
LocalNostrViewModel provides viewModel,
LocalSnackbarHostState provides snackbarHostState,
) {
rememberCoroutineScope()
val navController = rememberNavController()
val hasSecret by viewModel.hasSecret.collectAsState(initial = null)
val emptySecret by viewModel.emptySecret.collectAsState(initial = null)
LaunchedEffect(hasSecret) {
LaunchedEffect(emptySecret) {
// Navigate to the home screen if the secret is already set
if (hasSecret == true) {
// Start a background notification handler
viewModel.startNotificationHandler()
if (emptySecret == false) {
// Get chat rooms
viewModel.getChatRooms()
// Navigate to the home screen
@@ -81,11 +94,11 @@ fun App(dbPath: String) {
}
// Show loading screen while initializing
if (hasSecret == null) return@CompositionLocalProvider
if (emptySecret == null) return@CompositionLocalProvider
NavHost(
navController = navController,
startDestination = if (hasSecret == true) Screen.Home else Screen.Onboarding
startDestination = if (emptySecret == false) Screen.Home else Screen.Onboarding
) {
composable<Screen.Onboarding> { backStackEntry ->
OnboardingScreen(

View File

@@ -20,6 +20,8 @@ import androidx.compose.material3.ExperimentalMaterial3ExpressiveApi
import androidx.compose.material3.LoadingIndicator
import androidx.compose.material3.MaterialTheme
import androidx.compose.material3.OutlinedTextField
import androidx.compose.material3.Scaffold
import androidx.compose.material3.SnackbarHost
import androidx.compose.material3.Surface
import androidx.compose.material3.Text
import androidx.compose.runtime.Composable
@@ -33,6 +35,7 @@ import androidx.compose.ui.draw.clip
import androidx.compose.ui.layout.ContentScale
import androidx.compose.ui.unit.dp
import coil3.compose.AsyncImage
import su.reya.coop.LocalSnackbarHostState
@OptIn(ExperimentalMaterial3ExpressiveApi::class)
@Composable
@@ -40,6 +43,7 @@ fun NewIdentityScreen(
isLoading: Boolean,
onSave: (name: String, bio: String, picture: Uri?) -> Unit
) {
val snackbarHostState = LocalSnackbarHostState.current
var name by remember { mutableStateOf("") }
var bio by remember { mutableStateOf("") }
var picture by remember { mutableStateOf<Uri?>(null) }
@@ -49,70 +53,79 @@ fun NewIdentityScreen(
picture = uri
}
Column(
modifier = Modifier
.fillMaxSize()
.padding(16.dp)
.verticalScroll(rememberScrollState()),
horizontalAlignment = Alignment.CenterHorizontally,
verticalArrangement = Arrangement.spacedBy(16.dp)
) {
Text(
text = "New Identity",
style = MaterialTheme.typography.headlineMediumEmphasized
)
Box(
modifier = Modifier
.size(120.dp)
.clip(CircleShape),
contentAlignment = Alignment.Center
) {
if (picture != null) {
AsyncImage(
model = picture,
contentDescription = "Profile picture",
modifier = Modifier.fillMaxSize(),
contentScale = ContentScale.Crop
)
} else {
Surface(
color = MaterialTheme.colorScheme.surfaceVariant,
modifier = Modifier.fillMaxSize()
Scaffold(
containerColor = MaterialTheme.colorScheme.surfaceContainer,
snackbarHost = { SnackbarHost(snackbarHostState) },
content = { innerPadding ->
Surface(
modifier = Modifier
.fillMaxSize()
.padding(top = innerPadding.calculateTopPadding()),
color = MaterialTheme.colorScheme.surfaceContainer,
) {
Column(
modifier = Modifier
.fillMaxSize()
.padding(16.dp)
.verticalScroll(rememberScrollState()),
horizontalAlignment = Alignment.CenterHorizontally,
verticalArrangement = Arrangement.spacedBy(16.dp)
) {
//
Box(
modifier = Modifier
.size(120.dp)
.clip(CircleShape),
contentAlignment = Alignment.Center
) {
if (picture != null) {
AsyncImage(
model = picture,
contentDescription = "Profile picture",
modifier = Modifier.fillMaxSize(),
contentScale = ContentScale.Crop
)
} else {
Surface(
color = MaterialTheme.colorScheme.surfaceVariant,
modifier = Modifier.fillMaxSize()
) {
//
}
}
}
OutlinedTextField(
value = name,
onValueChange = { name = it },
label = { Text("Name") },
modifier = Modifier.fillMaxWidth(),
singleLine = true,
)
OutlinedTextField(
value = bio,
onValueChange = { bio = it },
label = { Text("Bio:") },
modifier = Modifier
.fillMaxWidth()
.height(150.dp),
minLines = 3,
)
Spacer(modifier = Modifier.weight(1f))
Button(
onClick = {
onSave(name, bio, picture)
},
modifier = Modifier.fillMaxWidth(),
enabled = name.isNotBlank() && !isLoading,
) {
if (isLoading) {
LoadingIndicator()
} else {
Text("Save & Continue")
}
}
}
}
}
OutlinedTextField(
value = name,
onValueChange = { name = it },
label = { Text("Name") },
modifier = Modifier.fillMaxWidth(),
singleLine = true,
)
OutlinedTextField(
value = bio,
onValueChange = { bio = it },
label = { Text("Bio:") },
modifier = Modifier
.fillMaxWidth()
.height(150.dp),
minLines = 3,
)
Spacer(modifier = Modifier.weight(1f))
Button(
onClick = {
onSave(name, bio, picture)
},
modifier = Modifier.fillMaxWidth(),
enabled = name.isNotBlank() && !isLoading,
) {
if (isLoading) {
LoadingIndicator()
} else {
Text("Save & Continue")
}
}
}
)
}