mirror of
https://github.com/streamyfin/streamyfin.git
synced 2026-06-12 08:50:25 +01:00
fix: loading indicator style
This commit is contained in:
@@ -2,6 +2,7 @@ import { Button } from "@/components/Button";
|
|||||||
import { Text } from "@/components/common/Text";
|
import { Text } from "@/components/common/Text";
|
||||||
import { LargeMovieCarousel } from "@/components/home/LargeMovieCarousel";
|
import { LargeMovieCarousel } from "@/components/home/LargeMovieCarousel";
|
||||||
import { ScrollingCollectionList } from "@/components/home/ScrollingCollectionList";
|
import { ScrollingCollectionList } from "@/components/home/ScrollingCollectionList";
|
||||||
|
import { Loader } from "@/components/Loader";
|
||||||
import { MediaListSection } from "@/components/medialists/MediaListSection";
|
import { MediaListSection } from "@/components/medialists/MediaListSection";
|
||||||
import { apiAtom, userAtom } from "@/providers/JellyfinProvider";
|
import { apiAtom, userAtom } from "@/providers/JellyfinProvider";
|
||||||
import { useSettings } from "@/utils/atoms/settings";
|
import { useSettings } from "@/utils/atoms/settings";
|
||||||
@@ -19,12 +20,7 @@ import { useQuery, useQueryClient } from "@tanstack/react-query";
|
|||||||
import { useRouter } from "expo-router";
|
import { useRouter } from "expo-router";
|
||||||
import { useAtom } from "jotai";
|
import { useAtom } from "jotai";
|
||||||
import { useCallback, useEffect, useMemo, useState } from "react";
|
import { useCallback, useEffect, useMemo, useState } from "react";
|
||||||
import {
|
import { RefreshControl, ScrollView, View } from "react-native";
|
||||||
ActivityIndicator,
|
|
||||||
RefreshControl,
|
|
||||||
ScrollView,
|
|
||||||
View,
|
|
||||||
} from "react-native";
|
|
||||||
|
|
||||||
export default function index() {
|
export default function index() {
|
||||||
const router = useRouter();
|
const router = useRouter();
|
||||||
@@ -250,10 +246,10 @@ export default function index() {
|
|||||||
</View>
|
</View>
|
||||||
);
|
);
|
||||||
|
|
||||||
if (isLoading)
|
if (true)
|
||||||
return (
|
return (
|
||||||
<View className="justify-center items-center h-full">
|
<View className="justify-center items-center h-full">
|
||||||
<ActivityIndicator />
|
<Loader />
|
||||||
</View>
|
</View>
|
||||||
);
|
);
|
||||||
|
|
||||||
|
|||||||
@@ -3,6 +3,7 @@ import { TouchableItemRouter } from "@/components/common/TouchableItemRouter";
|
|||||||
import { FilterButton } from "@/components/filters/FilterButton";
|
import { FilterButton } from "@/components/filters/FilterButton";
|
||||||
import { ResetFiltersButton } from "@/components/filters/ResetFiltersButton";
|
import { ResetFiltersButton } from "@/components/filters/ResetFiltersButton";
|
||||||
import { ItemCardText } from "@/components/ItemCardText";
|
import { ItemCardText } from "@/components/ItemCardText";
|
||||||
|
import { Loader } from "@/components/Loader";
|
||||||
import MoviePoster from "@/components/posters/MoviePoster";
|
import MoviePoster from "@/components/posters/MoviePoster";
|
||||||
import { apiAtom, userAtom } from "@/providers/JellyfinProvider";
|
import { apiAtom, userAtom } from "@/providers/JellyfinProvider";
|
||||||
import {
|
import {
|
||||||
@@ -24,7 +25,7 @@ import { useInfiniteQuery, useQuery } from "@tanstack/react-query";
|
|||||||
import { useLocalSearchParams } from "expo-router";
|
import { useLocalSearchParams } from "expo-router";
|
||||||
import { useAtom } from "jotai";
|
import { useAtom } from "jotai";
|
||||||
import React, { useCallback, useMemo } from "react";
|
import React, { useCallback, useMemo } from "react";
|
||||||
import { ActivityIndicator, ScrollView, View } from "react-native";
|
import { ScrollView, View } from "react-native";
|
||||||
|
|
||||||
const page: React.FC = () => {
|
const page: React.FC = () => {
|
||||||
const searchParams = useLocalSearchParams();
|
const searchParams = useLocalSearchParams();
|
||||||
@@ -284,12 +285,10 @@ const page: React.FC = () => {
|
|||||||
</View>
|
</View>
|
||||||
</ScrollView>
|
</ScrollView>
|
||||||
{!type && isFetching && (
|
{!type && isFetching && (
|
||||||
<ActivityIndicator
|
<Loader
|
||||||
style={{
|
style={{
|
||||||
marginTop: 300,
|
marginTop: 300,
|
||||||
}}
|
}}
|
||||||
size={"small"}
|
|
||||||
color={"white"}
|
|
||||||
/>
|
/>
|
||||||
)}
|
)}
|
||||||
</View>
|
</View>
|
||||||
|
|||||||
@@ -1,35 +1,21 @@
|
|||||||
import { Text } from "@/components/common/Text";
|
import { Text } from "@/components/common/Text";
|
||||||
|
import { Loader } from "@/components/Loader";
|
||||||
import { apiAtom, userAtom } from "@/providers/JellyfinProvider";
|
import { apiAtom, userAtom } from "@/providers/JellyfinProvider";
|
||||||
import { getItemsApi, getUserViewsApi } from "@jellyfin/sdk/lib/utils/api";
|
import { getPrimaryImageUrl } from "@/utils/jellyfin/image/getPrimaryImageUrl";
|
||||||
import { useQuery, useQueryClient } from "@tanstack/react-query";
|
import { BaseItemDto } from "@jellyfin/sdk/lib/generated-client/models";
|
||||||
|
import { getUserViewsApi } from "@jellyfin/sdk/lib/utils/api";
|
||||||
|
import { FlashList } from "@shopify/flash-list";
|
||||||
|
import { useQuery } from "@tanstack/react-query";
|
||||||
|
import { Image } from "expo-image";
|
||||||
import { useRouter } from "expo-router";
|
import { useRouter } from "expo-router";
|
||||||
import { useAtom } from "jotai";
|
import { useAtom } from "jotai";
|
||||||
import { useCallback, useMemo, useState } from "react";
|
import { useMemo } from "react";
|
||||||
import {
|
import { TouchableOpacity, View } from "react-native";
|
||||||
ActivityIndicator,
|
|
||||||
RefreshControl,
|
|
||||||
ScrollView,
|
|
||||||
TouchableOpacity,
|
|
||||||
View,
|
|
||||||
} from "react-native";
|
|
||||||
import { Ionicons } from "@expo/vector-icons";
|
|
||||||
import { useSettings } from "@/utils/atoms/settings";
|
|
||||||
import { FlashList } from "@shopify/flash-list";
|
|
||||||
import { BaseItemDto } from "@jellyfin/sdk/lib/generated-client/models";
|
|
||||||
import { getPrimaryImageUrl } from "@/utils/jellyfin/image/getPrimaryImageUrl";
|
|
||||||
import { Image } from "expo-image";
|
|
||||||
import { useSafeAreaInsets } from "react-native-safe-area-context";
|
|
||||||
|
|
||||||
export default function index() {
|
export default function index() {
|
||||||
const router = useRouter();
|
|
||||||
const queryClient = useQueryClient();
|
|
||||||
|
|
||||||
const [api] = useAtom(apiAtom);
|
const [api] = useAtom(apiAtom);
|
||||||
const [user] = useAtom(userAtom);
|
const [user] = useAtom(userAtom);
|
||||||
|
|
||||||
const [loading, setLoading] = useState(false);
|
|
||||||
const [settings, _] = useSettings();
|
|
||||||
|
|
||||||
const { data, isLoading: isLoading } = useQuery({
|
const { data, isLoading: isLoading } = useQuery({
|
||||||
queryKey: ["user-views", user?.Id],
|
queryKey: ["user-views", user?.Id],
|
||||||
queryFn: async () => {
|
queryFn: async () => {
|
||||||
@@ -50,7 +36,7 @@ export default function index() {
|
|||||||
if (isLoading)
|
if (isLoading)
|
||||||
return (
|
return (
|
||||||
<View className="justify-center items-center h-full">
|
<View className="justify-center items-center h-full">
|
||||||
<ActivityIndicator />
|
<Loader />
|
||||||
</View>
|
</View>
|
||||||
);
|
);
|
||||||
|
|
||||||
|
|||||||
@@ -4,28 +4,19 @@ import { Text } from "@/components/common/Text";
|
|||||||
import { TouchableItemRouter } from "@/components/common/TouchableItemRouter";
|
import { TouchableItemRouter } from "@/components/common/TouchableItemRouter";
|
||||||
import ContinueWatchingPoster from "@/components/ContinueWatchingPoster";
|
import ContinueWatchingPoster from "@/components/ContinueWatchingPoster";
|
||||||
import { ItemCardText } from "@/components/ItemCardText";
|
import { ItemCardText } from "@/components/ItemCardText";
|
||||||
|
import { Loader } from "@/components/Loader";
|
||||||
import AlbumCover from "@/components/posters/AlbumCover";
|
import AlbumCover from "@/components/posters/AlbumCover";
|
||||||
import MoviePoster from "@/components/posters/MoviePoster";
|
import MoviePoster from "@/components/posters/MoviePoster";
|
||||||
import Poster from "@/components/posters/Poster";
|
|
||||||
import SeriesPoster from "@/components/posters/SeriesPoster";
|
import SeriesPoster from "@/components/posters/SeriesPoster";
|
||||||
import { apiAtom, userAtom } from "@/providers/JellyfinProvider";
|
import { apiAtom, userAtom } from "@/providers/JellyfinProvider";
|
||||||
import { getPrimaryImageUrl } from "@/utils/jellyfin/image/getPrimaryImageUrl";
|
|
||||||
import { getUserItemData } from "@/utils/jellyfin/user-library/getUserItemData";
|
import { getUserItemData } from "@/utils/jellyfin/user-library/getUserItemData";
|
||||||
import { Ionicons } from "@expo/vector-icons";
|
|
||||||
import { BaseItemDto } from "@jellyfin/sdk/lib/generated-client/models";
|
import { BaseItemDto } from "@jellyfin/sdk/lib/generated-client/models";
|
||||||
import { getSearchApi } from "@jellyfin/sdk/lib/utils/api";
|
import { getSearchApi } from "@jellyfin/sdk/lib/utils/api";
|
||||||
import { useQuery } from "@tanstack/react-query";
|
import { useQuery } from "@tanstack/react-query";
|
||||||
import { router, Stack, useNavigation } from "expo-router";
|
import { router, useNavigation } from "expo-router";
|
||||||
import { useAtom } from "jotai";
|
import { useAtom } from "jotai";
|
||||||
import React, { useLayoutEffect, useMemo, useState } from "react";
|
import React, { useLayoutEffect, useMemo, useState } from "react";
|
||||||
import {
|
import { Platform, ScrollView, TouchableOpacity, View } from "react-native";
|
||||||
ActivityIndicator,
|
|
||||||
Platform,
|
|
||||||
ScrollView,
|
|
||||||
TouchableOpacity,
|
|
||||||
View,
|
|
||||||
} from "react-native";
|
|
||||||
import _ from "lodash";
|
|
||||||
import { useDebounce } from "use-debounce";
|
import { useDebounce } from "use-debounce";
|
||||||
|
|
||||||
const exampleSearches = [
|
const exampleSearches = [
|
||||||
@@ -308,7 +299,7 @@ export default function search() {
|
|||||||
/>
|
/>
|
||||||
{loading ? (
|
{loading ? (
|
||||||
<View className="mt-4 flex justify-center items-center">
|
<View className="mt-4 flex justify-center items-center">
|
||||||
<ActivityIndicator size="small" color="white" />
|
<Loader />
|
||||||
</View>
|
</View>
|
||||||
) : noResults && debouncedSearch.length > 0 ? (
|
) : noResults && debouncedSearch.length > 0 ? (
|
||||||
<View>
|
<View>
|
||||||
|
|||||||
@@ -1,4 +1,5 @@
|
|||||||
import { Text } from "@/components/common/Text";
|
import { Text } from "@/components/common/Text";
|
||||||
|
import { Loader } from "@/components/Loader";
|
||||||
import ArtistPoster from "@/components/posters/ArtistPoster";
|
import ArtistPoster from "@/components/posters/ArtistPoster";
|
||||||
import MoviePoster from "@/components/posters/MoviePoster";
|
import MoviePoster from "@/components/posters/MoviePoster";
|
||||||
import { apiAtom, userAtom } from "@/providers/JellyfinProvider";
|
import { apiAtom, userAtom } from "@/providers/JellyfinProvider";
|
||||||
@@ -13,12 +14,7 @@ import { useQuery } from "@tanstack/react-query";
|
|||||||
import { router, useLocalSearchParams } from "expo-router";
|
import { router, useLocalSearchParams } from "expo-router";
|
||||||
import { useAtom } from "jotai";
|
import { useAtom } from "jotai";
|
||||||
import { useMemo, useState } from "react";
|
import { useMemo, useState } from "react";
|
||||||
import {
|
import { ScrollView, TouchableOpacity, View } from "react-native";
|
||||||
ActivityIndicator,
|
|
||||||
ScrollView,
|
|
||||||
TouchableOpacity,
|
|
||||||
View,
|
|
||||||
} from "react-native";
|
|
||||||
|
|
||||||
const page: React.FC = () => {
|
const page: React.FC = () => {
|
||||||
const searchParams = useLocalSearchParams();
|
const searchParams = useLocalSearchParams();
|
||||||
@@ -154,7 +150,7 @@ const page: React.FC = () => {
|
|||||||
</View>
|
</View>
|
||||||
{isLoading ? (
|
{isLoading ? (
|
||||||
<View className="my-12">
|
<View className="my-12">
|
||||||
<ActivityIndicator color={"white"} />
|
<Loader />
|
||||||
</View>
|
</View>
|
||||||
) : (
|
) : (
|
||||||
<View className="flex flex-row flex-wrap">
|
<View className="flex flex-row flex-wrap">
|
||||||
|
|||||||
@@ -1,23 +1,18 @@
|
|||||||
import { Text } from "@/components/common/Text";
|
import { Text } from "@/components/common/Text";
|
||||||
import { MovieCard } from "@/components/downloads/MovieCard";
|
import { MovieCard } from "@/components/downloads/MovieCard";
|
||||||
import { SeriesCard } from "@/components/downloads/SeriesCard";
|
import { SeriesCard } from "@/components/downloads/SeriesCard";
|
||||||
|
import { Loader } from "@/components/Loader";
|
||||||
|
import { runningProcesses } from "@/utils/atoms/downloads";
|
||||||
|
import { queueAtom } from "@/utils/atoms/queue";
|
||||||
|
import { Ionicons } from "@expo/vector-icons";
|
||||||
import { BaseItemDto } from "@jellyfin/sdk/lib/generated-client/models";
|
import { BaseItemDto } from "@jellyfin/sdk/lib/generated-client/models";
|
||||||
import AsyncStorage from "@react-native-async-storage/async-storage";
|
import AsyncStorage from "@react-native-async-storage/async-storage";
|
||||||
import { useQuery } from "@tanstack/react-query";
|
import { useQuery } from "@tanstack/react-query";
|
||||||
import { useEffect, useMemo } from "react";
|
|
||||||
import {
|
|
||||||
ActivityIndicator,
|
|
||||||
ScrollView,
|
|
||||||
TouchableOpacity,
|
|
||||||
View,
|
|
||||||
} from "react-native";
|
|
||||||
import { useAtom } from "jotai";
|
|
||||||
import { runningProcesses } from "@/utils/atoms/downloads";
|
|
||||||
import { router } from "expo-router";
|
import { router } from "expo-router";
|
||||||
import { Ionicons } from "@expo/vector-icons";
|
|
||||||
import { FFmpegKit } from "ffmpeg-kit-react-native";
|
import { FFmpegKit } from "ffmpeg-kit-react-native";
|
||||||
import * as FileSystem from "expo-file-system";
|
import { useAtom } from "jotai";
|
||||||
import { queueAtom } from "@/utils/atoms/queue";
|
import { useMemo } from "react";
|
||||||
|
import { ScrollView, TouchableOpacity, View } from "react-native";
|
||||||
|
|
||||||
const downloads: React.FC = () => {
|
const downloads: React.FC = () => {
|
||||||
const [process, setProcess] = useAtom(runningProcesses);
|
const [process, setProcess] = useAtom(runningProcesses);
|
||||||
@@ -27,14 +22,14 @@ const downloads: React.FC = () => {
|
|||||||
queryKey: ["downloaded_files", process?.item.Id],
|
queryKey: ["downloaded_files", process?.item.Id],
|
||||||
queryFn: async () =>
|
queryFn: async () =>
|
||||||
JSON.parse(
|
JSON.parse(
|
||||||
(await AsyncStorage.getItem("downloaded_files")) || "[]",
|
(await AsyncStorage.getItem("downloaded_files")) || "[]"
|
||||||
) as BaseItemDto[],
|
) as BaseItemDto[],
|
||||||
staleTime: 0,
|
staleTime: 0,
|
||||||
});
|
});
|
||||||
|
|
||||||
const movies = useMemo(
|
const movies = useMemo(
|
||||||
() => downloadedFiles?.filter((f) => f.Type === "Movie") || [],
|
() => downloadedFiles?.filter((f) => f.Type === "Movie") || [],
|
||||||
[downloadedFiles],
|
[downloadedFiles]
|
||||||
);
|
);
|
||||||
|
|
||||||
const groupedBySeries = useMemo(() => {
|
const groupedBySeries = useMemo(() => {
|
||||||
@@ -61,7 +56,7 @@ const downloads: React.FC = () => {
|
|||||||
if (isLoading) {
|
if (isLoading) {
|
||||||
return (
|
return (
|
||||||
<View className="h-full flex flex-col items-center justify-center -mt-6">
|
<View className="h-full flex flex-col items-center justify-center -mt-6">
|
||||||
<ActivityIndicator size="small" color="white" />
|
<Loader />
|
||||||
</View>
|
</View>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -6,6 +6,7 @@ import {
|
|||||||
playingAtom,
|
playingAtom,
|
||||||
} from "@/components/CurrentlyPlayingBar";
|
} from "@/components/CurrentlyPlayingBar";
|
||||||
import { DownloadItem } from "@/components/DownloadItem";
|
import { DownloadItem } from "@/components/DownloadItem";
|
||||||
|
import { Loader } from "@/components/Loader";
|
||||||
import { OverviewText } from "@/components/OverviewText";
|
import { OverviewText } from "@/components/OverviewText";
|
||||||
import { PlayButton } from "@/components/PlayButton";
|
import { PlayButton } from "@/components/PlayButton";
|
||||||
import { PlayedStatus } from "@/components/PlayedStatus";
|
import { PlayedStatus } from "@/components/PlayedStatus";
|
||||||
@@ -34,7 +35,7 @@ import { Image } from "expo-image";
|
|||||||
import { useLocalSearchParams } from "expo-router";
|
import { useLocalSearchParams } from "expo-router";
|
||||||
import { useAtom } from "jotai";
|
import { useAtom } from "jotai";
|
||||||
import { useCallback, useMemo, useState } from "react";
|
import { useCallback, useMemo, useState } from "react";
|
||||||
import { ActivityIndicator, ScrollView, View } from "react-native";
|
import { View } from "react-native";
|
||||||
import CastContext, {
|
import CastContext, {
|
||||||
PlayServicesState,
|
PlayServicesState,
|
||||||
useCastDevice,
|
useCastDevice,
|
||||||
@@ -194,7 +195,7 @@ const page: React.FC = () => {
|
|||||||
if (l1)
|
if (l1)
|
||||||
return (
|
return (
|
||||||
<View className="justify-center items-center h-full">
|
<View className="justify-center items-center h-full">
|
||||||
<ActivityIndicator />
|
<Loader />
|
||||||
</View>
|
</View>
|
||||||
);
|
);
|
||||||
|
|
||||||
|
|||||||
@@ -1,37 +1,38 @@
|
|||||||
|
import { AudioTrackSelector } from "@/components/AudioTrackSelector";
|
||||||
|
import { Bitrate, BitrateSelector } from "@/components/BitrateSelector";
|
||||||
|
import { Chromecast } from "@/components/Chromecast";
|
||||||
import { Text } from "@/components/common/Text";
|
import { Text } from "@/components/common/Text";
|
||||||
|
import {
|
||||||
|
currentlyPlayingItemAtom,
|
||||||
|
playingAtom,
|
||||||
|
} from "@/components/CurrentlyPlayingBar";
|
||||||
import { DownloadItem } from "@/components/DownloadItem";
|
import { DownloadItem } from "@/components/DownloadItem";
|
||||||
|
import { Loader } from "@/components/Loader";
|
||||||
|
import { MoviesTitleHeader } from "@/components/movies/MoviesTitleHeader";
|
||||||
|
import { ParallaxScrollView } from "@/components/ParallaxPage";
|
||||||
|
import { PlayButton } from "@/components/PlayButton";
|
||||||
|
import { NextEpisodeButton } from "@/components/series/NextEpisodeButton";
|
||||||
import { SimilarItems } from "@/components/SimilarItems";
|
import { SimilarItems } from "@/components/SimilarItems";
|
||||||
|
import { SubtitleTrackSelector } from "@/components/SubtitleTrackSelector";
|
||||||
import { apiAtom, userAtom } from "@/providers/JellyfinProvider";
|
import { apiAtom, userAtom } from "@/providers/JellyfinProvider";
|
||||||
|
import { getBackdropUrl } from "@/utils/jellyfin/image/getBackdropUrl";
|
||||||
|
import { getLogoImageUrlById } from "@/utils/jellyfin/image/getLogoImageUrlById";
|
||||||
|
import { getStreamUrl } from "@/utils/jellyfin/media/getStreamUrl";
|
||||||
|
import { getUserItemData } from "@/utils/jellyfin/user-library/getUserItemData";
|
||||||
|
import { chromecastProfile } from "@/utils/profiles/chromecast";
|
||||||
|
import ios from "@/utils/profiles/ios";
|
||||||
|
import { getMediaInfoApi } from "@jellyfin/sdk/lib/utils/api";
|
||||||
import { useQuery } from "@tanstack/react-query";
|
import { useQuery } from "@tanstack/react-query";
|
||||||
import { Image } from "expo-image";
|
import { Image } from "expo-image";
|
||||||
import { useLocalSearchParams, useNavigation } from "expo-router";
|
import { useLocalSearchParams, useNavigation } from "expo-router";
|
||||||
import { useAtom } from "jotai";
|
import { useAtom } from "jotai";
|
||||||
import { useCallback, useEffect, useMemo, useState } from "react";
|
import { useCallback, useEffect, useMemo, useState } from "react";
|
||||||
import { ActivityIndicator, ScrollView, View } from "react-native";
|
import { ScrollView, View } from "react-native";
|
||||||
import { getUserItemData } from "@/utils/jellyfin/user-library/getUserItemData";
|
|
||||||
import { getBackdropUrl } from "@/utils/jellyfin/image/getBackdropUrl";
|
|
||||||
import { getLogoImageUrlById } from "@/utils/jellyfin/image/getLogoImageUrlById";
|
|
||||||
import { PlayButton } from "@/components/PlayButton";
|
|
||||||
import { Bitrate, BitrateSelector } from "@/components/BitrateSelector";
|
|
||||||
import { getMediaInfoApi } from "@jellyfin/sdk/lib/utils/api";
|
|
||||||
import { getStreamUrl } from "@/utils/jellyfin/media/getStreamUrl";
|
|
||||||
import CastContext, {
|
import CastContext, {
|
||||||
PlayServicesState,
|
PlayServicesState,
|
||||||
useCastDevice,
|
useCastDevice,
|
||||||
useRemoteMediaClient,
|
useRemoteMediaClient,
|
||||||
} from "react-native-google-cast";
|
} from "react-native-google-cast";
|
||||||
import { chromecastProfile } from "@/utils/profiles/chromecast";
|
|
||||||
import {
|
|
||||||
currentlyPlayingItemAtom,
|
|
||||||
playingAtom,
|
|
||||||
} from "@/components/CurrentlyPlayingBar";
|
|
||||||
import { AudioTrackSelector } from "@/components/AudioTrackSelector";
|
|
||||||
import { SubtitleTrackSelector } from "@/components/SubtitleTrackSelector";
|
|
||||||
import { NextEpisodeButton } from "@/components/series/NextEpisodeButton";
|
|
||||||
import { MoviesTitleHeader } from "@/components/movies/MoviesTitleHeader";
|
|
||||||
import { ParallaxScrollView } from "@/components/ParallaxPage";
|
|
||||||
import { Chromecast } from "@/components/Chromecast";
|
|
||||||
import ios from "@/utils/profiles/ios";
|
|
||||||
|
|
||||||
const page: React.FC = () => {
|
const page: React.FC = () => {
|
||||||
const local = useLocalSearchParams();
|
const local = useLocalSearchParams();
|
||||||
@@ -84,12 +85,12 @@ const page: React.FC = () => {
|
|||||||
quality: 90,
|
quality: 90,
|
||||||
width: 1000,
|
width: 1000,
|
||||||
}),
|
}),
|
||||||
[item],
|
[item]
|
||||||
);
|
);
|
||||||
|
|
||||||
const logoUrl = useMemo(
|
const logoUrl = useMemo(
|
||||||
() => (item?.Type === "Movie" ? getLogoImageUrlById({ api, item }) : null),
|
() => (item?.Type === "Movie" ? getLogoImageUrlById({ api, item }) : null),
|
||||||
[item],
|
[item]
|
||||||
);
|
);
|
||||||
|
|
||||||
const { data: sessionData } = useQuery({
|
const { data: sessionData } = useQuery({
|
||||||
@@ -173,13 +174,13 @@ const page: React.FC = () => {
|
|||||||
setPlaying(true);
|
setPlaying(true);
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
[playbackUrl, item],
|
[playbackUrl, item]
|
||||||
);
|
);
|
||||||
|
|
||||||
if (l1)
|
if (l1)
|
||||||
return (
|
return (
|
||||||
<View className="justify-center items-center h-full">
|
<View className="justify-center items-center h-full">
|
||||||
<ActivityIndicator />
|
<Loader />
|
||||||
</View>
|
</View>
|
||||||
);
|
);
|
||||||
|
|
||||||
|
|||||||
@@ -1,6 +1,7 @@
|
|||||||
import React, { PropsWithChildren, ReactNode, useMemo } from "react";
|
|
||||||
import { TouchableOpacity, Text, ActivityIndicator, View } from "react-native";
|
|
||||||
import * as Haptics from "expo-haptics";
|
import * as Haptics from "expo-haptics";
|
||||||
|
import React, { PropsWithChildren, ReactNode, useMemo } from "react";
|
||||||
|
import { Text, TouchableOpacity, View } from "react-native";
|
||||||
|
import { Loader } from "./Loader";
|
||||||
|
|
||||||
interface ButtonProps extends React.ComponentProps<typeof TouchableOpacity> {
|
interface ButtonProps extends React.ComponentProps<typeof TouchableOpacity> {
|
||||||
onPress?: () => void;
|
onPress?: () => void;
|
||||||
@@ -57,7 +58,7 @@ export const Button: React.FC<PropsWithChildren<ButtonProps>> = ({
|
|||||||
{...props}
|
{...props}
|
||||||
>
|
>
|
||||||
{loading ? (
|
{loading ? (
|
||||||
<ActivityIndicator color={"white"} size={24} />
|
<Loader />
|
||||||
) : (
|
) : (
|
||||||
<View
|
<View
|
||||||
className={`
|
className={`
|
||||||
|
|||||||
@@ -1,5 +1,4 @@
|
|||||||
import { apiAtom, userAtom } from "@/providers/JellyfinProvider";
|
import { apiAtom, userAtom } from "@/providers/JellyfinProvider";
|
||||||
import { useSettings } from "@/utils/atoms/settings";
|
|
||||||
import { getBackdropUrl } from "@/utils/jellyfin/image/getBackdropUrl";
|
import { getBackdropUrl } from "@/utils/jellyfin/image/getBackdropUrl";
|
||||||
import { getAuthHeaders } from "@/utils/jellyfin/jellyfin";
|
import { getAuthHeaders } from "@/utils/jellyfin/jellyfin";
|
||||||
import { reportPlaybackProgress } from "@/utils/jellyfin/playstate/reportPlaybackProgress";
|
import { reportPlaybackProgress } from "@/utils/jellyfin/playstate/reportPlaybackProgress";
|
||||||
@@ -14,13 +13,7 @@ import { BlurView } from "expo-blur";
|
|||||||
import { useRouter, useSegments } from "expo-router";
|
import { useRouter, useSegments } from "expo-router";
|
||||||
import { atom, useAtom } from "jotai";
|
import { atom, useAtom } from "jotai";
|
||||||
import { useCallback, useEffect, useMemo, useRef, useState } from "react";
|
import { useCallback, useEffect, useMemo, useRef, useState } from "react";
|
||||||
import {
|
import { Alert, Platform, TouchableOpacity, View } from "react-native";
|
||||||
ActivityIndicator,
|
|
||||||
Alert,
|
|
||||||
Platform,
|
|
||||||
TouchableOpacity,
|
|
||||||
View,
|
|
||||||
} from "react-native";
|
|
||||||
import Animated, {
|
import Animated, {
|
||||||
useAnimatedStyle,
|
useAnimatedStyle,
|
||||||
useSharedValue,
|
useSharedValue,
|
||||||
@@ -28,6 +21,7 @@ import Animated, {
|
|||||||
} from "react-native-reanimated";
|
} from "react-native-reanimated";
|
||||||
import Video, { OnProgressData, VideoRef } from "react-native-video";
|
import Video, { OnProgressData, VideoRef } from "react-native-video";
|
||||||
import { Text } from "./common/Text";
|
import { Text } from "./common/Text";
|
||||||
|
import { Loader } from "./Loader";
|
||||||
|
|
||||||
export const currentlyPlayingItemAtom = atom<{
|
export const currentlyPlayingItemAtom = atom<{
|
||||||
item: BaseItemDto;
|
item: BaseItemDto;
|
||||||
@@ -292,7 +286,7 @@ export const CurrentlyPlayingBar: React.FC = () => {
|
|||||||
renderLoader={
|
renderLoader={
|
||||||
item?.Type !== "Audio" && (
|
item?.Type !== "Audio" && (
|
||||||
<View className="flex flex-col items-center justify-center h-full">
|
<View className="flex flex-col items-center justify-center h-full">
|
||||||
<ActivityIndicator size={"small"} color={"white"} />
|
<Loader />
|
||||||
</View>
|
</View>
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -9,7 +9,8 @@ import AsyncStorage from "@react-native-async-storage/async-storage";
|
|||||||
import { useQuery } from "@tanstack/react-query";
|
import { useQuery } from "@tanstack/react-query";
|
||||||
import { router } from "expo-router";
|
import { router } from "expo-router";
|
||||||
import { useAtom } from "jotai";
|
import { useAtom } from "jotai";
|
||||||
import { ActivityIndicator, TouchableOpacity, View } from "react-native";
|
import { TouchableOpacity, View } from "react-native";
|
||||||
|
import { Loader } from "./Loader";
|
||||||
import ProgressCircle from "./ProgressCircle";
|
import ProgressCircle from "./ProgressCircle";
|
||||||
|
|
||||||
type DownloadProps = {
|
type DownloadProps = {
|
||||||
@@ -39,7 +40,7 @@ export const DownloadItem: React.FC<DownloadProps> = ({
|
|||||||
if (!item.Id) return false;
|
if (!item.Id) return false;
|
||||||
|
|
||||||
const data: BaseItemDto[] = JSON.parse(
|
const data: BaseItemDto[] = JSON.parse(
|
||||||
(await AsyncStorage.getItem("downloaded_files")) || "[]",
|
(await AsyncStorage.getItem("downloaded_files")) || "[]"
|
||||||
);
|
);
|
||||||
|
|
||||||
return data.some((d) => d.Id === item.Id);
|
return data.some((d) => d.Id === item.Id);
|
||||||
@@ -50,7 +51,7 @@ export const DownloadItem: React.FC<DownloadProps> = ({
|
|||||||
if (isLoading || isLoadingDownloaded) {
|
if (isLoading || isLoadingDownloaded) {
|
||||||
return (
|
return (
|
||||||
<View className="rounded h-10 aspect-square flex items-center justify-center">
|
<View className="rounded h-10 aspect-square flex items-center justify-center">
|
||||||
<ActivityIndicator size={"small"} color={"white"} />
|
<Loader />
|
||||||
</View>
|
</View>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
@@ -72,7 +73,7 @@ export const DownloadItem: React.FC<DownloadProps> = ({
|
|||||||
>
|
>
|
||||||
<View className="rounded h-10 aspect-square flex items-center justify-center">
|
<View className="rounded h-10 aspect-square flex items-center justify-center">
|
||||||
{process.progress === 0 ? (
|
{process.progress === 0 ? (
|
||||||
<ActivityIndicator size={"small"} color={"white"} />
|
<Loader />
|
||||||
) : (
|
) : (
|
||||||
<View className="-rotate-45">
|
<View className="-rotate-45">
|
||||||
<ProgressCircle
|
<ProgressCircle
|
||||||
|
|||||||
18
components/Loader.tsx
Normal file
18
components/Loader.tsx
Normal file
@@ -0,0 +1,18 @@
|
|||||||
|
import {
|
||||||
|
ActivityIndicator,
|
||||||
|
ActivityIndicatorProps,
|
||||||
|
Platform,
|
||||||
|
View,
|
||||||
|
} from "react-native";
|
||||||
|
|
||||||
|
interface Props extends ActivityIndicatorProps {}
|
||||||
|
|
||||||
|
export const Loader: React.FC<Props> = ({ ...props }) => {
|
||||||
|
return (
|
||||||
|
<ActivityIndicator
|
||||||
|
size={"small"}
|
||||||
|
color={Platform.OS === "ios" ? "white" : "#9333ea"}
|
||||||
|
{...props}
|
||||||
|
/>
|
||||||
|
);
|
||||||
|
};
|
||||||
@@ -1,9 +0,0 @@
|
|||||||
import { ActivityIndicator, View } from "react-native";
|
|
||||||
|
|
||||||
export const Loading: React.FC = () => {
|
|
||||||
return (
|
|
||||||
<View>
|
|
||||||
<ActivityIndicator />
|
|
||||||
</View>
|
|
||||||
);
|
|
||||||
};
|
|
||||||
@@ -6,14 +6,10 @@ import { useQuery } from "@tanstack/react-query";
|
|||||||
import { router } from "expo-router";
|
import { router } from "expo-router";
|
||||||
import { useAtom } from "jotai";
|
import { useAtom } from "jotai";
|
||||||
import { useMemo } from "react";
|
import { useMemo } from "react";
|
||||||
import {
|
import { ScrollView, TouchableOpacity, View } from "react-native";
|
||||||
ActivityIndicator,
|
|
||||||
ScrollView,
|
|
||||||
TouchableOpacity,
|
|
||||||
View,
|
|
||||||
} from "react-native";
|
|
||||||
import { ItemCardText } from "./ItemCardText";
|
|
||||||
import { Text } from "./common/Text";
|
import { Text } from "./common/Text";
|
||||||
|
import { ItemCardText } from "./ItemCardText";
|
||||||
|
import { Loader } from "./Loader";
|
||||||
|
|
||||||
type SimilarItemsProps = {
|
type SimilarItemsProps = {
|
||||||
itemId: string;
|
itemId: string;
|
||||||
@@ -49,7 +45,7 @@ export const SimilarItems: React.FC<SimilarItemsProps> = ({ itemId }) => {
|
|||||||
<Text className="px-4 text-2xl font-bold mb-2">Similar items</Text>
|
<Text className="px-4 text-2xl font-bold mb-2">Similar items</Text>
|
||||||
{isLoading ? (
|
{isLoading ? (
|
||||||
<View className="my-12">
|
<View className="my-12">
|
||||||
<ActivityIndicator />
|
<Loader />
|
||||||
</View>
|
</View>
|
||||||
) : (
|
) : (
|
||||||
<ScrollView horizontal>
|
<ScrollView horizontal>
|
||||||
|
|||||||
@@ -1,16 +1,11 @@
|
|||||||
import React, { useEffect } from "react";
|
import React, { useEffect } from "react";
|
||||||
import {
|
import { ScrollView, ScrollViewProps, View, ViewStyle } from "react-native";
|
||||||
ScrollView,
|
|
||||||
View,
|
|
||||||
ViewStyle,
|
|
||||||
ActivityIndicator,
|
|
||||||
ScrollViewProps,
|
|
||||||
} from "react-native";
|
|
||||||
import Animated, {
|
import Animated, {
|
||||||
useAnimatedStyle,
|
useAnimatedStyle,
|
||||||
useSharedValue,
|
useSharedValue,
|
||||||
withTiming,
|
withTiming,
|
||||||
} from "react-native-reanimated";
|
} from "react-native-reanimated";
|
||||||
|
import { Loader } from "../Loader";
|
||||||
import { Text } from "./Text";
|
import { Text } from "./Text";
|
||||||
|
|
||||||
interface HorizontalScrollProps<T> extends ScrollViewProps {
|
interface HorizontalScrollProps<T> extends ScrollViewProps {
|
||||||
@@ -58,7 +53,7 @@ export function HorizontalScroll<T>({
|
|||||||
loadingContainerStyle,
|
loadingContainerStyle,
|
||||||
]}
|
]}
|
||||||
>
|
>
|
||||||
<ActivityIndicator size="small" />
|
<Loader />
|
||||||
</View>
|
</View>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,22 +1,22 @@
|
|||||||
import { ActivityIndicator, View, ViewProps } from "react-native";
|
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 { BaseItemDto } from "@jellyfin/sdk/lib/generated-client/models";
|
import { BaseItemDto } from "@jellyfin/sdk/lib/generated-client/models";
|
||||||
import { getItemsApi } from "@jellyfin/sdk/lib/utils/api";
|
import { getItemsApi } from "@jellyfin/sdk/lib/utils/api";
|
||||||
import { useQuery, useQueryClient } from "@tanstack/react-query";
|
import { useQuery, useQueryClient } from "@tanstack/react-query";
|
||||||
import { apiAtom, userAtom } from "@/providers/JellyfinProvider";
|
import { Image } from "expo-image";
|
||||||
import { useRouter } from "expo-router";
|
import { useRouter } from "expo-router";
|
||||||
import { useAtom } from "jotai";
|
import { useAtom } from "jotai";
|
||||||
import { useSettings } from "@/utils/atoms/settings";
|
import React, { useMemo } from "react";
|
||||||
import { Dimensions } from "react-native";
|
import { Dimensions, View, ViewProps } from "react-native";
|
||||||
import { useSharedValue } from "react-native-reanimated";
|
import { useSharedValue } from "react-native-reanimated";
|
||||||
import Carousel, {
|
import Carousel, {
|
||||||
ICarouselInstance,
|
ICarouselInstance,
|
||||||
Pagination,
|
Pagination,
|
||||||
} from "react-native-reanimated-carousel";
|
} from "react-native-reanimated-carousel";
|
||||||
import React, { useMemo } from "react";
|
|
||||||
import { Image } from "expo-image";
|
|
||||||
import { getLogoImageUrlById } from "@/utils/jellyfin/image/getLogoImageUrlById";
|
|
||||||
import { getBackdropUrl } from "@/utils/jellyfin/image/getBackdropUrl";
|
|
||||||
import { TouchableItemRouter } from "../common/TouchableItemRouter";
|
import { TouchableItemRouter } from "../common/TouchableItemRouter";
|
||||||
|
import { Loader } from "../Loader";
|
||||||
|
|
||||||
interface Props extends ViewProps {}
|
interface Props extends ViewProps {}
|
||||||
|
|
||||||
@@ -84,7 +84,7 @@ export const LargeMovieCarousel: React.FC<Props> = ({ ...props }) => {
|
|||||||
if (l1 || l2)
|
if (l1 || l2)
|
||||||
return (
|
return (
|
||||||
<View className="h-[242px] flex items-center justify-center">
|
<View className="h-[242px] flex items-center justify-center">
|
||||||
<ActivityIndicator size={"small"} color="#fff" />
|
<Loader />
|
||||||
</View>
|
</View>
|
||||||
);
|
);
|
||||||
|
|
||||||
|
|||||||
@@ -1,18 +1,12 @@
|
|||||||
import {
|
|
||||||
ActivityIndicator,
|
|
||||||
Linking,
|
|
||||||
Switch,
|
|
||||||
TouchableOpacity,
|
|
||||||
View,
|
|
||||||
} from "react-native";
|
|
||||||
import { Text } from "../common/Text";
|
|
||||||
import { useSettings } from "@/utils/atoms/settings";
|
|
||||||
import * as DropdownMenu from "zeego/dropdown-menu";
|
|
||||||
import { apiAtom, userAtom } from "@/providers/JellyfinProvider";
|
import { apiAtom, userAtom } from "@/providers/JellyfinProvider";
|
||||||
|
import { useSettings } from "@/utils/atoms/settings";
|
||||||
import { getItemsApi } from "@jellyfin/sdk/lib/utils/api";
|
import { getItemsApi } from "@jellyfin/sdk/lib/utils/api";
|
||||||
import { useQuery } from "@tanstack/react-query";
|
import { useQuery } from "@tanstack/react-query";
|
||||||
import { useAtom } from "jotai";
|
import { useAtom } from "jotai";
|
||||||
import { useState } from "react";
|
import { Linking, Switch, TouchableOpacity, View } from "react-native";
|
||||||
|
import * as DropdownMenu from "zeego/dropdown-menu";
|
||||||
|
import { Text } from "../common/Text";
|
||||||
|
import { Loader } from "../Loader";
|
||||||
|
|
||||||
export const SettingToggles: React.FC = () => {
|
export const SettingToggles: React.FC = () => {
|
||||||
const [settings, updateSettings] = useSettings();
|
const [settings, updateSettings] = useSettings();
|
||||||
@@ -83,7 +77,7 @@ export const SettingToggles: React.FC = () => {
|
|||||||
<TouchableOpacity
|
<TouchableOpacity
|
||||||
onPress={() => {
|
onPress={() => {
|
||||||
Linking.openURL(
|
Linking.openURL(
|
||||||
"https://github.com/lostb1t/jellyfin-plugin-media-lists",
|
"https://github.com/lostb1t/jellyfin-plugin-media-lists"
|
||||||
);
|
);
|
||||||
}}
|
}}
|
||||||
>
|
>
|
||||||
@@ -121,7 +115,7 @@ export const SettingToggles: React.FC = () => {
|
|||||||
mediaListCollectionIds:
|
mediaListCollectionIds:
|
||||||
settings?.mediaListCollectionIds.includes(mlc.Id!)
|
settings?.mediaListCollectionIds.includes(mlc.Id!)
|
||||||
? settings?.mediaListCollectionIds.filter(
|
? settings?.mediaListCollectionIds.filter(
|
||||||
(id) => id !== mlc.Id,
|
(id) => id !== mlc.Id
|
||||||
)
|
)
|
||||||
: [...settings?.mediaListCollectionIds, mlc.Id!],
|
: [...settings?.mediaListCollectionIds, mlc.Id!],
|
||||||
});
|
});
|
||||||
@@ -131,7 +125,7 @@ export const SettingToggles: React.FC = () => {
|
|||||||
))}
|
))}
|
||||||
{isLoadingMediaListCollections && (
|
{isLoadingMediaListCollections && (
|
||||||
<View className="flex flex-row items-center justify-center bg-neutral-900 p-4">
|
<View className="flex flex-row items-center justify-center bg-neutral-900 p-4">
|
||||||
<ActivityIndicator size="small" color="#fff" />
|
<Loader />
|
||||||
</View>
|
</View>
|
||||||
)}
|
)}
|
||||||
{mediaListCollections?.length === 0 && (
|
{mediaListCollections?.length === 0 && (
|
||||||
|
|||||||
Reference in New Issue
Block a user