diff --git a/src/pages/Video/VideoPage.module.css b/src/pages/Video/VideoPage.module.css index e69de29..0fb5ad9 100644 --- a/src/pages/Video/VideoPage.module.css +++ b/src/pages/Video/VideoPage.module.css @@ -0,0 +1,7 @@ +.videoMark::before { + inset: 18px 15px 18px 22px; + background: var(--color-accent); + border: 0; + border-radius: 0; + clip-path: polygon(0 0, 100% 50%, 0 100%); +} diff --git a/src/pages/Video/VideoPage.tsx b/src/pages/Video/VideoPage.tsx index 9f31087..e3c969f 100644 --- a/src/pages/Video/VideoPage.tsx +++ b/src/pages/Video/VideoPage.tsx @@ -1,6 +1,300 @@ +import { useEffect, useState, type ChangeEvent, type FormEvent } from "react"; +import { useNavigate } from "react-router-dom"; +import logo from "../../assets/common/HITGENERATOR-Logo.png"; +import { useAppDispatch, useAppSelector } from "../../store/hooks"; +import { clearRequestError, request, resetState } from "../../store/slices/slice"; +import pictureStyles from "../Picture/Picture.module.css"; +import styles from "./VideoPage.module.css"; + +type AspectRatio = "1:1" | "4:5" | "16:9"; + +const aspectRatios: AspectRatio[] = ["1:1", "4:5", "16:9"]; export default function Video() { - return ( -
video
- ) -} \ No newline at end of file + const navigate = useNavigate(); + const dispatch = useAppDispatch(); + const { loading, error: requestError } = useAppSelector((state) => state.request); + + const [prompt, setPrompt] = useState(""); + const [characters, setCharacters] = useState([]); + const [scenes, setScenes] = useState([]); + const [screenplays, setScreenplays] = useState([]); + const [aspectRatio, setAspectRatio] = useState("16:9"); + const [formError, setFormError] = useState(null); + const [isSubmitted, setIsSubmitted] = useState(false); + + useEffect(() => { + dispatch(clearRequestError()); + }, [dispatch]); + + function resetErrors() { + setFormError(null); + dispatch(clearRequestError()); + } + + function addImageFiles( + event: ChangeEvent, + setFiles: React.Dispatch>,) { + const nextFiles = event.currentTarget.files?.[0] ?? null; + + if (nextFiles && !nextFiles.type.startsWith("image/")) { + setFormError("Выберите файлы изображений."); + return; + } + + resetErrors(); + setFiles((currentFiles) => nextFiles ? [...currentFiles, nextFiles] : currentFiles); + setIsSubmitted(false); + } + + function addScreenplayFiles(event: ChangeEvent) { + const nextFiles = Array.from(event.currentTarget.files ?? []); + const isTextFile = (file: File) => { + return (file.type.startsWith("text/") || /\.(txt|md)$/i.test(file.name)) || file.type.startsWith("image/"); + + }; + if (nextFiles.some((file) => !isTextFile(file))) { + setFormError("Выберите текстовые файлы в формате TXT или MD."); + return; + } + + resetErrors(); + setScreenplays((currentFiles) => [...currentFiles, ...nextFiles]); + event.currentTarget.value = ""; + setIsSubmitted(false); + } + + function removeFile( + index: number, + setFiles: React.Dispatch>, + ) { + setFiles((currentFiles) => currentFiles.filter((_, fileIndex) => fileIndex !== index)); + setIsSubmitted(false); + } + + async function handleSubmit(event: FormEvent) { + event.preventDefault(); + + const text = prompt.trim(); + + if (!text) { + setFormError("Добавьте описание видео."); + return; + } + + + + resetErrors(); + setIsSubmitted(false); + + await dispatch( + request({ + id: Date.now(), + text, + files: [...characters, ...scenes, ...screenplays], + ratio: aspectRatio, + }), + ).unwrap(); + setIsSubmitted(true); + } + + const previewClassName = [ + pictureStyles.previewCanvas, + aspectRatio === "1:1" + ? pictureStyles.square + : aspectRatio === "4:5" + ? pictureStyles.portrait + : pictureStyles.wide, + ].join(" "); + const status = loading ? "Генерируем" : isSubmitted ? "Запрос принят" : "Ожидание"; + //const attachmentsCount = characters.length + scenes.length + screenplays.length; + + return ( +
+
+
+ + ShitGenerator +
+ VIDEO +
+ +
+
+
+

VIDEO LAB

+

Генерация видео

+
+ +