Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

fix: Use custom controls for iOS video (#7729) #7737

Merged
merged 3 commits into from
Nov 10, 2023
Merged
Changes from 2 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
149 changes: 126 additions & 23 deletions app/components/Views/MediaPlayer/index.js
Original file line number Diff line number Diff line change
@@ -1,32 +1,102 @@
import React, { useState } from 'react';
import React, { useRef, useState } from 'react';
import PropTypes from 'prop-types';
import { StyleSheet, View, ViewPropTypes } from 'react-native';
import {
StyleSheet,
TouchableOpacity,
View,
ViewPropTypes,
} from 'react-native';
import AndroidMediaPlayer from './AndroidMediaPlayer';
import Video from 'react-native-video';
import Device from '../../../util/device';
import Loader from './Loader';
import Ionicons from 'react-native-vector-icons/Ionicons';
import { TapGestureHandler } from 'react-native-gesture-handler';
import Animated, {
useAnimatedStyle,
useSharedValue,
withDelay,
withSequence,
withTiming,
} from 'react-native-reanimated';
import { useStyles } from '../../../component-library/hooks';

const styles = StyleSheet.create({
loaderContainer: {
position: 'absolute',
zIndex: 999,
width: '100%',
height: '100%',
},
});
const styleSheet = ({ theme: { colors }, vars: { isPlaying } }) =>
StyleSheet.create({
loaderContainer: {
position: 'absolute',
zIndex: 999,
width: '100%',
height: '100%',
},
playButtonCircle: {
backgroundColor: colors.overlay.default,
height: 44,
width: 44,
borderRadius: 22,
justifyContent: 'center',
alignItems: 'center',
},
videoControlsStyle: {
...StyleSheet.absoluteFillObject,
justifyContent: 'center',
alignItems: 'center',
},
playIcon: { left: isPlaying ? 2 : 0 },
volumeButtonCircle: {
backgroundColor: colors.overlay.default,
position: 'absolute',
right: 16,
top: 36,
height: 36,
width: 36,
borderRadius: 18,
justifyContent: 'center',
alignItems: 'center',
},
});

function MediaPlayer({ uri, style, onClose, textTracks, selectedTextTrack }) {
const [loading, setLoading] = useState(true);
const [error, setError] = useState(false);
const videoRef = useRef();
const [isPlaying, setIsPlaying] = useState(true);
const [isMuted, setIsMuted] = useState(true);
const videoControlsOpacity = useSharedValue(0);
const {
styles,
theme: { colors },
} = useStyles(styleSheet, { isPlaying });

const onLoad = () => setLoading(false);
const onLoad = () => {
setLoading(false);
setIsPlaying(true);
};

const onError = () => setError(true);
const onError = () => {
setError(true);
setIsPlaying(false);
};

// Video source can be either a number returned by import for bundled files
// or an object of the form { uri: 'http://...' } for remote files
const source = Number.isInteger(uri) ? uri : { uri };

const videoControlsStyle = useAnimatedStyle(() => ({
...styles.videoControlsStyle,
opacity: videoControlsOpacity.value,
}));

const onPressVideoControls = () => {
videoControlsOpacity.value = withSequence(
withTiming(1),
withDelay(500, withTiming(0)),
);
setIsPlaying(!isPlaying);
};

const onPressVolumeControls = () => setIsMuted(!isMuted);

return (
<View style={style}>
{loading && (
Expand All @@ -44,17 +114,50 @@ function MediaPlayer({ uri, style, onClose, textTracks, selectedTextTrack }) {
selectedTextTrack={selectedTextTrack}
/>
) : (
<Video
onLoad={onLoad}
onError={onError}
style={style}
muted
source={source}
controls
textTracks={textTracks}
selectedTextTrack={selectedTextTrack}
ignoreSilentSwitch="ignore"
/>
<>
<Video
onLoad={onLoad}
onError={onError}
style={style}
muted={isMuted}
paused={!isPlaying}
source={source}
controls={false}
fullscreen={false}
textTracks={textTracks}
selectedTextTrack={selectedTextTrack}
ignoreSilentSwitch="ignore"
ref={videoRef}
/>
{/**
* Use custom controls for iOS since iOS 17.2+ begins crashing. https://github.com/react-native-video/react-native-video/issues/3329
*/}
<TapGestureHandler onEnded={onPressVideoControls}>
<Animated.View style={videoControlsStyle}>
<View style={styles.playButtonCircle}>
<Ionicons
name={isPlaying ? 'ios-play' : 'ios-pause'}
size={24}
color={colors.overlay.inverse}
style={styles.playIcon}
/>
</View>
</Animated.View>
</TapGestureHandler>
{isPlaying ? (
<TouchableOpacity
activeOpacity={1}
tommasini marked this conversation as resolved.
Show resolved Hide resolved
style={styles.volumeButtonCircle}
onPress={onPressVolumeControls}
>
<Ionicons
name={isMuted ? 'ios-volume-off' : 'ios-volume-mute'}
size={isMuted ? 22 : 28}
color={colors.overlay.inverse}
/>
</TouchableOpacity>
) : null}
</>
)}
</View>
);
Expand Down
Loading