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;