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 VideoPlayerContexts component files to TypeScript #37134

Merged
Show file tree
Hide file tree
Changes from 17 commits
Commits
Show all changes
32 commits
Select commit Hold shift + click to select a range
00c5efc
PlaybackContext migrated to ts
smelaa Feb 23, 2024
73707a8
VolumeContext migreated to ts
smelaa Feb 23, 2024
3873457
VideoPopoverMenuContext migrated to ts
smelaa Feb 23, 2024
386d2b6
Update src/components/VideoPlayerContexts/VideoPopoverMenuContext.tsx
smelaa Feb 23, 2024
bbcfcb2
Update src/components/VideoPlayerContexts/PlaybackContext.tsx
smelaa Feb 23, 2024
737a1fa
children type update
smelaa Feb 23, 2024
388c665
Update src/components/VideoPlayerContexts/PlaybackContext.tsx
smelaa Feb 23, 2024
3261c35
adding optional chaining & adding shouldPutLeftPaddingWhenNoIcon to S…
smelaa Feb 23, 2024
09aaad5
reverting || -> ?? change
smelaa Feb 23, 2024
8e911c5
main sync
smelaa Feb 23, 2024
c69e9f9
eslint disable nullish coalescing
smelaa Feb 23, 2024
2add946
Merge branch 'main' into ts-migration-videoplayercontext
smelaa Feb 23, 2024
331abe2
Merge branch 'main' into ts-migration-videoplayercontext and solve co…
smelaa Feb 27, 2024
5728c39
Updateing types
smelaa Feb 27, 2024
90affed
reverting podfile change
smelaa Feb 27, 2024
6f92384
Addressing review comments
smelaa Feb 28, 2024
4108ff4
Optional chaining removed
smelaa Feb 28, 2024
f15e179
PopoverMenuItem reuse
smelaa Feb 28, 2024
ee6d152
addressing reviewer suggestion
smelaa Feb 28, 2024
02e3d19
Merge branch 'main' into ts-migration-videoplayercontext
smelaa Feb 28, 2024
bd9bb4e
Merge branch 'main' into ts-migration-videoplayercontext
smelaa Feb 28, 2024
86443d0
Merge remote-tracking branch 'origin/main' into ts-migration-videopla…
smelaa Feb 29, 2024
e15bec7
Merge branch 'main' into ts-migration-videoplayercontext
smelaa Mar 1, 2024
7a33fec
Merge branch 'main' into ts-migration-videoplayercontext
smelaa Mar 6, 2024
c8df480
base
smelaa Mar 6, 2024
6d9823a
Add comment to eslint-disable-next-line
smelaa Mar 7, 2024
c35ac5d
Change eslint-disable-next-line comment
smelaa Mar 7, 2024
0bd5d80
Merge branch 'main' into ts-migration-videoplayercontext
blazejkustra Mar 11, 2024
a7d0fb4
Fix failing typecheck
blazejkustra Mar 11, 2024
c37fa48
Merge branch 'main' into ts-migration-videoplayercontext
smelaa Mar 12, 2024
a0b14c9
Merge with main and solve conflicts
smelaa Mar 12, 2024
4c579d9
Solving conflict
smelaa Mar 13, 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,52 +1,43 @@
import PropTypes from 'prop-types';
import type {AVPlaybackStatusToSet, Video} from 'expo-av';
import React, {useCallback, useContext, useEffect, useMemo, useRef, useState} from 'react';
import type {View} from 'react-native';
import useCurrentReportID from '@hooks/useCurrentReportID';
import type ChildrenProps from '@src/types/utils/ChildrenProps';
import type {PlaybackContext, StatusCallback} from './types';

const PlaybackContext = React.createContext(null);
const Context = React.createContext<PlaybackContext | null>(null);

