@marktiderman/genesis-support 0.2.0 → 0.2.1
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 +61 -4
- package/dist/{block-fZjTS2sY.d.ts → block-ClKVvk-d.d.ts} +10 -2
- package/dist/chunk-MMBNG2NK.js +89 -0
- package/dist/chunk-MMBNG2NK.js.map +1 -0
- package/dist/{chunk-ZV4KJBUF.js → chunk-OSB6G6Y3.js} +18 -88
- package/dist/chunk-OSB6G6Y3.js.map +1 -0
- package/dist/{chunk-4VFYVQUF.js → chunk-QNUAKW7K.js} +136 -7
- package/dist/chunk-QNUAKW7K.js.map +1 -0
- package/dist/{copy-CuTArBFg.d.ts → copy-D3rc0SCl.d.ts} +141 -2
- package/dist/index.d.ts +3 -3
- package/dist/index.js +4 -3
- package/dist/index.js.map +1 -1
- package/dist/native.d.ts +28 -4
- package/dist/native.js +30 -9
- package/dist/native.js.map +1 -1
- package/dist/web-chat.d.ts +43 -0
- package/dist/web-chat.js +140 -0
- package/dist/web-chat.js.map +1 -0
- package/dist/web.d.ts +1 -1
- package/dist/web.js +2 -1
- package/dist/web.js.map +1 -1
- package/migrations/0001_genesis_support_chat.sql +616 -0
- package/migrations/tests/0001_genesis_support_chat.test.sql +288 -0
- package/package.json +57 -5
- package/dist/chunk-4VFYVQUF.js.map +0 -1
- package/dist/chunk-ZV4KJBUF.js.map +0 -1
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["../src/chat.ts","../src/client.ts","../src/block.ts"],"names":[],"mappings":"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","file":"chunk-QNUAKW7K.js","sourcesContent":["/**\n * Support threads on genesis-chat (the `support-chat` block, migration\n * `0001_genesis_support_chat.sql` in this package): what the support client\n * needs from a genesis-chat client, as a structural type, so genesis-support\n * never imports genesis-chat. Pass `createChatClient(supabase)` from\n * `@marktiderman/genesis-chat` as the support client's `chat` option.\n */\n\n/** The block's FeatureId. */\nexport const SUPPORT_CHAT_BLOCK_ID = \"support-chat\" as const;\n\n/** The genesis_chat kind a report's conversation is, and its subject type. */\nexport const SUPPORT_CHAT_KIND = \"support\";\nexport const SUPPORT_CHAT_SUBJECT = \"support_report\";\n\n/** A file a chat message carries (a subset of genesis-chat's ChatAttachment). */\nexport interface SupportChatAttachment {\n id: string;\n kind: string;\n path: string | null;\n url: string | null;\n mime: string | null;\n name: string | null;\n width: number | null;\n height: number | null;\n durationMs: number | null;\n}\n\n/** A chat message (a subset of genesis-chat's ChatMessage). */\nexport interface SupportChatMessage {\n id: string;\n seq: number;\n senderId: string | null;\n senderRole: string;\n kind: string;\n body: string;\n visibility: \"everyone\" | \"observers\";\n createdAt: string;\n deletedAt: string | null;\n attachments: SupportChatAttachment[];\n}\n\n/** The part of genesis-chat's client (`createChatClient`) a support thread uses. */\nexport interface SupportChatClient {\n messages(conversationId: string, opts?: { limit?: number }): Promise<{ messages: SupportChatMessage[]; hasEarlier: boolean }>;\n send(input: { conversationId: string; clientId: string; body?: string; visibility?: \"everyone\" | \"observers\" }): Promise<SupportChatMessage>;\n markRead(conversationId: string, seq?: number): Promise<unknown>;\n fileUrls(paths: string[]): Promise<Record<string, string>>;\n subscribeConversation(\n conversationId: string,\n me: string,\n handlers: { onSignal: (signal: unknown) => void; onJoin?: () => void },\n ): { unsubscribe(): void };\n}\n\n/** Where a report's thread lives: a genesis-chat conversation, or support_replies (no support-chat block). */\nexport type SupportThreadInfo = { mode: \"chat\"; conversationId: string } | { mode: \"replies\"; conversationId: null };\n\n/** A support report's status, as genesis-chat's conversation status (the migration's mapping). */\nexport const SUPPORT_TO_CHAT_STATUS = {\n open: \"open\",\n submitted: \"open\",\n in_progress: \"pending\",\n addressed: \"solved\",\n wont_fix: \"closed\",\n} as const;\n\n/** PostgREST's \"no such function\" (PGRST202) or Postgres's (42883): the block's migration isn't applied. */\nexport function isMissingFunction(error: { code?: string; message?: string } | null | undefined): boolean {\n if (!error) return false;\n return error.code === \"PGRST202\" || error.code === \"42883\";\n}\n","/**\n * The support client: the app's own Supabase (migration 0006 from\n * `@marktiderman/genesis-supabase`) under row level security. Everything a\n * person does (send feedback, read their reports and the replies, reply, vote\n * on the idea board) and the Supabase sink for the feedback record.\n *\n * With a genesis-chat client (`chat` option) and the support-chat block's\n * migration applied, a report's thread runs on genesis-chat: replies, reply,\n * markRead and subscribe go through the report's conversation (live updates,\n * files, internal notes for staff). Otherwise they use support_replies.\n */\nimport type { SupabaseClient } from \"@supabase/supabase-js\";\nimport type { FeedbackStatus } from \"./contract\";\nimport { attachmentBytes, sanitizeContext, type FeedbackDelivery, type FeedbackRecord, type FeedbackSink, type FeedbackType } from \"./record\";\nimport { isMissingFunction, type SupportChatAttachment, type SupportChatClient, type SupportChatMessage, type SupportThreadInfo } from \"./chat\";\n\n/** Any supabase-js client; the app's generated Database types don't need to list the support tables. */\n// eslint-disable-next-line @typescript-eslint/no-explicit-any\nexport type SupportSupabase = SupabaseClient<any, any, any>;\n\n/** The storage bucket migration 0006 creates. */\nexport const SUPPORT_BUCKET = \"genesis-support\";\n\n/** A Supabase refusal, with its code (`GS429` rate limited, `42501` not allowed, …). */\nexport class SupportError extends Error {\n constructor(\n message: string,\n readonly code: string | undefined,\n readonly operation: string,\n ) {\n super(message);\n this.name = \"SupportError\";\n }\n /** The database's rate limit (SQLSTATE GS429). */\n get rateLimited(): boolean {\n return this.code === \"GS429\";\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 SupportError(result.error.message, result.error.code, operation);\n return result.data as T;\n}\n\nexport type IdeaStatus = \"open\" | \"planned\" | \"in_progress\" | \"shipped\" | \"declined\" | \"hidden\";\n\nexport interface SupportReport {\n id: string;\n type: FeedbackType;\n title: string | null;\n message: string;\n status: FeedbackStatus;\n /** The FeatureId staff linked it to. */\n featureId: string | null;\n route: string | null;\n appVersion: string | null;\n createdAt: string;\n updatedAt: string;\n lastStaffReplyAt: string | null;\n lastUserReplyAt: string | null;\n userReadAt: string | null;\n staffReadAt: string | null;\n /** A staff reply the person hasn't read. */\n unread: boolean;\n /** Who sent it (the person's own id, or for staff the reporter's). */\n userId?: string | null;\n}\n\nexport interface SupportReply {\n id: string;\n reportId: string;\n body: string;\n fromStaff: boolean;\n /** Written by the signed-in person. */\n mine: boolean;\n createdAt: string;\n /** A staff-only internal note (support-chat block; staff only ever read them). Always false on support_replies. */\n internal?: boolean;\n /** Photos, files and voice notes (support-chat block). Empty on support_replies. */\n attachments?: SupportReplyAttachment[];\n /** Its number in the conversation (support-chat block), else null. */\n seq?: number | null;\n}\n\n/** A file on a reply, with a signed URL when it is in storage. */\nexport interface SupportReplyAttachment extends Omit<SupportChatAttachment, \"url\"> {\n /** A signed URL (or the attachment's own https URL); null when it couldn't be signed. */\n url: string | null;\n}\n\nexport interface SupportAttachmentView {\n id: string;\n path: string;\n contentType: string;\n width: number | null;\n height: number | null;\n /** A signed URL, valid for `signedUrlSeconds` (default an hour). Null when signing failed. */\n url: string | null;\n}\n\nexport interface SupportIdea {\n id: string;\n title: string;\n body: string | null;\n status: IdeaStatus;\n /** The FeatureId that delivers it (set when it ships). */\n featureId: string | null;\n shippedAt: string | null;\n voteCount: number;\n /** The signed-in person voted for it. */\n voted: boolean;\n /** The signed-in person suggested it. */\n mine: boolean;\n createdAt: string;\n}\n\nexport interface SupportClient extends FeedbackSink {\n /** The signed-in person's reports, newest first. */\n myReports(options?: { limit?: number }): Promise<SupportReport[]>;\n report(id: string): Promise<SupportReport | null>;\n replies(reportId: string): Promise<SupportReply[]>;\n /** `internal`: a staff-only note (support-chat block, staff only). */\n reply(reportId: string, body: string, options?: { internal?: boolean }): Promise<SupportReply>;\n markRead(reportId: string): Promise<void>;\n /**\n * Where the report's thread lives: its genesis-chat conversation when a `chat` client was given\n * and the support-chat block is installed, else support_replies.\n */\n thread(reportId: string): Promise<SupportThreadInfo>;\n /** The report's genesis-chat conversation id, or null (no chat client, no block, or not readable). */\n conversationId(reportId: string): Promise<string | null>;\n /** Live updates for the thread (chat only): `onChange` runs on each hint and each (re)join. Returns the stop. */\n subscribe(reportId: string, onChange: () => void): () => void;\n /** Whether the signed-in person is support staff. */\n isStaff(): Promise<boolean>;\n /** Staff: the report's status, and the FeatureId it is about (null keeps it, \"\" clears it). */\n setStatus(reportId: string, status: FeedbackStatus, featureId?: string | null): Promise<void>;\n /** Staff (support-chat block): who has the report, a staff member or null. */\n assign(reportId: string, userId: string | null): Promise<void>;\n /** Deletes the person's own report; its screenshots are queued for removal. */\n deleteReport(reportId: string): Promise<void>;\n attachments(reportId: string): Promise<SupportAttachmentView[]>;\n /** The board, most votes first. Hidden ideas are left out, the person's own included. */\n ideas(options?: { limit?: number; statuses?: IdeaStatus[] }): Promise<SupportIdea[]>;\n suggestIdea(input: { title: string; body?: string }): Promise<SupportIdea>;\n vote(ideaId: string): Promise<void>;\n unvote(ideaId: string): Promise<void>;\n /** \"You asked, we shipped\": shipped ideas the person suggested or voted for. */\n shippedForMe(): Promise<SupportIdea[]>;\n}\n\nexport interface SupportClientOptions {\n /**\n * A genesis-chat client (`createChatClient(supabase)`): with it, and the support-chat block's\n * migration applied, a report's thread runs on genesis-chat. Without either, support_replies.\n */\n chat?: SupportChatClient | false;\n /** Told once when a chat client was given but the block's migration isn't applied. Default `console.warn`. */\n onChatUnavailable?: (message: string) => void;\n bucket?: string;\n signedUrlSeconds?: number;\n /** For the attachment file names. Default `crypto.randomUUID`. */\n randomId?: () => string;\n}\n\nconst REPORT_COLUMNS =\n \"id, user_id, type, title, message, status, feature_id, route, app_version, created_at, updated_at, last_staff_reply_at, last_user_reply_at, user_read_at, staff_read_at\";\nconst IDEA_COLUMNS = \"id, title, body, status, feature_id, shipped_at, vote_count, created_at\";\n\ntype Row = Record<string, unknown>;\n\n/** True when there is a staff reply newer than the person's last read. */\nexport function isUnread(row: { lastStaffReplyAt: string | null; userReadAt: string | null }): boolean {\n if (!row.lastStaffReplyAt) return false;\n return !row.userReadAt || new Date(row.lastStaffReplyAt).getTime() > new Date(row.userReadAt).getTime();\n}\n\nfunction toReport(r: Row): SupportReport {\n const report: Omit<SupportReport, \"unread\"> = {\n id: String(r.id),\n type: r.type as FeedbackType,\n title: (r.title as string | null) ?? null,\n message: String(r.message ?? \"\"),\n status: r.status as FeedbackStatus,\n featureId: (r.feature_id as string | null) ?? null,\n route: (r.route as string | null) ?? null,\n appVersion: (r.app_version as string | null) ?? null,\n createdAt: String(r.created_at),\n updatedAt: String(r.updated_at),\n lastStaffReplyAt: (r.last_staff_reply_at as string | null) ?? null,\n lastUserReplyAt: (r.last_user_reply_at as string | null) ?? null,\n userReadAt: (r.user_read_at as string | null) ?? null,\n staffReadAt: (r.staff_read_at as string | null) ?? null,\n userId: (r.user_id as string | null) ?? null,\n };\n return { ...report, unread: isUnread(report) };\n}\n\nfunction toIdea(r: Row, mine: ReadonlySet<string>, voted: ReadonlySet<string>): SupportIdea {\n return {\n id: String(r.id),\n title: String(r.title ?? \"\"),\n body: (r.body as string | null) ?? null,\n status: r.status as IdeaStatus,\n featureId: (r.feature_id as string | null) ?? null,\n shippedAt: (r.shipped_at as string | null) ?? null,\n voteCount: Number(r.vote_count ?? 0),\n voted: voted.has(String(r.id)),\n mine: mine.has(String(r.id)),\n createdAt: String(r.created_at),\n };\n}\n\nconst EXTENSIONS: Record<string, string> = { \"image/png\": \"png\", \"image/jpeg\": \"jpg\", \"image/webp\": \"webp\" };\n\nfunction randomUuid(): string {\n const c = (globalThis as { crypto?: { randomUUID?: () => string } }).crypto;\n if (c?.randomUUID) return c.randomUUID();\n const hex = Array.from({ length: 32 }, () => Math.floor(Math.random() * 16).toString(16));\n hex[12] = \"4\";\n hex[16] = ((parseInt(hex[16]!, 16) & 3) | 8).toString(16);\n const h = hex.join(\"\");\n return `${h.slice(0, 8)}-${h.slice(8, 12)}-${h.slice(12, 16)}-${h.slice(16, 20)}-${h.slice(20)}`;\n}\n\nfunction defaultRandomId(): string {\n const c = (globalThis as { crypto?: { randomUUID?: () => string } }).crypto;\n if (c?.randomUUID) return c.randomUUID();\n return `${Date.now().toString(36)}-${Math.random().toString(36).slice(2, 10)}`;\n}\n\n/** The support client over the app's Supabase client. */\nexport function createSupportClient(supabase: SupportSupabase, options: SupportClientOptions = {}): SupportClient {\n const bucket = options.bucket ?? SUPPORT_BUCKET;\n const signedUrlSeconds = options.signedUrlSeconds ?? 3600;\n const randomId = options.randomId ?? defaultRandomId;\n\n const me = async (): Promise<string | null> => {\n const { data } = await supabase.auth.getSession();\n return data.session?.user?.id ?? null;\n };\n\n // The support-chat block: learned on first use (the database function is there or not).\n const chat = options.chat || null;\n let chatInstalled: boolean | null = chat ? null : false;\n const conversations = new Map<string, string>();\n const warn = options.onChatUnavailable ?? ((message: string) => console.warn(message));\n\n async function thread(reportId: string): Promise<SupportThreadInfo> {\n if (!chat || chatInstalled === false) return { mode: \"replies\", conversationId: null };\n const known = conversations.get(reportId);\n if (known) return { mode: \"chat\", conversationId: known };\n const result = await supabase.rpc(\"support_chat_conversation\", { report: reportId });\n if (result.error) {\n if (isMissingFunction(result.error)) {\n chatInstalled = false;\n warn(\n \"genesis-support: a chat client was given, but the support-chat block's migration (0001_genesis_support_chat.sql) isn't applied. Report threads use support_replies.\",\n );\n return { mode: \"replies\", conversationId: null };\n }\n throw new SupportError(result.error.message, result.error.code, \"thread\");\n }\n chatInstalled = true;\n const id = typeof result.data === \"string\" && result.data ? result.data : null;\n // No conversation (not readable, or a report the block hasn't reached): support_replies still mirrors it.\n if (!id) return { mode: \"replies\", conversationId: null };\n conversations.set(reportId, id);\n return { mode: \"chat\", conversationId: id };\n }\n\n async function chatReply(reportId: string, m: SupportChatMessage, userId: string | null): Promise<SupportReply> {\n const paths = m.attachments.map((a) => a.path).filter((p): p is string => Boolean(p));\n const signed = paths.length && chat ? await chat.fileUrls(paths).catch(() => ({}) as Record<string, string>) : {};\n return {\n id: m.id,\n reportId,\n body: m.body,\n fromStaff: m.senderRole === \"observer\",\n mine: Boolean(userId) && m.senderId === userId,\n createdAt: m.createdAt,\n internal: m.visibility === \"observers\",\n seq: m.seq,\n attachments: m.attachments.map((a) => ({ ...a, url: (a.path ? signed[a.path] : null) ?? a.url ?? null })),\n };\n }\n\n const client: SupportClient = {\n async submit(record: FeedbackRecord): Promise<FeedbackDelivery> {\n const userId = await me();\n if (!userId) throw new SupportError(\"Sign in to send feedback.\", \"42501\", \"submit\");\n const context = sanitizeContext(record.context);\n const row = must<Row>(\n \"submit\",\n await supabase\n .from(\"support_reports\")\n .insert({\n type: record.type,\n title: record.title?.trim() ? record.title.trim().slice(0, 300) : null,\n message: record.message,\n route: context.route ?? null,\n app_version: context.appVersion ?? null,\n platform: context.platform ?? null,\n device: context.device ?? {},\n context: context.extra ?? {},\n })\n .select(\"id\")\n .single(),\n );\n const id = String(row.id);\n let dropped = 0;\n for (const attachment of (record.attachments ?? []).slice(0, 5)) {\n try {\n const bytes = attachmentBytes(attachment);\n if (!bytes) throw new Error(\"no image data\");\n const path = `${userId}/${id}/${randomId().replace(/[^A-Za-z0-9_-]/g, \"\")}.${EXTENSIONS[attachment.contentType] ?? \"png\"}`;\n const upload = await supabase.storage.from(bucket).upload(path, bytes, { contentType: attachment.contentType, upsert: false });\n if (upload.error) throw upload.error;\n must(\n \"attach\",\n await supabase.from(\"support_attachments\").insert({\n report_id: id,\n path,\n content_type: attachment.contentType,\n byte_size: bytes.byteLength,\n width: attachment.width ? Math.round(attachment.width) : null,\n height: attachment.height ? Math.round(attachment.height) : null,\n }),\n );\n } catch {\n dropped += 1;\n }\n }\n dropped += Math.max(0, (record.attachments?.length ?? 0) - 5);\n return { id, attachmentsDropped: dropped };\n },\n\n async myReports({ limit = 50 } = {}) {\n const userId = await me();\n if (!userId) return [];\n const rows = must<Row[]>(\n \"myReports\",\n await supabase.from(\"support_reports\").select(REPORT_COLUMNS).eq(\"user_id\", userId).order(\"created_at\", { ascending: false }).limit(limit),\n );\n return rows.map(toReport);\n },\n\n async report(id) {\n const row = must<Row | null>(\"report\", await supabase.from(\"support_reports\").select(REPORT_COLUMNS).eq(\"id\", id).maybeSingle());\n return row ? toReport(row) : null;\n },\n\n async replies(reportId) {\n const where = await thread(reportId);\n if (where.mode === \"chat\" && chat) {\n const [page, userId] = await Promise.all([chat.messages(where.conversationId, { limit: 200 }), me()]);\n const shown = page.messages.filter((m) => m.kind === \"text\" && !m.deletedAt);\n return Promise.all(shown.map((m) => chatReply(reportId, m, userId)));\n }\n // Who wrote a reply isn't readable (staff stay anonymous): on the person's own report, theirs are the non-staff ones.\n const rows = must<Row[]>(\n \"replies\",\n await supabase.from(\"support_replies\").select(\"id, report_id, body, from_staff, created_at\").eq(\"report_id\", reportId).order(\"created_at\", { ascending: true }),\n );\n return rows.map((r) => ({\n id: String(r.id),\n reportId: String(r.report_id),\n body: String(r.body ?? \"\"),\n fromStaff: r.from_staff === true,\n mine: r.from_staff !== true,\n createdAt: String(r.created_at),\n }));\n },\n\n async reply(reportId, body, { internal = false } = {}) {\n const where = await thread(reportId);\n if (where.mode === \"chat\" && chat) {\n const [sent, userId] = await Promise.all([\n chat\n .send({ conversationId: where.conversationId, clientId: randomUuid(), body, visibility: internal ? \"observers\" : \"everyone\" })\n .catch((error: { message?: string; code?: string }) => {\n throw new SupportError(error?.message ?? \"Not sent\", error?.code, \"reply\");\n }),\n me(),\n ]);\n return chatReply(reportId, sent, userId);\n }\n if (internal) throw new SupportError(\"Internal notes need the support-chat block.\", \"0A000\", \"reply\");\n const row = must<Row>(\n \"reply\",\n await supabase.from(\"support_replies\").insert({ report_id: reportId, body }).select(\"id, report_id, body, from_staff, created_at\").single(),\n );\n return { id: String(row.id), reportId: String(row.report_id), body: String(row.body), fromStaff: row.from_staff === true, mine: true, createdAt: String(row.created_at) };\n },\n\n async markRead(reportId) {\n const where = await thread(reportId);\n if (where.mode === \"chat\" && chat) {\n // The migration carries the conversation's read place to the report.\n await chat.markRead(where.conversationId);\n return;\n }\n must(\"markRead\", await supabase.rpc(\"support_mark_read\", { report: reportId }));\n },\n\n thread,\n\n async conversationId(reportId) {\n return (await thread(reportId)).conversationId;\n },\n\n subscribe(reportId, onChange) {\n let stopped = false;\n let stop = () => {};\n void (async () => {\n const where = await thread(reportId);\n if (where.mode !== \"chat\" || !chat || stopped) return;\n const sub = chat.subscribeConversation(where.conversationId, (await me()) ?? \"\", { onSignal: () => onChange(), onJoin: () => onChange() });\n if (stopped) sub.unsubscribe();\n else stop = () => sub.unsubscribe();\n })().catch(() => undefined);\n return () => {\n stopped = true;\n stop();\n };\n },\n\n async isStaff() {\n return must<boolean | null>(\"isStaff\", await supabase.rpc(\"support_is_staff\")) === true;\n },\n\n async setStatus(reportId, status, featureId = null) {\n must(\"setStatus\", await supabase.rpc(\"support_set_report_status\", { report: reportId, status, feature: featureId }));\n },\n\n async assign(reportId, userId) {\n must(\"assign\", await supabase.rpc(\"support_assign_report\", { report: reportId, assignee: userId }));\n },\n\n async deleteReport(reportId) {\n const rows = must<Row[]>(\"deleteReport\", await supabase.from(\"support_reports\").delete().eq(\"id\", reportId).select(\"id\"));\n if (!rows.length) throw new SupportError(\"That report wasn't deleted.\", \"42501\", \"deleteReport\");\n },\n\n async attachments(reportId) {\n const rows = must<Row[]>(\n \"attachments\",\n await supabase.from(\"support_attachments\").select(\"id, path, content_type, width, height\").eq(\"report_id\", reportId).order(\"created_at\"),\n );\n if (!rows.length) return [];\n const signed = await supabase.storage.from(bucket).createSignedUrls(\n rows.map((r) => String(r.path)),\n signedUrlSeconds,\n );\n const urls = new Map((signed.data ?? []).map((s) => [s.path, s.signedUrl] as const));\n return rows.map((r) => ({\n id: String(r.id),\n path: String(r.path),\n contentType: String(r.content_type),\n width: (r.width as number | null) ?? null,\n height: (r.height as number | null) ?? null,\n url: urls.get(String(r.path)) ?? null,\n }));\n },\n\n async ideas({ limit = 100, statuses } = {}) {\n const userId = await me();\n let query = supabase.from(\"support_ideas\").select(IDEA_COLUMNS).neq(\"status\", \"hidden\");\n if (statuses?.length) query = query.in(\"status\", statuses);\n const rows = must<Row[]>(\"ideas\", await query.order(\"vote_count\", { ascending: false }).order(\"created_at\", { ascending: false }).limit(limit));\n const votes = userId ? must<Row[]>(\"ideas\", await supabase.from(\"support_idea_votes\").select(\"idea_id\").eq(\"user_id\", userId)) : [];\n const voted = new Set(votes.map((v) => String(v.idea_id)));\n const mineIds = userId ? must<unknown[]>(\"ideas\", await supabase.rpc(\"support_my_idea_ids\")) : [];\n const mine = new Set((mineIds ?? []).map((v) => String(typeof v === \"object\" && v !== null ? Object.values(v)[0] : v)));\n return rows.map((r) => toIdea(r, mine, voted));\n },\n\n async suggestIdea({ title, body }) {\n const row = must<Row>(\"suggestIdea\", await supabase.from(\"support_ideas\").insert({ title: title.trim(), body: body?.trim() || null }).select(IDEA_COLUMNS).single());\n return toIdea(row, new Set([String(row.id)]), new Set());\n },\n\n async vote(ideaId) {\n const result = await supabase.from(\"support_idea_votes\").insert({ idea_id: ideaId });\n // Voting twice is already voting.\n if (result.error && result.error.code !== \"23505\") must(\"vote\", result);\n },\n\n async unvote(ideaId) {\n const userId = await me();\n if (!userId) return;\n must(\"unvote\", await supabase.from(\"support_idea_votes\").delete().eq(\"idea_id\", ideaId).eq(\"user_id\", userId));\n },\n\n async shippedForMe() {\n const rows = must<Row[]>(\"shippedForMe\", await supabase.rpc(\"support_shipped_for_me\", { max_rows: 50 }));\n const ids = (pick: string) => new Set(rows.filter((r) => r[pick] === true).map((r) => String(r.id)));\n return rows.map((r) => toIdea(r, ids(\"mine\"), ids(\"voted\")));\n },\n };\n return client;\n}\n\n/** The Supabase sink on its own (the support client is one). */\nexport function createSupabaseSink(supabase: SupportSupabase, options: SupportClientOptions = {}): FeedbackSink {\n const client = createSupportClient(supabase, options);\n return { submit: (record) => client.submit(record) };\n}\n","/**\n * The support block as a `defineBlock` declaration: what it adds to an app\n * (two Settings rows, its routes, migration 0006) and what it means for the\n * stores' privacy forms. Platform-neutral; `createSupport` (native) builds\n * on it, and a web app can resolve it with `resolveBlocks` too.\n */\nimport { defineBlock, type BlockContributions, type BlockSettingsRow } from \"@marktiderman/genesis-features/blocks\";\nimport { resolveSupportCopy, type SupportCopyOverrides } from \"./copy\";\nimport { SUPPORT_CHAT_BLOCK_ID } from \"./chat\";\n\n/** The FeatureId of the support block (features.ts). */\nexport const SUPPORT_BLOCK_ID = \"support-feedback\";\n\n/** Where the block's screens live, under `basePath`. */\nexport interface SupportPaths {\n /** Help: the person's reports and links to the idea board. */\n inbox: string;\n /** The feedback form. */\n feedback: string;\n /** One report's thread: `thread(id)`. */\n thread: (id: string) => string;\n ideas: string;\n shipped: string;\n}\n\nexport function supportPaths(basePath = \"/support\"): SupportPaths {\n const base = basePath.replace(/\\/$/, \"\") || \"/support\";\n return {\n inbox: base,\n feedback: `${base}/new`,\n thread: (id) => `${base}/${encodeURIComponent(id)}`,\n ideas: `${base}/ideas`,\n shipped: `${base}/shipped`,\n };\n}\n\nexport interface SupportBlockOptions {\n /** Default `/support`. The app owns the route files under it. */\n basePath?: string;\n copy?: SupportCopyOverrides;\n /** The Settings rows' section (default `support`), or `false` for none. */\n settings?: false | { section?: string };\n /** A subtitle hook for the Help row (an unread count). Called on every Settings render. */\n useHelpSubtitle?: () => string | null | undefined;\n /** `false` leaves the idea board out. Default on. */\n ideas?: boolean;\n /** The block's other FeatureIds it needs (default `[\"account\"]`: a signed-in person). */\n requires?: readonly string[];\n}\n\nconst PACKAGE = \"@marktiderman/genesis-supabase\";\n\n/** The block's contributions, for any platform. */\nexport function supportContributions(options: SupportBlockOptions = {}): BlockContributions<never> {\n const copy = resolveSupportCopy(options.copy);\n const paths = supportPaths(options.basePath);\n const section = options.settings === false ? null : (options.settings?.section ?? \"support\");\n const settings: BlockSettingsRow[] = section\n ? [\n { id: \"support-send-feedback\", section, title: copy.settings.sendFeedback, href: paths.feedback, order: 10, testID: \"genesis-support-send-feedback\" },\n { id: \"support-help\", section, title: copy.settings.help, href: paths.inbox, order: 20, testID: \"genesis-support-help\", useSubtitle: options.useHelpSubtitle },\n ]\n : [];\n const routes = [\n { path: paths.inbox, description: \"the person's reports and staff replies\", signedIn: true },\n { path: paths.feedback, description: \"send feedback, with a screenshot\", signedIn: true },\n { path: `${paths.inbox}/[id]`, description: \"one report's conversation\", signedIn: true },\n ...(options.ideas === false\n ? []\n : [\n { path: paths.ideas, description: \"the idea board, with votes\", signedIn: true },\n { path: paths.shipped, description: \"ideas the person asked for that shipped\", signedIn: true },\n ]),\n ];\n return {\n settings,\n routes,\n migrations: [{ package: PACKAGE, file: \"migrations/0006_genesis_support.sql\", test: \"migrations/tests/0006_genesis_support.test.sql\" }],\n privacy: {\n collects: [\n { type: \"user ID\", purposes: [\"app functionality\"], linked: true, tracking: false },\n { type: \"customer support\", purposes: [\"app functionality\"], linked: true, tracking: false },\n { type: \"other user content\", purposes: [\"app functionality\"], linked: true, tracking: false },\n { type: \"photos or videos\", purposes: [\"app functionality\"], linked: true, tracking: false },\n { type: \"other diagnostic data\", purposes: [\"app functionality\"], linked: true, tracking: false },\n ],\n notes: [\n \"Feedback is sent only when the person taps Send, and a screenshot only when they include it, after they can hide parts of it.\",\n \"Reports, screenshots and replies are visible to the person and the app's support staff only, and go when the person deletes them or their account.\",\n \"Feedback includes the app version, the screen's path (never its query string; private-looking segments are replaced), the platform and basic device facts.\",\n ],\n },\n };\n}\n\n/** The platform-neutral declaration. */\nexport function defineSupportBlock(options: SupportBlockOptions = {}) {\n return defineBlock({\n id: SUPPORT_BLOCK_ID,\n requires: options.requires ?? [\"account\"],\n contributes: supportContributions(options),\n });\n}\n\n/**\n * The support-chat block: a report's conversation runs on genesis-chat. It adds no screens of\n * its own (the support block's thread route renders the conversation); it brings migration\n * `0001_genesis_support_chat.sql` from this package, after 0006 and genesis-chat's migrations.\n */\nexport function defineSupportChatBlock(options: { requires?: readonly string[] } = {}) {\n return defineBlock({\n id: SUPPORT_CHAT_BLOCK_ID,\n requires: options.requires ?? [SUPPORT_BLOCK_ID, \"chat\"],\n contributes: {\n migrations: [\n { package: \"@marktiderman/genesis-support\", file: \"migrations/0001_genesis_support_chat.sql\", test: \"migrations/tests/0001_genesis_support_chat.test.sql\" },\n ],\n },\n });\n}\n"]}
|
|
@@ -224,11 +224,98 @@ declare function base64ToBytes(input: string): Uint8Array;
|
|
|
224
224
|
/** The bytes of an attachment, from `bytes` or `base64`. */
|
|
225
225
|
declare function attachmentBytes(attachment: FeedbackAttachment): Uint8Array | null;
|
|
226
226
|
|
|
227
|
+
/**
|
|
228
|
+
* Support threads on genesis-chat (the `support-chat` block, migration
|
|
229
|
+
* `0001_genesis_support_chat.sql` in this package): what the support client
|
|
230
|
+
* needs from a genesis-chat client, as a structural type, so genesis-support
|
|
231
|
+
* never imports genesis-chat. Pass `createChatClient(supabase)` from
|
|
232
|
+
* `@marktiderman/genesis-chat` as the support client's `chat` option.
|
|
233
|
+
*/
|
|
234
|
+
/** The block's FeatureId. */
|
|
235
|
+
declare const SUPPORT_CHAT_BLOCK_ID: "support-chat";
|
|
236
|
+
/** The genesis_chat kind a report's conversation is, and its subject type. */
|
|
237
|
+
declare const SUPPORT_CHAT_KIND = "support";
|
|
238
|
+
declare const SUPPORT_CHAT_SUBJECT = "support_report";
|
|
239
|
+
/** A file a chat message carries (a subset of genesis-chat's ChatAttachment). */
|
|
240
|
+
interface SupportChatAttachment {
|
|
241
|
+
id: string;
|
|
242
|
+
kind: string;
|
|
243
|
+
path: string | null;
|
|
244
|
+
url: string | null;
|
|
245
|
+
mime: string | null;
|
|
246
|
+
name: string | null;
|
|
247
|
+
width: number | null;
|
|
248
|
+
height: number | null;
|
|
249
|
+
durationMs: number | null;
|
|
250
|
+
}
|
|
251
|
+
/** A chat message (a subset of genesis-chat's ChatMessage). */
|
|
252
|
+
interface SupportChatMessage {
|
|
253
|
+
id: string;
|
|
254
|
+
seq: number;
|
|
255
|
+
senderId: string | null;
|
|
256
|
+
senderRole: string;
|
|
257
|
+
kind: string;
|
|
258
|
+
body: string;
|
|
259
|
+
visibility: "everyone" | "observers";
|
|
260
|
+
createdAt: string;
|
|
261
|
+
deletedAt: string | null;
|
|
262
|
+
attachments: SupportChatAttachment[];
|
|
263
|
+
}
|
|
264
|
+
/** The part of genesis-chat's client (`createChatClient`) a support thread uses. */
|
|
265
|
+
interface SupportChatClient {
|
|
266
|
+
messages(conversationId: string, opts?: {
|
|
267
|
+
limit?: number;
|
|
268
|
+
}): Promise<{
|
|
269
|
+
messages: SupportChatMessage[];
|
|
270
|
+
hasEarlier: boolean;
|
|
271
|
+
}>;
|
|
272
|
+
send(input: {
|
|
273
|
+
conversationId: string;
|
|
274
|
+
clientId: string;
|
|
275
|
+
body?: string;
|
|
276
|
+
visibility?: "everyone" | "observers";
|
|
277
|
+
}): Promise<SupportChatMessage>;
|
|
278
|
+
markRead(conversationId: string, seq?: number): Promise<unknown>;
|
|
279
|
+
fileUrls(paths: string[]): Promise<Record<string, string>>;
|
|
280
|
+
subscribeConversation(conversationId: string, me: string, handlers: {
|
|
281
|
+
onSignal: (signal: unknown) => void;
|
|
282
|
+
onJoin?: () => void;
|
|
283
|
+
}): {
|
|
284
|
+
unsubscribe(): void;
|
|
285
|
+
};
|
|
286
|
+
}
|
|
287
|
+
/** Where a report's thread lives: a genesis-chat conversation, or support_replies (no support-chat block). */
|
|
288
|
+
type SupportThreadInfo = {
|
|
289
|
+
mode: "chat";
|
|
290
|
+
conversationId: string;
|
|
291
|
+
} | {
|
|
292
|
+
mode: "replies";
|
|
293
|
+
conversationId: null;
|
|
294
|
+
};
|
|
295
|
+
/** A support report's status, as genesis-chat's conversation status (the migration's mapping). */
|
|
296
|
+
declare const SUPPORT_TO_CHAT_STATUS: {
|
|
297
|
+
readonly open: "open";
|
|
298
|
+
readonly submitted: "open";
|
|
299
|
+
readonly in_progress: "pending";
|
|
300
|
+
readonly addressed: "solved";
|
|
301
|
+
readonly wont_fix: "closed";
|
|
302
|
+
};
|
|
303
|
+
/** PostgREST's "no such function" (PGRST202) or Postgres's (42883): the block's migration isn't applied. */
|
|
304
|
+
declare function isMissingFunction(error: {
|
|
305
|
+
code?: string;
|
|
306
|
+
message?: string;
|
|
307
|
+
} | null | undefined): boolean;
|
|
308
|
+
|
|
227
309
|
/**
|
|
228
310
|
* The support client: the app's own Supabase (migration 0006 from
|
|
229
311
|
* `@marktiderman/genesis-supabase`) under row level security. Everything a
|
|
230
312
|
* person does (send feedback, read their reports and the replies, reply, vote
|
|
231
313
|
* on the idea board) and the Supabase sink for the feedback record.
|
|
314
|
+
*
|
|
315
|
+
* With a genesis-chat client (`chat` option) and the support-chat block's
|
|
316
|
+
* migration applied, a report's thread runs on genesis-chat: replies, reply,
|
|
317
|
+
* markRead and subscribe go through the report's conversation (live updates,
|
|
318
|
+
* files, internal notes for staff). Otherwise they use support_replies.
|
|
232
319
|
*/
|
|
233
320
|
|
|
234
321
|
/** Any supabase-js client; the app's generated Database types don't need to list the support tables. */
|
|
@@ -262,6 +349,8 @@ interface SupportReport {
|
|
|
262
349
|
staffReadAt: string | null;
|
|
263
350
|
/** A staff reply the person hasn't read. */
|
|
264
351
|
unread: boolean;
|
|
352
|
+
/** Who sent it (the person's own id, or for staff the reporter's). */
|
|
353
|
+
userId?: string | null;
|
|
265
354
|
}
|
|
266
355
|
interface SupportReply {
|
|
267
356
|
id: string;
|
|
@@ -271,6 +360,17 @@ interface SupportReply {
|
|
|
271
360
|
/** Written by the signed-in person. */
|
|
272
361
|
mine: boolean;
|
|
273
362
|
createdAt: string;
|
|
363
|
+
/** A staff-only internal note (support-chat block; staff only ever read them). Always false on support_replies. */
|
|
364
|
+
internal?: boolean;
|
|
365
|
+
/** Photos, files and voice notes (support-chat block). Empty on support_replies. */
|
|
366
|
+
attachments?: SupportReplyAttachment[];
|
|
367
|
+
/** Its number in the conversation (support-chat block), else null. */
|
|
368
|
+
seq?: number | null;
|
|
369
|
+
}
|
|
370
|
+
/** A file on a reply, with a signed URL when it is in storage. */
|
|
371
|
+
interface SupportReplyAttachment extends Omit<SupportChatAttachment, "url"> {
|
|
372
|
+
/** A signed URL (or the attachment's own https URL); null when it couldn't be signed. */
|
|
373
|
+
url: string | null;
|
|
274
374
|
}
|
|
275
375
|
interface SupportAttachmentView {
|
|
276
376
|
id: string;
|
|
@@ -303,8 +403,26 @@ interface SupportClient extends FeedbackSink {
|
|
|
303
403
|
}): Promise<SupportReport[]>;
|
|
304
404
|
report(id: string): Promise<SupportReport | null>;
|
|
305
405
|
replies(reportId: string): Promise<SupportReply[]>;
|
|
306
|
-
|
|
406
|
+
/** `internal`: a staff-only note (support-chat block, staff only). */
|
|
407
|
+
reply(reportId: string, body: string, options?: {
|
|
408
|
+
internal?: boolean;
|
|
409
|
+
}): Promise<SupportReply>;
|
|
307
410
|
markRead(reportId: string): Promise<void>;
|
|
411
|
+
/**
|
|
412
|
+
* Where the report's thread lives: its genesis-chat conversation when a `chat` client was given
|
|
413
|
+
* and the support-chat block is installed, else support_replies.
|
|
414
|
+
*/
|
|
415
|
+
thread(reportId: string): Promise<SupportThreadInfo>;
|
|
416
|
+
/** The report's genesis-chat conversation id, or null (no chat client, no block, or not readable). */
|
|
417
|
+
conversationId(reportId: string): Promise<string | null>;
|
|
418
|
+
/** Live updates for the thread (chat only): `onChange` runs on each hint and each (re)join. Returns the stop. */
|
|
419
|
+
subscribe(reportId: string, onChange: () => void): () => void;
|
|
420
|
+
/** Whether the signed-in person is support staff. */
|
|
421
|
+
isStaff(): Promise<boolean>;
|
|
422
|
+
/** Staff: the report's status, and the FeatureId it is about (null keeps it, "" clears it). */
|
|
423
|
+
setStatus(reportId: string, status: FeedbackStatus, featureId?: string | null): Promise<void>;
|
|
424
|
+
/** Staff (support-chat block): who has the report, a staff member or null. */
|
|
425
|
+
assign(reportId: string, userId: string | null): Promise<void>;
|
|
308
426
|
/** Deletes the person's own report; its screenshots are queued for removal. */
|
|
309
427
|
deleteReport(reportId: string): Promise<void>;
|
|
310
428
|
attachments(reportId: string): Promise<SupportAttachmentView[]>;
|
|
@@ -323,6 +441,13 @@ interface SupportClient extends FeedbackSink {
|
|
|
323
441
|
shippedForMe(): Promise<SupportIdea[]>;
|
|
324
442
|
}
|
|
325
443
|
interface SupportClientOptions {
|
|
444
|
+
/**
|
|
445
|
+
* A genesis-chat client (`createChatClient(supabase)`): with it, and the support-chat block's
|
|
446
|
+
* migration applied, a report's thread runs on genesis-chat. Without either, support_replies.
|
|
447
|
+
*/
|
|
448
|
+
chat?: SupportChatClient | false;
|
|
449
|
+
/** Told once when a chat client was given but the block's migration isn't applied. Default `console.warn`. */
|
|
450
|
+
onChatUnavailable?: (message: string) => void;
|
|
326
451
|
bucket?: string;
|
|
327
452
|
signedUrlSeconds?: number;
|
|
328
453
|
/** For the attachment file names. Default `crypto.randomUUID`. */
|
|
@@ -420,6 +545,20 @@ declare const supportCopy: {
|
|
|
420
545
|
sending: string;
|
|
421
546
|
notSent: string;
|
|
422
547
|
retryHint: string;
|
|
548
|
+
/** A message's file, where the thread can't show it. */
|
|
549
|
+
voiceNote: string;
|
|
550
|
+
video: string;
|
|
551
|
+
file: string;
|
|
552
|
+
internalNote: string;
|
|
553
|
+
};
|
|
554
|
+
staff: {
|
|
555
|
+
status: string;
|
|
556
|
+
assignee: string;
|
|
557
|
+
unassigned: string;
|
|
558
|
+
reporter: string;
|
|
559
|
+
unavailableTitle: string;
|
|
560
|
+
unavailableBody: string;
|
|
561
|
+
failed: string;
|
|
423
562
|
};
|
|
424
563
|
status: {
|
|
425
564
|
open: string;
|
|
@@ -474,4 +613,4 @@ declare function resolveSupportCopy(overrides?: SupportCopyOverrides): SupportCo
|
|
|
474
613
|
/** Fills `{name}` placeholders. */
|
|
475
614
|
declare function fill(template: string, values: Readonly<Record<string, string | number>>): string;
|
|
476
615
|
|
|
477
|
-
export {
|
|
616
|
+
export { type WidgetTypeWire as $, type SupportAttachmentView as A, type BootConfig as B, CONTRACT_VERSION as C, DEVICE_MAX_BYTES as D, EXTRA_MAX_BYTES as E, type FeedbackReportSummary as F, type SupportChatAttachment as G, type SupportChatClient as H, type IdeaStatus as I, type SupportChatMessage as J, type SupportClient as K, LIMITS as L, type SupportClientOptions as M, type SupportCopy as N, type SupportCopyOverrides as O, SupportError as P, type SupportIdea as Q, type SupportReply as R, type SubmitFeedbackInput as S, type SupportReplyAttachment as T, type SupportReport as U, type SupportSupabase as V, type SupportThreadInfo as W, type ThemeTokens as X, WIDGET_HEADERS as Y, type WidgetConfig as Z, type WidgetType as _, type SubmitFeedbackResult as a, attachmentBytes as a0, base64ToBytes as a1, cleanRoute as a2, composeCollectors as a3, createContextCollector as a4, createSupabaseSink as a5, createSupportClient as a6, fill as a7, isMissingFunction as a8, isUnread as a9, resolveSupportCopy as aa, sanitizeContext as ab, supportCopy as ac, validateSubmitInput as ad, type FeedbackSink as b, type ContextCollector as c, type ContextCollectorOptions as d, FEEDBACK_REPORT_TYPES as e, FEEDBACK_STATUSES as f, FEEDBACK_TYPES as g, type FeedbackAttachment as h, type FeedbackContext as i, type FeedbackDelivery as j, type FeedbackOp as k, type FeedbackRecord as l, type FeedbackReportType as m, type FeedbackStatus as n, type FeedbackType as o, type FindingSeverity as p, type IdentityMode as q, type LauncherPosition as r, type Lazy as s, SUPPORT_BUCKET as t, SUPPORT_CHAT_BLOCK_ID as u, SUPPORT_CHAT_KIND as v, SUPPORT_CHAT_SUBJECT as w, SUPPORT_TO_CHAT_STATUS as x, type ScreenshotContentType as y, type SubmitValidation as z };
|
package/dist/index.d.ts
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
|
-
import { B as BootConfig, S as SubmitFeedbackInput, a as SubmitFeedbackResult, F as FeedbackReportSummary, b as FeedbackSink } from './copy-
|
|
2
|
-
export { C as CONTRACT_VERSION, c as ContextCollector, d as ContextCollectorOptions, D as DEVICE_MAX_BYTES, E as EXTRA_MAX_BYTES, e as FEEDBACK_REPORT_TYPES, f as FEEDBACK_STATUSES, g as FEEDBACK_TYPES, h as FeedbackAttachment, i as FeedbackContext, j as FeedbackDelivery, k as FeedbackOp, l as FeedbackRecord, m as FeedbackReportType, n as FeedbackStatus, o as FeedbackType, p as FindingSeverity, I as IdeaStatus, q as IdentityMode, L as LIMITS, r as LauncherPosition, s as Lazy, t as SUPPORT_BUCKET, u as
|
|
3
|
-
export { S as SUPPORT_BLOCK_ID, a as SupportBlockOptions, b as SupportPaths, d as defineSupportBlock, s as supportContributions,
|
|
1
|
+
import { B as BootConfig, S as SubmitFeedbackInput, a as SubmitFeedbackResult, F as FeedbackReportSummary, b as FeedbackSink } from './copy-D3rc0SCl.js';
|
|
2
|
+
export { C as CONTRACT_VERSION, c as ContextCollector, d as ContextCollectorOptions, D as DEVICE_MAX_BYTES, E as EXTRA_MAX_BYTES, e as FEEDBACK_REPORT_TYPES, f as FEEDBACK_STATUSES, g as FEEDBACK_TYPES, h as FeedbackAttachment, i as FeedbackContext, j as FeedbackDelivery, k as FeedbackOp, l as FeedbackRecord, m as FeedbackReportType, n as FeedbackStatus, o as FeedbackType, p as FindingSeverity, I as IdeaStatus, q as IdentityMode, L as LIMITS, r as LauncherPosition, s as Lazy, t as SUPPORT_BUCKET, u as SUPPORT_CHAT_BLOCK_ID, v as SUPPORT_CHAT_KIND, w as SUPPORT_CHAT_SUBJECT, x as SUPPORT_TO_CHAT_STATUS, y as ScreenshotContentType, z as SubmitValidation, A as SupportAttachmentView, G as SupportChatAttachment, H as SupportChatClient, J as SupportChatMessage, K as SupportClient, M as SupportClientOptions, N as SupportCopy, O as SupportCopyOverrides, P as SupportError, Q as SupportIdea, R as SupportReply, T as SupportReplyAttachment, U as SupportReport, V as SupportSupabase, W as SupportThreadInfo, X as ThemeTokens, Y as WIDGET_HEADERS, Z as WidgetConfig, _ as WidgetType, $ as WidgetTypeWire, a0 as attachmentBytes, a1 as base64ToBytes, a2 as cleanRoute, a3 as composeCollectors, a4 as createContextCollector, a5 as createSupabaseSink, a6 as createSupportClient, a7 as fill, a8 as isMissingFunction, a9 as isUnread, aa as resolveSupportCopy, ab as sanitizeContext, ac as supportCopy, ad as validateSubmitInput } from './copy-D3rc0SCl.js';
|
|
3
|
+
export { S as SUPPORT_BLOCK_ID, a as SupportBlockOptions, b as SupportPaths, d as defineSupportBlock, c as defineSupportChatBlock, s as supportContributions, e as supportPaths } from './block-ClKVvk-d.js';
|
|
4
4
|
import '@supabase/supabase-js';
|
|
5
5
|
import '@marktiderman/genesis-features/blocks';
|
|
6
6
|
|
package/dist/index.js
CHANGED
|
@@ -1,6 +1,7 @@
|
|
|
1
|
-
export { SUPPORT_BLOCK_ID, SUPPORT_BUCKET, SupportError, createSupabaseSink, createSupportClient, defineSupportBlock, isUnread, supportContributions, supportPaths } from './chunk-
|
|
2
|
-
|
|
3
|
-
|
|
1
|
+
export { SUPPORT_BLOCK_ID, SUPPORT_BUCKET, SUPPORT_CHAT_BLOCK_ID, SUPPORT_CHAT_KIND, SUPPORT_CHAT_SUBJECT, SUPPORT_TO_CHAT_STATUS, SupportError, createSupabaseSink, createSupportClient, defineSupportBlock, defineSupportChatBlock, isMissingFunction, isUnread, supportContributions, supportPaths } from './chunk-QNUAKW7K.js';
|
|
2
|
+
export { DEVICE_MAX_BYTES, EXTRA_MAX_BYTES, FEEDBACK_TYPES, attachmentBytes, base64ToBytes, composeCollectors, createContextCollector, sanitizeContext } from './chunk-MMBNG2NK.js';
|
|
3
|
+
import { WIDGET_HEADERS, CONTRACT_VERSION } from './chunk-OSB6G6Y3.js';
|
|
4
|
+
export { CONTRACT_VERSION, FEEDBACK_REPORT_TYPES, FEEDBACK_STATUSES, LIMITS, WIDGET_HEADERS, cleanRoute, fill, resolveSupportCopy, supportCopy, validateSubmitInput } from './chunk-OSB6G6Y3.js';
|
|
4
5
|
|
|
5
6
|
// src/intake.ts
|
|
6
7
|
var IntakeError = class extends Error {
|
package/dist/index.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["../src/intake.ts"],"names":[],"mappings":"
|
|
1
|
+
{"version":3,"sources":["../src/intake.ts"],"names":[],"mappings":"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","file":"index.js","sourcesContent":["/**\n * The HTTP client for the `genesis-support-intake` Edge Function: the wire\n * contract (boot, submit, list) for code that doesn't hold a supabase-js\n * client, such as a script-tag web loader or another backend. Zero\n * dependencies, global `fetch`.\n */\nimport {\n CONTRACT_VERSION,\n WIDGET_HEADERS,\n type BootConfig,\n type FeedbackOp,\n type FeedbackReportSummary,\n type SubmitFeedbackInput,\n type SubmitFeedbackResult,\n} from \"./contract\";\nimport type { FeedbackSink } from \"./record\";\n\nexport interface IntakeClientConfig {\n /** The app's Supabase URL (`https://<project>.supabase.co`, or the local one). */\n endpoint: string;\n /** The publishable key, sent as `apikey` and `x-genesis-key` (an identifier, not a secret). */\n publishableKey: string;\n /** The signed-in person's access token, read per request. */\n getAccessToken?: () => string | null | undefined | Promise<string | null | undefined>;\n /** The function's name, if you deployed it under another. */\n functionName?: string;\n fetchImpl?: typeof fetch;\n}\n\n/** A non-2xx answer (`status` 0: it never arrived). `code` is the function's `error`. */\nexport class IntakeError extends Error {\n constructor(\n message: string,\n readonly status: number,\n readonly code?: string,\n ) {\n super(message);\n this.name = \"IntakeError\";\n }\n}\n\nexport interface IntakeClient {\n boot(signal?: AbortSignal): Promise<BootConfig>;\n submit(input: SubmitFeedbackInput, signal?: AbortSignal): Promise<SubmitFeedbackResult>;\n list(signal?: AbortSignal): Promise<FeedbackReportSummary[]>;\n}\n\nexport function createIntakeClient(config: IntakeClientConfig): IntakeClient {\n const url = `${config.endpoint.replace(/\\/$/, \"\")}/functions/v1/${config.functionName ?? \"genesis-support-intake\"}`;\n const f = config.fetchImpl ?? fetch;\n\n async function headers(): Promise<Record<string, string>> {\n const token = await config.getAccessToken?.();\n return {\n apikey: config.publishableKey,\n [WIDGET_HEADERS.key]: config.publishableKey,\n [WIDGET_HEADERS.contract]: String(CONTRACT_VERSION),\n ...(token ? { authorization: `Bearer ${token}` } : {}),\n };\n }\n\n async function send<T>(init: RequestInit): Promise<T> {\n let res: Response;\n try {\n res = await f(url, init);\n } catch (error) {\n throw new IntakeError(error instanceof Error ? error.message : \"network request failed\", 0, \"transport_error\");\n }\n if (!res.ok) {\n let code: string | undefined;\n try {\n code = ((await res.json()) as { error?: string }).error;\n } catch {\n // not JSON\n }\n throw new IntakeError(code ?? `${res.status} ${res.statusText}`, res.status, code);\n }\n return (await res.json()) as T;\n }\n\n const post = async <T,>(op: FeedbackOp, args: object, signal?: AbortSignal) =>\n // `op` last, so an `op` in args can never pick the operation.\n send<T>({ method: \"POST\", headers: { ...(await headers()), \"content-type\": \"application/json\" }, body: JSON.stringify({ ...args, op }), signal });\n\n return {\n boot: async (signal) => send<BootConfig>({ method: \"GET\", headers: await headers(), signal }),\n submit: (input, signal) => post<SubmitFeedbackResult>(\"submit\", input, signal),\n list: (signal) => post<FeedbackReportSummary[]>(\"list\", {}, signal),\n };\n}\n\n/**\n * A sink over the intake function. The function takes no screenshots (they\n * go to storage with a session), so attachments are reported as dropped.\n */\nexport function createIntakeSink(client: IntakeClient): FeedbackSink {\n return {\n async submit(record) {\n const result = await client.submit({\n type: record.type,\n title: record.title,\n body: record.message,\n route: record.context.route,\n appVersion: record.context.appVersion,\n envType: record.context.platform,\n context: record.context.extra,\n });\n return { id: result.reportId, attachmentsDropped: record.attachments?.length ?? 0 };\n },\n };\n}\n"]}
|
package/dist/native.d.ts
CHANGED
|
@@ -1,8 +1,8 @@
|
|
|
1
1
|
import * as react from 'react';
|
|
2
2
|
import { ComponentType, ReactNode } from 'react';
|
|
3
3
|
import { NativeBlock, NativeBlockRootProps } from '@marktiderman/genesis-native';
|
|
4
|
-
import { S as SUPPORT_BLOCK_ID, b as SupportPaths } from './block-
|
|
5
|
-
import {
|
|
4
|
+
import { S as SUPPORT_BLOCK_ID, b as SupportPaths } from './block-ClKVvk-d.js';
|
|
5
|
+
import { y as ScreenshotContentType, V as SupportSupabase, H as SupportChatClient, c as ContextCollector, O as SupportCopyOverrides, K as SupportClient, N as SupportCopy, o as FeedbackType, j as FeedbackDelivery, Q as SupportIdea, W as SupportThreadInfo, U as SupportReport } from './copy-D3rc0SCl.js';
|
|
6
6
|
import { InboxPageProps, ThreadPageProps } from '@marktiderman/genesis-ui-native/layouts';
|
|
7
7
|
import '@marktiderman/genesis-features/blocks';
|
|
8
8
|
import '@supabase/supabase-js';
|
|
@@ -70,6 +70,11 @@ interface PendingFeedback {
|
|
|
70
70
|
interface SupportOptions {
|
|
71
71
|
/** The app's Supabase client (migration 0006 applied). */
|
|
72
72
|
supabase: SupportSupabase;
|
|
73
|
+
/**
|
|
74
|
+
* genesis-chat's client (`createChatClient(supabase)`): with the support-chat block's migration
|
|
75
|
+
* applied, a report's thread runs on genesis-chat (live, with files). Without it, support_replies.
|
|
76
|
+
*/
|
|
77
|
+
chat?: SupportChatClient;
|
|
73
78
|
/** Opens a path (expo-router's `router.push`). */
|
|
74
79
|
navigate: (href: string) => void;
|
|
75
80
|
/** `Platform.OS`. Default from React Native. */
|
|
@@ -177,8 +182,27 @@ type ThreadProps = Pick<ThreadPageProps, "messages" | "composer" | "closedNotice
|
|
|
177
182
|
loading: boolean;
|
|
178
183
|
/** Read, and there is no such report (deleted, or not the person's): leave the screen. */
|
|
179
184
|
notFound: boolean;
|
|
185
|
+
/** `chat`: the thread runs on genesis-chat (the support-chat block), live; `replies`: support_replies, re-read every `pollMs`. */
|
|
186
|
+
mode: SupportThreadInfo["mode"] | null;
|
|
187
|
+
/** The report's genesis-chat conversation, for a screen that renders it with genesis-chat's own native parts. */
|
|
188
|
+
conversationId: string | null;
|
|
180
189
|
};
|
|
181
|
-
/**
|
|
190
|
+
/**
|
|
191
|
+
* Where a report's thread lives: its genesis-chat conversation (the support-chat block, with a
|
|
192
|
+
* `chat` client given to `createSupport`), or support_replies. For a screen of your own that
|
|
193
|
+
* renders the conversation with `@marktiderman/genesis-chat/native` (ChatThreadScreen).
|
|
194
|
+
*/
|
|
195
|
+
declare function useSupportConversation(reportId: string): {
|
|
196
|
+
mode: SupportThreadInfo["mode"] | null;
|
|
197
|
+
conversationId: string | null;
|
|
198
|
+
loading: boolean;
|
|
199
|
+
error: boolean;
|
|
200
|
+
};
|
|
201
|
+
/**
|
|
202
|
+
* One report as a conversation: the report itself, then the replies. Marks it read as it opens
|
|
203
|
+
* and as replies arrive. On genesis-chat (the support-chat block) it updates live and shows the
|
|
204
|
+
* photos in replies; otherwise it re-reads support_replies every `pollMs`.
|
|
205
|
+
*/
|
|
182
206
|
declare function useSupportThread(reportId: string, options?: {
|
|
183
207
|
pollMs?: number;
|
|
184
208
|
}): ThreadProps;
|
|
@@ -204,4 +228,4 @@ declare function SendFeedbackButton({ testID, screenshot }: {
|
|
|
204
228
|
screenshot?: boolean;
|
|
205
229
|
}): react.JSX.Element | null;
|
|
206
230
|
|
|
207
|
-
export { type AccelerometerLike, type AccelerometerMeasurement, type CaptureOptions, FeedbackForm, type FeedbackFormProps, type PendingFeedback, type PendingScreenshot, SUPPORT_QUERY_ROOT, ScreenshotAnnotator, type ScreenshotAnnotatorProps, SendFeedbackButton, type ShakeOptions, type SupportBlock, type SupportOptions, SupportProvider, type ViewShotLike, VoteButton, boxOf, createSupport, listenForShake, strokeDots, useIdeaBoard, useOptionalSupport, usePendingFeedback, useShippedIdeas, useSupport, useSupportInbox, useSupportThread, useUnreadReplies };
|
|
231
|
+
export { type AccelerometerLike, type AccelerometerMeasurement, type CaptureOptions, FeedbackForm, type FeedbackFormProps, type PendingFeedback, type PendingScreenshot, SUPPORT_QUERY_ROOT, ScreenshotAnnotator, type ScreenshotAnnotatorProps, SendFeedbackButton, type ShakeOptions, type SupportBlock, type SupportOptions, SupportProvider, type ViewShotLike, VoteButton, boxOf, createSupport, listenForShake, strokeDots, useIdeaBoard, useOptionalSupport, usePendingFeedback, useShippedIdeas, useSupport, useSupportConversation, useSupportInbox, useSupportThread, useUnreadReplies };
|
package/dist/native.js
CHANGED
|
@@ -1,5 +1,6 @@
|
|
|
1
|
-
import { supportPaths, createSupportClient, defineSupportBlock, SupportError } from './chunk-
|
|
2
|
-
import {
|
|
1
|
+
import { supportPaths, createSupportClient, defineSupportBlock, SupportError } from './chunk-QNUAKW7K.js';
|
|
2
|
+
import { createContextCollector, composeCollectors, FEEDBACK_TYPES } from './chunk-MMBNG2NK.js';
|
|
3
|
+
import { resolveSupportCopy, fill } from './chunk-OSB6G6Y3.js';
|
|
3
4
|
import { createContext, useContext, useSyncExternalStore, useRef, useState, useEffect, useMemo } from 'react';
|
|
4
5
|
import { Platform, Dimensions, PanResponder, View, Image, Modal, Alert } from 'react-native';
|
|
5
6
|
import { useQuery, useQueryClient, useMutation } from '@tanstack/react-query';
|
|
@@ -50,7 +51,7 @@ function useUnreadReplies() {
|
|
|
50
51
|
function createSupport(options) {
|
|
51
52
|
const copy = resolveSupportCopy(options.copy);
|
|
52
53
|
const paths = supportPaths(options.basePath);
|
|
53
|
-
const client = createSupportClient(options.supabase);
|
|
54
|
+
const client = createSupportClient(options.supabase, { chat: options.chat });
|
|
54
55
|
const platform = options.platform ?? Platform.OS;
|
|
55
56
|
let route = null;
|
|
56
57
|
let pending = null;
|
|
@@ -511,14 +512,31 @@ function firstLine(text) {
|
|
|
511
512
|
const line = text.split("\n").find((l) => l.trim()) ?? "";
|
|
512
513
|
return line.length > 120 ? `${line.slice(0, 119)}\u2026` : line;
|
|
513
514
|
}
|
|
515
|
+
function useSupportConversation(reportId) {
|
|
516
|
+
const { client } = useSupport();
|
|
517
|
+
const query = useQuery({ queryKey: key("thread", reportId), queryFn: () => client.thread(reportId), staleTime: Infinity });
|
|
518
|
+
return { mode: query.data?.mode ?? null, conversationId: query.data?.conversationId ?? null, loading: query.isPending, error: query.isError };
|
|
519
|
+
}
|
|
520
|
+
function fileLabels(reply, copy) {
|
|
521
|
+
return (reply.attachments ?? []).filter((a) => a.kind !== "image").map((a) => a.kind === "voice" ? copy.thread.voiceNote : a.kind === "video" ? copy.thread.video : a.name || copy.thread.file);
|
|
522
|
+
}
|
|
514
523
|
var CLOSED = /* @__PURE__ */ new Set(["addressed", "wont_fix"]);
|
|
515
524
|
function useSupportThread(reportId, options = {}) {
|
|
516
525
|
const block = useSupport();
|
|
517
526
|
const { copy, client } = block;
|
|
518
527
|
const queryClient = useQueryClient();
|
|
519
|
-
const
|
|
528
|
+
const where = useQuery({ queryKey: key("thread", reportId), queryFn: () => client.thread(reportId), staleTime: Infinity });
|
|
529
|
+
const live = where.data?.mode === "chat";
|
|
530
|
+
const poll = live ? false : options.pollMs ?? 3e4;
|
|
520
531
|
const report = useQuery({ queryKey: key("report", reportId), queryFn: () => client.report(reportId), refetchInterval: poll });
|
|
521
|
-
const replies = useQuery({ queryKey: key("replies", reportId), queryFn: () => client.replies(reportId), refetchInterval: poll });
|
|
532
|
+
const replies = useQuery({ queryKey: key("replies", reportId), queryFn: () => client.replies(reportId), refetchInterval: poll, enabled: where.isFetched });
|
|
533
|
+
useEffect(() => {
|
|
534
|
+
if (!live) return;
|
|
535
|
+
return client.subscribe(reportId, () => {
|
|
536
|
+
void queryClient.invalidateQueries({ queryKey: key("replies", reportId) });
|
|
537
|
+
void queryClient.invalidateQueries({ queryKey: key("report", reportId) });
|
|
538
|
+
});
|
|
539
|
+
}, [client, reportId, live, queryClient]);
|
|
522
540
|
const attachments = useQuery({ queryKey: key("attachments", reportId), queryFn: () => client.attachments(reportId), staleTime: 50 * 6e4 });
|
|
523
541
|
const [draft, setDraft] = useState("");
|
|
524
542
|
const [failed, setFailed] = useState([]);
|
|
@@ -545,12 +563,13 @@ function useSupportThread(reportId, options = {}) {
|
|
|
545
563
|
timestamp: r.createdAt,
|
|
546
564
|
attachments: (attachments.data ?? []).filter((a) => a.url).map((a) => ({ id: a.id, uri: a.url, width: a.width ?? void 0, height: a.height ?? void 0, accessibilityLabel: copy.thread.screenshot }))
|
|
547
565
|
};
|
|
548
|
-
const rest = (replies.data ?? []).map((reply) => ({
|
|
566
|
+
const rest = (replies.data ?? []).filter((reply) => !reply.internal).map((reply) => ({
|
|
549
567
|
id: reply.id,
|
|
550
|
-
body: reply.body,
|
|
568
|
+
body: [reply.body, ...fileLabels(reply, copy)].filter((part) => part.trim()).join("\n"),
|
|
551
569
|
mine: reply.mine,
|
|
552
570
|
authorName: reply.fromStaff ? copy.thread.support : void 0,
|
|
553
|
-
timestamp: reply.createdAt
|
|
571
|
+
timestamp: reply.createdAt,
|
|
572
|
+
attachments: (reply.attachments ?? []).filter((a) => a.kind === "image" && a.url).map((a) => ({ id: a.id, uri: a.url, width: a.width ?? void 0, height: a.height ?? void 0, accessibilityLabel: copy.thread.screenshot }))
|
|
554
573
|
}));
|
|
555
574
|
const pending = send.isPending && send.variables ? [{ id: "sending", body: send.variables, mine: true, timestamp: (/* @__PURE__ */ new Date()).toISOString(), status: "sending" }] : [];
|
|
556
575
|
const failedMessages = failed.map((f) => ({
|
|
@@ -573,6 +592,8 @@ function useSupportThread(reportId, options = {}) {
|
|
|
573
592
|
report: r,
|
|
574
593
|
loading: report.isPending,
|
|
575
594
|
notFound: report.isSuccess && r === null,
|
|
595
|
+
mode: where.data?.mode ?? null,
|
|
596
|
+
conversationId: where.data?.conversationId ?? null,
|
|
576
597
|
labels: { you: copy.thread.you, sending: copy.thread.sending, notSent: copy.thread.notSent, retryHint: copy.thread.retryHint },
|
|
577
598
|
messages,
|
|
578
599
|
readAt: r?.staffReadAt ?? null,
|
|
@@ -717,6 +738,6 @@ function SendFeedbackButton({ testID = "genesis-support-send-feedback-button", s
|
|
|
717
738
|
return /* @__PURE__ */ jsx(NativeButton, { variant: "outline", onPress: () => void block.openFeedback({ screenshot }), testID, children: block.copy.settings.sendFeedback });
|
|
718
739
|
}
|
|
719
740
|
|
|
720
|
-
export { FeedbackForm, SUPPORT_QUERY_ROOT, ScreenshotAnnotator, SendFeedbackButton, SupportProvider, VoteButton, boxOf, createSupport, listenForShake, strokeDots, useIdeaBoard, useOptionalSupport, usePendingFeedback, useShippedIdeas, useSupport, useSupportInbox, useSupportThread, useUnreadReplies };
|
|
741
|
+
export { FeedbackForm, SUPPORT_QUERY_ROOT, ScreenshotAnnotator, SendFeedbackButton, SupportProvider, VoteButton, boxOf, createSupport, listenForShake, strokeDots, useIdeaBoard, useOptionalSupport, usePendingFeedback, useShippedIdeas, useSupport, useSupportConversation, useSupportInbox, useSupportThread, useUnreadReplies };
|
|
721
742
|
//# sourceMappingURL=native.js.map
|
|
722
743
|
//# sourceMappingURL=native.js.map
|