import type { AppSupabaseClient } from '../supabase/client'; import type { MemberRole } from '../supabase/types'; async function currentUserId(client: AppSupabaseClient): Promise { const { data, error } = await client.auth.getUser(); if (error) throw error; if (!data.user) throw new Error('not authenticated'); return data.user.id; } export interface CreateGroupParams { client: AppSupabaseClient; name: string; memberUserIds: string[]; } // Client-side 3-step create: conversation → self as admin → peers as members. // On any peer-insert failure the conversation still survives (partial group is // still usable, admin can retry). On self-insert failure we delete the empty // conversation to avoid orphans. export async function createGroup(params: CreateGroupParams): Promise { const myId = await currentUserId(params.client); const trimmed = params.name.trim(); if (trimmed.length === 0) throw new Error('group name required'); // Generate the uuid client-side so we don't need a post-insert SELECT on // conversations — the SELECT policy requires membership, which only exists // AFTER we insert the creator's member row in step 2. const conversationId = crypto.randomUUID(); const { error: cErr } = await params.client .from('conversations') .insert({ id: conversationId, type: 'group', name: trimmed, created_by: myId, } as never); if (cErr) throw cErr; const { error: sErr } = await params.client.from('conversation_members').insert({ conversation_id: conversationId, user_id: myId, role: 'admin', accepted: true, } as never); if (sErr) { await params.client.from('conversations').delete().eq('id', conversationId); throw sErr; } const peerIds = Array.from(new Set(params.memberUserIds)).filter((id) => id !== myId); if (peerIds.length > 0) { const rows = peerIds.map((user_id) => ({ conversation_id: conversationId, user_id, role: 'member' as MemberRole, accepted: true, })); const { error: mErr } = await params.client .from('conversation_members') .insert(rows as never); if (mErr) throw mErr; } return conversationId; } // Add an accepted friend to a group. RLS allows the insert only when the // caller is admin/mod of the conversation AND is friends with `userId`. export async function addGroupMember( client: AppSupabaseClient, conversationId: string, userId: string, ): Promise { const { error } = await client.from('conversation_members').insert({ conversation_id: conversationId, user_id: userId, role: 'member', accepted: true, } as never); if (error) throw error; } export async function leaveGroup( client: AppSupabaseClient, conversationId: string, ): Promise { const myId = await currentUserId(client); const { error } = await client .from('conversation_members') .delete() .eq('conversation_id', conversationId) .eq('user_id', myId); if (error) throw error; }