This commit is contained in:
2026-07-06 09:26:45 +07:00
parent d23b15df6f
commit a48c0c0e7c
3 changed files with 23 additions and 38 deletions

View File

@@ -1,9 +1,7 @@
package su.reya.coop.screens package su.reya.coop.screens
import androidx.compose.runtime.Composable import androidx.compose.runtime.Composable
import androidx.compose.runtime.getValue
import androidx.compose.runtime.rememberCoroutineScope import androidx.compose.runtime.rememberCoroutineScope
import androidx.lifecycle.compose.collectAsStateWithLifecycle
import kotlinx.coroutines.launch import kotlinx.coroutines.launch
import su.reya.coop.LocalAuthViewModel import su.reya.coop.LocalAuthViewModel
import su.reya.coop.LocalNavigator import su.reya.coop.LocalNavigator
@@ -16,13 +14,9 @@ fun NewIdentityScreen() {
val navigator = LocalNavigator.current val navigator = LocalNavigator.current
val scope = rememberCoroutineScope() val scope = rememberCoroutineScope()
val authState by authViewModel.state.collectAsStateWithLifecycle()
val isBusy = authState.isBusy
ProfileEditor( ProfileEditor(
title = "Create a new identity", title = "Create a new identity",
buttonLabel = "Continue", buttonLabel = "Continue",
isBusy = isBusy,
onBack = { navigator.goBack() }, onBack = { navigator.goBack() },
onConfirm = { name, bio, bytes, type -> onConfirm = { name, bio, bytes, type ->
scope.launch { scope.launch {

View File

@@ -68,7 +68,6 @@ fun ProfileEditor(
initialName: String = "", initialName: String = "",
initialBio: String = "", initialBio: String = "",
initialPicture: Any? = null, // Accepts Uri (picked) or String (current URL) initialPicture: Any? = null, // Accepts Uri (picked) or String (current URL)
isBusy: Boolean = false,
onBack: () -> Unit, onBack: () -> Unit,
onConfirm: (name: String, bio: String, pictureBytes: ByteArray?, contentType: String?) -> Unit onConfirm: (name: String, bio: String, pictureBytes: ByteArray?, contentType: String?) -> Unit
) { ) {
@@ -80,6 +79,7 @@ fun ProfileEditor(
var name by remember(initialName) { mutableStateOf(initialName) } var name by remember(initialName) { mutableStateOf(initialName) }
var bio by remember(initialBio) { mutableStateOf(initialBio) } var bio by remember(initialBio) { mutableStateOf(initialBio) }
var picture by remember(initialPicture) { mutableStateOf(initialPicture) } var picture by remember(initialPicture) { mutableStateOf(initialPicture) }
var isBusy by remember { mutableStateOf(false) }
val hasPicture = remember(picture) { val hasPicture = remember(picture) {
when (picture) { when (picture) {
@@ -267,14 +267,20 @@ fun ProfileEditor(
.size(ButtonDefaults.MediumContainerHeight), .size(ButtonDefaults.MediumContainerHeight),
onClick = { onClick = {
scope.launch { scope.launch {
val bytes = withContext(Dispatchers.IO) { isBusy = true
(picture as? Uri)?.let { try {
context.contentResolver.openInputStream(it)?.readBytes() val bytes = withContext(Dispatchers.IO) {
(picture as? Uri)?.let {
context.contentResolver.openInputStream(it)?.readBytes()
}
} }
val type =
(picture as? Uri)?.let { context.contentResolver.getType(it) }
onConfirm(name, bio, bytes, type)
} catch (e: Exception) {
snackbarHostState.showSnackbar(e.message ?: "Error")
} }
val type = isBusy = false
(picture as? Uri)?.let { context.contentResolver.getType(it) }
onConfirm(name, bio, bytes, type)
} }
}, },
enabled = name.isNotBlank() && !isBusy enabled = name.isNotBlank() && !isBusy

View File

@@ -21,7 +21,6 @@ import su.reya.coop.storage.SecretStorage
import kotlin.time.Duration.Companion.seconds import kotlin.time.Duration.Companion.seconds
data class AuthState( data class AuthState(
val isBusy: Boolean = false,
val signerRequired: Boolean? = null, val signerRequired: Boolean? = null,
val isNotificationBannerDismissed: Boolean = false, val isNotificationBannerDismissed: Boolean = false,
) )
@@ -88,8 +87,6 @@ class AuthViewModel(
fun logout(onLogout: () -> Unit = {}) { fun logout(onLogout: () -> Unit = {}) {
viewModelScope.launch { viewModelScope.launch {
try { try {
_state.update { it.copy(isBusy = true) }
// Reset the nostr signer and prune the database // Reset the nostr signer and prune the database
nostr.signer.switch(Keys.generate()) nostr.signer.switch(Keys.generate())
nostr.prune() nostr.prune()
@@ -99,11 +96,10 @@ class AuthViewModel(
// Clear credentials from persistent storage // Clear credentials from persistent storage
secretStore.clear(KEY_USER_SIGNER) secretStore.clear(KEY_USER_SIGNER)
secretStore.clear(KEY_BANNER_DISMISSED) secretStore.clear(KEY_BANNER_DISMISSED)
// Call cleanup callback (e.g. to reset other ViewModels) // Call cleanup callback (e.g. to reset other ViewModels)
onLogout() onLogout()
// Reset local states
_state.update { it.copy(isBusy = false, signerRequired = true) } _state.update { it.copy(signerRequired = true) }
} }
} }
} }
@@ -207,27 +203,16 @@ class AuthViewModel(
picture: ByteArray?, picture: ByteArray?,
contentType: String? = null contentType: String? = null
) { ) {
_state.update { it.copy(isBusy = true) }
val keys = Keys.generate() val keys = Keys.generate()
val secret = keys.secretKey().toBech32() val secret = keys.secretKey().toBech32()
val avatarUrl = picture?.let {
try { mediaRepository.blossomUpload(nostr.signer.get(), it, contentType ?: "image/jpeg")
val avatarUrl = picture?.let {
mediaRepository.blossomUpload(nostr.signer.get(), it, contentType ?: "image/jpeg")
}
// Create identity
nostr.profiles.createIdentity(keys = keys, name = name, bio = bio, picture = avatarUrl)
// Persist the secret in the secret storage
secretStore.set(KEY_USER_SIGNER, secret)
// Update local states
_state.update { it.copy(isBusy = false, signerRequired = false) }
} catch (e: Exception) {
showError("Error: ${e.message}")
_state.update { it.copy(isBusy = false) }
} }
// Create identity
nostr.profiles.createIdentity(keys = keys, name = name, bio = bio, picture = avatarUrl)
// Persist the secret in the secret storage
secretStore.set(KEY_USER_SIGNER, secret)
// Update local states
_state.update { it.copy(signerRequired = false) }
} }
} }