[251116] feat: playeReducer , playActions.js에 videoState 추가

🕐 커밋 시간: 2025. 11. 16. 17:28:35

📊 변경 통계:
  • 총 파일: 5개
  • 추가: +398줄
  • 삭제: -8줄

📁 추가된 파일:
  + com.twin.app.shoptime/[251116]_video_state_management_design.md

📝 수정된 파일:
  ~ com.twin.app.shoptime/src/actions/actionTypes.js
  ~ com.twin.app.shoptime/src/actions/playActions.js
  ~ com.twin.app.shoptime/src/reducers/playReducer.js
  ~ com.twin.app.shoptime/src/views/HomePanel/HomeBanner/RandomUnit.jsx

🔧 함수 변경 내용:
  📄 com.twin.app.shoptime/src/actions/playActions.js (javascript):
     Added: returnToPreview()
    🔄 Modified: finishModalVideoForce(), shrinkVideoTo1px(), resumePlayerControl()
  📄 com.twin.app.shoptime/src/views/HomePanel/HomeBanner/RandomUnit.jsx (javascript):
    🔄 Modified: SpotlightContainerDecorator()
  📄 com.twin.app.shoptime/[251116]_video_state_management_design.md (md파일):
     Added: curry(), dispatch(), useSelector()

🔧 주요 변경 내용:
  • 타입 시스템 안정성 강화
  • 핵심 비즈니스 로직 개선
  • 개발 문서 및 가이드 개선
This commit is contained in:
2025-11-16 17:28:35 +09:00
parent 4ebbb773db
commit 2d93ee6ca4
5 changed files with 633 additions and 15 deletions

View File

