1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
|
import { bind } from "astal";
import { Astal, Gtk, type Gdk } from "astal/gtk3";
import SWeather, { type WeatherData } from "../../services/weather";
import { ellipsize } from "../../utils/strings";
import { bindCurrentTime } from "../../utils/system";
import { Calendar as WCal } from "../../utils/widgets";
import PopupWindow from "../../widgets/popupwindow";
const getHoursFromUpdate = (data: WeatherData, hours: number) => {
const updateTime = data.location.localtime_epoch;
const updateHourStart = updateTime - (updateTime % 3600);
let nextHour = new Date((updateHourStart + hours * 3600) * 1000).getHours();
if (nextHour >= 24) nextHour -= 24;
return nextHour;
};
const Time = () => (
<box className="time">
<box hexpand halign={Gtk.Align.CENTER}>
<label label={bindCurrentTime("%I:%M:%S")} />
<label className="ampm" label={bindCurrentTime("%p", c => (c.get_hour() < 12 ? "AM" : "PM"))} />
</box>
</box>
);
const Calendar = () => (
<box className="calendar">
<eventbox
setup={self => {
self.connect("button-press-event", (_, event: Gdk.Event) => {
if (event.get_button()[1] === Astal.MouseButton.MIDDLE) {
const now = new Date();
const child = self.get_child() as WCal | null;
if (!child) return;
child.select_month(now.getMonth(), now.getFullYear());
}
});
}}
>
<WCal
hexpand
showDetails={false}
day={0}
setup={self => {
const update = () => {
const now = new Date();
if (self.month === now.getMonth() && self.year === now.getFullYear()) self.day = now.getDate();
else self.day = 0;
};
self.connect("month-changed", update);
self.connect("next-month", update);
self.connect("prev-month", update);
self.connect("next-year", update);
self.connect("prev-year", update);
update();
}}
/>
</eventbox>
</box>
);
const Weather = () => {
const weather = SWeather.get_default();
return (
<box vertical className="weather">
<centerbox className="current">
<label
halign={Gtk.Align.START}
valign={Gtk.Align.CENTER}
className="status-icon"
label={bind(weather, "icon")}
/>
<box vertical halign={Gtk.Align.CENTER} valign={Gtk.Align.CENTER} className="status">
<box halign={Gtk.Align.CENTER} className="temperature">
<label label={bind(weather, "temperature").as(t => `${Math.round(t)}°C`)} />
<label
className={bind(weather, "tempColour").as(c => `temp-icon ${c}`)}
label={bind(weather, "tempIcon")}
/>
</box>
<label label={bind(weather, "condition").as(c => ellipsize(c, 16))} />
</box>
<box vertical halign={Gtk.Align.END} valign={Gtk.Align.CENTER} className="other-data">
<label xalign={0} label={bind(weather, "wind").as(w => ` ${Math.round(w)} kph`)} />
<label xalign={0} label={bind(weather, "rainChance").as(r => ` ${r}%`)} />
</box>
</centerbox>
<box className="separator" />
<box className="forecast">
{Array.from({ length: 4 }).map((_, i) => (
<box vertical hexpand className="hour">
<label
label={bind(weather, "raw").as(r => {
const hour = getHoursFromUpdate(r, i + 1);
return `${hour % 12 || 12}${hour < 12 ? "AM" : "PM"}`;
})}
/>
<label
className="icon"
label={bind(weather, "raw").as(r =>
weather.getIcon(weather.forecast[getHoursFromUpdate(r, i + 1)]?.condition.text ?? "")
)}
/>
<label
label={bind(weather, "raw").as(
r => `${Math.round(weather.forecast[getHoursFromUpdate(r, i + 1)]?.temp_c) ?? "-"}°C`
)}
/>
</box>
))}
</box>
</box>
);
};
export default () => (
<PopupWindow name="sideright">
<box vertical className="sideright">
<Time />
<Calendar />
<Weather />
</box>
</PopupWindow>
);
|