184 lines
5.2 KiB
JavaScript
184 lines
5.2 KiB
JavaScript
import { useQuery, useMutation } from '@apollo/client'
|
|
import { CenterLayout } from '@/components/layout'
|
|
import { CopyInput, Input, InputSkeleton } from '@/components/form'
|
|
import InputGroup from 'react-bootstrap/InputGroup'
|
|
import InvoiceStatus from '@/components/invoice-status'
|
|
import { useRouter } from 'next/router'
|
|
import { WITHDRAWL } from '@/fragments/wallet'
|
|
import Link from 'next/link'
|
|
import { SSR, INVOICE_RETENTION_DAYS, FAST_POLL_INTERVAL } from '@/lib/constants'
|
|
import { numWithUnits } from '@/lib/format'
|
|
import Bolt11Info from '@/components/bolt11-info'
|
|
import { datePivot, timeLeft } from '@/lib/time'
|
|
import { useMe } from '@/components/me'
|
|
import { useToast } from '@/components/toast'
|
|
import { gql } from 'graphql-tag'
|
|
import { useShowModal } from '@/components/modal'
|
|
import { DeleteConfirm } from '@/components/delete'
|
|
import { getGetServerSideProps } from '@/api/ssrApollo'
|
|
|
|
// force SSR to include CSP nonces
|
|
export const getServerSideProps = getGetServerSideProps({ query: null })
|
|
|
|
export default function Withdrawl () {
|
|
return (
|
|
<CenterLayout>
|
|
<LoadWithdrawl />
|
|
</CenterLayout>
|
|
)
|
|
}
|
|
|
|
export function WithdrawlSkeleton ({ status }) {
|
|
return (
|
|
<>
|
|
<div className='w-100 form-group'>
|
|
<InputSkeleton label='invoice' />
|
|
</div>
|
|
<div className='w-100 form-group'>
|
|
<InputSkeleton label='max fee' />
|
|
</div>
|
|
<InvoiceStatus status={status} />
|
|
<div className='w-100 mt-3'>
|
|
<Bolt11Info />
|
|
</div>
|
|
</>
|
|
)
|
|
}
|
|
|
|
function LoadWithdrawl () {
|
|
const router = useRouter()
|
|
const { loading, error, data } = useQuery(WITHDRAWL, SSR
|
|
? {}
|
|
: {
|
|
variables: { id: router.query.id },
|
|
pollInterval: FAST_POLL_INTERVAL,
|
|
nextFetchPolicy: 'cache-and-network'
|
|
})
|
|
if (error) return <div>error</div>
|
|
if (!data || loading) {
|
|
return <WithdrawlSkeleton status='loading' />
|
|
}
|
|
|
|
const TryMaxFee = () =>
|
|
<Link href='/wallet?type=withdraw' className='text-reset text-underline'>
|
|
<small className='ms-3'>try increasing max fee</small>
|
|
</Link>
|
|
|
|
let status = 'pending'
|
|
let variant = 'default'
|
|
switch (data.withdrawl.status) {
|
|
case 'CONFIRMED':
|
|
status = `sent ${numWithUnits(data.withdrawl.satsPaid, { abbreviate: false })} with ${numWithUnits(data.withdrawl.satsFeePaid, { abbreviate: false })} in routing fees`
|
|
variant = 'confirmed'
|
|
break
|
|
case 'INSUFFICIENT_BALANCE':
|
|
status = <>insufficient balance <small className='ms-3'>contact keyan!</small></>
|
|
variant = 'failed'
|
|
break
|
|
case 'INVALID_PAYMENT':
|
|
status = 'invalid invoice'
|
|
variant = 'failed'
|
|
break
|
|
case 'PATHFINDING_TIMEOUT':
|
|
status = <>timed out finding route <TryMaxFee /></>
|
|
variant = 'failed'
|
|
break
|
|
case 'ROUTE_NOT_FOUND':
|
|
status = <>no route <TryMaxFee /></>
|
|
variant = 'failed'
|
|
break
|
|
case 'UNKNOWN_FAILURE':
|
|
status = <>unknown error</>
|
|
variant = 'failed'
|
|
break
|
|
default:
|
|
break
|
|
}
|
|
|
|
return (
|
|
<>
|
|
<div className='w-100'>
|
|
<CopyInput
|
|
label='invoice' type='text'
|
|
placeholder={data.withdrawl.bolt11 || 'deleted'} readOnly noForm
|
|
/>
|
|
</div>
|
|
<div className='w-100'>
|
|
<Input
|
|
label='max fee' type='text'
|
|
placeholder={data.withdrawl.satsFeePaying} readOnly noForm
|
|
append={<InputGroup.Text className='text-monospace'>sats</InputGroup.Text>}
|
|
/>
|
|
</div>
|
|
<InvoiceStatus variant={variant} status={status} />
|
|
<div className='w-100 mt-3'>
|
|
<Bolt11Info bolt11={data.withdrawl.bolt11} preimage={data.withdrawl.preimage}>
|
|
<PrivacyOption wd={data.withdrawl} />
|
|
</Bolt11Info>
|
|
</div>
|
|
</>
|
|
)
|
|
}
|
|
|
|
function PrivacyOption ({ wd }) {
|
|
if (!wd.bolt11) return
|
|
|
|
const me = useMe()
|
|
const keepUntil = datePivot(new Date(wd.createdAt), { days: INVOICE_RETENTION_DAYS })
|
|
const oldEnough = new Date() >= keepUntil
|
|
if (!oldEnough) {
|
|
return (
|
|
<span className='text-muted fst-italic'>
|
|
{`this invoice ${me.privates?.autoDropBolt11s ? 'will be auto-deleted' : 'can be deleted'} in ${timeLeft(keepUntil)}`}
|
|
</span>
|
|
)
|
|
}
|
|
|
|
const showModal = useShowModal()
|
|
const toaster = useToast()
|
|
const [dropBolt11] = useMutation(
|
|
gql`
|
|
mutation dropBolt11($id: ID!) {
|
|
dropBolt11(id: $id) {
|
|
id
|
|
}
|
|
}`, {
|
|
update (cache) {
|
|
cache.modify({
|
|
id: `Withdrawl:${wd.id}`,
|
|
fields: {
|
|
bolt11: () => null,
|
|
hash: () => null
|
|
}
|
|
})
|
|
}
|
|
})
|
|
|
|
return (
|
|
<span
|
|
className='btn btn-md btn-danger' onClick={() => {
|
|
showModal(onClose => {
|
|
return (
|
|
<DeleteConfirm
|
|
type='invoice'
|
|
onConfirm={async () => {
|
|
if (me) {
|
|
try {
|
|
await dropBolt11({ variables: { id: wd.id } })
|
|
} catch (err) {
|
|
toaster.danger('unable to delete invoice: ' + err.message || err.toString?.())
|
|
throw err
|
|
} finally {
|
|
onClose()
|
|
}
|
|
}
|
|
}}
|
|
/>
|
|
)
|
|
})
|
|
}}
|
|
>delete invoice
|
|
</span>
|
|
)
|
|
}
|