Watch party presentation mode: share a website, Google Slides/Docs or PDF; everyone sees it, Slides and PDFs follow the presenter's page
This commit is contained in:
@@ -12,6 +12,8 @@
|
||||
* any → all {type:'voice', on} (voice-chat presence)
|
||||
* any → one {type:'rtc', to, data} (WebRTC offer/answer/ICE, relayed)
|
||||
* guest → host {type:'cmd', cmd, …} (only when the host allows control)
|
||||
* host → all {type:'present', present: {url, page} | null} shared document/website
|
||||
* (guests too while the host allows control; kept for late joiners)
|
||||
* server → you {type:'hello', code, secret?, you, now, members, state, chat, allowControl}
|
||||
* server → all {type:'members', members} · {type:'ended'}
|
||||
* A party survives its host dropping for PARTY_GRACE_MS (a reload, a flaky
|
||||
@@ -93,7 +95,7 @@ export function createPartyHub({ now = () => Date.now() } = {}) {
|
||||
if (!p) {
|
||||
lost = !!d.code; // resuming a room the server no longer has (restart / expiry)
|
||||
const code = newCode();
|
||||
p = { direct: createDirectRelay(), code, secret: randomBytes(18).toString('base64url'), hostPid: d.pid, members: new Map(), state: null, chat: [], dj: createDJQueue(), allowControl: false, hostOnline: true, hostLeftAt: 0 };
|
||||
p = { present: null, direct: createDirectRelay(), code, secret: randomBytes(18).toString('base64url'), hostPid: d.pid, members: new Map(), state: null, chat: [], dj: createDJQueue(), allowControl: false, hostOnline: true, hostLeftAt: 0 };
|
||||
parties.set(code, p);
|
||||
}
|
||||
p.hostPid = d.pid;
|
||||
@@ -112,7 +114,7 @@ export function createPartyHub({ now = () => Date.now() } = {}) {
|
||||
p.members.set(d.pid, { ws, pid: d.pid, name: d.name, voice: false, lastChat: 0, key: d.key || '', host: d.role === 'host', tokens: MSG_BURST, refill: now() });
|
||||
send(ws, {
|
||||
type: 'hello', code: p.code, secret: d.role === 'host' ? p.secret : undefined, you: d.pid, lost,
|
||||
now: now(), members: members(p), state: p.state, chat: p.chat, dj: queueSnapshot(p.dj), allowControl: p.allowControl,
|
||||
now: now(), members: members(p), state: p.state, chat: p.chat, present: p.present, dj: queueSnapshot(p.dj), allowControl: p.allowControl,
|
||||
});
|
||||
broadcast(p, { type: 'members', members: members(p), joined: d.name }, ws);
|
||||
}
|
||||
@@ -140,6 +142,15 @@ export function createPartyHub({ now = () => Date.now() } = {}) {
|
||||
} else if (m.type === 'state' && isHost) {
|
||||
p.state = { ...(m.state || {}), ts: now() };
|
||||
broadcast(p, { type: 'state', state: p.state }, ws);
|
||||
} else if (m.type === 'present' && (isHost || p.allowControl)) {
|
||||
const v = m.present;
|
||||
if (v === null) p.present = null;
|
||||
else {
|
||||
const url = String(v?.url || '').trim();
|
||||
if (!/^https?:\/\/[^\s<>"]{3,2000}$/i.test(url)) return;
|
||||
p.present = { url, page: Math.max(1, Math.min(9999, Math.floor(Number(v.page) || 1))), by: me.name, at: now() };
|
||||
}
|
||||
broadcast(p, { type: 'present', present: p.present });
|
||||
} else if (m.type === 'settings' && isHost) {
|
||||
p.allowControl = !!m.allowControl;
|
||||
broadcast(p, { type: 'settings', allowControl: p.allowControl });
|
||||
|
||||
Reference in New Issue
Block a user