From 3e210b979e6b221c74d38a217c66dca75e967647 Mon Sep 17 00:00:00 2001 From: mrjvs Date: Sun, 8 Oct 2023 17:48:48 +0200 Subject: [PATCH] volume controls Co-authored-by: Jip Frijlink --- src/components/player/Player.tsx | 1 + .../player/atoms/LeftSideControls.tsx | 3 + src/components/player/atoms/Volume.tsx | 82 +++++++++++++++++++ src/components/player/atoms/index.ts | 1 + .../player/base/LeftSideControls.tsx | 25 ++++++ .../player/hooks/useInitializePlayer.ts | 19 +++++ src/components/player/hooks/usePlayer.ts | 3 + src/components/player/hooks/useVolume.ts | 34 ++++++++ src/components/player/internals/Button.tsx | 7 +- src/pages/PlayerView.tsx | 5 +- src/stores/player/slices/interface.ts | 6 ++ tailwind.config.js | 4 + 12 files changed, 186 insertions(+), 4 deletions(-) create mode 100644 src/components/player/atoms/LeftSideControls.tsx create mode 100644 src/components/player/atoms/Volume.tsx create mode 100644 src/components/player/base/LeftSideControls.tsx create mode 100644 src/components/player/hooks/useInitializePlayer.ts create mode 100644 src/components/player/hooks/useVolume.ts diff --git a/src/components/player/Player.tsx b/src/components/player/Player.tsx index c9aad803..db1b2b34 100644 --- a/src/components/player/Player.tsx +++ b/src/components/player/Player.tsx @@ -5,4 +5,5 @@ export * from "./base/CenterControls"; export * from "./base/BottomControls"; export * from "./base/BlackOverlay"; export * from "./base/BackLink"; +export * from "./base/LeftSideControls"; export * from "./internals/BookmarkButton"; diff --git a/src/components/player/atoms/LeftSideControls.tsx b/src/components/player/atoms/LeftSideControls.tsx new file mode 100644 index 00000000..8d49f1d3 --- /dev/null +++ b/src/components/player/atoms/LeftSideControls.tsx @@ -0,0 +1,3 @@ +export function LeftSideControls(props: {children: React.ReactNode}) { + +} diff --git a/src/components/player/atoms/Volume.tsx b/src/components/player/atoms/Volume.tsx new file mode 100644 index 00000000..75da58c5 --- /dev/null +++ b/src/components/player/atoms/Volume.tsx @@ -0,0 +1,82 @@ +import { useCallback, useRef, useState } from "react"; + +import { Icon, Icons } from "@/components/Icon"; +import { + makePercentage, + makePercentageString, + useProgressBar, +} from "@/hooks/useProgressBar"; +import { usePlayerStore } from "@/stores/player/store"; +import { canChangeVolume } from "@/utils/detectFeatures"; + +import { useVolume } from "../hooks/useVolume"; + +interface Props { + className?: string; +} + +export function Volume(props: Props) { + const ref = useRef(null); + const setHovering = usePlayerStore((s) => s.setHoveringLeftControls); + const hovering = usePlayerStore((s) => s.interface.leftControlHovering); + const volume = usePlayerStore((s) => s.mediaPlaying.volume); + const { setVolume, toggleMute } = useVolume(); + + const commitVolume = useCallback( + (percentage) => { + setVolume(percentage); + }, + [setVolume] + ); + + const { dragging, dragPercentage, dragMouseDown } = useProgressBar( + ref, + commitVolume, + true + ); + + const handleClick = useCallback(() => { + toggleMute(); + }, [toggleMute]); + + const handleMouseEnter = useCallback(async () => { + if (await canChangeVolume()) setHovering(true); + }, [setHovering]); + + let percentage = makePercentage(volume * 100); + if (dragging) percentage = makePercentage(dragPercentage); + const percentageString = makePercentageString(percentage); + + return ( +
+
+
+ 0 ? Icons.VOLUME : Icons.VOLUME_X} /> +
+
+
+
+
+
+
+
+
+
+
+
+ ); +} diff --git a/src/components/player/atoms/index.ts b/src/components/player/atoms/index.ts index 0ac37749..80d87ffa 100644 --- a/src/components/player/atoms/index.ts +++ b/src/components/player/atoms/index.ts @@ -5,3 +5,4 @@ export * from "./Skips"; export * from "./Time"; export * from "./LoadingSpinner"; export * from "./AutoPlayStart"; +export * from "./Volume"; diff --git a/src/components/player/base/LeftSideControls.tsx b/src/components/player/base/LeftSideControls.tsx new file mode 100644 index 00000000..38401f8e --- /dev/null +++ b/src/components/player/base/LeftSideControls.tsx @@ -0,0 +1,25 @@ +import { useCallback, useEffect } from "react"; + +import { usePlayerStore } from "@/stores/player/store"; + +export function LeftSideControls(props: { children: React.ReactNode }) { + const setHoveringLeftControls = usePlayerStore( + (s) => s.setHoveringLeftControls + ); + + const mouseLeave = useCallback(() => { + setHoveringLeftControls(false); + }, [setHoveringLeftControls]); + + useEffect(() => { + return () => { + setHoveringLeftControls(false); + }; + }, [setHoveringLeftControls]); + + return ( +
+ {props.children} +
+ ); +} diff --git a/src/components/player/hooks/useInitializePlayer.ts b/src/components/player/hooks/useInitializePlayer.ts new file mode 100644 index 00000000..dba0fe4b --- /dev/null +++ b/src/components/player/hooks/useInitializePlayer.ts @@ -0,0 +1,19 @@ +import { useCallback } from "react"; + +import { getStoredVolume } from "@/_oldvideo/components/hooks/volumeStore"; +import { usePlayerStore } from "@/stores/player/store"; + +// TODO use new stored volume + +export function useInitializePlayer() { + const display = usePlayerStore((s) => s.display); + + const init = useCallback(() => { + const storedVolume = getStoredVolume(); + display?.setVolume(storedVolume); + }, [display]); + + return { + init, + }; +} diff --git a/src/components/player/hooks/usePlayer.ts b/src/components/player/hooks/usePlayer.ts index add6b988..ea248fe5 100644 --- a/src/components/player/hooks/usePlayer.ts +++ b/src/components/player/hooks/usePlayer.ts @@ -1,4 +1,5 @@ import { MWStreamType } from "@/backend/helpers/streams"; +import { useInitializePlayer } from "@/components/player/hooks/useInitializePlayer"; import { playerStatus } from "@/stores/player/slices/source"; import { usePlayerStore } from "@/stores/player/store"; @@ -11,12 +12,14 @@ export function usePlayer() { const setStatus = usePlayerStore((s) => s.setStatus); const status = usePlayerStore((s) => s.status); const display = usePlayerStore((s) => s.display); + const { init } = useInitializePlayer(); return { status, playMedia(source: Source) { display?.load(source); setStatus(playerStatus.PLAYING); + init(); }, setScrapeStatus() { setStatus(playerStatus.SCRAPING); diff --git a/src/components/player/hooks/useVolume.ts b/src/components/player/hooks/useVolume.ts new file mode 100644 index 00000000..35242a43 --- /dev/null +++ b/src/components/player/hooks/useVolume.ts @@ -0,0 +1,34 @@ +import { + getStoredVolume, + setStoredVolume, +} from "@/_oldvideo/components/hooks/volumeStore"; +import { usePlayerStore } from "@/stores/player/store"; + +// TODO use new stored volume + +export function useVolume() { + const volume = usePlayerStore((s) => s.mediaPlaying.volume); + const display = usePlayerStore((s) => s.display); + + const toggleVolume = (_isKeyboardEvent = false) => { + // TODO use keyboard event + if (volume > 0) { + setStoredVolume(volume); + display?.setVolume(0); + } else { + const storedVolume = getStoredVolume(); + if (storedVolume > 0) display?.setVolume(storedVolume); + else display?.setVolume(1); + } + }; + + return { + toggleMute() { + toggleVolume(); + }, + setVolume(vol: number) { + setStoredVolume(vol); + display?.setVolume(vol); + }, + }; +} diff --git a/src/components/player/internals/Button.tsx b/src/components/player/internals/Button.tsx index 4a9412a4..e65a3b1e 100644 --- a/src/components/player/internals/Button.tsx +++ b/src/components/player/internals/Button.tsx @@ -2,17 +2,20 @@ import { Icon, Icons } from "@/components/Icon"; export function VideoPlayerButton(props: { children?: React.ReactNode; - onClick: () => void; + onClick?: () => void; icon?: Icons; iconSizeClass?: string; className?: string; + activeClass?: string; }) { return (