MM-31202 | MM-30866 fix: Video playback button & hide header footer on playback (#5014) (#5027)

* MM-31202 | MM-30866 fix: Video playback button & hide header footer on playback

* Feedback review

(cherry picked from commit 9abc89129b)

Co-authored-by: Elias Nahum <nahumhbl@gmail.com>
This commit is contained in:
Mattermost Build 2020-12-10 23:06:55 +01:00 committed by GitHub
parent 20496f5a4f
commit 29a12c152a
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
6 changed files with 62 additions and 48 deletions

View file

@ -129,6 +129,7 @@ const Footer = forwardRef<FooterRef, FooterProps>((props: FooterProps, ref) => {
useImperativeHandle(ref, () => ({
isVisible,
setVisible,
toggle,
}), [visible]);

View file

@ -128,8 +128,15 @@ export default class Gallery extends PureComponent {
this.setState({index}, this.initHeader);
};
handleTapped = () => {
const visible = this.footer.current?.getWrappedInstance()?.toggle();
handleTapped = (display) => {
let visible;
if (display === undefined) {
visible = this.footer.current?.getWrappedInstance()?.toggle();
} else {
visible = display;
this.footer.current?.getWrappedInstance()?.setVisible(display);
}
const options = {
topBar: {
background: {

View file

@ -16,7 +16,7 @@ import {GalleryItemProps} from 'types/screens/gallery';
import VideoControls, {VideoControlsRef} from './video_controls';
const GalleryVideo = ({file, deviceHeight, deviceWidth, intl, isActive, onDoubleTap, theme}: GalleryItemProps) => {
const GalleryVideo = ({file, deviceHeight, deviceWidth, intl, isActive, showHideHeaderFooter, theme}: GalleryItemProps) => {
const statusBar = DeviceTypes.IS_IPHONE_WITH_INSETS ? 0 : 20;
const width = deviceWidth;
const height = deviceHeight - statusBar;
@ -25,16 +25,6 @@ const GalleryVideo = ({file, deviceHeight, deviceWidth, intl, isActive, onDouble
const [paused, setPaused] = useState(true);
const videoRef = useRef<Video>(null);
const controlsRef = useRef<VideoControlsRef>(null);
const doubleTapRef = useRef<TapGestureHandler>(null);
const doubleTap = (e: TapGestureHandlerStateChangeEvent) => {
if (e.nativeEvent.state === State.ACTIVE) {
if (onDoubleTap) {
onDoubleTap();
}
}
};
const onPlayPause = () => {
setPaused(!paused);
};
@ -120,30 +110,21 @@ const GalleryVideo = ({file, deviceHeight, deviceWidth, intl, isActive, onDouble
<TapGestureHandler
numberOfTaps={1}
onHandlerStateChange={singleTap}
waitFor={doubleTapRef}
>
<View>
<TapGestureHandler
ref={doubleTapRef}
numberOfTaps={2}
onHandlerStateChange={doubleTap}
>
<View>
<Video
ref={videoRef}
style={{width, height}}
resizeMode='contain'
source={{uri}}
volume={1.0}
paused={paused}
controls={false}
onEnd={videoEnded}
onLoad={videoLoaded}
onProgress={videoProgress}
onError={videoError}
/>
</View>
</TapGestureHandler>
<Video
ref={videoRef}
style={{width, height}}
resizeMode='contain'
source={{uri}}
volume={1.0}
paused={paused}
controls={false}
onEnd={videoEnded}
onLoad={videoLoaded}
onProgress={videoProgress}
onError={videoError}
/>
</View>
</TapGestureHandler>
<VideoControls
@ -153,6 +134,7 @@ const GalleryVideo = ({file, deviceHeight, deviceWidth, intl, isActive, onDouble
paused={paused}
onPlayPause={onPlayPause}
onSeek={onSeek}
showHideHeaderFooter={showHideHeaderFooter}
/>
</>
);

View file

@ -18,8 +18,12 @@ import GalleryImage from './gallery_image';
import GalleryVideo from './gallery_video';
const itemTopStyle = (props: GalleryProps): number => {
if (Platform.OS === 'android' && props.footerVisible) {
return props.isLandscape ? -64 : -99;
if (Platform.OS === 'android') {
if (props.footerVisible) {
return props.isLandscape ? -64 : -99;
}
return props.isLandscape ? -6 : -41;
}
return 0;
@ -161,7 +165,7 @@ const GalleryViewer = (props: GalleryProps) => {
>
<GalleryVideo
isActive={currentIndex === i}
onDoubleTap={props.onTap}
showHideHeaderFooter={props.onTap}
theme={props.theme}
{...itemProps}
/>

View file

@ -21,6 +21,7 @@ interface VideoControlsProps {
paused: boolean;
onPlayPause(): void;
onSeek(value: number): void;
showHideHeaderFooter?(display: boolean): void;
}
export interface VideoControlsRef {
@ -97,6 +98,8 @@ const humanizeVideoDuration = (seconds: number) => {
return date.toISOString().substr(begin, end);
};
let animation: Animated.CompositeAnimation;
const VideoControls = forwardRef<VideoControlsRef, VideoControlsProps>((props: VideoControlsProps, ref) => {
const opacity = useRef(new Animated.Value(0)).current;
const [duration, setDuration] = useState(0);
@ -105,12 +108,21 @@ const VideoControls = forwardRef<VideoControlsRef, VideoControlsProps>((props: V
const styles = getStyles(props.isLandscape);
const fadeControls = (toValue: number, delay = 0, callback?: CallbackFunctionWithoutArguments) => {
Animated.timing(opacity, {
if (animation) {
animation.stop();
}
animation = Animated.timing(opacity, {
toValue,
duration: 250,
delay,
useNativeDriver: true,
}).start(callback);
});
animation.start((result: Animated.EndResult) => {
if (callback && result.finished) {
callback();
}
});
};
useEffect(() => {
@ -123,12 +135,18 @@ const VideoControls = forwardRef<VideoControlsRef, VideoControlsProps>((props: V
videoProgress,
}), []);
const showControls = (playing: boolean) => {
setVisible(true);
const display = (show: boolean) => {
setVisible(show);
if (props.showHideHeaderFooter) {
props.showHideHeaderFooter(show);
}
};
const showControls = (playing: boolean) => {
display(true);
if (playing) {
fadeControls(1, 0, () => {
fadeControls(0, 1000, () => setVisible(false));
fadeControls(0, 1000, () => display(false));
});
} else {
fadeControls(1, 0);
@ -137,9 +155,9 @@ const VideoControls = forwardRef<VideoControlsRef, VideoControlsProps>((props: V
const playPause = () => {
if (props.paused) {
fadeControls(0, 250, () => setVisible(false));
fadeControls(0, 250, () => display(false));
} else {
fadeControls(1, 250, () => setVisible(true));
fadeControls(1, 250, () => display(true));
}
props.onPlayPause();
@ -149,7 +167,7 @@ const VideoControls = forwardRef<VideoControlsRef, VideoControlsProps>((props: V
props.onSeek(value);
setProgress(value);
if (!props.paused) {
fadeControls(0, 1000, () => setVisible(false));
fadeControls(0, 1000, () => display(false));
}
};
@ -159,7 +177,7 @@ const VideoControls = forwardRef<VideoControlsRef, VideoControlsProps>((props: V
const seekStart = () => {
opacity.stopAnimation();
setVisible(true);
display(true);
};
const videoDuration = (value: number) => {
@ -195,6 +213,7 @@ const VideoControls = forwardRef<VideoControlsRef, VideoControlsProps>((props: V
<SafeAreaView
edges={['left', 'right']}
mode='margin'
pointerEvents='box-none'
style={styles.progressColumnContainer}
>
<View style={[styles.controlsRow, styles.progressContainer]}>

View file

@ -55,6 +55,7 @@ export interface FooterProps {
export interface FooterRef {
toggle(): boolean;
isVisible(): boolean;
setVisible(visible: boolean): void;
}
export interface GalleryProps {
@ -76,7 +77,7 @@ export interface GalleryItemProps {
deviceWidth: number;
intl?: typeof intlShape;
isActive?: boolean;
onDoubleTap?: CallbackFunctionWithoutArguments;
showHideHeaderFooter?(display: boolean): void;
style?: StyleProp<Animated.AnimateStyle>;
theme?: Theme;
}