Skip to content

Commit ac4a98c

Browse files
committed
refactor: ThemeContext
1 parent 144d9cf commit ac4a98c

File tree

6 files changed

+82
-43
lines changed

6 files changed

+82
-43
lines changed

frontend/src/components/StartPage.tsx

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -10,7 +10,7 @@ export const StartPage = () => {
1010
only be
1111
accessed if enough secret shares are collected.
1212
</p>
13-
<img className="h-16 w-16 lg:h-32 lg:w-32" src="/favicon.svg"/>
13+
<img alt="synod logo" className="h-16 w-16 lg:h-32 lg:w-32" src="/favicon.svg"/>
1414
</div>
1515
</div>
1616
</div>

frontend/src/components/ThemeSwitcher.tsx

Lines changed: 3 additions & 24 deletions
Original file line numberDiff line numberDiff line change
@@ -1,28 +1,7 @@
1-
import {useEffect, useState} from "react";
1+
import {useTheme} from "../contexts/ThemeContext.tsx";
22

33
export const ThemeSwitcher = () => {
4-
const themes = ["corporate", "business"]
5-
const [theme, setTheme] = useState(currentTheme())
6-
7-
function currentTheme(): string {
8-
return document.documentElement.getAttribute("data-theme") ?? "corporate"
9-
}
10-
11-
useEffect(() => {
12-
const saved = localStorage.getItem("theme");
13-
if (saved) {
14-
setTheme(saved);
15-
document.documentElement.setAttribute("data-theme", saved);
16-
}
17-
}, []);
18-
19-
const toggleTheme = () => {
20-
const nextIndex = (themes.indexOf(theme) + 1) % themes.length;
21-
const nextTheme = themes[nextIndex];
22-
setTheme(nextTheme);
23-
document.documentElement.setAttribute("data-theme", nextTheme);
24-
localStorage.setItem("theme", nextTheme);
25-
}
4+
const {theme, switchTheme} = useTheme();
265

276
return <div className="dropdown dropdown-end">
287
<label className="flex cursor-pointer gap-2">
@@ -44,7 +23,7 @@ export const ThemeSwitcher = () => {
4423
type="checkbox"
4524
checked={theme == "business"}
4625
className="toggle theme-controller"
47-
onChange={toggleTheme}
26+
onChange={switchTheme}
4827
/>
4928
<svg
5029
xmlns="http://www.w3.org/2000/svg"

frontend/src/components/secrets/SecretModal.tsx

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -41,6 +41,8 @@ export const SecretModal: React.FC<SecretModalProps> = ({handleSecret, existingS
4141
setSecret(existingSecret?.value ?? "")
4242
setUrl(existingSecret?.url ?? "")
4343
setTags(existingSecret?.tags ?? [])
44+
setTag("")
45+
setPasswordVisible(false)
4446
}, [existingSecret, isOpen]);
4547

4648
async function onSubmit() {
@@ -116,17 +118,15 @@ export const SecretModal: React.FC<SecretModalProps> = ({handleSecret, existingS
116118
setTag(e.target.value)
117119
}}
118120
onKeyDown={(e) => {
119-
if (e.code == "Space" || e.key == " " || e.code == "Comma" || e.key == ",") {
121+
if (e.code == "Enter") {
120122
if (tag.length == 0) return
121123
setTags([...tags, tag])
122124
setTag("")
123125
}
124126
}}
125127
disabled={tags.length >= 3} className="grow"/>
126128
<span className="badge badge-neutral badge-xs">&lt;4</span>
127-
<kbd className="kbd kbd-sm"></kbd>
128-
/
129-
<kbd className="kbd kbd-sm">,</kbd>
129+
<kbd className="kbd kbd-sm"></kbd>
130130
</label>
131131
<div className="flex flex-col gap-2">
132132
{tags.map((tag) => (

frontend/src/contexts/AuthContext.tsx

Lines changed: 6 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -15,19 +15,14 @@ const AuthContext = createContext<AuthContextType | undefined>(undefined);
1515
export function AuthProvider({children}: { children: React.ReactNode }) {
1616
const [authStatus, setAuthStatus] = useState<AuthStatus | null>(null);
1717

18-
const checkAuth = async () => {
19-
try {
20-
const authStatus = await getAuth();
21-
setAuthStatus(authStatus)
22-
} catch {
23-
setAuthStatus(null);
24-
}
18+
const checkAuth = () => {
19+
getAuth()
20+
.then(authStatus => setAuthStatus(authStatus))
21+
.catch(() => setAuthStatus(null))
2522
}
2623

2724
useEffect(() => {
28-
(async () => {
29-
await checkAuth();
30-
})();
25+
checkAuth();
3126
}, []);
3227

3328
useEffect(() => {
@@ -50,9 +45,7 @@ export function AuthProvider({children}: { children: React.ReactNode }) {
5045
};
5146

5247
const reloadAuth = () => {
53-
(async () => {
54-
await checkAuth();
55-
})();
48+
checkAuth();
5649
}
5750

5851
return (
Lines changed: 64 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,64 @@
1+
import React, {createContext, useContext, useEffect, useState} from "react";
2+
3+
type Theme = "corporate" | "business"
4+
5+
interface ThemeContextType {
6+
theme: Theme;
7+
switchTheme: () => void;
8+
}
9+
10+
const ThemeContext = createContext<ThemeContextType | undefined>(undefined);
11+
12+
export const ThemeProvider = ({children}: { children: React.ReactNode }) => {
13+
const getPreferredTheme = (): Theme => {
14+
const stored = localStorage.getItem("theme") as Theme | null;
15+
if (stored) return stored;
16+
17+
if (window.matchMedia("(prefers-color-scheme: dark)").matches) {
18+
return "business";
19+
}
20+
21+
return "corporate";
22+
};
23+
24+
const [theme, setTheme] = useState<Theme>(getPreferredTheme);
25+
26+
useEffect(() => {
27+
document.documentElement.setAttribute("data-theme", theme);
28+
localStorage.setItem("theme", theme);
29+
}, [theme]);
30+
31+
useEffect(() => {
32+
const media = window.matchMedia("(prefers-color-scheme: dark)");
33+
const handler = (e: MediaQueryListEvent) => {
34+
if (!localStorage.getItem("theme")) {
35+
setTheme(e.matches ? "business" : "corporate");
36+
}
37+
};
38+
media.addEventListener("change", handler);
39+
return () => media.removeEventListener("change", handler);
40+
}, []);
41+
42+
const switchTheme = () => {
43+
setTheme(currentTheme => {
44+
switch (currentTheme) {
45+
case "corporate":
46+
return "business"
47+
case "business":
48+
return "corporate"
49+
}
50+
})
51+
}
52+
53+
return (
54+
<ThemeContext.Provider value={{theme, switchTheme}}>
55+
{children}
56+
</ThemeContext.Provider>
57+
);
58+
};
59+
60+
export const useTheme = () => {
61+
const ctx = useContext(ThemeContext);
62+
if (!ctx) throw new Error("useTheme must be used within ThemeProvider");
63+
return ctx;
64+
};

frontend/src/main.tsx

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -3,11 +3,14 @@ import {createRoot} from 'react-dom/client'
33
import './index.css'
44
import {App} from './App.tsx'
55
import {AuthProvider} from "./contexts/AuthContext.tsx";
6+
import {ThemeProvider} from "./contexts/ThemeContext.tsx";
67

78
createRoot(document.getElementById('root')!).render(
89
<StrictMode>
910
<AuthProvider>
10-
<App/>
11+
<ThemeProvider>
12+
<App/>
13+
</ThemeProvider>
1114
</AuthProvider>
1215
</StrictMode>
1316
)

0 commit comments

Comments
 (0)