function PlaybackContextProvider({children}) {
const [currentlyPlayingURL, setCurrentlyPlayingURL] = useState(null);
const [sharedElement, setSharedElement] = useState(null);
const [originalParent, setOriginalParent] = useState(null);
const currentVideoPlayerRef = useRef(null);
const {currentReportID} = useCurrentReportID();
function PlaybackContextProvider({children}: ChildrenProps) {
const [currentlyPlayingURL, setCurrentlyPlayingURL] = useState<string | null>(null);
const [sharedElement, setSharedElement] = useState<View | null>(null);
const [originalParent, setOriginalParent] = useState<View | null>(null);
const currentVideoPlayerRef = useRef<Video | null>(null);
const {currentReportID} = useCurrentReportID() ?? {};

const pauseVideo = useCallback(() => {
if (!(currentVideoPlayerRef && currentVideoPlayerRef.current && currentVideoPlayerRef.current.setStatusAsync)) {
return;
}
currentVideoPlayerRef.current.setStatusAsync({shouldPlay: false});
currentVideoPlayerRef.current?.setStatusAsync({shouldPlay: false});
}, [currentVideoPlayerRef]);
smelaa marked this conversation as resolved.
Show resolved Hide resolved

const stopVideo = useCallback(() => {
if (!(currentVideoPlayerRef && currentVideoPlayerRef.current && currentVideoPlayerRef.current.stopAsync)) {
return;
}
currentVideoPlayerRef.current.stopAsync({shouldPlay: false});
currentVideoPlayerRef.current?.stopAsync();
smelaa marked this conversation as resolved.
Show resolved Hide resolved
}, [currentVideoPlayerRef]);

const playVideo = useCallback(() => {
if (!(currentVideoPlayerRef && currentVideoPlayerRef.current && currentVideoPlayerRef.current.setStatusAsync)) {
return;
}
currentVideoPlayerRef.current.getStatusAsync().then((status) => {
const newStatus = {shouldPlay: true};
if (status.durationMillis === status.positionMillis) {
currentVideoPlayerRef.current?.getStatusAsync().then((status) => {
const newStatus: AVPlaybackStatusToSet = {shouldPlay: true};
if ('durationMillis' in status && status.durationMillis === status.positionMillis) {
newStatus.positionMillis = 0;
smelaa marked this conversation as resolved.
Show resolved Hide resolved
}
currentVideoPlayerRef.current.setStatusAsync(newStatus);
currentVideoPlayerRef.current?.setStatusAsync(newStatus);
});
}, [currentVideoPlayerRef]);

const unloadVideo = useCallback(() => {
if (!(currentVideoPlayerRef && currentVideoPlayerRef.current && currentVideoPlayerRef.current.unloadAsync)) {
return;
}
currentVideoPlayerRef.current.unloadAsync();
currentVideoPlayerRef.current?.unloadAsync();
}, [currentVideoPlayerRef]);

const updateCurrentlyPlayingURL = useCallback(
(url) => {
(url: string) => {
if (currentlyPlayingURL && url !== currentlyPlayingURL) {
pauseVideo();
}
Expand All @@ -56,7 +47,7 @@ function PlaybackContextProvider({children}) {
);

const shareVideoPlayerElements = useCallback(
(ref, parent, child, isUploading) => {
(ref: Video, parent: View, child: View, isUploading: boolean) => {
currentVideoPlayerRef.current = ref;
setOriginalParent(parent);
setSharedElement(child);
Expand All @@ -69,12 +60,9 @@ function PlaybackContextProvider({children}) {
);

const checkVideoPlaying = useCallback(
(statusCallback) => {
if (!(currentVideoPlayerRef && currentVideoPlayerRef.current && currentVideoPlayerRef.current.getStatusAsync)) {
return;
}
currentVideoPlayerRef.current.getStatusAsync().then((status) => {
statusCallback(status.isPlaying);
(statusCallback: StatusCallback) => {
currentVideoPlayerRef.current?.getStatusAsync().then((status) => {
statusCallback('isPlaying' in status && status.isPlaying);
});
hayata-suenaga marked this conversation as resolved.
Show resolved Hide resolved
},
[currentVideoPlayerRef],
Expand Down Expand Up @@ -110,21 +98,17 @@ function PlaybackContextProvider({children}) {
}),
[updateCurrentlyPlayingURL, currentlyPlayingURL, originalParent, sharedElement, shareVideoPlayerElements, playVideo, pauseVideo, checkVideoPlaying],
);
return <PlaybackContext.Provider value={contextValue}>{children}</PlaybackContext.Provider>;
return <Context.Provider value={contextValue}>{children}</Context.Provider>;
}

function usePlaybackContext() {
const context = useContext(PlaybackContext);
if (context === undefined) {
const playbackContext = useContext(Context);
if (!playbackContext) {
throw new Error('usePlaybackContext must be used within a PlaybackContextProvider');
smelaa marked this conversation as resolved.
Show resolved Hide resolved
}
return context;
return playbackContext;
}

PlaybackContextProvider.displayName = 'PlaybackContextProvider';
PlaybackContextProvider.propTypes = {
/** Actual content wrapped by this component */
children: PropTypes.node.isRequired,
};

export {PlaybackContextProvider, usePlaybackContext};
Original file line number Diff line number Diff line change
@@ -1,37 +1,40 @@
import PropTypes from 'prop-types';
import React, {useCallback, useContext, useMemo, useState} from 'react';
import _ from 'underscore';
import * as Expensicons from '@components/Icon/Expensicons';
import useLocalize from '@hooks/useLocalize';
import useNetwork from '@hooks/useNetwork';
import addEncryptedAuthTokenToURL from '@libs/addEncryptedAuthTokenToURL';
import fileDownload from '@libs/fileDownload';
import CONST from '@src/CONST';
import type ChildrenProps from '@src/types/utils/ChildrenProps';
import {usePlaybackContext} from './PlaybackContext';
import type {MenuItem, PlaybackSpeed, SingularMenuItem, VideoPopoverMenuContext} from './types';

const VideoPopoverMenuContext = React.createContext(null);
const Context = React.createContext<VideoPopoverMenuContext | null>(null);

function VideoPopoverMenuContextProvider({children}) {
function VideoPopoverMenuContextProvider({children}: ChildrenProps) {
const {currentVideoPlayerRef, currentlyPlayingURL} = usePlaybackContext();
const {translate} = useLocalize();
const [currentPlaybackSpeed, setCurrentPlaybackSpeed] = useState(CONST.VIDEO_PLAYER.PLAYBACK_SPEEDS[2]);
const [currentPlaybackSpeed, setCurrentPlaybackSpeed] = useState<PlaybackSpeed>(CONST.VIDEO_PLAYER.PLAYBACK_SPEEDS[2]);
const {isOffline} = useNetwork();

const updatePlaybackSpeed = useCallback(
(speed) => {
(speed: PlaybackSpeed) => {
setCurrentPlaybackSpeed(speed);
currentVideoPlayerRef.current.setStatusAsync({rate: speed});
currentVideoPlayerRef.current?.setStatusAsync({rate: speed});
},
[currentVideoPlayerRef],
);

const downloadAttachment = useCallback(() => {
if (currentlyPlayingURL === null) {
return;
}
const sourceURI = currentlyPlayingURL.startsWith('blob:') || currentlyPlayingURL.startsWith('file:') ? currentlyPlayingURL : addEncryptedAuthTokenToURL(currentlyPlayingURL);
fileDownload(sourceURI);
}, [currentlyPlayingURL]);

const menuItems = useMemo(() => {
const items = [];
const items: Array<SingularMenuItem | MenuItem> = [];

if (!isOffline) {
items.push({
Expand All @@ -46,37 +49,30 @@ function VideoPopoverMenuContextProvider({children}) {
items.push({
icon: Expensicons.Meter,
text: translate('videoPlayer.playbackSpeed'),
subMenuItems: [
..._.map(CONST.VIDEO_PLAYER.PLAYBACK_SPEEDS, (speed) => ({
icon: currentPlaybackSpeed === speed ? Expensicons.Checkmark : null,
text: speed.toString(),
onSelected: () => {
updatePlaybackSpeed(speed);
},
shouldPutLeftPaddingWhenNoIcon: true,
})),
],
subMenuItems: CONST.VIDEO_PLAYER.PLAYBACK_SPEEDS.map((speed) => ({
icon: currentPlaybackSpeed === speed ? Expensicons.Checkmark : null,
text: speed.toString(),
onSelected: () => {
updatePlaybackSpeed(speed);
},
shouldPutLeftPaddingWhenNoIcon: true,
})),
});

return items;
}, [currentPlaybackSpeed, downloadAttachment, translate, updatePlaybackSpeed, isOffline]);

const contextValue = useMemo(() => ({menuItems, updatePlaybackSpeed}), [menuItems, updatePlaybackSpeed]);
return <VideoPopoverMenuContext.Provider value={contextValue}>{children}</VideoPopoverMenuContext.Provider>;
return <Context.Provider value={contextValue}>{children}</Context.Provider>;
}

function useVideoPopoverMenuContext() {
const context = useContext(VideoPopoverMenuContext);
if (context === undefined) {
const videoPopooverMenuContext = useContext(Context);
if (!videoPopooverMenuContext) {
throw new Error('useVideoPopoverMenuContext must be used within a VideoPopoverMenuContext');
smelaa marked this conversation as resolved.
Show resolved Hide resolved
}
return context;
return videoPopooverMenuContext;
}

VideoPopoverMenuContextProvider.displayName = 'VideoPopoverMenuContextProvider';
VideoPopoverMenuContextProvider.propTypes = {
/** Actual content wrapped by this component */
children: PropTypes.node.isRequired,
};

export {VideoPopoverMenuContextProvider, useVideoPopoverMenuContext};
Original file line number Diff line number Diff line change
@@ -1,16 +1,17 @@
import PropTypes from 'prop-types';
import React, {useCallback, useContext, useEffect, useMemo} from 'react';
import {useSharedValue} from 'react-native-reanimated';
import type ChildrenProps from '@src/types/utils/ChildrenProps';
import {usePlaybackContext} from './PlaybackContext';
import type {VolumeContext} from './types';

const VolumeContext = React.createContext(null);
const Context = React.createContext<VolumeContext | null>(null);

function VolumeContextProvider({children}) {
function VolumeContextProvider({children}: ChildrenProps) {
const {currentVideoPlayerRef, originalParent} = usePlaybackContext();
const volume = useSharedValue(0);

const updateVolume = useCallback(
(newVolume) => {
(newVolume: number) => {
if (!currentVideoPlayerRef.current) {
return;
}
Expand All @@ -30,21 +31,17 @@ function VolumeContextProvider({children}) {
}, [originalParent, updateVolume, volume.value]);

const contextValue = useMemo(() => ({updateVolume, volume}), [updateVolume, volume]);
return <VolumeContext.Provider value={contextValue}>{children}</VolumeContext.Provider>;
return <Context.Provider value={contextValue}>{children}</Context.Provider>;
}

function useVolumeContext() {
const context = useContext(VolumeContext);
if (context === undefined) {
const volumeContext = useContext(Context);
if (!volumeContext) {
throw new Error('useVolumeContext must be used within a VolumeContextProvider');
smelaa marked this conversation as resolved.
Show resolved Hide resolved
}
return context;
return volumeContext;
}

VolumeContextProvider.displayName = 'VolumeContextProvider';
VolumeContextProvider.propTypes = {
/** Actual content wrapped by this component */
children: PropTypes.node.isRequired,
};

export {VolumeContextProvider, useVolumeContext};
47 changes: 47 additions & 0 deletions src/components/VideoPlayerContexts/types.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,47 @@
import type {Video} from 'expo-av';
import type {MutableRefObject} from 'react';
import type {View} from 'react-native';
import type {SharedValue} from 'react-native-reanimated';
import type CONST from '@src/CONST';
import type IconAsset from '@src/types/utils/IconAsset';

type PlaybackContext = {
updateCurrentlyPlayingURL: (url: string) => void;
currentlyPlayingURL: string | null;
originalParent: View | null;
sharedElement: View | null;
currentVideoPlayerRef: MutableRefObject<Video | null>;
shareVideoPlayerElements: (ref: Video, parent: View, child: View, isUploading: boolean) => void;
playVideo: () => void;
pauseVideo: () => void;
checkVideoPlaying: (statusCallback: (isPlaying: boolean) => void) => void;
smelaa marked this conversation as resolved.
Show resolved Hide resolved
};

type VolumeContext = {
updateVolume: (newVolume: number) => void;
volume: SharedValue<number>;
};

type SingularMenuItem = {
icon: IconAsset | null;
text: string;
onSelected: () => void;
shouldPutLeftPaddingWhenNoIcon?: boolean;
};

type MenuItem = {
icon: IconAsset;
text: string;
subMenuItems: SingularMenuItem[];
};

type VideoPopoverMenuContext = {
menuItems: Array<SingularMenuItem | MenuItem>;
smelaa marked this conversation as resolved.
Show resolved Hide resolved
updatePlaybackSpeed: (speed: PlaybackSpeed) => void;
};

type StatusCallback = (isPlaying: boolean) => void;

type PlaybackSpeed = (typeof CONST.VIDEO_PLAYER.PLAYBACK_SPEEDS)[number];

export type {PlaybackContext, VolumeContext, VideoPopoverMenuContext, MenuItem, SingularMenuItem, StatusCallback, PlaybackSpeed};
1 change: 1 addition & 0 deletions src/libs/fileDownload/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -29,6 +29,7 @@ const fileDownload: FileDownload = (url, fileName) => {
// adding href to anchor
link.href = href;
link.style.display = 'none';
// eslint-disable-next-line @typescript-eslint/prefer-nullish-coalescing -- nullish coalescing doesn't achieve the same result in this case
hayata-suenaga marked this conversation as resolved.
Show resolved Hide resolved
link.download = FileUtils.appendTimeToFileName(fileName || FileUtils.getFileName(url));

// Append to html link element page
Expand Down
2 changes: 1 addition & 1 deletion src/libs/fileDownload/types.ts
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
import type {Asset} from 'react-native-image-picker';

type FileDownload = (url: string, fileName: string, successMessage?: string) => Promise<void>;
type FileDownload = (url: string, fileName?: string, successMessage?: string) => Promise<void>;

type ImageResolution = {width: number; height: number};
type GetImageResolution = (url: File | Asset) => Promise<ImageResolution>;
Expand Down
Loading