From ef8ca5e4cbc10dc8bf320a92dd619927d1a0cba3 Mon Sep 17 00:00:00 2001 From: greenart7c3 Date: Mon, 20 May 2024 09:08:37 -0300 Subject: [PATCH] show relay error messages to the user --- .../greenart7c3/nostrsigner/relays/Relay.kt | 6 +- .../nostrsigner/relays/RelayPool.kt | 2 + .../nostrsigner/ui/AccountScreen.kt | 91 +++++++++++++++++++ .../nostrsigner/ui/AccountStateViewModel.kt | 29 ++++++ app/src/main/res/values/strings.xml | 1 + 5 files changed, 128 insertions(+), 1 deletion(-) diff --git a/app/src/main/java/com/greenart7c3/nostrsigner/relays/Relay.kt b/app/src/main/java/com/greenart7c3/nostrsigner/relays/Relay.kt index ee594ef4..6cd9ac6f 100644 --- a/app/src/main/java/com/greenart7c3/nostrsigner/relays/Relay.kt +++ b/app/src/main/java/com/greenart7c3/nostrsigner/relays/Relay.kt @@ -309,7 +309,11 @@ class Relay( Log.w("Relay", "Relay on OK $url, $eventId, $success, $message") it.onSendResponse(this@Relay, eventId, success, message) - onOk?.let { it() } + if (success) { + onOk?.let { it() } + } else if (message.isNotEmpty()) { + RelayPool.accountStateViewModel?.toast("Relay", message) + } } "AUTH" -> listeners.forEach { diff --git a/app/src/main/java/com/greenart7c3/nostrsigner/relays/RelayPool.kt b/app/src/main/java/com/greenart7c3/nostrsigner/relays/RelayPool.kt index 640fe91b..c4583606 100644 --- a/app/src/main/java/com/greenart7c3/nostrsigner/relays/RelayPool.kt +++ b/app/src/main/java/com/greenart7c3/nostrsigner/relays/RelayPool.kt @@ -22,6 +22,7 @@ package com.greenart7c3.nostrsigner.relays import androidx.compose.runtime.Immutable import com.greenart7c3.nostrsigner.checkNotInMainThread +import com.greenart7c3.nostrsigner.ui.AccountStateViewModel import com.vitorpamplona.quartz.events.Event import com.vitorpamplona.quartz.events.EventInterface import kotlinx.coroutines.DelicateCoroutinesApi @@ -36,6 +37,7 @@ import kotlinx.coroutines.launch * RelayPool manages the connection to multiple Relays and lets consumers deal with simple events. */ object RelayPool : Relay.Listener { + var accountStateViewModel: AccountStateViewModel? = null private var relays = listOf() private var listeners = setOf() diff --git a/app/src/main/java/com/greenart7c3/nostrsigner/ui/AccountScreen.kt b/app/src/main/java/com/greenart7c3/nostrsigner/ui/AccountScreen.kt index 3d73614c..a78ab0d5 100644 --- a/app/src/main/java/com/greenart7c3/nostrsigner/ui/AccountScreen.kt +++ b/app/src/main/java/com/greenart7c3/nostrsigner/ui/AccountScreen.kt @@ -5,6 +5,19 @@ import android.content.Intent import androidx.compose.animation.Crossfade import androidx.compose.animation.core.tween import androidx.compose.foundation.layout.Column +import androidx.compose.foundation.layout.PaddingValues +import androidx.compose.foundation.layout.Row +import androidx.compose.foundation.layout.Spacer +import androidx.compose.foundation.layout.width +import androidx.compose.foundation.text.selection.SelectionContainer +import androidx.compose.material.icons.Icons +import androidx.compose.material.icons.outlined.Done +import androidx.compose.material3.AlertDialog +import androidx.compose.material3.Button +import androidx.compose.material3.ButtonColors +import androidx.compose.material3.ButtonDefaults +import androidx.compose.material3.Icon +import androidx.compose.material3.Text import androidx.compose.runtime.Composable import androidx.compose.runtime.LaunchedEffect import androidx.compose.runtime.SideEffect @@ -14,12 +27,20 @@ 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.LocalContext +import androidx.compose.ui.res.stringResource +import androidx.compose.ui.unit.dp +import androidx.lifecycle.compose.collectAsStateWithLifecycle import com.greenart7c3.nostrsigner.BuildConfig import com.greenart7c3.nostrsigner.LocalPreferences import com.greenart7c3.nostrsigner.NostrSigner +import com.greenart7c3.nostrsigner.R import com.greenart7c3.nostrsigner.models.IntentData import com.greenart7c3.nostrsigner.relays.Client import com.greenart7c3.nostrsigner.relays.Relay +import com.greenart7c3.nostrsigner.relays.RelayPool import com.greenart7c3.nostrsigner.service.ConnectivityService import com.greenart7c3.nostrsigner.service.IntentUtils import com.greenart7c3.nostrsigner.service.NotificationDataSource @@ -120,9 +141,79 @@ fun AccountScreen( } } + RelayPool.accountStateViewModel = accountStateViewModel + + DisplayErrorMessages(accountStateViewModel) MainScreen(state.account, accountStateViewModel, newIntents, packageName, appName, localRoute, database) } } } } } + +@Composable +private fun DisplayErrorMessages(accountViewModel: AccountStateViewModel) { + val context = LocalContext.current + val openDialogMsg = accountViewModel.toasts.collectAsStateWithLifecycle(null) + + openDialogMsg.value?.let { obj -> + when (obj) { + is ResourceToastMsg -> + if (obj.params != null) { + InformationDialog( + context.getString(obj.titleResId), + context.getString(obj.resourceId, *obj.params), + ) { + accountViewModel.clearToasts() + } + } else { + InformationDialog( + context.getString(obj.titleResId), + context.getString(obj.resourceId), + ) { + accountViewModel.clearToasts() + } + } + + is StringToastMsg -> + InformationDialog( + obj.title, + obj.msg, + ) { + accountViewModel.clearToasts() + } + } + } +} + +@Composable +fun InformationDialog( + title: String, + textContent: String, + buttonColors: ButtonColors = ButtonDefaults.buttonColors(), + onDismiss: () -> Unit, +) { + AlertDialog( + onDismissRequest = onDismiss, + title = { Text(title) }, + text = { SelectionContainer { Text(textContent) } }, + confirmButton = { + Button( + onClick = onDismiss, + colors = buttonColors, + contentPadding = PaddingValues(horizontal = 16.dp), + ) { + Row( + verticalAlignment = Alignment.CenterVertically, + ) { + Icon( + imageVector = Icons.Outlined.Done, + contentDescription = null, + ) + Spacer(Modifier.width(5.dp)) + Text(stringResource(R.string.ok)) + } + } + }, + ) +} diff --git a/app/src/main/java/com/greenart7c3/nostrsigner/ui/AccountStateViewModel.kt b/app/src/main/java/com/greenart7c3/nostrsigner/ui/AccountStateViewModel.kt index b9453eaf..19ff340a 100644 --- a/app/src/main/java/com/greenart7c3/nostrsigner/ui/AccountStateViewModel.kt +++ b/app/src/main/java/com/greenart7c3/nostrsigner/ui/AccountStateViewModel.kt @@ -1,7 +1,9 @@ package com.greenart7c3.nostrsigner.ui +import androidx.compose.runtime.Immutable import androidx.compose.runtime.Stable import androidx.lifecycle.ViewModel +import androidx.lifecycle.viewModelScope import com.greenart7c3.nostrsigner.LocalPreferences import com.greenart7c3.nostrsigner.models.Account import com.vitorpamplona.quartz.crypto.CryptoUtils @@ -11,20 +13,47 @@ import fr.acinq.secp256k1.Hex import kotlinx.coroutines.DelicateCoroutinesApi import kotlinx.coroutines.Dispatchers import kotlinx.coroutines.GlobalScope +import kotlinx.coroutines.channels.BufferOverflow +import kotlinx.coroutines.flow.MutableSharedFlow import kotlinx.coroutines.flow.MutableStateFlow import kotlinx.coroutines.flow.asStateFlow import kotlinx.coroutines.flow.update import kotlinx.coroutines.launch +@Immutable +open class ToastMsg() + +@Immutable +class StringToastMsg(val title: String, val msg: String) : ToastMsg() + +@Immutable +class ResourceToastMsg( + val titleResId: Int, + val resourceId: Int, + val params: Array? = null, +) : ToastMsg() + @Stable class AccountStateViewModel(npub: String?) : ViewModel() { private val _accountContent = MutableStateFlow(AccountState.LoggedOff) val accountContent = _accountContent.asStateFlow() + val toasts = MutableSharedFlow(0, 3, onBufferOverflow = BufferOverflow.DROP_OLDEST) init { tryLoginExistingAccount(null, npub) } + fun clearToasts() { + viewModelScope.launch { toasts.emit(null) } + } + + fun toast( + title: String, + message: String, + ) { + viewModelScope.launch { toasts.emit(StringToastMsg(title, message)) } + } + private fun tryLoginExistingAccount( route: String?, npub: String?, diff --git a/app/src/main/res/values/strings.xml b/app/src/main/res/values/strings.xml index 87b09e46..a74f36c6 100644 --- a/app/src/main/res/values/strings.xml +++ b/app/src/main/res/values/strings.xml @@ -127,4 +127,5 @@ No activities found Home Settings + OK