@zezosoft/zezo-ott-react-native-video-player
Version:
React Native OTT Video Player for Android & iOS. Supports playlists, seasons, auto-next playback, subtitles, theming, analytics, fullscreen mode, and custom controls. 🚀 Powered by ZezoSoft.
208 lines (207 loc) • 5.87 kB
JavaScript
"use strict";
/* eslint-disable react-native/no-inline-styles */
import React, { useState, useRef } from 'react';
import { ActivityIndicator, StyleSheet, Text, TouchableOpacity, View, Animated, Easing } from 'react-native';
import { moderateScale, scale } from 'react-native-size-matters';
import { RFValue } from 'react-native-responsive-fontsize';
import { Play, Pause, RotateCcw, RotateCw } from 'lucide-react-native';
import { useVideoPlayerStore } from "../store/videoPlayerStore.js";
import { videoRef } from "../utils/videoRef.js";
import { handlePause } from "../utils/index.js";
import { useVideoPlayerConfig } from "../context/index.js";
import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
const BUTTON_SIZE = moderateScale(80);
const SMALL_BUTTON_SIZE = moderateScale(60);
const FEEDBACK_DURATION = 500;
const MOVE_DISTANCE = 60;
const MiddleControls = () => {
const {
isPaused,
isBuffering,
setStartWatchTime,
error,
setIsPaused,
duration,
currentTime,
setCurrentTime
} = useVideoPlayerStore();
const {
colors
} = useVideoPlayerConfig();
if (error) {
return /*#__PURE__*/_jsx(View, {
style: [StyleSheet.absoluteFillObject, styles.container],
children: /*#__PURE__*/_jsx(Text, {
style: [styles.errorText, {
color: colors.text
}],
children: error
})
});
}
const handleSkip = delta => {
let newTime = currentTime + delta;
if (newTime < 0) newTime = 0;
if (newTime > duration) newTime = duration;
videoRef?.current?.seek(newTime);
setCurrentTime(newTime);
};
const isVideoEnded = duration > 0 && currentTime >= duration;
return /*#__PURE__*/_jsxs(View, {
style: [StyleSheet.absoluteFillObject, styles.container],
children: [/*#__PURE__*/_jsx(SkipButton, {
direction: "backward",
onPress: ({
second
}) => handleSkip(-second),
colors: colors,
disabled: currentTime <= 0
}), /*#__PURE__*/_jsx(TouchableOpacity, {
onPress: () => {
if (isBuffering) return;
if (isVideoEnded) {
videoRef.current?.seek(0);
setCurrentTime(0);
setIsPaused(false);
setStartWatchTime(Date.now());
return;
}
if (!isPaused) {
handlePause();
setIsPaused(true);
} else {
setIsPaused(false);
setStartWatchTime(Date.now());
}
},
style: [styles.button, styles.playButton],
activeOpacity: 0.8,
children: isBuffering ? /*#__PURE__*/_jsx(ActivityIndicator, {
size: scale(50),
color: colors.text
}) : isPaused ? /*#__PURE__*/_jsx(Play, {
size: scale(50),
color: colors.text
}) : /*#__PURE__*/_jsx(Pause, {
size: scale(50),
color: colors.text
})
}), /*#__PURE__*/_jsx(SkipButton, {
direction: "forward",
onPress: ({
second
}) => handleSkip(second),
colors: colors,
disabled: currentTime >= duration
})]
});
};
export default MiddleControls;
const SkipButton = ({
direction,
onPress,
colors,
disabled
}) => {
const [step, setStep] = useState(0);
const anim = useRef(new Animated.Value(0)).current;
const timeoutRef = useRef(null);
const handlePress = () => {
if (disabled) return;
const newStep = step + 10;
setStep(newStep);
onPress({
second: newStep
});
anim.setValue(0);
Animated.timing(anim, {
toValue: 1,
duration: FEEDBACK_DURATION,
easing: Easing.out(Easing.quad),
useNativeDriver: true
}).start();
if (timeoutRef.current) clearTimeout(timeoutRef.current);
timeoutRef.current = setTimeout(() => setStep(0), FEEDBACK_DURATION);
};
const translateX = anim.interpolate({
inputRange: [0, 1],
outputRange: direction === 'forward' ? [SMALL_BUTTON_SIZE, SMALL_BUTTON_SIZE + MOVE_DISTANCE] : [-SMALL_BUTTON_SIZE, -SMALL_BUTTON_SIZE - MOVE_DISTANCE]
});
const opacity = anim.interpolate({
inputRange: [0, 0.8, 1],
outputRange: [1, 1, 0]
});
const scaleAnim = anim.interpolate({
inputRange: [0, 0.5, 1],
outputRange: [1, 1.3, 1]
});
return /*#__PURE__*/_jsxs(TouchableOpacity, {
onPress: handlePress,
style: [styles.button, styles.smallButton, disabled && {
opacity: 0.3
}],
activeOpacity: disabled ? 1 : 0.7,
disabled: disabled,
children: [direction === 'forward' ? /*#__PURE__*/_jsx(RotateCw, {
size: scale(35),
color: colors.text
}) : /*#__PURE__*/_jsx(RotateCcw, {
size: scale(35),
color: colors.text
}), step > 0 && !disabled && /*#__PURE__*/_jsx(Animated.View, {
style: [styles.feedbackOverlay, {
transform: [{
translateX
}, {
scale: scaleAnim
}],
opacity
}],
children: /*#__PURE__*/_jsxs(Text, {
style: [styles.feedbackText, {
color: colors.text
}],
children: [step, "s"]
})
})]
});
};
const styles = StyleSheet.create({
container: {
flex: 1,
flexDirection: 'row',
justifyContent: 'center',
alignItems: 'center'
},
button: {
justifyContent: 'center',
alignItems: 'center',
borderRadius: 100
},
playButton: {
width: BUTTON_SIZE,
height: BUTTON_SIZE,
marginHorizontal: moderateScale(14)
},
smallButton: {
width: SMALL_BUTTON_SIZE,
height: SMALL_BUTTON_SIZE,
marginHorizontal: moderateScale(10)
},
errorText: {
fontSize: RFValue(18),
fontWeight: '600',
textAlign: 'center',
paddingHorizontal: scale(20)
},
feedbackOverlay: {
position: 'absolute',
justifyContent: 'center',
alignItems: 'center'
},
feedbackText: {
fontSize: RFValue(15),
fontWeight: '600'
}
});
//# sourceMappingURL=MiddleControls.js.map