[251111] fix: MediaPlayer FullScreen Overlay Toggle

🕐 커밋 시간: 2025. 11. 11. 19:08:01

📊 변경 통계:
  • 총 파일: 6개
  • 추가: +71줄
  • 삭제: -70줄

📁 추가된 파일:
  + com.twin.app.shoptime/src/actions/mediaOverlayActions.js
  + com.twin.app.shoptime/src/middleware/mediaAutoCloseMiddleware.js
  + com.twin.app.shoptime/src/reducers/mediaOverlayReducer.js

📝 수정된 파일:
  ~ com.twin.app.shoptime/src/components/VideoPlayer/MediaPlayer.v2.jsx
  ~ com.twin.app.shoptime/src/store/store.js
  ~ com.twin.app.shoptime/src/views/DetailPanel/ProductContentSection/ProductVideo/ProductVideo.v2.jsx

🔧 주요 변경 내용:
  • UI 컴포넌트 아키텍처 개선
  • 핵심 비즈니스 로직 개선
  • 소규모 기능 개선
  • 코드 정리 및 최적화
  • 모듈 구조 개선
This commit is contained in:
2025-11-11 19:08:04 +09:00
parent ff9fd163ac
commit 4b5d60252f
6 changed files with 448 additions and 70 deletions

View File

@@ -0,0 +1,70 @@
// Media Overlay Actions - Redux를 통한 MediaPanel/MediaPlayer overlay 제어
// 3 Layer 구조(MediaPanel + MediaPlayer) 전용 overlay 상태 관리
export const MEDIA_OVERLAY_ACTIONS = {
// Media Controls 표시/숨김
SET_MEDIA_CONTROLS_SHOW: 'SET_MEDIA_CONTROLS_SHOW',
SET_MEDIA_CONTROLS_HIDE: 'SET_MEDIA_CONTROLS_HIDE',
SET_MEDIA_CONTROLS_TOGGLE: 'SET_MEDIA_CONTROLS_TOGGLE',
// Media AutoClose 제어
START_MEDIA_AUTO_CLOSE: 'START_MEDIA_AUTO_CLOSE',
STOP_MEDIA_AUTO_CLOSE: 'STOP_MEDIA_AUTO_CLOSE',
RESET_MEDIA_AUTO_CLOSE: 'RESET_MEDIA_AUTO_CLOSE',
UPDATE_MEDIA_AUTO_CLOSE_TIMEOUT: 'UPDATE_MEDIA_AUTO_CLOSE_TIMEOUT',
// Media Overlay 세부 제어
SET_MEDIA_OVERLAY_VISIBILITY: 'SET_MEDIA_OVERLAY_VISIBILITY',
};
// Media Controls 표시/숨김 액션
export const setMediaControlShow = () => ({
type: MEDIA_OVERLAY_ACTIONS.SET_MEDIA_CONTROLS_SHOW,
payload: { timestamp: Date.now() },
});
export const setMediaControlHide = () => ({
type: MEDIA_OVERLAY_ACTIONS.SET_MEDIA_CONTROLS_HIDE,
payload: { timestamp: Date.now() },
});
export const setMediaControlToggle = () => ({
type: MEDIA_OVERLAY_ACTIONS.SET_MEDIA_CONTROLS_TOGGLE,
payload: { timestamp: Date.now() },
});
// Media AutoClose 타이머 제어 액션
export const startMediaAutoClose = (timeout = 3000) => ({
type: MEDIA_OVERLAY_ACTIONS.START_MEDIA_AUTO_CLOSE,
payload: {
timeout,
timestamp: Date.now(),
},
});
export const stopMediaAutoClose = () => ({
type: MEDIA_OVERLAY_ACTIONS.STOP_MEDIA_AUTO_CLOSE,
payload: { timestamp: Date.now() },
});
export const resetMediaAutoClose = () => ({
type: MEDIA_OVERLAY_ACTIONS.RESET_MEDIA_AUTO_CLOSE,
payload: { timestamp: Date.now() },
});
export const updateMediaAutoCloseTimeout = (timeout) => ({
type: MEDIA_OVERLAY_ACTIONS.UPDATE_MEDIA_AUTO_CLOSE_TIMEOUT,
payload: {
timeout,
timestamp: Date.now(),
},
});
// Media Overlay 세부 제어 액션
export const setMediaOverlayVisibility = (overlayConfig) => ({
type: MEDIA_OVERLAY_ACTIONS.SET_MEDIA_OVERLAY_VISIBILITY,
payload: {
...overlayConfig,
timestamp: Date.now(),
},
});

