mirror of
https://github.com/streamyfin/streamyfin.git
synced 2026-06-01 19:48:28 +01:00
chore(bottom-sheet): Migrate to expo-ui bottom sheet
This commit is contained in:
@@ -1,4 +1,3 @@
|
|||||||
import { BottomSheetModal } from "@gorhom/bottom-sheet";
|
|
||||||
import { useNavigation } from "expo-router";
|
import { useNavigation } from "expo-router";
|
||||||
import { useAtom } from "jotai";
|
import { useAtom } from "jotai";
|
||||||
import { useEffect, useMemo, useRef, useState } from "react";
|
import { useEffect, useMemo, useRef, useState } from "react";
|
||||||
@@ -18,6 +17,7 @@ import { useDownload } from "@/providers/DownloadProvider";
|
|||||||
import { type DownloadedItem } from "@/providers/Downloads/types";
|
import { type DownloadedItem } from "@/providers/Downloads/types";
|
||||||
import { OfflineModeProvider } from "@/providers/OfflineModeProvider";
|
import { OfflineModeProvider } from "@/providers/OfflineModeProvider";
|
||||||
import { queueAtom } from "@/utils/atoms/queue";
|
import { queueAtom } from "@/utils/atoms/queue";
|
||||||
|
import type { BottomSheetMethods } from "@/utils/expoUiBottomSheet";
|
||||||
import { writeToLog } from "@/utils/log";
|
import { writeToLog } from "@/utils/log";
|
||||||
|
|
||||||
export default function DownloadsPage() {
|
export default function DownloadsPage() {
|
||||||
@@ -26,7 +26,7 @@ export default function DownloadsPage() {
|
|||||||
const [_queue, _setQueue] = useAtom(queueAtom);
|
const [_queue, _setQueue] = useAtom(queueAtom);
|
||||||
const { downloadedItems, deleteFileByType, deleteAllFiles } = useDownload();
|
const { downloadedItems, deleteFileByType, deleteAllFiles } = useDownload();
|
||||||
const router = useRouter();
|
const router = useRouter();
|
||||||
const bottomSheetModalRef = useRef<BottomSheetModal>(null);
|
const bottomSheetModalRef = useRef<BottomSheetMethods>(null);
|
||||||
|
|
||||||
const [showMigration, setShowMigration] = useState(false);
|
const [showMigration, setShowMigration] = useState(false);
|
||||||
|
|
||||||
|
|||||||
@@ -1,11 +1,4 @@
|
|||||||
import { Ionicons } from "@expo/vector-icons";
|
import { Ionicons } from "@expo/vector-icons";
|
||||||
import {
|
|
||||||
BottomSheetBackdrop,
|
|
||||||
type BottomSheetBackdropProps,
|
|
||||||
BottomSheetModal,
|
|
||||||
BottomSheetTextInput,
|
|
||||||
BottomSheetView,
|
|
||||||
} from "@gorhom/bottom-sheet";
|
|
||||||
import { useQuery } from "@tanstack/react-query";
|
import { useQuery } from "@tanstack/react-query";
|
||||||
import { Image } from "expo-image";
|
import { Image } from "expo-image";
|
||||||
import { useLocalSearchParams, useNavigation } from "expo-router";
|
import { useLocalSearchParams, useNavigation } from "expo-router";
|
||||||
@@ -31,6 +24,12 @@ import { ItemActions } from "@/components/series/SeriesActions";
|
|||||||
import useRouter from "@/hooks/useAppRouter";
|
import useRouter from "@/hooks/useAppRouter";
|
||||||
import { useJellyseerr } from "@/hooks/useJellyseerr";
|
import { useJellyseerr } from "@/hooks/useJellyseerr";
|
||||||
import { useJellyseerrCanRequest } from "@/utils/_jellyseerr/useJellyseerrCanRequest";
|
import { useJellyseerrCanRequest } from "@/utils/_jellyseerr/useJellyseerrCanRequest";
|
||||||
|
import {
|
||||||
|
type BottomSheetMethods,
|
||||||
|
BottomSheetModal,
|
||||||
|
BottomSheetTextInput,
|
||||||
|
BottomSheetView,
|
||||||
|
} from "@/utils/expoUiBottomSheet";
|
||||||
import { ANIME_KEYWORD_ID } from "@/utils/jellyseerr/server/api/themoviedb/constants";
|
import { ANIME_KEYWORD_ID } from "@/utils/jellyseerr/server/api/themoviedb/constants";
|
||||||
import {
|
import {
|
||||||
type IssueType,
|
type IssueType,
|
||||||
@@ -76,8 +75,8 @@ const MobilePage: React.FC = () => {
|
|||||||
const [issueMessage, setIssueMessage] = useState<string>();
|
const [issueMessage, setIssueMessage] = useState<string>();
|
||||||
const [requestBody, _setRequestBody] = useState<MediaRequestBody>();
|
const [requestBody, _setRequestBody] = useState<MediaRequestBody>();
|
||||||
const [issueTypeDropdownOpen, setIssueTypeDropdownOpen] = useState(false);
|
const [issueTypeDropdownOpen, setIssueTypeDropdownOpen] = useState(false);
|
||||||
const advancedReqModalRef = useRef<BottomSheetModal>(null);
|
const advancedReqModalRef = useRef<BottomSheetMethods>(null);
|
||||||
const bottomSheetModalRef = useRef<BottomSheetModal>(null);
|
const bottomSheetModalRef = useRef<BottomSheetMethods>(null);
|
||||||
|
|
||||||
const {
|
const {
|
||||||
data: details,
|
data: details,
|
||||||
@@ -143,17 +142,6 @@ const MobilePage: React.FC = () => {
|
|||||||
}
|
}
|
||||||
}, [jellyseerrApi, pendingRequest, refetch, t]);
|
}, [jellyseerrApi, pendingRequest, refetch, t]);
|
||||||
|
|
||||||
const renderBackdrop = useCallback(
|
|
||||||
(props: BottomSheetBackdropProps) => (
|
|
||||||
<BottomSheetBackdrop
|
|
||||||
{...props}
|
|
||||||
disappearsOnIndex={-1}
|
|
||||||
appearsOnIndex={0}
|
|
||||||
/>
|
|
||||||
),
|
|
||||||
[],
|
|
||||||
);
|
|
||||||
|
|
||||||
const submitIssue = useCallback(() => {
|
const submitIssue = useCallback(() => {
|
||||||
if (result.id && issueType && issueMessage && details) {
|
if (result.id && issueType && issueMessage && details) {
|
||||||
jellyseerrApi
|
jellyseerrApi
|
||||||
@@ -478,6 +466,7 @@ const MobilePage: React.FC = () => {
|
|||||||
// This is till it's fixed because the menu isn't selectable on TV
|
// This is till it's fixed because the menu isn't selectable on TV
|
||||||
<BottomSheetModal
|
<BottomSheetModal
|
||||||
ref={bottomSheetModalRef}
|
ref={bottomSheetModalRef}
|
||||||
|
enablePanDownToClose
|
||||||
enableDynamicSizing
|
enableDynamicSizing
|
||||||
handleIndicatorStyle={{
|
handleIndicatorStyle={{
|
||||||
backgroundColor: "white",
|
backgroundColor: "white",
|
||||||
@@ -485,8 +474,6 @@ const MobilePage: React.FC = () => {
|
|||||||
backgroundStyle={{
|
backgroundStyle={{
|
||||||
backgroundColor: "#171717",
|
backgroundColor: "#171717",
|
||||||
}}
|
}}
|
||||||
backdropComponent={renderBackdrop}
|
|
||||||
stackBehavior='push'
|
|
||||||
onDismiss={handleIssueModalDismiss}
|
onDismiss={handleIssueModalDismiss}
|
||||||
>
|
>
|
||||||
<BottomSheetView>
|
<BottomSheetView>
|
||||||
|
|||||||
@@ -1,6 +1,5 @@
|
|||||||
import { ExpoAvRoutePickerView } from "@douglowder/expo-av-route-picker-view";
|
import { ExpoAvRoutePickerView } from "@douglowder/expo-av-route-picker-view";
|
||||||
import { Ionicons } from "@expo/vector-icons";
|
import { Ionicons } from "@expo/vector-icons";
|
||||||
import { BottomSheetModalProvider } from "@gorhom/bottom-sheet";
|
|
||||||
import type {
|
import type {
|
||||||
BaseItemDto,
|
BaseItemDto,
|
||||||
MediaSourceInfo,
|
MediaSourceInfo,
|
||||||
@@ -222,7 +221,6 @@ export default function NowPlayingScreen() {
|
|||||||
|
|
||||||
if (!currentTrack) {
|
if (!currentTrack) {
|
||||||
return (
|
return (
|
||||||
<BottomSheetModalProvider>
|
|
||||||
<View
|
<View
|
||||||
className='flex-1 bg-[#121212] items-center justify-center'
|
className='flex-1 bg-[#121212] items-center justify-center'
|
||||||
style={{
|
style={{
|
||||||
@@ -232,12 +230,10 @@ export default function NowPlayingScreen() {
|
|||||||
>
|
>
|
||||||
<Text className='text-neutral-500'>No track playing</Text>
|
<Text className='text-neutral-500'>No track playing</Text>
|
||||||
</View>
|
</View>
|
||||||
</BottomSheetModalProvider>
|
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<BottomSheetModalProvider>
|
|
||||||
<View
|
<View
|
||||||
className='flex-1 bg-[#121212]'
|
className='flex-1 bg-[#121212]'
|
||||||
style={{
|
style={{
|
||||||
@@ -348,7 +344,6 @@ export default function NowPlayingScreen() {
|
|||||||
initialTrackId={currentTrack?.Id}
|
initialTrackId={currentTrack?.Id}
|
||||||
/>
|
/>
|
||||||
</View>
|
</View>
|
||||||
</BottomSheetModalProvider>
|
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -1,6 +1,5 @@
|
|||||||
import "@/augmentations";
|
import "@/augmentations";
|
||||||
import { ActionSheetProvider } from "@expo/react-native-action-sheet";
|
import { ActionSheetProvider } from "@expo/react-native-action-sheet";
|
||||||
import { BottomSheetModalProvider } from "@gorhom/bottom-sheet";
|
|
||||||
import NetInfo from "@react-native-community/netinfo";
|
import NetInfo from "@react-native-community/netinfo";
|
||||||
import { createSyncStoragePersister } from "@tanstack/query-sync-storage-persister";
|
import { createSyncStoragePersister } from "@tanstack/query-sync-storage-persister";
|
||||||
import { onlineManager, QueryClient } from "@tanstack/react-query";
|
import { onlineManager, QueryClient } from "@tanstack/react-query";
|
||||||
@@ -412,7 +411,6 @@ function Layout() {
|
|||||||
<DownloadProvider>
|
<DownloadProvider>
|
||||||
<MusicPlayerProvider>
|
<MusicPlayerProvider>
|
||||||
<GlobalModalProvider>
|
<GlobalModalProvider>
|
||||||
<BottomSheetModalProvider>
|
|
||||||
<IntroSheetProvider>
|
<IntroSheetProvider>
|
||||||
<ThemeProvider value={DarkTheme}>
|
<ThemeProvider value={DarkTheme}>
|
||||||
<SystemBars style='light' hidden={false} />
|
<SystemBars style='light' hidden={false} />
|
||||||
@@ -532,7 +530,6 @@ function Layout() {
|
|||||||
{!Platform.isTV && <GlobalModal />}
|
{!Platform.isTV && <GlobalModal />}
|
||||||
</ThemeProvider>
|
</ThemeProvider>
|
||||||
</IntroSheetProvider>
|
</IntroSheetProvider>
|
||||||
</BottomSheetModalProvider>
|
|
||||||
</GlobalModalProvider>
|
</GlobalModalProvider>
|
||||||
</MusicPlayerProvider>
|
</MusicPlayerProvider>
|
||||||
</DownloadProvider>
|
</DownloadProvider>
|
||||||
|
|||||||
6
bun.lock
6
bun.lock
@@ -1,6 +1,5 @@
|
|||||||
{
|
{
|
||||||
"lockfileVersion": 1,
|
"lockfileVersion": 1,
|
||||||
"configVersion": 1,
|
|
||||||
"workspaces": {
|
"workspaces": {
|
||||||
"": {
|
"": {
|
||||||
"name": "streamyfin",
|
"name": "streamyfin",
|
||||||
@@ -11,7 +10,6 @@
|
|||||||
"@expo/react-native-action-sheet": "^4.1.1",
|
"@expo/react-native-action-sheet": "^4.1.1",
|
||||||
"@expo/ui": "~56.0.14",
|
"@expo/ui": "~56.0.14",
|
||||||
"@expo/vector-icons": "^15.0.3",
|
"@expo/vector-icons": "^15.0.3",
|
||||||
"@gorhom/bottom-sheet": "5.2.8",
|
|
||||||
"@jellyfin/sdk": "^0.13.0",
|
"@jellyfin/sdk": "^0.13.0",
|
||||||
"@react-native-community/netinfo": "^12.0.0",
|
"@react-native-community/netinfo": "^12.0.0",
|
||||||
"@react-navigation/material-top-tabs": "7.4.28",
|
"@react-navigation/material-top-tabs": "7.4.28",
|
||||||
@@ -365,10 +363,6 @@
|
|||||||
|
|
||||||
"@expo/xcpretty": ["@expo/xcpretty@4.4.4", "", { "dependencies": { "@babel/code-frame": "^7.20.0", "chalk": "^4.1.0", "js-yaml": "^4.1.0" }, "bin": { "excpretty": "build/cli.js" } }, "sha512-4aQzz9vgxcNXFfo/iyNgDDYfsU5XGKKxWxZopw0cVotHiW+U8IJbIxMaxsINs6bHhtkG3StKNPcOrn3eBuxKPw=="],
|
"@expo/xcpretty": ["@expo/xcpretty@4.4.4", "", { "dependencies": { "@babel/code-frame": "^7.20.0", "chalk": "^4.1.0", "js-yaml": "^4.1.0" }, "bin": { "excpretty": "build/cli.js" } }, "sha512-4aQzz9vgxcNXFfo/iyNgDDYfsU5XGKKxWxZopw0cVotHiW+U8IJbIxMaxsINs6bHhtkG3StKNPcOrn3eBuxKPw=="],
|
||||||
|
|
||||||
"@gorhom/bottom-sheet": ["@gorhom/bottom-sheet@5.2.8", "", { "dependencies": { "@gorhom/portal": "1.0.14", "invariant": "^2.2.4" }, "peerDependencies": { "@types/react": "*", "@types/react-native": "*", "react": "*", "react-native": "*", "react-native-gesture-handler": ">=2.16.1", "react-native-reanimated": ">=3.16.0 || >=4.0.0-" }, "optionalPeers": ["@types/react", "@types/react-native"] }, "sha512-+N27SMpbBxXZQ/IA2nlEV6RGxL/qSFHKfdFKcygvW+HqPG5jVNb1OqehLQsGfBP+Up42i0gW5ppI+DhpB7UCzA=="],
|
|
||||||
|
|
||||||
"@gorhom/portal": ["@gorhom/portal@1.0.14", "", { "dependencies": { "nanoid": "^3.3.1" }, "peerDependencies": { "react": "*", "react-native": "*" } }, "sha512-MXyL4xvCjmgaORr/rtryDNFy3kU4qUbKlwtQqqsygd0xX3mhKjOLn6mQK8wfu0RkoE0pBE0nAasRoHua+/QZ7A=="],
|
|
||||||
|
|
||||||
"@hapi/hoek": ["@hapi/hoek@9.3.0", "", {}, "sha512-/c6rf4UJlmHlC9b5BaNvzAcFv7HZ2QHaV0D4/HNlBdvFnvQq8RI4kYdhyPCl7Xj+oWvTWQ8ujhqS53LIgAe6KQ=="],
|
"@hapi/hoek": ["@hapi/hoek@9.3.0", "", {}, "sha512-/c6rf4UJlmHlC9b5BaNvzAcFv7HZ2QHaV0D4/HNlBdvFnvQq8RI4kYdhyPCl7Xj+oWvTWQ8ujhqS53LIgAe6KQ=="],
|
||||||
|
|
||||||
"@hapi/topo": ["@hapi/topo@5.1.0", "", { "dependencies": { "@hapi/hoek": "^9.0.0" } }, "sha512-foQZKJig7Ob0BMAYBfcJk8d77QtOe7Wo4ox7ff1lQYoNNAb6jwcY1ncdoy2e9wQZzvNy7ODZCYJkK8kzmcAnAg=="],
|
"@hapi/topo": ["@hapi/topo@5.1.0", "", { "dependencies": { "@hapi/hoek": "^9.0.0" } }, "sha512-foQZKJig7Ob0BMAYBfcJk8d77QtOe7Wo4ox7ff1lQYoNNAb6jwcY1ncdoy2e9wQZzvNy7ODZCYJkK8kzmcAnAg=="],
|
||||||
|
|||||||
@@ -1,10 +1,4 @@
|
|||||||
import { Ionicons } from "@expo/vector-icons";
|
import { Ionicons } from "@expo/vector-icons";
|
||||||
import {
|
|
||||||
BottomSheetBackdrop,
|
|
||||||
type BottomSheetBackdropProps,
|
|
||||||
BottomSheetModal,
|
|
||||||
BottomSheetView,
|
|
||||||
} from "@gorhom/bottom-sheet";
|
|
||||||
import type React from "react";
|
import type React from "react";
|
||||||
import { useCallback, useEffect, useMemo, useRef } from "react";
|
import { useCallback, useEffect, useMemo, useRef } from "react";
|
||||||
import { useTranslation } from "react-i18next";
|
import { useTranslation } from "react-i18next";
|
||||||
@@ -12,6 +6,11 @@ import { Alert, Platform, TouchableOpacity, View } from "react-native";
|
|||||||
import { Swipeable } from "react-native-gesture-handler";
|
import { Swipeable } from "react-native-gesture-handler";
|
||||||
import { useSafeAreaInsets } from "react-native-safe-area-context";
|
import { useSafeAreaInsets } from "react-native-safe-area-context";
|
||||||
import { Colors } from "@/constants/Colors";
|
import { Colors } from "@/constants/Colors";
|
||||||
|
import {
|
||||||
|
type BottomSheetMethods,
|
||||||
|
BottomSheetModal,
|
||||||
|
BottomSheetView,
|
||||||
|
} from "@/utils/expoUiBottomSheet";
|
||||||
import {
|
import {
|
||||||
deleteAccountCredential,
|
deleteAccountCredential,
|
||||||
type SavedServer,
|
type SavedServer,
|
||||||
@@ -39,7 +38,7 @@ export const AccountsSheet: React.FC<AccountsSheetProps> = ({
|
|||||||
}) => {
|
}) => {
|
||||||
const { t } = useTranslation();
|
const { t } = useTranslation();
|
||||||
const insets = useSafeAreaInsets();
|
const insets = useSafeAreaInsets();
|
||||||
const bottomSheetModalRef = useRef<BottomSheetModal>(null);
|
const bottomSheetModalRef = useRef<BottomSheetMethods>(null);
|
||||||
|
|
||||||
const isAndroid = Platform.OS === "android";
|
const isAndroid = Platform.OS === "android";
|
||||||
const snapPoints = useMemo(
|
const snapPoints = useMemo(
|
||||||
@@ -64,17 +63,6 @@ export const AccountsSheet: React.FC<AccountsSheetProps> = ({
|
|||||||
[setOpen],
|
[setOpen],
|
||||||
);
|
);
|
||||||
|
|
||||||
const renderBackdrop = useCallback(
|
|
||||||
(props: BottomSheetBackdropProps) => (
|
|
||||||
<BottomSheetBackdrop
|
|
||||||
{...props}
|
|
||||||
disappearsOnIndex={-1}
|
|
||||||
appearsOnIndex={0}
|
|
||||||
/>
|
|
||||||
),
|
|
||||||
[],
|
|
||||||
);
|
|
||||||
|
|
||||||
const handleDeleteAccount = async (account: SavedServerAccount) => {
|
const handleDeleteAccount = async (account: SavedServerAccount) => {
|
||||||
if (!server) return;
|
if (!server) return;
|
||||||
|
|
||||||
@@ -118,15 +106,16 @@ export const AccountsSheet: React.FC<AccountsSheetProps> = ({
|
|||||||
);
|
);
|
||||||
|
|
||||||
if (!server) return null;
|
if (!server) return null;
|
||||||
|
if (Platform.isTV) return null;
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<BottomSheetModal
|
<BottomSheetModal
|
||||||
ref={bottomSheetModalRef}
|
ref={bottomSheetModalRef}
|
||||||
|
enablePanDownToClose
|
||||||
snapPoints={snapPoints}
|
snapPoints={snapPoints}
|
||||||
onChange={handleSheetChanges}
|
onChange={handleSheetChanges}
|
||||||
handleIndicatorStyle={{ backgroundColor: "white" }}
|
handleIndicatorStyle={{ backgroundColor: "white" }}
|
||||||
backgroundStyle={{ backgroundColor: "#171717" }}
|
backgroundStyle={{ backgroundColor: "#171717" }}
|
||||||
backdropComponent={renderBackdrop}
|
|
||||||
>
|
>
|
||||||
<BottomSheetView
|
<BottomSheetView
|
||||||
style={{
|
style={{
|
||||||
|
|||||||
@@ -1,10 +1,4 @@
|
|||||||
import Ionicons from "@expo/vector-icons/Ionicons";
|
import Ionicons from "@expo/vector-icons/Ionicons";
|
||||||
import {
|
|
||||||
BottomSheetBackdrop,
|
|
||||||
type BottomSheetBackdropProps,
|
|
||||||
BottomSheetModal,
|
|
||||||
BottomSheetView,
|
|
||||||
} from "@gorhom/bottom-sheet";
|
|
||||||
import type {
|
import type {
|
||||||
BaseItemDto,
|
BaseItemDto,
|
||||||
MediaSourceInfo,
|
MediaSourceInfo,
|
||||||
@@ -23,6 +17,11 @@ import { useDownload } from "@/providers/DownloadProvider";
|
|||||||
import { apiAtom, userAtom } from "@/providers/JellyfinProvider";
|
import { apiAtom, userAtom } from "@/providers/JellyfinProvider";
|
||||||
import { queueAtom } from "@/utils/atoms/queue";
|
import { queueAtom } from "@/utils/atoms/queue";
|
||||||
import { useSettings } from "@/utils/atoms/settings";
|
import { useSettings } from "@/utils/atoms/settings";
|
||||||
|
import {
|
||||||
|
type BottomSheetMethods,
|
||||||
|
BottomSheetModal,
|
||||||
|
BottomSheetView,
|
||||||
|
} from "@/utils/expoUiBottomSheet";
|
||||||
import { getDefaultPlaySettings } from "@/utils/jellyfin/getDefaultPlaySettings";
|
import { getDefaultPlaySettings } from "@/utils/jellyfin/getDefaultPlaySettings";
|
||||||
import { getDownloadUrl } from "@/utils/jellyfin/media/getDownloadUrl";
|
import { getDownloadUrl } from "@/utils/jellyfin/media/getDownloadUrl";
|
||||||
import { AudioTrackSelector } from "./AudioTrackSelector";
|
import { AudioTrackSelector } from "./AudioTrackSelector";
|
||||||
@@ -90,7 +89,7 @@ export const DownloadItems: React.FC<DownloadProps> = ({
|
|||||||
[user],
|
[user],
|
||||||
);
|
);
|
||||||
|
|
||||||
const bottomSheetModalRef = useRef<BottomSheetModal>(null);
|
const bottomSheetModalRef = useRef<BottomSheetMethods>(null);
|
||||||
|
|
||||||
const handlePresentModalPress = useCallback(() => {
|
const handlePresentModalPress = useCallback(() => {
|
||||||
bottomSheetModalRef.current?.present();
|
bottomSheetModalRef.current?.present();
|
||||||
@@ -317,17 +316,6 @@ export const DownloadItems: React.FC<DownloadProps> = ({
|
|||||||
}
|
}
|
||||||
}, [closeModal, initiateDownload, itemsToDownload, userCanDownload]);
|
}, [closeModal, initiateDownload, itemsToDownload, userCanDownload]);
|
||||||
|
|
||||||
const renderBackdrop = useCallback(
|
|
||||||
(props: BottomSheetBackdropProps) => (
|
|
||||||
<BottomSheetBackdrop
|
|
||||||
{...props}
|
|
||||||
disappearsOnIndex={-1}
|
|
||||||
appearsOnIndex={0}
|
|
||||||
/>
|
|
||||||
),
|
|
||||||
[],
|
|
||||||
);
|
|
||||||
|
|
||||||
const renderButtonContent = () => {
|
const renderButtonContent = () => {
|
||||||
// For single item downloads, show progress if item is being processed
|
// For single item downloads, show progress if item is being processed
|
||||||
// For multi-item downloads (season/series), show progress only if 2+ items are in progress or queued
|
// For multi-item downloads (season/series), show progress only if 2+ items are in progress or queued
|
||||||
@@ -375,6 +363,8 @@ export const DownloadItems: React.FC<DownloadProps> = ({
|
|||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
|
if (Platform.isTV) return null;
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<View {...props}>
|
<View {...props}>
|
||||||
<RoundButton size={size} onPress={onButtonPress}>
|
<RoundButton size={size} onPress={onButtonPress}>
|
||||||
@@ -390,10 +380,7 @@ export const DownloadItems: React.FC<DownloadProps> = ({
|
|||||||
backgroundColor: "#171717",
|
backgroundColor: "#171717",
|
||||||
}}
|
}}
|
||||||
onChange={handleSheetChanges}
|
onChange={handleSheetChanges}
|
||||||
backdropComponent={renderBackdrop}
|
|
||||||
enablePanDownToClose
|
enablePanDownToClose
|
||||||
enableDismissOnClose
|
|
||||||
android_keyboardInputMode='adjustResize'
|
|
||||||
keyboardBehavior='interactive'
|
keyboardBehavior='interactive'
|
||||||
keyboardBlurBehavior='restore'
|
keyboardBlurBehavior='restore'
|
||||||
>
|
>
|
||||||
|
|||||||
@@ -1,10 +1,7 @@
|
|||||||
import {
|
|
||||||
BottomSheetBackdrop,
|
|
||||||
type BottomSheetBackdropProps,
|
|
||||||
BottomSheetModal,
|
|
||||||
} from "@gorhom/bottom-sheet";
|
|
||||||
import { useCallback, useEffect } from "react";
|
import { useCallback, useEffect } from "react";
|
||||||
|
import { Platform } from "react-native";
|
||||||
import { useGlobalModal } from "@/providers/GlobalModalProvider";
|
import { useGlobalModal } from "@/providers/GlobalModalProvider";
|
||||||
|
import { BottomSheetModal } from "@/utils/expoUiBottomSheet";
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* GlobalModal Component
|
* GlobalModal Component
|
||||||
@@ -12,8 +9,7 @@ import { useGlobalModal } from "@/providers/GlobalModalProvider";
|
|||||||
* This component renders a global bottom sheet modal that can be controlled
|
* This component renders a global bottom sheet modal that can be controlled
|
||||||
* from anywhere in the app using the useGlobalModal hook.
|
* from anywhere in the app using the useGlobalModal hook.
|
||||||
*
|
*
|
||||||
* Place this component at the root level of your app (in _layout.tsx)
|
* Place this component at the root level of your app (in _layout.tsx).
|
||||||
* after BottomSheetModalProvider.
|
|
||||||
*/
|
*/
|
||||||
export const GlobalModal = () => {
|
export const GlobalModal = () => {
|
||||||
const { hideModal, modalState, modalRef, isVisible } = useGlobalModal();
|
const { hideModal, modalState, modalRef, isVisible } = useGlobalModal();
|
||||||
@@ -33,17 +29,6 @@ export const GlobalModal = () => {
|
|||||||
[hideModal],
|
[hideModal],
|
||||||
);
|
);
|
||||||
|
|
||||||
const renderBackdrop = useCallback(
|
|
||||||
(props: BottomSheetBackdropProps) => (
|
|
||||||
<BottomSheetBackdrop
|
|
||||||
{...props}
|
|
||||||
disappearsOnIndex={-1}
|
|
||||||
appearsOnIndex={0}
|
|
||||||
/>
|
|
||||||
),
|
|
||||||
[],
|
|
||||||
);
|
|
||||||
|
|
||||||
const defaultOptions = {
|
const defaultOptions = {
|
||||||
enableDynamicSizing: true,
|
enableDynamicSizing: true,
|
||||||
enablePanDownToClose: true,
|
enablePanDownToClose: true,
|
||||||
@@ -58,6 +43,8 @@ export const GlobalModal = () => {
|
|||||||
// Merge default options with provided options
|
// Merge default options with provided options
|
||||||
const modalOptions = { ...defaultOptions, ...modalState.options };
|
const modalOptions = { ...defaultOptions, ...modalState.options };
|
||||||
|
|
||||||
|
if (Platform.isTV) return null;
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<BottomSheetModal
|
<BottomSheetModal
|
||||||
ref={modalRef}
|
ref={modalRef}
|
||||||
@@ -65,12 +52,9 @@ export const GlobalModal = () => {
|
|||||||
? { snapPoints: modalOptions.snapPoints }
|
? { snapPoints: modalOptions.snapPoints }
|
||||||
: { enableDynamicSizing: modalOptions.enableDynamicSizing })}
|
: { enableDynamicSizing: modalOptions.enableDynamicSizing })}
|
||||||
onChange={handleSheetChanges}
|
onChange={handleSheetChanges}
|
||||||
backdropComponent={renderBackdrop}
|
|
||||||
handleIndicatorStyle={modalOptions.handleIndicatorStyle}
|
handleIndicatorStyle={modalOptions.handleIndicatorStyle}
|
||||||
backgroundStyle={modalOptions.backgroundStyle}
|
backgroundStyle={modalOptions.backgroundStyle}
|
||||||
enablePanDownToClose={modalOptions.enablePanDownToClose}
|
enablePanDownToClose={modalOptions.enablePanDownToClose}
|
||||||
enableDismissOnClose
|
|
||||||
stackBehavior='push'
|
|
||||||
style={{ zIndex: 1000 }}
|
style={{ zIndex: 1000 }}
|
||||||
>
|
>
|
||||||
{modalState.content}
|
{modalState.content}
|
||||||
|
|||||||
@@ -1,10 +1,4 @@
|
|||||||
import { Feather, Ionicons } from "@expo/vector-icons";
|
import { Feather, Ionicons } from "@expo/vector-icons";
|
||||||
import {
|
|
||||||
BottomSheetBackdrop,
|
|
||||||
type BottomSheetBackdropProps,
|
|
||||||
BottomSheetModal,
|
|
||||||
BottomSheetScrollView,
|
|
||||||
} from "@gorhom/bottom-sheet";
|
|
||||||
import { Image } from "expo-image";
|
import { Image } from "expo-image";
|
||||||
import { forwardRef, useCallback, useImperativeHandle, useRef } from "react";
|
import { forwardRef, useCallback, useImperativeHandle, useRef } from "react";
|
||||||
import { useTranslation } from "react-i18next";
|
import { useTranslation } from "react-i18next";
|
||||||
@@ -13,6 +7,11 @@ import { useSafeAreaInsets } from "react-native-safe-area-context";
|
|||||||
import { Button } from "@/components/Button";
|
import { Button } from "@/components/Button";
|
||||||
import { Text } from "@/components/common/Text";
|
import { Text } from "@/components/common/Text";
|
||||||
import useRouter from "@/hooks/useAppRouter";
|
import useRouter from "@/hooks/useAppRouter";
|
||||||
|
import {
|
||||||
|
type BottomSheetMethods,
|
||||||
|
BottomSheetModal,
|
||||||
|
BottomSheetScrollView,
|
||||||
|
} from "@/utils/expoUiBottomSheet";
|
||||||
import { storage } from "@/utils/mmkv";
|
import { storage } from "@/utils/mmkv";
|
||||||
|
|
||||||
export interface IntroSheetRef {
|
export interface IntroSheetRef {
|
||||||
@@ -21,7 +20,7 @@ export interface IntroSheetRef {
|
|||||||
}
|
}
|
||||||
|
|
||||||
export const IntroSheet = forwardRef<IntroSheetRef>((_, ref) => {
|
export const IntroSheet = forwardRef<IntroSheetRef>((_, ref) => {
|
||||||
const bottomSheetRef = useRef<BottomSheetModal>(null);
|
const bottomSheetRef = useRef<BottomSheetMethods>(null);
|
||||||
const { t } = useTranslation();
|
const { t } = useTranslation();
|
||||||
const insets = useSafeAreaInsets();
|
const insets = useSafeAreaInsets();
|
||||||
const router = useRouter();
|
const router = useRouter();
|
||||||
@@ -36,17 +35,6 @@ export const IntroSheet = forwardRef<IntroSheetRef>((_, ref) => {
|
|||||||
},
|
},
|
||||||
}));
|
}));
|
||||||
|
|
||||||
const renderBackdrop = useCallback(
|
|
||||||
(props: BottomSheetBackdropProps) => (
|
|
||||||
<BottomSheetBackdrop
|
|
||||||
{...props}
|
|
||||||
disappearsOnIndex={-1}
|
|
||||||
appearsOnIndex={0}
|
|
||||||
/>
|
|
||||||
),
|
|
||||||
[],
|
|
||||||
);
|
|
||||||
|
|
||||||
const handleDismiss = useCallback(() => {
|
const handleDismiss = useCallback(() => {
|
||||||
bottomSheetRef.current?.dismiss();
|
bottomSheetRef.current?.dismiss();
|
||||||
}, []);
|
}, []);
|
||||||
@@ -56,11 +44,13 @@ export const IntroSheet = forwardRef<IntroSheetRef>((_, ref) => {
|
|||||||
router.push("/settings");
|
router.push("/settings");
|
||||||
}, []);
|
}, []);
|
||||||
|
|
||||||
|
if (Platform.isTV) return null;
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<BottomSheetModal
|
<BottomSheetModal
|
||||||
ref={bottomSheetRef}
|
ref={bottomSheetRef}
|
||||||
|
enablePanDownToClose
|
||||||
enableDynamicSizing
|
enableDynamicSizing
|
||||||
backdropComponent={renderBackdrop}
|
|
||||||
backgroundStyle={{ backgroundColor: "#171717" }}
|
backgroundStyle={{ backgroundColor: "#171717" }}
|
||||||
handleIndicatorStyle={{ backgroundColor: "#737373" }}
|
handleIndicatorStyle={{ backgroundColor: "#737373" }}
|
||||||
>
|
>
|
||||||
|
|||||||
@@ -1,10 +1,4 @@
|
|||||||
import { Ionicons } from "@expo/vector-icons";
|
import { Ionicons } from "@expo/vector-icons";
|
||||||
import {
|
|
||||||
BottomSheetBackdrop,
|
|
||||||
type BottomSheetBackdropProps,
|
|
||||||
BottomSheetModal,
|
|
||||||
BottomSheetScrollView,
|
|
||||||
} from "@gorhom/bottom-sheet";
|
|
||||||
import type {
|
import type {
|
||||||
MediaSourceInfo,
|
MediaSourceInfo,
|
||||||
MediaStream,
|
MediaStream,
|
||||||
@@ -12,8 +6,13 @@ import type {
|
|||||||
import type React from "react";
|
import type React from "react";
|
||||||
import { useMemo, useRef } from "react";
|
import { useMemo, useRef } from "react";
|
||||||
import { useTranslation } from "react-i18next";
|
import { useTranslation } from "react-i18next";
|
||||||
import { TouchableOpacity, View } from "react-native";
|
import { Platform, TouchableOpacity, View } from "react-native";
|
||||||
import { formatBitrate } from "@/utils/bitrate";
|
import { formatBitrate } from "@/utils/bitrate";
|
||||||
|
import {
|
||||||
|
type BottomSheetMethods,
|
||||||
|
BottomSheetModal,
|
||||||
|
BottomSheetScrollView,
|
||||||
|
} from "@/utils/expoUiBottomSheet";
|
||||||
import { Badge } from "./Badge";
|
import { Badge } from "./Badge";
|
||||||
import { Text } from "./common/Text";
|
import { Text } from "./common/Text";
|
||||||
|
|
||||||
@@ -22,9 +21,20 @@ interface Props {
|
|||||||
}
|
}
|
||||||
|
|
||||||
export const ItemTechnicalDetails: React.FC<Props> = ({ source }) => {
|
export const ItemTechnicalDetails: React.FC<Props> = ({ source }) => {
|
||||||
const bottomSheetModalRef = useRef<BottomSheetModal>(null);
|
const bottomSheetModalRef = useRef<BottomSheetMethods>(null);
|
||||||
const { t } = useTranslation();
|
const { t } = useTranslation();
|
||||||
|
|
||||||
|
if (Platform.isTV) {
|
||||||
|
return (
|
||||||
|
<View className='px-4 mt-2 mb-4'>
|
||||||
|
<Text className='text-lg font-bold mb-4'>{t("item_card.video")}</Text>
|
||||||
|
<View className='flex flex-row space-x-2'>
|
||||||
|
<VideoStreamInfo source={source} />
|
||||||
|
</View>
|
||||||
|
</View>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<View className='px-4 mt-2 mb-4'>
|
<View className='px-4 mt-2 mb-4'>
|
||||||
<Text className='text-lg font-bold mb-4'>{t("item_card.video")}</Text>
|
<Text className='text-lg font-bold mb-4'>{t("item_card.video")}</Text>
|
||||||
@@ -36,27 +46,27 @@ export const ItemTechnicalDetails: React.FC<Props> = ({ source }) => {
|
|||||||
</TouchableOpacity>
|
</TouchableOpacity>
|
||||||
<BottomSheetModal
|
<BottomSheetModal
|
||||||
ref={bottomSheetModalRef}
|
ref={bottomSheetModalRef}
|
||||||
snapPoints={["80%"]}
|
enableDynamicSizing
|
||||||
|
enablePanDownToClose
|
||||||
handleIndicatorStyle={{
|
handleIndicatorStyle={{
|
||||||
backgroundColor: "white",
|
backgroundColor: "white",
|
||||||
}}
|
}}
|
||||||
backgroundStyle={{
|
backgroundStyle={{
|
||||||
backgroundColor: "#171717",
|
backgroundColor: "#171717",
|
||||||
}}
|
}}
|
||||||
backdropComponent={(props: BottomSheetBackdropProps) => (
|
|
||||||
<BottomSheetBackdrop
|
|
||||||
{...props}
|
|
||||||
disappearsOnIndex={-1}
|
|
||||||
appearsOnIndex={0}
|
|
||||||
/>
|
|
||||||
)}
|
|
||||||
>
|
>
|
||||||
<BottomSheetScrollView>
|
<BottomSheetScrollView>
|
||||||
<View className='flex flex-col space-y-2 p-4 mb-4'>
|
<View className='flex flex-col space-y-2 p-4'>
|
||||||
|
<View className='flex-row items-center justify-between mb-2'>
|
||||||
|
<Text className='text-lg font-bold'>{t("item_card.video")}</Text>
|
||||||
|
<TouchableOpacity
|
||||||
|
onPress={() => bottomSheetModalRef.current?.dismiss()}
|
||||||
|
hitSlop={12}
|
||||||
|
>
|
||||||
|
<Ionicons name='close' size={24} color='white' />
|
||||||
|
</TouchableOpacity>
|
||||||
|
</View>
|
||||||
<View>
|
<View>
|
||||||
<Text className='text-lg font-bold mb-4'>
|
|
||||||
{t("item_card.video")}
|
|
||||||
</Text>
|
|
||||||
<View className='flex flex-row space-x-2'>
|
<View className='flex flex-row space-x-2'>
|
||||||
<VideoStreamInfo source={source} />
|
<VideoStreamInfo source={source} />
|
||||||
</View>
|
</View>
|
||||||
|
|||||||
@@ -1,9 +1,3 @@
|
|||||||
import {
|
|
||||||
BottomSheetBackdrop,
|
|
||||||
type BottomSheetBackdropProps,
|
|
||||||
BottomSheetModal,
|
|
||||||
BottomSheetView,
|
|
||||||
} from "@gorhom/bottom-sheet";
|
|
||||||
import type React from "react";
|
import type React from "react";
|
||||||
import { useCallback, useEffect, useMemo, useRef, useState } from "react";
|
import { useCallback, useEffect, useMemo, useRef, useState } from "react";
|
||||||
import { useTranslation } from "react-i18next";
|
import { useTranslation } from "react-i18next";
|
||||||
@@ -17,6 +11,11 @@ import {
|
|||||||
} from "react-native";
|
} from "react-native";
|
||||||
import { useSafeAreaInsets } from "react-native-safe-area-context";
|
import { useSafeAreaInsets } from "react-native-safe-area-context";
|
||||||
import { useHaptic } from "@/hooks/useHaptic";
|
import { useHaptic } from "@/hooks/useHaptic";
|
||||||
|
import {
|
||||||
|
type BottomSheetMethods,
|
||||||
|
BottomSheetModal,
|
||||||
|
BottomSheetView,
|
||||||
|
} from "@/utils/expoUiBottomSheet";
|
||||||
import { verifyAccountPIN } from "@/utils/secureCredentials";
|
import { verifyAccountPIN } from "@/utils/secureCredentials";
|
||||||
import { Button } from "./Button";
|
import { Button } from "./Button";
|
||||||
import { Text } from "./common/Text";
|
import { Text } from "./common/Text";
|
||||||
@@ -43,7 +42,7 @@ export const PINEntryModal: React.FC<PINEntryModalProps> = ({
|
|||||||
}) => {
|
}) => {
|
||||||
const { t } = useTranslation();
|
const { t } = useTranslation();
|
||||||
const insets = useSafeAreaInsets();
|
const insets = useSafeAreaInsets();
|
||||||
const bottomSheetModalRef = useRef<BottomSheetModal>(null);
|
const bottomSheetModalRef = useRef<BottomSheetMethods>(null);
|
||||||
const [pinCode, setPinCode] = useState("");
|
const [pinCode, setPinCode] = useState("");
|
||||||
const [error, setError] = useState<string | null>(null);
|
const [error, setError] = useState<string | null>(null);
|
||||||
const [isVerifying, setIsVerifying] = useState(false);
|
const [isVerifying, setIsVerifying] = useState(false);
|
||||||
@@ -78,17 +77,6 @@ export const PINEntryModal: React.FC<PINEntryModalProps> = ({
|
|||||||
[onClose],
|
[onClose],
|
||||||
);
|
);
|
||||||
|
|
||||||
const renderBackdrop = useCallback(
|
|
||||||
(props: BottomSheetBackdropProps) => (
|
|
||||||
<BottomSheetBackdrop
|
|
||||||
{...props}
|
|
||||||
disappearsOnIndex={-1}
|
|
||||||
appearsOnIndex={0}
|
|
||||||
/>
|
|
||||||
),
|
|
||||||
[],
|
|
||||||
);
|
|
||||||
|
|
||||||
const shake = () => {
|
const shake = () => {
|
||||||
Animated.sequence([
|
Animated.sequence([
|
||||||
Animated.timing(shakeAnimation, {
|
Animated.timing(shakeAnimation, {
|
||||||
@@ -159,18 +147,18 @@ export const PINEntryModal: React.FC<PINEntryModalProps> = ({
|
|||||||
]);
|
]);
|
||||||
};
|
};
|
||||||
|
|
||||||
|
if (Platform.isTV) return null;
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<BottomSheetModal
|
<BottomSheetModal
|
||||||
ref={bottomSheetModalRef}
|
ref={bottomSheetModalRef}
|
||||||
|
enablePanDownToClose
|
||||||
snapPoints={snapPoints}
|
snapPoints={snapPoints}
|
||||||
onChange={handleSheetChanges}
|
onChange={handleSheetChanges}
|
||||||
handleIndicatorStyle={{ backgroundColor: "white" }}
|
handleIndicatorStyle={{ backgroundColor: "white" }}
|
||||||
backgroundStyle={{ backgroundColor: "#171717" }}
|
backgroundStyle={{ backgroundColor: "#171717" }}
|
||||||
backdropComponent={renderBackdrop}
|
|
||||||
keyboardBehavior={isAndroid ? "fillParent" : "interactive"}
|
keyboardBehavior={isAndroid ? "fillParent" : "interactive"}
|
||||||
keyboardBlurBehavior='restore'
|
keyboardBlurBehavior='restore'
|
||||||
android_keyboardInputMode='adjustResize'
|
|
||||||
topInset={isAndroid ? 0 : undefined}
|
|
||||||
>
|
>
|
||||||
<BottomSheetView
|
<BottomSheetView
|
||||||
style={{
|
style={{
|
||||||
|
|||||||
@@ -1,16 +1,15 @@
|
|||||||
import {
|
|
||||||
BottomSheetBackdrop,
|
|
||||||
type BottomSheetBackdropProps,
|
|
||||||
BottomSheetModal,
|
|
||||||
BottomSheetTextInput,
|
|
||||||
BottomSheetView,
|
|
||||||
} from "@gorhom/bottom-sheet";
|
|
||||||
import type React from "react";
|
import type React from "react";
|
||||||
import { useCallback, useEffect, useMemo, useRef, useState } from "react";
|
import { useCallback, useEffect, useMemo, useRef, useState } from "react";
|
||||||
import { useTranslation } from "react-i18next";
|
import { useTranslation } from "react-i18next";
|
||||||
import { ActivityIndicator, Platform, View } from "react-native";
|
import { ActivityIndicator, Platform, View } from "react-native";
|
||||||
import { useSafeAreaInsets } from "react-native-safe-area-context";
|
import { useSafeAreaInsets } from "react-native-safe-area-context";
|
||||||
import { useHaptic } from "@/hooks/useHaptic";
|
import { useHaptic } from "@/hooks/useHaptic";
|
||||||
|
import {
|
||||||
|
type BottomSheetMethods,
|
||||||
|
BottomSheetModal,
|
||||||
|
BottomSheetTextInput,
|
||||||
|
BottomSheetView,
|
||||||
|
} from "@/utils/expoUiBottomSheet";
|
||||||
import { Button } from "./Button";
|
import { Button } from "./Button";
|
||||||
import { Text } from "./common/Text";
|
import { Text } from "./common/Text";
|
||||||
|
|
||||||
@@ -29,7 +28,7 @@ export const PasswordEntryModal: React.FC<PasswordEntryModalProps> = ({
|
|||||||
}) => {
|
}) => {
|
||||||
const { t } = useTranslation();
|
const { t } = useTranslation();
|
||||||
const insets = useSafeAreaInsets();
|
const insets = useSafeAreaInsets();
|
||||||
const bottomSheetModalRef = useRef<BottomSheetModal>(null);
|
const bottomSheetModalRef = useRef<BottomSheetMethods>(null);
|
||||||
const [password, setPassword] = useState("");
|
const [password, setPassword] = useState("");
|
||||||
const [error, setError] = useState<string | null>(null);
|
const [error, setError] = useState<string | null>(null);
|
||||||
const [isLoading, setIsLoading] = useState(false);
|
const [isLoading, setIsLoading] = useState(false);
|
||||||
@@ -62,17 +61,6 @@ export const PasswordEntryModal: React.FC<PasswordEntryModalProps> = ({
|
|||||||
[onClose],
|
[onClose],
|
||||||
);
|
);
|
||||||
|
|
||||||
const renderBackdrop = useCallback(
|
|
||||||
(props: BottomSheetBackdropProps) => (
|
|
||||||
<BottomSheetBackdrop
|
|
||||||
{...props}
|
|
||||||
disappearsOnIndex={-1}
|
|
||||||
appearsOnIndex={0}
|
|
||||||
/>
|
|
||||||
),
|
|
||||||
[],
|
|
||||||
);
|
|
||||||
|
|
||||||
const handleSubmit = async () => {
|
const handleSubmit = async () => {
|
||||||
if (!password) {
|
if (!password) {
|
||||||
setError(t("password.enter_password"));
|
setError(t("password.enter_password"));
|
||||||
@@ -93,18 +81,18 @@ export const PasswordEntryModal: React.FC<PasswordEntryModalProps> = ({
|
|||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
|
if (Platform.isTV) return null;
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<BottomSheetModal
|
<BottomSheetModal
|
||||||
ref={bottomSheetModalRef}
|
ref={bottomSheetModalRef}
|
||||||
|
enablePanDownToClose
|
||||||
snapPoints={snapPoints}
|
snapPoints={snapPoints}
|
||||||
onChange={handleSheetChanges}
|
onChange={handleSheetChanges}
|
||||||
handleIndicatorStyle={{ backgroundColor: "white" }}
|
handleIndicatorStyle={{ backgroundColor: "white" }}
|
||||||
backgroundStyle={{ backgroundColor: "#171717" }}
|
backgroundStyle={{ backgroundColor: "#171717" }}
|
||||||
backdropComponent={renderBackdrop}
|
|
||||||
keyboardBehavior={isAndroid ? "fillParent" : "interactive"}
|
keyboardBehavior={isAndroid ? "fillParent" : "interactive"}
|
||||||
keyboardBlurBehavior='restore'
|
keyboardBlurBehavior='restore'
|
||||||
android_keyboardInputMode='adjustResize'
|
|
||||||
topInset={isAndroid ? 0 : undefined}
|
|
||||||
>
|
>
|
||||||
<BottomSheetView
|
<BottomSheetView
|
||||||
style={{
|
style={{
|
||||||
|
|||||||
@@ -1,5 +1,4 @@
|
|||||||
import { Ionicons } from "@expo/vector-icons";
|
import { Ionicons } from "@expo/vector-icons";
|
||||||
import { BottomSheetScrollView } from "@gorhom/bottom-sheet";
|
|
||||||
import React, { useEffect, useState } from "react";
|
import React, { useEffect, useState } from "react";
|
||||||
import {
|
import {
|
||||||
type LayoutChangeEvent,
|
type LayoutChangeEvent,
|
||||||
@@ -11,6 +10,7 @@ import {
|
|||||||
import { useSafeAreaInsets } from "react-native-safe-area-context";
|
import { useSafeAreaInsets } from "react-native-safe-area-context";
|
||||||
import { Text } from "@/components/common/Text";
|
import { Text } from "@/components/common/Text";
|
||||||
import { useGlobalModal } from "@/providers/GlobalModalProvider";
|
import { useGlobalModal } from "@/providers/GlobalModalProvider";
|
||||||
|
import { BottomSheetScrollView } from "@/utils/expoUiBottomSheet";
|
||||||
|
|
||||||
// @expo/ui's SwiftUI native module (ExpoUI) does not exist in tvOS builds.
|
// @expo/ui's SwiftUI native module (ExpoUI) does not exist in tvOS builds.
|
||||||
// A static top-level import evaluates requireNativeModule('ExpoUI') at module
|
// A static top-level import evaluates requireNativeModule('ExpoUI') at module
|
||||||
|
|||||||
@@ -1,6 +1,5 @@
|
|||||||
import { useActionSheet } from "@expo/react-native-action-sheet";
|
import { useActionSheet } from "@expo/react-native-action-sheet";
|
||||||
import { Feather, Ionicons } from "@expo/vector-icons";
|
import { Feather, Ionicons } from "@expo/vector-icons";
|
||||||
import { BottomSheetView } from "@gorhom/bottom-sheet";
|
|
||||||
import type { BaseItemDto } from "@jellyfin/sdk/lib/generated-client";
|
import type { BaseItemDto } from "@jellyfin/sdk/lib/generated-client";
|
||||||
import { useAtom, useAtomValue } from "jotai";
|
import { useAtom, useAtomValue } from "jotai";
|
||||||
import { useCallback, useEffect } from "react";
|
import { useCallback, useEffect } from "react";
|
||||||
@@ -32,6 +31,7 @@ import { apiAtom, userAtom } from "@/providers/JellyfinProvider";
|
|||||||
import { useOfflineMode } from "@/providers/OfflineModeProvider";
|
import { useOfflineMode } from "@/providers/OfflineModeProvider";
|
||||||
import { itemThemeColorAtom } from "@/utils/atoms/primaryColor";
|
import { itemThemeColorAtom } from "@/utils/atoms/primaryColor";
|
||||||
import { useSettings } from "@/utils/atoms/settings";
|
import { useSettings } from "@/utils/atoms/settings";
|
||||||
|
import { BottomSheetView } from "@/utils/expoUiBottomSheet";
|
||||||
import { getParentBackdropImageUrl } from "@/utils/jellyfin/image/getParentBackdropImageUrl";
|
import { getParentBackdropImageUrl } from "@/utils/jellyfin/image/getParentBackdropImageUrl";
|
||||||
import { getPrimaryImageUrl } from "@/utils/jellyfin/image/getPrimaryImageUrl";
|
import { getPrimaryImageUrl } from "@/utils/jellyfin/image/getPrimaryImageUrl";
|
||||||
import { getStreamUrl } from "@/utils/jellyfin/media/getStreamUrl";
|
import { getStreamUrl } from "@/utils/jellyfin/media/getStreamUrl";
|
||||||
|
|||||||
@@ -1,15 +1,14 @@
|
|||||||
import { Ionicons } from "@expo/vector-icons";
|
import { Ionicons } from "@expo/vector-icons";
|
||||||
import {
|
|
||||||
BottomSheetBackdrop,
|
|
||||||
type BottomSheetBackdropProps,
|
|
||||||
BottomSheetModal,
|
|
||||||
BottomSheetView,
|
|
||||||
} from "@gorhom/bottom-sheet";
|
|
||||||
import type React from "react";
|
import type React from "react";
|
||||||
import { useCallback, useEffect, useMemo, useRef, useState } from "react";
|
import { useCallback, useEffect, useMemo, useRef, useState } from "react";
|
||||||
import { useTranslation } from "react-i18next";
|
import { useTranslation } from "react-i18next";
|
||||||
import { Platform, TouchableOpacity, View } from "react-native";
|
import { Platform, TouchableOpacity, View } from "react-native";
|
||||||
import { useSafeAreaInsets } from "react-native-safe-area-context";
|
import { useSafeAreaInsets } from "react-native-safe-area-context";
|
||||||
|
import {
|
||||||
|
type BottomSheetMethods,
|
||||||
|
BottomSheetModal,
|
||||||
|
BottomSheetView,
|
||||||
|
} from "@/utils/expoUiBottomSheet";
|
||||||
import type { AccountSecurityType } from "@/utils/secureCredentials";
|
import type { AccountSecurityType } from "@/utils/secureCredentials";
|
||||||
import { Button } from "./Button";
|
import { Button } from "./Button";
|
||||||
import { Text } from "./common/Text";
|
import { Text } from "./common/Text";
|
||||||
@@ -58,7 +57,7 @@ export const SaveAccountModal: React.FC<SaveAccountModalProps> = ({
|
|||||||
}) => {
|
}) => {
|
||||||
const { t } = useTranslation();
|
const { t } = useTranslation();
|
||||||
const insets = useSafeAreaInsets();
|
const insets = useSafeAreaInsets();
|
||||||
const bottomSheetModalRef = useRef<BottomSheetModal>(null);
|
const bottomSheetModalRef = useRef<BottomSheetMethods>(null);
|
||||||
const [selectedType, setSelectedType] = useState<AccountSecurityType>("none");
|
const [selectedType, setSelectedType] = useState<AccountSecurityType>("none");
|
||||||
const [pinCode, setPinCode] = useState("");
|
const [pinCode, setPinCode] = useState("");
|
||||||
const [pinError, setPinError] = useState<string | null>(null);
|
const [pinError, setPinError] = useState<string | null>(null);
|
||||||
@@ -93,17 +92,6 @@ export const SaveAccountModal: React.FC<SaveAccountModalProps> = ({
|
|||||||
setPinError(null);
|
setPinError(null);
|
||||||
};
|
};
|
||||||
|
|
||||||
const renderBackdrop = useCallback(
|
|
||||||
(props: BottomSheetBackdropProps) => (
|
|
||||||
<BottomSheetBackdrop
|
|
||||||
{...props}
|
|
||||||
disappearsOnIndex={-1}
|
|
||||||
appearsOnIndex={0}
|
|
||||||
/>
|
|
||||||
),
|
|
||||||
[],
|
|
||||||
);
|
|
||||||
|
|
||||||
const handleOptionSelect = (type: AccountSecurityType) => {
|
const handleOptionSelect = (type: AccountSecurityType) => {
|
||||||
setSelectedType(type);
|
setSelectedType(type);
|
||||||
setPinCode("");
|
setPinCode("");
|
||||||
@@ -135,18 +123,18 @@ export const SaveAccountModal: React.FC<SaveAccountModalProps> = ({
|
|||||||
return true;
|
return true;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
if (Platform.isTV) return null;
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<BottomSheetModal
|
<BottomSheetModal
|
||||||
ref={bottomSheetModalRef}
|
ref={bottomSheetModalRef}
|
||||||
|
enablePanDownToClose
|
||||||
snapPoints={snapPoints}
|
snapPoints={snapPoints}
|
||||||
onChange={handleSheetChanges}
|
onChange={handleSheetChanges}
|
||||||
handleIndicatorStyle={{ backgroundColor: "white" }}
|
handleIndicatorStyle={{ backgroundColor: "white" }}
|
||||||
backgroundStyle={{ backgroundColor: "#171717" }}
|
backgroundStyle={{ backgroundColor: "#171717" }}
|
||||||
backdropComponent={renderBackdrop}
|
|
||||||
keyboardBehavior={isAndroid ? "fillParent" : "interactive"}
|
keyboardBehavior={isAndroid ? "fillParent" : "interactive"}
|
||||||
keyboardBlurBehavior='restore'
|
keyboardBlurBehavior='restore'
|
||||||
android_keyboardInputMode='adjustResize'
|
|
||||||
topInset={isAndroid ? 0 : undefined}
|
|
||||||
>
|
>
|
||||||
<BottomSheetView
|
<BottomSheetView
|
||||||
style={{
|
style={{
|
||||||
|
|||||||
@@ -1,15 +1,10 @@
|
|||||||
import { Ionicons } from "@expo/vector-icons";
|
import { Ionicons } from "@expo/vector-icons";
|
||||||
import {
|
|
||||||
BottomSheetBackdrop,
|
|
||||||
type BottomSheetBackdropProps,
|
|
||||||
BottomSheetModal,
|
|
||||||
BottomSheetScrollView,
|
|
||||||
} from "@gorhom/bottom-sheet";
|
|
||||||
import { isEqual } from "lodash";
|
import { isEqual } from "lodash";
|
||||||
import type React from "react";
|
import type React from "react";
|
||||||
import { useCallback, useEffect, useMemo, useRef, useState } from "react";
|
import { useCallback, useEffect, useMemo, useRef, useState } from "react";
|
||||||
import { useTranslation } from "react-i18next";
|
import { useTranslation } from "react-i18next";
|
||||||
import {
|
import {
|
||||||
|
Platform,
|
||||||
StyleSheet,
|
StyleSheet,
|
||||||
TouchableOpacity,
|
TouchableOpacity,
|
||||||
View,
|
View,
|
||||||
@@ -17,6 +12,11 @@ import {
|
|||||||
} from "react-native";
|
} from "react-native";
|
||||||
import { useSafeAreaInsets } from "react-native-safe-area-context";
|
import { useSafeAreaInsets } from "react-native-safe-area-context";
|
||||||
import { Text } from "@/components/common/Text";
|
import { Text } from "@/components/common/Text";
|
||||||
|
import {
|
||||||
|
type BottomSheetMethods,
|
||||||
|
BottomSheetModal,
|
||||||
|
BottomSheetScrollView,
|
||||||
|
} from "@/utils/expoUiBottomSheet";
|
||||||
import { Button } from "../Button";
|
import { Button } from "../Button";
|
||||||
import { Input } from "../common/Input";
|
import { Input } from "../common/Input";
|
||||||
|
|
||||||
@@ -75,7 +75,7 @@ export const FilterSheet = <T,>({
|
|||||||
disableSearch = false,
|
disableSearch = false,
|
||||||
multiple = false,
|
multiple = false,
|
||||||
}: Props<T>) => {
|
}: Props<T>) => {
|
||||||
const bottomSheetModalRef = useRef<BottomSheetModal>(null);
|
const bottomSheetModalRef = useRef<BottomSheetMethods>(null);
|
||||||
const snapPoints = useMemo(() => ["85%"], []);
|
const snapPoints = useMemo(() => ["85%"], []);
|
||||||
const { t } = useTranslation();
|
const { t } = useTranslation();
|
||||||
const insets = useSafeAreaInsets();
|
const insets = useSafeAreaInsets();
|
||||||
@@ -143,24 +143,15 @@ export const FilterSheet = <T,>({
|
|||||||
return data;
|
return data;
|
||||||
}, [search, filteredData, data]);
|
}, [search, filteredData, data]);
|
||||||
|
|
||||||
const renderBackdrop = useCallback(
|
if (Platform.isTV) return null;
|
||||||
(props: BottomSheetBackdropProps) => (
|
|
||||||
<BottomSheetBackdrop
|
|
||||||
{...props}
|
|
||||||
disappearsOnIndex={-1}
|
|
||||||
appearsOnIndex={0}
|
|
||||||
/>
|
|
||||||
),
|
|
||||||
[],
|
|
||||||
);
|
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<BottomSheetModal
|
<BottomSheetModal
|
||||||
ref={bottomSheetModalRef}
|
ref={bottomSheetModalRef}
|
||||||
|
enablePanDownToClose
|
||||||
index={0}
|
index={0}
|
||||||
snapPoints={snapPoints}
|
snapPoints={snapPoints}
|
||||||
onChange={handleSheetChanges}
|
onChange={handleSheetChanges}
|
||||||
backdropComponent={renderBackdrop}
|
|
||||||
handleIndicatorStyle={{
|
handleIndicatorStyle={{
|
||||||
backgroundColor: "white",
|
backgroundColor: "white",
|
||||||
}}
|
}}
|
||||||
|
|||||||
@@ -1,4 +1,3 @@
|
|||||||
import { BottomSheetTextInput } from "@gorhom/bottom-sheet";
|
|
||||||
import React, { useCallback, useImperativeHandle, useRef } from "react";
|
import React, { useCallback, useImperativeHandle, useRef } from "react";
|
||||||
import {
|
import {
|
||||||
type StyleProp,
|
type StyleProp,
|
||||||
@@ -8,6 +7,7 @@ import {
|
|||||||
View,
|
View,
|
||||||
type ViewStyle,
|
type ViewStyle,
|
||||||
} from "react-native";
|
} from "react-native";
|
||||||
|
import { BottomSheetTextInput } from "@/utils/expoUiBottomSheet";
|
||||||
|
|
||||||
interface PinInputProps
|
interface PinInputProps
|
||||||
extends Omit<TextInputProps, "value" | "onChangeText" | "style"> {
|
extends Omit<TextInputProps, "value" | "onChangeText" | "style"> {
|
||||||
|
|||||||
@@ -1,23 +1,22 @@
|
|||||||
import {
|
|
||||||
BottomSheetBackdrop,
|
|
||||||
type BottomSheetBackdropProps,
|
|
||||||
BottomSheetModal,
|
|
||||||
BottomSheetView,
|
|
||||||
} from "@gorhom/bottom-sheet";
|
|
||||||
import type { BottomSheetModalMethods } from "@gorhom/bottom-sheet/lib/typescript/types";
|
|
||||||
import { useQuery } from "@tanstack/react-query";
|
import { useQuery } from "@tanstack/react-query";
|
||||||
import { forwardRef, useCallback, useMemo, useState } from "react";
|
import { forwardRef, useCallback, useMemo, useState } from "react";
|
||||||
import { useTranslation } from "react-i18next";
|
import { useTranslation } from "react-i18next";
|
||||||
import { View, type ViewProps } from "react-native";
|
import { Platform, View, type ViewProps } from "react-native";
|
||||||
import { Button } from "@/components/Button";
|
import { Button } from "@/components/Button";
|
||||||
import { Text } from "@/components/common/Text";
|
import { Text } from "@/components/common/Text";
|
||||||
import { PlatformDropdown } from "@/components/PlatformDropdown";
|
import { PlatformDropdown } from "@/components/PlatformDropdown";
|
||||||
import { useJellyseerr } from "@/hooks/useJellyseerr";
|
import { useJellyseerr } from "@/hooks/useJellyseerr";
|
||||||
|
import {
|
||||||
|
type BottomSheetMethods,
|
||||||
|
BottomSheetModal,
|
||||||
|
BottomSheetView,
|
||||||
|
} from "@/utils/expoUiBottomSheet";
|
||||||
import type {
|
import type {
|
||||||
QualityProfile,
|
QualityProfile,
|
||||||
RootFolder,
|
RootFolder,
|
||||||
Tag,
|
Tag,
|
||||||
} from "@/utils/jellyseerr/server/api/servarr/base";
|
} from "@/utils/jellyseerr/server/api/servarr/base";
|
||||||
|
|
||||||
import type { MediaType } from "@/utils/jellyseerr/server/constants/media";
|
import type { MediaType } from "@/utils/jellyseerr/server/constants/media";
|
||||||
import type { MediaRequestBody } from "@/utils/jellyseerr/server/interfaces/api/requestInterfaces";
|
import type { MediaRequestBody } from "@/utils/jellyseerr/server/interfaces/api/requestInterfaces";
|
||||||
import { writeDebugLog } from "@/utils/log";
|
import { writeDebugLog } from "@/utils/log";
|
||||||
@@ -34,7 +33,7 @@ interface Props {
|
|||||||
}
|
}
|
||||||
|
|
||||||
const RequestModal = forwardRef<
|
const RequestModal = forwardRef<
|
||||||
BottomSheetModalMethods,
|
BottomSheetMethods,
|
||||||
Props & Omit<ViewProps, "id">
|
Props & Omit<ViewProps, "id">
|
||||||
>(
|
>(
|
||||||
(
|
(
|
||||||
@@ -283,11 +282,13 @@ const RequestModal = forwardRef<
|
|||||||
defaultTags,
|
defaultTags,
|
||||||
]);
|
]);
|
||||||
|
|
||||||
|
if (Platform.isTV) return null;
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<BottomSheetModal
|
<BottomSheetModal
|
||||||
ref={ref}
|
ref={ref}
|
||||||
|
enablePanDownToClose
|
||||||
enableDynamicSizing
|
enableDynamicSizing
|
||||||
enableDismissOnClose
|
|
||||||
onDismiss={handleDismiss}
|
onDismiss={handleDismiss}
|
||||||
handleIndicatorStyle={{
|
handleIndicatorStyle={{
|
||||||
backgroundColor: "white",
|
backgroundColor: "white",
|
||||||
@@ -295,14 +296,6 @@ const RequestModal = forwardRef<
|
|||||||
backgroundStyle={{
|
backgroundStyle={{
|
||||||
backgroundColor: "#171717",
|
backgroundColor: "#171717",
|
||||||
}}
|
}}
|
||||||
backdropComponent={(sheetProps: BottomSheetBackdropProps) => (
|
|
||||||
<BottomSheetBackdrop
|
|
||||||
{...sheetProps}
|
|
||||||
disappearsOnIndex={-1}
|
|
||||||
appearsOnIndex={0}
|
|
||||||
/>
|
|
||||||
)}
|
|
||||||
stackBehavior='push'
|
|
||||||
>
|
>
|
||||||
<BottomSheetView>
|
<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'>
|
||||||
|
|||||||
@@ -1,10 +1,3 @@
|
|||||||
import {
|
|
||||||
BottomSheetBackdrop,
|
|
||||||
type BottomSheetBackdropProps,
|
|
||||||
BottomSheetModal,
|
|
||||||
BottomSheetTextInput,
|
|
||||||
BottomSheetView,
|
|
||||||
} from "@gorhom/bottom-sheet";
|
|
||||||
import React, {
|
import React, {
|
||||||
useCallback,
|
useCallback,
|
||||||
useEffect,
|
useEffect,
|
||||||
@@ -13,11 +6,17 @@ import React, {
|
|||||||
useState,
|
useState,
|
||||||
} from "react";
|
} from "react";
|
||||||
import { useTranslation } from "react-i18next";
|
import { useTranslation } from "react-i18next";
|
||||||
import { ActivityIndicator, Keyboard } from "react-native";
|
import { ActivityIndicator, Keyboard, Platform } from "react-native";
|
||||||
import { useSafeAreaInsets } from "react-native-safe-area-context";
|
import { useSafeAreaInsets } from "react-native-safe-area-context";
|
||||||
import { Button } from "@/components/Button";
|
import { Button } from "@/components/Button";
|
||||||
import { Text } from "@/components/common/Text";
|
import { Text } from "@/components/common/Text";
|
||||||
import { useCreatePlaylist } from "@/hooks/usePlaylistMutations";
|
import { useCreatePlaylist } from "@/hooks/usePlaylistMutations";
|
||||||
|
import {
|
||||||
|
type BottomSheetMethods,
|
||||||
|
BottomSheetModal,
|
||||||
|
BottomSheetTextInput,
|
||||||
|
BottomSheetView,
|
||||||
|
} from "@/utils/expoUiBottomSheet";
|
||||||
|
|
||||||
interface Props {
|
interface Props {
|
||||||
open: boolean;
|
open: boolean;
|
||||||
@@ -32,7 +31,7 @@ export const CreatePlaylistModal: React.FC<Props> = ({
|
|||||||
onPlaylistCreated,
|
onPlaylistCreated,
|
||||||
initialTrackId,
|
initialTrackId,
|
||||||
}) => {
|
}) => {
|
||||||
const bottomSheetModalRef = useRef<BottomSheetModal>(null);
|
const bottomSheetModalRef = useRef<BottomSheetMethods>(null);
|
||||||
const insets = useSafeAreaInsets();
|
const insets = useSafeAreaInsets();
|
||||||
const { t } = useTranslation();
|
const { t } = useTranslation();
|
||||||
const createPlaylist = useCreatePlaylist();
|
const createPlaylist = useCreatePlaylist();
|
||||||
@@ -59,17 +58,6 @@ export const CreatePlaylistModal: React.FC<Props> = ({
|
|||||||
[setOpen],
|
[setOpen],
|
||||||
);
|
);
|
||||||
|
|
||||||
const renderBackdrop = useCallback(
|
|
||||||
(props: BottomSheetBackdropProps) => (
|
|
||||||
<BottomSheetBackdrop
|
|
||||||
{...props}
|
|
||||||
disappearsOnIndex={-1}
|
|
||||||
appearsOnIndex={0}
|
|
||||||
/>
|
|
||||||
),
|
|
||||||
[],
|
|
||||||
);
|
|
||||||
|
|
||||||
const handleCreate = useCallback(async () => {
|
const handleCreate = useCallback(async () => {
|
||||||
if (!name.trim()) return;
|
if (!name.trim()) return;
|
||||||
|
|
||||||
@@ -86,13 +74,15 @@ export const CreatePlaylistModal: React.FC<Props> = ({
|
|||||||
|
|
||||||
const isValid = name.trim().length > 0;
|
const isValid = name.trim().length > 0;
|
||||||
|
|
||||||
|
if (Platform.isTV) return null;
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<BottomSheetModal
|
<BottomSheetModal
|
||||||
ref={bottomSheetModalRef}
|
ref={bottomSheetModalRef}
|
||||||
|
enablePanDownToClose
|
||||||
index={0}
|
index={0}
|
||||||
snapPoints={snapPoints}
|
snapPoints={snapPoints}
|
||||||
onChange={handleSheetChanges}
|
onChange={handleSheetChanges}
|
||||||
backdropComponent={renderBackdrop}
|
|
||||||
handleIndicatorStyle={{
|
handleIndicatorStyle={{
|
||||||
backgroundColor: "white",
|
backgroundColor: "white",
|
||||||
}}
|
}}
|
||||||
|
|||||||
@@ -1,18 +1,23 @@
|
|||||||
import { Ionicons } from "@expo/vector-icons";
|
import { Ionicons } from "@expo/vector-icons";
|
||||||
import {
|
|
||||||
BottomSheetBackdrop,
|
|
||||||
type BottomSheetBackdropProps,
|
|
||||||
BottomSheetModal,
|
|
||||||
BottomSheetView,
|
|
||||||
} from "@gorhom/bottom-sheet";
|
|
||||||
import type { BaseItemDto } from "@jellyfin/sdk/lib/generated-client/models";
|
import type { BaseItemDto } from "@jellyfin/sdk/lib/generated-client/models";
|
||||||
import React, { useCallback, useEffect, useMemo, useRef } from "react";
|
import React, { useCallback, useEffect, useMemo, useRef } from "react";
|
||||||
import { useTranslation } from "react-i18next";
|
import { useTranslation } from "react-i18next";
|
||||||
import { Alert, StyleSheet, TouchableOpacity, View } from "react-native";
|
import {
|
||||||
|
Alert,
|
||||||
|
Platform,
|
||||||
|
StyleSheet,
|
||||||
|
TouchableOpacity,
|
||||||
|
View,
|
||||||
|
} from "react-native";
|
||||||
import { useSafeAreaInsets } from "react-native-safe-area-context";
|
import { useSafeAreaInsets } from "react-native-safe-area-context";
|
||||||
import { Text } from "@/components/common/Text";
|
import { Text } from "@/components/common/Text";
|
||||||
import useRouter from "@/hooks/useAppRouter";
|
import useRouter from "@/hooks/useAppRouter";
|
||||||
import { useDeletePlaylist } from "@/hooks/usePlaylistMutations";
|
import { useDeletePlaylist } from "@/hooks/usePlaylistMutations";
|
||||||
|
import {
|
||||||
|
type BottomSheetMethods,
|
||||||
|
BottomSheetModal,
|
||||||
|
BottomSheetView,
|
||||||
|
} from "@/utils/expoUiBottomSheet";
|
||||||
|
|
||||||
interface Props {
|
interface Props {
|
||||||
open: boolean;
|
open: boolean;
|
||||||
@@ -25,7 +30,7 @@ export const PlaylistOptionsSheet: React.FC<Props> = ({
|
|||||||
setOpen,
|
setOpen,
|
||||||
playlist,
|
playlist,
|
||||||
}) => {
|
}) => {
|
||||||
const bottomSheetModalRef = useRef<BottomSheetModal>(null);
|
const bottomSheetModalRef = useRef<BottomSheetMethods>(null);
|
||||||
const router = useRouter();
|
const router = useRouter();
|
||||||
const insets = useSafeAreaInsets();
|
const insets = useSafeAreaInsets();
|
||||||
const { t } = useTranslation();
|
const { t } = useTranslation();
|
||||||
@@ -47,17 +52,6 @@ export const PlaylistOptionsSheet: React.FC<Props> = ({
|
|||||||
[setOpen],
|
[setOpen],
|
||||||
);
|
);
|
||||||
|
|
||||||
const renderBackdrop = useCallback(
|
|
||||||
(props: BottomSheetBackdropProps) => (
|
|
||||||
<BottomSheetBackdrop
|
|
||||||
{...props}
|
|
||||||
disappearsOnIndex={-1}
|
|
||||||
appearsOnIndex={0}
|
|
||||||
/>
|
|
||||||
),
|
|
||||||
[],
|
|
||||||
);
|
|
||||||
|
|
||||||
const handleDeletePlaylist = useCallback(() => {
|
const handleDeletePlaylist = useCallback(() => {
|
||||||
if (!playlist?.Id) return;
|
if (!playlist?.Id) return;
|
||||||
|
|
||||||
@@ -89,14 +83,15 @@ export const PlaylistOptionsSheet: React.FC<Props> = ({
|
|||||||
}, [playlist, deletePlaylist, setOpen, router, t]);
|
}, [playlist, deletePlaylist, setOpen, router, t]);
|
||||||
|
|
||||||
if (!playlist) return null;
|
if (!playlist) return null;
|
||||||
|
if (Platform.isTV) return null;
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<BottomSheetModal
|
<BottomSheetModal
|
||||||
ref={bottomSheetModalRef}
|
ref={bottomSheetModalRef}
|
||||||
|
enablePanDownToClose
|
||||||
index={0}
|
index={0}
|
||||||
snapPoints={snapPoints}
|
snapPoints={snapPoints}
|
||||||
onChange={handleSheetChanges}
|
onChange={handleSheetChanges}
|
||||||
backdropComponent={renderBackdrop}
|
|
||||||
handleIndicatorStyle={{
|
handleIndicatorStyle={{
|
||||||
backgroundColor: "white",
|
backgroundColor: "white",
|
||||||
}}
|
}}
|
||||||
|
|||||||
@@ -1,10 +1,4 @@
|
|||||||
import { Ionicons } from "@expo/vector-icons";
|
import { Ionicons } from "@expo/vector-icons";
|
||||||
import {
|
|
||||||
BottomSheetBackdrop,
|
|
||||||
type BottomSheetBackdropProps,
|
|
||||||
BottomSheetModal,
|
|
||||||
BottomSheetScrollView,
|
|
||||||
} from "@gorhom/bottom-sheet";
|
|
||||||
import type { BaseItemDto } from "@jellyfin/sdk/lib/generated-client/models";
|
import type { BaseItemDto } from "@jellyfin/sdk/lib/generated-client/models";
|
||||||
import { getItemsApi } from "@jellyfin/sdk/lib/utils/api";
|
import { getItemsApi } from "@jellyfin/sdk/lib/utils/api";
|
||||||
import { useQuery } from "@tanstack/react-query";
|
import { useQuery } from "@tanstack/react-query";
|
||||||
@@ -20,6 +14,7 @@ import React, {
|
|||||||
import { useTranslation } from "react-i18next";
|
import { useTranslation } from "react-i18next";
|
||||||
import {
|
import {
|
||||||
ActivityIndicator,
|
ActivityIndicator,
|
||||||
|
Platform,
|
||||||
StyleSheet,
|
StyleSheet,
|
||||||
TouchableOpacity,
|
TouchableOpacity,
|
||||||
View,
|
View,
|
||||||
@@ -29,6 +24,11 @@ import { Input } from "@/components/common/Input";
|
|||||||
import { Text } from "@/components/common/Text";
|
import { Text } from "@/components/common/Text";
|
||||||
import { useAddToPlaylist } from "@/hooks/usePlaylistMutations";
|
import { useAddToPlaylist } from "@/hooks/usePlaylistMutations";
|
||||||
import { apiAtom, userAtom } from "@/providers/JellyfinProvider";
|
import { apiAtom, userAtom } from "@/providers/JellyfinProvider";
|
||||||
|
import {
|
||||||
|
type BottomSheetMethods,
|
||||||
|
BottomSheetModal,
|
||||||
|
BottomSheetScrollView,
|
||||||
|
} from "@/utils/expoUiBottomSheet";
|
||||||
|
|
||||||
interface Props {
|
interface Props {
|
||||||
open: boolean;
|
open: boolean;
|
||||||
@@ -43,7 +43,7 @@ export const PlaylistPickerSheet: React.FC<Props> = ({
|
|||||||
trackToAdd,
|
trackToAdd,
|
||||||
onCreateNew,
|
onCreateNew,
|
||||||
}) => {
|
}) => {
|
||||||
const bottomSheetModalRef = useRef<BottomSheetModal>(null);
|
const bottomSheetModalRef = useRef<BottomSheetMethods>(null);
|
||||||
const [api] = useAtom(apiAtom);
|
const [api] = useAtom(apiAtom);
|
||||||
const [user] = useAtom(userAtom);
|
const [user] = useAtom(userAtom);
|
||||||
const insets = useSafeAreaInsets();
|
const insets = useSafeAreaInsets();
|
||||||
@@ -101,17 +101,6 @@ export const PlaylistPickerSheet: React.FC<Props> = ({
|
|||||||
[setOpen],
|
[setOpen],
|
||||||
);
|
);
|
||||||
|
|
||||||
const renderBackdrop = useCallback(
|
|
||||||
(props: BottomSheetBackdropProps) => (
|
|
||||||
<BottomSheetBackdrop
|
|
||||||
{...props}
|
|
||||||
disappearsOnIndex={-1}
|
|
||||||
appearsOnIndex={0}
|
|
||||||
/>
|
|
||||||
),
|
|
||||||
[],
|
|
||||||
);
|
|
||||||
|
|
||||||
const handleSelectPlaylist = useCallback(
|
const handleSelectPlaylist = useCallback(
|
||||||
async (playlist: BaseItemDto) => {
|
async (playlist: BaseItemDto) => {
|
||||||
if (!trackToAdd?.Id || !playlist.Id) return;
|
if (!trackToAdd?.Id || !playlist.Id) return;
|
||||||
@@ -142,13 +131,15 @@ export const PlaylistPickerSheet: React.FC<Props> = ({
|
|||||||
[api],
|
[api],
|
||||||
);
|
);
|
||||||
|
|
||||||
|
if (Platform.isTV) return null;
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<BottomSheetModal
|
<BottomSheetModal
|
||||||
ref={bottomSheetModalRef}
|
ref={bottomSheetModalRef}
|
||||||
|
enablePanDownToClose
|
||||||
index={0}
|
index={0}
|
||||||
snapPoints={snapPoints}
|
snapPoints={snapPoints}
|
||||||
onChange={handleSheetChanges}
|
onChange={handleSheetChanges}
|
||||||
backdropComponent={renderBackdrop}
|
|
||||||
handleIndicatorStyle={{
|
handleIndicatorStyle={{
|
||||||
backgroundColor: "white",
|
backgroundColor: "white",
|
||||||
}}
|
}}
|
||||||
|
|||||||
@@ -1,15 +1,14 @@
|
|||||||
import { Ionicons } from "@expo/vector-icons";
|
import { Ionicons } from "@expo/vector-icons";
|
||||||
import {
|
|
||||||
BottomSheetBackdrop,
|
|
||||||
type BottomSheetBackdropProps,
|
|
||||||
BottomSheetModal,
|
|
||||||
BottomSheetView,
|
|
||||||
} from "@gorhom/bottom-sheet";
|
|
||||||
import React, { useCallback, useEffect, useMemo, useRef } from "react";
|
import React, { useCallback, useEffect, useMemo, useRef } from "react";
|
||||||
import { useTranslation } from "react-i18next";
|
import { useTranslation } from "react-i18next";
|
||||||
import { StyleSheet, TouchableOpacity, View } from "react-native";
|
import { Platform, StyleSheet, TouchableOpacity, View } from "react-native";
|
||||||
import { useSafeAreaInsets } from "react-native-safe-area-context";
|
import { useSafeAreaInsets } from "react-native-safe-area-context";
|
||||||
import { Text } from "@/components/common/Text";
|
import { Text } from "@/components/common/Text";
|
||||||
|
import {
|
||||||
|
type BottomSheetMethods,
|
||||||
|
BottomSheetModal,
|
||||||
|
BottomSheetView,
|
||||||
|
} from "@/utils/expoUiBottomSheet";
|
||||||
|
|
||||||
export type PlaylistSortOption = "SortName" | "DateCreated";
|
export type PlaylistSortOption = "SortName" | "DateCreated";
|
||||||
|
|
||||||
@@ -43,7 +42,7 @@ export const PlaylistSortSheet: React.FC<Props> = ({
|
|||||||
sortOrder,
|
sortOrder,
|
||||||
onSortChange,
|
onSortChange,
|
||||||
}) => {
|
}) => {
|
||||||
const bottomSheetModalRef = useRef<BottomSheetModal>(null);
|
const bottomSheetModalRef = useRef<BottomSheetMethods>(null);
|
||||||
const insets = useSafeAreaInsets();
|
const insets = useSafeAreaInsets();
|
||||||
const { t } = useTranslation();
|
const { t } = useTranslation();
|
||||||
|
|
||||||
@@ -63,17 +62,6 @@ export const PlaylistSortSheet: React.FC<Props> = ({
|
|||||||
[setOpen],
|
[setOpen],
|
||||||
);
|
);
|
||||||
|
|
||||||
const renderBackdrop = useCallback(
|
|
||||||
(props: BottomSheetBackdropProps) => (
|
|
||||||
<BottomSheetBackdrop
|
|
||||||
{...props}
|
|
||||||
disappearsOnIndex={-1}
|
|
||||||
appearsOnIndex={0}
|
|
||||||
/>
|
|
||||||
),
|
|
||||||
[],
|
|
||||||
);
|
|
||||||
|
|
||||||
const handleSortSelect = useCallback(
|
const handleSortSelect = useCallback(
|
||||||
(option: PlaylistSortOption) => {
|
(option: PlaylistSortOption) => {
|
||||||
// If selecting same option, toggle order; otherwise use sensible default
|
// If selecting same option, toggle order; otherwise use sensible default
|
||||||
@@ -93,13 +81,15 @@ export const PlaylistSortSheet: React.FC<Props> = ({
|
|||||||
[sortBy, sortOrder, onSortChange, setOpen],
|
[sortBy, sortOrder, onSortChange, setOpen],
|
||||||
);
|
);
|
||||||
|
|
||||||
|
if (Platform.isTV) return null;
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<BottomSheetModal
|
<BottomSheetModal
|
||||||
ref={bottomSheetModalRef}
|
ref={bottomSheetModalRef}
|
||||||
|
enablePanDownToClose
|
||||||
index={0}
|
index={0}
|
||||||
snapPoints={snapPoints}
|
snapPoints={snapPoints}
|
||||||
onChange={handleSheetChanges}
|
onChange={handleSheetChanges}
|
||||||
backdropComponent={renderBackdrop}
|
|
||||||
handleIndicatorStyle={{
|
handleIndicatorStyle={{
|
||||||
backgroundColor: "white",
|
backgroundColor: "white",
|
||||||
}}
|
}}
|
||||||
|
|||||||
@@ -1,10 +1,4 @@
|
|||||||
import { Ionicons } from "@expo/vector-icons";
|
import { Ionicons } from "@expo/vector-icons";
|
||||||
import {
|
|
||||||
BottomSheetBackdrop,
|
|
||||||
type BottomSheetBackdropProps,
|
|
||||||
BottomSheetModal,
|
|
||||||
BottomSheetView,
|
|
||||||
} from "@gorhom/bottom-sheet";
|
|
||||||
import type { BaseItemDto } from "@jellyfin/sdk/lib/generated-client/models";
|
import type { BaseItemDto } from "@jellyfin/sdk/lib/generated-client/models";
|
||||||
import { Image } from "expo-image";
|
import { Image } from "expo-image";
|
||||||
import { useAtom } from "jotai";
|
import { useAtom } from "jotai";
|
||||||
@@ -18,6 +12,7 @@ import React, {
|
|||||||
import { useTranslation } from "react-i18next";
|
import { useTranslation } from "react-i18next";
|
||||||
import {
|
import {
|
||||||
ActivityIndicator,
|
ActivityIndicator,
|
||||||
|
Platform,
|
||||||
StyleSheet,
|
StyleSheet,
|
||||||
TouchableOpacity,
|
TouchableOpacity,
|
||||||
View,
|
View,
|
||||||
@@ -36,6 +31,11 @@ import {
|
|||||||
} from "@/providers/AudioStorage";
|
} from "@/providers/AudioStorage";
|
||||||
import { apiAtom, userAtom } from "@/providers/JellyfinProvider";
|
import { apiAtom, userAtom } from "@/providers/JellyfinProvider";
|
||||||
import { useMusicPlayer } from "@/providers/MusicPlayerProvider";
|
import { useMusicPlayer } from "@/providers/MusicPlayerProvider";
|
||||||
|
import {
|
||||||
|
type BottomSheetMethods,
|
||||||
|
BottomSheetModal,
|
||||||
|
BottomSheetView,
|
||||||
|
} from "@/utils/expoUiBottomSheet";
|
||||||
import { getAudioStreamUrl } from "@/utils/jellyfin/audio/getAudioStreamUrl";
|
import { getAudioStreamUrl } from "@/utils/jellyfin/audio/getAudioStreamUrl";
|
||||||
import { getPrimaryImageUrl } from "@/utils/jellyfin/image/getPrimaryImageUrl";
|
import { getPrimaryImageUrl } from "@/utils/jellyfin/image/getPrimaryImageUrl";
|
||||||
|
|
||||||
@@ -56,7 +56,7 @@ export const TrackOptionsSheet: React.FC<Props> = ({
|
|||||||
playlistId,
|
playlistId,
|
||||||
onRemoveFromPlaylist,
|
onRemoveFromPlaylist,
|
||||||
}) => {
|
}) => {
|
||||||
const bottomSheetModalRef = useRef<BottomSheetModal>(null);
|
const bottomSheetModalRef = useRef<BottomSheetMethods>(null);
|
||||||
const [api] = useAtom(apiAtom);
|
const [api] = useAtom(apiAtom);
|
||||||
const [user] = useAtom(userAtom);
|
const [user] = useAtom(userAtom);
|
||||||
const router = useRouter();
|
const router = useRouter();
|
||||||
@@ -128,17 +128,6 @@ export const TrackOptionsSheet: React.FC<Props> = ({
|
|||||||
[setOpen],
|
[setOpen],
|
||||||
);
|
);
|
||||||
|
|
||||||
const renderBackdrop = useCallback(
|
|
||||||
(props: BottomSheetBackdropProps) => (
|
|
||||||
<BottomSheetBackdrop
|
|
||||||
{...props}
|
|
||||||
disappearsOnIndex={-1}
|
|
||||||
appearsOnIndex={0}
|
|
||||||
/>
|
|
||||||
),
|
|
||||||
[],
|
|
||||||
);
|
|
||||||
|
|
||||||
const handlePlayNext = useCallback(() => {
|
const handlePlayNext = useCallback(() => {
|
||||||
if (track) {
|
if (track) {
|
||||||
playNext(track);
|
playNext(track);
|
||||||
@@ -227,13 +216,14 @@ export const TrackOptionsSheet: React.FC<Props> = ({
|
|||||||
const hasAlbum = !!(track?.AlbumId || track?.ParentId);
|
const hasAlbum = !!(track?.AlbumId || track?.ParentId);
|
||||||
|
|
||||||
if (!track) return null;
|
if (!track) return null;
|
||||||
|
if (Platform.isTV) return null;
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<BottomSheetModal
|
<BottomSheetModal
|
||||||
ref={bottomSheetModalRef}
|
ref={bottomSheetModalRef}
|
||||||
|
enablePanDownToClose
|
||||||
enableDynamicSizing
|
enableDynamicSizing
|
||||||
onChange={handleSheetChanges}
|
onChange={handleSheetChanges}
|
||||||
backdropComponent={renderBackdrop}
|
|
||||||
handleIndicatorStyle={{
|
handleIndicatorStyle={{
|
||||||
backgroundColor: "white",
|
backgroundColor: "white",
|
||||||
}}
|
}}
|
||||||
|
|||||||
@@ -1,14 +1,9 @@
|
|||||||
import { Ionicons } from "@expo/vector-icons";
|
import { Ionicons } from "@expo/vector-icons";
|
||||||
import {
|
|
||||||
BottomSheetBackdrop,
|
|
||||||
type BottomSheetBackdropProps,
|
|
||||||
BottomSheetModal,
|
|
||||||
BottomSheetView,
|
|
||||||
} from "@gorhom/bottom-sheet";
|
|
||||||
import type React from "react";
|
import type React from "react";
|
||||||
import { useCallback, useEffect, useRef } from "react";
|
import { useCallback, useEffect, useRef } from "react";
|
||||||
import { useTranslation } from "react-i18next";
|
import { useTranslation } from "react-i18next";
|
||||||
import {
|
import {
|
||||||
|
Platform,
|
||||||
StyleSheet,
|
StyleSheet,
|
||||||
TouchableOpacity,
|
TouchableOpacity,
|
||||||
View,
|
View,
|
||||||
@@ -16,6 +11,11 @@ import {
|
|||||||
} from "react-native";
|
} from "react-native";
|
||||||
import { useSafeAreaInsets } from "react-native-safe-area-context";
|
import { useSafeAreaInsets } from "react-native-safe-area-context";
|
||||||
import { Text } from "@/components/common/Text";
|
import { Text } from "@/components/common/Text";
|
||||||
|
import {
|
||||||
|
type BottomSheetMethods,
|
||||||
|
BottomSheetModal,
|
||||||
|
BottomSheetView,
|
||||||
|
} from "@/utils/expoUiBottomSheet";
|
||||||
|
|
||||||
interface LibraryOptions {
|
interface LibraryOptions {
|
||||||
display: "row" | "list";
|
display: "row" | "list";
|
||||||
@@ -132,7 +132,7 @@ export const LibraryOptionsSheet: React.FC<Props> = ({
|
|||||||
updateSettings,
|
updateSettings,
|
||||||
disabled = false,
|
disabled = false,
|
||||||
}) => {
|
}) => {
|
||||||
const bottomSheetModalRef = useRef<BottomSheetModal>(null);
|
const bottomSheetModalRef = useRef<BottomSheetMethods>(null);
|
||||||
const { t } = useTranslation();
|
const { t } = useTranslation();
|
||||||
const insets = useSafeAreaInsets();
|
const insets = useSafeAreaInsets();
|
||||||
|
|
||||||
@@ -161,25 +161,14 @@ export const LibraryOptionsSheet: React.FC<Props> = ({
|
|||||||
[setOpen],
|
[setOpen],
|
||||||
);
|
);
|
||||||
|
|
||||||
const renderBackdrop = useCallback(
|
|
||||||
(props: BottomSheetBackdropProps) => (
|
|
||||||
<BottomSheetBackdrop
|
|
||||||
{...props}
|
|
||||||
disappearsOnIndex={-1}
|
|
||||||
appearsOnIndex={0}
|
|
||||||
/>
|
|
||||||
),
|
|
||||||
[],
|
|
||||||
);
|
|
||||||
|
|
||||||
if (disabled) return null;
|
if (disabled) return null;
|
||||||
|
if (Platform.isTV) return null;
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<BottomSheetModal
|
<BottomSheetModal
|
||||||
ref={bottomSheetModalRef}
|
ref={bottomSheetModalRef}
|
||||||
enableDynamicSizing
|
enableDynamicSizing
|
||||||
onChange={handleSheetChanges}
|
onChange={handleSheetChanges}
|
||||||
backdropComponent={renderBackdrop}
|
|
||||||
handleIndicatorStyle={{
|
handleIndicatorStyle={{
|
||||||
backgroundColor: "white",
|
backgroundColor: "white",
|
||||||
}}
|
}}
|
||||||
@@ -187,7 +176,6 @@ export const LibraryOptionsSheet: React.FC<Props> = ({
|
|||||||
backgroundColor: "#171717",
|
backgroundColor: "#171717",
|
||||||
}}
|
}}
|
||||||
enablePanDownToClose
|
enablePanDownToClose
|
||||||
enableDismissOnClose
|
|
||||||
>
|
>
|
||||||
<BottomSheetView>
|
<BottomSheetView>
|
||||||
<View
|
<View
|
||||||
|
|||||||
@@ -1,9 +1,3 @@
|
|||||||
import {
|
|
||||||
BottomSheetBackdrop,
|
|
||||||
type BottomSheetBackdropProps,
|
|
||||||
BottomSheetModal,
|
|
||||||
BottomSheetView,
|
|
||||||
} from "@gorhom/bottom-sheet";
|
|
||||||
import { getQuickConnectApi } from "@jellyfin/sdk/lib/utils/api";
|
import { getQuickConnectApi } from "@jellyfin/sdk/lib/utils/api";
|
||||||
import { useAtom } from "jotai";
|
import { useAtom } from "jotai";
|
||||||
import type React from "react";
|
import type React from "react";
|
||||||
@@ -12,6 +6,11 @@ import { useTranslation } from "react-i18next";
|
|||||||
import { Alert, Platform, View, type ViewProps } from "react-native";
|
import { Alert, Platform, View, type ViewProps } from "react-native";
|
||||||
import { useHaptic } from "@/hooks/useHaptic";
|
import { useHaptic } from "@/hooks/useHaptic";
|
||||||
import { apiAtom, userAtom } from "@/providers/JellyfinProvider";
|
import { apiAtom, userAtom } from "@/providers/JellyfinProvider";
|
||||||
|
import {
|
||||||
|
type BottomSheetMethods,
|
||||||
|
BottomSheetModal,
|
||||||
|
BottomSheetView,
|
||||||
|
} from "@/utils/expoUiBottomSheet";
|
||||||
import { Button } from "../Button";
|
import { Button } from "../Button";
|
||||||
import { Text } from "../common/Text";
|
import { Text } from "../common/Text";
|
||||||
import { PinInput } from "../inputs/PinInput";
|
import { PinInput } from "../inputs/PinInput";
|
||||||
@@ -25,7 +24,7 @@ export const QuickConnect: React.FC<Props> = ({ ...props }) => {
|
|||||||
const [api] = useAtom(apiAtom);
|
const [api] = useAtom(apiAtom);
|
||||||
const [user] = useAtom(userAtom);
|
const [user] = useAtom(userAtom);
|
||||||
const [quickConnectCode, setQuickConnectCode] = useState<string>();
|
const [quickConnectCode, setQuickConnectCode] = useState<string>();
|
||||||
const bottomSheetModalRef = useRef<BottomSheetModal>(null);
|
const bottomSheetModalRef = useRef<BottomSheetMethods>(null);
|
||||||
const successHapticFeedback = useHaptic("success");
|
const successHapticFeedback = useHaptic("success");
|
||||||
const errorHapticFeedback = useHaptic("error");
|
const errorHapticFeedback = useHaptic("error");
|
||||||
const snapPoints = useMemo(
|
const snapPoints = useMemo(
|
||||||
@@ -36,17 +35,6 @@ export const QuickConnect: React.FC<Props> = ({ ...props }) => {
|
|||||||
|
|
||||||
const { t } = useTranslation();
|
const { t } = useTranslation();
|
||||||
|
|
||||||
const renderBackdrop = useCallback(
|
|
||||||
(props: BottomSheetBackdropProps) => (
|
|
||||||
<BottomSheetBackdrop
|
|
||||||
{...props}
|
|
||||||
disappearsOnIndex={-1}
|
|
||||||
appearsOnIndex={0}
|
|
||||||
/>
|
|
||||||
),
|
|
||||||
[],
|
|
||||||
);
|
|
||||||
|
|
||||||
const authorizeQuickConnect = useCallback(async () => {
|
const authorizeQuickConnect = useCallback(async () => {
|
||||||
if (quickConnectCode) {
|
if (quickConnectCode) {
|
||||||
try {
|
try {
|
||||||
@@ -97,6 +85,7 @@ export const QuickConnect: React.FC<Props> = ({ ...props }) => {
|
|||||||
|
|
||||||
<BottomSheetModal
|
<BottomSheetModal
|
||||||
ref={bottomSheetModalRef}
|
ref={bottomSheetModalRef}
|
||||||
|
enablePanDownToClose
|
||||||
snapPoints={snapPoints}
|
snapPoints={snapPoints}
|
||||||
handleIndicatorStyle={{
|
handleIndicatorStyle={{
|
||||||
backgroundColor: "white",
|
backgroundColor: "white",
|
||||||
@@ -104,11 +93,8 @@ export const QuickConnect: React.FC<Props> = ({ ...props }) => {
|
|||||||
backgroundStyle={{
|
backgroundStyle={{
|
||||||
backgroundColor: "#171717",
|
backgroundColor: "#171717",
|
||||||
}}
|
}}
|
||||||
backdropComponent={renderBackdrop}
|
|
||||||
keyboardBehavior={isAndroid ? "fillParent" : "interactive"}
|
keyboardBehavior={isAndroid ? "fillParent" : "interactive"}
|
||||||
keyboardBlurBehavior='restore'
|
keyboardBlurBehavior='restore'
|
||||||
android_keyboardInputMode='adjustResize'
|
|
||||||
topInset={isAndroid ? 0 : undefined}
|
|
||||||
>
|
>
|
||||||
<BottomSheetView>
|
<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'>
|
||||||
|
|||||||
@@ -1,10 +1,4 @@
|
|||||||
import { Ionicons } from "@expo/vector-icons";
|
import { Ionicons } from "@expo/vector-icons";
|
||||||
import {
|
|
||||||
BottomSheetBackdrop,
|
|
||||||
type BottomSheetBackdropProps,
|
|
||||||
BottomSheetModal,
|
|
||||||
BottomSheetView,
|
|
||||||
} from "@gorhom/bottom-sheet";
|
|
||||||
import type { BaseItemDto } from "@jellyfin/sdk/lib/generated-client/models";
|
import type { BaseItemDto } from "@jellyfin/sdk/lib/generated-client/models";
|
||||||
import React, {
|
import React, {
|
||||||
forwardRef,
|
forwardRef,
|
||||||
@@ -16,6 +10,7 @@ import React, {
|
|||||||
import { useTranslation } from "react-i18next";
|
import { useTranslation } from "react-i18next";
|
||||||
import {
|
import {
|
||||||
ActivityIndicator,
|
ActivityIndicator,
|
||||||
|
Platform,
|
||||||
StyleSheet,
|
StyleSheet,
|
||||||
TouchableOpacity,
|
TouchableOpacity,
|
||||||
View,
|
View,
|
||||||
@@ -31,6 +26,11 @@ import {
|
|||||||
useItemInWatchlists,
|
useItemInWatchlists,
|
||||||
useMyWatchlistsQuery,
|
useMyWatchlistsQuery,
|
||||||
} from "@/hooks/useWatchlists";
|
} from "@/hooks/useWatchlists";
|
||||||
|
import {
|
||||||
|
type BottomSheetMethods,
|
||||||
|
BottomSheetModal,
|
||||||
|
BottomSheetView,
|
||||||
|
} from "@/utils/expoUiBottomSheet";
|
||||||
import type { StreamystatsWatchlist } from "@/utils/streamystats/types";
|
import type { StreamystatsWatchlist } from "@/utils/streamystats/types";
|
||||||
|
|
||||||
export interface WatchlistSheetRef {
|
export interface WatchlistSheetRef {
|
||||||
@@ -263,7 +263,7 @@ const WatchlistSheetContent: React.FC<WatchlistSheetContentProps> = ({
|
|||||||
|
|
||||||
export const WatchlistSheet = forwardRef<WatchlistSheetRef, object>(
|
export const WatchlistSheet = forwardRef<WatchlistSheetRef, object>(
|
||||||
(_props, ref) => {
|
(_props, ref) => {
|
||||||
const bottomSheetModalRef = useRef<BottomSheetModal>(null);
|
const bottomSheetModalRef = useRef<BottomSheetMethods>(null);
|
||||||
const [currentItem, setCurrentItem] = React.useState<BaseItemDto | null>(
|
const [currentItem, setCurrentItem] = React.useState<BaseItemDto | null>(
|
||||||
null,
|
null,
|
||||||
);
|
);
|
||||||
@@ -283,23 +283,13 @@ export const WatchlistSheet = forwardRef<WatchlistSheetRef, object>(
|
|||||||
bottomSheetModalRef.current?.dismiss();
|
bottomSheetModalRef.current?.dismiss();
|
||||||
}, []);
|
}, []);
|
||||||
|
|
||||||
const renderBackdrop = useCallback(
|
if (Platform.isTV) return null;
|
||||||
(props: BottomSheetBackdropProps) => (
|
|
||||||
<BottomSheetBackdrop
|
|
||||||
{...props}
|
|
||||||
disappearsOnIndex={-1}
|
|
||||||
appearsOnIndex={0}
|
|
||||||
/>
|
|
||||||
),
|
|
||||||
[],
|
|
||||||
);
|
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<BottomSheetModal
|
<BottomSheetModal
|
||||||
ref={bottomSheetModalRef}
|
ref={bottomSheetModalRef}
|
||||||
|
enablePanDownToClose
|
||||||
enableDynamicSizing
|
enableDynamicSizing
|
||||||
maxDynamicContentSize={600}
|
|
||||||
backdropComponent={renderBackdrop}
|
|
||||||
handleIndicatorStyle={{
|
handleIndicatorStyle={{
|
||||||
backgroundColor: "white",
|
backgroundColor: "white",
|
||||||
}}
|
}}
|
||||||
|
|||||||
@@ -32,7 +32,6 @@
|
|||||||
"@expo/react-native-action-sheet": "^4.1.1",
|
"@expo/react-native-action-sheet": "^4.1.1",
|
||||||
"@expo/ui": "~56.0.14",
|
"@expo/ui": "~56.0.14",
|
||||||
"@expo/vector-icons": "^15.0.3",
|
"@expo/vector-icons": "^15.0.3",
|
||||||
"@gorhom/bottom-sheet": "5.2.8",
|
|
||||||
"@jellyfin/sdk": "^0.13.0",
|
"@jellyfin/sdk": "^0.13.0",
|
||||||
"@react-native-community/netinfo": "^12.0.0",
|
"@react-native-community/netinfo": "^12.0.0",
|
||||||
"@react-navigation/material-top-tabs": "7.4.28",
|
"@react-navigation/material-top-tabs": "7.4.28",
|
||||||
|
|||||||
@@ -1,4 +1,3 @@
|
|||||||
import type { BottomSheetModal } from "@gorhom/bottom-sheet";
|
|
||||||
import type React from "react";
|
import type React from "react";
|
||||||
import {
|
import {
|
||||||
createContext,
|
createContext,
|
||||||
@@ -11,6 +10,7 @@ import {
|
|||||||
} from "react";
|
} from "react";
|
||||||
|
|
||||||
import { BackHandler, Platform } from "react-native";
|
import { BackHandler, Platform } from "react-native";
|
||||||
|
import type { BottomSheetMethods } from "@/utils/expoUiBottomSheet";
|
||||||
|
|
||||||
interface ModalOptions {
|
interface ModalOptions {
|
||||||
enableDynamicSizing?: boolean;
|
enableDynamicSizing?: boolean;
|
||||||
@@ -30,7 +30,7 @@ interface GlobalModalContextType {
|
|||||||
hideModal: () => void;
|
hideModal: () => void;
|
||||||
isVisible: boolean;
|
isVisible: boolean;
|
||||||
modalState: GlobalModalState;
|
modalState: GlobalModalState;
|
||||||
modalRef: React.RefObject<BottomSheetModal | null>;
|
modalRef: React.RefObject<BottomSheetMethods | null>;
|
||||||
}
|
}
|
||||||
|
|
||||||
const GlobalModalContext = createContext<GlobalModalContextType | undefined>(
|
const GlobalModalContext = createContext<GlobalModalContextType | undefined>(
|
||||||
@@ -57,7 +57,7 @@ export const GlobalModalProvider: React.FC<GlobalModalProviderProps> = ({
|
|||||||
options: undefined,
|
options: undefined,
|
||||||
});
|
});
|
||||||
const [isVisible, setIsVisible] = useState(false);
|
const [isVisible, setIsVisible] = useState(false);
|
||||||
const modalRef = useRef<BottomSheetModal>(null);
|
const modalRef = useRef<BottomSheetMethods>(null);
|
||||||
|
|
||||||
const showModal = useCallback(
|
const showModal = useCallback(
|
||||||
(content: ReactNode, options?: ModalOptions) => {
|
(content: ReactNode, options?: ModalOptions) => {
|
||||||
|
|||||||
40
utils/expoUiBottomSheet.ts
Normal file
40
utils/expoUiBottomSheet.ts
Normal file
@@ -0,0 +1,40 @@
|
|||||||
|
import { Platform } from "react-native";
|
||||||
|
|
||||||
|
/**
|
||||||
|
* TV-safe re-exports of `@expo/ui/community/bottom-sheet`.
|
||||||
|
*
|
||||||
|
* `@expo/ui` resolves its SwiftUI bridge at module load via
|
||||||
|
* `requireNativeModule('ExpoUI')`. That native module does not exist on tvOS,
|
||||||
|
* so a static top-level import from any route file crashes the whole route
|
||||||
|
* tree (expo-router eagerly loads every route).
|
||||||
|
*
|
||||||
|
* We `require()` the module lazily and only when *not* on tvOS. On TV the
|
||||||
|
* exported components are `undefined`, which is fine because every call site
|
||||||
|
* must early-return (`if (Platform.isTV) return null;`) before rendering a
|
||||||
|
* bottom sheet.
|
||||||
|
*
|
||||||
|
* Usage:
|
||||||
|
*
|
||||||
|
* import {
|
||||||
|
* BottomSheetModal,
|
||||||
|
* BottomSheetView,
|
||||||
|
* type BottomSheetMethods,
|
||||||
|
* } from "@/utils/expoUiBottomSheet";
|
||||||
|
*
|
||||||
|
* const ref = useRef<BottomSheetMethods>(null);
|
||||||
|
*
|
||||||
|
* if (Platform.isTV) return null;
|
||||||
|
* return <BottomSheetModal ref={ref}>...</BottomSheetModal>;
|
||||||
|
*/
|
||||||
|
type BottomSheetMod = typeof import("@expo/ui/community/bottom-sheet");
|
||||||
|
|
||||||
|
const mod: BottomSheetMod = Platform.isTV
|
||||||
|
? ({} as BottomSheetMod)
|
||||||
|
: (require("@expo/ui/community/bottom-sheet") as BottomSheetMod);
|
||||||
|
|
||||||
|
export const BottomSheetModal = mod.BottomSheetModal;
|
||||||
|
export const BottomSheetView = mod.BottomSheetView;
|
||||||
|
export const BottomSheetScrollView = mod.BottomSheetScrollView;
|
||||||
|
export const BottomSheetTextInput = mod.BottomSheetTextInput;
|
||||||
|
|
||||||
|
export type { BottomSheetMethods } from "@expo/ui/community/bottom-sheet";
|
||||||
Reference in New Issue
Block a user