@@ -0,0 +1,220 @@
# [251116] 새로운 비디오 상태 관리 시스템 구현
## 개요
기존의 videoPlayReducer는 유지하되, PlayerPanel과 VideoPlayer.js를 위한 새로운 비디오 상태 관리 시스템을 playerReducer에 구현한다. 재생 상태와 화면 상태를 분리하여 더 정밀한 비디오 상태 제어를 가능하게 한다.
## 설계 원칙
1. **기존 videoPlayReducer 유지**: 다른 컴포넌트에서 사용 중일 수 있으므로 그대로 둔다
2. **playerReducer에 새로운 상태 시스템 구현**: PlayerPanel과 VideoPlayer.js 전용
3. **이중 상태 관리**: 재생 상태(Playback Status) + 화면 상태(Display Status)
4. **기존 패턴 따르기**: FP 스타일의 curry, get, set 활용
## 새로운 상태 구조
### 상수 정의 (playerActions.js)
```javascript
// 재생 상태
export const PLAYBACK_STATUS = {
LOADING: 'loading', // 비디오 로딩 중
LOAD_SUCCESS: 'load_success', // 비디오 로딩 성공
LOAD_ERROR: 'load_error', // 비디오 로딩 오류
PLAYING: 'playing', // 비디오 재생 중
NOT_PLAYING: 'not_playing', // 비디오 재생 아님 (정지/일시정지)
BUFFERING: 'buffering' // 버퍼링 중
};
// 화면 상태
export const DISPLAY_STATUS = {
HIDDEN: 'hidden', // 화면에 안보임
VISIBLE: 'visible', // 화면에 보임
MINIMIZED: 'minimized', // 최소화됨
FULLSCREEN: 'fullscreen' // 전체화면
};
```
### 초기 상태 (playerReducer)
```javascript
// 기존 playerReducer 상태에 추가
const initialState = {
// ... 기존 상태들
playerVideoState: {
// 현재 상태
playback: PLAYBACK_STATUS.NOT_PLAYING,
display: DISPLAY_STATUS.HIDDEN,
videoId: null,
progress: 0,
error: null,
timestamp: null
}
};
```
## 액션 타입 및 함수
### 액션 타입
```javascript
export const PLAYER_VIDEO_ACTIONS = {
// 재생 상태 액션
SET_PLAYBACK_LOADING: 'SET_PLAYBACK_LOADING',
SET_PLAYBACK_SUCCESS: 'SET_PLAYBACK_SUCCESS',
SET_PLAYBACK_ERROR: 'SET_PLAYBACK_ERROR',
SET_PLAYBACK_PLAYING: 'SET_PLAYBACK_PLAYING',
SET_PLAYBACK_NOT_PLAYING: 'SET_PLAYBACK_NOT_PLAYING',
SET_PLAYBACK_BUFFERING: 'SET_PLAYBACK_BUFFERING',
// 화면 상태 액션
SET_DISPLAY_HIDDEN: 'SET_DISPLAY_HIDDEN',
SET_DISPLAY_VISIBLE: 'SET_DISPLAY_VISIBLE',
SET_DISPLAY_MINIMIZED: 'SET_DISPLAY_MINIMIZED',
SET_DISPLAY_FULLSCREEN: 'SET_DISPLAY_FULLSCREEN',
// 복합 액션
SET_VIDEO_LOADING: 'SET_VIDEO_LOADING',
SET_VIDEO_PLAYING: 'SET_VIDEO_PLAYING',
SET_VIDEO_STOPPED: 'SET_VIDEO_STOPPED',
SET_VIDEO_MINIMIZED_PLAYING: 'SET_VIDEO_MINIMIZED_PLAYING',
};
```
### 액션 함수
```javascript
// 기본 액션 함수들 (FP 스타일)
export const setPlaybackLoading = curry((videoId, displayMode = 'visible') => ({
type: PLAYER_VIDEO_ACTIONS.SET_VIDEO_LOADING,
payload: {
playback: PLAYBACK_STATUS.LOADING,
display: displayMode,
videoId,
progress: 0,
error: null,
timestamp: Date.now()
}
}));
export const setPlaybackPlaying = curry((videoId, displayMode = 'fullscreen') => ({
type: PLAYER_VIDEO_ACTIONS.SET_VIDEO_PLAYING,
payload: {
playback: PLAYBACK_STATUS.PLAYING,
display: displayMode,
videoId,
progress: 100,
error: null,
timestamp: Date.now()
}
}));
export const setPlaybackError = curry((videoId, error) => ({
type: PLAYER_VIDEO_ACTIONS.SET_PLAYBACK_ERROR,
payload: {
playback: PLAYBACK_STATUS.LOAD_ERROR,
display: DISPLAY_STATUS.VISIBLE,
videoId,
error,
progress: 0,
timestamp: Date.now()
}
}));
export const setVideoStopped = () => ({
type: PLAYER_VIDEO_ACTIONS.SET_VIDEO_STOPPED,
payload: {
playback: PLAYBACK_STATUS.NOT_PLAYING,
display: DISPLAY_STATUS.HIDDEN,
videoId: null,
error: null,
progress: 0,
timestamp: Date.now()
}
}));
```
## 상태 사용 예시
### PlayerPanel.jsx
```javascript
import {
setPlaybackLoading,
setPlaybackPlaying,
setPlaybackError,
setVideoStopped
} from '../actions/playerActions';
// 비디오 로딩 시작
const handleVideoLoadStart = (videoId) => {
dispatch(setPlaybackLoading(videoId, 'fullscreen'));
};
// 비디오 재생 시작
const handleVideoPlay = (videoId) => {
dispatch(setPlaybackPlaying(videoId, 'fullscreen'));
};
// 비디오 에러 발생
const handleVideoError = (videoId, error) => {
dispatch(setPlaybackError(videoId, error));
};
// 상태 확인
const videoState = useSelector(state => state.player.playerVideoState);
const isLoading = videoState.playback === PLAYBACK_STATUS.LOADING;
const isPlaying = videoState.playback === PLAYBACK_STATUS.PLAYING;
const hasError = videoState.playback === PLAYBACK_STATUS.LOAD_ERROR;
const isFullscreen = videoState.display === DISPLAY_STATUS.FULLSCREEN;
```
### VideoPlayer.js
```javascript
// 현재 상태에 따른 UI 렌더링
const renderVideoState = () => {
const { playback, display, error, progress } = videoState;
if (playback === PLAYBACK_STATUS.LOADING) {
return <LoadingSpinner progress={progress} />;
}
if (playback === PLAYBACK_STATUS.LOAD_ERROR) {
return <ErrorMessage error={error} onRetry={handleRetry} />;
}
if (playback === PLAYBACK_STATUS.BUFFERING) {
return <BufferingIndicator />;
}
if (playback === PLAYBACK_STATUS.PLAYING && display === DISPLAY_STATUS.FULLSCREEN) {
return <VideoPlayer videoId={videoState.videoId} />;
}
return null;
};
```
## 구현 순서
1. [ ] playerActions.js에 상수 및 액션 함수들 추가
2. [ ] playerReducer.js에 초기 상태 및 핸들러들 추가
3. [ ] PlayerPanel.jsx에서 새로운 상태 시스템으로 전환
4. [ ] VideoPlayer.js에서 새로운 상태 시스템으로 전환
5. [ ] 테스트 및 검증
## 장점
1. **정밀한 상태 제어**: 재생 상태와 화면 상태를 별도로 관리
2. **명확한 상태 의미**: 각 상태가 명확한 의미를 가짐
3. **확장성**: 새로운 상태 추가가 용이
4. **유지보수성**: 기존 코드 영향 최소화
5. **재사용성**: 다른 컴포넌트에서도 활용 가능
## 주의사항
- 기존 videoPlayReducer와 충돌하지 않도록 주의
- PlayerPanel과 VideoPlayer.js에만 집중하여 구현
- 기존 비디오 재생 로직과 호환성 유지

