request queue dedupes requests by id
This commit is contained in:
parent
3f82b7d97a
commit
4f68ddfad5
@ -35,7 +35,6 @@ export default function Main({ sidebar, header, footer, Page }: Props) {
|
|||||||
const theme = useTheme();
|
const theme = useTheme();
|
||||||
const quiz = useCurrentQuiz();
|
const quiz = useCurrentQuiz();
|
||||||
const quizConfig = quiz?.config;
|
const quizConfig = quiz?.config;
|
||||||
const { questions } = useQuestionsStore();
|
|
||||||
const { editQuizId } = useQuizStore();
|
const { editQuizId } = useQuizStore();
|
||||||
const currentStep = useQuizStore((state) => state.currentStep);
|
const currentStep = useQuizStore((state) => state.currentStep);
|
||||||
const { isTestServer } = useDomainDefine();
|
const { isTestServer } = useDomainDefine();
|
||||||
|
@ -380,15 +380,15 @@ export const findQuestionById = (quizId: number) => {
|
|||||||
let found = null;
|
let found = null;
|
||||||
questionStore
|
questionStore
|
||||||
.getState()
|
.getState()
|
||||||
["listQuestions"][quizId].some(
|
[
|
||||||
(quiz: AnyTypedQuizQuestion, index: number) => {
|
"listQuestions"
|
||||||
|
][quizId].some((quiz: AnyTypedQuizQuestion, index: number) => {
|
||||||
if (quiz.backendId === quizId) {
|
if (quiz.backendId === quizId) {
|
||||||
found = { quiz, index };
|
found = { quiz, index };
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
return false;
|
return false;
|
||||||
},
|
});
|
||||||
);
|
|
||||||
return found;
|
return found;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
@ -13,21 +13,14 @@ import {
|
|||||||
UntypedQuizQuestion,
|
UntypedQuizQuestion,
|
||||||
createQuestionVariant,
|
createQuestionVariant,
|
||||||
} from "@model/questionTypes/shared";
|
} from "@model/questionTypes/shared";
|
||||||
import { defaultQuestionByType } from "../../constants/default";
|
|
||||||
import { produce } from "immer";
|
import { produce } from "immer";
|
||||||
import { nanoid } from "nanoid";
|
import { nanoid } from "nanoid";
|
||||||
import { enqueueSnackbar } from "notistack";
|
import { enqueueSnackbar } from "notistack";
|
||||||
|
import { defaultQuestionByType } from "../../constants/default";
|
||||||
import { isAxiosCanceledError } from "../../utils/isAxiosCanceledError";
|
import { isAxiosCanceledError } from "../../utils/isAxiosCanceledError";
|
||||||
import { RequestQueue } from "../../utils/requestQueue";
|
|
||||||
import { updateRootContentId } from "@root/quizes/actions";
|
|
||||||
import { useCurrentQuiz } from "@root/quizes/hooks";
|
|
||||||
import { QuestionsStore, useQuestionsStore } from "./store";
|
|
||||||
import { useUiTools } from "../uiTools/store";
|
|
||||||
import { withErrorBoundary } from "react-error-boundary";
|
|
||||||
import { QuizQuestionResult } from "@model/questionTypes/result";
|
|
||||||
import { replaceEmptyLinesToSpace } from "../../utils/replaceEmptyLinesToSpace";
|
import { replaceEmptyLinesToSpace } from "../../utils/replaceEmptyLinesToSpace";
|
||||||
import { useQuizPreviewStore } from "@root/quizPreview";
|
import { RequestQueue } from "../../utils/requestQueue";
|
||||||
import { useQuizStore } from "@root/quizes/store";
|
import { QuestionsStore, useQuestionsStore } from "./store";
|
||||||
|
|
||||||
export const setQuestions = (questions: RawQuestion[] | null | undefined) =>
|
export const setQuestions = (questions: RawQuestion[] | null | undefined) =>
|
||||||
setProducedState(
|
setProducedState(
|
||||||
@ -244,9 +237,7 @@ export const cancelQuestionDeletion = (questionId: string) =>
|
|||||||
},
|
},
|
||||||
);
|
);
|
||||||
|
|
||||||
const REQUEST_DEBOUNCE = 200;
|
|
||||||
const requestQueue = new RequestQueue();
|
const requestQueue = new RequestQueue();
|
||||||
let requestTimeoutId: ReturnType<typeof setTimeout>;
|
|
||||||
|
|
||||||
export const updateQuestion = async <T = AnyTypedQuizQuestion>(
|
export const updateQuestion = async <T = AnyTypedQuizQuestion>(
|
||||||
questionId: string,
|
questionId: string,
|
||||||
@ -275,8 +266,6 @@ export const updateQuestion = async <T = AnyTypedQuizQuestion>(
|
|||||||
},
|
},
|
||||||
);
|
);
|
||||||
|
|
||||||
// clearTimeout(requestTimeoutId);
|
|
||||||
|
|
||||||
const request = async () => {
|
const request = async () => {
|
||||||
const q =
|
const q =
|
||||||
useQuestionsStore.getState().questions.find((q) => q.id === questionId) ||
|
useQuestionsStore.getState().questions.find((q) => q.id === questionId) ||
|
||||||
@ -321,9 +310,7 @@ export const updateQuestion = async <T = AnyTypedQuizQuestion>(
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
// requestTimeoutId = setTimeout(() => {
|
requestQueue.enqueue(`updateQuestion-${questionId}`, request);
|
||||||
requestQueue.enqueue(request);
|
|
||||||
// }, REQUEST_DEBOUNCE);
|
|
||||||
};
|
};
|
||||||
|
|
||||||
export const addQuestionVariant = (questionId: string) => {
|
export const addQuestionVariant = (questionId: string) => {
|
||||||
@ -453,7 +440,7 @@ export const createTypedQuestion = async (
|
|||||||
questionId: string,
|
questionId: string,
|
||||||
type: QuestionType,
|
type: QuestionType,
|
||||||
) =>
|
) =>
|
||||||
requestQueue.enqueue(async () => {
|
requestQueue.enqueue(`createTypedQuestion-${questionId}`, async () => {
|
||||||
const questions = useQuestionsStore.getState().questions;
|
const questions = useQuestionsStore.getState().questions;
|
||||||
const question = questions.find((q) => q.id === questionId);
|
const question = questions.find((q) => q.id === questionId);
|
||||||
if (!question) return;
|
if (!question) return;
|
||||||
@ -501,7 +488,7 @@ export const createTypedQuestion = async (
|
|||||||
});
|
});
|
||||||
|
|
||||||
export const deleteQuestion = async (questionId: string) =>
|
export const deleteQuestion = async (questionId: string) =>
|
||||||
requestQueue.enqueue(async () => {
|
requestQueue.enqueue(`deleteQuestion-${questionId}`, async () => {
|
||||||
const question = useQuestionsStore
|
const question = useQuestionsStore
|
||||||
.getState()
|
.getState()
|
||||||
.questions.find((q) => q.id === questionId);
|
.questions.find((q) => q.id === questionId);
|
||||||
@ -525,7 +512,7 @@ export const deleteQuestion = async (questionId: string) =>
|
|||||||
});
|
});
|
||||||
|
|
||||||
export const copyQuestion = async (questionId: string, quizId: number) =>
|
export const copyQuestion = async (questionId: string, quizId: number) =>
|
||||||
requestQueue.enqueue(async () => {
|
requestQueue.enqueue(`copyQuestion-${quizId}-${questionId}`, async () => {
|
||||||
const question = useQuestionsStore
|
const question = useQuestionsStore
|
||||||
.getState()
|
.getState()
|
||||||
.questions.find((q) => q.id === questionId);
|
.questions.find((q) => q.id === questionId);
|
||||||
@ -585,7 +572,7 @@ export const copyQuestion = async (questionId: string, quizId: number) =>
|
|||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
function setProducedState<A extends string | { type: unknown }>(
|
function setProducedState<A extends string | { type: string }>(
|
||||||
recipe: (state: QuestionsStore) => void,
|
recipe: (state: QuestionsStore) => void,
|
||||||
action?: A,
|
action?: A,
|
||||||
) {
|
) {
|
||||||
@ -635,7 +622,7 @@ export const createResult = async (
|
|||||||
quizId: number | null | undefined,
|
quizId: number | null | undefined,
|
||||||
parentContentId?: string,
|
parentContentId?: string,
|
||||||
) =>
|
) =>
|
||||||
requestQueue.enqueue(async () => {
|
requestQueue.enqueue(`createResult-${quizId}`, async () => {
|
||||||
if (!quizId || !parentContentId) {
|
if (!quizId || !parentContentId) {
|
||||||
console.error(
|
console.error(
|
||||||
"Нет данных для создания результата. quizId: ",
|
"Нет данных для создания результата. quizId: ",
|
||||||
|
@ -3,15 +3,14 @@ import { devlog, getMessageFromFetchError } from "@frontend/kitui";
|
|||||||
import { quizToEditQuizRequest } from "@model/quiz/edit";
|
import { quizToEditQuizRequest } from "@model/quiz/edit";
|
||||||
import { Quiz, RawQuiz, rawQuizToQuiz } from "@model/quiz/quiz";
|
import { Quiz, RawQuiz, rawQuizToQuiz } from "@model/quiz/quiz";
|
||||||
import { QuizConfig, maxQuizSetupSteps } from "@model/quizSettings";
|
import { QuizConfig, maxQuizSetupSteps } from "@model/quizSettings";
|
||||||
|
import { createUntypedQuestion, updateQuestion } from "@root/questions/actions";
|
||||||
|
import { useQuestionsStore } from "@root/questions/store";
|
||||||
import { produce } from "immer";
|
import { produce } from "immer";
|
||||||
import { enqueueSnackbar } from "notistack";
|
import { enqueueSnackbar } from "notistack";
|
||||||
import { NavigateFunction } from "react-router-dom";
|
import { NavigateFunction } from "react-router-dom";
|
||||||
import { isAxiosCanceledError } from "../../utils/isAxiosCanceledError";
|
import { isAxiosCanceledError } from "../../utils/isAxiosCanceledError";
|
||||||
import { RequestQueue } from "../../utils/requestQueue";
|
import { RequestQueue } from "../../utils/requestQueue";
|
||||||
import { QuizStore, useQuizStore } from "./store";
|
import { QuizStore, useQuizStore } from "./store";
|
||||||
import { createUntypedQuestion, updateQuestion } from "@root/questions/actions";
|
|
||||||
import { useCurrentQuiz } from "./hooks";
|
|
||||||
import { useQuestionsStore } from "@root/questions/store";
|
|
||||||
|
|
||||||
export const setEditQuizId = (quizId: number | null) =>
|
export const setEditQuizId = (quizId: number | null) =>
|
||||||
setProducedState(
|
setProducedState(
|
||||||
@ -157,7 +156,7 @@ export const updateQuiz = (
|
|||||||
clearTimeout(requestTimeoutId);
|
clearTimeout(requestTimeoutId);
|
||||||
requestTimeoutId = setTimeout(async () => {
|
requestTimeoutId = setTimeout(async () => {
|
||||||
requestQueue
|
requestQueue
|
||||||
.enqueue(async () => {
|
.enqueue(`updateQuiz-${quizId}`, async () => {
|
||||||
const quiz = useQuizStore
|
const quiz = useQuizStore
|
||||||
.getState()
|
.getState()
|
||||||
.quizes.find((q) => q.id === quizId);
|
.quizes.find((q) => q.id === quizId);
|
||||||
@ -178,7 +177,7 @@ export const updateQuiz = (
|
|||||||
};
|
};
|
||||||
|
|
||||||
export const createQuiz = async (navigate: NavigateFunction) =>
|
export const createQuiz = async (navigate: NavigateFunction) =>
|
||||||
requestQueue.enqueue(async () => {
|
requestQueue.enqueue("createQuiz", async () => {
|
||||||
try {
|
try {
|
||||||
const rawQuiz = await quizApi.create();
|
const rawQuiz = await quizApi.create();
|
||||||
const quiz = rawQuizToQuiz(rawQuiz);
|
const quiz = rawQuizToQuiz(rawQuiz);
|
||||||
@ -196,7 +195,7 @@ export const createQuiz = async (navigate: NavigateFunction) =>
|
|||||||
});
|
});
|
||||||
|
|
||||||
export const deleteQuiz = async (quizId: string) =>
|
export const deleteQuiz = async (quizId: string) =>
|
||||||
requestQueue.enqueue(async () => {
|
requestQueue.enqueue(`deleteQuiz-${quizId}`, async () => {
|
||||||
const quiz = useQuizStore.getState().quizes.find((q) => q.id === quizId);
|
const quiz = useQuizStore.getState().quizes.find((q) => q.id === quizId);
|
||||||
if (!quiz) return;
|
if (!quiz) return;
|
||||||
|
|
||||||
|
@ -33,7 +33,7 @@ const removeResult = (resultId: string) =>
|
|||||||
});
|
});
|
||||||
|
|
||||||
export const deleteResult = async (resultId: number) =>
|
export const deleteResult = async (resultId: number) =>
|
||||||
requestQueue.enqueue(async () => {
|
requestQueue.enqueue(`deleteResult-${resultId}`, async () => {
|
||||||
const result = useResultStore
|
const result = useResultStore
|
||||||
.getState()
|
.getState()
|
||||||
.results.find((r) => r.id === resultId);
|
.results.find((r) => r.id === resultId);
|
||||||
@ -54,7 +54,9 @@ export const obsolescenceResult = async (
|
|||||||
resultId: string,
|
resultId: string,
|
||||||
editQuizId: number,
|
editQuizId: number,
|
||||||
) =>
|
) =>
|
||||||
requestQueue.enqueue(async () => {
|
requestQueue.enqueue(
|
||||||
|
`obsolescenceResult-${resultId}-${editQuizId}`,
|
||||||
|
async () => {
|
||||||
const result = useResultStore
|
const result = useResultStore
|
||||||
.getState()
|
.getState()
|
||||||
.results.find((r) => r.id === resultId);
|
.results.find((r) => r.id === resultId);
|
||||||
@ -79,7 +81,8 @@ export const obsolescenceResult = async (
|
|||||||
}, 3000);
|
}, 3000);
|
||||||
const resultList = await resultApi.getList(editQuizId);
|
const resultList = await resultApi.getList(editQuizId);
|
||||||
setResults(resultList);
|
setResults(resultList);
|
||||||
});
|
},
|
||||||
|
);
|
||||||
|
|
||||||
export const answerResultListExport = async (
|
export const answerResultListExport = async (
|
||||||
editQuizId: number,
|
editQuizId: number,
|
||||||
@ -111,7 +114,7 @@ export const answerResultListExport = async (
|
|||||||
download();
|
download();
|
||||||
};
|
};
|
||||||
|
|
||||||
function setProducedState<A extends string | { type: unknown }>(
|
function setProducedState<A extends string | { type: string }>(
|
||||||
recipe: (state: ResultStore) => void,
|
recipe: (state: ResultStore) => void,
|
||||||
action?: A,
|
action?: A,
|
||||||
) {
|
) {
|
||||||
|
@ -1,14 +1,15 @@
|
|||||||
export class RequestQueue<T = unknown> {
|
export class RequestQueue<IdType, T = unknown> {
|
||||||
private pendingPromise = false;
|
private pendingPromise = false;
|
||||||
private items: Array<{
|
private items: Array<{
|
||||||
|
id: IdType;
|
||||||
action: () => Promise<T>;
|
action: () => Promise<T>;
|
||||||
resolve: (value: T) => void;
|
resolve: (value: T) => void;
|
||||||
reject: (reason?: any) => void;
|
reject: (reason?: any) => void;
|
||||||
}> = [];
|
}> = [];
|
||||||
|
|
||||||
enqueue(action: () => Promise<T>) {
|
enqueue(id: IdType, action: () => Promise<T>) {
|
||||||
return new Promise((resolve, reject) => {
|
return new Promise((resolve, reject) => {
|
||||||
this.items.push({ action, resolve, reject });
|
this.items.push({ action, resolve, reject, id });
|
||||||
this.dequeue();
|
this.dequeue();
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
@ -19,6 +20,9 @@ export class RequestQueue<T = unknown> {
|
|||||||
const item = this.items.shift();
|
const item = this.items.shift();
|
||||||
if (!item) return;
|
if (!item) return;
|
||||||
|
|
||||||
|
// remove tasks with same id since they are outdated
|
||||||
|
this.items = this.items.filter((i) => i.id !== item.id);
|
||||||
|
|
||||||
try {
|
try {
|
||||||
this.pendingPromise = true;
|
this.pendingPromise = true;
|
||||||
const payload = await item.action();
|
const payload = await item.action();
|
||||||
|
Loading…
Reference in New Issue
Block a user