feat: F5.3b the editor on a project of server F
?project=<uuid> opens the shared document over /sync and the room over /sim (RemoteSimHost: messages wait for the socket, a lost socket comes back with what it watched, F's refusal or parting ends it); instead of the editor, a word why when F refuses or lets go. F tells each connection its access at once. Who only reads sees everything and changes nothing: no palette or undo, a locked surface, the sheet's fields disabled (tabs stay), no starting or stopping; thawed, the document comes anew. The toolbar names who started the simulation. Refusals carry no message, so Hocuspocus writes nothing outside F's JSON log. A browser test runs three people against a real F. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This commit is contained in:
@@ -0,0 +1,51 @@
|
||||
// The editor on a project of server F (F5.3b): the shared document over
|
||||
// /sync and the room over /sim, both signed in by the cookie F set at
|
||||
// /__auth. Until the document is there, a word that it is coming; when F
|
||||
// refuses or lets go — of the document or the room — a word why, instead of
|
||||
// the editor. Who only reads gets the editor locked (Editor's readOnly).
|
||||
|
||||
import { useEffect, useMemo, useState } from 'react';
|
||||
import { Editor } from '../Editor.js';
|
||||
import { CATALOGS, useLanguage, useTranslate } from '../i18n/i18n.js';
|
||||
import type { SimHostFactory } from '../simulation/useSimulation.js';
|
||||
import { RemoteSimHost, simUrl } from './remoteSimHost.js';
|
||||
import { connectSharedDoc, type SharedDocState } from './sharedDoc.js';
|
||||
|
||||
export interface SharedProjectProps {
|
||||
/** F's address. */
|
||||
readonly server: string;
|
||||
readonly project: string;
|
||||
}
|
||||
|
||||
export function SharedProject({ server, project }: SharedProjectProps) {
|
||||
const t = useTranslate();
|
||||
const language = useLanguage();
|
||||
const [state, setState] = useState<SharedDocState>({ kind: 'connecting' });
|
||||
/** F ended the room for this page: the editor goes as when the document is let go. */
|
||||
const [roomEnd, setRoomEnd] = useState<string | null>(null);
|
||||
useEffect(() => connectSharedDoc({ server, project, onState: setState }), [server, project]);
|
||||
const createSimHost = useMemo<SimHostFactory>(
|
||||
() => () => {
|
||||
const host = new RemoteSimHost(simUrl(server, project));
|
||||
host.onEnd((end) => setRoomEnd(end.reason));
|
||||
return host;
|
||||
},
|
||||
[server, project],
|
||||
);
|
||||
const ended = roomEnd ?? (state.kind === 'refused' || state.kind === 'closed' ? state.reason : null);
|
||||
if (ended !== null) {
|
||||
const key = `remote.${ended}`;
|
||||
return (
|
||||
<p role="alert" className="remote-message">
|
||||
{key in CATALOGS[language] ? t(key) : t('remote.refused', { reason: ended })}
|
||||
</p>
|
||||
);
|
||||
}
|
||||
if (state.kind !== 'ready')
|
||||
return (
|
||||
<p role="status" className="remote-message">
|
||||
{t('remote.connecting')}
|
||||
</p>
|
||||
);
|
||||
return <Editor key={state.doc.guid} doc={state.doc} createSimHost={createSimHost} readOnly={state.readOnly} frozen={state.frozen} />;
|
||||
}
|
||||
Reference in New Issue
Block a user