feat: add ability to define poster
props as Image type and render poster as custom component (#3972)
This commit is contained in:
113
src/Video.tsx
113
src/Video.tsx
@@ -8,7 +8,13 @@ import React, {
|
||||
} from 'react';
|
||||
import type {ElementRef} from 'react';
|
||||
import {View, StyleSheet, Image, Platform, processColor} from 'react-native';
|
||||
import type {StyleProp, ImageStyle, NativeSyntheticEvent} from 'react-native';
|
||||
import type {
|
||||
StyleProp,
|
||||
ImageStyle,
|
||||
NativeSyntheticEvent,
|
||||
ViewStyle,
|
||||
ImageResizeMode,
|
||||
} from 'react-native';
|
||||
|
||||
import NativeVideoComponent from './specs/VideoNativeComponent';
|
||||
import type {
|
||||
@@ -67,8 +73,9 @@ const Video = forwardRef<VideoRef, ReactVideoProps>(
|
||||
source,
|
||||
style,
|
||||
resizeMode,
|
||||
posterResizeMode,
|
||||
poster,
|
||||
posterResizeMode,
|
||||
renderLoader,
|
||||
drm,
|
||||
textTracks,
|
||||
selectedVideoTrack,
|
||||
@@ -113,25 +120,28 @@ const Video = forwardRef<VideoRef, ReactVideoProps>(
|
||||
ref,
|
||||
) => {
|
||||
const nativeRef = useRef<ElementRef<typeof NativeVideoComponent>>(null);
|
||||
const [showPoster, setShowPoster] = useState(!!poster);
|
||||
|
||||
const isPosterDeprecated = typeof poster === 'string';
|
||||
|
||||
const hasPoster = useMemo(() => {
|
||||
if (renderLoader) {
|
||||
return true;
|
||||
}
|
||||
|
||||
if (isPosterDeprecated) {
|
||||
return !!poster;
|
||||
}
|
||||
|
||||
return !!poster?.source;
|
||||
}, [isPosterDeprecated, poster, renderLoader]);
|
||||
|
||||
const [showPoster, setShowPoster] = useState(hasPoster);
|
||||
|
||||
const [
|
||||
_restoreUserInterfaceForPIPStopCompletionHandler,
|
||||
setRestoreUserInterfaceForPIPStopCompletionHandler,
|
||||
] = useState<boolean | undefined>();
|
||||
|
||||
const hasPoster = !!poster;
|
||||
|
||||
const posterStyle = useMemo<StyleProp<ImageStyle>>(
|
||||
() => ({
|
||||
...StyleSheet.absoluteFillObject,
|
||||
resizeMode:
|
||||
posterResizeMode && posterResizeMode !== 'none'
|
||||
? posterResizeMode
|
||||
: 'contain',
|
||||
}),
|
||||
[posterResizeMode],
|
||||
);
|
||||
|
||||
const src = useMemo<VideoSrc | undefined>(() => {
|
||||
if (!source) {
|
||||
return undefined;
|
||||
@@ -598,13 +608,78 @@ const Video = forwardRef<VideoRef, ReactVideoProps>(
|
||||
: ViewType.SURFACE;
|
||||
}, [drm, useSecureView, useTextureView, viewType]);
|
||||
|
||||
const _renderPoster = useCallback(() => {
|
||||
if (!hasPoster || !showPoster) {
|
||||
return null;
|
||||
}
|
||||
|
||||
// poster resize mode
|
||||
let _posterResizeMode: ImageResizeMode = 'contain';
|
||||
|
||||
if (!isPosterDeprecated && poster?.resizeMode) {
|
||||
_posterResizeMode = poster.resizeMode;
|
||||
} else if (posterResizeMode && posterResizeMode !== 'none') {
|
||||
_posterResizeMode = posterResizeMode;
|
||||
}
|
||||
|
||||
// poster style
|
||||
const baseStyle: StyleProp<ImageStyle> = {
|
||||
...StyleSheet.absoluteFillObject,
|
||||
resizeMode: _posterResizeMode,
|
||||
};
|
||||
|
||||
let posterStyle: StyleProp<ImageStyle> = baseStyle;
|
||||
|
||||
if (!isPosterDeprecated && poster?.style) {
|
||||
const styles = Array.isArray(poster.style)
|
||||
? poster.style
|
||||
: [poster.style];
|
||||
posterStyle = [baseStyle, ...styles];
|
||||
}
|
||||
|
||||
// render poster
|
||||
if (renderLoader && (poster || posterResizeMode)) {
|
||||
console.warn(
|
||||
'You provided both `renderLoader` and `poster` or `posterResizeMode` props. `renderLoader` will be used.',
|
||||
);
|
||||
}
|
||||
|
||||
// render loader
|
||||
if (renderLoader) {
|
||||
return <View style={StyleSheet.absoluteFill}>{renderLoader}</View>;
|
||||
}
|
||||
|
||||
return (
|
||||
<Image
|
||||
{...(isPosterDeprecated ? {} : poster)}
|
||||
source={isPosterDeprecated ? {uri: poster} : poster?.source}
|
||||
style={posterStyle}
|
||||
/>
|
||||
);
|
||||
}, [
|
||||
hasPoster,
|
||||
isPosterDeprecated,
|
||||
poster,
|
||||
posterResizeMode,
|
||||
renderLoader,
|
||||
showPoster,
|
||||
]);
|
||||
|
||||
const _style: StyleProp<ViewStyle> = useMemo(
|
||||
() => ({
|
||||
...StyleSheet.absoluteFillObject,
|
||||
...(showPoster ? {display: 'none'} : {}),
|
||||
}),
|
||||
[showPoster],
|
||||
);
|
||||
|
||||
return (
|
||||
<View style={style}>
|
||||
<NativeVideoComponent
|
||||
ref={nativeRef}
|
||||
{...rest}
|
||||
src={src}
|
||||
style={StyleSheet.absoluteFill}
|
||||
style={_style}
|
||||
resizeMode={resizeMode}
|
||||
restoreUserInterfaceForPIPStopCompletionHandler={
|
||||
_restoreUserInterfaceForPIPStopCompletionHandler
|
||||
@@ -679,9 +754,7 @@ const Video = forwardRef<VideoRef, ReactVideoProps>(
|
||||
}
|
||||
viewType={_viewType}
|
||||
/>
|
||||
{hasPoster && showPoster ? (
|
||||
<Image style={posterStyle} source={{uri: poster}} />
|
||||
) : null}
|
||||
{_renderPoster()}
|
||||
</View>
|
||||
);
|
||||
},
|
||||
|
@@ -1,6 +1,14 @@
|
||||
import type {ISO639_1} from './language';
|
||||
import type {ReactVideoEvents} from './events';
|
||||
import type {StyleProp, ViewProps, ViewStyle} from 'react-native';
|
||||
import type {
|
||||
ImageProps,
|
||||
StyleProp,
|
||||
ViewProps,
|
||||
ViewStyle,
|
||||
ImageRequireSource,
|
||||
ImageURISource,
|
||||
} from 'react-native';
|
||||
import type {ReactNode} from 'react';
|
||||
import type VideoResizeMode from './ResizeMode';
|
||||
import type FilterType from './FilterType';
|
||||
import type ViewType from './ViewType';
|
||||
@@ -34,6 +42,13 @@ export type ReactVideoSource = Readonly<
|
||||
}
|
||||
>;
|
||||
|
||||
export type ReactVideoPosterSource = ImageURISource | ImageRequireSource;
|
||||
|
||||
export type ReactVideoPoster = Omit<ImageProps, 'source'> & {
|
||||
// prevents giving source in the array
|
||||
source?: ReactVideoPosterSource;
|
||||
};
|
||||
|
||||
export type VideoMetadata = Readonly<{
|
||||
title?: string;
|
||||
subtitle?: string;
|
||||
@@ -243,12 +258,14 @@ export interface ReactVideoProps extends ReactVideoEvents, ViewProps {
|
||||
pictureInPicture?: boolean; // iOS
|
||||
playInBackground?: boolean;
|
||||
playWhenInactive?: boolean; // iOS
|
||||
poster?: string;
|
||||
poster?: string | ReactVideoPoster; // string is deprecated
|
||||
/** @deprecated use **resizeMode** key in **poster** props instead */
|
||||
posterResizeMode?: EnumValues<PosterResizeModeType>;
|
||||
preferredForwardBufferDuration?: number; // iOS
|
||||
preventsDisplaySleepDuringVideoPlayback?: boolean;
|
||||
progressUpdateInterval?: number;
|
||||
rate?: number;
|
||||
renderLoader?: ReactNode;
|
||||
repeat?: boolean;
|
||||
reportBandwidth?: boolean; //Android
|
||||
resizeMode?: EnumValues<VideoResizeMode>;
|
||||
|
Reference in New Issue
Block a user