import { useModal, useModalControl } from "@/modules/modals"; import { BuildKey } from "@/utilities"; import { LOG } from "@/utilities/console"; import { faCheck, faCircleNotch, faInfoCircle, faTimes, faTrash, } from "@fortawesome/free-solid-svg-icons"; import { FontAwesomeIcon } from "@fortawesome/react-fontawesome"; import { createContext, useCallback, useContext, useEffect, useMemo, useRef, useState, } from "react"; import { Button, Container, Form } from "react-bootstrap"; import { Column } from "react-table"; import { LanguageSelector, MessageIcon } from ".."; import { FileForm } from "../inputs"; import { SimpleTable } from "../tables"; type ModifyFn = (index: number, info?: PendingSubtitle) => void; const RowContext = createContext>(() => { LOG("error", "RowContext not initialized"); }); export function useRowMutation() { return useContext(RowContext); } export interface PendingSubtitle

{ file: File; state: "valid" | "fetching" | "warning" | "error"; messages: string[]; language: Language.Info | null; forced: boolean; hi: boolean; payload: P; } export type Validator = ( item: PendingSubtitle ) => Pick, "state" | "messages">; interface Props { initial: T; availableLanguages: Language.Info[]; upload: (items: PendingSubtitle[]) => void; update: (items: PendingSubtitle[]) => Promise[]>; validate: Validator; columns: Column>[]; hideAllLanguages?: boolean; } function SubtitleUploader(props: Props) { const { initial, columns, upload, update, validate, availableLanguages, hideAllLanguages, } = props; const [pending, setPending] = useState[]>([]); const showTable = pending.length > 0; const Modal = useModal({ size: showTable ? "xl" : "lg", }); const { hide } = useModalControl(); const fileList = useMemo(() => pending.map((v) => v.file), [pending]); const initialRef = useRef(initial); const setFiles = useCallback( async (files: File[]) => { const initialLanguage = availableLanguages.length > 0 ? availableLanguages[0] : null; let list = files.map>((file) => ({ file, state: "fetching", messages: [], language: initialLanguage, forced: false, hi: false, payload: { ...initialRef.current }, })); if (update) { setPending(list); list = await update(list); } else { list = list.map>((v) => ({ ...v, state: "valid", })); } list = list.map((v) => ({ ...v, ...validate(v), })); setPending(list); }, [update, validate, availableLanguages] ); const modify = useCallback( (index: number, info?: PendingSubtitle) => { setPending((pd) => { const newPending = [...pd]; if (info) { info = { ...info, ...validate(info) }; newPending[index] = info; } else { newPending.splice(index, 1); } return newPending; }); }, [validate] ); useEffect(() => { setPending((pd) => { const newPd = pd.map((v) => { if (v.state !== "fetching") { return { ...v, ...validate(v) }; } else { return v; } }); return newPd; }); }, [validate]); const columnsWithAction = useMemo>[]>( () => [ { id: "icon", accessor: "state", className: "text-center", Cell: ({ value, row }) => { let icon = faCircleNotch; let color: string | undefined = undefined; let spin = false; switch (value) { case "fetching": spin = true; break; case "warning": icon = faInfoCircle; color = "var(--warning)"; break; case "valid": icon = faCheck; color = "var(--success)"; break; default: icon = faTimes; color = "var(--danger)"; break; } const messages = row.original.messages; return ( ); }, }, { Header: "File", accessor: (d) => d.file.name, }, { id: "hi", Header: "HI", accessor: "hi", Cell: ({ row, value }) => { const { original, index } = row; const mutate = useRowMutation(); return ( { const newInfo = { ...row.original }; newInfo.hi = v.target.checked; mutate(row.index, newInfo); }} > ); }, }, { id: "forced", Header: "Forced", accessor: "forced", Cell: ({ row, value }) => { const { original, index } = row; const mutate = useRowMutation(); return ( { const newInfo = { ...row.original }; newInfo.forced = v.target.checked; mutate(row.index, newInfo); }} > ); }, }, { id: "language", Header: "Language", accessor: "language", className: "w-25", Cell: ({ row, value }) => { const mutate = useRowMutation(); return ( { if (lang) { const newInfo = { ...row.original }; newInfo.language = lang; mutate(row.index, newInfo); } }} > ); }, }, ...columns, { id: "action", accessor: "file", Cell: ({ row }) => { const mutate = useRowMutation(); return ( ); }, }, ], [columns, availableLanguages] ); const canUpload = useMemo( () => pending.length > 0 && pending.every((v) => v.state === "valid" || v.state === "warning"), [pending] ); const footer = (

); return (
); } export default SubtitleUploader;