feat: import M3Shapes, M3 loading indicator and weather + resources + media revamp in lockscreen
This commit is contained in:
+31
-3
@@ -1,12 +1,12 @@
|
||||
pragma Singleton
|
||||
|
||||
import QtQml
|
||||
import Quickshell
|
||||
import Quickshell.Io
|
||||
import Quickshell.Services.Mpris
|
||||
import QtQml
|
||||
import ZShell
|
||||
import qs.Config
|
||||
import qs.Components
|
||||
import qs.Config
|
||||
|
||||
Singleton {
|
||||
id: root
|
||||
@@ -15,7 +15,24 @@ Singleton {
|
||||
readonly property list<MprisPlayer> list: Mpris.players.values
|
||||
property alias manualActive: props.manualActive
|
||||
|
||||
function getArtUrl(player: MprisPlayer): string {
|
||||
if (!player)
|
||||
return "";
|
||||
if (player.trackArtUrl)
|
||||
return player.trackArtUrl;
|
||||
|
||||
const url = player.metadata["xesam:url"] ?? "";
|
||||
if (url.startsWith("https://www.youtube.com/watch")) {
|
||||
// Fallback for youtube
|
||||
const id = url.match(/[?&]v=([\w-]{11})/)?.[1];
|
||||
return id ? `https://img.youtube.com/vi/${id}/hqdefault.jpg` : "";
|
||||
}
|
||||
return "";
|
||||
}
|
||||
|
||||
function getIdentity(player: MprisPlayer): string {
|
||||
if (!player)
|
||||
return "";
|
||||
const alias = Config.services.playerAliases.find(a => a.from === player.identity);
|
||||
return alias?.to ?? player.identity;
|
||||
}
|
||||
@@ -25,9 +42,12 @@ Singleton {
|
||||
if (!Config.utilities.toasts.nowPlaying) {
|
||||
return;
|
||||
}
|
||||
if (root.active.trackArtist != "" && root.active.trackTitle != "") {
|
||||
Toaster.toast(qsTr("Now Playing"), qsTr("%1 - %2").arg(root.active.trackArtist).arg(root.active.trackTitle), "music_note");
|
||||
}
|
||||
}
|
||||
|
||||
target: active
|
||||
target: root.active
|
||||
}
|
||||
|
||||
PersistentProperties {
|
||||
@@ -38,8 +58,10 @@ Singleton {
|
||||
reloadableId: "players"
|
||||
}
|
||||
|
||||
// qmllint disable unresolved-type
|
||||
CustomShortcut {
|
||||
description: "Toggle media playback"
|
||||
// qmllint enable unresolved-type
|
||||
name: "mediaToggle"
|
||||
|
||||
onPressed: {
|
||||
@@ -49,8 +71,10 @@ Singleton {
|
||||
}
|
||||
}
|
||||
|
||||
// qmllint disable unresolved-type
|
||||
CustomShortcut {
|
||||
description: "Previous track"
|
||||
// qmllint enable unresolved-type
|
||||
name: "mediaPrev"
|
||||
|
||||
onPressed: {
|
||||
@@ -60,8 +84,10 @@ Singleton {
|
||||
}
|
||||
}
|
||||
|
||||
// qmllint disable unresolved-type
|
||||
CustomShortcut {
|
||||
description: "Next track"
|
||||
// qmllint enable unresolved-type
|
||||
name: "mediaNext"
|
||||
|
||||
onPressed: {
|
||||
@@ -71,8 +97,10 @@ Singleton {
|
||||
}
|
||||
}
|
||||
|
||||
// qmllint disable unresolved-type
|
||||
CustomShortcut {
|
||||
description: "Stop media playback"
|
||||
// qmllint enable unresolved-type
|
||||
name: "mediaStop"
|
||||
|
||||
onPressed: root.active?.stop()
|
||||
|
||||
+107
-32
@@ -1,7 +1,7 @@
|
||||
pragma Singleton
|
||||
|
||||
import Quickshell
|
||||
import QtQuick
|
||||
import Quickshell
|
||||
import ZShell
|
||||
import qs.Config
|
||||
|
||||
@@ -12,15 +12,15 @@ Singleton {
|
||||
property var cc
|
||||
property string city
|
||||
readonly property string description: cc?.weatherDesc ?? qsTr("No weather")
|
||||
readonly property string feelsLike: `${cc?.feelsLikeC ?? 0}°C`
|
||||
readonly property string feelsLike: formatTemp(cc?.feelsLikeC)
|
||||
property list<var> forecast
|
||||
property list<var> hourlyForecast
|
||||
readonly property int humidity: cc?.humidity ?? 0
|
||||
readonly property string icon: cc ? Icons.getWeatherIcon(cc.weatherCode) : "cloud_alert"
|
||||
property string loc
|
||||
readonly property string sunrise: cc ? Qt.formatDateTime(new Date(cc.sunrise), "h:mm") : "--:--"
|
||||
readonly property string sunset: cc ? Qt.formatDateTime(new Date(cc.sunset), "h:mm") : "--:--"
|
||||
readonly property string temp: `${cc?.tempC ?? 0}°C`
|
||||
readonly property string sunrise: cc ? Qt.formatDateTime(new Date(cc.sunrise), Config.services.useTwelveHourClock ? "h:mm A" : "h:mm") : "--:--"
|
||||
readonly property string sunset: cc ? Qt.formatDateTime(new Date(cc.sunset), Config.services.useTwelveHourClock ? "h:mm A" : "h:mm") : "--:--"
|
||||
readonly property string temp: formatTemp(cc?.tempC)
|
||||
readonly property real windSpeed: cc?.windSpeed ?? 0
|
||||
|
||||
function fetchCityFromCoords(coords: string): void {
|
||||
@@ -29,29 +29,48 @@ Singleton {
|
||||
return;
|
||||
}
|
||||
|
||||
const [lat, lon] = coords.split(",");
|
||||
const url = `https://nominatim.openstreetmap.org/reverse?lat=${lat}&lon=${lon}&format=geocodejson`;
|
||||
Requests.get(url, text => {
|
||||
const [lat, lon] = coords.split(",").map(s => s.trim());
|
||||
const lang = Qt.locale().name.split("_")[0] || "en";
|
||||
|
||||
const fallbackToBigDataCloud = () => {
|
||||
const fallbackUrl = `https://api.bigdatacloud.net/data/reverse-geocode-client?latitude=${lat}&longitude=${lon}&localityLanguage=${lang}`;
|
||||
Requests.get(fallbackUrl, text => {
|
||||
const geo = JSON.parse(text);
|
||||
const geoCity = geo.city || geo.locality;
|
||||
if (geoCity) {
|
||||
city = fixCityName(geoCity);
|
||||
cachedCities.set(coords, city);
|
||||
} else {
|
||||
city = "Unknown City";
|
||||
}
|
||||
});
|
||||
};
|
||||
|
||||
const nominatimUrl = `https://nominatim.openstreetmap.org/reverse?lat=${lat}&lon=${lon}&format=geocodejson&accept-language=${lang}`;
|
||||
Requests.get(nominatimUrl, text => {
|
||||
const geo = JSON.parse(text).features?.[0]?.properties.geocoding;
|
||||
if (geo) {
|
||||
const geoCity = geo.type === "city" ? geo.name : geo.city;
|
||||
city = geoCity;
|
||||
cachedCities.set(coords, geoCity);
|
||||
} else {
|
||||
city = "Unknown City";
|
||||
if (geoCity) {
|
||||
city = fixCityName(geoCity);
|
||||
cachedCities.set(coords, city);
|
||||
return;
|
||||
}
|
||||
}
|
||||
});
|
||||
fallbackToBigDataCloud();
|
||||
}, fallbackToBigDataCloud);
|
||||
}
|
||||
|
||||
function fetchCoordsFromCity(cityName: string): void {
|
||||
const url = `https://geocoding-api.open-meteo.com/v1/search?name=${encodeURIComponent(cityName)}&count=1&language=en&format=json`;
|
||||
const lang = Qt.locale().name.split("_")[0] || "en";
|
||||
const url = `https://geocoding-api.open-meteo.com/v1/search?name=${encodeURIComponent(cityName)}&count=1&language=${lang}&format=json`;
|
||||
|
||||
Requests.get(url, text => {
|
||||
const json = JSON.parse(text);
|
||||
if (json.results && json.results.length > 0) {
|
||||
const result = json.results[0];
|
||||
loc = result.latitude + "," + result.longitude;
|
||||
city = result.name;
|
||||
city = fixCityName(result.name);
|
||||
} else {
|
||||
loc = "";
|
||||
reload();
|
||||
@@ -72,25 +91,21 @@ Singleton {
|
||||
cc = {
|
||||
weatherCode: json.current.weather_code,
|
||||
weatherDesc: getWeatherCondition(json.current.weather_code),
|
||||
tempC: Math.round(json.current.temperature_2m),
|
||||
tempF: Math.round(toFahrenheit(json.current.temperature_2m)),
|
||||
feelsLikeC: Math.round(json.current.apparent_temperature),
|
||||
feelsLikeF: Math.round(toFahrenheit(json.current.apparent_temperature)),
|
||||
tempC: json.current.temperature_2m,
|
||||
feelsLikeC: json.current.apparent_temperature,
|
||||
humidity: json.current.relative_humidity_2m,
|
||||
windSpeed: json.current.wind_speed_10m,
|
||||
isDay: json.current.is_day,
|
||||
sunrise: json.daily.sunrise[0],
|
||||
sunset: json.daily.sunset[0]
|
||||
sunrise: json.daily.sunrise[0].replace("T", " "),
|
||||
sunset: json.daily.sunset[0].replace("T", " ")
|
||||
};
|
||||
|
||||
const forecastList = [];
|
||||
for (let i = 0; i < json.daily.time.length; i++)
|
||||
forecastList.push({
|
||||
date: json.daily.time[i],
|
||||
maxTempC: Math.round(json.daily.temperature_2m_max[i]),
|
||||
maxTempF: Math.round(toFahrenheit(json.daily.temperature_2m_max[i])),
|
||||
minTempC: Math.round(json.daily.temperature_2m_min[i]),
|
||||
minTempF: Math.round(toFahrenheit(json.daily.temperature_2m_min[i])),
|
||||
date: json.daily.time[i].replace(/-/g, "/"),
|
||||
maxTempC: json.daily.temperature_2m_max[i],
|
||||
minTempC: json.daily.temperature_2m_min[i],
|
||||
weatherCode: json.daily.weather_code[i],
|
||||
icon: Icons.getWeatherIcon(json.daily.weather_code[i])
|
||||
});
|
||||
@@ -99,7 +114,8 @@ Singleton {
|
||||
const hourlyList = [];
|
||||
const now = new Date();
|
||||
for (let i = 0; i < json.hourly.time.length; i++) {
|
||||
const time = new Date(json.hourly.time[i]);
|
||||
const time = new Date(json.hourly.time[i].replace("T", " "));
|
||||
|
||||
if (time < now)
|
||||
continue;
|
||||
|
||||
@@ -107,7 +123,7 @@ Singleton {
|
||||
timestamp: json.hourly.time[i],
|
||||
hour: time.getHours(),
|
||||
tempC: Math.round(json.hourly.temperature_2m[i]),
|
||||
tempF: Math.round(toFahrenheit(json.hourly.temperature_2m[i])),
|
||||
precipChance: json.hourly.precipitation_probability[i],
|
||||
weatherCode: json.hourly.weather_code[i],
|
||||
icon: Icons.getWeatherIcon(json.hourly.weather_code[i])
|
||||
});
|
||||
@@ -116,6 +132,59 @@ Singleton {
|
||||
});
|
||||
}
|
||||
|
||||
function fixCityName(cityName: string): string {
|
||||
if (!cityName)
|
||||
return "";
|
||||
const mapping = {
|
||||
// Polish
|
||||
"Poznan": "Poznań",
|
||||
"Wroclaw": "Wrocław",
|
||||
"Krakow": "Kraków",
|
||||
"Gdansk": "Gdańsk",
|
||||
"Lodz": "Łódź",
|
||||
"Rzeszow": "Rzeszów",
|
||||
"Torun": "Toruń",
|
||||
"Bialystok": "Białystok",
|
||||
"Czestochowa": "Częstochowa",
|
||||
"Plock": "Płock",
|
||||
"Ruda Slaska": "Ruda Śląska",
|
||||
"Dabrowa Gornicza": "Dąbrowa Górnicza",
|
||||
"Elblag": "Elbląg",
|
||||
"Gorzow Wielkopolski": "Gorzów Wielkopolski",
|
||||
"Zielona Gora": "Zielona Góra",
|
||||
"Slupsk": "Słupsk",
|
||||
|
||||
// German
|
||||
"Munchen": "München",
|
||||
"Koln": "Köln",
|
||||
"Dusseldorf": "Düsseldorf",
|
||||
"Nurnberg": "Nürnberg",
|
||||
|
||||
// French & Spanish & Portuguese
|
||||
"Sao Paulo": "São Paulo",
|
||||
"Montreal": "Montréal",
|
||||
"Quebec": "Québec",
|
||||
"Bogota": "Bogotá",
|
||||
"Medellin": "Medellín",
|
||||
"Cordoba": "Córdoba",
|
||||
|
||||
// Turkish
|
||||
"Istanbul": "İstanbul",
|
||||
"Izmir": "İzmir",
|
||||
|
||||
// Scandinavian & others
|
||||
"Malmo": "Malmö",
|
||||
"Goteborg": "Göteborg",
|
||||
"Zurich": "Zürich",
|
||||
"Geneve": "Genève"
|
||||
};
|
||||
return mapping[cityName] || cityName;
|
||||
}
|
||||
|
||||
function formatTemp(temp: var): string {
|
||||
return Config.services.useFahrenheit ? `${temp !== undefined ? Math.round(toFahrenheit(temp)) : "--"}°F` : `${temp !== undefined ? Math.round(temp) : "--"}°C`;
|
||||
}
|
||||
|
||||
function getWeatherCondition(code: string): string {
|
||||
const conditions = {
|
||||
"0": "Clear",
|
||||
@@ -154,9 +223,9 @@ Singleton {
|
||||
if (!loc || loc.indexOf(",") === -1)
|
||||
return "";
|
||||
|
||||
const [lat, lon] = loc.split(",");
|
||||
const [lat, lon] = loc.split(",").map(s => s.trim());
|
||||
const baseUrl = "https://api.open-meteo.com/v1/forecast";
|
||||
const params = ["latitude=" + lat, "longitude=" + lon, "hourly=weather_code,temperature_2m", "daily=weather_code,temperature_2m_max,temperature_2m_min,sunrise,sunset", "current=temperature_2m,relative_humidity_2m,apparent_temperature,is_day,weather_code,wind_speed_10m", "timezone=auto", "forecast_days=7"];
|
||||
const params = ["latitude=" + lat, "longitude=" + lon, "hourly=weather_code,temperature_2m,precipitation_probability", "daily=weather_code,temperature_2m_max,temperature_2m_min,sunrise,sunset", "current=temperature_2m,relative_humidity_2m,apparent_temperature,is_day,weather_code,wind_speed_10m", "timezone=auto", "forecast_days=7"];
|
||||
|
||||
return baseUrl + "?" + params.join("&");
|
||||
}
|
||||
@@ -189,7 +258,14 @@ Singleton {
|
||||
|
||||
onLocChanged: fetchWeatherData()
|
||||
|
||||
// Refresh current location hourly
|
||||
Connections {
|
||||
function onWeatherLocationChanged(): void {
|
||||
root.reload();
|
||||
}
|
||||
|
||||
target: Config.services
|
||||
}
|
||||
|
||||
Timer {
|
||||
interval: 3600000 // 1 hour
|
||||
repeat: true
|
||||
@@ -200,6 +276,5 @@ Singleton {
|
||||
|
||||
ElapsedTimer {
|
||||
id: timer
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user