import { useState, useEffect } from 'react' import { useWaste } from '../store' interface Props { status: 'disconnected' | 'connecting' | 'connected' } // Parse URL search params for pre-filling the join form. // Supports: // ?invite=waste: waste: invite string (anchor + network name) // ?n= network name shorthand // ?network= network name // ?a= anchor URL hint (informational — daemon controls its anchor) function parseInviteParams(): { network: string; anchor: string; inviteString: string } { const p = new URLSearchParams(window.location.search) const inviteString = p.get('invite') ?? '' let network = p.get('n') ?? p.get('network') ?? '' let anchor = p.get('a') ?? p.get('anchor') ?? '' if (inviteString.startsWith('waste:')) { try { const json = JSON.parse(atob(inviteString.slice(6).replace(/-/g, '+').replace(/_/g, '/'))) network = network || json.network || '' anchor = anchor || json.anchor || '' } catch { /* ignore bad invite */ } } return { network, anchor, inviteString } } export function Onboarding({ status }: Props) { const { send, masterAlias, masterId, exportedBackup } = useWaste() const [network, setNetwork] = useState('') const [inviteString, setInviteString] = useState('') const [anchorHint, setAnchorHint] = useState('') const [exportPass, setExportPass] = useState('') const [importJson, setImportJson] = useState('') const [importPass, setImportPass] = useState('') const [importStatus, setImportStatus] = useState('') const [showBackup, setShowBackup] = useState(false) useEffect(() => { const { network: n, anchor: a, inviteString: inv } = parseInviteParams() if (n) setNetwork(n) if (a) setAnchorHint(a) if (inv) setInviteString(inv) }, []) function joinNetwork(e: React.FormEvent) { e.preventDefault() const name = network.trim() if (!name) return send({ type: 'join_network', network_name: name }) } function exportIdentity(e: React.FormEvent) { e.preventDefault() if (!exportPass.trim()) return setExportBlob('') send({ type: 'export_identity', passphrase: exportPass }) } function importIdentity(e: React.FormEvent) { e.preventDefault() if (!importJson.trim() || !importPass.trim()) return setImportStatus('Verifying…') send({ type: 'import_identity', backup: importJson, passphrase: importPass }) } const shortId = masterId ? masterId.slice(0, 16).replace(/(.{4})/g, '$1 ').trim() : null // ── disconnected / connecting ──────────────────────────────────────────────── if (status !== 'connected') { return (

waste

{status === 'connecting' ? (

Connecting to daemon…

) : ( <>

Daemon not running

Start the daemon to use the web UI:

./launch-web.sh

Or pass a custom alias and network:

ALIAS=alice NETWORK=friends ./launch-web.sh
)}
) } // ── connected — join form ──────────────────────────────────────────────────── return (

waste

{masterAlias && (
{masterAlias} {shortId && {shortId}}
)}
{anchorHint && (

via {anchorHint}

)} setNetwork(e.target.value)} placeholder="network name" autoComplete="off" autoFocus /> {inviteString && ( )}
{showBackup && (

Export your identity as an encrypted file. You can import it on any machine or in the TUI with --import-identity.

setExportPass(e.target.value)} placeholder="passphrase for backup" autoComplete="new-password" />
{exportedBackup && (