import { useRouter } from 'next/router'
import { Checkbox, Form, Input, InputUserSuggest, SubmitButton } from '@/components/form'
import Link from 'next/link'
import Button from 'react-bootstrap/Button'
import { gql, useMutation, useQuery } from '@apollo/client'
import Qr, { QrSkeleton } from '@/components/qr'
import { CenterLayout } from '@/components/layout'
import InputGroup from 'react-bootstrap/InputGroup'
import { WithdrawlSkeleton } from '@/pages/withdrawals/[id]'
import { useMe } from '@/components/me'
import { useEffect, useState } from 'react'
import { requestProvider } from 'webln'
import Alert from 'react-bootstrap/Alert'
import { CREATE_WITHDRAWL, SEND_TO_LNADDR } from '@/fragments/wallet'
import { getGetServerSideProps } from '@/api/ssrApollo'
import { amountSchema, lnAddrSchema, withdrawlSchema } from '@/lib/validate'
import Nav from 'react-bootstrap/Nav'
import { BALANCE_LIMIT_MSATS, FAST_POLL_INTERVAL, SSR } from '@/lib/constants'
import { msatsToSats, numWithUnits } from '@/lib/format'
import styles from '@/components/user-header.module.css'
import HiddenWalletSummary from '@/components/hidden-wallet-summary'
import AccordianItem from '@/components/accordian-item'
import { lnAddrOptions } from '@/lib/lnurl'
import useDebounceCallback from '@/components/use-debounce-callback'
import { QrScanner } from '@yudiel/react-qr-scanner'
import CameraIcon from '@/svgs/camera-line.svg'
import { useShowModal } from '@/components/modal'
import { useField } from 'formik'
import { useToast } from '@/components/toast'
import { WalletLimitBanner } from '@/components/banners'
import Plug from '@/svgs/plug.svg'
import { decode } from 'bolt11'
export const getServerSideProps = getGetServerSideProps({ authRequired: true })
export default function Wallet () {
const router = useRouter()
if (router.query.type === 'fund') {
return (
)
} else if (router.query.type?.includes('withdraw')) {
return (
)
} else {
return (
)
}
}
function YouHaveSats () {
const me = useMe()
const limitReached = me?.privates?.sats >= msatsToSats(BALANCE_LIMIT_MSATS)
return (
you have{' '}
{me && (
me.privates?.hideWalletBalance
?
: numWithUnits(me.privates?.sats, { abbreviate: false, format: true })
)}
)
}
function WalletHistory () {
return (
)
}
export function WalletForm () {
return (
)
}
export function FundForm () {
const me = useMe()
const [showAlert, setShowAlert] = useState(true)
const router = useRouter()
const [createInvoice, { called, error }] = useMutation(gql`
mutation createInvoice($amount: Int!) {
createInvoice(amount: $amount) {
id
}
}`)
useEffect(() => {
setShowAlert(!window.localStorage.getItem('hideLnAddrAlert'))
}, [])
if (called && !error) {
return
}
return (
<>
{me && showAlert &&
{
window.localStorage.setItem('hideLnAddrAlert', 'yep')
setShowAlert(false)
}}
>
You can also fund your account via lightning address with {`${me.name}@stacker.news`}
}
>
)
}
export function WithdrawalForm () {
const router = useRouter()
return (
invoice
QR code
lightning address
)
}
export function SelectedWithdrawalForm () {
const router = useRouter()
switch (router.query.type) {
case 'withdraw':
return
case 'lnurl-withdraw':
return
case 'lnaddr-withdraw':
return
}
}
export function InvWithdrawal () {
const router = useRouter()
const me = useMe()
const [createWithdrawl, { called, error }] = useMutation(CREATE_WITHDRAWL)
const maxFeeDefault = me?.privates?.withdrawMaxFeeDefault
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.privates?.sats - maxFeeDefault, 0)
})
const { data } = await createWithdrawl({ variables: { invoice, maxFee: maxFeeDefault } })
router.push(`/withdrawals/${data.createWithdrawl.id}`)
} catch (e) {
console.log(e.message)
}
}
effect()
}, [])
if (called && !error) {
return
}
return (
<>
>
)
}
function InvoiceScanner ({ fieldName }) {
const showModal = useShowModal()
const [,, helpers] = useField(fieldName)
const toaster = useToast()
return (
{
showModal(onClose => {
return (
{
if (result.split('lightning=')[1]) {
helpers.setValue(result.split('lightning=')[1].split(/[&?]/)[0].toLowerCase())
} else if (decode(result.replace(/^lightning:/, ''))) {
helpers.setValue(result.replace(/^lightning:/, '').toLowerCase())
} else {
throw new Error('Not a proper lightning payment request')
}
onClose()
}}
onError={(error) => {
if (error instanceof DOMException) {
console.log(error)
} else {
toaster.danger('qr scan: ' + error?.message || error?.toString?.())
}
onClose()
}}
/>
)
})
}}
>
)
}
function LnQRWith ({ k1, encodedUrl }) {
const router = useRouter()
const query = gql`
{
lnWith(k1: "${k1}") {
withdrawalId
k1
}
}`
const { data } = useQuery(query, SSR ? {} : { pollInterval: FAST_POLL_INTERVAL, nextFetchPolicy: 'cache-and-network' })
if (data?.lnWith?.withdrawalId) {
router.push(`/withdrawals/${data.lnWith.withdrawalId}`)
}
return
}
export function LnWithdrawal () {
// query for challenge
const [createWith, { data, error }] = useMutation(gql`
mutation createWith {
createWith {
k1
encodedUrl
}
}`)
const toaster = useToast()
useEffect(() => {
createWith().catch(e => {
toaster.danger('withdrawal creation: ' + e?.message || e?.toString?.())
})
}, [createWith, toaster])
if (error) return
if (!data) {
return
}
return
}
export function LnAddrWithdrawal () {
const me = useMe()
const router = useRouter()
const [sendToLnAddr, { called, error }] = useMutation(SEND_TO_LNADDR)
const defaultOptions = { min: 1 }
const [addrOptions, setAddrOptions] = useState(defaultOptions)
const [formSchema, setFormSchema] = useState(lnAddrSchema())
const maxFeeDefault = me?.privates?.withdrawMaxFeeDefault
const onAddrChange = useDebounceCallback(async (formik, e) => {
if (!e?.target?.value) {
setAddrOptions(defaultOptions)
setFormSchema(lnAddrSchema())
return
}
let options
try {
options = await lnAddrOptions(e.target.value)
setAddrOptions(options)
setFormSchema(lnAddrSchema(options))
} catch (e) {
console.log(e)
setAddrOptions(defaultOptions)
setFormSchema(lnAddrSchema())
}
}, 500, [setAddrOptions, setFormSchema])
return (
<>
{called && !error && }
>
)
}