mirror of
https://github.com/pacnpal/thrilltrack-explorer.git
synced 2025-12-20 12:31:26 -05:00
102 lines
3.3 KiB
TypeScript
102 lines
3.3 KiB
TypeScript
import { useEffect, useRef, useState, useCallback } from 'react';
|
|
import { supabase } from '@/integrations/supabase/client';
|
|
import { RealtimeChannel } from '@supabase/supabase-js';
|
|
import { useUserRole } from './useUserRole';
|
|
|
|
type ConnectionState = 'connecting' | 'connected' | 'disconnected' | 'error';
|
|
|
|
interface UseRealtimeSubmissionsOptions {
|
|
onInsert?: (payload: any) => void;
|
|
onUpdate?: (payload: any) => void;
|
|
onDelete?: (payload: any) => void;
|
|
enabled?: boolean;
|
|
}
|
|
|
|
export const useRealtimeSubmissions = (options: UseRealtimeSubmissionsOptions = {}) => {
|
|
const { onInsert, onUpdate, onDelete, enabled = true } = options;
|
|
const { isModerator, loading: roleLoading } = useUserRole();
|
|
|
|
const [channel, setChannel] = useState<RealtimeChannel | null>(null);
|
|
const [connectionState, setConnectionState] = useState<ConnectionState>('disconnected');
|
|
|
|
// Only enable realtime when user is confirmed as moderator
|
|
const realtimeEnabled = enabled && !roleLoading && isModerator();
|
|
|
|
// Use refs to store latest callbacks without triggering re-subscriptions
|
|
const onInsertRef = useRef(onInsert);
|
|
const onUpdateRef = useRef(onUpdate);
|
|
const onDeleteRef = useRef(onDelete);
|
|
|
|
// Update refs when callbacks change
|
|
useEffect(() => {
|
|
onInsertRef.current = onInsert;
|
|
onUpdateRef.current = onUpdate;
|
|
onDeleteRef.current = onDelete;
|
|
}, [onInsert, onUpdate, onDelete]);
|
|
|
|
const reconnect = useCallback(() => {
|
|
if (channel) {
|
|
supabase.removeChannel(channel);
|
|
}
|
|
setConnectionState('connecting');
|
|
}, [channel]);
|
|
|
|
useEffect(() => {
|
|
if (!realtimeEnabled) {
|
|
console.log('[Realtime:content-submissions] Realtime disabled');
|
|
return;
|
|
}
|
|
|
|
console.log('[Realtime:content-submissions] Creating new broadcast channel');
|
|
setConnectionState('connecting');
|
|
|
|
const setupChannel = async () => {
|
|
// Set auth token for private channel
|
|
await supabase.realtime.setAuth();
|
|
|
|
const newChannel = supabase
|
|
.channel('moderation:content_submissions', {
|
|
config: { private: true },
|
|
})
|
|
.on('broadcast', { event: 'INSERT' }, (payload) => {
|
|
console.log('Submission inserted:', payload);
|
|
onInsertRef.current?.(payload);
|
|
})
|
|
.on('broadcast', { event: 'UPDATE' }, (payload) => {
|
|
console.log('Submission updated:', payload);
|
|
onUpdateRef.current?.(payload);
|
|
})
|
|
.on('broadcast', { event: 'DELETE' }, (payload) => {
|
|
console.log('Submission deleted:', payload);
|
|
onDeleteRef.current?.(payload);
|
|
})
|
|
.subscribe((status) => {
|
|
console.log('[Realtime:content-submissions] Subscription status:', status);
|
|
|
|
if (status === 'SUBSCRIBED') {
|
|
setConnectionState('connected');
|
|
} else if (status === 'CHANNEL_ERROR') {
|
|
setConnectionState('error');
|
|
} else if (status === 'TIMED_OUT') {
|
|
setConnectionState('disconnected');
|
|
} else if (status === 'CLOSED') {
|
|
setConnectionState('disconnected');
|
|
}
|
|
});
|
|
|
|
setChannel(newChannel);
|
|
};
|
|
|
|
setupChannel();
|
|
|
|
return () => {
|
|
if (channel) {
|
|
console.log('[Realtime:content-submissions] Cleaning up channel');
|
|
supabase.removeChannel(channel);
|
|
}
|
|
};
|
|
}, [realtimeEnabled]);
|
|
|
|
return { channel, connectionState, reconnect };
|
|
};
|