diff --git a/src/features/campaigns/components/ActivitiesOverview/items/TaskOverviewListItem.tsx b/src/features/campaigns/components/ActivitiesOverview/items/TaskOverviewListItem.tsx index 1cd9ad5c5b..a911c5a854 100644 --- a/src/features/campaigns/components/ActivitiesOverview/items/TaskOverviewListItem.tsx +++ b/src/features/campaigns/components/ActivitiesOverview/items/TaskOverviewListItem.tsx @@ -3,8 +3,7 @@ import { CheckBoxOutlined, People } from '@mui/icons-material'; import OverviewListItem from './OverviewListItem'; import { TaskActivity } from 'features/campaigns/models/CampaignActivitiesModel'; -import TaskModel from 'features/tasks/models/TaskModel'; -import useModel from 'core/useModel'; +import useTaskStats from 'features/tasks/hooks/useTaskStats'; import ZUIStackedStatusBar from 'zui/ZUIStackedStatusBar'; interface TasksOverviewListItemProps { @@ -17,11 +16,7 @@ const TaskOverviewListItem: FC = ({ focusDate, }) => { const task = activity.data; - const model = useModel( - (env) => new TaskModel(env, task.organization.id, task.id) - ); - - const stats = model.getTaskStats().data; + const { data: stats } = useTaskStats(task.organization.id, task.id); return ( { - const model = useModel((env) => new TaskModel(env, orgId, taskId)); - const task = model.getTask().data; - const stats = model.getTaskStats().data; + const { data: task } = useTask(orgId, taskId); + const { data: stats, isLoading: statsLoading } = useTaskStats(orgId, taskId); if (!task) { return null; @@ -31,8 +30,6 @@ const TaskListItem = ({ orgId, taskId }: TaskListItemProps) => { color = STATUS_COLORS.BLUE; } - const statsLoading = model.getTaskStats().isLoading; - return ( state.tasks); + + const item = tasks.tasksList.items.find((item) => item.id === taskId); + + const taskFuture = loadItemIfNecessary(item, env.store, { + actionOnLoad: () => taskLoad(taskId), + actionOnSuccess: (data) => taskLoaded(data), + loader: () => + apiClient.get(`/api/orgs/${orgId}/tasks/${taskId}`), + }); + + return { + data: taskFuture.data, + }; +} diff --git a/src/features/tasks/hooks/useTaskStats.ts b/src/features/tasks/hooks/useTaskStats.ts new file mode 100644 index 0000000000..2e9d495034 --- /dev/null +++ b/src/features/tasks/hooks/useTaskStats.ts @@ -0,0 +1,34 @@ +import { useSelector } from 'react-redux'; + +import { loadItemIfNecessary } from 'core/caching/cacheUtils'; +import { RootState } from 'core/store'; +import getStats, { TaskStats } from '../rpc/getTaskStats'; +import { statsLoad, statsLoaded } from '../store'; +import { useApiClient, useEnv } from 'core/hooks'; + +interface UseTaskStatsReturn { + data: TaskStats | null; + isLoading: boolean; +} + +export default function useTaskStats( + orgId: number, + taskId: number +): UseTaskStatsReturn { + const apiClient = useApiClient(); + const env = useEnv(); + const tasks = useSelector((state: RootState) => state.tasks); + + const item = tasks.statsById[taskId!]; + + const taskStatsFuture = loadItemIfNecessary(item, env.store, { + actionOnLoad: () => statsLoad(taskId!), + actionOnSuccess: (data) => statsLoaded([taskId!, data]), + loader: () => apiClient.rpc(getStats, { orgId, taskId }), + }); + + return { + data: taskStatsFuture.data, + isLoading: taskStatsFuture.isLoading, + }; +} diff --git a/src/features/tasks/hooks/useTasks.ts b/src/features/tasks/hooks/useTasks.ts new file mode 100644 index 0000000000..cf338f9bef --- /dev/null +++ b/src/features/tasks/hooks/useTasks.ts @@ -0,0 +1,28 @@ +import { useSelector } from 'react-redux'; + +import { IFuture } from 'core/caching/futures'; +import { loadListIfNecessary } from 'core/caching/cacheUtils'; +import { RootState } from 'core/store'; +import { ZetkinTask } from '../components/types'; +import { tasksLoad, tasksLoaded } from '../store'; +import { useApiClient, useEnv } from 'core/hooks'; + +interface UseTasksReturn { + tasksFuture: IFuture; +} + +export default function useTasks(orgId: number): UseTasksReturn { + const taskList = useSelector((state: RootState) => state.tasks.tasksList); + const env = useEnv(); + const apiClient = useApiClient(); + + const tasksFuture = loadListIfNecessary(taskList, env.store, { + actionOnLoad: () => tasksLoad(), + actionOnSuccess: (data) => tasksLoaded(data), + loader: () => apiClient.get(`/api/orgs/${orgId}/tasks/`), + }); + + return { + tasksFuture, + }; +} diff --git a/src/features/tasks/models/TaskModel.ts b/src/features/tasks/models/TaskModel.ts deleted file mode 100644 index af9dad13b4..0000000000 --- a/src/features/tasks/models/TaskModel.ts +++ /dev/null @@ -1,27 +0,0 @@ -import Environment from 'core/env/Environment'; -import { IFuture } from 'core/caching/futures'; -import { ModelBase } from 'core/models'; -import TasksRepo from '../repos/TasksRepo'; -import { TaskStats } from '../rpc/getTaskStats'; -import { ZetkinTask } from '../components/types'; - -export default class TaskModel extends ModelBase { - private _orgId: number; - private _repo: TasksRepo; - private _taskId: number; - - constructor(env: Environment, orgId: number, taskId: number) { - super(); - this._orgId = orgId; - this._repo = new TasksRepo(env); - this._taskId = taskId; - } - - getTask(): IFuture { - return this._repo.getTask(this._orgId, this._taskId); - } - - getTaskStats(): IFuture { - return this._repo.getTaskStats(this._orgId, this._taskId); - } -} diff --git a/src/features/tasks/repos/TasksRepo.ts b/src/features/tasks/repos/TasksRepo.ts index f8b8c02f78..275b360fd1 100644 --- a/src/features/tasks/repos/TasksRepo.ts +++ b/src/features/tasks/repos/TasksRepo.ts @@ -1,21 +1,10 @@ import Environment from 'core/env/Environment'; import IApiClient from 'core/api/client/IApiClient'; import { IFuture } from 'core/caching/futures'; +import { loadListIfNecessary } from 'core/caching/cacheUtils'; import { Store } from 'core/store'; import { ZetkinTask } from '../components/types'; -import getStats, { TaskStats } from '../rpc/getTaskStats'; -import { - loadItemIfNecessary, - loadListIfNecessary, -} from 'core/caching/cacheUtils'; -import { - statsLoad, - statsLoaded, - taskLoad, - taskLoaded, - tasksLoad, - tasksLoaded, -} from '../store'; +import { tasksLoad, tasksLoaded } from '../store'; export default class TasksRepo { private _apiClient: IApiClient; @@ -26,28 +15,6 @@ export default class TasksRepo { this._store = env.store; } - getTask(orgId: number, taskId: number): IFuture { - const state = this._store.getState(); - const item = state.tasks.tasksList.items.find((item) => item.id === taskId); - - return loadItemIfNecessary(item, this._store, { - actionOnLoad: () => taskLoad(taskId), - actionOnSuccess: (data) => taskLoaded(data), - loader: () => - this._apiClient.get(`/api/orgs/${orgId}/tasks/${taskId}`), - }); - } - - getTaskStats(orgId: number, taskId: number): IFuture { - const state = this._store.getState(); - const item = state.tasks.statsById[taskId]; - return loadItemIfNecessary(item, this._store, { - actionOnLoad: () => statsLoad(taskId), - actionOnSuccess: (data) => statsLoaded([taskId, data]), - loader: () => this._apiClient.rpc(getStats, { orgId, taskId }), - }); - } - getTasks(orgId: number): IFuture { const state = this._store.getState(); const taskList = state.tasks.tasksList;