diff --git a/app/components/reusable/FileWrapper/FileWrapper.scss b/app/components/reusable/FileWrapper/FileWrapper.scss deleted file mode 100644 index 02cf3c1fcc..0000000000 --- a/app/components/reusable/FileWrapper/FileWrapper.scss +++ /dev/null @@ -1,48 +0,0 @@ -.fy-file-wrapper { - // flex-grow: 1; - // height: calc(100% - 4px); - - .file-picker-button { - cursor: pointer; - } - - .fy-file-wrapper-input { - visibility: hidden; - height: 0; - width: 0; - // overflow: hidden; - position: absolute; - - + label { - cursor: pointer; - } - } - .fy-uploader-layout { - display: flex; - align-items: center; - width: 100%; - - .fy-uploader-layout-icon { - font-size: 2.625rem; - margin-right: 1rem; - } - } -} - -.fy-file-dragging { - background: rgba(#676781, 0.25); - border-color: rgba(#fff, 0.33); -} - -.fy-uploader { - [type="file"] { - + label { - border: dashed 2px rgba(255, 255, 255, 0.12) !important; - border-radius: 0.5rem; - transition: 0.1s all; - &:hover { - // background-color: lighten($panel, 6%); - } - } - } -} diff --git a/app/components/reusable/FileWrapper/FileWrapper.tsx b/app/components/reusable/FileWrapper/FileWrapper.tsx deleted file mode 100644 index 3cab26f232..0000000000 --- a/app/components/reusable/FileWrapper/FileWrapper.tsx +++ /dev/null @@ -1,98 +0,0 @@ -import React, { ReactNode, useId, useRef, useState } from "react"; -import { AssetType } from "~/enums"; -import { UploadModalObject } from "~/components"; -import { twMerge } from "tailwind-merge"; - -interface Props { - render: (parentId: string) => ReactNode; - fileTypes?: string[]; - onChange?: (file?: React.ChangeEvent) => void; - onSuccess: () => void; - panelClass?: string; - type: AssetType; -} - -export function FileWrapper({ - render, - fileTypes = [], - onSuccess, - type, - ...rest -}: Props) { - const fileRef = useRef(null); - const [file, setFile] = useState(); - const [modalOpen, setModalOpen] = useState(false); - const [resetModal, setResetModal] = useState(false); - const [dragging, setDragging] = useState(false); - const id = "file-input-" + useId(); - const accept = fileTypes - .map((fileType) => `.${fileType.toLowerCase()}`) - .join(","); - - const clearFile = () => { - if (fileRef.current !== null) { - fileRef.current.value = ""; - setFile(undefined); - } - }; - const closeModal = () => setModalOpen(false); - - const fileChange = (event: React.ChangeEvent) => { - setResetModal(true); - event.preventDefault(); - setFile(event.target?.files?.[0]); - setModalOpen(true); - }; - const onDragDrop = (e: React.DragEvent) => { - e.preventDefault(); - e.stopPropagation(); - }; - const onDragEvent = - (onOff: boolean) => - (event: React.DragEvent): void => { - setDragging(onOff); - onDragDrop(event); - }; - const onDrop = (event: React.DragEvent): void => { - setDragging(false); - onDragDrop(event); - if (event.dataTransfer.files && event.dataTransfer.files[0]) { - setResetModal(true); - setFile(event.dataTransfer.files[0]); - setModalOpen(true); - } - }; - - return ( -
- - {render(id)} - -
- ); -} diff --git a/app/components/reusable/FileWrapper/LoadingIcon.tsx b/app/components/reusable/FileWrapper/LoadingIcon.tsx deleted file mode 100644 index dce6894198..0000000000 --- a/app/components/reusable/FileWrapper/LoadingIcon.tsx +++ /dev/null @@ -1,12 +0,0 @@ -import React from "react"; - -export default function LoadingIcon() { - return <> - - Loading... - ; -}; \ No newline at end of file diff --git a/app/components/reusable/FileWrapper/index.tsx b/app/components/reusable/FileWrapper/index.tsx deleted file mode 100644 index e980f61040..0000000000 --- a/app/components/reusable/FileWrapper/index.tsx +++ /dev/null @@ -1 +0,0 @@ -export * from "./FileWrapper"; diff --git a/app/components/reusable/UploadModal/UploadFiles.tsx b/app/components/reusable/UploadModal/UploadFiles.tsx index 69b965f359..df4a3f4e2e 100644 --- a/app/components/reusable/UploadModal/UploadFiles.tsx +++ b/app/components/reusable/UploadModal/UploadFiles.tsx @@ -1,10 +1,17 @@ import { Button, H6, Input, ListDropdown } from "~/components"; import { FileUploader } from "./FileUploader"; import { useState } from "react"; -import { MediaFileAnimationType } from "~/enums"; +import { + THUMBNAILS_FILE_TYPE, + MediaFileAnimationType, + FilterEngineCategories, +} from "~/enums"; +import { uploadAssets } from "./uploadAssets"; +import { UploaderState } from "~/models"; interface Props { title: string; + engineCategory: FilterEngineCategories; fileTypes: string[]; onClose: () => void; options?: { @@ -12,21 +19,16 @@ interface Props { hasLength?: boolean; hasThumbnailUpload?: boolean; }; - onSubmit: (options: { - title: string; - typeOption?: MediaFileAnimationType; - assetFile: File; - length: number; - thumbnailFile: File | null; - }) => void; + onUploadProgress: (newState: UploaderState) => void; } export const UploadFiles = ({ fileTypes, + engineCategory, onClose, title, options, - onSubmit, + onUploadProgress, }: Props) => { const fileSubtypes = options?.fileSubtypes; const hasLength = options?.hasLength; @@ -69,12 +71,15 @@ export const UploadFiles = ({ })); return; } - onSubmit({ + + uploadAssets({ title: uploadTitle.value, assetFile: assetFile.value, - thumbnailFile: thumbnailFile, - length: uploadLength ?? 1000, - typeOption, + engineCategory: engineCategory, + animationType: typeOption, + thumbnailFile: thumbnailFile || undefined, + length: uploadLength, + progressCallback: onUploadProgress, }); }; @@ -118,7 +123,7 @@ export const UploadFiles = ({ {hasThumbnailUpload && ( diff --git a/app/components/reusable/UploadModal/UploadModal.tsx b/app/components/reusable/UploadModal/UploadModal.tsx index 503323b7d5..63c27ae84c 100644 --- a/app/components/reusable/UploadModal/UploadModal.tsx +++ b/app/components/reusable/UploadModal/UploadModal.tsx @@ -1,18 +1,13 @@ import { useEffect, useState } from "react"; -import { v4 as uuidv4 } from "uuid"; import { TransitionDialogue, LoadingDots } from "~/components"; import { UploadAssetError } from "./UploadAssetError"; import { UploadSuccess } from "./UploadSuccess"; import { UploadFiles } from "./UploadFiles"; -import { MediaFilesApi, MediaUploadApi } from "~/Classes/ApiManager"; -import { - FilterEngineCategories, - MediaFileAnimationType, - UploaderState, -} from "~/enums"; -import { getFileName } from "~/utilities"; + +import { FilterEngineCategories, UploaderStates } from "~/enums"; +import { UploaderState, initialUploaderState } from "~/models"; interface Props { onClose: () => void; @@ -28,10 +23,6 @@ interface Props { }; } -const initialState = { - status: UploaderState.ready, -}; - export function UploadModal({ isOpen, onClose, @@ -41,13 +32,15 @@ export function UploadModal({ type, options, }: Props) { - const [uploaderState, setUploaderState] = useState<{ - status: UploaderState; - errorMessage?: string; - }>(initialState); + const [uploaderState, setUploaderState] = + useState(initialUploaderState); + + const updateUploaderState = (newState: UploaderState) => { + setUploaderState(newState); + }; const resetModalState = () => { - setUploaderState(initialState); + setUploaderState(initialUploaderState); }; useEffect(() => { @@ -56,98 +49,29 @@ export function UploadModal({ } }, [isOpen]); - const onSubmit = async ({ - title, - typeOption, - assetFile, - length, - thumbnailFile, - }: { - title: string; - typeOption?: MediaFileAnimationType; - assetFile: File; - length: number; - thumbnailFile: File | null; - }) => { - setUploaderState({ status: UploaderState.uploadingAsset }); - const mediaUploadApi = new MediaUploadApi(); - const assetReponse = await mediaUploadApi.UploadNewEngineAsset({ - file: assetFile, - fileName: assetFile.name, - engine_category: type, - maybe_animation_type: typeOption, - maybe_duration_millis: length, - maybe_title: title, - uuid: uuidv4(), - }); - - if (!assetReponse.success || !assetReponse.data) { - setUploaderState({ - status: UploaderState.assetError, - errorMessage: assetReponse.errorMessage, - }); - return; - } - const assetToken = assetReponse.data; - if (!thumbnailFile) { - setUploaderState({ status: UploaderState.success }); - return; - } - - setUploaderState({ status: UploaderState.uploadingCover }); - const thumbnailResponse = await mediaUploadApi.UploadImage({ - uuid: uuidv4(), - blob: thumbnailFile, - fileName: getFileName(thumbnailFile), - maybe_title: "thumbnail_" + title, - }); - if (!thumbnailResponse.success || !thumbnailResponse.data) { - setUploaderState({ - status: UploaderState.coverCreateError, - errorMessage: thumbnailResponse.errorMessage, - }); - return; - } - - setUploaderState({ status: UploaderState.settingCover }); - const thumbnailToken = thumbnailResponse.data; - const mediaFilesApi = new MediaFilesApi(); - const setThumbnailResponse = await mediaFilesApi.UpdateCoverImage({ - mediaFileToken: assetToken, - imageToken: thumbnailToken, - }); - if (!setThumbnailResponse.success) { - setUploaderState({ - status: UploaderState.coverSetError, - errorMessage: setThumbnailResponse.errorMessage, - }); - return; - } - setUploaderState({ status: UploaderState.success }); - }; - const UploaderModalContent = () => { switch (uploaderState.status) { - case UploaderState.ready: + case UploaderStates.ready: return ( ); - case UploaderState.uploadingAsset: - case UploaderState.uploadingCover: - case UploaderState.settingCover: + case UploaderStates.uploadingAsset: + case UploaderStates.uploadingCover: + case UploaderStates.settingCover: return ( <>
Uploading...
); - case UploaderState.success: + case UploaderStates.success: return ( ); - case UploaderState.assetError: + case UploaderStates.assetError: return ( ); - case UploaderState.coverCreateError: - case UploaderState.coverSetError: + case UploaderStates.coverCreateError: + case UploaderStates.coverSetError: return ( void; +}) => { + progressCallback({ status: UploaderStates.uploadingAsset }); + const mediaUploadApi = new MediaUploadApi(); + const assetReponse = await mediaUploadApi.UploadNewEngineAsset({ + file: assetFile, + fileName: assetFile.name, + engine_category: engineCategory, + maybe_animation_type: animationType, + maybe_duration_millis: length, + maybe_title: title, + uuid: uuidv4(), + }); + + if (!assetReponse.success || !assetReponse.data) { + progressCallback({ + status: UploaderStates.assetError, + errorMessage: assetReponse.errorMessage, + }); + return; + } + const assetToken = assetReponse.data; + if (!thumbnailFile) { + progressCallback({ status: UploaderStates.success }); + return; + } + + progressCallback({ status: UploaderStates.uploadingCover }); + const thumbnailResponse = await mediaUploadApi.UploadImage({ + uuid: uuidv4(), + blob: thumbnailFile, + fileName: getFileName(thumbnailFile), + maybe_title: "thumbnail_" + title, + }); + if (!thumbnailResponse.success || !thumbnailResponse.data) { + progressCallback({ + status: UploaderStates.coverCreateError, + errorMessage: thumbnailResponse.errorMessage, + }); + return; + } + + progressCallback({ status: UploaderStates.settingCover }); + const thumbnailToken = thumbnailResponse.data; + const mediaFilesApi = new MediaFilesApi(); + const setThumbnailResponse = await mediaFilesApi.UpdateCoverImage({ + mediaFileToken: assetToken, + imageToken: thumbnailToken, + }); + if (!setThumbnailResponse.success) { + progressCallback({ + status: UploaderStates.coverSetError, + errorMessage: setThumbnailResponse.errorMessage, + }); + return; + } + progressCallback({ status: UploaderStates.success }); +}; diff --git a/app/components/reusable/UploadModal3DPreview/UploadFiles3D.tsx b/app/components/reusable/UploadModal3D/UploadFiles3D.tsx similarity index 75% rename from app/components/reusable/UploadModal3DPreview/UploadFiles3D.tsx rename to app/components/reusable/UploadModal3D/UploadFiles3D.tsx index 1f81ce1d5c..797f4d43f4 100644 --- a/app/components/reusable/UploadModal3DPreview/UploadFiles3D.tsx +++ b/app/components/reusable/UploadModal3D/UploadFiles3D.tsx @@ -1,40 +1,31 @@ import { useCallback, useEffect, useState, useRef } from "react"; -import { MediaFileAnimationType } from "~/enums"; - import { Button, H6, Input, ListDropdown } from "~/components"; import { FileUploader } from "../UploadModal/FileUploader"; -import { loadPreviewOnCanvas } from "./utilities"; +import { loadPreviewOnCanvas, snapshotCanvasAsThumbnail } from "./utilities"; +import { upload3DObjects } from "./utilities/upload3DObjects"; +import { UploaderState } from "~/models"; +import { MediaFileAnimationType } from "~/enums"; interface Props { title: string; fileTypes: string[]; - onClose: () => void; options?: { fileSubtypes?: { [key: string]: string }[]; hasLength?: boolean; hasThumbnailUpload?: boolean; }; - onSubmit: (options: { - title: string; - typeOption?: MediaFileAnimationType; - assetFile: File; - length: number; - thumbnailFile: File | null; - }) => void; + onClose: () => void; + onUploadProgress: (newState: UploaderState) => void; } export const UploadFiles3D = ({ - fileTypes, - onClose, title, + fileTypes, options, - onSubmit, + onClose, + onUploadProgress, }: Props) => { - const fileSubtypes = options?.fileSubtypes; - const hasLength = options?.hasLength; - const hasThumbnailUpload = options?.hasThumbnailUpload; - const canvasRef = useRef(undefined); const canvasCallbackRef = useCallback((node: HTMLCanvasElement) => { if (node !== null) { @@ -42,13 +33,16 @@ export const UploadFiles3D = ({ } }, []); - const [typeOption, setTypeOption] = useState< - MediaFileAnimationType | undefined - >( + const fileSubtypes = options?.fileSubtypes; + // const hasLength = options?.hasLength; + // const hasThumbnailUpload = options?.hasThumbnailUpload; + + const [subtype, setSubtype] = useState( fileSubtypes ? (Object.values(fileSubtypes[0])[0] as MediaFileAnimationType) : undefined, ); + const [uploadTitle, setUploadTitle] = useState<{ value: string; error?: string; @@ -66,8 +60,9 @@ export const UploadFiles3D = ({ message?: string; }>({ type: "init" }); - const [uploadLength, setUploadLength] = useState(); - const [thumbnailFile, setThumbnailFile] = useState(null); + const [thumbnailFile, setThumbnailFile] = useState( + undefined, + ); const handleSubmit = () => { if (!uploadTitle.value) { @@ -86,12 +81,12 @@ export const UploadFiles3D = ({ return; } - onSubmit({ + upload3DObjects({ title: uploadTitle.value, assetFile: assetFile.value, - thumbnailFile: thumbnailFile, - length: uploadLength ?? 1000, - typeOption, + thumbnailSnapshot: thumbnailFile, + animationType: subtype, + progressCallback: onUploadProgress, }); }; @@ -107,6 +102,19 @@ export const UploadFiles3D = ({ } }, [assetFile.value]); + useEffect(() => { + if (previewStatus.type === "OK" && canvasRef.current) { + snapshotCanvasAsThumbnail({ + targetNode: canvasRef.current!, + resultCallback: (snapshotBlob) => { + if (snapshotBlob) { + setThumbnailFile(snapshotBlob); + } + }, + }); + } + }, [previewStatus]); + return ( <>
@@ -117,20 +125,7 @@ export const UploadFiles3D = ({ onChange={(event) => setUploadTitle({ value: event.target.value })} className={uploadTitle.error ? "mb-3" : ""} /> - {fileSubtypes && fileSubtypes.length > 1 && ( - setTypeOption(value as MediaFileAnimationType)} - /> - )} - {hasLength && ( - setUploadLength(parseInt(event.target.value))} - /> - )} + + {fileSubtypes && fileSubtypes.length > 1 && ( + setSubtype(value as MediaFileAnimationType)} + /> + )} {assetFile.error && (
{assetFile.error}
)} @@ -163,14 +164,6 @@ export const UploadFiles3D = ({ )}
- {hasThumbnailUpload && ( - - )}