The editor was only open to somebody with an account. A guest now picks
a demo project on the sign-in screen and gets the whole editor on a copy
held in the tab. The server writes nothing for a guest, so there is no
guest session, no guest folder and no quota to keep.
- Demo copies are frozen copies like a release, in data/demos/. An
administrator makes, updates and removes one with "Demo copy..." in
the project's main menu. GET demos and GET demos/{token} need no
session. An empty project is just a demo that is empty.
- Demo material: an administrator offers an uploaded file with "Offer
as demo" in the pickers (PATCH {demo}, data/demo-files.json). Every
list carries it with demo: true, for accounts and guests alike.
Signed out, a list holds the demo material alone, without the owner.
- An offered file is only an administrator's to delete. A file a demo
copy names counts as used, like one a release names.
- The guest's project lives in MemoryProjectStore. Every store that
would write refuses in the tab before a request is made, and the
autosave is not attached at all.
- The main menu drops projects, snapshots and release for a guest. The
pickers drop Upload and Delete, a dropped or pasted file is refused,
and a recording is saved as a download.
- The toolbar says the work is not saved. The Account menu holds
"Sign in..." instead of Logout and the password.
- A guest's demo never goes into the address: a reload brings the
sign-in screen back, not the copy. Found by e2e 70.
- GET plugins needs no session any more, since a demo may switch one
on. It only reads the server's own modules.
Tests:
- e2e 70 (atx): ALL PASSED, 13 checks. Counter-test with the guest
autosave sent to the server: 70f and 70g red on the PUT.
- tests/core/guest.test.ts: 9 tests. PHP: demos 20, files 46,
rights 80 checks (the matrix asks every new route).
- Gate (atx): CHECK EXIT 0, 314 files, 4852 tests.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
32 lines
1.3 KiB
TypeScript
32 lines
1.3 KiB
TypeScript
import type { SaveStatus } from './state';
|
||
|
||
/** When a snapshot was made, in the local time of the browser: `2026-09-25 17:39`. */
|
||
export function snapshotTime(at: Date): string {
|
||
const two = (n: number) => String(n).padStart(2, '0');
|
||
return `${at.getFullYear()}-${two(at.getMonth() + 1)}-${two(at.getDate())} ${two(at.getHours())}:${two(at.getMinutes())}`;
|
||
}
|
||
|
||
const COPY_WORDS = { snapshot: 'take a snapshot of', release: 'release', demo: 'make a demo copy of' };
|
||
|
||
/**
|
||
* Why a snapshot cannot be made or restored right now, or null when it can (#231). The server copies
|
||
* what it has stored, so the editor has to have saved everything first: a snapshot without the
|
||
* last edits would be a stand nobody ever saw. A release (#266) and a demo copy (#428) are copies as
|
||
* well and ask the same.
|
||
*/
|
||
export function snapshotBlocker(
|
||
status: SaveStatus,
|
||
copy: 'snapshot' | 'release' | 'demo' = 'snapshot',
|
||
): string | null {
|
||
switch (status) {
|
||
case 'saved':
|
||
return null;
|
||
case 'conflict':
|
||
return 'This project was changed elsewhere. Reload to continue.';
|
||
case 'error':
|
||
return `The project could not be saved, so there is nothing current to ${COPY_WORDS[copy]}`;
|
||
default:
|
||
return 'The latest changes are not saved yet – stop the game or wait a moment';
|
||
}
|
||
}
|