Files
streamyfin/components/item/ItemPeopleSections.tsx
Uruk ac0d3fcb86 refactor(item): improves top people display logic
Improves the logic for displaying the top people in the item sections.

The previous implementation simply sliced the people array which
could result in duplicate people being displayed.

This change ensures that the top people displayed are unique by
filtering duplicates based on their id, limiting the number of
displayed people to three.
2026-02-10 19:13:44 +01:00

89 lines
2.4 KiB
TypeScript

import type {
BaseItemDto,
BaseItemPerson,
} from "@jellyfin/sdk/lib/generated-client/models";
import type React from "react";
import { useCallback, useEffect, useMemo, useState } from "react";
import { InteractionManager, View, type ViewProps } from "react-native";
import { MoreMoviesWithActor } from "@/components/MoreMoviesWithActor";
import { CastAndCrew } from "@/components/series/CastAndCrew";
import { useItemPeopleQuery } from "@/hooks/useItemPeopleQuery";
import { useOfflineMode } from "@/providers/OfflineModeProvider";
interface Props extends ViewProps {
item: BaseItemDto;
}
export const ItemPeopleSections: React.FC<Props> = ({ item, ...props }) => {
const isOffline = useOfflineMode();
const [enabled, setEnabled] = useState(false);
useEffect(() => {
if (isOffline) return;
const task = InteractionManager.runAfterInteractions(() =>
setEnabled(true),
);
return () => task.cancel();
}, [isOffline]);
const { data, isLoading } = useItemPeopleQuery(
item.Id,
enabled && !isOffline,
);
const people = useMemo(() => (Array.isArray(data) ? data : []), [data]);
const itemWithPeople = useMemo(() => {
return { ...item, People: people } as BaseItemDto;
}, [item, people]);
const topPeople = useMemo(() => {
const seen = new Set<string>();
const unique: BaseItemPerson[] = [];
for (const person of people) {
if (person.Id && !seen.has(person.Id)) {
seen.add(person.Id);
unique.push(person);
}
if (unique.length >= 3) break;
}
return unique;
}, [people]);
const renderActorSection = useCallback(
(person: BaseItemPerson, idx: number, total: number) => {
if (!person.Id) return null;
const spacingClassName = idx === total - 1 ? undefined : "mb-2";
return (
<MoreMoviesWithActor
key={person.Id}
currentItem={item}
actorId={person.Id}
actorName={person.Name}
className={spacingClassName}
/>
);
},
[item],
);
if (isOffline || !enabled) return null;
const shouldSpaceCastAndCrew = topPeople.length > 0;
return (
<View {...props}>
<CastAndCrew
item={itemWithPeople}
loading={isLoading}
className={shouldSpaceCastAndCrew ? "mb-2" : undefined}
/>
{topPeople.map((person, idx) =>
renderActorSection(person, idx, topPeople.length),
)}
</View>
);
};