feat: add configurable wind units
This commit is contained in:
@@ -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">
|
||||
|
||||
Reference in New Issue
Block a user