mirror of
https://github.com/greenart7c3/Amber.git
synced 2026-10-06 03:18:23 +00:00
support for sign multiple events simultaneous
This commit is contained in:
+1
-1
@@ -75,7 +75,7 @@ dependencies {
|
||||
implementation "androidx.core:core-splashscreen:1.0.1"
|
||||
|
||||
implementation "androidx.core:core-ktx:$core_ktx_version"
|
||||
implementation 'androidx.activity:activity-compose:1.8.0'
|
||||
implementation 'androidx.activity:activity-compose:1.8.1'
|
||||
implementation "androidx.compose.ui:ui:$compose_ui_version"
|
||||
implementation "androidx.compose.ui:ui-tooling-preview:$compose_ui_version"
|
||||
|
||||
|
||||
@@ -1,5 +1,6 @@
|
||||
package com.greenart7c3.nostrsigner
|
||||
|
||||
import android.content.Intent
|
||||
import android.os.Bundle
|
||||
import androidx.activity.compose.setContent
|
||||
import androidx.appcompat.app.AppCompatActivity
|
||||
@@ -9,12 +10,24 @@ import androidx.compose.material3.Surface
|
||||
import androidx.compose.ui.Modifier
|
||||
import androidx.core.splashscreen.SplashScreen.Companion.installSplashScreen
|
||||
import androidx.core.view.WindowCompat
|
||||
import androidx.lifecycle.MutableLiveData
|
||||
import androidx.lifecycle.ViewModel
|
||||
import androidx.lifecycle.viewmodel.compose.viewModel
|
||||
import com.greenart7c3.nostrsigner.models.IntentData
|
||||
import com.greenart7c3.nostrsigner.service.IntentUtils
|
||||
import com.greenart7c3.nostrsigner.ui.AccountScreen
|
||||
import com.greenart7c3.nostrsigner.ui.AccountStateViewModel
|
||||
import com.greenart7c3.nostrsigner.ui.theme.NostrSignerTheme
|
||||
|
||||
class MainViewModel : ViewModel() {
|
||||
var intents = IntentLiveData()
|
||||
}
|
||||
|
||||
class IntentLiveData : MutableLiveData<MutableList<IntentData?>>(mutableListOf())
|
||||
|
||||
class MainActivity : AppCompatActivity() {
|
||||
private val mainViewModel = MainViewModel()
|
||||
|
||||
override fun onCreate(savedInstanceState: Bundle?) {
|
||||
super.onCreate(savedInstanceState)
|
||||
WindowCompat.setDecorFitsSystemWindows(window, false)
|
||||
@@ -37,9 +50,23 @@ class MainActivity : AppCompatActivity() {
|
||||
val accountStateViewModel: AccountStateViewModel = viewModel {
|
||||
AccountStateViewModel(intent.getStringExtra("current_user"))
|
||||
}
|
||||
AccountScreen(accountStateViewModel, intent, packageName, appName)
|
||||
AccountScreen(accountStateViewModel, intent, packageName, appName, mainViewModel)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
override fun onNewIntent(intent: Intent?) {
|
||||
super.onNewIntent(intent)
|
||||
if (intent == null) return
|
||||
|
||||
val list = mutableListOf(IntentUtils.getIntentData(intent))
|
||||
mainViewModel.intents.value?.forEach {
|
||||
if (list.none { item -> item?.id == it?.id }) {
|
||||
list.add(it)
|
||||
}
|
||||
}
|
||||
|
||||
mainViewModel.intents.value = list
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,5 +1,6 @@
|
||||
package com.greenart7c3.nostrsigner.models
|
||||
|
||||
import androidx.compose.runtime.MutableState
|
||||
import com.vitorpamplona.quartz.encoders.HexKey
|
||||
|
||||
data class Permission(
|
||||
@@ -44,7 +45,10 @@ class IntentData(
|
||||
val callBackUrl: String?,
|
||||
val compression: CompressionType,
|
||||
val returnType: ReturnType,
|
||||
val permissions: List<Permission>?
|
||||
val permissions: List<Permission>?,
|
||||
val currentAccount: String,
|
||||
val checked: MutableState<Boolean>,
|
||||
val rememberMyChoice: MutableState<Boolean>
|
||||
)
|
||||
|
||||
enum class SignerType {
|
||||
|
||||
@@ -2,6 +2,7 @@ package com.greenart7c3.nostrsigner.service
|
||||
|
||||
import android.content.Intent
|
||||
import android.net.Uri
|
||||
import androidx.compose.runtime.mutableStateOf
|
||||
import com.google.gson.Gson
|
||||
import com.google.gson.reflect.TypeToken
|
||||
import com.greenart7c3.nostrsigner.models.CompressionType
|
||||
@@ -60,7 +61,10 @@ object IntentUtils {
|
||||
intent.extras?.getString("callbackUrl"),
|
||||
compressionType,
|
||||
returnType,
|
||||
permissions
|
||||
permissions,
|
||||
intent.extras?.getString("current_user") ?: "",
|
||||
mutableStateOf(true),
|
||||
mutableStateOf(false)
|
||||
)
|
||||
}
|
||||
return null
|
||||
|
||||
@@ -7,6 +7,8 @@ import androidx.compose.foundation.layout.Column
|
||||
import androidx.compose.runtime.Composable
|
||||
import androidx.compose.runtime.collectAsState
|
||||
import androidx.compose.runtime.getValue
|
||||
import androidx.compose.runtime.livedata.observeAsState
|
||||
import com.greenart7c3.nostrsigner.MainViewModel
|
||||
import com.greenart7c3.nostrsigner.service.IntentUtils
|
||||
|
||||
@Composable
|
||||
@@ -14,9 +16,11 @@ fun AccountScreen(
|
||||
accountStateViewModel: AccountStateViewModel,
|
||||
intent: Intent,
|
||||
packageName: String?,
|
||||
appName: String?
|
||||
appName: String?,
|
||||
mainViewModel: MainViewModel
|
||||
) {
|
||||
val accountState by accountStateViewModel.accountContent.collectAsState()
|
||||
val intents by mainViewModel.intents.observeAsState()
|
||||
|
||||
Column {
|
||||
Crossfade(
|
||||
@@ -29,9 +33,12 @@ fun AccountScreen(
|
||||
LoginPage(accountStateViewModel)
|
||||
}
|
||||
is AccountState.LoggedIn -> {
|
||||
val intentData = IntentUtils.getIntentData(intent)
|
||||
val localIntents = intents?.mapNotNull { it } ?: listOf()
|
||||
val newIntents = localIntents.ifEmpty {
|
||||
listOf(IntentUtils.getIntentData(intent))
|
||||
}
|
||||
|
||||
MainScreen(state.account, accountStateViewModel, intentData, packageName, appName, state.route)
|
||||
MainScreen(state.account, accountStateViewModel, newIntents, packageName, appName, state.route)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,20 +1,48 @@
|
||||
package com.greenart7c3.nostrsigner.ui
|
||||
|
||||
import android.app.Activity
|
||||
import android.content.Intent
|
||||
import android.widget.Toast
|
||||
import androidx.compose.foundation.clickable
|
||||
import androidx.compose.foundation.layout.Arrangement
|
||||
import androidx.compose.foundation.layout.Box
|
||||
import androidx.compose.foundation.layout.Column
|
||||
import androidx.compose.foundation.layout.Row
|
||||
import androidx.compose.foundation.layout.fillMaxHeight
|
||||
import androidx.compose.foundation.layout.fillMaxSize
|
||||
import androidx.compose.foundation.layout.fillMaxWidth
|
||||
import androidx.compose.foundation.layout.padding
|
||||
import androidx.compose.foundation.lazy.LazyColumn
|
||||
import androidx.compose.material.icons.Icons
|
||||
import androidx.compose.material.icons.filled.KeyboardArrowDown
|
||||
import androidx.compose.material.icons.filled.KeyboardArrowUp
|
||||
import androidx.compose.material3.Button
|
||||
import androidx.compose.material3.Card
|
||||
import androidx.compose.material3.CircularProgressIndicator
|
||||
import androidx.compose.material3.Icon
|
||||
import androidx.compose.material3.Switch
|
||||
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.rememberCoroutineScope
|
||||
import androidx.compose.runtime.setValue
|
||||
import androidx.compose.ui.Alignment
|
||||
import androidx.compose.ui.Modifier
|
||||
import androidx.compose.ui.graphics.Color
|
||||
import androidx.compose.ui.platform.LocalClipboardManager
|
||||
import androidx.compose.ui.platform.LocalContext
|
||||
import androidx.compose.ui.res.stringResource
|
||||
import androidx.compose.ui.text.SpanStyle
|
||||
import androidx.compose.ui.text.buildAnnotatedString
|
||||
import androidx.compose.ui.text.font.FontWeight
|
||||
import androidx.compose.ui.text.style.TextOverflow
|
||||
import androidx.compose.ui.text.withStyle
|
||||
import androidx.compose.ui.unit.dp
|
||||
import androidx.compose.ui.unit.sp
|
||||
import com.google.gson.GsonBuilder
|
||||
import com.greenart7c3.nostrsigner.LocalPreferences
|
||||
import com.greenart7c3.nostrsigner.R
|
||||
import com.greenart7c3.nostrsigner.models.Account
|
||||
import com.greenart7c3.nostrsigner.models.IntentData
|
||||
@@ -22,9 +50,12 @@ import com.greenart7c3.nostrsigner.models.SignerType
|
||||
import com.greenart7c3.nostrsigner.service.AmberUtils
|
||||
import com.greenart7c3.nostrsigner.service.IntentUtils
|
||||
import com.greenart7c3.nostrsigner.service.getAppCompatActivity
|
||||
import com.greenart7c3.nostrsigner.service.toShortenHex
|
||||
import com.greenart7c3.nostrsigner.ui.components.EncryptDecryptData
|
||||
import com.greenart7c3.nostrsigner.ui.components.EventData
|
||||
import com.greenart7c3.nostrsigner.ui.components.LoginWithPubKey
|
||||
import com.greenart7c3.nostrsigner.ui.components.RememberMyChoice
|
||||
import com.greenart7c3.nostrsigner.ui.theme.ButtonBorder
|
||||
import com.vitorpamplona.quartz.encoders.toHexKey
|
||||
import com.vitorpamplona.quartz.encoders.toNpub
|
||||
import com.vitorpamplona.quartz.events.Event
|
||||
@@ -35,7 +66,7 @@ import kotlinx.coroutines.launch
|
||||
@Composable
|
||||
fun HomeScreen(
|
||||
modifier: Modifier,
|
||||
json: IntentData?,
|
||||
intents: List<IntentData?>,
|
||||
packageName: String?,
|
||||
applicationName: String?,
|
||||
account: Account
|
||||
@@ -43,110 +74,224 @@ fun HomeScreen(
|
||||
val coroutineScope = rememberCoroutineScope()
|
||||
val context = LocalContext.current
|
||||
val clipboardManager = LocalClipboardManager.current
|
||||
var loading by remember { mutableStateOf(false) }
|
||||
|
||||
Box(
|
||||
modifier
|
||||
) {
|
||||
if (json == null) {
|
||||
if (loading) {
|
||||
Box(
|
||||
modifier
|
||||
) {
|
||||
Column(
|
||||
Modifier.fillMaxSize(),
|
||||
Arrangement.Center,
|
||||
Alignment.CenterHorizontally
|
||||
) {
|
||||
Text(stringResource(R.string.no_event_to_sign))
|
||||
CircularProgressIndicator()
|
||||
}
|
||||
} else {
|
||||
json.let {
|
||||
var key = "$packageName-${it.type}"
|
||||
val appName = packageName ?: it.name
|
||||
when (it.type) {
|
||||
SignerType.GET_PUBLIC_KEY -> {
|
||||
val remember = remember {
|
||||
mutableStateOf(account.savedApps[key] ?: false)
|
||||
}
|
||||
LoginWithPubKey(
|
||||
appName,
|
||||
applicationName,
|
||||
json.permissions,
|
||||
{ permissions ->
|
||||
val sig = account.keyPair.pubKey.toNpub()
|
||||
coroutineScope.launch {
|
||||
sendResult(
|
||||
context,
|
||||
packageName,
|
||||
account,
|
||||
key,
|
||||
remember.value,
|
||||
clipboardManager,
|
||||
sig,
|
||||
sig,
|
||||
it,
|
||||
permissions = permissions
|
||||
)
|
||||
}
|
||||
} else {
|
||||
Box(
|
||||
modifier
|
||||
) {
|
||||
if (intents.filterNotNull().isEmpty()) {
|
||||
Column(
|
||||
Modifier.fillMaxSize(),
|
||||
Arrangement.Center,
|
||||
Alignment.CenterHorizontally
|
||||
) {
|
||||
Text(stringResource(R.string.no_event_to_sign))
|
||||
}
|
||||
} else {
|
||||
if (intents.size == 1) {
|
||||
intents.first()?.let {
|
||||
var key = "$packageName-${it.type}"
|
||||
val appName = packageName ?: it.name
|
||||
when (it.type) {
|
||||
SignerType.GET_PUBLIC_KEY -> {
|
||||
val remember = remember {
|
||||
mutableStateOf(account.savedApps[key] ?: false)
|
||||
}
|
||||
return@LoginWithPubKey
|
||||
},
|
||||
{
|
||||
context.getAppCompatActivity()?.finish()
|
||||
}
|
||||
)
|
||||
}
|
||||
|
||||
SignerType.NIP04_DECRYPT, SignerType.NIP04_ENCRYPT, SignerType.NIP44_ENCRYPT, SignerType.NIP44_DECRYPT, SignerType.DECRYPT_ZAP_EVENT -> {
|
||||
val remember = remember {
|
||||
mutableStateOf(account.savedApps[key] ?: false)
|
||||
}
|
||||
val shouldRunOnAccept = account.savedApps[key] ?: false
|
||||
EncryptDecryptData(
|
||||
it.data,
|
||||
shouldRunOnAccept,
|
||||
remember,
|
||||
packageName,
|
||||
applicationName,
|
||||
appName,
|
||||
it.type,
|
||||
{
|
||||
if (it.type == SignerType.NIP04_ENCRYPT && it.data.contains("?iv=", ignoreCase = true)) {
|
||||
coroutineScope.launch {
|
||||
Toast.makeText(
|
||||
context,
|
||||
context.getString(R.string.message_already_encrypted),
|
||||
Toast.LENGTH_SHORT
|
||||
).show()
|
||||
LoginWithPubKey(
|
||||
appName,
|
||||
applicationName,
|
||||
it.permissions,
|
||||
{ permissions ->
|
||||
val sig = account.keyPair.pubKey.toNpub()
|
||||
coroutineScope.launch {
|
||||
sendResult(
|
||||
context,
|
||||
packageName,
|
||||
account,
|
||||
key,
|
||||
remember.value,
|
||||
clipboardManager,
|
||||
sig,
|
||||
sig,
|
||||
it,
|
||||
permissions = permissions
|
||||
)
|
||||
}
|
||||
return@LoginWithPubKey
|
||||
},
|
||||
{
|
||||
context.getAppCompatActivity()?.finish()
|
||||
}
|
||||
return@EncryptDecryptData
|
||||
} else {
|
||||
try {
|
||||
coroutineScope.launch(Dispatchers.IO) {
|
||||
val sig = try {
|
||||
AmberUtils.encryptOrDecryptData(
|
||||
it.data,
|
||||
it.type,
|
||||
account,
|
||||
it.pubKey
|
||||
)
|
||||
?: "Could not decrypt the message"
|
||||
} catch (e: Exception) {
|
||||
"Could not decrypt the message"
|
||||
}
|
||||
)
|
||||
}
|
||||
|
||||
SignerType.NIP04_DECRYPT, SignerType.NIP04_ENCRYPT, SignerType.NIP44_ENCRYPT, SignerType.NIP44_DECRYPT, SignerType.DECRYPT_ZAP_EVENT -> {
|
||||
val remember = remember {
|
||||
mutableStateOf(account.savedApps[key] ?: false)
|
||||
}
|
||||
val shouldRunOnAccept = account.savedApps[key] ?: false
|
||||
EncryptDecryptData(
|
||||
it.data,
|
||||
shouldRunOnAccept,
|
||||
remember,
|
||||
packageName,
|
||||
applicationName,
|
||||
appName,
|
||||
it.type,
|
||||
{
|
||||
if (it.type == SignerType.NIP04_ENCRYPT && it.data.contains(
|
||||
"?iv=",
|
||||
ignoreCase = true
|
||||
)
|
||||
) {
|
||||
coroutineScope.launch {
|
||||
Toast.makeText(
|
||||
context,
|
||||
context.getString(R.string.message_already_encrypted),
|
||||
Toast.LENGTH_SHORT
|
||||
).show()
|
||||
}
|
||||
return@EncryptDecryptData
|
||||
} else {
|
||||
try {
|
||||
coroutineScope.launch(Dispatchers.IO) {
|
||||
val sig = try {
|
||||
AmberUtils.encryptOrDecryptData(
|
||||
it.data,
|
||||
it.type,
|
||||
account,
|
||||
it.pubKey
|
||||
)
|
||||
?: "Could not decrypt the message"
|
||||
} catch (e: Exception) {
|
||||
"Could not decrypt the message"
|
||||
}
|
||||
|
||||
if (it.type == SignerType.NIP04_ENCRYPT && sig == "Could not decrypt the message") {
|
||||
coroutineScope.launch {
|
||||
Toast.makeText(
|
||||
context,
|
||||
"Error encrypting content",
|
||||
Toast.LENGTH_SHORT
|
||||
).show()
|
||||
}
|
||||
return@launch
|
||||
} else {
|
||||
val result =
|
||||
if (sig == "Could not decrypt the message" && (it.type == SignerType.DECRYPT_ZAP_EVENT)) {
|
||||
""
|
||||
} else {
|
||||
sig
|
||||
}
|
||||
|
||||
sendResult(
|
||||
context,
|
||||
packageName,
|
||||
account,
|
||||
key,
|
||||
remember.value,
|
||||
clipboardManager,
|
||||
result,
|
||||
result,
|
||||
it
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
return@EncryptDecryptData
|
||||
} catch (e: Exception) {
|
||||
val message = if (it.type.toString().contains("ENCRYPT", true)) {
|
||||
context.getString(R.string.encrypt)
|
||||
} else {
|
||||
context.getString(R.string.decrypt)
|
||||
}
|
||||
|
||||
if (it.type == SignerType.NIP04_ENCRYPT && sig == "Could not decrypt the message") {
|
||||
coroutineScope.launch {
|
||||
Toast.makeText(
|
||||
context,
|
||||
"Error encrypting content",
|
||||
context.getString(
|
||||
R.string.error_to_data,
|
||||
message
|
||||
),
|
||||
Toast.LENGTH_SHORT
|
||||
).show()
|
||||
}
|
||||
return@launch
|
||||
} else {
|
||||
val result =
|
||||
if (sig == "Could not decrypt the message" && (it.type == SignerType.DECRYPT_ZAP_EVENT)) {
|
||||
""
|
||||
} else {
|
||||
sig
|
||||
}
|
||||
return@EncryptDecryptData
|
||||
}
|
||||
}
|
||||
},
|
||||
{
|
||||
context.getAppCompatActivity()?.finish()
|
||||
},
|
||||
{
|
||||
try {
|
||||
AmberUtils.encryptOrDecryptData(
|
||||
it.data,
|
||||
it.type,
|
||||
account,
|
||||
it.pubKey
|
||||
)
|
||||
?: "Could not decrypt the message"
|
||||
} catch (e: Exception) {
|
||||
"Could not decrypt the message"
|
||||
}
|
||||
}
|
||||
)
|
||||
}
|
||||
|
||||
else -> {
|
||||
val event = IntentUtils.getIntent(it.data, account.keyPair)
|
||||
key = "$packageName-${it.type}-${event.kind}"
|
||||
val remember = remember {
|
||||
mutableStateOf(account.savedApps[key] ?: false)
|
||||
}
|
||||
val shouldRunOnAccept = account.savedApps[key] ?: false
|
||||
EventData(
|
||||
shouldRunOnAccept,
|
||||
remember,
|
||||
packageName,
|
||||
appName,
|
||||
applicationName,
|
||||
event,
|
||||
it.data,
|
||||
{
|
||||
if (event.pubKey != account.keyPair.pubKey.toHexKey()) {
|
||||
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
|
||||
}
|
||||
|
||||
val localEvent = try {
|
||||
Event.fromJson(it.data)
|
||||
} catch (e: Exception) {
|
||||
Event.fromJson(event.toJson())
|
||||
}
|
||||
|
||||
account.signer.sign<Event>(
|
||||
localEvent.createdAt,
|
||||
localEvent.kind,
|
||||
localEvent.tags,
|
||||
localEvent.content
|
||||
) { signedEvent ->
|
||||
coroutineScope.launch {
|
||||
sendResult(
|
||||
context,
|
||||
packageName,
|
||||
@@ -154,107 +299,294 @@ fun HomeScreen(
|
||||
key,
|
||||
remember.value,
|
||||
clipboardManager,
|
||||
result,
|
||||
result,
|
||||
signedEvent.toJson(),
|
||||
if (localEvent is LnZapRequestEvent && localEvent.tags.any { tag -> tag.any { t -> t == "anon" } }) signedEvent.toJson() else signedEvent.sig,
|
||||
it
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
return@EncryptDecryptData
|
||||
} catch (e: Exception) {
|
||||
val message =
|
||||
if (it.type.toString().contains("ENCRYPT", true)) {
|
||||
context.getString(R.string.encrypt)
|
||||
} else {
|
||||
context.getString(R.string.decrypt)
|
||||
}
|
||||
coroutineScope.launch {
|
||||
Toast.makeText(
|
||||
context,
|
||||
context.getString(R.string.error_to_data, message),
|
||||
Toast.LENGTH_SHORT
|
||||
).show()
|
||||
}
|
||||
return@EncryptDecryptData
|
||||
},
|
||||
{
|
||||
context.getAppCompatActivity()?.finish()
|
||||
}
|
||||
}
|
||||
},
|
||||
{
|
||||
context.getAppCompatActivity()?.finish()
|
||||
},
|
||||
{
|
||||
try {
|
||||
AmberUtils.encryptOrDecryptData(
|
||||
it.data,
|
||||
it.type,
|
||||
account,
|
||||
it.pubKey
|
||||
)
|
||||
?: "Could not decrypt the message"
|
||||
} catch (e: Exception) {
|
||||
"Could not decrypt the message"
|
||||
}
|
||||
)
|
||||
}
|
||||
)
|
||||
}
|
||||
|
||||
else -> {
|
||||
val event = IntentUtils.getIntent(it.data, account.keyPair)
|
||||
key = "$packageName-${it.type}-${event.kind}"
|
||||
val remember = remember {
|
||||
mutableStateOf(account.savedApps[key] ?: false)
|
||||
}
|
||||
val shouldRunOnAccept = account.savedApps[key] ?: false
|
||||
EventData(
|
||||
shouldRunOnAccept,
|
||||
remember,
|
||||
packageName,
|
||||
appName,
|
||||
applicationName,
|
||||
event,
|
||||
it.data,
|
||||
{
|
||||
if (event.pubKey != account.keyPair.pubKey.toHexKey()) {
|
||||
coroutineScope.launch {
|
||||
Toast.makeText(
|
||||
context,
|
||||
context.getString(R.string.event_pubkey_is_not_equal_to_current_logged_in_user),
|
||||
Toast.LENGTH_SHORT
|
||||
).show()
|
||||
}
|
||||
} else {
|
||||
Column(
|
||||
Modifier.fillMaxSize()
|
||||
) {
|
||||
var selectAll by remember {
|
||||
mutableStateOf(false)
|
||||
}
|
||||
Row(
|
||||
horizontalArrangement = Arrangement.SpaceBetween,
|
||||
verticalAlignment = Alignment.CenterVertically,
|
||||
modifier = Modifier
|
||||
.padding(horizontal = 8.dp)
|
||||
.clickable {
|
||||
selectAll = !selectAll
|
||||
intents
|
||||
.filterNotNull()
|
||||
.forEach {
|
||||
it.checked.value = selectAll
|
||||
}
|
||||
}
|
||||
) {
|
||||
Text(
|
||||
modifier = Modifier.weight(1f),
|
||||
text = stringResource(R.string.select_deselect_all)
|
||||
)
|
||||
Switch(
|
||||
checked = selectAll,
|
||||
onCheckedChange = {
|
||||
selectAll = !selectAll
|
||||
intents.filterNotNull().forEach {
|
||||
it.checked.value = selectAll
|
||||
}
|
||||
return@EventData
|
||||
}
|
||||
|
||||
val localEvent = try {
|
||||
Event.fromJson(it.data)
|
||||
} catch (e: Exception) {
|
||||
Event.fromJson(event.toJson())
|
||||
}
|
||||
|
||||
account.signer.sign<Event>(localEvent.createdAt, localEvent.kind, localEvent.tags, localEvent.content) { signedEvent ->
|
||||
coroutineScope.launch {
|
||||
sendResult(
|
||||
context,
|
||||
packageName,
|
||||
account,
|
||||
key,
|
||||
remember.value,
|
||||
clipboardManager,
|
||||
signedEvent.toJson(),
|
||||
if (localEvent is LnZapRequestEvent && localEvent.tags.any { tag -> tag.any { t -> t == "anon" } }) signedEvent.toJson() else signedEvent.sig,
|
||||
it
|
||||
)
|
||||
}
|
||||
LazyColumn(
|
||||
Modifier.fillMaxHeight(0.9f)
|
||||
) {
|
||||
items(intents.size) {
|
||||
var isExpanded by remember { mutableStateOf(false) }
|
||||
Card(
|
||||
Modifier
|
||||
.padding(4.dp)
|
||||
.clickable {
|
||||
isExpanded = !isExpanded
|
||||
}
|
||||
) {
|
||||
intents[it]?.let {
|
||||
val name = LocalPreferences.getAccountName(it.currentAccount)
|
||||
Row(
|
||||
Modifier
|
||||
.fillMaxWidth(),
|
||||
Arrangement.Center,
|
||||
Alignment.CenterVertically
|
||||
) {
|
||||
Text(
|
||||
name.ifBlank { it.currentAccount.toShortenHex() }
|
||||
)
|
||||
}
|
||||
}
|
||||
Row(
|
||||
verticalAlignment = Alignment.CenterVertically,
|
||||
modifier = Modifier
|
||||
.fillMaxWidth()
|
||||
.padding(4.dp)
|
||||
) {
|
||||
Icon(
|
||||
Icons.Default.run {
|
||||
if (isExpanded) {
|
||||
KeyboardArrowDown
|
||||
} else {
|
||||
KeyboardArrowUp
|
||||
}
|
||||
},
|
||||
contentDescription = "",
|
||||
tint = Color.LightGray
|
||||
)
|
||||
|
||||
intents[it]?.let {
|
||||
val appName = applicationName ?: packageName ?: it.name
|
||||
val text = if (it.type == SignerType.SIGN_EVENT) {
|
||||
val event =
|
||||
IntentUtils.getIntent(it.data, account.keyPair)
|
||||
if (event.kind == 22242) "requests client authentication" else "requests event signature"
|
||||
} else {
|
||||
when (it.type) {
|
||||
SignerType.NIP44_ENCRYPT -> stringResource(R.string.encrypt_nip44)
|
||||
SignerType.NIP04_ENCRYPT -> stringResource(R.string.encrypt_nip04)
|
||||
SignerType.NIP44_DECRYPT -> stringResource(R.string.decrypt_nip44)
|
||||
SignerType.NIP04_DECRYPT -> stringResource(R.string.decrypt_nip04)
|
||||
SignerType.DECRYPT_ZAP_EVENT -> stringResource(R.string.decrypt_zap_event)
|
||||
else -> stringResource(R.string.encrypt_decrypt)
|
||||
}
|
||||
}
|
||||
Text(
|
||||
modifier = Modifier.weight(1f),
|
||||
text = buildAnnotatedString {
|
||||
withStyle(style = SpanStyle(fontWeight = FontWeight.Bold)) {
|
||||
append(appName)
|
||||
}
|
||||
append(" $text")
|
||||
},
|
||||
fontSize = 18.sp
|
||||
)
|
||||
|
||||
Switch(
|
||||
checked = it.checked.value,
|
||||
onCheckedChange = { _ ->
|
||||
it.checked.value = !it.checked.value
|
||||
}
|
||||
)
|
||||
}
|
||||
}
|
||||
intents[it]?.let {
|
||||
if (isExpanded) {
|
||||
Column(
|
||||
Modifier
|
||||
.fillMaxSize()
|
||||
.padding(10.dp)
|
||||
) {
|
||||
Text(
|
||||
"Event content",
|
||||
fontWeight = FontWeight.Bold
|
||||
)
|
||||
val content =
|
||||
if (it.type == SignerType.SIGN_EVENT) {
|
||||
val event = IntentUtils.getIntent(
|
||||
it.data,
|
||||
account.keyPair
|
||||
)
|
||||
if (event.kind == 22242) event.relay() else event.content
|
||||
} else {
|
||||
it.data
|
||||
}
|
||||
Text(
|
||||
content,
|
||||
maxLines = 1,
|
||||
overflow = TextOverflow.Ellipsis,
|
||||
modifier = Modifier
|
||||
.fillMaxWidth()
|
||||
.padding(top = 8.dp)
|
||||
)
|
||||
RememberMyChoice(
|
||||
shouldRunOnAccept = false,
|
||||
it.rememberMyChoice.value,
|
||||
packageName,
|
||||
{ }
|
||||
) {
|
||||
it.rememberMyChoice.value = !it.rememberMyChoice.value
|
||||
intents.filter { intentData ->
|
||||
intentData?.type == it.type
|
||||
}.forEach { intentData ->
|
||||
intentData?.let { intent ->
|
||||
intent.rememberMyChoice.value =
|
||||
it.rememberMyChoice.value
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
{
|
||||
context.getAppCompatActivity()?.finish()
|
||||
}
|
||||
)
|
||||
}
|
||||
Row(
|
||||
Modifier
|
||||
.fillMaxWidth()
|
||||
.padding(10.dp),
|
||||
Arrangement.Center
|
||||
) {
|
||||
Button(
|
||||
modifier = Modifier.fillMaxWidth(),
|
||||
shape = ButtonBorder,
|
||||
onClick = {
|
||||
loading = true
|
||||
coroutineScope.launch(Dispatchers.IO) {
|
||||
try {
|
||||
val activity = context.getAppCompatActivity()
|
||||
val results = mutableListOf<Result>()
|
||||
|
||||
for (intentData in intents.filterNotNull()) {
|
||||
val localAccount =
|
||||
if (intentData.currentAccount.isNotBlank()) {
|
||||
LocalPreferences.loadFromEncryptedStorage(
|
||||
intentData.currentAccount
|
||||
)
|
||||
} else {
|
||||
account
|
||||
} ?: continue
|
||||
|
||||
if (packageName != null) {
|
||||
if (intentData.type == SignerType.SIGN_EVENT) {
|
||||
val localEvent = try {
|
||||
Event.fromJson(intentData.data)
|
||||
} catch (e: Exception) {
|
||||
Event.fromJson(
|
||||
IntentUtils.getIntent(
|
||||
intentData.data,
|
||||
localAccount.keyPair
|
||||
).toJson()
|
||||
)
|
||||
}
|
||||
val key = "$packageName-${intentData.type}-${localEvent.kind}"
|
||||
if (intentData.rememberMyChoice.value) {
|
||||
localAccount.savedApps[key] =
|
||||
intentData.rememberMyChoice.value
|
||||
LocalPreferences.saveToEncryptedStorage(
|
||||
localAccount
|
||||
)
|
||||
}
|
||||
localAccount.signer.sign<Event>(
|
||||
localEvent.createdAt,
|
||||
localEvent.kind,
|
||||
localEvent.tags,
|
||||
localEvent.content
|
||||
) { signedEvent ->
|
||||
results.add(
|
||||
Result(
|
||||
null,
|
||||
signedEvent.sig,
|
||||
intentData.id
|
||||
)
|
||||
)
|
||||
}
|
||||
} else {
|
||||
val key = "$packageName-${intentData.type}"
|
||||
localAccount.savedApps[key] =
|
||||
intentData.rememberMyChoice.value
|
||||
LocalPreferences.saveToEncryptedStorage(
|
||||
localAccount
|
||||
)
|
||||
val signature = AmberUtils.encryptOrDecryptData(
|
||||
intentData.data,
|
||||
intentData.type,
|
||||
localAccount,
|
||||
intentData.pubKey
|
||||
) ?: continue
|
||||
results.add(
|
||||
Result(
|
||||
null,
|
||||
signature,
|
||||
intentData.id
|
||||
)
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (results.isNotEmpty()) {
|
||||
val gson = GsonBuilder().serializeNulls().create()
|
||||
val json = gson.toJson(results)
|
||||
val intent = Intent()
|
||||
intent.putExtra("results", json)
|
||||
activity?.setResult(Activity.RESULT_OK, intent)
|
||||
}
|
||||
activity?.finish()
|
||||
} finally {
|
||||
loading = false
|
||||
}
|
||||
}
|
||||
}
|
||||
) {
|
||||
Text("Confirm")
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
class Result(
|
||||
val `package`: String?,
|
||||
val signature: String?,
|
||||
val id: String?
|
||||
)
|
||||
|
||||
@@ -175,7 +175,7 @@ fun GoToTop(goToTop: () -> Unit) {
|
||||
fun MainScreen(
|
||||
account: Account,
|
||||
accountStateViewModel: AccountStateViewModel,
|
||||
json: IntentData?,
|
||||
intents: List<IntentData?>,
|
||||
packageName: String?,
|
||||
appName: String?,
|
||||
route: String?
|
||||
@@ -291,7 +291,7 @@ fun MainScreen(
|
||||
Modifier
|
||||
.fillMaxSize()
|
||||
.padding(padding),
|
||||
json,
|
||||
intents,
|
||||
packageName,
|
||||
appName,
|
||||
account
|
||||
|
||||
@@ -122,10 +122,12 @@ fun EncryptDecryptData(
|
||||
|
||||
RememberMyChoice(
|
||||
shouldRunOnAccept,
|
||||
remember,
|
||||
remember.value,
|
||||
packageName,
|
||||
onAccept
|
||||
)
|
||||
) {
|
||||
remember.value = !remember.value
|
||||
}
|
||||
|
||||
AcceptRejectButtons(
|
||||
onAccept,
|
||||
|
||||
@@ -91,10 +91,12 @@ fun EventData(
|
||||
|
||||
RememberMyChoice(
|
||||
shouldRunOnAccept,
|
||||
remember,
|
||||
remember.value,
|
||||
packageName,
|
||||
onAccept
|
||||
)
|
||||
) {
|
||||
remember.value = !remember.value
|
||||
}
|
||||
|
||||
AcceptRejectButtons(
|
||||
onAccept,
|
||||
|
||||
@@ -7,7 +7,6 @@ import androidx.compose.material3.Switch
|
||||
import androidx.compose.material3.Text
|
||||
import androidx.compose.runtime.Composable
|
||||
import androidx.compose.runtime.LaunchedEffect
|
||||
import androidx.compose.runtime.MutableState
|
||||
import androidx.compose.ui.Alignment
|
||||
import androidx.compose.ui.Modifier
|
||||
import androidx.compose.ui.res.stringResource
|
||||
@@ -16,9 +15,10 @@ import com.greenart7c3.nostrsigner.R
|
||||
@Composable
|
||||
fun RememberMyChoice(
|
||||
shouldRunOnAccept: Boolean,
|
||||
remember: MutableState<Boolean>,
|
||||
remember: Boolean,
|
||||
packageName: String?,
|
||||
onAccept: () -> Unit
|
||||
onAccept: () -> Unit,
|
||||
onChanged: () -> Unit
|
||||
) {
|
||||
if (shouldRunOnAccept) {
|
||||
LaunchedEffect(Unit) {
|
||||
@@ -31,7 +31,7 @@ fun RememberMyChoice(
|
||||
verticalAlignment = Alignment.CenterVertically,
|
||||
modifier = Modifier
|
||||
.clickable {
|
||||
remember.value = !remember.value
|
||||
onChanged()
|
||||
}
|
||||
) {
|
||||
Text(
|
||||
@@ -39,9 +39,9 @@ fun RememberMyChoice(
|
||||
text = stringResource(R.string.remember_my_choice_and_don_t_ask_again)
|
||||
)
|
||||
Switch(
|
||||
checked = remember.value,
|
||||
checked = remember,
|
||||
onCheckedChange = {
|
||||
remember.value = !remember.value
|
||||
onChanged()
|
||||
}
|
||||
)
|
||||
}
|
||||
|
||||
@@ -81,4 +81,5 @@
|
||||
<string name="relay">Relay</string>
|
||||
<string name="go_to_top">go to top</string>
|
||||
<string name="event_copied_to_the_clipboard">Event copied to the clipboard</string>
|
||||
<string name="select_deselect_all">Select/Deselect all</string>
|
||||
</resources>
|
||||
Reference in New Issue
Block a user