feat: add configurable wind units

This commit is contained in:
zv
2026-06-13 13:36:36 +02:00
parent a8d4d1e23c
commit 7ad95550eb
21 changed files with 152 additions and 38 deletions

View File

@@ -1,7 +1,7 @@
"use client";
import { useEffect, useMemo, useState } from "react";
import { Bell, BellRing, Clock3, Languages, MapPinned, Palette, Settings, Smartphone } from "lucide-react";
import { Bell, BellRing, Clock3, Languages, MapPinned, Palette, Settings, Smartphone, Wind } from "lucide-react";
import type { LucideIcon } from "lucide-react";
import { Button } from "@/components/ui/button";
import { Card } from "@/components/ui/card";
@@ -15,7 +15,9 @@ import { locateSynopStations } from "@/lib/location-utils";
import { decodeBase64UrlKey, deletePushSubscription, fetchVapidPublicKey, savePushSubscription, sendTestPushNotification } from "@/lib/notification-api";
import { formatProvinceName, getProvinceForSelection, PROVINCES } from "@/lib/provinces";
import { useWeatherStore } from "@/lib/store";
import { WIND_SPEED_UNITS } from "@/lib/weather-utils";
import type { Province } from "@/types/province";
import type { WindSpeedUnit } from "@/types/units";
type NotificationSupportStatus = "checking" | "unconfigured" | "unsupported" | "needs-install" | "default" | "denied" | "granted";
@@ -100,6 +102,12 @@ function NotificationSwitchLabel({ icon: Icon, title, description, checked, disa
);
}
const windSpeedUnitKeys: Record<WindSpeedUnit, "settings.units.wind.ms" | "settings.units.wind.kmh" | "settings.units.wind.mph"> = {
ms: "settings.units.wind.ms",
kmh: "settings.units.wind.kmh",
mph: "settings.units.wind.mph",
};
export function SettingsPage() {
const { language, t } = useI18n();
const [notificationStatus, setNotificationStatus] = useState<NotificationSupportStatus>("checking");
@@ -116,11 +124,13 @@ export function SettingsPage() {
const tomorrowBriefEnabled = useWeatherStore((state) => state.tomorrowBriefNotificationsEnabled);
const provinceMode = useWeatherStore((state) => state.warningNotificationProvinceMode);
const manualProvince = useWeatherStore((state) => state.warningNotificationProvince);
const windSpeedUnit = useWeatherStore((state) => state.windSpeedUnit);
const setNotificationsEnabled = useWeatherStore((state) => state.setWarningNotificationsEnabled);
const setMorningBriefEnabled = useWeatherStore((state) => state.setMorningBriefNotificationsEnabled);
const setTomorrowBriefEnabled = useWeatherStore((state) => state.setTomorrowBriefNotificationsEnabled);
const setProvinceMode = useWeatherStore((state) => state.setWarningNotificationProvinceMode);
const setManualProvince = useWeatherStore((state) => state.setWarningNotificationProvince);
const setWindSpeedUnit = useWeatherStore((state) => state.setWindSpeedUnit);
const selectedStation = stations?.find((station) => station.id === selectedStationId)
?? stations?.find((station) => station.id === DEFAULT_STATION_ID)
@@ -170,6 +180,7 @@ export function SettingsPage() {
longitude: notificationLongitude,
locationName: notificationLocationName,
countyTeryt: notificationCountyTeryt,
windSpeedUnit,
}).catch(() => undefined);
}
@@ -177,7 +188,7 @@ export function SettingsPage() {
return () => {
cancelled = true;
};
}, [effectiveProvince, language, morningBriefEnabled, notificationCountyTeryt, notificationLatitude, notificationLocationName, notificationLongitude, notificationStatus, notificationsEnabled, tomorrowBriefEnabled, vapidPublicKey]);
}, [effectiveProvince, language, morningBriefEnabled, notificationCountyTeryt, notificationLatitude, notificationLocationName, notificationLongitude, notificationStatus, notificationsEnabled, tomorrowBriefEnabled, vapidPublicKey, windSpeedUnit]);
const notificationStatusLabel = useMemo(() => {
switch (notificationStatus) {
@@ -236,6 +247,7 @@ export function SettingsPage() {
longitude: notificationLongitude,
locationName: notificationLocationName,
countyTeryt: notificationCountyTeryt,
windSpeedUnit,
});
setNotificationsEnabled(true);
setNotificationMessage(t("settings.notifications.saveSuccess"));
@@ -298,6 +310,7 @@ export function SettingsPage() {
longitude: notificationLongitude,
locationName: notificationLocationName,
countyTeryt: notificationCountyTeryt,
windSpeedUnit,
});
} catch {
setNotificationMessage(t("settings.notifications.saveError"));
@@ -318,6 +331,7 @@ export function SettingsPage() {
longitude: notificationLongitude,
locationName: notificationLocationName,
countyTeryt: notificationCountyTeryt,
windSpeedUnit,
});
} catch {
setNotificationMessage(t("settings.notifications.saveError"));
@@ -339,6 +353,26 @@ export function SettingsPage() {
<SettingsRow icon={Palette} title={t("settings.theme.title")} description={t("settings.theme.description")}>
<ThemeToggle />
</SettingsRow>
<SettingsRow icon={Wind} title={t("settings.units.wind.title")} description={t("settings.units.wind.description")}>
<div className="surface-control inline-flex rounded-control p-1" role="radiogroup" aria-label={t("settings.units.wind.label")}>
{WIND_SPEED_UNITS.map((unit) => (
<button
key={unit}
type="button"
role="radio"
aria-checked={windSpeedUnit === unit}
className={`rounded-[calc(var(--radius-control)-0.25rem)] px-3 py-1.5 text-sm font-medium transition-colors focus-visible:outline focus-visible:outline-2 focus-visible:outline-offset-2 focus-visible:outline-accent ${
windSpeedUnit === unit
? "bg-accent text-accent-foreground shadow-soft"
: "text-muted hover:text-foreground"
}`}
onClick={() => setWindSpeedUnit(unit)}
>
{t(windSpeedUnitKeys[unit])}
</button>
))}
</div>
</SettingsRow>
</Card>
<Card className="p-4 sm:p-5">