@ai-matrx/meet 0.11.35 → 0.11.37
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/core.cjs +99 -86
- package/dist/core.cjs.map +1 -1
- package/dist/core.d.cts +73 -22
- package/dist/core.d.ts +73 -22
- package/dist/core.js +99 -86
- package/dist/core.js.map +1 -1
- package/dist/index.cjs +99 -86
- 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 +99 -86
- package/dist/index.js.map +1 -1
- package/dist/react.cjs +213 -106
- package/dist/react.cjs.map +1 -1
- package/dist/react.d.cts +20 -4
- package/dist/react.d.ts +20 -4
- package/dist/react.js +213 -106
- package/dist/react.js.map +1 -1
- package/dist/skins/meet.cjs +126 -20
- package/dist/skins/meet.cjs.map +1 -1
- package/dist/skins/meet.js +126 -20
- package/dist/skins/meet.js.map +1 -1
- package/dist/styles.css +7 -0
- package/package.json +3 -3
package/dist/react.cjs
CHANGED
|
@@ -40,6 +40,7 @@ __export(react_exports, {
|
|
|
40
40
|
DATA_TOPICS: () => DATA_TOPICS,
|
|
41
41
|
DEFAULT_CONNECTION_POLICY: () => DEFAULT_CONNECTION_POLICY,
|
|
42
42
|
DEFAULT_EFFECTS: () => DEFAULT_EFFECTS,
|
|
43
|
+
DEFAULT_LEAVE_POLICY: () => DEFAULT_LEAVE_POLICY,
|
|
43
44
|
DEFAULT_PAGE_SIZE: () => DEFAULT_PAGE_SIZE,
|
|
44
45
|
EMPTY_COLLAB: () => EMPTY_COLLAB,
|
|
45
46
|
END_MEETING_NOT_SERVED: () => END_MEETING_NOT_SERVED,
|
|
@@ -83,6 +84,7 @@ __export(react_exports, {
|
|
|
83
84
|
NO_SHARE: () => NO_SHARE,
|
|
84
85
|
ORGANIZATION_HEADER: () => ORGANIZATION_HEADER,
|
|
85
86
|
PARTICIPANT_CAN_RECORD_ATTRIBUTE: () => PARTICIPANT_CAN_RECORD_ATTRIBUTE,
|
|
87
|
+
PARTICIPANT_HAND_ATTRIBUTE: () => PARTICIPANT_HAND_ATTRIBUTE,
|
|
86
88
|
PARTICIPANT_ROLE_ATTRIBUTE: () => PARTICIPANT_ROLE_ATTRIBUTE,
|
|
87
89
|
PARTICIPANT_STANDING_ATTRIBUTE: () => PARTICIPANT_STANDING_ATTRIBUTE,
|
|
88
90
|
REACTION_TTL_MS: () => REACTION_TTL_MS,
|
|
@@ -196,6 +198,8 @@ __export(react_exports, {
|
|
|
196
198
|
isWaitingPhase: () => isWaitingPhase,
|
|
197
199
|
learnName: () => learnName,
|
|
198
200
|
learnNames: () => learnNames,
|
|
201
|
+
leaveChoices: () => leaveChoices,
|
|
202
|
+
leavePolicyFrom: () => leavePolicyFrom,
|
|
199
203
|
listenOnlyNotice: () => listenOnlyNotice,
|
|
200
204
|
loadFollowUpDraft: () => loadFollowUpDraft,
|
|
201
205
|
loadGuestMeetingRecord: () => loadGuestMeetingRecord,
|
|
@@ -297,6 +301,7 @@ __export(react_exports, {
|
|
|
297
301
|
useHandQueue: () => useHandQueue,
|
|
298
302
|
useHostControls: () => useHostControls,
|
|
299
303
|
useIsHost: () => useIsHost,
|
|
304
|
+
useLeave: () => useLeave,
|
|
300
305
|
useListenOnlyNotice: () => useListenOnlyNotice,
|
|
301
306
|
useLobby: () => useLobby,
|
|
302
307
|
useLocalMedia: () => useLocalMedia,
|
|
@@ -1026,6 +1031,30 @@ function invalidResponse(operation, detail) {
|
|
|
1026
1031
|
);
|
|
1027
1032
|
}
|
|
1028
1033
|
|
|
1034
|
+
// src/core/leave.ts
|
|
1035
|
+
var DEFAULT_LEAVE_POLICY = { hostLeavePrompt: "leave_or_end", endConfirm: false };
|
|
1036
|
+
var PROMPTS = ["leave_or_end", "must_assign_or_end", "leave_only"];
|
|
1037
|
+
function leavePolicyFrom(raw) {
|
|
1038
|
+
if (typeof raw !== "object" || raw === null) return DEFAULT_LEAVE_POLICY;
|
|
1039
|
+
const value = raw;
|
|
1040
|
+
const prompt = value["host_leave_prompt"];
|
|
1041
|
+
const confirm = value["end_confirm"];
|
|
1042
|
+
return {
|
|
1043
|
+
hostLeavePrompt: PROMPTS.includes(prompt) ? prompt : DEFAULT_LEAVE_POLICY.hostLeavePrompt,
|
|
1044
|
+
endConfirm: typeof confirm === "boolean" ? confirm : DEFAULT_LEAVE_POLICY.endConfirm
|
|
1045
|
+
};
|
|
1046
|
+
}
|
|
1047
|
+
function leaveChoices(args) {
|
|
1048
|
+
if (args.role !== "host") return ["leave"];
|
|
1049
|
+
switch (args.policy.hostLeavePrompt) {
|
|
1050
|
+
case "must_assign_or_end":
|
|
1051
|
+
return args.othersPresent ? ["assign_and_leave", "end_for_all"] : ["leave", "end_for_all"];
|
|
1052
|
+
case "leave_only":
|
|
1053
|
+
case "leave_or_end":
|
|
1054
|
+
return ["leave", "end_for_all"];
|
|
1055
|
+
}
|
|
1056
|
+
}
|
|
1057
|
+
|
|
1029
1058
|
// src/core/tokens.ts
|
|
1030
1059
|
function createWebRoomTokenStorage(onDiagnostic) {
|
|
1031
1060
|
const store = globalThis.localStorage;
|
|
@@ -1217,7 +1246,8 @@ function parseRoomToken(body, roomName) {
|
|
|
1217
1246
|
pollSeconds: typeof payload["poll_seconds"] === "number" ? payload["poll_seconds"] : void 0,
|
|
1218
1247
|
breakoutRoomId: readString(payload, "breakout_room_id") ?? null,
|
|
1219
1248
|
observersVisibleTo: readString(payload, "observers_visible_to") === "hosts" ? "hosts" : "everyone",
|
|
1220
|
-
connectionPolicy: connectionPolicyFrom(payload["connection_policy"])
|
|
1249
|
+
connectionPolicy: connectionPolicyFrom(payload["connection_policy"]),
|
|
1250
|
+
leavePolicy: leavePolicyFrom(payload["leave_policy"])
|
|
1221
1251
|
};
|
|
1222
1252
|
}
|
|
1223
1253
|
function createTokenClient(options) {
|
|
@@ -4117,6 +4147,10 @@ var EMPTY = {
|
|
|
4117
4147
|
chatHistoryError: null,
|
|
4118
4148
|
reactions: [],
|
|
4119
4149
|
spotlight: null,
|
|
4150
|
+
actingHost: null,
|
|
4151
|
+
presenter: null,
|
|
4152
|
+
waitingCount: 0,
|
|
4153
|
+
leavePolicy: DEFAULT_LEAVE_POLICY,
|
|
4120
4154
|
moderationNotice: null,
|
|
4121
4155
|
actionNotice: null,
|
|
4122
4156
|
privateChatRequest: null,
|
|
@@ -4178,8 +4212,6 @@ function createMeetStore(initial = {}) {
|
|
|
4178
4212
|
},
|
|
4179
4213
|
setParticipants(participants) {
|
|
4180
4214
|
const names = learnNames(state.names, participants);
|
|
4181
|
-
const hands = new Map(state.participants.map((p) => [p.identity, p.handRaised]));
|
|
4182
|
-
const handTimes = new Map(state.participants.map((p) => [p.identity, p.handRaisedAt ?? null]));
|
|
4183
4215
|
const quality = new Map(state.participants.map((p) => [p.identity, p.quality]));
|
|
4184
4216
|
commit({
|
|
4185
4217
|
...state,
|
|
@@ -4192,8 +4224,6 @@ function createMeetStore(initial = {}) {
|
|
|
4192
4224
|
// identity could reach a screen, and it is the place that asks the
|
|
4193
4225
|
// name book. `roster-names.test.ts` fails if a token ever gets past it.
|
|
4194
4226
|
displayName: displayNameFor(names, participant.identity, participant.displayName),
|
|
4195
|
-
handRaised: hands.get(participant.identity) ?? participant.handRaised,
|
|
4196
|
-
handRaisedAt: handTimes.get(participant.identity) ?? participant.handRaisedAt ?? null,
|
|
4197
4227
|
quality: participant.quality === "unknown" ? quality.get(participant.identity) ?? "unknown" : participant.quality
|
|
4198
4228
|
}))
|
|
4199
4229
|
});
|
|
@@ -4216,20 +4246,6 @@ function createMeetStore(initial = {}) {
|
|
|
4216
4246
|
)
|
|
4217
4247
|
});
|
|
4218
4248
|
},
|
|
4219
|
-
setHand(identity, raised, at) {
|
|
4220
|
-
commit({
|
|
4221
|
-
...state,
|
|
4222
|
-
participants: state.participants.map(
|
|
4223
|
-
(participant) => participant.identity === identity ? {
|
|
4224
|
-
...participant,
|
|
4225
|
-
handRaised: raised,
|
|
4226
|
-
// A hand that is ALREADY up keeps its place in the queue when
|
|
4227
|
-
// it is re-announced (to a late joiner): the first time wins.
|
|
4228
|
-
handRaisedAt: raised ? participant.handRaised && participant.handRaisedAt != null ? participant.handRaisedAt : at ?? Date.now() : null
|
|
4229
|
-
} : participant
|
|
4230
|
-
)
|
|
4231
|
-
});
|
|
4232
|
-
},
|
|
4233
4249
|
addChat(message) {
|
|
4234
4250
|
if (state.chat.some((existing) => existing.id === message.id)) return;
|
|
4235
4251
|
const chat = [...state.chat, message];
|
|
@@ -4589,7 +4605,7 @@ function createMeetingSession(options) {
|
|
|
4589
4605
|
})();
|
|
4590
4606
|
return reading;
|
|
4591
4607
|
}
|
|
4592
|
-
async function postLeave(intent) {
|
|
4608
|
+
async function postLeave(intent, extra = {}) {
|
|
4593
4609
|
const meeting = requireMeeting(`meet.${intent}`);
|
|
4594
4610
|
const pass = options.resolvePass?.() ?? null;
|
|
4595
4611
|
const response = await options.api.request({
|
|
@@ -4597,7 +4613,7 @@ function createMeetingSession(options) {
|
|
|
4597
4613
|
method: "POST",
|
|
4598
4614
|
requireAuth: false,
|
|
4599
4615
|
operation: `meet.${intent}`,
|
|
4600
|
-
body: { meeting_id: meeting.id, intent },
|
|
4616
|
+
body: { meeting_id: meeting.id, intent, ...extra },
|
|
4601
4617
|
...pass !== null ? { headers: { [MEETING_PASS_HEADER]: pass } } : {}
|
|
4602
4618
|
});
|
|
4603
4619
|
if (!response.ok) {
|
|
@@ -4611,7 +4627,7 @@ function createMeetingSession(options) {
|
|
|
4611
4627
|
throw new MeetError(
|
|
4612
4628
|
"transport",
|
|
4613
4629
|
refusal2?.reason ?? `meet.${intent}: the server answered ${response.status}`,
|
|
4614
|
-
refusal2?.remedy ?? "You are still in the waiting room. Try again."
|
|
4630
|
+
refusal2?.remedy ?? (intent === "withdraw" ? "You are still in the waiting room. Try again." : "Nothing was changed. Try again.")
|
|
4615
4631
|
);
|
|
4616
4632
|
}
|
|
4617
4633
|
}
|
|
@@ -4835,6 +4851,10 @@ function createMeetingSession(options) {
|
|
|
4835
4851
|
store.dispatch({ type: "admission", admission: "withdrawn" });
|
|
4836
4852
|
hint();
|
|
4837
4853
|
},
|
|
4854
|
+
async hostLeave(intent) {
|
|
4855
|
+
if (intent === "end_for_all") await postLeave("end_for_all");
|
|
4856
|
+
else await postLeave("assign_and_leave", { assign_to: intent.assignTo });
|
|
4857
|
+
},
|
|
4838
4858
|
async askAgain() {
|
|
4839
4859
|
await options.rejoin?.();
|
|
4840
4860
|
},
|
|
@@ -5295,18 +5315,6 @@ var ROOM_EVENTS = {
|
|
|
5295
5315
|
wrapUp: "matrx.meet.wrap_up",
|
|
5296
5316
|
/** The meeting ended for everyone. Payload `{ meeting_id, ended_at, trigger }`. */
|
|
5297
5317
|
ended: "matrx.meet.ended",
|
|
5298
|
-
/**
|
|
5299
|
-
* A `meet_recordings` row changed state. Payload
|
|
5300
|
-
* `{ meeting_id, recording: { id, state, started_by } }`.
|
|
5301
|
-
*
|
|
5302
|
-
* 🚨 THIS IS THE ONLY WAY A GUEST LEARNS A RECORDING IS RUNNING. PreJoin
|
|
5303
|
-
* promises every joiner an indicator, and `anon` holds no SELECT grant on
|
|
5304
|
-
* `meet_recordings` (MRI-A10 settled that widening it is forbidden), so
|
|
5305
|
-
* without this packet the promise was a sentence with nothing behind it: on
|
|
5306
|
-
* production 2026-09-09 the guest lane showed no indicator at all while the
|
|
5307
|
-
* host recorded (MRI-C8).
|
|
5308
|
-
*/
|
|
5309
|
-
recording: "matrx.meet.recording",
|
|
5310
5318
|
/**
|
|
5311
5319
|
* A chat message to everyone was SAVED (Meet wave 2). Payload
|
|
5312
5320
|
* `{ message: { id, meeting_id, sender_identity, sender_name, body, created_at } }`.
|
|
@@ -5359,8 +5367,6 @@ function applyRoomEvent(args) {
|
|
|
5359
5367
|
return applyWrapUp(store, payload, now);
|
|
5360
5368
|
case ROOM_EVENTS.ended:
|
|
5361
5369
|
return applyEnded(store, payload);
|
|
5362
|
-
case ROOM_EVENTS.recording:
|
|
5363
|
-
return applyRecording(store, payload);
|
|
5364
5370
|
case ROOM_EVENTS.chat:
|
|
5365
5371
|
return applyChat(store, payload);
|
|
5366
5372
|
case ROOM_EVENTS.moderation:
|
|
@@ -5440,9 +5446,9 @@ function applyWrapUp(store, payload, now) {
|
|
|
5440
5446
|
store.announceWrapUp(announcement);
|
|
5441
5447
|
return HANDLED;
|
|
5442
5448
|
}
|
|
5443
|
-
function
|
|
5449
|
+
function applyRecordingState(store, payload) {
|
|
5444
5450
|
const row = object(payload["recording"]);
|
|
5445
|
-
if (row === null) return ignored("
|
|
5451
|
+
if (row === null) return ignored("room state carries no recording");
|
|
5446
5452
|
const meetingId = text2(payload["meeting_id"]);
|
|
5447
5453
|
const meeting = store.snapshot().meeting;
|
|
5448
5454
|
if (meeting !== null && meetingId !== null && meetingId !== meeting.id) {
|
|
@@ -5524,19 +5530,6 @@ function applyModeration(store, payload, now) {
|
|
|
5524
5530
|
}
|
|
5525
5531
|
const kind = text2(payload["kind"]);
|
|
5526
5532
|
switch (kind) {
|
|
5527
|
-
case "lower-hands": {
|
|
5528
|
-
const raw = payload["identities"];
|
|
5529
|
-
const everyone = raw === "*";
|
|
5530
|
-
const named = new Set(
|
|
5531
|
-
Array.isArray(raw) ? raw.filter((value) => typeof value === "string") : []
|
|
5532
|
-
);
|
|
5533
|
-
for (const participant of snapshot.participants) {
|
|
5534
|
-
if (participant.handRaised && (everyone || named.has(participant.identity))) {
|
|
5535
|
-
store.setHand(participant.identity, false);
|
|
5536
|
-
}
|
|
5537
|
-
}
|
|
5538
|
-
return HANDLED;
|
|
5539
|
-
}
|
|
5540
5533
|
case "role": {
|
|
5541
5534
|
const identity = text2(payload["identity"]);
|
|
5542
5535
|
const role = text2(payload["role"]);
|
|
@@ -5630,7 +5623,6 @@ function noteTakerLabel(status) {
|
|
|
5630
5623
|
|
|
5631
5624
|
// src/core/room-driver.ts
|
|
5632
5625
|
var DATA_TOPICS = {
|
|
5633
|
-
hand: "mx.hand",
|
|
5634
5626
|
reaction: "mx.reaction",
|
|
5635
5627
|
chat: "mx.chat"
|
|
5636
5628
|
};
|
|
@@ -5654,6 +5646,11 @@ function SCREEN_SHARE_BLOCKED_REMEDY_FOR(remedy) {
|
|
|
5654
5646
|
return /administrator|policy/i.test(remedy) ? remedy : SCREEN_SHARE_BLOCKED_REMEDY;
|
|
5655
5647
|
}
|
|
5656
5648
|
var PARTICIPANT_ROLE_ATTRIBUTE = "matrx.participant.role";
|
|
5649
|
+
var PARTICIPANT_HAND_ATTRIBUTE = "matrx.hand";
|
|
5650
|
+
function handFrom(value) {
|
|
5651
|
+
const at = value === void 0 || value.length === 0 ? Number.NaN : Number(value);
|
|
5652
|
+
return Number.isFinite(at) ? { handRaised: true, handRaisedAt: at } : { handRaised: false, handRaisedAt: null };
|
|
5653
|
+
}
|
|
5657
5654
|
var PARTICIPANT_CAN_RECORD_ATTRIBUTE = "matrx.participant.can_record";
|
|
5658
5655
|
var CONNECTION_KIND_ATTRIBUTE = "matrx.participant.connection_kind";
|
|
5659
5656
|
var PARTICIPANT_STANDING_ATTRIBUTE = "matrx.participant.standing";
|
|
@@ -5707,7 +5704,10 @@ function projectParticipant(driver, joinedAt) {
|
|
|
5707
5704
|
cameraEnabled: driver.cameraEnabled,
|
|
5708
5705
|
screenSharing: driver.screenSharing,
|
|
5709
5706
|
speaking: false,
|
|
5710
|
-
|
|
5707
|
+
// S8: a hand is the SERVER's (`hand_raised_at` on the person row, `/moderate raise_hand`),
|
|
5708
|
+
// replicated as the `matrx.hand` attribute: epoch ms of the raise, "" when down. It is
|
|
5709
|
+
// therefore delivered to late joiners and survives refresh and reconnect.
|
|
5710
|
+
...handFrom(driver.attributes[PARTICIPANT_HAND_ATTRIBUTE]),
|
|
5711
5711
|
quality: "unknown",
|
|
5712
5712
|
joinedAt
|
|
5713
5713
|
};
|
|
@@ -5837,40 +5837,38 @@ function createRoomEngine(options) {
|
|
|
5837
5837
|
},
|
|
5838
5838
|
participants(list) {
|
|
5839
5839
|
const joinedAt = new Date(now()).toISOString();
|
|
5840
|
-
const before = new Set(store.snapshot().participants.map((p) => p.identity));
|
|
5841
5840
|
projectRoster(list, joinedAt);
|
|
5842
|
-
const snapshot = store.snapshot();
|
|
5843
|
-
const newcomer = snapshot.participants.some(
|
|
5844
|
-
(participant) => !participant.isLocal && !before.has(participant.identity)
|
|
5845
|
-
);
|
|
5846
|
-
const me = snapshot.participants.find((p) => p.identity === snapshot.localIdentity);
|
|
5847
|
-
if (newcomer && before.size > 0 && me?.handRaised === true) {
|
|
5848
|
-
void publish(DATA_TOPICS.hand, { raised: true, raisedAt: me.handRaisedAt ?? now() }).catch(
|
|
5849
|
-
() => void 0
|
|
5850
|
-
);
|
|
5851
|
-
}
|
|
5852
5841
|
},
|
|
5853
5842
|
participantLeft(who, reason) {
|
|
5854
5843
|
departures.set(who, reason);
|
|
5855
5844
|
},
|
|
5856
5845
|
roomMetadata(metadata) {
|
|
5857
|
-
let
|
|
5846
|
+
let parsed = {};
|
|
5858
5847
|
if (metadata.length > 0) {
|
|
5859
5848
|
try {
|
|
5860
|
-
const
|
|
5861
|
-
if (typeof
|
|
5862
|
-
const value = parsed.spotlight;
|
|
5863
|
-
if (typeof value === "string" && value.length > 0) spotlight = asParticipantIdentity(value);
|
|
5864
|
-
}
|
|
5849
|
+
const value = JSON.parse(metadata);
|
|
5850
|
+
if (typeof value === "object" && value !== null) parsed = value;
|
|
5865
5851
|
} catch {
|
|
5866
5852
|
report(
|
|
5867
5853
|
"warn",
|
|
5868
|
-
"meet: the room's metadata is not JSON, so
|
|
5869
|
-
"Only aidream writes room metadata; check services/meet/
|
|
5854
|
+
"meet: the room's metadata is not JSON, so the room state was not read from it",
|
|
5855
|
+
"Only aidream writes room metadata; check services/meet/room_state.py."
|
|
5870
5856
|
);
|
|
5871
5857
|
}
|
|
5872
5858
|
}
|
|
5873
|
-
|
|
5859
|
+
const key = (name) => {
|
|
5860
|
+
const value = parsed[name];
|
|
5861
|
+
return typeof value === "string" && value.length > 0 ? asParticipantIdentity(personOf(value)) : null;
|
|
5862
|
+
};
|
|
5863
|
+
const waiting = typeof parsed["waiting_count"] === "number" ? parsed["waiting_count"] : 0;
|
|
5864
|
+
const before = store.snapshot();
|
|
5865
|
+
const next = { spotlight: key("spotlight"), actingHost: key("acting_host"), presenter: key("presenter"), waitingCount: waiting };
|
|
5866
|
+
if (before.spotlight !== next.spotlight || before.actingHost !== next.actingHost || before.presenter !== next.presenter || before.waitingCount !== next.waitingCount) {
|
|
5867
|
+
store.patch(next);
|
|
5868
|
+
}
|
|
5869
|
+
if (parsed["recording"] !== void 0 && parsed["recording"] !== null) {
|
|
5870
|
+
applyRecordingState(store, { meeting_id: parsed["meeting_id"], recording: parsed["recording"] });
|
|
5871
|
+
}
|
|
5874
5872
|
},
|
|
5875
5873
|
activeSpeakers(identities) {
|
|
5876
5874
|
store.setSpeaking([...new Set(identities.map((who) => personOf(who)))]);
|
|
@@ -5972,13 +5970,6 @@ function createRoomEngine(options) {
|
|
|
5972
5970
|
}
|
|
5973
5971
|
const sender = personOf(from ?? "");
|
|
5974
5972
|
switch (topic) {
|
|
5975
|
-
case DATA_TOPICS.hand:
|
|
5976
|
-
store.setHand(
|
|
5977
|
-
sender,
|
|
5978
|
-
envelope.raised === true,
|
|
5979
|
-
typeof envelope.raisedAt === "number" ? envelope.raisedAt : now()
|
|
5980
|
-
);
|
|
5981
|
-
return;
|
|
5982
5973
|
case DATA_TOPICS.reaction:
|
|
5983
5974
|
if (typeof envelope.emoji === "string") {
|
|
5984
5975
|
store.addReaction({
|
|
@@ -6077,6 +6068,7 @@ function createRoomEngine(options) {
|
|
|
6077
6068
|
current = token;
|
|
6078
6069
|
lastServerUrl = token.serverUrl;
|
|
6079
6070
|
connectionPolicy = token.connectionPolicy ?? DEFAULT_CONNECTION_POLICY;
|
|
6071
|
+
if (token.leavePolicy !== void 0) store.patch({ leavePolicy: token.leavePolicy });
|
|
6080
6072
|
store.patch({
|
|
6081
6073
|
localIdentity: token.identity,
|
|
6082
6074
|
breakoutRoomId: token.breakoutRoomId ?? null,
|
|
@@ -6111,6 +6103,7 @@ function createRoomEngine(options) {
|
|
|
6111
6103
|
return;
|
|
6112
6104
|
}
|
|
6113
6105
|
driver = created;
|
|
6106
|
+
if (token.connectionKind === "companion") created.setRemoteAudioPlayback?.(false);
|
|
6114
6107
|
await created.connect(token.serverUrl, token.token);
|
|
6115
6108
|
store.patch({ failure: null, connection: "stable" });
|
|
6116
6109
|
store.dispatch({ type: "connected" });
|
|
@@ -6372,6 +6365,19 @@ function createRoomEngine(options) {
|
|
|
6372
6365
|
}
|
|
6373
6366
|
},
|
|
6374
6367
|
leave: leaveRoom,
|
|
6368
|
+
async checkElsewhere(roomName) {
|
|
6369
|
+
const claim = options.tabClaim;
|
|
6370
|
+
const userId = identity.userId;
|
|
6371
|
+
if (claim === void 0 || userId === null) return false;
|
|
6372
|
+
const before = store.snapshot();
|
|
6373
|
+
if (before.phase !== "prejoin" || before.secondDevice !== null) return false;
|
|
6374
|
+
if (!await claim.presentElsewhere(String(roomName), `user:${String(userId)}`)) return false;
|
|
6375
|
+
const now2 = store.snapshot();
|
|
6376
|
+
if (now2.phase !== "prejoin" || now2.secondDevice !== null) return false;
|
|
6377
|
+
store.patch({ secondDevice: { rule: "ask_switch_or_join" } });
|
|
6378
|
+
report("info", "meet: this browser already has you in this meeting \u2014 offering the choice before Join");
|
|
6379
|
+
return true;
|
|
6380
|
+
},
|
|
6375
6381
|
backToPrejoin() {
|
|
6376
6382
|
store.dispatch({ type: "back_to_prejoin", click: true });
|
|
6377
6383
|
},
|
|
@@ -6429,11 +6435,6 @@ function createRoomEngine(options) {
|
|
|
6429
6435
|
dismissShareNotice() {
|
|
6430
6436
|
if (store.snapshot().share.state === "refused") store.patch({ share: NO_SHARE });
|
|
6431
6437
|
},
|
|
6432
|
-
async raiseHand(raised) {
|
|
6433
|
-
const local = store.snapshot().localIdentity;
|
|
6434
|
-
if (local !== null) store.setHand(local, raised);
|
|
6435
|
-
await publish(DATA_TOPICS.hand, { raised, raisedAt: now() });
|
|
6436
|
-
},
|
|
6437
6438
|
async react(emoji) {
|
|
6438
6439
|
const local = store.snapshot().localIdentity;
|
|
6439
6440
|
const id = `${local ?? "local"}-${now()}`;
|
|
@@ -6562,6 +6563,8 @@ var MODERATION_ACTIONS = [
|
|
|
6562
6563
|
"ask_unmute",
|
|
6563
6564
|
"ask_all_unmute",
|
|
6564
6565
|
"spotlight",
|
|
6566
|
+
"present",
|
|
6567
|
+
"raise_hand",
|
|
6565
6568
|
"lower_hand",
|
|
6566
6569
|
"lower_all_hands",
|
|
6567
6570
|
"make_cohost",
|
|
@@ -6605,7 +6608,8 @@ function createInRoomClient(options) {
|
|
|
6605
6608
|
action: request.action,
|
|
6606
6609
|
...request.identity != null ? { identity: request.identity } : {},
|
|
6607
6610
|
...request.name !== void 0 ? { name: request.name } : {},
|
|
6608
|
-
...request.reason !== void 0 ? { reason: request.reason } : {}
|
|
6611
|
+
...request.reason !== void 0 ? { reason: request.reason } : {},
|
|
6612
|
+
...request.on !== void 0 ? { on: request.on } : {}
|
|
6609
6613
|
},
|
|
6610
6614
|
...hdrs !== void 0 ? { headers: hdrs } : {}
|
|
6611
6615
|
});
|
|
@@ -6921,6 +6925,7 @@ function createLiveKitDriver(handlers, options = {}) {
|
|
|
6921
6925
|
projectRoster = roster;
|
|
6922
6926
|
created.on(RoomEvent.TrackSubscribed, (track) => {
|
|
6923
6927
|
if (track.kind !== Track.Kind.Audio || typeof document === "undefined") return;
|
|
6928
|
+
if (!remoteAudioPlayback) return;
|
|
6924
6929
|
const element = track.attach();
|
|
6925
6930
|
element.setAttribute("data-mx-meet-audio", "");
|
|
6926
6931
|
audioSink().appendChild(element);
|
|
@@ -7005,6 +7010,7 @@ function createLiveKitDriver(handlers, options = {}) {
|
|
|
7005
7010
|
let sources = null;
|
|
7006
7011
|
let trackEvents = null;
|
|
7007
7012
|
let audioContainer = null;
|
|
7013
|
+
let remoteAudioPlayback = true;
|
|
7008
7014
|
function audioSink() {
|
|
7009
7015
|
if (audioContainer !== null && audioContainer.isConnected) return audioContainer;
|
|
7010
7016
|
const container = document.createElement("div");
|
|
@@ -7163,6 +7169,14 @@ function createLiveKitDriver(handlers, options = {}) {
|
|
|
7163
7169
|
throw normalizeDeviceError(error, "meet.switchDevice");
|
|
7164
7170
|
}
|
|
7165
7171
|
},
|
|
7172
|
+
setRemoteAudioPlayback(enabled) {
|
|
7173
|
+
remoteAudioPlayback = enabled;
|
|
7174
|
+
if (enabled || audioContainer === null) return;
|
|
7175
|
+
for (const element of Array.from(audioContainer.querySelectorAll("[data-mx-meet-audio]"))) {
|
|
7176
|
+
if (element instanceof HTMLMediaElement) element.srcObject = null;
|
|
7177
|
+
element.remove();
|
|
7178
|
+
}
|
|
7179
|
+
},
|
|
7166
7180
|
async startAudio() {
|
|
7167
7181
|
const target = room;
|
|
7168
7182
|
if (target === null) return;
|
|
@@ -8434,6 +8448,10 @@ function useRoom() {
|
|
|
8434
8448
|
async (request) => requireEngine(engine, "join").join(request),
|
|
8435
8449
|
[engine]
|
|
8436
8450
|
),
|
|
8451
|
+
checkElsewhere: (0, import_react4.useCallback)(
|
|
8452
|
+
async (roomName) => engine === null ? false : engine.checkElsewhere(roomName),
|
|
8453
|
+
[engine]
|
|
8454
|
+
),
|
|
8437
8455
|
leave: (0, import_react4.useCallback)(async () => requireEngine(engine, "leave").leave(), [engine]),
|
|
8438
8456
|
toggleMicrophone: (0, import_react4.useCallback)(
|
|
8439
8457
|
async () => requireEngine(engine, "toggleMicrophone").setMicrophoneEnabled(
|
|
@@ -8445,16 +8463,20 @@ function useRoom() {
|
|
|
8445
8463
|
async () => requireEngine(engine, "toggleCamera").setCameraEnabled(!(local?.cameraEnabled ?? false)),
|
|
8446
8464
|
[engine, local]
|
|
8447
8465
|
),
|
|
8448
|
-
toggleScreenShare: (0, import_react4.useCallback)(
|
|
8449
|
-
|
|
8450
|
-
|
|
8451
|
-
)
|
|
8452
|
-
|
|
8453
|
-
|
|
8454
|
-
|
|
8455
|
-
|
|
8456
|
-
|
|
8457
|
-
|
|
8466
|
+
toggleScreenShare: (0, import_react4.useCallback)(async () => {
|
|
8467
|
+
const on = !(local?.screenSharing ?? false);
|
|
8468
|
+
await requireEngine(engine, "toggleScreenShare").setScreenShareEnabled(on);
|
|
8469
|
+
const sharing = host?.store.snapshot().participants.find((p) => p.isLocal)?.screenSharing ?? false;
|
|
8470
|
+
if (host !== null && sharing === on) {
|
|
8471
|
+
await host.inRoom.moderate({ action: "present", on }).catch(() => void 0);
|
|
8472
|
+
}
|
|
8473
|
+
}, [engine, host, local]),
|
|
8474
|
+
// S8: a hand is SERVER state (`/moderate raise_hand · lower_hand`, server time), replicated
|
|
8475
|
+
// back as the `matrx.hand` attribute — no client sets its own hand on the wire.
|
|
8476
|
+
toggleHand: (0, import_react4.useCallback)(async () => {
|
|
8477
|
+
if (host === null) throw noRuntime("toggleHand");
|
|
8478
|
+
await host.inRoom.moderate({ action: local?.handRaised === true ? "lower_hand" : "raise_hand" });
|
|
8479
|
+
}, [host, local]),
|
|
8458
8480
|
react: (0, import_react4.useCallback)(
|
|
8459
8481
|
async (emoji) => requireEngine(engine, "react").react(emoji),
|
|
8460
8482
|
[engine]
|
|
@@ -8636,6 +8658,31 @@ function useWaiting() {
|
|
|
8636
8658
|
askAgain: (0, import_react4.useCallback)(async () => requireSession(session, "askAgain").askAgain(), [session])
|
|
8637
8659
|
};
|
|
8638
8660
|
}
|
|
8661
|
+
function useLeave() {
|
|
8662
|
+
const host = useMeetHost();
|
|
8663
|
+
const snapshot = useMeetSnapshot();
|
|
8664
|
+
const session = host?.meetings ?? null;
|
|
8665
|
+
const engine = host?.engine ?? null;
|
|
8666
|
+
const policy = snapshot?.leavePolicy ?? DEFAULT_LEAVE_POLICY;
|
|
8667
|
+
const participants = snapshot?.participants ?? [];
|
|
8668
|
+
const me = participants.find((p) => p.isLocal);
|
|
8669
|
+
const candidates = participants.filter((p) => !p.isLocal && !p.isAgent && p.role !== "viewer");
|
|
8670
|
+
const choices = leaveChoices({ policy, role: me?.role ?? null, othersPresent: candidates.length > 0 });
|
|
8671
|
+
return {
|
|
8672
|
+
choices,
|
|
8673
|
+
policy,
|
|
8674
|
+
candidates,
|
|
8675
|
+
leave: (0, import_react4.useCallback)(async () => requireEngine(engine, "leave").leave(), [engine]),
|
|
8676
|
+
endForAll: (0, import_react4.useCallback)(async () => requireSession(session, "endForAll").hostLeave("end_for_all"), [session]),
|
|
8677
|
+
assignAndLeave: (0, import_react4.useCallback)(
|
|
8678
|
+
async (person) => {
|
|
8679
|
+
await requireSession(session, "assignAndLeave").hostLeave({ assignTo: String(person.identity) });
|
|
8680
|
+
await requireEngine(engine, "leave").leave();
|
|
8681
|
+
},
|
|
8682
|
+
[session, engine]
|
|
8683
|
+
)
|
|
8684
|
+
};
|
|
8685
|
+
}
|
|
8639
8686
|
function useRecording() {
|
|
8640
8687
|
const host = useMeetHost();
|
|
8641
8688
|
const snapshot = useMeetSnapshot();
|
|
@@ -11559,19 +11606,75 @@ function ControlBar({ onLeave }) {
|
|
|
11559
11606
|
] }) : null
|
|
11560
11607
|
] }) }) : null
|
|
11561
11608
|
] }),
|
|
11562
|
-
/* @__PURE__ */ (0, import_jsx_runtime6.jsx)(
|
|
11563
|
-
|
|
11609
|
+
/* @__PURE__ */ (0, import_jsx_runtime6.jsx)(LeaveControl, { onLeave })
|
|
11610
|
+
] })
|
|
11611
|
+
] });
|
|
11612
|
+
}
|
|
11613
|
+
var LEAVE_WORDS = {
|
|
11614
|
+
leave_or_end: { leave: "Leave call", assign_and_leave: "Assign a new host", end_for_all: "End call for everyone" },
|
|
11615
|
+
must_assign_or_end: { leave: "Leave meeting", assign_and_leave: "Assign a new host", end_for_all: "End meeting for all" },
|
|
11616
|
+
leave_only: { leave: "Leave", assign_and_leave: "Assign a new host", end_for_all: "End meeting" }
|
|
11617
|
+
};
|
|
11618
|
+
function LeaveControl({ onLeave }) {
|
|
11619
|
+
const leave = useLeave();
|
|
11620
|
+
const [open, setOpen] = (0, import_react10.useState)("none");
|
|
11621
|
+
const [failure, setFailure] = (0, import_react10.useState)(null);
|
|
11622
|
+
const words = LEAVE_WORDS[leave.policy.hostLeavePrompt];
|
|
11623
|
+
const run = (work, leaving) => {
|
|
11624
|
+
setFailure(null);
|
|
11625
|
+
void work().then(() => {
|
|
11626
|
+
setOpen("none");
|
|
11627
|
+
if (leaving) onLeave?.();
|
|
11628
|
+
}).catch((error) => setFailure(error instanceof Error ? error.message : String(error)));
|
|
11629
|
+
};
|
|
11630
|
+
const leaveNow = () => run(leave.leave, true);
|
|
11631
|
+
const endNow = () => run(leave.endForAll, false);
|
|
11632
|
+
return /* @__PURE__ */ (0, import_jsx_runtime6.jsxs)("span", { className: "mx-meet__anchor", children: [
|
|
11633
|
+
/* @__PURE__ */ (0, import_jsx_runtime6.jsx)(
|
|
11634
|
+
ControlButton,
|
|
11635
|
+
{
|
|
11636
|
+
label: "Leave",
|
|
11637
|
+
danger: true,
|
|
11638
|
+
active: open !== "none",
|
|
11639
|
+
onClick: () => {
|
|
11640
|
+
if (leave.choices.length === 1 && leave.choices[0] === "leave") leaveNow();
|
|
11641
|
+
else setOpen(open === "none" ? "menu" : "none");
|
|
11642
|
+
},
|
|
11643
|
+
children: /* @__PURE__ */ (0, import_jsx_runtime6.jsx)(PhoneOffIcon, {})
|
|
11644
|
+
}
|
|
11645
|
+
),
|
|
11646
|
+
open !== "none" ? /* @__PURE__ */ (0, import_jsx_runtime6.jsx)(Popover, { label: open === "assign" ? words.assign_and_leave : "Leave", onClose: () => setOpen("none"), children: /* @__PURE__ */ (0, import_jsx_runtime6.jsxs)("div", { className: "mx-meet__menu", "data-meet-leave-menu": leave.policy.hostLeavePrompt, children: [
|
|
11647
|
+
open === "menu" ? leave.choices.map((choice) => /* @__PURE__ */ (0, import_jsx_runtime6.jsx)(
|
|
11648
|
+
"button",
|
|
11564
11649
|
{
|
|
11565
|
-
|
|
11566
|
-
danger:
|
|
11650
|
+
type: "button",
|
|
11651
|
+
className: choice === "end_for_all" ? "mx-meet__menu-item mx-meet__menu-item--danger" : "mx-meet__menu-item",
|
|
11567
11652
|
onClick: () => {
|
|
11568
|
-
|
|
11569
|
-
|
|
11653
|
+
if (choice === "leave") leaveNow();
|
|
11654
|
+
else if (choice === "assign_and_leave") setOpen("assign");
|
|
11655
|
+
else if (leave.policy.endConfirm) setOpen("confirm");
|
|
11656
|
+
else endNow();
|
|
11570
11657
|
},
|
|
11571
|
-
children:
|
|
11572
|
-
}
|
|
11573
|
-
|
|
11574
|
-
|
|
11658
|
+
children: words[choice]
|
|
11659
|
+
},
|
|
11660
|
+
choice
|
|
11661
|
+
)) : null,
|
|
11662
|
+
open === "assign" ? leave.candidates.map((person) => /* @__PURE__ */ (0, import_jsx_runtime6.jsx)(
|
|
11663
|
+
"button",
|
|
11664
|
+
{
|
|
11665
|
+
type: "button",
|
|
11666
|
+
className: "mx-meet__menu-item",
|
|
11667
|
+
onClick: () => run(() => leave.assignAndLeave(person), true),
|
|
11668
|
+
children: person.displayName
|
|
11669
|
+
},
|
|
11670
|
+
String(person.identity)
|
|
11671
|
+
)) : null,
|
|
11672
|
+
open === "confirm" ? /* @__PURE__ */ (0, import_jsx_runtime6.jsxs)(import_jsx_runtime6.Fragment, { children: [
|
|
11673
|
+
/* @__PURE__ */ (0, import_jsx_runtime6.jsx)("button", { type: "button", className: "mx-meet__menu-item mx-meet__menu-item--danger", onClick: endNow, children: words.end_for_all }),
|
|
11674
|
+
/* @__PURE__ */ (0, import_jsx_runtime6.jsx)("button", { type: "button", className: "mx-meet__menu-item", onClick: () => setOpen("menu"), children: "Cancel" })
|
|
11675
|
+
] }) : null,
|
|
11676
|
+
failure !== null ? /* @__PURE__ */ (0, import_jsx_runtime6.jsx)("p", { className: "mx-meet__menu-note", role: "alert", children: failure }) : null
|
|
11677
|
+
] }) }) : null
|
|
11575
11678
|
] });
|
|
11576
11679
|
}
|
|
11577
11680
|
function useUnreadChat(open) {
|
|
@@ -12845,6 +12948,10 @@ function PreJoinBody({
|
|
|
12845
12948
|
(0, import_react10.useEffect)(() => {
|
|
12846
12949
|
void host.devices.refresh();
|
|
12847
12950
|
}, [host]);
|
|
12951
|
+
const { checkElsewhere } = room;
|
|
12952
|
+
(0, import_react10.useEffect)(() => {
|
|
12953
|
+
void checkElsewhere(roomName);
|
|
12954
|
+
}, [checkElsewhere, roomName]);
|
|
12848
12955
|
const joinWith = (secondDevice) => {
|
|
12849
12956
|
setJoining(true);
|
|
12850
12957
|
preview.release();
|