@ai-matrx/meet 0.11.36 → 0.11.38
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 +129 -87
- package/dist/core.cjs.map +1 -1
- package/dist/core.d.cts +66 -22
- package/dist/core.d.ts +66 -22
- package/dist/core.js +129 -87
- package/dist/core.js.map +1 -1
- package/dist/index.cjs +129 -87
- 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 +129 -87
- package/dist/index.js.map +1 -1
- package/dist/react.cjs +231 -107
- package/dist/react.cjs.map +1 -1
- package/dist/react.d.cts +17 -3
- package/dist/react.d.ts +17 -3
- package/dist/react.js +231 -107
- package/dist/react.js.map +1 -1
- package/dist/skins/meet.cjs +120 -20
- package/dist/skins/meet.cjs.map +1 -1
- package/dist/skins/meet.js +120 -20
- package/dist/skins/meet.js.map +1 -1
- package/dist/styles.css +7 -0
- package/package.json +5 -5
package/dist/react.js
CHANGED
|
@@ -710,6 +710,30 @@ function invalidResponse(operation, detail) {
|
|
|
710
710
|
);
|
|
711
711
|
}
|
|
712
712
|
|
|
713
|
+
// src/core/leave.ts
|
|
714
|
+
var DEFAULT_LEAVE_POLICY = { hostLeavePrompt: "leave_or_end", endConfirm: false };
|
|
715
|
+
var PROMPTS = ["leave_or_end", "must_assign_or_end", "leave_only"];
|
|
716
|
+
function leavePolicyFrom(raw) {
|
|
717
|
+
if (typeof raw !== "object" || raw === null) return DEFAULT_LEAVE_POLICY;
|
|
718
|
+
const value = raw;
|
|
719
|
+
const prompt = value["host_leave_prompt"];
|
|
720
|
+
const confirm = value["end_confirm"];
|
|
721
|
+
return {
|
|
722
|
+
hostLeavePrompt: PROMPTS.includes(prompt) ? prompt : DEFAULT_LEAVE_POLICY.hostLeavePrompt,
|
|
723
|
+
endConfirm: typeof confirm === "boolean" ? confirm : DEFAULT_LEAVE_POLICY.endConfirm
|
|
724
|
+
};
|
|
725
|
+
}
|
|
726
|
+
function leaveChoices(args) {
|
|
727
|
+
if (args.role !== "host") return ["leave"];
|
|
728
|
+
switch (args.policy.hostLeavePrompt) {
|
|
729
|
+
case "must_assign_or_end":
|
|
730
|
+
return args.othersPresent ? ["assign_and_leave", "end_for_all"] : ["leave", "end_for_all"];
|
|
731
|
+
case "leave_only":
|
|
732
|
+
case "leave_or_end":
|
|
733
|
+
return ["leave", "end_for_all"];
|
|
734
|
+
}
|
|
735
|
+
}
|
|
736
|
+
|
|
713
737
|
// src/core/tokens.ts
|
|
714
738
|
function createWebRoomTokenStorage(onDiagnostic) {
|
|
715
739
|
const store = globalThis.localStorage;
|
|
@@ -817,6 +841,12 @@ var MEET_ROUTES = {
|
|
|
817
841
|
state: "/api/v1/meet/state",
|
|
818
842
|
/** S4: `withdraw` (stop waiting) or `leave` (close this tab); the pass may ride in the body. */
|
|
819
843
|
leave: "/api/v1/meet/leave",
|
|
844
|
+
/**
|
|
845
|
+
* S8: a host's browser just called an admission DOOR (`meet_admit` · `meet_deny` ·
|
|
846
|
+
* `meet_undeny` · `meet_admit_all`). Payload-free: the server republishes the room's
|
|
847
|
+
* replicated state (the People badge's waiting count) from the database truth.
|
|
848
|
+
*/
|
|
849
|
+
admissionChanged: "/api/v1/meet/admission/changed",
|
|
820
850
|
/** S4: a pass holder (guests included) saw the recording notice, for this run. */
|
|
821
851
|
consent: "/api/v1/meet/consent"
|
|
822
852
|
};
|
|
@@ -901,7 +931,8 @@ function parseRoomToken(body, roomName) {
|
|
|
901
931
|
pollSeconds: typeof payload["poll_seconds"] === "number" ? payload["poll_seconds"] : void 0,
|
|
902
932
|
breakoutRoomId: readString(payload, "breakout_room_id") ?? null,
|
|
903
933
|
observersVisibleTo: readString(payload, "observers_visible_to") === "hosts" ? "hosts" : "everyone",
|
|
904
|
-
connectionPolicy: connectionPolicyFrom(payload["connection_policy"])
|
|
934
|
+
connectionPolicy: connectionPolicyFrom(payload["connection_policy"]),
|
|
935
|
+
leavePolicy: leavePolicyFrom(payload["leave_policy"])
|
|
905
936
|
};
|
|
906
937
|
}
|
|
907
938
|
function createTokenClient(options) {
|
|
@@ -3801,6 +3832,10 @@ var EMPTY = {
|
|
|
3801
3832
|
chatHistoryError: null,
|
|
3802
3833
|
reactions: [],
|
|
3803
3834
|
spotlight: null,
|
|
3835
|
+
actingHost: null,
|
|
3836
|
+
presenter: null,
|
|
3837
|
+
waitingCount: 0,
|
|
3838
|
+
leavePolicy: DEFAULT_LEAVE_POLICY,
|
|
3804
3839
|
moderationNotice: null,
|
|
3805
3840
|
actionNotice: null,
|
|
3806
3841
|
privateChatRequest: null,
|
|
@@ -3862,8 +3897,6 @@ function createMeetStore(initial = {}) {
|
|
|
3862
3897
|
},
|
|
3863
3898
|
setParticipants(participants) {
|
|
3864
3899
|
const names = learnNames(state.names, participants);
|
|
3865
|
-
const hands = new Map(state.participants.map((p) => [p.identity, p.handRaised]));
|
|
3866
|
-
const handTimes = new Map(state.participants.map((p) => [p.identity, p.handRaisedAt ?? null]));
|
|
3867
3900
|
const quality = new Map(state.participants.map((p) => [p.identity, p.quality]));
|
|
3868
3901
|
commit({
|
|
3869
3902
|
...state,
|
|
@@ -3876,8 +3909,6 @@ function createMeetStore(initial = {}) {
|
|
|
3876
3909
|
// identity could reach a screen, and it is the place that asks the
|
|
3877
3910
|
// name book. `roster-names.test.ts` fails if a token ever gets past it.
|
|
3878
3911
|
displayName: displayNameFor(names, participant.identity, participant.displayName),
|
|
3879
|
-
handRaised: hands.get(participant.identity) ?? participant.handRaised,
|
|
3880
|
-
handRaisedAt: handTimes.get(participant.identity) ?? participant.handRaisedAt ?? null,
|
|
3881
3912
|
quality: participant.quality === "unknown" ? quality.get(participant.identity) ?? "unknown" : participant.quality
|
|
3882
3913
|
}))
|
|
3883
3914
|
});
|
|
@@ -3900,20 +3931,6 @@ function createMeetStore(initial = {}) {
|
|
|
3900
3931
|
)
|
|
3901
3932
|
});
|
|
3902
3933
|
},
|
|
3903
|
-
setHand(identity, raised, at) {
|
|
3904
|
-
commit({
|
|
3905
|
-
...state,
|
|
3906
|
-
participants: state.participants.map(
|
|
3907
|
-
(participant) => participant.identity === identity ? {
|
|
3908
|
-
...participant,
|
|
3909
|
-
handRaised: raised,
|
|
3910
|
-
// A hand that is ALREADY up keeps its place in the queue when
|
|
3911
|
-
// it is re-announced (to a late joiner): the first time wins.
|
|
3912
|
-
handRaisedAt: raised ? participant.handRaised && participant.handRaisedAt != null ? participant.handRaisedAt : at ?? Date.now() : null
|
|
3913
|
-
} : participant
|
|
3914
|
-
)
|
|
3915
|
-
});
|
|
3916
|
-
},
|
|
3917
3934
|
addChat(message) {
|
|
3918
3935
|
if (state.chat.some((existing) => existing.id === message.id)) return;
|
|
3919
3936
|
const chat = [...state.chat, message];
|
|
@@ -4273,7 +4290,7 @@ function createMeetingSession(options) {
|
|
|
4273
4290
|
})();
|
|
4274
4291
|
return reading;
|
|
4275
4292
|
}
|
|
4276
|
-
async function postLeave(intent) {
|
|
4293
|
+
async function postLeave(intent, extra = {}) {
|
|
4277
4294
|
const meeting = requireMeeting(`meet.${intent}`);
|
|
4278
4295
|
const pass = options.resolvePass?.() ?? null;
|
|
4279
4296
|
const response = await options.api.request({
|
|
@@ -4281,7 +4298,7 @@ function createMeetingSession(options) {
|
|
|
4281
4298
|
method: "POST",
|
|
4282
4299
|
requireAuth: false,
|
|
4283
4300
|
operation: `meet.${intent}`,
|
|
4284
|
-
body: { meeting_id: meeting.id, intent },
|
|
4301
|
+
body: { meeting_id: meeting.id, intent, ...extra },
|
|
4285
4302
|
...pass !== null ? { headers: { [MEETING_PASS_HEADER]: pass } } : {}
|
|
4286
4303
|
});
|
|
4287
4304
|
if (!response.ok) {
|
|
@@ -4295,13 +4312,41 @@ function createMeetingSession(options) {
|
|
|
4295
4312
|
throw new MeetError(
|
|
4296
4313
|
"transport",
|
|
4297
4314
|
refusal2?.reason ?? `meet.${intent}: the server answered ${response.status}`,
|
|
4298
|
-
refusal2?.remedy ?? "You are still in the waiting room. Try again."
|
|
4315
|
+
refusal2?.remedy ?? (intent === "withdraw" ? "You are still in the waiting room. Try again." : "Nothing was changed. Try again.")
|
|
4299
4316
|
);
|
|
4300
4317
|
}
|
|
4301
4318
|
}
|
|
4302
4319
|
function hint() {
|
|
4303
4320
|
channel?.send(MEET_EVENTS.stateChanged, { v: null });
|
|
4304
4321
|
}
|
|
4322
|
+
async function tellServerAdmissionChanged(operation) {
|
|
4323
|
+
const meeting = store.snapshot().meeting;
|
|
4324
|
+
if (meeting === null) return;
|
|
4325
|
+
const pass = options.resolvePass?.() ?? null;
|
|
4326
|
+
try {
|
|
4327
|
+
const response = await options.api.request({
|
|
4328
|
+
path: MEET_ROUTES.admissionChanged,
|
|
4329
|
+
method: "POST",
|
|
4330
|
+
requireAuth: false,
|
|
4331
|
+
operation,
|
|
4332
|
+
body: { meeting_id: meeting.id },
|
|
4333
|
+
...pass !== null ? { headers: { [MEETING_PASS_HEADER]: pass } } : {}
|
|
4334
|
+
});
|
|
4335
|
+
if (!response.ok) {
|
|
4336
|
+
report(
|
|
4337
|
+
"warn",
|
|
4338
|
+
`${operation}: the server answered ${response.status} when told about the admission change`,
|
|
4339
|
+
"The decision was saved. The waiting count in the room may lag until the next change."
|
|
4340
|
+
);
|
|
4341
|
+
}
|
|
4342
|
+
} catch (error) {
|
|
4343
|
+
report(
|
|
4344
|
+
"warn",
|
|
4345
|
+
`${operation}: ${String(error)}`,
|
|
4346
|
+
"The decision was saved. The waiting count in the room may lag until the next change."
|
|
4347
|
+
);
|
|
4348
|
+
}
|
|
4349
|
+
}
|
|
4305
4350
|
async function refreshQueue() {
|
|
4306
4351
|
const meeting = store.snapshot().meeting;
|
|
4307
4352
|
if (meeting === null || isGuest(identity) || !isHostRole(effectiveRole(store.snapshot()))) return;
|
|
@@ -4457,6 +4502,7 @@ function createMeetingSession(options) {
|
|
|
4457
4502
|
decidedAt: (/* @__PURE__ */ new Date()).toISOString(),
|
|
4458
4503
|
decidedBy: by
|
|
4459
4504
|
});
|
|
4505
|
+
await tellServerAdmissionChanged("meet.admit");
|
|
4460
4506
|
hint();
|
|
4461
4507
|
},
|
|
4462
4508
|
async undeny(entry) {
|
|
@@ -4469,6 +4515,7 @@ function createMeetingSession(options) {
|
|
|
4469
4515
|
byUserId: by
|
|
4470
4516
|
});
|
|
4471
4517
|
store.upsertLobby({ ...entry, state: decidedState(row.admissionState), decidedAt: (/* @__PURE__ */ new Date()).toISOString(), decidedBy: by });
|
|
4518
|
+
await tellServerAdmissionChanged("meet.undeny");
|
|
4472
4519
|
hint();
|
|
4473
4520
|
},
|
|
4474
4521
|
async admitAll() {
|
|
@@ -4478,6 +4525,7 @@ function createMeetingSession(options) {
|
|
|
4478
4525
|
for (const row of rows) {
|
|
4479
4526
|
store.setAdmission(row.identity, row.admissionState, by);
|
|
4480
4527
|
}
|
|
4528
|
+
await tellServerAdmissionChanged("meet.admitAll");
|
|
4481
4529
|
hint();
|
|
4482
4530
|
},
|
|
4483
4531
|
startWaiting() {
|
|
@@ -4519,6 +4567,10 @@ function createMeetingSession(options) {
|
|
|
4519
4567
|
store.dispatch({ type: "admission", admission: "withdrawn" });
|
|
4520
4568
|
hint();
|
|
4521
4569
|
},
|
|
4570
|
+
async hostLeave(intent) {
|
|
4571
|
+
if (intent === "end_for_all") await postLeave("end_for_all");
|
|
4572
|
+
else await postLeave("assign_and_leave", { assign_to: intent.assignTo });
|
|
4573
|
+
},
|
|
4522
4574
|
async askAgain() {
|
|
4523
4575
|
await options.rejoin?.();
|
|
4524
4576
|
},
|
|
@@ -4981,18 +5033,6 @@ var ROOM_EVENTS = {
|
|
|
4981
5033
|
wrapUp: "matrx.meet.wrap_up",
|
|
4982
5034
|
/** The meeting ended for everyone. Payload `{ meeting_id, ended_at, trigger }`. */
|
|
4983
5035
|
ended: "matrx.meet.ended",
|
|
4984
|
-
/**
|
|
4985
|
-
* A `meet_recordings` row changed state. Payload
|
|
4986
|
-
* `{ meeting_id, recording: { id, state, started_by } }`.
|
|
4987
|
-
*
|
|
4988
|
-
* 🚨 THIS IS THE ONLY WAY A GUEST LEARNS A RECORDING IS RUNNING. PreJoin
|
|
4989
|
-
* promises every joiner an indicator, and `anon` holds no SELECT grant on
|
|
4990
|
-
* `meet_recordings` (MRI-A10 settled that widening it is forbidden), so
|
|
4991
|
-
* without this packet the promise was a sentence with nothing behind it: on
|
|
4992
|
-
* production 2026-09-09 the guest lane showed no indicator at all while the
|
|
4993
|
-
* host recorded (MRI-C8).
|
|
4994
|
-
*/
|
|
4995
|
-
recording: "matrx.meet.recording",
|
|
4996
5036
|
/**
|
|
4997
5037
|
* A chat message to everyone was SAVED (Meet wave 2). Payload
|
|
4998
5038
|
* `{ message: { id, meeting_id, sender_identity, sender_name, body, created_at } }`.
|
|
@@ -5045,8 +5085,6 @@ function applyRoomEvent(args) {
|
|
|
5045
5085
|
return applyWrapUp(store, payload, now);
|
|
5046
5086
|
case ROOM_EVENTS.ended:
|
|
5047
5087
|
return applyEnded(store, payload);
|
|
5048
|
-
case ROOM_EVENTS.recording:
|
|
5049
|
-
return applyRecording(store, payload);
|
|
5050
5088
|
case ROOM_EVENTS.chat:
|
|
5051
5089
|
return applyChat(store, payload);
|
|
5052
5090
|
case ROOM_EVENTS.moderation:
|
|
@@ -5126,9 +5164,9 @@ function applyWrapUp(store, payload, now) {
|
|
|
5126
5164
|
store.announceWrapUp(announcement);
|
|
5127
5165
|
return HANDLED;
|
|
5128
5166
|
}
|
|
5129
|
-
function
|
|
5167
|
+
function applyRecordingState(store, payload) {
|
|
5130
5168
|
const row = object(payload["recording"]);
|
|
5131
|
-
if (row === null) return ignored("
|
|
5169
|
+
if (row === null) return ignored("room state carries no recording");
|
|
5132
5170
|
const meetingId = text2(payload["meeting_id"]);
|
|
5133
5171
|
const meeting = store.snapshot().meeting;
|
|
5134
5172
|
if (meeting !== null && meetingId !== null && meetingId !== meeting.id) {
|
|
@@ -5210,19 +5248,6 @@ function applyModeration(store, payload, now) {
|
|
|
5210
5248
|
}
|
|
5211
5249
|
const kind = text2(payload["kind"]);
|
|
5212
5250
|
switch (kind) {
|
|
5213
|
-
case "lower-hands": {
|
|
5214
|
-
const raw = payload["identities"];
|
|
5215
|
-
const everyone = raw === "*";
|
|
5216
|
-
const named = new Set(
|
|
5217
|
-
Array.isArray(raw) ? raw.filter((value) => typeof value === "string") : []
|
|
5218
|
-
);
|
|
5219
|
-
for (const participant of snapshot.participants) {
|
|
5220
|
-
if (participant.handRaised && (everyone || named.has(participant.identity))) {
|
|
5221
|
-
store.setHand(participant.identity, false);
|
|
5222
|
-
}
|
|
5223
|
-
}
|
|
5224
|
-
return HANDLED;
|
|
5225
|
-
}
|
|
5226
5251
|
case "role": {
|
|
5227
5252
|
const identity = text2(payload["identity"]);
|
|
5228
5253
|
const role = text2(payload["role"]);
|
|
@@ -5316,7 +5341,6 @@ function noteTakerLabel(status) {
|
|
|
5316
5341
|
|
|
5317
5342
|
// src/core/room-driver.ts
|
|
5318
5343
|
var DATA_TOPICS = {
|
|
5319
|
-
hand: "mx.hand",
|
|
5320
5344
|
reaction: "mx.reaction",
|
|
5321
5345
|
chat: "mx.chat"
|
|
5322
5346
|
};
|
|
@@ -5340,6 +5364,11 @@ function SCREEN_SHARE_BLOCKED_REMEDY_FOR(remedy) {
|
|
|
5340
5364
|
return /administrator|policy/i.test(remedy) ? remedy : SCREEN_SHARE_BLOCKED_REMEDY;
|
|
5341
5365
|
}
|
|
5342
5366
|
var PARTICIPANT_ROLE_ATTRIBUTE = "matrx.participant.role";
|
|
5367
|
+
var PARTICIPANT_HAND_ATTRIBUTE = "matrx.hand";
|
|
5368
|
+
function handFrom(value) {
|
|
5369
|
+
const at = value === void 0 || value.length === 0 ? Number.NaN : Number(value);
|
|
5370
|
+
return Number.isFinite(at) ? { handRaised: true, handRaisedAt: at } : { handRaised: false, handRaisedAt: null };
|
|
5371
|
+
}
|
|
5343
5372
|
var PARTICIPANT_CAN_RECORD_ATTRIBUTE = "matrx.participant.can_record";
|
|
5344
5373
|
var CONNECTION_KIND_ATTRIBUTE = "matrx.participant.connection_kind";
|
|
5345
5374
|
var PARTICIPANT_STANDING_ATTRIBUTE = "matrx.participant.standing";
|
|
@@ -5393,7 +5422,10 @@ function projectParticipant(driver, joinedAt) {
|
|
|
5393
5422
|
cameraEnabled: driver.cameraEnabled,
|
|
5394
5423
|
screenSharing: driver.screenSharing,
|
|
5395
5424
|
speaking: false,
|
|
5396
|
-
|
|
5425
|
+
// S8: a hand is the SERVER's (`hand_raised_at` on the person row, `/moderate raise_hand`),
|
|
5426
|
+
// replicated as the `matrx.hand` attribute: epoch ms of the raise, "" when down. It is
|
|
5427
|
+
// therefore delivered to late joiners and survives refresh and reconnect.
|
|
5428
|
+
...handFrom(driver.attributes[PARTICIPANT_HAND_ATTRIBUTE]),
|
|
5397
5429
|
quality: "unknown",
|
|
5398
5430
|
joinedAt
|
|
5399
5431
|
};
|
|
@@ -5523,40 +5555,38 @@ function createRoomEngine(options) {
|
|
|
5523
5555
|
},
|
|
5524
5556
|
participants(list) {
|
|
5525
5557
|
const joinedAt = new Date(now()).toISOString();
|
|
5526
|
-
const before = new Set(store.snapshot().participants.map((p) => p.identity));
|
|
5527
5558
|
projectRoster(list, joinedAt);
|
|
5528
|
-
const snapshot = store.snapshot();
|
|
5529
|
-
const newcomer = snapshot.participants.some(
|
|
5530
|
-
(participant) => !participant.isLocal && !before.has(participant.identity)
|
|
5531
|
-
);
|
|
5532
|
-
const me = snapshot.participants.find((p) => p.identity === snapshot.localIdentity);
|
|
5533
|
-
if (newcomer && before.size > 0 && me?.handRaised === true) {
|
|
5534
|
-
void publish(DATA_TOPICS.hand, { raised: true, raisedAt: me.handRaisedAt ?? now() }).catch(
|
|
5535
|
-
() => void 0
|
|
5536
|
-
);
|
|
5537
|
-
}
|
|
5538
5559
|
},
|
|
5539
5560
|
participantLeft(who, reason) {
|
|
5540
5561
|
departures.set(who, reason);
|
|
5541
5562
|
},
|
|
5542
5563
|
roomMetadata(metadata) {
|
|
5543
|
-
let
|
|
5564
|
+
let parsed = {};
|
|
5544
5565
|
if (metadata.length > 0) {
|
|
5545
5566
|
try {
|
|
5546
|
-
const
|
|
5547
|
-
if (typeof
|
|
5548
|
-
const value = parsed.spotlight;
|
|
5549
|
-
if (typeof value === "string" && value.length > 0) spotlight = asParticipantIdentity(value);
|
|
5550
|
-
}
|
|
5567
|
+
const value = JSON.parse(metadata);
|
|
5568
|
+
if (typeof value === "object" && value !== null) parsed = value;
|
|
5551
5569
|
} catch {
|
|
5552
5570
|
report(
|
|
5553
5571
|
"warn",
|
|
5554
|
-
"meet: the room's metadata is not JSON, so
|
|
5555
|
-
"Only aidream writes room metadata; check services/meet/
|
|
5572
|
+
"meet: the room's metadata is not JSON, so the room state was not read from it",
|
|
5573
|
+
"Only aidream writes room metadata; check services/meet/room_state.py."
|
|
5556
5574
|
);
|
|
5557
5575
|
}
|
|
5558
5576
|
}
|
|
5559
|
-
|
|
5577
|
+
const key = (name) => {
|
|
5578
|
+
const value = parsed[name];
|
|
5579
|
+
return typeof value === "string" && value.length > 0 ? asParticipantIdentity(personOf(value)) : null;
|
|
5580
|
+
};
|
|
5581
|
+
const waiting = typeof parsed["waiting_count"] === "number" ? parsed["waiting_count"] : 0;
|
|
5582
|
+
const before = store.snapshot();
|
|
5583
|
+
const next = { spotlight: key("spotlight"), actingHost: key("acting_host"), presenter: key("presenter"), waitingCount: waiting };
|
|
5584
|
+
if (before.spotlight !== next.spotlight || before.actingHost !== next.actingHost || before.presenter !== next.presenter || before.waitingCount !== next.waitingCount) {
|
|
5585
|
+
store.patch(next);
|
|
5586
|
+
}
|
|
5587
|
+
if (parsed["recording"] !== void 0 && parsed["recording"] !== null) {
|
|
5588
|
+
applyRecordingState(store, { meeting_id: parsed["meeting_id"], recording: parsed["recording"] });
|
|
5589
|
+
}
|
|
5560
5590
|
},
|
|
5561
5591
|
activeSpeakers(identities) {
|
|
5562
5592
|
store.setSpeaking([...new Set(identities.map((who) => personOf(who)))]);
|
|
@@ -5658,13 +5688,6 @@ function createRoomEngine(options) {
|
|
|
5658
5688
|
}
|
|
5659
5689
|
const sender = personOf(from ?? "");
|
|
5660
5690
|
switch (topic) {
|
|
5661
|
-
case DATA_TOPICS.hand:
|
|
5662
|
-
store.setHand(
|
|
5663
|
-
sender,
|
|
5664
|
-
envelope.raised === true,
|
|
5665
|
-
typeof envelope.raisedAt === "number" ? envelope.raisedAt : now()
|
|
5666
|
-
);
|
|
5667
|
-
return;
|
|
5668
5691
|
case DATA_TOPICS.reaction:
|
|
5669
5692
|
if (typeof envelope.emoji === "string") {
|
|
5670
5693
|
store.addReaction({
|
|
@@ -5763,6 +5786,7 @@ function createRoomEngine(options) {
|
|
|
5763
5786
|
current = token;
|
|
5764
5787
|
lastServerUrl = token.serverUrl;
|
|
5765
5788
|
connectionPolicy = token.connectionPolicy ?? DEFAULT_CONNECTION_POLICY;
|
|
5789
|
+
if (token.leavePolicy !== void 0) store.patch({ leavePolicy: token.leavePolicy });
|
|
5766
5790
|
store.patch({
|
|
5767
5791
|
localIdentity: token.identity,
|
|
5768
5792
|
breakoutRoomId: token.breakoutRoomId ?? null,
|
|
@@ -6129,11 +6153,6 @@ function createRoomEngine(options) {
|
|
|
6129
6153
|
dismissShareNotice() {
|
|
6130
6154
|
if (store.snapshot().share.state === "refused") store.patch({ share: NO_SHARE });
|
|
6131
6155
|
},
|
|
6132
|
-
async raiseHand(raised) {
|
|
6133
|
-
const local = store.snapshot().localIdentity;
|
|
6134
|
-
if (local !== null) store.setHand(local, raised);
|
|
6135
|
-
await publish(DATA_TOPICS.hand, { raised, raisedAt: now() });
|
|
6136
|
-
},
|
|
6137
6156
|
async react(emoji) {
|
|
6138
6157
|
const local = store.snapshot().localIdentity;
|
|
6139
6158
|
const id = `${local ?? "local"}-${now()}`;
|
|
@@ -6262,6 +6281,8 @@ var MODERATION_ACTIONS = [
|
|
|
6262
6281
|
"ask_unmute",
|
|
6263
6282
|
"ask_all_unmute",
|
|
6264
6283
|
"spotlight",
|
|
6284
|
+
"present",
|
|
6285
|
+
"raise_hand",
|
|
6265
6286
|
"lower_hand",
|
|
6266
6287
|
"lower_all_hands",
|
|
6267
6288
|
"make_cohost",
|
|
@@ -6292,7 +6313,22 @@ function createInRoomClient(options) {
|
|
|
6292
6313
|
function refusal2(response, operation, remedy) {
|
|
6293
6314
|
return refusalFromResponse(response, operation, remedy);
|
|
6294
6315
|
}
|
|
6295
|
-
|
|
6316
|
+
let toldPresenting = false;
|
|
6317
|
+
let tellChain = Promise.resolve();
|
|
6318
|
+
store.subscribe(() => {
|
|
6319
|
+
const local = store.snapshot().participants.find((p) => p.isLocal);
|
|
6320
|
+
const sharing = local?.screenSharing === true;
|
|
6321
|
+
if (sharing === toldPresenting || local === void 0) return;
|
|
6322
|
+
toldPresenting = sharing;
|
|
6323
|
+
const on = sharing;
|
|
6324
|
+
tellChain = tellChain.then(() => client.moderate({ action: "present", on })).then(
|
|
6325
|
+
() => void 0,
|
|
6326
|
+
(error) => {
|
|
6327
|
+
console.warn(`[meet] the server did not take presenter=${String(on)}: ${String(error)}`);
|
|
6328
|
+
}
|
|
6329
|
+
);
|
|
6330
|
+
});
|
|
6331
|
+
const client = {
|
|
6296
6332
|
async moderate(request) {
|
|
6297
6333
|
const operation = `meet.moderate(${request.action})`;
|
|
6298
6334
|
const hdrs = headers();
|
|
@@ -6305,7 +6341,8 @@ function createInRoomClient(options) {
|
|
|
6305
6341
|
action: request.action,
|
|
6306
6342
|
...request.identity != null ? { identity: request.identity } : {},
|
|
6307
6343
|
...request.name !== void 0 ? { name: request.name } : {},
|
|
6308
|
-
...request.reason !== void 0 ? { reason: request.reason } : {}
|
|
6344
|
+
...request.reason !== void 0 ? { reason: request.reason } : {},
|
|
6345
|
+
...request.on !== void 0 ? { on: request.on } : {}
|
|
6309
6346
|
},
|
|
6310
6347
|
...hdrs !== void 0 ? { headers: hdrs } : {}
|
|
6311
6348
|
});
|
|
@@ -6496,6 +6533,7 @@ function createInRoomClient(options) {
|
|
|
6496
6533
|
});
|
|
6497
6534
|
}
|
|
6498
6535
|
};
|
|
6536
|
+
return client;
|
|
6499
6537
|
}
|
|
6500
6538
|
|
|
6501
6539
|
// src/core/livekit-driver.ts
|
|
@@ -8159,16 +8197,16 @@ function useRoom() {
|
|
|
8159
8197
|
async () => requireEngine(engine, "toggleCamera").setCameraEnabled(!(local?.cameraEnabled ?? false)),
|
|
8160
8198
|
[engine, local]
|
|
8161
8199
|
),
|
|
8162
|
-
toggleScreenShare: useCallback2(
|
|
8163
|
-
|
|
8164
|
-
|
|
8165
|
-
|
|
8166
|
-
|
|
8167
|
-
|
|
8168
|
-
toggleHand: useCallback2(
|
|
8169
|
-
|
|
8170
|
-
|
|
8171
|
-
),
|
|
8200
|
+
toggleScreenShare: useCallback2(async () => {
|
|
8201
|
+
const on = !(local?.screenSharing ?? false);
|
|
8202
|
+
await requireEngine(engine, "toggleScreenShare").setScreenShareEnabled(on);
|
|
8203
|
+
}, [engine, local]),
|
|
8204
|
+
// S8: a hand is SERVER state (`/moderate raise_hand · lower_hand`, server time), replicated
|
|
8205
|
+
// back as the `matrx.hand` attribute — no client sets its own hand on the wire.
|
|
8206
|
+
toggleHand: useCallback2(async () => {
|
|
8207
|
+
if (host === null) throw noRuntime("toggleHand");
|
|
8208
|
+
await host.inRoom.moderate({ action: local?.handRaised === true ? "lower_hand" : "raise_hand" });
|
|
8209
|
+
}, [host, local]),
|
|
8172
8210
|
react: useCallback2(
|
|
8173
8211
|
async (emoji) => requireEngine(engine, "react").react(emoji),
|
|
8174
8212
|
[engine]
|
|
@@ -8350,6 +8388,31 @@ function useWaiting() {
|
|
|
8350
8388
|
askAgain: useCallback2(async () => requireSession(session, "askAgain").askAgain(), [session])
|
|
8351
8389
|
};
|
|
8352
8390
|
}
|
|
8391
|
+
function useLeave() {
|
|
8392
|
+
const host = useMeetHost();
|
|
8393
|
+
const snapshot = useMeetSnapshot();
|
|
8394
|
+
const session = host?.meetings ?? null;
|
|
8395
|
+
const engine = host?.engine ?? null;
|
|
8396
|
+
const policy = snapshot?.leavePolicy ?? DEFAULT_LEAVE_POLICY;
|
|
8397
|
+
const participants = snapshot?.participants ?? [];
|
|
8398
|
+
const me = participants.find((p) => p.isLocal);
|
|
8399
|
+
const candidates = participants.filter((p) => !p.isLocal && !p.isAgent && p.role !== "viewer");
|
|
8400
|
+
const choices = leaveChoices({ policy, role: me?.role ?? null, othersPresent: candidates.length > 0 });
|
|
8401
|
+
return {
|
|
8402
|
+
choices,
|
|
8403
|
+
policy,
|
|
8404
|
+
candidates,
|
|
8405
|
+
leave: useCallback2(async () => requireEngine(engine, "leave").leave(), [engine]),
|
|
8406
|
+
endForAll: useCallback2(async () => requireSession(session, "endForAll").hostLeave("end_for_all"), [session]),
|
|
8407
|
+
assignAndLeave: useCallback2(
|
|
8408
|
+
async (person) => {
|
|
8409
|
+
await requireSession(session, "assignAndLeave").hostLeave({ assignTo: String(person.identity) });
|
|
8410
|
+
await requireEngine(engine, "leave").leave();
|
|
8411
|
+
},
|
|
8412
|
+
[session, engine]
|
|
8413
|
+
)
|
|
8414
|
+
};
|
|
8415
|
+
}
|
|
8353
8416
|
function useRecording() {
|
|
8354
8417
|
const host = useMeetHost();
|
|
8355
8418
|
const snapshot = useMeetSnapshot();
|
|
@@ -11273,19 +11336,75 @@ function ControlBar({ onLeave }) {
|
|
|
11273
11336
|
] }) : null
|
|
11274
11337
|
] }) }) : null
|
|
11275
11338
|
] }),
|
|
11276
|
-
/* @__PURE__ */ jsx6(
|
|
11277
|
-
|
|
11339
|
+
/* @__PURE__ */ jsx6(LeaveControl, { onLeave })
|
|
11340
|
+
] })
|
|
11341
|
+
] });
|
|
11342
|
+
}
|
|
11343
|
+
var LEAVE_WORDS = {
|
|
11344
|
+
leave_or_end: { leave: "Leave call", assign_and_leave: "Assign a new host", end_for_all: "End call for everyone" },
|
|
11345
|
+
must_assign_or_end: { leave: "Leave meeting", assign_and_leave: "Assign a new host", end_for_all: "End meeting for all" },
|
|
11346
|
+
leave_only: { leave: "Leave", assign_and_leave: "Assign a new host", end_for_all: "End meeting" }
|
|
11347
|
+
};
|
|
11348
|
+
function LeaveControl({ onLeave }) {
|
|
11349
|
+
const leave = useLeave();
|
|
11350
|
+
const [open, setOpen] = useState4("none");
|
|
11351
|
+
const [failure, setFailure] = useState4(null);
|
|
11352
|
+
const words = LEAVE_WORDS[leave.policy.hostLeavePrompt];
|
|
11353
|
+
const run = (work, leaving) => {
|
|
11354
|
+
setFailure(null);
|
|
11355
|
+
void work().then(() => {
|
|
11356
|
+
setOpen("none");
|
|
11357
|
+
if (leaving) onLeave?.();
|
|
11358
|
+
}).catch((error) => setFailure(error instanceof Error ? error.message : String(error)));
|
|
11359
|
+
};
|
|
11360
|
+
const leaveNow = () => run(leave.leave, true);
|
|
11361
|
+
const endNow = () => run(leave.endForAll, false);
|
|
11362
|
+
return /* @__PURE__ */ jsxs3("span", { className: "mx-meet__anchor", children: [
|
|
11363
|
+
/* @__PURE__ */ jsx6(
|
|
11364
|
+
ControlButton,
|
|
11365
|
+
{
|
|
11366
|
+
label: "Leave",
|
|
11367
|
+
danger: true,
|
|
11368
|
+
active: open !== "none",
|
|
11369
|
+
onClick: () => {
|
|
11370
|
+
if (leave.choices.length === 1 && leave.choices[0] === "leave") leaveNow();
|
|
11371
|
+
else setOpen(open === "none" ? "menu" : "none");
|
|
11372
|
+
},
|
|
11373
|
+
children: /* @__PURE__ */ jsx6(PhoneOffIcon, {})
|
|
11374
|
+
}
|
|
11375
|
+
),
|
|
11376
|
+
open !== "none" ? /* @__PURE__ */ jsx6(Popover, { label: open === "assign" ? words.assign_and_leave : "Leave", onClose: () => setOpen("none"), children: /* @__PURE__ */ jsxs3("div", { className: "mx-meet__menu", "data-meet-leave-menu": leave.policy.hostLeavePrompt, children: [
|
|
11377
|
+
open === "menu" ? leave.choices.map((choice) => /* @__PURE__ */ jsx6(
|
|
11378
|
+
"button",
|
|
11278
11379
|
{
|
|
11279
|
-
|
|
11280
|
-
danger:
|
|
11380
|
+
type: "button",
|
|
11381
|
+
className: choice === "end_for_all" ? "mx-meet__menu-item mx-meet__menu-item--danger" : "mx-meet__menu-item",
|
|
11281
11382
|
onClick: () => {
|
|
11282
|
-
|
|
11283
|
-
|
|
11383
|
+
if (choice === "leave") leaveNow();
|
|
11384
|
+
else if (choice === "assign_and_leave") setOpen("assign");
|
|
11385
|
+
else if (leave.policy.endConfirm) setOpen("confirm");
|
|
11386
|
+
else endNow();
|
|
11284
11387
|
},
|
|
11285
|
-
children:
|
|
11286
|
-
}
|
|
11287
|
-
|
|
11288
|
-
|
|
11388
|
+
children: words[choice]
|
|
11389
|
+
},
|
|
11390
|
+
choice
|
|
11391
|
+
)) : null,
|
|
11392
|
+
open === "assign" ? leave.candidates.map((person) => /* @__PURE__ */ jsx6(
|
|
11393
|
+
"button",
|
|
11394
|
+
{
|
|
11395
|
+
type: "button",
|
|
11396
|
+
className: "mx-meet__menu-item",
|
|
11397
|
+
onClick: () => run(() => leave.assignAndLeave(person), true),
|
|
11398
|
+
children: person.displayName
|
|
11399
|
+
},
|
|
11400
|
+
String(person.identity)
|
|
11401
|
+
)) : null,
|
|
11402
|
+
open === "confirm" ? /* @__PURE__ */ jsxs3(Fragment, { children: [
|
|
11403
|
+
/* @__PURE__ */ jsx6("button", { type: "button", className: "mx-meet__menu-item mx-meet__menu-item--danger", onClick: endNow, children: words.end_for_all }),
|
|
11404
|
+
/* @__PURE__ */ jsx6("button", { type: "button", className: "mx-meet__menu-item", onClick: () => setOpen("menu"), children: "Cancel" })
|
|
11405
|
+
] }) : null,
|
|
11406
|
+
failure !== null ? /* @__PURE__ */ jsx6("p", { className: "mx-meet__menu-note", role: "alert", children: failure }) : null
|
|
11407
|
+
] }) }) : null
|
|
11289
11408
|
] });
|
|
11290
11409
|
}
|
|
11291
11410
|
function useUnreadChat(open) {
|
|
@@ -13893,6 +14012,7 @@ export {
|
|
|
13893
14012
|
DATA_TOPICS,
|
|
13894
14013
|
DEFAULT_CONNECTION_POLICY,
|
|
13895
14014
|
DEFAULT_EFFECTS,
|
|
14015
|
+
DEFAULT_LEAVE_POLICY,
|
|
13896
14016
|
DEFAULT_PAGE_SIZE,
|
|
13897
14017
|
EMPTY_COLLAB,
|
|
13898
14018
|
END_MEETING_NOT_SERVED,
|
|
@@ -13936,6 +14056,7 @@ export {
|
|
|
13936
14056
|
NO_SHARE,
|
|
13937
14057
|
ORGANIZATION_HEADER,
|
|
13938
14058
|
PARTICIPANT_CAN_RECORD_ATTRIBUTE,
|
|
14059
|
+
PARTICIPANT_HAND_ATTRIBUTE,
|
|
13939
14060
|
PARTICIPANT_ROLE_ATTRIBUTE,
|
|
13940
14061
|
PARTICIPANT_STANDING_ATTRIBUTE,
|
|
13941
14062
|
REACTION_TTL_MS,
|
|
@@ -14049,6 +14170,8 @@ export {
|
|
|
14049
14170
|
isWaitingPhase,
|
|
14050
14171
|
learnName,
|
|
14051
14172
|
learnNames,
|
|
14173
|
+
leaveChoices,
|
|
14174
|
+
leavePolicyFrom,
|
|
14052
14175
|
listenOnlyNotice,
|
|
14053
14176
|
loadFollowUpDraft,
|
|
14054
14177
|
loadGuestMeetingRecord,
|
|
@@ -14150,6 +14273,7 @@ export {
|
|
|
14150
14273
|
useHandQueue,
|
|
14151
14274
|
useHostControls,
|
|
14152
14275
|
useIsHost,
|
|
14276
|
+
useLeave,
|
|
14153
14277
|
useListenOnlyNotice,
|
|
14154
14278
|
useLobby,
|
|
14155
14279
|
useLocalMedia,
|