fix: loading indicator style

This commit is contained in:
Fredrik Burmester
2024-08-18 07:44:28 +02:00
parent d56bb79ac2
commit 550fc39faa
17 changed files with 116 additions and 161 deletions

View File

@@ -2,6 +2,7 @@ import { Button } from "@/components/Button";
import { Text } from "@/components/common/Text";
import { LargeMovieCarousel } from "@/components/home/LargeMovieCarousel";
import { ScrollingCollectionList } from "@/components/home/ScrollingCollectionList";
import { Loader } from "@/components/Loader";
import { MediaListSection } from "@/components/medialists/MediaListSection";
import { apiAtom, userAtom } from "@/providers/JellyfinProvider";
import { useSettings } from "@/utils/atoms/settings";
@@ -19,12 +20,7 @@ import { useQuery, useQueryClient } from "@tanstack/react-query";
import { useRouter } from "expo-router";
import { useAtom } from "jotai";
import { useCallback, useEffect, useMemo, useState } from "react";
import {
ActivityIndicator,
RefreshControl,
ScrollView,
View,
} from "react-native";
import { RefreshControl, ScrollView, View } from "react-native";
export default function index() {
const router = useRouter();
@@ -250,10 +246,10 @@ export default function index() {
</View>
);
if (isLoading)
if (true)
return (
<View className="justify-center items-center h-full">
<ActivityIndicator />
<Loader />
</View>
);

View File

@@ -3,6 +3,7 @@ import { TouchableItemRouter } from "@/components/common/TouchableItemRouter";
import { FilterButton } from "@/components/filters/FilterButton";
import { ResetFiltersButton } from "@/components/filters/ResetFiltersButton";
import { ItemCardText } from "@/components/ItemCardText";
import { Loader } from "@/components/Loader";
import MoviePoster from "@/components/posters/MoviePoster";
import { apiAtom, userAtom } from "@/providers/JellyfinProvider";
import {
@@ -24,7 +25,7 @@ import { useInfiniteQuery, useQuery } from "@tanstack/react-query";
import { useLocalSearchParams } from "expo-router";
import { useAtom } from "jotai";
import React, { useCallback, useMemo } from "react";
import { ActivityIndicator, ScrollView, View } from "react-native";
import { ScrollView, View } from "react-native";
const page: React.FC = () => {
const searchParams = useLocalSearchParams();
@@ -284,12 +285,10 @@ const page: React.FC = () => {
</View>
</ScrollView>
{!type && isFetching && (
<ActivityIndicator
<Loader
style={{
marginTop: 300,
}}
size={"small"}
color={"white"}
/>
)}
</View>

View File

@@ -1,35 +1,21 @@
import { Text } from "@/components/common/Text";
import { Loader } from "@/components/Loader";
import { apiAtom, userAtom } from "@/providers/JellyfinProvider";
import { getItemsApi, getUserViewsApi } from "@jellyfin/sdk/lib/utils/api";
import { useQuery, useQueryClient } from "@tanstack/react-query";
import { getPrimaryImageUrl } from "@/utils/jellyfin/image/getPrimaryImageUrl";
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 { useAtom } from "jotai";
import { useCallback, useMemo, useState } from "react";
import {
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";
import { useMemo } from "react";
import { TouchableOpacity, View } from "react-native";
export default function index() {
const router = useRouter();
const queryClient = useQueryClient();
const [api] = useAtom(apiAtom);
const [user] = useAtom(userAtom);
const [loading, setLoading] = useState(false);
const [settings, _] = useSettings();
const { data, isLoading: isLoading } = useQuery({
queryKey: ["user-views", user?.Id],
queryFn: async () => {
@@ -50,7 +36,7 @@ export default function index() {
if (isLoading)
return (
<View className="justify-center items-center h-full">
<ActivityIndicator />
<Loader />
</View>
);

View File

@@ -4,28 +4,19 @@ import { Text } from "@/components/common/Text";
import { TouchableItemRouter } from "@/components/common/TouchableItemRouter";
import ContinueWatchingPoster from "@/components/ContinueWatchingPoster";
import { ItemCardText } from "@/components/ItemCardText";
import { Loader } from "@/components/Loader";
import AlbumCover from "@/components/posters/AlbumCover";
import MoviePoster from "@/components/posters/MoviePoster";
import Poster from "@/components/posters/Poster";
import SeriesPoster from "@/components/posters/SeriesPoster";
import { apiAtom, userAtom } from "@/providers/JellyfinProvider";
import { getPrimaryImageUrl } from "@/utils/jellyfin/image/getPrimaryImageUrl";
import { getUserItemData } from "@/utils/jellyfin/user-library/getUserItemData";
import { Ionicons } from "@expo/vector-icons";
import { BaseItemDto } from "@jellyfin/sdk/lib/generated-client/models";
import { getSearchApi } from "@jellyfin/sdk/lib/utils/api";
import { useQuery } from "@tanstack/react-query";
import { router, Stack, useNavigation } from "expo-router";
import { router, useNavigation } from "expo-router";
import { useAtom } from "jotai";
import React, { useLayoutEffect, useMemo, useState } from "react";
import {
ActivityIndicator,
Platform,
ScrollView,
TouchableOpacity,
View,
} from "react-native";
import _ from "lodash";
import { Platform, ScrollView, TouchableOpacity, View } from "react-native";
import { useDebounce } from "use-debounce";
const exampleSearches = [
@@ -308,7 +299,7 @@ export default function search() {
/>
{loading ? (
<View className="mt-4 flex justify-center items-center">
<ActivityIndicator size="small" color="white" />
<Loader />
</View>
) : noResults && debouncedSearch.length > 0 ? (
<View>

View File

@@ -1,4 +1,5 @@
import { Text } from "@/components/common/Text";
import { Loader } from "@/components/Loader";
import ArtistPoster from "@/components/posters/ArtistPoster";
import MoviePoster from "@/components/posters/MoviePoster";
import { apiAtom, userAtom } from "@/providers/JellyfinProvider";
@@ -13,12 +14,7 @@ import { useQuery } from "@tanstack/react-query";
import { router, useLocalSearchParams } from "expo-router";
import { useAtom } from "jotai";
import { useMemo, useState } from "react";
import {
ActivityIndicator,
ScrollView,
TouchableOpacity,
View,
} from "react-native";
import { ScrollView, TouchableOpacity, View } from "react-native";
const page: React.FC = () => {
const searchParams = useLocalSearchParams();
@@ -154,7 +150,7 @@ const page: React.FC = () => {
</View>
{isLoading ? (
<View className="my-12">
<ActivityIndicator color={"white"} />
<Loader />
</View>
) : (
<View className="flex flex-row flex-wrap">

View File

@@ -1,23 +1,18 @@
import { Text } from "@/components/common/Text";
import { MovieCard } from "@/components/downloads/MovieCard";
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 AsyncStorage from "@react-native-async-storage/async-storage";
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 { Ionicons } from "@expo/vector-icons";
import { FFmpegKit } from "ffmpeg-kit-react-native";
import * as FileSystem from "expo-file-system";
import { queueAtom } from "@/utils/atoms/queue";
import { useAtom } from "jotai";
import { useMemo } from "react";
import { ScrollView, TouchableOpacity, View } from "react-native";
const downloads: React.FC = () => {
const [process, setProcess] = useAtom(runningProcesses);
@@ -27,14 +22,14 @@ const downloads: React.FC = () => {
queryKey: ["downloaded_files", process?.item.Id],
queryFn: async () =>
JSON.parse(
(await AsyncStorage.getItem("downloaded_files")) || "[]",
(await AsyncStorage.getItem("downloaded_files")) || "[]"
) as BaseItemDto[],
staleTime: 0,
});
const movies = useMemo(
() => downloadedFiles?.filter((f) => f.Type === "Movie") || [],
[downloadedFiles],
[downloadedFiles]
);
const groupedBySeries = useMemo(() => {
@@ -61,7 +56,7 @@ const downloads: React.FC = () => {
if (isLoading) {
return (
<View className="h-full flex flex-col items-center justify-center -mt-6">
<ActivityIndicator size="small" color="white" />
<Loader />
</View>
);
}

View File

@@ -6,6 +6,7 @@ import {
playingAtom,
} from "@/components/CurrentlyPlayingBar";
import { DownloadItem } from "@/components/DownloadItem";
import { Loader } from "@/components/Loader";
import { OverviewText } from "@/components/OverviewText";
import { PlayButton } from "@/components/PlayButton";
import { PlayedStatus } from "@/components/PlayedStatus";
@@ -34,7 +35,7 @@ import { Image } from "expo-image";
import { useLocalSearchParams } from "expo-router";
import { useAtom } from "jotai";
import { useCallback, useMemo, useState } from "react";
import { ActivityIndicator, ScrollView, View } from "react-native";
import { View } from "react-native";
import CastContext, {
PlayServicesState,
useCastDevice,
@@ -194,7 +195,7 @@ const page: React.FC = () => {
if (l1)
return (
<View className="justify-center items-center h-full">
<ActivityIndicator />
<Loader />
</View>
);

View File

@@ -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 {
currentlyPlayingItemAtom,
playingAtom,
} from "@/components/CurrentlyPlayingBar";
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 { SubtitleTrackSelector } from "@/components/SubtitleTrackSelector";
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 { Image } from "expo-image";
import { useLocalSearchParams, useNavigation } from "expo-router";
import { useAtom } from "jotai";
import { useCallback, useEffect, useMemo, useState } from "react";
import { ActivityIndicator, 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 { ScrollView, View } from "react-native";
import CastContext, {
PlayServicesState,
useCastDevice,
useRemoteMediaClient,
} 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 local = useLocalSearchParams();
@@ -84,12 +85,12 @@ const page: React.FC = () => {
quality: 90,
width: 1000,
}),
[item],
[item]
);
const logoUrl = useMemo(
() => (item?.Type === "Movie" ? getLogoImageUrlById({ api, item }) : null),
[item],
[item]
);
const { data: sessionData } = useQuery({
@@ -173,13 +174,13 @@ const page: React.FC = () => {
setPlaying(true);
}
},
[playbackUrl, item],
[playbackUrl, item]
);
if (l1)
return (
<View className="justify-center items-center h-full">
<ActivityIndicator />
<Loader />
</View>
);

View File

@@ -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 React, { PropsWithChildren, ReactNode, useMemo } from "react";
import { Text, TouchableOpacity, View } from "react-native";
import { Loader } from "./Loader";
interface ButtonProps extends React.ComponentProps<typeof TouchableOpacity> {
onPress?: () => void;
@@ -57,7 +58,7 @@ export const Button: React.FC<PropsWithChildren<ButtonProps>> = ({
{...props}
>
{loading ? (
<ActivityIndicator color={"white"} size={24} />
<Loader />
) : (
<View
className={`

View File

@@ -1,5 +1,4 @@
import { apiAtom, userAtom } from "@/providers/JellyfinProvider";
import { useSettings } from "@/utils/atoms/settings";
import { getBackdropUrl } from "@/utils/jellyfin/image/getBackdropUrl";
import { getAuthHeaders } from "@/utils/jellyfin/jellyfin";
import { reportPlaybackProgress } from "@/utils/jellyfin/playstate/reportPlaybackProgress";
@@ -14,13 +13,7 @@ import { BlurView } from "expo-blur";
import { useRouter, useSegments } from "expo-router";
import { atom, useAtom } from "jotai";
import { useCallback, useEffect, useMemo, useRef, useState } from "react";
import {
ActivityIndicator,
Alert,
Platform,
TouchableOpacity,
View,
} from "react-native";
import { Alert, Platform, TouchableOpacity, View } from "react-native";
import Animated, {
useAnimatedStyle,
useSharedValue,
@@ -28,6 +21,7 @@ import Animated, {
} from "react-native-reanimated";
import Video, { OnProgressData, VideoRef } from "react-native-video";
import { Text } from "./common/Text";
import { Loader } from "./Loader";
export const currentlyPlayingItemAtom = atom<{
item: BaseItemDto;
@@ -292,7 +286,7 @@ export const CurrentlyPlayingBar: React.FC = () => {
renderLoader={
item?.Type !== "Audio" && (
<View className="flex flex-col items-center justify-center h-full">
<ActivityIndicator size={"small"} color={"white"} />
<Loader />
</View>
)
}

View File

@@ -9,7 +9,8 @@ import AsyncStorage from "@react-native-async-storage/async-storage";
import { useQuery } from "@tanstack/react-query";
import { router } from "expo-router";
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";
type DownloadProps = {
@@ -39,7 +40,7 @@ export const DownloadItem: React.FC<DownloadProps> = ({
if (!item.Id) return false;
const data: BaseItemDto[] = JSON.parse(
(await AsyncStorage.getItem("downloaded_files")) || "[]",
(await AsyncStorage.getItem("downloaded_files")) || "[]"
);
return data.some((d) => d.Id === item.Id);
@@ -50,7 +51,7 @@ export const DownloadItem: React.FC<DownloadProps> = ({
if (isLoading || isLoadingDownloaded) {
return (
<View className="rounded h-10 aspect-square flex items-center justify-center">
<ActivityIndicator size={"small"} color={"white"} />
<Loader />
</View>
);
}
@@ -72,7 +73,7 @@ export const DownloadItem: React.FC<DownloadProps> = ({
>
<View className="rounded h-10 aspect-square flex items-center justify-center">
{process.progress === 0 ? (
<ActivityIndicator size={"small"} color={"white"} />
<Loader />
) : (
<View className="-rotate-45">
<ProgressCircle

18
components/Loader.tsx Normal file
View 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}
/>
);
};

View File

@@ -1,9 +0,0 @@
import { ActivityIndicator, View } from "react-native";
export const Loading: React.FC = () => {
return (
<View>
<ActivityIndicator />
</View>
);
};

View File

@@ -6,14 +6,10 @@ import { useQuery } from "@tanstack/react-query";
import { router } from "expo-router";
import { useAtom } from "jotai";
import { useMemo } from "react";
import {
ActivityIndicator,
ScrollView,
TouchableOpacity,
View,
} from "react-native";
import { ItemCardText } from "./ItemCardText";
import { ScrollView, TouchableOpacity, View } from "react-native";
import { Text } from "./common/Text";
import { ItemCardText } from "./ItemCardText";
import { Loader } from "./Loader";
type SimilarItemsProps = {
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>
{isLoading ? (
<View className="my-12">
<ActivityIndicator />
<Loader />
</View>
) : (
<ScrollView horizontal>

View File

@@ -1,16 +1,11 @@
import React, { useEffect } from "react";
import {
ScrollView,
View,
ViewStyle,
ActivityIndicator,
ScrollViewProps,
} from "react-native";
import { ScrollView, ScrollViewProps, View, ViewStyle } from "react-native";
import Animated, {
useAnimatedStyle,
useSharedValue,
withTiming,
} from "react-native-reanimated";
import { Loader } from "../Loader";
import { Text } from "./Text";
interface HorizontalScrollProps<T> extends ScrollViewProps {
@@ -58,7 +53,7 @@ export function HorizontalScroll<T>({
loadingContainerStyle,
]}
>
<ActivityIndicator size="small" />
<Loader />
</View>
);
}

View File

@@ -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 { getItemsApi } from "@jellyfin/sdk/lib/utils/api";
import { useQuery, useQueryClient } from "@tanstack/react-query";
import { apiAtom, userAtom } from "@/providers/JellyfinProvider";
import { Image } from "expo-image";
import { useRouter } from "expo-router";
import { useAtom } from "jotai";
import { useSettings } from "@/utils/atoms/settings";
import { Dimensions } from "react-native";
import React, { useMemo } from "react";
import { Dimensions, View, ViewProps } from "react-native";
import { useSharedValue } from "react-native-reanimated";
import Carousel, {
ICarouselInstance,
Pagination,
} 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 { Loader } from "../Loader";
interface Props extends ViewProps {}
@@ -84,7 +84,7 @@ export const LargeMovieCarousel: React.FC<Props> = ({ ...props }) => {
if (l1 || l2)
return (
<View className="h-[242px] flex items-center justify-center">
<ActivityIndicator size={"small"} color="#fff" />
<Loader />
</View>
);

View File

@@ -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 { useSettings } from "@/utils/atoms/settings";
import { getItemsApi } from "@jellyfin/sdk/lib/utils/api";
import { useQuery } from "@tanstack/react-query";
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 = () => {
const [settings, updateSettings] = useSettings();
@@ -83,7 +77,7 @@ export const SettingToggles: React.FC = () => {
<TouchableOpacity
onPress={() => {
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:
settings?.mediaListCollectionIds.includes(mlc.Id!)
? settings?.mediaListCollectionIds.filter(
(id) => id !== mlc.Id,
(id) => id !== mlc.Id
)
: [...settings?.mediaListCollectionIds, mlc.Id!],
});
@@ -131,7 +125,7 @@ export const SettingToggles: React.FC = () => {
))}
{isLoadingMediaListCollections && (
<View className="flex flex-row items-center justify-center bg-neutral-900 p-4">
<ActivityIndicator size="small" color="#fff" />
<Loader />
</View>
)}
{mediaListCollections?.length === 0 && (