61 lines
1.6 KiB
TypeScript
61 lines
1.6 KiB
TypeScript
import { defineStore } from "pinia";
|
|
|
|
type ThemeName = "light" | "dark";
|
|
const themeCookieName = "theme";
|
|
|
|
function isThemeName(value: string | null | undefined): value is ThemeName {
|
|
return value === "dark" || value === "light";
|
|
}
|
|
|
|
function getCookieTheme(): ThemeName | null {
|
|
if (!import.meta.client) return null;
|
|
|
|
const cookie = document.cookie
|
|
.split("; ")
|
|
.find((item) => item.startsWith(`${themeCookieName}=`));
|
|
const value = cookie ? decodeURIComponent(cookie.split("=").slice(1).join("=")) : null;
|
|
return isThemeName(value) ? value : null;
|
|
}
|
|
|
|
function persistTheme(theme: ThemeName) {
|
|
localStorage.setItem(themeCookieName, theme);
|
|
document.cookie = `${themeCookieName}=${theme}; Path=/; Max-Age=31536000; SameSite=Lax`;
|
|
}
|
|
|
|
export const useThemeStore = defineStore("theme", {
|
|
state: () => ({
|
|
current: "light" as ThemeName,
|
|
userSelected: false
|
|
}),
|
|
actions: {
|
|
getInitialTheme(): ThemeName {
|
|
if (!import.meta.client) return "light";
|
|
|
|
const saved = localStorage.getItem(themeCookieName);
|
|
if (isThemeName(saved)) {
|
|
this.userSelected = true;
|
|
persistTheme(saved);
|
|
return saved;
|
|
}
|
|
|
|
const cookieTheme = getCookieTheme();
|
|
if (cookieTheme) {
|
|
this.userSelected = true;
|
|
persistTheme(cookieTheme);
|
|
return cookieTheme;
|
|
}
|
|
|
|
if (window.matchMedia("(prefers-color-scheme: dark)").matches) {
|
|
return "dark";
|
|
}
|
|
return "light";
|
|
},
|
|
setTheme(theme: ThemeName, fromUser: boolean) {
|
|
this.current = theme;
|
|
if (import.meta.client && fromUser) {
|
|
this.userSelected = true;
|
|
persistTheme(theme);
|
|
}
|
|
}
|
|
}
|
|
});
|