Skip to content

Commit

Permalink
add dark mode toggle (#7)
Browse files Browse the repository at this point in the history
Co-authored-by: lukqw <[email protected]>
  • Loading branch information
lukqw and lukqw committed Nov 23, 2023
1 parent 3f608fe commit 823d83a
Show file tree
Hide file tree
Showing 6 changed files with 114 additions and 28 deletions.
37 changes: 23 additions & 14 deletions packages/frontend/src/Routes.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -7,20 +7,29 @@ import ShowNote from "./content/ShowNote";
import NotFound from "./content/NotFound";

import { BASE_URL } from "./config";
import { useThemeContext } from "./hooks/useThemeContext";

const Routes = () => (
<div className="mt-md-4 d-flex flex-column justify-content-center">
<Suspense fallback={<div>Loading...</div>}>
<BrowserRouter basename={BASE_URL}>
<RouterRoutes>
<Route path="/" element={<ListNotes/>} />
<Route path="/note/new" element={<CreateNote/>} />
<Route path="/notes/:noteId" element={<ShowNote/>} />
<Route element={<NotFound/>} />
</RouterRoutes>
</BrowserRouter>
</Suspense>
</div>
);
const Routes = () => {
const { darkMode } = useThemeContext();

return (
<div className={`${darkMode ? 'bg-dark' : ''}`} style={{ height: "100%", overflowY: 'hidden', minHeight: '100vh' }}>
<div className="container" >
<div className="mt-md-4 d-flex flex-column justify-content-center">
<Suspense fallback={<div>Loading...</div>}>
<BrowserRouter basename={BASE_URL}>
<RouterRoutes>
<Route path="/" element={<ListNotes/>} />
<Route path="/note/new" element={<CreateNote/>} />
<Route path="/notes/:noteId" element={<ShowNote/>} />
<Route element={<NotFound/>} />
</RouterRoutes>
</BrowserRouter>
</Suspense>
</div>
</div>
</div>
);
}

export { Routes };
18 changes: 12 additions & 6 deletions packages/frontend/src/components/PageContainer.tsx
Original file line number Diff line number Diff line change
@@ -1,14 +1,20 @@
import React from "react";
import { Card } from "react-bootstrap";
import { ThemeSwitch } from "../content/ThemeSwitch";

const PageContainer = (props: {
header: React.ReactNode;
children: React.ReactNode;
}) => (
<Card>
<Card.Header>{props.header}</Card.Header>
<Card.Body>{props.children}</Card.Body>
</Card>
);
}) => {
return (
<>
<Card>
<Card.Header>{props.header}</Card.Header>
<Card.Body>{props.children}</Card.Body>
</Card>
<ThemeSwitch />
</>
)
};

export { PageContainer };
25 changes: 25 additions & 0 deletions packages/frontend/src/content/ThemeSwitch.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,25 @@
import React, { useState } from "react";
import { useThemeContext } from "../hooks/useThemeContext";
import { Form } from "react-bootstrap";


export const ThemeSwitch = () => {
const { darkMode, setDarkMode } = useThemeContext();

const toggleTheme = () => {
setDarkMode(!darkMode);
};

return (
<Form style={{paddingTop: '1rem', display: 'flex', justifyContent: 'flex-end'}}>
<Form.Check // prettier-ignore
type="switch"
onClick={toggleTheme}
id="custom-switch"
defaultChecked={darkMode}
style={{color: darkMode ? 'white' : ''}}
label="Dark Mode"
/>
</Form>
);
}
14 changes: 14 additions & 0 deletions packages/frontend/src/hooks/usePersistedState.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
import { useEffect, useState } from "react";

// to store the state to local storage
export default function usePersistedState(key: string, defaultValue: any) {
const [state, setState] = useState(() => {
return JSON.parse(localStorage.getItem(key) ?? '{}') || defaultValue;
});

useEffect(() => {
localStorage.setItem(key, JSON.stringify(state));
}, [key, state]);

return [state, setState];
}
32 changes: 32 additions & 0 deletions packages/frontend/src/hooks/useThemeContext.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,32 @@
import React from "react";
import { createContext, useContext, ReactElement } from "react";
import usePersistedState from "./usePersistedState";

type ThemeContextValue = {
darkMode: boolean;
setDarkMode: React.Dispatch<React.SetStateAction<boolean>>;
};

// register the context
const ThemeContext = createContext<ThemeContextValue>(undefined as any);

type Props = {
children: React.ReactNode;
};

export const ThemeProvider = ({ children }: Props): ReactElement => {
/** usePersistedState for storing state in local store */
const [darkMode, setDarkMode] = usePersistedState("darkmode", false);

return (
<ThemeContext.Provider value={{ darkMode, setDarkMode }}>
{children}
</ThemeContext.Provider>
);
}

// export a custom hook to use this specific context
export const useThemeContext = (): ThemeContextValue => {
return useContext(ThemeContext);
}

16 changes: 8 additions & 8 deletions packages/frontend/src/index.tsx
Original file line number Diff line number Diff line change
@@ -1,8 +1,9 @@
import { Buffer } from "buffer";
import process from "process";
import React from "react";
import { createRoot } from "react-dom/client";
import ReactDOM from "react-dom";
import { Routes } from "./Routes";
import { ThemeProvider } from "./hooks/useThemeContext";

// Polyfills required for MicrophoneStream
if (typeof (window as any).global === "undefined") {
Expand All @@ -15,11 +16,10 @@ if (typeof (window as any).process === "undefined") {
(window as any).process = process;
}

const container = document.getElementById("root");
// @ts-ignore
const root = createRoot(container);
root.render(
<div className="container" style={{ height: "100vh" }}>
<Routes />
</div>

ReactDOM.render(
<ThemeProvider>
<Routes />
</ThemeProvider>,
document.getElementById("root")
);

0 comments on commit 823d83a

Please sign in to comment.