Skip to content

Commit

Permalink
Improved bookmarks
Browse files Browse the repository at this point in the history
  • Loading branch information
Rodeoclash committed Apr 18, 2022
1 parent 83cce70 commit 90d8e4b
Show file tree
Hide file tree
Showing 9 changed files with 404 additions and 170 deletions.
61 changes: 49 additions & 12 deletions src/components/Drawing/Drawing.tsx
Original file line number Diff line number Diff line change
@@ -1,30 +1,55 @@
import {
useCallback,
useRef,
useLayoutEffect,
useState,
useEffect,
} from "react";
import { Tldraw } from "@tldraw/tldraw";
import useStore from "../../services/store";

import { useRef, useLayoutEffect, useState, useEffect } from "react";
import { Tldraw, TldrawApp } from "@tldraw/tldraw";

import { Box } from "@chakra-ui/react";

import type { Video } from "../../services/models/Video";
import type { VideoBookmark } from "../../services/models/VideoBookmark";

type Props = {
video: Video;
videoBookmark: VideoBookmark | undefined;
fullscreen: boolean;
showUI: boolean;
};

export default function Drawing({ fullscreen }: Props) {
export default function Drawing({
fullscreen,
video,
videoBookmark,
showUI,
}: Props) {
const tlDrawRef = useRef(null);
const outerRef = useRef(null);
const [originalWidth, setOriginalWidth] = useState(null);
const [scale, setScale] = useState(null);

const handleMount = useCallback((app) => {
const setVideoBookmarkDrawing = useStore(
(state) => state.setVideoBookmarkDrawing
);

function handleMount(app: TldrawApp) {
tlDrawRef.current = app;
}, []);

if (videoBookmark.drawing) {
tlDrawRef.current.loadDocument(
JSON.parse(JSON.stringify(videoBookmark.drawing)) // we need to load a copy of the document
);

tlDrawRef.current.selectNone();
}
}

function handlePersist(app: TldrawApp) {
if (videoBookmark) {
setVideoBookmarkDrawing(video, videoBookmark, app.document);
}
}

useEffect(() => {
if (scale === null) {
if (scale === null || tlDrawRef.current === null) {
return;
}

Expand Down Expand Up @@ -59,12 +84,24 @@ export default function Drawing({ fullscreen }: Props) {
bottom={"0"}
ref={outerRef}
>
{!showUI && (
<Box
position="absolute"
top={"0"}
left={"0"}
right={"0"}
bottom={"0"}
zIndex={2}
/>
)}
<Tldraw
onMount={handleMount}
onPersist={handlePersist}
showMenu={false}
showPages={false}
showStyles={fullscreen === false}
showZoom={false}
showUI={showUI}
/>
</Box>
);
Expand Down
4 changes: 2 additions & 2 deletions src/components/GlobalTimeControl/GlobalTimeControl.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,7 @@ import {
SliderTrack,
} from "@chakra-ui/react";

import VideoBookmark from "../VideoBookmark/VideoBookmark";
import VideoBookmarkTimeline from "../VideoBookmarkTimeline/VideoBookmarkTimeline";

import type { Video } from "../../services/models/Video";

Expand Down Expand Up @@ -171,7 +171,7 @@ export default function GlobalTimeControl({ video }: Props) {
rounded={"full"}
zIndex={"1"}
>
<VideoBookmark video={video} bookmark={bookmark} />
<VideoBookmarkTimeline video={video} bookmark={bookmark} />
</Flex>
);
});
Expand Down
147 changes: 97 additions & 50 deletions src/components/VideoBookmark/VideoBookmark.tsx
Original file line number Diff line number Diff line change
@@ -1,81 +1,128 @@
import { SyntheticEvent, useState } from "react";
import { css } from "@emotion/react";

import useStore from "../../services/store";

import {
Box,
Button,
ButtonGroup,
Popover,
PopoverArrow,
PopoverBody,
PopoverCloseButton,
PopoverContent,
PopoverFooter,
PopoverHeader,
PopoverTrigger,
Text,
} from "@chakra-ui/react";

import { Bookmark as BookmarkIcon } from "tabler-icons-react";
import { Box, Flex, Button, ButtonGroup, Text } from "@chakra-ui/react";
import Draggable from "react-draggable"; // The default

import VideoBookmarkForm from "../VideoBookmarkForm/VideoBookmarkForm";

import type { Video } from "../../services/models/Video";
import type { VideoBookmark } from "../../services/models/VideoBookmark";

type Props = {
bookmark: VideoBookmark;
video: Video;
bookmark: VideoBookmark;
};

const dragHandleStyles = css`
cursor: move;
height: 2rem;
background: repeating-linear-gradient(
45deg,
rgba(255, 255, 255, 0.05),
rgba(255, 255, 255, 0.05) 10px,
rgba(255, 255, 255, 0.1) 10px,
rgba(255, 255, 255, 0.1) 20px
);
`;

export default function VideoBookmark({ video, bookmark }: Props) {
const [isOpen, setIsOpen] = useState(false);
const playing = useStore((state) => state.playing);
const editingBookmark = useStore((state) => state.editingBookmark);

const deleteVideoBookmark = useStore((state) => state.deleteVideoBookmark);
const setCurrentTime = useStore((state) => state.setCurrentTime);
const startEditingBookmark = useStore((state) => state.startEditingBookmark);
const stopEditingBookmark = useStore((state) => state.stopEditingBookmark);

function handleOpen(event: SyntheticEvent) {
setIsOpen(true);
const setVideoBookmarkCoords = useStore(
(state) => state.setVideoBookmarkCoords
);

const setVideoBookmarkContent = useStore(
(state) => state.setVideoBookmarkContent
);

if (playing === true) {
return null;
}

function handleClose() {
setIsOpen(false);
if (!bookmark) {
return null;
}

function handleDelete() {
deleteVideoBookmark(video, bookmark);
stopEditingBookmark();
}

function handleEdit() {
startEditingBookmark();
}

function handleDone() {
stopEditingBookmark();
}

function handleGoto() {
setCurrentTime(bookmark.time);
setIsOpen(false);
function handleDragStop(e: MouseEvent, data: { x: number; y: number }) {
setVideoBookmarkCoords(video, bookmark, { x: data.x, y: data.y });
}

function handleContentUpdate(content: string) {
setVideoBookmarkContent(video, bookmark, content);
}

const position = bookmark.position
? { x: bookmark.position.x, y: bookmark.position.y }
: null;

const renderedContent = editingBookmark ? (
<VideoBookmarkForm onChange={handleContentUpdate} bookmark={bookmark} />
) : (
<Text>{bookmark.content}</Text>
);

const renderedPositiveAction = editingBookmark ? (
<Button onClick={handleDone}>Done</Button>
) : (
<Button onClick={handleEdit}>Edit</Button>
);

return (
<Box>
<Popover isOpen={isOpen} onClose={handleClose} placement="top">
<PopoverTrigger>
<Box onClick={handleOpen} cursor="pointer">
<BookmarkIcon />
<Flex
position={"absolute"}
left={0}
top={0}
right={0}
bottom={0}
align={"flex-end"}
justify={"flex-end"}
padding="4"
pointerEvents={"none"}
zIndex={3}
>
<Draggable
key={bookmark.id}
onStop={handleDragStop}
bounds={"parent"}
handle="#dragHandle"
position={position}
>
<Box pointerEvents={"all"} background={"blackAlpha.900"} width={"md"}>
<Box id="dragHandle" css={dragHandleStyles} />
<Box padding={"4"} borderBottom="1px" borderColor={"whiteAlpha.500"}>
{renderedContent}
</Box>
</PopoverTrigger>
<PopoverContent>
<PopoverArrow />
<PopoverCloseButton />
<PopoverHeader>&nbsp;</PopoverHeader>
<PopoverBody>
<Text fontSize={"sm"}>{bookmark.content}</Text>
</PopoverBody>
<PopoverFooter>
<ButtonGroup size="sm" display={"flex"} justifyContent={"right"}>
<Button colorScheme={"red"} onClick={handleDelete}>
<Flex padding={"4"} justifyContent="flex-end">
<ButtonGroup size="sm">
<Button onClick={handleDelete} colorScheme="red">
Delete
</Button>
<Button colorScheme={"green"} onClick={handleGoto}>
Goto
</Button>
{renderedPositiveAction}
</ButtonGroup>
</PopoverFooter>
</PopoverContent>
</Popover>
</Box>
</Flex>
</Box>
</Draggable>
</Flex>
);
}
Loading

0 comments on commit 90d8e4b

Please sign in to comment.