Compare commits

...

6 Commits

Author SHA1 Message Date
ekzyis
d6916fa3f4
Merge pull request #1557 from stackernews/fix-invoice-waiting
fix invoice waiting
2024-11-08 05:25:05 +01:00
k00b
dcab8e1365 fix invoice waiting 2024-11-07 19:55:34 -06:00
Keyan
e9a5925c50
Merge pull request #1556 from stackernews/fix-1544
fix invoice status display
2024-11-07 19:25:06 -06:00
k00b
544a54399c fix invoice status display 2024-11-07 19:17:50 -06:00
Keyan
0891e51c9e
fix passphrase scanning (#1553) 2024-11-07 16:24:41 -06:00
Keyan
a67ef43f6e
listen for local storage wallet changes (#1552) 2024-11-07 13:15:39 -06:00
5 changed files with 89 additions and 73 deletions

View File

@ -1126,7 +1126,7 @@ function QrPassword ({ value }) {
)
}
function PasswordScanner ({ onScan }) {
function PasswordScanner ({ onScan, text }) {
const showModal = useShowModal()
const toaster = useToast()
@ -1136,6 +1136,8 @@ function PasswordScanner ({ onScan }) {
onClick={() => {
showModal(onClose => {
return (
<div>
{text && <h5 className='line-height-md mb-4 text-center'>{text}</h5>}
<Scanner
formats={['qr_code']}
onScan={([{ rawValue: result }]) => {
@ -1156,6 +1158,7 @@ function PasswordScanner ({ onScan }) {
onClose()
}}
/>
</div>
)
})
}}
@ -1167,9 +1170,10 @@ function PasswordScanner ({ onScan }) {
)
}
export function PasswordInput ({ newPass, qr, copy, readOnly, append, value, ...props }) {
export function PasswordInput ({ newPass, qr, copy, readOnly, append, value: initialValue, ...props }) {
const [showPass, setShowPass] = useState(false)
const [field, helpers] = props.noForm ? [{ value }, {}, {}] : useField(props)
const [value, setValue] = useState(initialValue)
const [field,, helpers] = props.noForm ? [{ value }, {}, { setValue }] : useField(props)
const Append = useMemo(() => {
return (
@ -1181,12 +1185,13 @@ export function PasswordInput ({ newPass, qr, copy, readOnly, append, value, ...
{qr && (readOnly
? <QrPassword value={field?.value} />
: <PasswordScanner
text="Where'd you learn to square dance?"
onScan={v => helpers.setValue(v)}
/>)}
{append}
</>
)
}, [showPass, copy, field?.value, qr, readOnly, append])
}, [showPass, copy, field?.value, helpers.setValue, qr, readOnly, append])
const style = props.style ? { ...props.style } : {}
if (props.as === 'textarea') {

View File

@ -55,11 +55,15 @@ export default function Invoice ({
let variant = 'default'
let status = 'waiting for you'
if (invoice.cancelled) {
if (invoice.confirmedAt) {
variant = 'confirmed'
status = `${numWithUnits(invoice.satsReceived, { abbreviate: false })} ${successVerb}`
useWallet = false
} else if (invoice.cancelled) {
variant = 'failed'
status = 'cancelled'
useWallet = false
} else if (invoice.isHeld && invoice.satsReceived && !expired) {
} else if (invoice.isHeld) {
variant = 'pending'
status = (
<div className='d-flex justify-content-center'>
@ -67,15 +71,11 @@ export default function Invoice ({
</div>
)
useWallet = false
} else if (invoice.confirmedAt) {
variant = 'confirmed'
status = `${numWithUnits(invoice.satsReceived, { abbreviate: false })} ${successVerb}`
useWallet = false
} else if (expired) {
variant = 'failed'
status = 'expired'
useWallet = false
} else if (invoice.expiresAt) {
} else {
variant = 'pending'
status = (
<CompactLongCountdown

View File

@ -1,4 +1,4 @@
import { useCallback, useMemo } from 'react'
import { useCallback } from 'react'
import { useMe } from './me'
import { gql, useApolloClient, useMutation } from '@apollo/client'
import { useWallet } from '@/wallets/index'
@ -60,10 +60,23 @@ export const useInvoice = () => {
return that(data.invoice)
}, [client])
const waitController = useMemo(() => {
const cancel = useCallback(async ({ hash, hmac }) => {
if (!hash || !hmac) {
throw new Error('missing hash or hmac')
}
console.log('canceling invoice:', hash)
const inv = await cancelInvoice({ variables: { hash, hmac } })
return inv
}, [cancelInvoice])
return { create, cancel, isInvoice }
}
const invoiceController = (id, isInvoice) => {
const controller = new AbortController()
const signal = controller.signal
controller.wait = async ({ id }, waitFor = inv => inv?.actionState === 'PAID') => {
controller.wait = async (waitFor = inv => inv?.actionState === 'PAID') => {
return await new Promise((resolve, reject) => {
const interval = setInterval(async () => {
try {
@ -95,19 +108,6 @@ export const useInvoice = () => {
controller.stop = () => controller.abort()
return controller
}, [isInvoice])
const cancel = useCallback(async ({ hash, hmac }) => {
if (!hash || !hmac) {
throw new Error('missing hash or hmac')
}
console.log('canceling invoice:', hash)
const inv = await cancelInvoice({ variables: { hash, hmac } })
return inv
}, [cancelInvoice])
return { create, waitUntilPaid: waitController.wait, stopWaiting: waitController.stop, cancel }
}
export const useWalletPayment = () => {
@ -118,12 +118,13 @@ export const useWalletPayment = () => {
if (!wallet) {
throw new NoAttachedWalletError()
}
const controller = invoiceController(id, invoice.isInvoice)
try {
return await new Promise((resolve, reject) => {
// can't use await here since we might pay JIT invoices and sendPaymentAsync is not supported yet.
// see https://www.webln.guide/building-lightning-apps/webln-reference/webln.sendpaymentasync
wallet.sendPayment(bolt11).catch(reject)
invoice.waitUntilPaid({ id }, waitFor)
controller.wait(waitFor)
.then(resolve)
.catch(reject)
})
@ -131,7 +132,7 @@ export const useWalletPayment = () => {
console.error('payment failed:', err)
throw err
} finally {
invoice.stopWaiting()
controller.stop()
}
}, [wallet, invoice])

View File

@ -522,7 +522,7 @@ export const deviceSyncSchema = object().shape({
try {
await string().oneOf(bip39Words).validate(w)
} catch {
return context.createError({ message: `'${w}' is not a valid pairing phrase word` })
return context.createError({ message: `'${w.slice(0, 10)}${w.length > 10 ? '...' : ''}' is not a valid pairing phrase word` })
}
}

View File

@ -41,7 +41,17 @@ function useLocalWallets () {
}, [wallets, setWallets, me?.id])
useEffect(() => {
// listen for changes to any wallet config in local storage
// from any window with the same origin
const handleStorage = (event) => {
if (event.key.startsWith(getStorageKey(''))) {
loadWallets()
}
}
window.addEventListener('storage', handleStorage)
loadWallets()
return () => window.removeEventListener('storage', handleStorage)
}, [loadWallets])
return { wallets, reloadLocalWallets: loadWallets, removeLocalWallets: removeWallets }