import { listPeerReadsForMessages, markMessagesRead } from '@chat-app/shared/chat'; import { useCallback, useEffect, useMemo, useState } from 'react'; import { supabase } from './supabase'; // Tracks which of our own messages the peer has read. For groups this would // return a per-message Map; M1 is DM-focused so we just return // a Set of message ids read by the single peer. export function useMessageReads( messageIds: string[], peerUserId: string | undefined, ): { peerReadSet: Set; refresh: () => Promise } { const idsKey = useMemo(() => messageIds.join(','), [messageIds]); const [peerReadSet, setPeerReadSet] = useState>(new Set()); const refresh = useCallback(async () => { if (!peerUserId || messageIds.length === 0) { setPeerReadSet(new Set()); return; } try { const s = await listPeerReadsForMessages(supabase, messageIds, peerUserId); setPeerReadSet(s); } catch (err: unknown) { console.error('listPeerReadsForMessages failed', err); } // eslint-disable-next-line react-hooks/exhaustive-deps }, [peerUserId, idsKey]); useEffect(() => { void refresh(); if (!peerUserId) return; const channel = supabase .channel('reads:' + peerUserId) .on( 'postgres_changes', { event: 'INSERT', schema: 'public', table: 'message_reads', filter: 'user_id=eq.' + peerUserId, }, () => { void refresh(); }, ) .subscribe(); return () => { void supabase.removeChannel(channel); }; }, [peerUserId, refresh]); return { peerReadSet, refresh }; } // Mark a batch of messages as read. Caller uses this when they arrive while // the conversation is actively being viewed. export async function markRead(messageIds: string[]): Promise { await markMessagesRead(supabase, messageIds); }