import { parseSpaceUri } from '@pdsjs/spaces/uri'; import { useMutation, useQuery, useQueryClient } from '@tanstack/react-query'; import { ArrowRight, Boxes, Plus } from 'lucide-react'; import { useState } from 'react'; import { AppTile } from '#/components/atoms/app-tile.jsx'; import { Badge } from '#/components/atoms/badge.jsx'; import { Button } from '#/components/atoms/button.jsx'; import { Card } from '#/components/atoms/card.jsx'; import { Link } from '#/components/atoms/link.jsx'; import { Notice } from '#/components/atoms/notice.jsx'; import { Skeleton } from '#/components/atoms/skeleton.jsx'; import { ActorAvatar } from '#/components/molecules/actor-avatar.jsx'; import { AtUri } from '#/components/molecules/at-uri.jsx'; import { EmptyState } from '#/components/molecules/empty-state.jsx'; import { Identity } from '#/components/molecules/identity.jsx'; import { TextField } from '#/components/molecules/text-field.jsx'; import { spaceParts } from '#/lib/format.js'; import { href, useNavigate } from '#/lib/navigation.jsx'; import { useSession } from '#/lib/session.jsx'; import { createSpace, listSpaces } from '#/lib/spaces.js'; /** One row in the list: what the space is, and who governs it. */ function SpaceRow({ uri, isOwner, did }) { const { spaceDid, spaceType, skey } = spaceParts(uri); return ( {/* The space type is an NSID, and the domain it reverses to is the app the space belongs to. Its icon says at a glance which app a space is for, which the type alone takes a moment to read. */}
{spaceType} {isOwner && you host this}
{skey}
{spaceDid === did ? ( 'Under your own authority' ) : ( <> Under )}
); } /** * The way into a space this list does not name. * * `listSpaces` reports the spaces this account has written to, which is what a * PDS tracks. A space somebody has admitted this account to but which it has * never written in appears nowhere, and its URI is the only way to reach it. */ function OpenByUri() { const navigate = useNavigate(); const [uri, setUri] = useState(''); const [error, setError] = useState(null); function open(event) { event.preventDefault(); const trimmed = uri.trim(); try { parseSpaceUri(trimmed); } catch (failure) { setError(failure.message); return; } navigate(href('/space', { uri: trimmed })); } return (
{ setUri(event.target.value); setError(null); }} placeholder="at://did:plc:…/space/com.example.forum/general" spellCheck="false" autoCapitalize="none" autoCorrect="off" aria-label="Open a space by URI" className="h-9 min-w-0 flex-1 rounded-lg border border-input bg-transparent px-3 font-mono text-[12.5px] text-foreground outline-none placeholder:text-faint focus-visible:border-ring" />
{error && {error}}
); } /** The form for a space anchored on this account. */ function NewSpace({ onCreated }) { const { session } = useSession(); const [open, setOpen] = useState(false); const [type, setType] = useState(''); const [skey, setSkey] = useState(''); const create = useMutation({ mutationFn: () => createSpace(session, { type, skey: skey || undefined }), onSuccess: () => { setOpen(false); setType(''); setSkey(''); onCreated(); }, }); if (!open) { return ( ); } return ( setType(event.target.value)} /> setSkey(event.target.value)} /> {create.error && {create.error.message}}
); } export function SpacesPage() { const { session, writable } = useSession(); const client = useQueryClient(); const { data, error, isPending } = useQuery({ queryKey: ['spaces', session.did], queryFn: () => listSpaces(session), }); if (isPending) { return (
{[0, 1, 2].map((row) => ( ))}
); } if (error) { return ( {error.name === 'MethodNotImplemented' ? 'This PDS does not serve permissioned spaces.' : error.message} ); } const spaces = data.spaces || []; return (

The spaces your repo holds data in. A PDS tracks the spaces its own user has written to, so a space you have been admitted to but never written in is not listed here.

{spaces.length === 0 ? ( } title="No spaces" description="This account has written nothing into a permissioned space. Open one above by its URI." /> ) : ( spaces.map((space) => ( )) )} {writable && ( client.invalidateQueries({ queryKey: ['spaces'] })} /> )}
); }