diff --git a/app/components/reusable/UploadModalMedia/UploadImageMediaModal.tsx b/app/components/reusable/UploadModalMedia/UploadImageMediaModal.tsx index 14c792fbaa..713b23c77e 100644 --- a/app/components/reusable/UploadModalMedia/UploadImageMediaModal.tsx +++ b/app/components/reusable/UploadModalMedia/UploadImageMediaModal.tsx @@ -11,7 +11,7 @@ import { UploadImageMedia } from "./UploadImageMedia"; interface Props { onClose: () => void; - onSuccess: (data?: unknown) => void; + onSuccess: (data?: string) => void; isOpen: boolean; title: string; fileTypes: string[]; diff --git a/app/models/UploaderState.ts b/app/models/UploaderState.ts index 3196827b75..901bfe5189 100644 --- a/app/models/UploaderState.ts +++ b/app/models/UploaderState.ts @@ -3,7 +3,7 @@ import { UploaderStates } from "~/enums"; export interface UploaderState { status: UploaderStates; errorMessage?: string; - data?: unknown; + data?: string; } export const initialUploaderState = { diff --git a/app/pages/PageEnigma/comps/Timeline/CharacterFrameButtons.tsx b/app/pages/PageEnigma/comps/Timeline/CharacterFrameButtons.tsx index 17de4076c1..6731173153 100644 --- a/app/pages/PageEnigma/comps/Timeline/CharacterFrameButtons.tsx +++ b/app/pages/PageEnigma/comps/Timeline/CharacterFrameButtons.tsx @@ -1,11 +1,18 @@ -import { faImage } from "@fortawesome/pro-solid-svg-icons"; +import { faImage, faTrashCan } from "@fortawesome/pro-solid-svg-icons"; import { ButtonIconStack } from "~/components/reusable/ButtonIconStack"; import { frameTrackButtonWidthPx } from "../../signals"; -import { useCallback, useContext, useState } from "react"; +import { useCallback, useContext, useRef, useState } from "react"; import { EngineContext } from "../../contexts/EngineContext"; import { CHARACTER_FRAME_FILE_TYPE } from "~/enums"; import { UploadModalMedia } from "~/components/reusable/UploadModalMedia"; import { UploadImageMediaModal } from "~/components/reusable/UploadModalMedia/UploadImageMediaModal"; +import { MediaFilesApi } from "~/Classes/ApiManager"; +import { MediaFile } from "../../models"; +import { ApiResponse } from "~/Classes/ApiManager/ApiManager"; +import { get_media_url } from "~/Classes/ApiHelpers"; +import { twMerge } from "tailwind-merge"; +import { ButtonIcon } from "~/components"; +import { FontAwesomeIcon } from "@fortawesome/react-fontawesome"; export enum CharacterFrameTarget { Start, @@ -31,25 +38,93 @@ export default function CharacterFrameButton( }: CharacterFrameButtonProps ) { + const clickable = useRef(true); const editorEngine = useContext(EngineContext); const [isUploadModalOpen, setIsUploadModalOpen] = useState(false); + const [mediaFile, setMediaFile] = useState(undefined); const handleFrameClick = useCallback(() => { + if (!clickable.current) { + return; + } - }) + // Make button unresponsive to clicks until modal is closed + clickable.current = false; + setIsUploadModalOpen(true); + }, []) - return ( - <> -
- setIsUploadModalOpen(true)} /> -
- setIsUploadModalOpen(false)} - onSuccess={(data) => { console.log(data); setIsUploadModalOpen(false) }} - title={"Upload Character Frame Image"} - fileTypes={Object.values(CHARACTER_FRAME_FILE_TYPE)} - /> - - ) + + const unlockButton = useCallback(() => { + clickable.current = true; + setIsUploadModalOpen(false); + }, [setIsUploadModalOpen]); + + + const handleFrameSet = useCallback((token?: string) => { + if (!token) { + return; + } + + // TODO: Fetch the image and pass the token to the character engine + + // Fetch the image and set as button bg + get_media_url(token) + .then((url) => { + setMediaFile(url); + }) + .catch((error) => { + console.error("Error fetching media file", error); + }) + .finally(() => { + unlockButton(); + }) + + }, [unlockButton, setMediaFile]); + + const handleDeleteFrame = useCallback(() => { + setMediaFile(undefined); + unlockButton(); + }, [unlockButton, setMediaFile]); + + + if (!mediaFile) { + return ( + <> +
+ +
+ + + ) + } else { + const classes = twMerge([ + className, + "bg-character-unselected overflow-hidden" + ]); + + const buttonClasses = twMerge([ + "absolute flex box-content top-1 right-1 w-6 h-6 rounded-md invisible group-hover:visible items-center justify-center", + "bg-gray-800 hover:bg-red text-gray-500 hover:text-gray-100 border-2 border-transparent hover:border-white", + "transition-all duration-150", + ]) + + return ( + <> +
+
+ {CharacterFrameStrings[target]} + +
+
+ + ) + } } diff --git a/app/pages/PageEnigma/comps/Timeline/TrackClip.tsx b/app/pages/PageEnigma/comps/Timeline/TrackClip.tsx index b20c99d46e..18fa1457ae 100644 --- a/app/pages/PageEnigma/comps/Timeline/TrackClip.tsx +++ b/app/pages/PageEnigma/comps/Timeline/TrackClip.tsx @@ -3,6 +3,7 @@ import { useState } from "react"; import { canDrop, dragItem, + frameTrackButtonWidthPx, scale, selectedItem, } from "~/pages/PageEnigma/signals"; @@ -54,7 +55,7 @@ export const TrackClip = ({ clip, min, max, group, updateClip }: Props) => { : "", ].join(" ")} style={{ - left: offset * 4 * scale.value, + left: offset * 4 * scale.value + frameTrackButtonWidthPx, }} onPointerDown={(event) => onPointerDown(event, "left")} onPointerMove={() => { @@ -83,7 +84,7 @@ export const TrackClip = ({ clip, min, max, group, updateClip }: Props) => { ].join(" ")} style={{ width: length * 4 * scale.value - 30, - left: offset * 4 * scale.value + 15, + left: offset * 4 * scale.value + 15 + frameTrackButtonWidthPx, cursor: "move", }} onPointerDown={(event) => onPointerDown(event, "drag")} @@ -116,7 +117,7 @@ export const TrackClip = ({ clip, min, max, group, updateClip }: Props) => { : "", ].join(" ")} style={{ - left: offset * 4 * scale.value + length * 4 * scale.value - 15, + left: offset * 4 * scale.value + length * 4 * scale.value - 15 + frameTrackButtonWidthPx, }} onPointerDown={(event) => onPointerDown(event, "right")} onPointerMove={() => { diff --git a/app/pages/PageEnigma/comps/Timeline/TrackClips.tsx b/app/pages/PageEnigma/comps/Timeline/TrackClips.tsx index 22d7128a3d..45f723b637 100644 --- a/app/pages/PageEnigma/comps/Timeline/TrackClips.tsx +++ b/app/pages/PageEnigma/comps/Timeline/TrackClips.tsx @@ -95,7 +95,7 @@ export const TrackClips = ({ id, clips, updateClip, group, type }: Props) => {