From 6fe64db0a658e8eeb770a206abfad50845e2ad22 Mon Sep 17 00:00:00 2001 From: lonkaars Date: Mon, 21 Jun 2021 15:35:21 +0200 Subject: sort-of working with hookstate --- package.json | 4 +- pages/editor.tsx | 208 +++++++++++++++++++++++++++--------------------------- styles/editor.css | 5 +- yarn.lock | 10 +-- 4 files changed, 114 insertions(+), 113 deletions(-) diff --git a/package.json b/package.json index ea90dc2..0b8f060 100644 --- a/package.json +++ b/package.json @@ -8,6 +8,7 @@ "license": "MIT", "private": false, "dependencies": { + "@hookstate/core": "^3.0.8", "@material-ui/core": "^4.11.4", "@material-ui/icons": "^4.11.2", "@material-ui/lab": "^4.0.0-alpha.58", @@ -24,8 +25,7 @@ "react-use-gesture": "^9.1.3", "timecode-boss": "^4.2.3", "ts-json-schema-generator": "^0.92.0", - "uuid": "^8.3.2", - "zustand": "^3.5.1" + "uuid": "^8.3.2" }, "devDependencies": { "typescript": "^4.2.4" diff --git a/pages/editor.tsx b/pages/editor.tsx index c34d3bd..0695d5c 100644 --- a/pages/editor.tsx +++ b/pages/editor.tsx @@ -1,8 +1,8 @@ +import { createState, Downgraded, useState as useHookState } from '@hookstate/core'; import mousetrap from 'mousetrap'; import { CSSProperties, ReactNode, useEffect, useRef, useState } from 'react'; import { animated, useSpring } from 'react-spring'; import { useDrag } from 'react-use-gesture'; -import create from 'zustand'; import { FullScreenControlsRoundedIcon, @@ -47,26 +47,44 @@ var keyframeInAnimations: { [key: string]: { x: number; y: number; }; } = {}; var slideAPIs: { [key: string]: any; }[] = []; var player = new TimedVideoPlayer(); -var useWorkingTimeline = create((set, get) => ({ - timeline: [], - setTimeline: (newTimeline: anySlide[]) => set(() => ({ timeline: newTimeline })), - refreshLiveTimeline: () => { - player.timeline.slides = Array(...((get() as any).timeline)); - player.timeline.slides = player.timeline.slides.filter(slide => slide != null); - player.timeline.slides.sort((a, b) => a.frame - b.frame); - player.timeline.slides[-1] = { // TODO: dry - id: '00000000-0000-0000-0000-000000000000', - frame: 0, - type: 'default', - clickThroughBehaviour: 'ImmediatelySkip', - }; - }, -})); -var getTimelineZoom = create(set => ({ - zoom: 0.687077725615, - setZoom: (newValue: number) => set(() => ({ zoom: newValue })), -})); +interface project { + timeline: { + playing: boolean; + frame: number; + labels: ReactNode[]; + zoom: number; + workingTimeline: anySlide[]; + tool: string; + }; + update: { + refreshLiveTimeline: () => void; + }; +} + +var project = createState({ + timeline: { + playing: false, + frame: 0, + labels: [], + zoom: 0.687077725615, + workingTimeline: [], + tool: 'cursor', + }, + update: { + refreshLiveTimeline: () => { + player.timeline.slides = Array(...(project.timeline.workingTimeline.value)); + player.timeline.slides = player.timeline.slides.filter(slide => slide != null); + player.timeline.slides.sort((a, b) => a.frame - b.frame); + player.timeline.slides[-1] = { // TODO: dry + id: '00000000-0000-0000-0000-000000000000', + frame: 0, + type: 'default', + clickThroughBehaviour: 'ImmediatelySkip', + }; + }, + }, +}); var zoomToPx = (zoom: number) => (12 - 0.5) * zoom ** (1 / 0.4) + 0.5; @@ -77,21 +95,6 @@ function getFrameAtOffset(offset: number, timelineZoom: number) { return frame; } -var useTimelineLabels = create(set => ({ - labels: [], - setLabels: (newLabels: Array) => set(() => ({ labels: newLabels })), -})); - -var useFrame = create(set => ({ - currentFrame: 0, - setFrame: (newFrame: number) => set(() => ({ currentFrame: newFrame })), -})); - -var usePlaying = create(set => ({ - playing: false, - setPlaying: (playing: boolean) => set(() => ({ playing })), -})); - function calculateSelectionOffsets(left: slideTypes, right: slideTypes) { var offsets = { default: { left: -6, right: 6 }, @@ -110,14 +113,14 @@ function calculateSelectionOffsets(left: slideTypes, right: slideTypes) { function TimelineKeyframe(props: { slide: slide; }) { - var workingTimeline = useWorkingTimeline((st: any) => st.timeline); - var setWorkingTimeline = useWorkingTimeline((st: any) => st.setTimeline); - var updateTimeline = useWorkingTimeline((st: any) => st.refreshLiveTimeline); + var workingTimeline = useHookState(project.timeline.workingTimeline); + var setWorkingTimeline = useHookState(project.timeline.workingTimeline).set; + var updateTimeline = useHookState(project.update.refreshLiveTimeline).value; function modifySlide(newProps: Partial) { - var slide = workingTimeline.find((s: anySlide) => s.id == props.slide.id); + var slide = workingTimeline.value.find(s => s.id == props.slide.id); slide = Object.assign(slide, newProps); - setWorkingTimeline(workingTimeline); + setWorkingTimeline(workingTimeline.value); } var dragRef = useRef(null); @@ -146,13 +149,13 @@ function TimelineKeyframe(props: { delete keyframeInAnimations[props.slide.id]; }, []); - var timelineZoom = getTimelineZoom((st: any) => st.zoom); + var timelineZoom = useHookState(project.timeline.zoom); // drag keyframe var [startOffset, setStartOffset] = useState(0); var [endOffset, setEndOffset] = useState(0); useDrag(({ xy: [x, _y], first }) => { - var frame = Math.max(0, Math.round(getFrameAtOffset(x - 240, timelineZoom)) - 1); + var frame = Math.max(0, Math.round(getFrameAtOffset(x - 240, timelineZoom.value)) - 1); if (props.slide.type == 'loop') { if (first) { @@ -180,7 +183,7 @@ function TimelineKeyframe(props: { if (props.slide.type == 'loop') { // loop start useDrag(({ xy: [x, _y] }) => { - var frame = Math.max(0, Math.round(getFrameAtOffset(x - 240, timelineZoom)) - 1); + var frame = Math.max(0, Math.round(getFrameAtOffset(x - 240, timelineZoom.value)) - 1); api.start({ begin: frame }); @@ -189,7 +192,7 @@ function TimelineKeyframe(props: { // loop end useDrag(({ xy: [x, _y] }) => { - var frame = Math.max(0, Math.round(getFrameAtOffset(x - 240, timelineZoom)) - 1); + var frame = Math.max(0, Math.round(getFrameAtOffset(x - 240, timelineZoom.value)) - 1); api.start({ frame }); @@ -233,30 +236,25 @@ function TimelineKeyframe(props: { ; } -function TimelineEditor(props: { - player: TimedVideoPlayer; - selectedTool: string; -}) { - var timelineZoom = getTimelineZoom((st: any) => st.zoom); +function TimelineEditor() { + var timelineZoom = useHookState(project.timeline.zoom); + var timelineLabels = useHookState(project.timeline.labels); + var workingTimeline = useHookState(project.timeline.workingTimeline); - var timelineLabels = useTimelineLabels((st: any) => st.labels); - var setTimelineLabels = useTimelineLabels((st: any) => st.setLabels); + var refreshWorkingTimline = useHookState(project.update.refreshLiveTimeline).value; + var setFrame = useHookState(project.timeline.frame).set; - var workingTimeline = useWorkingTimeline((st: any) => st.timeline); - var setWorkingTimeline = useWorkingTimeline((st: any) => st.setTimeline); - var refreshWorkingTimline = useWorkingTimeline((st: any) => st.refreshLiveTimeline); - - var setFrame = useFrame((st: any) => st.setFrame); + var tool = useHookState(project.timeline.tool); useEffect(() => { - props.player.addEventListener('TimedVideoPlayerOnFrame', (event: CustomEvent) => { + player.addEventListener('TimedVideoPlayerOnFrame', (event: CustomEvent) => { setFrame(event.detail); scrubberSpring.start({ frame: event.detail }); }); }, []); useEffect(() => { - props.player.addEventListener('TimedVideoPlayerSlide', (event: CustomEvent) => { + player.addEventListener('TimedVideoPlayerSlide', (event: CustomEvent) => { document.querySelectorAll('.keyframes .frame').forEach(el => { el.classList.remove('current'); if (event.detail && el.id == 'slide-' + (event.detail as slide).id) { @@ -320,7 +318,7 @@ function TimelineEditor(props: { left: Math.round(rect[0] + frameWidth / 2), top: rect[1], }} - children={props.player.frameToTimestampString(frame - 1)} + children={player.frameToTimestampString(frame - 1)} />, ); } @@ -330,7 +328,7 @@ function TimelineEditor(props: { a++; } - setTimelineLabels(labels); + project.timeline.labels.set(labels); requestAnimationFrame(draw); } @@ -354,12 +352,11 @@ function TimelineEditor(props: { }), ); useDrag(({ xy: [x, _y] }) => { - var frame = Math.max(0, Math.round(getFrameAtOffset(x - 240, timelineZoom)) - 1); + var frame = Math.max(0, Math.round(getFrameAtOffset(x - 240, timelineZoom.value)) - 1); setFrame(frame); scrubberSpring.start({ frame }); - if (props.player.player) { - var player = props.player.player; - player.currentTime = props.player.frameToTimestamp(frame + 1); + if (player.player) { + player.player.currentTime = player.frameToTimestamp(frame + 1); } }, { domTarget: scrubberDragRef, eventOptions: { passive: false } }); @@ -402,13 +399,13 @@ function TimelineEditor(props: { useDrag(({ movement: [x, _y], last }) => { if (!selectionPlaced) return; if (selection.length < 1) return; - var frameOffset = Math.round(x / zoomToPx(timelineZoom)); + var frameOffset = Math.round(x / zoomToPx(timelineZoom.value)); selection.forEach((slide: anySlide) => { var api = slideAPIs[slide.id]; switch (slide.type as slideTypes | 'loopBegin') { case 'loopBegin': { if (!api) break; - var loop = workingTimeline.find((s: anySlide) => s.id == slide.id) as loopSlide; + var loop = workingTimeline.value.find(s => s.id == slide.id) as loopSlide; var begin = loop.beginFrame + frameOffset; api.start({ begin }); @@ -425,7 +422,8 @@ function TimelineEditor(props: { api.start({ frame }); if (last) { - workingTimeline.find((s: anySlide) => s.id == slide.id).frame = frame; + workingTimeline.value.find(s => s.id == slide.id).frame = frame; + project.timeline.workingTimeline.set(workingTimeline.value); refreshWorkingTimline(); } } @@ -436,7 +434,7 @@ function TimelineEditor(props: { }); }, { domTarget: selectionRef, eventOptions: { passive: false } }); useDrag(({ xy: [x, y], initial: [bx, by], first, last, movement: [ox, oy] }) => { - if (props.selectedTool != 'cursor') return; + if (tool.value != 'cursor') return; var minDistance = 5; // minimal drag distance in pixels to register selection var distanceTraveled = Math.sqrt(ox ** 2 + oy ** 2); @@ -467,7 +465,7 @@ function TimelineEditor(props: { var x2 = x1 + Math.abs(sx); var y2 = y1 + Math.abs(sy); - var zoom = zoomToPx(timelineZoom); + var zoom = zoomToPx(timelineZoom.value); var frameWidth = Math.abs(sx) / zoom; var startingFrame = x1 / zoom; @@ -528,9 +526,10 @@ function TimelineEditor(props: { selection.forEach((slide: anySlide) => { if (!slideTypes.includes(slide.type)) return; - var index = workingTimeline.findIndex((s: anySlide) => s?.id == slide.id); + var index = workingTimeline.value.findIndex(s => s?.id == slide.id); if (index == -1) return; - delete workingTimeline[index]; + delete workingTimeline.value[index]; + // !!! AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA }); refreshWorkingTimline(); @@ -552,10 +551,10 @@ function TimelineEditor(props: { // place new keyframe var offset = -4; // keyframe offset var x = event.clientX - 240 + offset; - var frame = getFrameAtOffset(x, timelineZoom) - 0.5; - var slide = new toolToSlide[props.selectedTool](Math.round(frame)); - workingTimeline.push(slide); - setWorkingTimeline(workingTimeline); + var frame = getFrameAtOffset(x, timelineZoom.value) - 0.5; + var slide = new toolToSlide[tool.value](Math.round(frame)); + workingTimeline.value.push(slide); + workingTimeline.set(workingTimeline.value); keyframeInAnimations[slide.id] = { x: frame, y: event.clientY - window.innerHeight + 210, @@ -563,9 +562,9 @@ function TimelineEditor(props: { refreshWorkingTimline(); }} /> -
+
-
+
- {workingTimeline.map((slide: anySlide) => )} + {workingTimeline.value.map(slide => )}
-
+
} + children={} />
; @@ -628,8 +627,10 @@ function TimelineEditor(props: { // https://material.io/design/navigation/navigation-transitions.html#peer-transitions function DefaultSettings() { - var setPlaying = usePlaying((st: any) => st.setPlaying); - var setWorkingTimeline = useWorkingTimeline((st: any) => st.setTimeline); + var setPlaying = useHookState(project.timeline.playing).set; + + var setWorkingTimeline = useHookState(project.timeline.workingTimeline).set; + var refreshLiveTimeline = useHookState(project.update.refreshLiveTimeline).value; var [nextSlideKeybinds, setNextSlideKeybinds] = useState(['Space', 'n', 'Enter']); var [previousSlideKeybinds, setPreviousSlideKeybinds] = useState(['Backspace', 'p']); @@ -649,6 +650,7 @@ function DefaultSettings() {