fix: navigatte to item
Some checks failed
🤖 Android APK Build (Phone + TV) / 🏗️ Build Android APK (phone) (push) Has been cancelled
🤖 Android APK Build (Phone + TV) / 🏗️ Build Android APK (tv) (push) Has been cancelled
🤖 iOS IPA Build (Phone + TV) / 🏗️ Build iOS IPA (phone) (push) Has been cancelled
🔒 Lockfile Consistency Check / 🔍 Check bun.lock and package.json consistency (push) Has been cancelled
🛡️ CodeQL Analysis / 🔎 Analyze with CodeQL (actions) (push) Has been cancelled
🛡️ CodeQL Analysis / 🔎 Analyze with CodeQL (javascript-typescript) (push) Has been cancelled
🏷️🔀Merge Conflict Labeler / 🏷️ Labeling Merge Conflicts (push) Has been cancelled
🚦 Security & Quality Gate / 🚑 Expo Doctor Check (push) Has been cancelled
🚦 Security & Quality Gate / 🔍 Lint & Test (check) (push) Has been cancelled
🚦 Security & Quality Gate / 📝 Validate PR Title (push) Has been cancelled
🚦 Security & Quality Gate / 🔍 Vulnerable Dependencies (push) Has been cancelled
🚦 Security & Quality Gate / 🔍 Lint & Test (format) (push) Has been cancelled
🚦 Security & Quality Gate / 🔍 Lint & Test (lint) (push) Has been cancelled
🚦 Security & Quality Gate / 🔍 Lint & Test (typecheck) (push) Has been cancelled

This commit is contained in:
Fredrik Burmester
2025-09-29 15:02:24 +02:00
parent 2273b7be0a
commit 0ec44add7d

View File

