feat: add shared observer location with opt-out

This commit is contained in:
James Ward
2026-01-29 23:41:13 -08:00
parent d51da40a67
commit 384d02649a
15 changed files with 382 additions and 147 deletions
+13 -10
View File
@@ -31,16 +31,19 @@ let autoScroll = localStorage.getItem('autoScroll') !== 'false';
let muted = localStorage.getItem('audioMuted') === 'true';
// Observer location (load from localStorage or default to London)
let observerLocation = (function() {
const saved = localStorage.getItem('observerLocation');
if (saved) {
try {
const parsed = JSON.parse(saved);
if (parsed.lat && parsed.lon) return parsed;
} catch (e) {}
}
return { lat: 51.5074, lon: -0.1278 };
})();
let observerLocation = (function() {
if (window.ObserverLocation && ObserverLocation.getForModule) {
return ObserverLocation.getForModule('observerLocation');
}
const saved = localStorage.getItem('observerLocation');
if (saved) {
try {
const parsed = JSON.parse(saved);
if (parsed.lat && parsed.lon) return parsed;
} catch (e) {}
}
return { lat: 51.5074, lon: -0.1278 };
})();
// Message storage for export
let allMessages = [];
+103
View File
@@ -0,0 +1,103 @@
// Shared observer location helper for map-based modules.
// Default: shared location enabled unless explicitly disabled via config.
window.ObserverLocation = (function() {
const DEFAULT_LOCATION = { lat: 51.5074, lon: -0.1278 };
const SHARED_KEY = 'observerLocation';
const AIS_KEY = 'ais_observerLocation';
const LEGACY_LAT_KEY = 'observerLat';
const LEGACY_LON_KEY = 'observerLon';
function isSharedEnabled() {
return window.INTERCEPT_SHARED_OBSERVER_LOCATION !== false;
}
function normalize(lat, lon) {
const latNum = parseFloat(lat);
const lonNum = parseFloat(lon);
if (Number.isNaN(latNum) || Number.isNaN(lonNum)) return null;
if (latNum < -90 || latNum > 90 || lonNum < -180 || lonNum > 180) return null;
return { lat: latNum, lon: lonNum };
}
function parseLocation(raw) {
if (!raw) return null;
try {
const parsed = JSON.parse(raw);
if (parsed && parsed.lat !== undefined && parsed.lon !== undefined) {
return normalize(parsed.lat, parsed.lon);
}
} catch (e) {}
return null;
}
function readKey(key) {
return parseLocation(localStorage.getItem(key));
}
function readLegacyLatLon() {
const lat = localStorage.getItem(LEGACY_LAT_KEY);
const lon = localStorage.getItem(LEGACY_LON_KEY);
if (!lat || !lon) return null;
return normalize(lat, lon);
}
function getShared() {
const current = readKey(SHARED_KEY);
if (current) return current;
const legacy = readKey(AIS_KEY) || readLegacyLatLon();
if (legacy) {
setShared(legacy);
return legacy;
}
return { ...DEFAULT_LOCATION };
}
function setShared(location, options = {}) {
if (!location) return;
localStorage.setItem(SHARED_KEY, JSON.stringify(location));
if (options.updateLegacy !== false) {
localStorage.setItem(LEGACY_LAT_KEY, location.lat.toString());
localStorage.setItem(LEGACY_LON_KEY, location.lon.toString());
}
}
function getForModule(moduleKey, options = {}) {
if (isSharedEnabled()) {
return getShared();
}
if (moduleKey) {
const moduleLocation = readKey(moduleKey);
if (moduleLocation) return moduleLocation;
}
if (options.fallbackToLatLon) {
const legacy = readLegacyLatLon();
if (legacy) return legacy;
}
return { ...DEFAULT_LOCATION };
}
function setForModule(moduleKey, location, options = {}) {
if (!location) return;
if (isSharedEnabled()) {
setShared(location, options);
return;
}
if (moduleKey) {
localStorage.setItem(moduleKey, JSON.stringify(location));
} else if (options.fallbackToLatLon) {
localStorage.setItem(LEGACY_LAT_KEY, location.lat.toString());
localStorage.setItem(LEGACY_LON_KEY, location.lon.toString());
}
}
return {
isSharedEnabled,
getShared,
setShared,
getForModule,
setForModule,
normalize,
DEFAULT_LOCATION: { ...DEFAULT_LOCATION }
};
})();
+28 -14
View File
@@ -547,9 +547,14 @@ document.addEventListener('DOMContentLoaded', () => {
/**
* Load and display current observer location
*/
function loadObserverLocation() {
const lat = localStorage.getItem('observerLat');
const lon = localStorage.getItem('observerLon');
function loadObserverLocation() {
let lat = localStorage.getItem('observerLat');
let lon = localStorage.getItem('observerLon');
if (window.ObserverLocation && ObserverLocation.isSharedEnabled()) {
const shared = ObserverLocation.getShared();
lat = shared.lat.toString();
lon = shared.lon.toString();
}
const latInput = document.getElementById('observerLatInput');
const lonInput = document.getElementById('observerLonInput');
@@ -622,9 +627,9 @@ function detectLocationGPS(btn) {
/**
* Save observer location to localStorage
*/
function saveObserverLocation() {
const latInput = document.getElementById('observerLatInput');
const lonInput = document.getElementById('observerLonInput');
function saveObserverLocation() {
const latInput = document.getElementById('observerLatInput');
const lonInput = document.getElementById('observerLonInput');
const lat = parseFloat(latInput?.value);
const lon = parseFloat(lonInput?.value);
@@ -647,18 +652,27 @@ function saveObserverLocation() {
return;
}
localStorage.setItem('observerLat', lat.toString());
localStorage.setItem('observerLon', lon.toString());
if (window.ObserverLocation && ObserverLocation.isSharedEnabled()) {
ObserverLocation.setShared({ lat, lon });
} else {
localStorage.setItem('observerLat', lat.toString());
localStorage.setItem('observerLon', lon.toString());
}
// Update display
const currentLatDisplay = document.getElementById('currentLatDisplay');
const currentLonDisplay = document.getElementById('currentLonDisplay');
if (currentLatDisplay) currentLatDisplay.textContent = lat.toFixed(4) + '°';
if (currentLonDisplay) currentLonDisplay.textContent = lon.toFixed(4) + '°';
if (typeof showNotification === 'function') {
showNotification('Location', 'Observer location saved');
}
if (currentLatDisplay) currentLatDisplay.textContent = lat.toFixed(4) + '°';
if (currentLonDisplay) currentLonDisplay.textContent = lon.toFixed(4) + '°';
if (typeof showNotification === 'function') {
showNotification('Location', 'Observer location saved');
}
if (window.observerLocation) {
window.observerLocation.lat = lat;
window.observerLocation.lon = lon;
}
// Refresh SSTV ISS schedule if available
if (typeof SSTV !== 'undefined' && typeof SSTV.loadIssSchedule === 'function') {
+33 -20
View File
@@ -37,15 +37,20 @@ const SSTV = (function() {
/**
* Load location into input fields
*/
function loadLocationInputs() {
const latInput = document.getElementById('sstvObsLat');
const lonInput = document.getElementById('sstvObsLon');
const storedLat = localStorage.getItem('observerLat');
const storedLon = localStorage.getItem('observerLon');
if (latInput && storedLat) latInput.value = storedLat;
if (lonInput && storedLon) lonInput.value = storedLon;
function loadLocationInputs() {
const latInput = document.getElementById('sstvObsLat');
const lonInput = document.getElementById('sstvObsLon');
let storedLat = localStorage.getItem('observerLat');
let storedLon = localStorage.getItem('observerLon');
if (window.ObserverLocation && ObserverLocation.isSharedEnabled()) {
const shared = ObserverLocation.getShared();
storedLat = shared.lat.toString();
storedLon = shared.lon.toString();
}
if (latInput && storedLat) latInput.value = storedLat;
if (lonInput && storedLon) lonInput.value = storedLon;
// Add change handlers to save and refresh
if (latInput) latInput.addEventListener('change', saveLocationFromInputs);
@@ -55,19 +60,23 @@ const SSTV = (function() {
/**
* Save location from input fields
*/
function saveLocationFromInputs() {
const latInput = document.getElementById('sstvObsLat');
const lonInput = document.getElementById('sstvObsLon');
function saveLocationFromInputs() {
const latInput = document.getElementById('sstvObsLat');
const lonInput = document.getElementById('sstvObsLon');
const lat = parseFloat(latInput?.value);
const lon = parseFloat(lonInput?.value);
if (!isNaN(lat) && lat >= -90 && lat <= 90 &&
!isNaN(lon) && lon >= -180 && lon <= 180) {
localStorage.setItem('observerLat', lat.toString());
localStorage.setItem('observerLon', lon.toString());
loadIssSchedule(); // Refresh pass predictions
}
if (!isNaN(lat) && lat >= -90 && lat <= 90 &&
!isNaN(lon) && lon >= -180 && lon <= 180) {
if (window.ObserverLocation && ObserverLocation.isSharedEnabled()) {
ObserverLocation.setShared({ lat, lon });
} else {
localStorage.setItem('observerLat', lat.toString());
localStorage.setItem('observerLon', lon.toString());
}
loadIssSchedule(); // Refresh pass predictions
}
}
/**
@@ -94,8 +103,12 @@ const SSTV = (function() {
if (latInput) latInput.value = lat;
if (lonInput) lonInput.value = lon;
localStorage.setItem('observerLat', lat);
localStorage.setItem('observerLon', lon);
if (window.ObserverLocation && ObserverLocation.isSharedEnabled()) {
ObserverLocation.setShared({ lat: parseFloat(lat), lon: parseFloat(lon) });
} else {
localStorage.setItem('observerLat', lat);
localStorage.setItem('observerLon', lon);
}
btn.innerHTML = originalText;
btn.disabled = false;