Can toggle dark theme

This commit is contained in:
Pierre HUBERT 2023-07-08 16:54:26 +02:00
parent f93bc704a5
commit 099e517688
15 changed files with 134 additions and 63 deletions

View File

@ -1,4 +1,3 @@
import { ThemeProvider, createTheme } from "@mui/material/styles";
import React from "react";
import { Route, Routes } from "react-router-dom";
import "./App.css";
@ -24,12 +23,6 @@ interface AuthContext {
const AuthContextK = React.createContext<AuthContext | null>(null);
const darkTheme = createTheme({
palette: {
mode: "dark", // TODO : switch back to light
},
});
/**
* Core app
*/
@ -42,37 +35,35 @@ export function App(): React.ReactElement {
};
return (
<ThemeProvider theme={darkTheme}>
<AuthContextK.Provider value={context}>
<Routes>
<Route path="delete_account" element={<DeleteAccountRoute />} />
<AuthContextK.Provider value={context}>
<Routes>
<Route path="delete_account" element={<DeleteAccountRoute />} />
{signedIn ? (
<Route path="*" element={<BaseAuthenticatedPage />}>
<Route path="" element={<FamiliesListRoute />} />
<Route path="profile" element={<ProfileRoute />} />
<Route path="family/:familyId/*" element={<BaseFamilyRoute />}>
<Route path="" element={<FamilyHomeRoute />} />
<Route path="*" element={<NotFoundRoute />} />
</Route>
{signedIn ? (
<Route path="*" element={<BaseAuthenticatedPage />}>
<Route path="" element={<FamiliesListRoute />} />
<Route path="profile" element={<ProfileRoute />} />
<Route path="family/:familyId/*" element={<BaseFamilyRoute />}>
<Route path="" element={<FamilyHomeRoute />} />
<Route path="*" element={<NotFoundRoute />} />
</Route>
) : (
<Route path="*" element={<BaseLoginPage />}>
<Route path="" element={<LoginRoute />} />
<Route path="oidc_cb" element={<OIDCCbRoute />} />
<Route path="new-account" element={<NewAccountRoute />} />
<Route
path="password_forgotten"
element={<PasswordForgottenRoute />}
/>
<Route path="reset_password" element={<ResetPasswordRoute />} />
<Route path="*" element={<NotFoundRoute />} />
</Route>
)}
</Routes>
</AuthContextK.Provider>
</ThemeProvider>
<Route path="*" element={<NotFoundRoute />} />
</Route>
) : (
<Route path="*" element={<BaseLoginPage />}>
<Route path="" element={<LoginRoute />} />
<Route path="oidc_cb" element={<OIDCCbRoute />} />
<Route path="new-account" element={<NewAccountRoute />} />
<Route
path="password_forgotten"
element={<PasswordForgottenRoute />}
/>
<Route path="reset_password" element={<ResetPasswordRoute />} />
<Route path="*" element={<NotFoundRoute />} />
</Route>
)}
</Routes>
</AuthContextK.Provider>
);
}

View File

@ -0,0 +1,50 @@
import { ThemeProvider, createTheme } from "@mui/material/styles";
import React from "react";
import { PropsWithChildren } from "react";
const localStorageKey = "dark-theme";
const darkTheme = createTheme({
palette: {
mode: "dark",
},
});
const lightTheme = createTheme({
palette: {
mode: "light",
},
});
interface DarkThemeContext {
enabled: boolean;
setEnabled: (enabled: boolean) => void;
}
const DarkThemeContextK = React.createContext<DarkThemeContext | null>(null);
export function DarkThemeProvider(p: PropsWithChildren): React.ReactElement {
const [enabled, setEnabled] = React.useState(
localStorage.getItem(localStorageKey) === "true"
);
return (
<DarkThemeContextK.Provider
value={{
enabled: enabled,
setEnabled(enabled) {
localStorage.setItem(localStorageKey, enabled ? "true" : "false");
setEnabled(enabled);
},
}}
>
<ThemeProvider theme={enabled ? darkTheme : lightTheme}>
{p.children}
</ThemeProvider>
</DarkThemeContextK.Provider>
);
}
export function useDarkTheme(): DarkThemeContext {
return React.useContext(DarkThemeContextK)!;
}

