diff --git a/src/App.tsx b/src/App.tsx index d4d695ea..b8133c4c 100644 --- a/src/App.tsx +++ b/src/App.tsx @@ -35,6 +35,7 @@ import { DefaultNavBar } from '@/components/navbar/DefaultNavBar'; import { DownloadSettings } from '@/screens/settings/DownloadSettings.tsx'; import { ServerSettings } from '@/screens/settings/ServerSettings.tsx'; import { WebUISettings } from '@/screens/settings/WebUISettings.tsx'; +import { ServerUpdateChecker } from '@/components/settings/ServerUpdateChecker.tsx'; if (__DEV__) { // Adds messages only in a dev environment @@ -43,6 +44,7 @@ if (__DEV__) { } export const App: React.FC = () => ( + { + const { t } = useTranslation(); + + const [lastUpdateCheck, setLastUpdateCheck] = useLocalStorage('lastAutomatedServerUpdateCheck', Date.now()); + + const { + data: serverUpdateCheckData, + loading: isCheckingForServerUpdate, + error: serverUpdateCheckError, + refetch: checkForUpdate, + } = requestManager.useCheckForServerUpdate({ notifyOnNetworkStatusChange: true, fetchPolicy: 'cache-only' }); + + const { data } = requestManager.useGetAbout(); + const { aboutServer } = data ?? {}; + + const [open, setOpen] = useState(true); + + const selectedServerChannelInfo = serverUpdateCheckData?.checkForServerUpdates?.find( + (channel) => channel.channel === aboutServer?.buildType, + ); + const version = aboutServer ? getVersion(aboutServer) : undefined; + const isServerUpdateAvailable = !!selectedServerChannelInfo?.tag && selectedServerChannelInfo.tag !== version; + + const handleClose = () => { + setOpen(false); + }; + + useEffect(() => { + const remainingTimeTillNextUpdateCheck = Date.now() - lastUpdateCheck; + + let timeout: NodeJS.Timeout | undefined; + const scheduleUpdateCheck = (timeoutMS: number) => { + timeout = setTimeout(() => { + checkForUpdate().catch(() => {}); + setLastUpdateCheck(Date.now()); + scheduleUpdateCheck(UPDATE_CHECK_INTERVAL); + }, timeoutMS); + }; + + scheduleUpdateCheck(remainingTimeTillNextUpdateCheck); + + return () => clearTimeout(timeout); + }, []); + + if (isCheckingForServerUpdate) { + return null; + } + + if (serverUpdateCheckError) { + return null; + } + + if (!isServerUpdateAvailable) { + return null; + } + + const isAboutPage = window.location.pathname === '/settings/about'; + if (isAboutPage) { + return null; + } + + return ( + + {t('global.update.label.available')} + + + {t('global.update.label.info', { + channel: selectedServerChannelInfo.channel, + version: selectedServerChannelInfo.tag, + })} + + + + + + + + ); +}; diff --git a/src/i18n/locale/en.json b/src/i18n/locale/en.json index e127e317..c262cae9 100644 --- a/src/i18n/locale/en.json +++ b/src/i18n/locale/en.json @@ -307,6 +307,7 @@ "label": { "browse": "Browse", "client": "Client", + "close": "Close", "disabled": "Disabled", "discord": "Discord", "display": "Display", @@ -345,6 +346,7 @@ "available": "Update available", "check_failure": "Could not check for update", "checking": "Checking for update", + "info": "Server version {{version}} ({{channel}}) available for download", "up_to_date": "This is the latest version", "update_failure": "Update failed", "updating": "{{progress}}% | Updating…" diff --git a/src/screens/settings/About.tsx b/src/screens/settings/About.tsx index 2a87dbef..950b55db 100644 --- a/src/screens/settings/About.tsx +++ b/src/screens/settings/About.tsx @@ -28,7 +28,7 @@ import { makeToast } from '@/components/util/Toast.tsx'; type AboutServer = GetAboutQuery['aboutServer']; -const getVersion = (aboutServer: AboutServer) => { +export const getVersion = (aboutServer: AboutServer) => { if (aboutServer.buildType === 'Stable') return `${aboutServer.version}`; return `${aboutServer.version}-${aboutServer.revision}`; };