@ai-matrx/meet 0.11.47 → 0.11.49
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-D1TX5uf9.d.cts → collab-Dk3r37MW.d.cts} +5 -0
- package/dist/{collab-D1TX5uf9.d.ts → collab-Dk3r37MW.d.ts} +5 -0
- package/dist/{components-CNwBgmYU.d.ts → components-BWqIoc_V.d.ts} +1 -1
- package/dist/{components-CkyDos0f.d.cts → components-fpQrqtcs.d.cts} +1 -1
- package/dist/core.cjs +367 -11
- package/dist/core.cjs.map +1 -1
- package/dist/core.d.cts +79 -5
- package/dist/core.d.ts +79 -5
- package/dist/core.js +367 -11
- package/dist/core.js.map +1 -1
- package/dist/index.cjs +367 -11
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +1 -1
- package/dist/index.d.ts +1 -1
- package/dist/index.js +367 -11
- package/dist/index.js.map +1 -1
- package/dist/react.cjs +482 -25
- package/dist/react.cjs.map +1 -1
- package/dist/react.d.cts +11 -4
- package/dist/react.d.ts +11 -4
- package/dist/react.js +482 -25
- package/dist/react.js.map +1 -1
- package/dist/skins/meet.cjs +125 -16
- 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 +125 -16
- package/dist/skins/meet.js.map +1 -1
- package/package.json +5 -5
package/dist/react.cjs
CHANGED
|
@@ -487,10 +487,11 @@ function webTabPlatform() {
|
|
|
487
487
|
|
|
488
488
|
// src/core/connection-supervisor.ts
|
|
489
489
|
var DEFAULT_CONNECTION_POLICY = {
|
|
490
|
-
bannerDelayMs:
|
|
490
|
+
bannerDelayMs: 5e3,
|
|
491
491
|
autoRejoin: true,
|
|
492
492
|
autoRejoinMaxAttempts: 5,
|
|
493
|
-
reconnectGraceSeconds: 30
|
|
493
|
+
reconnectGraceSeconds: 30,
|
|
494
|
+
videoAutoOffAfterPoorSeconds: 20
|
|
494
495
|
};
|
|
495
496
|
function clampInt(value, min, max, fallback) {
|
|
496
497
|
if (typeof value !== "number" || !Number.isFinite(value)) return fallback;
|
|
@@ -504,7 +505,8 @@ function connectionPolicyFrom(raw) {
|
|
|
504
505
|
bannerDelayMs: clampInt(row["reconnect_banner_delay_ms"], 0, 1e4, d.bannerDelayMs),
|
|
505
506
|
autoRejoin: typeof row["auto_rejoin"] === "boolean" ? row["auto_rejoin"] : d.autoRejoin,
|
|
506
507
|
autoRejoinMaxAttempts: clampInt(row["auto_rejoin_max_attempts"], 0, 20, d.autoRejoinMaxAttempts),
|
|
507
|
-
reconnectGraceSeconds: clampInt(row["reconnect_grace_seconds"], 0, 600, d.reconnectGraceSeconds)
|
|
508
|
+
reconnectGraceSeconds: clampInt(row["reconnect_grace_seconds"], 0, 600, d.reconnectGraceSeconds),
|
|
509
|
+
videoAutoOffAfterPoorSeconds: clampInt(row["video_auto_off_after_poor_seconds"], 0, 600, d.videoAutoOffAfterPoorSeconds)
|
|
508
510
|
};
|
|
509
511
|
}
|
|
510
512
|
var LIVEKIT_BASE_DELAYS_MS = [0, 300, 1200, 2700, 4800, 7e3];
|
|
@@ -2771,11 +2773,14 @@ function recordingLabel(state) {
|
|
|
2771
2773
|
|
|
2772
2774
|
// src/core/live-policy.ts
|
|
2773
2775
|
var DEFAULT_LIVE_POLICY = {
|
|
2776
|
+
headlessPowers: "members_admit",
|
|
2774
2777
|
chatMode: "everyone",
|
|
2775
2778
|
avLock: "off",
|
|
2776
2779
|
timeLimitMinutes: 0,
|
|
2777
2780
|
timeLimitWarningMinutes: 5,
|
|
2778
|
-
recordingConsent: "notice"
|
|
2781
|
+
recordingConsent: "notice",
|
|
2782
|
+
shareWhoCan: "everyone",
|
|
2783
|
+
shareConcurrency: "one_takeover"
|
|
2779
2784
|
};
|
|
2780
2785
|
function oneOf(value, allowed, fallback) {
|
|
2781
2786
|
return typeof value === "string" && allowed.includes(value) ? value : fallback;
|
|
@@ -2787,6 +2792,7 @@ function whole(value, fallback) {
|
|
|
2787
2792
|
function livePolicyFrom(raw) {
|
|
2788
2793
|
const o = typeof raw === "object" && raw !== null ? raw : {};
|
|
2789
2794
|
return {
|
|
2795
|
+
headlessPowers: oneOf(o["headless_powers"], ["members_admit", "none", "cohosts_only"], DEFAULT_LIVE_POLICY.headlessPowers),
|
|
2790
2796
|
chatMode: oneOf(o["chat_mode"], ["everyone", "hosts_only", "off"], DEFAULT_LIVE_POLICY.chatMode),
|
|
2791
2797
|
avLock: oneOf(o["attendee_av_lock"], ["off", "mic", "camera", "both"], DEFAULT_LIVE_POLICY.avLock),
|
|
2792
2798
|
timeLimitMinutes: whole(o["time_limit_minutes"], 0),
|
|
@@ -2795,17 +2801,33 @@ function livePolicyFrom(raw) {
|
|
|
2795
2801
|
o["recording_consent"],
|
|
2796
2802
|
["notice", "ok_or_leave", "view_only_until_agree"],
|
|
2797
2803
|
DEFAULT_LIVE_POLICY.recordingConsent
|
|
2798
|
-
)
|
|
2804
|
+
),
|
|
2805
|
+
shareWhoCan: oneOf(o["share_who_can"], ["everyone", "host_only", "org", "presenters"], DEFAULT_LIVE_POLICY.shareWhoCan),
|
|
2806
|
+
shareConcurrency: oneOf(o["share_concurrency"], ["one_takeover", "one_replace", "multiple"], DEFAULT_LIVE_POLICY.shareConcurrency)
|
|
2799
2807
|
};
|
|
2800
2808
|
}
|
|
2801
2809
|
function samePolicy(a, b) {
|
|
2802
|
-
return a.chatMode === b.chatMode && a.avLock === b.avLock && a.timeLimitMinutes === b.timeLimitMinutes && a.timeLimitWarningMinutes === b.timeLimitWarningMinutes && a.recordingConsent === b.recordingConsent;
|
|
2810
|
+
return a.headlessPowers === b.headlessPowers && a.chatMode === b.chatMode && a.avLock === b.avLock && a.timeLimitMinutes === b.timeLimitMinutes && a.timeLimitWarningMinutes === b.timeLimitWarningMinutes && a.recordingConsent === b.recordingConsent && a.shareWhoCan === b.shareWhoCan && a.shareConcurrency === b.shareConcurrency;
|
|
2803
2811
|
}
|
|
2804
2812
|
var MODERATORS = /* @__PURE__ */ new Set(["host", "cohost"]);
|
|
2805
2813
|
function isLockedFor(policy, role, kind) {
|
|
2806
2814
|
if (MODERATORS.has(role)) return false;
|
|
2807
2815
|
return policy.avLock === "both" || (kind === "microphone" ? policy.avLock === "mic" : policy.avLock === "camera");
|
|
2808
2816
|
}
|
|
2817
|
+
function canShareUnder(policy, role, standing) {
|
|
2818
|
+
if (role === "viewer") return false;
|
|
2819
|
+
if (MODERATORS.has(role)) return true;
|
|
2820
|
+
switch (policy.shareWhoCan) {
|
|
2821
|
+
case "host_only":
|
|
2822
|
+
return false;
|
|
2823
|
+
case "presenters":
|
|
2824
|
+
return role === "presenter";
|
|
2825
|
+
case "org":
|
|
2826
|
+
return role === "presenter" || standing === "member";
|
|
2827
|
+
default:
|
|
2828
|
+
return true;
|
|
2829
|
+
}
|
|
2830
|
+
}
|
|
2809
2831
|
function consentPending(policy, role, recordingOn, given) {
|
|
2810
2832
|
return recordingOn && !given && policy.recordingConsent !== "notice" && !MODERATORS.has(role);
|
|
2811
2833
|
}
|
|
@@ -2817,6 +2839,12 @@ function minutesLeft(policy, startedAtMs, nowMs) {
|
|
|
2817
2839
|
if (policy.timeLimitMinutes <= 0 || startedAtMs === null) return null;
|
|
2818
2840
|
return Math.ceil((startedAtMs + policy.timeLimitMinutes * 6e4 - nowMs) / 6e4);
|
|
2819
2841
|
}
|
|
2842
|
+
function mayAdmitNow(role, participants, policy) {
|
|
2843
|
+
if (role === "none") return false;
|
|
2844
|
+
if (role === "host" || role === "cohost") return true;
|
|
2845
|
+
const hostIn = participants.some((p) => p.role === "host" || p.role === "cohost");
|
|
2846
|
+
return !hostIn && policy.headlessPowers === "members_admit";
|
|
2847
|
+
}
|
|
2820
2848
|
|
|
2821
2849
|
// src/core/attendance.ts
|
|
2822
2850
|
var IDLE_PREVIEW = { state: "idle", unavailable: null };
|
|
@@ -4277,6 +4305,7 @@ var NO_GRANTS = {
|
|
|
4277
4305
|
canPublish: false,
|
|
4278
4306
|
canSubscribe: false
|
|
4279
4307
|
};
|
|
4308
|
+
var UNKNOWN_LOCAL_NETWORK = { path: "unknown", quality: "unknown", videoPaused: false };
|
|
4280
4309
|
var NO_SHARE = { state: "none", reason: null, remedy: null, at: 0 };
|
|
4281
4310
|
var EMPTY = {
|
|
4282
4311
|
phase: "resolving",
|
|
@@ -4285,6 +4314,7 @@ var EMPTY = {
|
|
|
4285
4314
|
roomName: null,
|
|
4286
4315
|
connection: "stable",
|
|
4287
4316
|
connectionBanner: false,
|
|
4317
|
+
localNetwork: UNKNOWN_LOCAL_NETWORK,
|
|
4288
4318
|
share: NO_SHARE,
|
|
4289
4319
|
call: null,
|
|
4290
4320
|
waiting: null,
|
|
@@ -5866,6 +5896,14 @@ function applyModeration(store, payload, now) {
|
|
|
5866
5896
|
}
|
|
5867
5897
|
return HANDLED;
|
|
5868
5898
|
}
|
|
5899
|
+
case "share-ended": {
|
|
5900
|
+
const reason = text2(payload["reason"]) === "not_allowed" ? "not_allowed" : "replaced";
|
|
5901
|
+
const by = text2(payload["by"]);
|
|
5902
|
+
store.patch({
|
|
5903
|
+
share: { state: "ended", reason, remedy: null, at: now(), ...by !== null ? { by } : {} }
|
|
5904
|
+
});
|
|
5905
|
+
return HANDLED;
|
|
5906
|
+
}
|
|
5869
5907
|
case "muted":
|
|
5870
5908
|
case "ask-unmute":
|
|
5871
5909
|
case "removed": {
|
|
@@ -5963,6 +6001,187 @@ var TRANSCRIPTION_ATTRIBUTES = {
|
|
|
5963
6001
|
speakerIdentity: "lk.transcribed_participant_identity"
|
|
5964
6002
|
};
|
|
5965
6003
|
|
|
6004
|
+
// src/core/network-health.ts
|
|
6005
|
+
function pathOf(reading) {
|
|
6006
|
+
if (reading.relayed === null && reading.tcp === null) return "unknown";
|
|
6007
|
+
return reading.relayed === true || reading.tcp === true ? "restricted" : "direct";
|
|
6008
|
+
}
|
|
6009
|
+
var POOR_RTT_MS = 300;
|
|
6010
|
+
var RECOVERED_RTT_MS = 220;
|
|
6011
|
+
var POOR_LOSS = 0.08;
|
|
6012
|
+
var RECOVERED_LOSS = 0.03;
|
|
6013
|
+
function createSelfAssessor(options = {}) {
|
|
6014
|
+
const poorSamples = options.poorSamples ?? 2;
|
|
6015
|
+
const goodSamples = options.goodSamples ?? 3;
|
|
6016
|
+
let quality = "unknown";
|
|
6017
|
+
let badRun = 0;
|
|
6018
|
+
let goodRun = 0;
|
|
6019
|
+
return {
|
|
6020
|
+
push(reading) {
|
|
6021
|
+
const { rttMs, lossRatio } = reading;
|
|
6022
|
+
if (rttMs === null && lossRatio === null) return quality;
|
|
6023
|
+
const bad = rttMs !== null && rttMs >= POOR_RTT_MS || lossRatio !== null && lossRatio >= POOR_LOSS;
|
|
6024
|
+
const clear = (rttMs === null || rttMs < RECOVERED_RTT_MS) && (lossRatio === null || lossRatio < RECOVERED_LOSS);
|
|
6025
|
+
badRun = bad ? badRun + 1 : 0;
|
|
6026
|
+
goodRun = clear ? goodRun + 1 : 0;
|
|
6027
|
+
if (badRun >= poorSamples) quality = "poor";
|
|
6028
|
+
else if (quality !== "poor" && !bad) quality = "good";
|
|
6029
|
+
else if (quality === "poor" && goodRun >= goodSamples) quality = "good";
|
|
6030
|
+
return quality;
|
|
6031
|
+
},
|
|
6032
|
+
current: () => quality,
|
|
6033
|
+
reset() {
|
|
6034
|
+
quality = "unknown";
|
|
6035
|
+
badRun = 0;
|
|
6036
|
+
goodRun = 0;
|
|
6037
|
+
}
|
|
6038
|
+
};
|
|
6039
|
+
}
|
|
6040
|
+
var RANK = {
|
|
6041
|
+
lost: 0,
|
|
6042
|
+
poor: 1,
|
|
6043
|
+
good: 2,
|
|
6044
|
+
excellent: 3,
|
|
6045
|
+
unknown: 4
|
|
6046
|
+
};
|
|
6047
|
+
function worstQuality(a, b) {
|
|
6048
|
+
if (b === null || b === void 0 || b === "unknown") return a;
|
|
6049
|
+
if (a === "unknown") return b;
|
|
6050
|
+
return RANK[b] < RANK[a] ? b : a;
|
|
6051
|
+
}
|
|
6052
|
+
var ALIVE_TOPIC = "mx.alive";
|
|
6053
|
+
function encodeBeat(beat) {
|
|
6054
|
+
return JSON.stringify({ q: beat.q, vp: beat.vp });
|
|
6055
|
+
}
|
|
6056
|
+
function decodeBeat(payload) {
|
|
6057
|
+
try {
|
|
6058
|
+
const raw = JSON.parse(payload);
|
|
6059
|
+
const q = raw.q === "good" || raw.q === "poor" ? raw.q : "unknown";
|
|
6060
|
+
return { q, vp: raw.vp === true };
|
|
6061
|
+
} catch {
|
|
6062
|
+
return null;
|
|
6063
|
+
}
|
|
6064
|
+
}
|
|
6065
|
+
function heartbeatIntervalMs(roomSize) {
|
|
6066
|
+
return 2e3 * Math.max(1, Math.ceil(Math.max(1, roomSize) / 25));
|
|
6067
|
+
}
|
|
6068
|
+
function staleAfterMs(roomSize) {
|
|
6069
|
+
return heartbeatIntervalMs(roomSize) * 3 + 1e3;
|
|
6070
|
+
}
|
|
6071
|
+
function createLivenessTracker(now) {
|
|
6072
|
+
const lastHeard = /* @__PURE__ */ new Map();
|
|
6073
|
+
const beats = /* @__PURE__ */ new Map();
|
|
6074
|
+
return {
|
|
6075
|
+
heard(who, beat) {
|
|
6076
|
+
lastHeard.set(who, now());
|
|
6077
|
+
if (beat !== null) beats.set(who, beat);
|
|
6078
|
+
},
|
|
6079
|
+
stale(present, roomSize) {
|
|
6080
|
+
const limit = staleAfterMs(roomSize);
|
|
6081
|
+
const at = now();
|
|
6082
|
+
const out = /* @__PURE__ */ new Set();
|
|
6083
|
+
for (const who of present) {
|
|
6084
|
+
const heard = lastHeard.get(who);
|
|
6085
|
+
if (heard !== void 0 && at - heard > limit) out.add(who);
|
|
6086
|
+
}
|
|
6087
|
+
return out;
|
|
6088
|
+
},
|
|
6089
|
+
reported: (who) => beats.get(who) ?? null,
|
|
6090
|
+
restartClock() {
|
|
6091
|
+
const at = now();
|
|
6092
|
+
for (const who of lastHeard.keys()) lastHeard.set(who, at);
|
|
6093
|
+
},
|
|
6094
|
+
forget(who) {
|
|
6095
|
+
lastHeard.delete(who);
|
|
6096
|
+
beats.delete(who);
|
|
6097
|
+
},
|
|
6098
|
+
clear() {
|
|
6099
|
+
lastHeard.clear();
|
|
6100
|
+
beats.clear();
|
|
6101
|
+
}
|
|
6102
|
+
};
|
|
6103
|
+
}
|
|
6104
|
+
function createVideoAutoOff(afterMs, now) {
|
|
6105
|
+
let poorSince = null;
|
|
6106
|
+
let armed = true;
|
|
6107
|
+
return {
|
|
6108
|
+
step(quality, cameraOn) {
|
|
6109
|
+
if (quality === "good") {
|
|
6110
|
+
poorSince = null;
|
|
6111
|
+
armed = true;
|
|
6112
|
+
return null;
|
|
6113
|
+
}
|
|
6114
|
+
if (quality !== "poor" || !cameraOn) {
|
|
6115
|
+
poorSince = quality === "poor" ? poorSince : null;
|
|
6116
|
+
return null;
|
|
6117
|
+
}
|
|
6118
|
+
const at = now();
|
|
6119
|
+
if (poorSince === null) poorSince = at;
|
|
6120
|
+
const limit = afterMs();
|
|
6121
|
+
if (!armed || limit <= 0 || at - poorSince < limit) return null;
|
|
6122
|
+
armed = false;
|
|
6123
|
+
return "pause";
|
|
6124
|
+
},
|
|
6125
|
+
retried() {
|
|
6126
|
+
armed = false;
|
|
6127
|
+
},
|
|
6128
|
+
reset() {
|
|
6129
|
+
poorSince = null;
|
|
6130
|
+
armed = true;
|
|
6131
|
+
}
|
|
6132
|
+
};
|
|
6133
|
+
}
|
|
6134
|
+
function readingFromStats(entries) {
|
|
6135
|
+
const byId2 = /* @__PURE__ */ new Map();
|
|
6136
|
+
for (const entry of entries) byId2.set(entry.id, entry);
|
|
6137
|
+
let pair;
|
|
6138
|
+
for (const entry of byId2.values()) {
|
|
6139
|
+
if (entry.type === "transport" && typeof entry["selectedCandidatePairId"] === "string") {
|
|
6140
|
+
pair = byId2.get(entry["selectedCandidatePairId"]);
|
|
6141
|
+
if (pair !== void 0) break;
|
|
6142
|
+
}
|
|
6143
|
+
}
|
|
6144
|
+
if (pair === void 0) {
|
|
6145
|
+
for (const entry of byId2.values()) {
|
|
6146
|
+
if (entry.type === "candidate-pair" && entry["nominated"] === true && entry["state"] === "succeeded") {
|
|
6147
|
+
pair = entry;
|
|
6148
|
+
break;
|
|
6149
|
+
}
|
|
6150
|
+
}
|
|
6151
|
+
}
|
|
6152
|
+
let lossRatio = null;
|
|
6153
|
+
for (const entry of byId2.values()) {
|
|
6154
|
+
if (entry.type === "remote-inbound-rtp" && typeof entry["fractionLost"] === "number") {
|
|
6155
|
+
lossRatio = Math.max(lossRatio ?? 0, entry["fractionLost"]);
|
|
6156
|
+
}
|
|
6157
|
+
}
|
|
6158
|
+
if (pair === void 0) return { rttMs: null, lossRatio, relayed: null, tcp: null };
|
|
6159
|
+
const rtt = pair["currentRoundTripTime"];
|
|
6160
|
+
const local = typeof pair["localCandidateId"] === "string" ? byId2.get(pair["localCandidateId"]) : void 0;
|
|
6161
|
+
const candidateType = local?.["candidateType"];
|
|
6162
|
+
const protocol = local?.["protocol"];
|
|
6163
|
+
const relayProtocol = local?.["relayProtocol"];
|
|
6164
|
+
return {
|
|
6165
|
+
rttMs: typeof rtt === "number" ? Math.round(rtt * 1e3) : null,
|
|
6166
|
+
lossRatio,
|
|
6167
|
+
relayed: typeof candidateType === "string" ? candidateType === "relay" : null,
|
|
6168
|
+
tcp: typeof protocol === "string" || typeof relayProtocol === "string" ? protocol === "tcp" || relayProtocol === "tcp" || relayProtocol === "tls" : null
|
|
6169
|
+
};
|
|
6170
|
+
}
|
|
6171
|
+
function mergeReadings(readings) {
|
|
6172
|
+
const max = (values) => {
|
|
6173
|
+
const known = values.filter((v) => v !== null);
|
|
6174
|
+
return known.length === 0 ? null : Math.max(...known);
|
|
6175
|
+
};
|
|
6176
|
+
const any = (values) => values.every((v) => v === null) ? null : values.some((v) => v === true);
|
|
6177
|
+
return {
|
|
6178
|
+
rttMs: max(readings.map((r) => r.rttMs)),
|
|
6179
|
+
lossRatio: max(readings.map((r) => r.lossRatio)),
|
|
6180
|
+
relayed: any(readings.map((r) => r.relayed)),
|
|
6181
|
+
tcp: any(readings.map((r) => r.tcp))
|
|
6182
|
+
};
|
|
6183
|
+
}
|
|
6184
|
+
|
|
5966
6185
|
// src/core/room.ts
|
|
5967
6186
|
function SCREEN_SHARE_BLOCKED_REMEDY_FOR(remedy) {
|
|
5968
6187
|
return /administrator|policy/i.test(remedy) ? remedy : SCREEN_SHARE_BLOCKED_REMEDY;
|
|
@@ -6055,6 +6274,76 @@ function createRoomEngine(options) {
|
|
|
6055
6274
|
return personConnections.get(personOf(who)) ?? [who];
|
|
6056
6275
|
}
|
|
6057
6276
|
const departures = /* @__PURE__ */ new Map();
|
|
6277
|
+
const liveness = createLivenessTracker(now);
|
|
6278
|
+
const assessor = createSelfAssessor();
|
|
6279
|
+
const autoOff = createVideoAutoOff(() => connectionPolicy.videoAutoOffAfterPoorSeconds * 1e3, now);
|
|
6280
|
+
const lkQuality = /* @__PURE__ */ new Map();
|
|
6281
|
+
let silent = /* @__PURE__ */ new Set();
|
|
6282
|
+
let healthTimer = null;
|
|
6283
|
+
let lastBeatAt = 0;
|
|
6284
|
+
let wasUnstable = false;
|
|
6285
|
+
function patchLocalNetwork(next) {
|
|
6286
|
+
const before = store.snapshot().localNetwork;
|
|
6287
|
+
const merged = { ...before, ...next };
|
|
6288
|
+
if (merged.path !== before.path || merged.quality !== before.quality || merged.videoPaused !== before.videoPaused) {
|
|
6289
|
+
store.patch({ localNetwork: merged });
|
|
6290
|
+
}
|
|
6291
|
+
}
|
|
6292
|
+
function effectiveQuality(person, connection, isLocal) {
|
|
6293
|
+
const fromServer = lkQuality.get(person) ?? "unknown";
|
|
6294
|
+
if (isLocal) return worstQuality(fromServer, assessor.current());
|
|
6295
|
+
return worstQuality(fromServer, connection === void 0 ? null : liveness.reported(connection)?.q);
|
|
6296
|
+
}
|
|
6297
|
+
function refreshQualities() {
|
|
6298
|
+
const snap = store.snapshot();
|
|
6299
|
+
for (const p of snap.participants) {
|
|
6300
|
+
if (p.reconnecting === true) continue;
|
|
6301
|
+
const next = effectiveQuality(p.identity, mainConnection.get(p.identity), p.isLocal);
|
|
6302
|
+
if (next !== "unknown" && next !== p.quality) store.setQuality(p.identity, next);
|
|
6303
|
+
}
|
|
6304
|
+
}
|
|
6305
|
+
function stopHealth() {
|
|
6306
|
+
if (healthTimer !== null) clearInterval(healthTimer);
|
|
6307
|
+
healthTimer = null;
|
|
6308
|
+
liveness.clear();
|
|
6309
|
+
assessor.reset();
|
|
6310
|
+
autoOff.reset();
|
|
6311
|
+
silent = /* @__PURE__ */ new Set();
|
|
6312
|
+
lkQuality.clear();
|
|
6313
|
+
lastBeatAt = 0;
|
|
6314
|
+
wasUnstable = false;
|
|
6315
|
+
if (store.snapshot().localNetwork !== UNKNOWN_LOCAL_NETWORK) store.patch({ localNetwork: UNKNOWN_LOCAL_NETWORK });
|
|
6316
|
+
}
|
|
6317
|
+
function healthTick() {
|
|
6318
|
+
const snap = store.snapshot();
|
|
6319
|
+
const target = driver;
|
|
6320
|
+
if (disposed || target === null || snap.phase !== "in_call") return;
|
|
6321
|
+
if (snap.connection !== "stable") {
|
|
6322
|
+
wasUnstable = true;
|
|
6323
|
+
return;
|
|
6324
|
+
}
|
|
6325
|
+
if (wasUnstable) {
|
|
6326
|
+
wasUnstable = false;
|
|
6327
|
+
liveness.restartClock();
|
|
6328
|
+
}
|
|
6329
|
+
const size = liveRoster.length;
|
|
6330
|
+
const at = now();
|
|
6331
|
+
if (at - lastBeatAt >= heartbeatIntervalMs(size)) {
|
|
6332
|
+
lastBeatAt = at;
|
|
6333
|
+
const beat = encodeBeat({ q: assessor.current(), vp: snap.localNetwork.videoPaused });
|
|
6334
|
+
void target.publishData(ALIVE_TOPIC, beat).catch(() => void 0);
|
|
6335
|
+
}
|
|
6336
|
+
const remotes = liveRoster.filter((p) => !p.isLocal).map((p) => p.identity);
|
|
6337
|
+
const next = liveness.stale(remotes, size);
|
|
6338
|
+
if (next.size !== silent.size || [...next].some((who) => !silent.has(who))) {
|
|
6339
|
+
silent = next;
|
|
6340
|
+
projectRoster(liveRoster, new Date(at).toISOString());
|
|
6341
|
+
}
|
|
6342
|
+
}
|
|
6343
|
+
function startHealth() {
|
|
6344
|
+
if (healthTimer !== null) return;
|
|
6345
|
+
healthTimer = setInterval(healthTick, 1e3);
|
|
6346
|
+
}
|
|
6058
6347
|
function requestAsNow() {
|
|
6059
6348
|
if (lastRequest === null) return null;
|
|
6060
6349
|
const media2 = store.snapshot().media;
|
|
@@ -6073,6 +6362,7 @@ function createRoomEngine(options) {
|
|
|
6073
6362
|
media.onDisconnected();
|
|
6074
6363
|
store.patch({ share: NO_SHARE, connection: "stable" });
|
|
6075
6364
|
clearGhosts();
|
|
6365
|
+
stopHealth();
|
|
6076
6366
|
await target?.disconnect();
|
|
6077
6367
|
}
|
|
6078
6368
|
async function leaveRoom() {
|
|
@@ -6164,6 +6454,8 @@ function createRoomEngine(options) {
|
|
|
6164
6454
|
},
|
|
6165
6455
|
participantLeft(who, reason) {
|
|
6166
6456
|
departures.set(who, reason);
|
|
6457
|
+
liveness.forget(who);
|
|
6458
|
+
silent.delete(who);
|
|
6167
6459
|
},
|
|
6168
6460
|
roomMetadata(metadata) {
|
|
6169
6461
|
let parsed = {};
|
|
@@ -6197,7 +6489,27 @@ function createRoomEngine(options) {
|
|
|
6197
6489
|
store.setSpeaking([...new Set(identities.map((who) => personOf(who)))]);
|
|
6198
6490
|
},
|
|
6199
6491
|
quality(who, value) {
|
|
6200
|
-
|
|
6492
|
+
const person = personOf(who);
|
|
6493
|
+
lkQuality.set(person, value);
|
|
6494
|
+
const local = store.snapshot().participants.find((p) => p.identity === person)?.isLocal === true;
|
|
6495
|
+
store.setQuality(person, value === "lost" ? value : effectiveQuality(person, who, local));
|
|
6496
|
+
},
|
|
6497
|
+
networkReading(reading) {
|
|
6498
|
+
const snap = store.snapshot();
|
|
6499
|
+
if (snap.phase !== "in_call" || snap.connection !== "stable") return;
|
|
6500
|
+
startHealth();
|
|
6501
|
+
const quality = assessor.push(reading);
|
|
6502
|
+
const path = pathOf(reading);
|
|
6503
|
+
patchLocalNetwork({ quality, ...path !== "unknown" ? { path } : {} });
|
|
6504
|
+
refreshQualities();
|
|
6505
|
+
const cameraOn = snap.media.camera.enabled;
|
|
6506
|
+
if (autoOff.step(quality, cameraOn) === "pause" && driver !== null) {
|
|
6507
|
+
const target = driver;
|
|
6508
|
+
report("info", "meet: the camera was paused because the connection is poor", "The audio is kept; Try video again turns it back on.");
|
|
6509
|
+
void enableTrack(target, "camera", false).then((state) => {
|
|
6510
|
+
if (!state.enabled) patchLocalNetwork({ videoPaused: true });
|
|
6511
|
+
});
|
|
6512
|
+
}
|
|
6201
6513
|
},
|
|
6202
6514
|
connectionState(state) {
|
|
6203
6515
|
supervisor.driverState(state);
|
|
@@ -6292,6 +6604,22 @@ function createRoomEngine(options) {
|
|
|
6292
6604
|
return;
|
|
6293
6605
|
}
|
|
6294
6606
|
const sender = personOf(from ?? "");
|
|
6607
|
+
if (from !== null) {
|
|
6608
|
+
const beat = topic === ALIVE_TOPIC ? decodeBeat(payload) : null;
|
|
6609
|
+
const before = liveness.reported(from);
|
|
6610
|
+
liveness.heard(from, beat);
|
|
6611
|
+
if (topic === ALIVE_TOPIC) {
|
|
6612
|
+
startHealth();
|
|
6613
|
+
if (beat !== null && (before?.q !== beat.q || before?.vp !== beat.vp || silent.has(from))) {
|
|
6614
|
+
silent.delete(from);
|
|
6615
|
+
projectRoster(liveRoster, new Date(now()).toISOString());
|
|
6616
|
+
refreshQualities();
|
|
6617
|
+
} else if (silent.delete(from)) {
|
|
6618
|
+
projectRoster(liveRoster, new Date(now()).toISOString());
|
|
6619
|
+
}
|
|
6620
|
+
return;
|
|
6621
|
+
}
|
|
6622
|
+
}
|
|
6295
6623
|
switch (topic) {
|
|
6296
6624
|
case DATA_TOPICS.reaction:
|
|
6297
6625
|
if (typeof envelope.emoji === "string") {
|
|
@@ -6534,7 +6862,16 @@ function createRoomEngine(options) {
|
|
|
6534
6862
|
store.setParticipants(
|
|
6535
6863
|
[...people.values()].map(({ entry }) => {
|
|
6536
6864
|
const projected = projectParticipant(entry, joinedAt);
|
|
6537
|
-
|
|
6865
|
+
if (entry.reconnecting === true || !entry.isLocal && silent.has(entry.identity)) {
|
|
6866
|
+
return { ...projected, reconnecting: true, quality: "lost" };
|
|
6867
|
+
}
|
|
6868
|
+
const videoPaused = entry.isLocal ? store.snapshot().localNetwork.videoPaused : liveness.reported(entry.identity)?.vp === true;
|
|
6869
|
+
const quality = effectiveQuality(projected.identity, entry.identity, entry.isLocal);
|
|
6870
|
+
return {
|
|
6871
|
+
...projected,
|
|
6872
|
+
...quality !== "unknown" && quality !== "lost" ? { quality } : {},
|
|
6873
|
+
...videoPaused ? { videoPaused: true } : {}
|
|
6874
|
+
};
|
|
6538
6875
|
})
|
|
6539
6876
|
);
|
|
6540
6877
|
scheduleGhostExpiry();
|
|
@@ -6720,7 +7057,12 @@ function createRoomEngine(options) {
|
|
|
6720
7057
|
},
|
|
6721
7058
|
async setCameraEnabled(enabled) {
|
|
6722
7059
|
if (driver === null) return;
|
|
6723
|
-
await enableTrack(driver, "camera", enabled);
|
|
7060
|
+
const state = await enableTrack(driver, "camera", enabled);
|
|
7061
|
+
if (enabled && state.enabled && store.snapshot().localNetwork.videoPaused) {
|
|
7062
|
+
autoOff.retried();
|
|
7063
|
+
patchLocalNetwork({ videoPaused: false });
|
|
7064
|
+
}
|
|
7065
|
+
if (!enabled && store.snapshot().localNetwork.videoPaused) patchLocalNetwork({ videoPaused: false });
|
|
6724
7066
|
},
|
|
6725
7067
|
async setScreenShareEnabled(enabled) {
|
|
6726
7068
|
if (driver === null) return;
|
|
@@ -6766,7 +7108,8 @@ function createRoomEngine(options) {
|
|
|
6766
7108
|
}
|
|
6767
7109
|
},
|
|
6768
7110
|
dismissShareNotice() {
|
|
6769
|
-
|
|
7111
|
+
const state = store.snapshot().share.state;
|
|
7112
|
+
if (state === "refused" || state === "ended") store.patch({ share: NO_SHARE });
|
|
6770
7113
|
},
|
|
6771
7114
|
async react(emoji) {
|
|
6772
7115
|
const local = store.snapshot().localIdentity;
|
|
@@ -7244,6 +7587,30 @@ function createLiveKitDriver(handlers, options = {}) {
|
|
|
7244
7587
|
let projectRoster = null;
|
|
7245
7588
|
let audioCapture = DEFAULT_AUDIO_PROCESSING;
|
|
7246
7589
|
let videoEffect = { kind: "none" };
|
|
7590
|
+
let statsTimer = null;
|
|
7591
|
+
function stopStatsPoll() {
|
|
7592
|
+
if (statsTimer !== null) clearInterval(statsTimer);
|
|
7593
|
+
statsTimer = null;
|
|
7594
|
+
}
|
|
7595
|
+
function startStatsPoll(target) {
|
|
7596
|
+
stopStatsPoll();
|
|
7597
|
+
if (handlers.networkReading === void 0) return;
|
|
7598
|
+
const sample = async () => {
|
|
7599
|
+
if (room !== target) return;
|
|
7600
|
+
const manager = target.engine.pcManager;
|
|
7601
|
+
if (manager === void 0) return;
|
|
7602
|
+
const readings = [];
|
|
7603
|
+
for (const transport of [manager.publisher, manager.subscriber]) {
|
|
7604
|
+
try {
|
|
7605
|
+
const report = await transport?.getStats();
|
|
7606
|
+
if (report !== void 0) readings.push(readingFromStats(report.values()));
|
|
7607
|
+
} catch {
|
|
7608
|
+
}
|
|
7609
|
+
}
|
|
7610
|
+
if (readings.length > 0 && room === target) handlers.networkReading?.(mergeReadings(readings));
|
|
7611
|
+
};
|
|
7612
|
+
statsTimer = setInterval(() => void sample(), 2e3);
|
|
7613
|
+
}
|
|
7247
7614
|
async function load() {
|
|
7248
7615
|
if (room !== null) return room;
|
|
7249
7616
|
let module2;
|
|
@@ -7318,6 +7685,7 @@ function createLiveKitDriver(handlers, options = {}) {
|
|
|
7318
7685
|
participant?.identity ?? null
|
|
7319
7686
|
);
|
|
7320
7687
|
}).on(RoomEvent.Disconnected, (reason) => {
|
|
7688
|
+
stopStatsPoll();
|
|
7321
7689
|
audioContainer?.remove();
|
|
7322
7690
|
audioContainer = null;
|
|
7323
7691
|
handlers.disconnected(reasonName(reason));
|
|
@@ -7403,6 +7771,7 @@ function createLiveKitDriver(handlers, options = {}) {
|
|
|
7403
7771
|
projectRoster?.();
|
|
7404
7772
|
handlers.roomMetadata?.(target.metadata ?? "");
|
|
7405
7773
|
handlers.audioPlayback?.(target.canPlaybackAudio);
|
|
7774
|
+
startStatsPoll(target);
|
|
7406
7775
|
},
|
|
7407
7776
|
async disconnect() {
|
|
7408
7777
|
if (room === null) return;
|
|
@@ -7413,6 +7782,7 @@ function createLiveKitDriver(handlers, options = {}) {
|
|
|
7413
7782
|
} catch {
|
|
7414
7783
|
}
|
|
7415
7784
|
projectRoster = null;
|
|
7785
|
+
stopStatsPoll();
|
|
7416
7786
|
audioContainer?.remove();
|
|
7417
7787
|
audioContainer = null;
|
|
7418
7788
|
await target.disconnect();
|
|
@@ -8007,7 +8377,7 @@ function observe(input) {
|
|
|
8007
8377
|
const phase = observedPhase(input);
|
|
8008
8378
|
const inCall = phase === "in_call";
|
|
8009
8379
|
const actual = inCall && snapshot !== null ? effectiveRole(snapshot) : "none";
|
|
8010
|
-
const canAdmit =
|
|
8380
|
+
const canAdmit = snapshot !== null && mayAdmitNow(actual, snapshot.participants, snapshot.livePolicy);
|
|
8011
8381
|
const role = actual === "none" || snapshot === null ? "none" : observedRole(actual, effectiveStanding(snapshot));
|
|
8012
8382
|
const deviceInPlay = phase === "joining" || inCall;
|
|
8013
8383
|
const region = snapshot?.mediaRegion;
|
|
@@ -8863,6 +9233,13 @@ function requireSession(session, action) {
|
|
|
8863
9233
|
if (session === null) throw noRuntime(action);
|
|
8864
9234
|
return session;
|
|
8865
9235
|
}
|
|
9236
|
+
function otherPresenterOf(snapshot) {
|
|
9237
|
+
if (snapshot === null || snapshot.presenter === null || snapshot.presenter === snapshot.localIdentity) return null;
|
|
9238
|
+
const rule = snapshot.livePolicy.shareConcurrency;
|
|
9239
|
+
if (rule === "multiple") return null;
|
|
9240
|
+
const who = snapshot.participants.find((p) => p.identity === snapshot.presenter);
|
|
9241
|
+
return who === void 0 ? null : { name: who.displayName, rule };
|
|
9242
|
+
}
|
|
8866
9243
|
function useRoom() {
|
|
8867
9244
|
const host = useMeetHost();
|
|
8868
9245
|
const snapshot = useMeetSnapshot();
|
|
@@ -8877,7 +9254,9 @@ function useRoom() {
|
|
|
8877
9254
|
secondDevice: snapshot?.secondDevice ?? null,
|
|
8878
9255
|
local,
|
|
8879
9256
|
// An observer (role `viewer`) publishes nothing: no Share screen to press and be refused (MD-16).
|
|
8880
|
-
|
|
9257
|
+
sharingAllowed: snapshot === null || canShareUnder(snapshot.livePolicy, effectiveRole(snapshot), effectiveStanding(snapshot)),
|
|
9258
|
+
canShareScreen: (engine?.supportsScreenShare() ?? true) && !isObserverRole(snapshot?.grants.role ?? null) && (snapshot === null || canShareUnder(snapshot.livePolicy, effectiveRole(snapshot), effectiveStanding(snapshot))),
|
|
9259
|
+
otherPresenter: otherPresenterOf(snapshot),
|
|
8881
9260
|
join: (0, import_react4.useCallback)(
|
|
8882
9261
|
async (request) => requireEngine(engine, "join").join(request),
|
|
8883
9262
|
[engine]
|
|
@@ -9028,7 +9407,7 @@ function useLobby() {
|
|
|
9028
9407
|
declined: (snapshot?.lobby ?? []).filter((entry) => entry.state === "denied"),
|
|
9029
9408
|
// No runtime means no host powers — `<LobbyPanel>` renders nothing rather
|
|
9030
9409
|
// than an admit button that would throw.
|
|
9031
|
-
canAdmit: session !== null && isHost,
|
|
9410
|
+
canAdmit: session !== null && (isHost || snapshot !== null && snapshot.phase === "in_call" && mayAdmitNow(snapshot.participants.find((p) => p.identity === snapshot.localIdentity)?.role ?? "none", snapshot.participants, snapshot.livePolicy)),
|
|
9032
9411
|
admit: (0, import_react4.useCallback)(
|
|
9033
9412
|
async (entry) => requireSession(session, "admit").admit(entry, true),
|
|
9034
9413
|
[session]
|
|
@@ -11902,7 +12281,8 @@ function ParticipantTile({
|
|
|
11902
12281
|
participant.isLocal ? " (you)" : "",
|
|
11903
12282
|
participant.isAgent ? /* @__PURE__ */ (0, import_jsx_runtime6.jsx)("span", { className: "mx-meet__badge", children: "AI" }) : null,
|
|
11904
12283
|
/* @__PURE__ */ (0, import_jsx_runtime6.jsx)(RoleBadge, { role: participant.role, standing: participant.standing }),
|
|
11905
|
-
participant.reconnecting === true ? /* @__PURE__ */ (0, import_jsx_runtime6.jsx)("span", { className: "mx-meet__badge", children: "Reconnecting\u2026" }) : null
|
|
12284
|
+
participant.reconnecting === true ? /* @__PURE__ */ (0, import_jsx_runtime6.jsx)("span", { className: "mx-meet__badge", children: "Reconnecting\u2026" }) : null,
|
|
12285
|
+
participant.reconnecting !== true && participant.videoPaused === true && !participant.isLocal ? /* @__PURE__ */ (0, import_jsx_runtime6.jsx)("span", { className: "mx-meet__badge", "data-meet-video-paused": "", children: "Video paused to save audio" }) : null
|
|
11906
12286
|
] }),
|
|
11907
12287
|
!participant.micEnabled ? /* @__PURE__ */ (0, import_jsx_runtime6.jsx)(MicOffIcon, { width: 14, height: 14 }) : null,
|
|
11908
12288
|
/* @__PURE__ */ (0, import_jsx_runtime6.jsx)(QualityBars, { quality: participant.quality })
|
|
@@ -11992,6 +12372,8 @@ function ControlBar({ onLeave }) {
|
|
|
11992
12372
|
const queue = useHandQueue();
|
|
11993
12373
|
const micUnavailable = useLocalMedia().microphone.unavailable;
|
|
11994
12374
|
const live = useLivePolicy();
|
|
12375
|
+
const core = useMeetCore();
|
|
12376
|
+
const [askPresentInstead, setAskPresentInstead] = (0, import_react10.useState)(false);
|
|
11995
12377
|
const unread = useUnreadChat(panel === "chat");
|
|
11996
12378
|
const [whiteboard, setWhiteboard] = (0, import_react10.useState)(false);
|
|
11997
12379
|
const app = useMeetAppPanel();
|
|
@@ -12080,15 +12462,39 @@ function ControlBar({ onLeave }) {
|
|
|
12080
12462
|
}
|
|
12081
12463
|
),
|
|
12082
12464
|
/* @__PURE__ */ (0, import_jsx_runtime6.jsx)(CaptionsControl, {}),
|
|
12083
|
-
room.canShareScreen || room.local?.screenSharing === true ? /* @__PURE__ */ (0, import_jsx_runtime6.
|
|
12084
|
-
|
|
12085
|
-
|
|
12086
|
-
|
|
12087
|
-
|
|
12088
|
-
|
|
12089
|
-
|
|
12090
|
-
|
|
12091
|
-
|
|
12465
|
+
room.canShareScreen || room.local?.screenSharing === true && core.share.state !== "ended" ? /* @__PURE__ */ (0, import_jsx_runtime6.jsxs)("span", { className: "mx-meet__anchor", children: [
|
|
12466
|
+
/* @__PURE__ */ (0, import_jsx_runtime6.jsx)(
|
|
12467
|
+
ControlButton,
|
|
12468
|
+
{
|
|
12469
|
+
label: room.local?.screenSharing === true ? "Stop sharing" : "Share screen",
|
|
12470
|
+
active: room.local?.screenSharing === true,
|
|
12471
|
+
onClick: () => {
|
|
12472
|
+
if (room.local?.screenSharing !== true && room.otherPresenter !== null) setAskPresentInstead(true);
|
|
12473
|
+
else void room.toggleScreenShare();
|
|
12474
|
+
},
|
|
12475
|
+
children: /* @__PURE__ */ (0, import_jsx_runtime6.jsx)(ScreenShareIcon, {})
|
|
12476
|
+
}
|
|
12477
|
+
),
|
|
12478
|
+
askPresentInstead && room.otherPresenter !== null ? /* @__PURE__ */ (0, import_jsx_runtime6.jsxs)("div", { className: "mx-meet__confirm", role: "group", "aria-label": "Someone else is presenting", "data-meet-notice": "share-takeover", children: [
|
|
12479
|
+
/* @__PURE__ */ (0, import_jsx_runtime6.jsxs)("span", { className: "mx-meet__confirm-text", children: [
|
|
12480
|
+
room.otherPresenter.name,
|
|
12481
|
+
" is presenting"
|
|
12482
|
+
] }),
|
|
12483
|
+
/* @__PURE__ */ (0, import_jsx_runtime6.jsx)(
|
|
12484
|
+
"button",
|
|
12485
|
+
{
|
|
12486
|
+
type: "button",
|
|
12487
|
+
className: "mx-meet__primary mx-meet__primary--small",
|
|
12488
|
+
onClick: () => {
|
|
12489
|
+
setAskPresentInstead(false);
|
|
12490
|
+
void room.toggleScreenShare();
|
|
12491
|
+
},
|
|
12492
|
+
children: room.otherPresenter.rule === "one_takeover" ? "Present instead" : "Share anyway"
|
|
12493
|
+
}
|
|
12494
|
+
),
|
|
12495
|
+
/* @__PURE__ */ (0, import_jsx_runtime6.jsx)("button", { type: "button", className: "mx-meet__link", onClick: () => setAskPresentInstead(false), children: "Cancel" })
|
|
12496
|
+
] }) : null
|
|
12497
|
+
] }) : null,
|
|
12092
12498
|
/* @__PURE__ */ (0, import_jsx_runtime6.jsxs)("span", { className: "mx-meet__anchor", children: [
|
|
12093
12499
|
/* @__PURE__ */ (0, import_jsx_runtime6.jsx)(
|
|
12094
12500
|
ControlButton,
|
|
@@ -12185,7 +12591,7 @@ function ControlBar({ onLeave }) {
|
|
|
12185
12591
|
]
|
|
12186
12592
|
}
|
|
12187
12593
|
),
|
|
12188
|
-
!room.canShareScreen && room.local?.screenSharing !== true ? (
|
|
12594
|
+
!room.canShareScreen && room.sharingAllowed && room.local?.screenSharing !== true ? (
|
|
12189
12595
|
// `share-unsupported-mobile`: the control is absent; the menu says where it lives.
|
|
12190
12596
|
/* @__PURE__ */ (0, import_jsx_runtime6.jsxs)("p", { className: "mx-meet__menu-note", role: "note", "data-meet-notice": "share-unsupported", children: [
|
|
12191
12597
|
/* @__PURE__ */ (0, import_jsx_runtime6.jsx)(ScreenShareIcon, { width: 16, height: 16 }),
|
|
@@ -14190,13 +14596,53 @@ function MeetSkinRoom({
|
|
|
14190
14596
|
] }),
|
|
14191
14597
|
core.connectionBanner && core.connection !== "stable" ? /* @__PURE__ */ (0, import_jsx_runtime6.jsx)("p", { className: "mx-meet__banner", role: "status", "data-meet-notice": `connection-${core.connection}`, children: CONNECTION_BANNER[core.connection] }) : null,
|
|
14192
14598
|
/* @__PURE__ */ (0, import_jsx_runtime6.jsx)(ShareNotice, {}),
|
|
14599
|
+
/* @__PURE__ */ (0, import_jsx_runtime6.jsx)(NetworkNotice, {}),
|
|
14193
14600
|
/* @__PURE__ */ (0, import_jsx_runtime6.jsx)(Stage, {}),
|
|
14194
14601
|
/* @__PURE__ */ (0, import_jsx_runtime6.jsx)(Captions, {}),
|
|
14195
14602
|
/* @__PURE__ */ (0, import_jsx_runtime6.jsx)(ControlBar, { ...onLeave !== void 0 ? { onLeave } : {} })
|
|
14196
14603
|
] });
|
|
14197
14604
|
}
|
|
14605
|
+
var RESTRICTED_NOTICE_MS = 3e4;
|
|
14606
|
+
function NetworkNotice() {
|
|
14607
|
+
const { snapshot, connectionBanner } = useMeetCore();
|
|
14608
|
+
const room = useRoom();
|
|
14609
|
+
const network = snapshot?.localNetwork;
|
|
14610
|
+
const [closed, setClosed] = (0, import_react10.useState)(() => /* @__PURE__ */ new Set());
|
|
14611
|
+
const [restrictedSince, setRestrictedSince] = (0, import_react10.useState)(null);
|
|
14612
|
+
const [, setTick] = (0, import_react10.useState)(0);
|
|
14613
|
+
const restricted = network?.path === "restricted";
|
|
14614
|
+
(0, import_react10.useEffect)(() => {
|
|
14615
|
+
if (!restricted || restrictedSince !== null) return;
|
|
14616
|
+
setRestrictedSince(Date.now());
|
|
14617
|
+
const timer = setTimeout(() => setTick((n) => n + 1), RESTRICTED_NOTICE_MS + 50);
|
|
14618
|
+
return () => clearTimeout(timer);
|
|
14619
|
+
}, [restricted, restrictedSince]);
|
|
14620
|
+
if (network === void 0 || snapshot?.phase !== "in_call" || connectionBanner) return null;
|
|
14621
|
+
const close = (code) => () => setClosed((prev) => /* @__PURE__ */ new Set([...prev, code]));
|
|
14622
|
+
const cameraOn = room.local?.cameraEnabled === true;
|
|
14623
|
+
if (network.videoPaused && !cameraOn) {
|
|
14624
|
+
return /* @__PURE__ */ (0, import_jsx_runtime6.jsxs)("p", { className: "mx-meet__banner", role: "status", "data-meet-notice": "video-paused", children: [
|
|
14625
|
+
"Your video is paused to save your audio",
|
|
14626
|
+
/* @__PURE__ */ (0, import_jsx_runtime6.jsx)("button", { type: "button", className: "mx-meet__link", onClick: () => void room.toggleCamera(), children: "Try video again" })
|
|
14627
|
+
] });
|
|
14628
|
+
}
|
|
14629
|
+
if (network.quality === "poor" && !closed.has("connection-poor")) {
|
|
14630
|
+
return /* @__PURE__ */ (0, import_jsx_runtime6.jsxs)("p", { className: "mx-meet__banner", role: "status", "data-meet-notice": "connection-poor", children: [
|
|
14631
|
+
"Your connection is unstable",
|
|
14632
|
+
cameraOn ? /* @__PURE__ */ (0, import_jsx_runtime6.jsx)("button", { type: "button", className: "mx-meet__link", onClick: () => void room.toggleCamera(), children: "Turn off camera" }) : null,
|
|
14633
|
+
/* @__PURE__ */ (0, import_jsx_runtime6.jsx)("button", { type: "button", className: "mx-meet__link", onClick: close("connection-poor"), children: "Dismiss" })
|
|
14634
|
+
] });
|
|
14635
|
+
}
|
|
14636
|
+
if (restricted && !closed.has("restricted-network") && restrictedSince !== null && Date.now() - restrictedSince < RESTRICTED_NOTICE_MS) {
|
|
14637
|
+
return /* @__PURE__ */ (0, import_jsx_runtime6.jsxs)("p", { className: "mx-meet__banner", role: "status", "data-meet-notice": "restricted-network", children: [
|
|
14638
|
+
"Restricted network \xB7 quality may be reduced",
|
|
14639
|
+
/* @__PURE__ */ (0, import_jsx_runtime6.jsx)("button", { type: "button", className: "mx-meet__link", onClick: close("restricted-network"), children: "Dismiss" })
|
|
14640
|
+
] });
|
|
14641
|
+
}
|
|
14642
|
+
return null;
|
|
14643
|
+
}
|
|
14198
14644
|
function ShareNotice() {
|
|
14199
|
-
const { share, actions } = useMeetCore();
|
|
14645
|
+
const { share, actions, snapshot } = useMeetCore();
|
|
14200
14646
|
const [soundHintClosed, setSoundHintClosed] = (0, import_react10.useState)(null);
|
|
14201
14647
|
if (share.state === "presenting" && share.audio === false && soundHintClosed !== share.at) {
|
|
14202
14648
|
return /* @__PURE__ */ (0, import_jsx_runtime6.jsxs)("p", { className: "mx-meet__banner", role: "status", "data-meet-notice": "share-audio-missing", children: [
|
|
@@ -14205,6 +14651,16 @@ function ShareNotice() {
|
|
|
14205
14651
|
/* @__PURE__ */ (0, import_jsx_runtime6.jsx)("button", { type: "button", className: "mx-meet__link", onClick: () => setSoundHintClosed(share.at), children: "Dismiss" })
|
|
14206
14652
|
] });
|
|
14207
14653
|
}
|
|
14654
|
+
if (share.state === "ended") {
|
|
14655
|
+
const replaced = share.reason === "replaced";
|
|
14656
|
+
const rule = snapshot?.livePolicy.shareWhoCan ?? "host_only";
|
|
14657
|
+
const why = replaced ? `${share.by ?? "Someone"} is now presenting` : rule === "presenters" ? "Sharing is turned off. Only presenters can share their screen" : rule === "org" ? "Sharing is turned off. Only people in the organization can share their screen" : "Sharing is turned off. Only the host can share their screen";
|
|
14658
|
+
return /* @__PURE__ */ (0, import_jsx_runtime6.jsxs)("p", { className: "mx-meet__banner", role: "status", "data-meet-notice": replaced ? "share-ended" : "share-unavailable", children: [
|
|
14659
|
+
replaced ? "Your presentation ended" : "Your presentation was stopped",
|
|
14660
|
+
/* @__PURE__ */ (0, import_jsx_runtime6.jsx)("span", { className: "mx-meet__remedy", children: why }),
|
|
14661
|
+
/* @__PURE__ */ (0, import_jsx_runtime6.jsx)("button", { type: "button", className: "mx-meet__link", onClick: () => actions.dismissShareNotice(), children: "Dismiss" })
|
|
14662
|
+
] });
|
|
14663
|
+
}
|
|
14208
14664
|
if (share.state !== "refused") return null;
|
|
14209
14665
|
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";
|
|
14210
14666
|
return /* @__PURE__ */ (0, import_jsx_runtime6.jsxs)("p", { className: "mx-meet__banner", role: "status", "data-meet-notice": "share-unavailable", children: [
|
|
@@ -14623,6 +15079,7 @@ function MeetingRecordBody({
|
|
|
14623
15079
|
}
|
|
14624
15080
|
if (record.loading || record.record === null) {
|
|
14625
15081
|
return /* @__PURE__ */ (0, import_jsx_runtime7.jsx)("div", { className: "mx-meet mx-meet--record", children: /* @__PURE__ */ (0, import_jsx_runtime7.jsxs)("div", { className: "mx-meet__empty", role: "status", children: [
|
|
15082
|
+
/* @__PURE__ */ (0, import_jsx_runtime7.jsx)("p", { className: "mx-meet__empty-title", "data-meet-notice": "meeting-ended", children: "The meeting has ended" }),
|
|
14626
15083
|
/* @__PURE__ */ (0, import_jsx_runtime7.jsx)("p", { className: "mx-meet__empty-title", children: "Opening the meeting record\u2026" }),
|
|
14627
15084
|
/* @__PURE__ */ (0, import_jsx_runtime7.jsx)("p", { className: "mx-meet__empty-body", children: "Reading the summary, the notes and the transcript this meeting left behind." })
|
|
14628
15085
|
] }) });
|