mirror of
https://github.com/hanzoai/billing.git
synced 2026-10-30 18:46:54 +00:00
BUG 1 — the crypto/wallet flow crashed the whole app on refresh with
"DialogClose must be used within Dialog" (a Radix dialog error surfaced
via @hanzo/ui, thrown when the wallet connector rendered before its
provider mounted — the same race class as the earlier
WagmiProviderNotFoundError). Two robust, in-app guards:
- Gate the Crypto payment tab on a new useWalletReady() context that is
true ONLY inside the mounted WagmiProvider/RainbowKitProvider subtree
(lib/wallet-ready.tsx, set by wallet-providers.tsx). Replaces the
effect-based getWalletConfig() probe that could win the race against
the still-lazy provider, so the connector never renders unprovided.
- Wrap the crypto tab (payment-manager) and the top-up page's crypto +
HUSD tabs in a reusable client ErrorBoundary
(lib/components/error-boundary.tsx), so any wallet-lib hiccup degrades
to an honest inline message instead of a whole-page "Something went
wrong".
BUG 2 — a claimed $5 starter credit showed as un-claimed (the "Claim $5
Starter Credit" button persisted). Commerce records the starter credit
as a Deposit transaction tagged "starter-credit" (deliberately NOT a
credit-grant), so getCreditGrants() is empty and billing-shell
synthesizes a grant from the transaction — but named it after the
transaction's freeform notes ("Welcome credit: $100.00 USD ..."), which
don't contain "starter". The credits UI keys "claimed" off
name.includes('starter'), so it never matched. Name the synthesized
grant "Starter Credit" (the starter-credit tag is the source of truth)
and carry the real on-chain expiry, so the claimed credit shows with its
real balance.
51 lines
1.3 KiB
TypeScript
51 lines
1.3 KiB
TypeScript
'use client'
|
|
|
|
import * as React from 'react'
|
|
|
|
type Fallback = React.ReactNode | ((error: Error, reset: () => void) => React.ReactNode)
|
|
|
|
interface Props {
|
|
children: React.ReactNode
|
|
/** Rendered instead of the crashed subtree. A function gets the error + a reset(). */
|
|
fallback?: Fallback
|
|
/** Notified on catch (e.g. to log). */
|
|
onError?: (error: Error) => void
|
|
}
|
|
|
|
interface State {
|
|
error: Error | null
|
|
}
|
|
|
|
/**
|
|
* Client error boundary — contains a render-time crash to its own subtree so a
|
|
* single misbehaving widget (e.g. a wallet-connect modal that throws
|
|
* `DialogClose must be used within Dialog` on a bad refresh) degrades to an
|
|
* honest inline message instead of taking down the whole page.
|
|
*/
|
|
export class ErrorBoundary extends React.Component<Props, State> {
|
|
state: State = { error: null }
|
|
|
|
static getDerivedStateFromError(error: Error): State {
|
|
return { error }
|
|
}
|
|
|
|
componentDidCatch(error: Error): void {
|
|
this.props.onError?.(error)
|
|
}
|
|
|
|
reset = (): void => {
|
|
this.setState({ error: null })
|
|
}
|
|
|
|
render(): React.ReactNode {
|
|
const { error } = this.state
|
|
if (error) {
|
|
const { fallback } = this.props
|
|
if (typeof fallback === 'function') return fallback(error, this.reset)
|
|
if (fallback !== undefined) return fallback
|
|
return null
|
|
}
|
|
return this.props.children
|
|
}
|
|
}
|