mirror of
https://github.com/tapframe/NuvioStreaming.git
synced 2026-03-11 17:45:38 +00:00
skip intro ui changes
This commit is contained in:
parent
1d9a3b645b
commit
2d97cad1dc
2 changed files with 47 additions and 4 deletions
|
|
@ -105,7 +105,10 @@ const UpNextButton: React.FC<UpNextButtonProps> = ({
|
||||||
|
|
||||||
// Animate vertical offset based on controls visibility
|
// Animate vertical offset based on controls visibility
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
const target = controlsVisible ? -(controlsFixedOffset / 2) : 0;
|
// Android needs more offset to clear the slider
|
||||||
|
const androidOffset = controlsFixedOffset - 8;
|
||||||
|
const iosOffset = controlsFixedOffset / 2;
|
||||||
|
const target = controlsVisible ? -(Platform.OS === 'android' ? androidOffset : iosOffset) : 0;
|
||||||
Animated.timing(translateY, {
|
Animated.timing(translateY, {
|
||||||
toValue: target,
|
toValue: target,
|
||||||
duration: 220,
|
duration: 220,
|
||||||
|
|
|
||||||
|
|
@ -40,8 +40,10 @@ export const SkipIntroButton: React.FC<SkipIntroButtonProps> = ({
|
||||||
const [introData, setIntroData] = useState<IntroTimestamps | null>(null);
|
const [introData, setIntroData] = useState<IntroTimestamps | null>(null);
|
||||||
const [isVisible, setIsVisible] = useState(false);
|
const [isVisible, setIsVisible] = useState(false);
|
||||||
const [hasSkipped, setHasSkipped] = useState(false);
|
const [hasSkipped, setHasSkipped] = useState(false);
|
||||||
|
const [autoHidden, setAutoHidden] = useState(false);
|
||||||
const fetchedRef = useRef(false);
|
const fetchedRef = useRef(false);
|
||||||
const lastEpisodeRef = useRef<string>('');
|
const lastEpisodeRef = useRef<string>('');
|
||||||
|
const autoHideTimerRef = useRef<NodeJS.Timeout | null>(null);
|
||||||
|
|
||||||
// Animation values
|
// Animation values
|
||||||
const opacity = useSharedValue(0);
|
const opacity = useSharedValue(0);
|
||||||
|
|
@ -68,6 +70,7 @@ export const SkipIntroButton: React.FC<SkipIntroButtonProps> = ({
|
||||||
lastEpisodeRef.current = episodeKey;
|
lastEpisodeRef.current = episodeKey;
|
||||||
fetchedRef.current = true;
|
fetchedRef.current = true;
|
||||||
setHasSkipped(false);
|
setHasSkipped(false);
|
||||||
|
setAutoHidden(false);
|
||||||
|
|
||||||
const fetchIntroData = async () => {
|
const fetchIntroData = async () => {
|
||||||
logger.log(`[SkipIntroButton] Fetching intro data for ${imdbId} S${season}E${episode}...`);
|
logger.log(`[SkipIntroButton] Fetching intro data for ${imdbId} S${season}E${episode}...`);
|
||||||
|
|
@ -93,8 +96,11 @@ export const SkipIntroButton: React.FC<SkipIntroButtonProps> = ({
|
||||||
const shouldShowButton = useCallback(() => {
|
const shouldShowButton = useCallback(() => {
|
||||||
if (!introData || hasSkipped) return false;
|
if (!introData || hasSkipped) return false;
|
||||||
// Show when within intro range, with a small buffer at the end
|
// Show when within intro range, with a small buffer at the end
|
||||||
return currentTime >= introData.start_sec && currentTime < (introData.end_sec - 0.5);
|
const inIntroRange = currentTime >= introData.start_sec && currentTime < (introData.end_sec - 0.5);
|
||||||
}, [introData, currentTime, hasSkipped]);
|
// If auto-hidden, only show when controls are visible
|
||||||
|
if (autoHidden && !controlsVisible) return false;
|
||||||
|
return inIntroRange;
|
||||||
|
}, [introData, currentTime, hasSkipped, autoHidden, controlsVisible]);
|
||||||
|
|
||||||
// Handle visibility animations
|
// Handle visibility animations
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
|
|
@ -105,8 +111,21 @@ export const SkipIntroButton: React.FC<SkipIntroButtonProps> = ({
|
||||||
setIsVisible(true);
|
setIsVisible(true);
|
||||||
opacity.value = withTiming(1, { duration: 300, easing: Easing.out(Easing.cubic) });
|
opacity.value = withTiming(1, { duration: 300, easing: Easing.out(Easing.cubic) });
|
||||||
scale.value = withSpring(1, { damping: 15, stiffness: 150 });
|
scale.value = withSpring(1, { damping: 15, stiffness: 150 });
|
||||||
|
|
||||||
|
// Start 15-second auto-hide timer
|
||||||
|
if (autoHideTimerRef.current) clearTimeout(autoHideTimerRef.current);
|
||||||
|
autoHideTimerRef.current = setTimeout(() => {
|
||||||
|
if (!hasSkipped) {
|
||||||
|
logger.log('[SkipIntroButton] Auto-hiding after 15 seconds');
|
||||||
|
setAutoHidden(true);
|
||||||
|
opacity.value = withTiming(0, { duration: 200 });
|
||||||
|
scale.value = withTiming(0.8, { duration: 200 });
|
||||||
|
setTimeout(() => setIsVisible(false), 250);
|
||||||
|
}
|
||||||
|
}, 15000);
|
||||||
} else if (!shouldShow && isVisible) {
|
} else if (!shouldShow && isVisible) {
|
||||||
logger.log(`[SkipIntroButton] Hiding button - currentTime: ${currentTime.toFixed(1)}s, hasSkipped: ${hasSkipped}`);
|
logger.log(`[SkipIntroButton] Hiding button - currentTime: ${currentTime.toFixed(1)}s, hasSkipped: ${hasSkipped}`);
|
||||||
|
if (autoHideTimerRef.current) clearTimeout(autoHideTimerRef.current);
|
||||||
opacity.value = withTiming(0, { duration: 200 });
|
opacity.value = withTiming(0, { duration: 200 });
|
||||||
scale.value = withTiming(0.8, { duration: 200 });
|
scale.value = withTiming(0.8, { duration: 200 });
|
||||||
// Delay hiding to allow animation to complete
|
// Delay hiding to allow animation to complete
|
||||||
|
|
@ -114,9 +133,30 @@ export const SkipIntroButton: React.FC<SkipIntroButtonProps> = ({
|
||||||
}
|
}
|
||||||
}, [shouldShowButton, isVisible]);
|
}, [shouldShowButton, isVisible]);
|
||||||
|
|
||||||
|
// Re-show when controls become visible (if still in intro range and was auto-hidden)
|
||||||
|
useEffect(() => {
|
||||||
|
if (controlsVisible && autoHidden && introData && !hasSkipped) {
|
||||||
|
const inIntroRange = currentTime >= introData.start_sec && currentTime < (introData.end_sec - 0.5);
|
||||||
|
if (inIntroRange) {
|
||||||
|
logger.log('[SkipIntroButton] Re-showing button because controls became visible');
|
||||||
|
setAutoHidden(false);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}, [controlsVisible, autoHidden, introData, hasSkipped, currentTime]);
|
||||||
|
|
||||||
|
// Cleanup timer on unmount
|
||||||
|
useEffect(() => {
|
||||||
|
return () => {
|
||||||
|
if (autoHideTimerRef.current) clearTimeout(autoHideTimerRef.current);
|
||||||
|
};
|
||||||
|
}, []);
|
||||||
|
|
||||||
// Animate position based on controls visibility
|
// Animate position based on controls visibility
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
const target = controlsVisible ? -(controlsFixedOffset / 2) : 0;
|
// Android needs more offset to clear the slider
|
||||||
|
const androidOffset = controlsFixedOffset - 8;
|
||||||
|
const iosOffset = controlsFixedOffset / 2;
|
||||||
|
const target = controlsVisible ? -(Platform.OS === 'android' ? androidOffset : iosOffset) : 0;
|
||||||
translateY.value = withTiming(target, { duration: 220, easing: Easing.out(Easing.cubic) });
|
translateY.value = withTiming(target, { duration: 220, easing: Easing.out(Easing.cubic) });
|
||||||
}, [controlsVisible, controlsFixedOffset]);
|
}, [controlsVisible, controlsFixedOffset]);
|
||||||
|
|
||||||
|
|
|
||||||
Loading…
Reference in a new issue