Finish all options sets

This commit is contained in:
Jas Singh
2024-07-20 22:59:25 -07:00
parent c6c4e0cf57
commit ab721edbd2
24 changed files with 514 additions and 392 deletions

View File

@@ -0,0 +1,28 @@
import icons from "../../../../../icons/index.js";
export const HourlyIcon = (theWeather, getNextEpoch) => {
return Widget.Icon({
class_name: "hourly-weather-icon",
icon: theWeather.bind("value").as((w) => {
if (!Object.keys(w).length) {
return "-";
}
const nextEpoch = getNextEpoch(w);
const weatherAtEpoch = w.forecast.forecastday[0].hour.find(
(h) => h.time_epoch === nextEpoch,
);
let iconQuery = weatherAtEpoch?.condition.text
.trim()
.toLowerCase()
.replaceAll(" ", "_");
if (!weatherAtEpoch?.isDay && iconQuery === "partly_cloudy") {
iconQuery = "partly_cloudy_night";
}
return icons.weather[iconQuery];
}),
});
};

View File

@@ -0,0 +1,44 @@
import { HourlyIcon } from "./icon/index.js";
import { HourlyTemp } from "./temperature/index.js";
import { HourlyTime } from "./time/index.js";
export const Hourly = (theWeather) => {
return Widget.Box({
vertical: false,
hexpand: true,
hpack: "fill",
class_name: "hourly-weather-container",
children: [1, 2, 3, 4].map((hoursFromNow) => {
const getNextEpoch = (wthr) => {
const currentEpoch = wthr.location.localtime_epoch;
const epochAtHourStart = currentEpoch - (currentEpoch % 3600);
let nextEpoch = 3600 * hoursFromNow + epochAtHourStart;
const curHour = new Date(currentEpoch * 1000).getHours();
/*
* NOTE: Since the API is only capable of showing the current day; if
* the hours left in the day are less than 4 (aka spilling into the next day),
* then rewind to contain the prediction within the current day.
*/
if (curHour > 19) {
const hoursToRewind = curHour - 19;
nextEpoch =
3600 * hoursFromNow + epochAtHourStart - hoursToRewind * 3600;
}
return nextEpoch;
};
return Widget.Box({
class_name: "hourly-weather-item",
hexpand: true,
vertical: true,
children: [
HourlyTime(theWeather, getNextEpoch),
HourlyIcon(theWeather, getNextEpoch),
HourlyTemp(theWeather, getNextEpoch),
],
});
}),
});
};

View File

@@ -0,0 +1,27 @@
import options from "options";
const { unit } = options.menus.clock.weather;
export const HourlyTemp = (theWeather, getNextEpoch) => {
return Widget.Label({
class_name: "hourly-weather-temp",
label: Utils.merge(
[theWeather.bind("value"), unit.bind("value")],
(wthr, unt) => {
if (!Object.keys(wthr).length) {
return "-";
}
const nextEpoch = getNextEpoch(wthr);
const weatherAtEpoch = wthr.forecast.forecastday[0].hour.find(
(h) => h.time_epoch === nextEpoch,
);
if (unt === "imperial") {
return `${weatherAtEpoch ? Math.ceil(weatherAtEpoch.temp_f) : "-"}° F`;
}
return `${weatherAtEpoch ? Math.ceil(weatherAtEpoch.temp_c) : "-"}° C`;
},
),
});
};

View File

@@ -0,0 +1,18 @@
export const HourlyTime = (theWeather, getNextEpoch) => {
return Widget.Label({
class_name: "hourly-weather-time",
label: theWeather.bind("value").as((w) => {
if (!Object.keys(w).length) {
return "-";
}
const nextEpoch = getNextEpoch(w);
const dateAtEpoch = new Date(nextEpoch * 1000);
let hours = dateAtEpoch.getHours();
const ampm = hours >= 12 ? "PM" : "AM";
hours = hours % 12 || 12;
return `${hours}${ampm}`;
}),
});
};

