Gate widget pollers on their Widgets-tab enabled flag, not just sidebar visibility

Disabling Weather/Stocks/PoE2 in the Widgets tab previously only hid the
sidebar box — the backend kept polling on schedule regardless. scheduler.ts
now skips both the interval tick and the startup-immediate poll for any
widget currently disabled, so there's no outbound call for something nobody's
displaying. Re-enabling a widget (via PATCH /api/admin/settings) triggers an
immediate poll instead of waiting out its normal cadence, mirroring the
existing weather-location-change behavior.

Verified live: booted with poe2 disabled and confirmed no poll fired at
startup (previously always immediate), then re-enabled via PATCH and
confirmed the poll fired immediately.
This commit is contained in:
Claude
2026-07-26 18:40:50 +00:00
parent 175794cfc3
commit 2f083feb76
2 changed files with 33 additions and 4 deletions
+16
View File
@@ -38,12 +38,28 @@ export async function registerAdminRoutes(app: FastifyInstance) {
categoriesDb.setCategoryOrder(body.categoryPriority);
delete body.categoryPriority;
}
const before = settingsDb.getSettings();
const settings = withStorageUsed(settingsDb.updateSettings(body));
if (body.weather) {
// Poll immediately rather than waiting for the next scheduler tick (up to 45
// minutes) — the admin just changed the location/unit and expects to see it reflected.
pollWeatherNow().catch((err) => logger.error('weather', `Immediate poll failed: ${err.message}`));
}
if (body.widgets) {
// Re-enabling a widget (see the Widgets tab) should show fresh data right away
// instead of waiting out its normal cadence (up to 45m/15m/1h) — scheduler.ts
// skips polling entirely while a widget is disabled, so there's nothing recent
// to fall back on otherwise.
if (body.widgets.weather && !before.widgets.weather) {
pollWeatherNow().catch((err) => logger.error('weather', `Immediate poll failed: ${err.message}`));
}
if (body.widgets.stocks && !before.widgets.stocks) {
pollStocksNow().catch((err) => logger.error('stocks', `Immediate poll failed: ${err.message}`));
}
if (body.widgets.poe2 && !before.widgets.poe2) {
pollPoe2Now().catch((err) => logger.error('poe2', `Immediate poll failed: ${err.message}`));
}
}
return { ...settings, categoryPriority: categoriesDb.listCategories() };
});
+17 -4
View File
@@ -69,19 +69,32 @@ export function startScheduler() {
// Immediate first call for all three — unlike RSS sources (whose "due" check makes a
// brand-new source eligible on the very next 1-minute tick), weather/stocks/poe2 have
// no such shortcut; without this the sidebar is empty for up to 45/15/60 minutes after
// every restart.
pollWeatherNow().catch((err) => logger.error('weather', `Initial poll failed: ${err.message}`));
// every restart. Each is also gated on its Widgets-tab enabled flag (see
// admin/settings' consolidated Widgets tab) — disabling a widget stops these external
// calls entirely rather than just hiding the sidebar box, so there's no pointless
// polling for something nobody's looking at. Re-enabling it triggers an immediate
// poll instead (see admin.ts's PATCH /api/admin/settings), same as this initial call.
if (settingsDb.getSettings().widgets.weather) {
pollWeatherNow().catch((err) => logger.error('weather', `Initial poll failed: ${err.message}`));
}
setInterval(() => {
if (!settingsDb.getSettings().widgets.weather) return;
pollWeatherNow().catch((err) => logger.error('weather', `Poll tick failed: ${err.message}`));
}, WEATHER_TICK_MS);
pollStocksNow().catch((err) => logger.error('stocks', `Initial poll failed: ${err.message}`));
if (settingsDb.getSettings().widgets.stocks) {
pollStocksNow().catch((err) => logger.error('stocks', `Initial poll failed: ${err.message}`));
}
setInterval(() => {
if (!settingsDb.getSettings().widgets.stocks) return;
pollStocksNow().catch((err) => logger.error('stocks', `Poll tick failed: ${err.message}`));
}, STOCKS_TICK_MS);
pollPoe2Now().catch((err) => logger.error('poe2', `Initial poll failed: ${err.message}`));
if (settingsDb.getSettings().widgets.poe2) {
pollPoe2Now().catch((err) => logger.error('poe2', `Initial poll failed: ${err.message}`));
}
setInterval(() => {
if (!settingsDb.getSettings().widgets.poe2) return;
pollPoe2Now().catch((err) => logger.error('poe2', `Poll tick failed: ${err.message}`));
}, POE2_TICK_MS);