fix: Jellyseerr discovery crash (#1032)

This commit is contained in:
stenlan
2025-09-03 21:41:35 +02:00
committed by GitHub
parent 6308375438
commit c778956a52
48 changed files with 90 additions and 120 deletions

View File

@@ -12,7 +12,7 @@ import { apiAtom, userAtom } from "@/providers/JellyfinProvider";
import { useSettings } from "@/utils/atoms/settings"; import { useSettings } from "@/utils/atoms/settings";
export default function page() { export default function page() {
const [settings, updateSettings, pluginSettings] = useSettings(null); const { settings, updateSettings, pluginSettings } = useSettings();
const user = useAtomValue(userAtom); const user = useAtomValue(userAtom);
const api = useAtomValue(apiAtom); const api = useAtomValue(apiAtom);

View File

@@ -3,7 +3,7 @@ import { JellyseerrSettings } from "@/components/settings/Jellyseerr";
import { useSettings } from "@/utils/atoms/settings"; import { useSettings } from "@/utils/atoms/settings";
export default function page() { export default function page() {
const [_settings, _updateSettings, pluginSettings] = useSettings(null); const { pluginSettings } = useSettings();
return ( return (
<DisabledSetting <DisabledSetting

View File

@@ -21,7 +21,7 @@ export default function page() {
const { t } = useTranslation(); const { t } = useTranslation();
const [settings, updateSettings, pluginSettings] = useSettings(null); const { settings, updateSettings, pluginSettings } = useSettings();
const queryClient = useQueryClient(); const queryClient = useQueryClient();
const [value, setValue] = useState<string>(settings?.marlinServerUrl || ""); const [value, setValue] = useState<string>(settings?.marlinServerUrl || "");

View File

@@ -15,7 +15,7 @@ import { COMPANY_LOGO_IMAGE_FILTER } from "@/utils/jellyseerr/src/components/Dis
export default function page() { export default function page() {
const local = useLocalSearchParams(); const local = useLocalSearchParams();
const { jellyseerrApi } = useJellyseerr(); const { jellyseerrApi, isJellyseerrMovieOrTvResult } = useJellyseerr();
const { companyId, image, type } = local as unknown as { const { companyId, image, type } = local as unknown as {
companyId: string; companyId: string;
@@ -53,7 +53,10 @@ export default function page() {
uniqBy( uniqBy(
data?.pages data?.pages
?.filter((p) => p?.results.length) ?.filter((p) => p?.results.length)
.flatMap((p) => p?.results ?? []), .flatMap(
(p) =>
p?.results.filter((r) => isJellyseerrMovieOrTvResult(r)) ?? [],
),
"id", "id",
) ?? [], ) ?? [],
[data], [data],
@@ -98,9 +101,7 @@ export default function page() {
}} }}
/> />
} }
renderItem={(item, _index) => ( renderItem={(item, _index) => <JellyseerrPoster item={item} />}
<JellyseerrPoster item={item as MovieResult | TvResult} />
)}
/> />
); );
} }

View File

@@ -8,14 +8,10 @@ import ParallaxSlideShow from "@/components/jellyseerr/ParallaxSlideShow";
import JellyseerrPoster from "@/components/posters/JellyseerrPoster"; import JellyseerrPoster from "@/components/posters/JellyseerrPoster";
import { Endpoints, useJellyseerr } from "@/hooks/useJellyseerr"; import { Endpoints, useJellyseerr } from "@/hooks/useJellyseerr";
import { DiscoverSliderType } from "@/utils/jellyseerr/server/constants/discover"; import { DiscoverSliderType } from "@/utils/jellyseerr/server/constants/discover";
import {
type MovieResult,
type TvResult,
} from "@/utils/jellyseerr/server/models/Search";
export default function page() { export default function page() {
const local = useLocalSearchParams(); const local = useLocalSearchParams();
const { jellyseerrApi } = useJellyseerr(); const { jellyseerrApi, isJellyseerrMovieOrTvResult } = useJellyseerr();
const { genreId, name, type } = local as unknown as { const { genreId, name, type } = local as unknown as {
genreId: string; genreId: string;
@@ -51,7 +47,10 @@ export default function page() {
uniqBy( uniqBy(
data?.pages data?.pages
?.filter((p) => p?.results.length) ?.filter((p) => p?.results.length)
.flatMap((p) => p?.results ?? []), .flatMap(
(p) =>
p?.results.filter((r) => isJellyseerrMovieOrTvResult(r)) ?? [],
),
"id", "id",
) ?? [], ) ?? [],
[data], [data],
@@ -62,7 +61,7 @@ export default function page() {
jellyseerrApi jellyseerrApi
? flatData.map((r) => ? flatData.map((r) =>
jellyseerrApi.imageProxy( jellyseerrApi.imageProxy(
(r as TvResult | MovieResult).backdropPath, r.backdropPath,
"w1920_and_h800_multi_faces", "w1920_and_h800_multi_faces",
), ),
) )
@@ -92,9 +91,7 @@ export default function page() {
{name} {name}
</Text> </Text>
} }
renderItem={(item, _index) => ( renderItem={(item, _index) => <JellyseerrPoster item={item} />}
<JellyseerrPoster item={item as MovieResult | TvResult} />
)}
/> />
); );
} }

View File

@@ -10,10 +10,6 @@ import { OverviewText } from "@/components/OverviewText";
import JellyseerrPoster from "@/components/posters/JellyseerrPoster"; import JellyseerrPoster from "@/components/posters/JellyseerrPoster";
import { useJellyseerr } from "@/hooks/useJellyseerr"; import { useJellyseerr } from "@/hooks/useJellyseerr";
import type { PersonCreditCast } from "@/utils/jellyseerr/server/models/Person"; import type { PersonCreditCast } from "@/utils/jellyseerr/server/models/Person";
import type {
MovieResult,
TvResult,
} from "@/utils/jellyseerr/server/models/Search";
export default function page() { export default function page() {
const local = useLocalSearchParams(); const local = useLocalSearchParams();
@@ -106,9 +102,7 @@ export default function page() {
MainContent={() => ( MainContent={() => (
<OverviewText text={data?.details?.biography} className='mt-4' /> <OverviewText text={data?.details?.biography} className='mt-4' />
)} )}
renderItem={(item, _index) => ( renderItem={(item, _index) => <JellyseerrPoster item={item} />}
<JellyseerrPoster item={item as MovieResult | TvResult} />
)}
/> />
); );
} }

View File

@@ -9,7 +9,7 @@ const DropdownMenu = !Platform.isTV ? require("zeego/dropdown-menu") : null;
import { useTranslation } from "react-i18next"; import { useTranslation } from "react-i18next";
export default function IndexLayout() { export default function IndexLayout() {
const [settings, updateSettings, pluginSettings] = useSettings(null); const { settings, updateSettings, pluginSettings } = useSettings();
const { t } = useTranslation(); const { t } = useTranslation();

View File

@@ -19,7 +19,7 @@ export default function index() {
const [api] = useAtom(apiAtom); const [api] = useAtom(apiAtom);
const [user] = useAtom(userAtom); const [user] = useAtom(userAtom);
const queryClient = useQueryClient(); const queryClient = useQueryClient();
const [settings] = useSettings(null); const { settings } = useSettings();
const { t } = useTranslation(); const { t } = useTranslation();

View File

@@ -71,7 +71,7 @@ export default function search() {
const [api] = useAtom(apiAtom); const [api] = useAtom(apiAtom);
const [settings] = useSettings(null); const { settings } = useSettings();
const { jellyseerrApi } = useJellyseerr(); const { jellyseerrApi } = useJellyseerr();
const [jellyseerrOrderBy, setJellyseerrOrderBy] = const [jellyseerrOrderBy, setJellyseerrOrderBy] =
useState<JellyseerrSearchSort>( useState<JellyseerrSearchSort>(

View File

@@ -27,7 +27,7 @@ export const NativeTabs = withLayoutContext<
>(Navigator); >(Navigator);
export default function TabLayout() { export default function TabLayout() {
const [settings] = useSettings(null); const { settings } = useSettings();
const { t } = useTranslation(); const { t } = useTranslation();
const router = useRouter(); const router = useRouter();

View File

@@ -97,7 +97,7 @@ export default function page() {
/** Playback position in ticks. */ /** Playback position in ticks. */
playbackPosition?: string; playbackPosition?: string;
}>(); }>();
const [_settings] = useSettings(null); useSettings();
const offline = offlineStr === "true"; const offline = offlineStr === "true";
const playbackManager = usePlaybackManager(); const playbackManager = usePlaybackManager();
@@ -748,7 +748,6 @@ export default function page() {
setAspectRatio={setAspectRatio} setAspectRatio={setAspectRatio}
setScaleFactor={setScaleFactor} setScaleFactor={setScaleFactor}
isVlc isVlc
api={api}
downloadedFiles={downloadedFiles} downloadedFiles={downloadedFiles}
/> />
)} )}

View File

@@ -230,7 +230,7 @@ const queryClient = new QueryClient({
}); });
function Layout() { function Layout() {
const [settings] = useSettings(null); const { settings } = useSettings();
const [user] = useAtom(userAtom); const [user] = useAtom(userAtom);
const [api] = useAtom(apiAtom); const [api] = useAtom(apiAtom);
const appState = useRef(AppState.currentState); const appState = useRef(AppState.currentState);

View File

@@ -61,7 +61,7 @@ export const DownloadItems: React.FC<DownloadProps> = ({
const [api] = useAtom(apiAtom); const [api] = useAtom(apiAtom);
const [user] = useAtom(userAtom); const [user] = useAtom(userAtom);
const [queue, _setQueue] = useAtom(queueAtom); const [queue, _setQueue] = useAtom(queueAtom);
const [settings] = useSettings(null); const { settings } = useSettings();
const [downloadUnwatchedOnly, setDownloadUnwatchedOnly] = useState(false); const [downloadUnwatchedOnly, setDownloadUnwatchedOnly] = useState(false);
const { processes, startBackgroundDownload, getDownloadedItems } = const { processes, startBackgroundDownload, getDownloadedItems } =

View File

@@ -54,7 +54,7 @@ interface ItemContentProps {
export const ItemContent: React.FC<ItemContentProps> = React.memo( export const ItemContent: React.FC<ItemContentProps> = React.memo(
({ item, isOffline }) => { ({ item, isOffline }) => {
const [api] = useAtom(apiAtom); const [api] = useAtom(apiAtom);
const [settings] = useSettings(null); const { settings } = useSettings();
const { orientation } = useOrientation(); const { orientation } = useOrientation();
const navigation = useNavigation(); const navigation = useNavigation();
const insets = useSafeAreaInsets(); const insets = useSafeAreaInsets();

View File

@@ -67,7 +67,7 @@ export const PlayButton: React.FC<Props> = ({
const startColor = useSharedValue(colorAtom); const startColor = useSharedValue(colorAtom);
const widthProgress = useSharedValue(0); const widthProgress = useSharedValue(0);
const colorChangeProgress = useSharedValue(0); const colorChangeProgress = useSharedValue(0);
const [settings, updateSettings] = useSettings(null); const { settings, updateSettings } = useSettings();
const lightHapticFeedback = useHaptic("light"); const lightHapticFeedback = useHaptic("light");
const goToPlayer = useCallback( const goToPlayer = useCallback(

View File

@@ -44,7 +44,7 @@ export const PlayButton: React.FC<Props> = ({
const startColor = useSharedValue(colorAtom); const startColor = useSharedValue(colorAtom);
const widthProgress = useSharedValue(0); const widthProgress = useSharedValue(0);
const colorChangeProgress = useSharedValue(0); const colorChangeProgress = useSharedValue(0);
const [settings] = useSettings(null); const { settings } = useSettings();
const lightHapticFeedback = useHaptic("light"); const lightHapticFeedback = useHaptic("light");
const goToPlayer = useCallback( const goToPlayer = useCallback(

View File

@@ -10,6 +10,7 @@ import {
Permission, Permission,
} from "@/utils/jellyseerr/server/lib/permissions"; } from "@/utils/jellyseerr/server/lib/permissions";
import type { MovieDetails } from "@/utils/jellyseerr/server/models/Movie"; import type { MovieDetails } from "@/utils/jellyseerr/server/models/Movie";
import { PersonCreditCast } from "@/utils/jellyseerr/server/models/Person";
import type { import type {
MovieResult, MovieResult,
TvResult, TvResult,
@@ -17,7 +18,7 @@ import type {
import type { TvDetails } from "@/utils/jellyseerr/server/models/Tv"; import type { TvDetails } from "@/utils/jellyseerr/server/models/Tv";
interface Props extends TouchableOpacityProps { interface Props extends TouchableOpacityProps {
result?: MovieResult | TvResult | MovieDetails | TvDetails; result?: MovieResult | TvResult | MovieDetails | TvDetails | PersonCreditCast;
mediaTitle: string; mediaTitle: string;
releaseYear: number; releaseYear: number;
canRequest: boolean; canRequest: boolean;

View File

@@ -26,7 +26,7 @@ import { itemRouter } from "../common/TouchableItemRouter";
interface Props extends ViewProps {} interface Props extends ViewProps {}
export const LargeMovieCarousel: React.FC<Props> = ({ ...props }) => { export const LargeMovieCarousel: React.FC<Props> = ({ ...props }) => {
const [settings] = useSettings(null); const { settings } = useSettings();
const ref = React.useRef<ICarouselInstance>(null); const ref = React.useRef<ICarouselInstance>(null);
const progress = useSharedValue<number>(0); const progress = useSharedValue<number>(0);

View File

@@ -11,16 +11,12 @@ import {
useJellyseerr, useJellyseerr,
} from "@/hooks/useJellyseerr"; } from "@/hooks/useJellyseerr";
import { DiscoverSliderType } from "@/utils/jellyseerr/server/constants/discover"; import { DiscoverSliderType } from "@/utils/jellyseerr/server/constants/discover";
import type {
MovieResult,
TvResult,
} from "@/utils/jellyseerr/server/models/Search";
const MovieTvSlide: React.FC<SlideProps & ViewProps> = ({ const MovieTvSlide: React.FC<SlideProps & ViewProps> = ({
slide, slide,
...props ...props
}) => { }) => {
const { jellyseerrApi } = useJellyseerr(); const { jellyseerrApi, isJellyseerrMovieOrTvResult } = useJellyseerr();
const { data, fetchNextPage, hasNextPage } = useInfiniteQuery({ const { data, fetchNextPage, hasNextPage } = useInfiniteQuery({
queryKey: ["jellyseerr", "discover", slide.id], queryKey: ["jellyseerr", "discover", slide.id],
@@ -69,7 +65,9 @@ const MovieTvSlide: React.FC<SlideProps & ViewProps> = ({
uniqBy( uniqBy(
data?.pages data?.pages
?.filter((p) => p?.results.length) ?.filter((p) => p?.results.length)
.flatMap((p) => p?.results), .flatMap((p) =>
p?.results.filter((r) => isJellyseerrMovieOrTvResult(r)),
),
"id", "id",
), ),
[data], [data],
@@ -86,12 +84,7 @@ const MovieTvSlide: React.FC<SlideProps & ViewProps> = ({
onEndReached={() => { onEndReached={() => {
if (hasNextPage) fetchNextPage(); if (hasNextPage) fetchNextPage();
}} }}
renderItem={(item) => ( renderItem={(item) => <JellyseerrPoster item={item} key={item?.id} />}
<JellyseerrPoster
item={item as MovieResult | TvResult}
key={item?.id}
/>
)}
/> />
) )
); );

View File

@@ -41,7 +41,7 @@ const icons: Record<CollectionType, IconName> = {
export const LibraryItemCard: React.FC<Props> = ({ library, ...props }) => { export const LibraryItemCard: React.FC<Props> = ({ library, ...props }) => {
const [api] = useAtom(apiAtom); const [api] = useAtom(apiAtom);
const [user] = useAtom(userAtom); const [user] = useAtom(userAtom);
const [settings] = useSettings(null); const { settings } = useSettings();
const { t } = useTranslation(); const { t } = useTranslation();

View File

@@ -20,6 +20,7 @@ import { MediaStatus } from "@/utils/jellyseerr/server/constants/media";
import type MediaRequest from "@/utils/jellyseerr/server/entity/MediaRequest"; import type MediaRequest from "@/utils/jellyseerr/server/entity/MediaRequest";
import type { DownloadingItem } from "@/utils/jellyseerr/server/lib/downloadtracker"; import type { DownloadingItem } from "@/utils/jellyseerr/server/lib/downloadtracker";
import type { MovieDetails } from "@/utils/jellyseerr/server/models/Movie"; import type { MovieDetails } from "@/utils/jellyseerr/server/models/Movie";
import { PersonCreditCast } from "@/utils/jellyseerr/server/models/Person";
import type { import type {
MovieResult, MovieResult,
TvResult, TvResult,
@@ -27,7 +28,7 @@ import type {
import type { TvDetails } from "@/utils/jellyseerr/server/models/Tv"; import type { TvDetails } from "@/utils/jellyseerr/server/models/Tv";
interface Props extends ViewProps { interface Props extends ViewProps {
item?: MovieResult | TvResult | MovieDetails | TvDetails; item?: MovieResult | TvResult | MovieDetails | TvDetails | PersonCreditCast;
horizontal?: boolean; horizontal?: boolean;
showDownloadInfo?: boolean; showDownloadInfo?: boolean;
mediaRequest?: MediaRequest; mediaRequest?: MediaRequest;

View File

@@ -12,7 +12,7 @@ interface Props extends ViewProps {}
export const AppLanguageSelector: React.FC<Props> = () => { export const AppLanguageSelector: React.FC<Props> = () => {
const isTv = Platform.isTV; const isTv = Platform.isTV;
const [settings, updateSettings] = useSettings(null); const { settings, updateSettings } = useSettings();
const { t } = useTranslation(); const { t } = useTranslation();
if (isTv) return null; if (isTv) return null;

View File

@@ -17,7 +17,7 @@ export const AudioToggles: React.FC<Props> = ({ ...props }) => {
const isTv = Platform.isTV; const isTv = Platform.isTV;
const media = useMedia(); const media = useMedia();
const [_, __, pluginSettings] = useSettings(null); const { pluginSettings } = useSettings();
const { settings, updateSettings } = media; const { settings, updateSettings } = media;
const cultures = media.cultures; const cultures = media.cultures;
const { t } = useTranslation(); const { t } = useTranslation();

View File

@@ -4,7 +4,7 @@ import { ListGroup } from "../list/ListGroup";
import { ListItem } from "../list/ListItem"; import { ListItem } from "../list/ListItem";
export const ChromecastSettings: React.FC = ({ ...props }) => { export const ChromecastSettings: React.FC = ({ ...props }) => {
const [settings, updateSettings] = useSettings(null); const { settings, updateSettings } = useSettings();
return ( return (
<View {...props}> <View {...props}>
<ListGroup title={"Chromecast"}> <ListGroup title={"Chromecast"}>

View File

@@ -7,7 +7,7 @@ import { ListGroup } from "../list/ListGroup";
import { ListItem } from "../list/ListItem"; import { ListItem } from "../list/ListItem";
export const Dashboard = () => { export const Dashboard = () => {
const [settings, _updateSettings] = useSettings(null); const { settings } = useSettings();
const { sessions = [] } = useSessions({} as useSessionsProps); const { sessions = [] } = useSessions({} as useSessionsProps);
const router = useRouter(); const router = useRouter();

View File

@@ -7,7 +7,7 @@ import { ListGroup } from "../list/ListGroup";
import { ListItem } from "../list/ListItem"; import { ListItem } from "../list/ListItem";
export default function DownloadSettings({ ...props }) { export default function DownloadSettings({ ...props }) {
const [settings, updateSettings, pluginSettings] = useSettings(null); const { settings, updateSettings, pluginSettings } = useSettings();
const { t } = useTranslation(); const { t } = useTranslation();
const allDisabled = useMemo( const allDisabled = useMemo(

View File

@@ -13,7 +13,7 @@ interface Props extends ViewProps {}
export const GestureControls: React.FC<Props> = ({ ...props }) => { export const GestureControls: React.FC<Props> = ({ ...props }) => {
const { t } = useTranslation(); const { t } = useTranslation();
const [settings, updateSettings, pluginSettings] = useSettings(null); const { settings, updateSettings, pluginSettings } = useSettings();
const disabled = useMemo( const disabled = useMemo(
() => () =>

View File

@@ -64,13 +64,7 @@ export const HomeIndex = () => {
const user = useAtomValue(userAtom); const user = useAtomValue(userAtom);
const [loading, setLoading] = useState(false); const [loading, setLoading] = useState(false);
const [ const { settings, refreshStreamyfinPluginSettings } = useSettings();
settings,
_updateSettings,
_pluginSettings,
_setPluginSettings,
refreshStreamyfinPluginSettings,
] = useSettings(null);
const navigation = useNavigation(); const navigation = useNavigation();

View File

@@ -20,7 +20,7 @@ export const JellyseerrSettings = () => {
const { t } = useTranslation(); const { t } = useTranslation();
const [user] = useAtom(userAtom); const [user] = useAtom(userAtom);
const [settings, updateSettings, _pluginSettings] = useSettings(null); const { settings, updateSettings } = useSettings();
const [jellyseerrPassword, setJellyseerrPassword] = useState< const [jellyseerrPassword, setJellyseerrPassword] = useState<
string | undefined string | undefined

View File

@@ -28,7 +28,7 @@ export const useMedia = () => {
}; };
export const MediaProvider = ({ children }: { children: ReactNode }) => { export const MediaProvider = ({ children }: { children: ReactNode }) => {
const [settings, updateSettings] = useSettings(null); const { settings, updateSettings } = useSettings();
const api = useAtomValue(apiAtom); const api = useAtomValue(apiAtom);
const queryClient = useQueryClient(); const queryClient = useQueryClient();

View File

@@ -13,7 +13,7 @@ interface Props extends ViewProps {}
export const MediaToggles: React.FC<Props> = ({ ...props }) => { export const MediaToggles: React.FC<Props> = ({ ...props }) => {
const { t } = useTranslation(); const { t } = useTranslation();
const [settings, updateSettings, pluginSettings] = useSettings(null); const { settings, updateSettings, pluginSettings } = useSettings();
const disabled = useMemo( const disabled = useMemo(
() => () =>

View File

@@ -23,7 +23,7 @@ import { ListItem } from "../list/ListItem";
export const OtherSettings: React.FC = () => { export const OtherSettings: React.FC = () => {
const router = useRouter(); const router = useRouter();
const [settings, updateSettings, pluginSettings] = useSettings(null); const { settings, updateSettings, pluginSettings } = useSettings();
const { t } = useTranslation(); const { t } = useTranslation();

View File

@@ -6,7 +6,7 @@ import { ListGroup } from "../list/ListGroup";
import { ListItem } from "../list/ListItem"; import { ListItem } from "../list/ListItem";
export const PluginSettings = () => { export const PluginSettings = () => {
const [settings, _updateSettings] = useSettings(null); const { settings } = useSettings();
const router = useRouter(); const router = useRouter();

View File

@@ -20,7 +20,7 @@ export const SubtitleToggles: React.FC<Props> = ({ ...props }) => {
const isTv = Platform.isTV; const isTv = Platform.isTV;
const media = useMedia(); const media = useMedia();
const [_, __, pluginSettings] = useSettings(null); const { pluginSettings } = useSettings();
const { settings, updateSettings } = media; const { settings, updateSettings } = media;
const cultures = media.cultures; const cultures = media.cultures;
const { t } = useTranslation(); const { t } = useTranslation();

View File

@@ -88,7 +88,7 @@ export const BottomControls: FC<BottomControlsProps> = ({
trickplayInfo, trickplayInfo,
time, time,
}) => { }) => {
const [settings] = useSettings(null); const { settings } = useSettings();
const insets = useSafeAreaInsets(); const insets = useSafeAreaInsets();
return ( return (

View File

@@ -30,7 +30,7 @@ export const CenterControls: FC<CenterControlsProps> = ({
handleSkipBackward, handleSkipBackward,
handleSkipForward, handleSkipForward,
}) => { }) => {
const [settings] = useSettings(null); const { settings } = useSettings();
const insets = useSafeAreaInsets(); const insets = useSafeAreaInsets();
return ( return (

View File

@@ -16,7 +16,7 @@ export interface ContinueWatchingOverlayProps {
const ContinueWatchingOverlay: React.FC<ContinueWatchingOverlayProps> = ({ const ContinueWatchingOverlay: React.FC<ContinueWatchingOverlayProps> = ({
goToNextItem, goToNextItem,
}) => { }) => {
const [settings] = useSettings(null); const { settings } = useSettings();
const router = useRouter(); const router = useRouter();
return settings.autoPlayEpisodeCount >= return settings.autoPlayEpisodeCount >=

View File

@@ -116,7 +116,7 @@ export const Controls: FC<Props> = ({
api = null, api = null,
downloadedFiles = undefined, downloadedFiles = undefined,
}) => { }) => {
const [settings, updateSettings] = useSettings(api); const { settings, updateSettings } = useSettings();
const router = useRouter(); const router = useRouter();
const lightHapticFeedback = useHaptic("light"); const lightHapticFeedback = useHaptic("light");

View File

@@ -31,7 +31,7 @@ export const GestureOverlay = ({
onSkipForward, onSkipForward,
onSkipBackward, onSkipBackward,
}: Props) => { }: Props) => {
const [settings] = useSettings(null); const { settings } = useSettings();
const lightHaptic = useHaptic("light"); const lightHaptic = useHaptic("light");
const [feedback, setFeedback] = useState<FeedbackState>({ const [feedback, setFeedback] = useState<FeedbackState>({

View File

@@ -70,7 +70,7 @@ export const HeaderControls: FC<HeaderControlsProps> = ({
setVideoAspectRatio, setVideoAspectRatio,
setVideoScaleFactor, setVideoScaleFactor,
}) => { }) => {
const [settings] = useSettings(null); const { settings } = useSettings();
const router = useRouter(); const router = useRouter();
const insets = useSafeAreaInsets(); const insets = useSafeAreaInsets();
const { width: screenWidth } = useWindowDimensions(); const { width: screenWidth } = useWindowDimensions();

View File

@@ -20,7 +20,7 @@ export function useVideoNavigation({
seek, seek,
play, play,
}: UseVideoNavigationProps) { }: UseVideoNavigationProps) {
const [settings] = useSettings(null); const { settings } = useSettings();
const lightHapticFeedback = useHaptic("light"); const lightHapticFeedback = useHaptic("light");
const wasPlayingRef = useRef(false); const wasPlayingRef = useRef(false);

View File

@@ -14,7 +14,7 @@ export type HapticFeedbackType =
| "error"; | "error";
export const useHaptic = (feedbackType: HapticFeedbackType = "selection") => { export const useHaptic = (feedbackType: HapticFeedbackType = "selection") => {
const [settings] = useSettings(null); const { settings } = useSettings();
const isTv = Platform.isTV; const isTv = Platform.isTV;
const isDisabled = const isDisabled =
isTv || isTv ||

View File

@@ -14,7 +14,7 @@ import { useQueryClient } from "@tanstack/react-query";
import { t } from "i18next"; import { t } from "i18next";
import { useCallback, useMemo } from "react"; import { useCallback, useMemo } from "react";
import { toast } from "sonner-native"; import { toast } from "sonner-native";
import { defaultValues, Settings } from "@/utils/atoms/settings"; import { useSettings } from "@/utils/atoms/settings";
import type { RTRating } from "@/utils/jellyseerr/server/api/rating/rottentomatoes"; import type { RTRating } from "@/utils/jellyseerr/server/api/rating/rottentomatoes";
import { import {
IssueStatus, IssueStatus,
@@ -40,6 +40,7 @@ import type { UserResultsResponse } from "@/utils/jellyseerr/server/interfaces/a
import type { MovieDetails } from "@/utils/jellyseerr/server/models/Movie"; import type { MovieDetails } from "@/utils/jellyseerr/server/models/Movie";
import type { import type {
CombinedCredit, CombinedCredit,
PersonCreditCast,
PersonDetails, PersonDetails,
} from "@/utils/jellyseerr/server/models/Person"; } from "@/utils/jellyseerr/server/models/Person";
import type { import type {
@@ -416,10 +417,8 @@ export class JellyseerrApi {
const jellyseerrUserAtom = atom(storage.get<JellyseerrUser>(JELLYSEERR_USER)); const jellyseerrUserAtom = atom(storage.get<JellyseerrUser>(JELLYSEERR_USER));
export const useJellyseerr = ( export const useJellyseerr = () => {
settings: Settings = defaultValues, const { settings, updateSettings } = useSettings();
updateSettings: (update: Partial<Settings>) => void = () => {},
) => {
const [jellyseerrUser, setJellyseerrUser] = useAtom(jellyseerrUserAtom); const [jellyseerrUser, setJellyseerrUser] = useAtom(jellyseerrUserAtom);
const queryClient = useQueryClient(); const queryClient = useQueryClient();
@@ -468,49 +467,47 @@ export const useJellyseerr = (
[jellyseerrApi], [jellyseerrApi],
); );
const isJellyseerrResult = ( const isJellyseerrMovieOrTvResult = (
items: any | null | undefined, items: any | null | undefined,
): items is Results => { ): items is MovieResult | TvResult => {
return ( return (
items && items &&
Object.hasOwn(items, "mediaType") && Object.hasOwn(items, "mediaType") &&
Object.values(MediaType).includes(items.mediaType as MediaType) (items.mediaType === MediaType.MOVIE || items.mediaType === MediaType.TV)
); );
}; };
const getTitle = ( const getTitle = (
item?: TvResult | TvDetails | MovieResult | MovieDetails, item?: TvResult | TvDetails | MovieResult | MovieDetails | PersonCreditCast,
) => { ) => {
return isJellyseerrResult(item) return isJellyseerrMovieOrTvResult(item)
? item.mediaType === MediaType.MOVIE ? item.mediaType === MediaType.MOVIE
? item?.title ? item?.title
: item?.name : item?.name
: item?.mediaInfo.mediaType === MediaType.MOVIE : item?.mediaInfo?.mediaType === MediaType.MOVIE
? (item as MovieDetails)?.title ? (item as MovieDetails)?.title
: (item as TvDetails)?.name; : (item as TvDetails)?.name;
}; };
const getYear = ( const getYear = (
item?: TvResult | TvDetails | MovieResult | MovieDetails, item?: TvResult | TvDetails | MovieResult | MovieDetails | PersonCreditCast,
) => { ) => {
return new Date( return new Date(
(isJellyseerrResult(item) (isJellyseerrMovieOrTvResult(item)
? item.mediaType === MediaType.MOVIE ? item.mediaType === MediaType.MOVIE
? item?.releaseDate ? item?.releaseDate
: item?.firstAirDate : item?.firstAirDate
: item?.mediaInfo.mediaType === MediaType.MOVIE : item?.mediaInfo?.mediaType === MediaType.MOVIE
? (item as MovieDetails)?.releaseDate ? (item as MovieDetails)?.releaseDate
: (item as TvDetails)?.firstAirDate) || "", : (item as TvDetails)?.firstAirDate) || "",
)?.getFullYear?.(); )?.getFullYear?.();
}; };
const getMediaType = ( const getMediaType = (
item?: TvResult | TvDetails | MovieResult | MovieDetails, item?: TvResult | TvDetails | MovieResult | MovieDetails | PersonCreditCast,
): MediaType => { ): MediaType => {
return isJellyseerrResult(item) return isJellyseerrMovieOrTvResult(item)
? item.mediaType === "movie" ? (item.mediaType as MediaType)
? MediaType.MOVIE
: MediaType.TV
: item?.mediaInfo?.mediaType; : item?.mediaInfo?.mediaType;
}; };
@@ -528,7 +525,7 @@ export const useJellyseerr = (
jellyseerrUser, jellyseerrUser,
setJellyseerrUser, setJellyseerrUser,
clearAllJellyseerData, clearAllJellyseerData,
isJellyseerrResult, isJellyseerrMovieOrTvResult,
getTitle, getTitle,
getYear, getYear,
getMediaType, getMediaType,

View File

@@ -87,7 +87,7 @@ function useDownloadProvider() {
const { saveSeriesPrimaryImage } = useDownloadHelper(); const { saveSeriesPrimaryImage } = useDownloadHelper();
const { saveImage } = useImageStorage(); const { saveImage } = useImageStorage();
const [processes, setProcesses] = useAtom<JobStatus[]>(processesAtom); const [processes, setProcesses] = useAtom<JobStatus[]>(processesAtom);
const [settings] = useSettings(null); const { settings } = useSettings();
const successHapticFeedback = useHaptic("success"); const successHapticFeedback = useHaptic("success");
/// Cant use the background downloader callback. As its not triggered if size is unknown. /// Cant use the background downloader callback. As its not triggered if size is unknown.

View File

@@ -79,17 +79,8 @@ export const JellyfinProvider: React.FC<{ children: ReactNode }> = ({
const [user, setUser] = useAtom(userAtom); const [user, setUser] = useAtom(userAtom);
const [isPolling, setIsPolling] = useState<boolean>(false); const [isPolling, setIsPolling] = useState<boolean>(false);
const [secret, setSecret] = useState<string | null>(null); const [secret, setSecret] = useState<string | null>(null);
const [ const { setPluginSettings, refreshStreamyfinPluginSettings } = useSettings();
_settings, const { clearAllJellyseerData, setJellyseerrUser } = useJellyseerr();
_updateSettings,
_pluginSettings,
setPluginSettings,
refreshStreamyfinPluginSettings,
] = useSettings(api);
const { clearAllJellyseerData, setJellyseerrUser } = useJellyseerr(
_settings || {},
_updateSettings,
);
const headers = useMemo(() => { const headers = useMemo(() => {
if (!deviceId) return {}; if (!deviceId) return {};

View File

@@ -9,6 +9,7 @@ import {
hasPermission, hasPermission,
Permission, Permission,
} from "@/utils/jellyseerr/server/lib/permissions"; } from "@/utils/jellyseerr/server/lib/permissions";
import { PersonCreditCast } from "@/utils/jellyseerr/server/models/Person";
import type { import type {
MovieResult, MovieResult,
TvResult, TvResult,
@@ -18,7 +19,7 @@ import type { MovieDetails } from "../jellyseerr/server/models/Movie";
import type { TvDetails } from "../jellyseerr/server/models/Tv"; import type { TvDetails } from "../jellyseerr/server/models/Tv";
export const useJellyseerrCanRequest = ( export const useJellyseerrCanRequest = (
item?: MovieResult | TvResult | MovieDetails | TvDetails, item?: MovieResult | TvResult | MovieDetails | TvDetails | PersonCreditCast,
) => { ) => {
const { jellyseerrUser } = useJellyseerr(); const { jellyseerrUser } = useJellyseerr();

View File

@@ -56,7 +56,7 @@ export const useJobProcessor = () => {
const [queue, setQueue] = useAtom(queueAtom); const [queue, setQueue] = useAtom(queueAtom);
const [running, setRunning] = useAtom(runningAtom); const [running, setRunning] = useAtom(runningAtom);
const [processes] = useAtom<JobStatus[]>(processesAtom); const [processes] = useAtom<JobStatus[]>(processesAtom);
const [settings] = useSettings(null); const { settings } = useSettings();
useEffect(() => { useEffect(() => {
if ( if (

View File

@@ -1,4 +1,3 @@
import type { Api } from "@jellyfin/sdk";
import { import {
type BaseItemKind, type BaseItemKind,
type CultureDto, type CultureDto,
@@ -7,11 +6,12 @@ import {
type SortOrder, type SortOrder,
SubtitlePlaybackMode, SubtitlePlaybackMode,
} from "@jellyfin/sdk/lib/generated-client"; } from "@jellyfin/sdk/lib/generated-client";
import { atom, useAtom } from "jotai"; import { atom, useAtom, useAtomValue } from "jotai";
import { useCallback, useEffect, useMemo } from "react"; import { useCallback, useEffect, useMemo } from "react";
import { Platform } from "react-native"; import { Platform } from "react-native";
import { BITRATES, type Bitrate } from "@/components/BitrateSelector"; import { BITRATES, type Bitrate } from "@/components/BitrateSelector";
import * as ScreenOrientation from "@/packages/expo-screen-orientation"; import * as ScreenOrientation from "@/packages/expo-screen-orientation";
import { apiAtom } from "@/providers/JellyfinProvider";
import { writeInfoLog } from "@/utils/log"; import { writeInfoLog } from "@/utils/log";
import { storage } from "../mmkv"; import { storage } from "../mmkv";
@@ -278,7 +278,8 @@ export const pluginSettingsAtom = atom<PluginLockableSettings | undefined>(
loadPluginSettings(), loadPluginSettings(),
); );
export const useSettings = (api: Api | null) => { export const useSettings = () => {
const api = useAtomValue(apiAtom);
const [_settings, setSettings] = useAtom(settingsAtom); const [_settings, setSettings] = useAtom(settingsAtom);
const [pluginSettings, _setPluginSettings] = useAtom(pluginSettingsAtom); const [pluginSettings, _setPluginSettings] = useAtom(pluginSettingsAtom);
@@ -302,11 +303,11 @@ export const useSettings = (api: Api | null) => {
return; return;
} }
const settings = await api.getStreamyfinPluginConfig().then( const settings = await api.getStreamyfinPluginConfig().then(
({ data }: any) => { ({ data }) => {
writeInfoLog("Got plugin settings", data?.settings); writeInfoLog("Got plugin settings", data?.settings);
return data?.settings; return data?.settings;
}, },
(_err: any) => undefined, (_err) => undefined,
); );
setPluginSettings(settings); setPluginSettings(settings);
return settings; return settings;
@@ -367,11 +368,11 @@ export const useSettings = (api: Api | null) => {
}; };
}, [_settings, pluginSettings]); }, [_settings, pluginSettings]);
return [ return {
settings, settings,
updateSettings, updateSettings,
pluginSettings, pluginSettings,
setPluginSettings, setPluginSettings,
refreshStreamyfinPluginSettings, refreshStreamyfinPluginSettings,
] as const; };
}; };