notification changes
This commit is contained in:
@@ -0,0 +1,187 @@
|
||||
pragma Singleton
|
||||
|
||||
import qs.Config
|
||||
import Quickshell
|
||||
import Quickshell.Services.Notifications
|
||||
import QtQuick
|
||||
|
||||
Singleton {
|
||||
id: root
|
||||
|
||||
readonly property var weatherIcons: ({
|
||||
"0": "clear_day",
|
||||
"1": "clear_day",
|
||||
"2": "partly_cloudy_day",
|
||||
"3": "cloud",
|
||||
"45": "foggy",
|
||||
"48": "foggy",
|
||||
"51": "rainy",
|
||||
"53": "rainy",
|
||||
"55": "rainy",
|
||||
"56": "rainy",
|
||||
"57": "rainy",
|
||||
"61": "rainy",
|
||||
"63": "rainy",
|
||||
"65": "rainy",
|
||||
"66": "rainy",
|
||||
"67": "rainy",
|
||||
"71": "cloudy_snowing",
|
||||
"73": "cloudy_snowing",
|
||||
"75": "snowing_heavy",
|
||||
"77": "cloudy_snowing",
|
||||
"80": "rainy",
|
||||
"81": "rainy",
|
||||
"82": "rainy",
|
||||
"85": "cloudy_snowing",
|
||||
"86": "snowing_heavy",
|
||||
"95": "thunderstorm",
|
||||
"96": "thunderstorm",
|
||||
"99": "thunderstorm"
|
||||
})
|
||||
|
||||
readonly property var categoryIcons: ({
|
||||
WebBrowser: "web",
|
||||
Printing: "print",
|
||||
Security: "security",
|
||||
Network: "chat",
|
||||
Archiving: "archive",
|
||||
Compression: "archive",
|
||||
Development: "code",
|
||||
IDE: "code",
|
||||
TextEditor: "edit_note",
|
||||
Audio: "music_note",
|
||||
Music: "music_note",
|
||||
Player: "music_note",
|
||||
Recorder: "mic",
|
||||
Game: "sports_esports",
|
||||
FileTools: "files",
|
||||
FileManager: "files",
|
||||
Filesystem: "files",
|
||||
FileTransfer: "files",
|
||||
Settings: "settings",
|
||||
DesktopSettings: "settings",
|
||||
HardwareSettings: "settings",
|
||||
TerminalEmulator: "terminal",
|
||||
ConsoleOnly: "terminal",
|
||||
Utility: "build",
|
||||
Monitor: "monitor_heart",
|
||||
Midi: "graphic_eq",
|
||||
Mixer: "graphic_eq",
|
||||
AudioVideoEditing: "video_settings",
|
||||
AudioVideo: "music_video",
|
||||
Video: "videocam",
|
||||
Building: "construction",
|
||||
Graphics: "photo_library",
|
||||
"2DGraphics": "photo_library",
|
||||
RasterGraphics: "photo_library",
|
||||
TV: "tv",
|
||||
System: "host",
|
||||
Office: "content_paste"
|
||||
})
|
||||
|
||||
function getAppIcon(name: string, fallback: string): string {
|
||||
const icon = DesktopEntries.heuristicLookup(name)?.icon;
|
||||
if (fallback !== "undefined")
|
||||
return Quickshell.iconPath(icon, fallback);
|
||||
return Quickshell.iconPath(icon);
|
||||
}
|
||||
|
||||
function getAppCategoryIcon(name: string, fallback: string): string {
|
||||
const categories = DesktopEntries.heuristicLookup(name)?.categories;
|
||||
|
||||
if (categories)
|
||||
for (const [key, value] of Object.entries(categoryIcons))
|
||||
if (categories.includes(key))
|
||||
return value;
|
||||
return fallback;
|
||||
}
|
||||
|
||||
function getNetworkIcon(strength: int, isSecure = false): string {
|
||||
if (isSecure) {
|
||||
if (strength >= 80)
|
||||
return "network_wifi_locked";
|
||||
if (strength >= 60)
|
||||
return "network_wifi_3_bar_locked";
|
||||
if (strength >= 40)
|
||||
return "network_wifi_2_bar_locked";
|
||||
if (strength >= 20)
|
||||
return "network_wifi_1_bar_locked";
|
||||
return "signal_wifi_0_bar";
|
||||
} else {
|
||||
if (strength >= 80)
|
||||
return "network_wifi";
|
||||
if (strength >= 60)
|
||||
return "network_wifi_3_bar";
|
||||
if (strength >= 40)
|
||||
return "network_wifi_2_bar";
|
||||
if (strength >= 20)
|
||||
return "network_wifi_1_bar";
|
||||
return "signal_wifi_0_bar";
|
||||
}
|
||||
}
|
||||
|
||||
function getBluetoothIcon(icon: string): string {
|
||||
if (icon.includes("headset") || icon.includes("headphones"))
|
||||
return "headphones";
|
||||
if (icon.includes("audio"))
|
||||
return "speaker";
|
||||
if (icon.includes("phone"))
|
||||
return "smartphone";
|
||||
if (icon.includes("mouse"))
|
||||
return "mouse";
|
||||
if (icon.includes("keyboard"))
|
||||
return "keyboard";
|
||||
return "bluetooth";
|
||||
}
|
||||
|
||||
function getWeatherIcon(code: string): string {
|
||||
if (weatherIcons.hasOwnProperty(code))
|
||||
return weatherIcons[code];
|
||||
return "air";
|
||||
}
|
||||
|
||||
function getNotifIcon(summary: string, urgency: int): string {
|
||||
summary = summary.toLowerCase();
|
||||
if (summary.includes("reboot"))
|
||||
return "restart_alt";
|
||||
if (summary.includes("recording"))
|
||||
return "screen_record";
|
||||
if (summary.includes("battery"))
|
||||
return "power";
|
||||
if (summary.includes("screenshot"))
|
||||
return "screenshot_monitor";
|
||||
if (summary.includes("welcome"))
|
||||
return "waving_hand";
|
||||
if (summary.includes("time") || summary.includes("a break"))
|
||||
return "schedule";
|
||||
if (summary.includes("installed"))
|
||||
return "download";
|
||||
if (summary.includes("update"))
|
||||
return "update";
|
||||
if (summary.includes("unable to"))
|
||||
return "deployed_code_alert";
|
||||
if (summary.includes("profile"))
|
||||
return "person";
|
||||
if (summary.includes("file"))
|
||||
return "folder_copy";
|
||||
if (urgency === NotificationUrgency.Critical)
|
||||
return "release_alert";
|
||||
return "chat";
|
||||
}
|
||||
|
||||
function getVolumeIcon(volume: real, isMuted: bool): string {
|
||||
if (isMuted)
|
||||
return "no_sound";
|
||||
if (volume >= 0.5)
|
||||
return "volume_up";
|
||||
if (volume > 0)
|
||||
return "volume_down";
|
||||
return "volume_mute";
|
||||
}
|
||||
|
||||
function getMicVolumeIcon(volume: real, isMuted: bool): string {
|
||||
if (!isMuted && volume > 0)
|
||||
return "mic";
|
||||
return "mic_off";
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,56 @@
|
||||
pragma Singleton
|
||||
|
||||
import Quickshell
|
||||
import Quickshell.Io
|
||||
import Quickshell.Wayland
|
||||
|
||||
Singleton {
|
||||
id: root
|
||||
|
||||
property alias enabled: props.enabled
|
||||
readonly property alias enabledSince: props.enabledSince
|
||||
|
||||
onEnabledChanged: {
|
||||
if (enabled)
|
||||
props.enabledSince = new Date();
|
||||
}
|
||||
|
||||
PersistentProperties {
|
||||
id: props
|
||||
|
||||
property bool enabled
|
||||
property date enabledSince
|
||||
|
||||
reloadableId: "idleInhibitor"
|
||||
}
|
||||
|
||||
IdleInhibitor {
|
||||
enabled: props.enabled
|
||||
window: PanelWindow {
|
||||
implicitWidth: 0
|
||||
implicitHeight: 0
|
||||
color: "transparent"
|
||||
mask: Region {}
|
||||
}
|
||||
}
|
||||
|
||||
IpcHandler {
|
||||
target: "idleInhibitor"
|
||||
|
||||
function isEnabled(): bool {
|
||||
return props.enabled;
|
||||
}
|
||||
|
||||
function toggle(): void {
|
||||
props.enabled = !props.enabled;
|
||||
}
|
||||
|
||||
function enable(): void {
|
||||
props.enabled = true;
|
||||
}
|
||||
|
||||
function disable(): void {
|
||||
props.enabled = false;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -9,6 +9,12 @@ Singleton {
|
||||
readonly property int minutes: clock.minutes
|
||||
readonly property int seconds: clock.seconds
|
||||
|
||||
readonly property string timeStr: format("hh:mm")
|
||||
readonly property list<string> timeComponents: timeStr.split(":")
|
||||
readonly property string hourStr: timeComponents[0] ?? ""
|
||||
readonly property string minuteStr: timeComponents[1] ?? ""
|
||||
readonly property string amPmStr: timeComponents[2] ?? ""
|
||||
|
||||
function format(fmt: string): string {
|
||||
return Qt.formatDateTime(clock.date, fmt);
|
||||
}
|
||||
|
||||
@@ -0,0 +1,16 @@
|
||||
pragma Singleton
|
||||
|
||||
import Quickshell
|
||||
|
||||
Singleton {
|
||||
property var screens: new Map()
|
||||
property var bars: new Map()
|
||||
|
||||
function load(screen: ShellScreen, visibilities: var): void {
|
||||
screens.set(Hypr.monitorFor(screen), visibilities);
|
||||
}
|
||||
|
||||
function getForActive(): PersistentProperties {
|
||||
return screens.get(Hypr.focusedMonitor);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,204 @@
|
||||
pragma Singleton
|
||||
|
||||
import qs.Config
|
||||
import Quickshell
|
||||
import QtQuick
|
||||
|
||||
Singleton {
|
||||
id: root
|
||||
|
||||
property string city
|
||||
property string loc
|
||||
property var cc
|
||||
property list<var> forecast
|
||||
property list<var> hourlyForecast
|
||||
|
||||
readonly property string icon: cc ? Icons.getWeatherIcon(cc.weatherCode) : "cloud_alert"
|
||||
readonly property string description: cc?.weatherDesc ?? qsTr("No weather")
|
||||
readonly property string temp: `${cc?.tempC ?? 0}°C`
|
||||
readonly property string feelsLike: `${cc?.feelsLikeC ?? 0}°C`
|
||||
readonly property int humidity: cc?.humidity ?? 0
|
||||
readonly property real windSpeed: cc?.windSpeed ?? 0
|
||||
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 var cachedCities: new Map()
|
||||
|
||||
function reload(): void {
|
||||
const configLocation = Config.services.weatherLocation;
|
||||
|
||||
if (configLocation) {
|
||||
if (configLocation.indexOf(",") !== -1 && !isNaN(parseFloat(configLocation.split(",")[0]))) {
|
||||
loc = configLocation;
|
||||
fetchCityFromCoords(configLocation);
|
||||
} else {
|
||||
fetchCoordsFromCity(configLocation);
|
||||
}
|
||||
} else if (!loc || timer.elapsed() > 900) {
|
||||
Requests.get("https://ipinfo.io/json", text => {
|
||||
const response = JSON.parse(text);
|
||||
if (response.loc) {
|
||||
loc = response.loc;
|
||||
city = response.city ?? "";
|
||||
timer.restart();
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
function fetchCityFromCoords(coords: string): void {
|
||||
if (cachedCities.has(coords)) {
|
||||
city = cachedCities.get(coords);
|
||||
return;
|
||||
}
|
||||
|
||||
const [lat, lon] = coords.split(",");
|
||||
const url = `https://nominatim.openstreetmap.org/reverse?lat=${lat}&lon=${lon}&format=geocodejson`;
|
||||
Requests.get(url, 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";
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
function fetchCoordsFromCity(cityName: string): void {
|
||||
const url = `https://geocoding-api.open-meteo.com/v1/search?name=${encodeURIComponent(cityName)}&count=1&language=en&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;
|
||||
} else {
|
||||
loc = "";
|
||||
reload();
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
function fetchWeatherData(): void {
|
||||
const url = getWeatherUrl();
|
||||
if (url === "")
|
||||
return;
|
||||
|
||||
Requests.get(url, text => {
|
||||
const json = JSON.parse(text);
|
||||
if (!json.current || !json.daily)
|
||||
return;
|
||||
|
||||
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)),
|
||||
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]
|
||||
};
|
||||
|
||||
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])),
|
||||
weatherCode: json.daily.weather_code[i],
|
||||
icon: Icons.getWeatherIcon(json.daily.weather_code[i])
|
||||
});
|
||||
forecast = forecastList;
|
||||
|
||||
const hourlyList = [];
|
||||
const now = new Date();
|
||||
for (let i = 0; i < json.hourly.time.length; i++) {
|
||||
const time = new Date(json.hourly.time[i]);
|
||||
if (time < now)
|
||||
continue;
|
||||
|
||||
hourlyList.push({
|
||||
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])),
|
||||
weatherCode: json.hourly.weather_code[i],
|
||||
icon: Icons.getWeatherIcon(json.hourly.weather_code[i])
|
||||
});
|
||||
}
|
||||
hourlyForecast = hourlyList;
|
||||
});
|
||||
}
|
||||
|
||||
function toFahrenheit(celcius: real): real {
|
||||
return celcius * 9 / 5 + 32;
|
||||
}
|
||||
|
||||
function getWeatherUrl(): string {
|
||||
if (!loc || loc.indexOf(",") === -1)
|
||||
return "";
|
||||
|
||||
const [lat, lon] = loc.split(",");
|
||||
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"];
|
||||
|
||||
return baseUrl + "?" + params.join("&");
|
||||
}
|
||||
|
||||
function getWeatherCondition(code: string): string {
|
||||
const conditions = {
|
||||
"0": "Clear",
|
||||
"1": "Clear",
|
||||
"2": "Partly cloudy",
|
||||
"3": "Overcast",
|
||||
"45": "Fog",
|
||||
"48": "Fog",
|
||||
"51": "Drizzle",
|
||||
"53": "Drizzle",
|
||||
"55": "Drizzle",
|
||||
"56": "Freezing drizzle",
|
||||
"57": "Freezing drizzle",
|
||||
"61": "Light rain",
|
||||
"63": "Rain",
|
||||
"65": "Heavy rain",
|
||||
"66": "Light rain",
|
||||
"67": "Heavy rain",
|
||||
"71": "Light snow",
|
||||
"73": "Snow",
|
||||
"75": "Heavy snow",
|
||||
"77": "Snow",
|
||||
"80": "Light rain",
|
||||
"81": "Rain",
|
||||
"82": "Heavy rain",
|
||||
"85": "Light snow showers",
|
||||
"86": "Heavy snow showers",
|
||||
"95": "Thunderstorm",
|
||||
"96": "Thunderstorm with hail",
|
||||
"99": "Thunderstorm with hail"
|
||||
};
|
||||
return conditions[code] || "Unknown";
|
||||
}
|
||||
|
||||
onLocChanged: fetchWeatherData()
|
||||
|
||||
// Refresh current location hourly
|
||||
Timer {
|
||||
interval: 3600000 // 1 hour
|
||||
running: true
|
||||
repeat: true
|
||||
onTriggered: fetchWeatherData()
|
||||
}
|
||||
|
||||
ElapsedTimer {
|
||||
id: timer
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user