/* * 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 '@/lib/DefaultPromiseErrorHandler.ts'; import { UpdateState, WebUiChannel, WebUiUpdateStatus } from '@/lib/graphql/generated/graphql.ts'; import { useLocalStorage } from '@/modules/core/hooks/useStorage.tsx'; import { requestManager } from '@/lib/requests/requests/RequestManager.ts'; import { makeToast } from '@/modules/core/utils/Toast.ts'; import { ABOUT_WEBUI, WEBUI_UPDATE_CHECK } from '@/lib/graphql/fragments/InfoFragments.ts'; import { VersionUpdateInfoDialog } from '@/modules/app-updates/components/VersionUpdateInfoDialog.tsx'; import { useUpdateChecker } from '@/modules/app-updates/hooks/useUpdateChecker.tsx'; import { useMetadataServerSettings } from '@/modules/settings/services/ServerSettingsMetadata.ts'; const disabledUpdateCheck = () => Promise.resolve(); export const WebUIUpdateChecker = () => { const { t } = useTranslation(); const [webUIVersion, setWebUIVersion] = useLocalStorage('webUIVersion'); const [open, setOpen] = useState(false); const { settings: { webUIInformAvailableUpdate }, } = useMetadataServerSettings(); const serverSettings = requestManager.useGetServerSettings(); const isAutoUpdateEnabled = !!serverSettings.data?.settings.webUIUpdateCheckInterval; const shouldCheckForUpdate = !isAutoUpdateEnabled && webUIInformAvailableUpdate; const { data: aboutData } = requestManager.useGetAbout(); const { aboutWebUI } = aboutData ?? {}; const { data: webUIUpdateData, refetch: checkForUpdate } = requestManager.useCheckForWebUIUpdate({ notifyOnNetworkStatusChange: true, fetchPolicy: 'cache-only', }); const { data: webUIUpdateStatusData } = requestManager.useGetWebUIUpdateStatus(); const { state: webUIUpdateState, ...updateStatus } = (webUIUpdateStatusData?.getWebUIUpdateStatus ?? { state: UpdateState.Idle, progress: 0, info: undefined, }) satisfies OptionalProperty; const updateChecker = useUpdateChecker( 'webUI', shouldCheckForUpdate ? checkForUpdate : disabledUpdateCheck, webUIUpdateData?.checkForWebUIUpdate.tag, ); 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`; const newVersion = aboutWebUI?.tag; const isSameAsCurrent = !newVersion || !webUIVersion || webUIVersion === newVersion; const saveInitialVersion = !webUIVersion && !!newVersion; if (saveInitialVersion) { setWebUIVersion(newVersion); } if (!isSameAsCurrent && !open) { setOpen(true); } 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, }, }); }, [webUIUpdateState]); const isUpdateAvailable = shouldCheckForUpdate && updateChecker.handleUpdate && webUIUpdateData?.checkForWebUIUpdate.updateAvailable; if (isUpdateAvailable) { const isUpdateInProgress = webUIUpdateState === UpdateState.Downloading; return ( requestManager .updateWebUI() .response.catch(() => makeToast(t('settings.about.webui.label.update_failure'), 'error')) } actionTitle={ isUpdateInProgress ? t('global.update.label.updating', { progress: updateStatus.progress }) : t('extension.action.label.update') } updateCheckerProps={[ 'webUI', isAutoUpdateEnabled ? disabledUpdateCheck : checkForUpdate, webUIUpdateData?.checkForWebUIUpdate.tag, ]} /> ); } if (!open) { return null; } return ( {t('settings.about.webui.label.updated')} {t('global.update.label.update_success', { name: t('settings.webui.title.webui'), version: newVersion, channel: aboutWebUI?.channel, })} ); };