feat: light/dark theme + app shell (sidebar, topbar, auth, login page)

This commit is contained in:
asepharyana committed 2026-09-15 16:35:03 +07:00
1 parent 1881e7c6b1
commit ff156112e7
5 files changed
+309 -40

No files matched your search

+49
View File
@@ -0,0 +1,49 @@
import { createResource, createSignal, Show } from "solid-js";
import { api, type AuthUser } from "../lib/api";
// AuthState: loads /api/auth/me once; exposes login/logout + theme toggle.
function theme(): string {
return localStorage.getItem("fs-theme") ||
(matchMedia("(prefers-color-scheme: light)").matches ? "light" : "dark");
}
export function applyTheme(t: string) {
document.documentElement.dataset.theme = t;
localStorage.setItem("fs-theme", t);
}
applyTheme(theme());
export function ThemeToggle() {
const [t, setT] = createSignal(theme());
return (
<button class="fs-iconbtn" title="Toggle light/dark" onClick={() => {
const n = t() === "dark" ? "light" : "dark";
setT(n); applyTheme(n);
}}>
{t() === "dark" ? "☀️" : "🌙"}
</button>
);
}
export function useAuth() {
const [me, { refetch }] = createResource(async (): Promise<AuthUser | null> => {
try { return (await api.me()).user; } catch { return null; }
});
return { me, refetch };
}
export function AuthButton(props: { me: AuthUser | null | undefined; onLogout: () => void }) {
return (
<Show when={props.me} fallback={
<a class="fs-btn" href="/api/auth/start">Sign in with Google</a>
}>
{(u) => (
<span class="fs-user">
{u().avatar_url ? <img class="fs-avatar" src={u().avatar_url} alt="" /> : null}
{u().name || u().email}
<button class="fs-iconbtn" onClick={props.onLogout}>Logout</button>
</span>
)}
</Show>
);
}