@@ -7,9 +7,10 @@ import {
import { useQuery } from "@tanstack/react-query"; import { useQuery } from "@tanstack/react-query";
import { Image } from "expo-image"; import { Image } from "expo-image";
import { LinearGradient } from "expo-linear-gradient"; import { LinearGradient } from "expo-linear-gradient";
import { useRouter } from "expo-router";
import { useAtomValue } from "jotai"; import { useAtomValue } from "jotai";
import { useCallback, useEffect, useMemo, useState } from "react"; import { useCallback, useEffect, useMemo, useState } from "react";
import { Dimensions, Pressable, View } from "react-native"; import { Dimensions, Pressable, TouchableOpacity, View } from "react-native";
import { Gesture, GestureDetector } from "react-native-gesture-handler"; import { Gesture, GestureDetector } from "react-native-gesture-handler";
import Animated, { import Animated, {
Easing, Easing,
@@ -25,6 +26,7 @@ import { apiAtom, userAtom } from "@/providers/JellyfinProvider";
import { useSettings } from "@/utils/atoms/settings"; import { useSettings } from "@/utils/atoms/settings";
import { getLogoImageUrlById } from "@/utils/jellyfin/image/getLogoImageUrlById"; import { getLogoImageUrlById } from "@/utils/jellyfin/image/getLogoImageUrlById";
import { ItemImage } from "./common/ItemImage"; import { ItemImage } from "./common/ItemImage";
import { getItemNavigation } from "./common/TouchableItemRouter";
import type { SelectedOptions } from "./ItemContent"; import type { SelectedOptions } from "./ItemContent";
import { PlayButton } from "./PlayButton"; import { PlayButton } from "./PlayButton";
import { PlayedStatus } from "./PlayedStatus"; import { PlayedStatus } from "./PlayedStatus";
@@ -150,6 +152,7 @@ export const AppleTVCarousel: React.FC<AppleTVCarouselProps> = ({
const api = useAtomValue(apiAtom); const api = useAtomValue(apiAtom);
const user = useAtomValue(userAtom); const user = useAtomValue(userAtom);
const { isConnected, serverConnected } = useNetworkStatus(); const { isConnected, serverConnected } = useNetworkStatus();
const router = useRouter();
const [currentIndex, setCurrentIndex] = useState(initialIndex); const [currentIndex, setCurrentIndex] = useState(initialIndex);
const translateX = useSharedValue(-currentIndex * screenWidth); const translateX = useSharedValue(-currentIndex * screenWidth);
@@ -301,6 +304,14 @@ export const AppleTVCarousel: React.FC<AppleTVCarouselProps> = ({
[hasItems, items, onItemChange, translateX], [hasItems, items, onItemChange, translateX],
); );
const navigateToItem = useCallback(
(item: BaseItemDto) => {
const navigation = getItemNavigation(item, "(home)");
router.push(navigation as any);
},
[router],
);
const panGesture = Gesture.Pan() const panGesture = Gesture.Pan()
.activeOffsetX([-PAN_ACTIVE_OFFSET, PAN_ACTIVE_OFFSET]) .activeOffsetX([-PAN_ACTIVE_OFFSET, PAN_ACTIVE_OFFSET])
.onUpdate((event) => { .onUpdate((event) => {
@@ -591,7 +602,8 @@ export const AppleTVCarousel: React.FC<AppleTVCarouselProps> = ({
{/* Logo Section */} {/* Logo Section */}
{itemLogoUrl && ( {itemLogoUrl && (
<View <TouchableOpacity
onPress={() => navigateToItem(item)}
style={{ style={{
position: "absolute", position: "absolute",
bottom: LOGO_BOTTOM_POSITION, bottom: LOGO_BOTTOM_POSITION,
@@ -611,7 +623,7 @@ export const AppleTVCarousel: React.FC<AppleTVCarouselProps> = ({
}} }}
contentFit='contain' contentFit='contain'
/> />
</View> </TouchableOpacity>
)} )}
{/* Type and Genres Section */} {/* Type and Genres Section */}
@@ -625,41 +637,56 @@ export const AppleTVCarousel: React.FC<AppleTVCarouselProps> = ({
alignItems: "center", alignItems: "center",
}} }}
> >
<Animated.Text <TouchableOpacity onPress={() => navigateToItem(item)}>
style={{ <Animated.Text
color: `rgba(255, 255, 255, ${TEXT_OPACITY})`, style={{
fontSize: GENRES_FONT_SIZE, color: `rgba(255, 255, 255, ${TEXT_OPACITY})`,
fontWeight: "500", fontSize: GENRES_FONT_SIZE,
textAlign: "center", fontWeight: "500",
textShadowColor: TEXT_SHADOW_COLOR, textAlign: "center",
textShadowOffset: { width: 0, height: 1 }, textShadowColor: TEXT_SHADOW_COLOR,
textShadowRadius: TEXT_SHADOW_RADIUS, textShadowOffset: { width: 0, height: 1 },
}} textShadowRadius: TEXT_SHADOW_RADIUS,
> }}
{(() => { >
const typeLabel = {(() => {
item.Type === "Series" let typeLabel = "";
? "TV Show"
: item.Type === "Movie"
? "Movie"
: item.Type || "";
const genres = if (item.Type === "Episode") {
item.Genres && item.Genres.length > 0 // For episodes, show season and episode number
? item.Genres.slice(0, MAX_GENRES_COUNT).join(" • ") const season = item.ParentIndexNumber;
: ""; const episode = item.IndexNumber;
if (season && episode) {
typeLabel = `S${season} • E${episode}`;
} else {
typeLabel = "Episode";
}
} else {
typeLabel =
item.Type === "Series"
? "TV Show"
: item.Type === "Movie"
? "Movie"
: item.Type || "";
}
if (typeLabel && genres) { const genres =
return `${typeLabel}${genres}`; item.Genres && item.Genres.length > 0
} else if (typeLabel) { ? item.Genres.slice(0, MAX_GENRES_COUNT).join(" • ")
return typeLabel; : "";
} else if (genres) {
return genres; if (typeLabel && genres) {
} else { return `${typeLabel}${genres}`;
return ""; } else if (typeLabel) {
} return typeLabel;
})()} } else if (genres) {
</Animated.Text> return genres;
} else {
return "";
}
})()}
</Animated.Text>
</TouchableOpacity>
</View> </View>
{/* Controls Section */} {/* Controls Section */}