mirror of
https://github.com/streamyfin/streamyfin.git
synced 2026-06-02 12:08:37 +01:00
Bring 323 commits of develop (incl. the Expo SDK 56 / TV-branch work) into the chromecast refactor. Conflict resolutions: - chapters: take develop's reviewed version (ChapterList/ChapterTicks/ chapters.ts/test) — adds chapterNameAt, markers API, themed Colors. - auto-skip: keep chromecast's unified useSegmentSkipper for the phone player; restore develop's useCreditSkipper/useIntroSkipper (deleted on chromecast) so develop's Controls.tv.tsx compiles. TV->useSegmentSkipper migration left as follow-up. - en.json: union the two player blocks (kept chromecast casting keys + develop's subtitle/playback keys). - TechnicalInfoOverlay/PlatformDropdown: take develop's TV-safe versions (kept chromecast's disabled-prop branch, aliased to avoid shadowing the @expo/ui disabled modifier). - SDK 56 fixes: expo-router Router -> ImperativeRouter in cast components; ChapterTicks markers API in CastPlayerProgressBar. - restore utils/profiles/chromecast* (deleted on chromecast, still used by PlayButton). Typecheck passes; bun.lock regenerated against merged package.json.
70 lines
1.9 KiB
TypeScript
70 lines
1.9 KiB
TypeScript
import type { ImperativeRouter } from "expo-router";
|
|
import { useCallback } from "react";
|
|
import { Gesture } from "react-native-gesture-handler";
|
|
import {
|
|
runOnJS,
|
|
useAnimatedStyle,
|
|
useSharedValue,
|
|
withSpring,
|
|
} from "react-native-reanimated";
|
|
|
|
interface UseCastDismissGestureParams {
|
|
router: ImperativeRouter;
|
|
}
|
|
|
|
/**
|
|
* Swipe-down-to-dismiss gesture cluster for the casting player modal.
|
|
* Owns the `translateY`/`context` shared values, the pan gesture, the animated
|
|
* style, and the `dismissModal` callback (also invoked by the header button).
|
|
*/
|
|
export function useCastDismissGesture({ router }: UseCastDismissGestureParams) {
|
|
// Swipe down to dismiss gesture
|
|
const translateY = useSharedValue(0);
|
|
const context = useSharedValue({ y: 0 });
|
|
|
|
const dismissModal = useCallback(() => {
|
|
// Navigate immediately without animation to prevent crashes
|
|
if (router.canGoBack()) {
|
|
router.back();
|
|
} else {
|
|
router.replace("/(auth)/(tabs)/(home)/");
|
|
}
|
|
}, [router]);
|
|
|
|
const panGesture = Gesture.Pan()
|
|
.onStart(() => {
|
|
context.value = { y: translateY.value };
|
|
})
|
|
.onUpdate((event) => {
|
|
// Only allow downward swipes from top of screen
|
|
if (event.translationY > 0) {
|
|
translateY.value = context.value.y + event.translationY;
|
|
}
|
|
})
|
|
.onEnd((event) => {
|
|
// Dismiss if swiped down more than 150px or fast swipe
|
|
if (event.translationY > 150 || event.velocityY > 600) {
|
|
// Animate down and dismiss
|
|
translateY.value = withSpring(
|
|
1000,
|
|
{
|
|
damping: 20,
|
|
stiffness: 90,
|
|
},
|
|
() => {
|
|
runOnJS(dismissModal)();
|
|
},
|
|
);
|
|
} else {
|
|
// Spring back to position
|
|
translateY.value = withSpring(0);
|
|
}
|
|
});
|
|
|
|
const animatedStyle = useAnimatedStyle(() => ({
|
|
transform: [{ translateY: translateY.value }],
|
|
}));
|
|
|
|
return { panGesture, animatedStyle, dismissModal };
|
|
}
|