@ai-matrx/meet 0.7.2 → 0.7.3
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 +20 -0
- package/dist/index.cjs +460 -35
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +268 -3
- package/dist/index.d.ts +268 -3
- package/dist/index.js +460 -35
- package/dist/index.js.map +1 -1
- package/dist/react.cjs +463 -38
- package/dist/react.cjs.map +1 -1
- package/dist/react.d.cts +1 -1
- package/dist/react.d.ts +1 -1
- package/dist/react.js +463 -38
- package/dist/react.js.map +1 -1
- package/package.json +3 -3
package/dist/react.js
CHANGED
|
@@ -45,7 +45,7 @@ var MeetError = class extends Error {
|
|
|
45
45
|
};
|
|
46
46
|
var FORBIDDEN_CODES = /* @__PURE__ */ new Set(["42501", "PGRST301", "PGRST302"]);
|
|
47
47
|
var NOT_FOUND_CODES = /* @__PURE__ */ new Set(["PGRST116", "PGRST205"]);
|
|
48
|
-
var CONFLICT_CODES = /* @__PURE__ */ new Set(["23505"]);
|
|
48
|
+
var CONFLICT_CODES = /* @__PURE__ */ new Set(["23505", "40001", "55000"]);
|
|
49
49
|
var SESSION_MARKERS = [
|
|
50
50
|
"auth session missing",
|
|
51
51
|
"jwt expired",
|
|
@@ -203,8 +203,19 @@ var MEET_ROUTES = {
|
|
|
203
203
|
*/
|
|
204
204
|
record: "/api/v1/meet/record",
|
|
205
205
|
/** A host chooses what guests may read of ONE meeting (MRF-1). */
|
|
206
|
-
recordAccess: "/api/v1/meet/record/access"
|
|
206
|
+
recordAccess: "/api/v1/meet/record/access",
|
|
207
|
+
/**
|
|
208
|
+
* Meet wave 1 (0.8): bring every invitee up to date after a write — sends the
|
|
209
|
+
* invitation / update / cancellation emails with their `.ics` and re-queues
|
|
210
|
+
* reminders. Idempotent. `{meeting_id}` is substituted by `meetingPath`.
|
|
211
|
+
*/
|
|
212
|
+
meetingAnnounce: "/api/v1/meet/meetings/{meeting_id}/announce",
|
|
213
|
+
/** Put a one-off meeting on the caller's own Google Calendar (gated by the server). */
|
|
214
|
+
meetingGoogleCalendar: "/api/v1/meet/meetings/{meeting_id}/google-calendar"
|
|
207
215
|
};
|
|
216
|
+
function meetingPath(template, meetingId) {
|
|
217
|
+
return template.replace("{meeting_id}", encodeURIComponent(meetingId));
|
|
218
|
+
}
|
|
208
219
|
var ROOM_TOKEN_HEADER = "x-meet-room-token";
|
|
209
220
|
var REFRESH_RATIO = 0.8;
|
|
210
221
|
function readString(source, key) {
|
|
@@ -334,6 +345,19 @@ function createTokenClient(options) {
|
|
|
334
345
|
"The link is wrong or the meeting was deleted. Show the 'meeting not found' screen; a durable link that 404s is never a retry."
|
|
335
346
|
);
|
|
336
347
|
}
|
|
348
|
+
if (response.status === 410) {
|
|
349
|
+
let reason = "This meeting was cancelled.";
|
|
350
|
+
try {
|
|
351
|
+
const body = await response.json();
|
|
352
|
+
if (typeof body?.detail?.message === "string") reason = body.detail.message;
|
|
353
|
+
} catch {
|
|
354
|
+
}
|
|
355
|
+
throw new MeetError(
|
|
356
|
+
"cancelled",
|
|
357
|
+
reason,
|
|
358
|
+
"Nobody can join a cancelled meeting. Show the cancelled screen with this sentence; ask the host to schedule a new one."
|
|
359
|
+
);
|
|
360
|
+
}
|
|
337
361
|
if (response.status === 423) {
|
|
338
362
|
throw new MeetError(
|
|
339
363
|
"room-locked",
|
|
@@ -535,12 +559,12 @@ function createMeetAi(options) {
|
|
|
535
559
|
async function dispatch(route, body, operation) {
|
|
536
560
|
const response = await send({ path: route, body, operation, requireAuth: true });
|
|
537
561
|
if (!response.ok) {
|
|
538
|
-
const
|
|
539
|
-
options.store.patch({ noteTakerRefusal:
|
|
562
|
+
const refusal2 = await refusalFor(response, operation);
|
|
563
|
+
options.store.patch({ noteTakerRefusal: refusal2 });
|
|
540
564
|
throw new MeetError(
|
|
541
565
|
response.status === 403 ? "forbidden" : "transport",
|
|
542
|
-
|
|
543
|
-
|
|
566
|
+
refusal2.reason,
|
|
567
|
+
refusal2.remedy
|
|
544
568
|
);
|
|
545
569
|
}
|
|
546
570
|
options.store.patch({ noteTakerRefusal: null });
|
|
@@ -578,11 +602,11 @@ function createMeetAi(options) {
|
|
|
578
602
|
...signal !== void 0 ? { signal } : {}
|
|
579
603
|
});
|
|
580
604
|
if (!response.ok) {
|
|
581
|
-
const
|
|
605
|
+
const refusal2 = await refusalFor(response, "meet.ai.ask");
|
|
582
606
|
throw new MeetError(
|
|
583
607
|
response.status === 403 || response.status === 401 ? "forbidden" : response.status === 404 ? "not-found" : "transport",
|
|
584
|
-
|
|
585
|
-
|
|
608
|
+
refusal2.reason,
|
|
609
|
+
refusal2.remedy
|
|
586
610
|
);
|
|
587
611
|
}
|
|
588
612
|
const body = response.body;
|
|
@@ -2022,11 +2046,11 @@ function createMeetingSession(options) {
|
|
|
2022
2046
|
} catch {
|
|
2023
2047
|
body = null;
|
|
2024
2048
|
}
|
|
2025
|
-
const
|
|
2049
|
+
const refusal2 = readServerRefusal(body);
|
|
2026
2050
|
throw new MeetError(
|
|
2027
2051
|
response.status === 403 ? "forbidden" : "transport",
|
|
2028
|
-
|
|
2029
|
-
|
|
2052
|
+
refusal2?.reason ?? `meet.endForEveryone: the server answered ${response.status}`,
|
|
2053
|
+
refusal2?.remedy !== void 0 && refusal2.remedy.length > 0 ? refusal2.remedy : "The meeting is still running. Only a host may end it, and the server re-checks that \u2014 being a host on this screen is not the decision."
|
|
2030
2054
|
);
|
|
2031
2055
|
}
|
|
2032
2056
|
let payload = null;
|
|
@@ -2069,7 +2093,11 @@ var TABLES = {
|
|
|
2069
2093
|
invites: "meet_call_invites",
|
|
2070
2094
|
recordings: "meet_recordings",
|
|
2071
2095
|
transcripts: "meet_transcript_segments",
|
|
2072
|
-
notes: "meet_notes"
|
|
2096
|
+
notes: "meet_notes",
|
|
2097
|
+
/** Wave 1 (0.8): who a meeting was sent to, and what they answered. */
|
|
2098
|
+
invitees: "meet_invitees",
|
|
2099
|
+
/** Wave 1 (0.8): one occurrence of a series cancelled or moved. */
|
|
2100
|
+
occurrenceOverrides: "meet_occurrence_overrides"
|
|
2073
2101
|
};
|
|
2074
2102
|
var RPCS = {
|
|
2075
2103
|
/** Atomic: create the invite row and stamp its expiry in one statement. */
|
|
@@ -2085,10 +2113,41 @@ var RPCS = {
|
|
|
2085
2113
|
admit: "meet_admit_participant",
|
|
2086
2114
|
setLock: "meet_set_lock",
|
|
2087
2115
|
endMeeting: "meet_end_meeting",
|
|
2088
|
-
recordConsent: "meet_record_consent"
|
|
2116
|
+
recordConsent: "meet_record_consent",
|
|
2117
|
+
/*
|
|
2118
|
+
* ── MEET WAVE 1 — management (0.8). Every write is a SECURITY DEFINER door
|
|
2119
|
+
* authorized by the meeting's host or a real iam grant (editor to change,
|
|
2120
|
+
* admin = co-host to archive or name a co-host) — never an organization
|
|
2121
|
+
* match. After any of these writes, call `announceMeeting` so the server
|
|
2122
|
+
* sends invitations / updates / cancellations and re-queues reminders.
|
|
2123
|
+
*/
|
|
2124
|
+
/** Schedule with every setting at create; unset settings come from the knobs. */
|
|
2125
|
+
scheduleMeeting: "meet_schedule_meeting",
|
|
2126
|
+
/** Edit / reschedule — a JSON patch with a version compare-and-swap. */
|
|
2127
|
+
updateMeeting: "meet_update_meeting",
|
|
2128
|
+
/** Cancel is a state (`cancelled_at`); the link keeps resolving. */
|
|
2129
|
+
cancelMeeting: "meet_cancel_meeting",
|
|
2130
|
+
/** Archive (soft delete) — the invitations and exceptions cascade. */
|
|
2131
|
+
archiveMeeting: "meet_archive_meeting",
|
|
2132
|
+
restoreMeeting: "meet_restore_meeting",
|
|
2133
|
+
/** Cancel, move or restore ONE occurrence of a series by its original start. */
|
|
2134
|
+
setOccurrence: "meet_set_occurrence",
|
|
2135
|
+
addInvitees: "meet_add_invitees",
|
|
2136
|
+
setInviteeRole: "meet_set_invitee_role",
|
|
2137
|
+
removeInvitee: "meet_remove_invitee",
|
|
2138
|
+
/** A signed-in invitee answers for themselves. */
|
|
2139
|
+
respond: "meet_respond",
|
|
2140
|
+
/** The emailed RSVP link (no account): read and answer. */
|
|
2141
|
+
invitationByToken: "meet_invitation_by_token",
|
|
2142
|
+
respondByToken: "meet_respond_by_token",
|
|
2143
|
+
/** One meeting's occurrences between two instants (INVOKER — the reader's RLS). */
|
|
2144
|
+
meetingOccurrences: "meet_meeting_occurrences",
|
|
2145
|
+
/** Every occurrence the reader can see between two instants — the Upcoming list. */
|
|
2146
|
+
occurrencesBetween: "meet_occurrences_between"
|
|
2089
2147
|
};
|
|
2090
2148
|
var VOID_RPCS = [RPCS.admit, RPCS.recordConsent];
|
|
2091
|
-
var MEETING_COLUMNS = "id,organization_id,room_name,slug,title,kind,host_user_id,scheduled_for,scheduled_duration_minutes,started_at,ended_at,locked,lobby_enabled,recording_policy,ai_enabled,metadata";
|
|
2149
|
+
var MEETING_COLUMNS = "id,organization_id,room_name,slug,title,kind,host_user_id,scheduled_for,scheduled_duration_minutes,started_at,ended_at,locked,lobby_enabled,recording_policy,ai_enabled,metadata,time_zone,agenda,recurrence_rule,join_before_host,cancelled_at,cancellation_reason,deleted_at,calendar_sequence,version";
|
|
2150
|
+
var INVITEE_COLUMNS = "id,meeting_id,invitee_user_id,email,display_name,role,rsvp_state,responded_at,rsvp_note,last_sent_sequence,last_sent_at";
|
|
2092
2151
|
var RECORDING_COLUMNS = "id,meeting_id,state,started_at,ended_at,started_by,file_id,failure_reason";
|
|
2093
2152
|
var SEGMENT_COLUMNS = "id,meeting_id,identity,speaker,text,started_at,is_final,metadata";
|
|
2094
2153
|
var NOTE_COLUMNS = "id,meeting_id,text,kind,assignee_user_id,created_at,metadata";
|
|
@@ -2186,6 +2245,138 @@ function projectNote(row) {
|
|
|
2186
2245
|
source: "record"
|
|
2187
2246
|
};
|
|
2188
2247
|
}
|
|
2248
|
+
var RSVP_STATES = ["needs_action", "accepted", "declined", "tentative"];
|
|
2249
|
+
var OCCURRENCE_STATES = ["scheduled", "moved", "cancelled"];
|
|
2250
|
+
function rsvpState(value) {
|
|
2251
|
+
return RSVP_STATES.includes(value) ? value : "needs_action";
|
|
2252
|
+
}
|
|
2253
|
+
function num(row, key) {
|
|
2254
|
+
const value = row[key];
|
|
2255
|
+
return typeof value === "number" ? value : null;
|
|
2256
|
+
}
|
|
2257
|
+
function when(row, key) {
|
|
2258
|
+
const value = row[key];
|
|
2259
|
+
if (value instanceof Date) return value.toISOString();
|
|
2260
|
+
return text(row, key);
|
|
2261
|
+
}
|
|
2262
|
+
function projectInvitee(row) {
|
|
2263
|
+
const operation = "meet.projectInvitee";
|
|
2264
|
+
const userId = text(row, "invitee_user_id");
|
|
2265
|
+
return {
|
|
2266
|
+
id: requireText(row, "id", operation),
|
|
2267
|
+
meetingId: asMeetingId(requireText(row, "meeting_id", operation)),
|
|
2268
|
+
userId: userId === null ? null : asUserId(userId),
|
|
2269
|
+
email: text(row, "email"),
|
|
2270
|
+
displayName: text(row, "display_name"),
|
|
2271
|
+
role: text(row, "role") === "cohost" ? "cohost" : "invitee",
|
|
2272
|
+
rsvpState: rsvpState(text(row, "rsvp_state")),
|
|
2273
|
+
respondedAt: when(row, "responded_at"),
|
|
2274
|
+
rsvpNote: text(row, "rsvp_note"),
|
|
2275
|
+
lastSentSequence: num(row, "last_sent_sequence"),
|
|
2276
|
+
lastSentAt: when(row, "last_sent_at")
|
|
2277
|
+
};
|
|
2278
|
+
}
|
|
2279
|
+
function projectOccurrence(row) {
|
|
2280
|
+
const operation = "meet.projectOccurrence";
|
|
2281
|
+
const state = text(row, "state");
|
|
2282
|
+
return {
|
|
2283
|
+
meetingId: asMeetingId(requireText(row, "meeting_id", operation)),
|
|
2284
|
+
originalStart: when(row, "original_start") ?? requireText(row, "original_start", operation),
|
|
2285
|
+
occurrenceStart: when(row, "occurrence_start") ?? requireText(row, "occurrence_start", operation),
|
|
2286
|
+
durationMinutes: num(row, "duration_minutes"),
|
|
2287
|
+
// An unknown state is shown as scheduled, never dropped: the row is real.
|
|
2288
|
+
state: OCCURRENCE_STATES.includes(state) ? state : "scheduled",
|
|
2289
|
+
sequence: num(row, "sequence") ?? 0
|
|
2290
|
+
};
|
|
2291
|
+
}
|
|
2292
|
+
function projectUpcoming(row) {
|
|
2293
|
+
const operation = "meet.projectUpcoming";
|
|
2294
|
+
const kind = text(row, "kind");
|
|
2295
|
+
const myRole = text(row, "my_role");
|
|
2296
|
+
const myRsvp = text(row, "my_rsvp");
|
|
2297
|
+
return {
|
|
2298
|
+
...projectOccurrence(row),
|
|
2299
|
+
organizationId: asOrganizationId(requireText(row, "organization_id", operation)),
|
|
2300
|
+
title: text(row, "title") ?? "Meeting",
|
|
2301
|
+
slug: requireText(row, "slug", operation),
|
|
2302
|
+
kind: kind === "scheduled" || kind === "recurring" ? kind : "instant",
|
|
2303
|
+
hostUserId: asUserId(requireText(row, "host_user_id", operation)),
|
|
2304
|
+
timeZone: text(row, "time_zone") ?? "UTC",
|
|
2305
|
+
meetingCancelled: bool(row, "meeting_cancelled", false),
|
|
2306
|
+
myRsvp: myRsvp === null ? null : rsvpState(myRsvp),
|
|
2307
|
+
myRole: myRole === "host" || myRole === "cohost" || myRole === "invitee" ? myRole : null
|
|
2308
|
+
};
|
|
2309
|
+
}
|
|
2310
|
+
function projectOverride(row) {
|
|
2311
|
+
const operation = "meet.projectOverride";
|
|
2312
|
+
return {
|
|
2313
|
+
id: requireText(row, "id", operation),
|
|
2314
|
+
meetingId: asMeetingId(requireText(row, "meeting_id", operation)),
|
|
2315
|
+
originalStart: when(row, "original_start") ?? requireText(row, "original_start", operation),
|
|
2316
|
+
state: text(row, "state") === "moved" ? "moved" : "cancelled",
|
|
2317
|
+
newStart: when(row, "new_start"),
|
|
2318
|
+
newDurationMinutes: num(row, "new_duration_minutes"),
|
|
2319
|
+
reason: text(row, "reason"),
|
|
2320
|
+
calendarSequence: num(row, "calendar_sequence") ?? 0,
|
|
2321
|
+
deletedAt: when(row, "deleted_at")
|
|
2322
|
+
};
|
|
2323
|
+
}
|
|
2324
|
+
function projectRsvpInvitation(body) {
|
|
2325
|
+
const operation = "meet.projectRsvpInvitation";
|
|
2326
|
+
if (typeof body !== "object" || body === null) {
|
|
2327
|
+
throw invalidResponse(operation, "the RSVP door returned no object");
|
|
2328
|
+
}
|
|
2329
|
+
const row = body;
|
|
2330
|
+
if (row["ok"] !== true) {
|
|
2331
|
+
return {
|
|
2332
|
+
ok: false,
|
|
2333
|
+
message: text(row, "message") ?? "This invitation link is no longer valid \u2014 ask the host to send it again."
|
|
2334
|
+
};
|
|
2335
|
+
}
|
|
2336
|
+
const meeting = row["meeting"] ?? {};
|
|
2337
|
+
const invitee = row["invitee"] ?? {};
|
|
2338
|
+
return {
|
|
2339
|
+
ok: true,
|
|
2340
|
+
meeting: {
|
|
2341
|
+
title: text(meeting, "title") ?? "Meeting",
|
|
2342
|
+
slug: requireText(meeting, "slug", operation),
|
|
2343
|
+
scheduledFor: when(meeting, "scheduled_for"),
|
|
2344
|
+
durationMinutes: num(meeting, "duration_minutes"),
|
|
2345
|
+
timeZone: text(meeting, "time_zone") ?? "UTC",
|
|
2346
|
+
agenda: text(meeting, "agenda"),
|
|
2347
|
+
recurrenceRule: text(meeting, "recurrence_rule"),
|
|
2348
|
+
cancelledAt: when(meeting, "cancelled_at"),
|
|
2349
|
+
cancellationReason: text(meeting, "cancellation_reason"),
|
|
2350
|
+
endedAt: when(meeting, "ended_at")
|
|
2351
|
+
},
|
|
2352
|
+
invitee: {
|
|
2353
|
+
displayName: text(invitee, "display_name"),
|
|
2354
|
+
email: text(invitee, "email"),
|
|
2355
|
+
rsvpState: rsvpState(text(invitee, "rsvp_state")),
|
|
2356
|
+
respondedAt: when(invitee, "responded_at")
|
|
2357
|
+
}
|
|
2358
|
+
};
|
|
2359
|
+
}
|
|
2360
|
+
function settingsPatch(settings) {
|
|
2361
|
+
const patch = {};
|
|
2362
|
+
if (settings.lobbyEnabled !== void 0) patch["lobby_enabled"] = settings.lobbyEnabled;
|
|
2363
|
+
if (settings.recordingPolicy !== void 0) patch["recording_policy"] = settings.recordingPolicy;
|
|
2364
|
+
if (settings.aiEnabled !== void 0) patch["ai_enabled"] = settings.aiEnabled;
|
|
2365
|
+
if (settings.joinBeforeHost !== void 0) patch["join_before_host"] = settings.joinBeforeHost;
|
|
2366
|
+
return patch;
|
|
2367
|
+
}
|
|
2368
|
+
function changesPatch(changes) {
|
|
2369
|
+
const patch = settingsPatch(changes);
|
|
2370
|
+
if (changes.title !== void 0) patch["title"] = changes.title;
|
|
2371
|
+
if (changes.agenda !== void 0) patch["agenda"] = changes.agenda;
|
|
2372
|
+
if (changes.timeZone !== void 0) patch["time_zone"] = changes.timeZone;
|
|
2373
|
+
if (changes.scheduledFor !== void 0) patch["scheduled_for"] = changes.scheduledFor;
|
|
2374
|
+
if (changes.scheduledDurationMinutes !== void 0) {
|
|
2375
|
+
patch["scheduled_duration_minutes"] = changes.scheduledDurationMinutes;
|
|
2376
|
+
}
|
|
2377
|
+
if (changes.recurrenceRule !== void 0) patch["recurrence_rule"] = changes.recurrenceRule;
|
|
2378
|
+
return patch;
|
|
2379
|
+
}
|
|
2189
2380
|
function createMeetRepository(options) {
|
|
2190
2381
|
const db = options.client.schema(MEET_SCHEMA);
|
|
2191
2382
|
async function guardVoid(operation, run) {
|
|
@@ -2255,7 +2446,16 @@ function createMeetRepository(options) {
|
|
|
2255
2446
|
lobbyEnabled: bool(row, "lobby_enabled", true),
|
|
2256
2447
|
recordingPolicy: policy === "disabled" || policy === "always-on" ? policy : "host-controlled",
|
|
2257
2448
|
aiEnabled: bool(row, "ai_enabled", true),
|
|
2258
|
-
metadata: typeof metadata === "object" && metadata !== null && !Array.isArray(metadata) ? metadata : {}
|
|
2449
|
+
metadata: typeof metadata === "object" && metadata !== null && !Array.isArray(metadata) ? metadata : {},
|
|
2450
|
+
timeZone: text(row, "time_zone") ?? "UTC",
|
|
2451
|
+
agenda: text(row, "agenda"),
|
|
2452
|
+
recurrenceRule: text(row, "recurrence_rule"),
|
|
2453
|
+
joinBeforeHost: bool(row, "join_before_host", true),
|
|
2454
|
+
cancelledAt: text(row, "cancelled_at"),
|
|
2455
|
+
cancellationReason: text(row, "cancellation_reason"),
|
|
2456
|
+
deletedAt: text(row, "deleted_at"),
|
|
2457
|
+
calendarSequence: typeof row["calendar_sequence"] === "number" ? row["calendar_sequence"] : 0,
|
|
2458
|
+
version: typeof row["version"] === "number" ? row["version"] : 1
|
|
2259
2459
|
};
|
|
2260
2460
|
}
|
|
2261
2461
|
function projectRecording(row) {
|
|
@@ -2342,6 +2542,170 @@ function createMeetRepository(options) {
|
|
|
2342
2542
|
projectNote,
|
|
2343
2543
|
projectRecording,
|
|
2344
2544
|
projectAttendee,
|
|
2545
|
+
projectInvitee,
|
|
2546
|
+
async scheduleMeeting(input) {
|
|
2547
|
+
requireOrg(input.organizationId, "meet.scheduleMeeting");
|
|
2548
|
+
const row = await guard(
|
|
2549
|
+
"meet.scheduleMeeting",
|
|
2550
|
+
() => db.rpc(RPCS.scheduleMeeting, {
|
|
2551
|
+
p_organization_id: input.organizationId,
|
|
2552
|
+
p_host_user_id: input.hostUserId,
|
|
2553
|
+
p_title: input.title,
|
|
2554
|
+
p_scheduled_for: input.scheduledFor,
|
|
2555
|
+
p_time_zone: input.timeZone,
|
|
2556
|
+
p_duration_minutes: input.durationMinutes ?? null,
|
|
2557
|
+
p_agenda: input.agenda ?? null,
|
|
2558
|
+
p_recurrence_rule: input.recurrenceRule ?? null,
|
|
2559
|
+
p_settings: settingsPatch(input.settings ?? {})
|
|
2560
|
+
})
|
|
2561
|
+
);
|
|
2562
|
+
return projectMeeting(row);
|
|
2563
|
+
},
|
|
2564
|
+
async updateMeeting(meetingId, changes, options2 = {}) {
|
|
2565
|
+
const row = await guard(
|
|
2566
|
+
"meet.updateMeeting",
|
|
2567
|
+
() => db.rpc(RPCS.updateMeeting, {
|
|
2568
|
+
p_meeting_id: meetingId,
|
|
2569
|
+
p_changes: changesPatch(changes),
|
|
2570
|
+
p_expected_version: options2.expectedVersion ?? null,
|
|
2571
|
+
p_by_user_id: options2.byUserId ?? null
|
|
2572
|
+
})
|
|
2573
|
+
);
|
|
2574
|
+
return projectMeeting(row);
|
|
2575
|
+
},
|
|
2576
|
+
async cancelMeeting(meetingId, reason, byUserId = null) {
|
|
2577
|
+
const row = await guard(
|
|
2578
|
+
"meet.cancelMeeting",
|
|
2579
|
+
() => db.rpc(RPCS.cancelMeeting, {
|
|
2580
|
+
p_meeting_id: meetingId,
|
|
2581
|
+
p_reason: reason,
|
|
2582
|
+
p_by_user_id: byUserId
|
|
2583
|
+
})
|
|
2584
|
+
);
|
|
2585
|
+
return projectMeeting(row);
|
|
2586
|
+
},
|
|
2587
|
+
async archiveMeeting(meetingId, byUserId = null) {
|
|
2588
|
+
const row = await guard(
|
|
2589
|
+
"meet.archiveMeeting",
|
|
2590
|
+
() => db.rpc(RPCS.archiveMeeting, { p_meeting_id: meetingId, p_by_user_id: byUserId })
|
|
2591
|
+
);
|
|
2592
|
+
return projectMeeting(row);
|
|
2593
|
+
},
|
|
2594
|
+
async restoreMeeting(meetingId, byUserId = null) {
|
|
2595
|
+
const row = await guard(
|
|
2596
|
+
"meet.restoreMeeting",
|
|
2597
|
+
() => db.rpc(RPCS.restoreMeeting, { p_meeting_id: meetingId, p_by_user_id: byUserId })
|
|
2598
|
+
);
|
|
2599
|
+
return projectMeeting(row);
|
|
2600
|
+
},
|
|
2601
|
+
async setOccurrence(args) {
|
|
2602
|
+
const row = await guard(
|
|
2603
|
+
"meet.setOccurrence",
|
|
2604
|
+
() => db.rpc(RPCS.setOccurrence, {
|
|
2605
|
+
p_meeting_id: args.meetingId,
|
|
2606
|
+
p_original_start: args.originalStart,
|
|
2607
|
+
p_action: args.action,
|
|
2608
|
+
p_new_start: args.newStart ?? null,
|
|
2609
|
+
p_new_duration_minutes: args.newDurationMinutes ?? null,
|
|
2610
|
+
p_reason: args.reason ?? null,
|
|
2611
|
+
p_by_user_id: args.byUserId ?? null
|
|
2612
|
+
})
|
|
2613
|
+
);
|
|
2614
|
+
return projectOverride(row);
|
|
2615
|
+
},
|
|
2616
|
+
async meetingOccurrences(meetingId, { from, to, limit = 200 }) {
|
|
2617
|
+
const rows = await guard(
|
|
2618
|
+
"meet.meetingOccurrences",
|
|
2619
|
+
() => db.rpc(RPCS.meetingOccurrences, {
|
|
2620
|
+
p_meeting_id: meetingId,
|
|
2621
|
+
p_from: from,
|
|
2622
|
+
p_to: to,
|
|
2623
|
+
p_limit: limit
|
|
2624
|
+
})
|
|
2625
|
+
);
|
|
2626
|
+
return rows.map(projectOccurrence);
|
|
2627
|
+
},
|
|
2628
|
+
async occurrencesBetween({ from, to, organizationId = null, limit = 500 }) {
|
|
2629
|
+
const rows = await guard(
|
|
2630
|
+
"meet.occurrencesBetween",
|
|
2631
|
+
() => db.rpc(RPCS.occurrencesBetween, {
|
|
2632
|
+
p_from: from,
|
|
2633
|
+
p_to: to,
|
|
2634
|
+
p_organization_id: organizationId,
|
|
2635
|
+
p_limit: limit
|
|
2636
|
+
})
|
|
2637
|
+
);
|
|
2638
|
+
return rows.map(projectUpcoming);
|
|
2639
|
+
},
|
|
2640
|
+
async invitees(meetingId) {
|
|
2641
|
+
const response = await db.from(TABLES.invitees).select(INVITEE_COLUMNS).eq("meeting_id", meetingId).is("deleted_at", null).order("created_at", { ascending: true }).order("id", { ascending: true });
|
|
2642
|
+
if (response.error !== null) throw normalizeMeetError(response.error, "meet.invitees");
|
|
2643
|
+
return (response.data ?? []).map(projectInvitee);
|
|
2644
|
+
},
|
|
2645
|
+
async addInvitees(meetingId, people, byUserId = null) {
|
|
2646
|
+
const rows = await guard(
|
|
2647
|
+
"meet.addInvitees",
|
|
2648
|
+
() => db.rpc(RPCS.addInvitees, {
|
|
2649
|
+
p_meeting_id: meetingId,
|
|
2650
|
+
p_invitees: people.map((person) => ({
|
|
2651
|
+
...person.userId !== void 0 ? { user_id: person.userId } : {},
|
|
2652
|
+
...person.email !== void 0 ? { email: person.email } : {},
|
|
2653
|
+
...person.displayName !== void 0 ? { display_name: person.displayName } : {},
|
|
2654
|
+
...person.role !== void 0 ? { role: person.role } : {}
|
|
2655
|
+
})),
|
|
2656
|
+
p_by_user_id: byUserId
|
|
2657
|
+
})
|
|
2658
|
+
);
|
|
2659
|
+
return rows.map(projectInvitee);
|
|
2660
|
+
},
|
|
2661
|
+
async setInviteeRole(inviteeId, role, byUserId = null) {
|
|
2662
|
+
const row = await guard(
|
|
2663
|
+
"meet.setInviteeRole",
|
|
2664
|
+
() => db.rpc(RPCS.setInviteeRole, {
|
|
2665
|
+
p_invitee_id: inviteeId,
|
|
2666
|
+
p_role: role,
|
|
2667
|
+
p_by_user_id: byUserId
|
|
2668
|
+
})
|
|
2669
|
+
);
|
|
2670
|
+
return projectInvitee(row);
|
|
2671
|
+
},
|
|
2672
|
+
async removeInvitee(inviteeId, byUserId = null) {
|
|
2673
|
+
const row = await guard(
|
|
2674
|
+
"meet.removeInvitee",
|
|
2675
|
+
() => db.rpc(RPCS.removeInvitee, { p_invitee_id: inviteeId, p_by_user_id: byUserId })
|
|
2676
|
+
);
|
|
2677
|
+
return projectInvitee(row);
|
|
2678
|
+
},
|
|
2679
|
+
async respond(meetingId, answer, note = null) {
|
|
2680
|
+
const row = await guard(
|
|
2681
|
+
"meet.respond",
|
|
2682
|
+
() => db.rpc(RPCS.respond, {
|
|
2683
|
+
p_meeting_id: meetingId,
|
|
2684
|
+
p_response: answer,
|
|
2685
|
+
p_note: note,
|
|
2686
|
+
p_by_user_id: null
|
|
2687
|
+
})
|
|
2688
|
+
);
|
|
2689
|
+
return projectInvitee(row);
|
|
2690
|
+
},
|
|
2691
|
+
async invitationByToken(secret) {
|
|
2692
|
+
const body = await guard(
|
|
2693
|
+
"meet.invitationByToken",
|
|
2694
|
+
() => db.rpc(RPCS.invitationByToken, { p_secret: secret })
|
|
2695
|
+
);
|
|
2696
|
+
return projectRsvpInvitation(body);
|
|
2697
|
+
},
|
|
2698
|
+
async respondByToken(secret, answer, note = null) {
|
|
2699
|
+
const body = await guard(
|
|
2700
|
+
"meet.respondByToken",
|
|
2701
|
+
() => db.rpc(RPCS.respondByToken, {
|
|
2702
|
+
p_secret: secret,
|
|
2703
|
+
p_response: answer,
|
|
2704
|
+
p_note: note
|
|
2705
|
+
})
|
|
2706
|
+
);
|
|
2707
|
+
return projectRsvpInvitation(body);
|
|
2708
|
+
},
|
|
2345
2709
|
async attendees(meetingId) {
|
|
2346
2710
|
const response = await db.from(TABLES.participants).select(ATTENDEE_COLUMNS).eq("meeting_id", meetingId).order("joined_at", { ascending: true }).order("id", { ascending: true });
|
|
2347
2711
|
if (response.error !== null) throw normalizeMeetError(response.error, "meet.attendees");
|
|
@@ -2769,14 +3133,14 @@ function noteTakerStatus(args) {
|
|
|
2769
3133
|
const row = typeof raw === "object" && raw !== null && !Array.isArray(raw) ? raw : {};
|
|
2770
3134
|
const recorded = readState(row["state"]);
|
|
2771
3135
|
const detail = typeof row["reason"] === "string" && row["reason"].length > 0 ? row["reason"] : null;
|
|
2772
|
-
const
|
|
3136
|
+
const refusal2 = args.refusal ?? null;
|
|
2773
3137
|
if (recorded === null) {
|
|
2774
|
-
return { state: "absent", detail, present, refusal };
|
|
3138
|
+
return { state: "absent", detail, present, refusal: refusal2 };
|
|
2775
3139
|
}
|
|
2776
3140
|
if (recorded === "transcribing" && !present) {
|
|
2777
|
-
return { state: "handoff_pending", detail, present, refusal };
|
|
3141
|
+
return { state: "handoff_pending", detail, present, refusal: refusal2 };
|
|
2778
3142
|
}
|
|
2779
|
-
return { state: recorded, detail, present, refusal };
|
|
3143
|
+
return { state: recorded, detail, present, refusal: refusal2 };
|
|
2780
3144
|
}
|
|
2781
3145
|
function noteTakerLabel(status) {
|
|
2782
3146
|
switch (status.state) {
|
|
@@ -3801,11 +4165,11 @@ function computeStage(args) {
|
|
|
3801
4165
|
presenting: sharer !== null
|
|
3802
4166
|
};
|
|
3803
4167
|
}
|
|
3804
|
-
function gridColumns(
|
|
3805
|
-
if (
|
|
3806
|
-
if (
|
|
3807
|
-
if (
|
|
3808
|
-
if (
|
|
4168
|
+
function gridColumns(count2) {
|
|
4169
|
+
if (count2 <= 1) return 1;
|
|
4170
|
+
if (count2 <= 4) return 2;
|
|
4171
|
+
if (count2 <= 9) return 3;
|
|
4172
|
+
if (count2 <= 16) return 4;
|
|
3809
4173
|
return 5;
|
|
3810
4174
|
}
|
|
3811
4175
|
|
|
@@ -3979,7 +4343,7 @@ function meetingWhen(meeting, locale) {
|
|
|
3979
4343
|
if (start === null) return "Time not recorded";
|
|
3980
4344
|
const startedAt = new Date(start);
|
|
3981
4345
|
if (Number.isNaN(startedAt.getTime())) return "Time not recorded";
|
|
3982
|
-
const
|
|
4346
|
+
const when2 = startedAt.toLocaleString(locale, {
|
|
3983
4347
|
weekday: "long",
|
|
3984
4348
|
year: "numeric",
|
|
3985
4349
|
month: "long",
|
|
@@ -3987,11 +4351,11 @@ function meetingWhen(meeting, locale) {
|
|
|
3987
4351
|
hour: "numeric",
|
|
3988
4352
|
minute: "2-digit"
|
|
3989
4353
|
});
|
|
3990
|
-
if (meeting.endedAt === null || meeting.startedAt === null) return
|
|
4354
|
+
if (meeting.endedAt === null || meeting.startedAt === null) return when2;
|
|
3991
4355
|
const endedAt = new Date(meeting.endedAt);
|
|
3992
|
-
if (Number.isNaN(endedAt.getTime())) return
|
|
4356
|
+
if (Number.isNaN(endedAt.getTime())) return when2;
|
|
3993
4357
|
const elapsedMs = endedAt.getTime() - startedAt.getTime();
|
|
3994
|
-
return `${
|
|
4358
|
+
return `${when2} \xB7 ${formatDurationMs(elapsedMs, { style: "long" })}`;
|
|
3995
4359
|
}
|
|
3996
4360
|
var GUEST_RECORD_ACCESS_LEVELS = [
|
|
3997
4361
|
"none",
|
|
@@ -4098,8 +4462,8 @@ async function loadGuestMeetingRecord(args) {
|
|
|
4098
4462
|
);
|
|
4099
4463
|
}
|
|
4100
4464
|
if (!response.ok) {
|
|
4101
|
-
const
|
|
4102
|
-
return refusedBundle(meeting,
|
|
4465
|
+
const refusal2 = await readRefusal(response);
|
|
4466
|
+
return refusedBundle(meeting, refusal2.message, refusal2.remedy);
|
|
4103
4467
|
}
|
|
4104
4468
|
const body = await response.json();
|
|
4105
4469
|
const payload = typeof body["data"] === "object" && body["data"] !== null ? body["data"] : body;
|
|
@@ -4190,8 +4554,8 @@ async function setGuestRecordAccess(args) {
|
|
|
4190
4554
|
}
|
|
4191
4555
|
});
|
|
4192
4556
|
if (!response.ok) {
|
|
4193
|
-
const
|
|
4194
|
-
throw new MeetError("forbidden",
|
|
4557
|
+
const refusal2 = await readRefusal(response);
|
|
4558
|
+
throw new MeetError("forbidden", refusal2.message, refusal2.remedy);
|
|
4195
4559
|
}
|
|
4196
4560
|
const body = await response.json();
|
|
4197
4561
|
const payload = typeof body["data"] === "object" && body["data"] !== null ? body["data"] : body;
|
|
@@ -4858,8 +5222,8 @@ function callStateLabel(state, outgoing) {
|
|
|
4858
5222
|
return "Call";
|
|
4859
5223
|
}
|
|
4860
5224
|
}
|
|
4861
|
-
function countLabel(
|
|
4862
|
-
return `${
|
|
5225
|
+
function countLabel(count2, singular, plural = `${singular}s`) {
|
|
5226
|
+
return `${count2} ${count2 === 1 ? singular : plural}`;
|
|
4863
5227
|
}
|
|
4864
5228
|
|
|
4865
5229
|
// src/react/icons.tsx
|
|
@@ -5917,7 +6281,7 @@ function ScheduleMeetingBody({
|
|
|
5917
6281
|
host
|
|
5918
6282
|
}) {
|
|
5919
6283
|
const [title, setTitle] = useState3("");
|
|
5920
|
-
const [
|
|
6284
|
+
const [when2, setWhen] = useState3("");
|
|
5921
6285
|
const [kind, setKind] = useState3("scheduled");
|
|
5922
6286
|
const [busy, setBusy] = useState3(false);
|
|
5923
6287
|
const [link, setLink] = useState3(null);
|
|
@@ -5937,7 +6301,7 @@ function ScheduleMeetingBody({
|
|
|
5937
6301
|
hostUserId: host.identity.userId,
|
|
5938
6302
|
title: title.trim(),
|
|
5939
6303
|
kind,
|
|
5940
|
-
scheduledFor:
|
|
6304
|
+
scheduledFor: when2.length > 0 ? new Date(when2).toISOString() : null
|
|
5941
6305
|
}).then((meeting) => {
|
|
5942
6306
|
setLink(meetingLink(appBaseUrl, meeting.slug));
|
|
5943
6307
|
onScheduled?.(meeting.slug);
|
|
@@ -5967,7 +6331,7 @@ function ScheduleMeetingBody({
|
|
|
5967
6331
|
{
|
|
5968
6332
|
className: "mx-meet__input",
|
|
5969
6333
|
type: "datetime-local",
|
|
5970
|
-
value:
|
|
6334
|
+
value: when2,
|
|
5971
6335
|
onChange: (event) => setWhen(event.target.value)
|
|
5972
6336
|
}
|
|
5973
6337
|
)
|
|
@@ -6547,6 +6911,57 @@ function createMeetingInviteHandler(options) {
|
|
|
6547
6911
|
}
|
|
6548
6912
|
};
|
|
6549
6913
|
}
|
|
6914
|
+
|
|
6915
|
+
// src/core/management.ts
|
|
6916
|
+
async function refusal(response, operation) {
|
|
6917
|
+
let body = null;
|
|
6918
|
+
try {
|
|
6919
|
+
body = await response.json();
|
|
6920
|
+
} catch {
|
|
6921
|
+
body = null;
|
|
6922
|
+
}
|
|
6923
|
+
const said = readServerRefusal(body);
|
|
6924
|
+
return new MeetError(
|
|
6925
|
+
response.status === 403 || response.status === 401 ? "forbidden" : response.status === 404 ? "not-found" : response.status === 409 ? "conflict" : "transport",
|
|
6926
|
+
`${operation}: ${said?.reason ?? `the server answered ${response.status}`}`,
|
|
6927
|
+
said?.remedy || "The meeting itself is saved \u2014 only the telling failed. Try again; the call is idempotent."
|
|
6928
|
+
);
|
|
6929
|
+
}
|
|
6930
|
+
function count(body, key) {
|
|
6931
|
+
const value = body[key];
|
|
6932
|
+
return typeof value === "number" ? value : 0;
|
|
6933
|
+
}
|
|
6934
|
+
async function announceMeeting(api, meetingId) {
|
|
6935
|
+
const operation = "meet.announceMeeting";
|
|
6936
|
+
const response = await api.request({
|
|
6937
|
+
path: meetingPath(MEET_ROUTES.meetingAnnounce, meetingId),
|
|
6938
|
+
method: "POST",
|
|
6939
|
+
requireAuth: true,
|
|
6940
|
+
operation
|
|
6941
|
+
});
|
|
6942
|
+
if (!response.ok) throw await refusal(response, operation);
|
|
6943
|
+
const body = await response.json();
|
|
6944
|
+
return {
|
|
6945
|
+
invitations: count(body, "invitations"),
|
|
6946
|
+
updates: count(body, "updates"),
|
|
6947
|
+
cancellations: count(body, "cancellations"),
|
|
6948
|
+
alreadyCurrent: count(body, "already_current"),
|
|
6949
|
+
remindersQueued: count(body, "reminders_queued"),
|
|
6950
|
+
remindersCancelled: count(body, "reminders_cancelled")
|
|
6951
|
+
};
|
|
6952
|
+
}
|
|
6953
|
+
async function pushMeetingToGoogleCalendar(api, meetingId, target) {
|
|
6954
|
+
const operation = "meet.pushMeetingToGoogleCalendar";
|
|
6955
|
+
const response = await api.request({
|
|
6956
|
+
path: meetingPath(MEET_ROUTES.meetingGoogleCalendar, meetingId),
|
|
6957
|
+
method: "POST",
|
|
6958
|
+
requireAuth: true,
|
|
6959
|
+
operation,
|
|
6960
|
+
body: { connection_id: target.connectionId, calendar_id: target.calendarId ?? "primary" }
|
|
6961
|
+
});
|
|
6962
|
+
if (!response.ok) throw await refusal(response, operation);
|
|
6963
|
+
return await response.json();
|
|
6964
|
+
}
|
|
6550
6965
|
export {
|
|
6551
6966
|
AiPanel,
|
|
6552
6967
|
AttendanceNotice,
|
|
@@ -6632,6 +7047,7 @@ export {
|
|
|
6632
7047
|
TRANSCRIPTION_TOPIC,
|
|
6633
7048
|
WifiLowIcon,
|
|
6634
7049
|
absenceSentence,
|
|
7050
|
+
announceMeeting,
|
|
6635
7051
|
applyRoomEvent,
|
|
6636
7052
|
asCallId,
|
|
6637
7053
|
asMeetingId,
|
|
@@ -6646,6 +7062,7 @@ export {
|
|
|
6646
7062
|
canModerateParticipant,
|
|
6647
7063
|
canTransition,
|
|
6648
7064
|
captionSegment,
|
|
7065
|
+
changesPatch,
|
|
6649
7066
|
computeStage,
|
|
6650
7067
|
countLabel,
|
|
6651
7068
|
createCallCenter,
|
|
@@ -6690,6 +7107,7 @@ export {
|
|
|
6690
7107
|
mapQuality,
|
|
6691
7108
|
meetingInviteAction,
|
|
6692
7109
|
meetingLink,
|
|
7110
|
+
meetingPath,
|
|
6693
7111
|
meetingSlug,
|
|
6694
7112
|
meetingTopic,
|
|
6695
7113
|
meetingWhen,
|
|
@@ -6706,9 +7124,15 @@ export {
|
|
|
6706
7124
|
parseRoomToken,
|
|
6707
7125
|
participantSummary,
|
|
6708
7126
|
playableRecordingFileId,
|
|
7127
|
+
projectInvitee,
|
|
6709
7128
|
projectNote,
|
|
7129
|
+
projectOccurrence,
|
|
7130
|
+
projectOverride,
|
|
6710
7131
|
projectParticipant,
|
|
7132
|
+
projectRsvpInvitation,
|
|
6711
7133
|
projectSegment,
|
|
7134
|
+
projectUpcoming,
|
|
7135
|
+
pushMeetingToGoogleCalendar,
|
|
6712
7136
|
readServerRefusal,
|
|
6713
7137
|
readableTranscript,
|
|
6714
7138
|
recordingLabel,
|
|
@@ -6716,6 +7140,7 @@ export {
|
|
|
6716
7140
|
roleOf,
|
|
6717
7141
|
roomTopic,
|
|
6718
7142
|
setGuestRecordAccess,
|
|
7143
|
+
settingsPatch,
|
|
6719
7144
|
snapshotParticipant,
|
|
6720
7145
|
unavailableFrom,
|
|
6721
7146
|
useCalls,
|