Files
nym/documentation/docs/components/playground/messaging-section.tsx
T
mfahampshire 7c890ea0c5 TS SDK docs (#6840)
* First sweep packages + some minor tweaking

* Second sweep

* Regenerate lockfile + package.json mods

* Regenerate lockfile again

* Fix CI

* Fix CI again

* All building properly

* unblock

* Tweak examples

* Comments + readme + fix rotten unit test

* First pass docs

* Big pass

* Massive pass on new docs

* Update integrations.md w mobile

* Partial overhaul review

* new playground + big pass

* new fix lychee err

* IPR notice tweak
2026-06-09 13:31:08 +00:00

140 lines
5.2 KiB
TypeScript

import React, { useEffect, useRef, useState } from 'react';
import { box, row, legend, sub, input, Button, LogPanel, StatusText, useLogs, type Status } from './ui';
// Raw mixnet messaging demo, styled to match the mix-* playground sections.
// Uses @nymproject/sdk-full-fat (a separate wasm client from the smolmix tunnel)
// to create a Nym client, send a message to a Nym address, and receive it.
//
// The SDK is imported dynamically on Connect, not at module scope: that keeps
// the page SSR/SSG-safe and means the second wasm runtime + gateway connection
// only load when the visitor opts in.
const nymApiUrl = 'https://validator.nymtech.net/api';
const preferredGateway = 'q2A2cbooyC16YJzvdYaSMH9X3cSiieZNtfBr8cE8Fi1';
// Minimal shape of the bits of NymMixnetClient we use, to type the `any` from
// the dynamic import.
interface MessagingClient {
client: {
start(opts: {
clientId: string;
nymApiUrl: string;
forceTls?: boolean;
preferredGateway?: string;
}): Promise<void>;
stop(): Promise<void>;
send(args: { payload: { message: string; mimeType: string }; recipient: string }): Promise<void>;
};
events: {
subscribeToLoaded(cb: (e: { args: unknown }) => void): void;
subscribeToConnected(cb: (e: { args: { address: string } }) => void): void;
subscribeToTextMessageReceivedEvent(cb: (e: { args: { payload: string } }) => void): void;
};
}
export function MessagingDemo() {
const { log, lines } = useLogs();
const [status, setStatus] = useState<Status>({ text: 'Not connected', colour: 'gray' });
const [connected, setConnected] = useState(false);
const [busy, setBusy] = useState(false);
const [selfAddress, setSelfAddress] = useState('');
const [recipient, setRecipient] = useState('');
const [message, setMessage] = useState('hello through the mixnet');
const clientRef = useRef<MessagingClient | null>(null);
useEffect(() => {
return () => {
clientRef.current?.client.stop().catch(() => {});
};
}, []);
async function connect() {
setBusy(true);
setStatus({ text: 'Loading SDK...', colour: 'orange' });
log('msg', 'Loading @nymproject/sdk-full-fat (wasm)...');
try {
// @ts-ignore -- published separately; dynamic import keeps the wasm off SSR and lazy
const mod = await import('@nymproject/sdk-full-fat');
const nym = (await mod.createNymMixnetClient()) as unknown as MessagingClient;
clientRef.current = nym;
nym.events.subscribeToLoaded(() => log('msg', 'client wasm loaded', 'green'));
nym.events.subscribeToConnected((e) => {
const addr = e.args.address;
setSelfAddress(addr);
setRecipient((r) => r || addr); // default to sending to yourself
setConnected(true);
setStatus({ text: 'Connected', colour: 'green' });
log('msg', `connected; self address: ${addr}`, 'green');
});
nym.events.subscribeToTextMessageReceivedEvent((e) => {
log('msg', `received: ${e.args.payload}`, 'green');
});
log('msg', 'Starting client and connecting to a gateway...');
setStatus({ text: 'Connecting to mixnet...', colour: 'orange' });
await nym.client.start({ clientId: crypto.randomUUID(), nymApiUrl, forceTls: true, preferredGateway });
} catch (err) {
setStatus({ text: 'Failed', colour: 'red' });
log('msg', `error: ${err instanceof Error ? err.message : String(err)}`, 'red');
setBusy(false);
}
setBusy(false);
}
async function send() {
const nym = clientRef.current;
if (!nym || !recipient || !message) return;
try {
await nym.client.send({ payload: { message, mimeType: 'text/plain' }, recipient });
log('msg', `sent: ${message}`);
} catch (err) {
log('msg', `send failed: ${err instanceof Error ? err.message : String(err)}`, 'red');
}
}
return (
<div style={box}>
<div style={legend}>Raw mixnet messaging</div>
<div style={sub}>
Creates a client with <code>@nymproject/sdk-full-fat</code> and sends a message to a Nym
address through the mixnet (defaults to your own address). This loads a separate wasm runtime
and connects its own client, so it is opt-in.
</div>
<div style={{ ...row, marginTop: '0.75rem' }}>
<Button onClick={connect} disabled={busy || connected}>
{busy ? 'Connecting...' : 'Connect'}
</Button>
<StatusText status={status} />
</div>
{selfAddress && (
<div style={{ ...sub, wordBreak: 'break-all', margin: '0 0 0.5rem' }}>
Your address: <code>{selfAddress}</code>
</div>
)}
<div style={row}>
<input
style={input}
value={recipient}
onChange={(e) => setRecipient(e.target.value)}
placeholder="recipient Nym address"
disabled={!connected}
/>
</div>
<div style={row}>
<input
style={input}
value={message}
onChange={(e) => setMessage(e.target.value)}
placeholder="message"
disabled={!connected}
/>
<Button onClick={send} disabled={!connected}>
Send
</Button>
</div>
<LogPanel lines={lines('msg')} placeholder="Press Connect to create a mixnet client." />
</div>
);
}