From d1a675c754ecc1d1a8879bb6bba361e6e70d3655 Mon Sep 17 00:00:00 2001 From: greenart7c3 Date: Wed, 25 Jun 2025 10:15:54 -0300 Subject: [PATCH] Separated Bunker and Intents home screen --- .../nostrsigner/service/AmberUtils.kt | 7 + ...reen.kt => BunkerSingleEventHomeScreen.kt} | 334 +--------------- .../components/IntentSingleEventHomeScreen.kt | 365 ++++++++++++++++++ 3 files changed, 373 insertions(+), 333 deletions(-) rename app/src/main/java/com/greenart7c3/nostrsigner/ui/components/{SingleEventHomeScreen.kt => BunkerSingleEventHomeScreen.kt} (70%) create mode 100644 app/src/main/java/com/greenart7c3/nostrsigner/ui/components/IntentSingleEventHomeScreen.kt diff --git a/app/src/main/java/com/greenart7c3/nostrsigner/service/AmberUtils.kt b/app/src/main/java/com/greenart7c3/nostrsigner/service/AmberUtils.kt index 25c005fa..46475894 100644 --- a/app/src/main/java/com/greenart7c3/nostrsigner/service/AmberUtils.kt +++ b/app/src/main/java/com/greenart7c3/nostrsigner/service/AmberUtils.kt @@ -292,3 +292,10 @@ fun String.toShortenHex(): String { if (length <= 16) return this return replaceRange(8, length - 8, ":") } + +fun isPrivateEvent( + kind: Int, + tags: Array>, +): Boolean { + return kind == LnZapRequestEvent.KIND && tags.any { t -> t.size > 1 && t[0] == "anon" } +} diff --git a/app/src/main/java/com/greenart7c3/nostrsigner/ui/components/SingleEventHomeScreen.kt b/app/src/main/java/com/greenart7c3/nostrsigner/ui/components/BunkerSingleEventHomeScreen.kt similarity index 70% rename from app/src/main/java/com/greenart7c3/nostrsigner/ui/components/SingleEventHomeScreen.kt rename to app/src/main/java/com/greenart7c3/nostrsigner/ui/components/BunkerSingleEventHomeScreen.kt index e7ae0928..52d8d12b 100644 --- a/app/src/main/java/com/greenart7c3/nostrsigner/ui/components/SingleEventHomeScreen.kt +++ b/app/src/main/java/com/greenart7c3/nostrsigner/ui/components/BunkerSingleEventHomeScreen.kt @@ -18,7 +18,6 @@ import androidx.compose.runtime.rememberCoroutineScope import androidx.compose.runtime.setValue import androidx.compose.ui.Alignment import androidx.compose.ui.Modifier -import androidx.compose.ui.platform.LocalClipboard import androidx.compose.ui.platform.LocalContext import androidx.compose.ui.res.stringResource import androidx.compose.ui.text.font.FontWeight @@ -31,13 +30,11 @@ import com.greenart7c3.nostrsigner.database.AppDatabase import com.greenart7c3.nostrsigner.database.ApplicationWithPermissions import com.greenart7c3.nostrsigner.models.Account import com.greenart7c3.nostrsigner.models.AmberBunkerRequest -import com.greenart7c3.nostrsigner.models.IntentData -import com.greenart7c3.nostrsigner.models.IntentResultType import com.greenart7c3.nostrsigner.models.Permission import com.greenart7c3.nostrsigner.models.SignerType import com.greenart7c3.nostrsigner.models.kindToNip import com.greenart7c3.nostrsigner.service.BunkerRequestUtils -import com.greenart7c3.nostrsigner.service.IntentUtils +import com.greenart7c3.nostrsigner.service.isPrivateEvent import com.greenart7c3.nostrsigner.service.toShortenHex import com.greenart7c3.nostrsigner.ui.RememberType import com.vitorpamplona.quartz.nip01Core.core.toHexKey @@ -51,333 +48,11 @@ import com.vitorpamplona.quartz.nip46RemoteSigner.BunkerRequestNip44Decrypt import com.vitorpamplona.quartz.nip46RemoteSigner.BunkerRequestNip44Encrypt import com.vitorpamplona.quartz.nip46RemoteSigner.BunkerRequestSign import com.vitorpamplona.quartz.nip55AndroidSigner.signString -import com.vitorpamplona.quartz.nip57Zaps.LnZapRequestEvent import com.vitorpamplona.quartz.utils.Hex import com.vitorpamplona.quartz.utils.TimeUtils import kotlinx.coroutines.Dispatchers import kotlinx.coroutines.launch -@Composable -fun IntentSingleEventHomeScreen( - modifier: Modifier, - packageName: String?, - applicationName: String?, - intentData: IntentData, - account: Account, - onRemoveIntentData: (List, IntentResultType) -> Unit, - onLoading: (Boolean) -> Unit, -) { - var applicationEntity by remember { - mutableStateOf(null) - } - val key = "$packageName" - - LaunchedEffect(Unit) { - launch(Dispatchers.IO) { - applicationEntity = Amber.instance.getDatabase(account.npub).applicationDao().getByKey(key) - } - } - - var appName = applicationEntity?.application?.name ?: packageName ?: intentData.name - appName = appName.ifBlank { key.toShortenHex() } - val coroutineScope = rememberCoroutineScope() - val context = LocalContext.current - val clipboardManager = LocalClipboard.current - - when (intentData.type) { - SignerType.GET_PUBLIC_KEY -> { - LoginWithPubKey( - shouldCloseApp = applicationEntity?.application?.closeApplication != false, - modifier = modifier, - account = account, - packageName = packageName, - appName = appName, - applicationName = applicationName, - permissions = intentData.permissions, - onAccept = { permissions, signPolicy, closeApplication, rememberType -> - val result = if (packageName == null) { - account.hexKey - } else { - account.npub - } - - IntentUtils.sendResult( - context, - packageName, - account, - key, - clipboardManager, - result, - result, - intentData, - null, - permissions = permissions, - appName = applicationName ?: appName, - onLoading = onLoading, - signPolicy = signPolicy, - onRemoveIntentData = onRemoveIntentData, - shouldCloseApplication = closeApplication, - rememberType = rememberType, - ) - }, - onReject = { - val activity = Amber.instance.getMainActivity() - activity?.intent = null - activity?.finish() - onRemoveIntentData(listOf(intentData), IntentResultType.REMOVE) - onLoading(false) - }, - ) - } - - SignerType.SIGN_MESSAGE -> { - val permission = - applicationEntity?.permissions?.firstOrNull { - it.pkKey == key && it.type == intentData.type.toString() - } - - val acceptUntil = permission?.acceptUntil ?: 0 - val rejectUntil = permission?.rejectUntil ?: 0 - - val acceptOrReject = if (rejectUntil == 0L && acceptUntil == 0L) { - null - } else if (rejectUntil > TimeUtils.now() && rejectUntil > 0) { - false - } else if (acceptUntil > TimeUtils.now() && acceptUntil > 0) { - true - } else { - null - } - - SignMessage( - account = account, - modifier = modifier, - content = intentData.data, - shouldRunOnAccept = acceptOrReject, - packageName = packageName, - applicationName = applicationName, - appName = appName, - type = intentData.type, - onAccept = { - Amber.instance.applicationIOScope.launch(Dispatchers.IO) { - val result = signString(intentData.data, account.signer.keyPair.privKey!!).toHexKey() - IntentUtils.sendResult( - context, - packageName, - account, - key, - clipboardManager, - result, - result, - intentData, - null, - onLoading, - onRemoveIntentData = onRemoveIntentData, - rememberType = it, - ) - } - }, - onReject = { - IntentUtils.sendRejection( - key = key, - account = account, - intentData = intentData, - appName = appName, - rememberType = it, - onLoading = onLoading, - onRemoveIntentData = onRemoveIntentData, - kind = null, - ) - }, - ) - } - - SignerType.NIP04_DECRYPT, SignerType.NIP04_ENCRYPT, SignerType.NIP44_ENCRYPT, SignerType.NIP44_DECRYPT, SignerType.DECRYPT_ZAP_EVENT -> { - val nip = when (intentData.type) { - SignerType.NIP04_DECRYPT, SignerType.NIP04_ENCRYPT -> 4 - SignerType.NIP44_ENCRYPT, SignerType.NIP44_DECRYPT -> 44 - SignerType.DECRYPT_ZAP_EVENT -> null - else -> null - } - var permission = - applicationEntity?.permissions?.firstOrNull { - it.pkKey == key && it.type == intentData.type.toString() - } - if (permission == null && nip != null) { - permission = - applicationEntity?.permissions?.firstOrNull { - it.pkKey == key && it.type == "NIP" && it.kind == nip - } - } - - val acceptUntil = permission?.acceptUntil ?: 0 - val rejectUntil = permission?.rejectUntil ?: 0 - - val acceptOrReject = if (rejectUntil == 0L && acceptUntil == 0L) { - null - } else if (rejectUntil > TimeUtils.now() && rejectUntil > 0) { - false - } else if (acceptUntil > TimeUtils.now() && acceptUntil > 0) { - true - } else { - null - } - - EncryptDecryptData( - account = account, - modifier = modifier, - content = intentData.data, - encryptedData = intentData.encryptedData ?: "", - shouldRunOnAccept = acceptOrReject, - packageName = packageName, - applicationName = applicationName, - appName = appName, - type = intentData.type, - onAccept = { - val result = - if (intentData.encryptedData == "Could not decrypt the message" && (intentData.type == SignerType.DECRYPT_ZAP_EVENT)) { - "" - } else { - intentData.encryptedData ?: "" - } - - IntentUtils.sendResult( - context, - packageName, - account, - key, - clipboardManager, - result, - result, - intentData, - null, - onRemoveIntentData = onRemoveIntentData, - onLoading = onLoading, - rememberType = it, - ) - }, - onReject = { - IntentUtils.sendRejection( - key = key, - account = account, - intentData = intentData, - appName = appName, - rememberType = it, - onLoading = onLoading, - onRemoveIntentData = onRemoveIntentData, - kind = null, - ) - }, - ) - } - - else -> { - val event = intentData.event!! - val accounts = - LocalPreferences.allSavedAccounts(context).filter { - it.npub.bechToBytes().toHexKey() == event.pubKey - } - - if (accounts.isEmpty() && !isPrivateEvent(event.kind, event.tags)) { - Column( - Modifier.fillMaxSize(), - Arrangement.Center, - Alignment.CenterHorizontally, - ) { - Text( - Hex.decode(event.pubKey).toNpub().toShortenHex(), - fontWeight = FontWeight.Bold, - fontSize = 21.sp, - ) - Spacer(Modifier.size(8.dp)) - Text("Not logged in") - } - } else { - val permission = - applicationEntity?.permissions?.firstOrNull { - val nip = event.kind.kindToNip()?.toIntOrNull() - it.pkKey == key && ((it.type == intentData.type.toString() && it.kind == event.kind) || (nip != null && it.type == "NIP" && it.kind == nip)) - } - - val acceptUntil = permission?.acceptUntil ?: 0 - val rejectUntil = permission?.rejectUntil ?: 0 - - val acceptOrReject = if (rejectUntil == 0L && acceptUntil == 0L) { - null - } else if (rejectUntil > TimeUtils.now() && rejectUntil > 0) { - false - } else if (acceptUntil > TimeUtils.now() && acceptUntil > 0) { - true - } else { - null - } - - EventData( - account = account, - modifier = modifier, - shouldAcceptOrReject = acceptOrReject, - packageName = packageName, - appName = appName, - applicationName = applicationName, - event = event, - rawJson = event.toJson(), - type = intentData.type, - onAccept = { - if (event.pubKey != account.hexKey && !isPrivateEvent(event.kind, event.tags)) { - coroutineScope.launch { - Toast.makeText( - context, - context.getString(R.string.event_pubkey_is_not_equal_to_current_logged_in_user), - Toast.LENGTH_SHORT, - ).show() - } - return@EventData - } - - IntentUtils.sendResult( - context = context, - packageName = packageName, - account = account, - key = key, - clipboardManager = clipboardManager, - event = event.toJson(), - value = if (event is LnZapRequestEvent && - event.tags.any { tag -> - tag.any { t -> t == "anon" } - } - ) { - event.toJson() - } else { - event.sig - }, - intentData = intentData, - kind = event.kind, - onLoading = onLoading, - onRemoveIntentData = onRemoveIntentData, - signPolicy = null, - appName = applicationName ?: appName, - permissions = null, - rememberType = it, - ) - }, - onReject = { - IntentUtils.sendRejection( - key = key, - account = account, - intentData = intentData, - appName = appName, - rememberType = it, - onLoading = onLoading, - onRemoveIntentData = onRemoveIntentData, - kind = event.kind, - ) - }, - ) - } - } - } -} - @Composable fun BunkerSingleEventHomeScreen( modifier: Modifier, @@ -1081,10 +756,3 @@ fun BunkerSingleEventHomeScreen( } } } - -fun isPrivateEvent( - kind: Int, - tags: Array>, -): Boolean { - return kind == LnZapRequestEvent.KIND && tags.any { t -> t.size > 1 && t[0] == "anon" } -} diff --git a/app/src/main/java/com/greenart7c3/nostrsigner/ui/components/IntentSingleEventHomeScreen.kt b/app/src/main/java/com/greenart7c3/nostrsigner/ui/components/IntentSingleEventHomeScreen.kt new file mode 100644 index 00000000..a3bbc78b --- /dev/null +++ b/app/src/main/java/com/greenart7c3/nostrsigner/ui/components/IntentSingleEventHomeScreen.kt @@ -0,0 +1,365 @@ +package com.greenart7c3.nostrsigner.ui.components + +import android.widget.Toast +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.size +import androidx.compose.material3.Text +import androidx.compose.runtime.Composable +import androidx.compose.runtime.LaunchedEffect +import androidx.compose.runtime.getValue +import androidx.compose.runtime.mutableStateOf +import androidx.compose.runtime.remember +import androidx.compose.runtime.rememberCoroutineScope +import androidx.compose.runtime.setValue +import androidx.compose.ui.Alignment +import androidx.compose.ui.Modifier +import androidx.compose.ui.platform.LocalClipboard +import androidx.compose.ui.platform.LocalContext +import androidx.compose.ui.text.font.FontWeight +import androidx.compose.ui.unit.dp +import androidx.compose.ui.unit.sp +import com.greenart7c3.nostrsigner.Amber +import com.greenart7c3.nostrsigner.LocalPreferences +import com.greenart7c3.nostrsigner.R +import com.greenart7c3.nostrsigner.database.ApplicationWithPermissions +import com.greenart7c3.nostrsigner.models.Account +import com.greenart7c3.nostrsigner.models.IntentData +import com.greenart7c3.nostrsigner.models.IntentResultType +import com.greenart7c3.nostrsigner.models.SignerType +import com.greenart7c3.nostrsigner.models.kindToNip +import com.greenart7c3.nostrsigner.service.IntentUtils +import com.greenart7c3.nostrsigner.service.isPrivateEvent +import com.greenart7c3.nostrsigner.service.toShortenHex +import com.vitorpamplona.quartz.nip01Core.core.toHexKey +import com.vitorpamplona.quartz.nip19Bech32.bech32.bechToBytes +import com.vitorpamplona.quartz.nip19Bech32.toNpub +import com.vitorpamplona.quartz.nip55AndroidSigner.signString +import com.vitorpamplona.quartz.nip57Zaps.LnZapRequestEvent +import com.vitorpamplona.quartz.utils.Hex +import com.vitorpamplona.quartz.utils.TimeUtils +import kotlinx.coroutines.Dispatchers +import kotlinx.coroutines.launch + +@Composable +fun IntentSingleEventHomeScreen( + modifier: Modifier, + packageName: String?, + applicationName: String?, + intentData: IntentData, + account: Account, + onRemoveIntentData: (List, IntentResultType) -> Unit, + onLoading: (Boolean) -> Unit, +) { + var applicationEntity by remember { + mutableStateOf(null) + } + val key = "$packageName" + + LaunchedEffect(Unit) { + launch(Dispatchers.IO) { + applicationEntity = Amber.instance.getDatabase(account.npub).applicationDao().getByKey(key) + } + } + + var appName = applicationEntity?.application?.name ?: packageName ?: intentData.name + appName = appName.ifBlank { key.toShortenHex() } + val coroutineScope = rememberCoroutineScope() + val context = LocalContext.current + val clipboardManager = LocalClipboard.current + + when (intentData.type) { + SignerType.GET_PUBLIC_KEY -> { + LoginWithPubKey( + shouldCloseApp = applicationEntity?.application?.closeApplication != false, + modifier = modifier, + account = account, + packageName = packageName, + appName = appName, + applicationName = applicationName, + permissions = intentData.permissions, + onAccept = { permissions, signPolicy, closeApplication, rememberType -> + val result = if (packageName == null) { + account.hexKey + } else { + account.npub + } + + IntentUtils.sendResult( + context, + packageName, + account, + key, + clipboardManager, + result, + result, + intentData, + null, + permissions = permissions, + appName = applicationName ?: appName, + onLoading = onLoading, + signPolicy = signPolicy, + onRemoveIntentData = onRemoveIntentData, + shouldCloseApplication = closeApplication, + rememberType = rememberType, + ) + }, + onReject = { + val activity = Amber.instance.getMainActivity() + activity?.intent = null + activity?.finish() + onRemoveIntentData(listOf(intentData), IntentResultType.REMOVE) + onLoading(false) + }, + ) + } + + SignerType.SIGN_MESSAGE -> { + val permission = + applicationEntity?.permissions?.firstOrNull { + it.pkKey == key && it.type == intentData.type.toString() + } + + val acceptUntil = permission?.acceptUntil ?: 0 + val rejectUntil = permission?.rejectUntil ?: 0 + + val acceptOrReject = if (rejectUntil == 0L && acceptUntil == 0L) { + null + } else if (rejectUntil > TimeUtils.now() && rejectUntil > 0) { + false + } else if (acceptUntil > TimeUtils.now() && acceptUntil > 0) { + true + } else { + null + } + + SignMessage( + account = account, + modifier = modifier, + content = intentData.data, + shouldRunOnAccept = acceptOrReject, + packageName = packageName, + applicationName = applicationName, + appName = appName, + type = intentData.type, + onAccept = { + Amber.instance.applicationIOScope.launch(Dispatchers.IO) { + val result = signString(intentData.data, account.signer.keyPair.privKey!!).toHexKey() + IntentUtils.sendResult( + context, + packageName, + account, + key, + clipboardManager, + result, + result, + intentData, + null, + onLoading, + onRemoveIntentData = onRemoveIntentData, + rememberType = it, + ) + } + }, + onReject = { + IntentUtils.sendRejection( + key = key, + account = account, + intentData = intentData, + appName = appName, + rememberType = it, + onLoading = onLoading, + onRemoveIntentData = onRemoveIntentData, + kind = null, + ) + }, + ) + } + + SignerType.NIP04_DECRYPT, SignerType.NIP04_ENCRYPT, SignerType.NIP44_ENCRYPT, SignerType.NIP44_DECRYPT, SignerType.DECRYPT_ZAP_EVENT -> { + val nip = when (intentData.type) { + SignerType.NIP04_DECRYPT, SignerType.NIP04_ENCRYPT -> 4 + SignerType.NIP44_ENCRYPT, SignerType.NIP44_DECRYPT -> 44 + SignerType.DECRYPT_ZAP_EVENT -> null + else -> null + } + var permission = + applicationEntity?.permissions?.firstOrNull { + it.pkKey == key && it.type == intentData.type.toString() + } + if (permission == null && nip != null) { + permission = + applicationEntity?.permissions?.firstOrNull { + it.pkKey == key && it.type == "NIP" && it.kind == nip + } + } + + val acceptUntil = permission?.acceptUntil ?: 0 + val rejectUntil = permission?.rejectUntil ?: 0 + + val acceptOrReject = if (rejectUntil == 0L && acceptUntil == 0L) { + null + } else if (rejectUntil > TimeUtils.now() && rejectUntil > 0) { + false + } else if (acceptUntil > TimeUtils.now() && acceptUntil > 0) { + true + } else { + null + } + + EncryptDecryptData( + account = account, + modifier = modifier, + content = intentData.data, + encryptedData = intentData.encryptedData ?: "", + shouldRunOnAccept = acceptOrReject, + packageName = packageName, + applicationName = applicationName, + appName = appName, + type = intentData.type, + onAccept = { + val result = + if (intentData.encryptedData == "Could not decrypt the message" && (intentData.type == SignerType.DECRYPT_ZAP_EVENT)) { + "" + } else { + intentData.encryptedData ?: "" + } + + IntentUtils.sendResult( + context, + packageName, + account, + key, + clipboardManager, + result, + result, + intentData, + null, + onRemoveIntentData = onRemoveIntentData, + onLoading = onLoading, + rememberType = it, + ) + }, + onReject = { + IntentUtils.sendRejection( + key = key, + account = account, + intentData = intentData, + appName = appName, + rememberType = it, + onLoading = onLoading, + onRemoveIntentData = onRemoveIntentData, + kind = null, + ) + }, + ) + } + + else -> { + val event = intentData.event!! + val accounts = + LocalPreferences.allSavedAccounts(context).filter { + it.npub.bechToBytes().toHexKey() == event.pubKey + } + + if (accounts.isEmpty() && !isPrivateEvent(event.kind, event.tags)) { + Column( + Modifier.fillMaxSize(), + Arrangement.Center, + Alignment.CenterHorizontally, + ) { + Text( + Hex.decode(event.pubKey).toNpub().toShortenHex(), + fontWeight = FontWeight.Bold, + fontSize = 21.sp, + ) + Spacer(Modifier.size(8.dp)) + Text("Not logged in") + } + } else { + val permission = + applicationEntity?.permissions?.firstOrNull { + val nip = event.kind.kindToNip()?.toIntOrNull() + it.pkKey == key && ((it.type == intentData.type.toString() && it.kind == event.kind) || (nip != null && it.type == "NIP" && it.kind == nip)) + } + + val acceptUntil = permission?.acceptUntil ?: 0 + val rejectUntil = permission?.rejectUntil ?: 0 + + val acceptOrReject = if (rejectUntil == 0L && acceptUntil == 0L) { + null + } else if (rejectUntil > TimeUtils.now() && rejectUntil > 0) { + false + } else if (acceptUntil > TimeUtils.now() && acceptUntil > 0) { + true + } else { + null + } + + EventData( + account = account, + modifier = modifier, + shouldAcceptOrReject = acceptOrReject, + packageName = packageName, + appName = appName, + applicationName = applicationName, + event = event, + rawJson = event.toJson(), + type = intentData.type, + onAccept = { + if (event.pubKey != account.hexKey && !isPrivateEvent(event.kind, event.tags)) { + coroutineScope.launch { + Toast.makeText( + context, + context.getString(R.string.event_pubkey_is_not_equal_to_current_logged_in_user), + Toast.LENGTH_SHORT, + ).show() + } + return@EventData + } + + IntentUtils.sendResult( + context = context, + packageName = packageName, + account = account, + key = key, + clipboardManager = clipboardManager, + event = event.toJson(), + value = if (event is LnZapRequestEvent && + event.tags.any { tag -> + tag.any { t -> t == "anon" } + } + ) { + event.toJson() + } else { + event.sig + }, + intentData = intentData, + kind = event.kind, + onLoading = onLoading, + onRemoveIntentData = onRemoveIntentData, + signPolicy = null, + appName = applicationName ?: appName, + permissions = null, + rememberType = it, + ) + }, + onReject = { + IntentUtils.sendRejection( + key = key, + account = account, + intentData = intentData, + appName = appName, + rememberType = it, + onLoading = onLoading, + onRemoveIntentData = onRemoveIntentData, + kind = event.kind, + ) + }, + ) + } + } + } +}