mirror of
https://github.com/streamyfin/streamyfin.git
synced 2026-05-31 11:08:26 +01:00
feat: KSPlayer as an option for iOS + other improvements (#1266)
This commit is contained in:
committed by
GitHub
parent
d1795c9df8
commit
74d86b5d12
@@ -58,7 +58,7 @@ export const HorizontalScroll = <T,>(
|
||||
|
||||
if (!data || loading) {
|
||||
return (
|
||||
<View className='px-4 mb-2'>
|
||||
<View className='px-4'>
|
||||
<View className='bg-neutral-950 h-24 w-full rounded-md mb-2' />
|
||||
<View className='bg-neutral-950 h-10 w-full rounded-md mb-1' />
|
||||
</View>
|
||||
|
||||
42
components/common/SectionHeader.tsx
Normal file
42
components/common/SectionHeader.tsx
Normal file
@@ -0,0 +1,42 @@
|
||||
import { TouchableOpacity, View } from "react-native";
|
||||
import { Colors } from "@/constants/Colors";
|
||||
import { Text } from "./Text";
|
||||
|
||||
type Props = {
|
||||
title: string;
|
||||
actionLabel?: string;
|
||||
actionDisabled?: boolean;
|
||||
onPressAction?: () => void;
|
||||
};
|
||||
|
||||
export const SectionHeader: React.FC<Props> = ({
|
||||
title,
|
||||
actionLabel,
|
||||
actionDisabled = false,
|
||||
onPressAction,
|
||||
}) => {
|
||||
const shouldShowAction = Boolean(actionLabel) && Boolean(onPressAction);
|
||||
|
||||
return (
|
||||
<View className='px-4 flex flex-row items-center justify-between mb-2'>
|
||||
<Text className='text-lg font-bold text-neutral-100'>{title}</Text>
|
||||
{shouldShowAction && (
|
||||
<TouchableOpacity
|
||||
onPress={onPressAction}
|
||||
disabled={actionDisabled}
|
||||
accessibilityRole='button'
|
||||
accessibilityLabel={actionLabel}
|
||||
className='py-1 pl-3'
|
||||
>
|
||||
<Text
|
||||
style={{
|
||||
color: actionDisabled ? "rgba(255,255,255,0.4)" : Colors.primary,
|
||||
}}
|
||||
>
|
||||
{actionLabel}
|
||||
</Text>
|
||||
</TouchableOpacity>
|
||||
)}
|
||||
</View>
|
||||
);
|
||||
};
|
||||
@@ -16,6 +16,10 @@ export const itemRouter = (item: BaseItemDto, from: string) => {
|
||||
return `/(auth)/(tabs)/${from}/livetv`;
|
||||
}
|
||||
|
||||
if ("CollectionType" in item && item.CollectionType === "music") {
|
||||
return `/(auth)/(tabs)/(libraries)/music/${item.Id}`;
|
||||
}
|
||||
|
||||
if (item.Type === "Series") {
|
||||
return `/(auth)/(tabs)/${from}/series/${item.Id}`;
|
||||
}
|
||||
@@ -50,6 +54,13 @@ export const getItemNavigation = (item: BaseItemDto, _from: string) => {
|
||||
};
|
||||
}
|
||||
|
||||
if ("CollectionType" in item && item.CollectionType === "music") {
|
||||
return {
|
||||
pathname: "/music/[libraryId]" as const,
|
||||
params: { libraryId: item.Id! },
|
||||
};
|
||||
}
|
||||
|
||||
if (item.Type === "Series") {
|
||||
return {
|
||||
pathname: "/series/[id]" as const,
|
||||
@@ -99,6 +110,25 @@ export const TouchableItemRouter: React.FC<PropsWithChildren<Props>> = ({
|
||||
|
||||
const from = (segments as string[])[2] || "(home)";
|
||||
|
||||
const handlePress = useCallback(() => {
|
||||
// For offline mode, we still need to use query params
|
||||
if (isOffline) {
|
||||
const url = `${itemRouter(item, from)}&offline=true`;
|
||||
router.push(url as any);
|
||||
return;
|
||||
}
|
||||
|
||||
// Force music libraries to navigate via the explicit string route.
|
||||
// This avoids losing the dynamic [libraryId] param when going through a nested navigator.
|
||||
if ("CollectionType" in item && item.CollectionType === "music") {
|
||||
router.push(itemRouter(item, from) as any);
|
||||
return;
|
||||
}
|
||||
|
||||
const navigation = getItemNavigation(item, from);
|
||||
router.push(navigation as any);
|
||||
}, [from, isOffline, item, router]);
|
||||
|
||||
const showActionSheet = useCallback(() => {
|
||||
if (
|
||||
!(
|
||||
@@ -108,13 +138,14 @@ export const TouchableItemRouter: React.FC<PropsWithChildren<Props>> = ({
|
||||
)
|
||||
)
|
||||
return;
|
||||
const options = [
|
||||
|
||||
const options: string[] = [
|
||||
"Mark as Played",
|
||||
"Mark as Not Played",
|
||||
isFavorite ? "Unmark as Favorite" : "Mark as Favorite",
|
||||
"Cancel",
|
||||
];
|
||||
const cancelButtonIndex = 3;
|
||||
const cancelButtonIndex = options.length - 1;
|
||||
|
||||
showActionSheetWithOptions(
|
||||
{
|
||||
@@ -131,28 +162,24 @@ export const TouchableItemRouter: React.FC<PropsWithChildren<Props>> = ({
|
||||
}
|
||||
},
|
||||
);
|
||||
}, [showActionSheetWithOptions, isFavorite, markAsPlayedStatus]);
|
||||
}, [
|
||||
showActionSheetWithOptions,
|
||||
isFavorite,
|
||||
markAsPlayedStatus,
|
||||
toggleFavorite,
|
||||
]);
|
||||
|
||||
if (
|
||||
from === "(home)" ||
|
||||
from === "(search)" ||
|
||||
from === "(libraries)" ||
|
||||
from === "(favorites)"
|
||||
from === "(favorites)" ||
|
||||
from === "(watchlists)"
|
||||
)
|
||||
return (
|
||||
<TouchableOpacity
|
||||
onLongPress={showActionSheet}
|
||||
onPress={() => {
|
||||
if (isOffline) {
|
||||
// For offline mode, we still need to use query params
|
||||
const url = `${itemRouter(item, from)}&offline=true`;
|
||||
router.push(url as any);
|
||||
return;
|
||||
}
|
||||
|
||||
const navigation = getItemNavigation(item, from);
|
||||
router.push(navigation as any);
|
||||
}}
|
||||
onPress={handlePress}
|
||||
{...props}
|
||||
>
|
||||
{children}
|
||||
|
||||
Reference in New Issue
Block a user