Files
dotfiles/ags/weather/weather_logic.js
T
2024-09-03 12:22:09 +07:00

486 lines
17 KiB
JavaScript

import Weather from '../../services/weather.js';
import Widget from 'resource:///com/github/Aylur/ags/widget.js';
import App from 'resource:///com/github/Aylur/ags/app.js';
export const TemperatureIndicator = () => Widget.Stack({
children: {
true: PannelButton(),
false: Widget.Label(),
},
setup: self => self.hook(Weather, () => {
self.shown = Weather.temperatureWeather ? true : false;
}),
});
export const PannelButton = props => Widget.Button({
...props,
class_name: 'weather panel-button',
child: Temperature(),
on_clicked: () => App.toggleWindow('weather'),
on_secondary_click: () => Weather.weatherData,
setup: self => self
.hook(App, (_, win, visible) => {
self.toggleClassName('active', win === 'weather' && visible);
}),
});
export const Temperature = props => Widget.Label({
...props,
setup: self => self.hook(Weather, () => {
if (Weather.temperatureWeather && self.label !== Weather.temperatureWeather) {
self.label = Weather.temperatureWeather.toString()
}
}),
});
function weatherBackgroundStyle(icon, box) {
switch (icon) {
case "🌇": {//sunset
box.setCss(`
background: linear-gradient(to bottom, #ff6f61, #ffca58, #f0e68c);
color: #000000;
`);
break;
}
case "🌄": {//sunrise
box.setCss(`
background: linear-gradient(to bottom, #ffcc00, #ff6f61, #ff5e62, #d55f74);
color: #000000;
`);
break;
}
case "🌤": {//few clouds
box.setCss(`
background: linear-gradient(to bottom, #80b3ff, #ffffff, #ffdb4d);
color: #000000;
`);
break;
}
case "🌩": {//thunderstorm
box.setCss(`
background: linear-gradient(to bottom, #0c0e23, #1a1c38, #121320, #0c0e23);
color: #ffffff;
`);
break;
}
case "🌑": {
box.setCss(`
background: linear-gradient(to bottom, #2c3e50, #1a2533);
color: #ffffff;
`);
break;
}
case "🌕": {
box.setCss(`
background: linear-gradient(to bottom, #001f3f, #002f4f, #003f5f, #004f6f, #005f7f);
color: #ffffff;
`);
break;
}
case "☀️": {
box.setCss(`
background: linear-gradient(to bottom, #ffeb99, #ffe580, #ffd866, #ffcf4c, #ffc333);
color: #000000;
`);
break;
}
case "☁":
case "☁️": {//cloudy
box.setCss(`
background: linear-gradient(to bottom, #c4c4c4, #d1d1d1, #dedede, #ebebeb, #f8f8f8);
color: #000000;
`);
break;
}
case "": {
box.setCss(`
background: linear-gradient(to bottom, #1c2331, #212a38, #263141, #2b3749, #303d51);
color: #ffffff;
`);
break;
}
case "": {//fog
box.setCss(`
background: linear-gradient(to bottom, #d8d8d8, #e2e2e2, #ececec, #f6f6f6, #ffffff);
color: #000000;
`);
break;
}
case "": {
box.setCss(`
background: linear-gradient(to bottom, #1c2331, #1c2331, #1c2331, #293547, #38475f);
color: #ffffff;
`);
break;
}
case "⛈️": {//heavy rain
box.setCss(`
background: linear-gradient(to bottom, #2c3e50, #34495e, #2c3e50, #34495e, #2c3e50);
color: #000000;
`);
break;
}
case "󰙾": {
box.setCss(`
background: linear-gradient(to bottom, #050818, #070b1d, #0a0e22, #0d1126, #10152b);
color: #ffffff;
`);
break;
}
case "🌦️": {//light rain
box.setCss(`
background: linear-gradient(to bottom, #547aad, #6692b8, #78a9c3, #8abfd0, #9cd7dd);
color: #000000;
`);
break;
}
case "": {
box.setCss(`
background: linear-gradient(to bottom, #0e1620, #121c2a, #162133, #18273c, #1c2c46);
color: #ffffff;
`);
break;
}
case "⛅": {//partly cloudy
box.setCss(`
background: linear-gradient(to bottom, #a8c9f0, #c4dfea, #f0f0cc, #ffd700, #f0f0cc, #c4dfea, #a8c9f0);
color: #000000;
`);
break;
}
case "": {
box.setCss(`
background: linear-gradient(to bottom, #040d1c, #081427, #0c1a32, #101f3d, #142348);
color: #ffffff;
`);
break;
}
case "🌧️": {//rain showers
box.setCss(`
background: linear-gradient(to bottom, #5e7d99, #6a8ba6, #7698b3, #87a5bf, #97b3cb, #a6c0d8, #b4cedf);
color: #000000;
`);
break;
}
case "": {
box.setCss(`
background: linear-gradient(to bottom, #050818, #070b1d, #0a0e22, #0d1126, #10152b);
color: #ffffff;
`);
break;
}
case "🌨": {//snow
box.setCss(`
background: linear-gradient(to bottom, #d0e6ec, #e0f0f5, #f0f5f9, #f5fafd, #ffffff);
color: #000000;
`);
break;
}
case "": {
box.setCss(`
background: linear-gradient(to bottom, #0e1620, #162133, #1c263f, #232c4c, #293259);
color: #ffffff;
`);
break;
}
case "🌨️": {//sleet
box.setCss(`
background: linear-gradient(to bottom, #d0e6ec, #e0f0f5, #f0f5f9, #ffffff, #f0f5f9, #e0f0f5, #d0e6ec);
color: #000000;
`);
break;
}
case "": {
box.setCss(`
background: linear-gradient(to bottom, #050818, #08142a, #0c1d3b, #101f47, #142556);
color: #ffffff;
`);
break;
}
default: {
box.setCss(`
background: red;
color: blue;
`);
break;
}
}
}
export const WeatherInfo = (weatherData) => Widget.Box({
class_name: 'weather-info',
vertical: true,
children: [
Widget.Label({ label: weatherData.date.substring(0, 3).toUpperCase(), }),
Widget.Label({ label: weatherData.hour + 'h', }),
Widget.Label({ label: weatherData.icon, class_name: 'weather-icon', }),
Widget.Label({ label: weatherData.temperature, class_name: 'weather-temperature' }),
Widget.Box({ vexpand: true }),
weatherData.rain !== '0 mm' ? Widget.Label({ label: weatherData.rain, }) : null,
// Widget.Label({ label: " " + weatherData.wind, }),
// Widget.Label({ label: weatherData.humidity, }),
Widget.Label({ label: '↑ ' + weatherData.maxTemp, class_name: 'weather-max', }),
Widget.Label({ label: '↓ ' + weatherData.minTemp, class_name: 'weather-min', }),
],
setup: self => self.hook(Weather, () => {
weatherBackgroundStyle(weatherData.icon, self)
}),
});
function getMostCommon(arr){
return arr.sort((a,b) =>
arr.filter(v => v===a).length
- arr.filter(v => v===b).length
).pop();
}
export const WeatherBoxChild = (w) => Widget.Box({
class_name: 'qs-weather-box-child',
vertical: true,
hexpand: true,
children: [
Widget.Label({ label: w.hour + 'h', class_name: 'weather-hour', }),
Widget.Label({ label: w.icon, class_name: 'weather-icon', }),
Widget.Label({ label: w.temperature, class_name: 'weather-temperature' }),
Widget.Box({ vexpand: true }),
w.rain !== '0 mm' ? Widget.Label({ label: w.rain, class_name: 'weather-rain', }) : null,
Widget.Label({ label: " " + Math.round(w.wind.replace(/kph$/, '')) + ' kph', class_name: 'weather-wind', }),
],
});
export const WeatherBoxChildWrapper = (w, temperatureDataPerDay, totalWeatherForecastDataArray) => Widget.Box({
class_name: 'qs-weather-box-child-wrapper',
hexpand: true,
setup: self => self.hook(Weather, () => {
let useTotalInstead = false;
if (totalWeatherForecastDataArray.length) {
useTotalInstead = true;
totalWeatherForecastDataArray.forEach(totalEl => {
self.add(
WeatherBoxChild(totalEl),
)
})
}
if (false === useTotalInstead && w && temperatureDataPerDay && temperatureDataPerDay[w.date.substring(0, 3).toUpperCase()].data.length) {
temperatureDataPerDay[w.date.substring(0, 3).toUpperCase()].data.forEach(el => {
self.add(
WeatherBoxChild(el),
)
})
}
}),
});
export const WeatherMainWidget = (widgetIcon, widgetDate, rain, temperatureDataPerDay, w, totalWeatherForecastDataArray = []) => Widget.Box({
class_name: 'qsweather-widget',
vertical: true,
hexpand: true,
children: [
Widget.Box({
class_name: 'qs-weather-box-main',
children: [
Widget.Label({ label: widgetIcon, class_name: 'weather-icon', }),
rain != 0 ? Widget.Label({ label: rain + 'mm', class_name: 'weather-rain', }): null,
Widget.Box({ hexpand: true }),
Widget.Label({ label: '↑ ' + temperatureDataPerDay[widgetDate.substring(0, 3).toUpperCase()].maxTemp + ' ', class_name: 'weather-max', }),
Widget.Label({ label: '↓ ' + temperatureDataPerDay[widgetDate.substring(0, 3).toUpperCase()].minTemp + ' ', class_name: 'weather-min', }),
Widget.Label({ label: widgetDate.substring(0, 3).toUpperCase(), class_name: 'weather-hour', }),
]
}),
WeatherBoxChildWrapper(w, temperatureDataPerDay, totalWeatherForecastDataArray)
],
setup: self => self.hook(Weather, () => {
weatherBackgroundStyle(widgetIcon, self);
}),
});
export const Tooltip = (total = null) => Widget.Box({
setup: self => self.hook(Weather, () => {
let tooltip = Weather.tooltip;
if (tooltip) {
self.get_children().forEach(ch => ch.destroy());
let prevDayName = null;
let temperatureDataPerDay = {};
let weatherStatusIconArray = [];
let weatherForecastDataArray = [];
let totalWeatherForecastDataArray = [];
let totalWeatherForecastsCounter = total;
let forecastWidgetsNumber = 0;
tooltip.forEach(w => {
if (w.date !== prevDayName) {
weatherStatusIconArray = [];
prevDayName = w.date;
forecastWidgetsNumber = 0;
weatherForecastDataArray = [];
}
// used to retrieve min/max temp per day
const date = w.date.substring(0, 3).toUpperCase();
const temperature = parseInt(w.temperature);
const rain = w.rain.replace(/ mm$/, '');
forecastWidgetsNumber = forecastWidgetsNumber + 1;
weatherForecastDataArray.push(w);
if (total && totalWeatherForecastsCounter && totalWeatherForecastsCounter > 0) {
totalWeatherForecastDataArray.push(w);
totalWeatherForecastsCounter = totalWeatherForecastsCounter - 1;
}
// If the date is not already in the object, initialize it
if (!temperatureDataPerDay[date]) {
temperatureDataPerDay[date] = {
minTemp: temperature,
maxTemp: temperature,
rain: rain,
icons: [],
widgetsNumber: forecastWidgetsNumber,
data: weatherForecastDataArray,
};
} else {
// Update min and max temperatures if necessary
temperatureDataPerDay[date].minTemp = Math.min(temperatureDataPerDay[date].minTemp, temperature);
temperatureDataPerDay[date].maxTemp = Math.max(temperatureDataPerDay[date].maxTemp, temperature);
temperatureDataPerDay[date].rain = Math.max(temperatureDataPerDay[date].rain, rain);
temperatureDataPerDay[date].icons = weatherStatusIconArray;
temperatureDataPerDay[date].widgetsNumber = forecastWidgetsNumber;
temperatureDataPerDay[date].data = weatherForecastDataArray;
}
// add icon to the array in between somewhat sunny hours, used to later get most common icon for main widget days
if (w.hour >= 7 && w.hour <= 19) {
weatherStatusIconArray.push(w.icon);
}
});
// clear for next loop
prevDayName = null;
let widgetIcon = null;
let widgetDate = null;
let w = null;
for (let i = 0; i < tooltip.length; i++) {
w = tooltip[i];
// console.log('loop ' + w.date + ' h ' + w.hour + ' i ' + w.icon )
// used to limit forecast to specified amount (if total variable is provided, it will display that amount of forecast widgets on a main one)
if (total && total >= 0) {
if (!widgetIcon) {
widgetIcon = w.icon;
}
if (!widgetDate) {
widgetDate = w.date;
}
const rain = temperatureDataPerDay[widgetDate.substring(0, 3).toUpperCase()].rain;
self.add(
WeatherMainWidget(widgetIcon, widgetDate, rain, temperatureDataPerDay, w, totalWeatherForecastDataArray)
);
break;
}
// if provided date differs to previous day name, by default prevDayName is null
if (w.date !== prevDayName && i > 0) {
// adds spacing to the widgets
self.add(
Widget.Box({
children: [
Widget.Label({ label: ' ', class_name: 'weather-spacing' }),
],
}),
);
}
// logic that determines if the widget contains just 1 forecast widget, if so it will display it in a smaller widget - like the one for the forecast days
if (temperatureDataPerDay[w.date.substring(0, 3).toUpperCase()].widgetsNumber === 1) {
prevDayName = w.date;
self.add(WeatherInfo(w));
continue;
}
// this one creates main one - wide weather widget that contains smaller forecast widgets
if (w.date !== prevDayName) {
const rain = temperatureDataPerDay[w.date.substring(0, 3).toUpperCase()].rain;
let icon = getMostCommon(temperatureDataPerDay[w.date.substring(0, 3).toUpperCase()].icons);
widgetDate = w.date;
if (!icon) {
icon = w.icon;
}
widgetIcon = icon;
self.add(
WeatherMainWidget(widgetIcon, widgetDate, rain, temperatureDataPerDay, w)
);
}
prevDayName = w.date;
};
}
}),
});
export const ResetTimer = props => Widget.Label({
...props,
setup: self => {
self.poll(600000, (self) => {
Weather.weatherData
});
},
});
export const Forecast = () => Widget.Box({
class_name: 'datemenu',
vertical: true,
children: [
Widget.Box({
class_name: 'tooltip',
children: [
ResetTimer(),
Tooltip(),
]
})
]
});
export const PopupContent = () => Widget.Box({
class_name: 'weather',
vexpand: false,
children: [
Widget.Box({
vertical: true,
children: [
Forecast(),
// RefreshButton({ class_name: 'header panel-button', }),
]
})
]
});
export const RefreshButton = props => Widget.Button({
...props,
class_name: 'weather-refresh',
child: Widget.Icon({
icon: 'view-refresh-symbolic',
hpack: 'end',
}),
on_clicked: () => {
Weather.weatherData
},
});