chore: linting fixes && github actions for linting (#612)

This commit is contained in:
Ahmed Sbai
2025-03-31 07:44:10 +02:00
committed by GitHub
parent 16b834cf71
commit b9bb109f4a
105 changed files with 604 additions and 570 deletions

View File

@@ -26,11 +26,11 @@ export default function menuLinks() {
const getMenuLinks = useCallback(async () => {
try {
const response = await api?.axiosInstance.get(
api?.basePath + "/web/config.json",
`${api?.basePath}/web/config.json`,
);
const config = response?.data;
if (!config && !config.hasOwnProperty("menuLinks")) {
if (!config && !Object.hasOwn(config, "menuLinks")) {
console.error("Menu links not found");
return;
}

View File

@@ -17,7 +17,7 @@ export default function SearchLayout() {
backgroundColor: "black",
},
headerBlurEffect: "prominent",
headerTransparent: Platform.OS === "ios" ? true : false,
headerTransparent: Platform.OS === "ios",
headerShadowVisible: false,
}}
/>

View File

@@ -32,7 +32,7 @@ export default function IndexLayout() {
{!Platform.isTV && (
<>
<Chromecast.Chromecast />
{user && user.Policy?.IsAdministrator && <SessionsButton />}
{user?.Policy?.IsAdministrator && <SessionsButton />}
<SettingsButton />
</>
)}

View File

@@ -29,7 +29,7 @@ export default function page() {
try {
return (
downloadedFiles
?.filter((f) => f.item.SeriesId == seriesId)
?.filter((f) => f.item.SeriesId === seriesId)
?.sort(
(a, b) => a?.item.ParentIndexNumber! - b.item.ParentIndexNumber!,
) || []

View File

@@ -36,7 +36,7 @@ export default function page() {
</View>
);
if (!sessions || sessions.length == 0)
if (!sessions || sessions.length === 0)
return (
<View className='h-full w-full flex justify-center items-center'>
<Text className='text-lg text-neutral-500'>
@@ -175,7 +175,7 @@ const SessionCard = ({ session }: SessionCardProps) => {
</View>
<View className='align-bottom bg-gray-800 h-1'>
<View
className={`bg-purple-600 h-full`}
className={"bg-purple-600 h-full"}
style={{
width: `${getProgressPercentage()}%`,
}}
@@ -298,7 +298,7 @@ const TranscodingStreamView = ({
const TranscodingView = ({ session }: SessionCardProps) => {
const videoStream = useMemo(() => {
return session.NowPlayingItem?.MediaStreams?.filter(
(s) => s.Type == "Video",
(s) => s.Type === "Video",
)[0];
}, [session]);
@@ -318,7 +318,7 @@ const TranscodingView = ({ session }: SessionCardProps) => {
const isTranscoding = useMemo(() => {
return (
session.PlayState?.PlayMethod == "Transcode" && session.TranscodingInfo
session.PlayState?.PlayMethod === "Transcode" && session.TranscodingInfo
);
}, [session.PlayState?.PlayMethod, session.TranscodingInfo]);
@@ -341,9 +341,7 @@ const TranscodingView = ({ session }: SessionCardProps) => {
codec: session.TranscodingInfo?.VideoCodec,
}}
isTranscoding={
isTranscoding && !session.TranscodingInfo?.IsVideoDirect
? true
: false
!!(isTranscoding && !session.TranscodingInfo?.IsVideoDirect)
}
/>
@@ -360,24 +358,20 @@ const TranscodingView = ({ session }: SessionCardProps) => {
audioChannels: session.TranscodingInfo?.AudioChannels?.toString(),
}}
isTranscoding={
isTranscoding && !session.TranscodingInfo?.IsVideoDirect
? true
: false
!!(isTranscoding && !session.TranscodingInfo?.IsVideoDirect)
}
/>
{subtitleStream && (
<>
<TranscodingStreamView
title='Subtitle'
isTranscoding={false}
properties={{
language: subtitleStream?.Language,
codec: subtitleStream?.Codec,
}}
transcodeValue={null}
/>
</>
<TranscodingStreamView
title='Subtitle'
isTranscoding={false}
properties={{
language: subtitleStream?.Language,
codec: subtitleStream?.Codec,
}}
transcodeValue={null}
/>
)}
</View>
);

View File

@@ -1,73 +1,77 @@
import { Loader } from "@/components/Loader";
import { Text } from "@/components/common/Text";
import {LogLevel, useLog, writeErrorLog} from "@/utils/log";
import { FilterButton } from "@/components/filters/FilterButton";
import { LogLevel, useLog, writeErrorLog } from "@/utils/log";
import * as FileSystem from "expo-file-system";
import { useNavigation } from "expo-router";
import * as Sharing from "expo-sharing";
import React, { useCallback, useEffect, useMemo, useState } from "react";
import { useTranslation } from "react-i18next";
import {ScrollView, TouchableOpacity, View} from "react-native";
import { ScrollView, TouchableOpacity, View } from "react-native";
import Collapsible from "react-native-collapsible";
import React, {useCallback, useEffect, useMemo, useState} from "react";
import {FilterButton} from "@/components/filters/FilterButton";
import {useNavigation} from "expo-router";
import * as FileSystem from 'expo-file-system';
import * as Sharing from 'expo-sharing';
import {Loader} from "@/components/Loader";
export default function page() {
const navigation = useNavigation();
const { logs } = useLog();
const { t } = useTranslation();
const defaultLevels: LogLevel[] = ["INFO", "ERROR", "DEBUG", "WARN"]
const defaultLevels: LogLevel[] = ["INFO", "ERROR", "DEBUG", "WARN"];
const codeBlockStyle = {
backgroundColor: '#000',
backgroundColor: "#000",
padding: 10,
fontFamily: 'monospace',
maxHeight: 300
}
fontFamily: "monospace",
maxHeight: 300,
};
const [loading, setLoading] = useState<boolean>(false)
const [state, setState] = useState<Record<string, boolean>>({})
const [loading, setLoading] = useState<boolean>(false);
const [state, setState] = useState<Record<string, boolean>>({});
const [order, setOrder] = useState<"asc" | "desc">("desc");
const [levels, setLevels] = useState<LogLevel[]>(defaultLevels);
const filteredLogs = useMemo(
() => logs
?.filter(log => levels.includes(log.level))
// Already in asc order as they are recorded. just reverse for desc
?.[order === "desc" ? "reverse" : "concat"]?.(),
[logs, order, levels]
)
() =>
logs
?.filter((log) => levels.includes(log.level))
?.[
// Already in asc order as they are recorded. just reverse for desc
order === "desc" ? "reverse" : "concat"
]?.(),
[logs, order, levels],
);
// Sharing it as txt while its formatted allows us to share it with many more applications
const share = useCallback(async () => {
const uri = FileSystem.documentDirectory + "logs.txt"
const uri = `${FileSystem.documentDirectory}logs.txt`;
setLoading(true)
setLoading(true);
FileSystem.writeAsStringAsync(uri, JSON.stringify(filteredLogs))
.then(() => {
setLoading(false)
Sharing.shareAsync(uri, {mimeType: "txt", UTI: "txt"})
setLoading(false);
Sharing.shareAsync(uri, { mimeType: "txt", UTI: "txt" });
})
.catch((e) => writeErrorLog("Something went wrong attempting to export", e))
.finally(() => setLoading(false))
}, [filteredLogs])
.catch((e) =>
writeErrorLog("Something went wrong attempting to export", e),
)
.finally(() => setLoading(false));
}, [filteredLogs]);
useEffect(() => {
navigation.setOptions({
headerRight: () => (
loading
? <Loader/>
: (
headerRight: () =>
loading ? (
<Loader />
) : (
<TouchableOpacity onPress={share}>
<Text>{t("home.settings.logs.export_logs")}</Text>
</TouchableOpacity>
)
),
),
});
}, [share, loading]);
return (
<>
<View className="flex flex-row justify-end py-2 px-4 space-x-2">
<View className='flex flex-row justify-end py-2 px-4 space-x-2'>
<FilterButton
id='order'
queryKey='log'
@@ -93,25 +97,30 @@ export default function page() {
<ScrollView className='pb-4 px-4'>
<View className='flex flex-col space-y-2'>
{filteredLogs?.map((log, index) => (
<View
className='bg-neutral-900 rounded-xl p-3'
key={index}
>
<View className='bg-neutral-900 rounded-xl p-3' key={index}>
<TouchableOpacity
disabled={!log.data}
onPress={() => setState((v) => ({...v, [log.timestamp]: !v[log.timestamp]}))}
onPress={() =>
setState((v) => ({
...v,
[log.timestamp]: !v[log.timestamp],
}))
}
>
<View className="flex flex-row justify-between">
<View className='flex flex-row justify-between'>
<Text
className={`mb-1
${log.level === "INFO" && "text-blue-500"}
${log.level === "ERROR" && "text-red-500"}
${log.level === "DEBUG" && "text-purple-500"}
`}>
`}
>
{log.level}
</Text>
<Text className="text-xs">{new Date(log.timestamp).toLocaleString()}</Text>
<Text className='text-xs'>
{new Date(log.timestamp).toLocaleString()}
</Text>
</View>
<Text uiTextView selectable className='text-xs'>
{log.message}
@@ -121,14 +130,14 @@ export default function page() {
{log.data && (
<>
{!state[log.timestamp] && (
<Text className="text-xs mt-0.5">{t("home.settings.logs.click_for_more_info")}</Text>
<Text className='text-xs mt-0.5'>
{t("home.settings.logs.click_for_more_info")}
</Text>
)}
<Collapsible collapsed={!state[log.timestamp]}>
<View className="mt-2 flex flex-col space-y-2">
<ScrollView className="rounded-xl" style={codeBlockStyle}>
<Text>
{JSON.stringify(log.data, null, 2)}
</Text>
<View className='mt-2 flex flex-col space-y-2'>
<ScrollView className='rounded-xl' style={codeBlockStyle}>
<Text>{JSON.stringify(log.data, null, 2)}</Text>
</ScrollView>
</View>
</Collapsible>

View File

@@ -93,7 +93,7 @@ export default function page() {
showText={!pluginSettings?.searchEngine?.locked}
className='mt-2 flex flex-col rounded-xl overflow-hidden pl-4 bg-neutral-900 px-4'
>
<View className={`flex flex-row items-center bg-neutral-900 h-11 pr-4`}>
<View className={"flex flex-row items-center bg-neutral-900 h-11 pr-4"}>
<Text className='mr-4'>
{t("home.settings.plugins.marlin_search.url")}
</Text>

View File

@@ -31,7 +31,7 @@ export default function page() {
return;
}
const updatedUrl = newVal.endsWith("/") ? newVal : newVal + "/";
const updatedUrl = newVal.endsWith("/") ? newVal : `${newVal}/`;
updateSettings({
optimizedVersionsServerUrl: updatedUrl,

View File

@@ -133,7 +133,7 @@ const page: React.FC = () => {
queryFn={fetchItems}
queryKey={["actor", "movies", actorId]}
/>
<View className='h-12'></View>
<View className='h-12' />
</View>
</ParallaxScrollView>
);

View File

@@ -157,9 +157,8 @@ const page: React.FC = () => {
if (accumulatedItems < totalItems) {
return lastPage?.Items?.length * pages.length;
} else {
return undefined;
}
return undefined;
},
initialPageParam: 0,
enabled: !!api && !!user?.Id && !!collection,
@@ -412,7 +411,7 @@ const page: React.FC = () => {
width: 10,
height: 10,
}}
></View>
/>
)}
/>
);

View File

@@ -93,19 +93,19 @@ const Page: React.FC = () => {
height: item?.Type === "Episode" ? 300 : 450,
}}
className='bg-transparent rounded-lg mb-4 w-full'
></View>
<View className='h-6 bg-neutral-900 rounded mb-4 w-14'></View>
<View className='h-10 bg-neutral-900 rounded-lg mb-2 w-1/2'></View>
<View className='h-3 bg-neutral-900 rounded mb-3 w-8'></View>
/>
<View className='h-6 bg-neutral-900 rounded mb-4 w-14' />
<View className='h-10 bg-neutral-900 rounded-lg mb-2 w-1/2' />
<View className='h-3 bg-neutral-900 rounded mb-3 w-8' />
<View className='flex flex-row space-x-1 mb-8'>
<View className='h-6 bg-neutral-900 rounded mb-3 w-14'></View>
<View className='h-6 bg-neutral-900 rounded mb-3 w-14'></View>
<View className='h-6 bg-neutral-900 rounded mb-3 w-14'></View>
<View className='h-6 bg-neutral-900 rounded mb-3 w-14' />
<View className='h-6 bg-neutral-900 rounded mb-3 w-14' />
<View className='h-6 bg-neutral-900 rounded mb-3 w-14' />
</View>
<View className='h-3 bg-neutral-900 rounded w-2/3 mb-1'></View>
<View className='h-10 bg-neutral-900 rounded-lg w-full mb-2'></View>
<View className='h-12 bg-neutral-900 rounded-lg w-full mb-2'></View>
<View className='h-24 bg-neutral-900 rounded-lg mb-1 w-full'></View>
<View className='h-3 bg-neutral-900 rounded w-2/3 mb-1' />
<View className='h-10 bg-neutral-900 rounded-lg w-full mb-2' />
<View className='h-12 bg-neutral-900 rounded-lg w-full mb-2' />
<View className='h-24 bg-neutral-900 rounded-lg mb-1 w-full' />
</Animated.View>
{item && <ItemContent item={item} />}
</View>

View File

@@ -33,9 +33,11 @@ export default function page() {
};
return jellyseerrApi?.discover(
(type == DiscoverSliderType.NETWORKS
? Endpoints.DISCOVER_TV_NETWORK
: Endpoints.DISCOVER_MOVIES_STUDIO) + `/${companyId}`,
`${
type === DiscoverSliderType.NETWORKS
? Endpoints.DISCOVER_TV_NETWORK
: Endpoints.DISCOVER_MOVIES_STUDIO
}/${companyId}`,
params,
);
},

View File

@@ -36,7 +36,7 @@ export default function page() {
};
return jellyseerrApi?.discover(
type == DiscoverSliderType.MOVIE_GENRES
type === DiscoverSliderType.MOVIE_GENRES
? Endpoints.DISCOVER_MOVIES
: Endpoints.DISCOVER_TV,
params,

View File

@@ -240,7 +240,7 @@ const Page: React.FC = () => {
<GenreTags genres={details?.genres?.map((g) => g.name) || []} />
</View>
{isLoading || isFetching ? (
<Button loading={true} disabled={true} color='purple'></Button>
<Button loading={true} disabled={true} color='purple' />
) : canRequest ? (
<Button color='purple' onPress={request}>
{t("jellyseerr.request_button")}

View File

@@ -124,7 +124,7 @@ export default function page() {
height: HOUR_HEIGHT,
}}
className='bg-neutral-800'
></View>
/>
{channels?.Items?.map((c, i) => (
<View className='h-16 w-16 mr-4 rounded-lg overflow-hidden' key={i}>
<ItemImage

View File

@@ -87,23 +87,21 @@ const page: React.FC = () => {
<View className='flex flex-row items-center space-x-2'>
<AddToFavorites item={item} />
{!Platform.isTV && (
<>
<DownloadItems
size='large'
title={t("item_card.download.download_series")}
items={allEpisodes || []}
MissingDownloadIconComponent={() => (
<Ionicons name='download' size={22} color='white' />
)}
DownloadedIconComponent={() => (
<Ionicons
name='checkmark-done-outline'
size={24}
color='#9333ea'
/>
)}
/>
</>
<DownloadItems
size='large'
title={t("item_card.download.download_series")}
items={allEpisodes || []}
MissingDownloadIconComponent={() => (
<Ionicons name='download' size={22} color='white' />
)}
DownloadedIconComponent={() => (
<Ionicons
name='checkmark-done-outline'
size={24}
color='#9333ea'
/>
)}
/>
)}
</View>
),
@@ -127,20 +125,18 @@ const page: React.FC = () => {
/>
}
logo={
<>
{logoUrl ? (
<Image
source={{
uri: logoUrl,
}}
style={{
height: 130,
width: "100%",
resizeMode: "contain",
}}
/>
) : null}
</>
logoUrl ? (
<Image
source={{
uri: logoUrl,
}}
style={{
height: 130,
width: "100%",
resizeMode: "contain",
}}
/>
) : null
}
>
<View className='flex flex-col pt-4'>

View File

@@ -216,9 +216,8 @@ const Page = () => {
if (accumulatedItems < totalItems) {
return lastPage?.Items?.length * pages.length;
} else {
return undefined;
}
return undefined;
},
initialPageParam: 0,
enabled: !!api && !!user?.Id && !!library,
@@ -478,7 +477,7 @@ const Page = () => {
width: 10,
height: 10,
}}
></View>
/>
)}
/>
);

View File

@@ -25,7 +25,7 @@ export default function IndexLayout() {
headerLargeStyle: {
backgroundColor: "black",
},
headerTransparent: Platform.OS === "ios" ? true : false,
headerTransparent: Platform.OS === "ios",
headerShadowVisible: false,
headerRight: () =>
!pluginSettings?.libraryOptions?.locked &&
@@ -159,7 +159,7 @@ export default function IndexLayout() {
updateSettings({
libraryOptions: {
...settings.libraryOptions,
showTitles: newValue === "on" ? true : false,
showTitles: newValue === "on",
},
});
}}
@@ -176,7 +176,7 @@ export default function IndexLayout() {
updateSettings({
libraryOptions: {
...settings.libraryOptions,
showStats: newValue === "on" ? true : false,
showStats: newValue === "on",
},
});
}}
@@ -200,7 +200,7 @@ export default function IndexLayout() {
title: "",
headerShown: true,
headerBlurEffect: "prominent",
headerTransparent: Platform.OS === "ios" ? true : false,
headerTransparent: Platform.OS === "ios",
headerShadowVisible: false,
}}
/>
@@ -213,7 +213,7 @@ export default function IndexLayout() {
title: "",
headerShown: true,
headerBlurEffect: "prominent",
headerTransparent: Platform.OS === "ios" ? true : false,
headerTransparent: Platform.OS === "ios",
headerShadowVisible: false,
}}
/>

View File

@@ -100,7 +100,7 @@ export default function index() {
height: StyleSheet.hairlineWidth,
}}
className='bg-neutral-800 mx-2 my-4'
></View>
/>
) : (
<View className='h-4' />
)

View File

@@ -20,7 +20,7 @@ export default function SearchLayout() {
backgroundColor: "black",
},
headerBlurEffect: "prominent",
headerTransparent: Platform.OS === "ios" ? true : false,
headerTransparent: Platform.OS === "ios",
headerShadowVisible: false,
}}
/>
@@ -33,7 +33,7 @@ export default function SearchLayout() {
title: "",
headerShown: true,
headerBlurEffect: "prominent",
headerTransparent: Platform.OS === "ios" ? true : false,
headerTransparent: Platform.OS === "ios",
headerShadowVisible: false,
}}
/>

View File

@@ -13,8 +13,7 @@ import SeriesPoster from "@/components/posters/SeriesPoster";
import { LoadingSkeleton } from "@/components/search/LoadingSkeleton";
import { SearchItemWrapper } from "@/components/search/SearchItemWrapper";
import { useJellyseerr } from "@/hooks/useJellyseerr";
import { apiAtom, userAtom } from "@/providers/JellyfinProvider";
import { sortOrderOptions } from "@/utils/atoms/filters";
import { apiAtom } from "@/providers/JellyfinProvider";
import { useSettings } from "@/utils/atoms/settings";
import { eventBus } from "@/utils/eventBus";
import type {
@@ -64,7 +63,6 @@ export default function search() {
const [debouncedSearch] = useDebounce(search, 500);
const [api] = useAtom(apiAtom);
const [user] = useAtom(userAtom);
const [settings] = useSettings();
const { jellyseerrApi } = useJellyseerr();
@@ -83,7 +81,9 @@ export default function search() {
}, [settings]);
useEffect(() => {
if (q && q.length > 0) setSearch(q);
if (q && q.length > 0) {
setSearch(q);
}
}, [q]);
const searchFn = useCallback(
@@ -94,7 +94,9 @@ export default function search() {
types: BaseItemKind[];
query: string;
}): Promise<BaseItemDto[]> => {
if (!api || !query) return [];
if (!api || !query) {
return [];
}
try {
if (searchEngine === "Jellyfin") {
@@ -105,28 +107,31 @@ export default function search() {
});
return (searchApi.data.SearchHints as BaseItemDto[]) || [];
} else {
if (!settings?.marlinServerUrl) return [];
const url = `${
settings.marlinServerUrl
}/search?q=${encodeURIComponent(query)}&includeItemTypes=${types
.map((type) => encodeURIComponent(type))
.join("&includeItemTypes=")}`;
const response1 = await axios.get(url);
const ids = response1.data.ids;
if (!ids || !ids.length) return [];
const response2 = await getItemsApi(api).getItems({
ids,
enableImageTypes: ["Primary", "Backdrop", "Thumb"],
});
return (response2.data.Items as BaseItemDto[]) || [];
}
if (!settings?.marlinServerUrl) {
return [];
}
const url = `${
settings.marlinServerUrl
}/search?q=${encodeURIComponent(query)}&includeItemTypes=${types
.map((type) => encodeURIComponent(type))
.join("&includeItemTypes=")}`;
const response1 = await axios.get(url);
const ids = response1.data.ids;
if (!ids || !ids.length) {
return [];
}
const response2 = await getItemsApi(api).getItems({
ids,
enableImageTypes: ["Primary", "Backdrop", "Thumb"],
});
return (response2.data.Items as BaseItemDto[]) || [];
} catch (error) {
console.error("Error during search:", error);
return []; // Ensure an empty array is returned in case of an error
@@ -162,8 +167,10 @@ export default function search() {
useEffect(() => {
const unsubscribe = eventBus.on("searchTabPressed", () => {
// Screen not actuve
if (!searchBarRef.current) return;
// Screen not active
if (!searchBarRef.current) {
return;
}
// Screen is active, focus search bar
searchBarRef.current?.focus();
});
@@ -254,64 +261,62 @@ export default function search() {
}}
>
{jellyseerrApi && (
<>
<ScrollView
horizontal
className='flex flex-row flex-wrap space-x-2 px-4 mb-2'
>
<TouchableOpacity onPress={() => setSearchType("Library")}>
<Tag
text={t("search.library")}
textClass='p-1'
className={
searchType === "Library" ? "bg-purple-600" : undefined
}
/>
</TouchableOpacity>
<TouchableOpacity onPress={() => setSearchType("Discover")}>
<Tag
text={t("search.discover")}
textClass='p-1'
className={
searchType === "Discover" ? "bg-purple-600" : undefined
}
/>
</TouchableOpacity>
{searchType === "Discover" &&
!loading &&
noResults &&
debouncedSearch.length > 0 && (
<View className='flex flex-row justify-end items-center space-x-1'>
<FilterButton
id='search'
queryKey='jellyseerr_search'
queryFn={async () =>
Object.keys(JellyseerrSearchSort).filter((v) =>
isNaN(Number(v)),
)
}
set={(value) => setJellyseerrOrderBy(value[0])}
values={[jellyseerrOrderBy]}
title={t("library.filters.sort_by")}
renderItemLabel={(item) =>
t(`home.settings.plugins.jellyseerr.order_by.${item}`)
}
showSearch={false}
/>
<FilterButton
id='order'
queryKey='jellysearr_search'
queryFn={async () => ["asc", "desc"]}
set={(value) => setJellyseerrSortOrder(value[0])}
values={[jellyseerrSortOrder]}
title={t("library.filters.sort_order")}
renderItemLabel={(item) => t(`library.filters.${item}`)}
showSearch={false}
/>
</View>
)}
</ScrollView>
</>
<ScrollView
horizontal
className='flex flex-row flex-wrap space-x-2 px-4 mb-2'
>
<TouchableOpacity onPress={() => setSearchType("Library")}>
<Tag
text={t("search.library")}
textClass='p-1'
className={
searchType === "Library" ? "bg-purple-600" : undefined
}
/>
</TouchableOpacity>
<TouchableOpacity onPress={() => setSearchType("Discover")}>
<Tag
text={t("search.discover")}
textClass='p-1'
className={
searchType === "Discover" ? "bg-purple-600" : undefined
}
/>
</TouchableOpacity>
{searchType === "Discover" &&
!loading &&
noResults &&
debouncedSearch.length > 0 && (
<View className='flex flex-row justify-end items-center space-x-1'>
<FilterButton
id='search'
queryKey='jellyseerr_search'
queryFn={async () =>
Object.keys(JellyseerrSearchSort).filter((v) =>
Number.isNaN(Number(v)),
)
}
set={(value) => setJellyseerrOrderBy(value[0])}
values={[jellyseerrOrderBy]}
title={t("library.filters.sort_by")}
renderItemLabel={(item) =>
t(`home.settings.plugins.jellyseerr.order_by.${item}`)
}
showSearch={false}
/>
<FilterButton
id='order'
queryKey='jellysearr_search'
queryFn={async () => ["asc", "desc"]}
set={(value) => setJellyseerrSortOrder(value[0])}
values={[jellyseerrSortOrder]}
title={t("library.filters.sort_order")}
renderItemLabel={(item) => t(`library.filters.${item}`)}
showSearch={false}
/>
</View>
)}
</ScrollView>
)}
<View className='mt-2'>
@@ -411,32 +416,29 @@ export default function search() {
/>
)}
{searchType === "Library" && (
<>
{!loading && noResults && debouncedSearch.length > 0 ? (
<View>
<Text className='text-center text-lg font-bold mt-4'>
{t("search.no_results_found_for")}
</Text>
<Text className='text-xs text-purple-600 text-center'>
"{debouncedSearch}"
</Text>
</View>
) : debouncedSearch.length === 0 ? (
<View className='mt-4 flex flex-col items-center space-y-2'>
{exampleSearches.map((e) => (
<TouchableOpacity
onPress={() => setSearch(e)}
key={e}
className='mb-2'
>
<Text className='text-purple-600'>{e}</Text>
</TouchableOpacity>
))}
</View>
) : null}
</>
)}
{searchType === "Library" &&
(!loading && noResults && debouncedSearch.length > 0 ? (
<View>
<Text className='text-center text-lg font-bold mt-4'>
{t("search.no_results_found_for")}
</Text>
<Text className='text-xs text-purple-600 text-center'>
"{debouncedSearch}"
</Text>
</View>
) : debouncedSearch.length === 0 ? (
<View className='mt-4 flex flex-col items-center space-y-2'>
{exampleSearches.map((e) => (
<TouchableOpacity
onPress={() => setSearch(e)}
key={e}
className='mb-2'
>
<Text className='text-purple-600'>{e}</Text>
</TouchableOpacity>
))}
</View>
) : null)}
</View>
</ScrollView>
</>

View File

@@ -72,7 +72,7 @@ export default function TabLayout() {
options={{
title: t("tabs.home"),
tabBarIcon:
Platform.OS == "android"
Platform.OS === "android"
? ({ color, focused, size }) =>
require("@/assets/icons/house.fill.png")
: ({ focused }) =>
@@ -91,7 +91,7 @@ export default function TabLayout() {
options={{
title: t("tabs.search"),
tabBarIcon:
Platform.OS == "android"
Platform.OS === "android"
? ({ color, focused, size }) =>
require("@/assets/icons/magnifyingglass.png")
: ({ focused }) =>
@@ -105,7 +105,7 @@ export default function TabLayout() {
options={{
title: t("tabs.favorites"),
tabBarIcon:
Platform.OS == "android"
Platform.OS === "android"
? ({ color, focused, size }) =>
focused
? require("@/assets/icons/heart.fill.png")
@@ -121,7 +121,7 @@ export default function TabLayout() {
options={{
title: t("tabs.library"),
tabBarIcon:
Platform.OS == "android"
Platform.OS === "android"
? ({ color, focused, size }) =>
require("@/assets/icons/server.rack.png")
: ({ focused }) =>
@@ -135,9 +135,9 @@ export default function TabLayout() {
options={{
title: t("tabs.custom_links"),
// @ts-expect-error
tabBarItemHidden: settings?.showCustomMenuLinks ? false : true,
tabBarItemHidden: !settings?.showCustomMenuLinks,
tabBarIcon:
Platform.OS == "android"
Platform.OS === "android"
? ({ focused }) => require("@/assets/icons/list.png")
: ({ focused }) =>
focused

View File

@@ -16,7 +16,12 @@ import {
BACKGROUND_FETCH_TASK_SESSIONS,
registerBackgroundFetchAsyncSessions,
} from "@/utils/background-tasks";
import {LogProvider, writeDebugLog, writeErrorLog, writeToLog} from "@/utils/log";
import {
LogProvider,
writeDebugLog,
writeErrorLog,
writeToLog,
} from "@/utils/log";
import { storage } from "@/utils/mmkv";
import { cancelJobById, getAllJobsByDeviceId } from "@/utils/optimize-server";
import { ActionSheetProvider } from "@expo/react-native-action-sheet";
@@ -31,8 +36,8 @@ import { QueryClient, QueryClientProvider } from "@tanstack/react-query";
const BackgroundFetch = !Platform.isTV
? require("expo-background-fetch")
: null;
import * as FileSystem from "expo-file-system";
import * as Device from "expo-device";
import * as FileSystem from "expo-file-system";
const Notifications = !Platform.isTV ? require("expo-notifications") : null;
import * as ScreenOrientation from "@/packages/expo-screen-orientation";
import { Stack, router, useSegments } from "expo-router";
@@ -161,7 +166,7 @@ if (!Platform.isTV) {
for (const job of jobs) {
if (job.status === "completed") {
const downloadUrl = url + "download/" + job.id;
const downloadUrl = `${url}download/${job.id}`;
const tasks = await BackGroundDownloader.checkForExistingDownloads();
if (tasks.find((task: { id: string }) => task.id === job.id)) {
@@ -194,7 +199,7 @@ if (!Platform.isTV) {
title: job.item.Name,
body: "Download completed",
data: {
url: `/downloads`,
url: "/downloads",
},
},
trigger: null,
@@ -208,7 +213,7 @@ if (!Platform.isTV) {
title: job.item.Name,
body: "Download failed",
data: {
url: `/downloads`,
url: "/downloads",
},
},
trigger: null,
@@ -333,7 +338,7 @@ function Layout() {
}
// only create push token for real devices (pointless for emulators)
if(Device.isDevice) {
if (Device.isDevice) {
Notifications?.getExpoPushTokenAsync()
.then((token: ExpoPushToken) => token && setExpoPushToken(token))
.catch((reason: any) => console.log("Failed to get token", reason));
@@ -357,9 +362,12 @@ function Layout() {
Notifications?.addNotificationResponseReceivedListener(
(response: NotificationResponse) => {
// Currently the notifications supported by the plugin will send data for deep links.
const {title, data} = response.notification.request.content;
const { title, data } = response.notification.request.content;
writeDebugLog(`Notification ${title} opened`, response.notification.request.content)
writeDebugLog(
`Notification ${title} opened`,
response.notification.request.content,
);
if (data && Object.keys(data).length > 0) {
const type = data?.type?.toLower?.();
@@ -367,12 +375,12 @@ function Layout() {
switch (type) {
case "movie":
router.push(`/(auth)/(tabs)/home/items/page?id=${itemId}`)
router.push(`/(auth)/(tabs)/home/items/page?id=${itemId}`);
break;
case "episode":
// We just clicked a notification for an individual episode.
if (itemId) {
router.push(`/(auth)/(tabs)/home/items/page?id=${itemId}`)
router.push(`/(auth)/(tabs)/home/items/page?id=${itemId}`);
}
// summarized season notification for multiple episodes. Bring them to series season
else {
@@ -380,10 +388,11 @@ function Layout() {
const seasonIndex = data.seasonIndex;
if (seasonIndex) {
router.push(`/(auth)/(tabs)/home/series/${seriesId}?seasonIndex=${seasonIndex}`)
}
else {
router.push(`/(auth)/(tabs)/home/series/${seriesId}`)
router.push(
`/(auth)/(tabs)/home/series/${seriesId}?seasonIndex=${seasonIndex}`,
);
} else {
router.push(`/(auth)/(tabs)/home/series/${seriesId}`);
}
}
break;

View File

@@ -218,16 +218,14 @@ const Login: React.FC = () => {
<View className='px-4 -mt-20 w-full'>
<View className='flex flex-col space-y-2'>
<Text className='text-2xl font-bold -mb-2'>
<>
{serverName ? (
<>
{t("login.login_to_title") + " "}
<Text className='text-purple-600'>{serverName}</Text>
</>
) : (
t("login.login_title")
)}
</>
{serverName ? (
<>
{`${t("login.login_to_title")} `}
<Text className='text-purple-600'>{serverName}</Text>
</>
) : (
t("login.login_title")
)}
</Text>
<Text className='text-xs text-neutral-400'>
{api.basePath}
@@ -284,7 +282,7 @@ const Login: React.FC = () => {
</View>
</View>
<View className='absolute bottom-0 left-0 w-full px-4 mb-2'></View>
<View className='absolute bottom-0 left-0 w-full px-4 mb-2' />
</View>
</>
) : (