diff --git a/backend/src/api/admin.ts b/backend/src/api/admin.ts index 8792ef5..cf2b1f3 100644 --- a/backend/src/api/admin.ts +++ b/backend/src/api/admin.ts @@ -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() }; }); diff --git a/backend/src/queue/scheduler.ts b/backend/src/queue/scheduler.ts index 39d9f56..76dd754 100644 --- a/backend/src/queue/scheduler.ts +++ b/backend/src/queue/scheduler.ts @@ -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);