From 8db569e8a54352f83e5a3e6bd5060fe271c9da9f Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?=E6=80=AA=E7=9B=97kidou?= <bestkidou@gmail.com>
Date: Tue, 27 Jun 2023 14:43:51 +0000
Subject: [PATCH 1/4] Translated using Weblate (Chinese (Simplified))

Currently translated at 94.5% (361 of 382 strings)

Translation: ntfy/Web app
Translate-URL: https://hosted.weblate.org/projects/ntfy/web/zh_Hans/
---
 web/public/static/langs/zh_Hans.json | 9 ++++++++-
 1 file changed, 8 insertions(+), 1 deletion(-)

diff --git a/web/public/static/langs/zh_Hans.json b/web/public/static/langs/zh_Hans.json
index 2db95f56..52e0a1b8 100644
--- a/web/public/static/langs/zh_Hans.json
+++ b/web/public/static/langs/zh_Hans.json
@@ -352,5 +352,12 @@
     "account_upgrade_dialog_tier_price_billed_monthly": "{{price}} 每年。按月计费。",
     "account_upgrade_dialog_tier_price_billed_yearly": "{{价格}} 按年计费。节省 {{save}}。",
     "account_upgrade_dialog_billing_contact_email": "有关账单问题,请直接<Link>联系我们 </Link>。",
-    "account_upgrade_dialog_billing_contact_website": "有关账单问题,请参考我们的<Link>网站 </Link>。"
+    "account_upgrade_dialog_billing_contact_website": "有关账单问题,请参考我们的<Link>网站 </Link>。",
+    "publish_dialog_call_item": "拨打电话 {{number}}",
+    "publish_dialog_call_label": "拨号",
+    "publish_dialog_chip_call_label": "拨号",
+    "publish_dialog_chip_call_no_verified_numbers_tooltip": "未验证的手机号",
+    "account_basics_phone_numbers_title": "电话号码",
+    "account_basics_phone_numbers_description": "电话通知",
+    "account_basics_phone_numbers_dialog_description": "要使用来电通知功能,您需要添加并验证至少一个电话号码。可以通过短信或电话进行验证。"
 }

From d838790b8fbca371750d3c51c4d38bb841fe7ef0 Mon Sep 17 00:00:00 2001
From: nimbleghost <132819643+nimbleghost@users.noreply.github.com>
Date: Wed, 28 Jun 2023 20:42:34 +0200
Subject: [PATCH 2/4] Fix ntfy upgrade banner in dark mode

---
 web/src/components/ActionBar.jsx  |  5 ++++-
 web/src/components/Navigation.jsx | 22 +++++++++++++++++-----
 web/src/components/theme.js       |  2 --
 3 files changed, 21 insertions(+), 8 deletions(-)

