forked from Ninjalama/streamyfin_mirror
Compare commits
1 Commits
renovate/r
...
revert-377
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
0e73299429 |
@@ -13,7 +13,7 @@ import { SubtitleToggles } from "@/components/settings/SubtitleToggles";
|
|||||||
import { UserInfo } from "@/components/settings/UserInfo";
|
import { UserInfo } from "@/components/settings/UserInfo";
|
||||||
import { useJellyfin } from "@/providers/JellyfinProvider";
|
import { useJellyfin } from "@/providers/JellyfinProvider";
|
||||||
import { clearLogs } from "@/utils/log";
|
import { clearLogs } from "@/utils/log";
|
||||||
import { useHaptic } from "@/hooks/useHaptic";
|
import * as Haptics from "expo-haptics";
|
||||||
import { useNavigation, useRouter } from "expo-router";
|
import { useNavigation, useRouter } from "expo-router";
|
||||||
import { useEffect } from "react";
|
import { useEffect } from "react";
|
||||||
import { ScrollView, TouchableOpacity, View } from "react-native";
|
import { ScrollView, TouchableOpacity, View } from "react-native";
|
||||||
@@ -23,11 +23,10 @@ export default function settings() {
|
|||||||
const router = useRouter();
|
const router = useRouter();
|
||||||
const insets = useSafeAreaInsets();
|
const insets = useSafeAreaInsets();
|
||||||
const { logout } = useJellyfin();
|
const { logout } = useJellyfin();
|
||||||
const successHapticFeedback = useHaptic("success");
|
|
||||||
|
|
||||||
const onClearLogsClicked = async () => {
|
const onClearLogsClicked = async () => {
|
||||||
clearLogs();
|
clearLogs();
|
||||||
successHapticFeedback();
|
Haptics.notificationAsync(Haptics.NotificationFeedbackType.Success);
|
||||||
};
|
};
|
||||||
|
|
||||||
const navigation = useNavigation();
|
const navigation = useNavigation();
|
||||||
|
|||||||
@@ -27,7 +27,7 @@ import {
|
|||||||
getUserLibraryApi,
|
getUserLibraryApi,
|
||||||
} from "@jellyfin/sdk/lib/utils/api";
|
} from "@jellyfin/sdk/lib/utils/api";
|
||||||
import { useQuery } from "@tanstack/react-query";
|
import { useQuery } from "@tanstack/react-query";
|
||||||
import { useHaptic } from "@/hooks/useHaptic";
|
import * as Haptics from "expo-haptics";
|
||||||
import { useFocusEffect, useGlobalSearchParams } from "expo-router";
|
import { useFocusEffect, useGlobalSearchParams } from "expo-router";
|
||||||
import { useAtomValue } from "jotai";
|
import { useAtomValue } from "jotai";
|
||||||
import React, {
|
import React, {
|
||||||
@@ -68,11 +68,9 @@ export default function page() {
|
|||||||
const { getDownloadedItem } = useDownload();
|
const { getDownloadedItem } = useDownload();
|
||||||
const revalidateProgressCache = useInvalidatePlaybackProgressCache();
|
const revalidateProgressCache = useInvalidatePlaybackProgressCache();
|
||||||
|
|
||||||
const lightHapticFeedback = useHaptic("light");
|
|
||||||
|
|
||||||
const setShowControls = useCallback((show: boolean) => {
|
const setShowControls = useCallback((show: boolean) => {
|
||||||
_setShowControls(show);
|
_setShowControls(show);
|
||||||
lightHapticFeedback();
|
Haptics.impactAsync(Haptics.ImpactFeedbackStyle.Light);
|
||||||
}, []);
|
}, []);
|
||||||
|
|
||||||
const {
|
const {
|
||||||
@@ -177,7 +175,7 @@ export default function page() {
|
|||||||
const togglePlay = useCallback(async () => {
|
const togglePlay = useCallback(async () => {
|
||||||
if (!api) return;
|
if (!api) return;
|
||||||
|
|
||||||
lightHapticFeedback();
|
Haptics.impactAsync(Haptics.ImpactFeedbackStyle.Light);
|
||||||
if (isPlaying) {
|
if (isPlaying) {
|
||||||
await videoRef.current?.pause();
|
await videoRef.current?.pause();
|
||||||
|
|
||||||
|
|||||||
@@ -17,7 +17,7 @@ import {
|
|||||||
getUserLibraryApi,
|
getUserLibraryApi,
|
||||||
} from "@jellyfin/sdk/lib/utils/api";
|
} from "@jellyfin/sdk/lib/utils/api";
|
||||||
import { useQuery } from "@tanstack/react-query";
|
import { useQuery } from "@tanstack/react-query";
|
||||||
import { useHaptic } from "@/hooks/useHaptic";
|
import * as Haptics from "expo-haptics";
|
||||||
import { Image } from "expo-image";
|
import { Image } from "expo-image";
|
||||||
import { useFocusEffect, useLocalSearchParams } from "expo-router";
|
import { useFocusEffect, useLocalSearchParams } from "expo-router";
|
||||||
import { useAtomValue } from "jotai";
|
import { useAtomValue } from "jotai";
|
||||||
@@ -45,8 +45,6 @@ export default function page() {
|
|||||||
const isSeeking = useSharedValue(false);
|
const isSeeking = useSharedValue(false);
|
||||||
const cacheProgress = useSharedValue(0);
|
const cacheProgress = useSharedValue(0);
|
||||||
|
|
||||||
const lightHapticFeedback = useHaptic("light");
|
|
||||||
|
|
||||||
const {
|
const {
|
||||||
itemId,
|
itemId,
|
||||||
audioIndex: audioIndexStr,
|
audioIndex: audioIndexStr,
|
||||||
@@ -126,7 +124,7 @@ export default function page() {
|
|||||||
|
|
||||||
const togglePlay = useCallback(
|
const togglePlay = useCallback(
|
||||||
async (ticks: number) => {
|
async (ticks: number) => {
|
||||||
lightHapticFeedback();
|
Haptics.impactAsync(Haptics.ImpactFeedbackStyle.Light);
|
||||||
if (isPlaying) {
|
if (isPlaying) {
|
||||||
videoRef.current?.pause();
|
videoRef.current?.pause();
|
||||||
await getPlaystateApi(api!).onPlaybackProgress({
|
await getPlaystateApi(api!).onPlaybackProgress({
|
||||||
|
|||||||
@@ -20,7 +20,7 @@ import {
|
|||||||
getUserLibraryApi,
|
getUserLibraryApi,
|
||||||
} from "@jellyfin/sdk/lib/utils/api";
|
} from "@jellyfin/sdk/lib/utils/api";
|
||||||
import { useQuery } from "@tanstack/react-query";
|
import { useQuery } from "@tanstack/react-query";
|
||||||
import { useHaptic } from "@/hooks/useHaptic";
|
import * as Haptics from "expo-haptics";
|
||||||
import { useFocusEffect, useLocalSearchParams } from "expo-router";
|
import { useFocusEffect, useLocalSearchParams } from "expo-router";
|
||||||
import { useAtomValue } from "jotai";
|
import { useAtomValue } from "jotai";
|
||||||
import React, {
|
import React, {
|
||||||
@@ -48,7 +48,6 @@ const Player = () => {
|
|||||||
|
|
||||||
const firstTime = useRef(true);
|
const firstTime = useRef(true);
|
||||||
const revalidateProgressCache = useInvalidatePlaybackProgressCache();
|
const revalidateProgressCache = useInvalidatePlaybackProgressCache();
|
||||||
const lightHapticFeedback = useHaptic("light");
|
|
||||||
|
|
||||||
const [isPlaybackStopped, setIsPlaybackStopped] = useState(false);
|
const [isPlaybackStopped, setIsPlaybackStopped] = useState(false);
|
||||||
const [showControls, _setShowControls] = useState(true);
|
const [showControls, _setShowControls] = useState(true);
|
||||||
@@ -59,7 +58,7 @@ const Player = () => {
|
|||||||
|
|
||||||
const setShowControls = useCallback((show: boolean) => {
|
const setShowControls = useCallback((show: boolean) => {
|
||||||
_setShowControls(show);
|
_setShowControls(show);
|
||||||
lightHapticFeedback();
|
Haptics.impactAsync(Haptics.ImpactFeedbackStyle.Light);
|
||||||
}, []);
|
}, []);
|
||||||
|
|
||||||
const progress = useSharedValue(0);
|
const progress = useSharedValue(0);
|
||||||
@@ -168,7 +167,7 @@ const Player = () => {
|
|||||||
const videoSource = useVideoSource(item, api, poster, stream?.url);
|
const videoSource = useVideoSource(item, api, poster, stream?.url);
|
||||||
|
|
||||||
const togglePlay = useCallback(async () => {
|
const togglePlay = useCallback(async () => {
|
||||||
lightHapticFeedback();
|
Haptics.impactAsync(Haptics.ImpactFeedbackStyle.Light);
|
||||||
if (isPlaying) {
|
if (isPlaying) {
|
||||||
videoRef.current?.pause();
|
videoRef.current?.pause();
|
||||||
await getPlaystateApi(api!).onPlaybackProgress({
|
await getPlaystateApi(api!).onPlaybackProgress({
|
||||||
|
|||||||
@@ -1,4 +1,4 @@
|
|||||||
import { useHaptic } from "@/hooks/useHaptic";
|
import * as Haptics from "expo-haptics";
|
||||||
import React, { PropsWithChildren, ReactNode, useMemo } from "react";
|
import React, { PropsWithChildren, ReactNode, useMemo } from "react";
|
||||||
import { Text, TouchableOpacity, View } from "react-native";
|
import { Text, TouchableOpacity, View } from "react-native";
|
||||||
import { Loader } from "./Loader";
|
import { Loader } from "./Loader";
|
||||||
@@ -43,8 +43,6 @@ export const Button: React.FC<PropsWithChildren<ButtonProps>> = ({
|
|||||||
}
|
}
|
||||||
}, [color]);
|
}, [color]);
|
||||||
|
|
||||||
const lightHapticFeedback = useHaptic("light");
|
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<TouchableOpacity
|
<TouchableOpacity
|
||||||
className={`
|
className={`
|
||||||
@@ -56,7 +54,7 @@ export const Button: React.FC<PropsWithChildren<ButtonProps>> = ({
|
|||||||
onPress={() => {
|
onPress={() => {
|
||||||
if (!loading && !disabled && onPress) {
|
if (!loading && !disabled && onPress) {
|
||||||
onPress();
|
onPress();
|
||||||
lightHapticFeedback();
|
Haptics.impactAsync(Haptics.ImpactFeedbackStyle.Light);
|
||||||
}
|
}
|
||||||
}}
|
}}
|
||||||
disabled={disabled || loading}
|
disabled={disabled || loading}
|
||||||
|
|||||||
@@ -32,7 +32,7 @@ import Animated, {
|
|||||||
import { Button } from "./Button";
|
import { Button } from "./Button";
|
||||||
import { SelectedOptions } from "./ItemContent";
|
import { SelectedOptions } from "./ItemContent";
|
||||||
import { chromecastProfile } from "@/utils/profiles/chromecast";
|
import { chromecastProfile } from "@/utils/profiles/chromecast";
|
||||||
import { useHaptic } from "@/hooks/useHaptic";
|
import * as Haptics from "expo-haptics";
|
||||||
|
|
||||||
interface Props extends React.ComponentProps<typeof Button> {
|
interface Props extends React.ComponentProps<typeof Button> {
|
||||||
item: BaseItemDto;
|
item: BaseItemDto;
|
||||||
@@ -64,7 +64,6 @@ export const PlayButton: React.FC<Props> = ({
|
|||||||
const widthProgress = useSharedValue(0);
|
const widthProgress = useSharedValue(0);
|
||||||
const colorChangeProgress = useSharedValue(0);
|
const colorChangeProgress = useSharedValue(0);
|
||||||
const [settings] = useSettings();
|
const [settings] = useSettings();
|
||||||
const lightHapticFeedback = useHaptic("light");
|
|
||||||
|
|
||||||
const goToPlayer = useCallback(
|
const goToPlayer = useCallback(
|
||||||
(q: string, bitrateValue: number | undefined) => {
|
(q: string, bitrateValue: number | undefined) => {
|
||||||
@@ -80,7 +79,7 @@ export const PlayButton: React.FC<Props> = ({
|
|||||||
const onPress = useCallback(async () => {
|
const onPress = useCallback(async () => {
|
||||||
if (!item) return;
|
if (!item) return;
|
||||||
|
|
||||||
lightHapticFeedback();
|
Haptics.impactAsync(Haptics.ImpactFeedbackStyle.Light);
|
||||||
|
|
||||||
const queryParams = new URLSearchParams({
|
const queryParams = new URLSearchParams({
|
||||||
itemId: item.Id!,
|
itemId: item.Id!,
|
||||||
|
|||||||
@@ -6,7 +6,7 @@ import {
|
|||||||
TouchableOpacity,
|
TouchableOpacity,
|
||||||
TouchableOpacityProps,
|
TouchableOpacityProps,
|
||||||
} from "react-native";
|
} from "react-native";
|
||||||
import { useHaptic } from "@/hooks/useHaptic";
|
import * as Haptics from "expo-haptics";
|
||||||
|
|
||||||
interface Props extends TouchableOpacityProps {
|
interface Props extends TouchableOpacityProps {
|
||||||
onPress?: () => void;
|
onPress?: () => void;
|
||||||
@@ -29,11 +29,10 @@ export const RoundButton: React.FC<PropsWithChildren<Props>> = ({
|
|||||||
}) => {
|
}) => {
|
||||||
const buttonSize = size === "large" ? "h-10 w-10" : "h-9 w-9";
|
const buttonSize = size === "large" ? "h-10 w-10" : "h-9 w-9";
|
||||||
const fillColorClass = fillColor === "primary" ? "bg-purple-600" : "";
|
const fillColorClass = fillColor === "primary" ? "bg-purple-600" : "";
|
||||||
const lightHapticFeedback = useHaptic("light");
|
|
||||||
|
|
||||||
const handlePress = () => {
|
const handlePress = () => {
|
||||||
if (hapticFeedback) {
|
if (hapticFeedback) {
|
||||||
lightHapticFeedback();
|
Haptics.impactAsync(Haptics.ImpactFeedbackStyle.Light);
|
||||||
}
|
}
|
||||||
onPress?.();
|
onPress?.();
|
||||||
};
|
};
|
||||||
|
|||||||
@@ -1,5 +1,5 @@
|
|||||||
import { BaseItemDto } from "@jellyfin/sdk/lib/generated-client/models";
|
import { BaseItemDto } from "@jellyfin/sdk/lib/generated-client/models";
|
||||||
import { useHaptic } from "@/hooks/useHaptic";
|
import * as Haptics from "expo-haptics";
|
||||||
import React, { useCallback, useMemo } from "react";
|
import React, { useCallback, useMemo } from "react";
|
||||||
import { TouchableOpacity, TouchableOpacityProps, View } from "react-native";
|
import { TouchableOpacity, TouchableOpacityProps, View } from "react-native";
|
||||||
import {
|
import {
|
||||||
@@ -26,7 +26,6 @@ export const EpisodeCard: React.FC<EpisodeCardProps> = ({ item, ...props }) => {
|
|||||||
const { deleteFile } = useDownload();
|
const { deleteFile } = useDownload();
|
||||||
const { openFile } = useDownloadedFileOpener();
|
const { openFile } = useDownloadedFileOpener();
|
||||||
const { showActionSheetWithOptions } = useActionSheet();
|
const { showActionSheetWithOptions } = useActionSheet();
|
||||||
const successHapticFeedback = useHaptic("success");
|
|
||||||
|
|
||||||
const base64Image = useMemo(() => {
|
const base64Image = useMemo(() => {
|
||||||
return storage.getString(item.Id!);
|
return storage.getString(item.Id!);
|
||||||
@@ -42,7 +41,7 @@ export const EpisodeCard: React.FC<EpisodeCardProps> = ({ item, ...props }) => {
|
|||||||
const handleDeleteFile = useCallback(() => {
|
const handleDeleteFile = useCallback(() => {
|
||||||
if (item.Id) {
|
if (item.Id) {
|
||||||
deleteFile(item.Id);
|
deleteFile(item.Id);
|
||||||
successHapticFeedback();
|
Haptics.notificationAsync(Haptics.NotificationFeedbackType.Success);
|
||||||
}
|
}
|
||||||
}, [deleteFile, item.Id]);
|
}, [deleteFile, item.Id]);
|
||||||
|
|
||||||
|
|||||||
@@ -3,7 +3,7 @@ import {
|
|||||||
useActionSheet,
|
useActionSheet,
|
||||||
} from "@expo/react-native-action-sheet";
|
} from "@expo/react-native-action-sheet";
|
||||||
import { BaseItemDto } from "@jellyfin/sdk/lib/generated-client/models";
|
import { BaseItemDto } from "@jellyfin/sdk/lib/generated-client/models";
|
||||||
import { useHaptic } from "@/hooks/useHaptic";
|
import * as Haptics from "expo-haptics";
|
||||||
import React, { useCallback, useMemo } from "react";
|
import React, { useCallback, useMemo } from "react";
|
||||||
import { TouchableOpacity, View } from "react-native";
|
import { TouchableOpacity, View } from "react-native";
|
||||||
|
|
||||||
@@ -28,7 +28,6 @@ export const MovieCard: React.FC<MovieCardProps> = ({ item }) => {
|
|||||||
const { deleteFile } = useDownload();
|
const { deleteFile } = useDownload();
|
||||||
const { openFile } = useDownloadedFileOpener();
|
const { openFile } = useDownloadedFileOpener();
|
||||||
const { showActionSheetWithOptions } = useActionSheet();
|
const { showActionSheetWithOptions } = useActionSheet();
|
||||||
const successHapticFeedback = useHaptic("success");
|
|
||||||
|
|
||||||
const handleOpenFile = useCallback(() => {
|
const handleOpenFile = useCallback(() => {
|
||||||
openFile(item);
|
openFile(item);
|
||||||
@@ -44,7 +43,7 @@ export const MovieCard: React.FC<MovieCardProps> = ({ item }) => {
|
|||||||
const handleDeleteFile = useCallback(() => {
|
const handleDeleteFile = useCallback(() => {
|
||||||
if (item.Id) {
|
if (item.Id) {
|
||||||
deleteFile(item.Id);
|
deleteFile(item.Id);
|
||||||
successHapticFeedback();
|
Haptics.notificationAsync(Haptics.NotificationFeedbackType.Success);
|
||||||
}
|
}
|
||||||
}, [deleteFile, item.Id]);
|
}, [deleteFile, item.Id]);
|
||||||
|
|
||||||
|
|||||||
@@ -22,7 +22,7 @@ import { itemRouter, TouchableItemRouter } from "../common/TouchableItemRouter";
|
|||||||
import { Loader } from "../Loader";
|
import { Loader } from "../Loader";
|
||||||
import { Gesture, GestureDetector } from "react-native-gesture-handler";
|
import { Gesture, GestureDetector } from "react-native-gesture-handler";
|
||||||
import { useRouter, useSegments } from "expo-router";
|
import { useRouter, useSegments } from "expo-router";
|
||||||
import { useHaptic } from "@/hooks/useHaptic";
|
import * as Haptics from "expo-haptics";
|
||||||
|
|
||||||
interface Props extends ViewProps {}
|
interface Props extends ViewProps {}
|
||||||
|
|
||||||
@@ -128,7 +128,6 @@ const RenderItem: React.FC<{ item: BaseItemDto }> = ({ item }) => {
|
|||||||
const [api] = useAtom(apiAtom);
|
const [api] = useAtom(apiAtom);
|
||||||
const router = useRouter();
|
const router = useRouter();
|
||||||
const screenWidth = Dimensions.get("screen").width;
|
const screenWidth = Dimensions.get("screen").width;
|
||||||
const lightHapticFeedback = useHaptic("light");
|
|
||||||
|
|
||||||
const uri = useMemo(() => {
|
const uri = useMemo(() => {
|
||||||
if (!api) return null;
|
if (!api) return null;
|
||||||
@@ -154,7 +153,7 @@ const RenderItem: React.FC<{ item: BaseItemDto }> = ({ item }) => {
|
|||||||
const handleRoute = useCallback(() => {
|
const handleRoute = useCallback(() => {
|
||||||
if (!from) return;
|
if (!from) return;
|
||||||
const url = itemRouter(item, from);
|
const url = itemRouter(item, from);
|
||||||
lightHapticFeedback();
|
Haptics.impactAsync(Haptics.ImpactFeedbackStyle.Light);
|
||||||
// @ts-ignore
|
// @ts-ignore
|
||||||
if (url) router.push(url);
|
if (url) router.push(url);
|
||||||
}, [item, from]);
|
}, [item, from]);
|
||||||
|
|||||||
@@ -178,15 +178,6 @@ export const OtherSettings: React.FC = () => {
|
|||||||
}
|
}
|
||||||
/>
|
/>
|
||||||
</ListItem>
|
</ListItem>
|
||||||
|
|
||||||
<ListItem title="Disable Haptic Feedback">
|
|
||||||
<Switch
|
|
||||||
value={settings.disableHapticFeedback}
|
|
||||||
onValueChange={(value) =>
|
|
||||||
updateSettings({ disableHapticFeedback: value })
|
|
||||||
}
|
|
||||||
/>
|
|
||||||
</ListItem>
|
|
||||||
</ListGroup>
|
</ListGroup>
|
||||||
);
|
);
|
||||||
};
|
};
|
||||||
|
|||||||
@@ -7,7 +7,7 @@ import {
|
|||||||
BottomSheetView,
|
BottomSheetView,
|
||||||
} from "@gorhom/bottom-sheet";
|
} from "@gorhom/bottom-sheet";
|
||||||
import { getQuickConnectApi } from "@jellyfin/sdk/lib/utils/api";
|
import { getQuickConnectApi } from "@jellyfin/sdk/lib/utils/api";
|
||||||
import { useHaptic } from "@/hooks/useHaptic";
|
import * as Haptics from "expo-haptics";
|
||||||
import { useAtom } from "jotai";
|
import { useAtom } from "jotai";
|
||||||
import React, { useCallback, useRef, useState } from "react";
|
import React, { useCallback, useRef, useState } from "react";
|
||||||
import { Alert, View, ViewProps } from "react-native";
|
import { Alert, View, ViewProps } from "react-native";
|
||||||
@@ -23,8 +23,6 @@ export const QuickConnect: React.FC<Props> = ({ ...props }) => {
|
|||||||
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<BottomSheetModal>(null);
|
||||||
const successHapticFeedback = useHaptic("success");
|
|
||||||
const errorHapticFeedback = useHaptic("error");
|
|
||||||
|
|
||||||
const renderBackdrop = useCallback(
|
const renderBackdrop = useCallback(
|
||||||
(props: BottomSheetBackdropProps) => (
|
(props: BottomSheetBackdropProps) => (
|
||||||
@@ -45,16 +43,16 @@ export const QuickConnect: React.FC<Props> = ({ ...props }) => {
|
|||||||
userId: user?.Id,
|
userId: user?.Id,
|
||||||
});
|
});
|
||||||
if (res.status === 200) {
|
if (res.status === 200) {
|
||||||
successHapticFeedback();
|
Haptics.notificationAsync(Haptics.NotificationFeedbackType.Success);
|
||||||
Alert.alert("Success", "Quick connect authorized");
|
Alert.alert("Success", "Quick connect authorized");
|
||||||
setQuickConnectCode(undefined);
|
setQuickConnectCode(undefined);
|
||||||
bottomSheetModalRef?.current?.close();
|
bottomSheetModalRef?.current?.close();
|
||||||
} else {
|
} else {
|
||||||
errorHapticFeedback();
|
Haptics.notificationAsync(Haptics.NotificationFeedbackType.Error);
|
||||||
Alert.alert("Error", "Invalid code");
|
Alert.alert("Error", "Invalid code");
|
||||||
}
|
}
|
||||||
} catch (e) {
|
} catch (e) {
|
||||||
errorHapticFeedback();
|
Haptics.notificationAsync(Haptics.NotificationFeedbackType.Error);
|
||||||
Alert.alert("Error", "Invalid code");
|
Alert.alert("Error", "Invalid code");
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -4,7 +4,7 @@ import { useDownload } from "@/providers/DownloadProvider";
|
|||||||
import { clearLogs } from "@/utils/log";
|
import { clearLogs } from "@/utils/log";
|
||||||
import { useQuery } from "@tanstack/react-query";
|
import { useQuery } from "@tanstack/react-query";
|
||||||
import * as FileSystem from "expo-file-system";
|
import * as FileSystem from "expo-file-system";
|
||||||
import { useHaptic } from "@/hooks/useHaptic";
|
import * as Haptics from "expo-haptics";
|
||||||
import { View } from "react-native";
|
import { View } from "react-native";
|
||||||
import * as Progress from "react-native-progress";
|
import * as Progress from "react-native-progress";
|
||||||
import { toast } from "sonner-native";
|
import { toast } from "sonner-native";
|
||||||
@@ -13,8 +13,6 @@ import { ListItem } from "../list/ListItem";
|
|||||||
|
|
||||||
export const StorageSettings = () => {
|
export const StorageSettings = () => {
|
||||||
const { deleteAllFiles, appSizeUsage } = useDownload();
|
const { deleteAllFiles, appSizeUsage } = useDownload();
|
||||||
const successHapticFeedback = useHaptic("success");
|
|
||||||
const errorHapticFeedback = useHaptic("error");
|
|
||||||
|
|
||||||
const { data: size, isLoading: appSizeLoading } = useQuery({
|
const { data: size, isLoading: appSizeLoading } = useQuery({
|
||||||
queryKey: ["appSize", appSizeUsage],
|
queryKey: ["appSize", appSizeUsage],
|
||||||
@@ -31,9 +29,9 @@ export const StorageSettings = () => {
|
|||||||
const onDeleteClicked = async () => {
|
const onDeleteClicked = async () => {
|
||||||
try {
|
try {
|
||||||
await deleteAllFiles();
|
await deleteAllFiles();
|
||||||
successHapticFeedback();
|
Haptics.notificationAsync(Haptics.NotificationFeedbackType.Success);
|
||||||
} catch (e) {
|
} catch (e) {
|
||||||
errorHapticFeedback();
|
Haptics.notificationAsync(Haptics.NotificationFeedbackType.Error);
|
||||||
toast.error("Error deleting files");
|
toast.error("Error deleting files");
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|||||||
@@ -29,7 +29,7 @@ import {
|
|||||||
BaseItemDto,
|
BaseItemDto,
|
||||||
MediaSourceInfo,
|
MediaSourceInfo,
|
||||||
} from "@jellyfin/sdk/lib/generated-client";
|
} from "@jellyfin/sdk/lib/generated-client";
|
||||||
import { useHaptic } from "@/hooks/useHaptic";
|
import * as Haptics from "expo-haptics";
|
||||||
import { Image } from "expo-image";
|
import { Image } from "expo-image";
|
||||||
import { useLocalSearchParams, useRouter } from "expo-router";
|
import { useLocalSearchParams, useRouter } from "expo-router";
|
||||||
import { useAtom } from "jotai";
|
import { useAtom } from "jotai";
|
||||||
@@ -157,12 +157,10 @@ export const Controls: React.FC<Props> = ({
|
|||||||
isVlc
|
isVlc
|
||||||
);
|
);
|
||||||
|
|
||||||
const lightHapticFeedback = useHaptic("light");
|
|
||||||
|
|
||||||
const goToPreviousItem = useCallback(() => {
|
const goToPreviousItem = useCallback(() => {
|
||||||
if (!previousItem || !settings) return;
|
if (!previousItem || !settings) return;
|
||||||
|
|
||||||
lightHapticFeedback();
|
Haptics.impactAsync(Haptics.ImpactFeedbackStyle.Light);
|
||||||
|
|
||||||
const previousIndexes: previousIndexes = {
|
const previousIndexes: previousIndexes = {
|
||||||
subtitleIndex: subtitleIndex ? parseInt(subtitleIndex) : undefined,
|
subtitleIndex: subtitleIndex ? parseInt(subtitleIndex) : undefined,
|
||||||
@@ -200,7 +198,7 @@ export const Controls: React.FC<Props> = ({
|
|||||||
const goToNextItem = useCallback(() => {
|
const goToNextItem = useCallback(() => {
|
||||||
if (!nextItem || !settings) return;
|
if (!nextItem || !settings) return;
|
||||||
|
|
||||||
lightHapticFeedback();
|
Haptics.impactAsync(Haptics.ImpactFeedbackStyle.Light);
|
||||||
|
|
||||||
const previousIndexes: previousIndexes = {
|
const previousIndexes: previousIndexes = {
|
||||||
subtitleIndex: subtitleIndex ? parseInt(subtitleIndex) : undefined,
|
subtitleIndex: subtitleIndex ? parseInt(subtitleIndex) : undefined,
|
||||||
@@ -328,7 +326,7 @@ export const Controls: React.FC<Props> = ({
|
|||||||
const handleSkipBackward = useCallback(async () => {
|
const handleSkipBackward = useCallback(async () => {
|
||||||
if (!settings?.rewindSkipTime) return;
|
if (!settings?.rewindSkipTime) return;
|
||||||
wasPlayingRef.current = isPlaying;
|
wasPlayingRef.current = isPlaying;
|
||||||
lightHapticFeedback();
|
Haptics.impactAsync(Haptics.ImpactFeedbackStyle.Light);
|
||||||
try {
|
try {
|
||||||
const curr = progress.value;
|
const curr = progress.value;
|
||||||
if (curr !== undefined) {
|
if (curr !== undefined) {
|
||||||
@@ -346,7 +344,7 @@ export const Controls: React.FC<Props> = ({
|
|||||||
const handleSkipForward = useCallback(async () => {
|
const handleSkipForward = useCallback(async () => {
|
||||||
if (!settings?.forwardSkipTime) return;
|
if (!settings?.forwardSkipTime) return;
|
||||||
wasPlayingRef.current = isPlaying;
|
wasPlayingRef.current = isPlaying;
|
||||||
lightHapticFeedback();
|
Haptics.impactAsync(Haptics.ImpactFeedbackStyle.Light);
|
||||||
try {
|
try {
|
||||||
const curr = progress.value;
|
const curr = progress.value;
|
||||||
if (curr !== undefined) {
|
if (curr !== undefined) {
|
||||||
@@ -363,7 +361,7 @@ export const Controls: React.FC<Props> = ({
|
|||||||
|
|
||||||
const toggleIgnoreSafeAreas = useCallback(() => {
|
const toggleIgnoreSafeAreas = useCallback(() => {
|
||||||
setIgnoreSafeAreas((prev) => !prev);
|
setIgnoreSafeAreas((prev) => !prev);
|
||||||
lightHapticFeedback();
|
Haptics.impactAsync(Haptics.ImpactFeedbackStyle.Light);
|
||||||
}, []);
|
}, []);
|
||||||
|
|
||||||
const memoizedRenderBubble = useCallback(() => {
|
const memoizedRenderBubble = useCallback(() => {
|
||||||
@@ -442,7 +440,7 @@ export const Controls: React.FC<Props> = ({
|
|||||||
const gotoItem = await getItemById(api, itemId);
|
const gotoItem = await getItemById(api, itemId);
|
||||||
if (!settings || !gotoItem) return;
|
if (!settings || !gotoItem) return;
|
||||||
|
|
||||||
lightHapticFeedback();
|
Haptics.impactAsync(Haptics.ImpactFeedbackStyle.Light);
|
||||||
|
|
||||||
const previousIndexes: previousIndexes = {
|
const previousIndexes: previousIndexes = {
|
||||||
subtitleIndex: subtitleIndex ? parseInt(subtitleIndex) : undefined,
|
subtitleIndex: subtitleIndex ? parseInt(subtitleIndex) : undefined,
|
||||||
@@ -586,7 +584,7 @@ export const Controls: React.FC<Props> = ({
|
|||||||
)}
|
)}
|
||||||
<TouchableOpacity
|
<TouchableOpacity
|
||||||
onPress={async () => {
|
onPress={async () => {
|
||||||
lightHapticFeedback();
|
Haptics.impactAsync(Haptics.ImpactFeedbackStyle.Light);
|
||||||
router.back();
|
router.back();
|
||||||
}}
|
}}
|
||||||
className="aspect-square flex flex-col bg-neutral-800/90 rounded-xl items-center justify-center p-2"
|
className="aspect-square flex flex-col bg-neutral-800/90 rounded-xl items-center justify-center p-2"
|
||||||
|
|||||||
@@ -5,7 +5,7 @@ import { apiAtom } from "@/providers/JellyfinProvider";
|
|||||||
import { getAuthHeaders } from "@/utils/jellyfin/jellyfin";
|
import { getAuthHeaders } from "@/utils/jellyfin/jellyfin";
|
||||||
import { writeToLog } from "@/utils/log";
|
import { writeToLog } from "@/utils/log";
|
||||||
import { msToSeconds, secondsToMs } from "@/utils/time";
|
import { msToSeconds, secondsToMs } from "@/utils/time";
|
||||||
import { useHaptic } from "./useHaptic";
|
import * as Haptics from "expo-haptics";
|
||||||
|
|
||||||
interface CreditTimestamps {
|
interface CreditTimestamps {
|
||||||
Introduction: {
|
Introduction: {
|
||||||
@@ -29,7 +29,6 @@ export const useCreditSkipper = (
|
|||||||
) => {
|
) => {
|
||||||
const [api] = useAtom(apiAtom);
|
const [api] = useAtom(apiAtom);
|
||||||
const [showSkipCreditButton, setShowSkipCreditButton] = useState(false);
|
const [showSkipCreditButton, setShowSkipCreditButton] = useState(false);
|
||||||
const lightHapticFeedback = useHaptic("light");
|
|
||||||
|
|
||||||
if (isVlc) {
|
if (isVlc) {
|
||||||
currentTime = msToSeconds(currentTime);
|
currentTime = msToSeconds(currentTime);
|
||||||
@@ -80,7 +79,7 @@ export const useCreditSkipper = (
|
|||||||
if (!creditTimestamps) return;
|
if (!creditTimestamps) return;
|
||||||
console.log(`Skipping credits to ${creditTimestamps.Credits.End}`);
|
console.log(`Skipping credits to ${creditTimestamps.Credits.End}`);
|
||||||
try {
|
try {
|
||||||
lightHapticFeedback();
|
Haptics.impactAsync(Haptics.ImpactFeedbackStyle.Light);
|
||||||
wrappedSeek(creditTimestamps.Credits.End);
|
wrappedSeek(creditTimestamps.Credits.End);
|
||||||
setTimeout(() => {
|
setTimeout(() => {
|
||||||
play();
|
play();
|
||||||
|
|||||||
@@ -1,54 +0,0 @@
|
|||||||
import { useCallback, useMemo } from "react";
|
|
||||||
import { Platform } from "react-native";
|
|
||||||
import * as Haptics from "expo-haptics";
|
|
||||||
import { useSettings } from "@/utils/atoms/settings";
|
|
||||||
|
|
||||||
export type HapticFeedbackType =
|
|
||||||
| "light"
|
|
||||||
| "medium"
|
|
||||||
| "heavy"
|
|
||||||
| "selection"
|
|
||||||
| "success"
|
|
||||||
| "warning"
|
|
||||||
| "error";
|
|
||||||
|
|
||||||
export const useHaptic = (feedbackType: HapticFeedbackType = "selection") => {
|
|
||||||
const [settings] = useSettings();
|
|
||||||
|
|
||||||
const createHapticHandler = useCallback(
|
|
||||||
(type: Haptics.ImpactFeedbackStyle) => {
|
|
||||||
return Platform.OS === "web" ? () => {} : () => Haptics.impactAsync(type);
|
|
||||||
},
|
|
||||||
[]
|
|
||||||
);
|
|
||||||
const createNotificationFeedback = useCallback(
|
|
||||||
(type: Haptics.NotificationFeedbackType) => {
|
|
||||||
return Platform.OS === "web"
|
|
||||||
? () => {}
|
|
||||||
: () => Haptics.notificationAsync(type);
|
|
||||||
},
|
|
||||||
[]
|
|
||||||
);
|
|
||||||
|
|
||||||
const hapticHandlers = useMemo(
|
|
||||||
() => ({
|
|
||||||
light: createHapticHandler(Haptics.ImpactFeedbackStyle.Light),
|
|
||||||
medium: createHapticHandler(Haptics.ImpactFeedbackStyle.Medium),
|
|
||||||
heavy: createHapticHandler(Haptics.ImpactFeedbackStyle.Heavy),
|
|
||||||
selection: Platform.OS === "web" ? () => {} : Haptics.selectionAsync,
|
|
||||||
success: createNotificationFeedback(
|
|
||||||
Haptics.NotificationFeedbackType.Success
|
|
||||||
),
|
|
||||||
warning: createNotificationFeedback(
|
|
||||||
Haptics.NotificationFeedbackType.Warning
|
|
||||||
),
|
|
||||||
error: createNotificationFeedback(Haptics.NotificationFeedbackType.Error),
|
|
||||||
}),
|
|
||||||
[createHapticHandler, createNotificationFeedback]
|
|
||||||
);
|
|
||||||
|
|
||||||
if (settings?.disableHapticFeedback) {
|
|
||||||
return () => {};
|
|
||||||
}
|
|
||||||
return hapticHandlers[feedbackType];
|
|
||||||
};
|
|
||||||
@@ -5,7 +5,7 @@ import { apiAtom } from "@/providers/JellyfinProvider";
|
|||||||
import { getAuthHeaders } from "@/utils/jellyfin/jellyfin";
|
import { getAuthHeaders } from "@/utils/jellyfin/jellyfin";
|
||||||
import { writeToLog } from "@/utils/log";
|
import { writeToLog } from "@/utils/log";
|
||||||
import { msToSeconds, secondsToMs } from "@/utils/time";
|
import { msToSeconds, secondsToMs } from "@/utils/time";
|
||||||
import { useHaptic } from "./useHaptic";
|
import * as Haptics from "expo-haptics";
|
||||||
|
|
||||||
interface IntroTimestamps {
|
interface IntroTimestamps {
|
||||||
EpisodeId: string;
|
EpisodeId: string;
|
||||||
@@ -33,7 +33,6 @@ export const useIntroSkipper = (
|
|||||||
if (isVlc) {
|
if (isVlc) {
|
||||||
currentTime = msToSeconds(currentTime);
|
currentTime = msToSeconds(currentTime);
|
||||||
}
|
}
|
||||||
const lightHapticFeedback = useHaptic("light");
|
|
||||||
|
|
||||||
const wrappedSeek = (seconds: number) => {
|
const wrappedSeek = (seconds: number) => {
|
||||||
if (isVlc) {
|
if (isVlc) {
|
||||||
@@ -79,7 +78,7 @@ export const useIntroSkipper = (
|
|||||||
const skipIntro = useCallback(() => {
|
const skipIntro = useCallback(() => {
|
||||||
if (!introTimestamps) return;
|
if (!introTimestamps) return;
|
||||||
try {
|
try {
|
||||||
lightHapticFeedback();
|
Haptics.impactAsync(Haptics.ImpactFeedbackStyle.Light);
|
||||||
wrappedSeek(introTimestamps.IntroEnd);
|
wrappedSeek(introTimestamps.IntroEnd);
|
||||||
setTimeout(() => {
|
setTimeout(() => {
|
||||||
play();
|
play();
|
||||||
|
|||||||
@@ -3,14 +3,13 @@ import { markAsNotPlayed } from "@/utils/jellyfin/playstate/markAsNotPlayed";
|
|||||||
import { markAsPlayed } from "@/utils/jellyfin/playstate/markAsPlayed";
|
import { markAsPlayed } from "@/utils/jellyfin/playstate/markAsPlayed";
|
||||||
import { BaseItemDto } from "@jellyfin/sdk/lib/generated-client/models";
|
import { BaseItemDto } from "@jellyfin/sdk/lib/generated-client/models";
|
||||||
import { useQueryClient } from "@tanstack/react-query";
|
import { useQueryClient } from "@tanstack/react-query";
|
||||||
import { useHaptic } from "./useHaptic";
|
import * as Haptics from "expo-haptics";
|
||||||
import { useAtom } from "jotai";
|
import { useAtom } from "jotai";
|
||||||
|
|
||||||
export const useMarkAsPlayed = (item: BaseItemDto) => {
|
export const useMarkAsPlayed = (item: BaseItemDto) => {
|
||||||
const [api] = useAtom(apiAtom);
|
const [api] = useAtom(apiAtom);
|
||||||
const [user] = useAtom(userAtom);
|
const [user] = useAtom(userAtom);
|
||||||
const queryClient = useQueryClient();
|
const queryClient = useQueryClient();
|
||||||
const lightHapticFeedback = useHaptic("light");
|
|
||||||
|
|
||||||
const invalidateQueries = () => {
|
const invalidateQueries = () => {
|
||||||
const queriesToInvalidate = [
|
const queriesToInvalidate = [
|
||||||
@@ -30,7 +29,7 @@ export const useMarkAsPlayed = (item: BaseItemDto) => {
|
|||||||
};
|
};
|
||||||
|
|
||||||
const markAsPlayedStatus = async (played: boolean) => {
|
const markAsPlayedStatus = async (played: boolean) => {
|
||||||
lightHapticFeedback();
|
Haptics.impactAsync(Haptics.ImpactFeedbackStyle.Light);
|
||||||
|
|
||||||
// Optimistic update
|
// Optimistic update
|
||||||
queryClient.setQueryData(
|
queryClient.setQueryData(
|
||||||
|
|||||||
@@ -48,7 +48,7 @@ import useImageStorage from "@/hooks/useImageStorage";
|
|||||||
import { storage } from "@/utils/mmkv";
|
import { storage } from "@/utils/mmkv";
|
||||||
import useDownloadHelper from "@/utils/download";
|
import useDownloadHelper from "@/utils/download";
|
||||||
import { FileInfo } from "expo-file-system";
|
import { FileInfo } from "expo-file-system";
|
||||||
import { useHaptic } from "@/hooks/useHaptic";
|
import * as Haptics from "expo-haptics";
|
||||||
import * as Application from "expo-application";
|
import * as Application from "expo-application";
|
||||||
|
|
||||||
export type DownloadedItem = {
|
export type DownloadedItem = {
|
||||||
@@ -78,8 +78,6 @@ function useDownloadProvider() {
|
|||||||
|
|
||||||
const [processes, setProcesses] = useAtom<JobStatus[]>(processesAtom);
|
const [processes, setProcesses] = useAtom<JobStatus[]>(processesAtom);
|
||||||
|
|
||||||
const successHapticFeedback = useHaptic("success");
|
|
||||||
|
|
||||||
const authHeader = useMemo(() => {
|
const authHeader = useMemo(() => {
|
||||||
return api?.accessToken;
|
return api?.accessToken;
|
||||||
}, [api]);
|
}, [api]);
|
||||||
@@ -534,7 +532,9 @@ function useDownloadProvider() {
|
|||||||
if (i.Id) return deleteFile(i.Id);
|
if (i.Id) return deleteFile(i.Id);
|
||||||
return;
|
return;
|
||||||
})
|
})
|
||||||
).then(() => successHapticFeedback());
|
).then(() =>
|
||||||
|
Haptics.notificationAsync(Haptics.NotificationFeedbackType.Success)
|
||||||
|
);
|
||||||
};
|
};
|
||||||
|
|
||||||
const cleanCacheDirectory = async () => {
|
const cleanCacheDirectory = async () => {
|
||||||
|
|||||||
@@ -84,7 +84,6 @@ export type Settings = {
|
|||||||
downloadMethod: "optimized" | "remux";
|
downloadMethod: "optimized" | "remux";
|
||||||
autoDownload: boolean;
|
autoDownload: boolean;
|
||||||
showCustomMenuLinks: boolean;
|
showCustomMenuLinks: boolean;
|
||||||
disableHapticFeedback: boolean;
|
|
||||||
subtitleSize: number;
|
subtitleSize: number;
|
||||||
remuxConcurrentLimit: 1 | 2 | 3 | 4;
|
remuxConcurrentLimit: 1 | 2 | 3 | 4;
|
||||||
safeAreaInControlsEnabled: boolean;
|
safeAreaInControlsEnabled: boolean;
|
||||||
@@ -123,7 +122,6 @@ const loadSettings = (): Settings => {
|
|||||||
downloadMethod: "remux",
|
downloadMethod: "remux",
|
||||||
autoDownload: false,
|
autoDownload: false,
|
||||||
showCustomMenuLinks: false,
|
showCustomMenuLinks: false,
|
||||||
disableHapticFeedback: false,
|
|
||||||
subtitleSize: Platform.OS === "ios" ? 60 : 100,
|
subtitleSize: Platform.OS === "ios" ? 60 : 100,
|
||||||
remuxConcurrentLimit: 1,
|
remuxConcurrentLimit: 1,
|
||||||
safeAreaInControlsEnabled: true,
|
safeAreaInControlsEnabled: true,
|
||||||
|
|||||||
Reference in New Issue
Block a user