chore: merge the develop branch into master #1

Merged
reya merged 43 commits from develop into master 2026-05-23 00:50:13 +00:00
5 changed files with 159 additions and 36 deletions
Showing only changes of commit e02338fd52 - Show all commits

View File

@@ -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<Screen.Import> { backStackEntry ->
ImportScreen()
val isCreating by viewModel.isCreating.collectAsState()
ImportScreen(
isLoading = isCreating,
onSave = { secret ->
viewModel.import(secret)
}
)
}
composable<Screen.NewIdentity> { backStackEntry ->
val isCreating by viewModel.isCreating.collectAsState()

View File

@@ -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")
}
}
}
},
)
}

View File

@@ -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")
}
}
}
}

View File

@@ -56,10 +56,7 @@ class Nostr {
}
suspend fun connect() {
client?.addRelay(
url = RelayUrl.parse("wss://relay.damus.io"),
capabilities = RelayCapabilities.none()
)
try {
client?.addRelay(
url = RelayUrl.parse("wss://relay.primal.net"),
capabilities = RelayCapabilities.none()
@@ -69,10 +66,13 @@ class Nostr {
capabilities = RelayCapabilities.none()
)
client?.addRelay(
url = RelayUrl.parse("https://indexer.coracle.social"),
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() {

View File

@@ -119,6 +119,10 @@ class NostrViewModel(
}
}
fun import(secret: String) {
// TODO: Implement import
}
override fun onCleared() {
super.onCleared()
// Ensure all relays are disconnect