Files
shoptime/com.twin.app.shoptime/src/views/PlayerPanel/PlayerTabContents/v2/TabContainer.v2.jsx
junghoon86.park f46090863f [영상내 shopnow 수정]
- shopnowIcon이미지 변경. qvc로 고정되어있었던부분 영상에 맞게 노출되도록 수정.
 - qvc영상일때 이미지는 border-radius먹지않도록 변경.
2025-12-15 11:02:52 +09:00

378 lines
12 KiB
JavaScript
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

import React, { useCallback, useEffect, useMemo } from 'react';
import classNames from 'classnames';
import { useSelector } from 'react-redux';
import Spotlight from '@enact/spotlight';
import SpotlightContainerDecorator from '@enact/spotlight/SpotlightContainerDecorator';
import Spottable from '@enact/spotlight/Spottable';
// import icon_arrow_right from '../../../../../assets/images/icons';
import icon_arrow_dwon from '../../../../../assets/images/player/icon_tabcontainer_arrow_down.png';
import usePrevious from '../../../../hooks/usePrevious';
import { LOG_MENU } from '../../../../utils/Config';
import { createDebugHelpers } from '../../../../utils/debug';
import { $L } from '../../../../utils/helperMethods';
import { SpotlightIds } from '../../../../utils/SpotlightIds';
import FeaturedShowContents from '../TabContents/FeaturedShowContents';
import LiveChannelContents from '../TabContents/LiveChannelContents';
import ShopNowContents from '../TabContents/ShopNowContents';
import LiveChannelNext from './LiveChannelNext';
import ShopNowButton from './ShopNowButton';
import css from './TabContainer.v2.module.less';
// 디버그 헬퍼 설정
const DEBUG_MODE = false;
const { dlog, dwarn, derror } = createDebugHelpers(DEBUG_MODE);
const Container = SpotlightContainerDecorator(
{ enterTo: 'last-focused' },
'div'
);
const SpottableDiv = Spottable('div');
export default function TabContainerV2({
panelInfo,
playListInfo,
shopNowInfo,
selectedIndex,
setSelectedIndex,
liveChannelInfos,
videoVerticalVisible,
handleItemFocus,
prevChannelIndex,
currentTime,
spotlightId,
tabIndex = 1, // tabIndex prop으로 제어 (0: ShopNow, 1: LiveChannel, 2: ShopNowButton)
onShopNowButtonClick,
onLiveChannelButtonClick,
onLiveNext,
onTabClose, // 탭 닫기 콜백 함수
tabVisible,
}) {
const youmaylikeInfos = useSelector((state) => state.main.youmaylikeInfos);
// 다음 재생 가능한 쇼 찾기
const findNextPlayableShow = useCallback((currentPlayList, currentIndex) => {
if (!currentPlayList || currentPlayList.length === 0) return null;
let nextIndex =
currentIndex === currentPlayList.length - 1 ? 0 : currentIndex + 1;
let initialIndex = nextIndex;
let attempts = 0;
// 유효한 showId를 가진 다음 쇼 찾기
while (
!currentPlayList[nextIndex]?.showId &&
attempts < currentPlayList.length
) {
nextIndex = nextIndex === currentPlayList.length - 1 ? 0 : nextIndex + 1;
attempts++;
if (nextIndex === initialIndex) break;
}
if (currentPlayList[nextIndex]?.showId) {
return currentPlayList[nextIndex];
}
return null;
}, []);
// 다음 쇼 정보 계산
const nextShowInfo = useMemo(() => {
return findNextPlayableShow(playListInfo, selectedIndex);
}, [playListInfo, selectedIndex, findNextPlayableShow]);
// ✨ DEBUG: youmaylikeInfos 데이터 로그
useEffect(() => {
dlog('[DEBUG] TabContainerV2 - youmaylikeInfos:', {
exists: !!youmaylikeInfos,
length: youmaylikeInfos?.length,
data: youmaylikeInfos,
shopNowInfo_length: shopNowInfo?.length,
shouldShowYouMayAlso:
shopNowInfo &&
shopNowInfo.length < 3 &&
youmaylikeInfos &&
youmaylikeInfos.length > 0,
});
}, [youmaylikeInfos, shopNowInfo]);
const tabList = [
$L('SHOP NOW'),
panelInfo?.shptmBanrTpNm === 'LIVE'
? $L('LIVE CHANNEL')
: $L('FEATURED SHOWS'),
];
useEffect(() => {
let nowMenu;
if (tabIndex === 0) {
nowMenu = LOG_MENU.FULL_SHOP_NOW;
}
if (tabIndex === 1) {
const isLive = panelInfo?.shptmBanrTpNm === 'LIVE';
nowMenu = isLive
? LOG_MENU.FULL_LIVE_CHANNELS
: LOG_MENU.FULL_FEATURED_SHOWS;
}
if (nowMenu) {
handleItemFocus(nowMenu);
}
}, [handleItemFocus, panelInfo?.shptmBanrTpNm, tabIndex]);
const _handleItemFocus = useCallback(
(nowMenu) => {
if (handleItemFocus) {
handleItemFocus(nowMenu);
}
},
[handleItemFocus]
);
const onSpotlightIndicatorUpButton = useCallback(
(e) => {
if (videoVerticalVisible) {
e.stopPropagation();
e.preventDefault();
Spotlight.focus('spotlightId-video-contaienr');
}
},
[videoVerticalVisible]
);
const handleCloseButtonClick = useCallback(
(e) => {
e.stopPropagation();
e.preventDefault();
if (onTabClose) {
onTabClose(2); // tabIndex를 2로 설정
}
},
[onTabClose]
);
// 위 방향 포커스 이동 시 백 버튼으로 이동
const handleSpotlightUpToBackButton = useCallback((e) => {
e.stopPropagation();
e.preventDefault();
// VideoPlayer가 belowContentsVisible prop을 감지해서 이미 controls를 표시했으므로
// 바로 포커스 이동
Spotlight.focus(SpotlightIds.PLAYER_BACK_BUTTON);
}, []);
// 이전 tabIndex 값 추적
const prevTabIndexRef = usePrevious(tabIndex);
const prevTabIndex = prevTabIndexRef.current;
// 하나의 함수에서 모든 tabIndex 변화 처리
const handleTabIndexChange = useCallback((newTabIndex, oldTabIndex) => {
console.log(
`[TabIndexChange] Tab changed from ${oldTabIndex} to ${newTabIndex}`
);
if (newTabIndex === 0) {
// tabIndex = 0 (ShopNow)
const timeoutId = setTimeout(() => {
Spotlight.focus('shownow_close_button');
}, 100);
return () => clearTimeout(timeoutId);
}
if (newTabIndex === 1) {
// tabIndex = 1 (LiveChannel)
const timeoutId = setTimeout(() => {
Spotlight.focus('below-tab-live-channel-button');
}, 100);
return () => clearTimeout(timeoutId);
}
if (newTabIndex === 2) {
// tabIndex = 2 (ShopNowButton)
const timeoutId = setTimeout(() => {
Spotlight.focus('below-tab-shop-now-button');
}, 100);
return () => clearTimeout(timeoutId);
}
}, []);
// tabIndex 변화 감지 및 처리
useEffect(() => {
// 초기 렌더링이 아닐 때만 실행 (prevTabIndex가 정의되었을 때)
if (prevTabIndex !== undefined && prevTabIndex !== tabIndex) {
handleTabIndexChange(tabIndex, prevTabIndex);
}
}, [tabIndex, prevTabIndex, handleTabIndexChange]);
return (
<Container
className={classNames(
css.tabContainer,
videoVerticalVisible && css.vertical,
css[`tabIndex${tabIndex}`]
)}
spotlightId={spotlightId}
>
{tabVisible && tabIndex === 0 && (
<>
<div className={css.shopNowHeaderContainer}>
<div className={css.shopNowHeader}>
<SpottableDiv
className={css.shopNowHeaderLeft}
spotlightId="shownow_close_button"
onClick={handleCloseButtonClick}
onSpotlightUp={handleSpotlightUpToBackButton}
onSpotlightDown={(e) => {
// 첫 번째 ShopNow 아이템으로 포커스 이동
e.stopPropagation();
e.preventDefault();
Spotlight.focus('shop-now-item-0');
}}
>
<div
className={classNames(
css.shopNowIconWrapper,
playListInfo[selectedIndex]?.patncNm === 'QVC' &&
css.shopNowQvcIconWrapper
)}
>
<img
src={playListInfo[selectedIndex]?.patncLogoPath}
alt="shop now icon"
className={css.shopNowIcon}
/>
</div>
<div className={css.shopNowHeaderText}>SHOP NOW</div>
<div className={css.arrowIcon}>
<img src={icon_arrow_dwon} alt="arrow down" />
</div>
</SpottableDiv>
{/* <SpottableDiv
className={css.closeButton}
spotlightId="below-tab-close-button"
onClick={handleCloseButtonClick}
onSpotlightUp={handleSpotlightUpToBackButton}
onSpotlightDown={(e) => {
// 첫 번째 ShopNow 아이템으로 포커스 이동
e.stopPropagation();
e.preventDefault();
Spotlight.focus("shop-now-item-0");
}}
>
×
</SpottableDiv> */}
</div>
{/* YouMayAlso Like 헤더 (ShopNow 아이템 < 3 && YouMayLike 데이터 존재) */}
{shopNowInfo &&
shopNowInfo.length < 3 &&
youmaylikeInfos &&
youmaylikeInfos.length > 0 && (
<div className={css.youMayAlsoLikeHeader}>
<div className={css.youMayAlsoLikeText}>
You may also like
</div>
</div>
)}
</div>
<ShopNowContents
tabTitle={tabList}
shopNowInfo={shopNowInfo}
playListInfo={playListInfo && playListInfo[selectedIndex]}
videoVerticalVisible={videoVerticalVisible}
panelInfo={panelInfo}
tabIndex={tabIndex}
handleItemFocus={_handleItemFocus}
version={2}
direction="horizontal"
/>
</>
)}
{tabVisible && tabIndex === 1 && (
<>
<SpottableDiv
className={css.liveChannelButton}
onClick={onLiveChannelButtonClick}
spotlightId={
panelInfo?.shptmBanrTpNm === 'LIVE'
? 'below-tab-live-channel-button'
: 'below-tab-featured-show-button'
}
onSpotlightUp={handleSpotlightUpToBackButton}
onSpotlightDown={(e) => {
// 첫 번째 PlayerItem으로 포커스 이동
Spotlight.focus('tabChannel-video-0');
}}
onSpotlightFocus={() => {
console.log('[TabContainerV2] below-tab button focused');
}}
>
<span className={css.buttonText}>{tabList[1]}</span>
<div className={css.arrowIcon}>
<img src={icon_arrow_dwon} alt="arrow down" />
</div>
</SpottableDiv>
{panelInfo?.shptmBanrTpNm === 'LIVE' && playListInfo && (
<LiveChannelContents
tabTitle={tabList}
selectedIndex={selectedIndex}
setSelectedIndex={setSelectedIndex}
videoVerticalVisible={videoVerticalVisible}
currentVideoShowId={playListInfo[selectedIndex]?.showId}
liveInfos={playListInfo}
tabIndex={tabIndex}
handleItemFocus={_handleItemFocus}
panelInfo={panelInfo}
currentTime={currentTime}
version={2}
direction="horizontal"
/>
)}
{panelInfo?.shptmBanrTpNm === 'VOD' && playListInfo && (
<FeaturedShowContents
tabTitle={tabList}
featuredShowsInfos={playListInfo}
currentVideoInfo={playListInfo[selectedIndex]}
setSelectedIndex={setSelectedIndex}
selectedIndex={selectedIndex}
videoVerticalVisible={videoVerticalVisible}
currentVideoShowId={playListInfo[selectedIndex]?.showId}
tabIndex={tabIndex}
handleItemFocus={_handleItemFocus}
panelInfo={panelInfo}
version={2}
direction="horizontal"
/>
)}
</>
)}
{tabVisible && tabIndex === 2 && (
<>
<LiveChannelNext
channelLogo={nextShowInfo?.patncLogoPath}
channelName={nextShowInfo?.patncNm || 'ShopLC'}
programName={nextShowInfo?.showNm || 'Live Channel'}
backgroundColor={
nextShowInfo?.dfltThumbnailImgPath ||
'linear-gradient(180deg, #284998 0%, #06B0EE 100%)'
}
onClick={onLiveNext}
spotlightId="live-channel-next-button"
onFocus={onLiveNext}
/>
<ShopNowButton onClick={onShopNowButtonClick} />
</>
)}
</Container>
);
}