@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.
- package/CHANGELOG.md +12 -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 +403 -98
- 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 +403 -98
- package/dist/core.js.map +1 -1
- package/dist/index.cjs +403 -98
- 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 +403 -98
- package/dist/index.js.map +1 -1
- package/dist/react.cjs +696 -116
- 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 +697 -117
- 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 +2 -2
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. */
|
|
@@ -3004,8 +3213,15 @@ var NO_GRANTS = {
|
|
|
3004
3213
|
canPublish: false,
|
|
3005
3214
|
canSubscribe: false
|
|
3006
3215
|
};
|
|
3216
|
+
var NO_SHARE = { state: "none", reason: null, remedy: null, at: 0 };
|
|
3007
3217
|
var EMPTY = {
|
|
3008
|
-
phase: "
|
|
3218
|
+
phase: "resolving",
|
|
3219
|
+
reason: null,
|
|
3220
|
+
phaseNotice: null,
|
|
3221
|
+
roomName: null,
|
|
3222
|
+
connection: "stable",
|
|
3223
|
+
share: NO_SHARE,
|
|
3224
|
+
call: null,
|
|
3009
3225
|
waiting: null,
|
|
3010
3226
|
meeting: null,
|
|
3011
3227
|
localIdentity: null,
|
|
@@ -3069,8 +3285,20 @@ function createMeetStore(initial = {}) {
|
|
|
3069
3285
|
return () => listeners.delete(listener);
|
|
3070
3286
|
},
|
|
3071
3287
|
patch(next) {
|
|
3288
|
+
if ("phase" in next || "reason" in next || "phaseNotice" in next) {
|
|
3289
|
+
throw new TypeError("meet store: the phase moves only through store.dispatch(event)");
|
|
3290
|
+
}
|
|
3072
3291
|
commit({ ...state, ...next });
|
|
3073
3292
|
},
|
|
3293
|
+
dispatch(event, detail) {
|
|
3294
|
+
const to = nextPhase(state.phase, event);
|
|
3295
|
+
if (to === null) return false;
|
|
3296
|
+
const reason = detail?.reason !== void 0 ? detail.reason : reasonOf(to);
|
|
3297
|
+
const phaseNotice = detail?.notice ?? null;
|
|
3298
|
+
if (to === state.phase && reason === state.reason && phaseNotice === state.phaseNotice) return true;
|
|
3299
|
+
commit({ ...state, phase: to, reason, phaseNotice });
|
|
3300
|
+
return true;
|
|
3301
|
+
},
|
|
3074
3302
|
setParticipants(participants) {
|
|
3075
3303
|
const names = learnNames(state.names, participants);
|
|
3076
3304
|
const hands = new Map(state.participants.map((p) => [p.identity, p.handRaised]));
|
|
@@ -3388,7 +3616,7 @@ function createMeetingSession(options) {
|
|
|
3388
3616
|
}
|
|
3389
3617
|
function apply(read) {
|
|
3390
3618
|
const snapshot = store.snapshot();
|
|
3391
|
-
if (snapshot.phase
|
|
3619
|
+
if (!isWaitingPhase(snapshot.phase)) return;
|
|
3392
3620
|
const admission = read.admission_state ?? null;
|
|
3393
3621
|
const ended = admission === "ended" || read.session_phase === "ended" || read.meeting_phase === "ended";
|
|
3394
3622
|
if (admission === "admitted" && !ended) {
|
|
@@ -3405,6 +3633,7 @@ function createMeetingSession(options) {
|
|
|
3405
3633
|
if (read.meeting_phase === "ended" && meeting !== null && meeting.endedAt === null) {
|
|
3406
3634
|
store.patch({ meeting: { ...meeting, endedAt: (/* @__PURE__ */ new Date()).toISOString() } });
|
|
3407
3635
|
}
|
|
3636
|
+
store.dispatch({ type: "admission", admission: "ended" }, { reason: "ended" });
|
|
3408
3637
|
return;
|
|
3409
3638
|
}
|
|
3410
3639
|
if (admission === null) return;
|
|
@@ -3416,6 +3645,7 @@ function createMeetingSession(options) {
|
|
|
3416
3645
|
pollSeconds: read.poll_seconds ?? snapshot.waiting?.pollSeconds ?? 3,
|
|
3417
3646
|
liveVersion: read.live_version ?? 0
|
|
3418
3647
|
});
|
|
3648
|
+
store.dispatch({ type: "admission", admission });
|
|
3419
3649
|
if (admission === "denied" || admission === "expired" || admission === "withdrawn" || admission === "removed") {
|
|
3420
3650
|
stopWaiting();
|
|
3421
3651
|
}
|
|
@@ -3511,7 +3741,7 @@ function createMeetingSession(options) {
|
|
|
3511
3741
|
// 🚨 PAYLOAD-FREE. Every receiver reads again; a spoofed hint only causes a read.
|
|
3512
3742
|
event: MEET_EVENTS.stateChanged,
|
|
3513
3743
|
onMessage: () => {
|
|
3514
|
-
if (store.snapshot().phase
|
|
3744
|
+
if (isWaitingPhase(store.snapshot().phase)) void readState2();
|
|
3515
3745
|
void refreshQueue();
|
|
3516
3746
|
}
|
|
3517
3747
|
}
|
|
@@ -3523,7 +3753,7 @@ function createMeetingSession(options) {
|
|
|
3523
3753
|
if (meeting === null) return;
|
|
3524
3754
|
const fresh = isGuest(identity) ? await repository.meetingBySlug(meeting.slug) : await readMeeting(meeting.id);
|
|
3525
3755
|
store.patch({ meeting: fresh, locked: fresh.locked });
|
|
3526
|
-
if (store.snapshot().phase
|
|
3756
|
+
if (isWaitingPhase(store.snapshot().phase)) void readState2();
|
|
3527
3757
|
}
|
|
3528
3758
|
});
|
|
3529
3759
|
},
|
|
@@ -3690,10 +3920,34 @@ function createMeetingSession(options) {
|
|
|
3690
3920
|
},
|
|
3691
3921
|
stopWaiting,
|
|
3692
3922
|
readState: readState2,
|
|
3923
|
+
async readExit(connection) {
|
|
3924
|
+
const meeting = store.snapshot().meeting;
|
|
3925
|
+
const pass = options.resolvePass?.() ?? null;
|
|
3926
|
+
if (meeting === null || pass === null) return void 0;
|
|
3927
|
+
try {
|
|
3928
|
+
const response = await options.api.request({
|
|
3929
|
+
path: `${MEET_ROUTES.state}?meeting_id=${encodeURIComponent(meeting.id)}&connection=${encodeURIComponent(connection)}`,
|
|
3930
|
+
method: "GET",
|
|
3931
|
+
requireAuth: false,
|
|
3932
|
+
operation: "meet.state(exit)",
|
|
3933
|
+
headers: { [MEETING_PASS_HEADER]: pass }
|
|
3934
|
+
});
|
|
3935
|
+
if (!response.ok) return null;
|
|
3936
|
+
const read = await response.json();
|
|
3937
|
+
return {
|
|
3938
|
+
exitReason: read.exit_reason ?? null,
|
|
3939
|
+
sessionPhase: read.session_phase ?? null,
|
|
3940
|
+
meetingPhase: read.meeting_phase ?? null
|
|
3941
|
+
};
|
|
3942
|
+
} catch {
|
|
3943
|
+
return null;
|
|
3944
|
+
}
|
|
3945
|
+
},
|
|
3693
3946
|
async withdraw() {
|
|
3694
3947
|
stopWaiting();
|
|
3695
3948
|
await postLeave("withdraw");
|
|
3696
3949
|
patchWaiting({ admission: "withdrawn" });
|
|
3950
|
+
store.dispatch({ type: "admission", admission: "withdrawn" });
|
|
3697
3951
|
hint();
|
|
3698
3952
|
},
|
|
3699
3953
|
async askAgain() {
|
|
@@ -4177,6 +4431,9 @@ var TRANSCRIPTION_ATTRIBUTES = {
|
|
|
4177
4431
|
};
|
|
4178
4432
|
|
|
4179
4433
|
// src/core/room.ts
|
|
4434
|
+
function SCREEN_SHARE_BLOCKED_REMEDY_FOR(remedy) {
|
|
4435
|
+
return /administrator|policy/i.test(remedy) ? remedy : SCREEN_SHARE_BLOCKED_REMEDY;
|
|
4436
|
+
}
|
|
4180
4437
|
var PARTICIPANT_ROLE_ATTRIBUTE = "matrx.participant.role";
|
|
4181
4438
|
var PARTICIPANT_CAN_RECORD_ATTRIBUTE = "matrx.participant.can_record";
|
|
4182
4439
|
var PARTICIPANT_STANDING_ATTRIBUTE = "matrx.participant.standing";
|
|
@@ -4244,7 +4501,7 @@ function createRoomEngine(options) {
|
|
|
4244
4501
|
let disposed = false;
|
|
4245
4502
|
let moving = false;
|
|
4246
4503
|
const confirmingEnds = /* @__PURE__ */ new WeakSet();
|
|
4247
|
-
async function
|
|
4504
|
+
async function dropRoom() {
|
|
4248
4505
|
const target = driver;
|
|
4249
4506
|
driver = null;
|
|
4250
4507
|
if (current !== null) {
|
|
@@ -4253,9 +4510,14 @@ function createRoomEngine(options) {
|
|
|
4253
4510
|
}
|
|
4254
4511
|
store.setLocalTrack("microphone", IDLE_TRACK);
|
|
4255
4512
|
store.setLocalTrack("camera", IDLE_TRACK);
|
|
4256
|
-
store.patch({
|
|
4513
|
+
store.patch({ share: NO_SHARE, connection: "stable" });
|
|
4257
4514
|
await target?.disconnect();
|
|
4258
4515
|
}
|
|
4516
|
+
async function leaveRoom() {
|
|
4517
|
+
store.dispatch({ type: "leave" });
|
|
4518
|
+
await dropRoom();
|
|
4519
|
+
options.onLeft?.(null);
|
|
4520
|
+
}
|
|
4259
4521
|
async function confirmMeetingEnd() {
|
|
4260
4522
|
const meeting = store.snapshot().meeting;
|
|
4261
4523
|
const target = driver;
|
|
@@ -4277,7 +4539,9 @@ function createRoomEngine(options) {
|
|
|
4277
4539
|
const latest = store.snapshot().meeting;
|
|
4278
4540
|
if (latest === null) return;
|
|
4279
4541
|
store.patch({ meeting: { ...latest, endedAt: verified.endedAt } });
|
|
4280
|
-
|
|
4542
|
+
store.dispatch({ type: "ended" }, { reason: "ended" });
|
|
4543
|
+
await dropRoom();
|
|
4544
|
+
options.onLeft?.("ended");
|
|
4281
4545
|
} catch (error) {
|
|
4282
4546
|
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
4547
|
} finally {
|
|
@@ -4289,12 +4553,12 @@ function createRoomEngine(options) {
|
|
|
4289
4553
|
options.onDiagnostic?.(remedy === void 0 ? { level, message } : { level, message, remedy });
|
|
4290
4554
|
}
|
|
4291
4555
|
let ambiguousShareDenials = 0;
|
|
4292
|
-
function
|
|
4556
|
+
function failJoin(error, operation) {
|
|
4293
4557
|
const normalized = normalizeMeetError(error, operation);
|
|
4294
4558
|
store.patch({
|
|
4295
|
-
phase: "failed",
|
|
4296
4559
|
failure: { message: normalized.message, remedy: normalized.remedy, reason: normalized.reason }
|
|
4297
4560
|
});
|
|
4561
|
+
store.dispatch({ type: "join_failed", reason: normalized.reason ?? null });
|
|
4298
4562
|
report(
|
|
4299
4563
|
normalized.code === "session-unavailable" ? "warn" : "error",
|
|
4300
4564
|
normalized.message,
|
|
@@ -4302,6 +4566,14 @@ function createRoomEngine(options) {
|
|
|
4302
4566
|
);
|
|
4303
4567
|
return normalized;
|
|
4304
4568
|
}
|
|
4569
|
+
async function lostRoom(error, operation) {
|
|
4570
|
+
const normalized = normalizeMeetError(error, operation);
|
|
4571
|
+
store.patch({ failure: { message: normalized.message, remedy: normalized.remedy, reason: normalized.reason } });
|
|
4572
|
+
await dropRoom();
|
|
4573
|
+
store.dispatch({ type: "exit", to: "disconnected" });
|
|
4574
|
+
report("error", normalized.message, normalized.remedy);
|
|
4575
|
+
return normalized;
|
|
4576
|
+
}
|
|
4305
4577
|
const handlers = {
|
|
4306
4578
|
participants(list) {
|
|
4307
4579
|
const joinedAt = new Date(now()).toISOString();
|
|
@@ -4344,9 +4616,12 @@ function createRoomEngine(options) {
|
|
|
4344
4616
|
store.setQuality(asParticipantIdentity(who), value);
|
|
4345
4617
|
},
|
|
4346
4618
|
connectionState(state) {
|
|
4347
|
-
if (state === "connected")
|
|
4348
|
-
|
|
4349
|
-
|
|
4619
|
+
if (state === "connected") {
|
|
4620
|
+
if (store.snapshot().connection !== "stable") store.patch({ connection: "stable" });
|
|
4621
|
+
store.dispatch({ type: "connected" });
|
|
4622
|
+
} else if (state === "reconnecting") {
|
|
4623
|
+
store.patch({ connection: "reconnecting" });
|
|
4624
|
+
}
|
|
4350
4625
|
},
|
|
4351
4626
|
transcription(event) {
|
|
4352
4627
|
const snapshot = store.snapshot();
|
|
@@ -4473,17 +4748,48 @@ function createRoomEngine(options) {
|
|
|
4473
4748
|
}
|
|
4474
4749
|
},
|
|
4475
4750
|
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);
|
|
4751
|
+
if (moving || driver === null) return;
|
|
4752
|
+
void resolveExitAndApply(reason);
|
|
4483
4753
|
}
|
|
4484
4754
|
};
|
|
4755
|
+
async function resolveExitAndApply(hint) {
|
|
4756
|
+
const connection = current?.identity ?? store.snapshot().localIdentity ?? null;
|
|
4757
|
+
await dropRoom();
|
|
4758
|
+
let server;
|
|
4759
|
+
if (options.readExit === void 0 || connection === null || store.snapshot().meeting === null) {
|
|
4760
|
+
server = void 0;
|
|
4761
|
+
} else {
|
|
4762
|
+
try {
|
|
4763
|
+
server = await options.readExit(connection);
|
|
4764
|
+
} catch {
|
|
4765
|
+
server = null;
|
|
4766
|
+
}
|
|
4767
|
+
}
|
|
4768
|
+
if (disposed) return;
|
|
4769
|
+
const resolved = resolveExit({ server, hint, frozen: options.wasFrozen?.() ?? false });
|
|
4770
|
+
store.patch({ connection: resolved.connection });
|
|
4771
|
+
if (resolved.to === "waiting:knocking") {
|
|
4772
|
+
store.patch({
|
|
4773
|
+
waiting: {
|
|
4774
|
+
admission: "knocking",
|
|
4775
|
+
admittersPresent: null,
|
|
4776
|
+
reknockAllowed: true,
|
|
4777
|
+
knockExpiresAt: null,
|
|
4778
|
+
pollSeconds: store.snapshot().waiting?.pollSeconds ?? 3,
|
|
4779
|
+
liveVersion: 0
|
|
4780
|
+
}
|
|
4781
|
+
});
|
|
4782
|
+
}
|
|
4783
|
+
store.dispatch({ type: "exit", to: resolved.to }, { reason: resolved.reason, notice: resolved.notice });
|
|
4784
|
+
report(
|
|
4785
|
+
resolved.to === "disconnected" ? "warn" : "info",
|
|
4786
|
+
`meet: the room connection ended (${hint ?? "no LiveKit reason"}) \u2192 ${resolved.to}`,
|
|
4787
|
+
resolved.to === "disconnected" ? "Press Rejoin; the meeting is still going." : "The screen says what happened."
|
|
4788
|
+
);
|
|
4789
|
+
options.onLeft?.(hint);
|
|
4790
|
+
}
|
|
4485
4791
|
async function connect(request) {
|
|
4486
|
-
store.patch({
|
|
4792
|
+
store.patch({ failure: null, waiting: null, roomName: request.roomName, share: NO_SHARE, connection: "stable" });
|
|
4487
4793
|
const token = await tokens.get({
|
|
4488
4794
|
roomName: request.roomName,
|
|
4489
4795
|
identity,
|
|
@@ -4506,10 +4812,10 @@ function createRoomEngine(options) {
|
|
|
4506
4812
|
}
|
|
4507
4813
|
});
|
|
4508
4814
|
if (token.lobby || !token.canSubscribe) {
|
|
4815
|
+
const admission = token.admissionState !== void 0 && token.admissionState !== "admitted" ? token.admissionState : "knocking";
|
|
4509
4816
|
store.patch({
|
|
4510
|
-
phase: "lobby",
|
|
4511
4817
|
waiting: {
|
|
4512
|
-
admission
|
|
4818
|
+
admission,
|
|
4513
4819
|
admittersPresent: null,
|
|
4514
4820
|
reknockAllowed: true,
|
|
4515
4821
|
knockExpiresAt: null,
|
|
@@ -4517,9 +4823,10 @@ function createRoomEngine(options) {
|
|
|
4517
4823
|
liveVersion: 0
|
|
4518
4824
|
}
|
|
4519
4825
|
});
|
|
4826
|
+
store.dispatch({ type: "admission", admission });
|
|
4520
4827
|
return;
|
|
4521
4828
|
}
|
|
4522
|
-
store.patch({
|
|
4829
|
+
store.patch({ waiting: null });
|
|
4523
4830
|
const created = await options.driverFactory(handlers);
|
|
4524
4831
|
if (disposed) {
|
|
4525
4832
|
await created.disconnect();
|
|
@@ -4527,7 +4834,8 @@ function createRoomEngine(options) {
|
|
|
4527
4834
|
}
|
|
4528
4835
|
driver = created;
|
|
4529
4836
|
await created.connect(token.serverUrl, token.token);
|
|
4530
|
-
store.patch({
|
|
4837
|
+
store.patch({ failure: null, connection: "stable" });
|
|
4838
|
+
store.dispatch({ type: "connected" });
|
|
4531
4839
|
store.setLocalTrack("microphone", IDLE_TRACK);
|
|
4532
4840
|
store.setLocalTrack("camera", IDLE_TRACK);
|
|
4533
4841
|
const effects = options.effectsStorage?.load() ?? store.snapshot().effects;
|
|
@@ -4598,9 +4906,17 @@ function createRoomEngine(options) {
|
|
|
4598
4906
|
return {
|
|
4599
4907
|
join(request) {
|
|
4600
4908
|
if (joining !== null) return joining;
|
|
4909
|
+
if (store.snapshot().phase !== "joining" && !store.dispatch({ type: "join", click: true })) {
|
|
4910
|
+
report(
|
|
4911
|
+
"warn",
|
|
4912
|
+
`meet.join: cannot join from "${store.snapshot().phase}"`,
|
|
4913
|
+
"Join is offered from the pre-join, left, disconnected and replaced screens only."
|
|
4914
|
+
);
|
|
4915
|
+
return Promise.resolve();
|
|
4916
|
+
}
|
|
4601
4917
|
lastRequest = request;
|
|
4602
4918
|
joining = connect(request).catch((error) => {
|
|
4603
|
-
throw
|
|
4919
|
+
throw failJoin(error, "meet.join");
|
|
4604
4920
|
}).finally(() => {
|
|
4605
4921
|
joining = null;
|
|
4606
4922
|
});
|
|
@@ -4617,10 +4933,16 @@ function createRoomEngine(options) {
|
|
|
4617
4933
|
return;
|
|
4618
4934
|
}
|
|
4619
4935
|
if (joining !== null) return joining;
|
|
4936
|
+
const phase = store.snapshot().phase;
|
|
4937
|
+
const moved = phase === "joining" || store.dispatch({ type: "join", click: true }) || store.dispatch({ type: "admitted" });
|
|
4938
|
+
if (!moved) {
|
|
4939
|
+
report("warn", `meet.rejoin: cannot rejoin from "${phase}"`, "Rejoin is offered after leaving, a lost connection, or admission.");
|
|
4940
|
+
return;
|
|
4941
|
+
}
|
|
4620
4942
|
tokens.invalidate(request.roomName);
|
|
4621
4943
|
current = null;
|
|
4622
4944
|
joining = connect(request).catch((error) => {
|
|
4623
|
-
throw
|
|
4945
|
+
throw failJoin(error, "meet.rejoin");
|
|
4624
4946
|
}).finally(() => {
|
|
4625
4947
|
joining = null;
|
|
4626
4948
|
});
|
|
@@ -4665,11 +4987,11 @@ function createRoomEngine(options) {
|
|
|
4665
4987
|
joining = connect(lastRequest).finally(() => {
|
|
4666
4988
|
joining = null;
|
|
4667
4989
|
});
|
|
4668
|
-
await joining.catch((again) => {
|
|
4669
|
-
throw
|
|
4990
|
+
await joining.catch(async (again) => {
|
|
4991
|
+
throw await lostRoom(again, "meet.moveToBreakout");
|
|
4670
4992
|
});
|
|
4671
4993
|
} else {
|
|
4672
|
-
throw
|
|
4994
|
+
throw await lostRoom(error, "meet.moveToBreakout");
|
|
4673
4995
|
}
|
|
4674
4996
|
throw normalized;
|
|
4675
4997
|
} finally {
|
|
@@ -4677,6 +4999,9 @@ function createRoomEngine(options) {
|
|
|
4677
4999
|
}
|
|
4678
5000
|
},
|
|
4679
5001
|
leave: leaveRoom,
|
|
5002
|
+
backToPrejoin() {
|
|
5003
|
+
store.dispatch({ type: "back_to_prejoin", click: true });
|
|
5004
|
+
},
|
|
4680
5005
|
// Turning a device on mid-meeting is the SAME operation as turning it on
|
|
4681
5006
|
// during the join, so it runs the same function. A failure here used to
|
|
4682
5007
|
// stamp the room `failed` — a busy microphone would blank a live meeting.
|
|
@@ -4690,21 +5015,41 @@ function createRoomEngine(options) {
|
|
|
4690
5015
|
},
|
|
4691
5016
|
async setScreenShareEnabled(enabled) {
|
|
4692
5017
|
if (driver === null) return;
|
|
5018
|
+
if (enabled && driver.supportsScreenShare?.() === false) {
|
|
5019
|
+
store.patch({ share: { state: "refused", reason: "unsupported", remedy: null, at: now() } });
|
|
5020
|
+
report("warn", "meet: this browser cannot share a screen", "Share from a desktop browser (Chrome, Edge, Firefox or Safari).");
|
|
5021
|
+
return;
|
|
5022
|
+
}
|
|
5023
|
+
if (enabled) store.patch({ share: { state: "picking", reason: null, remedy: null, at: now() } });
|
|
4693
5024
|
try {
|
|
4694
5025
|
await driver.setScreenShareEnabled(enabled);
|
|
4695
5026
|
ambiguousShareDenials = 0;
|
|
5027
|
+
store.patch({ share: { state: enabled ? "presenting" : "none", reason: null, remedy: null, at: now() } });
|
|
4696
5028
|
} catch (error) {
|
|
4697
5029
|
const kind = classifyScreenShareError(error);
|
|
4698
5030
|
if (kind === "ambiguous") {
|
|
4699
5031
|
ambiguousShareDenials += 1;
|
|
4700
|
-
if (ambiguousShareDenials < 2)
|
|
5032
|
+
if (ambiguousShareDenials < 2) {
|
|
5033
|
+
store.patch({ share: NO_SHARE });
|
|
5034
|
+
return;
|
|
5035
|
+
}
|
|
4701
5036
|
}
|
|
5037
|
+
ambiguousShareDenials = 0;
|
|
4702
5038
|
const normalized = normalizeScreenShareError(error, "meet.setScreenShareEnabled");
|
|
4703
5039
|
report("warn", normalized.message, normalized.remedy);
|
|
4704
|
-
|
|
4705
|
-
|
|
5040
|
+
store.patch({
|
|
5041
|
+
share: {
|
|
5042
|
+
state: "refused",
|
|
5043
|
+
reason: kind === "other" ? "failed" : "blocked",
|
|
5044
|
+
remedy: kind === "other" ? normalized.remedy : SCREEN_SHARE_BLOCKED_REMEDY_FOR(normalized.remedy),
|
|
5045
|
+
at: now()
|
|
5046
|
+
}
|
|
5047
|
+
});
|
|
4706
5048
|
}
|
|
4707
5049
|
},
|
|
5050
|
+
dismissShareNotice() {
|
|
5051
|
+
if (store.snapshot().share.state === "refused") store.patch({ share: NO_SHARE });
|
|
5052
|
+
},
|
|
4708
5053
|
async raiseHand(raised) {
|
|
4709
5054
|
const local = store.snapshot().localIdentity;
|
|
4710
5055
|
if (local !== null) store.setHand(local, raised);
|
|
@@ -5206,7 +5551,8 @@ function createLiveKitDriver(handlers, options = {}) {
|
|
|
5206
5551
|
}).on(RoomEvent.Disconnected, (reason) => {
|
|
5207
5552
|
audioContainer?.remove();
|
|
5208
5553
|
audioContainer = null;
|
|
5209
|
-
|
|
5554
|
+
const named = reason === void 0 ? null : module?.DisconnectReason?.[reason] ?? String(reason);
|
|
5555
|
+
handlers.disconnected(named);
|
|
5210
5556
|
});
|
|
5211
5557
|
try {
|
|
5212
5558
|
created.registerTextStreamHandler(TRANSCRIPTION_TOPIC, (reader, participantInfo) => {
|
|
@@ -5594,7 +5940,9 @@ function MeetRuntime(props) {
|
|
|
5594
5940
|
driverFactory: (handlers) => (driverRef.current ?? createLiveKitDriver)(handlers),
|
|
5595
5941
|
onDiagnostic: report,
|
|
5596
5942
|
effectsStorage,
|
|
5597
|
-
resolveDevice: (kind) => devicesHolder.current?.resolve(kind) ?? null
|
|
5943
|
+
resolveDevice: (kind) => devicesHolder.current?.resolve(kind) ?? null,
|
|
5944
|
+
// §3.4: on every LiveKit disconnect, ask the server what happened to this connection.
|
|
5945
|
+
readExit: (connection) => meetings.readExit(connection)
|
|
5598
5946
|
});
|
|
5599
5947
|
const meetings = createMeetingSession({
|
|
5600
5948
|
manager,
|
|
@@ -5629,17 +5977,17 @@ function MeetRuntime(props) {
|
|
|
5629
5977
|
const inRoom = createInRoomClient({ api, store, resolveRoomToken });
|
|
5630
5978
|
store.subscribe(() => {
|
|
5631
5979
|
const snapshot = store.snapshot();
|
|
5632
|
-
if (snapshot.phase
|
|
5980
|
+
if (isWaitingPhase(snapshot.phase) && snapshot.waiting !== null && (snapshot.waiting.admission === "knocking" || snapshot.waiting.admission === "waiting_host")) {
|
|
5633
5981
|
meetings.startWaiting();
|
|
5634
|
-
} else if (snapshot.phase
|
|
5982
|
+
} else if (!isWaitingPhase(snapshot.phase)) {
|
|
5635
5983
|
meetings.stopWaiting();
|
|
5636
5984
|
}
|
|
5637
5985
|
});
|
|
5638
5986
|
let chatReadFor = null;
|
|
5639
5987
|
store.subscribe(() => {
|
|
5640
5988
|
const snapshot = store.snapshot();
|
|
5641
|
-
if (snapshot.phase !== "
|
|
5642
|
-
if (snapshot.phase === "left" || snapshot.phase === "
|
|
5989
|
+
if (snapshot.phase !== "in_call" || snapshot.meeting === null) {
|
|
5990
|
+
if (snapshot.phase === "left" || snapshot.phase === "prejoin" || snapshot.phase === "resolving") chatReadFor = null;
|
|
5643
5991
|
return;
|
|
5644
5992
|
}
|
|
5645
5993
|
if (chatReadFor === snapshot.meeting.id) return;
|
|
@@ -5692,6 +6040,10 @@ function MeetRuntime(props) {
|
|
|
5692
6040
|
store,
|
|
5693
6041
|
identity,
|
|
5694
6042
|
onIncoming: (invite) => incomingRef.current?.(invite),
|
|
6043
|
+
// §3.6: my call was answered — the caller joins the call's room on the same machine.
|
|
6044
|
+
onAnswered: (invite) => {
|
|
6045
|
+
void engine.join({ roomName: invite.roomName, mode: invite.mode }).catch(() => void 0);
|
|
6046
|
+
},
|
|
5695
6047
|
onDiagnostic: report,
|
|
5696
6048
|
...props.ringTimeoutMs !== void 0 ? { ringTimeoutMs: props.ringTimeoutMs } : {}
|
|
5697
6049
|
});
|
|
@@ -5831,43 +6183,12 @@ function endedRecord(snapshot, given) {
|
|
|
5831
6183
|
function observedPhase(input) {
|
|
5832
6184
|
const { snapshot } = input;
|
|
5833
6185
|
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
|
-
}
|
|
6186
|
+
const phase = snapshot.phase;
|
|
6187
|
+
if (endedRecord(snapshot, input.meeting ?? null) && !isLivePhase(phase) && !isCallPhase(phase)) return "ended";
|
|
6188
|
+
return phase;
|
|
5853
6189
|
}
|
|
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
|
-
}
|
|
6190
|
+
function isCallPhase(phase) {
|
|
6191
|
+
return phase.startsWith("ringing_") || phase.startsWith("call_") || phase === "answered_elsewhere";
|
|
5871
6192
|
}
|
|
5872
6193
|
var ADMITTER_ROLES = /* @__PURE__ */ new Set(["host", "cohost"]);
|
|
5873
6194
|
function observe(input) {
|
|
@@ -5880,10 +6201,10 @@ function observe(input) {
|
|
|
5880
6201
|
const deviceInPlay = phase === "joining" || inCall;
|
|
5881
6202
|
return {
|
|
5882
6203
|
phase,
|
|
5883
|
-
reason: phase
|
|
6204
|
+
reason: phase === "ended" ? snapshot?.reason ?? null : snapshot?.reason ?? reasonOf(phase),
|
|
5884
6205
|
role,
|
|
5885
|
-
connection: snapshot?.
|
|
5886
|
-
hostPresent: inCall && snapshot !== null ? snapshot.participants.some((p) => ADMITTER_ROLES.has(p.role)) : snapshot
|
|
6206
|
+
connection: snapshot?.connection ?? "stable",
|
|
6207
|
+
hostPresent: inCall && snapshot !== null ? snapshot.participants.some((p) => ADMITTER_ROLES.has(p.role)) : snapshot !== null && isWaitingPhase(snapshot.phase) ? snapshot.waiting?.admittersPresent ?? null : null,
|
|
5887
6208
|
lobbyCount: canAdmit && snapshot !== null ? snapshot.lobby.filter((entry) => entry.state === "knocking").length : 0,
|
|
5888
6209
|
recording: snapshot?.recording === "recording" || snapshot?.recording === "stopping",
|
|
5889
6210
|
audioBlocked: null,
|
|
@@ -6684,7 +7005,7 @@ function useRoom() {
|
|
|
6684
7005
|
(participant) => participant.identity === snapshot.localIdentity
|
|
6685
7006
|
) ?? null;
|
|
6686
7007
|
return {
|
|
6687
|
-
phase: snapshot?.phase ?? "
|
|
7008
|
+
phase: snapshot?.phase ?? "resolving",
|
|
6688
7009
|
participants: snapshot?.participants ?? [],
|
|
6689
7010
|
failure: snapshot?.failure ?? null,
|
|
6690
7011
|
local,
|
|
@@ -8035,7 +8356,7 @@ function useSharedFocus(panelKey) {
|
|
|
8035
8356
|
const snapshot = useMeetSnapshot();
|
|
8036
8357
|
const focus = snapshot?.collab.focus ?? null;
|
|
8037
8358
|
const mine = focus !== null && focus.panel === panelKey ? focus : null;
|
|
8038
|
-
const canMove = !isObserverRole(snapshot?.grants.role) && snapshot?.phase === "
|
|
8359
|
+
const canMove = !isObserverRole(snapshot?.grants.role) && snapshot?.phase === "in_call";
|
|
8039
8360
|
return {
|
|
8040
8361
|
section: mine?.section || null,
|
|
8041
8362
|
byName: mine?.byName || null,
|
|
@@ -8111,6 +8432,36 @@ function useCollabPanelRequest(onRequest) {
|
|
|
8111
8432
|
var MEET_REACTIONS = ["\u{1F496}", "\u{1F44D}", "\u{1F389}", "\u{1F44F}", "\u{1F602}", "\u{1F62E}", "\u{1F622}", "\u{1F914}", "\u{1F44E}"];
|
|
8112
8433
|
|
|
8113
8434
|
// src/react/facade.ts
|
|
8435
|
+
var NO_ACTIONS = {
|
|
8436
|
+
join: async () => void 0,
|
|
8437
|
+
rejoin: async () => void 0,
|
|
8438
|
+
leave: async () => void 0,
|
|
8439
|
+
backToPrejoin: () => void 0,
|
|
8440
|
+
retry: () => void 0,
|
|
8441
|
+
resolved: () => void 0,
|
|
8442
|
+
dismissShareNotice: () => void 0,
|
|
8443
|
+
dismissCall: () => void 0
|
|
8444
|
+
};
|
|
8445
|
+
function actionsFor(host) {
|
|
8446
|
+
if (host === null) return NO_ACTIONS;
|
|
8447
|
+
const { engine, store } = host;
|
|
8448
|
+
return {
|
|
8449
|
+
join: (request) => engine.join(request),
|
|
8450
|
+
rejoin: () => engine.rejoin(),
|
|
8451
|
+
leave: () => engine.leave(),
|
|
8452
|
+
backToPrejoin: () => engine.backToPrejoin(),
|
|
8453
|
+
retry: () => {
|
|
8454
|
+
store.dispatch({ type: "retry", click: true });
|
|
8455
|
+
},
|
|
8456
|
+
resolved: () => {
|
|
8457
|
+
store.dispatch({ type: "resolved" });
|
|
8458
|
+
},
|
|
8459
|
+
dismissShareNotice: () => engine.dismissShareNotice(),
|
|
8460
|
+
dismissCall: () => {
|
|
8461
|
+
if (store.dispatch({ type: "reset" })) store.patch({ call: null });
|
|
8462
|
+
}
|
|
8463
|
+
};
|
|
8464
|
+
}
|
|
8114
8465
|
function useMeetCore() {
|
|
8115
8466
|
const host = useMeetHost();
|
|
8116
8467
|
const snapshot = useMeetSnapshot();
|
|
@@ -8118,9 +8469,24 @@ function useMeetCore() {
|
|
|
8118
8469
|
const role = useRole();
|
|
8119
8470
|
const isHost = useIsHost();
|
|
8120
8471
|
const observation = useCoreObservation();
|
|
8472
|
+
const actions = useMemo5(() => actionsFor(host), [host]);
|
|
8121
8473
|
return useMemo5(
|
|
8122
|
-
() => ({
|
|
8123
|
-
|
|
8474
|
+
() => ({
|
|
8475
|
+
host,
|
|
8476
|
+
snapshot,
|
|
8477
|
+
devices,
|
|
8478
|
+
role,
|
|
8479
|
+
isHost,
|
|
8480
|
+
observation,
|
|
8481
|
+
phase: snapshot?.phase ?? "resolving",
|
|
8482
|
+
reason: snapshot?.reason ?? null,
|
|
8483
|
+
phaseNotice: snapshot?.phaseNotice ?? null,
|
|
8484
|
+
connection: snapshot?.connection ?? "stable",
|
|
8485
|
+
share: snapshot?.share ?? NO_SHARE,
|
|
8486
|
+
call: snapshot?.call ?? null,
|
|
8487
|
+
actions
|
|
8488
|
+
}),
|
|
8489
|
+
[host, snapshot, devices, role, isHost, observation, actions]
|
|
8124
8490
|
);
|
|
8125
8491
|
}
|
|
8126
8492
|
|
|
@@ -11051,11 +11417,52 @@ function MeetSkinRoom({
|
|
|
11051
11417
|
headerControls,
|
|
11052
11418
|
observe: observe2 = false
|
|
11053
11419
|
}) {
|
|
11054
|
-
const
|
|
11420
|
+
const core = useMeetCore();
|
|
11421
|
+
const { host, snapshot, isHost, phase } = core;
|
|
11055
11422
|
const elapsed = useElapsed(snapshot?.meeting?.startedAt ?? null);
|
|
11056
|
-
const phase = snapshot?.phase ?? "idle";
|
|
11057
11423
|
if (host === null) return null;
|
|
11058
|
-
|
|
11424
|
+
const screen = (() => {
|
|
11425
|
+
if (isRefusedPhase(phase)) return "refused";
|
|
11426
|
+
switch (phase) {
|
|
11427
|
+
case "resolving":
|
|
11428
|
+
return "resolving";
|
|
11429
|
+
case "prejoin":
|
|
11430
|
+
case "guest_name":
|
|
11431
|
+
return "prejoin";
|
|
11432
|
+
case "joining":
|
|
11433
|
+
case "in_call":
|
|
11434
|
+
return "room";
|
|
11435
|
+
case "waiting:host_not_started":
|
|
11436
|
+
case "waiting:knocking":
|
|
11437
|
+
case "denied":
|
|
11438
|
+
case "knock_expired":
|
|
11439
|
+
return "waiting";
|
|
11440
|
+
case "left":
|
|
11441
|
+
return snapshot?.waiting?.admission === "withdrawn" ? "waiting" : "exit";
|
|
11442
|
+
case "disconnected":
|
|
11443
|
+
case "removed":
|
|
11444
|
+
case "superseded":
|
|
11445
|
+
case "inactive":
|
|
11446
|
+
case "unsupported":
|
|
11447
|
+
case "ended":
|
|
11448
|
+
case "record":
|
|
11449
|
+
case "ringing_out":
|
|
11450
|
+
case "ringing_in":
|
|
11451
|
+
case "call_declined":
|
|
11452
|
+
case "call_missed":
|
|
11453
|
+
case "call_busy":
|
|
11454
|
+
case "answered_elsewhere":
|
|
11455
|
+
return "exit";
|
|
11456
|
+
default: {
|
|
11457
|
+
const unhandled = phase;
|
|
11458
|
+
return unhandled;
|
|
11459
|
+
}
|
|
11460
|
+
}
|
|
11461
|
+
})();
|
|
11462
|
+
if (screen === "resolving") {
|
|
11463
|
+
return /* @__PURE__ */ jsx6("div", { className: "mx-meet", children: /* @__PURE__ */ jsx6("p", { className: "mx-meet__empty-body", role: "status", children: "Getting the meeting ready\u2026" }) });
|
|
11464
|
+
}
|
|
11465
|
+
if (screen === "prejoin") {
|
|
11059
11466
|
return /* @__PURE__ */ jsx6("div", { className: "mx-meet", children: /* @__PURE__ */ jsx6(
|
|
11060
11467
|
PreJoin,
|
|
11061
11468
|
{
|
|
@@ -11066,9 +11473,12 @@ function MeetSkinRoom({
|
|
|
11066
11473
|
}
|
|
11067
11474
|
) });
|
|
11068
11475
|
}
|
|
11069
|
-
if (
|
|
11476
|
+
if (screen === "waiting") {
|
|
11070
11477
|
return /* @__PURE__ */ jsx6("div", { className: "mx-meet mx-meet--lobby", children: /* @__PURE__ */ jsx6(WaitingRoom, { host, ...onLeave !== void 0 ? { onLeave } : {} }) });
|
|
11071
11478
|
}
|
|
11479
|
+
if (screen === "refused" || screen === "exit") {
|
|
11480
|
+
return /* @__PURE__ */ jsx6("div", { className: "mx-meet mx-meet--lobby", children: /* @__PURE__ */ jsx6(ExitScreen, { ...onLeave !== void 0 ? { onLeave } : {} }) });
|
|
11481
|
+
}
|
|
11072
11482
|
return /* @__PURE__ */ jsxs3("div", { className: "mx-meet", children: [
|
|
11073
11483
|
/* @__PURE__ */ jsx6(ConsentNotice, {}),
|
|
11074
11484
|
/* @__PURE__ */ jsx6(AttendanceNotice, {}),
|
|
@@ -11088,14 +11498,148 @@ function MeetSkinRoom({
|
|
|
11088
11498
|
headerControls !== void 0 && headerControls !== null ? /* @__PURE__ */ jsx6("span", { className: "mx-meet__header-controls", "data-meet-header-controls": "", children: headerControls }) : null,
|
|
11089
11499
|
isHost ? /* @__PURE__ */ jsx6(HostMenu, {}) : null
|
|
11090
11500
|
] }),
|
|
11091
|
-
|
|
11501
|
+
core.connection !== "stable" ? /* @__PURE__ */ jsx6("p", { className: "mx-meet__banner", role: "status", children: "Reconnecting\u2026" }) : null,
|
|
11502
|
+
/* @__PURE__ */ jsx6(ShareNotice, {}),
|
|
11092
11503
|
/* @__PURE__ */ jsx6(Stage, {}),
|
|
11093
11504
|
/* @__PURE__ */ jsx6(Captions, {}),
|
|
11094
11505
|
/* @__PURE__ */ jsx6(ControlBar, { ...onLeave !== void 0 ? { onLeave } : {} })
|
|
11095
11506
|
] });
|
|
11096
11507
|
}
|
|
11508
|
+
function ShareNotice() {
|
|
11509
|
+
const { share, actions } = useMeetCore();
|
|
11510
|
+
if (share.state !== "refused") return null;
|
|
11511
|
+
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";
|
|
11512
|
+
return /* @__PURE__ */ jsxs3("p", { className: "mx-meet__banner", role: "status", "data-meet-notice": "share-unavailable", children: [
|
|
11513
|
+
text3,
|
|
11514
|
+
share.remedy !== null ? /* @__PURE__ */ jsx6("span", { className: "mx-meet__remedy", children: share.remedy }) : null,
|
|
11515
|
+
/* @__PURE__ */ jsx6("button", { type: "button", className: "mx-meet__link", onClick: actions.dismissShareNotice, children: "Dismiss" })
|
|
11516
|
+
] });
|
|
11517
|
+
}
|
|
11518
|
+
var REFUSED_TITLES = {
|
|
11519
|
+
not_found: "Check your meeting code",
|
|
11520
|
+
cancelled: "This meeting was cancelled",
|
|
11521
|
+
expired: "This meeting link has expired",
|
|
11522
|
+
too_early: "This meeting hasn't started yet",
|
|
11523
|
+
sign_in_required: "Sign in to join this meeting",
|
|
11524
|
+
wrong_account: "You're signed in with a different account",
|
|
11525
|
+
not_invited: "You aren't invited to this meeting",
|
|
11526
|
+
locked: "This meeting is locked",
|
|
11527
|
+
removed: "You can't rejoin this meeting",
|
|
11528
|
+
lobby_rejects: "You can't ask to join this meeting",
|
|
11529
|
+
meeting_full: "This meeting is full",
|
|
11530
|
+
platform_capacity: "Meetings are busy right now",
|
|
11531
|
+
verification_required: "Verify your account to join",
|
|
11532
|
+
registration_required: "Register to join this meeting"
|
|
11533
|
+
};
|
|
11534
|
+
function ExitScreen({ onLeave }) {
|
|
11535
|
+
const { phase, reason, host, snapshot, actions, call } = useMeetCore();
|
|
11536
|
+
const [busy, setBusy] = useState4(false);
|
|
11537
|
+
const failure = snapshot?.failure ?? null;
|
|
11538
|
+
const rejoin = /* @__PURE__ */ jsx6(
|
|
11539
|
+
Button,
|
|
11540
|
+
{
|
|
11541
|
+
type: "button",
|
|
11542
|
+
disabled: busy,
|
|
11543
|
+
onClick: () => {
|
|
11544
|
+
setBusy(true);
|
|
11545
|
+
void actions.rejoin().catch(() => void 0).finally(() => setBusy(false));
|
|
11546
|
+
},
|
|
11547
|
+
children: "Rejoin"
|
|
11548
|
+
}
|
|
11549
|
+
);
|
|
11550
|
+
const home = /* @__PURE__ */ jsx6(
|
|
11551
|
+
"a",
|
|
11552
|
+
{
|
|
11553
|
+
className: "mx-meet__link",
|
|
11554
|
+
href: "/",
|
|
11555
|
+
onClick: (event) => {
|
|
11556
|
+
if (call !== null) {
|
|
11557
|
+
event.preventDefault();
|
|
11558
|
+
actions.dismissCall();
|
|
11559
|
+
}
|
|
11560
|
+
if (onLeave !== void 0) {
|
|
11561
|
+
event.preventDefault();
|
|
11562
|
+
onLeave();
|
|
11563
|
+
}
|
|
11564
|
+
},
|
|
11565
|
+
children: "Go home"
|
|
11566
|
+
}
|
|
11567
|
+
);
|
|
11568
|
+
let title;
|
|
11569
|
+
let line = null;
|
|
11570
|
+
let notice = null;
|
|
11571
|
+
let actionsNode = home;
|
|
11572
|
+
if (phase.startsWith("refused:")) {
|
|
11573
|
+
const code = phase.slice("refused:".length);
|
|
11574
|
+
title = REFUSED_TITLES[code] ?? "You can't join this meeting";
|
|
11575
|
+
line = failure?.remedy ?? null;
|
|
11576
|
+
actionsNode = /* @__PURE__ */ jsxs3(Fragment, { children: [
|
|
11577
|
+
/* @__PURE__ */ jsx6(Button, { type: "button", variant: "outline", onClick: actions.retry, children: "Try again" }),
|
|
11578
|
+
home
|
|
11579
|
+
] });
|
|
11580
|
+
} else {
|
|
11581
|
+
switch (phase) {
|
|
11582
|
+
case "left":
|
|
11583
|
+
title = "You left the meeting";
|
|
11584
|
+
actionsNode = /* @__PURE__ */ jsxs3(Fragment, { children: [
|
|
11585
|
+
rejoin,
|
|
11586
|
+
home
|
|
11587
|
+
] });
|
|
11588
|
+
break;
|
|
11589
|
+
case "disconnected":
|
|
11590
|
+
title = "You lost your connection to the meeting";
|
|
11591
|
+
line = failure?.remedy ?? "Check your internet connection, then rejoin.";
|
|
11592
|
+
notice = "disconnected";
|
|
11593
|
+
actionsNode = /* @__PURE__ */ jsxs3(Fragment, { children: [
|
|
11594
|
+
rejoin,
|
|
11595
|
+
home
|
|
11596
|
+
] });
|
|
11597
|
+
break;
|
|
11598
|
+
case "removed":
|
|
11599
|
+
title = "You've been removed from the meeting";
|
|
11600
|
+
notice = "removed";
|
|
11601
|
+
break;
|
|
11602
|
+
case "superseded":
|
|
11603
|
+
title = "You joined from another tab or device";
|
|
11604
|
+
line = "This screen left the meeting so you aren't in it twice.";
|
|
11605
|
+
notice = "displaced";
|
|
11606
|
+
actionsNode = /* @__PURE__ */ jsxs3(Fragment, { children: [
|
|
11607
|
+
rejoin,
|
|
11608
|
+
home
|
|
11609
|
+
] });
|
|
11610
|
+
break;
|
|
11611
|
+
case "inactive":
|
|
11612
|
+
title = "You left the meeting because this tab was inactive";
|
|
11613
|
+
actionsNode = /* @__PURE__ */ jsxs3(Fragment, { children: [
|
|
11614
|
+
rejoin,
|
|
11615
|
+
home
|
|
11616
|
+
] });
|
|
11617
|
+
break;
|
|
11618
|
+
case "unsupported":
|
|
11619
|
+
title = "This browser can't join meetings";
|
|
11620
|
+
line = "Open the link in Chrome, Edge, Firefox or Safari.";
|
|
11621
|
+
break;
|
|
11622
|
+
case "ended":
|
|
11623
|
+
case "record":
|
|
11624
|
+
title = reason === "time_limit" ? "The meeting reached its time limit" : "The meeting has ended";
|
|
11625
|
+
notice = "meeting-ended";
|
|
11626
|
+
break;
|
|
11627
|
+
default:
|
|
11628
|
+
title = "The call has ended";
|
|
11629
|
+
break;
|
|
11630
|
+
}
|
|
11631
|
+
}
|
|
11632
|
+
return /* @__PURE__ */ jsxs3("div", { className: "mx-meet__waiting", "data-meet-exit": phase, children: [
|
|
11633
|
+
/* @__PURE__ */ jsx6(Avatar, { name: host?.identity.displayName ?? "", url: host?.identity.avatarUrl ?? null, size: 72 }),
|
|
11634
|
+
/* @__PURE__ */ jsx6("h1", { className: "mx-meet__prejoin-title", children: title }),
|
|
11635
|
+
line !== null ? /* @__PURE__ */ jsx6("p", { className: "mx-meet__empty-body", children: line }) : null,
|
|
11636
|
+
notice !== null ? /* @__PURE__ */ jsx6("span", { role: "status", "data-meet-notice": notice, className: "mx-meet__sr-only", children: title }) : null,
|
|
11637
|
+
/* @__PURE__ */ jsx6("div", { className: "mx-meet__waiting-actions", children: actionsNode })
|
|
11638
|
+
] });
|
|
11639
|
+
}
|
|
11097
11640
|
function WaitingRoom({ host, onLeave }) {
|
|
11098
11641
|
const { waiting, withdraw, askAgain } = useWaiting();
|
|
11642
|
+
const { phaseNotice } = useMeetCore();
|
|
11099
11643
|
const [busy, setBusy] = useState4(false);
|
|
11100
11644
|
const admission = waiting?.admission ?? "knocking";
|
|
11101
11645
|
const run = (action) => () => {
|
|
@@ -11144,6 +11688,7 @@ function WaitingRoom({ host, onLeave }) {
|
|
|
11144
11688
|
return /* @__PURE__ */ jsxs3("div", { className: "mx-meet__waiting", children: [
|
|
11145
11689
|
/* @__PURE__ */ jsx6(Avatar, { name: host.identity.displayName, url: host.identity.avatarUrl, size: 72 }),
|
|
11146
11690
|
/* @__PURE__ */ jsx6("h1", { className: "mx-meet__prejoin-title", children: title }),
|
|
11691
|
+
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
11692
|
line !== null ? /* @__PURE__ */ jsx6("p", { className: "mx-meet__empty-body", role: "status", "data-meet-notice": "host-absent", children: line }) : null,
|
|
11148
11693
|
/* @__PURE__ */ jsx6("div", { className: "mx-meet__waiting-actions", children: actions })
|
|
11149
11694
|
] });
|
|
@@ -11638,10 +12183,17 @@ function MeetingSkinRootBody({
|
|
|
11638
12183
|
host
|
|
11639
12184
|
}) {
|
|
11640
12185
|
const snapshot = useMeetSnapshot();
|
|
11641
|
-
const phase = snapshot?.phase ?? "
|
|
12186
|
+
const phase = snapshot?.phase ?? "resolving";
|
|
11642
12187
|
useEffect8(() => {
|
|
11643
12188
|
host.meetings.watch(roomName);
|
|
11644
12189
|
}, [host, roomName]);
|
|
12190
|
+
useEffect8(() => {
|
|
12191
|
+
const now = host.store.snapshot();
|
|
12192
|
+
if (now.phase !== "resolving" && now.roomName !== null && now.roomName !== roomName && now.call === null) {
|
|
12193
|
+
host.store.dispatch({ type: "reset" });
|
|
12194
|
+
}
|
|
12195
|
+
if (host.store.snapshot().phase === "resolving") host.store.dispatch({ type: "resolved" });
|
|
12196
|
+
}, [host, roomName, phase]);
|
|
11645
12197
|
useEffect8(() => {
|
|
11646
12198
|
if (given === void 0) return;
|
|
11647
12199
|
if (host.store.snapshot().meeting !== null) return;
|
|
@@ -11653,7 +12205,7 @@ function MeetingSkinRootBody({
|
|
|
11653
12205
|
host.meetings.watchMeeting(asMeetingId(watchedId));
|
|
11654
12206
|
}, [host, watchedId]);
|
|
11655
12207
|
const record = snapshot?.meeting ?? given ?? null;
|
|
11656
|
-
if (hasEnded(record) && phase
|
|
12208
|
+
if (hasEnded(record) && !isLivePhase(phase)) {
|
|
11657
12209
|
return /* @__PURE__ */ jsx8(MeetingRecordView, { meeting: record });
|
|
11658
12210
|
}
|
|
11659
12211
|
return /* @__PURE__ */ jsx8(
|
|
@@ -11670,11 +12222,49 @@ function MeetingSkinRootBody({
|
|
|
11670
12222
|
}
|
|
11671
12223
|
|
|
11672
12224
|
// src/react/calls-ui.tsx
|
|
11673
|
-
import {
|
|
12225
|
+
import { useEffect as useEffect9 } from "react";
|
|
12226
|
+
import { Fragment as Fragment3, jsx as jsx9, jsxs as jsxs5 } from "react/jsx-runtime";
|
|
11674
12227
|
function IncomingCallHost() {
|
|
11675
12228
|
const calls = useCalls();
|
|
11676
|
-
|
|
11677
|
-
|
|
12229
|
+
const { call, phase, actions } = useMeetCore();
|
|
12230
|
+
useEffect9(() => {
|
|
12231
|
+
if (call !== null && phase === "left") actions.dismissCall();
|
|
12232
|
+
}, [call, phase, actions]);
|
|
12233
|
+
const outcome = call !== null && (phase === "call_declined" || phase === "call_missed" || phase === "call_busy" || phase === "answered_elsewhere");
|
|
12234
|
+
useEffect9(() => {
|
|
12235
|
+
if (!outcome) return void 0;
|
|
12236
|
+
const timer = setTimeout(() => actions.dismissCall(), 5e3);
|
|
12237
|
+
return () => clearTimeout(timer);
|
|
12238
|
+
}, [outcome, actions]);
|
|
12239
|
+
const inCall = call !== null && (phase === "joining" || phase === "in_call" || phase === "disconnected" || phase === "removed" || phase === "superseded");
|
|
12240
|
+
return /* @__PURE__ */ jsxs5(Fragment3, { children: [
|
|
12241
|
+
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,
|
|
12242
|
+
calls.incoming.length > 0 || call !== null && (phase === "ringing_out" || outcome) ? /* @__PURE__ */ jsxs5("div", { className: "mx-meet__ringing-stack", children: [
|
|
12243
|
+
call !== null && phase === "ringing_out" ? /* @__PURE__ */ jsx9(OutgoingCallCard, { invite: call.invite }) : null,
|
|
12244
|
+
outcome && call !== null ? /* @__PURE__ */ jsx9(CallOutcomeCard, { invite: call.invite }) : null,
|
|
12245
|
+
calls.incoming.map((invite) => /* @__PURE__ */ jsx9(IncomingCallCard, { invite }, invite.id))
|
|
12246
|
+
] }) : null
|
|
12247
|
+
] });
|
|
12248
|
+
}
|
|
12249
|
+
function OutgoingCallCard({ invite }) {
|
|
12250
|
+
const calls = useCalls();
|
|
12251
|
+
return /* @__PURE__ */ jsxs5("section", { className: "mx-meet__ringing", role: "status", "aria-label": "Calling", "data-meet-notice": "ringing-out", children: [
|
|
12252
|
+
/* @__PURE__ */ jsx9(Avatar, { name: invite.callerName, url: invite.callerAvatarUrl, size: 56 }),
|
|
12253
|
+
/* @__PURE__ */ jsxs5("div", { className: "mx-meet__ringing-text", children: [
|
|
12254
|
+
/* @__PURE__ */ jsx9("strong", { children: "Calling\u2026" }),
|
|
12255
|
+
/* @__PURE__ */ jsx9("span", { children: invite.mode === "video" ? "Video call" : "Voice call" })
|
|
12256
|
+
] }),
|
|
12257
|
+
/* @__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, {}) }) })
|
|
12258
|
+
] });
|
|
12259
|
+
}
|
|
12260
|
+
function CallOutcomeCard({ invite }) {
|
|
12261
|
+
const { phase, actions, call } = useMeetCore();
|
|
12262
|
+
const outgoing = call?.direction === "outgoing";
|
|
12263
|
+
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}`;
|
|
12264
|
+
return /* @__PURE__ */ jsxs5("section", { className: "mx-meet__ringing", role: "status", "aria-label": text3, "data-meet-call-outcome": phase, children: [
|
|
12265
|
+
/* @__PURE__ */ jsx9("div", { className: "mx-meet__ringing-text", children: /* @__PURE__ */ jsx9("strong", { children: text3 }) }),
|
|
12266
|
+
/* @__PURE__ */ jsx9("div", { className: "mx-meet__ringing-actions", children: /* @__PURE__ */ jsx9(ControlButton, { label: "Close", onClick: actions.dismissCall, children: /* @__PURE__ */ jsx9(PhoneOffIcon, {}) }) })
|
|
12267
|
+
] });
|
|
11678
12268
|
}
|
|
11679
12269
|
function IncomingCallCard({ invite }) {
|
|
11680
12270
|
const calls = useCalls();
|
|
@@ -11849,28 +12439,6 @@ async function pushMeetingToGoogleCalendar(api, meetingId, target) {
|
|
|
11849
12439
|
if (!response.ok) throw await refusal(response, operation);
|
|
11850
12440
|
return await response.json();
|
|
11851
12441
|
}
|
|
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
12442
|
export {
|
|
11875
12443
|
ATTENDANCE_CSV_HEADER,
|
|
11876
12444
|
CALL_INVITE_KIND,
|
|
@@ -11889,11 +12457,13 @@ export {
|
|
|
11889
12457
|
GUEST_RECORD_NOT_SHARED,
|
|
11890
12458
|
GuestRecordAccessControl,
|
|
11891
12459
|
IDLE_MEDIA,
|
|
12460
|
+
IDLE_PHASES,
|
|
11892
12461
|
IDLE_TRACK,
|
|
11893
12462
|
IncomingCallCard,
|
|
11894
12463
|
IncomingCallHost,
|
|
11895
12464
|
JOIN_REFUSAL_REASONS,
|
|
11896
12465
|
LISTEN_ONLY_NOTICE,
|
|
12466
|
+
LIVE_PHASES,
|
|
11897
12467
|
MEETING_INVITE_KIND,
|
|
11898
12468
|
MEETING_INVITE_VERSION,
|
|
11899
12469
|
MEETING_PASS_HEADER,
|
|
@@ -11915,6 +12485,7 @@ export {
|
|
|
11915
12485
|
NOTE_TAKER_IDENTITY_PREFIX,
|
|
11916
12486
|
NOTE_TAKER_METADATA_KEY,
|
|
11917
12487
|
NO_GRANTS,
|
|
12488
|
+
NO_SHARE,
|
|
11918
12489
|
ORGANIZATION_HEADER,
|
|
11919
12490
|
PARTICIPANT_CAN_RECORD_ATTRIBUTE,
|
|
11920
12491
|
PARTICIPANT_ROLE_ATTRIBUTE,
|
|
@@ -11929,9 +12500,11 @@ export {
|
|
|
11929
12500
|
TABLES,
|
|
11930
12501
|
TRANSCRIPTION_ATTRIBUTES,
|
|
11931
12502
|
TRANSCRIPTION_TOPIC,
|
|
12503
|
+
WAITING_PHASES,
|
|
11932
12504
|
absenceSentence,
|
|
11933
12505
|
activeWrapUpSummary,
|
|
11934
12506
|
activityAttention,
|
|
12507
|
+
admissionPhase,
|
|
11935
12508
|
announceMeeting,
|
|
11936
12509
|
appPanelOf,
|
|
11937
12510
|
applyCollabPacket,
|
|
@@ -12001,10 +12574,13 @@ export {
|
|
|
12001
12574
|
isJoinRefusalReason,
|
|
12002
12575
|
isLinkFollower,
|
|
12003
12576
|
isListenOnly,
|
|
12577
|
+
isLivePhase,
|
|
12004
12578
|
isNoteTaker,
|
|
12005
12579
|
isObserverRole,
|
|
12006
12580
|
isRealName,
|
|
12581
|
+
isRefusedPhase,
|
|
12007
12582
|
isRoomEventTopic,
|
|
12583
|
+
isWaitingPhase,
|
|
12008
12584
|
learnName,
|
|
12009
12585
|
learnNames,
|
|
12010
12586
|
listenOnlyNotice,
|
|
@@ -12029,6 +12605,7 @@ export {
|
|
|
12029
12605
|
mergeRecord,
|
|
12030
12606
|
microphoneNoticeCode,
|
|
12031
12607
|
moveToRoom,
|
|
12608
|
+
nextPhase,
|
|
12032
12609
|
nextTranslationBatch,
|
|
12033
12610
|
normalizeDeviceError,
|
|
12034
12611
|
normalizeMeetError,
|
|
@@ -12061,10 +12638,13 @@ export {
|
|
|
12061
12638
|
pushMeetingToGoogleCalendar,
|
|
12062
12639
|
readServerRefusal,
|
|
12063
12640
|
readableTranscript,
|
|
12641
|
+
reasonOf,
|
|
12064
12642
|
recordingLabel,
|
|
12065
12643
|
recordingNotice,
|
|
12066
12644
|
recordingOffsetMs,
|
|
12645
|
+
refusalPhase,
|
|
12067
12646
|
rememberedMeetingPass,
|
|
12647
|
+
resolveExit,
|
|
12068
12648
|
roleOf,
|
|
12069
12649
|
roomTopic,
|
|
12070
12650
|
rootAttributes,
|