Allow retries of pessimistic actions

This commit is contained in:
ekzyis 2024-11-26 10:05:00 +01:00
parent 1f2b717da9
commit be4ce5daf9
4 changed files with 14 additions and 21 deletions

View File

@ -304,14 +304,6 @@ export async function retryPaidAction (actionType, args, incomingContext) {
throw new Error(`retryPaidAction - must be logged in ${actionType}`)
}
if (!action.paymentMethods.includes(PAID_ACTION_PAYMENT_METHODS.OPTIMISTIC)) {
throw new Error(`retryPaidAction - action does not support optimism ${actionType}`)
}
if (!action.retry) {
throw new Error(`retryPaidAction - action does not support retrying ${actionType}`)
}
if (!failedInvoice) {
throw new Error(`retryPaidAction - missing invoice ${actionType}`)
}
@ -319,7 +311,7 @@ export async function retryPaidAction (actionType, args, incomingContext) {
const { msatsRequested, actionId, actionArgs } = failedInvoice
const retryContext = {
...incomingContext,
optimistic: true,
optimistic: failedInvoice.actionOptimistic,
me: await models.user.findUnique({ where: { id: me.id } }),
cost: BigInt(msatsRequested),
actionId
@ -345,7 +337,7 @@ export async function retryPaidAction (actionType, args, incomingContext) {
const invoice = await createDbInvoice(actionType, actionArgs, context)
return {
result: await action.retry({ invoiceId: failedInvoice.id, newInvoiceId: invoice.id }, context),
result: await action.retry?.({ invoiceId: failedInvoice.id, newInvoiceId: invoice.id }, context),
invoice,
paymentMethod: 'OPTIMISTIC'
}

View File

@ -86,7 +86,7 @@ export const useQrPayment = () => {
await invoice.cancel(inv).catch(console.error)
reject(new InvoiceCanceledError(inv?.hash))
}
resolve()
resolve(inv)
}
showModal(onClose =>
<Invoice
@ -99,7 +99,7 @@ export const useQrPayment = () => {
waitFor={waitFor}
onExpired={inv => reject(new InvoiceExpiredError(inv?.hash))}
onCanceled={inv => { onClose(); reject(new InvoiceCanceledError(inv?.hash, inv?.actionError)) }}
onPayment={() => { paid = true; onClose(); resolve() }}
onPayment={() => { paid = true; onClose(); resolve(inv) }}
poll
/>,
{ keepOpen, persistOnNavigate, onClose: cancelAndReject })

View File

@ -83,7 +83,7 @@ export function usePaidMutation (mutation,
throw new Error('usePaidMutation: exactly one mutation at a time is supported')
}
const response = Object.values(data)[0]
const invoice = response?.invoice
let invoice = response?.invoice
// if the mutation returns an invoice, pay it
if (invoice) {
@ -113,7 +113,8 @@ export function usePaidMutation (mutation,
// the action is pessimistic
try {
// wait for the invoice to be paid
await waitForPayment(invoice, { alwaysShowQROnFailure: true, persistOnNavigate, waitFor })
// returns the invoice that was paid since it might have been updated via retries
invoice = await waitForPayment(invoice, { alwaysShowQROnFailure: true, persistOnNavigate, waitFor })
if (!response.result) {
// if the mutation didn't return any data, ie pessimistic, we need to fetch it
const { data: { paidAction } } = await getPaidAction({ variables: { invoiceId: parseInt(invoice.id) } })

View File

@ -47,7 +47,7 @@ export function useWalletPayment () {
let walletInvoice = invoice
for (const wallet of walletsWithPayments) {
const controller = invoiceController(walletInvoice.id, invoiceHelper.isInvoice)
const controller = invoiceController(walletInvoice, invoiceHelper.isInvoice)
try {
return await new Promise((resolve, reject) => {
// can't await wallet payments since we might pay hold invoices and thus payments might not settle immediately.
@ -102,20 +102,20 @@ export function useWalletPayment () {
return waitForPayment
}
const invoiceController = (id, isInvoice) => {
const invoiceController = (inv, isInvoice) => {
const controller = new AbortController()
const signal = controller.signal
controller.wait = async (waitFor = inv => inv?.actionState === 'PAID') => {
return await new Promise((resolve, reject) => {
const interval = setInterval(async () => {
try {
const paid = await isInvoice({ id }, waitFor)
const paid = await isInvoice(inv, waitFor)
if (paid) {
resolve()
resolve(inv)
clearInterval(interval)
signal.removeEventListener('abort', abort)
} else {
console.info(`invoice #${id}: waiting for payment ...`)
console.info(`invoice #${inv.id}: waiting for payment ...`)
}
} catch (err) {
reject(err)
@ -125,8 +125,8 @@ const invoiceController = (id, isInvoice) => {
}, FAST_POLL_INTERVAL)
const abort = () => {
console.info(`invoice #${id}: stopped waiting`)
resolve()
console.info(`invoice #${inv.id}: stopped waiting`)
resolve(inv)
clearInterval(interval)
signal.removeEventListener('abort', abort)
}