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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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