fix: improve cookie store

This commit is contained in:
Carl-Gerhard Lindesvärd
2025-10-20 10:29:38 +02:00
parent 90c1a813af
commit f958230a66
2 changed files with 36 additions and 19 deletions

View File

@@ -18,18 +18,6 @@ export type AppTheme = z.infer<typeof AppThemeSchema>;
const themeStorageKey = 'ui-theme';
const getStoredUserTheme = createIsomorphicFn()
.server((): UserTheme => 'system')
.client((): UserTheme => {
const stored = localStorage.getItem(themeStorageKey);
return UserThemeSchema.parse(stored);
});
const setStoredTheme = clientOnly((theme: UserTheme) => {
const validatedTheme = UserThemeSchema.parse(theme);
localStorage.setItem(themeStorageKey, validatedTheme);
});
const getSystemTheme = createIsomorphicFn()
.server((): AppTheme => 'light')
.client((): AppTheme => {
@@ -73,7 +61,10 @@ const themes = mapKeys(themeConfig).map((key) => ({
const themeScript = (() => {
function themeFn() {
try {
const storedTheme = localStorage.getItem('ui-theme') || 'system';
// Read theme from cookie
const cookies = document.cookie.split('; ');
const themeCookie = cookies.find((c) => c.startsWith('ui-theme='));
const storedTheme = themeCookie ? themeCookie.split('=')[1] : 'system';
const validTheme = ['light', 'dark', 'system'].includes(storedTheme)
? storedTheme
: 'system';
@@ -131,7 +122,6 @@ export function ThemeProvider({ children }: ThemeProviderProps) {
const setTheme = (newUserTheme: UserTheme) => {
const validatedTheme = UserThemeSchema.parse(newUserTheme);
setUserTheme(validatedTheme);
setStoredTheme(validatedTheme);
handleThemeChange(validatedTheme);
};

View File

@@ -2,10 +2,11 @@ import { useRouteContext } from '@tanstack/react-router';
import { createServerFn, createServerOnlyFn } from '@tanstack/react-start';
import { getCookies, setCookie } from '@tanstack/react-start/server';
import { pick } from 'ramda';
import { useMemo, useState } from 'react';
import { useEffect, useMemo, useRef, useState } from 'react';
import { z } from 'zod';
const VALID_COOKIES = ['ui-theme', 'chartType', 'range'] as const;
const COOKIE_EVENT_NAME = '__cookie-change';
const setCookieFn = createServerFn({ method: 'POST' })
.inputValidator(z.object({ key: z.enum(VALID_COOKIES), value: z.string() }))
@@ -25,15 +26,41 @@ export function useCookieStore<T>(
) {
const { cookies } = useRouteContext({ strict: false });
const [value, setValue] = useState<T>((cookies?.[key] ?? defaultValue) as T);
const ref = useRef(Math.random().toString(36).substring(7));
useEffect(() => {
const handleCookieChange = (
event: CustomEvent<{ key: string; value: T; from: string }>,
) => {
if (event.detail.key === key && event.detail.from !== ref.current) {
setValue(event.detail.value);
}
};
window.addEventListener(
COOKIE_EVENT_NAME,
handleCookieChange as EventListener,
);
return () => {
window.removeEventListener(
COOKIE_EVENT_NAME,
handleCookieChange as EventListener,
);
};
}, [key]);
return useMemo(
() =>
[
value,
(value: T) => {
console.log('setting cookie', key, value);
setValue(value);
setCookieFn({ data: { key, value: String(value) } });
(newValue: T) => {
setValue(newValue);
setCookieFn({ data: { key, value: String(newValue) } });
window.dispatchEvent(
new CustomEvent(COOKIE_EVENT_NAME, {
detail: { key, value: newValue, from: ref.current },
}),
);
},
] as const,
[value, key],