From 3091f40ca813b7d72555d6e2dadbdd710a32f01a Mon Sep 17 00:00:00 2001 From: Mark McDowall Date: Fri, 7 Nov 2025 20:03:16 -0800 Subject: [PATCH] Use react-query for tasks --- frontend/src/App/State/SystemAppState.ts | 5 --- frontend/src/Components/SignalRListener.tsx | 6 ++- frontend/src/Store/Actions/systemActions.js | 16 -------- .../Tasks/Scheduled/ScheduledTaskRow.tsx | 39 +++++-------------- .../System/Tasks/Scheduled/ScheduledTasks.tsx | 31 ++++++++------- frontend/src/System/Tasks/useTasks.ts | 15 +++++++ 6 files changed, 46 insertions(+), 66 deletions(-) create mode 100644 frontend/src/System/Tasks/useTasks.ts diff --git a/frontend/src/App/State/SystemAppState.ts b/frontend/src/App/State/SystemAppState.ts index f83142ab7..a6d16f452 100644 --- a/frontend/src/App/State/SystemAppState.ts +++ b/frontend/src/App/State/SystemAppState.ts @@ -1,12 +1,7 @@ -import Task from 'typings/Task'; -import AppSectionState from './AppSectionState'; import BackupAppState from './BackupAppState'; -export type TaskAppState = AppSectionState; - interface SystemAppState { backups: BackupAppState; - tasks: TaskAppState; } export default SystemAppState; diff --git a/frontend/src/Components/SignalRListener.tsx b/frontend/src/Components/SignalRListener.tsx index 9dccbbe56..41dcb54cb 100644 --- a/frontend/src/Components/SignalRListener.tsx +++ b/frontend/src/Components/SignalRListener.tsx @@ -292,7 +292,11 @@ function SignalRListener() { } if (name === 'system/task') { - dispatch(fetchCommands()); + if (version < 5) { + return; + } + + queryClient.invalidateQueries({ queryKey: ['/system/task'] }); return; } diff --git a/frontend/src/Store/Actions/systemActions.js b/frontend/src/Store/Actions/systemActions.js index 1ed69ef1a..5ae534ccc 100644 --- a/frontend/src/Store/Actions/systemActions.js +++ b/frontend/src/Store/Actions/systemActions.js @@ -18,13 +18,6 @@ const backupsSection = 'system.backups'; // State export const defaultState = { - tasks: { - isFetching: false, - isPopulated: false, - error: null, - items: [] - }, - backups: { isFetching: false, isPopulated: false, @@ -40,9 +33,6 @@ export const defaultState = { // // Actions Types -export const FETCH_TASK = 'system/tasks/fetchTask'; -export const FETCH_TASKS = 'system/tasks/fetchTasks'; - export const FETCH_BACKUPS = 'system/backups/fetchBackups'; export const RESTORE_BACKUP = 'system/backups/restoreBackup'; export const CLEAR_RESTORE_BACKUP = 'system/backups/clearRestoreBackup'; @@ -54,9 +44,6 @@ export const SHUTDOWN = 'system/shutdown'; // // Action Creators -export const fetchTask = createThunk(FETCH_TASK); -export const fetchTasks = createThunk(FETCH_TASKS); - export const fetchBackups = createThunk(FETCH_BACKUPS); export const restoreBackup = createThunk(RESTORE_BACKUP); export const clearRestoreBackup = createAction(CLEAR_RESTORE_BACKUP); @@ -69,9 +56,6 @@ export const shutdown = createThunk(SHUTDOWN); // Action Handlers export const actionHandlers = handleThunks({ - [FETCH_TASK]: createFetchHandler('system.tasks', '/system/task'), - [FETCH_TASKS]: createFetchHandler('system.tasks', '/system/task'), - [FETCH_BACKUPS]: createFetchHandler(backupsSection, '/system/backup'), [RESTORE_BACKUP]: function(getState, payload, dispatch) { diff --git a/frontend/src/System/Tasks/Scheduled/ScheduledTaskRow.tsx b/frontend/src/System/Tasks/Scheduled/ScheduledTaskRow.tsx index 3a3cd02de..08716240a 100644 --- a/frontend/src/System/Tasks/Scheduled/ScheduledTaskRow.tsx +++ b/frontend/src/System/Tasks/Scheduled/ScheduledTaskRow.tsx @@ -4,10 +4,8 @@ import { useDispatch, useSelector } from 'react-redux'; import SpinnerIconButton from 'Components/Link/SpinnerIconButton'; import TableRowCell from 'Components/Table/Cells/TableRowCell'; import TableRow from 'Components/Table/TableRow'; -import usePrevious from 'Helpers/Hooks/usePrevious'; import { icons } from 'Helpers/Props'; import { executeCommand } from 'Store/Actions/commandActions'; -import { fetchTask } from 'Store/Actions/systemActions'; import createCommandSelector from 'Store/Selectors/createCommandSelector'; import createUISettingsSelector from 'Store/Selectors/createUISettingsSelector'; import { isCommandExecuting } from 'Utilities/Command'; @@ -27,20 +25,16 @@ interface ScheduledTaskRowProps { nextExecution: string; } -function ScheduledTaskRow(props: ScheduledTaskRowProps) { - const { - id, - taskName, - name, - interval, - lastExecution, - lastStartTime, - lastDuration, - nextExecution, - } = props; - +function ScheduledTaskRow({ + taskName, + name, + interval, + lastExecution, + lastStartTime, + lastDuration, + nextExecution, +}: ScheduledTaskRowProps) { const dispatch = useDispatch(); - const { showRelativeDates, longDateFormat, shortDateFormat, timeFormat } = useSelector(createUISettingsSelector()); const command = useSelector(createCommandSelector(taskName)); @@ -49,7 +43,6 @@ function ScheduledTaskRow(props: ScheduledTaskRowProps) { const isQueued = !!(command && command.status === 'queued'); const isExecuting = isCommandExecuting(command); - const wasExecuting = usePrevious(isExecuting); const isDisabled = interval === 0; const executeNow = !isDisabled && moment().isAfter(nextExecution); const hasNextExecutionTime = !isDisabled && !executeNow; @@ -88,21 +81,9 @@ function ScheduledTaskRow(props: ScheduledTaskRowProps) { ]); const handleExecutePress = useCallback(() => { - dispatch( - executeCommand({ - name: taskName, - }) - ); + dispatch(executeCommand({ name: taskName })); }, [taskName, dispatch]); - useEffect(() => { - if (!isExecuting && wasExecuting) { - setTimeout(() => { - dispatch(fetchTask({ id })); - }, 1000); - } - }, [id, isExecuting, wasExecuting, dispatch]); - useEffect(() => { const interval = setInterval(() => setTime(Date.now()), 1000); return () => { diff --git a/frontend/src/System/Tasks/Scheduled/ScheduledTasks.tsx b/frontend/src/System/Tasks/Scheduled/ScheduledTasks.tsx index fcf5764bb..542ba02c7 100644 --- a/frontend/src/System/Tasks/Scheduled/ScheduledTasks.tsx +++ b/frontend/src/System/Tasks/Scheduled/ScheduledTasks.tsx @@ -1,13 +1,11 @@ -import React, { useEffect } from 'react'; -import { useDispatch, useSelector } from 'react-redux'; -import AppState from 'App/State/AppState'; +import React from 'react'; import FieldSet from 'Components/FieldSet'; import LoadingIndicator from 'Components/Loading/LoadingIndicator'; import Column from 'Components/Table/Column'; import Table from 'Components/Table/Table'; import TableBody from 'Components/Table/TableBody'; -import { fetchTasks } from 'Store/Actions/systemActions'; import translate from 'Utilities/String/translate'; +import useTasks from '../useTasks'; import ScheduledTaskRow from './ScheduledTaskRow'; const columns: Column[] = [ @@ -44,28 +42,31 @@ const columns: Column[] = [ ]; function ScheduledTasks() { - const dispatch = useDispatch(); - const { isFetching, isPopulated, items } = useSelector( - (state: AppState) => state.system.tasks - ); + const { data: tasks, isLoading, error } = useTasks(); - useEffect(() => { - dispatch(fetchTasks()); - }, [dispatch]); + if (error) { + return ( +
+
Error loading tasks: {error.message}
+
+ ); + } return (
- {isFetching && !isPopulated && } + {isLoading && } - {isPopulated && ( + {tasks.length > 0 && ( - {items.map((item) => { - return ; + {tasks.map((task) => { + return ; })}
)} + + {!isLoading && tasks.length === 0 &&
No scheduled tasks found.
}
); } diff --git a/frontend/src/System/Tasks/useTasks.ts b/frontend/src/System/Tasks/useTasks.ts new file mode 100644 index 000000000..7dcd12dbc --- /dev/null +++ b/frontend/src/System/Tasks/useTasks.ts @@ -0,0 +1,15 @@ +import useApiQuery from 'Helpers/Hooks/useApiQuery'; +import Task from 'typings/Task'; + +const useTasks = () => { + const result = useApiQuery({ + path: '/system/task', + }); + + return { + ...result, + data: result.data ?? [], + }; +}; + +export default useTasks;