This commit is contained in:
Vitor Pamplona 2023-11-28 16:07:24 -05:00
parent 5356d869fd
commit e2aaa8efe0
10 changed files with 109 additions and 158 deletions

View File

@ -152,9 +152,7 @@ class Account(
data class PaymentRequest(
val relayUrl: String,
val lnInvoice: String?,
val description: String?,
val otherOptionsUrl: String?
val description: String
)
var transientPaymentRequestDismissals: Set<PaymentRequest> = emptySet()
val transientPaymentRequests: MutableStateFlow<Set<PaymentRequest>> = MutableStateFlow(emptySet())

View File

@ -245,11 +245,11 @@ object NostrAccountDataSource : NostrDataSource("AccountData") {
}
}
override fun pay(relay: Relay, lnInvoice: String?, description: String?, otherOptionsUrl: String?) {
super.pay(relay, lnInvoice, description, otherOptionsUrl)
override fun notify(relay: Relay, description: String) {
super.notify(relay, description)
if (this::account.isInitialized) {
account.addPaymentRequestIfNew(Account.PaymentRequest(relay.url, lnInvoice, description, otherOptionsUrl))
account.addPaymentRequestIfNew(Account.PaymentRequest(relay.url, description))
}
}
}

View File

@ -77,13 +77,11 @@ abstract class NostrDataSource(val debugName: String) {
auth(relay, challenge)
}
override fun onPaymentRequired(
override fun onNotify(
relay: Relay,
lnInvoice: String?,
description: String?,
otherOptionsUrl: String?
description: String
) {
pay(relay, lnInvoice, description, otherOptionsUrl)
notify(relay, description)
}
}
@ -199,5 +197,5 @@ abstract class NostrDataSource(val debugName: String) {
abstract fun updateChannelFilters()
open fun auth(relay: Relay, challenge: String) = Unit
open fun pay(relay: Relay, lnInvoice: String?, description: String?, otherOptionsUrl: String?) = Unit
open fun notify(relay: Relay, description: String) = Unit
}

View File

@ -177,11 +177,11 @@ object Client : RelayPool.Listener {
}
}
override fun onPaymentRequired(relay: Relay, lnInvoice: String?, description: String?, otherOptionsUrl: String?) {
override fun onNotify(relay: Relay, description: String) {
// Releases the Web thread for the new payload.
// May need to add a processing queue if processing new events become too costly.
GlobalScope.launch(Dispatchers.Default) {
listeners.forEach { it.onPaymentRequired(relay, lnInvoice, description, otherOptionsUrl) }
listeners.forEach { it.onNotify(relay, description) }
}
}
@ -224,6 +224,6 @@ object Client : RelayPool.Listener {
open fun onAuth(relay: Relay, challenge: String) = Unit
open fun onPaymentRequired(relay: Relay, lnInvoice: String?, description: String?, otherOptionsUrl: String?) = Unit
open fun onNotify(relay: Relay, description: String) = Unit
}
}

View File

