Skip to content

Commit 4f39c7c

Browse files
committed
Implement dark mode
Resolves #206
1 parent d40b776 commit 4f39c7c

10 files changed

+69
-29
lines changed

web/src/components/Account.jsx

+5-1
Original file line numberDiff line numberDiff line change
@@ -33,6 +33,7 @@ import {
3333
IconButton,
3434
MenuItem,
3535
DialogContentText,
36+
useTheme,
3637
} from "@mui/material";
3738
import EditIcon from "@mui/icons-material/Edit";
3839
import { Trans, useTranslation } from "react-i18next";
@@ -55,7 +56,6 @@ import DialogFooter from "./DialogFooter";
5556
import { Paragraph } from "./styles";
5657
import { IncorrectPasswordError, UnauthorizedError } from "../app/errors";
5758
import { ProChip } from "./SubscriptionPopup";
58-
import theme from "./theme";
5959
import session from "../app/Session";
6060

6161
const Account = () => {
@@ -147,6 +147,7 @@ const ChangePassword = () => {
147147
};
148148

149149
const ChangePasswordDialog = (props) => {
150+
const theme = useTheme();
150151
const { t } = useTranslation();
151152
const [error, setError] = useState("");
152153
const [currentPassword, setCurrentPassword] = useState("");
@@ -430,6 +431,7 @@ const PhoneNumbers = () => {
430431
};
431432

432433
const AddPhoneNumberDialog = (props) => {
434+
const theme = useTheme();
433435
const { t } = useTranslation();
434436
const [error, setError] = useState("");
435437
const [phoneNumber, setPhoneNumber] = useState("");
@@ -928,6 +930,7 @@ const TokensTable = (props) => {
928930
};
929931

930932
const TokenDialog = (props) => {
933+
const theme = useTheme();
931934
const { t } = useTranslation();
932935
const [error, setError] = useState("");
933936
const [label, setLabel] = useState(props.token?.label || "");
@@ -1069,6 +1072,7 @@ const DeleteAccount = () => {
10691072
};
10701073

10711074
const DeleteAccountDialog = (props) => {
1075+
const theme = useTheme();
10721076
const { t } = useTranslation();
10731077
const { account } = useContext(AccountContext);
10741078
const [error, setError] = useState("");

web/src/components/App.jsx

+16-3
Original file line numberDiff line numberDiff line change
@@ -1,11 +1,11 @@
11
import * as React from "react";
22
import { createContext, Suspense, useContext, useEffect, useState, useMemo } from "react";
3-
import { Box, Toolbar, CssBaseline, Backdrop, CircularProgress } from "@mui/material";
4-
import { ThemeProvider } from "@mui/material/styles";
3+
import { Box, Toolbar, CssBaseline, Backdrop, CircularProgress, useMediaQuery } from "@mui/material";
4+
import { ThemeProvider, createTheme } from "@mui/material/styles";
55
import { useLiveQuery } from "dexie-react-hooks";
66
import { BrowserRouter, Outlet, Route, Routes, useParams } from "react-router-dom";
77
import { AllSubscriptions, SingleSubscription } from "./Notifications";
8-
import theme from "./theme";
8+
import themeOptions, { darkPalette, lightPalette } from "./theme";
99
import Navigation from "./Navigation";
1010
import ActionBar from "./ActionBar";
1111
import notifier from "../app/Notifier";
@@ -29,6 +29,19 @@ const App = () => {
2929
const [account, setAccount] = useState(null);
3030
const accountMemo = useMemo(() => ({ account, setAccount }), [account, setAccount]);
3131

32+
const prefersDarkMode = useMediaQuery("(prefers-color-scheme: dark)");
33+
34+
const theme = React.useMemo(
35+
() =>
36+
createTheme({
37+
...themeOptions,
38+
palette: {
39+
...(prefersDarkMode ? darkPalette : lightPalette),
40+
},
41+
}),
42+
[prefersDarkMode]
43+
);
44+
3245
return (
3346
<Suspense fallback={<Loader />}>
3447
<BrowserRouter>

web/src/components/Preferences.jsx

+2-1
Original file line numberDiff line numberDiff line change
@@ -26,6 +26,7 @@ import {
2626
DialogTitle,
2727
DialogContent,
2828
DialogActions,
29+
useTheme,
2930
} from "@mui/material";
3031
import EditIcon from "@mui/icons-material/Edit";
3132
import CloseIcon from "@mui/icons-material/Close";
@@ -34,7 +35,6 @@ import { useLiveQuery } from "dexie-react-hooks";
3435
import { useTranslation } from "react-i18next";
3536
import { Info } from "@mui/icons-material";
3637
import { useOutletContext } from "react-router-dom";
37-
import theme from "./theme";
3838
import userManager from "../app/UserManager";
3939
import { playSound, shortUrl, shuffle, sounds, validUrl } from "../app/utils";
4040
import session from "../app/Session";
@@ -400,6 +400,7 @@ const UserTable = (props) => {
400400
};
401401

402402
const UserDialog = (props) => {
403+
const theme = useTheme();
403404
const { t } = useTranslation();
404405
const [baseUrl, setBaseUrl] = useState("");
405406
const [username, setUsername] = useState("");

web/src/components/PublishDialog.jsx

+3-1
Original file line numberDiff line numberDiff line change
@@ -19,6 +19,7 @@ import {
1919
IconButton,
2020
MenuItem,
2121
Box,
22+
useTheme,
2223
} from "@mui/material";
2324
import InsertEmoticonIcon from "@mui/icons-material/InsertEmoticon";
2425
import { Close } from "@mui/icons-material";
@@ -34,14 +35,14 @@ import DialogFooter from "./DialogFooter";
3435
import api from "../app/Api";
3536
import userManager from "../app/UserManager";
3637
import EmojiPicker from "./EmojiPicker";
37-
import theme from "./theme";
3838
import session from "../app/Session";
3939
import routes from "./routes";
4040
import accountApi from "../app/AccountApi";
4141
import { UnauthorizedError } from "../app/errors";
4242
import { AccountContext } from "./App";
4343

4444
const PublishDialog = (props) => {
45+
const theme = useTheme();
4546
const { t } = useTranslation();
4647
const { account } = useContext(AccountContext);
4748
const [baseUrl, setBaseUrl] = useState("");
@@ -806,6 +807,7 @@ const AttachmentBox = (props) => {
806807
};
807808

808809
const ExpandingTextField = (props) => {
810+
const theme = useTheme();
809811
const invisibleFieldRef = useRef();
810812
const [textWidth, setTextWidth] = useState(props.minWidth);
811813
const determineTextWidth = () => {

web/src/components/ReserveDialogs.jsx

+4-1
Original file line numberDiff line numberDiff line change
@@ -14,10 +14,10 @@ import {
1414
MenuItem,
1515
ListItemIcon,
1616
ListItemText,
17+
useTheme,
1718
} from "@mui/material";
1819
import { useTranslation } from "react-i18next";
1920
import { Check, DeleteForever } from "@mui/icons-material";
20-
import theme from "./theme";
2121
import { validTopic } from "../app/utils";
2222
import DialogFooter from "./DialogFooter";
2323
import session from "../app/Session";
@@ -27,6 +27,7 @@ import ReserveTopicSelect from "./ReserveTopicSelect";
2727
import { TopicReservedError, UnauthorizedError } from "../app/errors";
2828

2929
export const ReserveAddDialog = (props) => {
30+
const theme = useTheme();
3031
const { t } = useTranslation();
3132
const [error, setError] = useState("");
3233
const [topic, setTopic] = useState(props.topic || "");
@@ -87,6 +88,7 @@ export const ReserveAddDialog = (props) => {
8788
};
8889

8990
export const ReserveEditDialog = (props) => {
91+
const theme = useTheme();
9092
const { t } = useTranslation();
9193
const [error, setError] = useState("");
9294
const [everyone, setEveryone] = useState(props.reservation?.everyone || Permission.DENY_ALL);
@@ -124,6 +126,7 @@ export const ReserveEditDialog = (props) => {
124126
};
125127

126128
export const ReserveDeleteDialog = (props) => {
129+
const theme = useTheme();
127130
const { t } = useTranslation();
128131
const [error, setError] = useState("");
129132
const [deleteMessages, setDeleteMessages] = useState(false);

web/src/components/SubscribeDialog.jsx

+2-1
Original file line numberDiff line numberDiff line change
@@ -12,10 +12,10 @@ import {
1212
FormGroup,
1313
useMediaQuery,
1414
Switch,
15+
useTheme,
1516
} from "@mui/material";
1617
import { useTranslation } from "react-i18next";
1718
import { useLiveQuery } from "dexie-react-hooks";
18-
import theme from "./theme";
1919
import api from "../app/Api";
2020
import { randomAlphanumericString, topicUrl, validTopic, validUrl } from "../app/utils";
2121
import userManager from "../app/UserManager";
@@ -49,6 +49,7 @@ export const subscribeTopic = async (baseUrl, topic, opts) => {
4949
};
5050

5151
const SubscribeDialog = (props) => {
52+
const theme = useTheme();
5253
const [baseUrl, setBaseUrl] = useState("");
5354
const [topic, setTopic] = useState("");
5455
const [showLoginPage, setShowLoginPage] = useState(false);

web/src/components/SubscriptionPopup.jsx

+2-1
Original file line numberDiff line numberDiff line change
@@ -15,6 +15,7 @@ import {
1515
MenuItem,
1616
IconButton,
1717
ListItemIcon,
18+
useTheme,
1819
} from "@mui/material";
1920
import { useTranslation } from "react-i18next";
2021
import { useNavigate } from "react-router-dom";
@@ -30,7 +31,6 @@ import {
3031
RemoveCircle,
3132
Send,
3233
} from "@mui/icons-material";
33-
import theme from "./theme";
3434
import subscriptionManager from "../app/SubscriptionManager";
3535
import DialogFooter from "./DialogFooter";
3636
import accountApi, { Role } from "../app/AccountApi";
@@ -281,6 +281,7 @@ export const SubscriptionPopup = (props) => {
281281
};
282282

283283
const DisplayNameDialog = (props) => {
284+
const theme = useTheme();
284285
const { t } = useTranslation();
285286
const { subscription } = props;
286287
const [error, setError] = useState("");

web/src/components/UpgradeDialog.jsx

+2-1
Original file line numberDiff line numberDiff line change
@@ -21,6 +21,7 @@ import {
2121
Box,
2222
DialogContentText,
2323
DialogActions,
24+
useTheme,
2425
} from "@mui/material";
2526
import { Trans, useTranslation } from "react-i18next";
2627
import { Check, Close } from "@mui/icons-material";
@@ -31,7 +32,6 @@ import { AccountContext } from "./App";
3132
import routes from "./routes";
3233
import session from "../app/Session";
3334
import accountApi, { SubscriptionInterval } from "../app/AccountApi";
34-
import theme from "./theme";
3535

3636
const Feature = (props) => <FeatureItem feature>{props.children}</FeatureItem>;
3737

@@ -61,6 +61,7 @@ const Banner = {
6161
};
6262

6363
const UpgradeDialog = (props) => {
64+
const theme = useTheme();
6465
const { t } = useTranslation();
6566
const { account } = useContext(AccountContext); // May be undefined!
6667
const [error, setError] = useState("");

web/src/components/styles.js

+2-3
Original file line numberDiff line numberDiff line change
@@ -1,19 +1,18 @@
11
import { Typography, Container, Backdrop, styled } from "@mui/material";
2-
import theme from "./theme";
32

43
export const Paragraph = styled(Typography)({
54
paddingTop: 8,
65
paddingBottom: 8,
76
});
87

9-
export const VerticallyCenteredContainer = styled(Container)({
8+
export const VerticallyCenteredContainer = styled(Container)(({ theme }) => ({
109
display: "flex",
1110
flexGrow: 1,
1211
flexDirection: "column",
1312
justifyContent: "center",
1413
alignContent: "center",
1514
color: theme.palette.text.primary,
16-
});
15+
}));
1716

1817
export const LightboxBackdrop = styled(Backdrop)({
1918
backgroundColor: "rgba(0, 0, 0, 0.8)", // was: 0.5

web/src/components/theme.js

+31-16
Original file line numberDiff line numberDiff line change
@@ -1,18 +1,7 @@
1-
import { red } from "@mui/material/colors";
2-
import { createTheme } from "@mui/material/styles";
1+
import { grey, red } from "@mui/material/colors";
32

4-
const theme = createTheme({
5-
palette: {
6-
primary: {
7-
main: "#338574",
8-
},
9-
secondary: {
10-
main: "#6cead0",
11-
},
12-
error: {
13-
main: red.A400,
14-
},
15-
},
3+
/** @type {import("@mui/material").ThemeOptions} */
4+
const themeOptions = {
165
components: {
176
MuiListItemIcon: {
187
styleOverrides: {
@@ -31,6 +20,32 @@ const theme = createTheme({
3120
},
3221
},
3322
},
34-
});
23+
};
24+
25+
/** @type {import("@mui/material").ThemeOptions['palette']} */
26+
export const lightPalette = {
27+
mode: "light",
28+
primary: {
29+
main: "#338574",
30+
},
31+
secondary: {
32+
main: "#6cead0",
33+
},
34+
error: {
35+
main: red.A400,
36+
},
37+
};
38+
39+
/** @type {import("@mui/material").ThemeOptions['palette']} */
40+
export const darkPalette = {
41+
...lightPalette,
42+
mode: "dark",
43+
background: {
44+
paper: grey["800"],
45+
},
46+
primary: {
47+
main: "#6cead0",
48+
},
49+
};
3550

36-
export default theme;
51+
export default themeOptions;

0 commit comments

Comments
 (0)