frontend client for gemstone. decentralised workplace app
1import { useFacet } from "@/lib/facet";
2import type { Facet } from "@/lib/facet/src/facet";
3import type { FacetPalette } from "@/lib/facet/src/palette";
4import type { Enumify } from "@/lib/utils/types";
5import type { Dispatch, ReactNode, SetStateAction } from "react";
6import { createContext, useContext, useState } from "react";
7
8export const ColorMode = {
9 DARK: "dark",
10 LIGHT: "light",
11} as const;
12
13// eslint-disable-next-line @typescript-eslint/no-redeclare -- intentional. lets us have enum-like objects without some of the weirdness
14export type ColorMode = Enumify<typeof ColorMode>;
15
16interface ThemeContextValue {
17 colorMode: ColorMode;
18 setColorMode: Dispatch<SetStateAction<ColorMode>>;
19 currentPalette: FacetPalette;
20}
21
22const ThemeContext = createContext<ThemeContextValue | null>(null);
23
24const useThemeProvider = () => {
25 const value = useContext(ThemeContext);
26 if (!value)
27 throw new Error(
28 "Theme provider failed to initialise. Did you access this out of tree somehow? Tried to access theme values before it was initialised.",
29 );
30 return value;
31};
32
33export const useColorMode = () => {
34 const { colorMode } = useThemeProvider();
35 return colorMode;
36};
37
38export const useSetColorMode = () => {
39 const { setColorMode } = useThemeProvider();
40 return setColorMode;
41};
42
43export const useCurrentPalette = () => {
44 const { currentPalette } = useThemeProvider();
45 return currentPalette;
46};
47
48export const ThemeProvider = ({ children }: { children: ReactNode }) => {
49 const [colorMode, setColorMode] = useState<ColorMode>(ColorMode.DARK);
50 const facet = useFacet();
51 const currentPalette =
52 colorMode === "dark"
53 ? facet.variants.obsidian
54 : // TODO: temporary. will remove the null coalesce when pearl is defined.
55 (facet.variants.pearl ?? facet.variants.obsidian);
56
57 // TODO: remove nullability with obsidian and pearl palettes.
58 // instead, these are provided as defaults and if we add more themes
59 // in the future, they can just be tacked on.
60 if (!currentPalette) throw new Error("Don't use light mode for now.");
61
62 const value: ThemeContextValue = {
63 colorMode,
64 setColorMode,
65 currentPalette,
66 };
67 return <ThemeContext value={value}>{children}</ThemeContext>;
68};