feat: show alert/prompt when settings changes require restart (#2401)

* fix: correct 'StatusChecker' typo

* feat: add restart required check to StatusChecker

* fix(perms): remove MANAGE_SETTINGS permission

* fix: allow alert to be dismissed

* fix(lang): add missing string in SettingsServices

* fix(frontend): fix modal icon border

* fix(frontend): un-dismiss alert if setting reverted not require server restart

* fix(backend): restart flag only needs to track main settings

* fix: rebase issue

* refactor: appease Prettier

* refactor: swap settings badge order

* fix: type import for MainSettings

* test: add cypress test for restart prompt
This commit is contained in:
TheCatLady
2022-08-16 09:58:11 -07:00
committed by GitHub
parent 70dc4c4b3b
commit f3e56da3b7
40 changed files with 239 additions and 140 deletions

View File

@@ -0,0 +1,94 @@
import { RefreshIcon, SparklesIcon } from '@heroicons/react/outline';
import React, { useEffect, useState } from 'react';
import { defineMessages, useIntl } from 'react-intl';
import useSWR from 'swr';
import type { StatusResponse } from '../../../server/interfaces/api/settingsInterfaces';
import useSettings from '../../hooks/useSettings';
import { Permission, useUser } from '../../hooks/useUser';
import globalMessages from '../../i18n/globalMessages';
import Modal from '../Common/Modal';
import Transition from '../Transition';
const messages = defineMessages({
appUpdated: '{applicationTitle} Updated',
appUpdatedDescription:
'Please click the button below to reload the application.',
reloadApp: 'Reload {applicationTitle}',
restartRequired: 'Server Restart Required',
restartRequiredDescription:
'Please restart the server to apply the updated settings.',
});
const StatusChecker: React.FC = () => {
const intl = useIntl();
const settings = useSettings();
const { hasPermission } = useUser();
const { data, error } = useSWR<StatusResponse>('/api/v1/status', {
refreshInterval: 60 * 1000,
});
const [alertDismissed, setAlertDismissed] = useState(false);
useEffect(() => {
if (!data?.restartRequired) {
setAlertDismissed(false);
}
}, [data?.restartRequired]);
if (!data && !error) {
return null;
}
if (!data) {
return null;
}
return (
<Transition
enter="opacity-0 transition duration-300"
enterFrom="opacity-0"
enterTo="opacity-100"
leave="opacity-100 transition duration-300"
leaveFrom="opacity-100"
leaveTo="opacity-0"
appear
show={
!alertDismissed &&
((hasPermission(Permission.ADMIN) && data.restartRequired) ||
data.commitTag !== process.env.commitTag)
}
>
{hasPermission(Permission.ADMIN) && data.restartRequired ? (
<Modal
iconSvg={<RefreshIcon />}
title={intl.formatMessage(messages.restartRequired)}
backgroundClickable={false}
onOk={() => {
setAlertDismissed(true);
if (data.commitTag !== process.env.commitTag) {
location.reload();
}
}}
okText={intl.formatMessage(globalMessages.close)}
>
{intl.formatMessage(messages.restartRequiredDescription)}
</Modal>
) : (
<Modal
iconSvg={<SparklesIcon />}
title={intl.formatMessage(messages.appUpdated, {
applicationTitle: settings.currentSettings.applicationTitle,
})}
onOk={() => location.reload()}
okText={intl.formatMessage(messages.reloadApp, {
applicationTitle: settings.currentSettings.applicationTitle,
})}
backgroundClickable={false}
>
{intl.formatMessage(messages.appUpdatedDescription)}
</Modal>
)}
</Transition>
);
};
export default StatusChecker;