import { MaterialIcons } from '@expo/vector-icons'; import Slider from '@react-native-community/slider'; import { ReactNode, useEffect, useState } from 'react'; import { LayoutRectangle, StyleSheet, Text, View } from 'react-native'; import LinearGradient from 'react-native-linear-gradient'; import { ProgressInfo, VideoInfo, VideoPlayer } from '../Player.types'; import { VideoPlayerEventsObserver, VideoPlayerListener } from '../VideoView'; import { Focussable } from './components/Focussable'; type ControlsBarProps = { player: VideoPlayer; playerObserver: VideoPlayerEventsObserver; onBack?: () => void; onPrevious?: () => void; onNext?: () => void; onBackward?: () => void; onForward?: () => void; centerLeftButton?: ReactNode; leftButton?: ReactNode; leftButtons?: ReactNode; rightButton?: ReactNode; rightButtons?: ReactNode; backwardSeconds: number; forwardSeconds: number; }; const buttonSize = 40; export const ControlsBar = ({ player, playerObserver, onBack, onPrevious, onNext, onBackward, onForward, leftButton, leftButtons, rightButton, rightButtons, centerLeftButton, backwardSeconds, forwardSeconds }: ControlsBarProps) => { const title = player.title; const [paused, setPaused] = useState(player.paused); const [progress, setProgress] = useState(); const [videoInfo, setVideoInfo] = useState(); const [layout, setLayout] = useState(); useEffect(() => { const listener: VideoPlayerListener = { onPaused: setPaused, onProgress: setProgress, onLoaded: setVideoInfo }; playerObserver.addEventListener(listener); return () => { playerObserver.removeEventListener(listener); }; }, []); const backwardIcon = (): any => { switch (backwardSeconds) { case 5: return 'replay-5'; case 10: return 'replay-10'; case 30: return 'replay-30'; default: return 'fast-rewind'; } }; const forwardIcon = (): any => { switch (forwardSeconds) { case 5: return 'forward-5'; case 10: return 'forward-10'; case 30: return 'forward-30'; default: return 'fast-forward'; } }; return ( setLayout(e.nativeEvent.layout)}> {onBack && ( )} {title && ( {title} )} {centerLeftButton} {(videoInfo?.seekable && ( {toTime(progress?.time)} (player.time = time)} minimumValue={0} maximumValue={videoInfo.duration || 0} /> {toTime(videoInfo.duration || 0)} )) || {''}} {leftButton} {leftButtons} {onPrevious && ( )} player.togglePlay()}> {onNext && ( )} {rightButtons} {rightButton} ); }; const styles = StyleSheet.create({ container: { position: 'absolute', flex: 1, width: '100%', height: '100%', alignItems: 'center' }, top: { width: '100%', flexDirection: 'row', alignItems: 'flex-start', justifyContent: 'center', paddingHorizontal: 20, gap: 20, paddingTop: 10 }, center: { flex: 1, width: '100%', flexDirection: 'row', justifyContent: 'flex-start', alignItems: 'flex-start' }, progressionBar: { zIndex: 10, width: '100%', flexDirection: 'row', justifyContent: 'center', alignItems: 'center', gap: 5, height: 40 }, bottom: { paddingHorizontal: 15, width: '100%', flexDirection: 'row', justifyContent: 'space-between', alignItems: 'center', gap: 20 }, progressControls: { flex: 1, flexDirection: 'row', justifyContent: 'center', alignItems: 'center', gap: 20 }, part: { width: buttonSize, justifyContent: 'center', alignItems: 'center' } }); const toTime = (timeInMillis: number | undefined) => { if (timeInMillis !== undefined) { const timeInSeconds = Math.floor(timeInMillis / 1000); const hours = Math.floor(timeInSeconds / 3600); const minutes = Math.floor((timeInSeconds - hours * 3600) / 60); const seconds = timeInSeconds % 60; return `${leftPad(hours, 2)}:${leftPad(minutes, 2)}:${leftPad(seconds, 2)}`; } return ''; }; const leftPad = (value?: number, pad = 2) => (value?.toFixed() || '').padStart(pad, '0');