mirror of
https://github.com/wassname/Open-Assistant.git
synced 2026-06-27 16:10:30 +08:00
ab4dce3f60
* website: Support new widget types for labelling Adds proper support for yes/no spam style questions as well as a simple interface for flag style labels. Also cleaned up the Task component to fix some rerender issues. * website: Fix some UI text, adjust yes/no button alignment * website: Remove left over console.log Co-authored-by: notmd <33456881+notmd@users.noreply.github.com> --------- Co-authored-by: notmd <33456881+notmd@users.noreply.github.com>
226 lines
6.4 KiB
TypeScript
226 lines
6.4 KiB
TypeScript
import { useTranslation } from "next-i18next";
|
|
import { useCallback, useEffect, useReducer } from "react";
|
|
import { useMemo, useRef } from "react";
|
|
import { TaskControls } from "src/components/Survey/TaskControls";
|
|
import { CreateTask } from "src/components/Tasks/CreateTask";
|
|
import { EvaluateTask } from "src/components/Tasks/EvaluateTask";
|
|
import { LabelTask } from "src/components/Tasks/LabelTask";
|
|
import { TaskCategory, TaskInfo, TaskInfos } from "src/components/Tasks/TaskTypes";
|
|
import { UnchangedWarning } from "src/components/Tasks/UnchangedWarning";
|
|
import { post } from "src/lib/api";
|
|
import { getTypeSafei18nKey } from "src/lib/i18n";
|
|
import { BaseTask, TaskContent, TaskReplyValidity } from "src/types/Task";
|
|
import useSWRMutation from "swr/mutation";
|
|
|
|
interface EditMode {
|
|
mode: "EDIT";
|
|
replyValidity: TaskReplyValidity;
|
|
}
|
|
interface ReviewMode {
|
|
mode: "REVIEW";
|
|
}
|
|
interface DefaultWarnMode {
|
|
mode: "DEFAULT_WARN";
|
|
}
|
|
interface SubmittedMode {
|
|
mode: "SUBMITTED";
|
|
}
|
|
|
|
export type TaskStatus = EditMode | DefaultWarnMode | ReviewMode | SubmittedMode;
|
|
|
|
interface NewTask {
|
|
action: "NEW_TASK";
|
|
}
|
|
|
|
interface Review {
|
|
action: "REVIEW";
|
|
}
|
|
|
|
interface SetSubmitted {
|
|
action: "SET_SUBMITTED";
|
|
}
|
|
|
|
interface ReturnToEdit {
|
|
action: "RETURN_EDIT";
|
|
}
|
|
|
|
interface AcceptDefault {
|
|
action: "ACCEPT_DEFAULT";
|
|
}
|
|
|
|
interface UpdateValidity {
|
|
action: "UPDATE_VALIDITY";
|
|
replyValidity: TaskReplyValidity;
|
|
}
|
|
|
|
export interface TaskSurveyProps<TaskType extends BaseTask, T> {
|
|
task: TaskType;
|
|
taskType: TaskInfo;
|
|
isEditable: boolean;
|
|
isDisabled?: boolean;
|
|
onReplyChanged: (content: T) => void;
|
|
onValidityChanged: (validity: TaskReplyValidity) => void;
|
|
}
|
|
|
|
export const Task = ({ frontendId, task, trigger, mutate }) => {
|
|
const { t } = useTranslation("tasks");
|
|
const [taskStatus, taskEvent] = useReducer(
|
|
(
|
|
status: TaskStatus,
|
|
event: NewTask | UpdateValidity | AcceptDefault | Review | ReturnToEdit | SetSubmitted
|
|
): TaskStatus => {
|
|
switch (event.action) {
|
|
case "NEW_TASK":
|
|
return { mode: "EDIT", replyValidity: "INVALID" };
|
|
case "UPDATE_VALIDITY":
|
|
return status.mode === "EDIT" ? { mode: "EDIT", replyValidity: event.replyValidity } : status;
|
|
case "ACCEPT_DEFAULT":
|
|
return status.mode === "DEFAULT_WARN" ? { mode: "REVIEW" } : status;
|
|
case "REVIEW": {
|
|
if (status.mode === "EDIT") {
|
|
switch (status.replyValidity) {
|
|
case "DEFAULT":
|
|
return { mode: "DEFAULT_WARN" };
|
|
case "VALID":
|
|
return { mode: "REVIEW" };
|
|
}
|
|
}
|
|
return status;
|
|
}
|
|
case "RETURN_EDIT": {
|
|
switch (status.mode) {
|
|
case "REVIEW":
|
|
return { mode: "EDIT", replyValidity: "VALID" };
|
|
case "DEFAULT_WARN":
|
|
return { mode: "EDIT", replyValidity: "DEFAULT" };
|
|
default:
|
|
return status;
|
|
}
|
|
}
|
|
case "SET_SUBMITTED": {
|
|
return status.mode === "REVIEW" ? { mode: "SUBMITTED" } : status;
|
|
}
|
|
}
|
|
},
|
|
{ mode: "EDIT", replyValidity: "INVALID" }
|
|
);
|
|
|
|
const replyContent = useRef<TaskContent>(null);
|
|
const updateValidity = useCallback(
|
|
(replyValidity: TaskReplyValidity) => taskEvent({ action: "UPDATE_VALIDITY", replyValidity }),
|
|
[taskEvent]
|
|
);
|
|
|
|
useEffect(() => {
|
|
taskEvent({ action: "NEW_TASK" });
|
|
}, [task.id, updateValidity]);
|
|
|
|
const rootEl = useRef<HTMLDivElement>(null);
|
|
|
|
const taskType = useMemo(
|
|
() => TaskInfos.find((taskType) => taskType.type === task.type && taskType.mode === task.mode),
|
|
[task.type, task.mode]
|
|
);
|
|
|
|
const { trigger: sendRejection } = useSWRMutation("/api/reject_task", post, {
|
|
onSuccess: async () => {
|
|
mutate();
|
|
},
|
|
});
|
|
|
|
const rejectTask = (reason: string) => {
|
|
sendRejection({
|
|
id: frontendId,
|
|
reason,
|
|
});
|
|
};
|
|
|
|
const onReplyChanged = useCallback(
|
|
(content: TaskContent) => {
|
|
replyContent.current = content;
|
|
},
|
|
[replyContent]
|
|
);
|
|
|
|
const submitResponse = () => {
|
|
if (taskStatus.mode === "REVIEW") {
|
|
trigger({
|
|
id: frontendId,
|
|
update_type: taskType.update_type,
|
|
content: replyContent.current,
|
|
});
|
|
taskEvent({ action: "SET_SUBMITTED" });
|
|
scrollToTop(rootEl.current);
|
|
}
|
|
};
|
|
|
|
const taskTypeComponent = useMemo(() => {
|
|
switch (taskType.category) {
|
|
case TaskCategory.Create:
|
|
return (
|
|
<CreateTask
|
|
task={task}
|
|
taskType={taskType}
|
|
isEditable={taskStatus.mode === "EDIT"}
|
|
isDisabled={taskStatus.mode === "SUBMITTED"}
|
|
onReplyChanged={onReplyChanged}
|
|
onValidityChanged={updateValidity}
|
|
/>
|
|
);
|
|
case TaskCategory.Evaluate:
|
|
return (
|
|
<EvaluateTask
|
|
task={task}
|
|
taskType={taskType}
|
|
isEditable={taskStatus.mode === "EDIT"}
|
|
isDisabled={taskStatus.mode === "SUBMITTED"}
|
|
onReplyChanged={onReplyChanged}
|
|
onValidityChanged={updateValidity}
|
|
/>
|
|
);
|
|
case TaskCategory.Label:
|
|
return (
|
|
<LabelTask
|
|
task={task}
|
|
taskType={taskType}
|
|
isEditable={taskStatus.mode === "EDIT"}
|
|
isDisabled={taskStatus.mode === "SUBMITTED"}
|
|
onReplyChanged={onReplyChanged}
|
|
onValidityChanged={updateValidity}
|
|
/>
|
|
);
|
|
}
|
|
}, [task, taskType, taskStatus.mode, onReplyChanged, updateValidity]);
|
|
|
|
return (
|
|
<div ref={rootEl}>
|
|
{taskTypeComponent}
|
|
<TaskControls
|
|
task={task}
|
|
taskStatus={taskStatus}
|
|
onEdit={() => taskEvent({ action: "RETURN_EDIT" })}
|
|
onReview={() => taskEvent({ action: "REVIEW" })}
|
|
onSubmit={submitResponse}
|
|
onSkip={rejectTask}
|
|
/>
|
|
<UnchangedWarning
|
|
show={taskStatus.mode === "DEFAULT_WARN"}
|
|
title={t(getTypeSafei18nKey(`${taskType.id}.unchanged_title`)) || t("default.unchanged_title")}
|
|
message={t(getTypeSafei18nKey(`${taskType.id}.unchanged_message`)) || t("default.unchanged_message")}
|
|
continueButtonText={"Continue anyway"}
|
|
onClose={() => taskEvent({ action: "RETURN_EDIT" })}
|
|
onContinueAnyway={() => {
|
|
taskEvent({ action: "ACCEPT_DEFAULT" });
|
|
}}
|
|
/>
|
|
</div>
|
|
);
|
|
};
|
|
|
|
const scrollToTop = (element: HTMLElement) => {
|
|
while (element) {
|
|
element.scrollTop = 0;
|
|
element = element.parentElement;
|
|
}
|
|
};
|