1
0
Fork 0
mirror of https://github.com/sussy-code/smov.git synced 2024-12-29 16:07:40 +01:00

Add cool new popout stuff

Co-authored-by: mrjvs <mistrjvs@gmail.com>
This commit is contained in:
Jip Fr 2023-03-12 21:49:58 +01:00
parent 89f77debca
commit c0867182d7
12 changed files with 308 additions and 239 deletions

View file

@ -116,7 +116,12 @@ export function FloatingCard(props: RootFloatingCardProps) {
}
export function PopoutFloatingCard(props: FloatingCardProps) {
return <FloatingCard className="overflow-hidden rounded-md" {...props} />;
return (
<FloatingCard
className="overflow-hidden rounded-md bg-ash-300"
{...props}
/>
);
}
export const FloatingCardView = {
@ -149,7 +154,7 @@ export const FloatingCardView = {
);
return (
<div className="mb-[-1px] flex flex-col bg-[#1C161B] bg-opacity-80 backdrop-blur-xl">
<div className="flex flex-col bg-[#1C161B]">
<FloatingDragHandle />
<PopoutSection>
<div className="flex justify-between">
@ -165,12 +170,20 @@ export const FloatingCardView = {
</div>
);
},
Content(props: { children: React.ReactNode }) {
Content(props: { children: React.ReactNode; noSection?: boolean }) {
return (
<PopoutSection className="bg-ash-300">
{props.children}
<div className="grid h-full grid-rows-[auto,minmax(0,1fr)]">
{props.noSection ? (
<div className="relative h-full overflow-y-auto bg-ash-300">
{props.children}
</div>
) : (
<PopoutSection className="relative h-full overflow-y-auto bg-ash-300">
{props.children}
</PopoutSection>
)}
<MobilePopoutSpacer />
</PopoutSection>
</div>
);
},
};

View file

@ -6,7 +6,7 @@ export function FloatingDragHandle() {
if (!isMobile) return null;
return (
<div className="mx-auto my-3 -mb-3 h-1 w-12 rounded-full bg-ash-500 bg-opacity-30" />
<div className="relative z-50 mx-auto my-3 -mb-3 h-1 w-12 rounded-full bg-ash-500 bg-opacity-30" />
);
}

View file

@ -22,7 +22,10 @@ export function FloatingView(props: Props) {
show={props.show}
>
<div
className={[props.className ?? ""].join(" ")}
className={[
props.className ?? "",
"grid grid-rows-[auto,minmax(0,1fr)]",
].join(" ")}
data-floating-page={props.show ? "true" : undefined}
style={{
height: props.height ? `${props.height}px` : undefined,

View file

@ -10,10 +10,7 @@ import { MobileCenterAction } from "@/video/components/actions/MobileCenterActio
import { PageTitleAction } from "@/video/components/actions/PageTitleAction";
import { PauseAction } from "@/video/components/actions/PauseAction";
import { ProgressAction } from "@/video/components/actions/ProgressAction";
import { QualityDisplayAction } from "@/video/components/actions/QualityDisplayAction";
import { SeriesSelectionAction } from "@/video/components/actions/SeriesSelectionAction";
import { SourceSelectionAction } from "@/video/components/actions/SourceSelectionAction";
import { CaptionsSelectionAction } from "@/video/components/actions/CaptionsSelectionAction";
import { ShowTitleAction } from "@/video/components/actions/ShowTitleAction";
import { KeyboardShortcutsAction } from "@/video/components/actions/KeyboardShortcutsAction";
import { SkipTimeAction } from "@/video/components/actions/SkipTimeAction";

View file

@ -1,6 +1,6 @@
import { Icons } from "@/components/Icon";
import { useTranslation } from "react-i18next";
import { PopoutListAction } from "../popouts/PopoutUtils";
import { PopoutListAction } from "../../popouts/PopoutUtils";
interface Props {
onClick: () => any;

View file

@ -1,6 +1,6 @@
import { Icon, Icons } from "@/components/Icon";
import { useTranslation } from "react-i18next";
import { PopoutListAction } from "../popouts/PopoutUtils";
import { PopoutListAction } from "../../popouts/PopoutUtils";
import { QualityDisplayAction } from "./QualityDisplayAction";
interface Props {

View file

@ -1,6 +1,9 @@
import { getCaptionUrl } from "@/backend/helpers/captions";
import { MWCaption } from "@/backend/helpers/streams";
import { Icon, Icons } from "@/components/Icon";
import { FloatingCardView } from "@/components/popout/FloatingCard";
import { FloatingView } from "@/components/popout/FloatingView";
import { useFloatingRouter } from "@/hooks/useFloatingRouter";
import { useLoading } from "@/hooks/useLoading";
import { useVideoPlayerDescriptor } from "@/video/state/hooks";
import { useControls } from "@/video/state/logic/controls";
@ -14,7 +17,10 @@ function makeCaptionId(caption: MWCaption, isLinked: boolean): string {
return isLinked ? `linked-${caption.langIso}` : `external-${caption.langIso}`;
}
export function CaptionSelectionPopout() {
export function CaptionSelectionPopout(props: {
router: ReturnType<typeof useFloatingRouter>;
prefix: string;
}) {
const { t } = useTranslation();
const descriptor = useVideoPlayerDescriptor();
@ -39,11 +45,17 @@ export function CaptionSelectionPopout() {
const currentCaption = source.source?.caption?.id;
return (
<>
<PopoutSection className="bg-ash-100 font-bold text-white">
<div>{t("videoPlayer.popouts.captions")}</div>
</PopoutSection>
<div className="relative overflow-y-auto">
<FloatingView
{...props.router.pageProps(props.prefix)}
width={320}
height={500}
>
<FloatingCardView.Header
title={t("videoPlayer.popouts.sources")}
description="What provider do you want to use?"
goBack={() => props.router.navigate("/")}
/>
<FloatingCardView.Content noSection>
<PopoutSection>
<PopoutListEntry
active={!currentCaption}
@ -56,7 +68,7 @@ export function CaptionSelectionPopout() {
</PopoutListEntry>
</PopoutSection>
<p className="sticky top-0 z-10 flex items-center space-x-1 bg-ash-200 px-5 py-3 text-sm font-bold uppercase">
<p className="sticky top-0 z-10 flex items-center space-x-1 bg-ash-300 px-5 py-3 text-xs font-bold uppercase">
<Icon className="text-base" icon={Icons.LINK} />
<span>{t("videoPlayer.popouts.linkedCaptions")}</span>
</p>
@ -79,7 +91,7 @@ export function CaptionSelectionPopout() {
))}
</div>
</PopoutSection>
</div>
</>
</FloatingCardView.Content>
</FloatingView>
);
}

View file

@ -13,19 +13,21 @@ import { useControls } from "@/video/state/logic/controls";
import { useWatchedContext } from "@/state/watched";
import { useTranslation } from "react-i18next";
import { FloatingView } from "@/components/popout/FloatingView";
import { PopoutListEntry, PopoutSection } from "./PopoutUtils";
import { useFloatingRouter } from "@/hooks/useFloatingRouter";
import { FloatingCardView } from "@/components/popout/FloatingCard";
import { PopoutListEntry } from "./PopoutUtils";
export function EpisodeSelectionPopout() {
const params = useParams<{
media: string;
}>();
const { t } = useTranslation();
const { pageProps, navigate } = useFloatingRouter("/season/episodes");
const descriptor = useVideoPlayerDescriptor();
const meta = useMeta(descriptor);
const controls = useControls(descriptor);
const [isPickingSeason, setIsPickingSeason] = useState<boolean>(false);
const [currentVisibleSeason, setCurrentVisibleSeason] = useState<{
seasonId: string;
season?: MWSeasonWithEpisodeMeta;
@ -41,7 +43,6 @@ export function EpisodeSelectionPopout() {
seasonId: sId,
season: undefined,
});
setIsPickingSeason(false);
reqSeasonMeta(decodeJWId(params.media)?.id as string, sId).then((v) => {
if (v?.meta.type !== MWMediaType.SERIES) return;
setCurrentVisibleSeason({
@ -80,132 +81,216 @@ export function EpisodeSelectionPopout() {
)?.episodes;
}, [meta, currentSeasonId, currentVisibleSeason]);
const toggleIsPickingSeason = () => {
setIsPickingSeason(!isPickingSeason);
};
const setSeason = (id: string) => {
requestSeason(id);
setCurrentVisibleSeason({ seasonId: id });
navigate("/season");
};
const { watched } = useWatchedContext();
const titlePositionClass = useMemo(() => {
const offset = isPickingSeason ? "left-0" : "left-10";
return [
"absolute w-full transition-[left,opacity] duration-200",
offset,
].join(" ");
}, [isPickingSeason]);
const closePopout = () => {
controls.closePopout();
};
return (
<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">
<>
<FloatingView {...pageProps("episodes")} height={600} width={375}>
<FloatingCardView.Header
title="Seasons"
description="Choose which season you want to watch"
goBack={() => navigate("/season")}
backText={`To ${currentSeasonInfo?.title.toLowerCase()}`}
/>
<FloatingCardView.Content>
{currentSeasonInfo
? meta?.seasons?.map?.((season) => (
<PopoutListEntry
key={season.id}
active={meta?.episode?.seasonId === season.id}
onClick={() => setSeason(season.id)}
>
{season.title}
</PopoutListEntry>
))
: "No season"}
</FloatingCardView.Content>
</FloatingView>
<FloatingView {...pageProps("season")} height={600} width={375}>
<FloatingCardView.Header
title={currentSeasonInfo?.title ?? "Unknown season"}
description="Pick an episode"
goBack={closePopout}
close
action={
<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"
onClick={() => navigate("/season/episodes")}
className="flex cursor-pointer items-center space-x-2 transition-colors duration-200 hover:text-white"
>
<Icon icon={Icons.CHEVRON_LEFT} />
<span>Other seasons</span>
<Icon icon={Icons.CHEVRON_RIGHT} />
</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 />
}
/>
<FloatingCardView.Content>
{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>
) : 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>
</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>
)}
</FloatingCardView.Content>
</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>
);
}

View file

@ -1,7 +1,5 @@
import { useSyncPopouts } from "@/video/components/hooks/useSyncPopouts";
import { EpisodeSelectionPopout } from "@/video/components/popouts/EpisodeSelectionPopout";
import { SourceSelectionPopout } from "@/video/components/popouts/SourceSelectionPopout";
import { CaptionSelectionPopout } from "@/video/components/popouts/CaptionSelectionPopout";
import { SettingsPopout } from "@/video/components/popouts/SettingsPopout";
import { useVideoPlayerDescriptor } from "@/video/state/hooks";
import { useControls } from "@/video/state/logic/controls";
@ -14,8 +12,6 @@ import "./Popouts.css";
function ShowPopout(props: { popoutId: string | null; onClose: () => void }) {
const popoutMap = {
source: <SourceSelectionPopout />,
captions: <CaptionSelectionPopout />,
settings: <SettingsPopout />,
episodes: <EpisodeSelectionPopout />,
};

View file

@ -1,49 +1,29 @@
import { FloatingCardView } from "@/components/popout/FloatingCard";
import { FloatingDragHandle } from "@/components/popout/FloatingDragHandle";
import { FloatingView } from "@/components/popout/FloatingView";
import { useFloatingRouter } from "@/hooks/useFloatingRouter";
import { DownloadAction } from "@/video/components/actions/list-entries/DownloadAction";
import { CaptionsSelectionAction } from "../actions/CaptionsSelectionAction";
import { SourceSelectionAction } from "../actions/SourceSelectionAction";
import { CaptionsSelectionAction } from "@/video/components/actions/list-entries/CaptionsSelectionAction";
import { SourceSelectionAction } from "@/video/components/actions/list-entries/SourceSelectionAction";
import { CaptionSelectionPopout } from "./CaptionSelectionPopout";
import { PopoutSection } from "./PopoutUtils";
import { SourceSelectionPopout } from "./SourceSelectionPopout";
function TestPopout(props: { router: ReturnType<typeof useFloatingRouter> }) {
const isCollapsed = props.router.isLoaded("embed");
return (
<div>
<p onClick={() => props.router.navigate("/")}>go back</p>
<p>{isCollapsed ? "opened" : "closed"}</p>
<p onClick={() => props.router.navigate("/source/embed")}>Open</p>
</div>
);
}
export function SettingsPopout() {
const floatingRouter = useFloatingRouter();
const { pageProps, navigate, isLoaded, isActive } = floatingRouter;
const { pageProps, navigate } = floatingRouter;
return (
<>
<FloatingView {...pageProps("/")} width={320}>
<PopoutSection>
<FloatingDragHandle />
<FloatingCardView.Content>
<DownloadAction />
<SourceSelectionAction onClick={() => navigate("/source")} />
<CaptionsSelectionAction onClick={() => navigate("/captions")} />
</PopoutSection>
</FloatingView>
<FloatingView
active={isActive("source")}
show={isLoaded("source")}
height={500}
width={320}
>
{/* <TestPopout router={floatingRouter} /> */}
<SourceSelectionPopout />
</FloatingView>
<FloatingView {...pageProps("captions")} height={500} width={320}>
<CaptionSelectionPopout />
</FloatingCardView.Content>
</FloatingView>
<SourceSelectionPopout router={floatingRouter} prefix="source" />
<CaptionSelectionPopout router={floatingRouter} prefix="captions" />
</>
);
}

View file

@ -1,5 +1,5 @@
import { useMemo, useRef, useState } from "react";
import { Icon, Icons } from "@/components/Icon";
import { Icons } from "@/components/Icon";
import { useLoading } from "@/hooks/useLoading";
import { Loading } from "@/components/layout/Loading";
import { IconPatch } from "@/components/buttons/IconPatch";
@ -15,7 +15,10 @@ import { runEmbedScraper, runProvider } from "@/backend/helpers/run";
import { MWProviderScrapeResult } from "@/backend/helpers/provider";
import { useTranslation } from "react-i18next";
import { MWEmbed, MWEmbedType } from "@/backend/helpers/embed";
import { PopoutListEntry, PopoutSection } from "./PopoutUtils";
import { FloatingCardView } from "@/components/popout/FloatingCard";
import { FloatingView } from "@/components/popout/FloatingView";
import { useFloatingRouter } from "@/hooks/useFloatingRouter";
import { PopoutListEntry } from "./PopoutUtils";
interface EmbedEntryProps {
name: string;
@ -49,7 +52,10 @@ export function EmbedEntry(props: EmbedEntryProps) {
);
}
export function SourceSelectionPopout() {
export function SourceSelectionPopout(props: {
router: ReturnType<typeof useFloatingRouter>;
prefix: string;
}) {
const { t } = useTranslation();
const descriptor = useVideoPlayerDescriptor();
@ -66,7 +72,6 @@ export function SourceSelectionPopout() {
const [selectedProvider, setSelectedProvider] = useState<string | null>(null);
const [scrapeResult, setScrapeResult] =
useState<MWProviderScrapeResult | null>(null);
const showingProvider = !!selectedProvider;
const selectedProviderPopulated = useMemo(
() => providers.find((v) => v.id === selectedProvider) ?? null,
[providers, selectedProvider]
@ -106,6 +111,7 @@ export function SourceSelectionPopout() {
if (!providerId) {
providerRef.current = null;
setSelectedProvider(null);
props.router.navigate(`/${props.prefix}/source`);
return;
}
@ -135,16 +141,9 @@ export function SourceSelectionPopout() {
});
providerRef.current = providerId;
setSelectedProvider(providerId);
props.router.navigate(`/${props.prefix}/source/embeds`);
};
const titlePositionClass = useMemo(() => {
const offset = !showingProvider ? "left-0" : "left-10";
return [
"absolute w-full transition-[left,opacity] duration-200",
offset,
].join(" ");
}, [showingProvider]);
const visibleEmbeds = useMemo(() => {
const embeds = scrapeResult?.embeds || [];
@ -174,45 +173,43 @@ export function SourceSelectionPopout() {
return (
<>
<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",
!showingProvider ? "pointer-events-none opacity-0" : "opacity-1",
].join(" ")}
onClick={() => selectProvider()}
type="button"
>
<Icon icon={Icons.CHEVRON_LEFT} />
</button>
<span
className={[
titlePositionClass,
showingProvider ? "opacity-1" : "opacity-0",
].join(" ")}
>
{selectedProviderPopulated?.displayName ?? ""}
</span>
<span
className={[
titlePositionClass,
!showingProvider ? "opacity-1" : "opacity-0",
].join(" ")}
>
{t("videoPlayer.popouts.sources")}
</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",
showingProvider
? "max-h-full border-t"
: "max-h-0 overflow-hidden py-0",
].join(" ")}
>
{/* List providers */}
<FloatingView
{...props.router.pageProps(props.prefix)}
width={320}
height={500}
>
<FloatingCardView.Header
title={t("videoPlayer.popouts.sources")}
description="What provider do you want to use?"
goBack={() => props.router.navigate("/")}
/>
<FloatingCardView.Content>
{providers.map((v) => (
<PopoutListEntry
key={v.id}
onClick={() => {
selectProvider(v.id);
}}
>
{v.displayName}
</PopoutListEntry>
))}
</FloatingCardView.Content>
</FloatingView>
{/* List embeds */}
<FloatingView
{...props.router.pageProps(`embeds`)}
width={320}
height={500}
>
<FloatingCardView.Header
title={selectedProviderPopulated?.displayName ?? ""}
description="Choose which video to view"
goBack={() => props.router.navigate(`/${props.prefix}`)}
/>
<FloatingCardView.Content>
{loading ? (
<div className="flex h-full w-full items-center justify-center">
<Loading />
@ -268,22 +265,8 @@ export function SourceSelectionPopout() {
)}
</>
)}
</PopoutSection>
<PopoutSection className="relative h-full overflow-y-auto">
<div>
{providers.map((v) => (
<PopoutListEntry
key={v.id}
onClick={() => {
selectProvider(v.id);
}}
>
{v.displayName}
</PopoutListEntry>
))}
</div>
</PopoutSection>
</div>
</FloatingCardView.Content>
</FloatingView>
</>
);
}