Use react-query for System Status

This commit is contained in:
Mark McDowall 2025-10-12 13:16:17 +09:00
parent fc0c26c2b3
commit 49c52c2e1a
24 changed files with 95 additions and 115 deletions

View file

@ -25,7 +25,7 @@ import { SeriesType } from 'Series/Series';
import SeriesPoster from 'Series/SeriesPoster';
import createDimensionsSelector from 'Store/Selectors/createDimensionsSelector';
import selectSettings from 'Store/Selectors/selectSettings';
import useIsWindows from 'System/useIsWindows';
import { useIsWindows } from 'System/Status/useSystemStatus';
import { InputChanged } from 'typings/inputs';
import translate from 'Utilities/String/translate';
import { useAddSeries } from './useAddSeries';

View file

@ -17,7 +17,7 @@ import {
addRootFolder,
fetchRootFolders,
} from 'Store/Actions/rootFolderActions';
import useIsWindows from 'System/useIsWindows';
import { useIsWindows } from 'System/Status/useSystemStatus';
import { InputChanged } from 'typings/inputs';
import translate from 'Utilities/String/translate';
import styles from './ImportSeriesSelectFolder.css';

View file

@ -1,14 +1,12 @@
import DiskSpace from 'typings/DiskSpace';
import Health from 'typings/Health';
import LogFile from 'typings/LogFile';
import SystemStatus from 'typings/SystemStatus';
import Task from 'typings/Task';
import AppSectionState, { AppSectionItemState } from './AppSectionState';
import AppSectionState from './AppSectionState';
import BackupAppState from './BackupAppState';
export type DiskSpaceAppState = AppSectionState<DiskSpace>;
export type HealthAppState = AppSectionState<Health>;
export type SystemStatusAppState = AppSectionItemState<SystemStatus>;
export type TaskAppState = AppSectionState<Task>;
export type LogFilesAppState = AppSectionState<LogFile>;
@ -17,7 +15,6 @@ interface SystemAppState {
diskSpace: DiskSpaceAppState;
health: HealthAppState;
logFiles: LogFilesAppState;
status: SystemStatusAppState;
tasks: TaskAppState;
updateLogFiles: LogFilesAppState;
}

View file

