mirror of
https://github.com/streamyfin/streamyfin.git
synced 2026-01-15 23:59:08 +00:00
feat: fade and slide in controls (#964)
This commit is contained in:
committed by
GitHub
parent
f2219a1daa
commit
55ce7d8cec
@@ -27,7 +27,6 @@ import { useInvalidatePlaybackProgressCache } from "@/hooks/useRevalidatePlaybac
|
||||
import { useWebSocket } from "@/hooks/useWebsockets";
|
||||
import { VlcPlayerView } from "@/modules";
|
||||
import type {
|
||||
PipStartedPayload,
|
||||
PlaybackStatePayload,
|
||||
ProgressUpdatePayload,
|
||||
VlcPlayerViewRef,
|
||||
@@ -60,7 +59,6 @@ export default function page() {
|
||||
const [isMuted, setIsMuted] = useState(false);
|
||||
const [isBuffering, setIsBuffering] = useState(true);
|
||||
const [isVideoLoaded, setIsVideoLoaded] = useState(false);
|
||||
const [isPipStarted, setIsPipStarted] = useState(false);
|
||||
|
||||
const progress = useSharedValue(0);
|
||||
const isSeeking = useSharedValue(false);
|
||||
@@ -390,11 +388,6 @@ export default function page() {
|
||||
],
|
||||
);
|
||||
|
||||
const onPipStarted = useCallback((e: PipStartedPayload) => {
|
||||
const { pipStarted } = e.nativeEvent;
|
||||
setIsPipStarted(pipStarted);
|
||||
}, []);
|
||||
|
||||
/** Gets the initial playback position in seconds. */
|
||||
const startPosition = useMemo(() => {
|
||||
return ticksToSeconds(getInitialPlaybackTicks());
|
||||
@@ -690,7 +683,6 @@ export default function page() {
|
||||
onVideoProgress={onProgress}
|
||||
progressUpdateInterval={1000}
|
||||
onVideoStateChange={onPlaybackStateChanged}
|
||||
onPipStarted={onPipStarted}
|
||||
onVideoLoadEnd={() => {
|
||||
setIsVideoLoaded(true);
|
||||
}}
|
||||
@@ -704,7 +696,7 @@ export default function page() {
|
||||
}}
|
||||
/>
|
||||
</View>
|
||||
{!isPipStarted && isMounted === true && item && (
|
||||
{isMounted === true && item && (
|
||||
<Controls
|
||||
mediaSource={stream?.mediaSource}
|
||||
item={item}
|
||||
|
||||
@@ -117,7 +117,6 @@ export const BottomControls: FC<BottomControlsProps> = ({
|
||||
style={{
|
||||
flexDirection: "column",
|
||||
alignSelf: "flex-end",
|
||||
opacity: showControls ? 1 : 0,
|
||||
}}
|
||||
pointerEvents={showControls ? "box-none" : "none"}
|
||||
>
|
||||
@@ -164,9 +163,6 @@ export const BottomControls: FC<BottomControlsProps> = ({
|
||||
</View>
|
||||
<View
|
||||
className={"flex flex-col-reverse rounded-lg items-center my-2"}
|
||||
style={{
|
||||
opacity: showControls ? 1 : 0,
|
||||
}}
|
||||
pointerEvents={showControls ? "box-none" : "none"}
|
||||
>
|
||||
<View className={"flex flex-col w-full shrink"}>
|
||||
|
||||
@@ -55,7 +55,6 @@ export const CenterControls: FC<CenterControlsProps> = ({
|
||||
transform: [{ rotate: "270deg" }],
|
||||
left: 0,
|
||||
bottom: 30,
|
||||
opacity: showControls ? 1 : 0,
|
||||
}}
|
||||
>
|
||||
<BrightnessSlider />
|
||||
@@ -68,7 +67,6 @@ export const CenterControls: FC<CenterControlsProps> = ({
|
||||
position: "relative",
|
||||
justifyContent: "center",
|
||||
alignItems: "center",
|
||||
opacity: showControls ? 1 : 0,
|
||||
}}
|
||||
>
|
||||
<Ionicons
|
||||
@@ -101,9 +99,6 @@ export const CenterControls: FC<CenterControlsProps> = ({
|
||||
name={isPlaying ? "pause" : "play"}
|
||||
size={ICON_SIZES.CENTER}
|
||||
color='white'
|
||||
style={{
|
||||
opacity: showControls ? 1 : 0,
|
||||
}}
|
||||
/>
|
||||
) : (
|
||||
<Loader size={"large"} />
|
||||
@@ -118,7 +113,6 @@ export const CenterControls: FC<CenterControlsProps> = ({
|
||||
position: "relative",
|
||||
justifyContent: "center",
|
||||
alignItems: "center",
|
||||
opacity: showControls ? 1 : 0,
|
||||
}}
|
||||
>
|
||||
<Ionicons
|
||||
|
||||
@@ -13,10 +13,13 @@ import {
|
||||
useState,
|
||||
} from "react";
|
||||
import { useWindowDimensions } from "react-native";
|
||||
import {
|
||||
import Animated, {
|
||||
Easing,
|
||||
type SharedValue,
|
||||
useAnimatedReaction,
|
||||
useAnimatedStyle,
|
||||
useSharedValue,
|
||||
withTiming,
|
||||
} from "react-native-reanimated";
|
||||
import ContinueWatchingOverlay from "@/components/video-player/controls/ContinueWatchingOverlay";
|
||||
import { useCreditSkipper } from "@/hooks/useCreditSkipper";
|
||||
@@ -130,10 +133,61 @@ export const Controls: FC<Props> = ({
|
||||
const min = useSharedValue(0);
|
||||
const max = useSharedValue(item.RunTimeTicks || 0);
|
||||
|
||||
// Animation values for controls
|
||||
const controlsOpacity = useSharedValue(showControls ? 1 : 0);
|
||||
const headerTranslateY = useSharedValue(showControls ? 0 : -50);
|
||||
const bottomTranslateY = useSharedValue(showControls ? 0 : 50);
|
||||
|
||||
useEffect(() => {
|
||||
prefetchAllTrickplayImages();
|
||||
}, [prefetchAllTrickplayImages]);
|
||||
|
||||
// Animate controls visibility
|
||||
useEffect(() => {
|
||||
const animationConfig = {
|
||||
duration: 300,
|
||||
easing: Easing.out(Easing.quad),
|
||||
};
|
||||
|
||||
controlsOpacity.value = withTiming(showControls ? 1 : 0, animationConfig);
|
||||
headerTranslateY.value = withTiming(
|
||||
showControls ? 0 : -10,
|
||||
animationConfig,
|
||||
);
|
||||
bottomTranslateY.value = withTiming(showControls ? 0 : 10, animationConfig);
|
||||
}, [showControls, controlsOpacity, headerTranslateY, bottomTranslateY]);
|
||||
|
||||
// Create animated styles
|
||||
const headerAnimatedStyle = useAnimatedStyle(() => ({
|
||||
opacity: controlsOpacity.value,
|
||||
transform: [{ translateY: headerTranslateY.value }],
|
||||
position: "absolute" as const,
|
||||
top: 0,
|
||||
left: 0,
|
||||
right: 0,
|
||||
zIndex: 10,
|
||||
}));
|
||||
|
||||
const centerAnimatedStyle = useAnimatedStyle(() => ({
|
||||
opacity: controlsOpacity.value,
|
||||
position: "absolute" as const,
|
||||
top: 0,
|
||||
left: 0,
|
||||
right: 0,
|
||||
bottom: 0,
|
||||
zIndex: 5,
|
||||
}));
|
||||
|
||||
const bottomAnimatedStyle = useAnimatedStyle(() => ({
|
||||
opacity: controlsOpacity.value,
|
||||
transform: [{ translateY: bottomTranslateY.value }],
|
||||
position: "absolute" as const,
|
||||
bottom: 0,
|
||||
left: 0,
|
||||
right: 0,
|
||||
zIndex: 10,
|
||||
}));
|
||||
|
||||
// Initialize progress values
|
||||
useEffect(() => {
|
||||
if (item) {
|
||||
@@ -435,68 +489,83 @@ export const Controls: FC<Props> = ({
|
||||
showControls={showControls}
|
||||
onToggleControls={toggleControls}
|
||||
/>
|
||||
<HeaderControls
|
||||
item={item}
|
||||
showControls={showControls}
|
||||
offline={offline}
|
||||
mediaSource={mediaSource}
|
||||
startPictureInPicture={startPictureInPicture}
|
||||
switchOnEpisodeMode={switchOnEpisodeMode}
|
||||
goToPreviousItem={goToPreviousItem}
|
||||
goToNextItem={goToNextItem}
|
||||
previousItem={previousItem}
|
||||
nextItem={nextItem}
|
||||
getAudioTracks={getAudioTracks}
|
||||
getSubtitleTracks={getSubtitleTracks}
|
||||
setAudioTrack={setAudioTrack}
|
||||
setSubtitleTrack={setSubtitleTrack}
|
||||
setSubtitleURL={setSubtitleURL}
|
||||
aspectRatio={aspectRatio}
|
||||
scaleFactor={scaleFactor}
|
||||
setAspectRatio={setAspectRatio}
|
||||
setScaleFactor={setScaleFactor}
|
||||
setVideoAspectRatio={setVideoAspectRatio}
|
||||
setVideoScaleFactor={setVideoScaleFactor}
|
||||
/>
|
||||
<CenterControls
|
||||
showControls={showControls}
|
||||
isPlaying={isPlaying}
|
||||
isBuffering={isBuffering}
|
||||
showAudioSlider={showAudioSlider}
|
||||
setShowAudioSlider={setShowAudioSlider}
|
||||
togglePlay={togglePlay}
|
||||
handleSkipBackward={handleSkipBackward}
|
||||
handleSkipForward={handleSkipForward}
|
||||
/>
|
||||
<BottomControls
|
||||
item={item}
|
||||
showControls={showControls}
|
||||
isSliding={isSliding}
|
||||
showRemoteBubble={showRemoteBubble}
|
||||
currentTime={currentTime}
|
||||
remainingTime={remainingTime}
|
||||
isVlc={isVlc}
|
||||
showSkipButton={showSkipButton}
|
||||
showSkipCreditButton={showSkipCreditButton}
|
||||
skipIntro={skipIntro}
|
||||
skipCredit={skipCredit}
|
||||
nextItem={nextItem}
|
||||
handleNextEpisodeAutoPlay={handleNextEpisodeAutoPlay}
|
||||
handleNextEpisodeManual={handleNextEpisodeManual}
|
||||
handleControlsInteraction={handleControlsInteraction}
|
||||
min={min}
|
||||
max={max}
|
||||
effectiveProgress={effectiveProgress}
|
||||
cacheProgress={cacheProgress}
|
||||
handleSliderStart={handleSliderStart}
|
||||
handleSliderComplete={handleSliderComplete}
|
||||
handleSliderChange={handleSliderChange}
|
||||
handleTouchStart={handleTouchStart}
|
||||
handleTouchEnd={handleTouchEnd}
|
||||
trickPlayUrl={trickPlayUrl}
|
||||
trickplayInfo={trickplayInfo}
|
||||
time={isSliding || showRemoteBubble ? time : remoteTime}
|
||||
/>
|
||||
<Animated.View
|
||||
style={headerAnimatedStyle}
|
||||
pointerEvents={showControls ? "auto" : "none"}
|
||||
>
|
||||
<HeaderControls
|
||||
item={item}
|
||||
showControls={showControls}
|
||||
offline={offline}
|
||||
mediaSource={mediaSource}
|
||||
startPictureInPicture={startPictureInPicture}
|
||||
switchOnEpisodeMode={switchOnEpisodeMode}
|
||||
goToPreviousItem={goToPreviousItem}
|
||||
goToNextItem={goToNextItem}
|
||||
previousItem={previousItem}
|
||||
nextItem={nextItem}
|
||||
getAudioTracks={getAudioTracks}
|
||||
getSubtitleTracks={getSubtitleTracks}
|
||||
setAudioTrack={setAudioTrack}
|
||||
setSubtitleTrack={setSubtitleTrack}
|
||||
setSubtitleURL={setSubtitleURL}
|
||||
aspectRatio={aspectRatio}
|
||||
scaleFactor={scaleFactor}
|
||||
setAspectRatio={setAspectRatio}
|
||||
setScaleFactor={setScaleFactor}
|
||||
setVideoAspectRatio={setVideoAspectRatio}
|
||||
setVideoScaleFactor={setVideoScaleFactor}
|
||||
/>
|
||||
</Animated.View>
|
||||
<Animated.View
|
||||
style={centerAnimatedStyle}
|
||||
pointerEvents={showControls ? "box-none" : "none"}
|
||||
>
|
||||
<CenterControls
|
||||
showControls={showControls}
|
||||
isPlaying={isPlaying}
|
||||
isBuffering={isBuffering}
|
||||
showAudioSlider={showAudioSlider}
|
||||
setShowAudioSlider={setShowAudioSlider}
|
||||
togglePlay={togglePlay}
|
||||
handleSkipBackward={handleSkipBackward}
|
||||
handleSkipForward={handleSkipForward}
|
||||
/>
|
||||
</Animated.View>
|
||||
<Animated.View
|
||||
style={bottomAnimatedStyle}
|
||||
pointerEvents={showControls ? "auto" : "none"}
|
||||
>
|
||||
<BottomControls
|
||||
item={item}
|
||||
showControls={showControls}
|
||||
isSliding={isSliding}
|
||||
showRemoteBubble={showRemoteBubble}
|
||||
currentTime={currentTime}
|
||||
remainingTime={remainingTime}
|
||||
isVlc={isVlc}
|
||||
showSkipButton={showSkipButton}
|
||||
showSkipCreditButton={showSkipCreditButton}
|
||||
skipIntro={skipIntro}
|
||||
skipCredit={skipCredit}
|
||||
nextItem={nextItem}
|
||||
handleNextEpisodeAutoPlay={handleNextEpisodeAutoPlay}
|
||||
handleNextEpisodeManual={handleNextEpisodeManual}
|
||||
handleControlsInteraction={handleControlsInteraction}
|
||||
min={min}
|
||||
max={max}
|
||||
effectiveProgress={effectiveProgress}
|
||||
cacheProgress={cacheProgress}
|
||||
handleSliderStart={handleSliderStart}
|
||||
handleSliderComplete={handleSliderComplete}
|
||||
handleSliderChange={handleSliderChange}
|
||||
handleTouchStart={handleTouchStart}
|
||||
handleTouchEnd={handleTouchEnd}
|
||||
trickPlayUrl={trickPlayUrl}
|
||||
trickplayInfo={trickplayInfo}
|
||||
time={isSliding || showRemoteBubble ? time : remoteTime}
|
||||
/>
|
||||
</Animated.View>
|
||||
</>
|
||||
)}
|
||||
{settings.maxAutoPlayEpisodeCount.value !== -1 && (
|
||||
|
||||
@@ -106,7 +106,6 @@ export const HeaderControls: FC<HeaderControlsProps> = ({
|
||||
width: settings?.safeAreaInControlsEnabled
|
||||
? screenWidth - insets.left - insets.right
|
||||
: screenWidth,
|
||||
opacity: showControls ? 1 : 0,
|
||||
},
|
||||
]}
|
||||
pointerEvents={showControls ? "auto" : "none"}
|
||||
@@ -138,7 +137,6 @@ export const HeaderControls: FC<HeaderControlsProps> = ({
|
||||
name='picture-in-picture'
|
||||
size={ICON_SIZES.HEADER}
|
||||
color='white'
|
||||
style={{ opacity: showControls ? 1 : 0 }}
|
||||
/>
|
||||
</TouchableOpacity>
|
||||
)}
|
||||
|
||||
Reference in New Issue
Block a user