refactor: simplify widget reload persistence

This commit is contained in:
Thomas Camlong committed 2026-08-23 09:00:33 +02:00
1 parent 65a96b6b14
commit 89e4cb843d
8 files changed
+79 -241

No files matched your search

+3 -5
View File
@@ -10,7 +10,7 @@ import { AdvancedClockView } from "./advanced-view";
import { clockTimeFormatShowsSeconds, resolveClockTimeFormat } from "./format";
import { useCurrentTime } from "./use-current-time";
import { ClockWeatherSummary } from "./weather-summary";
import { getResolvedLocalTimeZone, isTimeZoneSupported } from "./world-clock";
import { isTimeZoneSupported } from "./world-clock";
dayjs.extend(advancedFormat);
@@ -18,10 +18,8 @@ export default function ClockWidget({ options, width, height, displayMode }: Wid
const isAdvanced = displayMode === "advanced";
const showSeconds = clockTimeFormatShowsSeconds(options.customTimeFormat);
const time = useCurrentTime({ showSeconds });
const initialLocalTimeZone = useWidgetLocalTimeZone();
const requestedTimeZone = options.useCustomTimezone
? options.timezone
: (initialLocalTimeZone ?? getResolvedLocalTimeZone());
const localTimeZone = useWidgetLocalTimeZone();
const requestedTimeZone = options.useCustomTimezone ? options.timezone : localTimeZone;
const primaryTimeZoneInvalid = !isTimeZoneSupported(requestedTimeZone);
const primaryTimeZone = primaryTimeZoneInvalid ? "UTC" : requestedTimeZone;
const zonedTime = time === null ? null : dayjs(time).tz(primaryTimeZone);
@@ -24,9 +24,11 @@ describe("WidgetTimeProvider", () => {
vi.useFakeTimers();
const serverTimestamp = new Date("2026-08-22T12:34:00.000Z").getTime();
const clientTimestamp = serverTimestamp + 60_000;
const browserTimeZone = Intl.DateTimeFormat().resolvedOptions().timeZone || "UTC";
const serverTimeZone = browserTimeZone === "UTC" ? "Europe/Paris" : "UTC";
vi.setSystemTime(serverTimestamp);
const content = (
<WidgetTimeProvider initialTimestamp={serverTimestamp} initialTimeZone="UTC">
<WidgetTimeProvider initialTimestamp={serverTimestamp} initialTimeZone={serverTimeZone}>
<TimeProbe />
</WidgetTimeProvider>
);
@@ -34,15 +36,12 @@ describe("WidgetTimeProvider", () => {
const host = document.createElement("div");
host.innerHTML = html;
const consoleError = vi.spyOn(console, "error").mockImplementation(() => undefined);
vi.spyOn(Intl.DateTimeFormat.prototype, "resolvedOptions").mockReturnValue({
timeZone: "Europe/Paris",
} as Intl.ResolvedDateTimeFormatOptions);
vi.setSystemTime(clientTimestamp);
expect(host.textContent).toBe("2026-08-22T12:34:00.000Z|UTC");
expect(host.textContent).toBe(`2026-08-22T12:34:00.000Z|${serverTimeZone}`);
const root = hydrateRoot(host, content);
await act(async () => undefined);
expect(host.textContent).toBe("2026-08-22T12:35:00.000Z|Europe/Paris");
expect(host.textContent).toBe(`2026-08-22T12:35:00.000Z|${browserTimeZone}`);
expect(consoleError).not.toHaveBeenCalled();
await act(async () => root.unmount());
@@ -1,14 +1,10 @@
"use client";
import type { PropsWithChildren } from "react";
import { createContext, useContext, useEffect, useMemo, useState } from "react";
import { createContext, useContext, useSyncExternalStore } from "react";
interface WidgetTimeContextValue {
initialTimestamp: number | null;
localTimeZone: string | null;
}
const WidgetTimeContext = createContext<WidgetTimeContextValue>({ initialTimestamp: null, localTimeZone: null });
const WidgetTimestampContext = createContext<number | null>(null);
const WidgetTimeZoneContext = createContext("UTC");
interface WidgetTimeProviderProps {
initialTimestamp: number;
@@ -19,19 +15,21 @@ export const WidgetTimeProvider = ({
children,
initialTimestamp,
initialTimeZone,
}: PropsWithChildren<WidgetTimeProviderProps>) => {
const [localTimeZone, setLocalTimeZone] = useState(() => getSupportedTimeZone(initialTimeZone));
}: PropsWithChildren<WidgetTimeProviderProps>) => (
<WidgetTimestampContext.Provider value={initialTimestamp}>
<WidgetTimeZoneContext.Provider value={getSupportedTimeZone(initialTimeZone)}>
{children}
</WidgetTimeZoneContext.Provider>
</WidgetTimestampContext.Provider>
);
useEffect(() => {
setLocalTimeZone(getResolvedLocalTimeZone());
}, []);
const value = useMemo(() => ({ initialTimestamp, localTimeZone }), [initialTimestamp, localTimeZone]);
return <WidgetTimeContext.Provider value={value}>{children}</WidgetTimeContext.Provider>;
export const useWidgetInitialTimestamp = () => useContext(WidgetTimestampContext);
export const useWidgetLocalTimeZone = () => {
const initialTimeZone = useContext(WidgetTimeZoneContext);
return useSyncExternalStore(subscribeToTimeZone, getResolvedLocalTimeZone, () => initialTimeZone);
};
export const useWidgetInitialTimestamp = () => useContext(WidgetTimeContext).initialTimestamp;
export const useWidgetLocalTimeZone = () => useContext(WidgetTimeContext).localTimeZone;
const subscribeToTimeZone = () => () => undefined;
const getResolvedLocalTimeZone = () => getSupportedTimeZone(Intl.DateTimeFormat().resolvedOptions().timeZone);