@zezosoft/zezo-ott-react-native-video-player
Version:
Production-ready React Native OTT video player library for Android & iOS. Features: playlists, seasons, auto-next playback, subtitles (SRT/VTT), custom theming, analytics tracking, fullscreen mode, gesture controls, ads player (pre-roll/mid-roll/post-roll
167 lines (164 loc) • 5.19 kB
JavaScript
"use strict";
import React, { useCallback, useMemo } from 'react';
import { StyleSheet, Text, TouchableOpacity, View } from 'react-native';
import { scale, verticalScale, moderateScale } from 'react-native-size-matters';
import { RFValue } from 'react-native-responsive-fontsize';
import { Settings as SettingsIcon, SlidersHorizontal as SpeedIcon, Subtitles as SubtitlesIcon, ListChecks as EpisodesIcon } from 'lucide-react-native';
import { useVideoPlayerStore } from "../store/videoPlayerStore.js";
import { useAdsPlayerStore } from "../../AdsPlayer/store/adsPlayerStore.js";
import { videoRef } from "../utils/index.js";
import { formatTime } from "../utils/index.js";
import ProgressBar from "../components/ProgressBar.js";
import { useVideoPlayerConfig } from "../context/index.js";
import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
const ControlButton = ({
onPress,
icon,
label,
accessibilityLabel,
buttonStyle,
labelStyle,
colors
}) => /*#__PURE__*/_jsxs(TouchableOpacity, {
onPress: onPress,
style: [styles.button, buttonStyle],
activeOpacity: 0.8,
accessibilityLabel: accessibilityLabel ?? label,
children: [icon, /*#__PURE__*/_jsx(Text, {
style: [styles.buttonText, labelStyle, {
color: colors.text
}],
children: label
})]
});
const BottomControls = () => {
const {
duration,
currentTime,
activeTrack,
playableDuration,
setSettingsModal,
setIsPaused,
setControlsVisible,
playBackRateLabel
} = useVideoPlayerStore();
const {
pendingMidRollAds
} = useAdsPlayerStore();
const {
colors
} = useVideoPlayerConfig();
const getRemainingTime = useCallback((totalDuration, currentPos) => {
const remainingTime = totalDuration - currentPos;
return formatTime(Math.max(remainingTime, 0));
}, []);
const onSeek = value => {
videoRef.current?.seek(value);
};
const onOpenSettings = type => {
setSettingsModal({
action: type,
isVisible: true
});
setIsPaused(true);
setControlsVisible(false);
};
// Memoize ad markers to avoid recalculation on every render
const adMarkers = useMemo(() => pendingMidRollAds.filter(ad => ad.time !== undefined && ad.time > 0).map(ad => ad.time).sort((a, b) => a - b), [pendingMidRollAds]);
// Memoize time text style
const timeTextStyle = useMemo(() => [styles.timeText, {
color: colors.text
}], [colors.text]);
return /*#__PURE__*/_jsxs(View, {
children: [/*#__PURE__*/_jsxs(View, {
style: styles.sliderContainer,
children: [/*#__PURE__*/_jsx(View, {
style: styles.sliderWrapper,
children: /*#__PURE__*/_jsx(ProgressBar, {
duration: duration,
currentTime: currentTime,
bufferedTime: playableDuration,
onSeek: onSeek,
adMarkers: adMarkers
})
}), /*#__PURE__*/_jsx(Text, {
style: timeTextStyle,
children: getRemainingTime(duration, currentTime)
})]
}), /*#__PURE__*/_jsxs(View, {
style: styles.buttonsContainer,
children: [/*#__PURE__*/_jsx(ControlButton, {
onPress: () => onOpenSettings('speed'),
icon: /*#__PURE__*/_jsx(SpeedIcon, {
size: moderateScale(18),
color: colors.text
}),
label: playBackRateLabel && playBackRateLabel !== 'Normal' ? `Speed (${playBackRateLabel})` : 'Speed',
colors: colors
}), /*#__PURE__*/_jsx(ControlButton, {
onPress: () => onOpenSettings('audioOrSubtitle'),
icon: /*#__PURE__*/_jsx(SubtitlesIcon, {
size: moderateScale(19),
color: colors.text
}),
label: "Audio & Subtitles",
colors: colors
}), /*#__PURE__*/_jsx(ControlButton, {
onPress: () => onOpenSettings('settings'),
icon: /*#__PURE__*/_jsx(SettingsIcon, {
size: moderateScale(18),
color: colors.text
}),
label: "Settings",
colors: colors
}), !activeTrack?.isTrailer && activeTrack?.type === 'series' && /*#__PURE__*/_jsx(ControlButton, {
onPress: () => onOpenSettings('episodes'),
icon: /*#__PURE__*/_jsx(EpisodesIcon, {
size: moderateScale(17),
color: colors.text
}),
label: "Episodes",
colors: colors
})]
})]
});
};
export default BottomControls;
const styles = StyleSheet.create({
sliderContainer: {
flexDirection: 'row',
alignItems: 'center',
paddingHorizontal: scale(12)
},
sliderWrapper: {
flex: 1,
minWidth: 0
},
timeText: {
width: scale(50),
textAlign: 'right',
marginLeft: scale(4),
fontSize: RFValue(12),
fontWeight: '500'
},
buttonsContainer: {
flexDirection: 'row',
justifyContent: 'center',
flexWrap: 'wrap',
paddingHorizontal: scale(10)
},
button: {
flexDirection: 'row',
alignItems: 'center',
paddingHorizontal: scale(12),
paddingVertical: verticalScale(6),
marginHorizontal: scale(6),
marginVertical: verticalScale(4),
borderRadius: moderateScale(8)
},
buttonText: {
fontSize: RFValue(13),
marginLeft: scale(6)
}
});
//# sourceMappingURL=BottomControls.js.map