import { useState, useCallback, useEffect, useRef } from 'react'; import { api } from '../api'; import { takePrefetchOrFetch } from '../prefetch'; import { toast } from '../components/ui/sonner'; import type { HealthStatus, RadioConfig, RadioConfigUpdate } from '../types'; export function useRadioControl() { const [health, setHealth] = useState(null); const [config, setConfig] = useState(null); const prevHealthRef = useRef(null); const rebootPollTokenRef = useRef(0); // Cancel any in-flight reboot polling on unmount useEffect(() => { return () => { rebootPollTokenRef.current += 1; }; }, []); const fetchConfig = useCallback(async () => { try { const data = await takePrefetchOrFetch('config', api.getRadioConfig); setConfig(data); } catch (err) { console.error('Failed to fetch config:', err); } }, []); const handleSaveConfig = useCallback( async (update: RadioConfigUpdate) => { await api.updateRadioConfig(update); await fetchConfig(); }, [fetchConfig] ); const handleSetPrivateKey = useCallback( async (key: string) => { await api.setPrivateKey(key); await fetchConfig(); }, [fetchConfig] ); const handleReboot = useCallback(async () => { await api.rebootRadio(); setHealth((prev) => prev ? { ...prev, radio_connected: false, radio_initializing: false } : prev ); const pollToken = ++rebootPollTokenRef.current; const pollUntilReconnected = async () => { for (let i = 0; i < 30; i++) { await new Promise((r) => setTimeout(r, 1000)); if (rebootPollTokenRef.current !== pollToken) return; try { const data = await api.getHealth(); if (rebootPollTokenRef.current !== pollToken) return; setHealth(data); if (data.radio_connected) { fetchConfig(); return; } } catch { // Keep polling } } }; pollUntilReconnected(); }, [fetchConfig]); const handleDisconnect = useCallback(async () => { await api.disconnectRadio(); const pausedHealth = await api.getHealth(); setHealth(pausedHealth); }, []); const handleReconnect = useCallback(async () => { await api.reconnectRadio(); const refreshedHealth = await api.getHealth(); setHealth(refreshedHealth); if (refreshedHealth.radio_connected) { await fetchConfig(); } }, [fetchConfig]); const handleAdvertise = useCallback(async () => { try { await api.sendAdvertisement(); toast.success('Advertisement sent'); } catch (err) { console.error('Failed to send advertisement:', err); toast.error('Failed to send advertisement', { description: err instanceof Error ? err.message : 'Check radio connection', }); } }, []); const handleHealthRefresh = useCallback(async () => { try { const data = await api.getHealth(); setHealth(data); } catch (err) { console.error('Failed to refresh health:', err); } }, []); return { health, setHealth, config, setConfig, prevHealthRef, fetchConfig, handleSaveConfig, handleSetPrivateKey, handleReboot, handleDisconnect, handleReconnect, handleAdvertise, handleHealthRefresh, }; }