/* eslint-disable no-undef */ import LottieView from 'lottie-react-native'; import React, { forwardRef, useCallback, useEffect, useImperativeHandle, useRef, useState, } from 'react'; import { Dimensions, Image, StatusBar, StyleSheet, TouchableOpacity, useWindowDimensions, View, ViewStyle, } from 'react-native'; import { AwesomeSliderProps, Slider, SliderThemeType, } from 'react-native-awesome-slider/src/index'; import { clamp } from 'react-native-awesome-slider/src/utils'; import type { PanGesture } from 'react-native-gesture-handler'; import { Gesture, GestureDetector } from 'react-native-gesture-handler'; import Orientation, { OrientationType } from '42-react-native-orientation-locker'; import Animated, { cancelAnimation, runOnJS, useAnimatedProps, useAnimatedStyle, useDerivedValue, useSharedValue, withDelay, withTiming, } from 'react-native-reanimated'; import { useSafeAreaInsets } from 'react-native-safe-area-context'; import Video, { OnLoadData, OnProgressData, OnSeekData, VideoProperties, } from 'react-native-video'; import { Text } from './components'; import { Ripple } from './components/ripple'; import { TapControler } from './tap-controler'; import { palette } from './theme/palette'; import { bin, isIos, useRefs } from './utils'; import { VideoLoader } from './video-loading'; import { formatTime, formatTimeToMins, secondToTime } from './video-utils'; export const { width, height, scale, fontScale } = Dimensions.get('window'); const VIDEO_DEFAULT_HEIGHT = width * (9 / 16); const hitSlop = { left: 8, bottom: 8, right: 8, top: 8 }; const controlAnimteConfig = { duration: 200, }; const AnimatedLottieView = Animated.createAnimatedComponent(LottieView); export type VideoProps = VideoProperties & { rateList?: number[]; showOnStart?: boolean; onEnterFullscreen?: () => void; onExitFullscreen?: () => void; controlTimeout?: number; videoDefaultHeight?: number; headerBarTitle?: string; onTapBack?: () => void; navigation?: any; autoPlay?: boolean; onToggleAutoPlay?: (state: boolean) => void; onTapMore?: () => void; doubleTapInterval?: number; theme?: SliderThemeType; paused: boolean; onPausedChange: (paused: boolean) => void; onTapPause?: (paused: boolean) => void; sliderProps?: Omit< AwesomeSliderProps, 'progress' | 'minimumValue' | 'maximumValue' >; videoHeight: Animated.SharedValue; customAnimationStyle?: Animated.AnimateStyle; controlViewOpacityValue?: Animated.SharedValue; onCustomPanGesture?: PanGesture; isFullScreen: Animated.SharedValue; disableControl?: boolean; renderBackIcon?: () => JSX.Element; renderFullScreenBackIcon?: () => JSX.Element; renderMore?: () => JSX.Element; renderFullScreen?: () => JSX.Element; onVideoPlayEnd?: () => void; onAutoPlayText?: string; offAutoPlayText?: string; }; export type VideoPlayerRef = { /** * Check control view to see if it is displayed before playing */ setPlay: () => void; /** * Check control view to see if it is displayed before pause */ setPause: () => void; /** * toggle full screen */ toggleFullSreen: (isFullScreen: boolean) => void; /** * toggle control opatity */ toggleControlViewOpacity: (isShow: boolean) => void; /** * seek to progress */ setSeekTo: (second: number) => void; }; const VideoPlayer = forwardRef( ( { resizeMode = 'contain', showOnStart = true, rateList = [0.5, 1, 1.5, 2], source, style, onEnterFullscreen, onExitFullscreen, controlTimeout = 2000, videoDefaultHeight = VIDEO_DEFAULT_HEIGHT, headerBarTitle = '', onTapBack, navigation, autoPlay = false, onToggleAutoPlay, onTapMore, doubleTapInterval = 500, theme = { minimumTrackTintColor: palette.Main(1), maximumTrackTintColor: palette.B(0.6), cacheTrackTintColor: palette.G1(1), bubbleBackgroundColor: palette.B(0.8), disableMinTrackTintColor: palette.Main(1), }, paused, onPausedChange, onTapPause, sliderProps, videoHeight, customAnimationStyle, onCustomPanGesture, isFullScreen, disableControl, renderBackIcon, renderMore, renderFullScreen, renderFullScreenBackIcon, onVideoPlayEnd, onAutoPlayText = 'Autoplay is on', offAutoPlayText = 'Autoplay is off', ...rest }, ref, ) => { /** * hooks */ const insets = useSafeAreaInsets(); const insetsRef = useRef(insets); const dimensions = useWindowDimensions(); const leftDoubleTapBoundary = dimensions.width / 2 - insets.left - insets.right - 80; const rightDoubleTapBoundary = dimensions.width - leftDoubleTapBoundary - insets.left - insets.right; const [isFullScreenState, setIsFullscreen] = useState(false); const [currentTime, setCurrentTime] = useState(0); const [duration, setDuration] = useState(0); const [isLoadEnd, setIsLoadEnd] = useState(false); const [loading, setIsLoading] = useState(false); const [showTimeRemaining, setShowTimeRemaining] = useState(true); const [allowAutoPlayVideo, setAllowAutoPlayVideo] = useState(autoPlay); const [rateIndex, setRateIndex] = useState(rateList.findIndex((item) => item === 1)); useImperativeHandle(ref, () => ({ setPlay: () => { 'worklet'; checkTapTakesEffect(); play(); }, setPause: () => { 'worklet'; checkTapTakesEffect(); pause(); }, toggleFullSreen: (isFullScrren: boolean) => { isFullScrren ? enterFullScreen() : exitFullScreen(); }, toggleControlViewOpacity: (isShow: boolean) => { 'worklet'; isShow ? showControlAnimation() : hideControlAnimation(); }, setSeekTo: (seconds: number) => { seekTo(seconds); }, })); /** * refs */ const videoPlayer = useRef