2.5.1
Slow down API calls to every 15mins on non-pro
This commit is contained in:
@@ -52,7 +52,19 @@ export function useAppState() {
|
|||||||
setLocation(loc);
|
setLocation(loc);
|
||||||
};
|
};
|
||||||
|
|
||||||
const { forecast, airQuality, loading, error, now, fetchedAt } = useForecast(location);
|
// Pro tier flag is read early so useForecast can pick its refresh cadence
|
||||||
|
// (Pro: 5 min, free: 15 min). Full setup notes in the PRO TIER section below.
|
||||||
|
const [isPro, setIsPro] = useState(() => {
|
||||||
|
const params = new URLSearchParams(window.location.search);
|
||||||
|
if (params.get('pro') === '1') {
|
||||||
|
localStorage.setItem('sunscope_pro', '1');
|
||||||
|
window.history.replaceState({}, '', window.location.pathname);
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
return localStorage.getItem('sunscope_pro') === '1';
|
||||||
|
});
|
||||||
|
|
||||||
|
const { forecast, airQuality, loading, error, now, fetchedAt } = useForecast(location, isPro);
|
||||||
|
|
||||||
const [searchQuery, setSearchQuery] = useState('');
|
const [searchQuery, setSearchQuery] = useState('');
|
||||||
const [searchResults, setSearchResults] = useState([]);
|
const [searchResults, setSearchResults] = useState([]);
|
||||||
@@ -109,15 +121,7 @@ export function useAppState() {
|
|||||||
};
|
};
|
||||||
|
|
||||||
// ── 3. PRO TIER ──────────────────────────────────────────────────────
|
// ── 3. PRO TIER ──────────────────────────────────────────────────────
|
||||||
const [isPro, setIsPro] = useState(() => {
|
// (isPro is declared near the top so useForecast can read it; see above.)
|
||||||
const params = new URLSearchParams(window.location.search);
|
|
||||||
if (params.get('pro') === '1') {
|
|
||||||
localStorage.setItem('sunscope_pro', '1');
|
|
||||||
window.history.replaceState({}, '', window.location.pathname);
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
return localStorage.getItem('sunscope_pro') === '1';
|
|
||||||
});
|
|
||||||
|
|
||||||
// ── 4. PROFILE + COLUMN VISIBILITY ───────────────────────────────────
|
// ── 4. PROFILE + COLUMN VISIBILITY ───────────────────────────────────
|
||||||
const [activeProfile, setActiveProfile] = useState(() => {
|
const [activeProfile, setActiveProfile] = useState(() => {
|
||||||
|
|||||||
@@ -12,13 +12,15 @@
|
|||||||
// c. Stale soil cache — used if ICON fetch fails.
|
// c. Stale soil cache — used if ICON fetch fails.
|
||||||
// 3. Fetch /v1/air-quality on location change, with a 6-hour
|
// 3. Fetch /v1/air-quality on location change, with a 6-hour
|
||||||
// localStorage cache (AQI / pollen update slowly).
|
// localStorage cache (AQI / pollen update slowly).
|
||||||
// 4. Auto-refresh forecast every 15 minutes (matches Open-Meteo
|
// 4. Auto-refresh forecast every 5 minutes for Pro, 15 minutes for
|
||||||
// update cadence). Air quality only refetches if cache is stale.
|
// free (Open-Meteo updates ~every 15 min). Air quality only
|
||||||
|
// refetches if cache is stale.
|
||||||
// 5. Tick `now` every 2 minutes to keep the current-row highlight
|
// 5. Tick `now` every 2 minutes to keep the current-row highlight
|
||||||
// accurate without any API cost.
|
// accurate without any API cost.
|
||||||
//
|
//
|
||||||
// Inputs:
|
// Inputs:
|
||||||
// location - { lat, lon, name, country }
|
// location - { lat, lon, name, country }
|
||||||
|
// isPro - boolean; true tightens auto-refresh to 5 min
|
||||||
//
|
//
|
||||||
// Outputs:
|
// Outputs:
|
||||||
// forecast - raw /v1/forecast response, or null
|
// forecast - raw /v1/forecast response, or null
|
||||||
@@ -119,7 +121,7 @@ function mergeSoil(forecastData, soilHourly) {
|
|||||||
return { ...forecastData, hourly: h };
|
return { ...forecastData, hourly: h };
|
||||||
}
|
}
|
||||||
|
|
||||||
export function useForecast(location) {
|
export function useForecast(location, isPro = false) {
|
||||||
const [forecast, setForecast] = useState(null);
|
const [forecast, setForecast] = useState(null);
|
||||||
const [airQuality, setAirQuality] = useState(null);
|
const [airQuality, setAirQuality] = useState(null);
|
||||||
const [loading, setLoading] = useState(false);
|
const [loading, setLoading] = useState(false);
|
||||||
@@ -235,8 +237,11 @@ export function useForecast(location) {
|
|||||||
return () => clearInterval(id);
|
return () => clearInterval(id);
|
||||||
}, []);
|
}, []);
|
||||||
|
|
||||||
// ─── 15-MINUTE REFRESH (matches Open-Meteo update cadence) ────────
|
// ─── AUTO REFRESH (Pro: 5 min, free: 15 min) ─────────────────────
|
||||||
|
// Open-Meteo updates roughly every 15 min; Pro users get a tighter
|
||||||
|
// poll so the current-conditions row stays fresher.
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
|
const refreshMs = isPro ? FIVE_MIN_MS : FIFTEEN_MIN_MS;
|
||||||
const id = setInterval(() => {
|
const id = setInterval(() => {
|
||||||
async function refresh() {
|
async function refresh() {
|
||||||
const key = forecastCacheKey(location);
|
const key = forecastCacheKey(location);
|
||||||
@@ -254,9 +259,9 @@ export function useForecast(location) {
|
|||||||
loadAirQuality(location);
|
loadAirQuality(location);
|
||||||
}
|
}
|
||||||
refresh();
|
refresh();
|
||||||
}, FIFTEEN_MIN_MS);
|
}, refreshMs);
|
||||||
return () => clearInterval(id);
|
return () => clearInterval(id);
|
||||||
}, [location]);
|
}, [location, isPro]);
|
||||||
|
|
||||||
return { forecast, airQuality, loading, error, now, fetchedAt };
|
return { forecast, airQuality, loading, error, now, fetchedAt };
|
||||||
}
|
}
|
||||||
Reference in New Issue
Block a user