mirror of
https://github.com/movie-web/movie-web.git
synced 2024-12-28 17:51:50 +01:00
Localization, center loading, create divider action, rename season/episode route in EpisodeSelectionPopout
This commit is contained in:
parent
c0867182d7
commit
5e8ad2e996
@ -172,7 +172,7 @@ export const FloatingCardView = {
|
|||||||
},
|
},
|
||||||
Content(props: { children: React.ReactNode; noSection?: boolean }) {
|
Content(props: { children: React.ReactNode; noSection?: boolean }) {
|
||||||
return (
|
return (
|
||||||
<div className="grid h-full grid-rows-[auto,minmax(0,1fr)]">
|
<div className="grid h-full grid-rows-[1fr]">
|
||||||
{props.noSection ? (
|
{props.noSection ? (
|
||||||
<div className="relative h-full overflow-y-auto bg-ash-300">
|
<div className="relative h-full overflow-y-auto bg-ash-300">
|
||||||
{props.children}
|
{props.children}
|
||||||
|
@ -75,6 +75,13 @@
|
|||||||
"errors": {
|
"errors": {
|
||||||
"loadingWentWong": "Something went wrong loading the episodes for {{seasonTitle}}",
|
"loadingWentWong": "Something went wrong loading the episodes for {{seasonTitle}}",
|
||||||
"embedsError": "Something went wrong loading the embeds for this thing that you like"
|
"embedsError": "Something went wrong loading the embeds for this thing that you like"
|
||||||
|
},
|
||||||
|
"descriptions": {
|
||||||
|
"sources": "What provider do you want to use?",
|
||||||
|
"embeds": "Choose which video to view",
|
||||||
|
"seasons": "Choose which season you want to watch",
|
||||||
|
"episode": "Pick an episode",
|
||||||
|
"captions": "Choose a subtitle language"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"errors": {
|
"errors": {
|
||||||
|
@ -28,6 +28,7 @@ import { PopoutProviderAction } from "@/video/components/popouts/PopoutProviderA
|
|||||||
import { ChromecastAction } from "@/video/components/actions/ChromecastAction";
|
import { ChromecastAction } from "@/video/components/actions/ChromecastAction";
|
||||||
import { CastingTextAction } from "@/video/components/actions/CastingTextAction";
|
import { CastingTextAction } from "@/video/components/actions/CastingTextAction";
|
||||||
import { SettingsAction } from "./actions/SettingsAction";
|
import { SettingsAction } from "./actions/SettingsAction";
|
||||||
|
import { DividerAction } from "./actions/DividerAction";
|
||||||
|
|
||||||
type Props = VideoPlayerBaseProps;
|
type Props = VideoPlayerBaseProps;
|
||||||
|
|
||||||
@ -150,7 +151,7 @@ export function VideoPlayer(props: Props) {
|
|||||||
<LeftSideControls />
|
<LeftSideControls />
|
||||||
<div className="flex-1" />
|
<div className="flex-1" />
|
||||||
<SeriesSelectionAction />
|
<SeriesSelectionAction />
|
||||||
<div className="mx-2 h-6 w-px bg-white opacity-50" />
|
<DividerAction />
|
||||||
<SettingsAction />
|
<SettingsAction />
|
||||||
<ChromecastAction />
|
<ChromecastAction />
|
||||||
<AirplayAction />
|
<AirplayAction />
|
||||||
|
@ -27,7 +27,9 @@ function VideoPlayerBaseWithState(props: VideoPlayerBaseProps) {
|
|||||||
|
|
||||||
const children =
|
const children =
|
||||||
typeof props.children === "function"
|
typeof props.children === "function"
|
||||||
? props.children({ isFullscreen: videoInterface.isFullscreen })
|
? props.children({
|
||||||
|
isFullscreen: videoInterface.isFullscreen,
|
||||||
|
})
|
||||||
: props.children;
|
: props.children;
|
||||||
|
|
||||||
// TODO move error boundary to only decorated, <VideoPlayer /> shouldn't have styling
|
// TODO move error boundary to only decorated, <VideoPlayer /> shouldn't have styling
|
||||||
|
12
src/video/components/actions/DividerAction.tsx
Normal file
12
src/video/components/actions/DividerAction.tsx
Normal file
@ -0,0 +1,12 @@
|
|||||||
|
import { useVideoPlayerDescriptor } from "@/video/state/hooks";
|
||||||
|
import { useMeta } from "@/video/state/logic/meta";
|
||||||
|
import { MWMediaType } from "@/backend/metadata/types";
|
||||||
|
|
||||||
|
export function DividerAction() {
|
||||||
|
const descriptor = useVideoPlayerDescriptor();
|
||||||
|
const meta = useMeta(descriptor);
|
||||||
|
|
||||||
|
if (meta?.meta.meta.type !== MWMediaType.SERIES) return null;
|
||||||
|
|
||||||
|
return <div className="mx-2 h-6 w-px bg-white opacity-50" />;
|
||||||
|
}
|
@ -51,8 +51,8 @@ export function CaptionSelectionPopout(props: {
|
|||||||
height={500}
|
height={500}
|
||||||
>
|
>
|
||||||
<FloatingCardView.Header
|
<FloatingCardView.Header
|
||||||
title={t("videoPlayer.popouts.sources")}
|
title={t("videoPlayer.popouts.captions")}
|
||||||
description="What provider do you want to use?"
|
description={t("videoPlayer.popouts.descriptions.captions")}
|
||||||
goBack={() => props.router.navigate("/")}
|
goBack={() => props.router.navigate("/")}
|
||||||
/>
|
/>
|
||||||
<FloatingCardView.Content noSection>
|
<FloatingCardView.Content noSection>
|
||||||
|
@ -22,7 +22,7 @@ export function EpisodeSelectionPopout() {
|
|||||||
media: string;
|
media: string;
|
||||||
}>();
|
}>();
|
||||||
const { t } = useTranslation();
|
const { t } = useTranslation();
|
||||||
const { pageProps, navigate } = useFloatingRouter("/season/episodes");
|
const { pageProps, navigate } = useFloatingRouter("/episodes");
|
||||||
|
|
||||||
const descriptor = useVideoPlayerDescriptor();
|
const descriptor = useVideoPlayerDescriptor();
|
||||||
const meta = useMeta(descriptor);
|
const meta = useMeta(descriptor);
|
||||||
@ -84,7 +84,7 @@ export function EpisodeSelectionPopout() {
|
|||||||
const setSeason = (id: string) => {
|
const setSeason = (id: string) => {
|
||||||
requestSeason(id);
|
requestSeason(id);
|
||||||
setCurrentVisibleSeason({ seasonId: id });
|
setCurrentVisibleSeason({ seasonId: id });
|
||||||
navigate("/season");
|
navigate("/episodes");
|
||||||
};
|
};
|
||||||
|
|
||||||
const { watched } = useWatchedContext();
|
const { watched } = useWatchedContext();
|
||||||
@ -95,11 +95,11 @@ export function EpisodeSelectionPopout() {
|
|||||||
|
|
||||||
return (
|
return (
|
||||||
<>
|
<>
|
||||||
<FloatingView {...pageProps("episodes")} height={600} width={375}>
|
<FloatingView {...pageProps("seasons")} height={600} width={375}>
|
||||||
<FloatingCardView.Header
|
<FloatingCardView.Header
|
||||||
title="Seasons"
|
title={t("videoPlayer.popouts.seasons")}
|
||||||
description="Choose which season you want to watch"
|
description={t("videoPlayer.popouts.descriptions.seasons")}
|
||||||
goBack={() => navigate("/season")}
|
goBack={() => navigate("/episodes")}
|
||||||
backText={`To ${currentSeasonInfo?.title.toLowerCase()}`}
|
backText={`To ${currentSeasonInfo?.title.toLowerCase()}`}
|
||||||
/>
|
/>
|
||||||
<FloatingCardView.Content>
|
<FloatingCardView.Content>
|
||||||
@ -116,16 +116,16 @@ export function EpisodeSelectionPopout() {
|
|||||||
: "No season"}
|
: "No season"}
|
||||||
</FloatingCardView.Content>
|
</FloatingCardView.Content>
|
||||||
</FloatingView>
|
</FloatingView>
|
||||||
<FloatingView {...pageProps("season")} height={600} width={375}>
|
<FloatingView {...pageProps("episodes")} height={600} width={375}>
|
||||||
<FloatingCardView.Header
|
<FloatingCardView.Header
|
||||||
title={currentSeasonInfo?.title ?? "Unknown season"}
|
title={currentSeasonInfo?.title ?? "Unknown season"}
|
||||||
description="Pick an episode"
|
description={t("videoPlayer.popouts.descriptions.episode")}
|
||||||
goBack={closePopout}
|
goBack={closePopout}
|
||||||
close
|
close
|
||||||
action={
|
action={
|
||||||
<button
|
<button
|
||||||
type="button"
|
type="button"
|
||||||
onClick={() => navigate("/season/episodes")}
|
onClick={() => navigate("/episodes/seasons")}
|
||||||
className="flex cursor-pointer items-center space-x-2 transition-colors duration-200 hover:text-white"
|
className="flex cursor-pointer items-center space-x-2 transition-colors duration-200 hover:text-white"
|
||||||
>
|
>
|
||||||
<span>Other seasons</span>
|
<span>Other seasons</span>
|
||||||
@ -146,7 +146,7 @@ export function EpisodeSelectionPopout() {
|
|||||||
className="text-xl text-bink-600"
|
className="text-xl text-bink-600"
|
||||||
/>
|
/>
|
||||||
<p className="mt-6 w-full text-center">
|
<p className="mt-6 w-full text-center">
|
||||||
{t("videoPLayer.popouts.errors.loadingWentWrong", {
|
{t("videoPlayer.popouts.errors.loadingWentWrong", {
|
||||||
seasonTitle: currentSeasonInfo?.title?.toLowerCase(),
|
seasonTitle: currentSeasonInfo?.title?.toLowerCase(),
|
||||||
})}
|
})}
|
||||||
</p>
|
</p>
|
||||||
@ -185,112 +185,5 @@ export function EpisodeSelectionPopout() {
|
|||||||
</FloatingCardView.Content>
|
</FloatingCardView.Content>
|
||||||
</FloatingView>
|
</FloatingView>
|
||||||
</>
|
</>
|
||||||
// <FloatingView show height={500} width={320}>
|
|
||||||
// <div className="grid h-full grid-rows-[auto,minmax(0,1fr)]">
|
|
||||||
// <PopoutSection className="bg-ash-100 font-bold text-white">
|
|
||||||
// <div className="relative flex items-center">
|
|
||||||
// <button
|
|
||||||
// className={[
|
|
||||||
// "-m-1.5 rounded-lg p-1.5 transition-opacity duration-100 hover:bg-ash-200",
|
|
||||||
// isPickingSeason ? "pointer-events-none opacity-0" : "opacity-1",
|
|
||||||
// ].join(" ")}
|
|
||||||
// onClick={toggleIsPickingSeason}
|
|
||||||
// type="button"
|
|
||||||
// >
|
|
||||||
// <Icon icon={Icons.CHEVRON_LEFT} />
|
|
||||||
// </button>
|
|
||||||
// <span
|
|
||||||
// className={[
|
|
||||||
// titlePositionClass,
|
|
||||||
// !isPickingSeason ? "opacity-1" : "opacity-0",
|
|
||||||
// ].join(" ")}
|
|
||||||
// >
|
|
||||||
// {currentSeasonInfo?.title || ""}
|
|
||||||
// </span>
|
|
||||||
// <span
|
|
||||||
// className={[
|
|
||||||
// titlePositionClass,
|
|
||||||
// isPickingSeason ? "opacity-1" : "opacity-0",
|
|
||||||
// ].join(" ")}
|
|
||||||
// >
|
|
||||||
// {t("videoPlayer.popouts.seasons")}
|
|
||||||
// </span>
|
|
||||||
// </div>
|
|
||||||
// </PopoutSection>
|
|
||||||
// <div className="relative grid h-full grid-rows-[minmax(1px,1fr)]">
|
|
||||||
// <PopoutSection
|
|
||||||
// className={[
|
|
||||||
// "absolute inset-0 z-30 overflow-y-auto border-ash-400 bg-ash-100 transition-[max-height,padding] duration-200",
|
|
||||||
// isPickingSeason
|
|
||||||
// ? "max-h-full border-t"
|
|
||||||
// : "max-h-0 overflow-hidden py-0",
|
|
||||||
// ].join(" ")}
|
|
||||||
// >
|
|
||||||
// {currentSeasonInfo
|
|
||||||
// ? meta?.seasons?.map?.((season) => (
|
|
||||||
// <PopoutListEntry
|
|
||||||
// key={season.id}
|
|
||||||
// active={meta?.episode?.seasonId === season.id}
|
|
||||||
// onClick={() => setSeason(season.id)}
|
|
||||||
// isOnDarkBackground
|
|
||||||
// >
|
|
||||||
// {season.title}
|
|
||||||
// </PopoutListEntry>
|
|
||||||
// ))
|
|
||||||
// : "No season"}
|
|
||||||
// </PopoutSection>
|
|
||||||
// <PopoutSection className="relative h-full overflow-y-auto">
|
|
||||||
// {loading ? (
|
|
||||||
// <div className="flex h-full w-full items-center justify-center">
|
|
||||||
// <Loading />
|
|
||||||
// </div>
|
|
||||||
// ) : error ? (
|
|
||||||
// <div className="flex h-full w-full items-center justify-center">
|
|
||||||
// <div className="flex flex-col flex-wrap items-center text-slate-400">
|
|
||||||
// <IconPatch
|
|
||||||
// icon={Icons.EYE_SLASH}
|
|
||||||
// className="text-xl text-bink-600"
|
|
||||||
// />
|
|
||||||
// <p className="mt-6 w-full text-center">
|
|
||||||
// {t("videoPLayer.popouts.errors.loadingWentWrong", {
|
|
||||||
// seasonTitle: currentSeasonInfo?.title?.toLowerCase(),
|
|
||||||
// })}
|
|
||||||
// </p>
|
|
||||||
// </div>
|
|
||||||
// </div>
|
|
||||||
// ) : (
|
|
||||||
// <div>
|
|
||||||
// {currentSeasonEpisodes && currentSeasonInfo
|
|
||||||
// ? currentSeasonEpisodes.map((e) => (
|
|
||||||
// <PopoutListEntry
|
|
||||||
// key={e.id}
|
|
||||||
// active={e.id === meta?.episode?.episodeId}
|
|
||||||
// onClick={() => {
|
|
||||||
// if (e.id === meta?.episode?.episodeId)
|
|
||||||
// controls.closePopout();
|
|
||||||
// else setCurrent(currentSeasonInfo.id, e.id);
|
|
||||||
// }}
|
|
||||||
// percentageCompleted={
|
|
||||||
// watched.items.find(
|
|
||||||
// (item) =>
|
|
||||||
// item.item?.series?.seasonId ===
|
|
||||||
// currentSeasonInfo.id &&
|
|
||||||
// item.item?.series?.episodeId === e.id
|
|
||||||
// )?.percentage
|
|
||||||
// }
|
|
||||||
// >
|
|
||||||
// {t("videoPlayer.popouts.episode", {
|
|
||||||
// index: e.number,
|
|
||||||
// title: e.title,
|
|
||||||
// })}
|
|
||||||
// </PopoutListEntry>
|
|
||||||
// ))
|
|
||||||
// : "No episodes"}
|
|
||||||
// </div>
|
|
||||||
// )}
|
|
||||||
// </PopoutSection>
|
|
||||||
// </div>
|
|
||||||
// </div>
|
|
||||||
// </FloatingView>
|
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
@ -181,7 +181,7 @@ export function SourceSelectionPopout(props: {
|
|||||||
>
|
>
|
||||||
<FloatingCardView.Header
|
<FloatingCardView.Header
|
||||||
title={t("videoPlayer.popouts.sources")}
|
title={t("videoPlayer.popouts.sources")}
|
||||||
description="What provider do you want to use?"
|
description={t("videoPlayer.popouts.descriptions.sources")}
|
||||||
goBack={() => props.router.navigate("/")}
|
goBack={() => props.router.navigate("/")}
|
||||||
/>
|
/>
|
||||||
<FloatingCardView.Content>
|
<FloatingCardView.Content>
|
||||||
@ -206,7 +206,7 @@ export function SourceSelectionPopout(props: {
|
|||||||
>
|
>
|
||||||
<FloatingCardView.Header
|
<FloatingCardView.Header
|
||||||
title={selectedProviderPopulated?.displayName ?? ""}
|
title={selectedProviderPopulated?.displayName ?? ""}
|
||||||
description="Choose which video to view"
|
description={t("videoPlayer.popouts.descriptions.embeds")}
|
||||||
goBack={() => props.router.navigate(`/${props.prefix}`)}
|
goBack={() => props.router.navigate(`/${props.prefix}`)}
|
||||||
/>
|
/>
|
||||||
<FloatingCardView.Content>
|
<FloatingCardView.Content>
|
||||||
|
Loading…
Reference in New Issue
Block a user