Files
artcraft/app/components/UploadAudioComponent/UploadAudioComponent.tsx
T
wilandwil 7dd101828b Audio Sidepanel v2.5 (#275)
- Finished UI and functionality for Pagination
- Pagination implemented in audio library
- Finished UI for uploading audio
- Connected the UI to the upload media endpoint
- Audio Library auto-refreshes upon finishing upload
- Fixed up audio upload bugs that already existed in V2V workflow

Co-authored-by: wil <wil@storyteller.ai>
2024-04-30 10:32:59 -04:00

199 lines
5.8 KiB
TypeScript

import { useState, useEffect } from "react";
import { FileUploader } from "react-drag-drop-files";
import { v4 as uuidv4 } from "uuid";
import {
faCheck,
faCircleXmark,
faFileArrowUp,
faFileAudio,
faSpinnerThird,
faXmark,
} from "@fortawesome/pro-solid-svg-icons";
import { FontAwesomeIcon } from "@fortawesome/react-fontawesome";
import { UploadAudio, UploadAudioIsOk, UploadAudioRequest } from "./utilities";
import { Button } from "../Button";
import { ButtonIcon, P, WaveformPlayer } from "~/components";
import Tooltip from "../Tooltip";
const FILE_TYPES = ["MP3", "WAV", "FLAC", "OGG"];
interface Props {
sessionToken: string;
file?: File;
onFileStaged?: (file: File) => void;
onClear?: () => void;
onFileUploaded: (token: string) => void;
}
export const UploadAudioComponent = ({
sessionToken,
file: propsFile,
onFileStaged,
onClear,
onFileUploaded,
}: Props) => {
const [{ file, uploadState, uploadToken }, setState] = useState<{
file: File | undefined;
uploadState: "init" | "none" | "uploading" | "uploaded" | "error";
uploadToken?: string;
}>({
file: propsFile,
uploadState: propsFile ? "none" : "init",
});
const audioUrl = file ? URL.createObjectURL(file) : "";
const handleChange = (file: File) => {
setState((curr) => ({
...curr,
file: file,
uploadState: "none",
}));
};
const handleClear = () => {
setState((curr) => ({
...curr,
file: undefined,
uploadState: "none",
uploadToken: undefined,
}));
};
const handleUploadFile = () => {
if (file === undefined) return false;
setState((curr) => ({ ...curr, uploadState: "uploading" }));
const request: UploadAudioRequest = {
uuid_idempotency_token: uuidv4(),
file: file,
source: "file",
};
UploadAudio(request, sessionToken).then((res) => {
if (UploadAudioIsOk(res)) {
setState((curr) => ({
...curr,
uploadState: "uploaded",
uploadToken: res.upload_token,
}));
} else {
setState((curr) => ({
...curr,
uploadState: "error",
uploadToken: undefined,
}));
}
});
};
useEffect(() => {
if (file && uploadState === "none" && onFileStaged) onFileStaged(file);
if (!file && uploadState === "none" && onClear) onClear();
}, [file, uploadState]);
useEffect(() => {
if (uploadState === "uploaded" && uploadToken) onFileUploaded(uploadToken);
}, [uploadToken, uploadState]);
return (
<div className="flex flex-col gap-3">
{/* Usage refer to https://github.com/KarimMokhtar/react-drag-drop-files */}
<FileUploader
handleChange={handleChange}
name="file"
types={FILE_TYPES}
maxSize={50}
children={<DragAndDropZone file={file} />}
/>
{file && (
<>
<div className="flex items-center gap-3 rounded-lg bg-brand-secondary p-3">
<div className="grow">
<WaveformPlayer audio={audioUrl} hasPlayButton/>
</div>
<Tooltip content="Remove" position="top">
<ButtonIcon
icon={faXmark}
onClick={handleClear}
className="h-auto w-auto bg-transparent p-0 text-xl opacity-60 hover:bg-transparent hover:opacity-90"
/>
</Tooltip>
</div>
<div className="flex gap-2">
<Button
className="w-full py-2.5"
variant="action"
onClick={handleUploadFile}
disabled={uploadState !== "none" && uploadState !== "error"}
icon={
uploadState === "uploaded"
? faCheck
: uploadState === "error"
? faCircleXmark
: faFileArrowUp
}>
{uploadState === "uploaded"
? "Uploaded"
: uploadState === "error"
? "Upload Error"
: "Upload Audio"}
{uploadState === "uploading" && (
<FontAwesomeIcon icon={faSpinnerThird} spin />
)}
</Button>
</div>
</>
)}
</div>
);
}
const DragAndDropZone = ({file }: { file: File|undefined }) => {
const fileSize =
file && file.size >= 1024 * 1024
? (file.size / 1024 / 1024).toFixed(2) + " MB"
: file
? `${Math.floor(file.size / 1024)} KB`
: null;
const fileName =
file && file.name
? file.name.split(".")[0].toUpperCase()
: "";
if (!file) {
return (
<div className="flex cursor-pointer items-center gap-3.5 rounded-lg border-2 border-dashed border-ui-controls-button/50 bg-brand-secondary p-3">
<FontAwesomeIcon icon={faFileArrowUp} className="text-4xl" />
<div className="flex flex-col gap-0">
<P className="font-medium">
<u>Upload a file</u> or drop it here
</P>
<P className="flex items-center gap-2 text-sm font-normal opacity-50">
{FILE_TYPES.join(", ").toString()} supported
</P>
</div>
</div>
);
} else {
return (
<div className="flex cursor-pointer items-center gap-3.5 rounded-lg border-2 border-dashed border-ui-controls-button/50 bg-brand-secondary p-3">
<FontAwesomeIcon icon={faFileAudio} className="text-4xl" />
<div className="flex flex-col gap-0">
<P className="font-medium">
{file.name.slice(0, file.name.lastIndexOf("."))}
</P>
<P className="flex items-center gap-2 text-sm font-normal">
<span className="opacity-50">
{`${fileName} file size: ${fileSize} `}
</span>
<u className="transition-all hover:text-white/80">Change File</u>
</P>
</div>
</div>
);
}
};