mirror of
https://github.com/streamyfin/streamyfin.git
synced 2026-05-28 09:38:25 +01:00
wip
This commit is contained in:
@@ -1,8 +1,8 @@
|
||||
import { RoundButton } from "@/components/RoundButton";
|
||||
import { useFavorite } from "@/hooks/useFavorite";
|
||||
import type { BaseItemDto } from "@jellyfin/sdk/lib/generated-client";
|
||||
import type { FC } from "react";
|
||||
import { View, type ViewProps } from "react-native";
|
||||
import { RoundButton } from "@/components/RoundButton";
|
||||
import { useFavorite } from "@/hooks/useFavorite";
|
||||
|
||||
interface Props extends ViewProps {
|
||||
item: BaseItemDto;
|
||||
|
||||
@@ -1,7 +1,9 @@
|
||||
import type { MediaSourceInfo } from "@jellyfin/sdk/lib/generated-client/models";
|
||||
import { useMemo } from "react";
|
||||
import { Platform, TouchableOpacity, View } from "react-native";
|
||||
const DropdownMenu = !Platform.isTV ? require("zeego/dropdown-menu") : null;
|
||||
import { TouchableOpacity, View } from "react-native";
|
||||
|
||||
const DropdownMenu = require("zeego/dropdown-menu");
|
||||
|
||||
import { useTranslation } from "react-i18next";
|
||||
import { Text } from "./common/Text";
|
||||
|
||||
@@ -17,7 +19,6 @@ export const AudioTrackSelector: React.FC<Props> = ({
|
||||
selected,
|
||||
...props
|
||||
}) => {
|
||||
if (Platform.isTV) return null;
|
||||
const audioStreams = useMemo(
|
||||
() => source?.MediaStreams?.filter((x) => x.Type === "Audio"),
|
||||
[source],
|
||||
|
||||
@@ -1,5 +1,7 @@
|
||||
import { Platform, TouchableOpacity, View } from "react-native";
|
||||
const DropdownMenu = !Platform.isTV ? require("zeego/dropdown-menu") : null;
|
||||
import { TouchableOpacity, View } from "react-native";
|
||||
|
||||
const DropdownMenu = require("zeego/dropdown-menu");
|
||||
|
||||
import { useMemo } from "react";
|
||||
import { useTranslation } from "react-i18next";
|
||||
import { Text } from "./common/Text";
|
||||
@@ -58,7 +60,6 @@ export const BitrateSelector: React.FC<Props> = ({
|
||||
inverted,
|
||||
...props
|
||||
}) => {
|
||||
if (Platform.isTV) return null;
|
||||
const sorted = useMemo(() => {
|
||||
if (inverted)
|
||||
return BITRATES.sort(
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
import { useHaptic } from "@/hooks/useHaptic";
|
||||
import type React from "react";
|
||||
import { type PropsWithChildren, type ReactNode, useMemo } from "react";
|
||||
import { Platform, Text, TouchableOpacity, View } from "react-native";
|
||||
import { Text, TouchableOpacity, View } from "react-native";
|
||||
import { useHaptic } from "@/hooks/useHaptic";
|
||||
import { Loader } from "./Loader";
|
||||
|
||||
export interface ButtonProps
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
import { Feather } from "@expo/vector-icons";
|
||||
import React, { useCallback, useEffect } from "react";
|
||||
import { Platform, TouchableOpacity, type ViewProps } from "react-native";
|
||||
import { useCallback, useEffect } from "react";
|
||||
import { Platform, type ViewProps } from "react-native";
|
||||
import GoogleCast, {
|
||||
CastButton,
|
||||
CastContext,
|
||||
|
||||
@@ -1,11 +1,11 @@
|
||||
import { apiAtom } from "@/providers/JellyfinProvider";
|
||||
import { Ionicons } from "@expo/vector-icons";
|
||||
import type { BaseItemDto } from "@jellyfin/sdk/lib/generated-client/models";
|
||||
import { Image } from "expo-image";
|
||||
import { useAtomValue } from "jotai";
|
||||
import { useMemo } from "react";
|
||||
import type React from "react";
|
||||
import { useMemo } from "react";
|
||||
import { View } from "react-native";
|
||||
import { apiAtom } from "@/providers/JellyfinProvider";
|
||||
import { WatchedIndicator } from "./WatchedIndicator";
|
||||
|
||||
type ContinueWatchingPosterProps = {
|
||||
|
||||
@@ -1,11 +1,3 @@
|
||||
import { useDownload } from "@/providers/DownloadProvider";
|
||||
import { apiAtom, userAtom } from "@/providers/JellyfinProvider";
|
||||
import { queueActions, queueAtom } from "@/utils/atoms/queue";
|
||||
import { DownloadMethod, useSettings } from "@/utils/atoms/settings";
|
||||
import { getDefaultPlaySettings } from "@/utils/jellyfin/getDefaultPlaySettings";
|
||||
import { getStreamUrl } from "@/utils/jellyfin/media/getStreamUrl";
|
||||
import { saveDownloadItemInfoToDiskTmp } from "@/utils/optimize-server";
|
||||
import download from "@/utils/profiles/download";
|
||||
import Ionicons from "@expo/vector-icons/Ionicons";
|
||||
import {
|
||||
BottomSheetBackdrop,
|
||||
@@ -24,15 +16,23 @@ import type React from "react";
|
||||
import { useCallback, useMemo, useRef, useState } from "react";
|
||||
import { Alert, Platform, View, type ViewProps } from "react-native";
|
||||
import { toast } from "sonner-native";
|
||||
import { useDownload } from "@/providers/DownloadProvider";
|
||||
import { apiAtom, userAtom } from "@/providers/JellyfinProvider";
|
||||
import { queueAtom } from "@/utils/atoms/queue";
|
||||
import { DownloadMethod, useSettings } from "@/utils/atoms/settings";
|
||||
import { getDefaultPlaySettings } from "@/utils/jellyfin/getDefaultPlaySettings";
|
||||
import { getStreamUrl } from "@/utils/jellyfin/media/getStreamUrl";
|
||||
import { saveDownloadItemInfoToDiskTmp } from "@/utils/optimize-server";
|
||||
import download from "@/utils/profiles/download";
|
||||
import { AudioTrackSelector } from "./AudioTrackSelector";
|
||||
import { type Bitrate, BitrateSelector } from "./BitrateSelector";
|
||||
import { Button } from "./Button";
|
||||
import { Text } from "./common/Text";
|
||||
import { Loader } from "./Loader";
|
||||
import { MediaSourceSelector } from "./MediaSourceSelector";
|
||||
import ProgressCircle from "./ProgressCircle";
|
||||
import { RoundButton } from "./RoundButton";
|
||||
import { SubtitleTrackSelector } from "./SubtitleTrackSelector";
|
||||
import { Text } from "./common/Text";
|
||||
|
||||
interface DownloadProps extends ViewProps {
|
||||
items: BaseItemDto[];
|
||||
@@ -88,7 +88,7 @@ export const DownloadItems: React.FC<DownloadProps> = ({
|
||||
bottomSheetModalRef.current?.present();
|
||||
}, []);
|
||||
|
||||
const handleSheetChanges = useCallback((index: number) => {}, []);
|
||||
const handleSheetChanges = useCallback((_index: number) => {}, []);
|
||||
|
||||
const closeModal = useCallback(() => {
|
||||
bottomSheetModalRef.current?.dismiss();
|
||||
|
||||
@@ -1,4 +1,3 @@
|
||||
import { tc } from "@/utils/textTools";
|
||||
import type { BaseItemDto } from "@jellyfin/sdk/lib/generated-client/models";
|
||||
import type React from "react";
|
||||
import { View } from "react-native";
|
||||
|
||||
@@ -1,25 +1,3 @@
|
||||
import { AudioTrackSelector } from "@/components/AudioTrackSelector";
|
||||
import { type Bitrate, BitrateSelector } from "@/components/BitrateSelector";
|
||||
import { DownloadSingleItem } from "@/components/DownloadItem";
|
||||
import { OverviewText } from "@/components/OverviewText";
|
||||
import { ParallaxScrollView } from "@/components/ParallaxPage";
|
||||
// const PlayButton = !Platform.isTV ? require("@/components/PlayButton") : null;
|
||||
import { PlayButton } from "@/components/PlayButton";
|
||||
import { PlayedStatus } from "@/components/PlayedStatus";
|
||||
import { SimilarItems } from "@/components/SimilarItems";
|
||||
import { SubtitleTrackSelector } from "@/components/SubtitleTrackSelector";
|
||||
import { ItemImage } from "@/components/common/ItemImage";
|
||||
import { CastAndCrew } from "@/components/series/CastAndCrew";
|
||||
import { CurrentSeries } from "@/components/series/CurrentSeries";
|
||||
import { SeasonEpisodesCarousel } from "@/components/series/SeasonEpisodesCarousel";
|
||||
import useDefaultPlaySettings from "@/hooks/useDefaultPlaySettings";
|
||||
import { useImageColors } from "@/hooks/useImageColors";
|
||||
import { useOrientation } from "@/hooks/useOrientation";
|
||||
import * as ScreenOrientation from "@/packages/expo-screen-orientation";
|
||||
import { apiAtom } from "@/providers/JellyfinProvider";
|
||||
import { userAtom } from "@/providers/JellyfinProvider";
|
||||
import { useSettings } from "@/utils/atoms/settings";
|
||||
import { getLogoImageUrlById } from "@/utils/jellyfin/image/getLogoImageUrlById";
|
||||
import type {
|
||||
BaseItemDto,
|
||||
MediaSourceInfo,
|
||||
@@ -28,15 +6,37 @@ import { Image } from "expo-image";
|
||||
import { useNavigation } from "expo-router";
|
||||
import { useAtom } from "jotai";
|
||||
import React, { useEffect, useMemo, useState } from "react";
|
||||
import { Platform, View } from "react-native";
|
||||
import { View } from "react-native";
|
||||
import { useSafeAreaInsets } from "react-native-safe-area-context";
|
||||
import { AudioTrackSelector } from "@/components/AudioTrackSelector";
|
||||
import { type Bitrate, BitrateSelector } from "@/components/BitrateSelector";
|
||||
import { ItemImage } from "@/components/common/ItemImage";
|
||||
import { DownloadSingleItem } from "@/components/DownloadItem";
|
||||
import { OverviewText } from "@/components/OverviewText";
|
||||
import { ParallaxScrollView } from "@/components/ParallaxPage";
|
||||
// const PlayButton = !Platform.isTV ? require("@/components/PlayButton") : null;
|
||||
import { PlayButton } from "@/components/PlayButton";
|
||||
import { PlayedStatus } from "@/components/PlayedStatus";
|
||||
import { SimilarItems } from "@/components/SimilarItems";
|
||||
import { SubtitleTrackSelector } from "@/components/SubtitleTrackSelector";
|
||||
import { CastAndCrew } from "@/components/series/CastAndCrew";
|
||||
import { CurrentSeries } from "@/components/series/CurrentSeries";
|
||||
import { SeasonEpisodesCarousel } from "@/components/series/SeasonEpisodesCarousel";
|
||||
import useDefaultPlaySettings from "@/hooks/useDefaultPlaySettings";
|
||||
import { useImageColors } from "@/hooks/useImageColors";
|
||||
import { useOrientation } from "@/hooks/useOrientation";
|
||||
import * as ScreenOrientation from "@/packages/expo-screen-orientation";
|
||||
import { apiAtom, userAtom } from "@/providers/JellyfinProvider";
|
||||
import { useSettings } from "@/utils/atoms/settings";
|
||||
import { getLogoImageUrlById } from "@/utils/jellyfin/image/getLogoImageUrlById";
|
||||
import { AddToFavorites } from "./AddToFavorites";
|
||||
import { ItemHeader } from "./ItemHeader";
|
||||
import { ItemTechnicalDetails } from "./ItemTechnicalDetails";
|
||||
import { MediaSourceSelector } from "./MediaSourceSelector";
|
||||
import { MoreMoviesWithActor } from "./MoreMoviesWithActor";
|
||||
import { PlayInRemoteSessionButton } from "./PlayInRemoteSession";
|
||||
const Chromecast = !Platform.isTV ? require("./Chromecast") : null;
|
||||
|
||||
const Chromecast = require("./Chromecast");
|
||||
|
||||
export type SelectedOptions = {
|
||||
bitrate: Bitrate;
|
||||
@@ -85,35 +85,27 @@ export const ItemContent: React.FC<{ item: BaseItemDto }> = React.memo(
|
||||
defaultMediaSource,
|
||||
]);
|
||||
|
||||
if (!Platform.isTV) {
|
||||
useEffect(() => {
|
||||
navigation.setOptions({
|
||||
headerRight: () =>
|
||||
item && (
|
||||
<View className='flex flex-row items-center space-x-2'>
|
||||
<Chromecast.Chromecast
|
||||
background='blur'
|
||||
width={22}
|
||||
height={22}
|
||||
/>
|
||||
{item.Type !== "Program" && (
|
||||
<View className='flex flex-row items-center space-x-2'>
|
||||
{!Platform.isTV && (
|
||||
<DownloadSingleItem item={item} size='large' />
|
||||
)}
|
||||
{user?.Policy?.IsAdministrator && (
|
||||
<PlayInRemoteSessionButton item={item} size='large' />
|
||||
)}
|
||||
useEffect(() => {
|
||||
navigation.setOptions({
|
||||
headerRight: () =>
|
||||
item && (
|
||||
<View className='flex flex-row items-center space-x-2'>
|
||||
<Chromecast.Chromecast background='blur' width={22} height={22} />
|
||||
{item.Type !== "Program" && (
|
||||
<View className='flex flex-row items-center space-x-2'>
|
||||
<DownloadSingleItem item={item} size='large' />
|
||||
{user?.Policy?.IsAdministrator && (
|
||||
<PlayInRemoteSessionButton item={item} size='large' />
|
||||
)}
|
||||
|
||||
<PlayedStatus items={[item]} size='large' />
|
||||
<AddToFavorites item={item} />
|
||||
</View>
|
||||
)}
|
||||
</View>
|
||||
),
|
||||
});
|
||||
}, [item]);
|
||||
}
|
||||
<PlayedStatus items={[item]} size='large' />
|
||||
<AddToFavorites item={item} />
|
||||
</View>
|
||||
)}
|
||||
</View>
|
||||
),
|
||||
});
|
||||
}, [item]);
|
||||
|
||||
useEffect(() => {
|
||||
if (orientation !== ScreenOrientation.OrientationLock.PORTRAIT_UP)
|
||||
@@ -174,7 +166,7 @@ export const ItemContent: React.FC<{ item: BaseItemDto }> = React.memo(
|
||||
<View className='flex flex-col bg-transparent shrink'>
|
||||
<View className='flex flex-col px-4 w-full space-y-2 pt-2 mb-2 shrink'>
|
||||
<ItemHeader item={item} className='mb-4' />
|
||||
{item.Type !== "Program" && !Platform.isTV && (
|
||||
{item.Type !== "Program" && (
|
||||
<View className='flex flex-row items-center justify-start w-full h-16'>
|
||||
<BitrateSelector
|
||||
className='mr-1'
|
||||
|
||||
@@ -2,8 +2,8 @@ import type { BaseItemDto } from "@jellyfin/sdk/lib/generated-client/models";
|
||||
import type React from "react";
|
||||
import { View, type ViewProps } from "react-native";
|
||||
import { GenreTags } from "./GenreTags";
|
||||
import { Ratings } from "./Ratings";
|
||||
import { MoviesTitleHeader } from "./movies/MoviesTitleHeader";
|
||||
import { Ratings } from "./Ratings";
|
||||
import { EpisodeTitleHeader } from "./series/EpisodeTitleHeader";
|
||||
import { ItemActions } from "./series/SeriesActions";
|
||||
|
||||
|
||||
@@ -1,11 +1,9 @@
|
||||
import { formatBitrate } from "@/utils/bitrate";
|
||||
import { Ionicons } from "@expo/vector-icons";
|
||||
import {
|
||||
BottomSheetBackdrop,
|
||||
type BottomSheetBackdropProps,
|
||||
BottomSheetModal,
|
||||
BottomSheetScrollView,
|
||||
BottomSheetView,
|
||||
} from "@gorhom/bottom-sheet";
|
||||
import type {
|
||||
MediaSourceInfo,
|
||||
@@ -15,8 +13,8 @@ import type React from "react";
|
||||
import { useMemo, useRef } from "react";
|
||||
import { useTranslation } from "react-i18next";
|
||||
import { TouchableOpacity, View } from "react-native";
|
||||
import { formatBitrate } from "@/utils/bitrate";
|
||||
import { Badge } from "./Badge";
|
||||
import { Button } from "./Button";
|
||||
import { Text } from "./common/Text";
|
||||
|
||||
interface Props {
|
||||
@@ -103,7 +101,7 @@ const SubtitleStreamInfo = ({
|
||||
}) => {
|
||||
return (
|
||||
<View className='flex flex-col'>
|
||||
{subtitleStreams.map((stream, index) => (
|
||||
{subtitleStreams.map((stream, _index) => (
|
||||
<View key={stream.Index} className='flex flex-col'>
|
||||
<Text className='text-xs mb-3 text-neutral-400'>
|
||||
{stream.DisplayTitle}
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
import { useJellyfinDiscovery } from "@/hooks/useJellyfinDiscovery";
|
||||
import type React from "react";
|
||||
import { useTranslation } from "react-i18next";
|
||||
import { Text, TouchableOpacity, View } from "react-native";
|
||||
import { Text, View } from "react-native";
|
||||
import { useJellyfinDiscovery } from "@/hooks/useJellyfinDiscovery";
|
||||
import { Button } from "./Button";
|
||||
import { ListGroup } from "./list/ListGroup";
|
||||
import { ListItem } from "./list/ListItem";
|
||||
|
||||
@@ -2,7 +2,6 @@ import {
|
||||
ActivityIndicator,
|
||||
type ActivityIndicatorProps,
|
||||
Platform,
|
||||
View,
|
||||
} from "react-native";
|
||||
|
||||
interface Props extends ActivityIndicatorProps {}
|
||||
|
||||
@@ -4,7 +4,9 @@ import type {
|
||||
} from "@jellyfin/sdk/lib/generated-client/models";
|
||||
import { useMemo } from "react";
|
||||
import { Platform, TouchableOpacity, View } from "react-native";
|
||||
|
||||
const DropdownMenu = !Platform.isTV ? require("zeego/dropdown-menu") : null;
|
||||
|
||||
import { useTranslation } from "react-i18next";
|
||||
import { Text } from "./common/Text";
|
||||
|
||||
|
||||
@@ -1,10 +1,3 @@
|
||||
import { ItemCardText } from "@/components/ItemCardText";
|
||||
import { HorizontalScroll } from "@/components/common/HorrizontalScroll";
|
||||
import { Text } from "@/components/common/Text";
|
||||
import { TouchableItemRouter } from "@/components/common/TouchableItemRouter";
|
||||
import MoviePoster from "@/components/posters/MoviePoster";
|
||||
import { apiAtom, userAtom } from "@/providers/JellyfinProvider";
|
||||
import { getUserItemData } from "@/utils/jellyfin/user-library/getUserItemData";
|
||||
import type { BaseItemDto } from "@jellyfin/sdk/lib/generated-client/models";
|
||||
import { getItemsApi } from "@jellyfin/sdk/lib/utils/api";
|
||||
import { useQuery } from "@tanstack/react-query";
|
||||
@@ -12,6 +5,13 @@ import { useAtom } from "jotai";
|
||||
import type React from "react";
|
||||
import { useTranslation } from "react-i18next";
|
||||
import { View, type ViewProps } from "react-native";
|
||||
import { HorizontalScroll } from "@/components/common/HorrizontalScroll";
|
||||
import { Text } from "@/components/common/Text";
|
||||
import { TouchableItemRouter } from "@/components/common/TouchableItemRouter";
|
||||
import { ItemCardText } from "@/components/ItemCardText";
|
||||
import MoviePoster from "@/components/posters/MoviePoster";
|
||||
import { apiAtom, userAtom } from "@/providers/JellyfinProvider";
|
||||
import { getUserItemData } from "@/utils/jellyfin/user-library/getUserItemData";
|
||||
|
||||
interface Props extends ViewProps {
|
||||
actorId: string;
|
||||
|
||||
@@ -1,8 +1,8 @@
|
||||
import { Text } from "@/components/common/Text";
|
||||
import { tc } from "@/utils/textTools";
|
||||
import { useState } from "react";
|
||||
import { useTranslation } from "react-i18next";
|
||||
import { TouchableOpacity, View, type ViewProps } from "react-native";
|
||||
import { Text } from "@/components/common/Text";
|
||||
import { tc } from "@/utils/textTools";
|
||||
|
||||
interface Props extends ViewProps {
|
||||
text?: string | null;
|
||||
|
||||
@@ -1,11 +1,6 @@
|
||||
import { LinearGradient } from "expo-linear-gradient";
|
||||
import type { PropsWithChildren, ReactElement } from "react";
|
||||
import {
|
||||
type NativeScrollEvent,
|
||||
NativeSyntheticEvent,
|
||||
View,
|
||||
type ViewProps,
|
||||
} from "react-native";
|
||||
import { type NativeScrollEvent, View, type ViewProps } from "react-native";
|
||||
import Animated, {
|
||||
interpolate,
|
||||
useAnimatedRef,
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
import { BlurView } from "expo-blur";
|
||||
import type React from "react";
|
||||
import { Platform, View, type ViewProps } from "react-native";
|
||||
|
||||
interface Props extends ViewProps {
|
||||
blurAmount?: number;
|
||||
blurType?: "light" | "dark" | "xlight";
|
||||
|
||||
@@ -1,13 +1,3 @@
|
||||
import { useHaptic } from "@/hooks/useHaptic";
|
||||
import { apiAtom, userAtom } from "@/providers/JellyfinProvider";
|
||||
import { itemThemeColorAtom } from "@/utils/atoms/primaryColor";
|
||||
import { useSettings } from "@/utils/atoms/settings";
|
||||
import { getParentBackdropImageUrl } from "@/utils/jellyfin/image/getParentBackdropImageUrl";
|
||||
import { getPrimaryImageUrl } from "@/utils/jellyfin/image/getPrimaryImageUrl";
|
||||
import { getStreamUrl } from "@/utils/jellyfin/media/getStreamUrl";
|
||||
import { chromecast } from "@/utils/profiles/chromecast";
|
||||
import { chromecasth265 } from "@/utils/profiles/chromecasth265";
|
||||
import { runtimeTicksToMinutes } from "@/utils/time";
|
||||
import { useActionSheet } from "@expo/react-native-action-sheet";
|
||||
import { Feather, Ionicons, MaterialCommunityIcons } from "@expo/vector-icons";
|
||||
import type { BaseItemDto } from "@jellyfin/sdk/lib/generated-client";
|
||||
@@ -15,7 +5,6 @@ import { useRouter } from "expo-router";
|
||||
import { useAtom, useAtomValue } from "jotai";
|
||||
import { useCallback, useEffect } from "react";
|
||||
import { useTranslation } from "react-i18next";
|
||||
import { Platform, Pressable } from "react-native";
|
||||
import { Alert, TouchableOpacity, View } from "react-native";
|
||||
import CastContext, {
|
||||
CastButton,
|
||||
@@ -33,6 +22,16 @@ import Animated, {
|
||||
useSharedValue,
|
||||
withTiming,
|
||||
} from "react-native-reanimated";
|
||||
import { useHaptic } from "@/hooks/useHaptic";
|
||||
import { apiAtom, userAtom } from "@/providers/JellyfinProvider";
|
||||
import { itemThemeColorAtom } from "@/utils/atoms/primaryColor";
|
||||
import { useSettings } from "@/utils/atoms/settings";
|
||||
import { getParentBackdropImageUrl } from "@/utils/jellyfin/image/getParentBackdropImageUrl";
|
||||
import { getPrimaryImageUrl } from "@/utils/jellyfin/image/getPrimaryImageUrl";
|
||||
import { getStreamUrl } from "@/utils/jellyfin/media/getStreamUrl";
|
||||
import { chromecast } from "@/utils/profiles/chromecast";
|
||||
import { chromecasth265 } from "@/utils/profiles/chromecasth265";
|
||||
import { runtimeTicksToMinutes } from "@/utils/time";
|
||||
import type { Button } from "./Button";
|
||||
import type { SelectedOptions } from "./ItemContent";
|
||||
|
||||
|
||||
@@ -1,226 +0,0 @@
|
||||
import { useHaptic } from "@/hooks/useHaptic";
|
||||
import { apiAtom, userAtom } from "@/providers/JellyfinProvider";
|
||||
import { itemThemeColorAtom } from "@/utils/atoms/primaryColor";
|
||||
import { useSettings } from "@/utils/atoms/settings";
|
||||
import { runtimeTicksToMinutes } from "@/utils/time";
|
||||
import { useActionSheet } from "@expo/react-native-action-sheet";
|
||||
import { Feather, Ionicons, MaterialCommunityIcons } from "@expo/vector-icons";
|
||||
import type { BaseItemDto } from "@jellyfin/sdk/lib/generated-client";
|
||||
import { useRouter } from "expo-router";
|
||||
import { useAtom, useAtomValue } from "jotai";
|
||||
import { useCallback, useEffect } from "react";
|
||||
import { useTranslation } from "react-i18next";
|
||||
import { Platform } from "react-native";
|
||||
import { Alert, TouchableOpacity, View } from "react-native";
|
||||
import Animated, {
|
||||
Easing,
|
||||
interpolate,
|
||||
interpolateColor,
|
||||
useAnimatedReaction,
|
||||
useAnimatedStyle,
|
||||
useDerivedValue,
|
||||
useSharedValue,
|
||||
withTiming,
|
||||
} from "react-native-reanimated";
|
||||
import type { Button } from "./Button";
|
||||
import type { SelectedOptions } from "./ItemContent";
|
||||
|
||||
interface Props extends React.ComponentProps<typeof Button> {
|
||||
item: BaseItemDto;
|
||||
selectedOptions: SelectedOptions;
|
||||
}
|
||||
|
||||
const ANIMATION_DURATION = 500;
|
||||
const MIN_PLAYBACK_WIDTH = 15;
|
||||
|
||||
export const PlayButton: React.FC<Props> = ({
|
||||
item,
|
||||
selectedOptions,
|
||||
...props
|
||||
}: Props) => {
|
||||
const { showActionSheetWithOptions } = useActionSheet();
|
||||
const { t } = useTranslation();
|
||||
|
||||
const [colorAtom] = useAtom(itemThemeColorAtom);
|
||||
const api = useAtomValue(apiAtom);
|
||||
const user = useAtomValue(userAtom);
|
||||
|
||||
const router = useRouter();
|
||||
|
||||
const startWidth = useSharedValue(0);
|
||||
const targetWidth = useSharedValue(0);
|
||||
const endColor = useSharedValue(colorAtom);
|
||||
const startColor = useSharedValue(colorAtom);
|
||||
const widthProgress = useSharedValue(0);
|
||||
const colorChangeProgress = useSharedValue(0);
|
||||
const [settings] = useSettings();
|
||||
const lightHapticFeedback = useHaptic("light");
|
||||
|
||||
const goToPlayer = useCallback(
|
||||
(q: string) => {
|
||||
router.push(`/player/direct-player?${q}`);
|
||||
},
|
||||
[router],
|
||||
);
|
||||
|
||||
const onPress = () => {
|
||||
console.log("onpress");
|
||||
if (!item) return;
|
||||
|
||||
lightHapticFeedback();
|
||||
|
||||
const queryParams = new URLSearchParams({
|
||||
itemId: item.Id!,
|
||||
audioIndex: selectedOptions.audioIndex?.toString() ?? "",
|
||||
subtitleIndex: selectedOptions.subtitleIndex?.toString() ?? "",
|
||||
mediaSourceId: selectedOptions.mediaSource?.Id ?? "",
|
||||
bitrateValue: selectedOptions.bitrate?.value?.toString() ?? "",
|
||||
});
|
||||
|
||||
const queryString = queryParams.toString();
|
||||
goToPlayer(queryString);
|
||||
return;
|
||||
};
|
||||
|
||||
const derivedTargetWidth = useDerivedValue(() => {
|
||||
if (!item || !item.RunTimeTicks) return 0;
|
||||
const userData = item.UserData;
|
||||
if (userData?.PlaybackPositionTicks) {
|
||||
return userData.PlaybackPositionTicks > 0
|
||||
? Math.max(
|
||||
(userData.PlaybackPositionTicks / item.RunTimeTicks) * 100,
|
||||
MIN_PLAYBACK_WIDTH,
|
||||
)
|
||||
: 0;
|
||||
}
|
||||
return 0;
|
||||
}, [item]);
|
||||
|
||||
useAnimatedReaction(
|
||||
() => derivedTargetWidth.value,
|
||||
(newWidth) => {
|
||||
targetWidth.value = newWidth;
|
||||
widthProgress.value = 0;
|
||||
widthProgress.value = withTiming(1, {
|
||||
duration: ANIMATION_DURATION,
|
||||
easing: Easing.bezier(0.7, 0, 0.3, 1.0),
|
||||
});
|
||||
},
|
||||
[item],
|
||||
);
|
||||
|
||||
useAnimatedReaction(
|
||||
() => colorAtom,
|
||||
(newColor) => {
|
||||
endColor.value = newColor;
|
||||
colorChangeProgress.value = 0;
|
||||
colorChangeProgress.value = withTiming(1, {
|
||||
duration: ANIMATION_DURATION,
|
||||
easing: Easing.bezier(0.9, 0, 0.31, 0.99),
|
||||
});
|
||||
},
|
||||
[colorAtom],
|
||||
);
|
||||
|
||||
useEffect(() => {
|
||||
const timeout_2 = setTimeout(() => {
|
||||
startColor.value = colorAtom;
|
||||
startWidth.value = targetWidth.value;
|
||||
}, ANIMATION_DURATION);
|
||||
|
||||
return () => {
|
||||
clearTimeout(timeout_2);
|
||||
};
|
||||
}, [colorAtom, item]);
|
||||
|
||||
/**
|
||||
* ANIMATED STYLES
|
||||
*/
|
||||
const animatedAverageStyle = useAnimatedStyle(() => ({
|
||||
backgroundColor: interpolateColor(
|
||||
colorChangeProgress.value,
|
||||
[0, 1],
|
||||
[startColor.value.primary, endColor.value.primary],
|
||||
),
|
||||
}));
|
||||
|
||||
const animatedPrimaryStyle = useAnimatedStyle(() => ({
|
||||
backgroundColor: interpolateColor(
|
||||
colorChangeProgress.value,
|
||||
[0, 1],
|
||||
[startColor.value.primary, endColor.value.primary],
|
||||
),
|
||||
}));
|
||||
|
||||
const animatedWidthStyle = useAnimatedStyle(() => ({
|
||||
width: `${interpolate(
|
||||
widthProgress.value,
|
||||
[0, 1],
|
||||
[startWidth.value, targetWidth.value],
|
||||
)}%`,
|
||||
}));
|
||||
|
||||
const animatedTextStyle = useAnimatedStyle(() => ({
|
||||
color: interpolateColor(
|
||||
colorChangeProgress.value,
|
||||
[0, 1],
|
||||
[startColor.value.text, endColor.value.text],
|
||||
),
|
||||
}));
|
||||
/**
|
||||
* *********************
|
||||
*/
|
||||
|
||||
return (
|
||||
<TouchableOpacity
|
||||
accessibilityLabel='Play button'
|
||||
accessibilityHint='Tap to play the media'
|
||||
onPress={onPress}
|
||||
className={"relative"}
|
||||
{...props}
|
||||
>
|
||||
<View className='absolute w-full h-full top-0 left-0 rounded-xl z-10 overflow-hidden'>
|
||||
<Animated.View
|
||||
style={[
|
||||
animatedPrimaryStyle,
|
||||
animatedWidthStyle,
|
||||
{
|
||||
height: "100%",
|
||||
},
|
||||
]}
|
||||
/>
|
||||
</View>
|
||||
|
||||
<Animated.View
|
||||
style={[animatedAverageStyle, { opacity: 0.5 }]}
|
||||
className='absolute w-full h-full top-0 left-0 rounded-xl'
|
||||
/>
|
||||
<View
|
||||
style={{
|
||||
borderWidth: 1,
|
||||
borderColor: colorAtom.primary,
|
||||
borderStyle: "solid",
|
||||
}}
|
||||
className='flex flex-row items-center justify-center bg-transparent rounded-xl z-20 h-12 w-full '
|
||||
>
|
||||
<View className='flex flex-row items-center space-x-2'>
|
||||
<Animated.Text style={[animatedTextStyle, { fontWeight: "bold" }]}>
|
||||
{runtimeTicksToMinutes(item?.RunTimeTicks)}
|
||||
</Animated.Text>
|
||||
<Animated.Text style={animatedTextStyle}>
|
||||
<Ionicons name='play-circle' size={24} />
|
||||
</Animated.Text>
|
||||
{settings?.openInVLC && (
|
||||
<Animated.Text style={animatedTextStyle}>
|
||||
<MaterialCommunityIcons
|
||||
name='vlc'
|
||||
size={18}
|
||||
color={animatedTextStyle.color}
|
||||
/>
|
||||
</Animated.Text>
|
||||
)}
|
||||
</View>
|
||||
</View>
|
||||
</TouchableOpacity>
|
||||
);
|
||||
};
|
||||
@@ -1,5 +1,3 @@
|
||||
import { useAllSessions, type useSessionsProps } from "@/hooks/useSessions";
|
||||
import { apiAtom } from "@/providers/JellyfinProvider";
|
||||
import { Ionicons } from "@expo/vector-icons";
|
||||
import {
|
||||
type BaseItemDto,
|
||||
@@ -15,9 +13,11 @@ import {
|
||||
TouchableOpacity,
|
||||
View,
|
||||
} from "react-native";
|
||||
import { useAllSessions, type useSessionsProps } from "@/hooks/useSessions";
|
||||
import { apiAtom } from "@/providers/JellyfinProvider";
|
||||
import { Text } from "./common/Text";
|
||||
import { Loader } from "./Loader";
|
||||
import { RoundButton } from "./RoundButton";
|
||||
import { Text } from "./common/Text";
|
||||
|
||||
interface Props extends React.ComponentProps<typeof View> {
|
||||
item: BaseItemDto;
|
||||
|
||||
@@ -1,8 +1,8 @@
|
||||
import { useMarkAsPlayed } from "@/hooks/useMarkAsPlayed";
|
||||
import type { BaseItemDto } from "@jellyfin/sdk/lib/generated-client/models";
|
||||
import { useQueryClient } from "@tanstack/react-query";
|
||||
import type React from "react";
|
||||
import { View, type ViewProps } from "react-native";
|
||||
import { useMarkAsPlayed } from "@/hooks/useMarkAsPlayed";
|
||||
import { RoundButton } from "./RoundButton";
|
||||
|
||||
interface Props extends ViewProps {
|
||||
@@ -13,7 +13,7 @@ interface Props extends ViewProps {
|
||||
export const PlayedStatus: React.FC<Props> = ({ items, ...props }) => {
|
||||
const queryClient = useQueryClient();
|
||||
|
||||
const invalidateQueries = () => {
|
||||
const _invalidateQueries = () => {
|
||||
items.forEach((item) => {
|
||||
queryClient.invalidateQueries({
|
||||
queryKey: ["item", item.Id],
|
||||
|
||||
@@ -1,5 +1,4 @@
|
||||
import type React from "react";
|
||||
import { StyleSheet, View } from "react-native";
|
||||
import { AnimatedCircularProgress } from "react-native-circular-progress";
|
||||
|
||||
type ProgressCircleProps = {
|
||||
|
||||
@@ -1,3 +1,9 @@
|
||||
import { Ionicons } from "@expo/vector-icons";
|
||||
import type { BaseItemDto } from "@jellyfin/sdk/lib/generated-client/models";
|
||||
import { useQuery } from "@tanstack/react-query";
|
||||
import { Image } from "expo-image";
|
||||
import { useMemo } from "react";
|
||||
import { View, type ViewProps } from "react-native";
|
||||
import { useJellyseerr } from "@/hooks/useJellyseerr";
|
||||
import { MediaType } from "@/utils/jellyseerr/server/constants/media";
|
||||
import type { MovieDetails } from "@/utils/jellyseerr/server/models/Movie";
|
||||
@@ -6,12 +12,6 @@ import type {
|
||||
TvResult,
|
||||
} from "@/utils/jellyseerr/server/models/Search";
|
||||
import type { TvDetails } from "@/utils/jellyseerr/server/models/Tv";
|
||||
import { Ionicons } from "@expo/vector-icons";
|
||||
import type { BaseItemDto } from "@jellyfin/sdk/lib/generated-client/models";
|
||||
import { useQuery } from "@tanstack/react-query";
|
||||
import { Image } from "expo-image";
|
||||
import { useMemo } from "react";
|
||||
import { View, type ViewProps } from "react-native";
|
||||
import { Badge } from "./Badge";
|
||||
|
||||
interface Props extends ViewProps {
|
||||
|
||||
@@ -1,4 +1,3 @@
|
||||
import { useHaptic } from "@/hooks/useHaptic";
|
||||
import { Ionicons } from "@expo/vector-icons";
|
||||
import { BlurView } from "expo-blur";
|
||||
import type { PropsWithChildren } from "react";
|
||||
@@ -7,6 +6,7 @@ import {
|
||||
TouchableOpacity,
|
||||
type TouchableOpacityProps,
|
||||
} from "react-native";
|
||||
import { useHaptic } from "@/hooks/useHaptic";
|
||||
|
||||
interface Props extends TouchableOpacityProps {
|
||||
onPress?: () => void;
|
||||
|
||||
@@ -1,23 +1,16 @@
|
||||
import MoviePoster from "@/components/posters/MoviePoster";
|
||||
import { apiAtom, userAtom } from "@/providers/JellyfinProvider";
|
||||
import type { BaseItemDto } from "@jellyfin/sdk/lib/generated-client/models";
|
||||
import { getLibraryApi } from "@jellyfin/sdk/lib/utils/api";
|
||||
import { useQuery } from "@tanstack/react-query";
|
||||
import { router } from "expo-router";
|
||||
import { useAtom } from "jotai";
|
||||
import { useMemo } from "react";
|
||||
import { useTranslation } from "react-i18next";
|
||||
import {
|
||||
ScrollView,
|
||||
TouchableOpacity,
|
||||
View,
|
||||
type ViewProps,
|
||||
} from "react-native";
|
||||
import { ItemCardText } from "./ItemCardText";
|
||||
import { Loader } from "./Loader";
|
||||
import { View, type ViewProps } from "react-native";
|
||||
import MoviePoster from "@/components/posters/MoviePoster";
|
||||
import { apiAtom, userAtom } from "@/providers/JellyfinProvider";
|
||||
import { HorizontalScroll } from "./common/HorrizontalScroll";
|
||||
import { Text } from "./common/Text";
|
||||
import { TouchableItemRouter } from "./common/TouchableItemRouter";
|
||||
import { ItemCardText } from "./ItemCardText";
|
||||
|
||||
interface SimilarItemsProps extends ViewProps {
|
||||
itemId?: string | null;
|
||||
|
||||
@@ -1,8 +1,10 @@
|
||||
import { tc } from "@/utils/textTools";
|
||||
import type { MediaSourceInfo } from "@jellyfin/sdk/lib/generated-client/models";
|
||||
import { useMemo } from "react";
|
||||
import { Platform, TouchableOpacity, View } from "react-native";
|
||||
import { tc } from "@/utils/textTools";
|
||||
|
||||
const DropdownMenu = !Platform.isTV ? require("zeego/dropdown-menu") : null;
|
||||
|
||||
import { useTranslation } from "react-i18next";
|
||||
import { Text } from "./common/Text";
|
||||
|
||||
|
||||
@@ -1,4 +1,3 @@
|
||||
import * as React from "react";
|
||||
import renderer from "react-test-renderer";
|
||||
|
||||
import { ThemedText } from "../ThemedText";
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
import { Text } from "@/components/common/Text";
|
||||
import { View, type ViewProps } from "react-native";
|
||||
import { Text } from "@/components/common/Text";
|
||||
|
||||
interface Props extends ViewProps {}
|
||||
|
||||
|
||||
@@ -1,7 +1,6 @@
|
||||
import { useMemo } from "react";
|
||||
import { StyleSheet, View, type ViewProps } from "react-native";
|
||||
import { View, type ViewProps } from "react-native";
|
||||
|
||||
const getItemStyle = (index: number, numColumns: number) => {
|
||||
const _getItemStyle = (index: number, numColumns: number) => {
|
||||
const alignItems = (() => {
|
||||
if (numColumns < 2 || index % numColumns === 0) return "flex-start";
|
||||
if ((index + 1) % numColumns === 0) return "flex-end";
|
||||
|
||||
@@ -1,13 +1,14 @@
|
||||
const DropdownMenu = !Platform.isTV ? require("zeego/dropdown-menu") : null;
|
||||
import { Text } from "@/components/common/Text";
|
||||
import DisabledSetting from "@/components/settings/DisabledSetting";
|
||||
import React, {
|
||||
|
||||
import {
|
||||
type PropsWithChildren,
|
||||
type ReactNode,
|
||||
useEffect,
|
||||
useState,
|
||||
} from "react";
|
||||
import { Platform, TouchableOpacity, View, type ViewProps } from "react-native";
|
||||
import { Text } from "@/components/common/Text";
|
||||
import DisabledSetting from "@/components/settings/DisabledSetting";
|
||||
|
||||
interface Props<T> {
|
||||
data: T[];
|
||||
@@ -58,7 +59,7 @@ const Dropdown = <T,>({
|
||||
</TouchableOpacity>
|
||||
</View>
|
||||
) : (
|
||||
<>{title}</>
|
||||
title
|
||||
)}
|
||||
</DropdownMenu.Trigger>
|
||||
<DropdownMenu.Content
|
||||
@@ -71,7 +72,7 @@ const Dropdown = <T,>({
|
||||
sideOffset={0}
|
||||
>
|
||||
<DropdownMenu.Label>{label}</DropdownMenu.Label>
|
||||
{data.map((item, idx) =>
|
||||
{data.map((item, _idx) =>
|
||||
multiple ? (
|
||||
<DropdownMenu.CheckboxItem
|
||||
value={
|
||||
@@ -80,7 +81,10 @@ const Dropdown = <T,>({
|
||||
: "off"
|
||||
}
|
||||
key={keyExtractor(item)}
|
||||
onValueChange={(next: "on" | "off", previous: "on" | "off") => {
|
||||
onValueChange={(
|
||||
next: "on" | "off",
|
||||
_previous: "on" | "off",
|
||||
) => {
|
||||
setSelected((p) => {
|
||||
const prev = p || [];
|
||||
if (next === "on") {
|
||||
|
||||
@@ -1,4 +1,3 @@
|
||||
import { Text } from "@/components/common/Text";
|
||||
import { Ionicons } from "@expo/vector-icons";
|
||||
import { BlurView, type BlurViewProps } from "expo-blur";
|
||||
import { useRouter } from "expo-router";
|
||||
@@ -6,8 +5,6 @@ import {
|
||||
Platform,
|
||||
TouchableOpacity,
|
||||
type TouchableOpacityProps,
|
||||
View,
|
||||
ViewProps,
|
||||
} from "react-native";
|
||||
|
||||
interface Props extends BlurViewProps {
|
||||
|
||||
@@ -1,4 +1,3 @@
|
||||
import { apiAtom, userAtom } from "@/providers/JellyfinProvider";
|
||||
import type {
|
||||
BaseItemDto,
|
||||
BaseItemDtoQueryResult,
|
||||
@@ -14,6 +13,7 @@ import Animated, {
|
||||
useSharedValue,
|
||||
withTiming,
|
||||
} from "react-native-reanimated";
|
||||
import { apiAtom, userAtom } from "@/providers/JellyfinProvider";
|
||||
import { Loader } from "../Loader";
|
||||
import { Text } from "./Text";
|
||||
|
||||
|
||||
@@ -1,11 +1,11 @@
|
||||
import { apiAtom } from "@/providers/JellyfinProvider";
|
||||
import { getItemImage } from "@/utils/getItemImage";
|
||||
import { Ionicons } from "@expo/vector-icons";
|
||||
import type { BaseItemDto } from "@jellyfin/sdk/lib/generated-client/models";
|
||||
import { Image, type ImageProps } from "expo-image";
|
||||
import { useAtom } from "jotai";
|
||||
import { type FC, useMemo } from "react";
|
||||
import { View, type ViewProps } from "react-native";
|
||||
import { apiAtom } from "@/providers/JellyfinProvider";
|
||||
import { getItemImage } from "@/utils/getItemImage";
|
||||
|
||||
interface Props extends ImageProps {
|
||||
item: BaseItemDto;
|
||||
|
||||
@@ -1,9 +1,13 @@
|
||||
import { useRouter, useSegments } from "expo-router";
|
||||
import type React from "react";
|
||||
import { type PropsWithChildren, useCallback, useMemo } from "react";
|
||||
import { TouchableOpacity, type TouchableOpacityProps } from "react-native";
|
||||
import * as ContextMenu from "@/components/ContextMenu";
|
||||
import { useJellyseerr } from "@/hooks/useJellyseerr";
|
||||
import { MediaType } from "@/utils/jellyseerr/server/constants/media";
|
||||
import {
|
||||
Permission,
|
||||
hasPermission,
|
||||
Permission,
|
||||
} from "@/utils/jellyseerr/server/lib/permissions";
|
||||
import type { MovieDetails } from "@/utils/jellyseerr/server/models/Movie";
|
||||
import type {
|
||||
@@ -11,10 +15,6 @@ import type {
|
||||
TvResult,
|
||||
} from "@/utils/jellyseerr/server/models/Search";
|
||||
import type { TvDetails } from "@/utils/jellyseerr/server/models/Tv";
|
||||
import { useRouter, useSegments } from "expo-router";
|
||||
import type React from "react";
|
||||
import { type PropsWithChildren, useCallback, useMemo } from "react";
|
||||
import { TouchableOpacity, type TouchableOpacityProps } from "react-native";
|
||||
|
||||
interface Props extends TouchableOpacityProps {
|
||||
result?: MovieResult | TvResult | MovieDetails | TvDetails;
|
||||
|
||||
@@ -1,6 +1,4 @@
|
||||
import React from "react";
|
||||
import { Platform, type TextProps } from "react-native";
|
||||
import { Text as RNText } from "react-native";
|
||||
import { Platform, Text as RNText, type TextProps } from "react-native";
|
||||
import { UITextView } from "react-native-uitextview";
|
||||
export function Text(
|
||||
props: TextProps & {
|
||||
|
||||
@@ -1,5 +1,3 @@
|
||||
import { useFavorite } from "@/hooks/useFavorite";
|
||||
import { useMarkAsPlayed } from "@/hooks/useMarkAsPlayed";
|
||||
import { useActionSheet } from "@expo/react-native-action-sheet";
|
||||
import type {
|
||||
BaseItemDto,
|
||||
@@ -8,6 +6,8 @@ import type {
|
||||
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";
|
||||
|
||||
interface Props extends TouchableOpacityProps {
|
||||
item: BaseItemDto;
|
||||
@@ -17,10 +17,6 @@ export const itemRouter = (
|
||||
item: BaseItemDto | BaseItemPerson,
|
||||
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}`;
|
||||
}
|
||||
|
||||
@@ -1,4 +1,3 @@
|
||||
import { Text } from "@/components/common/Text";
|
||||
import { View, type ViewProps } from "react-native";
|
||||
|
||||
interface Props extends ViewProps {
|
||||
|
||||
@@ -1,9 +1,3 @@
|
||||
import { Text } from "@/components/common/Text";
|
||||
import { useDownload } from "@/providers/DownloadProvider";
|
||||
import { DownloadMethod, useSettings } from "@/utils/atoms/settings";
|
||||
import { storage } from "@/utils/mmkv";
|
||||
import type { JobStatus } from "@/utils/optimize-server";
|
||||
import { formatTimeString } from "@/utils/time";
|
||||
import { Ionicons } from "@expo/vector-icons";
|
||||
import { useMutation, useQueryClient } from "@tanstack/react-query";
|
||||
import { Image } from "expo-image";
|
||||
@@ -19,7 +13,14 @@ import {
|
||||
type ViewProps,
|
||||
} from "react-native";
|
||||
import { toast } from "sonner-native";
|
||||
import { Text } from "@/components/common/Text";
|
||||
import { useDownload } from "@/providers/DownloadProvider";
|
||||
import { DownloadMethod, useSettings } from "@/utils/atoms/settings";
|
||||
import { storage } from "@/utils/mmkv";
|
||||
import type { JobStatus } from "@/utils/optimize-server";
|
||||
import { formatTimeString } from "@/utils/time";
|
||||
import { Button } from "../Button";
|
||||
|
||||
const BackGroundDownloader = !Platform.isTV
|
||||
? require("@kesha-antonov/react-native-background-downloader")
|
||||
: null;
|
||||
|
||||
@@ -1,9 +1,9 @@
|
||||
import { Text } from "@/components/common/Text";
|
||||
import { useDownload } from "@/providers/DownloadProvider";
|
||||
import type { BaseItemDto } from "@jellyfin/sdk/lib/generated-client/models";
|
||||
import type React from "react";
|
||||
import { useEffect, useMemo, useState } from "react";
|
||||
import type { TextProps } from "react-native";
|
||||
import { Text } from "@/components/common/Text";
|
||||
import { useDownload } from "@/providers/DownloadProvider";
|
||||
|
||||
interface DownloadSizeProps extends TextProps {
|
||||
items: BaseItemDto[];
|
||||
|
||||
@@ -1,4 +1,3 @@
|
||||
import { useHaptic } from "@/hooks/useHaptic";
|
||||
import {
|
||||
ActionSheetProvider,
|
||||
useActionSheet,
|
||||
@@ -11,17 +10,14 @@ import {
|
||||
type TouchableOpacityProps,
|
||||
View,
|
||||
} from "react-native";
|
||||
|
||||
import { Text } from "@/components/common/Text";
|
||||
import { DownloadSize } from "@/components/downloads/DownloadSize";
|
||||
import { useDownloadedFileOpener } from "@/hooks/useDownloadedFileOpener";
|
||||
import { useHaptic } from "@/hooks/useHaptic";
|
||||
import { useDownload } from "@/providers/DownloadProvider";
|
||||
import { storage } from "@/utils/mmkv";
|
||||
import { runtimeTicksToSeconds } from "@/utils/time";
|
||||
import { Ionicons } from "@expo/vector-icons";
|
||||
import { Image } from "expo-image";
|
||||
import ContinueWatchingPoster from "../ContinueWatchingPoster";
|
||||
import { TouchableItemRouter } from "../common/TouchableItemRouter";
|
||||
|
||||
interface EpisodeCardProps extends TouchableOpacityProps {
|
||||
item: BaseItemDto;
|
||||
@@ -33,7 +29,7 @@ export const EpisodeCard: React.FC<EpisodeCardProps> = ({ item, ...props }) => {
|
||||
const { showActionSheetWithOptions } = useActionSheet();
|
||||
const successHapticFeedback = useHaptic("success");
|
||||
|
||||
const base64Image = useMemo(() => {
|
||||
const _base64Image = useMemo(() => {
|
||||
return storage.getString(item.Id!);
|
||||
}, [item]);
|
||||
|
||||
|
||||
@@ -1,19 +1,18 @@
|
||||
import { useHaptic } from "@/hooks/useHaptic";
|
||||
import {
|
||||
ActionSheetProvider,
|
||||
useActionSheet,
|
||||
} from "@expo/react-native-action-sheet";
|
||||
import { Ionicons } from "@expo/vector-icons";
|
||||
import type { BaseItemDto } from "@jellyfin/sdk/lib/generated-client/models";
|
||||
import { Image } from "expo-image";
|
||||
import type React from "react";
|
||||
import { useCallback, useMemo } from "react";
|
||||
import { TouchableOpacity, View } from "react-native";
|
||||
|
||||
import { DownloadSize } from "@/components/downloads/DownloadSize";
|
||||
import { useDownloadedFileOpener } from "@/hooks/useDownloadedFileOpener";
|
||||
import { useHaptic } from "@/hooks/useHaptic";
|
||||
import { useDownload } from "@/providers/DownloadProvider";
|
||||
import { storage } from "@/utils/mmkv";
|
||||
import { Ionicons } from "@expo/vector-icons";
|
||||
import { Image } from "expo-image";
|
||||
import { ItemCardText } from "../ItemCardText";
|
||||
|
||||
interface MovieCardProps {
|
||||
|
||||
@@ -1,6 +1,3 @@
|
||||
import { DownloadSize } from "@/components/downloads/DownloadSize";
|
||||
import { useDownload } from "@/providers/DownloadProvider";
|
||||
import { storage } from "@/utils/mmkv";
|
||||
import { useActionSheet } from "@expo/react-native-action-sheet";
|
||||
import { Ionicons } from "@expo/vector-icons";
|
||||
import type { BaseItemDto } from "@jellyfin/sdk/lib/generated-client/models";
|
||||
@@ -9,6 +6,9 @@ import { router } from "expo-router";
|
||||
import type React from "react";
|
||||
import { useCallback, useMemo } from "react";
|
||||
import { TouchableOpacity, View } from "react-native";
|
||||
import { DownloadSize } from "@/components/downloads/DownloadSize";
|
||||
import { useDownload } from "@/providers/DownloadProvider";
|
||||
import { storage } from "@/utils/mmkv";
|
||||
import { Text } from "../common/Text";
|
||||
|
||||
export const SeriesCard: React.FC<{ items: BaseItemDto[] }> = ({ items }) => {
|
||||
|
||||
@@ -1,8 +1,8 @@
|
||||
import { Text } from "@/components/common/Text";
|
||||
import { FontAwesome, Ionicons } from "@expo/vector-icons";
|
||||
import { useQuery } from "@tanstack/react-query";
|
||||
import { useState } from "react";
|
||||
import { TouchableOpacity, View, type ViewProps } from "react-native";
|
||||
import { Text } from "@/components/common/Text";
|
||||
import { FilterSheet } from "./FilterSheet";
|
||||
|
||||
interface FilterButtonProps<T> extends ViewProps {
|
||||
|
||||
@@ -1,16 +1,12 @@
|
||||
import { Ionicons } from "@expo/vector-icons";
|
||||
import {
|
||||
BottomSheetBackdrop,
|
||||
type BottomSheetBackdropProps,
|
||||
BottomSheetFlatList,
|
||||
BottomSheetModal,
|
||||
BottomSheetScrollView,
|
||||
BottomSheetView,
|
||||
} from "@gorhom/bottom-sheet";
|
||||
import type React from "react";
|
||||
import { useCallback, useEffect, useMemo, useRef, useState } from "react";
|
||||
|
||||
import { Text } from "@/components/common/Text";
|
||||
import { Ionicons } from "@expo/vector-icons";
|
||||
import { useTranslation } from "react-i18next";
|
||||
import {
|
||||
StyleSheet,
|
||||
@@ -18,6 +14,7 @@ import {
|
||||
View,
|
||||
type ViewProps,
|
||||
} from "react-native";
|
||||
import { Text } from "@/components/common/Text";
|
||||
import { Button } from "../Button";
|
||||
import { Input } from "../common/Input";
|
||||
|
||||
|
||||
@@ -1,11 +1,11 @@
|
||||
import { Ionicons } from "@expo/vector-icons";
|
||||
import { useAtom } from "jotai";
|
||||
import { TouchableOpacity, type TouchableOpacityProps } from "react-native";
|
||||
import {
|
||||
genreFilterAtom,
|
||||
tagsFilterAtom,
|
||||
yearFilterAtom,
|
||||
} from "@/utils/atoms/filters";
|
||||
import { Ionicons } from "@expo/vector-icons";
|
||||
import { useAtom } from "jotai";
|
||||
import { TouchableOpacity, type TouchableOpacityProps } from "react-native";
|
||||
|
||||
interface Props extends TouchableOpacityProps {}
|
||||
|
||||
|
||||
@@ -1,5 +1,3 @@
|
||||
import { Colors } from "@/constants/Colors";
|
||||
import { apiAtom, userAtom } from "@/providers/JellyfinProvider";
|
||||
import type { Api } from "@jellyfin/sdk";
|
||||
import type { BaseItemKind } from "@jellyfin/sdk/lib/generated-client";
|
||||
import { getItemsApi } from "@jellyfin/sdk/lib/utils/api";
|
||||
@@ -7,10 +5,11 @@ import { t } from "i18next";
|
||||
import { useAtom } from "jotai";
|
||||
import { useCallback, useEffect, useState } from "react";
|
||||
import { Image, Text, View } from "react-native";
|
||||
import { ScrollingCollectionList } from "./ScrollingCollectionList";
|
||||
|
||||
// PNG ASSET
|
||||
import heart from "@/assets/icons/heart.fill.png";
|
||||
import { Colors } from "@/constants/Colors";
|
||||
import { apiAtom, userAtom } from "@/providers/JellyfinProvider";
|
||||
import { ScrollingCollectionList } from "./ScrollingCollectionList";
|
||||
|
||||
type FavoriteTypes =
|
||||
| "Series"
|
||||
|
||||
@@ -1,8 +1,3 @@
|
||||
import { useHaptic } from "@/hooks/useHaptic";
|
||||
import { apiAtom, userAtom } from "@/providers/JellyfinProvider";
|
||||
import { useSettings } from "@/utils/atoms/settings";
|
||||
import { getBackdropUrl } from "@/utils/jellyfin/image/getBackdropUrl";
|
||||
import { getLogoImageUrlById } from "@/utils/jellyfin/image/getLogoImageUrlById";
|
||||
import type { BaseItemDto } from "@jellyfin/sdk/lib/generated-client/models";
|
||||
import { getItemsApi } from "@jellyfin/sdk/lib/utils/api";
|
||||
import { useQuery } from "@tanstack/react-query";
|
||||
@@ -21,6 +16,11 @@ import Carousel, {
|
||||
type ICarouselInstance,
|
||||
Pagination,
|
||||
} from "react-native-reanimated-carousel";
|
||||
import { useHaptic } from "@/hooks/useHaptic";
|
||||
import { apiAtom, userAtom } from "@/providers/JellyfinProvider";
|
||||
import { useSettings } from "@/utils/atoms/settings";
|
||||
import { getBackdropUrl } from "@/utils/jellyfin/image/getBackdropUrl";
|
||||
import { getLogoImageUrlById } from "@/utils/jellyfin/image/getLogoImageUrlById";
|
||||
import { itemRouter } from "../common/TouchableItemRouter";
|
||||
|
||||
interface Props extends ViewProps {}
|
||||
|
||||
@@ -1,5 +1,3 @@
|
||||
import { Text } from "@/components/common/Text";
|
||||
import MoviePoster from "@/components/posters/MoviePoster";
|
||||
import type { BaseItemDto } from "@jellyfin/sdk/lib/generated-client/models";
|
||||
import {
|
||||
type QueryFunction,
|
||||
@@ -8,9 +6,11 @@ import {
|
||||
} from "@tanstack/react-query";
|
||||
import { useTranslation } from "react-i18next";
|
||||
import { ScrollView, View, type ViewProps } from "react-native";
|
||||
import { Text } from "@/components/common/Text";
|
||||
import MoviePoster from "@/components/posters/MoviePoster";
|
||||
import ContinueWatchingPoster from "../ContinueWatchingPoster";
|
||||
import { ItemCardText } from "../ItemCardText";
|
||||
import { TouchableItemRouter } from "../common/TouchableItemRouter";
|
||||
import { ItemCardText } from "../ItemCardText";
|
||||
import SeriesPoster from "../posters/SeriesPoster";
|
||||
|
||||
interface Props extends ViewProps {
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
import { TouchableOpacity } from "react-native";
|
||||
import { Text } from "@/components/common/Text";
|
||||
import DisabledSetting from "@/components/settings/DisabledSetting";
|
||||
import { TouchableOpacity, View } from "react-native";
|
||||
|
||||
interface StepperProps {
|
||||
value: number;
|
||||
|
||||
@@ -1,11 +1,11 @@
|
||||
import { Text } from "@/components/common/Text";
|
||||
import PersonPoster from "@/components/jellyseerr/PersonPoster";
|
||||
import type { MovieDetails } from "@/utils/jellyseerr/server/models/Movie";
|
||||
import type { TvDetails } from "@/utils/jellyseerr/server/models/Tv";
|
||||
import { FlashList } from "@shopify/flash-list";
|
||||
import type React from "react";
|
||||
import { useTranslation } from "react-i18next";
|
||||
import { View, type ViewProps } from "react-native";
|
||||
import { Text } from "@/components/common/Text";
|
||||
import PersonPoster from "@/components/jellyseerr/PersonPoster";
|
||||
import type { MovieDetails } from "@/utils/jellyseerr/server/models/Movie";
|
||||
import type { TvDetails } from "@/utils/jellyseerr/server/models/Tv";
|
||||
|
||||
const CastSlide: React.FC<
|
||||
{ details?: MovieDetails | TvDetails } & ViewProps
|
||||
|
||||
@@ -1,15 +1,15 @@
|
||||
import { Text } from "@/components/common/Text";
|
||||
import { useJellyseerr } from "@/hooks/useJellyseerr";
|
||||
import { ANIME_KEYWORD_ID } from "@/utils/jellyseerr/server/api/themoviedb/constants";
|
||||
import type { TmdbRelease } from "@/utils/jellyseerr/server/api/themoviedb/interfaces";
|
||||
import type { MovieDetails } from "@/utils/jellyseerr/server/models/Movie";
|
||||
import type { TvDetails } from "@/utils/jellyseerr/server/models/Tv";
|
||||
import { Ionicons, MaterialCommunityIcons } from "@expo/vector-icons";
|
||||
import { uniqBy } from "lodash";
|
||||
import { useMemo } from "react";
|
||||
import { useTranslation } from "react-i18next";
|
||||
import { View, type ViewProps } from "react-native";
|
||||
import CountryFlag from "react-native-country-flag";
|
||||
import { Text } from "@/components/common/Text";
|
||||
import { useJellyseerr } from "@/hooks/useJellyseerr";
|
||||
import { ANIME_KEYWORD_ID } from "@/utils/jellyseerr/server/api/themoviedb/constants";
|
||||
import type { TmdbRelease } from "@/utils/jellyseerr/server/api/themoviedb/interfaces";
|
||||
import type { MovieDetails } from "@/utils/jellyseerr/server/models/Movie";
|
||||
import type { TvDetails } from "@/utils/jellyseerr/server/models/Tv";
|
||||
|
||||
interface Release {
|
||||
certification: string;
|
||||
|
||||
@@ -1,3 +1,13 @@
|
||||
import { orderBy, uniqBy } from "lodash";
|
||||
import type React from "react";
|
||||
import { useMemo } from "react";
|
||||
import { useTranslation } from "react-i18next";
|
||||
import { View, type ViewProps } from "react-native";
|
||||
import {
|
||||
useAnimatedReaction,
|
||||
useSharedValue,
|
||||
withTiming,
|
||||
} from "react-native-reanimated";
|
||||
import Discover from "@/components/jellyseerr/discover/Discover";
|
||||
import { useJellyseerr } from "@/hooks/useJellyseerr";
|
||||
import { MediaType } from "@/utils/jellyseerr/server/constants/media";
|
||||
@@ -7,17 +17,6 @@ import type {
|
||||
TvResult,
|
||||
} from "@/utils/jellyseerr/server/models/Search";
|
||||
import { useReactNavigationQuery } from "@/utils/useReactNavigationQuery";
|
||||
import { orderBy, uniqBy } from "lodash";
|
||||
import type React from "react";
|
||||
import { useMemo, useState } from "react";
|
||||
import { useTranslation } from "react-i18next";
|
||||
import { View, type ViewProps } from "react-native";
|
||||
import {
|
||||
useAnimatedReaction,
|
||||
useAnimatedStyle,
|
||||
useSharedValue,
|
||||
withTiming,
|
||||
} from "react-native-reanimated";
|
||||
import { Text } from "../common/Text";
|
||||
import JellyseerrPoster from "../posters/JellyseerrPoster";
|
||||
import { LoadingSkeleton } from "../search/LoadingSkeleton";
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
import { MediaType } from "@/utils/jellyseerr/server/constants/media";
|
||||
import { Feather, MaterialCommunityIcons } from "@expo/vector-icons";
|
||||
import { useMemo } from "react";
|
||||
import { View, type ViewProps } from "react-native";
|
||||
import { MediaType } from "@/utils/jellyseerr/server/constants/media";
|
||||
|
||||
const JellyseerrMediaIcon: React.FC<
|
||||
{ mediaType: "tv" | "movie" } & ViewProps
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
import { MediaStatus } from "@/utils/jellyseerr/server/constants/media";
|
||||
import { MaterialCommunityIcons } from "@expo/vector-icons";
|
||||
import { useEffect, useState } from "react";
|
||||
import { TouchableOpacity, View, type ViewProps } from "react-native";
|
||||
import { MediaStatus } from "@/utils/jellyseerr/server/constants/media";
|
||||
|
||||
interface Props {
|
||||
mediaStatus?: MediaStatus;
|
||||
|
||||
@@ -1,7 +1,4 @@
|
||||
import { ParallaxScrollView } from "@/components/ParallaxPage";
|
||||
import { Text } from "@/components/common/Text";
|
||||
import { FlashList } from "@shopify/flash-list";
|
||||
import { useFocusEffect } from "expo-router";
|
||||
import type React from "react";
|
||||
import {
|
||||
type PropsWithChildren,
|
||||
@@ -10,9 +7,10 @@ import {
|
||||
useRef,
|
||||
useState,
|
||||
} from "react";
|
||||
import { Dimensions, View, type ViewProps } from "react-native";
|
||||
import { Animated } from "react-native";
|
||||
import { Animated, View, type ViewProps } from "react-native";
|
||||
import { useSafeAreaInsets } from "react-native-safe-area-context";
|
||||
import { Text } from "@/components/common/Text";
|
||||
import { ParallaxScrollView } from "@/components/ParallaxPage";
|
||||
|
||||
const ANIMATION_ENTER = 250;
|
||||
const ANIMATION_EXIT = 250;
|
||||
|
||||
@@ -1,9 +1,9 @@
|
||||
import { Text } from "@/components/common/Text";
|
||||
import Poster from "@/components/posters/Poster";
|
||||
import { useJellyseerr } from "@/hooks/useJellyseerr";
|
||||
import { useRouter, useSegments } from "expo-router";
|
||||
import type React from "react";
|
||||
import { TouchableOpacity, View, type ViewProps } from "react-native";
|
||||
import { Text } from "@/components/common/Text";
|
||||
import Poster from "@/components/posters/Poster";
|
||||
import { useJellyseerr } from "@/hooks/useJellyseerr";
|
||||
|
||||
interface Props {
|
||||
id: string;
|
||||
|
||||
@@ -1,3 +1,14 @@
|
||||
import {
|
||||
BottomSheetBackdrop,
|
||||
type BottomSheetBackdropProps,
|
||||
BottomSheetModal,
|
||||
BottomSheetView,
|
||||
} from "@gorhom/bottom-sheet";
|
||||
import type { BottomSheetModalMethods } from "@gorhom/bottom-sheet/lib/typescript/types";
|
||||
import { useQuery } from "@tanstack/react-query";
|
||||
import { forwardRef, useCallback, useMemo, useState } from "react";
|
||||
import { useTranslation } from "react-i18next";
|
||||
import { View, type ViewProps } from "react-native";
|
||||
import { Button } from "@/components/Button";
|
||||
import Dropdown from "@/components/common/Dropdown";
|
||||
import { Text } from "@/components/common/Text";
|
||||
@@ -10,17 +21,6 @@ import type {
|
||||
import type { MediaType } from "@/utils/jellyseerr/server/constants/media";
|
||||
import type { MediaRequestBody } from "@/utils/jellyseerr/server/interfaces/api/requestInterfaces";
|
||||
import { writeDebugLog } from "@/utils/log";
|
||||
import {
|
||||
BottomSheetBackdrop,
|
||||
type BottomSheetBackdropProps,
|
||||
BottomSheetModal,
|
||||
BottomSheetView,
|
||||
} from "@gorhom/bottom-sheet";
|
||||
import type { BottomSheetModalMethods } from "@gorhom/bottom-sheet/lib/typescript/types";
|
||||
import { useQuery } from "@tanstack/react-query";
|
||||
import React, { forwardRef, useCallback, useMemo, useState } from "react";
|
||||
import { useTranslation } from "react-i18next";
|
||||
import { View, type ViewProps } from "react-native";
|
||||
|
||||
interface Props {
|
||||
id: number;
|
||||
|
||||
@@ -1,3 +1,7 @@
|
||||
import { router, useSegments } from "expo-router";
|
||||
import type React from "react";
|
||||
import { useCallback } from "react";
|
||||
import { TouchableOpacity, type ViewProps } from "react-native";
|
||||
import GenericSlideCard from "@/components/jellyseerr/discover/GenericSlideCard";
|
||||
import Slide, { type SlideProps } from "@/components/jellyseerr/discover/Slide";
|
||||
import { useJellyseerr } from "@/hooks/useJellyseerr";
|
||||
@@ -6,10 +10,6 @@ import {
|
||||
type Network,
|
||||
} from "@/utils/jellyseerr/src/components/Discover/NetworkSlider";
|
||||
import type { Studio } from "@/utils/jellyseerr/src/components/Discover/StudioSlider";
|
||||
import { router, useSegments } from "expo-router";
|
||||
import type React from "react";
|
||||
import { useCallback } from "react";
|
||||
import { TouchableOpacity, type ViewProps } from "react-native";
|
||||
|
||||
const CompanySlide: React.FC<
|
||||
{ data: Network[] | Studio[] } & SlideProps & ViewProps
|
||||
@@ -33,7 +33,7 @@ const CompanySlide: React.FC<
|
||||
slide={slide}
|
||||
data={data}
|
||||
keyExtractor={(item) => item.id.toString()}
|
||||
renderItem={(item, index) => (
|
||||
renderItem={(item, _index) => (
|
||||
<TouchableOpacity className='mr-2' onPress={() => navigate(item)}>
|
||||
<GenericSlideCard
|
||||
className='w-28 rounded-lg overflow-hidden border border-neutral-900 p-4'
|
||||
|
||||
@@ -1,3 +1,7 @@
|
||||
import { sortBy } from "lodash";
|
||||
import type React from "react";
|
||||
import { useMemo } from "react";
|
||||
import { View } from "react-native";
|
||||
import CompanySlide from "@/components/jellyseerr/discover/CompanySlide";
|
||||
import GenreSlide from "@/components/jellyseerr/discover/GenreSlide";
|
||||
import MovieTvSlide from "@/components/jellyseerr/discover/MovieTvSlide";
|
||||
@@ -6,10 +10,6 @@ import { DiscoverSliderType } from "@/utils/jellyseerr/server/constants/discover
|
||||
import type DiscoverSlider from "@/utils/jellyseerr/server/entity/DiscoverSlider";
|
||||
import { networks } from "@/utils/jellyseerr/src/components/Discover/NetworkSlider";
|
||||
import { studios } from "@/utils/jellyseerr/src/components/Discover/StudioSlider";
|
||||
import { sortBy } from "lodash";
|
||||
import type React from "react";
|
||||
import { useMemo } from "react";
|
||||
import { View } from "react-native";
|
||||
|
||||
interface Props {
|
||||
sliders?: DiscoverSlider[];
|
||||
|
||||
@@ -1,8 +1,8 @@
|
||||
import { Text } from "@/components/common/Text";
|
||||
import { Image, type ImageContentFit } from "expo-image";
|
||||
import { LinearGradient } from "expo-linear-gradient";
|
||||
import type React from "react";
|
||||
import { StyleSheet, View, type ViewProps } from "react-native";
|
||||
import { Text } from "@/components/common/Text";
|
||||
|
||||
export const textShadowStyle = StyleSheet.create({
|
||||
shadow: {
|
||||
|
||||
@@ -1,14 +1,14 @@
|
||||
import { useQuery } from "@tanstack/react-query";
|
||||
import { router, useSegments } from "expo-router";
|
||||
import type React from "react";
|
||||
import { useCallback } from "react";
|
||||
import { TouchableOpacity, type ViewProps } from "react-native";
|
||||
import GenericSlideCard from "@/components/jellyseerr/discover/GenericSlideCard";
|
||||
import Slide, { type SlideProps } from "@/components/jellyseerr/discover/Slide";
|
||||
import { Endpoints, useJellyseerr } from "@/hooks/useJellyseerr";
|
||||
import { DiscoverSliderType } from "@/utils/jellyseerr/server/constants/discover";
|
||||
import type { GenreSliderItem } from "@/utils/jellyseerr/server/interfaces/api/discoverInterfaces";
|
||||
import { genreColorMap } from "@/utils/jellyseerr/src/components/Discover/constants";
|
||||
import { useQuery } from "@tanstack/react-query";
|
||||
import { router, useSegments } from "expo-router";
|
||||
import type React from "react";
|
||||
import { useCallback } from "react";
|
||||
import { TouchableOpacity, type ViewProps } from "react-native";
|
||||
|
||||
const GenreSlide: React.FC<SlideProps & ViewProps> = ({ slide, ...props }) => {
|
||||
const segments = useSegments();
|
||||
@@ -43,7 +43,7 @@ const GenreSlide: React.FC<SlideProps & ViewProps> = ({ slide, ...props }) => {
|
||||
slide={slide}
|
||||
data={data}
|
||||
keyExtractor={(item) => item.id.toString()}
|
||||
renderItem={(item, index) => (
|
||||
renderItem={(item, _index) => (
|
||||
<TouchableOpacity className='mr-2' onPress={() => navigate(item)}>
|
||||
<GenericSlideCard
|
||||
className='w-28 rounded-lg overflow-hidden border border-neutral-900'
|
||||
|
||||
@@ -1,3 +1,8 @@
|
||||
import { useInfiniteQuery } from "@tanstack/react-query";
|
||||
import { uniqBy } from "lodash";
|
||||
import type React from "react";
|
||||
import { useMemo } from "react";
|
||||
import type { ViewProps } from "react-native";
|
||||
import Slide, { type SlideProps } from "@/components/jellyseerr/discover/Slide";
|
||||
import JellyseerrPoster from "@/components/posters/JellyseerrPoster";
|
||||
import {
|
||||
@@ -10,11 +15,6 @@ import type {
|
||||
MovieResult,
|
||||
TvResult,
|
||||
} from "@/utils/jellyseerr/server/models/Search";
|
||||
import { useInfiniteQuery } from "@tanstack/react-query";
|
||||
import { uniqBy } from "lodash";
|
||||
import type React from "react";
|
||||
import { useMemo } from "react";
|
||||
import type { ViewProps } from "react-native";
|
||||
|
||||
const MovieTvSlide: React.FC<SlideProps & ViewProps> = ({
|
||||
slide,
|
||||
@@ -25,7 +25,7 @@ const MovieTvSlide: React.FC<SlideProps & ViewProps> = ({
|
||||
const { data, fetchNextPage, hasNextPage } = useInfiniteQuery({
|
||||
queryKey: ["jellyseerr", "discover", slide.id],
|
||||
queryFn: async ({ pageParam }) => {
|
||||
let endpoint: DiscoverEndpoint | undefined = undefined;
|
||||
let endpoint: DiscoverEndpoint | undefined;
|
||||
let params: any = {
|
||||
page: Number(pageParam),
|
||||
};
|
||||
|
||||
@@ -1,12 +1,12 @@
|
||||
import { useQuery } from "@tanstack/react-query";
|
||||
import type React from "react";
|
||||
import type { ViewProps } from "react-native";
|
||||
import Slide, { type SlideProps } from "@/components/jellyseerr/discover/Slide";
|
||||
import JellyseerrPoster from "@/components/posters/JellyseerrPoster";
|
||||
import { useJellyseerr } from "@/hooks/useJellyseerr";
|
||||
import { MediaType } from "@/utils/jellyseerr/server/constants/media";
|
||||
import type MediaRequest from "@/utils/jellyseerr/server/entity/MediaRequest";
|
||||
import type { NonFunctionProperties } from "@/utils/jellyseerr/server/interfaces/api/common";
|
||||
import { useQuery } from "@tanstack/react-query";
|
||||
import type React from "react";
|
||||
import type { ViewProps } from "react-native";
|
||||
|
||||
const RequestCard: React.FC<{ request: MediaRequest }> = ({ request }) => {
|
||||
const { jellyseerrApi } = useJellyseerr();
|
||||
|
||||
@@ -1,12 +1,12 @@
|
||||
import { Text } from "@/components/common/Text";
|
||||
import { DiscoverSliderType } from "@/utils/jellyseerr/server/constants/discover";
|
||||
import type DiscoverSlider from "@/utils/jellyseerr/server/entity/DiscoverSlider";
|
||||
import { FlashList } from "@shopify/flash-list";
|
||||
import type { ContentStyle } from "@shopify/flash-list/src/FlashListProps";
|
||||
import { t } from "i18next";
|
||||
import type React from "react";
|
||||
import type { PropsWithChildren } from "react";
|
||||
import { View, type ViewProps } from "react-native";
|
||||
import { Text } from "@/components/common/Text";
|
||||
import { DiscoverSliderType } from "@/utils/jellyseerr/server/constants/discover";
|
||||
import type DiscoverSlider from "@/utils/jellyseerr/server/entity/DiscoverSlider";
|
||||
|
||||
export interface SlideProps {
|
||||
slide: DiscoverSlider;
|
||||
|
||||
@@ -1,7 +1,3 @@
|
||||
import { Text } from "@/components/common/Text";
|
||||
import { apiAtom, userAtom } from "@/providers/JellyfinProvider";
|
||||
import { useSettings } from "@/utils/atoms/settings";
|
||||
import { getPrimaryImageUrl } from "@/utils/jellyfin/image/getPrimaryImageUrl";
|
||||
import { Ionicons } from "@expo/vector-icons";
|
||||
import type {
|
||||
BaseItemDto,
|
||||
@@ -15,6 +11,10 @@ import { useAtom } from "jotai";
|
||||
import { useMemo } from "react";
|
||||
import { useTranslation } from "react-i18next";
|
||||
import { type TouchableOpacityProps, View } from "react-native";
|
||||
import { Text } from "@/components/common/Text";
|
||||
import { apiAtom, userAtom } from "@/providers/JellyfinProvider";
|
||||
import { useSettings } from "@/utils/atoms/settings";
|
||||
import { getPrimaryImageUrl } from "@/utils/jellyfin/image/getPrimaryImageUrl";
|
||||
import { TouchableItemRouter } from "../common/TouchableItemRouter";
|
||||
|
||||
interface Props extends TouchableOpacityProps {
|
||||
@@ -32,7 +32,6 @@ const icons: Record<CollectionType, IconName> = {
|
||||
boxsets: "albums",
|
||||
playlists: "list",
|
||||
folders: "folder",
|
||||
livetv: "tv",
|
||||
musicvideos: "musical-notes",
|
||||
photos: "images",
|
||||
trailers: "videocam",
|
||||
|
||||
@@ -1,13 +1,12 @@
|
||||
import {
|
||||
Children,
|
||||
type PropsWithChildren,
|
||||
type ReactElement,
|
||||
cloneElement,
|
||||
isValidElement,
|
||||
type PropsWithChildren,
|
||||
type ReactElement,
|
||||
} from "react";
|
||||
import { StyleSheet, View, type ViewProps, type ViewStyle } from "react-native";
|
||||
import { Text } from "../common/Text";
|
||||
import { ListItem } from "./ListItem";
|
||||
|
||||
interface Props extends ViewProps {
|
||||
title?: string | null | undefined;
|
||||
|
||||
@@ -1,43 +0,0 @@
|
||||
import React from "react";
|
||||
import { View } from "react-native";
|
||||
import { Text } from "../common/Text";
|
||||
|
||||
export const HourHeader = ({ height }: { height: number }) => {
|
||||
const now = new Date();
|
||||
const currentHour = now.getHours();
|
||||
const hoursRemaining = 24 - currentHour;
|
||||
const hours = generateHours(currentHour, hoursRemaining);
|
||||
|
||||
return (
|
||||
<View
|
||||
className='flex flex-row'
|
||||
style={{
|
||||
height,
|
||||
}}
|
||||
>
|
||||
{hours.map((hour, index) => (
|
||||
<HourCell key={index} hour={hour} />
|
||||
))}
|
||||
</View>
|
||||
);
|
||||
};
|
||||
|
||||
const HourCell = ({ hour }: { hour: Date }) => (
|
||||
<View className='w-[200px] flex items-center justify-center bg-neutral-800'>
|
||||
<Text className='text-xs text-gray-600'>
|
||||
{hour.toLocaleTimeString([], {
|
||||
hour: "2-digit",
|
||||
minute: "2-digit",
|
||||
})}
|
||||
</Text>
|
||||
</View>
|
||||
);
|
||||
|
||||
const generateHours = (startHour: number, count: number): Date[] => {
|
||||
const now = new Date();
|
||||
return Array.from({ length: count }, (_, i) => {
|
||||
const hour = new Date(now);
|
||||
hour.setHours(startHour + i, 0, 0, 0);
|
||||
return hour;
|
||||
});
|
||||
};
|
||||
@@ -1,96 +0,0 @@
|
||||
import type { BaseItemDto } from "@jellyfin/sdk/lib/generated-client";
|
||||
import { useMemo, useRef } from "react";
|
||||
import { Dimensions, View } from "react-native";
|
||||
import { Text } from "../common/Text";
|
||||
import { TouchableItemRouter } from "../common/TouchableItemRouter";
|
||||
|
||||
export const LiveTVGuideRow = ({
|
||||
channel,
|
||||
programs,
|
||||
scrollX = 0,
|
||||
isVisible = true,
|
||||
}: {
|
||||
channel: BaseItemDto;
|
||||
programs?: BaseItemDto[] | null;
|
||||
scrollX?: number;
|
||||
isVisible?: boolean;
|
||||
}) => {
|
||||
const positionRefs = useRef<{ [key: string]: number }>({});
|
||||
const screenWidth = Dimensions.get("window").width;
|
||||
|
||||
const calculateWidth = (s?: string | null, e?: string | null) => {
|
||||
if (!s || !e) return 0;
|
||||
const start = new Date(s);
|
||||
const end = new Date(e);
|
||||
const duration = end.getTime() - start.getTime();
|
||||
const minutes = duration / 60000;
|
||||
const width = (minutes / 60) * 200;
|
||||
return width;
|
||||
};
|
||||
|
||||
const programsWithPositions = useMemo(() => {
|
||||
let cumulativeWidth = 0;
|
||||
return programs
|
||||
?.filter((p) => p.ChannelId === channel.Id)
|
||||
.map((p) => {
|
||||
const width = calculateWidth(p.StartDate, p.EndDate);
|
||||
const position = cumulativeWidth;
|
||||
cumulativeWidth += width;
|
||||
return { ...p, width, position };
|
||||
});
|
||||
}, [programs, channel.Id]);
|
||||
|
||||
const isCurrentlyLive = (program: BaseItemDto) => {
|
||||
if (!program.StartDate || !program.EndDate) return false;
|
||||
const now = new Date();
|
||||
const start = new Date(program.StartDate);
|
||||
const end = new Date(program.EndDate);
|
||||
return now >= start && now <= end;
|
||||
};
|
||||
|
||||
if (!isVisible) {
|
||||
return <View style={{ height: 64 }} />;
|
||||
}
|
||||
|
||||
return (
|
||||
<View key={channel.ChannelNumber} className='flex flex-row h-16'>
|
||||
{programsWithPositions?.map((p) => (
|
||||
<TouchableItemRouter item={p} key={p.Id}>
|
||||
<View
|
||||
style={{
|
||||
width: p.width,
|
||||
height: "100%",
|
||||
position: "absolute",
|
||||
left: p.position,
|
||||
backgroundColor: isCurrentlyLive(p)
|
||||
? "rgba(255, 255, 255, 0.1)"
|
||||
: "transparent",
|
||||
}}
|
||||
className='flex flex-col items-center justify-center border border-neutral-800 overflow-hidden'
|
||||
>
|
||||
{(() => {
|
||||
return (
|
||||
<View
|
||||
style={{
|
||||
marginLeft:
|
||||
p.width > screenWidth && scrollX > p.position
|
||||
? scrollX - p.position
|
||||
: 0,
|
||||
}}
|
||||
className='px-4 self-start'
|
||||
>
|
||||
<Text
|
||||
numberOfLines={2}
|
||||
className='text-xs text-start self-start'
|
||||
>
|
||||
{p.Name}
|
||||
</Text>
|
||||
</View>
|
||||
);
|
||||
})()}
|
||||
</View>
|
||||
</TouchableItemRouter>
|
||||
))}
|
||||
</View>
|
||||
);
|
||||
};
|
||||
@@ -1,4 +1,3 @@
|
||||
import { apiAtom, userAtom } from "@/providers/JellyfinProvider";
|
||||
import type {
|
||||
BaseItemDto,
|
||||
BaseItemDtoQueryResult,
|
||||
@@ -12,10 +11,11 @@ import {
|
||||
import { useAtom } from "jotai";
|
||||
import { useCallback } from "react";
|
||||
import { View, type ViewProps } from "react-native";
|
||||
import { ItemCardText } from "../ItemCardText";
|
||||
import { apiAtom, userAtom } from "@/providers/JellyfinProvider";
|
||||
import { InfiniteHorizontalScroll } from "../common/InfiniteHorrizontalScroll";
|
||||
import { Text } from "../common/Text";
|
||||
import { TouchableItemRouter } from "../common/TouchableItemRouter";
|
||||
import { ItemCardText } from "../ItemCardText";
|
||||
import MoviePoster from "../posters/MoviePoster";
|
||||
|
||||
interface Props extends ViewProps {
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
import { Text } from "@/components/common/Text";
|
||||
import type { BaseItemDto } from "@jellyfin/sdk/lib/generated-client/models";
|
||||
import { View, type ViewProps } from "react-native";
|
||||
import { Text } from "@/components/common/Text";
|
||||
|
||||
interface Props extends ViewProps {
|
||||
item: BaseItemDto;
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
// You can explore the built-in icon families and icons on the web at https://icons.expo.fyi/
|
||||
|
||||
import Ionicons from "@expo/vector-icons/Ionicons";
|
||||
import type { IconProps } from "@expo/vector-icons/build/createIconSet";
|
||||
import Ionicons from "@expo/vector-icons/Ionicons";
|
||||
import type { ComponentProps } from "react";
|
||||
|
||||
export function TabBarIcon({
|
||||
|
||||
@@ -1,11 +1,10 @@
|
||||
import { WatchedIndicator } from "@/components/WatchedIndicator";
|
||||
import { apiAtom } from "@/providers/JellyfinProvider";
|
||||
import { getPrimaryImageUrl } from "@/utils/jellyfin/image/getPrimaryImageUrl";
|
||||
import type { BaseItemDto } from "@jellyfin/sdk/lib/generated-client/models";
|
||||
import { Image } from "expo-image";
|
||||
import { useAtom } from "jotai";
|
||||
import { useMemo, useState } from "react";
|
||||
import { View } from "react-native";
|
||||
import { WatchedIndicator } from "@/components/WatchedIndicator";
|
||||
import { apiAtom } from "@/providers/JellyfinProvider";
|
||||
|
||||
type MoviePosterProps = {
|
||||
item: BaseItemDto;
|
||||
@@ -24,7 +23,7 @@ export const EpisodePoster: React.FC<MoviePosterProps> = ({
|
||||
}
|
||||
}, [item]);
|
||||
|
||||
const [progress, setProgress] = useState(
|
||||
const [progress, _setProgress] = useState(
|
||||
item.UserData?.PlayedPercentage || 0,
|
||||
);
|
||||
|
||||
|
||||
@@ -1,12 +1,8 @@
|
||||
import { Text } from "@/components/common/Text";
|
||||
import {
|
||||
type BaseItemDto,
|
||||
BaseItemKind,
|
||||
} from "@jellyfin/sdk/lib/generated-client/models";
|
||||
import { type BaseItemDto } from "@jellyfin/sdk/lib/generated-client/models";
|
||||
import { useState } from "react";
|
||||
import { View, type ViewProps } from "react-native";
|
||||
import { WatchedIndicator } from "../WatchedIndicator";
|
||||
import { ItemImage } from "../common/ItemImage";
|
||||
import { WatchedIndicator } from "../WatchedIndicator";
|
||||
|
||||
interface Props extends ViewProps {
|
||||
item: BaseItemDto;
|
||||
@@ -18,7 +14,7 @@ export const ItemPoster: React.FC<Props> = ({
|
||||
showProgress,
|
||||
...props
|
||||
}) => {
|
||||
const [progress, setProgress] = useState(
|
||||
const [progress, _setProgress] = useState(
|
||||
item.UserData?.PlayedPercentage || 0,
|
||||
);
|
||||
|
||||
|
||||
@@ -1,9 +1,18 @@
|
||||
import { Tag, Tags } from "@/components/GenreTags";
|
||||
import { Image } from "expo-image";
|
||||
import { useMemo } from "react";
|
||||
import { useTranslation } from "react-i18next";
|
||||
import { View, type ViewProps } from "react-native";
|
||||
import Animated, {
|
||||
useAnimatedStyle,
|
||||
useSharedValue,
|
||||
withTiming,
|
||||
} from "react-native-reanimated";
|
||||
import { TouchableJellyseerrRouter } from "@/components/common/JellyseerrItemRouter";
|
||||
import { Text } from "@/components/common/Text";
|
||||
import { Tag, Tags } from "@/components/GenreTags";
|
||||
import { textShadowStyle } from "@/components/jellyseerr/discover/GenericSlideCard";
|
||||
import JellyseerrMediaIcon from "@/components/jellyseerr/JellyseerrMediaIcon";
|
||||
import JellyseerrStatusIcon from "@/components/jellyseerr/JellyseerrStatusIcon";
|
||||
import { textShadowStyle } from "@/components/jellyseerr/discover/GenericSlideCard";
|
||||
import { Colors } from "@/constants/Colors";
|
||||
import { useJellyseerr } from "@/hooks/useJellyseerr";
|
||||
import { useJellyseerrCanRequest } from "@/utils/_jellyseerr/useJellyseerrCanRequest";
|
||||
@@ -16,15 +25,6 @@ import type {
|
||||
TvResult,
|
||||
} from "@/utils/jellyseerr/server/models/Search";
|
||||
import type { TvDetails } from "@/utils/jellyseerr/server/models/Tv";
|
||||
import { Image } from "expo-image";
|
||||
import { useMemo } from "react";
|
||||
import { useTranslation } from "react-i18next";
|
||||
import { View, type ViewProps } from "react-native";
|
||||
import Animated, {
|
||||
useAnimatedStyle,
|
||||
useSharedValue,
|
||||
withTiming,
|
||||
} from "react-native-reanimated";
|
||||
|
||||
interface Props extends ViewProps {
|
||||
item?: MovieResult | TvResult | MovieDetails | TvDetails;
|
||||
|
||||
@@ -1,11 +1,11 @@
|
||||
import { WatchedIndicator } from "@/components/WatchedIndicator";
|
||||
import { apiAtom } from "@/providers/JellyfinProvider";
|
||||
import { getPrimaryImageUrl } from "@/utils/jellyfin/image/getPrimaryImageUrl";
|
||||
import type { BaseItemDto } from "@jellyfin/sdk/lib/generated-client/models";
|
||||
import { Image } from "expo-image";
|
||||
import { useAtom } from "jotai";
|
||||
import { useMemo, useState } from "react";
|
||||
import { View } from "react-native";
|
||||
import { WatchedIndicator } from "@/components/WatchedIndicator";
|
||||
import { apiAtom } from "@/providers/JellyfinProvider";
|
||||
import { getPrimaryImageUrl } from "@/utils/jellyfin/image/getPrimaryImageUrl";
|
||||
|
||||
type MoviePosterProps = {
|
||||
item: BaseItemDto;
|
||||
@@ -26,7 +26,7 @@ const MoviePoster: React.FC<MoviePosterProps> = ({
|
||||
});
|
||||
}, [item]);
|
||||
|
||||
const [progress, setProgress] = useState(
|
||||
const [progress, _setProgress] = useState(
|
||||
item.UserData?.PlayedPercentage || 0,
|
||||
);
|
||||
|
||||
|
||||
@@ -1,8 +1,8 @@
|
||||
import { apiAtom } from "@/providers/JellyfinProvider";
|
||||
import { Image } from "expo-image";
|
||||
import { useAtom } from "jotai";
|
||||
import { useMemo } from "react";
|
||||
import { View } from "react-native";
|
||||
import { apiAtom } from "@/providers/JellyfinProvider";
|
||||
|
||||
type PosterProps = {
|
||||
id?: string;
|
||||
|
||||
@@ -1,11 +1,10 @@
|
||||
import { WatchedIndicator } from "@/components/WatchedIndicator";
|
||||
import { apiAtom } from "@/providers/JellyfinProvider";
|
||||
import { getPrimaryImageUrl } from "@/utils/jellyfin/image/getPrimaryImageUrl";
|
||||
import type { BaseItemDto } from "@jellyfin/sdk/lib/generated-client/models";
|
||||
import { Image } from "expo-image";
|
||||
import { useAtom } from "jotai";
|
||||
import { useMemo, useState } from "react";
|
||||
import { useMemo } from "react";
|
||||
import { View } from "react-native";
|
||||
import { apiAtom } from "@/providers/JellyfinProvider";
|
||||
import { getPrimaryImageUrl } from "@/utils/jellyfin/image/getPrimaryImageUrl";
|
||||
|
||||
type MoviePosterProps = {
|
||||
item: BaseItemDto;
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
import { View } from "react-native";
|
||||
import Animated, {
|
||||
useAnimatedStyle,
|
||||
useAnimatedReaction,
|
||||
useAnimatedStyle,
|
||||
useSharedValue,
|
||||
withTiming,
|
||||
} from "react-native-reanimated";
|
||||
|
||||
@@ -1,11 +1,8 @@
|
||||
import { apiAtom, userAtom } from "@/providers/JellyfinProvider";
|
||||
import { getUserItemData } from "@/utils/jellyfin/user-library/getUserItemData";
|
||||
import type { BaseItemDto } from "@jellyfin/sdk/lib/generated-client";
|
||||
import { FlashList } from "@shopify/flash-list";
|
||||
import { useQuery } from "@tanstack/react-query";
|
||||
import { useAtom } from "jotai";
|
||||
import type React from "react";
|
||||
import type { PropsWithChildren } from "react";
|
||||
import { apiAtom, userAtom } from "@/providers/JellyfinProvider";
|
||||
import { Text } from "../common/Text";
|
||||
|
||||
type SearchItemWrapperProps<T> = {
|
||||
@@ -21,8 +18,8 @@ export const SearchItemWrapper = <T,>({
|
||||
header,
|
||||
onEndReached,
|
||||
}: PropsWithChildren<SearchItemWrapperProps<T>>) => {
|
||||
const [api] = useAtom(apiAtom);
|
||||
const [user] = useAtom(userAtom);
|
||||
const [_api] = useAtom(apiAtom);
|
||||
const [_user] = useAtom(userAtom);
|
||||
|
||||
if (!items || items.length === 0) return null;
|
||||
|
||||
|
||||
@@ -1,5 +1,3 @@
|
||||
import { apiAtom } from "@/providers/JellyfinProvider";
|
||||
import { getPrimaryImageUrl } from "@/utils/jellyfin/image/getPrimaryImageUrl";
|
||||
import type {
|
||||
BaseItemDto,
|
||||
BaseItemPerson,
|
||||
@@ -10,6 +8,8 @@ import type React from "react";
|
||||
import { useMemo } from "react";
|
||||
import { useTranslation } from "react-i18next";
|
||||
import { TouchableOpacity, View, type ViewProps } from "react-native";
|
||||
import { apiAtom } from "@/providers/JellyfinProvider";
|
||||
import { getPrimaryImageUrl } from "@/utils/jellyfin/image/getPrimaryImageUrl";
|
||||
import { HorizontalScroll } from "../common/HorrizontalScroll";
|
||||
import { Text } from "../common/Text";
|
||||
import { itemRouter } from "../common/TouchableItemRouter";
|
||||
@@ -48,7 +48,7 @@ export const CastAndCrew: React.FC<Props> = ({ item, loading, ...props }) => {
|
||||
</Text>
|
||||
<HorizontalScroll
|
||||
loading={loading}
|
||||
keyExtractor={(i, idx) => i.Id.toString()}
|
||||
keyExtractor={(i, _idx) => i.Id.toString()}
|
||||
height={247}
|
||||
data={destinctPeople}
|
||||
renderItem={(i) => (
|
||||
|
||||
@@ -1,11 +1,11 @@
|
||||
import { apiAtom } from "@/providers/JellyfinProvider";
|
||||
import { getPrimaryImageUrlById } from "@/utils/jellyfin/image/getPrimaryImageUrlById";
|
||||
import type { BaseItemDto } from "@jellyfin/sdk/lib/generated-client/models";
|
||||
import { router } from "expo-router";
|
||||
import { useAtom } from "jotai";
|
||||
import type React from "react";
|
||||
import { useTranslation } from "react-i18next";
|
||||
import { TouchableOpacity, View, type ViewProps } from "react-native";
|
||||
import { apiAtom } from "@/providers/JellyfinProvider";
|
||||
import { getPrimaryImageUrlById } from "@/utils/jellyfin/image/getPrimaryImageUrlById";
|
||||
import { HorizontalScroll } from "../common/HorrizontalScroll";
|
||||
import { Text } from "../common/Text";
|
||||
import Poster from "../posters/Poster";
|
||||
@@ -26,7 +26,7 @@ export const CurrentSeries: React.FC<Props> = ({ item, ...props }) => {
|
||||
<HorizontalScroll
|
||||
data={[item]}
|
||||
height={247}
|
||||
renderItem={(item, index) => (
|
||||
renderItem={(item, _index) => (
|
||||
<TouchableOpacity
|
||||
key={item.Id}
|
||||
onPress={() => router.push(`/series/${item.SeriesId}`)}
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
import { Text } from "@/components/common/Text";
|
||||
import type { BaseItemDto } from "@jellyfin/sdk/lib/generated-client/models";
|
||||
import { useRouter } from "expo-router";
|
||||
import { TouchableOpacity, View, type ViewProps } from "react-native";
|
||||
import { Text } from "@/components/common/Text";
|
||||
|
||||
interface Props extends ViewProps {
|
||||
item: BaseItemDto;
|
||||
|
||||
@@ -1,21 +1,3 @@
|
||||
import { Tags } from "@/components/GenreTags";
|
||||
import { RoundButton } from "@/components/RoundButton";
|
||||
import { HorizontalScroll } from "@/components/common/HorrizontalScroll";
|
||||
import { Text } from "@/components/common/Text";
|
||||
import { dateOpts } from "@/components/jellyseerr/DetailFacts";
|
||||
import JellyseerrStatusIcon from "@/components/jellyseerr/JellyseerrStatusIcon";
|
||||
import { textShadowStyle } from "@/components/jellyseerr/discover/GenericSlideCard";
|
||||
import { useJellyseerr } from "@/hooks/useJellyseerr";
|
||||
import {
|
||||
MediaStatus,
|
||||
MediaType,
|
||||
} from "@/utils/jellyseerr/server/constants/media";
|
||||
import type MediaRequest from "@/utils/jellyseerr/server/entity/MediaRequest";
|
||||
import type Season from "@/utils/jellyseerr/server/entity/Season";
|
||||
import type { MediaRequestBody } from "@/utils/jellyseerr/server/interfaces/api/requestInterfaces";
|
||||
import type { MovieDetails } from "@/utils/jellyseerr/server/models/Movie";
|
||||
import { TvResult } from "@/utils/jellyseerr/server/models/Search";
|
||||
import type { TvDetails } from "@/utils/jellyseerr/server/models/Tv";
|
||||
import { Ionicons } from "@expo/vector-icons";
|
||||
import { FlashList } from "@shopify/flash-list";
|
||||
import {
|
||||
@@ -29,6 +11,23 @@ import { orderBy } from "lodash";
|
||||
import type React from "react";
|
||||
import { useCallback, useMemo, useState } from "react";
|
||||
import { Alert, TouchableOpacity, View } from "react-native";
|
||||
import { HorizontalScroll } from "@/components/common/HorrizontalScroll";
|
||||
import { Text } from "@/components/common/Text";
|
||||
import { Tags } from "@/components/GenreTags";
|
||||
import { dateOpts } from "@/components/jellyseerr/DetailFacts";
|
||||
import { textShadowStyle } from "@/components/jellyseerr/discover/GenericSlideCard";
|
||||
import JellyseerrStatusIcon from "@/components/jellyseerr/JellyseerrStatusIcon";
|
||||
import { RoundButton } from "@/components/RoundButton";
|
||||
import { useJellyseerr } from "@/hooks/useJellyseerr";
|
||||
import {
|
||||
MediaStatus,
|
||||
MediaType,
|
||||
} from "@/utils/jellyseerr/server/constants/media";
|
||||
import type MediaRequest from "@/utils/jellyseerr/server/entity/MediaRequest";
|
||||
import type Season from "@/utils/jellyseerr/server/entity/Season";
|
||||
import type { MediaRequestBody } from "@/utils/jellyseerr/server/interfaces/api/requestInterfaces";
|
||||
import type { MovieDetails } from "@/utils/jellyseerr/server/models/Movie";
|
||||
import type { TvDetails } from "@/utils/jellyseerr/server/models/Tv";
|
||||
import { Loader } from "../Loader";
|
||||
|
||||
const JellyseerrSeasonEpisodes: React.FC<{
|
||||
@@ -91,7 +90,7 @@ const RenderItem = ({ item, index }: any) => {
|
||||
cachePolicy={"memory-disk"}
|
||||
contentFit='cover'
|
||||
className='w-full h-full'
|
||||
onError={(e) => {
|
||||
onError={(_e) => {
|
||||
setImageError(true);
|
||||
}}
|
||||
/>
|
||||
|
||||
@@ -1,4 +1,3 @@
|
||||
import { apiAtom, userAtom } from "@/providers/JellyfinProvider";
|
||||
import { Ionicons } from "@expo/vector-icons";
|
||||
import type { BaseItemDto } from "@jellyfin/sdk/lib/generated-client/models";
|
||||
import { getItemsApi } from "@jellyfin/sdk/lib/utils/api";
|
||||
@@ -6,6 +5,7 @@ import { useQuery } from "@tanstack/react-query";
|
||||
import { useRouter } from "expo-router";
|
||||
import { useAtom } from "jotai";
|
||||
import { useMemo } from "react";
|
||||
import { apiAtom, userAtom } from "@/providers/JellyfinProvider";
|
||||
import { Button } from "../Button";
|
||||
|
||||
interface Props extends React.ComponentProps<typeof Button> {
|
||||
|
||||
@@ -1,18 +1,15 @@
|
||||
import { apiAtom, userAtom } from "@/providers/JellyfinProvider";
|
||||
import { BaseItemDto } from "@jellyfin/sdk/lib/generated-client/models";
|
||||
import { getTvShowsApi } from "@jellyfin/sdk/lib/utils/api";
|
||||
import { FlashList } from "@shopify/flash-list";
|
||||
import { useQuery } from "@tanstack/react-query";
|
||||
import { router } from "expo-router";
|
||||
import { useAtom } from "jotai";
|
||||
import type React from "react";
|
||||
import { useTranslation } from "react-i18next";
|
||||
import { TouchableOpacity, View } from "react-native";
|
||||
import { View } from "react-native";
|
||||
import { apiAtom, userAtom } from "@/providers/JellyfinProvider";
|
||||
import ContinueWatchingPoster from "../ContinueWatchingPoster";
|
||||
import { ItemCardText } from "../ItemCardText";
|
||||
import { HorizontalScroll } from "../common/HorrizontalScroll";
|
||||
import { Text } from "../common/Text";
|
||||
import { TouchableItemRouter } from "../common/TouchableItemRouter";
|
||||
import { ItemCardText } from "../ItemCardText";
|
||||
|
||||
export const NextUp: React.FC<{ seriesId: string }> = ({ seriesId }) => {
|
||||
const [user] = useAtom(userAtom);
|
||||
|
||||
@@ -1,7 +1,9 @@
|
||||
import type { BaseItemDto } from "@jellyfin/sdk/lib/generated-client/models";
|
||||
import { useEffect, useMemo } from "react";
|
||||
import { Platform, TouchableOpacity, View } from "react-native";
|
||||
|
||||
const DropdownMenu = !Platform.isTV ? require("zeego/dropdown-menu") : null;
|
||||
|
||||
import { t } from "i18next";
|
||||
import { Text } from "../common/Text";
|
||||
|
||||
|
||||
@@ -1,17 +1,17 @@
|
||||
import { apiAtom, userAtom } from "@/providers/JellyfinProvider";
|
||||
import { getUserItemData } from "@/utils/jellyfin/user-library/getUserItemData";
|
||||
import type { BaseItemDto } from "@jellyfin/sdk/lib/generated-client/models";
|
||||
import { useQuery, useQueryClient } from "@tanstack/react-query";
|
||||
import { router } from "expo-router";
|
||||
import { useAtom } from "jotai";
|
||||
import { useEffect, useMemo, useRef } from "react";
|
||||
import { TouchableOpacity, View, type ViewProps } from "react-native";
|
||||
import { TouchableOpacity, type ViewProps } from "react-native";
|
||||
import { apiAtom, userAtom } from "@/providers/JellyfinProvider";
|
||||
import { getUserItemData } from "@/utils/jellyfin/user-library/getUserItemData";
|
||||
import ContinueWatchingPoster from "../ContinueWatchingPoster";
|
||||
import { ItemCardText } from "../ItemCardText";
|
||||
import {
|
||||
HorizontalScroll,
|
||||
type HorizontalScrollRef,
|
||||
} from "../common/HorrizontalScroll";
|
||||
import { ItemCardText } from "../ItemCardText";
|
||||
|
||||
interface Props extends ViewProps {
|
||||
item?: BaseItemDto | null;
|
||||
@@ -123,7 +123,7 @@ export const SeasonEpisodesCarousel: React.FC<Props> = ({
|
||||
data={episodes}
|
||||
extraData={item}
|
||||
loading={loading || isLoading || isFetching}
|
||||
renderItem={(_item, idx) => (
|
||||
renderItem={(_item, _idx) => (
|
||||
<TouchableOpacity
|
||||
key={_item.Id}
|
||||
onPress={() => {
|
||||
|
||||
@@ -1,11 +1,4 @@
|
||||
import {
|
||||
SeasonDropdown,
|
||||
type SeasonIndexState,
|
||||
} from "@/components/series/SeasonDropdown";
|
||||
import { apiAtom, userAtom } from "@/providers/JellyfinProvider";
|
||||
import { getUserItemData } from "@/utils/jellyfin/user-library/getUserItemData";
|
||||
import { runtimeTicksToSeconds } from "@/utils/time";
|
||||
import { Ionicons, MaterialCommunityIcons } from "@expo/vector-icons";
|
||||
import { Ionicons } from "@expo/vector-icons";
|
||||
import type { BaseItemDto } from "@jellyfin/sdk/lib/generated-client/models";
|
||||
import { getTvShowsApi } from "@jellyfin/sdk/lib/utils/api";
|
||||
import { useQuery, useQueryClient } from "@tanstack/react-query";
|
||||
@@ -13,12 +6,19 @@ import { atom, useAtom } from "jotai";
|
||||
import { useEffect, useMemo, useState } from "react";
|
||||
import { useTranslation } from "react-i18next";
|
||||
import { View } from "react-native";
|
||||
import {
|
||||
SeasonDropdown,
|
||||
type SeasonIndexState,
|
||||
} from "@/components/series/SeasonDropdown";
|
||||
import { apiAtom, userAtom } from "@/providers/JellyfinProvider";
|
||||
import { getUserItemData } from "@/utils/jellyfin/user-library/getUserItemData";
|
||||
import { runtimeTicksToSeconds } from "@/utils/time";
|
||||
import ContinueWatchingPoster from "../ContinueWatchingPoster";
|
||||
import { Text } from "../common/Text";
|
||||
import { TouchableItemRouter } from "../common/TouchableItemRouter";
|
||||
import { DownloadItems, DownloadSingleItem } from "../DownloadItem";
|
||||
import { Loader } from "../Loader";
|
||||
import { PlayedStatus } from "../PlayedStatus";
|
||||
import { Text } from "../common/Text";
|
||||
import { TouchableItemRouter } from "../common/TouchableItemRouter";
|
||||
|
||||
type Props = {
|
||||
item: BaseItemDto;
|
||||
|
||||
@@ -1,5 +1,3 @@
|
||||
import type { MovieDetails } from "@/utils/jellyseerr/server/models/Movie";
|
||||
import type { TvDetails } from "@/utils/jellyseerr/server/models/Tv";
|
||||
import { Ionicons } from "@expo/vector-icons";
|
||||
import type { BaseItemDto } from "@jellyfin/sdk/lib/generated-client";
|
||||
import { useCallback, useMemo } from "react";
|
||||
@@ -10,6 +8,8 @@ import {
|
||||
View,
|
||||
type ViewProps,
|
||||
} from "react-native";
|
||||
import type { MovieDetails } from "@/utils/jellyseerr/server/models/Movie";
|
||||
import type { TvDetails } from "@/utils/jellyseerr/server/models/Tv";
|
||||
|
||||
interface Props extends ViewProps {
|
||||
item: BaseItemDto | MovieDetails | TvDetails;
|
||||
|
||||
@@ -1,8 +1,8 @@
|
||||
import type { BaseItemDto } from "@jellyfin/sdk/lib/generated-client";
|
||||
import { useMemo } from "react";
|
||||
import { View } from "react-native";
|
||||
import { Ratings } from "../Ratings";
|
||||
import { Text } from "../common/Text";
|
||||
import { Ratings } from "../Ratings";
|
||||
import { ItemActions } from "./SeriesActions";
|
||||
|
||||
interface Props {
|
||||
|
||||
@@ -1,8 +1,9 @@
|
||||
const DropdownMenu = !Platform.isTV ? require("zeego/dropdown-menu") : null;
|
||||
import { APP_LANGUAGES } from "@/i18n";
|
||||
import { useSettings } from "@/utils/atoms/settings";
|
||||
|
||||
import { useTranslation } from "react-i18next";
|
||||
import { Platform, TouchableOpacity, View, type ViewProps } from "react-native";
|
||||
import { APP_LANGUAGES } from "@/i18n";
|
||||
import { useSettings } from "@/utils/atoms/settings";
|
||||
import { Text } from "../common/Text";
|
||||
import { ListGroup } from "../list/ListGroup";
|
||||
import { ListItem } from "../list/ListItem";
|
||||
|
||||
@@ -1,9 +1,11 @@
|
||||
import { Platform, TouchableOpacity, View, type ViewProps } from "react-native";
|
||||
|
||||
const DropdownMenu = !Platform.isTV ? require("zeego/dropdown-menu") : null;
|
||||
import { useSettings } from "@/utils/atoms/settings";
|
||||
|
||||
import { Ionicons } from "@expo/vector-icons";
|
||||
import { useTranslation } from "react-i18next";
|
||||
import { Switch } from "react-native-gesture-handler";
|
||||
import { useSettings } from "@/utils/atoms/settings";
|
||||
import { Text } from "../common/Text";
|
||||
import { ListGroup } from "../list/ListGroup";
|
||||
import { ListItem } from "../list/ListItem";
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
import { useSettings } from "@/utils/atoms/settings";
|
||||
import { Switch, View } from "react-native";
|
||||
import { useSettings } from "@/utils/atoms/settings";
|
||||
import { ListGroup } from "../list/ListGroup";
|
||||
import { ListItem } from "../list/ListItem";
|
||||
|
||||
|
||||
@@ -1,14 +1,13 @@
|
||||
import { useSessions, type useSessionsProps } from "@/hooks/useSessions";
|
||||
import { useSettings } from "@/utils/atoms/settings";
|
||||
import { useRouter } from "expo-router";
|
||||
import React from "react";
|
||||
import { useTranslation } from "react-i18next";
|
||||
import { View } from "react-native";
|
||||
import { useSessions, type useSessionsProps } from "@/hooks/useSessions";
|
||||
import { useSettings } from "@/utils/atoms/settings";
|
||||
import { ListGroup } from "../list/ListGroup";
|
||||
import { ListItem } from "../list/ListItem";
|
||||
|
||||
export const Dashboard = () => {
|
||||
const [settings, updateSettings] = useSettings();
|
||||
const [settings, _updateSettings] = useSettings();
|
||||
const { sessions = [], isLoading } = useSessions({} as useSessionsProps);
|
||||
const router = useRouter();
|
||||
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
import { Text } from "@/components/common/Text";
|
||||
import { View, type ViewProps } from "react-native";
|
||||
import { Text } from "@/components/common/Text";
|
||||
|
||||
const DisabledSetting: React.FC<
|
||||
{ disabled: boolean; showText?: boolean; text?: string } & ViewProps
|
||||
|
||||
@@ -1,3 +1,8 @@
|
||||
import { Ionicons } from "@expo/vector-icons";
|
||||
import { useQueryClient } from "@tanstack/react-query";
|
||||
import { useRouter } from "expo-router";
|
||||
import { useMemo } from "react";
|
||||
import { Platform, Switch, TouchableOpacity } from "react-native";
|
||||
import { Stepper } from "@/components/inputs/Stepper";
|
||||
import { useDownload } from "@/providers/DownloadProvider";
|
||||
import {
|
||||
@@ -5,14 +10,11 @@ import {
|
||||
type Settings,
|
||||
useSettings,
|
||||
} from "@/utils/atoms/settings";
|
||||
import { Ionicons } from "@expo/vector-icons";
|
||||
import { useQueryClient } from "@tanstack/react-query";
|
||||
import { useRouter } from "expo-router";
|
||||
import React, { useMemo } from "react";
|
||||
import { Platform, Switch, TouchableOpacity } from "react-native";
|
||||
|
||||
const DropdownMenu = !Platform.isTV ? require("zeego/dropdown-menu") : null;
|
||||
import DisabledSetting from "@/components/settings/DisabledSetting";
|
||||
|
||||
import { useTranslation } from "react-i18next";
|
||||
import DisabledSetting from "@/components/settings/DisabledSetting";
|
||||
import { Text } from "../common/Text";
|
||||
import { ListGroup } from "../list/ListGroup";
|
||||
import { ListItem } from "../list/ListItem";
|
||||
|
||||
@@ -1,5 +0,0 @@
|
||||
import React from "react";
|
||||
|
||||
export default function DownloadSettings({ ...props }) {
|
||||
return <></>;
|
||||
}
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user