@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/CHANGELOG.md +6 -0
- package/dist/{collab-ytIzjceh.d.cts → collab-CIyXdQ5l.d.cts} +1 -2
- package/dist/{collab-ytIzjceh.d.ts → collab-CIyXdQ5l.d.ts} +1 -2
- package/dist/{components-CnnefQn-.d.cts → components-DUvy3BQf.d.cts} +1 -1
- package/dist/{components-DjzH3u_c.d.ts → components-DiGxfKXI.d.ts} +1 -1
- package/dist/core.cjs +417 -100
- package/dist/core.cjs.map +1 -1
- package/dist/core.d.cts +254 -24
- package/dist/core.d.ts +254 -24
- package/dist/core.js +417 -100
- package/dist/core.js.map +1 -1
- package/dist/index.cjs +417 -100
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +2 -2
- package/dist/index.d.ts +2 -2
- package/dist/index.js +417 -100
- package/dist/index.js.map +1 -1
- package/dist/react.cjs +710 -118
- package/dist/react.cjs.map +1 -1
- package/dist/react.d.cts +68 -7
- package/dist/react.d.ts +68 -7
- package/dist/react.js +711 -119
- package/dist/react.js.map +1 -1
- package/dist/skins/meet.cjs +265 -50
- 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 +265 -50
- package/dist/skins/meet.js.map +1 -1
- package/dist/styles.css +20 -0
- package/package.json +6 -6
package/dist/react.js
CHANGED
|
@@ -1010,6 +1010,166 @@ var MEET_EVENTS = {
|
|
|
1010
1010
|
stateChanged: "mx.state.changed"
|
|
1011
1011
|
};
|
|
1012
1012
|
|
|
1013
|
+
// src/core/reasons.ts
|
|
1014
|
+
var JOIN_REFUSAL_REASONS = [
|
|
1015
|
+
"not_found",
|
|
1016
|
+
"cancelled",
|
|
1017
|
+
"ended",
|
|
1018
|
+
"expired",
|
|
1019
|
+
"too_early",
|
|
1020
|
+
"sign_in_required",
|
|
1021
|
+
"wrong_account",
|
|
1022
|
+
"not_invited",
|
|
1023
|
+
"locked",
|
|
1024
|
+
"removed",
|
|
1025
|
+
"lobby_rejects",
|
|
1026
|
+
"meeting_full",
|
|
1027
|
+
"platform_capacity",
|
|
1028
|
+
"verification_required",
|
|
1029
|
+
"registration_required"
|
|
1030
|
+
];
|
|
1031
|
+
function isJoinRefusalReason(value) {
|
|
1032
|
+
return typeof value === "string" && JOIN_REFUSAL_REASONS.includes(value);
|
|
1033
|
+
}
|
|
1034
|
+
|
|
1035
|
+
// src/core/phase.ts
|
|
1036
|
+
var LIVE_PHASES = /* @__PURE__ */ new Set(["joining", "in_call"]);
|
|
1037
|
+
var WAITING_PHASES = /* @__PURE__ */ new Set([
|
|
1038
|
+
"waiting:host_not_started",
|
|
1039
|
+
"waiting:knocking",
|
|
1040
|
+
"denied",
|
|
1041
|
+
"knock_expired"
|
|
1042
|
+
]);
|
|
1043
|
+
var JOINABLE_FROM = /* @__PURE__ */ new Set([
|
|
1044
|
+
"prejoin",
|
|
1045
|
+
"left",
|
|
1046
|
+
"disconnected",
|
|
1047
|
+
"superseded",
|
|
1048
|
+
"inactive",
|
|
1049
|
+
"denied",
|
|
1050
|
+
"knock_expired"
|
|
1051
|
+
]);
|
|
1052
|
+
var NOT_ENDABLE = /* @__PURE__ */ new Set([
|
|
1053
|
+
"ended",
|
|
1054
|
+
"record",
|
|
1055
|
+
"unsupported",
|
|
1056
|
+
"ringing_out",
|
|
1057
|
+
"ringing_in",
|
|
1058
|
+
"call_declined",
|
|
1059
|
+
"call_missed",
|
|
1060
|
+
"call_busy",
|
|
1061
|
+
"answered_elsewhere"
|
|
1062
|
+
]);
|
|
1063
|
+
var RINGING = /* @__PURE__ */ new Set(["ringing_out", "ringing_in"]);
|
|
1064
|
+
var IDLE_PHASES = /* @__PURE__ */ new Set([
|
|
1065
|
+
"resolving",
|
|
1066
|
+
"left",
|
|
1067
|
+
"call_declined",
|
|
1068
|
+
"call_missed",
|
|
1069
|
+
"call_busy",
|
|
1070
|
+
"answered_elsewhere"
|
|
1071
|
+
]);
|
|
1072
|
+
function isLivePhase(phase) {
|
|
1073
|
+
return LIVE_PHASES.has(phase);
|
|
1074
|
+
}
|
|
1075
|
+
function isWaitingPhase(phase) {
|
|
1076
|
+
return WAITING_PHASES.has(phase);
|
|
1077
|
+
}
|
|
1078
|
+
function isRefusedPhase(phase) {
|
|
1079
|
+
return phase.startsWith("refused:");
|
|
1080
|
+
}
|
|
1081
|
+
function reasonOf(phase) {
|
|
1082
|
+
return isRefusedPhase(phase) ? phase.slice("refused:".length) : null;
|
|
1083
|
+
}
|
|
1084
|
+
function refusalPhase(reason) {
|
|
1085
|
+
if (reason === null || !isJoinRefusalReason(reason)) return "disconnected";
|
|
1086
|
+
return reason === "ended" ? "ended" : `refused:${reason}`;
|
|
1087
|
+
}
|
|
1088
|
+
function admissionPhase(admission) {
|
|
1089
|
+
switch (admission) {
|
|
1090
|
+
case "waiting_host":
|
|
1091
|
+
return "waiting:host_not_started";
|
|
1092
|
+
case "knocking":
|
|
1093
|
+
return "waiting:knocking";
|
|
1094
|
+
case "denied":
|
|
1095
|
+
return "denied";
|
|
1096
|
+
case "expired":
|
|
1097
|
+
return "knock_expired";
|
|
1098
|
+
case "withdrawn":
|
|
1099
|
+
return "left";
|
|
1100
|
+
case "ended":
|
|
1101
|
+
return "ended";
|
|
1102
|
+
case "removed":
|
|
1103
|
+
return "refused:removed";
|
|
1104
|
+
default:
|
|
1105
|
+
return null;
|
|
1106
|
+
}
|
|
1107
|
+
}
|
|
1108
|
+
function nextPhase(from, event) {
|
|
1109
|
+
switch (event.type) {
|
|
1110
|
+
case "resolved":
|
|
1111
|
+
return from === "resolving" ? "prejoin" : null;
|
|
1112
|
+
case "unsupported":
|
|
1113
|
+
return from === "resolving" ? "unsupported" : null;
|
|
1114
|
+
case "join":
|
|
1115
|
+
return JOINABLE_FROM.has(from) ? "joining" : null;
|
|
1116
|
+
case "back_to_prejoin":
|
|
1117
|
+
return from === "left" || from === "disconnected" ? "prejoin" : null;
|
|
1118
|
+
case "retry":
|
|
1119
|
+
return isRefusedPhase(from) || from === "unsupported" ? "resolving" : null;
|
|
1120
|
+
case "admission": {
|
|
1121
|
+
if (from !== "joining" && !WAITING_PHASES.has(from)) return null;
|
|
1122
|
+
return admissionPhase(event.admission);
|
|
1123
|
+
}
|
|
1124
|
+
case "admitted":
|
|
1125
|
+
return WAITING_PHASES.has(from) ? "joining" : null;
|
|
1126
|
+
case "connected":
|
|
1127
|
+
return from === "joining" ? "in_call" : null;
|
|
1128
|
+
case "join_failed":
|
|
1129
|
+
return from === "joining" ? refusalPhase(event.reason) : null;
|
|
1130
|
+
case "exit":
|
|
1131
|
+
return from === "in_call" || from === "joining" ? event.to : null;
|
|
1132
|
+
case "leave":
|
|
1133
|
+
return from === "in_call" || from === "joining" || WAITING_PHASES.has(from) ? "left" : null;
|
|
1134
|
+
case "ended":
|
|
1135
|
+
return NOT_ENDABLE.has(from) ? null : "ended";
|
|
1136
|
+
case "ring_out":
|
|
1137
|
+
return IDLE_PHASES.has(from) || from === "prejoin" ? "ringing_out" : null;
|
|
1138
|
+
case "ring_in":
|
|
1139
|
+
return IDLE_PHASES.has(from) ? "ringing_in" : null;
|
|
1140
|
+
case "call_answered":
|
|
1141
|
+
return RINGING.has(from) ? "joining" : null;
|
|
1142
|
+
case "call_outcome":
|
|
1143
|
+
return RINGING.has(from) ? event.outcome : null;
|
|
1144
|
+
case "reset":
|
|
1145
|
+
return LIVE_PHASES.has(from) ? null : "resolving";
|
|
1146
|
+
}
|
|
1147
|
+
}
|
|
1148
|
+
function resolveExit(input) {
|
|
1149
|
+
const { server, hint } = input;
|
|
1150
|
+
if (server === null) {
|
|
1151
|
+
return { to: "disconnected", reason: null, connection: "server_unreachable", notice: null };
|
|
1152
|
+
}
|
|
1153
|
+
const word = server?.exitReason ?? null;
|
|
1154
|
+
if (server !== void 0 && (server.sessionPhase === "ended" || server.meetingPhase === "ended" || word === "ended" || word === "time_limit")) {
|
|
1155
|
+
return { to: "ended", reason: word === "time_limit" ? "time_limit" : "ended", connection: "stable", notice: null };
|
|
1156
|
+
}
|
|
1157
|
+
if (word === "removed") return { to: "removed", reason: "removed", connection: "stable", notice: null };
|
|
1158
|
+
if (word === "displaced") return { to: "superseded", reason: "displaced", connection: "stable", notice: null };
|
|
1159
|
+
if (word === "sent_to_waiting") {
|
|
1160
|
+
return { to: "waiting:knocking", reason: "sent_to_waiting", connection: "stable", notice: "sent-to-waiting" };
|
|
1161
|
+
}
|
|
1162
|
+
if (hint === "DUPLICATE_IDENTITY") return { to: "superseded", reason: "displaced", connection: "stable", notice: null };
|
|
1163
|
+
if (hint === "PARTICIPANT_REMOVED" && word === null && server === void 0) {
|
|
1164
|
+
return { to: "removed", reason: "removed", connection: "stable", notice: null };
|
|
1165
|
+
}
|
|
1166
|
+
if (hint === "ROOM_DELETED" && server === void 0) {
|
|
1167
|
+
return { to: "ended", reason: "ended", connection: "stable", notice: null };
|
|
1168
|
+
}
|
|
1169
|
+
if (input.frozen === true) return { to: "inactive", reason: "inactive", connection: "stable", notice: null };
|
|
1170
|
+
return { to: "disconnected", reason: null, connection: "stable", notice: null };
|
|
1171
|
+
}
|
|
1172
|
+
|
|
1013
1173
|
// src/core/types.ts
|
|
1014
1174
|
var asMeetingId = (value) => value;
|
|
1015
1175
|
var asCallId = (value) => value;
|
|
@@ -1101,6 +1261,39 @@ function createCallCenter(options) {
|
|
|
1101
1261
|
clearRing(invite.id);
|
|
1102
1262
|
incoming.delete(invite.id);
|
|
1103
1263
|
if (outgoing !== null && outgoing.id === invite.id) outgoing = invite;
|
|
1264
|
+
const held = options.store.snapshot().call;
|
|
1265
|
+
if (held !== null && held.invite.id === invite.id && held.direction === "incoming") {
|
|
1266
|
+
const outcome = invite.state === "connecting" || invite.state === "active" ? "answered_elsewhere" : invite.state === "declined" ? "reset" : "call_missed";
|
|
1267
|
+
const moved = outcome === "reset" ? options.store.dispatch({ type: "reset" }) : options.store.dispatch({ type: "call_outcome", outcome });
|
|
1268
|
+
if (moved) options.store.patch({ call: outcome === "reset" ? null : { ...held, invite } });
|
|
1269
|
+
}
|
|
1270
|
+
options.onSettled?.(invite);
|
|
1271
|
+
}
|
|
1272
|
+
function settleOutgoing(invite) {
|
|
1273
|
+
if (outgoing === null || outgoing.id !== invite.id) return;
|
|
1274
|
+
clearRing(invite.id);
|
|
1275
|
+
outgoing = invite;
|
|
1276
|
+
const store = options.store;
|
|
1277
|
+
const held = store.snapshot().call;
|
|
1278
|
+
if (held !== null && held.invite.id === invite.id) store.patch({ call: { ...held, invite } });
|
|
1279
|
+
switch (invite.state) {
|
|
1280
|
+
case "connecting":
|
|
1281
|
+
case "active":
|
|
1282
|
+
if (store.dispatch({ type: "call_answered" })) options.onAnswered?.(invite);
|
|
1283
|
+
break;
|
|
1284
|
+
case "declined":
|
|
1285
|
+
store.dispatch({ type: "call_outcome", outcome: "call_declined" });
|
|
1286
|
+
break;
|
|
1287
|
+
case "busy":
|
|
1288
|
+
store.dispatch({ type: "call_outcome", outcome: "call_busy" });
|
|
1289
|
+
break;
|
|
1290
|
+
case "missed":
|
|
1291
|
+
case "failed":
|
|
1292
|
+
store.dispatch({ type: "call_outcome", outcome: "call_missed" });
|
|
1293
|
+
break;
|
|
1294
|
+
default:
|
|
1295
|
+
break;
|
|
1296
|
+
}
|
|
1104
1297
|
options.onSettled?.(invite);
|
|
1105
1298
|
}
|
|
1106
1299
|
function ingest(invite) {
|
|
@@ -1109,7 +1302,7 @@ function createCallCenter(options) {
|
|
|
1109
1302
|
if (identity.userId === null) return;
|
|
1110
1303
|
if (invite.callerUserId === identity.userId) return;
|
|
1111
1304
|
const phase = options.store.snapshot().phase;
|
|
1112
|
-
if (phase
|
|
1305
|
+
if (isLivePhase(phase) || phase === "ringing_out") {
|
|
1113
1306
|
void repository.settleCallInvite(invite.id, "busy", null).then((settled) => {
|
|
1114
1307
|
to(settled.callerUserId).send(MEET_EVENTS.settled, settled);
|
|
1115
1308
|
}).catch((error) => {
|
|
@@ -1122,6 +1315,9 @@ function createCallCenter(options) {
|
|
|
1122
1315
|
return;
|
|
1123
1316
|
}
|
|
1124
1317
|
incoming.set(invite.id, { ...invite, state: "ringing" });
|
|
1318
|
+
if (options.store.snapshot().call === null && options.store.dispatch({ type: "ring_in" })) {
|
|
1319
|
+
options.store.patch({ call: { invite: { ...invite, state: "ringing" }, direction: "incoming" } });
|
|
1320
|
+
}
|
|
1125
1321
|
options.onIncoming?.(invite);
|
|
1126
1322
|
timeouts.set(
|
|
1127
1323
|
invite.id,
|
|
@@ -1159,6 +1355,7 @@ function createCallCenter(options) {
|
|
|
1159
1355
|
const invite = message.data;
|
|
1160
1356
|
const held = incoming.get(invite.id);
|
|
1161
1357
|
if (held !== void 0) settleLocal(invite);
|
|
1358
|
+
else if (outgoing?.id === invite.id) settleOutgoing(invite);
|
|
1162
1359
|
}
|
|
1163
1360
|
}
|
|
1164
1361
|
],
|
|
@@ -1225,6 +1422,9 @@ function createCallCenter(options) {
|
|
|
1225
1422
|
expiresAt: new Date(timers.now() + ringTimeoutMs).toISOString()
|
|
1226
1423
|
});
|
|
1227
1424
|
outgoing = invite;
|
|
1425
|
+
if (options.store.dispatch({ type: "ring_out" })) {
|
|
1426
|
+
options.store.patch({ call: { invite, direction: "outgoing" } });
|
|
1427
|
+
}
|
|
1228
1428
|
for (const callee of people) {
|
|
1229
1429
|
to(callee).send(MEET_EVENTS.ring, invite);
|
|
1230
1430
|
}
|
|
@@ -1233,8 +1433,7 @@ function createCallCenter(options) {
|
|
|
1233
1433
|
timers.setTimeout(() => {
|
|
1234
1434
|
if (outgoing?.id !== invite.id) return;
|
|
1235
1435
|
void repository.settleCallInvite(invite.id, "missed", null).then((settled) => {
|
|
1236
|
-
|
|
1237
|
-
options.onSettled?.(settled);
|
|
1436
|
+
settleOutgoing(settled);
|
|
1238
1437
|
}).catch(() => void 0);
|
|
1239
1438
|
}, ringTimeoutMs)
|
|
1240
1439
|
);
|
|
@@ -1249,7 +1448,9 @@ function createCallCenter(options) {
|
|
|
1249
1448
|
"A late accept must not resurrect a settled call. Close the ringing UI; the caller has already moved on."
|
|
1250
1449
|
);
|
|
1251
1450
|
}
|
|
1451
|
+
if (options.store.snapshot().phase === "ringing_in") options.store.dispatch({ type: "call_answered" });
|
|
1252
1452
|
const settled = await repository.settleCallInvite(id, "connecting", null);
|
|
1453
|
+
options.store.patch({ call: { invite: settled, direction: "incoming" } });
|
|
1253
1454
|
settleLocal(settled);
|
|
1254
1455
|
channel?.send(MEET_EVENTS.settled, settled);
|
|
1255
1456
|
to(settled.callerUserId).send(MEET_EVENTS.settled, { ...settled, mode });
|
|
@@ -1257,6 +1458,9 @@ function createCallCenter(options) {
|
|
|
1257
1458
|
},
|
|
1258
1459
|
async decline(id, message) {
|
|
1259
1460
|
const settled = await repository.settleCallInvite(id, "declined", message ?? null);
|
|
1461
|
+
if (options.store.snapshot().call?.invite.id === id && options.store.dispatch({ type: "reset" })) {
|
|
1462
|
+
options.store.patch({ call: null });
|
|
1463
|
+
}
|
|
1260
1464
|
settleLocal(settled);
|
|
1261
1465
|
channel?.send(MEET_EVENTS.settled, settled);
|
|
1262
1466
|
to(settled.callerUserId).send(MEET_EVENTS.settled, settled);
|
|
@@ -1266,6 +1470,9 @@ function createCallCenter(options) {
|
|
|
1266
1470
|
const settled = await repository.settleCallInvite(id, "cancelled", null);
|
|
1267
1471
|
clearRing(id);
|
|
1268
1472
|
if (outgoing?.id === id) outgoing = settled;
|
|
1473
|
+
if (options.store.snapshot().call?.invite.id === id && options.store.dispatch({ type: "reset" })) {
|
|
1474
|
+
options.store.patch({ call: null });
|
|
1475
|
+
}
|
|
1269
1476
|
for (const callee of settled.calleeUserIds) {
|
|
1270
1477
|
to(callee).send(MEET_EVENTS.cancel, settled);
|
|
1271
1478
|
}
|
|
@@ -1651,7 +1858,9 @@ var TABLES = {
|
|
|
1651
1858
|
/** Wave 1 (0.8): who a meeting was sent to, and what they answered. */
|
|
1652
1859
|
invitees: "meet_invitees",
|
|
1653
1860
|
/** Wave 1 (0.8): one occurrence of a series cancelled or moved. */
|
|
1654
|
-
occurrenceOverrides: "meet_occurrence_overrides"
|
|
1861
|
+
occurrenceOverrides: "meet_occurrence_overrides",
|
|
1862
|
+
/** S5: one row per RUN of a meeting; carries that run's note-taker state. */
|
|
1863
|
+
sessions: "meet_sessions"
|
|
1655
1864
|
};
|
|
1656
1865
|
var RPCS = {
|
|
1657
1866
|
/** Atomic: create the invite row and stamp its expiry in one statement. */
|
|
@@ -2127,6 +2336,18 @@ function createMeetRepository(options) {
|
|
|
2127
2336
|
);
|
|
2128
2337
|
}
|
|
2129
2338
|
}
|
|
2339
|
+
async function withRunNoteTaker(meeting) {
|
|
2340
|
+
const response = await db.from(TABLES.sessions).select("note_taker,started_at").eq("meeting_id", meeting.id).order("started_at", { ascending: false }).limit(1);
|
|
2341
|
+
const run = response.error === null ? (response.data ?? [])[0] : void 0;
|
|
2342
|
+
const noteTaker = run?.["note_taker"];
|
|
2343
|
+
if (typeof noteTaker !== "object" || noteTaker === null || Array.isArray(noteTaker)) {
|
|
2344
|
+
return meeting;
|
|
2345
|
+
}
|
|
2346
|
+
return {
|
|
2347
|
+
...meeting,
|
|
2348
|
+
metadata: { ...meeting.metadata ?? {}, note_taker: noteTaker }
|
|
2349
|
+
};
|
|
2350
|
+
}
|
|
2130
2351
|
return {
|
|
2131
2352
|
schema: MEET_SCHEMA,
|
|
2132
2353
|
tables: TABLES,
|
|
@@ -2365,13 +2586,13 @@ function createMeetRepository(options) {
|
|
|
2365
2586
|
"meet.meetingBySlug",
|
|
2366
2587
|
() => db.rpc(RPCS.meetingBySlug, { p_slug: slug })
|
|
2367
2588
|
);
|
|
2368
|
-
return projectMeeting(row);
|
|
2589
|
+
return withRunNoteTaker(projectMeeting(row));
|
|
2369
2590
|
},
|
|
2370
2591
|
async meeting(id) {
|
|
2371
2592
|
const response = await db.from(TABLES.meetings).select(MEETING_COLUMNS).eq("id", id).single();
|
|
2372
2593
|
if (response.error !== null) throw normalizeMeetError(response.error, "meet.meeting");
|
|
2373
2594
|
if (response.data === null) throw invalidResponse("meet.meeting", "no such meeting");
|
|
2374
|
-
return projectMeeting(response.data);
|
|
2595
|
+
return withRunNoteTaker(projectMeeting(response.data));
|
|
2375
2596
|
},
|
|
2376
2597
|
async listMeetings({ organizationId = null, limit = 25, before = null }) {
|
|
2377
2598
|
let query = db.from(TABLES.meetings).select(MEETING_COLUMNS);
|
|
@@ -3004,8 +3225,15 @@ var NO_GRANTS = {
|
|
|
3004
3225
|
canPublish: false,
|
|
3005
3226
|
canSubscribe: false
|
|
3006
3227
|
};
|
|
3228
|
+
var NO_SHARE = { state: "none", reason: null, remedy: null, at: 0 };
|
|
3007
3229
|
var EMPTY = {
|
|
3008
|
-
phase: "
|
|
3230
|
+
phase: "resolving",
|
|
3231
|
+
reason: null,
|
|
3232
|
+
phaseNotice: null,
|
|
3233
|
+
roomName: null,
|
|
3234
|
+
connection: "stable",
|
|
3235
|
+
share: NO_SHARE,
|
|
3236
|
+
call: null,
|
|
3009
3237
|
waiting: null,
|
|
3010
3238
|
meeting: null,
|
|
3011
3239
|
localIdentity: null,
|
|
@@ -3069,8 +3297,20 @@ function createMeetStore(initial = {}) {
|
|
|
3069
3297
|
return () => listeners.delete(listener);
|
|
3070
3298
|
},
|
|
3071
3299
|
patch(next) {
|
|
3300
|
+
if ("phase" in next || "reason" in next || "phaseNotice" in next) {
|
|
3301
|
+
throw new TypeError("meet store: the phase moves only through store.dispatch(event)");
|
|
3302
|
+
}
|
|
3072
3303
|
commit({ ...state, ...next });
|
|
3073
3304
|
},
|
|
3305
|
+
dispatch(event, detail) {
|
|
3306
|
+
const to = nextPhase(state.phase, event);
|
|
3307
|
+
if (to === null) return false;
|
|
3308
|
+
const reason = detail?.reason !== void 0 ? detail.reason : reasonOf(to);
|
|
3309
|
+
const phaseNotice = detail?.notice ?? null;
|
|
3310
|
+
if (to === state.phase && reason === state.reason && phaseNotice === state.phaseNotice) return true;
|
|
3311
|
+
commit({ ...state, phase: to, reason, phaseNotice });
|
|
3312
|
+
return true;
|
|
3313
|
+
},
|
|
3074
3314
|
setParticipants(participants) {
|
|
3075
3315
|
const names = learnNames(state.names, participants);
|
|
3076
3316
|
const hands = new Map(state.participants.map((p) => [p.identity, p.handRaised]));
|
|
@@ -3388,7 +3628,7 @@ function createMeetingSession(options) {
|
|
|
3388
3628
|
}
|
|
3389
3629
|
function apply(read) {
|
|
3390
3630
|
const snapshot = store.snapshot();
|
|
3391
|
-
if (snapshot.phase
|
|
3631
|
+
if (!isWaitingPhase(snapshot.phase)) return;
|
|
3392
3632
|
const admission = read.admission_state ?? null;
|
|
3393
3633
|
const ended = admission === "ended" || read.session_phase === "ended" || read.meeting_phase === "ended";
|
|
3394
3634
|
if (admission === "admitted" && !ended) {
|
|
@@ -3405,6 +3645,7 @@ function createMeetingSession(options) {
|
|
|
3405
3645
|
if (read.meeting_phase === "ended" && meeting !== null && meeting.endedAt === null) {
|
|
3406
3646
|
store.patch({ meeting: { ...meeting, endedAt: (/* @__PURE__ */ new Date()).toISOString() } });
|
|
3407
3647
|
}
|
|
3648
|
+
store.dispatch({ type: "admission", admission: "ended" }, { reason: "ended" });
|
|
3408
3649
|
return;
|
|
3409
3650
|
}
|
|
3410
3651
|
if (admission === null) return;
|
|
@@ -3416,6 +3657,7 @@ function createMeetingSession(options) {
|
|
|
3416
3657
|
pollSeconds: read.poll_seconds ?? snapshot.waiting?.pollSeconds ?? 3,
|
|
3417
3658
|
liveVersion: read.live_version ?? 0
|
|
3418
3659
|
});
|
|
3660
|
+
store.dispatch({ type: "admission", admission });
|
|
3419
3661
|
if (admission === "denied" || admission === "expired" || admission === "withdrawn" || admission === "removed") {
|
|
3420
3662
|
stopWaiting();
|
|
3421
3663
|
}
|
|
@@ -3511,7 +3753,7 @@ function createMeetingSession(options) {
|
|
|
3511
3753
|
// 🚨 PAYLOAD-FREE. Every receiver reads again; a spoofed hint only causes a read.
|
|
3512
3754
|
event: MEET_EVENTS.stateChanged,
|
|
3513
3755
|
onMessage: () => {
|
|
3514
|
-
if (store.snapshot().phase
|
|
3756
|
+
if (isWaitingPhase(store.snapshot().phase)) void readState2();
|
|
3515
3757
|
void refreshQueue();
|
|
3516
3758
|
}
|
|
3517
3759
|
}
|
|
@@ -3523,7 +3765,7 @@ function createMeetingSession(options) {
|
|
|
3523
3765
|
if (meeting === null) return;
|
|
3524
3766
|
const fresh = isGuest(identity) ? await repository.meetingBySlug(meeting.slug) : await readMeeting(meeting.id);
|
|
3525
3767
|
store.patch({ meeting: fresh, locked: fresh.locked });
|
|
3526
|
-
if (store.snapshot().phase
|
|
3768
|
+
if (isWaitingPhase(store.snapshot().phase)) void readState2();
|
|
3527
3769
|
}
|
|
3528
3770
|
});
|
|
3529
3771
|
},
|
|
@@ -3690,10 +3932,34 @@ function createMeetingSession(options) {
|
|
|
3690
3932
|
},
|
|
3691
3933
|
stopWaiting,
|
|
3692
3934
|
readState: readState2,
|
|
3935
|
+
async readExit(connection) {
|
|
3936
|
+
const meeting = store.snapshot().meeting;
|
|
3937
|
+
const pass = options.resolvePass?.() ?? null;
|
|
3938
|
+
if (meeting === null || pass === null) return void 0;
|
|
3939
|
+
try {
|
|
3940
|
+
const response = await options.api.request({
|
|
3941
|
+
path: `${MEET_ROUTES.state}?meeting_id=${encodeURIComponent(meeting.id)}&connection=${encodeURIComponent(connection)}`,
|
|
3942
|
+
method: "GET",
|
|
3943
|
+
requireAuth: false,
|
|
3944
|
+
operation: "meet.state(exit)",
|
|
3945
|
+
headers: { [MEETING_PASS_HEADER]: pass }
|
|
3946
|
+
});
|
|
3947
|
+
if (!response.ok) return null;
|
|
3948
|
+
const read = await response.json();
|
|
3949
|
+
return {
|
|
3950
|
+
exitReason: read.exit_reason ?? null,
|
|
3951
|
+
sessionPhase: read.session_phase ?? null,
|
|
3952
|
+
meetingPhase: read.meeting_phase ?? null
|
|
3953
|
+
};
|
|
3954
|
+
} catch {
|
|
3955
|
+
return null;
|
|
3956
|
+
}
|
|
3957
|
+
},
|
|
3693
3958
|
async withdraw() {
|
|
3694
3959
|
stopWaiting();
|
|
3695
3960
|
await postLeave("withdraw");
|
|
3696
3961
|
patchWaiting({ admission: "withdrawn" });
|
|
3962
|
+
store.dispatch({ type: "admission", admission: "withdrawn" });
|
|
3697
3963
|
hint();
|
|
3698
3964
|
},
|
|
3699
3965
|
async askAgain() {
|
|
@@ -4177,6 +4443,9 @@ var TRANSCRIPTION_ATTRIBUTES = {
|
|
|
4177
4443
|
};
|
|
4178
4444
|
|
|
4179
4445
|
// src/core/room.ts
|
|
4446
|
+
function SCREEN_SHARE_BLOCKED_REMEDY_FOR(remedy) {
|
|
4447
|
+
return /administrator|policy/i.test(remedy) ? remedy : SCREEN_SHARE_BLOCKED_REMEDY;
|
|
4448
|
+
}
|
|
4180
4449
|
var PARTICIPANT_ROLE_ATTRIBUTE = "matrx.participant.role";
|
|
4181
4450
|
var PARTICIPANT_CAN_RECORD_ATTRIBUTE = "matrx.participant.can_record";
|
|
4182
4451
|
var PARTICIPANT_STANDING_ATTRIBUTE = "matrx.participant.standing";
|
|
@@ -4244,7 +4513,7 @@ function createRoomEngine(options) {
|
|
|
4244
4513
|
let disposed = false;
|
|
4245
4514
|
let moving = false;
|
|
4246
4515
|
const confirmingEnds = /* @__PURE__ */ new WeakSet();
|
|
4247
|
-
async function
|
|
4516
|
+
async function dropRoom() {
|
|
4248
4517
|
const target = driver;
|
|
4249
4518
|
driver = null;
|
|
4250
4519
|
if (current !== null) {
|
|
@@ -4253,9 +4522,14 @@ function createRoomEngine(options) {
|
|
|
4253
4522
|
}
|
|
4254
4523
|
store.setLocalTrack("microphone", IDLE_TRACK);
|
|
4255
4524
|
store.setLocalTrack("camera", IDLE_TRACK);
|
|
4256
|
-
store.patch({
|
|
4525
|
+
store.patch({ share: NO_SHARE, connection: "stable" });
|
|
4257
4526
|
await target?.disconnect();
|
|
4258
4527
|
}
|
|
4528
|
+
async function leaveRoom() {
|
|
4529
|
+
store.dispatch({ type: "leave" });
|
|
4530
|
+
await dropRoom();
|
|
4531
|
+
options.onLeft?.(null);
|
|
4532
|
+
}
|
|
4259
4533
|
async function confirmMeetingEnd() {
|
|
4260
4534
|
const meeting = store.snapshot().meeting;
|
|
4261
4535
|
const target = driver;
|
|
@@ -4277,7 +4551,9 @@ function createRoomEngine(options) {
|
|
|
4277
4551
|
const latest = store.snapshot().meeting;
|
|
4278
4552
|
if (latest === null) return;
|
|
4279
4553
|
store.patch({ meeting: { ...latest, endedAt: verified.endedAt } });
|
|
4280
|
-
|
|
4554
|
+
store.dispatch({ type: "ended" }, { reason: "ended" });
|
|
4555
|
+
await dropRoom();
|
|
4556
|
+
options.onLeft?.("ended");
|
|
4281
4557
|
} catch (error) {
|
|
4282
4558
|
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.");
|
|
4283
4559
|
} finally {
|
|
@@ -4289,12 +4565,12 @@ function createRoomEngine(options) {
|
|
|
4289
4565
|
options.onDiagnostic?.(remedy === void 0 ? { level, message } : { level, message, remedy });
|
|
4290
4566
|
}
|
|
4291
4567
|
let ambiguousShareDenials = 0;
|
|
4292
|
-
function
|
|
4568
|
+
function failJoin(error, operation) {
|
|
4293
4569
|
const normalized = normalizeMeetError(error, operation);
|
|
4294
4570
|
store.patch({
|
|
4295
|
-
phase: "failed",
|
|
4296
4571
|
failure: { message: normalized.message, remedy: normalized.remedy, reason: normalized.reason }
|
|
4297
4572
|
});
|
|
4573
|
+
store.dispatch({ type: "join_failed", reason: normalized.reason ?? null });
|
|
4298
4574
|
report(
|
|
4299
4575
|
normalized.code === "session-unavailable" ? "warn" : "error",
|
|
4300
4576
|
normalized.message,
|
|
@@ -4302,6 +4578,14 @@ function createRoomEngine(options) {
|
|
|
4302
4578
|
);
|
|
4303
4579
|
return normalized;
|
|
4304
4580
|
}
|
|
4581
|
+
async function lostRoom(error, operation) {
|
|
4582
|
+
const normalized = normalizeMeetError(error, operation);
|
|
4583
|
+
store.patch({ failure: { message: normalized.message, remedy: normalized.remedy, reason: normalized.reason } });
|
|
4584
|
+
await dropRoom();
|
|
4585
|
+
store.dispatch({ type: "exit", to: "disconnected" });
|
|
4586
|
+
report("error", normalized.message, normalized.remedy);
|
|
4587
|
+
return normalized;
|
|
4588
|
+
}
|
|
4305
4589
|
const handlers = {
|
|
4306
4590
|
participants(list) {
|
|
4307
4591
|
const joinedAt = new Date(now()).toISOString();
|
|
@@ -4344,9 +4628,12 @@ function createRoomEngine(options) {
|
|
|
4344
4628
|
store.setQuality(asParticipantIdentity(who), value);
|
|
4345
4629
|
},
|
|
4346
4630
|
connectionState(state) {
|
|
4347
|
-
if (state === "connected")
|
|
4348
|
-
|
|
4349
|
-
|
|
4631
|
+
if (state === "connected") {
|
|
4632
|
+
if (store.snapshot().connection !== "stable") store.patch({ connection: "stable" });
|
|
4633
|
+
store.dispatch({ type: "connected" });
|
|
4634
|
+
} else if (state === "reconnecting") {
|
|
4635
|
+
store.patch({ connection: "reconnecting" });
|
|
4636
|
+
}
|
|
4350
4637
|
},
|
|
4351
4638
|
transcription(event) {
|
|
4352
4639
|
const snapshot = store.snapshot();
|
|
@@ -4473,17 +4760,48 @@ function createRoomEngine(options) {
|
|
|
4473
4760
|
}
|
|
4474
4761
|
},
|
|
4475
4762
|
disconnected(reason) {
|
|
4476
|
-
if (moving) return;
|
|
4477
|
-
|
|
4478
|
-
tokens.invalidate(current.roomName);
|
|
4479
|
-
current = null;
|
|
4480
|
-
}
|
|
4481
|
-
store.patch({ phase: "left" });
|
|
4482
|
-
options.onLeft?.(reason);
|
|
4763
|
+
if (moving || driver === null) return;
|
|
4764
|
+
void resolveExitAndApply(reason);
|
|
4483
4765
|
}
|
|
4484
4766
|
};
|
|
4767
|
+
async function resolveExitAndApply(hint) {
|
|
4768
|
+
const connection = current?.identity ?? store.snapshot().localIdentity ?? null;
|
|
4769
|
+
await dropRoom();
|
|
4770
|
+
let server;
|
|
4771
|
+
if (options.readExit === void 0 || connection === null || store.snapshot().meeting === null) {
|
|
4772
|
+
server = void 0;
|
|
4773
|
+
} else {
|
|
4774
|
+
try {
|
|
4775
|
+
server = await options.readExit(connection);
|
|
4776
|
+
} catch {
|
|
4777
|
+
server = null;
|
|
4778
|
+
}
|
|
4779
|
+
}
|
|
4780
|
+
if (disposed) return;
|
|
4781
|
+
const resolved = resolveExit({ server, hint, frozen: options.wasFrozen?.() ?? false });
|
|
4782
|
+
store.patch({ connection: resolved.connection });
|
|
4783
|
+
if (resolved.to === "waiting:knocking") {
|
|
4784
|
+
store.patch({
|
|
4785
|
+
waiting: {
|
|
4786
|
+
admission: "knocking",
|
|
4787
|
+
admittersPresent: null,
|
|
4788
|
+
reknockAllowed: true,
|
|
4789
|
+
knockExpiresAt: null,
|
|
4790
|
+
pollSeconds: store.snapshot().waiting?.pollSeconds ?? 3,
|
|
4791
|
+
liveVersion: 0
|
|
4792
|
+
}
|
|
4793
|
+
});
|
|
4794
|
+
}
|
|
4795
|
+
store.dispatch({ type: "exit", to: resolved.to }, { reason: resolved.reason, notice: resolved.notice });
|
|
4796
|
+
report(
|
|
4797
|
+
resolved.to === "disconnected" ? "warn" : "info",
|
|
4798
|
+
`meet: the room connection ended (${hint ?? "no LiveKit reason"}) \u2192 ${resolved.to}`,
|
|
4799
|
+
resolved.to === "disconnected" ? "Press Rejoin; the meeting is still going." : "The screen says what happened."
|
|
4800
|
+
);
|
|
4801
|
+
options.onLeft?.(hint);
|
|
4802
|
+
}
|
|
4485
4803
|
async function connect(request) {
|
|
4486
|
-
store.patch({
|
|
4804
|
+
store.patch({ failure: null, waiting: null, roomName: request.roomName, share: NO_SHARE, connection: "stable" });
|
|
4487
4805
|
const token = await tokens.get({
|
|
4488
4806
|
roomName: request.roomName,
|
|
4489
4807
|
identity,
|
|
@@ -4506,10 +4824,10 @@ function createRoomEngine(options) {
|
|
|
4506
4824
|
}
|
|
4507
4825
|
});
|
|
4508
4826
|
if (token.lobby || !token.canSubscribe) {
|
|
4827
|
+
const admission = token.admissionState !== void 0 && token.admissionState !== "admitted" ? token.admissionState : "knocking";
|
|
4509
4828
|
store.patch({
|
|
4510
|
-
phase: "lobby",
|
|
4511
4829
|
waiting: {
|
|
4512
|
-
admission
|
|
4830
|
+
admission,
|
|
4513
4831
|
admittersPresent: null,
|
|
4514
4832
|
reknockAllowed: true,
|
|
4515
4833
|
knockExpiresAt: null,
|
|
@@ -4517,9 +4835,10 @@ function createRoomEngine(options) {
|
|
|
4517
4835
|
liveVersion: 0
|
|
4518
4836
|
}
|
|
4519
4837
|
});
|
|
4838
|
+
store.dispatch({ type: "admission", admission });
|
|
4520
4839
|
return;
|
|
4521
4840
|
}
|
|
4522
|
-
store.patch({
|
|
4841
|
+
store.patch({ waiting: null });
|
|
4523
4842
|
const created = await options.driverFactory(handlers);
|
|
4524
4843
|
if (disposed) {
|
|
4525
4844
|
await created.disconnect();
|
|
@@ -4527,7 +4846,8 @@ function createRoomEngine(options) {
|
|
|
4527
4846
|
}
|
|
4528
4847
|
driver = created;
|
|
4529
4848
|
await created.connect(token.serverUrl, token.token);
|
|
4530
|
-
store.patch({
|
|
4849
|
+
store.patch({ failure: null, connection: "stable" });
|
|
4850
|
+
store.dispatch({ type: "connected" });
|
|
4531
4851
|
store.setLocalTrack("microphone", IDLE_TRACK);
|
|
4532
4852
|
store.setLocalTrack("camera", IDLE_TRACK);
|
|
4533
4853
|
const effects = options.effectsStorage?.load() ?? store.snapshot().effects;
|
|
@@ -4598,9 +4918,17 @@ function createRoomEngine(options) {
|
|
|
4598
4918
|
return {
|
|
4599
4919
|
join(request) {
|
|
4600
4920
|
if (joining !== null) return joining;
|
|
4921
|
+
if (store.snapshot().phase !== "joining" && !store.dispatch({ type: "join", click: true })) {
|
|
4922
|
+
report(
|
|
4923
|
+
"warn",
|
|
4924
|
+
`meet.join: cannot join from "${store.snapshot().phase}"`,
|
|
4925
|
+
"Join is offered from the pre-join, left, disconnected and replaced screens only."
|
|
4926
|
+
);
|
|
4927
|
+
return Promise.resolve();
|
|
4928
|
+
}
|
|
4601
4929
|
lastRequest = request;
|
|
4602
4930
|
joining = connect(request).catch((error) => {
|
|
4603
|
-
throw
|
|
4931
|
+
throw failJoin(error, "meet.join");
|
|
4604
4932
|
}).finally(() => {
|
|
4605
4933
|
joining = null;
|
|
4606
4934
|
});
|
|
@@ -4617,10 +4945,16 @@ function createRoomEngine(options) {
|
|
|
4617
4945
|
return;
|
|
4618
4946
|
}
|
|
4619
4947
|
if (joining !== null) return joining;
|
|
4948
|
+
const phase = store.snapshot().phase;
|
|
4949
|
+
const moved = phase === "joining" || store.dispatch({ type: "join", click: true }) || store.dispatch({ type: "admitted" });
|
|
4950
|
+
if (!moved) {
|
|
4951
|
+
report("warn", `meet.rejoin: cannot rejoin from "${phase}"`, "Rejoin is offered after leaving, a lost connection, or admission.");
|
|
4952
|
+
return;
|
|
4953
|
+
}
|
|
4620
4954
|
tokens.invalidate(request.roomName);
|
|
4621
4955
|
current = null;
|
|
4622
4956
|
joining = connect(request).catch((error) => {
|
|
4623
|
-
throw
|
|
4957
|
+
throw failJoin(error, "meet.rejoin");
|
|
4624
4958
|
}).finally(() => {
|
|
4625
4959
|
joining = null;
|
|
4626
4960
|
});
|
|
@@ -4665,11 +4999,11 @@ function createRoomEngine(options) {
|
|
|
4665
4999
|
joining = connect(lastRequest).finally(() => {
|
|
4666
5000
|
joining = null;
|
|
4667
5001
|
});
|
|
4668
|
-
await joining.catch((again) => {
|
|
4669
|
-
throw
|
|
5002
|
+
await joining.catch(async (again) => {
|
|
5003
|
+
throw await lostRoom(again, "meet.moveToBreakout");
|
|
4670
5004
|
});
|
|
4671
5005
|
} else {
|
|
4672
|
-
throw
|
|
5006
|
+
throw await lostRoom(error, "meet.moveToBreakout");
|
|
4673
5007
|
}
|
|
4674
5008
|
throw normalized;
|
|
4675
5009
|
} finally {
|
|
@@ -4677,6 +5011,9 @@ function createRoomEngine(options) {
|
|
|
4677
5011
|
}
|
|
4678
5012
|
},
|
|
4679
5013
|
leave: leaveRoom,
|
|
5014
|
+
backToPrejoin() {
|
|
5015
|
+
store.dispatch({ type: "back_to_prejoin", click: true });
|
|
5016
|
+
},
|
|
4680
5017
|
// Turning a device on mid-meeting is the SAME operation as turning it on
|
|
4681
5018
|
// during the join, so it runs the same function. A failure here used to
|
|
4682
5019
|
// stamp the room `failed` — a busy microphone would blank a live meeting.
|
|
@@ -4690,21 +5027,41 @@ function createRoomEngine(options) {
|
|
|
4690
5027
|
},
|
|
4691
5028
|
async setScreenShareEnabled(enabled) {
|
|
4692
5029
|
if (driver === null) return;
|
|
5030
|
+
if (enabled && driver.supportsScreenShare?.() === false) {
|
|
5031
|
+
store.patch({ share: { state: "refused", reason: "unsupported", remedy: null, at: now() } });
|
|
5032
|
+
report("warn", "meet: this browser cannot share a screen", "Share from a desktop browser (Chrome, Edge, Firefox or Safari).");
|
|
5033
|
+
return;
|
|
5034
|
+
}
|
|
5035
|
+
if (enabled) store.patch({ share: { state: "picking", reason: null, remedy: null, at: now() } });
|
|
4693
5036
|
try {
|
|
4694
5037
|
await driver.setScreenShareEnabled(enabled);
|
|
4695
5038
|
ambiguousShareDenials = 0;
|
|
5039
|
+
store.patch({ share: { state: enabled ? "presenting" : "none", reason: null, remedy: null, at: now() } });
|
|
4696
5040
|
} catch (error) {
|
|
4697
5041
|
const kind = classifyScreenShareError(error);
|
|
4698
5042
|
if (kind === "ambiguous") {
|
|
4699
5043
|
ambiguousShareDenials += 1;
|
|
4700
|
-
if (ambiguousShareDenials < 2)
|
|
5044
|
+
if (ambiguousShareDenials < 2) {
|
|
5045
|
+
store.patch({ share: NO_SHARE });
|
|
5046
|
+
return;
|
|
5047
|
+
}
|
|
4701
5048
|
}
|
|
5049
|
+
ambiguousShareDenials = 0;
|
|
4702
5050
|
const normalized = normalizeScreenShareError(error, "meet.setScreenShareEnabled");
|
|
4703
5051
|
report("warn", normalized.message, normalized.remedy);
|
|
4704
|
-
|
|
4705
|
-
|
|
5052
|
+
store.patch({
|
|
5053
|
+
share: {
|
|
5054
|
+
state: "refused",
|
|
5055
|
+
reason: kind === "other" ? "failed" : "blocked",
|
|
5056
|
+
remedy: kind === "other" ? normalized.remedy : SCREEN_SHARE_BLOCKED_REMEDY_FOR(normalized.remedy),
|
|
5057
|
+
at: now()
|
|
5058
|
+
}
|
|
5059
|
+
});
|
|
4706
5060
|
}
|
|
4707
5061
|
},
|
|
5062
|
+
dismissShareNotice() {
|
|
5063
|
+
if (store.snapshot().share.state === "refused") store.patch({ share: NO_SHARE });
|
|
5064
|
+
},
|
|
4708
5065
|
async raiseHand(raised) {
|
|
4709
5066
|
const local = store.snapshot().localIdentity;
|
|
4710
5067
|
if (local !== null) store.setHand(local, raised);
|
|
@@ -5206,7 +5563,8 @@ function createLiveKitDriver(handlers, options = {}) {
|
|
|
5206
5563
|
}).on(RoomEvent.Disconnected, (reason) => {
|
|
5207
5564
|
audioContainer?.remove();
|
|
5208
5565
|
audioContainer = null;
|
|
5209
|
-
|
|
5566
|
+
const named = reason === void 0 ? null : module?.DisconnectReason?.[reason] ?? String(reason);
|
|
5567
|
+
handlers.disconnected(named);
|
|
5210
5568
|
});
|
|
5211
5569
|
try {
|
|
5212
5570
|
created.registerTextStreamHandler(TRANSCRIPTION_TOPIC, (reader, participantInfo) => {
|
|
@@ -5594,7 +5952,9 @@ function MeetRuntime(props) {
|
|
|
5594
5952
|
driverFactory: (handlers) => (driverRef.current ?? createLiveKitDriver)(handlers),
|
|
5595
5953
|
onDiagnostic: report,
|
|
5596
5954
|
effectsStorage,
|
|
5597
|
-
resolveDevice: (kind) => devicesHolder.current?.resolve(kind) ?? null
|
|
5955
|
+
resolveDevice: (kind) => devicesHolder.current?.resolve(kind) ?? null,
|
|
5956
|
+
// §3.4: on every LiveKit disconnect, ask the server what happened to this connection.
|
|
5957
|
+
readExit: (connection) => meetings.readExit(connection)
|
|
5598
5958
|
});
|
|
5599
5959
|
const meetings = createMeetingSession({
|
|
5600
5960
|
manager,
|
|
@@ -5629,17 +5989,17 @@ function MeetRuntime(props) {
|
|
|
5629
5989
|
const inRoom = createInRoomClient({ api, store, resolveRoomToken });
|
|
5630
5990
|
store.subscribe(() => {
|
|
5631
5991
|
const snapshot = store.snapshot();
|
|
5632
|
-
if (snapshot.phase
|
|
5992
|
+
if (isWaitingPhase(snapshot.phase) && snapshot.waiting !== null && (snapshot.waiting.admission === "knocking" || snapshot.waiting.admission === "waiting_host")) {
|
|
5633
5993
|
meetings.startWaiting();
|
|
5634
|
-
} else if (snapshot.phase
|
|
5994
|
+
} else if (!isWaitingPhase(snapshot.phase)) {
|
|
5635
5995
|
meetings.stopWaiting();
|
|
5636
5996
|
}
|
|
5637
5997
|
});
|
|
5638
5998
|
let chatReadFor = null;
|
|
5639
5999
|
store.subscribe(() => {
|
|
5640
6000
|
const snapshot = store.snapshot();
|
|
5641
|
-
if (snapshot.phase !== "
|
|
5642
|
-
if (snapshot.phase === "left" || snapshot.phase === "
|
|
6001
|
+
if (snapshot.phase !== "in_call" || snapshot.meeting === null) {
|
|
6002
|
+
if (snapshot.phase === "left" || snapshot.phase === "prejoin" || snapshot.phase === "resolving") chatReadFor = null;
|
|
5643
6003
|
return;
|
|
5644
6004
|
}
|
|
5645
6005
|
if (chatReadFor === snapshot.meeting.id) return;
|
|
@@ -5692,6 +6052,10 @@ function MeetRuntime(props) {
|
|
|
5692
6052
|
store,
|
|
5693
6053
|
identity,
|
|
5694
6054
|
onIncoming: (invite) => incomingRef.current?.(invite),
|
|
6055
|
+
// §3.6: my call was answered — the caller joins the call's room on the same machine.
|
|
6056
|
+
onAnswered: (invite) => {
|
|
6057
|
+
void engine.join({ roomName: invite.roomName, mode: invite.mode }).catch(() => void 0);
|
|
6058
|
+
},
|
|
5695
6059
|
onDiagnostic: report,
|
|
5696
6060
|
...props.ringTimeoutMs !== void 0 ? { ringTimeoutMs: props.ringTimeoutMs } : {}
|
|
5697
6061
|
});
|
|
@@ -5831,43 +6195,12 @@ function endedRecord(snapshot, given) {
|
|
|
5831
6195
|
function observedPhase(input) {
|
|
5832
6196
|
const { snapshot } = input;
|
|
5833
6197
|
if (snapshot === null) return "resolving";
|
|
5834
|
-
const
|
|
5835
|
-
|
|
5836
|
-
|
|
5837
|
-
const reason = raw === "failed" ? snapshot.failure?.reason ?? null : null;
|
|
5838
|
-
if (reason !== null) return reason === "ended" ? "ended" : `refused:${reason}`;
|
|
5839
|
-
switch (raw) {
|
|
5840
|
-
case "idle":
|
|
5841
|
-
case "failed":
|
|
5842
|
-
case "left":
|
|
5843
|
-
return "prejoin";
|
|
5844
|
-
case "lobby":
|
|
5845
|
-
return waitingPhase(snapshot);
|
|
5846
|
-
case "requesting-token":
|
|
5847
|
-
case "connecting":
|
|
5848
|
-
return "joining";
|
|
5849
|
-
case "connected":
|
|
5850
|
-
case "reconnecting":
|
|
5851
|
-
return "in_call";
|
|
5852
|
-
}
|
|
6198
|
+
const phase = snapshot.phase;
|
|
6199
|
+
if (endedRecord(snapshot, input.meeting ?? null) && !isLivePhase(phase) && !isCallPhase(phase)) return "ended";
|
|
6200
|
+
return phase;
|
|
5853
6201
|
}
|
|
5854
|
-
function
|
|
5855
|
-
|
|
5856
|
-
case "waiting_host":
|
|
5857
|
-
return "waiting:host_not_started";
|
|
5858
|
-
case "denied":
|
|
5859
|
-
return "denied";
|
|
5860
|
-
case "expired":
|
|
5861
|
-
return "knock_expired";
|
|
5862
|
-
case "withdrawn":
|
|
5863
|
-
return "left";
|
|
5864
|
-
case "ended":
|
|
5865
|
-
return "ended";
|
|
5866
|
-
case "removed":
|
|
5867
|
-
return "refused:removed";
|
|
5868
|
-
default:
|
|
5869
|
-
return "waiting:knocking";
|
|
5870
|
-
}
|
|
6202
|
+
function isCallPhase(phase) {
|
|
6203
|
+
return phase.startsWith("ringing_") || phase.startsWith("call_") || phase === "answered_elsewhere";
|
|
5871
6204
|
}
|
|
5872
6205
|
var ADMITTER_ROLES = /* @__PURE__ */ new Set(["host", "cohost"]);
|
|
5873
6206
|
function observe(input) {
|
|
@@ -5880,10 +6213,10 @@ function observe(input) {
|
|
|
5880
6213
|
const deviceInPlay = phase === "joining" || inCall;
|
|
5881
6214
|
return {
|
|
5882
6215
|
phase,
|
|
5883
|
-
reason: phase
|
|
6216
|
+
reason: phase === "ended" ? snapshot?.reason ?? null : snapshot?.reason ?? reasonOf(phase),
|
|
5884
6217
|
role,
|
|
5885
|
-
connection: snapshot?.
|
|
5886
|
-
hostPresent: inCall && snapshot !== null ? snapshot.participants.some((p) => ADMITTER_ROLES.has(p.role)) : snapshot
|
|
6218
|
+
connection: snapshot?.connection ?? "stable",
|
|
6219
|
+
hostPresent: inCall && snapshot !== null ? snapshot.participants.some((p) => ADMITTER_ROLES.has(p.role)) : snapshot !== null && isWaitingPhase(snapshot.phase) ? snapshot.waiting?.admittersPresent ?? null : null,
|
|
5887
6220
|
lobbyCount: canAdmit && snapshot !== null ? snapshot.lobby.filter((entry) => entry.state === "knocking").length : 0,
|
|
5888
6221
|
recording: snapshot?.recording === "recording" || snapshot?.recording === "stopping",
|
|
5889
6222
|
audioBlocked: null,
|
|
@@ -6684,7 +7017,7 @@ function useRoom() {
|
|
|
6684
7017
|
(participant) => participant.identity === snapshot.localIdentity
|
|
6685
7018
|
) ?? null;
|
|
6686
7019
|
return {
|
|
6687
|
-
phase: snapshot?.phase ?? "
|
|
7020
|
+
phase: snapshot?.phase ?? "resolving",
|
|
6688
7021
|
participants: snapshot?.participants ?? [],
|
|
6689
7022
|
failure: snapshot?.failure ?? null,
|
|
6690
7023
|
local,
|
|
@@ -8035,7 +8368,7 @@ function useSharedFocus(panelKey) {
|
|
|
8035
8368
|
const snapshot = useMeetSnapshot();
|
|
8036
8369
|
const focus = snapshot?.collab.focus ?? null;
|
|
8037
8370
|
const mine = focus !== null && focus.panel === panelKey ? focus : null;
|
|
8038
|
-
const canMove = !isObserverRole(snapshot?.grants.role) && snapshot?.phase === "
|
|
8371
|
+
const canMove = !isObserverRole(snapshot?.grants.role) && snapshot?.phase === "in_call";
|
|
8039
8372
|
return {
|
|
8040
8373
|
section: mine?.section || null,
|
|
8041
8374
|
byName: mine?.byName || null,
|
|
@@ -8111,6 +8444,36 @@ function useCollabPanelRequest(onRequest) {
|
|
|
8111
8444
|
var MEET_REACTIONS = ["\u{1F496}", "\u{1F44D}", "\u{1F389}", "\u{1F44F}", "\u{1F602}", "\u{1F62E}", "\u{1F622}", "\u{1F914}", "\u{1F44E}"];
|
|
8112
8445
|
|
|
8113
8446
|
// src/react/facade.ts
|
|
8447
|
+
var NO_ACTIONS = {
|
|
8448
|
+
join: async () => void 0,
|
|
8449
|
+
rejoin: async () => void 0,
|
|
8450
|
+
leave: async () => void 0,
|
|
8451
|
+
backToPrejoin: () => void 0,
|
|
8452
|
+
retry: () => void 0,
|
|
8453
|
+
resolved: () => void 0,
|
|
8454
|
+
dismissShareNotice: () => void 0,
|
|
8455
|
+
dismissCall: () => void 0
|
|
8456
|
+
};
|
|
8457
|
+
function actionsFor(host) {
|
|
8458
|
+
if (host === null) return NO_ACTIONS;
|
|
8459
|
+
const { engine, store } = host;
|
|
8460
|
+
return {
|
|
8461
|
+
join: (request) => engine.join(request),
|
|
8462
|
+
rejoin: () => engine.rejoin(),
|
|
8463
|
+
leave: () => engine.leave(),
|
|
8464
|
+
backToPrejoin: () => engine.backToPrejoin(),
|
|
8465
|
+
retry: () => {
|
|
8466
|
+
store.dispatch({ type: "retry", click: true });
|
|
8467
|
+
},
|
|
8468
|
+
resolved: () => {
|
|
8469
|
+
store.dispatch({ type: "resolved" });
|
|
8470
|
+
},
|
|
8471
|
+
dismissShareNotice: () => engine.dismissShareNotice(),
|
|
8472
|
+
dismissCall: () => {
|
|
8473
|
+
if (store.dispatch({ type: "reset" })) store.patch({ call: null });
|
|
8474
|
+
}
|
|
8475
|
+
};
|
|
8476
|
+
}
|
|
8114
8477
|
function useMeetCore() {
|
|
8115
8478
|
const host = useMeetHost();
|
|
8116
8479
|
const snapshot = useMeetSnapshot();
|
|
@@ -8118,9 +8481,24 @@ function useMeetCore() {
|
|
|
8118
8481
|
const role = useRole();
|
|
8119
8482
|
const isHost = useIsHost();
|
|
8120
8483
|
const observation = useCoreObservation();
|
|
8484
|
+
const actions = useMemo5(() => actionsFor(host), [host]);
|
|
8121
8485
|
return useMemo5(
|
|
8122
|
-
() => ({
|
|
8123
|
-
|
|
8486
|
+
() => ({
|
|
8487
|
+
host,
|
|
8488
|
+
snapshot,
|
|
8489
|
+
devices,
|
|
8490
|
+
role,
|
|
8491
|
+
isHost,
|
|
8492
|
+
observation,
|
|
8493
|
+
phase: snapshot?.phase ?? "resolving",
|
|
8494
|
+
reason: snapshot?.reason ?? null,
|
|
8495
|
+
phaseNotice: snapshot?.phaseNotice ?? null,
|
|
8496
|
+
connection: snapshot?.connection ?? "stable",
|
|
8497
|
+
share: snapshot?.share ?? NO_SHARE,
|
|
8498
|
+
call: snapshot?.call ?? null,
|
|
8499
|
+
actions
|
|
8500
|
+
}),
|
|
8501
|
+
[host, snapshot, devices, role, isHost, observation, actions]
|
|
8124
8502
|
);
|
|
8125
8503
|
}
|
|
8126
8504
|
|
|
@@ -11051,11 +11429,52 @@ function MeetSkinRoom({
|
|
|
11051
11429
|
headerControls,
|
|
11052
11430
|
observe: observe2 = false
|
|
11053
11431
|
}) {
|
|
11054
|
-
const
|
|
11432
|
+
const core = useMeetCore();
|
|
11433
|
+
const { host, snapshot, isHost, phase } = core;
|
|
11055
11434
|
const elapsed = useElapsed(snapshot?.meeting?.startedAt ?? null);
|
|
11056
|
-
const phase = snapshot?.phase ?? "idle";
|
|
11057
11435
|
if (host === null) return null;
|
|
11058
|
-
|
|
11436
|
+
const screen = (() => {
|
|
11437
|
+
if (isRefusedPhase(phase)) return "refused";
|
|
11438
|
+
switch (phase) {
|
|
11439
|
+
case "resolving":
|
|
11440
|
+
return "resolving";
|
|
11441
|
+
case "prejoin":
|
|
11442
|
+
case "guest_name":
|
|
11443
|
+
return "prejoin";
|
|
11444
|
+
case "joining":
|
|
11445
|
+
case "in_call":
|
|
11446
|
+
return "room";
|
|
11447
|
+
case "waiting:host_not_started":
|
|
11448
|
+
case "waiting:knocking":
|
|
11449
|
+
case "denied":
|
|
11450
|
+
case "knock_expired":
|
|
11451
|
+
return "waiting";
|
|
11452
|
+
case "left":
|
|
11453
|
+
return snapshot?.waiting?.admission === "withdrawn" ? "waiting" : "exit";
|
|
11454
|
+
case "disconnected":
|
|
11455
|
+
case "removed":
|
|
11456
|
+
case "superseded":
|
|
11457
|
+
case "inactive":
|
|
11458
|
+
case "unsupported":
|
|
11459
|
+
case "ended":
|
|
11460
|
+
case "record":
|
|
11461
|
+
case "ringing_out":
|
|
11462
|
+
case "ringing_in":
|
|
11463
|
+
case "call_declined":
|
|
11464
|
+
case "call_missed":
|
|
11465
|
+
case "call_busy":
|
|
11466
|
+
case "answered_elsewhere":
|
|
11467
|
+
return "exit";
|
|
11468
|
+
default: {
|
|
11469
|
+
const unhandled = phase;
|
|
11470
|
+
return unhandled;
|
|
11471
|
+
}
|
|
11472
|
+
}
|
|
11473
|
+
})();
|
|
11474
|
+
if (screen === "resolving") {
|
|
11475
|
+
return /* @__PURE__ */ jsx6("div", { className: "mx-meet", children: /* @__PURE__ */ jsx6("p", { className: "mx-meet__empty-body", role: "status", children: "Getting the meeting ready\u2026" }) });
|
|
11476
|
+
}
|
|
11477
|
+
if (screen === "prejoin") {
|
|
11059
11478
|
return /* @__PURE__ */ jsx6("div", { className: "mx-meet", children: /* @__PURE__ */ jsx6(
|
|
11060
11479
|
PreJoin,
|
|
11061
11480
|
{
|
|
@@ -11066,9 +11485,12 @@ function MeetSkinRoom({
|
|
|
11066
11485
|
}
|
|
11067
11486
|
) });
|
|
11068
11487
|
}
|
|
11069
|
-
if (
|
|
11488
|
+
if (screen === "waiting") {
|
|
11070
11489
|
return /* @__PURE__ */ jsx6("div", { className: "mx-meet mx-meet--lobby", children: /* @__PURE__ */ jsx6(WaitingRoom, { host, ...onLeave !== void 0 ? { onLeave } : {} }) });
|
|
11071
11490
|
}
|
|
11491
|
+
if (screen === "refused" || screen === "exit") {
|
|
11492
|
+
return /* @__PURE__ */ jsx6("div", { className: "mx-meet mx-meet--lobby", children: /* @__PURE__ */ jsx6(ExitScreen, { ...onLeave !== void 0 ? { onLeave } : {} }) });
|
|
11493
|
+
}
|
|
11072
11494
|
return /* @__PURE__ */ jsxs3("div", { className: "mx-meet", children: [
|
|
11073
11495
|
/* @__PURE__ */ jsx6(ConsentNotice, {}),
|
|
11074
11496
|
/* @__PURE__ */ jsx6(AttendanceNotice, {}),
|
|
@@ -11088,14 +11510,148 @@ function MeetSkinRoom({
|
|
|
11088
11510
|
headerControls !== void 0 && headerControls !== null ? /* @__PURE__ */ jsx6("span", { className: "mx-meet__header-controls", "data-meet-header-controls": "", children: headerControls }) : null,
|
|
11089
11511
|
isHost ? /* @__PURE__ */ jsx6(HostMenu, {}) : null
|
|
11090
11512
|
] }),
|
|
11091
|
-
|
|
11513
|
+
core.connection !== "stable" ? /* @__PURE__ */ jsx6("p", { className: "mx-meet__banner", role: "status", children: "Reconnecting\u2026" }) : null,
|
|
11514
|
+
/* @__PURE__ */ jsx6(ShareNotice, {}),
|
|
11092
11515
|
/* @__PURE__ */ jsx6(Stage, {}),
|
|
11093
11516
|
/* @__PURE__ */ jsx6(Captions, {}),
|
|
11094
11517
|
/* @__PURE__ */ jsx6(ControlBar, { ...onLeave !== void 0 ? { onLeave } : {} })
|
|
11095
11518
|
] });
|
|
11096
11519
|
}
|
|
11520
|
+
function ShareNotice() {
|
|
11521
|
+
const { share, actions } = useMeetCore();
|
|
11522
|
+
if (share.state !== "refused") return null;
|
|
11523
|
+
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";
|
|
11524
|
+
return /* @__PURE__ */ jsxs3("p", { className: "mx-meet__banner", role: "status", "data-meet-notice": "share-unavailable", children: [
|
|
11525
|
+
text3,
|
|
11526
|
+
share.remedy !== null ? /* @__PURE__ */ jsx6("span", { className: "mx-meet__remedy", children: share.remedy }) : null,
|
|
11527
|
+
/* @__PURE__ */ jsx6("button", { type: "button", className: "mx-meet__link", onClick: actions.dismissShareNotice, children: "Dismiss" })
|
|
11528
|
+
] });
|
|
11529
|
+
}
|
|
11530
|
+
var REFUSED_TITLES = {
|
|
11531
|
+
not_found: "Check your meeting code",
|
|
11532
|
+
cancelled: "This meeting was cancelled",
|
|
11533
|
+
expired: "This meeting link has expired",
|
|
11534
|
+
too_early: "This meeting hasn't started yet",
|
|
11535
|
+
sign_in_required: "Sign in to join this meeting",
|
|
11536
|
+
wrong_account: "You're signed in with a different account",
|
|
11537
|
+
not_invited: "You aren't invited to this meeting",
|
|
11538
|
+
locked: "This meeting is locked",
|
|
11539
|
+
removed: "You can't rejoin this meeting",
|
|
11540
|
+
lobby_rejects: "You can't ask to join this meeting",
|
|
11541
|
+
meeting_full: "This meeting is full",
|
|
11542
|
+
platform_capacity: "Meetings are busy right now",
|
|
11543
|
+
verification_required: "Verify your account to join",
|
|
11544
|
+
registration_required: "Register to join this meeting"
|
|
11545
|
+
};
|
|
11546
|
+
function ExitScreen({ onLeave }) {
|
|
11547
|
+
const { phase, reason, host, snapshot, actions, call } = useMeetCore();
|
|
11548
|
+
const [busy, setBusy] = useState4(false);
|
|
11549
|
+
const failure = snapshot?.failure ?? null;
|
|
11550
|
+
const rejoin = /* @__PURE__ */ jsx6(
|
|
11551
|
+
Button,
|
|
11552
|
+
{
|
|
11553
|
+
type: "button",
|
|
11554
|
+
disabled: busy,
|
|
11555
|
+
onClick: () => {
|
|
11556
|
+
setBusy(true);
|
|
11557
|
+
void actions.rejoin().catch(() => void 0).finally(() => setBusy(false));
|
|
11558
|
+
},
|
|
11559
|
+
children: "Rejoin"
|
|
11560
|
+
}
|
|
11561
|
+
);
|
|
11562
|
+
const home = /* @__PURE__ */ jsx6(
|
|
11563
|
+
"a",
|
|
11564
|
+
{
|
|
11565
|
+
className: "mx-meet__link",
|
|
11566
|
+
href: "/",
|
|
11567
|
+
onClick: (event) => {
|
|
11568
|
+
if (call !== null) {
|
|
11569
|
+
event.preventDefault();
|
|
11570
|
+
actions.dismissCall();
|
|
11571
|
+
}
|
|
11572
|
+
if (onLeave !== void 0) {
|
|
11573
|
+
event.preventDefault();
|
|
11574
|
+
onLeave();
|
|
11575
|
+
}
|
|
11576
|
+
},
|
|
11577
|
+
children: "Go home"
|
|
11578
|
+
}
|
|
11579
|
+
);
|
|
11580
|
+
let title;
|
|
11581
|
+
let line = null;
|
|
11582
|
+
let notice = null;
|
|
11583
|
+
let actionsNode = home;
|
|
11584
|
+
if (phase.startsWith("refused:")) {
|
|
11585
|
+
const code = phase.slice("refused:".length);
|
|
11586
|
+
title = REFUSED_TITLES[code] ?? "You can't join this meeting";
|
|
11587
|
+
line = failure?.remedy ?? null;
|
|
11588
|
+
actionsNode = /* @__PURE__ */ jsxs3(Fragment, { children: [
|
|
11589
|
+
/* @__PURE__ */ jsx6(Button, { type: "button", variant: "outline", onClick: actions.retry, children: "Try again" }),
|
|
11590
|
+
home
|
|
11591
|
+
] });
|
|
11592
|
+
} else {
|
|
11593
|
+
switch (phase) {
|
|
11594
|
+
case "left":
|
|
11595
|
+
title = "You left the meeting";
|
|
11596
|
+
actionsNode = /* @__PURE__ */ jsxs3(Fragment, { children: [
|
|
11597
|
+
rejoin,
|
|
11598
|
+
home
|
|
11599
|
+
] });
|
|
11600
|
+
break;
|
|
11601
|
+
case "disconnected":
|
|
11602
|
+
title = "You lost your connection to the meeting";
|
|
11603
|
+
line = failure?.remedy ?? "Check your internet connection, then rejoin.";
|
|
11604
|
+
notice = "disconnected";
|
|
11605
|
+
actionsNode = /* @__PURE__ */ jsxs3(Fragment, { children: [
|
|
11606
|
+
rejoin,
|
|
11607
|
+
home
|
|
11608
|
+
] });
|
|
11609
|
+
break;
|
|
11610
|
+
case "removed":
|
|
11611
|
+
title = "You've been removed from the meeting";
|
|
11612
|
+
notice = "removed";
|
|
11613
|
+
break;
|
|
11614
|
+
case "superseded":
|
|
11615
|
+
title = "You joined from another tab or device";
|
|
11616
|
+
line = "This screen left the meeting so you aren't in it twice.";
|
|
11617
|
+
notice = "displaced";
|
|
11618
|
+
actionsNode = /* @__PURE__ */ jsxs3(Fragment, { children: [
|
|
11619
|
+
rejoin,
|
|
11620
|
+
home
|
|
11621
|
+
] });
|
|
11622
|
+
break;
|
|
11623
|
+
case "inactive":
|
|
11624
|
+
title = "You left the meeting because this tab was inactive";
|
|
11625
|
+
actionsNode = /* @__PURE__ */ jsxs3(Fragment, { children: [
|
|
11626
|
+
rejoin,
|
|
11627
|
+
home
|
|
11628
|
+
] });
|
|
11629
|
+
break;
|
|
11630
|
+
case "unsupported":
|
|
11631
|
+
title = "This browser can't join meetings";
|
|
11632
|
+
line = "Open the link in Chrome, Edge, Firefox or Safari.";
|
|
11633
|
+
break;
|
|
11634
|
+
case "ended":
|
|
11635
|
+
case "record":
|
|
11636
|
+
title = reason === "time_limit" ? "The meeting reached its time limit" : "The meeting has ended";
|
|
11637
|
+
notice = "meeting-ended";
|
|
11638
|
+
break;
|
|
11639
|
+
default:
|
|
11640
|
+
title = "The call has ended";
|
|
11641
|
+
break;
|
|
11642
|
+
}
|
|
11643
|
+
}
|
|
11644
|
+
return /* @__PURE__ */ jsxs3("div", { className: "mx-meet__waiting", "data-meet-exit": phase, children: [
|
|
11645
|
+
/* @__PURE__ */ jsx6(Avatar, { name: host?.identity.displayName ?? "", url: host?.identity.avatarUrl ?? null, size: 72 }),
|
|
11646
|
+
/* @__PURE__ */ jsx6("h1", { className: "mx-meet__prejoin-title", children: title }),
|
|
11647
|
+
line !== null ? /* @__PURE__ */ jsx6("p", { className: "mx-meet__empty-body", children: line }) : null,
|
|
11648
|
+
notice !== null ? /* @__PURE__ */ jsx6("span", { role: "status", "data-meet-notice": notice, className: "mx-meet__sr-only", children: title }) : null,
|
|
11649
|
+
/* @__PURE__ */ jsx6("div", { className: "mx-meet__waiting-actions", children: actionsNode })
|
|
11650
|
+
] });
|
|
11651
|
+
}
|
|
11097
11652
|
function WaitingRoom({ host, onLeave }) {
|
|
11098
11653
|
const { waiting, withdraw, askAgain } = useWaiting();
|
|
11654
|
+
const { phaseNotice } = useMeetCore();
|
|
11099
11655
|
const [busy, setBusy] = useState4(false);
|
|
11100
11656
|
const admission = waiting?.admission ?? "knocking";
|
|
11101
11657
|
const run = (action) => () => {
|
|
@@ -11144,6 +11700,7 @@ function WaitingRoom({ host, onLeave }) {
|
|
|
11144
11700
|
return /* @__PURE__ */ jsxs3("div", { className: "mx-meet__waiting", children: [
|
|
11145
11701
|
/* @__PURE__ */ jsx6(Avatar, { name: host.identity.displayName, url: host.identity.avatarUrl, size: 72 }),
|
|
11146
11702
|
/* @__PURE__ */ jsx6("h1", { className: "mx-meet__prejoin-title", children: title }),
|
|
11703
|
+
phaseNotice === "sent-to-waiting" ? /* @__PURE__ */ jsx6("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,
|
|
11147
11704
|
line !== null ? /* @__PURE__ */ jsx6("p", { className: "mx-meet__empty-body", role: "status", "data-meet-notice": "host-absent", children: line }) : null,
|
|
11148
11705
|
/* @__PURE__ */ jsx6("div", { className: "mx-meet__waiting-actions", children: actions })
|
|
11149
11706
|
] });
|
|
@@ -11638,10 +12195,17 @@ function MeetingSkinRootBody({
|
|
|
11638
12195
|
host
|
|
11639
12196
|
}) {
|
|
11640
12197
|
const snapshot = useMeetSnapshot();
|
|
11641
|
-
const phase = snapshot?.phase ?? "
|
|
12198
|
+
const phase = snapshot?.phase ?? "resolving";
|
|
11642
12199
|
useEffect8(() => {
|
|
11643
12200
|
host.meetings.watch(roomName);
|
|
11644
12201
|
}, [host, roomName]);
|
|
12202
|
+
useEffect8(() => {
|
|
12203
|
+
const now = host.store.snapshot();
|
|
12204
|
+
if (now.phase !== "resolving" && now.roomName !== null && now.roomName !== roomName && now.call === null) {
|
|
12205
|
+
host.store.dispatch({ type: "reset" });
|
|
12206
|
+
}
|
|
12207
|
+
if (host.store.snapshot().phase === "resolving") host.store.dispatch({ type: "resolved" });
|
|
12208
|
+
}, [host, roomName, phase]);
|
|
11645
12209
|
useEffect8(() => {
|
|
11646
12210
|
if (given === void 0) return;
|
|
11647
12211
|
if (host.store.snapshot().meeting !== null) return;
|
|
@@ -11653,7 +12217,7 @@ function MeetingSkinRootBody({
|
|
|
11653
12217
|
host.meetings.watchMeeting(asMeetingId(watchedId));
|
|
11654
12218
|
}, [host, watchedId]);
|
|
11655
12219
|
const record = snapshot?.meeting ?? given ?? null;
|
|
11656
|
-
if (hasEnded(record) && phase
|
|
12220
|
+
if (hasEnded(record) && !isLivePhase(phase)) {
|
|
11657
12221
|
return /* @__PURE__ */ jsx8(MeetingRecordView, { meeting: record });
|
|
11658
12222
|
}
|
|
11659
12223
|
return /* @__PURE__ */ jsx8(
|
|
@@ -11670,11 +12234,49 @@ function MeetingSkinRootBody({
|
|
|
11670
12234
|
}
|
|
11671
12235
|
|
|
11672
12236
|
// src/react/calls-ui.tsx
|
|
11673
|
-
import {
|
|
12237
|
+
import { useEffect as useEffect9 } from "react";
|
|
12238
|
+
import { Fragment as Fragment3, jsx as jsx9, jsxs as jsxs5 } from "react/jsx-runtime";
|
|
11674
12239
|
function IncomingCallHost() {
|
|
11675
12240
|
const calls = useCalls();
|
|
11676
|
-
|
|
11677
|
-
|
|
12241
|
+
const { call, phase, actions } = useMeetCore();
|
|
12242
|
+
useEffect9(() => {
|
|
12243
|
+
if (call !== null && phase === "left") actions.dismissCall();
|
|
12244
|
+
}, [call, phase, actions]);
|
|
12245
|
+
const outcome = call !== null && (phase === "call_declined" || phase === "call_missed" || phase === "call_busy" || phase === "answered_elsewhere");
|
|
12246
|
+
useEffect9(() => {
|
|
12247
|
+
if (!outcome) return void 0;
|
|
12248
|
+
const timer = setTimeout(() => actions.dismissCall(), 5e3);
|
|
12249
|
+
return () => clearTimeout(timer);
|
|
12250
|
+
}, [outcome, actions]);
|
|
12251
|
+
const inCall = call !== null && (phase === "joining" || phase === "in_call" || phase === "disconnected" || phase === "removed" || phase === "superseded");
|
|
12252
|
+
return /* @__PURE__ */ jsxs5(Fragment3, { children: [
|
|
12253
|
+
inCall && call !== null ? /* @__PURE__ */ jsx9("div", { className: "mx-meet__call-overlay", "data-meet-call": call.invite.id, children: /* @__PURE__ */ jsx9(MeetRoot, { children: /* @__PURE__ */ jsx9(MeetSkinRoom, { roomName: call.invite.roomName, onLeave: actions.dismissCall }) }) }) : null,
|
|
12254
|
+
calls.incoming.length > 0 || call !== null && (phase === "ringing_out" || outcome) ? /* @__PURE__ */ jsxs5("div", { className: "mx-meet__ringing-stack", children: [
|
|
12255
|
+
call !== null && phase === "ringing_out" ? /* @__PURE__ */ jsx9(OutgoingCallCard, { invite: call.invite }) : null,
|
|
12256
|
+
outcome && call !== null ? /* @__PURE__ */ jsx9(CallOutcomeCard, { invite: call.invite }) : null,
|
|
12257
|
+
calls.incoming.map((invite) => /* @__PURE__ */ jsx9(IncomingCallCard, { invite }, invite.id))
|
|
12258
|
+
] }) : null
|
|
12259
|
+
] });
|
|
12260
|
+
}
|
|
12261
|
+
function OutgoingCallCard({ invite }) {
|
|
12262
|
+
const calls = useCalls();
|
|
12263
|
+
return /* @__PURE__ */ jsxs5("section", { className: "mx-meet__ringing", role: "status", "aria-label": "Calling", "data-meet-notice": "ringing-out", children: [
|
|
12264
|
+
/* @__PURE__ */ jsx9(Avatar, { name: invite.callerName, url: invite.callerAvatarUrl, size: 56 }),
|
|
12265
|
+
/* @__PURE__ */ jsxs5("div", { className: "mx-meet__ringing-text", children: [
|
|
12266
|
+
/* @__PURE__ */ jsx9("strong", { children: "Calling\u2026" }),
|
|
12267
|
+
/* @__PURE__ */ jsx9("span", { children: invite.mode === "video" ? "Video call" : "Voice call" })
|
|
12268
|
+
] }),
|
|
12269
|
+
/* @__PURE__ */ jsx9("div", { className: "mx-meet__ringing-actions", children: /* @__PURE__ */ jsx9(ControlButton, { label: "Cancel call", danger: true, onClick: () => void calls.cancel(invite), children: /* @__PURE__ */ jsx9(PhoneOffIcon, {}) }) })
|
|
12270
|
+
] });
|
|
12271
|
+
}
|
|
12272
|
+
function CallOutcomeCard({ invite }) {
|
|
12273
|
+
const { phase, actions, call } = useMeetCore();
|
|
12274
|
+
const outgoing = call?.direction === "outgoing";
|
|
12275
|
+
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}`;
|
|
12276
|
+
return /* @__PURE__ */ jsxs5("section", { className: "mx-meet__ringing", role: "status", "aria-label": text3, "data-meet-call-outcome": phase, children: [
|
|
12277
|
+
/* @__PURE__ */ jsx9("div", { className: "mx-meet__ringing-text", children: /* @__PURE__ */ jsx9("strong", { children: text3 }) }),
|
|
12278
|
+
/* @__PURE__ */ jsx9("div", { className: "mx-meet__ringing-actions", children: /* @__PURE__ */ jsx9(ControlButton, { label: "Close", onClick: actions.dismissCall, children: /* @__PURE__ */ jsx9(PhoneOffIcon, {}) }) })
|
|
12279
|
+
] });
|
|
11678
12280
|
}
|
|
11679
12281
|
function IncomingCallCard({ invite }) {
|
|
11680
12282
|
const calls = useCalls();
|
|
@@ -11849,28 +12451,6 @@ async function pushMeetingToGoogleCalendar(api, meetingId, target) {
|
|
|
11849
12451
|
if (!response.ok) throw await refusal(response, operation);
|
|
11850
12452
|
return await response.json();
|
|
11851
12453
|
}
|
|
11852
|
-
|
|
11853
|
-
// src/core/reasons.ts
|
|
11854
|
-
var JOIN_REFUSAL_REASONS = [
|
|
11855
|
-
"not_found",
|
|
11856
|
-
"cancelled",
|
|
11857
|
-
"ended",
|
|
11858
|
-
"expired",
|
|
11859
|
-
"too_early",
|
|
11860
|
-
"sign_in_required",
|
|
11861
|
-
"wrong_account",
|
|
11862
|
-
"not_invited",
|
|
11863
|
-
"locked",
|
|
11864
|
-
"removed",
|
|
11865
|
-
"lobby_rejects",
|
|
11866
|
-
"meeting_full",
|
|
11867
|
-
"platform_capacity",
|
|
11868
|
-
"verification_required",
|
|
11869
|
-
"registration_required"
|
|
11870
|
-
];
|
|
11871
|
-
function isJoinRefusalReason(value) {
|
|
11872
|
-
return typeof value === "string" && JOIN_REFUSAL_REASONS.includes(value);
|
|
11873
|
-
}
|
|
11874
12454
|
export {
|
|
11875
12455
|
ATTENDANCE_CSV_HEADER,
|
|
11876
12456
|
CALL_INVITE_KIND,
|
|
@@ -11889,11 +12469,13 @@ export {
|
|
|
11889
12469
|
GUEST_RECORD_NOT_SHARED,
|
|
11890
12470
|
GuestRecordAccessControl,
|
|
11891
12471
|
IDLE_MEDIA,
|
|
12472
|
+
IDLE_PHASES,
|
|
11892
12473
|
IDLE_TRACK,
|
|
11893
12474
|
IncomingCallCard,
|
|
11894
12475
|
IncomingCallHost,
|
|
11895
12476
|
JOIN_REFUSAL_REASONS,
|
|
11896
12477
|
LISTEN_ONLY_NOTICE,
|
|
12478
|
+
LIVE_PHASES,
|
|
11897
12479
|
MEETING_INVITE_KIND,
|
|
11898
12480
|
MEETING_INVITE_VERSION,
|
|
11899
12481
|
MEETING_PASS_HEADER,
|
|
@@ -11915,6 +12497,7 @@ export {
|
|
|
11915
12497
|
NOTE_TAKER_IDENTITY_PREFIX,
|
|
11916
12498
|
NOTE_TAKER_METADATA_KEY,
|
|
11917
12499
|
NO_GRANTS,
|
|
12500
|
+
NO_SHARE,
|
|
11918
12501
|
ORGANIZATION_HEADER,
|
|
11919
12502
|
PARTICIPANT_CAN_RECORD_ATTRIBUTE,
|
|
11920
12503
|
PARTICIPANT_ROLE_ATTRIBUTE,
|
|
@@ -11929,9 +12512,11 @@ export {
|
|
|
11929
12512
|
TABLES,
|
|
11930
12513
|
TRANSCRIPTION_ATTRIBUTES,
|
|
11931
12514
|
TRANSCRIPTION_TOPIC,
|
|
12515
|
+
WAITING_PHASES,
|
|
11932
12516
|
absenceSentence,
|
|
11933
12517
|
activeWrapUpSummary,
|
|
11934
12518
|
activityAttention,
|
|
12519
|
+
admissionPhase,
|
|
11935
12520
|
announceMeeting,
|
|
11936
12521
|
appPanelOf,
|
|
11937
12522
|
applyCollabPacket,
|
|
@@ -12001,10 +12586,13 @@ export {
|
|
|
12001
12586
|
isJoinRefusalReason,
|
|
12002
12587
|
isLinkFollower,
|
|
12003
12588
|
isListenOnly,
|
|
12589
|
+
isLivePhase,
|
|
12004
12590
|
isNoteTaker,
|
|
12005
12591
|
isObserverRole,
|
|
12006
12592
|
isRealName,
|
|
12593
|
+
isRefusedPhase,
|
|
12007
12594
|
isRoomEventTopic,
|
|
12595
|
+
isWaitingPhase,
|
|
12008
12596
|
learnName,
|
|
12009
12597
|
learnNames,
|
|
12010
12598
|
listenOnlyNotice,
|
|
@@ -12029,6 +12617,7 @@ export {
|
|
|
12029
12617
|
mergeRecord,
|
|
12030
12618
|
microphoneNoticeCode,
|
|
12031
12619
|
moveToRoom,
|
|
12620
|
+
nextPhase,
|
|
12032
12621
|
nextTranslationBatch,
|
|
12033
12622
|
normalizeDeviceError,
|
|
12034
12623
|
normalizeMeetError,
|
|
@@ -12061,10 +12650,13 @@ export {
|
|
|
12061
12650
|
pushMeetingToGoogleCalendar,
|
|
12062
12651
|
readServerRefusal,
|
|
12063
12652
|
readableTranscript,
|
|
12653
|
+
reasonOf,
|
|
12064
12654
|
recordingLabel,
|
|
12065
12655
|
recordingNotice,
|
|
12066
12656
|
recordingOffsetMs,
|
|
12657
|
+
refusalPhase,
|
|
12067
12658
|
rememberedMeetingPass,
|
|
12659
|
+
resolveExit,
|
|
12068
12660
|
roleOf,
|
|
12069
12661
|
roomTopic,
|
|
12070
12662
|
rootAttributes,
|