Rename UI_MODE to THEME

pull/792/head v2.6.0
binwiederhier 2023-06-28 13:30:51 -04:00
parent e9f170a197
commit 64ac111d55
5 changed files with 40 additions and 36 deletions

View File

@ -7,15 +7,22 @@ Released June 28, 2023
With this release, the ntfy web app now contains a **[progressive web app](https://docs.ntfy.sh/subscribe/pwa/) (PWA) With this release, the ntfy web app now contains a **[progressive web app](https://docs.ntfy.sh/subscribe/pwa/) (PWA)
with Web Push support**, which means you'll be able to **install the ntfy web app on your desktop or phone** similar with Web Push support**, which means you'll be able to **install the ntfy web app on your desktop or phone** similar
to a native app (__even on iOS!__ 🥳), and get basic push notification support (without any battery drain). to a native app (__even on iOS!__ 🥳). Installing the PWA gives ntfy web its own launcher, a standalone window,
push notifications, and an app badge with the unread notification count.
Installing the PWA gives ntfy web its own launcher (e.g. shortcut on Windows, app on macOS, launcher shortcut on Linux, On top of that, this release also brings **dark mode** 🧛🌙 to the web app.
home screen icon on iOS, and launcher icon on Android), a standalone window, push notifications, and an app badge with
the unread notification count. 🙏 A huge thanks for this release goes to [@nimbleghost](https://github.com/nimbleghost), for basically implementing the
Web Push / PWA and dark mode feature by himself. I'm really grateful for your contributions.
❤️ If you like ntfy, **please consider sponsoring us** via [GitHub Sponsors](https://github.com/sponsors/binwiederhier)
and [Liberapay](https://en.liberapay.com/ntfy/), or buying a [paid plan via the web app](https://ntfy.sh/app) (20% off
if you use promo code `MYTOPIC`). ntfy will always remain open source.
**Features:** **Features:**
* The web app now supports Web Push, and is installable as a [progressive web app (PWA)](https://docs.ntfy.sh/subscribe/pwa/) on Chrome, Edge, Android, and iOS ([#751](https://github.com/binwiederhier/ntfy/pull/751), thanks to [@nimbleghost](https://github.com/nimbleghost)) * The web app now supports Web Push, and is installable as a [progressive web app (PWA)](https://docs.ntfy.sh/subscribe/pwa/) on Chrome, Edge, Android, and iOS ([#751](https://github.com/binwiederhier/ntfy/pull/751), thanks to [@nimbleghost](https://github.com/nimbleghost))
* Support for dark mode in the web app ([#206](https://github.com/binwiederhier/ntfy/issues/206), thanks to [@nimbleghost](https://github.com/nimbleghost))
**Bug fixes:** **Bug fixes:**

View File

@ -338,10 +338,6 @@
"prefs_notifications_web_push_disabled_description": "Notification are received when the web app is running (via WebSocket)", "prefs_notifications_web_push_disabled_description": "Notification are received when the web app is running (via WebSocket)",
"prefs_notifications_web_push_enabled": "Enabled for {{server}}", "prefs_notifications_web_push_enabled": "Enabled for {{server}}",
"prefs_notifications_web_push_disabled": "Disabled", "prefs_notifications_web_push_disabled": "Disabled",
"prefs_ui_mode_title": "UI Mode",
"prefs_ui_mode_system": "System (default)",
"prefs_ui_mode_dark": "Dark",
"prefs_ui_mode_light": "Light",
"prefs_users_title": "Manage users", "prefs_users_title": "Manage users",
"prefs_users_description": "Add/remove users for your protected topics here. Please note that username and password are stored in the browser's local storage.", "prefs_users_description": "Add/remove users for your protected topics here. Please note that username and password are stored in the browser's local storage.",
"prefs_users_description_no_sync": "Users and passwords are not synchronized to your account.", "prefs_users_description_no_sync": "Users and passwords are not synchronized to your account.",
@ -359,6 +355,10 @@
"prefs_users_dialog_password_label": "Password", "prefs_users_dialog_password_label": "Password",
"prefs_appearance_title": "Appearance", "prefs_appearance_title": "Appearance",
"prefs_appearance_language_title": "Language", "prefs_appearance_language_title": "Language",
"prefs_appearance_theme_title": "Theme",
"prefs_appearance_theme_system": "System (default)",
"prefs_appearance_theme_dark": "Dark mode",
"prefs_appearance_theme_light": "Light mode",
"prefs_reservations_title": "Reserved topics", "prefs_reservations_title": "Reserved topics",
"prefs_reservations_description": "You can reserve topic names for personal use here. Reserving a topic gives you ownership over the topic, and allows you to define access permissions for other users over the topic.", "prefs_reservations_description": "You can reserve topic names for personal use here. Reserving a topic gives you ownership over the topic, and allows you to define access permissions for other users over the topic.",
"prefs_reservations_limit_reached": "You reached your reserved topics limit.", "prefs_reservations_limit_reached": "You reached your reserved topics limit.",

View File

@ -1,6 +1,6 @@
import db from "./db"; import db from "./db";
export const UI_MODE = { export const THEME = {
DARK: "dark", DARK: "dark",
LIGHT: "light", LIGHT: "light",
SYSTEM: "system", SYSTEM: "system",
@ -47,13 +47,13 @@ class Prefs {
await this.db.prefs.put({ key: "webPushEnabled", value: enabled }); await this.db.prefs.put({ key: "webPushEnabled", value: enabled });
} }
async uiMode() { async theme() {
const uiMode = await this.db.prefs.get("uiMode"); const theme = await this.db.prefs.get("theme");
return uiMode?.value ?? UI_MODE.SYSTEM; return theme?.value ?? THEME.SYSTEM;
} }
async setUIMode(mode) { async setTheme(mode) {
await this.db.prefs.put({ key: "uiMode", value: mode }); await this.db.prefs.put({ key: "theme", value: mode });
} }
} }

View File

@ -22,19 +22,19 @@ import Login from "./Login";
import Signup from "./Signup"; import Signup from "./Signup";
import Account from "./Account"; import Account from "./Account";
import "../app/i18n"; // Translations! import "../app/i18n"; // Translations!
import prefs, { UI_MODE } from "../app/Prefs"; import prefs, { THEME } from "../app/Prefs";
export const AccountContext = createContext(null); export const AccountContext = createContext(null);
const darkModeEnabled = (prefersDarkMode, uiModePreference) => { const darkModeEnabled = (prefersDarkMode, themePreference) => {
switch (uiModePreference) { switch (themePreference) {
case UI_MODE.DARK: case THEME.DARK:
return true; return true;
case UI_MODE.LIGHT: case THEME.LIGHT:
return false; return false;
case UI_MODE.SYSTEM: case THEME.SYSTEM:
default: default:
return prefersDarkMode; return prefersDarkMode;
} }
@ -43,20 +43,17 @@ const darkModeEnabled = (prefersDarkMode, uiModePreference) => {
const App = () => { const App = () => {
const [account, setAccount] = useState(null); const [account, setAccount] = useState(null);
const accountMemo = useMemo(() => ({ account, setAccount }), [account, setAccount]); const accountMemo = useMemo(() => ({ account, setAccount }), [account, setAccount]);
const prefersDarkMode = useMediaQuery("(prefers-color-scheme: dark)"); const prefersDarkMode = useMediaQuery("(prefers-color-scheme: dark)");
const themePreference = useLiveQuery(() => prefs.theme());
const uiModePreference = useLiveQuery(() => prefs.uiMode());
const theme = React.useMemo( const theme = React.useMemo(
() => () =>
createTheme({ createTheme({
...themeOptions, ...themeOptions,
palette: { palette: {
...(darkModeEnabled(prefersDarkMode, uiModePreference) ? darkPalette : lightPalette), ...(darkModeEnabled(prefersDarkMode, themePreference) ? darkPalette : lightPalette),
}, },
}), }),
[prefersDarkMode, uiModePreference] [prefersDarkMode, themePreference]
); );
return ( return (

View File

@ -43,7 +43,7 @@ import accountApi, { Permission, Role } from "../app/AccountApi";
import { Pref, PrefGroup } from "./Pref"; import { Pref, PrefGroup } from "./Pref";
import { AccountContext } from "./App"; import { AccountContext } from "./App";
import { Paragraph } from "./styles"; import { Paragraph } from "./styles";
import prefs, { UI_MODE } from "../app/Prefs"; import prefs, { THEME } from "../app/Prefs";
import { PermissionDenyAll, PermissionRead, PermissionReadWrite, PermissionWrite } from "./ReserveIcons"; import { PermissionDenyAll, PermissionRead, PermissionReadWrite, PermissionWrite } from "./ReserveIcons";
import { ReserveAddDialog, ReserveDeleteDialog, ReserveEditDialog } from "./ReserveDialogs"; import { ReserveAddDialog, ReserveDeleteDialog, ReserveEditDialog } from "./ReserveDialogs";
import { UnauthorizedError } from "../app/errors"; import { UnauthorizedError } from "../app/errors";
@ -86,7 +86,6 @@ const Notifications = () => {
{t("prefs_notifications_title")} {t("prefs_notifications_title")}
</Typography> </Typography>
<PrefGroup> <PrefGroup>
<UIMode />
<Sound /> <Sound />
<MinPriority /> <MinPriority />
<DeleteAfter /> <DeleteAfter />
@ -238,21 +237,21 @@ const DeleteAfter = () => {
); );
}; };
const UIMode = () => { const Theme = () => {
const { t } = useTranslation(); const { t } = useTranslation();
const labelId = "prefUIMode"; const labelId = "prefTheme";
const enabled = useLiveQuery(async () => prefs.uiMode()); const enabled = useLiveQuery(async () => prefs.theme());
const handleChange = async (ev) => { const handleChange = async (ev) => {
await prefs.setUIMode(ev.target.value); await prefs.setTheme(ev.target.value);
}; };
return ( return (
<Pref labelId={labelId} title={t("prefs_ui_mode_title")}> <Pref labelId={labelId} title={t("prefs_appearance_theme_title")}>
<FormControl fullWidth variant="standard" sx={{ m: 1 }}> <FormControl fullWidth variant="standard" sx={{ m: 1 }}>
<Select value={enabled ?? false} onChange={handleChange} aria-labelledby={labelId}> <Select value={enabled ?? false} onChange={handleChange} aria-labelledby={labelId}>
<MenuItem value={UI_MODE.SYSTEM}>{t("prefs_ui_mode_system")}</MenuItem> <MenuItem value={THEME.SYSTEM}>{t("prefs_appearance_theme_system")}</MenuItem>
<MenuItem value={UI_MODE.DARK}>{t("prefs_ui_mode_dark")}</MenuItem> <MenuItem value={THEME.DARK}>{t("prefs_appearance_theme_dark")}</MenuItem>
<MenuItem value={UI_MODE.LIGHT}>{t("prefs_ui_mode_light")}</MenuItem> <MenuItem value={THEME.LIGHT}>{t("prefs_appearance_theme_light")}</MenuItem>
</Select> </Select>
</FormControl> </FormControl>
</Pref> </Pref>
@ -511,6 +510,7 @@ const Appearance = () => {
{t("prefs_appearance_title")} {t("prefs_appearance_title")}
</Typography> </Typography>
<PrefGroup> <PrefGroup>
<Theme />
<Language /> <Language />
</PrefGroup> </PrefGroup>
</Card> </Card>