mirror of
https://github.com/vitorpamplona/amethyst.git
synced 2026-08-08 23:54:39 +00:00
feat(wallet): prefer NIP-44 for NWC and notify non-zap payments
NIP-44 encryption preference: NwcSignerState now fetches each wallet's kind 13194 info event (via the relay client, injected by Account), caches whether it advertises `nip44_v2`, and sends pay/RPC requests with NIP-44 when supported — satisfying NIP-47's "client should always prefer nip44 if supported by the wallet service". Falls back to NIP-04 (the legacy default) when unknown or unsupported; response decryption already auto-detects the scheme. Non-zap payment notifications: NwcPaymentNotificationWatcher keeps a standing subscription to each connected wallet's NIP-47 notification stream (kind 23197/23196) on the wallet relay, decrypts payment_received events, and posts a tray notification on a new Payments Received channel. Payments carrying a NIP-57 zap request are skipped, since those already surface through the kind-9735 ZapNotification path — so only plain, non-zap incoming payments are announced. Deep-link pairing: the "Connect wallet via app" button now builds its `nostrnwc://connect` URI through the tested quartz Nip47DeepLink helper instead of a hardcoded percent-encoded string. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01RDAAS4ktFbWtRnEVXQsjfs
This commit is contained in:
@@ -71,6 +71,7 @@ import com.vitorpamplona.amethyst.service.images.ThumbnailDiskCache
|
||||
import com.vitorpamplona.amethyst.service.location.LocationState
|
||||
import com.vitorpamplona.amethyst.service.notifications.AlwaysOnNotificationServiceManager
|
||||
import com.vitorpamplona.amethyst.service.notifications.NotificationDispatcher
|
||||
import com.vitorpamplona.amethyst.service.notifications.NwcPaymentNotificationWatcher
|
||||
import com.vitorpamplona.amethyst.service.notifications.PokeyReceiver
|
||||
import com.vitorpamplona.amethyst.service.okhttp.DualHttpClientManager
|
||||
import com.vitorpamplona.amethyst.service.okhttp.DualHttpClientManagerForRelays
|
||||
@@ -882,6 +883,16 @@ class AppModules(
|
||||
scope = applicationIOScope,
|
||||
)
|
||||
|
||||
// Surfaces non-zap Lightning payments reported by the logged-in account's NWC
|
||||
// wallet(s) as tray notifications (zaps are already shown via ZapNotification).
|
||||
val nwcPaymentNotificationWatcher =
|
||||
NwcPaymentNotificationWatcher(
|
||||
context = appContext,
|
||||
client = client,
|
||||
scope = applicationIOScope,
|
||||
accountFlow = sessionManager.accountContent.map { (it as? AccountState.LoggedIn)?.account },
|
||||
).also { it.start() }
|
||||
|
||||
fun subscribedFlow(
|
||||
address: Address,
|
||||
account: Account,
|
||||
|
||||
@@ -291,6 +291,7 @@ import com.vitorpamplona.quartz.nip37Drafts.DraftEventCache
|
||||
import com.vitorpamplona.quartz.nip37Drafts.DraftWrapEvent
|
||||
import com.vitorpamplona.quartz.nip42RelayAuth.RelayAuthEvent
|
||||
import com.vitorpamplona.quartz.nip47WalletConnect.Nip47WalletConnect
|
||||
import com.vitorpamplona.quartz.nip47WalletConnect.events.NwcInfoEvent
|
||||
import com.vitorpamplona.quartz.nip47WalletConnect.rpc.Request
|
||||
import com.vitorpamplona.quartz.nip47WalletConnect.rpc.Response
|
||||
import com.vitorpamplona.quartz.nip51Lists.bookmarkList.BookmarkListEvent
|
||||
@@ -470,7 +471,22 @@ class Account(
|
||||
// account never surfaces under another (the old cache was a process-wide singleton).
|
||||
val relayNotifications = NotifyRequestsCache()
|
||||
|
||||
override val nip47SignerState = NwcSignerState(signer, nwcFilterAssembler, cache, scope, settings)
|
||||
override val nip47SignerState =
|
||||
NwcSignerState(
|
||||
signer,
|
||||
nwcFilterAssembler,
|
||||
cache,
|
||||
scope,
|
||||
settings,
|
||||
fetchInfoEvent = { uri ->
|
||||
// Fetch the wallet's kind 13194 info event so NwcSignerState can prefer
|
||||
// NIP-44 when the wallet advertises it (NIP-47 encryption negotiation).
|
||||
client.fetchFirst(
|
||||
uri.relayUri,
|
||||
Filter(kinds = listOf(NwcInfoEvent.KIND), authors = listOf(uri.pubKeyHex), limit = 1),
|
||||
) as? NwcInfoEvent
|
||||
},
|
||||
)
|
||||
|
||||
val nip65RelayList = Nip65RelayListState(signer, cache, scope, settings)
|
||||
val localRelayList = LocalRelayListState(signer, cache, scope, settings)
|
||||
|
||||
+62
-2
@@ -37,18 +37,24 @@ import com.vitorpamplona.quartz.nip47WalletConnect.cache.NostrWalletConnectReque
|
||||
import com.vitorpamplona.quartz.nip47WalletConnect.cache.NostrWalletConnectResponseCache
|
||||
import com.vitorpamplona.quartz.nip47WalletConnect.events.LnZapPaymentRequestEvent
|
||||
import com.vitorpamplona.quartz.nip47WalletConnect.events.LnZapPaymentResponseEvent
|
||||
import com.vitorpamplona.quartz.nip47WalletConnect.events.NwcInfoEvent
|
||||
import com.vitorpamplona.quartz.nip47WalletConnect.rpc.Request
|
||||
import com.vitorpamplona.quartz.nip47WalletConnect.rpc.Response
|
||||
import kotlinx.coroutines.CancellationException
|
||||
import kotlinx.coroutines.CoroutineScope
|
||||
import kotlinx.coroutines.Dispatchers
|
||||
import kotlinx.coroutines.delay
|
||||
import kotlinx.coroutines.flow.SharingStarted
|
||||
import kotlinx.coroutines.flow.StateFlow
|
||||
import kotlinx.coroutines.flow.collectLatest
|
||||
import kotlinx.coroutines.flow.combine
|
||||
import kotlinx.coroutines.flow.distinctUntilChanged
|
||||
import kotlinx.coroutines.flow.filterNotNull
|
||||
import kotlinx.coroutines.flow.flowOn
|
||||
import kotlinx.coroutines.flow.map
|
||||
import kotlinx.coroutines.flow.stateIn
|
||||
import kotlinx.coroutines.launch
|
||||
import java.util.concurrent.ConcurrentHashMap
|
||||
|
||||
/**
|
||||
* Manages NIP-47 (Nostr Wallet Connect) related signing operations and decryption cache for a given account.
|
||||
@@ -60,6 +66,12 @@ class NwcSignerState(
|
||||
val cache: LocalCache,
|
||||
val scope: CoroutineScope,
|
||||
val settings: AccountSettings,
|
||||
/**
|
||||
* Fetches a wallet's kind 13194 info event so we can negotiate encryption.
|
||||
* Injected by [com.vitorpamplona.amethyst.model.Account] (which owns the relay
|
||||
* client). Null in tests / when unavailable — requests then fall back to NIP-04.
|
||||
*/
|
||||
val fetchInfoEvent: (suspend (Nip47WalletConnect.Nip47URINorm) -> NwcInfoEvent?)? = null,
|
||||
) : INwcSignerState {
|
||||
/**
|
||||
* Flow of the default wallet's NWC URI, derived from multi-wallet settings.
|
||||
@@ -105,6 +117,54 @@ class NwcSignerState(
|
||||
NostrSignerInternal(KeyPair(it))
|
||||
}
|
||||
|
||||
/**
|
||||
* Per-wallet (keyed by wallet service pubkey) cache of whether the wallet
|
||||
* advertises NIP-44 (`nip44_v2`) support in its kind 13194 info event.
|
||||
* NIP-47 says a client "should always prefer nip44 if supported by the wallet
|
||||
* service"; absent/unknown means we keep the NIP-04 legacy default.
|
||||
*/
|
||||
private val nip44SupportByWallet = ConcurrentHashMap<HexKey, Boolean>()
|
||||
|
||||
init {
|
||||
// Warm the encryption preference in the background whenever the default
|
||||
// wallet changes so the payment hot path can read it without blocking.
|
||||
scope.launch(Dispatchers.IO) {
|
||||
defaultWalletUri
|
||||
.filterNotNull()
|
||||
.distinctUntilChanged { a, b -> a.pubKeyHex == b.pubKeyHex && a.relayUri == b.relayUri }
|
||||
.collectLatest { warmEncryptionPreference(it) }
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Fetches the wallet's info event once and records whether it supports NIP-44.
|
||||
* Best-effort: any failure leaves the wallet on the NIP-04 fallback.
|
||||
*/
|
||||
private suspend fun warmEncryptionPreference(uri: Nip47WalletConnect.Nip47URINorm) {
|
||||
val fetch = fetchInfoEvent ?: return
|
||||
if (nip44SupportByWallet.containsKey(uri.pubKeyHex)) return
|
||||
|
||||
val supports =
|
||||
try {
|
||||
fetch(uri)?.encryptionSchemes()?.any { it.equals("nip44_v2", ignoreCase = true) } == true
|
||||
} catch (e: Exception) {
|
||||
if (e is CancellationException) throw e
|
||||
false
|
||||
}
|
||||
|
||||
nip44SupportByWallet[uri.pubKeyHex] = supports
|
||||
}
|
||||
|
||||
/**
|
||||
* Non-blocking read of the negotiated encryption preference for a wallet.
|
||||
* Returns true only once the info event has been fetched and advertised
|
||||
* `nip44_v2`; otherwise NIP-04 (the legacy default).
|
||||
*/
|
||||
private fun prefersNip44(uri: Nip47WalletConnect.Nip47URINorm?): Boolean {
|
||||
uri ?: return false
|
||||
return nip44SupportByWallet[uri.pubKeyHex] ?: false
|
||||
}
|
||||
|
||||
fun hasWalletConnectSetup(): Boolean = settings.nwcWallets.value.isNotEmpty()
|
||||
|
||||
override fun isNIP47Author(pubKey: HexKey?): Boolean = nip47Signer.value.pubKey == pubKey
|
||||
@@ -138,7 +198,7 @@ class NwcSignerState(
|
||||
val walletService = walletUri ?: throw IllegalArgumentException("No NIP47 setup")
|
||||
val walletSigner = buildSigner(walletService) ?: signer
|
||||
|
||||
val event = LnZapPaymentRequestEvent.createRequest(request, walletService.pubKeyHex, walletSigner)
|
||||
val event = LnZapPaymentRequestEvent.createRequest(request, walletService.pubKeyHex, walletSigner, useNip44 = prefersNip44(walletService))
|
||||
|
||||
val filter =
|
||||
NWCPaymentQueryState(
|
||||
@@ -184,7 +244,7 @@ class NwcSignerState(
|
||||
): Pair<LnZapPaymentRequestEvent, NormalizedRelayUrl> {
|
||||
val walletService = defaultWalletUri.value ?: throw IllegalArgumentException("No NIP47 setup")
|
||||
|
||||
val event = LnZapPaymentRequestEvent.create(bolt11, walletService.pubKeyHex, nip47Signer.value)
|
||||
val event = LnZapPaymentRequestEvent.create(bolt11, walletService.pubKeyHex, nip47Signer.value, useNip44 = prefersNip44(walletService))
|
||||
|
||||
val filter =
|
||||
NWCPaymentQueryState(
|
||||
|
||||
+13
@@ -226,6 +226,19 @@ enum class NotificationCategory(
|
||||
group = "com.vitorpamplona.amethyst.CHESS_NOTIFICATION",
|
||||
summaryId = 0x30000,
|
||||
),
|
||||
PAYMENT_RECEIVED(
|
||||
channelIdRes = R.string.app_notification_payments_channel_id,
|
||||
channelNameRes = R.string.app_notification_payments_channel_name,
|
||||
channelDescriptionRes = R.string.app_notification_payments_channel_description,
|
||||
summaryTextRes = R.string.app_notification_payments_summary,
|
||||
importance = NotificationManager.IMPORTANCE_DEFAULT,
|
||||
color = 0xFF16B979.toInt(), // lightning green — distinct from the gold zap channel
|
||||
smallIcon = R.drawable.ic_notif_zap,
|
||||
settingsIcon = MaterialSymbols.AccountBalanceWallet,
|
||||
channelGroup = NotifChannelGroup.PAYMENTS,
|
||||
group = "com.vitorpamplona.amethyst.PAYMENT_RECEIVED_NOTIFICATION",
|
||||
summaryId = 0xC0000,
|
||||
),
|
||||
;
|
||||
|
||||
fun channelId(context: Context): String = stringRes(context, channelIdRes)
|
||||
|
||||
+128
@@ -0,0 +1,128 @@
|
||||
/*
|
||||
* Copyright (c) 2025 Vitor Pamplona
|
||||
*
|
||||
* Permission is hereby granted, free of charge, to any person obtaining a copy of
|
||||
* this software and associated documentation files (the "Software"), to deal in
|
||||
* the Software without restriction, including without limitation the rights to use,
|
||||
* copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the
|
||||
* Software, and to permit persons to whom the Software is furnished to do so,
|
||||
* subject to the following conditions:
|
||||
*
|
||||
* The above copyright notice and this permission notice shall be included in all
|
||||
* copies or substantial portions of the Software.
|
||||
*
|
||||
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS
|
||||
* FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR
|
||||
* COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN
|
||||
* AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
|
||||
* WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
||||
*/
|
||||
package com.vitorpamplona.amethyst.service.notifications
|
||||
|
||||
import android.content.Context
|
||||
import com.vitorpamplona.amethyst.commons.model.nip47WalletConnect.NwcWalletEntryNorm
|
||||
import com.vitorpamplona.amethyst.model.Account
|
||||
import com.vitorpamplona.amethyst.service.notifications.renderers.NwcPaymentNotifier
|
||||
import com.vitorpamplona.quartz.nip01Core.core.HexKey
|
||||
import com.vitorpamplona.quartz.nip01Core.relay.client.INostrClient
|
||||
import com.vitorpamplona.quartz.nip01Core.relay.client.reqs.subscribeAsFlow
|
||||
import com.vitorpamplona.quartz.nip01Core.relay.filters.Filter
|
||||
import com.vitorpamplona.quartz.nip01Core.signers.NostrSigner
|
||||
import com.vitorpamplona.quartz.nip47WalletConnect.events.NwcNotificationEvent
|
||||
import com.vitorpamplona.quartz.nip47WalletConnect.rpc.PaymentReceivedNotification
|
||||
import com.vitorpamplona.quartz.utils.TimeUtils
|
||||
import kotlinx.coroutines.CancellationException
|
||||
import kotlinx.coroutines.CoroutineScope
|
||||
import kotlinx.coroutines.Dispatchers
|
||||
import kotlinx.coroutines.coroutineScope
|
||||
import kotlinx.coroutines.flow.Flow
|
||||
import kotlinx.coroutines.flow.collectLatest
|
||||
import kotlinx.coroutines.flow.distinctUntilChanged
|
||||
import kotlinx.coroutines.launch
|
||||
|
||||
/**
|
||||
* Always-on watcher that turns NIP-47 wallet notifications (kind 23197/23196) into
|
||||
* user-facing tray notifications for the logged-in account.
|
||||
*
|
||||
* For each configured NWC wallet it opens a standing subscription on that wallet's
|
||||
* own relay, filtered to notifications `p`-tagged to our client pubkey. Each event
|
||||
* is decrypted with the per-wallet connection secret and, when it is an incoming
|
||||
* `payment_received`, posted via [NwcPaymentNotifier] — **unless** the transaction
|
||||
* metadata carries a NIP-57 zap request, in which case it is dropped: zaps already
|
||||
* surface through the kind-9735 `ZapNotification` path, so notifying here would
|
||||
* double up.
|
||||
*
|
||||
* Only new notifications (`since` = watch start) are surfaced, so relaunching the
|
||||
* app never replays old payments as fresh alerts; a per-subscription id set
|
||||
* de-duplicates re-delivery across relay reconnects.
|
||||
*/
|
||||
class NwcPaymentNotificationWatcher(
|
||||
private val context: Context,
|
||||
private val client: INostrClient,
|
||||
private val scope: CoroutineScope,
|
||||
private val accountFlow: Flow<Account?>,
|
||||
) {
|
||||
fun start() {
|
||||
scope.launch(Dispatchers.IO) {
|
||||
accountFlow
|
||||
.distinctUntilChanged { a, b -> a?.signer?.pubKey == b?.signer?.pubKey }
|
||||
.collectLatest { account ->
|
||||
account ?: return@collectLatest
|
||||
account.settings.nwcWallets.collectLatest { wallets ->
|
||||
watchWallets(account, wallets)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private suspend fun watchWallets(
|
||||
account: Account,
|
||||
wallets: List<NwcWalletEntryNorm>,
|
||||
) = coroutineScope {
|
||||
wallets.forEach { wallet ->
|
||||
val signer = account.nip47SignerState.buildSigner(wallet.uri) ?: return@forEach
|
||||
|
||||
val filter =
|
||||
Filter(
|
||||
kinds = listOf(NwcNotificationEvent.KIND, NwcNotificationEvent.LEGACY_KIND),
|
||||
authors = listOf(wallet.uri.pubKeyHex),
|
||||
tags = mapOf("p" to listOf(signer.pubKey)),
|
||||
since = TimeUtils.now(),
|
||||
)
|
||||
|
||||
launch(Dispatchers.IO) {
|
||||
val seen = HashSet<HexKey>()
|
||||
client.subscribeAsFlow(wallet.uri.relayUri, filter).collect { events ->
|
||||
events.forEach { event ->
|
||||
val notification = event as? NwcNotificationEvent ?: return@forEach
|
||||
if (seen.add(notification.id)) {
|
||||
handle(account, notification, signer)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private suspend fun handle(
|
||||
account: Account,
|
||||
event: NwcNotificationEvent,
|
||||
signer: NostrSigner,
|
||||
) {
|
||||
val notification =
|
||||
try {
|
||||
event.decryptNotification(signer)
|
||||
} catch (e: Exception) {
|
||||
if (e is CancellationException) throw e
|
||||
return
|
||||
}
|
||||
|
||||
val tx = (notification as? PaymentReceivedNotification)?.notification ?: return
|
||||
|
||||
// A payment carrying a NIP-57 zap request is already shown by ZapNotification.
|
||||
if (tx.parsedMetadata()?.nostr != null) return
|
||||
|
||||
NwcPaymentNotifier.notify(context, account, tx)
|
||||
}
|
||||
}
|
||||
+78
@@ -0,0 +1,78 @@
|
||||
/*
|
||||
* Copyright (c) 2025 Vitor Pamplona
|
||||
*
|
||||
* Permission is hereby granted, free of charge, to any person obtaining a copy of
|
||||
* this software and associated documentation files (the "Software"), to deal in
|
||||
* the Software without restriction, including without limitation the rights to use,
|
||||
* copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the
|
||||
* Software, and to permit persons to whom the Software is furnished to do so,
|
||||
* subject to the following conditions:
|
||||
*
|
||||
* The above copyright notice and this permission notice shall be included in all
|
||||
* copies or substantial portions of the Software.
|
||||
*
|
||||
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS
|
||||
* FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR
|
||||
* COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN
|
||||
* AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
|
||||
* WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
||||
*/
|
||||
package com.vitorpamplona.amethyst.service.notifications.renderers
|
||||
|
||||
import android.content.Context
|
||||
import com.vitorpamplona.amethyst.R
|
||||
import com.vitorpamplona.amethyst.model.Account
|
||||
import com.vitorpamplona.amethyst.service.notifications.NotificationCategory
|
||||
import com.vitorpamplona.amethyst.service.notifications.NotificationRoutes
|
||||
import com.vitorpamplona.amethyst.service.notifications.NotificationUtils.postStandard
|
||||
import com.vitorpamplona.amethyst.service.notifications.notificationManager
|
||||
import com.vitorpamplona.amethyst.ui.note.showAmount
|
||||
import com.vitorpamplona.amethyst.ui.stringRes
|
||||
import com.vitorpamplona.quartz.nip47WalletConnect.rpc.NwcTransaction
|
||||
import com.vitorpamplona.quartz.utils.TimeUtils
|
||||
|
||||
/**
|
||||
* Posts a tray notification for an incoming Lightning payment reported by the
|
||||
* connected NWC wallet (NIP-47 `payment_received`). Renders on the green Payments
|
||||
* channel with a wallet icon; the title leads with the amount.
|
||||
*
|
||||
* Zaps are intentionally NOT routed here — a `payment_received` whose transaction
|
||||
* metadata carries a NIP-57 zap request is filtered out upstream by
|
||||
* [com.vitorpamplona.amethyst.service.notifications.NwcPaymentNotificationWatcher],
|
||||
* because those already surface through the kind-9735 [ZapNotification] path.
|
||||
*/
|
||||
object NwcPaymentNotifier {
|
||||
suspend fun notify(
|
||||
context: Context,
|
||||
account: Account,
|
||||
tx: NwcTransaction,
|
||||
) {
|
||||
val nm = context.notificationManager()
|
||||
if (!nm.areNotificationsEnabled()) return
|
||||
|
||||
val msats = tx.amount ?: return
|
||||
val amount = showAmount((msats / 1000L).toBigDecimal())
|
||||
|
||||
val id = tx.payment_hash ?: tx.invoice ?: tx.created_at?.toString() ?: return
|
||||
val time = tx.settled_at ?: tx.created_at ?: TimeUtils.now()
|
||||
|
||||
val title = stringRes(context, R.string.app_notification_payments_channel_message, amount)
|
||||
val comment = (tx.parsedMetadata()?.comment ?: tx.description)?.ifBlank { null }
|
||||
val body = comment ?: title
|
||||
|
||||
val accountNpub = NotificationRoutes.accountNpub(account)
|
||||
val uri = NotificationRoutes.notificationsUri(accountNpub, id)
|
||||
|
||||
nm.postStandard(
|
||||
category = NotificationCategory.PAYMENT_RECEIVED,
|
||||
id = id,
|
||||
messageTitle = title,
|
||||
messageBody = body,
|
||||
time = time,
|
||||
pictureUrl = null,
|
||||
uri = uri,
|
||||
applicationContext = context,
|
||||
)
|
||||
}
|
||||
}
|
||||
+6
-1
@@ -65,6 +65,7 @@ import com.vitorpamplona.amethyst.ui.stringRes
|
||||
import com.vitorpamplona.amethyst.ui.theme.ButtonBorder
|
||||
import com.vitorpamplona.amethyst.ui.theme.DoubleHorzSpacer
|
||||
import com.vitorpamplona.amethyst.ui.theme.Size24Modifier
|
||||
import com.vitorpamplona.quartz.nip47WalletConnect.Nip47DeepLink
|
||||
import com.vitorpamplona.quartz.nip47WalletConnect.Nip47WalletConnect
|
||||
import kotlinx.coroutines.CancellationException
|
||||
import kotlinx.coroutines.launch
|
||||
@@ -137,7 +138,11 @@ fun AddNwcWalletScreen(
|
||||
onClick = {
|
||||
try {
|
||||
uri.openUri(
|
||||
"nostrnwc://connect?appname=Amethyst&appicon=https%3A%2F%2Fraw.githubusercontent.com%2Fvitorpamplona%2Famethyst%2Frefs%2Fheads%2Fmain%2Ficon.png&callback=amethyst%2Bwalletconnect%3A%2F%2Fdlnwc",
|
||||
Nip47DeepLink.buildConnectUri(
|
||||
callback = "amethyst+walletconnect://dlnwc",
|
||||
appName = "Amethyst",
|
||||
appIcon = "https://raw.githubusercontent.com/vitorpamplona/amethyst/refs/heads/main/icon.png",
|
||||
),
|
||||
)
|
||||
} catch (_: IllegalArgumentException) {
|
||||
accountViewModel.toastManager.toast(
|
||||
|
||||
@@ -1858,6 +1858,12 @@
|
||||
<string name="app_notification_zaps_channel_message_from">From %1$s</string>
|
||||
<string name="app_notification_zaps_channel_message_for">for %1$s</string>
|
||||
|
||||
<string name="app_notification_payments_channel_id" translatable="false">PaymentsReceivedID</string>
|
||||
<string name="app_notification_payments_channel_name">Payments Received</string>
|
||||
<string name="app_notification_payments_channel_description">Notifies you when your connected wallet receives a payment that is not a Nostr zap</string>
|
||||
<string name="app_notification_payments_channel_message">Received %1$s sats</string>
|
||||
<string name="app_notification_payments_summary">New payments</string>
|
||||
|
||||
<string name="app_notification_reply_label">Reply</string>
|
||||
<string name="app_notification_mark_read_label">Mark Read</string>
|
||||
<string name="app_notification_me">Me</string>
|
||||
|
||||
Reference in New Issue
Block a user