Files
shoptime/com.twin.app.shoptime/src/views/HomePanel/HomeBanner/HomeBanner.jsx

328 lines
11 KiB
JavaScript

// src/views/HomePanel/HomeBanner/HomeBanner.jsx
import React, { useCallback, useEffect, useMemo, useRef, useState } from 'react';
import { useDispatch, useSelector } from 'react-redux';
import Spotlight from '@enact/spotlight';
import { SpotlightContainerDecorator } from '@enact/spotlight/SpotlightContainerDecorator';
import Spottable from '@enact/spotlight/Spottable';
import { changeAppStatus } from '../../../actions/commonActions';
import { justForYou } from '../../../actions/forYouActions';
import {
clearAllVideoTimers,
releasePlayControl,
requestPlayControl,
startVideoPlayer,
startVideoPlayerNew,
} from '../../../actions/playActions';
import CustomImage from '../../../components/CustomImage/CustomImage';
import { useFocusHistory } from '../../../hooks/useFocusHistory/useFocusHistory';
// [COMMENTED OUT] useVideoMove 관련 코드 주석 처리 - 향후 사용 검토 필요
// import { useVideoMove } from '../../../hooks/useVideoTransition/useVideoMove';
import { panel_names } from '../../../utils/Config';
import { $L } from '../../../utils/helperMethods';
import css from './HomeBanner.module.less';
// import * as Config from "../../../utils/Config";
// 새로운 비디오 유닛 컴포넌트 임포트
import PersistentVideoUnit from './PersistentVideoUnit';
import JustForSwitchBanner from './RandomBannerType/JustForYouBanner';
import Random from './RandomUnit';
// import RandomUnitNew from './RandomUnit';
import Rolling from './RollingUnit';
import SimpleVideoContainer from './SimpleVideoContainer';
const SpottableComponent = Spottable('div');
const Container = SpotlightContainerDecorator({ enterTo: 'last-focused' }, 'div');
const ContainerBasic = SpotlightContainerDecorator({ enterTo: 'last-focused' }, 'div');
export default function HomeBanner({
firstSpot,
spotlightId,
handleItemFocus,
handleShelfFocus,
videoPlayIntentRef,
}) {
const dispatch = useDispatch();
useEffect(() => {
dispatch(justForYou());
}, [dispatch]);
const homeTopDisplayInfo = useSelector((state) => state.home.homeTopDisplayInfo);
const bannerDataList = useSelector((state) => state.home.bannerData?.bannerInfos);
const popupVisible = useSelector((state) => state.common.popup.popupVisible);
const panels = useSelector((state) => state.panels.panels);
// 🔽 useFocusHistory - 경량화된 범용 포커스 히스토리
const focusHistory = useFocusHistory({
enableLogging: true,
useGlobalState: true,
logPrefix: '[HomeBanner-Focus]',
});
// 🔽 useVideoMove - 포커스 전환 기반 동영상 제어
// [COMMENTED OUT] useVideoMove 미사용 - playByTransition() 호출되지 않음
// const { playByTransition, cleanup } = useVideoMove({
// enableLogging: true,
// logPrefix: "[HomeBanner-VideoMove]",
// });
// 🔽 컴포넌트 언마운트 시 비디오 리소스 정리
useEffect(() => {
return () => {
// console.log('[HomeBanner] 컴포넌트 언마운트 - 비디오 리소스 정리');
// [COMMENTED OUT] useVideoMove cleanup 미사용
// cleanup();
// 전역 비디오 타이머 정리 (메모리 누수 방지)
clearAllVideoTimers();
// 백그라운드 비디오 정지
if (window.mediaPlayer && window.mediaPlayer._controller) {
try {
window.mediaPlayer._controller.pause();
console.log('[HomeBanner] 백그라운드 비디오 정지 완료');
} catch (error) {
console.warn('[HomeBanner] 백그라운드 비디오 정지 실패:', error);
}
}
};
}, []); // [COMMENTED OUT] cleanup 변수 제거 - useVideoMove 미사용으로 인한 의존성 제거
const selectTemplate = useMemo(() => {
return homeTopDisplayInfo.shptmTmplCd;
}, [homeTopDisplayInfo.shptmTmplCd]);
const _handleItemFocus = useCallback(() => {
if (handleItemFocus) {
handleItemFocus();
}
}, [handleItemFocus]);
// 🔽 초기 비디오 재생 플래그 (1회만 실행되도록)
const isInitialVideoPlayRef = useRef(false);
//------------------------------------------------------------------------------
const _handleShelfFocus = useCallback(() => {
if (handleShelfFocus) {
handleShelfFocus();
}
}, [handleShelfFocus]);
const defaultFocus = useMemo(() => {
if (bannerDataList) {
let targetIndex = 0;
for (let i = 0; i < bannerDataList.length; i++) {
const data = bannerDataList[i];
let bannerDetailInfos = data.bannerDetailInfos;
if (data.shptmDspyTpNm === 'Random') {
if (
bannerDetailInfos[data.randomIndex].shptmBanrTpNm === 'LIVE' ||
bannerDetailInfos[data.randomIndex].shptmBanrTpNm === 'VOD'
) {
targetIndex = i;
break;
}
} else if (
bannerDetailInfos.find((el) => el.shptmBanrTpNm === 'LIVE' || el.shptmBanrTpNm === 'VOD')
) {
targetIndex = i;
break;
}
}
return 'banner' + targetIndex;
}
return null;
}, [bannerDataList]);
// 🔽 초기 비디오 자동 재생 (렌더링 완료 후)
useEffect(() => {
// 조건 체크
if (!bannerDataList || isInitialVideoPlayRef.current || !defaultFocus) {
return;
}
// 한 번만 실행
isInitialVideoPlayRef.current = true;
// defaultFocus에서 배너 인덱스 추출 (예: "banner0" -> 0)
const bannerIndex = parseInt(defaultFocus.replace('banner', ''));
const targetBannerData = bannerDataList[bannerIndex];
if (!targetBannerData) {
return;
}
// 비디오 재생 가능한 배너 찾기
let videoData = null;
if (targetBannerData.shptmDspyTpNm === 'Random') {
videoData = targetBannerData.bannerDetailInfos?.[targetBannerData.randomIndex];
} else {
videoData = targetBannerData.bannerDetailInfos?.[0];
}
// DetailPanel이 떠 있는 동안에는 배너 자동 재생을 스킵 (PlayerPanel 모달 재설정 방지)
const hasDetailPanel = panels.some((p) => p.name === panel_names.DETAIL_PANEL);
if (!hasDetailPanel && videoData && (videoData.shptmBanrTpNm === 'LIVE' || videoData.shptmBanrTpNm === 'VOD')) {
console.log('[HomeBanner] 초기 비디오 자동 재생:', defaultFocus);
dispatch(
startVideoPlayerNew({
bannerId: defaultFocus,
showUrl: videoData.showUrl,
patnrId: videoData.patnrId,
showId: videoData.showId,
shptmBanrTpNm: videoData.shptmBanrTpNm,
lgCatCd: videoData.lgCatCd,
chanId: videoData.brdcChnlId,
// 기본: 배너는 modal=true로 재생
modal: true,
modalContainerId: defaultFocus,
})
);
}
}, [bannerDataList, defaultFocus, dispatch, panels]);
const renderItem = useCallback(
(index, isHorizontal) => {
const data = bannerDataList?.[index] ?? {};
// videoPlayable을 동적으로 계산
// Random이나 Rolling 배너에서 LIVE 또는 VOD 타입의 비디오가 있는지 확인
const videoPlayerable =
(data.shptmDspyTpNm === 'Random' || data.shptmDspyTpNm === 'Rolling') &&
data.bannerDetailInfos?.some(
(item) => item.shptmBanrTpNm === 'LIVE' || item.shptmBanrTpNm === 'VOD'
);
return (
<div className={!isHorizontal ? css.imgBox : undefined}>
{data.shptmDspyTpNm === 'Rolling' ? (
<Rolling
bannerData={data}
isHorizontal={isHorizontal}
key={'banner' + index}
spotlightId={'banner' + index}
handleShelfFocus={_handleShelfFocus}
handleItemFocus={_handleItemFocus}
videoPlayerable={videoPlayerable}
/>
) : data.shptmDspyTpNm === 'Random' ? (
<Random
bannerData={data}
isHorizontal={isHorizontal}
key={'banner' + index}
spotlightId={'banner' + index}
handleShelfFocus={_handleShelfFocus}
handleItemFocus={_handleItemFocus}
randomNumber={data.randomIndex}
videoPlayerable={videoPlayerable}
videoPlayIntentRef={videoPlayIntentRef}
/>
) : (
<SpottableComponent spotlightId={'banner' + index}>
<CustomImage
delay={0}
src={
isHorizontal
? homeTopDisplayInfo.wdthtpImgPath1
: homeTopDisplayInfo.vtctpImgPath1
}
aria-label={
isHorizontal ? homeTopDisplayInfo.wdthtpImgNm1 : homeTopDisplayInfo.vtctpImgNm1
}
/>
</SpottableComponent>
)}
</div>
);
},
[_handleItemFocus, _handleShelfFocus, bannerDataList, videoPlayIntentRef]
);
// 0번째 배너(영구 재생)를 위한 렌더링 함수
const renderItemPersistentVideo = useCallback(
(index, isHorizontal) => {
return (
<div className={!isHorizontal ? css.imgBox : undefined}>
<SimpleVideoContainer
spotlightId={'banner' + index} // "banner0"
isHorizontal={isHorizontal}
handleShelfFocus={_handleShelfFocus}
/>
</div>
);
},
[_handleShelfFocus]
);
const renderSimpleVideoContainer = useCallback(
(index, isHorizontal) => {
return (
<div className={!isHorizontal ? css.imgBox : undefined}>
<SimpleVideoContainer
spotlightId={'banner' + index}
isHorizontal={isHorizontal}
handleShelfFocus={_handleShelfFocus}
/>
</div>
);
},
[_handleShelfFocus]
);
const renderLayout = useCallback(() => {
switch (selectTemplate) {
case 'DSP00201': {
return (
<>
<ContainerBasic className={css.smallBox}>
{renderItem(0, true)}
{renderItem(1, true)}
</ContainerBasic>
{renderItem(2, false)}
{renderItem(3, false)}
</>
);
}
case 'DSP00202': {
return (
<>
{renderItem(0, false)}
<ContainerBasic className={css.smallBox}>
{renderItem(1, true)}
{renderItem(2, true)}
</ContainerBasic>
{renderItem(3, false)}
</>
);
}
case 'DSP00203': {
return (
<>
{renderItem(0, false)}
{renderItem(1, false)}
<ContainerBasic className={css.smallBox}>
{renderItem(2, true)}
{renderItem(3, true)}
</ContainerBasic>
</>
);
}
}
return null;
}, [selectTemplate, renderItem, renderSimpleVideoContainer]);
return (
<>
<Container className={css.container} spotlightId={spotlightId} data-wheel-point={true}>
<div className={css.homeTemplateBox}>{renderLayout()}</div>
</Container>
</>
);
}