178 lines
8.0 KiB
TypeScript
178 lines
8.0 KiB
TypeScript
"use client";
|
|
|
|
import { create } from "zustand";
|
|
import { persist } from "zustand/middleware";
|
|
import type { SelectedLocation } from "@/types/location";
|
|
import type { Province } from "@/types/province";
|
|
import type { DistanceUnit, PrecipitationUnit, PressureUnit, TemperatureUnit, WindSpeedUnit } from "@/types/units";
|
|
import {
|
|
DEFAULT_APP_SECTION_VISIBILITY,
|
|
normalizeAppSectionVisibility,
|
|
type AppSectionId,
|
|
type AppSectionVisibility,
|
|
} from "@/lib/app-sections";
|
|
import {
|
|
DEFAULT_DASHBOARD_SECTION_ORDER,
|
|
DEFAULT_DASHBOARD_SECTION_VISIBILITY,
|
|
normalizeDashboardSectionOrder,
|
|
normalizeDashboardSectionVisibility,
|
|
type DashboardSectionId,
|
|
type DashboardSectionOrder,
|
|
type DashboardSectionVisibility,
|
|
} from "@/lib/dashboard-sections";
|
|
import {
|
|
DEFAULT_DISTANCE_UNIT,
|
|
DEFAULT_PRECIPITATION_UNIT,
|
|
DEFAULT_PRESSURE_UNIT,
|
|
DEFAULT_TEMPERATURE_UNIT,
|
|
DEFAULT_WIND_SPEED_UNIT,
|
|
isDistanceUnit,
|
|
isPrecipitationUnit,
|
|
isPressureUnit,
|
|
isTemperatureUnit,
|
|
isWindSpeedUnit,
|
|
} from "@/lib/weather-utils";
|
|
import type { WeatherRegion } from "@/types/weather-region";
|
|
import { getWeatherRegionForCountryCode } from "@/types/weather-region";
|
|
|
|
type NotificationProvinceMode = "selected" | "manual";
|
|
|
|
interface WeatherStore {
|
|
favorites: string[];
|
|
selectedStationId: string | null;
|
|
selectedLocation: SelectedLocation | null;
|
|
warningNotificationsEnabled: boolean;
|
|
morningBriefNotificationsEnabled: boolean;
|
|
tomorrowBriefNotificationsEnabled: boolean;
|
|
warningNotificationProvinceMode: NotificationProvinceMode;
|
|
warningNotificationProvince: Province | null;
|
|
temperatureUnit: TemperatureUnit;
|
|
windSpeedUnit: WindSpeedUnit;
|
|
precipitationUnit: PrecipitationUnit;
|
|
pressureUnit: PressureUnit;
|
|
distanceUnit: DistanceUnit;
|
|
dashboardSections: DashboardSectionVisibility;
|
|
dashboardSectionOrder: DashboardSectionOrder;
|
|
appSections: AppSectionVisibility;
|
|
toggleFavorite: (id: string) => void;
|
|
selectStation: (id: string) => void;
|
|
selectLocation: (location: SelectedLocation) => void;
|
|
setWarningNotificationsEnabled: (enabled: boolean) => void;
|
|
setMorningBriefNotificationsEnabled: (enabled: boolean) => void;
|
|
setTomorrowBriefNotificationsEnabled: (enabled: boolean) => void;
|
|
setWarningNotificationProvinceMode: (mode: NotificationProvinceMode) => void;
|
|
setWarningNotificationProvince: (province: Province | null) => void;
|
|
setTemperatureUnit: (unit: TemperatureUnit) => void;
|
|
setWindSpeedUnit: (unit: WindSpeedUnit) => void;
|
|
setPrecipitationUnit: (unit: PrecipitationUnit) => void;
|
|
setPressureUnit: (unit: PressureUnit) => void;
|
|
setDistanceUnit: (unit: DistanceUnit) => void;
|
|
setDashboardSectionVisible: (section: DashboardSectionId, visible: boolean) => void;
|
|
moveDashboardSection: (section: DashboardSectionId, direction: -1 | 1) => void;
|
|
setAppSectionVisible: (section: AppSectionId, visible: boolean) => void;
|
|
}
|
|
|
|
export const useWeatherStore = create<WeatherStore>()(
|
|
persist(
|
|
(set) => ({
|
|
favorites: [],
|
|
selectedStationId: null,
|
|
selectedLocation: null,
|
|
warningNotificationsEnabled: false,
|
|
morningBriefNotificationsEnabled: false,
|
|
tomorrowBriefNotificationsEnabled: false,
|
|
warningNotificationProvinceMode: "selected",
|
|
warningNotificationProvince: null,
|
|
temperatureUnit: DEFAULT_TEMPERATURE_UNIT,
|
|
windSpeedUnit: DEFAULT_WIND_SPEED_UNIT,
|
|
precipitationUnit: DEFAULT_PRECIPITATION_UNIT,
|
|
pressureUnit: DEFAULT_PRESSURE_UNIT,
|
|
distanceUnit: DEFAULT_DISTANCE_UNIT,
|
|
dashboardSections: DEFAULT_DASHBOARD_SECTION_VISIBILITY,
|
|
dashboardSectionOrder: DEFAULT_DASHBOARD_SECTION_ORDER,
|
|
appSections: DEFAULT_APP_SECTION_VISIBILITY,
|
|
toggleFavorite: (id) =>
|
|
set((state) => ({
|
|
favorites: state.favorites.includes(id)
|
|
? state.favorites.filter((favoriteId) => favoriteId !== id)
|
|
: [...state.favorites, id],
|
|
})),
|
|
selectStation: (id) => set({ selectedStationId: id, selectedLocation: null }),
|
|
selectLocation: (location) => set({ selectedStationId: location.stationId, selectedLocation: location }),
|
|
setWarningNotificationsEnabled: (enabled) => set({ warningNotificationsEnabled: enabled }),
|
|
setMorningBriefNotificationsEnabled: (enabled) => set({ morningBriefNotificationsEnabled: enabled }),
|
|
setTomorrowBriefNotificationsEnabled: (enabled) => set({ tomorrowBriefNotificationsEnabled: enabled }),
|
|
setWarningNotificationProvinceMode: (mode) => set({ warningNotificationProvinceMode: mode }),
|
|
setWarningNotificationProvince: (province) => set({ warningNotificationProvince: province }),
|
|
setTemperatureUnit: (unit) => set({ temperatureUnit: unit }),
|
|
setWindSpeedUnit: (unit) => set({ windSpeedUnit: unit }),
|
|
setPrecipitationUnit: (unit) => set({ precipitationUnit: unit }),
|
|
setPressureUnit: (unit) => set({ pressureUnit: unit }),
|
|
setDistanceUnit: (unit) => set({ distanceUnit: unit }),
|
|
setDashboardSectionVisible: (section, visible) =>
|
|
set((state) => ({
|
|
dashboardSections: { ...state.dashboardSections, [section]: visible },
|
|
})),
|
|
moveDashboardSection: (section, direction) =>
|
|
set((state) => {
|
|
const order = normalizeDashboardSectionOrder(state.dashboardSectionOrder);
|
|
const index = order.indexOf(section);
|
|
const nextIndex = index + direction;
|
|
if (index === -1 || nextIndex < 0 || nextIndex >= order.length) return state;
|
|
const nextOrder = [...order];
|
|
[nextOrder[index], nextOrder[nextIndex]] = [nextOrder[nextIndex], nextOrder[index]];
|
|
return { dashboardSectionOrder: nextOrder };
|
|
}),
|
|
setAppSectionVisible: (section, visible) =>
|
|
set((state) => ({
|
|
appSections: { ...state.appSections, [section]: visible },
|
|
})),
|
|
}),
|
|
{
|
|
name: "wtr:preferences",
|
|
migrate: (persisted) => {
|
|
const state = persisted as Partial<WeatherStore> | undefined;
|
|
const location = state?.selectedLocation as
|
|
| (Partial<SelectedLocation> & { region?: WeatherRegion })
|
|
| null
|
|
| undefined;
|
|
if (!state) return persisted;
|
|
const dashboardSections = normalizeDashboardSectionVisibility(state.dashboardSections);
|
|
const dashboardSectionOrder = normalizeDashboardSectionOrder(state.dashboardSectionOrder);
|
|
const appSections = normalizeAppSectionVisibility(state.appSections);
|
|
const unitPreferences = {
|
|
temperatureUnit: isTemperatureUnit(state.temperatureUnit) ? state.temperatureUnit : DEFAULT_TEMPERATURE_UNIT,
|
|
windSpeedUnit: isWindSpeedUnit(state.windSpeedUnit) ? state.windSpeedUnit : DEFAULT_WIND_SPEED_UNIT,
|
|
precipitationUnit: isPrecipitationUnit(state.precipitationUnit)
|
|
? state.precipitationUnit
|
|
: DEFAULT_PRECIPITATION_UNIT,
|
|
pressureUnit: isPressureUnit(state.pressureUnit) ? state.pressureUnit : DEFAULT_PRESSURE_UNIT,
|
|
distanceUnit: isDistanceUnit(state.distanceUnit) ? state.distanceUnit : DEFAULT_DISTANCE_UNIT,
|
|
};
|
|
if (!location) return { ...state, ...unitPreferences, dashboardSections, dashboardSectionOrder, appSections };
|
|
const countryCode = location.countryCode ?? (location.province ? "PL" : null);
|
|
const region = location.region ?? getWeatherRegionForCountryCode(countryCode);
|
|
return {
|
|
...state,
|
|
...unitPreferences,
|
|
dashboardSections,
|
|
dashboardSectionOrder,
|
|
appSections,
|
|
selectedLocation: {
|
|
...location,
|
|
country: location.country ?? (countryCode === "PL" ? "Polska" : null),
|
|
countryCode,
|
|
admin1: location.admin1 ?? location.province ?? null,
|
|
admin2: location.admin2 ?? location.district ?? null,
|
|
timezone: location.timezone ?? (countryCode === "PL" ? "Europe/Warsaw" : null),
|
|
region,
|
|
stationId: location.stationId ?? null,
|
|
stationName: location.stationName ?? null,
|
|
distanceKm: location.distanceKm ?? null,
|
|
},
|
|
};
|
|
},
|
|
},
|
|
),
|
|
);
|