diff --git a/components/GlobalModal.tsx b/components/GlobalModal.tsx
index db80675d..d97f2f6d 100644
--- a/components/GlobalModal.tsx
+++ b/components/GlobalModal.tsx
@@ -2,7 +2,6 @@ import {
BottomSheetBackdrop,
type BottomSheetBackdropProps,
BottomSheetModal,
- BottomSheetView,
} from "@gorhom/bottom-sheet";
import { useCallback } from "react";
import { useGlobalModal } from "@/providers/GlobalModalProvider";
@@ -66,7 +65,7 @@ export const GlobalModal = () => {
enablePanDownToClose={modalOptions.enablePanDownToClose}
enableDismissOnClose
>
- {modalState.content}
+ {modalState.content}
);
};
diff --git a/components/PlatformDropdown.tsx b/components/PlatformDropdown.tsx
index 61461146..52ea5fd9 100644
--- a/components/PlatformDropdown.tsx
+++ b/components/PlatformDropdown.tsx
@@ -1,5 +1,6 @@
import { Button, ContextMenu, Host, Picker } from "@expo/ui/swift-ui";
import { Ionicons } from "@expo/vector-icons";
+import { BottomSheetScrollView } from "@gorhom/bottom-sheet";
import React, { useEffect } from "react";
import { Platform, StyleSheet, TouchableOpacity, View } from "react-native";
import { useSafeAreaInsets } from "react-native-safe-area-context";
@@ -156,7 +157,7 @@ const BottomSheetContent: React.FC<{
}));
return (
- (
))}
-
+
);
};
@@ -228,25 +229,48 @@ const PlatformDropdownComponent = ({
const items = [];
- // Add Picker for radio options if present
+ // Add Picker for radio options ONLY if there's a group title
+ // Otherwise render as individual buttons
if (radioOptions.length > 0) {
- items.push(
- opt.label)}
- variant='menu'
- selectedIndex={radioOptions.findIndex(
- (opt) => opt.selected,
- )}
- onOptionSelected={(event: any) => {
- const index = event.nativeEvent.index;
- const selectedOption = radioOptions[index];
- selectedOption?.onPress();
- onOptionSelect?.(selectedOption?.value);
- }}
- />,
- );
+ if (group.title) {
+ // Use Picker for grouped options
+ items.push(
+ opt.label)}
+ variant='menu'
+ selectedIndex={radioOptions.findIndex(
+ (opt) => opt.selected,
+ )}
+ onOptionSelected={(event: any) => {
+ const index = event.nativeEvent.index;
+ const selectedOption = radioOptions[index];
+ selectedOption?.onPress();
+ onOptionSelect?.(selectedOption?.value);
+ }}
+ />,
+ );
+ } else {
+ // Render radio options as direct buttons
+ radioOptions.forEach((option, optionIndex) => {
+ items.push(
+ ,
+ );
+ });
+ }
}
// Add Buttons for toggle options
diff --git a/components/settings/AppLanguageSelector.tsx b/components/settings/AppLanguageSelector.tsx
index c384a897..ac52896c 100644
--- a/components/settings/AppLanguageSelector.tsx
+++ b/components/settings/AppLanguageSelector.tsx
@@ -35,7 +35,6 @@ export const AppLanguageSelector: React.FC = () => {
return [
{
- title: t("home.settings.languages.title"),
options,
},
];
diff --git a/components/settings/AudioToggles.tsx b/components/settings/AudioToggles.tsx
index aa6d2bd6..1a1c1457 100644
--- a/components/settings/AudioToggles.tsx
+++ b/components/settings/AudioToggles.tsx
@@ -49,7 +49,6 @@ export const AudioToggles: React.FC = ({ ...props }) => {
return [
{
- title: t("home.settings.audio.language"),
options,
},
];
diff --git a/components/settings/OtherSettings.tsx b/components/settings/OtherSettings.tsx
index 5e73d99f..61b95aeb 100644
--- a/components/settings/OtherSettings.tsx
+++ b/components/settings/OtherSettings.tsx
@@ -92,7 +92,6 @@ export const OtherSettings: React.FC = () => {
const orientationOptions = useMemo(
() => [
{
- title: t("home.settings.other.orientation"),
options: orientations.map((orientation) => ({
type: "radio" as const,
label: t(ScreenOrientationEnum[orientation]),
@@ -109,7 +108,6 @@ export const OtherSettings: React.FC = () => {
const bitrateOptions = useMemo(
() => [
{
- title: t("home.settings.other.default_quality"),
options: BITRATES.map((bitrate) => ({
type: "radio" as const,
label: bitrate.key,
@@ -125,7 +123,6 @@ export const OtherSettings: React.FC = () => {
const autoPlayEpisodeOptions = useMemo(
() => [
{
- title: t("home.settings.other.max_auto_play_episode_count"),
options: AUTOPLAY_EPISODES_COUNT(t).map((item) => ({
type: "radio" as const,
label: item.key,
diff --git a/components/settings/SubtitleToggles.tsx b/components/settings/SubtitleToggles.tsx
index 749d81be..53fb2a99 100644
--- a/components/settings/SubtitleToggles.tsx
+++ b/components/settings/SubtitleToggles.tsx
@@ -65,7 +65,6 @@ export const SubtitleToggles: React.FC = ({ ...props }) => {
return [
{
- title: t("home.settings.subtitles.language"),
options,
},
];
@@ -82,7 +81,6 @@ export const SubtitleToggles: React.FC = ({ ...props }) => {
return [
{
- title: t("home.settings.subtitles.subtitle_mode"),
options,
},
];
diff --git a/components/video-player/controls/ScaleFactorSelector.tsx b/components/video-player/controls/ScaleFactorSelector.tsx
index b6ed1853..0e5f2b10 100644
--- a/components/video-player/controls/ScaleFactorSelector.tsx
+++ b/components/video-player/controls/ScaleFactorSelector.tsx
@@ -1,8 +1,10 @@
import { Ionicons } from "@expo/vector-icons";
-import React, { useState } from "react";
-import { Platform, TouchableOpacity } from "react-native";
-import { Text } from "@/components/common/Text";
-import { FilterSheet } from "@/components/filters/FilterSheet";
+import React, { useMemo } from "react";
+import { Platform, View } from "react-native";
+import {
+ type OptionGroup,
+ PlatformDropdown,
+} from "@/components/PlatformDropdown";
import { useHaptic } from "@/hooks/useHaptic";
export type ScaleFactor =
@@ -94,56 +96,51 @@ export const ScaleFactorSelector: React.FC = ({
disabled = false,
}) => {
const lightHapticFeedback = useHaptic("light");
- const [open, setOpen] = useState(false);
-
- // Hide on TV platforms
- if (Platform.isTV) return null;
const handleScaleSelect = (scale: ScaleFactor) => {
onScaleChange(scale);
lightHapticFeedback();
};
- const currentOption = SCALE_FACTOR_OPTIONS.find(
- (option) => option.id === currentScale,
- );
+ const optionGroups = useMemo(() => {
+ return [
+ {
+ options: SCALE_FACTOR_OPTIONS.map((option) => ({
+ type: "radio" as const,
+ label: option.label,
+ value: option.id,
+ selected: option.id === currentScale,
+ onPress: () => handleScaleSelect(option.id),
+ disabled,
+ })),
+ },
+ ];
+ // eslint-disable-next-line react-hooks/exhaustive-deps
+ }, [currentScale, disabled]);
- return (
- <>
- (
+ setOpen(true)}
>
-
+
+ ),
+ [disabled],
+ );
- {
- const option = item as ScaleFactorOption;
- return (
- option.label.toLowerCase().includes(query.toLowerCase()) ||
- option.description.toLowerCase().includes(query.toLowerCase())
- );
- }}
- renderItemLabel={(item) => {
- const option = item as ScaleFactorOption;
- return {option.label};
- }}
- set={(vals) => {
- const chosen = vals[0] as ScaleFactorOption | undefined;
- if (chosen) {
- handleScaleSelect(chosen.id);
- }
- }}
- />
- >
+ // Hide on TV platforms
+ if (Platform.isTV) return null;
+
+ return (
+
);
};
diff --git a/components/video-player/controls/VideoScalingModeSelector.tsx b/components/video-player/controls/VideoScalingModeSelector.tsx
index 00866666..2608f04d 100644
--- a/components/video-player/controls/VideoScalingModeSelector.tsx
+++ b/components/video-player/controls/VideoScalingModeSelector.tsx
@@ -1,8 +1,10 @@
import { Ionicons } from "@expo/vector-icons";
-import React, { useState } from "react";
-import { Platform, TouchableOpacity } from "react-native";
-import { Text } from "@/components/common/Text";
-import { FilterSheet } from "@/components/filters/FilterSheet";
+import React, { useMemo } from "react";
+import { Platform, View } from "react-native";
+import {
+ type OptionGroup,
+ PlatformDropdown,
+} from "@/components/PlatformDropdown";
import { useHaptic } from "@/hooks/useHaptic";
export type AspectRatio = "default" | "16:9" | "4:3" | "1:1" | "21:9";
@@ -53,56 +55,51 @@ export const AspectRatioSelector: React.FC = ({
disabled = false,
}) => {
const lightHapticFeedback = useHaptic("light");
- const [open, setOpen] = useState(false);
-
- // Hide on TV platforms
- if (Platform.isTV) return null;
const handleRatioSelect = (ratio: AspectRatio) => {
onRatioChange(ratio);
lightHapticFeedback();
};
- const currentOption = ASPECT_RATIO_OPTIONS.find(
- (option) => option.id === currentRatio,
- );
+ const optionGroups = useMemo(() => {
+ return [
+ {
+ options: ASPECT_RATIO_OPTIONS.map((option) => ({
+ type: "radio" as const,
+ label: option.label,
+ value: option.id,
+ selected: option.id === currentRatio,
+ onPress: () => handleRatioSelect(option.id),
+ disabled,
+ })),
+ },
+ ];
+ // eslint-disable-next-line react-hooks/exhaustive-deps
+ }, [currentRatio, disabled]);
- return (
- <>
- (
+ setOpen(true)}
>
-
+
+ ),
+ [disabled],
+ );
- {
- const option = item as AspectRatioOption;
- return (
- option.label.toLowerCase().includes(query.toLowerCase()) ||
- option.description.toLowerCase().includes(query.toLowerCase())
- );
- }}
- renderItemLabel={(item) => {
- const option = item as AspectRatioOption;
- return {option.label};
- }}
- set={(vals) => {
- const chosen = vals[0] as AspectRatioOption | undefined;
- if (chosen) {
- handleRatioSelect(chosen.id);
- }
- }}
- />
- >
+ // Hide on TV platforms
+ if (Platform.isTV) return null;
+
+ return (
+
);
};
diff --git a/components/video-player/controls/dropdown/DropdownView.tsx b/components/video-player/controls/dropdown/DropdownView.tsx
index 56dfc23f..e1332e43 100644
--- a/components/video-player/controls/dropdown/DropdownView.tsx
+++ b/components/video-player/controls/dropdown/DropdownView.tsx
@@ -1,7 +1,7 @@
import { Ionicons } from "@expo/vector-icons";
import { useLocalSearchParams, useRouter } from "expo-router";
import { useCallback, useMemo, useRef } from "react";
-import { Platform, TouchableOpacity } from "react-native";
+import { Platform, View } from "react-native";
import { BITRATES } from "@/components/BitrateSelector";
import {
type OptionGroup,
@@ -133,9 +133,9 @@ const DropdownView = () => {
// Memoize the trigger to prevent re-renders
const trigger = useMemo(
() => (
-
+
-
+
),
[],
);
diff --git a/components/video-player/controls/useControlsTimeout.ts b/components/video-player/controls/useControlsTimeout.ts
index d1e95b8c..80d41af2 100644
--- a/components/video-player/controls/useControlsTimeout.ts
+++ b/components/video-player/controls/useControlsTimeout.ts
@@ -13,7 +13,7 @@ export const useControlsTimeout = ({
isSliding,
episodeView,
onHideControls,
- timeout = 4000,
+ timeout = 10000,
}: UseControlsTimeoutProps) => {
const controlsTimeoutRef = useRef | null>(null);