diff --git a/packages/media-console/README.md b/packages/media-console/README.md
index 95318fc..9914d11 100644
--- a/packages/media-console/README.md
+++ b/packages/media-console/README.md
@@ -93,27 +93,27 @@ the `` and it will pass them through to the `` component
In addition, the `` also takes these props:
-| Prop | Type | Default | Description |
-|------------------------------|-----------------------------------------------|------------------------------------------|------------------------------------------------------------------------------------------------------------------------------------------------------------------------|
-| alwaysShowControls | Boolean | false | Always show controls. |
-| controlAnimationTiming | Integer | 500 | The amount of time (in milliseconds) to animate the controls in and out. |
-| controlTimeoutDelay | Integer | 15000 | Hide controls after X amount of time in milliseconds | |
-| doubleTapTime | Integer | 130 | Tapping twice within this amount of time in milliseconds is considered a double tap. Single taps will not be actioned until this time has expired. |
-| isFullscreen | Boolean | false | The VideoPlayer fullscreen state |
-| navigator | Navigator | null | When using the default React Native navigator and do not override the `onBack` function, you'll need to pass the navigator to the VideoPlayer for it to function |
-| rewindTime | Integer | 15 | Number of seconds to rewind or forward. |
-| seekColor | String(#HEX) | '#FFF' | Fill/handle colour of the seekbar |
-| showDuration | Boolean | false | Show duration of the media. |
-| showOnStart | Boolean | false | Show or hide the controls on first render |
-| showOnEnd | Boolean | false | Show or hide the controls on end of video |
-| showTimeRemaining | Boolean | false | If true, show the time remaing, else show the current time in the Player. |
-| showHours | Boolean | false | If true, convert time to hours in the Player |
-| tapAnywhereToPause | Boolean | false | If true, single tapping anywhere on the video (other than a control) toggles between playing and paused. |
-| toggleResizeModeOnFullscreen | Boolean | false | If true, clicking the fullscreen button will toggle the `` component between cover/contain, set to false if you want to customize fullscreen behaviour |
-| containerStyle | ViewStyle | | StyleSheet passed to the container of the component |
-| videoStyle | ViewStyle | | StyleSheet passed to the component |
-| videoRef | Video | undefined | Pass ref to the `` component |
-| pan | `{ horizontal: Boolean, inverted: Boolean } ` | `{ horizontal: true, inverted: false } ` | An object allowing fine grained control over the `PanResponder` controlling the volume and seek. Use this if you need to apply rotation transformations to the player. |
+| Prop | Type | Default | Description |
+|------------------------------|---------------------------------------------------------------|------------------------------------------|-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------|
+| alwaysShowControls | Boolean | false | Always show controls. |
+| controlAnimationTiming | Integer | 500 | The amount of time (in milliseconds) to animate the controls in and out. |
+| controlTimeoutDelay | Integer | 15000 | Hide controls after X amount of time in milliseconds | |
+| doubleTapTime | Integer | 130 | Tapping twice within this amount of time in milliseconds is considered a double tap. Single taps will not be actioned until this time has expired. |
+| isFullscreen | Boolean | false | The VideoPlayer fullscreen state |
+| navigator | Navigator | null | When using the default React Native navigator and do not override the `onBack` function, you'll need to pass the navigator to the VideoPlayer for it to function |
+| rewindTime | Integer | 15 | Number of seconds to rewind or forward. |
+| seekColor | String(#HEX) | '#FFF' | Fill/handle colour of the seekbar |
+| showDuration | Boolean | false | Show duration of the media. |
+| showOnStart | Boolean | false | Show or hide the controls on first render |
+| showOnEnd | Boolean | false | Show or hide the controls on end of video |
+| showTimeRemaining | Boolean | false | If true, show the time remaing, else show the current time in the Player. |
+| showHours | Boolean | false | If true, convert time to hours in the Player |
+| tapAnywhereToPause | Boolean | false | If true, single tapping anywhere on the video (other than a control) toggles between playing and paused. |
+| toggleResizeModeOnFullscreen | Boolean | false | If true, clicking the fullscreen button will toggle the `` component between cover/contain, set to false if you want to customize fullscreen behaviour |
+| containerStyle | ViewStyle | | StyleSheet passed to the container of the component |
+| videoStyle | ViewStyle | | StyleSheet passed to the component |
+| videoRef | Video | undefined | Pass ref to the `` component |
+| pan | `{ horizontal: Boolean, inverted: Boolean, parentList: {} } ` | `{ horizontal: true, inverted: false } ` | An object allowing fine grained control over the `PanResponder` controlling the volume and seek. Use this if you need to apply rotation transformations, or to use the player inside a FlatList/ScrollView. |
### Events
diff --git a/packages/media-console/src/VideoPlayer.tsx b/packages/media-console/src/VideoPlayer.tsx
index 9ee82de..14575c5 100644
--- a/packages/media-console/src/VideoPlayer.tsx
+++ b/packages/media-console/src/VideoPlayer.tsx
@@ -74,7 +74,7 @@ const AnimatedVideoPlayer = (
disableOverlay,
navigator,
rewindTime = 15,
- pan: {horizontal: horizontalPan, inverted: invertedPan} = {},
+ pan,
testID,
} = props;
@@ -291,8 +291,7 @@ const AnimatedVideoPlayer = (
setSeeking,
setControlTimeout,
onEnd: events.onEnd,
- horizontal: horizontalPan,
- inverted: invertedPan,
+ pan,
});
useEffect(() => {
diff --git a/packages/media-console/src/hooks/usePanResponders.tsx b/packages/media-console/src/hooks/usePanResponders.tsx
index e9618d3..2e502ee 100644
--- a/packages/media-console/src/hooks/usePanResponders.tsx
+++ b/packages/media-console/src/hooks/usePanResponders.tsx
@@ -1,7 +1,8 @@
import {Dispatch, SetStateAction, useEffect} from 'react';
import {PanResponder} from 'react-native';
+import {VideoPlayerProps} from '../types';
-interface PanRespondersProps {
+interface PanRespondersProps extends Pick {
duration: number;
seekerOffset: number;
volumeOffset: number;
@@ -16,8 +17,6 @@ interface PanRespondersProps {
setSeeking: Dispatch>;
setControlTimeout: () => void;
onEnd: () => void;
- horizontal?: boolean;
- inverted?: boolean;
}
export const usePanResponders = ({
@@ -35,14 +34,31 @@ export const usePanResponders = ({
setSeeking,
setControlTimeout,
onEnd,
- horizontal = true,
- inverted = false,
+ pan: {horizontal = true, inverted = false, parentList} = {},
}: PanRespondersProps) => {
+ const {ref, scrollEnabled = true} = parentList || {};
+
+ const enableParentScroll = () =>
+ ref?.current?.setNativeProps({scrollEnabled});
+
+ const disableParentScroll = () =>
+ ref?.current?.setNativeProps({scrollEnabled: false});
+
+ /**
+ * Options to make the video player work seamlessly within FlatLists or ScrollViews.
+ * @link https://github.com/LunatiqueCoder/react-native-media-console/issues/104
+ */
+ const parentScrollPanOptions = {
+ onPanResponderEnd: enableParentScroll,
+ onPanResponderTerminationRequest: () => false, // https://stackoverflow.com/a/76875305/14056591
+ };
+
const volumePanResponder = PanResponder.create({
onStartShouldSetPanResponder: () => true,
onMoveShouldSetPanResponder: () => true,
onPanResponderGrant: () => {
clearControlTimeout();
+ disableParentScroll();
},
onPanResponderMove: (_evt, gestureState) => {
const diff = horizontal ? gestureState.dx : gestureState.dy;
@@ -52,6 +68,7 @@ export const usePanResponders = ({
onPanResponderRelease: () => {
setControlTimeout();
},
+ ...parentScrollPanOptions,
});
const seekPanResponder = PanResponder.create({
@@ -62,6 +79,7 @@ export const usePanResponders = ({
clearControlTimeout();
const position = evt.nativeEvent.locationX;
setSeekerPosition(position);
+ disableParentScroll();
},
onPanResponderMove: (_evt, gestureState) => {
const diff = horizontal ? gestureState.dx : gestureState.dy;
@@ -74,14 +92,13 @@ export const usePanResponders = ({
const time = duration * percent;
if (time >= duration && !loading) {
- if (typeof onEnd === 'function') {
- onEnd();
- }
+ onEnd?.();
}
setSeeking(false);
seek && seek(time);
},
+ ...parentScrollPanOptions,
});
useEffect(() => {
diff --git a/packages/media-console/src/types.ts b/packages/media-console/src/types.ts
index 87c401b..f05a85b 100644
--- a/packages/media-console/src/types.ts
+++ b/packages/media-console/src/types.ts
@@ -1,5 +1,11 @@
import type {RefObject} from 'react';
-import type {ViewStyle, StyleProp, Animated} from 'react-native';
+import {
+ ViewStyle,
+ StyleProp,
+ Animated,
+ FlatList,
+ ScrollView,
+} from 'react-native';
import type Reanimated from 'react-native-reanimated';
import type {StyleProps} from 'react-native-reanimated';
import type {VideoRef, ReactVideoProps} from 'react-native-video';
@@ -245,7 +251,7 @@ export interface VideoPlayerProps extends ReactVideoProps {
rewindTime?: number;
/**
- * Object allowing fine grained control of the pan responder
+ * Object allowing fine-grained control of the pan responder
*
* @default { horizontal: true, inverted: false }
*/
@@ -256,13 +262,30 @@ export interface VideoPlayerProps extends ReactVideoProps {
* @default true
*/
horizontal?: boolean;
-
/**
* Boolean representing if the player controls pan gesture should be inverted
*
* @default false
*/
inverted?: boolean;
+ /**
+ * Options to make the video player work seamlessly within FlatLists or ScrollViews.
+ *
+ * @link https://github.com/LunatiqueCoder/react-native-media-console/issues/104
+ */
+ parentList?: {
+ /**
+ * Internally, `ref?.current?.setNativeProps({scrollEnabled: boolean});` is used in order
+ * to make the video player work seamlessly within FlatLists or ScrollViews:
+ * @link https://github.com/LunatiqueCoder/react-native-media-console/issues/104
+ */
+ ref: RefObject;
+ /**
+ * Used to keep the state consistency when using `ref?.current?.setNativeProps({scrollEnabled: boolean});`.
+ * @see ref
+ */
+ scrollEnabled?: boolean;
+ };
};
/**
* testID selector for testing