add simple create identity flow
This commit is contained in:
@@ -1,6 +1,7 @@
|
||||
package su.reya.coop
|
||||
|
||||
import androidx.compose.material3.MaterialTheme
|
||||
import androidx.compose.material3.ExperimentalMaterial3ExpressiveApi
|
||||
import androidx.compose.material3.MaterialExpressiveTheme
|
||||
import androidx.compose.runtime.Composable
|
||||
import androidx.compose.runtime.LaunchedEffect
|
||||
import androidx.compose.runtime.collectAsState
|
||||
@@ -15,7 +16,13 @@ import androidx.navigation.compose.rememberNavController
|
||||
import androidx.navigation.toRoute
|
||||
import kotlinx.coroutines.flow.flow
|
||||
import su.reya.coop.coop.storage.SecretStore
|
||||
import su.reya.coop.screens.ChatScreen
|
||||
import su.reya.coop.screens.HomeScreen
|
||||
import su.reya.coop.screens.ImportScreen
|
||||
import su.reya.coop.screens.NewIdentityScreen
|
||||
import su.reya.coop.screens.OnboardingScreen
|
||||
|
||||
@OptIn(ExperimentalMaterial3ExpressiveApi::class)
|
||||
@Composable
|
||||
fun App(dbPath: String) {
|
||||
val context = LocalContext.current
|
||||
@@ -27,7 +34,7 @@ fun App(dbPath: String) {
|
||||
viewModel.initAndConnect(dbPath)
|
||||
}
|
||||
|
||||
MaterialTheme {
|
||||
MaterialExpressiveTheme {
|
||||
rememberCoroutineScope()
|
||||
val navController = rememberNavController()
|
||||
|
||||
@@ -41,24 +48,32 @@ fun App(dbPath: String) {
|
||||
|
||||
if (hasSecret == null) {
|
||||
// Loading state
|
||||
return@MaterialTheme
|
||||
return@MaterialExpressiveTheme
|
||||
}
|
||||
|
||||
NavHost(
|
||||
navController = navController,
|
||||
startDestination = if (hasSecret == true) Screen.Onboarding else Screen.Home
|
||||
startDestination = if (hasSecret == true) Screen.Home else Screen.Onboarding
|
||||
) {
|
||||
composable<Screen.Onboarding> { backStackEntry ->
|
||||
OnboardingScreen(
|
||||
onOpenImport = { navController.navigate(Screen.Import) },
|
||||
onOpenNew = { navController.navigate(Screen.New) }
|
||||
onOpenNew = { navController.navigate(Screen.NewIdentity) }
|
||||
)
|
||||
}
|
||||
composable<Screen.Import> { backStackEntry ->
|
||||
ImportScreen()
|
||||
}
|
||||
composable<Screen.New> { backStackEntry ->
|
||||
NewScreen()
|
||||
composable<Screen.NewIdentity> { backStackEntry ->
|
||||
val isCreating by viewModel.isCreating.collectAsState()
|
||||
|
||||
NewIdentityScreen(
|
||||
isLoading = isCreating,
|
||||
onSave = { name, bio, uri ->
|
||||
viewModel.createIdentity(name, bio, uri?.toString())
|
||||
navController.navigate(Screen.Home)
|
||||
}
|
||||
)
|
||||
}
|
||||
composable<Screen.Home> { backStackEntry ->
|
||||
HomeScreen(
|
||||
|
||||
@@ -4,8 +4,6 @@ import android.os.Bundle
|
||||
import androidx.activity.ComponentActivity
|
||||
import androidx.activity.compose.setContent
|
||||
import androidx.activity.enableEdgeToEdge
|
||||
import androidx.compose.runtime.Composable
|
||||
import androidx.compose.ui.tooling.preview.Preview
|
||||
import java.io.File
|
||||
|
||||
class MainActivity : ComponentActivity() {
|
||||
@@ -22,9 +20,3 @@ class MainActivity : ComponentActivity() {
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@Preview
|
||||
@Composable
|
||||
fun AppAndroidPreview() {
|
||||
App()
|
||||
}
|
||||
20
composeApp/src/androidMain/kotlin/su/reya/coop/Navigation.kt
Normal file
20
composeApp/src/androidMain/kotlin/su/reya/coop/Navigation.kt
Normal file
@@ -0,0 +1,20 @@
|
||||
package su.reya.coop
|
||||
|
||||
import kotlinx.serialization.Serializable
|
||||
|
||||
sealed interface Screen {
|
||||
@Serializable
|
||||
data object Home : Screen
|
||||
|
||||
@Serializable
|
||||
data class Chat(val id: String) : Screen
|
||||
|
||||
@Serializable
|
||||
data object Onboarding : Screen
|
||||
|
||||
@Serializable
|
||||
data object Import : Screen
|
||||
|
||||
@Serializable
|
||||
data object NewIdentity : Screen
|
||||
}
|
||||
@@ -1,82 +0,0 @@
|
||||
package su.reya.coop
|
||||
|
||||
import androidx.compose.foundation.layout.Box
|
||||
import androidx.compose.foundation.layout.Column
|
||||
import androidx.compose.foundation.layout.Spacer
|
||||
import androidx.compose.foundation.layout.fillMaxSize
|
||||
import androidx.compose.foundation.layout.height
|
||||
import androidx.compose.material3.Button
|
||||
import androidx.compose.material3.Text
|
||||
import androidx.compose.runtime.Composable
|
||||
import androidx.compose.ui.Alignment
|
||||
import androidx.compose.ui.Modifier
|
||||
import androidx.compose.ui.unit.dp
|
||||
import kotlinx.serialization.Serializable
|
||||
|
||||
sealed interface Screen {
|
||||
@Serializable
|
||||
data object Home : Screen
|
||||
|
||||
@Serializable
|
||||
data class Chat(val id: String) : Screen
|
||||
|
||||
@Serializable
|
||||
data object Onboarding : Screen
|
||||
|
||||
@Serializable
|
||||
data object Import : Screen
|
||||
|
||||
@Serializable
|
||||
data object New : Screen
|
||||
}
|
||||
|
||||
@Composable
|
||||
fun HomeScreen(onOpenChat: (String) -> Unit) {
|
||||
Box(modifier = Modifier.fillMaxSize(), contentAlignment = Alignment.Center) {
|
||||
Column(horizontalAlignment = Alignment.CenterHorizontally) {
|
||||
Text("Home Screen")
|
||||
Spacer(modifier = Modifier.height(16.dp))
|
||||
Button(onClick = { onOpenChat("123") }) {
|
||||
Text("Open Chat 123")
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@Composable
|
||||
fun ChatScreen(id: String) {
|
||||
Box(modifier = Modifier.fillMaxSize(), contentAlignment = Alignment.Center) {
|
||||
Text("Chat Screen (ID: $id)")
|
||||
}
|
||||
}
|
||||
|
||||
@Composable
|
||||
fun OnboardingScreen(onOpenImport: () -> Unit, onOpenNew: () -> Unit) {
|
||||
Box(modifier = Modifier.fillMaxSize(), contentAlignment = Alignment.Center) {
|
||||
Column(horizontalAlignment = Alignment.CenterHorizontally) {
|
||||
Text("Onboarding Screen")
|
||||
Spacer(modifier = Modifier.height(16.dp))
|
||||
Button(onClick = onOpenImport) {
|
||||
Text("Import")
|
||||
}
|
||||
Spacer(modifier = Modifier.height(8.dp))
|
||||
Button(onClick = onOpenNew) {
|
||||
Text("New")
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@Composable
|
||||
fun ImportScreen() {
|
||||
Box(modifier = Modifier.fillMaxSize(), contentAlignment = Alignment.Center) {
|
||||
Text("Import Screen")
|
||||
}
|
||||
}
|
||||
|
||||
@Composable
|
||||
fun NewScreen() {
|
||||
Box(modifier = Modifier.fillMaxSize(), contentAlignment = Alignment.Center) {
|
||||
Text("New Screen")
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,15 @@
|
||||
package su.reya.coop.screens
|
||||
|
||||
import androidx.compose.foundation.layout.Box
|
||||
import androidx.compose.foundation.layout.fillMaxSize
|
||||
import androidx.compose.material3.Text
|
||||
import androidx.compose.runtime.Composable
|
||||
import androidx.compose.ui.Alignment
|
||||
import androidx.compose.ui.Modifier
|
||||
|
||||
@Composable
|
||||
fun ChatScreen(id: String) {
|
||||
Box(modifier = Modifier.fillMaxSize(), contentAlignment = Alignment.Center) {
|
||||
Text("Chat Screen (ID: $id)")
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,26 @@
|
||||
package su.reya.coop.screens
|
||||
|
||||
import androidx.compose.foundation.layout.Box
|
||||
import androidx.compose.foundation.layout.Column
|
||||
import androidx.compose.foundation.layout.Spacer
|
||||
import androidx.compose.foundation.layout.fillMaxSize
|
||||
import androidx.compose.foundation.layout.height
|
||||
import androidx.compose.material3.Button
|
||||
import androidx.compose.material3.Text
|
||||
import androidx.compose.runtime.Composable
|
||||
import androidx.compose.ui.Alignment
|
||||
import androidx.compose.ui.Modifier
|
||||
import androidx.compose.ui.unit.dp
|
||||
|
||||
@Composable
|
||||
fun HomeScreen(onOpenChat: (String) -> Unit) {
|
||||
Box(modifier = Modifier.fillMaxSize(), contentAlignment = Alignment.Center) {
|
||||
Column(horizontalAlignment = Alignment.CenterHorizontally) {
|
||||
Text("Home Screen")
|
||||
Spacer(modifier = Modifier.height(16.dp))
|
||||
Button(onClick = { onOpenChat("123") }) {
|
||||
Text("Open Chat 123")
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,15 @@
|
||||
package su.reya.coop.screens
|
||||
|
||||
import androidx.compose.foundation.layout.Box
|
||||
import androidx.compose.foundation.layout.fillMaxSize
|
||||
import androidx.compose.material3.Text
|
||||
import androidx.compose.runtime.Composable
|
||||
import androidx.compose.ui.Alignment
|
||||
import androidx.compose.ui.Modifier
|
||||
|
||||
@Composable
|
||||
fun ImportScreen() {
|
||||
Box(modifier = Modifier.fillMaxSize(), contentAlignment = Alignment.Center) {
|
||||
Text("Import Screen")
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,118 @@
|
||||
package su.reya.coop.screens
|
||||
|
||||
import android.net.Uri
|
||||
import androidx.activity.compose.rememberLauncherForActivityResult
|
||||
import androidx.activity.result.contract.ActivityResultContracts
|
||||
import androidx.compose.foundation.layout.Arrangement
|
||||
import androidx.compose.foundation.layout.Box
|
||||
import androidx.compose.foundation.layout.Column
|
||||
import androidx.compose.foundation.layout.Spacer
|
||||
import androidx.compose.foundation.layout.fillMaxSize
|
||||
import androidx.compose.foundation.layout.fillMaxWidth
|
||||
import androidx.compose.foundation.layout.height
|
||||
import androidx.compose.foundation.layout.padding
|
||||
import androidx.compose.foundation.layout.size
|
||||
import androidx.compose.foundation.rememberScrollState
|
||||
import androidx.compose.foundation.shape.CircleShape
|
||||
import androidx.compose.foundation.verticalScroll
|
||||
import androidx.compose.material3.Button
|
||||
import androidx.compose.material3.ExperimentalMaterial3ExpressiveApi
|
||||
import androidx.compose.material3.LoadingIndicator
|
||||
import androidx.compose.material3.MaterialTheme
|
||||
import androidx.compose.material3.OutlinedTextField
|
||||
import androidx.compose.material3.Surface
|
||||
import androidx.compose.material3.Text
|
||||
import androidx.compose.runtime.Composable
|
||||
import androidx.compose.runtime.getValue
|
||||
import androidx.compose.runtime.mutableStateOf
|
||||
import androidx.compose.runtime.remember
|
||||
import androidx.compose.runtime.setValue
|
||||
import androidx.compose.ui.Alignment
|
||||
import androidx.compose.ui.Modifier
|
||||
import androidx.compose.ui.draw.clip
|
||||
import androidx.compose.ui.layout.ContentScale
|
||||
import androidx.compose.ui.unit.dp
|
||||
import coil3.compose.AsyncImage
|
||||
|
||||
@OptIn(ExperimentalMaterial3ExpressiveApi::class)
|
||||
@Composable
|
||||
fun NewIdentityScreen(
|
||||
isLoading: Boolean,
|
||||
onSave: (name: String, bio: String, picture: Uri?) -> Unit
|
||||
) {
|
||||
var name by remember { mutableStateOf("") }
|
||||
var bio by remember { mutableStateOf("") }
|
||||
var picture by remember { mutableStateOf<Uri?>(null) }
|
||||
|
||||
val launcher =
|
||||
rememberLauncherForActivityResult(contract = ActivityResultContracts.GetContent()) { uri: Uri? ->
|
||||
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()
|
||||
|
||||
) {
|
||||
//
|
||||
}
|
||||
}
|
||||
}
|
||||
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")
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,30 @@
|
||||
package su.reya.coop.screens
|
||||
|
||||
import androidx.compose.foundation.layout.Box
|
||||
import androidx.compose.foundation.layout.Column
|
||||
import androidx.compose.foundation.layout.Spacer
|
||||
import androidx.compose.foundation.layout.fillMaxSize
|
||||
import androidx.compose.foundation.layout.height
|
||||
import androidx.compose.material3.Button
|
||||
import androidx.compose.material3.Text
|
||||
import androidx.compose.runtime.Composable
|
||||
import androidx.compose.ui.Alignment
|
||||
import androidx.compose.ui.Modifier
|
||||
import androidx.compose.ui.unit.dp
|
||||
|
||||
@Composable
|
||||
fun OnboardingScreen(onOpenImport: () -> Unit, onOpenNew: () -> Unit) {
|
||||
Box(modifier = Modifier.fillMaxSize(), contentAlignment = Alignment.Center) {
|
||||
Column(horizontalAlignment = Alignment.CenterHorizontally) {
|
||||
Text("Onboarding Screen")
|
||||
Spacer(modifier = Modifier.height(16.dp))
|
||||
Button(onClick = onOpenImport) {
|
||||
Text("Import")
|
||||
}
|
||||
Spacer(modifier = Modifier.height(8.dp))
|
||||
Button(onClick = onOpenNew) {
|
||||
Text("New")
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user