mirror of
https://github.com/p-stream/p-stream.git
synced 2026-04-21 13:22:16 +00:00
add time remaining to pause overlay
This commit is contained in:
parent
4630d7822b
commit
f1af25bf7b
1 changed files with 69 additions and 2 deletions
|
|
@ -1,4 +1,5 @@
|
||||||
import { useEffect, useState } from "react";
|
import { useEffect, useState } from "react";
|
||||||
|
import { useTranslation } from "react-i18next";
|
||||||
import { useIdle } from "react-use";
|
import { useIdle } from "react-use";
|
||||||
|
|
||||||
import {
|
import {
|
||||||
|
|
@ -12,6 +13,8 @@ import { useIsMobile } from "@/hooks/useIsMobile";
|
||||||
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";
|
||||||
import { usePreferencesStore } from "@/stores/preferences";
|
import { usePreferencesStore } from "@/stores/preferences";
|
||||||
|
import { durationExceedsHour, formatSeconds } from "@/utils/formatSeconds";
|
||||||
|
import { uses12HourClock } from "@/utils/uses12HourClock";
|
||||||
|
|
||||||
interface PauseDetails {
|
interface PauseDetails {
|
||||||
voteAverage: number | null;
|
voteAverage: number | null;
|
||||||
|
|
@ -23,10 +26,14 @@ export function PauseOverlay() {
|
||||||
const isPaused = usePlayerStore((s) => s.mediaPlaying.isPaused);
|
const isPaused = usePlayerStore((s) => s.mediaPlaying.isPaused);
|
||||||
const status = usePlayerStore((s) => s.status);
|
const status = usePlayerStore((s) => s.status);
|
||||||
const meta = usePlayerStore((s) => s.meta);
|
const meta = usePlayerStore((s) => s.meta);
|
||||||
|
const { time, duration, draggingTime } = usePlayerStore((s) => s.progress);
|
||||||
|
const { isSeeking } = usePlayerStore((s) => s.interface);
|
||||||
|
const playbackRate = usePlayerStore((s) => s.mediaPlaying.playbackRate);
|
||||||
const enablePauseOverlay = usePreferencesStore((s) => s.enablePauseOverlay);
|
const enablePauseOverlay = usePreferencesStore((s) => s.enablePauseOverlay);
|
||||||
const enableImageLogos = usePreferencesStore((s) => s.enableImageLogos);
|
const enableImageLogos = usePreferencesStore((s) => s.enableImageLogos);
|
||||||
const { isMobile } = useIsMobile();
|
const { isMobile } = useIsMobile();
|
||||||
const { showTargets } = useShouldShowControls();
|
const { showTargets } = useShouldShowControls();
|
||||||
|
const { t } = useTranslation();
|
||||||
const [logoUrl, setLogoUrl] = useState<string | null>(null);
|
const [logoUrl, setLogoUrl] = useState<string | null>(null);
|
||||||
const [details, setDetails] = useState<PauseDetails>({
|
const [details, setDetails] = useState<PauseDetails>({
|
||||||
voteAverage: null,
|
voteAverage: null,
|
||||||
|
|
@ -117,6 +124,25 @@ export function PauseOverlay() {
|
||||||
const overview =
|
const overview =
|
||||||
meta.type === "show" ? meta.episode?.overview : meta.overview;
|
meta.type === "show" ? meta.episode?.overview : meta.overview;
|
||||||
|
|
||||||
|
const hasHours = durationExceedsHour(duration);
|
||||||
|
const currentTime = Math.min(
|
||||||
|
Math.max(isSeeking ? draggingTime : time, 0),
|
||||||
|
duration,
|
||||||
|
);
|
||||||
|
const secondsRemaining = Math.abs(currentTime - duration);
|
||||||
|
const secondsRemainingAdjusted =
|
||||||
|
playbackRate > 0 ? secondsRemaining / playbackRate : secondsRemaining;
|
||||||
|
|
||||||
|
const timeLeft = formatSeconds(
|
||||||
|
secondsRemaining,
|
||||||
|
durationExceedsHour(secondsRemaining),
|
||||||
|
);
|
||||||
|
const timeWatched = formatSeconds(currentTime, hasHours);
|
||||||
|
const timeFinished = new Date(Date.now() + secondsRemainingAdjusted * 1e3);
|
||||||
|
const durationFormatted = formatSeconds(duration, hasHours);
|
||||||
|
|
||||||
|
const localizationKey = "remaining";
|
||||||
|
|
||||||
// Don't render anything if we don't have content, but keep structure for fade if valid
|
// Don't render anything if we don't have content, but keep structure for fade if valid
|
||||||
const hasDetails = details.voteAverage !== null || details.genres.length > 0;
|
const hasDetails = details.voteAverage !== null || details.genres.length > 0;
|
||||||
const hasContent = overview || logoUrl || meta.title || hasDetails;
|
const hasContent = overview || logoUrl || meta.title || hasDetails;
|
||||||
|
|
@ -128,7 +154,7 @@ export function PauseOverlay() {
|
||||||
shouldShow ? "opacity-100" : "opacity-0"
|
shouldShow ? "opacity-100" : "opacity-0"
|
||||||
}`}
|
}`}
|
||||||
>
|
>
|
||||||
<div className="md:ml-16 max-w-sm lg:max-w-2xl p-8">
|
<div className="md:ml-16 max-w-md lg:max-w-2xl p-8">
|
||||||
{logoUrl ? (
|
{logoUrl ? (
|
||||||
<img
|
<img
|
||||||
src={logoUrl}
|
src={logoUrl}
|
||||||
|
|
@ -147,7 +173,9 @@ export function PauseOverlay() {
|
||||||
</h2>
|
</h2>
|
||||||
)}
|
)}
|
||||||
|
|
||||||
{(details.voteAverage !== null || details.genres.length > 0) && (
|
{(details.voteAverage !== null ||
|
||||||
|
details.genres.length > 0 ||
|
||||||
|
duration > 0) && (
|
||||||
<div className="mb-3 flex flex-wrap items-center gap-x-2 gap-y-1 text-sm text-white/80 drop-shadow-md">
|
<div className="mb-3 flex flex-wrap items-center gap-x-2 gap-y-1 text-sm text-white/80 drop-shadow-md">
|
||||||
{details.voteAverage !== null && (
|
{details.voteAverage !== null && (
|
||||||
<span>
|
<span>
|
||||||
|
|
@ -163,6 +191,45 @@ export function PauseOverlay() {
|
||||||
<span>{details.genres.slice(0, 4).join(", ")}</span>
|
<span>{details.genres.slice(0, 4).join(", ")}</span>
|
||||||
</>
|
</>
|
||||||
)}
|
)}
|
||||||
|
{duration > 0 && (
|
||||||
|
<>
|
||||||
|
{(details.voteAverage !== null ||
|
||||||
|
details.genres.length > 0) && (
|
||||||
|
<span className="text-white/60">•</span>
|
||||||
|
)}
|
||||||
|
<span>
|
||||||
|
{(() => {
|
||||||
|
const text = t(`player.time.${localizationKey}`, {
|
||||||
|
timeFinished,
|
||||||
|
timeWatched,
|
||||||
|
timeLeft,
|
||||||
|
duration: durationFormatted,
|
||||||
|
formatParams: {
|
||||||
|
timeFinished: {
|
||||||
|
hour: "numeric",
|
||||||
|
minute: "numeric",
|
||||||
|
hour12: uses12HourClock(),
|
||||||
|
},
|
||||||
|
},
|
||||||
|
});
|
||||||
|
if (
|
||||||
|
localizationKey === "remaining" &&
|
||||||
|
text.includes(" • ")
|
||||||
|
) {
|
||||||
|
const [left, right] = text.split(" • ");
|
||||||
|
return (
|
||||||
|
<>
|
||||||
|
{left}
|
||||||
|
<span className="text-white/60 mx-1">•</span>
|
||||||
|
{right}
|
||||||
|
</>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
return text;
|
||||||
|
})()}
|
||||||
|
</span>
|
||||||
|
</>
|
||||||
|
)}
|
||||||
</div>
|
</div>
|
||||||
)}
|
)}
|
||||||
|
|
||||||
|
|
|
||||||
Loading…
Reference in a new issue