// 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 (
{data.shptmDspyTpNm === 'Rolling' ? ( ) : data.shptmDspyTpNm === 'Random' ? ( ) : ( )}
); }, [_handleItemFocus, _handleShelfFocus, bannerDataList, videoPlayIntentRef] ); // 0번째 배너(영구 재생)를 위한 렌더링 함수 const renderItemPersistentVideo = useCallback( (index, isHorizontal) => { return (
); }, [_handleShelfFocus] ); const renderSimpleVideoContainer = useCallback( (index, isHorizontal) => { return (
); }, [_handleShelfFocus] ); const renderLayout = useCallback(() => { switch (selectTemplate) { case 'DSP00201': { return ( <> {renderItem(0, true)} {renderItem(1, true)} {renderItem(2, false)} {renderItem(3, false)} ); } case 'DSP00202': { return ( <> {renderItem(0, false)} {renderItem(1, true)} {renderItem(2, true)} {renderItem(3, false)} ); } case 'DSP00203': { return ( <> {renderItem(0, false)} {renderItem(1, false)} {renderItem(2, true)} {renderItem(3, true)} ); } } return null; }, [selectTemplate, renderItem, renderSimpleVideoContainer]); return ( <>
{renderLayout()}
); }