@@ -54,8 +54,9 @@ function BaseVideoPlayer({
5454 onTap,
5555} : VideoPlayerProps & { reportID : string } ) {
5656 const styles = useThemeStyles ( ) ;
57- const { currentlyPlayingURL, sharedElement, originalParent, currentVideoPlayerRef, currentVideoViewRef, mountedVideoPlayersRef, playerStatus} = usePlaybackStateContext ( ) ;
58- const { pauseVideo, playVideo, replayVideo, shareVideoPlayerElements, updateCurrentURLAndReportID, setCurrentlyPlayingURL, updatePlayerStatus} = usePlaybackActionsContext ( ) ;
57+ const { currentlyPlayingURL, sharedElement, originalParent, currentVideoPlayerRef, currentVideoViewRef, mountedVideoPlayersRef, playerStatus, shareVersion} = usePlaybackStateContext ( ) ;
58+ const { pauseVideo, playVideo, replayVideo, shareVideoPlayerElements, updateCurrentURLAndReportID, setCurrentlyPlayingURL, updatePlayerStatus, requestDonorReRegistration} =
59+ usePlaybackActionsContext ( ) ;
5960 const { isFullScreenRef} = useFullScreenState ( ) ;
6061
6162 const isOffline = useNetwork ( ) . isOffline ;
@@ -134,6 +135,12 @@ function BaseVideoPlayer({
134135 const videoPlayerElementRef = useRef < View | HTMLDivElement | null > ( null ) ;
135136 const sharedVideoPlayerParentRef = useRef < View | HTMLDivElement | null > ( null ) ;
136137 const isReadyForDisplayRef = useRef ( false ) ;
138+ const savedCurrentTimeRef = useRef ( 0 ) ;
139+ const shouldUseSharedVideoElementRef = useRef ( shouldUseSharedVideoElement ) ;
140+ // This needs to be updated synchronously during render (not in an effect) so that
141+ // cleanup functions of useLayoutEffect always read the latest value.
142+ // eslint-disable-next-line react-hooks/refs
143+ shouldUseSharedVideoElementRef . current = shouldUseSharedVideoElement ;
137144 const canUseTouchScreen = canUseTouchScreenLib ( ) ;
138145 const isCurrentlyURLSet = currentlyPlayingURL === url ;
139146 const isUploading = CONST . ATTACHMENT_LOCAL_URL_PREFIX . some ( ( prefix ) => url . startsWith ( prefix ) ) ;
@@ -295,6 +302,13 @@ function BaseVideoPlayer({
295302 onSourceLoaded ?.( event ) ;
296303 } ) ;
297304
305+ useEffect ( ( ) => {
306+ if ( currentTime <= 0 ) {
307+ return ;
308+ }
309+ savedCurrentTimeRef . current = currentTime ;
310+ } , [ currentTime ] ) ;
311+
298312 useEffect ( ( ) => {
299313 if ( ! videoPlayerRef . current . duration ) {
300314 return ;
@@ -315,7 +329,11 @@ function BaseVideoPlayer({
315329 // ref url: https://reactjs.org/blog/2020/08/10/react-v17-rc.html#effect-cleanup-timing
316330 useLayoutEffect (
317331 ( ) => ( ) => {
318- if ( shouldUseSharedVideoElement || videoPlayerRef . current !== currentVideoPlayerRef . current ) {
332+ // Use ref to read the latest value of shouldUseSharedVideoElement, preventing
333+ // destructive cleanup when this value changes during viewport resize.
334+ // Without the ref, the cleanup captures the stale (old) closure value and
335+ // incorrectly destroys the player when transitioning between shared/non-shared modes.
336+ if ( shouldUseSharedVideoElementRef . current || videoPlayerRef . current !== currentVideoPlayerRef . current ) {
319337 return ;
320338 }
321339 if ( currentVideoPlayerRef . current ) {
@@ -324,7 +342,8 @@ function BaseVideoPlayer({
324342 currentVideoPlayerRef . current = null ;
325343 }
326344 } ,
327- [ currentVideoPlayerRef , mountedVideoPlayersRef , shouldUseSharedVideoElement , url ] ,
345+ // eslint-disable-next-line react-hooks/exhaustive-deps
346+ [ currentVideoPlayerRef , mountedVideoPlayersRef , url ] ,
328347 ) ;
329348
330349 useEffect ( ( ) => {
@@ -346,15 +365,30 @@ function BaseVideoPlayer({
346365
347366 useEffect (
348367 ( ) => ( ) => {
349- if ( shouldUseSharedVideoElement || ! isCurrentlyURLSetRef . current ) {
368+ // Use ref to read the latest value of shouldUseSharedVideoElement, preventing
369+ // premature URL clearing when this value changes during viewport resize.
370+ if ( shouldUseSharedVideoElementRef . current || ! isCurrentlyURLSetRef . current ) {
350371 return ;
351372 }
352373
353374 setCurrentlyPlayingURL ( null ) ;
354375 } ,
355- [ setCurrentlyPlayingURL , shouldUseSharedVideoElement ] ,
376+ // eslint-disable-next-line react-hooks/exhaustive-deps
377+ [ setCurrentlyPlayingURL ] ,
356378 ) ;
357379
380+ // When transitioning from non-shared to shared mode (e.g. narrow → wide viewport), the stale
381+ // non-shared player refs remain in the context. Request all donors to re-register so the real
382+ // donor (e.g. the chat player) reclaims the context refs.
383+ const prevShouldUseSharedVideoElementRef = useRef ( shouldUseSharedVideoElement ) ;
384+ useEffect ( ( ) => {
385+ const wasShared = prevShouldUseSharedVideoElementRef . current ;
386+ if ( shouldUseSharedVideoElement && ! wasShared ) {
387+ requestDonorReRegistration ( ) ;
388+ }
389+ prevShouldUseSharedVideoElementRef . current = shouldUseSharedVideoElement ;
390+ } , [ shouldUseSharedVideoElement , requestDonorReRegistration , url ] ) ;
391+
358392 // update shared video elements
359393 useEffect ( ( ) => {
360394 // On mobile safari, we need to auto-play when sharing video element here
@@ -373,6 +407,7 @@ function BaseVideoPlayer({
373407 currentlyPlayingURL ,
374408 shouldUseSharedVideoElement ,
375409 shareVideoPlayerElements ,
410+ shareVersion ,
376411 url ,
377412 isUploading ,
378413 reportID ,
@@ -409,6 +444,11 @@ function BaseVideoPlayer({
409444 newParentRef . appendChild ( sharedElement as HTMLDivElement ) ;
410445 }
411446 }
447+ // Restore the playback position after moving the video element in the DOM.
448+ // Moving elements can reset currentTime to 0 in some browsers/video implementations.
449+ if ( videoPlayerRef . current && savedCurrentTimeRef . current > 0 && videoPlayerRef . current . currentTime === 0 ) {
450+ videoPlayerRef . current . currentTime = savedCurrentTimeRef . current ;
451+ }
412452 return ( ) => {
413453 if ( ! originalParent || ! ( 'appendChild' in originalParent ) ) {
414454 return ;
0 commit comments