Highlight wanted calls. Closes #117

This commit is contained in:
Ian Renton
2026-08-15 11:43:44 +01:00
parent 2ffa4780e8
commit 3006ee38cb
16 changed files with 224 additions and 122 deletions
+34 -12
View File
@@ -9,15 +9,17 @@ let alerts = [];
// Load alerts and populate the table. No need for QRZ/HamQTH credential headers as these won't add any useful data
// to alerts
function loadAlerts() {
$.ajax({url: '/api/v2/alerts' + buildQueryString(), dataType: 'json', success: function (jsonData) {
// Store last updated time
lastUpdateTime = moment.utc();
updateRefreshDisplay();
// Store data
alerts = jsonData;
// Update table
updateTable();
}});
$.ajax({
url: '/api/v2/alerts' + buildQueryString(), dataType: 'json', success: function (jsonData) {
// Store last updated time
lastUpdateTime = moment.utc();
updateRefreshDisplay();
// Store data
alerts = jsonData;
// Update table
updateTable();
}
});
}
// Build a query string for the API, based on the filters that the user has selected.
@@ -115,12 +117,28 @@ function addAlertRowsToTable(tbody, alerts) {
// Create row
let $tr = $('<tr>');
// Calculate some values
let wantThis = false;
if (a["dx_calls"] != null) {
for (i in a["dx_calls"]) {
if (callWanted(a["dx_calls"][i])) {
wantThis = true;
break;
}
}
}
// Apply striping to the table. We can't just use Bootstrap's table-striped class because we have all sorts of
// extra faff to deal with, like the mobile view having extra rows, and the On Now / Next 24h / Later banners
// which cause the table-striped colouring to go awry.
if (count % 2 === 1) {
// which cause the table-striped colouring to go awry. If the call is wanted, suppress the striping so the
// highlight colour looks right.
if (count % 2 === 1 && !wantThis) {
$tr.addClass("table-active");
}
// If this is a wanted call, highlight it
if (wantThis) {
$tr.addClass("row-wanted");
}
// Use local time instead of UTC?
const useLocalTime = $("#timeZone")[0].value === "local";
@@ -255,9 +273,13 @@ function addAlertRowsToTable(tbody, alerts) {
// Second row for mobile view only, containing source, ref, freqs/modes & comment
const $tr2 = $("<tr class='hidenotonmobile'>");
if (count % 2 === 1) {
if (count % 2 === 1 && !wantThis) {
$tr2.addClass("table-active");
}
if (wantThis) {
$tr2.addClass("row-wanted");
}
const $td2 = $("<td colspan='100'>");
if (showSource) {
$td2.append(`<span class='icon-wrapper'><i class='fa-solid ${sigToIcon(a["sig"], "fa-globe-africa")}'></i></span> `);
+4 -2
View File
@@ -19,7 +19,8 @@ function loadSpots() {
if (sseAbortController != null) {
sseAbortController.abort();
}
$.ajax({url: '/api/v2/spots' + buildQueryString(), dataType: 'json', success: function (jsonData) {
$.ajax({
url: '/api/v2/spots' + buildQueryString(), dataType: 'json', success: function (jsonData) {
// Store data
spots = jsonData;
// Update bands display
@@ -201,7 +202,8 @@ function updateBands() {
// Now each spot is tagged with how far down the div it should go, add them to the DOM.
spotList.forEach(s => {
let worked = alreadyWorked(s["dx_call"], s["band"], s["mode"]);
bandSpotsDiv.append(`<div class="band-spot" style="top: ${s['pxDownBandLabel']}px; border-top: 1px solid ${bandToColor(s['band'])}; border-left: 5px solid ${bandToColor(s['band'])}; border-bottom: 1px solid ${bandToColor(s['band'])}; border-right: 1px solid ${bandToColor(s['band'])}; text-decoration: ${worked ? 'line-through' : 'none'};"><span class="band-spot-call">${s.dx_call}${s.dx_ssid != null ? "-" + s.dx_ssid : ""}</span><span class="band-spot-info">${s.dx_call}${s.dx_ssid != null ? "-" + s.dx_ssid : ""} ${(s.freq / 1000000).toFixed(3)} ${s.mode}</span></div>`);
let wanted = callWanted(s["dx_call"]) && !worked;
bandSpotsDiv.append(`<div class="band-spot ${wanted ? "band-spot-wanted" : ""}" style="top: ${s['pxDownBandLabel']}px; border-top: 1px solid ${bandToColor(s['band'])}; border-left: 5px solid ${bandToColor(s['band'])}; border-bottom: 1px solid ${bandToColor(s['band'])}; border-right: 1px solid ${bandToColor(s['band'])}; text-decoration: ${worked ? 'line-through' : 'none'};"><span class="band-spot-call">${s.dx_call}${s.dx_ssid != null ? "-" + s.dx_ssid : ""}</span><span class="band-spot-info">${s.dx_call}${s.dx_ssid != null ? "-" + s.dx_ssid : ""} ${(s.freq / 1000000).toFixed(3)} ${s.mode}</span></div>`);
});
// Work out how tall the canvas should be. Normally this is matching the normal band column height, but if some
+16
View File
@@ -269,6 +269,22 @@ function getCredentialHeaders() {
return headers;
}
// Does the DX call match something in the user's "wanted" list?
function callWanted(call) {
let wantedString = $("#wantedCalls").val();
if (wantedString) {
let split = wantedString.split(/[ ,]+/);
for (const i in split) {
if (split[i].length > 0 && call.toUpperCase().includes(split[i].toUpperCase())) {
return true;
}
}
return false;
} else {
return false;
}
}
// Startup
$(document).ready(function () {
+34 -27
View File
@@ -46,34 +46,35 @@ function loadSpots() {
// data if they can.
$.ajax({
url: '/api/v2/spots' + buildQueryString(), dataType: 'json', success: function (jsonData) {
// Store data
spots = jsonData;
// Update map
updateMap();
if ($("#showTerminator")[0].checked) {
terminator.setTime();
// Store data
spots = jsonData;
// Update map
updateMap();
if ($("#showTerminator")[0].checked) {
terminator.setTime();
}
// Check if we have any credentials to use
if (Object.keys(getCredentialHeaders()).length > 0) {
// OK, we have credentials and have loaded once without them so the user has a basic map. Now reload
// with the credentials and replace what's on the map, so we can improve the data.
$.ajax({
url: '/api/v2/spots' + buildQueryString(),
dataType: 'json',
headers: getCredentialHeaders(),
success: function (jsonData2) {
spots = jsonData2;
updateMap();
// Now start the ongoing SSE connection
startSSEConnection();
}
});
} else {
// We had no credentials with which to augment the data anyway, so just start the SSE connection
// now
startSSEConnection();
}
}
// Check if we have any credentials to use
if (Object.keys(getCredentialHeaders()).length > 0) {
// OK, we have credentials and have loaded once without them so the user has a basic map. Now reload
// with the credentials and replace what's on the map, so we can improve the data.
$.ajax({
url: '/api/v2/spots' + buildQueryString(),
dataType: 'json',
headers: getCredentialHeaders(),
success: function(jsonData2) {
spots = jsonData2;
updateMap();
// Now start the ongoing SSE connection
startSSEConnection();
}
});
} else {
// We had no credentials with which to augment the data anyway, so just start the SSE connection
// now
startSSEConnection();
}
}});
});
}
// Start the SSE connection to receive new spots as they arrive
@@ -143,6 +144,12 @@ function addSpotToMap(s) {
markersLayer.addLayer(m);
oms.addMarker(m);
// Add highlight for wanted spots
if (callWanted(s["dx_call"])) {
console.log(s["dx_call"]);
$(m._icon).addClass('marker-icon-wanted');
}
let geodesic = null;
if ($("#mapShowGeodesics")[0].checked && s["de_latitude"] != null && s["de_longitude"] != null) {
try {
+35 -21
View File
@@ -19,17 +19,19 @@ function loadSpots() {
}
// Make the new query. No credential headers on the first load to keep things quick
$.ajax({url: '/api/v2/spots' + buildQueryString(), dataType: 'json', success: function (jsonData) {
// Store data
spots = jsonData;
// Update table
updateTable();
// Start SSE connection to fetch updates in the background, if we are in "run" mode
let run = $('#runButton:checked').val();
if (run) {
startSSEConnection();
$.ajax({
url: '/api/v2/spots' + buildQueryString(), dataType: 'json', success: function (jsonData) {
// Store data
spots = jsonData;
// Update table
updateTable();
// Start SSE connection to fetch updates in the background, if we are in "run" mode
let run = $('#runButton:checked').val();
if (run) {
startSSEConnection();
}
}
}});
});
}
// Start an SSE connection (closing an existing one if it exists). This will then be used to add to the table on the
@@ -210,23 +212,32 @@ function createNewTableRowsForSpot(s, highlightNew) {
// Create row
let $tr = $('<tr>');
// Calculate some values
let alreadyWorkedThis = alreadyWorked(s["dx_call"], s["band"], s["mode"]);
let wantThis = callWanted(s["dx_call"]);
let qrt = s["qrt"]
// Apply striping to the table. We can't just use Bootstrap's table-striped class because we have all sorts of
// extra faff to deal with, like the mobile view having extra rows, and the On Now / Next 24h / Later banners
// which cause the table-striped colouring to go awry.
if (rowCount % 2 === 1) {
// which cause the table-striped colouring to go awry. If the call is wanted, suppress the striping so the highlight
// colour looks right.
if (rowCount % 2 === 1 && !(wantThis && !qrt && !alreadyWorkedThis)) {
$tr.addClass("table-active");
}
// Show faded out if QRT or already worked
let alreadyWorkedThis = alreadyWorked(s["dx_call"], s["band"], s["mode"]);
if (s["qrt"] === true || alreadyWorkedThis) {
if (qrt || alreadyWorkedThis) {
$tr.addClass("table-faded");
}
// If we are asked to highlight new rows (i.e. this row is being added "live" via the SSE client and not as a bulk
// reload of the whole table)
if (highlightNew) {
$tr.addClass("new");
// reload of the whole table), do so
if (highlightNew && !qrt && !alreadyWorkedThis) {
$tr.addClass("row-new");
}
// If this is a wanted call, highlight it
if (wantThis && !qrt && !alreadyWorkedThis) {
$tr.addClass("row-wanted");
}
// Format a UTC or local time for display
@@ -399,14 +410,17 @@ function createNewTableRowsForSpot(s, highlightNew) {
const $tr2 = $("<tr class='hidenotonmobile'>");
// Apply styles as per the first row
if (rowCount % 2 === 1) {
if (rowCount % 2 === 1 && !(wantThis && !qrt && !alreadyWorkedThis)) {
$tr2.addClass("table-active");
}
if (s["qrt"] === true || alreadyWorkedThis) {
if (qrt || alreadyWorkedThis) {
$tr2.addClass("table-faded");
}
if (highlightNew) {
$tr2.addClass("new");
if (highlightNew && !qrt && !alreadyWorkedThis) {
$tr2.addClass("row-new");
}
if (wantThis && !qrt && !alreadyWorkedThis) {
$tr2.addClass("row-wanted");
}
const $td2 = $("<td colspan='100'>");