feat: bind public vestibules to domain login gates

This commit is contained in:
冰朔 2026-08-10 05:59:17 +08:00
commit b24d5b5452
6 changed files with 147 additions and 22 deletions

View file

@ -1,4 +1,4 @@
import { useCallback, useEffect, useMemo, useState, type CSSProperties, type PointerEvent as ReactPointerEvent } from 'react';
import { useCallback, useEffect, useMemo, useRef, useState, type CSSProperties, type PointerEvent as ReactPointerEvent } from 'react';
import { api, DocTreeNode, DocContent, type ChannelState, type ModuleManifest } from './api';
import { DocTree } from './components/DocTree';
import { Editor } from './components/Editor';
@ -13,6 +13,7 @@ import { ModuleLibrarySheet } from './components/ModuleLibrarySheet';
import { cleanDisplayText } from './presentation';
import { WorldEntry } from './components/WorldEntry';
import { DomainConnectionSheet } from './components/DomainConnectionSheet';
import { createDomainEntryTarget, type DomainEntryTarget, type DomainNodeType } from './domain-entry-state';
import type { DomainRouteId } from './public-domain-directory';
type View = 'editor' | 'history';
@ -84,6 +85,7 @@ export default function App() {
const [activeModule, setActiveModule] = useState<ModuleId>('knowledge');
const [storageSheetOpen, setStorageSheetOpen] = useState(false);
const [domainConnectionOpen, setDomainConnectionOpen] = useState(false);
const [domainEntryTarget, setDomainEntryTarget] = useState<DomainEntryTarget | null>(null);
const [storageSheetInitialMode, setStorageSheetInitialMode] = useState<'local' | 'server' | undefined>();
const [serverSession, setServerSession] = useState<ServerSession>({ authenticated: false, nodeId: '' });
const [serverProfiles, setServerProfiles] = useState<ServerProfile[]>([]);
@ -101,6 +103,7 @@ export default function App() {
const [lastChannelReceipt, setLastChannelReceipt] = useState('');
const [worldEntered, setWorldEntered] = useState(false);
const [domainAccess, setDomainAccess] = useState<DomainAccessStatus>({ blockers: [], runtimeReady: false, stage: 'checking' });
const domainAccessRequest = useRef(0);
const storageMode = repositoryStatus?.remote ? 'server' : 'local';
const storageLabel = storageMode === 'server' ? '服务器已托管' : '仅本机';
@ -176,19 +179,35 @@ export default function App() {
}
}, []);
const refreshDomainAccess = useCallback(async () => {
const refreshDomainAccess = useCallback(async (domainId = 'DOM-FIFTH-0001') => {
const request = ++domainAccessRequest.current;
const server = (window as any).hololake?.server;
if (!server?.domainAccess) {
setDomainAccess({ blockers: ['desktop_runtime_required'], runtimeReady: false, stage: 'login-required' });
if (request === domainAccessRequest.current) setDomainAccess({ blockers: ['desktop_runtime_required'], domainId, runtimeReady: false, stage: 'login-required' });
return;
}
try {
setDomainAccess(await server.domainAccess('DOM-FIFTH-0001'));
const result = await server.domainAccess(domainId);
if (request === domainAccessRequest.current) setDomainAccess(result);
} catch {
setDomainAccess({ blockers: ['domain_access_probe_failed'], runtimeReady: false, stage: 'login-required' });
if (request === domainAccessRequest.current) setDomainAccess({ blockers: ['domain_access_probe_failed'], domainId, runtimeReady: false, stage: 'login-required' });
}
}, []);
const openDomainConnection = useCallback((routeId: DomainRouteId | null) => {
const target = routeId ? createDomainEntryTarget(routeId) : null;
setDomainEntryTarget(target);
setDomainConnectionOpen(true);
if (target) {
setDomainAccess({ blockers: [], domainId: target.domain.stableDomainId, runtimeReady: false, stage: 'checking' });
void refreshDomainAccess(target.domain.stableDomainId);
}
}, [refreshDomainAccess]);
const selectDomainNodeType = useCallback((nodeType: DomainNodeType) => {
setDomainEntryTarget(current => current ? createDomainEntryTarget(current.domain.routeId, nodeType) : null);
}, []);
const refreshChannel = useCallback(async () => {
try {
const [channel, registry] = await Promise.all([api.getChannel(), api.getModules()]);
@ -393,9 +412,7 @@ export default function App() {
setActiveModule('knowledge');
setWorldEntered(true);
}}
onOpenConnection={() => {
setDomainConnectionOpen(true);
}}
onOpenConnection={openDomainConnection}
/>
)}
<div
@ -581,10 +598,13 @@ export default function App() {
/>
<DomainConnectionSheet
access={domainAccess}
target={domainEntryTarget}
open={domainConnectionOpen}
onClose={() => setDomainConnectionOpen(false)}
onSelectTarget={routeId => openDomainConnection(routeId)}
onSelectNodeType={selectDomainNodeType}
onEnterRuntime={() => {
if (!domainAccess.runtimeReady) return;
if (!domainEntryTarget || domainEntryTarget.domain.routeId !== 'fifth' || !domainAccess.runtimeReady || domainAccess.domainId !== domainEntryTarget.domain.stableDomainId) return;
setActiveRoute('fifth');
setActiveModule('knowledge');
setWorldEntered(true);