diff --git a/app.json b/app.json index dc399d04..7b40fa03 100644 --- a/app.json +++ b/app.json @@ -63,12 +63,6 @@ } } ], - [ - "expo-screen-orientation", - { - "initialOrientation": "DEFAULT" - } - ], [ "expo-sensors", { diff --git a/app/(auth)/(tabs)/(custom-links)/_layout.tsx b/app/(auth)/(tabs)/(custom-links)/_layout.tsx deleted file mode 100644 index ed0529d4..00000000 --- a/app/(auth)/(tabs)/(custom-links)/_layout.tsx +++ /dev/null @@ -1,20 +0,0 @@ -import {Stack} from "expo-router"; -import { Platform } from "react-native"; - -export default function CustomMenuLayout() { - return ( - - - - ); -} diff --git a/app/(auth)/(tabs)/(custom-links)/index.tsx b/app/(auth)/(tabs)/(custom-links)/index.tsx deleted file mode 100644 index 76b10fb8..00000000 --- a/app/(auth)/(tabs)/(custom-links)/index.tsx +++ /dev/null @@ -1,73 +0,0 @@ -import {FlatList, TouchableOpacity, View} from "react-native"; -import {useSafeAreaInsets} from "react-native-safe-area-context"; -import React, {useCallback, useEffect, useState} from "react"; -import {useAtom} from "jotai/index"; -import {apiAtom} from "@/providers/JellyfinProvider"; -import {ListItem} from "@/components/ListItem"; -import * as WebBrowser from 'expo-web-browser'; -import Ionicons from '@expo/vector-icons/Ionicons'; -import {Text} from "@/components/common/Text"; - -export interface MenuLink { - name: string, - url: string, - icon: string -} - -export default function menuLinks() { - const [api] = useAtom(apiAtom); - const insets = useSafeAreaInsets() - const [menuLinks, setMenuLinks] = useState([]) - - const getMenuLinks = useCallback(async () => { - try { - const response = await api?.axiosInstance.get(api?.basePath + "/web/config.json") - const config = response?.data; - - if (!config && !config.hasOwnProperty("menuLinks")) { - console.error("Menu links not found"); - return; - } - - setMenuLinks(config?.menuLinks as MenuLink[]) - } catch (error) { - console.error("Failed to retrieve config:", error); - } - }, - [api] - ) - - useEffect(() => { getMenuLinks() }, []); - return ( - ( - WebBrowser.openBrowserAsync(item.url) }> - } - /> - - ) - } - ItemSeparatorComponent={() => ( - - )} - ListEmptyComponent={ - - No links - - } - /> - ); -} \ No newline at end of file diff --git a/app/(auth)/(tabs)/(home)/settings.tsx b/app/(auth)/(tabs)/(home)/settings.tsx index 72b362b3..e19001c5 100644 --- a/app/(auth)/(tabs)/(home)/settings.tsx +++ b/app/(auth)/(tabs)/(home)/settings.tsx @@ -7,7 +7,6 @@ import { clearLogs, useLog } from "@/utils/log"; import { getQuickConnectApi } from "@jellyfin/sdk/lib/utils/api"; import { useQuery } from "@tanstack/react-query"; import * as FileSystem from "expo-file-system"; -import * as Haptics from "expo-haptics"; import { useAtom } from "jotai"; import { Alert, ScrollView, View } from "react-native"; import * as Progress from "react-native-progress"; @@ -35,16 +34,11 @@ export default function settings() { userId: user?.Id, }); if (res.status === 200) { - Haptics.notificationAsync( - Haptics.NotificationFeedbackType.Success - ); Alert.alert("Success", "Quick connect authorized"); } else { - Haptics.notificationAsync(Haptics.NotificationFeedbackType.Error); Alert.alert("Error", "Invalid code"); } } catch (e) { - Haptics.notificationAsync(Haptics.NotificationFeedbackType.Error); Alert.alert("Error", "Invalid code"); } } diff --git a/app/(auth)/(tabs)/(home,libraries,search)/collections/[collectionId].tsx b/app/(auth)/(tabs)/(home,libraries,search)/collections/[collectionId].tsx index 4c2b72ae..63351a59 100644 --- a/app/(auth)/(tabs)/(home,libraries,search)/collections/[collectionId].tsx +++ b/app/(auth)/(tabs)/(home,libraries,search)/collections/[collectionId].tsx @@ -29,7 +29,6 @@ import { import { FlashList } from "@shopify/flash-list"; import { useInfiniteQuery, useQuery } from "@tanstack/react-query"; import { useLocalSearchParams, useNavigation } from "expo-router"; -import * as ScreenOrientation from "expo-screen-orientation"; import { useAtom } from "jotai"; import React, { useCallback, useEffect, useMemo, useState } from "react"; import { FlatList, View } from "react-native"; @@ -41,10 +40,6 @@ const page: React.FC = () => { const [api] = useAtom(apiAtom); const [user] = useAtom(userAtom); const navigation = useNavigation(); - const [orientation, setOrientation] = useState( - ScreenOrientation.Orientation.PORTRAIT_UP - ); - const [selectedGenres, setSelectedGenres] = useAtom(genreFilterAtom); const [selectedYears, setSelectedYears] = useAtom(yearFilterAtom); const [selectedTags, setSelectedTags] = useAtom(tagsFilterAtom); @@ -174,8 +169,7 @@ const page: React.FC = () => { key={item.Id} style={{ width: "100%", - marginBottom: - orientation === ScreenOrientation.Orientation.PORTRAIT_UP ? 4 : 16, + marginBottom: 16, }} item={item} > @@ -389,9 +383,7 @@ const page: React.FC = () => { renderItem={renderItem} keyExtractor={keyExtractor} estimatedItemSize={255} - numColumns={ - orientation === ScreenOrientation.Orientation.PORTRAIT_UP ? 3 : 5 - } + numColumns={5} onEndReached={() => { if (hasNextPage) { fetchNextPage(); diff --git a/app/(auth)/(tabs)/(libraries)/[libraryId].tsx b/app/(auth)/(tabs)/(libraries)/[libraryId].tsx index bd0df182..0d69348c 100644 --- a/app/(auth)/(tabs)/(libraries)/[libraryId].tsx +++ b/app/(auth)/(tabs)/(libraries)/[libraryId].tsx @@ -1,6 +1,5 @@ import { useInfiniteQuery, useQuery } from "@tanstack/react-query"; import { useLocalSearchParams, useNavigation } from "expo-router"; -import * as ScreenOrientation from "expo-screen-orientation"; import { useAtom } from "jotai"; import React, { useCallback, useEffect, useMemo } from "react"; import { FlatList, useWindowDimensions, View } from "react-native"; @@ -12,7 +11,6 @@ import { ResetFiltersButton } from "@/components/filters/ResetFiltersButton"; import { ItemCardText } from "@/components/ItemCardText"; import { Loader } from "@/components/Loader"; import { ItemPoster } from "@/components/posters/ItemPoster"; -import { useOrientation } from "@/hooks/useOrientation"; import { apiAtom, userAtom } from "@/providers/JellyfinProvider"; import { genreFilterAtom, @@ -61,8 +59,6 @@ const Page = () => { sortOrderPreferenceAtom ); - const { orientation } = useOrientation(); - useEffect(() => { const sop = getSortOrderPreference(libraryId, sortOrderPreference); if (sop) { @@ -241,14 +237,7 @@ const Page = () => { > diff --git a/app/(auth)/player/direct-player.tsx b/app/(auth)/player/direct-player.tsx index 22670cf0..0acbff13 100644 --- a/app/(auth)/player/direct-player.tsx +++ b/app/(auth)/player/direct-player.tsx @@ -2,8 +2,6 @@ import { BITRATES } from "@/components/BitrateSelector"; import { Text } from "@/components/common/Text"; import { Loader } from "@/components/Loader"; import { Controls } from "@/components/video-player/controls/Controls"; -import { useOrientation } from "@/hooks/useOrientation"; -import { useOrientationSettings } from "@/hooks/useOrientationSettings"; import { useInvalidatePlaybackProgressCache } from "@/hooks/useRevalidatePlaybackProgressCache"; import { useWebSocket } from "@/hooks/useWebsockets"; import { VlcPlayerView } from "@/modules/vlc-player"; @@ -26,7 +24,6 @@ import { getUserLibraryApi, } from "@jellyfin/sdk/lib/utils/api"; import { useQuery } from "@tanstack/react-query"; -import * as Haptics from "expo-haptics"; import { useFocusEffect, useGlobalSearchParams } from "expo-router"; import { useAtomValue } from "jotai"; import React, { @@ -59,7 +56,6 @@ export default function page() { const setShowControls = useCallback((show: boolean) => { _setShowControls(show); - Haptics.impactAsync(Haptics.ImpactFeedbackStyle.Light); }, []); const { @@ -142,7 +138,6 @@ export default function page() { const togglePlay = useCallback(async () => { if (!api) return; - Haptics.impactAsync(Haptics.ImpactFeedbackStyle.Light); if (isPlaying) { await videoRef.current?.pause(); @@ -254,9 +249,6 @@ export default function page() { [item?.Id, isPlaying, api, isPlaybackStopped, audioIndex, subtitleIndex] ); - useOrientation(); - useOrientationSettings(); - useWebSocket({ isPlaying: isPlaying, togglePlay: togglePlay, diff --git a/app/(auth)/player/music-player.tsx b/app/(auth)/player/music-player.tsx index 13aa4ecc..cdef5a8e 100644 --- a/app/(auth)/player/music-player.tsx +++ b/app/(auth)/player/music-player.tsx @@ -1,8 +1,6 @@ import { Text } from "@/components/common/Text"; import { Loader } from "@/components/Loader"; import { Controls } from "@/components/video-player/controls/Controls"; -import { useOrientation } from "@/hooks/useOrientation"; -import { useOrientationSettings } from "@/hooks/useOrientationSettings"; import { useWebSocket } from "@/hooks/useWebsockets"; import { apiAtom, userAtom } from "@/providers/JellyfinProvider"; import { useSettings } from "@/utils/atoms/settings"; @@ -17,7 +15,6 @@ import { getUserLibraryApi, } from "@jellyfin/sdk/lib/utils/api"; import { useQuery } from "@tanstack/react-query"; -import * as Haptics from "expo-haptics"; import { Image } from "expo-image"; import { useFocusEffect, useLocalSearchParams } from "expo-router"; import { useAtomValue } from "jotai"; @@ -124,7 +121,6 @@ export default function page() { const togglePlay = useCallback( async (ticks: number) => { - Haptics.impactAsync(Haptics.ImpactFeedbackStyle.Light); if (isPlaying) { videoRef.current?.pause(); await getPlaystateApi(api!).onPlaybackProgress({ @@ -261,9 +257,6 @@ export default function page() { }, [play, stop]) ); - useOrientation(); - useOrientationSettings(); - useWebSocket({ isPlaying: isPlaying, pauseVideo: pause, diff --git a/app/(auth)/player/transcoding-player.tsx b/app/(auth)/player/transcoding-player.tsx index 2191e441..6dd88eb2 100644 --- a/app/(auth)/player/transcoding-player.tsx +++ b/app/(auth)/player/transcoding-player.tsx @@ -1,8 +1,6 @@ import { Text } from "@/components/common/Text"; import { Loader } from "@/components/Loader"; import { Controls } from "@/components/video-player/controls/Controls"; -import { useOrientation } from "@/hooks/useOrientation"; -import { useOrientationSettings } from "@/hooks/useOrientationSettings"; import { useInvalidatePlaybackProgressCache } from "@/hooks/useRevalidatePlaybackProgressCache"; import { useWebSocket } from "@/hooks/useWebsockets"; import { TrackInfo } from "@/modules/vlc-player"; @@ -20,7 +18,6 @@ import { getUserLibraryApi, } from "@jellyfin/sdk/lib/utils/api"; import { useQuery } from "@tanstack/react-query"; -import * as Haptics from "expo-haptics"; import { useFocusEffect, useLocalSearchParams } from "expo-router"; import { useAtomValue } from "jotai"; import React, { @@ -58,7 +55,6 @@ const Player = () => { const setShowControls = useCallback((show: boolean) => { _setShowControls(show); - Haptics.impactAsync(Haptics.ImpactFeedbackStyle.Light); }, []); const progress = useSharedValue(0); @@ -167,7 +163,6 @@ const Player = () => { const videoSource = useVideoSource(item, api, poster, stream?.url); const togglePlay = useCallback(async () => { - Haptics.impactAsync(Haptics.ImpactFeedbackStyle.Light); if (isPlaying) { videoRef.current?.pause(); await getPlaystateApi(api!).onPlaybackProgress({ @@ -299,9 +294,6 @@ const Player = () => { ] ); - useOrientation(); - useOrientationSettings(); - useWebSocket({ isPlaying: isPlaying, togglePlay: togglePlay, diff --git a/bun.lockb b/bun.lockb index fd8896ee..f0112a99 100755 Binary files a/bun.lockb and b/bun.lockb differ diff --git a/components/Button.tsx b/components/Button.tsx index 1a73ad01..0a46b7ee 100644 --- a/components/Button.tsx +++ b/components/Button.tsx @@ -1,4 +1,3 @@ -import * as Haptics from "expo-haptics"; import React, { PropsWithChildren, ReactNode, useMemo } from "react"; import { Text, TouchableOpacity, View } from "react-native"; import { Loader } from "./Loader"; @@ -54,7 +53,6 @@ export const Button: React.FC> = ({ onPress={() => { if (!loading && !disabled && onPress) { onPress(); - Haptics.impactAsync(Haptics.ImpactFeedbackStyle.Light); } }} disabled={disabled || loading} diff --git a/components/ItemContent.tsx b/components/ItemContent.tsx index a1d82b83..e4c5e820 100644 --- a/components/ItemContent.tsx +++ b/components/ItemContent.tsx @@ -12,7 +12,6 @@ import { CurrentSeries } from "@/components/series/CurrentSeries"; import { SeasonEpisodesCarousel } from "@/components/series/SeasonEpisodesCarousel"; import useDefaultPlaySettings from "@/hooks/useDefaultPlaySettings"; import { useImageColors } from "@/hooks/useImageColors"; -import { useOrientation } from "@/hooks/useOrientation"; import { apiAtom } from "@/providers/JellyfinProvider"; import { SubtitleHelper } from "@/utils/SubtitleHelper"; import { useSettings } from "@/utils/atoms/settings"; @@ -23,7 +22,6 @@ import { } from "@jellyfin/sdk/lib/generated-client/models"; import { Image } from "expo-image"; import { useNavigation } from "expo-router"; -import * as ScreenOrientation from "expo-screen-orientation"; import { useAtom } from "jotai"; import React, { useEffect, useMemo, useState } from "react"; import { View } from "react-native"; @@ -44,7 +42,6 @@ export const ItemContent: React.FC<{ item: BaseItemDto }> = React.memo( ({ item }) => { const [api] = useAtom(apiAtom); const [settings] = useSettings(); - const { orientation } = useOrientation(); const navigation = useNavigation(); const insets = useSafeAreaInsets(); useImageColors({ item }); @@ -94,11 +91,9 @@ export const ItemContent: React.FC<{ item: BaseItemDto }> = React.memo( }, [item]); useEffect(() => { - if (orientation !== ScreenOrientation.OrientationLock.PORTRAIT_UP) - setHeaderHeight(230); - else if (item.Type === "Movie") setHeaderHeight(500); + if (item.Type === "Movie") setHeaderHeight(500); else setHeaderHeight(350); - }, [item.Type, orientation]); + }, [item.Type]); const logoUrl = useMemo(() => getLogoImageUrlById({ api, item }), [item]); diff --git a/components/PlayButton.tsx b/components/PlayButton.tsx index d0c2c227..3804b031 100644 --- a/components/PlayButton.tsx +++ b/components/PlayButton.tsx @@ -5,7 +5,6 @@ import { runtimeTicksToMinutes } from "@/utils/time"; import { useActionSheet } from "@expo/react-native-action-sheet"; import { Ionicons, MaterialCommunityIcons } from "@expo/vector-icons"; import { BaseItemDto } from "@jellyfin/sdk/lib/generated-client"; -import * as Haptics from "expo-haptics"; import { useRouter } from "expo-router"; import { useAtom, useAtomValue } from "jotai"; import { useCallback, useEffect } from "react"; @@ -66,8 +65,6 @@ export const PlayButton: React.FC = ({ const onPress = useCallback(async () => { if (!item) return; - Haptics.impactAsync(Haptics.ImpactFeedbackStyle.Light); - const queryParams = new URLSearchParams({ itemId: item.Id!, audioIndex: selectedOptions.audioIndex?.toString() ?? "", diff --git a/components/RoundButton.tsx b/components/RoundButton.tsx index 7bcecdcb..d78eaf48 100644 --- a/components/RoundButton.tsx +++ b/components/RoundButton.tsx @@ -6,7 +6,6 @@ import { TouchableOpacity, TouchableOpacityProps, } from "react-native"; -import * as Haptics from "expo-haptics"; interface Props extends TouchableOpacityProps { onPress?: () => void; @@ -32,7 +31,6 @@ export const RoundButton: React.FC> = ({ const handlePress = () => { if (hapticFeedback) { - Haptics.impactAsync(Haptics.ImpactFeedbackStyle.Light); } onPress?.(); }; diff --git a/components/common/JellyseerrItemRouter.tsx b/components/common/JellyseerrItemRouter.tsx index 90f9c336..cadc9cf7 100644 --- a/components/common/JellyseerrItemRouter.tsx +++ b/components/common/JellyseerrItemRouter.tsx @@ -1,11 +1,13 @@ -import {useRouter, useSegments} from "expo-router"; -import React, {PropsWithChildren, useCallback, useMemo} from "react"; -import {TouchableOpacity, TouchableOpacityProps} from "react-native"; -import * as ContextMenu from "zeego/context-menu"; -import {MovieResult, TvResult} from "@/utils/jellyseerr/server/models/Search"; -import {useJellyseerr} from "@/hooks/useJellyseerr"; -import {hasPermission, Permission} from "@/utils/jellyseerr/server/lib/permissions"; -import {MediaType} from "@/utils/jellyseerr/server/constants/media"; +import { useRouter, useSegments } from "expo-router"; +import React, { PropsWithChildren, useCallback, useMemo } from "react"; +import { TouchableOpacity, TouchableOpacityProps } from "react-native"; +import { MovieResult, TvResult } from "@/utils/jellyseerr/server/models/Search"; +import { useJellyseerr } from "@/hooks/useJellyseerr"; +import { + hasPermission, + Permission, +} from "@/utils/jellyseerr/server/lib/permissions"; +import { MediaType } from "@/utils/jellyseerr/server/constants/media"; interface Props extends TouchableOpacityProps { result: MovieResult | TvResult; @@ -26,78 +28,49 @@ export const TouchableJellyseerrRouter: React.FC> = ({ }) => { const router = useRouter(); const segments = useSegments(); - const {jellyseerrApi, jellyseerrUser, requestMedia} = useJellyseerr() + const { jellyseerrApi, jellyseerrUser, requestMedia } = useJellyseerr(); const from = segments[2]; const autoApprove = useMemo(() => { - return jellyseerrUser && hasPermission( - Permission.AUTO_APPROVE, - jellyseerrUser.permissions, - {type: 'or'} - ) - }, [jellyseerrApi, jellyseerrUser]) + return ( + jellyseerrUser && + hasPermission(Permission.AUTO_APPROVE, jellyseerrUser.permissions, { + type: "or", + }) + ); + }, [jellyseerrApi, jellyseerrUser]); - const request = useCallback(() => + const request = useCallback( + () => requestMedia(mediaTitle, { mediaId: result.id, - mediaType: result.mediaType - } - ), + mediaType: result.mediaType, + }), [jellyseerrApi, result] - ) + ); if (from === "(home)" || from === "(search)" || from === "(libraries)") return ( <> - - - { - // @ts-ignore - router.push({pathname: `/(auth)/(tabs)/${from}/jellyseerr/page`, params: {...result, mediaTitle, releaseYear, canRequest, posterSrc}}); - }} - {...props} - > - {children} - - - - Actions - {canRequest && result.mediaType === MediaType.MOVIE && ( - { - if (autoApprove) { - request() - } - }} - shouldDismissMenuOnSelect - > - Request - - - )} - - + { + // @ts-ignore + router.push({ + pathname: `/(auth)/(tabs)/${from}/jellyseerr/page`, + params: { + ...result, + mediaTitle, + releaseYear, + canRequest, + posterSrc, + }, + }); + }} + {...props} + > + {children} + ); }; diff --git a/components/common/TouchableItemRouter.tsx b/components/common/TouchableItemRouter.tsx index d50c88bf..a58ce6b3 100644 --- a/components/common/TouchableItemRouter.tsx +++ b/components/common/TouchableItemRouter.tsx @@ -1,6 +1,5 @@ import { useMarkAsPlayed } from "@/hooks/useMarkAsPlayed"; import { BaseItemDto } from "@jellyfin/sdk/lib/generated-client/models"; -import * as Haptics from "expo-haptics"; import { useRouter, useSegments } from "expo-router"; import { PropsWithChildren } from "react"; import { TouchableOpacity, TouchableOpacityProps } from "react-native"; @@ -68,78 +67,15 @@ export const TouchableItemRouter: React.FC> = ({ if (from === "(home)" || from === "(search)" || from === "(libraries)") return ( - - - { - const url = itemRouter(item, from); - // @ts-ignore - router.push(url); - }} - {...props} - > - {children} - - - - Actions - { - markAsPlayedStatus(true); - }} - shouldDismissMenuOnSelect - > - - Mark as watched - - - - { - markAsPlayedStatus(false); - }} - shouldDismissMenuOnSelect - destructive - > - - Mark as not watched - - - - - + { + const url = itemRouter(item, from); + // @ts-ignore + router.push(url); + }} + {...props} + > + {children} + ); }; diff --git a/components/home/LargeMovieCarousel.tsx b/components/home/LargeMovieCarousel.tsx index 11676b88..37a0cda2 100644 --- a/components/home/LargeMovieCarousel.tsx +++ b/components/home/LargeMovieCarousel.tsx @@ -22,7 +22,6 @@ import { itemRouter, TouchableItemRouter } from "../common/TouchableItemRouter"; import { Loader } from "../Loader"; import { Gesture, GestureDetector } from "react-native-gesture-handler"; import { useRouter, useSegments } from "expo-router"; -import * as Haptics from "expo-haptics"; interface Props extends ViewProps {} @@ -147,7 +146,6 @@ const RenderItem: React.FC<{ item: BaseItemDto }> = ({ item }) => { const handleRoute = useCallback(() => { if (!from) return; const url = itemRouter(item, from); - Haptics.impactAsync(Haptics.ImpactFeedbackStyle.Light); // @ts-ignore if (url) router.push(url); }, [item, from]); diff --git a/components/settings/SettingToggles.tsx b/components/settings/SettingToggles.tsx index 4368ff15..d828f6d9 100644 --- a/components/settings/SettingToggles.tsx +++ b/components/settings/SettingToggles.tsx @@ -1,8 +1,7 @@ import { apiAtom, userAtom } from "@/providers/JellyfinProvider"; -import { ScreenOrientationEnum, useSettings } from "@/utils/atoms/settings"; +import { useSettings } from "@/utils/atoms/settings"; import { getItemsApi } from "@jellyfin/sdk/lib/utils/api"; import { useQuery, useQueryClient } from "@tanstack/react-query"; -import * as ScreenOrientation from "expo-screen-orientation"; import { useAtom } from "jotai"; import React, { useState } from "react"; import { @@ -98,113 +97,6 @@ export const SettingToggles: React.FC = ({ ...props }) => { /> - - - Video orientation - - Set the full screen video player orientation. - - - - - - - {ScreenOrientationEnum[settings.defaultVideoOrientation]} - - - - - Orientation - { - updateSettings({ - defaultVideoOrientation: - ScreenOrientation.OrientationLock.DEFAULT, - }); - }} - > - - { - ScreenOrientationEnum[ - ScreenOrientation.OrientationLock.DEFAULT - ] - } - - - { - updateSettings({ - defaultVideoOrientation: - ScreenOrientation.OrientationLock.PORTRAIT_UP, - }); - }} - > - - { - ScreenOrientationEnum[ - ScreenOrientation.OrientationLock.PORTRAIT_UP - ] - } - - - { - updateSettings({ - defaultVideoOrientation: - ScreenOrientation.OrientationLock.LANDSCAPE_LEFT, - }); - }} - > - - { - ScreenOrientationEnum[ - ScreenOrientation.OrientationLock.LANDSCAPE_LEFT - ] - } - - - { - updateSettings({ - defaultVideoOrientation: - ScreenOrientation.OrientationLock.LANDSCAPE_RIGHT, - }); - }} - > - - { - ScreenOrientationEnum[ - ScreenOrientation.OrientationLock.LANDSCAPE_RIGHT - ] - } - - - - - - Safe area in controls @@ -377,31 +269,6 @@ export const SettingToggles: React.FC = ({ ...props }) => { )} - - - - Show Custom Menu Links - - Show custom menu links defined inside your Jellyfin web - config.json file - - - Linking.openURL( - "https://jellyfin.org/docs/general/clients/web-config/#custom-menu-links" - ) - } - > - More info - - - - updateSettings({ showCustomMenuLinks: value }) - } - /> - diff --git a/components/video-player/controls/Controls.tsx b/components/video-player/controls/Controls.tsx index 4110a7e5..5b17b184 100644 --- a/components/video-player/controls/Controls.tsx +++ b/components/video-player/controls/Controls.tsx @@ -29,7 +29,6 @@ import { BaseItemDto, MediaSourceInfo, } from "@jellyfin/sdk/lib/generated-client"; -import * as Haptics from "expo-haptics"; import { Image } from "expo-image"; import { useLocalSearchParams, useRouter } from "expo-router"; import { useAtom } from "jotai"; @@ -159,8 +158,6 @@ export const Controls: React.FC = ({ const goToPreviousItem = useCallback(() => { if (!previousItem || !settings) return; - Haptics.impactAsync(Haptics.ImpactFeedbackStyle.Light); - const previousIndexes: previousIndexes = { subtitleIndex: subtitleIndex ? parseInt(subtitleIndex) : undefined, audioIndex: audioIndex ? parseInt(audioIndex) : undefined, @@ -197,8 +194,6 @@ export const Controls: React.FC = ({ const goToNextItem = useCallback(() => { if (!nextItem || !settings) return; - Haptics.impactAsync(Haptics.ImpactFeedbackStyle.Light); - const previousIndexes: previousIndexes = { subtitleIndex: subtitleIndex ? parseInt(subtitleIndex) : undefined, audioIndex: audioIndex ? parseInt(audioIndex) : undefined, @@ -325,7 +320,7 @@ export const Controls: React.FC = ({ const handleSkipBackward = useCallback(async () => { if (!settings?.rewindSkipTime) return; wasPlayingRef.current = isPlaying; - Haptics.impactAsync(Haptics.ImpactFeedbackStyle.Light); + try { const curr = progress.value; if (curr !== undefined) { @@ -343,7 +338,7 @@ export const Controls: React.FC = ({ const handleSkipForward = useCallback(async () => { if (!settings?.forwardSkipTime) return; wasPlayingRef.current = isPlaying; - Haptics.impactAsync(Haptics.ImpactFeedbackStyle.Light); + try { const curr = progress.value; if (curr !== undefined) { @@ -360,7 +355,6 @@ export const Controls: React.FC = ({ const toggleIgnoreSafeAreas = useCallback(() => { setIgnoreSafeAreas((prev) => !prev); - Haptics.impactAsync(Haptics.ImpactFeedbackStyle.Light); }, []); const memoizedRenderBubble = useCallback(() => { @@ -439,8 +433,6 @@ export const Controls: React.FC = ({ const gotoItem = await getItemById(api, itemId); if (!settings || !gotoItem) return; - Haptics.impactAsync(Haptics.ImpactFeedbackStyle.Light); - const previousIndexes: previousIndexes = { subtitleIndex: subtitleIndex ? parseInt(subtitleIndex) : undefined, audioIndex: audioIndex ? parseInt(audioIndex) : undefined, @@ -588,7 +580,6 @@ export const Controls: React.FC = ({ )} { - Haptics.impactAsync(Haptics.ImpactFeedbackStyle.Light); router.back(); }} className="aspect-square flex flex-col bg-neutral-800/90 rounded-xl items-center justify-center p-2" diff --git a/hooks/useCreditSkipper.ts b/hooks/useCreditSkipper.ts index 1430e7c9..5f7a8b34 100644 --- a/hooks/useCreditSkipper.ts +++ b/hooks/useCreditSkipper.ts @@ -5,7 +5,6 @@ import { apiAtom } from "@/providers/JellyfinProvider"; import { getAuthHeaders } from "@/utils/jellyfin/jellyfin"; import { writeToLog } from "@/utils/log"; import { msToSeconds, secondsToMs } from "@/utils/time"; -import * as Haptics from "expo-haptics"; interface CreditTimestamps { Introduction: { @@ -79,7 +78,6 @@ export const useCreditSkipper = ( if (!creditTimestamps) return; console.log(`Skipping credits to ${creditTimestamps.Credits.End}`); try { - Haptics.impactAsync(Haptics.ImpactFeedbackStyle.Light); wrappedSeek(creditTimestamps.Credits.End); setTimeout(() => { play(); diff --git a/hooks/useIntroSkipper.ts b/hooks/useIntroSkipper.ts index 15aaff05..214f4a5a 100644 --- a/hooks/useIntroSkipper.ts +++ b/hooks/useIntroSkipper.ts @@ -5,7 +5,6 @@ import { apiAtom } from "@/providers/JellyfinProvider"; import { getAuthHeaders } from "@/utils/jellyfin/jellyfin"; import { writeToLog } from "@/utils/log"; import { msToSeconds, secondsToMs } from "@/utils/time"; -import * as Haptics from "expo-haptics"; interface IntroTimestamps { EpisodeId: string; @@ -78,7 +77,6 @@ export const useIntroSkipper = ( const skipIntro = useCallback(() => { if (!introTimestamps) return; try { - Haptics.impactAsync(Haptics.ImpactFeedbackStyle.Light); wrappedSeek(introTimestamps.IntroEnd); setTimeout(() => { play(); diff --git a/hooks/useMarkAsPlayed.ts b/hooks/useMarkAsPlayed.ts index ff039cc8..e1b28fe7 100644 --- a/hooks/useMarkAsPlayed.ts +++ b/hooks/useMarkAsPlayed.ts @@ -3,7 +3,6 @@ import { markAsNotPlayed } from "@/utils/jellyfin/playstate/markAsNotPlayed"; import { markAsPlayed } from "@/utils/jellyfin/playstate/markAsPlayed"; import { BaseItemDto } from "@jellyfin/sdk/lib/generated-client/models"; import { useQueryClient } from "@tanstack/react-query"; -import * as Haptics from "expo-haptics"; import { useAtom } from "jotai"; export const useMarkAsPlayed = (item: BaseItemDto) => { @@ -29,8 +28,6 @@ export const useMarkAsPlayed = (item: BaseItemDto) => { }; const markAsPlayedStatus = async (played: boolean) => { - Haptics.impactAsync(Haptics.ImpactFeedbackStyle.Light); - // Optimistic update queryClient.setQueryData( ["item", item.Id], diff --git a/hooks/useOrientation.ts b/hooks/useOrientation.ts deleted file mode 100644 index 1ecb31ac..00000000 --- a/hooks/useOrientation.ts +++ /dev/null @@ -1,28 +0,0 @@ -import orientationToOrientationLock from "@/utils/OrientationLockConverter"; -import * as ScreenOrientation from "expo-screen-orientation"; -import { useEffect, useState } from "react"; - -export const useOrientation = () => { - const [orientation, setOrientation] = useState( - ScreenOrientation.OrientationLock.UNKNOWN - ); - - useEffect(() => { - const orientationSubscription = - ScreenOrientation.addOrientationChangeListener((event) => { - setOrientation( - orientationToOrientationLock(event.orientationInfo.orientation) - ); - }); - - ScreenOrientation.getOrientationAsync().then((orientation) => { - setOrientation(orientationToOrientationLock(orientation)); - }); - - return () => { - orientationSubscription.remove(); - }; - }, []); - - return { orientation, setOrientation }; -}; diff --git a/hooks/useOrientationSettings.ts b/hooks/useOrientationSettings.ts deleted file mode 100644 index 85b8a113..00000000 --- a/hooks/useOrientationSettings.ts +++ /dev/null @@ -1,25 +0,0 @@ -import { useSettings } from "@/utils/atoms/settings"; -import * as ScreenOrientation from "expo-screen-orientation"; -import { useEffect } from "react"; - -export const useOrientationSettings = () => { - const [settings] = useSettings(); - - useEffect(() => { - if (settings?.autoRotate) { - // Don't need to do anything - } else if (settings?.defaultVideoOrientation) { - ScreenOrientation.lockAsync(settings.defaultVideoOrientation); - } - - return () => { - if (settings?.autoRotate) { - ScreenOrientation.unlockAsync(); - } else { - ScreenOrientation.lockAsync( - ScreenOrientation.OrientationLock.PORTRAIT_UP - ); - } - }; - }, [settings]); -}; diff --git a/package.json b/package.json index 952a50f8..9fee217b 100644 --- a/package.json +++ b/package.json @@ -45,20 +45,17 @@ "expo-dev-client": "~4.0.29", "expo-device": "~6.0.2", "expo-font": "~12.0.10", - "expo-haptics": "~13.0.1", "expo-image": "~1.13.0", "expo-keep-awake": "~13.0.2", "expo-linear-gradient": "~13.0.2", "expo-linking": "~6.3.1", "expo-network": "~6.0.1", "expo-router": "~3.5.24", - "expo-screen-orientation": "~7.0.5", "expo-sensors": "~13.0.9", "expo-splash-screen": "~0.27.7", "expo-status-bar": "~1.12.1", "expo-system-ui": "^3.0.7", "expo-updates": "~0.25.27", - "expo-web-browser": "~13.0.3", "ffmpeg-kit-react-native": "^6.0.2", "install": "^0.13.0", "jotai": "^2.10.1", @@ -66,7 +63,7 @@ "nativewind": "^2.0.11", "react": "18.2.0", "react-dom": "18.2.0", - "react-native": "npm:react-native-tvos@latest", + "react-native": "npm:react-native-tvos@0.74.5-0", "react-native-awesome-slider": "^2.5.6", "react-native-bottom-tabs": "0.7.1", "react-native-circular-progress": "^1.4.1", @@ -76,7 +73,6 @@ "react-native-gesture-handler": "~2.16.1", "react-native-get-random-values": "^1.11.0", "react-native-image-colors": "^2.4.0", - "react-native-ios-context-menu": "^2.5.2", "react-native-ios-utilities": "^4.5.1", "react-native-mmkv": "^2.12.2", "react-native-pager-view": "6.3.0", diff --git a/utils/OrientationLockConverter.ts b/utils/OrientationLockConverter.ts deleted file mode 100644 index 748ffcc6..00000000 --- a/utils/OrientationLockConverter.ts +++ /dev/null @@ -1,21 +0,0 @@ -import { Orientation, OrientationLock } from "expo-screen-orientation"; - -function orientationToOrientationLock( - orientation: Orientation -): OrientationLock { - switch (orientation) { - case Orientation.PORTRAIT_UP: - return OrientationLock.PORTRAIT_UP; - case Orientation.PORTRAIT_DOWN: - return OrientationLock.PORTRAIT_DOWN; - case Orientation.LANDSCAPE_LEFT: - return OrientationLock.LANDSCAPE_LEFT; - case Orientation.LANDSCAPE_RIGHT: - return OrientationLock.LANDSCAPE_RIGHT; - case Orientation.UNKNOWN: - default: - return OrientationLock.DEFAULT; - } -} - -export default orientationToOrientationLock; diff --git a/utils/atoms/orientation.ts b/utils/atoms/orientation.ts deleted file mode 100644 index e4680fe3..00000000 --- a/utils/atoms/orientation.ts +++ /dev/null @@ -1,6 +0,0 @@ -import * as ScreenOrientation from "expo-screen-orientation"; -import { atom } from "jotai"; - -export const orientationAtom = atom( - ScreenOrientation.OrientationLock.PORTRAIT_UP -); diff --git a/utils/atoms/settings.ts b/utils/atoms/settings.ts index f38cee36..a7e77921 100644 --- a/utils/atoms/settings.ts +++ b/utils/atoms/settings.ts @@ -1,6 +1,5 @@ import { atom, useAtom } from "jotai"; import { useEffect } from "react"; -import * as ScreenOrientation from "expo-screen-orientation"; import { storage } from "../mmkv"; import { Platform } from "react-native"; import { @@ -8,22 +7,6 @@ import { SubtitlePlaybackMode, } from "@jellyfin/sdk/lib/generated-client"; -export const ScreenOrientationEnum: Record< - ScreenOrientation.OrientationLock, - string -> = { - [ScreenOrientation.OrientationLock.DEFAULT]: "Default", - [ScreenOrientation.OrientationLock.ALL]: "All", - [ScreenOrientation.OrientationLock.PORTRAIT]: "Portrait", - [ScreenOrientation.OrientationLock.PORTRAIT_UP]: "Portrait Up", - [ScreenOrientation.OrientationLock.PORTRAIT_DOWN]: "Portrait Down", - [ScreenOrientation.OrientationLock.LANDSCAPE]: "Landscape", - [ScreenOrientation.OrientationLock.LANDSCAPE_LEFT]: "Landscape Left", - [ScreenOrientation.OrientationLock.LANDSCAPE_RIGHT]: "Landscape Right", - [ScreenOrientation.OrientationLock.OTHER]: "Other", - [ScreenOrientation.OrientationLock.UNKNOWN]: "Unknown", -}; - export type LibraryOptions = { display: "row" | "list"; cardStyle: "compact" | "detailed"; @@ -54,7 +37,6 @@ export type Settings = { subtitleMode: SubtitlePlaybackMode; rememberSubtitleSelections: boolean; showHomeTitles: boolean; - defaultVideoOrientation: ScreenOrientation.OrientationLock; forwardSkipTime: number; rewindSkipTime: number; optimizedVersionsServerUrl?: string | null; @@ -89,7 +71,6 @@ const loadSettings = (): Settings => { subtitleMode: SubtitlePlaybackMode.Default, rememberSubtitleSelections: true, showHomeTitles: true, - defaultVideoOrientation: ScreenOrientation.OrientationLock.DEFAULT, forwardSkipTime: 30, rewindSkipTime: 10, optimizedVersionsServerUrl: null,