|
|
|
|
@@ -91,6 +91,51 @@ const findSelector = (selector, maxAttempts = 5, currentAttempts = 0) => {
|
|
|
|
|
}
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
// 배너 위치 수집 함수 (top, left만 저장)
|
|
|
|
|
const collectBannerPositions = () => {
|
|
|
|
|
const positions = [];
|
|
|
|
|
|
|
|
|
|
// banner0, banner1 등의 배너 위치 수집
|
|
|
|
|
for (let i = 0; i < 10; i++) {
|
|
|
|
|
const bannerId = `banner${i}`;
|
|
|
|
|
const node = document.querySelector(`[data-spotlight-id="${bannerId}"]`);
|
|
|
|
|
|
|
|
|
|
if (node) {
|
|
|
|
|
const { top, left } = node.getBoundingClientRect();
|
|
|
|
|
positions.push({
|
|
|
|
|
bannerId,
|
|
|
|
|
position: { top: Math.round(top), left: Math.round(left) }
|
|
|
|
|
});
|
|
|
|
|
dlog(`[PlayerPanel] 배너 위치 수집: ${bannerId}`, { top: Math.round(top), left: Math.round(left) });
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
return positions;
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
// 위치 검증 함수 (오차 범위: 1px)
|
|
|
|
|
const isPositionMatching = (bannerPositions, bannerId, currentPosition) => {
|
|
|
|
|
const validPosition = bannerPositions.find(p => p.bannerId === bannerId);
|
|
|
|
|
|
|
|
|
|
if (!validPosition) {
|
|
|
|
|
dlog(`[PlayerPanel] 배너 위치 검증 실패: ${bannerId} 배너를 찾을 수 없음`);
|
|
|
|
|
return false;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
const tolerance = 1; // 1px 오차 범위
|
|
|
|
|
const isMatching =
|
|
|
|
|
Math.abs(currentPosition.top - validPosition.position.top) <= tolerance &&
|
|
|
|
|
Math.abs(currentPosition.left - validPosition.position.left) <= tolerance;
|
|
|
|
|
|
|
|
|
|
dlog(`[PlayerPanel] 배너 위치 검증: ${bannerId}`, {
|
|
|
|
|
expected: validPosition.position,
|
|
|
|
|
current: currentPosition,
|
|
|
|
|
matching: isMatching
|
|
|
|
|
});
|
|
|
|
|
|
|
|
|
|
return isMatching;
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
const getLogTpNo = (type, nowMenu) => {
|
|
|
|
|
if (type === 'LIVE') {
|
|
|
|
|
switch (nowMenu) {
|
|
|
|
|
@@ -219,6 +264,7 @@ const PlayerPanel = ({ isTabActivated, panelInfo, isOnTop, spotlightId, ...props
|
|
|
|
|
const [tabIndexV2, setTabIndexV2] = USE_STATE('tabIndexV2', 1); // 0: ShopNow, 1: LiveChannel, 2: ShopNowButton
|
|
|
|
|
const [tabContainerVersion, setTabContainerVersion] = USE_STATE('tabContainerVersion', 2); // 1: TabContainer (우측), 2: TabContainerV2 (하단)
|
|
|
|
|
const [isModalClosed, setIsModalClosed] = USE_STATE('isModalClosed', true); // 모달이 false 상태인지 나타내는 플래그
|
|
|
|
|
const [validBannerPositions, setValidBannerPositions] = USE_STATE('validBannerPositions', []); // 유효한 배너 위치 (top, left)
|
|
|
|
|
|
|
|
|
|
const panels = USE_SELECTOR('panels', (state) => state.panels.panels);
|
|
|
|
|
const chatData = USE_SELECTOR('chatData', (state) => state.play.chatData);
|
|
|
|
|
@@ -1202,12 +1248,12 @@ const PlayerPanel = ({ isTabActivated, panelInfo, isOnTop, spotlightId, ...props
|
|
|
|
|
return () => {
|
|
|
|
|
// 패널이 2개 존재할때만 popPanel 진행
|
|
|
|
|
// 현재 스택의 top이 PlayerPanel일 때만 pop 수행 (다른 패널이 올라온 상태에서 오작동 방지)
|
|
|
|
|
console.log('[PP-TRACE] cleanup start', {
|
|
|
|
|
modal: panelInfo.modal,
|
|
|
|
|
isOnTop,
|
|
|
|
|
topPanel: panels[panels.length - 1]?.name,
|
|
|
|
|
stack: panels.map((p) => p.name),
|
|
|
|
|
});
|
|
|
|
|
// console.log('[PP-TRACE] cleanup start', {
|
|
|
|
|
// modal: panelInfo.modal,
|
|
|
|
|
// isOnTop,
|
|
|
|
|
// topPanel: panels[panels.length - 1]?.name,
|
|
|
|
|
// stack: panels.map((p) => p.name),
|
|
|
|
|
// });
|
|
|
|
|
|
|
|
|
|
// 🔽 [251221] PlayerPanel unmount 시 DeepLink 플래그 리셋
|
|
|
|
|
dispatch(
|
|
|
|
|
@@ -1226,7 +1272,7 @@ const PlayerPanel = ({ isTabActivated, panelInfo, isOnTop, spotlightId, ...props
|
|
|
|
|
topPanelName === panel_names.PLAYER_PANEL &&
|
|
|
|
|
panels.length === 1 // 다른 패널 존재 시 pop 금지 (DetailPanel 제거 방지)
|
|
|
|
|
) {
|
|
|
|
|
console.log('[PP-TRACE] popPanel - useEffect cleanup (top is PlayerPanel)');
|
|
|
|
|
// console.log('[PP-TRACE] popPanel - useEffect cleanup (top is PlayerPanel)');
|
|
|
|
|
dispatch(PanelActions.popPanel());
|
|
|
|
|
} else {
|
|
|
|
|
Spotlight.focus('tbody');
|
|
|
|
|
@@ -1799,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);
|
|
|
|
|
};
|
|
|
|
|
}, []);
|
|
|
|
|
|
|
|
|
|
@@ -1937,6 +1984,30 @@ const PlayerPanel = ({ isTabActivated, panelInfo, isOnTop, spotlightId, ...props
|
|
|
|
|
panelInfo: { modalStyle: modalStyle, modalScale: scale },
|
|
|
|
|
})
|
|
|
|
|
);
|
|
|
|
|
|
|
|
|
|
// 🔽 배너 위치 수집 (초기 로드 시에만 실행)
|
|
|
|
|
if (validBannerPositions.length === 0) {
|
|
|
|
|
const positions = collectBannerPositions();
|
|
|
|
|
if (positions.length > 0) {
|
|
|
|
|
setValidBannerPositions(positions);
|
|
|
|
|
dlog('[PlayerPanel] ✅ 배너 위치 초기 수집 완료:', positions);
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// 🔽 배너 위치 검증 (위치가 맞지 않으면 비디오 재생 중단)
|
|
|
|
|
if (validBannerPositions.length > 0) {
|
|
|
|
|
const currentPosition = { top: Math.round(top), left: Math.round(left) };
|
|
|
|
|
const isValidPosition = isPositionMatching(validBannerPositions, panelInfo.modalContainerId, currentPosition);
|
|
|
|
|
|
|
|
|
|
if (!isValidPosition) {
|
|
|
|
|
dlog('[PlayerPanel] ⚠️ 배너 위치 검증 실패 - 비디오 재생 중단', {
|
|
|
|
|
bannerId: panelInfo.modalContainerId,
|
|
|
|
|
currentPosition,
|
|
|
|
|
validBannerPositions
|
|
|
|
|
});
|
|
|
|
|
return; // 비디오 재생 중단
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
} else {
|
|
|
|
|
dlog('[PlayerPanel] Condition 1: Node not found, using saved modalStyle');
|
|
|
|
|
setModalStyle(panelInfo.modalStyle);
|
|
|
|
|
@@ -2275,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) {
|
|
|
|
|
@@ -2323,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) {
|
|
|
|
|
@@ -2465,14 +2536,105 @@ const PlayerPanel = ({ isTabActivated, panelInfo, isOnTop, spotlightId, ...props
|
|
|
|
|
);
|
|
|
|
|
|
|
|
|
|
const onKeyDown = (ev) => {
|
|
|
|
|
if (ev.keyCode === 34) {
|
|
|
|
|
// tabIndex === 1 (LiveChannelContents 표시)이고 비디오 배너에 포커스가 있는 경우
|
|
|
|
|
const currentFocused = Spotlight.getCurrent();
|
|
|
|
|
const spotlightId = currentFocused?.getAttribute('data-spotlight-id');
|
|
|
|
|
const isVideoItemFocused = spotlightId?.startsWith('tabChannel-video');
|
|
|
|
|
|
|
|
|
|
// LiveChannelContents의 비디오 배너에 포커스가 있는 경우: PageUp/PageDown을 좌우 이동으로 변환
|
|
|
|
|
if (tabIndexV2 === 1 && isVideoItemFocused) {
|
|
|
|
|
// DOM에서 실제로 렌더링된 모든 비디오 배너 찾기 (가상화 대응)
|
|
|
|
|
const allVideoBanners = Array.from(
|
|
|
|
|
document.querySelectorAll('[data-spotlight-id^="tabChannel-video-"]')
|
|
|
|
|
);
|
|
|
|
|
|
|
|
|
|
if (allVideoBanners.length > 0) {
|
|
|
|
|
// 현재 포커스된 배너의 인덱스 찾기
|
|
|
|
|
const currentBannerIndex = allVideoBanners.findIndex(
|
|
|
|
|
(el) => el === currentFocused
|
|
|
|
|
);
|
|
|
|
|
|
|
|
|
|
if (currentBannerIndex !== -1) {
|
|
|
|
|
if (ev.keyCode === 34) { // PageDown -> 오른쪽 배너로 포커스 이동
|
|
|
|
|
ev.stopPropagation();
|
|
|
|
|
ev.preventDefault();
|
|
|
|
|
|
|
|
|
|
// DOM에 렌더링된 다음 배너로 이동 (마지막이면 무시 또는 첫 번째로)
|
|
|
|
|
if (currentBannerIndex < allVideoBanners.length - 1) {
|
|
|
|
|
// 다음 배너가 DOM에 있으면 이동
|
|
|
|
|
const nextBanner = allVideoBanners[currentBannerIndex + 1];
|
|
|
|
|
const nextSpotlightId = nextBanner.getAttribute('data-spotlight-id');
|
|
|
|
|
|
|
|
|
|
dlog('[PlayerPanel] 🎯 PageDown (비디오 배너) -> 오른쪽으로 이동', {
|
|
|
|
|
current: spotlightId,
|
|
|
|
|
next: nextSpotlightId,
|
|
|
|
|
currentBannerIndex,
|
|
|
|
|
totalVisibleBanners: allVideoBanners.length,
|
|
|
|
|
});
|
|
|
|
|
|
|
|
|
|
Spotlight.focus(nextSpotlightId);
|
|
|
|
|
} else {
|
|
|
|
|
// 마지막 배너면 첫 번째로 이동 시도 (DOM에 있으면)
|
|
|
|
|
const firstBanner = allVideoBanners[0];
|
|
|
|
|
const firstSpotlightId = firstBanner.getAttribute('data-spotlight-id');
|
|
|
|
|
|
|
|
|
|
dlog('[PlayerPanel] 🎯 PageDown (마지막 배너) -> 첫 번째 배너로 이동 시도', {
|
|
|
|
|
current: spotlightId,
|
|
|
|
|
next: firstSpotlightId,
|
|
|
|
|
isWrapAround: true,
|
|
|
|
|
});
|
|
|
|
|
|
|
|
|
|
Spotlight.focus(firstSpotlightId);
|
|
|
|
|
}
|
|
|
|
|
return;
|
|
|
|
|
} else if (ev.keyCode === 33) { // PageUp -> 왼쪽 배너로 포커스 이동
|
|
|
|
|
ev.stopPropagation();
|
|
|
|
|
ev.preventDefault();
|
|
|
|
|
|
|
|
|
|
// DOM에 렌더링된 이전 배너로 이동 (첫 번째면 무시 또는 마지막으로)
|
|
|
|
|
if (currentBannerIndex > 0) {
|
|
|
|
|
// 이전 배너가 DOM에 있으면 이동
|
|
|
|
|
const prevBanner = allVideoBanners[currentBannerIndex - 1];
|
|
|
|
|
const prevSpotlightId = prevBanner.getAttribute('data-spotlight-id');
|
|
|
|
|
|
|
|
|
|
dlog('[PlayerPanel] 🎯 PageUp (비디오 배너) -> 왼쪽으로 이동', {
|
|
|
|
|
current: spotlightId,
|
|
|
|
|
prev: prevSpotlightId,
|
|
|
|
|
currentBannerIndex,
|
|
|
|
|
totalVisibleBanners: allVideoBanners.length,
|
|
|
|
|
});
|
|
|
|
|
|
|
|
|
|
Spotlight.focus(prevSpotlightId);
|
|
|
|
|
} else {
|
|
|
|
|
// 첫 번째 배너면 마지막으로 이동 시도 (DOM에 있으면)
|
|
|
|
|
const lastBanner = allVideoBanners[allVideoBanners.length - 1];
|
|
|
|
|
const lastSpotlightId = lastBanner.getAttribute('data-spotlight-id');
|
|
|
|
|
|
|
|
|
|
dlog('[PlayerPanel] 🎯 PageUp (첫 번째 배너) -> 마지막 배너로 이동 시도', {
|
|
|
|
|
current: spotlightId,
|
|
|
|
|
prev: lastSpotlightId,
|
|
|
|
|
isWrapAround: true,
|
|
|
|
|
});
|
|
|
|
|
|
|
|
|
|
Spotlight.focus(lastSpotlightId);
|
|
|
|
|
}
|
|
|
|
|
return;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// 기존 로직: LiveChannelButton 또는 다른 경우에는 상/하 이동
|
|
|
|
|
if (ev.keyCode === 34) { // PageDown
|
|
|
|
|
handleIndicatorDownClick();
|
|
|
|
|
ev.stopPropagation();
|
|
|
|
|
ev.preventDefault();
|
|
|
|
|
} else if (ev.keyCode === 33) {
|
|
|
|
|
dlog('[PlayerPanel] 📺 PageDown (버튼 또는 다른 경우) -> 다음 비디오');
|
|
|
|
|
} else if (ev.keyCode === 33) { // PageUp
|
|
|
|
|
handleIndicatorUpClick();
|
|
|
|
|
ev.stopPropagation();
|
|
|
|
|
ev.preventDefault();
|
|
|
|
|
dlog('[PlayerPanel] 📺 PageUp (버튼 또는 다른 경우) -> 이전 비디오');
|
|
|
|
|
}
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
@@ -2483,6 +2645,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 &&
|
|
|
|
|
@@ -2594,17 +2761,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(() => {
|
|
|
|
|
// 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);
|
|
|
|
|
}, timeout);
|
|
|
|
|
}
|
|
|
|
|
} else {
|
|
|
|
|
// 활동이 감지되면 경과 시간 리셋
|
|
|
|
|
dlog('[PlayerPanel] 🔄 Activity detected - resetting elapsed time', {
|
|
|
|
|
previousElapsed: elapsedTime,
|
|
|
|
|
});
|
|
|
|
|
elapsedTime = 0;
|
|
|
|
|
}
|
|
|
|
|
}, 100);
|
|
|
|
|
},
|
|
|
|
|
[clearTimerTabAutoAdvance]
|
|
|
|
|
[clearTimerTabAutoAdvance, isInactive]
|
|
|
|
|
);
|
|
|
|
|
|
|
|
|
|
// Redux로 오버레이 숨김
|
|
|
|
|
@@ -2629,6 +2841,10 @@ const PlayerPanel = ({ isTabActivated, panelInfo, isOnTop, spotlightId, ...props
|
|
|
|
|
if (timerIdTabAutoAdvance.current) {
|
|
|
|
|
clearTimerTabAutoAdvance();
|
|
|
|
|
}
|
|
|
|
|
if (activityCheckIntervalRef.current) {
|
|
|
|
|
clearInterval(activityCheckIntervalRef.current);
|
|
|
|
|
activityCheckIntervalRef.current = null;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
dispatch(resetPlayerOverlays());
|
|
|
|
|
}
|
|
|
|
|
@@ -2843,6 +3059,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일 때만 실행
|
|
|
|
|
@@ -2869,6 +3132,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}`);
|
|
|
|
|
|
|
|
|
|
|