From 15c66bc714b1336403a7ee528b23840e46fff47a Mon Sep 17 00:00:00 2001 From: Lance Chant <13349722+lancechant@users.noreply.github.com> Date: Fri, 17 Jul 2026 11:45:42 +0200 Subject: [PATCH] Addressing PR comments Signed-off-by: Lance Chant <13349722+lancechant@users.noreply.github.com> --- components/video-player/controls/Controls.tsx | 12 ++++++- .../controls/SkipSegmentOverlay.tsx | 35 +++++++++++++++---- 2 files changed, 40 insertions(+), 7 deletions(-) diff --git a/components/video-player/controls/Controls.tsx b/components/video-player/controls/Controls.tsx index a80e8416b..662af826a 100644 --- a/components/video-player/controls/Controls.tsx +++ b/components/video-player/controls/Controls.tsx @@ -339,6 +339,16 @@ export const Controls: FC = ({ maxMs, ); + // Whether the "Next Episode" countdown will actually be rendered. The Skip + // Credits button yields to it only when this is true; if autoplay is + // disabled or its episode limit is reached, Skip Credits must stay available + // (mirrors the NextEpisodeCountDownButton mount gate in BottomControls). + const willShowNextEpisode = + !!nextItem && + settings.autoPlayNextEpisode !== false && + (settings.maxAutoPlayEpisodeCount.value === -1 || + settings.autoPlayEpisodeCount < settings.maxAutoPlayEpisodeCount.value); + const goToItemCommon = useCallback( (item: BaseItemDto) => { if (!item || !settings) { @@ -598,7 +608,7 @@ export const Controls: FC = ({ showSkipButton={showSkipButton} showSkipCreditButton={showSkipCreditButton} hasContentAfterCredits={hasContentAfterCredits} - hasNextItem={!!nextItem} + willShowNextEpisode={willShowNextEpisode} skipIntro={skipIntro} skipCredit={skipCredit} controlsVisible={showControls} diff --git a/components/video-player/controls/SkipSegmentOverlay.tsx b/components/video-player/controls/SkipSegmentOverlay.tsx index 1adba22d8..107e735b5 100644 --- a/components/video-player/controls/SkipSegmentOverlay.tsx +++ b/components/video-player/controls/SkipSegmentOverlay.tsx @@ -1,5 +1,5 @@ import type { FC } from "react"; -import { useEffect } from "react"; +import { useEffect, useState } from "react"; import { StyleSheet } from "react-native"; import Animated, { Easing, @@ -14,7 +14,7 @@ interface Props { showSkipButton: boolean; showSkipCreditButton: boolean; hasContentAfterCredits: boolean; - hasNextItem: boolean; + willShowNextEpisode: boolean; skipIntro: () => void; skipCredit: () => void; controlsVisible: boolean; @@ -34,6 +34,23 @@ const VISIBLE_BOTTOM = 65; const VISIBLE_RIGHT = 42; const ANIM_DURATION = 250; +// Keeps `value` true for `duration` ms after it turns false. SkipButton hides +// itself instantly via a `hidden` (display:none) class, which would preempt +// the parent's opacity fade-out — lagging the flag keeps the button rendered +// (and visible) while the fade plays out. +const useDelayedHide = (value: boolean, duration: number): boolean => { + const [display, setDisplay] = useState(value); + useEffect(() => { + if (value) { + setDisplay(true); + return; + } + const t = setTimeout(() => setDisplay(false), duration); + return () => clearTimeout(t); + }, [value, duration]); + return display; +}; + /** * Floating Skip Intro / Skip Credits buttons shown independently of the * player controls. They appear on their own during an intro or credits segment @@ -43,7 +60,7 @@ export const SkipSegmentOverlay: FC = ({ showSkipButton, showSkipCreditButton, hasContentAfterCredits, - hasNextItem, + willShowNextEpisode, skipIntro, skipCredit, controlsVisible, @@ -51,9 +68,15 @@ export const SkipSegmentOverlay: FC = ({ const insets = useControlsSafeAreaInsets(); const showCredit = - showSkipCreditButton && (hasContentAfterCredits || !hasNextItem); + showSkipCreditButton && (hasContentAfterCredits || !willShowNextEpisode); const visible = showSkipButton || showCredit; + // Drive each SkipButton with a lagged flag so it stays visible while the + // opacity fade-out plays, instead of disappearing the instant its segment + // ends. `visible` above still drives opacity/pointerEvents immediately. + const renderSkip = useDelayedHide(showSkipButton, ANIM_DURATION); + const renderCredit = useDelayedHide(showCredit, ANIM_DURATION); + const opacity = useSharedValue(visible ? 1 : 0); useEffect(() => { @@ -79,12 +102,12 @@ export const SkipSegmentOverlay: FC = ({ pointerEvents={visible ? "box-none" : "none"} >