'use client'; import { useState, useEffect, useCallback } from 'react'; import { listIPNSKeys, ipnsPublish, ipnsResolve, ipnsGenKey, type IPNSKey } from '@/lib/api/ipns'; import PortalLayout from '@/app/layout-portal'; import { RefreshCw, ExternalLink, Key, FileText, Plus } from 'lucide-react'; import { SkeletonTable } from '@/components/Skeleton'; import ErrorBoundary from '@/components/ErrorBoundary'; import type { Status } from './helpers'; export default function IPNSPage() { const [keys, setKeys] = useState([]); const [status, setStatus] = useState('loading'); const [error, setError] = useState(''); // Publish form const [pubCid, setPubCid] = useState(''); const [pubKey, setPubKey] = useState('self'); const [pubResult, setPubResult] = useState<{ name: string; value: string } | null>(null); const [pubLoading, setPubLoading] = useState(false); const [pubError, setPubError] = useState(''); // Resolve form const [resolveName, setResolveName] = useState(''); const [resolveResult, setResolveResult] = useState(''); const [resolveLoading, setResolveLoading] = useState(false); const [resolveError, setResolveError] = useState(''); // Gen key form const [genName, setGenName] = useState(''); const [genLoading, setGenLoading] = useState(false); const [genError, setGenError] = useState(''); const loadKeys = useCallback(async () => { setStatus('loading'); setError(''); try { const k = await listIPNSKeys(); setKeys(k); setStatus('success'); } catch (e) { setError(e instanceof Error ? e.message : 'Failed to load keys'); setStatus('error'); } }, []); useEffect(() => { loadKeys(); }, [loadKeys]); const handlePublish = async (e: React.FormEvent) => { e.preventDefault(); if (!pubCid.trim()) return; setPubLoading(true); setPubError(''); setPubResult(null); try { const res = await ipnsPublish(pubCid.trim(), pubKey); setPubResult(res); } catch (e) { setPubError(e instanceof Error ? e.message : 'Publish failed'); } finally { setPubLoading(false); } }; const handleResolve = async (e: React.FormEvent) => { e.preventDefault(); if (!resolveName.trim()) return; setResolveLoading(true); setResolveError(''); setResolveResult(''); try { const path = await ipnsResolve(resolveName.trim()); setResolveResult(path); } catch (e) { setResolveError(e instanceof Error ? e.message : 'Resolve failed'); } finally { setResolveLoading(false); } }; const handleGenKey = async (e: React.FormEvent) => { e.preventDefault(); if (!genName.trim()) return; setGenLoading(true); setGenError(''); try { await ipnsGenKey(genName.trim()); setGenName(''); await loadKeys(); } catch (e) { setGenError(e instanceof Error ? e.message : 'Key generation failed'); } finally { setGenLoading(false); } }; return ( {/* Header */}

IPNS

InterPlanetary Name System — human-readable names for IPFS content

{/* Keys */}

Signing Keys

{keys.length} key{keys.length !== 1 ? 's' : ''}
{status === 'loading' && (
)} {status === 'error' && (
{error}
)} {status === 'success' && keys.length === 0 && (

No IPNS keys found.

)} {keys.length > 0 && (
{keys.map((key) => (

{key.name}

{key.id}

))}
)} {/* Generate key */}
setGenName(e.target.value)} placeholder="key-name" className="flex-1 px-3 py-1.5 text-sm bg-surface-900 border border-surface-700 rounded-lg text-surface-200 placeholder-surface-600 focus:outline-none focus:border-brand-500/50" required />
{genError &&

{genError}

}
{/* Two-column: Publish + Resolve */}
{/* Publish */}

Publish

Point an IPNS name to a CID. Publish is signed with the selected key.

setPubCid(e.target.value)} placeholder="Qm... or bafy..." className="w-full px-3 py-1.5 text-sm bg-surface-900 border border-surface-700 rounded-lg text-surface-200 placeholder-surface-600 focus:outline-none focus:border-brand-500/50 font-mono" required />
{pubError &&

{pubError}

} {pubResult && (

Name: {pubResult.name}

Value: {pubResult.value}

)}
{/* Resolve */}

Resolve

Resolve an IPNS name or peer ID to the current IPFS path.

setResolveName(e.target.value)} placeholder="k51... or /ipns/example.com" className="w-full px-3 py-1.5 text-sm bg-surface-900 border border-surface-700 rounded-lg text-surface-200 placeholder-surface-600 focus:outline-none focus:border-brand-500/50 font-mono" required />
{resolveError &&

{resolveError}

} {resolveResult && (

Resolved to:

{resolveResult}

)}
); }