View File

@@ -259,6 +259,27 @@ export const types = {
CLEAR_PLAYER_INFO: 'CLEAR_PLAYER_INFO',
UPDATE_VIDEO_PLAY_STATE: 'UPDATE_VIDEO_PLAY_STATE',
// 🔽 [251116] 새로운 비디오 상태 관리 시스템 - 재생 상태
SET_PLAYBACK_LOADING: 'SET_PLAYBACK_LOADING',
SET_PLAYBACK_SUCCESS: 'SET_PLAYBACK_SUCCESS',
SET_PLAYBACK_ERROR: 'SET_PLAYBACK_ERROR',
SET_PLAYBACK_PLAYING: 'SET_PLAYBACK_PLAYING',
SET_PLAYBACK_NOT_PLAYING: 'SET_PLAYBACK_NOT_PLAYING',
SET_PLAYBACK_BUFFERING: 'SET_PLAYBACK_BUFFERING',
// 🔽 [251116] 새로운 비디오 상태 관리 시스템 - 화면 상태
SET_DISPLAY_HIDDEN: 'SET_DISPLAY_HIDDEN',
SET_DISPLAY_VISIBLE: 'SET_DISPLAY_VISIBLE',
SET_DISPLAY_MINIMIZED: 'SET_DISPLAY_MINIMIZED',
SET_DISPLAY_FULLSCREEN: 'SET_DISPLAY_FULLSCREEN',
// 🔽 [251116] 복합 상태 액션들
SET_VIDEO_LOADING: 'SET_VIDEO_LOADING',
SET_VIDEO_PLAYING: 'SET_VIDEO_PLAYING',
SET_VIDEO_STOPPED: 'SET_VIDEO_STOPPED',
SET_VIDEO_MINIMIZED_PLAYING: 'SET_VIDEO_MINIMIZED_PLAYING',
SET_VIDEO_ERROR: 'SET_VIDEO_ERROR',
// 🔽 [추가] 플레이 제어 매니저 액션 타입
/**
* 홈 화면 배너의 비디오 재생 제어를 위한 액션 타입.

View File

@@ -6,6 +6,24 @@ import { panel_names } from '../utils/Config';
import { types } from './actionTypes';
import { popPanel, pushPanel, updatePanel } from './panelActions';
// 🔽 [251116] 새로운 비디오 상태 관리 시스템 - 재생 상태
export const PLAYBACK_STATUS = {
LOADING: 'loading',
LOAD_SUCCESS: 'load_success',
LOAD_ERROR: 'load_error',
PLAYING: 'playing',
NOT_PLAYING: 'not_playing',
BUFFERING: 'buffering',
};
// 🔽 [251116] 새로운 비디오 상태 관리 시스템 - 화면 상태
export const DISPLAY_STATUS = {
HIDDEN: 'hidden',
VISIBLE: 'visible',
MINIMIZED: 'minimized',
FULLSCREEN: 'fullscreen',
};
//yhcho
/*
dispatch(startVideoPreview({
@@ -95,7 +113,15 @@ const shouldSkipVideoPlayback = (
};
export const startVideoPlayerNew =
({ modal, modalContainerId, modalClassName, spotlightDisable, useNewPlayer, bannerId, ...rest }) =>
({
modal,
modalContainerId,
modalClassName,
spotlightDisable,
useNewPlayer,
bannerId,
...rest
}) =>
(dispatch, getState) => {
const panels = getState().panels.panels;
const topPanel = panels[panels.length - 1];
@@ -179,9 +205,7 @@ export const finishAllVideoForce = () => (dispatch, getState) => {
const panels = getState().panels.panels;
// 모든 PlayerPanel이 존재하는지 확인 (스택 어디에 있든)
const hasPlayerPanel = panels.some(
(panel) => panel.name === panel_names.PLAYER_PANEL
);
const hasPlayerPanel = panels.some((panel) => panel.name === panel_names.PLAYER_PANEL);
if (hasPlayerPanel) {
if (startVideoFocusTimer) {
@@ -352,7 +376,10 @@ export const expandVideoFrom1px = () => (dispatch, getState) => {
// 축소된 modal PlayerPanel 찾기
const shrunkModalPlayerPanel = panels.find(
(panel) => panel.name === panel_names.PLAYER_PANEL && panel.panelInfo?.modal && panel.panelInfo?.shouldShrinkTo1px
(panel) =>
panel.name === panel_names.PLAYER_PANEL &&
panel.panelInfo?.modal &&
panel.panelInfo?.shouldShrinkTo1px
);
if (shrunkModalPlayerPanel) {
@@ -581,7 +608,6 @@ export const goToFullScreen = () => (dispatch, getState) => {
);
};
/**
* 영구재생 비디오를 일시정지 상태로 만듭니다. (내부 사용)
*/
@@ -617,3 +643,152 @@ export const returnToPreview = () => (dispatch, getState) => {
dispatch(finishVideoPreview());
}
};
/* 🔽 [251116] 새로운 비디오 상태 관리 시스템 - 액션 함수들 */
/**
* 비디오 로딩 시작 상태로 설정
* @param {string} videoId - 비디오 ID
* @param {string} displayMode - 화면 모드 ('visible', 'fullscreen', 'minimized')
*/
export const setPlaybackLoading = (videoId, displayMode = DISPLAY_STATUS.VISIBLE) => ({
type: types.SET_VIDEO_LOADING,
payload: {
playback: PLAYBACK_STATUS.LOADING,
display: displayMode,
videoId,
loadingProgress: 0,
loadingError: null,
lastUpdate: Date.now(),
},
});
/**
* 비디오 로딩 성공 상태로 설정
* @param {string} videoId - 비디오 ID
* @param {string} displayMode - 화면 모드
*/
export const setPlaybackSuccess = (videoId, displayMode = DISPLAY_STATUS.VISIBLE) => ({
type: types.SET_PLAYBACK_SUCCESS,
payload: {
playback: PLAYBACK_STATUS.LOAD_SUCCESS,
display: displayMode,
videoId,
loadingProgress: 100,
loadingError: null,
lastUpdate: Date.now(),
},
});
/**
* 비디오 로딩 에러 상태로 설정
* @param {string} videoId - 비디오 ID
* @param {object} error - 에러 정보
*/
export const setPlaybackError = (videoId, error) => ({
type: types.SET_VIDEO_ERROR,
payload: {
playback: PLAYBACK_STATUS.LOAD_ERROR,
display: DISPLAY_STATUS.VISIBLE,
videoId,
loadingProgress: 0,
loadingError: error,
lastUpdate: Date.now(),
},
});
/**
* 비디오 재생 상태로 설정
* @param {string} videoId - 비디오 ID
* @param {string} displayMode - 화면 모드
*/
export const setPlaybackPlaying = (videoId, displayMode = DISPLAY_STATUS.FULLSCREEN) => ({
type: types.SET_VIDEO_PLAYING,
payload: {
playback: PLAYBACK_STATUS.PLAYING,
display: displayMode,
videoId,
loadingProgress: 100,
loadingError: null,
lastUpdate: Date.now(),
},
});
/**
* 비디오 정지 상태로 설정
*/
export const setVideoStopped = () => ({
type: types.SET_VIDEO_STOPPED,
payload: {
playback: PLAYBACK_STATUS.NOT_PLAYING,
display: DISPLAY_STATUS.HIDDEN,
videoId: null,
loadingProgress: 0,
loadingError: null,
lastUpdate: Date.now(),
},
});
/**
* 비디오 버퍼링 상태로 설정
* @param {string} videoId - 비디오 ID
*/
export const setPlaybackBuffering = (videoId) => ({
type: types.SET_PLAYBACK_BUFFERING,
payload: {
playback: PLAYBACK_STATUS.BUFFERING,
videoId,
lastUpdate: Date.now(),
},
});
/**
* 최소화된 상태로 비디오 재생
* @param {string} videoId - 비디오 ID
*/
export const setVideoMinimizedPlaying = (videoId) => ({
type: types.SET_VIDEO_MINIMIZED_PLAYING,
payload: {
playback: PLAYBACK_STATUS.PLAYING,
display: DISPLAY_STATUS.MINIMIZED,
videoId,
loadingProgress: 100,
loadingError: null,
lastUpdate: Date.now(),
},
});
/**
* 화면 상태만 변경하는 액션들
*/
export const setDisplayHidden = () => ({
type: types.SET_DISPLAY_HIDDEN,
payload: {
display: DISPLAY_STATUS.HIDDEN,
lastUpdate: Date.now(),
},
});
export const setDisplayVisible = () => ({
type: types.SET_DISPLAY_VISIBLE,
payload: {
display: DISPLAY_STATUS.VISIBLE,
lastUpdate: Date.now(),
},
});
export const setDisplayMinimized = () => ({
type: types.SET_DISPLAY_MINIMIZED,
payload: {
display: DISPLAY_STATUS.MINIMIZED,
lastUpdate: Date.now(),
},
});
export const setDisplayFullscreen = () => ({
type: types.SET_DISPLAY_FULLSCREEN,
payload: {
display: DISPLAY_STATUS.FULLSCREEN,
lastUpdate: Date.now(),
},
});

