@ai-matrx/meet 0.10.7 → 0.11.1

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/dist/react.cjs CHANGED
@@ -49,11 +49,13 @@ __export(react_exports, {
49
49
  GUEST_RECORD_NOT_SHARED: () => GUEST_RECORD_NOT_SHARED,
50
50
  GuestRecordAccessControl: () => GuestRecordAccessControl,
51
51
  IDLE_MEDIA: () => IDLE_MEDIA,
52
+ IDLE_PHASES: () => IDLE_PHASES,
52
53
  IDLE_TRACK: () => IDLE_TRACK,
53
54
  IncomingCallCard: () => IncomingCallCard,
54
55
  IncomingCallHost: () => IncomingCallHost,
55
56
  JOIN_REFUSAL_REASONS: () => JOIN_REFUSAL_REASONS,
56
57
  LISTEN_ONLY_NOTICE: () => LISTEN_ONLY_NOTICE,
58
+ LIVE_PHASES: () => LIVE_PHASES,
57
59
  MEETING_INVITE_KIND: () => MEETING_INVITE_KIND,
58
60
  MEETING_INVITE_VERSION: () => MEETING_INVITE_VERSION,
59
61
  MEETING_PASS_HEADER: () => MEETING_PASS_HEADER,
@@ -75,6 +77,7 @@ __export(react_exports, {
75
77
  NOTE_TAKER_IDENTITY_PREFIX: () => NOTE_TAKER_IDENTITY_PREFIX,
76
78
  NOTE_TAKER_METADATA_KEY: () => NOTE_TAKER_METADATA_KEY,
77
79
  NO_GRANTS: () => NO_GRANTS,
80
+ NO_SHARE: () => NO_SHARE,
78
81
  ORGANIZATION_HEADER: () => ORGANIZATION_HEADER,
79
82
  PARTICIPANT_CAN_RECORD_ATTRIBUTE: () => PARTICIPANT_CAN_RECORD_ATTRIBUTE,
80
83
  PARTICIPANT_ROLE_ATTRIBUTE: () => PARTICIPANT_ROLE_ATTRIBUTE,
@@ -89,9 +92,11 @@ __export(react_exports, {
89
92
  TABLES: () => TABLES,
90
93
  TRANSCRIPTION_ATTRIBUTES: () => TRANSCRIPTION_ATTRIBUTES,
91
94
  TRANSCRIPTION_TOPIC: () => TRANSCRIPTION_TOPIC,
95
+ WAITING_PHASES: () => WAITING_PHASES,
92
96
  absenceSentence: () => absenceSentence,
93
97
  activeWrapUpSummary: () => activeWrapUpSummary,
94
98
  activityAttention: () => activityAttention,
99
+ admissionPhase: () => admissionPhase,
95
100
  announceMeeting: () => announceMeeting,
96
101
  appPanelOf: () => appPanelOf,
97
102
  applyCollabPacket: () => applyCollabPacket,
@@ -161,10 +166,13 @@ __export(react_exports, {
161
166
  isJoinRefusalReason: () => isJoinRefusalReason,
162
167
  isLinkFollower: () => isLinkFollower,
163
168
  isListenOnly: () => isListenOnly,
169
+ isLivePhase: () => isLivePhase,
164
170
  isNoteTaker: () => isNoteTaker,
165
171
  isObserverRole: () => isObserverRole,
166
172
  isRealName: () => isRealName,
173
+ isRefusedPhase: () => isRefusedPhase,
167
174
  isRoomEventTopic: () => isRoomEventTopic,
175
+ isWaitingPhase: () => isWaitingPhase,
168
176
  learnName: () => learnName,
169
177
  learnNames: () => learnNames,
170
178
  listenOnlyNotice: () => listenOnlyNotice,
@@ -189,6 +197,7 @@ __export(react_exports, {
189
197
  mergeRecord: () => mergeRecord,
190
198
  microphoneNoticeCode: () => microphoneNoticeCode,
191
199
  moveToRoom: () => moveToRoom,
200
+ nextPhase: () => nextPhase,
192
201
  nextTranslationBatch: () => nextTranslationBatch,
193
202
  normalizeDeviceError: () => normalizeDeviceError,
194
203
  normalizeMeetError: () => normalizeMeetError,
@@ -221,10 +230,13 @@ __export(react_exports, {
221
230
  pushMeetingToGoogleCalendar: () => pushMeetingToGoogleCalendar,
222
231
  readServerRefusal: () => readServerRefusal,
223
232
  readableTranscript: () => readableTranscript,
233
+ reasonOf: () => reasonOf,
224
234
  recordingLabel: () => recordingLabel,
225
235
  recordingNotice: () => recordingNotice,
226
236
  recordingOffsetMs: () => recordingOffsetMs,
237
+ refusalPhase: () => refusalPhase,
227
238
  rememberedMeetingPass: () => rememberedMeetingPass,
239
+ resolveExit: () => resolveExit,
228
240
  roleOf: () => roleOf,
229
241
  roomTopic: () => roomTopic,
230
242
  rootAttributes: () => rootAttributes,
@@ -1291,6 +1303,166 @@ var MEET_EVENTS = {
1291
1303
  stateChanged: "mx.state.changed"
1292
1304
  };
1293
1305
 
1306
+ // src/core/reasons.ts
1307
+ var JOIN_REFUSAL_REASONS = [
1308
+ "not_found",
1309
+ "cancelled",
1310
+ "ended",
1311
+ "expired",
1312
+ "too_early",
1313
+ "sign_in_required",
1314
+ "wrong_account",
1315
+ "not_invited",
1316
+ "locked",
1317
+ "removed",
1318
+ "lobby_rejects",
1319
+ "meeting_full",
1320
+ "platform_capacity",
1321
+ "verification_required",
1322
+ "registration_required"
1323
+ ];
1324
+ function isJoinRefusalReason(value) {
1325
+ return typeof value === "string" && JOIN_REFUSAL_REASONS.includes(value);
1326
+ }
1327
+
1328
+ // src/core/phase.ts
1329
+ var LIVE_PHASES = /* @__PURE__ */ new Set(["joining", "in_call"]);
1330
+ var WAITING_PHASES = /* @__PURE__ */ new Set([
1331
+ "waiting:host_not_started",
1332
+ "waiting:knocking",
1333
+ "denied",
1334
+ "knock_expired"
1335
+ ]);
1336
+ var JOINABLE_FROM = /* @__PURE__ */ new Set([
1337
+ "prejoin",
1338
+ "left",
1339
+ "disconnected",
1340
+ "superseded",
1341
+ "inactive",
1342
+ "denied",
1343
+ "knock_expired"
1344
+ ]);
1345
+ var NOT_ENDABLE = /* @__PURE__ */ new Set([
1346
+ "ended",
1347
+ "record",
1348
+ "unsupported",
1349
+ "ringing_out",
1350
+ "ringing_in",
1351
+ "call_declined",
1352
+ "call_missed",
1353
+ "call_busy",
1354
+ "answered_elsewhere"
1355
+ ]);
1356
+ var RINGING = /* @__PURE__ */ new Set(["ringing_out", "ringing_in"]);
1357
+ var IDLE_PHASES = /* @__PURE__ */ new Set([
1358
+ "resolving",
1359
+ "left",
1360
+ "call_declined",
1361
+ "call_missed",
1362
+ "call_busy",
1363
+ "answered_elsewhere"
1364
+ ]);
1365
+ function isLivePhase(phase) {
1366
+ return LIVE_PHASES.has(phase);
1367
+ }
1368
+ function isWaitingPhase(phase) {
1369
+ return WAITING_PHASES.has(phase);
1370
+ }
1371
+ function isRefusedPhase(phase) {
1372
+ return phase.startsWith("refused:");
1373
+ }
1374
+ function reasonOf(phase) {
1375
+ return isRefusedPhase(phase) ? phase.slice("refused:".length) : null;
1376
+ }
1377
+ function refusalPhase(reason) {
1378
+ if (reason === null || !isJoinRefusalReason(reason)) return "disconnected";
1379
+ return reason === "ended" ? "ended" : `refused:${reason}`;
1380
+ }
1381
+ function admissionPhase(admission) {
1382
+ switch (admission) {
1383
+ case "waiting_host":
1384
+ return "waiting:host_not_started";
1385
+ case "knocking":
1386
+ return "waiting:knocking";
1387
+ case "denied":
1388
+ return "denied";
1389
+ case "expired":
1390
+ return "knock_expired";
1391
+ case "withdrawn":
1392
+ return "left";
1393
+ case "ended":
1394
+ return "ended";
1395
+ case "removed":
1396
+ return "refused:removed";
1397
+ default:
1398
+ return null;
1399
+ }
1400
+ }
1401
+ function nextPhase(from, event) {
1402
+ switch (event.type) {
1403
+ case "resolved":
1404
+ return from === "resolving" ? "prejoin" : null;
1405
+ case "unsupported":
1406
+ return from === "resolving" ? "unsupported" : null;
1407
+ case "join":
1408
+ return JOINABLE_FROM.has(from) ? "joining" : null;
1409
+ case "back_to_prejoin":
1410
+ return from === "left" || from === "disconnected" ? "prejoin" : null;
1411
+ case "retry":
1412
+ return isRefusedPhase(from) || from === "unsupported" ? "resolving" : null;
1413
+ case "admission": {
1414
+ if (from !== "joining" && !WAITING_PHASES.has(from)) return null;
1415
+ return admissionPhase(event.admission);
1416
+ }
1417
+ case "admitted":
1418
+ return WAITING_PHASES.has(from) ? "joining" : null;
1419
+ case "connected":
1420
+ return from === "joining" ? "in_call" : null;
1421
+ case "join_failed":
1422
+ return from === "joining" ? refusalPhase(event.reason) : null;
1423
+ case "exit":
1424
+ return from === "in_call" || from === "joining" ? event.to : null;
1425
+ case "leave":
1426
+ return from === "in_call" || from === "joining" || WAITING_PHASES.has(from) ? "left" : null;
1427
+ case "ended":
1428
+ return NOT_ENDABLE.has(from) ? null : "ended";
1429
+ case "ring_out":
1430
+ return IDLE_PHASES.has(from) || from === "prejoin" ? "ringing_out" : null;
1431
+ case "ring_in":
1432
+ return IDLE_PHASES.has(from) ? "ringing_in" : null;
1433
+ case "call_answered":
1434
+ return RINGING.has(from) ? "joining" : null;
1435
+ case "call_outcome":
1436
+ return RINGING.has(from) ? event.outcome : null;
1437
+ case "reset":
1438
+ return LIVE_PHASES.has(from) ? null : "resolving";
1439
+ }
1440
+ }
1441
+ function resolveExit(input) {
1442
+ const { server, hint } = input;
1443
+ if (server === null) {
1444
+ return { to: "disconnected", reason: null, connection: "server_unreachable", notice: null };
1445
+ }
1446
+ const word = server?.exitReason ?? null;
1447
+ if (server !== void 0 && (server.sessionPhase === "ended" || server.meetingPhase === "ended" || word === "ended" || word === "time_limit")) {
1448
+ return { to: "ended", reason: word === "time_limit" ? "time_limit" : "ended", connection: "stable", notice: null };
1449
+ }
1450
+ if (word === "removed") return { to: "removed", reason: "removed", connection: "stable", notice: null };
1451
+ if (word === "displaced") return { to: "superseded", reason: "displaced", connection: "stable", notice: null };
1452
+ if (word === "sent_to_waiting") {
1453
+ return { to: "waiting:knocking", reason: "sent_to_waiting", connection: "stable", notice: "sent-to-waiting" };
1454
+ }
1455
+ if (hint === "DUPLICATE_IDENTITY") return { to: "superseded", reason: "displaced", connection: "stable", notice: null };
1456
+ if (hint === "PARTICIPANT_REMOVED" && word === null && server === void 0) {
1457
+ return { to: "removed", reason: "removed", connection: "stable", notice: null };
1458
+ }
1459
+ if (hint === "ROOM_DELETED" && server === void 0) {
1460
+ return { to: "ended", reason: "ended", connection: "stable", notice: null };
1461
+ }
1462
+ if (input.frozen === true) return { to: "inactive", reason: "inactive", connection: "stable", notice: null };
1463
+ return { to: "disconnected", reason: null, connection: "stable", notice: null };
1464
+ }
1465
+
1294
1466
  // src/core/types.ts
1295
1467
  var asMeetingId = (value) => value;
1296
1468
  var asCallId = (value) => value;
@@ -1382,6 +1554,39 @@ function createCallCenter(options) {
1382
1554
  clearRing(invite.id);
1383
1555
  incoming.delete(invite.id);
1384
1556
  if (outgoing !== null && outgoing.id === invite.id) outgoing = invite;
1557
+ const held = options.store.snapshot().call;
1558
+ if (held !== null && held.invite.id === invite.id && held.direction === "incoming") {
1559
+ const outcome = invite.state === "connecting" || invite.state === "active" ? "answered_elsewhere" : invite.state === "declined" ? "reset" : "call_missed";
1560
+ const moved = outcome === "reset" ? options.store.dispatch({ type: "reset" }) : options.store.dispatch({ type: "call_outcome", outcome });
1561
+ if (moved) options.store.patch({ call: outcome === "reset" ? null : { ...held, invite } });
1562
+ }
1563
+ options.onSettled?.(invite);
1564
+ }
1565
+ function settleOutgoing(invite) {
1566
+ if (outgoing === null || outgoing.id !== invite.id) return;
1567
+ clearRing(invite.id);
1568
+ outgoing = invite;
1569
+ const store = options.store;
1570
+ const held = store.snapshot().call;
1571
+ if (held !== null && held.invite.id === invite.id) store.patch({ call: { ...held, invite } });
1572
+ switch (invite.state) {
1573
+ case "connecting":
1574
+ case "active":
1575
+ if (store.dispatch({ type: "call_answered" })) options.onAnswered?.(invite);
1576
+ break;
1577
+ case "declined":
1578
+ store.dispatch({ type: "call_outcome", outcome: "call_declined" });
1579
+ break;
1580
+ case "busy":
1581
+ store.dispatch({ type: "call_outcome", outcome: "call_busy" });
1582
+ break;
1583
+ case "missed":
1584
+ case "failed":
1585
+ store.dispatch({ type: "call_outcome", outcome: "call_missed" });
1586
+ break;
1587
+ default:
1588
+ break;
1589
+ }
1385
1590
  options.onSettled?.(invite);
1386
1591
  }
1387
1592
  function ingest(invite) {
@@ -1390,7 +1595,7 @@ function createCallCenter(options) {
1390
1595
  if (identity.userId === null) return;
1391
1596
  if (invite.callerUserId === identity.userId) return;
1392
1597
  const phase = options.store.snapshot().phase;
1393
- if (phase === "connected" || phase === "connecting" || phase === "reconnecting") {
1598
+ if (isLivePhase(phase) || phase === "ringing_out") {
1394
1599
  void repository.settleCallInvite(invite.id, "busy", null).then((settled) => {
1395
1600
  to(settled.callerUserId).send(MEET_EVENTS.settled, settled);
1396
1601
  }).catch((error) => {
@@ -1403,6 +1608,9 @@ function createCallCenter(options) {
1403
1608
  return;
1404
1609
  }
1405
1610
  incoming.set(invite.id, { ...invite, state: "ringing" });
1611
+ if (options.store.snapshot().call === null && options.store.dispatch({ type: "ring_in" })) {
1612
+ options.store.patch({ call: { invite: { ...invite, state: "ringing" }, direction: "incoming" } });
1613
+ }
1406
1614
  options.onIncoming?.(invite);
1407
1615
  timeouts.set(
1408
1616
  invite.id,
@@ -1440,6 +1648,7 @@ function createCallCenter(options) {
1440
1648
  const invite = message.data;
1441
1649
  const held = incoming.get(invite.id);
1442
1650
  if (held !== void 0) settleLocal(invite);
1651
+ else if (outgoing?.id === invite.id) settleOutgoing(invite);
1443
1652
  }
1444
1653
  }
1445
1654
  ],
@@ -1506,6 +1715,9 @@ function createCallCenter(options) {
1506
1715
  expiresAt: new Date(timers.now() + ringTimeoutMs).toISOString()
1507
1716
  });
1508
1717
  outgoing = invite;
1718
+ if (options.store.dispatch({ type: "ring_out" })) {
1719
+ options.store.patch({ call: { invite, direction: "outgoing" } });
1720
+ }
1509
1721
  for (const callee of people) {
1510
1722
  to(callee).send(MEET_EVENTS.ring, invite);
1511
1723
  }
@@ -1514,8 +1726,7 @@ function createCallCenter(options) {
1514
1726
  timers.setTimeout(() => {
1515
1727
  if (outgoing?.id !== invite.id) return;
1516
1728
  void repository.settleCallInvite(invite.id, "missed", null).then((settled) => {
1517
- outgoing = settled;
1518
- options.onSettled?.(settled);
1729
+ settleOutgoing(settled);
1519
1730
  }).catch(() => void 0);
1520
1731
  }, ringTimeoutMs)
1521
1732
  );
@@ -1530,7 +1741,9 @@ function createCallCenter(options) {
1530
1741
  "A late accept must not resurrect a settled call. Close the ringing UI; the caller has already moved on."
1531
1742
  );
1532
1743
  }
1744
+ if (options.store.snapshot().phase === "ringing_in") options.store.dispatch({ type: "call_answered" });
1533
1745
  const settled = await repository.settleCallInvite(id, "connecting", null);
1746
+ options.store.patch({ call: { invite: settled, direction: "incoming" } });
1534
1747
  settleLocal(settled);
1535
1748
  channel?.send(MEET_EVENTS.settled, settled);
1536
1749
  to(settled.callerUserId).send(MEET_EVENTS.settled, { ...settled, mode });
@@ -1538,6 +1751,9 @@ function createCallCenter(options) {
1538
1751
  },
1539
1752
  async decline(id, message) {
1540
1753
  const settled = await repository.settleCallInvite(id, "declined", message ?? null);
1754
+ if (options.store.snapshot().call?.invite.id === id && options.store.dispatch({ type: "reset" })) {
1755
+ options.store.patch({ call: null });
1756
+ }
1541
1757
  settleLocal(settled);
1542
1758
  channel?.send(MEET_EVENTS.settled, settled);
1543
1759
  to(settled.callerUserId).send(MEET_EVENTS.settled, settled);
@@ -1547,6 +1763,9 @@ function createCallCenter(options) {
1547
1763
  const settled = await repository.settleCallInvite(id, "cancelled", null);
1548
1764
  clearRing(id);
1549
1765
  if (outgoing?.id === id) outgoing = settled;
1766
+ if (options.store.snapshot().call?.invite.id === id && options.store.dispatch({ type: "reset" })) {
1767
+ options.store.patch({ call: null });
1768
+ }
1550
1769
  for (const callee of settled.calleeUserIds) {
1551
1770
  to(callee).send(MEET_EVENTS.cancel, settled);
1552
1771
  }
@@ -1932,7 +2151,9 @@ var TABLES = {
1932
2151
  /** Wave 1 (0.8): who a meeting was sent to, and what they answered. */
1933
2152
  invitees: "meet_invitees",
1934
2153
  /** Wave 1 (0.8): one occurrence of a series cancelled or moved. */
1935
- occurrenceOverrides: "meet_occurrence_overrides"
2154
+ occurrenceOverrides: "meet_occurrence_overrides",
2155
+ /** S5: one row per RUN of a meeting; carries that run's note-taker state. */
2156
+ sessions: "meet_sessions"
1936
2157
  };
1937
2158
  var RPCS = {
1938
2159
  /** Atomic: create the invite row and stamp its expiry in one statement. */
@@ -2408,6 +2629,18 @@ function createMeetRepository(options) {
2408
2629
  );
2409
2630
  }
2410
2631
  }
2632
+ async function withRunNoteTaker(meeting) {
2633
+ const response = await db.from(TABLES.sessions).select("note_taker,started_at").eq("meeting_id", meeting.id).order("started_at", { ascending: false }).limit(1);
2634
+ const run = response.error === null ? (response.data ?? [])[0] : void 0;
2635
+ const noteTaker = run?.["note_taker"];
2636
+ if (typeof noteTaker !== "object" || noteTaker === null || Array.isArray(noteTaker)) {
2637
+ return meeting;
2638
+ }
2639
+ return {
2640
+ ...meeting,
2641
+ metadata: { ...meeting.metadata ?? {}, note_taker: noteTaker }
2642
+ };
2643
+ }
2411
2644
  return {
2412
2645
  schema: MEET_SCHEMA,
2413
2646
  tables: TABLES,
@@ -2646,13 +2879,13 @@ function createMeetRepository(options) {
2646
2879
  "meet.meetingBySlug",
2647
2880
  () => db.rpc(RPCS.meetingBySlug, { p_slug: slug })
2648
2881
  );
2649
- return projectMeeting(row);
2882
+ return withRunNoteTaker(projectMeeting(row));
2650
2883
  },
2651
2884
  async meeting(id) {
2652
2885
  const response = await db.from(TABLES.meetings).select(MEETING_COLUMNS).eq("id", id).single();
2653
2886
  if (response.error !== null) throw normalizeMeetError(response.error, "meet.meeting");
2654
2887
  if (response.data === null) throw invalidResponse("meet.meeting", "no such meeting");
2655
- return projectMeeting(response.data);
2888
+ return withRunNoteTaker(projectMeeting(response.data));
2656
2889
  },
2657
2890
  async listMeetings({ organizationId = null, limit = 25, before = null }) {
2658
2891
  let query = db.from(TABLES.meetings).select(MEETING_COLUMNS);
@@ -3285,8 +3518,15 @@ var NO_GRANTS = {
3285
3518
  canPublish: false,
3286
3519
  canSubscribe: false
3287
3520
  };
3521
+ var NO_SHARE = { state: "none", reason: null, remedy: null, at: 0 };
3288
3522
  var EMPTY = {
3289
- phase: "idle",
3523
+ phase: "resolving",
3524
+ reason: null,
3525
+ phaseNotice: null,
3526
+ roomName: null,
3527
+ connection: "stable",
3528
+ share: NO_SHARE,
3529
+ call: null,
3290
3530
  waiting: null,
3291
3531
  meeting: null,
3292
3532
  localIdentity: null,
@@ -3350,8 +3590,20 @@ function createMeetStore(initial = {}) {
3350
3590
  return () => listeners.delete(listener);
3351
3591
  },
3352
3592
  patch(next) {
3593
+ if ("phase" in next || "reason" in next || "phaseNotice" in next) {
3594
+ throw new TypeError("meet store: the phase moves only through store.dispatch(event)");
3595
+ }
3353
3596
  commit({ ...state, ...next });
3354
3597
  },
3598
+ dispatch(event, detail) {
3599
+ const to = nextPhase(state.phase, event);
3600
+ if (to === null) return false;
3601
+ const reason = detail?.reason !== void 0 ? detail.reason : reasonOf(to);
3602
+ const phaseNotice = detail?.notice ?? null;
3603
+ if (to === state.phase && reason === state.reason && phaseNotice === state.phaseNotice) return true;
3604
+ commit({ ...state, phase: to, reason, phaseNotice });
3605
+ return true;
3606
+ },
3355
3607
  setParticipants(participants) {
3356
3608
  const names = learnNames(state.names, participants);
3357
3609
  const hands = new Map(state.participants.map((p) => [p.identity, p.handRaised]));
@@ -3669,7 +3921,7 @@ function createMeetingSession(options) {
3669
3921
  }
3670
3922
  function apply(read) {
3671
3923
  const snapshot = store.snapshot();
3672
- if (snapshot.phase !== "lobby") return;
3924
+ if (!isWaitingPhase(snapshot.phase)) return;
3673
3925
  const admission = read.admission_state ?? null;
3674
3926
  const ended = admission === "ended" || read.session_phase === "ended" || read.meeting_phase === "ended";
3675
3927
  if (admission === "admitted" && !ended) {
@@ -3686,6 +3938,7 @@ function createMeetingSession(options) {
3686
3938
  if (read.meeting_phase === "ended" && meeting !== null && meeting.endedAt === null) {
3687
3939
  store.patch({ meeting: { ...meeting, endedAt: (/* @__PURE__ */ new Date()).toISOString() } });
3688
3940
  }
3941
+ store.dispatch({ type: "admission", admission: "ended" }, { reason: "ended" });
3689
3942
  return;
3690
3943
  }
3691
3944
  if (admission === null) return;
@@ -3697,6 +3950,7 @@ function createMeetingSession(options) {
3697
3950
  pollSeconds: read.poll_seconds ?? snapshot.waiting?.pollSeconds ?? 3,
3698
3951
  liveVersion: read.live_version ?? 0
3699
3952
  });
3953
+ store.dispatch({ type: "admission", admission });
3700
3954
  if (admission === "denied" || admission === "expired" || admission === "withdrawn" || admission === "removed") {
3701
3955
  stopWaiting();
3702
3956
  }
@@ -3792,7 +4046,7 @@ function createMeetingSession(options) {
3792
4046
  // 🚨 PAYLOAD-FREE. Every receiver reads again; a spoofed hint only causes a read.
3793
4047
  event: MEET_EVENTS.stateChanged,
3794
4048
  onMessage: () => {
3795
- if (store.snapshot().phase === "lobby") void readState2();
4049
+ if (isWaitingPhase(store.snapshot().phase)) void readState2();
3796
4050
  void refreshQueue();
3797
4051
  }
3798
4052
  }
@@ -3804,7 +4058,7 @@ function createMeetingSession(options) {
3804
4058
  if (meeting === null) return;
3805
4059
  const fresh = isGuest(identity) ? await repository.meetingBySlug(meeting.slug) : await readMeeting(meeting.id);
3806
4060
  store.patch({ meeting: fresh, locked: fresh.locked });
3807
- if (store.snapshot().phase === "lobby") void readState2();
4061
+ if (isWaitingPhase(store.snapshot().phase)) void readState2();
3808
4062
  }
3809
4063
  });
3810
4064
  },
@@ -3971,10 +4225,34 @@ function createMeetingSession(options) {
3971
4225
  },
3972
4226
  stopWaiting,
3973
4227
  readState: readState2,
4228
+ async readExit(connection) {
4229
+ const meeting = store.snapshot().meeting;
4230
+ const pass = options.resolvePass?.() ?? null;
4231
+ if (meeting === null || pass === null) return void 0;
4232
+ try {
4233
+ const response = await options.api.request({
4234
+ path: `${MEET_ROUTES.state}?meeting_id=${encodeURIComponent(meeting.id)}&connection=${encodeURIComponent(connection)}`,
4235
+ method: "GET",
4236
+ requireAuth: false,
4237
+ operation: "meet.state(exit)",
4238
+ headers: { [MEETING_PASS_HEADER]: pass }
4239
+ });
4240
+ if (!response.ok) return null;
4241
+ const read = await response.json();
4242
+ return {
4243
+ exitReason: read.exit_reason ?? null,
4244
+ sessionPhase: read.session_phase ?? null,
4245
+ meetingPhase: read.meeting_phase ?? null
4246
+ };
4247
+ } catch {
4248
+ return null;
4249
+ }
4250
+ },
3974
4251
  async withdraw() {
3975
4252
  stopWaiting();
3976
4253
  await postLeave("withdraw");
3977
4254
  patchWaiting({ admission: "withdrawn" });
4255
+ store.dispatch({ type: "admission", admission: "withdrawn" });
3978
4256
  hint();
3979
4257
  },
3980
4258
  async askAgain() {
@@ -4456,6 +4734,9 @@ var TRANSCRIPTION_ATTRIBUTES = {
4456
4734
  };
4457
4735
 
4458
4736
  // src/core/room.ts
4737
+ function SCREEN_SHARE_BLOCKED_REMEDY_FOR(remedy) {
4738
+ return /administrator|policy/i.test(remedy) ? remedy : SCREEN_SHARE_BLOCKED_REMEDY;
4739
+ }
4459
4740
  var PARTICIPANT_ROLE_ATTRIBUTE = "matrx.participant.role";
4460
4741
  var PARTICIPANT_CAN_RECORD_ATTRIBUTE = "matrx.participant.can_record";
4461
4742
  var PARTICIPANT_STANDING_ATTRIBUTE = "matrx.participant.standing";
@@ -4523,7 +4804,7 @@ function createRoomEngine(options) {
4523
4804
  let disposed = false;
4524
4805
  let moving = false;
4525
4806
  const confirmingEnds = /* @__PURE__ */ new WeakSet();
4526
- async function leaveRoom() {
4807
+ async function dropRoom() {
4527
4808
  const target = driver;
4528
4809
  driver = null;
4529
4810
  if (current !== null) {
@@ -4532,9 +4813,14 @@ function createRoomEngine(options) {
4532
4813
  }
4533
4814
  store.setLocalTrack("microphone", IDLE_TRACK);
4534
4815
  store.setLocalTrack("camera", IDLE_TRACK);
4535
- store.patch({ phase: "left" });
4816
+ store.patch({ share: NO_SHARE, connection: "stable" });
4536
4817
  await target?.disconnect();
4537
4818
  }
4819
+ async function leaveRoom() {
4820
+ store.dispatch({ type: "leave" });
4821
+ await dropRoom();
4822
+ options.onLeft?.(null);
4823
+ }
4538
4824
  async function confirmMeetingEnd() {
4539
4825
  const meeting = store.snapshot().meeting;
4540
4826
  const target = driver;
@@ -4556,7 +4842,9 @@ function createRoomEngine(options) {
4556
4842
  const latest = store.snapshot().meeting;
4557
4843
  if (latest === null) return;
4558
4844
  store.patch({ meeting: { ...latest, endedAt: verified.endedAt } });
4559
- await leaveRoom();
4845
+ store.dispatch({ type: "ended" }, { reason: "ended" });
4846
+ await dropRoom();
4847
+ options.onLeft?.("ended");
4560
4848
  } catch (error) {
4561
4849
  report("error", `Meet could not confirm or finish meeting teardown: ${String(error)}`, "Check the meeting link again; if media remains active, close this meeting tab.");
4562
4850
  } finally {
@@ -4568,12 +4856,12 @@ function createRoomEngine(options) {
4568
4856
  options.onDiagnostic?.(remedy === void 0 ? { level, message } : { level, message, remedy });
4569
4857
  }
4570
4858
  let ambiguousShareDenials = 0;
4571
- function fail(error, operation) {
4859
+ function failJoin(error, operation) {
4572
4860
  const normalized = normalizeMeetError(error, operation);
4573
4861
  store.patch({
4574
- phase: "failed",
4575
4862
  failure: { message: normalized.message, remedy: normalized.remedy, reason: normalized.reason }
4576
4863
  });
4864
+ store.dispatch({ type: "join_failed", reason: normalized.reason ?? null });
4577
4865
  report(
4578
4866
  normalized.code === "session-unavailable" ? "warn" : "error",
4579
4867
  normalized.message,
@@ -4581,6 +4869,14 @@ function createRoomEngine(options) {
4581
4869
  );
4582
4870
  return normalized;
4583
4871
  }
4872
+ async function lostRoom(error, operation) {
4873
+ const normalized = normalizeMeetError(error, operation);
4874
+ store.patch({ failure: { message: normalized.message, remedy: normalized.remedy, reason: normalized.reason } });
4875
+ await dropRoom();
4876
+ store.dispatch({ type: "exit", to: "disconnected" });
4877
+ report("error", normalized.message, normalized.remedy);
4878
+ return normalized;
4879
+ }
4584
4880
  const handlers = {
4585
4881
  participants(list) {
4586
4882
  const joinedAt = new Date(now()).toISOString();
@@ -4623,9 +4919,12 @@ function createRoomEngine(options) {
4623
4919
  store.setQuality(asParticipantIdentity(who), value);
4624
4920
  },
4625
4921
  connectionState(state) {
4626
- if (state === "connected") store.patch({ phase: "connected", failure: null });
4627
- else if (state === "reconnecting") store.patch({ phase: "reconnecting" });
4628
- else if (state === "connecting") store.patch({ phase: "connecting" });
4922
+ if (state === "connected") {
4923
+ if (store.snapshot().connection !== "stable") store.patch({ connection: "stable" });
4924
+ store.dispatch({ type: "connected" });
4925
+ } else if (state === "reconnecting") {
4926
+ store.patch({ connection: "reconnecting" });
4927
+ }
4629
4928
  },
4630
4929
  transcription(event) {
4631
4930
  const snapshot = store.snapshot();
@@ -4752,17 +5051,48 @@ function createRoomEngine(options) {
4752
5051
  }
4753
5052
  },
4754
5053
  disconnected(reason) {
4755
- if (moving) return;
4756
- if (current !== null) {
4757
- tokens.invalidate(current.roomName);
4758
- current = null;
4759
- }
4760
- store.patch({ phase: "left" });
4761
- options.onLeft?.(reason);
5054
+ if (moving || driver === null) return;
5055
+ void resolveExitAndApply(reason);
4762
5056
  }
4763
5057
  };
5058
+ async function resolveExitAndApply(hint) {
5059
+ const connection = current?.identity ?? store.snapshot().localIdentity ?? null;
5060
+ await dropRoom();
5061
+ let server;
5062
+ if (options.readExit === void 0 || connection === null || store.snapshot().meeting === null) {
5063
+ server = void 0;
5064
+ } else {
5065
+ try {
5066
+ server = await options.readExit(connection);
5067
+ } catch {
5068
+ server = null;
5069
+ }
5070
+ }
5071
+ if (disposed) return;
5072
+ const resolved = resolveExit({ server, hint, frozen: options.wasFrozen?.() ?? false });
5073
+ store.patch({ connection: resolved.connection });
5074
+ if (resolved.to === "waiting:knocking") {
5075
+ store.patch({
5076
+ waiting: {
5077
+ admission: "knocking",
5078
+ admittersPresent: null,
5079
+ reknockAllowed: true,
5080
+ knockExpiresAt: null,
5081
+ pollSeconds: store.snapshot().waiting?.pollSeconds ?? 3,
5082
+ liveVersion: 0
5083
+ }
5084
+ });
5085
+ }
5086
+ store.dispatch({ type: "exit", to: resolved.to }, { reason: resolved.reason, notice: resolved.notice });
5087
+ report(
5088
+ resolved.to === "disconnected" ? "warn" : "info",
5089
+ `meet: the room connection ended (${hint ?? "no LiveKit reason"}) \u2192 ${resolved.to}`,
5090
+ resolved.to === "disconnected" ? "Press Rejoin; the meeting is still going." : "The screen says what happened."
5091
+ );
5092
+ options.onLeft?.(hint);
5093
+ }
4764
5094
  async function connect(request) {
4765
- store.patch({ phase: "requesting-token", failure: null, waiting: null });
5095
+ store.patch({ failure: null, waiting: null, roomName: request.roomName, share: NO_SHARE, connection: "stable" });
4766
5096
  const token = await tokens.get({
4767
5097
  roomName: request.roomName,
4768
5098
  identity,
@@ -4785,10 +5115,10 @@ function createRoomEngine(options) {
4785
5115
  }
4786
5116
  });
4787
5117
  if (token.lobby || !token.canSubscribe) {
5118
+ const admission = token.admissionState !== void 0 && token.admissionState !== "admitted" ? token.admissionState : "knocking";
4788
5119
  store.patch({
4789
- phase: "lobby",
4790
5120
  waiting: {
4791
- admission: token.admissionState !== void 0 && token.admissionState !== "admitted" ? token.admissionState : "knocking",
5121
+ admission,
4792
5122
  admittersPresent: null,
4793
5123
  reknockAllowed: true,
4794
5124
  knockExpiresAt: null,
@@ -4796,9 +5126,10 @@ function createRoomEngine(options) {
4796
5126
  liveVersion: 0
4797
5127
  }
4798
5128
  });
5129
+ store.dispatch({ type: "admission", admission });
4799
5130
  return;
4800
5131
  }
4801
- store.patch({ phase: "connecting", waiting: null });
5132
+ store.patch({ waiting: null });
4802
5133
  const created = await options.driverFactory(handlers);
4803
5134
  if (disposed) {
4804
5135
  await created.disconnect();
@@ -4806,7 +5137,8 @@ function createRoomEngine(options) {
4806
5137
  }
4807
5138
  driver = created;
4808
5139
  await created.connect(token.serverUrl, token.token);
4809
- store.patch({ phase: "connected", failure: null });
5140
+ store.patch({ failure: null, connection: "stable" });
5141
+ store.dispatch({ type: "connected" });
4810
5142
  store.setLocalTrack("microphone", IDLE_TRACK);
4811
5143
  store.setLocalTrack("camera", IDLE_TRACK);
4812
5144
  const effects = options.effectsStorage?.load() ?? store.snapshot().effects;
@@ -4877,9 +5209,17 @@ function createRoomEngine(options) {
4877
5209
  return {
4878
5210
  join(request) {
4879
5211
  if (joining !== null) return joining;
5212
+ if (store.snapshot().phase !== "joining" && !store.dispatch({ type: "join", click: true })) {
5213
+ report(
5214
+ "warn",
5215
+ `meet.join: cannot join from "${store.snapshot().phase}"`,
5216
+ "Join is offered from the pre-join, left, disconnected and replaced screens only."
5217
+ );
5218
+ return Promise.resolve();
5219
+ }
4880
5220
  lastRequest = request;
4881
5221
  joining = connect(request).catch((error) => {
4882
- throw fail(error, "meet.join");
5222
+ throw failJoin(error, "meet.join");
4883
5223
  }).finally(() => {
4884
5224
  joining = null;
4885
5225
  });
@@ -4896,10 +5236,16 @@ function createRoomEngine(options) {
4896
5236
  return;
4897
5237
  }
4898
5238
  if (joining !== null) return joining;
5239
+ const phase = store.snapshot().phase;
5240
+ const moved = phase === "joining" || store.dispatch({ type: "join", click: true }) || store.dispatch({ type: "admitted" });
5241
+ if (!moved) {
5242
+ report("warn", `meet.rejoin: cannot rejoin from "${phase}"`, "Rejoin is offered after leaving, a lost connection, or admission.");
5243
+ return;
5244
+ }
4899
5245
  tokens.invalidate(request.roomName);
4900
5246
  current = null;
4901
5247
  joining = connect(request).catch((error) => {
4902
- throw fail(error, "meet.rejoin");
5248
+ throw failJoin(error, "meet.rejoin");
4903
5249
  }).finally(() => {
4904
5250
  joining = null;
4905
5251
  });
@@ -4944,11 +5290,11 @@ function createRoomEngine(options) {
4944
5290
  joining = connect(lastRequest).finally(() => {
4945
5291
  joining = null;
4946
5292
  });
4947
- await joining.catch((again) => {
4948
- throw fail(again, "meet.moveToBreakout");
5293
+ await joining.catch(async (again) => {
5294
+ throw await lostRoom(again, "meet.moveToBreakout");
4949
5295
  });
4950
5296
  } else {
4951
- throw fail(error, "meet.moveToBreakout");
5297
+ throw await lostRoom(error, "meet.moveToBreakout");
4952
5298
  }
4953
5299
  throw normalized;
4954
5300
  } finally {
@@ -4956,6 +5302,9 @@ function createRoomEngine(options) {
4956
5302
  }
4957
5303
  },
4958
5304
  leave: leaveRoom,
5305
+ backToPrejoin() {
5306
+ store.dispatch({ type: "back_to_prejoin", click: true });
5307
+ },
4959
5308
  // Turning a device on mid-meeting is the SAME operation as turning it on
4960
5309
  // during the join, so it runs the same function. A failure here used to
4961
5310
  // stamp the room `failed` — a busy microphone would blank a live meeting.
@@ -4969,21 +5318,41 @@ function createRoomEngine(options) {
4969
5318
  },
4970
5319
  async setScreenShareEnabled(enabled) {
4971
5320
  if (driver === null) return;
5321
+ if (enabled && driver.supportsScreenShare?.() === false) {
5322
+ store.patch({ share: { state: "refused", reason: "unsupported", remedy: null, at: now() } });
5323
+ report("warn", "meet: this browser cannot share a screen", "Share from a desktop browser (Chrome, Edge, Firefox or Safari).");
5324
+ return;
5325
+ }
5326
+ if (enabled) store.patch({ share: { state: "picking", reason: null, remedy: null, at: now() } });
4972
5327
  try {
4973
5328
  await driver.setScreenShareEnabled(enabled);
4974
5329
  ambiguousShareDenials = 0;
5330
+ store.patch({ share: { state: enabled ? "presenting" : "none", reason: null, remedy: null, at: now() } });
4975
5331
  } catch (error) {
4976
5332
  const kind = classifyScreenShareError(error);
4977
5333
  if (kind === "ambiguous") {
4978
5334
  ambiguousShareDenials += 1;
4979
- if (ambiguousShareDenials < 2) return;
5335
+ if (ambiguousShareDenials < 2) {
5336
+ store.patch({ share: NO_SHARE });
5337
+ return;
5338
+ }
4980
5339
  }
5340
+ ambiguousShareDenials = 0;
4981
5341
  const normalized = normalizeScreenShareError(error, "meet.setScreenShareEnabled");
4982
5342
  report("warn", normalized.message, normalized.remedy);
4983
- const text3 = kind === "other" ? `Screen sharing is not available. ${normalized.remedy}` : `Screen sharing did not start. ${SCREEN_SHARE_BLOCKED_REMEDY}`;
4984
- store.patch({ actionNotice: { text: text3, failed: true, at: now() } });
5343
+ store.patch({
5344
+ share: {
5345
+ state: "refused",
5346
+ reason: kind === "other" ? "failed" : "blocked",
5347
+ remedy: kind === "other" ? normalized.remedy : SCREEN_SHARE_BLOCKED_REMEDY_FOR(normalized.remedy),
5348
+ at: now()
5349
+ }
5350
+ });
4985
5351
  }
4986
5352
  },
5353
+ dismissShareNotice() {
5354
+ if (store.snapshot().share.state === "refused") store.patch({ share: NO_SHARE });
5355
+ },
4987
5356
  async raiseHand(raised) {
4988
5357
  const local = store.snapshot().localIdentity;
4989
5358
  if (local !== null) store.setHand(local, raised);
@@ -5485,7 +5854,8 @@ function createLiveKitDriver(handlers, options = {}) {
5485
5854
  }).on(RoomEvent.Disconnected, (reason) => {
5486
5855
  audioContainer?.remove();
5487
5856
  audioContainer = null;
5488
- handlers.disconnected(reason === void 0 ? null : String(reason));
5857
+ const named = reason === void 0 ? null : module2?.DisconnectReason?.[reason] ?? String(reason);
5858
+ handlers.disconnected(named);
5489
5859
  });
5490
5860
  try {
5491
5861
  created.registerTextStreamHandler(TRANSCRIPTION_TOPIC, (reader, participantInfo) => {
@@ -5873,7 +6243,9 @@ function MeetRuntime(props) {
5873
6243
  driverFactory: (handlers) => (driverRef.current ?? createLiveKitDriver)(handlers),
5874
6244
  onDiagnostic: report,
5875
6245
  effectsStorage,
5876
- resolveDevice: (kind) => devicesHolder.current?.resolve(kind) ?? null
6246
+ resolveDevice: (kind) => devicesHolder.current?.resolve(kind) ?? null,
6247
+ // §3.4: on every LiveKit disconnect, ask the server what happened to this connection.
6248
+ readExit: (connection) => meetings.readExit(connection)
5877
6249
  });
5878
6250
  const meetings = createMeetingSession({
5879
6251
  manager,
@@ -5908,17 +6280,17 @@ function MeetRuntime(props) {
5908
6280
  const inRoom = createInRoomClient({ api, store, resolveRoomToken });
5909
6281
  store.subscribe(() => {
5910
6282
  const snapshot = store.snapshot();
5911
- if (snapshot.phase === "lobby" && snapshot.waiting !== null && (snapshot.waiting.admission === "knocking" || snapshot.waiting.admission === "waiting_host")) {
6283
+ if (isWaitingPhase(snapshot.phase) && snapshot.waiting !== null && (snapshot.waiting.admission === "knocking" || snapshot.waiting.admission === "waiting_host")) {
5912
6284
  meetings.startWaiting();
5913
- } else if (snapshot.phase !== "lobby") {
6285
+ } else if (!isWaitingPhase(snapshot.phase)) {
5914
6286
  meetings.stopWaiting();
5915
6287
  }
5916
6288
  });
5917
6289
  let chatReadFor = null;
5918
6290
  store.subscribe(() => {
5919
6291
  const snapshot = store.snapshot();
5920
- if (snapshot.phase !== "connected" || snapshot.meeting === null) {
5921
- if (snapshot.phase === "left" || snapshot.phase === "idle") chatReadFor = null;
6292
+ if (snapshot.phase !== "in_call" || snapshot.meeting === null) {
6293
+ if (snapshot.phase === "left" || snapshot.phase === "prejoin" || snapshot.phase === "resolving") chatReadFor = null;
5922
6294
  return;
5923
6295
  }
5924
6296
  if (chatReadFor === snapshot.meeting.id) return;
@@ -5971,6 +6343,10 @@ function MeetRuntime(props) {
5971
6343
  store,
5972
6344
  identity,
5973
6345
  onIncoming: (invite) => incomingRef.current?.(invite),
6346
+ // §3.6: my call was answered — the caller joins the call's room on the same machine.
6347
+ onAnswered: (invite) => {
6348
+ void engine.join({ roomName: invite.roomName, mode: invite.mode }).catch(() => void 0);
6349
+ },
5974
6350
  onDiagnostic: report,
5975
6351
  ...props.ringTimeoutMs !== void 0 ? { ringTimeoutMs: props.ringTimeoutMs } : {}
5976
6352
  });
@@ -6110,43 +6486,12 @@ function endedRecord(snapshot, given) {
6110
6486
  function observedPhase(input) {
6111
6487
  const { snapshot } = input;
6112
6488
  if (snapshot === null) return "resolving";
6113
- const raw = snapshot.phase;
6114
- const live = raw === "connected" || raw === "reconnecting";
6115
- if (endedRecord(snapshot, input.meeting ?? null) && !live) return "ended";
6116
- const reason = raw === "failed" ? snapshot.failure?.reason ?? null : null;
6117
- if (reason !== null) return reason === "ended" ? "ended" : `refused:${reason}`;
6118
- switch (raw) {
6119
- case "idle":
6120
- case "failed":
6121
- case "left":
6122
- return "prejoin";
6123
- case "lobby":
6124
- return waitingPhase(snapshot);
6125
- case "requesting-token":
6126
- case "connecting":
6127
- return "joining";
6128
- case "connected":
6129
- case "reconnecting":
6130
- return "in_call";
6131
- }
6489
+ const phase = snapshot.phase;
6490
+ if (endedRecord(snapshot, input.meeting ?? null) && !isLivePhase(phase) && !isCallPhase(phase)) return "ended";
6491
+ return phase;
6132
6492
  }
6133
- function waitingPhase(snapshot) {
6134
- switch (snapshot.waiting?.admission) {
6135
- case "waiting_host":
6136
- return "waiting:host_not_started";
6137
- case "denied":
6138
- return "denied";
6139
- case "expired":
6140
- return "knock_expired";
6141
- case "withdrawn":
6142
- return "left";
6143
- case "ended":
6144
- return "ended";
6145
- case "removed":
6146
- return "refused:removed";
6147
- default:
6148
- return "waiting:knocking";
6149
- }
6493
+ function isCallPhase(phase) {
6494
+ return phase.startsWith("ringing_") || phase.startsWith("call_") || phase === "answered_elsewhere";
6150
6495
  }
6151
6496
  var ADMITTER_ROLES = /* @__PURE__ */ new Set(["host", "cohost"]);
6152
6497
  function observe(input) {
@@ -6159,10 +6504,10 @@ function observe(input) {
6159
6504
  const deviceInPlay = phase === "joining" || inCall;
6160
6505
  return {
6161
6506
  phase,
6162
- reason: phase.startsWith("refused:") ? phase.slice("refused:".length) : null,
6507
+ reason: phase === "ended" ? snapshot?.reason ?? null : snapshot?.reason ?? reasonOf(phase),
6163
6508
  role,
6164
- connection: snapshot?.phase === "reconnecting" ? "reconnecting" : "stable",
6165
- hostPresent: inCall && snapshot !== null ? snapshot.participants.some((p) => ADMITTER_ROLES.has(p.role)) : snapshot?.phase === "lobby" ? snapshot.waiting?.admittersPresent ?? null : null,
6509
+ connection: snapshot?.connection ?? "stable",
6510
+ hostPresent: inCall && snapshot !== null ? snapshot.participants.some((p) => ADMITTER_ROLES.has(p.role)) : snapshot !== null && isWaitingPhase(snapshot.phase) ? snapshot.waiting?.admittersPresent ?? null : null,
6166
6511
  lobbyCount: canAdmit && snapshot !== null ? snapshot.lobby.filter((entry) => entry.state === "knocking").length : 0,
6167
6512
  recording: snapshot?.recording === "recording" || snapshot?.recording === "stopping",
6168
6513
  audioBlocked: null,
@@ -6963,7 +7308,7 @@ function useRoom() {
6963
7308
  (participant) => participant.identity === snapshot.localIdentity
6964
7309
  ) ?? null;
6965
7310
  return {
6966
- phase: snapshot?.phase ?? "idle",
7311
+ phase: snapshot?.phase ?? "resolving",
6967
7312
  participants: snapshot?.participants ?? [],
6968
7313
  failure: snapshot?.failure ?? null,
6969
7314
  local,
@@ -8314,7 +8659,7 @@ function useSharedFocus(panelKey) {
8314
8659
  const snapshot = useMeetSnapshot();
8315
8660
  const focus = snapshot?.collab.focus ?? null;
8316
8661
  const mine = focus !== null && focus.panel === panelKey ? focus : null;
8317
- const canMove = !isObserverRole(snapshot?.grants.role) && snapshot?.phase === "connected";
8662
+ const canMove = !isObserverRole(snapshot?.grants.role) && snapshot?.phase === "in_call";
8318
8663
  return {
8319
8664
  section: mine?.section || null,
8320
8665
  byName: mine?.byName || null,
@@ -8390,6 +8735,36 @@ function useCollabPanelRequest(onRequest) {
8390
8735
  var MEET_REACTIONS = ["\u{1F496}", "\u{1F44D}", "\u{1F389}", "\u{1F44F}", "\u{1F602}", "\u{1F62E}", "\u{1F622}", "\u{1F914}", "\u{1F44E}"];
8391
8736
 
8392
8737
  // src/react/facade.ts
8738
+ var NO_ACTIONS = {
8739
+ join: async () => void 0,
8740
+ rejoin: async () => void 0,
8741
+ leave: async () => void 0,
8742
+ backToPrejoin: () => void 0,
8743
+ retry: () => void 0,
8744
+ resolved: () => void 0,
8745
+ dismissShareNotice: () => void 0,
8746
+ dismissCall: () => void 0
8747
+ };
8748
+ function actionsFor(host) {
8749
+ if (host === null) return NO_ACTIONS;
8750
+ const { engine, store } = host;
8751
+ return {
8752
+ join: (request) => engine.join(request),
8753
+ rejoin: () => engine.rejoin(),
8754
+ leave: () => engine.leave(),
8755
+ backToPrejoin: () => engine.backToPrejoin(),
8756
+ retry: () => {
8757
+ store.dispatch({ type: "retry", click: true });
8758
+ },
8759
+ resolved: () => {
8760
+ store.dispatch({ type: "resolved" });
8761
+ },
8762
+ dismissShareNotice: () => engine.dismissShareNotice(),
8763
+ dismissCall: () => {
8764
+ if (store.dispatch({ type: "reset" })) store.patch({ call: null });
8765
+ }
8766
+ };
8767
+ }
8393
8768
  function useMeetCore() {
8394
8769
  const host = useMeetHost();
8395
8770
  const snapshot = useMeetSnapshot();
@@ -8397,9 +8772,24 @@ function useMeetCore() {
8397
8772
  const role = useRole();
8398
8773
  const isHost = useIsHost();
8399
8774
  const observation = useCoreObservation();
8775
+ const actions = (0, import_react8.useMemo)(() => actionsFor(host), [host]);
8400
8776
  return (0, import_react8.useMemo)(
8401
- () => ({ host, snapshot, devices, role, isHost, observation }),
8402
- [host, snapshot, devices, role, isHost, observation]
8777
+ () => ({
8778
+ host,
8779
+ snapshot,
8780
+ devices,
8781
+ role,
8782
+ isHost,
8783
+ observation,
8784
+ phase: snapshot?.phase ?? "resolving",
8785
+ reason: snapshot?.reason ?? null,
8786
+ phaseNotice: snapshot?.phaseNotice ?? null,
8787
+ connection: snapshot?.connection ?? "stable",
8788
+ share: snapshot?.share ?? NO_SHARE,
8789
+ call: snapshot?.call ?? null,
8790
+ actions
8791
+ }),
8792
+ [host, snapshot, devices, role, isHost, observation, actions]
8403
8793
  );
8404
8794
  }
8405
8795
 
@@ -11330,11 +11720,52 @@ function MeetSkinRoom({
11330
11720
  headerControls,
11331
11721
  observe: observe2 = false
11332
11722
  }) {
11333
- const { host, snapshot, isHost } = useMeetCore();
11723
+ const core = useMeetCore();
11724
+ const { host, snapshot, isHost, phase } = core;
11334
11725
  const elapsed = useElapsed(snapshot?.meeting?.startedAt ?? null);
11335
- const phase = snapshot?.phase ?? "idle";
11336
11726
  if (host === null) return null;
11337
- if (phase === "idle" || phase === "failed" || phase === "left") {
11727
+ const screen = (() => {
11728
+ if (isRefusedPhase(phase)) return "refused";
11729
+ switch (phase) {
11730
+ case "resolving":
11731
+ return "resolving";
11732
+ case "prejoin":
11733
+ case "guest_name":
11734
+ return "prejoin";
11735
+ case "joining":
11736
+ case "in_call":
11737
+ return "room";
11738
+ case "waiting:host_not_started":
11739
+ case "waiting:knocking":
11740
+ case "denied":
11741
+ case "knock_expired":
11742
+ return "waiting";
11743
+ case "left":
11744
+ return snapshot?.waiting?.admission === "withdrawn" ? "waiting" : "exit";
11745
+ case "disconnected":
11746
+ case "removed":
11747
+ case "superseded":
11748
+ case "inactive":
11749
+ case "unsupported":
11750
+ case "ended":
11751
+ case "record":
11752
+ case "ringing_out":
11753
+ case "ringing_in":
11754
+ case "call_declined":
11755
+ case "call_missed":
11756
+ case "call_busy":
11757
+ case "answered_elsewhere":
11758
+ return "exit";
11759
+ default: {
11760
+ const unhandled = phase;
11761
+ return unhandled;
11762
+ }
11763
+ }
11764
+ })();
11765
+ if (screen === "resolving") {
11766
+ return /* @__PURE__ */ (0, import_jsx_runtime6.jsx)("div", { className: "mx-meet", children: /* @__PURE__ */ (0, import_jsx_runtime6.jsx)("p", { className: "mx-meet__empty-body", role: "status", children: "Getting the meeting ready\u2026" }) });
11767
+ }
11768
+ if (screen === "prejoin") {
11338
11769
  return /* @__PURE__ */ (0, import_jsx_runtime6.jsx)("div", { className: "mx-meet", children: /* @__PURE__ */ (0, import_jsx_runtime6.jsx)(
11339
11770
  PreJoin,
11340
11771
  {
@@ -11345,9 +11776,12 @@ function MeetSkinRoom({
11345
11776
  }
11346
11777
  ) });
11347
11778
  }
11348
- if (phase === "lobby") {
11779
+ if (screen === "waiting") {
11349
11780
  return /* @__PURE__ */ (0, import_jsx_runtime6.jsx)("div", { className: "mx-meet mx-meet--lobby", children: /* @__PURE__ */ (0, import_jsx_runtime6.jsx)(WaitingRoom, { host, ...onLeave !== void 0 ? { onLeave } : {} }) });
11350
11781
  }
11782
+ if (screen === "refused" || screen === "exit") {
11783
+ return /* @__PURE__ */ (0, import_jsx_runtime6.jsx)("div", { className: "mx-meet mx-meet--lobby", children: /* @__PURE__ */ (0, import_jsx_runtime6.jsx)(ExitScreen, { ...onLeave !== void 0 ? { onLeave } : {} }) });
11784
+ }
11351
11785
  return /* @__PURE__ */ (0, import_jsx_runtime6.jsxs)("div", { className: "mx-meet", children: [
11352
11786
  /* @__PURE__ */ (0, import_jsx_runtime6.jsx)(ConsentNotice, {}),
11353
11787
  /* @__PURE__ */ (0, import_jsx_runtime6.jsx)(AttendanceNotice, {}),
@@ -11367,14 +11801,148 @@ function MeetSkinRoom({
11367
11801
  headerControls !== void 0 && headerControls !== null ? /* @__PURE__ */ (0, import_jsx_runtime6.jsx)("span", { className: "mx-meet__header-controls", "data-meet-header-controls": "", children: headerControls }) : null,
11368
11802
  isHost ? /* @__PURE__ */ (0, import_jsx_runtime6.jsx)(HostMenu, {}) : null
11369
11803
  ] }),
11370
- phase === "reconnecting" ? /* @__PURE__ */ (0, import_jsx_runtime6.jsx)("p", { className: "mx-meet__banner", role: "status", children: "Reconnecting\u2026" }) : null,
11804
+ core.connection !== "stable" ? /* @__PURE__ */ (0, import_jsx_runtime6.jsx)("p", { className: "mx-meet__banner", role: "status", children: "Reconnecting\u2026" }) : null,
11805
+ /* @__PURE__ */ (0, import_jsx_runtime6.jsx)(ShareNotice, {}),
11371
11806
  /* @__PURE__ */ (0, import_jsx_runtime6.jsx)(Stage, {}),
11372
11807
  /* @__PURE__ */ (0, import_jsx_runtime6.jsx)(Captions, {}),
11373
11808
  /* @__PURE__ */ (0, import_jsx_runtime6.jsx)(ControlBar, { ...onLeave !== void 0 ? { onLeave } : {} })
11374
11809
  ] });
11375
11810
  }
11811
+ function ShareNotice() {
11812
+ const { share, actions } = useMeetCore();
11813
+ if (share.state !== "refused") return null;
11814
+ const text3 = share.reason === "unsupported" ? "You can't share your screen from this browser" : share.reason === "blocked" ? "Screen sharing is blocked" : "Couldn't start sharing your screen";
11815
+ return /* @__PURE__ */ (0, import_jsx_runtime6.jsxs)("p", { className: "mx-meet__banner", role: "status", "data-meet-notice": "share-unavailable", children: [
11816
+ text3,
11817
+ share.remedy !== null ? /* @__PURE__ */ (0, import_jsx_runtime6.jsx)("span", { className: "mx-meet__remedy", children: share.remedy }) : null,
11818
+ /* @__PURE__ */ (0, import_jsx_runtime6.jsx)("button", { type: "button", className: "mx-meet__link", onClick: actions.dismissShareNotice, children: "Dismiss" })
11819
+ ] });
11820
+ }
11821
+ var REFUSED_TITLES = {
11822
+ not_found: "Check your meeting code",
11823
+ cancelled: "This meeting was cancelled",
11824
+ expired: "This meeting link has expired",
11825
+ too_early: "This meeting hasn't started yet",
11826
+ sign_in_required: "Sign in to join this meeting",
11827
+ wrong_account: "You're signed in with a different account",
11828
+ not_invited: "You aren't invited to this meeting",
11829
+ locked: "This meeting is locked",
11830
+ removed: "You can't rejoin this meeting",
11831
+ lobby_rejects: "You can't ask to join this meeting",
11832
+ meeting_full: "This meeting is full",
11833
+ platform_capacity: "Meetings are busy right now",
11834
+ verification_required: "Verify your account to join",
11835
+ registration_required: "Register to join this meeting"
11836
+ };
11837
+ function ExitScreen({ onLeave }) {
11838
+ const { phase, reason, host, snapshot, actions, call } = useMeetCore();
11839
+ const [busy, setBusy] = (0, import_react10.useState)(false);
11840
+ const failure = snapshot?.failure ?? null;
11841
+ const rejoin = /* @__PURE__ */ (0, import_jsx_runtime6.jsx)(
11842
+ import_design_system.Button,
11843
+ {
11844
+ type: "button",
11845
+ disabled: busy,
11846
+ onClick: () => {
11847
+ setBusy(true);
11848
+ void actions.rejoin().catch(() => void 0).finally(() => setBusy(false));
11849
+ },
11850
+ children: "Rejoin"
11851
+ }
11852
+ );
11853
+ const home = /* @__PURE__ */ (0, import_jsx_runtime6.jsx)(
11854
+ "a",
11855
+ {
11856
+ className: "mx-meet__link",
11857
+ href: "/",
11858
+ onClick: (event) => {
11859
+ if (call !== null) {
11860
+ event.preventDefault();
11861
+ actions.dismissCall();
11862
+ }
11863
+ if (onLeave !== void 0) {
11864
+ event.preventDefault();
11865
+ onLeave();
11866
+ }
11867
+ },
11868
+ children: "Go home"
11869
+ }
11870
+ );
11871
+ let title;
11872
+ let line = null;
11873
+ let notice = null;
11874
+ let actionsNode = home;
11875
+ if (phase.startsWith("refused:")) {
11876
+ const code = phase.slice("refused:".length);
11877
+ title = REFUSED_TITLES[code] ?? "You can't join this meeting";
11878
+ line = failure?.remedy ?? null;
11879
+ actionsNode = /* @__PURE__ */ (0, import_jsx_runtime6.jsxs)(import_jsx_runtime6.Fragment, { children: [
11880
+ /* @__PURE__ */ (0, import_jsx_runtime6.jsx)(import_design_system.Button, { type: "button", variant: "outline", onClick: actions.retry, children: "Try again" }),
11881
+ home
11882
+ ] });
11883
+ } else {
11884
+ switch (phase) {
11885
+ case "left":
11886
+ title = "You left the meeting";
11887
+ actionsNode = /* @__PURE__ */ (0, import_jsx_runtime6.jsxs)(import_jsx_runtime6.Fragment, { children: [
11888
+ rejoin,
11889
+ home
11890
+ ] });
11891
+ break;
11892
+ case "disconnected":
11893
+ title = "You lost your connection to the meeting";
11894
+ line = failure?.remedy ?? "Check your internet connection, then rejoin.";
11895
+ notice = "disconnected";
11896
+ actionsNode = /* @__PURE__ */ (0, import_jsx_runtime6.jsxs)(import_jsx_runtime6.Fragment, { children: [
11897
+ rejoin,
11898
+ home
11899
+ ] });
11900
+ break;
11901
+ case "removed":
11902
+ title = "You've been removed from the meeting";
11903
+ notice = "removed";
11904
+ break;
11905
+ case "superseded":
11906
+ title = "You joined from another tab or device";
11907
+ line = "This screen left the meeting so you aren't in it twice.";
11908
+ notice = "displaced";
11909
+ actionsNode = /* @__PURE__ */ (0, import_jsx_runtime6.jsxs)(import_jsx_runtime6.Fragment, { children: [
11910
+ rejoin,
11911
+ home
11912
+ ] });
11913
+ break;
11914
+ case "inactive":
11915
+ title = "You left the meeting because this tab was inactive";
11916
+ actionsNode = /* @__PURE__ */ (0, import_jsx_runtime6.jsxs)(import_jsx_runtime6.Fragment, { children: [
11917
+ rejoin,
11918
+ home
11919
+ ] });
11920
+ break;
11921
+ case "unsupported":
11922
+ title = "This browser can't join meetings";
11923
+ line = "Open the link in Chrome, Edge, Firefox or Safari.";
11924
+ break;
11925
+ case "ended":
11926
+ case "record":
11927
+ title = reason === "time_limit" ? "The meeting reached its time limit" : "The meeting has ended";
11928
+ notice = "meeting-ended";
11929
+ break;
11930
+ default:
11931
+ title = "The call has ended";
11932
+ break;
11933
+ }
11934
+ }
11935
+ return /* @__PURE__ */ (0, import_jsx_runtime6.jsxs)("div", { className: "mx-meet__waiting", "data-meet-exit": phase, children: [
11936
+ /* @__PURE__ */ (0, import_jsx_runtime6.jsx)(Avatar, { name: host?.identity.displayName ?? "", url: host?.identity.avatarUrl ?? null, size: 72 }),
11937
+ /* @__PURE__ */ (0, import_jsx_runtime6.jsx)("h1", { className: "mx-meet__prejoin-title", children: title }),
11938
+ line !== null ? /* @__PURE__ */ (0, import_jsx_runtime6.jsx)("p", { className: "mx-meet__empty-body", children: line }) : null,
11939
+ notice !== null ? /* @__PURE__ */ (0, import_jsx_runtime6.jsx)("span", { role: "status", "data-meet-notice": notice, className: "mx-meet__sr-only", children: title }) : null,
11940
+ /* @__PURE__ */ (0, import_jsx_runtime6.jsx)("div", { className: "mx-meet__waiting-actions", children: actionsNode })
11941
+ ] });
11942
+ }
11376
11943
  function WaitingRoom({ host, onLeave }) {
11377
11944
  const { waiting, withdraw, askAgain } = useWaiting();
11945
+ const { phaseNotice } = useMeetCore();
11378
11946
  const [busy, setBusy] = (0, import_react10.useState)(false);
11379
11947
  const admission = waiting?.admission ?? "knocking";
11380
11948
  const run = (action) => () => {
@@ -11423,6 +11991,7 @@ function WaitingRoom({ host, onLeave }) {
11423
11991
  return /* @__PURE__ */ (0, import_jsx_runtime6.jsxs)("div", { className: "mx-meet__waiting", children: [
11424
11992
  /* @__PURE__ */ (0, import_jsx_runtime6.jsx)(Avatar, { name: host.identity.displayName, url: host.identity.avatarUrl, size: 72 }),
11425
11993
  /* @__PURE__ */ (0, import_jsx_runtime6.jsx)("h1", { className: "mx-meet__prejoin-title", children: title }),
11994
+ phaseNotice === "sent-to-waiting" ? /* @__PURE__ */ (0, import_jsx_runtime6.jsx)("p", { className: "mx-meet__empty-body", role: "status", "data-meet-notice": "sent-to-waiting", children: "The host moved you back to the waiting room" }) : null,
11426
11995
  line !== null ? /* @__PURE__ */ (0, import_jsx_runtime6.jsx)("p", { className: "mx-meet__empty-body", role: "status", "data-meet-notice": "host-absent", children: line }) : null,
11427
11996
  /* @__PURE__ */ (0, import_jsx_runtime6.jsx)("div", { className: "mx-meet__waiting-actions", children: actions })
11428
11997
  ] });
@@ -11917,10 +12486,17 @@ function MeetingSkinRootBody({
11917
12486
  host
11918
12487
  }) {
11919
12488
  const snapshot = useMeetSnapshot();
11920
- const phase = snapshot?.phase ?? "idle";
12489
+ const phase = snapshot?.phase ?? "resolving";
11921
12490
  (0, import_react12.useEffect)(() => {
11922
12491
  host.meetings.watch(roomName);
11923
12492
  }, [host, roomName]);
12493
+ (0, import_react12.useEffect)(() => {
12494
+ const now = host.store.snapshot();
12495
+ if (now.phase !== "resolving" && now.roomName !== null && now.roomName !== roomName && now.call === null) {
12496
+ host.store.dispatch({ type: "reset" });
12497
+ }
12498
+ if (host.store.snapshot().phase === "resolving") host.store.dispatch({ type: "resolved" });
12499
+ }, [host, roomName, phase]);
11924
12500
  (0, import_react12.useEffect)(() => {
11925
12501
  if (given === void 0) return;
11926
12502
  if (host.store.snapshot().meeting !== null) return;
@@ -11932,7 +12508,7 @@ function MeetingSkinRootBody({
11932
12508
  host.meetings.watchMeeting(asMeetingId(watchedId));
11933
12509
  }, [host, watchedId]);
11934
12510
  const record = snapshot?.meeting ?? given ?? null;
11935
- if (hasEnded(record) && phase !== "connected" && phase !== "reconnecting") {
12511
+ if (hasEnded(record) && !isLivePhase(phase)) {
11936
12512
  return /* @__PURE__ */ (0, import_jsx_runtime8.jsx)(MeetingRecordView, { meeting: record });
11937
12513
  }
11938
12514
  return /* @__PURE__ */ (0, import_jsx_runtime8.jsx)(
@@ -11949,11 +12525,49 @@ function MeetingSkinRootBody({
11949
12525
  }
11950
12526
 
11951
12527
  // src/react/calls-ui.tsx
12528
+ var import_react13 = require("react");
11952
12529
  var import_jsx_runtime9 = require("react/jsx-runtime");
11953
12530
  function IncomingCallHost() {
11954
12531
  const calls = useCalls();
11955
- if (calls.incoming.length === 0) return null;
11956
- return /* @__PURE__ */ (0, import_jsx_runtime9.jsx)("div", { className: "mx-meet__ringing-stack", children: calls.incoming.map((invite) => /* @__PURE__ */ (0, import_jsx_runtime9.jsx)(IncomingCallCard, { invite }, invite.id)) });
12532
+ const { call, phase, actions } = useMeetCore();
12533
+ (0, import_react13.useEffect)(() => {
12534
+ if (call !== null && phase === "left") actions.dismissCall();
12535
+ }, [call, phase, actions]);
12536
+ const outcome = call !== null && (phase === "call_declined" || phase === "call_missed" || phase === "call_busy" || phase === "answered_elsewhere");
12537
+ (0, import_react13.useEffect)(() => {
12538
+ if (!outcome) return void 0;
12539
+ const timer = setTimeout(() => actions.dismissCall(), 5e3);
12540
+ return () => clearTimeout(timer);
12541
+ }, [outcome, actions]);
12542
+ const inCall = call !== null && (phase === "joining" || phase === "in_call" || phase === "disconnected" || phase === "removed" || phase === "superseded");
12543
+ return /* @__PURE__ */ (0, import_jsx_runtime9.jsxs)(import_jsx_runtime9.Fragment, { children: [
12544
+ inCall && call !== null ? /* @__PURE__ */ (0, import_jsx_runtime9.jsx)("div", { className: "mx-meet__call-overlay", "data-meet-call": call.invite.id, children: /* @__PURE__ */ (0, import_jsx_runtime9.jsx)(MeetRoot, { children: /* @__PURE__ */ (0, import_jsx_runtime9.jsx)(MeetSkinRoom, { roomName: call.invite.roomName, onLeave: actions.dismissCall }) }) }) : null,
12545
+ calls.incoming.length > 0 || call !== null && (phase === "ringing_out" || outcome) ? /* @__PURE__ */ (0, import_jsx_runtime9.jsxs)("div", { className: "mx-meet__ringing-stack", children: [
12546
+ call !== null && phase === "ringing_out" ? /* @__PURE__ */ (0, import_jsx_runtime9.jsx)(OutgoingCallCard, { invite: call.invite }) : null,
12547
+ outcome && call !== null ? /* @__PURE__ */ (0, import_jsx_runtime9.jsx)(CallOutcomeCard, { invite: call.invite }) : null,
12548
+ calls.incoming.map((invite) => /* @__PURE__ */ (0, import_jsx_runtime9.jsx)(IncomingCallCard, { invite }, invite.id))
12549
+ ] }) : null
12550
+ ] });
12551
+ }
12552
+ function OutgoingCallCard({ invite }) {
12553
+ const calls = useCalls();
12554
+ return /* @__PURE__ */ (0, import_jsx_runtime9.jsxs)("section", { className: "mx-meet__ringing", role: "status", "aria-label": "Calling", "data-meet-notice": "ringing-out", children: [
12555
+ /* @__PURE__ */ (0, import_jsx_runtime9.jsx)(Avatar, { name: invite.callerName, url: invite.callerAvatarUrl, size: 56 }),
12556
+ /* @__PURE__ */ (0, import_jsx_runtime9.jsxs)("div", { className: "mx-meet__ringing-text", children: [
12557
+ /* @__PURE__ */ (0, import_jsx_runtime9.jsx)("strong", { children: "Calling\u2026" }),
12558
+ /* @__PURE__ */ (0, import_jsx_runtime9.jsx)("span", { children: invite.mode === "video" ? "Video call" : "Voice call" })
12559
+ ] }),
12560
+ /* @__PURE__ */ (0, import_jsx_runtime9.jsx)("div", { className: "mx-meet__ringing-actions", children: /* @__PURE__ */ (0, import_jsx_runtime9.jsx)(ControlButton, { label: "Cancel call", danger: true, onClick: () => void calls.cancel(invite), children: /* @__PURE__ */ (0, import_jsx_runtime9.jsx)(PhoneOffIcon, {}) }) })
12561
+ ] });
12562
+ }
12563
+ function CallOutcomeCard({ invite }) {
12564
+ const { phase, actions, call } = useMeetCore();
12565
+ const outgoing = call?.direction === "outgoing";
12566
+ const text3 = phase === "call_declined" ? "Call declined" : phase === "call_busy" ? "They're on another call" : phase === "answered_elsewhere" ? "Answered on another device" : outgoing ? "No answer" : `Missed call from ${invite.callerName}`;
12567
+ return /* @__PURE__ */ (0, import_jsx_runtime9.jsxs)("section", { className: "mx-meet__ringing", role: "status", "aria-label": text3, "data-meet-call-outcome": phase, children: [
12568
+ /* @__PURE__ */ (0, import_jsx_runtime9.jsx)("div", { className: "mx-meet__ringing-text", children: /* @__PURE__ */ (0, import_jsx_runtime9.jsx)("strong", { children: text3 }) }),
12569
+ /* @__PURE__ */ (0, import_jsx_runtime9.jsx)("div", { className: "mx-meet__ringing-actions", children: /* @__PURE__ */ (0, import_jsx_runtime9.jsx)(ControlButton, { label: "Close", onClick: actions.dismissCall, children: /* @__PURE__ */ (0, import_jsx_runtime9.jsx)(PhoneOffIcon, {}) }) })
12570
+ ] });
11957
12571
  }
11958
12572
  function IncomingCallCard({ invite }) {
11959
12573
  const calls = useCalls();
@@ -12128,26 +12742,4 @@ async function pushMeetingToGoogleCalendar(api, meetingId, target) {
12128
12742
  if (!response.ok) throw await refusal(response, operation);
12129
12743
  return await response.json();
12130
12744
  }
12131
-
12132
- // src/core/reasons.ts
12133
- var JOIN_REFUSAL_REASONS = [
12134
- "not_found",
12135
- "cancelled",
12136
- "ended",
12137
- "expired",
12138
- "too_early",
12139
- "sign_in_required",
12140
- "wrong_account",
12141
- "not_invited",
12142
- "locked",
12143
- "removed",
12144
- "lobby_rejects",
12145
- "meeting_full",
12146
- "platform_capacity",
12147
- "verification_required",
12148
- "registration_required"
12149
- ];
12150
- function isJoinRefusalReason(value) {
12151
- return typeof value === "string" && JOIN_REFUSAL_REASONS.includes(value);
12152
- }
12153
12745
  //# sourceMappingURL=react.cjs.map