diff --git a/src/components/WorldMap.jsx b/src/components/WorldMap.jsx
index 2af51f2..3036258 100644
--- a/src/components/WorldMap.jsx
+++ b/src/components/WorldMap.jsx
@@ -134,9 +134,38 @@ export const WorldMap = ({
const [pluginLayerStates, setPluginLayerStates] = useState({});
const isLocalInstall = useLocalInstall();
- // Filter out localOnly layers on hosted version
- const getAvailableLayers = () => getAllLayers().filter(l => !l.localOnly || isLocalInstall);
+ const [integrationsRev, setIntegrationsRev] = useState(0);
+ // Re-evaluate feature-gated integrations when toggles change in Settings
+ useEffect(() => {
+ const bump = () => setIntegrationsRev(v => v + 1);
+ try {
+ window.addEventListener('ohc-n3fjp-config-changed', bump);
+ window.addEventListener('ohc-rotator-config-changed', bump);
+ } catch {}
+ return () => {
+ try {
+ window.removeEventListener('ohc-n3fjp-config-changed', bump);
+ window.removeEventListener('ohc-rotator-config-changed', bump);
+ } catch {}
+ };
+ }, []);
+
+ // Filter out localOnly layers on hosted version
+ const getAvailableLayers = () => {
+ // Reference integrationsRev so changes trigger a re-render pass.
+ void integrationsRev;
+ let n3fjpEnabled = false;
+ try { n3fjpEnabled = localStorage.getItem('ohc_n3fjp_enabled') === '1'; } catch {}
+
+ return getAllLayers().filter(l => {
+ if (l.localOnly && !isLocalInstall) return false;
+ // N3FJP is local-only + feature-gated (so hosted never shows it and local users must opt-in)
+ if (l.id === 'n3fjp_logged_qsos' && !n3fjpEnabled) return false;
+ return true;
+ });
+ };
+
// Load map style from localStorage
const getStoredMapSettings = () => {
try {
@@ -359,7 +388,8 @@ export const WorldMap = ({
const de = deRef.current;
if (de?.lat != null && de?.lon != null) {
const az = initialBearingDeg(de.lat, de.lon, e.latlng.lat, lon);
- rotatorTurnRef.current(az);
+ // Never allow an async failure here to create an unhandled rejection.
+ Promise.resolve(rotatorTurnRef.current(az)).catch(() => {});
return;
}
}
@@ -1100,7 +1130,7 @@ export const WorldMap = ({
} catch (err) {
console.error('Plugin system error:', err);
}
- }, [pluginLayerStates]);
+ }, [pluginLayerStates, integrationsRev]);
// Update PSKReporter markers
useEffect(() => {
diff --git a/src/plugins/layers/useN3FJPLoggedQSOs.js b/src/plugins/layers/useN3FJPLoggedQSOs.js
index 49d3a7d..33300f7 100644
--- a/src/plugins/layers/useN3FJPLoggedQSOs.js
+++ b/src/plugins/layers/useN3FJPLoggedQSOs.js
@@ -6,6 +6,7 @@ export const metadata = {
description: 'Shows recently logged QSOs sent from the N3FJP bridge.',
icon: '🗺️',
category: 'overlay',
+ localOnly: true,
defaultEnabled: false,
defaultOpacity: 0.9,
version: '0.2.0'
@@ -250,91 +251,25 @@ export function useLayer({ enabled = false, opacity = 0.9, map = null }) {
};
}, [enabled]);
- // Add a small on-map control for display window + line color
+ /// React to Integrations panel changes (display window + color)
useEffect(() => {
- if (!enabled || !map || typeof L === 'undefined') return;
-
- const control = L.control({ position: 'topright' });
-
- control.onAdd = () => {
- const div = L.DomUtil.create('div', 'n3fjp-layer-control');
- div.style.background = 'rgba(0,0,0,0.65)';
- div.style.color = 'white';
- div.style.padding = '8px 10px';
- div.style.borderRadius = '8px';
- div.style.fontFamily = 'JetBrains Mono, monospace';
- div.style.fontSize = '12px';
- div.style.minWidth = '190px';
-
- // Stop map dragging/zooming when interacting with the control
- L.DomEvent.disableClickPropagation(div);
- L.DomEvent.disableScrollPropagation(div);
-
- div.innerHTML = `
-
N3FJP QSOs
-
-
- Show last
-
-
-
-
- Line color
-
-
- `;
-
- // Initialize controls
- const sel = div.querySelector('#n3fjp_minutes');
- const col = div.querySelector('#n3fjp_color');
-
- if (sel) sel.value = String(displayMinutes);
- if (col) col.value = String(lineColor);
-
- // Wire events
- if (sel) {
- sel.addEventListener('change', (e) => {
- const v = parseInt(e.target.value, 10);
- if (Number.isFinite(v)) {
- localStorage.setItem(STORAGE_MINUTES_KEY, String(v));
- setDisplayMinutes(v);
- }
- });
- }
-
- if (col) {
- col.addEventListener('change', (e) => {
- const v = e.target.value || '#3388ff';
- localStorage.setItem(STORAGE_COLOR_KEY, v);
- setLineColor(v);
- });
- }
+ if (!enabled) return;
- return div;
+ const sync = () => {
+ try {
+ const m = parseInt(localStorage.getItem(STORAGE_MINUTES_KEY) || '15', 10);
+ if (Number.isFinite(m)) setDisplayMinutes(m);
+ } catch {}
+ try {
+ const c = localStorage.getItem(STORAGE_COLOR_KEY) || '#3388ff';
+ setLineColor(c);
+ } catch {}
};
- control.addTo(map);
-
- // Add draggable and minimize functionality
- const controlElement = control.getContainer();
- if (controlElement) {
- setTimeout(() => {
- makeDraggable(controlElement, 'n3fjp-position');
- addMinimizeToggle(controlElement, 'n3fjp-position');
- }, 150);
- }
-
- return () => {
- try { control.remove(); } catch {}
- };
- }, [enabled, map, displayMinutes, lineColor]);
+ sync();
+ window.addEventListener('ohc-n3fjp-config-changed', sync);
+ return () => window.removeEventListener('ohc-n3fjp-config-changed', sync);
+ }, [enabled]);
// Draw markers/lines whenever qsos changes
useEffect(() => {
@@ -366,16 +301,34 @@ export function useLayer({ enabled = false, opacity = 0.9, map = null }) {
// Read station position from OpenHamClock config (if present)
let station = null;
+
try {
const raw = localStorage.getItem('openhamclock_config');
if (raw) {
const cfg = JSON.parse(raw);
const lat = cfg?.location?.lat;
const lon = cfg?.location?.lon;
- if (typeof lat === 'number' && typeof lon === 'number') station = { lat, lon };
+ if (typeof lat === 'number' && typeof lon === 'number') {
+ station = { lat, lon };
+ }
}
} catch {}
+ // ✅ Fallback to Maidenhead if lat/lon missing
+ if (!station) {
+ try {
+ const raw = localStorage.getItem('openhamclock_config');
+ if (raw) {
+ const cfg = JSON.parse(raw);
+ const grid = cfg?.station?.locator;
+ if (grid && grid.length >= 4) {
+ const { lat, lon } = maidenheadToLatLon(grid);
+ station = { lat, lon };
+ }
+ }
+ } catch {}
+ }
+
const newLayers = [];
// Optional: show station marker