View File

@@ -1,14 +1,24 @@
import { types } from '../actions/actionTypes';
import { PLAYBACK_STATUS, DISPLAY_STATUS } from '../actions/playActions';
const initialState = {
subTitleBlobs: {},
chatData: null,
videoPlayState: {
// 기존 상태들 유지
isPlaying: false,
isPaused: true,
currentTime: 0,
duration: 0,
playbackRate: 1,
// 🔽 [251116] 새로운 비디오 상태 관리 시스템
playback: PLAYBACK_STATUS.NOT_PLAYING, // 재생 상태
display: DISPLAY_STATUS.HIDDEN, // 화면 표시 상태
videoId: null, // 현재 비디오 ID
loadingProgress: 0, // 로딩 진행률 (0-100)
loadingError: null, // 로딩 에러 정보
lastUpdate: null, // 마지막 업데이트 시간
},
};
@@ -59,6 +69,193 @@ export const playReducer = (state = initialState, action) => {
};
}
// 🔽 [251116] 새로운 비디오 상태 관리 시스템 - 재생 상태 액션들
case types.SET_PLAYBACK_LOADING: {
return {
...state,
videoPlayState: {
...state.videoPlayState,
playback: PLAYBACK_STATUS.LOADING,
loadingProgress: 0,
loadingError: null,
lastUpdate: Date.now(),
},
};
}
case types.SET_PLAYBACK_SUCCESS: {
return {
...state,
videoPlayState: {
...state.videoPlayState,
playback: PLAYBACK_STATUS.LOAD_SUCCESS,
loadingProgress: 100,
loadingError: null,
lastUpdate: Date.now(),
},
};
}
case types.SET_PLAYBACK_ERROR: {
return {
...state,
videoPlayState: {
...state.videoPlayState,
playback: PLAYBACK_STATUS.LOAD_ERROR,
loadingProgress: 0,
loadingError: action.payload,
lastUpdate: Date.now(),
},
};
}
case types.SET_PLAYBACK_PLAYING: {
return {
...state,
videoPlayState: {
...state.videoPlayState,
playback: PLAYBACK_STATUS.PLAYING,
isPlaying: true,
isPaused: false,
loadingProgress: 100,
loadingError: null,
lastUpdate: Date.now(),
},
};
}
case types.SET_PLAYBACK_NOT_PLAYING: {
return {
...state,
videoPlayState: {
...state.videoPlayState,
playback: PLAYBACK_STATUS.NOT_PLAYING,
isPlaying: false,
isPaused: true,
loadingProgress: 0,
lastUpdate: Date.now(),
},
};
}
case types.SET_PLAYBACK_BUFFERING: {
return {
...state,
videoPlayState: {
...state.videoPlayState,
playback: PLAYBACK_STATUS.BUFFERING,
loadingError: null,
lastUpdate: Date.now(),
},
};
}
// 🔽 [251116] 새로운 비디오 상태 관리 시스템 - 화면 상태 액션들
case types.SET_DISPLAY_HIDDEN: {
return {
...state,
videoPlayState: {
...state.videoPlayState,
display: DISPLAY_STATUS.HIDDEN,
lastUpdate: Date.now(),
},
};
}
case types.SET_DISPLAY_VISIBLE: {
return {
...state,
videoPlayState: {
...state.videoPlayState,
display: DISPLAY_STATUS.VISIBLE,
lastUpdate: Date.now(),
},
};
}
case types.SET_DISPLAY_MINIMIZED: {
return {
...state,
videoPlayState: {
...state.videoPlayState,
display: DISPLAY_STATUS.MINIMIZED,
lastUpdate: Date.now(),
},
};
}
case types.SET_DISPLAY_FULLSCREEN: {
return {
...state,
videoPlayState: {
...state.videoPlayState,
display: DISPLAY_STATUS.FULLSCREEN,
lastUpdate: Date.now(),
},
};
}
// 🔽 [251116] 복합 상태 액션들
case types.SET_VIDEO_LOADING: {
return {
...state,
videoPlayState: {
...state.videoPlayState,
...action.payload,
isPlaying: false,
isPaused: true,
},
};
}
case types.SET_VIDEO_PLAYING: {
return {
...state,
videoPlayState: {
...state.videoPlayState,
...action.payload,
isPlaying: true,
isPaused: false,
},
};
}
case types.SET_VIDEO_STOPPED: {
return {
...state,
videoPlayState: {
...state.videoPlayState,
...action.payload,
isPlaying: false,
isPaused: true,
},
};
}
case types.SET_VIDEO_MINIMIZED_PLAYING: {
return {
...state,
videoPlayState: {
...state.videoPlayState,
...action.payload,
isPlaying: true,
isPaused: false,
},
};
}
case types.SET_VIDEO_ERROR: {
return {
...state,
videoPlayState: {
...state.videoPlayState,
...action.payload,
isPlaying: false,
isPaused: true,
},
};
}
default:
return state;
}

