pages load *kazoo*
This commit is contained in:
parent
da020cf899
commit
48640cbed6
|
@ -54,10 +54,9 @@ export default {
|
||||||
}
|
}
|
||||||
|
|
||||||
for (const entry of entries) {
|
for (const entry of entries) {
|
||||||
txs.push(models.vaultEntry.upsert({
|
txs.push(models.vaultEntry.update({
|
||||||
where: { userId: me.id, key: entry.key },
|
where: { id: entry.id },
|
||||||
update: { key: entry.key, value: entry.value },
|
data: { key: entry.key, value: entry.value }
|
||||||
create: { key: entry.key, value: entry.value, userId: me.id, walletId: entry.walletId }
|
|
||||||
}))
|
}))
|
||||||
}
|
}
|
||||||
await models.prisma.$transaction(txs)
|
await models.prisma.$transaction(txs)
|
||||||
|
|
|
@ -19,7 +19,8 @@ import assertApiKeyNotPermitted from './apiKey'
|
||||||
import { bolt11Tags } from '@/lib/bolt11'
|
import { bolt11Tags } from '@/lib/bolt11'
|
||||||
import { finalizeHodlInvoice } from 'worker/wallet'
|
import { finalizeHodlInvoice } from 'worker/wallet'
|
||||||
import walletDefs from 'wallets/server'
|
import walletDefs from 'wallets/server'
|
||||||
import { generateResolverName, generateTypeDefName, isConfigured } from '@/lib/wallet'
|
import { generateResolverName, generateTypeDefName } from '@/wallets/graphql'
|
||||||
|
import { isConfigured } from '@/wallets/common'
|
||||||
import { lnAddrOptions } from '@/lib/lnurl'
|
import { lnAddrOptions } from '@/lib/lnurl'
|
||||||
import { GqlAuthenticationError, GqlAuthorizationError, GqlInputError } from '@/lib/error'
|
import { GqlAuthenticationError, GqlAuthorizationError, GqlInputError } from '@/lib/error'
|
||||||
import { getNodeSockets, getOurPubkey } from '../lnd'
|
import { getNodeSockets, getOurPubkey } from '../lnd'
|
||||||
|
|
|
@ -1,6 +1,6 @@
|
||||||
import { gql } from 'graphql-tag'
|
import { gql } from 'graphql-tag'
|
||||||
import { fieldToGqlArg, fieldToGqlArgOptional, generateResolverName, generateTypeDefName, isServerField } from '@/lib/wallet'
|
import { fieldToGqlArg, fieldToGqlArgOptional, generateResolverName, generateTypeDefName } from '@/wallets/graphql'
|
||||||
|
import { isServerField } from '@/wallets/common'
|
||||||
import walletDefs from 'wallets/server'
|
import walletDefs from 'wallets/server'
|
||||||
|
|
||||||
function injectTypeDefs (typeDefs) {
|
function injectTypeDefs (typeDefs) {
|
||||||
|
|
|
@ -5,6 +5,7 @@ import { useEffect, useState } from 'react'
|
||||||
import { isNumber } from '@/lib/validate'
|
import { isNumber } from '@/lib/validate'
|
||||||
import { useIsClient } from './use-client'
|
import { useIsClient } from './use-client'
|
||||||
import Link from 'next/link'
|
import Link from 'next/link'
|
||||||
|
import { isConfigured } from '@/wallets/common'
|
||||||
|
|
||||||
function autoWithdrawThreshold ({ me }) {
|
function autoWithdrawThreshold ({ me }) {
|
||||||
return isNumber(me?.privates?.autoWithdrawThreshold) ? me?.privates?.autoWithdrawThreshold : 10000
|
return isNumber(me?.privates?.autoWithdrawThreshold) ? me?.privates?.autoWithdrawThreshold : 10000
|
||||||
|
@ -33,7 +34,7 @@ export function AutowithdrawSettings ({ wallet }) {
|
||||||
return (
|
return (
|
||||||
<>
|
<>
|
||||||
<Checkbox
|
<Checkbox
|
||||||
disabled={isClient && !wallet.isConfigured}
|
disabled={isClient && !isConfigured(wallet)}
|
||||||
label='enabled'
|
label='enabled'
|
||||||
id='enabled'
|
id='enabled'
|
||||||
name='enabled'
|
name='enabled'
|
||||||
|
|
|
@ -1,7 +1,7 @@
|
||||||
import { useCallback, useEffect, useState } from 'react'
|
import { useCallback, useEffect, useState } from 'react'
|
||||||
import { useMe } from './me'
|
import { useMe } from './me'
|
||||||
import { useShowModal } from './modal'
|
import { useShowModal } from './modal'
|
||||||
import useVault, { useVaultConfigurator, useVaultMigration } from './vault/use-vault'
|
import { useVaultConfigurator } from './vault/use-vault-configurator'
|
||||||
import { Button, InputGroup } from 'react-bootstrap'
|
import { Button, InputGroup } from 'react-bootstrap'
|
||||||
import { Form, Input, PasswordInput, SubmitButton } from './form'
|
import { Form, Input, PasswordInput, SubmitButton } from './form'
|
||||||
import bip39Words from '@/lib/bip39-words'
|
import bip39Words from '@/lib/bip39-words'
|
||||||
|
@ -21,9 +21,6 @@ export default function DeviceSync () {
|
||||||
const enabled = !!me?.privates?.vaultKeyHash
|
const enabled = !!me?.privates?.vaultKeyHash
|
||||||
const connected = !!value?.key
|
const connected = !!value?.key
|
||||||
|
|
||||||
const migrate = useVaultMigration()
|
|
||||||
const [debugValue, setDebugValue, clearValue] = useVault(me, 'debug')
|
|
||||||
|
|
||||||
const manage = useCallback(async () => {
|
const manage = useCallback(async () => {
|
||||||
if (enabled && connected) {
|
if (enabled && connected) {
|
||||||
showModal((onClose) => (
|
showModal((onClose) => (
|
||||||
|
@ -55,7 +52,7 @@ export default function DeviceSync () {
|
||||||
<ConnectForm onClose={onClose} onConnect={onConnect} enabled={enabled} />
|
<ConnectForm onClose={onClose} onConnect={onConnect} enabled={enabled} />
|
||||||
))
|
))
|
||||||
}
|
}
|
||||||
}, [migrate, enabled, connected, value])
|
}, [enabled, connected, value])
|
||||||
|
|
||||||
const reset = useCallback(async () => {
|
const reset = useCallback(async () => {
|
||||||
const schema = yup.object().shape({
|
const schema = yup.object().shape({
|
||||||
|
@ -106,7 +103,6 @@ export default function DeviceSync () {
|
||||||
if (values.passphrase) {
|
if (values.passphrase) {
|
||||||
try {
|
try {
|
||||||
await setVaultKey(values.passphrase)
|
await setVaultKey(values.passphrase)
|
||||||
await migrate()
|
|
||||||
apollo.cache.evict({ fieldName: 'BestWallets' })
|
apollo.cache.evict({ fieldName: 'BestWallets' })
|
||||||
apollo.cache.gc()
|
apollo.cache.gc()
|
||||||
await apollo.refetchQueries({ include: ['BestWallets'] })
|
await apollo.refetchQueries({ include: ['BestWallets'] })
|
||||||
|
@ -115,7 +111,7 @@ export default function DeviceSync () {
|
||||||
throw e
|
throw e
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}, [setVaultKey, migrate])
|
}, [setVaultKey])
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<>
|
<>
|
||||||
|
@ -139,33 +135,6 @@ export default function DeviceSync () {
|
||||||
</p>
|
</p>
|
||||||
</Info>
|
</Info>
|
||||||
</div>
|
</div>
|
||||||
<Button
|
|
||||||
onClick={async () => {
|
|
||||||
try {
|
|
||||||
const v = window.prompt('Enter debug value', debugValue)
|
|
||||||
|
|
||||||
await setDebugValue(v)
|
|
||||||
} catch (e) {
|
|
||||||
console.error(e)
|
|
||||||
}
|
|
||||||
}}
|
|
||||||
>
|
|
||||||
Set value
|
|
||||||
</Button>
|
|
||||||
<Button
|
|
||||||
onClick={() => {
|
|
||||||
clearValue()
|
|
||||||
}}
|
|
||||||
>
|
|
||||||
Clear value
|
|
||||||
</Button>
|
|
||||||
<Button
|
|
||||||
onClick={() => {
|
|
||||||
window.alert(debugValue)
|
|
||||||
}}
|
|
||||||
>
|
|
||||||
Show value
|
|
||||||
</Button>
|
|
||||||
{enabled && !connected && (
|
{enabled && !connected && (
|
||||||
<div className='mt-2 d-flex align-items-center'>
|
<div className='mt-2 d-flex align-items-center'>
|
||||||
<div>
|
<div>
|
||||||
|
|
|
@ -13,7 +13,7 @@ import { usePaidMutation } from './use-paid-mutation'
|
||||||
import { ACT_MUTATION } from '@/fragments/paidAction'
|
import { ACT_MUTATION } from '@/fragments/paidAction'
|
||||||
import { meAnonSats } from '@/lib/apollo'
|
import { meAnonSats } from '@/lib/apollo'
|
||||||
import { BoostItemInput } from './adv-post-form'
|
import { BoostItemInput } from './adv-post-form'
|
||||||
import { useWallet } from '../wallets/common'
|
import { useWallet } from '@/wallets/index'
|
||||||
|
|
||||||
const defaultTips = [100, 1000, 10_000, 100_000]
|
const defaultTips = [100, 1000, 10_000, 100_000]
|
||||||
|
|
||||||
|
|
|
@ -22,10 +22,9 @@ import SearchIcon from '../../svgs/search-line.svg'
|
||||||
import classNames from 'classnames'
|
import classNames from 'classnames'
|
||||||
import SnIcon from '@/svgs/sn.svg'
|
import SnIcon from '@/svgs/sn.svg'
|
||||||
import { useHasNewNotes } from '../use-has-new-notes'
|
import { useHasNewNotes } from '../use-has-new-notes'
|
||||||
import { useWallets } from '@/wallets/common'
|
import { useWallets } from '@/wallets/index'
|
||||||
import SwitchAccountList, { useAccounts } from '@/components/account'
|
import SwitchAccountList, { useAccounts } from '@/components/account'
|
||||||
import { useShowModal } from '@/components/modal'
|
import { useShowModal } from '@/components/modal'
|
||||||
import { unsetLocalKey as resetVaultKey } from '@/components/use-vault'
|
|
||||||
|
|
||||||
export function Brand ({ className }) {
|
export function Brand ({ className }) {
|
||||||
return (
|
return (
|
||||||
|
@ -266,7 +265,6 @@ function LogoutObstacle ({ onClose }) {
|
||||||
const { registration: swRegistration, togglePushSubscription } = useServiceWorker()
|
const { registration: swRegistration, togglePushSubscription } = useServiceWorker()
|
||||||
const wallets = useWallets()
|
const wallets = useWallets()
|
||||||
const { multiAuthSignout } = useAccounts()
|
const { multiAuthSignout } = useAccounts()
|
||||||
const { me } = useMe()
|
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div className='d-flex m-auto flex-column w-fit-content'>
|
<div className='d-flex m-auto flex-column w-fit-content'>
|
||||||
|
@ -295,7 +293,6 @@ function LogoutObstacle ({ onClose }) {
|
||||||
}
|
}
|
||||||
|
|
||||||
await wallets.resetClient().catch(console.error)
|
await wallets.resetClient().catch(console.error)
|
||||||
await resetVaultKey(me?.id)
|
|
||||||
|
|
||||||
await signOut({ callbackUrl: '/' })
|
await signOut({ callbackUrl: '/' })
|
||||||
}}
|
}}
|
||||||
|
|
|
@ -1,35 +1,13 @@
|
||||||
import { useCallback, useMemo } from 'react'
|
import { useCallback, useMemo } from 'react'
|
||||||
import { useMe } from './me'
|
import { useMe } from './me'
|
||||||
import { gql, useApolloClient, useMutation } from '@apollo/client'
|
import { gql, useApolloClient, useMutation } from '@apollo/client'
|
||||||
import { useWallet } from '@/wallets/common'
|
import { useWallet } from '@/wallets/index'
|
||||||
import { FAST_POLL_INTERVAL, JIT_INVOICE_TIMEOUT_MS } from '@/lib/constants'
|
import { FAST_POLL_INTERVAL, JIT_INVOICE_TIMEOUT_MS } from '@/lib/constants'
|
||||||
import { INVOICE } from '@/fragments/wallet'
|
import { INVOICE } from '@/fragments/wallet'
|
||||||
import Invoice from '@/components/invoice'
|
import Invoice from '@/components/invoice'
|
||||||
import { useFeeButton } from './fee-button'
|
import { useFeeButton } from './fee-button'
|
||||||
import { useShowModal } from './modal'
|
import { useShowModal } from './modal'
|
||||||
|
import { InvoiceCanceledError, NoAttachedWalletError, InvoiceExpiredError } from '@/wallets/errors'
|
||||||
export class InvoiceCanceledError extends Error {
|
|
||||||
constructor (hash, actionError) {
|
|
||||||
super(actionError ?? `invoice canceled: ${hash}`)
|
|
||||||
this.name = 'InvoiceCanceledError'
|
|
||||||
this.hash = hash
|
|
||||||
this.actionError = actionError
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
export class NoAttachedWalletError extends Error {
|
|
||||||
constructor () {
|
|
||||||
super('no attached wallet found')
|
|
||||||
this.name = 'NoAttachedWalletError'
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
export class InvoiceExpiredError extends Error {
|
|
||||||
constructor (hash) {
|
|
||||||
super(`invoice expired: ${hash}`)
|
|
||||||
this.name = 'InvoiceExpiredError'
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
export const useInvoice = () => {
|
export const useInvoice = () => {
|
||||||
const client = useApolloClient()
|
const client = useApolloClient()
|
||||||
|
|
|
@ -2,7 +2,7 @@ import { QRCodeSVG } from 'qrcode.react'
|
||||||
import { CopyInput, InputSkeleton } from './form'
|
import { CopyInput, InputSkeleton } from './form'
|
||||||
import InvoiceStatus from './invoice-status'
|
import InvoiceStatus from './invoice-status'
|
||||||
import { useEffect } from 'react'
|
import { useEffect } from 'react'
|
||||||
import { useWallet } from '@/wallets/common'
|
import { useWallet } from '@/wallets/index'
|
||||||
import Bolt11Info from './bolt11-info'
|
import Bolt11Info from './bolt11-info'
|
||||||
|
|
||||||
export default function Qr ({ asIs, value, useWallet: automated, statusVariant, description, status }) {
|
export default function Qr ({ asIs, value, useWallet: automated, statusVariant, description, status }) {
|
||||||
|
|
|
@ -1,7 +1,7 @@
|
||||||
import { useState, useEffect, useCallback, useRef } from 'react'
|
import { useState, useEffect, useCallback, useRef } from 'react'
|
||||||
|
|
||||||
export function getDbName (userId) {
|
export function getDbName (userId, name) {
|
||||||
return `app:storage${userId ? `:${userId}` : ''}`
|
return `app:storage:${userId ?? ''}${name ? `:${name}` : ''}`
|
||||||
}
|
}
|
||||||
|
|
||||||
function useIndexedDB ({ dbName, storeName, options = { keyPath: 'id', autoIncrement: true }, indices = [], version = 1 }) {
|
function useIndexedDB ({ dbName, storeName, options = { keyPath: 'id', autoIncrement: true }, indices = [], version = 1 }) {
|
||||||
|
|
|
@ -1,11 +1,10 @@
|
||||||
import { UPDATE_VAULT_KEY } from '@/fragments/users'
|
|
||||||
import { useMutation, useQuery } from '@apollo/client'
|
import { useMutation, useQuery } from '@apollo/client'
|
||||||
import { useMe } from '../me'
|
import { useMe } from '../me'
|
||||||
import { useToast } from '../toast'
|
import { useToast } from '../toast'
|
||||||
import useIndexedDB, { getDbName } from '../use-indexeddb'
|
import useIndexedDB, { getDbName } from '../use-indexeddb'
|
||||||
import { useCallback, useEffect, useState } from 'react'
|
import { useCallback, useEffect, useState } from 'react'
|
||||||
import { E_VAULT_KEY_EXISTS } from '@/lib/error'
|
import { E_VAULT_KEY_EXISTS } from '@/lib/error'
|
||||||
import { CLEAR_VAULT, GET_VAULT_ENTRIES } from '@/fragments/vault'
|
import { CLEAR_VAULT, GET_VAULT_ENTRIES, UPDATE_VAULT_KEY } from '@/fragments/vault'
|
||||||
import { toHex } from '@/lib/hex'
|
import { toHex } from '@/lib/hex'
|
||||||
import { decryptData, encryptData } from './use-vault'
|
import { decryptData, encryptData } from './use-vault'
|
||||||
|
|
||||||
|
@ -22,7 +21,7 @@ const useImperativeQuery = (query) => {
|
||||||
export function useVaultConfigurator () {
|
export function useVaultConfigurator () {
|
||||||
const { me } = useMe()
|
const { me } = useMe()
|
||||||
const toaster = useToast()
|
const toaster = useToast()
|
||||||
const { set, get, remove } = useIndexedDB({ dbName: getDbName(me?.id), storeName: 'vault' })
|
const { set, get, remove } = useIndexedDB({ dbName: getDbName(me?.id, 'vault'), storeName: 'vault' })
|
||||||
const [updateVaultKey] = useMutation(UPDATE_VAULT_KEY)
|
const [updateVaultKey] = useMutation(UPDATE_VAULT_KEY)
|
||||||
const getVaultEntries = useImperativeQuery(GET_VAULT_ENTRIES)
|
const getVaultEntries = useImperativeQuery(GET_VAULT_ENTRIES)
|
||||||
const [key, setKey] = useState(null)
|
const [key, setKey] = useState(null)
|
||||||
|
@ -44,10 +43,10 @@ export function useVaultConfigurator () {
|
||||||
}
|
}
|
||||||
setKey(localVaultKey)
|
setKey(localVaultKey)
|
||||||
} catch (e) {
|
} catch (e) {
|
||||||
toaster.danger('error loading vault configuration ' + e.message)
|
// toaster?.danger('error loading vault configuration ' + e.message)
|
||||||
}
|
}
|
||||||
})()
|
})()
|
||||||
}, [me?.privates?.vaultKeyHash, keyHash, get, remove])
|
}, [me?.privates?.vaultKeyHash, keyHash, get, remove, toaster])
|
||||||
|
|
||||||
// clear vault: remove everything and reset the key
|
// clear vault: remove everything and reset the key
|
||||||
const [clearVault] = useMutation(CLEAR_VAULT, {
|
const [clearVault] = useMutation(CLEAR_VAULT, {
|
||||||
|
|
|
@ -1,6 +1,7 @@
|
||||||
import { Button } from 'react-bootstrap'
|
import { Button } from 'react-bootstrap'
|
||||||
import CancelButton from './cancel-button'
|
import CancelButton from './cancel-button'
|
||||||
import { SubmitButton } from './form'
|
import { SubmitButton } from './form'
|
||||||
|
import { isConfigured } from '@/wallets/common'
|
||||||
|
|
||||||
export default function WalletButtonBar ({
|
export default function WalletButtonBar ({
|
||||||
wallet, disable,
|
wallet, disable,
|
||||||
|
@ -10,12 +11,12 @@ export default function WalletButtonBar ({
|
||||||
return (
|
return (
|
||||||
<div className={`mt-3 ${className}`}>
|
<div className={`mt-3 ${className}`}>
|
||||||
<div className='d-flex justify-content-between'>
|
<div className='d-flex justify-content-between'>
|
||||||
{wallet.hasConfig && wallet.isConfigured &&
|
{isConfigured(wallet) &&
|
||||||
<Button onClick={onDelete} variant='grey-medium'>{deleteText}</Button>}
|
<Button onClick={onDelete} variant='grey-medium'>{deleteText}</Button>}
|
||||||
{children}
|
{children}
|
||||||
<div className='d-flex align-items-center ms-auto'>
|
<div className='d-flex align-items-center ms-auto'>
|
||||||
{hasCancel && <CancelButton onClick={onCancel} />}
|
{hasCancel && <CancelButton onClick={onCancel} />}
|
||||||
<SubmitButton variant='primary' disabled={disable}>{wallet.isConfigured ? editText : createText}</SubmitButton>
|
<SubmitButton variant='primary' disabled={disable}>{isConfigured(wallet) ? editText : createText}</SubmitButton>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
|
@ -3,26 +3,18 @@ import styles from '@/styles/wallet.module.css'
|
||||||
import Plug from '@/svgs/plug.svg'
|
import Plug from '@/svgs/plug.svg'
|
||||||
import Gear from '@/svgs/settings-5-fill.svg'
|
import Gear from '@/svgs/settings-5-fill.svg'
|
||||||
import Link from 'next/link'
|
import Link from 'next/link'
|
||||||
import { Status } from '@/wallets/common'
|
import { Status, isConfigured } from '@/wallets/common'
|
||||||
import DraggableIcon from '@/svgs/draggable.svg'
|
import DraggableIcon from '@/svgs/draggable.svg'
|
||||||
|
|
||||||
export default function WalletCard ({ wallet, draggable, onDragStart, onDragEnter, onDragEnd, onTouchStart, sourceIndex, targetIndex, index }) {
|
export default function WalletCard ({ wallet, draggable, onDragStart, onDragEnter, onDragEnd, onTouchStart, sourceIndex, targetIndex, index }) {
|
||||||
const { card: { title, badges } } = wallet
|
const { card: { title, badges } } = wallet.def
|
||||||
|
|
||||||
let indicator = styles.disabled
|
let indicator = styles.disabled
|
||||||
switch (wallet.status) {
|
switch (wallet.status) {
|
||||||
case Status.Enabled:
|
case Status.Enabled:
|
||||||
case true:
|
|
||||||
indicator = styles.success
|
indicator = styles.success
|
||||||
break
|
break
|
||||||
case Status.Locked:
|
default:
|
||||||
indicator = styles.warning
|
|
||||||
break
|
|
||||||
case Status.Error:
|
|
||||||
indicator = styles.error
|
|
||||||
break
|
|
||||||
case Status.Initialized:
|
|
||||||
case false:
|
|
||||||
indicator = styles.disabled
|
indicator = styles.disabled
|
||||||
break
|
break
|
||||||
}
|
}
|
||||||
|
@ -57,9 +49,9 @@ export default function WalletCard ({ wallet, draggable, onDragStart, onDragEnte
|
||||||
</Badge>)}
|
</Badge>)}
|
||||||
</Card.Subtitle>
|
</Card.Subtitle>
|
||||||
</Card.Body>
|
</Card.Body>
|
||||||
<Link href={`/settings/wallets/${wallet.name}`}>
|
<Link href={`/settings/wallets/${wallet.def.name}`}>
|
||||||
<Card.Footer className={styles.attach}>
|
<Card.Footer className={styles.attach}>
|
||||||
{wallet.isConfigured
|
{isConfigured(wallet)
|
||||||
? <>configure<Gear width={14} height={14} /></>
|
? <>configure<Gear width={14} height={14} /></>
|
||||||
: <>attach<Plug width={14} height={14} /></>}
|
: <>attach<Plug width={14} height={14} /></>}
|
||||||
</Card.Footer>
|
</Card.Footer>
|
||||||
|
|
|
@ -86,10 +86,14 @@ const INDICES = [
|
||||||
{ name: 'wallet_ts', keyPath: ['wallet', 'ts'] }
|
{ name: 'wallet_ts', keyPath: ['wallet', 'ts'] }
|
||||||
]
|
]
|
||||||
|
|
||||||
|
function getWalletLogDbName (userId) {
|
||||||
|
return getDbName(userId)
|
||||||
|
}
|
||||||
|
|
||||||
function useWalletLogDB () {
|
function useWalletLogDB () {
|
||||||
const { me } = useMe()
|
const { me } = useMe()
|
||||||
const { add, getPage, clear, error, notSupported } = useIndexedDB({
|
const { add, getPage, clear, error, notSupported } = useIndexedDB({
|
||||||
dbName: getDbName(me?.id),
|
dbName: getWalletLogDbName(me?.id),
|
||||||
storeName: 'wallet_logs',
|
storeName: 'wallet_logs',
|
||||||
indices: INDICES
|
indices: INDICES
|
||||||
})
|
})
|
||||||
|
@ -127,7 +131,7 @@ export function useWalletLogger (wallet, setLogs) {
|
||||||
)
|
)
|
||||||
|
|
||||||
const deleteLogs = useCallback(async (wallet, options) => {
|
const deleteLogs = useCallback(async (wallet, options) => {
|
||||||
if ((!wallet || wallet.walletType) && !options?.clientOnly) {
|
if ((!wallet || wallet.def.walletType) && !options?.clientOnly) {
|
||||||
await deleteServerWalletLogs({ variables: { wallet: wallet?.walletType } })
|
await deleteServerWalletLogs({ variables: { wallet: wallet?.walletType } })
|
||||||
}
|
}
|
||||||
if (!wallet || wallet.sendPayment) {
|
if (!wallet || wallet.sendPayment) {
|
||||||
|
@ -190,7 +194,7 @@ export function useWalletLogs (wallet, initialPage = 1, logsPerPage = 10) {
|
||||||
|
|
||||||
result = await getPage(page, pageSize, indexName, query, 'prev')
|
result = await getPage(page, pageSize, indexName, query, 'prev')
|
||||||
// no walletType means we're using the local IDB
|
// no walletType means we're using the local IDB
|
||||||
if (wallet && !wallet.walletType) {
|
if (wallet && !wallet.def.walletType) {
|
||||||
return result
|
return result
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -22,7 +22,7 @@ import dynamic from 'next/dynamic'
|
||||||
import { HasNewNotesProvider } from '@/components/use-has-new-notes'
|
import { HasNewNotesProvider } from '@/components/use-has-new-notes'
|
||||||
import { WebLnProvider } from '@/wallets/webln/client'
|
import { WebLnProvider } from '@/wallets/webln/client'
|
||||||
import { AccountProvider } from '@/components/account'
|
import { AccountProvider } from '@/components/account'
|
||||||
import { WalletProvider } from '@/wallets/common'
|
import { WalletsProvider } from '@/wallets/index'
|
||||||
|
|
||||||
const PWAPrompt = dynamic(() => import('react-ios-pwa-prompt'), { ssr: false })
|
const PWAPrompt = dynamic(() => import('react-ios-pwa-prompt'), { ssr: false })
|
||||||
|
|
||||||
|
@ -105,7 +105,7 @@ export default function MyApp ({ Component, pageProps: { ...props } }) {
|
||||||
<PlausibleProvider domain='stacker.news' trackOutboundLinks>
|
<PlausibleProvider domain='stacker.news' trackOutboundLinks>
|
||||||
<ApolloProvider client={client}>
|
<ApolloProvider client={client}>
|
||||||
<MeProvider me={me}>
|
<MeProvider me={me}>
|
||||||
<WalletProvider>
|
<WalletsProvider>
|
||||||
<HasNewNotesProvider>
|
<HasNewNotesProvider>
|
||||||
<LoggerProvider>
|
<LoggerProvider>
|
||||||
<WebLnProvider>
|
<WebLnProvider>
|
||||||
|
@ -132,7 +132,7 @@ export default function MyApp ({ Component, pageProps: { ...props } }) {
|
||||||
</WebLnProvider>
|
</WebLnProvider>
|
||||||
</LoggerProvider>
|
</LoggerProvider>
|
||||||
</HasNewNotesProvider>
|
</HasNewNotesProvider>
|
||||||
</WalletProvider>
|
</WalletsProvider>
|
||||||
</MeProvider>
|
</MeProvider>
|
||||||
</ApolloProvider>
|
</ApolloProvider>
|
||||||
</PlausibleProvider>
|
</PlausibleProvider>
|
||||||
|
|
|
@ -5,14 +5,13 @@ import { WalletSecurityBanner } from '@/components/banners'
|
||||||
import { WalletLogs } from '@/components/wallet-logger'
|
import { WalletLogs } from '@/components/wallet-logger'
|
||||||
import { useToast } from '@/components/toast'
|
import { useToast } from '@/components/toast'
|
||||||
import { useRouter } from 'next/router'
|
import { useRouter } from 'next/router'
|
||||||
import { useWallet } from '@/wallets/common'
|
import { useWallet } from '@/wallets/index'
|
||||||
import Info from '@/components/info'
|
import Info from '@/components/info'
|
||||||
import Text from '@/components/text'
|
import Text from '@/components/text'
|
||||||
import { AutowithdrawSettings } from '@/components/autowithdraw-shared'
|
import { AutowithdrawSettings } from '@/components/autowithdraw-shared'
|
||||||
import dynamic from 'next/dynamic'
|
import { isConfigured } from '@/wallets/common'
|
||||||
import { useIsClient } from '@/components/use-client'
|
import { SSR } from '@/lib/constants'
|
||||||
|
import WalletButtonBar from '@/components/wallet-buttonbar'
|
||||||
const WalletButtonBar = dynamic(() => import('@/components/wallet-buttonbar.js'), { ssr: false })
|
|
||||||
|
|
||||||
export const getServerSideProps = getGetServerSideProps({ authRequired: true })
|
export const getServerSideProps = getGetServerSideProps({ authRequired: true })
|
||||||
|
|
||||||
|
@ -22,7 +21,7 @@ export default function WalletSettings () {
|
||||||
const { wallet: name } = router.query
|
const { wallet: name } = router.query
|
||||||
const wallet = useWallet(name)
|
const wallet = useWallet(name)
|
||||||
|
|
||||||
const initial = wallet?.fields.reduce((acc, field) => {
|
const initial = wallet?.def.fields.reduce((acc, field) => {
|
||||||
// We still need to run over all wallet fields via reduce
|
// We still need to run over all wallet fields via reduce
|
||||||
// even though we use wallet.config as the initial value
|
// even though we use wallet.config as the initial value
|
||||||
// since wallet.config is empty when wallet is not configured.
|
// since wallet.config is empty when wallet is not configured.
|
||||||
|
@ -41,8 +40,8 @@ export default function WalletSettings () {
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<CenterLayout>
|
<CenterLayout>
|
||||||
<h2 className='pb-2'>{wallet?.card?.title}</h2>
|
<h2 className='pb-2'>{wallet?.def.card.title}</h2>
|
||||||
<h6 className='text-muted text-center pb-3'><Text>{wallet?.card?.subtitle}</Text></h6>
|
<h6 className='text-muted text-center pb-3'><Text>{wallet?.def.card.subtitle}</Text></h6>
|
||||||
{wallet?.canSend && wallet?.hasConfig > 0 && <WalletSecurityBanner />}
|
{wallet?.canSend && wallet?.hasConfig > 0 && <WalletSecurityBanner />}
|
||||||
<Form
|
<Form
|
||||||
initial={initial}
|
initial={initial}
|
||||||
|
@ -50,7 +49,7 @@ export default function WalletSettings () {
|
||||||
{...validateProps}
|
{...validateProps}
|
||||||
onSubmit={async ({ amount, ...values }) => {
|
onSubmit={async ({ amount, ...values }) => {
|
||||||
try {
|
try {
|
||||||
const newConfig = !wallet?.isConfigured
|
const newConfig = !isConfigured(wallet)
|
||||||
|
|
||||||
// enable wallet if wallet was just configured
|
// enable wallet if wallet was just configured
|
||||||
if (newConfig) {
|
if (newConfig) {
|
||||||
|
@ -68,11 +67,11 @@ export default function WalletSettings () {
|
||||||
}}
|
}}
|
||||||
>
|
>
|
||||||
{wallet && <WalletFields wallet={wallet} />}
|
{wallet && <WalletFields wallet={wallet} />}
|
||||||
{wallet?.clientOnly
|
{wallet?.def.clientOnly
|
||||||
? (
|
? (
|
||||||
<CheckboxGroup name='enabled'>
|
<CheckboxGroup name='enabled'>
|
||||||
<Checkbox
|
<Checkbox
|
||||||
disabled={!wallet?.isConfigured}
|
disabled={!isConfigured(wallet)}
|
||||||
label='enabled'
|
label='enabled'
|
||||||
name='enabled'
|
name='enabled'
|
||||||
groupClassName='mb-0'
|
groupClassName='mb-0'
|
||||||
|
@ -101,16 +100,16 @@ export default function WalletSettings () {
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
function WalletFields ({ wallet: { config, fields, isConfigured } }) {
|
function WalletFields ({ wallet }) {
|
||||||
const isClient = useIsClient()
|
console.log('wallet', wallet)
|
||||||
|
|
||||||
return fields
|
return wallet.def.fields
|
||||||
.map(({ name, label = '', type, help, optional, editable, clientOnly, serverOnly, ...props }, i) => {
|
.map(({ name, label = '', type, help, optional, editable, clientOnly, serverOnly, ...props }, i) => {
|
||||||
const rawProps = {
|
const rawProps = {
|
||||||
...props,
|
...props,
|
||||||
name,
|
name,
|
||||||
initialValue: config?.[name],
|
initialValue: wallet.config?.[name],
|
||||||
readOnly: isClient && isConfigured && editable === false && !!config?.[name],
|
readOnly: !SSR && isConfigured(wallet) && editable === false && !!wallet.config?.[name],
|
||||||
groupClassName: props.hidden ? 'd-none' : undefined,
|
groupClassName: props.hidden ? 'd-none' : undefined,
|
||||||
label: label
|
label: label
|
||||||
? (
|
? (
|
||||||
|
|
|
@ -2,12 +2,10 @@ import { getGetServerSideProps } from '@/api/ssrApollo'
|
||||||
import Layout from '@/components/layout'
|
import Layout from '@/components/layout'
|
||||||
import styles from '@/styles/wallet.module.css'
|
import styles from '@/styles/wallet.module.css'
|
||||||
import Link from 'next/link'
|
import Link from 'next/link'
|
||||||
import { useWallets, walletPrioritySort } from '@/wallets/common'
|
import { useWallets } from '@/wallets/index'
|
||||||
import { useState } from 'react'
|
import { useState } from 'react'
|
||||||
import dynamic from 'next/dynamic'
|
|
||||||
import { useIsClient } from '@/components/use-client'
|
import { useIsClient } from '@/components/use-client'
|
||||||
|
import WalletCard from '@/components/wallet-card'
|
||||||
const WalletCard = dynamic(() => import('@/components/wallet-card'), { ssr: false })
|
|
||||||
|
|
||||||
export const getServerSideProps = getGetServerSideProps({ authRequired: true })
|
export const getServerSideProps = getGetServerSideProps({ authRequired: true })
|
||||||
|
|
||||||
|
@ -28,7 +26,7 @@ async function reorder (wallets, sourceIndex, targetIndex) {
|
||||||
}
|
}
|
||||||
|
|
||||||
export default function Wallet ({ ssrData }) {
|
export default function Wallet ({ ssrData }) {
|
||||||
const { wallets } = useWallets()
|
const wallets = useWallets()
|
||||||
|
|
||||||
const isClient = useIsClient()
|
const isClient = useIsClient()
|
||||||
const [sourceIndex, setSourceIndex] = useState(null)
|
const [sourceIndex, setSourceIndex] = useState(null)
|
||||||
|
@ -76,49 +74,33 @@ export default function Wallet ({ ssrData }) {
|
||||||
</Link>
|
</Link>
|
||||||
</div>
|
</div>
|
||||||
<div className={styles.walletGrid} onDragEnd={onDragEnd}>
|
<div className={styles.walletGrid} onDragEnd={onDragEnd}>
|
||||||
{wallets
|
{wallets.map((w, i) => {
|
||||||
.sort((w1, w2) => {
|
const draggable = isClient && w.config?.enabled
|
||||||
// enabled/configured wallets always come before disabled/unconfigured wallets
|
|
||||||
if ((w1.enabled && !w2.enabled) || (w1.isConfigured && !w2.isConfigured)) {
|
|
||||||
return -1
|
|
||||||
} else if ((w2.enabled && !w1.enabled) || (w2.isConfigured && !w1.isConfigured)) {
|
|
||||||
return 1
|
|
||||||
}
|
|
||||||
|
|
||||||
return walletPrioritySort(w1, w2)
|
return (
|
||||||
})
|
<div
|
||||||
.map((w, i) => {
|
key={w.def.name}
|
||||||
const draggable = isClient && w.enabled
|
className={
|
||||||
|
|
||||||
return (
|
|
||||||
<div
|
|
||||||
key={w?.name}
|
|
||||||
draggable={draggable}
|
|
||||||
style={{ cursor: draggable ? 'move' : 'default' }}
|
|
||||||
onDragStart={draggable ? onDragStart(i) : undefined}
|
|
||||||
onTouchStart={draggable ? onTouchStart(i) : undefined}
|
|
||||||
onDragEnter={draggable ? onDragEnter(i) : undefined}
|
|
||||||
className={
|
|
||||||
!draggable
|
!draggable
|
||||||
? ''
|
? ''
|
||||||
: (`${sourceIndex === i ? styles.drag : ''} ${draggable && targetIndex === i ? styles.drop : ''}`)
|
: (`${sourceIndex === i ? styles.drag : ''} ${draggable && targetIndex === i ? styles.drop : ''}`)
|
||||||
}
|
}
|
||||||
suppressHydrationWarning
|
suppressHydrationWarning
|
||||||
>
|
>
|
||||||
<WalletCard
|
<WalletCard
|
||||||
wallet={w}
|
wallet={w}
|
||||||
draggable={draggable}
|
draggable={draggable}
|
||||||
onDragStart={draggable ? onDragStart(i) : undefined}
|
onDragStart={draggable ? onDragStart(i) : undefined}
|
||||||
onTouchStart={draggable ? onTouchStart(i) : undefined}
|
onTouchStart={draggable ? onTouchStart(i) : undefined}
|
||||||
onDragEnter={draggable ? onDragEnter(i) : undefined}
|
onDragEnter={draggable ? onDragEnter(i) : undefined}
|
||||||
sourceIndex={sourceIndex}
|
sourceIndex={sourceIndex}
|
||||||
targetIndex={targetIndex}
|
targetIndex={targetIndex}
|
||||||
index={i}
|
index={i}
|
||||||
/>
|
/>
|
||||||
</div>
|
</div>
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
)}
|
)}
|
||||||
|
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
|
@ -1,10 +1,8 @@
|
||||||
import walletDefs from 'wallets/client'
|
import walletDefs from 'wallets/client'
|
||||||
|
|
||||||
export const Status = {
|
export const Status = {
|
||||||
Initialized: 'Initialized',
|
|
||||||
Enabled: 'Enabled',
|
Enabled: 'Enabled',
|
||||||
Locked: 'Locked',
|
Disabled: 'Disabled'
|
||||||
Error: 'Error'
|
|
||||||
}
|
}
|
||||||
|
|
||||||
export function getWalletByName (name) {
|
export function getWalletByName (name) {
|
||||||
|
@ -28,13 +26,20 @@ export function getStorageKey (name, me) {
|
||||||
}
|
}
|
||||||
|
|
||||||
export function walletPrioritySort (w1, w2) {
|
export function walletPrioritySort (w1, w2) {
|
||||||
const delta = w1.priority - w2.priority
|
// enabled/configured wallets always come before disabled/unconfigured wallets
|
||||||
|
if ((w1.config?.enabled && !w2.config?.enabled) || (isConfigured(w1) && !isConfigured(w2))) {
|
||||||
|
return -1
|
||||||
|
} else if ((w2.config?.enabled && !w1.config?.enabled) || (isConfigured(w2) && !isConfigured(w1))) {
|
||||||
|
return 1
|
||||||
|
}
|
||||||
|
|
||||||
|
const delta = w1.config?.priority - w2.config?.priority
|
||||||
// delta is NaN if either priority is undefined
|
// delta is NaN if either priority is undefined
|
||||||
if (!Number.isNaN(delta) && delta !== 0) return delta
|
if (!Number.isNaN(delta) && delta !== 0) return delta
|
||||||
|
|
||||||
// if one wallet has a priority but the other one doesn't, the one with the priority comes first
|
// if one wallet has a priority but the other one doesn't, the one with the priority comes first
|
||||||
if (w1.priority !== undefined && w2.priority === undefined) return -1
|
if (w1.config?.priority !== undefined && w2.config?.priority === undefined) return -1
|
||||||
if (w1.priority === undefined && w2.priority !== undefined) return 1
|
if (w1.config?.priority === undefined && w2.config?.priority !== undefined) return 1
|
||||||
|
|
||||||
// both wallets have no priority set, falling back to other methods
|
// both wallets have no priority set, falling back to other methods
|
||||||
|
|
||||||
|
@ -43,5 +48,50 @@ export function walletPrioritySort (w1, w2) {
|
||||||
if (w1.config?.id && w2.config?.id) return Number(w1.config.id) - Number(w2.config.id)
|
if (w1.config?.id && w2.config?.id) return Number(w1.config.id) - Number(w2.config.id)
|
||||||
|
|
||||||
// else we will use the card title as tie breaker
|
// else we will use the card title as tie breaker
|
||||||
return w1.card.title < w2.card.title ? -1 : 1
|
return w1.def.card.title < w2.def.card.title ? -1 : 1
|
||||||
|
}
|
||||||
|
|
||||||
|
export function isServerField (f) {
|
||||||
|
return f.serverOnly || !f.clientOnly
|
||||||
|
}
|
||||||
|
|
||||||
|
export function isClientField (f) {
|
||||||
|
return f.clientOnly || !f.serverOnly
|
||||||
|
}
|
||||||
|
|
||||||
|
function checkFields ({ fields, config }) {
|
||||||
|
// a wallet is configured if all of its required fields are set
|
||||||
|
let val = fields.every(f => {
|
||||||
|
return f.optional ? true : !!config?.[f.name]
|
||||||
|
})
|
||||||
|
|
||||||
|
// however, a wallet is not configured if all fields are optional and none are set
|
||||||
|
// since that usually means that one of them is required
|
||||||
|
if (val && fields.length > 0) {
|
||||||
|
val = !(fields.every(f => f.optional) && fields.every(f => !config?.[f.name]))
|
||||||
|
}
|
||||||
|
|
||||||
|
return val
|
||||||
|
}
|
||||||
|
|
||||||
|
export function isConfigured (wallet) {
|
||||||
|
return isSendConfigured(wallet) || isReceiveConfigured(wallet)
|
||||||
|
}
|
||||||
|
|
||||||
|
function isSendConfigured (wallet) {
|
||||||
|
const fields = wallet.def.fields.filter(isClientField)
|
||||||
|
return checkFields({ fields, config: wallet.config })
|
||||||
|
}
|
||||||
|
|
||||||
|
function isReceiveConfigured (wallet) {
|
||||||
|
const fields = wallet.def.fields.filter(isServerField)
|
||||||
|
return checkFields({ fields, config: wallet.config })
|
||||||
|
}
|
||||||
|
|
||||||
|
export function canSend (wallet) {
|
||||||
|
return !!wallet.def.sendPayment && isSendConfigured(wallet)
|
||||||
|
}
|
||||||
|
|
||||||
|
export function canReceive (wallet) {
|
||||||
|
return !wallet.def.clientOnly && isReceiveConfigured(wallet)
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,7 +1,7 @@
|
||||||
import { useMe } from '@/components/me'
|
import { useMe } from '@/components/me'
|
||||||
import useVault from '@/components/use-vault'
|
import useVault from '@/components/vault/use-vault'
|
||||||
import { useCallback } from 'react'
|
import { useCallback } from 'react'
|
||||||
import { getStorageKey } from './common'
|
import { getStorageKey, isClientField, isServerField } from './common'
|
||||||
import { useMutation } from '@apollo/client'
|
import { useMutation } from '@apollo/client'
|
||||||
import { generateMutation } from './graphql'
|
import { generateMutation } from './graphql'
|
||||||
import { REMOVE_WALLET } from '@/fragments/wallet'
|
import { REMOVE_WALLET } from '@/fragments/wallet'
|
||||||
|
@ -11,8 +11,8 @@ import { useWalletLogger } from '@/components/wallet-logger'
|
||||||
export function useWalletConfigurator (wallet) {
|
export function useWalletConfigurator (wallet) {
|
||||||
const { me } = useMe()
|
const { me } = useMe()
|
||||||
const { encrypt, isActive } = useVault()
|
const { encrypt, isActive } = useVault()
|
||||||
const { logger } = useWalletLogger(wallet.def)
|
const { logger } = useWalletLogger(wallet?.def)
|
||||||
const [upsertWallet] = useMutation(generateMutation(wallet.def))
|
const [upsertWallet] = useMutation(generateMutation(wallet?.def))
|
||||||
const [removeWallet] = useMutation(REMOVE_WALLET)
|
const [removeWallet] = useMutation(REMOVE_WALLET)
|
||||||
|
|
||||||
const _saveToServer = useCallback(async (serverConfig, clientConfig) => {
|
const _saveToServer = useCallback(async (serverConfig, clientConfig) => {
|
||||||
|
@ -117,48 +117,3 @@ function extractClientConfig (fields, config) {
|
||||||
function extractServerConfig (fields, config) {
|
function extractServerConfig (fields, config) {
|
||||||
return extractConfig(fields, config, false, true)
|
return extractConfig(fields, config, false, true)
|
||||||
}
|
}
|
||||||
|
|
||||||
export function isServerField (f) {
|
|
||||||
return f.serverOnly || !f.clientOnly
|
|
||||||
}
|
|
||||||
|
|
||||||
export function isClientField (f) {
|
|
||||||
return f.clientOnly || !f.serverOnly
|
|
||||||
}
|
|
||||||
|
|
||||||
function checkFields ({ fields, config }) {
|
|
||||||
// a wallet is configured if all of its required fields are set
|
|
||||||
let val = fields.every(f => {
|
|
||||||
return f.optional ? true : !!config?.[f.name]
|
|
||||||
})
|
|
||||||
|
|
||||||
// however, a wallet is not configured if all fields are optional and none are set
|
|
||||||
// since that usually means that one of them is required
|
|
||||||
if (val && fields.length > 0) {
|
|
||||||
val = !(fields.every(f => f.optional) && fields.every(f => !config?.[f.name]))
|
|
||||||
}
|
|
||||||
|
|
||||||
return val
|
|
||||||
}
|
|
||||||
|
|
||||||
export function isConfigured (wallet) {
|
|
||||||
return isSendConfigured(wallet) || isReceiveConfigured(wallet)
|
|
||||||
}
|
|
||||||
|
|
||||||
function isSendConfigured (wallet) {
|
|
||||||
const fields = wallet.def.fields.filter(isClientField)
|
|
||||||
return checkFields({ fields, config: wallet.config })
|
|
||||||
}
|
|
||||||
|
|
||||||
function isReceiveConfigured (wallet) {
|
|
||||||
const fields = wallet.def.fields.filter(isServerField)
|
|
||||||
return checkFields({ fields, config: wallet.config })
|
|
||||||
}
|
|
||||||
|
|
||||||
export function canSend (wallet) {
|
|
||||||
return !!wallet.def.sendPayment && isSendConfigured(wallet)
|
|
||||||
}
|
|
||||||
|
|
||||||
export function canReceive (wallet) {
|
|
||||||
return !wallet.def.clientOnly && isReceiveConfigured(wallet)
|
|
||||||
}
|
|
||||||
|
|
|
@ -0,0 +1,22 @@
|
||||||
|
export class InvoiceCanceledError extends Error {
|
||||||
|
constructor (hash, actionError) {
|
||||||
|
super(actionError ?? `invoice canceled: ${hash}`)
|
||||||
|
this.name = 'InvoiceCanceledError'
|
||||||
|
this.hash = hash
|
||||||
|
this.actionError = actionError
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
export class NoAttachedWalletError extends Error {
|
||||||
|
constructor () {
|
||||||
|
super('no attached wallet found')
|
||||||
|
this.name = 'NoAttachedWalletError'
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
export class InvoiceExpiredError extends Error {
|
||||||
|
constructor (hash) {
|
||||||
|
super(`invoice expired: ${hash}`)
|
||||||
|
this.name = 'InvoiceExpiredError'
|
||||||
|
}
|
||||||
|
}
|
|
@ -1,5 +1,5 @@
|
||||||
import gql from 'graphql-tag'
|
import gql from 'graphql-tag'
|
||||||
import { isServerField } from './config'
|
import { isServerField } from './common'
|
||||||
|
|
||||||
export function fieldToGqlArg (field) {
|
export function fieldToGqlArg (field) {
|
||||||
let arg = `${field.name}: String`
|
let arg = `${field.name}: String`
|
||||||
|
|
|
@ -3,12 +3,11 @@ import { WALLETS } from '@/fragments/wallet'
|
||||||
import { NORMAL_POLL_INTERVAL, SSR } from '@/lib/constants'
|
import { NORMAL_POLL_INTERVAL, SSR } from '@/lib/constants'
|
||||||
import { useQuery } from '@apollo/client'
|
import { useQuery } from '@apollo/client'
|
||||||
import { createContext, useCallback, useContext, useEffect, useMemo, useState } from 'react'
|
import { createContext, useCallback, useContext, useEffect, useMemo, useState } from 'react'
|
||||||
import { getStorageKey, getWalletByType } from './common'
|
import { getStorageKey, getWalletByType, Status, walletPrioritySort, canSend } from './common'
|
||||||
import useVault from '@/components/use-vault'
|
import useVault from '@/components/vault/use-vault'
|
||||||
import { useWalletLogger } from '@/components/wallet-logger'
|
import { useWalletLogger } from '@/components/wallet-logger'
|
||||||
import { bolt11Tags } from '@/lib/bolt11'
|
import { bolt11Tags } from '@/lib/bolt11'
|
||||||
import walletDefs from 'wallets/client'
|
import walletDefs from 'wallets/client'
|
||||||
import { canSend } from './config'
|
|
||||||
|
|
||||||
const WalletsContext = createContext({
|
const WalletsContext = createContext({
|
||||||
wallets: []
|
wallets: []
|
||||||
|
@ -47,6 +46,8 @@ function useLocalWallets () {
|
||||||
return wallets
|
return wallets
|
||||||
}
|
}
|
||||||
|
|
||||||
|
const walletDefsOnly = walletDefs.map(w => ({ def: w, config: {} }))
|
||||||
|
|
||||||
export function WalletsProvider ({ children }) {
|
export function WalletsProvider ({ children }) {
|
||||||
const { me } = useMe()
|
const { me } = useMe()
|
||||||
const { decrypt } = useVault()
|
const { decrypt } = useVault()
|
||||||
|
@ -70,16 +71,19 @@ export function WalletsProvider ({ children }) {
|
||||||
}
|
}
|
||||||
|
|
||||||
return { config, def }
|
return { config, def }
|
||||||
})
|
}) ?? []
|
||||||
|
|
||||||
// merge wallets on name
|
// merge wallets on name
|
||||||
const merged = {}
|
const merged = {}
|
||||||
for (const wallet of [...localWallets, ...wallets]) {
|
for (const wallet of [...walletDefsOnly, ...localWallets, ...wallets]) {
|
||||||
merged[wallet.def.name] = { ...merged[wallet.def.name], ...wallet }
|
merged[wallet.def.name] = { ...merged[wallet.def.name], ...wallet }
|
||||||
}
|
}
|
||||||
return Object.values(merged)
|
return Object.values(merged)
|
||||||
|
.sort(walletPrioritySort)
|
||||||
|
.map(w => ({ ...w, status: w.config?.enabled ? Status.Enabled : Status.Disabled }))
|
||||||
}, [data?.wallets, localWallets])
|
}, [data?.wallets, localWallets])
|
||||||
|
|
||||||
|
// provides priority sorted wallets to children
|
||||||
return (
|
return (
|
||||||
<WalletsContext.Provider value={wallets}>
|
<WalletsContext.Provider value={wallets}>
|
||||||
{children}
|
{children}
|
||||||
|
@ -101,10 +105,10 @@ export function useWallet (name) {
|
||||||
|
|
||||||
return wallets
|
return wallets
|
||||||
.filter(w => !w.def.isAvailable || w.def.isAvailable())
|
.filter(w => !w.def.isAvailable || w.def.isAvailable())
|
||||||
.filter(w => w.config.enabled && canSend(w))[0]
|
.filter(w => w.config?.enabled && canSend(w))[0]
|
||||||
}, [wallets, name])
|
}, [wallets, name])
|
||||||
|
|
||||||
const { logger } = useWalletLogger(wallet.def)
|
const { logger } = useWalletLogger(wallet?.def)
|
||||||
|
|
||||||
const sendPayment = useCallback(async (bolt11) => {
|
const sendPayment = useCallback(async (bolt11) => {
|
||||||
const hash = bolt11Tags(bolt11).payment_hash
|
const hash = bolt11Tags(bolt11).payment_hash
|
||||||
|
|
|
@ -1,4 +1,4 @@
|
||||||
import { InvoiceCanceledError, InvoiceExpiredError } from '@/components/payment'
|
import { InvoiceCanceledError, InvoiceExpiredError } from '@/wallets/errors'
|
||||||
import { bolt11Tags } from '@/lib/bolt11'
|
import { bolt11Tags } from '@/lib/bolt11'
|
||||||
import { Mutex } from 'async-mutex'
|
import { Mutex } from 'async-mutex'
|
||||||
export * from 'wallets/lnc'
|
export * from 'wallets/lnc'
|
||||||
|
|
Loading…
Reference in New Issue