withdrawl page
This commit is contained in:
parent
7a8afd56c3
commit
ce55fdfe9c
@ -30,6 +30,6 @@ export default gql`
|
|||||||
msatsPaid: Int
|
msatsPaid: Int
|
||||||
msatsFeePaying: Int!
|
msatsFeePaying: Int!
|
||||||
msatsFeePaid: Int
|
msatsFeePaid: Int
|
||||||
status: String!
|
status: String
|
||||||
}
|
}
|
||||||
`
|
`
|
||||||
|
@ -4,6 +4,8 @@ import BootstrapForm from 'react-bootstrap/Form'
|
|||||||
import Alert from 'react-bootstrap/Alert'
|
import Alert from 'react-bootstrap/Alert'
|
||||||
import { Formik, Form as FormikForm, useFormikContext, useField } from 'formik'
|
import { Formik, Form as FormikForm, useFormikContext, useField } from 'formik'
|
||||||
import { useRef, useState } from 'react'
|
import { useRef, useState } from 'react'
|
||||||
|
import copy from 'clipboard-copy'
|
||||||
|
import Thumb from '../svgs/thumb-up-fill.svg'
|
||||||
|
|
||||||
export function SubmitButton ({ children, variant, ...props }) {
|
export function SubmitButton ({ children, variant, ...props }) {
|
||||||
const { isSubmitting } = useFormikContext()
|
const { isSubmitting } = useFormikContext()
|
||||||
@ -19,8 +21,35 @@ export function SubmitButton ({ children, variant, ...props }) {
|
|||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
export function CopyInput (props) {
|
||||||
|
const [copied, setCopied] = useState(false)
|
||||||
|
|
||||||
|
const handleClick = () => {
|
||||||
|
copy(props.placeholder)
|
||||||
|
setCopied(true)
|
||||||
|
setTimeout(() => setCopied(false), 1500)
|
||||||
|
}
|
||||||
|
|
||||||
|
return (
|
||||||
|
<Input
|
||||||
|
onClick={handleClick}
|
||||||
|
append={<Button onClick={handleClick}>{copied ? <Thumb width={18} height={18} /> : 'copy'}</Button>}
|
||||||
|
{...props}
|
||||||
|
/>
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
export function InputSkeleton ({ label }) {
|
||||||
|
return (
|
||||||
|
<BootstrapForm.Group>
|
||||||
|
{label && <BootstrapForm.Label>{label}</BootstrapForm.Label>}
|
||||||
|
<div className='clouds form-control' />
|
||||||
|
</BootstrapForm.Group>
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
export function Input ({ label, prepend, append, hint, ...props }) {
|
export function Input ({ label, prepend, append, hint, ...props }) {
|
||||||
const [field, meta] = useField(props)
|
const [field, meta] = props.readOnly ? [{}, {}] : useField(props)
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<BootstrapForm.Group>
|
<BootstrapForm.Group>
|
||||||
@ -28,7 +57,7 @@ export function Input ({ label, prepend, append, hint, ...props }) {
|
|||||||
<InputGroup hasValidation>
|
<InputGroup hasValidation>
|
||||||
{prepend && (
|
{prepend && (
|
||||||
<InputGroup.Prepend>
|
<InputGroup.Prepend>
|
||||||
<InputGroup.Text>{prepend}</InputGroup.Text>
|
{prepend}
|
||||||
</InputGroup.Prepend>
|
</InputGroup.Prepend>
|
||||||
)}
|
)}
|
||||||
<BootstrapForm.Control
|
<BootstrapForm.Control
|
||||||
@ -37,7 +66,7 @@ export function Input ({ label, prepend, append, hint, ...props }) {
|
|||||||
/>
|
/>
|
||||||
{append && (
|
{append && (
|
||||||
<InputGroup.Append>
|
<InputGroup.Append>
|
||||||
<InputGroup.Text>{append}</InputGroup.Text>
|
{append}
|
||||||
</InputGroup.Append>
|
</InputGroup.Append>
|
||||||
)}
|
)}
|
||||||
<BootstrapForm.Control.Feedback type='invalid'>
|
<BootstrapForm.Control.Feedback type='invalid'>
|
||||||
|
41
components/invoice-status.js
Normal file
41
components/invoice-status.js
Normal file
@ -0,0 +1,41 @@
|
|||||||
|
import Moon from '../svgs/moon-fill.svg'
|
||||||
|
import Check from '../svgs/check-double-line.svg'
|
||||||
|
import Fail from '../svgs/close-line.svg'
|
||||||
|
|
||||||
|
function InvoiceDefaultStatus ({ status }) {
|
||||||
|
return (
|
||||||
|
<div className='d-flex mt-2'>
|
||||||
|
<Moon className='spin fill-grey' />
|
||||||
|
<div className='ml-3 text-muted' style={{ fontWeight: '600' }}>{status}</div>
|
||||||
|
</div>
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
function InvoiceConfirmedStatus ({ status }) {
|
||||||
|
return (
|
||||||
|
<div className='d-flex mt-2'>
|
||||||
|
<Check className='fill-success' />
|
||||||
|
<div className='ml-3 text-success' style={{ fontWeight: '600' }}>{status}</div>
|
||||||
|
</div>
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
function InvoiceFailedStatus ({ status }) {
|
||||||
|
return (
|
||||||
|
<div className='d-flex mt-2'>
|
||||||
|
<Fail className='fill-danger' />
|
||||||
|
<div className='ml-3 text-danger' style={{ fontWeight: '600' }}>{status}</div>
|
||||||
|
</div>
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
export default function InvoiceStatus ({ variant, status }) {
|
||||||
|
switch (variant) {
|
||||||
|
case 'confirmed':
|
||||||
|
return <InvoiceConfirmedStatus status={status} />
|
||||||
|
case 'failed':
|
||||||
|
return <InvoiceFailedStatus status={status} />
|
||||||
|
default:
|
||||||
|
return <InvoiceDefaultStatus status={status} />
|
||||||
|
}
|
||||||
|
}
|
@ -1,28 +1,20 @@
|
|||||||
import QRCode from 'qrcode.react'
|
import QRCode from 'qrcode.react'
|
||||||
import { InputGroup } from 'react-bootstrap'
|
import { CopyInput, InputSkeleton } from './form'
|
||||||
import Moon from '../svgs/moon-fill.svg'
|
import InvoiceStatus from './invoice-status'
|
||||||
import copy from 'clipboard-copy'
|
|
||||||
import Thumb from '../svgs/thumb-up-fill.svg'
|
|
||||||
import { useState } from 'react'
|
|
||||||
import BootstrapForm from 'react-bootstrap/Form'
|
|
||||||
import Button from 'react-bootstrap/Button'
|
|
||||||
import Check from '../svgs/check-double-line.svg'
|
|
||||||
import Fail from '../svgs/close-line.svg'
|
|
||||||
|
|
||||||
export function Invoice ({ invoice }) {
|
export function Invoice ({ invoice }) {
|
||||||
const [copied, setCopied] = useState(false)
|
|
||||||
const qrValue = 'lightning:' + invoice.bolt11.toUpperCase()
|
const qrValue = 'lightning:' + invoice.bolt11.toUpperCase()
|
||||||
|
|
||||||
let InvoiceStatus = InvoiceDefaultStatus
|
let variant = 'default'
|
||||||
let status = 'waiting for you'
|
let status = 'waiting for you'
|
||||||
if (invoice.confirmedAt) {
|
if (invoice.confirmedAt) {
|
||||||
InvoiceStatus = InvoiceConfirmedStatus
|
variant = 'confirmed'
|
||||||
status = `${invoice.msatsReceived / 1000} sats deposited`
|
status = `${invoice.msatsReceived / 1000} sats deposited`
|
||||||
} else if (invoice.cancelled) {
|
} else if (invoice.cancelled) {
|
||||||
InvoiceStatus = InvoiceFailedStatus
|
variant = 'failed'
|
||||||
status = 'cancelled'
|
status = 'cancelled'
|
||||||
} else if (invoice.expiresAt <= new Date()) {
|
} else if (invoice.expiresAt <= new Date()) {
|
||||||
InvoiceStatus = InvoiceFailedStatus
|
variant = 'failed'
|
||||||
status = 'expired'
|
status = 'expired'
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -32,58 +24,21 @@ export function Invoice ({ invoice }) {
|
|||||||
<QRCode className='h-auto mw-100' value={qrValue} renderAs='svg' size={300} />
|
<QRCode className='h-auto mw-100' value={qrValue} renderAs='svg' size={300} />
|
||||||
</div>
|
</div>
|
||||||
<div className='mt-3 w-100'>
|
<div className='mt-3 w-100'>
|
||||||
<InputGroup onClick={() => {
|
<CopyInput type='text' placeholder={invoice.bolt11} readOnly />
|
||||||
copy(invoice.bolt11)
|
|
||||||
setCopied(true)
|
|
||||||
setTimeout(() => setCopied(false), 1500)
|
|
||||||
}}
|
|
||||||
>
|
|
||||||
<BootstrapForm.Control type='text' placeholder={invoice.bolt11} readOnly />
|
|
||||||
<InputGroup.Append>
|
|
||||||
<Button>{copied ? <Thumb width={20} height={20} /> : 'copy'}</Button>
|
|
||||||
</InputGroup.Append>
|
|
||||||
</InputGroup>
|
|
||||||
</div>
|
</div>
|
||||||
<InvoiceStatus status={status} />
|
<InvoiceStatus variant={variant} status={status} />
|
||||||
</>
|
</>
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
export function InvoiceDefaultStatus ({ status }) {
|
|
||||||
return (
|
|
||||||
<div className='d-flex mt-4'>
|
|
||||||
<Moon className='spin fill-grey' />
|
|
||||||
<div className='ml-3 text-muted' style={{ fontWeight: '600' }}>{status}</div>
|
|
||||||
</div>
|
|
||||||
)
|
|
||||||
}
|
|
||||||
|
|
||||||
export function InvoiceConfirmedStatus ({ status }) {
|
|
||||||
return (
|
|
||||||
<div className='d-flex mt-4'>
|
|
||||||
<Check className='fill-success' />
|
|
||||||
<div className='ml-3 text-success' style={{ fontWeight: '600' }}>{status}</div>
|
|
||||||
</div>
|
|
||||||
)
|
|
||||||
}
|
|
||||||
|
|
||||||
export function InvoiceFailedStatus ({ status }) {
|
|
||||||
return (
|
|
||||||
<div className='d-flex mt-4'>
|
|
||||||
<Fail className='fill-danger' />
|
|
||||||
<div className='ml-3 text-danger' style={{ fontWeight: '600' }}>{status}</div>
|
|
||||||
</div>
|
|
||||||
)
|
|
||||||
}
|
|
||||||
|
|
||||||
export function InvoiceSkeleton ({ status }) {
|
export function InvoiceSkeleton ({ status }) {
|
||||||
return (
|
return (
|
||||||
<>
|
<>
|
||||||
<div className='h-auto w-100 clouds' style={{ paddingTop: 'min(300px, 100%)', maxWidth: '300px' }} />
|
<div className='h-auto w-100 clouds' style={{ paddingTop: 'min(300px, 100%)', maxWidth: '300px' }} />
|
||||||
<div className='mt-3 w-100'>
|
<div className='mt-3 w-100'>
|
||||||
<div className='w-100 clouds form-control' />
|
<InputSkeleton />
|
||||||
</div>
|
</div>
|
||||||
<InvoiceDefaultStatus status={status} />
|
<InvoiceStatus variant='default' status={status} />
|
||||||
</>
|
</>
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
@ -6,6 +6,7 @@ import * as Yup from 'yup'
|
|||||||
import { gql, useMutation, useQuery } from '@apollo/client'
|
import { gql, useMutation, useQuery } from '@apollo/client'
|
||||||
import { InvoiceSkeleton } from '../components/invoice'
|
import { InvoiceSkeleton } from '../components/invoice'
|
||||||
import LayoutCenter from '../components/layout-center'
|
import LayoutCenter from '../components/layout-center'
|
||||||
|
import InputGroup from 'react-bootstrap/InputGroup'
|
||||||
|
|
||||||
export default function Wallet () {
|
export default function Wallet () {
|
||||||
return (
|
return (
|
||||||
@ -73,7 +74,7 @@ export function FundForm () {
|
|||||||
name='amount'
|
name='amount'
|
||||||
required
|
required
|
||||||
autoFocus
|
autoFocus
|
||||||
append='sats'
|
append={<InputGroup.Text className='text-monospace'>sats</InputGroup.Text>}
|
||||||
/>
|
/>
|
||||||
<SubmitButton variant='success' className='mt-2'>generate invoice</SubmitButton>
|
<SubmitButton variant='success' className='mt-2'>generate invoice</SubmitButton>
|
||||||
</Form>
|
</Form>
|
||||||
@ -130,7 +131,7 @@ export function WithdrawlForm () {
|
|||||||
label='max fee'
|
label='max fee'
|
||||||
name='maxFee'
|
name='maxFee'
|
||||||
required
|
required
|
||||||
append='millisats'
|
append={<InputGroup.Text className='text-monospace'>millisats</InputGroup.Text>}
|
||||||
/>
|
/>
|
||||||
<SubmitButton variant='success' className='mt-2'>withdrawl</SubmitButton>
|
<SubmitButton variant='success' className='mt-2'>withdrawl</SubmitButton>
|
||||||
</Form>
|
</Form>
|
||||||
|
@ -1,6 +1,9 @@
|
|||||||
import { useQuery } from '@apollo/client'
|
import { useQuery } from '@apollo/client'
|
||||||
import gql from 'graphql-tag'
|
import gql from 'graphql-tag'
|
||||||
import LayoutCenter from '../../components/layout-center'
|
import LayoutCenter from '../../components/layout-center'
|
||||||
|
import { CopyInput, Input, InputSkeleton } from '../../components/form'
|
||||||
|
import InputGroup from 'react-bootstrap/InputGroup'
|
||||||
|
import InvoiceStatus from '../../components/invoice-status'
|
||||||
|
|
||||||
export async function getServerSideProps ({ params: { id } }) {
|
export async function getServerSideProps ({ params: { id } }) {
|
||||||
return {
|
return {
|
||||||
@ -15,6 +18,8 @@ export default function Withdrawl ({ id }) {
|
|||||||
{
|
{
|
||||||
withdrawl(id: ${id}) {
|
withdrawl(id: ${id}) {
|
||||||
bolt11
|
bolt11
|
||||||
|
msatsFeePaying
|
||||||
|
status
|
||||||
}
|
}
|
||||||
}`
|
}`
|
||||||
return (
|
return (
|
||||||
@ -28,8 +33,35 @@ function LoadWithdrawl ({ query }) {
|
|||||||
const { loading, error, data } = useQuery(query, { pollInterval: 1000 })
|
const { loading, error, data } = useQuery(query, { pollInterval: 1000 })
|
||||||
if (error) return <div>error</div>
|
if (error) return <div>error</div>
|
||||||
if (!data || loading) {
|
if (!data || loading) {
|
||||||
return <div>withdrawl loading</div>
|
return (
|
||||||
|
<>
|
||||||
|
<div className='w-100'>
|
||||||
|
<InputSkeleton label='invoice' />
|
||||||
|
</div>
|
||||||
|
<div className='w-100'>
|
||||||
|
<InputSkeleton label='max fee' />
|
||||||
|
</div>
|
||||||
|
<InvoiceStatus status='pending' />
|
||||||
|
</>
|
||||||
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
return <div>hi</div>
|
return (
|
||||||
|
<>
|
||||||
|
<div className='w-100'>
|
||||||
|
<CopyInput
|
||||||
|
label='invoice' type='text'
|
||||||
|
placeholder={data.withdrawl.bolt11} readOnly
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
<div className='w-100'>
|
||||||
|
<Input
|
||||||
|
label='max fee' type='text'
|
||||||
|
placeholder={data.withdrawl.msatsFeePaying} readOnly
|
||||||
|
append={<InputGroup.Text className='text-monospace'>millisats</InputGroup.Text>}
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
<InvoiceStatus status='pending' />
|
||||||
|
</>
|
||||||
|
)
|
||||||
}
|
}
|
||||||
|
Loading…
x
Reference in New Issue
Block a user