Merge branch 'dev' into 'staging'
Dev See merge request frontend/squzanswerer!161
This commit is contained in:
commit
26c2e90703
@ -1,4 +1 @@
|
|||||||
#!/bin/sh
|
|
||||||
. "$(dirname "$0")/_/husky.sh"
|
|
||||||
|
|
||||||
yarn lint-staged --allow-empty
|
yarn lint-staged --allow-empty
|
||||||
|
@ -10,12 +10,10 @@ import { useVkMetricsGoals } from "@/utils/hooks/metrics/useVkMetricsGoals";
|
|||||||
import { useYandexMetricsGoals } from "@/utils/hooks/metrics/useYandexMetricsGoals";
|
import { useYandexMetricsGoals } from "@/utils/hooks/metrics/useYandexMetricsGoals";
|
||||||
import { DESIGN_LIST } from "@/utils/designList";
|
import { DESIGN_LIST } from "@/utils/designList";
|
||||||
import { quizThemes } from "@utils/themes/Publication/themePublication";
|
import { quizThemes } from "@utils/themes/Publication/themePublication";
|
||||||
|
|
||||||
import YoutubeEmbedIframe from "./tools/YoutubeEmbedIframe";
|
|
||||||
|
|
||||||
import { NameplateLogo } from "@icons/NameplateLogo";
|
import { NameplateLogo } from "@icons/NameplateLogo";
|
||||||
|
|
||||||
import type { QuizQuestionResult } from "@/model/questionTypes/result";
|
import type { QuizQuestionResult } from "@/model/questionTypes/result";
|
||||||
|
import QuizVideo from "@/ui_kit/VideoIframe/VideoIframe";
|
||||||
|
|
||||||
type ResultFormProps = {
|
type ResultFormProps = {
|
||||||
resultQuestion: QuizQuestionResult;
|
resultQuestion: QuizQuestionResult;
|
||||||
@ -117,7 +115,7 @@ export const ResultForm = ({ resultQuestion }: ResultFormProps) => {
|
|||||||
}}
|
}}
|
||||||
>
|
>
|
||||||
{!resultQuestion?.content.useImage && resultQuestion.content.video && (
|
{!resultQuestion?.content.useImage && resultQuestion.content.video && (
|
||||||
<YoutubeEmbedIframe
|
<QuizVideo
|
||||||
videoUrl={resultQuestion.content.video}
|
videoUrl={resultQuestion.content.video}
|
||||||
containerSX={{
|
containerSX={{
|
||||||
width: "100%",
|
width: "100%",
|
||||||
|
@ -14,8 +14,7 @@ import { DESIGN_LIST } from "@/utils/designList";
|
|||||||
|
|
||||||
import { useVkMetricsGoals } from "@/utils/hooks/metrics/useVkMetricsGoals";
|
import { useVkMetricsGoals } from "@/utils/hooks/metrics/useVkMetricsGoals";
|
||||||
import { useYandexMetricsGoals } from "@/utils/hooks/metrics/useYandexMetricsGoals";
|
import { useYandexMetricsGoals } from "@/utils/hooks/metrics/useYandexMetricsGoals";
|
||||||
|
import QuizVideo from "@/ui_kit/VideoIframe/VideoIframe";
|
||||||
import YoutubeEmbedIframe from "../tools/YoutubeEmbedIframe";
|
|
||||||
|
|
||||||
export const StartPageViewPublication = () => {
|
export const StartPageViewPublication = () => {
|
||||||
const theme = useTheme();
|
const theme = useTheme();
|
||||||
@ -54,7 +53,7 @@ export const StartPageViewPublication = () => {
|
|||||||
/>
|
/>
|
||||||
) : settings.cfg.startpage.background.type === "video" ? (
|
) : settings.cfg.startpage.background.type === "video" ? (
|
||||||
settings.cfg.startpage.background.video ? (
|
settings.cfg.startpage.background.video ? (
|
||||||
<YoutubeEmbedIframe
|
<QuizVideo
|
||||||
videoUrl={settings.cfg.startpage.background.video}
|
videoUrl={settings.cfg.startpage.background.video}
|
||||||
containerSX={{
|
containerSX={{
|
||||||
width: settings.cfg.startpageType === "centered" ? "550px" : "100%",
|
width: settings.cfg.startpageType === "centered" ? "550px" : "100%",
|
||||||
|
@ -1,3 +1,7 @@
|
|||||||
|
import { ContactForm } from "@/components/ViewPublicationPage/ContactForm/ContactForm.tsx";
|
||||||
|
import { extractImageLinksFromQuestion } from "@/utils/extractImageLinks";
|
||||||
|
import { useVKMetrics } from "@/utils/hooks/metrics/useVKMetrics";
|
||||||
|
import { useYandexMetrics } from "@/utils/hooks/metrics/useYandexMetrics";
|
||||||
import { sendAnswer } from "@api/quizRelase";
|
import { sendAnswer } from "@api/quizRelase";
|
||||||
import { useQuizSettings } from "@contexts/QuizDataContext";
|
import { useQuizSettings } from "@contexts/QuizDataContext";
|
||||||
import { ThemeProvider, Typography } from "@mui/material";
|
import { ThemeProvider, Typography } from "@mui/material";
|
||||||
@ -7,15 +11,13 @@ import { notReachable } from "@utils/notReachable";
|
|||||||
import { quizThemes } from "@utils/themes/Publication/themePublication";
|
import { quizThemes } from "@utils/themes/Publication/themePublication";
|
||||||
import { enqueueSnackbar } from "notistack";
|
import { enqueueSnackbar } from "notistack";
|
||||||
import { ReactElement, useEffect } from "react";
|
import { ReactElement, useEffect } from "react";
|
||||||
|
import { Helmet } from "react-helmet";
|
||||||
import { Question } from "./Question";
|
import { Question } from "./Question";
|
||||||
|
import QuestionSelect from "./QuestionSelect";
|
||||||
import { ResultForm } from "./ResultForm";
|
import { ResultForm } from "./ResultForm";
|
||||||
import { StartPageViewPublication } from "./StartPageViewPublication";
|
import { StartPageViewPublication } from "./StartPageViewPublication";
|
||||||
import NextButton from "./tools/NextButton";
|
import NextButton from "./tools/NextButton";
|
||||||
import PrevButton from "./tools/PrevButton";
|
import PrevButton from "./tools/PrevButton";
|
||||||
import QuestionSelect from "./QuestionSelect";
|
|
||||||
import { useYandexMetrics } from "@/utils/hooks/metrics/useYandexMetrics";
|
|
||||||
import { useVKMetrics } from "@/utils/hooks/metrics/useVKMetrics";
|
|
||||||
import { ContactForm } from "@/components/ViewPublicationPage/ContactForm/ContactForm.tsx";
|
|
||||||
|
|
||||||
export default function ViewPublicationPage() {
|
export default function ViewPublicationPage() {
|
||||||
const { settings, recentlyCompleted, quizId, preview, changeFaviconAndTitle } = useQuizSettings();
|
const { settings, recentlyCompleted, quizId, preview, changeFaviconAndTitle } = useQuizSettings();
|
||||||
@ -24,6 +26,7 @@ export default function ViewPublicationPage() {
|
|||||||
const {
|
const {
|
||||||
currentQuestion,
|
currentQuestion,
|
||||||
currentQuestionStepNumber,
|
currentQuestionStepNumber,
|
||||||
|
nextQuestion,
|
||||||
isNextButtonEnabled,
|
isNextButtonEnabled,
|
||||||
isPreviousButtonEnabled,
|
isPreviousButtonEnabled,
|
||||||
moveToPrevQuestion,
|
moveToPrevQuestion,
|
||||||
@ -56,7 +59,10 @@ export default function ViewPublicationPage() {
|
|||||||
if (!currentQuestion)
|
if (!currentQuestion)
|
||||||
return (
|
return (
|
||||||
<ThemeProvider theme={quizThemes[settings.cfg.theme || "StandardTheme"].theme}>
|
<ThemeProvider theme={quizThemes[settings.cfg.theme || "StandardTheme"].theme}>
|
||||||
<Typography textAlign={"center"} mt="50px">
|
<Typography
|
||||||
|
textAlign={"center"}
|
||||||
|
mt="50px"
|
||||||
|
>
|
||||||
Вопрос не выбран
|
Вопрос не выбран
|
||||||
</Typography>
|
</Typography>
|
||||||
</ThemeProvider>
|
</ThemeProvider>
|
||||||
@ -80,7 +86,10 @@ export default function ViewPublicationPage() {
|
|||||||
currentQuestion={currentQuestion}
|
currentQuestion={currentQuestion}
|
||||||
currentQuestionStepNumber={currentQuestionStepNumber}
|
currentQuestionStepNumber={currentQuestionStepNumber}
|
||||||
prevButton={
|
prevButton={
|
||||||
<PrevButton isPreviousButtonEnabled={isPreviousButtonEnabled} moveToPrevQuestion={moveToPrevQuestion} />
|
<PrevButton
|
||||||
|
isPreviousButtonEnabled={isPreviousButtonEnabled}
|
||||||
|
moveToPrevQuestion={moveToPrevQuestion}
|
||||||
|
/>
|
||||||
}
|
}
|
||||||
nextButton={
|
nextButton={
|
||||||
<NextButton
|
<NextButton
|
||||||
@ -102,20 +111,47 @@ export default function ViewPublicationPage() {
|
|||||||
}}
|
}}
|
||||||
/>
|
/>
|
||||||
}
|
}
|
||||||
questionSelect={<QuestionSelect selectedQuestion={currentQuestion} setQuestion={setQuestion} />}
|
questionSelect={
|
||||||
|
<QuestionSelect
|
||||||
|
selectedQuestion={currentQuestion}
|
||||||
|
setQuestion={setQuestion}
|
||||||
|
/>
|
||||||
|
}
|
||||||
/>
|
/>
|
||||||
);
|
);
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
case "contactform": {
|
case "contactform": {
|
||||||
quizStepElement = <ContactForm currentQuestion={currentQuestion} onShowResult={showResultAfterContactForm} />;
|
quizStepElement = (
|
||||||
|
<ContactForm
|
||||||
|
currentQuestion={currentQuestion}
|
||||||
|
onShowResult={showResultAfterContactForm}
|
||||||
|
/>
|
||||||
|
);
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
default:
|
default:
|
||||||
notReachable(currentQuizStep);
|
notReachable(currentQuizStep);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
const preloadLinks = new Set([
|
||||||
|
...extractImageLinksFromQuestion(currentQuestion),
|
||||||
|
...extractImageLinksFromQuestion(nextQuestion),
|
||||||
|
]);
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<ThemeProvider theme={quizThemes[settings.cfg.theme || "StandardTheme"].theme}>{quizStepElement}</ThemeProvider>
|
<ThemeProvider theme={quizThemes[settings.cfg.theme || "StandardTheme"].theme}>
|
||||||
|
<Helmet>
|
||||||
|
{Array.from(preloadLinks).map((link) => (
|
||||||
|
<link
|
||||||
|
key={link}
|
||||||
|
rel="preload"
|
||||||
|
as="image"
|
||||||
|
href={link}
|
||||||
|
/>
|
||||||
|
))}
|
||||||
|
</Helmet>
|
||||||
|
{quizStepElement}
|
||||||
|
</ThemeProvider>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
@ -58,12 +58,10 @@ export const UploadFile = ({ currentQuestion, setModalWarningType, isSending, se
|
|||||||
},
|
},
|
||||||
qid: quizId,
|
qid: quizId,
|
||||||
});
|
});
|
||||||
|
console.log(data);
|
||||||
await sendAnswer({
|
await sendAnswer({
|
||||||
questionId: currentQuestion.id,
|
questionId: currentQuestion.id,
|
||||||
body: `https://s3.timeweb.cloud/3c580be9-cf31f296-d055-49cf-b39e-30c7959dc17b/squizanswer/${quizId}/${
|
body: `${data!.data.fileIDMap[currentQuestion.id]}`,
|
||||||
currentQuestion.id
|
|
||||||
}/${data!.data.fileIDMap[currentQuestion.id]}`,
|
|
||||||
qid: quizId,
|
qid: quizId,
|
||||||
preview,
|
preview,
|
||||||
});
|
});
|
||||||
|
@ -1,8 +1,6 @@
|
|||||||
import { Box, Typography, useTheme } from "@mui/material";
|
import { Box, Typography, useTheme } from "@mui/material";
|
||||||
|
|
||||||
import YoutubeEmbedIframe from "@/components/ViewPublicationPage/tools/YoutubeEmbedIframe";
|
|
||||||
|
|
||||||
import type { QuizQuestionPage } from "@model/questionTypes/page";
|
import type { QuizQuestionPage } from "@model/questionTypes/page";
|
||||||
|
import QuizVideo from "@/ui_kit/VideoIframe/VideoIframe";
|
||||||
|
|
||||||
type PageProps = {
|
type PageProps = {
|
||||||
currentQuestion: QuizQuestionPage;
|
currentQuestion: QuizQuestionPage;
|
||||||
@ -35,32 +33,35 @@ export const Page = ({ currentQuestion }: PageProps) => {
|
|||||||
}}
|
}}
|
||||||
>
|
>
|
||||||
{currentQuestion.content.useImage ? (
|
{currentQuestion.content.useImage ? (
|
||||||
<Box
|
currentQuestion.content.back && (
|
||||||
sx={{
|
<Box
|
||||||
borderRadius: "12px",
|
sx={{
|
||||||
border: "1px solid #9A9AAF",
|
borderRadius: "12px",
|
||||||
overflow: "hidden",
|
border: "1px solid #9A9AAF",
|
||||||
}}
|
overflow: "hidden",
|
||||||
>
|
|
||||||
<img
|
|
||||||
key={currentQuestion.id}
|
|
||||||
src={currentQuestion.content.back}
|
|
||||||
alt=""
|
|
||||||
style={{
|
|
||||||
display: "block",
|
|
||||||
width: "100%",
|
|
||||||
height: "100%",
|
|
||||||
objectFit: "contain",
|
|
||||||
}}
|
}}
|
||||||
/>
|
>
|
||||||
</Box>
|
<img
|
||||||
|
key={currentQuestion.id}
|
||||||
|
src={currentQuestion.content.back}
|
||||||
|
alt=""
|
||||||
|
style={{
|
||||||
|
display: "block",
|
||||||
|
width: "100%",
|
||||||
|
height: "100%",
|
||||||
|
objectFit: "contain",
|
||||||
|
}}
|
||||||
|
/>
|
||||||
|
</Box>
|
||||||
|
)
|
||||||
) : (
|
) : (
|
||||||
<YoutubeEmbedIframe
|
<QuizVideo
|
||||||
containerSX={{
|
containerSX={{
|
||||||
width: "100%",
|
width: "100%",
|
||||||
height: "calc(100% - 270px)",
|
height: "calc(100% - 270px)",
|
||||||
maxHeight: "80%",
|
maxHeight: "80%",
|
||||||
objectFit: "contain",
|
objectFit: "contain",
|
||||||
|
aspectRatio: "16 / 9",
|
||||||
}}
|
}}
|
||||||
videoUrl={currentQuestion.content.video}
|
videoUrl={currentQuestion.content.video}
|
||||||
/>
|
/>
|
||||||
|
@ -1,49 +0,0 @@
|
|||||||
import { Box, SxProps } from "@mui/material";
|
|
||||||
|
|
||||||
interface Props {
|
|
||||||
videoUrl: string;
|
|
||||||
containerSX?: SxProps;
|
|
||||||
}
|
|
||||||
|
|
||||||
export default function YoutubeEmbedIframe({ videoUrl, containerSX }: Props) {
|
|
||||||
const extractYoutubeVideoId =
|
|
||||||
/(?:youtube\.com\/(?:[^\/]+\/.+\/|(?:v|e(?:mbed)?)\/|.*[?&]v=)|youtu\.be\/)([^"&?\/\s]{11})/gi;
|
|
||||||
const videoId = extractYoutubeVideoId.exec(videoUrl)?.[1];
|
|
||||||
// if (!videoId) return null;
|
|
||||||
|
|
||||||
const embedUrl = `https://www.youtube.com/embed/${videoId}?controls=0&autoplay=1&modestbranding=0&showinfo=0&disablekb=1&mute=1&loop=1`;
|
|
||||||
// https://www.youtube.com/shorts/9VgqBPd6RPA
|
|
||||||
// https://www.youtube.com/watch?v=I2N8hTHhvGY
|
|
||||||
return (
|
|
||||||
<Box
|
|
||||||
sx={{
|
|
||||||
width: "100%",
|
|
||||||
height: "100%",
|
|
||||||
pointerEvents: "none",
|
|
||||||
"& iframe": {
|
|
||||||
width: "100%",
|
|
||||||
height: "100%",
|
|
||||||
},
|
|
||||||
...containerSX,
|
|
||||||
}}
|
|
||||||
>
|
|
||||||
<Box
|
|
||||||
component="video"
|
|
||||||
sx={{
|
|
||||||
width: "100%",
|
|
||||||
height: "100%",
|
|
||||||
}}
|
|
||||||
autoPlay
|
|
||||||
muted
|
|
||||||
src={videoUrl}
|
|
||||||
/>
|
|
||||||
{/* <iframe
|
|
||||||
src={embedUrl}
|
|
||||||
title="YouTube video player"
|
|
||||||
frameBorder="0"
|
|
||||||
allow="accelerometer; autoplay; clipboard-write; encrypted-media; gyroscope; picture-in-picture; web-share"
|
|
||||||
allowFullScreen
|
|
||||||
/> */}
|
|
||||||
</Box>
|
|
||||||
);
|
|
||||||
}
|
|
@ -4,8 +4,12 @@ export const isResultQuestionEmpty = (resultQuestion: QuizQuestionResult) => {
|
|||||||
if (
|
if (
|
||||||
(resultQuestion.title.length > 0 && resultQuestion.title !== " ") ||
|
(resultQuestion.title.length > 0 && resultQuestion.title !== " ") ||
|
||||||
(resultQuestion.description.length > 0 && resultQuestion.description !== " ") ||
|
(resultQuestion.description.length > 0 && resultQuestion.description !== " ") ||
|
||||||
(resultQuestion.content.back.length > 0 && resultQuestion.content.back !== " ") ||
|
(resultQuestion.content.back !== null &&
|
||||||
(resultQuestion.content.originalBack.length > 0 && resultQuestion.content.originalBack !== " ") ||
|
resultQuestion.content.back.length > 0 &&
|
||||||
|
resultQuestion.content.back !== " ") ||
|
||||||
|
(resultQuestion.content.originalBack &&
|
||||||
|
resultQuestion.content.originalBack.length > 0 &&
|
||||||
|
resultQuestion.content.originalBack !== " ") ||
|
||||||
(resultQuestion.content.innerName.length > 0 && resultQuestion.content.innerName !== " ") ||
|
(resultQuestion.content.innerName.length > 0 && resultQuestion.content.innerName !== " ") ||
|
||||||
(resultQuestion.content.text.length > 0 && resultQuestion.content.text !== " ") ||
|
(resultQuestion.content.text.length > 0 && resultQuestion.content.text !== " ") ||
|
||||||
(resultQuestion.content.video.length > 0 && resultQuestion.content.video !== " ") ||
|
(resultQuestion.content.video.length > 0 && resultQuestion.content.video !== " ") ||
|
||||||
|
@ -1,5 +1,8 @@
|
|||||||
import QuizAnswerer from "./components/QuizAnswerer";
|
import QuizAnswerer from "./components/QuizAnswerer";
|
||||||
export type { QuizSettings } from "@model/settingsData";
|
import QuizVideo from "./ui_kit/VideoIframe/VideoIframe";
|
||||||
export type * from "./model/widget";
|
|
||||||
|
|
||||||
export { QuizAnswerer };
|
export type { QuizSettings, QuizConfig } from "@model/settingsData";
|
||||||
|
export type * from "./model/widget";
|
||||||
|
export type * from "@model/questionTypes";
|
||||||
|
|
||||||
|
export { QuizAnswerer, QuizVideo };
|
||||||
|
@ -14,8 +14,8 @@ export interface QuizQuestionDate extends QuizQuestionBase {
|
|||||||
time: boolean;
|
time: boolean;
|
||||||
hint: QuestionHint;
|
hint: QuestionHint;
|
||||||
rule: QuestionBranchingRule;
|
rule: QuestionBranchingRule;
|
||||||
back: string;
|
back: string | null;
|
||||||
originalBack: string;
|
originalBack: string | null;
|
||||||
autofill: boolean;
|
autofill: boolean;
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
@ -17,8 +17,8 @@ export interface QuizQuestionEmoji extends QuizQuestionBase {
|
|||||||
variants: QuestionVariant[];
|
variants: QuestionVariant[];
|
||||||
hint: QuestionHint;
|
hint: QuestionHint;
|
||||||
rule: QuestionBranchingRule;
|
rule: QuestionBranchingRule;
|
||||||
back: string;
|
back: string | null;
|
||||||
originalBack: string;
|
originalBack: string | null;
|
||||||
autofill: boolean;
|
autofill: boolean;
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
@ -24,7 +24,7 @@ export interface QuizQuestionFile extends QuizQuestionBase {
|
|||||||
type: UploadFileType;
|
type: UploadFileType;
|
||||||
hint: QuestionHint;
|
hint: QuestionHint;
|
||||||
rule: QuestionBranchingRule;
|
rule: QuestionBranchingRule;
|
||||||
back: string;
|
back: string | null;
|
||||||
originalBack: string;
|
originalBack: string | null;
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
13
lib/model/questionTypes/index.ts
Normal file
13
lib/model/questionTypes/index.ts
Normal file
@ -0,0 +1,13 @@
|
|||||||
|
export * from "./date";
|
||||||
|
export * from "./emoji";
|
||||||
|
export * from "./file";
|
||||||
|
export * from "./images";
|
||||||
|
export * from "./number";
|
||||||
|
export * from "./page";
|
||||||
|
export * from "./rating";
|
||||||
|
export * from "./result";
|
||||||
|
export * from "./select";
|
||||||
|
export * from "./shared";
|
||||||
|
export * from "./text";
|
||||||
|
export * from "./variant";
|
||||||
|
export * from "./varimg";
|
@ -23,8 +23,8 @@ export interface QuizQuestionNumber extends QuizQuestionBase {
|
|||||||
chooseRange: boolean;
|
chooseRange: boolean;
|
||||||
hint: QuestionHint;
|
hint: QuestionHint;
|
||||||
rule: QuestionBranchingRule;
|
rule: QuestionBranchingRule;
|
||||||
back: string;
|
back: string | null;
|
||||||
originalBack: string;
|
originalBack: string | null;
|
||||||
autofill: boolean;
|
autofill: boolean;
|
||||||
form: "star" | "trophie" | "flag" | "heart" | "like" | "bubble" | "hashtag";
|
form: "star" | "trophie" | "flag" | "heart" | "like" | "bubble" | "hashtag";
|
||||||
};
|
};
|
||||||
|
@ -15,8 +15,8 @@ export interface QuizQuestionPage extends QuizQuestionBase {
|
|||||||
video: string;
|
video: string;
|
||||||
hint: QuestionHint;
|
hint: QuestionHint;
|
||||||
rule: QuestionBranchingRule;
|
rule: QuestionBranchingRule;
|
||||||
back: string;
|
back: string | null;
|
||||||
originalBack: string;
|
originalBack: string | null;
|
||||||
autofill: boolean;
|
autofill: boolean;
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
@ -16,8 +16,8 @@ export interface QuizQuestionRating extends QuizQuestionBase {
|
|||||||
form: string;
|
form: string;
|
||||||
hint: QuestionHint;
|
hint: QuestionHint;
|
||||||
rule: QuestionBranchingRule;
|
rule: QuestionBranchingRule;
|
||||||
back: string;
|
back: string | null;
|
||||||
originalBack: string;
|
originalBack: string | null;
|
||||||
autofill: boolean;
|
autofill: boolean;
|
||||||
/** Позитивное описание рейтинга */
|
/** Позитивное описание рейтинга */
|
||||||
ratingPositiveDescription: string;
|
ratingPositiveDescription: string;
|
||||||
|
@ -7,8 +7,8 @@ export interface QuizQuestionResult extends QuizQuestionBase {
|
|||||||
type: "result";
|
type: "result";
|
||||||
content: {
|
content: {
|
||||||
id: string;
|
id: string;
|
||||||
back: string;
|
back: string | null;
|
||||||
originalBack: string;
|
originalBack: string | null;
|
||||||
video: string;
|
video: string;
|
||||||
innerName: string;
|
innerName: string;
|
||||||
text: string;
|
text: string;
|
||||||
@ -17,6 +17,7 @@ export interface QuizQuestionResult extends QuizQuestionBase {
|
|||||||
rule: ResultQuestionBranchingRule;
|
rule: ResultQuestionBranchingRule;
|
||||||
hint: QuestionHint;
|
hint: QuestionHint;
|
||||||
autofill: boolean;
|
autofill: boolean;
|
||||||
|
usage: boolean;
|
||||||
redirect: string;
|
redirect: string;
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
@ -17,8 +17,8 @@ export interface QuizQuestionSelect extends QuizQuestionBase {
|
|||||||
variants: QuestionVariant[];
|
variants: QuestionVariant[];
|
||||||
rule: QuestionBranchingRule;
|
rule: QuestionBranchingRule;
|
||||||
hint: QuestionHint;
|
hint: QuestionHint;
|
||||||
back: string;
|
back: string | null;
|
||||||
originalBack: string;
|
originalBack: string | null;
|
||||||
autofill: boolean;
|
autofill: boolean;
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
@ -1,4 +1,3 @@
|
|||||||
import { nanoid } from "nanoid";
|
|
||||||
import type { QuizQuestionDate } from "./date";
|
import type { QuizQuestionDate } from "./date";
|
||||||
import type { QuizQuestionEmoji } from "./emoji";
|
import type { QuizQuestionEmoji } from "./emoji";
|
||||||
import type { QuizQuestionFile } from "./file";
|
import type { QuizQuestionFile } from "./file";
|
||||||
@ -64,6 +63,7 @@ export type QuestionType =
|
|||||||
| "result";
|
| "result";
|
||||||
|
|
||||||
export interface QuizQuestionBase {
|
export interface QuizQuestionBase {
|
||||||
|
backendId: number;
|
||||||
/** Stable id, generated on client */
|
/** Stable id, generated on client */
|
||||||
id: string;
|
id: string;
|
||||||
quizId: number;
|
quizId: number;
|
||||||
@ -109,25 +109,3 @@ type FilterQuestionsWithVariants<T> = T extends {
|
|||||||
: never;
|
: never;
|
||||||
|
|
||||||
export type QuizQuestionsWithVariants = FilterQuestionsWithVariants<AnyTypedQuizQuestion>;
|
export type QuizQuestionsWithVariants = FilterQuestionsWithVariants<AnyTypedQuizQuestion>;
|
||||||
|
|
||||||
export const createBranchingRuleMain: (targetId: string, parentId: string) => QuestionBranchingRuleMain = (
|
|
||||||
targetId,
|
|
||||||
parentId
|
|
||||||
) => ({
|
|
||||||
next: targetId,
|
|
||||||
or: false,
|
|
||||||
rules: [
|
|
||||||
{
|
|
||||||
question: parentId,
|
|
||||||
answers: [] as string[],
|
|
||||||
},
|
|
||||||
],
|
|
||||||
});
|
|
||||||
|
|
||||||
export const createQuestionVariant: () => QuestionVariant = () => ({
|
|
||||||
id: nanoid(),
|
|
||||||
answer: "",
|
|
||||||
extendedText: "",
|
|
||||||
hints: "",
|
|
||||||
originalImageUrl: "",
|
|
||||||
});
|
|
||||||
|
@ -13,11 +13,11 @@ export interface QuizQuestionText extends QuizQuestionBase {
|
|||||||
required: boolean;
|
required: boolean;
|
||||||
/** Чекбокс "Автозаполнение адреса" */
|
/** Чекбокс "Автозаполнение адреса" */
|
||||||
autofill: boolean;
|
autofill: boolean;
|
||||||
answerType: "single" | "multi";
|
answerType: "single" | "multi" | "numberOnly";
|
||||||
hint: QuestionHint;
|
hint: QuestionHint;
|
||||||
rule: QuestionBranchingRule;
|
rule: QuestionBranchingRule;
|
||||||
back: string;
|
back: string | null;
|
||||||
originalBack: string;
|
originalBack: string | null;
|
||||||
onlyNumbers: boolean;
|
onlyNumbers: boolean;
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
@ -20,8 +20,8 @@ export interface QuizQuestionVariant extends QuizQuestionBase {
|
|||||||
variants: QuestionVariant[];
|
variants: QuestionVariant[];
|
||||||
hint: QuestionHint;
|
hint: QuestionHint;
|
||||||
rule: QuestionBranchingRule;
|
rule: QuestionBranchingRule;
|
||||||
back: string;
|
back: string | null;
|
||||||
originalBack: string;
|
originalBack: string | null;
|
||||||
autofill: boolean;
|
autofill: boolean;
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
@ -15,8 +15,8 @@ export interface QuizQuestionVarImg extends QuizQuestionBase {
|
|||||||
variants: QuestionVariant[];
|
variants: QuestionVariant[];
|
||||||
hint: QuestionHint;
|
hint: QuestionHint;
|
||||||
rule: QuestionBranchingRule;
|
rule: QuestionBranchingRule;
|
||||||
back: string;
|
back: string | null;
|
||||||
originalBack: string;
|
originalBack: string | null;
|
||||||
autofill: boolean;
|
autofill: boolean;
|
||||||
largeCheck: boolean;
|
largeCheck: boolean;
|
||||||
replText: string;
|
replText: string;
|
||||||
|
43
lib/ui_kit/VideoIframe/VideoIframe.tsx
Normal file
43
lib/ui_kit/VideoIframe/VideoIframe.tsx
Normal file
@ -0,0 +1,43 @@
|
|||||||
|
import { Box } from "@mui/material";
|
||||||
|
import { getVideo } from "./helper";
|
||||||
|
import type { SxProps } from "@mui/material";
|
||||||
|
import useSWR from "swr";
|
||||||
|
import LoadingSkeleton from "../LoadingSkeleton";
|
||||||
|
import { ApologyPage } from "@/components/ViewPublicationPage/ApologyPage";
|
||||||
|
|
||||||
|
type VideoIframeProps = {
|
||||||
|
videoUrl: string;
|
||||||
|
containerSX?: SxProps;
|
||||||
|
};
|
||||||
|
|
||||||
|
export default function QuizVideo({ videoUrl, containerSX }: VideoIframeProps) {
|
||||||
|
const { data: videoData, error, isLoading } = useSWR(["video", videoUrl], (params) => getVideo(params[1]));
|
||||||
|
|
||||||
|
return (
|
||||||
|
<Box
|
||||||
|
sx={{
|
||||||
|
width: "100%",
|
||||||
|
height: "100%",
|
||||||
|
minHeight: videoData?.sourceName === "tiktok" ? "740px" : 0,
|
||||||
|
"& iframe": { width: "100%", height: "100%" },
|
||||||
|
...containerSX,
|
||||||
|
}}
|
||||||
|
>
|
||||||
|
{isLoading ? (
|
||||||
|
<LoadingSkeleton />
|
||||||
|
) : !videoData || error ? (
|
||||||
|
<ApologyPage error={error ?? new Error()} />
|
||||||
|
) : videoData.sourceName === "custom" || videoData.sourceName === "yandex" ? (
|
||||||
|
<Box component="video" sx={{ width: "100%", height: "100%" }} autoPlay controls muted src={videoData.url} />
|
||||||
|
) : (
|
||||||
|
<Box
|
||||||
|
component="iframe"
|
||||||
|
src={videoData.url}
|
||||||
|
title={videoData.sourceName}
|
||||||
|
allow="accelerometer; autoplay; clipboard-write; encrypted-media; gyroscope; picture-in-picture; web-share"
|
||||||
|
{...{ allowFullScreen: true, frameBorder: 0 }}
|
||||||
|
/>
|
||||||
|
)}
|
||||||
|
</Box>
|
||||||
|
);
|
||||||
|
}
|
165
lib/ui_kit/VideoIframe/helper.ts
Normal file
165
lib/ui_kit/VideoIframe/helper.ts
Normal file
@ -0,0 +1,165 @@
|
|||||||
|
import axios from "axios";
|
||||||
|
|
||||||
|
type SourceType = "youtube" | "short" | "vk" | "google" | "yandex" | "mail" | "tiktok" | "custom";
|
||||||
|
|
||||||
|
export type VideoUrlResult = {
|
||||||
|
sourceName: SourceType;
|
||||||
|
url: string;
|
||||||
|
};
|
||||||
|
|
||||||
|
type TiktokResponse = {
|
||||||
|
version: string;
|
||||||
|
type: string;
|
||||||
|
title: string;
|
||||||
|
author_url: string;
|
||||||
|
author_name: string;
|
||||||
|
width: string;
|
||||||
|
height: string;
|
||||||
|
html: string;
|
||||||
|
thumbnail_width: number;
|
||||||
|
thumbnail_height: number;
|
||||||
|
thumbnail_url: string;
|
||||||
|
provider_url: string;
|
||||||
|
provider_name: string;
|
||||||
|
author_unique_id: string;
|
||||||
|
embed_product_id: string;
|
||||||
|
embed_type: string;
|
||||||
|
};
|
||||||
|
|
||||||
|
type YandexResponse = {
|
||||||
|
antivirus_status: string;
|
||||||
|
views_count: number;
|
||||||
|
resource_id: string;
|
||||||
|
file: string;
|
||||||
|
owner: {
|
||||||
|
login: string;
|
||||||
|
display_name: string;
|
||||||
|
uid: string;
|
||||||
|
};
|
||||||
|
size: number;
|
||||||
|
exif: unknown;
|
||||||
|
media_type: string;
|
||||||
|
preview: string;
|
||||||
|
type: string;
|
||||||
|
mime_type: string;
|
||||||
|
revision: number;
|
||||||
|
public_url: string;
|
||||||
|
path: string;
|
||||||
|
md5: string;
|
||||||
|
public_key: string;
|
||||||
|
sha256: string;
|
||||||
|
name: string;
|
||||||
|
created: string;
|
||||||
|
sizes: {
|
||||||
|
url: string;
|
||||||
|
name: string;
|
||||||
|
}[];
|
||||||
|
modified: string;
|
||||||
|
comment_ids: {
|
||||||
|
private_resource: string;
|
||||||
|
public_resource: string;
|
||||||
|
};
|
||||||
|
};
|
||||||
|
|
||||||
|
const FILTER_SITES_REGEX: Record<Exclude<SourceType, "short" | "custom">, RegExp> = {
|
||||||
|
youtube: /^(https?:\/\/)?(www\.)?((m\.youtube|youtube)\.com|youtu\.be)\/.+$/,
|
||||||
|
vk: /^(https?:\/\/)?(m.)?vk\..+$/,
|
||||||
|
tiktok: /^(https?:\/\/)?((www|vt).)?tiktok\..+$/,
|
||||||
|
google: /^(https?:\/\/)?(www.)?drive\.google\..+$/,
|
||||||
|
yandex: /^(https?:\/\/)?disk\.yandex\..+$/,
|
||||||
|
mail: /^(https?:\/\/)?cloud\.mail\..+$/,
|
||||||
|
};
|
||||||
|
|
||||||
|
const EXTRACT_VIDEO_ID_REGEX: Record<Exclude<SourceType, "custom">, RegExp> = {
|
||||||
|
youtube: /(?<=v=|v\/|d\/|be\/|embed\/)[\w-]+/,
|
||||||
|
short: /(?<=v=|v\/|d\/|be\/|embed\/)[\w-]+/,
|
||||||
|
vk: /(-?(\d+)_(\d+))/,
|
||||||
|
google: /(?<=(file\/d\/))[\w-]+/,
|
||||||
|
yandex: /(?<=i\/)[\w-]+/,
|
||||||
|
mail: /$/,
|
||||||
|
tiktok: /(?<=video\/|\.com\/)[\w-]+/,
|
||||||
|
};
|
||||||
|
|
||||||
|
export const getVideo = async (videoUrl: string): Promise<VideoUrlResult> => {
|
||||||
|
if (videoUrl.match(FILTER_SITES_REGEX.youtube)?.[0]) {
|
||||||
|
if (videoUrl.includes("youtube.com/shorts")) {
|
||||||
|
const videoId = videoUrl.match(EXTRACT_VIDEO_ID_REGEX.short)?.[0];
|
||||||
|
|
||||||
|
return {
|
||||||
|
sourceName: "short",
|
||||||
|
url: `https://www.youtube.com/embed/${videoId}?controls=0&autoplay=1&modestbranding=0&showinfo=0&disablekb=1&mute=1&loop=1`,
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
const videoId = videoUrl.match(EXTRACT_VIDEO_ID_REGEX.youtube)?.[0];
|
||||||
|
return {
|
||||||
|
sourceName: "youtube",
|
||||||
|
url: `https://www.youtube.com/embed/${videoId}?controls=0&autoplay=1&modestbranding=0&showinfo=0&disablekb=1&mute=1&loop=1`,
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
if (videoUrl.match(FILTER_SITES_REGEX.vk)) {
|
||||||
|
const videoId = videoUrl.match(EXTRACT_VIDEO_ID_REGEX.vk)?.[0];
|
||||||
|
|
||||||
|
return {
|
||||||
|
sourceName: "vk",
|
||||||
|
url: `https://vk.com/video_ext.php?oid=${videoId?.split("_")[0]}&id=${videoId?.split("_")[1]}`,
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
if (videoUrl.match(FILTER_SITES_REGEX.tiktok)) {
|
||||||
|
const videoId = videoUrl.match(EXTRACT_VIDEO_ID_REGEX.tiktok)?.[0] ?? "";
|
||||||
|
|
||||||
|
if (/[a-zA-Z]/.test(videoId)) {
|
||||||
|
try {
|
||||||
|
const { data } = await axios.get<TiktokResponse>("https://www.tiktok.com/oembed", {
|
||||||
|
params: { url: videoUrl },
|
||||||
|
});
|
||||||
|
|
||||||
|
return {
|
||||||
|
sourceName: "tiktok",
|
||||||
|
url: `https://www.tiktok.com/embed/v2/${data.embed_product_id}?embedFrom=embed_page_preview`,
|
||||||
|
};
|
||||||
|
} catch {}
|
||||||
|
|
||||||
|
return {
|
||||||
|
sourceName: "tiktok",
|
||||||
|
url: `https://www.tiktok.com/embed/v2/${videoId}?embedFrom=embed_page_preview`,
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
return {
|
||||||
|
sourceName: "tiktok",
|
||||||
|
url: `https://www.tiktok.com/embed/v2/${videoId}?embedFrom=embed_page_preview`,
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
if (videoUrl.match(FILTER_SITES_REGEX.google)) {
|
||||||
|
const videoId = videoUrl.match(EXTRACT_VIDEO_ID_REGEX.google)?.[0];
|
||||||
|
|
||||||
|
return {
|
||||||
|
sourceName: "google",
|
||||||
|
url: `https://drive.google.com/file/d/${videoId}/preview`,
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
if (videoUrl.match(FILTER_SITES_REGEX.yandex)) {
|
||||||
|
const videoId = videoUrl.match(EXTRACT_VIDEO_ID_REGEX.yandex);
|
||||||
|
|
||||||
|
try {
|
||||||
|
const { data } = await axios.get<YandexResponse>("https://cloud-api.yandex.net/v1/disk/public/resources", {
|
||||||
|
params: { public_key: `https://disk.yandex.ru/i/${videoId}` },
|
||||||
|
});
|
||||||
|
|
||||||
|
return { sourceName: "yandex", url: data.file };
|
||||||
|
} catch {}
|
||||||
|
|
||||||
|
return { sourceName: "yandex", url: "" };
|
||||||
|
}
|
||||||
|
|
||||||
|
if (videoUrl.match(FILTER_SITES_REGEX.mail)) {
|
||||||
|
return { sourceName: "mail", url: videoUrl };
|
||||||
|
}
|
||||||
|
|
||||||
|
return { sourceName: "custom", url: videoUrl };
|
||||||
|
};
|
29
lib/utils/extractImageLinks.ts
Normal file
29
lib/utils/extractImageLinks.ts
Normal file
@ -0,0 +1,29 @@
|
|||||||
|
import { AnyTypedQuizQuestion } from "@/model/questionTypes";
|
||||||
|
|
||||||
|
export function extractImageLinksFromQuestion(question: AnyTypedQuizQuestion | null | undefined): string[] {
|
||||||
|
if (!question) return [];
|
||||||
|
|
||||||
|
const links: string[] = [];
|
||||||
|
|
||||||
|
if (question.type === "images") {
|
||||||
|
question.content.variants.forEach((variant) => {
|
||||||
|
if (variant.extendedText.startsWith("https://")) links.push(variant.extendedText);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
if (question.type === "varimg") {
|
||||||
|
question.content.variants.forEach((variant) => {
|
||||||
|
if (variant.extendedText.startsWith("https://")) links.push(variant.extendedText);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
if (question.type === "page") {
|
||||||
|
if (question.content.back?.startsWith("https://")) links.push(question.content.back);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (question.type === "result") {
|
||||||
|
if (question.content.back?.startsWith("https://")) links.push(question.content.back);
|
||||||
|
}
|
||||||
|
|
||||||
|
return links;
|
||||||
|
}
|
@ -197,6 +197,7 @@ export function useQuestionFlowControl() {
|
|||||||
return {
|
return {
|
||||||
currentQuestion,
|
currentQuestion,
|
||||||
currentQuestionStepNumber: linearQuestionIndex === null ? null : linearQuestionIndex + 1,
|
currentQuestionStepNumber: linearQuestionIndex === null ? null : linearQuestionIndex + 1,
|
||||||
|
nextQuestion,
|
||||||
isNextButtonEnabled,
|
isNextButtonEnabled,
|
||||||
isPreviousButtonEnabled,
|
isPreviousButtonEnabled,
|
||||||
moveToPrevQuestion,
|
moveToPrevQuestion,
|
||||||
|
@ -1,6 +1,6 @@
|
|||||||
{
|
{
|
||||||
"name": "@frontend/squzanswerer",
|
"name": "@frontend/squzanswerer",
|
||||||
"version": "1.0.44",
|
"version": "1.0.51",
|
||||||
"type": "module",
|
"type": "module",
|
||||||
"main": "./dist-package/index.js",
|
"main": "./dist-package/index.js",
|
||||||
"module": "./dist-package/index.js",
|
"module": "./dist-package/index.js",
|
||||||
@ -26,7 +26,7 @@
|
|||||||
"preview": "vite preview",
|
"preview": "vite preview",
|
||||||
"cypress:open": "cypress open",
|
"cypress:open": "cypress open",
|
||||||
"prepublishOnly": "npm run build:package",
|
"prepublishOnly": "npm run build:package",
|
||||||
"prepare": "husky install"
|
"prepare": "husky"
|
||||||
},
|
},
|
||||||
"devDependencies": {
|
"devDependencies": {
|
||||||
"@emoji-mart/data": "^1.1.2",
|
"@emoji-mart/data": "^1.1.2",
|
||||||
@ -39,6 +39,7 @@
|
|||||||
"@types/node": "^16.7.13",
|
"@types/node": "^16.7.13",
|
||||||
"@types/react": "^18.2.43",
|
"@types/react": "^18.2.43",
|
||||||
"@types/react-dom": "^18.2.17",
|
"@types/react-dom": "^18.2.17",
|
||||||
|
"@types/react-helmet": "^6.1.11",
|
||||||
"@typescript-eslint/eslint-plugin": "^6.14.0",
|
"@typescript-eslint/eslint-plugin": "^6.14.0",
|
||||||
"@typescript-eslint/parser": "^6.14.0",
|
"@typescript-eslint/parser": "^6.14.0",
|
||||||
"@vitejs/plugin-react": "^4.2.1",
|
"@vitejs/plugin-react": "^4.2.1",
|
||||||
@ -95,6 +96,7 @@
|
|||||||
"hex-rgb": "^5.0.0",
|
"hex-rgb": "^5.0.0",
|
||||||
"mobile-detect": "^1.4.5",
|
"mobile-detect": "^1.4.5",
|
||||||
"mui-tel-input": "^5.1.2",
|
"mui-tel-input": "^5.1.2",
|
||||||
|
"react-helmet": "^6.1.0",
|
||||||
"react-imask": "^7.6.0",
|
"react-imask": "^7.6.0",
|
||||||
"react-phone-number-input": "^3.4.1"
|
"react-phone-number-input": "^3.4.1"
|
||||||
},
|
},
|
||||||
@ -108,7 +110,8 @@
|
|||||||
"endOfLine": "auto",
|
"endOfLine": "auto",
|
||||||
"bracketSpacing": true,
|
"bracketSpacing": true,
|
||||||
"arrowParens": "always",
|
"arrowParens": "always",
|
||||||
"jsxSingleQuote": false
|
"jsxSingleQuote": false,
|
||||||
|
"singleAttributePerLine": true
|
||||||
},
|
},
|
||||||
"lint-staged": {
|
"lint-staged": {
|
||||||
"*": "prettier --write --ignore-unknown"
|
"*": "prettier --write --ignore-unknown"
|
||||||
|
@ -66,10 +66,10 @@ export default function QuizBanner({
|
|||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
document.addEventListener("mouseleave", handleMouseLeave);
|
document.documentElement.addEventListener("mouseleave", handleMouseLeave);
|
||||||
|
|
||||||
return () => {
|
return () => {
|
||||||
document.removeEventListener("mouseleave", handleMouseLeave);
|
document.documentElement.removeEventListener("mouseleave", handleMouseLeave);
|
||||||
};
|
};
|
||||||
},
|
},
|
||||||
[openOnLeaveAttempt]
|
[openOnLeaveAttempt]
|
||||||
@ -85,7 +85,7 @@ export default function QuizBanner({
|
|||||||
if (!quizData) return null;
|
if (!quizData) return null;
|
||||||
|
|
||||||
const isQuizCompleted = quizData.settings.cfg.antifraud ? quizData.recentlyCompleted : false;
|
const isQuizCompleted = quizData.settings.cfg.antifraud ? quizData.recentlyCompleted : false;
|
||||||
const showButtonFlash = isQuizCompleted && isFlashEnabled;
|
const showButtonFlash = !isQuizCompleted && isFlashEnabled;
|
||||||
|
|
||||||
let hoverBackgroundColor = buttonBackgroundColor;
|
let hoverBackgroundColor = buttonBackgroundColor;
|
||||||
if (buttonBackgroundColor) {
|
if (buttonBackgroundColor) {
|
||||||
|
@ -74,7 +74,7 @@ export default function OpenQuizButton({
|
|||||||
if (!quizData) return null;
|
if (!quizData) return null;
|
||||||
|
|
||||||
const isQuizCompleted = quizData.settings.cfg.antifraud ? quizData.recentlyCompleted : false;
|
const isQuizCompleted = quizData.settings.cfg.antifraud ? quizData.recentlyCompleted : false;
|
||||||
const showButtonFlash = isQuizCompleted && isFlashEnabled;
|
const showButtonFlash = !isQuizCompleted && isFlashEnabled;
|
||||||
|
|
||||||
let hoverBackgroundColor = buttonBackgroundColor;
|
let hoverBackgroundColor = buttonBackgroundColor;
|
||||||
if (buttonBackgroundColor) {
|
if (buttonBackgroundColor) {
|
||||||
|
@ -56,7 +56,7 @@ export default function QuizSideButton({
|
|||||||
if (!quizData) return null;
|
if (!quizData) return null;
|
||||||
|
|
||||||
const isQuizCompleted = quizData.settings.cfg.antifraud ? quizData.recentlyCompleted : false;
|
const isQuizCompleted = quizData.settings.cfg.antifraud ? quizData.recentlyCompleted : false;
|
||||||
const showButtonFlash = isQuizCompleted && isFlashEnabled;
|
const showButtonFlash = !isQuizCompleted && isFlashEnabled;
|
||||||
|
|
||||||
return createPortal(
|
return createPortal(
|
||||||
<ThemeProvider theme={lightTheme}>
|
<ThemeProvider theme={lightTheme}>
|
||||||
|
29
yarn.lock
29
yarn.lock
@ -985,6 +985,13 @@
|
|||||||
dependencies:
|
dependencies:
|
||||||
"@types/react" "*"
|
"@types/react" "*"
|
||||||
|
|
||||||
|
"@types/react-helmet@^6.1.11":
|
||||||
|
version "6.1.11"
|
||||||
|
resolved "https://registry.yarnpkg.com/@types/react-helmet/-/react-helmet-6.1.11.tgz#8cafcafff38f75361f451563ba7b406b0c5d3907"
|
||||||
|
integrity sha512-0QcdGLddTERotCXo3VFlUSWO3ztraw8nZ6e3zJSgG7apwV5xt+pJUS8ewPBqT4NYB1optGLprNQzFleIY84u/g==
|
||||||
|
dependencies:
|
||||||
|
"@types/react" "*"
|
||||||
|
|
||||||
"@types/react-transition-group@^4.4.10", "@types/react-transition-group@^4.4.8":
|
"@types/react-transition-group@^4.4.10", "@types/react-transition-group@^4.4.8":
|
||||||
version "4.4.10"
|
version "4.4.10"
|
||||||
resolved "https://registry.yarnpkg.com/@types/react-transition-group/-/react-transition-group-4.4.10.tgz#6ee71127bdab1f18f11ad8fb3322c6da27c327ac"
|
resolved "https://registry.yarnpkg.com/@types/react-transition-group/-/react-transition-group-4.4.10.tgz#6ee71127bdab1f18f11ad8fb3322c6da27c327ac"
|
||||||
@ -3213,7 +3220,7 @@ process@^0.11.10:
|
|||||||
resolved "https://registry.yarnpkg.com/process/-/process-0.11.10.tgz#7332300e840161bda3e69a1d1d91a7d4bc16f182"
|
resolved "https://registry.yarnpkg.com/process/-/process-0.11.10.tgz#7332300e840161bda3e69a1d1d91a7d4bc16f182"
|
||||||
integrity sha512-cdGef/drWFoydD1JsMzuFf8100nZl+GT+yacc2bEced5f9Rjk4z+WtFUTBu9PhOi9j/jfmBPu0mMEY4wIdAF8A==
|
integrity sha512-cdGef/drWFoydD1JsMzuFf8100nZl+GT+yacc2bEced5f9Rjk4z+WtFUTBu9PhOi9j/jfmBPu0mMEY4wIdAF8A==
|
||||||
|
|
||||||
prop-types@^15.6.2, prop-types@^15.8.1:
|
prop-types@^15.6.2, prop-types@^15.7.2, prop-types@^15.8.1:
|
||||||
version "15.8.1"
|
version "15.8.1"
|
||||||
resolved "https://registry.yarnpkg.com/prop-types/-/prop-types-15.8.1.tgz#67d87bf1a694f48435cf332c24af10214a3140b5"
|
resolved "https://registry.yarnpkg.com/prop-types/-/prop-types-15.8.1.tgz#67d87bf1a694f48435cf332c24af10214a3140b5"
|
||||||
integrity sha512-oj87CgZICdulUohogVAR7AjlC0327U4el4L6eAvOqCeudMDVU0NThNaV+b9Df4dXgSP1gXMTnPdhfe/2qDH5cg==
|
integrity sha512-oj87CgZICdulUohogVAR7AjlC0327U4el4L6eAvOqCeudMDVU0NThNaV+b9Df4dXgSP1gXMTnPdhfe/2qDH5cg==
|
||||||
@ -3282,6 +3289,21 @@ react-error-boundary@^4.0.12:
|
|||||||
dependencies:
|
dependencies:
|
||||||
"@babel/runtime" "^7.12.5"
|
"@babel/runtime" "^7.12.5"
|
||||||
|
|
||||||
|
react-fast-compare@^3.1.1:
|
||||||
|
version "3.2.2"
|
||||||
|
resolved "https://registry.yarnpkg.com/react-fast-compare/-/react-fast-compare-3.2.2.tgz#929a97a532304ce9fee4bcae44234f1ce2c21d49"
|
||||||
|
integrity sha512-nsO+KSNgo1SbJqJEYRE9ERzo7YtYbou/OqjSQKxV7jcKox7+usiUVZOAC+XnDOABXggQTno0Y1CpVnuWEc1boQ==
|
||||||
|
|
||||||
|
react-helmet@^6.1.0:
|
||||||
|
version "6.1.0"
|
||||||
|
resolved "https://registry.yarnpkg.com/react-helmet/-/react-helmet-6.1.0.tgz#a750d5165cb13cf213e44747502652e794468726"
|
||||||
|
integrity sha512-4uMzEY9nlDlgxr61NL3XbKRy1hEkXmKNXhjbAIOVw5vcFrsdYbH2FEwcNyWvWinl103nXgzYNlns9ca+8kFiWw==
|
||||||
|
dependencies:
|
||||||
|
object-assign "^4.1.1"
|
||||||
|
prop-types "^15.7.2"
|
||||||
|
react-fast-compare "^3.1.1"
|
||||||
|
react-side-effect "^2.1.0"
|
||||||
|
|
||||||
react-imask@^7.6.0:
|
react-imask@^7.6.0:
|
||||||
version "7.6.0"
|
version "7.6.0"
|
||||||
resolved "https://registry.yarnpkg.com/react-imask/-/react-imask-7.6.0.tgz#5948fc39e1d7d036292d4fade43df4636d43e7b7"
|
resolved "https://registry.yarnpkg.com/react-imask/-/react-imask-7.6.0.tgz#5948fc39e1d7d036292d4fade43df4636d43e7b7"
|
||||||
@ -3331,6 +3353,11 @@ react-router@6.23.1:
|
|||||||
dependencies:
|
dependencies:
|
||||||
"@remix-run/router" "1.16.1"
|
"@remix-run/router" "1.16.1"
|
||||||
|
|
||||||
|
react-side-effect@^2.1.0:
|
||||||
|
version "2.1.2"
|
||||||
|
resolved "https://registry.yarnpkg.com/react-side-effect/-/react-side-effect-2.1.2.tgz#dc6345b9e8f9906dc2eeb68700b615e0b4fe752a"
|
||||||
|
integrity sha512-PVjOcvVOyIILrYoyGEpDN3vmYNLdy1CajSFNt4TDsVQC5KpTijDvWVoR+/7Rz2xT978D8/ZtFceXxzsPwZEDvw==
|
||||||
|
|
||||||
react-transition-group@^4.4.5:
|
react-transition-group@^4.4.5:
|
||||||
version "4.4.5"
|
version "4.4.5"
|
||||||
resolved "https://registry.yarnpkg.com/react-transition-group/-/react-transition-group-4.4.5.tgz#e53d4e3f3344da8521489fbef8f2581d42becdd1"
|
resolved "https://registry.yarnpkg.com/react-transition-group/-/react-transition-group-4.4.5.tgz#e53d4e3f3344da8521489fbef8f2581d42becdd1"
|
||||||
|
Loading…
Reference in New Issue
Block a user