Files
ChatApp/packages/shared/src/chat/whiteboards.ts
T
byGalax 3df7cc01ea feat(P4B.T2): WhiteboardPayload + shared whiteboards CRUD wrappers + tests
Adds WhiteboardPayload wire-format variant to MessagePayload/ParsedMessagePayload,
creates the whiteboards.ts CRUD wrapper (createWhiteboard, listWhiteboardStrokes,
insertWhiteboardStroke, clearWhiteboardStrokes), writes 5 unit tests (38/38 pass),
re-exports from chat/index.ts, and registers conversation_whiteboards +
whiteboard_strokes in packages/db-types so typecheck passes cleanly.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-05-16 20:23:45 +02:00

97 lines
2.6 KiB
TypeScript

import type { Json } from '@chat-app/db-types';
import type { AppSupabaseClient } from '../supabase/client';
export interface Whiteboard {
id: string;
conversationId: string;
ownerUserId: string;
createdAt: string;
}
export interface WhiteboardStroke {
id: string;
whiteboardId: string;
authorUserId: string;
strokeJson: unknown;
createdAt: string;
}
export async function createWhiteboard(
client: AppSupabaseClient,
conversationId: string,
): Promise<Whiteboard> {
const { data: session } = await client.auth.getUser();
if (!session.user) throw new Error('not authenticated');
const { data, error } = await client
.from('conversation_whiteboards')
.insert({
conversation_id: conversationId,
owner_user_id: session.user.id,
})
.select('id, conversation_id, owner_user_id, created_at')
.single();
if (error) throw error;
return {
id: data.id,
conversationId: data.conversation_id,
ownerUserId: data.owner_user_id,
createdAt: data.created_at,
};
}
export async function listWhiteboardStrokes(
client: AppSupabaseClient,
whiteboardId: string,
): Promise<WhiteboardStroke[]> {
const { data, error } = await client
.from('whiteboard_strokes')
.select('id, whiteboard_id, author_user_id, stroke_json, created_at')
.eq('whiteboard_id', whiteboardId)
.order('created_at', { ascending: true });
if (error) throw error;
return data.map((row) => ({
id: row.id,
whiteboardId: row.whiteboard_id,
authorUserId: row.author_user_id,
strokeJson: row.stroke_json,
createdAt: row.created_at,
}));
}
export async function insertWhiteboardStroke(
client: AppSupabaseClient,
params: { whiteboardId: string; strokeJson: unknown },
): Promise<WhiteboardStroke> {
const { data: session } = await client.auth.getUser();
if (!session.user) throw new Error('not authenticated');
const { data, error } = await client
.from('whiteboard_strokes')
.insert({
whiteboard_id: params.whiteboardId,
author_user_id: session.user.id,
stroke_json: params.strokeJson as unknown as Json,
})
.select('id, whiteboard_id, author_user_id, stroke_json, created_at')
.single();
if (error) throw error;
return {
id: data.id,
whiteboardId: data.whiteboard_id,
authorUserId: data.author_user_id,
strokeJson: data.stroke_json,
createdAt: data.created_at,
};
}
export async function clearWhiteboardStrokes(
client: AppSupabaseClient,
whiteboardId: string,
): Promise<void> {
const { error } = await client
.from('whiteboard_strokes')
.delete()
.eq('whiteboard_id', whiteboardId);
if (error) throw error;
}