'use client'; import { IVtuber } from "@futureporn/types"; import { useSearchParams } from 'next/navigation'; import React from 'react'; import AwsS3 from '@uppy/aws-s3'; import RemoteSources from '@uppy/remote-sources'; import { LoginButton, useAuth } from '@/app/components/auth'; import { Dashboard } from '@uppy/react'; import styles from '@/assets/styles/fp.module.css' import { projektMelodyEpoch } from "@/app/lib/constants"; import add from "date-fns/add"; import sub from "date-fns/sub"; import { FontAwesomeIcon } from "@fortawesome/react-fontawesome"; import { faEraser, faPaperPlane, faSpinner, faX, faXmark } from "@fortawesome/free-solid-svg-icons"; import { useForm, ValidationMode } from 'react-hook-form'; import { yupResolver } from '@hookform/resolvers/yup'; import * as Yup from 'yup'; import { toast } from "react-toastify"; import { ErrorMessage } from "@hookform/error-message" import Uppy from '@uppy/core'; import { companionUrl } from '@/app/lib/constants'; interface IUploadFormProps { vtubers: IVtuber[]; } interface IValidationResults { valid: boolean; issues: string[] | null; } interface IFormSchema extends Yup.InferType { }; const validationSchema = Yup.object().shape({ vtuber: Yup.number() .required('VTuber is required'), streamCuid: Yup.string().optional(), date: Yup.date() .typeError('Invalid date') // https://stackoverflow.com/a/72985532/1004931 .min(sub(projektMelodyEpoch, { days: 1 }), 'Date must be after February 7 2020') .max(add(new Date(), { days: 1 }), 'Date cannot be in the future') .required('Date is required'), notes: Yup.string().optional(), attribution: Yup.boolean().optional(), files: Yup.array() .of( Yup.object().shape({ key: Yup.string().required('key is required'), uploadId: Yup.string().required('uploadId is required') }), ) .min(1, 'At least one file is required'), }); export default function UploadForm({ vtubers }: IUploadFormProps) { const searchParams = useSearchParams(); const cuid = searchParams.get('cuid'); const { authData } = useAuth(); const uppy = new Uppy( { autoProceed: true, debug: true, logger: { debug: console.info, warn: console.log, error: console.error }, } ) .use(RemoteSources, { companionUrl, sources: [ 'GoogleDrive', 'Dropbox', 'Url' ] }) .use(AwsS3, { companionUrl, shouldUseMultipart: true, abortMultipartUpload: () => {}, // @see https://github.com/transloadit/uppy/issues/1197#issuecomment-491756118 companionHeaders: { 'authorization': `Bearer ${authData?.accessToken}` } }) const formOptions = { resolver: yupResolver(validationSchema), mode: 'onChange' as keyof ValidationMode, }; const { register, handleSubmit, setError, clearErrors, formState: { errors, isValid, isSubmitted, isSubmitSuccessful, isSubmitting }, setValue, watch, reset } = useForm(formOptions); // useEffect(() => { // if (!cuid) return; // (async () => { // console.log('query') // const query = qs.stringify({ // filters: { // cuid: { // '$eq': cuid // } // } // }); // const res = await fetch(`${process.env.NEXT_PUBLIC_STRAPI_URL}/api/streams?${query}`); // if (!res.ok) return; // const matchingStream = (await res.json()).data as IStream; // console.log(matchingStream); // setValue('vtuber', matchingStream.attributes.vtuber.data.id); // })(); // }, [cuid]); // setValue('streamCuid', cuid||''); const files = watch('files'); async function createUSC(data: IFormSchema) { try { const res = await fetch(`${process.env.NEXT_PUBLIC_STRAPI_URL}/api/user-submitted-contents/createFromUppy`, { method: 'POST', headers: { 'authorization': `Bearer ${authData?.accessToken}`, 'content-type': 'application/json', 'accept': 'application/json' }, body: JSON.stringify({ data: { files: data.files, attribution: data.attribution, notes: data.notes, vtuber: data.vtuber, date: data.date, streamCuid: cuid } }) }); if (!res.ok) { console.error('failed to fetch /api/user-submitted-contents/createFromUppy'); const body = await res.json(); const error = body.error; toast.error(`${error.type} ${error.message}`, { theme: 'dark' }); setError('root.serverError', { type: error.type, message: error.message }) } } catch (e) { if (e instanceof Error) { toast.error(`${e.message}`, { theme: 'dark' }); setError('root.serverError', { type: "remote", message: e.message, }); } else { toast.error(`Something went wrong. Please try again.`, { theme: 'dark' }); setError('root.serverError', { type: 'remote', message: 'Something went wrong. Please try again.' }) } } } uppy.on('complete', async (result: any) => { for (const s of result.successful) { if (!s?.s3Multipart) { const m = 'file was missing s3Multipart' toast.error(`${m}`, { theme: 'dark' }); setError('root.serverError', { type: 'remote', message: m }) throw new Error(m) } } console.log('uppy complete! ') console.log(result) toast.success(`upload complete`); let files = result.successful.map((f: any) => ({ key: f.s3Multipart.key, uploadId: f.s3Multipart.uploadId })); setValue('files', files); }); return ( <>

Upload VOD

Together we can archive all lewdtuber livestreams!

{(!authData?.accessToken) ? <> : (
{(!isSubmitSuccessful) &&

Step 1

Upload the file

{/* Here is how we upload the files to the server. From uppy, we get a list of files. we add the files to a hidden input box. the input box is part of the form which gets POSTed. */} {errors.files &&

{errors.files.message?.toString()}

}
} {(!isSubmitSuccessful) &&
{/* {(!cuid) && } */}

Step 2

Tell us about the VOD

{/* */}

Choose the VTuber this VOD belongs to. (More VTubers will be added when storage/bandwidth funding is secured.)

{errors.vtuber &&

vtuber error

}
setDate(evt.target.value)} >

The date when the VOD was originally streamed.

{errors.date &&

{errors.date.message?.toString()}

}

If there are any issues with the VOD, put a note here. If there are no VOD issues, leave this field blank.

}

Step 3

Send the form

{errors.root?.serverError && (
)} {!isSubmitSuccessful && ( )} {isSubmitting && (

)} {isSubmitSuccessful && ( <> )}
) }
) }