diff --git a/server/config.go b/server/config.go
index 5136e82e..8a111ed4 100644
--- a/server/config.go
+++ b/server/config.go
@@ -182,7 +182,7 @@ type Config struct {
WebPushStartupQueries string
WebPushExpiryDuration time.Duration
WebPushExpiryWarningDuration time.Duration
- Version string // injected by App
+ Version string // Injected by App
}
// NewConfig instantiates a default new server config
@@ -279,86 +279,13 @@ func NewConfig() *Config {
}
}
-// configHashData is a subset of Config fields used for computing the config hash.
-// It excludes sensitive fields (keys, passwords, tokens) and runtime-only fields.
-type configHashData struct {
- BaseURL string
- ListenHTTP string
- ListenHTTPS string
- ListenUnix string
- CacheDuration time.Duration
- AttachmentTotalSizeLimit int64
- AttachmentFileSizeLimit int64
- AttachmentExpiryDuration time.Duration
- KeepaliveInterval time.Duration
- ManagerInterval time.Duration
- DisallowedTopics []string
- WebRoot string
- MessageDelayMin time.Duration
- MessageDelayMax time.Duration
- MessageSizeLimit int
- TotalTopicLimit int
- VisitorSubscriptionLimit int
- VisitorAttachmentTotalSizeLimit int64
- VisitorAttachmentDailyBandwidthLimit int64
- VisitorRequestLimitBurst int
- VisitorRequestLimitReplenish time.Duration
- VisitorMessageDailyLimit int
- VisitorEmailLimitBurst int
- VisitorEmailLimitReplenish time.Duration
- EnableSignup bool
- EnableLogin bool
- RequireLogin bool
- EnableReservations bool
- EnableMetrics bool
- EnablePayments bool
- EnableCalls bool
- EnableEmails bool
- EnableWebPush bool
- BillingContact string
- Version string
-}
-
// Hash computes a SHA-256 hash of the configuration. This is used to detect
// configuration changes for the web app version check feature.
func (c *Config) Hash() string {
- data := configHashData{
- BaseURL: c.BaseURL,
- ListenHTTP: c.ListenHTTP,
- ListenHTTPS: c.ListenHTTPS,
- ListenUnix: c.ListenUnix,
- CacheDuration: c.CacheDuration,
- AttachmentTotalSizeLimit: c.AttachmentTotalSizeLimit,
- AttachmentFileSizeLimit: c.AttachmentFileSizeLimit,
- AttachmentExpiryDuration: c.AttachmentExpiryDuration,
- KeepaliveInterval: c.KeepaliveInterval,
- ManagerInterval: c.ManagerInterval,
- DisallowedTopics: c.DisallowedTopics,
- WebRoot: c.WebRoot,
- MessageDelayMin: c.MessageDelayMin,
- MessageDelayMax: c.MessageDelayMax,
- MessageSizeLimit: c.MessageSizeLimit,
- TotalTopicLimit: c.TotalTopicLimit,
- VisitorSubscriptionLimit: c.VisitorSubscriptionLimit,
- VisitorAttachmentTotalSizeLimit: c.VisitorAttachmentTotalSizeLimit,
- VisitorAttachmentDailyBandwidthLimit: c.VisitorAttachmentDailyBandwidthLimit,
- VisitorRequestLimitBurst: c.VisitorRequestLimitBurst,
- VisitorRequestLimitReplenish: c.VisitorRequestLimitReplenish,
- VisitorMessageDailyLimit: c.VisitorMessageDailyLimit,
- VisitorEmailLimitBurst: c.VisitorEmailLimitBurst,
- VisitorEmailLimitReplenish: c.VisitorEmailLimitReplenish,
- EnableSignup: c.EnableSignup,
- EnableLogin: c.EnableLogin,
- RequireLogin: c.RequireLogin,
- EnableReservations: c.EnableReservations,
- EnableMetrics: c.EnableMetrics,
- EnablePayments: c.StripeSecretKey != "",
- EnableCalls: c.TwilioAccount != "",
- EnableEmails: c.SMTPSenderFrom != "",
- EnableWebPush: c.WebPushPublicKey != "",
- BillingContact: c.BillingContact,
- Version: c.Version,
+ b, err := json.Marshal(c)
+ if err != nil {
+ fmt.Println(err)
}
- b, _ := json.Marshal(data)
+ fmt.Println(string(b))
return fmt.Sprintf("%x", sha256.Sum256(b))
}
diff --git a/web/public/static/langs/en.json b/web/public/static/langs/en.json
index bfbcf610..15e78976 100644
--- a/web/public/static/langs/en.json
+++ b/web/public/static/langs/en.json
@@ -5,7 +5,8 @@
"common_back": "Back",
"common_copy_to_clipboard": "Copy to clipboard",
"common_refresh": "Refresh",
- "version_update_available": "New ntfy version available. Please refresh the page.",
+ "version_update_available_title": "New version available",
+ "version_update_available_description": "The ntfy server has been updated. Please refresh the page.",
"signup_title": "Create a ntfy account",
"signup_form_username": "Username",
"signup_form_password": "Password",
diff --git a/web/src/app/VersionChecker.js b/web/src/app/VersionChecker.js
index ae0272b4..07459cb2 100644
--- a/web/src/app/VersionChecker.js
+++ b/web/src/app/VersionChecker.js
@@ -3,7 +3,7 @@
* or configuration changes, prompting users to refresh the page.
*/
-const CHECK_INTERVAL = 5 * 60 * 1000; // 5 minutes
+const CHECK_INTERVAL = 30 * 1000; // 5 * 60 * 1000; // 5 minutes
class VersionChecker {
constructor() {
diff --git a/web/src/components/App.jsx b/web/src/components/App.jsx
index e6157b86..9a2c3e66 100644
--- a/web/src/components/App.jsx
+++ b/web/src/components/App.jsx
@@ -1,18 +1,6 @@
import * as React from "react";
-import { createContext, Suspense, useContext, useEffect, useState, useMemo, useCallback } from "react";
-import {
- Box,
- Toolbar,
- CssBaseline,
- Backdrop,
- CircularProgress,
- useMediaQuery,
- ThemeProvider,
- createTheme,
- Snackbar,
- Button,
- Alert,
-} from "@mui/material";
+import { createContext, Suspense, useContext, useEffect, useState, useMemo } from "react";
+import { Box, Toolbar, CssBaseline, Backdrop, CircularProgress, useMediaQuery, ThemeProvider, createTheme } from "@mui/material";
import { useLiveQuery } from "dexie-react-hooks";
import { BrowserRouter, Outlet, Route, Routes, useParams } from "react-router-dom";
import { useTranslation } from "react-i18next";
@@ -26,13 +14,7 @@ import userManager from "../app/UserManager";
import { expandUrl, getKebabCaseLangStr } from "../app/utils";
import ErrorBoundary from "./ErrorBoundary";
import routes from "./routes";
-import {
- useAccountListener,
- useBackgroundProcesses,
- useConnectionListeners,
- useWebPushTopics,
- useVersionChangeListener,
-} from "./hooks";
+import { useAccountListener, useBackgroundProcesses, useConnectionListeners, useWebPushTopics } from "./hooks";
import PublishDialog from "./PublishDialog";
import Messaging from "./Messaging";
import Login from "./Login";
@@ -118,12 +100,10 @@ const updateTitle = (newNotificationsCount) => {
};
const Layout = () => {
- const { t } = useTranslation();
const params = useParams();
const { account, setAccount } = useContext(AccountContext);
const [mobileDrawerOpen, setMobileDrawerOpen] = useState(false);
const [sendDialogOpenMode, setSendDialogOpenMode] = useState("");
- const [versionChanged, setVersionChanged] = useState(false);
const users = useLiveQuery(() => userManager.all());
const subscriptions = useLiveQuery(() => subscriptionManager.all());
const webPushTopics = useWebPushTopics();
@@ -135,18 +115,9 @@ const Layout = () => {
(config.base_url === s.baseUrl && params.topic === s.topic)
);
- const handleVersionChange = useCallback(() => {
- setVersionChanged(true);
- }, []);
-
- const handleRefresh = useCallback(() => {
- window.location.reload();
- }, []);
-
useConnectionListeners(account, subscriptions, users, webPushTopics);
useAccountListener(setAccount);
useBackgroundProcesses();
- useVersionChangeListener(handleVersionChange);
useEffect(() => updateTitle(newNotificationsCount), [newNotificationsCount]);
return (
@@ -169,23 +140,6 @@ const Layout = () => {
/>
-
-
- {t("common_refresh")}
-
- }
- >
- {t("version_update_available")}
-
-
);
};
diff --git a/web/src/components/Navigation.jsx b/web/src/components/Navigation.jsx
index 7e30931a..75b587ea 100644
--- a/web/src/components/Navigation.jsx
+++ b/web/src/components/Navigation.jsx
@@ -21,7 +21,7 @@ import {
useTheme,
} from "@mui/material";
import * as React from "react";
-import { useContext, useState } from "react";
+import { useCallback, useContext, useState } from "react";
import ChatBubbleOutlineIcon from "@mui/icons-material/ChatBubbleOutline";
import Person from "@mui/icons-material/Person";
import SettingsIcon from "@mui/icons-material/Settings";
@@ -44,7 +44,7 @@ import UpgradeDialog from "./UpgradeDialog";
import { AccountContext } from "./App";
import { PermissionDenyAll, PermissionRead, PermissionReadWrite, PermissionWrite } from "./ReserveIcons";
import { SubscriptionPopup } from "./SubscriptionPopup";
-import { useNotificationPermissionListener } from "./hooks";
+import { useNotificationPermissionListener, useVersionChangeListener } from "./hooks";
const navWidth = 280;
@@ -91,6 +91,13 @@ const NavList = (props) => {
const { account } = useContext(AccountContext);
const [subscribeDialogKey, setSubscribeDialogKey] = useState(0);
const [subscribeDialogOpen, setSubscribeDialogOpen] = useState(false);
+ const [versionChanged, setVersionChanged] = useState(false);
+
+ const handleVersionChange = useCallback(() => {
+ setVersionChanged(true);
+ }, []);
+
+ useVersionChangeListener(handleVersionChange);
const handleSubscribeReset = () => {
setSubscribeDialogOpen(false);
@@ -119,6 +126,7 @@ const NavList = (props) => {
const showNotificationContextNotSupportedBox = notifier.browserSupported() && !notifier.contextSupported(); // Only show if notifications are generally supported in the browser
const alertVisible =
+ versionChanged ||
showNotificationPermissionRequired ||
showNotificationPermissionDenied ||
showNotificationIOSInstallRequired ||
@@ -129,6 +137,7 @@ const NavList = (props) => {
<>
+ {versionChanged && }
{showNotificationPermissionRequired && }
{showNotificationPermissionDenied && }
{showNotificationBrowserNotSupportedBox && }
@@ -425,4 +434,20 @@ const NotificationContextNotSupportedAlert = () => {
);
};
+const VersionUpdateBanner = () => {
+ const { t } = useTranslation();
+ const handleRefresh = () => {
+ window.location.reload();
+ };
+ return (
+
+ {t("version_update_available_title")}
+ {t("version_update_available_description")}
+
+
+ );
+};
+
export default Navigation;