Delete duplicate code and unify source selecting views

There were two because one was used on the atoms settings menu and the other was in place of ScrapePart prior to video playback
This commit is contained in:
Pas 2025-10-19 18:11:45 -06:00
parent 9a6bb041b2
commit 28d03a06a5
2 changed files with 14 additions and 162 deletions

View file

@ -21,6 +21,7 @@ export interface SourceSelectionViewProps {
export interface EmbedSelectionViewProps {
id: string;
sourceId: string | null;
onBack?: () => void;
}
export function EmbedOption(props: {
@ -60,7 +61,11 @@ export function EmbedOption(props: {
);
}
export function EmbedSelectionView({ sourceId, id }: EmbedSelectionViewProps) {
export function EmbedSelectionView({
sourceId,
id,
onBack,
}: EmbedSelectionViewProps) {
const { t } = useTranslation();
const router = useOverlayRouter(id);
const { run, watching, notfound, loading, items, errored } =
@ -126,7 +131,7 @@ export function EmbedSelectionView({ sourceId, id }: EmbedSelectionViewProps) {
return (
<>
<Menu.BackLink onClick={() => router.navigate("/source")}>
<Menu.BackLink onClick={onBack || (() => router.navigate("/source"))}>
{sourceName}
</Menu.BackLink>
<Menu.Section>{content}</Menu.Section>

View file

@ -1,164 +1,20 @@
import { ScrapeMedia } from "@p-stream/providers";
import React, { ReactNode, useEffect, useMemo, useRef } from "react";
import React from "react";
import { useTranslation } from "react-i18next";
import { getCachedMetadata } from "@/backend/helpers/providerApi";
import { Loading } from "@/components/layout/Loading";
import {
useEmbedScraping,
useSourceScraping,
} from "@/components/player/hooks/useSourceSelection";
EmbedSelectionView,
SourceSelectionView,
} from "@/components/player/atoms/settings/SourceSelectingView";
import { Menu } from "@/components/player/internals/ContextMenu";
import { SelectableLink } from "@/components/player/internals/ContextMenu/Links";
import { usePreferencesStore } from "@/stores/preferences";
// Embed option component
function EmbedOption(props: {
embedId: string;
url: string;
sourceId: string;
routerId: string;
}) {
const { t } = useTranslation();
const unknownEmbedName = t("player.menus.sources.unknownOption");
const embedName = useMemo(() => {
if (!props.embedId) return unknownEmbedName;
const sourceMeta = getCachedMetadata().find((s) => s.id === props.embedId);
return sourceMeta?.name ?? unknownEmbedName;
}, [props.embedId, unknownEmbedName]);
const { run, errored, loading } = useEmbedScraping(
props.routerId,
props.sourceId,
props.url,
props.embedId,
);
return (
<SelectableLink loading={loading} error={errored} onClick={run}>
<span className="flex flex-col">
<span>{embedName}</span>
</span>
</SelectableLink>
);
}
// Embed selection view (when a source is selected)
function EmbedSelectionView(props: {
sourceId: string;
routerId: string;
onBack: () => void;
}) {
const { t } = useTranslation();
const { run, notfound, loading, items, errored } = useSourceScraping(
props.sourceId,
props.routerId,
);
const sourceName = useMemo(() => {
if (!props.sourceId) return "...";
const sourceMeta = getCachedMetadata().find((s) => s.id === props.sourceId);
return sourceMeta?.name ?? "...";
}, [props.sourceId]);
const lastSourceId = useRef<string | null>(null);
useEffect(() => {
if (lastSourceId.current === props.sourceId) return;
lastSourceId.current = props.sourceId;
if (!props.sourceId) return;
run();
}, [run, props.sourceId]);
let content: ReactNode = null;
if (loading)
content = (
<Menu.TextDisplay noIcon>
<Loading />
</Menu.TextDisplay>
);
else if (notfound)
content = (
<Menu.TextDisplay
title={t("player.menus.sources.noStream.title") ?? undefined}
>
{t("player.menus.sources.noStream.text")}
</Menu.TextDisplay>
);
else if (items?.length === 0)
content = (
<Menu.TextDisplay
title={t("player.menus.sources.noEmbeds.title") ?? undefined}
>
{t("player.menus.sources.noEmbeds.text")}
</Menu.TextDisplay>
);
else if (errored)
content = (
<Menu.TextDisplay
title={t("player.menus.sources.failed.title") ?? undefined}
>
{t("player.menus.sources.failed.text")}
</Menu.TextDisplay>
);
else if (items && props.sourceId)
content = items.map((v) => (
<EmbedOption
key={`${v.embedId}-${v.url}`}
embedId={v.embedId}
url={v.url}
routerId={props.routerId}
sourceId={props.sourceId}
/>
));
return (
<>
<Menu.BackLink onClick={props.onBack}>{sourceName}</Menu.BackLink>
<Menu.Section>{content}</Menu.Section>
</>
);
}
// Main source selection view
export function SourceSelectPart(props: { media: ScrapeMedia }) {
export function SourceSelectPart(_props: { media: ScrapeMedia }) {
const { t } = useTranslation();
const [selectedSourceId, setSelectedSourceId] = React.useState<string | null>(
null,
);
const routerId = "manualSourceSelect";
const preferredSourceOrder = usePreferencesStore((s) => s.sourceOrder);
const enableSourceOrder = usePreferencesStore((s) => s.enableSourceOrder);
const sources = useMemo(() => {
const metaType = props.media.type;
if (!metaType) return [];
const allSources = getCachedMetadata()
.filter((v) => v.type === "source")
.filter((v) => v.mediaTypes?.includes(metaType));
if (!enableSourceOrder || preferredSourceOrder.length === 0) {
return allSources;
}
// Sort sources according to preferred order
const orderedSources = [];
const remainingSources = [...allSources];
// Add sources in preferred order
for (const sourceId of preferredSourceOrder) {
const sourceIndex = remainingSources.findIndex((s) => s.id === sourceId);
if (sourceIndex !== -1) {
orderedSources.push(remainingSources[sourceIndex]);
remainingSources.splice(sourceIndex, 1);
}
}
// Add remaining sources that weren't in the preferred order
orderedSources.push(...remainingSources);
return orderedSources;
}, [props.media.type, preferredSourceOrder, enableSourceOrder]);
if (selectedSourceId) {
return (
@ -167,7 +23,7 @@ export function SourceSelectPart(props: { media: ScrapeMedia }) {
<Menu.CardWithScrollable>
<EmbedSelectionView
sourceId={selectedSourceId}
routerId={routerId}
id={routerId}
onBack={() => setSelectedSourceId(null)}
/>
</Menu.CardWithScrollable>
@ -181,16 +37,7 @@ export function SourceSelectPart(props: { media: ScrapeMedia }) {
<div className="w-full max-w-md h-[50vh] flex flex-col">
<Menu.CardWithScrollable>
<Menu.Title>{t("player.menus.sources.title")}</Menu.Title>
<Menu.Section className="pb-4">
{sources.map((v) => (
<SelectableLink
key={v.id}
onClick={() => setSelectedSourceId(v.id)}
>
{v.name}
</SelectableLink>
))}
</Menu.Section>
<SourceSelectionView id={routerId} onChoose={setSelectedSourceId} />
</Menu.CardWithScrollable>
</div>
</div>