2021-05-06 21:15:22 +00:00
|
|
|
import { useRouter } from 'next/router'
|
|
|
|
import { Form, Input, SubmitButton } from '../components/form'
|
|
|
|
import Link from 'next/link'
|
|
|
|
import Button from 'react-bootstrap/Button'
|
2021-10-28 19:59:53 +00:00
|
|
|
import { gql, useMutation, useQuery } from '@apollo/client'
|
2023-01-18 18:49:20 +00:00
|
|
|
import Qr, { QrSkeleton } from '../components/qr'
|
2021-05-06 21:15:22 +00:00
|
|
|
import LayoutCenter from '../components/layout-center'
|
2021-05-13 13:28:38 +00:00
|
|
|
import InputGroup from 'react-bootstrap/InputGroup'
|
2021-08-19 21:42:21 +00:00
|
|
|
import { WithdrawlSkeleton } from './withdrawals/[id]'
|
2021-07-15 16:42:02 +00:00
|
|
|
import { useMe } from '../components/me'
|
2021-10-08 14:35:57 +00:00
|
|
|
import { useEffect, useState } from 'react'
|
2021-09-07 17:52:59 +00:00
|
|
|
import { requestProvider } from 'webln'
|
2021-10-08 14:35:57 +00:00
|
|
|
import { Alert } from 'react-bootstrap'
|
2022-01-23 17:21:55 +00:00
|
|
|
import { CREATE_WITHDRAWL, SEND_TO_LNADDR } from '../fragments/wallet'
|
2022-04-21 22:50:02 +00:00
|
|
|
import { getGetServerSideProps } from '../api/ssrApollo'
|
2023-02-08 19:38:04 +00:00
|
|
|
import { amountSchema, lnAddrSchema, withdrawlSchema } from '../lib/validate'
|
2022-04-21 22:50:02 +00:00
|
|
|
|
|
|
|
export const getServerSideProps = getGetServerSideProps()
|
2021-05-06 21:15:22 +00:00
|
|
|
|
|
|
|
export default function Wallet () {
|
|
|
|
return (
|
|
|
|
<LayoutCenter>
|
|
|
|
<WalletForm />
|
|
|
|
</LayoutCenter>
|
|
|
|
)
|
|
|
|
}
|
|
|
|
|
2021-07-15 16:42:02 +00:00
|
|
|
function YouHaveSats () {
|
|
|
|
const me = useMe()
|
|
|
|
return (
|
|
|
|
<h2 className={`${me ? 'visible' : 'invisible'} text-success pb-5`}>
|
|
|
|
you have <span className='text-monospace'>{me && me.sats}</span> sats
|
|
|
|
</h2>
|
|
|
|
)
|
|
|
|
}
|
|
|
|
|
2021-12-16 20:17:50 +00:00
|
|
|
function WalletHistory () {
|
|
|
|
return (
|
|
|
|
<div className='pt-4'>
|
|
|
|
<Link href='/satistics?inc=invoice,withdrawal' passHref>
|
|
|
|
<a className='text-muted font-weight-bold text-underline'>wallet history</a>
|
|
|
|
</Link>
|
|
|
|
</div>
|
|
|
|
)
|
|
|
|
}
|
|
|
|
|
2021-05-06 21:15:22 +00:00
|
|
|
export function WalletForm () {
|
|
|
|
const router = useRouter()
|
2021-12-16 20:17:50 +00:00
|
|
|
|
2021-05-06 21:15:22 +00:00
|
|
|
if (!router.query.type) {
|
|
|
|
return (
|
2021-09-02 17:22:48 +00:00
|
|
|
<div className='align-items-center text-center'>
|
2021-07-15 16:42:02 +00:00
|
|
|
<YouHaveSats />
|
2021-05-06 21:15:22 +00:00
|
|
|
<Link href='/wallet?type=fund'>
|
|
|
|
<Button variant='success'>fund</Button>
|
|
|
|
</Link>
|
|
|
|
<span className='mx-3 font-weight-bold text-muted'>or</span>
|
2021-08-19 21:42:21 +00:00
|
|
|
<Link href='/wallet?type=withdraw'>
|
|
|
|
<Button variant='success'>withdraw</Button>
|
2021-05-06 21:15:22 +00:00
|
|
|
</Link>
|
2021-12-16 20:17:50 +00:00
|
|
|
<WalletHistory />
|
2021-05-06 21:15:22 +00:00
|
|
|
</div>
|
|
|
|
)
|
|
|
|
}
|
|
|
|
|
|
|
|
if (router.query.type === 'fund') {
|
|
|
|
return <FundForm />
|
2021-10-28 19:59:53 +00:00
|
|
|
} else if (router.query.type === 'withdraw') {
|
2021-05-06 21:15:22 +00:00
|
|
|
return <WithdrawlForm />
|
2022-01-23 17:21:55 +00:00
|
|
|
} else if (router.query.type === 'lnurl-withdraw') {
|
2021-10-28 19:59:53 +00:00
|
|
|
return <LnWithdrawal />
|
2022-01-23 17:21:55 +00:00
|
|
|
} else {
|
|
|
|
return <LnAddrWithdrawal />
|
2021-05-06 21:15:22 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
export function FundForm () {
|
2021-10-08 14:35:57 +00:00
|
|
|
const me = useMe()
|
|
|
|
const [showAlert, setShowAlert] = useState(true)
|
2021-05-06 21:15:22 +00:00
|
|
|
const router = useRouter()
|
2021-05-13 21:19:51 +00:00
|
|
|
const [createInvoice, { called, error }] = useMutation(gql`
|
2021-05-06 21:15:22 +00:00
|
|
|
mutation createInvoice($amount: Int!) {
|
2021-05-11 15:52:50 +00:00
|
|
|
createInvoice(amount: $amount) {
|
|
|
|
id
|
|
|
|
}
|
2021-05-06 21:15:22 +00:00
|
|
|
}`)
|
|
|
|
|
2021-10-08 14:35:57 +00:00
|
|
|
useEffect(() => {
|
2021-10-08 14:40:35 +00:00
|
|
|
setShowAlert(!localStorage.getItem('hideLnAddrAlert'))
|
2021-10-08 14:35:57 +00:00
|
|
|
}, [])
|
|
|
|
|
2021-05-13 21:19:51 +00:00
|
|
|
if (called && !error) {
|
2023-01-18 18:49:20 +00:00
|
|
|
return <QrSkeleton status='generating' />
|
2021-05-06 21:15:22 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
return (
|
2021-07-15 16:42:02 +00:00
|
|
|
<>
|
|
|
|
<YouHaveSats />
|
2021-10-08 14:35:57 +00:00
|
|
|
{me && showAlert &&
|
|
|
|
<Alert
|
|
|
|
variant='success' dismissible onClose={() => {
|
|
|
|
localStorage.setItem('hideLnAddrAlert', 'yep')
|
|
|
|
setShowAlert(false)
|
|
|
|
}}
|
|
|
|
>
|
2022-03-17 18:04:53 +00:00
|
|
|
You can also fund your account via lightning address with <strong>{`${me.name}@stacker.news`}</strong>
|
2021-10-08 14:35:57 +00:00
|
|
|
</Alert>}
|
2021-07-15 16:42:02 +00:00
|
|
|
<Form
|
|
|
|
initial={{
|
|
|
|
amount: 1000
|
|
|
|
}}
|
2022-05-31 22:17:48 +00:00
|
|
|
initialError={error?.toString()}
|
2023-02-08 19:38:04 +00:00
|
|
|
schema={amountSchema}
|
2021-07-15 16:42:02 +00:00
|
|
|
onSubmit={async ({ amount }) => {
|
|
|
|
const { data } = await createInvoice({ variables: { amount: Number(amount) } })
|
|
|
|
router.push(`/invoices/${data.createInvoice.id}`)
|
|
|
|
}}
|
|
|
|
>
|
|
|
|
<Input
|
|
|
|
label='amount'
|
|
|
|
name='amount'
|
|
|
|
required
|
|
|
|
autoFocus
|
|
|
|
append={<InputGroup.Text className='text-monospace'>sats</InputGroup.Text>}
|
|
|
|
/>
|
|
|
|
<SubmitButton variant='success' className='mt-2'>generate invoice</SubmitButton>
|
|
|
|
</Form>
|
2021-12-16 20:17:50 +00:00
|
|
|
<WalletHistory />
|
2021-07-15 16:42:02 +00:00
|
|
|
</>
|
2021-05-06 21:15:22 +00:00
|
|
|
)
|
|
|
|
}
|
|
|
|
|
2021-09-07 17:52:59 +00:00
|
|
|
const MAX_FEE_DEFAULT = 10
|
|
|
|
|
2021-05-06 21:15:22 +00:00
|
|
|
export function WithdrawlForm () {
|
2021-05-13 01:51:37 +00:00
|
|
|
const router = useRouter()
|
2021-09-07 17:52:59 +00:00
|
|
|
const me = useMe()
|
2021-05-12 23:04:19 +00:00
|
|
|
|
2021-10-28 19:59:53 +00:00
|
|
|
const [createWithdrawl, { called, error }] = useMutation(CREATE_WITHDRAWL)
|
2021-05-12 23:04:19 +00:00
|
|
|
|
2022-11-06 17:28:58 +00:00
|
|
|
useEffect(() => {
|
|
|
|
async function effect () {
|
|
|
|
try {
|
|
|
|
const provider = await requestProvider()
|
|
|
|
const { paymentRequest: invoice } = await provider.makeInvoice({
|
|
|
|
defaultMemo: `Withdrawal for @${me.name} on SN`,
|
|
|
|
maximumAmount: Math.max(me.sats - MAX_FEE_DEFAULT, 0)
|
|
|
|
})
|
|
|
|
const { data } = await createWithdrawl({ variables: { invoice, maxFee: MAX_FEE_DEFAULT } })
|
|
|
|
router.push(`/withdrawals/${data.createWithdrawl.id}`)
|
|
|
|
} catch (e) {
|
|
|
|
console.log(e.message)
|
|
|
|
}
|
2021-09-07 17:52:59 +00:00
|
|
|
}
|
2022-11-06 17:28:58 +00:00
|
|
|
effect()
|
2021-09-07 17:52:59 +00:00
|
|
|
}, [])
|
|
|
|
|
2021-05-13 21:19:51 +00:00
|
|
|
if (called && !error) {
|
|
|
|
return <WithdrawlSkeleton status='sending' />
|
|
|
|
}
|
|
|
|
|
2021-05-12 23:04:19 +00:00
|
|
|
return (
|
|
|
|
<>
|
2021-07-15 16:42:02 +00:00
|
|
|
<YouHaveSats />
|
2021-05-12 23:04:19 +00:00
|
|
|
<Form
|
|
|
|
initial={{
|
2021-05-13 01:51:37 +00:00
|
|
|
invoice: '',
|
2021-09-07 17:52:59 +00:00
|
|
|
maxFee: MAX_FEE_DEFAULT
|
2021-05-12 23:04:19 +00:00
|
|
|
}}
|
2021-05-13 21:19:51 +00:00
|
|
|
initialError={error ? error.toString() : undefined}
|
2023-02-08 19:38:04 +00:00
|
|
|
schema={withdrawlSchema}
|
2021-05-12 23:04:19 +00:00
|
|
|
onSubmit={async ({ invoice, maxFee }) => {
|
2021-05-13 01:51:37 +00:00
|
|
|
const { data } = await createWithdrawl({ variables: { invoice, maxFee: Number(maxFee) } })
|
2021-08-19 21:42:21 +00:00
|
|
|
router.push(`/withdrawals/${data.createWithdrawl.id}`)
|
2021-05-12 23:04:19 +00:00
|
|
|
}}
|
|
|
|
>
|
|
|
|
<Input
|
|
|
|
label='invoice'
|
|
|
|
name='invoice'
|
|
|
|
required
|
|
|
|
autoFocus
|
2023-05-11 19:34:42 +00:00
|
|
|
clear
|
2021-05-12 23:04:19 +00:00
|
|
|
/>
|
|
|
|
<Input
|
|
|
|
label='max fee'
|
|
|
|
name='maxFee'
|
|
|
|
required
|
2021-05-13 21:19:51 +00:00
|
|
|
append={<InputGroup.Text className='text-monospace'>sats</InputGroup.Text>}
|
2021-05-12 23:04:19 +00:00
|
|
|
/>
|
2021-08-19 21:42:21 +00:00
|
|
|
<SubmitButton variant='success' className='mt-2'>withdraw</SubmitButton>
|
2021-05-12 23:04:19 +00:00
|
|
|
</Form>
|
2022-01-23 17:21:55 +00:00
|
|
|
<span className='my-3 font-weight-bold text-muted'>or via</span>
|
2021-10-28 19:59:53 +00:00
|
|
|
<Link href='/wallet?type=lnurl-withdraw'>
|
|
|
|
<Button variant='grey'>QR code</Button>
|
|
|
|
</Link>
|
2022-01-23 17:21:55 +00:00
|
|
|
<Link href='/wallet?type=lnaddr-withdraw'>
|
|
|
|
<Button className='mt-2' variant='grey'>Lightning Address</Button>
|
|
|
|
</Link>
|
2021-12-16 20:17:50 +00:00
|
|
|
<WalletHistory />
|
2021-05-12 23:04:19 +00:00
|
|
|
</>
|
|
|
|
)
|
2021-05-06 21:15:22 +00:00
|
|
|
}
|
2021-10-28 19:59:53 +00:00
|
|
|
|
|
|
|
function LnQRWith ({ k1, encodedUrl }) {
|
|
|
|
const router = useRouter()
|
|
|
|
const query = gql`
|
|
|
|
{
|
|
|
|
lnWith(k1: "${k1}") {
|
|
|
|
withdrawalId
|
|
|
|
k1
|
|
|
|
}
|
|
|
|
}`
|
|
|
|
const { data } = useQuery(query, { pollInterval: 1000, fetchPolicy: 'cache-first' })
|
|
|
|
|
|
|
|
if (data?.lnWith?.withdrawalId) {
|
|
|
|
router.push(`/withdrawals/${data.lnWith.withdrawalId}`)
|
|
|
|
}
|
|
|
|
|
2023-01-18 18:49:20 +00:00
|
|
|
return <Qr value={encodedUrl} status='waiting for you' />
|
2021-10-28 19:59:53 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
export function LnWithdrawal () {
|
|
|
|
// query for challenge
|
2022-11-06 17:28:58 +00:00
|
|
|
const [createWith, { data, error }] = useMutation(gql`
|
|
|
|
mutation createWith {
|
2021-10-28 19:59:53 +00:00
|
|
|
createWith {
|
|
|
|
k1
|
|
|
|
encodedUrl
|
|
|
|
}
|
|
|
|
}`)
|
|
|
|
|
2022-11-06 17:28:58 +00:00
|
|
|
useEffect(() => {
|
|
|
|
createWith()
|
|
|
|
}, [])
|
2021-10-28 19:59:53 +00:00
|
|
|
|
|
|
|
if (error) return <div>error</div>
|
|
|
|
|
|
|
|
if (!data) {
|
2023-01-18 18:49:20 +00:00
|
|
|
return <QrSkeleton status='generating' />
|
2021-10-28 19:59:53 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
return <LnQRWith {...data.createWith} />
|
|
|
|
}
|
2022-01-23 17:21:55 +00:00
|
|
|
|
|
|
|
export function LnAddrWithdrawal () {
|
|
|
|
const router = useRouter()
|
|
|
|
const [sendToLnAddr, { called, error }] = useMutation(SEND_TO_LNADDR)
|
|
|
|
|
|
|
|
if (called && !error) {
|
|
|
|
return <WithdrawlSkeleton status='sending' />
|
|
|
|
}
|
|
|
|
|
|
|
|
return (
|
|
|
|
<>
|
|
|
|
<YouHaveSats />
|
|
|
|
<Form
|
|
|
|
initial={{
|
|
|
|
addr: '',
|
|
|
|
amount: 1,
|
|
|
|
maxFee: 10
|
|
|
|
}}
|
2023-02-08 19:38:04 +00:00
|
|
|
schema={lnAddrSchema}
|
2022-01-23 17:21:55 +00:00
|
|
|
initialError={error ? error.toString() : undefined}
|
|
|
|
onSubmit={async ({ addr, amount, maxFee }) => {
|
2022-02-02 20:56:14 +00:00
|
|
|
const { data } = await sendToLnAddr({ variables: { addr, amount: Number(amount), maxFee: Number(maxFee) } })
|
2022-01-23 17:21:55 +00:00
|
|
|
router.push(`/withdrawals/${data.sendToLnAddr.id}`)
|
|
|
|
}}
|
|
|
|
>
|
|
|
|
<Input
|
|
|
|
label='lightning address'
|
|
|
|
name='addr'
|
|
|
|
required
|
|
|
|
autoFocus
|
|
|
|
/>
|
|
|
|
<Input
|
|
|
|
label='amount'
|
|
|
|
name='amount'
|
|
|
|
required
|
|
|
|
autoFocus
|
|
|
|
append={<InputGroup.Text className='text-monospace'>sats</InputGroup.Text>}
|
|
|
|
/>
|
|
|
|
<Input
|
|
|
|
label='max fee'
|
|
|
|
name='maxFee'
|
|
|
|
required
|
|
|
|
append={<InputGroup.Text className='text-monospace'>sats</InputGroup.Text>}
|
|
|
|
/>
|
|
|
|
<SubmitButton variant='success' className='mt-2'>send</SubmitButton>
|
|
|
|
</Form>
|
|
|
|
</>
|
|
|
|
)
|
|
|
|
}
|