@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.cjs
CHANGED
|
@@ -592,7 +592,17 @@ var MEET_ROUTES = {
|
|
|
592
592
|
*/
|
|
593
593
|
collab: "/api/v1/meet/collab",
|
|
594
594
|
/** Meet wave 5: translate finished caption lines into this viewer's language. */
|
|
595
|
-
captionsTranslate: "/api/v1/meet/captions/translate"
|
|
595
|
+
captionsTranslate: "/api/v1/meet/captions/translate",
|
|
596
|
+
/**
|
|
597
|
+
* S4 — THE STATE READ (CORE-DESIGN §2.6): `GET ?meeting_id=` with `x-meet-pass`. This
|
|
598
|
+
* person's admission (lazy expiry applied), whether anyone who can admit is here, the run's
|
|
599
|
+
* phase. Read every `waiting_poll_seconds` while waiting and on every `mx.state.changed`.
|
|
600
|
+
*/
|
|
601
|
+
state: "/api/v1/meet/state",
|
|
602
|
+
/** S4: `withdraw` (stop waiting) or `leave` (close this tab); the pass may ride in the body. */
|
|
603
|
+
leave: "/api/v1/meet/leave",
|
|
604
|
+
/** S4: a pass holder (guests included) saw the recording notice, for this run. */
|
|
605
|
+
consent: "/api/v1/meet/consent"
|
|
596
606
|
};
|
|
597
607
|
function meetingPath(template, meetingId) {
|
|
598
608
|
return template.replace("{meeting_id}", encodeURIComponent(meetingId));
|
|
@@ -666,6 +676,8 @@ function parseRoomToken(body, roomName) {
|
|
|
666
676
|
meetingPassExpiresAt: readString(payload, "meeting_pass_expires_at"),
|
|
667
677
|
sessionId: readString(payload, "session_id"),
|
|
668
678
|
lobby: readBoolean(payload, "lobby", false),
|
|
679
|
+
admissionState: readString(payload, "admission_state") ?? void 0,
|
|
680
|
+
pollSeconds: typeof payload["poll_seconds"] === "number" ? payload["poll_seconds"] : void 0,
|
|
669
681
|
breakoutRoomId: readString(payload, "breakout_room_id") ?? null
|
|
670
682
|
};
|
|
671
683
|
}
|
|
@@ -1253,14 +1265,15 @@ var MEET_EVENTS = {
|
|
|
1253
1265
|
cancel: "mx.call.cancel",
|
|
1254
1266
|
/** You answered (or declined) on another device. Stop ringing here. */
|
|
1255
1267
|
settled: "mx.call.settled",
|
|
1256
|
-
/**
|
|
1257
|
-
|
|
1258
|
-
|
|
1259
|
-
|
|
1260
|
-
|
|
1261
|
-
|
|
1262
|
-
|
|
1263
|
-
|
|
1268
|
+
/**
|
|
1269
|
+
* 🚨 S4 (CORE-DESIGN §2.6): the ONE room-channel event — payload-free "something changed,
|
|
1270
|
+
* read again". Nothing a person must see rides it: a waiting person learns admitted /
|
|
1271
|
+
* denied / expired / ended by READING their row (`GET /v1/meet/state`) on a timer and on
|
|
1272
|
+
* every hint. The four payload broadcasts (`mx.lobby.knock`, `mx.lobby.admission`,
|
|
1273
|
+
* `mx.room.lock`, `mx.room.recording`) are gone with their senders and receivers. The
|
|
1274
|
+
* server publishes the same event on the same topic (aidream `admission.room_topic`).
|
|
1275
|
+
*/
|
|
1276
|
+
stateChanged: "mx.state.changed"
|
|
1264
1277
|
};
|
|
1265
1278
|
|
|
1266
1279
|
// src/core/types.ts
|
|
@@ -1286,6 +1299,7 @@ function isLinkFollower(standing) {
|
|
|
1286
1299
|
function isHostRole(role) {
|
|
1287
1300
|
return role === "host" || role === "cohost";
|
|
1288
1301
|
}
|
|
1302
|
+
var WAITING_ADMISSION_STATES = ["waiting_host", "knocking"];
|
|
1289
1303
|
|
|
1290
1304
|
// src/core/calls.ts
|
|
1291
1305
|
var TRANSITIONS = {
|
|
@@ -2301,6 +2315,7 @@ var NO_GRANTS = {
|
|
|
2301
2315
|
};
|
|
2302
2316
|
var EMPTY = {
|
|
2303
2317
|
phase: "idle",
|
|
2318
|
+
waiting: null,
|
|
2304
2319
|
meeting: null,
|
|
2305
2320
|
localIdentity: null,
|
|
2306
2321
|
grants: NO_GRANTS,
|
|
@@ -2557,19 +2572,25 @@ function roleOf(participants, identity) {
|
|
|
2557
2572
|
return participants.find((p) => p.identity === identity)?.role ?? "participant";
|
|
2558
2573
|
}
|
|
2559
2574
|
var END_MEETING_NOT_SERVED = "Ending a meeting is not available on this server yet";
|
|
2560
|
-
function lobbyEntryFrom(attendee) {
|
|
2561
|
-
if (attendee.admissionState
|
|
2575
|
+
function lobbyEntryFrom(attendee, now = Date.now()) {
|
|
2576
|
+
if (!WAITING_ADMISSION_STATES.includes(attendee.admissionState)) return null;
|
|
2577
|
+
const expires = attendee.knockExpiresAt ? Date.parse(attendee.knockExpiresAt) : Number.NaN;
|
|
2578
|
+
if (!Number.isNaN(expires) && expires <= now) return null;
|
|
2562
2579
|
return {
|
|
2563
2580
|
identity: attendee.identity,
|
|
2564
2581
|
displayName: attendee.displayName,
|
|
2565
2582
|
avatarUrl: attendee.avatarUrl,
|
|
2566
2583
|
userId: attendee.userId,
|
|
2567
2584
|
state: "knocking",
|
|
2568
|
-
knockedAt: attendee.joinedAt ?? (/* @__PURE__ */ new Date()).toISOString(),
|
|
2585
|
+
knockedAt: attendee.knockedAt ?? attendee.joinedAt ?? (/* @__PURE__ */ new Date()).toISOString(),
|
|
2586
|
+
knockExpiresAt: attendee.knockExpiresAt ?? null,
|
|
2569
2587
|
decidedAt: null,
|
|
2570
2588
|
decidedBy: null
|
|
2571
2589
|
};
|
|
2572
2590
|
}
|
|
2591
|
+
function decidedState(state) {
|
|
2592
|
+
return WAITING_ADMISSION_STATES.includes(state) ? "expired" : state;
|
|
2593
|
+
}
|
|
2573
2594
|
function createMeetingSession(options) {
|
|
2574
2595
|
const { manager, repository, store, identity } = options;
|
|
2575
2596
|
let channel = null;
|
|
@@ -2593,11 +2614,6 @@ function createMeetingSession(options) {
|
|
|
2593
2614
|
}
|
|
2594
2615
|
return identity.userId;
|
|
2595
2616
|
}
|
|
2596
|
-
function knockIdentity() {
|
|
2597
|
-
return asParticipantIdentity(
|
|
2598
|
-
identity.userId !== null ? `user:${identity.userId}` : `guest:${identity.deviceId}`
|
|
2599
|
-
);
|
|
2600
|
-
}
|
|
2601
2617
|
function requireMeeting(operation) {
|
|
2602
2618
|
const meeting = store.snapshot().meeting;
|
|
2603
2619
|
if (meeting === null) {
|
|
@@ -2609,6 +2625,191 @@ function createMeetingSession(options) {
|
|
|
2609
2625
|
}
|
|
2610
2626
|
return meeting;
|
|
2611
2627
|
}
|
|
2628
|
+
let polling = false;
|
|
2629
|
+
let pollTimer = null;
|
|
2630
|
+
let reading = null;
|
|
2631
|
+
let admittedOnce = false;
|
|
2632
|
+
let unlisten = null;
|
|
2633
|
+
function report(level, message, remedy) {
|
|
2634
|
+
options.onDiagnostic?.(remedy === void 0 ? { level, message } : { level, message, remedy });
|
|
2635
|
+
}
|
|
2636
|
+
function pollMs() {
|
|
2637
|
+
const seconds = store.snapshot().waiting?.pollSeconds ?? 3;
|
|
2638
|
+
return Math.max(1, Math.min(60, seconds)) * 1e3;
|
|
2639
|
+
}
|
|
2640
|
+
function schedule(ms2) {
|
|
2641
|
+
if (pollTimer !== null) clearTimeout(pollTimer);
|
|
2642
|
+
if (!polling) return;
|
|
2643
|
+
pollTimer = setTimeout(() => {
|
|
2644
|
+
pollTimer = null;
|
|
2645
|
+
void readState2().finally(() => schedule(pollMs()));
|
|
2646
|
+
}, ms2);
|
|
2647
|
+
}
|
|
2648
|
+
function listen() {
|
|
2649
|
+
if (unlisten !== null || typeof window === "undefined") return;
|
|
2650
|
+
const nudge = () => {
|
|
2651
|
+
if (typeof document !== "undefined" && document.visibilityState === "hidden") return;
|
|
2652
|
+
void readState2();
|
|
2653
|
+
};
|
|
2654
|
+
const goodbye = () => {
|
|
2655
|
+
const meeting = store.snapshot().meeting;
|
|
2656
|
+
const pass = options.resolvePass?.() ?? null;
|
|
2657
|
+
const waiting = store.snapshot().waiting;
|
|
2658
|
+
if (meeting === null || pass === null || waiting === null) return;
|
|
2659
|
+
if (!WAITING_ADMISSION_STATES.includes(waiting.admission)) return;
|
|
2660
|
+
try {
|
|
2661
|
+
navigator.sendBeacon?.(
|
|
2662
|
+
options.api.url(MEET_ROUTES.leave),
|
|
2663
|
+
new Blob([JSON.stringify({ meeting_id: meeting.id, intent: "withdraw", meeting_pass: pass })], {
|
|
2664
|
+
type: "application/json"
|
|
2665
|
+
})
|
|
2666
|
+
);
|
|
2667
|
+
} catch {
|
|
2668
|
+
}
|
|
2669
|
+
};
|
|
2670
|
+
window.addEventListener("online", nudge);
|
|
2671
|
+
window.addEventListener("pagehide", goodbye);
|
|
2672
|
+
document.addEventListener("visibilitychange", nudge);
|
|
2673
|
+
unlisten = () => {
|
|
2674
|
+
window.removeEventListener("online", nudge);
|
|
2675
|
+
window.removeEventListener("pagehide", goodbye);
|
|
2676
|
+
document.removeEventListener("visibilitychange", nudge);
|
|
2677
|
+
};
|
|
2678
|
+
}
|
|
2679
|
+
function stopWaiting() {
|
|
2680
|
+
polling = false;
|
|
2681
|
+
if (pollTimer !== null) clearTimeout(pollTimer);
|
|
2682
|
+
pollTimer = null;
|
|
2683
|
+
unlisten?.();
|
|
2684
|
+
unlisten = null;
|
|
2685
|
+
}
|
|
2686
|
+
function patchWaiting(next) {
|
|
2687
|
+
const current = store.snapshot().waiting;
|
|
2688
|
+
const base = current ?? {
|
|
2689
|
+
admission: "knocking",
|
|
2690
|
+
admittersPresent: null,
|
|
2691
|
+
reknockAllowed: true,
|
|
2692
|
+
knockExpiresAt: null,
|
|
2693
|
+
pollSeconds: 3,
|
|
2694
|
+
liveVersion: 0
|
|
2695
|
+
};
|
|
2696
|
+
store.patch({ waiting: { ...base, ...next } });
|
|
2697
|
+
}
|
|
2698
|
+
function apply(read) {
|
|
2699
|
+
const snapshot = store.snapshot();
|
|
2700
|
+
if (snapshot.phase !== "lobby") return;
|
|
2701
|
+
const admission = read.admission_state ?? null;
|
|
2702
|
+
const ended = admission === "ended" || read.session_phase === "ended" || read.meeting_phase === "ended";
|
|
2703
|
+
if (admission === "admitted" && !ended) {
|
|
2704
|
+
if (admittedOnce) return;
|
|
2705
|
+
admittedOnce = true;
|
|
2706
|
+
stopWaiting();
|
|
2707
|
+
options.onAdmitted?.();
|
|
2708
|
+
return;
|
|
2709
|
+
}
|
|
2710
|
+
if (ended) {
|
|
2711
|
+
stopWaiting();
|
|
2712
|
+
patchWaiting({ admission: "ended" });
|
|
2713
|
+
const meeting = snapshot.meeting;
|
|
2714
|
+
if (read.meeting_phase === "ended" && meeting !== null && meeting.endedAt === null) {
|
|
2715
|
+
store.patch({ meeting: { ...meeting, endedAt: (/* @__PURE__ */ new Date()).toISOString() } });
|
|
2716
|
+
}
|
|
2717
|
+
return;
|
|
2718
|
+
}
|
|
2719
|
+
if (admission === null) return;
|
|
2720
|
+
patchWaiting({
|
|
2721
|
+
admission,
|
|
2722
|
+
admittersPresent: read.admitters_present ?? null,
|
|
2723
|
+
reknockAllowed: read.reknock_allowed ?? true,
|
|
2724
|
+
knockExpiresAt: read.knock_expires_at ?? null,
|
|
2725
|
+
pollSeconds: read.poll_seconds ?? snapshot.waiting?.pollSeconds ?? 3,
|
|
2726
|
+
liveVersion: read.live_version ?? 0
|
|
2727
|
+
});
|
|
2728
|
+
if (admission === "denied" || admission === "expired" || admission === "withdrawn" || admission === "removed") {
|
|
2729
|
+
stopWaiting();
|
|
2730
|
+
}
|
|
2731
|
+
}
|
|
2732
|
+
async function readState2() {
|
|
2733
|
+
if (reading !== null) return reading;
|
|
2734
|
+
const meeting = store.snapshot().meeting;
|
|
2735
|
+
const pass = options.resolvePass?.() ?? null;
|
|
2736
|
+
if (meeting === null || pass === null) return;
|
|
2737
|
+
reading = (async () => {
|
|
2738
|
+
try {
|
|
2739
|
+
const response = await options.api.request({
|
|
2740
|
+
path: `${MEET_ROUTES.state}?meeting_id=${encodeURIComponent(meeting.id)}`,
|
|
2741
|
+
method: "GET",
|
|
2742
|
+
requireAuth: false,
|
|
2743
|
+
operation: "meet.state",
|
|
2744
|
+
headers: { [MEETING_PASS_HEADER]: pass }
|
|
2745
|
+
});
|
|
2746
|
+
if (!response.ok) {
|
|
2747
|
+
let body = null;
|
|
2748
|
+
try {
|
|
2749
|
+
body = await response.json();
|
|
2750
|
+
} catch {
|
|
2751
|
+
body = null;
|
|
2752
|
+
}
|
|
2753
|
+
const refusal2 = readServerRefusal(body);
|
|
2754
|
+
report(
|
|
2755
|
+
"warn",
|
|
2756
|
+
refusal2?.reason ?? `meet.state: the server answered ${response.status}`,
|
|
2757
|
+
refusal2?.remedy ?? "The waiting room keeps asking; nothing on this screen changes until the server answers."
|
|
2758
|
+
);
|
|
2759
|
+
return;
|
|
2760
|
+
}
|
|
2761
|
+
apply(await response.json());
|
|
2762
|
+
} catch (error) {
|
|
2763
|
+
report("info", `meet.state: ${String(error)}`, "Waiting continues; the state is read again shortly.");
|
|
2764
|
+
} finally {
|
|
2765
|
+
reading = null;
|
|
2766
|
+
}
|
|
2767
|
+
})();
|
|
2768
|
+
return reading;
|
|
2769
|
+
}
|
|
2770
|
+
async function postLeave(intent) {
|
|
2771
|
+
const meeting = requireMeeting(`meet.${intent}`);
|
|
2772
|
+
const pass = options.resolvePass?.() ?? null;
|
|
2773
|
+
const response = await options.api.request({
|
|
2774
|
+
path: MEET_ROUTES.leave,
|
|
2775
|
+
method: "POST",
|
|
2776
|
+
requireAuth: false,
|
|
2777
|
+
operation: `meet.${intent}`,
|
|
2778
|
+
body: { meeting_id: meeting.id, intent },
|
|
2779
|
+
...pass !== null ? { headers: { [MEETING_PASS_HEADER]: pass } } : {}
|
|
2780
|
+
});
|
|
2781
|
+
if (!response.ok) {
|
|
2782
|
+
let body = null;
|
|
2783
|
+
try {
|
|
2784
|
+
body = await response.json();
|
|
2785
|
+
} catch {
|
|
2786
|
+
body = null;
|
|
2787
|
+
}
|
|
2788
|
+
const refusal2 = readServerRefusal(body);
|
|
2789
|
+
throw new MeetError(
|
|
2790
|
+
"transport",
|
|
2791
|
+
refusal2?.reason ?? `meet.${intent}: the server answered ${response.status}`,
|
|
2792
|
+
refusal2?.remedy ?? "You are still in the waiting room. Try again."
|
|
2793
|
+
);
|
|
2794
|
+
}
|
|
2795
|
+
}
|
|
2796
|
+
function hint() {
|
|
2797
|
+
channel?.send(MEET_EVENTS.stateChanged, { v: null });
|
|
2798
|
+
}
|
|
2799
|
+
async function refreshQueue() {
|
|
2800
|
+
const meeting = store.snapshot().meeting;
|
|
2801
|
+
if (meeting === null || isGuest(identity) || !isHostRole(effectiveRole(store.snapshot()))) return;
|
|
2802
|
+
try {
|
|
2803
|
+
const attendees = await repository.attendees(meeting.id);
|
|
2804
|
+
store.learn(attendees);
|
|
2805
|
+
for (const attendee of attendees) {
|
|
2806
|
+
const entry = lobbyEntryFrom(attendee);
|
|
2807
|
+
if (entry !== null) store.upsertLobby(entry);
|
|
2808
|
+
else store.setAdmission(attendee.identity, decidedState(attendee.admissionState), null);
|
|
2809
|
+
}
|
|
2810
|
+
} catch {
|
|
2811
|
+
}
|
|
2812
|
+
}
|
|
2612
2813
|
return {
|
|
2613
2814
|
watch(roomName) {
|
|
2614
2815
|
channel?.close();
|
|
@@ -2616,58 +2817,22 @@ function createMeetingSession(options) {
|
|
|
2616
2817
|
topic: roomTopic(roomName),
|
|
2617
2818
|
broadcast: [
|
|
2618
2819
|
{
|
|
2619
|
-
|
|
2620
|
-
|
|
2621
|
-
|
|
2622
|
-
|
|
2623
|
-
|
|
2624
|
-
{
|
|
2625
|
-
event: MEET_EVENTS.admission,
|
|
2626
|
-
onMessage: (message) => {
|
|
2627
|
-
const verdict = message.data;
|
|
2628
|
-
const decided = asParticipantIdentity(verdict.identity);
|
|
2629
|
-
store.setAdmission(
|
|
2630
|
-
decided,
|
|
2631
|
-
verdict.admitted ? "admitted" : "denied",
|
|
2632
|
-
verdict.by === null ? null : verdict.by
|
|
2633
|
-
);
|
|
2634
|
-
if (decided === knockIdentity()) {
|
|
2635
|
-
if (verdict.admitted) options.onAdmitted?.();
|
|
2636
|
-
else options.onDenied?.();
|
|
2637
|
-
}
|
|
2638
|
-
}
|
|
2639
|
-
},
|
|
2640
|
-
{
|
|
2641
|
-
event: MEET_EVENTS.lock,
|
|
2642
|
-
onMessage: (message) => {
|
|
2643
|
-
const { locked } = message.data;
|
|
2644
|
-
const meeting = store.snapshot().meeting;
|
|
2645
|
-
store.patch({
|
|
2646
|
-
locked,
|
|
2647
|
-
...meeting !== null ? { meeting: { ...meeting, locked } } : {}
|
|
2648
|
-
});
|
|
2649
|
-
}
|
|
2650
|
-
},
|
|
2651
|
-
{
|
|
2652
|
-
event: MEET_EVENTS.recording,
|
|
2653
|
-
onMessage: (message) => {
|
|
2654
|
-
const payload = message.data;
|
|
2655
|
-
store.setRecording(
|
|
2656
|
-
payload.state,
|
|
2657
|
-
payload.by === null ? null : payload.by,
|
|
2658
|
-
"room"
|
|
2659
|
-
);
|
|
2820
|
+
// 🚨 PAYLOAD-FREE. Every receiver reads again; a spoofed hint only causes a read.
|
|
2821
|
+
event: MEET_EVENTS.stateChanged,
|
|
2822
|
+
onMessage: () => {
|
|
2823
|
+
if (store.snapshot().phase === "lobby") void readState2();
|
|
2824
|
+
void refreshQueue();
|
|
2660
2825
|
}
|
|
2661
2826
|
}
|
|
2662
2827
|
],
|
|
2663
|
-
// THE BACKFILL DOOR. A
|
|
2664
|
-
//
|
|
2665
|
-
// re-announce on their own timer.
|
|
2828
|
+
// THE BACKFILL DOOR. A socket blip must not lose a decision: re-read the meeting
|
|
2829
|
+
// row, and a waiting tab re-reads its own state.
|
|
2666
2830
|
onBackfill: async () => {
|
|
2667
2831
|
const meeting = store.snapshot().meeting;
|
|
2668
2832
|
if (meeting === null) return;
|
|
2669
2833
|
const fresh = isGuest(identity) ? await repository.meetingBySlug(meeting.slug) : await readMeeting(meeting.id);
|
|
2670
2834
|
store.patch({ meeting: fresh, locked: fresh.locked });
|
|
2835
|
+
if (store.snapshot().phase === "lobby") void readState2();
|
|
2671
2836
|
}
|
|
2672
2837
|
});
|
|
2673
2838
|
},
|
|
@@ -2728,7 +2893,7 @@ function createMeetingSession(options) {
|
|
|
2728
2893
|
store.upsertLobby(entry);
|
|
2729
2894
|
return;
|
|
2730
2895
|
}
|
|
2731
|
-
store.setAdmission(attendee.identity, attendee.admissionState, null);
|
|
2896
|
+
store.setAdmission(attendee.identity, decidedState(attendee.admissionState), null);
|
|
2732
2897
|
}
|
|
2733
2898
|
},
|
|
2734
2899
|
{
|
|
@@ -2780,62 +2945,75 @@ function createMeetingSession(options) {
|
|
|
2780
2945
|
if (entry !== null) {
|
|
2781
2946
|
store.upsertLobby(entry);
|
|
2782
2947
|
} else {
|
|
2783
|
-
store.setAdmission(attendee.identity, attendee.admissionState, null);
|
|
2948
|
+
store.setAdmission(attendee.identity, decidedState(attendee.admissionState), null);
|
|
2784
2949
|
}
|
|
2785
2950
|
}
|
|
2786
2951
|
}
|
|
2787
2952
|
});
|
|
2788
2953
|
meetingChannel.requestBackfill("manual");
|
|
2789
2954
|
},
|
|
2790
|
-
knock(roomName) {
|
|
2791
|
-
const handle = channel ?? manager.open({ topic: roomTopic(roomName) });
|
|
2792
|
-
channel = handle;
|
|
2793
|
-
const entry = {
|
|
2794
|
-
identity: knockIdentity(),
|
|
2795
|
-
displayName: identity.displayName,
|
|
2796
|
-
avatarUrl: identity.avatarUrl,
|
|
2797
|
-
userId: identity.userId,
|
|
2798
|
-
state: "knocking",
|
|
2799
|
-
knockedAt: (/* @__PURE__ */ new Date()).toISOString(),
|
|
2800
|
-
decidedAt: null,
|
|
2801
|
-
decidedBy: null
|
|
2802
|
-
};
|
|
2803
|
-
store.upsertLobby(entry);
|
|
2804
|
-
handle.send(MEET_EVENTS.knock, entry);
|
|
2805
|
-
},
|
|
2806
2955
|
async admit(entry, admitted) {
|
|
2807
2956
|
const by = requireHostUser("meet.admit");
|
|
2808
2957
|
const meeting = requireMeeting("meet.admit");
|
|
2809
|
-
await repository.
|
|
2958
|
+
const row = await repository.decideAdmission({
|
|
2810
2959
|
meetingId: meeting.id,
|
|
2811
2960
|
identity: entry.identity,
|
|
2812
|
-
admitted,
|
|
2961
|
+
decision: admitted ? "admit" : "deny",
|
|
2813
2962
|
byUserId: by
|
|
2814
2963
|
});
|
|
2815
2964
|
store.upsertLobby({
|
|
2816
2965
|
...entry,
|
|
2817
|
-
state:
|
|
2966
|
+
state: decidedState(row.admissionState),
|
|
2818
2967
|
decidedAt: (/* @__PURE__ */ new Date()).toISOString(),
|
|
2819
2968
|
decidedBy: by
|
|
2820
2969
|
});
|
|
2821
|
-
|
|
2970
|
+
hint();
|
|
2971
|
+
},
|
|
2972
|
+
async undeny(entry) {
|
|
2973
|
+
const by = requireHostUser("meet.undeny");
|
|
2974
|
+
const meeting = requireMeeting("meet.undeny");
|
|
2975
|
+
const row = await repository.decideAdmission({
|
|
2976
|
+
meetingId: meeting.id,
|
|
2822
2977
|
identity: entry.identity,
|
|
2823
|
-
|
|
2824
|
-
by
|
|
2978
|
+
decision: "undeny",
|
|
2979
|
+
byUserId: by
|
|
2825
2980
|
});
|
|
2981
|
+
store.upsertLobby({ ...entry, state: decidedState(row.admissionState), decidedAt: (/* @__PURE__ */ new Date()).toISOString(), decidedBy: by });
|
|
2982
|
+
hint();
|
|
2826
2983
|
},
|
|
2827
2984
|
async admitAll() {
|
|
2828
|
-
const
|
|
2829
|
-
|
|
2830
|
-
|
|
2831
|
-
|
|
2985
|
+
const by = requireHostUser("meet.admitAll");
|
|
2986
|
+
const meeting = requireMeeting("meet.admitAll");
|
|
2987
|
+
const rows = await repository.admitAll(meeting.id, by);
|
|
2988
|
+
for (const row of rows) {
|
|
2989
|
+
store.setAdmission(row.identity, row.admissionState, by);
|
|
2990
|
+
}
|
|
2991
|
+
hint();
|
|
2992
|
+
},
|
|
2993
|
+
startWaiting() {
|
|
2994
|
+
if (polling) return;
|
|
2995
|
+
polling = true;
|
|
2996
|
+
admittedOnce = false;
|
|
2997
|
+
listen();
|
|
2998
|
+
schedule(0);
|
|
2999
|
+
},
|
|
3000
|
+
stopWaiting,
|
|
3001
|
+
readState: readState2,
|
|
3002
|
+
async withdraw() {
|
|
3003
|
+
stopWaiting();
|
|
3004
|
+
await postLeave("withdraw");
|
|
3005
|
+
patchWaiting({ admission: "withdrawn" });
|
|
3006
|
+
hint();
|
|
3007
|
+
},
|
|
3008
|
+
async askAgain() {
|
|
3009
|
+
await options.rejoin?.();
|
|
2832
3010
|
},
|
|
2833
3011
|
async setLock(locked) {
|
|
2834
3012
|
const by = requireHostUser("meet.setLock");
|
|
2835
3013
|
const meeting = requireMeeting("meet.setLock");
|
|
2836
3014
|
const updated = await repository.setLock(meeting.id, locked, by);
|
|
2837
3015
|
store.patch({ meeting: updated, locked: updated.locked });
|
|
2838
|
-
|
|
3016
|
+
hint();
|
|
2839
3017
|
return updated;
|
|
2840
3018
|
},
|
|
2841
3019
|
/**
|
|
@@ -2893,6 +3071,7 @@ function createMeetingSession(options) {
|
|
|
2893
3071
|
return { endedAt, served: true };
|
|
2894
3072
|
},
|
|
2895
3073
|
dispose() {
|
|
3074
|
+
stopWaiting();
|
|
2896
3075
|
channel?.close();
|
|
2897
3076
|
channel = null;
|
|
2898
3077
|
meetingChannel?.close();
|
|
@@ -2937,7 +3116,14 @@ var RPCS = {
|
|
|
2937
3116
|
getOrCreateMeeting: "meet_get_or_create_meeting",
|
|
2938
3117
|
/** Resolve a durable public link, before/during/after its scheduled time. */
|
|
2939
3118
|
meetingBySlug: "meet_meeting_by_slug",
|
|
2940
|
-
|
|
3119
|
+
/*
|
|
3120
|
+
* ── S4 ADMISSION (CORE-DESIGN §2.4). Each returns the decided row; the caller then
|
|
3121
|
+
* sends the payload-free `mx.state.changed` hint. Authorized by `who_may_admit`.
|
|
3122
|
+
*/
|
|
3123
|
+
admit: "meet_admit",
|
|
3124
|
+
deny: "meet_deny",
|
|
3125
|
+
undeny: "meet_undeny",
|
|
3126
|
+
admitAll: "meet_admit_all",
|
|
2941
3127
|
setLock: "meet_set_lock",
|
|
2942
3128
|
recordConsent: "meet_record_consent",
|
|
2943
3129
|
/*
|
|
@@ -2980,13 +3166,13 @@ var RPCS = {
|
|
|
2980
3166
|
/** Titles, agendas, current notes and final transcript lines; snippets mark hits with ⟦ ⟧. */
|
|
2981
3167
|
search: "meet_search"
|
|
2982
3168
|
};
|
|
2983
|
-
var VOID_RPCS = [RPCS.
|
|
3169
|
+
var VOID_RPCS = [RPCS.recordConsent];
|
|
2984
3170
|
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";
|
|
2985
3171
|
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";
|
|
2986
3172
|
var RECORDING_COLUMNS = "id,meeting_id,state,started_at,ended_at,started_by,file_id,failure_reason";
|
|
2987
3173
|
var SEGMENT_COLUMNS = "id,meeting_id,identity,speaker,text,started_at,is_final,metadata";
|
|
2988
3174
|
var NOTE_COLUMNS = "id,meeting_id,text,kind,assignee_user_id,created_at,metadata";
|
|
2989
|
-
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";
|
|
3175
|
+
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";
|
|
2990
3176
|
function projectStays(value) {
|
|
2991
3177
|
if (!Array.isArray(value)) return [];
|
|
2992
3178
|
const stays = [];
|
|
@@ -3353,7 +3539,16 @@ function createMeetRepository(options) {
|
|
|
3353
3539
|
const role = text(row, "role");
|
|
3354
3540
|
const admission = text(row, "admission_state");
|
|
3355
3541
|
const known = ["host", "cohost", "presenter", "participant", "viewer"];
|
|
3356
|
-
const states = [
|
|
3542
|
+
const states = [
|
|
3543
|
+
"waiting_host",
|
|
3544
|
+
"knocking",
|
|
3545
|
+
"admitted",
|
|
3546
|
+
"denied",
|
|
3547
|
+
"expired",
|
|
3548
|
+
"withdrawn",
|
|
3549
|
+
"removed",
|
|
3550
|
+
"ended"
|
|
3551
|
+
];
|
|
3357
3552
|
const userId = text(row, "participant_user_id");
|
|
3358
3553
|
const identity = requireText(row, "identity", operation);
|
|
3359
3554
|
return {
|
|
@@ -3367,6 +3562,8 @@ function createMeetRepository(options) {
|
|
|
3367
3562
|
userId: userId === null ? null : asUserId(userId),
|
|
3368
3563
|
role: known.includes(role) ? role : "participant",
|
|
3369
3564
|
admissionState: states.includes(admission) ? admission : "admitted",
|
|
3565
|
+
knockedAt: text(row, "knocked_at"),
|
|
3566
|
+
knockExpiresAt: text(row, "knock_expires_at"),
|
|
3370
3567
|
isAgent: bool(row, "is_agent", false),
|
|
3371
3568
|
joinedAt: text(row, "joined_at"),
|
|
3372
3569
|
leftAt: text(row, "left_at"),
|
|
@@ -3645,16 +3842,26 @@ function createMeetRepository(options) {
|
|
|
3645
3842
|
hasMore: rows.length > limit
|
|
3646
3843
|
};
|
|
3647
3844
|
},
|
|
3648
|
-
async
|
|
3649
|
-
|
|
3650
|
-
|
|
3651
|
-
|
|
3845
|
+
async decideAdmission({ meetingId, identity, decision, byUserId }) {
|
|
3846
|
+
const fn = decision === "admit" ? RPCS.admit : decision === "deny" ? RPCS.deny : RPCS.undeny;
|
|
3847
|
+
const row = await guard(
|
|
3848
|
+
`meet.${decision}`,
|
|
3849
|
+
() => db.rpc(fn, {
|
|
3652
3850
|
p_meeting_id: meetingId,
|
|
3653
3851
|
p_identity: identity,
|
|
3654
|
-
p_admitted: admitted,
|
|
3655
3852
|
p_by_user_id: byUserId
|
|
3656
3853
|
})
|
|
3657
3854
|
);
|
|
3855
|
+
return projectAttendee(row);
|
|
3856
|
+
},
|
|
3857
|
+
async admitAll(meetingId, byUserId) {
|
|
3858
|
+
const response = await db.rpc(RPCS.admitAll, {
|
|
3859
|
+
p_meeting_id: meetingId,
|
|
3860
|
+
p_by_user_id: byUserId
|
|
3861
|
+
});
|
|
3862
|
+
if (response.error !== null) throw normalizeMeetError(response.error, "meet.admitAll");
|
|
3863
|
+
const rows = Array.isArray(response.data) ? response.data : [];
|
|
3864
|
+
return rows.map(projectAttendee);
|
|
3658
3865
|
},
|
|
3659
3866
|
async setLock(meetingId, locked, byUserId) {
|
|
3660
3867
|
const row = await guard(
|
|
@@ -4499,7 +4706,7 @@ function createRoomEngine(options) {
|
|
|
4499
4706
|
}
|
|
4500
4707
|
};
|
|
4501
4708
|
async function connect(request) {
|
|
4502
|
-
store.patch({ phase: "requesting-token", failure: null });
|
|
4709
|
+
store.patch({ phase: "requesting-token", failure: null, waiting: null });
|
|
4503
4710
|
const token = await tokens.get({
|
|
4504
4711
|
roomName: request.roomName,
|
|
4505
4712
|
identity,
|
|
@@ -4519,11 +4726,21 @@ function createRoomEngine(options) {
|
|
|
4519
4726
|
canSubscribe: token.canSubscribe
|
|
4520
4727
|
}
|
|
4521
4728
|
});
|
|
4522
|
-
if (token.lobby) {
|
|
4523
|
-
store.patch({
|
|
4729
|
+
if (token.lobby || !token.canSubscribe) {
|
|
4730
|
+
store.patch({
|
|
4731
|
+
phase: "lobby",
|
|
4732
|
+
waiting: {
|
|
4733
|
+
admission: token.admissionState !== void 0 && token.admissionState !== "admitted" ? token.admissionState : "knocking",
|
|
4734
|
+
admittersPresent: null,
|
|
4735
|
+
reknockAllowed: true,
|
|
4736
|
+
knockExpiresAt: null,
|
|
4737
|
+
pollSeconds: token.pollSeconds ?? 3,
|
|
4738
|
+
liveVersion: 0
|
|
4739
|
+
}
|
|
4740
|
+
});
|
|
4524
4741
|
return;
|
|
4525
4742
|
}
|
|
4526
|
-
store.patch({ phase: "connecting" });
|
|
4743
|
+
store.patch({ phase: "connecting", waiting: null });
|
|
4527
4744
|
const created = await options.driverFactory(handlers);
|
|
4528
4745
|
if (disposed) {
|
|
4529
4746
|
await created.disconnect();
|
|
@@ -5606,14 +5823,18 @@ function MeetRuntime(props) {
|
|
|
5606
5823
|
api,
|
|
5607
5824
|
onDiagnostic: report,
|
|
5608
5825
|
// ADMISSION IS A JOIN, NOT A RE-RENDER. The lobby token grants neither
|
|
5609
|
-
// publish nor subscribe, so the host saying yes has to mint a new one.
|
|
5826
|
+
// publish nor subscribe, so the host saying yes has to mint a new one. The
|
|
5827
|
+
// "yes" is read from the server (the state read), never a broadcast (S4).
|
|
5610
5828
|
onAdmitted: () => {
|
|
5611
|
-
void engine.rejoin();
|
|
5612
|
-
},
|
|
5613
|
-
// A "no" is ALSO a mint: the server refuses it with its own sentence, which
|
|
5614
|
-
// turns the waiting room into the pre-join screen showing that sentence.
|
|
5615
|
-
onDenied: () => {
|
|
5616
5829
|
void engine.rejoin().catch(() => void 0);
|
|
5830
|
+
},
|
|
5831
|
+
rejoin: () => engine.rejoin(),
|
|
5832
|
+
// THE MEETING PASS this device holds — what the state read and `/leave` carry.
|
|
5833
|
+
resolvePass: () => {
|
|
5834
|
+
const roomName = store.snapshot().meeting?.roomName;
|
|
5835
|
+
const active = engine.token();
|
|
5836
|
+
if (active?.meetingPass) return active.meetingPass;
|
|
5837
|
+
return roomName === void 0 ? null : tokens.remembered(roomName)?.meetingPass ?? null;
|
|
5617
5838
|
}
|
|
5618
5839
|
});
|
|
5619
5840
|
const resolveRoomToken = () => {
|
|
@@ -5625,6 +5846,14 @@ function MeetRuntime(props) {
|
|
|
5625
5846
|
};
|
|
5626
5847
|
const ai = createMeetAi({ api, store, resolveRoomToken });
|
|
5627
5848
|
const inRoom = createInRoomClient({ api, store, resolveRoomToken });
|
|
5849
|
+
store.subscribe(() => {
|
|
5850
|
+
const snapshot = store.snapshot();
|
|
5851
|
+
if (snapshot.phase === "lobby" && snapshot.waiting !== null && (snapshot.waiting.admission === "knocking" || snapshot.waiting.admission === "waiting_host")) {
|
|
5852
|
+
meetings.startWaiting();
|
|
5853
|
+
} else if (snapshot.phase !== "lobby") {
|
|
5854
|
+
meetings.stopWaiting();
|
|
5855
|
+
}
|
|
5856
|
+
});
|
|
5628
5857
|
let chatReadFor = null;
|
|
5629
5858
|
store.subscribe(() => {
|
|
5630
5859
|
const snapshot = store.snapshot();
|
|
@@ -5832,7 +6061,7 @@ function observedPhase(input) {
|
|
|
5832
6061
|
case "left":
|
|
5833
6062
|
return "prejoin";
|
|
5834
6063
|
case "lobby":
|
|
5835
|
-
return
|
|
6064
|
+
return waitingPhase(snapshot);
|
|
5836
6065
|
case "requesting-token":
|
|
5837
6066
|
case "connecting":
|
|
5838
6067
|
return "joining";
|
|
@@ -5841,6 +6070,24 @@ function observedPhase(input) {
|
|
|
5841
6070
|
return "in_call";
|
|
5842
6071
|
}
|
|
5843
6072
|
}
|
|
6073
|
+
function waitingPhase(snapshot) {
|
|
6074
|
+
switch (snapshot.waiting?.admission) {
|
|
6075
|
+
case "waiting_host":
|
|
6076
|
+
return "waiting:host_not_started";
|
|
6077
|
+
case "denied":
|
|
6078
|
+
return "denied";
|
|
6079
|
+
case "expired":
|
|
6080
|
+
return "knock_expired";
|
|
6081
|
+
case "withdrawn":
|
|
6082
|
+
return "left";
|
|
6083
|
+
case "ended":
|
|
6084
|
+
return "ended";
|
|
6085
|
+
case "removed":
|
|
6086
|
+
return "refused:removed";
|
|
6087
|
+
default:
|
|
6088
|
+
return "waiting:knocking";
|
|
6089
|
+
}
|
|
6090
|
+
}
|
|
5844
6091
|
var ADMITTER_ROLES = /* @__PURE__ */ new Set(["host", "cohost"]);
|
|
5845
6092
|
function observe(input) {
|
|
5846
6093
|
const { snapshot } = input;
|
|
@@ -5855,7 +6102,7 @@ function observe(input) {
|
|
|
5855
6102
|
reason: phase.startsWith("refused:") ? phase.slice("refused:".length) : null,
|
|
5856
6103
|
role,
|
|
5857
6104
|
connection: snapshot?.phase === "reconnecting" ? "reconnecting" : "stable",
|
|
5858
|
-
hostPresent: inCall && snapshot !== null ? snapshot.participants.some((p) => ADMITTER_ROLES.has(p.role)) : null,
|
|
6105
|
+
hostPresent: inCall && snapshot !== null ? snapshot.participants.some((p) => ADMITTER_ROLES.has(p.role)) : snapshot?.phase === "lobby" ? snapshot.waiting?.admittersPresent ?? null : null,
|
|
5859
6106
|
lobbyCount: canAdmit && snapshot !== null ? snapshot.lobby.filter((entry) => entry.state === "knocking").length : 0,
|
|
5860
6107
|
recording: snapshot?.recording === "recording" || snapshot?.recording === "stopping",
|
|
5861
6108
|
audioBlocked: null,
|
|
@@ -6816,12 +7063,22 @@ function useLobby() {
|
|
|
6816
7063
|
async () => requireSession(session, "admitAll").admitAll(),
|
|
6817
7064
|
[session]
|
|
6818
7065
|
),
|
|
6819
|
-
|
|
6820
|
-
(
|
|
7066
|
+
undeny: (0, import_react4.useCallback)(
|
|
7067
|
+
async (entry) => requireSession(session, "undeny").undeny(entry),
|
|
6821
7068
|
[session]
|
|
6822
7069
|
)
|
|
6823
7070
|
};
|
|
6824
7071
|
}
|
|
7072
|
+
function useWaiting() {
|
|
7073
|
+
const host = useMeetHost();
|
|
7074
|
+
const snapshot = useMeetSnapshot();
|
|
7075
|
+
const session = host?.meetings ?? null;
|
|
7076
|
+
return {
|
|
7077
|
+
waiting: snapshot?.waiting ?? null,
|
|
7078
|
+
withdraw: (0, import_react4.useCallback)(async () => requireSession(session, "withdraw").withdraw(), [session]),
|
|
7079
|
+
askAgain: (0, import_react4.useCallback)(async () => requireSession(session, "askAgain").askAgain(), [session])
|
|
7080
|
+
};
|
|
7081
|
+
}
|
|
6825
7082
|
function useRecording() {
|
|
6826
7083
|
const host = useMeetHost();
|
|
6827
7084
|
const snapshot = useMeetSnapshot();
|
|
@@ -10952,7 +11209,7 @@ function MeetSkinRoom({
|
|
|
10952
11209
|
) });
|
|
10953
11210
|
}
|
|
10954
11211
|
if (phase === "lobby") {
|
|
10955
|
-
return /* @__PURE__ */ (0, import_jsx_runtime5.jsx)("div", { className: "mx-meet mx-meet--lobby", children: /* @__PURE__ */ (0, import_jsx_runtime5.jsx)(WaitingRoom, {
|
|
11212
|
+
return /* @__PURE__ */ (0, import_jsx_runtime5.jsx)("div", { className: "mx-meet mx-meet--lobby", children: /* @__PURE__ */ (0, import_jsx_runtime5.jsx)(WaitingRoom, { host, ...onLeave !== void 0 ? { onLeave } : {} }) });
|
|
10956
11213
|
}
|
|
10957
11214
|
return /* @__PURE__ */ (0, import_jsx_runtime5.jsxs)("div", { className: "mx-meet", children: [
|
|
10958
11215
|
/* @__PURE__ */ (0, import_jsx_runtime5.jsx)(ConsentNotice, {}),
|
|
@@ -10977,21 +11234,58 @@ function MeetSkinRoom({
|
|
|
10977
11234
|
/* @__PURE__ */ (0, import_jsx_runtime5.jsx)(ControlBar, { ...onLeave !== void 0 ? { onLeave } : {} })
|
|
10978
11235
|
] });
|
|
10979
11236
|
}
|
|
10980
|
-
function WaitingRoom({
|
|
10981
|
-
const
|
|
10982
|
-
(0, import_react9.
|
|
10983
|
-
|
|
10984
|
-
|
|
10985
|
-
|
|
10986
|
-
|
|
11237
|
+
function WaitingRoom({ host, onLeave }) {
|
|
11238
|
+
const { waiting, withdraw, askAgain } = useWaiting();
|
|
11239
|
+
const [busy, setBusy] = (0, import_react9.useState)(false);
|
|
11240
|
+
const admission = waiting?.admission ?? "knocking";
|
|
11241
|
+
const run = (action) => () => {
|
|
11242
|
+
setBusy(true);
|
|
11243
|
+
void action().catch(() => void 0).finally(() => setBusy(false));
|
|
11244
|
+
};
|
|
11245
|
+
const home = /* @__PURE__ */ (0, import_jsx_runtime5.jsx)(import_design_system.Button, { type: "button", variant: "outline", onClick: () => onLeave !== void 0 ? onLeave() : window.location.assign("/"), children: "Back to home" });
|
|
11246
|
+
let title;
|
|
11247
|
+
let line = null;
|
|
11248
|
+
let actions;
|
|
11249
|
+
switch (admission) {
|
|
11250
|
+
case "waiting_host":
|
|
11251
|
+
title = "Waiting for the host to start";
|
|
11252
|
+
actions = /* @__PURE__ */ (0, import_jsx_runtime5.jsx)(import_design_system.Button, { type: "button", variant: "outline", disabled: busy, onClick: run(withdraw), children: "Leave" });
|
|
11253
|
+
break;
|
|
11254
|
+
case "denied":
|
|
11255
|
+
title = "The host declined your request";
|
|
11256
|
+
actions = /* @__PURE__ */ (0, import_jsx_runtime5.jsxs)(import_jsx_runtime5.Fragment, { children: [
|
|
11257
|
+
waiting?.reknockAllowed === true ? /* @__PURE__ */ (0, import_jsx_runtime5.jsx)(import_design_system.Button, { type: "button", disabled: busy, onClick: run(askAgain), children: "Ask to join again" }) : null,
|
|
11258
|
+
home
|
|
11259
|
+
] });
|
|
11260
|
+
break;
|
|
11261
|
+
case "expired":
|
|
11262
|
+
title = "No one responded";
|
|
11263
|
+
actions = /* @__PURE__ */ (0, import_jsx_runtime5.jsxs)(import_jsx_runtime5.Fragment, { children: [
|
|
11264
|
+
/* @__PURE__ */ (0, import_jsx_runtime5.jsx)(import_design_system.Button, { type: "button", disabled: busy, onClick: run(askAgain), children: "Ask again" }),
|
|
11265
|
+
home
|
|
11266
|
+
] });
|
|
11267
|
+
break;
|
|
11268
|
+
case "withdrawn":
|
|
11269
|
+
title = "You left the waiting room";
|
|
11270
|
+
actions = /* @__PURE__ */ (0, import_jsx_runtime5.jsxs)(import_jsx_runtime5.Fragment, { children: [
|
|
11271
|
+
/* @__PURE__ */ (0, import_jsx_runtime5.jsx)(import_design_system.Button, { type: "button", disabled: busy, onClick: run(askAgain), children: "Rejoin" }),
|
|
11272
|
+
home
|
|
11273
|
+
] });
|
|
11274
|
+
break;
|
|
11275
|
+
case "ended":
|
|
11276
|
+
title = "The meeting has ended";
|
|
11277
|
+
actions = home;
|
|
11278
|
+
break;
|
|
11279
|
+
default:
|
|
11280
|
+
title = "Waiting for the host to let you in";
|
|
11281
|
+
line = waiting?.admittersPresent === false ? "The host isn't here yet" : null;
|
|
11282
|
+
actions = /* @__PURE__ */ (0, import_jsx_runtime5.jsx)(import_design_system.Button, { type: "button", variant: "outline", disabled: busy, onClick: run(withdraw), children: "Cancel request" });
|
|
11283
|
+
}
|
|
10987
11284
|
return /* @__PURE__ */ (0, import_jsx_runtime5.jsxs)("div", { className: "mx-meet__waiting", children: [
|
|
10988
11285
|
/* @__PURE__ */ (0, import_jsx_runtime5.jsx)(Avatar, { name: host.identity.displayName, url: host.identity.avatarUrl, size: 72 }),
|
|
10989
|
-
/* @__PURE__ */ (0, import_jsx_runtime5.jsx)("h1", { className: "mx-meet__prejoin-title", children:
|
|
10990
|
-
/* @__PURE__ */ (0, import_jsx_runtime5.
|
|
10991
|
-
|
|
10992
|
-
host.identity.displayName,
|
|
10993
|
-
". You will join automatically once you are admitted."
|
|
10994
|
-
] })
|
|
11286
|
+
/* @__PURE__ */ (0, import_jsx_runtime5.jsx)("h1", { className: "mx-meet__prejoin-title", children: title }),
|
|
11287
|
+
line !== null ? /* @__PURE__ */ (0, import_jsx_runtime5.jsx)("p", { className: "mx-meet__empty-body", role: "status", "data-meet-notice": "host-absent", children: line }) : null,
|
|
11288
|
+
/* @__PURE__ */ (0, import_jsx_runtime5.jsx)("div", { className: "mx-meet__waiting-actions", children: actions })
|
|
10995
11289
|
] });
|
|
10996
11290
|
}
|
|
10997
11291
|
function HostMenu() {
|