chore: improve performance #41
@@ -1,6 +1,8 @@
|
|||||||
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.lifecycle.compose.collectAsStateWithLifecycle
|
||||||
import su.reya.coop.LocalAccountViewModel
|
import su.reya.coop.LocalAccountViewModel
|
||||||
import su.reya.coop.LocalNavigator
|
import su.reya.coop.LocalNavigator
|
||||||
import su.reya.coop.Screen
|
import su.reya.coop.Screen
|
||||||
@@ -10,10 +12,12 @@ import su.reya.coop.shared.ProfileEditor
|
|||||||
fun NewIdentityScreen() {
|
fun NewIdentityScreen() {
|
||||||
val accountViewModel = LocalAccountViewModel.current
|
val accountViewModel = LocalAccountViewModel.current
|
||||||
val navigator = LocalNavigator.current
|
val navigator = LocalNavigator.current
|
||||||
|
val accountState by accountViewModel.state.collectAsStateWithLifecycle()
|
||||||
|
|
||||||
ProfileEditor(
|
ProfileEditor(
|
||||||
title = "Create a new identity",
|
title = "Create a new identity",
|
||||||
buttonLabel = "Continue",
|
buttonLabel = "Continue",
|
||||||
|
isBusy = accountState.isImporting,
|
||||||
onBack = { navigator.goBack() },
|
onBack = { navigator.goBack() },
|
||||||
onConfirm = { name, bio, bytes, type ->
|
onConfirm = { name, bio, bytes, type ->
|
||||||
accountViewModel.createIdentity(name, bio, bytes, type)
|
accountViewModel.createIdentity(name, bio, bytes, type)
|
||||||
|
|||||||
@@ -14,6 +14,7 @@ fun UpdateProfileScreen() {
|
|||||||
|
|
||||||
val currentUser by accountViewModel.currentUserProfile.collectAsStateWithLifecycle()
|
val currentUser by accountViewModel.currentUserProfile.collectAsStateWithLifecycle()
|
||||||
val profile = currentUser?.metadata?.asRecord()
|
val profile = currentUser?.metadata?.asRecord()
|
||||||
|
val isUpdatingProfile by accountViewModel.isUpdatingProfile.collectAsStateWithLifecycle()
|
||||||
|
|
||||||
ProfileEditor(
|
ProfileEditor(
|
||||||
title = "Update profile",
|
title = "Update profile",
|
||||||
@@ -21,6 +22,7 @@ fun UpdateProfileScreen() {
|
|||||||
initialName = profile?.displayName ?: profile?.name ?: "",
|
initialName = profile?.displayName ?: profile?.name ?: "",
|
||||||
initialBio = profile?.about ?: "",
|
initialBio = profile?.about ?: "",
|
||||||
initialPicture = profile?.picture,
|
initialPicture = profile?.picture,
|
||||||
|
isBusy = isUpdatingProfile,
|
||||||
onBack = { navigator.goBack() },
|
onBack = { navigator.goBack() },
|
||||||
onConfirm = { name, bio, bytes, type ->
|
onConfirm = { name, bio, bytes, type ->
|
||||||
accountViewModel.updateProfile(name, bio, bytes, type)
|
accountViewModel.updateProfile(name, bio, bytes, type)
|
||||||
|
|||||||
@@ -69,6 +69,7 @@ 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,
|
||||||
ioDispatcher: CoroutineDispatcher = Dispatchers.IO,
|
ioDispatcher: CoroutineDispatcher = Dispatchers.IO,
|
||||||
@@ -81,7 +82,6 @@ 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) {
|
||||||
@@ -269,7 +269,6 @@ fun ProfileEditor(
|
|||||||
.size(ButtonDefaults.MediumContainerHeight),
|
.size(ButtonDefaults.MediumContainerHeight),
|
||||||
onClick = {
|
onClick = {
|
||||||
scope.launch {
|
scope.launch {
|
||||||
isBusy = true
|
|
||||||
try {
|
try {
|
||||||
val bytes = withContext(ioDispatcher) {
|
val bytes = withContext(ioDispatcher) {
|
||||||
(picture as? Uri)?.let {
|
(picture as? Uri)?.let {
|
||||||
@@ -282,7 +281,6 @@ fun ProfileEditor(
|
|||||||
} catch (e: Exception) {
|
} catch (e: Exception) {
|
||||||
snackbarHostState.showSnackbar(e.message ?: "Error")
|
snackbarHostState.showSnackbar(e.message ?: "Error")
|
||||||
}
|
}
|
||||||
isBusy = false
|
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
enabled = name.isNotBlank() && !isBusy
|
enabled = name.isNotBlank() && !isBusy
|
||||||
|
|||||||
@@ -145,9 +145,7 @@ class AccountAuthDelegate(
|
|||||||
}
|
}
|
||||||
|
|
||||||
secret.startsWith("nip55://") -> {
|
secret.startsWith("nip55://") -> {
|
||||||
val handler = externalSignerHandler
|
val handler = externalSignerHandler ?: throw IllegalStateException("Not available")
|
||||||
?: throw IllegalStateException("External signer not available on this platform")
|
|
||||||
|
|
||||||
val parts = secret.removePrefix("nip55://").split("/", limit = 2)
|
val parts = secret.removePrefix("nip55://").split("/", limit = 2)
|
||||||
val packageName = parts[0]
|
val packageName = parts[0]
|
||||||
val pubkey = PublicKey.parse(parts[1])
|
val pubkey = PublicKey.parse(parts[1])
|
||||||
@@ -160,15 +158,21 @@ class AccountAuthDelegate(
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fun isExternalSignerAvailable(): Boolean {
|
||||||
|
return externalSignerHandler?.isAvailable() == true
|
||||||
|
}
|
||||||
|
|
||||||
fun importIdentity(secret: String, password: String? = null) {
|
fun importIdentity(secret: String, password: String? = null) {
|
||||||
scope.launch {
|
scope.launch {
|
||||||
_state.update { it.copy(isImporting = true, importError = null) }
|
_state.update { it.copy(isImporting = true, importError = null) }
|
||||||
try {
|
try {
|
||||||
val (signer, decryptedSecret) = createSigner(secret, password)
|
val (signer, decryptedSecret) = createSigner(secret, password)
|
||||||
|
|
||||||
nostr.setSigner(signer)
|
nostr.setSigner(signer)
|
||||||
|
onSignerReady()
|
||||||
|
|
||||||
storage.setSecret(KEY_USER_SIGNER, decryptedSecret ?: secret)
|
storage.setSecret(KEY_USER_SIGNER, decryptedSecret ?: secret)
|
||||||
_state.update { it.copy(signerRequired = false, isImporting = false) }
|
_state.update { it.copy(signerRequired = false, isImporting = false) }
|
||||||
onSignerReady()
|
|
||||||
} catch (e: Exception) {
|
} catch (e: Exception) {
|
||||||
onError("Import failed: ${e.message}")
|
onError("Import failed: ${e.message}")
|
||||||
_state.update { it.copy(isImporting = false, importError = e.message) }
|
_state.update { it.copy(isImporting = false, importError = e.message) }
|
||||||
@@ -180,8 +184,8 @@ class AccountAuthDelegate(
|
|||||||
scope.launch {
|
scope.launch {
|
||||||
_state.update { it.copy(isImporting = true, importError = null) }
|
_state.update { it.copy(isImporting = true, importError = null) }
|
||||||
try {
|
try {
|
||||||
val handler = externalSignerHandler
|
val handler =
|
||||||
?: throw IllegalStateException("Signer not available")
|
externalSignerHandler ?: throw IllegalStateException("Signer not available")
|
||||||
|
|
||||||
val permissions = SignerPermissions.toJson(
|
val permissions = SignerPermissions.toJson(
|
||||||
listOf(
|
listOf(
|
||||||
@@ -200,14 +204,13 @@ class AccountAuthDelegate(
|
|||||||
|
|
||||||
val result = handler.getPublicKey(permissions) ?: throw Exception("Rejected")
|
val result = handler.getPublicKey(permissions) ?: throw Exception("Rejected")
|
||||||
val signer = ExternalSignerProxy(handler, result.pubkey)
|
val signer = ExternalSignerProxy(handler, result.pubkey)
|
||||||
|
val uri = "nip55://${result.packageName}/${result.pubkey.toHex()}"
|
||||||
|
|
||||||
nostr.setSigner(signer)
|
nostr.setSigner(signer)
|
||||||
storage.setSecret(
|
|
||||||
KEY_USER_SIGNER,
|
|
||||||
"nip55://${result.packageName}/${result.pubkey.toHex()}"
|
|
||||||
)
|
|
||||||
_state.update { it.copy(signerRequired = false, isImporting = false) }
|
|
||||||
onSignerReady()
|
onSignerReady()
|
||||||
|
|
||||||
|
storage.setSecret(KEY_USER_SIGNER, uri)
|
||||||
|
_state.update { it.copy(signerRequired = false, isImporting = false) }
|
||||||
} catch (e: Exception) {
|
} catch (e: Exception) {
|
||||||
onError("External signer connection failed: ${e.message}")
|
onError("External signer connection failed: ${e.message}")
|
||||||
_state.update { it.copy(isImporting = false, importError = e.message) }
|
_state.update { it.copy(isImporting = false, importError = e.message) }
|
||||||
@@ -215,10 +218,6 @@ class AccountAuthDelegate(
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fun isExternalSignerAvailable(): Boolean {
|
|
||||||
return externalSignerHandler?.isAvailable() == true
|
|
||||||
}
|
|
||||||
|
|
||||||
fun createIdentity(
|
fun createIdentity(
|
||||||
name: String,
|
name: String,
|
||||||
bio: String?,
|
bio: String?,
|
||||||
@@ -233,15 +232,17 @@ class AccountAuthDelegate(
|
|||||||
val avatarUrl = picture?.let {
|
val avatarUrl = picture?.let {
|
||||||
mediaRepository.blossomUpload(keys, it, contentType ?: "image/jpeg")
|
mediaRepository.blossomUpload(keys, it, contentType ?: "image/jpeg")
|
||||||
}
|
}
|
||||||
|
|
||||||
nostr.profiles.createIdentity(
|
nostr.profiles.createIdentity(
|
||||||
keys = keys,
|
keys = keys,
|
||||||
name = name,
|
name = name,
|
||||||
bio = bio,
|
bio = bio,
|
||||||
picture = avatarUrl
|
picture = avatarUrl
|
||||||
)
|
)
|
||||||
|
onSignerReady()
|
||||||
|
|
||||||
storage.setSecret(KEY_USER_SIGNER, secret)
|
storage.setSecret(KEY_USER_SIGNER, secret)
|
||||||
_state.update { it.copy(signerRequired = false, isImporting = false) }
|
_state.update { it.copy(signerRequired = false, isImporting = false) }
|
||||||
onSignerReady()
|
|
||||||
} catch (e: Exception) {
|
} catch (e: Exception) {
|
||||||
onError("Identity creation failed: ${e.message}")
|
onError("Identity creation failed: ${e.message}")
|
||||||
_state.update { it.copy(isImporting = false, importError = e.message) }
|
_state.update { it.copy(isImporting = false, importError = e.message) }
|
||||||
|
|||||||
Reference in New Issue
Block a user