Fix QR code interaction with WebLN provider (#834)

* Fix passing of bolt11 for QR payments

* Fix missing provider check

* Only cancel invoice if hash and hmac were given

* Fix duplicate toast on error

* Fix relay might not be set yet when sendPayment is called
This commit is contained in:
ekzyis 2024-02-15 18:20:15 +01:00 committed by GitHub
parent 8727f95fd9
commit afe096e516
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
3 changed files with 25 additions and 28 deletions

View File

@ -3,27 +3,24 @@ import { CopyInput, InputSkeleton } from './form'
import InvoiceStatus from './invoice-status' import InvoiceStatus from './invoice-status'
import { useEffect } from 'react' import { useEffect } from 'react'
import { useWebLN } from './webln' import { useWebLN } from './webln'
import { useToast } from './toast'
export default function Qr ({ asIs, value, webLn, statusVariant, description, status }) { export default function Qr ({ asIs, value, webLn, statusVariant, description, status }) {
const qrValue = asIs ? value : 'lightning:' + value.toUpperCase() const qrValue = asIs ? value : 'lightning:' + value.toUpperCase()
const provider = useWebLN() const provider = useWebLN()
const toaster = useToast()
useEffect(() => { useEffect(() => {
async function effect () { async function effect () {
if (webLn) { if (webLn && provider?.enabled) {
try { try {
await provider.sendPayment(value) await provider.sendPayment({ bolt11: value })
} catch (e) { } catch (e) {
console.log(e?.message) console.log(e?.message)
toaster.danger(`${provider.name}: ${e?.message}`)
} }
} }
} }
effect() effect()
}, []) }, [provider])
return ( return (
<> <>

View File

@ -87,7 +87,8 @@ function RawWebLNProvider ({ children }) {
autohide: false, autohide: false,
onCancel: async () => { onCancel: async () => {
try { try {
await cancelInvoice({ variables: { hash, hmac } }) // hash and hmac are only passed for HODL invoices
if (hash && hmac) await cancelInvoice({ variables: { hash, hmac } })
canceled = true canceled = true
toaster.warning('payment canceled') toaster.warning('payment canceled')
removeToast = undefined removeToast = undefined

View File

@ -1,6 +1,6 @@
// https://github.com/getAlby/js-sdk/blob/master/src/webln/NostrWeblnProvider.ts // https://github.com/getAlby/js-sdk/blob/master/src/webln/NostrWeblnProvider.ts
import { createContext, useCallback, useContext, useEffect, useState } from 'react' import { createContext, useCallback, useContext, useEffect, useRef, useState } from 'react'
import { Relay, finalizeEvent, nip04 } from 'nostr-tools' import { Relay, finalizeEvent, nip04 } from 'nostr-tools'
import { parseNwcUrl } from '../../lib/url' import { parseNwcUrl } from '../../lib/url'
@ -13,11 +13,21 @@ export function NWCProvider ({ children }) {
const [secret, setSecret] = useState() const [secret, setSecret] = useState()
const [enabled, setEnabled] = useState() const [enabled, setEnabled] = useState()
const [initialized, setInitialized] = useState(false) const [initialized, setInitialized] = useState(false)
const [relay, setRelay] = useState()
const relayRef = useRef()
const name = 'NWC' const name = 'NWC'
const storageKey = 'webln:provider:nwc' const storageKey = 'webln:provider:nwc'
const updateRelay = async (relayUrl) => {
try {
relayRef.current?.close()
if (relayUrl) relayRef.current = await Relay.connect(relayUrl)
} catch (err) {
console.error(err)
}
}
const loadConfig = useCallback(async () => { const loadConfig = useCallback(async () => {
const configStr = window.localStorage.getItem(storageKey) const configStr = window.localStorage.getItem(storageKey)
if (!configStr) { if (!configStr) {
@ -39,6 +49,7 @@ export function NWCProvider ({ children }) {
try { try {
const supported = await validateParams(params) const supported = await validateParams(params)
setEnabled(supported.includes('pay_invoice')) setEnabled(supported.includes('pay_invoice'))
await updateRelay(params.relayUrl)
} catch (err) { } catch (err) {
console.error('invalid NWC config:', err) console.error('invalid NWC config:', err)
setEnabled(false) setEnabled(false)
@ -69,6 +80,7 @@ export function NWCProvider ({ children }) {
try { try {
const supported = await validateParams(params) const supported = await validateParams(params)
setEnabled(supported.includes('pay_invoice')) setEnabled(supported.includes('pay_invoice'))
await updateRelay(params.relayUrl)
} catch (err) { } catch (err) {
console.error('invalid NWC config:', err) console.error('invalid NWC config:', err)
setEnabled(false) setEnabled(false)
@ -85,25 +97,12 @@ export function NWCProvider ({ children }) {
setEnabled(undefined) setEnabled(undefined)
}, []) }, [])
useEffect(() => {
let relay
(async function () {
if (relayUrl) {
relay = await Relay.connect(relayUrl)
setRelay(relay)
}
})().catch((err) => {
console.error(err)
setRelay(null)
})
return () => {
relay?.close()
setRelay(null)
}
}, [relayUrl])
const sendPayment = useCallback((bolt11) => { const sendPayment = useCallback((bolt11) => {
return new Promise(function (resolve, reject) { return new Promise(function (resolve, reject) {
const relay = relayRef.current
if (!relay) {
return reject(new Error('not connected to relay'))
}
(async function () { (async function () {
// XXX set this to mock NWC relays // XXX set this to mock NWC relays
const MOCK_NWC_RELAY = false const MOCK_NWC_RELAY = false
@ -168,9 +167,9 @@ export function NWCProvider ({ children }) {
}) })
})().catch(reject) })().catch(reject)
}) })
}, [relay, walletPubkey, secret]) }, [walletPubkey, secret])
const getInfo = useCallback(() => getInfoWithRelay(relay, walletPubkey), [relay, walletPubkey]) const getInfo = useCallback(() => getInfoWithRelay(relayRef?.current, walletPubkey), [relayRef?.current, walletPubkey])
useEffect(() => { useEffect(() => {
loadConfig().catch(console.error) loadConfig().catch(console.error)