chore: merge the develop branch into master #1
@@ -23,7 +23,9 @@ kotlin {
|
|||||||
implementation("org.jetbrains.kotlinx:kotlinx-serialization-json:1.8.0")
|
implementation("org.jetbrains.kotlinx:kotlinx-serialization-json:1.8.0")
|
||||||
implementation("androidx.datastore:datastore-preferences:1.2.1")
|
implementation("androidx.datastore:datastore-preferences:1.2.1")
|
||||||
implementation("androidx.datastore:datastore-preferences-core:1.2.1")
|
implementation("androidx.datastore:datastore-preferences-core:1.2.1")
|
||||||
implementation("org.jetbrains.compose.material3:material3*:1.10.0-alpha05")
|
implementation("org.jetbrains.compose.material3:material3:1.11.0-alpha07")
|
||||||
|
implementation("io.coil-kt.coil3:coil-compose:3.4.0")
|
||||||
|
implementation("io.coil-kt.coil3:coil-network-okhttp:3.4.0")
|
||||||
}
|
}
|
||||||
commonMain.dependencies {
|
commonMain.dependencies {
|
||||||
implementation(libs.compose.runtime)
|
implementation(libs.compose.runtime)
|
||||||
|
|||||||
@@ -1,6 +1,7 @@
|
|||||||
package su.reya.coop
|
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.Composable
|
||||||
import androidx.compose.runtime.LaunchedEffect
|
import androidx.compose.runtime.LaunchedEffect
|
||||||
import androidx.compose.runtime.collectAsState
|
import androidx.compose.runtime.collectAsState
|
||||||
@@ -15,7 +16,13 @@ import androidx.navigation.compose.rememberNavController
|
|||||||
import androidx.navigation.toRoute
|
import androidx.navigation.toRoute
|
||||||
import kotlinx.coroutines.flow.flow
|
import kotlinx.coroutines.flow.flow
|
||||||
import su.reya.coop.coop.storage.SecretStore
|
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
|
@Composable
|
||||||
fun App(dbPath: String) {
|
fun App(dbPath: String) {
|
||||||
val context = LocalContext.current
|
val context = LocalContext.current
|
||||||
@@ -27,7 +34,7 @@ fun App(dbPath: String) {
|
|||||||
viewModel.initAndConnect(dbPath)
|
viewModel.initAndConnect(dbPath)
|
||||||
}
|
}
|
||||||
|
|
||||||
MaterialTheme {
|
MaterialExpressiveTheme {
|
||||||
rememberCoroutineScope()
|
rememberCoroutineScope()
|
||||||
val navController = rememberNavController()
|
val navController = rememberNavController()
|
||||||
|
|
||||||
@@ -41,24 +48,32 @@ fun App(dbPath: String) {
|
|||||||
|
|
||||||
if (hasSecret == null) {
|
if (hasSecret == null) {
|
||||||
// Loading state
|
// Loading state
|
||||||
return@MaterialTheme
|
return@MaterialExpressiveTheme
|
||||||
}
|
}
|
||||||
|
|
||||||
NavHost(
|
NavHost(
|
||||||
navController = navController,
|
navController = navController,
|
||||||
startDestination = if (hasSecret == true) Screen.Onboarding else Screen.Home
|
startDestination = if (hasSecret == true) Screen.Home else Screen.Onboarding
|
||||||
) {
|
) {
|
||||||
composable<Screen.Onboarding> { backStackEntry ->
|
composable<Screen.Onboarding> { backStackEntry ->
|
||||||
OnboardingScreen(
|
OnboardingScreen(
|
||||||
onOpenImport = { navController.navigate(Screen.Import) },
|
onOpenImport = { navController.navigate(Screen.Import) },
|
||||||
onOpenNew = { navController.navigate(Screen.New) }
|
onOpenNew = { navController.navigate(Screen.NewIdentity) }
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
composable<Screen.Import> { backStackEntry ->
|
composable<Screen.Import> { backStackEntry ->
|
||||||
ImportScreen()
|
ImportScreen()
|
||||||
}
|
}
|
||||||
composable<Screen.New> { backStackEntry ->
|
composable<Screen.NewIdentity> { backStackEntry ->
|
||||||
NewScreen()
|
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 ->
|
composable<Screen.Home> { backStackEntry ->
|
||||||
HomeScreen(
|
HomeScreen(
|
||||||
|
|||||||
@@ -4,8 +4,6 @@ import android.os.Bundle
|
|||||||
import androidx.activity.ComponentActivity
|
import androidx.activity.ComponentActivity
|
||||||
import androidx.activity.compose.setContent
|
import androidx.activity.compose.setContent
|
||||||
import androidx.activity.enableEdgeToEdge
|
import androidx.activity.enableEdgeToEdge
|
||||||
import androidx.compose.runtime.Composable
|
|
||||||
import androidx.compose.ui.tooling.preview.Preview
|
|
||||||
import java.io.File
|
import java.io.File
|
||||||
|
|
||||||
class MainActivity : ComponentActivity() {
|
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")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -1,9 +0,0 @@
|
|||||||
package su.reya.coop
|
|
||||||
|
|
||||||
import android.os.Build
|
|
||||||
|
|
||||||
class AndroidPlatform : Platform {
|
|
||||||
override val name: String = "Android ${Build.VERSION.SDK_INT}"
|
|
||||||
}
|
|
||||||
|
|
||||||
actual fun getPlatform(): Platform = AndroidPlatform()
|
|
||||||
@@ -3,13 +3,22 @@ package su.reya.coop
|
|||||||
import rust.nostr.sdk.Client
|
import rust.nostr.sdk.Client
|
||||||
import rust.nostr.sdk.ClientBuilder
|
import rust.nostr.sdk.ClientBuilder
|
||||||
import rust.nostr.sdk.ClientOptions
|
import rust.nostr.sdk.ClientOptions
|
||||||
|
import rust.nostr.sdk.EventBuilder
|
||||||
|
import rust.nostr.sdk.Keys
|
||||||
|
import rust.nostr.sdk.Metadata
|
||||||
|
import rust.nostr.sdk.MetadataRecord
|
||||||
import rust.nostr.sdk.NostrDatabase
|
import rust.nostr.sdk.NostrDatabase
|
||||||
import rust.nostr.sdk.NostrGossip
|
import rust.nostr.sdk.NostrGossip
|
||||||
|
import rust.nostr.sdk.NostrSigner
|
||||||
import rust.nostr.sdk.RelayUrl
|
import rust.nostr.sdk.RelayUrl
|
||||||
|
|
||||||
class Nostr {
|
class Nostr {
|
||||||
var client: Client? = null
|
var client: Client? = null
|
||||||
private set
|
private set
|
||||||
|
var signer: NostrSigner? = null
|
||||||
|
private set
|
||||||
|
var deviceSigner: NostrSigner? = null
|
||||||
|
private set
|
||||||
|
|
||||||
fun init(dbPath: String) {
|
fun init(dbPath: String) {
|
||||||
val lmdb = NostrDatabase.lmdb(dbPath)
|
val lmdb = NostrDatabase.lmdb(dbPath)
|
||||||
@@ -29,4 +38,25 @@ class Nostr {
|
|||||||
suspend fun disconnect() {
|
suspend fun disconnect() {
|
||||||
this.client?.shutdown()
|
this.client?.shutdown()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
suspend fun createIdentity(keys: Keys, name: String, bio: String, picture: String?) {
|
||||||
|
signer = NostrSigner.keys(keys)
|
||||||
|
|
||||||
|
// Construct metadata
|
||||||
|
val metadata = Metadata.fromRecord(
|
||||||
|
MetadataRecord(
|
||||||
|
name = name,
|
||||||
|
displayName = name,
|
||||||
|
about = bio,
|
||||||
|
picture = picture
|
||||||
|
)
|
||||||
|
)
|
||||||
|
|
||||||
|
// Construct event and sign it
|
||||||
|
val builder = EventBuilder.metadata(metadata).build(keys.publicKey())
|
||||||
|
val event = this.signer?.signEvent(builder) ?: return
|
||||||
|
|
||||||
|
// Send event to relays
|
||||||
|
this.client?.sendEvent(event)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -7,6 +7,7 @@ import kotlinx.coroutines.flow.MutableStateFlow
|
|||||||
import kotlinx.coroutines.flow.asStateFlow
|
import kotlinx.coroutines.flow.asStateFlow
|
||||||
import kotlinx.coroutines.launch
|
import kotlinx.coroutines.launch
|
||||||
import kotlinx.coroutines.withContext
|
import kotlinx.coroutines.withContext
|
||||||
|
import rust.nostr.sdk.Keys
|
||||||
import su.reya.coop.storage.SecretStorage
|
import su.reya.coop.storage.SecretStorage
|
||||||
|
|
||||||
class NostrViewModel(
|
class NostrViewModel(
|
||||||
@@ -16,6 +17,9 @@ class NostrViewModel(
|
|||||||
private val _isConnected = MutableStateFlow(false)
|
private val _isConnected = MutableStateFlow(false)
|
||||||
val isConnected = _isConnected.asStateFlow()
|
val isConnected = _isConnected.asStateFlow()
|
||||||
|
|
||||||
|
private val _isCreating = MutableStateFlow(false)
|
||||||
|
val isCreating = _isCreating.asStateFlow()
|
||||||
|
|
||||||
fun initAndConnect(dbPath: String) {
|
fun initAndConnect(dbPath: String) {
|
||||||
// Initialize nostr client
|
// Initialize nostr client
|
||||||
nostr.init(dbPath)
|
nostr.init(dbPath)
|
||||||
@@ -32,6 +36,24 @@ class NostrViewModel(
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fun createIdentity(name: String, bio: String, picture: String?) {
|
||||||
|
viewModelScope.launch {
|
||||||
|
try {
|
||||||
|
val keys = Keys.generate()
|
||||||
|
val secret = keys.secretKey().toBech32()
|
||||||
|
// Set loading state
|
||||||
|
_isCreating.value = true
|
||||||
|
// Create identity
|
||||||
|
nostr.createIdentity(keys, name, bio, picture)
|
||||||
|
// Save secret to the secret storage
|
||||||
|
secretStore.set("user_signer", secret)
|
||||||
|
} catch (e: Exception) {
|
||||||
|
_isCreating.value = false
|
||||||
|
println(e)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
override fun onCleared() {
|
override fun onCleared() {
|
||||||
super.onCleared()
|
super.onCleared()
|
||||||
// Ensure all relays are disconnect
|
// Ensure all relays are disconnect
|
||||||
|
|||||||
@@ -1,9 +0,0 @@
|
|||||||
package su.reya.coop
|
|
||||||
|
|
||||||
import platform.UIKit.UIDevice
|
|
||||||
|
|
||||||
class IOSPlatform: Platform {
|
|
||||||
override val name: String = UIDevice.currentDevice.systemName() + " " + UIDevice.currentDevice.systemVersion
|
|
||||||
}
|
|
||||||
|
|
||||||
actual fun getPlatform(): Platform = IOSPlatform()
|
|
||||||
Reference in New Issue
Block a user