diff --git a/website/src/App.tsx b/website/src/App.tsx index d3f98fa4..f59434f0 100644 --- a/website/src/App.tsx +++ b/website/src/App.tsx @@ -8,6 +8,7 @@ import { grpc } from './Api'; import { AppState } from './AppState'; import { YourDevices } from './pages/YourDevices'; import { AllDevices } from './pages/admin/AllDevices'; +import { ThemeProvider, createTheme } from '@mui/material/styles'; export const App = observer(class App extends React.Component { async componentDidMount() { @@ -18,20 +19,29 @@ export const App = observer(class App extends React.Component { if (!AppState.info) { return

loading...

; } + + const darkLightTheme = createTheme({ + palette: { + mode: AppState.darkMode ? 'dark' : 'light', + }, + }); + return ( - - - - - } /> - {AppState.info.isAdmin && ( - <> - } /> - - )} - - + + + + + + } /> + {AppState.info.isAdmin && ( + <> + } /> + + )} + + + ); } diff --git a/website/src/AppState.ts b/website/src/AppState.ts index c7ef1f73..fb4d7220 100644 --- a/website/src/AppState.ts +++ b/website/src/AppState.ts @@ -1,13 +1,24 @@ -import { observable, makeObservable } from 'mobx'; +import {observable, makeObservable, runInAction} from 'mobx'; import { InfoRes } from './sdk/server_pb'; class GlobalAppState { info?: InfoRes.AsObject; + darkMode: boolean; constructor() { makeObservable(this, { - info: observable + info: observable, + darkMode: observable }); + + this.darkMode = false; + } + + setDarkMode(darkMode: boolean) { + runInAction(() => { + this.darkMode = darkMode; + }) + } } diff --git a/website/src/components/Navigation.tsx b/website/src/components/Navigation.tsx index 900e4bfb..6256f4c9 100644 --- a/website/src/components/Navigation.tsx +++ b/website/src/components/Navigation.tsx @@ -1,4 +1,4 @@ -import React from 'react'; +import React, {useEffect} from 'react'; import makeStyles from '@mui/styles/makeStyles'; import { getCookie } from '../Cookies'; import { AppState } from '../AppState'; @@ -10,6 +10,10 @@ import Link from '@mui/material/Link'; import Button from '@mui/material/Button'; import Chip from '@mui/material/Chip'; import VpnKey from '@mui/icons-material/VpnKey'; +import IconButton from "@mui/material/IconButton"; +import Brightness4Icon from '@mui/icons-material/Brightness4'; +import Brightness7Icon from '@mui/icons-material/Brightness7'; +import {useMediaQuery} from "@mui/material"; const useStyles = makeStyles((theme) => ({ title: { @@ -38,6 +42,8 @@ export default function Navigation() { )} + + {AppState.info?.isAdmin && ( @@ -53,3 +59,39 @@ export default function Navigation() { ); } + +function DarkModeToggle() { + + const CUSTOM_DARK_MODE_KEY = "customDarkMode"; + const prefersDarkMode = useMediaQuery('(prefers-color-scheme: dark)'); + + useEffect(()=>{ + let customDarkMode = localStorage.getItem(CUSTOM_DARK_MODE_KEY); + if (customDarkMode) { + AppState.setDarkMode(JSON.parse(customDarkMode)); + } + else { + AppState.setDarkMode(prefersDarkMode); + } + + },[prefersDarkMode]); + + function toggleDarkMode() { + AppState.setDarkMode(!AppState.darkMode); + + // We only persist the preference in the local storage if it is different to the OS setting. + if (prefersDarkMode !== AppState.darkMode) { + localStorage.setItem(CUSTOM_DARK_MODE_KEY, JSON.stringify(AppState.darkMode)); + } + else { + localStorage.removeItem(CUSTOM_DARK_MODE_KEY); + } + } + + return ( + + {AppState.darkMode ? : } + + ); + +} diff --git a/website/src/components/Present.tsx b/website/src/components/Present.tsx index 5795b38e..14683593 100644 --- a/website/src/components/Present.tsx +++ b/website/src/components/Present.tsx @@ -4,6 +4,8 @@ import DialogActions from '@mui/material/DialogActions'; import DialogContent from '@mui/material/DialogContent'; import DialogContentText from '@mui/material/DialogContentText'; import DialogTitle from '@mui/material/DialogTitle'; +import { ThemeProvider, createTheme } from '@mui/material/styles'; +import { AppState } from '../AppState'; import React from 'react'; import { render, unmountComponentAtNode } from 'react-dom'; @@ -20,7 +22,14 @@ export function present(content: (close: (result: T) => void) => React.ReactN } export function confirm(msg: string): Promise { + const darkLightTheme = createTheme({ + palette: { + mode: AppState.darkMode ? 'dark' : 'light', + }, + }); + return present((close) => ( + close(false)}> Confirm @@ -35,5 +44,6 @@ export function confirm(msg: string): Promise { + )); }