View File

@@ -87,8 +87,8 @@ export default function RandomUnit({
const isVideoTransitionLocked = useSelector((state) => state.home.videoTransitionLocked);
const timerRef = useRef();
const keepTimerOnBlurRef = useRef(false);
const hasAutoPlayStartedRef = useRef(false);
const hasPlaybackStartedRef = useRef(false);
const isDefaultAutoPlayTarget = defaultFocus === spotlightId;
const bannerDataRef = useRef(bannerData);
const randomDataRef = useRef(
@@ -105,6 +105,7 @@ export default function RandomUnit({
const handleStartVideo = useCallback(
(videoProps) => {
dispatch(setVideoTransitionLock(true));
hasPlaybackStartedRef.current = true;
dispatch(startVideoPlayerNew(videoProps));
},
[dispatch]
@@ -115,6 +116,14 @@ export default function RandomUnit({
dispatch(setVideoTransitionLock(false));
}, [dispatch]);
useEffect(() => {
if (currentVideoBannerId === spotlightId) {
hasPlaybackStartedRef.current = true;
} else {
hasPlaybackStartedRef.current = false;
}
}, [currentVideoBannerId, spotlightId]);
useEffect(() => {
if (isVideoTransitionLocked && isCurrentBannerVideoPlaying) {
dispatch(setVideoTransitionLock(false));
@@ -595,11 +604,10 @@ export default function RandomUnit({
}, [randomData]);
useEffect(() => {
if (isFocused && !videoError) {
if (isFocused && !videoError && !hasPlaybackStartedRef.current) {
if (timerRef.current) {
clearTimeout(timerRef.current);
}
keepTimerOnBlurRef.current = isDefaultAutoPlayTarget && !hasAutoPlayStartedRef.current;
timerRef.current = setTimeout(() => {
handleStartVideo({
bannerId: spotlightId,
@@ -617,30 +625,27 @@ export default function RandomUnit({
if (isDefaultAutoPlayTarget) {
hasAutoPlayStartedRef.current = true;
}
keepTimerOnBlurRef.current = false;
timerRef.current = null;
}, 1000);
}
if (!isFocused) {
setVideoError(false);
if (timerRef.current && !keepTimerOnBlurRef.current) {
if (timerRef.current && !hasPlaybackStartedRef.current) {
clearTimeout(timerRef.current);
timerRef.current = null;
}
}
return () => {
if (timerRef.current) {
if (timerRef.current && !hasPlaybackStartedRef.current) {
clearTimeout(timerRef.current);
timerRef.current = null;
}
keepTimerOnBlurRef.current = false;
};
}, [
isFocused,
videoError,
isHorizontal,
randomData,
dispatch,
isDefaultAutoPlayTarget,
spotlightId,
handleStartVideo,