Show wallets query error (#2284)

* Show wallets query error to user

* Also show decryption errors to user
This commit is contained in:
ekzyis 2025-07-16 17:11:52 +02:00 committed by GitHub
parent d670b38d1d
commit 45d7eaf1bb
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
4 changed files with 31 additions and 5 deletions

View File

@ -68,6 +68,19 @@ export default function Wallet () {
)
}
if (status instanceof Error) {
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}
</small>
</div>
</WalletLayout>
)
}
if (status === Status.NO_WALLETS && !showWallets) {
return (
<WalletLayout>

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 } from '@/wallets/client/context'
import { SET_WALLETS, WRONG_KEY, KEY_MATCH, NO_KEY, useWalletsDispatch, WALLETS_QUERY_ERROR } from '@/wallets/client/context'
import { useIndexedDB } from '@/components/use-indexeddb'
export function useServerWallets () {
@ -19,6 +19,7 @@ export function useServerWallets () {
useEffect(() => {
if (query.error) {
console.error('failed to fetch wallets:', query.error)
dispatch({ type: WALLETS_QUERY_ERROR, error: query.error })
return
}
if (query.loading) return

View File

@ -15,6 +15,7 @@ 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 WALLETS_QUERY_ERROR = 'WALLETS_QUERY_ERROR'
export default function reducer (state, action) {
switch (action.type) {
@ -32,6 +33,11 @@ export default function reducer (state, action) {
templates
}
}
case WALLETS_QUERY_ERROR:
return {
...state,
status: transitionStatus(action, state, action.error)
}
case SET_KEY:
return {
...state,
@ -61,10 +67,10 @@ export default function reducer (state, action) {
function transitionStatus ({ type }, { status: from }, to) {
switch (type) {
case SET_WALLETS: {
return [Status.PASSPHRASE_REQUIRED, Status.WALLETS_UNAVAILABLE].includes(from) ? from : to
return (from instanceof Error || [Status.PASSPHRASE_REQUIRED, Status.WALLETS_UNAVAILABLE].includes(from)) ? from : to
}
case KEY_MATCH: {
return from === Status.LOADING_WALLETS ? from : to
return (from instanceof Error || from === Status.LOADING_WALLETS) ? from : to
}
default:
return to

View File

@ -38,6 +38,7 @@ export function useWalletsQuery () {
const { me } = useMe()
const query = useQuery(WALLETS, { skip: !me })
const [wallets, setWallets] = useState(null)
const [error, setError] = useState(null)
const { decryptWallet, ready } = useWalletDecryption()
@ -48,10 +49,14 @@ export function useWalletsQuery () {
)
.then(wallets => wallets.map(protocolCheck))
.then(wallets => wallets.map(undoFieldAlias))
.then(wallets => setWallets(wallets))
.then(wallets => {
setWallets(wallets)
setError(null)
})
.catch(err => {
console.error('failed to decrypt wallets:', err)
setWallets([])
setError(new Error('decryption error: ' + err.message))
})
}, [query.data, decryptWallet, ready])
@ -59,9 +64,10 @@ export function useWalletsQuery () {
return useMemo(() => ({
...query,
error: error ?? query.error,
loading: !wallets,
data: wallets ? { wallets } : null
}), [query, wallets])
}), [query, error, wallets])
}
function protocolCheck (wallet) {