fix видео на стартовых отображаются

This commit is contained in:
Nastya 2025-10-06 00:06:55 +03:00
parent ba6bb0afb9
commit 4010a209e6
18 changed files with 109 additions and 134 deletions

@ -11,7 +11,6 @@ const getLanguageFromUrlFallback = (): string => {
};
export const NameplateLogo: FC<SVGProps<SVGSVGElement>> = (props) => {
console.log("props", props);
let lang = "ru"; // fallback
try {

@ -51,8 +51,6 @@ function QuizAnswererInner({
const yandexMetrics = useYandexMetricsGoals(quizSettings?.settings.cfg.yandexMetricsNumber);
const r = useQuizStore();
const { settings, questions } = useQuizStore();
console.log("settings", settings);
console.log("questions", questions);
useEffect(() => {
addquizid(quizId);

@ -76,7 +76,7 @@ const StandartLayout = ({ alignType, quizHeaderBlock, quizMainBlock, backgroundB
{quizHeaderBlock}
{quizMainBlock}
</Box>
{settings.cfg.startpage.background.desktop && (
{(settings.cfg.startpage.background.desktop || settings.cfg.startpage.background.type === "video") && (
<Box sx={{ width: "60%", overflow: "hidden" }}>
<Box
sx={{
@ -195,21 +195,22 @@ const CenteredLayout = ({ quizHeaderBlock, quizMainBlock, backgroundBlock }: Lay
}}
>
{quizHeaderBlock}
{backgroundBlock && settings.cfg.startpage.background.desktop && (
<Box
sx={{
width: "100%",
maxWidth: "844px",
height: isTablet ? "530px" : "306px",
display: "flex",
justifyContent: "center",
"& > img": { width: "100%", borderRadius: "12px" },
}}
onClick={(event) => event.preventDefault()}
>
{backgroundBlock}
</Box>
)}
{backgroundBlock &&
(settings.cfg.startpage.background.desktop || settings.cfg.startpage.background.type === "video") && (
<Box
sx={{
width: "100%",
maxWidth: "844px",
height: isTablet ? "530px" : "306px",
display: "flex",
justifyContent: "center",
"& > img": { width: "100%", borderRadius: "12px" },
}}
onClick={(event) => event.preventDefault()}
>
{backgroundBlock}
</Box>
)}
{quizMainBlock}
</Box>
);

@ -60,7 +60,7 @@ const StandartMobileLayout = ({ quizHeaderBlock, quizMainBlock, backgroundBlock
}}
>
<Box sx={{ marginBottom: "13px" }}>{quizHeaderBlock}</Box>
{settings.cfg.startpage.background.desktop && (
{(settings.cfg.startpage.background.desktop || settings.cfg.startpage.background.type === "video") && (
<Box sx={{ width: "100%", overflow: "hidden" }}>
<Box
sx={{
@ -200,7 +200,7 @@ const CenteredMobileLayout = ({ quizHeaderBlock, quizMainBlock, backgroundBlock
}}
>
{quizHeaderBlock}
{settings.cfg.startpage.background.desktop && (
{(settings.cfg.startpage.background.desktop || settings.cfg.startpage.background.type === "video") && (
<Box
sx={{
width: "100%",

@ -39,6 +39,15 @@ export const StartPageViewPublication = () => {
yandexMetrics.phoneNumberOpened();
};
console.log("------------------------------------------------");
console.log("Background type:", settings.cfg.startpage.background.type);
console.log("Is image type:", settings.cfg.startpage.background.type === "image");
console.log("Is video type:", settings.cfg.startpage.background.type === "video");
console.log("Video URL:", settings.cfg.startpage.background.video);
console.log("Desktop background:", settings.cfg.startpage.background.desktop);
console.log("Startpage type:", settings.cfg.startpageType);
console.log("------------------------------------------------");
const background =
settings.cfg.startpage.background.type === "image" ? (
<img
@ -56,25 +65,30 @@ export const StartPageViewPublication = () => {
/>
) : settings.cfg.startpage.background.type === "video" ? (
settings.cfg.startpage.background.video ? (
<QuizVideo
videoUrl={settings.cfg.startpage.background.video}
containerSX={{
width: settings.cfg.startpageType === "centered" ? "550px" : "100%",
height: settings.cfg.startpageType === "centered" ? "275px" : "100%",
borderRadius: settings.cfg.startpageType === "centered" ? "10px" : "0",
overflow: "hidden",
"& iframe": {
width: "100%",
height: "100%",
transform:
settings.cfg.startpageType === "centered"
? ""
: settings.cfg.startpageType === "expanded"
? "scale(1.5)"
: "scale(2.4)",
},
}}
/>
(() => {
console.log("Rendering QuizVideo with URL:", settings.cfg.startpage.background.video);
return (
<QuizVideo
videoUrl={settings.cfg.startpage.background.video}
containerSX={{
width: settings.cfg.startpageType === "centered" ? "550px" : "100%",
height: settings.cfg.startpageType === "centered" ? "275px" : "100%",
borderRadius: settings.cfg.startpageType === "centered" ? "10px" : "0",
overflow: "hidden",
"& iframe": {
width: "100%",
height: "100%",
transform:
settings.cfg.startpageType === "centered"
? ""
: settings.cfg.startpageType === "expanded"
? "scale(1.5)"
: "scale(2.4)",
},
}}
/>
);
})()
) : null
) : null;

@ -57,10 +57,18 @@ export default function ViewPublicationPage() {
useEffect(
function setFaviconAndTitle() {
if (!changeFaviconAndTitle) return;
if (!changeFaviconAndTitle) {
return;
}
const link = document.querySelector('link[rel="icon"]');
if (link && settings.cfg.startpage.favIcon) {
// Для localhost:5173 устанавливаем фавиконку из настроек квиза
if (window.location.hostname === "localhost" && window.location.port === "5173") {
if (link && settings.cfg.startpage.favIcon) {
link.setAttribute("href", settings.cfg.startpage.favIcon);
}
} else if (link && settings.cfg.startpage.favIcon) {
link.setAttribute("href", settings.cfg.startpage.favIcon);
}
@ -74,7 +82,6 @@ export default function ViewPublicationPage() {
// Обработка noStartPage - обновляем состояние в store
useEffect(() => {
if (currentQuizStep === "startpage" && settings.cfg.noStartPage) {
console.log("🔄 noStartPage: Setting currentQuizStep to 'question'");
setCurrentQuizStep("question");
}
}, [currentQuizStep, settings.cfg.noStartPage, setCurrentQuizStep]);

@ -28,18 +28,11 @@ interface OwnInputProps {
}
const OwnInput = ({ questionId, variant, largeCheck, ownPlaceholder }: OwnInputProps) => {
const theme = useTheme();
console.log("здравствуй, я овн вопрос. Вот инфо обо мне: ");
console.log("questionId ", questionId);
console.log("variant", variant);
console.log("largeCheck", largeCheck);
console.log("ownPlaceholder", ownPlaceholder);
console.log("--------------------------");
const ownVariants = useQuizViewStore((state) => state.ownVariants);
const { updateOwnVariant } = useQuizViewStore((state) => state);
const ownAnswer = ownVariants[ownVariants.findIndex((v) => v.id === variant.id)]?.variant.answer || "";
console.log("Вот каков стор в данный момент времени:", ownVariants);
return largeCheck ? (
<TextareaAutosize
@ -66,7 +59,6 @@ const OwnInput = ({ questionId, variant, largeCheck, ownPlaceholder }: OwnInputP
value={ownAnswer}
onClick={(e: React.MouseEvent<HTMLTextAreaElement>) => e.stopPropagation()}
onChange={(e: React.ChangeEvent<HTMLTextAreaElement>) => {
console.log("клик", e.target.value);
updateOwnVariant(variant.id, e.target.value);
}}
/>

@ -99,8 +99,6 @@ export const createQuizViewStore = () =>
updateOwnVariant(id, answer, extendedText, originalImageUrl, localImageUrl) {
set(
(state) => {
console.log("!!! STORE !!! ___ Случился вызов варианта свой ответ. Вот что я получил:");
console.log(id, answer, extendedText, originalImageUrl, localImageUrl);
const index = state.ownVariants.findIndex((variant) => variant.id === id);
if (index < 0) {

@ -11,10 +11,20 @@ type VideoIframeProps = {
};
export default function QuizVideo({ videoUrl, containerSX }: VideoIframeProps) {
console.log("=== QuizVideo component called ===");
console.log("QuizVideo component called with URL:", videoUrl);
console.log("ContainerSX:", containerSX);
const { data: videoData, error, isLoading } = useSWR(["video", videoUrl], (params) => getVideo(params[1]));
console.log("Video data:", videoData);
console.log("Error:", error);
console.log("Is loading:", isLoading);
console.log("=== End QuizVideo component ===");
return (
<Box
id="test-video-container"
sx={{
width: "100%",
height: "100%",
@ -28,7 +38,19 @@ export default function QuizVideo({ videoUrl, containerSX }: VideoIframeProps) {
) : !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="video"
sx={{ width: "100%", height: "100%" }}
autoPlay
controls
muted
loop
playsInline
onError={(e) => console.error("Video error:", e)}
onLoadStart={() => console.log("Video load started")}
onCanPlay={() => console.log("Video can play")}
src={videoData.url}
/>
) : (
<Box
component="iframe"

@ -1,6 +1,6 @@
import axios from "axios";
type SourceType = "youtube" | "short" | "vk" | "google" | "yandex" | "mail" | "tiktok" | "custom";
type SourceType = "youtube" | "short" | "vk" | "google" | "yandex" | "mail" | "tiktok" | "s3" | "custom";
export type VideoUrlResult = {
sourceName: SourceType;
@ -68,6 +68,7 @@ const FILTER_SITES_REGEX: Record<Exclude<SourceType, "short" | "custom">, RegExp
google: /^(https?:\/\/)?(www.)?drive\.google\..+$/,
yandex: /^(https?:\/\/)?disk\.yandex\..+$/,
mail: /^(https?:\/\/)?cloud\.mail\..+$/,
s3: /^(https?:\/\/)?.*\.s3\..+$/,
};
const EXTRACT_VIDEO_ID_REGEX: Record<Exclude<SourceType, "custom">, RegExp> = {
@ -78,9 +79,12 @@ const EXTRACT_VIDEO_ID_REGEX: Record<Exclude<SourceType, "custom">, RegExp> = {
yandex: /(?<=i\/)[\w-]+/,
mail: /$/,
tiktok: /(?<=video\/|\.com\/)[\w-]+/,
s3: /$/,
};
export const getVideo = async (videoUrl: string): Promise<VideoUrlResult> => {
console.log("getVideo called with URL:", videoUrl);
if (videoUrl.match(FILTER_SITES_REGEX.youtube)?.[0]) {
if (videoUrl.includes("youtube.com/shorts")) {
const videoId = videoUrl.match(EXTRACT_VIDEO_ID_REGEX.short)?.[0];
@ -161,5 +165,11 @@ export const getVideo = async (videoUrl: string): Promise<VideoUrlResult> => {
return { sourceName: "mail", url: videoUrl };
}
if (videoUrl.match(FILTER_SITES_REGEX.s3)) {
console.log("S3 URL detected, returning as custom video");
return { sourceName: "custom", url: videoUrl };
}
console.log("No matching source found, returning custom:", videoUrl);
return { sourceName: "custom", url: videoUrl };
};

@ -15,14 +15,6 @@ export function useAIQuiz() {
//Получаем инфо о квизе и список вопросов.
const { settings, questions, quizId, cnt, quizStep, preview } = useQuizStore();
// Отладочная информация о настройках таймера
console.log("🤖 AI Quiz settings:", {
questionTimerEnabled: settings.questionTimerEnabled,
time_of_passing: settings.cfg.time_of_passing,
timerEnabled: Boolean(settings.questionTimerEnabled),
timerSeconds: settings.cfg.time_of_passing ?? 0,
});
//Список ответов на вопрос. Мы записываем ответы локально, параллельно отправляя на бек информацию о ответах
const answers = useQuizViewStore((state) => state.answers);
@ -113,7 +105,6 @@ export function useAIQuiz() {
preview,
currentQuestion,
onNext: () => {
console.log("🤖 AI Quiz: Timer triggered moveToNextQuestion");
moveToNextQuestion();
},
});

@ -12,18 +12,9 @@ import { useYandexMetricsGoals } from "@/utils/hooks/metrics/useYandexMetricsGoa
import { useQuestionTimer } from "./useQuestionTimer";
export function useBranchingQuiz() {
console.log("РАБОТАЮ Я, УПРАВЛЯТОР КВИЗА ВЕТВЛЕНИЯ");
//Получаем инфо о квизе и список вопросов.
const { settings, questions, quizId, cnt, preview } = useQuizStore();
// Отладочная информация о настройках таймера
console.log("🌳 Branching Quiz settings:", {
questionTimerEnabled: settings.questionTimerEnabled,
time_of_passing: settings.cfg.time_of_passing,
timerEnabled: Boolean(settings.questionTimerEnabled),
timerSeconds: settings.cfg.time_of_passing ?? 0,
});
//Когда квиз линейный, не ветвящийся, мы идём по вопросам по их порядковому номеру. Это их page.
//За корректность page отвечает конструктор квизов. Интересный факт, если в конструкторе удалить из середины вопрос, то случится куча запросов изменения вопросов с изменением этого page
const sortedQuestions = useMemo(() => {
@ -58,16 +49,11 @@ export function useBranchingQuiz() {
if (settings.cfg.haveRoot) {
// Если есть ветвление, то settings.cfg.haveRoot будет заполнен
//Если заполнен, то дерево растёт с root и это 1 вопрос :)
console.log("Существует запись о корне: " + settings.cfg.haveRoot);
const nextQuestion = sortedQuestions.find(
//Функция ищет первое совпадение по массиву
(question) => question.id === settings.cfg.haveRoot || question.content.id === settings.cfg.haveRoot
);
console.log("___nextQuestion____");
console.log(nextQuestion);
console.log("___sortedQuestions____");
console.log(sortedQuestions);
if (!nextQuestion) return null;
return nextQuestion.id;
}
@ -189,7 +175,6 @@ export function useBranchingQuiz() {
preview,
currentQuestion,
onNext: () => {
console.log("🌳 Branching Quiz: Timer triggered moveToNextQuestion");
moveToNextQuestion();
},
});

@ -15,14 +15,6 @@ export function useLinearQuiz() {
//Получаем инфо о квизе и список вопросов.
const { settings, questions, quizId, cnt, preview } = useQuizStore();
// Отладочная информация о настройках таймера
console.log("📏 Linear Quiz settings:", {
questionTimerEnabled: settings.questionTimerEnabled,
time_of_passing: settings.cfg.time_of_passing,
timerEnabled: Boolean(settings.questionTimerEnabled),
timerSeconds: settings.cfg.time_of_passing ?? 0,
});
//Когда квиз линейный, не ветвящийся, мы идём по вопросам по их порядковому номеру. Это их page.
//За корректность page отвечает конструктор квизов. Интересный факт, если в конструкторе удалить из середины вопрос, то случится куча запросов изменения вопросов с изменением этого page
const sortedQuestions = useMemo(() => {
@ -183,7 +175,6 @@ export function useLinearQuiz() {
preview,
currentQuestion,
onNext: () => {
console.log("📏 Linear Quiz: Timer triggered moveToNextQuestion");
// Программный переход к следующему вопросу
moveToNextQuestion();
},

@ -20,44 +20,24 @@ export function useQuestionTimer({ enabled, seconds, quizId, preview, currentQue
const isFirstQuestionRef = useRef<boolean>(true);
useEffect(() => {
console.log("🕐 useQuestionTimer useEffect triggered", {
enabled,
seconds,
quizId,
preview,
currentQuestionId: currentQuestion?.id,
currentQuestionType: currentQuestion?.type,
currentQuizStep,
hasCurrentQuestion: !!currentQuestion,
timestamp: new Date().toISOString(),
});
if (!enabled) {
console.log("❌ Timer disabled");
return;
}
if (!seconds || seconds <= 0) {
console.log("❌ Invalid seconds:", seconds);
return;
}
if (!currentQuestion) {
console.log("❌ No current question");
return;
}
if (currentQuizStep !== "question") {
console.log("❌ Not on question step:", currentQuizStep);
return;
}
if (currentQuestion.type === "result") {
console.log("❌ Question is result type");
return;
}
console.log("✅ Starting timer for", seconds, "seconds");
// Сбрасываем предыдущий таймер
if (timeoutRef.current) {
console.log("🔄 Clearing previous timer");
clearTimeout(timeoutRef.current);
timeoutRef.current = null;
}
@ -67,27 +47,21 @@ export function useQuestionTimer({ enabled, seconds, quizId, preview, currentQue
const startDelay = isFirstQuestion ? 2000 : 100; // 2 секунды для первого вопроса, 100ms для остальных
if (isFirstQuestion) {
console.log("🔄 First question detected, adding 2s delay to prevent navigation conflicts");
isFirstQuestionRef.current = false;
}
timeoutRef.current = window.setTimeout(
async () => {
console.log("⏰ Timer expired! Auto-advancing to next question");
try {
if (!preview) {
console.log("📤 Sending empty answer for question:", currentQuestion.id);
// Отправляем пустую строку в ответе (questionAnswer === undefined)
await sendQuestionAnswer(quizId, currentQuestion, undefined, ownVariants);
console.log("✅ Empty answer sent successfully");
} else {
console.log("👀 Preview mode - skipping answer send");
}
} catch (e) {
console.error("❌ Error sending empty timed answer", e);
enqueueSnackbar("Ошибка при отправке ответа по таймеру");
} finally {
console.log("➡️ Calling onNext()");
onNext();
}
},
@ -95,7 +69,6 @@ export function useQuestionTimer({ enabled, seconds, quizId, preview, currentQue
);
return () => {
console.log("🧹 Cleaning up timer");
if (timeoutRef.current) {
clearTimeout(timeoutRef.current);
timeoutRef.current = null;

@ -29,7 +29,6 @@ function analyicStatus({ status, haveRoot }: StatusData) {
}
export const initDataManager = (data: StatusData) => {
console.log("🔧 Initializing DataManager with:", data);
analyicStatus(data);
switch (statusOfQuiz) {
case "line":
@ -43,7 +42,6 @@ export const initDataManager = (data: StatusData) => {
break;
}
isInitialized = true;
console.log("✅ DataManager initialized with type:", statusOfQuiz);
};
// Главный хук (интерфейс для потребителей)
@ -52,7 +50,6 @@ export const useQuestionFlowControl = () => {
// Попытка автоматической инициализации на основе текущих настроек
const { settings } = useQuizStore.getState();
if (settings && settings.status) {
console.log("🔄 Auto-initializing DataManager with settings:", settings);
initDataManager({
status: settings.status,
haveRoot: settings.cfg.haveRoot,
@ -66,7 +63,6 @@ export const useQuestionFlowControl = () => {
// Функция для сброса состояния (полезна для HMR)
export const resetDataManager = () => {
console.log("🔄 Resetting DataManager");
cachedManager = null as any;
isInitialized = false;
statusOfQuiz = null as any;

@ -11,7 +11,6 @@ export async function sendQuestionAnswer(
ownVariants: OwnVariant[]
) {
if (!questionAnswer) {
console.log("📤 sendQuestionAnswer: Sending empty answer for question", question.id);
return sendAnswer({
questionId: question.id,
body: "",
@ -101,9 +100,7 @@ export async function sendQuestionAnswer(
return;
}
case "images": {
console.log("Работает отправщик ответа для типа КАРТИНКИ");
if (question.content.multi) {
console.log("Этот вопрос есть МУЛЬТИ");
const answer = questionAnswer.answer;
const ownAnswer = Array.isArray(answer)
? ownVariants[ownVariants.findIndex((variant) => answer.some((a: string) => a === variant.id))]?.variant
@ -151,7 +148,6 @@ export async function sendQuestionAnswer(
qid: quizId,
});
}
console.log("Этот вопрос НЕ есть МУЛЬТИ");
const variant = question.content.variants.find((v) => v.id === questionAnswer.answer);
@ -160,9 +156,6 @@ export async function sendQuestionAnswer(
// Берем fileId из ownVariants для own вариантов
const ownVariantData = ownVariants.find((v) => v.id === variant.id)?.variant;
console.log("Был выбран вариант ", variant);
console.log("Был выбран ownVariant ", ownVariantData);
let imageValue = variant.extendedText;
if (variant.isOwn) {
if (ownVariantData?.originalImageUrl) {
@ -175,11 +168,6 @@ export async function sendQuestionAnswer(
}
}
console.log("В конечном итоге я планирую отправить вот эти данные: ", {
Image: imageValue,
Description: ownVariantData ? ownVariantData.answer : variant.answer,
});
const body = {
Image: imageValue,
Description: ownVariantData ? ownVariantData.answer : variant.answer,

@ -26,7 +26,7 @@
"preview": "vite preview",
"cypress:open": "cypress open",
"prepublishOnly": "npm run build:package",
"deploy": "echo '🚀 Начало процесса деплоя...' && docker login gitea.pena && if [ $? -eq 0 ]; then echo '✅ Успешный логин в Docker registry'; else echo '❌ Ошибка логина в Docker registry'; exit 1; fi && echo '🏗️ Сборка Docker образа...' && docker build -t gitea.pena/squiz/frontanswerer/$(git branch --show-current):latest . && if [ $? -eq 0 ]; then echo '✅ Docker образ успешно собран'; else echo '❌ Ошибка сборки Docker образа'; exit 1; fi && echo '📤 Пуш образа в registry...' && docker push gitea.pena/squiz/frontanswerer/$(git branch --show-current):latest && if [ $? -eq 0 ]; then echo '✅ Образ успешно загружен в registry'; echo '🎉 Деплой завершен успешно!'; else echo '❌ Ошибка загрузки образа в registry'; exit 1; fi",
"deploy": "docker login gitea.pena && docker build -t gitea.pena/squiz/frontanswerer/$(git branch --show-current):latest . && docker push gitea.pena/squiz/frontanswerer/$(git branch --show-current):latest",
"prepare": "husky",
"test": "cypress open"
},

@ -4,7 +4,13 @@ import { Dialog, IconButton, Slide, SlideProps, SxProps, Theme } from "@mui/mate
import { forwardRef } from "react";
const SlideTransition = forwardRef<unknown, SlideProps>((props, ref) => {
return <Slide direction="up" ref={ref} {...props} />;
return (
<Slide
direction="up"
ref={ref}
{...props}
/>
);
});
interface Props {
@ -46,7 +52,11 @@ export default function QuizDialog({
],
}}
>
<QuizAnswerer quizId={quizId} changeFaviconAndTitle={false} disableGlobalCss />
<QuizAnswerer
quizId={quizId}
changeFaviconAndTitle={false}
disableGlobalCss
/>
<IconButton
onClick={onClose}
sx={{