52 lines
1.4 KiB
TypeScript
52 lines
1.4 KiB
TypeScript
import type { PresenceState } from '@chat-app/shared/supabase';
|
|
import { useEffect, useState } from 'react';
|
|
|
|
import { supabase } from './supabase';
|
|
|
|
// Subscribe to a single peer's presence_state via Supabase realtime.
|
|
// Returns null until the first row arrives, or when userId is undefined.
|
|
export function usePeerPresence(userId: string | undefined): PresenceState | null {
|
|
const [presence, setPresence] = useState<PresenceState | null>(null);
|
|
|
|
useEffect(() => {
|
|
if (!userId) {
|
|
setPresence(null);
|
|
return;
|
|
}
|
|
let cancelled = false;
|
|
|
|
void supabase
|
|
.from('profiles')
|
|
.select('presence_state')
|
|
.eq('user_id', userId)
|
|
.maybeSingle()
|
|
.then(({ data }) => {
|
|
if (!cancelled) setPresence(data?.presence_state ?? null);
|
|
});
|
|
|
|
const channel = supabase
|
|
.channel('peer-presence:' + userId)
|
|
.on(
|
|
'postgres_changes',
|
|
{
|
|
event: 'UPDATE',
|
|
schema: 'public',
|
|
table: 'profiles',
|
|
filter: 'user_id=eq.' + userId,
|
|
},
|
|
(payload: { new: Record<string, unknown> }) => {
|
|
const next = payload.new['presence_state'];
|
|
if (typeof next === 'string') setPresence(next as PresenceState);
|
|
},
|
|
)
|
|
.subscribe();
|
|
|
|
return () => {
|
|
cancelled = true;
|
|
void supabase.removeChannel(channel);
|
|
};
|
|
}, [userId]);
|
|
|
|
return presence;
|
|
}
|