index.tsx

 1import 'fontsource-roboto/300.css';
 2import 'fontsource-roboto/400.css';
 3import 'fontsource-roboto/500.css';
 4import 'fontsource-roboto/700.css';
 5
 6import { createMuiTheme, CssBaseline, IconButton, responsiveFontSizes, ThemeProvider } from '@material-ui/core';
 7import { Brightness4, Brightness7 } from '@material-ui/icons';
 8import { useLocalStorage } from '@rehooks/local-storage';
 9import * as React from 'react';
10import * as ReactDOM from 'react-dom';
11
12import { App } from './app';
13import { AboutButton } from './components/about';
14import * as serviceWorker from './serviceWorker';
15
16function useTheme() {
17    const [themeName, setThemeName] = useLocalStorage<'light' | 'dark'>('themeName', 'dark');
18
19    // Workaround for https://github.com/mui-org/material-ui/issues/20708.
20    //
21    // When in strict mode (development only), this is required to properly allow
22    // the theme to be changed, as unused styles are not cleaned up. Create a new theme
23    // each time, so that they're forced to be injected again at the end of the existing
24    // block of stylesheets (as the styling library will see them as "new" styles, rather than
25    // assuming they can just be reused).
26    //
27    // This is gross, as it means every time the button is clicked it's a slew of extra
28    // stylesheets (each overriding the previous), but in production the cleanup works
29    // so this extra work is "only" a performance hit. If the bug is ever fixed, we can
30    // simply store two global themes and swap between them.
31    const theme = responsiveFontSizes(
32        createMuiTheme({
33            palette: {
34                type: themeName,
35            },
36        })
37    );
38
39    const toggleTheme = React.useMemo(() => {
40        if (themeName === 'light') {
41            return () => setThemeName('dark');
42        } else {
43            return () => setThemeName('light');
44        }
45    }, [themeName, setThemeName]);
46
47    return { theme, toggleTheme, isDark: themeName === 'dark' };
48}
49
50const Root = (_props: {}) => {
51    const { theme, toggleTheme, isDark } = useTheme();
52
53    return (
54        <React.StrictMode>
55            <ThemeProvider theme={theme}>
56                <CssBaseline />
57                <div
58                    style={{
59                        position: 'absolute',
60                        top: 0,
61                        right: 0,
62                        margin: '0.5rem',
63                    }}
64                >
65                    <AboutButton style={{ marginRight: '0.5rem' }} />
66                    <IconButton size="small" onClick={toggleTheme}>
67                        {isDark ? <Brightness7 /> : <Brightness4 />}
68                    </IconButton>
69                </div>
70                <App />
71            </ThemeProvider>
72        </React.StrictMode>
73    );
74};
75
76ReactDOM.render(<Root />, document.getElementById('root'));
77
78// If you want your app to work offline and load faster, you can change
79// unregister() to register() below. Note this comes with some pitfalls.
80// Learn more about service workers: https://bit.ly/CRA-PWA
81serviceWorker.unregister();
82
83// TODO: Try using a service worker to aid in reloading when needed.
84// let refreshing = false;
85// serviceWorker.register({
86//     onUpdate: () => {
87//         if (!refreshing) {
88//             refreshing = true;
89//             window.location.reload();
90//         }
91//     }
92// });