Fix wallets error handling order (#2286)

This commit is contained in:
ekzyis 2025-07-16 21:50:08 +02:00 committed by GitHub
parent 2ee685d5a9
commit 5502d29d7e
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
6 changed files with 59 additions and 64 deletions

View File

@ -1,6 +1,6 @@
import { getGetServerSideProps } from '@/api/ssrApollo'
import { Button } from 'react-bootstrap'
import { useWallets, useTemplates, DndProvider, Status, useStatus } from '@/wallets/client/context'
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'
@ -13,7 +13,9 @@ export const getServerSideProps = getGetServerSideProps({ authRequired: true })
export default function Wallet () {
const wallets = useWallets()
const status = useStatus()
const walletsLoading = useWalletsLoading()
const walletsError = useWalletsError()
const keyError = useKeyError()
const [showWallets, setShowWallets] = useState(false)
const templates = useTemplates()
const showPassphrase = useShowPassphrase()
@ -29,18 +31,20 @@ export default function Wallet () {
}
}, [wallets, templates, searchFilter])
if (status === Status.LOADING_WALLETS) {
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 text-muted'>
<Moon className='spin fill-grey' height={28} width={28} />
<small className='d-block mt-3 text-muted'>loading wallets</small>
<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 (status === Status.PASSPHRASE_REQUIRED) {
if (keyError === KeyStatus.WRONG_KEY) {
return (
<WalletLayout>
<div className='py-5 text-center d-flex flex-column align-items-center justify-content-center flex-grow-1'>
@ -55,33 +59,31 @@ export default function Wallet () {
)
}
if (status === Status.WALLETS_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 (status instanceof Error) {
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'>
{status.message}
{walletsError.message}
</small>
</div>
</WalletLayout>
)
}
if (status === Status.NO_WALLETS && !showWallets) {
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'>

View File

@ -9,7 +9,7 @@ import {
useWalletMigrationMutation, CryptoKeyRequiredError, useIsWrongKey
} from '@/wallets/client/hooks'
import { WalletConfigurationError } from '@/wallets/client/errors'
import { SET_WALLETS, WRONG_KEY, KEY_MATCH, NO_KEY, useWalletsDispatch, WALLETS_QUERY_ERROR } from '@/wallets/client/context'
import { SET_WALLETS, WRONG_KEY, KEY_MATCH, useWalletsDispatch, WALLETS_QUERY_ERROR, KEY_STORAGE_UNAVAILABLE } from '@/wallets/client/context'
import { useIndexedDB } from '@/components/use-indexeddb'
export function useServerWallets () {
@ -110,7 +110,7 @@ export function useKeyInit () {
useEffect(() => {
if (typeof window.indexedDB === 'undefined') {
dispatch({ type: NO_KEY })
dispatch({ type: KEY_STORAGE_UNAVAILABLE })
} else if (wrongKey) {
dispatch({ type: WRONG_KEY })
} else {

View File

@ -1,5 +1,5 @@
import { createContext, useContext, useReducer } from 'react'
import walletsReducer, { Status } from './reducer'
import walletsReducer from './reducer'
import { useServerWallets, useKeyCheck, useAutomatedRetries, useKeyInit, useWalletMigration } from './hooks'
import { WebLnProvider } from '@/wallets/lib/protocols/webln'
@ -17,14 +17,14 @@ export function useTemplates () {
return templates
}
export function useLoading () {
const { status } = useContext(WalletsContext)
return status === Status.LOADING_WALLETS
export function useWalletsLoading () {
const { walletsLoading } = useContext(WalletsContext)
return walletsLoading
}
export function useStatus () {
const { status } = useContext(WalletsContext)
return status
export function useWalletsError () {
const { walletsError } = useContext(WalletsContext)
return walletsError
}
export function useWalletsDispatch () {
@ -41,13 +41,20 @@ export function useKeyHash () {
return keyHash
}
export function useKeyError () {
const { keyError } = useContext(WalletsContext)
return keyError
}
export default function WalletsProvider ({ children }) {
const [state, dispatch] = useReducer(walletsReducer, {
status: Status.LOADING_WALLETS,
wallets: [],
walletsLoading: true,
walletsError: null,
templates: [],
key: null,
keyHash: null
keyHash: null,
keyError: null
})
return (

View File

@ -1,12 +1,9 @@
import { isTemplate, isWallet } from '@/wallets/lib/util'
// states that dictate if we show a button or wallets on the wallets page
export const Status = {
LOADING_WALLETS: 'LOADING_WALLETS',
NO_WALLETS: 'NO_WALLETS',
HAS_WALLETS: 'HAS_WALLETS',
PASSPHRASE_REQUIRED: 'PASSPHRASE_REQUIRED',
WALLETS_UNAVAILABLE: 'WALLETS_UNAVAILABLE'
export const KeyStatus = {
KEY_MATCH: 'KEY_MATCH',
NO_KEY: 'NO_KEY',
WRONG_KEY: 'WRONG_KEY'
}
// wallet actions
@ -14,7 +11,7 @@ export const SET_WALLETS = 'SET_WALLETS'
export const SET_KEY = 'SET_KEY'
export const WRONG_KEY = 'WRONG_KEY'
export const KEY_MATCH = 'KEY_MATCH'
export const NO_KEY = 'KEY_UNAVAILABLE'
export const KEY_STORAGE_UNAVAILABLE = 'KEY_STORAGE_UNAVAILABLE'
export const WALLETS_QUERY_ERROR = 'WALLETS_QUERY_ERROR'
export default function reducer (state, action) {
@ -28,7 +25,8 @@ export default function reducer (state, action) {
.sort((a, b) => a.name.localeCompare(b.name))
return {
...state,
status: transitionStatus(action, state, wallets.length > 0 ? Status.HAS_WALLETS : Status.NO_WALLETS),
walletsLoading: false,
walletsError: null,
wallets,
templates
}
@ -36,7 +34,8 @@ export default function reducer (state, action) {
case WALLETS_QUERY_ERROR:
return {
...state,
status: transitionStatus(action, state, action.error)
walletsLoading: false,
walletsError: action.error
}
case SET_KEY:
return {
@ -47,32 +46,19 @@ export default function reducer (state, action) {
case WRONG_KEY:
return {
...state,
status: transitionStatus(action, state, Status.PASSPHRASE_REQUIRED)
keyError: KeyStatus.WRONG_KEY
}
case KEY_MATCH:
return {
...state,
status: transitionStatus(action, state, state.wallets.length > 0 ? Status.HAS_WALLETS : Status.NO_WALLETS)
keyError: null
}
case NO_KEY:
case KEY_STORAGE_UNAVAILABLE:
return {
...state,
status: transitionStatus(action, state, Status.WALLETS_UNAVAILABLE)
keyError: KeyStatus.KEY_STORAGE_UNAVAILABLE
}
default:
return state
}
}
function transitionStatus ({ type }, { status: from }, to) {
switch (type) {
case SET_WALLETS: {
return (from instanceof Error || [Status.PASSPHRASE_REQUIRED, Status.WALLETS_UNAVAILABLE].includes(from)) ? from : to
}
case KEY_MATCH: {
return (from instanceof Error || from === Status.LOADING_WALLETS) ? from : to
}
default:
return to
}
}

View File

@ -1,7 +1,7 @@
import { useWallets, useLoading } from '@/wallets/client/context'
import { useWallets, useWalletsLoading } from '@/wallets/client/context'
export function useWalletIndicator () {
const wallets = useWallets()
const loading = useLoading()
const loading = useWalletsLoading()
return !loading && wallets.length === 0
}

View File

@ -32,7 +32,7 @@ import { timeoutSignal } from '@/lib/time'
import { WALLET_SEND_PAYMENT_TIMEOUT_MS } from '@/lib/constants'
import { useToast } from '@/components/toast'
import { useMe } from '@/components/me'
import { useWallets, useLoading as useWalletsLoading } from '@/wallets/client/context'
import { useWallets, useWalletsLoading } from '@/wallets/client/context'
export function useWalletsQuery () {
const { me } = useMe()