Globally inform about successful webUI updates
In case automatic webUI updates are disabled, the user gets informed about available updates
This commit is contained in:
@@ -302,6 +302,7 @@
|
|||||||
"add": "Add",
|
"add": "Add",
|
||||||
"browse": "Browse",
|
"browse": "Browse",
|
||||||
"cancel": "Cancel",
|
"cancel": "Cancel",
|
||||||
|
"changelog": "Changelog",
|
||||||
"clear": "Clear",
|
"clear": "Clear",
|
||||||
"copy": "Copy",
|
"copy": "Copy",
|
||||||
"create": "Create",
|
"create": "Create",
|
||||||
@@ -317,6 +318,7 @@
|
|||||||
"open_site": "Open Site",
|
"open_site": "Open Site",
|
||||||
"options": "Options",
|
"options": "Options",
|
||||||
"popular": "Popular",
|
"popular": "Popular",
|
||||||
|
"refresh": "Refresh",
|
||||||
"remove": "Remove",
|
"remove": "Remove",
|
||||||
"reset": "Reset",
|
"reset": "Reset",
|
||||||
"reset_to_default": "Reset to Default",
|
"reset_to_default": "Reset to Default",
|
||||||
@@ -764,7 +766,8 @@
|
|||||||
"channel": "$t(settings.webui.title.webui) channel",
|
"channel": "$t(settings.webui.title.webui) channel",
|
||||||
"github": "$t(global.label.github) $t(settings.webui.title.webui)",
|
"github": "$t(global.label.github) $t(settings.webui.title.webui)",
|
||||||
"update_failure": "Could not update $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"
|
"version": "$t(settings.webui.title.webui) version"
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -39,6 +39,7 @@ import { Migrate } from '@/screens/Migrate.tsx';
|
|||||||
import { DeviceSetting } from '@/components/settings/DeviceSetting.tsx';
|
import { DeviceSetting } from '@/components/settings/DeviceSetting.tsx';
|
||||||
import { TrackingSettings } from '@/screens/settings/TrackingSettings.tsx';
|
import { TrackingSettings } from '@/screens/settings/TrackingSettings.tsx';
|
||||||
import { TrackerOAuthLogin } from '@/screens/TrackerOAuthLogin.tsx';
|
import { TrackerOAuthLogin } from '@/screens/TrackerOAuthLogin.tsx';
|
||||||
|
import { WebUIUpdateChecker } from '@/components/util/WebUIUpdateChecker.tsx';
|
||||||
|
|
||||||
if (process.env.NODE_ENV !== 'production') {
|
if (process.env.NODE_ENV !== 'production') {
|
||||||
// Adds messages only in a dev environment
|
// Adds messages only in a dev environment
|
||||||
@@ -74,6 +75,7 @@ export const App: React.FC = () => (
|
|||||||
<AppContext>
|
<AppContext>
|
||||||
<ScrollToTop />
|
<ScrollToTop />
|
||||||
<ServerUpdateChecker />
|
<ServerUpdateChecker />
|
||||||
|
<WebUIUpdateChecker />
|
||||||
<BackgroundSubscriptions />
|
<BackgroundSubscriptions />
|
||||||
<CssBaseline />
|
<CssBaseline />
|
||||||
<DefaultNavBar />
|
<DefaultNavBar />
|
||||||
|
|||||||
125
src/components/util/WebUIUpdateChecker.tsx
Normal file
125
src/components/util/WebUIUpdateChecker.tsx
Normal file
@@ -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<string>('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<WebUiUpdateStatus, 'info'>;
|
||||||
|
|
||||||
|
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 (
|
||||||
|
<Dialog open={open}>
|
||||||
|
<DialogTitle>{t('settings.about.webui.label.updated')}</DialogTitle>
|
||||||
|
<DialogContent>
|
||||||
|
<DialogContentText>
|
||||||
|
{t('settings.about.webui.label.update_success', {
|
||||||
|
version: webUIVersion,
|
||||||
|
})}
|
||||||
|
</DialogContentText>
|
||||||
|
</DialogContent>
|
||||||
|
<DialogActions>
|
||||||
|
<Button href={changelogUrl} target="_blank">
|
||||||
|
{t('global.button.changelog')}
|
||||||
|
</Button>
|
||||||
|
<Button
|
||||||
|
onClick={() => {
|
||||||
|
setOpen(false);
|
||||||
|
window.location.reload();
|
||||||
|
}}
|
||||||
|
variant="contained"
|
||||||
|
>
|
||||||
|
{t('global.button.refresh')}
|
||||||
|
</Button>
|
||||||
|
</DialogActions>
|
||||||
|
</Dialog>
|
||||||
|
);
|
||||||
|
};
|
||||||
@@ -27,8 +27,6 @@ import { ListItemLink } from '@/components/util/ListItemLink';
|
|||||||
import { NavBarContext, useSetDefaultBackTo } from '@/components/context/NavbarContext';
|
import { NavBarContext, useSetDefaultBackTo } from '@/components/context/NavbarContext';
|
||||||
import { LoadingPlaceholder } from '@/components/util/LoadingPlaceholder';
|
import { LoadingPlaceholder } from '@/components/util/LoadingPlaceholder';
|
||||||
import { GetAboutQuery, UpdateState } from '@/lib/graphql/generated/graphql.ts';
|
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';
|
import { defaultPromiseErrorHandler } from '@/util/defaultPromiseErrorHandler.ts';
|
||||||
|
|
||||||
type AboutServer = GetAboutQuery['aboutServer'];
|
type AboutServer = GetAboutQuery['aboutServer'];
|
||||||
@@ -226,51 +224,6 @@ export function About() {
|
|||||||
progress: 0,
|
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) {
|
if (!aboutServer || !aboutWebUI) {
|
||||||
return <LoadingPlaceholder />;
|
return <LoadingPlaceholder />;
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user