mirror of
https://github.com/streamyfin/streamyfin.git
synced 2026-04-17 22:42:03 +01:00
Merge branch 'develop' into feat/i18n
This commit is contained in:
@@ -7,11 +7,13 @@ import { useTranslation } from "react-i18next";
|
||||
import { ListGroup } from "../list/ListGroup";
|
||||
import { ListItem } from "../list/ListItem";
|
||||
import { Ionicons } from "@expo/vector-icons";
|
||||
import {useSettings} from "@/utils/atoms/settings";
|
||||
|
||||
interface Props extends ViewProps {}
|
||||
|
||||
export const AudioToggles: React.FC<Props> = ({ ...props }) => {
|
||||
const media = useMedia();
|
||||
const [_, __, pluginSettings] = useSettings();
|
||||
const { settings, updateSettings } = media;
|
||||
const cultures = media.cultures;
|
||||
const { t } = useTranslation();
|
||||
@@ -28,9 +30,13 @@ export const AudioToggles: React.FC<Props> = ({ ...props }) => {
|
||||
</Text>
|
||||
}
|
||||
>
|
||||
<ListItem title={t("home.settings.audio.set_audio_track")}>
|
||||
<ListItem
|
||||
title={t("home.settings.audio.set_audio_track")}
|
||||
disabled={pluginSettings?.rememberAudioSelections?.locked}
|
||||
>
|
||||
<Switch
|
||||
value={settings.rememberAudioSelections}
|
||||
disabled={pluginSettings?.rememberAudioSelections?.locked}
|
||||
onValueChange={(value) =>
|
||||
updateSettings({ rememberAudioSelections: value })
|
||||
}
|
||||
|
||||
26
components/settings/DisabledSetting.tsx
Normal file
26
components/settings/DisabledSetting.tsx
Normal file
@@ -0,0 +1,26 @@
|
||||
import {View, ViewProps} from "react-native";
|
||||
import {Text} from "@/components/common/Text";
|
||||
|
||||
const DisabledSetting: React.FC<{disabled: boolean, showText?: boolean, text?: string} & ViewProps> = ({
|
||||
disabled = false,
|
||||
showText = true,
|
||||
text,
|
||||
children,
|
||||
...props
|
||||
}) => (
|
||||
<View
|
||||
pointerEvents={disabled ? "none" : "auto"}
|
||||
style={{
|
||||
opacity: disabled ? 0.5 : 1,
|
||||
}}
|
||||
>
|
||||
<View {...props}>
|
||||
{disabled && showText &&
|
||||
<Text className="text-center text-red-700 my-4">{text ?? "Currently disabled by admin."}</Text>
|
||||
}
|
||||
{children}
|
||||
</View>
|
||||
</View>
|
||||
)
|
||||
|
||||
export default DisabledSetting;
|
||||
@@ -1,35 +1,47 @@
|
||||
import { Stepper } from "@/components/inputs/Stepper";
|
||||
import { useDownload } from "@/providers/DownloadProvider";
|
||||
import { Settings, useSettings } from "@/utils/atoms/settings";
|
||||
import { DownloadMethod, 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 from "react";
|
||||
import { Switch, TouchableOpacity, View } from "react-native";
|
||||
import React, { useMemo } from "react";
|
||||
import { Switch, TouchableOpacity } from "react-native";
|
||||
import * as DropdownMenu from "zeego/dropdown-menu";
|
||||
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 const DownloadSettings: React.FC = ({ ...props }) => {
|
||||
const [settings, updateSettings] = useSettings();
|
||||
const [settings, updateSettings, pluginSettings] = useSettings();
|
||||
const { setProcesses } = useDownload();
|
||||
const router = useRouter();
|
||||
const queryClient = useQueryClient();
|
||||
const { t } = useTranslation();
|
||||
|
||||
const allDisabled = useMemo(
|
||||
() =>
|
||||
pluginSettings?.downloadMethod?.locked === true &&
|
||||
pluginSettings?.remuxConcurrentLimit?.locked === true &&
|
||||
pluginSettings?.autoDownload.locked === true,
|
||||
[pluginSettings]
|
||||
);
|
||||
|
||||
if (!settings) return null;
|
||||
|
||||
return (
|
||||
<View {...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")}>
|
||||
<ListItem
|
||||
title={t("home.settings.downloads.download_method")}
|
||||
disabled={pluginSettings?.downloadMethod?.locked}
|
||||
>
|
||||
<DropdownMenu.Root>
|
||||
<DropdownMenu.Trigger>
|
||||
<TouchableOpacity className="flex flex-row items-center justify-between py-3 pl-3">
|
||||
<Text className="mr-1 text-[#8E8D91]">
|
||||
{settings.downloadMethod === "remux"
|
||||
{settings.downloadMethod === DownloadMethod.Remux
|
||||
? t("home.settings.downloads.default")
|
||||
: t("home.settings.downloads.optimized")}
|
||||
</Text>
|
||||
@@ -53,7 +65,7 @@ export const DownloadSettings: React.FC = ({ ...props }) => {
|
||||
<DropdownMenu.Item
|
||||
key="1"
|
||||
onSelect={() => {
|
||||
updateSettings({ downloadMethod: "remux" });
|
||||
updateSettings({ downloadMethod: DownloadMethod.Remux });
|
||||
setProcesses([]);
|
||||
}}
|
||||
>
|
||||
@@ -62,7 +74,7 @@ export const DownloadSettings: React.FC = ({ ...props }) => {
|
||||
<DropdownMenu.Item
|
||||
key="2"
|
||||
onSelect={() => {
|
||||
updateSettings({ downloadMethod: "optimized" });
|
||||
updateSettings({ downloadMethod: DownloadMethod.Optimized });
|
||||
setProcesses([]);
|
||||
queryClient.invalidateQueries({ queryKey: ["search"] });
|
||||
}}
|
||||
@@ -75,7 +87,10 @@ export const DownloadSettings: React.FC = ({ ...props }) => {
|
||||
|
||||
<ListItem
|
||||
title={t("home.settings.downloads.remux_max_download")}
|
||||
disabled={settings.downloadMethod !== "remux"}
|
||||
disabled={
|
||||
pluginSettings?.remuxConcurrentLimit?.locked ||
|
||||
settings.downloadMethod !== DownloadMethod.Remux
|
||||
}
|
||||
>
|
||||
<Stepper
|
||||
value={settings.remuxConcurrentLimit}
|
||||
@@ -92,22 +107,31 @@ export const DownloadSettings: React.FC = ({ ...props }) => {
|
||||
|
||||
<ListItem
|
||||
title={t("home.settings.downloads.auto_download")}
|
||||
disabled={settings.downloadMethod !== "optimized"}
|
||||
disabled={
|
||||
pluginSettings?.autoDownload?.locked ||
|
||||
settings.downloadMethod !== DownloadMethod.Optimized
|
||||
}
|
||||
>
|
||||
<Switch
|
||||
disabled={settings.downloadMethod !== "optimized"}
|
||||
disabled={
|
||||
pluginSettings?.autoDownload?.locked ||
|
||||
settings.downloadMethod !== DownloadMethod.Optimized
|
||||
}
|
||||
value={settings.autoDownload}
|
||||
onValueChange={(value) => updateSettings({ autoDownload: value })}
|
||||
/>
|
||||
</ListItem>
|
||||
|
||||
<ListItem
|
||||
disabled={settings.downloadMethod !== "optimized"}
|
||||
disabled={
|
||||
pluginSettings?.optimizedVersionsServerUrl?.locked ||
|
||||
settings.downloadMethod !== DownloadMethod.Optimized
|
||||
}
|
||||
onPress={() => router.push("/settings/optimized-server/page")}
|
||||
showArrow
|
||||
title={t("home.settings.downloads.optimized_versions_server")}
|
||||
></ListItem>
|
||||
</ListGroup>
|
||||
</View>
|
||||
</DisabledSetting>
|
||||
);
|
||||
};
|
||||
|
||||
@@ -24,7 +24,7 @@ export const JellyseerrSettings = () => {
|
||||
const { t } = useTranslation();
|
||||
|
||||
const [user] = useAtom(userAtom);
|
||||
const [settings, updateSettings] = useSettings();
|
||||
const [settings, updateSettings, pluginSettings] = useSettings();
|
||||
|
||||
const [promptForJellyseerrPass, setPromptForJellyseerrPass] =
|
||||
useState<boolean>(false);
|
||||
|
||||
@@ -1,74 +1,64 @@
|
||||
import React from "react";
|
||||
import { TouchableOpacity, View, ViewProps } from "react-native";
|
||||
import React, {useMemo} from "react";
|
||||
import { ViewProps } from "react-native";
|
||||
import { useSettings } from "@/utils/atoms/settings";
|
||||
import { ListGroup } from "../list/ListGroup";
|
||||
import { ListItem } from "../list/ListItem";
|
||||
import { Text } from "../common/Text";
|
||||
import { useTranslation } from "react-i18next";
|
||||
import DisabledSetting from "@/components/settings/DisabledSetting";
|
||||
import {Stepper} from "@/components/inputs/Stepper";
|
||||
|
||||
interface Props extends ViewProps {}
|
||||
|
||||
export const MediaToggles: React.FC<Props> = ({ ...props }) => {
|
||||
const [settings, updateSettings] = useSettings();
|
||||
const { t } = useTranslation();
|
||||
|
||||
const [settings, updateSettings, pluginSettings] = useSettings();
|
||||
|
||||
if (!settings) return null;
|
||||
|
||||
const renderSkipControl = (
|
||||
value: number,
|
||||
onDecrease: () => void,
|
||||
onIncrease: () => void
|
||||
) => (
|
||||
<View className="flex flex-row items-center">
|
||||
<TouchableOpacity
|
||||
onPress={onDecrease}
|
||||
className="w-8 h-8 bg-neutral-800 rounded-l-lg flex items-center justify-center"
|
||||
>
|
||||
<Text>-</Text>
|
||||
</TouchableOpacity>
|
||||
<Text className="w-12 h-8 bg-neutral-800 first-letter:px-3 py-2 flex items-center justify-center">
|
||||
{value}s
|
||||
</Text>
|
||||
<TouchableOpacity
|
||||
className="w-8 h-8 bg-neutral-800 rounded-r-lg flex items-center justify-center"
|
||||
onPress={onIncrease}
|
||||
>
|
||||
<Text>+</Text>
|
||||
</TouchableOpacity>
|
||||
</View>
|
||||
);
|
||||
const disabled = useMemo(() => (
|
||||
pluginSettings?.forwardSkipTime?.locked === true &&
|
||||
pluginSettings?.rewindSkipTime?.locked === true
|
||||
),
|
||||
[pluginSettings]
|
||||
)
|
||||
|
||||
return (
|
||||
<View {...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")}>
|
||||
{renderSkipControl(
|
||||
settings.forwardSkipTime,
|
||||
() =>
|
||||
updateSettings({
|
||||
forwardSkipTime: Math.max(0, settings.forwardSkipTime - 5),
|
||||
}),
|
||||
() =>
|
||||
updateSettings({
|
||||
forwardSkipTime: Math.min(60, settings.forwardSkipTime + 5),
|
||||
})
|
||||
)}
|
||||
<ListItem
|
||||
title={t("home.settings.media_controls.forward_skip_length")}
|
||||
disabled={pluginSettings?.forwardSkipTime?.locked}
|
||||
>
|
||||
<Stepper
|
||||
value={settings.forwardSkipTime}
|
||||
disabled={pluginSettings?.forwardSkipTime?.locked}
|
||||
step={5}
|
||||
appendValue="s"
|
||||
min={0}
|
||||
max={60}
|
||||
onUpdate={(forwardSkipTime) => updateSettings({forwardSkipTime})}
|
||||
/>
|
||||
</ListItem>
|
||||
|
||||
<ListItem title={t("home.settings.media_controls.rewind_length")}>
|
||||
{renderSkipControl(
|
||||
settings.rewindSkipTime,
|
||||
() =>
|
||||
updateSettings({
|
||||
rewindSkipTime: Math.max(0, settings.rewindSkipTime - 5),
|
||||
}),
|
||||
() =>
|
||||
updateSettings({
|
||||
rewindSkipTime: Math.min(60, settings.rewindSkipTime + 5),
|
||||
})
|
||||
)}
|
||||
<ListItem
|
||||
title={t("home.settings.media_controls.rewind_length")}
|
||||
disabled={pluginSettings?.rewindSkipTime?.locked}
|
||||
>
|
||||
<Stepper
|
||||
value={settings.rewindSkipTime}
|
||||
disabled={pluginSettings?.rewindSkipTime?.locked}
|
||||
step={5}
|
||||
appendValue="s"
|
||||
min={0}
|
||||
max={60}
|
||||
onUpdate={(rewindSkipTime) => updateSettings({rewindSkipTime})}
|
||||
/>
|
||||
</ListItem>
|
||||
</ListGroup>
|
||||
</View>
|
||||
</DisabledSetting>
|
||||
);
|
||||
};
|
||||
|
||||
@@ -9,20 +9,19 @@ import * as BackgroundFetch from "expo-background-fetch";
|
||||
import { useRouter } from "expo-router";
|
||||
import * as ScreenOrientation from "expo-screen-orientation";
|
||||
import * as TaskManager from "expo-task-manager";
|
||||
import React, { useEffect } from "react";
|
||||
import { Linking, Switch, TouchableOpacity, ViewProps } from "react-native";
|
||||
import React, {useEffect, useMemo} from "react";
|
||||
import { Linking, Switch, TouchableOpacity } from "react-native";
|
||||
import { toast } from "sonner-native";
|
||||
import * as DropdownMenu from "zeego/dropdown-menu";
|
||||
import { Text } from "../common/Text";
|
||||
import { ListGroup } from "../list/ListGroup";
|
||||
import { ListItem } from "../list/ListItem";
|
||||
import { useTranslation } from "react-i18next";
|
||||
|
||||
interface Props extends ViewProps {}
|
||||
import DisabledSetting from "@/components/settings/DisabledSetting";
|
||||
import Dropdown from "@/components/common/Dropdown";
|
||||
|
||||
export const OtherSettings: React.FC = () => {
|
||||
const router = useRouter();
|
||||
const [settings, updateSettings] = useSettings();
|
||||
const [settings, updateSettings, pluginSettings] = useSettings();
|
||||
|
||||
const { t } = useTranslation();
|
||||
|
||||
@@ -56,146 +55,114 @@ export const OtherSettings: React.FC = () => {
|
||||
/**********************
|
||||
*********************/
|
||||
|
||||
const disabled = useMemo(() => (
|
||||
pluginSettings?.autoRotate?.locked === true &&
|
||||
pluginSettings?.defaultVideoOrientation?.locked === true &&
|
||||
pluginSettings?.safeAreaInControlsEnabled?.locked === true &&
|
||||
pluginSettings?.showCustomMenuLinks?.locked === true &&
|
||||
pluginSettings?.hiddenLibraries?.locked === true &&
|
||||
pluginSettings?.disableHapticFeedback?.locked === true
|
||||
), [pluginSettings]);
|
||||
|
||||
const orientations = [
|
||||
ScreenOrientation.OrientationLock.DEFAULT,
|
||||
ScreenOrientation.OrientationLock.PORTRAIT_UP,
|
||||
ScreenOrientation.OrientationLock.LANDSCAPE_LEFT,
|
||||
ScreenOrientation.OrientationLock.LANDSCAPE_RIGHT
|
||||
]
|
||||
|
||||
if (!settings) return null;
|
||||
|
||||
return (
|
||||
<ListGroup title={t("home.settings.other.other_title")} className="">
|
||||
<ListItem title={t("home.settings.other.auto_rotate")}>
|
||||
<Switch
|
||||
value={settings.autoRotate}
|
||||
onValueChange={(value) => updateSettings({ autoRotate: value })}
|
||||
/>
|
||||
</ListItem>
|
||||
<DisabledSetting
|
||||
disabled={disabled}
|
||||
>
|
||||
<ListGroup title={t("home.settings.other.other_title")} className="">
|
||||
<ListItem
|
||||
title={t("home.settings.other.auto_rotate")}
|
||||
disabled={pluginSettings?.autoRotate?.locked}
|
||||
>
|
||||
<Switch
|
||||
value={settings.autoRotate}
|
||||
disabled={pluginSettings?.autoRotate?.locked}
|
||||
onValueChange={(value) => updateSettings({autoRotate: value})}
|
||||
/>
|
||||
</ListItem>
|
||||
|
||||
<ListItem title={t("home.settings.other.video_orientation")} disabled={settings.autoRotate}>
|
||||
<DropdownMenu.Root>
|
||||
<DropdownMenu.Trigger>
|
||||
<TouchableOpacity className="flex flex-row items-center justify-between py-3 pl-3">
|
||||
<Text className="mr-1 text-[#8E8D91]">
|
||||
{ScreenOrientationEnum[settings.defaultVideoOrientation]}
|
||||
</Text>
|
||||
<Ionicons name="chevron-expand-sharp" size={18} color="#5A5960" />
|
||||
</TouchableOpacity>
|
||||
</DropdownMenu.Trigger>
|
||||
<DropdownMenu.Content
|
||||
loop={true}
|
||||
side="bottom"
|
||||
align="start"
|
||||
alignOffset={0}
|
||||
avoidCollisions={true}
|
||||
collisionPadding={8}
|
||||
sideOffset={8}
|
||||
>
|
||||
<DropdownMenu.Label>Orientation</DropdownMenu.Label>
|
||||
<DropdownMenu.Item
|
||||
key="1"
|
||||
onSelect={() => {
|
||||
updateSettings({
|
||||
defaultVideoOrientation:
|
||||
ScreenOrientation.OrientationLock.DEFAULT,
|
||||
});
|
||||
}}
|
||||
>
|
||||
<DropdownMenu.ItemTitle>
|
||||
{
|
||||
ScreenOrientationEnum[
|
||||
ScreenOrientation.OrientationLock.DEFAULT
|
||||
]
|
||||
}
|
||||
</DropdownMenu.ItemTitle>
|
||||
</DropdownMenu.Item>
|
||||
<DropdownMenu.Item
|
||||
key="2"
|
||||
onSelect={() => {
|
||||
updateSettings({
|
||||
defaultVideoOrientation:
|
||||
ScreenOrientation.OrientationLock.PORTRAIT_UP,
|
||||
});
|
||||
}}
|
||||
>
|
||||
<DropdownMenu.ItemTitle>
|
||||
{
|
||||
ScreenOrientationEnum[
|
||||
ScreenOrientation.OrientationLock.PORTRAIT_UP
|
||||
]
|
||||
}
|
||||
</DropdownMenu.ItemTitle>
|
||||
</DropdownMenu.Item>
|
||||
<DropdownMenu.Item
|
||||
key="3"
|
||||
onSelect={() => {
|
||||
updateSettings({
|
||||
defaultVideoOrientation:
|
||||
ScreenOrientation.OrientationLock.LANDSCAPE_LEFT,
|
||||
});
|
||||
}}
|
||||
>
|
||||
<DropdownMenu.ItemTitle>
|
||||
{
|
||||
ScreenOrientationEnum[
|
||||
ScreenOrientation.OrientationLock.LANDSCAPE_LEFT
|
||||
]
|
||||
}
|
||||
</DropdownMenu.ItemTitle>
|
||||
</DropdownMenu.Item>
|
||||
<DropdownMenu.Item
|
||||
key="4"
|
||||
onSelect={() => {
|
||||
updateSettings({
|
||||
defaultVideoOrientation:
|
||||
ScreenOrientation.OrientationLock.LANDSCAPE_RIGHT,
|
||||
});
|
||||
}}
|
||||
>
|
||||
<DropdownMenu.ItemTitle>
|
||||
{
|
||||
ScreenOrientationEnum[
|
||||
ScreenOrientation.OrientationLock.LANDSCAPE_RIGHT
|
||||
]
|
||||
}
|
||||
</DropdownMenu.ItemTitle>
|
||||
</DropdownMenu.Item>
|
||||
</DropdownMenu.Content>
|
||||
</DropdownMenu.Root>
|
||||
</ListItem>
|
||||
<ListItem
|
||||
title={t("home.settings.other.video_orientation")}
|
||||
disabled={pluginSettings?.defaultVideoOrientation?.locked || settings.autoRotate}
|
||||
>
|
||||
<Dropdown
|
||||
data={orientations}
|
||||
disabled={pluginSettings?.defaultVideoOrientation?.locked || settings.autoRotate}
|
||||
keyExtractor={String}
|
||||
titleExtractor={(item) =>
|
||||
ScreenOrientationEnum[item]
|
||||
}
|
||||
title={
|
||||
<TouchableOpacity className="flex flex-row items-center justify-between py-3 pl-3">
|
||||
<Text className="mr-1 text-[#8E8D91]">
|
||||
{ScreenOrientationEnum[settings.defaultVideoOrientation]}
|
||||
</Text>
|
||||
<Ionicons name="chevron-expand-sharp" size={18} color="#5A5960"/>
|
||||
</TouchableOpacity>
|
||||
}
|
||||
label="Orientation"
|
||||
onSelected={(defaultVideoOrientation) =>
|
||||
updateSettings({defaultVideoOrientation})
|
||||
}
|
||||
/>
|
||||
</ListItem>
|
||||
|
||||
<ListItem title={t("home.settings.other.safe_area_in_controls")}>
|
||||
<Switch
|
||||
value={settings.safeAreaInControlsEnabled}
|
||||
onValueChange={(value) =>
|
||||
updateSettings({ safeAreaInControlsEnabled: value })
|
||||
}
|
||||
/>
|
||||
</ListItem>
|
||||
<ListItem
|
||||
title={t("home.settings.other.safe_area_in_controls")}
|
||||
disabled={pluginSettings?.safeAreaInControlsEnabled?.locked}
|
||||
>
|
||||
<Switch
|
||||
value={settings.safeAreaInControlsEnabled}
|
||||
disabled={pluginSettings?.safeAreaInControlsEnabled?.locked}
|
||||
onValueChange={(value) =>
|
||||
updateSettings({safeAreaInControlsEnabled: value})
|
||||
}
|
||||
/>
|
||||
</ListItem>
|
||||
|
||||
<ListItem
|
||||
title={t("home.settings.other.show_custom_menu_links")}
|
||||
onPress={() =>
|
||||
Linking.openURL(
|
||||
"https://jellyfin.org/docs/general/clients/web-config/#custom-menu-links"
|
||||
)
|
||||
}
|
||||
>
|
||||
<Switch
|
||||
value={settings.showCustomMenuLinks}
|
||||
onValueChange={(value) =>
|
||||
updateSettings({ showCustomMenuLinks: value })
|
||||
<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"
|
||||
)
|
||||
}
|
||||
>
|
||||
<Switch
|
||||
value={settings.showCustomMenuLinks}
|
||||
disabled={pluginSettings?.showCustomMenuLinks?.locked}
|
||||
onValueChange={(value) =>
|
||||
updateSettings({showCustomMenuLinks: value})
|
||||
}
|
||||
/>
|
||||
</ListItem>
|
||||
<ListItem
|
||||
onPress={() => router.push("/settings/hide-libraries/page")}
|
||||
title="Hide Libraries"
|
||||
showArrow
|
||||
/>
|
||||
</ListItem>
|
||||
<ListItem
|
||||
onPress={() => router.push("/settings/hide-libraries/page")}
|
||||
title="Hide Libraries"
|
||||
showArrow
|
||||
/>
|
||||
<ListItem title="Disable Haptic Feedback">
|
||||
<Switch
|
||||
value={settings.disableHapticFeedback}
|
||||
onValueChange={(value) =>
|
||||
updateSettings({ disableHapticFeedback: value })
|
||||
}
|
||||
/>
|
||||
</ListItem>
|
||||
</ListGroup>
|
||||
<ListItem
|
||||
title="Disable Haptic Feedback"
|
||||
disabled={pluginSettings?.disableHapticFeedback?.locked}
|
||||
>
|
||||
<Switch
|
||||
value={settings.disableHapticFeedback}
|
||||
disabled={pluginSettings?.disableHapticFeedback?.locked}
|
||||
onValueChange={(disableHapticFeedback) =>
|
||||
updateSettings({disableHapticFeedback})
|
||||
}
|
||||
/>
|
||||
</ListItem>
|
||||
</ListGroup>
|
||||
</DisabledSetting>
|
||||
);
|
||||
};
|
||||
|
||||
@@ -1,12 +1,9 @@
|
||||
import { Button } from "@/components/Button";
|
||||
import { Text } from "@/components/common/Text";
|
||||
import { useHaptic } from "@/hooks/useHaptic";
|
||||
import { useDownload } from "@/providers/DownloadProvider";
|
||||
import { clearLogs } from "@/utils/log";
|
||||
import { useQuery } from "@tanstack/react-query";
|
||||
import * as FileSystem from "expo-file-system";
|
||||
import { useHaptic } from "@/hooks/useHaptic";
|
||||
import { View } from "react-native";
|
||||
import * as Progress from "react-native-progress";
|
||||
import { toast } from "sonner-native";
|
||||
import { ListGroup } from "../list/ListGroup";
|
||||
import { ListItem } from "../list/ListItem";
|
||||
|
||||
@@ -8,11 +8,15 @@ import { ListItem } from "../list/ListItem";
|
||||
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";
|
||||
|
||||
interface Props extends ViewProps {}
|
||||
|
||||
export const SubtitleToggles: React.FC<Props> = ({ ...props }) => {
|
||||
const media = useMedia();
|
||||
const [_, __, pluginSettings] = useSettings();
|
||||
const { settings, updateSettings } = media;
|
||||
const cultures = media.cultures;
|
||||
const { t } = useTranslation();
|
||||
@@ -38,8 +42,11 @@ export const SubtitleToggles: React.FC<Props> = ({ ...props }) => {
|
||||
}
|
||||
>
|
||||
<ListItem title={t("home.settings.subtitles.subtitle_language")}>
|
||||
<DropdownMenu.Root>
|
||||
<DropdownMenu.Trigger>
|
||||
<Dropdown
|
||||
data={[{DisplayName: "None", ThreeLetterISOLanguageName: "none-subs" },...(cultures ?? [])]}
|
||||
keyExtractor={(item) => item?.ThreeLetterISOLanguageName ?? "unknown"}
|
||||
titleExtractor={(item) => item?.DisplayName}
|
||||
title={
|
||||
<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")}
|
||||
@@ -50,48 +57,28 @@ export const SubtitleToggles: React.FC<Props> = ({ ...props }) => {
|
||||
color="#5A5960"
|
||||
/>
|
||||
</TouchableOpacity>
|
||||
</DropdownMenu.Trigger>
|
||||
<DropdownMenu.Content
|
||||
loop={true}
|
||||
side="bottom"
|
||||
align="start"
|
||||
alignOffset={0}
|
||||
avoidCollisions={true}
|
||||
collisionPadding={8}
|
||||
sideOffset={8}
|
||||
>
|
||||
<DropdownMenu.Label>Languages</DropdownMenu.Label>
|
||||
<DropdownMenu.Item
|
||||
key={"none-subs"}
|
||||
onSelect={() => {
|
||||
updateSettings({
|
||||
defaultSubtitleLanguage: null,
|
||||
});
|
||||
}}
|
||||
>
|
||||
<DropdownMenu.ItemTitle>{t("home.settings.subtitles.none")}</DropdownMenu.ItemTitle>
|
||||
</DropdownMenu.Item>
|
||||
{cultures?.map((l) => (
|
||||
<DropdownMenu.Item
|
||||
key={l?.ThreeLetterISOLanguageName ?? "unknown"}
|
||||
onSelect={() => {
|
||||
updateSettings({
|
||||
defaultSubtitleLanguage: l,
|
||||
});
|
||||
}}
|
||||
>
|
||||
<DropdownMenu.ItemTitle>
|
||||
{l.DisplayName}
|
||||
</DropdownMenu.ItemTitle>
|
||||
</DropdownMenu.Item>
|
||||
))}
|
||||
</DropdownMenu.Content>
|
||||
</DropdownMenu.Root>
|
||||
}
|
||||
label="Languages"
|
||||
onSelected={(defaultSubtitleLanguage) =>
|
||||
updateSettings({
|
||||
defaultSubtitleLanguage: defaultSubtitleLanguage.DisplayName === t("home.settings.subtitles.none")
|
||||
? null
|
||||
: defaultSubtitleLanguage
|
||||
})
|
||||
}
|
||||
/>
|
||||
</ListItem>
|
||||
|
||||
<ListItem title={t("home.settings.subtitles.subtitle_mode")}>
|
||||
<DropdownMenu.Root>
|
||||
<DropdownMenu.Trigger>
|
||||
<ListItem
|
||||
title={t("home.settings.subtitles.subtitle_mode")}
|
||||
disabled={pluginSettings?.subtitleMode?.locked}
|
||||
>
|
||||
<Dropdown
|
||||
data={subtitleModes}
|
||||
disabled={pluginSettings?.subtitleMode?.locked}
|
||||
keyExtractor={String}
|
||||
titleExtractor={String}
|
||||
title={
|
||||
<TouchableOpacity className="flex flex-row items-center justify-between py-3 pl-3">
|
||||
<Text className="mr-1 text-[#8E8D91]">
|
||||
{settings?.subtitleMode || "Loading"}
|
||||
@@ -102,68 +89,39 @@ export const SubtitleToggles: React.FC<Props> = ({ ...props }) => {
|
||||
color="#5A5960"
|
||||
/>
|
||||
</TouchableOpacity>
|
||||
</DropdownMenu.Trigger>
|
||||
<DropdownMenu.Content
|
||||
loop={true}
|
||||
side="bottom"
|
||||
align="start"
|
||||
alignOffset={0}
|
||||
avoidCollisions={true}
|
||||
collisionPadding={8}
|
||||
sideOffset={8}
|
||||
>
|
||||
<DropdownMenu.Label>Subtitle Mode</DropdownMenu.Label>
|
||||
{subtitleModes?.map((l) => (
|
||||
<DropdownMenu.Item
|
||||
key={l}
|
||||
onSelect={() => {
|
||||
updateSettings({
|
||||
subtitleMode: l,
|
||||
});
|
||||
}}
|
||||
>
|
||||
<DropdownMenu.ItemTitle>{l}</DropdownMenu.ItemTitle>
|
||||
</DropdownMenu.Item>
|
||||
))}
|
||||
</DropdownMenu.Content>
|
||||
</DropdownMenu.Root>
|
||||
}
|
||||
label="Subtitle Mode"
|
||||
onSelected={(subtitleMode) =>
|
||||
updateSettings({subtitleMode})
|
||||
}
|
||||
/>
|
||||
</ListItem>
|
||||
|
||||
<ListItem title={t("home.settings.subtitles.set_subtitle_track")}>
|
||||
<ListItem
|
||||
title={t("home.settings.subtitles.set_subtitle_track")}
|
||||
disabled={pluginSettings?.rememberSubtitleSelections?.locked}
|
||||
>
|
||||
<Switch
|
||||
value={settings.rememberSubtitleSelections}
|
||||
disabled={pluginSettings?.rememberSubtitleSelections?.locked}
|
||||
onValueChange={(value) =>
|
||||
updateSettings({ rememberSubtitleSelections: value })
|
||||
}
|
||||
/>
|
||||
</ListItem>
|
||||
|
||||
<ListItem title={t("home.settings.subtitles.subtitle_size")}>
|
||||
<View className="flex flex-row items-center">
|
||||
<TouchableOpacity
|
||||
onPress={() =>
|
||||
updateSettings({
|
||||
subtitleSize: Math.max(0, settings.subtitleSize - 5),
|
||||
})
|
||||
}
|
||||
className="w-8 h-8 bg-neutral-800 rounded-l-lg flex items-center justify-center"
|
||||
>
|
||||
<Text>-</Text>
|
||||
</TouchableOpacity>
|
||||
<Text className="w-12 h-8 bg-neutral-800 px-3 py-2 flex items-center justify-center">
|
||||
{settings.subtitleSize}
|
||||
</Text>
|
||||
<TouchableOpacity
|
||||
className="w-8 h-8 bg-neutral-800 rounded-r-lg flex items-center justify-center"
|
||||
onPress={() =>
|
||||
updateSettings({
|
||||
subtitleSize: Math.min(120, settings.subtitleSize + 5),
|
||||
})
|
||||
}
|
||||
>
|
||||
<Text>+</Text>
|
||||
</TouchableOpacity>
|
||||
</View>
|
||||
<ListItem
|
||||
title={t("home.settings.subtitles.subtitle_size")}
|
||||
disabled={pluginSettings?.subtitleSize?.locked}
|
||||
>
|
||||
<Stepper
|
||||
value={settings.subtitleSize}
|
||||
disabled={pluginSettings?.subtitleSize?.locked}
|
||||
step={5}
|
||||
min={0}
|
||||
max={120}
|
||||
onUpdate={(subtitleSize) => updateSettings({subtitleSize})}
|
||||
/>
|
||||
</ListItem>
|
||||
</ListGroup>
|
||||
</View>
|
||||
|
||||
Reference in New Issue
Block a user