3 Commits

Author SHA1 Message Date
07a042cca6 [251217] fix: PlayerPanel 배너동영상 위치 검증추가
🕐 커밋 시간: 2025. 12. 17. 12:03:44

📊 변경 통계:
  • 총 파일: 1개
  • 추가: +70줄

📝 수정된 파일:
  ~ com.twin.app.shoptime/src/views/PlayerPanel/PlayerPanel.jsx

🔧 주요 변경 내용:
  • 소규모 기능 개선
2025-12-17 12:03:44 +09:00
junghoon86.park
d93960f40a [영상]
- 프로그레스바 크기변경
 - 타임 노출부분 변경
 - cc 버튼 위치변경
2025-12-17 10:38:36 +09:00
4dfa15b4c0 [251217] fix: TabContainer.v2.jsx ShopNowButton 포커스 10ms
🕐 커밋 시간: 2025. 12. 17. 09:13:23

📊 변경 통계:
  • 총 파일: 2개
  • 추가: +5줄
  • 삭제: -1줄

📝 수정된 파일:
  ~ com.twin.app.shoptime/src/views/DetailPanel/ProductContentSection/ProductDescription/ProductDescription.jsx
  ~ com.twin.app.shoptime/src/views/PlayerPanel/PlayerTabContents/v2/TabContainer.v2.jsx
2025-12-17 09:13:23 +09:00
8 changed files with 92 additions and 9 deletions

View File

