Skip to content
Merged
Show file tree
Hide file tree
Changes from 20 commits
Commits
Show all changes
23 commits
Select commit Hold shift + click to select a range
bbd0677
Merge pull request #807 from accius/Staging
accius Mar 23, 2026
0666181
Merge pull request #808 from accius/Staging
accius Mar 23, 2026
feeecd8
Merge pull request #809 from accius/Staging
accius Mar 23, 2026
8fbe601
Merge pull request #810 from accius/Staging
accius Mar 23, 2026
3d1c9d0
Merge pull request #812 from accius/Staging
accius Mar 23, 2026
ee19454
Merge pull request #813 from accius/Staging
accius Mar 23, 2026
d1d54de
Merge pull request #814 from accius/Staging
accius Mar 23, 2026
50cf205
Merge pull request #815 from accius/Staging
accius Mar 23, 2026
0c801a6
Merge pull request #817 from accius/Staging
accius Mar 23, 2026
e454c46
Merge pull request #818 from accius/Staging
accius Mar 24, 2026
51e70a3
Merge pull request #819 from accius/Staging
accius Mar 24, 2026
a442d64
Merge pull request #820 from accius/Staging
accius Mar 24, 2026
53fd97a
Merge pull request #821 from accius/Staging
accius Mar 24, 2026
bda2c92
Merge pull request #822 from accius/Staging
accius Mar 24, 2026
0033b6c
Merge pull request #823 from accius/Staging
accius Mar 24, 2026
71da431
Merge pull request #824 from accius/Staging
accius Mar 24, 2026
08c2064
Merge pull request #825 from accius/Staging
accius Mar 24, 2026
ab90f56
Merge pull request #828 from accius/Staging
accius Mar 24, 2026
e9121ad
Fix contest QSO DX target updates
s53zo Mar 24, 2026
f1135fe
Fix repository formatting for CI
s53zo Mar 24, 2026
80f9f8a
Refine contest QSO DX auto-targeting
Mar 27, 2026
fbcb7a1
Apply Prettier formatting for CI
Mar 27, 2026
0c6ea88
Run CI for Staging pull requests
Mar 27, 2026
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
8 changes: 7 additions & 1 deletion AddOns/APRS-Newsfeed/aprs_newsfeed.user.js
Original file line number Diff line number Diff line change
Expand Up @@ -160,7 +160,13 @@
let lastUpdateTs = parseInt(localStorage.getItem('ohc_aprs_last_update')) || 0;

