volume controls

Co-authored-by: Jip Frijlink <JipFr@users.noreply.github.com>
This commit is contained in:
mrjvs 2023-10-08 17:48:48 +02:00
parent 4289b96039
commit 3e210b979e
12 changed files with 186 additions and 4 deletions

View File

@ -5,4 +5,5 @@ export * from "./base/CenterControls";
export * from "./base/BottomControls"; export * from "./base/BottomControls";
export * from "./base/BlackOverlay"; export * from "./base/BlackOverlay";
export * from "./base/BackLink"; export * from "./base/BackLink";
export * from "./base/LeftSideControls";
export * from "./internals/BookmarkButton"; export * from "./internals/BookmarkButton";

View File

@ -0,0 +1,3 @@
export function LeftSideControls(props: {children: React.ReactNode}) {
}

View File

@ -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<HTMLDivElement>(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 (
<div className={props.className} onMouseEnter={handleMouseEnter}>
<div className="pointer-events-auto flex cursor-pointer items-center py-0">
<div className="px-4 text-2xl text-white" onClick={handleClick}>
<Icon icon={percentage > 0 ? Icons.VOLUME : Icons.VOLUME_X} />
</div>
<div
className={`linear -ml-2 w-0 overflow-hidden transition-[width,opacity] duration-300 ${
hovering || dragging ? "!w-24 opacity-100" : "w-4 opacity-0"
}`}
>
<div
ref={ref}
className="flex h-10 w-20 items-center px-2"
onMouseDown={dragMouseDown}
onTouchStart={dragMouseDown}
>
<div className="relative h-1 flex-1 rounded-full bg-gray-500 bg-opacity-50">
<div
className="absolute inset-y-0 left-0 flex items-center justify-end rounded-full bg-video-audio-set"
style={{
width: percentageString,
}}
>
<div className="absolute h-3 w-3 translate-x-1/2 rounded-full bg-white" />
</div>
</div>
</div>
</div>
</div>
</div>
);
}

View File

@ -5,3 +5,4 @@ export * from "./Skips";
export * from "./Time"; export * from "./Time";
export * from "./LoadingSpinner"; export * from "./LoadingSpinner";
export * from "./AutoPlayStart"; export * from "./AutoPlayStart";
export * from "./Volume";

View File

@ -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 (
<div className="flex space-x-3 items-center" onMouseLeave={mouseLeave}>
{props.children}
</div>
);
}

View File

@ -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,
};
}

View File

@ -1,4 +1,5 @@
import { MWStreamType } from "@/backend/helpers/streams"; import { MWStreamType } from "@/backend/helpers/streams";
import { useInitializePlayer } from "@/components/player/hooks/useInitializePlayer";
import { playerStatus } from "@/stores/player/slices/source"; import { playerStatus } from "@/stores/player/slices/source";
import { usePlayerStore } from "@/stores/player/store"; import { usePlayerStore } from "@/stores/player/store";
@ -11,12 +12,14 @@ export function usePlayer() {
const setStatus = usePlayerStore((s) => s.setStatus); const setStatus = usePlayerStore((s) => s.setStatus);
const status = usePlayerStore((s) => s.status); const status = usePlayerStore((s) => s.status);
const display = usePlayerStore((s) => s.display); const display = usePlayerStore((s) => s.display);
const { init } = useInitializePlayer();
return { return {
status, status,
playMedia(source: Source) { playMedia(source: Source) {
display?.load(source); display?.load(source);
setStatus(playerStatus.PLAYING); setStatus(playerStatus.PLAYING);
init();
}, },
setScrapeStatus() { setScrapeStatus() {
setStatus(playerStatus.SCRAPING); setStatus(playerStatus.SCRAPING);

View File

@ -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);
},
};
}

View File

@ -2,17 +2,20 @@ import { Icon, Icons } from "@/components/Icon";
export function VideoPlayerButton(props: { export function VideoPlayerButton(props: {
children?: React.ReactNode; children?: React.ReactNode;
onClick: () => void; onClick?: () => void;
icon?: Icons; icon?: Icons;
iconSizeClass?: string; iconSizeClass?: string;
className?: string; className?: string;
activeClass?: string;
}) { }) {
return ( return (
<button <button
type="button" type="button"
onClick={props.onClick} onClick={props.onClick}
className={[ className={[
"p-2 rounded-full hover:bg-video-buttonBackground hover:bg-opacity-75 transition-transform duration-100 active:scale-110 active:bg-opacity-100 active:text-white", "p-2 rounded-full hover:bg-video-buttonBackground hover:bg-opacity-75 transition-transform duration-100",
props.activeClass ??
"active:scale-110 active:bg-opacity-100 active:text-white",
props.className ?? "", props.className ?? "",
].join(" ")} ].join(" ")}
> >

View File

@ -64,12 +64,13 @@ export function PlayerView() {
<Player.BottomControls show={desktopControlsVisible}> <Player.BottomControls show={desktopControlsVisible}>
<Player.ProgressBar /> <Player.ProgressBar />
<div className="flex justify-between"> <div className="flex justify-between">
<div className="flex space-x-3 items-center"> <Player.LeftSideControls>
<Player.Pause /> <Player.Pause />
<Player.SkipBackward /> <Player.SkipBackward />
<Player.SkipForward /> <Player.SkipForward />
<Player.Volume />
<Player.Time /> <Player.Time />
</div> </Player.LeftSideControls>
<div> <div>
<Player.Fullscreen /> <Player.Fullscreen />
</div> </div>

View File

@ -26,6 +26,7 @@ export interface InterfaceSlice {
updateInterfaceHovering(newState: PlayerHoverState): void; updateInterfaceHovering(newState: PlayerHoverState): void;
setSeeking(seeking: boolean): void; setSeeking(seeking: boolean): void;
setTimeFormat(format: VideoPlayerTimeFormat): void; setTimeFormat(format: VideoPlayerTimeFormat): void;
setHoveringLeftControls(state: boolean): void;
} }
export const createInterfaceSlice: MakeSlice<InterfaceSlice> = (set, get) => ({ export const createInterfaceSlice: MakeSlice<InterfaceSlice> = (set, get) => ({
@ -56,4 +57,9 @@ export const createInterfaceSlice: MakeSlice<InterfaceSlice> = (set, get) => ({
s.interface.isSeeking = seeking; s.interface.isSeeking = seeking;
}); });
}, },
setHoveringLeftControls(state) {
set((s) => {
s.interface.leftControlHovering = state;
});
},
}); });

View File

@ -119,6 +119,10 @@ module.exports = {
background: "#8787A8", background: "#8787A8",
preloaded: "#8787A8", preloaded: "#8787A8",
watched: "#A75FC9" watched: "#A75FC9"
},
audio: {
set: "#A75FC9"
} }
} }
} }