diff --git a/com.twin.app.shoptime/src/views/PlayerPanel/PlayerPanel.jsx b/com.twin.app.shoptime/src/views/PlayerPanel/PlayerPanel.jsx index ec7e7134..8d80a0a1 100644 --- a/com.twin.app.shoptime/src/views/PlayerPanel/PlayerPanel.jsx +++ b/com.twin.app.shoptime/src/views/PlayerPanel/PlayerPanel.jsx @@ -1845,6 +1845,7 @@ const PlayerPanel = ({ isTabActivated, panelInfo, isOnTop, spotlightId, ...props if (watchIntervalLive.current) clearInterval(watchIntervalLive.current); if (watchIntervalVod.current) clearInterval(watchIntervalVod.current); if (watchIntervalMedia.current) clearInterval(watchIntervalMedia.current); + if (activityCheckIntervalRef.current) clearInterval(activityCheckIntervalRef.current); }; }, []); @@ -2345,7 +2346,7 @@ const PlayerPanel = ({ isTabActivated, panelInfo, isOnTop, spotlightId, ...props setPrevChannelIndex(selectedIndex); } setSideContentsVisible(true); - }, [dispatch, playListInfo, selectedIndex, sideContentsVisible, initialEnter]); + }, [dispatch, playListInfo, selectedIndex, sideContentsVisible, initialEnter, tabContainerVersion, tabIndexV2]); const handleIndicatorUpClick = useCallback(() => { if (!initialEnter) { @@ -2393,7 +2394,7 @@ const PlayerPanel = ({ isTabActivated, panelInfo, isOnTop, spotlightId, ...props setPrevChannelIndex(selectedIndex); } setSideContentsVisible(true); - }, [dispatch, playListInfo, selectedIndex, sideContentsVisible, initialEnter]); + }, [dispatch, playListInfo, selectedIndex, sideContentsVisible, initialEnter, tabContainerVersion, tabIndexV2]); useEffect(() => { if (panelInfo.shptmBanrTpNm === 'VOD' && panelInfo.patnrId && panelInfo.showId) { @@ -2553,6 +2554,11 @@ const PlayerPanel = ({ isTabActivated, panelInfo, isOnTop, spotlightId, ...props const timerIdTabAutoAdvance = useRef(null); const prevTabIndexV2 = useRef(null); + // Activity Check for tabIndex auto-advance + const lastActivityTimeRef = useRef(Date.now()); + const activityCheckIntervalRef = useRef(null); + const ACTIVITY_TIMEOUT = 1000; // 1초 동안 활동이 없으면 타이머 진행 + const showSideContents = useMemo(() => { return ( sideContentsVisible && @@ -2664,17 +2670,62 @@ const PlayerPanel = ({ isTabActivated, panelInfo, isOnTop, spotlightId, ...props timerIdTabAutoAdvance.current = null; }, []); + // Activity 감지 함수 + const onActivityDetected = useCallback(() => { + lastActivityTimeRef.current = Date.now(); + dlog('[PlayerPanel] 🎯 Activity detected - timer will be delayed', { + timestamp: new Date().toISOString(), + }); + }, []); + + // Activity 여부를 확인하는 함수 (1초 타임아웃 체크) + const isInactive = useCallback(() => { + const now = Date.now(); + const timeSinceLastActivity = now - lastActivityTimeRef.current; + return timeSinceLastActivity > ACTIVITY_TIMEOUT; + }, []); + const resetTimerTabAutoAdvance = useCallback( (timeout) => { if (timerIdTabAutoAdvance.current) { clearTimerTabAutoAdvance(); } - timerIdTabAutoAdvance.current = setTimeout(() => { - setTabIndexV2(2); - }, timeout); + // Activity check interval 설정 (매 100ms마다 체크) + if (activityCheckIntervalRef.current) { + clearInterval(activityCheckIntervalRef.current); + } + + let elapsedTime = 0; + + activityCheckIntervalRef.current = setInterval(() => { + // 활동이 없을 때만 경과 시간 증가 + if (isInactive()) { + elapsedTime += 100; + dlog('[PlayerPanel] ⏱️ TabIndex auto-advance: inactive', { + elapsedTime, + requiredTime: timeout, + }); + + // 필요한 시간만큼 경과했으면 타이머 실행 + if (elapsedTime >= timeout) { + dlog('[PlayerPanel] ✅ TabIndex auto-advance executing - setTabIndexV2(2)', { + totalElapsed: elapsedTime, + timeout, + }); + clearInterval(activityCheckIntervalRef.current); + setTabIndexV2(2); + } + } else { + // 활동이 감지되면 경과 시간 리셋 + dlog('[PlayerPanel] 🔄 Activity detected - resetting elapsed time', { + previousElapsed: elapsedTime, + }); + elapsedTime = 0; + } + }, 100); }, - [clearTimerTabAutoAdvance] + [clearTimerTabAutoAdvance, isInactive] ); // Redux로 오버레이 숨김 @@ -2699,6 +2750,10 @@ const PlayerPanel = ({ isTabActivated, panelInfo, isOnTop, spotlightId, ...props if (timerIdTabAutoAdvance.current) { clearTimerTabAutoAdvance(); } + if (activityCheckIntervalRef.current) { + clearInterval(activityCheckIntervalRef.current); + activityCheckIntervalRef.current = null; + } dispatch(resetPlayerOverlays()); } @@ -2913,6 +2968,53 @@ const PlayerPanel = ({ isTabActivated, panelInfo, isOnTop, spotlightId, ...props panelInfo?.modal, ]); + // PageUp/PageDown으로 비디오 변경 시 현재 재생 배너로 포커스 이동 + useEffect(() => { + if (tabContainerVersion === 2 && + tabIndexV2 === 1 && + panelInfo?.isIndicatorByClick && + selectedIndex !== null && + selectedIndex >= 0) { + + dlog('[PlayerPanel] 🎯 PageUp/PageDown 후 포커스 이동 준비', { + selectedIndex, + tabContainerVersion, + tabIndexV2, + isIndicatorByClick: panelInfo.isIndicatorByClick + }); + + const bannerSpotlightId = `banner${selectedIndex}`; + + setTimeout(() => { + dlog('[PlayerPanel] 🔍 포커스 이동 시도:', bannerSpotlightId); + + const bannerElement = document.querySelector(`[data-spotlight-id="${bannerSpotlightId}"]`); + + if (bannerElement) { + dlog('[PlayerPanel] ✅ 배너 요소 찾음, 포커스 이동 실행'); + Spotlight.focus(bannerElement); + } else { + dlog('[PlayerPanel] ⚠️ 배너 요소 찾지 못함:', bannerSpotlightId); + // 모든 배너 요소 목록 출력 + const allBanners = document.querySelectorAll('[data-spotlight-id^="banner"]'); + dlog('[PlayerPanel] 🔍 사용 가능한 배너 목록:', + Array.from(allBanners).map(el => el.getAttribute('data-spotlight-id')) + ); + } + + // 플래그 리셋 + dispatch( + updatePanel({ + name: panel_names.PLAYER_PANEL, + panelInfo: { + isIndicatorByClick: false + }, + }) + ); + }, 200); // DOM 업데이트 대기 + } + }, [selectedIndex, tabContainerVersion, tabIndexV2, panelInfo?.isIndicatorByClick, dispatch]); + // TabIndex 1 자동 다음 단계로 이동 useEffect(() => { // tabIndex === 1일 때만 실행 @@ -2939,6 +3041,31 @@ const PlayerPanel = ({ isTabActivated, panelInfo, isOnTop, spotlightId, ...props clearTimerTabAutoAdvance, ]); + // Activity detection for tabIndex auto-advance (mousemove, keydown, click) + useEffect(() => { + // tabIndex === 1일 때만 Activity 감지 활성화 + if (tabIndexV2 !== 1 || !belowContentsVisible) { + return; + } + + dlog('[PlayerPanel] 🎙️ Activity listener registered for tabIndex=1'); + + const handleMouseMove = onActivityDetected; + const handleKeyDown = onActivityDetected; + const handleClick = onActivityDetected; + + document.addEventListener('mousemove', handleMouseMove); + document.addEventListener('keydown', handleKeyDown); + document.addEventListener('click', handleClick); + + return () => { + dlog('[PlayerPanel] 🎙️ Activity listener unregistered'); + document.removeEventListener('mousemove', handleMouseMove); + document.removeEventListener('keydown', handleKeyDown); + document.removeEventListener('click', handleClick); + }; + }, [tabIndexV2, belowContentsVisible, onActivityDetected]); + useLayoutEffect(() => { const videoContainer = document.querySelector(`.${css.videoContainer}`);