@ -244,9 +244,9 @@ class Relay(
// Log.w("Relay", "Relay$url, ${msg[1].asString}")
it.onAuth(this@Relay, msgArray[1].asText())
}
"PAY" -> listeners.forEach {
"NOTIFY" -> listeners.forEach {
// Log.w("Relay", "Relay$url, ${msg[1].asString}")
it.onPaymentRequired(this@Relay, msgArray[1].asText(), msgArray[2].asText(), msgArray[3].asText())
it.onNotify(this@Relay, msgArray[1].asText())
}
else -> listeners.forEach {
Log.w("Relay", "Unsupported message: $newMessage")
@ -326,20 +326,17 @@ class Relay(
eventUploadCounterInBytes += event.bytesUsedInMemory()
} else {
if (write) {
val event = """["EVENT",${signedEvent.toJson()}]"""
if (isConnected()) {
if (isReady) {
val event = """["EVENT",${signedEvent.toJson()}]"""
socket?.send(event)
eventUploadCounterInBytes += event.bytesUsedInMemory()
}
} else {
// waits 60 seconds to reconnect after disconnected.
if (TimeUtils.now() > closingTimeInSeconds + RECONNECTING_IN_SECONDS) {
// sends all filters after connection is successful.
connectAndRun {
checkNotInMainThread()
val event = """["EVENT",${signedEvent.toJson()}]"""
socket?.send(event)
eventUploadCounterInBytes += event.bytesUsedInMemory()
@ -352,7 +349,6 @@ class Relay(
}
}
}
}
fun close(subscriptionId: String) {
socket?.send("""["CLOSE","$subscriptionId"]""")
@ -401,6 +397,6 @@ class Relay(
/**
* Relay sent an invoice
*/
fun onPaymentRequired(relay: Relay, lnInvoice: String?, description: String?, otherOptionsUrl: String?)
fun onNotify(relay: Relay, description: String)
}
}

View File

@ -113,7 +113,7 @@ object RelayPool : Relay.Listener {
fun onAuth(relay: Relay, challenge: String)
fun onPaymentRequired(relay: Relay, lnInvoice: String?, description: String?, otherOptionsUrl: String?)
fun onNotify(relay: Relay, description: String)
}
override fun onEvent(relay: Relay, subscriptionId: String, event: Event) {
@ -140,8 +140,8 @@ object RelayPool : Relay.Listener {
listeners.forEach { it.onAuth(relay, challenge) }
}
override fun onPaymentRequired(relay: Relay, lnInvoice: String?, description: String?, otherOptionsUrl: String?) {
listeners.forEach { it.onPaymentRequired(relay, lnInvoice, description, otherOptionsUrl) }
override fun onNotify(relay: Relay, description: String) {
listeners.forEach { it.onNotify(relay, description) }
}
private fun updateStatus() {

View File

@ -0,0 +1,74 @@
package com.vitorpamplona.amethyst.ui.actions
import androidx.compose.foundation.layout.PaddingValues
import androidx.compose.foundation.layout.Row
import androidx.compose.foundation.layout.Spacer
import androidx.compose.foundation.layout.fillMaxWidth
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.MaterialTheme
import androidx.compose.material3.Text
import androidx.compose.runtime.Composable
import androidx.compose.runtime.mutableStateOf
import androidx.compose.runtime.remember
import androidx.compose.ui.Alignment
import androidx.compose.ui.Modifier
import androidx.compose.ui.res.stringResource
import com.vitorpamplona.amethyst.R
import com.vitorpamplona.amethyst.ui.components.TranslatableRichTextViewer
import com.vitorpamplona.amethyst.ui.screen.loggedIn.AccountViewModel
import com.vitorpamplona.amethyst.ui.theme.Size16dp
import com.vitorpamplona.amethyst.ui.theme.StdHorzSpacer
import com.vitorpamplona.quartz.events.EmptyTagList
@Composable
fun NotifyRequestDialog(
title: String,
textContent: String,
buttonColors: ButtonColors = ButtonDefaults.buttonColors(),
accountViewModel: AccountViewModel,
nav: (String) -> Unit,
onDismiss: () -> Unit
) {
AlertDialog(
onDismissRequest = onDismiss,
title = {
Text(title)
},
text = {
val defaultBackground = MaterialTheme.colorScheme.background
val background = remember {
mutableStateOf(defaultBackground)
}
TranslatableRichTextViewer(
textContent,
canPreview = true,
Modifier.fillMaxWidth(),
EmptyTagList,
background,
accountViewModel,
nav
)
},
confirmButton = {
Button(onClick = onDismiss, colors = buttonColors, contentPadding = PaddingValues(horizontal = Size16dp)) {
Row(
verticalAlignment = Alignment.CenterVertically
) {
Icon(
imageVector = Icons.Outlined.Done,
contentDescription = null
)
Spacer(StdHorzSpacer)
Text(stringResource(R.string.error_dialog_button_ok))
}
}
}
)
}

View File

@ -1,107 +0,0 @@
package com.vitorpamplona.amethyst.ui.actions
import androidx.compose.animation.Crossfade
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.material.icons.Icons
import androidx.compose.material.icons.outlined.OpenInNew
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.LocalTextStyle
import androidx.compose.material3.Text
import androidx.compose.material3.TextButton
import androidx.compose.runtime.Composable
import androidx.compose.ui.Alignment
import androidx.compose.ui.platform.LocalUriHandler
import androidx.compose.ui.res.stringResource
import androidx.compose.ui.text.style.TextDirection
import com.vitorpamplona.amethyst.R
import com.vitorpamplona.amethyst.ui.components.InvoicePreview
import com.vitorpamplona.amethyst.ui.components.LoadValueFromInvoice
import com.vitorpamplona.amethyst.ui.theme.Size16dp
import com.vitorpamplona.amethyst.ui.theme.StdHorzSpacer
import com.vitorpamplona.amethyst.ui.theme.StdVertSpacer
@Composable
fun PayRequestDialog(
title: String,
textContent: String,
lnInvoice: String?,
textContent2: String,
otherOptions: String?,
buttonColors: ButtonColors = ButtonDefaults.buttonColors(),
onDismiss: () -> Unit
) {
val uri = LocalUriHandler.current
val uriOpener: @Composable (() -> Unit) = otherOptions?.let {
{
Button(
onClick = {
runCatching {
uri.openUri(it)
}
},
colors = buttonColors,
contentPadding = PaddingValues(horizontal = Size16dp)
) {
Row(
verticalAlignment = Alignment.CenterVertically
) {
Icon(
imageVector = Icons.Outlined.OpenInNew,
contentDescription = null
)
Spacer(StdHorzSpacer)
Text(stringResource(R.string.other_options))
}
}
}
} ?: {
Row() {}
}
AlertDialog(
onDismissRequest = onDismiss,
title = {
Text(title)
},
text = {
Column {
Text(textContent)
Spacer(modifier = StdVertSpacer)
if (lnInvoice != null) {
LoadValueFromInvoice(lnbcWord = lnInvoice) { invoiceAmount ->
Crossfade(targetState = invoiceAmount, label = "PayRequestDialog") {
if (it != null) {
InvoicePreview(it.invoice, it.amount)
} else {
Text(
text = lnInvoice,
style = LocalTextStyle.current.copy(textDirection = TextDirection.Content)
)
}
}
}
}
Spacer(modifier = StdVertSpacer)
Text(textContent2)
}
},
confirmButton = uriOpener,
dismissButton = {
TextButton(
onClick = {
onDismiss()
}
) {
Text(text = stringResource(R.string.dismiss))
}
}
)
}

View File

@ -56,7 +56,7 @@ import androidx.navigation.compose.rememberNavController
import com.vitorpamplona.amethyst.R
import com.vitorpamplona.amethyst.model.BooleanType
import com.vitorpamplona.amethyst.ui.actions.InformationDialog
import com.vitorpamplona.amethyst.ui.actions.PayRequestDialog
import com.vitorpamplona.amethyst.ui.actions.NotifyRequestDialog
import com.vitorpamplona.amethyst.ui.buttons.ChannelFabColumn
import com.vitorpamplona.amethyst.ui.buttons.NewCommunityNoteButton
import com.vitorpamplona.amethyst.ui.buttons.NewImageButton
@ -136,7 +136,7 @@ fun MainScreen(
}
DisplayErrorMessages(accountViewModel)
DisplayPayMessages(accountViewModel)
DisplayNotifyMessages(accountViewModel, nav)
val navPopBack = remember(navController) {
{
@ -421,18 +421,15 @@ private fun DisplayErrorMessages(accountViewModel: AccountViewModel) {
}
@Composable
private fun DisplayPayMessages(accountViewModel: AccountViewModel) {
private fun DisplayNotifyMessages(accountViewModel: AccountViewModel, nav: (String) -> Unit) {
val openDialogMsg = accountViewModel.account.transientPaymentRequests.collectAsStateWithLifecycle(null)
openDialogMsg.value?.firstOrNull()?.let { request ->
PayRequestDialog(
stringResource(id = R.string.payment_required_title, request.relayUrl.removePrefix("wss://").removeSuffix("/")),
request.description?.let {
stringResource(id = R.string.payment_required_explain, it)
} ?: stringResource(id = R.string.payment_required_explain_null_description),
request.lnInvoice,
stringResource(id = R.string.payment_required_explain2),
request.otherOptionsUrl
NotifyRequestDialog(
title = stringResource(id = R.string.payment_required_title, request.relayUrl.removePrefix("wss://").removeSuffix("/")),
textContent = request.description,
accountViewModel = accountViewModel,
nav = nav
) {
accountViewModel.dismissPaymentRequest(request)
}

View File

@ -662,10 +662,5 @@
After installing, select the app you want to use in the Settings.
</string>
<string name="payment_required_title">Payment Required for %1$s</string>
<string name="payment_required_explain">Relay has requested a payment of the invoice below for the %1$s.</string>
<string name="payment_required_explain_null_description">Relay has requested a payment of the invoice below</string>
<string name="payment_required_explain2">If you do not intend to use this relay anymore, please remove it from your relay list</string>
<string name="dismiss">Dismiss</string>
<string name="other_options">See other options</string>
<string name="payment_required_title">Message from %1$s</string>
</resources>