feat: F5.4 presence: who else is in the project
Over Yjs awareness each editor tells who it is (F names the person in the access message), the device it has chosen and the windows it has open, and nothing else: no pointer, nothing that changes without a cause, sent only when it changed. Others' states are read with care. A frame in their colour round a device they chose, a dot on a window they have open too, a circle with initials each beside the mode buttons; the bridge tells the dashboard who is there. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This commit is contained in:
@@ -10,6 +10,7 @@ import { randomUUID } from 'node:crypto';
|
||||
import { afterAll, beforeAll, describe, expect, it } from 'vitest';
|
||||
import WebSocket from 'ws';
|
||||
import { addDevice } from '@mdg-filius/doc';
|
||||
import { peersOf } from '../../editor/src/remote/presence.js';
|
||||
import { connectSharedDoc, type SharedDocState } from '../../editor/src/remote/sharedDoc.js';
|
||||
import { PostgresSessions, PostgresStore } from '../src/index.js';
|
||||
import { testDatabase, type TestDatabase } from './database.js';
|
||||
@@ -68,6 +69,29 @@ describe("the editor's shared document", () => {
|
||||
await server.close();
|
||||
});
|
||||
|
||||
it('knows who it is, from F, and sees the others through the awareness', async () => {
|
||||
const server = await startTestServer(store, sessions);
|
||||
const project = await newProject(store);
|
||||
const [miaId, benId] = [randomUUID(), randomUUID()];
|
||||
const mia = connectEditor(server, project, await signIn(server, project, { user: miaId, name: 'Mia' }));
|
||||
const ben = connectEditor(server, project, await signIn(server, project, { user: benId, name: 'Ben', level: 'read' }));
|
||||
await mia.ready();
|
||||
await ben.ready();
|
||||
const miaReady = mia.last() as Extract<SharedDocState, { kind: 'ready' }>;
|
||||
const benReady = ben.last() as Extract<SharedDocState, { kind: 'ready' }>;
|
||||
expect(miaReady.you).toEqual({ id: miaId, name: 'Mia', color: '#3366cc' });
|
||||
expect(benReady.you).toMatchObject({ id: benId, name: 'Ben' });
|
||||
// A reader is seen too: the awareness is not the document.
|
||||
benReady.awareness.setLocalState({ v: 1, user: benReady.you, selection: 'router', windows: [] });
|
||||
await until(() => peersOf(miaReady.awareness.getStates(), miaReady.awareness.clientID).length === 1);
|
||||
expect(peersOf(miaReady.awareness.getStates(), miaReady.awareness.clientID)[0]!.state).toEqual({ v: 1, user: benReady.you, selection: 'router', windows: [] });
|
||||
// Gone, he is gone for her too.
|
||||
ben.close();
|
||||
await until(() => peersOf(miaReady.awareness.getStates(), miaReady.awareness.clientID).length === 0);
|
||||
mia.close();
|
||||
await server.close();
|
||||
});
|
||||
|
||||
it('reads while the project is frozen and gets the document anew when it is thawed', async () => {
|
||||
const server = await startTestServer(store, sessions);
|
||||
const project = await newProject(store);
|
||||
|
||||
Reference in New Issue
Block a user