diff --git a/app/Classes/ApiManager/ApiManager.ts b/app/Classes/ApiManager/ApiManager.ts index ac41cf669f..5de0818027 100644 --- a/app/Classes/ApiManager/ApiManager.ts +++ b/app/Classes/ApiManager/ApiManager.ts @@ -64,6 +64,27 @@ export class ApiManager { return response.json(); } + public async fetchMultipartFormData( + endpoint: string, + { + method, + body, + }: { + method: string; + body: FormData; + }, + ): Promise { + const response = await fetch(endpoint, { + method, + headers: { + Accept: "application/json", + }, + credentials: "include", + body: body, + }); + return response.json(); + } + protected get({ endpoint, query, @@ -129,7 +150,11 @@ export class ApiManager { } else if (blob) { formData.append("file", blob); } - return this.fetch(endpoint, { + // return this.fetch(endpoint, { + // method: "POST", + // body: formData, + // }); + return this.fetchMultipartFormData(endpoint, { method: "POST", body: formData, }); diff --git a/app/components/reusable/FileUploader/DragAndDropZone.tsx b/app/components/reusable/FileUploader/DragAndDropZone.tsx new file mode 100644 index 0000000000..68cc73b869 --- /dev/null +++ b/app/components/reusable/FileUploader/DragAndDropZone.tsx @@ -0,0 +1,52 @@ +import { faFileArrowUp, faFileAudio } from "@fortawesome/pro-solid-svg-icons"; +import { FontAwesomeIcon } from "@fortawesome/react-fontawesome"; + +import { P } from "~/components"; + +export const DragAndDropZone = ({ + file, + fileTypes, +}: { + file?: File; + fileTypes: string[]; +}) => { + if (!file) { + return ( +
+ +
+

+ Upload a file or drop it here +

+ +

+ {fileTypes.join(", ").toString()} supported +

+
+
+ ); + } + + const fileName = file.name.split(".")[0].toUpperCase(); + const fileSize = + file.size >= 1024 * 1024 + ? (file.size / 1024 / 1024).toFixed(2) + " MB" + : `${Math.floor(file.size / 1024)} KB`; + + return ( +
+ +
+

+ {file.name.slice(0, file.name.lastIndexOf("."))} +

+

+ + {`${fileName} file size: ${fileSize} `} + + Change File +

+
+
+ ); +}; diff --git a/app/components/reusable/FileUploader/FileUploader.tsx b/app/components/reusable/FileUploader/FileUploader.tsx new file mode 100644 index 0000000000..196bd12a3e --- /dev/null +++ b/app/components/reusable/FileUploader/FileUploader.tsx @@ -0,0 +1,23 @@ +import { FileUploader as DragDropFileUploader } from "react-drag-drop-files"; +// Usage refer to https://github.com/KarimMokhtar/react-drag-drop-files + +import { DragAndDropZone } from "./DragAndDropZone"; + +export const FileUploader = ({ + file, + fileTypes, + handleChange, +}: { + file: File | undefined; + handleChange: (file: File) => void; + fileTypes: string[]; +}) => ( + + + +); diff --git a/app/components/reusable/FileUploader/index.ts b/app/components/reusable/FileUploader/index.ts new file mode 100644 index 0000000000..75ffa32865 --- /dev/null +++ b/app/components/reusable/FileUploader/index.ts @@ -0,0 +1 @@ +export * from "./FileUploader"; diff --git a/app/components/reusable/TransitionDialogue.tsx b/app/components/reusable/TransitionDialogue.tsx index 4a28fa1352..94b3c720ab 100644 --- a/app/components/reusable/TransitionDialogue.tsx +++ b/app/components/reusable/TransitionDialogue.tsx @@ -63,7 +63,7 @@ export const TransitionDialogue = ({ }) => { return ( - +
diff --git a/app/components/reusable/index.ts b/app/components/reusable/index.ts index 67f1848846..9ee54ddfcb 100644 --- a/app/components/reusable/index.ts +++ b/app/components/reusable/index.ts @@ -6,6 +6,7 @@ export * from "./ButtonIcon"; export * from "./ButtonIconSelect"; export * from "./ButtonLink"; export * from "./ConfirmationModal"; +export * from "./FileUploader"; export * from "./FileWrapper"; //need to rid of scss export * from "./FilterButtons"; //this relies on a list of filters locally, could be more flexible export * from "./Gravatar"; diff --git a/app/components/signaled/UploadAudioButtonDialogue/UploadAudioButtonDialogue.tsx b/app/components/signaled/UploadAudioButtonDialogue/UploadAudioButtonDialogue.tsx index 3b848f2bbb..20eaf6caa7 100644 --- a/app/components/signaled/UploadAudioButtonDialogue/UploadAudioButtonDialogue.tsx +++ b/app/components/signaled/UploadAudioButtonDialogue/UploadAudioButtonDialogue.tsx @@ -1,45 +1,28 @@ import { useEffect, useState } from "react"; import { v4 as uuidv4 } from "uuid"; -import { - faCirclePlus, - faFileArrowUp, - faFileAudio, - faSpinnerThird, -} from "@fortawesome/pro-solid-svg-icons"; +import { faCirclePlus, faSpinnerThird } from "@fortawesome/pro-solid-svg-icons"; import { FontAwesomeIcon } from "@fortawesome/react-fontawesome"; -import { FileUploader } from "react-drag-drop-files"; import { Button, + FileUploader, H2, - P, TransitionDialogue, WaveformPlayer, } from "~/components"; -import { UploadMedia, UploadMediaResponse } from "./utilities"; +import { MediaUploadApi } from "~/Classes/ApiManager/MediaUploadApi"; +import { AUDIO_FILE_TYPE, ToastTypes } from "~/enums"; +export const FILE_TYPES = Object.keys(AUDIO_FILE_TYPE); -import { AUDIO_FILE_TYPE } from "~/enums"; -const FILE_TYPES = Object.keys(AUDIO_FILE_TYPE); - -// import { ListDropdown } from "../ListDropdown"; -// const visiblityOptions = [ -// { -// name: "private", -// value: "private", -// }, -// { -// name: "public", -// value: "public", -// }, -// ]; +import { getFileName } from "~/utilities"; +import { addToast } from "~/signals"; type ComponentState = { isOpen: boolean; file: File | undefined; audioUrl: string; uploadState: "init" | "staged" | "uploading" | "uploaded" | "error"; - // visibility: string; fileToken: string | undefined; }; const initialValues: ComponentState = { @@ -47,14 +30,9 @@ const initialValues: ComponentState = { file: undefined, audioUrl: "", uploadState: "init", - // visibility: "private", fileToken: undefined, }; -const getFileName = (file: File) => { - return file.name.split(".")[0] || ""; -}; - export const UploadAudioButtonDialogue = ({ onUploaded, }: { @@ -65,9 +43,7 @@ export const UploadAudioButtonDialogue = ({ const closeModal = () => setState(initialValues); const openModal = () => setState((curr) => ({ ...curr, isOpen: true })); - // const selectVisibility = (value:string) => { - // setState((curr)=>({...curr, visibility:value})) - // }; + const handleFileChange = (file: File) => { setState((curr) => ({ ...curr, @@ -82,39 +58,52 @@ export const UploadAudioButtonDialogue = ({ isOpen: true, }); }; - const submitUpload = () => { - if (file) { + const submitUpload = async () => { + if (!file) { + //TODO: Maybe more verbose on not having file + return; + } + setState((curr) => ({ + ...curr, + uploadState: "uploading", + })); + const mediaApi = new MediaUploadApi(); + + const request = { + blob: file, + fileName: getFileName(file), + uuid: uuidv4(), + maybe_title: getFileName(file), + }; + const response = await mediaApi.UploadAudio(request); + if (response.success && response.data) { setState((curr) => ({ ...curr, - uploadState: "uploading", + uploadState: "uploaded", + fileToken: response.data, })); - UploadMedia({ - uuid_idempotency_token: uuidv4(), - file: file, - source: "file", - title: getFileName(file), - }).then((res: UploadMediaResponse) => { - // console.log(res); - if ("media_file_token" in res) { - setState((curr) => ({ - ...curr, - uploadState: "uploaded", - fileToken: res.media_file_token, - })); - } else { - setState((curr) => ({ - ...curr, - uploadState: "error", - })); - } - }); + return; } + + //handle errors + console.log(response.errorMessage); + addToast( + ToastTypes.ERROR, + response.errorMessage || "Unknown Error: Upload Audio", + ); + setState((curr) => ({ + ...curr, + uploadState: "staged", + })); }; + useEffect(() => { - if (fileToken) { + if (fileToken && uploadState === "uploaded") { + //calling the callback on a successful upload onUploaded(fileToken); } - }, [fileToken]); + }, [fileToken, uploadState, onUploaded]); + return ( <>