@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.js
CHANGED
|
@@ -147,10 +147,11 @@ function webTabPlatform() {
|
|
|
147
147
|
|
|
148
148
|
// src/core/connection-supervisor.ts
|
|
149
149
|
var DEFAULT_CONNECTION_POLICY = {
|
|
150
|
-
bannerDelayMs:
|
|
150
|
+
bannerDelayMs: 5e3,
|
|
151
151
|
autoRejoin: true,
|
|
152
152
|
autoRejoinMaxAttempts: 5,
|
|
153
|
-
reconnectGraceSeconds: 30
|
|
153
|
+
reconnectGraceSeconds: 30,
|
|
154
|
+
videoAutoOffAfterPoorSeconds: 20
|
|
154
155
|
};
|
|
155
156
|
function clampInt(value, min, max, fallback) {
|
|
156
157
|
if (typeof value !== "number" || !Number.isFinite(value)) return fallback;
|
|
@@ -164,7 +165,8 @@ function connectionPolicyFrom(raw) {
|
|
|
164
165
|
bannerDelayMs: clampInt(row["reconnect_banner_delay_ms"], 0, 1e4, d.bannerDelayMs),
|
|
165
166
|
autoRejoin: typeof row["auto_rejoin"] === "boolean" ? row["auto_rejoin"] : d.autoRejoin,
|
|
166
167
|
autoRejoinMaxAttempts: clampInt(row["auto_rejoin_max_attempts"], 0, 20, d.autoRejoinMaxAttempts),
|
|
167
|
-
reconnectGraceSeconds: clampInt(row["reconnect_grace_seconds"], 0, 600, d.reconnectGraceSeconds)
|
|
168
|
+
reconnectGraceSeconds: clampInt(row["reconnect_grace_seconds"], 0, 600, d.reconnectGraceSeconds),
|
|
169
|
+
videoAutoOffAfterPoorSeconds: clampInt(row["video_auto_off_after_poor_seconds"], 0, 600, d.videoAutoOffAfterPoorSeconds)
|
|
168
170
|
};
|
|
169
171
|
}
|
|
170
172
|
var LIVEKIT_BASE_DELAYS_MS = [0, 300, 1200, 2700, 4800, 7e3];
|
|
@@ -2444,11 +2446,14 @@ function recordingLabel(state) {
|
|
|
2444
2446
|
|
|
2445
2447
|
// src/core/live-policy.ts
|
|
2446
2448
|
var DEFAULT_LIVE_POLICY = {
|
|
2449
|
+
headlessPowers: "members_admit",
|
|
2447
2450
|
chatMode: "everyone",
|
|
2448
2451
|
avLock: "off",
|
|
2449
2452
|
timeLimitMinutes: 0,
|
|
2450
2453
|
timeLimitWarningMinutes: 5,
|
|
2451
|
-
recordingConsent: "notice"
|
|
2454
|
+
recordingConsent: "notice",
|
|
2455
|
+
shareWhoCan: "everyone",
|
|
2456
|
+
shareConcurrency: "one_takeover"
|
|
2452
2457
|
};
|
|
2453
2458
|
function oneOf(value, allowed, fallback) {
|
|
2454
2459
|
return typeof value === "string" && allowed.includes(value) ? value : fallback;
|
|
@@ -2460,6 +2465,7 @@ function whole(value, fallback) {
|
|
|
2460
2465
|
function livePolicyFrom(raw) {
|
|
2461
2466
|
const o = typeof raw === "object" && raw !== null ? raw : {};
|
|
2462
2467
|
return {
|
|
2468
|
+
headlessPowers: oneOf(o["headless_powers"], ["members_admit", "none", "cohosts_only"], DEFAULT_LIVE_POLICY.headlessPowers),
|
|
2463
2469
|
chatMode: oneOf(o["chat_mode"], ["everyone", "hosts_only", "off"], DEFAULT_LIVE_POLICY.chatMode),
|
|
2464
2470
|
avLock: oneOf(o["attendee_av_lock"], ["off", "mic", "camera", "both"], DEFAULT_LIVE_POLICY.avLock),
|
|
2465
2471
|
timeLimitMinutes: whole(o["time_limit_minutes"], 0),
|
|
@@ -2468,17 +2474,33 @@ function livePolicyFrom(raw) {
|
|
|
2468
2474
|
o["recording_consent"],
|
|
2469
2475
|
["notice", "ok_or_leave", "view_only_until_agree"],
|
|
2470
2476
|
DEFAULT_LIVE_POLICY.recordingConsent
|
|
2471
|
-
)
|
|
2477
|
+
),
|
|
2478
|
+
shareWhoCan: oneOf(o["share_who_can"], ["everyone", "host_only", "org", "presenters"], DEFAULT_LIVE_POLICY.shareWhoCan),
|
|
2479
|
+
shareConcurrency: oneOf(o["share_concurrency"], ["one_takeover", "one_replace", "multiple"], DEFAULT_LIVE_POLICY.shareConcurrency)
|
|
2472
2480
|
};
|
|
2473
2481
|
}
|
|
2474
2482
|
function samePolicy(a, b) {
|
|
2475
|
-
return a.chatMode === b.chatMode && a.avLock === b.avLock && a.timeLimitMinutes === b.timeLimitMinutes && a.timeLimitWarningMinutes === b.timeLimitWarningMinutes && a.recordingConsent === b.recordingConsent;
|
|
2483
|
+
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;
|
|
2476
2484
|
}
|
|
2477
2485
|
var MODERATORS = /* @__PURE__ */ new Set(["host", "cohost"]);
|
|
2478
2486
|
function isLockedFor(policy, role, kind) {
|
|
2479
2487
|
if (MODERATORS.has(role)) return false;
|
|
2480
2488
|
return policy.avLock === "both" || (kind === "microphone" ? policy.avLock === "mic" : policy.avLock === "camera");
|
|
2481
2489
|
}
|
|
2490
|
+
function canShareUnder(policy, role, standing) {
|
|
2491
|
+
if (role === "viewer") return false;
|
|
2492
|
+
if (MODERATORS.has(role)) return true;
|
|
2493
|
+
switch (policy.shareWhoCan) {
|
|
2494
|
+
case "host_only":
|
|
2495
|
+
return false;
|
|
2496
|
+
case "presenters":
|
|
2497
|
+
return role === "presenter";
|
|
2498
|
+
case "org":
|
|
2499
|
+
return role === "presenter" || standing === "member";
|
|
2500
|
+
default:
|
|
2501
|
+
return true;
|
|
2502
|
+
}
|
|
2503
|
+
}
|
|
2482
2504
|
function consentPending(policy, role, recordingOn, given) {
|
|
2483
2505
|
return recordingOn && !given && policy.recordingConsent !== "notice" && !MODERATORS.has(role);
|
|
2484
2506
|
}
|
|
@@ -2490,6 +2512,12 @@ function minutesLeft(policy, startedAtMs, nowMs) {
|
|
|
2490
2512
|
if (policy.timeLimitMinutes <= 0 || startedAtMs === null) return null;
|
|
2491
2513
|
return Math.ceil((startedAtMs + policy.timeLimitMinutes * 6e4 - nowMs) / 6e4);
|
|
2492
2514
|
}
|
|
2515
|
+
function mayAdmitNow(role, participants, policy) {
|
|
2516
|
+
if (role === "none") return false;
|
|
2517
|
+
if (role === "host" || role === "cohost") return true;
|
|
2518
|
+
const hostIn = participants.some((p) => p.role === "host" || p.role === "cohost");
|
|
2519
|
+
return !hostIn && policy.headlessPowers === "members_admit";
|
|
2520
|
+
}
|
|
2493
2521
|
|
|
2494
2522
|
// src/core/attendance.ts
|
|
2495
2523
|
var IDLE_PREVIEW = { state: "idle", unavailable: null };
|
|
@@ -3950,6 +3978,7 @@ var NO_GRANTS = {
|
|
|
3950
3978
|
canPublish: false,
|
|
3951
3979
|
canSubscribe: false
|
|
3952
3980
|
};
|
|
3981
|
+
var UNKNOWN_LOCAL_NETWORK = { path: "unknown", quality: "unknown", videoPaused: false };
|
|
3953
3982
|
var NO_SHARE = { state: "none", reason: null, remedy: null, at: 0 };
|
|
3954
3983
|
var EMPTY = {
|
|
3955
3984
|
phase: "resolving",
|
|
@@ -3958,6 +3987,7 @@ var EMPTY = {
|
|
|
3958
3987
|
roomName: null,
|
|
3959
3988
|
connection: "stable",
|
|
3960
3989
|
connectionBanner: false,
|
|
3990
|
+
localNetwork: UNKNOWN_LOCAL_NETWORK,
|
|
3961
3991
|
share: NO_SHARE,
|
|
3962
3992
|
call: null,
|
|
3963
3993
|
waiting: null,
|
|
@@ -5541,6 +5571,14 @@ function applyModeration(store, payload, now) {
|
|
|
5541
5571
|
}
|
|
5542
5572
|
return HANDLED;
|
|
5543
5573
|
}
|
|
5574
|
+
case "share-ended": {
|
|
5575
|
+
const reason = text2(payload["reason"]) === "not_allowed" ? "not_allowed" : "replaced";
|
|
5576
|
+
const by = text2(payload["by"]);
|
|
5577
|
+
store.patch({
|
|
5578
|
+
share: { state: "ended", reason, remedy: null, at: now(), ...by !== null ? { by } : {} }
|
|
5579
|
+
});
|
|
5580
|
+
return HANDLED;
|
|
5581
|
+
}
|
|
5544
5582
|
case "muted":
|
|
5545
5583
|
case "ask-unmute":
|
|
5546
5584
|
case "removed": {
|
|
@@ -5638,6 +5676,187 @@ var TRANSCRIPTION_ATTRIBUTES = {
|
|
|
5638
5676
|
speakerIdentity: "lk.transcribed_participant_identity"
|
|
5639
5677
|
};
|
|
5640
5678
|
|
|
5679
|
+
// src/core/network-health.ts
|
|
5680
|
+
function pathOf(reading) {
|
|
5681
|
+
if (reading.relayed === null && reading.tcp === null) return "unknown";
|
|
5682
|
+
return reading.relayed === true || reading.tcp === true ? "restricted" : "direct";
|
|
5683
|
+
}
|
|
5684
|
+
var POOR_RTT_MS = 300;
|
|
5685
|
+
var RECOVERED_RTT_MS = 220;
|
|
5686
|
+
var POOR_LOSS = 0.08;
|
|
5687
|
+
var RECOVERED_LOSS = 0.03;
|
|
5688
|
+
function createSelfAssessor(options = {}) {
|
|
5689
|
+
const poorSamples = options.poorSamples ?? 2;
|
|
5690
|
+
const goodSamples = options.goodSamples ?? 3;
|
|
5691
|
+
let quality = "unknown";
|
|
5692
|
+
let badRun = 0;
|
|
5693
|
+
let goodRun = 0;
|
|
5694
|
+
return {
|
|
5695
|
+
push(reading) {
|
|
5696
|
+
const { rttMs, lossRatio } = reading;
|
|
5697
|
+
if (rttMs === null && lossRatio === null) return quality;
|
|
5698
|
+
const bad = rttMs !== null && rttMs >= POOR_RTT_MS || lossRatio !== null && lossRatio >= POOR_LOSS;
|
|
5699
|
+
const clear = (rttMs === null || rttMs < RECOVERED_RTT_MS) && (lossRatio === null || lossRatio < RECOVERED_LOSS);
|
|
5700
|
+
badRun = bad ? badRun + 1 : 0;
|
|
5701
|
+
goodRun = clear ? goodRun + 1 : 0;
|
|
5702
|
+
if (badRun >= poorSamples) quality = "poor";
|
|
5703
|
+
else if (quality !== "poor" && !bad) quality = "good";
|
|
5704
|
+
else if (quality === "poor" && goodRun >= goodSamples) quality = "good";
|
|
5705
|
+
return quality;
|
|
5706
|
+
},
|
|
5707
|
+
current: () => quality,
|
|
5708
|
+
reset() {
|
|
5709
|
+
quality = "unknown";
|
|
5710
|
+
badRun = 0;
|
|
5711
|
+
goodRun = 0;
|
|
5712
|
+
}
|
|
5713
|
+
};
|
|
5714
|
+
}
|
|
5715
|
+
var RANK = {
|
|
5716
|
+
lost: 0,
|
|
5717
|
+
poor: 1,
|
|
5718
|
+
good: 2,
|
|
5719
|
+
excellent: 3,
|
|
5720
|
+
unknown: 4
|
|
5721
|
+
};
|
|
5722
|
+
function worstQuality(a, b) {
|
|
5723
|
+
if (b === null || b === void 0 || b === "unknown") return a;
|
|
5724
|
+
if (a === "unknown") return b;
|
|
5725
|
+
return RANK[b] < RANK[a] ? b : a;
|
|
5726
|
+
}
|
|
5727
|
+
var ALIVE_TOPIC = "mx.alive";
|
|
5728
|
+
function encodeBeat(beat) {
|
|
5729
|
+
return JSON.stringify({ q: beat.q, vp: beat.vp });
|
|
5730
|
+
}
|
|
5731
|
+
function decodeBeat(payload) {
|
|
5732
|
+
try {
|
|
5733
|
+
const raw = JSON.parse(payload);
|
|
5734
|
+
const q = raw.q === "good" || raw.q === "poor" ? raw.q : "unknown";
|
|
5735
|
+
return { q, vp: raw.vp === true };
|
|
5736
|
+
} catch {
|
|
5737
|
+
return null;
|
|
5738
|
+
}
|
|
5739
|
+
}
|
|
5740
|
+
function heartbeatIntervalMs(roomSize) {
|
|
5741
|
+
return 2e3 * Math.max(1, Math.ceil(Math.max(1, roomSize) / 25));
|
|
5742
|
+
}
|
|
5743
|
+
function staleAfterMs(roomSize) {
|
|
5744
|
+
return heartbeatIntervalMs(roomSize) * 3 + 1e3;
|
|
5745
|
+
}
|
|
5746
|
+
function createLivenessTracker(now) {
|
|
5747
|
+
const lastHeard = /* @__PURE__ */ new Map();
|
|
5748
|
+
const beats = /* @__PURE__ */ new Map();
|
|
5749
|
+
return {
|
|
5750
|
+
heard(who, beat) {
|
|
5751
|
+
lastHeard.set(who, now());
|
|
5752
|
+
if (beat !== null) beats.set(who, beat);
|
|
5753
|
+
},
|
|
5754
|
+
stale(present, roomSize) {
|
|
5755
|
+
const limit = staleAfterMs(roomSize);
|
|
5756
|
+
const at = now();
|
|
5757
|
+
const out = /* @__PURE__ */ new Set();
|
|
5758
|
+
for (const who of present) {
|
|
5759
|
+
const heard = lastHeard.get(who);
|
|
5760
|
+
if (heard !== void 0 && at - heard > limit) out.add(who);
|
|
5761
|
+
}
|
|
5762
|
+
return out;
|
|
5763
|
+
},
|
|
5764
|
+
reported: (who) => beats.get(who) ?? null,
|
|
5765
|
+
restartClock() {
|
|
5766
|
+
const at = now();
|
|
5767
|
+
for (const who of lastHeard.keys()) lastHeard.set(who, at);
|
|
5768
|
+
},
|
|
5769
|
+
forget(who) {
|
|
5770
|
+
lastHeard.delete(who);
|
|
5771
|
+
beats.delete(who);
|
|
5772
|
+
},
|
|
5773
|
+
clear() {
|
|
5774
|
+
lastHeard.clear();
|
|
5775
|
+
beats.clear();
|
|
5776
|
+
}
|
|
5777
|
+
};
|
|
5778
|
+
}
|
|
5779
|
+
function createVideoAutoOff(afterMs, now) {
|
|
5780
|
+
let poorSince = null;
|
|
5781
|
+
let armed = true;
|
|
5782
|
+
return {
|
|
5783
|
+
step(quality, cameraOn) {
|
|
5784
|
+
if (quality === "good") {
|
|
5785
|
+
poorSince = null;
|
|
5786
|
+
armed = true;
|
|
5787
|
+
return null;
|
|
5788
|
+
}
|
|
5789
|
+
if (quality !== "poor" || !cameraOn) {
|
|
5790
|
+
poorSince = quality === "poor" ? poorSince : null;
|
|
5791
|
+
return null;
|
|
5792
|
+
}
|
|
5793
|
+
const at = now();
|
|
5794
|
+
if (poorSince === null) poorSince = at;
|
|
5795
|
+
const limit = afterMs();
|
|
5796
|
+
if (!armed || limit <= 0 || at - poorSince < limit) return null;
|
|
5797
|
+
armed = false;
|
|
5798
|
+
return "pause";
|
|
5799
|
+
},
|
|
5800
|
+
retried() {
|
|
5801
|
+
armed = false;
|
|
5802
|
+
},
|
|
5803
|
+
reset() {
|
|
5804
|
+
poorSince = null;
|
|
5805
|
+
armed = true;
|
|
5806
|
+
}
|
|
5807
|
+
};
|
|
5808
|
+
}
|
|
5809
|
+
function readingFromStats(entries) {
|
|
5810
|
+
const byId2 = /* @__PURE__ */ new Map();
|
|
5811
|
+
for (const entry of entries) byId2.set(entry.id, entry);
|
|
5812
|
+
let pair;
|
|
5813
|
+
for (const entry of byId2.values()) {
|
|
5814
|
+
if (entry.type === "transport" && typeof entry["selectedCandidatePairId"] === "string") {
|
|
5815
|
+
pair = byId2.get(entry["selectedCandidatePairId"]);
|
|
5816
|
+
if (pair !== void 0) break;
|
|
5817
|
+
}
|
|
5818
|
+
}
|
|
5819
|
+
if (pair === void 0) {
|
|
5820
|
+
for (const entry of byId2.values()) {
|
|
5821
|
+
if (entry.type === "candidate-pair" && entry["nominated"] === true && entry["state"] === "succeeded") {
|
|
5822
|
+
pair = entry;
|
|
5823
|
+
break;
|
|
5824
|
+
}
|
|
5825
|
+
}
|
|
5826
|
+
}
|
|
5827
|
+
let lossRatio = null;
|
|
5828
|
+
for (const entry of byId2.values()) {
|
|
5829
|
+
if (entry.type === "remote-inbound-rtp" && typeof entry["fractionLost"] === "number") {
|
|
5830
|
+
lossRatio = Math.max(lossRatio ?? 0, entry["fractionLost"]);
|
|
5831
|
+
}
|
|
5832
|
+
}
|
|
5833
|
+
if (pair === void 0) return { rttMs: null, lossRatio, relayed: null, tcp: null };
|
|
5834
|
+
const rtt = pair["currentRoundTripTime"];
|
|
5835
|
+
const local = typeof pair["localCandidateId"] === "string" ? byId2.get(pair["localCandidateId"]) : void 0;
|
|
5836
|
+
const candidateType = local?.["candidateType"];
|
|
5837
|
+
const protocol = local?.["protocol"];
|
|
5838
|
+
const relayProtocol = local?.["relayProtocol"];
|
|
5839
|
+
return {
|
|
5840
|
+
rttMs: typeof rtt === "number" ? Math.round(rtt * 1e3) : null,
|
|
5841
|
+
lossRatio,
|
|
5842
|
+
relayed: typeof candidateType === "string" ? candidateType === "relay" : null,
|
|
5843
|
+
tcp: typeof protocol === "string" || typeof relayProtocol === "string" ? protocol === "tcp" || relayProtocol === "tcp" || relayProtocol === "tls" : null
|
|
5844
|
+
};
|
|
5845
|
+
}
|
|
5846
|
+
function mergeReadings(readings) {
|
|
5847
|
+
const max = (values) => {
|
|
5848
|
+
const known = values.filter((v) => v !== null);
|
|
5849
|
+
return known.length === 0 ? null : Math.max(...known);
|
|
5850
|
+
};
|
|
5851
|
+
const any = (values) => values.every((v) => v === null) ? null : values.some((v) => v === true);
|
|
5852
|
+
return {
|
|
5853
|
+
rttMs: max(readings.map((r) => r.rttMs)),
|
|
5854
|
+
lossRatio: max(readings.map((r) => r.lossRatio)),
|
|
5855
|
+
relayed: any(readings.map((r) => r.relayed)),
|
|
5856
|
+
tcp: any(readings.map((r) => r.tcp))
|
|
5857
|
+
};
|
|
5858
|
+
}
|
|
5859
|
+
|
|
5641
5860
|
// src/core/room.ts
|
|
5642
5861
|
function SCREEN_SHARE_BLOCKED_REMEDY_FOR(remedy) {
|
|
5643
5862
|
return /administrator|policy/i.test(remedy) ? remedy : SCREEN_SHARE_BLOCKED_REMEDY;
|
|
@@ -5730,6 +5949,76 @@ function createRoomEngine(options) {
|
|
|
5730
5949
|
return personConnections.get(personOf(who)) ?? [who];
|
|
5731
5950
|
}
|
|
5732
5951
|
const departures = /* @__PURE__ */ new Map();
|
|
5952
|
+
const liveness = createLivenessTracker(now);
|
|
5953
|
+
const assessor = createSelfAssessor();
|
|
5954
|
+
const autoOff = createVideoAutoOff(() => connectionPolicy.videoAutoOffAfterPoorSeconds * 1e3, now);
|
|
5955
|
+
const lkQuality = /* @__PURE__ */ new Map();
|
|
5956
|
+
let silent = /* @__PURE__ */ new Set();
|
|
5957
|
+
let healthTimer = null;
|
|
5958
|
+
let lastBeatAt = 0;
|
|
5959
|
+
let wasUnstable = false;
|
|
5960
|
+
function patchLocalNetwork(next) {
|
|
5961
|
+
const before = store.snapshot().localNetwork;
|
|
5962
|
+
const merged = { ...before, ...next };
|
|
5963
|
+
if (merged.path !== before.path || merged.quality !== before.quality || merged.videoPaused !== before.videoPaused) {
|
|
5964
|
+
store.patch({ localNetwork: merged });
|
|
5965
|
+
}
|
|
5966
|
+
}
|
|
5967
|
+
function effectiveQuality(person, connection, isLocal) {
|
|
5968
|
+
const fromServer = lkQuality.get(person) ?? "unknown";
|
|
5969
|
+
if (isLocal) return worstQuality(fromServer, assessor.current());
|
|
5970
|
+
return worstQuality(fromServer, connection === void 0 ? null : liveness.reported(connection)?.q);
|
|
5971
|
+
}
|
|
5972
|
+
function refreshQualities() {
|
|
5973
|
+
const snap = store.snapshot();
|
|
5974
|
+
for (const p of snap.participants) {
|
|
5975
|
+
if (p.reconnecting === true) continue;
|
|
5976
|
+
const next = effectiveQuality(p.identity, mainConnection.get(p.identity), p.isLocal);
|
|
5977
|
+
if (next !== "unknown" && next !== p.quality) store.setQuality(p.identity, next);
|
|
5978
|
+
}
|
|
5979
|
+
}
|
|
5980
|
+
function stopHealth() {
|
|
5981
|
+
if (healthTimer !== null) clearInterval(healthTimer);
|
|
5982
|
+
healthTimer = null;
|
|
5983
|
+
liveness.clear();
|
|
5984
|
+
assessor.reset();
|
|
5985
|
+
autoOff.reset();
|
|
5986
|
+
silent = /* @__PURE__ */ new Set();
|
|
5987
|
+
lkQuality.clear();
|
|
5988
|
+
lastBeatAt = 0;
|
|
5989
|
+
wasUnstable = false;
|
|
5990
|
+
if (store.snapshot().localNetwork !== UNKNOWN_LOCAL_NETWORK) store.patch({ localNetwork: UNKNOWN_LOCAL_NETWORK });
|
|
5991
|
+
}
|
|
5992
|
+
function healthTick() {
|
|
5993
|
+
const snap = store.snapshot();
|
|
5994
|
+
const target = driver;
|
|
5995
|
+
if (disposed || target === null || snap.phase !== "in_call") return;
|
|
5996
|
+
if (snap.connection !== "stable") {
|
|
5997
|
+
wasUnstable = true;
|
|
5998
|
+
return;
|
|
5999
|
+
}
|
|
6000
|
+
if (wasUnstable) {
|
|
6001
|
+
wasUnstable = false;
|
|
6002
|
+
liveness.restartClock();
|
|
6003
|
+
}
|
|
6004
|
+
const size = liveRoster.length;
|
|
6005
|
+
const at = now();
|
|
6006
|
+
if (at - lastBeatAt >= heartbeatIntervalMs(size)) {
|
|
6007
|
+
lastBeatAt = at;
|
|
6008
|
+
const beat = encodeBeat({ q: assessor.current(), vp: snap.localNetwork.videoPaused });
|
|
6009
|
+
void target.publishData(ALIVE_TOPIC, beat).catch(() => void 0);
|
|
6010
|
+
}
|
|
6011
|
+
const remotes = liveRoster.filter((p) => !p.isLocal).map((p) => p.identity);
|
|
6012
|
+
const next = liveness.stale(remotes, size);
|
|
6013
|
+
if (next.size !== silent.size || [...next].some((who) => !silent.has(who))) {
|
|
6014
|
+
silent = next;
|
|
6015
|
+
projectRoster(liveRoster, new Date(at).toISOString());
|
|
6016
|
+
}
|
|
6017
|
+
}
|
|
6018
|
+
function startHealth() {
|
|
6019
|
+
if (healthTimer !== null) return;
|
|
6020
|
+
healthTimer = setInterval(healthTick, 1e3);
|
|
6021
|
+
}
|
|
5733
6022
|
function requestAsNow() {
|
|
5734
6023
|
if (lastRequest === null) return null;
|
|
5735
6024
|
const media2 = store.snapshot().media;
|
|
@@ -5748,6 +6037,7 @@ function createRoomEngine(options) {
|
|
|
5748
6037
|
media.onDisconnected();
|
|
5749
6038
|
store.patch({ share: NO_SHARE, connection: "stable" });
|
|
5750
6039
|
clearGhosts();
|
|
6040
|
+
stopHealth();
|
|
5751
6041
|
await target?.disconnect();
|
|
5752
6042
|
}
|
|
5753
6043
|
async function leaveRoom() {
|
|
@@ -5839,6 +6129,8 @@ function createRoomEngine(options) {
|
|
|
5839
6129
|
},
|
|
5840
6130
|
participantLeft(who, reason) {
|
|
5841
6131
|
departures.set(who, reason);
|
|
6132
|
+
liveness.forget(who);
|
|
6133
|
+
silent.delete(who);
|
|
5842
6134
|
},
|
|
5843
6135
|
roomMetadata(metadata) {
|
|
5844
6136
|
let parsed = {};
|
|
@@ -5872,7 +6164,27 @@ function createRoomEngine(options) {
|
|
|
5872
6164
|
store.setSpeaking([...new Set(identities.map((who) => personOf(who)))]);
|
|
5873
6165
|
},
|
|
5874
6166
|
quality(who, value) {
|
|
5875
|
-
|
|
6167
|
+
const person = personOf(who);
|
|
6168
|
+
lkQuality.set(person, value);
|
|
6169
|
+
const local = store.snapshot().participants.find((p) => p.identity === person)?.isLocal === true;
|
|
6170
|
+
store.setQuality(person, value === "lost" ? value : effectiveQuality(person, who, local));
|
|
6171
|
+
},
|
|
6172
|
+
networkReading(reading) {
|
|
6173
|
+
const snap = store.snapshot();
|
|
6174
|
+
if (snap.phase !== "in_call" || snap.connection !== "stable") return;
|
|
6175
|
+
startHealth();
|
|
6176
|
+
const quality = assessor.push(reading);
|
|
6177
|
+
const path = pathOf(reading);
|
|
6178
|
+
patchLocalNetwork({ quality, ...path !== "unknown" ? { path } : {} });
|
|
6179
|
+
refreshQualities();
|
|
6180
|
+
const cameraOn = snap.media.camera.enabled;
|
|
6181
|
+
if (autoOff.step(quality, cameraOn) === "pause" && driver !== null) {
|
|
6182
|
+
const target = driver;
|
|
6183
|
+
report("info", "meet: the camera was paused because the connection is poor", "The audio is kept; Try video again turns it back on.");
|
|
6184
|
+
void enableTrack(target, "camera", false).then((state) => {
|
|
6185
|
+
if (!state.enabled) patchLocalNetwork({ videoPaused: true });
|
|
6186
|
+
});
|
|
6187
|
+
}
|
|
5876
6188
|
},
|
|
5877
6189
|
connectionState(state) {
|
|
5878
6190
|
supervisor.driverState(state);
|
|
@@ -5967,6 +6279,22 @@ function createRoomEngine(options) {
|
|
|
5967
6279
|
return;
|
|
5968
6280
|
}
|
|
5969
6281
|
const sender = personOf(from ?? "");
|
|
6282
|
+
if (from !== null) {
|
|
6283
|
+
const beat = topic === ALIVE_TOPIC ? decodeBeat(payload) : null;
|
|
6284
|
+
const before = liveness.reported(from);
|
|
6285
|
+
liveness.heard(from, beat);
|
|
6286
|
+
if (topic === ALIVE_TOPIC) {
|
|
6287
|
+
startHealth();
|
|
6288
|
+
if (beat !== null && (before?.q !== beat.q || before?.vp !== beat.vp || silent.has(from))) {
|
|
6289
|
+
silent.delete(from);
|
|
6290
|
+
projectRoster(liveRoster, new Date(now()).toISOString());
|
|
6291
|
+
refreshQualities();
|
|
6292
|
+
} else if (silent.delete(from)) {
|
|
6293
|
+
projectRoster(liveRoster, new Date(now()).toISOString());
|
|
6294
|
+
}
|
|
6295
|
+
return;
|
|
6296
|
+
}
|
|
6297
|
+
}
|
|
5970
6298
|
switch (topic) {
|
|
5971
6299
|
case DATA_TOPICS.reaction:
|
|
5972
6300
|
if (typeof envelope.emoji === "string") {
|
|
@@ -6209,7 +6537,16 @@ function createRoomEngine(options) {
|
|
|
6209
6537
|
store.setParticipants(
|
|
6210
6538
|
[...people.values()].map(({ entry }) => {
|
|
6211
6539
|
const projected = projectParticipant(entry, joinedAt);
|
|
6212
|
-
|
|
6540
|
+
if (entry.reconnecting === true || !entry.isLocal && silent.has(entry.identity)) {
|
|
6541
|
+
return { ...projected, reconnecting: true, quality: "lost" };
|
|
6542
|
+
}
|
|
6543
|
+
const videoPaused = entry.isLocal ? store.snapshot().localNetwork.videoPaused : liveness.reported(entry.identity)?.vp === true;
|
|
6544
|
+
const quality = effectiveQuality(projected.identity, entry.identity, entry.isLocal);
|
|
6545
|
+
return {
|
|
6546
|
+
...projected,
|
|
6547
|
+
...quality !== "unknown" && quality !== "lost" ? { quality } : {},
|
|
6548
|
+
...videoPaused ? { videoPaused: true } : {}
|
|
6549
|
+
};
|
|
6213
6550
|
})
|
|
6214
6551
|
);
|
|
6215
6552
|
scheduleGhostExpiry();
|
|
@@ -6395,7 +6732,12 @@ function createRoomEngine(options) {
|
|
|
6395
6732
|
},
|
|
6396
6733
|
async setCameraEnabled(enabled) {
|
|
6397
6734
|
if (driver === null) return;
|
|
6398
|
-
await enableTrack(driver, "camera", enabled);
|
|
6735
|
+
const state = await enableTrack(driver, "camera", enabled);
|
|
6736
|
+
if (enabled && state.enabled && store.snapshot().localNetwork.videoPaused) {
|
|
6737
|
+
autoOff.retried();
|
|
6738
|
+
patchLocalNetwork({ videoPaused: false });
|
|
6739
|
+
}
|
|
6740
|
+
if (!enabled && store.snapshot().localNetwork.videoPaused) patchLocalNetwork({ videoPaused: false });
|
|
6399
6741
|
},
|
|
6400
6742
|
async setScreenShareEnabled(enabled) {
|
|
6401
6743
|
if (driver === null) return;
|
|
@@ -6441,7 +6783,8 @@ function createRoomEngine(options) {
|
|
|
6441
6783
|
}
|
|
6442
6784
|
},
|
|
6443
6785
|
dismissShareNotice() {
|
|
6444
|
-
|
|
6786
|
+
const state = store.snapshot().share.state;
|
|
6787
|
+
if (state === "refused" || state === "ended") store.patch({ share: NO_SHARE });
|
|
6445
6788
|
},
|
|
6446
6789
|
async react(emoji) {
|
|
6447
6790
|
const local = store.snapshot().localIdentity;
|
|
@@ -6919,6 +7262,30 @@ function createLiveKitDriver(handlers, options = {}) {
|
|
|
6919
7262
|
let projectRoster = null;
|
|
6920
7263
|
let audioCapture = DEFAULT_AUDIO_PROCESSING;
|
|
6921
7264
|
let videoEffect = { kind: "none" };
|
|
7265
|
+
let statsTimer = null;
|
|
7266
|
+
function stopStatsPoll() {
|
|
7267
|
+
if (statsTimer !== null) clearInterval(statsTimer);
|
|
7268
|
+
statsTimer = null;
|
|
7269
|
+
}
|
|
7270
|
+
function startStatsPoll(target) {
|
|
7271
|
+
stopStatsPoll();
|
|
7272
|
+
if (handlers.networkReading === void 0) return;
|
|
7273
|
+
const sample = async () => {
|
|
7274
|
+
if (room !== target) return;
|
|
7275
|
+
const manager = target.engine.pcManager;
|
|
7276
|
+
if (manager === void 0) return;
|
|
7277
|
+
const readings = [];
|
|
7278
|
+
for (const transport of [manager.publisher, manager.subscriber]) {
|
|
7279
|
+
try {
|
|
7280
|
+
const report = await transport?.getStats();
|
|
7281
|
+
if (report !== void 0) readings.push(readingFromStats(report.values()));
|
|
7282
|
+
} catch {
|
|
7283
|
+
}
|
|
7284
|
+
}
|
|
7285
|
+
if (readings.length > 0 && room === target) handlers.networkReading?.(mergeReadings(readings));
|
|
7286
|
+
};
|
|
7287
|
+
statsTimer = setInterval(() => void sample(), 2e3);
|
|
7288
|
+
}
|
|
6922
7289
|
async function load() {
|
|
6923
7290
|
if (room !== null) return room;
|
|
6924
7291
|
let module;
|
|
@@ -6993,6 +7360,7 @@ function createLiveKitDriver(handlers, options = {}) {
|
|
|
6993
7360
|
participant?.identity ?? null
|
|
6994
7361
|
);
|
|
6995
7362
|
}).on(RoomEvent.Disconnected, (reason) => {
|
|
7363
|
+
stopStatsPoll();
|
|
6996
7364
|
audioContainer?.remove();
|
|
6997
7365
|
audioContainer = null;
|
|
6998
7366
|
handlers.disconnected(reasonName(reason));
|
|
@@ -7078,6 +7446,7 @@ function createLiveKitDriver(handlers, options = {}) {
|
|
|
7078
7446
|
projectRoster?.();
|
|
7079
7447
|
handlers.roomMetadata?.(target.metadata ?? "");
|
|
7080
7448
|
handlers.audioPlayback?.(target.canPlaybackAudio);
|
|
7449
|
+
startStatsPoll(target);
|
|
7081
7450
|
},
|
|
7082
7451
|
async disconnect() {
|
|
7083
7452
|
if (room === null) return;
|
|
@@ -7088,6 +7457,7 @@ function createLiveKitDriver(handlers, options = {}) {
|
|
|
7088
7457
|
} catch {
|
|
7089
7458
|
}
|
|
7090
7459
|
projectRoster = null;
|
|
7460
|
+
stopStatsPoll();
|
|
7091
7461
|
audioContainer?.remove();
|
|
7092
7462
|
audioContainer = null;
|
|
7093
7463
|
await target.disconnect();
|
|
@@ -7682,7 +8052,7 @@ function observe(input) {
|
|
|
7682
8052
|
const phase = observedPhase(input);
|
|
7683
8053
|
const inCall = phase === "in_call";
|
|
7684
8054
|
const actual = inCall && snapshot !== null ? effectiveRole(snapshot) : "none";
|
|
7685
|
-
const canAdmit =
|
|
8055
|
+
const canAdmit = snapshot !== null && mayAdmitNow(actual, snapshot.participants, snapshot.livePolicy);
|
|
7686
8056
|
const role = actual === "none" || snapshot === null ? "none" : observedRole(actual, effectiveStanding(snapshot));
|
|
7687
8057
|
const deviceInPlay = phase === "joining" || inCall;
|
|
7688
8058
|
const region = snapshot?.mediaRegion;
|
|
@@ -8538,6 +8908,13 @@ function requireSession(session, action) {
|
|
|
8538
8908
|
if (session === null) throw noRuntime(action);
|
|
8539
8909
|
return session;
|
|
8540
8910
|
}
|
|
8911
|
+
function otherPresenterOf(snapshot) {
|
|
8912
|
+
if (snapshot === null || snapshot.presenter === null || snapshot.presenter === snapshot.localIdentity) return null;
|
|
8913
|
+
const rule = snapshot.livePolicy.shareConcurrency;
|
|
8914
|
+
if (rule === "multiple") return null;
|
|
8915
|
+
const who = snapshot.participants.find((p) => p.identity === snapshot.presenter);
|
|
8916
|
+
return who === void 0 ? null : { name: who.displayName, rule };
|
|
8917
|
+
}
|
|
8541
8918
|
function useRoom() {
|
|
8542
8919
|
const host = useMeetHost();
|
|
8543
8920
|
const snapshot = useMeetSnapshot();
|
|
@@ -8552,7 +8929,9 @@ function useRoom() {
|
|
|
8552
8929
|
secondDevice: snapshot?.secondDevice ?? null,
|
|
8553
8930
|
local,
|
|
8554
8931
|
// An observer (role `viewer`) publishes nothing: no Share screen to press and be refused (MD-16).
|
|
8555
|
-
|
|
8932
|
+
sharingAllowed: snapshot === null || canShareUnder(snapshot.livePolicy, effectiveRole(snapshot), effectiveStanding(snapshot)),
|
|
8933
|
+
canShareScreen: (engine?.supportsScreenShare() ?? true) && !isObserverRole(snapshot?.grants.role ?? null) && (snapshot === null || canShareUnder(snapshot.livePolicy, effectiveRole(snapshot), effectiveStanding(snapshot))),
|
|
8934
|
+
otherPresenter: otherPresenterOf(snapshot),
|
|
8556
8935
|
join: useCallback2(
|
|
8557
8936
|
async (request) => requireEngine(engine, "join").join(request),
|
|
8558
8937
|
[engine]
|
|
@@ -8703,7 +9082,7 @@ function useLobby() {
|
|
|
8703
9082
|
declined: (snapshot?.lobby ?? []).filter((entry) => entry.state === "denied"),
|
|
8704
9083
|
// No runtime means no host powers — `<LobbyPanel>` renders nothing rather
|
|
8705
9084
|
// than an admit button that would throw.
|
|
8706
|
-
canAdmit: session !== null && isHost,
|
|
9085
|
+
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)),
|
|
8707
9086
|
admit: useCallback2(
|
|
8708
9087
|
async (entry) => requireSession(session, "admit").admit(entry, true),
|
|
8709
9088
|
[session]
|
|
@@ -11577,7 +11956,8 @@ function ParticipantTile({
|
|
|
11577
11956
|
participant.isLocal ? " (you)" : "",
|
|
11578
11957
|
participant.isAgent ? /* @__PURE__ */ jsx6("span", { className: "mx-meet__badge", children: "AI" }) : null,
|
|
11579
11958
|
/* @__PURE__ */ jsx6(RoleBadge, { role: participant.role, standing: participant.standing }),
|
|
11580
|
-
participant.reconnecting === true ? /* @__PURE__ */ jsx6("span", { className: "mx-meet__badge", children: "Reconnecting\u2026" }) : null
|
|
11959
|
+
participant.reconnecting === true ? /* @__PURE__ */ jsx6("span", { className: "mx-meet__badge", children: "Reconnecting\u2026" }) : null,
|
|
11960
|
+
participant.reconnecting !== true && participant.videoPaused === true && !participant.isLocal ? /* @__PURE__ */ jsx6("span", { className: "mx-meet__badge", "data-meet-video-paused": "", children: "Video paused to save audio" }) : null
|
|
11581
11961
|
] }),
|
|
11582
11962
|
!participant.micEnabled ? /* @__PURE__ */ jsx6(MicOffIcon, { width: 14, height: 14 }) : null,
|
|
11583
11963
|
/* @__PURE__ */ jsx6(QualityBars, { quality: participant.quality })
|
|
@@ -11667,6 +12047,8 @@ function ControlBar({ onLeave }) {
|
|
|
11667
12047
|
const queue = useHandQueue();
|
|
11668
12048
|
const micUnavailable = useLocalMedia().microphone.unavailable;
|
|
11669
12049
|
const live = useLivePolicy();
|
|
12050
|
+
const core = useMeetCore();
|
|
12051
|
+
const [askPresentInstead, setAskPresentInstead] = useState5(false);
|
|
11670
12052
|
const unread = useUnreadChat(panel === "chat");
|
|
11671
12053
|
const [whiteboard, setWhiteboard] = useState5(false);
|
|
11672
12054
|
const app = useMeetAppPanel();
|
|
@@ -11755,15 +12137,39 @@ function ControlBar({ onLeave }) {
|
|
|
11755
12137
|
}
|
|
11756
12138
|
),
|
|
11757
12139
|
/* @__PURE__ */ jsx6(CaptionsControl, {}),
|
|
11758
|
-
room.canShareScreen || room.local?.screenSharing === true ? /* @__PURE__ */
|
|
11759
|
-
|
|
11760
|
-
|
|
11761
|
-
|
|
11762
|
-
|
|
11763
|
-
|
|
11764
|
-
|
|
11765
|
-
|
|
11766
|
-
|
|
12140
|
+
room.canShareScreen || room.local?.screenSharing === true && core.share.state !== "ended" ? /* @__PURE__ */ jsxs4("span", { className: "mx-meet__anchor", children: [
|
|
12141
|
+
/* @__PURE__ */ jsx6(
|
|
12142
|
+
ControlButton,
|
|
12143
|
+
{
|
|
12144
|
+
label: room.local?.screenSharing === true ? "Stop sharing" : "Share screen",
|
|
12145
|
+
active: room.local?.screenSharing === true,
|
|
12146
|
+
onClick: () => {
|
|
12147
|
+
if (room.local?.screenSharing !== true && room.otherPresenter !== null) setAskPresentInstead(true);
|
|
12148
|
+
else void room.toggleScreenShare();
|
|
12149
|
+
},
|
|
12150
|
+
children: /* @__PURE__ */ jsx6(ScreenShareIcon, {})
|
|
12151
|
+
}
|
|
12152
|
+
),
|
|
12153
|
+
askPresentInstead && room.otherPresenter !== null ? /* @__PURE__ */ jsxs4("div", { className: "mx-meet__confirm", role: "group", "aria-label": "Someone else is presenting", "data-meet-notice": "share-takeover", children: [
|
|
12154
|
+
/* @__PURE__ */ jsxs4("span", { className: "mx-meet__confirm-text", children: [
|
|
12155
|
+
room.otherPresenter.name,
|
|
12156
|
+
" is presenting"
|
|
12157
|
+
] }),
|
|
12158
|
+
/* @__PURE__ */ jsx6(
|
|
12159
|
+
"button",
|
|
12160
|
+
{
|
|
12161
|
+
type: "button",
|
|
12162
|
+
className: "mx-meet__primary mx-meet__primary--small",
|
|
12163
|
+
onClick: () => {
|
|
12164
|
+
setAskPresentInstead(false);
|
|
12165
|
+
void room.toggleScreenShare();
|
|
12166
|
+
},
|
|
12167
|
+
children: room.otherPresenter.rule === "one_takeover" ? "Present instead" : "Share anyway"
|
|
12168
|
+
}
|
|
12169
|
+
),
|
|
12170
|
+
/* @__PURE__ */ jsx6("button", { type: "button", className: "mx-meet__link", onClick: () => setAskPresentInstead(false), children: "Cancel" })
|
|
12171
|
+
] }) : null
|
|
12172
|
+
] }) : null,
|
|
11767
12173
|
/* @__PURE__ */ jsxs4("span", { className: "mx-meet__anchor", children: [
|
|
11768
12174
|
/* @__PURE__ */ jsx6(
|
|
11769
12175
|
ControlButton,
|
|
@@ -11860,7 +12266,7 @@ function ControlBar({ onLeave }) {
|
|
|
11860
12266
|
]
|
|
11861
12267
|
}
|
|
11862
12268
|
),
|
|
11863
|
-
!room.canShareScreen && room.local?.screenSharing !== true ? (
|
|
12269
|
+
!room.canShareScreen && room.sharingAllowed && room.local?.screenSharing !== true ? (
|
|
11864
12270
|
// `share-unsupported-mobile`: the control is absent; the menu says where it lives.
|
|
11865
12271
|
/* @__PURE__ */ jsxs4("p", { className: "mx-meet__menu-note", role: "note", "data-meet-notice": "share-unsupported", children: [
|
|
11866
12272
|
/* @__PURE__ */ jsx6(ScreenShareIcon, { width: 16, height: 16 }),
|
|
@@ -13865,13 +14271,53 @@ function MeetSkinRoom({
|
|
|
13865
14271
|
] }),
|
|
13866
14272
|
core.connectionBanner && core.connection !== "stable" ? /* @__PURE__ */ jsx6("p", { className: "mx-meet__banner", role: "status", "data-meet-notice": `connection-${core.connection}`, children: CONNECTION_BANNER[core.connection] }) : null,
|
|
13867
14273
|
/* @__PURE__ */ jsx6(ShareNotice, {}),
|
|
14274
|
+
/* @__PURE__ */ jsx6(NetworkNotice, {}),
|
|
13868
14275
|
/* @__PURE__ */ jsx6(Stage, {}),
|
|
13869
14276
|
/* @__PURE__ */ jsx6(Captions, {}),
|
|
13870
14277
|
/* @__PURE__ */ jsx6(ControlBar, { ...onLeave !== void 0 ? { onLeave } : {} })
|
|
13871
14278
|
] });
|
|
13872
14279
|
}
|
|
14280
|
+
var RESTRICTED_NOTICE_MS = 3e4;
|
|
14281
|
+
function NetworkNotice() {
|
|
14282
|
+
const { snapshot, connectionBanner } = useMeetCore();
|
|
14283
|
+
const room = useRoom();
|
|
14284
|
+
const network = snapshot?.localNetwork;
|
|
14285
|
+
const [closed, setClosed] = useState5(() => /* @__PURE__ */ new Set());
|
|
14286
|
+
const [restrictedSince, setRestrictedSince] = useState5(null);
|
|
14287
|
+
const [, setTick] = useState5(0);
|
|
14288
|
+
const restricted = network?.path === "restricted";
|
|
14289
|
+
useEffect6(() => {
|
|
14290
|
+
if (!restricted || restrictedSince !== null) return;
|
|
14291
|
+
setRestrictedSince(Date.now());
|
|
14292
|
+
const timer = setTimeout(() => setTick((n) => n + 1), RESTRICTED_NOTICE_MS + 50);
|
|
14293
|
+
return () => clearTimeout(timer);
|
|
14294
|
+
}, [restricted, restrictedSince]);
|
|
14295
|
+
if (network === void 0 || snapshot?.phase !== "in_call" || connectionBanner) return null;
|
|
14296
|
+
const close = (code) => () => setClosed((prev) => /* @__PURE__ */ new Set([...prev, code]));
|
|
14297
|
+
const cameraOn = room.local?.cameraEnabled === true;
|
|
14298
|
+
if (network.videoPaused && !cameraOn) {
|
|
14299
|
+
return /* @__PURE__ */ jsxs4("p", { className: "mx-meet__banner", role: "status", "data-meet-notice": "video-paused", children: [
|
|
14300
|
+
"Your video is paused to save your audio",
|
|
14301
|
+
/* @__PURE__ */ jsx6("button", { type: "button", className: "mx-meet__link", onClick: () => void room.toggleCamera(), children: "Try video again" })
|
|
14302
|
+
] });
|
|
14303
|
+
}
|
|
14304
|
+
if (network.quality === "poor" && !closed.has("connection-poor")) {
|
|
14305
|
+
return /* @__PURE__ */ jsxs4("p", { className: "mx-meet__banner", role: "status", "data-meet-notice": "connection-poor", children: [
|
|
14306
|
+
"Your connection is unstable",
|
|
14307
|
+
cameraOn ? /* @__PURE__ */ jsx6("button", { type: "button", className: "mx-meet__link", onClick: () => void room.toggleCamera(), children: "Turn off camera" }) : null,
|
|
14308
|
+
/* @__PURE__ */ jsx6("button", { type: "button", className: "mx-meet__link", onClick: close("connection-poor"), children: "Dismiss" })
|
|
14309
|
+
] });
|
|
14310
|
+
}
|
|
14311
|
+
if (restricted && !closed.has("restricted-network") && restrictedSince !== null && Date.now() - restrictedSince < RESTRICTED_NOTICE_MS) {
|
|
14312
|
+
return /* @__PURE__ */ jsxs4("p", { className: "mx-meet__banner", role: "status", "data-meet-notice": "restricted-network", children: [
|
|
14313
|
+
"Restricted network \xB7 quality may be reduced",
|
|
14314
|
+
/* @__PURE__ */ jsx6("button", { type: "button", className: "mx-meet__link", onClick: close("restricted-network"), children: "Dismiss" })
|
|
14315
|
+
] });
|
|
14316
|
+
}
|
|
14317
|
+
return null;
|
|
14318
|
+
}
|
|
13873
14319
|
function ShareNotice() {
|
|
13874
|
-
const { share, actions } = useMeetCore();
|
|
14320
|
+
const { share, actions, snapshot } = useMeetCore();
|
|
13875
14321
|
const [soundHintClosed, setSoundHintClosed] = useState5(null);
|
|
13876
14322
|
if (share.state === "presenting" && share.audio === false && soundHintClosed !== share.at) {
|
|
13877
14323
|
return /* @__PURE__ */ jsxs4("p", { className: "mx-meet__banner", role: "status", "data-meet-notice": "share-audio-missing", children: [
|
|
@@ -13880,6 +14326,16 @@ function ShareNotice() {
|
|
|
13880
14326
|
/* @__PURE__ */ jsx6("button", { type: "button", className: "mx-meet__link", onClick: () => setSoundHintClosed(share.at), children: "Dismiss" })
|
|
13881
14327
|
] });
|
|
13882
14328
|
}
|
|
14329
|
+
if (share.state === "ended") {
|
|
14330
|
+
const replaced = share.reason === "replaced";
|
|
14331
|
+
const rule = snapshot?.livePolicy.shareWhoCan ?? "host_only";
|
|
14332
|
+
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";
|
|
14333
|
+
return /* @__PURE__ */ jsxs4("p", { className: "mx-meet__banner", role: "status", "data-meet-notice": replaced ? "share-ended" : "share-unavailable", children: [
|
|
14334
|
+
replaced ? "Your presentation ended" : "Your presentation was stopped",
|
|
14335
|
+
/* @__PURE__ */ jsx6("span", { className: "mx-meet__remedy", children: why }),
|
|
14336
|
+
/* @__PURE__ */ jsx6("button", { type: "button", className: "mx-meet__link", onClick: () => actions.dismissShareNotice(), children: "Dismiss" })
|
|
14337
|
+
] });
|
|
14338
|
+
}
|
|
13883
14339
|
if (share.state !== "refused") return null;
|
|
13884
14340
|
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";
|
|
13885
14341
|
return /* @__PURE__ */ jsxs4("p", { className: "mx-meet__banner", role: "status", "data-meet-notice": "share-unavailable", children: [
|
|
@@ -14298,6 +14754,7 @@ function MeetingRecordBody({
|
|
|
14298
14754
|
}
|
|
14299
14755
|
if (record.loading || record.record === null) {
|
|
14300
14756
|
return /* @__PURE__ */ jsx7("div", { className: "mx-meet mx-meet--record", children: /* @__PURE__ */ jsxs5("div", { className: "mx-meet__empty", role: "status", children: [
|
|
14757
|
+
/* @__PURE__ */ jsx7("p", { className: "mx-meet__empty-title", "data-meet-notice": "meeting-ended", children: "The meeting has ended" }),
|
|
14301
14758
|
/* @__PURE__ */ jsx7("p", { className: "mx-meet__empty-title", children: "Opening the meeting record\u2026" }),
|
|
14302
14759
|
/* @__PURE__ */ jsx7("p", { className: "mx-meet__empty-body", children: "Reading the summary, the notes and the transcript this meeting left behind." })
|
|
14303
14760
|
] }) });
|