Skip to content

Commit 618d80a

Browse files
committed
split PlaybackContext into state and actions contexts
1 parent 705bc68 commit 618d80a

File tree

9 files changed

+104
-107
lines changed

9 files changed

+104
-107
lines changed

src/components/Attachments/AttachmentView/index.tsx

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -12,7 +12,7 @@ import Icon from '@components/Icon';
1212
import PerDiemEReceipt from '@components/PerDiemEReceipt';
1313
import ScrollView from '@components/ScrollView';
1414
import Text from '@components/Text';
15-
import {usePlaybackContext} from '@components/VideoPlayerContexts/PlaybackContext';
15+
import {usePlaybackActionsContext, usePlaybackStateContext} from '@components/VideoPlayerContexts/PlaybackContext';
1616
import useFirstRenderRoute from '@hooks/useFirstRenderRoute';
1717
import {useMemoizedLazyExpensifyIcons} from '@hooks/useLazyAsset';
1818
import useLocalize from '@hooks/useLocalize';
@@ -135,7 +135,8 @@ function AttachmentView({
135135
const [transactionFromOnyx] = useOnyx(`${ONYXKEYS.COLLECTION.TRANSACTION}${getNonEmptyStringOnyxID(transactionID)}`, {canBeMissing: true});
136136
const transaction = transactionProp ?? transactionFromOnyx;
137137
const {translate} = useLocalize();
138-
const {updateCurrentURLAndReportID, currentlyPlayingURL, playVideo} = usePlaybackContext();
138+
const {currentlyPlayingURL} = usePlaybackStateContext();
139+
const {updateCurrentURLAndReportID, playVideo} = usePlaybackActionsContext();
139140

140141
const attachmentCarouselPagerContext = useContext(AttachmentCarouselPagerContext);
141142
const {onAttachmentError, onTap} = attachmentCarouselPagerContext ?? {};

src/components/VideoPlayer/BaseVideoPlayer.tsx

Lines changed: 3 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -13,7 +13,7 @@ import FullScreenLoadingIndicator from '@components/FullscreenLoadingIndicator';
1313
import Hoverable from '@components/Hoverable';
1414
import PressableWithoutFeedback from '@components/Pressable/PressableWithoutFeedback';
1515
import {useFullScreenContext} from '@components/VideoPlayerContexts/FullScreenContext';
16-
import {usePlaybackContext} from '@components/VideoPlayerContexts/PlaybackContext';
16+
import {usePlaybackActionsContext, usePlaybackStateContext} from '@components/VideoPlayerContexts/PlaybackContext';
1717
import type {PlaybackSpeed} from '@components/VideoPlayerContexts/types';
1818
import {useVideoPopoverMenuContext} from '@components/VideoPlayerContexts/VideoPopoverMenuContext';
1919
import {useVolumeContext} from '@components/VideoPlayerContexts/VolumeContext';
@@ -53,22 +53,8 @@ function BaseVideoPlayer({
5353
onTap,
5454
}: VideoPlayerProps & {reportID: string}) {
5555
const styles = useThemeStyles();
56-
const {
57-
pauseVideo,
58-
playVideo,
59-
replayVideo,
60-
currentlyPlayingURL,
61-
sharedElement,
62-
originalParent,
63-
shareVideoPlayerElements,
64-
currentVideoPlayerRef,
65-
currentVideoViewRef,
66-
updateCurrentURLAndReportID,
67-
setCurrentlyPlayingURL,
68-
mountedVideoPlayersRef,
69-
playerStatus,
70-
updatePlayerStatus,
71-
} = usePlaybackContext();
56+
const {currentlyPlayingURL, sharedElement, originalParent, currentVideoPlayerRef, currentVideoViewRef, mountedVideoPlayersRef, playerStatus} = usePlaybackStateContext();
57+
const {pauseVideo, playVideo, replayVideo, shareVideoPlayerElements, updateCurrentURLAndReportID, setCurrentlyPlayingURL, updatePlayerStatus} = usePlaybackActionsContext();
7258
const {isFullScreenRef} = useFullScreenContext();
7359

7460
const isOffline = useNetwork().isOffline;

src/components/VideoPlayer/VideoPlayerControls/ProgressBar/index.tsx

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,7 @@ import type {GestureStateChangeEvent, GestureUpdateEvent, PanGestureChangeEventP
44
import {Gesture, GestureDetector} from 'react-native-gesture-handler';
55
import Animated, {useAnimatedStyle, useSharedValue} from 'react-native-reanimated';
66
import {scheduleOnRN} from 'react-native-worklets';
7-
import {usePlaybackContext} from '@components/VideoPlayerContexts/PlaybackContext';
7+
import {usePlaybackActionsContext} from '@components/VideoPlayerContexts/PlaybackContext';
88
import useThemeStyles from '@hooks/useThemeStyles';
99

1010
type ProgressBarProps = {
@@ -24,7 +24,7 @@ function getProgress(currentPosition: number, maxPosition: number): number {
2424

2525
function ProgressBar({duration, position, seekPosition}: ProgressBarProps) {
2626
const styles = useThemeStyles();
27-
const {pauseVideo, playVideo, checkIfVideoIsPlaying} = usePlaybackContext();
27+
const {pauseVideo, playVideo, checkIfVideoIsPlaying} = usePlaybackActionsContext();
2828
const [sliderWidth, setSliderWidth] = useState(1);
2929
const [isSliderPressed, setIsSliderPressed] = useState(false);
3030
const progressWidth = useSharedValue(0);

src/components/VideoPlayer/VideoPlayerControls/index.tsx

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -8,7 +8,7 @@ import type {ValueOf} from 'type-fest';
88
import Text from '@components/Text';
99
import IconButton from '@components/VideoPlayer/IconButton';
1010
import {convertSecondsToTime} from '@components/VideoPlayer/utils';
11-
import {usePlaybackContext} from '@components/VideoPlayerContexts/PlaybackContext';
11+
import {usePlaybackActionsContext} from '@components/VideoPlayerContexts/PlaybackContext';
1212
import {useMemoizedLazyExpensifyIcons} from '@hooks/useLazyAsset';
1313
import useLocalize from '@hooks/useLocalize';
1414
import useThemeStyles from '@hooks/useThemeStyles';
@@ -69,7 +69,7 @@ function VideoPlayerControls({
6969
const icons = useMemoizedLazyExpensifyIcons(['ThreeDots', 'Pause', 'Play', 'Fullscreen']);
7070
const styles = useThemeStyles();
7171
const {translate} = useLocalize();
72-
const {updateCurrentURLAndReportID} = usePlaybackContext();
72+
const {updateCurrentURLAndReportID} = usePlaybackActionsContext();
7373
const [shouldShowTime, setShouldShowTime] = useState(false);
7474
const iconSpacing = small ? styles.mr3 : styles.mr4;
7575

src/components/VideoPlayerContexts/PlaybackContext/index.tsx

Lines changed: 53 additions & 52 deletions
Original file line numberDiff line numberDiff line change
@@ -1,19 +1,20 @@
11
import type {VideoPlayer, VideoPlayerStatus, VideoView} from 'expo-video';
2-
import React, {useCallback, useContext, useEffect, useMemo, useRef, useState} from 'react';
2+
import React, {useCallback, useContext, useEffect, useRef, useState} from 'react';
33
import type {View} from 'react-native';
44
import {getReportOrDraftReport, isChatThread} from '@libs/ReportUtils';
55
import Navigation from '@navigation/Navigation';
66
import type ChildrenProps from '@src/types/utils/ChildrenProps';
77
import type {ProtectedCurrentRouteReportID} from './playbackContextReportIDUtils';
88
import {findURLInReportOrAncestorAttachments, getCurrentRouteReportID, NO_REPORT_ID, NO_REPORT_ID_IN_PARAMS, normalizeReportID} from './playbackContextReportIDUtils';
9-
import type {OriginalParent, PlaybackContext, PlaybackContextValues} from './types';
9+
import type {OriginalParent, PlaybackActionsContext, PlaybackActionsContextValues, PlaybackStateContext, PlaybackStateContextValues} from './types';
1010
import usePlaybackContextVideoRefs from './usePlaybackContextVideoRefs';
1111

12-
const Context = React.createContext<PlaybackContext | null>(null);
12+
const ContextState = React.createContext<PlaybackStateContext | null>(null);
13+
const ContextActions = React.createContext<PlaybackActionsContext | null>(null);
1314

1415
function PlaybackContextProvider({children}: ChildrenProps) {
15-
const [currentlyPlayingURL, setCurrentlyPlayingURL] = useState<PlaybackContextValues['currentlyPlayingURL']>(null);
16-
const [sharedElement, setSharedElement] = useState<PlaybackContextValues['sharedElement']>(null);
16+
const [currentlyPlayingURL, setCurrentlyPlayingURL] = useState<PlaybackStateContextValues['currentlyPlayingURL']>(null);
17+
const [sharedElement, setSharedElement] = useState<PlaybackStateContextValues['sharedElement']>(null);
1718
const [originalParent, setOriginalParent] = useState<OriginalParent>(null);
1819
const [currentRouteReportID, setCurrentRouteReportID] = useState<ProtectedCurrentRouteReportID>(NO_REPORT_ID);
1920
const mountedVideoPlayersRef = useRef<string[]>([]);
@@ -28,7 +29,7 @@ function PlaybackContextProvider({children}: ChildrenProps) {
2829

2930
const video = usePlaybackContextVideoRefs(resetContextProperties);
3031

31-
const updateCurrentURLAndReportID: PlaybackContextValues['updateCurrentURLAndReportID'] = useCallback(
32+
const updateCurrentURLAndReportID: PlaybackActionsContextValues['updateCurrentURLAndReportID'] = useCallback(
3233
(url, reportID) => {
3334
if (!reportID) {
3435
return;
@@ -69,7 +70,7 @@ function PlaybackContextProvider({children}: ChildrenProps) {
6970
playerStatus.current = newStatus;
7071
}, []);
7172

72-
const shareVideoPlayerElements: PlaybackContextValues['shareVideoPlayerElements'] = useCallback(
73+
const shareVideoPlayerElements: PlaybackActionsContextValues['shareVideoPlayerElements'] = useCallback(
7374
(
7475
videoPlayerRef: VideoPlayer | null,
7576
videoViewRef: VideoView | null,
@@ -117,55 +118,55 @@ function PlaybackContextProvider({children}: ChildrenProps) {
117118
});
118119
}, [currentRouteReportID, currentlyPlayingURL, video, video.resetPlayerData]);
119120

120-
const contextValue: PlaybackContext = useMemo(
121-
() => ({
122-
updateCurrentURLAndReportID,
123-
currentlyPlayingURL,
124-
currentRouteReportID: normalizeReportID(currentRouteReportID),
125-
originalParent,
126-
sharedElement,
127-
shareVideoPlayerElements,
128-
setCurrentlyPlayingURL,
129-
currentVideoPlayerRef: video.playerRef,
130-
currentVideoViewRef: video.viewRef,
131-
playVideo: video.play,
132-
pauseVideo: video.pause,
133-
replayVideo: video.replay,
134-
stopVideo: video.stop,
135-
checkIfVideoIsPlaying: video.isPlaying,
136-
resetVideoPlayerData: video.resetPlayerData,
137-
mountedVideoPlayersRef,
138-
playerStatus,
139-
updatePlayerStatus,
140-
}),
141-
[
142-
updateCurrentURLAndReportID,
143-
currentlyPlayingURL,
144-
currentRouteReportID,
145-
originalParent,
146-
sharedElement,
147-
shareVideoPlayerElements,
148-
video.playerRef,
149-
video.viewRef,
150-
video.play,
151-
video.pause,
152-
video.replay,
153-
video.stop,
154-
video.isPlaying,
155-
video.resetPlayerData,
156-
updatePlayerStatus,
157-
],
121+
// Because of the React Compiler we don't need to memoize it manually
122+
// eslint-disable-next-line react/jsx-no-constructed-context-values
123+
const stateValue: PlaybackStateContext = {
124+
currentlyPlayingURL,
125+
currentRouteReportID: normalizeReportID(currentRouteReportID),
126+
originalParent,
127+
sharedElement,
128+
currentVideoPlayerRef: video.playerRef,
129+
currentVideoViewRef: video.viewRef,
130+
mountedVideoPlayersRef,
131+
playerStatus,
132+
};
133+
134+
// Because of the React Compiler we don't need to memoize it manually
135+
// eslint-disable-next-line react/jsx-no-constructed-context-values
136+
const actionsValue: PlaybackActionsContext = {
137+
updateCurrentURLAndReportID,
138+
shareVideoPlayerElements,
139+
setCurrentlyPlayingURL,
140+
playVideo: video.play,
141+
pauseVideo: video.pause,
142+
replayVideo: video.replay,
143+
stopVideo: video.stop,
144+
checkIfVideoIsPlaying: video.isPlaying,
145+
resetVideoPlayerData: video.resetPlayerData,
146+
updatePlayerStatus,
147+
};
148+
149+
return (
150+
<ContextState.Provider value={stateValue}>
151+
<ContextActions.Provider value={actionsValue}>{children}</ContextActions.Provider>
152+
</ContextState.Provider>
158153
);
154+
}
159155

160-
return <Context.Provider value={contextValue}>{children}</Context.Provider>;
156+
function usePlaybackStateContext() {
157+
const playbackStateContext = useContext(ContextState);
158+
if (!playbackStateContext) {
159+
throw new Error('usePlaybackStateContext must be used within a PlaybackContextProvider');
160+
}
161+
return playbackStateContext;
161162
}
162163

163-
function usePlaybackContext() {
164-
const playbackContext = useContext(Context);
165-
if (!playbackContext) {
166-
throw new Error('usePlaybackContext must be used within a PlaybackContextProvider');
164+
function usePlaybackActionsContext() {
165+
const playbackActionsContext = useContext(ContextActions);
166+
if (!playbackActionsContext) {
167+
throw new Error('usePlaybackActionsContext must be used within a PlaybackContextProvider');
167168
}
168-
return playbackContext;
169+
return playbackActionsContext;
169170
}
170171

171-
export {Context as PlaybackContext, PlaybackContextProvider, usePlaybackContext};
172+
export {ContextActions as PlaybackActionsContext, ContextState as PlaybackStateContext, PlaybackContextProvider, usePlaybackStateContext, usePlaybackActionsContext};

src/components/VideoPlayerContexts/PlaybackContext/types.ts

Lines changed: 34 additions & 26 deletions
Original file line numberDiff line numberDiff line change
@@ -39,17 +39,7 @@ type VideoElementData = {
3939
reportID: string | undefined;
4040
};
4141

42-
/**
43-
* Playback-related context values available throughout the app.
44-
*/
45-
type PlaybackContextValues = {
46-
/**
47-
* Updates the currently tracked video URL and associated report ID.
48-
* @param url The new video URL.
49-
* @param reportID The new report ID.
50-
*/
51-
updateCurrentURLAndReportID: (url: string | undefined, reportID: string | undefined) => void;
52-
42+
type PlaybackStateContextValues = {
5343
/**
5444
* The URL of the video currently being played, or null if none.
5545
*/
@@ -70,6 +60,28 @@ type PlaybackContextValues = {
7060
*/
7161
sharedElement: View | HTMLDivElement | null;
7262

63+
/**
64+
* Array of currently mounted Video Player instances
65+
*/
66+
mountedVideoPlayersRef: RefObject<string[]>;
67+
68+
/**
69+
* Status of the currently used Video Player
70+
*/
71+
playerStatus: RefObject<VideoPlayerStatus>;
72+
};
73+
74+
/**
75+
* Playback-related context actions values available throughout the app.
76+
*/
77+
type PlaybackActionsContextValues = {
78+
/**
79+
* Updates the currently tracked video URL and associated report ID.
80+
* @param url The new video URL.
81+
* @param reportID The new report ID.
82+
*/
83+
updateCurrentURLAndReportID: (url: string | undefined, reportID: string | undefined) => void;
84+
7385
/**
7486
* Updates shared video player elements across different parts of the UI.
7587
* @param playerRef Reference to the VideoPlayer instance.
@@ -93,16 +105,6 @@ type PlaybackContextValues = {
93105
*/
94106
setCurrentlyPlayingURL: React.Dispatch<React.SetStateAction<string | null>>;
95107

96-
/**
97-
* Array of currently mounted Video Player instances
98-
*/
99-
mountedVideoPlayersRef: RefObject<string[]>;
100-
101-
/**
102-
* Status of the currently used Video Player
103-
*/
104-
playerStatus: RefObject<VideoPlayerStatus>;
105-
106108
/**
107109
* Updates current videoPlayer status
108110
* @param newStatus New videoPlayer status
@@ -164,17 +166,23 @@ type PlaybackContextVideoRefs = {
164166
};
165167

166168
/**
167-
* Combined playback context with values and video control helpers.
169+
* Combined playback state context with values and video control helpers.
168170
*/
169-
type PlaybackContext = PlaybackContextValues & {
171+
type PlaybackStateContext = PlaybackStateContextValues & {
172+
currentVideoPlayerRef: PlaybackContextVideoRefs['playerRef'];
173+
currentVideoViewRef: PlaybackContextVideoRefs['viewRef'];
174+
};
175+
176+
/**
177+
* Combined playback actions context with values and video control helpers.
178+
*/
179+
type PlaybackActionsContext = PlaybackActionsContextValues & {
170180
resetVideoPlayerData: PlaybackContextVideoRefs['resetPlayerData'];
171181
playVideo: PlaybackContextVideoRefs['play'];
172182
pauseVideo: PlaybackContextVideoRefs['pause'];
173183
replayVideo: PlaybackContextVideoRefs['replay'];
174184
stopVideo: PlaybackContextVideoRefs['stop'];
175185
checkIfVideoIsPlaying: PlaybackContextVideoRefs['isPlaying'];
176-
currentVideoPlayerRef: PlaybackContextVideoRefs['playerRef'];
177-
currentVideoViewRef: PlaybackContextVideoRefs['viewRef'];
178186
};
179187

180-
export type {PlaybackContextVideoRefs, StopVideo, PlaybackContextValues, PlaybackContext, OriginalParent};
188+
export type {PlaybackContextVideoRefs, StopVideo, PlaybackStateContextValues, PlaybackActionsContextValues, PlaybackStateContext, PlaybackActionsContext, OriginalParent};

src/components/VideoPlayerContexts/VolumeContext.tsx

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,13 +1,13 @@
11
import React, {useCallback, useContext, useEffect, useMemo} from 'react';
22
import {useSharedValue} from 'react-native-reanimated';
33
import type ChildrenProps from '@src/types/utils/ChildrenProps';
4-
import {usePlaybackContext} from './PlaybackContext';
4+
import {usePlaybackStateContext} from './PlaybackContext';
55
import type {VolumeContext} from './types';
66

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

99
function VolumeContextProvider({children}: ChildrenProps) {
10-
const {currentVideoPlayerRef, originalParent} = usePlaybackContext();
10+
const {currentVideoPlayerRef, originalParent} = usePlaybackStateContext();
1111
const volume = useSharedValue(0);
1212
// We need this field to remember the last value before clicking mute
1313
const lastNonZeroVolume = useSharedValue(1);

src/components/VideoPlayerPreview/index.tsx

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -6,7 +6,7 @@ import {View} from 'react-native';
66
import {useIsOnSearch} from '@components/Search/SearchScopeProvider';
77
import VideoPlayer from '@components/VideoPlayer';
88
import IconButton from '@components/VideoPlayer/IconButton';
9-
import {usePlaybackContext} from '@components/VideoPlayerContexts/PlaybackContext';
9+
import {usePlaybackActionsContext, usePlaybackStateContext} from '@components/VideoPlayerContexts/PlaybackContext';
1010
import useCheckIfRouteHasRemainedUnchanged from '@hooks/useCheckIfRouteHasRemainedUnchanged';
1111
import {useMemoizedLazyExpensifyIcons} from '@hooks/useLazyAsset';
1212
import useLocalize from '@hooks/useLocalize';
@@ -52,7 +52,8 @@ function VideoPlayerPreview({videoUrl, thumbnailUrl, reportID, fileName, videoDi
5252
const icons = useMemoizedLazyExpensifyIcons(['Expand']);
5353
const styles = useThemeStyles();
5454
const {translate} = useLocalize();
55-
const {currentlyPlayingURL, currentRouteReportID, updateCurrentURLAndReportID} = usePlaybackContext();
55+
const {currentlyPlayingURL, currentRouteReportID} = usePlaybackStateContext();
56+
const {updateCurrentURLAndReportID} = usePlaybackActionsContext();
5657

5758
/* This needs to be isSmallScreenWidth because we want to be able to play video in chat (not in attachment modal) when preview is inside an RHP */
5859
// eslint-disable-next-line rulesdir/prefer-shouldUseNarrowLayout-instead-of-isSmallScreenWidth

src/pages/Search/SearchPage.tsx

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -18,7 +18,7 @@ import {ScrollOffsetContext} from '@components/ScrollOffsetContextProvider';
1818
import {useSearchContext} from '@components/Search/SearchContext';
1919
import type {SearchHeaderOptionValue} from '@components/Search/SearchPageHeader/SearchPageHeader';
2020
import type {PaymentData, SearchParams} from '@components/Search/types';
21-
import {usePlaybackContext} from '@components/VideoPlayerContexts/PlaybackContext';
21+
import {usePlaybackActionsContext} from '@components/VideoPlayerContexts/PlaybackContext';
2222
import useAllTransactions from '@hooks/useAllTransactions';
2323
import useBulkPayOptions from '@hooks/useBulkPayOptions';
2424
import useConfirmModal from '@hooks/useConfirmModal';
@@ -1165,7 +1165,7 @@ function SearchPage({route}: SearchPageProps) {
11651165
validateFiles(files, Array.from(e.dataTransfer?.items ?? []));
11661166
};
11671167

1168-
const {resetVideoPlayerData} = usePlaybackContext();
1168+
const {resetVideoPlayerData} = usePlaybackActionsContext();
11691169

11701170
const metadata = searchResults?.search;
11711171
const shouldShowFooter = !!metadata?.count || selectedTransactionsKeys.length > 0;

0 commit comments

Comments
 (0)