@ai-matrx/meet 0.7.78 → 0.7.79
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 +21 -0
- package/dist/index.cjs +219 -4
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +120 -1
- package/dist/index.d.ts +120 -1
- package/dist/index.js +219 -4
- package/dist/index.js.map +1 -1
- package/dist/react.cjs +478 -203
- package/dist/react.cjs.map +1 -1
- package/dist/react.d.cts +4 -3
- package/dist/react.d.ts +4 -3
- package/dist/react.js +373 -98
- package/dist/react.js.map +1 -1
- package/package.json +1 -1
package/CHANGELOG.md
CHANGED
|
@@ -1,5 +1,26 @@
|
|
|
1
1
|
# Changelog — `@ai-matrx/meet`
|
|
2
2
|
|
|
3
|
+
## 0.7.79
|
|
4
|
+
|
|
5
|
+
**A meeting's record is read once per tab, and kept live.** `useMeetingRecord` held the bundle
|
|
6
|
+
in its own state and read all four parts (`meet_participants`, `meet_notes`,
|
|
7
|
+
`meet_transcript_segments`, `meet_recordings`) on every mount — so a board tile that slept
|
|
8
|
+
and woke (React `<Activity>` re-runs every effect), a Remove + Undo, and every second view of
|
|
9
|
+
the same meeting read the whole record again. The record now lives in one per-tab cache
|
|
10
|
+
(`core/record-cache.ts`, keyed by reader + meeting): the first view reads, later views and
|
|
11
|
+
remounts render the kept record and read nothing; a read in flight is joined; Retry and a
|
|
12
|
+
reconnect read again, and an older answer never paints over a newer one. A member's record is
|
|
13
|
+
kept current by ONE change-feed channel per meeting (notes, transcript, attendance,
|
|
14
|
+
recordings — applied row by row, no re-read), held while any view is mounted and 60 s after;
|
|
15
|
+
past that the record is marked stale and the next view reads it once. A meeting rename
|
|
16
|
+
re-assembles the kept record instead of re-reading it. New exports: `loadMeetingRecordParts`,
|
|
17
|
+
`assembleMeetingRecord`, `createMeetingRecordCache`, `MEETING_RECORD_GRACE_MS`, and
|
|
18
|
+
`meetingRecordCache()` from `/react`. Guard: `react/meeting-record-reads-once.test.tsx`
|
|
19
|
+
(4/4 red on 0.7.78).
|
|
20
|
+
|
|
21
|
+
Consumer action: none. A host test that renders the record for the same meeting twice and
|
|
22
|
+
expects two reads resets the cache between cases (`meetingRecordCache().reset()`).
|
|
23
|
+
|
|
3
24
|
## 0.7.78
|
|
4
25
|
|
|
5
26
|
**A meeting can run inside a box.** `.mx-meet` was `height: 100dvh`, so a stage mounted in a
|
package/dist/index.cjs
CHANGED
|
@@ -50,6 +50,7 @@ __export(src_exports, {
|
|
|
50
50
|
LISTEN_ONLY_NOTICE: () => LISTEN_ONLY_NOTICE,
|
|
51
51
|
MEETING_INVITE_KIND: () => MEETING_INVITE_KIND,
|
|
52
52
|
MEETING_INVITE_VERSION: () => MEETING_INVITE_VERSION,
|
|
53
|
+
MEETING_RECORD_GRACE_MS: () => MEETING_RECORD_GRACE_MS,
|
|
53
54
|
MEET_ANSWER_CONTEXT_EVENT: () => MEET_ANSWER_CONTEXT_EVENT,
|
|
54
55
|
MEET_ANSWER_EVENTS: () => MEET_ANSWER_EVENTS,
|
|
55
56
|
MEET_EVENTS: () => MEET_EVENTS,
|
|
@@ -84,6 +85,7 @@ __export(src_exports, {
|
|
|
84
85
|
asParticipantIdentity: () => asParticipantIdentity,
|
|
85
86
|
asRoomName: () => asRoomName,
|
|
86
87
|
asUserId: () => asUserId,
|
|
88
|
+
assembleMeetingRecord: () => assembleMeetingRecord,
|
|
87
89
|
assignedRoom: () => assignedRoom,
|
|
88
90
|
attendanceCsv: () => attendanceCsv,
|
|
89
91
|
attendanceReport: () => attendanceReport,
|
|
@@ -113,6 +115,7 @@ __export(src_exports, {
|
|
|
113
115
|
createMeetRepository: () => createMeetRepository,
|
|
114
116
|
createMeetStore: () => createMeetStore,
|
|
115
117
|
createMeetingInviteHandler: () => createMeetingInviteHandler,
|
|
118
|
+
createMeetingRecordCache: () => createMeetingRecordCache,
|
|
116
119
|
createMeetingSession: () => createMeetingSession,
|
|
117
120
|
createMemoryDeviceStorage: () => createMemoryDeviceStorage,
|
|
118
121
|
createRecordingController: () => createRecordingController,
|
|
@@ -147,6 +150,7 @@ __export(src_exports, {
|
|
|
147
150
|
loadMeetingActivities: () => loadMeetingActivities,
|
|
148
151
|
loadMeetingChat: () => loadMeetingChat,
|
|
149
152
|
loadMeetingRecord: () => loadMeetingRecord,
|
|
153
|
+
loadMeetingRecordParts: () => loadMeetingRecordParts,
|
|
150
154
|
mapConnectionState: () => mapConnectionState,
|
|
151
155
|
mapQuality: () => mapQuality,
|
|
152
156
|
meetingInviteAction: () => meetingInviteAction,
|
|
@@ -1033,13 +1037,13 @@ function mergeCaption(list, caption) {
|
|
|
1033
1037
|
return list.map((entry, position) => position === index ? caption : entry);
|
|
1034
1038
|
}
|
|
1035
1039
|
function mergeRecord(list, record) {
|
|
1036
|
-
const
|
|
1037
|
-
if (
|
|
1038
|
-
const existing = list[
|
|
1040
|
+
const byId2 = list.findIndex((entry) => entry.source === "record" && entry.id === record.id);
|
|
1041
|
+
if (byId2 !== -1) {
|
|
1042
|
+
const existing = list[byId2];
|
|
1039
1043
|
if (existing !== void 0 && existing.text === record.text && existing.final === record.final) {
|
|
1040
1044
|
return list;
|
|
1041
1045
|
}
|
|
1042
|
-
return list.map((entry, position) => position ===
|
|
1046
|
+
return list.map((entry, position) => position === byId2 ? record : entry);
|
|
1043
1047
|
}
|
|
1044
1048
|
const captionIndex = list.findIndex((entry) => supersedes(record, entry));
|
|
1045
1049
|
if (captionIndex === -1) return [...list, record];
|
|
@@ -5869,6 +5873,9 @@ function restrictionFor(part, error) {
|
|
|
5869
5873
|
};
|
|
5870
5874
|
}
|
|
5871
5875
|
async function loadMeetingRecord(repository, meetingId, meeting, reader = { userId: null }) {
|
|
5876
|
+
return assembleMeetingRecord(meeting, await loadMeetingRecordParts(repository, meetingId, reader));
|
|
5877
|
+
}
|
|
5878
|
+
async function loadMeetingRecordParts(repository, meetingId, reader = { userId: null }) {
|
|
5872
5879
|
const restrictions = [];
|
|
5873
5880
|
async function attempt(part, run, empty) {
|
|
5874
5881
|
if (reader.userId === null) {
|
|
@@ -5896,6 +5903,10 @@ async function loadMeetingRecord(repository, meetingId, meeting, reader = { user
|
|
|
5896
5903
|
[]
|
|
5897
5904
|
)
|
|
5898
5905
|
]);
|
|
5906
|
+
return { attendees, notes, transcript, recordings, restrictions };
|
|
5907
|
+
}
|
|
5908
|
+
function assembleMeetingRecord(meeting, parts) {
|
|
5909
|
+
const { attendees, notes, transcript, recordings, restrictions } = parts;
|
|
5899
5910
|
return {
|
|
5900
5911
|
meeting,
|
|
5901
5912
|
attendees,
|
|
@@ -6240,4 +6251,208 @@ async function setGuestRecordAccess(args) {
|
|
|
6240
6251
|
};
|
|
6241
6252
|
}
|
|
6242
6253
|
var GUEST_RECORD_NOT_SHARED = NOT_SHARED;
|
|
6254
|
+
|
|
6255
|
+
// src/core/record-cache.ts
|
|
6256
|
+
var MEETING_RECORD_GRACE_MS = 6e4;
|
|
6257
|
+
var INITIAL = { loading: true, record: null, failure: null };
|
|
6258
|
+
function upsert2(rows, row, removedId, order) {
|
|
6259
|
+
const id = row?.id ?? removedId;
|
|
6260
|
+
if (id === void 0) return rows;
|
|
6261
|
+
const rest = rows.filter((existing) => existing.id !== id);
|
|
6262
|
+
return row === null ? rest : [...rest, row].sort(order);
|
|
6263
|
+
}
|
|
6264
|
+
var byId = (a, b) => a.id < b.id ? -1 : a.id > b.id ? 1 : 0;
|
|
6265
|
+
var ascending = (a, b) => a === b ? 0 : a === null ? 1 : b === null ? -1 : a < b ? -1 : 1;
|
|
6266
|
+
function createMeetingRecordCache(options = {}) {
|
|
6267
|
+
const graceMs = options.graceMs ?? MEETING_RECORD_GRACE_MS;
|
|
6268
|
+
const entries = /* @__PURE__ */ new Map();
|
|
6269
|
+
const listeners = /* @__PURE__ */ new Set();
|
|
6270
|
+
const notify = () => {
|
|
6271
|
+
for (const listener of listeners) listener();
|
|
6272
|
+
};
|
|
6273
|
+
const entryOf = (key) => {
|
|
6274
|
+
let entry = entries.get(key);
|
|
6275
|
+
if (!entry) {
|
|
6276
|
+
entry = {
|
|
6277
|
+
state: INITIAL,
|
|
6278
|
+
meeting: null,
|
|
6279
|
+
parts: null,
|
|
6280
|
+
fresh: false,
|
|
6281
|
+
seq: 0,
|
|
6282
|
+
inflight: null,
|
|
6283
|
+
holders: 0,
|
|
6284
|
+
close: null,
|
|
6285
|
+
owner: null,
|
|
6286
|
+
grace: null
|
|
6287
|
+
};
|
|
6288
|
+
entries.set(key, entry);
|
|
6289
|
+
}
|
|
6290
|
+
return entry;
|
|
6291
|
+
};
|
|
6292
|
+
const assemble = (entry) => {
|
|
6293
|
+
if (entry.meeting === null || entry.parts === null) return;
|
|
6294
|
+
entry.state = { loading: false, failure: null, record: assembleMeetingRecord(entry.meeting, entry.parts) };
|
|
6295
|
+
notify();
|
|
6296
|
+
};
|
|
6297
|
+
return {
|
|
6298
|
+
state: (key) => key === null ? INITIAL : entries.get(key)?.state ?? INITIAL,
|
|
6299
|
+
subscribe(listener) {
|
|
6300
|
+
listeners.add(listener);
|
|
6301
|
+
return () => {
|
|
6302
|
+
listeners.delete(listener);
|
|
6303
|
+
};
|
|
6304
|
+
},
|
|
6305
|
+
ensure(key, read, { force = false } = {}) {
|
|
6306
|
+
const entry = entryOf(key);
|
|
6307
|
+
if (!force) {
|
|
6308
|
+
if (entry.inflight !== null) return entry.inflight;
|
|
6309
|
+
if (entry.fresh) return Promise.resolve();
|
|
6310
|
+
}
|
|
6311
|
+
const seq = ++entry.seq;
|
|
6312
|
+
if (entry.state.failure !== null) {
|
|
6313
|
+
entry.state = { ...entry.state, loading: true, failure: null };
|
|
6314
|
+
notify();
|
|
6315
|
+
}
|
|
6316
|
+
const request = read().then(
|
|
6317
|
+
(answer) => {
|
|
6318
|
+
if (seq !== entry.seq) return;
|
|
6319
|
+
entry.fresh = true;
|
|
6320
|
+
if (answer.kind === "parts") {
|
|
6321
|
+
entry.meeting = answer.meeting;
|
|
6322
|
+
entry.parts = answer.parts;
|
|
6323
|
+
assemble(entry);
|
|
6324
|
+
} else {
|
|
6325
|
+
entry.meeting = answer.bundle.meeting;
|
|
6326
|
+
entry.parts = null;
|
|
6327
|
+
entry.state = { loading: false, failure: null, record: answer.bundle };
|
|
6328
|
+
notify();
|
|
6329
|
+
}
|
|
6330
|
+
},
|
|
6331
|
+
(thrown) => {
|
|
6332
|
+
if (seq !== entry.seq) return;
|
|
6333
|
+
entry.fresh = false;
|
|
6334
|
+
entry.state = {
|
|
6335
|
+
loading: false,
|
|
6336
|
+
record: entry.state.record,
|
|
6337
|
+
failure: {
|
|
6338
|
+
message: thrown?.message ?? String(thrown),
|
|
6339
|
+
remedy: thrown?.remedy ?? "The meeting record could not be read. Reload the page; nothing was changed."
|
|
6340
|
+
}
|
|
6341
|
+
};
|
|
6342
|
+
notify();
|
|
6343
|
+
}
|
|
6344
|
+
);
|
|
6345
|
+
const tracked = request.finally(() => {
|
|
6346
|
+
if (entry.inflight === tracked) entry.inflight = null;
|
|
6347
|
+
});
|
|
6348
|
+
entry.inflight = tracked;
|
|
6349
|
+
return tracked;
|
|
6350
|
+
},
|
|
6351
|
+
setMeeting(key, meeting) {
|
|
6352
|
+
const entry = entries.get(key);
|
|
6353
|
+
if (!entry || entry.meeting === meeting) return;
|
|
6354
|
+
if (entry.parts !== null) {
|
|
6355
|
+
entry.meeting = meeting;
|
|
6356
|
+
assemble(entry);
|
|
6357
|
+
} else if (entry.state.record !== null) {
|
|
6358
|
+
entry.meeting = meeting;
|
|
6359
|
+
entry.state = { ...entry.state, record: { ...entry.state.record, meeting } };
|
|
6360
|
+
notify();
|
|
6361
|
+
}
|
|
6362
|
+
},
|
|
6363
|
+
apply(key, change) {
|
|
6364
|
+
const entry = entries.get(key);
|
|
6365
|
+
const parts = entry?.parts;
|
|
6366
|
+
if (!entry || !parts) return;
|
|
6367
|
+
const refused = parts.restrictions.some((r) => r.part === change.part);
|
|
6368
|
+
if (refused) return;
|
|
6369
|
+
switch (change.part) {
|
|
6370
|
+
case "transcript":
|
|
6371
|
+
entry.parts = {
|
|
6372
|
+
...parts,
|
|
6373
|
+
transcript: upsert2(
|
|
6374
|
+
parts.transcript,
|
|
6375
|
+
change.row,
|
|
6376
|
+
change.removedId,
|
|
6377
|
+
(a, b) => ascending(a.startedAt, b.startedAt) || byId(a, b)
|
|
6378
|
+
)
|
|
6379
|
+
};
|
|
6380
|
+
break;
|
|
6381
|
+
case "notes":
|
|
6382
|
+
entry.parts = {
|
|
6383
|
+
...parts,
|
|
6384
|
+
notes: upsert2(
|
|
6385
|
+
parts.notes,
|
|
6386
|
+
change.row,
|
|
6387
|
+
change.removedId,
|
|
6388
|
+
(a, b) => ascending(a.createdAt, b.createdAt) || byId(a, b)
|
|
6389
|
+
)
|
|
6390
|
+
};
|
|
6391
|
+
break;
|
|
6392
|
+
case "attendance":
|
|
6393
|
+
entry.parts = {
|
|
6394
|
+
...parts,
|
|
6395
|
+
attendees: upsert2(
|
|
6396
|
+
parts.attendees,
|
|
6397
|
+
change.row,
|
|
6398
|
+
change.removedId,
|
|
6399
|
+
(a, b) => ascending(a.joinedAt, b.joinedAt) || byId(a, b)
|
|
6400
|
+
)
|
|
6401
|
+
};
|
|
6402
|
+
break;
|
|
6403
|
+
case "recording":
|
|
6404
|
+
entry.parts = {
|
|
6405
|
+
...parts,
|
|
6406
|
+
// Newest first, as the read orders them.
|
|
6407
|
+
recordings: upsert2(
|
|
6408
|
+
parts.recordings,
|
|
6409
|
+
change.row,
|
|
6410
|
+
change.removedId,
|
|
6411
|
+
(a, b) => ascending(b.startedAt, a.startedAt) || byId(a, b)
|
|
6412
|
+
)
|
|
6413
|
+
};
|
|
6414
|
+
break;
|
|
6415
|
+
}
|
|
6416
|
+
assemble(entry);
|
|
6417
|
+
},
|
|
6418
|
+
hold(key, open, owner = null) {
|
|
6419
|
+
const entry = entryOf(key);
|
|
6420
|
+
if (entry.grace !== null) {
|
|
6421
|
+
clearTimeout(entry.grace);
|
|
6422
|
+
entry.grace = null;
|
|
6423
|
+
}
|
|
6424
|
+
if (entry.close !== null && entry.owner !== owner) {
|
|
6425
|
+
entry.close();
|
|
6426
|
+
entry.close = null;
|
|
6427
|
+
}
|
|
6428
|
+
if (entry.close === null) {
|
|
6429
|
+
entry.close = open();
|
|
6430
|
+
entry.owner = owner;
|
|
6431
|
+
}
|
|
6432
|
+
entry.holders += 1;
|
|
6433
|
+
let released = false;
|
|
6434
|
+
return () => {
|
|
6435
|
+
if (released) return;
|
|
6436
|
+
released = true;
|
|
6437
|
+
entry.holders -= 1;
|
|
6438
|
+
if (entry.holders > 0) return;
|
|
6439
|
+
entry.grace = setTimeout(() => {
|
|
6440
|
+
entry.grace = null;
|
|
6441
|
+
if (entry.holders > 0) return;
|
|
6442
|
+
entry.close?.();
|
|
6443
|
+
entry.close = null;
|
|
6444
|
+
entry.fresh = false;
|
|
6445
|
+
}, graceMs);
|
|
6446
|
+
};
|
|
6447
|
+
},
|
|
6448
|
+
reset() {
|
|
6449
|
+
for (const entry of entries.values()) {
|
|
6450
|
+
if (entry.grace !== null) clearTimeout(entry.grace);
|
|
6451
|
+
entry.close?.();
|
|
6452
|
+
}
|
|
6453
|
+
entries.clear();
|
|
6454
|
+
notify();
|
|
6455
|
+
}
|
|
6456
|
+
};
|
|
6457
|
+
}
|
|
6243
6458
|
//# sourceMappingURL=index.cjs.map
|