improve error handling
This commit is contained in:
@@ -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(
|
||||
|
||||
@@ -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")
|
||||
}
|
||||
}
|
||||
}
|
||||
)
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user