diff --git a/composeApp/src/androidMain/kotlin/su/reya/coop/App.kt b/composeApp/src/androidMain/kotlin/su/reya/coop/App.kt index 5ab4604..5cfac06 100644 --- a/composeApp/src/androidMain/kotlin/su/reya/coop/App.kt +++ b/composeApp/src/androidMain/kotlin/su/reya/coop/App.kt @@ -1,7 +1,12 @@ package su.reya.coop +import androidx.compose.foundation.isSystemInDarkTheme import androidx.compose.material3.ExperimentalMaterial3ExpressiveApi import androidx.compose.material3.MaterialExpressiveTheme +import androidx.compose.material3.darkColorScheme +import androidx.compose.material3.dynamicDarkColorScheme +import androidx.compose.material3.dynamicLightColorScheme +import androidx.compose.material3.lightColorScheme import androidx.compose.runtime.Composable import androidx.compose.runtime.LaunchedEffect import androidx.compose.runtime.collectAsState @@ -29,11 +34,23 @@ fun App(dbPath: String) { val secretStore = remember { SecretStore(context) } val viewModel: NostrViewModel = viewModel { NostrViewModel(nostr, secretStore) } + val darkMode = isSystemInDarkTheme() + val colorScheme = when { + android.os.Build.VERSION.SDK_INT >= android.os.Build.VERSION_CODES.S -> { + if (darkMode) dynamicDarkColorScheme(context) else dynamicLightColorScheme(context) + } + + darkMode -> darkColorScheme() + else -> lightColorScheme() + } + LaunchedEffect(Unit) { viewModel.initAndConnect(dbPath) } - MaterialExpressiveTheme { + MaterialExpressiveTheme( + colorScheme = colorScheme, + ) { rememberCoroutineScope() val navController = rememberNavController() val hasSecret by viewModel.hasSecret.collectAsState(initial = null) @@ -65,7 +82,14 @@ fun App(dbPath: String) { ) } composable { backStackEntry -> - ImportScreen() + val isCreating by viewModel.isCreating.collectAsState() + + ImportScreen( + isLoading = isCreating, + onSave = { secret -> + viewModel.import(secret) + } + ) } composable { backStackEntry -> val isCreating by viewModel.isCreating.collectAsState() diff --git a/composeApp/src/androidMain/kotlin/su/reya/coop/screens/HomeScreen.kt b/composeApp/src/androidMain/kotlin/su/reya/coop/screens/HomeScreen.kt index ad47bfb..7960316 100644 --- a/composeApp/src/androidMain/kotlin/su/reya/coop/screens/HomeScreen.kt +++ b/composeApp/src/androidMain/kotlin/su/reya/coop/screens/HomeScreen.kt @@ -1,26 +1,73 @@ 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.fillMaxWidth import androidx.compose.foundation.layout.height -import androidx.compose.material3.Button +import androidx.compose.foundation.layout.padding +import androidx.compose.foundation.lazy.LazyColumn +import androidx.compose.foundation.text.input.rememberTextFieldState +import androidx.compose.material3.AppBarWithSearch +import androidx.compose.material3.ExperimentalMaterial3Api +import androidx.compose.material3.ExperimentalMaterial3ExpressiveApi +import androidx.compose.material3.Scaffold +import androidx.compose.material3.SearchBarDefaults import androidx.compose.material3.Text +import androidx.compose.material3.rememberSearchBarState import androidx.compose.runtime.Composable -import androidx.compose.ui.Alignment +import androidx.compose.runtime.rememberCoroutineScope import androidx.compose.ui.Modifier +import androidx.compose.ui.input.nestedscroll.nestedScroll +import androidx.compose.ui.semantics.clearAndSetSemantics import androidx.compose.ui.unit.dp +import kotlinx.coroutines.launch +@OptIn(ExperimentalMaterial3ExpressiveApi::class, ExperimentalMaterial3Api::class) @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") - } + val scope = rememberCoroutineScope() + val searchState = rememberSearchBarState() + val textState = rememberTextFieldState() + + val scrollBehavior = SearchBarDefaults.enterAlwaysSearchBarScrollBehavior() + + val inputField = + @Composable { + SearchBarDefaults.InputField( + textFieldState = textState, + searchBarState = searchState, + onSearch = { scope.launch { searchState.animateToCollapsed() } }, + placeholder = { + Text(modifier = Modifier.clearAndSetSemantics() {}, text = "Search") + }, + ) } - } + + Scaffold( + modifier = Modifier.nestedScroll(scrollBehavior.nestedScrollConnection), + topBar = { + AppBarWithSearch( + state = searchState, + inputField = inputField, + scrollBehavior = scrollBehavior, + ) + }, + content = { innerPadding -> + LazyColumn( + modifier = Modifier + .fillMaxSize() + .padding(innerPadding), + ) { + items(count = 100) { index -> + Box( + modifier = Modifier + .fillMaxWidth() + .height(50.dp) + ) { + Text("Chat $index") + } + } + } + }, + ) } diff --git a/composeApp/src/androidMain/kotlin/su/reya/coop/screens/ImportScreen.kt b/composeApp/src/androidMain/kotlin/su/reya/coop/screens/ImportScreen.kt index 10526dd..7100c1e 100644 --- a/composeApp/src/androidMain/kotlin/su/reya/coop/screens/ImportScreen.kt +++ b/composeApp/src/androidMain/kotlin/su/reya/coop/screens/ImportScreen.kt @@ -1,15 +1,63 @@ package su.reya.coop.screens -import androidx.compose.foundation.layout.Box +import androidx.compose.foundation.layout.Arrangement +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.padding +import androidx.compose.foundation.rememberScrollState +import androidx.compose.foundation.verticalScroll +import androidx.compose.material3.Button +import androidx.compose.material3.ExperimentalMaterial3ExpressiveApi +import androidx.compose.material3.LoadingIndicator +import androidx.compose.material3.OutlinedTextField 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.unit.dp +@OptIn(ExperimentalMaterial3ExpressiveApi::class) @Composable -fun ImportScreen() { - Box(modifier = Modifier.fillMaxSize(), contentAlignment = Alignment.Center) { - Text("Import Screen") +fun ImportScreen( + isLoading: Boolean, + onSave: (secret: String) -> Unit +) { + var secret by remember { mutableStateOf("") } + + Column( + modifier = Modifier + .fillMaxSize() + .padding(16.dp) + .verticalScroll(rememberScrollState()), + horizontalAlignment = Alignment.CenterHorizontally, + verticalArrangement = Arrangement.spacedBy(16.dp) + ) { + OutlinedTextField( + value = secret, + onValueChange = { secret = it }, + label = { Text("Enter nsec or bunker") }, + modifier = Modifier.fillMaxWidth(), + singleLine = true, + ) + Spacer(modifier = Modifier.weight(1f)) + Button( + onClick = { + onSave(secret) + }, + modifier = Modifier.fillMaxWidth(), + enabled = secret.isNotBlank() && !isLoading, + ) { + if (isLoading) { + LoadingIndicator() + } else { + Text("Save & Continue") + } + } } } diff --git a/shared/src/commonMain/kotlin/su/reya/coop/Nostr.kt b/shared/src/commonMain/kotlin/su/reya/coop/Nostr.kt index 42d6bb4..fe67e6c 100644 --- a/shared/src/commonMain/kotlin/su/reya/coop/Nostr.kt +++ b/shared/src/commonMain/kotlin/su/reya/coop/Nostr.kt @@ -56,23 +56,23 @@ class Nostr { } suspend fun connect() { - client?.addRelay( - url = RelayUrl.parse("wss://relay.damus.io"), - capabilities = RelayCapabilities.none() - ) - client?.addRelay( - url = RelayUrl.parse("wss://relay.primal.net"), - capabilities = RelayCapabilities.none() - ) - client?.addRelay( - url = RelayUrl.parse("wss://user.kindpag.es"), - capabilities = RelayCapabilities.none() - ) - client?.addRelay( - url = RelayUrl.parse("https://indexer.coracle.social"), - capabilities = RelayCapabilities.gossip() - ) - client?.connect() + try { + client?.addRelay( + url = RelayUrl.parse("wss://relay.primal.net"), + capabilities = RelayCapabilities.none() + ) + client?.addRelay( + url = RelayUrl.parse("wss://user.kindpag.es"), + capabilities = RelayCapabilities.none() + ) + client?.addRelay( + url = RelayUrl.parse("wss://indexer.coracle.social"), + capabilities = RelayCapabilities.gossip() + ) + client?.connect() + } catch (e: Exception) { + println("Failed to connect to relays: ${e.message}") + } } suspend fun disconnect() { diff --git a/shared/src/commonMain/kotlin/su/reya/coop/NostrViewModel.kt b/shared/src/commonMain/kotlin/su/reya/coop/NostrViewModel.kt index e6e5361..1e86250 100644 --- a/shared/src/commonMain/kotlin/su/reya/coop/NostrViewModel.kt +++ b/shared/src/commonMain/kotlin/su/reya/coop/NostrViewModel.kt @@ -45,7 +45,7 @@ class NostrViewModel( // Connect to bootstrap relays nostr.connect() - + // Get user's signer secret val secret = secretStore.get("user_signer") @@ -119,6 +119,10 @@ class NostrViewModel( } } + fun import(secret: String) { + // TODO: Implement import + } + override fun onCleared() { super.onCleared() // Ensure all relays are disconnect