From a3ecc0108c711cc705316fac84018e766753c9b9 Mon Sep 17 00:00:00 2001 From: Seyed Mostafa Hasani Date: Mon, 8 Jul 2024 11:24:32 +0330 Subject: [PATCH] chore(example/basic): refactor state variable (#3949) * refactor: basic example from class component to functional component * refactor: toast component path * refactor: bufferConfig prop * refacotr: import component path * fix: seekbar issue on iOS * refactor: state variable * chore: refactor overlay component according to new state structure --- examples/basic/src/VideoPlayer.tsx | 237 ++++++++++++-------- examples/basic/src/components/Overlay.tsx | 258 ++++++++++++++-------- examples/basic/src/constants/general.ts | 36 +-- examples/basic/src/types/types.ts | 40 +--- 4 files changed, 305 insertions(+), 266 deletions(-) diff --git a/examples/basic/src/VideoPlayer.tsx b/examples/basic/src/VideoPlayer.tsx index b6593302..627b198a 100644 --- a/examples/basic/src/VideoPlayer.tsx +++ b/examples/basic/src/VideoPlayer.tsx @@ -1,6 +1,6 @@ 'use strict'; -import React, {FC, useRef, useState} from 'react'; +import React, {FC, useCallback, useRef, useState} from 'react'; import {TouchableOpacity, View} from 'react-native'; @@ -25,24 +25,55 @@ import Video, { BufferingStrategyType, ReactVideoSource, SelectedTrackType, + ResizeMode, + VideoTrack, + SelectedTrack, + SelectedVideoTrack, } from 'react-native-video'; import styles from './styles'; import {AdditionalSourceInfo} from './types'; -import { - bufferConfig, - defaultValue, - srcList, - textTracksSelectionBy, -} from './constants'; +import {bufferConfig, srcList, textTracksSelectionBy} from './constants'; import {Overlay, toast} from './components'; type Props = NonNullable; const VideoPlayer: FC = ({}) => { - const [state, setState] = useState(defaultValue); + const [rate, setRate] = useState(1); + const [volume, setVolume] = useState(1); + const [muted, setMuted] = useState(false); + const [resizeMode, setResizeMode] = useState(ResizeMode.CONTAIN); + const [duration, setDuration] = useState(0); + const [currentTime, setCurrentTime] = useState(0); + const [_, setVideoSize] = useState({videoWidth: 0, videoHeight: 0}); + const [paused, setPaused] = useState(false); + const [fullscreen, setFullscreen] = useState(true); + const [decoration, setDecoration] = useState(true); + const [isLoading, setIsLoading] = useState(false); + const [audioTracks, setAudioTracks] = useState([]); + const [textTracks, setTextTracks] = useState([]); + const [videoTracks, setVideoTracks] = useState([]); + const [selectedAudioTrack, setSelectedAudioTrack] = useState< + SelectedTrack | undefined + >(undefined); + const [selectedTextTrack, setSelectedTextTrack] = useState< + SelectedTrack | undefined + >(undefined); + const [selectedVideoTrack, setSelectedVideoTrack] = + useState({ + type: SelectedVideoTrackType.AUTO, + }); + const [srcListId, setSrcListId] = useState(0); + const [repeat, setRepeat] = useState(false); + const [controls, setControls] = useState(false); + const [useCache, setUseCache] = useState(false); + const [poster, setPoster] = useState(undefined); + const [showNotificationControls, setShowNotificationControls] = + useState(false); + const [isSeeking, setIsSeeking] = useState(false); + const videoRef = useRef(null); - const viewStyle = state.fullscreen ? styles.fullScreen : styles.halfScreen; - const currentSrc = srcList[state.srcListId]; + const viewStyle = fullscreen ? styles.fullScreen : styles.halfScreen; + const currentSrc = srcList[srcListId]; const additional = currentSrc as AdditionalSourceInfo; const onAudioTracks = (data: OnAudioTracksData) => { @@ -50,28 +81,19 @@ const VideoPlayer: FC = ({}) => { return x.selected; }); if (selectedTrack?.index) { - setState({ - ...state, - audioTracks: data.audioTracks, - selectedAudioTrack: { - type: SelectedTrackType.INDEX, - value: selectedTrack?.index, - }, + setAudioTracks(data.audioTracks); + setSelectedAudioTrack({ + type: SelectedTrackType.INDEX, + value: selectedTrack.index, }); } else { - setState({ - ...state, - audioTracks: data.audioTracks, - }); + setAudioTracks(data.audioTracks); } }; const onVideoTracks = (data: OnVideoTracksData) => { console.log('onVideoTracks', data.videoTracks); - setState({ - ...state, - videoTracks: data.videoTracks, - }); + setVideoTracks(data.videoTracks); }; const onTextTracks = (data: OnTextTracksData) => { @@ -80,46 +102,42 @@ const VideoPlayer: FC = ({}) => { }); if (selectedTrack?.language) { - setState({ - ...state, - textTracks: data.textTracks, - selectedTextTrack: - textTracksSelectionBy === 'index' - ? { - type: SelectedTrackType.INDEX, - value: selectedTrack?.index, - } - : { - type: SelectedTrackType.LANGUAGE, - value: selectedTrack?.language, - }, - }); + setTextTracks(data.textTracks); + if (textTracksSelectionBy === 'index') { + setSelectedTextTrack({ + type: SelectedTrackType.INDEX, + value: selectedTrack?.index, + }); + } else { + setSelectedTextTrack({ + type: SelectedTrackType.LANGUAGE, + value: selectedTrack?.language, + }); + } } else { - setState({ - ...state, - textTracks: data.textTracks, - }); + setTextTracks(data.textTracks); } }; const onLoad = (data: OnLoadData) => { + setDuration(data.duration); onAudioTracks(data); onTextTracks(data); onVideoTracks(data); - setState({...state, duration: data.duration}); }; const onProgress = (data: OnProgressData) => { - setState({...state, currentTime: data.currentTime}); + setCurrentTime(data.currentTime); }; const onSeek = (data: OnSeekData) => { - setState({...state, currentTime: data.currentTime, isSeeking: false}); + setCurrentTime(data.currentTime); + setIsSeeking(false); }; const onVideoLoadStart = () => { console.log('onVideoLoadStart'); - setState({...state, isLoading: true}); + setIsLoading(true); }; const onTextTrackDataChanged = (data: OnTextTrackDataChangedData) => { @@ -128,29 +146,25 @@ const VideoPlayer: FC = ({}) => { const onAspectRatio = (data: OnVideoAspectRatioData) => { console.log('onAspectRadio called ' + JSON.stringify(data)); - setState({ - ...state, - videoWidth: data.width, - videoHeight: data.height, - }); + setVideoSize({videoWidth: data.width, videoHeight: data.height}); }; const onVideoBuffer = (param: OnBufferData) => { console.log('onVideoBuffer'); - setState({...state, isLoading: param.isBuffering}); + setIsLoading(param.isBuffering); }; const onReadyForDisplay = () => { console.log('onReadyForDisplay'); - setState({...state, isLoading: false}); + setIsLoading(false); }; const onAudioBecomingNoisy = () => { - setState({...state, paused: true}); + setPaused(true); }; const onAudioFocusChanged = (event: OnAudioFocusChangedData) => { - setState({...state, paused: !event.hasAudioFocus}); + setPaused(!event.hasAudioFocus); }; const onError = (err: OnVideoErrorData) => { @@ -159,7 +173,7 @@ const VideoPlayer: FC = ({}) => { }; const onEnd = () => { - if (!state.loop) { + if (!repeat) { channelUp(); } }; @@ -173,53 +187,49 @@ const VideoPlayer: FC = ({}) => { }; const goToChannel = (channel: number) => { - setState({ - ...state, - srcListId: channel, - duration: 0.0, - currentTime: 0.0, - videoWidth: 0, - videoHeight: 0, - isLoading: false, - audioTracks: [], - textTracks: [], - selectedAudioTrack: undefined, - selectedTextTrack: undefined, - selectedVideoTrack: { - type: SelectedVideoTrackType.AUTO, - }, + setSrcListId(channel); + setDuration(0); + setCurrentTime(0); + setVideoSize({videoWidth: 0, videoHeight: 0}); + setIsLoading(false); + setAudioTracks([]); + setTextTracks([]); + setSelectedAudioTrack(undefined); + setSelectedTextTrack(undefined); + setSelectedVideoTrack({ + type: SelectedVideoTrackType.AUTO, }); }; - const channelUp = () => { + const channelUp = useCallback(() => { console.log('channel up'); - goToChannel((state.srcListId + 1) % srcList.length); - }; + goToChannel((srcListId + 1) % srcList.length); + }, [srcListId]); - const channelDown = () => { + const channelDown = useCallback(() => { console.log('channel down'); - goToChannel((state.srcListId + srcList.length - 1) % srcList.length); - }; + goToChannel((srcListId + srcList.length - 1) % srcList.length); + }, [srcListId]); return ( - {(srcList[state.srcListId] as AdditionalSourceInfo)?.noView ? null : ( + {(srcList[srcListId] as AdditionalSourceInfo)?.noView ? null : ( ); diff --git a/examples/basic/src/components/Overlay.tsx b/examples/basic/src/components/Overlay.tsx index 83337d82..b9c806e2 100644 --- a/examples/basic/src/components/Overlay.tsx +++ b/examples/basic/src/components/Overlay.tsx @@ -13,13 +13,17 @@ import MultiValueControl, { MultiValueControlPropType, } from '../MultiValueControl.tsx'; import { + AudioTrack, ResizeMode, + SelectedTrack, SelectedTrackType, + SelectedVideoTrack, SelectedVideoTrackType, + TextTrack, VideoDecoderProperties, VideoRef, + VideoTrack, } from 'react-native-video'; -import {StateType} from '../types'; import { toast, Seeker, @@ -32,12 +36,91 @@ import { type Props = { channelDown: () => void; channelUp: () => void; - setState: (value: StateType) => void; - state: StateType; + fullscreen: boolean; + setFullscreen: (value: boolean) => void; + controls: boolean; + setControls: (value: boolean) => void; + decoration: boolean; + setDecoration: (value: boolean) => void; + showNotificationControls: boolean; + setShowNotificationControls: (value: boolean) => void; + selectedAudioTrack: SelectedTrack | undefined; + setSelectedAudioTrack: (value: SelectedTrack | undefined) => void; + selectedTextTrack: SelectedTrack | undefined; + setSelectedTextTrack: (value: SelectedTrack | undefined) => void; + selectedVideoTrack: SelectedVideoTrack; + setSelectedVideoTrack: (value: SelectedVideoTrack) => void; + setIsSeeking: (value: boolean) => void; + rate: number; + setRate: (value: number) => void; + volume: number; + setVolume: (value: number) => void; + resizeMode: ResizeMode; + setResizeMode: (value: ResizeMode) => void; + isLoading: boolean; + srcListId: number; + useCache: boolean; + setUseCache: (value: boolean) => void; + paused: boolean; + setPaused: (value: boolean) => void; + repeat: boolean; + setRepeat: (value: boolean) => void; + poster: string | undefined; + setPoster: (value: string | undefined) => void; + muted: boolean; + setMuted: (value: boolean) => void; + currentTime: number; + duration: number; + isSeeking: boolean; + audioTracks: AudioTrack[]; + textTracks: TextTrack[]; + videoTracks: VideoTrack[]; }; const _Overlay = forwardRef((props, ref) => { - const {state, setState, channelUp, channelDown} = props; + const { + channelUp, + channelDown, + setFullscreen, + fullscreen, + setControls, + controls, + setDecoration, + decoration, + setShowNotificationControls, + showNotificationControls, + setSelectedAudioTrack, + setSelectedTextTrack, + setSelectedVideoTrack, + setIsSeeking, + rate, + setRate, + volume, + setVolume, + resizeMode, + setResizeMode, + isLoading, + srcListId, + setUseCache, + useCache, + paused, + setPaused, + setRepeat, + repeat, + setPoster, + poster, + setMuted, + muted, + duration, + isSeeking, + currentTime, + textTracks, + videoTracks, + audioTracks, + selectedAudioTrack, + selectedVideoTrack, + selectedTextTrack, + } = props; const popupInfo = useCallback(() => { VideoDecoderProperties.getWidevineLevel().then((widevineLevel: number) => { VideoDecoderProperties.isHEVCSupported().then((hevc: string) => { @@ -59,82 +142,62 @@ const _Overlay = forwardRef((props, ref) => { }, []); const toggleFullscreen = () => { - setState({...state, fullscreen: !state.fullscreen}); + setFullscreen(!fullscreen); }; const toggleControls = () => { - setState({...state, showRNVControls: !state.showRNVControls}); + setControls(!controls); }; const toggleDecoration = () => { - setState({...state, decoration: !state.decoration}); + setDecoration(!decoration); // eslint-disable-next-line @typescript-eslint/ban-ts-comment // @ts-expect-error - ref.current?.setFullScreen(!state.decoration); + ref.current?.setFullScreen(!decoration); }; const toggleShowNotificationControls = () => { - setState({ - ...state, - showNotificationControls: !state.showNotificationControls, - }); + setShowNotificationControls(!showNotificationControls); }; const onSelectedAudioTrackChange = (itemValue: string) => { console.log('on audio value change ' + itemValue); if (itemValue === 'none') { - setState({ - ...state, - selectedAudioTrack: { - type: SelectedTrackType.DISABLED, - }, + setSelectedAudioTrack({ + type: SelectedTrackType.DISABLED, }); } else { - setState({ - ...state, - selectedAudioTrack: { - type: SelectedTrackType.INDEX, - value: itemValue, - }, + setSelectedAudioTrack({ + type: SelectedTrackType.INDEX, + value: itemValue, }); } }; const onSelectedTextTrackChange = (itemValue: string) => { console.log('on value change ' + itemValue); - setState({ - ...state, - selectedTextTrack: { - type: - textTracksSelectionBy === 'index' - ? SelectedTrackType.INDEX - : SelectedTrackType.LANGUAGE, - value: itemValue, - }, - }); + const type = + textTracksSelectionBy === 'index' + ? SelectedTrackType.INDEX + : SelectedTrackType.LANGUAGE; + setSelectedTextTrack({type, value: itemValue}); }; const onSelectedVideoTrackChange = (itemValue: string) => { console.log('on value change ' + itemValue); if (itemValue === undefined || itemValue === 'auto') { - setState({ - ...state, - selectedVideoTrack: { - type: SelectedVideoTrackType.AUTO, - }, + setSelectedVideoTrack({ + type: SelectedVideoTrackType.AUTO, }); } else { - setState({ - ...state, - selectedVideoTrack: { - type: SelectedVideoTrackType.INDEX, - value: itemValue, - }, + setSelectedVideoTrack({ + type: SelectedVideoTrackType.INDEX, + value: itemValue, }); } }; const videoSeek = (position: number) => { - setState({...state, isSeeking: true}); + setIsSeeking(true); // eslint-disable-next-line @typescript-eslint/ban-ts-comment // @ts-expect-error ref.current?.seek(position); @@ -142,33 +205,45 @@ const _Overlay = forwardRef((props, ref) => { const onRateSelected = (value: MultiValueControlPropType) => { if (typeof value === 'number') { - setState({...state, rate: value}); - } - }; - const onVolumeSelected = (value: MultiValueControlPropType) => { - if (typeof value === 'number') { - setState({...state, volume: value}); - } - }; - const onResizeModeSelected = (value: MultiValueControlPropType) => { - if (typeof value === 'object') { - setState({...state, resizeMode: value}); + setRate(value); } }; + const onVolumeSelected = (value: MultiValueControlPropType) => { + if (typeof value === 'number') { + setVolume(value); + } + }; + + const onResizeModeSelected = (value: MultiValueControlPropType) => { + if (typeof value === 'object') { + setResizeMode(value); + } + }; + + const toggleCache = () => setUseCache(!useCache); + + const togglePause = () => setPaused(!paused); + + const toggleRepeat = () => setRepeat(!repeat); + + const togglePoster = () => setPoster(poster ? undefined : samplePoster); + + const toggleMuted = () => setMuted(!muted); + return ( <> - + - {!state.showRNVControls ? ( + {!controls ? ( <> @@ -182,46 +257,35 @@ const _Overlay = forwardRef((props, ref) => { { - setState({...state, useCache: !state.useCache}); - }} + isSelected={useCache} + onPress={toggleCache} selectedText="enable cache" unselectedText="disable cache" /> ) : null} { - setState({...state, paused: !state.paused}); - }} + isSelected={paused} + onPress={togglePause} selectedText="pause" unselectedText="playing" /> { - setState({...state, loop: !state.loop}); - }} + isSelected={repeat} + onPress={toggleRepeat} selectedText="loop enable" unselectedText="loop disable" /> { - setState({ - ...state, - poster: state.poster ? undefined : samplePoster, - }); - }} + isSelected={!!poster} + onPress={togglePoster} selectedText="poster" unselectedText="no poster" /> ((props, ref) => { {/* shall be replaced by slider */} ((props, ref) => { ResizeMode.STRETCH, ]} onPress={onResizeModeSelected} - selected={state.resizeMode} + selected={resizeMode} /> { - setState({...state, muted: !state.muted}); - }} + isSelected={muted} + onPress={toggleMuted} text="muted" /> {isIos ? ( { // eslint-disable-next-line @typescript-eslint/ban-ts-comment // @ts-expect-error @@ -276,27 +338,27 @@ const _Overlay = forwardRef((props, ref) => { ) : null} videoSeek(prop)} - isUISeeking={state.isSeeking} + isUISeeking={isSeeking} /> diff --git a/examples/basic/src/constants/general.ts b/examples/basic/src/constants/general.ts index 3d9ccc35..02c39b90 100644 --- a/examples/basic/src/constants/general.ts +++ b/examples/basic/src/constants/general.ts @@ -2,11 +2,9 @@ import { BufferConfig, DRMType, ISO639_1, - ResizeMode, - SelectedVideoTrackType, TextTrackType, } from 'react-native-video'; -import {SampleVideoSource, StateType} from '../types'; +import {SampleVideoSource} from '../types'; import {localeVideo} from '../assets'; import {Platform} from 'react-native'; @@ -159,36 +157,6 @@ export const srcList: SampleVideoSource[] = srcAllPlatformList.concat( isAndroid ? srcAndroidList : srcIosList, ); -export const defaultValue: StateType = { - rate: 1, - volume: 1, - muted: false, - resizeMode: ResizeMode.CONTAIN, - duration: 0.0, - currentTime: 0.0, - videoWidth: 0, - videoHeight: 0, - paused: false, - fullscreen: true, - decoration: true, - isLoading: false, - audioTracks: [], - textTracks: [], - videoTracks: [], - selectedAudioTrack: undefined, - selectedTextTrack: undefined, - selectedVideoTrack: { - type: SelectedVideoTrackType.AUTO, - }, - srcListId: 0, - loop: false, - showRNVControls: false, - useCache: false, - poster: undefined, - showNotificationControls: false, - isSeeking: false, -}; - export const bufferConfig: BufferConfig = { minBufferMs: 15000, maxBufferMs: 50000, @@ -197,4 +165,4 @@ export const bufferConfig: BufferConfig = { live: { targetOffsetMs: 500, }, -} +}; diff --git a/examples/basic/src/types/types.ts b/examples/basic/src/types/types.ts index ab0e1ebf..7df4ead8 100644 --- a/examples/basic/src/types/types.ts +++ b/examples/basic/src/types/types.ts @@ -1,14 +1,4 @@ -import { - AudioTrack, - Drm, - ReactVideoSource, - ResizeMode, - SelectedTrack, - SelectedVideoTrack, - TextTrack, - TextTracks, - VideoTrack, -} from 'react-native-video'; +import {Drm, ReactVideoSource, TextTracks} from 'react-native-video'; export type AdditionalSourceInfo = { textTracks: TextTracks; @@ -19,31 +9,3 @@ export type AdditionalSourceInfo = { }; export type SampleVideoSource = ReactVideoSource | AdditionalSourceInfo; - -export interface StateType { - rate: number; - volume: number; - muted: boolean; - resizeMode: ResizeMode; - duration: number; - currentTime: number; - videoWidth: number; - videoHeight: number; - paused: boolean; - fullscreen: boolean; - decoration: boolean; - isLoading: boolean; - audioTracks: Array; - textTracks: Array; - videoTracks: Array; - selectedAudioTrack: SelectedTrack | undefined; - selectedTextTrack: SelectedTrack | undefined; - selectedVideoTrack: SelectedVideoTrack; - srcListId: number; - loop: boolean; - showRNVControls: boolean; - useCache: boolean; - poster?: string; - showNotificationControls: boolean; - isSeeking: boolean; -}