Weather Station / ECOWITT / DNT
0

Configure Feed

Select the types of activity you want to include in your feed.

at master 3.3 kB View raw
1"use client"; 2 3import React, { createContext, useCallback, useContext, useEffect, useState } from 'react'; 4import { API_ENDPOINTS } from '@/constants'; 5 6type RTData = any; 7 8/** 9 * Defines the shape of the RealtimeContext. 10 */ 11interface RealtimeContextType { 12 /** The most recent real-time data object. */ 13 data: RTData | null; 14 /** Any error message from the last fetch attempt. */ 15 error: string | null; 16 /** True if data is currently being fetched. */ 17 loading: boolean; 18 /** The timestamp of the last successful data update. */ 19 lastUpdated: Date | null; 20 /** A function to manually trigger a data refetch. */ 21 refetch: () => void; 22} 23 24const RealtimeContext = createContext<RealtimeContextType | undefined>(undefined); 25 26/** 27 * A React context provider that fetches and manages real-time weather data. 28 * It periodically refetches the data and makes it available to all child components. 29 * @param props - The component props. 30 * @param props.children - The child components that will consume the context. 31 * @returns A RealtimeContext.Provider component. 32 */ 33export function RealtimeProvider({ children }: { children: React.ReactNode }) { 34 const [data, setData] = useState<RTData | null>(null); 35 const [error, setError] = useState<string | null>(null); 36 const [loading, setLoading] = useState<boolean>(false); 37 const [lastUpdated, setLastUpdated] = useState<Date | null>(null); 38 39 const fetchData = useCallback(async () => { 40 console.log('[RealtimeContext] fetchData called'); 41 try { 42 setLoading(true); 43 setError(null); 44 const res = await fetch(API_ENDPOINTS.RT_LAST, { cache: "no-store" }); 45 if (!res.ok) throw new Error(`HTTP ${res.status}`); 46 const rec = await res.json(); 47 if (!rec || rec.ok === false) { 48 const msg = rec?.error || 'No data available'; 49 setError(msg); 50 return; 51 } 52 setData(rec.data ?? null); 53 setLastUpdated(rec.updatedAt ? new Date(rec.updatedAt) : new Date()); 54 } catch (e: any) { 55 setError(e?.message || String(e)); 56 } finally { 57 setLoading(false); 58 } 59 }, []); 60 61 useEffect(() => { 62 console.log('[RealtimeContext] useEffect running, setting up interval'); 63 fetchData(); 64 const refreshMs = Number(process.env.NEXT_PUBLIC_RT_REFRESH_MS || 300000); // default 5 min 65 const id = setInterval(() => { 66 console.log('[RealtimeContext] Interval tick'); 67 fetchData(); 68 }, isFinite(refreshMs) && refreshMs > 0 ? refreshMs : 300000); 69 return () => { 70 console.log('[RealtimeContext] Cleaning up interval'); 71 clearInterval(id); 72 }; 73 }, []); // Remove fetchData from dependencies to prevent infinite loop 74 75 return ( 76 <RealtimeContext.Provider value={{ 77 data, 78 error, 79 loading, 80 lastUpdated, 81 refetch: fetchData 82 }}> 83 {children} 84 </RealtimeContext.Provider> 85 ); 86} 87 88/** 89 * A custom hook to access the real-time weather data context. 90 * Throws an error if used outside of a `RealtimeProvider`. 91 * @returns The real-time context, including data, loading state, errors, and a refetch function. 92 */ 93export function useRealtime() { 94 const context = useContext(RealtimeContext); 95 if (context === undefined) { 96 throw new Error('useRealtime must be used within a RealtimeProvider'); 97 } 98 return context; 99}