From 7e9cc153dbf9afa866122ec81b583f385c2b09c6 Mon Sep 17 00:00:00 2001 From: David Heinemeier Hansson Date: Mon, 3 Aug 2026 15:40:22 -0500 Subject: [PATCH] Retry failed weather fetches so the bar icon can't stay stuck The Open-Meteo fetch is the only thing that updates the bar icon when a location is configured, but a failed response was dropped silently with no retry, leaving a stale icon until the next refresh tick. Give it the same short retry loop the wttr fetch already has, and reset both retry budgets on each full refresh cycle so an exhausted round (e.g. waking before the network is back) doesn't starve retries for the session. Co-Authored-By: Claude Fable 5 --- shell/plugins/panels/weather/Panel.qml | 32 ++++++++++++++++++++++++-- 1 file changed, 30 insertions(+), 2 deletions(-) diff --git a/shell/plugins/panels/weather/Panel.qml b/shell/plugins/panels/weather/Panel.qml index 3757d942..2053d88b 100644 --- a/shell/plugins/panels/weather/Panel.qml +++ b/shell/plugins/panels/weather/Panel.qml @@ -87,6 +87,7 @@ Panel { onLocationQueryChanged: { if (savingLocation) savingLocationQueryStarted = true forecastRetries = 0 + dailyForecastRetries = 0 forecastProc.running = false dailyForecastProc.running = false Qt.callLater(refresh) @@ -112,6 +113,7 @@ Panel { } property int forecastRetries: 0 + property int dailyForecastRetries: 0 // Click-to-edit state for the location label. property bool editingLocation: false @@ -148,6 +150,11 @@ Panel { readonly property string reportHumidity: current ? (current.humidity + "%") : "" function refresh() { + // Each full refresh cycle gets a fresh retry budget, so an earlier + // exhausted round (e.g. waking with the network still down) doesn't + // starve retries for the rest of the session. + forecastRetries = 0 + dailyForecastRetries = 0 if (!forecastProc.running) forecastProc.running = true if (root.locationQuery === "" && !locationProc.running) locationProc.running = true // With stored coordinates this fetches open-meteo right away — no need @@ -368,22 +375,42 @@ Panel { onTriggered: if (!forecastProc.running) forecastProc.running = true } + // With configured coordinates this fetch is the only thing that updates the + // bar icon, so a dropped response (e.g. waking before the network is back) + // must retry rather than wait out the refresh timer with a stale icon. + function scheduleDailyForecastRetry() { + if (dailyForecastRetries >= 3) return + dailyForecastRetries++ + dailyForecastRetryTimer.restart() + } + + Timer { + id: dailyForecastRetryTimer + interval: 2500 + onTriggered: root.refreshDailyForecast(null) + } + Process { id: dailyForecastProc stdout: StdioCollector { waitForEnd: true onStreamFinished: { var raw = String(text || "").trim() - if (!raw) return + if (!raw) { + root.scheduleDailyForecastRetry() + return + } try { var parsed = JSON.parse(raw) var parsedCurrent = Model.openMeteoCurrentCondition(parsed) root.dailyForecastReport = parsed root.label = Model.currentIcon(parsedCurrent, root.label) + root.dailyForecastRetries = 0 if (Model.weatherResponseCompletesSave(root.hasConfiguredCoordinates, "open-meteo")) root.finishSavingLocation() } catch (e) { - // Keep last-good daily forecast on parse failure. + // Keep last-good daily forecast visible, but try again shortly. + root.scheduleDailyForecastRetry() } } } @@ -418,6 +445,7 @@ Panel { if (!root.savingLocationQueryStarted) { root.savingLocationQueryStarted = true root.forecastRetries = 0 + root.dailyForecastRetries = 0 forecastProc.running = false dailyForecastProc.running = false Qt.callLater(root.refresh)