From 78f1bcbe01486bab95142cc1068f2c315ec4e30f Mon Sep 17 00:00:00 2001 From: Fredrik Burmester Date: Thu, 21 Aug 2025 11:35:14 +0200 Subject: [PATCH] fix: change to use new bottom sheet --- components/BitrateSelector.tsx | 79 ++++---- .../controls/ScaleFactorSelector.tsx | 71 ++++---- .../controls/VideoScalingModeSelector.tsx | 74 ++++---- .../controls/dropdown/DropdownView.tsx | 170 ++++++++---------- 4 files changed, 183 insertions(+), 211 deletions(-) diff --git a/components/BitrateSelector.tsx b/components/BitrateSelector.tsx index d52a6bf6..e73722d1 100644 --- a/components/BitrateSelector.tsx +++ b/components/BitrateSelector.tsx @@ -1,9 +1,9 @@ -import { Platform, TouchableOpacity, View } from "react-native"; - -const DropdownMenu = !Platform.isTV ? require("zeego/dropdown-menu") : null; - -import { useMemo } from "react"; +import React, { useMemo } from "react"; import { useTranslation } from "react-i18next"; +import { Platform, View } from "react-native"; +import SelectBottomSheet, { + type SelectOptionGroup, +} from "./common/SelectBottomSheet"; import { Text } from "./common/Text"; export type Bitrate = { @@ -61,6 +61,7 @@ export const BitrateSelector: React.FC = ({ ...props }) => { const isTv = Platform.isTV; + const { t } = useTranslation(); const sorted = useMemo(() => { if (inverted) @@ -76,7 +77,32 @@ export const BitrateSelector: React.FC = ({ ); }, [inverted]); - const { t } = useTranslation(); + const optionGroups = useMemo((): SelectOptionGroup[] => { + return [ + { + id: "bitrates", + title: "Quality", + options: sorted.map((bitrate) => ({ + id: bitrate.key, + label: bitrate.key, + value: bitrate.value, + selected: selected?.value === bitrate.value, + onSelect: () => onChange(bitrate), + })), + }, + ]; + }, [sorted, selected, onChange]); + + const customTrigger = ( + + {t("item_card.quality")} + + + {BITRATES.find((b) => b.value === selected?.value)?.key} + + + + ); if (isTv) return null; @@ -88,41 +114,12 @@ export const BitrateSelector: React.FC = ({ maxWidth: 200, }} > - - - - - {t("item_card.quality")} - - - - {BITRATES.find((b) => b.value === selected?.value)?.key} - - - - - - Bitrates - {sorted.map((b) => ( - { - onChange(b); - }} - > - {b.key} - - ))} - - + ); }; diff --git a/components/video-player/controls/ScaleFactorSelector.tsx b/components/video-player/controls/ScaleFactorSelector.tsx index c251a59a..ff1e35e5 100644 --- a/components/video-player/controls/ScaleFactorSelector.tsx +++ b/components/video-player/controls/ScaleFactorSelector.tsx @@ -1,10 +1,11 @@ import { Ionicons } from "@expo/vector-icons"; -import React from "react"; -import { Platform, TouchableOpacity } from "react-native"; +import React, { useMemo } from "react"; +import { View } from "react-native"; +import SelectBottomSheet, { + type SelectOptionGroup, +} from "@/components/common/SelectBottomSheet"; import { useHaptic } from "@/hooks/useHaptic"; -const DropdownMenu = !Platform.isTV ? require("zeego/dropdown-menu") : null; - export type ScaleFactor = | 1.0 | 1.1 @@ -95,41 +96,43 @@ export const ScaleFactorSelector: React.FC = ({ }) => { const lightHapticFeedback = useHaptic("light"); - // Hide on TV platforms since zeego doesn't support TV - if (Platform.isTV || !DropdownMenu) return null; - const handleScaleSelect = (scale: ScaleFactor) => { onScaleChange(scale); lightHapticFeedback(); }; + const optionGroups = useMemo((): SelectOptionGroup[] => { + return [ + { + id: "scale-factor", + title: "Scale Factor", + options: SCALE_FACTOR_OPTIONS.map((option) => ({ + id: option.id.toString(), + label: option.label, + value: option.id, + selected: currentScale === option.id, + onSelect: () => handleScaleSelect(option.id), + })), + }, + ]; + }, [currentScale, handleScaleSelect]); + + if (disabled) { + return ( + + + + ); + } + return ( - - - - - - - - - Scale Factor - - - {SCALE_FACTOR_OPTIONS.map((option) => ( - handleScaleSelect(option.id)} - > - {option.label} - - - ))} - - + ); }; diff --git a/components/video-player/controls/VideoScalingModeSelector.tsx b/components/video-player/controls/VideoScalingModeSelector.tsx index 82874abf..e43f880f 100644 --- a/components/video-player/controls/VideoScalingModeSelector.tsx +++ b/components/video-player/controls/VideoScalingModeSelector.tsx @@ -1,10 +1,11 @@ import { Ionicons } from "@expo/vector-icons"; -import React from "react"; -import { Platform, TouchableOpacity } from "react-native"; +import React, { useMemo } from "react"; +import { View } from "react-native"; +import SelectBottomSheet, { + type SelectOptionGroup, +} from "@/components/common/SelectBottomSheet"; import { useHaptic } from "@/hooks/useHaptic"; -const DropdownMenu = !Platform.isTV ? require("zeego/dropdown-menu") : null; - export type AspectRatio = "default" | "16:9" | "4:3" | "1:1" | "21:9"; interface AspectRatioSelectorProps { @@ -54,44 +55,43 @@ export const AspectRatioSelector: React.FC = ({ }) => { const lightHapticFeedback = useHaptic("light"); - // Hide on TV platforms since zeego doesn't support TV - if (Platform.isTV || !DropdownMenu) return null; - const handleRatioSelect = (ratio: AspectRatio) => { onRatioChange(ratio); lightHapticFeedback(); }; + const optionGroups = useMemo((): SelectOptionGroup[] => { + return [ + { + id: "aspect-ratio", + title: "Aspect Ratio", + options: ASPECT_RATIO_OPTIONS.map((option) => ({ + id: option.id, + label: option.label, + value: option.id, + selected: currentRatio === option.id, + onSelect: () => handleRatioSelect(option.id), + })), + }, + ]; + }, [currentRatio, handleRatioSelect]); + + if (disabled) { + return ( + + + + ); + } + return ( - - - - - - - - - Aspect Ratio - - - {ASPECT_RATIO_OPTIONS.map((option) => ( - handleRatioSelect(option.id)} - > - {option.label} - - {option.description} - - - - ))} - - + ); }; diff --git a/components/video-player/controls/dropdown/DropdownView.tsx b/components/video-player/controls/dropdown/DropdownView.tsx index ac7501c7..3c37718f 100644 --- a/components/video-player/controls/dropdown/DropdownView.tsx +++ b/components/video-player/controls/dropdown/DropdownView.tsx @@ -1,11 +1,9 @@ -import { Ionicons } from "@expo/vector-icons"; -import { useCallback } from "react"; -import { Platform, TouchableOpacity } from "react-native"; - -const DropdownMenu = !Platform.isTV ? require("zeego/dropdown-menu") : null; - import { useLocalSearchParams, useRouter } from "expo-router"; +import { useCallback, useMemo } from "react"; import { BITRATES } from "@/components/BitrateSelector"; +import SelectBottomSheet, { + type SelectOptionGroup, +} from "@/components/common/SelectBottomSheet"; import { useControlContext } from "../contexts/ControlContext"; import { useVideoContext } from "../contexts/VideoContext"; @@ -48,100 +46,74 @@ const DropdownView = () => { [item, mediaSource, subtitleIndex, audioIndex, playbackPosition], ); + const optionGroups = useMemo((): SelectOptionGroup[] => { + const groups: SelectOptionGroup[] = []; + + // Quality group (only if not offline) + if (!isOffline && BITRATES) { + groups.push({ + id: "quality", + title: "Quality", + options: BITRATES.map((bitrate) => ({ + id: `quality-${bitrate.value}`, + label: bitrate.key, + value: bitrate.value, + selected: bitrateValue === (bitrate.value?.toString() ?? ""), + onSelect: () => changeBitrate(bitrate.value?.toString() ?? ""), + })), + }); + } + + // Subtitle group + if (subtitleTracks && subtitleTracks.length > 0) { + groups.push({ + id: "subtitle", + title: "Subtitle", + options: subtitleTracks.map((sub) => ({ + id: `subtitle-${sub.index}`, + label: sub.name, + value: sub.index, + selected: subtitleIndex === sub.index.toString(), + onSelect: () => sub.setTrack(), + })), + }); + } + + // Audio group + if (audioTracks && audioTracks.length > 0) { + groups.push({ + id: "audio", + title: "Audio", + options: audioTracks.map((track) => ({ + id: `audio-${track.index}`, + label: track.name, + value: track.index, + selected: audioIndex === track.index.toString(), + onSelect: () => track.setTrack(), + })), + }); + } + + return groups; + }, [ + isOffline, + bitrateValue, + subtitleTracks, + subtitleIndex, + audioTracks, + audioIndex, + changeBitrate, + ]); + return ( - - - - - - - - {!isOffline && ( - - - Quality - - - {BITRATES?.map((bitrate, idx: number) => ( - - changeBitrate(bitrate.value?.toString() ?? "") - } - > - - {bitrate.key} - - - ))} - - - )} - - - Subtitle - - - {subtitleTracks?.map((sub, idx: number) => ( - sub.setTrack()} - > - - {sub.name} - - - ))} - - - - - Audio - - - {audioTracks?.map((track, idx: number) => ( - track.setTrack()} - > - - {track.name} - - - ))} - - - - + ); };