diff --git a/com.twin.app.shoptime/src/views/PlayerPanel/PlayerPanel.jsx b/com.twin.app.shoptime/src/views/PlayerPanel/PlayerPanel.jsx index 25533659..237c2f84 100644 --- a/com.twin.app.shoptime/src/views/PlayerPanel/PlayerPanel.jsx +++ b/com.twin.app.shoptime/src/views/PlayerPanel/PlayerPanel.jsx @@ -178,7 +178,17 @@ const PlayerPanel = ({ const videoPlayer = useRef(null); const [playListInfo, setPlayListInfo] = USE_STATE("playListInfo", ""); + const playListInfoRef = useRef([]); + const sortedFilteredListRef = useRef(null); // 정렬된 필터링 리스트 저장 + const currentPlayingShowIdRef = useRef(null); const [shopNowInfo, setShopNowInfo] = USE_STATE("shopNowInfo"); + const [isFilteredByPatnr19, setIsFilteredByPatnr19] = USE_STATE( + "isFilteredByPatnr19", + false + ); + const isFilteredByPatnr19Ref = useRef(false); + const [clickedShowId, setClickedShowId] = USE_STATE("clickedShowId", null); + const hasSetInitialIndex = useRef(false); const [backupInitialIndex, setBackupInitialIndex] = USE_STATE( "backupInitialIndex", 0 @@ -863,6 +873,7 @@ const PlayerPanel = ({ const onClickBack = useCallback( (ev, isEnd) => { //modal로부터 Full 전환된 경우 다시 preview 모드로 돌아감. + console.log("###onClickBack", backupInitialIndex, isEnd); if ( sideContentsVisible && !videoVerticalVisible && @@ -966,7 +977,7 @@ const PlayerPanel = ({ // 아이템클릭 진입시 포커스 let hasProperSpot = false; let targetId; - if (!isInitialFocusOccurred || panelInfo.targetId) { + if (!isInitialFocusOccurred) { targetId = panelInfo.targetId; initialFocusTimeoutJob.current.start(() => { @@ -988,11 +999,6 @@ const PlayerPanel = ({ if (retryTarget) { Spotlight.focus(retryTarget); setIsInitialFocusOccurred(true); - } else { - if (shopNowInfo?.length > 0) { - Spotlight.focus("playVideoShopNowBox"); - setIsInitialFocusOccurred(true); - } } }); } @@ -1003,7 +1009,6 @@ const PlayerPanel = ({ panelInfo.targetId, panelInfo.modal, videoVerticalVisible, - shopNowInfo, ]); const videoInitialFocused = useCallback(() => { @@ -1028,15 +1033,15 @@ const PlayerPanel = ({ } } - if (panelInfo.isIndicatorByClick && shopNowInfo?.length > 0) { - Spotlight.focus("playVideoShopNowBox"); - return; - } - if (!panelInfo.modal && !videoVerticalVisible && !hasProperSpot) { Spotlight.focus(SpotlightIds.PLAYER_TAB_BUTTON); return; } + //비디오 진입시 포커스 + if (panelInfo.isIndicatorByClick && shopNowInfo?.length > 0) { + Spotlight.focus("playVideoShopNowBox"); + return; + } }, [ shopNowInfo, videoVerticalVisible, @@ -1107,6 +1112,49 @@ const PlayerPanel = ({ panelInfo?.shptmBanrTpNm, ]); + // 라이브 채널 클릭 시 필터링 상태 설정 + useEffect(() => { + if (panelInfo.shptmBanrTpNm === "LIVE" && panelInfo.isUpdatedByClick) { + if (panelInfo.patnrId === "19") { + // 이미 필터링 상태가 아닐 때만 리셋 (최초 진입) + const wasNotFiltered = !isFilteredByPatnr19Ref.current; + + setIsFilteredByPatnr19(true); + isFilteredByPatnr19Ref.current = true; + + if (wasNotFiltered && panelInfo.showId) { + // 최초 필터링 진입 시에만 리셋 + setClickedShowId(panelInfo.showId); + hasSetInitialIndex.current = false; + sortedFilteredListRef.current = null; + } + } else { + setIsFilteredByPatnr19(false); + isFilteredByPatnr19Ref.current = false; + setClickedShowId(null); + hasSetInitialIndex.current = false; + sortedFilteredListRef.current = null; + } + } + }, [panelInfo]); + + // selectedIndex가 변경될 때마다 현재 재생 중인 showId 업데이트 + useEffect(() => { + if ( + isFilteredByPatnr19 && + playListInfo && + playListInfo.length > 0 && + selectedIndex !== null && + selectedIndex >= 0 && + selectedIndex < playListInfo.length + ) { + const currentShowId = playListInfo[selectedIndex]?.showId; + if (currentShowId) { + currentPlayingShowIdRef.current = currentShowId; + } + } + }, [selectedIndex, playListInfo, isFilteredByPatnr19]); + useEffect(() => { if ( panelInfo.shptmBanrTpNm === "VOD" && @@ -1134,6 +1182,7 @@ const PlayerPanel = ({ setShopNowInfo(showDetailInfo[0].productInfos); saveToLocalSettings(showDetailInfo[0].showId, showDetailInfo[0].patnrId); } + // console.log("###showDetailInfo", showDetailInfo); }, [showDetailInfo]); //LIVE @@ -1187,16 +1236,12 @@ const PlayerPanel = ({ videoInitialFocused(); } } - }, [playListInfo, panelInfo.targetId]); + }, [playListInfo]); //10초 후 닫힐때 TabButton 포커스 useEffect(() => { if (playListInfo && playListInfo.length > 0) { - if (panelInfo.targetId) { - videoItemFocused(); - } else { - videoInitialFocused(); - } + videoInitialFocused(); } }, [sideContentsVisible, panelInfo.modal]); @@ -1290,21 +1335,8 @@ const PlayerPanel = ({ playlist.forEach((item) => { if (item.showType === "vod" && Array.isArray(item.vodInfos)) { - // vodInfos를 정렬 (showId 기준) - const sortedVodInfos = [...item.vodInfos].sort((a, b) => { - // showId가 있으면 showId로 정렬 - if (a.showId && b.showId) { - return a.showId.localeCompare(b.showId); - } - // strtDt가 있으면 시작일로 정렬 - if (a.strtDt && b.strtDt) { - return new Date(a.strtDt) - new Date(b.strtDt); - } - // 정렬 기준이 없으면 원래 순서 유지 - return 0; - }); - - const mergedVodInfos = sortedVodInfos.map((vod) => ({ + // 정렬 없이 vodInfos를 그대로 사용 + const mergedVodInfos = item.vodInfos.map((vod) => ({ ...vod, patnrId: item.patnrId, patncNm: item.patncNm, @@ -1318,7 +1350,65 @@ const PlayerPanel = ({ } }); - setPlayListInfo(modifiedList); + // 필터링 상태에 따라 리스트 결정 + let finalList = modifiedList; + + if (isFilteredByPatnr19) { + // 이미 정렬된 리스트가 있으면 그것을 사용 + if (sortedFilteredListRef.current) { + finalList = sortedFilteredListRef.current; + + // 클릭 시 현재 재생 중인 아이템의 인덱스 찾기 + if (panelInfo.isUpdatedByClick && panelInfo.showId) { + const newIndex = finalList.findIndex( + (item) => item.showId === panelInfo.showId + ); + if (newIndex >= 0) { + setSelectedIndex(newIndex); + } + } + } else { + // 없으면 새로 필터링하고 정렬 + const filtered = modifiedList.filter( + (item) => item.patnrId === "19" + ); + if (filtered.length > 0) { + // 클릭한 아이템을 맨 앞으로 배치 + if (clickedShowId) { + const clickedIndex = filtered.findIndex( + (item) => item.showId === clickedShowId + ); + + if (clickedIndex > 0) { + // 클릭한 아이템이 있고 첫 번째가 아니면, 맨 앞으로 이동 + const clickedItem = filtered[clickedIndex]; + finalList = [ + clickedItem, + ...filtered.slice(0, clickedIndex), + ...filtered.slice(clickedIndex + 1), + ]; + } else { + // 이미 첫 번째거나 찾지 못한 경우 그대로 사용 + finalList = filtered; + } + } else { + finalList = filtered; + } + + // 정렬된 리스트 저장 + sortedFilteredListRef.current = finalList; + + // 초기 인덱스 설정 + if (!hasSetInitialIndex.current) { + setSelectedIndex(0); // 클릭한 아이템이 맨 앞이므로 항상 0 + hasSetInitialIndex.current = true; + } + } + } + } + + setPlayListInfo(finalList); + playListInfoRef.current = finalList; if (showNowInfos?.prdtChgYn === "N") { return; @@ -1346,6 +1436,8 @@ const PlayerPanel = ({ liveShowInfos, showNowInfos, dispatch, + isFilteredByPatnr19, + clickedShowId, ]); const liveTotalTime = useMemo(() => { @@ -1826,7 +1918,6 @@ const PlayerPanel = ({ }, }) ); - Spotlight.focus("playVideoShopNowBox"); } } if (!sideContentsVisible) { @@ -1839,7 +1930,6 @@ const PlayerPanel = ({ selectedIndex, sideContentsVisible, initialEnter, - panelInfo, ]); const handleIndicatorUpClick = useCallback(() => { @@ -1881,7 +1971,6 @@ const PlayerPanel = ({ }, }) ); - Spotlight.focus("playVideoShopNowBox"); } } if (!sideContentsVisible) { @@ -1894,7 +1983,6 @@ const PlayerPanel = ({ selectedIndex, sideContentsVisible, initialEnter, - panelInfo, ]); useEffect(() => { @@ -1983,30 +2071,38 @@ const PlayerPanel = ({ return; } - // LIVE 타입이면서 patnrId가 19일 때 자동재생 처리 + // LIVE 타입이면서 필터링된 상태(patnrId 19)일 때 자동재생 처리 if ( panelInfoRef.current.shptmBanrTpNm === "LIVE" && - panelInfoRef.current.patnrId === "19" + isFilteredByPatnr19Ref.current ) { - const currentIndex = selectedIndex; + const currentPlayList = playListInfoRef.current; + + // 현재 재생 중인 비디오의 showId를 기준으로 인덱스 찾기 + const currentShowId = currentPlayingShowIdRef.current; + const currentIndex = currentPlayList.findIndex( + (item) => item.showId === currentShowId + ); const nextIndex = currentIndex + 1; if ( - playListInfo && - nextIndex < playListInfo.length && - playListInfo[nextIndex]?.showId + currentPlayList && + currentIndex >= 0 && + nextIndex < currentPlayList.length && + currentPlayList[nextIndex]?.showId ) { // 다음 비디오가 있으면 자동으로 다음 비디오 재생 + // selectedIndex 변경 시 currentPlayingShowIdRef가 자동 업데이트됨 setSelectedIndex(nextIndex); // LIVE 비디오의 경우 startVideoPlayer 호출 dispatch( startVideoPlayer({ - chanId: playListInfo[nextIndex]?.chanId, + chanId: currentPlayList[nextIndex]?.chanId, modal: panelInfoRef.current.modal || false, - patnrId: playListInfo[nextIndex]?.patnrId, - showId: playListInfo[nextIndex]?.showId, - showUrl: playListInfo[nextIndex]?.showUrl, + patnrId: currentPlayList[nextIndex]?.patnrId, + showId: currentPlayList[nextIndex]?.showId, + showUrl: currentPlayList[nextIndex]?.showUrl, shptmBanrTpNm: "LIVE", }) ); @@ -2023,7 +2119,7 @@ const PlayerPanel = ({ return; } }, - [selectedIndex, playListInfo, dispatch] + [selectedIndex, dispatch] ); const onKeyDown = (ev) => { @@ -2375,6 +2471,7 @@ const PlayerPanel = ({ handleItemFocus={handleItemFocus} prevChannelIndex={prevChannelIndex} currentTime={currentTime} + isFilteredByPatnr19={isFilteredByPatnr19} /> )} diff --git a/com.twin.app.shoptime/src/views/PlayerPanel/PlayerTabContents/TabContainer.jsx b/com.twin.app.shoptime/src/views/PlayerPanel/PlayerTabContents/TabContainer.jsx index b7a0448f..6d083d2a 100644 --- a/com.twin.app.shoptime/src/views/PlayerPanel/PlayerTabContents/TabContainer.jsx +++ b/com.twin.app.shoptime/src/views/PlayerPanel/PlayerTabContents/TabContainer.jsx @@ -33,13 +33,16 @@ export default function TabContainer({ prevChannelIndex, currentTime, spotlightId, + isFilteredByPatnr19, }) { const [tab, setTab] = useState(0); const tabList = [ $L("SHOP NOW"), panelInfo?.shptmBanrTpNm === "LIVE" - ? $L("LIVE CHANNEL") + ? isFilteredByPatnr19 + ? $L("VOD CHANNEL") + : $L("LIVE CHANNEL") : $L("FEATURED SHOWS"), ]; @@ -172,6 +175,7 @@ export default function TabContainer({ handleItemFocus={_handleItemFocus} panelInfo={panelInfo} currentTime={currentTime} + isFilteredByPatnr19={isFilteredByPatnr19} /> )} diff --git a/com.twin.app.shoptime/src/views/PlayerPanel/PlayerTabContents/TabContents/LiveChannelContents.jsx b/com.twin.app.shoptime/src/views/PlayerPanel/PlayerTabContents/TabContents/LiveChannelContents.jsx index 336fa0b3..88a877c7 100644 --- a/com.twin.app.shoptime/src/views/PlayerPanel/PlayerTabContents/TabContents/LiveChannelContents.jsx +++ b/com.twin.app.shoptime/src/views/PlayerPanel/PlayerTabContents/TabContents/LiveChannelContents.jsx @@ -6,7 +6,12 @@ import Spotlight from "@enact/spotlight"; import { updatePanel } from "../../../../actions/panelActions"; import TVirtualGridList from "../../../../components/TVirtualGridList/TVirtualGridList"; -import { LOG_CONTEXT_NAME, LOG_MENU, LOG_MESSAGE_ID, panel_names } from "../../../../utils/Config"; +import { + LOG_CONTEXT_NAME, + LOG_MENU, + LOG_MESSAGE_ID, + panel_names, +} from "../../../../utils/Config"; import { $L } from "../../../../utils/helperMethods"; import PlayerItemCard, { TYPES } from "../../PlayerItemCard/PlayerItemCard"; import ListEmptyContents from "../TabContents/ListEmptyContents/ListEmptyContents"; @@ -22,10 +27,12 @@ export default function LiveChannelContents({ tabIndex, handleItemFocus, tabTitle, - panelInfo + panelInfo, + isFilteredByPatnr19, }) { const dispatch = useDispatch(); const isClickBlocked = useRef(false); + const scrollToRef = useRef(null); const handleFocus = useCallback( () => () => { if (handleItemFocus) { @@ -35,6 +42,18 @@ export default function LiveChannelContents({ [handleItemFocus] ); + // cbScrollTo 콜백으로 scrollTo 함수 받기 + const handleScrollTo = useCallback((scrollToFn) => { + scrollToRef.current = scrollToFn; + }, []); + + // VOD Channel로 전환될 때 스크롤을 최상단으로 이동 + useEffect(() => { + if (isFilteredByPatnr19 && scrollToRef.current) { + scrollToRef.current({ index: 0, animate: false, focus: false }); + } + }, [isFilteredByPatnr19]); + const renderItem = useCallback( ({ index, ...rest }) => { const { @@ -63,8 +82,8 @@ export default function LiveChannelContents({ category: catNm, partner: patncNm, contextName: LOG_CONTEXT_NAME.SHOW, - messageId: LOG_MESSAGE_ID.CONTENTCLICK - } + messageId: LOG_MESSAGE_ID.CONTENTCLICK, + }; dispatch(sendLogTotalRecommend(params)); //중복클릭방지 if (isClickBlocked.current) { @@ -122,6 +141,7 @@ export default function LiveChannelContents({ startDt={strtDt} endDt={endDt} currentTime={currentTime} + currentVideoVisible={currentVideoShowId === liveInfos[index].showId} /> ); }, @@ -140,6 +160,7 @@ export default function LiveChannelContents({