diff --git a/app/contexts/ClipContext/ClipContext.tsx b/app/contexts/ClipContext/ClipContext.tsx new file mode 100644 index 0000000000..7b0d264c92 --- /dev/null +++ b/app/contexts/ClipContext/ClipContext.tsx @@ -0,0 +1,18 @@ +import { createContext } from "react"; +import { AnimationClip, AudioClip } from "~/models/track"; + +export const ClipContext = createContext<{ + animationClips: AnimationClip[]; + audioClips: AudioClip[]; + dragType: "animation" | "audio" | null; + dragId: string | null; + startDrag: (type: "animation" | "audio", id: string) => void; + endDrag: () => void; +}>({ + animationClips: [], + audioClips: [], + dragType: null, + dragId: null, + startDrag: () => {}, + endDrag: () => {}, +}); diff --git a/app/contexts/ClipContext/ClipProvider.tsx b/app/contexts/ClipContext/ClipProvider.tsx new file mode 100644 index 0000000000..0b541b204f --- /dev/null +++ b/app/contexts/ClipContext/ClipProvider.tsx @@ -0,0 +1,42 @@ +import { ReactNode, useCallback, useEffect, useMemo, useState } from "react"; +import { ClipContext } from "./ClipContext"; +import { AnimationClip, AudioClip } from "~/models/track"; + +interface Props { + children: ReactNode; +} + +export const ClipProvider = ({ children }: Props) => { + const [state, setState] = useState<{ + dragType: "animation" | "audio" | null; + dragId: string | null; + }>({ dragType: null, dragId: null }); + const [animationClips, setAnimationClips] = useState([]); + const [audioClips, setAudioClips] = useState([]); + + useEffect(() => { + setAnimationClips([]); + setAudioClips([]); + }, []); + + const startDrag = useCallback((type: "animation" | "audio", id: string) => { + setState({ dragId: id, dragType: type }); + }, []); + + const endDrag = useCallback(() => { + setState({ dragId: null, dragType: null }); + }, []); + + const values = useMemo(() => { + return { + dragType: state.dragType, + dragId: state.dragId, + animationClips, + audioClips, + startDrag, + endDrag, + }; + }, [state, animationClips, audioClips, startDrag, endDrag]); + + return {children}; +}; diff --git a/app/modules/LowerPanel/LowerPanel.tsx b/app/modules/LowerPanel/LowerPanel.tsx index 411a629518..680a097953 100644 --- a/app/modules/LowerPanel/LowerPanel.tsx +++ b/app/modules/LowerPanel/LowerPanel.tsx @@ -1,10 +1,11 @@ import { useState } from "react"; interface LowerPanelPropsI { + timelineHeight: number; children: React.ReactNode; } -export const LowerPanel = ({ children }: LowerPanelPropsI) => { +export const LowerPanel = ({ children, timelineHeight }: LowerPanelPropsI) => { const [open, setOpen] = useState(false); const handleOpen = () => { setOpen(true); @@ -14,10 +15,11 @@ export const LowerPanel = ({ children }: LowerPanelPropsI) => {
{children}
diff --git a/app/pages/PageEnigma/PageEnigma.tsx b/app/pages/PageEnigma/PageEnigma.tsx index c4b35be83d..be63286bb3 100644 --- a/app/pages/PageEnigma/PageEnigma.tsx +++ b/app/pages/PageEnigma/PageEnigma.tsx @@ -1,104 +1,10 @@ -import { useCallback, useEffect, useRef, useState } from "react"; - -import { faSparkles } from "@fortawesome/pro-solid-svg-icons"; - -import { Button, ButtonLink } from "~/components"; -// import { ButtonDialogue } from "~/modules/ButtonDialogue"; -import { TopBarHelmet } from "~/modules/TopBarHelmet/TopBarHelmet"; -import { SidePanel } from "~/modules/SidePanel"; -import { Controls3D } from "./comps/Controls3D"; -import { ControlsTopButtons } from "./comps/ControlsTopButtons"; -import { ControlsVideo } from "./comps/ControlsVideo"; -import { PreviewWindow } from "./comps/PreviewWindow"; -import { Timeline } from "./comps/Timeline"; -import { SidePanelTabs } from "./comps/SidePanelTabs"; - -import { TrackProvider } from "~/contexts/TrackContext/TrackProvider"; -import { EngineProvider } from "~/contexts/EngineProvider"; +import { ClipProvider } from "~/contexts/ClipContext/ClipProvider"; +import { PageEnigmaComponent } from "./PageEnigmaComponent"; export const PageEnigma = () => { - const canvasRef = useRef(null); - - const [timelineHeight, setTimelineHeight] = useState(0); - const timelineRef = useRef(null); - - - const updateTimelineHeight = useCallback(() => { - if (timelineRef.current) { - setTimelineHeight(timelineRef.current.offsetHeight); - } - }, []); - - //for updating timeline/engine div height (for resizing) - useEffect(() => { - const observer = new ResizeObserver((entries) => { - for (let entry of entries) { - updateTimelineHeight(); - } - }); - - if (timelineRef.current) { - observer.observe(timelineRef.current); - } - - return () => observer.disconnect(); - }, [updateTimelineHeight]); - return ( -
- - - - -
- {/* Engine section/side panel */} -
-
- - - {/* Top controls */} -
-
- - -
-
- - {/* Bottom controls */} -
- - -
-
- - {/* Side panel */} - - - -
- - {/* Timeline */} -
- - - -
-
-
-
+ + + ); }; diff --git a/app/pages/PageEnigma/PageEnigmaComponent.tsx b/app/pages/PageEnigma/PageEnigmaComponent.tsx new file mode 100644 index 0000000000..930336ccca --- /dev/null +++ b/app/pages/PageEnigma/PageEnigmaComponent.tsx @@ -0,0 +1,98 @@ +import { useCallback, useEffect, useRef, useState } from "react"; + +import { faSparkles } from "@fortawesome/pro-solid-svg-icons"; + +import { Button } from "~/components"; +import { TopBarHelmet } from "~/modules/TopBarHelmet/TopBarHelmet"; +import { SidePanel } from "~/modules/SidePanel"; +import { Controls3D } from "./comps/Controls3D"; +import { ControlsTopButtons } from "./comps/ControlsTopButtons"; +import { ControlsVideo } from "./comps/ControlsVideo"; +import { PreviewWindow } from "./comps/PreviewWindow"; +import { Timeline } from "./comps/Timeline"; +import { SidePanelTabs } from "./comps/SidePanelTabs"; + +import { TrackProvider } from "~/contexts/TrackContext/TrackProvider"; +import { EngineProvider } from "~/contexts/EngineProvider"; + +export const PageEnigmaComponent = () => { + const canvasRef = useRef(null); + + const [timelineHeight, setTimelineHeight] = useState(260); + const timelineRef = useRef(null); + + const updateTimelineHeight = useCallback(() => { + if (timelineRef.current) { + setTimelineHeight(timelineRef.current.offsetHeight); + } + }, []); + + //for updating timeline/engine div height (for resizing) + useEffect(() => { + const observer = new ResizeObserver((entries) => { + for (let entry of entries) { + updateTimelineHeight(); + } + }); + + if (timelineRef.current) { + observer.observe(timelineRef.current); + } + + return () => observer.disconnect(); + }, [updateTimelineHeight]); + + return ( +
+ + + + +
+ {/* Engine section/side panel */} +
+
+ + + {/* Top controls */} +
+
+ + +
+
+ + {/* Bottom controls */} +
+ + +
+
+ + {/* Side panel */} + + + +
+ + {/* Timeline */} +
+ + + +
+
+
+
+ ); +}; diff --git a/app/pages/PageEnigma/comps/SidePanelTabs/index.tsx b/app/pages/PageEnigma/comps/SidePanelTabs/index.tsx index 38929885a1..ba739f09d4 100644 --- a/app/pages/PageEnigma/comps/SidePanelTabs/index.tsx +++ b/app/pages/PageEnigma/comps/SidePanelTabs/index.tsx @@ -1,13 +1,20 @@ import { Tabs } from "~/modules/Tabs"; import { TabStyling } from "./TabStyling"; -export const SidePanelTabs = ()=>{ - return( +export const SidePanelTabs = () => { + return ( Animation Tab

, + children: ( +
+
+
+ ), }, { header: "Camera", @@ -24,5 +31,4 @@ export const SidePanelTabs = ()=>{ ]} /> ); - -} \ No newline at end of file +}; diff --git a/app/pages/PageEnigma/comps/Timeline/Audio.tsx b/app/pages/PageEnigma/comps/Timeline/Audio.tsx index 67ad885d2b..79a50d8a44 100644 --- a/app/pages/PageEnigma/comps/Timeline/Audio.tsx +++ b/app/pages/PageEnigma/comps/Timeline/Audio.tsx @@ -1,8 +1,6 @@ -import { TrackClip } from "./TrackClip"; import { useContext } from "react"; import { TrackContext } from "~/contexts/TrackContext/TrackContext"; -import { FontAwesomeIcon } from "@fortawesome/react-fontawesome"; -import { faVolume, faVolumeSlash } from "@fortawesome/pro-solid-svg-icons"; +import { Track } from "~/pages/PageEnigma/comps/Timeline/Track"; export const Audio = () => { const { audio, updateAudio, length, scale, toggleAudioMute } = @@ -17,48 +15,14 @@ export const Audio = () => { >
Global Audio
-
-
- {clips.map((clip, index) => ( - 0 - ? clips[index - 1].offset + clips[index - 1].length - : 0 - } - max={ - index < clips.length - 1 - ? clips[index + 1].offset - : length * 60 - } - style="audio" - updateClip={updateAudio} - clip={clip} - /> - ))} -
- Global Audio Track -
- -
-
+
); diff --git a/app/pages/PageEnigma/comps/Timeline/Camera.tsx b/app/pages/PageEnigma/comps/Timeline/Camera.tsx index 3aff1ab2b0..17c438e1b0 100644 --- a/app/pages/PageEnigma/comps/Timeline/Camera.tsx +++ b/app/pages/PageEnigma/comps/Timeline/Camera.tsx @@ -1,6 +1,6 @@ -import { TrackClip } from "./TrackClip"; import { useContext } from "react"; import { TrackContext } from "~/contexts/TrackContext/TrackContext"; +import { Track } from "~/pages/PageEnigma/comps/Timeline/Track"; export const Camera = () => { const { camera, updateCamera, length, scale } = useContext(TrackContext); @@ -14,34 +14,12 @@ export const Camera = () => { >
Camera
-
-
- {clips.map((clip, index) => ( - 0 - ? clips[index - 1].offset + clips[index - 1].length - : 0 - } - max={ - index < clips.length - 1 - ? clips[index + 1].offset - : length * 60 - } - style="camera" - updateClip={updateCamera} - clip={clip} - /> - ))} -
- Camera Position/Rotation -
-
-
+
); diff --git a/app/pages/PageEnigma/comps/Timeline/Character.tsx b/app/pages/PageEnigma/comps/Timeline/Character.tsx index 57e538f7ba..e4fe7f8e11 100644 --- a/app/pages/PageEnigma/comps/Timeline/Character.tsx +++ b/app/pages/PageEnigma/comps/Timeline/Character.tsx @@ -1,8 +1,6 @@ -import { TrackClip } from "./TrackClip"; -import { useContext, useMemo } from "react"; +import { useCallback, useContext, useMemo } from "react"; import { TrackContext } from "~/contexts/TrackContext/TrackContext"; -import { FontAwesomeIcon } from "@fortawesome/react-fontawesome"; -import { faVolumeSlash, faVolume } from "@fortawesome/pro-solid-svg-icons"; +import { Track } from "~/pages/PageEnigma/comps/Timeline/Track"; function buildUpdaters( updateCharacters: (options: { @@ -47,6 +45,9 @@ export const Character = ({ characterId }: Props) => { const { updateClipLipSync, updateClipPosition, updateClipAnimations } = useMemo(() => buildUpdaters(updateCharacters), [updateCharacters]); + const toggleCharacterLipSyncMute = useCallback(() => { + toggleLipSyncMute(character?.id ?? ""); + }, []); if (!character) { return false; @@ -60,107 +61,29 @@ export const Character = ({ characterId }: Props) => { >
Character
-
-
- {animationClips.map((clip, index) => ( - 0 - ? animationClips[index - 1].offset + - animationClips[index - 1].length - : 0 - } - max={ - index < animationClips.length - 1 - ? animationClips[index + 1].offset - : length * 60 - } - style="character" - updateClip={updateClipAnimations} - clip={clip} - /> - ))} -
- Animation -
-
-
-
-
- {positionClips.map((clip, index) => ( - 0 - ? positionClips[index - 1].offset + - positionClips[index - 1].length - : 0 - } - max={ - index < positionClips.length - 1 - ? positionClips[index + 1].offset - : length * 60 - } - style="character" - updateClip={updateClipPosition} - clip={clip} - /> - ))} -
- Character Position/Rotation -
-
-
-
-
- {lipSyncClips.map((clip, index) => ( - 0 - ? lipSyncClips[index - 1].offset + - lipSyncClips[index - 1].length - : 0 - } - max={ - index < lipSyncClips.length - 1 - ? lipSyncClips[index + 1].offset - : length * 60 - } - style="character" - updateClip={updateClipLipSync} - clip={clip} - /> - ))} -
- Lipsync Audio Track -
- -
-
+ + +
); diff --git a/app/pages/PageEnigma/comps/Timeline/Objects.tsx b/app/pages/PageEnigma/comps/Timeline/Objects.tsx index 7f87120f41..44d3c9efb9 100644 --- a/app/pages/PageEnigma/comps/Timeline/Objects.tsx +++ b/app/pages/PageEnigma/comps/Timeline/Objects.tsx @@ -1,6 +1,6 @@ -import { TrackClip } from "./TrackClip"; import { useContext } from "react"; import { TrackContext } from "~/contexts/TrackContext/TrackContext"; +import { Track } from "~/pages/PageEnigma/comps/Timeline/Track"; export const Objects = () => { const { objects, updateObject, length, scale } = useContext(TrackContext); @@ -14,35 +14,12 @@ export const Objects = () => {
Objects
{objects.objects.map((object) => (
-
-
- {object.clips.map((clip, index) => ( - 0 - ? object.clips[index - 1].offset + - object.clips[index - 1].length - : 0 - } - max={ - index < object.clips.length - 1 - ? object.clips[index + 1].offset - : length * 60 - } - style="objects" - updateClip={updateObject} - clip={clip} - /> - ))} -
- Mask Position/Rotation -
-
-
+
))} diff --git a/app/pages/PageEnigma/comps/Timeline/Timeline.tsx b/app/pages/PageEnigma/comps/Timeline/Timeline.tsx index 26559b8812..c09a7a8d10 100644 --- a/app/pages/PageEnigma/comps/Timeline/Timeline.tsx +++ b/app/pages/PageEnigma/comps/Timeline/Timeline.tsx @@ -1,20 +1,7 @@ -import { - Fragment, - // useCallback, - useContext -} from "react"; - -import { Button } from "~/components"; - -// import Editor from "../../js/editor"; -import { EngineContext } from "~/contexts/EngineContext"; +import { Fragment, useContext } from "react"; import { TrackContext } from "~/contexts/TrackContext/TrackContext"; - import { LowerPanel } from "~/modules/LowerPanel"; import { Character } from "./Character"; - - - import { Camera } from "./Camera"; import { Audio } from "./Audio"; import { Objects } from "./Objects"; @@ -22,16 +9,11 @@ import { useMouseEventsAnimation } from "./utils/useMouseEventsAnimation"; import { faSortDown } from "@fortawesome/pro-solid-svg-icons"; import { FontAwesomeIcon } from "@fortawesome/react-fontawesome"; -export const Timeline = ({ - // editorCurrent, - // time, -}: { - // editorCurrent: Editor | null; - // time: number; -}) => { - const editorEngine = useContext(EngineContext); - // editorEngine replaced the engineRef.current passed as a prop - // const { characters, updateCharacters } = useContext(TrackContext); +interface Props { + timelineHeight: number; +} + +export const Timeline = ({ timelineHeight }: Props) => { const { characters, objects, scale, length } = useContext(TrackContext); const { onPointerDown, time } = useMouseEventsAnimation(); @@ -41,7 +23,7 @@ export const Timeline = ({ return ( <> - +
{Array(length) .fill(0) diff --git a/app/pages/PageEnigma/comps/Timeline/Track.tsx b/app/pages/PageEnigma/comps/Timeline/Track.tsx new file mode 100644 index 0000000000..038b631dd9 --- /dev/null +++ b/app/pages/PageEnigma/comps/Timeline/Track.tsx @@ -0,0 +1,78 @@ +import { TrackClip } from "~/pages/PageEnigma/comps/Timeline/TrackClip"; +import { FontAwesomeIcon } from "@fortawesome/react-fontawesome"; +import { faVolume, faVolumeSlash } from "@fortawesome/pro-solid-svg-icons"; +import { BaseClip } from "~/models/track"; +import { useContext } from "react"; +import { TrackContext } from "~/contexts/TrackContext/TrackContext"; +import { ClipContext } from "~/contexts/ClipContext/ClipContext"; + +interface Props { + clips: BaseClip[]; + title: string; + style: "character" | "audio" | "camera" | "objects"; + type?: "animations" | "positions" | "lipSync"; + toggleMute?: () => void; + muted?: boolean; + updateClip: (options: { id: string; length: number; offset: number }) => void; +} + +export const Track = ({ + clips, + toggleMute, + updateClip, + muted, + title, + style, + type, +}: Props) => { + const { length } = useContext(TrackContext); + const { dragType } = useContext(ClipContext); + const trackType = type ?? style; + + const className = dragType === trackType ? "highlight" : ""; + + return ( +
+
+ {clips.map((clip, index) => ( + 0 ? clips[index - 1].offset + clips[index - 1].length : 0 + } + max={ + index < clips.length - 1 ? clips[index + 1].offset : length * 60 + } + style={style} + updateClip={updateClip} + clip={clip} + /> + ))} +
+ {title} +
+ {!!toggleMute && ( + + )} +
+
+ ); +}; diff --git a/app/pages/PageEnigma/comps/Timeline/utils/useMouseEventsClip.ts b/app/pages/PageEnigma/comps/Timeline/utils/useMouseEventsClip.ts index d37ff7a6b1..883150c09c 100644 --- a/app/pages/PageEnigma/comps/Timeline/utils/useMouseEventsClip.ts +++ b/app/pages/PageEnigma/comps/Timeline/utils/useMouseEventsClip.ts @@ -59,7 +59,7 @@ export const useMouseEventsClip = ( ) { return; } - currLength.current = initLength.current - delta; + currLength.current = initLength.current + delta; } setState({ length: currLength.current, offset: currOffset.current }); }, diff --git a/app/routes/IdealEnigma.tsx b/app/routes/IdealEnigma.tsx index b8131431c2..6fe36a7f25 100644 --- a/app/routes/IdealEnigma.tsx +++ b/app/routes/IdealEnigma.tsx @@ -1,9 +1,9 @@ -import { useState } from 'react'; +import { useState } from "react"; import { PageEnigma } from "../pages/PageEnigma"; -import { Button } from '~/components/Button'; +import { Button } from "~/components/Button"; -export default function IdealEnigma(){ +export default function IdealEnigma() { //const [riddle, setRiddle] = useState(""); //const [submitted, setSubmitted] = useState(false); @@ -16,14 +16,13 @@ export default function IdealEnigma(){ // setSubmitted(state); //} - - return + return ; // if(submitted){ - // return + // return // }else{ // return( //
- //
@@ -38,5 +37,4 @@ export default function IdealEnigma(){ // ); // } - -} \ No newline at end of file +}