@ai-matrx/meet 0.10.6 → 0.11.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.
@@ -112,9 +112,6 @@ var import_react10 = require("react");
112
112
  // src/react/facade.ts
113
113
  var import_react8 = require("react");
114
114
 
115
- // src/react/root.tsx
116
- var import_react3 = require("react");
117
-
118
115
  // src/core/names.ts
119
116
  function isGuestIdentity(identity) {
120
117
  return String(identity).startsWith("guest:");
@@ -364,6 +361,27 @@ function isObserverRole(role) {
364
361
  return role === "viewer";
365
362
  }
366
363
 
364
+ // src/core/phase.ts
365
+ var LIVE_PHASES = /* @__PURE__ */ new Set(["joining", "in_call"]);
366
+ var WAITING_PHASES = /* @__PURE__ */ new Set([
367
+ "waiting:host_not_started",
368
+ "waiting:knocking",
369
+ "denied",
370
+ "knock_expired"
371
+ ]);
372
+ function isLivePhase(phase) {
373
+ return LIVE_PHASES.has(phase);
374
+ }
375
+ function isWaitingPhase(phase) {
376
+ return WAITING_PHASES.has(phase);
377
+ }
378
+ function isRefusedPhase(phase) {
379
+ return phase.startsWith("refused:");
380
+ }
381
+ function reasonOf(phase) {
382
+ return isRefusedPhase(phase) ? phase.slice("refused:".length) : null;
383
+ }
384
+
367
385
  // src/core/app-panel.ts
368
386
  function appPanelOf(meeting) {
369
387
  const raw = meeting?.metadata?.["app_panel"];
@@ -444,6 +462,7 @@ var DEFAULT_EFFECTS = {
444
462
  background: "none",
445
463
  backgroundImage: null
446
464
  };
465
+ var NO_SHARE = { state: "none", reason: null, remedy: null, at: 0 };
447
466
  function effectiveRole(snapshot) {
448
467
  const me = snapshot.participants.find(
449
468
  (participant) => participant.identity === snapshot.localIdentity
@@ -459,6 +478,9 @@ function effectiveStanding(snapshot) {
459
478
  }
460
479
  var REACTION_TTL_MS = 5e3;
461
480
 
481
+ // src/react/root.tsx
482
+ var import_react3 = require("react");
483
+
462
484
  // src/core/observation.ts
463
485
  function observedRole(role, standing) {
464
486
  if (role === "host" || role === "cohost") return role;
@@ -488,43 +510,12 @@ function endedRecord(snapshot, given) {
488
510
  function observedPhase(input) {
489
511
  const { snapshot } = input;
490
512
  if (snapshot === null) return "resolving";
491
- const raw = snapshot.phase;
492
- const live = raw === "connected" || raw === "reconnecting";
493
- if (endedRecord(snapshot, input.meeting ?? null) && !live) return "ended";
494
- const reason = raw === "failed" ? snapshot.failure?.reason ?? null : null;
495
- if (reason !== null) return reason === "ended" ? "ended" : `refused:${reason}`;
496
- switch (raw) {
497
- case "idle":
498
- case "failed":
499
- case "left":
500
- return "prejoin";
501
- case "lobby":
502
- return waitingPhase(snapshot);
503
- case "requesting-token":
504
- case "connecting":
505
- return "joining";
506
- case "connected":
507
- case "reconnecting":
508
- return "in_call";
509
- }
513
+ const phase = snapshot.phase;
514
+ if (endedRecord(snapshot, input.meeting ?? null) && !isLivePhase(phase) && !isCallPhase(phase)) return "ended";
515
+ return phase;
510
516
  }
511
- function waitingPhase(snapshot) {
512
- switch (snapshot.waiting?.admission) {
513
- case "waiting_host":
514
- return "waiting:host_not_started";
515
- case "denied":
516
- return "denied";
517
- case "expired":
518
- return "knock_expired";
519
- case "withdrawn":
520
- return "left";
521
- case "ended":
522
- return "ended";
523
- case "removed":
524
- return "refused:removed";
525
- default:
526
- return "waiting:knocking";
527
- }
517
+ function isCallPhase(phase) {
518
+ return phase.startsWith("ringing_") || phase.startsWith("call_") || phase === "answered_elsewhere";
528
519
  }
529
520
  var ADMITTER_ROLES = /* @__PURE__ */ new Set(["host", "cohost"]);
530
521
  function observe(input) {
@@ -537,10 +528,10 @@ function observe(input) {
537
528
  const deviceInPlay = phase === "joining" || inCall;
538
529
  return {
539
530
  phase,
540
- reason: phase.startsWith("refused:") ? phase.slice("refused:".length) : null,
531
+ reason: phase === "ended" ? snapshot?.reason ?? null : snapshot?.reason ?? reasonOf(phase),
541
532
  role,
542
- connection: snapshot?.phase === "reconnecting" ? "reconnecting" : "stable",
543
- hostPresent: inCall && snapshot !== null ? snapshot.participants.some((p) => ADMITTER_ROLES.has(p.role)) : snapshot?.phase === "lobby" ? snapshot.waiting?.admittersPresent ?? null : null,
533
+ connection: snapshot?.connection ?? "stable",
534
+ hostPresent: inCall && snapshot !== null ? snapshot.participants.some((p) => ADMITTER_ROLES.has(p.role)) : snapshot !== null && isWaitingPhase(snapshot.phase) ? snapshot.waiting?.admittersPresent ?? null : null,
544
535
  lobbyCount: canAdmit && snapshot !== null ? snapshot.lobby.filter((entry) => entry.state === "knocking").length : 0,
545
536
  recording: snapshot?.recording === "recording" || snapshot?.recording === "stopping",
546
537
  audioBlocked: null,
@@ -1054,7 +1045,7 @@ function useRoom() {
1054
1045
  (participant) => participant.identity === snapshot.localIdentity
1055
1046
  ) ?? null;
1056
1047
  return {
1057
- phase: snapshot?.phase ?? "idle",
1048
+ phase: snapshot?.phase ?? "resolving",
1058
1049
  participants: snapshot?.participants ?? [],
1059
1050
  failure: snapshot?.failure ?? null,
1060
1051
  local,
@@ -1653,6 +1644,36 @@ function useCollabPanelRequest(onRequest) {
1653
1644
  var MEET_REACTIONS = ["\u{1F496}", "\u{1F44D}", "\u{1F389}", "\u{1F44F}", "\u{1F602}", "\u{1F62E}", "\u{1F622}", "\u{1F914}", "\u{1F44E}"];
1654
1645
 
1655
1646
  // src/react/facade.ts
1647
+ var NO_ACTIONS = {
1648
+ join: async () => void 0,
1649
+ rejoin: async () => void 0,
1650
+ leave: async () => void 0,
1651
+ backToPrejoin: () => void 0,
1652
+ retry: () => void 0,
1653
+ resolved: () => void 0,
1654
+ dismissShareNotice: () => void 0,
1655
+ dismissCall: () => void 0
1656
+ };
1657
+ function actionsFor(host) {
1658
+ if (host === null) return NO_ACTIONS;
1659
+ const { engine, store } = host;
1660
+ return {
1661
+ join: (request) => engine.join(request),
1662
+ rejoin: () => engine.rejoin(),
1663
+ leave: () => engine.leave(),
1664
+ backToPrejoin: () => engine.backToPrejoin(),
1665
+ retry: () => {
1666
+ store.dispatch({ type: "retry", click: true });
1667
+ },
1668
+ resolved: () => {
1669
+ store.dispatch({ type: "resolved" });
1670
+ },
1671
+ dismissShareNotice: () => engine.dismissShareNotice(),
1672
+ dismissCall: () => {
1673
+ if (store.dispatch({ type: "reset" })) store.patch({ call: null });
1674
+ }
1675
+ };
1676
+ }
1656
1677
  function useMeetCore() {
1657
1678
  const host = useMeetHost();
1658
1679
  const snapshot = useMeetSnapshot();
@@ -1660,9 +1681,24 @@ function useMeetCore() {
1660
1681
  const role = useRole();
1661
1682
  const isHost = useIsHost();
1662
1683
  const observation = useCoreObservation();
1684
+ const actions = (0, import_react8.useMemo)(() => actionsFor(host), [host]);
1663
1685
  return (0, import_react8.useMemo)(
1664
- () => ({ host, snapshot, devices, role, isHost, observation }),
1665
- [host, snapshot, devices, role, isHost, observation]
1686
+ () => ({
1687
+ host,
1688
+ snapshot,
1689
+ devices,
1690
+ role,
1691
+ isHost,
1692
+ observation,
1693
+ phase: snapshot?.phase ?? "resolving",
1694
+ reason: snapshot?.reason ?? null,
1695
+ phaseNotice: snapshot?.phaseNotice ?? null,
1696
+ connection: snapshot?.connection ?? "stable",
1697
+ share: snapshot?.share ?? NO_SHARE,
1698
+ call: snapshot?.call ?? null,
1699
+ actions
1700
+ }),
1701
+ [host, snapshot, devices, role, isHost, observation, actions]
1666
1702
  );
1667
1703
  }
1668
1704
 
@@ -4609,11 +4645,52 @@ function MeetSkinRoom({
4609
4645
  headerControls,
4610
4646
  observe: observe2 = false
4611
4647
  }) {
4612
- const { host, snapshot, isHost } = useMeetCore();
4648
+ const core = useMeetCore();
4649
+ const { host, snapshot, isHost, phase } = core;
4613
4650
  const elapsed = useElapsed(snapshot?.meeting?.startedAt ?? null);
4614
- const phase = snapshot?.phase ?? "idle";
4615
4651
  if (host === null) return null;
4616
- if (phase === "idle" || phase === "failed" || phase === "left") {
4652
+ const screen = (() => {
4653
+ if (isRefusedPhase(phase)) return "refused";
4654
+ switch (phase) {
4655
+ case "resolving":
4656
+ return "resolving";
4657
+ case "prejoin":
4658
+ case "guest_name":
4659
+ return "prejoin";
4660
+ case "joining":
4661
+ case "in_call":
4662
+ return "room";
4663
+ case "waiting:host_not_started":
4664
+ case "waiting:knocking":
4665
+ case "denied":
4666
+ case "knock_expired":
4667
+ return "waiting";
4668
+ case "left":
4669
+ return snapshot?.waiting?.admission === "withdrawn" ? "waiting" : "exit";
4670
+ case "disconnected":
4671
+ case "removed":
4672
+ case "superseded":
4673
+ case "inactive":
4674
+ case "unsupported":
4675
+ case "ended":
4676
+ case "record":
4677
+ case "ringing_out":
4678
+ case "ringing_in":
4679
+ case "call_declined":
4680
+ case "call_missed":
4681
+ case "call_busy":
4682
+ case "answered_elsewhere":
4683
+ return "exit";
4684
+ default: {
4685
+ const unhandled = phase;
4686
+ return unhandled;
4687
+ }
4688
+ }
4689
+ })();
4690
+ if (screen === "resolving") {
4691
+ 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" }) });
4692
+ }
4693
+ if (screen === "prejoin") {
4617
4694
  return /* @__PURE__ */ (0, import_jsx_runtime6.jsx)("div", { className: "mx-meet", children: /* @__PURE__ */ (0, import_jsx_runtime6.jsx)(
4618
4695
  PreJoin,
4619
4696
  {
@@ -4624,9 +4701,12 @@ function MeetSkinRoom({
4624
4701
  }
4625
4702
  ) });
4626
4703
  }
4627
- if (phase === "lobby") {
4704
+ if (screen === "waiting") {
4628
4705
  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 } : {} }) });
4629
4706
  }
4707
+ if (screen === "refused" || screen === "exit") {
4708
+ 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 } : {} }) });
4709
+ }
4630
4710
  return /* @__PURE__ */ (0, import_jsx_runtime6.jsxs)("div", { className: "mx-meet", children: [
4631
4711
  /* @__PURE__ */ (0, import_jsx_runtime6.jsx)(ConsentNotice, {}),
4632
4712
  /* @__PURE__ */ (0, import_jsx_runtime6.jsx)(AttendanceNotice, {}),
@@ -4646,14 +4726,148 @@ function MeetSkinRoom({
4646
4726
  headerControls !== void 0 && headerControls !== null ? /* @__PURE__ */ (0, import_jsx_runtime6.jsx)("span", { className: "mx-meet__header-controls", "data-meet-header-controls": "", children: headerControls }) : null,
4647
4727
  isHost ? /* @__PURE__ */ (0, import_jsx_runtime6.jsx)(HostMenu, {}) : null
4648
4728
  ] }),
4649
- phase === "reconnecting" ? /* @__PURE__ */ (0, import_jsx_runtime6.jsx)("p", { className: "mx-meet__banner", role: "status", children: "Reconnecting\u2026" }) : null,
4729
+ core.connection !== "stable" ? /* @__PURE__ */ (0, import_jsx_runtime6.jsx)("p", { className: "mx-meet__banner", role: "status", children: "Reconnecting\u2026" }) : null,
4730
+ /* @__PURE__ */ (0, import_jsx_runtime6.jsx)(ShareNotice, {}),
4650
4731
  /* @__PURE__ */ (0, import_jsx_runtime6.jsx)(Stage, {}),
4651
4732
  /* @__PURE__ */ (0, import_jsx_runtime6.jsx)(Captions, {}),
4652
4733
  /* @__PURE__ */ (0, import_jsx_runtime6.jsx)(ControlBar, { ...onLeave !== void 0 ? { onLeave } : {} })
4653
4734
  ] });
4654
4735
  }
4736
+ function ShareNotice() {
4737
+ const { share, actions } = useMeetCore();
4738
+ if (share.state !== "refused") return null;
4739
+ const text = 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";
4740
+ return /* @__PURE__ */ (0, import_jsx_runtime6.jsxs)("p", { className: "mx-meet__banner", role: "status", "data-meet-notice": "share-unavailable", children: [
4741
+ text,
4742
+ share.remedy !== null ? /* @__PURE__ */ (0, import_jsx_runtime6.jsx)("span", { className: "mx-meet__remedy", children: share.remedy }) : null,
4743
+ /* @__PURE__ */ (0, import_jsx_runtime6.jsx)("button", { type: "button", className: "mx-meet__link", onClick: actions.dismissShareNotice, children: "Dismiss" })
4744
+ ] });
4745
+ }
4746
+ var REFUSED_TITLES = {
4747
+ not_found: "Check your meeting code",
4748
+ cancelled: "This meeting was cancelled",
4749
+ expired: "This meeting link has expired",
4750
+ too_early: "This meeting hasn't started yet",
4751
+ sign_in_required: "Sign in to join this meeting",
4752
+ wrong_account: "You're signed in with a different account",
4753
+ not_invited: "You aren't invited to this meeting",
4754
+ locked: "This meeting is locked",
4755
+ removed: "You can't rejoin this meeting",
4756
+ lobby_rejects: "You can't ask to join this meeting",
4757
+ meeting_full: "This meeting is full",
4758
+ platform_capacity: "Meetings are busy right now",
4759
+ verification_required: "Verify your account to join",
4760
+ registration_required: "Register to join this meeting"
4761
+ };
4762
+ function ExitScreen({ onLeave }) {
4763
+ const { phase, reason, host, snapshot, actions, call } = useMeetCore();
4764
+ const [busy, setBusy] = (0, import_react10.useState)(false);
4765
+ const failure = snapshot?.failure ?? null;
4766
+ const rejoin = /* @__PURE__ */ (0, import_jsx_runtime6.jsx)(
4767
+ import_design_system.Button,
4768
+ {
4769
+ type: "button",
4770
+ disabled: busy,
4771
+ onClick: () => {
4772
+ setBusy(true);
4773
+ void actions.rejoin().catch(() => void 0).finally(() => setBusy(false));
4774
+ },
4775
+ children: "Rejoin"
4776
+ }
4777
+ );
4778
+ const home = /* @__PURE__ */ (0, import_jsx_runtime6.jsx)(
4779
+ "a",
4780
+ {
4781
+ className: "mx-meet__link",
4782
+ href: "/",
4783
+ onClick: (event) => {
4784
+ if (call !== null) {
4785
+ event.preventDefault();
4786
+ actions.dismissCall();
4787
+ }
4788
+ if (onLeave !== void 0) {
4789
+ event.preventDefault();
4790
+ onLeave();
4791
+ }
4792
+ },
4793
+ children: "Go home"
4794
+ }
4795
+ );
4796
+ let title;
4797
+ let line = null;
4798
+ let notice = null;
4799
+ let actionsNode = home;
4800
+ if (phase.startsWith("refused:")) {
4801
+ const code = phase.slice("refused:".length);
4802
+ title = REFUSED_TITLES[code] ?? "You can't join this meeting";
4803
+ line = failure?.remedy ?? null;
4804
+ actionsNode = /* @__PURE__ */ (0, import_jsx_runtime6.jsxs)(import_jsx_runtime6.Fragment, { children: [
4805
+ /* @__PURE__ */ (0, import_jsx_runtime6.jsx)(import_design_system.Button, { type: "button", variant: "outline", onClick: actions.retry, children: "Try again" }),
4806
+ home
4807
+ ] });
4808
+ } else {
4809
+ switch (phase) {
4810
+ case "left":
4811
+ title = "You left the meeting";
4812
+ actionsNode = /* @__PURE__ */ (0, import_jsx_runtime6.jsxs)(import_jsx_runtime6.Fragment, { children: [
4813
+ rejoin,
4814
+ home
4815
+ ] });
4816
+ break;
4817
+ case "disconnected":
4818
+ title = "You lost your connection to the meeting";
4819
+ line = failure?.remedy ?? "Check your internet connection, then rejoin.";
4820
+ notice = "disconnected";
4821
+ actionsNode = /* @__PURE__ */ (0, import_jsx_runtime6.jsxs)(import_jsx_runtime6.Fragment, { children: [
4822
+ rejoin,
4823
+ home
4824
+ ] });
4825
+ break;
4826
+ case "removed":
4827
+ title = "You've been removed from the meeting";
4828
+ notice = "removed";
4829
+ break;
4830
+ case "superseded":
4831
+ title = "You joined from another tab or device";
4832
+ line = "This screen left the meeting so you aren't in it twice.";
4833
+ notice = "displaced";
4834
+ actionsNode = /* @__PURE__ */ (0, import_jsx_runtime6.jsxs)(import_jsx_runtime6.Fragment, { children: [
4835
+ rejoin,
4836
+ home
4837
+ ] });
4838
+ break;
4839
+ case "inactive":
4840
+ title = "You left the meeting because this tab was inactive";
4841
+ actionsNode = /* @__PURE__ */ (0, import_jsx_runtime6.jsxs)(import_jsx_runtime6.Fragment, { children: [
4842
+ rejoin,
4843
+ home
4844
+ ] });
4845
+ break;
4846
+ case "unsupported":
4847
+ title = "This browser can't join meetings";
4848
+ line = "Open the link in Chrome, Edge, Firefox or Safari.";
4849
+ break;
4850
+ case "ended":
4851
+ case "record":
4852
+ title = reason === "time_limit" ? "The meeting reached its time limit" : "The meeting has ended";
4853
+ notice = "meeting-ended";
4854
+ break;
4855
+ default:
4856
+ title = "The call has ended";
4857
+ break;
4858
+ }
4859
+ }
4860
+ return /* @__PURE__ */ (0, import_jsx_runtime6.jsxs)("div", { className: "mx-meet__waiting", "data-meet-exit": phase, children: [
4861
+ /* @__PURE__ */ (0, import_jsx_runtime6.jsx)(Avatar, { name: host?.identity.displayName ?? "", url: host?.identity.avatarUrl ?? null, size: 72 }),
4862
+ /* @__PURE__ */ (0, import_jsx_runtime6.jsx)("h1", { className: "mx-meet__prejoin-title", children: title }),
4863
+ line !== null ? /* @__PURE__ */ (0, import_jsx_runtime6.jsx)("p", { className: "mx-meet__empty-body", children: line }) : null,
4864
+ notice !== null ? /* @__PURE__ */ (0, import_jsx_runtime6.jsx)("span", { role: "status", "data-meet-notice": notice, className: "mx-meet__sr-only", children: title }) : null,
4865
+ /* @__PURE__ */ (0, import_jsx_runtime6.jsx)("div", { className: "mx-meet__waiting-actions", children: actionsNode })
4866
+ ] });
4867
+ }
4655
4868
  function WaitingRoom({ host, onLeave }) {
4656
4869
  const { waiting, withdraw, askAgain } = useWaiting();
4870
+ const { phaseNotice } = useMeetCore();
4657
4871
  const [busy, setBusy] = (0, import_react10.useState)(false);
4658
4872
  const admission = waiting?.admission ?? "knocking";
4659
4873
  const run = (action) => () => {
@@ -4702,6 +4916,7 @@ function WaitingRoom({ host, onLeave }) {
4702
4916
  return /* @__PURE__ */ (0, import_jsx_runtime6.jsxs)("div", { className: "mx-meet__waiting", children: [
4703
4917
  /* @__PURE__ */ (0, import_jsx_runtime6.jsx)(Avatar, { name: host.identity.displayName, url: host.identity.avatarUrl, size: 72 }),
4704
4918
  /* @__PURE__ */ (0, import_jsx_runtime6.jsx)("h1", { className: "mx-meet__prejoin-title", children: title }),
4919
+ 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,
4705
4920
  line !== null ? /* @__PURE__ */ (0, import_jsx_runtime6.jsx)("p", { className: "mx-meet__empty-body", role: "status", "data-meet-notice": "host-absent", children: line }) : null,
4706
4921
  /* @__PURE__ */ (0, import_jsx_runtime6.jsx)("div", { className: "mx-meet__waiting-actions", children: actions })
4707
4922
  ] });