diff --git a/server/scripts/modules/currentweatherscroll.mjs b/server/scripts/modules/currentweatherscroll.mjs
index b3411e8..6700b16 100644
--- a/server/scripts/modules/currentweatherscroll.mjs
+++ b/server/scripts/modules/currentweatherscroll.mjs
@@ -19,6 +19,8 @@ let hazardData;
// start drawing conditions
// reset starts from the first item in the text scroll list
const start = () => {
+ // if already started, nothing to do
+ if (interval) return;
// set up the interval if needed
if (!interval) {
interval = setInterval(incrementInterval, 500);
@@ -211,6 +213,7 @@ const drawScrollCondition = (screen) => {
const parseMessage = (event) => {
if (event?.data?.type === 'current-weather-scroll') {
if (event.data?.method === 'start') start();
+ if (event.data?.method === 'reload') stop(true);
}
};
diff --git a/server/scripts/modules/hazards.mjs b/server/scripts/modules/hazards.mjs
index 1ec5b0c..2cd1084 100644
--- a/server/scripts/modules/hazards.mjs
+++ b/server/scripts/modules/hazards.mjs
@@ -23,8 +23,15 @@ class Hazards extends WeatherDisplay {
this.showOnProgress = false;
this.okToDrawCurrentConditions = false;
+ // force a 1-minute refresh time for the most up-to-date hazards
+ this.refreshTime = 60_000;
+
// 0 screens skips this during "play"
this.timing.totalScreens = 0;
+
+ // take note of the already-shown alert ids
+ this.viewedAlerts = new Set();
+ this.viewedGetCount = 0;
}
async getData(weatherParameters, refresh) {
@@ -33,9 +40,18 @@ class Hazards extends WeatherDisplay {
// hazards performs a silent refresh, but does not fall back to a previous fetch if no data is available
// this is intentional to ensure the latest alerts only are displayed.
+ // auto reload must be set up specifically for hazards in case it is disabled via checkbox (for the bottom line scroll)
+ if (this.autoRefreshHandle === null) this.setAutoReload();
+
const alert = this.checkbox.querySelector('.alert');
alert.classList.remove('show');
+ // if not a refresh (new site), all alerts are new
+ if (!refresh) {
+ this.viewedGetCount = 0;
+ this.viewedAlerts.clear();
+ }
+
try {
// get the forecast
const url = new URL('https://api.weather.gov/alerts/active');
@@ -48,8 +64,23 @@ class Hazards extends WeatherDisplay {
const filteredAlerts = sortedAlerts.filter((hazard) => hazard.properties.severity !== 'Unknown' && (!hasImmediate || (hazard.properties.urgency === 'Immediate')));
this.data = filteredAlerts;
+ // every 10 times through the get process (10 minutes), reset the viewed messages
+ if (this.viewedGetCount >= 10) {
+ this.viewedGetCount = 0;
+ this.viewedAlerts.clear();
+ }
+ this.viewedGetCount += 1;
+
+ // count up un-viewed alerts
+ const unViewed = this.data.reduce((count, hazard) => {
+ if (!this.viewedAlerts.has(hazard.id)) return count + 1;
+ return count;
+ }, 0);
+
// show alert indicator
- if (this.data.length > 0) alert.classList.add('show');
+ if (unViewed > 0) alert.classList.add('show');
+ // draw the canvas to calculate the new timings and activate hazards in the slide deck again
+ this.drawLongCanvas();
} catch (error) {
console.error('Get hazards failed');
console.error(error.status, error.responseJSON);
@@ -73,7 +104,10 @@ class Hazards extends WeatherDisplay {
const list = this.elem.querySelector('.hazard-lines');
list.innerHTML = '';
- const lines = this.data.map((data) => {
+ // filter viewed alerts
+ const unViewed = this.data.filter((data) => !this.viewedAlerts.has(data.id));
+
+ const lines = unViewed.map((data) => {
const fillValues = {};
// text
fillValues['hazard-text'] = `${data.properties.event}
${data.properties.description.replaceAll('\n\n', '
').replaceAll('\n', ' ')}`;
@@ -92,18 +126,22 @@ class Hazards extends WeatherDisplay {
}
// update timing
+ this.setTiming(list);
+ this.setStatus(STATUS.loaded);
+ }
+
+ setTiming(list) {
// set up the timing
this.timing.baseDelay = 20;
// 24 hours = 6 pages
- const pages = Math.max(Math.ceil(list.scrollHeight / 400) - 3, 1);
- const timingStep = 400;
+ const pages = Math.max(Math.ceil(list.scrollHeight / 480) - 4);
+ const timingStep = 480;
this.timing.delay = [150 + timingStep];
// add additional pages
for (let i = 0; i < pages; i += 1) this.timing.delay.push(timingStep);
// add the final 3 second delay
this.timing.delay.push(250);
this.calcNavTiming();
- this.setStatus(STATUS.loaded);
}
drawCanvas() {
@@ -152,6 +190,8 @@ class Hazards extends WeatherDisplay {
if (superValue === false) {
// set total screens to zero to take this out of the rotation
this.timing.totalScreens = 0;
+ // note the ids shown
+ this?.data?.forEach((alert) => this.viewedAlerts.add(alert.id));
}
// return the value as expected
return superValue;
diff --git a/server/scripts/modules/navigation.mjs b/server/scripts/modules/navigation.mjs
index 4630638..d4e6f2f 100644
--- a/server/scripts/modules/navigation.mjs
+++ b/server/scripts/modules/navigation.mjs
@@ -67,6 +67,9 @@ const getWeather = async (latLon, haveDataCallback) => {
// update the main process for display purposes
populateWeatherParameters(weatherParameters);
+ // reset the scroll
+ postMessage({ type: 'current-weather-scroll', method: 'reload' });
+
// draw the progress canvas and hide others
hideAllCanvases();
document.querySelector('#loading').style.display = 'none';
diff --git a/server/scripts/modules/weatherdisplay.mjs b/server/scripts/modules/weatherdisplay.mjs
index a9aa948..cd38ea9 100644
--- a/server/scripts/modules/weatherdisplay.mjs
+++ b/server/scripts/modules/weatherdisplay.mjs
@@ -444,7 +444,9 @@ class WeatherDisplay {
}
setAutoReload() {
- this.autoRefreshHandle = this.autoRefreshHandle ?? setInterval(() => this.getData(false, true), settings.refreshTime.value);
+ // refresh time can be forced by the user (for hazards)
+ const refreshTime = this.refreshTime ?? settings.refreshTime.value;
+ this.autoRefreshHandle = this.autoRefreshHandle ?? setInterval(() => this.getData(false, true), refreshTime);
}
}