WizardConnect/docs/react.md
Dagur Valberg Johannsson 8cd22c34ba
Add session persistence and refactor dapp manager
Keep session persistance by default, such that when reloading a dapp,
the wizardconnect session is kept alive.

Improve signature request interface. Simplify the react API.
2026-04-03 17:11:18 +02:00

184 lines
5.8 KiB
Markdown

# React integration
`@wizardconnect/react` provides React components and hooks for dapp developers who want to add
WizardConnect wallet connectivity without writing relay boilerplate.
## Quick start
```tsx
import { useWizardConnect, WizardConnectQRDialog } from "@wizardconnect/react";
function App() {
const wc = useWizardConnect({ dappName: "My Dapp" });
return (
<>
<button onClick={wc.connect} disabled={wc.state !== "idle"}>
Connect Wallet
</button>
{wc.uri && wc.qrUri && (
<WizardConnectQRDialog
show={wc.state === "connecting"}
onClose={() => wc.disconnect()}
uri={wc.uri}
qrUri={wc.qrUri}
/>
)}
{wc.state === "connected" && (
<p>Connected to {wc.walletName}</p>
)}
</>
);
}
```
## useWizardConnect
The `useWizardConnect` hook manages the full relay lifecycle:
1. **`connect()`** — calls `initiateDappRelay()`, creates a `DappConnectionManager`, sets
`uri`/`qrUri` for QR display, and transitions to `"connecting"`.
2. **Key exchange** — persists the wallet's public key to session storage.
3. **`walletready`** — transitions to `"connected"` and populates `walletName`/`walletIcon`.
4. **Auto-reconnect** — on mount, checks session storage for a stored session with a
`walletPublicKey` and automatically reconnects. During auto-reconnect, `uri`/`qrUri` remain
`null` (no QR code to display — credentials are already known).
5. **`disconnect()`** — sends a courtesy disconnect, cleans up the relay, and clears session storage.
6. **Remote disconnect** — when the wallet sends a disconnect, the hook transitions to
`"disconnected"`, clears the session, and tears down the relay.
### Return value
```typescript
interface UseWizardConnectResult {
state: "idle" | "connecting" | "connected" | "disconnected";
manager: DappConnectionManager | null;
uri: string | null;
qrUri: string | null;
walletName: string | null;
walletIcon: string | null;
connect: () => boolean;
disconnect: () => Promise<void>;
error: string | null;
}
```
### Using the manager
Once `state === "connected"`, the `manager` is a live `DappConnectionManager` from
`@wizardconnect/dapp`. Use it to derive addresses and request signatures:
```typescript
// Derive a receive address pubkey
const pubkey = wc.manager.getPubkey(0, 0n);
// Send a sign request (auto-fills action, sequence, time)
const response = await wc.manager.signTransaction({
transaction: { transaction: txHex, sourceOutputs, broadcast: true },
inputPaths: [[0, "receive", 0]],
});
```
See the [dapp integration docs](dapp.md#sending-a-sign-request) for cancellation via
`AbortSignal` and the low-level `sendSignRequest` API.
For most dapps, you'll wrap the manager in an app-specific wallet adapter (like
`RelayWalletDapp` in the Cauldron and Moria codebases).
## WizardConnectQRDialog
A framework-independent modal dialog for displaying the connection QR code. Uses inline styles
(no Tailwind or CSS framework dependency) with a dark theme by default.
### Customization
Colors, text, and logos are all customizable via props:
```tsx
<WizardConnectQRDialog
show={true}
onClose={handleClose}
uri={connection.uri}
qrUri={connection.qrUri}
title="My Protocol"
subtitle="Scan to pair your wallet"
logoUrl="/my-logo.png"
theme={{
dialogBackground: "#0f172a",
headerBackground: "#0f172a",
borderColor: "#334155",
}}
onCopy={(uri) => {
navigator.clipboard.writeText(uri);
showToast("Copied!");
}}
/>
```
The `onCopy` callback replaces the default `navigator.clipboard.writeText` behavior, which is
useful when the dapp has its own toast/notification system.
## AlphanumericQRCode
A standalone canvas-based QR code component. Uses QR Alphanumeric mode with error correction
level H (30% recovery), which allows a center logo overlay without breaking the code.
```tsx
<AlphanumericQRCode
value="WIZ://..."
size={280}
foreground="#1e2a4a"
background="#ffffff"
logoUrl="/logo.png"
/>
```
## Session persistence
By default, `useWizardConnect` persists session data to localStorage under the
`wizardconnect-session` key. The stored session includes:
- **Relay credentials** (`privateKey`, `secret`) — saved on `connect()`
- **Wallet public key** (`walletPublicKey`) — saved after key exchange
- **Wallet identity** (`walletName`, `walletIcon`) — saved on `walletready`
- **Xpub paths** (`paths`) — saved on `walletready`
On page refresh, the hook auto-reconnects if a stored session with `walletPublicKey` exists.
Xpub paths and wallet name are restored immediately so `getPubkey()` works and the UI can
display the wallet name before the wallet app responds. During auto-reconnect, `uri` and
`qrUri` remain `null` — the QR dialog should not be shown. The typical pattern is:
```tsx
{wc.uri && wc.qrUri && (
<WizardConnectQRDialog
show={wc.state === "connecting"}
onClose={() => wc.disconnect()}
uri={wc.uri}
qrUri={wc.qrUri}
/>
)}
```
This naturally hides the dialog during auto-reconnect since `wc.uri` is `null`.
If the wallet sends a disconnect, the hook clears the session automatically and transitions
to `"disconnected"`. Dapps should listen for this state change to update their UI (e.g.
clear the wallet from their store).
### Customization
```typescript
useWizardConnect({
sessionKey: "my-app-session", // custom storage key (default: "wizardconnect-session")
persistSession: false, // disable persistence entirely
storage: myCustomStorage, // custom SessionStorage backend (default: localStorage)
});
```
The `storage` option accepts any object with `getItem`, `setItem`, and `removeItem` methods
(the standard Web Storage API). This is useful for React Native or server-side rendering
where `localStorage` is not available.
On disconnect, the stored session is cleared.