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

[TS migration] Migrate VideoPlayerPreview, VideoPlayerControls and VideoPopoverMenu component files to TypeScript #37425

Merged
Merged
Show file tree
Hide file tree
Changes from 22 commits
Commits
Show all changes
27 commits
Select commit Hold shift + click to select a range
7ef3d28
VideoPlayerThumbnail migreated to ts
smelaa Feb 27, 2024
1a70990
Migration of VideoPlayerPreview
smelaa Feb 28, 2024
aed45f8
Addressing review comments
smelaa Feb 28, 2024
0afcaae
Merge branch 'ts-migration-videoplayercontext' into ts-migration-vide…
smelaa Feb 28, 2024
d176eaf
VideoPopovermenu migrated to ts
smelaa Feb 28, 2024
03eae41
ProgressBar migrated to ts
smelaa Feb 29, 2024
2dbfe11
Merge branch 'ts-migration-videoplayercontext' into ts-migration-vide…
smelaa Feb 29, 2024
07947cd
VolumeButton migrated to ts
smelaa Feb 29, 2024
1c8b789
VideoPlayerControls migrated to ts
smelaa Feb 29, 2024
d2567a9
Changing types in VideoPlayerControls to match IconButton
smelaa Feb 29, 2024
528387f
Merging with main
smelaa Mar 1, 2024
361d4a0
Merge branch 'ts-migration-videoplayercontext' into ts-migration-vide…
smelaa Mar 1, 2024
2150628
Wrapping style in StyleProp component
smelaa Mar 1, 2024
4d84ed3
Migrate CategoryPicker to typescript
mateuuszzzzz Feb 12, 2024
b419a09
Merge branch 'ts-migration-videoplayercontext' into ts-migration-vide…
smelaa Mar 6, 2024
c768f5d
Address part of the review
smelaa Mar 13, 2024
6a50480
Address review comments
smelaa Mar 13, 2024
d2258f4
Solve typescript errors
smelaa Mar 13, 2024
fbef252
Merge branch 'main' into ts-migration-videoplayerpreview
smelaa Mar 14, 2024
db92a62
Add anchorref to VideoPopoverMenu
smelaa Mar 14, 2024
bea6377
Adress review comments
smelaa Mar 15, 2024
1a5c507
Address review comments.
smelaa Mar 15, 2024
410ef9f
Remove default value of props which are always set by a caller
smelaa Mar 18, 2024
7edb283
Merge branch 'main' into ts-migration-videoplayerpreview
smelaa Mar 18, 2024
607070c
Merge branch 'main' into ts-migration-videoplayerpreview
smelaa Mar 20, 2024
83a7bfd
Merge branch 'main' into ts-migration-videoplayerpreview
smelaa Mar 20, 2024
b0a2e69
Address review comments
smelaa Mar 20, 2024
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
Original file line number Diff line number Diff line change
@@ -1,44 +1,46 @@
import PropTypes from 'prop-types';
import React, {useEffect, useState} from 'react';
import type {LayoutChangeEvent, ViewStyle} from 'react-native';
import type {GestureStateChangeEvent, GestureUpdateEvent, PanGestureChangeEventPayload, PanGestureHandlerEventPayload} from 'react-native-gesture-handler';
import {Gesture, GestureDetector} from 'react-native-gesture-handler';
import Animated, {runOnJS, useAnimatedStyle, useSharedValue} from 'react-native-reanimated';
import {usePlaybackContext} from '@components/VideoPlayerContexts/PlaybackContext';
import useThemeStyles from '@hooks/useThemeStyles';

