feat: close-to-tray, meeting lobby, speaker select, link expiry, mobile RC touch (0.1.14/batch72)
General #1/#2 (closed-app notifications): the desktop app now CLOSES TO TRAY instead of quitting, keeping its chat WebSocket alive so calls/messages still notify. Tray icon + menu (Open / Quit), single-instance lock, first-close hint. Guest #4 (lobby/admit): meetings can require the host to admit guests joining by link. Setting on the schedule form ("Guests must be admitted by the host", default on) + ad-hoc default. Guests wait on a "waiting to be let in" screen; the host gets an Admit/Deny prompt; auto-cleanup on leave. Logged-in members always join directly. Guest #5 (speaker): headphones/speaker output picker in the meeting (setSinkId), remembered and applied to every tile. Guest #3 (link expiry): guest link/token dies ~2h after a scheduled meeting's end (HTTP 410) with a clear message; live-room links expire when the room empties. RC #4 (mobile): touch→mouse mapping so a phone/tablet viewer can control (tap=click, drag=move). Uses the same letterbox-correct coordinate mapping. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
This commit is contained in:
+6
-6
@@ -297,9 +297,9 @@ const attachments = {
|
||||
};
|
||||
|
||||
const scheduledMeetings = {
|
||||
create: ({ id, teamId, groupId, roomCode, title, description, scheduledAt, createdBy, participants, durationMins, recurrence, guestEmails }) =>
|
||||
db.prepare('INSERT INTO scheduled_meetings (id,team_id,group_id,room_code,title,description,scheduled_at,created_by,created_at,participants,duration_mins,recurrence,guest_emails) VALUES (?,?,?,?,?,?,?,?,?,?,?,?,?)')
|
||||
.run(id, teamId, groupId || null, roomCode, title, description || null, scheduledAt, createdBy, now(), (participants && participants.length) ? JSON.stringify(participants) : null, durationMins || null, (recurrence && recurrence.length) ? JSON.stringify(recurrence) : null, (guestEmails && guestEmails.length) ? JSON.stringify(guestEmails) : null),
|
||||
create: ({ id, teamId, groupId, roomCode, title, description, scheduledAt, createdBy, participants, durationMins, recurrence, guestEmails, lobby }) =>
|
||||
db.prepare('INSERT INTO scheduled_meetings (id,team_id,group_id,room_code,title,description,scheduled_at,created_by,created_at,participants,duration_mins,recurrence,guest_emails,lobby) VALUES (?,?,?,?,?,?,?,?,?,?,?,?,?,?)')
|
||||
.run(id, teamId, groupId || null, roomCode, title, description || null, scheduledAt, createdBy, now(), (participants && participants.length) ? JSON.stringify(participants) : null, durationMins || null, (recurrence && recurrence.length) ? JSON.stringify(recurrence) : null, (guestEmails && guestEmails.length) ? JSON.stringify(guestEmails) : null, (lobby === false ? 0 : 1)),
|
||||
byId: (id) => db.prepare('SELECT * FROM scheduled_meetings WHERE id=?').get(id),
|
||||
byCode: (code) => db.prepare('SELECT * FROM scheduled_meetings WHERE room_code=? ORDER BY created_at DESC LIMIT 1').get(code),
|
||||
// Meetings a user can see: created by them, a member of the group, or an invited participant.
|
||||
@@ -315,9 +315,9 @@ const scheduledMeetings = {
|
||||
end: (id, teamId) => db.prepare('UPDATE scheduled_meetings SET ended_at=? WHERE id=? AND team_id=?').run(now(), id, teamId),
|
||||
cancel: (id, teamId) => db.prepare('UPDATE scheduled_meetings SET cancelled=1, ended_at=? WHERE id=? AND team_id=?').run(now(), id, teamId),
|
||||
reschedule: (id, teamId, ts) => db.prepare('UPDATE scheduled_meetings SET scheduled_at=?, reminded=0 WHERE id=? AND team_id=?').run(ts, id, teamId), // recurrence: roll to next occurrence
|
||||
update: (id, teamId, { title, description, scheduledAt, durationMins, participants, recurrence, guestEmails }) =>
|
||||
db.prepare('UPDATE scheduled_meetings SET title=?, description=?, scheduled_at=?, duration_mins=?, participants=?, recurrence=?, guest_emails=?, reminded=0 WHERE id=? AND team_id=?')
|
||||
.run(title, description || null, scheduledAt, durationMins || null, (participants && participants.length) ? JSON.stringify(participants) : null, (recurrence && recurrence.length) ? JSON.stringify(recurrence) : null, (guestEmails && guestEmails.length) ? JSON.stringify(guestEmails) : null, id, teamId),
|
||||
update: (id, teamId, { title, description, scheduledAt, durationMins, participants, recurrence, guestEmails, lobby }) =>
|
||||
db.prepare('UPDATE scheduled_meetings SET title=?, description=?, scheduled_at=?, duration_mins=?, participants=?, recurrence=?, guest_emails=?, lobby=?, reminded=0 WHERE id=? AND team_id=?')
|
||||
.run(title, description || null, scheduledAt, durationMins || null, (participants && participants.length) ? JSON.stringify(participants) : null, (recurrence && recurrence.length) ? JSON.stringify(recurrence) : null, (guestEmails && guestEmails.length) ? JSON.stringify(guestEmails) : null, (lobby === false ? 0 : 1), id, teamId),
|
||||
remove: (id, teamId) => db.prepare('DELETE FROM scheduled_meetings WHERE id=? AND team_id=?').run(id, teamId),
|
||||
};
|
||||
|
||||
|
||||
Reference in New Issue
Block a user