diff --git a/web/src/components/ActionBar.jsx b/web/src/components/ActionBar.jsx
index 6a36cdb6..62a16286 100644
--- a/web/src/components/ActionBar.jsx
+++ b/web/src/components/ActionBar.jsx
@@ -44,7 +44,10 @@ const ActionBar = (props) => {
       <Toolbar
         sx={{
           pr: "24px",
-          background: theme.palette.actionBarBackground,
+          background:
+            theme.palette.mode === "light"
+              ? "linear-gradient(150deg, #338574 0%, #56bda8 100%)"
+              : "linear-gradient(150deg, #203631 0%, #2a6e60 100%)",
         }}
       >
         <IconButton
diff --git a/web/src/components/Navigation.jsx b/web/src/components/Navigation.jsx
index fe1cf8be..41b2b0ce 100644
--- a/web/src/components/Navigation.jsx
+++ b/web/src/components/Navigation.jsx
@@ -18,6 +18,7 @@ import {
   Box,
   IconButton,
   Button,
+  useTheme,
 } from "@mui/material";
 import * as React from "react";
 import { useContext, useState } from "react";
@@ -82,6 +83,7 @@ const Navigation = (props) => {
 Navigation.width = navWidth;
 
 const NavList = (props) => {
+  const theme = useTheme();
   const { t } = useTranslation();
   const navigate = useNavigate();
   const location = useLocation();
@@ -190,7 +192,11 @@ const NavList = (props) => {
           </ListItemIcon>
           <ListItemText primary={t("nav_button_subscribe")} />
         </ListItemButton>
-        {showUpgradeBanner && <UpgradeBanner />}
+        {showUpgradeBanner && (
+          // The text background gradient didn't seem to do well with switching between light/dark mode,
+          // So adding a `key` forces React to replace the entire component when the theme changes
+          <UpgradeBanner key={`upgrade-banner-${theme.palette.mode}`} mode={theme.palette.mode} />
+        )}
       </List>
       <SubscribeDialog
         key={`subscribeDialog${subscribeDialogKey}`} // Resets dialog when canceled/closed
@@ -203,7 +209,7 @@ const NavList = (props) => {
   );
 };
 
-const UpgradeBanner = () => {
+const UpgradeBanner = ({ mode }) => {
   const { t } = useTranslation();
   const [dialogKey, setDialogKey] = useState(0);
   const [dialogOpen, setDialogOpen] = useState(false);
@@ -220,13 +226,16 @@ const UpgradeBanner = () => {
         width: `${Navigation.width - 1}px`,
         bottom: 0,
         mt: "auto",
-        background: "linear-gradient(150deg, rgba(196, 228, 221, 0.46) 0%, rgb(255, 255, 255) 100%)",
+        background:
+          mode === "light"
+            ? "linear-gradient(150deg, rgba(196, 228, 221, 0.46) 0%, rgb(255, 255, 255) 100%)"
+            : "linear-gradient(150deg, #203631 0%, #2a6e60 100%)",
       }}
     >
       <Divider />
       <ListItemButton onClick={handleClick} sx={{ pt: 2, pb: 2 }}>
         <ListItemIcon>
-          <CelebrationIcon sx={{ color: "#55b86e" }} fontSize="large" />
+          <CelebrationIcon sx={{ color: mode === "light" ? "#55b86e" : "#00ff95" }} fontSize="large" />
         </ListItemIcon>
         <ListItemText
           sx={{ ml: 1 }}
@@ -236,7 +245,10 @@ const UpgradeBanner = () => {
             style: {
               fontWeight: 500,
               fontSize: "1.1rem",
-              background: "-webkit-linear-gradient(45deg, #09009f, #00ff95 80%)",
+              background:
+                mode === "light"
+                  ? "-webkit-linear-gradient(45deg, #09009f, #00ff95 80%)"
+                  : "-webkit-linear-gradient(45deg,rgb(255, 255, 255), #00ff95 80%)",
               WebkitBackgroundClip: "text",
               WebkitTextFillColor: "transparent",
             },
diff --git a/web/src/components/theme.js b/web/src/components/theme.js
index f789a0c5..cdfaf07d 100644
--- a/web/src/components/theme.js
+++ b/web/src/components/theme.js
@@ -34,7 +34,6 @@ export const lightPalette = {
   error: {
     main: "#c30000",
   },
-  actionBarBackground: "linear-gradient(150deg, #338574 0%, #56bda8 100%)",
 };
 
 /** @type {import("@mui/material").ThemeOptions['palette']} */
@@ -52,7 +51,6 @@ export const darkPalette = {
   error: {
     main: "#fe4d2e",
   },
-  actionBarBackground: "linear-gradient(150deg, #203631 0%, #2a6e60 100%)",
 };
 
 export default themeOptions;

From 0d231d8bd9f4783367548043b60198b202af3716 Mon Sep 17 00:00:00 2001
From: nimbleghost <132819643+nimbleghost@users.noreply.github.com>
Date: Wed, 28 Jun 2023 21:18:04 +0200
Subject: [PATCH 3/4] Fix snackbars in dark mode

---
 web/src/components/App.jsx  | 10 ++----
 web/src/components/theme.js | 70 +++++++++++++++++++++++--------------
 2 files changed, 46 insertions(+), 34 deletions(-)

diff --git a/web/src/components/App.jsx b/web/src/components/App.jsx
index 6c4761f1..70d6936a 100644
--- a/web/src/components/App.jsx
+++ b/web/src/components/App.jsx
@@ -5,7 +5,7 @@ import { ThemeProvider, createTheme } from "@mui/material/styles";
 import { useLiveQuery } from "dexie-react-hooks";
 import { BrowserRouter, Outlet, Route, Routes, useParams } from "react-router-dom";
 import { AllSubscriptions, SingleSubscription } from "./Notifications";
-import themeOptions, { darkPalette, lightPalette } from "./theme";
+import { darkTheme, lightTheme } from "./theme";
 import Navigation from "./Navigation";
 import ActionBar from "./ActionBar";
 import notifier from "../app/Notifier";
@@ -46,13 +46,7 @@ const App = () => {
   const prefersDarkMode = useMediaQuery("(prefers-color-scheme: dark)");
   const themePreference = useLiveQuery(() => prefs.theme());
   const theme = React.useMemo(
-    () =>
-      createTheme({
-        ...themeOptions,
-        palette: {
-          ...(darkModeEnabled(prefersDarkMode, themePreference) ? darkPalette : lightPalette),
-        },
-      }),
+    () => createTheme(darkModeEnabled(prefersDarkMode, themePreference) ? darkTheme : lightTheme),
     [prefersDarkMode, themePreference]
   );
 
diff --git a/web/src/components/theme.js b/web/src/components/theme.js
index cdfaf07d..64217eee 100644
--- a/web/src/components/theme.js
+++ b/web/src/components/theme.js
@@ -1,5 +1,5 @@
 /** @type {import("@mui/material").ThemeOptions} */
-const themeOptions = {
+const baseThemeOptions = {
   components: {
     MuiListItemIcon: {
       styleOverrides: {
@@ -22,35 +22,53 @@ const themeOptions = {
 
 // https://github.com/binwiederhier/ntfy-android/blob/main/app/src/main/res/values/colors.xml
 
-/** @type {import("@mui/material").ThemeOptions['palette']} */
-export const lightPalette = {
-  mode: "light",
-  primary: {
-    main: "#338574",
+/** @type {import("@mui/material").ThemeOptions} */
+export const lightTheme = {
+  ...baseThemeOptions,
+  components: {
+    ...baseThemeOptions.components,
   },
-  secondary: {
-    main: "#6cead0",
-  },
-  error: {
-    main: "#c30000",
+  palette: {
+    mode: "light",
+    primary: {
+      main: "#338574",
+    },
+    secondary: {
+      main: "#6cead0",
+    },
+    error: {
+      main: "#c30000",
+    },
   },
 };
 
-/** @type {import("@mui/material").ThemeOptions['palette']} */
-export const darkPalette = {
-  mode: "dark",
-  background: {
-    paper: "#1b2124",
+/** @type {import("@mui/material").ThemeOptions} */
+export const darkTheme = {
+  ...baseThemeOptions,
+  components: {
+    ...baseThemeOptions.components,
+    MuiSnackbarContent: {
+      styleOverrides: {
+        root: {
+          color: "#000",
+          backgroundColor: "#aeaeae",
+        },
+      },
+    },
   },
-  primary: {
-    main: "#65b5a3",
-  },
-  secondary: {
-    main: "#6cead0",
-  },
-  error: {
-    main: "#fe4d2e",
+  palette: {
+    mode: "dark",
+    background: {
+      paper: "#1b2124",
+    },
+    primary: {
+      main: "#65b5a3",
+    },
+    secondary: {
+      main: "#6cead0",
+    },
+    error: {
+      main: "#fe4d2e",
+    },
   },
 };
-
-export default themeOptions;

From 6b4c04c390c45b16b72729ba1f7ac8198bd2fc10 Mon Sep 17 00:00:00 2001
From: nimbleghost <132819643+nimbleghost@users.noreply.github.com>
Date: Thu, 29 Jun 2023 00:22:58 +0200
Subject: [PATCH 4/4] Make action bar match theme colour when run as PWA

---
 web/src/components/ActionBar.jsx  | 24 ++++++++++++++++++++----
 web/src/components/Navigation.jsx |  2 +-
 2 files changed, 21 insertions(+), 5 deletions(-)

diff --git a/web/src/components/ActionBar.jsx b/web/src/components/ActionBar.jsx
index 62a16286..1f41aac0 100644
--- a/web/src/components/ActionBar.jsx
+++ b/web/src/components/ActionBar.jsx
@@ -19,11 +19,14 @@ import Navigation from "./Navigation";
 import accountApi from "../app/AccountApi";
 import PopupMenu from "./PopupMenu";
 import { SubscriptionPopup } from "./SubscriptionPopup";
+import { useIsLaunchedPWA } from "./hooks";
 
 const ActionBar = (props) => {
   const theme = useTheme();
   const { t } = useTranslation();
   const location = useLocation();
+  const isLaunchedPWA = useIsLaunchedPWA();
+
   let title = "ntfy";
   if (props.selected) {
     title = topicDisplayName(props.selected);
@@ -32,6 +35,22 @@ const ActionBar = (props) => {
   } else if (location.pathname === routes.account) {
     title = t("action_bar_account");
   }
+
+  const getActionBarBackground = () => {
+    if (isLaunchedPWA) {
+      return "#317f6f";
+    }
+
+    switch (theme.palette.mode) {
+      case "dark":
+        return "linear-gradient(150deg, #203631 0%, #2a6e60 100%)";
+
+      case "light":
+      default:
+        return "linear-gradient(150deg, #338574 0%, #56bda8 100%)";
+    }
+  };
+
   return (
     <AppBar
       position="fixed"
@@ -44,10 +63,7 @@ const ActionBar = (props) => {
       <Toolbar
         sx={{
           pr: "24px",
-          background:
-            theme.palette.mode === "light"
-              ? "linear-gradient(150deg, #338574 0%, #56bda8 100%)"
-              : "linear-gradient(150deg, #203631 0%, #2a6e60 100%)",
+          background: getActionBarBackground(),
         }}
       >
         <IconButton
diff --git a/web/src/components/Navigation.jsx b/web/src/components/Navigation.jsx
index 41b2b0ce..855fd63b 100644
--- a/web/src/components/Navigation.jsx
+++ b/web/src/components/Navigation.jsx
@@ -132,7 +132,7 @@ const NavList = (props) => {
   return (
     <>
       <Toolbar sx={{ display: { xs: "none", sm: "block" } }} />
-      <List component="nav" sx={{ paddingTop: alertVisible ? "0" : "" }}>
+      <List component="nav" sx={{ paddingTop: { xs: 0, sm: alertVisible ? 0 : "" } }}>
         {showNotificationPermissionRequired && <NotificationPermissionRequired refreshPermissions={refreshPermissions} />}
         {showNotificationPermissionDenied && <NotificationPermissionDeniedAlert />}
         {showNotificationBrowserNotSupportedBox && <NotificationBrowserNotSupportedAlert />}