From 985de175a515ec462ce14df197b8b902add2ade7 Mon Sep 17 00:00:00 2001 From: FromTheLOLAshes Date: Wed, 27 Mar 2024 14:17:42 +0700 Subject: [PATCH] Add delete clip with modal --- app/components/ConfirmationModal.tsx | 54 +++++++++++++++++ app/modules/LowerPanel/LowerPanel.tsx | 1 - .../PageEnigma/comps/Timeline/Timeline.tsx | 59 ++++++++++++++++++- .../contexts/TrackContext/TrackContext.tsx | 8 +++ .../TrackContext/utils/useUpdateAudio.ts | 10 ++++ .../TrackContext/utils/useUpdateCamera.ts | 10 ++++ .../TrackContext/utils/useUpdateCharacters.ts | 20 +++++++ .../TrackContext/utils/useUpdateObject.ts | 13 ++++ 8 files changed, 172 insertions(+), 3 deletions(-) create mode 100644 app/components/ConfirmationModal.tsx diff --git a/app/components/ConfirmationModal.tsx b/app/components/ConfirmationModal.tsx new file mode 100644 index 0000000000..0efd8fc2d8 --- /dev/null +++ b/app/components/ConfirmationModal.tsx @@ -0,0 +1,54 @@ +import { Dialog } from "@headlessui/react"; + +interface Props { + text: string; + title: string; + open: boolean; + onClose: () => void; + onOk?: () => void; + okText?: string; + okColor?: string; + onCancel?: () => void; + cancelText?: string; +} + +export const ConfirmationModal = ({ + text, + title, + open, + onClose, + onOk, + okText = "OK", + okColor = "bg-brand-success", + onCancel, + cancelText = "Cancel", +}: Props) => { + return ( + +
+
+ + {title} + +

{text}

+ +
+ {!!onCancel && ( + + )} + {!!onOk && ( + + )} +
+
+
+
+ ); +}; diff --git a/app/modules/LowerPanel/LowerPanel.tsx b/app/modules/LowerPanel/LowerPanel.tsx index 2fb21e12b1..ba6a4762d0 100644 --- a/app/modules/LowerPanel/LowerPanel.tsx +++ b/app/modules/LowerPanel/LowerPanel.tsx @@ -23,7 +23,6 @@ export const LowerPanel = ({ children, timelineHeight }: LowerPanelPropsI) => { ].join(" ")} style={{ height: timelineHeight }} onPointerOver={() => { - console.log("over"); setOverTimeline(true); }} onPointerLeave={() => setOverTimeline(false)} diff --git a/app/pages/PageEnigma/comps/Timeline/Timeline.tsx b/app/pages/PageEnigma/comps/Timeline/Timeline.tsx index c310970174..2ae8022803 100644 --- a/app/pages/PageEnigma/comps/Timeline/Timeline.tsx +++ b/app/pages/PageEnigma/comps/Timeline/Timeline.tsx @@ -1,4 +1,4 @@ -import { Fragment, useContext } from "react"; +import { Fragment, useCallback, useContext, useEffect, useState } from "react"; import { TrackContext } from "~/pages/PageEnigma/contexts/TrackContext/TrackContext"; import { LowerPanel } from "~/modules/LowerPanel"; import { Character } from "./Character"; @@ -9,19 +9,61 @@ import { Objects } from "./Objects"; import { useMouseEventsAnimation } from "./utils/useMouseEventsAnimation"; import { faSortDown } from "@fortawesome/pro-solid-svg-icons"; import { FontAwesomeIcon } from "@fortawesome/react-fontawesome"; +import { ConfirmationModal } from "~/components/ConfirmationModal"; interface Props { timelineHeight: number; } export const Timeline = ({ timelineHeight }: Props) => { - const { characters, objects, scale, length } = useContext(TrackContext); + const { + characters, + objects, + scale, + length, + selectedClip, + deleteCharacterClip, + deleteAudioClip, + deleteCameraClip, + deleteObjectClip, + } = useContext(TrackContext); const { onPointerDown, time } = useMouseEventsAnimation(); + const [dialogOpen, setDialogOpen] = useState(false); const sectionWidth = 60 * 4 * scale; const fullHeight = characters.length * 200 + objects.objects.length * 80 + 248 + 96; + const onDeleteAsk = useCallback( + (event: KeyboardEvent) => { + if (["Backspace", "Delete"].indexOf(event.key) > -1 && selectedClip) { + setDialogOpen(true); + } + }, + [selectedClip], + ); + + const onDelete = useCallback(() => { + deleteCharacterClip(selectedClip!); + deleteCameraClip(selectedClip!); + deleteAudioClip(selectedClip!); + deleteObjectClip(selectedClip!); + }, [ + selectedClip, + deleteAudioClip, + deleteCharacterClip, + deleteCameraClip, + deleteObjectClip, + ]); + + useEffect(() => { + document.addEventListener("keydown", onDeleteAsk); + + return () => { + document.addEventListener("keydown", onDeleteAsk); + }; + }, [onDeleteAsk]); + return ( <> @@ -102,6 +144,19 @@ export const Timeline = ({ timelineHeight }: Props) => { /> + setDialogOpen(false)} + onOk={() => { + onDelete(); + setDialogOpen(false); + }} + okText="Delete" + okColor="bg-brand-primary" + onCancel={() => setDialogOpen(false)} + /> ); }; diff --git a/app/pages/PageEnigma/contexts/TrackContext/TrackContext.tsx b/app/pages/PageEnigma/contexts/TrackContext/TrackContext.tsx index 5ce6ff89c9..b9edb3d1e6 100644 --- a/app/pages/PageEnigma/contexts/TrackContext/TrackContext.tsx +++ b/app/pages/PageEnigma/contexts/TrackContext/TrackContext.tsx @@ -32,6 +32,7 @@ export const TrackContext = createContext<{ offset: number; }) => void; selectCharacterClip: (clipId: string) => void; + deleteCharacterClip: (clipId: string) => void; // timeline camera group camera: CameraGroup | null; @@ -41,6 +42,7 @@ export const TrackContext = createContext<{ offset: number; }) => void; selectCameraClip: (clipId: string) => void; + deleteCameraClip: (clipId: string) => void; // timeline global audio group audio: AudioGroup | null; @@ -52,6 +54,7 @@ export const TrackContext = createContext<{ toggleAudioMute: () => void; addGlobalAudio: (clipId: string, animationClips: BaseClip[]) => void; selectAudioClip: (clipId: string) => void; + deleteAudioClip: (clipId: string) => void; // timeline objects group objects: ObjectGroup; @@ -61,6 +64,7 @@ export const TrackContext = createContext<{ offset: number; }) => void; selectObjectClip: (clipId: string) => void; + deleteObjectClip: (clipId: string) => void; // current - only select one item - will be replaced selectedClip: string | null; @@ -103,20 +107,24 @@ export const TrackContext = createContext<{ addCharacterAnimation: () => {}, addCharacterAudio: () => {}, selectCharacterClip: () => {}, + deleteCharacterClip: () => {}, camera: null, updateCamera: () => {}, selectCameraClip: () => {}, + deleteCameraClip: () => {}, audio: null, updateAudio: () => {}, toggleAudioMute: () => {}, selectAudioClip: () => {}, addGlobalAudio: () => {}, + deleteAudioClip: () => {}, objects: { id: "", objects: [] }, updateObject: () => {}, selectObjectClip: () => {}, + deleteObjectClip: () => {}, selectedClip: null, selectClip: () => {}, diff --git a/app/pages/PageEnigma/contexts/TrackContext/utils/useUpdateAudio.ts b/app/pages/PageEnigma/contexts/TrackContext/utils/useUpdateAudio.ts index 8b7e95300b..8daab4f93c 100644 --- a/app/pages/PageEnigma/contexts/TrackContext/utils/useUpdateAudio.ts +++ b/app/pages/PageEnigma/contexts/TrackContext/utils/useUpdateAudio.ts @@ -91,11 +91,21 @@ export default function useUpdateAudio() { }); }, []); + const deleteAudioClip = useCallback((clipId: string) => { + setAudio((oldAudio) => { + return { + ...oldAudio, + clips: [...oldAudio.clips.filter((clip) => clip.id !== clipId)], + }; + }); + }, []); + return { audio, updateAudio, toggleAudioMute, selectAudioClip, addGlobalAudio, + deleteAudioClip, }; } diff --git a/app/pages/PageEnigma/contexts/TrackContext/utils/useUpdateCamera.ts b/app/pages/PageEnigma/contexts/TrackContext/utils/useUpdateCamera.ts index b0a0912157..b5b7d21da6 100644 --- a/app/pages/PageEnigma/contexts/TrackContext/utils/useUpdateCamera.ts +++ b/app/pages/PageEnigma/contexts/TrackContext/utils/useUpdateCamera.ts @@ -64,9 +64,19 @@ export default function useUpdateCamera() { }); }, []); + const deleteCameraClip = useCallback((clipId: string) => { + setCamera((oldCamera) => { + return { + ...oldCamera, + clips: [...oldCamera.clips.filter((clip) => clip.id !== clipId)], + }; + }); + }, []); + return { camera, updateCamera, selectCameraClip, + deleteCameraClip, }; } diff --git a/app/pages/PageEnigma/contexts/TrackContext/utils/useUpdateCharacters.ts b/app/pages/PageEnigma/contexts/TrackContext/utils/useUpdateCharacters.ts index ddbbab21da..88dc0ca4e5 100644 --- a/app/pages/PageEnigma/contexts/TrackContext/utils/useUpdateCharacters.ts +++ b/app/pages/PageEnigma/contexts/TrackContext/utils/useUpdateCharacters.ts @@ -203,6 +203,25 @@ export default function useUpdateCharacters() { }); }, []); + const deleteCharacterClip = useCallback((clipId: string) => { + setCharacters((oldCharacters) => { + return [ + ...oldCharacters.map((character) => ({ + ...character, + animationClips: character.animationClips.filter( + (clip) => clip.id !== clipId, + ), + positionClips: character.positionClips.filter( + (clip) => clip.id !== clipId, + ), + lipSyncClips: character.lipSyncClips.filter( + (clip) => clip.id !== clipId, + ), + })), + ]; + }); + }, []); + return { characters, updateCharacters, @@ -210,5 +229,6 @@ export default function useUpdateCharacters() { addCharacterAnimation, addCharacterAudio, selectCharacterClip, + deleteCharacterClip, }; } diff --git a/app/pages/PageEnigma/contexts/TrackContext/utils/useUpdateObject.ts b/app/pages/PageEnigma/contexts/TrackContext/utils/useUpdateObject.ts index 2866aa9faa..b23b738816 100644 --- a/app/pages/PageEnigma/contexts/TrackContext/utils/useUpdateObject.ts +++ b/app/pages/PageEnigma/contexts/TrackContext/utils/useUpdateObject.ts @@ -61,9 +61,22 @@ export default function useUpdateObject() { }); }, []); + const deleteObjectClip = useCallback((clipId: string) => { + setObjects((oldObjectGroup) => { + return { + ...oldObjectGroup, + objects: oldObjectGroup.objects.map((object) => ({ + ...object, + clips: object.clips.filter((clip) => clip.id !== clipId), + })), + }; + }); + }, []); + return { objects, updateObject, selectObjectClip, + deleteObjectClip, }; }