From 292f42f05e8f2286174b7eab93561bff81c22adf Mon Sep 17 00:00:00 2001 From: Suprita Naik Date: Wed, 12 Aug 2026 12:16:58 +0530 Subject: [PATCH] refactor: abstract community reports localStorage access --- src/components/LocationMap.jsx | 40 ++-------------------- src/hooks/useCommunityReports.js | 58 ++++++++++++++++++++++++++++++++ 2 files changed, 60 insertions(+), 38 deletions(-) create mode 100644 src/hooks/useCommunityReports.js diff --git a/src/components/LocationMap.jsx b/src/components/LocationMap.jsx index 5ce4f8e..ef04f37 100644 --- a/src/components/LocationMap.jsx +++ b/src/components/LocationMap.jsx @@ -4,6 +4,7 @@ import { useState, useEffect } from 'react'; import L from 'leaflet'; import { eventBus } from '../core/events'; import { getPollutantColor } from '../services/airQualityService'; +import { useCommunityReports } from '../hooks/useCommunityReports'; import PropTypes from "prop-types"; const COMMUNITY_REPORTS_STORAGE_KEY = 'pollution-community-reports'; @@ -18,24 +19,6 @@ const POLLUTANT_LAYERS = [ { key: 'carbon_monoxide', label: 'CO', limit: 4000 }, ]; -function readGeotaggedCommunityReports() { - try { - const raw = localStorage.getItem(COMMUNITY_REPORTS_STORAGE_KEY); - if (!raw) return []; - const parsed = JSON.parse(raw); - if (!Array.isArray(parsed)) return []; - return parsed.filter( - (report) => - report && - typeof report.latitude === 'number' && - typeof report.longitude === 'number' && - !isNaN(report.latitude) && - !isNaN(report.longitude) - ); - } catch { - return []; - } -} function readGeotaggedSymptomReports() { try { @@ -70,29 +53,10 @@ function readGeotaggedSymptomReports() { export default function LocationMap({ center, nearbyPoints, confidenceScore, windData, windError }) { const [showWind, setShowWind] = useState(false); const [showCommunityReports, setShowCommunityReports] = useState(false); - const [communityReports, setCommunityReports] = useState(() => readGeotaggedCommunityReports()); + const communityReports = useCommunityReports(); const [showSymptomReports, setShowSymptomReports] = useState(false); const [symptomReports, setSymptomReports] = useState(() => readGeotaggedSymptomReports()); const [selectedLayer, setSelectedLayer] = useState('aqi'); - useEffect(() => { - const updateReports = () => { - setCommunityReports(readGeotaggedCommunityReports()); - }; - - eventBus.on('COMMUNITY_REPORT_SUBMITTED', updateReports); - - const handleStorage = (e) => { - if (!e.key || e.key === COMMUNITY_REPORTS_STORAGE_KEY) { - updateReports(); - } - }; - window.addEventListener('storage', handleStorage); - - return () => { - eventBus.off('COMMUNITY_REPORT_SUBMITTED', updateReports); - window.removeEventListener('storage', handleStorage); - }; - }, []); useEffect(() => { const updateSymptomReports = () => { diff --git a/src/hooks/useCommunityReports.js b/src/hooks/useCommunityReports.js new file mode 100644 index 0000000..d2ccac3 --- /dev/null +++ b/src/hooks/useCommunityReports.js @@ -0,0 +1,58 @@ +import { useState, useEffect } from 'react'; +import { eventBus } from '../core/events'; + +const COMMUNITY_REPORTS_STORAGE_KEY = 'pollution-community-reports'; + +function readGeotaggedCommunityReports() { + try { + const raw = localStorage.getItem(COMMUNITY_REPORTS_STORAGE_KEY); + if (!raw) return []; + const parsed = JSON.parse(raw); + if (!Array.isArray(parsed)) return []; + return parsed.filter( + (report) => + report && + typeof report.latitude === 'number' && + typeof report.longitude === 'number' && + !isNaN(report.latitude) && + !isNaN(report.longitude) + ); + } catch { + return []; + } +} + +/** + * Reads geotagged community reports from localStorage and keeps them in sync with + * COMMUNITY_REPORT_SUBMITTED events and cross-tab storage changes. + * + * Extracted out of LocationMap so the component stays presentational and the + * localStorage side effect can be mocked in tests instead of touching real storage. + * + * @returns {Array} Geotagged community reports. + */ +export function useCommunityReports() { + const [communityReports, setCommunityReports] = useState(() => readGeotaggedCommunityReports()); + + useEffect(() => { + const updateReports = () => { + setCommunityReports(readGeotaggedCommunityReports()); + }; + + eventBus.on('COMMUNITY_REPORT_SUBMITTED', updateReports); + + const handleStorage = (e) => { + if (!e.key || e.key === COMMUNITY_REPORTS_STORAGE_KEY) { + updateReports(); + } + }; + window.addEventListener('storage', handleStorage); + + return () => { + eventBus.off('COMMUNITY_REPORT_SUBMITTED', updateReports); + window.removeEventListener('storage', handleStorage); + }; + }, []); + + return communityReports; +} \ No newline at end of file