View File

@@ -26,6 +26,15 @@ import { MediaSlider, Times, secondsToTime } from '../MediaPlayer';
import Overlay from './Overlay';
import Media from './Media';
import TReactPlayer from './TReactPlayer';
import { useDispatch, useSelector } from 'react-redux';
import {
setMediaControlShow,
setMediaControlHide,
setMediaControlToggle,
startMediaAutoClose,
stopMediaAutoClose,
resetMediaAutoClose
} from '../../actions/mediaOverlayActions';
import css from './MediaPlayer.module.less';
@@ -68,6 +77,10 @@ const getDurFmt = () => {
* MediaPlayer.v2 컴포넌트
*/
const MediaPlayerV2 = forwardRef((props, ref) => {
// Redux hooks
const dispatch = useDispatch();
const mediaOverlayState = useSelector((state) => state.mediaOverlay);
const {
// 비디오 소스
src,
@@ -123,11 +136,13 @@ const MediaPlayerV2 = forwardRef((props, ref) => {
const [paused, setPaused] = useState(!autoPlay);
const [loading, setLoading] = useState(true);
const [error, setError] = useState(null);
const [controlsVisible, setControlsVisible] = useState(false);
const [sourceUnavailable, setSourceUnavailable] = useState(true);
const [proportionLoaded, setProportionLoaded] = useState(0);
const [proportionPlayed, setProportionPlayed] = useState(0);
// controlsVisible은 Redux mediaOverlay 상태에서 가져오기
const controlsVisible = mediaOverlayState?.controls?.visible || false;
// ========== Refs ==========
const videoRef = useRef(null);
const playerRef = useRef(null);
@@ -267,32 +282,25 @@ const MediaPlayerV2 = forwardRef((props, ref) => {
const showControls = useCallback((timeout = 3000) => {
if (disabled || isModal) return;
setControlsVisible(true);
// timeout 후 자동 숨김 (기본 3초, Modal 전환 시 10초)
if (controlsTimeoutRef.current) {
clearTimeout(controlsTimeoutRef.current);
}
controlsTimeoutRef.current = setTimeout(() => {
setControlsVisible(false);
}, timeout);
}, [disabled, isModal]);
console.log('🎬 [MediaPlayer.v2] showControls called, dispatching setMediaControlShow');
dispatch(setMediaControlShow());
dispatch(startMediaAutoClose(timeout));
}, [disabled, isModal, dispatch]);
const hideControls = useCallback(() => {
setControlsVisible(false);
console.log('🎬 [MediaPlayer.v2] hideControls called, dispatching setMediaControlHide');
dispatch(setMediaControlHide());
dispatch(stopMediaAutoClose());
if (controlsTimeoutRef.current) {
clearTimeout(controlsTimeoutRef.current);
controlsTimeoutRef.current = null;
}
}, []);
}, [dispatch]);
const toggleControls = useCallback(() => {
if (controlsVisible) {
hideControls();
} else {
showControls();
}
}, [controlsVisible, hideControls, showControls]);
console.log('🎬 [MediaPlayer.v2] toggleControls called, dispatching setMediaControlToggle');
dispatch(setMediaControlToggle());
}, [dispatch]);
// ========== Playback Control ==========
const play = useCallback(() => {
@@ -420,8 +428,10 @@ const MediaPlayerV2 = forwardRef((props, ref) => {
if (controlsTimeoutRef.current) {
clearTimeout(controlsTimeoutRef.current);
}
// MediaPlayer 언마운트 시 Redux 상태 정리
dispatch(stopMediaAutoClose());
};
}, []);
}, [dispatch]);
// ========== Imperative Handle (API) ==========
useImperativeHandle(ref, () => ({

View File

@@ -0,0 +1,139 @@
// Media Auto Close Middleware - Redux를 통한 MediaPanel/MediaPlayer controls 자동 숨김 타이머 관리
// 3 Layer 구조(MediaPanel + MediaPlayer) 전용 autoClose 기능 제공
import {
MEDIA_OVERLAY_ACTIONS,
setMediaControlHide,
stopMediaAutoClose,
} from '../actions/mediaOverlayActions';
// 타이머 저장소
let mediaAutoCloseTimer = null;
/**
* Media Auto Close Middleware
* - START_MEDIA_AUTO_CLOSE: 타이머 시작
* - STOP_MEDIA_AUTO_CLOSE, SET_MEDIA_CONTROLS_HIDE: 타이머 중지
* - SET_MEDIA_CONTROLS_TOGGLE: Controls 표시 상태로 토글되면 타이머 시작
* - 사용자 활동 감지 시: 타이머 리셋
*/
export const mediaAutoCloseMiddleware = (store) => (next) => (action) => {
const result = next(action);
// 현재 상태 획득
const state = store.getState();
const mediaOverlayState = state.mediaOverlay;
// Action별 처리
switch (action.type) {
case MEDIA_OVERLAY_ACTIONS.START_MEDIA_AUTO_CLOSE: {
// 기존 타이머 정리
clearTimeout(mediaAutoCloseTimer);
const timeout = action.payload?.timeout || 3000;
console.log('[mediaAutoCloseMiddleware] Starting media auto-close timer:', timeout, 'ms');
// 새 타이머 시작
mediaAutoCloseTimer = setTimeout(() => {
console.log('[mediaAutoCloseMiddleware] Media auto-close timeout reached - hiding media controls');
store.dispatch(setMediaControlHide());
mediaAutoCloseTimer = null;
}, timeout);
break;
}
case MEDIA_OVERLAY_ACTIONS.STOP_MEDIA_AUTO_CLOSE: {
if (mediaAutoCloseTimer) {
console.log('[mediaAutoCloseMiddleware] Stopping media auto-close timer');
clearTimeout(mediaAutoCloseTimer);
mediaAutoCloseTimer = null;
}
break;
}
case MEDIA_OVERLAY_ACTIONS.SET_MEDIA_CONTROLS_HIDE: {
// Media Controls 숨김 시 타이머도 중지
if (mediaAutoCloseTimer) {
clearTimeout(mediaAutoCloseTimer);
mediaAutoCloseTimer = null;
}
break;
}
case MEDIA_OVERLAY_ACTIONS.SET_MEDIA_CONTROLS_TOGGLE: {
// Media Controls가 표시 상태로 토글되면 타이머 시작
if (!mediaOverlayState.controls.visible) {
// 다음 상태에서 visible이 true가 될 것이므로
// 타이머를 시작해야 함
clearTimeout(mediaAutoCloseTimer);
const timeout = mediaOverlayState.autoClose.timeout || 3000;
console.log('[mediaAutoCloseMiddleware] Media controls toggled to visible - starting timer:', timeout, 'ms');
mediaAutoCloseTimer = setTimeout(() => {
console.log('[mediaAutoCloseMiddleware] Toggle media auto-close timeout reached - hiding media controls');
store.dispatch(setMediaControlHide());
mediaAutoCloseTimer = null;
}, timeout);
} else {
// Media Controls 숨김 상태로 토글되면 타이머 중지
if (mediaAutoCloseTimer) {
clearTimeout(mediaAutoCloseTimer);
mediaAutoCloseTimer = null;
}
}
break;
}
case MEDIA_OVERLAY_ACTIONS.RESET_MEDIA_AUTO_CLOSE: {
// 타이머 리셋 (사용자 활동 감지)
clearTimeout(mediaAutoCloseTimer);
if (mediaOverlayState.controls.visible) {
const timeout = mediaOverlayState.autoClose.timeout || 3000;
console.log('[mediaAutoCloseMiddleware] Resetting media auto-close timer:', timeout, 'ms');
mediaAutoCloseTimer = setTimeout(() => {
console.log('[mediaAutoCloseMiddleware] Reset media auto-close timeout reached - hiding media controls');
store.dispatch(setMediaControlHide());
mediaAutoCloseTimer = null;
}, timeout);
}
break;
}
case MEDIA_OVERLAY_ACTIONS.SET_MEDIA_CONTROLS_SHOW: {
// Media Controls 표시 시 타이머 시작
clearTimeout(mediaAutoCloseTimer);
const timeout = mediaOverlayState.autoClose.timeout || 3000;
console.log('[mediaAutoCloseMiddleware] Media controls shown - starting timer:', timeout, 'ms');
mediaAutoCloseTimer = setTimeout(() => {
console.log('[mediaAutoCloseMiddleware] Show media auto-close timeout reached - hiding media controls');
store.dispatch(setMediaControlHide());
mediaAutoCloseTimer = null;
}, timeout);
break;
}
default:
break;
}
return result;
};
/**
* Cleanup 함수 (필요시 호출)
* - 앱 종료 시 타이머 정리
*/
export const cleanupMediaAutoCloseMiddleware = () => {
if (mediaAutoCloseTimer) {
clearTimeout(mediaAutoCloseTimer);
mediaAutoCloseTimer = null;
}
};

View File

@@ -0,0 +1,168 @@
// Media Overlay Reducer - 3 Layer 구조(MediaPanel + MediaPlayer)용 overlay 상태 관리
import { MEDIA_OVERLAY_ACTIONS } from '../actions/mediaOverlayActions';
export const initialState = {
// Media Overlay Controls 상태
controls: {
visible: false, // media controls 표시 여부
sliderVisible: false, // 슬라이더 표시 여부
titleVisible: true, // 제목 표시 여부
feedbackVisible: false, // 피드백 표시 여부
},
// Media AutoClose 타이머 제어
autoClose: {
enabled: true, // autoClose 활성화 여부
timeout: 3000, // 타임아웃 시간 (ms)
remainingTime: 0, // 남은 시간 (ms)
active: false, // 타이머 실행 중 여부
},
// 미디어 정보
mediaInfo: {
url: '',
title: '',
thumbnailUrl: '',
isYoutube: false,
},
// 상태 변경 기록 (디버깅용)
timestamp: null,
lastAction: null,
};
/**
* Reducer handlers 맵
*/
const handlers = {
[MEDIA_OVERLAY_ACTIONS.SET_MEDIA_CONTROLS_SHOW]: (state, action) => ({
...state,
controls: {
...state.controls,
visible: true,
sliderVisible: true,
titleVisible: true,
},
autoClose: {
...state.autoClose,
active: true, // Controls 표시 시 autoClose 시작
},
timestamp: action.payload.timestamp,
lastAction: MEDIA_OVERLAY_ACTIONS.SET_MEDIA_CONTROLS_SHOW,
}),
[MEDIA_OVERLAY_ACTIONS.SET_MEDIA_CONTROLS_HIDE]: (state, action) => ({
...state,
controls: {
...state.controls,
visible: false,
sliderVisible: false,
feedbackVisible: false,
},
autoClose: {
...state.autoClose,
active: false, // Controls 숨김 시 autoClose 중지
remainingTime: 0,
},
timestamp: action.payload.timestamp,
lastAction: MEDIA_OVERLAY_ACTIONS.SET_MEDIA_CONTROLS_HIDE,
}),
[MEDIA_OVERLAY_ACTIONS.SET_MEDIA_CONTROLS_TOGGLE]: (state, action) => {
const isCurrentlyVisible = state.controls.visible;
const newState = {
...state,
controls: {
...state.controls,
visible: !isCurrentlyVisible,
sliderVisible: !isCurrentlyVisible,
titleVisible: !isCurrentlyVisible || state.controls.titleVisible,
},
autoClose: {
...state.autoClose,
active: !isCurrentlyVisible, // 표시 상태로 토글되면 autoClose 시작
},
timestamp: action.payload.timestamp,
lastAction: MEDIA_OVERLAY_ACTIONS.SET_MEDIA_CONTROLS_TOGGLE,
};
console.log('🔄 [mediaOverlayReducer.SET_MEDIA_CONTROLS_TOGGLE] 상태 변화');
console.log('🔄 [mediaOverlayReducer.SET_MEDIA_CONTROLS_TOGGLE] 이전 visible:', isCurrentlyVisible);
console.log('🔄 [mediaOverlayReducer.SET_MEDIA_CONTROLS_TOGGLE] 이후 visible:', newState.controls.visible);
console.log('🔄 [mediaOverlayReducer.SET_MEDIA_CONTROLS_TOGGLE] 전체 상태:', newState);
return newState;
},
[MEDIA_OVERLAY_ACTIONS.START_MEDIA_AUTO_CLOSE]: (state, action) => ({
...state,
autoClose: {
...state.autoClose,
enabled: true,
timeout: action.payload.timeout || state.autoClose.timeout,
remainingTime: action.payload.timeout || state.autoClose.timeout,
active: true,
},
timestamp: action.payload.timestamp,
lastAction: MEDIA_OVERLAY_ACTIONS.START_MEDIA_AUTO_CLOSE,
}),
[MEDIA_OVERLAY_ACTIONS.STOP_MEDIA_AUTO_CLOSE]: (state, action) => ({
...state,
autoClose: {
...state.autoClose,
active: false,
remainingTime: 0,
},
timestamp: action.payload.timestamp,
lastAction: MEDIA_OVERLAY_ACTIONS.STOP_MEDIA_AUTO_CLOSE,
}),
[MEDIA_OVERLAY_ACTIONS.RESET_MEDIA_AUTO_CLOSE]: (state, action) => ({
...state,
autoClose: {
...state.autoClose,
remainingTime: state.autoClose.timeout,
active: true,
},
timestamp: action.payload.timestamp,
lastAction: MEDIA_OVERLAY_ACTIONS.RESET_MEDIA_AUTO_CLOSE,
}),
[MEDIA_OVERLAY_ACTIONS.UPDATE_MEDIA_AUTO_CLOSE_TIMEOUT]: (state, action) => ({
...state,
autoClose: {
...state.autoClose,
timeout: action.payload.timeout,
remainingTime: action.payload.timeout,
},
timestamp: action.payload.timestamp,
lastAction: MEDIA_OVERLAY_ACTIONS.UPDATE_MEDIA_AUTO_CLOSE_TIMEOUT,
}),
[MEDIA_OVERLAY_ACTIONS.SET_MEDIA_OVERLAY_VISIBILITY]: (state, action) => ({
...state,
controls: {
...state.controls,
...action.payload,
},
timestamp: action.payload.timestamp,
lastAction: MEDIA_OVERLAY_ACTIONS.SET_MEDIA_OVERLAY_VISIBILITY,
}),
};
/**
* Main Reducer
* @param {Object} state - 현재 상태
* @param {Object} action - Redux action
* @returns {Object} 다음 상태
*/
export const mediaOverlayReducer = (state = initialState, action = {}) => {
const handler = handlers[action.type];
if (handler) {
return handler(state, action);
}
return state;
};

View File

@@ -2,6 +2,7 @@ import { applyMiddleware, combineReducers, createStore } from 'redux';
import thunk from 'redux-thunk';
import { autoCloseMiddleware } from '../middleware/autoCloseMiddleware';
import { mediaAutoCloseMiddleware } from '../middleware/mediaAutoCloseMiddleware';
import { panelHistoryMiddleware } from '../middleware/panelHistoryMiddleware';
import panelQueueMiddleware from '../middleware/panelQueueMiddleware';
import { appDataReducer } from '../reducers/appDataReducer';
@@ -36,6 +37,7 @@ import { searchReducer } from '../reducers/searchReducer';
import { shippingReducer } from '../reducers/shippingReducer';
import { toastReducer } from '../reducers/toastReducer';
import { videoOverlayReducer } from '../reducers/videoOverlayReducer';
import { mediaOverlayReducer } from '../reducers/mediaOverlayReducer';
import { videoPlayReducer } from '../reducers/videoPlayReducer';
import { voiceReducer } from '../reducers/voiceReducer';
@@ -69,6 +71,7 @@ const rootReducer = combineReducers({
foryou: foryouReducer,
toast: toastReducer,
videoOverlay: videoOverlayReducer,
mediaOverlay: mediaOverlayReducer,
videoPlay: videoPlayReducer,
voice: voiceReducer,
convert: convertReducer,
@@ -76,5 +79,5 @@ const rootReducer = combineReducers({
export const store = createStore(
rootReducer,
applyMiddleware(thunk, panelHistoryMiddleware, autoCloseMiddleware, panelQueueMiddleware)
applyMiddleware(thunk, panelHistoryMiddleware, autoCloseMiddleware, mediaAutoCloseMiddleware, panelQueueMiddleware)
);

View File

@@ -11,13 +11,19 @@ import playImg from '../../../../../assets/images/btn/btn-play-thumb-nor.png';
import {
switchToModal,
switchToFullscreen,
showControls,
hideControls,
toggleControls,
startAutoClose,
stopAutoClose,
resetAutoClose,
} from '../../../../actions/videoOverlayActions';
import {
setMediaControlShow,
setMediaControlHide,
setMediaControlToggle,
startMediaAutoClose,
stopMediaAutoClose,
resetMediaAutoClose,
} from '../../../../actions/mediaOverlayActions';
import { pauseFullscreenVideo, resumeFullscreenVideo, clearAllVideoTimers } from '../../../../actions/playActions';
import css from './ProductVideo.module.less';
@@ -60,6 +66,7 @@ export function ProductVideoV2({
// Redux 상태 및 dispatch
const dispatch = useDispatch();
const overlayState = useSelector((state) => state.videoOverlay);
const mediaOverlayState = useSelector((state) => state.mediaOverlay);
// Local state
const [isPlaying, setIsPlaying] = useState(false);
@@ -160,13 +167,12 @@ export function ProductVideoV2({
// console.log('[BgVideo] ProductVideoV2 - Pausing background fullscreen video');
dispatch(pauseFullscreenVideo());
// Redux: 오버레이 상태 초기화
dispatch(showControls());
dispatch(startAutoClose(3000));
// MediaPlayer 직접 제어: controls 표시 및 autoClose 시작
// Redux: mediaOverlay 상태 초기화 (MediaPlayer 전용)
dispatch(setMediaControlShow());
dispatch(startMediaAutoClose(3000));
// MediaPlayer 직접 제어: controls 표시
setTimeout(() => {
videoPlayerRef.current?.showControls?.();
videoPlayerRef.current?.startAutoCloseTimeout?.();
}, 100);
}
}, [canPlayVideo, isPlaying, dispatch]);
@@ -181,11 +187,10 @@ export function ProductVideoV2({
// console.log('[BgVideo] ProductVideoV2 - Resuming background fullscreen video');
dispatch(resumeFullscreenVideo());
// Redux: 오버레이 상태 정리
dispatch(stopAutoClose());
dispatch(hideControls());
// MediaPlayer 직접 제어: autoClose 타이머 중지 및 controls 숨김
videoPlayerRef.current?.stopAutoCloseTimeout?.();
// Redux: mediaOverlay 상태 정리
dispatch(stopMediaAutoClose());
dispatch(setMediaControlHide());
// MediaPlayer 직접 제어: controls 숨김
videoPlayerRef.current?.hideControls?.();
}, [dispatch]);
@@ -220,10 +225,9 @@ export function ProductVideoV2({
// console.log('[BgVideo] ProductVideoV2 - Resuming background fullscreen video');
dispatch(resumeFullscreenVideo());
dispatch(stopAutoClose());
dispatch(hideControls());
// MediaPlayer 직접 제어: autoClose 타이머 중지 및 controls 숨김
videoPlayerRef.current?.stopAutoCloseTimeout?.();
dispatch(stopMediaAutoClose());
dispatch(setMediaControlHide());
// MediaPlayer 직접 제어: controls 숨김
videoPlayerRef.current?.hideControls?.();
}
}, [isFullscreen, isPlaying, dispatch]);
@@ -231,9 +235,8 @@ export function ProductVideoV2({
// 사용자 활동 감지 시 autoClose 타이머 리셋
const handleUserActivity = useCallback(() => {
if (isPlaying && !isFullscreen) {
// console.log('[ProductVideoV2] User activity detected - resetting autoClose timer');
dispatch(resetAutoClose());
videoPlayerRef.current?.startAutoCloseTimeout?.();
console.log('🎬 [ProductVideoV2] User activity detected - resetting mediaAutoClose timer');
dispatch(resetMediaAutoClose());
}
}, [isPlaying, isFullscreen, dispatch]);
@@ -321,8 +324,7 @@ export function ProductVideoV2({
// FullScreen 모드에서의 MediaPlayer Click 핸들러
const handleVideoPlayerClick = useCallback(
(e) => {
console.log('>>>>>>>>>>>>>>handleVideoPlayerClick');
(e) => {
if (!isPlaying) return;
if (!isFullscreen) {
@@ -330,54 +332,40 @@ export function ProductVideoV2({
e.stopPropagation?.();
toggleFullscreen();
return;
}
console.log('>>>>>>>>>>>>>>handleVideoPlayerClick-1');
}
// fullscreen: overlay toggle
e.preventDefault?.();
e.stopPropagation?.();
console.log('>>>>>>>>>>>>>>handleVideoPlayerClick-2');
// Redux overlayState 사용
const isCurrentlyVisible = overlayState.controls?.visible;
// VideoPlayer API로 실제 상태 확인
const isCurrentlyVisible = videoPlayerRef.current?.areControlsVisible?.();
if (isCurrentlyVisible) {
console.log('>>>>>>>>>>>>>>handleVideoPlayerClick-2-1 isCurrentlyVisible-true', isCurrentlyVisible);
dispatch(hideControls());
videoPlayerRef.current.hideControls?.();
videoPlayerRef.current?.hideControls?.();
dispatch(setMediaControlHide()); // MediaOverlay Redux 상태 동기화
} else {
console.log('>>>>>>>>>>>>>>handleVideoPlayerClick-2-2 isCurrentlyVisible-false', isCurrentlyVisible);
dispatch(showControls());
videoPlayerRef.current.showControls?.();
videoPlayerRef.current.startAutoCloseTimeout?.();
videoPlayerRef.current?.showControls?.();
dispatch(setMediaControlShow()); // MediaOverlay Redux 상태 동기화
}
},
[isPlaying, isFullscreen, toggleFullscreen, dispatch, overlayState.controls?.visible]
[isPlaying, isFullscreen, toggleFullscreen, dispatch, mediaOverlayState.controls?.visible]
);
// 오버레이 토글 통합 함수 (ESC와 동일한 효과)
const toggleOverlayVisibility = useCallback(() => {
console.log('🖥️ [toggleOverlayVisibility] 오버레이 토글 시작');
console.log('🖥️ [toggleOverlayVisibility] 현재 오버레이 상태:', overlayState.controls?.visible);
const isOverlayVisible = overlayState.controls?.visible;
const isOverlayVisible = mediaOverlayState.controls?.visible;
if (isOverlayVisible) {
console.log('🖥️ [toggleOverlayVisibility] 오버레이 숨기기 (ESC와 동일)');
dispatch(hideControls());
dispatch(setMediaControlHide());
videoPlayerRef.current?.hideControls?.();
} else {
console.log('🖥️ [toggleOverlayVisibility] 오버레이 보이기');
dispatch(showControls());
dispatch(setMediaControlShow());
videoPlayerRef.current?.showControls?.();
videoPlayerRef.current?.startAutoCloseTimeout?.();
}
}, [overlayState.controls?.visible, dispatch]);
}, [mediaOverlayState.controls?.visible, dispatch]);
// 전체 화면 컨테이너용 마우스 다운 핸들러 (Capture Phase)
const handleFullscreenContainerMouseDownCapture = useCallback((e) => {
if (!isPlaying || !isFullscreen) return;
console.log('🖥️ [Fullscreen Container] 마우스 클릭 - 토글 실행');
e.preventDefault();
e.stopPropagation();
// toggleOverlayVisibility();
@@ -512,8 +500,8 @@ export function ProductVideoV2({
dispatch(resumeFullscreenVideo());
}
dispatch(stopAutoClose());
dispatch(hideControls());
dispatch(stopMediaAutoClose());
dispatch(setMediaControlHide());
};
}, [dispatch, isPlaying]);