mirror of
https://git.ianrenton.com/ian/spothole.git
synced 2026-02-04 01:04:33 +00:00
Add ability to tag callsigns as worked. Closes #41
This commit is contained in:
@@ -66,7 +66,7 @@
|
|||||||
<p>This software is dedicated to the memory of Tom G1PJB, SK, a friend and colleague who sadly passed away around the time I started writing it in Autumn 2025. I was looking forward to showing it to you when it was done.</p>
|
<p>This software is dedicated to the memory of Tom G1PJB, SK, a friend and colleague who sadly passed away around the time I started writing it in Autumn 2025. I was looking forward to showing it to you when it was done.</p>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<script src="/js/common.js?v=7"></script>
|
<script src="/js/common.js?v=8"></script>
|
||||||
<script>$(document).ready(function() { $("#nav-link-about").addClass("active"); }); <!-- highlight active page in nav --></script>
|
<script>$(document).ready(function() { $("#nav-link-about").addClass("active"); }); <!-- highlight active page in nav --></script>
|
||||||
|
|
||||||
{% end %}
|
{% end %}
|
||||||
@@ -69,8 +69,8 @@
|
|||||||
|
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<script src="/js/common.js?v=7"></script>
|
<script src="/js/common.js?v=8"></script>
|
||||||
<script src="/js/add-spot.js?v=7"></script>
|
<script src="/js/add-spot.js?v=8"></script>
|
||||||
<script>$(document).ready(function() { $("#nav-link-add-spot").addClass("active"); }); <!-- highlight active page in nav --></script>
|
<script>$(document).ready(function() { $("#nav-link-add-spot").addClass("active"); }); <!-- highlight active page in nav --></script>
|
||||||
|
|
||||||
{% end %}
|
{% end %}
|
||||||
@@ -56,8 +56,8 @@
|
|||||||
|
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<script src="/js/common.js?v=7"></script>
|
<script src="/js/common.js?v=8"></script>
|
||||||
<script src="/js/alerts.js?v=7"></script>
|
<script src="/js/alerts.js?v=8"></script>
|
||||||
<script>$(document).ready(function() { $("#nav-link-alerts").addClass("active"); }); <!-- highlight active page in nav --></script>
|
<script>$(document).ready(function() { $("#nav-link-alerts").addClass("active"); }); <!-- highlight active page in nav --></script>
|
||||||
|
|
||||||
{% end %}
|
{% end %}
|
||||||
@@ -62,9 +62,9 @@
|
|||||||
<script>
|
<script>
|
||||||
let spotProvidersEnabledByDefault = {% raw json_encode(web_ui_options["spot-providers-enabled-by-default"]) %};
|
let spotProvidersEnabledByDefault = {% raw json_encode(web_ui_options["spot-providers-enabled-by-default"]) %};
|
||||||
</script>
|
</script>
|
||||||
<script src="/js/common.js?v=7"></script>
|
<script src="/js/common.js?v=8"></script>
|
||||||
<script src="/js/spotsbandsandmap.js?v=7"></script>
|
<script src="/js/spotsbandsandmap.js?v=8"></script>
|
||||||
<script src="/js/bands.js?v=7"></script>
|
<script src="/js/bands.js?v=8"></script>
|
||||||
<script>$(document).ready(function() { $("#nav-link-bands").addClass("active"); }); <!-- highlight active page in nav --></script>
|
<script>$(document).ready(function() { $("#nav-link-bands").addClass("active"); }); <!-- highlight active page in nav --></script>
|
||||||
|
|
||||||
{% end %}
|
{% end %}
|
||||||
@@ -46,10 +46,10 @@
|
|||||||
crossorigin="anonymous"></script>
|
crossorigin="anonymous"></script>
|
||||||
<script src="https://cdn.jsdelivr.net/npm/tinycolor2@1.6.0/cjs/tinycolor.min.js"></script>
|
<script src="https://cdn.jsdelivr.net/npm/tinycolor2@1.6.0/cjs/tinycolor.min.js"></script>
|
||||||
|
|
||||||
<script src="https://misc.ianrenton.com/jsutils/utils.js?v=7"></script>
|
<script src="https://misc.ianrenton.com/jsutils/utils.js?v=8"></script>
|
||||||
<script src="https://misc.ianrenton.com/jsutils/storage.js?v=7"></script>
|
<script src="https://misc.ianrenton.com/jsutils/storage.js?v=8"></script>
|
||||||
<script src="https://misc.ianrenton.com/jsutils/ui-ham.js?v=7"></script>
|
<script src="https://misc.ianrenton.com/jsutils/ui-ham.js?v=8"></script>
|
||||||
<script src="https://misc.ianrenton.com/jsutils/geo.js?v=7"></script>
|
<script src="https://misc.ianrenton.com/jsutils/geo.js?v=8"></script>
|
||||||
|
|
||||||
</head>
|
</head>
|
||||||
<body>
|
<body>
|
||||||
|
|||||||
@@ -38,6 +38,10 @@
|
|||||||
<input class="form-check-input storeable-checkbox" type="checkbox" id="tableShowDE" value="tableShowDE" oninput="columnsUpdated();" checked>
|
<input class="form-check-input storeable-checkbox" type="checkbox" id="tableShowDE" value="tableShowDE" oninput="columnsUpdated();" checked>
|
||||||
<label class="form-check-label" for="tableShowDE">DE</label>
|
<label class="form-check-label" for="tableShowDE">DE</label>
|
||||||
</div>
|
</div>
|
||||||
|
<div class="form-check form-check-inline">
|
||||||
|
<input class="form-check-input storeable-checkbox" type="checkbox" id="tableShowWorkedCheckbox" value="tableShowWorkedCheckbox" oninput="columnsUpdated();" checked>
|
||||||
|
<label class="form-check-label" for="tableShowWorkedCheckbox">Worked?</label>
|
||||||
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
6
templates/cards/worked-calls.html
Normal file
6
templates/cards/worked-calls.html
Normal file
@@ -0,0 +1,6 @@
|
|||||||
|
<div class="card">
|
||||||
|
<div class="card-body">
|
||||||
|
<h5 class="card-title">Worked Calls</h5>
|
||||||
|
<button type="button" class="btn btn-primary" onClick="clearWorked();">Clear worked calls</button>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
@@ -70,9 +70,9 @@
|
|||||||
<script>
|
<script>
|
||||||
let spotProvidersEnabledByDefault = {% raw json_encode(web_ui_options["spot-providers-enabled-by-default"]) %};
|
let spotProvidersEnabledByDefault = {% raw json_encode(web_ui_options["spot-providers-enabled-by-default"]) %};
|
||||||
</script>
|
</script>
|
||||||
<script src="/js/common.js?v=7"></script>
|
<script src="/js/common.js?v=8"></script>
|
||||||
<script src="/js/spotsbandsandmap.js?v=7"></script>
|
<script src="/js/spotsbandsandmap.js?v=8"></script>
|
||||||
<script src="/js/map.js?v=7"></script>
|
<script src="/js/map.js?v=8"></script>
|
||||||
<script>$(document).ready(function() { $("#nav-link-map").addClass("active"); }); <!-- highlight active page in nav --></script>
|
<script>$(document).ready(function() { $("#nav-link-map").addClass("active"); }); <!-- highlight active page in nav --></script>
|
||||||
|
|
||||||
{% end %}
|
{% end %}
|
||||||
@@ -65,6 +65,9 @@
|
|||||||
<div class="col">
|
<div class="col">
|
||||||
{% module Template("cards/location.html", web_ui_options=web_ui_options) %}
|
{% module Template("cards/location.html", web_ui_options=web_ui_options) %}
|
||||||
</div>
|
</div>
|
||||||
|
<div class="col">
|
||||||
|
{% module Template("cards/worked-calls.html", web_ui_options=web_ui_options) %}
|
||||||
|
</div>
|
||||||
<div class="col">
|
<div class="col">
|
||||||
{% module Template("cards/color-scheme-and-band-color-scheme.html", web_ui_options=web_ui_options) %}
|
{% module Template("cards/color-scheme-and-band-color-scheme.html", web_ui_options=web_ui_options) %}
|
||||||
</div>
|
</div>
|
||||||
@@ -84,9 +87,9 @@
|
|||||||
<script>
|
<script>
|
||||||
let spotProvidersEnabledByDefault = {% raw json_encode(web_ui_options["spot-providers-enabled-by-default"]) %};
|
let spotProvidersEnabledByDefault = {% raw json_encode(web_ui_options["spot-providers-enabled-by-default"]) %};
|
||||||
</script>
|
</script>
|
||||||
<script src="/js/common.js?v=7"></script>
|
<script src="/js/common.js?v=8"></script>
|
||||||
<script src="/js/spotsbandsandmap.js?v=7"></script>
|
<script src="/js/spotsbandsandmap.js?v=8"></script>
|
||||||
<script src="/js/spots.js?v=7"></script>
|
<script src="/js/spots.js?v=8"></script>
|
||||||
<script>$(document).ready(function() { $("#nav-link-spots").addClass("active"); }); <!-- highlight active page in nav --></script>
|
<script>$(document).ready(function() { $("#nav-link-spots").addClass("active"); }); <!-- highlight active page in nav --></script>
|
||||||
|
|
||||||
{% end %}
|
{% end %}
|
||||||
@@ -3,8 +3,8 @@
|
|||||||
|
|
||||||
<div id="status-container" class="row row-cols-1 row-cols-md-4 g-4 mt-4"></div>
|
<div id="status-container" class="row row-cols-1 row-cols-md-4 g-4 mt-4"></div>
|
||||||
|
|
||||||
<script src="/js/common.js?v=7"></script>
|
<script src="/js/common.js?v=8"></script>
|
||||||
<script src="/js/status.js?v=7"></script>
|
<script src="/js/status.js?v=8"></script>
|
||||||
<script>$(document).ready(function() { $("#nav-link-status").addClass("active"); }); <!-- highlight active page in nav --></script>
|
<script>$(document).ready(function() { $("#nav-link-status").addClass("active"); }); <!-- highlight active page in nav --></script>
|
||||||
|
|
||||||
{% end %}
|
{% end %}
|
||||||
@@ -145,7 +145,8 @@ function updateBands() {
|
|||||||
|
|
||||||
// Now each spot is tagged with how far down the div it should go, add them to the DOM.
|
// Now each spot is tagged with how far down the div it should go, add them to the DOM.
|
||||||
spotList.forEach(s => {
|
spotList.forEach(s => {
|
||||||
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'])};"><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 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>`);
|
||||||
});
|
});
|
||||||
|
|
||||||
// Work out how tall the canvas should be. Normally this is matching the normal band column height, but if some
|
// Work out how tall the canvas should be. Normally this is matching the normal band column height, but if some
|
||||||
|
|||||||
@@ -105,6 +105,7 @@ function updateTable() {
|
|||||||
var showType = $("#tableShowType")[0].checked;
|
var showType = $("#tableShowType")[0].checked;
|
||||||
var showRef = $("#tableShowRef")[0].checked;
|
var showRef = $("#tableShowRef")[0].checked;
|
||||||
var showDE = $("#tableShowDE")[0].checked;
|
var showDE = $("#tableShowDE")[0].checked;
|
||||||
|
var showWorkedCheckbox = $("#tableShowWorkedCheckbox")[0].checked;
|
||||||
|
|
||||||
// Populate table with headers
|
// Populate table with headers
|
||||||
let table = $("#table");
|
let table = $("#table");
|
||||||
@@ -136,12 +137,18 @@ function updateTable() {
|
|||||||
if (showDE) {
|
if (showDE) {
|
||||||
table.find('thead tr').append(`<th class='hideonmobile'>DE</th>`);
|
table.find('thead tr').append(`<th class='hideonmobile'>DE</th>`);
|
||||||
}
|
}
|
||||||
|
if (showWorkedCheckbox) {
|
||||||
|
table.find('thead tr').append(`<th class='hideonmobile'></th>`);
|
||||||
|
}
|
||||||
|
|
||||||
table.find('tbody').empty();
|
table.find('tbody').empty();
|
||||||
if (spots.length == 0) {
|
if (spots.length == 0) {
|
||||||
table.find('tbody').append('<tr class="table-danger"><td colspan="100" style="text-align:center;">No spots match your filters.</td></tr>');
|
table.find('tbody').append('<tr class="table-danger"><td colspan="100" style="text-align:center;">No spots match your filters.</td></tr>');
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// We are regenerating the entire table not just adding a new row, so reset the row counter
|
||||||
|
rowCount = 0;
|
||||||
|
|
||||||
let spotsNewestFirst = spots.toReversed();
|
let spotsNewestFirst = spots.toReversed();
|
||||||
spotsNewestFirst.forEach(s => addSpotToTopOfTable(s, false));
|
spotsNewestFirst.forEach(s => addSpotToTopOfTable(s, false));
|
||||||
}
|
}
|
||||||
@@ -174,6 +181,7 @@ function createNewTableRowsForSpot(s, highlightNew) {
|
|||||||
var showType = $("#tableShowType")[0].checked;
|
var showType = $("#tableShowType")[0].checked;
|
||||||
var showRef = $("#tableShowRef")[0].checked;
|
var showRef = $("#tableShowRef")[0].checked;
|
||||||
var showDE = $("#tableShowDE")[0].checked;
|
var showDE = $("#tableShowDE")[0].checked;
|
||||||
|
var showWorkedCheckbox = $("#tableShowWorkedCheckbox")[0].checked;
|
||||||
|
|
||||||
// Create row
|
// Create row
|
||||||
let $tr = $('<tr>');
|
let $tr = $('<tr>');
|
||||||
@@ -185,8 +193,9 @@ function createNewTableRowsForSpot(s, highlightNew) {
|
|||||||
$tr.addClass("table-active");
|
$tr.addClass("table-active");
|
||||||
}
|
}
|
||||||
|
|
||||||
// Show faded out if QRT
|
// Show faded out if QRT or already worked
|
||||||
if (s["qrt"] == true) {
|
let alreadyWorkedThis = alreadyWorked(s["dx_call"], s["band"], s["mode"]);
|
||||||
|
if (s["qrt"] == true || alreadyWorkedThis) {
|
||||||
$tr.addClass("table-faded");
|
$tr.addClass("table-faded");
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -308,6 +317,9 @@ function createNewTableRowsForSpot(s, highlightNew) {
|
|||||||
// Format band name
|
// Format band name
|
||||||
var bandFullName = s['band'] ? s['band'] + " band": "Unknown band";
|
var bandFullName = s['band'] ? s['band'] + " band": "Unknown band";
|
||||||
|
|
||||||
|
// Format "worked" checkbox
|
||||||
|
var workedCheckbox = `<input type="checkbox" ${alreadyWorkedThis ? "checked" : ""} onClick="setWorkedState('${s['dx_call']}', '${s['band']}', '${s['mode']}', ${alreadyWorkedThis ? "false" : "true"});">`;
|
||||||
|
|
||||||
// Populate the row
|
// Populate the row
|
||||||
if (showTime) {
|
if (showTime) {
|
||||||
$tr.append(`<td class='nowrap'>${time_formatted}</td>`);
|
$tr.append(`<td class='nowrap'>${time_formatted}</td>`);
|
||||||
@@ -336,6 +348,9 @@ function createNewTableRowsForSpot(s, highlightNew) {
|
|||||||
if (showDE) {
|
if (showDE) {
|
||||||
$tr.append(`<td class='nowrap hideonmobile'><span class='flag-wrapper' title='${de_country}'>${de_flag}</span>${de_call}</td>`);
|
$tr.append(`<td class='nowrap hideonmobile'><span class='flag-wrapper' title='${de_country}'>${de_flag}</span>${de_call}</td>`);
|
||||||
}
|
}
|
||||||
|
if (showWorkedCheckbox) {
|
||||||
|
$tr.append(`<td class='nowrap hideonmobile'>${workedCheckbox}</td>`);
|
||||||
|
}
|
||||||
|
|
||||||
// Second row for mobile view only, containing type, ref & comment
|
// Second row for mobile view only, containing type, ref & comment
|
||||||
$tr2 = $("<tr class='hidenotonmobile'>");
|
$tr2 = $("<tr class='hidenotonmobile'>");
|
||||||
@@ -344,7 +359,7 @@ function createNewTableRowsForSpot(s, highlightNew) {
|
|||||||
if (rowCount % 2 == 1) {
|
if (rowCount % 2 == 1) {
|
||||||
$tr2.addClass("table-active");
|
$tr2.addClass("table-active");
|
||||||
}
|
}
|
||||||
if (s["qrt"] == true) {
|
if (s["qrt"] == true || alreadyWorkedThis) {
|
||||||
$tr2.addClass("table-faded");
|
$tr2.addClass("table-faded");
|
||||||
}
|
}
|
||||||
if (highlightNew) {
|
if (highlightNew) {
|
||||||
@@ -367,6 +382,9 @@ function createNewTableRowsForSpot(s, highlightNew) {
|
|||||||
if (showDE) {
|
if (showDE) {
|
||||||
$td2floatright.append(` de ${de_call} `);
|
$td2floatright.append(` de ${de_call} `);
|
||||||
}
|
}
|
||||||
|
if (showWorkedCheckbox) {
|
||||||
|
$td2floatright.append(` ${workedCheckbox} `);
|
||||||
|
}
|
||||||
$td2.append($td2floatright);
|
$td2.append($td2floatright);
|
||||||
$td2.append(`</div><div style="clear: both;"></div>`);
|
$td2.append(`</div><div style="clear: both;"></div>`);
|
||||||
if (showComment) {
|
if (showComment) {
|
||||||
@@ -481,6 +499,27 @@ function displayIntroBox() {
|
|||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Mark a callsign-band-mode combination as worked (or unmark it). Persist this to localStorage.
|
||||||
|
function setWorkedState(callsign, band, mode, nowWorked) {
|
||||||
|
let combo = callsign + "-" + band + "-" + mode;
|
||||||
|
if (nowWorked && !worked.includes(combo)) {
|
||||||
|
worked.push(combo);
|
||||||
|
updateTable();
|
||||||
|
localStorage.setItem("worked", JSON.stringify(worked));
|
||||||
|
} else if (!nowWorked && worked.includes(combo)) {
|
||||||
|
worked.splice(worked.indexOf(combo), 1);
|
||||||
|
updateTable();
|
||||||
|
localStorage.setItem("worked", JSON.stringify(worked));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Clear the list of worked calls
|
||||||
|
function clearWorked() {
|
||||||
|
worked = [];
|
||||||
|
updateTable();
|
||||||
|
localStorage.setItem("worked", JSON.stringify(worked));
|
||||||
|
}
|
||||||
|
|
||||||
// Startup
|
// Startup
|
||||||
$(document).ready(function() {
|
$(document).ready(function() {
|
||||||
// Call loadOptions(), this will then trigger loading spots and setting up timers.
|
// Call loadOptions(), this will then trigger loading spots and setting up timers.
|
||||||
|
|||||||
@@ -1,5 +1,10 @@
|
|||||||
// Storage for the spot data that the server gives us.
|
// Storage for the spot data that the server gives us.
|
||||||
var spots = []
|
var spots = []
|
||||||
|
// List of people the user has worked. Each entry has the format callsign-band-mode. These can be added to the list by
|
||||||
|
// ticking the checkbox on a row of the table, and cleared from the Display menu. Where a row would be added to the
|
||||||
|
// table and the callsign-band-mode is in this list, it is shown struck through as already worked. This is persisted
|
||||||
|
// to localStorage.
|
||||||
|
let worked = []
|
||||||
|
|
||||||
// Dynamically add CSS code for the band toggle buttons to be in the appropriate colour.
|
// Dynamically add CSS code for the band toggle buttons to be in the appropriate colour.
|
||||||
// Some band names contain decimal points which are not allowed in CSS classes, so we text-replace them to "p".
|
// Some band names contain decimal points which are not allowed in CSS classes, so we text-replace them to "p".
|
||||||
@@ -118,6 +123,11 @@ function setBandColorSchemeFromUI() {
|
|||||||
window.location.reload();
|
window.location.reload();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Query if a callsign-band-mode combination as has already been worked
|
||||||
|
function alreadyWorked(callsign, band, mode) {
|
||||||
|
return worked.includes(callsign + "-" + band + "-" + mode);
|
||||||
|
}
|
||||||
|
|
||||||
// Reload spots on becoming visible. This forces a refresh when used as a PWA and the user switches back to the PWA
|
// Reload spots on becoming visible. This forces a refresh when used as a PWA and the user switches back to the PWA
|
||||||
// after some time has passed with it in the background.
|
// after some time has passed with it in the background.
|
||||||
addEventListener("visibilitychange", (event) => {
|
addEventListener("visibilitychange", (event) => {
|
||||||
@@ -125,3 +135,12 @@ addEventListener("visibilitychange", (event) => {
|
|||||||
loadSpots();
|
loadSpots();
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
|
// Startup
|
||||||
|
$(document).ready(function() {
|
||||||
|
// Load worked list
|
||||||
|
var tmpWorked = JSON.parse(localStorage.getItem("worked"));
|
||||||
|
if (tmpWorked) {
|
||||||
|
worked = tmpWorked;
|
||||||
|
}
|
||||||
|
});
|
||||||
Reference in New Issue
Block a user