From e95c0554397d92f4c18570ccdab3e2407d2cf90d Mon Sep 17 00:00:00 2001 From: Ralph Bolliger Date: Mon, 2 Mar 2026 18:51:16 +0100 Subject: [PATCH] [FEATURE] Suppress DX Icon Fixes #553 --- package-lock.json | 54 ++++++++--- src/App.jsx | 9 +- src/DockableApp.jsx | 1 + src/components/SettingsPanel.jsx | 156 +++++++++++++++++-------------- src/components/WorldMap.jsx | 5 +- src/hooks/app/useMapLayers.js | 10 ++ src/layouts/ClassicLayout.jsx | 3 + src/layouts/ModernLayout.jsx | 1 + 8 files changed, 148 insertions(+), 91 deletions(-) diff --git a/package-lock.json b/package-lock.json index 83d4f0f0..fa527a3e 100644 --- a/package-lock.json +++ b/package-lock.json @@ -111,7 +111,6 @@ "integrity": "sha512-CGOfOJqWjg2qW/Mb6zNsDm+u5vFQ8DxXfbM09z69p5Z6+mE1ikP2jUXw+j42Pf1XTYED2Rni5f95npYeuwMDQA==", "dev": true, "license": "MIT", - "peer": true, "dependencies": { "@babel/code-frame": "^7.29.0", "@babel/generator": "^7.29.0", @@ -467,7 +466,6 @@ } ], "license": "MIT", - "peer": true, "engines": { "node": ">=18" }, @@ -491,7 +489,6 @@ } ], "license": "MIT", - "peer": true, "engines": { "node": ">=18" } @@ -886,6 +883,7 @@ "dev": true, "license": "BSD-2-Clause", "optional": true, + "peer": true, "dependencies": { "cross-dirname": "^0.1.0", "debug": "^4.3.4", @@ -907,6 +905,7 @@ "dev": true, "license": "MIT", "optional": true, + "peer": true, "dependencies": { "graceful-fs": "^4.2.0", "jsonfile": "^6.0.1", @@ -923,6 +922,7 @@ "dev": true, "license": "MIT", "optional": true, + "peer": true, "dependencies": { "universalify": "^2.0.0" }, @@ -937,6 +937,7 @@ "dev": true, "license": "MIT", "optional": true, + "peer": true, "engines": { "node": ">= 10.0.0" } @@ -2309,7 +2310,6 @@ "integrity": "sha512-izzd2zmnk8Nl5ECYkW27328RbQ1nKvkm6Bb5DAaz1Gk59EbLkiCMa6OLT0NoaAYTjOFS6N+SMYW1nh4/9ljPiw==", "dev": true, "license": "MIT", - "peer": true, "dependencies": { "@vitest/utils": "2.1.9", "fflate": "^0.8.2", @@ -2418,7 +2418,6 @@ "integrity": "sha512-j3fVLgvTo527anyYyJOGTYJbG+vnnQYvE0m5mmkc1TK+nxAppkCLMIL0aZ4dblVCNoGShhm+kzE4ZUykBoMg4g==", "dev": true, "license": "MIT", - "peer": true, "dependencies": { "fast-deep-equal": "^3.1.1", "fast-json-stable-stringify": "^2.0.0", @@ -2990,7 +2989,6 @@ } ], "license": "MIT", - "peer": true, "dependencies": { "baseline-browser-mapping": "^2.9.0", "caniuse-lite": "^1.0.30001759", @@ -3811,7 +3809,8 @@ "integrity": "sha512-+R08/oI0nl3vfPcqftZRpytksBXDzOUveBq/NBVx0sUp1axwzPQrKinNx5yd5sxPu8j1wIy8AfnVQ+5eFdha6Q==", "dev": true, "license": "MIT", - "optional": true + "optional": true, + "peer": true }, "node_modules/cross-spawn": { "version": "7.0.6", @@ -4074,7 +4073,6 @@ "integrity": "sha512-uOOBA3f+kW3o4KpSoMQ6SNpdXU7WtxlJRb9vCZgOvqhTz4b3GjcoWKstdisizNZLsylhTMv8TLHFPFW0Uxsj/g==", "dev": true, "license": "MIT", - "peer": true, "dependencies": { "app-builder-lib": "26.7.0", "builder-util": "26.4.1", @@ -4413,6 +4411,7 @@ "dev": true, "hasInstallScript": true, "license": "MIT", + "peer": true, "dependencies": { "@electron/asar": "^3.2.1", "debug": "^4.1.1", @@ -4433,6 +4432,7 @@ "integrity": "sha512-YJDaCJZEnBmcbw13fvdAM9AwNOJwOzrE4pqMqBq5nFiEqXUqHwlK4B+3pUw6JNvfSPtX05xFHtYy/1ni01eGCw==", "dev": true, "license": "MIT", + "peer": true, "dependencies": { "graceful-fs": "^4.1.2", "jsonfile": "^4.0.0", @@ -4468,6 +4468,29 @@ "node": ">= 0.8" } }, + "node_modules/encoding": { + "version": "0.1.13", + "resolved": "https://registry.npmjs.org/encoding/-/encoding-0.1.13.tgz", + "integrity": "sha512-ETBauow1T35Y/WZMkio9jiM0Z5xjHHmJ4XmjZOq1l/dXz3lr2sRn87nJy20RupqSh1F2m3HHPSp8ShIPQJrJ3A==", + "license": "MIT", + "optional": true, + "dependencies": { + "iconv-lite": "^0.6.2" + } + }, + "node_modules/encoding/node_modules/iconv-lite": { + "version": "0.6.3", + "resolved": "https://registry.npmjs.org/iconv-lite/-/iconv-lite-0.6.3.tgz", + "integrity": "sha512-4fCk79wshMdzMp2rH06qWrJE4iolqLhCUH+OiuIgU++RB0+94NlDL81atO7GX55uUKueo0txHNtvEyI6D7WdMw==", + "license": "MIT", + "optional": true, + "dependencies": { + "safer-buffer": ">= 2.1.2 < 3.0.0" + }, + "engines": { + "node": ">=0.10.0" + } + }, "node_modules/end-of-stream": { "version": "1.4.5", "resolved": "https://registry.npmjs.org/end-of-stream/-/end-of-stream-1.4.5.tgz", @@ -5621,7 +5644,6 @@ } ], "license": "MIT", - "peer": true, "dependencies": { "@babel/runtime": "^7.28.4" }, @@ -5955,7 +5977,6 @@ "integrity": "sha512-8i7LzZj7BF8uplX+ZyOlIz86V6TAsSs+np6m1kpW9u0JWi4z/1t+FzcK1aek+ybTnAC4KhBL4uXCNT0wcUIeCw==", "dev": true, "license": "MIT", - "peer": true, "dependencies": { "cssstyle": "^4.1.0", "data-urls": "^5.0.0", @@ -6873,6 +6894,7 @@ "integrity": "sha512-FP+p8RB8OWpF3YZBCrP5gtADmtXApB5AMLn+vdyA+PyxCjrCs00mjyUozssO33cwDeT3wNGdLxJ5M//YqtHAJw==", "dev": true, "license": "MIT", + "peer": true, "dependencies": { "minimist": "^1.2.6" }, @@ -7572,7 +7594,6 @@ "integrity": "sha512-5gTmgEY/sqK6gFXLIsQNH19lWb4ebPDLA4SdLP7dsWkIXHWlG66oPuVvXSGFPppYZz8ZDZq0dYYrbHfBCVUb1Q==", "dev": true, "license": "MIT", - "peer": true, "engines": { "node": ">=12" }, @@ -7676,6 +7697,7 @@ "dev": true, "license": "MIT", "optional": true, + "peer": true, "dependencies": { "commander": "^9.4.0" }, @@ -7693,6 +7715,7 @@ "dev": true, "license": "MIT", "optional": true, + "peer": true, "engines": { "node": "^12.20.0 || >=14" } @@ -7878,7 +7901,6 @@ "resolved": "https://registry.npmjs.org/react/-/react-18.3.1.tgz", "integrity": "sha512-wS+hAgJShR0KhEvPJArfuPVN1+Hz1t0Y6n5jLrGQbkb4urgPE/0Rve+1kMB1v/oWgHgm4WIcV+i7F2pTVj+2iQ==", "license": "MIT", - "peer": true, "dependencies": { "loose-envify": "^1.1.0" }, @@ -7901,7 +7923,6 @@ "resolved": "https://registry.npmjs.org/react-dom/-/react-dom-18.3.1.tgz", "integrity": "sha512-5m4nQKp+rZRb09LNH59GM4BxTh9251/ylbKIbpe7TpGxfJ+9kv6BLkLBXIjjspbgbnIBNqlI23tRnTWT0snUIw==", "license": "MIT", - "peer": true, "dependencies": { "loose-envify": "^1.1.0", "scheduler": "^0.23.2" @@ -8068,6 +8089,7 @@ "deprecated": "Rimraf versions prior to v4 are no longer supported", "dev": true, "license": "ISC", + "peer": true, "dependencies": { "glob": "^7.1.3" }, @@ -8081,6 +8103,7 @@ "integrity": "sha512-9T9UjW3r0UW5c1Q7GTwllptXwhvYmEzFhzMfZ9H7FQWt+uZePjZPjBP/W1ZEyZ1twGWom5/56TF4lPcqjnDHcg==", "dev": true, "license": "MIT", + "peer": true, "dependencies": { "balanced-match": "^1.0.0", "concat-map": "0.0.1" @@ -8093,6 +8116,7 @@ "deprecated": "Old versions of glob are not supported, and contain widely publicized security vulnerabilities, which have been fixed in the current version. Please update. Support for old versions may be purchased (at exorbitant rates) by contacting i@izs.me", "dev": true, "license": "ISC", + "peer": true, "dependencies": { "fs.realpath": "^1.0.0", "inflight": "^1.0.4", @@ -8114,6 +8138,7 @@ "integrity": "sha512-J7p63hRiAjw1NDEww1W7i37+ByIrOWO5XQQAzZ3VOcL0PNybwpfmV/N05zFAzwQ9USyEcX6t3UO+K5aqBQOIHw==", "dev": true, "license": "ISC", + "peer": true, "dependencies": { "brace-expansion": "^1.1.7" }, @@ -8885,6 +8910,7 @@ "integrity": "sha512-yYrrsWnrXMcdsnu/7YMYAofM1ktpL5By7vZhf15CrXijWWrEYZks5AXBudalfSWJLlnen/QUJUB5aoB0kqZUGA==", "dev": true, "license": "MIT", + "peer": true, "dependencies": { "mkdirp": "^0.5.1", "rimraf": "~2.6.2" @@ -9339,7 +9365,6 @@ "integrity": "sha512-o5a9xKjbtuhY6Bi5S3+HvbRERmouabWbyUcpXXUA1u+GNUKoROi9byOJ8M0nHbHYHkYICiMlqxkg1KkYmm25Sw==", "dev": true, "license": "MIT", - "peer": true, "dependencies": { "esbuild": "^0.21.3", "postcss": "^8.4.43", @@ -9438,7 +9463,6 @@ "integrity": "sha512-MSmPM9REYqDGBI8439mA4mWhV5sKmDlBKWIYbA3lRb2PTHACE0mgKwA8yQ2xq9vxDTuk4iPrECBAEW2aoFXY0Q==", "dev": true, "license": "MIT", - "peer": true, "dependencies": { "@vitest/expect": "2.1.9", "@vitest/mocker": "2.1.9", diff --git a/src/App.jsx b/src/App.jsx index 397f2a98..12f73e42 100644 --- a/src/App.jsx +++ b/src/App.jsx @@ -138,6 +138,7 @@ const App = () => { const { mapLayers, + toggleDeDxMarkers, toggleDXPaths, toggleDXLabels, togglePOTA, @@ -153,6 +154,7 @@ const App = () => { togglePSKPaths, toggleWSJTX, toggleDXNews, + toggleRotatorBearing, toggleAPRS, } = useMapLayers(); @@ -197,9 +199,7 @@ const App = () => { const dxWeather = useWeather(dxLocation, config.allUnits); const pskReporter = usePSKReporter(config.callsign, { minutes: config.lowMemoryMode ? 5 : 30, - enabled: pskFilters?.filterMode === 'grid' - ? !!config.locator - : config.callsign !== 'N0CALL', + enabled: pskFilters?.filterMode === 'grid' ? !!config.locator : config.callsign !== 'N0CALL', maxSpots: config.lowMemoryMode ? 50 : 500, filterMode: pskFilters?.filterMode || 'call', gridSquare: config.locator || '', @@ -403,6 +403,7 @@ const App = () => { wwbotaFilters, setWwbotaFilters, mapLayers, + toggleDeDxMarkers, toggleDXPaths, toggleDXLabels, togglePOTA, @@ -418,6 +419,7 @@ const App = () => { togglePSKPaths, toggleWSJTX, toggleDXNews, + toggleRotatorBearing, toggleAPRS, hoveredSpot, setHoveredSpot, @@ -461,6 +463,7 @@ const App = () => { satelliteFilters={satelliteFilters} onSatelliteFiltersChange={setSatelliteFilters} mapLayers={mapLayers} + onToggleDeDxMarkers={toggleDeDxMarkers} onToggleDXNews={toggleDXNews} wakeLockStatus={wakeLockStatus} /> diff --git a/src/DockableApp.jsx b/src/DockableApp.jsx index d60d0ae3..c3bc7f93 100644 --- a/src/DockableApp.jsx +++ b/src/DockableApp.jsx @@ -537,6 +537,7 @@ export const DockableApp = ({ satellites={filteredSatellites} pskReporterSpots={filteredPskSpots} wsjtxSpots={wsjtxMapSpots} + showDeDxMarkers={mapLayersEff.showDeDxMarkers} showDXPaths={mapLayersEff.showDXPaths} showDXLabels={mapLayersEff.showDXLabels} onToggleDXLabels={mapLayersEff.showDXPaths ? toggleDXLabelsEff : undefined} diff --git a/src/components/SettingsPanel.jsx b/src/components/SettingsPanel.jsx index 8e81104f..737f99f2 100644 --- a/src/components/SettingsPanel.jsx +++ b/src/components/SettingsPanel.jsx @@ -33,6 +33,7 @@ export const SettingsPanel = ({ satelliteFilters, onSatelliteFiltersChange, mapLayers, + onToggleDeDxMarkers, onToggleDXNews, wakeLockStatus, }) => { @@ -2295,71 +2296,34 @@ export const SettingsPanel = ({ {/* Map Layers Tab */} {activeTab === 'layers' && (
- {/* Map Overlays section */} -
-
- Map Overlays -
- - -
- - {layers.length > 0 ? ( - (() => { + {(() => { + const overlayCards = [ + { + id: 'de-dx-markers', + checked: mapLayers?.showDeDxMarkers !== false, + onChange: () => onToggleDeDxMarkers?.(), + icon: 'πŸ“', + title: 'DE/DX Markers', + description: 'Show or hide your DE and DX position markers on the map', + }, + { + id: 'dx-news-ticker', + checked: mapLayers?.showDXNews !== false, + onChange: () => onToggleDXNews?.(), + icon: 'πŸ“°', + title: 'DX News Ticker', + description: 'Scrolling DX news headlines on the map', + }, + ]; + return (() => { const categoryOrder = [ + { key: 'overlay', label: 'πŸ—ΊοΈ Map Overlays' }, { key: 'propagation', label: 'πŸ“‘ Propagation' }, { key: 'amateur', label: 'πŸ“» Amateur Radio' }, { key: 'weather', label: '🌀️ Weather' }, { key: 'space-weather', label: 'β˜€οΈ Space Weather' }, { key: 'hazards', label: '⚠️ Natural Hazards' }, { key: 'geology', label: '🌍 Geology' }, - { key: 'overlay', label: 'πŸ—ΊοΈ Map Overlays' }, ]; const nonSatLayers = layers.filter((l) => l.category !== 'satellites'); @@ -2377,6 +2341,44 @@ export const SettingsPanel = ({ }), ); + const renderBuiltInOverlayCard = (item) => ( +
+ +
+ ); + const renderLayerCard = (layer) => (
{ - if (!grouped[key] || grouped[key].length === 0) return; + const hasBuiltInOverlays = key === 'overlay' && overlayCards.length > 0; + if (!grouped[key] && !hasBuiltInOverlays) return; + if ((!grouped[key] || grouped[key].length === 0) && !hasBuiltInOverlays) return; result.push(
, ); + if (key === 'overlay') { + overlayCards.forEach((item) => { + result.push(renderBuiltInOverlayCard(item)); + }); + } grouped[key].forEach((layer) => { result.push(renderLayerCard(layer)); rendered.add(layer.id); @@ -2506,20 +2515,23 @@ export const SettingsPanel = ({ .forEach((layer) => { result.push(renderLayerCard(layer)); }); + if (result.length === 0) { + return ( +
+ {t('station.settings.layers.noLayers')} +
+ ); + } return result; - })() - ) : ( -
- {t('station.settings.layers.noLayers')} -
- )} + })(); + })()}
)} diff --git a/src/components/WorldMap.jsx b/src/components/WorldMap.jsx index e14c0692..1141023e 100644 --- a/src/components/WorldMap.jsx +++ b/src/components/WorldMap.jsx @@ -87,6 +87,7 @@ export const WorldMap = ({ pskReporterSpots, wsjtxSpots, showDXPaths, + showDeDxMarkers = true, showDXLabels, onToggleDXLabels, showPOTA, @@ -997,6 +998,8 @@ export const WorldMap = ({ dxMarkerRef.current.forEach((m) => map.removeLayer(m)); dxMarkerRef.current = []; + if (!showDeDxMarkers) return; + // DE Marker β€” replicate across world copies replicatePoint(deLocation.lat, deLocation.lon).forEach(([lat, lon]) => { const deIcon = L.divIcon({ @@ -1024,7 +1027,7 @@ export const WorldMap = ({ attachPopupWeather(m, lat, lon, baseHtml); dxMarkerRef.current.push(m); }); - }, [deLocation, dxLocation, allUnits, dxWeatherAllowed]); + }, [deLocation, dxLocation, allUnits, dxWeatherAllowed, showDeDxMarkers]); // Update sun/moon markers every 60 seconds (matches terminator refresh) useEffect(() => { diff --git a/src/hooks/app/useMapLayers.js b/src/hooks/app/useMapLayers.js index 32b531d6..9ff2b3f5 100644 --- a/src/hooks/app/useMapLayers.js +++ b/src/hooks/app/useMapLayers.js @@ -3,6 +3,7 @@ import { syncAllSettingsToServer } from '../../utils'; export default function useMapLayers() { const defaults = { + showDeDxMarkers: true, showDXPaths: true, showDXLabels: true, showPOTA: true, @@ -47,6 +48,10 @@ export default function useMapLayers() { localStorage.setItem('openhamclock_mapLayers', JSON.stringify(mapLayers)); } catch {} + try { + window.dispatchEvent(new CustomEvent('mapLayersChanged', { detail: mapLayers })); + } catch {} + // If your upstream uses this utility, keep it β€” it helps keep settings in sync. try { syncAllSettingsToServer({ mapLayers }); @@ -54,6 +59,10 @@ export default function useMapLayers() { }, [mapLayers]); const toggleDXPaths = useCallback(() => setMapLayers((prev) => ({ ...prev, showDXPaths: !prev.showDXPaths })), []); + const toggleDeDxMarkers = useCallback( + () => setMapLayers((prev) => ({ ...prev, showDeDxMarkers: !prev.showDeDxMarkers })), + [], + ); const toggleDXLabels = useCallback(() => setMapLayers((prev) => ({ ...prev, showDXLabels: !prev.showDXLabels })), []); const togglePOTA = useCallback(() => setMapLayers((prev) => ({ ...prev, showPOTA: !prev.showPOTA })), []); const togglePOTALabels = useCallback( @@ -95,6 +104,7 @@ export default function useMapLayers() { return { mapLayers, setMapLayers, + toggleDeDxMarkers, toggleDXPaths, toggleDXLabels, togglePOTA, diff --git a/src/layouts/ClassicLayout.jsx b/src/layouts/ClassicLayout.jsx index bbc2cb48..0f266045 100644 --- a/src/layouts/ClassicLayout.jsx +++ b/src/layouts/ClassicLayout.jsx @@ -432,6 +432,7 @@ export default function ClassicLayout(props) { onMapBandFilterChange={setMapBandFilter} satellites={filteredSatellites} pskReporterSpots={filteredPskSpots} + showDeDxMarkers={mapLayers.showDeDxMarkers} showDXPaths={mapLayers.showDXPaths} showDXLabels={mapLayers.showDXLabels} onToggleDXLabels={toggleDXLabels} @@ -753,6 +754,7 @@ export default function ClassicLayout(props) { onMapBandFilterChange={setMapBandFilter} satellites={filteredSatellites} pskReporterSpots={filteredPskSpots} + showDeDxMarkers={mapLayers.showDeDxMarkers} showDXPaths={mapLayers.showDXPaths} showDXLabels={mapLayers.showDXLabels} onToggleDXLabels={toggleDXLabels} @@ -1381,6 +1383,7 @@ export default function ClassicLayout(props) { onMapBandFilterChange={setMapBandFilter} satellites={filteredSatellites} pskReporterSpots={filteredPskSpots} + showDeDxMarkers={mapLayers.showDeDxMarkers} showDXPaths={mapLayers.showDXPaths} showDXLabels={mapLayers.showDXLabels} onToggleDXLabels={toggleDXLabels} diff --git a/src/layouts/ModernLayout.jsx b/src/layouts/ModernLayout.jsx index 0d1052f5..54684b5f 100644 --- a/src/layouts/ModernLayout.jsx +++ b/src/layouts/ModernLayout.jsx @@ -150,6 +150,7 @@ export default function ModernLayout(props) { onMapBandFilterChange={setMapBandFilter} satellites={filteredSatellites} pskReporterSpots={filteredPskSpots} + showDeDxMarkers={mapLayers.showDeDxMarkers} showDXPaths={mapLayers.showDXPaths} showDXLabels={mapLayers.showDXLabels} onToggleDXLabels={toggleDXLabels}