* Wallet flow * Prepopulate fields of complementary protocol * Remove TODO about one mutation for save We need to save protocols in separate mutations so we can use the wallet id returned by the first protocol save for the following protocol saves and save them all to the same wallet. * Fix badges not updated on wallet delete * Fix useProtocol call * Fix lightning address save via prompt * Don't pass share as attribute to DOM * Fix useCallback dependency * Progress numbers as SVGs * Fix progress line margins * Remove unused saveWallet arguments * Update cache with settings response * Fix line does not connect with number 1 * Don't reuse page nav arrows in form nav * Fix missing SVG hover style * Fix missing space in wallet save log message * Reuse CSS from nav.module.css * align buttons and their icons/text * center form progress line * increase top padding of form on smaller screens * provide margin above button bar on settings form --------- Co-authored-by: k00b <k00b@stacker.news>
154 lines
5.6 KiB
JavaScript
154 lines
5.6 KiB
JavaScript
import { getGetServerSideProps } from '@/api/ssrApollo'
|
|
import { Button } from 'react-bootstrap'
|
|
import { useWallets, useTemplates, DndProvider, KeyStatus, useWalletsLoading, useKeyError, useWalletsError } from '@/wallets/client/context'
|
|
import { WalletCard, WalletLayout, WalletLayoutHeader, WalletLayoutLink, WalletLayoutSubHeader } from '@/wallets/client/components'
|
|
import styles from '@/styles/wallet.module.css'
|
|
import { usePassphrasePrompt, useShowPassphrase, useSetWalletPriorities } from '@/wallets/client/hooks'
|
|
import { WalletSearch } from '@/wallets/client/components/search'
|
|
import { useMemo, useState } from 'react'
|
|
import { walletDisplayName } from '@/wallets/lib/util'
|
|
import Moon from '@/svgs/moon-fill.svg'
|
|
|
|
export const getServerSideProps = getGetServerSideProps({ authRequired: true })
|
|
|
|
export default function Wallet () {
|
|
const wallets = useWallets()
|
|
const walletsLoading = useWalletsLoading()
|
|
const walletsError = useWalletsError()
|
|
const keyError = useKeyError()
|
|
const [showWallets, setShowWallets] = useState(false)
|
|
const templates = useTemplates()
|
|
const showPassphrase = useShowPassphrase()
|
|
const [showPassphrasePrompt, togglePassphrasePrompt, PassphrasePrompt] = usePassphrasePrompt()
|
|
const setWalletPriorities = useSetWalletPriorities()
|
|
const [searchFilter, setSearchFilter] = useState(() => (text) => true)
|
|
|
|
const { wallets: filteredWallets, templates: filteredTemplates } = useMemo(() => {
|
|
const walletFilter = ({ name }) => searchFilter(walletDisplayName(name)) || searchFilter(name)
|
|
return {
|
|
wallets: wallets.filter(walletFilter),
|
|
templates: templates.filter(walletFilter)
|
|
}
|
|
}, [wallets, templates, searchFilter])
|
|
|
|
if (keyError === KeyStatus.KEY_STORAGE_UNAVAILABLE) {
|
|
return (
|
|
<WalletLayout>
|
|
<div className='py-5 text-center d-flex flex-column align-items-center justify-content-center flex-grow-1'>
|
|
<span className='text-muted fw-bold my-1'>wallets unavailable</span>
|
|
<small className='d-block text-muted'>
|
|
this device does not support storage of cryptographic keys via IndexedDB
|
|
</small>
|
|
</div>
|
|
</WalletLayout>
|
|
)
|
|
}
|
|
|
|
if (keyError === KeyStatus.WRONG_KEY) {
|
|
return showPassphrasePrompt
|
|
? (
|
|
<WalletLayout>
|
|
<div className='py-5 d-flex flex-column align-items-center justify-content-center flex-grow-1 mx-auto' style={{ maxWidth: '500px' }}>
|
|
{PassphrasePrompt}
|
|
</div>
|
|
</WalletLayout>
|
|
)
|
|
: (
|
|
<WalletLayout>
|
|
<div className='py-5 text-center d-flex flex-column align-items-center justify-content-center flex-grow-1'>
|
|
<Button
|
|
onClick={togglePassphrasePrompt}
|
|
size='md' variant='secondary'
|
|
>unlock wallets
|
|
</Button>
|
|
<small className='d-block mt-3 text-muted'>your passphrase is required</small>
|
|
</div>
|
|
</WalletLayout>
|
|
)
|
|
}
|
|
|
|
if (walletsError) {
|
|
return (
|
|
<WalletLayout>
|
|
<div className='py-5 text-center d-flex flex-column align-items-center justify-content-center flex-grow-1'>
|
|
<span className='text-muted fw-bold my-1'>failed to load wallets</span>
|
|
<small className='d-block text-muted'>
|
|
{walletsError.message}
|
|
</small>
|
|
</div>
|
|
</WalletLayout>
|
|
)
|
|
}
|
|
|
|
if (walletsLoading) {
|
|
return (
|
|
<WalletLayout>
|
|
<div className='py-5 text-center d-flex flex-column align-items-center justify-content-center flex-grow-1 text-muted'>
|
|
<Moon className='spin fill-grey' height={28} width={28} />
|
|
<small className='d-block mt-3 text-muted'>loading wallets</small>
|
|
</div>
|
|
</WalletLayout>
|
|
)
|
|
}
|
|
|
|
if (wallets.length === 0 && !showWallets) {
|
|
return (
|
|
<WalletLayout>
|
|
<div className='py-5 text-center d-flex flex-column align-items-center justify-content-center flex-grow-1'>
|
|
<Button
|
|
onClick={() => setShowWallets(true)}
|
|
size='md' variant='secondary'
|
|
>attach wallet
|
|
</Button>
|
|
<small className='d-block mt-3 text-muted'>attach a wallet to send and receive sats</small>
|
|
</div>
|
|
</WalletLayout>
|
|
)
|
|
}
|
|
|
|
return (
|
|
<WalletLayout>
|
|
<div className='py-5'>
|
|
<WalletLayoutHeader>wallets</WalletLayoutHeader>
|
|
<WalletLayoutSubHeader>use real bitcoin</WalletLayoutSubHeader>
|
|
<div className='text-center'>
|
|
<WalletLayoutLink href='/wallets/logs'>wallet logs</WalletLayoutLink>
|
|
{showPassphrase && (
|
|
<>
|
|
<span className='mx-2'>•</span>
|
|
<Button
|
|
variant='link'
|
|
className='text-muted fw-bold text-underline p-0 align-baseline'
|
|
onClick={showPassphrase}
|
|
>
|
|
passphrase
|
|
</Button>
|
|
</>
|
|
)}
|
|
</div>
|
|
<WalletSearch setSearchFilter={setSearchFilter} />
|
|
{filteredWallets.length > 0 && (
|
|
<>
|
|
<DndProvider items={filteredWallets} onReorder={setWalletPriorities}>
|
|
<div className={styles.walletGrid}>
|
|
{filteredWallets.map((wallet, index) => (
|
|
<WalletCard
|
|
key={wallet.id}
|
|
wallet={wallet}
|
|
index={index}
|
|
draggable
|
|
/>
|
|
))}
|
|
</div>
|
|
</DndProvider>
|
|
<div className={styles.separator} />
|
|
</>
|
|
)}
|
|
<div className={styles.walletGrid}>
|
|
{filteredTemplates.map((w, i) => <WalletCard key={i} wallet={w} />)}
|
|
</div>
|
|
</div>
|
|
</WalletLayout>
|
|
)
|
|
}
|