@ai-matrx/meet 0.9.0 → 0.10.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.md +20 -0
- package/dist/{collab-B6scmSST.d.cts → collab-C2R-kQnV.d.cts} +6 -1
- package/dist/{collab-B6scmSST.d.ts → collab-C2R-kQnV.d.ts} +6 -1
- package/dist/{components-D0TqyeiY.d.ts → components-CMtSekZS.d.ts} +1 -1
- package/dist/{components-BeCQ9h0h.d.cts → components-DKHfkEVB.d.cts} +1 -1
- package/dist/core.cjs +339 -104
- package/dist/core.cjs.map +1 -1
- package/dist/core.d.cts +79 -24
- package/dist/core.d.ts +79 -24
- package/dist/core.js +339 -104
- package/dist/core.js.map +1 -1
- package/dist/index.cjs +339 -104
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +1 -1
- package/dist/index.d.ts +1 -1
- package/dist/index.js +339 -104
- package/dist/index.js.map +1 -1
- package/dist/react.cjs +421 -127
- package/dist/react.cjs.map +1 -1
- package/dist/react.d.cts +5 -4
- package/dist/react.d.ts +5 -4
- package/dist/react.js +421 -127
- package/dist/react.js.map +1 -1
- package/dist/skins/meet.cjs +94 -19
- package/dist/skins/meet.cjs.map +1 -1
- package/dist/skins/meet.d.cts +2 -2
- package/dist/skins/meet.d.ts +2 -2
- package/dist/skins/meet.js +94 -19
- package/dist/skins/meet.js.map +1 -1
- package/dist/styles.css +8 -0
- package/package.json +5 -5
package/dist/react.js
CHANGED
|
@@ -322,7 +322,17 @@ var MEET_ROUTES = {
|
|
|
322
322
|
*/
|
|
323
323
|
collab: "/api/v1/meet/collab",
|
|
324
324
|
/** Meet wave 5: translate finished caption lines into this viewer's language. */
|
|
325
|
-
captionsTranslate: "/api/v1/meet/captions/translate"
|
|
325
|
+
captionsTranslate: "/api/v1/meet/captions/translate",
|
|
326
|
+
/**
|
|
327
|
+
* S4 — THE STATE READ (CORE-DESIGN §2.6): `GET ?meeting_id=` with `x-meet-pass`. This
|
|
328
|
+
* person's admission (lazy expiry applied), whether anyone who can admit is here, the run's
|
|
329
|
+
* phase. Read every `waiting_poll_seconds` while waiting and on every `mx.state.changed`.
|
|
330
|
+
*/
|
|
331
|
+
state: "/api/v1/meet/state",
|
|
332
|
+
/** S4: `withdraw` (stop waiting) or `leave` (close this tab); the pass may ride in the body. */
|
|
333
|
+
leave: "/api/v1/meet/leave",
|
|
334
|
+
/** S4: a pass holder (guests included) saw the recording notice, for this run. */
|
|
335
|
+
consent: "/api/v1/meet/consent"
|
|
326
336
|
};
|
|
327
337
|
function meetingPath(template, meetingId) {
|
|
328
338
|
return template.replace("{meeting_id}", encodeURIComponent(meetingId));
|
|
@@ -396,6 +406,8 @@ function parseRoomToken(body, roomName) {
|
|
|
396
406
|
meetingPassExpiresAt: readString(payload, "meeting_pass_expires_at"),
|
|
397
407
|
sessionId: readString(payload, "session_id"),
|
|
398
408
|
lobby: readBoolean(payload, "lobby", false),
|
|
409
|
+
admissionState: readString(payload, "admission_state") ?? void 0,
|
|
410
|
+
pollSeconds: typeof payload["poll_seconds"] === "number" ? payload["poll_seconds"] : void 0,
|
|
399
411
|
breakoutRoomId: readString(payload, "breakout_room_id") ?? null
|
|
400
412
|
};
|
|
401
413
|
}
|
|
@@ -983,14 +995,15 @@ var MEET_EVENTS = {
|
|
|
983
995
|
cancel: "mx.call.cancel",
|
|
984
996
|
/** You answered (or declined) on another device. Stop ringing here. */
|
|
985
997
|
settled: "mx.call.settled",
|
|
986
|
-
/**
|
|
987
|
-
|
|
988
|
-
|
|
989
|
-
|
|
990
|
-
|
|
991
|
-
|
|
992
|
-
|
|
993
|
-
|
|
998
|
+
/**
|
|
999
|
+
* 🚨 S4 (CORE-DESIGN §2.6): the ONE room-channel event — payload-free "something changed,
|
|
1000
|
+
* read again". Nothing a person must see rides it: a waiting person learns admitted /
|
|
1001
|
+
* denied / expired / ended by READING their row (`GET /v1/meet/state`) on a timer and on
|
|
1002
|
+
* every hint. The four payload broadcasts (`mx.lobby.knock`, `mx.lobby.admission`,
|
|
1003
|
+
* `mx.room.lock`, `mx.room.recording`) are gone with their senders and receivers. The
|
|
1004
|
+
* server publishes the same event on the same topic (aidream `admission.room_topic`).
|
|
1005
|
+
*/
|
|
1006
|
+
stateChanged: "mx.state.changed"
|
|
994
1007
|
};
|
|
995
1008
|
|
|
996
1009
|
// src/core/types.ts
|
|
@@ -1016,6 +1029,7 @@ function isLinkFollower(standing) {
|
|
|
1016
1029
|
function isHostRole(role) {
|
|
1017
1030
|
return role === "host" || role === "cohost";
|
|
1018
1031
|
}
|
|
1032
|
+
var WAITING_ADMISSION_STATES = ["waiting_host", "knocking"];
|
|
1019
1033
|
|
|
1020
1034
|
// src/core/calls.ts
|
|
1021
1035
|
var TRANSITIONS = {
|
|
@@ -2031,6 +2045,7 @@ var NO_GRANTS = {
|
|
|
2031
2045
|
};
|
|
2032
2046
|
var EMPTY = {
|
|
2033
2047
|
phase: "idle",
|
|
2048
|
+
waiting: null,
|
|
2034
2049
|
meeting: null,
|
|
2035
2050
|
localIdentity: null,
|
|
2036
2051
|
grants: NO_GRANTS,
|
|
@@ -2287,19 +2302,25 @@ function roleOf(participants, identity) {
|
|
|
2287
2302
|
return participants.find((p) => p.identity === identity)?.role ?? "participant";
|
|
2288
2303
|
}
|
|
2289
2304
|
var END_MEETING_NOT_SERVED = "Ending a meeting is not available on this server yet";
|
|
2290
|
-
function lobbyEntryFrom(attendee) {
|
|
2291
|
-
if (attendee.admissionState
|
|
2305
|
+
function lobbyEntryFrom(attendee, now = Date.now()) {
|
|
2306
|
+
if (!WAITING_ADMISSION_STATES.includes(attendee.admissionState)) return null;
|
|
2307
|
+
const expires = attendee.knockExpiresAt ? Date.parse(attendee.knockExpiresAt) : Number.NaN;
|
|
2308
|
+
if (!Number.isNaN(expires) && expires <= now) return null;
|
|
2292
2309
|
return {
|
|
2293
2310
|
identity: attendee.identity,
|
|
2294
2311
|
displayName: attendee.displayName,
|
|
2295
2312
|
avatarUrl: attendee.avatarUrl,
|
|
2296
2313
|
userId: attendee.userId,
|
|
2297
2314
|
state: "knocking",
|
|
2298
|
-
knockedAt: attendee.joinedAt ?? (/* @__PURE__ */ new Date()).toISOString(),
|
|
2315
|
+
knockedAt: attendee.knockedAt ?? attendee.joinedAt ?? (/* @__PURE__ */ new Date()).toISOString(),
|
|
2316
|
+
knockExpiresAt: attendee.knockExpiresAt ?? null,
|
|
2299
2317
|
decidedAt: null,
|
|
2300
2318
|
decidedBy: null
|
|
2301
2319
|
};
|
|
2302
2320
|
}
|
|
2321
|
+
function decidedState(state) {
|
|
2322
|
+
return WAITING_ADMISSION_STATES.includes(state) ? "expired" : state;
|
|
2323
|
+
}
|
|
2303
2324
|
function createMeetingSession(options) {
|
|
2304
2325
|
const { manager, repository, store, identity } = options;
|
|
2305
2326
|
let channel = null;
|
|
@@ -2323,11 +2344,6 @@ function createMeetingSession(options) {
|
|
|
2323
2344
|
}
|
|
2324
2345
|
return identity.userId;
|
|
2325
2346
|
}
|
|
2326
|
-
function knockIdentity() {
|
|
2327
|
-
return asParticipantIdentity(
|
|
2328
|
-
identity.userId !== null ? `user:${identity.userId}` : `guest:${identity.deviceId}`
|
|
2329
|
-
);
|
|
2330
|
-
}
|
|
2331
2347
|
function requireMeeting(operation) {
|
|
2332
2348
|
const meeting = store.snapshot().meeting;
|
|
2333
2349
|
if (meeting === null) {
|
|
@@ -2339,6 +2355,191 @@ function createMeetingSession(options) {
|
|
|
2339
2355
|
}
|
|
2340
2356
|
return meeting;
|
|
2341
2357
|
}
|
|
2358
|
+
let polling = false;
|
|
2359
|
+
let pollTimer = null;
|
|
2360
|
+
let reading = null;
|
|
2361
|
+
let admittedOnce = false;
|
|
2362
|
+
let unlisten = null;
|
|
2363
|
+
function report(level, message, remedy) {
|
|
2364
|
+
options.onDiagnostic?.(remedy === void 0 ? { level, message } : { level, message, remedy });
|
|
2365
|
+
}
|
|
2366
|
+
function pollMs() {
|
|
2367
|
+
const seconds = store.snapshot().waiting?.pollSeconds ?? 3;
|
|
2368
|
+
return Math.max(1, Math.min(60, seconds)) * 1e3;
|
|
2369
|
+
}
|
|
2370
|
+
function schedule(ms2) {
|
|
2371
|
+
if (pollTimer !== null) clearTimeout(pollTimer);
|
|
2372
|
+
if (!polling) return;
|
|
2373
|
+
pollTimer = setTimeout(() => {
|
|
2374
|
+
pollTimer = null;
|
|
2375
|
+
void readState2().finally(() => schedule(pollMs()));
|
|
2376
|
+
}, ms2);
|
|
2377
|
+
}
|
|
2378
|
+
function listen() {
|
|
2379
|
+
if (unlisten !== null || typeof window === "undefined") return;
|
|
2380
|
+
const nudge = () => {
|
|
2381
|
+
if (typeof document !== "undefined" && document.visibilityState === "hidden") return;
|
|
2382
|
+
void readState2();
|
|
2383
|
+
};
|
|
2384
|
+
const goodbye = () => {
|
|
2385
|
+
const meeting = store.snapshot().meeting;
|
|
2386
|
+
const pass = options.resolvePass?.() ?? null;
|
|
2387
|
+
const waiting = store.snapshot().waiting;
|
|
2388
|
+
if (meeting === null || pass === null || waiting === null) return;
|
|
2389
|
+
if (!WAITING_ADMISSION_STATES.includes(waiting.admission)) return;
|
|
2390
|
+
try {
|
|
2391
|
+
navigator.sendBeacon?.(
|
|
2392
|
+
options.api.url(MEET_ROUTES.leave),
|
|
2393
|
+
new Blob([JSON.stringify({ meeting_id: meeting.id, intent: "withdraw", meeting_pass: pass })], {
|
|
2394
|
+
type: "application/json"
|
|
2395
|
+
})
|
|
2396
|
+
);
|
|
2397
|
+
} catch {
|
|
2398
|
+
}
|
|
2399
|
+
};
|
|
2400
|
+
window.addEventListener("online", nudge);
|
|
2401
|
+
window.addEventListener("pagehide", goodbye);
|
|
2402
|
+
document.addEventListener("visibilitychange", nudge);
|
|
2403
|
+
unlisten = () => {
|
|
2404
|
+
window.removeEventListener("online", nudge);
|
|
2405
|
+
window.removeEventListener("pagehide", goodbye);
|
|
2406
|
+
document.removeEventListener("visibilitychange", nudge);
|
|
2407
|
+
};
|
|
2408
|
+
}
|
|
2409
|
+
function stopWaiting() {
|
|
2410
|
+
polling = false;
|
|
2411
|
+
if (pollTimer !== null) clearTimeout(pollTimer);
|
|
2412
|
+
pollTimer = null;
|
|
2413
|
+
unlisten?.();
|
|
2414
|
+
unlisten = null;
|
|
2415
|
+
}
|
|
2416
|
+
function patchWaiting(next) {
|
|
2417
|
+
const current = store.snapshot().waiting;
|
|
2418
|
+
const base = current ?? {
|
|
2419
|
+
admission: "knocking",
|
|
2420
|
+
admittersPresent: null,
|
|
2421
|
+
reknockAllowed: true,
|
|
2422
|
+
knockExpiresAt: null,
|
|
2423
|
+
pollSeconds: 3,
|
|
2424
|
+
liveVersion: 0
|
|
2425
|
+
};
|
|
2426
|
+
store.patch({ waiting: { ...base, ...next } });
|
|
2427
|
+
}
|
|
2428
|
+
function apply(read) {
|
|
2429
|
+
const snapshot = store.snapshot();
|
|
2430
|
+
if (snapshot.phase !== "lobby") return;
|
|
2431
|
+
const admission = read.admission_state ?? null;
|
|
2432
|
+
const ended = admission === "ended" || read.session_phase === "ended" || read.meeting_phase === "ended";
|
|
2433
|
+
if (admission === "admitted" && !ended) {
|
|
2434
|
+
if (admittedOnce) return;
|
|
2435
|
+
admittedOnce = true;
|
|
2436
|
+
stopWaiting();
|
|
2437
|
+
options.onAdmitted?.();
|
|
2438
|
+
return;
|
|
2439
|
+
}
|
|
2440
|
+
if (ended) {
|
|
2441
|
+
stopWaiting();
|
|
2442
|
+
patchWaiting({ admission: "ended" });
|
|
2443
|
+
const meeting = snapshot.meeting;
|
|
2444
|
+
if (read.meeting_phase === "ended" && meeting !== null && meeting.endedAt === null) {
|
|
2445
|
+
store.patch({ meeting: { ...meeting, endedAt: (/* @__PURE__ */ new Date()).toISOString() } });
|
|
2446
|
+
}
|
|
2447
|
+
return;
|
|
2448
|
+
}
|
|
2449
|
+
if (admission === null) return;
|
|
2450
|
+
patchWaiting({
|
|
2451
|
+
admission,
|
|
2452
|
+
admittersPresent: read.admitters_present ?? null,
|
|
2453
|
+
reknockAllowed: read.reknock_allowed ?? true,
|
|
2454
|
+
knockExpiresAt: read.knock_expires_at ?? null,
|
|
2455
|
+
pollSeconds: read.poll_seconds ?? snapshot.waiting?.pollSeconds ?? 3,
|
|
2456
|
+
liveVersion: read.live_version ?? 0
|
|
2457
|
+
});
|
|
2458
|
+
if (admission === "denied" || admission === "expired" || admission === "withdrawn" || admission === "removed") {
|
|
2459
|
+
stopWaiting();
|
|
2460
|
+
}
|
|
2461
|
+
}
|
|
2462
|
+
async function readState2() {
|
|
2463
|
+
if (reading !== null) return reading;
|
|
2464
|
+
const meeting = store.snapshot().meeting;
|
|
2465
|
+
const pass = options.resolvePass?.() ?? null;
|
|
2466
|
+
if (meeting === null || pass === null) return;
|
|
2467
|
+
reading = (async () => {
|
|
2468
|
+
try {
|
|
2469
|
+
const response = await options.api.request({
|
|
2470
|
+
path: `${MEET_ROUTES.state}?meeting_id=${encodeURIComponent(meeting.id)}`,
|
|
2471
|
+
method: "GET",
|
|
2472
|
+
requireAuth: false,
|
|
2473
|
+
operation: "meet.state",
|
|
2474
|
+
headers: { [MEETING_PASS_HEADER]: pass }
|
|
2475
|
+
});
|
|
2476
|
+
if (!response.ok) {
|
|
2477
|
+
let body = null;
|
|
2478
|
+
try {
|
|
2479
|
+
body = await response.json();
|
|
2480
|
+
} catch {
|
|
2481
|
+
body = null;
|
|
2482
|
+
}
|
|
2483
|
+
const refusal2 = readServerRefusal(body);
|
|
2484
|
+
report(
|
|
2485
|
+
"warn",
|
|
2486
|
+
refusal2?.reason ?? `meet.state: the server answered ${response.status}`,
|
|
2487
|
+
refusal2?.remedy ?? "The waiting room keeps asking; nothing on this screen changes until the server answers."
|
|
2488
|
+
);
|
|
2489
|
+
return;
|
|
2490
|
+
}
|
|
2491
|
+
apply(await response.json());
|
|
2492
|
+
} catch (error) {
|
|
2493
|
+
report("info", `meet.state: ${String(error)}`, "Waiting continues; the state is read again shortly.");
|
|
2494
|
+
} finally {
|
|
2495
|
+
reading = null;
|
|
2496
|
+
}
|
|
2497
|
+
})();
|
|
2498
|
+
return reading;
|
|
2499
|
+
}
|
|
2500
|
+
async function postLeave(intent) {
|
|
2501
|
+
const meeting = requireMeeting(`meet.${intent}`);
|
|
2502
|
+
const pass = options.resolvePass?.() ?? null;
|
|
2503
|
+
const response = await options.api.request({
|
|
2504
|
+
path: MEET_ROUTES.leave,
|
|
2505
|
+
method: "POST",
|
|
2506
|
+
requireAuth: false,
|
|
2507
|
+
operation: `meet.${intent}`,
|
|
2508
|
+
body: { meeting_id: meeting.id, intent },
|
|
2509
|
+
...pass !== null ? { headers: { [MEETING_PASS_HEADER]: pass } } : {}
|
|
2510
|
+
});
|
|
2511
|
+
if (!response.ok) {
|
|
2512
|
+
let body = null;
|
|
2513
|
+
try {
|
|
2514
|
+
body = await response.json();
|
|
2515
|
+
} catch {
|
|
2516
|
+
body = null;
|
|
2517
|
+
}
|
|
2518
|
+
const refusal2 = readServerRefusal(body);
|
|
2519
|
+
throw new MeetError(
|
|
2520
|
+
"transport",
|
|
2521
|
+
refusal2?.reason ?? `meet.${intent}: the server answered ${response.status}`,
|
|
2522
|
+
refusal2?.remedy ?? "You are still in the waiting room. Try again."
|
|
2523
|
+
);
|
|
2524
|
+
}
|
|
2525
|
+
}
|
|
2526
|
+
function hint() {
|
|
2527
|
+
channel?.send(MEET_EVENTS.stateChanged, { v: null });
|
|
2528
|
+
}
|
|
2529
|
+
async function refreshQueue() {
|
|
2530
|
+
const meeting = store.snapshot().meeting;
|
|
2531
|
+
if (meeting === null || isGuest(identity) || !isHostRole(effectiveRole(store.snapshot()))) return;
|
|
2532
|
+
try {
|
|
2533
|
+
const attendees = await repository.attendees(meeting.id);
|
|
2534
|
+
store.learn(attendees);
|
|
2535
|
+
for (const attendee of attendees) {
|
|
2536
|
+
const entry = lobbyEntryFrom(attendee);
|
|
2537
|
+
if (entry !== null) store.upsertLobby(entry);
|
|
2538
|
+
else store.setAdmission(attendee.identity, decidedState(attendee.admissionState), null);
|
|
2539
|
+
}
|
|
2540
|
+
} catch {
|
|
2541
|
+
}
|
|
2542
|
+
}
|
|
2342
2543
|
return {
|
|
2343
2544
|
watch(roomName) {
|
|
2344
2545
|
channel?.close();
|
|
@@ -2346,58 +2547,22 @@ function createMeetingSession(options) {
|
|
|
2346
2547
|
topic: roomTopic(roomName),
|
|
2347
2548
|
broadcast: [
|
|
2348
2549
|
{
|
|
2349
|
-
|
|
2350
|
-
|
|
2351
|
-
|
|
2352
|
-
|
|
2353
|
-
|
|
2354
|
-
{
|
|
2355
|
-
event: MEET_EVENTS.admission,
|
|
2356
|
-
onMessage: (message) => {
|
|
2357
|
-
const verdict = message.data;
|
|
2358
|
-
const decided = asParticipantIdentity(verdict.identity);
|
|
2359
|
-
store.setAdmission(
|
|
2360
|
-
decided,
|
|
2361
|
-
verdict.admitted ? "admitted" : "denied",
|
|
2362
|
-
verdict.by === null ? null : verdict.by
|
|
2363
|
-
);
|
|
2364
|
-
if (decided === knockIdentity()) {
|
|
2365
|
-
if (verdict.admitted) options.onAdmitted?.();
|
|
2366
|
-
else options.onDenied?.();
|
|
2367
|
-
}
|
|
2368
|
-
}
|
|
2369
|
-
},
|
|
2370
|
-
{
|
|
2371
|
-
event: MEET_EVENTS.lock,
|
|
2372
|
-
onMessage: (message) => {
|
|
2373
|
-
const { locked } = message.data;
|
|
2374
|
-
const meeting = store.snapshot().meeting;
|
|
2375
|
-
store.patch({
|
|
2376
|
-
locked,
|
|
2377
|
-
...meeting !== null ? { meeting: { ...meeting, locked } } : {}
|
|
2378
|
-
});
|
|
2379
|
-
}
|
|
2380
|
-
},
|
|
2381
|
-
{
|
|
2382
|
-
event: MEET_EVENTS.recording,
|
|
2383
|
-
onMessage: (message) => {
|
|
2384
|
-
const payload = message.data;
|
|
2385
|
-
store.setRecording(
|
|
2386
|
-
payload.state,
|
|
2387
|
-
payload.by === null ? null : payload.by,
|
|
2388
|
-
"room"
|
|
2389
|
-
);
|
|
2550
|
+
// 🚨 PAYLOAD-FREE. Every receiver reads again; a spoofed hint only causes a read.
|
|
2551
|
+
event: MEET_EVENTS.stateChanged,
|
|
2552
|
+
onMessage: () => {
|
|
2553
|
+
if (store.snapshot().phase === "lobby") void readState2();
|
|
2554
|
+
void refreshQueue();
|
|
2390
2555
|
}
|
|
2391
2556
|
}
|
|
2392
2557
|
],
|
|
2393
|
-
// THE BACKFILL DOOR. A
|
|
2394
|
-
//
|
|
2395
|
-
// re-announce on their own timer.
|
|
2558
|
+
// THE BACKFILL DOOR. A socket blip must not lose a decision: re-read the meeting
|
|
2559
|
+
// row, and a waiting tab re-reads its own state.
|
|
2396
2560
|
onBackfill: async () => {
|
|
2397
2561
|
const meeting = store.snapshot().meeting;
|
|
2398
2562
|
if (meeting === null) return;
|
|
2399
2563
|
const fresh = isGuest(identity) ? await repository.meetingBySlug(meeting.slug) : await readMeeting(meeting.id);
|
|
2400
2564
|
store.patch({ meeting: fresh, locked: fresh.locked });
|
|
2565
|
+
if (store.snapshot().phase === "lobby") void readState2();
|
|
2401
2566
|
}
|
|
2402
2567
|
});
|
|
2403
2568
|
},
|
|
@@ -2458,7 +2623,7 @@ function createMeetingSession(options) {
|
|
|
2458
2623
|
store.upsertLobby(entry);
|
|
2459
2624
|
return;
|
|
2460
2625
|
}
|
|
2461
|
-
store.setAdmission(attendee.identity, attendee.admissionState, null);
|
|
2626
|
+
store.setAdmission(attendee.identity, decidedState(attendee.admissionState), null);
|
|
2462
2627
|
}
|
|
2463
2628
|
},
|
|
2464
2629
|
{
|
|
@@ -2510,62 +2675,75 @@ function createMeetingSession(options) {
|
|
|
2510
2675
|
if (entry !== null) {
|
|
2511
2676
|
store.upsertLobby(entry);
|
|
2512
2677
|
} else {
|
|
2513
|
-
store.setAdmission(attendee.identity, attendee.admissionState, null);
|
|
2678
|
+
store.setAdmission(attendee.identity, decidedState(attendee.admissionState), null);
|
|
2514
2679
|
}
|
|
2515
2680
|
}
|
|
2516
2681
|
}
|
|
2517
2682
|
});
|
|
2518
2683
|
meetingChannel.requestBackfill("manual");
|
|
2519
2684
|
},
|
|
2520
|
-
knock(roomName) {
|
|
2521
|
-
const handle = channel ?? manager.open({ topic: roomTopic(roomName) });
|
|
2522
|
-
channel = handle;
|
|
2523
|
-
const entry = {
|
|
2524
|
-
identity: knockIdentity(),
|
|
2525
|
-
displayName: identity.displayName,
|
|
2526
|
-
avatarUrl: identity.avatarUrl,
|
|
2527
|
-
userId: identity.userId,
|
|
2528
|
-
state: "knocking",
|
|
2529
|
-
knockedAt: (/* @__PURE__ */ new Date()).toISOString(),
|
|
2530
|
-
decidedAt: null,
|
|
2531
|
-
decidedBy: null
|
|
2532
|
-
};
|
|
2533
|
-
store.upsertLobby(entry);
|
|
2534
|
-
handle.send(MEET_EVENTS.knock, entry);
|
|
2535
|
-
},
|
|
2536
2685
|
async admit(entry, admitted) {
|
|
2537
2686
|
const by = requireHostUser("meet.admit");
|
|
2538
2687
|
const meeting = requireMeeting("meet.admit");
|
|
2539
|
-
await repository.
|
|
2688
|
+
const row = await repository.decideAdmission({
|
|
2540
2689
|
meetingId: meeting.id,
|
|
2541
2690
|
identity: entry.identity,
|
|
2542
|
-
admitted,
|
|
2691
|
+
decision: admitted ? "admit" : "deny",
|
|
2543
2692
|
byUserId: by
|
|
2544
2693
|
});
|
|
2545
2694
|
store.upsertLobby({
|
|
2546
2695
|
...entry,
|
|
2547
|
-
state:
|
|
2696
|
+
state: decidedState(row.admissionState),
|
|
2548
2697
|
decidedAt: (/* @__PURE__ */ new Date()).toISOString(),
|
|
2549
2698
|
decidedBy: by
|
|
2550
2699
|
});
|
|
2551
|
-
|
|
2700
|
+
hint();
|
|
2701
|
+
},
|
|
2702
|
+
async undeny(entry) {
|
|
2703
|
+
const by = requireHostUser("meet.undeny");
|
|
2704
|
+
const meeting = requireMeeting("meet.undeny");
|
|
2705
|
+
const row = await repository.decideAdmission({
|
|
2706
|
+
meetingId: meeting.id,
|
|
2552
2707
|
identity: entry.identity,
|
|
2553
|
-
|
|
2554
|
-
by
|
|
2708
|
+
decision: "undeny",
|
|
2709
|
+
byUserId: by
|
|
2555
2710
|
});
|
|
2711
|
+
store.upsertLobby({ ...entry, state: decidedState(row.admissionState), decidedAt: (/* @__PURE__ */ new Date()).toISOString(), decidedBy: by });
|
|
2712
|
+
hint();
|
|
2556
2713
|
},
|
|
2557
2714
|
async admitAll() {
|
|
2558
|
-
const
|
|
2559
|
-
|
|
2560
|
-
|
|
2561
|
-
|
|
2715
|
+
const by = requireHostUser("meet.admitAll");
|
|
2716
|
+
const meeting = requireMeeting("meet.admitAll");
|
|
2717
|
+
const rows = await repository.admitAll(meeting.id, by);
|
|
2718
|
+
for (const row of rows) {
|
|
2719
|
+
store.setAdmission(row.identity, row.admissionState, by);
|
|
2720
|
+
}
|
|
2721
|
+
hint();
|
|
2722
|
+
},
|
|
2723
|
+
startWaiting() {
|
|
2724
|
+
if (polling) return;
|
|
2725
|
+
polling = true;
|
|
2726
|
+
admittedOnce = false;
|
|
2727
|
+
listen();
|
|
2728
|
+
schedule(0);
|
|
2729
|
+
},
|
|
2730
|
+
stopWaiting,
|
|
2731
|
+
readState: readState2,
|
|
2732
|
+
async withdraw() {
|
|
2733
|
+
stopWaiting();
|
|
2734
|
+
await postLeave("withdraw");
|
|
2735
|
+
patchWaiting({ admission: "withdrawn" });
|
|
2736
|
+
hint();
|
|
2737
|
+
},
|
|
2738
|
+
async askAgain() {
|
|
2739
|
+
await options.rejoin?.();
|
|
2562
2740
|
},
|
|
2563
2741
|
async setLock(locked) {
|
|
2564
2742
|
const by = requireHostUser("meet.setLock");
|
|
2565
2743
|
const meeting = requireMeeting("meet.setLock");
|
|
2566
2744
|
const updated = await repository.setLock(meeting.id, locked, by);
|
|
2567
2745
|
store.patch({ meeting: updated, locked: updated.locked });
|
|
2568
|
-
|
|
2746
|
+
hint();
|
|
2569
2747
|
return updated;
|
|
2570
2748
|
},
|
|
2571
2749
|
/**
|
|
@@ -2623,6 +2801,7 @@ function createMeetingSession(options) {
|
|
|
2623
2801
|
return { endedAt, served: true };
|
|
2624
2802
|
},
|
|
2625
2803
|
dispose() {
|
|
2804
|
+
stopWaiting();
|
|
2626
2805
|
channel?.close();
|
|
2627
2806
|
channel = null;
|
|
2628
2807
|
meetingChannel?.close();
|
|
@@ -2667,7 +2846,14 @@ var RPCS = {
|
|
|
2667
2846
|
getOrCreateMeeting: "meet_get_or_create_meeting",
|
|
2668
2847
|
/** Resolve a durable public link, before/during/after its scheduled time. */
|
|
2669
2848
|
meetingBySlug: "meet_meeting_by_slug",
|
|
2670
|
-
|
|
2849
|
+
/*
|
|
2850
|
+
* ── S4 ADMISSION (CORE-DESIGN §2.4). Each returns the decided row; the caller then
|
|
2851
|
+
* sends the payload-free `mx.state.changed` hint. Authorized by `who_may_admit`.
|
|
2852
|
+
*/
|
|
2853
|
+
admit: "meet_admit",
|
|
2854
|
+
deny: "meet_deny",
|
|
2855
|
+
undeny: "meet_undeny",
|
|
2856
|
+
admitAll: "meet_admit_all",
|
|
2671
2857
|
setLock: "meet_set_lock",
|
|
2672
2858
|
recordConsent: "meet_record_consent",
|
|
2673
2859
|
/*
|
|
@@ -2710,13 +2896,13 @@ var RPCS = {
|
|
|
2710
2896
|
/** Titles, agendas, current notes and final transcript lines; snippets mark hits with ⟦ ⟧. */
|
|
2711
2897
|
search: "meet_search"
|
|
2712
2898
|
};
|
|
2713
|
-
var VOID_RPCS = [RPCS.
|
|
2899
|
+
var VOID_RPCS = [RPCS.recordConsent];
|
|
2714
2900
|
var MEETING_COLUMNS = "id,organization_id,room_name,slug,title,kind,host_user_id,scheduled_for,scheduled_duration_minutes,started_at,ended_at,locked,lobby_enabled,recording_policy,ai_enabled,metadata,time_zone,agenda,recurrence_rule,join_before_host,cancelled_at,cancellation_reason,deleted_at,calendar_sequence,version";
|
|
2715
2901
|
var INVITEE_COLUMNS = "id,meeting_id,invitee_user_id,email,display_name,role,rsvp_state,responded_at,rsvp_note,last_sent_sequence,last_sent_at";
|
|
2716
2902
|
var RECORDING_COLUMNS = "id,meeting_id,state,started_at,ended_at,started_by,file_id,failure_reason";
|
|
2717
2903
|
var SEGMENT_COLUMNS = "id,meeting_id,identity,speaker,text,started_at,is_final,metadata";
|
|
2718
2904
|
var NOTE_COLUMNS = "id,meeting_id,text,kind,assignee_user_id,created_at,metadata";
|
|
2719
|
-
var ATTENDEE_COLUMNS = "id,meeting_id,identity,participant_user_id,display_name,avatar_url,role,admission_state,is_agent,joined_at,left_at,sessions:metadata->sessions";
|
|
2905
|
+
var ATTENDEE_COLUMNS = "id,meeting_id,identity,participant_user_id,display_name,avatar_url,role,admission_state,knocked_at,knock_expires_at,is_agent,joined_at,left_at,sessions:metadata->sessions";
|
|
2720
2906
|
function projectStays(value) {
|
|
2721
2907
|
if (!Array.isArray(value)) return [];
|
|
2722
2908
|
const stays = [];
|
|
@@ -3083,7 +3269,16 @@ function createMeetRepository(options) {
|
|
|
3083
3269
|
const role = text(row, "role");
|
|
3084
3270
|
const admission = text(row, "admission_state");
|
|
3085
3271
|
const known = ["host", "cohost", "presenter", "participant", "viewer"];
|
|
3086
|
-
const states = [
|
|
3272
|
+
const states = [
|
|
3273
|
+
"waiting_host",
|
|
3274
|
+
"knocking",
|
|
3275
|
+
"admitted",
|
|
3276
|
+
"denied",
|
|
3277
|
+
"expired",
|
|
3278
|
+
"withdrawn",
|
|
3279
|
+
"removed",
|
|
3280
|
+
"ended"
|
|
3281
|
+
];
|
|
3087
3282
|
const userId = text(row, "participant_user_id");
|
|
3088
3283
|
const identity = requireText(row, "identity", operation);
|
|
3089
3284
|
return {
|
|
@@ -3097,6 +3292,8 @@ function createMeetRepository(options) {
|
|
|
3097
3292
|
userId: userId === null ? null : asUserId(userId),
|
|
3098
3293
|
role: known.includes(role) ? role : "participant",
|
|
3099
3294
|
admissionState: states.includes(admission) ? admission : "admitted",
|
|
3295
|
+
knockedAt: text(row, "knocked_at"),
|
|
3296
|
+
knockExpiresAt: text(row, "knock_expires_at"),
|
|
3100
3297
|
isAgent: bool(row, "is_agent", false),
|
|
3101
3298
|
joinedAt: text(row, "joined_at"),
|
|
3102
3299
|
leftAt: text(row, "left_at"),
|
|
@@ -3375,16 +3572,26 @@ function createMeetRepository(options) {
|
|
|
3375
3572
|
hasMore: rows.length > limit
|
|
3376
3573
|
};
|
|
3377
3574
|
},
|
|
3378
|
-
async
|
|
3379
|
-
|
|
3380
|
-
|
|
3381
|
-
|
|
3575
|
+
async decideAdmission({ meetingId, identity, decision, byUserId }) {
|
|
3576
|
+
const fn = decision === "admit" ? RPCS.admit : decision === "deny" ? RPCS.deny : RPCS.undeny;
|
|
3577
|
+
const row = await guard(
|
|
3578
|
+
`meet.${decision}`,
|
|
3579
|
+
() => db.rpc(fn, {
|
|
3382
3580
|
p_meeting_id: meetingId,
|
|
3383
3581
|
p_identity: identity,
|
|
3384
|
-
p_admitted: admitted,
|
|
3385
3582
|
p_by_user_id: byUserId
|
|
3386
3583
|
})
|
|
3387
3584
|
);
|
|
3585
|
+
return projectAttendee(row);
|
|
3586
|
+
},
|
|
3587
|
+
async admitAll(meetingId, byUserId) {
|
|
3588
|
+
const response = await db.rpc(RPCS.admitAll, {
|
|
3589
|
+
p_meeting_id: meetingId,
|
|
3590
|
+
p_by_user_id: byUserId
|
|
3591
|
+
});
|
|
3592
|
+
if (response.error !== null) throw normalizeMeetError(response.error, "meet.admitAll");
|
|
3593
|
+
const rows = Array.isArray(response.data) ? response.data : [];
|
|
3594
|
+
return rows.map(projectAttendee);
|
|
3388
3595
|
},
|
|
3389
3596
|
async setLock(meetingId, locked, byUserId) {
|
|
3390
3597
|
const row = await guard(
|
|
@@ -4231,7 +4438,7 @@ function createRoomEngine(options) {
|
|
|
4231
4438
|
}
|
|
4232
4439
|
};
|
|
4233
4440
|
async function connect(request) {
|
|
4234
|
-
store.patch({ phase: "requesting-token", failure: null });
|
|
4441
|
+
store.patch({ phase: "requesting-token", failure: null, waiting: null });
|
|
4235
4442
|
const token = await tokens.get({
|
|
4236
4443
|
roomName: request.roomName,
|
|
4237
4444
|
identity,
|
|
@@ -4251,11 +4458,21 @@ function createRoomEngine(options) {
|
|
|
4251
4458
|
canSubscribe: token.canSubscribe
|
|
4252
4459
|
}
|
|
4253
4460
|
});
|
|
4254
|
-
if (token.lobby) {
|
|
4255
|
-
store.patch({
|
|
4461
|
+
if (token.lobby || !token.canSubscribe) {
|
|
4462
|
+
store.patch({
|
|
4463
|
+
phase: "lobby",
|
|
4464
|
+
waiting: {
|
|
4465
|
+
admission: token.admissionState !== void 0 && token.admissionState !== "admitted" ? token.admissionState : "knocking",
|
|
4466
|
+
admittersPresent: null,
|
|
4467
|
+
reknockAllowed: true,
|
|
4468
|
+
knockExpiresAt: null,
|
|
4469
|
+
pollSeconds: token.pollSeconds ?? 3,
|
|
4470
|
+
liveVersion: 0
|
|
4471
|
+
}
|
|
4472
|
+
});
|
|
4256
4473
|
return;
|
|
4257
4474
|
}
|
|
4258
|
-
store.patch({ phase: "connecting" });
|
|
4475
|
+
store.patch({ phase: "connecting", waiting: null });
|
|
4259
4476
|
const created = await options.driverFactory(handlers);
|
|
4260
4477
|
if (disposed) {
|
|
4261
4478
|
await created.disconnect();
|
|
@@ -5338,14 +5555,18 @@ function MeetRuntime(props) {
|
|
|
5338
5555
|
api,
|
|
5339
5556
|
onDiagnostic: report,
|
|
5340
5557
|
// ADMISSION IS A JOIN, NOT A RE-RENDER. The lobby token grants neither
|
|
5341
|
-
// publish nor subscribe, so the host saying yes has to mint a new one.
|
|
5558
|
+
// publish nor subscribe, so the host saying yes has to mint a new one. The
|
|
5559
|
+
// "yes" is read from the server (the state read), never a broadcast (S4).
|
|
5342
5560
|
onAdmitted: () => {
|
|
5343
|
-
void engine.rejoin();
|
|
5344
|
-
},
|
|
5345
|
-
// A "no" is ALSO a mint: the server refuses it with its own sentence, which
|
|
5346
|
-
// turns the waiting room into the pre-join screen showing that sentence.
|
|
5347
|
-
onDenied: () => {
|
|
5348
5561
|
void engine.rejoin().catch(() => void 0);
|
|
5562
|
+
},
|
|
5563
|
+
rejoin: () => engine.rejoin(),
|
|
5564
|
+
// THE MEETING PASS this device holds — what the state read and `/leave` carry.
|
|
5565
|
+
resolvePass: () => {
|
|
5566
|
+
const roomName = store.snapshot().meeting?.roomName;
|
|
5567
|
+
const active = engine.token();
|
|
5568
|
+
if (active?.meetingPass) return active.meetingPass;
|
|
5569
|
+
return roomName === void 0 ? null : tokens.remembered(roomName)?.meetingPass ?? null;
|
|
5349
5570
|
}
|
|
5350
5571
|
});
|
|
5351
5572
|
const resolveRoomToken = () => {
|
|
@@ -5357,6 +5578,14 @@ function MeetRuntime(props) {
|
|
|
5357
5578
|
};
|
|
5358
5579
|
const ai = createMeetAi({ api, store, resolveRoomToken });
|
|
5359
5580
|
const inRoom = createInRoomClient({ api, store, resolveRoomToken });
|
|
5581
|
+
store.subscribe(() => {
|
|
5582
|
+
const snapshot = store.snapshot();
|
|
5583
|
+
if (snapshot.phase === "lobby" && snapshot.waiting !== null && (snapshot.waiting.admission === "knocking" || snapshot.waiting.admission === "waiting_host")) {
|
|
5584
|
+
meetings.startWaiting();
|
|
5585
|
+
} else if (snapshot.phase !== "lobby") {
|
|
5586
|
+
meetings.stopWaiting();
|
|
5587
|
+
}
|
|
5588
|
+
});
|
|
5360
5589
|
let chatReadFor = null;
|
|
5361
5590
|
store.subscribe(() => {
|
|
5362
5591
|
const snapshot = store.snapshot();
|
|
@@ -5564,7 +5793,7 @@ function observedPhase(input) {
|
|
|
5564
5793
|
case "left":
|
|
5565
5794
|
return "prejoin";
|
|
5566
5795
|
case "lobby":
|
|
5567
|
-
return
|
|
5796
|
+
return waitingPhase(snapshot);
|
|
5568
5797
|
case "requesting-token":
|
|
5569
5798
|
case "connecting":
|
|
5570
5799
|
return "joining";
|
|
@@ -5573,6 +5802,24 @@ function observedPhase(input) {
|
|
|
5573
5802
|
return "in_call";
|
|
5574
5803
|
}
|
|
5575
5804
|
}
|
|
5805
|
+
function waitingPhase(snapshot) {
|
|
5806
|
+
switch (snapshot.waiting?.admission) {
|
|
5807
|
+
case "waiting_host":
|
|
5808
|
+
return "waiting:host_not_started";
|
|
5809
|
+
case "denied":
|
|
5810
|
+
return "denied";
|
|
5811
|
+
case "expired":
|
|
5812
|
+
return "knock_expired";
|
|
5813
|
+
case "withdrawn":
|
|
5814
|
+
return "left";
|
|
5815
|
+
case "ended":
|
|
5816
|
+
return "ended";
|
|
5817
|
+
case "removed":
|
|
5818
|
+
return "refused:removed";
|
|
5819
|
+
default:
|
|
5820
|
+
return "waiting:knocking";
|
|
5821
|
+
}
|
|
5822
|
+
}
|
|
5576
5823
|
var ADMITTER_ROLES = /* @__PURE__ */ new Set(["host", "cohost"]);
|
|
5577
5824
|
function observe(input) {
|
|
5578
5825
|
const { snapshot } = input;
|
|
@@ -5587,7 +5834,7 @@ function observe(input) {
|
|
|
5587
5834
|
reason: phase.startsWith("refused:") ? phase.slice("refused:".length) : null,
|
|
5588
5835
|
role,
|
|
5589
5836
|
connection: snapshot?.phase === "reconnecting" ? "reconnecting" : "stable",
|
|
5590
|
-
hostPresent: inCall && snapshot !== null ? snapshot.participants.some((p) => ADMITTER_ROLES.has(p.role)) : null,
|
|
5837
|
+
hostPresent: inCall && snapshot !== null ? snapshot.participants.some((p) => ADMITTER_ROLES.has(p.role)) : snapshot?.phase === "lobby" ? snapshot.waiting?.admittersPresent ?? null : null,
|
|
5591
5838
|
lobbyCount: canAdmit && snapshot !== null ? snapshot.lobby.filter((entry) => entry.state === "knocking").length : 0,
|
|
5592
5839
|
recording: snapshot?.recording === "recording" || snapshot?.recording === "stopping",
|
|
5593
5840
|
audioBlocked: null,
|
|
@@ -6548,12 +6795,22 @@ function useLobby() {
|
|
|
6548
6795
|
async () => requireSession(session, "admitAll").admitAll(),
|
|
6549
6796
|
[session]
|
|
6550
6797
|
),
|
|
6551
|
-
|
|
6552
|
-
(
|
|
6798
|
+
undeny: useCallback2(
|
|
6799
|
+
async (entry) => requireSession(session, "undeny").undeny(entry),
|
|
6553
6800
|
[session]
|
|
6554
6801
|
)
|
|
6555
6802
|
};
|
|
6556
6803
|
}
|
|
6804
|
+
function useWaiting() {
|
|
6805
|
+
const host = useMeetHost();
|
|
6806
|
+
const snapshot = useMeetSnapshot();
|
|
6807
|
+
const session = host?.meetings ?? null;
|
|
6808
|
+
return {
|
|
6809
|
+
waiting: snapshot?.waiting ?? null,
|
|
6810
|
+
withdraw: useCallback2(async () => requireSession(session, "withdraw").withdraw(), [session]),
|
|
6811
|
+
askAgain: useCallback2(async () => requireSession(session, "askAgain").askAgain(), [session])
|
|
6812
|
+
};
|
|
6813
|
+
}
|
|
6557
6814
|
function useRecording() {
|
|
6558
6815
|
const host = useMeetHost();
|
|
6559
6816
|
const snapshot = useMeetSnapshot();
|
|
@@ -10684,7 +10941,7 @@ function MeetSkinRoom({
|
|
|
10684
10941
|
) });
|
|
10685
10942
|
}
|
|
10686
10943
|
if (phase === "lobby") {
|
|
10687
|
-
return /* @__PURE__ */ jsx5("div", { className: "mx-meet mx-meet--lobby", children: /* @__PURE__ */ jsx5(WaitingRoom, {
|
|
10944
|
+
return /* @__PURE__ */ jsx5("div", { className: "mx-meet mx-meet--lobby", children: /* @__PURE__ */ jsx5(WaitingRoom, { host, ...onLeave !== void 0 ? { onLeave } : {} }) });
|
|
10688
10945
|
}
|
|
10689
10946
|
return /* @__PURE__ */ jsxs3("div", { className: "mx-meet", children: [
|
|
10690
10947
|
/* @__PURE__ */ jsx5(ConsentNotice, {}),
|
|
@@ -10709,21 +10966,58 @@ function MeetSkinRoom({
|
|
|
10709
10966
|
/* @__PURE__ */ jsx5(ControlBar, { ...onLeave !== void 0 ? { onLeave } : {} })
|
|
10710
10967
|
] });
|
|
10711
10968
|
}
|
|
10712
|
-
function WaitingRoom({
|
|
10713
|
-
const
|
|
10714
|
-
|
|
10715
|
-
|
|
10716
|
-
|
|
10717
|
-
|
|
10718
|
-
|
|
10969
|
+
function WaitingRoom({ host, onLeave }) {
|
|
10970
|
+
const { waiting, withdraw, askAgain } = useWaiting();
|
|
10971
|
+
const [busy, setBusy] = useState4(false);
|
|
10972
|
+
const admission = waiting?.admission ?? "knocking";
|
|
10973
|
+
const run = (action) => () => {
|
|
10974
|
+
setBusy(true);
|
|
10975
|
+
void action().catch(() => void 0).finally(() => setBusy(false));
|
|
10976
|
+
};
|
|
10977
|
+
const home = /* @__PURE__ */ jsx5(Button, { type: "button", variant: "outline", onClick: () => onLeave !== void 0 ? onLeave() : window.location.assign("/"), children: "Back to home" });
|
|
10978
|
+
let title;
|
|
10979
|
+
let line = null;
|
|
10980
|
+
let actions;
|
|
10981
|
+
switch (admission) {
|
|
10982
|
+
case "waiting_host":
|
|
10983
|
+
title = "Waiting for the host to start";
|
|
10984
|
+
actions = /* @__PURE__ */ jsx5(Button, { type: "button", variant: "outline", disabled: busy, onClick: run(withdraw), children: "Leave" });
|
|
10985
|
+
break;
|
|
10986
|
+
case "denied":
|
|
10987
|
+
title = "The host declined your request";
|
|
10988
|
+
actions = /* @__PURE__ */ jsxs3(Fragment, { children: [
|
|
10989
|
+
waiting?.reknockAllowed === true ? /* @__PURE__ */ jsx5(Button, { type: "button", disabled: busy, onClick: run(askAgain), children: "Ask to join again" }) : null,
|
|
10990
|
+
home
|
|
10991
|
+
] });
|
|
10992
|
+
break;
|
|
10993
|
+
case "expired":
|
|
10994
|
+
title = "No one responded";
|
|
10995
|
+
actions = /* @__PURE__ */ jsxs3(Fragment, { children: [
|
|
10996
|
+
/* @__PURE__ */ jsx5(Button, { type: "button", disabled: busy, onClick: run(askAgain), children: "Ask again" }),
|
|
10997
|
+
home
|
|
10998
|
+
] });
|
|
10999
|
+
break;
|
|
11000
|
+
case "withdrawn":
|
|
11001
|
+
title = "You left the waiting room";
|
|
11002
|
+
actions = /* @__PURE__ */ jsxs3(Fragment, { children: [
|
|
11003
|
+
/* @__PURE__ */ jsx5(Button, { type: "button", disabled: busy, onClick: run(askAgain), children: "Rejoin" }),
|
|
11004
|
+
home
|
|
11005
|
+
] });
|
|
11006
|
+
break;
|
|
11007
|
+
case "ended":
|
|
11008
|
+
title = "The meeting has ended";
|
|
11009
|
+
actions = home;
|
|
11010
|
+
break;
|
|
11011
|
+
default:
|
|
11012
|
+
title = "Waiting for the host to let you in";
|
|
11013
|
+
line = waiting?.admittersPresent === false ? "The host isn't here yet" : null;
|
|
11014
|
+
actions = /* @__PURE__ */ jsx5(Button, { type: "button", variant: "outline", disabled: busy, onClick: run(withdraw), children: "Cancel request" });
|
|
11015
|
+
}
|
|
10719
11016
|
return /* @__PURE__ */ jsxs3("div", { className: "mx-meet__waiting", children: [
|
|
10720
11017
|
/* @__PURE__ */ jsx5(Avatar, { name: host.identity.displayName, url: host.identity.avatarUrl, size: 72 }),
|
|
10721
|
-
/* @__PURE__ */ jsx5("h1", { className: "mx-meet__prejoin-title", children:
|
|
10722
|
-
/* @__PURE__ */
|
|
10723
|
-
|
|
10724
|
-
host.identity.displayName,
|
|
10725
|
-
". You will join automatically once you are admitted."
|
|
10726
|
-
] })
|
|
11018
|
+
/* @__PURE__ */ jsx5("h1", { className: "mx-meet__prejoin-title", children: title }),
|
|
11019
|
+
line !== null ? /* @__PURE__ */ jsx5("p", { className: "mx-meet__empty-body", role: "status", "data-meet-notice": "host-absent", children: line }) : null,
|
|
11020
|
+
/* @__PURE__ */ jsx5("div", { className: "mx-meet__waiting-actions", children: actions })
|
|
10727
11021
|
] });
|
|
10728
11022
|
}
|
|
10729
11023
|
function HostMenu() {
|