mirror of
https://github.com/streamyfin/streamyfin.git
synced 2026-04-21 00:04:42 +01:00
chore: Apply linting rules and add git hok (#611)
Co-authored-by: Fredrik Burmester <fredrik.burmester@gmail.com>
This commit is contained in:
@@ -1,11 +1,11 @@
|
||||
const DropdownMenu = !Platform.isTV ? require("zeego/dropdown-menu") : null;
|
||||
import { Platform, TouchableOpacity, View, ViewProps } from "react-native";
|
||||
import { Text } from "../common/Text";
|
||||
import { APP_LANGUAGES } from "@/i18n";
|
||||
import { useSettings } from "@/utils/atoms/settings";
|
||||
import { useTranslation } from "react-i18next";
|
||||
import { Platform, TouchableOpacity, View, type ViewProps } from "react-native";
|
||||
import { Text } from "../common/Text";
|
||||
import { ListGroup } from "../list/ListGroup";
|
||||
import { ListItem } from "../list/ListItem";
|
||||
import { useTranslation } from "react-i18next";
|
||||
import { APP_LANGUAGES } from "@/i18n";
|
||||
|
||||
interface Props extends ViewProps {}
|
||||
|
||||
@@ -22,18 +22,18 @@ export const AppLanguageSelector: React.FC<Props> = ({ ...props }) => {
|
||||
<ListItem title={t("home.settings.languages.app_language")}>
|
||||
<DropdownMenu.Root>
|
||||
<DropdownMenu.Trigger>
|
||||
<TouchableOpacity className="bg-neutral-800 rounded-lg border-neutral-900 border px-3 py-2 flex flex-row items-center justify-between">
|
||||
<TouchableOpacity className='bg-neutral-800 rounded-lg border-neutral-900 border px-3 py-2 flex flex-row items-center justify-between'>
|
||||
<Text>
|
||||
{APP_LANGUAGES.find(
|
||||
(l) => l.value === settings?.preferedLanguage
|
||||
(l) => l.value === settings?.preferedLanguage,
|
||||
)?.label || t("home.settings.languages.system")}
|
||||
</Text>
|
||||
</TouchableOpacity>
|
||||
</DropdownMenu.Trigger>
|
||||
<DropdownMenu.Content
|
||||
loop={true}
|
||||
side="bottom"
|
||||
align="start"
|
||||
side='bottom'
|
||||
align='start'
|
||||
alignOffset={0}
|
||||
avoidCollisions={true}
|
||||
collisionPadding={8}
|
||||
|
||||
@@ -1,13 +1,13 @@
|
||||
import { Platform, TouchableOpacity, View, ViewProps } from "react-native";
|
||||
import { Platform, TouchableOpacity, View, type ViewProps } from "react-native";
|
||||
const DropdownMenu = !Platform.isTV ? require("zeego/dropdown-menu") : null;
|
||||
import { Text } from "../common/Text";
|
||||
import { useMedia } from "./MediaContext";
|
||||
import { Switch } from "react-native-gesture-handler";
|
||||
import { useSettings } from "@/utils/atoms/settings";
|
||||
import { Ionicons } from "@expo/vector-icons";
|
||||
import { useTranslation } from "react-i18next";
|
||||
import { Switch } from "react-native-gesture-handler";
|
||||
import { Text } from "../common/Text";
|
||||
import { ListGroup } from "../list/ListGroup";
|
||||
import { ListItem } from "../list/ListItem";
|
||||
import { Ionicons } from "@expo/vector-icons";
|
||||
import { useSettings } from "@/utils/atoms/settings";
|
||||
import { useMedia } from "./MediaContext";
|
||||
|
||||
interface Props extends ViewProps {}
|
||||
|
||||
@@ -26,7 +26,7 @@ export const AudioToggles: React.FC<Props> = ({ ...props }) => {
|
||||
<ListGroup
|
||||
title={t("home.settings.audio.audio_title")}
|
||||
description={
|
||||
<Text className="text-[#8E8D91] text-xs">
|
||||
<Text className='text-[#8E8D91] text-xs'>
|
||||
{t("home.settings.audio.audio_hint")}
|
||||
</Text>
|
||||
}
|
||||
@@ -46,22 +46,22 @@ export const AudioToggles: React.FC<Props> = ({ ...props }) => {
|
||||
<ListItem title={t("home.settings.audio.audio_language")}>
|
||||
<DropdownMenu.Root>
|
||||
<DropdownMenu.Trigger>
|
||||
<TouchableOpacity className="flex flex-row items-center justify-between py-3 pl-3 ">
|
||||
<Text className="mr-1 text-[#8E8D91]">
|
||||
<TouchableOpacity className='flex flex-row items-center justify-between py-3 pl-3 '>
|
||||
<Text className='mr-1 text-[#8E8D91]'>
|
||||
{settings?.defaultAudioLanguage?.DisplayName ||
|
||||
t("home.settings.audio.none")}
|
||||
</Text>
|
||||
<Ionicons
|
||||
name="chevron-expand-sharp"
|
||||
name='chevron-expand-sharp'
|
||||
size={18}
|
||||
color="#5A5960"
|
||||
color='#5A5960'
|
||||
/>
|
||||
</TouchableOpacity>
|
||||
</DropdownMenu.Trigger>
|
||||
<DropdownMenu.Content
|
||||
loop={true}
|
||||
side="bottom"
|
||||
align="start"
|
||||
side='bottom'
|
||||
align='start'
|
||||
alignOffset={0}
|
||||
avoidCollisions={true}
|
||||
collisionPadding={8}
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
import { useSettings } from "@/utils/atoms/settings";
|
||||
import { Switch, View } from "react-native";
|
||||
import { ListGroup } from "../list/ListGroup";
|
||||
import { useSettings } from "@/utils/atoms/settings";
|
||||
import { ListItem } from "../list/ListItem";
|
||||
|
||||
export const ChromecastSettings: React.FC = ({ ...props }) => {
|
||||
|
||||
@@ -1,11 +1,11 @@
|
||||
import { useSessions, type useSessionsProps } from "@/hooks/useSessions";
|
||||
import { useSettings } from "@/utils/atoms/settings";
|
||||
import { useRouter } from "expo-router";
|
||||
import React from "react";
|
||||
import { useTranslation } from "react-i18next";
|
||||
import { View } from "react-native";
|
||||
import { ListGroup } from "../list/ListGroup";
|
||||
import { ListItem } from "../list/ListItem";
|
||||
import { useTranslation } from "react-i18next";
|
||||
import { useSessions, useSessionsProps } from "@/hooks/useSessions";
|
||||
|
||||
export const Dashboard = () => {
|
||||
const [settings, updateSettings] = useSettings();
|
||||
@@ -17,7 +17,7 @@ export const Dashboard = () => {
|
||||
if (!settings) return null;
|
||||
return (
|
||||
<View>
|
||||
<ListGroup title={t("home.settings.dashboard.title")} className="mt-4">
|
||||
<ListGroup title={t("home.settings.dashboard.title")} className='mt-4'>
|
||||
<ListItem
|
||||
className={sessions.length != 0 ? "bg-purple-900" : ""}
|
||||
onPress={() => router.push("/settings/dashboard/sessions")}
|
||||
|
||||
@@ -1,13 +1,9 @@
|
||||
import {View, ViewProps} from "react-native";
|
||||
import {Text} from "@/components/common/Text";
|
||||
import { Text } from "@/components/common/Text";
|
||||
import { View, type ViewProps } from "react-native";
|
||||
|
||||
const DisabledSetting: React.FC<{disabled: boolean, showText?: boolean, text?: string} & ViewProps> = ({
|
||||
disabled = false,
|
||||
showText = true,
|
||||
text,
|
||||
children,
|
||||
...props
|
||||
}) => (
|
||||
const DisabledSetting: React.FC<
|
||||
{ disabled: boolean; showText?: boolean; text?: string } & ViewProps
|
||||
> = ({ disabled = false, showText = true, text, children, ...props }) => (
|
||||
<View
|
||||
pointerEvents={disabled ? "none" : "auto"}
|
||||
style={{
|
||||
@@ -15,12 +11,14 @@ const DisabledSetting: React.FC<{disabled: boolean, showText?: boolean, text?: s
|
||||
}}
|
||||
>
|
||||
<View {...props}>
|
||||
{disabled && showText &&
|
||||
<Text className="text-center text-red-700 my-4">{text ?? "Currently disabled by admin."}</Text>
|
||||
}
|
||||
{disabled && showText && (
|
||||
<Text className='text-center text-red-700 my-4'>
|
||||
{text ?? "Currently disabled by admin."}
|
||||
</Text>
|
||||
)}
|
||||
{children}
|
||||
</View>
|
||||
</View>
|
||||
)
|
||||
);
|
||||
|
||||
export default DisabledSetting;
|
||||
export default DisabledSetting;
|
||||
|
||||
@@ -1,17 +1,21 @@
|
||||
import { Stepper } from "@/components/inputs/Stepper";
|
||||
import { useDownload } from "@/providers/DownloadProvider";
|
||||
import { DownloadMethod, Settings, useSettings } from "@/utils/atoms/settings";
|
||||
import {
|
||||
DownloadMethod,
|
||||
type Settings,
|
||||
useSettings,
|
||||
} from "@/utils/atoms/settings";
|
||||
import { Ionicons } from "@expo/vector-icons";
|
||||
import { useQueryClient } from "@tanstack/react-query";
|
||||
import { useRouter } from "expo-router";
|
||||
import React, { useMemo } from "react";
|
||||
import { Platform, Switch, TouchableOpacity } from "react-native";
|
||||
const DropdownMenu = !Platform.isTV ? require("zeego/dropdown-menu") : null;
|
||||
import DisabledSetting from "@/components/settings/DisabledSetting";
|
||||
import { useTranslation } from "react-i18next";
|
||||
import { Text } from "../common/Text";
|
||||
import { ListGroup } from "../list/ListGroup";
|
||||
import { ListItem } from "../list/ListItem";
|
||||
import { useTranslation } from "react-i18next";
|
||||
import DisabledSetting from "@/components/settings/DisabledSetting";
|
||||
|
||||
export default function DownloadSettings({ ...props }) {
|
||||
const [settings, updateSettings, pluginSettings] = useSettings();
|
||||
@@ -25,13 +29,13 @@ export default function DownloadSettings({ ...props }) {
|
||||
pluginSettings?.downloadMethod?.locked === true &&
|
||||
pluginSettings?.remuxConcurrentLimit?.locked === true &&
|
||||
pluginSettings?.autoDownload.locked === true,
|
||||
[pluginSettings]
|
||||
[pluginSettings],
|
||||
);
|
||||
|
||||
if (!settings) return null;
|
||||
|
||||
return (
|
||||
<DisabledSetting disabled={allDisabled} {...props} className="mb-4">
|
||||
<DisabledSetting disabled={allDisabled} {...props} className='mb-4'>
|
||||
<ListGroup title={t("home.settings.downloads.downloads_title")}>
|
||||
<ListItem
|
||||
title={t("home.settings.downloads.download_method")}
|
||||
@@ -39,23 +43,23 @@ export default function DownloadSettings({ ...props }) {
|
||||
>
|
||||
<DropdownMenu.Root>
|
||||
<DropdownMenu.Trigger>
|
||||
<TouchableOpacity className="flex flex-row items-center justify-between py-3 pl-3">
|
||||
<Text className="mr-1 text-[#8E8D91]">
|
||||
<TouchableOpacity className='flex flex-row items-center justify-between py-3 pl-3'>
|
||||
<Text className='mr-1 text-[#8E8D91]'>
|
||||
{settings.downloadMethod === DownloadMethod.Remux
|
||||
? t("home.settings.downloads.default")
|
||||
: t("home.settings.downloads.optimized")}
|
||||
</Text>
|
||||
<Ionicons
|
||||
name="chevron-expand-sharp"
|
||||
name='chevron-expand-sharp'
|
||||
size={18}
|
||||
color="#5A5960"
|
||||
color='#5A5960'
|
||||
/>
|
||||
</TouchableOpacity>
|
||||
</DropdownMenu.Trigger>
|
||||
<DropdownMenu.Content
|
||||
loop={true}
|
||||
side="bottom"
|
||||
align="start"
|
||||
side='bottom'
|
||||
align='start'
|
||||
alignOffset={0}
|
||||
avoidCollisions={true}
|
||||
collisionPadding={8}
|
||||
@@ -65,7 +69,7 @@ export default function DownloadSettings({ ...props }) {
|
||||
{t("home.settings.downloads.download_method")}
|
||||
</DropdownMenu.Label>
|
||||
<DropdownMenu.Item
|
||||
key="1"
|
||||
key='1'
|
||||
onSelect={() => {
|
||||
updateSettings({ downloadMethod: DownloadMethod.Remux });
|
||||
setProcesses([]);
|
||||
@@ -76,7 +80,7 @@ export default function DownloadSettings({ ...props }) {
|
||||
</DropdownMenu.ItemTitle>
|
||||
</DropdownMenu.Item>
|
||||
<DropdownMenu.Item
|
||||
key="2"
|
||||
key='2'
|
||||
onSelect={() => {
|
||||
updateSettings({ downloadMethod: DownloadMethod.Optimized });
|
||||
setProcesses([]);
|
||||
|
||||
@@ -1,8 +1,8 @@
|
||||
import { Button } from "@/components/Button";
|
||||
import { Loader } from "@/components/Loader";
|
||||
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 { Colors } from "@/constants/Colors";
|
||||
import { useInvalidatePlaybackProgressCache } from "@/hooks/useRevalidatePlaybackProgressCache";
|
||||
@@ -11,8 +11,8 @@ import { apiAtom, userAtom } from "@/providers/JellyfinProvider";
|
||||
import { useSettings } from "@/utils/atoms/settings";
|
||||
import { eventBus } from "@/utils/eventBus";
|
||||
import { Feather, Ionicons } from "@expo/vector-icons";
|
||||
import { Api } from "@jellyfin/sdk";
|
||||
import {
|
||||
import type { Api } from "@jellyfin/sdk";
|
||||
import type {
|
||||
BaseItemDto,
|
||||
BaseItemKind,
|
||||
} from "@jellyfin/sdk/lib/generated-client/models";
|
||||
@@ -24,7 +24,7 @@ import {
|
||||
getUserViewsApi,
|
||||
} from "@jellyfin/sdk/lib/utils/api";
|
||||
import NetInfo from "@react-native-community/netinfo";
|
||||
import { QueryFunction, useQuery } from "@tanstack/react-query";
|
||||
import { type QueryFunction, useQuery } from "@tanstack/react-query";
|
||||
import {
|
||||
useNavigation,
|
||||
usePathname,
|
||||
@@ -94,10 +94,10 @@ export const HomeIndex = () => {
|
||||
onPress={() => {
|
||||
router.push("/(auth)/downloads");
|
||||
}}
|
||||
className="p-2"
|
||||
className='p-2'
|
||||
>
|
||||
<Feather
|
||||
name="download"
|
||||
name='download'
|
||||
color={hasDownloads ? Colors.primary : "white"}
|
||||
size={22}
|
||||
/>
|
||||
@@ -108,7 +108,7 @@ export const HomeIndex = () => {
|
||||
|
||||
useEffect(() => {
|
||||
cleanCacheDirectory().catch((e) =>
|
||||
console.error("Something went wrong cleaning cache directory")
|
||||
console.error("Something went wrong cleaning cache directory"),
|
||||
);
|
||||
}, []);
|
||||
|
||||
@@ -174,14 +174,14 @@ export const HomeIndex = () => {
|
||||
|
||||
const userViews = useMemo(
|
||||
() => data?.filter((l) => !settings?.hiddenLibraries?.includes(l.Id!)),
|
||||
[data, settings?.hiddenLibraries]
|
||||
[data, settings?.hiddenLibraries],
|
||||
);
|
||||
|
||||
const collections = useMemo(() => {
|
||||
const allow = ["movies", "tvshows"];
|
||||
return (
|
||||
userViews?.filter(
|
||||
(c) => c.CollectionType && allow.includes(c.CollectionType)
|
||||
(c) => c.CollectionType && allow.includes(c.CollectionType),
|
||||
) || []
|
||||
);
|
||||
}, [userViews]);
|
||||
@@ -194,13 +194,13 @@ export const HomeIndex = () => {
|
||||
await invalidateCache();
|
||||
setLoading(false);
|
||||
};
|
||||
|
||||
|
||||
const createCollectionConfig = useCallback(
|
||||
(
|
||||
title: string,
|
||||
queryKey: string[],
|
||||
includeItemTypes: BaseItemKind[],
|
||||
parentId: string | undefined
|
||||
parentId: string | undefined,
|
||||
): ScrollingCollectionListSection => ({
|
||||
title,
|
||||
queryKey,
|
||||
@@ -222,7 +222,7 @@ export const HomeIndex = () => {
|
||||
},
|
||||
type: "ScrollingCollectionList",
|
||||
}),
|
||||
[api, user?.Id]
|
||||
[api, user?.Id],
|
||||
);
|
||||
|
||||
let sections: Section[] = [];
|
||||
@@ -244,7 +244,7 @@ export const HomeIndex = () => {
|
||||
title || "",
|
||||
queryKey,
|
||||
includeItemTypes,
|
||||
c.Id
|
||||
c.Id,
|
||||
);
|
||||
});
|
||||
|
||||
@@ -312,7 +312,7 @@ export const HomeIndex = () => {
|
||||
try {
|
||||
const suggestions = await getSuggestions(api, user.Id);
|
||||
const nextUpPromises = suggestions.map((series) =>
|
||||
getNextUp(api, user.Id, series.Id)
|
||||
getNextUp(api, user.Id, series.Id),
|
||||
);
|
||||
const nextUpResults = await Promise.all(nextUpPromises);
|
||||
|
||||
@@ -376,32 +376,32 @@ export const HomeIndex = () => {
|
||||
|
||||
if (isConnected === false) {
|
||||
return (
|
||||
<View className="flex flex-col items-center justify-center h-full -mt-6 px-8">
|
||||
<Text className="text-3xl font-bold mb-2">{t("home.no_internet")}</Text>
|
||||
<Text className="text-center opacity-70">
|
||||
<View className='flex flex-col items-center justify-center h-full -mt-6 px-8'>
|
||||
<Text className='text-3xl font-bold mb-2'>{t("home.no_internet")}</Text>
|
||||
<Text className='text-center opacity-70'>
|
||||
{t("home.no_internet_message")}
|
||||
</Text>
|
||||
<View className="mt-4">
|
||||
<View className='mt-4'>
|
||||
<Button
|
||||
color="purple"
|
||||
color='purple'
|
||||
onPress={() => router.push("/(auth)/downloads")}
|
||||
justify="center"
|
||||
justify='center'
|
||||
iconRight={
|
||||
<Ionicons name="arrow-forward" size={20} color="white" />
|
||||
<Ionicons name='arrow-forward' size={20} color='white' />
|
||||
}
|
||||
>
|
||||
{t("home.go_to_downloads")}
|
||||
</Button>
|
||||
<Button
|
||||
color="black"
|
||||
color='black'
|
||||
onPress={() => {
|
||||
checkConnection();
|
||||
}}
|
||||
justify="center"
|
||||
className="mt-2"
|
||||
justify='center'
|
||||
className='mt-2'
|
||||
iconRight={
|
||||
loadingRetry ? null : (
|
||||
<Ionicons name="refresh" size={20} color="white" />
|
||||
<Ionicons name='refresh' size={20} color='white' />
|
||||
)
|
||||
}
|
||||
>
|
||||
@@ -418,9 +418,9 @@ export const HomeIndex = () => {
|
||||
|
||||
if (e1)
|
||||
return (
|
||||
<View className="flex flex-col items-center justify-center h-full -mt-6">
|
||||
<Text className="text-3xl font-bold mb-2">{t("home.oops")}</Text>
|
||||
<Text className="text-center opacity-70">
|
||||
<View className='flex flex-col items-center justify-center h-full -mt-6'>
|
||||
<Text className='text-3xl font-bold mb-2'>{t("home.oops")}</Text>
|
||||
<Text className='text-center opacity-70'>
|
||||
{t("home.error_message")}
|
||||
</Text>
|
||||
</View>
|
||||
@@ -428,7 +428,7 @@ export const HomeIndex = () => {
|
||||
|
||||
if (l1)
|
||||
return (
|
||||
<View className="justify-center items-center h-full">
|
||||
<View className='justify-center items-center h-full'>
|
||||
<Loader />
|
||||
</View>
|
||||
);
|
||||
@@ -438,7 +438,7 @@ export const HomeIndex = () => {
|
||||
scrollToOverflowEnabled={true}
|
||||
ref={scrollViewRef}
|
||||
nestedScrollEnabled
|
||||
contentInsetAdjustmentBehavior="automatic"
|
||||
contentInsetAdjustmentBehavior='automatic'
|
||||
refreshControl={
|
||||
<RefreshControl refreshing={loading} onRefresh={refetch} />
|
||||
}
|
||||
@@ -448,7 +448,7 @@ export const HomeIndex = () => {
|
||||
paddingBottom: 16,
|
||||
}}
|
||||
>
|
||||
<View className="flex flex-col space-y-4">
|
||||
<View className='flex flex-col space-y-4'>
|
||||
<LargeMovieCarousel />
|
||||
|
||||
{sections.map((section, index) => {
|
||||
@@ -495,7 +495,7 @@ async function getSuggestions(api: Api, userId: string | undefined) {
|
||||
async function getNextUp(
|
||||
api: Api,
|
||||
userId: string | undefined,
|
||||
seriesId: string | undefined
|
||||
seriesId: string | undefined,
|
||||
) {
|
||||
if (!userId || !seriesId) return null;
|
||||
const response = await getTvShowsApi(api).getNextUp({
|
||||
|
||||
@@ -2,9 +2,9 @@ import { JellyseerrApi, useJellyseerr } from "@/hooks/useJellyseerr";
|
||||
import { userAtom } from "@/providers/JellyfinProvider";
|
||||
import { useSettings } from "@/utils/atoms/settings";
|
||||
import { useMutation } from "@tanstack/react-query";
|
||||
import { useTranslation } from "react-i18next";
|
||||
import { useAtom } from "jotai";
|
||||
import { useState } from "react";
|
||||
import { useTranslation } from "react-i18next";
|
||||
import { View } from "react-native";
|
||||
import { toast } from "sonner-native";
|
||||
import { Button } from "../Button";
|
||||
@@ -41,7 +41,7 @@ export const JellyseerrSettings = () => {
|
||||
if (!user?.Name)
|
||||
throw new Error("Missing required information for login");
|
||||
const jellyseerrTempApi = new JellyseerrApi(
|
||||
jellyseerrServerUrl || settings.jellyseerrServerUrl || ""
|
||||
jellyseerrServerUrl || settings.jellyseerrServerUrl || "",
|
||||
);
|
||||
const testResult = await jellyseerrTempApi.test();
|
||||
if (!testResult.isValid) throw new Error("Invalid server url");
|
||||
@@ -68,14 +68,14 @@ export const JellyseerrSettings = () => {
|
||||
};
|
||||
|
||||
return (
|
||||
<View className="">
|
||||
<View className=''>
|
||||
<View>
|
||||
{jellyseerrUser ? (
|
||||
<>
|
||||
<ListGroup title={"Jellyseerr"}>
|
||||
<ListItem
|
||||
title={t(
|
||||
"home.settings.plugins.jellyseerr.total_media_requests"
|
||||
"home.settings.plugins.jellyseerr.total_media_requests",
|
||||
)}
|
||||
value={jellyseerrUser?.requestCount?.toString()}
|
||||
/>
|
||||
@@ -109,69 +109,69 @@ export const JellyseerrSettings = () => {
|
||||
/>
|
||||
</ListGroup>
|
||||
|
||||
<View className="p-4">
|
||||
<Button color="red" onPress={clearData}>
|
||||
<View className='p-4'>
|
||||
<Button color='red' onPress={clearData}>
|
||||
{t(
|
||||
"home.settings.plugins.jellyseerr.reset_jellyseerr_config_button"
|
||||
"home.settings.plugins.jellyseerr.reset_jellyseerr_config_button",
|
||||
)}
|
||||
</Button>
|
||||
</View>
|
||||
</>
|
||||
) : (
|
||||
<View className="flex flex-col rounded-xl overflow-hidden p-4 bg-neutral-900">
|
||||
<Text className="text-xs text-red-600 mb-2">
|
||||
<View className='flex flex-col rounded-xl overflow-hidden p-4 bg-neutral-900'>
|
||||
<Text className='text-xs text-red-600 mb-2'>
|
||||
{t("home.settings.plugins.jellyseerr.jellyseerr_warning")}
|
||||
</Text>
|
||||
<Text className="font-bold mb-1">
|
||||
<Text className='font-bold mb-1'>
|
||||
{t("home.settings.plugins.jellyseerr.server_url")}
|
||||
</Text>
|
||||
<View className="flex flex-col shrink mb-2">
|
||||
<Text className="text-xs text-gray-600">
|
||||
<View className='flex flex-col shrink mb-2'>
|
||||
<Text className='text-xs text-gray-600'>
|
||||
{t("home.settings.plugins.jellyseerr.server_url_hint")}
|
||||
</Text>
|
||||
</View>
|
||||
<Input
|
||||
className="border border-neutral-800 mb-2"
|
||||
className='border border-neutral-800 mb-2'
|
||||
placeholder={t(
|
||||
"home.settings.plugins.jellyseerr.server_url_placeholder"
|
||||
"home.settings.plugins.jellyseerr.server_url_placeholder",
|
||||
)}
|
||||
value={jellyseerrServerUrl ?? settings?.jellyseerrServerUrl}
|
||||
defaultValue={
|
||||
settings?.jellyseerrServerUrl ?? jellyseerrServerUrl
|
||||
}
|
||||
keyboardType="url"
|
||||
returnKeyType="done"
|
||||
autoCapitalize="none"
|
||||
textContentType="URL"
|
||||
keyboardType='url'
|
||||
returnKeyType='done'
|
||||
autoCapitalize='none'
|
||||
textContentType='URL'
|
||||
onChangeText={setjellyseerrServerUrl}
|
||||
editable={!loginToJellyseerrMutation.isPending}
|
||||
/>
|
||||
<View>
|
||||
<Text className="font-bold mb-2">
|
||||
<Text className='font-bold mb-2'>
|
||||
{t("home.settings.plugins.jellyseerr.password")}
|
||||
</Text>
|
||||
<Input
|
||||
className="border border-neutral-800"
|
||||
className='border border-neutral-800'
|
||||
autoFocus={true}
|
||||
focusable={true}
|
||||
placeholder={t(
|
||||
"home.settings.plugins.jellyseerr.password_placeholder",
|
||||
{ username: user?.Name }
|
||||
{ username: user?.Name },
|
||||
)}
|
||||
value={jellyseerrPassword}
|
||||
keyboardType="default"
|
||||
keyboardType='default'
|
||||
secureTextEntry={true}
|
||||
returnKeyType="done"
|
||||
autoCapitalize="none"
|
||||
textContentType="password"
|
||||
returnKeyType='done'
|
||||
autoCapitalize='none'
|
||||
textContentType='password'
|
||||
onChangeText={setJellyseerrPassword}
|
||||
editable={!loginToJellyseerrMutation.isPending}
|
||||
/>
|
||||
<Button
|
||||
loading={loginToJellyseerrMutation.isPending}
|
||||
disabled={loginToJellyseerrMutation.isPending}
|
||||
color="purple"
|
||||
className="h-12 mt-2"
|
||||
color='purple'
|
||||
className='h-12 mt-2'
|
||||
onPress={() => loginToJellyseerrMutation.mutate()}
|
||||
>
|
||||
{t("home.settings.plugins.jellyseerr.login_button")}
|
||||
|
||||
@@ -1,20 +1,20 @@
|
||||
import { Settings, useSettings } from "@/utils/atoms/settings";
|
||||
import { apiAtom } from "@/providers/JellyfinProvider";
|
||||
import { type Settings, useSettings } from "@/utils/atoms/settings";
|
||||
import type {
|
||||
CultureDto,
|
||||
UserConfiguration,
|
||||
UserDto,
|
||||
} from "@jellyfin/sdk/lib/generated-client/models";
|
||||
import { getLocalizationApi, getUserApi } from "@jellyfin/sdk/lib/utils/api";
|
||||
import { useQuery, useQueryClient } from "@tanstack/react-query";
|
||||
import { useAtomValue } from "jotai";
|
||||
import React, {
|
||||
createContext,
|
||||
useContext,
|
||||
ReactNode,
|
||||
type ReactNode,
|
||||
useEffect,
|
||||
useState,
|
||||
} from "react";
|
||||
import { apiAtom } from "@/providers/JellyfinProvider";
|
||||
import { getLocalizationApi, getUserApi } from "@jellyfin/sdk/lib/utils/api";
|
||||
import {
|
||||
CultureDto,
|
||||
UserDto,
|
||||
UserConfiguration,
|
||||
} from "@jellyfin/sdk/lib/generated-client/models";
|
||||
import { useQuery, useQueryClient } from "@tanstack/react-query";
|
||||
|
||||
interface MediaContextType {
|
||||
settings: Settings | null;
|
||||
@@ -40,7 +40,7 @@ export const MediaProvider = ({ children }: { children: ReactNode }) => {
|
||||
|
||||
const updateSetingsWrapper = (update: Partial<Settings>) => {
|
||||
const updateUserConfiguration = async (
|
||||
update: Partial<UserConfiguration>
|
||||
update: Partial<UserConfiguration>,
|
||||
) => {
|
||||
if (api && user) {
|
||||
try {
|
||||
@@ -57,7 +57,7 @@ export const MediaProvider = ({ children }: { children: ReactNode }) => {
|
||||
|
||||
updateSettings(update);
|
||||
|
||||
let updatePayload = {
|
||||
const updatePayload = {
|
||||
SubtitleMode: update?.subtitleMode ?? settings?.subtitleMode,
|
||||
PlayDefaultAudioTrack:
|
||||
update?.playDefaultAudioTrack ?? settings?.playDefaultAudioTrack,
|
||||
@@ -116,10 +116,10 @@ export const MediaProvider = ({ children }: { children: ReactNode }) => {
|
||||
const userAudioPreference = user?.Configuration?.AudioLanguagePreference;
|
||||
|
||||
const subtitlePreference = cultures.find(
|
||||
(x) => x.ThreeLetterISOLanguageName === userSubtitlePreference
|
||||
(x) => x.ThreeLetterISOLanguageName === userSubtitlePreference,
|
||||
);
|
||||
const audioPreference = cultures.find(
|
||||
(x) => x.ThreeLetterISOLanguageName === userAudioPreference
|
||||
(x) => x.ThreeLetterISOLanguageName === userAudioPreference,
|
||||
);
|
||||
|
||||
updateSettings({
|
||||
|
||||
@@ -1,11 +1,12 @@
|
||||
import React, {useMemo} from "react";
|
||||
import { ViewProps } from "react-native";
|
||||
import { Stepper } from "@/components/inputs/Stepper";
|
||||
import DisabledSetting from "@/components/settings/DisabledSetting";
|
||||
import { useSettings } from "@/utils/atoms/settings";
|
||||
import type React from "react";
|
||||
import { useMemo } from "react";
|
||||
import { useTranslation } from "react-i18next";
|
||||
import type { ViewProps } from "react-native";
|
||||
import { ListGroup } from "../list/ListGroup";
|
||||
import { ListItem } from "../list/ListItem";
|
||||
import { useTranslation } from "react-i18next";
|
||||
import DisabledSetting from "@/components/settings/DisabledSetting";
|
||||
import {Stepper} from "@/components/inputs/Stepper";
|
||||
|
||||
interface Props extends ViewProps {}
|
||||
|
||||
@@ -16,18 +17,15 @@ export const MediaToggles: React.FC<Props> = ({ ...props }) => {
|
||||
|
||||
if (!settings) return null;
|
||||
|
||||
const disabled = useMemo(() => (
|
||||
const disabled = useMemo(
|
||||
() =>
|
||||
pluginSettings?.forwardSkipTime?.locked === true &&
|
||||
pluginSettings?.rewindSkipTime?.locked === true
|
||||
),
|
||||
[pluginSettings]
|
||||
)
|
||||
pluginSettings?.rewindSkipTime?.locked === true,
|
||||
[pluginSettings],
|
||||
);
|
||||
|
||||
return (
|
||||
<DisabledSetting
|
||||
disabled={disabled}
|
||||
{...props}
|
||||
>
|
||||
<DisabledSetting disabled={disabled} {...props}>
|
||||
<ListGroup title={t("home.settings.media_controls.media_controls_title")}>
|
||||
<ListItem
|
||||
title={t("home.settings.media_controls.forward_skip_length")}
|
||||
@@ -40,7 +38,7 @@ export const MediaToggles: React.FC<Props> = ({ ...props }) => {
|
||||
appendValue={t("home.settings.media_controls.seconds_unit")}
|
||||
min={0}
|
||||
max={60}
|
||||
onUpdate={(forwardSkipTime) => updateSettings({forwardSkipTime})}
|
||||
onUpdate={(forwardSkipTime) => updateSettings({ forwardSkipTime })}
|
||||
/>
|
||||
</ListItem>
|
||||
|
||||
@@ -55,7 +53,7 @@ export const MediaToggles: React.FC<Props> = ({ ...props }) => {
|
||||
appendValue={t("home.settings.media_controls.seconds_unit")}
|
||||
min={0}
|
||||
max={60}
|
||||
onUpdate={(rewindSkipTime) => updateSettings({rewindSkipTime})}
|
||||
onUpdate={(rewindSkipTime) => updateSettings({ rewindSkipTime })}
|
||||
/>
|
||||
</ListItem>
|
||||
</ListGroup>
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
import { TextInput, View, Linking } from "react-native";
|
||||
import { Text } from "../common/Text";
|
||||
import { useTranslation } from "react-i18next";
|
||||
import { Linking, TextInput, View } from "react-native";
|
||||
import { Text } from "../common/Text";
|
||||
|
||||
interface Props {
|
||||
value: string;
|
||||
@@ -19,24 +19,24 @@ export const OptimizedServerForm: React.FC<Props> = ({
|
||||
|
||||
return (
|
||||
<View>
|
||||
<View className="flex flex-col rounded-xl overflow-hidden pl-4 bg-neutral-900 px-4">
|
||||
<View className='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`}>
|
||||
<Text className="mr-4">{t("home.settings.downloads.url")}</Text>
|
||||
<Text className='mr-4'>{t("home.settings.downloads.url")}</Text>
|
||||
<TextInput
|
||||
className="text-white"
|
||||
className='text-white'
|
||||
placeholder={t("home.settings.downloads.server_url_placeholder")}
|
||||
value={value}
|
||||
keyboardType="url"
|
||||
returnKeyType="done"
|
||||
autoCapitalize="none"
|
||||
textContentType="URL"
|
||||
keyboardType='url'
|
||||
returnKeyType='done'
|
||||
autoCapitalize='none'
|
||||
textContentType='URL'
|
||||
onChangeText={(text) => onChangeValue(text)}
|
||||
/>
|
||||
</View>
|
||||
</View>
|
||||
<Text className="px-4 text-xs text-neutral-500 mt-1">
|
||||
<Text className='px-4 text-xs text-neutral-500 mt-1'>
|
||||
{t("home.settings.downloads.optimized_version_hint")}{" "}
|
||||
<Text className="text-blue-500" onPress={handleOpenLink}>
|
||||
<Text className='text-blue-500' onPress={handleOpenLink}>
|
||||
{t("home.settings.downloads.read_more_about_optimized_server")}
|
||||
</Text>
|
||||
</Text>
|
||||
|
||||
@@ -10,14 +10,17 @@ import {
|
||||
} from "@/utils/background-tasks";
|
||||
import { Ionicons } from "@expo/vector-icons";
|
||||
import { useRouter } from "expo-router";
|
||||
import React, { useEffect, useMemo } from "react";
|
||||
import type React from "react";
|
||||
import { useEffect, useMemo } from "react";
|
||||
import { useTranslation } from "react-i18next";
|
||||
import { Linking, Platform, Switch, TouchableOpacity } from "react-native";
|
||||
import { toast } from "sonner-native";
|
||||
import { Text } from "../common/Text";
|
||||
import { ListGroup } from "../list/ListGroup";
|
||||
import { ListItem } from "../list/ListItem";
|
||||
const BackgroundFetch = !Platform.isTV ? require("expo-background-fetch") : null;
|
||||
const BackgroundFetch = !Platform.isTV
|
||||
? require("expo-background-fetch")
|
||||
: null;
|
||||
const TaskManager = !Platform.isTV ? require("expo-task-manager") : null;
|
||||
|
||||
export const OtherSettings: React.FC = () => {
|
||||
@@ -66,7 +69,7 @@ export const OtherSettings: React.FC = () => {
|
||||
pluginSettings?.showCustomMenuLinks?.locked === true &&
|
||||
pluginSettings?.hiddenLibraries?.locked === true &&
|
||||
pluginSettings?.disableHapticFeedback?.locked === true,
|
||||
[pluginSettings]
|
||||
[pluginSettings],
|
||||
);
|
||||
|
||||
const orientations = [
|
||||
@@ -78,19 +81,23 @@ export const OtherSettings: React.FC = () => {
|
||||
|
||||
const orientationTranslations = useMemo(
|
||||
() => ({
|
||||
[ScreenOrientation.OrientationLock.DEFAULT]: "home.settings.other.orientations.DEFAULT",
|
||||
[ScreenOrientation.OrientationLock.PORTRAIT_UP]: "home.settings.other.orientations.PORTRAIT_UP",
|
||||
[ScreenOrientation.OrientationLock.LANDSCAPE_LEFT]: "home.settings.other.orientations.LANDSCAPE_LEFT",
|
||||
[ScreenOrientation.OrientationLock.LANDSCAPE_RIGHT]: "home.settings.other.orientations.LANDSCAPE_RIGHT",
|
||||
[ScreenOrientation.OrientationLock.DEFAULT]:
|
||||
"home.settings.other.orientations.DEFAULT",
|
||||
[ScreenOrientation.OrientationLock.PORTRAIT_UP]:
|
||||
"home.settings.other.orientations.PORTRAIT_UP",
|
||||
[ScreenOrientation.OrientationLock.LANDSCAPE_LEFT]:
|
||||
"home.settings.other.orientations.LANDSCAPE_LEFT",
|
||||
[ScreenOrientation.OrientationLock.LANDSCAPE_RIGHT]:
|
||||
"home.settings.other.orientations.LANDSCAPE_RIGHT",
|
||||
}),
|
||||
[]
|
||||
[],
|
||||
);
|
||||
|
||||
if (!settings) return null;
|
||||
|
||||
return (
|
||||
<DisabledSetting disabled={disabled}>
|
||||
<ListGroup title={t("home.settings.other.other_title")} className="">
|
||||
<ListGroup title={t("home.settings.other.other_title")} className=''>
|
||||
<ListItem
|
||||
title={t("home.settings.other.follow_device_orientation")}
|
||||
disabled={pluginSettings?.followDeviceOrientation?.locked}
|
||||
@@ -98,31 +105,47 @@ export const OtherSettings: React.FC = () => {
|
||||
<Switch
|
||||
value={settings.followDeviceOrientation}
|
||||
disabled={pluginSettings?.followDeviceOrientation?.locked}
|
||||
onValueChange={(value) => updateSettings({ followDeviceOrientation: value })}
|
||||
onValueChange={(value) =>
|
||||
updateSettings({ followDeviceOrientation: value })
|
||||
}
|
||||
/>
|
||||
</ListItem>
|
||||
|
||||
<ListItem
|
||||
title={t("home.settings.other.video_orientation")}
|
||||
disabled={pluginSettings?.defaultVideoOrientation?.locked || settings.followDeviceOrientation}
|
||||
disabled={
|
||||
pluginSettings?.defaultVideoOrientation?.locked ||
|
||||
settings.followDeviceOrientation
|
||||
}
|
||||
>
|
||||
<Dropdown
|
||||
data={orientations}
|
||||
disabled={pluginSettings?.defaultVideoOrientation?.locked || settings.followDeviceOrientation}
|
||||
disabled={
|
||||
pluginSettings?.defaultVideoOrientation?.locked ||
|
||||
settings.followDeviceOrientation
|
||||
}
|
||||
keyExtractor={String}
|
||||
titleExtractor={(item) => t(ScreenOrientationEnum[item])}
|
||||
title={
|
||||
<TouchableOpacity className="flex flex-row items-center justify-between py-3 pl-3">
|
||||
<Text className="mr-1 text-[#8E8D91]">
|
||||
<TouchableOpacity className='flex flex-row items-center justify-between py-3 pl-3'>
|
||||
<Text className='mr-1 text-[#8E8D91]'>
|
||||
{t(
|
||||
orientationTranslations[settings.defaultVideoOrientation as keyof typeof orientationTranslations]
|
||||
orientationTranslations[
|
||||
settings.defaultVideoOrientation as keyof typeof orientationTranslations
|
||||
],
|
||||
) || "Unknown Orientation"}
|
||||
</Text>
|
||||
<Ionicons name="chevron-expand-sharp" size={18} color="#5A5960" />
|
||||
<Ionicons
|
||||
name='chevron-expand-sharp'
|
||||
size={18}
|
||||
color='#5A5960'
|
||||
/>
|
||||
</TouchableOpacity>
|
||||
}
|
||||
label={t("home.settings.other.orientation")}
|
||||
onSelected={(defaultVideoOrientation) => updateSettings({ defaultVideoOrientation })}
|
||||
onSelected={(defaultVideoOrientation) =>
|
||||
updateSettings({ defaultVideoOrientation })
|
||||
}
|
||||
/>
|
||||
</ListItem>
|
||||
|
||||
@@ -133,7 +156,9 @@ export const OtherSettings: React.FC = () => {
|
||||
<Switch
|
||||
value={settings.safeAreaInControlsEnabled}
|
||||
disabled={pluginSettings?.safeAreaInControlsEnabled?.locked}
|
||||
onValueChange={(value) => updateSettings({ safeAreaInControlsEnabled: value })}
|
||||
onValueChange={(value) =>
|
||||
updateSettings({ safeAreaInControlsEnabled: value })
|
||||
}
|
||||
/>
|
||||
</ListItem>
|
||||
|
||||
@@ -170,12 +195,18 @@ export const OtherSettings: React.FC = () => {
|
||||
<ListItem
|
||||
title={t("home.settings.other.show_custom_menu_links")}
|
||||
disabled={pluginSettings?.showCustomMenuLinks?.locked}
|
||||
onPress={() => Linking.openURL("https://jellyfin.org/docs/general/clients/web-config/#custom-menu-links")}
|
||||
onPress={() =>
|
||||
Linking.openURL(
|
||||
"https://jellyfin.org/docs/general/clients/web-config/#custom-menu-links",
|
||||
)
|
||||
}
|
||||
>
|
||||
<Switch
|
||||
value={settings.showCustomMenuLinks}
|
||||
disabled={pluginSettings?.showCustomMenuLinks?.locked}
|
||||
onValueChange={(value) => updateSettings({ showCustomMenuLinks: value })}
|
||||
onValueChange={(value) =>
|
||||
updateSettings({ showCustomMenuLinks: value })
|
||||
}
|
||||
/>
|
||||
</ListItem>
|
||||
<ListItem
|
||||
@@ -183,16 +214,25 @@ export const OtherSettings: React.FC = () => {
|
||||
title={t("home.settings.other.hide_libraries")}
|
||||
showArrow
|
||||
/>
|
||||
<ListItem title={t("home.settings.other.default_quality")} disabled={pluginSettings?.defaultBitrate?.locked}>
|
||||
<ListItem
|
||||
title={t("home.settings.other.default_quality")}
|
||||
disabled={pluginSettings?.defaultBitrate?.locked}
|
||||
>
|
||||
<Dropdown
|
||||
data={BITRATES}
|
||||
disabled={pluginSettings?.defaultBitrate?.locked}
|
||||
keyExtractor={(item) => item.key}
|
||||
titleExtractor={(item) => item.key}
|
||||
title={
|
||||
<TouchableOpacity className="flex flex-row items-center justify-between py-3 pl-3">
|
||||
<Text className="mr-1 text-[#8E8D91]">{settings.defaultBitrate?.key}</Text>
|
||||
<Ionicons name="chevron-expand-sharp" size={18} color="#5A5960" />
|
||||
<TouchableOpacity className='flex flex-row items-center justify-between py-3 pl-3'>
|
||||
<Text className='mr-1 text-[#8E8D91]'>
|
||||
{settings.defaultBitrate?.key}
|
||||
</Text>
|
||||
<Ionicons
|
||||
name='chevron-expand-sharp'
|
||||
size={18}
|
||||
color='#5A5960'
|
||||
/>
|
||||
</TouchableOpacity>
|
||||
}
|
||||
label={t("home.settings.other.default_quality")}
|
||||
@@ -206,7 +246,9 @@ export const OtherSettings: React.FC = () => {
|
||||
<Switch
|
||||
value={settings.disableHapticFeedback}
|
||||
disabled={pluginSettings?.disableHapticFeedback?.locked}
|
||||
onValueChange={(disableHapticFeedback) => updateSettings({ disableHapticFeedback })}
|
||||
onValueChange={(disableHapticFeedback) =>
|
||||
updateSettings({ disableHapticFeedback })
|
||||
}
|
||||
/>
|
||||
</ListItem>
|
||||
</ListGroup>
|
||||
|
||||
@@ -1,10 +1,10 @@
|
||||
import { useSettings } from "@/utils/atoms/settings";
|
||||
import { useRouter } from "expo-router";
|
||||
import React from "react";
|
||||
import { useTranslation } from "react-i18next";
|
||||
import { View } from "react-native";
|
||||
import { ListGroup } from "../list/ListGroup";
|
||||
import { ListItem } from "../list/ListItem";
|
||||
import { useTranslation } from "react-i18next";
|
||||
|
||||
export const PluginSettings = () => {
|
||||
const [settings, updateSettings] = useSettings();
|
||||
@@ -16,7 +16,10 @@ export const PluginSettings = () => {
|
||||
if (!settings) return null;
|
||||
return (
|
||||
<View>
|
||||
<ListGroup title={t("home.settings.plugins.plugins_title")} className="mb-4">
|
||||
<ListGroup
|
||||
title={t("home.settings.plugins.plugins_title")}
|
||||
className='mb-4'
|
||||
>
|
||||
<ListItem
|
||||
onPress={() => router.push("/settings/jellyseerr/page")}
|
||||
title={"Jellyseerr"}
|
||||
@@ -24,7 +27,7 @@ export const PluginSettings = () => {
|
||||
/>
|
||||
<ListItem
|
||||
onPress={() => router.push("/settings/marlin-search/page")}
|
||||
title="Marlin Search"
|
||||
title='Marlin Search'
|
||||
showArrow
|
||||
/>
|
||||
</ListGroup>
|
||||
|
||||
@@ -1,17 +1,18 @@
|
||||
import { useHaptic } from "@/hooks/useHaptic";
|
||||
import { apiAtom, userAtom } from "@/providers/JellyfinProvider";
|
||||
import {
|
||||
BottomSheetBackdrop,
|
||||
BottomSheetBackdropProps,
|
||||
type BottomSheetBackdropProps,
|
||||
BottomSheetModal,
|
||||
BottomSheetTextInput,
|
||||
BottomSheetView,
|
||||
} from "@gorhom/bottom-sheet";
|
||||
import { getQuickConnectApi } from "@jellyfin/sdk/lib/utils/api";
|
||||
import { useTranslation } from "react-i18next";
|
||||
import { useHaptic } from "@/hooks/useHaptic";
|
||||
import { useAtom } from "jotai";
|
||||
import React, { useCallback, useRef, useState } from "react";
|
||||
import { Alert, View, ViewProps } from "react-native";
|
||||
import type React from "react";
|
||||
import { useCallback, useRef, useState } from "react";
|
||||
import { useTranslation } from "react-i18next";
|
||||
import { Alert, View, type ViewProps } from "react-native";
|
||||
import { Button } from "../Button";
|
||||
import { Text } from "../common/Text";
|
||||
import { ListGroup } from "../list/ListGroup";
|
||||
@@ -37,7 +38,7 @@ export const QuickConnect: React.FC<Props> = ({ ...props }) => {
|
||||
appearsOnIndex={0}
|
||||
/>
|
||||
),
|
||||
[]
|
||||
[],
|
||||
);
|
||||
|
||||
const authorizeQuickConnect = useCallback(async () => {
|
||||
@@ -51,7 +52,7 @@ export const QuickConnect: React.FC<Props> = ({ ...props }) => {
|
||||
successHapticFeedback();
|
||||
Alert.alert(
|
||||
t("home.settings.quick_connect.success"),
|
||||
t("home.settings.quick_connect.quick_connect_autorized")
|
||||
t("home.settings.quick_connect.quick_connect_autorized"),
|
||||
);
|
||||
setQuickConnectCode(undefined);
|
||||
bottomSheetModalRef?.current?.close();
|
||||
@@ -59,14 +60,14 @@ export const QuickConnect: React.FC<Props> = ({ ...props }) => {
|
||||
errorHapticFeedback();
|
||||
Alert.alert(
|
||||
t("home.settings.quick_connect.error"),
|
||||
t("home.settings.quick_connect.invalid_code")
|
||||
t("home.settings.quick_connect.invalid_code"),
|
||||
);
|
||||
}
|
||||
} catch (e) {
|
||||
errorHapticFeedback();
|
||||
Alert.alert(
|
||||
t("home.settings.quick_connect.error"),
|
||||
t("home.settings.quick_connect.invalid_code")
|
||||
t("home.settings.quick_connect.invalid_code"),
|
||||
);
|
||||
}
|
||||
}
|
||||
@@ -78,7 +79,7 @@ export const QuickConnect: React.FC<Props> = ({ ...props }) => {
|
||||
<ListItem
|
||||
onPress={() => bottomSheetModalRef?.current?.present()}
|
||||
title={t("home.settings.quick_connect.authorize_button")}
|
||||
textColor="blue"
|
||||
textColor='blue'
|
||||
/>
|
||||
</ListGroup>
|
||||
|
||||
@@ -94,30 +95,30 @@ export const QuickConnect: React.FC<Props> = ({ ...props }) => {
|
||||
backdropComponent={renderBackdrop}
|
||||
>
|
||||
<BottomSheetView>
|
||||
<View className="flex flex-col space-y-4 px-4 pb-8 pt-2">
|
||||
<View className='flex flex-col space-y-4 px-4 pb-8 pt-2'>
|
||||
<View>
|
||||
<Text className="font-bold text-2xl text-neutral-100">
|
||||
<Text className='font-bold text-2xl text-neutral-100'>
|
||||
{t("home.settings.quick_connect.quick_connect_title")}
|
||||
</Text>
|
||||
</View>
|
||||
<View className="flex flex-col space-y-2">
|
||||
<View className="p-4 border border-neutral-800 rounded-xl bg-neutral-900 w-full">
|
||||
<View className='flex flex-col space-y-2'>
|
||||
<View className='p-4 border border-neutral-800 rounded-xl bg-neutral-900 w-full'>
|
||||
<BottomSheetTextInput
|
||||
style={{ color: "white" }}
|
||||
clearButtonMode="always"
|
||||
clearButtonMode='always'
|
||||
placeholder={t(
|
||||
"home.settings.quick_connect.enter_the_quick_connect_code"
|
||||
"home.settings.quick_connect.enter_the_quick_connect_code",
|
||||
)}
|
||||
placeholderTextColor="#9CA3AF"
|
||||
placeholderTextColor='#9CA3AF'
|
||||
value={quickConnectCode}
|
||||
onChangeText={setQuickConnectCode}
|
||||
/>
|
||||
</View>
|
||||
</View>
|
||||
<Button
|
||||
className="mt-auto"
|
||||
className='mt-auto'
|
||||
onPress={authorizeQuickConnect}
|
||||
color="purple"
|
||||
color='purple'
|
||||
>
|
||||
{t("home.settings.quick_connect.authorize")}
|
||||
</Button>
|
||||
|
||||
@@ -1,14 +1,14 @@
|
||||
import { Text } from "@/components/common/Text";
|
||||
import { Colors } from "@/constants/Colors";
|
||||
import { useHaptic } from "@/hooks/useHaptic";
|
||||
import { useDownload } from "@/providers/DownloadProvider";
|
||||
import { useQuery } from "@tanstack/react-query";
|
||||
import * as FileSystem from "expo-file-system";
|
||||
import { useTranslation } from "react-i18next";
|
||||
import { View } from "react-native";
|
||||
import { toast } from "sonner-native";
|
||||
import { ListGroup } from "../list/ListGroup";
|
||||
import { ListItem } from "../list/ListItem";
|
||||
import { useTranslation } from "react-i18next";
|
||||
import {Colors} from "@/constants/Colors";
|
||||
|
||||
export const StorageSettings = () => {
|
||||
const { deleteAllFiles, appSizeUsage } = useDownload();
|
||||
@@ -44,11 +44,11 @@ export const StorageSettings = () => {
|
||||
|
||||
return (
|
||||
<View>
|
||||
<View className="flex flex-col gap-y-1">
|
||||
<View className="flex flex-row items-center justify-between">
|
||||
<Text className="">{t("home.settings.storage.storage_title")}</Text>
|
||||
<View className='flex flex-col gap-y-1'>
|
||||
<View className='flex flex-row items-center justify-between'>
|
||||
<Text className=''>{t("home.settings.storage.storage_title")}</Text>
|
||||
{size && (
|
||||
<Text className="text-neutral-500">
|
||||
<Text className='text-neutral-500'>
|
||||
{t("home.settings.storage.size_used", {
|
||||
used: Number(size.total - size.remaining).bytesToReadable(),
|
||||
total: size.total?.bytesToReadable(),
|
||||
@@ -56,7 +56,7 @@ export const StorageSettings = () => {
|
||||
</Text>
|
||||
)}
|
||||
</View>
|
||||
<View className="h-3 w-full bg-gray-100/10 rounded-md overflow-hidden flex flex-row">
|
||||
<View className='h-3 w-full bg-gray-100/10 rounded-md overflow-hidden flex flex-row'>
|
||||
{size && (
|
||||
<>
|
||||
<View
|
||||
@@ -77,24 +77,24 @@ export const StorageSettings = () => {
|
||||
</>
|
||||
)}
|
||||
</View>
|
||||
<View className="flex flex-row gap-x-2">
|
||||
<View className='flex flex-row gap-x-2'>
|
||||
{size && (
|
||||
<>
|
||||
<View className="flex flex-row items-center">
|
||||
<View className="w-3 h-3 rounded-full bg-purple-600 mr-1"></View>
|
||||
<Text className="text-white text-xs">
|
||||
<View className='flex flex-row items-center'>
|
||||
<View className='w-3 h-3 rounded-full bg-purple-600 mr-1'></View>
|
||||
<Text className='text-white text-xs'>
|
||||
{t("home.settings.storage.app_usage", {
|
||||
usedSpace: calculatePercentage(size.app, size.total),
|
||||
})}
|
||||
</Text>
|
||||
</View>
|
||||
<View className="flex flex-row items-center">
|
||||
<View className="w-3 h-3 rounded-full bg-purple-400 mr-1"></View>
|
||||
<Text className="text-white text-xs">
|
||||
<View className='flex flex-row items-center'>
|
||||
<View className='w-3 h-3 rounded-full bg-purple-400 mr-1'></View>
|
||||
<Text className='text-white text-xs'>
|
||||
{t("home.settings.storage.device_usage", {
|
||||
availableSpace: calculatePercentage(
|
||||
size.total - size.remaining - size.app,
|
||||
size.total
|
||||
size.total,
|
||||
),
|
||||
})}
|
||||
</Text>
|
||||
@@ -105,7 +105,7 @@ export const StorageSettings = () => {
|
||||
</View>
|
||||
<ListGroup>
|
||||
<ListItem
|
||||
textColor="red"
|
||||
textColor='red'
|
||||
onPress={onDeleteClicked}
|
||||
title={t("home.settings.storage.delete_all_downloaded_files")}
|
||||
/>
|
||||
|
||||
@@ -1,16 +1,16 @@
|
||||
import { Platform, TouchableOpacity, View, ViewProps } from "react-native";
|
||||
import { Platform, TouchableOpacity, View, type ViewProps } from "react-native";
|
||||
const DropdownMenu = !Platform.isTV ? require("zeego/dropdown-menu") : null;
|
||||
import { Text } from "../common/Text";
|
||||
import { useMedia } from "./MediaContext";
|
||||
import { Switch } from "react-native-gesture-handler";
|
||||
import { ListGroup } from "../list/ListGroup";
|
||||
import { ListItem } from "../list/ListItem";
|
||||
import Dropdown from "@/components/common/Dropdown";
|
||||
import { Stepper } from "@/components/inputs/Stepper";
|
||||
import { useSettings } from "@/utils/atoms/settings";
|
||||
import { Ionicons } from "@expo/vector-icons";
|
||||
import { SubtitlePlaybackMode } from "@jellyfin/sdk/lib/generated-client";
|
||||
import { useTranslation } from "react-i18next";
|
||||
import { useSettings } from "@/utils/atoms/settings";
|
||||
import { Stepper } from "@/components/inputs/Stepper";
|
||||
import Dropdown from "@/components/common/Dropdown";
|
||||
import { Switch } from "react-native-gesture-handler";
|
||||
import { Text } from "../common/Text";
|
||||
import { ListGroup } from "../list/ListGroup";
|
||||
import { ListItem } from "../list/ListItem";
|
||||
import { useMedia } from "./MediaContext";
|
||||
|
||||
interface Props extends ViewProps {}
|
||||
|
||||
@@ -46,7 +46,7 @@ export const SubtitleToggles: React.FC<Props> = ({ ...props }) => {
|
||||
<ListGroup
|
||||
title={t("home.settings.subtitles.subtitle_title")}
|
||||
description={
|
||||
<Text className="text-[#8E8D91] text-xs">
|
||||
<Text className='text-[#8E8D91] text-xs'>
|
||||
{t("home.settings.subtitles.subtitle_hint")}
|
||||
</Text>
|
||||
}
|
||||
@@ -65,15 +65,15 @@ export const SubtitleToggles: React.FC<Props> = ({ ...props }) => {
|
||||
}
|
||||
titleExtractor={(item) => item?.DisplayName}
|
||||
title={
|
||||
<TouchableOpacity className="flex flex-row items-center justify-between py-3 pl-3">
|
||||
<Text className="mr-1 text-[#8E8D91]">
|
||||
<TouchableOpacity className='flex flex-row items-center justify-between py-3 pl-3'>
|
||||
<Text className='mr-1 text-[#8E8D91]'>
|
||||
{settings?.defaultSubtitleLanguage?.DisplayName ||
|
||||
t("home.settings.subtitles.none")}
|
||||
</Text>
|
||||
<Ionicons
|
||||
name="chevron-expand-sharp"
|
||||
name='chevron-expand-sharp'
|
||||
size={18}
|
||||
color="#5A5960"
|
||||
color='#5A5960'
|
||||
/>
|
||||
</TouchableOpacity>
|
||||
}
|
||||
@@ -100,15 +100,15 @@ export const SubtitleToggles: React.FC<Props> = ({ ...props }) => {
|
||||
keyExtractor={String}
|
||||
titleExtractor={(item) => t(subtitleModeKeys[item]) || String(item)}
|
||||
title={
|
||||
<TouchableOpacity className="flex flex-row items-center justify-between py-3 pl-3">
|
||||
<Text className="mr-1 text-[#8E8D91]">
|
||||
<TouchableOpacity className='flex flex-row items-center justify-between py-3 pl-3'>
|
||||
<Text className='mr-1 text-[#8E8D91]'>
|
||||
{t(subtitleModeKeys[settings?.subtitleMode]) ||
|
||||
t("home.settings.subtitles.loading")}
|
||||
</Text>
|
||||
<Ionicons
|
||||
name="chevron-expand-sharp"
|
||||
name='chevron-expand-sharp'
|
||||
size={18}
|
||||
color="#5A5960"
|
||||
color='#5A5960'
|
||||
/>
|
||||
</TouchableOpacity>
|
||||
}
|
||||
|
||||
@@ -1,13 +1,13 @@
|
||||
import { View, ViewProps } from "react-native";
|
||||
import { Text } from "../common/Text";
|
||||
import { ListItem } from "../list/ListItem";
|
||||
import { Button } from "../Button";
|
||||
import { apiAtom, useJellyfin, userAtom } from "@/providers/JellyfinProvider";
|
||||
import { useAtom } from "jotai";
|
||||
import Constants from "expo-constants";
|
||||
import Application from "expo-application";
|
||||
import { ListGroup } from "../list/ListGroup";
|
||||
import Constants from "expo-constants";
|
||||
import { useAtom } from "jotai";
|
||||
import { useTranslation } from "react-i18next";
|
||||
import { View, type ViewProps } from "react-native";
|
||||
import { Button } from "../Button";
|
||||
import { Text } from "../common/Text";
|
||||
import { ListGroup } from "../list/ListGroup";
|
||||
import { ListItem } from "../list/ListItem";
|
||||
|
||||
interface Props extends ViewProps {}
|
||||
|
||||
@@ -24,10 +24,22 @@ export const UserInfo: React.FC<Props> = ({ ...props }) => {
|
||||
return (
|
||||
<View {...props}>
|
||||
<ListGroup title={t("home.settings.user_info.user_info_title")}>
|
||||
<ListItem title={t("home.settings.user_info.user")} value={user?.Name} />
|
||||
<ListItem title={t("home.settings.user_info.server")} value={api?.basePath} />
|
||||
<ListItem title={t("home.settings.user_info.token")} value={api?.accessToken} />
|
||||
<ListItem title={t("home.settings.user_info.app_version")} value={version} />
|
||||
<ListItem
|
||||
title={t("home.settings.user_info.user")}
|
||||
value={user?.Name}
|
||||
/>
|
||||
<ListItem
|
||||
title={t("home.settings.user_info.server")}
|
||||
value={api?.basePath}
|
||||
/>
|
||||
<ListItem
|
||||
title={t("home.settings.user_info.token")}
|
||||
value={api?.accessToken}
|
||||
/>
|
||||
<ListItem
|
||||
title={t("home.settings.user_info.app_version")}
|
||||
value={version}
|
||||
/>
|
||||
</ListGroup>
|
||||
</View>
|
||||
);
|
||||
|
||||
Reference in New Issue
Block a user