webln saves at least *double kazoo*
This commit is contained in:
parent
48640cbed6
commit
2bdbb433df
|
@ -263,7 +263,7 @@ export default function LoginButton () {
|
|||
|
||||
function LogoutObstacle ({ onClose }) {
|
||||
const { registration: swRegistration, togglePushSubscription } = useServiceWorker()
|
||||
const wallets = useWallets()
|
||||
const { wallets } = useWallets()
|
||||
const { multiAuthSignout } = useAccounts()
|
||||
|
||||
return (
|
||||
|
|
|
@ -132,7 +132,7 @@ export function useWalletLogger (wallet, setLogs) {
|
|||
|
||||
const deleteLogs = useCallback(async (wallet, options) => {
|
||||
if ((!wallet || wallet.def.walletType) && !options?.clientOnly) {
|
||||
await deleteServerWalletLogs({ variables: { wallet: wallet?.walletType } })
|
||||
await deleteServerWalletLogs({ variables: { wallet: wallet?.def.walletType } })
|
||||
}
|
||||
if (!wallet || wallet.sendPayment) {
|
||||
try {
|
||||
|
@ -163,13 +163,13 @@ export function useWalletLogger (wallet, setLogs) {
|
|||
ok: (...message) => log('ok')(message.join(' ')),
|
||||
info: (...message) => log('info')(message.join(' ')),
|
||||
error: (...message) => log('error')(message.join(' '))
|
||||
}), [log, wallet?.name])
|
||||
}), [log])
|
||||
|
||||
return { logger, deleteLogs }
|
||||
}
|
||||
|
||||
function tag (wallet) {
|
||||
return wallet?.shortName || wallet?.name
|
||||
function tag (walletDef) {
|
||||
return walletDef.shortName || walletDef.name
|
||||
}
|
||||
|
||||
export function useWalletLogs (wallet, initialPage = 1, logsPerPage = 10) {
|
||||
|
@ -183,24 +183,24 @@ export function useWalletLogs (wallet, initialPage = 1, logsPerPage = 10) {
|
|||
const { getPage, error, notSupported } = useWalletLogDB()
|
||||
const [getWalletLogs] = useLazyQuery(WALLET_LOGS, SSR ? {} : { fetchPolicy: 'cache-and-network' })
|
||||
|
||||
const loadLogsPage = useCallback(async (page, pageSize, wallet) => {
|
||||
const loadLogsPage = useCallback(async (page, pageSize, walletDef) => {
|
||||
try {
|
||||
let result = { data: [], hasMore: false }
|
||||
if (notSupported) {
|
||||
console.log('cannot get client wallet logs: indexeddb not supported')
|
||||
} else {
|
||||
const indexName = wallet ? 'wallet_ts' : 'ts'
|
||||
const query = wallet ? window.IDBKeyRange.bound([tag(wallet), -Infinity], [tag(wallet), Infinity]) : null
|
||||
const indexName = walletDef ? 'wallet_ts' : 'ts'
|
||||
const query = walletDef ? window.IDBKeyRange.bound([tag(walletDef), -Infinity], [tag(walletDef), Infinity]) : null
|
||||
|
||||
result = await getPage(page, pageSize, indexName, query, 'prev')
|
||||
// no walletType means we're using the local IDB
|
||||
if (wallet && !wallet.def.walletType) {
|
||||
if (!walletDef?.walletType) {
|
||||
return result
|
||||
}
|
||||
}
|
||||
const { data } = await getWalletLogs({
|
||||
variables: {
|
||||
type: wallet?.walletType,
|
||||
type: walletDef.walletType,
|
||||
// if it client logs has more, page based on it's range
|
||||
from: result?.data[result.data.length - 1]?.ts && result.hasMore ? String(result.data[result.data.length - 1].ts) : null,
|
||||
// if we have a cursor (this isn't the first page), page based on it's range
|
||||
|
@ -231,28 +231,28 @@ export function useWalletLogs (wallet, initialPage = 1, logsPerPage = 10) {
|
|||
const loadMore = useCallback(async () => {
|
||||
if (hasMore) {
|
||||
setLoading(true)
|
||||
const result = await loadLogsPage(page + 1, logsPerPage, wallet)
|
||||
const result = await loadLogsPage(page + 1, logsPerPage, wallet?.def)
|
||||
setLogs(prevLogs => [...prevLogs, ...result.data])
|
||||
setHasMore(result.hasMore)
|
||||
setTotal(result.total)
|
||||
setPage(prevPage => prevPage + 1)
|
||||
setLoading(false)
|
||||
}
|
||||
}, [loadLogsPage, page, logsPerPage, wallet, hasMore])
|
||||
}, [loadLogsPage, page, logsPerPage, wallet?.def, hasMore])
|
||||
|
||||
const loadLogs = useCallback(async () => {
|
||||
setLoading(true)
|
||||
const result = await loadLogsPage(1, logsPerPage, wallet)
|
||||
const result = await loadLogsPage(1, logsPerPage, wallet?.def)
|
||||
setLogs(result.data)
|
||||
setHasMore(result.hasMore)
|
||||
setTotal(result.total)
|
||||
setPage(1)
|
||||
setLoading(false)
|
||||
}, [wallet, loadLogsPage])
|
||||
}, [wallet?.def, loadLogsPage])
|
||||
|
||||
useEffect(() => {
|
||||
loadLogs()
|
||||
}, [wallet])
|
||||
}, [wallet?.def])
|
||||
|
||||
return { logs, hasMore, total, loadMore, loadLogs, setLogs, loading }
|
||||
}
|
||||
|
|
|
@ -43,10 +43,10 @@ export async function formikValidate (validate, data) {
|
|||
}
|
||||
|
||||
export async function walletValidate (wallet, data) {
|
||||
if (typeof wallet.fieldValidation === 'function') {
|
||||
return await formikValidate(wallet.fieldValidation, data)
|
||||
if (typeof wallet.def.fieldValidation === 'function') {
|
||||
return await formikValidate(wallet.def.fieldValidation, data)
|
||||
} else {
|
||||
return await ssValidate(wallet.fieldValidation, data)
|
||||
return await ssValidate(wallet.def.fieldValidation, data)
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -9,9 +9,10 @@ import { useWallet } from '@/wallets/index'
|
|||
import Info from '@/components/info'
|
||||
import Text from '@/components/text'
|
||||
import { AutowithdrawSettings } from '@/components/autowithdraw-shared'
|
||||
import { isConfigured } from '@/wallets/common'
|
||||
import { canSend, isConfigured } from '@/wallets/common'
|
||||
import { SSR } from '@/lib/constants'
|
||||
import WalletButtonBar from '@/components/wallet-buttonbar'
|
||||
import { useWalletConfigurator } from '@/wallets/config'
|
||||
|
||||
export const getServerSideProps = getGetServerSideProps({ authRequired: true })
|
||||
|
||||
|
@ -20,6 +21,7 @@ export default function WalletSettings () {
|
|||
const router = useRouter()
|
||||
const { wallet: name } = router.query
|
||||
const wallet = useWallet(name)
|
||||
const { save, detach } = useWalletConfigurator(wallet)
|
||||
|
||||
const initial = wallet?.def.fields.reduce((acc, field) => {
|
||||
// We still need to run over all wallet fields via reduce
|
||||
|
@ -42,7 +44,7 @@ export default function WalletSettings () {
|
|||
<CenterLayout>
|
||||
<h2 className='pb-2'>{wallet?.def.card.title}</h2>
|
||||
<h6 className='text-muted text-center pb-3'><Text>{wallet?.def.card.subtitle}</Text></h6>
|
||||
{wallet?.canSend && wallet?.hasConfig > 0 && <WalletSecurityBanner />}
|
||||
{canSend(wallet) && <WalletSecurityBanner />}
|
||||
<Form
|
||||
initial={initial}
|
||||
enableReinitialize
|
||||
|
@ -56,7 +58,7 @@ export default function WalletSettings () {
|
|||
values.enabled = true
|
||||
}
|
||||
|
||||
await wallet.save(values)
|
||||
await save(values, true)
|
||||
|
||||
toaster.success('saved settings')
|
||||
router.push('/settings/wallets')
|
||||
|
@ -82,7 +84,7 @@ export default function WalletSettings () {
|
|||
<WalletButtonBar
|
||||
wallet={wallet} onDelete={async () => {
|
||||
try {
|
||||
await wallet?.delete()
|
||||
await detach()
|
||||
toaster.success('saved settings')
|
||||
router.push('/settings/wallets')
|
||||
} catch (err) {
|
||||
|
@ -101,8 +103,6 @@ export default function WalletSettings () {
|
|||
}
|
||||
|
||||
function WalletFields ({ wallet }) {
|
||||
console.log('wallet', wallet)
|
||||
|
||||
return wallet.def.fields
|
||||
.map(({ name, label = '', type, help, optional, editable, clientOnly, serverOnly, ...props }, i) => {
|
||||
const rawProps = {
|
||||
|
|
|
@ -26,7 +26,7 @@ async function reorder (wallets, sourceIndex, targetIndex) {
|
|||
}
|
||||
|
||||
export default function Wallet ({ ssrData }) {
|
||||
const wallets = useWallets()
|
||||
const { wallets } = useWallets()
|
||||
|
||||
const isClient = useIsClient()
|
||||
const [sourceIndex, setSourceIndex] = useState(null)
|
||||
|
|
|
@ -13,13 +13,13 @@ export function getWalletByType (type) {
|
|||
return walletDefs.find(def => def.walletType === type)
|
||||
}
|
||||
|
||||
export function getStorageKey (name, me) {
|
||||
export function getStorageKey (name, userId) {
|
||||
let storageKey = `wallet:${name}`
|
||||
|
||||
// WebLN has no credentials we need to scope to users
|
||||
// so we can use the same storage key for all users
|
||||
if (me && name !== 'webln') {
|
||||
storageKey = `${storageKey}:${me.id}`
|
||||
if (userId && name !== 'webln') {
|
||||
storageKey = `${storageKey}:${userId}`
|
||||
}
|
||||
|
||||
return storageKey
|
||||
|
|
|
@ -1,15 +1,17 @@
|
|||
import { useMe } from '@/components/me'
|
||||
import useVault from '@/components/vault/use-vault'
|
||||
import { useCallback } from 'react'
|
||||
import { getStorageKey, isClientField, isServerField } from './common'
|
||||
import { canReceive, canSend, getStorageKey, isClientField, isServerField } from './common'
|
||||
import { useMutation } from '@apollo/client'
|
||||
import { generateMutation } from './graphql'
|
||||
import { REMOVE_WALLET } from '@/fragments/wallet'
|
||||
import { walletValidate } from '@/lib/validate'
|
||||
import { useWalletLogger } from '@/components/wallet-logger'
|
||||
import { useWallets } from '.'
|
||||
|
||||
export function useWalletConfigurator (wallet) {
|
||||
const { me } = useMe()
|
||||
const { reloadLocalWallets } = useWallets()
|
||||
const { encrypt, isActive } = useVault()
|
||||
const { logger } = useWalletLogger(wallet?.def)
|
||||
const [upsertWallet] = useMutation(generateMutation(wallet?.def))
|
||||
|
@ -26,26 +28,29 @@ export function useWalletConfigurator (wallet) {
|
|||
}, [encrypt, isActive])
|
||||
|
||||
const _saveToLocal = useCallback(async (newConfig) => {
|
||||
window.localStorage.setItem(getStorageKey(wallet.name, me), JSON.stringify(newConfig))
|
||||
}, [me, wallet.name])
|
||||
window.localStorage.setItem(getStorageKey(wallet.def.name, me?.id), JSON.stringify(newConfig))
|
||||
reloadLocalWallets()
|
||||
}, [me?.id, wallet.def.name, reloadLocalWallets])
|
||||
|
||||
const save = useCallback(async (newConfig, validate = true) => {
|
||||
let clientConfig = extractClientConfig(wallet.def.fields, newConfig)
|
||||
let serverConfig = extractServerConfig(wallet.def.fields, newConfig)
|
||||
|
||||
if (validate) {
|
||||
if (clientConfig) {
|
||||
if (canSend(wallet)) {
|
||||
let transformedConfig = await walletValidate(wallet, clientConfig)
|
||||
if (transformedConfig) {
|
||||
clientConfig = Object.assign(clientConfig, transformedConfig)
|
||||
}
|
||||
transformedConfig = await wallet.def.testSendPayment(clientConfig, { me, logger })
|
||||
if (transformedConfig) {
|
||||
clientConfig = Object.assign(clientConfig, transformedConfig)
|
||||
if (wallet.def.testSendPayment) {
|
||||
transformedConfig = await wallet.def.testSendPayment(clientConfig, { me, logger })
|
||||
if (transformedConfig) {
|
||||
clientConfig = Object.assign(clientConfig, transformedConfig)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (serverConfig) {
|
||||
if (canReceive(wallet)) {
|
||||
const transformedConfig = await walletValidate(wallet, serverConfig)
|
||||
if (transformedConfig) {
|
||||
serverConfig = Object.assign(serverConfig, transformedConfig)
|
||||
|
@ -57,14 +62,14 @@ export function useWalletConfigurator (wallet) {
|
|||
if (isActive) {
|
||||
await _saveToServer(serverConfig, clientConfig)
|
||||
} else {
|
||||
if (clientConfig) {
|
||||
if (canSend(wallet)) {
|
||||
await _saveToLocal(clientConfig)
|
||||
}
|
||||
if (serverConfig) {
|
||||
if (canReceive(wallet)) {
|
||||
await _saveToServer(serverConfig)
|
||||
}
|
||||
}
|
||||
}, [wallet.def, encrypt, isActive])
|
||||
}, [wallet, encrypt, isActive])
|
||||
|
||||
const _detachFromServer = useCallback(async () => {
|
||||
await removeWallet({ variables: { id: wallet.config.id } })
|
||||
|
@ -72,8 +77,8 @@ export function useWalletConfigurator (wallet) {
|
|||
|
||||
const _detachFromLocal = useCallback(async () => {
|
||||
// if vault is not active and has a client config, delete from local storage
|
||||
window.localStorage.removeItem(getStorageKey(wallet.name, me))
|
||||
}, [me, wallet.name])
|
||||
window.localStorage.removeItem(getStorageKey(wallet.def.name, me?.id))
|
||||
}, [me?.id, wallet.def.name])
|
||||
|
||||
const detach = useCallback(async () => {
|
||||
if (isActive) {
|
||||
|
@ -87,7 +92,7 @@ export function useWalletConfigurator (wallet) {
|
|||
}
|
||||
}, [isActive, _detachFromServer, _detachFromLocal])
|
||||
|
||||
return [save, detach]
|
||||
return { save, detach }
|
||||
}
|
||||
|
||||
function extractConfig (fields, config, client, includeMeta = true) {
|
||||
|
@ -111,7 +116,7 @@ function extractConfig (fields, config, client, includeMeta = true) {
|
|||
}
|
||||
|
||||
function extractClientConfig (fields, config) {
|
||||
return extractConfig(fields, config, true, false)
|
||||
return extractConfig(fields, config, true, true)
|
||||
}
|
||||
|
||||
function extractServerConfig (fields, config) {
|
||||
|
|
|
@ -1,6 +1,6 @@
|
|||
import { useMe } from '@/components/me'
|
||||
import { WALLETS } from '@/fragments/wallet'
|
||||
import { NORMAL_POLL_INTERVAL, SSR } from '@/lib/constants'
|
||||
import { LONG_POLL_INTERVAL, SSR } from '@/lib/constants'
|
||||
import { useQuery } from '@apollo/client'
|
||||
import { createContext, useCallback, useContext, useEffect, useMemo, useState } from 'react'
|
||||
import { getStorageKey, getWalletByType, Status, walletPrioritySort, canSend } from './common'
|
||||
|
@ -21,44 +21,34 @@ function useLocalWallets () {
|
|||
// form wallets into a list of { config, def }
|
||||
const wallets = walletDefs.map(w => {
|
||||
try {
|
||||
const config = window.localStorage.getItem(getStorageKey(w.name, me))
|
||||
const config = window.localStorage.getItem(getStorageKey(w.name, me?.id))
|
||||
return { def: w, config: JSON.parse(config) }
|
||||
} catch (e) {
|
||||
return null
|
||||
}
|
||||
}).filter(Boolean)
|
||||
setWallets(wallets)
|
||||
}, [me, setWallets])
|
||||
}, [me?.id, setWallets])
|
||||
|
||||
// watch for changes to local storage
|
||||
useEffect(() => {
|
||||
loadWallets()
|
||||
// reload wallets if local storage to wallet changes
|
||||
const handler = (event) => {
|
||||
if (event.key.startsWith('wallet:')) {
|
||||
loadWallets()
|
||||
}
|
||||
}
|
||||
window.addEventListener('storage', handler)
|
||||
return () => window.removeEventListener('storage', handler)
|
||||
}, [loadWallets])
|
||||
|
||||
return wallets
|
||||
return { wallets, reloadLocalWallets: loadWallets }
|
||||
}
|
||||
|
||||
const walletDefsOnly = walletDefs.map(w => ({ def: w, config: {} }))
|
||||
|
||||
export function WalletsProvider ({ children }) {
|
||||
const { me } = useMe()
|
||||
const { decrypt } = useVault()
|
||||
const localWallets = useLocalWallets()
|
||||
const { wallets: localWallets, reloadLocalWallets } = useLocalWallets()
|
||||
|
||||
// TODO: instead of polling, this should only be called when the vault key is updated
|
||||
// or a denormalized field on the user 'vaultUpdatedAt' is changed
|
||||
const { data } = useQuery(WALLETS, {
|
||||
pollInterval: NORMAL_POLL_INTERVAL,
|
||||
pollInterval: LONG_POLL_INTERVAL,
|
||||
nextFetchPolicy: 'cache-and-network',
|
||||
skip: !me?.id || SSR
|
||||
skip: SSR
|
||||
})
|
||||
|
||||
const wallets = useMemo(() => {
|
||||
|
@ -85,7 +75,7 @@ export function WalletsProvider ({ children }) {
|
|||
|
||||
// provides priority sorted wallets to children
|
||||
return (
|
||||
<WalletsContext.Provider value={wallets}>
|
||||
<WalletsContext.Provider value={{ wallets, reloadLocalWallets }}>
|
||||
{children}
|
||||
</WalletsContext.Provider>
|
||||
)
|
||||
|
@ -96,7 +86,7 @@ export function useWallets () {
|
|||
}
|
||||
|
||||
export function useWallet (name) {
|
||||
const wallets = useWallets()
|
||||
const { wallets } = useWallets()
|
||||
|
||||
const wallet = useMemo(() => {
|
||||
if (name) {
|
||||
|
|
|
@ -39,14 +39,14 @@ export async function createInvoice (userId, { msats, description, descriptionHa
|
|||
msats = toPositiveNumber(msats)
|
||||
|
||||
for (const wallet of wallets) {
|
||||
const w = walletDefs.find(w => w.walletType === wallet.type)
|
||||
const w = walletDefs.find(w => w.walletType === wallet.def.walletType)
|
||||
try {
|
||||
const { walletType, walletField, createInvoice } = w
|
||||
|
||||
const walletFull = await models.wallet.findFirst({
|
||||
where: {
|
||||
userId,
|
||||
type: walletType
|
||||
type: wallet.def.walletType
|
||||
},
|
||||
include: {
|
||||
[walletField]: true
|
||||
|
|
Loading…
Reference in New Issue