(null);
-
- const taskType = useMemo(() => {
- return TaskInfos.find((taskType) => taskType.type === task.type);
- }, [task.type]);
-
- const { trigger: sendRejection } = useSWRMutation("/api/reject_task", post, {
- onSuccess: async () => {
- skipTask();
- },
- });
-
- const rejectTask = (reason: string) => {
- sendRejection({
- id: frontendId,
- reason,
- });
- };
+ }, [task.id]);
const onReplyChanged = useCallback(
(content: TaskContent) => {
@@ -150,25 +129,21 @@ export const Task = ({ frontendId, task, isLoading, completeTask, skipTask }: Ta
[replyContent]
);
- const submitResponse = () => {
+ const submitResponse = useCallback(() => {
if (taskStatus.mode === "REVIEW") {
- completeTask({
- id: frontendId,
- update_type: taskType.update_type,
- content: replyContent.current,
- });
+ completeTask(replyContent.current);
taskEvent({ action: "SET_SUBMITTED" });
scrollToTop(rootEl.current);
}
- };
+ }, [taskStatus.mode, completeTask]);
const taskTypeComponent = useMemo(() => {
- switch (taskType.category) {
+ switch (taskInfo.category) {
case TaskCategory.Create:
return (
);
}
- }, [task, taskType, taskStatus.mode, onReplyChanged, updateValidity]);
+ }, [taskInfo, task, taskStatus.mode, onReplyChanged, updateValidity]);
return (
@@ -214,8 +189,8 @@ export const Task = ({ frontendId, task, isLoading, completeTask, skipTask }: Ta
/>
taskEvent({ action: "RETURN_EDIT" })}
onContinueAnyway={() => {
diff --git a/website/src/components/Tasks/TaskTypes.tsx b/website/src/components/Tasks/TaskTypes.tsx
index e2a53d23..4f204720 100644
--- a/website/src/components/Tasks/TaskTypes.tsx
+++ b/website/src/components/Tasks/TaskTypes.tsx
@@ -1,28 +1,4 @@
-import { TaskType } from "src/types/Task";
-
-export enum TaskCategory {
- Create = "Create",
- Evaluate = "Evaluate",
- Label = "Label",
- Random = "Random",
-}
-
-export enum TaskUpdateType {
- MessageRanking = "message_ranking",
- Random = "random",
- TextLabels = "text_labels",
- TextReplyToMessage = "text_reply_to_message",
-}
-
-export interface TaskInfo {
- category: TaskCategory;
- help_link: string;
- id: string;
- mode?: string;
- pathname: string;
- type: string;
- update_type: string;
-}
+import { TaskCategory, TaskInfo, TaskType, TaskUpdateType } from "src/types/Task";
export const TaskCategoryLabels: { [key in TaskCategory]: string } = {
[TaskCategory.Random]: "grab_a_task",
diff --git a/website/src/context/TaskContext.ts b/website/src/context/TaskContext.ts
new file mode 100644
index 00000000..8950ddfd
--- /dev/null
+++ b/website/src/context/TaskContext.ts
@@ -0,0 +1,7 @@
+import { createContext, useContext } from "react";
+import { TaskApiHook } from "src/types/Hooks";
+import { BaseTask } from "src/types/Task";
+
+export const TaskContext = createContext>(null);
+
+export const useTaskContext = () => useContext(TaskContext);
diff --git a/website/src/hooks/tasks/useGenericTaskAPI.tsx b/website/src/hooks/tasks/useGenericTaskAPI.tsx
index fc09367b..f6fd2e40 100644
--- a/website/src/hooks/tasks/useGenericTaskAPI.tsx
+++ b/website/src/hooks/tasks/useGenericTaskAPI.tsx
@@ -1,38 +1,74 @@
-import { useState } from "react";
+import { useCallback, useState } from "react";
+import { TaskInfos } from "src/components/Tasks/TaskTypes";
import { get, post } from "src/lib/api";
import { TaskApiHook } from "src/types/Hooks";
-import { BaseTask, TaskAvailableResponse, TaskResponse, TaskType as TaskTypeEnum } from "src/types/Task";
+import { BaseTask, ServerTaskResponse, TaskResponse, TaskType as TaskTypeEnum } from "src/types/Task";
import useSWRImmutable from "swr/immutable";
import useSWRMutation from "swr/mutation";
-export const useGenericTaskAPI = (taskType: TaskTypeEnum): TaskApiHook => {
- const [response, setReponse] = useState>({ taskAvailability: "AWAITING_INITIAL" });
+export const useGenericTaskAPI = (
+ taskType: TaskTypeEnum
+): TaskApiHook => {
+ const [response, setResponse] = useState>({ taskAvailability: "AWAITING_INITIAL" });
+
// Note: We use isValidating to indiate we are loading beause it signals eash load, not just the first one.
- const { isValidating: isLoading, mutate: requestNewTask } = useSWRImmutable>(
+ const { isValidating: isLoading, mutate: requestNewTask } = useSWRImmutable>(
"/api/new_task/" + taskType,
get,
{
- onSuccess: (response) => {
- setReponse({ taskAvailability: "AVAILABLE", ...response });
+ onSuccess: (taskResponse) => {
+ setResponse({
+ ...taskResponse,
+ taskAvailability: "AVAILABLE",
+ taskInfo: TaskInfos.find((taskType) => taskType.type === taskResponse.task.type),
+ });
},
onError: () => {
- // We could check for code 503 here for truely unavailable, but we need to do something with other errors anyway.
- setReponse({ taskAvailability: "NONE_AVAILABLE" });
+ // We could check for code 503 here for truly unavailable, but we need to do something with other errors anyway.
+ setResponse({ taskAvailability: "NONE_AVAILABLE" });
},
revalidateOnMount: true,
dedupingInterval: 500,
}
);
- const { trigger: completeTask } = useSWRMutation>("/api/update_task", post, {
+ const { trigger: sendTaskContent } = useSWRMutation("/api/update_task", post, {
onSuccess: () => {
requestNewTask();
},
onError: () => {
- // We could check for code 503 here for truely unavailable, but we need to do something with other errors anyway.
- setReponse({ taskAvailability: "NONE_AVAILABLE" });
+ // We could check for code 503 here for truly unavailable, but we need to do something with other errors anyway.
+ setResponse({ taskAvailability: "NONE_AVAILABLE" });
},
});
- return { response, isLoading, completeTask, skipTask: requestNewTask };
+ // NOTE: it might make sense to split this hook into 2 parts
+
+ // makes sure that requestNewTask is always called without parameters:
+ const skipTask = useCallback(async () => {
+ await requestNewTask();
+ }, [requestNewTask]);
+
+ const { trigger: sendRejection } = useSWRMutation("/api/reject_task", post, { onSuccess: skipTask });
+ const rejectTask = useCallback(
+ async (reason: string) => {
+ if (response.taskAvailability !== "AVAILABLE") {
+ throw new Error("Cannot reject task that is not yet ready");
+ }
+ await sendRejection({ id: response.id, reason });
+ },
+ [response, sendRejection]
+ );
+
+ const completeTask = useCallback(
+ async (content: ResponseContent) => {
+ if (response.taskAvailability !== "AVAILABLE") {
+ throw new Error("Cannot complete task that is not yet ready");
+ }
+ await sendTaskContent({ id: response.id, update_type: response.taskInfo.update_type, content });
+ },
+ [response, sendTaskContent]
+ );
+
+ return { response, isLoading, rejectTask, completeTask, skipTask };
};
diff --git a/website/src/types/Hooks.ts b/website/src/types/Hooks.ts
index 8da40527..acdd0b58 100644
--- a/website/src/types/Hooks.ts
+++ b/website/src/types/Hooks.ts
@@ -1,16 +1,11 @@
-import { BaseTask, TaskContent, TaskResponse, TaskType } from "src/types/Task";
+import { BaseTask, TaskResponse, TaskType } from "src/types/Task";
-interface TaskInteraction {
- id: string;
- update_type: string;
- content: TaskContent;
-}
-
-export type TaskApiHook = {
+export type TaskApiHook = {
response: TaskResponse;
isLoading: boolean;
- completeTask: (interaction: TaskInteraction) => void;
- skipTask: () => void;
+ completeTask: (interaction: ResponseContent) => Promise;
+ skipTask: () => Promise;
+ rejectTask: (reason: string) => Promise;
};
-export type TaskApiHooks = Record TaskApiHook>;
+export type TaskApiHooks = Record TaskApiHook>;
diff --git a/website/src/types/Task.ts b/website/src/types/Task.ts
index 5e2abf16..cb493b56 100644
--- a/website/src/types/Task.ts
+++ b/website/src/types/Task.ts
@@ -14,6 +14,30 @@ export enum TaskType {
random = "random",
}
+export enum TaskCategory {
+ Create = "Create",
+ Evaluate = "Evaluate",
+ Label = "Label",
+ Random = "Random",
+}
+
+export interface TaskInfo {
+ category: TaskCategory;
+ help_link: string;
+ id: string;
+ mode?: string;
+ pathname: string;
+ type: string;
+ update_type: string;
+}
+
+export enum TaskUpdateType {
+ MessageRanking = "message_ranking",
+ Random = "random",
+ TextLabels = "text_labels",
+ TextReplyToMessage = "text_reply_to_message",
+}
+
// we need to reconsider how to handle task content types
// eslint-disable-next-line @typescript-eslint/no-explicit-any
export type TaskContent = any;
@@ -29,14 +53,15 @@ export interface BaseTask {
type: TaskType;
}
-export interface TaskAvailableResponse {
+export interface ServerTaskResponse {
id: string;
userId: string;
task: Task;
}
-interface TaskAvailable extends TaskAvailableResponse {
+interface TaskAvailable extends ServerTaskResponse {
taskAvailability: "AVAILABLE";
+ taskInfo: TaskInfo;
}
interface AwaitingInitialTask {