diff --git a/quickshell/Services/IpService.qml b/quickshell/Services/IpService.qml index 28cb59e..9a92395 100644 --- a/quickshell/Services/IpService.qml +++ b/quickshell/Services/IpService.qml @@ -1,6 +1,7 @@ import QtQuick import Quickshell import Quickshell.Io +import qs.Services import qs.Utils pragma Singleton @@ -14,44 +15,35 @@ Singleton { property string geoURLToken: "" function fetchIP() { - const xhr = new XMLHttpRequest(); - xhr.timeout = fetchTimeout * 1000; - xhr.onreadystatechange = function() { - if (xhr.readyState === XMLHttpRequest.DONE) { - if (xhr.status === 200) { - try { - const response = JSON.parse(xhr.responseText); - if (response && response.ip) { - let newIP = response.ip; - Logger.log("IpService", "Fetched IP: " + newIP); - if (newIP !== ip) { - ip = newIP; - fetchGeoInfo(); // Fetch geo info only if IP has changed - } - } else { - ip = "N/A"; - countryCode = "N/A"; - Logger.error("IpService", "IP response does not contain 'ip' field"); + curl.fetch(ipURL, function(success, data) { + if (success) { + try { + const response = JSON.parse(data); + if (response && response.ip) { + let newIP = response.ip; + Logger.log("IpService", "Fetched IP: " + newIP); + if (newIP !== ip) { + ip = newIP; + fetchGeoInfo(); // Fetch geo info only if IP has changed + SendNotification.show("New IP", `IP: ${ip}\nCountry: ${countryCode}`); + cacheFile.writeAdapter(); } - } catch (e) { + } else { ip = "N/A"; countryCode = "N/A"; - Logger.error("IpService", "Failed to parse IP response: " + e); + Logger.error("IpService", "IP response does not contain 'ip' field"); } - } else { + } catch (e) { ip = "N/A"; countryCode = "N/A"; - Logger.error("IpService", "Failed to fetch IP, status: " + xhr.status); + Logger.error("IpService", "Failed to parse IP response: " + e); } + } else { + ip = "N/A"; + countryCode = "N/A"; + Logger.error("IpService", "Failed to fetch IP"); } - }; - xhr.ontimeout = function() { - ip = "N/A"; - countryCode = "N/A"; - Logger.error("IpService", "Fetch IP request timed out"); - }; - xhr.open("GET", ipURL); - xhr.send(); + }); } function fetchGeoInfo() { @@ -59,47 +51,35 @@ Singleton { countryCode = "N/A"; return ; } - const xhr = new XMLHttpRequest(); - xhr.timeout = fetchTimeout * 1000; - xhr.onreadystatechange = function() { - if (xhr.readyState === XMLHttpRequest.DONE) { - if (xhr.status === 200) { - try { - const response = JSON.parse(xhr.responseText); - if (response && response.country_code) { - let newCountryCode = response.country_code; - Logger.log("IpService", "Fetched country code: " + newCountryCode); - if (newCountryCode !== countryCode) { - countryCode = newCountryCode; - SendNotification.show("New IP", `IP: ${ip}\nCountry: ${newCountryCode}`); - } - } else { - countryCode = "N/A"; - Logger.error("IpService", "Geo response does not contain 'country' field"); - } - cacheFileAdapter.ip = ip; - cacheFileAdapter.geoInfo = response; - cacheFile.writeAdapter(); - } catch (e) { - countryCode = "N/A"; - Logger.error("IpService", "Failed to parse geo response: " + e); - } - } else { - countryCode = "N/A"; - Logger.error("IpService", "Failed to fetch geo info, status: " + xhr.status); - } - } - }; - xhr.ontimeout = function() { - countryCode = "N/A"; - Logger.error("IpService", "Fetch geo info request timed out"); - }; let url = geoURL + ip; if (geoURLToken) url += "?token=" + geoURLToken; - xhr.open("GET", url); - xhr.send(); + curl.fetch(url, function(success, data) { + if (success) { + try { + const response = JSON.parse(data); + if (response && response.country_code) { + let newCountryCode = response.country_code; + Logger.log("IpService", "Fetched country code: " + newCountryCode); + if (newCountryCode !== countryCode) + countryCode = newCountryCode; + + } else { + countryCode = "N/A"; + Logger.error("IpService", "Geo response does not contain 'country_code' field"); + } + cacheFileAdapter.ip = ip; + cacheFileAdapter.geoInfo = response; + } catch (e) { + countryCode = "N/A"; + Logger.error("IpService", "Failed to parse geo response: " + e); + } + } else { + countryCode = "N/A"; + Logger.error("IpService", "Failed to fetch geo info"); + } + }); } function refresh() { @@ -112,6 +92,10 @@ Singleton { Component.onCompleted: { } + NetworkFetch { + id: curl + } + FileView { id: tokenFile diff --git a/quickshell/Services/LocationService.qml b/quickshell/Services/LocationService.qml index 8f5f70b..d40617f 100644 --- a/quickshell/Services/LocationService.qml +++ b/quickshell/Services/LocationService.qml @@ -2,6 +2,7 @@ import QtQuick import Quickshell import Quickshell.Io import qs.Constants +import qs.Services import qs.Utils pragma Singleton @@ -96,57 +97,47 @@ Singleton { function _geocodeLocation(locationName, callback, errorCallback) { Logger.log("Location", "Geocoding location name"); var geoUrl = "https://assets.noctalia.dev/geocode.php?city=" + encodeURIComponent(locationName) + "&language=en&format=json"; - var xhr = new XMLHttpRequest(); - xhr.onreadystatechange = function() { - if (xhr.readyState === XMLHttpRequest.DONE) { - if (xhr.status === 200) { - try { - var geoData = JSON.parse(xhr.responseText); - if (geoData.lat != null) - callback(geoData.lat, geoData.lng, geoData.name, geoData.country); - else - errorCallback("Location", "could not resolve location name"); - } catch (e) { - errorCallback("Location", "Failed to parse geocoding data: " + e); - } - } else { - errorCallback("Location", "Geocoding error: " + xhr.status); + curl.fetch(geoUrl, function(success, data) { + if (success) { + try { + var geoData = JSON.parse(data); + if (geoData.lat != null) + callback(geoData.lat, geoData.lng, geoData.name, geoData.country); + else + errorCallback("Location", "could not resolve location name"); + } catch (e) { + errorCallback("Location", "Failed to parse geocoding data: " + e); } + } else { + errorCallback("Location", "Geocoding error"); } - }; - xhr.open("GET", geoUrl); - xhr.send(); + }); } // -------------------------------- function _fetchWeather(latitude, longitude, errorCallback) { Logger.log("Location", "Fetching weather from api.open-meteo.com"); var url = "https://api.open-meteo.com/v1/forecast?latitude=" + latitude + "&longitude=" + longitude + "¤t_weather=true¤t=relativehumidity_2m,surface_pressure&daily=temperature_2m_max,temperature_2m_min,weathercode&timezone=auto"; - var xhr = new XMLHttpRequest(); - xhr.onreadystatechange = function() { - if (xhr.readyState === XMLHttpRequest.DONE) { - if (xhr.status === 200) { - try { - var weatherData = JSON.parse(xhr.responseText); - // Save core data - data.weather = weatherData; - data.weatherLastFetch = Time.timestamp; - // Update stable display values only when complete and successful - root.stableLatitude = data.latitude = weatherData.latitude.toString(); - root.stableLongitude = data.longitude = weatherData.longitude.toString(); - root.coordinatesReady = true; - isFetchingWeather = false; - Logger.log("Location", "Cached weather to disk - stable coordinates updated"); - } catch (e) { - errorCallback("Location", "Failed to parse weather data"); - } - } else { - errorCallback("Location", "Weather fetch error: " + xhr.status); + curl.fetch(url, function(success, data) { + if (success) { + try { + var weatherData = JSON.parse(data); + // Save core data + data.weather = weatherData; + data.weatherLastFetch = Time.timestamp; + // Update stable display values only when complete and successful + root.stableLatitude = data.latitude = weatherData.latitude.toString(); + root.stableLongitude = data.longitude = weatherData.longitude.toString(); + root.coordinatesReady = true; + isFetchingWeather = false; + Logger.log("Location", "Cached weather to disk - stable coordinates updated"); + } catch (e) { + errorCallback("Location", "Failed to parse weather data: " + e); } + } else { + errorCallback("Location", "Weather fetch error"); } - }; - xhr.open("GET", url); - xhr.send(); + }); } // -------------------------------- @@ -320,4 +311,8 @@ Singleton { onTriggered: locationFileView.writeAdapter() } + NetworkFetch { + id: curl + } + } diff --git a/quickshell/Services/NetworkFetch.qml b/quickshell/Services/NetworkFetch.qml new file mode 100644 index 0000000..e2e20e0 --- /dev/null +++ b/quickshell/Services/NetworkFetch.qml @@ -0,0 +1,66 @@ +import QtQuick +import Quickshell +import Quickshell.Io +import qs.Utils + +Item { + id: root + + property real fetchTimeout: 10 // in seconds + property string fetchedData: "" + property var fetchingCallback: null + + function fetch(url, callback) { + if (curlProcess.running) { + Logger.log("NetworkFetch", "A fetch operation is already in progress."); + return ; + } + fetchedData = ""; + fetchingCallback = callback; + curlProcess.command = ["curl", "-s", "-L", "-m", fetchTimeout.toString(), url]; + curlProcess.running = true; + } + + function fakeFetch(resp, callback) { + if (curlProcess.running) { + Logger.log("NetworkFetch", "A fetch operation is already in progress."); + return ; + } + fetchedData = ""; + fetchingCallback = callback; + curlProcess.command = ["echo", resp]; + curlProcess.running = true; + } + + Process { + id: curlProcess + + running: false + onStarted: { + Logger.log("NetworkFetch", "Process started with command: " + curlProcess.command.join(" ")); + } + onExited: function(exitCode, exitStatus) { + if (!fetchingCallback) { + Logger.error("NetworkFetch", "No callback defined for fetch operation."); + return ; + } + if (exitCode === 0) { + Logger.log("NetworkFetch", "Fetch completed successfully."); + Logger.log("NetworkFetch", "Fetched data: " + fetchedData); + fetchingCallback(true, fetchedData); + } else { + Logger.error("NetworkFetch", "Fetch failed with exit code: " + exitCode); + fetchingCallback(false, ""); + } + } + + stdout: SplitParser { + splitMarker: "" + onRead: (data) => { + fetchedData += data; + } + } + + } + +}