live > vod(핑크퐁) 클릭시 vod channel 리스트 추가 및 자동재생

This commit is contained in:
opacity@t-win.kr
2025-11-11 16:58:35 +09:00
parent 0eae4f3c5c
commit 6518edf059
3 changed files with 176 additions and 54 deletions

View File

@@ -178,7 +178,17 @@ const PlayerPanel = ({
const videoPlayer = useRef(null); const videoPlayer = useRef(null);
const [playListInfo, setPlayListInfo] = USE_STATE("playListInfo", ""); const [playListInfo, setPlayListInfo] = USE_STATE("playListInfo", "");
const playListInfoRef = useRef([]);
const sortedFilteredListRef = useRef(null); // 정렬된 필터링 리스트 저장
const currentPlayingShowIdRef = useRef(null);
const [shopNowInfo, setShopNowInfo] = USE_STATE("shopNowInfo"); 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( const [backupInitialIndex, setBackupInitialIndex] = USE_STATE(
"backupInitialIndex", "backupInitialIndex",
0 0
@@ -863,6 +873,7 @@ const PlayerPanel = ({
const onClickBack = useCallback( const onClickBack = useCallback(
(ev, isEnd) => { (ev, isEnd) => {
//modal로부터 Full 전환된 경우 다시 preview 모드로 돌아감. //modal로부터 Full 전환된 경우 다시 preview 모드로 돌아감.
console.log("###onClickBack", backupInitialIndex, isEnd);
if ( if (
sideContentsVisible && sideContentsVisible &&
!videoVerticalVisible && !videoVerticalVisible &&
@@ -966,7 +977,7 @@ const PlayerPanel = ({
// 아이템클릭 진입시 포커스 // 아이템클릭 진입시 포커스
let hasProperSpot = false; let hasProperSpot = false;
let targetId; let targetId;
if (!isInitialFocusOccurred || panelInfo.targetId) { if (!isInitialFocusOccurred) {
targetId = panelInfo.targetId; targetId = panelInfo.targetId;
initialFocusTimeoutJob.current.start(() => { initialFocusTimeoutJob.current.start(() => {
@@ -988,11 +999,6 @@ const PlayerPanel = ({
if (retryTarget) { if (retryTarget) {
Spotlight.focus(retryTarget); Spotlight.focus(retryTarget);
setIsInitialFocusOccurred(true); setIsInitialFocusOccurred(true);
} else {
if (shopNowInfo?.length > 0) {
Spotlight.focus("playVideoShopNowBox");
setIsInitialFocusOccurred(true);
}
} }
}); });
} }
@@ -1003,7 +1009,6 @@ const PlayerPanel = ({
panelInfo.targetId, panelInfo.targetId,
panelInfo.modal, panelInfo.modal,
videoVerticalVisible, videoVerticalVisible,
shopNowInfo,
]); ]);
const videoInitialFocused = useCallback(() => { const videoInitialFocused = useCallback(() => {
@@ -1028,15 +1033,15 @@ const PlayerPanel = ({
} }
} }
if (panelInfo.isIndicatorByClick && shopNowInfo?.length > 0) {
Spotlight.focus("playVideoShopNowBox");
return;
}
if (!panelInfo.modal && !videoVerticalVisible && !hasProperSpot) { if (!panelInfo.modal && !videoVerticalVisible && !hasProperSpot) {
Spotlight.focus(SpotlightIds.PLAYER_TAB_BUTTON); Spotlight.focus(SpotlightIds.PLAYER_TAB_BUTTON);
return; return;
} }
//비디오 진입시 포커스
if (panelInfo.isIndicatorByClick && shopNowInfo?.length > 0) {
Spotlight.focus("playVideoShopNowBox");
return;
}
}, [ }, [
shopNowInfo, shopNowInfo,
videoVerticalVisible, videoVerticalVisible,
@@ -1107,6 +1112,49 @@ const PlayerPanel = ({
panelInfo?.shptmBanrTpNm, 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(() => { useEffect(() => {
if ( if (
panelInfo.shptmBanrTpNm === "VOD" && panelInfo.shptmBanrTpNm === "VOD" &&
@@ -1134,6 +1182,7 @@ const PlayerPanel = ({
setShopNowInfo(showDetailInfo[0].productInfos); setShopNowInfo(showDetailInfo[0].productInfos);
saveToLocalSettings(showDetailInfo[0].showId, showDetailInfo[0].patnrId); saveToLocalSettings(showDetailInfo[0].showId, showDetailInfo[0].patnrId);
} }
// console.log("###showDetailInfo", showDetailInfo);
}, [showDetailInfo]); }, [showDetailInfo]);
//LIVE //LIVE
@@ -1187,16 +1236,12 @@ const PlayerPanel = ({
videoInitialFocused(); videoInitialFocused();
} }
} }
}, [playListInfo, panelInfo.targetId]); }, [playListInfo]);
//10초 후 닫힐때 TabButton 포커스 //10초 후 닫힐때 TabButton 포커스
useEffect(() => { useEffect(() => {
if (playListInfo && playListInfo.length > 0) { if (playListInfo && playListInfo.length > 0) {
if (panelInfo.targetId) { videoInitialFocused();
videoItemFocused();
} else {
videoInitialFocused();
}
} }
}, [sideContentsVisible, panelInfo.modal]); }, [sideContentsVisible, panelInfo.modal]);
@@ -1290,21 +1335,8 @@ const PlayerPanel = ({
playlist.forEach((item) => { playlist.forEach((item) => {
if (item.showType === "vod" && Array.isArray(item.vodInfos)) { if (item.showType === "vod" && Array.isArray(item.vodInfos)) {
// vodInfos를 정렬 (showId 기준) // 정렬 없이 vodInfos를 그대로 사용
const sortedVodInfos = [...item.vodInfos].sort((a, b) => { const mergedVodInfos = item.vodInfos.map((vod) => ({
// 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) => ({
...vod, ...vod,
patnrId: item.patnrId, patnrId: item.patnrId,
patncNm: item.patncNm, 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") { if (showNowInfos?.prdtChgYn === "N") {
return; return;
@@ -1346,6 +1436,8 @@ const PlayerPanel = ({
liveShowInfos, liveShowInfos,
showNowInfos, showNowInfos,
dispatch, dispatch,
isFilteredByPatnr19,
clickedShowId,
]); ]);
const liveTotalTime = useMemo(() => { const liveTotalTime = useMemo(() => {
@@ -1826,7 +1918,6 @@ const PlayerPanel = ({
}, },
}) })
); );
Spotlight.focus("playVideoShopNowBox");
} }
} }
if (!sideContentsVisible) { if (!sideContentsVisible) {
@@ -1839,7 +1930,6 @@ const PlayerPanel = ({
selectedIndex, selectedIndex,
sideContentsVisible, sideContentsVisible,
initialEnter, initialEnter,
panelInfo,
]); ]);
const handleIndicatorUpClick = useCallback(() => { const handleIndicatorUpClick = useCallback(() => {
@@ -1881,7 +1971,6 @@ const PlayerPanel = ({
}, },
}) })
); );
Spotlight.focus("playVideoShopNowBox");
} }
} }
if (!sideContentsVisible) { if (!sideContentsVisible) {
@@ -1894,7 +1983,6 @@ const PlayerPanel = ({
selectedIndex, selectedIndex,
sideContentsVisible, sideContentsVisible,
initialEnter, initialEnter,
panelInfo,
]); ]);
useEffect(() => { useEffect(() => {
@@ -1983,30 +2071,38 @@ const PlayerPanel = ({
return; return;
} }
// LIVE 타입이면서 patnrId 19일 때 자동재생 처리 // LIVE 타입이면서 필터링된 상태(patnrId 19)일 때 자동재생 처리
if ( if (
panelInfoRef.current.shptmBanrTpNm === "LIVE" && 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; const nextIndex = currentIndex + 1;
if ( if (
playListInfo && currentPlayList &&
nextIndex < playListInfo.length && currentIndex >= 0 &&
playListInfo[nextIndex]?.showId nextIndex < currentPlayList.length &&
currentPlayList[nextIndex]?.showId
) { ) {
// 다음 비디오가 있으면 자동으로 다음 비디오 재생 // 다음 비디오가 있으면 자동으로 다음 비디오 재생
// selectedIndex 변경 시 currentPlayingShowIdRef가 자동 업데이트됨
setSelectedIndex(nextIndex); setSelectedIndex(nextIndex);
// LIVE 비디오의 경우 startVideoPlayer 호출 // LIVE 비디오의 경우 startVideoPlayer 호출
dispatch( dispatch(
startVideoPlayer({ startVideoPlayer({
chanId: playListInfo[nextIndex]?.chanId, chanId: currentPlayList[nextIndex]?.chanId,
modal: panelInfoRef.current.modal || false, modal: panelInfoRef.current.modal || false,
patnrId: playListInfo[nextIndex]?.patnrId, patnrId: currentPlayList[nextIndex]?.patnrId,
showId: playListInfo[nextIndex]?.showId, showId: currentPlayList[nextIndex]?.showId,
showUrl: playListInfo[nextIndex]?.showUrl, showUrl: currentPlayList[nextIndex]?.showUrl,
shptmBanrTpNm: "LIVE", shptmBanrTpNm: "LIVE",
}) })
); );
@@ -2023,7 +2119,7 @@ const PlayerPanel = ({
return; return;
} }
}, },
[selectedIndex, playListInfo, dispatch] [selectedIndex, dispatch]
); );
const onKeyDown = (ev) => { const onKeyDown = (ev) => {
@@ -2375,6 +2471,7 @@ const PlayerPanel = ({
handleItemFocus={handleItemFocus} handleItemFocus={handleItemFocus}
prevChannelIndex={prevChannelIndex} prevChannelIndex={prevChannelIndex}
currentTime={currentTime} currentTime={currentTime}
isFilteredByPatnr19={isFilteredByPatnr19}
/> />
)} )}
</Container> </Container>

View File

@@ -33,13 +33,16 @@ export default function TabContainer({
prevChannelIndex, prevChannelIndex,
currentTime, currentTime,
spotlightId, spotlightId,
isFilteredByPatnr19,
}) { }) {
const [tab, setTab] = useState(0); const [tab, setTab] = useState(0);
const tabList = [ const tabList = [
$L("SHOP NOW"), $L("SHOP NOW"),
panelInfo?.shptmBanrTpNm === "LIVE" panelInfo?.shptmBanrTpNm === "LIVE"
? $L("LIVE CHANNEL") ? isFilteredByPatnr19
? $L("VOD CHANNEL")
: $L("LIVE CHANNEL")
: $L("FEATURED SHOWS"), : $L("FEATURED SHOWS"),
]; ];
@@ -172,6 +175,7 @@ export default function TabContainer({
handleItemFocus={_handleItemFocus} handleItemFocus={_handleItemFocus}
panelInfo={panelInfo} panelInfo={panelInfo}
currentTime={currentTime} currentTime={currentTime}
isFilteredByPatnr19={isFilteredByPatnr19}
/> />
)} )}

View File

@@ -6,7 +6,12 @@ import Spotlight from "@enact/spotlight";
import { updatePanel } from "../../../../actions/panelActions"; import { updatePanel } from "../../../../actions/panelActions";
import TVirtualGridList from "../../../../components/TVirtualGridList/TVirtualGridList"; 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 { $L } from "../../../../utils/helperMethods";
import PlayerItemCard, { TYPES } from "../../PlayerItemCard/PlayerItemCard"; import PlayerItemCard, { TYPES } from "../../PlayerItemCard/PlayerItemCard";
import ListEmptyContents from "../TabContents/ListEmptyContents/ListEmptyContents"; import ListEmptyContents from "../TabContents/ListEmptyContents/ListEmptyContents";
@@ -22,10 +27,12 @@ export default function LiveChannelContents({
tabIndex, tabIndex,
handleItemFocus, handleItemFocus,
tabTitle, tabTitle,
panelInfo panelInfo,
isFilteredByPatnr19,
}) { }) {
const dispatch = useDispatch(); const dispatch = useDispatch();
const isClickBlocked = useRef(false); const isClickBlocked = useRef(false);
const scrollToRef = useRef(null);
const handleFocus = useCallback( const handleFocus = useCallback(
() => () => { () => () => {
if (handleItemFocus) { if (handleItemFocus) {
@@ -35,6 +42,18 @@ export default function LiveChannelContents({
[handleItemFocus] [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( const renderItem = useCallback(
({ index, ...rest }) => { ({ index, ...rest }) => {
const { const {
@@ -63,8 +82,8 @@ export default function LiveChannelContents({
category: catNm, category: catNm,
partner: patncNm, partner: patncNm,
contextName: LOG_CONTEXT_NAME.SHOW, contextName: LOG_CONTEXT_NAME.SHOW,
messageId: LOG_MESSAGE_ID.CONTENTCLICK messageId: LOG_MESSAGE_ID.CONTENTCLICK,
} };
dispatch(sendLogTotalRecommend(params)); dispatch(sendLogTotalRecommend(params));
//중복클릭방지 //중복클릭방지
if (isClickBlocked.current) { if (isClickBlocked.current) {
@@ -122,6 +141,7 @@ export default function LiveChannelContents({
startDt={strtDt} startDt={strtDt}
endDt={endDt} endDt={endDt}
currentTime={currentTime} currentTime={currentTime}
currentVideoVisible={currentVideoShowId === liveInfos[index].showId}
/> />
); );
}, },
@@ -140,6 +160,7 @@ export default function LiveChannelContents({
<div className={css.container}> <div className={css.container}>
{liveInfos && liveInfos.length > 0 ? ( {liveInfos && liveInfos.length > 0 ? (
<TVirtualGridList <TVirtualGridList
cbScrollTo={handleScrollTo}
dataSize={liveInfos.length} dataSize={liveInfos.length}
direction="vertical" direction="vertical"
renderItem={renderItem} renderItem={renderItem}