mirror of
https://github.com/streamyfin/streamyfin.git
synced 2026-01-15 23:59:08 +00:00
Replaces generic `any` type casts with specific route pattern types to enhance type checking and prevent invalid navigation paths. Adds backward compatibility alias for the navigation utility function to maintain existing API contracts while improving code organization.
122 lines
3.2 KiB
TypeScript
122 lines
3.2 KiB
TypeScript
import { useActionSheet } from "@expo/react-native-action-sheet";
|
|
import type { BaseItemDto } from "@jellyfin/sdk/lib/generated-client/models";
|
|
import { useRouter, useSegments } from "expo-router";
|
|
import { type PropsWithChildren, useCallback } from "react";
|
|
import { TouchableOpacity, type TouchableOpacityProps } from "react-native";
|
|
import { useFavorite } from "@/hooks/useFavorite";
|
|
import { useMarkAsPlayed } from "@/hooks/useMarkAsPlayed";
|
|
import { getCurrentTab } from "@/utils/navigation";
|
|
|
|
interface Props extends TouchableOpacityProps {
|
|
item: BaseItemDto;
|
|
isOffline?: boolean;
|
|
}
|
|
|
|
export const itemRouter = (item: BaseItemDto, from: string) => {
|
|
if ("CollectionType" in item && item.CollectionType === "livetv") {
|
|
return `/(auth)/(tabs)/${from}/livetv`;
|
|
}
|
|
|
|
if (item.Type === "Series") {
|
|
return `/(auth)/(tabs)/${from}/series/${item.Id}`;
|
|
}
|
|
|
|
if (item.Type === "Person") {
|
|
return `/(auth)/(tabs)/${from}/persons/${item.Id}`;
|
|
}
|
|
|
|
if (item.Type === "BoxSet") {
|
|
return `/(auth)/(tabs)/${from}/collections/${item.Id}`;
|
|
}
|
|
|
|
if (item.Type === "UserView") {
|
|
return `/(auth)/(tabs)/${from}/collections/${item.Id}`;
|
|
}
|
|
|
|
if (item.Type === "CollectionFolder") {
|
|
return `/(auth)/(tabs)/(libraries)/${item.Id}`;
|
|
}
|
|
|
|
if (item.Type === "Playlist") {
|
|
return `/(auth)/(tabs)/(libraries)/${item.Id}`;
|
|
}
|
|
|
|
return `/(auth)/(tabs)/${from}/items/page?id=${item.Id}`;
|
|
};
|
|
|
|
// Export alias for backward compatibility
|
|
export const getItemNavigation = itemRouter;
|
|
|
|
export const TouchableItemRouter: React.FC<PropsWithChildren<Props>> = ({
|
|
item,
|
|
isOffline = false,
|
|
children,
|
|
...props
|
|
}) => {
|
|
const router = useRouter();
|
|
const segments = useSegments();
|
|
const { showActionSheetWithOptions } = useActionSheet();
|
|
const markAsPlayedStatus = useMarkAsPlayed([item]);
|
|
const { isFavorite, toggleFavorite } = useFavorite(item);
|
|
|
|
const from = getCurrentTab(segments as string[]);
|
|
|
|
const showActionSheet = useCallback(() => {
|
|
if (
|
|
!(
|
|
item.Type === "Movie" ||
|
|
item.Type === "Episode" ||
|
|
item.Type === "Series"
|
|
)
|
|
)
|
|
return;
|
|
const options = [
|
|
"Mark as Played",
|
|
"Mark as Not Played",
|
|
isFavorite ? "Unmark as Favorite" : "Mark as Favorite",
|
|
"Cancel",
|
|
];
|
|
const cancelButtonIndex = 3;
|
|
|
|
showActionSheetWithOptions(
|
|
{
|
|
options,
|
|
cancelButtonIndex,
|
|
},
|
|
async (selectedIndex) => {
|
|
if (selectedIndex === 0) {
|
|
await markAsPlayedStatus(true);
|
|
} else if (selectedIndex === 1) {
|
|
await markAsPlayedStatus(false);
|
|
} else if (selectedIndex === 2) {
|
|
toggleFavorite();
|
|
}
|
|
},
|
|
);
|
|
}, [showActionSheetWithOptions, isFavorite, markAsPlayedStatus]);
|
|
|
|
if (
|
|
from === "(home)" ||
|
|
from === "(search)" ||
|
|
from === "(libraries)" ||
|
|
from === "(favorites)"
|
|
)
|
|
return (
|
|
<TouchableOpacity
|
|
onLongPress={showActionSheet}
|
|
onPress={() => {
|
|
let url = itemRouter(item, from);
|
|
if (isOffline) {
|
|
url += `&offline=true`;
|
|
}
|
|
router.push(url as `/(auth)/(tabs)/${string}`);
|
|
}}
|
|
{...props}
|
|
>
|
|
{children}
|
|
</TouchableOpacity>
|
|
);
|
|
|
|
return null;
|
|
};
|