diff --git a/public/locales/en.json b/public/locales/en.json index 4267ebc3..d5220546 100644 --- a/public/locales/en.json +++ b/public/locales/en.json @@ -302,6 +302,7 @@ "add": "Add", "browse": "Browse", "cancel": "Cancel", + "changelog": "Changelog", "clear": "Clear", "copy": "Copy", "create": "Create", @@ -317,6 +318,7 @@ "open_site": "Open Site", "options": "Options", "popular": "Popular", + "refresh": "Refresh", "remove": "Remove", "reset": "Reset", "reset_to_default": "Reset to Default", @@ -764,7 +766,8 @@ "channel": "$t(settings.webui.title.webui) channel", "github": "$t(global.label.github) $t(settings.webui.title.webui)", "update_failure": "Could not update $t(settings.webui.title.webui)", - "update_success": "Updated $t(settings.webui.title.webui) to version {{version}}. Refresh required", + "update_success": "Updated $t(settings.webui.title.webui) to version {{version}}.", + "updated": "Updated version", "version": "$t(settings.webui.title.webui) version" } } diff --git a/src/App.tsx b/src/App.tsx index 0eeb60f9..819d4b09 100644 --- a/src/App.tsx +++ b/src/App.tsx @@ -39,6 +39,7 @@ import { Migrate } from '@/screens/Migrate.tsx'; import { DeviceSetting } from '@/components/settings/DeviceSetting.tsx'; import { TrackingSettings } from '@/screens/settings/TrackingSettings.tsx'; import { TrackerOAuthLogin } from '@/screens/TrackerOAuthLogin.tsx'; +import { WebUIUpdateChecker } from '@/components/util/WebUIUpdateChecker.tsx'; if (process.env.NODE_ENV !== 'production') { // Adds messages only in a dev environment @@ -74,6 +75,7 @@ export const App: React.FC = () => ( + diff --git a/src/components/util/WebUIUpdateChecker.tsx b/src/components/util/WebUIUpdateChecker.tsx new file mode 100644 index 00000000..7e4cd813 --- /dev/null +++ b/src/components/util/WebUIUpdateChecker.tsx @@ -0,0 +1,125 @@ +/* + * Copyright (C) Contributors to the Suwayomi project + * + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at https://mozilla.org/MPL/2.0/. + */ + +import { useTranslation } from 'react-i18next'; +import { useEffect, useState } from 'react'; +import Dialog from '@mui/material/Dialog'; +import DialogTitle from '@mui/material/DialogTitle'; +import DialogContent from '@mui/material/DialogContent'; +import DialogContentText from '@mui/material/DialogContentText'; +import DialogActions from '@mui/material/DialogActions'; +import Button from '@mui/material/Button'; +import { defaultPromiseErrorHandler } from '@/util/defaultPromiseErrorHandler.ts'; +import { UpdateState, WebUiChannel, WebUiUpdateStatus } from '@/lib/graphql/generated/graphql.ts'; +import { useLocalStorage } from '@/util/useStorage.tsx'; +import { requestManager } from '@/lib/requests/RequestManager.ts'; +import { makeToast } from '@/components/util/Toast.tsx'; +import { ABOUT_WEBUI, WEBUI_UPDATE_CHECK } from '@/lib/graphql/Fragments.ts'; + +export const WebUIUpdateChecker = () => { + const { t } = useTranslation(); + + const [webUIVersion, setWebUIVersion] = useLocalStorage('webUIVersion'); + const [open, setOpen] = useState(false); + + const { data: webUIUpdateStatusData } = requestManager.useGetWebUIUpdateStatus(); + const { state: webUIUpdateState, ...updateStatus } = (webUIUpdateStatusData?.getWebUIUpdateStatus ?? { + state: UpdateState.Idle, + progress: 0, + info: undefined, + }) satisfies OptionalProperty; + + const changelogUrl = + updateStatus.info?.channel === WebUiChannel.Stable + ? `https://github.com/Suwayomi/Suwayomi-WebUI/releases/tag/${updateStatus.info?.tag}` + : `https://github.com/Suwayomi/Suwayomi-WebUI/issues/749`; + + useEffect(() => { + const isError = webUIUpdateState === UpdateState.Error; + if (isError) { + makeToast(t('settings.about.webui.label.update_failure'), 'error'); + } + + const updateFinished = webUIUpdateState === UpdateState.Finished; + + const resetUpdateStatus = isError || updateFinished; + if (resetUpdateStatus) { + requestManager + .resetWebUIUpdateStatus() + .response.catch(defaultPromiseErrorHandler('WebUIUpdateChecker::resetWebUIUpdateStatus')); + } + + if (!updateFinished) { + return; + } + + if (!updateStatus.info) { + return; + } + requestManager.graphQLClient.client.cache.writeFragment({ + fragment: ABOUT_WEBUI, + data: { + __typename: 'AboutWebUI', + channel: webUIUpdateStatusData!.getWebUIUpdateStatus.info.channel, + tag: webUIUpdateStatusData!.getWebUIUpdateStatus.info.tag, + }, + }); + requestManager.graphQLClient.client.cache.writeFragment({ + fragment: WEBUI_UPDATE_CHECK, + data: { + __typename: 'WebUIUpdateCheck', + channel: webUIUpdateStatusData!.getWebUIUpdateStatus.info.channel, + tag: webUIUpdateStatusData!.getWebUIUpdateStatus.info.tag, + updateAvailable: false, + }, + }); + + const newVersion = updateStatus.info.tag; + const isSameAsCurrent = webUIVersion === newVersion; + + setWebUIVersion(newVersion); + + if (isSameAsCurrent) { + return; + } + + setOpen(true); + }, [webUIUpdateState]); + + const handleUpdate = open && webUIUpdateState === UpdateState.Idle; + if (!handleUpdate) { + return null; + } + + return ( + + {t('settings.about.webui.label.updated')} + + + {t('settings.about.webui.label.update_success', { + version: webUIVersion, + })} + + + + + + + + ); +}; diff --git a/src/screens/settings/About.tsx b/src/screens/settings/About.tsx index 371bd540..18e4d3ae 100644 --- a/src/screens/settings/About.tsx +++ b/src/screens/settings/About.tsx @@ -27,8 +27,6 @@ import { ListItemLink } from '@/components/util/ListItemLink'; import { NavBarContext, useSetDefaultBackTo } from '@/components/context/NavbarContext'; import { LoadingPlaceholder } from '@/components/util/LoadingPlaceholder'; import { GetAboutQuery, UpdateState } from '@/lib/graphql/generated/graphql.ts'; -import { ABOUT_WEBUI, WEBUI_UPDATE_CHECK } from '@/lib/graphql/Fragments.ts'; -import { makeToast } from '@/components/util/Toast.tsx'; import { defaultPromiseErrorHandler } from '@/util/defaultPromiseErrorHandler.ts'; type AboutServer = GetAboutQuery['aboutServer']; @@ -226,51 +224,6 @@ export function About() { progress: 0, }; - useEffect(() => { - const isError = webUIUpdateState === UpdateState.Error; - if (isError) { - makeToast(t('settings.about.webui.label.update_failure'), 'error'); - } - - const updateFinished = webUIUpdateState === UpdateState.Finished; - - const resetUpdateStatus = isError || updateFinished; - if (resetUpdateStatus) { - requestManager - .resetWebUIUpdateStatus() - .response.catch(defaultPromiseErrorHandler('About::resetWebUIUpdateStatus')); - } - - if (!updateFinished) { - return; - } - - makeToast( - t('settings.about.webui.label.update_success', { - version: webUIUpdateStatusData!.getWebUIUpdateStatus.info.tag, - }), - 'success', - ); - - requestManager.graphQLClient.client.cache.writeFragment({ - fragment: ABOUT_WEBUI, - data: { - __typename: 'AboutWebUI', - channel: webUIUpdateStatusData!.getWebUIUpdateStatus.info.channel, - tag: webUIUpdateStatusData!.getWebUIUpdateStatus.info.tag, - }, - }); - requestManager.graphQLClient.client.cache.writeFragment({ - fragment: WEBUI_UPDATE_CHECK, - data: { - __typename: 'WebUIUpdateCheck', - channel: webUIUpdateStatusData!.getWebUIUpdateStatus.info.channel, - tag: webUIUpdateStatusData!.getWebUIUpdateStatus.info.tag, - updateAvailable: false, - }, - }); - }, [webUIUpdateState]); - if (!aboutServer || !aboutWebUI) { return ; }