UX latency enhancements for paid actions (#1434)
* prevent multiple retries & pulse retry button * fix lint * don't wait for settlement on pessimistic zaps * optimistic act modal
This commit is contained in:
parent
450c969dfc
commit
9f06fd65ee
|
@ -160,7 +160,7 @@ async function performPessimisticAction (actionType, args, context) {
|
|||
|
||||
export async function retryPaidAction (actionType, args, context) {
|
||||
const { models, me } = context
|
||||
const { invoiceId } = args
|
||||
const { invoice: failedInvoice } = args
|
||||
|
||||
console.log('retryPaidAction', actionType, args)
|
||||
|
||||
|
@ -181,18 +181,13 @@ export async function retryPaidAction (actionType, args, context) {
|
|||
throw new Error(`retryPaidAction - action does not support retrying ${actionType}`)
|
||||
}
|
||||
|
||||
if (!invoiceId) {
|
||||
throw new Error(`retryPaidAction - missing invoiceId ${actionType}`)
|
||||
if (!failedInvoice) {
|
||||
throw new Error(`retryPaidAction - missing invoice ${actionType}`)
|
||||
}
|
||||
|
||||
context.optimistic = true
|
||||
context.me = await models.user.findUnique({ where: { id: me.id } })
|
||||
|
||||
const failedInvoice = await models.invoice.findUnique({ where: { id: invoiceId, actionState: 'FAILED' } })
|
||||
if (!failedInvoice) {
|
||||
throw new Error(`retryPaidAction ${actionType} - invoice ${invoiceId} not found or not in failed state`)
|
||||
}
|
||||
|
||||
const { msatsRequested, actionId } = failedInvoice
|
||||
context.cost = BigInt(msatsRequested)
|
||||
context.actionId = actionId
|
||||
|
@ -204,7 +199,7 @@ export async function retryPaidAction (actionType, args, context) {
|
|||
// update the old invoice to RETRYING, so that it's not confused with FAILED
|
||||
await tx.invoice.update({
|
||||
where: {
|
||||
id: invoiceId,
|
||||
id: failedInvoice.id,
|
||||
actionState: 'FAILED'
|
||||
},
|
||||
data: {
|
||||
|
@ -216,7 +211,7 @@ export async function retryPaidAction (actionType, args, context) {
|
|||
const invoice = await createDbInvoice(actionType, args, context, invoiceArgs)
|
||||
|
||||
return {
|
||||
result: await action.retry({ invoiceId, newInvoiceId: invoice.id }, context),
|
||||
result: await action.retry({ invoiceId: failedInvoice.id, newInvoiceId: invoice.id }, context),
|
||||
invoice,
|
||||
paymentMethod: 'OPTIMISTIC'
|
||||
}
|
||||
|
|
|
@ -56,7 +56,14 @@ export default {
|
|||
throw new Error('Invoice not found')
|
||||
}
|
||||
|
||||
const result = await retryPaidAction(invoice.actionType, { invoiceId }, { models, me, lnd })
|
||||
if (invoice.actionState !== 'FAILED') {
|
||||
if (invoice.actionState === 'PAID') {
|
||||
throw new Error('Invoice is already paid')
|
||||
}
|
||||
throw new Error(`Invoice is not in failed state: ${invoice.actionState}`)
|
||||
}
|
||||
|
||||
const result = await retryPaidAction(invoice.actionType, { invoice }, { models, me, lnd })
|
||||
|
||||
return {
|
||||
...result,
|
||||
|
|
|
@ -94,7 +94,7 @@ export function CommentFlat ({ item, rank, siblingComments, ...props }) {
|
|||
|
||||
export default function Comment ({
|
||||
item, children, replyOpen, includeParent, topLevel,
|
||||
rootText, noComments, noReply, truncate, depth, pin
|
||||
rootText, noComments, noReply, truncate, depth, pin, setDisableRetry, disableRetry
|
||||
}) {
|
||||
const [edit, setEdit] = useState()
|
||||
const { me } = useMe()
|
||||
|
@ -169,6 +169,8 @@ export default function Comment ({
|
|||
embellishUser={op && <><span> </span><Badge bg={op === 'fwd' ? 'secondary' : 'boost'} className={`${styles.op} bg-opacity-75`}>{op}</Badge></>}
|
||||
onQuoteReply={quoteReply}
|
||||
nested={!includeParent}
|
||||
setDisableRetry={setDisableRetry}
|
||||
disableRetry={disableRetry}
|
||||
extraInfo={
|
||||
<>
|
||||
{includeParent && <Parent item={item} rootText={rootText} />}
|
||||
|
|
|
@ -53,7 +53,7 @@ export function SubmitButton ({
|
|||
return (
|
||||
<Button
|
||||
variant={variant || 'main'}
|
||||
className={classNames(formik.isSubmitting && styles.pending, className)}
|
||||
className={classNames(formik.isSubmitting && 'pulse', className)}
|
||||
type='submit'
|
||||
disabled={disabled}
|
||||
onClick={value
|
||||
|
|
|
@ -13,8 +13,12 @@ import ItemJob from './item-job'
|
|||
import Item from './item'
|
||||
import { CommentFlat } from './comment'
|
||||
import classNames from 'classnames'
|
||||
import Moon from '@/svgs/moon-fill.svg'
|
||||
|
||||
export default function Invoice ({ id, query = INVOICE, modal, onPayment, onCanceled, info, successVerb, useWallet = true, walletError, poll, waitFor, ...props }) {
|
||||
export default function Invoice ({
|
||||
id, query = INVOICE, modal, onPayment, onCanceled, info, successVerb = 'deposited',
|
||||
heldVerb = 'settling', useWallet = true, walletError, poll, waitFor, ...props
|
||||
}) {
|
||||
const [expired, setExpired] = useState(false)
|
||||
const { data, error } = useQuery(query, SSR
|
||||
? {}
|
||||
|
@ -55,9 +59,17 @@ export default function Invoice ({ id, query = INVOICE, modal, onPayment, onCanc
|
|||
variant = 'failed'
|
||||
status = 'cancelled'
|
||||
useWallet = false
|
||||
} else if (invoice.confirmedAt || (invoice.isHeld && invoice.satsReceived && !expired)) {
|
||||
} else if (invoice.isHeld && invoice.satsReceived && !expired) {
|
||||
variant = 'pending'
|
||||
status = (
|
||||
<div className='d-flex justify-content-center'>
|
||||
<Moon className='spin fill-grey me-2' /> {heldVerb}
|
||||
</div>
|
||||
)
|
||||
useWallet = false
|
||||
} else if (invoice.confirmedAt) {
|
||||
variant = 'confirmed'
|
||||
status = `${numWithUnits(invoice.satsReceived, { abbreviate: false })} ${successVerb || 'deposited'}`
|
||||
status = `${numWithUnits(invoice.satsReceived, { abbreviate: false })} ${successVerb}`
|
||||
useWallet = false
|
||||
} else if (expired) {
|
||||
variant = 'failed'
|
||||
|
|
|
@ -90,6 +90,7 @@ function BoostForm ({ step, onSubmit, children, item, oValue, inputRef, act = 'B
|
|||
export default function ItemAct ({ onClose, item, act = 'TIP', step, children, abortSignal }) {
|
||||
const inputRef = useRef(null)
|
||||
const { me } = useMe()
|
||||
const wallet = useWallet()
|
||||
const [oValue, setOValue] = useState()
|
||||
|
||||
useEffect(() => {
|
||||
|
@ -110,6 +111,18 @@ export default function ItemAct ({ onClose, item, act = 'TIP', step, children, a
|
|||
}
|
||||
}
|
||||
}
|
||||
|
||||
const onPaid = () => {
|
||||
strike()
|
||||
onClose?.()
|
||||
if (!me) setItemMeAnonSats({ id: item.id, amount })
|
||||
}
|
||||
|
||||
const closeImmediately = !!wallet || me?.privates?.sats > Number(amount)
|
||||
if (closeImmediately) {
|
||||
onPaid()
|
||||
}
|
||||
|
||||
const { error } = await actor({
|
||||
variables: {
|
||||
id: item.id,
|
||||
|
@ -127,15 +140,11 @@ export default function ItemAct ({ onClose, item, act = 'TIP', step, children, a
|
|||
}
|
||||
: undefined,
|
||||
// don't close modal immediately because we want the QR modal to stack
|
||||
onCompleted: () => {
|
||||
strike()
|
||||
onClose?.()
|
||||
if (!me) setItemMeAnonSats({ id: item.id, amount })
|
||||
}
|
||||
onPaid: closeImmediately ? undefined : onPaid
|
||||
})
|
||||
if (error) throw error
|
||||
addCustomTip(Number(amount))
|
||||
}, [me, actor, act, item.id, onClose, abortSignal, strike])
|
||||
}, [me, actor, !!wallet, act, item.id, onClose, abortSignal, strike])
|
||||
|
||||
return act === 'BOOST'
|
||||
? <BoostForm step={step} onSubmit={onSubmit} item={item} oValue={oValue} inputRef={inputRef} act={act}>{children}</BoostForm>
|
||||
|
@ -226,6 +235,7 @@ export function useAct ({ query = ACT_MUTATION, ...options } = {}) {
|
|||
const getPaidActionResult = data => Object.values(data)[0]
|
||||
|
||||
const [act] = usePaidMutation(query, {
|
||||
waitFor: inv => inv?.satsReceived > 0,
|
||||
...options,
|
||||
update: (cache, { data }) => {
|
||||
const response = getPaidActionResult(data)
|
||||
|
|
|
@ -26,20 +26,20 @@ import { useQrPayment } from './payment'
|
|||
import { useRetryCreateItem } from './use-item-submit'
|
||||
import { useToast } from './toast'
|
||||
import { useShowModal } from './modal'
|
||||
import classNames from 'classnames'
|
||||
|
||||
export default function ItemInfo ({
|
||||
item, full, commentsText = 'comments',
|
||||
commentTextSingular = 'comment', className, embellishUser, extraInfo, onEdit, editText,
|
||||
onQuoteReply, extraBadges, nested, pinnable, showActionDropdown = true, showUser = true
|
||||
onQuoteReply, extraBadges, nested, pinnable, showActionDropdown = true, showUser = true,
|
||||
setDisableRetry, disableRetry
|
||||
}) {
|
||||
const editThreshold = new Date(item.invoice?.confirmedAt ?? item.createdAt).getTime() + 10 * 60000
|
||||
const { me } = useMe()
|
||||
const toaster = useToast()
|
||||
const router = useRouter()
|
||||
const [canEdit, setCanEdit] = useState(item.mine && (Date.now() < editThreshold))
|
||||
const [hasNewComments, setHasNewComments] = useState(false)
|
||||
const root = useRoot()
|
||||
const retryCreateItem = useRetryCreateItem({ id: item.id })
|
||||
const sub = item?.sub || root?.sub
|
||||
|
||||
useEffect(() => {
|
||||
|
@ -64,72 +64,6 @@ export default function ItemInfo ({
|
|||
const canPin = (isPost && mySub) || (myPost && rootReply)
|
||||
const meSats = (me ? item.meSats : item.meAnonSats) || 0
|
||||
|
||||
const EditInfo = () => {
|
||||
if (canEdit) {
|
||||
return (
|
||||
<>
|
||||
<span> \ </span>
|
||||
<span
|
||||
className='text-reset pointer fw-bold'
|
||||
onClick={() => onEdit ? onEdit() : router.push(`/items/${item.id}/edit`)}
|
||||
>
|
||||
<span>{editText || 'edit'} </span>
|
||||
{(!item.invoice?.actionState || item.invoice?.actionState === 'PAID') &&
|
||||
<Countdown
|
||||
date={editThreshold}
|
||||
onComplete={() => { setCanEdit(false) }}
|
||||
/>}
|
||||
</span>
|
||||
</>
|
||||
)
|
||||
}
|
||||
|
||||
return null
|
||||
}
|
||||
|
||||
const PaymentInfo = () => {
|
||||
const waitForQrPayment = useQrPayment()
|
||||
if (item.deletedAt) return null
|
||||
|
||||
let Component
|
||||
let onClick
|
||||
if (me && item.invoice?.actionState && item.invoice?.actionState !== 'PAID') {
|
||||
if (item.invoice?.actionState === 'FAILED') {
|
||||
Component = () => <span className='text-warning'>retry payment</span>
|
||||
onClick = async () => {
|
||||
try {
|
||||
const { error } = await retryCreateItem({ variables: { invoiceId: parseInt(item.invoice?.id) } })
|
||||
if (error) throw error
|
||||
} catch (error) {
|
||||
toaster.danger(error.message)
|
||||
}
|
||||
}
|
||||
} else {
|
||||
Component = () => (
|
||||
<span
|
||||
className='text-info'
|
||||
>pending
|
||||
</span>
|
||||
)
|
||||
onClick = () => waitForQrPayment({ id: item.invoice?.id }, null, { cancelOnClose: false }).catch(console.error)
|
||||
}
|
||||
} else {
|
||||
return null
|
||||
}
|
||||
|
||||
return (
|
||||
<>
|
||||
<span> \ </span>
|
||||
<span
|
||||
className='text-reset pointer fw-bold'
|
||||
onClick={onClick}
|
||||
>
|
||||
<Component />
|
||||
</span>
|
||||
</>
|
||||
)
|
||||
}
|
||||
|
||||
return (
|
||||
<div className={className || `${styles.other}`}>
|
||||
{!(item.position && (pinnable || !item.subName)) && !(!item.parentId && Number(item.user?.id) === USER_ID.ad) &&
|
||||
|
@ -217,8 +151,11 @@ export default function ItemInfo ({
|
|||
{
|
||||
showActionDropdown &&
|
||||
<>
|
||||
<EditInfo />
|
||||
<PaymentInfo />
|
||||
<EditInfo
|
||||
item={item} canEdit={canEdit}
|
||||
setCanEdit={setCanEdit} onEdit={onEdit} editText={editText} editThreshold={editThreshold}
|
||||
/>
|
||||
<PaymentInfo item={item} disableRetry={disableRetry} setDisableRetry={setDisableRetry} />
|
||||
<ActionDropdown>
|
||||
<CopyLinkDropdownItem item={item} />
|
||||
<InfoDropdownItem item={item} />
|
||||
|
@ -317,3 +254,85 @@ function InfoDropdownItem ({ item }) {
|
|||
</Dropdown.Item>
|
||||
)
|
||||
}
|
||||
|
||||
function PaymentInfo ({ item, disableRetry, setDisableRetry }) {
|
||||
const { me } = useMe()
|
||||
const toaster = useToast()
|
||||
const retryCreateItem = useRetryCreateItem({ id: item.id })
|
||||
const waitForQrPayment = useQrPayment()
|
||||
const [disableInfoRetry, setDisableInfoRetry] = useState(disableRetry)
|
||||
if (item.deletedAt) return null
|
||||
|
||||
const disableDualRetry = disableRetry || disableInfoRetry
|
||||
function setDisableDualRetry (value) {
|
||||
setDisableInfoRetry(value)
|
||||
setDisableRetry?.(value)
|
||||
}
|
||||
|
||||
let Component
|
||||
let onClick
|
||||
if (me && item.invoice?.actionState && item.invoice?.actionState !== 'PAID') {
|
||||
if (item.invoice?.actionState === 'FAILED') {
|
||||
Component = () => <span className={classNames('text-warning', disableDualRetry && 'pulse')}>retry payment</span>
|
||||
onClick = async () => {
|
||||
if (disableDualRetry) return
|
||||
setDisableDualRetry(true)
|
||||
try {
|
||||
const { error } = await retryCreateItem({ variables: { invoiceId: parseInt(item.invoice?.id) } })
|
||||
if (error) throw error
|
||||
} catch (error) {
|
||||
toaster.danger(error.message)
|
||||
} finally {
|
||||
setDisableDualRetry(false)
|
||||
}
|
||||
}
|
||||
} else {
|
||||
Component = () => (
|
||||
<span
|
||||
className='text-info'
|
||||
>pending
|
||||
</span>
|
||||
)
|
||||
onClick = () => waitForQrPayment({ id: item.invoice?.id }, null, { cancelOnClose: false }).catch(console.error)
|
||||
}
|
||||
} else {
|
||||
return null
|
||||
}
|
||||
|
||||
return (
|
||||
<>
|
||||
<span> \ </span>
|
||||
<span
|
||||
className='text-reset pointer fw-bold'
|
||||
onClick={onClick}
|
||||
>
|
||||
<Component />
|
||||
</span>
|
||||
</>
|
||||
)
|
||||
}
|
||||
|
||||
function EditInfo ({ item, canEdit, setCanEdit, onEdit, editText, editThreshold }) {
|
||||
const router = useRouter()
|
||||
|
||||
if (canEdit) {
|
||||
return (
|
||||
<>
|
||||
<span> \ </span>
|
||||
<span
|
||||
className='text-reset pointer fw-bold'
|
||||
onClick={() => onEdit ? onEdit() : router.push(`/items/${item.id}/edit`)}
|
||||
>
|
||||
<span>{editText || 'edit'} </span>
|
||||
{(!item.invoice?.actionState || item.invoice?.actionState === 'PAID') &&
|
||||
<Countdown
|
||||
date={editThreshold}
|
||||
onComplete={() => { setCanEdit(false) }}
|
||||
/>}
|
||||
</span>
|
||||
</>
|
||||
)
|
||||
}
|
||||
|
||||
return null
|
||||
}
|
||||
|
|
|
@ -87,7 +87,7 @@ function ItemLink ({ url, rel }) {
|
|||
|
||||
export default function Item ({
|
||||
item, rank, belowTitle, right, full, children, itemClassName,
|
||||
onQuoteReply, pinnable
|
||||
onQuoteReply, pinnable, setDisableRetry, disableRetry
|
||||
}) {
|
||||
const titleRef = useRef()
|
||||
const router = useRouter()
|
||||
|
@ -139,6 +139,8 @@ export default function Item ({
|
|||
onQuoteReply={onQuoteReply}
|
||||
pinnable={pinnable}
|
||||
extraBadges={Number(item?.user?.id) === USER_ID.ad && <Badge className={styles.newComment} bg={null}>AD</Badge>}
|
||||
setDisableRetry={setDisableRetry}
|
||||
disableRetry={disableRetry}
|
||||
/>
|
||||
{belowTitle}
|
||||
</div>
|
||||
|
|
|
@ -38,6 +38,7 @@ import { paidActionCacheMods } from './use-paid-mutation'
|
|||
import { useRetryCreateItem } from './use-item-submit'
|
||||
import { payBountyCacheMods } from './pay-bounty'
|
||||
import { useToast } from './toast'
|
||||
import classNames from 'classnames'
|
||||
|
||||
function Notification ({ n, fresh }) {
|
||||
const type = n.__typename
|
||||
|
@ -102,14 +103,14 @@ function NoteHeader ({ color, children, big }) {
|
|||
)
|
||||
}
|
||||
|
||||
function NoteItem ({ item }) {
|
||||
function NoteItem ({ item, ...props }) {
|
||||
return (
|
||||
<div>
|
||||
{item.title
|
||||
? <Item item={item} itemClassName='pt-0' />
|
||||
? <Item item={item} itemClassName='pt-0' {...props} />
|
||||
: (
|
||||
<RootProvider root={item.root}>
|
||||
<Comment item={item} noReply includeParent clickToContext />
|
||||
<Comment item={item} noReply includeParent clickToContext {...props} />
|
||||
</RootProvider>)}
|
||||
</div>
|
||||
)
|
||||
|
@ -343,7 +344,10 @@ function InvoicePaid ({ n }) {
|
|||
}
|
||||
|
||||
function useActRetry ({ invoice }) {
|
||||
const bountyCacheMods = invoice.item?.bounty ? payBountyCacheMods() : {}
|
||||
const bountyCacheMods =
|
||||
invoice.item.root?.bounty === invoice.satsRequested && invoice.item.root?.mine
|
||||
? payBountyCacheMods()
|
||||
: {}
|
||||
return useAct({
|
||||
query: RETRY_PAID_ACTION,
|
||||
onPayError: (e, cache, { data }) => {
|
||||
|
@ -383,6 +387,7 @@ function Invoicification ({ n: { invoice, sortTime } }) {
|
|||
const actRetry = useActRetry({ invoice })
|
||||
const retryCreateItem = useRetryCreateItem({ id: invoice.item?.id })
|
||||
const retryPollVote = usePollVote({ query: RETRY_PAID_ACTION, itemId: invoice.item?.id })
|
||||
const [disableRetry, setDisableRetry] = useState(false)
|
||||
// XXX if we navigate to an invoice after it is retried in notifications
|
||||
// the cache will clear invoice.item and will error on window.back
|
||||
// alternatively, we could/should
|
||||
|
@ -407,7 +412,7 @@ function Invoicification ({ n: { invoice, sortTime } }) {
|
|||
invoiceActionState = invoice.item.poll?.meInvoiceActionState
|
||||
} else {
|
||||
if (invoice.actionType === 'ZAP') {
|
||||
if (invoice.item.root?.bounty === invoice.satsRequested && invoice.item.root.mine) {
|
||||
if (invoice.item.root?.bounty === invoice.satsRequested && invoice.item.root?.mine) {
|
||||
actionString = 'bounty payment'
|
||||
} else {
|
||||
actionString = 'zap'
|
||||
|
@ -443,14 +448,19 @@ function Invoicification ({ n: { invoice, sortTime } }) {
|
|||
<span className='ms-1 text-muted fw-light'> {numWithUnits(invoice.satsRequested)}</span>
|
||||
<span className={invoiceActionState === 'FAILED' ? 'visible' : 'invisible'}>
|
||||
<Button
|
||||
size='sm' variant='outline-warning ms-2 border-1 rounded py-0'
|
||||
size='sm' variant={classNames('outline-warning ms-2 border-1 rounded py-0', disableRetry && 'pulse')}
|
||||
style={{ '--bs-btn-hover-color': '#fff', '--bs-btn-active-color': '#fff' }}
|
||||
disabled={disableRetry}
|
||||
onClick={async () => {
|
||||
if (disableRetry) return
|
||||
setDisableRetry(true)
|
||||
try {
|
||||
const { error } = await retry({ variables: { invoiceId: parseInt(invoiceId) } })
|
||||
if (error) throw error
|
||||
} catch (error) {
|
||||
toaster.danger(error?.message || error?.toString?.())
|
||||
} finally {
|
||||
setDisableRetry(false)
|
||||
}
|
||||
}}
|
||||
>
|
||||
|
@ -459,7 +469,7 @@ function Invoicification ({ n: { invoice, sortTime } }) {
|
|||
<span className='text-muted ms-2 fw-normal' suppressHydrationWarning>{timeSince(new Date(sortTime))}</span>
|
||||
</span>
|
||||
</NoteHeader>
|
||||
<NoteItem item={invoice.item} />
|
||||
<NoteItem item={invoice.item} setDisableRetry={setDisableRetry} disableRetry={disableRetry} />
|
||||
</div>
|
||||
)
|
||||
}
|
||||
|
|
|
@ -57,7 +57,10 @@ export function usePaidMutation (mutation,
|
|||
let { data, ...rest } = await mutate(innerOptions)
|
||||
|
||||
// use the most inner callbacks/options if they exist
|
||||
const { onPaid, onPayError, forceWaitForPayment, persistOnNavigate, update } = { ...options, ...innerOptions }
|
||||
const {
|
||||
onPaid, onPayError, forceWaitForPayment, persistOnNavigate,
|
||||
update, waitFor = inv => inv?.actionState === 'PAID'
|
||||
} = { ...options, ...innerOptions }
|
||||
const ourOnCompleted = innerOnCompleted || onCompleted
|
||||
|
||||
// get invoice without knowing the mutation name
|
||||
|
@ -95,7 +98,7 @@ export function usePaidMutation (mutation,
|
|||
// the action is pessimistic
|
||||
try {
|
||||
// wait for the invoice to be paid
|
||||
await waitForPayment(invoice, { alwaysShowQROnFailure: true, persistOnNavigate, waitFor: inv => inv?.actionState === 'PAID' })
|
||||
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) } })
|
||||
|
|
|
@ -263,6 +263,20 @@ $zindex-sticky: 900;
|
|||
justify-self: center;
|
||||
}
|
||||
|
||||
.pulse {
|
||||
animation-name: pulse;
|
||||
animation-iteration-count: infinite;
|
||||
animation-timing-function: ease-in-out;
|
||||
animation-duration: 0.66s;
|
||||
animation-direction: alternate;
|
||||
}
|
||||
|
||||
@keyframes pulse {
|
||||
0% {
|
||||
opacity: 42%;
|
||||
}
|
||||
}
|
||||
|
||||
svg {
|
||||
fill: var(--bs-body-color);
|
||||
}
|
||||
|
|
|
@ -104,10 +104,6 @@ async function work () {
|
|||
await boss.work('paidActionCanceling', jobWrapper(paidActionCanceling))
|
||||
await boss.work('paidActionFailed', jobWrapper(paidActionFailed))
|
||||
await boss.work('paidActionPaid', jobWrapper(paidActionPaid))
|
||||
// we renamed these jobs so we leave them so they can "migrate"
|
||||
await boss.work('holdAction', jobWrapper(paidActionHeld))
|
||||
await boss.work('settleActionError', jobWrapper(paidActionFailed))
|
||||
await boss.work('settleAction', jobWrapper(paidActionPaid))
|
||||
}
|
||||
if (isServiceEnabled('search')) {
|
||||
await boss.work('indexItem', jobWrapper(indexItem))
|
||||
|
|
Loading…
Reference in New Issue