@@ -34,7 +34,7 @@
padding: @slider-padding-v 0; padding: @slider-padding-v 0;
height: @sand-mediaplayer-slider-height; height: @sand-mediaplayer-slider-height;
right: 154px; right: 154px;
width: 1466px; width: 1558px;
// Add a tap area that extends to the edges of the screen, to make the slider more accessible // Add a tap area that extends to the edges of the screen, to make the slider more accessible
&::before { &::before {
content: ""; content: "";

View File

@@ -7,7 +7,7 @@
position: absolute; position: absolute;
font-family: @baseFont; font-family: @baseFont;
width: 100%; width: 100%;
right: 90px; right: 20px;
bottom: -5px; bottom: -5px;
font-size: 24px; font-size: 24px;
font-weight: bold; font-weight: bold;
@@ -16,12 +16,12 @@
letter-spacing: -1px; letter-spacing: -1px;
.separator { .separator {
position: absolute; position: absolute;
right: 105px; right: 95px;
bottom: -5px; bottom: -5px;
} }
.currentTime { .currentTime {
position: absolute; position: absolute;
right: 130px; right: 120px;
bottom: -5px; bottom: -5px;
} }
.totalTime { .totalTime {

View File

@@ -76,6 +76,10 @@ export default function ProductDescription({ productInfo }) {
const _onClose = useCallback(()=>{ const _onClose = useCallback(()=>{
dispatch(setHidePopup()); dispatch(setHidePopup());
// Restore focus to the description content after popup closes
setTimeout(() => {
Spotlight.focus('product-description-content');
}, 100);
},[dispatch]) },[dispatch])
// ProductDescription: Container 직접 사용 패턴 // ProductDescription: Container 직접 사용 패턴

View File

@@ -390,7 +390,7 @@ function PlayerOverlayContents({
e.preventDefault(); e.preventDefault();
// tabIndexV2가 2일 때만 LiveChannelNext로 포커스 // tabIndexV2가 2일 때만 LiveChannelNext로 포커스
if (tabContainerVersion === 2 && tabIndexV2 === 2) { if (tabContainerVersion === 2 && tabIndexV2 === 2) {
Spotlight.focus('live-channel-next-button'); Spotlight.focus('below-tab-shop-now-button');
} }
}} }}
onSpotlightDown={(e) => { onSpotlightDown={(e) => {

View File

@@ -13,8 +13,8 @@
background-image: url("../../../../assets/images/btn/btn-video-cc-nor@3x.png"); background-image: url("../../../../assets/images/btn/btn-video-cc-nor@3x.png");
background-size: cover; background-size: cover;
position: absolute; position: absolute;
right: 60px; right: 300px;
top: 800px; top: 680px;
z-index: 10; z-index: 10;
&.videoVericalSubtitleButton { &.videoVericalSubtitleButton {

View File

@@ -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) => { const getLogTpNo = (type, nowMenu) => {
if (type === 'LIVE') { if (type === 'LIVE') {
switch (nowMenu) { 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 [tabIndexV2, setTabIndexV2] = USE_STATE('tabIndexV2', 1); // 0: ShopNow, 1: LiveChannel, 2: ShopNowButton
const [tabContainerVersion, setTabContainerVersion] = USE_STATE('tabContainerVersion', 2); // 1: TabContainer (우측), 2: TabContainerV2 (하단) const [tabContainerVersion, setTabContainerVersion] = USE_STATE('tabContainerVersion', 2); // 1: TabContainer (우측), 2: TabContainerV2 (하단)
const [isModalClosed, setIsModalClosed] = USE_STATE('isModalClosed', true); // 모달이 false 상태인지 나타내는 플래그 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 panels = USE_SELECTOR('panels', (state) => state.panels.panels);
const chatData = USE_SELECTOR('chatData', (state) => state.play.chatData); const chatData = USE_SELECTOR('chatData', (state) => state.play.chatData);
@@ -1937,6 +1983,30 @@ const PlayerPanel = ({ isTabActivated, panelInfo, isOnTop, spotlightId, ...props
panelInfo: { modalStyle: modalStyle, modalScale: scale }, 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 { } else {
dlog('[PlayerPanel] Condition 1: Node not found, using saved modalStyle'); dlog('[PlayerPanel] Condition 1: Node not found, using saved modalStyle');
setModalStyle(panelInfo.modalStyle); setModalStyle(panelInfo.modalStyle);

View File

@@ -18,11 +18,19 @@ export default function ShopNowButton({ onClick }) {
}; };
const handleSpotlightDown = (e) => { const handleSpotlightDown = (e) => {
e.stopPropagation();
e.preventDefault();
// tabIndexV2가 2일 때만 CC 버튼으로 내려가기
Spotlight.focus('live-channel-next-button');
};
const handleSpotlightLeft = (e) => {
e.stopPropagation(); e.stopPropagation();
e.preventDefault(); e.preventDefault();
// tabIndexV2가 2일 때만 CC 버튼으로 내려가기 // tabIndexV2가 2일 때만 CC 버튼으로 내려가기
Spotlight.focus('player-subtitlebutton'); Spotlight.focus('player-subtitlebutton');
}; };
return ( return (
<div className={css.container}> <div className={css.container}>
<SpottableDiv <SpottableDiv
@@ -31,6 +39,7 @@ export default function ShopNowButton({ onClick }) {
spotlightId="below-tab-shop-now-button" spotlightId="below-tab-shop-now-button"
onSpotlightUp={handleSpotlightUp} onSpotlightUp={handleSpotlightUp}
onSpotlightDown={handleSpotlightDown} onSpotlightDown={handleSpotlightDown}
onSpotlightLeft={handleSpotlightLeft}
> >
<span className={css.buttonText}>SHOP NOW</span> <span className={css.buttonText}>SHOP NOW</span>
</SpottableDiv> </SpottableDiv>

View File

@@ -196,7 +196,7 @@ export default function TabContainerV2({
// tabIndex = 2 (ShopNowButton) // tabIndex = 2 (ShopNowButton)
const timeoutId = setTimeout(() => { const timeoutId = setTimeout(() => {
Spotlight.focus('below-tab-shop-now-button'); Spotlight.focus('below-tab-shop-now-button');
}, 100); }, 10);
return () => clearTimeout(timeoutId); return () => clearTimeout(timeoutId);
} }
}, []); }, []);