'use client'; import { useState, useEffect, useCallback } from 'react'; import { createClient } from '@/utils/supabase'; import type { RealtimeChannel } from '@supabase/supabase-js'; export type ConnectionStatus = | 'connecting' | 'connected' | 'disconnected' | 'updating'; // Singleton state let sharedChannel: RealtimeChannel | null = null; let sharedConnectionStatus: ConnectionStatus = 'disconnected'; const subscribers = new Set<(status: ConnectionStatus) => void>(); const statusUpdateCallbacks = new Set<() => void>(); //const subscribers: Set<(status: ConnectionStatus) => void> = new Set(); //const statusUpdateCallbacks: Set<() => void> = new Set(); let reconnectAttempts = 0; let reconnectTimeout: NodeJS.Timeout | undefined; const supabase = createClient(); const notifySubscribers = (status: ConnectionStatus) => { sharedConnectionStatus = status; subscribers.forEach(callback => callback(status)); }; const notifyStatusUpdate = () => { statusUpdateCallbacks.forEach(callback => callback()); }; const cleanup = () => { if (reconnectTimeout) { clearTimeout(reconnectTimeout); reconnectTimeout = undefined; } if (sharedChannel) { supabase.removeChannel(sharedChannel).catch((error) => { console.error('Error removing shared channel:', error); }); sharedChannel = null; } }; const connect = () => { if (sharedChannel) return; // Already connected or connecting cleanup(); notifySubscribers('connecting'); const channel = supabase .channel('shared_status_updates', { config: { broadcast: {self: true }} }) .on('broadcast', { event: 'status_updated' }, () => { notifyStatusUpdate(); }) .subscribe((status) => { // eslint-disable-next-line @typescript-eslint/no-unsafe-enum-comparison if (status === 'SUBSCRIBED') { notifySubscribers('connected'); reconnectAttempts = 0; // eslint-disable-next-line @typescript-eslint/no-unsafe-enum-comparison } else if (status === 'CHANNEL_ERROR' || status === 'CLOSED') { notifySubscribers('disconnected'); if (reconnectAttempts < 5) { reconnectAttempts++; const delay = 2000 * reconnectAttempts; reconnectTimeout = setTimeout(() => { if (subscribers.size > 0) { // Only reconnect if there are active subscribers connect(); } }, delay); } } }); sharedChannel = channel; }; const disconnect = () => { cleanup(); notifySubscribers('disconnected'); }; export const useSharedStatusSubscription = (onStatusUpdate?: () => void) => { const [connectionStatus, setConnectionStatus] = useState(sharedConnectionStatus); useEffect(() => { // Subscribe to status changes subscribers.add(setConnectionStatus); // Subscribe to status updates if (onStatusUpdate) { statusUpdateCallbacks.add(onStatusUpdate); } // Connect if this is the first subscriber if (subscribers.size === 1) { const timeout = setTimeout(connect, 1000); return () => clearTimeout(timeout); } return () => { // Cleanup subscriptions subscribers.delete(setConnectionStatus); if (onStatusUpdate) { statusUpdateCallbacks.delete(onStatusUpdate); } // Disconnect if no more subscribers if (subscribers.size === 0) { disconnect(); } }; }, [onStatusUpdate]); const reconnect = useCallback(() => { reconnectAttempts = 0; connect(); }, []); return { connectionStatus, connect: reconnect, disconnect, }; };