From a6d722f9a985381f29da6b8e5442c8c05fb9ef64 Mon Sep 17 00:00:00 2001 From: LemmyCook Date: Thu, 28 Aug 2025 08:20:17 -0400 Subject: [PATCH] LocationService + Settings: improved service stability and show geocoding results in the settings --- Modules/SettingsPanel/Tabs/TimeWeatherTab.qml | 36 ++++++--- Services/LocationService.qml | 77 +++++++++++++++---- 2 files changed, 90 insertions(+), 23 deletions(-) diff --git a/Modules/SettingsPanel/Tabs/TimeWeatherTab.qml b/Modules/SettingsPanel/Tabs/TimeWeatherTab.qml index 60bf275..519177f 100644 --- a/Modules/SettingsPanel/Tabs/TimeWeatherTab.qml +++ b/Modules/SettingsPanel/Tabs/TimeWeatherTab.qml @@ -9,15 +9,33 @@ ColumnLayout { id: root // Location section - NTextInput { - label: "Location name" - description: "Choose a known location near you." - text: Settings.data.location.name - placeholderText: "Enter the location name" - Layout.fillWidth: true - onEditingFinished: { - Settings.data.location.name = text.trim() - LocationService.resetWeather() + RowLayout { + spacing: Style.marginL * scaling + + NTextInput { + label: "Location name" + description: "Choose a known location near you." + text: Settings.data.location.name + placeholderText: "Enter the location name" + onEditingFinished: { + // Verify the location has really changed to avoid extra resets + var newLocation = text.trim() + if (newLocation != Settings.data.location.name) { + Settings.data.location.name = newLocation + LocationService.resetWeather() + } + } + } + + NText { + visible: LocationService.data.coordinatesReady + text: `${LocationService.data.stableName} (${LocationService.displayCoordinates})` + font.pointSize: Style.fontSizeS * scaling + color: Color.mOnSurfaceVariant + verticalAlignment: Text.AlignVCenter + horizontalAlignment: Text.AlignRight + Layout.alignment: Qt.AlignBottom + Layout.bottomMargin: 12 * scaling } } diff --git a/Services/LocationService.qml b/Services/LocationService.qml index 2e29e88..5db601e 100644 --- a/Services/LocationService.qml +++ b/Services/LocationService.qml @@ -6,7 +6,7 @@ import Quickshell.Io import qs.Commons import qs.Services -// Weather logic and caching +// Weather logic and caching with stable UI properties Singleton { id: root @@ -16,9 +16,16 @@ Singleton { property alias data: adapter // Used to access via LocationService.data.xxx FileView { + id: locationFileView path: locationFile - onAdapterUpdated: writeAdapter() + onAdapterUpdated: saveTimer.start() onLoaded: { + // Initialize stable properties on load + if (adapter.latitude !== "" && adapter.longitude !== "" && adapter.weatherLastFetch > 0) { + adapter.stableLatitude = adapter.latitude + adapter.stableLongitude = adapter.longitude + adapter.coordinatesReady = true + } updateWeather() } onLoadFailed: function (error) { @@ -28,14 +35,31 @@ Singleton { JsonAdapter { id: adapter + // Core data properties property string latitude: "" property string longitude: "" property string name: "" property int weatherLastFetch: 0 property var weather: null + + // Stable UI properties - only updated when location is fully resolved + property bool coordinatesReady: false + property string stableLatitude: "" + property string stableLongitude: "" + property string stableName: "" } } + // Helper property for UI components (outside JsonAdapter to avoid binding loops) + readonly property string displayCoordinates: { + if (!data.coordinatesReady || data.stableLatitude === "" || data.stableLongitude === "") { + return "" + } + const lat = parseFloat(data.stableLatitude).toFixed(4) + const lon = parseFloat(data.stableLongitude).toFixed(4) + return `${lat}, ${lon}` + } + // Every 20s check if we need to fetch new weather Timer { id: updateTimer @@ -47,6 +71,13 @@ Singleton { } } + Timer { + id: saveTimer + running: false + interval: 1000 + onTriggered: locationFileView.writeAdapter() + } + // -------------------------------- function init() { // does nothing but ensure the singleton is created @@ -58,12 +89,21 @@ Singleton { function resetWeather() { Logger.log("Location", "Resetting weather data") + // Mark as changing to prevent UI updates + data.coordinatesReady = false + + // Reset core data data.latitude = "" data.longitude = "" data.name = "" data.weatherLastFetch = 0 data.weather = null + // Reset stable properties + data.stableLatitude = "" + data.stableLongitude = "" + data.stableName = "" + // Try to fetch immediately updateWeather() } @@ -75,10 +115,6 @@ Singleton { return } - if (data.latitude === "") { - Logger.warn("Location", "Why is my latitude empty") - } - if ((data.weatherLastFetch === "") || (data.weather === null) || (data.latitude === "") || (data.longitude === "") || (data.name !== Settings.data.location.name) || (Time.timestamp >= data.weatherLastFetch + weatherUpdateFrequency)) { @@ -89,9 +125,17 @@ Singleton { // -------------------------------- function getFreshWeather() { isFetchingWeather = true - if ((data.latitude === "") || (data.longitude === "") || (data.name !== Settings.data.location.name)) { - _geocodeLocation(Settings.data.location.name, function (latitude, longitude) { + // Check if location name has changed + const locationChanged = data.name !== Settings.data.location.name + if (locationChanged) { + data.coordinatesReady = false + Logger.log("Location", "Location changed from", data.name, "to", Settings.data.location.name) + } + + if ((data.latitude === "") || (data.longitude === "") || locationChanged) { + + _geocodeLocation(Settings.data.location.name, function (latitude, longitude, name, country) { Logger.log("Location", "Geocoded", Settings.data.location.name, "to:", latitude, "/", longitude) // Save location name @@ -101,6 +145,8 @@ Singleton { data.latitude = latitude.toString() data.longitude = longitude.toString() + data.stableName = `${name}, ${country}` + _fetchWeather(latitude, longitude, errorCallback) }, errorCallback) } else { @@ -119,9 +165,8 @@ Singleton { if (xhr.status === 200) { try { var geoData = JSON.parse(xhr.responseText) - // Logger.logJSON.stringify(geoData)) if (geoData.lat != null) { - callback(geoData.lat, geoData.lng) + callback(geoData.lat, geoData.lng, geoData.name, geoData.country) } else { errorCallback("Location", "could not resolve location name") } @@ -148,15 +193,19 @@ Singleton { if (xhr.status === 200) { try { var weatherData = JSON.parse(xhr.responseText) + //console.log(JSON.stringify(weatherData)) - // Save data + // Save core data data.weather = weatherData data.weatherLastFetch = Time.timestamp - data.latitude = weatherData.latitude.toString() - data.longitude = weatherData.longitude.toString() + + // Update stable display values only when complete and successful + data.stableLatitude = data.latitude = weatherData.latitude.toString() + data.stableLongitude = data.longitude = weatherData.longitude.toString() + data.coordinatesReady = true isFetchingWeather = false - Logger.log("Location", "Cached weather to disk") + Logger.log("Location", "Cached weather to disk - stable coordinates updated") } catch (e) { errorCallback("Location", "Failed to parse weather data") }