@marktiderman/genesis-chat 0.2.0 → 0.2.2
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/README.md +82 -7
- package/dist/{chunk-5D5A7I4Q.js → chunk-A4YJX7KT.js} +130 -16
- package/dist/chunk-A4YJX7KT.js.map +1 -0
- package/dist/chunk-AZS3HNLM.js +30 -0
- package/dist/chunk-AZS3HNLM.js.map +1 -0
- package/dist/{chunk-7S6HYNWW.js → chunk-GBJ3YI4E.js} +72 -5
- package/dist/chunk-GBJ3YI4E.js.map +1 -0
- package/dist/{chunk-PZTOY2LY.js → chunk-JU2FAPBI.js} +46 -13
- package/dist/chunk-JU2FAPBI.js.map +1 -0
- package/dist/{client-DMa2j7kz.d.ts → client-DUIG1hum.d.ts} +36 -1
- package/dist/index.d.ts +4 -4
- package/dist/index.js +2 -2
- package/dist/native.d.ts +36 -7
- package/dist/native.js +239 -116
- package/dist/native.js.map +1 -1
- package/dist/{outbox-B0y3phE0.d.ts → outbox-B9NAWQ58.d.ts} +74 -6
- package/dist/push.d.ts +2 -2
- package/dist/react.d.ts +48 -21
- package/dist/react.js +3 -3
- package/dist/server.d.ts +43 -2
- package/dist/server.js +115 -15
- package/dist/server.js.map +1 -1
- package/dist/{thread-DWXHKPdw.d.ts → thread-oPra7pkW.d.ts} +1 -1
- package/dist/web.d.ts +31 -11
- package/dist/web.js +97 -48
- package/dist/web.js.map +1 -1
- package/migrations/0003_genesis_chat_search_threads.sql +85 -0
- package/migrations/tests/0002_genesis_chat_coaching.test.sql +7 -3
- package/migrations/tests/0003_genesis_chat_search_threads.test.sql +50 -0
- package/package.json +13 -9
- package/src/__tests__/coaching-core.test.ts +3 -1
- package/src/__tests__/coaching-react.test.tsx +73 -2
- package/src/__tests__/coaching-web.test.tsx +78 -6
- package/src/__tests__/fake-xhr.ts +54 -0
- package/src/__tests__/outbox-hosted.test.ts +269 -0
- package/src/__tests__/review-279.test.ts +41 -0
- package/src/__tests__/review-fixes-web.test.tsx +13 -10
- package/src/__tests__/server-fake-video.test.ts +81 -0
- package/src/__tests__/server-fakes.ts +7 -1
- package/src/__tests__/server-safety.test.ts +17 -4
- package/src/__tests__/web.test.tsx +16 -0
- package/src/core/client.ts +28 -2
- package/src/core/outbox.ts +222 -16
- package/src/core/transfer.ts +71 -0
- package/src/core/types.ts +17 -0
- package/src/index.ts +1 -0
- package/src/native/__tests__/emoji.test.tsx +16 -0
- package/src/native/__tests__/organise-screens.test.tsx +80 -0
- package/src/native/__tests__/video-media.test.tsx +81 -4
- package/src/native/attachment-cards.tsx +22 -6
- package/src/native/inbox-screen.tsx +12 -6
- package/src/native/index.ts +2 -2
- package/src/native/message-actions-sheet.tsx +10 -5
- package/src/native/message-bubble.tsx +3 -11
- package/src/native/modules.ts +2 -0
- package/src/native/schedule-sheet.tsx +23 -9
- package/src/native/search-screen.tsx +26 -5
- package/src/native/thread-screen.tsx +30 -3
- package/src/native/topics.tsx +70 -15
- package/src/native/video-player.tsx +46 -6
- package/src/native/video-recorder.tsx +6 -3
- package/src/native/voice-player.tsx +18 -0
- package/src/react/index.tsx +103 -19
- package/src/react/previews.ts +51 -0
- package/src/server/fake-video.ts +171 -0
- package/src/server/index.ts +1 -0
- package/src/web/attachments.tsx +6 -6
- package/src/web/bubble.tsx +22 -0
- package/src/web/inbox.tsx +7 -6
- package/src/web/memory-client.ts +11 -3
- package/src/web/panel.tsx +2 -2
- package/src/web/schedule.tsx +14 -2
- package/src/web/thread.tsx +36 -8
- package/src/web/video.tsx +28 -34
- package/dist/chunk-5D5A7I4Q.js.map +0 -1
- package/dist/chunk-7S6HYNWW.js.map +0 -1
- package/dist/chunk-PZTOY2LY.js.map +0 -1
|
@@ -496,7 +496,8 @@ function toSearchHit(r) {
|
|
|
496
496
|
senderId: str(r.sender_id),
|
|
497
497
|
createdAt: String(r.created_at),
|
|
498
498
|
snippet: String(r.snippet ?? ""),
|
|
499
|
-
inTranscript: Boolean(r.in_transcript)
|
|
499
|
+
inTranscript: Boolean(r.in_transcript),
|
|
500
|
+
threadRootId: str(r.thread_root_id)
|
|
500
501
|
};
|
|
501
502
|
}
|
|
502
503
|
function toLinkPreview(r) {
|
|
@@ -795,13 +796,32 @@ function createChatClient(supabase, options = {}) {
|
|
|
795
796
|
* Uploads a file into the conversation's folder for the signed-in person and
|
|
796
797
|
* returns its path, ready to attach. Resumable for big files is the native
|
|
797
798
|
* renderer's job (Supabase TUS); this is the plain upload.
|
|
799
|
+
*
|
|
800
|
+
* With `onProgress(sent, total)`, where XMLHttpRequest exists (browsers, React Native), the
|
|
801
|
+
* bytes go to a signed upload URL (`createSignedUploadUrl`, which checks the same storage
|
|
802
|
+
* policy as a plain upload) through XMLHttpRequest, which reports progress; elsewhere the
|
|
803
|
+
* plain upload runs and reports once, at the end. `signal` cancels it.
|
|
798
804
|
*/
|
|
799
805
|
async upload(conversationId, userId, file, opts) {
|
|
800
806
|
const ext = fileExtension(opts.name, opts.contentType);
|
|
801
807
|
if (!isAllowedExtension(ext)) throw new ChatError(`Files of type .${ext} can't be sent`, "22023", "upload");
|
|
802
808
|
const id = opts.id ?? randomId();
|
|
803
809
|
const path = `${conversationId}/${userId}/${id}.${ext}`;
|
|
804
|
-
|
|
810
|
+
const bucket = supabase.storage.from(CHAT_BUCKET);
|
|
811
|
+
if (opts.onProgress && typeof XMLHttpRequest !== "undefined" && typeof bucket.createSignedUploadUrl === "function") {
|
|
812
|
+
const signed = must("upload", await bucket.createSignedUploadUrl(path));
|
|
813
|
+
if (!signed?.signedUrl) throw new ChatError("No upload URL came back", void 0, "upload");
|
|
814
|
+
await sendBytes(signed.signedUrl, file, {
|
|
815
|
+
method: "PUT",
|
|
816
|
+
headers: { "content-type": opts.contentType, "x-upsert": "false", "cache-control": "max-age=3600" },
|
|
817
|
+
signal: opts.signal,
|
|
818
|
+
onProgress: opts.onProgress
|
|
819
|
+
});
|
|
820
|
+
return path;
|
|
821
|
+
}
|
|
822
|
+
must("upload", await bucket.upload(path, file, { contentType: opts.contentType, upsert: false }));
|
|
823
|
+
const total = typeof Blob !== "undefined" && file instanceof Blob ? file.size : file.byteLength;
|
|
824
|
+
opts.onProgress?.(total, total);
|
|
805
825
|
return path;
|
|
806
826
|
},
|
|
807
827
|
/** Signed links for files, cached until shortly before they expire. */
|
|
@@ -894,6 +914,53 @@ function attachmentArg(a) {
|
|
|
894
914
|
};
|
|
895
915
|
}
|
|
896
916
|
|
|
897
|
-
|
|
898
|
-
|
|
899
|
-
|
|
917
|
+
// src/core/transfer.ts
|
|
918
|
+
var sizeOf = (body) => typeof Blob !== "undefined" && body instanceof Blob ? body.size : body.byteLength;
|
|
919
|
+
var refusal = (status) => status >= 500 || status === 0 || status === 408 ? void 0 : status === 429 ? "GS429" : "22023";
|
|
920
|
+
var aborted = () => new ChatError("The upload was cancelled", "GSABORT", "upload");
|
|
921
|
+
function sendBytes(url, body, opts = {}) {
|
|
922
|
+
const method = opts.method ?? "PUT";
|
|
923
|
+
const total = sizeOf(body);
|
|
924
|
+
if (opts.signal?.aborted) return Promise.reject(aborted());
|
|
925
|
+
if (typeof XMLHttpRequest === "undefined" || opts.fetch) {
|
|
926
|
+
const f = opts.fetch ?? fetch;
|
|
927
|
+
return f(url, { method, body, headers: opts.headers, signal: opts.signal }).then(async (res) => {
|
|
928
|
+
if (!res.ok) throw new ChatError(`The upload failed (${res.status})`, refusal(res.status), "upload");
|
|
929
|
+
opts.onProgress?.(total, total);
|
|
930
|
+
return res.text().catch(() => "");
|
|
931
|
+
});
|
|
932
|
+
}
|
|
933
|
+
return new Promise((resolve, reject) => {
|
|
934
|
+
const xhr = new XMLHttpRequest();
|
|
935
|
+
const onAbort = () => xhr.abort();
|
|
936
|
+
const done = () => opts.signal?.removeEventListener("abort", onAbort);
|
|
937
|
+
xhr.open(method, url);
|
|
938
|
+
for (const [k, v] of Object.entries(opts.headers ?? {})) xhr.setRequestHeader(k, v);
|
|
939
|
+
if (opts.onProgress && xhr.upload) {
|
|
940
|
+
xhr.upload.onprogress = (e) => opts.onProgress?.(e.loaded, e.lengthComputable ? e.total : total);
|
|
941
|
+
}
|
|
942
|
+
xhr.onload = () => {
|
|
943
|
+
done();
|
|
944
|
+
if (xhr.status >= 200 && xhr.status < 300) {
|
|
945
|
+
opts.onProgress?.(total, total);
|
|
946
|
+
resolve(typeof xhr.responseText === "string" ? xhr.responseText : "");
|
|
947
|
+
} else {
|
|
948
|
+
reject(new ChatError(`The upload failed (${xhr.status})`, refusal(xhr.status), "upload"));
|
|
949
|
+
}
|
|
950
|
+
};
|
|
951
|
+
xhr.onerror = () => {
|
|
952
|
+
done();
|
|
953
|
+
reject(new ChatError("The upload didn't reach the server", void 0, "upload"));
|
|
954
|
+
};
|
|
955
|
+
xhr.onabort = () => {
|
|
956
|
+
done();
|
|
957
|
+
reject(aborted());
|
|
958
|
+
};
|
|
959
|
+
opts.signal?.addEventListener("abort", onAbort, { once: true });
|
|
960
|
+
xhr.send(body);
|
|
961
|
+
});
|
|
962
|
+
}
|
|
963
|
+
|
|
964
|
+
export { ALLOWED_EXTENSIONS, CHAT_BUCKET, CHAT_LIMITS, CHAT_SCHEMA, ChatError, MAX_RANGES_PER_DAY, PLAYBACK_RATES, WEEKDAYS, attachmentKindFor, awayLine, awaySummary, conversationTopic, createChatClient, fileExtension, formatDuration, hoursSummary, isAllowedExtension, isAway, isWithinWorkingHours, localClock, minutesOf, namesLine, nextPlaybackRate, normalizeHours, randomId, recordingLimitMs, sendBytes, timeZoneLabel, toAttachment, toAvailability, toLinkPreview, toMessage, toScheduled, toSearchHit, toTopic, untilLabel, userTopic, waveformFromMetering, waveformPeaks };
|
|
965
|
+
//# sourceMappingURL=chunk-GBJ3YI4E.js.map
|
|
966
|
+
//# sourceMappingURL=chunk-GBJ3YI4E.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["../src/core/availability.ts","../src/core/media.ts","../src/core/client.ts","../src/core/transfer.ts"],"names":["rows"],"mappings":"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","file":"chunk-GBJ3YI4E.js","sourcesContent":["/**\n * @feat:chat\n * Availability, the same on every platform: is someone inside their working hours (read in\n * their own time zone), are they away, and the one line a thread shows about it (\"Mark\n * replies Mon–Fri, 9–5 (Eastern)\", \"Away until Monday: Back Monday\"). Pure: `now` is passed\n * in, and time zones come from Intl.\n */\nimport type { ChatAvailability, Weekday, WorkingHours } from \"./types\";\n\nexport const WEEKDAYS: readonly Weekday[] = [\"mon\", \"tue\", \"wed\", \"thu\", \"fri\", \"sat\", \"sun\"];\n\nconst SHORT: Record<Weekday, string> = { mon: \"Mon\", tue: \"Tue\", wed: \"Wed\", thu: \"Thu\", fri: \"Fri\", sat: \"Sat\", sun: \"Sun\" };\n\n/** \"09:30\" → 570; a bad value (anything but such a string) → null. \"24:00\" is allowed as the end of a day. */\nexport function minutesOf(hhmm: unknown): number | null {\n if (typeof hhmm !== \"string\") return null;\n const m = /^(\\d{1,2}):(\\d{2})$/.exec(hhmm.trim());\n if (!m) return null;\n const h = Number(m[1]);\n const min = Number(m[2]);\n if (h > 24 || min > 59 || (h === 24 && min > 0)) return null;\n return h * 60 + min;\n}\n\n/** At most this many ranges a day, as the database allows. */\nexport const MAX_RANGES_PER_DAY = 4;\n\n/**\n * Working hours as they may safely be read: only mon..sun, each an array of `[\"HH:MM\", \"HH:MM\"]`\n * string pairs (at most four a day); every other day, range or value is dropped. Null when\n * nothing is left. Hours come from other people, so they are never trusted as typed.\n */\nexport function normalizeHours(raw: unknown): WorkingHours | null {\n if (!raw || typeof raw !== \"object\" || Array.isArray(raw)) return null;\n const src = raw as Record<string, unknown>;\n const out: WorkingHours = {};\n let any = false;\n for (const day of WEEKDAYS) {\n if (!Object.prototype.hasOwnProperty.call(src, day)) continue;\n const ranges = src[day];\n if (!Array.isArray(ranges)) continue;\n const kept: [string, string][] = [];\n for (const r of ranges) {\n if (!Array.isArray(r) || r.length !== 2) continue;\n const [from, to] = r as unknown[];\n if (typeof from !== \"string\" || typeof to !== \"string\" || minutesOf(from) == null || minutesOf(to) == null) continue;\n kept.push([from, to]);\n if (kept.length >= MAX_RANGES_PER_DAY) break;\n }\n if (kept.length) {\n out[day] = kept;\n any = true;\n }\n }\n return any ? out : null;\n}\n\n/** The weekday and minute of the day at `now` in `timeZone` (the device's zone when null or unknown). */\nexport function localClock(now: Date, timeZone?: string | null): { day: Weekday; minutes: number } {\n let parts: Intl.DateTimeFormatPart[];\n try {\n parts = new Intl.DateTimeFormat(\"en-US\", {\n timeZone: timeZone || undefined,\n weekday: \"short\",\n hour: \"2-digit\",\n minute: \"2-digit\",\n hourCycle: \"h23\",\n }).formatToParts(now);\n } catch {\n parts = new Intl.DateTimeFormat(\"en-US\", { weekday: \"short\", hour: \"2-digit\", minute: \"2-digit\", hourCycle: \"h23\" }).formatToParts(now);\n }\n const get = (t: string) => parts.find((p) => p.type === t)?.value ?? \"\";\n const day = get(\"weekday\").slice(0, 3).toLowerCase() as Weekday;\n const hour = Number(get(\"hour\")) % 24;\n return { day: WEEKDAYS.includes(day) ? day : \"mon\", minutes: hour * 60 + Number(get(\"minute\")) };\n}\n\n/**\n * Is `now` inside these working hours, read in `timeZone`? No hours set means always.\n * A range whose end is at or before its start runs overnight into the next day\n * (`fri: [[\"22:00\",\"02:00\"]]` covers Friday 22:00 to Saturday 02:00).\n */\nexport function isWithinWorkingHours(raw: WorkingHours | null | undefined, timeZone: string | null | undefined, now: Date = new Date()): boolean {\n const hours = normalizeHours(raw);\n if (!hours) return true;\n const { day, minutes } = localClock(now, timeZone);\n const yesterday = WEEKDAYS[(WEEKDAYS.indexOf(day) + 6) % 7]!;\n for (const [from, to] of hours[day] ?? []) {\n const a = minutesOf(from);\n const b = minutesOf(to);\n if (a == null || b == null) continue;\n if (b > a ? minutes >= a && minutes < b : minutes >= a) return true;\n }\n for (const [from, to] of hours[yesterday] ?? []) {\n const a = minutesOf(from);\n const b = minutesOf(to);\n if (a == null || b == null) continue;\n if (b <= a && minutes < b) return true;\n }\n return false;\n}\n\n/** Away now: an away message with no end date, or an end date still ahead. */\nexport function isAway(a: Pick<ChatAvailability, \"awayMessage\" | \"awayUntil\"> | null | undefined, now: Date = new Date()): boolean {\n if (!a) return false;\n if (a.awayUntil) return Date.parse(a.awayUntil) > now.getTime();\n return Boolean(a.awayMessage?.trim());\n}\n\n/** \"9\", \"9:30\", \"5\" (12-hour, no am/pm: the range reads \"9–5\"). */\nfunction clock(hhmm: string): string {\n const total = minutesOf(hhmm) ?? 0;\n const h = Math.floor(total / 60) % 24;\n const m = total % 60;\n const h12 = h % 12 === 0 ? 12 : h % 12;\n return m ? `${h12}:${String(m).padStart(2, \"0\")}` : String(h12);\n}\n\n/** \"Mon–Fri, 9–5\", \"Mon–Thu, 9–5; Fri, 9–1\". Days with the same hours are grouped. Empty when none. */\nexport function hoursSummary(raw: WorkingHours | null | undefined): string {\n const hours = normalizeHours(raw);\n if (!hours) return \"\";\n const key = (d: Weekday) => (hours[d] ?? []).map(([a, b]) => `${clock(a)}–${clock(b)}`).join(\", \");\n const runs: { from: Weekday; to: Weekday; text: string }[] = [];\n for (const d of WEEKDAYS) {\n const text = key(d);\n if (!text) continue;\n const last = runs[runs.length - 1];\n if (last && last.text === text && WEEKDAYS.indexOf(last.to) === WEEKDAYS.indexOf(d) - 1) last.to = d;\n else runs.push({ from: d, to: d, text });\n }\n return runs\n .map((r) => {\n const days = r.from === r.to ? SHORT[r.from] : WEEKDAYS.indexOf(r.to) - WEEKDAYS.indexOf(r.from) === 1 ? `${SHORT[r.from]}, ${SHORT[r.to]}` : `${SHORT[r.from]}–${SHORT[r.to]}`;\n return `${days}, ${r.text}`;\n })\n .join(\"; \");\n}\n\n/** \"Eastern\" for America/New_York (the zone's generic name, less \" Time\"); the city when Intl has no name. */\nexport function timeZoneLabel(timeZone: string | null | undefined, locale = \"en-US\"): string {\n if (!timeZone) return \"\";\n try {\n const name = new Intl.DateTimeFormat(locale, { timeZone, timeZoneName: \"longGeneric\" })\n .formatToParts(new Date())\n .find((p) => p.type === \"timeZoneName\")?.value;\n if (name && !/^GMT|^UTC/.test(name)) return name.replace(/ Time$/, \"\");\n } catch {\n // an unknown zone: fall through\n }\n return (timeZone.split(\"/\").pop() ?? timeZone).replace(/_/g, \" \");\n}\n\n/** \"3:00 PM\" today, \"Monday\" within the week, else \"12 Mar\". */\nexport function untilLabel(iso: string, now: Date = new Date(), locale?: string, timeZone?: string | null): string {\n const d = new Date(iso);\n if (!Number.isFinite(d.getTime())) return \"later\";\n try {\n return untilIn(d, now, locale, timeZone || undefined);\n } catch {\n // An unknown zone or locale: the device's.\n return untilIn(d, now, undefined, undefined);\n }\n}\n\nfunction untilIn(d: Date, now: Date, locale: string | undefined, tz: string | undefined): string {\n const dayKey = (x: Date) => new Intl.DateTimeFormat(\"en-CA\", { timeZone: tz, year: \"numeric\", month: \"2-digit\", day: \"2-digit\" }).format(x);\n if (dayKey(d) === dayKey(now)) return d.toLocaleTimeString(locale, { hour: \"numeric\", minute: \"2-digit\", timeZone: tz });\n const days = (d.getTime() - now.getTime()) / 86_400_000;\n if (days < 7) return d.toLocaleDateString(locale, { weekday: \"long\", timeZone: tz });\n return d.toLocaleDateString(locale, { day: \"numeric\", month: \"short\", timeZone: tz });\n}\n\nexport interface AwayLineOptions {\n now?: Date;\n locale?: string;\n /** Show the hours line even inside working hours (a profile, not a banner). */\n always?: boolean;\n}\n\n/**\n * The line a thread shows about someone, or null when they are around:\n * - away with a date: \"Away until Monday: Back Monday\" (the away message after the colon);\n * - away with no date: \"Mark is away: On a retreat\";\n * - outside working hours: \"Mark replies Mon–Fri, 9–5 (Eastern)\".\n */\nexport function awayLine(a: ChatAvailability | null | undefined, name: string, opts: AwayLineOptions = {}): string | null {\n if (!a) return null;\n const now = opts.now ?? new Date();\n const message = a.awayMessage?.trim();\n if (isAway(a, now)) {\n if (a.awayUntil) return `Away until ${untilLabel(a.awayUntil, now, opts.locale, a.timeZone)}${message ? `: ${message}` : \"\"}`;\n return `${name} is away${message ? `: ${message}` : \"\"}`;\n }\n const summary = hoursSummary(a.hours);\n if (!summary) return null;\n if (!opts.always && isWithinWorkingHours(a.hours, a.timeZone, now)) return null;\n const zone = timeZoneLabel(a.timeZone, opts.locale);\n return `${name} replies ${summary}${zone ? ` (${zone})` : \"\"}`;\n}\n\n/** \"Sam\", \"Sam and Lee\", \"Sam, Lee and 3 others\": at most two names. */\nexport function namesLine(names: string[]): string {\n if (names.length <= 1) return names[0] ?? \"\";\n if (names.length === 2) return `${names[0]} and ${names[1]}`;\n const rest = names.length - 2;\n return `${names[0]}, ${names[1]} and ${rest === 1 ? \"1 other\" : `${rest} others`}`;\n}\n\n/**\n * The line for several people (a group's members): one person's full `awayLine`, or for more\n * \"Sam and Lee are away\", \"Sam, Lee and 2 others are outside their hours\", or \"… are away or\n * outside their hours\" when it's a mix. Null when everyone is around.\n */\nexport function awaySummary(people: { availability: ChatAvailability | null | undefined; name: string }[], opts: AwayLineOptions = {}): string | null {\n const now = opts.now ?? new Date();\n const out = people.filter((p) => awayLine(p.availability, p.name, { ...opts, now, always: false }) !== null);\n if (out.length === 0) return null;\n if (out.length === 1) return awayLine(out[0]!.availability, out[0]!.name, { ...opts, now, always: false });\n const away = out.filter((p) => isAway(p.availability, now)).length;\n const what = away === out.length ? \"away\" : away === 0 ? \"outside their hours\" : \"away or outside their hours\";\n return `${namesLine(out.map((p) => p.name))} are ${what}`;\n}\n","/**\n * @feat:chat\n * Media rules shared by every platform: which files may be sent (the same list\n * migration 0001 checks), what kind an upload becomes, the default limits, and\n * the waveform a voice note carries so its bubble draws before the audio loads.\n */\nimport type { AttachmentKind } from \"./types\";\n\n/**\n * A random UUID (v4): `crypto.randomUUID` where the platform has it (some React Native\n * runtimes don't), else `crypto.getRandomValues`, else Math.random as a last resort. Ids\n * only need to be unique per sender: the server checks (sender, client_id).\n */\nexport function randomId(): string {\n const c = (globalThis as { crypto?: { randomUUID?: () => string; getRandomValues?: (a: Uint8Array) => Uint8Array } }).crypto;\n if (c?.randomUUID) return c.randomUUID();\n const b = new Uint8Array(16);\n if (c?.getRandomValues) c.getRandomValues(b);\n else for (let i = 0; i < 16; i++) b[i] = Math.floor(Math.random() * 256);\n b[6] = (b[6]! & 0x0f) | 0x40;\n b[8] = (b[8]! & 0x3f) | 0x80;\n const h = [...b].map((x) => x.toString(16).padStart(2, \"0\")).join(\"\");\n return `${h.slice(0, 8)}-${h.slice(8, 12)}-${h.slice(12, 16)}-${h.slice(16, 20)}-${h.slice(20)}`;\n}\n\n/** Mirrors genesis_chat.allowed_extension. Never SVG or HTML, which a browser runs as a page. */\nexport const ALLOWED_EXTENSIONS = [\n \"png\", \"jpg\", \"jpeg\", \"webp\", \"gif\", \"heic\", \"heif\",\n \"m4a\", \"aac\", \"mp3\", \"ogg\", \"oga\", \"opus\", \"weba\", \"webm\",\n \"mp4\", \"mov\", \"pdf\", \"txt\", \"csv\", \"docx\", \"xlsx\", \"pptx\",\n] as const;\n\nconst MIME_EXTENSIONS: Record<string, string> = {\n \"image/png\": \"png\",\n \"image/jpeg\": \"jpg\",\n \"image/webp\": \"webp\",\n \"image/gif\": \"gif\",\n \"image/heic\": \"heic\",\n \"image/heif\": \"heif\",\n \"audio/mp4\": \"m4a\",\n \"audio/x-m4a\": \"m4a\",\n \"audio/aac\": \"aac\",\n \"audio/mpeg\": \"mp3\",\n \"audio/ogg\": \"ogg\",\n \"audio/webm\": \"webm\",\n \"video/mp4\": \"mp4\",\n \"video/quicktime\": \"mov\",\n \"video/webm\": \"webm\",\n \"application/pdf\": \"pdf\",\n \"text/plain\": \"txt\",\n \"text/csv\": \"csv\",\n \"application/vnd.openxmlformats-officedocument.wordprocessingml.document\": \"docx\",\n \"application/vnd.openxmlformats-officedocument.spreadsheetml.sheet\": \"xlsx\",\n \"application/vnd.openxmlformats-officedocument.presentationml.presentation\": \"pptx\",\n};\n\n/** The extension a file is stored under: from its content type, else its name. */\nexport function fileExtension(name: string, contentType?: string): string {\n const base = (contentType ?? \"\").split(\";\")[0]!.trim().toLowerCase();\n const fromType = MIME_EXTENSIONS[base];\n if (fromType) return fromType;\n const dot = name.lastIndexOf(\".\");\n return dot >= 0 ? name.slice(dot + 1).toLowerCase() : \"\";\n}\n\nexport function isAllowedExtension(ext: string): boolean {\n return (ALLOWED_EXTENSIONS as readonly string[]).includes(ext.toLowerCase());\n}\n\n/** What an upload becomes in a message. */\nexport function attachmentKindFor(contentType: string, opts: { recordedVoice?: boolean } = {}): AttachmentKind {\n const t = contentType.toLowerCase();\n if (opts.recordedVoice || t.startsWith(\"audio/\")) return \"voice\";\n if (t.startsWith(\"image/\")) return \"image\";\n if (t.startsWith(\"video/\")) return \"video\";\n return \"file\";\n}\n\n/** Default limits (decision 5): an app may lower them, not raise them past the bucket's 100 MB. */\nexport const CHAT_LIMITS = {\n /** Voice notes and videos, for members. */\n recordingMs: 5 * 60_000,\n /** For owners, admins and observers (coaches, staff). */\n staffRecordingMs: 10 * 60_000,\n fileBytes: 50 * 1024 * 1024,\n videoBytes: 100 * 1024 * 1024,\n attachmentsPerMessage: 20,\n bodyChars: 10_000,\n} as const;\n\n/** The longest recording this role may make. */\nexport function recordingLimitMs(role: string | null | undefined): number {\n return role === \"owner\" || role === \"admin\" || role === \"observer\" ? CHAT_LIMITS.staffRecordingMs : CHAT_LIMITS.recordingMs;\n}\n\n/**\n * Peaks for a waveform: `count` values 0–100 from raw samples (-1…1), the loudest\n * in each bucket, scaled so the loudest bucket is 100. Quiet recordings still show\n * a shape; silence shows a flat line.\n */\nexport function waveformPeaks(samples: ArrayLike<number>, count = 48): number[] {\n const n = Math.max(1, Math.min(128, Math.floor(count)));\n if (samples.length === 0) return new Array(n).fill(0);\n const size = samples.length / n;\n const raw: number[] = [];\n for (let i = 0; i < n; i++) {\n let peak = 0;\n const end = Math.min(samples.length, Math.floor((i + 1) * size));\n for (let j = Math.floor(i * size); j < end; j++) {\n const v = Math.abs(samples[j]!);\n if (v > peak) peak = v;\n }\n raw.push(peak);\n }\n const max = Math.max(...raw);\n if (max < 0.01) return raw.map(() => 0);\n return raw.map((v) => Math.round((v / max) * 100));\n}\n\n/**\n * Peaks from a level meter read while recording (expo-audio's `metering`, in dB,\n * about -160…0): resampled to `count` values 0–100.\n */\nexport function waveformFromMetering(levelsDb: number[], count = 48): number[] {\n const linear = levelsDb.map((db) => (Number.isFinite(db) ? Math.pow(10, Math.max(-60, Math.min(0, db)) / 20) : 0));\n return waveformPeaks(linear, count);\n}\n\n/** 0:42, 1:05:09. */\nexport function formatDuration(ms: number | null | undefined): string {\n const total = Math.max(0, Math.round((ms ?? 0) / 1000));\n const h = Math.floor(total / 3600);\n const m = Math.floor((total % 3600) / 60);\n const s = total % 60;\n return h > 0 ? `${h}:${String(m).padStart(2, \"0\")}:${String(s).padStart(2, \"0\")}` : `${m}:${String(s).padStart(2, \"0\")}`;\n}\n\n/** Playback speeds offered for voice and video (free for everyone, decision 5). */\nexport const PLAYBACK_RATES = [1, 1.5, 2] as const;\n\nexport function nextPlaybackRate(rate: number): number {\n const i = PLAYBACK_RATES.indexOf(rate as (typeof PLAYBACK_RATES)[number]);\n return PLAYBACK_RATES[(i + 1) % PLAYBACK_RATES.length]!;\n}\n","/**\n * @feat:chat\n * The chat client: the app's own Supabase (migrations 0001 and 0002, the genesis_chat\n * schema) under row level security. Reads are table selects; every write is a\n * genesis_chat function. Live updates are hints on private Realtime topics,\n * after which the client fetches again, so nothing is trusted from the wire.\n */\nimport type { RealtimeChannel, SupabaseClient } from \"@supabase/supabase-js\";\nimport type {\n AttachmentInput,\n AvailabilityInput,\n ChatActivity,\n ChatAttachment,\n ChatAvailability,\n ChatLinkPreview,\n ChatSearchHit,\n ChatTopic,\n ScheduleInput,\n ScheduledMessage,\n TopicInput,\n ChatConversation,\n ChatInboxItem,\n ChatKindRules,\n ChatMember,\n ChatMessage,\n ChatRole,\n ChatSignal,\n SendInput,\n} from \"./types\";\nimport { normalizeHours } from \"./availability\";\nimport { fileExtension, isAllowedExtension, randomId } from \"./media\";\nimport { sendBytes, type UploadProgress } from \"./transfer\";\n\n/** Any supabase-js client; the app's generated Database types don't need to list genesis_chat. */\n// eslint-disable-next-line @typescript-eslint/no-explicit-any\nexport type ChatSupabase = SupabaseClient<any, any, any>;\n\nexport const CHAT_SCHEMA = \"genesis_chat\";\nexport const CHAT_BUCKET = \"genesis-chat\";\n\nexport const conversationTopic = (conversationId: string) => `genesis-chat:conv:${conversationId}`;\nexport const userTopic = (userId: string) => `genesis-chat:user:${userId}`;\n\n/** A refusal from the database, with its SQLSTATE (`42501` not allowed, `22023` bad input, `GS429` too many). */\nexport class ChatError extends Error {\n constructor(\n message: string,\n readonly code: string | undefined,\n readonly operation: string,\n ) {\n super(message);\n this.name = \"ChatError\";\n }\n get forbidden(): boolean {\n return this.code === \"42501\";\n }\n get rateLimited(): boolean {\n return this.code === \"GS429\";\n }\n /** Worth trying again: the network, not the database, said no. */\n get transient(): boolean {\n return this.code === undefined || this.code === \"\" || /^(08|57|53)/.test(this.code) || this.code === \"PGRST000\";\n }\n /**\n * Sending it again won't help: the database refused what was sent (not allowed 42501, bad\n * data 22xxx such as 22023, a conflict 23xxx such as 23505, another refusal 42xxx, or a\n * function's own `raise` P0001). A person may still retry by hand; nothing retries it alone.\n */\n get permanent(): boolean {\n return !this.transient && !this.rateLimited && /^(22|23|42|P0)/.test(this.code ?? \"\");\n }\n}\n\nfunction must<T>(operation: string, result: { data: T | null; error: { message: string; code?: string } | null }): T {\n if (result.error) throw new ChatError(result.error.message, result.error.code, operation);\n return result.data as T;\n}\n\ntype Row = Record<string, unknown>;\nconst str = (v: unknown) => (v == null ? null : String(v));\nconst num = (v: unknown) => (v == null ? null : Number(v));\n\nexport function toAttachment(r: Row): ChatAttachment {\n return {\n id: String(r.id),\n messageId: String(r.message_id),\n position: Number(r.position ?? 0),\n kind: String(r.kind),\n ref: (r.ref as Record<string, unknown>) ?? {},\n provider: (r.provider as ChatAttachment[\"provider\"]) ?? \"storage\",\n path: str(r.path),\n providerId: str(r.provider_id),\n playbackId: str(r.playback_id),\n url: str(r.url),\n status: (r.status as ChatAttachment[\"status\"]) ?? \"ready\",\n name: str(r.name),\n mime: str(r.mime),\n bytes: num(r.bytes),\n width: num(r.width),\n height: num(r.height),\n durationMs: num(r.duration_ms),\n waveform: (r.waveform as number[] | null) ?? null,\n posterPath: str(r.poster_path),\n thumbhash: str(r.thumbhash),\n transcript: str(r.transcript),\n transcriptStatus: (r.transcript_status as ChatAttachment[\"transcriptStatus\"]) ?? null,\n };\n}\n\nexport function toMessage(r: Row): ChatMessage {\n const attachments = ((r.attachments as Row[] | undefined) ?? []).map(toAttachment).sort((a, b) => a.position - b.position);\n const reactions = ((r.reactions as Row[] | undefined) ?? []).map((x) => ({ emoji: String(x.emoji), userId: String(x.user_id) }));\n return {\n id: String(r.id),\n conversationId: String(r.conversation_id),\n seq: Number(r.seq),\n clientId: String(r.client_id),\n senderId: str(r.sender_id),\n senderRole: r.sender_role as ChatMessage[\"senderRole\"],\n kind: (r.kind as ChatMessage[\"kind\"]) ?? \"text\",\n body: String(r.body ?? \"\"),\n mentions: (r.mentions as string[]) ?? [],\n replyToId: str(r.reply_to_id),\n threadRootId: str(r.thread_root_id),\n threadCount: Number(r.thread_count ?? 0),\n threadLastAt: str(r.thread_last_at),\n topic: str(r.topic),\n visibility: (r.visibility as ChatMessage[\"visibility\"]) ?? \"everyone\",\n silent: Boolean(r.silent),\n meta: (r.meta as Record<string, unknown>) ?? {},\n createdAt: String(r.created_at),\n editedAt: str(r.edited_at),\n deletedAt: str(r.deleted_at),\n moderation: r.moderation === \"hidden\" || r.moderation === \"flagged\" ? r.moderation : null,\n attachments,\n reactions,\n };\n}\n\nfunction toConversation(r: Row): ChatConversation {\n return {\n id: String(r.id),\n kind: String(r.kind),\n title: str(r.title),\n subjectType: str(r.subject_type),\n subjectId: str(r.subject_id),\n settings: (r.settings as ChatConversation[\"settings\"]) ?? {},\n status: (r.status as ChatConversation[\"status\"]) ?? null,\n assigneeId: str(r.assignee_id),\n lastSeq: Number(r.last_seq ?? 0),\n lastMessageAt: str(r.last_message_at),\n createdAt: String(r.created_at),\n archivedAt: str(r.archived_at),\n };\n}\n\nfunction toInboxItem(r: Row): ChatInboxItem {\n const last = r.last_message as Row | null;\n return {\n conversationId: String(r.conversation_id),\n kind: String(r.kind),\n title: str(r.title),\n subjectType: str(r.subject_type),\n subjectId: str(r.subject_id),\n status: (r.status as ChatInboxItem[\"status\"]) ?? null,\n role: r.role as ChatRole,\n otherUserId: str(r.other_user_id),\n memberCount: Number(r.member_count ?? 0),\n lastSeq: Number(r.last_seq ?? 0),\n lastReadSeq: Number(r.last_read_seq ?? 0),\n unread: Number(r.unread ?? 0),\n mentioned: Boolean(r.mentioned),\n muted: Boolean(r.muted),\n pinned: Boolean(r.pinned),\n archived: Boolean(r.archived),\n lastMessageAt: str(r.last_message_at),\n lastMessage: last\n ? {\n id: String(last.id),\n seq: Number(last.seq),\n senderId: str(last.sender_id),\n kind: (last.kind as \"text\" | \"system\") ?? \"text\",\n body: String(last.body ?? \"\"),\n deleted: Boolean(last.deleted),\n createdAt: String(last.created_at),\n attachment: (last.attachment as string | null) ?? null,\n }\n : null,\n };\n}\n\nexport function toTopic(r: Row): ChatTopic {\n return {\n conversationId: String(r.conversation_id),\n topic: String(r.topic),\n title: String(r.title ?? r.topic),\n position: Number(r.position ?? 0),\n closedAt: str(r.closed_at),\n createdBy: str(r.created_by),\n createdAt: String(r.created_at),\n };\n}\n\nfunction fromAttachmentArg(a: Row): AttachmentInput {\n const out: AttachmentInput = { kind: String(a.kind ?? \"file\") };\n const map: [keyof AttachmentInput, string][] = [\n [\"path\", \"path\"], [\"provider\", \"provider\"], [\"providerId\", \"provider_id\"], [\"url\", \"url\"], [\"ref\", \"ref\"],\n [\"status\", \"status\"], [\"name\", \"name\"], [\"mime\", \"mime\"], [\"bytes\", \"bytes\"], [\"width\", \"width\"],\n [\"height\", \"height\"], [\"durationMs\", \"duration_ms\"], [\"waveform\", \"waveform\"], [\"posterPath\", \"poster_path\"], [\"thumbhash\", \"thumbhash\"],\n ];\n for (const [k, col] of map) if (a[col] != null) (out as unknown as Record<string, unknown>)[k] = a[col];\n return out;\n}\n\nexport function toScheduled(r: Row): ScheduledMessage {\n const sentMessageId = str(r.sent_message_id);\n const cancelledAt = str(r.cancelled_at);\n const error = str(r.error);\n return {\n id: String(r.id),\n conversationId: String(r.conversation_id),\n clientId: String(r.client_id),\n body: String(r.body ?? \"\"),\n attachments: ((r.attachments as Row[] | null) ?? []).map(fromAttachmentArg),\n replyToId: str(r.reply_to_id),\n topic: str(r.topic),\n mentions: (r.mentions as string[] | null) ?? [],\n silent: Boolean(r.silent),\n sendAt: String(r.send_at),\n sentMessageId,\n cancelledAt,\n error,\n createdAt: String(r.created_at),\n status: sentMessageId ? \"sent\" : cancelledAt ? \"cancelled\" : error ? \"failed\" : \"scheduled\",\n };\n}\n\nexport function toAvailability(r: Row): ChatAvailability {\n return {\n userId: String(r.user_id),\n awayMessage: str(r.away_message),\n awayUntil: str(r.away_until),\n timeZone: str(r.time_zone),\n // Someone else's hours: only well-formed days and ranges reach the renderers.\n hours: normalizeHours(r.hours),\n };\n}\n\nexport function toSearchHit(r: Row): ChatSearchHit {\n return {\n messageId: String(r.message_id),\n conversationId: String(r.conversation_id),\n seq: Number(r.seq),\n senderId: str(r.sender_id),\n createdAt: String(r.created_at),\n snippet: String(r.snippet ?? \"\"),\n inTranscript: Boolean(r.in_transcript),\n threadRootId: str(r.thread_root_id),\n };\n}\n\nexport function toLinkPreview(r: Row): ChatLinkPreview {\n const image = str(r.image_url);\n return {\n url: String(r.url),\n status: (r.status as ChatLinkPreview[\"status\"]) ?? \"ok\",\n title: str(r.title),\n description: str(r.description),\n imageUrl: image && /^https:\\/\\//i.test(image) ? image : null,\n siteName: str(r.site_name),\n fetchedAt: String(r.fetched_at),\n };\n}\n\nconst MESSAGE_COLUMNS = \"*, attachments(*), reactions(emoji, user_id)\";\n\nexport interface MessagePage {\n messages: ChatMessage[];\n /** More, older messages exist before the first one. */\n hasEarlier: boolean;\n}\n\nexport interface ChatClientOptions {\n /** Signed file links live this long (seconds). Default an hour. */\n signedUrlSeconds?: number;\n}\n\nexport interface Subscription {\n unsubscribe(): void;\n}\n\nexport interface ConversationHandlers {\n onSignal: (s: ChatSignal) => void;\n onActivity?: (a: ChatActivity) => void;\n /** Every join, the first included: catch up on what arrived before the channel was live. */\n onJoin?: () => void;\n /** Re-joins only. Prefer `onJoin`. */\n onReconnect?: () => void;\n}\n\nexport interface ConversationChannel extends Subscription {\n /** Tell the others you are typing or recording (throttle to once every 3 seconds). */\n signal(activity: ChatActivity[\"activity\"]): void;\n}\n\nexport function createChatClient(supabase: ChatSupabase, options: ChatClientOptions = {}) {\n const db = () => supabase.schema(CHAT_SCHEMA);\n const signedUrlSeconds = options.signedUrlSeconds ?? 3600;\n const signedCache = new Map<string, { url: string; until: number }>();\n\n /**\n * Private Realtime channels authorise with the signed-in user's token; set it before\n * joining (supabase-js doesn't for a channel joined before the session loads), or the\n * join is refused and no hint ever arrives. A refusal to set it still joins and retries.\n */\n const unsubscribed = new WeakSet<RealtimeChannel>();\n function join(channel: RealtimeChannel, onStatus?: (status: string) => void) {\n const realtime = (supabase as unknown as { realtime?: { setAuth?: (token?: string | null) => Promise<void> | void } }).realtime;\n void Promise.resolve(realtime?.setAuth?.())\n .catch(() => undefined)\n .then(() => {\n if (!unsubscribed.has(channel)) channel.subscribe(onStatus);\n });\n return () => {\n unsubscribed.add(channel);\n void supabase.removeChannel(channel);\n };\n }\n\n async function rpc<T>(fn: string, args: Record<string, unknown>): Promise<T> {\n return must<T>(fn, await db().rpc(fn, args));\n }\n\n return {\n supabase,\n\n /** Your conversations: one `kind`, several `kinds`, or all but `excludeKinds` (e.g. Messages without `support`). */\n async inbox(\n opts: { kind?: string; kinds?: string[]; excludeKinds?: string[]; archived?: boolean; limit?: number; before?: string } = {},\n ): Promise<ChatInboxItem[]> {\n const rows = await rpc<Row[]>(\"inbox\", {\n p_kind: opts.kind ?? null,\n p_archived: opts.archived ?? false,\n p_limit: opts.limit ?? 50,\n p_before: opts.before ?? null,\n ...(opts.kinds?.length ? { p_kinds: opts.kinds } : {}),\n ...(opts.excludeKinds?.length ? { p_exclude_kinds: opts.excludeKinds } : {}),\n });\n return rows.map(toInboxItem);\n },\n\n async observed(kind: string, opts: { status?: string; limit?: number; before?: string } = {}) {\n return rpc<Row[]>(\"observed\", { p_kind: kind, p_status: opts.status ?? null, p_limit: opts.limit ?? 50, p_before: opts.before ?? null });\n },\n\n /** Unread across your conversations (for a badge), leaving out `excludeKinds` to match an inbox that does. */\n async unreadTotal(opts: { excludeKinds?: string[] } = {}): Promise<number> {\n return rpc<number>(\"unread_total\", opts.excludeKinds?.length ? { p_exclude_kinds: opts.excludeKinds } : {});\n },\n\n async conversation(id: string): Promise<ChatConversation | null> {\n const row = must(\"conversation\", await db().from(\"conversations\").select(\"*\").eq(\"id\", id).maybeSingle());\n return row ? toConversation(row as Row) : null;\n },\n\n /** A kind's rules (how many members, who posts, threads), for what the UI offers. */\n async kind(kind: string): Promise<ChatKindRules | null> {\n const row = must(\"kind\", await db().from(\"kinds\").select(\"kind, label, max_members, who_posts, membership, threads, observers_post\").eq(\"kind\", kind).maybeSingle()) as Row | null;\n return row\n ? {\n kind: String(row.kind),\n label: String(row.label),\n maxMembers: row.max_members == null ? null : Number(row.max_members),\n whoPosts: row.who_posts as ChatKindRules[\"whoPosts\"],\n membership: row.membership as ChatKindRules[\"membership\"],\n threads: Boolean(row.threads),\n observersPost: Boolean(row.observers_post),\n }\n : null;\n },\n\n async myRole(id: string): Promise<ChatRole | null> {\n return rpc<ChatRole | null>(\"my_role\", { p_conversation_id: id });\n },\n\n async canPost(id: string): Promise<boolean> {\n return rpc<boolean>(\"can_post\", { p_conversation_id: id });\n },\n\n async members(id: string): Promise<ChatMember[]> {\n const rows = must<Row[]>(\n \"members\",\n await db().from(\"members\").select(\"*\").eq(\"conversation_id\", id).is(\"left_at\", null).neq(\"role\", \"observer\"),\n );\n return rows.map((r) => ({\n conversationId: String(r.conversation_id),\n userId: String(r.user_id),\n role: r.role as ChatRole,\n lastReadSeq: Number(r.last_read_seq ?? 0),\n joinedAt: String(r.joined_at),\n }));\n },\n\n /**\n * A page of messages, oldest first: the latest `limit` (default 50), or those\n * before `beforeSeq`, or (to catch up after a reconnect) those after `afterSeq`.\n * A thread's replies with `threadRootId`; one topic's with `topic`.\n */\n async messages(\n conversationId: string,\n opts: { beforeSeq?: number; afterSeq?: number; limit?: number; threadRootId?: string; topic?: string } = {},\n ): Promise<MessagePage> {\n const limit = Math.min(Math.max(opts.limit ?? 50, 1), 200);\n let q = db().from(\"messages\").select(MESSAGE_COLUMNS).eq(\"conversation_id\", conversationId);\n q = opts.threadRootId ? q.eq(\"thread_root_id\", opts.threadRootId) : q.is(\"thread_root_id\", null);\n if (opts.topic) q = q.eq(\"topic\", opts.topic);\n if (opts.afterSeq != null) {\n const rows = must<Row[]>(\"messages\", await q.gt(\"seq\", opts.afterSeq).order(\"seq\", { ascending: true }).limit(limit));\n return { messages: rows.map(toMessage), hasEarlier: false };\n }\n if (opts.beforeSeq != null) q = q.lt(\"seq\", opts.beforeSeq);\n const rows = must<Row[]>(\"messages\", await q.order(\"seq\", { ascending: false }).limit(limit + 1));\n const hasEarlier = rows.length > limit;\n return { messages: rows.slice(0, limit).map(toMessage).reverse(), hasEarlier };\n },\n\n async message(id: string): Promise<ChatMessage | null> {\n const row = must(\"message\", await db().from(\"messages\").select(MESSAGE_COLUMNS).eq(\"id\", id).maybeSingle());\n return row ? toMessage(row as Row) : null;\n },\n\n async startConversation(kind: string, memberIds: string[], opts: { title?: string; subjectType?: string; subjectId?: string } = {}) {\n return rpc<string>(\"start_conversation\", {\n p_kind: kind,\n p_member_ids: memberIds,\n p_title: opts.title ?? null,\n p_subject_type: opts.subjectType ?? null,\n p_subject_id: opts.subjectId ?? null,\n });\n },\n\n /** Sends a message. Safe to retry with the same clientId. */\n async send(input: SendInput): Promise<ChatMessage> {\n const row = await rpc<Row>(\"send_message\", {\n p_conversation_id: input.conversationId,\n p_client_id: input.clientId,\n p_body: input.body ?? \"\",\n p_attachments: (input.attachments ?? []).map(attachmentArg),\n p_reply_to_id: input.replyToId ?? null,\n p_thread_root_id: input.threadRootId ?? null,\n p_topic: input.topic ?? null,\n p_mentions: input.mentions ?? [],\n p_visibility: input.visibility ?? \"everyone\",\n p_silent: input.silent ?? false,\n });\n // The function returns the message row; fetch it again for its attachments.\n return (await this.message(String(row.id))) ?? toMessage(row);\n },\n\n async edit(messageId: string, body: string, mentions: string[] = []) {\n return toMessage(await rpc<Row>(\"edit_message\", { p_message_id: messageId, p_body: body, p_mentions: mentions }));\n },\n\n async remove(messageId: string) {\n await rpc(\"delete_message\", { p_message_id: messageId });\n },\n\n async react(messageId: string, emoji: string, on = true) {\n await rpc(\"react\", { p_message_id: messageId, p_emoji: emoji, p_on: on });\n },\n\n async markRead(conversationId: string, seq?: number) {\n return rpc<number>(\"mark_read\", { p_conversation_id: conversationId, p_seq: seq ?? null });\n },\n\n async setMySettings(\n conversationId: string,\n s: { mutedUntil?: string | null; notify?: \"all\" | \"mentions\" | \"none\"; archived?: boolean; pinned?: boolean },\n ) {\n await rpc(\"set_my_settings\", {\n p_conversation_id: conversationId,\n p_muted_until: s.mutedUntil ?? null,\n p_notify: s.notify ?? null,\n p_archived: s.archived ?? null,\n p_pinned: s.pinned ?? null,\n p_clear_mute: s.mutedUntil === null,\n });\n },\n\n async updateConversation(conversationId: string, s: { title?: string; settings?: Record<string, unknown> }) {\n await rpc(\"update_conversation\", { p_conversation_id: conversationId, p_title: s.title ?? null, p_settings: s.settings ?? null });\n },\n\n async addMembers(conversationId: string, userIds: string[]) {\n await rpc(\"add_members\", { p_conversation_id: conversationId, p_user_ids: userIds });\n },\n\n async removeMember(conversationId: string, userId?: string) {\n await rpc(\"remove_member\", { p_conversation_id: conversationId, p_user_id: userId ?? null });\n },\n\n async setStatus(conversationId: string, status: ChatConversation[\"status\"], assigneeId?: string | null) {\n await rpc(\"set_status\", { p_conversation_id: conversationId, p_status: status, p_assignee_id: assigneeId ?? null });\n },\n\n async pin(messageId: string, on = true) {\n await rpc(\"pin_message\", { p_message_id: messageId, p_on: on });\n },\n\n async pins(conversationId: string): Promise<string[]> {\n const rows = must<Row[]>(\"pins\", await db().from(\"pins\").select(\"message_id\").eq(\"conversation_id\", conversationId));\n return rows.map((r) => String(r.message_id));\n },\n\n async save(messageId: string, on = true, note?: string) {\n await rpc(\"save_message\", { p_message_id: messageId, p_on: on, p_note: note ?? null });\n },\n\n async report(messageId: string, reason: \"spam\" | \"harassment\" | \"safety\" | \"other\", note?: string) {\n return rpc<string>(\"report_message\", { p_message_id: messageId, p_reason: reason, p_note: note ?? null });\n },\n\n // ── Migration 0002 ──────────────────────────────────────────────────────\n\n /** A conversation's topics, in order (position, then key). */\n async topics(conversationId: string): Promise<ChatTopic[]> {\n const rows = must<Row[]>(\n \"topics\",\n await db().from(\"topics\").select(\"*\").eq(\"conversation_id\", conversationId).order(\"position\", { ascending: true }).order(\"topic\", { ascending: true }),\n );\n return rows.map(toTopic);\n },\n\n /** Adds a topic, or renames, reorders, closes (`closed: true`) or reopens it. Owners and admins. */\n async setTopic(conversationId: string, topic: string, input: TopicInput = {}) {\n await rpc(\"set_topic\", {\n p_conversation_id: conversationId,\n p_topic: topic,\n p_title: input.title ?? null,\n p_position: input.position ?? null,\n p_closed: input.closed ?? null,\n });\n },\n\n /** Schedules a message (a minute to a year ahead) and returns its id. Safe to retry with the same clientId. */\n async schedule(input: ScheduleInput): Promise<string> {\n const sendAt = typeof input.sendAt === \"string\" ? input.sendAt : input.sendAt.toISOString();\n return rpc<string>(\"schedule_message\", {\n p_conversation_id: input.conversationId,\n p_client_id: input.clientId,\n p_send_at: sendAt,\n p_body: input.body ?? \"\",\n p_attachments: (input.attachments ?? []).map(attachmentArg),\n p_reply_to_id: input.replyToId ?? null,\n p_topic: input.topic ?? null,\n p_mentions: input.mentions ?? [],\n p_silent: input.silent ?? false,\n });\n },\n\n /** Your messages still waiting to go (and ones that failed when due), soonest first; in one conversation or all. */\n async scheduled(conversationId?: string): Promise<ScheduledMessage[]> {\n let q = db().from(\"scheduled\").select(\"*\").is(\"sent_message_id\", null).is(\"cancelled_at\", null);\n if (conversationId) q = q.eq(\"conversation_id\", conversationId);\n const rows = must<Row[]>(\"scheduled\", await q.order(\"send_at\", { ascending: true }).limit(200));\n return rows.map(toScheduled);\n },\n\n async cancelScheduled(id: string) {\n await rpc(\"cancel_scheduled\", { p_id: id });\n },\n\n /** The away message and hours of a conversation's members who set them (not observers). */\n async availabilityFor(conversationId: string): Promise<ChatAvailability[]> {\n const rows = await rpc<Row[]>(\"availability_for\", { p_conversation_id: conversationId });\n return (rows ?? []).map(toAvailability);\n },\n\n /** Your own away message and hours, or null when never set. */\n async myAvailability(): Promise<ChatAvailability | null> {\n const row = must(\"myAvailability\", await db().from(\"availability\").select(\"*\").maybeSingle()) as Row | null;\n return row ? toAvailability(row) : null;\n },\n\n /** Replaces your away message and hours (a field left out is cleared). */\n async setAvailability(input: AvailabilityInput) {\n const until = input.awayUntil ?? null;\n await rpc(\"set_availability\", {\n p_away_message: input.awayMessage ?? null,\n p_away_until: until,\n p_time_zone: input.timeZone ?? null,\n p_hours: input.hours ?? null,\n });\n },\n\n /**\n * Sends one text message into each of these conversations where you may post (a coach to\n * each client's direct conversation), so replies come back privately. Text only. Safe to\n * retry with the same clientId. Returns the ids of the messages sent.\n */\n async broadcast(conversationIds: string[], clientId: string, body: string, opts: { silent?: boolean } = {}): Promise<string[]> {\n const ids = await rpc<string[] | null>(\"broadcast_message\", {\n p_conversation_ids: conversationIds,\n p_client_id: clientId,\n p_body: body,\n p_attachments: [],\n p_silent: opts.silent ?? false,\n });\n return (ids ?? []).map(String);\n },\n\n /** Messages (and transcripts) you can read that match, newest first: in one conversation or all of yours. Two characters at least. */\n async search(query: string, conversationId?: string | null, opts: { limit?: number } = {}): Promise<ChatSearchHit[]> {\n if (query.trim().length < 2) return [];\n const rows = await rpc<Row[] | null>(\"search_messages\", {\n p_query: query,\n p_conversation_id: conversationId ?? null,\n p_limit: opts.limit ?? 30,\n });\n return (rows ?? []).map(toSearchHit);\n },\n\n /**\n * The previews the server has fetched for the links in these messages, by message id (only\n * `ok` ones; links not fetched yet are left out). Read through link_previews_for, one call\n * per message (up to 6 at once), which answers only for messages you can read: the preview\n * table itself is closed, since a URL can carry a secret.\n */\n async linkPreviewsFor(messageIds: string[]): Promise<Record<string, ChatLinkPreview[]>> {\n const ids = [...new Set(messageIds.filter(Boolean))].slice(0, 200);\n const out: Record<string, ChatLinkPreview[]> = {};\n let next = 0;\n const worker = async () => {\n while (next < ids.length) {\n const id = ids[next++]!;\n const rows = await rpc<Row[] | null>(\"link_previews_for\", { p_message_id: id });\n out[id] = (rows ?? []).map(toLinkPreview).filter((p) => p.status === \"ok\");\n }\n };\n await Promise.all(Array.from({ length: Math.min(6, ids.length) }, worker));\n return out;\n },\n\n /**\n * Uploads a file into the conversation's folder for the signed-in person and\n * returns its path, ready to attach. Resumable for big files is the native\n * renderer's job (Supabase TUS); this is the plain upload.\n *\n * With `onProgress(sent, total)`, where XMLHttpRequest exists (browsers, React Native), the\n * bytes go to a signed upload URL (`createSignedUploadUrl`, which checks the same storage\n * policy as a plain upload) through XMLHttpRequest, which reports progress; elsewhere the\n * plain upload runs and reports once, at the end. `signal` cancels it.\n */\n async upload(\n conversationId: string,\n userId: string,\n file: Blob | ArrayBuffer,\n opts: { name: string; contentType: string; id?: string; onProgress?: UploadProgress; signal?: AbortSignal },\n ) {\n const ext = fileExtension(opts.name, opts.contentType);\n if (!isAllowedExtension(ext)) throw new ChatError(`Files of type .${ext} can't be sent`, \"22023\", \"upload\");\n const id = opts.id ?? randomId();\n const path = `${conversationId}/${userId}/${id}.${ext}`;\n const bucket = supabase.storage.from(CHAT_BUCKET);\n if (opts.onProgress && typeof XMLHttpRequest !== \"undefined\" && typeof bucket.createSignedUploadUrl === \"function\") {\n const signed = must<{ signedUrl: string } | null>(\"upload\", await bucket.createSignedUploadUrl(path));\n if (!signed?.signedUrl) throw new ChatError(\"No upload URL came back\", undefined, \"upload\");\n await sendBytes(signed.signedUrl, file, {\n method: \"PUT\",\n headers: { \"content-type\": opts.contentType, \"x-upsert\": \"false\", \"cache-control\": \"max-age=3600\" },\n signal: opts.signal,\n onProgress: opts.onProgress,\n });\n return path;\n }\n must(\"upload\", await bucket.upload(path, file, { contentType: opts.contentType, upsert: false }));\n const total = typeof Blob !== \"undefined\" && file instanceof Blob ? file.size : (file as ArrayBuffer).byteLength;\n opts.onProgress?.(total, total);\n return path;\n },\n\n /** Signed links for files, cached until shortly before they expire. */\n async fileUrls(paths: string[]): Promise<Record<string, string>> {\n const now = Date.now();\n const out: Record<string, string> = {};\n const missing: string[] = [];\n for (const p of new Set(paths)) {\n const hit = signedCache.get(p);\n if (hit && hit.until > now) out[p] = hit.url;\n else missing.push(p);\n }\n if (missing.length) {\n const { data, error } = await supabase.storage.from(CHAT_BUCKET).createSignedUrls(missing, signedUrlSeconds);\n if (error) throw new ChatError(error.message, undefined, \"fileUrls\");\n for (const d of data ?? []) {\n if (d.signedUrl && d.path) {\n out[d.path] = d.signedUrl;\n signedCache.set(d.path, { url: d.signedUrl, until: now + (signedUrlSeconds - 60) * 1000 });\n }\n }\n }\n return out;\n },\n\n /** Your inbox's live hints (a new message anywhere you are, a membership change). */\n subscribeInbox(userId: string, onSignal: (s: ChatSignal) => void): Subscription {\n const channel = supabase\n .channel(userTopic(userId), { config: { private: true } })\n .on(\"broadcast\", { event: \"inbox\" }, ({ payload }) =>\n onSignal({ type: \"inbox\", conversationId: String(payload?.conversation_id), seq: payload?.seq }),\n );\n return { unsubscribe: join(channel) };\n },\n\n /**\n * A conversation's live hints, plus who is typing or recording. `onJoin` runs every\n * time the channel joins, the first time included: the moment to fetch what arrived\n * before the subscription was live. `onReconnect` (kept for older callers) runs on\n * re-joins only.\n */\n subscribeConversation(\n conversationId: string,\n me: string,\n handlers: ConversationHandlers,\n ): ConversationChannel {\n let joinedOnce = false;\n const channel: RealtimeChannel = supabase\n .channel(conversationTopic(conversationId), { config: { private: true, broadcast: { self: false } } })\n .on(\"broadcast\", { event: \"message\" }, ({ payload }) =>\n handlers.onSignal({ type: \"message\", conversationId, id: String(payload?.id), seq: payload?.seq, op: payload?.op ?? \"insert\" }),\n )\n .on(\"broadcast\", { event: \"reaction\" }, ({ payload }) =>\n handlers.onSignal({ type: \"reaction\", conversationId, messageId: String(payload?.message_id) }),\n )\n .on(\"broadcast\", { event: \"members\" }, () => handlers.onSignal({ type: \"members\", conversationId }))\n .on(\"broadcast\", { event: \"conversation\" }, () => handlers.onSignal({ type: \"conversation\", conversationId }))\n .on(\"broadcast\", { event: \"activity\" }, ({ payload }) => {\n if (payload?.userId && payload.userId !== me) handlers.onActivity?.({ userId: payload.userId, activity: payload.activity, at: Date.now() });\n });\n // Typing and recording are ephemeral: until the channel has joined, they're dropped\n // rather than sent (supabase-js would fall back to REST and warn).\n let joined = false;\n const leave = join(channel, (status) => {\n joined = status === \"SUBSCRIBED\";\n if (status === \"SUBSCRIBED\") {\n handlers.onJoin?.();\n if (joinedOnce) handlers.onReconnect?.();\n joinedOnce = true;\n }\n });\n return {\n signal(activity) {\n if (!joined) return;\n void channel.send({ type: \"broadcast\", event: \"activity\", payload: { userId: me, activity } });\n },\n unsubscribe: leave,\n };\n },\n };\n}\n\nexport type ChatClient = ReturnType<typeof createChatClient>;\n\nfunction attachmentArg(a: AttachmentInput) {\n return {\n kind: a.kind,\n path: a.path,\n provider: a.provider,\n provider_id: a.providerId,\n url: a.url,\n ref: a.ref,\n status: a.status,\n name: a.name,\n mime: a.mime,\n bytes: a.bytes,\n width: a.width,\n height: a.height,\n duration_ms: a.durationMs,\n waveform: a.waveform,\n poster_path: a.posterPath,\n thumbhash: a.thumbhash,\n };\n}\n","/**\n * @feat:chat\n * Sending bytes with progress, the same on every platform. fetch can't report how much of a\n * request body has gone, so where XMLHttpRequest exists (browsers, React Native) the body goes\n * through it and `onProgress(sent, total)` follows `upload.onprogress`; elsewhere it goes\n * through fetch and reports once, at the end.\n */\nimport { ChatError } from \"./client\";\n\nexport type UploadProgress = (sent: number, total: number) => void;\n\nexport interface SendBytesOptions {\n method?: \"PUT\" | \"POST\";\n headers?: Record<string, string>;\n signal?: AbortSignal;\n onProgress?: UploadProgress;\n /** For tests and runtimes without XMLHttpRequest. */\n fetch?: typeof fetch;\n}\n\nconst sizeOf = (body: Blob | ArrayBuffer) => (typeof Blob !== \"undefined\" && body instanceof Blob ? body.size : (body as ArrayBuffer).byteLength);\n\n/** A failed answer's code: a 5xx, 408 or no answer is worth retrying, 429 waits, any other 4xx is a refusal. */\nconst refusal = (status: number) => (status >= 500 || status === 0 || status === 408 ? undefined : status === 429 ? \"GS429\" : \"22023\");\n\nconst aborted = () => new ChatError(\"The upload was cancelled\", \"GSABORT\", \"upload\");\n\n/** Sends `body` to `url`; resolves with the response text, throws a ChatError on a non-2xx answer. */\nexport function sendBytes(url: string, body: Blob | ArrayBuffer, opts: SendBytesOptions = {}): Promise<string> {\n const method = opts.method ?? \"PUT\";\n const total = sizeOf(body);\n if (opts.signal?.aborted) return Promise.reject(aborted());\n if (typeof XMLHttpRequest === \"undefined\" || opts.fetch) {\n const f = opts.fetch ?? fetch;\n return f(url, { method, body, headers: opts.headers, signal: opts.signal }).then(async (res) => {\n if (!res.ok) throw new ChatError(`The upload failed (${res.status})`, refusal(res.status), \"upload\");\n opts.onProgress?.(total, total);\n return res.text().catch(() => \"\");\n });\n }\n return new Promise<string>((resolve, reject) => {\n const xhr = new XMLHttpRequest();\n const onAbort = () => xhr.abort();\n const done = () => opts.signal?.removeEventListener(\"abort\", onAbort);\n xhr.open(method, url);\n for (const [k, v] of Object.entries(opts.headers ?? {})) xhr.setRequestHeader(k, v);\n if (opts.onProgress && xhr.upload) {\n xhr.upload.onprogress = (e: ProgressEvent) => opts.onProgress?.(e.loaded, e.lengthComputable ? e.total : total);\n }\n xhr.onload = () => {\n done();\n if (xhr.status >= 200 && xhr.status < 300) {\n opts.onProgress?.(total, total);\n resolve(typeof xhr.responseText === \"string\" ? xhr.responseText : \"\");\n } else {\n reject(new ChatError(`The upload failed (${xhr.status})`, refusal(xhr.status), \"upload\"));\n }\n };\n // No answer at all: the connection, worth retrying.\n xhr.onerror = () => {\n done();\n reject(new ChatError(\"The upload didn't reach the server\", undefined, \"upload\"));\n };\n xhr.onabort = () => {\n done();\n reject(aborted());\n };\n opts.signal?.addEventListener(\"abort\", onAbort, { once: true });\n xhr.send(body);\n });\n}\n"]}
|
|
@@ -1,5 +1,5 @@
|
|
|
1
|
-
import { createOutbox, upsertMessages, currentActivity, mergeThread, throttle, firstUnreadId } from './chunk-
|
|
2
|
-
import { attachmentKindFor, randomId } from './chunk-
|
|
1
|
+
import { createOutbox, upsertMessages, currentActivity, mergeThread, throttle, firstUnreadId } from './chunk-A4YJX7KT.js';
|
|
2
|
+
import { sendBytes, attachmentKindFor, randomId } from './chunk-GBJ3YI4E.js';
|
|
3
3
|
import { useQueryClient, useQuery, useQueries } from '@tanstack/react-query';
|
|
4
4
|
import { createContext, useState, useRef, useMemo, useEffect, useContext, useCallback } from 'react';
|
|
5
5
|
import { jsx } from 'react/jsx-runtime';
|
|
@@ -34,6 +34,16 @@ function upsertInto(prev, incoming) {
|
|
|
34
34
|
function belongsTo(key, m) {
|
|
35
35
|
return key[0] === "genesis-chat" && key[1] === "conversation" && key[2] === m.conversationId && key[3] === "messages" && (key[4] ?? null) === (m.threadRootId ?? null) && (key[5] == null || key[5] === m.topic);
|
|
36
36
|
}
|
|
37
|
+
function hostedUploadFrom(videoUpload, options = {}) {
|
|
38
|
+
return async (conversationId, file, { signal, onProgress, resume }) => {
|
|
39
|
+
const minted = resume?.url ? { url: resume.url, providerId: resume.providerId, provider: resume.provider } : await videoUpload.create(conversationId);
|
|
40
|
+
const done = (async () => {
|
|
41
|
+
const body = typeof file.data === "string" ? await (options.readFile ?? (() => Promise.reject(new Error("This file can't be read on this device"))))(file.data) : file.data;
|
|
42
|
+
await sendBytes(minted.url, body, { method: "PUT", headers: { "Content-Type": file.contentType }, signal, onProgress, fetch: options.fetch });
|
|
43
|
+
})();
|
|
44
|
+
return { provider: minted.provider, providerId: minted.providerId, status: "pending", url: minted.url, done };
|
|
45
|
+
};
|
|
46
|
+
}
|
|
37
47
|
var ChatContext = createContext(null);
|
|
38
48
|
function ChatProvider({
|
|
39
49
|
client,
|
|
@@ -41,6 +51,7 @@ function ChatProvider({
|
|
|
41
51
|
outboxStore,
|
|
42
52
|
readFile,
|
|
43
53
|
upload,
|
|
54
|
+
hostedUpload,
|
|
44
55
|
newId,
|
|
45
56
|
persistFile,
|
|
46
57
|
releasePreview,
|
|
@@ -52,15 +63,23 @@ function ChatProvider({
|
|
|
52
63
|
const queryClient = useQueryClient();
|
|
53
64
|
const [queue, setQueue] = useState([]);
|
|
54
65
|
const listeners = useRef(/* @__PURE__ */ new Set());
|
|
66
|
+
const live = useRef({ readFile, upload, hostedUpload, videoUpload, persistFile });
|
|
67
|
+
live.current = { readFile, upload, hostedUpload, videoUpload, persistFile };
|
|
55
68
|
const outbox = useMemo(
|
|
56
69
|
() => createOutbox({
|
|
57
70
|
client,
|
|
58
71
|
userId,
|
|
59
72
|
store: outboxStore,
|
|
60
|
-
readFile,
|
|
61
|
-
upload,
|
|
73
|
+
readFile: readFile && ((uri) => (live.current.readFile ?? readFile)(uri)),
|
|
74
|
+
upload: upload && ((...args) => (live.current.upload ?? upload)(...args)),
|
|
75
|
+
hostedUpload: hostedUpload || videoUpload ? (...args) => {
|
|
76
|
+
const now = live.current;
|
|
77
|
+
const video = now.videoUpload ?? videoUpload;
|
|
78
|
+
const send = now.hostedUpload ?? (video ? hostedUploadFrom(video, { readFile: now.readFile ?? readFile }) : hostedUpload);
|
|
79
|
+
return send(...args);
|
|
80
|
+
} : void 0,
|
|
62
81
|
newId,
|
|
63
|
-
persistFile,
|
|
82
|
+
persistFile: persistFile && ((file) => (live.current.persistFile ?? persistFile)(file)),
|
|
64
83
|
releasePreview,
|
|
65
84
|
onChange: setQueue,
|
|
66
85
|
onSent: (message) => {
|
|
@@ -234,6 +253,18 @@ function useConversation(conversationId, opts = {}) {
|
|
|
234
253
|
useEffect(() => {
|
|
235
254
|
if (shouldMark && latestSeq > 0) void markRead().catch(() => void 0);
|
|
236
255
|
}, [shouldMark, latestSeq, markRead]);
|
|
256
|
+
const refetchConversation = useCallback(
|
|
257
|
+
() => Promise.all([
|
|
258
|
+
// The conversation and what hangs off it (topics, availability), never its message lists:
|
|
259
|
+
// a refetch would replace loaded history and merged live messages with one fresh page.
|
|
260
|
+
queryClient.refetchQueries({ queryKey: chatKeys.conversation(id), predicate: (q) => q.queryKey[3] !== "messages" }),
|
|
261
|
+
queryClient.refetchQueries({ queryKey: chatKeys.members(id) }),
|
|
262
|
+
queryClient.refetchQueries({ queryKey: chatKeys.role(id) }),
|
|
263
|
+
queryClient.refetchQueries({ queryKey: chatKeys.pins(id) }),
|
|
264
|
+
queryClient.refetchQueries({ queryKey: ["genesis-chat", "kind"] })
|
|
265
|
+
]).then(() => void 0),
|
|
266
|
+
[queryClient, id]
|
|
267
|
+
);
|
|
237
268
|
const loadEarlier = useCallback(async () => {
|
|
238
269
|
const first = list[0];
|
|
239
270
|
if (!first || loadingEarlier) return;
|
|
@@ -292,7 +323,10 @@ function useConversation(conversationId, opts = {}) {
|
|
|
292
323
|
conversationLoading: conversation.isLoading,
|
|
293
324
|
/** Your role couldn't be loaded (offline): offer `refetchAccess`, don't say "can't post". */
|
|
294
325
|
accessError: access.error ?? null,
|
|
295
|
-
|
|
326
|
+
/** Retries everything this conversation loads (role, members, the conversation, topics, availability, pins), not only access. */
|
|
327
|
+
refetchAccess: refetchConversation,
|
|
328
|
+
/** Same as refetchAccess: every per-conversation lookup again, for a Retry after being offline. */
|
|
329
|
+
refetchConversation,
|
|
296
330
|
/** The kind's rules (max members, threads, who posts). */
|
|
297
331
|
kind: kindRules.data ?? null,
|
|
298
332
|
/** May you pin here: owners and admins, or anyone in a two-person conversation. */
|
|
@@ -405,7 +439,7 @@ function useTopics(conversationId) {
|
|
|
405
439
|
return { topics, isLoading: query.isLoading, error: query.error, setTopic, refetch: query.refetch };
|
|
406
440
|
}
|
|
407
441
|
function useScheduled(conversationId, opts = {}) {
|
|
408
|
-
const { client,
|
|
442
|
+
const { client, outbox } = useChat();
|
|
409
443
|
const queryClient = useQueryClient();
|
|
410
444
|
const query = useQuery({
|
|
411
445
|
queryKey: chatKeys.scheduled(conversationId),
|
|
@@ -418,15 +452,14 @@ function useScheduled(conversationId, opts = {}) {
|
|
|
418
452
|
async (input, files = []) => {
|
|
419
453
|
const attachments = [...input.attachments ?? []];
|
|
420
454
|
for (const f of files) {
|
|
421
|
-
|
|
422
|
-
const path = await client.upload(input.conversationId, userId, f.data, { name: f.name, contentType: f.contentType });
|
|
455
|
+
const path = await outbox.uploadFile(input.conversationId, f);
|
|
423
456
|
attachments.push({ ...f.meta, kind: f.meta?.kind ?? attachmentKindFor(f.contentType), path, name: f.meta?.name ?? f.name, mime: f.contentType });
|
|
424
457
|
}
|
|
425
458
|
const id = await client.schedule({ ...input, clientId: input.clientId ?? randomId(), attachments });
|
|
426
459
|
await refresh();
|
|
427
460
|
return id;
|
|
428
461
|
},
|
|
429
|
-
[client,
|
|
462
|
+
[client, outbox, refresh]
|
|
430
463
|
);
|
|
431
464
|
const cancel = useCallback(
|
|
432
465
|
async (id) => {
|
|
@@ -571,6 +604,6 @@ function useLinkPreviews(messageIds) {
|
|
|
571
604
|
}
|
|
572
605
|
var EMPTY_PREVIEWS = {};
|
|
573
606
|
|
|
574
|
-
export { CATCH_UP_MAX_PAGES, CATCH_UP_PAGE, ChatProvider, LINK_PREVIEW_CONCURRENCY, belongsTo, chatKeys, useAvailability, useBroadcast, useChat, useConversation, useDebouncedValue, useFileUrls, useInbox, useLinkPreviews, useMessageActions, usePeople, usePerson, usePins, useScheduled, useSearch, useStartConversation, useTopics, useUnreadTotal };
|
|
575
|
-
//# sourceMappingURL=chunk-
|
|
576
|
-
//# sourceMappingURL=chunk-
|
|
607
|
+
export { CATCH_UP_MAX_PAGES, CATCH_UP_PAGE, ChatProvider, LINK_PREVIEW_CONCURRENCY, belongsTo, chatKeys, hostedUploadFrom, useAvailability, useBroadcast, useChat, useConversation, useDebouncedValue, useFileUrls, useInbox, useLinkPreviews, useMessageActions, usePeople, usePerson, usePins, useScheduled, useSearch, useStartConversation, useTopics, useUnreadTotal };
|
|
608
|
+
//# sourceMappingURL=chunk-JU2FAPBI.js.map
|
|
609
|
+
//# sourceMappingURL=chunk-JU2FAPBI.js.map
|