mirror of
https://github.com/p-stream/p-stream.git
synced 2026-04-20 22:42:09 +00:00
Init skip button
This commit is contained in:
parent
1737b1c2b2
commit
5f7650dfdd
2 changed files with 97 additions and 18 deletions
92
src/components/player/atoms/SkipIntroButton.tsx
Normal file
92
src/components/player/atoms/SkipIntroButton.tsx
Normal file
|
|
@ -0,0 +1,92 @@
|
||||||
|
import classNames from "classnames";
|
||||||
|
import { useCallback } from "react";
|
||||||
|
|
||||||
|
import { Icon, Icons } from "@/components/Icon";
|
||||||
|
import { Transition } from "@/components/utils/Transition";
|
||||||
|
import { usePlayerStore } from "@/stores/player/store";
|
||||||
|
|
||||||
|
function shouldShowSkipButton(
|
||||||
|
currentTime: number,
|
||||||
|
skipTime?: number | null,
|
||||||
|
): "always" | "hover" | "none" {
|
||||||
|
if (typeof skipTime !== "number") return "none";
|
||||||
|
|
||||||
|
// Show button from beginning until the skip point
|
||||||
|
if (currentTime >= 0 && currentTime < skipTime) {
|
||||||
|
return "always";
|
||||||
|
}
|
||||||
|
|
||||||
|
return "none";
|
||||||
|
}
|
||||||
|
|
||||||
|
function Button(props: {
|
||||||
|
className: string;
|
||||||
|
onClick?: () => void;
|
||||||
|
children: React.ReactNode;
|
||||||
|
}) {
|
||||||
|
return (
|
||||||
|
<button
|
||||||
|
className={classNames(
|
||||||
|
"font-bold rounded h-10 w-40 scale-95 hover:scale-100 transition-all duration-200",
|
||||||
|
props.className,
|
||||||
|
)}
|
||||||
|
type="button"
|
||||||
|
onClick={props.onClick}
|
||||||
|
>
|
||||||
|
{props.children}
|
||||||
|
</button>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
export function SkipIntroButton(props: {
|
||||||
|
controlsShowing: boolean;
|
||||||
|
skipTime?: number | null;
|
||||||
|
}) {
|
||||||
|
const time = usePlayerStore((s) => s.progress.time);
|
||||||
|
const status = usePlayerStore((s) => s.status);
|
||||||
|
const display = usePlayerStore((s) => s.display);
|
||||||
|
|
||||||
|
const showingState = shouldShowSkipButton(time, props.skipTime);
|
||||||
|
|
||||||
|
let show = false;
|
||||||
|
if (showingState === "always") show = true;
|
||||||
|
else if (showingState === "hover" && props.controlsShowing) show = true;
|
||||||
|
if (status !== "playing") show = false;
|
||||||
|
|
||||||
|
const animation = showingState === "hover" ? "slide-up" : "fade";
|
||||||
|
let bottom = "bottom-[calc(6rem+env(safe-area-inset-bottom))]";
|
||||||
|
if (showingState === "always") {
|
||||||
|
bottom = props.controlsShowing
|
||||||
|
? bottom
|
||||||
|
: "bottom-[calc(3rem+env(safe-area-inset-bottom))]";
|
||||||
|
}
|
||||||
|
|
||||||
|
const handleSkip = useCallback(() => {
|
||||||
|
if (typeof props.skipTime === "number" && display) {
|
||||||
|
display.setTime(props.skipTime);
|
||||||
|
}
|
||||||
|
}, [props.skipTime, display]);
|
||||||
|
|
||||||
|
return (
|
||||||
|
<Transition
|
||||||
|
animation={animation}
|
||||||
|
show={show}
|
||||||
|
className="absolute right-[calc(3rem+env(safe-area-inset-right))] bottom-0"
|
||||||
|
>
|
||||||
|
<div
|
||||||
|
className={classNames([
|
||||||
|
"absolute bottom-0 right-0 transition-[bottom] duration-200 flex items-center space-x-3",
|
||||||
|
bottom,
|
||||||
|
])}
|
||||||
|
>
|
||||||
|
<Button
|
||||||
|
onClick={handleSkip}
|
||||||
|
className="bg-buttons-primary hover:bg-buttons-primaryHover text-buttons-primaryText flex justify-center items-center"
|
||||||
|
>
|
||||||
|
<Icon className="text-xl mr-1" icon={Icons.SKIP_EPISODE} />
|
||||||
|
Skip Intro
|
||||||
|
</Button>
|
||||||
|
</div>
|
||||||
|
</Transition>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
@ -1,10 +1,11 @@
|
||||||
import { ReactNode, useCallback, useEffect, useState } from "react";
|
import { ReactNode, useState } from "react";
|
||||||
import { useParams } from "react-router-dom";
|
import { useParams } from "react-router-dom";
|
||||||
|
|
||||||
import IosPwaLimitations from "@/components/buttons/IosPwaLimitations";
|
import IosPwaLimitations from "@/components/buttons/IosPwaLimitations";
|
||||||
import { Icon, Icons } from "@/components/Icon";
|
import { Icon, Icons } from "@/components/Icon";
|
||||||
import { BrandPill } from "@/components/layout/BrandPill";
|
import { BrandPill } from "@/components/layout/BrandPill";
|
||||||
import { Player } from "@/components/player";
|
import { Player } from "@/components/player";
|
||||||
|
import { SkipIntroButton } from "@/components/player/atoms/SkipIntroButton";
|
||||||
import { Widescreen } from "@/components/player/atoms/Widescreen";
|
import { Widescreen } from "@/components/player/atoms/Widescreen";
|
||||||
import { usePlayerMeta } from "@/components/player/hooks/usePlayerMeta";
|
import { usePlayerMeta } from "@/components/player/hooks/usePlayerMeta";
|
||||||
import { useShouldShowControls } from "@/components/player/hooks/useShouldShowControls";
|
import { useShouldShowControls } from "@/components/player/hooks/useShouldShowControls";
|
||||||
|
|
@ -33,7 +34,6 @@ export function PlayerPart(props: PlayerPartProps) {
|
||||||
const { isMobile } = useIsMobile();
|
const { isMobile } = useIsMobile();
|
||||||
const isLoading = usePlayerStore((s) => s.mediaPlaying.isLoading);
|
const isLoading = usePlayerStore((s) => s.mediaPlaying.isLoading);
|
||||||
const { playerMeta: meta } = usePlayerMeta();
|
const { playerMeta: meta } = usePlayerMeta();
|
||||||
const display = usePlayerStore((s) => s.display);
|
|
||||||
|
|
||||||
const isIOS = /iPad|iPhone|iPod/.test(navigator.userAgent);
|
const isIOS = /iPad|iPhone|iPod/.test(navigator.userAgent);
|
||||||
const isIOSPWA =
|
const isIOSPWA =
|
||||||
|
|
@ -54,22 +54,7 @@ export function PlayerPart(props: PlayerPartProps) {
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
// Fullscreen on rotation horizontal
|
const skiptime = null;
|
||||||
const onRotate = useCallback(() => {
|
|
||||||
if (window.orientation === 90 || window.orientation === -90) {
|
|
||||||
if (!document.fullscreenElement && status === playerStatus.PLAYING) {
|
|
||||||
display?.toggleFullscreen();
|
|
||||||
}
|
|
||||||
} else if (document.fullscreenElement && status === playerStatus.PLAYING) {
|
|
||||||
display?.toggleFullscreen();
|
|
||||||
}
|
|
||||||
}, [display, status]);
|
|
||||||
useEffect(() => {
|
|
||||||
window.addEventListener("orientationchange", onRotate);
|
|
||||||
return () => {
|
|
||||||
window.removeEventListener("orientationchange", onRotate);
|
|
||||||
};
|
|
||||||
}, [onRotate]);
|
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<Player.Container onLoad={props.onLoad} showingControls={showTargets}>
|
<Player.Container onLoad={props.onLoad} showingControls={showTargets}>
|
||||||
|
|
@ -237,6 +222,8 @@ export function PlayerPart(props: PlayerPartProps) {
|
||||||
controlsShowing={showTargets}
|
controlsShowing={showTargets}
|
||||||
onChange={props.onMetaChange}
|
onChange={props.onMetaChange}
|
||||||
/>
|
/>
|
||||||
|
|
||||||
|
<SkipIntroButton controlsShowing={showTargets} skipTime={skiptime} />
|
||||||
</Player.Container>
|
</Player.Container>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
|
||||||
Loading…
Reference in a new issue