View File

@@ -0,0 +1,25 @@
import icons from "../../../../icons/index.js";
export const TodayIcon = (theWeather) => {
return Widget.Box({
vpack: "center",
hpack: "start",
class_name: "calendar-menu-weather today icon container",
children: [
Widget.Icon({
class_name: "calendar-menu-weather today icon",
icon: theWeather.bind("value").as((v) => {
let iconQuery = v.current.condition.text
.trim()
.toLowerCase()
.replaceAll(" ", "_");
if (!v.current.isDay && iconQuery === "partly_cloudy") {
iconQuery = "partly_cloudy_night";
}
return icons.weather[iconQuery];
}),
}),
],
});
};

View File

@@ -1,5 +1,10 @@
import icons from "../../../icons/index.js";
import { keyRing } from "../../../../../../Documents/Keys/keyList.js";
import options from "options";
import { TodayIcon } from "./icon/index.js";
import { TodayStats } from "./stats/index.js";
import { TodayTemperature } from "./temperature/index.js";
import { Hourly } from "./hourly/index.js";
const { key, interval } = options.menus.clock.weather;
const defaultWeather = {
location: {
@@ -34,53 +39,31 @@ const defaultWeather = {
const theWeather = Variable(defaultWeather);
const getIcon = (fahren) => {
const icons = {
100: "",
75: "",
50: "",
25: "",
0: "",
};
const colors = {
100: "weather-color red",
75: "weather-color orange",
50: "weather-color lavender",
25: "weather-color blue",
0: "weather-color sky",
};
const threshold =
fahren < 0
? 0
: [100, 75, 50, 25, 0].find((threshold) => threshold <= fahren);
return {
icon: icons[threshold],
color: colors[threshold],
};
};
const WeatherWidget = () => {
return Widget.Box({
class_name: "calendar-menu-item-container weather",
child: Widget.Box({
class_name: "weather-container-box",
setup: (self) => {
Utils.interval(60 * 1000, () => {
Utils.execAsync(
`curl "https://api.weatherapi.com/v1/forecast.json?key=${keyRing.weatherapi}&q=93722&days=1&aqi=no&alerts=no"`,
)
.then((res) => {
if (typeof res === "string") {
theWeather.value = JSON.parse(res);
}
})
.catch((err) => {
console.error(`Failed to fetch weather: ${err}`);
theWeather.value = defaultWeather;
Utils.merge(
[key.bind("value"), interval.bind("value")],
(weatherKey, weatherInterval) => {
Utils.interval(weatherInterval, () => {
Utils.execAsync(
`curl "https://api.weatherapi.com/v1/forecast.json?key=${weatherKey}&q=93722&days=1&aqi=no&alerts=no"`,
)
.then((res) => {
if (typeof res === "string") {
theWeather.value = JSON.parse(res);
}
})
.catch((err) => {
console.error(`Failed to fetch weather: ${err}`);
theWeather.value = defaultWeather;
});
});
});
},
);
return (self.child = Widget.Box({
vertical: true,
@@ -90,226 +73,15 @@ const WeatherWidget = () => {
class_name: "calendar-menu-weather today",
hexpand: true,
children: [
Widget.Box({
vpack: "center",
hpack: "start",
class_name: "calendar-menu-weather today icon container",
children: [
Widget.Icon({
class_name: "calendar-menu-weather today icon",
icon: theWeather.bind("value").as((v) => {
let iconQuery = v.current.condition.text
.trim()
.toLowerCase()
.replaceAll(" ", "_")
if (!v.current.isDay && iconQuery === "partly_cloudy") {
iconQuery = "partly_cloudy_night";
}
return icons.weather[iconQuery];
}),
}),
],
}),
Widget.Box({
hpack: "center",
vpack: "center",
vertical: true,
children: [
Widget.Box({
hexpand: true,
vpack: "center",
class_name: "calendar-menu-weather today temp container",
vertical: false,
children: [
Widget.Box({
hexpand: true,
hpack: "center",
children: [
Widget.Label({
class_name:
"calendar-menu-weather today temp label",
label: theWeather
.bind("value")
.as((v) => `${Math.ceil(v.current.temp_f)}° F`),
}),
Widget.Label({
class_name: theWeather
.bind("value")
.as(
(v) =>
`calendar-menu-weather today temp label icon ${getIcon(Math.ceil(v.current.temp_f)).color}`,
),
label: theWeather
.bind("value")
.as(
(v) =>
getIcon(Math.ceil(v.current.temp_f)).icon,
),
}),
],
}),
],
}),
Widget.Box({
hpack: "center",
child: Widget.Label({
max_width_chars: 17,
truncate: "end",
lines: 2,
class_name: theWeather
.bind("value")
.as(
(v) =>
`calendar-menu-weather today condition label ${getIcon(Math.ceil(v.current.temp_f)).color}`,
),
label: theWeather
.bind("value")
.as((v) => v.current.condition.text),
}),
}),
],
}),
Widget.Box({
class_name: "calendar-menu-weather today stats container",
hpack: "end",
vpack: "center",
vertical: true,
children: [
Widget.Box({
class_name: "weather wind",
children: [
Widget.Label({
class_name: "weather wind icon",
label: "",
}),
Widget.Label({
class_name: "weather wind label",
label: theWeather
.bind("value")
.as((v) => `${Math.floor(v.current.wind_mph)} mph`),
}),
],
}),
Widget.Box({
class_name: "weather precip",
children: [
Widget.Label({
class_name: "weather precip icon",
label: "",
}),
Widget.Label({
class_name: "weather precip label",
label: theWeather
.bind("value")
.as(
(v) =>
`${v.forecast.forecastday[0].day.daily_chance_of_rain}%`,
),
}),
],
}),
],
}),
TodayIcon(theWeather),
TodayTemperature(theWeather),
TodayStats(theWeather),
],
}),
Widget.Separator({
class_name: "menu-separator weather",
}),
Widget.Box({
vertical: false,
hexpand: true,
hpack: "fill",
class_name: "hourly-weather-container",
children: [1, 2, 3, 4].map((hoursFromNow) => {
const getNextEpoch = (wthr) => {
const currentEpoch = wthr.location.localtime_epoch;
const epochAtHourStart = currentEpoch - (currentEpoch % 3600);
let nextEpoch = 3600 * hoursFromNow + epochAtHourStart;
const curHour = new Date(currentEpoch * 1000).getHours();
/*
* NOTE: Since the API is only capable of showing the current day; if
* the hours left in the day are less than 4 (aka spilling into the next day),
* then rewind to contain the prediction within the current day.
*/
if (curHour > 19) {
const hoursToRewind = curHour - 19;
nextEpoch =
3600 * hoursFromNow +
epochAtHourStart -
hoursToRewind * 3600;
}
return nextEpoch;
};
return Widget.Box({
class_name: "hourly-weather-item",
hexpand: true,
vertical: true,
children: [
Widget.Label({
class_name: "hourly-weather-time",
label: theWeather.bind("value").as((w) => {
if (!Object.keys(w).length) {
return "-";
}
const nextEpoch = getNextEpoch(w);
const dateAtEpoch = new Date(nextEpoch * 1000);
let hours = dateAtEpoch.getHours();
const ampm = hours >= 12 ? "PM" : "AM";
hours = hours % 12 || 12;
return `${hours}${ampm}`;
}),
}),
Widget.Icon({
class_name: "hourly-weather-icon",
icon: theWeather.bind("value").as((w) => {
if (!Object.keys(w).length) {
return "-";
}
const nextEpoch = getNextEpoch(w);
const weatherAtEpoch =
w.forecast.forecastday[0].hour.find(
(h) => h.time_epoch === nextEpoch,
);
let iconQuery = weatherAtEpoch?.condition.text
.trim()
.toLowerCase()
.replaceAll(" ", "_")
if (!weatherAtEpoch?.isDay && iconQuery === "partly_cloudy") {
iconQuery = "partly_cloudy_night";
}
return icons.weather[iconQuery];
}),
}),
Widget.Label({
class_name: "hourly-weather-temp",
label: theWeather.bind("value").as((w) => {
if (!Object.keys(w).length) {
return "-";
}
const nextEpoch = getNextEpoch(w);
const weatherAtEpoch =
w.forecast.forecastday[0].hour.find(
(h) => h.time_epoch === nextEpoch,
);
return `${weatherAtEpoch ? Math.ceil(weatherAtEpoch.temp_f) : "-"}° F`;
}),
}),
],
});
}),
}),
Hourly(theWeather),
],
}));
},

View File

@@ -0,0 +1,52 @@
import options from "options";
const { unit } = options.menus.clock.weather;
export const TodayStats = (theWeather) => {
return Widget.Box({
class_name: "calendar-menu-weather today stats container",
hpack: "end",
vpack: "center",
vertical: true,
children: [
Widget.Box({
class_name: "weather wind",
children: [
Widget.Label({
class_name: "weather wind icon",
label: "",
}),
Widget.Label({
class_name: "weather wind label",
label: Utils.merge(
[theWeather.bind("value"), unit.bind("value")],
(wthr, unt) => {
if (unt === "imperial") {
return `${Math.floor(wthr.current.wind_mph)} mph`;
}
return `${Math.floor(wthr.current.wind_kph)} kph`;
},
),
}),
],
}),
Widget.Box({
class_name: "weather precip",
children: [
Widget.Label({
class_name: "weather precip icon",
label: "",
}),
Widget.Label({
class_name: "weather precip label",
label: theWeather
.bind("value")
.as(
(v) => `${v.forecast.forecastday[0].day.daily_chance_of_rain}%`,
),
}),
],
}),
],
});
};

View File

@@ -0,0 +1,92 @@
import options from "options";
const { unit } = options.menus.clock.weather;
export const TodayTemperature = (theWeather) => {
const getIcon = (fahren) => {
const icons = {
100: "",
75: "",
50: "",
25: "",
0: "",
};
const colors = {
100: "weather-color red",
75: "weather-color orange",
50: "weather-color lavender",
25: "weather-color blue",
0: "weather-color sky",
};
const threshold =
fahren < 0
? 0
: [100, 75, 50, 25, 0].find((threshold) => threshold <= fahren);
return {
icon: icons[threshold],
color: colors[threshold],
};
};
return Widget.Box({
hpack: "center",
vpack: "center",
vertical: true,
children: [
Widget.Box({
hexpand: true,
vpack: "center",
class_name: "calendar-menu-weather today temp container",
vertical: false,
children: [
Widget.Box({
hexpand: true,
hpack: "center",
children: [
Widget.Label({
class_name: "calendar-menu-weather today temp label",
label: Utils.merge(
[theWeather.bind("value"), unit.bind("value")],
(wthr, unt) => {
if (unt === "imperial") {
return `${Math.ceil(wthr.current.temp_f)}° F`;
} else {
return `${Math.ceil(wthr.current.temp_c)}° C`;
}
},
),
}),
Widget.Label({
class_name: theWeather
.bind("value")
.as(
(v) =>
`calendar-menu-weather today temp label icon ${getIcon(Math.ceil(v.current.temp_f)).color}`,
),
label: theWeather
.bind("value")
.as((v) => getIcon(Math.ceil(v.current.temp_f)).icon),
}),
],
}),
],
}),
Widget.Box({
hpack: "center",
child: Widget.Label({
max_width_chars: 17,
truncate: "end",
lines: 2,
class_name: theWeather
.bind("value")
.as(
(v) =>
`calendar-menu-weather today condition label ${getIcon(Math.ceil(v.current.temp_f)).color}`,
),
label: theWeather.bind("value").as((v) => v.current.condition.text),
}),
}),
],
});
};