@ -16,7 +16,7 @@ import TableBody from 'Components/Table/TableBody';
import usePrevious from 'Helpers/Hooks/usePrevious';
import { kinds, scrollDirections } from 'Helpers/Props';
import { clearPaths, fetchPaths } from 'Store/Actions/pathActions';
import createSystemStatusSelector from 'Store/Selectors/createSystemStatusSelector';
import { useSystemStatusData } from 'System/Status/useSystemStatus';
import { InputChanged } from 'typings/inputs';
import translate from 'Utilities/String/translate';
import createPathsSelector from './createPathsSelector';
@ -51,7 +51,8 @@ function FileBrowserModalContent(props: FileBrowserModalContentProps) {
const dispatch = useDispatch();
const { isWindows, mode } = useSelector(createSystemStatusSelector());
const { isWindows, mode } = useSystemStatusData();
const { isFetching, isPopulated, error, parent, directories, files, paths } =
useSelector(createPathsSelector());

View file

@ -1,5 +1,6 @@
import React from 'react';
import { Error } from 'App/State/AppSectionState';
import { ApiError } from 'Utilities/Fetch/fetchJson';
import getErrorMessage from 'Utilities/Object/getErrorMessage';
import translate from 'Utilities/String/translate';
import styles from './ErrorPage.css';
@ -13,7 +14,7 @@ interface ErrorPageProps {
tagsError?: Error;
qualityProfilesError?: Error;
uiSettingsError?: Error;
systemStatusError?: Error;
systemStatusError: ApiError | null;
}
function ErrorPage(props: ErrorPageProps) {

View file

@ -1,12 +1,11 @@
import React from 'react';
import { useSelector } from 'react-redux';
import { Shortcut, shortcuts } from 'Components/keyboardShortcuts';
import Button from 'Components/Link/Button';
import ModalBody from 'Components/Modal/ModalBody';
import ModalContent from 'Components/Modal/ModalContent';
import ModalFooter from 'Components/Modal/ModalFooter';
import ModalHeader from 'Components/Modal/ModalHeader';
import createSystemStatusSelector from 'Store/Selectors/createSystemStatusSelector';
import { useSystemStatusData } from 'System/Status/useSystemStatus';
import translate from 'Utilities/String/translate';
import styles from './KeyboardShortcutsModalContent.css';
@ -45,7 +44,7 @@ interface KeyboardShortcutsModalContentProps {
function KeyboardShortcutsModalContent({
onModalClose,
}: KeyboardShortcutsModalContentProps) {
const { isOsx } = useSelector(createSystemStatusSelector());
const { isOsx } = useSystemStatusData();
const allShortcuts = getShortcuts();
return (

View file

@ -1,6 +1,5 @@
import React, { useCallback } from 'react';
import { useDispatch, useSelector } from 'react-redux';
import AppState from 'App/State/AppState';
import { useDispatch } from 'react-redux';
import Icon from 'Components/Icon';
import Menu from 'Components/Menu/Menu';
import MenuButton from 'Components/Menu/MenuButton';
@ -9,6 +8,7 @@ import MenuItem from 'Components/Menu/MenuItem';
import MenuItemSeparator from 'Components/Menu/MenuItemSeparator';
import { align, icons, kinds } from 'Helpers/Props';
import { restart, shutdown } from 'Store/Actions/systemActions';
import { useSystemStatusData } from 'System/Status/useSystemStatus';
import translate from 'Utilities/String/translate';
import styles from './PageHeaderActionsMenu.css';
@ -20,10 +20,7 @@ function PageHeaderActionsMenu(props: PageHeaderActionsMenuProps) {
const { onKeyboardShortcutsPress } = props;
const dispatch = useDispatch();
const { authentication, isDocker } = useSelector(
(state: AppState) => state.system.status.item
);
const { authentication, isDocker } = useSystemStatusData();
const formsAuth = authentication === 'forms';

View file

@ -9,8 +9,8 @@ import AuthenticationRequiredModal from 'FirstRun/AuthenticationRequiredModal';
import useAppPage from 'Helpers/Hooks/useAppPage';
import { saveDimensions } from 'Store/Actions/appActions';
import createDimensionsSelector from 'Store/Selectors/createDimensionsSelector';
import createSystemStatusSelector from 'Store/Selectors/createSystemStatusSelector';
import createUISettingsSelector from 'Store/Selectors/createUISettingsSelector';
import { useSystemStatusData } from 'System/Status/useSystemStatus';
import ErrorPage from './ErrorPage';
import PageHeader from './Header/PageHeader';
import LoadingPage from './LoadingPage';
@ -31,7 +31,8 @@ function Page({ children }: PageProps) {
const { enableColorImpairedMode } = useSelector(createUISettingsSelector());
const { isSmallScreen } = useSelector(createDimensionsSelector());
const { authentication } = useSelector(createSystemStatusSelector());
const { authentication } = useSystemStatusData();
const authenticationEnabled = authentication !== 'none';
const { isSidebarVisible, isUpdated, isDisconnected, version } = useSelector(
(state: AppState) => state.app

View file

@ -22,8 +22,8 @@ import {
saveGeneralSettings,
setGeneralSettingsValue,
} from 'Store/Actions/settingsActions';
import { fetchStatus } from 'Store/Actions/systemActions';
import createSettingsSectionSelector from 'Store/Selectors/createSettingsSectionSelector';
import useSystemStatus from 'System/Status/useSystemStatus';
import { InputChanged } from 'typings/inputs';
import translate from 'Utilities/String/translate';
import styles from './AuthenticationRequiredModalContent.css';
@ -39,6 +39,7 @@ function onModalClose() {
export default function AuthenticationRequiredModalContent() {
const { isPopulated, error, isSaving, settings } = useSelector(selector);
const dispatch = useDispatch();
const { refetch: refetchStatus } = useSystemStatus();
const {
authenticationMethod,
@ -74,8 +75,8 @@ export default function AuthenticationRequiredModalContent() {
return;
}
dispatch(fetchStatus());
}, [isSaving, wasSaving, dispatch]);
refetchStatus();
}, [isSaving, wasSaving, refetchStatus]);
const onPress = useCallback(() => {
dispatch(saveGeneralSettings());

View file

@ -12,10 +12,15 @@ import {
fetchQualityProfiles,
fetchUISettings,
} from 'Store/Actions/settingsActions';
import { fetchStatus } from 'Store/Actions/systemActions';
import { fetchTags } from 'Store/Actions/tagActions';
import useSystemStatus from 'System/Status/useSystemStatus';
import { ApiError } from 'Utilities/Fetch/fetchJson';
const createErrorsSelector = () =>
const createErrorsSelector = ({
systemStatusError,
}: {
systemStatusError: ApiError | null;
}) =>
createSelector(
(state: AppState) => state.series.error,
(state: AppState) => state.customFilters.error,
@ -25,7 +30,6 @@ const createErrorsSelector = () =>
(state: AppState) => state.settings.languages.error,
(state: AppState) => state.settings.importLists.error,
(state: AppState) => state.settings.indexerFlags.error,
(state: AppState) => state.system.status.error,
(state: AppState) => state.app.translations.error,
(
seriesError,
@ -36,7 +40,6 @@ const createErrorsSelector = () =>
languagesError,
importListsError,
indexerFlagsError,
systemStatusError,
translationsError
) => {
const hasError = !!(
@ -72,23 +75,27 @@ const createErrorsSelector = () =>
const useAppPage = () => {
const dispatch = useDispatch();
const { isFetched: isSystemStatusFetched, error: systemStatusError } =
useSystemStatus();
const isPopulated = useSelector(
(state: AppState) =>
state.series.isPopulated &&
state.customFilters.isPopulated &&
state.tags.isPopulated &&
state.settings.ui.isPopulated &&
state.settings.qualityProfiles.isPopulated &&
state.settings.languages.isPopulated &&
state.settings.importLists.isPopulated &&
state.settings.indexerFlags.isPopulated &&
state.system.status.isPopulated &&
state.app.translations.isPopulated
const isPopulated =
useSelector(
(state: AppState) =>
state.series.isPopulated &&
state.customFilters.isPopulated &&
state.tags.isPopulated &&
state.settings.ui.isPopulated &&
state.settings.qualityProfiles.isPopulated &&
state.settings.languages.isPopulated &&
state.settings.importLists.isPopulated &&
state.settings.indexerFlags.isPopulated &&
state.app.translations.isPopulated
) && isSystemStatusFetched;
const { hasError, errors } = useSelector(
createErrorsSelector({ systemStatusError })
);
const { hasError, errors } = useSelector(createErrorsSelector());
const isLocalStorageSupported = useMemo(() => {
const key = 'sonarrTest';
@ -111,7 +118,6 @@ const useAppPage = () => {
dispatch(fetchImportLists());
dispatch(fetchIndexerFlags());
dispatch(fetchUISettings());
dispatch(fetchStatus());
dispatch(fetchTranslations());
}, [dispatch]);

View file

@ -1,5 +1,4 @@
import React, { useCallback, useState } from 'react';
import { useSelector } from 'react-redux';
import FormGroup from 'Components/Form/FormGroup';
import FormInputGroup from 'Components/Form/FormInputGroup';
import FormLabel from 'Components/Form/FormLabel';
@ -10,7 +9,7 @@ import ModalFooter from 'Components/Modal/ModalFooter';
import ModalHeader from 'Components/Modal/ModalHeader';
import useApiQuery from 'Helpers/Hooks/useApiQuery';
import { inputTypes } from 'Helpers/Props';
import createSystemStatusSelector from 'Store/Selectors/createSystemStatusSelector';
import { useIsWindows } from 'System/Status/useSystemStatus';
import { InputChanged } from 'typings/inputs';
import translate from 'Utilities/String/translate';
@ -32,7 +31,7 @@ interface SeriesFolder {
function RootFolderModalContent(props: RootFolderModalContentProps) {
const { seriesId, onSavePress, onModalClose } = props;
const { isWindows } = useSelector(createSystemStatusSelector());
const isWindows = useIsWindows();
const [rootFolderPath, setRootFolderPath] = useState(props.rootFolderPath);

View file

@ -19,7 +19,7 @@ import {
import { restart } from 'Store/Actions/systemActions';
import createCommandExecutingSelector from 'Store/Selectors/createCommandExecutingSelector';
import createSettingsSectionSelector from 'Store/Selectors/createSettingsSectionSelector';
import useIsWindowsService from 'System/useIsWindowsService';
import { useIsWindowsService } from 'System/Status/useSystemStatus';
import { InputChanged } from 'typings/inputs';
import translate from 'Utilities/String/translate';
import AnalyticSettings from './AnalyticSettings';

View file

@ -5,7 +5,7 @@ import FormInputGroup from 'Components/Form/FormInputGroup';
import FormLabel from 'Components/Form/FormLabel';
import useShowAdvancedSettings from 'Helpers/Hooks/useShowAdvancedSettings';
import { inputTypes, sizes } from 'Helpers/Props';
import useIsWindowsService from 'System/useIsWindowsService';
import { useIsWindowsService } from 'System/Status/useSystemStatus';
import { InputChanged } from 'typings/inputs';
import { PendingSection } from 'typings/pending';
import General from 'typings/Settings/General';

View file

@ -6,7 +6,7 @@ import FormLabel from 'Components/Form/FormLabel';
import { EnhancedSelectInputValue } from 'Components/Form/Select/EnhancedSelectInput';
import useShowAdvancedSettings from 'Helpers/Hooks/useShowAdvancedSettings';
import { inputTypes, sizes } from 'Helpers/Props';
import useSystemStatus from 'System/useSystemStatus';
import { useSystemStatusData } from 'System/Status/useSystemStatus';
import { InputChanged } from 'typings/inputs';
import { PendingSection } from 'typings/pending';
import General from 'typings/Settings/General';
@ -31,7 +31,7 @@ function UpdateSettings({
onInputChange,
}: UpdateSettingsProps) {
const showAdvancedSettings = useShowAdvancedSettings();
const { packageUpdateMechanism } = useSystemStatus();
const { packageUpdateMechanism } = useSystemStatusData();
if (!showAdvancedSettings) {
return null;

View file

@ -23,7 +23,7 @@ import {
setMediaManagementSettingsValue,
} from 'Store/Actions/settingsActions';
import createSettingsSectionSelector from 'Store/Selectors/createSettingsSectionSelector';
import useIsWindows from 'System/useIsWindows';
import { useIsWindows } from 'System/Status/useSystemStatus';
import { InputChanged } from 'typings/inputs';
import isEmpty from 'Utilities/Object/isEmpty';
import translate from 'Utilities/String/translate';

View file

@ -18,13 +18,6 @@ const backupsSection = 'system.backups';
// State
export const defaultState = {
status: {
isFetching: false,
isPopulated: false,
error: null,
item: {}
},
health: {
isFetching: false,
isPopulated: false,
@ -68,7 +61,6 @@ export const defaultState = {
//
// Actions Types
export const FETCH_STATUS = 'system/status/fetchStatus';
export const FETCH_HEALTH = 'system/health/fetchHealth';
export const FETCH_DISK_SPACE = 'system/diskSpace/fetchDiskSPace';
@ -86,7 +78,6 @@ export const SHUTDOWN = 'system/shutdown';
//
// Action Creators
export const fetchStatus = createThunk(FETCH_STATUS);
export const fetchHealth = createThunk(FETCH_HEALTH);
export const fetchDiskSpace = createThunk(FETCH_DISK_SPACE);
@ -105,7 +96,6 @@ export const shutdown = createThunk(SHUTDOWN);
// Action Handlers
export const actionHandlers = handleThunks({
[FETCH_STATUS]: createFetchHandler('system.status', '/system/status'),
[FETCH_HEALTH]: createFetchHandler('system.health', '/health'),
[FETCH_DISK_SPACE]: createFetchHandler('system.diskSpace', '/diskspace'),
[FETCH_TASK]: createFetchHandler('system.tasks', '/system/task'),

View file

@ -1,13 +0,0 @@
import { createSelector } from 'reselect';
import AppState from 'App/State/AppState';
function createSystemStatusSelector() {
return createSelector(
(state: AppState) => state.system.status,
(status) => {
return status.item;
}
);
}
export default createSystemStatusSelector;

View file

@ -1,6 +1,4 @@
import React from 'react';
import { useSelector } from 'react-redux';
import AppState from 'App/State/AppState';
import Alert from 'Components/Alert';
import LoadingIndicator from 'Components/Loading/LoadingIndicator';
import InlineMarkdown from 'Components/Markdown/InlineMarkdown';
@ -14,6 +12,7 @@ import Column from 'Components/Table/Column';
import Table from 'Components/Table/Table';
import TableBody from 'Components/Table/TableBody';
import { icons, kinds } from 'Helpers/Props';
import { useSystemStatusData } from 'System/Status/useSystemStatus';
import LogFile from 'typings/LogFile';
import combinePath from 'Utilities/String/combinePath';
import translate from 'Utilities/String/translate';
@ -58,9 +57,7 @@ function LogFiles({
onDeleteFilesPress,
...otherProps
}: LogFilesProps) {
const { appData, isWindows } = useSelector(
(state: AppState) => state.system.status.item
);
const { appData, isWindows } = useSystemStatusData();
const currentLogView =
type === 'update' ? translate('UpdaterLogFiles') : translate('LogFiles');

View file

@ -1,19 +1,16 @@
import React, { useEffect } from 'react';
import { useDispatch, useSelector } from 'react-redux';
import AppState from 'App/State/AppState';
import DescriptionList from 'Components/DescriptionList/DescriptionList';
import DescriptionListItem from 'Components/DescriptionList/DescriptionListItem';
import FieldSet from 'Components/FieldSet';
import InlineMarkdown from 'Components/Markdown/InlineMarkdown';
import { fetchStatus } from 'Store/Actions/systemActions';
import titleCase from 'Utilities/String/titleCase';
import translate from 'Utilities/String/translate';
import useSystemStatus from '../useSystemStatus';
import StartTime from './StartTime';
import styles from './About.css';
function About() {
const dispatch = useDispatch();
const { item } = useSelector((state: AppState) => state.system.status);
const { data, refetch } = useSystemStatus();
const {
version,
@ -29,11 +26,11 @@ function About() {
startupPath,
mode,
startTime,
} = item;
} = data;
useEffect(() => {
dispatch(fetchStatus());
}, [dispatch]);
refetch();
}, [refetch]);
return (
<FieldSet legend={translate('About')}>

View file

@ -0,0 +1,33 @@
import useApiQuery from 'Helpers/Hooks/useApiQuery';
import SystemStatus from 'typings/SystemStatus';
const useSystemStatus = () => {
const result = useApiQuery<SystemStatus>({
path: '/system/status',
});
return {
...result,
data: result.data ?? ({} as SystemStatus),
};
};
export default useSystemStatus;
export const useSystemStatusData = () => {
const { data } = useSystemStatus();
return data;
};
export const useIsWindows = () => {
const { isWindows } = useSystemStatusData();
return isWindows;
};
export const useIsWindowsService = () => {
const { isWindows, mode } = useSystemStatusData();
return isWindows && mode === 'service';
};

View file

@ -16,8 +16,8 @@ import useUpdateSettings from 'Settings/General/useUpdateSettings';
import { executeCommand } from 'Store/Actions/commandActions';
import { fetchGeneralSettings } from 'Store/Actions/settingsActions';
import createCommandExecutingSelector from 'Store/Selectors/createCommandExecutingSelector';
import createSystemStatusSelector from 'Store/Selectors/createSystemStatusSelector';
import createUISettingsSelector from 'Store/Selectors/createUISettingsSelector';
import { useSystemStatusData } from 'System/Status/useSystemStatus';
import { UpdateMechanism } from 'typings/Settings/General';
import formatDate from 'Utilities/Date/formatDate';
import formatDateTime from 'Utilities/Date/formatDateTime';
@ -30,9 +30,8 @@ const VERSION_REGEX = /\d+\.\d+\.\d+\.\d+/i;
function Updates() {
const currentVersion = useSelector((state: AppState) => state.app.version);
const { packageUpdateMechanismMessage } = useSelector(
createSystemStatusSelector()
);
const { packageUpdateMechanismMessage } = useSystemStatusData();
const { shortDateFormat, longDateFormat, timeFormat } = useSelector(
createUISettingsSelector()
);

View file

@ -1,8 +0,0 @@
import { useSelector } from 'react-redux';
import AppState from 'App/State/AppState';
function useIsWindows() {
return useSelector((state: AppState) => state.system.status.item.isWindows);
}
export default useIsWindows;

View file

@ -1,9 +0,0 @@
import useSystemStatus from './useSystemStatus';
function useIsWindowsService() {
const { isWindows, mode } = useSystemStatus();
return isWindows && mode === 'service';
}
export default useIsWindowsService;

View file

@ -1,8 +0,0 @@
import { useSelector } from 'react-redux';
import createSystemStatusSelector from 'Store/Selectors/createSystemStatusSelector';
function useSystemStatus() {
return useSelector(createSystemStatusSelector());
}
export default useSystemStatus;