diff --git a/com.twin.app.shoptime/[251116]_video_state_management_design.md b/com.twin.app.shoptime/[251116]_video_state_management_design.md
new file mode 100644
index 00000000..c96fd3f2
--- /dev/null
+++ b/com.twin.app.shoptime/[251116]_video_state_management_design.md
@@ -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 ;
+ }
+
+ if (playback === PLAYBACK_STATUS.LOAD_ERROR) {
+ return ;
+ }
+
+ if (playback === PLAYBACK_STATUS.BUFFERING) {
+ return ;
+ }
+
+ if (playback === PLAYBACK_STATUS.PLAYING && display === DISPLAY_STATUS.FULLSCREEN) {
+ return ;
+ }
+
+ return null;
+};
+```
+
+## 구현 순서
+
+1. [ ] playerActions.js에 상수 및 액션 함수들 추가
+2. [ ] playerReducer.js에 초기 상태 및 핸들러들 추가
+3. [ ] PlayerPanel.jsx에서 새로운 상태 시스템으로 전환
+4. [ ] VideoPlayer.js에서 새로운 상태 시스템으로 전환
+5. [ ] 테스트 및 검증
+
+## 장점
+
+1. **정밀한 상태 제어**: 재생 상태와 화면 상태를 별도로 관리
+2. **명확한 상태 의미**: 각 상태가 명확한 의미를 가짐
+3. **확장성**: 새로운 상태 추가가 용이
+4. **유지보수성**: 기존 코드 영향 최소화
+5. **재사용성**: 다른 컴포넌트에서도 활용 가능
+
+## 주의사항
+
+- 기존 videoPlayReducer와 충돌하지 않도록 주의
+- PlayerPanel과 VideoPlayer.js에만 집중하여 구현
+- 기존 비디오 재생 로직과 호환성 유지
\ No newline at end of file
diff --git a/com.twin.app.shoptime/src/actions/actionTypes.js b/com.twin.app.shoptime/src/actions/actionTypes.js
index b2bc63ea..0507bedb 100644
--- a/com.twin.app.shoptime/src/actions/actionTypes.js
+++ b/com.twin.app.shoptime/src/actions/actionTypes.js
@@ -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',
+
// 🔽 [추가] 플레이 제어 매니저 액션 타입
/**
* 홈 화면 배너의 비디오 재생 제어를 위한 액션 타입.
diff --git a/com.twin.app.shoptime/src/actions/playActions.js b/com.twin.app.shoptime/src/actions/playActions.js
index 463074a6..2abd75c8 100644
--- a/com.twin.app.shoptime/src/actions/playActions.js
+++ b/com.twin.app.shoptime/src/actions/playActions.js
@@ -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];
@@ -146,7 +172,7 @@ export const startVideoPlayerNew =
export const finishVideoPreview = () => (dispatch, getState) => {
const panels = getState().panels.panels;
- const topPanel = panels[panels.length-1];
+ const topPanel = panels[panels.length - 1];
if (topPanel && topPanel.name === panel_names.PLAYER_PANEL && topPanel.panelInfo.modal) {
if (startVideoFocusTimer) {
clearTimeout(startVideoFocusTimer);
@@ -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(),
+ },
+});
diff --git a/com.twin.app.shoptime/src/reducers/playReducer.js b/com.twin.app.shoptime/src/reducers/playReducer.js
index 579a5ea6..62e62914 100644
--- a/com.twin.app.shoptime/src/reducers/playReducer.js
+++ b/com.twin.app.shoptime/src/reducers/playReducer.js
@@ -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;
}
diff --git a/com.twin.app.shoptime/src/views/HomePanel/HomeBanner/RandomUnit.jsx b/com.twin.app.shoptime/src/views/HomePanel/HomeBanner/RandomUnit.jsx
index c562fc03..7b7720b5 100644
--- a/com.twin.app.shoptime/src/views/HomePanel/HomeBanner/RandomUnit.jsx
+++ b/com.twin.app.shoptime/src/views/HomePanel/HomeBanner/RandomUnit.jsx
@@ -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,