// Escape HTML to prevent XSS when interpolating into innerHTML
const esc = (s) => String(s).replace(/&/g,'&amp;').replace(/</g,'&lt;').replace(/>/g,'&gt;').replace(/"/g,'&quot;').replace(/'/g,'&#39;');
const esc = (s) =>
String(s)
.replace(/&/g, '&amp;')
.replace(/</g, '&lt;')
.replace(/>/g, '&gt;')
.replace(/"/g, '&quot;')
.replace(/'/g, '&#39;');

function getCallsign() {
try {
Expand Down
12 changes: 6 additions & 6 deletions docs/N1MM-SETUP.md
Original file line number Diff line number Diff line change
Expand Up @@ -53,12 +53,12 @@ If you're running directly with Node, restart the server after changing your `.e

All settings go in your `.env` or `.env.local` file:

| Variable | Default | Description |
|----------|---------|-------------|
| `N1MM_UDP_ENABLED` | `false` | Set to `true` to enable the UDP listener |
| `N1MM_UDP_PORT` | `12060` | UDP port to listen on (must match N1MM+ config) |
| `N1MM_MAX_QSOS` | `200` | Maximum QSOs to keep in memory |
| `N1MM_QSO_MAX_AGE_MINUTES` | `360` | QSOs older than this (6 hours) are pruned automatically |
| Variable | Default | Description |
| -------------------------- | ------- | ------------------------------------------------------- |
| `N1MM_UDP_ENABLED` | `false` | Set to `true` to enable the UDP listener |
| `N1MM_UDP_PORT` | `12060` | UDP port to listen on (must match N1MM+ config) |
| `N1MM_MAX_QSOS` | `200` | Maximum QSOs to keep in memory |
| `N1MM_QSO_MAX_AGE_MINUTES` | `360` | QSOs older than this (6 hours) are pruned automatically |

## Docker Users

Expand Down
10 changes: 8 additions & 2 deletions dxspider-proxy/server.js
Original file line number Diff line number Diff line change
Expand Up @@ -447,7 +447,10 @@ const handleDisconnect = () => {
// Detect rapid disconnect (kicked within seconds of connecting)
const connectionDuration = connectionStartTime ? Date.now() - connectionStartTime.getTime() : 0;
if (connectionDuration > 0 && connectionDuration < 15000) {
log('RECONNECT', `Rapid disconnect from ${currentNode?.name} after ${Math.round(connectionDuration / 1000)}s (likely auth rejection or SSID conflict)`);
log(
'RECONNECT',
`Rapid disconnect from ${currentNode?.name} after ${Math.round(connectionDuration / 1000)}s (likely auth rejection or SSID conflict)`,
);
}

reconnectAttempts++;
Expand All @@ -456,7 +459,10 @@ const handleDisconnect = () => {
// Try next node
currentNodeIndex = (currentNodeIndex + 1) % CONFIG.nodes.length;
reconnectAttempts = 0;
log('FAILOVER', `${CONFIG.maxReconnectAttempts} consecutive failures — switching to node: ${CONFIG.nodes[currentNodeIndex].name}`);
log(
'FAILOVER',
`${CONFIG.maxReconnectAttempts} consecutive failures — switching to node: ${CONFIG.nodes[currentNodeIndex].name}`,
);
}

log('RECONNECT', `Attempting reconnect in ${CONFIG.reconnectDelayMs}ms (attempt ${reconnectAttempts})`);
Expand Down
6 changes: 6 additions & 0 deletions src/components/PluginLayer.jsx
Original file line number Diff line number Diff line change
Expand Up @@ -27,6 +27,9 @@ const PluginLayerInner = ({
enabled,
opacity,
map,
dxLocation,
onDXChange,
dxLocked,
mapBandFilter,
callsign,
locator,
Expand All @@ -43,6 +46,9 @@ const PluginLayerInner = ({
map: safeMap,
enabled,
opacity,
dxLocation,
onDXChange,
dxLocked,
callsign,
locator,
mapBandFilter,
Expand Down
3 changes: 3 additions & 0 deletions src/components/WorldMap.jsx
Original file line number Diff line number Diff line change
Expand Up @@ -1924,6 +1924,9 @@ export const WorldMap = ({
plugin={layerDef}
enabled={pluginLayerStates[layerDef.id]?.enabled ?? layerDef.defaultEnabled}
opacity={pluginLayerStates[layerDef.id]?.opacity ?? layerDef.defaultOpacity}
dxLocation={dxLocation}
onDXChange={onDXChange}
dxLocked={dxLocked}
mapBandFilter={mapBandFilter}
config={pluginLayerStates[layerDef.id]?.config ?? layerDef.config}
map={isAzimuthal ? azimuthalMapRef.current : mapInstanceRef.current}
Expand Down
46 changes: 45 additions & 1 deletion src/plugins/layers/useContestQsos.js
Original file line number Diff line number Diff line change
Expand Up @@ -30,13 +30,22 @@ const bandFromAnyFrequency = (freq) => {
return normalizeBandKey(getBandFromFreq(n));
};

export function useLayer({ enabled = false, opacity = 0.7, map = null, mapBandFilter }) {
export function useLayer({
enabled = false,
opacity = 0.7,
map = null,
dxLocation = null,
onDXChange,
dxLocked = false,
mapBandFilter,
}) {
const [qsos, setQsos] = useState([]);
const [deLocation, setDeLocation] = useState(null);
const markersRef = useRef([]);
const linesRef = useRef([]);
const pollRef = useRef(null);
const configLoadedRef = useRef(false);
const lastAutoTargetQsoRef = useRef(null);

useEffect(() => {
if (!enabled || configLoadedRef.current) return;
Expand Down Expand Up @@ -80,6 +89,41 @@ export function useLayer({ enabled = false, opacity = 0.7, map = null, mapBandFi
};
}, [enabled]);

useEffect(() => {
if (!enabled) {
lastAutoTargetQsoRef.current = null;
}
}, [enabled]);

useEffect(() => {
if (!enabled || typeof onDXChange !== 'function' || dxLocked) return;

const latestLocatedQso = [...qsos]
.reverse()
.find((qso) => Number.isFinite(parseFloat(qso?.lat)) && Number.isFinite(parseFloat(qso?.lon)));

if (!latestLocatedQso) return;

const lat = parseFloat(latestLocatedQso.lat);
const lon = parseFloat(latestLocatedQso.lon);
const targetKey =
latestLocatedQso.id ||
`${latestLocatedQso.timestamp || ''}:${latestLocatedQso.dxCall || ''}:${lat.toFixed(4)}:${lon.toFixed(4)}`;

if (lastAutoTargetQsoRef.current === targetKey) return;

const alreadySelected =
Number.isFinite(dxLocation?.lat) &&
Number.isFinite(dxLocation?.lon) &&
Math.abs(dxLocation.lat - lat) < 1e-6 &&
Math.abs(dxLocation.lon - lon) < 1e-6;

lastAutoTargetQsoRef.current = targetKey;
if (!alreadySelected) {
onDXChange({ lat, lon });
}
}, [enabled, qsos, onDXChange, dxLocked, dxLocation]);

useEffect(() => {
if (!map || typeof L === 'undefined') return;

Expand Down
Loading