chore: Apply linting rules and add git hok (#611)

Co-authored-by: Fredrik Burmester <fredrik.burmester@gmail.com>
This commit is contained in:
lostb1t
2025-03-16 18:01:12 +01:00
committed by GitHub
parent 2688e1b981
commit 92513e234f
268 changed files with 9197 additions and 8394 deletions

View File

@@ -1,5 +1,5 @@
import { useMemo } from "react";
import { StyleSheet, View, ViewProps } from "react-native";
import { StyleSheet, View, type ViewProps } from "react-native";
const getItemStyle = (index: number, numColumns: number) => {
const alignItems = (() => {
@@ -29,7 +29,7 @@ export const ColumnItem = ({
...rest
}: ColumnItemProps) => {
return (
<View className="flex flex-col mb-2 p-4" style={{ width: "33.3%" }}>
<View className='flex flex-col mb-2 p-4' style={{ width: "33.3%" }}>
<View
className={`
`}

View File

@@ -1,13 +1,13 @@
const DropdownMenu = !Platform.isTV ? require("zeego/dropdown-menu") : null;
import { Platform, TouchableOpacity, View, ViewProps } from "react-native";
import { Text } from "@/components/common/Text";
import DisabledSetting from "@/components/settings/DisabledSetting";
import React, {
PropsWithChildren,
ReactNode,
type PropsWithChildren,
type ReactNode,
useEffect,
useState,
} from "react";
import DisabledSetting from "@/components/settings/DisabledSetting";
import { Platform, TouchableOpacity, View, type ViewProps } from "react-native";
interface Props<T> {
data: T[];
@@ -21,7 +21,7 @@ interface Props<T> {
multiple?: boolean;
}
const Dropdown = <T extends unknown>({
const Dropdown = <T,>({
data,
disabled,
placeholderText,
@@ -47,10 +47,10 @@ const Dropdown = <T extends unknown>({
<DropdownMenu.Root>
<DropdownMenu.Trigger>
{typeof title === "string" ? (
<View className="flex flex-col">
<Text className="opacity-50 mb-1 text-xs">{title}</Text>
<TouchableOpacity className="bg-neutral-900 h-10 rounded-xl border-neutral-800 border px-3 py-2 flex flex-row items-center justify-between">
<Text style={{}} className="" numberOfLines={1}>
<View className='flex flex-col'>
<Text className='opacity-50 mb-1 text-xs'>{title}</Text>
<TouchableOpacity className='bg-neutral-900 h-10 rounded-xl border-neutral-800 border px-3 py-2 flex flex-row items-center justify-between'>
<Text style={{}} className='' numberOfLines={1}>
{selected?.length !== undefined
? selected.map(titleExtractor).join(",")
: placeholderText}
@@ -63,8 +63,8 @@ const Dropdown = <T extends unknown>({
</DropdownMenu.Trigger>
<DropdownMenu.Content
loop={false}
side="bottom"
align="center"
side='bottom'
align='center'
alignOffset={0}
avoidCollisions={true}
collisionPadding={0}
@@ -88,10 +88,10 @@ const Dropdown = <T extends unknown>({
}
return [
...prev.filter(
(p) => keyExtractor(p) !== keyExtractor(item)
(p) => keyExtractor(p) !== keyExtractor(item),
),
];
})
});
}}
>
<DropdownMenu.ItemTitle>
@@ -107,7 +107,7 @@ const Dropdown = <T extends unknown>({
{titleExtractor(item)}
</DropdownMenu.ItemTitle>
</DropdownMenu.Item>
)
),
)}
</DropdownMenu.Content>
</DropdownMenu.Root>

View File

@@ -1,14 +1,14 @@
import { Text } from "@/components/common/Text";
import { Ionicons } from "@expo/vector-icons";
import { BlurView, type BlurViewProps } from "expo-blur";
import { useRouter } from "expo-router";
import {
Platform,
TouchableOpacity,
TouchableOpacityProps,
type TouchableOpacityProps,
View,
ViewProps,
} from "react-native";
import { Text } from "@/components/common/Text";
import { useRouter } from "expo-router";
import { Ionicons } from "@expo/vector-icons";
import { BlurView, BlurViewProps } from "expo-blur";
interface Props extends BlurViewProps {
background?: "blur" | "transparent";
@@ -31,13 +31,13 @@ export const HeaderBackButton: React.FC<Props> = ({
<BlurView
{...props}
intensity={100}
className="overflow-hidden rounded-full p-2"
className='overflow-hidden rounded-full p-2'
>
<Ionicons
className="drop-shadow-2xl"
name="arrow-back"
className='drop-shadow-2xl'
name='arrow-back'
size={24}
color="white"
color='white'
/>
</BlurView>
</TouchableOpacity>
@@ -46,14 +46,14 @@ export const HeaderBackButton: React.FC<Props> = ({
return (
<TouchableOpacity
onPress={() => router.back()}
className=" bg-neutral-800/80 rounded-full p-2"
className=' bg-neutral-800/80 rounded-full p-2'
{...touchableOpacityProps}
>
<Ionicons
className="drop-shadow-2xl"
name="arrow-back"
className='drop-shadow-2xl'
name='arrow-back'
size={24}
color="white"
color='white'
/>
</TouchableOpacity>
);

View File

@@ -1,6 +1,6 @@
import { FlashList, FlashListProps } from "@shopify/flash-list";
import { FlashList, type FlashListProps } from "@shopify/flash-list";
import React, { forwardRef, useImperativeHandle, useRef } from "react";
import { View, ViewStyle } from "react-native";
import { View, type ViewStyle } from "react-native";
import { Text } from "./Text";
type PartialExcept<T, K extends keyof T> = Partial<T> & Pick<T, K>;
@@ -44,7 +44,7 @@ export const HorizontalScroll = forwardRef<
noItemsText,
...props
}: HorizontalScrollProps<T>,
ref: React.ForwardedRef<HorizontalScrollRef>
ref: React.ForwardedRef<HorizontalScrollRef>,
) => {
const flashListRef = useRef<FlashList<T>>(null);
@@ -66,16 +66,16 @@ export const HorizontalScroll = forwardRef<
item: T;
index: number;
}) => (
<View className="mr-2">
<View className='mr-2'>
<React.Fragment>{renderItem(item, index)}</React.Fragment>
</View>
);
if (!data || loading) {
return (
<View className="px-4 mb-2">
<View className="bg-neutral-950 h-24 w-full rounded-md mb-2"></View>
<View className="bg-neutral-950 h-10 w-full rounded-md mb-1"></View>
<View className='px-4 mb-2'>
<View className='bg-neutral-950 h-24 w-full rounded-md mb-2'></View>
<View className='bg-neutral-950 h-10 w-full rounded-md mb-1'></View>
</View>
);
}
@@ -95,8 +95,8 @@ export const HorizontalScroll = forwardRef<
}}
keyExtractor={keyExtractor}
ListEmptyComponent={() => (
<View className="flex-1 justify-center items-center">
<Text className="text-center text-gray-500">
<View className='flex-1 justify-center items-center'>
<Text className='text-center text-gray-500'>
{noItemsText || "No data available"}
</Text>
</View>
@@ -104,5 +104,5 @@ export const HorizontalScroll = forwardRef<
{...props}
/>
);
}
},
);

View File

@@ -1,13 +1,14 @@
import { apiAtom, userAtom } from "@/providers/JellyfinProvider";
import {
import type {
BaseItemDto,
BaseItemDtoQueryResult,
} from "@jellyfin/sdk/lib/generated-client/models";
import { FlashList, FlashListProps } from "@shopify/flash-list";
import { FlashList, type FlashListProps } from "@shopify/flash-list";
import { useInfiniteQuery } from "@tanstack/react-query";
import { t } from "i18next";
import { useAtom } from "jotai";
import React, { useEffect, useMemo } from "react";
import { View, ViewStyle } from "react-native";
import { View, type ViewStyle } from "react-native";
import Animated, {
useAnimatedStyle,
useSharedValue,
@@ -15,7 +16,6 @@ import Animated, {
} from "react-native-reanimated";
import { Loader } from "../Loader";
import { Text } from "./Text";
import { t } from "i18next";
interface HorizontalScrollProps
extends Omit<FlashListProps<BaseItemDto>, "renderItem" | "data" | "style"> {
@@ -70,7 +70,7 @@ export function InfiniteHorizontalScroll({
const totalItems = lastPage.TotalRecordCount;
const accumulatedItems = pages.reduce(
(acc, curr) => acc + (curr?.Items?.length || 0),
0
0,
);
if (accumulatedItems < totalItems) {
@@ -118,7 +118,7 @@ export function InfiniteHorizontalScroll({
<FlashList
data={flatData}
renderItem={({ item, index }) => (
<View className="mr-2">
<View className='mr-2'>
<React.Fragment>{renderItem(item, index)}</React.Fragment>
</View>
)}
@@ -136,8 +136,10 @@ export function InfiniteHorizontalScroll({
}}
showsHorizontalScrollIndicator={false}
ListEmptyComponent={
<View className="flex-1 justify-center items-center">
<Text className="text-center text-gray-500">{t("item_card.no_data_available")}</Text>
<View className='flex-1 justify-center items-center'>
<Text className='text-center text-gray-500'>
{t("item_card.no_data_available")}
</Text>
</View>
}
{...props}

View File

@@ -1,32 +1,35 @@
import React from "react";
import {Platform, TextInput, TextInputProps, TouchableOpacity} from "react-native";
import {
Platform,
TextInput,
type TextInputProps,
TouchableOpacity,
} from "react-native";
export function Input(props: TextInputProps) {
const { style, ...otherProps } = props;
const inputRef = React.useRef<TextInput>(null);
return Platform.isTV ? (
<TouchableOpacity
onFocus={() => inputRef?.current?.focus?.()}
>
<TextInput
ref={inputRef}
className="p-4 rounded-xl bg-neutral-900"
allowFontScaling={false}
style={[{ color: "white" }, style]}
placeholderTextColor={"#9CA3AF"}
clearButtonMode="while-editing"
{...otherProps}
/>
</TouchableOpacity>
<TouchableOpacity onFocus={() => inputRef?.current?.focus?.()}>
<TextInput
ref={inputRef}
className='p-4 rounded-xl bg-neutral-900'
allowFontScaling={false}
style={[{ color: "white" }, style]}
placeholderTextColor={"#9CA3AF"}
clearButtonMode='while-editing'
{...otherProps}
/>
</TouchableOpacity>
) : (
<TextInput
ref={inputRef}
className="p-4 rounded-xl bg-neutral-900"
className='p-4 rounded-xl bg-neutral-900'
allowFontScaling={false}
style={[{ color: "white" }, style]}
placeholderTextColor={"#9CA3AF"}
clearButtonMode="while-editing"
clearButtonMode='while-editing'
{...otherProps}
/>
)
);
}

View File

@@ -1,11 +1,11 @@
import { apiAtom } from "@/providers/JellyfinProvider";
import { getItemImage } from "@/utils/getItemImage";
import { Ionicons } from "@expo/vector-icons";
import { BaseItemDto } from "@jellyfin/sdk/lib/generated-client/models";
import { Image, ImageProps } from "expo-image";
import type { BaseItemDto } from "@jellyfin/sdk/lib/generated-client/models";
import { Image, type ImageProps } from "expo-image";
import { useAtom } from "jotai";
import {FC, useMemo} from "react";
import { View, ViewProps } from "react-native";
import { type FC, useMemo } from "react";
import { View, type ViewProps } from "react-native";
interface Props extends ImageProps {
item: BaseItemDto;
@@ -52,13 +52,13 @@ export const ItemImage: FC<Props> = ({
if (!source?.uri)
return (
<View
{...props as ViewProps}
className="flex flex-col items-center justify-center border border-neutral-800 bg-neutral-900"
{...(props as ViewProps)}
className='flex flex-col items-center justify-center border border-neutral-800 bg-neutral-900'
>
<Ionicons
name="image-outline"
name='image-outline'
size={24}
color="white"
color='white'
style={{ opacity: 0.4 }}
/>
</View>

View File

@@ -1,16 +1,20 @@
import { useRouter, useSegments } from "expo-router";
import React, { PropsWithChildren, useCallback, useMemo } from "react";
import { TouchableOpacity, TouchableOpacityProps } from "react-native";
import * as ContextMenu from "@/components/ContextMenu";
import { MovieResult, TvResult } from "@/utils/jellyseerr/server/models/Search";
import { useJellyseerr } from "@/hooks/useJellyseerr";
import {
hasPermission,
Permission,
} from "@/utils/jellyseerr/server/lib/permissions";
import { MediaType } from "@/utils/jellyseerr/server/constants/media";
import {TvDetails} from "@/utils/jellyseerr/server/models/Tv";
import {MovieDetails} from "@/utils/jellyseerr/server/models/Movie";
import {
Permission,
hasPermission,
} from "@/utils/jellyseerr/server/lib/permissions";
import type { MovieDetails } from "@/utils/jellyseerr/server/models/Movie";
import type {
MovieResult,
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;
@@ -46,16 +50,13 @@ export const TouchableJellyseerrRouter: React.FC<PropsWithChildren<Props>> = ({
);
}, [jellyseerrApi, jellyseerrUser]);
const request = useCallback(
() => {
if (!result) return;
requestMedia(mediaTitle, {
mediaId: result.id,
mediaType,
})
},
[jellyseerrApi, result]
);
const request = useCallback(() => {
if (!result) return;
requestMedia(mediaTitle, {
mediaId: result.id,
mediaType,
});
}, [jellyseerrApi, result]);
if (from === "(home)" || from === "(search)" || from === "(libraries)")
return (
@@ -75,7 +76,7 @@ export const TouchableJellyseerrRouter: React.FC<PropsWithChildren<Props>> = ({
releaseYear,
canRequest,
posterSrc,
mediaType
mediaType,
},
});
}}
@@ -91,10 +92,10 @@ export const TouchableJellyseerrRouter: React.FC<PropsWithChildren<Props>> = ({
loop={false}
key={"content"}
>
<ContextMenu.Label key="label-1">Actions</ContextMenu.Label>
<ContextMenu.Label key='label-1'>Actions</ContextMenu.Label>
{canRequest && mediaType === MediaType.MOVIE && (
<ContextMenu.Item
key="item-1"
key='item-1'
onSelect={() => {
if (autoApprove) {
request();
@@ -102,7 +103,7 @@ export const TouchableJellyseerrRouter: React.FC<PropsWithChildren<Props>> = ({
}}
shouldDismissMenuOnSelect
>
<ContextMenu.ItemTitle key="item-1-title">
<ContextMenu.ItemTitle key='item-1-title'>
Request
</ContextMenu.ItemTitle>
<ContextMenu.ItemIcon
@@ -116,7 +117,7 @@ export const TouchableJellyseerrRouter: React.FC<PropsWithChildren<Props>> = ({
light: "purple",
},
}}
androidIconName="download"
androidIconName='download'
/>
</ContextMenu.Item>
)}

View File

@@ -4,16 +4,16 @@ import { View } from "react-native";
export const LargePoster: React.FC<{ url?: string | null }> = ({ url }) => {
if (!url)
return (
<View className="p-4 rounded-xl overflow-hidden ">
<View className="w-full aspect-video rounded-xl overflow-hidden border border-neutral-800"></View>
<View className='p-4 rounded-xl overflow-hidden '>
<View className='w-full aspect-video rounded-xl overflow-hidden border border-neutral-800'></View>
</View>
);
return (
<View className="p-4 rounded-xl overflow-hidden ">
<View className='p-4 rounded-xl overflow-hidden '>
<Image
source={{ uri: url }}
className="w-full aspect-video rounded-xl overflow-hidden border border-neutral-800"
className='w-full aspect-video rounded-xl overflow-hidden border border-neutral-800'
/>
</View>
);

View File

@@ -1,11 +1,11 @@
import React from "react";
import { Platform, TextProps } from "react-native";
import { UITextView } from "react-native-uitextview";
import { Platform, type TextProps } from "react-native";
import { Text as RNText } from "react-native";
import { UITextView } from "react-native-uitextview";
export function Text(
props: TextProps & {
uiTextView?: boolean;
}
},
) {
const { style, ...otherProps } = props;
if (Platform.isTV)

View File

@@ -1,13 +1,13 @@
import { useMarkAsPlayed } from "@/hooks/useMarkAsPlayed";
import { useFavorite } from "@/hooks/useFavorite";
import {
import { useMarkAsPlayed } from "@/hooks/useMarkAsPlayed";
import { useActionSheet } from "@expo/react-native-action-sheet";
import type {
BaseItemDto,
BaseItemPerson,
} from "@jellyfin/sdk/lib/generated-client/models";
import { useRouter, useSegments } from "expo-router";
import { PropsWithChildren, useCallback } from "react";
import { TouchableOpacity, TouchableOpacityProps } from "react-native";
import { useActionSheet } from "@expo/react-native-action-sheet";
import { type PropsWithChildren, useCallback } from "react";
import { TouchableOpacity, type TouchableOpacityProps } from "react-native";
interface Props extends TouchableOpacityProps {
item: BaseItemDto;
@@ -15,7 +15,7 @@ interface Props extends TouchableOpacityProps {
export const itemRouter = (
item: BaseItemDto | BaseItemPerson,
from: string
from: string,
) => {
if ("CollectionType" in item && item.CollectionType === "livetv") {
return `/(auth)/(tabs)/${from}/livetv`;
@@ -58,12 +58,24 @@ export const TouchableItemRouter: React.FC<PropsWithChildren<Props>> = ({
const { showActionSheetWithOptions } = useActionSheet();
const markAsPlayedStatus = useMarkAsPlayed([item]);
const { isFavorite, toggleFavorite } = useFavorite(item);
const from = segments[2];
const showActionSheet = useCallback(() => {
if (!(item.Type === "Movie" || item.Type === "Episode" || item.Type === "Series")) return;
const options = ["Mark as Played", "Mark as Not Played", isFavorite ? "Unmark as Favorite" : "Mark as Favorite", "Cancel"];
if (
!(
item.Type === "Movie" ||
item.Type === "Episode" ||
item.Type === "Series"
)
)
return;
const options = [
"Mark as Played",
"Mark as Not Played",
isFavorite ? "Unmark as Favorite" : "Mark as Favorite",
"Cancel",
];
const cancelButtonIndex = 3;
showActionSheetWithOptions(
@@ -77,9 +89,9 @@ export const TouchableItemRouter: React.FC<PropsWithChildren<Props>> = ({
} else if (selectedIndex === 1) {
await markAsPlayedStatus(false);
} else if (selectedIndex === 2) {
toggleFavorite()
toggleFavorite();
}
}
},
);
}, [showActionSheetWithOptions, isFavorite, markAsPlayedStatus]);

View File

@@ -1,5 +1,5 @@
import { View, ViewProps } from "react-native";
import { Text } from "@/components/common/Text";
import { View, type ViewProps } from "react-native";
interface Props extends ViewProps {
index: number;
@@ -12,18 +12,18 @@ export const VerticalSkeleton: React.FC<Props> = ({ index, ...props }) => {
style={{
width: "32%",
}}
className="flex flex-col"
className='flex flex-col'
{...props}
>
<View
style={{
aspectRatio: "10/15",
}}
className="w-full bg-neutral-800 mb-2 rounded-lg"
className='w-full bg-neutral-800 mb-2 rounded-lg'
></View>
<View className="h-2 bg-neutral-800 rounded-full mb-1"></View>
<View className="h-2 bg-neutral-800 rounded-full mb-1"></View>
<View className="h-2 bg-neutral-800 rounded-full mb-2 w-1/2"></View>
<View className='h-2 bg-neutral-800 rounded-full mb-1'></View>
<View className='h-2 bg-neutral-800 rounded-full mb-1'></View>
<View className='h-2 bg-neutral-800 rounded-full mb-2 w-1/2'></View>
</View>
);
};