[251119] fix: PlayerPanel - 1

🕐 커밋 시간: 2025. 11. 19. 10:15:17

📊 변경 통계:
  • 총 파일: 4개
  • 추가: +141줄
  • 삭제: -38줄

📝 수정된 파일:
  ~ com.twin.app.shoptime/src/views/PlayerPanel/PlayerOverlay/PlayerOverlayContents.jsx
  ~ com.twin.app.shoptime/src/views/PlayerPanel/PlayerPanel.jsx
  ~ com.twin.app.shoptime/src/views/PlayerPanel/PlayerTabContents/TabContents/ShopNowContents.jsx
  ~ com.twin.app.shoptime/src/views/PlayerPanel/PlayerTabContents/TabContents/YouMayLikeContents.jsx

🔧 주요 변경 내용:
  • 중간 규모 기능 개선
This commit is contained in:
2025-11-19 10:15:18 +09:00
parent cac42de0ca
commit cb0764b3ac
4 changed files with 143 additions and 40 deletions

View File

@@ -64,7 +64,7 @@ import TabContainerV2 from './PlayerTabContents/v2/TabContainer.v2';
// import ShopNowButton from './PlayerTabContents/v2/ShopNowButton';
const Container = SpotlightContainerDecorator(
{ enterTo: 'default-element', preserveld: true },
{ enterTo: 'last-focused', preserveId: true },
'div'
);
@@ -172,6 +172,7 @@ const PlayerPanel = ({ isTabActivated, panelInfo, isOnTop, spotlightId, ...props
});
const videoPlayer = useRef(null);
const prevModal = usePrevious(panelInfo?.modal);
const [playListInfo, setPlayListInfo] = USE_STATE('playListInfo', '');
const [shopNowInfo, setShopNowInfo] = USE_STATE('shopNowInfo');
const [backupInitialIndex, setBackupInitialIndex] = USE_STATE('backupInitialIndex', 0);
@@ -369,6 +370,25 @@ const PlayerPanel = ({ isTabActivated, panelInfo, isOnTop, spotlightId, ...props
}
}, [panelInfo?.isPaused]);
// Modal 상태 변화 감지 (true → false)
useEffect(() => {
if (prevModal !== undefined && prevModal === true && panelInfo?.modal === false) {
console.log('[PlayerPanel] 🔄 Modal 상태 변화: true → false (전체화면 모드로 복귀)');
console.log('[PlayerPanel] 🎯 포커스 복원 준비 - lastFocusedTargetId:', panelInfo?.lastFocusedTargetId);
// DetailPanel에서 복귀 시 포커스 복원
const lastFocusedTargetId = panelInfo?.lastFocusedTargetId;
if (lastFocusedTargetId) {
// ShopNowContents가 렌더링될 때까지 대기 후 포커스 복원
setTimeout(() => {
console.log('[PlayerPanel] 🔍 800ms 후 포커스 복원 시도:', lastFocusedTargetId);
Spotlight.focus(lastFocusedTargetId);
}, 800);
}
}
}, [panelInfo?.modal, prevModal, panelInfo?.lastFocusedTargetId]);
// creating live log params
useEffect(() => {
if (currentLiveShowInfo && Object.keys(currentLiveShowInfo).length > 0) {
@@ -1867,6 +1887,8 @@ const PlayerPanel = ({ isTabActivated, panelInfo, isOnTop, spotlightId, ...props
const [initialEnterV2, setInitialEnterV2] = USE_STATE('initialEnterV2', true);
const timerId = useRef(null);
const timerIdV2 = useRef(null);
const timerIdTabAutoAdvance = useRef(null);
const prevTabIndexV2 = useRef(null);
const showSideContents = useMemo(() => {
return (
@@ -1949,45 +1971,76 @@ const PlayerPanel = ({ isTabActivated, panelInfo, isOnTop, spotlightId, ...props
const resetTimerV2 = useCallback(
(timeout) => {
// console.log('[TabContainerV2] resetTimerV2 호출', timeout);
console.log('[TabContainerV2] resetTimerV2 호출', timeout);
if (timerIdV2.current) {
// console.log('[TabContainerV2] 기존 타이머 클리어');
console.log('[TabContainerV2] 기존 타이머 클리어');
clearTimerV2();
}
if (initialEnterV2) {
// console.log('[TabContainerV2] initialEnterV2 false로 변경');
console.log('[TabContainerV2] initialEnterV2 false로 변경');
setInitialEnterV2(false);
}
timerIdV2.current = setTimeout(() => {
// console.log('[TabContainerV2] 타이머 실행 - belowContentsVisible false로 변경');
console.log('[TabContainerV2] 타이머 실행 - belowContentsVisible false로 변경 (30초 경과)');
setBelowContentsVisible(false);
}, timeout);
},
[clearTimerV2, initialEnterV2, setInitialEnterV2, setBelowContentsVisible]
);
const clearTimerTabAutoAdvance = useCallback(() => {
clearTimeout(timerIdTabAutoAdvance.current);
timerIdTabAutoAdvance.current = null;
}, []);
const resetTimerTabAutoAdvance = useCallback(
(timeout) => {
if (timerIdTabAutoAdvance.current) {
clearTimerTabAutoAdvance();
}
timerIdTabAutoAdvance.current = setTimeout(() => {
setTabIndexV2(2);
}, timeout);
},
[clearTimerTabAutoAdvance]
);
// Redux로 오버레이 숨김
useEffect(() => {
if (shouldHideOverlays) {
console.log('[PlayerPanel] shouldHideOverlays true - 오버레이 숨김');
setSideContentsVisible(false);
console.log('[setBelowContentsVisible] Redux로 오버레이 숨김 - false로 변경');
setBelowContentsVisible(false);
if (videoPlayer.current?.hideControls) {
videoPlayer.current.hideControls();
}
// 모든 타이머 클리어
if (timerId.current) {
clearTimer();
}
if (timerIdV2.current) {
clearTimerV2();
}
if (timerIdTabAutoAdvance.current) {
clearTimerTabAutoAdvance();
}
dispatch(resetPlayerOverlays());
}
}, [shouldHideOverlays, dispatch]);
}, [shouldHideOverlays, dispatch, clearTimer, clearTimerV2, clearTimerTabAutoAdvance]);
// Redux로 오버레이 표시
useEffect(() => {
if (shouldShowOverlays) {
console.log('[PlayerPanel] shouldShowOverlays true - 오버레이 표시');
setSideContentsVisible(true);
console.log('[setBelowContentsVisible] Redux로 오버레이 표시 - true로 변경');
setBelowContentsVisible(true);
if (videoPlayer.current?.showControls) {
@@ -2001,12 +2054,25 @@ const PlayerPanel = ({ isTabActivated, panelInfo, isOnTop, spotlightId, ...props
// PlayerPanel이 최상단이 될 때 오버레이 표시 (DetailPanel에서 복귀)
useEffect(() => {
if (isOnTop && !panelInfo.modal && !videoVerticalVisible) {
console.log('[PlayerPanel] isOnTop true - 오버레이 표시');
console.log('[PlayerPanel] ✅ DetailPanel에서 복귀함! - 오버레이 표시');
setSideContentsVisible(true);
console.log('[setBelowContentsVisible] DetailPanel에서 복귀 - true로 변경');
setBelowContentsVisible(true);
// VideoPlayer가 belowContentsVisible prop을 감지해서 자동으로 controls 표시함
// DetailPanel에서 복귀 시 포커스 복원 시도
const lastFocusedTargetId = panelInfo?.lastFocusedTargetId;
console.log('[PlayerPanel] 🎯 DetailPanel 복귀 - lastFocusedTargetId:', lastFocusedTargetId);
if (lastFocusedTargetId) {
// ShopNowContents가 렌더링될 때까지 잠시 대기 후 포커스 복원
setTimeout(() => {
console.log('[PlayerPanel] 🔍 500ms 후 포커스 복원 시도:', lastFocusedTargetId);
Spotlight.focus(lastFocusedTargetId);
}, 500);
}
}
}, [isOnTop, panelInfo.modal, videoVerticalVisible]);
}, [isOnTop, panelInfo.modal, videoVerticalVisible, panelInfo?.lastFocusedTargetId]);
useEffect(() => {
// tabContainerVersion === 1일 때만 실행
@@ -2055,54 +2121,71 @@ const PlayerPanel = ({ isTabActivated, panelInfo, isOnTop, spotlightId, ...props
};
}, [sideContentsVisible]);
// TabContainerV2 자동 닫기
// TabContainerV2 자동 닫기 (tabIndex 1 → 2 변경 감지)
useEffect(() => {
// tabContainerVersion === 2일 때만 실행
if (tabContainerVersion !== 2) return;
// console.log('[TabContainerV2] useEffect 시작', {
// showBelowContents,
// videoVerticalVisible,
// initialEnterV2,
// });
const node = document.querySelector(`[data-spotlight-id=${TAB_CONTAINER_V2_SPOTLIGHT_ID}]`);
// console.log('[TabContainerV2] DOM node:', node);
if (!showBelowContents || !node || videoVerticalVisible) {
// console.log('[TabContainerV2] early return');
if (tabContainerVersion !== 2) {
prevTabIndexV2.current = tabIndexV2;
return;
}
// NOTE 탭이 표시될 때마다 타이머 시작 (첫 진입은 30초, 이후에는 REGULAR_TIMEOUT)
resetTimerV2(initialEnterV2 ? INITIAL_TIMEOUT : REGULAR_TIMEOUT);
// tabIndexV2가 1에서 2로 정확하게 변경되는 시점만 감지
const isTransitionedTo2 = prevTabIndexV2.current === 1 && tabIndexV2 === 2;
prevTabIndexV2.current = tabIndexV2;
const handleEvent = (e) => {
// console.log('[TabContainerV2] 이벤트 발생:', e.type);
resetTimerV2(REGULAR_TIMEOUT);
};
TARGET_EVENTS.forEach((event) => {
// console.log('[TabContainerV2] 이벤트 리스너 등록:', event);
node.addEventListener(event, handleEvent);
});
if (!isTransitionedTo2) {
if (timerIdV2.current) {
console.log('[TabContainerV2] 타이머 클리어 - tabIndex가 2가 아님', tabIndexV2);
clearTimerV2();
}
return;
}
console.log('[TabContainerV2] tabIndex 1 → 2 감지, 타이머 시작');
if (!belowContentsVisible || videoVerticalVisible) {
console.log('[TabContainerV2] early return - belowContentsVisible 또는 videoVerticalVisible 조건 불만족');
return;
}
// tabIndex 1 → 2로 변경된 정확한 시점에 30초 타이머 시작
console.log('[TabContainerV2] 30초 타이머 시작');
resetTimerV2(REGULAR_TIMEOUT);
return () => {
// console.log('[TabContainerV2] cleanup');
TARGET_EVENTS.forEach((event) => node.removeEventListener(event, handleEvent));
if (timerIdV2.current) {
clearTimerV2();
}
};
}, [
showBelowContents,
videoVerticalVisible,
tabContainerVersion,
tabIndexV2,
belowContentsVisible,
videoVerticalVisible,
resetTimerV2,
initialEnterV2,
clearTimerV2,
]);
// TabIndex 1 자동 다음 단계로 이동
useEffect(() => {
// tabIndex === 1일 때만 실행
if (tabIndexV2 !== 1 || !belowContentsVisible || videoVerticalVisible) {
if (timerIdTabAutoAdvance.current) {
clearTimerTabAutoAdvance();
}
return;
}
// 10초 후 tabIndex를 2로 변경
resetTimerTabAutoAdvance(10000);
return () => {
if (timerIdTabAutoAdvance.current) {
clearTimerTabAutoAdvance();
}
};
}, [tabIndexV2, belowContentsVisible, videoVerticalVisible, resetTimerTabAutoAdvance, clearTimerTabAutoAdvance]);
useLayoutEffect(() => {
const videoContainer = document.querySelector(`.${css.videoContainer}`);
@@ -2383,7 +2466,7 @@ const PlayerPanel = ({ isTabActivated, panelInfo, isOnTop, spotlightId, ...props
tabIndex={tabIndexV2}
onShopNowButtonClick={() => setTabIndexV2(0)}
onLiveChannelButtonClick={() => setTabIndexV2(2)}
onLiveNext={handleIndicatorDownClick}
onLiveNext={() => setTabIndexV2(1)}
onTabClose={(newTabIndex) => setTabIndexV2(newTabIndex)}
tabVisible={belowContentsVisible}
/>