const propTypes = {
duration: PropTypes.number.isRequired,
type ProgressBarProps = {
/** Total duration of a video. */
smelaa marked this conversation as resolved.
Show resolved Hide resolved
duration: number;

position: PropTypes.number.isRequired,
/** Position of progress pointer on the bar. */
position: number;

seekPosition: PropTypes.func.isRequired,
/** Function to seek to a specific position in the video. */
seekPosition: (newPosition: number) => void;
};

const defaultProps = {};

function getProgress(currentPosition, maxPosition) {
function getProgress(currentPosition: number, maxPosition: number): number {
return Math.min(Math.max((currentPosition / maxPosition) * 100, 0), 100);
}

function ProgressBar({duration, position, seekPosition}) {
function ProgressBar({duration, position, seekPosition}: ProgressBarProps) {
const styles = useThemeStyles();
const {pauseVideo, playVideo, checkVideoPlaying} = usePlaybackContext();
const [sliderWidth, setSliderWidth] = useState(1);
const [isSliderPressed, setIsSliderPressed] = useState(false);
const progressWidth = useSharedValue(0);
const wasVideoPlayingOnCheck = useSharedValue(false);

const onCheckVideoPlaying = (isPlaying) => {
const onCheckVideoPlaying = (isPlaying: boolean) => {
wasVideoPlayingOnCheck.value = isPlaying;
};

const progressBarInteraction = (event) => {
const progressBarInteraction = (event: GestureUpdateEvent<PanGestureHandlerEventPayload & PanGestureChangeEventPayload> | GestureStateChangeEvent<PanGestureHandlerEventPayload>) => {
const progress = getProgress(event.x, sliderWidth);
progressWidth.value = progress;
runOnJS(seekPosition)((progress * duration) / 100);
};

const onSliderLayout = (e) => {
setSliderWidth(e.nativeEvent.layout.width);
const onSliderLayout = (event: LayoutChangeEvent) => {
setSliderWidth(event.nativeEvent.layout.width);
};

const pan = Gesture.Pan()
Expand Down Expand Up @@ -66,7 +68,7 @@ function ProgressBar({duration, position, seekPosition}) {
progressWidth.value = getProgress(position, duration);
}, [duration, isSliderPressed, position, progressWidth]);

const progressBarStyle = useAnimatedStyle(() => ({width: `${progressWidth.value}%`}));
const progressBarStyle: ViewStyle = useAnimatedStyle(() => ({width: `${progressWidth.value}%`}));

return (
<GestureDetector gesture={pan}>
Expand All @@ -85,8 +87,6 @@ function ProgressBar({duration, position, seekPosition}) {
);
}

ProgressBar.propTypes = propTypes;
ProgressBar.defaultProps = defaultProps;
ProgressBar.displayName = 'ProgressBar';

export default ProgressBar;
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
import PropTypes from 'prop-types';
import React, {memo, useCallback, useState} from 'react';
import type {LayoutChangeEvent, StyleProp, ViewStyle} from 'react-native';
import {View} from 'react-native';
import type {GestureStateChangeEvent, GestureUpdateEvent, PanGestureChangeEventPayload, PanGestureHandlerEventPayload} from 'react-native-gesture-handler';
import {Gesture, GestureDetector} from 'react-native-gesture-handler';
import Animated, {runOnJS, useAnimatedStyle, useDerivedValue} from 'react-native-reanimated';
import Hoverable from '@components/Hoverable';
Expand All @@ -10,18 +11,16 @@ import {useVolumeContext} from '@components/VideoPlayerContexts/VolumeContext';
import useLocalize from '@hooks/useLocalize';
import useThemeStyles from '@hooks/useThemeStyles';
import * as NumberUtils from '@libs/NumberUtils';
import stylePropTypes from '@styles/stylePropTypes';

const propTypes = {
style: stylePropTypes.isRequired,
small: PropTypes.bool,
};
type VolumeButtonProps = {
/** Style for the volume button. */
smelaa marked this conversation as resolved.
Show resolved Hide resolved
style?: StyleProp<ViewStyle>;

const defaultProps = {
small: false,
/** Is button icon small. */
small?: boolean;
};

const getVolumeIcon = (volume) => {
const getVolumeIcon = (volume: number) => {
if (volume === 0) {
return Expensicons.Mute;
}
Expand All @@ -31,20 +30,20 @@ const getVolumeIcon = (volume) => {
return Expensicons.VolumeHigh;
};

function VolumeButton({style, small}) {
function VolumeButton({style, small = false}: VolumeButtonProps) {
const styles = useThemeStyles();
const {translate} = useLocalize();
const {updateVolume, volume} = useVolumeContext();
const [sliderHeight, setSliderHeight] = useState(1);
const [volumeIcon, setVolumeIcon] = useState({icon: getVolumeIcon(volume.value)});
const [isSliderBeingUsed, setIsSliderBeingUsed] = useState(false);

const onSliderLayout = useCallback((e) => {
setSliderHeight(e.nativeEvent.layout.height);
const onSliderLayout = useCallback((event: LayoutChangeEvent) => {
setSliderHeight(event.nativeEvent.layout.height);
}, []);

const changeVolumeOnPan = useCallback(
(event) => {
(event: GestureStateChangeEvent<PanGestureHandlerEventPayload> | GestureUpdateEvent<PanGestureHandlerEventPayload & PanGestureChangeEventPayload>) => {
const val = NumberUtils.roundToTwoDecimalPlaces(1 - event.y / sliderHeight);
volume.value = NumberUtils.clamp(val, 0, 1);
},
Expand All @@ -65,7 +64,7 @@ function VolumeButton({style, small}) {

const progressBarStyle = useAnimatedStyle(() => ({height: `${volume.value * 100}%`}));

const updateIcon = useCallback((vol) => {
const updateIcon = useCallback((vol: number) => {
setVolumeIcon({icon: getVolumeIcon(vol)});
}, []);

Expand Down Expand Up @@ -98,7 +97,6 @@ function VolumeButton({style, small}) {
tooltipText={volume.value === 0 ? translate('videoPlayer.unmute') : translate('videoPlayer.mute')}
onPress={() => updateVolume(volume.value === 0 ? 1 : 0)}
src={volumeIcon.icon}
fill={styles.white}
small={small}
smelaa marked this conversation as resolved.
Show resolved Hide resolved
shouldForceRenderingTooltipBelow
/>
Expand All @@ -108,8 +106,6 @@ function VolumeButton({style, small}) {
);
}

VolumeButton.propTypes = propTypes;
VolumeButton.defaultProps = defaultProps;
VolumeButton.displayName = 'VolumeButton';

export default memo(VolumeButton);
Original file line number Diff line number Diff line change
@@ -1,55 +1,58 @@
import PropTypes from 'prop-types';
import type {Video} from 'expo-av';
import type {MutableRefObject} from 'react';
import React, {useCallback, useMemo, useState} from 'react';
import type {GestureResponderEvent, LayoutChangeEvent, StyleProp, ViewStyle} from 'react-native';
import {View} from 'react-native';
import Animated from 'react-native-reanimated';
import * as Expensicons from '@components/Icon/Expensicons';
import refPropTypes from '@components/refPropTypes';
import Text from '@components/Text';
import IconButton from '@components/VideoPlayer/IconButton';
import convertMillisecondsToTime from '@components/VideoPlayer/utils';
import {usePlaybackContext} from '@components/VideoPlayerContexts/PlaybackContext';
import useLocalize from '@hooks/useLocalize';
import useThemeStyles from '@hooks/useThemeStyles';
import stylePropTypes from '@styles/stylePropTypes';
import CONST from '@src/CONST';
import ProgressBar from './ProgressBar';
import VolumeButton from './VolumeButton';

const propTypes = {
duration: PropTypes.number.isRequired,
type VideoPlayerControlsProps = {
/** Duration of a video. */
smelaa marked this conversation as resolved.
Show resolved Hide resolved
duration: number;

position: PropTypes.number.isRequired,
/** Position of progress pointer. */
position: number;

url: PropTypes.string.isRequired,
/** Url of a video. */
url: string;

videoPlayerRef: refPropTypes.isRequired,
/** Ref for video player. */
videoPlayerRef: MutableRefObject<Video>;

isPlaying: PropTypes.bool.isRequired,
/** Is video playing. */
isPlaying: boolean;

// Defines if component should have small icons and tighter spacing inline
small: PropTypes.bool,
/** Defines if component should have small icons and tighter spacing inline. */
small?: boolean;

style: stylePropTypes,
/** Style of video player controls. */
style?: StyleProp<ViewStyle>;

showPopoverMenu: PropTypes.func.isRequired,
/** Function called to show popover menu. */
showPopoverMenu: (event?: GestureResponderEvent | KeyboardEvent) => void | Promise<void>;

togglePlayCurrentVideo: PropTypes.func.isRequired,
/** Function to play and pause the video. */
togglePlayCurrentVideo: (event?: GestureResponderEvent | KeyboardEvent) => void | Promise<void>;
};

const defaultProps = {
small: false,
style: undefined,
};

function VideoPlayerControls({duration, position, url, videoPlayerRef, isPlaying, small, style, showPopoverMenu, togglePlayCurrentVideo}) {
function VideoPlayerControls({duration, position, url, videoPlayerRef, isPlaying, small = false, style, showPopoverMenu, togglePlayCurrentVideo}: VideoPlayerControlsProps) {
const styles = useThemeStyles();
const {translate} = useLocalize();
const {updateCurrentlyPlayingURL} = usePlaybackContext();
const [shouldShowTime, setShouldShowTime] = useState(false);
const iconSpacing = small ? styles.mr3 : styles.mr4;

const onLayout = (e) => {
setShouldShowTime(e.nativeEvent.layout.width > CONST.VIDEO_PLAYER.HIDE_TIME_TEXT_WIDTH);
const onLayout = (event: LayoutChangeEvent) => {
setShouldShowTime(event.nativeEvent.layout.width > CONST.VIDEO_PLAYER.HIDE_TIME_TEXT_WIDTH);
};

const enterFullScreenMode = useCallback(() => {
Expand All @@ -58,7 +61,7 @@ function VideoPlayerControls({duration, position, url, videoPlayerRef, isPlaying
}, [updateCurrentlyPlayingURL, url, videoPlayerRef]);

const seekPosition = useCallback(
(newPosition) => {
(newPosition: number) => {
videoPlayerRef.current.setStatusAsync({positionMillis: newPosition});
},
[videoPlayerRef],
Expand Down Expand Up @@ -116,8 +119,6 @@ function VideoPlayerControls({duration, position, url, videoPlayerRef, isPlaying
);
}

VideoPlayerControls.propTypes = propTypes;
VideoPlayerControls.defaultProps = defaultProps;
VideoPlayerControls.displayName = 'VideoPlayerControls';

export default VideoPlayerControls;
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
import PropTypes from 'prop-types';
import React from 'react';
import {View} from 'react-native';
import type {GestureResponderEvent} from 'react-native';
import Icon from '@components/Icon';
import * as Expensicons from '@components/Icon/Expensicons';
import Image from '@components/Image';
Expand All @@ -13,19 +13,18 @@ import * as ReportUtils from '@libs/ReportUtils';
import variables from '@styles/variables';
import CONST from '@src/CONST';

const propTypes = {
onPress: PropTypes.func.isRequired,
type VideoPlayerThumbnailProps = {
/** Url of thumbnail image. */
smelaa marked this conversation as resolved.
Show resolved Hide resolved
thumbnailUrl?: string;

accessibilityLabel: PropTypes.string.isRequired,
/** Callback executed on thumbnail press. */
onPress: (event?: GestureResponderEvent | KeyboardEvent) => void | Promise<void>;

thumbnailUrl: PropTypes.string,
/** Accessibility label for the thumbnail. */
accessibilityLabel: string;
};

const defaultProps = {
thumbnailUrl: undefined,
};

function VideoPlayerThumbnail({thumbnailUrl, onPress, accessibilityLabel}) {
function VideoPlayerThumbnail({thumbnailUrl, onPress, accessibilityLabel}: VideoPlayerThumbnailProps) {
const styles = useThemeStyles();

return (
Expand All @@ -48,9 +47,7 @@ function VideoPlayerThumbnail({thumbnailUrl, onPress, accessibilityLabel}) {
onPress={onPress}
onPressIn={() => DeviceCapabilities.canUseTouchScreen() && ControlSelection.block()}
onPressOut={() => ControlSelection.unblock()}
onLongPress={(event) =>
showContextMenuForReport(event, anchor, (report && report.reportID) || '', action, checkIfContextMenuActive, ReportUtils.isArchivedRoom(report))
}
onLongPress={(event) => showContextMenuForReport(event, anchor, report?.reportID ?? '', action, checkIfContextMenuActive, ReportUtils.isArchivedRoom(report))}
shouldUseHapticsOnLongPress
>
<View style={[styles.videoThumbnailPlayButton]}>
Expand All @@ -69,8 +66,6 @@ function VideoPlayerThumbnail({thumbnailUrl, onPress, accessibilityLabel}) {
);
}

VideoPlayerThumbnail.propTypes = propTypes;
VideoPlayerThumbnail.defaultProps = defaultProps;
VideoPlayerThumbnail.displayName = 'VideoPlayerThumbnail';

export default VideoPlayerThumbnail;
Loading
Loading