View File

@ -2,7 +2,7 @@ import React from "react";
import { TextInputDialog } from "./TextInputDialog";
import { ServerApi } from "../api/ServerApi";
import { FamilyApi } from "../api/FamilyApi";
import { useAlert } from "../widgets/AlertDialogProvider";
import { useAlert } from "../context_providers/AlertDialogProvider";
export function CreateFamilyDialog(p: {
open: boolean;

View File

@ -2,7 +2,7 @@ import React from "react";
import { TextInputDialog } from "./TextInputDialog";
import { ServerApi } from "../api/ServerApi";
import { FamilyApi, JoinFamilyResult } from "../api/FamilyApi";
import { useAlert } from "../widgets/AlertDialogProvider";
import { useAlert } from "../context_providers/AlertDialogProvider";
export function JoinFamilyDialog(p: {
open: boolean;

View File

@ -11,10 +11,11 @@ import "@fontsource/roboto/400.css";
import "@fontsource/roboto/500.css";
import "@fontsource/roboto/700.css";
import { BrowserRouter } from "react-router-dom";
import { ConfirmDialogProvider } from "./widgets/ConfirmDialogProvider";
import { AlertDialogProvider } from "./widgets/AlertDialogProvider";
import { ConfirmDialogProvider } from "./context_providers/ConfirmDialogProvider";
import { AlertDialogProvider } from "./context_providers/AlertDialogProvider";
import { AsyncWidget } from "./widgets/AsyncWidget";
import { SnackbarProvider } from "./widgets/SnackbarProvider";
import { SnackbarProvider } from "./context_providers/SnackbarProvider";
import { DarkThemeProvider } from "./context_providers/DarkThemeProvider";
async function init() {
try {
@ -25,20 +26,22 @@ async function init() {
root.render(
<React.StrictMode>
<BrowserRouter>
<AlertDialogProvider>
<ConfirmDialogProvider>
<SnackbarProvider>
<div style={{ height: "100vh" }}>
<AsyncWidget
loadKey={1}
load={async () => await ServerApi.LoadConfig()}
errMsg="Echec de la connexion au serveur pour la récupération de la configuration statique !"
build={() => <App />}
/>
</div>
</SnackbarProvider>
</ConfirmDialogProvider>
</AlertDialogProvider>
<DarkThemeProvider>
<AlertDialogProvider>
<ConfirmDialogProvider>
<SnackbarProvider>
<div style={{ height: "100vh" }}>
<AsyncWidget
loadKey={1}
load={async () => await ServerApi.LoadConfig()}
errMsg="Echec de la connexion au serveur pour la récupération de la configuration statique !"
build={() => <App />}
/>
</div>
</SnackbarProvider>
</ConfirmDialogProvider>
</AlertDialogProvider>
</DarkThemeProvider>
</BrowserRouter>
</React.StrictMode>
);

View File

@ -3,9 +3,9 @@ import React from "react";
import { Link, useLocation, useNavigate } from "react-router-dom";
import { AuthApi } from "../api/AuthApi";
import { DeleteAccountTokenInfo, UserApi } from "../api/UserApi";
import { useAlert } from "../widgets/AlertDialogProvider";
import { useAlert } from "../context_providers/AlertDialogProvider";
import { AsyncWidget } from "../widgets/AsyncWidget";
import { useConfirm } from "../widgets/ConfirmDialogProvider";
import { useConfirm } from "../context_providers/ConfirmDialogProvider";
export function DeleteAccountRoute(): React.ReactElement {
const alert = useAlert();

View File

@ -14,9 +14,9 @@ import React from "react";
import { Family, FamilyApi } from "../api/FamilyApi";
import { CreateFamilyDialog } from "../dialogs/CreateFamilyDialog";
import { JoinFamilyDialog } from "../dialogs/JoinFamilyDialog";
import { useAlert } from "../widgets/AlertDialogProvider";
import { useAlert } from "../context_providers/AlertDialogProvider";
import { AsyncWidget } from "../widgets/AsyncWidget";
import { useConfirm } from "../widgets/ConfirmDialogProvider";
import { useConfirm } from "../context_providers/ConfirmDialogProvider";
import { RouterLink } from "../widgets/RouterLink";
import { TimeWidget } from "../widgets/TimeWidget";

View File

@ -13,9 +13,9 @@ import {
import React from "react";
import { ServerApi } from "../api/ServerApi";
import { ReplacePasswordResponse, User, UserApi } from "../api/UserApi";
import { useAlert } from "../widgets/AlertDialogProvider";
import { useAlert } from "../context_providers/AlertDialogProvider";
import { useUser } from "../widgets/BaseAuthenticatedPage";
import { useConfirm } from "../widgets/ConfirmDialogProvider";
import { useConfirm } from "../context_providers/ConfirmDialogProvider";
import { PasswordInput } from "../widgets/PasswordInput";
import { formatDate } from "../widgets/TimeWidget";

View File

@ -14,6 +14,7 @@ import { AuthApi } from "../api/AuthApi";
import { User, UserApi } from "../api/UserApi";
import { AsyncWidget } from "./AsyncWidget";
import { RouterLink } from "./RouterLink";
import { DarkThemeButton } from "./DarkThemeButton";
interface UserContext {
user: User;
@ -90,6 +91,8 @@ export function BaseAuthenticatedPage(): React.ReactElement {
</Typography>
<div>
<DarkThemeButton />
<Button size="large" color="inherit">
{user!.name}
</Button>

View File

@ -27,9 +27,9 @@ import { Outlet, useLocation, useParams } from "react-router-dom";
import { Family, FamilyApi } from "../api/FamilyApi";
import { AsyncWidget } from "./AsyncWidget";
import { RouterLink } from "./RouterLink";
import { useSnackbar } from "./SnackbarProvider";
import { useConfirm } from "./ConfirmDialogProvider";
import { useAlert } from "./AlertDialogProvider";
import { useSnackbar } from "../context_providers/SnackbarProvider";
import { useConfirm } from "../context_providers/ConfirmDialogProvider";
import { useAlert } from "../context_providers/AlertDialogProvider";
interface FamilyContext {
family: Family;

View File

@ -8,6 +8,7 @@ import Paper from "@mui/material/Paper";
import Typography from "@mui/material/Typography";
import * as React from "react";
import { Link, Outlet } from "react-router-dom";
import { DarkThemeButton } from "./DarkThemeButton";
function Copyright(props: any) {
return (
@ -64,6 +65,10 @@ export function BaseLoginPage() {
alignItems: "center",
}}
>
<div style={{ position: "absolute", right: "10px", top: "5px" }}>
<DarkThemeButton />
</div>
<Avatar sx={{ m: 1, bgcolor: "secondary.main" }}>
<Icon path={mdiFamilyTree} size={1} />
</Avatar>

View File

@ -0,0 +1,19 @@
import Brightness7Icon from "@mui/icons-material/Brightness7";
import DarkModeIcon from "@mui/icons-material/DarkMode";
import { IconButton, Tooltip } from "@mui/material";
import { useDarkTheme } from "../context_providers/DarkThemeProvider";
export function DarkThemeButton(): React.ReactElement {
const darkTheme = useDarkTheme();
return (
<Tooltip title="Activer / désactiver le mode sombre">
<IconButton
onClick={() => darkTheme.setEnabled(!darkTheme.enabled)}
style={{ color: "inherit" }}
>
{!darkTheme.enabled ? <DarkModeIcon /> : <Brightness7Icon />}
</IconButton>
</Tooltip>
);
}