@ai-matrx/meet 0.11.32 → 0.11.33
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 +27 -0
- package/dist/core.cjs +133 -45
- package/dist/core.cjs.map +1 -1
- package/dist/core.d.cts +73 -4
- package/dist/core.d.ts +73 -4
- package/dist/core.js +133 -45
- package/dist/core.js.map +1 -1
- package/dist/index.cjs +133 -45
- package/dist/index.cjs.map +1 -1
- package/dist/index.js +133 -45
- package/dist/index.js.map +1 -1
- package/dist/react.cjs +476 -75
- package/dist/react.cjs.map +1 -1
- package/dist/react.d.cts +6 -0
- package/dist/react.d.ts +6 -0
- package/dist/react.js +476 -75
- package/dist/react.js.map +1 -1
- package/dist/skins/meet.cjs +217 -33
- package/dist/skins/meet.cjs.map +1 -1
- package/dist/skins/meet.js +217 -33
- package/dist/skins/meet.js.map +1 -1
- package/package.json +3 -3
package/dist/index.cjs
CHANGED
|
@@ -288,12 +288,18 @@ var MeetError = class extends Error {
|
|
|
288
288
|
* renders `refused:<reason>` (or `ended`) from it — never from the HTTP status.
|
|
289
289
|
*/
|
|
290
290
|
reason;
|
|
291
|
-
|
|
291
|
+
/**
|
|
292
|
+
* A refusal that lifts by itself says when: whole seconds from the moment the server answered
|
|
293
|
+
* (a denial's cooldown, `reknock_cooldown_seconds`). Null for every refusal that does not.
|
|
294
|
+
*/
|
|
295
|
+
retryAfterSeconds;
|
|
296
|
+
constructor(code, message, remedy, cause, reason, retryAfterSeconds) {
|
|
292
297
|
super(message);
|
|
293
298
|
this.name = "MeetError";
|
|
294
299
|
this.code = code;
|
|
295
300
|
this.remedy = remedy;
|
|
296
301
|
this.reason = reason ?? null;
|
|
302
|
+
this.retryAfterSeconds = retryAfterSeconds ?? null;
|
|
297
303
|
if (cause !== void 0) this.cause = cause;
|
|
298
304
|
}
|
|
299
305
|
/** True when retrying after the host re-establishes a session is the fix. */
|
|
@@ -973,7 +979,8 @@ function parseRoomToken(body, roomName) {
|
|
|
973
979
|
const serverUrl = readString(payload, "server_url") ?? readString(payload, "serverUrl");
|
|
974
980
|
const identity = readString(payload, "identity");
|
|
975
981
|
const expiresAt = readString(payload, "expires_at") ?? readString(payload, "expiresAt");
|
|
976
|
-
|
|
982
|
+
const secondDeviceChoice = readString(payload, "second_device_choice");
|
|
983
|
+
if (token === null && secondDeviceChoice === null || serverUrl === null || identity === null) {
|
|
977
984
|
throw invalidResponse(
|
|
978
985
|
"meet.token",
|
|
979
986
|
"the response is missing token, server_url, or identity \u2014 joining with a partial token would fail inside LiveKit with an opaque error instead of here"
|
|
@@ -985,11 +992,15 @@ function parseRoomToken(body, roomName) {
|
|
|
985
992
|
"the response carries no expires_at. Without it the package cannot refresh before the token dies, and a long meeting silently loses its ability to reconnect"
|
|
986
993
|
);
|
|
987
994
|
}
|
|
995
|
+
const personKey = readString(payload, "person_key") ?? identity;
|
|
988
996
|
return {
|
|
989
|
-
token,
|
|
997
|
+
token: token ?? "",
|
|
990
998
|
serverUrl,
|
|
991
999
|
roomName,
|
|
992
|
-
identity,
|
|
1000
|
+
identity: personKey,
|
|
1001
|
+
connectionIdentity: readString(payload, "connection_identity") ?? personKey,
|
|
1002
|
+
connectionKind: readString(payload, "connection_kind") === "companion" ? "companion" : "main",
|
|
1003
|
+
secondDeviceChoice,
|
|
993
1004
|
displayName: readString(payload, "display_name") ?? readString(payload, "displayName") ?? identity,
|
|
994
1005
|
expiresAt,
|
|
995
1006
|
canPublish: readBoolean(payload, "can_publish", true),
|
|
@@ -1064,7 +1075,9 @@ function createTokenClient(options) {
|
|
|
1064
1075
|
organization_id: input.identity.organizationId,
|
|
1065
1076
|
display_name: input.identity.displayName,
|
|
1066
1077
|
device_id: input.identity.deviceId,
|
|
1067
|
-
guest
|
|
1078
|
+
guest,
|
|
1079
|
+
...input.tabId !== void 0 ? { tab_id: input.tabId } : {},
|
|
1080
|
+
...input.secondDevice !== void 0 ? { second_device: input.secondDevice } : {}
|
|
1068
1081
|
}
|
|
1069
1082
|
});
|
|
1070
1083
|
} catch (error) {
|
|
@@ -1081,7 +1094,8 @@ function createTokenClient(options) {
|
|
|
1081
1094
|
said?.message ?? "You can't join this meeting.",
|
|
1082
1095
|
said?.remedy ?? "Ask the person who shared the link to let you in, then open the link again.",
|
|
1083
1096
|
void 0,
|
|
1084
|
-
said?.reason ?? null
|
|
1097
|
+
said?.reason ?? null,
|
|
1098
|
+
said?.retryAfterSeconds ?? null
|
|
1085
1099
|
);
|
|
1086
1100
|
}
|
|
1087
1101
|
if (response.status === 404) {
|
|
@@ -1139,6 +1153,10 @@ function createTokenClient(options) {
|
|
|
1139
1153
|
if (!expired(entry)) return Promise.resolve(entry.token);
|
|
1140
1154
|
}
|
|
1141
1155
|
const pending = request(input, true).then((token) => {
|
|
1156
|
+
if (token.secondDeviceChoice != null) {
|
|
1157
|
+
cache.delete(input.roomName);
|
|
1158
|
+
return token;
|
|
1159
|
+
}
|
|
1142
1160
|
cache.set(input.roomName, { token, mintedAt: now(), inFlight: null });
|
|
1143
1161
|
remember(token);
|
|
1144
1162
|
return token;
|
|
@@ -1173,6 +1191,9 @@ function placeholder(roomName) {
|
|
|
1173
1191
|
serverUrl: "",
|
|
1174
1192
|
roomName,
|
|
1175
1193
|
identity: "",
|
|
1194
|
+
connectionIdentity: "",
|
|
1195
|
+
connectionKind: "main",
|
|
1196
|
+
secondDeviceChoice: null,
|
|
1176
1197
|
displayName: "",
|
|
1177
1198
|
expiresAt: (/* @__PURE__ */ new Date(0)).toISOString(),
|
|
1178
1199
|
canPublish: false,
|
|
@@ -1197,7 +1218,9 @@ async function serverSentence(response) {
|
|
|
1197
1218
|
if (message === null) return null;
|
|
1198
1219
|
const remedy = typeof source.remedy === "string" && source.remedy.trim() !== "" ? source.remedy : void 0;
|
|
1199
1220
|
const reason = typeof source.reason === "string" && source.reason !== "" ? source.reason : null;
|
|
1200
|
-
|
|
1221
|
+
const retry = source.retry_after_seconds;
|
|
1222
|
+
const retryAfterSeconds = typeof retry === "number" && Number.isFinite(retry) && retry > 0 ? Math.ceil(retry) : null;
|
|
1223
|
+
return { message, remedy, reason, retryAfterSeconds };
|
|
1201
1224
|
} catch {
|
|
1202
1225
|
return null;
|
|
1203
1226
|
}
|
|
@@ -1673,7 +1696,10 @@ var JOINABLE_FROM = /* @__PURE__ */ new Set([
|
|
|
1673
1696
|
"superseded",
|
|
1674
1697
|
"inactive",
|
|
1675
1698
|
"denied",
|
|
1676
|
-
"knock_expired"
|
|
1699
|
+
"knock_expired",
|
|
1700
|
+
// A refused knock whose rule lets the person ask again (a cooldown that ran out): the machine
|
|
1701
|
+
// allows the press; the server's gate decides whether it knocks or is refused again.
|
|
1702
|
+
"refused:lobby_rejects"
|
|
1677
1703
|
]);
|
|
1678
1704
|
var NOT_ENDABLE = /* @__PURE__ */ new Set([
|
|
1679
1705
|
"ended",
|
|
@@ -1757,6 +1783,8 @@ function nextPhase(from, event) {
|
|
|
1757
1783
|
return WAITING_PHASES.has(from) ? "joining" : null;
|
|
1758
1784
|
case "connected":
|
|
1759
1785
|
return from === "joining" ? "in_call" : null;
|
|
1786
|
+
case "second_device":
|
|
1787
|
+
return from === "joining" ? "prejoin" : null;
|
|
1760
1788
|
case "join_failed":
|
|
1761
1789
|
return from === "joining" ? refusalPhase(event.reason) : null;
|
|
1762
1790
|
case "exit":
|
|
@@ -1774,7 +1802,7 @@ function nextPhase(from, event) {
|
|
|
1774
1802
|
case "call_outcome":
|
|
1775
1803
|
return RINGING.has(from) ? event.outcome : null;
|
|
1776
1804
|
case "reset":
|
|
1777
|
-
return LIVE_PHASES.has(from) ? null : "resolving";
|
|
1805
|
+
return LIVE_PHASES.has(from) && event.teardown !== true ? null : "resolving";
|
|
1778
1806
|
}
|
|
1779
1807
|
}
|
|
1780
1808
|
function resolveExit(input) {
|
|
@@ -2080,8 +2108,8 @@ function createCallCenter(options) {
|
|
|
2080
2108
|
"A late accept must not resurrect a settled call. Close the ringing UI; the caller has already moved on."
|
|
2081
2109
|
);
|
|
2082
2110
|
}
|
|
2083
|
-
if (options.store.snapshot().phase === "ringing_in") options.store.dispatch({ type: "call_answered" });
|
|
2084
2111
|
const settled = await repository.settleCallInvite(id, "connecting", null);
|
|
2112
|
+
if (options.store.snapshot().phase === "ringing_in") options.store.dispatch({ type: "call_answered" });
|
|
2085
2113
|
options.store.patch({ call: { invite: settled, direction: "incoming" } });
|
|
2086
2114
|
settleLocal(settled);
|
|
2087
2115
|
channel?.send(MEET_EVENTS.settled, settled);
|
|
@@ -3452,11 +3480,17 @@ async function loadMeetingActivities(api, meetingId, meetingPass = null) {
|
|
|
3452
3480
|
}
|
|
3453
3481
|
|
|
3454
3482
|
// src/core/names.ts
|
|
3483
|
+
var CONNECTION_SEPARATOR = "~";
|
|
3484
|
+
function personOf(identity) {
|
|
3485
|
+
const value = String(identity ?? "").trim();
|
|
3486
|
+
const cut = value.indexOf(CONNECTION_SEPARATOR);
|
|
3487
|
+
return cut === -1 ? value : value.slice(0, cut);
|
|
3488
|
+
}
|
|
3455
3489
|
function isGuestIdentity(identity) {
|
|
3456
|
-
return
|
|
3490
|
+
return personOf(identity).startsWith("guest:");
|
|
3457
3491
|
}
|
|
3458
3492
|
function isAgentIdentity(identity) {
|
|
3459
|
-
return
|
|
3493
|
+
return personOf(identity).startsWith("agent:");
|
|
3460
3494
|
}
|
|
3461
3495
|
var NOTE_TAKER_PRODUCT_NAME = "Matrx note-taker";
|
|
3462
3496
|
var AGENT_PRODUCT_NAME = "Matrx agent";
|
|
@@ -3670,6 +3704,7 @@ var EMPTY = {
|
|
|
3670
3704
|
waiting: null,
|
|
3671
3705
|
meeting: null,
|
|
3672
3706
|
localIdentity: null,
|
|
3707
|
+
secondDevice: null,
|
|
3673
3708
|
grants: NO_GRANTS,
|
|
3674
3709
|
participants: [],
|
|
3675
3710
|
activeSpeakers: [],
|
|
@@ -3920,7 +3955,8 @@ function createMeetStore(initial = {}) {
|
|
|
3920
3955
|
commit({ ...state, media: { ...state.media, [kind]: next } });
|
|
3921
3956
|
},
|
|
3922
3957
|
reset() {
|
|
3923
|
-
|
|
3958
|
+
const phase = nextPhase(state.phase, { type: "reset", teardown: true }) ?? EMPTY.phase;
|
|
3959
|
+
commit({ ...EMPTY, effects: state.effects, phase, reason: reasonOf(phase), phaseNotice: null });
|
|
3924
3960
|
}
|
|
3925
3961
|
};
|
|
3926
3962
|
}
|
|
@@ -4999,6 +5035,28 @@ function lobbyEntryFrom(attendee, now = Date.now()) {
|
|
|
4999
5035
|
decidedBy: null
|
|
5000
5036
|
};
|
|
5001
5037
|
}
|
|
5038
|
+
function deniedEntryFrom(attendee) {
|
|
5039
|
+
if (attendee.admissionState !== "denied") return null;
|
|
5040
|
+
return {
|
|
5041
|
+
identity: attendee.identity,
|
|
5042
|
+
displayName: attendee.displayName,
|
|
5043
|
+
avatarUrl: attendee.avatarUrl,
|
|
5044
|
+
userId: attendee.userId,
|
|
5045
|
+
state: "denied",
|
|
5046
|
+
knockedAt: attendee.knockedAt ?? attendee.joinedAt ?? (/* @__PURE__ */ new Date()).toISOString(),
|
|
5047
|
+
knockExpiresAt: null,
|
|
5048
|
+
decidedAt: null,
|
|
5049
|
+
decidedBy: null
|
|
5050
|
+
};
|
|
5051
|
+
}
|
|
5052
|
+
function applyDoorRow(store, attendee) {
|
|
5053
|
+
const entry = lobbyEntryFrom(attendee) ?? deniedEntryFrom(attendee);
|
|
5054
|
+
if (entry !== null) {
|
|
5055
|
+
store.upsertLobby(entry);
|
|
5056
|
+
return;
|
|
5057
|
+
}
|
|
5058
|
+
store.setAdmission(attendee.identity, decidedState(attendee.admissionState), null);
|
|
5059
|
+
}
|
|
5002
5060
|
function decidedState(state) {
|
|
5003
5061
|
return WAITING_ADMISSION_STATES.includes(state) ? "expired" : state;
|
|
5004
5062
|
}
|
|
@@ -5133,6 +5191,7 @@ function createMeetingSession(options) {
|
|
|
5133
5191
|
admission,
|
|
5134
5192
|
admittersPresent: read.admitters_present ?? null,
|
|
5135
5193
|
reknockAllowed: read.reknock_allowed ?? true,
|
|
5194
|
+
reknockAt: typeof read.reknock_in_seconds === "number" && read.reknock_in_seconds > 0 ? Date.now() + read.reknock_in_seconds * 1e3 : null,
|
|
5136
5195
|
knockExpiresAt: read.knock_expires_at ?? null,
|
|
5137
5196
|
pollSeconds: read.poll_seconds ?? snapshot.waiting?.pollSeconds ?? 3,
|
|
5138
5197
|
liveVersion: read.live_version ?? 0
|
|
@@ -5215,11 +5274,7 @@ function createMeetingSession(options) {
|
|
|
5215
5274
|
try {
|
|
5216
5275
|
const attendees = await repository.attendees(meeting.id);
|
|
5217
5276
|
store.learn(attendees);
|
|
5218
|
-
for (const attendee of attendees)
|
|
5219
|
-
const entry = lobbyEntryFrom(attendee);
|
|
5220
|
-
if (entry !== null) store.upsertLobby(entry);
|
|
5221
|
-
else store.setAdmission(attendee.identity, decidedState(attendee.admissionState), null);
|
|
5222
|
-
}
|
|
5277
|
+
for (const attendee of attendees) applyDoorRow(store, attendee);
|
|
5223
5278
|
} catch {
|
|
5224
5279
|
}
|
|
5225
5280
|
}
|
|
@@ -5301,12 +5356,7 @@ function createMeetingSession(options) {
|
|
|
5301
5356
|
if (row === null) return;
|
|
5302
5357
|
const attendee = repository.projectAttendee(row);
|
|
5303
5358
|
store.learn([attendee]);
|
|
5304
|
-
|
|
5305
|
-
if (entry !== null) {
|
|
5306
|
-
store.upsertLobby(entry);
|
|
5307
|
-
return;
|
|
5308
|
-
}
|
|
5309
|
-
store.setAdmission(attendee.identity, decidedState(attendee.admissionState), null);
|
|
5359
|
+
applyDoorRow(store, attendee);
|
|
5310
5360
|
}
|
|
5311
5361
|
},
|
|
5312
5362
|
{
|
|
@@ -5353,14 +5403,7 @@ function createMeetingSession(options) {
|
|
|
5353
5403
|
store.setRecording(latest.state, latest.startedBy, "record", latest.id);
|
|
5354
5404
|
}
|
|
5355
5405
|
store.learn(attendees);
|
|
5356
|
-
for (const attendee of attendees)
|
|
5357
|
-
const entry = lobbyEntryFrom(attendee);
|
|
5358
|
-
if (entry !== null) {
|
|
5359
|
-
store.upsertLobby(entry);
|
|
5360
|
-
} else {
|
|
5361
|
-
store.setAdmission(attendee.identity, decidedState(attendee.admissionState), null);
|
|
5362
|
-
}
|
|
5363
|
-
}
|
|
5406
|
+
for (const attendee of attendees) applyDoorRow(store, attendee);
|
|
5364
5407
|
}
|
|
5365
5408
|
});
|
|
5366
5409
|
meetingChannel.requestBackfill("manual");
|
|
@@ -6404,6 +6447,7 @@ function SCREEN_SHARE_BLOCKED_REMEDY_FOR(remedy) {
|
|
|
6404
6447
|
}
|
|
6405
6448
|
var PARTICIPANT_ROLE_ATTRIBUTE = "matrx.participant.role";
|
|
6406
6449
|
var PARTICIPANT_CAN_RECORD_ATTRIBUTE = "matrx.participant.can_record";
|
|
6450
|
+
var CONNECTION_KIND_ATTRIBUTE = "matrx.participant.connection_kind";
|
|
6407
6451
|
var PARTICIPANT_STANDING_ATTRIBUTE = "matrx.participant.standing";
|
|
6408
6452
|
var KNOWN_STANDINGS = ["member", "invited", "outsider", "guest"];
|
|
6409
6453
|
function parseStanding(value) {
|
|
@@ -6427,7 +6471,8 @@ function projectParticipant(driver, joinedAt) {
|
|
|
6427
6471
|
}
|
|
6428
6472
|
const userId = typeof metadata.user_id === "string" ? asUserId(metadata.user_id) : null;
|
|
6429
6473
|
return {
|
|
6430
|
-
|
|
6474
|
+
// S7: a tile is a PERSON; LiveKit names the tab's connection (`<person>~<tab>`).
|
|
6475
|
+
identity: personOf(driver.identity),
|
|
6431
6476
|
userId,
|
|
6432
6477
|
// 🚨 A RAW LIVEKIT IDENTITY IS NEVER A NAME (MRI-A18). Through 0.4.12 this
|
|
6433
6478
|
// line read `driver.name.length > 0 ? driver.name : driver.identity`, and
|
|
@@ -6474,6 +6519,11 @@ function createRoomEngine(options) {
|
|
|
6474
6519
|
let ghosts = /* @__PURE__ */ new Map();
|
|
6475
6520
|
let ghostTimer = null;
|
|
6476
6521
|
let liveRoster = [];
|
|
6522
|
+
let personConnections = /* @__PURE__ */ new Map();
|
|
6523
|
+
let mainConnection = /* @__PURE__ */ new Map();
|
|
6524
|
+
function connectionsOf(who) {
|
|
6525
|
+
return personConnections.get(personOf(who)) ?? [who];
|
|
6526
|
+
}
|
|
6477
6527
|
const departures = /* @__PURE__ */ new Map();
|
|
6478
6528
|
function requestAsNow() {
|
|
6479
6529
|
if (lastRequest === null) return null;
|
|
@@ -6538,7 +6588,12 @@ function createRoomEngine(options) {
|
|
|
6538
6588
|
function failJoin(error, operation) {
|
|
6539
6589
|
const normalized = normalizeMeetError(error, operation);
|
|
6540
6590
|
store.patch({
|
|
6541
|
-
failure: {
|
|
6591
|
+
failure: {
|
|
6592
|
+
message: normalized.message,
|
|
6593
|
+
remedy: normalized.remedy,
|
|
6594
|
+
reason: normalized.reason,
|
|
6595
|
+
retryAt: normalized.retryAfterSeconds === null ? null : now() + normalized.retryAfterSeconds * 1e3
|
|
6596
|
+
}
|
|
6542
6597
|
});
|
|
6543
6598
|
store.dispatch({ type: "join_failed", reason: normalized.reason ?? null });
|
|
6544
6599
|
report(
|
|
@@ -6610,10 +6665,10 @@ function createRoomEngine(options) {
|
|
|
6610
6665
|
if (store.snapshot().spotlight !== spotlight) store.patch({ spotlight });
|
|
6611
6666
|
},
|
|
6612
6667
|
activeSpeakers(identities) {
|
|
6613
|
-
store.setSpeaking(identities.map(
|
|
6668
|
+
store.setSpeaking([...new Set(identities.map((who) => personOf(who)))]);
|
|
6614
6669
|
},
|
|
6615
6670
|
quality(who, value) {
|
|
6616
|
-
store.setQuality(
|
|
6671
|
+
store.setQuality(personOf(who), value);
|
|
6617
6672
|
},
|
|
6618
6673
|
connectionState(state) {
|
|
6619
6674
|
supervisor.driverState(state);
|
|
@@ -6633,7 +6688,7 @@ function createRoomEngine(options) {
|
|
|
6633
6688
|
);
|
|
6634
6689
|
return;
|
|
6635
6690
|
}
|
|
6636
|
-
const speakerIdentity =
|
|
6691
|
+
const speakerIdentity = personOf(event.speakerIdentity);
|
|
6637
6692
|
const speaker = snapshot.participants.find(
|
|
6638
6693
|
(participant) => participant.identity === speakerIdentity
|
|
6639
6694
|
);
|
|
@@ -6707,7 +6762,7 @@ function createRoomEngine(options) {
|
|
|
6707
6762
|
);
|
|
6708
6763
|
return;
|
|
6709
6764
|
}
|
|
6710
|
-
const sender =
|
|
6765
|
+
const sender = personOf(from ?? "");
|
|
6711
6766
|
switch (topic) {
|
|
6712
6767
|
case DATA_TOPICS.hand:
|
|
6713
6768
|
store.setHand(
|
|
@@ -6749,7 +6804,7 @@ function createRoomEngine(options) {
|
|
|
6749
6804
|
}
|
|
6750
6805
|
};
|
|
6751
6806
|
async function resolveExitAndApply(hint) {
|
|
6752
|
-
const connection = current?.identity ?? store.snapshot().localIdentity ?? null;
|
|
6807
|
+
const connection = current?.connectionIdentity ?? current?.identity ?? store.snapshot().localIdentity ?? null;
|
|
6753
6808
|
lastRequest = requestAsNow();
|
|
6754
6809
|
await dropRoom();
|
|
6755
6810
|
let server;
|
|
@@ -6787,14 +6842,29 @@ function createRoomEngine(options) {
|
|
|
6787
6842
|
}
|
|
6788
6843
|
async function connect(request) {
|
|
6789
6844
|
store.patch({ failure: null, waiting: null, roomName: request.roomName, share: NO_SHARE, connection: "stable" });
|
|
6845
|
+
const tabId = options.tabClaim !== void 0 ? await options.tabClaim.tabId : null;
|
|
6790
6846
|
const token = await tokens.get({
|
|
6791
6847
|
roomName: request.roomName,
|
|
6792
6848
|
identity,
|
|
6793
6849
|
...request.meetingId !== void 0 ? { meetingId: request.meetingId } : {},
|
|
6794
6850
|
...request.slug !== void 0 ? { slug: request.slug } : {},
|
|
6795
6851
|
...request.breakoutRoomId != null ? { breakoutRoomId: request.breakoutRoomId } : {},
|
|
6796
|
-
...request.observe === true ? { observe: true } : {}
|
|
6852
|
+
...request.observe === true ? { observe: true } : {},
|
|
6853
|
+
...tabId !== null ? { tabId } : {},
|
|
6854
|
+
...request.secondDevice !== void 0 ? { secondDevice: request.secondDevice } : {}
|
|
6797
6855
|
});
|
|
6856
|
+
if (token.secondDeviceChoice != null) {
|
|
6857
|
+
store.patch({ secondDevice: { rule: token.secondDeviceChoice }, localIdentity: token.identity });
|
|
6858
|
+
store.dispatch({ type: "second_device" });
|
|
6859
|
+
report("info", `meet: already in this meeting elsewhere \u2014 offering the choice (${token.secondDeviceChoice})`);
|
|
6860
|
+
return;
|
|
6861
|
+
}
|
|
6862
|
+
if (store.snapshot().secondDevice !== null) store.patch({ secondDevice: null });
|
|
6863
|
+
if (request.secondDevice === "switch_here" && lastRequest !== null) {
|
|
6864
|
+
const { secondDevice: _answered, ...rest } = lastRequest;
|
|
6865
|
+
lastRequest = rest;
|
|
6866
|
+
}
|
|
6867
|
+
options.tabClaim?.inRoom(String(request.roomName), String(token.identity));
|
|
6798
6868
|
current = token;
|
|
6799
6869
|
lastServerUrl = token.serverUrl;
|
|
6800
6870
|
connectionPolicy = token.connectionPolicy ?? DEFAULT_CONNECTION_POLICY;
|
|
@@ -6859,7 +6929,7 @@ function createRoomEngine(options) {
|
|
|
6859
6929
|
if (request.mode === "video") {
|
|
6860
6930
|
await enableTrack(created, "camera", true);
|
|
6861
6931
|
}
|
|
6862
|
-
if (request.microphone !== false) {
|
|
6932
|
+
if (request.microphone !== false && current?.connectionKind !== "companion") {
|
|
6863
6933
|
await enableTrack(created, "microphone", true);
|
|
6864
6934
|
}
|
|
6865
6935
|
media.onConnected();
|
|
@@ -6881,7 +6951,8 @@ function createRoomEngine(options) {
|
|
|
6881
6951
|
}
|
|
6882
6952
|
async function publish(topic, body, to) {
|
|
6883
6953
|
if (driver === null) return;
|
|
6884
|
-
|
|
6954
|
+
const addresses = to === void 0 ? void 0 : to.flatMap((who) => connectionsOf(who));
|
|
6955
|
+
await driver.publishData(topic, JSON.stringify(body), addresses);
|
|
6885
6956
|
}
|
|
6886
6957
|
async function applyEffects(target, effects) {
|
|
6887
6958
|
try {
|
|
@@ -6919,8 +6990,19 @@ function createRoomEngine(options) {
|
|
|
6919
6990
|
liveRoster = list;
|
|
6920
6991
|
const merged = mergeGhosts(list, ghosts, at);
|
|
6921
6992
|
ghosts = merged.kept;
|
|
6993
|
+
const people = /* @__PURE__ */ new Map();
|
|
6994
|
+
const connections = /* @__PURE__ */ new Map();
|
|
6995
|
+
for (const entry of merged.roster) {
|
|
6996
|
+
const person = personOf(entry.identity);
|
|
6997
|
+
connections.set(person, [...connections.get(person) ?? [], entry.identity]);
|
|
6998
|
+
const rank = (entry.isLocal ? 8 : 0) + (entry.reconnecting === true ? 0 : 4) + (entry.attributes[CONNECTION_KIND_ATTRIBUTE] === "companion" ? 0 : 2) + (entry.cameraEnabled || entry.micEnabled ? 1 : 0);
|
|
6999
|
+
const held = people.get(person);
|
|
7000
|
+
if (held === void 0 || rank > held.rank) people.set(person, { entry, rank });
|
|
7001
|
+
}
|
|
7002
|
+
personConnections = connections;
|
|
7003
|
+
mainConnection = new Map([...people].map(([person, { entry }]) => [person, entry.identity]));
|
|
6922
7004
|
store.setParticipants(
|
|
6923
|
-
|
|
7005
|
+
[...people.values()].map(({ entry }) => {
|
|
6924
7006
|
const projected = projectParticipant(entry, joinedAt);
|
|
6925
7007
|
return entry.reconnecting === true ? { ...projected, reconnecting: true, quality: "lost" } : projected;
|
|
6926
7008
|
})
|
|
@@ -7097,7 +7179,13 @@ function createRoomEngine(options) {
|
|
|
7097
7179
|
},
|
|
7098
7180
|
async setScreenShareEnabled(enabled) {
|
|
7099
7181
|
if (driver === null) return;
|
|
7100
|
-
|
|
7182
|
+
let unsupported = false;
|
|
7183
|
+
try {
|
|
7184
|
+
unsupported = enabled && driver.supportsScreenShare?.() === false;
|
|
7185
|
+
} catch {
|
|
7186
|
+
unsupported = false;
|
|
7187
|
+
}
|
|
7188
|
+
if (unsupported) {
|
|
7101
7189
|
store.patch({ share: { state: "refused", reason: "unsupported", remedy: null, at: now() } });
|
|
7102
7190
|
report("warn", "meet: this browser cannot share a screen", "Share from a desktop browser (Chrome, Edge, Firefox or Safari).");
|
|
7103
7191
|
return;
|
|
@@ -7196,7 +7284,7 @@ function createRoomEngine(options) {
|
|
|
7196
7284
|
},
|
|
7197
7285
|
attach(who, kind, element) {
|
|
7198
7286
|
if (driver === null) return () => void 0;
|
|
7199
|
-
return driver.attach(who, kind, element);
|
|
7287
|
+
return driver.attach(mainConnection.get(personOf(who)) ?? who, kind, element);
|
|
7200
7288
|
},
|
|
7201
7289
|
token: () => current,
|
|
7202
7290
|
async dispose() {
|