chore: merge the develop branch into master #1
@@ -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()
|
||||
|
||||
@@ -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")
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
)
|
||||
}
|
||||
|
||||
@@ -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")
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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() {
|
||||
|
||||
@@ -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
|
||||
|
||||
Reference in New Issue
Block a user