better error handling
This commit is contained in:
@@ -60,8 +60,8 @@ export default function ListingPage({ params }: { params: Promise<{ pk: string }
|
|||||||
[['listing', pk], ['orders', pk]],
|
[['listing', pk], ['orders', pk]],
|
||||||
'create_order',
|
'create_order',
|
||||||
)
|
)
|
||||||
} catch (err) {
|
} catch {
|
||||||
console.error('create_order failed:', err)
|
// error already shown via toast in useTx
|
||||||
} finally {
|
} finally {
|
||||||
setOrderLoading(false)
|
setOrderLoading(false)
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -2,24 +2,26 @@
|
|||||||
|
|
||||||
import { createContext, useCallback, useContext, useState, type ReactNode } from 'react'
|
import { createContext, useCallback, useContext, useState, type ReactNode } from 'react'
|
||||||
|
|
||||||
const ToastCtx = createContext<(msg: string) => void>(() => {})
|
type ToastFn = (msg: string, type?: 'success' | 'error') => void
|
||||||
|
|
||||||
|
const ToastCtx = createContext<ToastFn>(() => {})
|
||||||
|
|
||||||
export function useToast() {
|
export function useToast() {
|
||||||
return useContext(ToastCtx)
|
return useContext(ToastCtx)
|
||||||
}
|
}
|
||||||
|
|
||||||
export function ToastProvider({ children }: { children: ReactNode }) {
|
export function ToastProvider({ children }: { children: ReactNode }) {
|
||||||
const [msg, setMsg] = useState('')
|
const [state, setState] = useState<{ msg: string; type: 'success' | 'error' } | null>(null)
|
||||||
|
|
||||||
const show = useCallback((m: string) => {
|
const show = useCallback<ToastFn>((m, type = 'success') => {
|
||||||
setMsg(m)
|
setState({ msg: m, type })
|
||||||
setTimeout(() => setMsg(''), 2800)
|
setTimeout(() => setState(null), type === 'error' ? 4500 : 2800)
|
||||||
}, [])
|
}, [])
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<ToastCtx.Provider value={show}>
|
<ToastCtx.Provider value={show}>
|
||||||
{children}
|
{children}
|
||||||
{msg && (
|
{state && (
|
||||||
<div
|
<div
|
||||||
style={{
|
style={{
|
||||||
position: 'fixed',
|
position: 'fixed',
|
||||||
@@ -32,7 +34,7 @@ export function ToastProvider({ children }: { children: ReactNode }) {
|
|||||||
gap: 10,
|
gap: 10,
|
||||||
padding: '12px 18px',
|
padding: '12px 18px',
|
||||||
background: 'var(--bg2)',
|
background: 'var(--bg2)',
|
||||||
border: '1px solid var(--accBd)',
|
border: `1px solid ${state.type === 'error' ? 'var(--errBd, #f87171)' : 'var(--accBd)'}`,
|
||||||
borderRadius: 12,
|
borderRadius: 12,
|
||||||
boxShadow: '0 16px 40px rgba(0,0,0,.55)',
|
boxShadow: '0 16px 40px rgba(0,0,0,.55)',
|
||||||
fontSize: 13,
|
fontSize: 13,
|
||||||
@@ -46,12 +48,12 @@ export function ToastProvider({ children }: { children: ReactNode }) {
|
|||||||
width: 8,
|
width: 8,
|
||||||
height: 8,
|
height: 8,
|
||||||
borderRadius: '50%',
|
borderRadius: '50%',
|
||||||
background: 'var(--acc2)',
|
background: state.type === 'error' ? '#f87171' : 'var(--acc2)',
|
||||||
boxShadow: '0 0 8px var(--acc2)',
|
boxShadow: state.type === 'error' ? '0 0 8px #f87171' : '0 0 8px var(--acc2)',
|
||||||
flexShrink: 0,
|
flexShrink: 0,
|
||||||
}}
|
}}
|
||||||
/>
|
/>
|
||||||
{msg}
|
{state.msg}
|
||||||
</div>
|
</div>
|
||||||
)}
|
)}
|
||||||
</ToastCtx.Provider>
|
</ToastCtx.Provider>
|
||||||
|
|||||||
@@ -39,10 +39,15 @@ export function useTx() {
|
|||||||
|
|
||||||
const signed = await signTransactionMessageWithSigners(txMsg)
|
const signed = await signTransactionMessageWithSigners(txMsg)
|
||||||
assertIsTransactionWithBlockhashLifetime(signed)
|
assertIsTransactionWithBlockhashLifetime(signed)
|
||||||
await sendAndConfirmTransactionFactory({ rpc: rpc as never, rpcSubscriptions: rpcSubscriptions as never })(
|
try {
|
||||||
signed as never,
|
await sendAndConfirmTransactionFactory({ rpc: rpc as never, rpcSubscriptions: rpcSubscriptions as never })(
|
||||||
{ commitment: 'confirmed' },
|
signed as never,
|
||||||
)
|
{ commitment: 'confirmed' },
|
||||||
|
)
|
||||||
|
} catch (err) {
|
||||||
|
toast(friendlyTxError(err), 'error')
|
||||||
|
throw err
|
||||||
|
}
|
||||||
|
|
||||||
toast(`Tx confirmed — ${label}`)
|
toast(`Tx confirmed — ${label}`)
|
||||||
for (const key of invalidateKeys) {
|
for (const key of invalidateKeys) {
|
||||||
@@ -50,3 +55,20 @@ export function useTx() {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
function friendlyTxError(err: unknown): string {
|
||||||
|
const msg = err instanceof Error ? err.message : String(err)
|
||||||
|
if (
|
||||||
|
msg.includes('Attempt to debit an account but found no record of a prior credit') ||
|
||||||
|
msg.includes('AccountNotFound')
|
||||||
|
) {
|
||||||
|
return 'Insufficient SOL — please fund your wallet and try again.'
|
||||||
|
}
|
||||||
|
if (msg.includes('insufficient lamports') || msg.includes('insufficient funds')) {
|
||||||
|
return 'Insufficient SOL — please fund your wallet and try again.'
|
||||||
|
}
|
||||||
|
if (msg.includes('User rejected') || msg.includes('Transaction was not confirmed')) {
|
||||||
|
return 'Transaction cancelled.'
|
||||||
|
}
|
||||||
|
return 'Transaction failed — please try again.'
|
||||||
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user