Migrates from PAY to NOTIFY as per https://github.com/nostr-protocol/nips/pull/901
This commit is contained in:
@@ -152,9 +152,7 @@ class Account(
|
|||||||
|
|
||||||
data class PaymentRequest(
|
data class PaymentRequest(
|
||||||
val relayUrl: String,
|
val relayUrl: String,
|
||||||
val lnInvoice: String?,
|
val description: String
|
||||||
val description: String?,
|
|
||||||
val otherOptionsUrl: String?
|
|
||||||
)
|
)
|
||||||
var transientPaymentRequestDismissals: Set<PaymentRequest> = emptySet()
|
var transientPaymentRequestDismissals: Set<PaymentRequest> = emptySet()
|
||||||
val transientPaymentRequests: MutableStateFlow<Set<PaymentRequest>> = MutableStateFlow(emptySet())
|
val transientPaymentRequests: MutableStateFlow<Set<PaymentRequest>> = MutableStateFlow(emptySet())
|
||||||
|
|||||||
@@ -245,11 +245,11 @@ object NostrAccountDataSource : NostrDataSource("AccountData") {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
override fun pay(relay: Relay, lnInvoice: String?, description: String?, otherOptionsUrl: String?) {
|
override fun notify(relay: Relay, description: String) {
|
||||||
super.pay(relay, lnInvoice, description, otherOptionsUrl)
|
super.notify(relay, description)
|
||||||
|
|
||||||
if (this::account.isInitialized) {
|
if (this::account.isInitialized) {
|
||||||
account.addPaymentRequestIfNew(Account.PaymentRequest(relay.url, lnInvoice, description, otherOptionsUrl))
|
account.addPaymentRequestIfNew(Account.PaymentRequest(relay.url, description))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -77,13 +77,11 @@ abstract class NostrDataSource(val debugName: String) {
|
|||||||
auth(relay, challenge)
|
auth(relay, challenge)
|
||||||
}
|
}
|
||||||
|
|
||||||
override fun onPaymentRequired(
|
override fun onNotify(
|
||||||
relay: Relay,
|
relay: Relay,
|
||||||
lnInvoice: String?,
|
description: String
|
||||||
description: String?,
|
|
||||||
otherOptionsUrl: String?
|
|
||||||
) {
|
) {
|
||||||
pay(relay, lnInvoice, description, otherOptionsUrl)
|
notify(relay, description)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -199,5 +197,5 @@ abstract class NostrDataSource(val debugName: String) {
|
|||||||
|
|
||||||
abstract fun updateChannelFilters()
|
abstract fun updateChannelFilters()
|
||||||
open fun auth(relay: Relay, challenge: String) = Unit
|
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
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -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.
|
// Releases the Web thread for the new payload.
|
||||||
// May need to add a processing queue if processing new events become too costly.
|
// May need to add a processing queue if processing new events become too costly.
|
||||||
GlobalScope.launch(Dispatchers.Default) {
|
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 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
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -244,9 +244,9 @@ class Relay(
|
|||||||
// Log.w("Relay", "Relay$url, ${msg[1].asString}")
|
// Log.w("Relay", "Relay$url, ${msg[1].asString}")
|
||||||
it.onAuth(this@Relay, msgArray[1].asText())
|
it.onAuth(this@Relay, msgArray[1].asText())
|
||||||
}
|
}
|
||||||
"PAY" -> listeners.forEach {
|
"NOTIFY" -> listeners.forEach {
|
||||||
// Log.w("Relay", "Relay$url, ${msg[1].asString}")
|
// 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 {
|
else -> listeners.forEach {
|
||||||
Log.w("Relay", "Unsupported message: $newMessage")
|
Log.w("Relay", "Unsupported message: $newMessage")
|
||||||
@@ -326,27 +326,23 @@ class Relay(
|
|||||||
eventUploadCounterInBytes += event.bytesUsedInMemory()
|
eventUploadCounterInBytes += event.bytesUsedInMemory()
|
||||||
} else {
|
} else {
|
||||||
if (write) {
|
if (write) {
|
||||||
|
val event = """["EVENT",${signedEvent.toJson()}]"""
|
||||||
if (isConnected()) {
|
if (isConnected()) {
|
||||||
if (isReady) {
|
if (isReady) {
|
||||||
val event = """["EVENT",${signedEvent.toJson()}]"""
|
|
||||||
socket?.send(event)
|
socket?.send(event)
|
||||||
eventUploadCounterInBytes += event.bytesUsedInMemory()
|
eventUploadCounterInBytes += event.bytesUsedInMemory()
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
// waits 60 seconds to reconnect after disconnected.
|
// sends all filters after connection is successful.
|
||||||
if (TimeUtils.now() > closingTimeInSeconds + RECONNECTING_IN_SECONDS) {
|
connectAndRun {
|
||||||
// sends all filters after connection is successful.
|
checkNotInMainThread()
|
||||||
connectAndRun {
|
|
||||||
checkNotInMainThread()
|
|
||||||
|
|
||||||
val event = """["EVENT",${signedEvent.toJson()}]"""
|
socket?.send(event)
|
||||||
socket?.send(event)
|
eventUploadCounterInBytes += event.bytesUsedInMemory()
|
||||||
eventUploadCounterInBytes += event.bytesUsedInMemory()
|
|
||||||
|
|
||||||
// Sends everything.
|
// Sends everything.
|
||||||
Client.allSubscriptions().forEach {
|
Client.allSubscriptions().forEach {
|
||||||
sendFilter(requestId = it)
|
sendFilter(requestId = it)
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -401,6 +397,6 @@ class Relay(
|
|||||||
/**
|
/**
|
||||||
* Relay sent an invoice
|
* Relay sent an invoice
|
||||||
*/
|
*/
|
||||||
fun onPaymentRequired(relay: Relay, lnInvoice: String?, description: String?, otherOptionsUrl: String?)
|
fun onNotify(relay: Relay, description: String)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -113,7 +113,7 @@ object RelayPool : Relay.Listener {
|
|||||||
|
|
||||||
fun onAuth(relay: Relay, challenge: String)
|
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) {
|
override fun onEvent(relay: Relay, subscriptionId: String, event: Event) {
|
||||||
@@ -140,8 +140,8 @@ object RelayPool : Relay.Listener {
|
|||||||
listeners.forEach { it.onAuth(relay, challenge) }
|
listeners.forEach { it.onAuth(relay, challenge) }
|
||||||
}
|
}
|
||||||
|
|
||||||
override fun onPaymentRequired(relay: Relay, lnInvoice: String?, description: String?, otherOptionsUrl: String?) {
|
override fun onNotify(relay: Relay, description: String) {
|
||||||
listeners.forEach { it.onPaymentRequired(relay, lnInvoice, description, otherOptionsUrl) }
|
listeners.forEach { it.onNotify(relay, description) }
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun updateStatus() {
|
private fun updateStatus() {
|
||||||
|
|||||||
@@ -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))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
)
|
||||||
|
}
|
||||||
@@ -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))
|
|
||||||
}
|
|
||||||
}
|
|
||||||
)
|
|
||||||
}
|
|
||||||
@@ -56,7 +56,7 @@ import androidx.navigation.compose.rememberNavController
|
|||||||
import com.vitorpamplona.amethyst.R
|
import com.vitorpamplona.amethyst.R
|
||||||
import com.vitorpamplona.amethyst.model.BooleanType
|
import com.vitorpamplona.amethyst.model.BooleanType
|
||||||
import com.vitorpamplona.amethyst.ui.actions.InformationDialog
|
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.ChannelFabColumn
|
||||||
import com.vitorpamplona.amethyst.ui.buttons.NewCommunityNoteButton
|
import com.vitorpamplona.amethyst.ui.buttons.NewCommunityNoteButton
|
||||||
import com.vitorpamplona.amethyst.ui.buttons.NewImageButton
|
import com.vitorpamplona.amethyst.ui.buttons.NewImageButton
|
||||||
@@ -136,7 +136,7 @@ fun MainScreen(
|
|||||||
}
|
}
|
||||||
|
|
||||||
DisplayErrorMessages(accountViewModel)
|
DisplayErrorMessages(accountViewModel)
|
||||||
DisplayPayMessages(accountViewModel)
|
DisplayNotifyMessages(accountViewModel, nav)
|
||||||
|
|
||||||
val navPopBack = remember(navController) {
|
val navPopBack = remember(navController) {
|
||||||
{
|
{
|
||||||
@@ -421,18 +421,15 @@ private fun DisplayErrorMessages(accountViewModel: AccountViewModel) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
@Composable
|
@Composable
|
||||||
private fun DisplayPayMessages(accountViewModel: AccountViewModel) {
|
private fun DisplayNotifyMessages(accountViewModel: AccountViewModel, nav: (String) -> Unit) {
|
||||||
val openDialogMsg = accountViewModel.account.transientPaymentRequests.collectAsStateWithLifecycle(null)
|
val openDialogMsg = accountViewModel.account.transientPaymentRequests.collectAsStateWithLifecycle(null)
|
||||||
|
|
||||||
openDialogMsg.value?.firstOrNull()?.let { request ->
|
openDialogMsg.value?.firstOrNull()?.let { request ->
|
||||||
PayRequestDialog(
|
NotifyRequestDialog(
|
||||||
stringResource(id = R.string.payment_required_title, request.relayUrl.removePrefix("wss://").removeSuffix("/")),
|
title = stringResource(id = R.string.payment_required_title, request.relayUrl.removePrefix("wss://").removeSuffix("/")),
|
||||||
request.description?.let {
|
textContent = request.description,
|
||||||
stringResource(id = R.string.payment_required_explain, it)
|
accountViewModel = accountViewModel,
|
||||||
} ?: stringResource(id = R.string.payment_required_explain_null_description),
|
nav = nav
|
||||||
request.lnInvoice,
|
|
||||||
stringResource(id = R.string.payment_required_explain2),
|
|
||||||
request.otherOptionsUrl
|
|
||||||
) {
|
) {
|
||||||
accountViewModel.dismissPaymentRequest(request)
|
accountViewModel.dismissPaymentRequest(request)
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -662,10 +662,5 @@
|
|||||||
After installing, select the app you want to use in the Settings.
|
After installing, select the app you want to use in the Settings.
|
||||||
</string>
|
</string>
|
||||||
|
|
||||||
<string name="payment_required_title">Payment Required for %1$s</string>
|
<string name="payment_required_title">Message from %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>
|
|
||||||
</resources>
|
</resources>
|
||||||
|
|||||||
Reference in New Issue
Block a user