@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/dist/react.js
CHANGED
|
@@ -1901,13 +1901,13 @@ function mergeCaption(list, caption) {
|
|
|
1901
1901
|
return list.map((entry, position) => position === index ? caption : entry);
|
|
1902
1902
|
}
|
|
1903
1903
|
function mergeRecord(list, record) {
|
|
1904
|
-
const
|
|
1905
|
-
if (
|
|
1906
|
-
const existing = list[
|
|
1904
|
+
const byId2 = list.findIndex((entry) => entry.source === "record" && entry.id === record.id);
|
|
1905
|
+
if (byId2 !== -1) {
|
|
1906
|
+
const existing = list[byId2];
|
|
1907
1907
|
if (existing !== void 0 && existing.text === record.text && existing.final === record.final) {
|
|
1908
1908
|
return list;
|
|
1909
1909
|
}
|
|
1910
|
-
return list.map((entry, position) => position ===
|
|
1910
|
+
return list.map((entry, position) => position === byId2 ? record : entry);
|
|
1911
1911
|
}
|
|
1912
1912
|
const captionIndex = list.findIndex((entry) => supersedes(record, entry));
|
|
1913
1913
|
if (captionIndex === -1) return [...list, record];
|
|
@@ -5481,50 +5481,8 @@ function useLocalUserId() {
|
|
|
5481
5481
|
}
|
|
5482
5482
|
|
|
5483
5483
|
// src/react/hooks.ts
|
|
5484
|
-
import { useCallback as useCallback2, useEffect as useEffect2, useMemo as useMemo2, useRef as useRef2, useState as useState2 } from "react";
|
|
5485
|
-
|
|
5486
|
-
// src/core/layout.ts
|
|
5487
|
-
var DEFAULT_PAGE_SIZE = 12;
|
|
5488
|
-
function orderParticipants(participants, activeSpeakers) {
|
|
5489
|
-
const speakerRank = /* @__PURE__ */ new Map();
|
|
5490
|
-
activeSpeakers.forEach((identity, index) => speakerRank.set(identity, index));
|
|
5491
|
-
return [...participants].sort((a, b) => {
|
|
5492
|
-
const speakerA = speakerRank.get(a.identity) ?? Number.MAX_SAFE_INTEGER;
|
|
5493
|
-
const speakerB = speakerRank.get(b.identity) ?? Number.MAX_SAFE_INTEGER;
|
|
5494
|
-
if (speakerA !== speakerB) return speakerA - speakerB;
|
|
5495
|
-
if (a.handRaised !== b.handRaised) return a.handRaised ? -1 : 1;
|
|
5496
|
-
if (a.cameraEnabled !== b.cameraEnabled) return a.cameraEnabled ? -1 : 1;
|
|
5497
|
-
if (a.joinedAt !== b.joinedAt) return a.joinedAt < b.joinedAt ? -1 : 1;
|
|
5498
|
-
return a.identity < b.identity ? -1 : 1;
|
|
5499
|
-
});
|
|
5500
|
-
}
|
|
5501
|
-
function computeStage(args) {
|
|
5502
|
-
const pageSize = Math.max(1, args.pageSize ?? DEFAULT_PAGE_SIZE);
|
|
5503
|
-
const ordered = orderParticipants(args.participants, args.activeSpeakers);
|
|
5504
|
-
const sharer = ordered.find((participant) => participant.screenSharing) ?? null;
|
|
5505
|
-
const pinnedParticipant = args.pinned === null ? null : ordered.find((participant) => participant.identity === args.pinned) ?? null;
|
|
5506
|
-
const spotlit = args.spotlight == null ? null : ordered.find((participant) => participant.identity === args.spotlight) ?? null;
|
|
5507
|
-
const feature = sharer !== null ? sharer : pinnedParticipant !== null && args.layout !== "grid" ? pinnedParticipant : spotlit !== null ? spotlit : args.layout === "grid" ? null : ordered[0] ?? null;
|
|
5508
|
-
const rest = feature === null ? ordered : ordered.filter((participant) => participant.identity !== feature.identity);
|
|
5509
|
-
const pageCount = Math.max(1, Math.ceil(rest.length / pageSize));
|
|
5510
|
-
const page = Math.min(Math.max(0, args.page ?? 0), pageCount - 1);
|
|
5511
|
-
const tiles = rest.slice(page * pageSize, page * pageSize + pageSize);
|
|
5512
|
-
return {
|
|
5513
|
-
feature,
|
|
5514
|
-
tiles,
|
|
5515
|
-
overflow: rest.length - tiles.length,
|
|
5516
|
-
page,
|
|
5517
|
-
pageCount,
|
|
5518
|
-
presenting: sharer !== null
|
|
5519
|
-
};
|
|
5520
|
-
}
|
|
5521
|
-
function gridColumns(count2) {
|
|
5522
|
-
if (count2 <= 1) return 1;
|
|
5523
|
-
if (count2 <= 4) return 2;
|
|
5524
|
-
if (count2 <= 9) return 3;
|
|
5525
|
-
if (count2 <= 16) return 4;
|
|
5526
|
-
return 5;
|
|
5527
|
-
}
|
|
5484
|
+
import { useCallback as useCallback2, useEffect as useEffect2, useMemo as useMemo2, useRef as useRef2, useState as useState2, useSyncExternalStore as useSyncExternalStore2 } from "react";
|
|
5485
|
+
import { useRealtimeManager as useRealtimeManager2 } from "@ai-matrx/realtime/react";
|
|
5528
5486
|
|
|
5529
5487
|
// src/core/record.ts
|
|
5530
5488
|
import { formatDurationMs } from "@ai-matrx/kit/format";
|
|
@@ -5548,6 +5506,9 @@ function restrictionFor(part, error) {
|
|
|
5548
5506
|
};
|
|
5549
5507
|
}
|
|
5550
5508
|
async function loadMeetingRecord(repository, meetingId, meeting, reader = { userId: null }) {
|
|
5509
|
+
return assembleMeetingRecord(meeting, await loadMeetingRecordParts(repository, meetingId, reader));
|
|
5510
|
+
}
|
|
5511
|
+
async function loadMeetingRecordParts(repository, meetingId, reader = { userId: null }) {
|
|
5551
5512
|
const restrictions = [];
|
|
5552
5513
|
async function attempt(part, run, empty) {
|
|
5553
5514
|
if (reader.userId === null) {
|
|
@@ -5575,6 +5536,10 @@ async function loadMeetingRecord(repository, meetingId, meeting, reader = { user
|
|
|
5575
5536
|
[]
|
|
5576
5537
|
)
|
|
5577
5538
|
]);
|
|
5539
|
+
return { attendees, notes, transcript, recordings, restrictions };
|
|
5540
|
+
}
|
|
5541
|
+
function assembleMeetingRecord(meeting, parts) {
|
|
5542
|
+
const { attendees, notes, transcript, recordings, restrictions } = parts;
|
|
5578
5543
|
return {
|
|
5579
5544
|
meeting,
|
|
5580
5545
|
attendees,
|
|
@@ -5920,6 +5885,253 @@ async function setGuestRecordAccess(args) {
|
|
|
5920
5885
|
}
|
|
5921
5886
|
var GUEST_RECORD_NOT_SHARED = NOT_SHARED;
|
|
5922
5887
|
|
|
5888
|
+
// src/core/record-cache.ts
|
|
5889
|
+
var MEETING_RECORD_GRACE_MS = 6e4;
|
|
5890
|
+
var INITIAL = { loading: true, record: null, failure: null };
|
|
5891
|
+
function upsert2(rows, row, removedId, order) {
|
|
5892
|
+
const id = row?.id ?? removedId;
|
|
5893
|
+
if (id === void 0) return rows;
|
|
5894
|
+
const rest = rows.filter((existing) => existing.id !== id);
|
|
5895
|
+
return row === null ? rest : [...rest, row].sort(order);
|
|
5896
|
+
}
|
|
5897
|
+
var byId = (a, b) => a.id < b.id ? -1 : a.id > b.id ? 1 : 0;
|
|
5898
|
+
var ascending = (a, b) => a === b ? 0 : a === null ? 1 : b === null ? -1 : a < b ? -1 : 1;
|
|
5899
|
+
function createMeetingRecordCache(options = {}) {
|
|
5900
|
+
const graceMs = options.graceMs ?? MEETING_RECORD_GRACE_MS;
|
|
5901
|
+
const entries = /* @__PURE__ */ new Map();
|
|
5902
|
+
const listeners = /* @__PURE__ */ new Set();
|
|
5903
|
+
const notify = () => {
|
|
5904
|
+
for (const listener of listeners) listener();
|
|
5905
|
+
};
|
|
5906
|
+
const entryOf = (key) => {
|
|
5907
|
+
let entry = entries.get(key);
|
|
5908
|
+
if (!entry) {
|
|
5909
|
+
entry = {
|
|
5910
|
+
state: INITIAL,
|
|
5911
|
+
meeting: null,
|
|
5912
|
+
parts: null,
|
|
5913
|
+
fresh: false,
|
|
5914
|
+
seq: 0,
|
|
5915
|
+
inflight: null,
|
|
5916
|
+
holders: 0,
|
|
5917
|
+
close: null,
|
|
5918
|
+
owner: null,
|
|
5919
|
+
grace: null
|
|
5920
|
+
};
|
|
5921
|
+
entries.set(key, entry);
|
|
5922
|
+
}
|
|
5923
|
+
return entry;
|
|
5924
|
+
};
|
|
5925
|
+
const assemble = (entry) => {
|
|
5926
|
+
if (entry.meeting === null || entry.parts === null) return;
|
|
5927
|
+
entry.state = { loading: false, failure: null, record: assembleMeetingRecord(entry.meeting, entry.parts) };
|
|
5928
|
+
notify();
|
|
5929
|
+
};
|
|
5930
|
+
return {
|
|
5931
|
+
state: (key) => key === null ? INITIAL : entries.get(key)?.state ?? INITIAL,
|
|
5932
|
+
subscribe(listener) {
|
|
5933
|
+
listeners.add(listener);
|
|
5934
|
+
return () => {
|
|
5935
|
+
listeners.delete(listener);
|
|
5936
|
+
};
|
|
5937
|
+
},
|
|
5938
|
+
ensure(key, read, { force = false } = {}) {
|
|
5939
|
+
const entry = entryOf(key);
|
|
5940
|
+
if (!force) {
|
|
5941
|
+
if (entry.inflight !== null) return entry.inflight;
|
|
5942
|
+
if (entry.fresh) return Promise.resolve();
|
|
5943
|
+
}
|
|
5944
|
+
const seq = ++entry.seq;
|
|
5945
|
+
if (entry.state.failure !== null) {
|
|
5946
|
+
entry.state = { ...entry.state, loading: true, failure: null };
|
|
5947
|
+
notify();
|
|
5948
|
+
}
|
|
5949
|
+
const request = read().then(
|
|
5950
|
+
(answer) => {
|
|
5951
|
+
if (seq !== entry.seq) return;
|
|
5952
|
+
entry.fresh = true;
|
|
5953
|
+
if (answer.kind === "parts") {
|
|
5954
|
+
entry.meeting = answer.meeting;
|
|
5955
|
+
entry.parts = answer.parts;
|
|
5956
|
+
assemble(entry);
|
|
5957
|
+
} else {
|
|
5958
|
+
entry.meeting = answer.bundle.meeting;
|
|
5959
|
+
entry.parts = null;
|
|
5960
|
+
entry.state = { loading: false, failure: null, record: answer.bundle };
|
|
5961
|
+
notify();
|
|
5962
|
+
}
|
|
5963
|
+
},
|
|
5964
|
+
(thrown) => {
|
|
5965
|
+
if (seq !== entry.seq) return;
|
|
5966
|
+
entry.fresh = false;
|
|
5967
|
+
entry.state = {
|
|
5968
|
+
loading: false,
|
|
5969
|
+
record: entry.state.record,
|
|
5970
|
+
failure: {
|
|
5971
|
+
message: thrown?.message ?? String(thrown),
|
|
5972
|
+
remedy: thrown?.remedy ?? "The meeting record could not be read. Reload the page; nothing was changed."
|
|
5973
|
+
}
|
|
5974
|
+
};
|
|
5975
|
+
notify();
|
|
5976
|
+
}
|
|
5977
|
+
);
|
|
5978
|
+
const tracked = request.finally(() => {
|
|
5979
|
+
if (entry.inflight === tracked) entry.inflight = null;
|
|
5980
|
+
});
|
|
5981
|
+
entry.inflight = tracked;
|
|
5982
|
+
return tracked;
|
|
5983
|
+
},
|
|
5984
|
+
setMeeting(key, meeting) {
|
|
5985
|
+
const entry = entries.get(key);
|
|
5986
|
+
if (!entry || entry.meeting === meeting) return;
|
|
5987
|
+
if (entry.parts !== null) {
|
|
5988
|
+
entry.meeting = meeting;
|
|
5989
|
+
assemble(entry);
|
|
5990
|
+
} else if (entry.state.record !== null) {
|
|
5991
|
+
entry.meeting = meeting;
|
|
5992
|
+
entry.state = { ...entry.state, record: { ...entry.state.record, meeting } };
|
|
5993
|
+
notify();
|
|
5994
|
+
}
|
|
5995
|
+
},
|
|
5996
|
+
apply(key, change) {
|
|
5997
|
+
const entry = entries.get(key);
|
|
5998
|
+
const parts = entry?.parts;
|
|
5999
|
+
if (!entry || !parts) return;
|
|
6000
|
+
const refused = parts.restrictions.some((r) => r.part === change.part);
|
|
6001
|
+
if (refused) return;
|
|
6002
|
+
switch (change.part) {
|
|
6003
|
+
case "transcript":
|
|
6004
|
+
entry.parts = {
|
|
6005
|
+
...parts,
|
|
6006
|
+
transcript: upsert2(
|
|
6007
|
+
parts.transcript,
|
|
6008
|
+
change.row,
|
|
6009
|
+
change.removedId,
|
|
6010
|
+
(a, b) => ascending(a.startedAt, b.startedAt) || byId(a, b)
|
|
6011
|
+
)
|
|
6012
|
+
};
|
|
6013
|
+
break;
|
|
6014
|
+
case "notes":
|
|
6015
|
+
entry.parts = {
|
|
6016
|
+
...parts,
|
|
6017
|
+
notes: upsert2(
|
|
6018
|
+
parts.notes,
|
|
6019
|
+
change.row,
|
|
6020
|
+
change.removedId,
|
|
6021
|
+
(a, b) => ascending(a.createdAt, b.createdAt) || byId(a, b)
|
|
6022
|
+
)
|
|
6023
|
+
};
|
|
6024
|
+
break;
|
|
6025
|
+
case "attendance":
|
|
6026
|
+
entry.parts = {
|
|
6027
|
+
...parts,
|
|
6028
|
+
attendees: upsert2(
|
|
6029
|
+
parts.attendees,
|
|
6030
|
+
change.row,
|
|
6031
|
+
change.removedId,
|
|
6032
|
+
(a, b) => ascending(a.joinedAt, b.joinedAt) || byId(a, b)
|
|
6033
|
+
)
|
|
6034
|
+
};
|
|
6035
|
+
break;
|
|
6036
|
+
case "recording":
|
|
6037
|
+
entry.parts = {
|
|
6038
|
+
...parts,
|
|
6039
|
+
// Newest first, as the read orders them.
|
|
6040
|
+
recordings: upsert2(
|
|
6041
|
+
parts.recordings,
|
|
6042
|
+
change.row,
|
|
6043
|
+
change.removedId,
|
|
6044
|
+
(a, b) => ascending(b.startedAt, a.startedAt) || byId(a, b)
|
|
6045
|
+
)
|
|
6046
|
+
};
|
|
6047
|
+
break;
|
|
6048
|
+
}
|
|
6049
|
+
assemble(entry);
|
|
6050
|
+
},
|
|
6051
|
+
hold(key, open, owner = null) {
|
|
6052
|
+
const entry = entryOf(key);
|
|
6053
|
+
if (entry.grace !== null) {
|
|
6054
|
+
clearTimeout(entry.grace);
|
|
6055
|
+
entry.grace = null;
|
|
6056
|
+
}
|
|
6057
|
+
if (entry.close !== null && entry.owner !== owner) {
|
|
6058
|
+
entry.close();
|
|
6059
|
+
entry.close = null;
|
|
6060
|
+
}
|
|
6061
|
+
if (entry.close === null) {
|
|
6062
|
+
entry.close = open();
|
|
6063
|
+
entry.owner = owner;
|
|
6064
|
+
}
|
|
6065
|
+
entry.holders += 1;
|
|
6066
|
+
let released = false;
|
|
6067
|
+
return () => {
|
|
6068
|
+
if (released) return;
|
|
6069
|
+
released = true;
|
|
6070
|
+
entry.holders -= 1;
|
|
6071
|
+
if (entry.holders > 0) return;
|
|
6072
|
+
entry.grace = setTimeout(() => {
|
|
6073
|
+
entry.grace = null;
|
|
6074
|
+
if (entry.holders > 0) return;
|
|
6075
|
+
entry.close?.();
|
|
6076
|
+
entry.close = null;
|
|
6077
|
+
entry.fresh = false;
|
|
6078
|
+
}, graceMs);
|
|
6079
|
+
};
|
|
6080
|
+
},
|
|
6081
|
+
reset() {
|
|
6082
|
+
for (const entry of entries.values()) {
|
|
6083
|
+
if (entry.grace !== null) clearTimeout(entry.grace);
|
|
6084
|
+
entry.close?.();
|
|
6085
|
+
}
|
|
6086
|
+
entries.clear();
|
|
6087
|
+
notify();
|
|
6088
|
+
}
|
|
6089
|
+
};
|
|
6090
|
+
}
|
|
6091
|
+
|
|
6092
|
+
// src/core/layout.ts
|
|
6093
|
+
var DEFAULT_PAGE_SIZE = 12;
|
|
6094
|
+
function orderParticipants(participants, activeSpeakers) {
|
|
6095
|
+
const speakerRank = /* @__PURE__ */ new Map();
|
|
6096
|
+
activeSpeakers.forEach((identity, index) => speakerRank.set(identity, index));
|
|
6097
|
+
return [...participants].sort((a, b) => {
|
|
6098
|
+
const speakerA = speakerRank.get(a.identity) ?? Number.MAX_SAFE_INTEGER;
|
|
6099
|
+
const speakerB = speakerRank.get(b.identity) ?? Number.MAX_SAFE_INTEGER;
|
|
6100
|
+
if (speakerA !== speakerB) return speakerA - speakerB;
|
|
6101
|
+
if (a.handRaised !== b.handRaised) return a.handRaised ? -1 : 1;
|
|
6102
|
+
if (a.cameraEnabled !== b.cameraEnabled) return a.cameraEnabled ? -1 : 1;
|
|
6103
|
+
if (a.joinedAt !== b.joinedAt) return a.joinedAt < b.joinedAt ? -1 : 1;
|
|
6104
|
+
return a.identity < b.identity ? -1 : 1;
|
|
6105
|
+
});
|
|
6106
|
+
}
|
|
6107
|
+
function computeStage(args) {
|
|
6108
|
+
const pageSize = Math.max(1, args.pageSize ?? DEFAULT_PAGE_SIZE);
|
|
6109
|
+
const ordered = orderParticipants(args.participants, args.activeSpeakers);
|
|
6110
|
+
const sharer = ordered.find((participant) => participant.screenSharing) ?? null;
|
|
6111
|
+
const pinnedParticipant = args.pinned === null ? null : ordered.find((participant) => participant.identity === args.pinned) ?? null;
|
|
6112
|
+
const spotlit = args.spotlight == null ? null : ordered.find((participant) => participant.identity === args.spotlight) ?? null;
|
|
6113
|
+
const feature = sharer !== null ? sharer : pinnedParticipant !== null && args.layout !== "grid" ? pinnedParticipant : spotlit !== null ? spotlit : args.layout === "grid" ? null : ordered[0] ?? null;
|
|
6114
|
+
const rest = feature === null ? ordered : ordered.filter((participant) => participant.identity !== feature.identity);
|
|
6115
|
+
const pageCount = Math.max(1, Math.ceil(rest.length / pageSize));
|
|
6116
|
+
const page = Math.min(Math.max(0, args.page ?? 0), pageCount - 1);
|
|
6117
|
+
const tiles = rest.slice(page * pageSize, page * pageSize + pageSize);
|
|
6118
|
+
return {
|
|
6119
|
+
feature,
|
|
6120
|
+
tiles,
|
|
6121
|
+
overflow: rest.length - tiles.length,
|
|
6122
|
+
page,
|
|
6123
|
+
pageCount,
|
|
6124
|
+
presenting: sharer !== null
|
|
6125
|
+
};
|
|
6126
|
+
}
|
|
6127
|
+
function gridColumns(count2) {
|
|
6128
|
+
if (count2 <= 1) return 1;
|
|
6129
|
+
if (count2 <= 4) return 2;
|
|
6130
|
+
if (count2 <= 9) return 3;
|
|
6131
|
+
if (count2 <= 16) return 4;
|
|
6132
|
+
return 5;
|
|
6133
|
+
}
|
|
6134
|
+
|
|
5923
6135
|
// src/react/hooks.ts
|
|
5924
6136
|
var EMPTY_NAMES = {};
|
|
5925
6137
|
var NO_RUNTIME_REMEDY = "Render this inside a <MeetProvider> that has a Supabase client, an organization id and an identity (userId or guestName). The provider stays inert until all three are real, and while it is inert this package's components render nothing rather than a dead control.";
|
|
@@ -6505,16 +6717,22 @@ function useMeeting() {
|
|
|
6505
6717
|
function useMeetingId() {
|
|
6506
6718
|
return useMeetSnapshot()?.meeting?.id ?? null;
|
|
6507
6719
|
}
|
|
6720
|
+
var RECORD_CACHE_KEY = /* @__PURE__ */ Symbol.for("@ai-matrx/meet/meeting-record-cache");
|
|
6721
|
+
function meetingRecordCache() {
|
|
6722
|
+
const holder = globalThis;
|
|
6723
|
+
let cache = holder[RECORD_CACHE_KEY];
|
|
6724
|
+
if (cache === void 0) {
|
|
6725
|
+
cache = createMeetingRecordCache();
|
|
6726
|
+
holder[RECORD_CACHE_KEY] = cache;
|
|
6727
|
+
}
|
|
6728
|
+
return cache;
|
|
6729
|
+
}
|
|
6730
|
+
var rowIdOf = (row) => typeof row["id"] === "string" ? row["id"] : void 0;
|
|
6508
6731
|
function useMeetingRecord(meeting) {
|
|
6509
6732
|
const host = useMeetHost();
|
|
6510
6733
|
const repository = host?.repository ?? null;
|
|
6511
6734
|
const ai = host?.ai ?? null;
|
|
6512
6735
|
const readerId = host?.identity.userId ?? null;
|
|
6513
|
-
const [recordReaderId, setRecordReaderId] = useState2(readerId);
|
|
6514
|
-
const [record, setRecord] = useState2(null);
|
|
6515
|
-
const [loading, setLoading] = useState2(true);
|
|
6516
|
-
const [failure, setFailure] = useState2(null);
|
|
6517
|
-
const [nonce, setNonce] = useState2(0);
|
|
6518
6736
|
const [asking, setAsking] = useState2(false);
|
|
6519
6737
|
const [answer, setAnswer] = useState2(null);
|
|
6520
6738
|
const [coverage, setCoverage] = useState2(null);
|
|
@@ -6523,57 +6741,107 @@ function useMeetingRecord(meeting) {
|
|
|
6523
6741
|
const api = host?.api ?? null;
|
|
6524
6742
|
const tokens = host?.tokens ?? null;
|
|
6525
6743
|
const roomName = meeting?.roomName ?? null;
|
|
6526
|
-
|
|
6527
|
-
|
|
6528
|
-
|
|
6529
|
-
|
|
6530
|
-
|
|
6531
|
-
|
|
6532
|
-
|
|
6533
|
-
|
|
6534
|
-
|
|
6535
|
-
|
|
6536
|
-
|
|
6537
|
-
|
|
6538
|
-
|
|
6539
|
-
|
|
6540
|
-
|
|
6541
|
-
// answers members and invitees; a person from another organization
|
|
6542
|
-
// the host admitted — or a guest who later made an account — reads
|
|
6543
|
-
// the guest share through the server, with their session as the pass.
|
|
6544
|
-
api !== null && direct.restrictions.some((part) => part.part === "notes") ? loadGuestMeetingRecord({
|
|
6744
|
+
const manager = useRealtimeManager2();
|
|
6745
|
+
const cache = meetingRecordCache();
|
|
6746
|
+
const key = repository !== null && meetingId !== null ? `${readerId ?? `guest:${roomName ?? "-"}`}|${meetingId}` : null;
|
|
6747
|
+
const state = useSyncExternalStore2(
|
|
6748
|
+
cache.subscribe,
|
|
6749
|
+
() => cache.state(key),
|
|
6750
|
+
() => cache.state(null)
|
|
6751
|
+
);
|
|
6752
|
+
const readRef = useRef2(() => Promise.reject(new Error("not ready")));
|
|
6753
|
+
readRef.current = async () => {
|
|
6754
|
+
if (repository === null || meeting === null || meetingId === null) throw new Error("No meeting to read.");
|
|
6755
|
+
if (readerId === null && api !== null && tokens !== null && roomName !== null) {
|
|
6756
|
+
return {
|
|
6757
|
+
kind: "bundle",
|
|
6758
|
+
bundle: await loadGuestMeetingRecord({
|
|
6545
6759
|
api,
|
|
6546
6760
|
repository,
|
|
6547
6761
|
meetingId,
|
|
6548
6762
|
meeting,
|
|
6549
|
-
roomToken:
|
|
6550
|
-
|
|
6551
|
-
|
|
6552
|
-
|
|
6553
|
-
|
|
6554
|
-
|
|
6555
|
-
|
|
6556
|
-
|
|
6557
|
-
|
|
6558
|
-
|
|
6559
|
-
|
|
6560
|
-
|
|
6561
|
-
|
|
6562
|
-
if (!live) return;
|
|
6563
|
-
setFailure({
|
|
6564
|
-
message: thrown.message,
|
|
6565
|
-
remedy: thrown.remedy ?? "The meeting record could not be read. Reload the page; nothing was changed."
|
|
6763
|
+
roomToken: tokens.remembered(roomName)
|
|
6764
|
+
})
|
|
6765
|
+
};
|
|
6766
|
+
}
|
|
6767
|
+
const parts = await loadMeetingRecordParts(repository, meetingId, { userId: readerId });
|
|
6768
|
+
if (api !== null && parts.restrictions.some((part) => part.part === "notes")) {
|
|
6769
|
+
const viaServer = await loadGuestMeetingRecord({
|
|
6770
|
+
api,
|
|
6771
|
+
repository,
|
|
6772
|
+
meetingId,
|
|
6773
|
+
meeting,
|
|
6774
|
+
roomToken: null,
|
|
6775
|
+
session: true
|
|
6566
6776
|
});
|
|
6567
|
-
|
|
6777
|
+
if (viaServer.restrictions.length < parts.restrictions.length) return { kind: "bundle", bundle: viaServer };
|
|
6778
|
+
}
|
|
6779
|
+
return { kind: "parts", meeting, parts };
|
|
6780
|
+
};
|
|
6781
|
+
useEffect2(() => {
|
|
6782
|
+
if (key === null) return;
|
|
6783
|
+
void cache.ensure(key, () => readRef.current());
|
|
6784
|
+
}, [cache, key]);
|
|
6785
|
+
useEffect2(() => {
|
|
6786
|
+
if (key !== null && meeting !== null) cache.setMeeting(key, meeting);
|
|
6787
|
+
}, [cache, key, meeting]);
|
|
6788
|
+
useEffect2(() => {
|
|
6789
|
+
if (key === null || manager === null || repository === null || meetingId === null || readerId === null) {
|
|
6790
|
+
return void 0;
|
|
6791
|
+
}
|
|
6792
|
+
const repo = repository;
|
|
6793
|
+
const binding = (table, apply) => ({
|
|
6794
|
+
event: "*",
|
|
6795
|
+
schema: repo.schema,
|
|
6796
|
+
table,
|
|
6797
|
+
filter: `meeting_id=eq.${meetingId}`,
|
|
6798
|
+
rowId: rowIdOf,
|
|
6799
|
+
onChange: ({ row, payload }) => {
|
|
6800
|
+
if (row === null) return;
|
|
6801
|
+
const gone = payload.eventType === "DELETE";
|
|
6802
|
+
apply(gone ? null : row, gone ? rowIdOf(row) : void 0);
|
|
6803
|
+
}
|
|
6568
6804
|
});
|
|
6569
|
-
return (
|
|
6570
|
-
|
|
6571
|
-
|
|
6572
|
-
|
|
6805
|
+
return cache.hold(
|
|
6806
|
+
key,
|
|
6807
|
+
() => {
|
|
6808
|
+
const handle = manager.open({
|
|
6809
|
+
topic: meetingTopic(meetingId),
|
|
6810
|
+
postgresChanges: [
|
|
6811
|
+
binding(
|
|
6812
|
+
repo.tables.notes,
|
|
6813
|
+
(row, removedId) => cache.apply(key, { part: "notes", row: row && repo.projectNote(row), removedId })
|
|
6814
|
+
),
|
|
6815
|
+
binding(
|
|
6816
|
+
repo.tables.transcripts,
|
|
6817
|
+
(row, removedId) => cache.apply(key, { part: "transcript", row: row && repo.projectSegment(row), removedId })
|
|
6818
|
+
),
|
|
6819
|
+
binding(
|
|
6820
|
+
repo.tables.participants,
|
|
6821
|
+
(row, removedId) => cache.apply(key, { part: "attendance", row: row && repo.projectAttendee(row), removedId })
|
|
6822
|
+
),
|
|
6823
|
+
binding(
|
|
6824
|
+
repo.tables.recordings,
|
|
6825
|
+
(row, removedId) => (
|
|
6826
|
+
// An archived recording is off the record (the read filters it the same way).
|
|
6827
|
+
row !== null && row["deleted_at"] != null ? cache.apply(key, { part: "recording", row: null, removedId: rowIdOf(row) }) : cache.apply(key, { part: "recording", row: row && repo.projectRecording(row), removedId })
|
|
6828
|
+
)
|
|
6829
|
+
)
|
|
6830
|
+
],
|
|
6831
|
+
// Realtime has no replay: a socket that blipped reads the record again.
|
|
6832
|
+
onBackfill: async () => {
|
|
6833
|
+
await cache.ensure(key, () => readRef.current(), { force: true });
|
|
6834
|
+
}
|
|
6835
|
+
});
|
|
6836
|
+
return () => handle.close();
|
|
6837
|
+
},
|
|
6838
|
+
manager
|
|
6839
|
+
);
|
|
6840
|
+
}, [cache, key, manager, repository, meetingId, readerId]);
|
|
6573
6841
|
return {
|
|
6574
|
-
loading: loading && failure === null,
|
|
6575
|
-
record:
|
|
6576
|
-
failure,
|
|
6842
|
+
loading: state.loading && state.failure === null,
|
|
6843
|
+
record: state.record,
|
|
6844
|
+
failure: state.failure,
|
|
6577
6845
|
asking,
|
|
6578
6846
|
answer,
|
|
6579
6847
|
coverage,
|
|
@@ -6604,7 +6872,9 @@ function useMeetingRecord(meeting) {
|
|
|
6604
6872
|
},
|
|
6605
6873
|
[ai, meetingId]
|
|
6606
6874
|
),
|
|
6607
|
-
reload: useCallback2(() =>
|
|
6875
|
+
reload: useCallback2(() => {
|
|
6876
|
+
if (key !== null) void cache.ensure(key, () => readRef.current(), { force: true });
|
|
6877
|
+
}, [cache, key])
|
|
6608
6878
|
};
|
|
6609
6879
|
}
|
|
6610
6880
|
function useMeetings(limit = 25, organizationFilter = null) {
|
|
@@ -11051,6 +11321,7 @@ export {
|
|
|
11051
11321
|
LockIcon,
|
|
11052
11322
|
MEETING_INVITE_KIND,
|
|
11053
11323
|
MEETING_INVITE_VERSION,
|
|
11324
|
+
MEETING_RECORD_GRACE_MS,
|
|
11054
11325
|
MEET_ANSWER_CONTEXT_EVENT,
|
|
11055
11326
|
MEET_ANSWER_EVENTS,
|
|
11056
11327
|
MEET_EVENTS,
|
|
@@ -11128,6 +11399,7 @@ export {
|
|
|
11128
11399
|
asParticipantIdentity,
|
|
11129
11400
|
asRoomName,
|
|
11130
11401
|
asUserId,
|
|
11402
|
+
assembleMeetingRecord,
|
|
11131
11403
|
assignedRoom,
|
|
11132
11404
|
attendanceCsv,
|
|
11133
11405
|
attendanceReport,
|
|
@@ -11157,6 +11429,7 @@ export {
|
|
|
11157
11429
|
createMeetRepository,
|
|
11158
11430
|
createMeetStore,
|
|
11159
11431
|
createMeetingInviteHandler,
|
|
11432
|
+
createMeetingRecordCache,
|
|
11160
11433
|
createMeetingSession,
|
|
11161
11434
|
createMemoryDeviceStorage,
|
|
11162
11435
|
createRecordingController,
|
|
@@ -11191,12 +11464,14 @@ export {
|
|
|
11191
11464
|
loadMeetingActivities,
|
|
11192
11465
|
loadMeetingChat,
|
|
11193
11466
|
loadMeetingRecord,
|
|
11467
|
+
loadMeetingRecordParts,
|
|
11194
11468
|
mapConnectionState,
|
|
11195
11469
|
mapQuality,
|
|
11196
11470
|
meetingInviteAction,
|
|
11197
11471
|
meetingLength,
|
|
11198
11472
|
meetingLink,
|
|
11199
11473
|
meetingPath,
|
|
11474
|
+
meetingRecordCache,
|
|
11200
11475
|
meetingSlug,
|
|
11201
11476
|
meetingTopic,
|
|
11202
11477
|
meetingWhen,
|