[251010] feat: HomeBanner 비디오 상시재생

🕐 커밋 시간: 2025. 10. 10. 13:38:25

📊 변경 통계:
  • 총 파일: 6개
  • 추가: +150줄
  • 삭제: -16줄

📝 수정된 파일:
  ~ com.twin.app.shoptime/src/actions/playActions.js
  ~ com.twin.app.shoptime/src/views/HomePanel/HomeBanner/HomeBanner.jsx
  ~ com.twin.app.shoptime/src/views/HomePanel/HomeBanner/RandomUnit.jsx
  ~ com.twin.app.shoptime/src/views/HomePanel/HomeBanner/RandomUnit.module.less
  ~ com.twin.app.shoptime/src/views/HomePanel/HomeBanner/RollingUnit.jsx
  ~ com.twin.app.shoptime/src/views/PlayerPanel/PlayerPanel.jsx

🔧 함수 변경 내용:
  📄 com.twin.app.shoptime/src/actions/playActions.js (javascript):
     Deleted: CLEAR_PLAYER_INFO(), resumePlayerControl()
  📄 com.twin.app.shoptime/src/views/HomePanel/HomeBanner/HomeBanner.jsx (javascript):
    🔄 Modified: SpotlightContainerDecorator()
  📄 com.twin.app.shoptime/src/views/HomePanel/HomeBanner/RollingUnit.jsx (javascript):
     Added: createPanelInfo()

🔧 주요 변경 내용:
  • 핵심 비즈니스 로직 개선
This commit is contained in:
2025-10-10 13:38:27 +09:00
parent a00856c461
commit 10d96f4d8a
6 changed files with 434 additions and 447 deletions

View File

@@ -1,10 +1,10 @@
import Spotlight from "@enact/spotlight";
import Spotlight from '@enact/spotlight';
import { URLS } from "../api/apiConfig";
import { TAxios } from "../api/TAxios";
import { panel_names } from "../utils/Config";
import { types } from "./actionTypes";
import { popPanel, pushPanel, updatePanel } from "./panelActions";
import { URLS } from '../api/apiConfig';
import { TAxios } from '../api/TAxios';
import { panel_names } from '../utils/Config';
import { types } from './actionTypes';
import { popPanel, pushPanel, updatePanel } from './panelActions';
//yhcho
/*
@@ -30,7 +30,7 @@ export const startVideoPlayer =
const panels = getState().panels.panels;
const topPanel = panels[panels.length - 1];
let panelWorkingAction = pushPanel;
const panelName = useNewPlayer ? panel_names.PLAYER_PANEL_NEW : panel_names.PLAYER_PANEL;
if (topPanel && topPanel.name === panelName) {
@@ -57,6 +57,7 @@ export const startVideoPlayer =
}, 0);
}
};
export const finishVideoPreview = () => (dispatch, getState) => {
const panels = getState().panels.panels;
const topPanel = panels[panels.length - 1];
@@ -68,12 +69,77 @@ export const finishVideoPreview = () => (dispatch, getState) => {
dispatch(popPanel());
}
};
export const finishModalVideoForce = () => (dispatch, getState) => {
const panels = getState().panels.panels;
// modal PlayerPanel이 존재하는지 확인 (스택 어디에 있든)
const hasModalPlayerPanel = panels.some(
(panel) => panel.name === panel_names.PLAYER_PANEL && panel.panelInfo?.modal
);
if (hasModalPlayerPanel) {
if (startVideoFocusTimer) {
clearTimeout(startVideoFocusTimer);
startVideoFocusTimer = null;
}
// panelName을 지정하면 스택 어디에 있든 해당 패널을 제거
dispatch(popPanel(panel_names.PLAYER_PANEL));
}
};
// 모달 비디오를 일시정지 (패널은 유지)
export const pauseModalVideo = () => (dispatch, getState) => {
const panels = getState().panels.panels;
// modal PlayerPanel 찾기
const modalPlayerPanel = panels.find(
(panel) => panel.name === panel_names.PLAYER_PANEL && panel.panelInfo?.modal
);
if (modalPlayerPanel) {
console.log('[pauseModalVideo] Pausing modal video');
dispatch(
updatePanel({
name: panel_names.PLAYER_PANEL,
panelInfo: {
...modalPlayerPanel.panelInfo,
isPaused: true, // 일시정지 플래그 추가
},
})
);
}
};
// 모달 비디오를 재생 (일시정지 해제)
export const resumeModalVideo = () => (dispatch, getState) => {
const panels = getState().panels.panels;
// modal PlayerPanel 찾기
const modalPlayerPanel = panels.find(
(panel) => panel.name === panel_names.PLAYER_PANEL && panel.panelInfo?.modal
);
if (modalPlayerPanel && modalPlayerPanel.panelInfo?.isPaused) {
console.log('[resumeModalVideo] Resuming modal video');
dispatch(
updatePanel({
name: panel_names.PLAYER_PANEL,
panelInfo: {
...modalPlayerPanel.panelInfo,
isPaused: false, // 일시정지 해제
},
})
);
}
};
// 채팅 로그 가져오기 IF-LGSP-371
export const getChatLog =
({ patnrId, showId }) =>
(dispatch, getState) => {
const onSuccess = (response) => {
console.log("getChatLog onSuccess", response.data);
console.log('getChatLog onSuccess', response.data);
dispatch({
type: types.GET_CHAT_LOG,
@@ -82,10 +148,10 @@ export const getChatLog =
};
const onFail = (error) => {
console.error("getChatLog onFail", error);
console.error('getChatLog onFail', error);
};
TAxios(dispatch, getState, "get", URLS.CHAT_LOG, { patnrId, showId }, {}, onSuccess, onFail);
TAxios(dispatch, getState, 'get', URLS.CHAT_LOG, { patnrId, showId }, {}, onSuccess, onFail);
};
// VOD 자막 가져오기 IF-LGSP-072
@@ -93,7 +159,7 @@ export const getSubTitle =
({ showSubtitleUrl }) =>
(dispatch, getState) => {
const onSuccess = (response) => {
console.log("getSubTitle onSuccess", response.data);
console.log('getSubTitle onSuccess', response.data);
dispatch({
type: types.GET_SUBTITLE,
@@ -102,15 +168,15 @@ export const getSubTitle =
};
const onFail = (error) => {
console.error("getSubTitle onFail", error);
console.error('getSubTitle onFail', error);
dispatch({
type: types.GET_SUBTITLE,
payload: { url: showSubtitleUrl, data: "Error" },
payload: { url: showSubtitleUrl, data: 'Error' },
});
};
if (!getState().play.subTitleBlobs[showSubtitleUrl]) {
TAxios(dispatch, getState, "get", URLS.SUBTITLE, { showSubtitleUrl }, {}, onSuccess, onFail);
TAxios(dispatch, getState, 'get', URLS.SUBTITLE, { showSubtitleUrl }, {}, onSuccess, onFail);
} else {
console.log("playActions getSubTitle no Nothing it's exist", showSubtitleUrl);
}
@@ -129,33 +195,35 @@ export const CLEAR_PLAYER_INFO = () => ({
* @param {object} videoInfo - 재생할 비디오 정보 (url, id 등)
*/
export const requestPlayControl = (ownerId, videoInfo) => (dispatch, getState) => {
const { playerControl } = getState().home;
const currentOwnerId = playerControl.ownerId;
const { playerControl } = getState().home;
const currentOwnerId = playerControl.ownerId;
if (currentOwnerId === ownerId) return; // 이미 제어권 소유
if (currentOwnerId === ownerId) return; // 이미 제어권 소유
if (currentOwnerId) {
// 현재 제어중인 컴포넌트가 영구재생 배너이면 '일시정지'
if (currentOwnerId === 'banner0_persistent') {
dispatch(pausePlayerControl());
} else {
// 다른 미리보기라면 완전히 숨김
dispatch(releasePlayControl(currentOwnerId, true));
}
if (currentOwnerId) {
// 현재 제어중인 컴포넌트가 영구재생 배너이면 '일시정지'
if (currentOwnerId === 'banner0_persistent') {
dispatch(pausePlayerControl());
} else {
// 다른 미리보기라면 완전히 숨김
dispatch(releasePlayControl(currentOwnerId, true));
}
// 1. 매니저 상태 업데이트
dispatch({ type: types.SET_PLAYER_CONTROL, payload: { ownerId } });
// 2. 공유 PlayerPanel의 상태 업데이트
dispatch(updatePanel({
name: panel_names.PLAYER_PANEL,
panelInfo: {
isHidden: false,
modal: true,
...videoInfo
}
}));
}
// 1. 매니저 상태 업데이트
dispatch({ type: types.SET_PLAYER_CONTROL, payload: { ownerId } });
// 2. 공유 PlayerPanel의 상태 업데이트
dispatch(
updatePanel({
name: panel_names.PLAYER_PANEL,
panelInfo: {
isHidden: false,
modal: true,
...videoInfo,
},
})
);
};
/**
@@ -163,28 +231,34 @@ export const requestPlayControl = (ownerId, videoInfo) => (dispatch, getState) =
* @param {string} ownerId - 제어권을 해제하는 컴포넌트의 고유 ID
* @param {boolean} fromPreemption - 다른 요청에 의해 강제로 중단되었는지 여부
*/
export const releasePlayControl = (ownerId, fromPreemption = false) => (dispatch, getState) => {
export const releasePlayControl =
(ownerId, fromPreemption = false) =>
(dispatch, getState) => {
const { playerControl } = getState().home;
if (fromPreemption || playerControl.ownerId === ownerId) {
// 1. 공유 PlayerPanel을 다시 숨김
dispatch(updatePanel({
name: panel_names.PLAYER_PANEL,
panelInfo: {
isHidden: true,
}
}));
// 1. 공유 PlayerPanel을 다시 숨김
dispatch(
updatePanel({
name: panel_names.PLAYER_PANEL,
panelInfo: {
isHidden: true,
},
})
);
// 2. 매니저 상태 업데이트 (현재 소유주 없음)
dispatch({ type: types.CLEAR_PLAYER_CONTROL });
// 2. 매니저 상태 업데이트 (현재 소유주 없음)
dispatch({ type: types.CLEAR_PLAYER_CONTROL });
// 3. 만약 '일시정지'된 영구재생 비디오가 있었다면, 제어권을 되돌려주고 다시 재생
if (playerControl.isPaused && playerControl.ownerId === 'banner0_persistent') {
const persistentVideoInfo = { /* 영구 비디오 정보를 가져오는 로직 (필요시) */ };
dispatch(requestPlayControl('banner0_persistent', persistentVideoInfo));
}
// 3. 만약 '일시정지'된 영구재생 비디오가 있었다면, 제어권을 되돌려주고 다시 재생
if (playerControl.isPaused && playerControl.ownerId === 'banner0_persistent') {
const persistentVideoInfo = {
/* 영구 비디오 정보를 가져오는 로직 (필요시) */
};
dispatch(requestPlayControl('banner0_persistent', persistentVideoInfo));
}
}
};
};
/**
* 현재 재생 중인 비디오를 '일시정지' 상태로 변경하는 액션.
@@ -224,25 +298,25 @@ export const resumePlayerControl = (ownerId) => (dispatch, getState) => {
* 이 액션은 어떤 배너에서든 클릭 시 호출됩니다.
*/
export const goToFullScreen = () => (dispatch, getState) => {
// 공유 PlayerPanel의 'modal' 상태를 false로 변경하여 전체화면으로 전환
dispatch(updatePanel({
name: panel_names.PLAYER_PANEL,
panelInfo: {
modal: false,
isHidden: false, // 혹시 숨겨져 있었다면 보이도록
}
}));
// 공유 PlayerPanel의 'modal' 상태를 false로 변경하여 전체화면으로 전환
dispatch(
updatePanel({
name: panel_names.PLAYER_PANEL,
panelInfo: {
modal: false,
isHidden: false, // 혹시 숨겨져 있었다면 보이도록
},
})
);
};
/**
* 영구재생 비디오를 일시정지 상태로 만듭니다. (내부 사용)
*/
export const pausePlayerControl = () => ({
type: types.PAUSE_PLAYER_CONTROL
type: types.PAUSE_PLAYER_CONTROL,
});
/**
* 전체화면 플레이어에서 미리보기 상태로 복귀할 때 호출됩니다.
* 중앙 'playerControl' 상태를 확인하여 올바른 위치와 비디오로 복원합니다.
@@ -270,4 +344,4 @@ export const returnToPreview = () => (dispatch, getState) => {
// 돌아갈 곳이 없으면 그냥 플레이어를 닫음
dispatch(finishVideoPreview());
}
};
};

View File

@@ -495,7 +495,7 @@ export default function HomeBanner({ firstSpot, spotlightId, handleItemFocus, ha
// );
const renderItem = useCallback(
(index, isHorizontal) => {
(index, isHorizontal, videoPlayerable = false) => {
const data = bannerDataList?.[index] ?? {};
return (
<div className={!isHorizontal ? css.imgBox : undefined}>
@@ -507,6 +507,7 @@ export default function HomeBanner({ firstSpot, spotlightId, handleItemFocus, ha
spotlightId={'banner' + index}
handleShelfFocus={_handleShelfFocus}
handleItemFocus={_handleItemFocus}
videoPlayerable={videoPlayerable}
/>
) : data.shptmDspyTpNm === 'Random' ? (
<Random
@@ -517,6 +518,7 @@ export default function HomeBanner({ firstSpot, spotlightId, handleItemFocus, ha
handleShelfFocus={_handleShelfFocus}
handleItemFocus={_handleItemFocus}
randomNumber={data.randomIndex}
videoPlayerable={videoPlayerable}
/>
) : (
<SpottableComponent spotlightId={'banner' + index}>
@@ -629,10 +631,10 @@ export default function HomeBanner({ firstSpot, spotlightId, handleItemFocus, ha
return (
<>
<ContainerBasic className={css.smallBox}>
{renderItem(0, true)}
{renderItem(1, true)}
{renderItem(0, true, true)}
{renderItem(1, true, true)}
</ContainerBasic>
{renderItem(2, false)}
{renderItem(2, false, false)}
{/* //NOTE - 약관 동의 여부 & 추후 API 따라 팝업 표시 여부 결정 */}
{introTermsAgree === 'Y' ? (
<div className={css.imgBox}>
@@ -645,7 +647,7 @@ export default function HomeBanner({ firstSpot, spotlightId, handleItemFocus, ha
/>
</div>
) : (
renderItem(3, false)
renderItem(3, false, false)
)}
</>
);
@@ -653,23 +655,23 @@ export default function HomeBanner({ firstSpot, spotlightId, handleItemFocus, ha
case 'DSP00202': {
return (
<>
{renderItem(0, false)}
{renderItem(0, false, false)}
<ContainerBasic className={css.smallBox}>
{renderItem(1, true)}
{renderItem(2, true)}
{renderItem(1, true, true)}
{renderItem(2, true, true)}
</ContainerBasic>
{renderItem(3, false)}
{renderItem(3, false, false)}
</>
);
}
case 'DSP00203': {
return (
<>
{renderItem(0, false)}
{renderItem(1, false)}
{renderItem(0, false, false)}
{renderItem(1, false, false)}
<ContainerBasic className={css.smallBox}>
{renderItem(2, true)}
{renderItem(3, true)}
{renderItem(2, true, true)}
{renderItem(3, true, true)}
</ContainerBasic>
</>
);

View File

@@ -1,55 +1,40 @@
import React, {
useCallback,
useEffect,
useMemo,
useRef,
useState,
} from "react";
import React, { useCallback, useEffect, useMemo, useRef, useState } from 'react';
import classNames from "classnames";
import { useDispatch, useSelector } from "react-redux";
import classNames from 'classnames';
import { useDispatch, useSelector } from 'react-redux';
import Spotlight from "@enact/spotlight";
import SpotlightContainerDecorator from "@enact/spotlight/SpotlightContainerDecorator";
import Spottable from "@enact/spotlight/Spottable";
import { getContainerId } from "@enact/spotlight/src/container";
import Spotlight from '@enact/spotlight';
import SpotlightContainerDecorator from '@enact/spotlight/SpotlightContainerDecorator';
import Spottable from '@enact/spotlight/Spottable';
import { getContainerId } from '@enact/spotlight/src/container';
import btnPlay from "../../../../assets/images/btn/btn-play-thumb-nor.png";
import defaultLogoImg from "../../../../assets/images/ic-tab-partners-default@3x.png";
import emptyHorImage from "../../../../assets/images/img-home-banner-empty-hor.png";
import emptyVerImage from "../../../../assets/images/img-home-banner-empty-ver.png";
import defaultImageItem from "../../../../assets/images/img-thumb-empty-product@3x.png";
import liveShow from "../../../../assets/images/tag-liveshow.png";
import { changeAppStatus } from "../../../actions/commonActions";
import { updateHomeInfo } from "../../../actions/homeActions";
import {
sendLogTopContents,
sendLogTotalRecommend,
} from "../../../actions/logActions";
import { pushPanel } from "../../../actions/panelActions";
import {
finishVideoPreview,
startVideoPlayer,
} from "../../../actions/playActions";
import CustomImage from "../../../components/CustomImage/CustomImage";
import usePriceInfo from "../../../hooks/usePriceInfo";
import btnPlay from '../../../../assets/images/btn/btn-play-thumb-nor.png';
import defaultLogoImg from '../../../../assets/images/ic-tab-partners-default@3x.png';
import emptyHorImage from '../../../../assets/images/img-home-banner-empty-hor.png';
import emptyVerImage from '../../../../assets/images/img-home-banner-empty-ver.png';
import defaultImageItem from '../../../../assets/images/img-thumb-empty-product@3x.png';
import liveShow from '../../../../assets/images/tag-liveshow.png';
import { changeAppStatus } from '../../../actions/commonActions';
import { updateHomeInfo } from '../../../actions/homeActions';
import { sendLogTopContents, sendLogTotalRecommend } from '../../../actions/logActions';
import { pushPanel } from '../../../actions/panelActions';
import { finishVideoPreview, startVideoPlayer } from '../../../actions/playActions';
import CustomImage from '../../../components/CustomImage/CustomImage';
import usePriceInfo from '../../../hooks/usePriceInfo';
import {
LOG_CONTEXT_NAME,
LOG_MENU,
LOG_MESSAGE_ID,
LOG_TP_NO,
panel_names,
} from "../../../utils/Config";
import { $L, formatGMTString } from "../../../utils/helperMethods";
import { TEMPLATE_CODE_CONF } from "../HomePanel";
import css from "./RandomUnit.module.less";
} from '../../../utils/Config';
import { $L, formatGMTString } from '../../../utils/helperMethods';
import { TEMPLATE_CODE_CONF } from '../HomePanel';
import css from './RandomUnit.module.less';
const SpottableComponent = Spottable("div");
const SpottableComponent = Spottable('div');
const Container = SpotlightContainerDecorator(
{ enterTo: "last-focused" },
"div"
);
const Container = SpotlightContainerDecorator({ enterTo: 'last-focused' }, 'div');
export default function RandomUnit({
bannerData,
@@ -58,30 +43,25 @@ export default function RandomUnit({
handleShelfFocus,
handleItemFocus,
randomNumber,
videoPlayerable = false,
}) {
const dispatch = useDispatch();
const bannerDetailInfos =
bannerData && bannerData.bannerDetailInfos
? bannerData.bannerDetailInfos
: [];
bannerData && bannerData.bannerDetailInfos ? bannerData.bannerDetailInfos : [];
const shptmTmplCd = useSelector(
(state) => state.home?.bannerData?.shptmTmplCd
);
const shptmTmplCd = useSelector((state) => state.home?.bannerData?.shptmTmplCd);
const nowMenu = useSelector((state) => state.common.menu.nowMenu);
const entryMenu = useSelector((state) => state.common.menu.entryMenu);
const homeCategory = useSelector(
(state) => state.home.menuData?.data?.homeCategory
);
const homeCategory = useSelector((state) => state.home.menuData?.data?.homeCategory);
const countryCode = useSelector((state) => state.common.httpHeader.cntry_cd);
// 정상적으로 로딩되면 빈객체로 넘어가고 , 에러가 나면 객체안에 타입이 담겨옵니다.
const broadcast = useSelector((state) => state.common.broadcast);
const { curationId, curationTitle } = useSelector((state) => state.home);
const [randomData, setRandomData] = useState("");
const [priceInfos, setpriceInfos] = useState("");
const [randomData, setRandomData] = useState('');
const [priceInfos, setpriceInfos] = useState('');
const [isFocused, setIsFocused] = useState(false);
const [videoError, setVideoError] = useState(false);
const [liveIndicies, setLiveIndicies] = useState([]);
@@ -89,9 +69,7 @@ export default function RandomUnit({
const timerRef = useRef();
const bannerDataRef = useRef(bannerData);
const randomDataRef = useRef(
bannerDetailInfos &&
randomNumber !== undefined &&
bannerDetailInfos.length > 0
bannerDetailInfos && randomNumber !== undefined && bannerDetailInfos.length > 0
? bannerDetailInfos[randomNumber]
: null
);
@@ -104,14 +82,14 @@ export default function RandomUnit({
switch (currentRandomData?.shptmBanrTpCd) {
// case: "LIVE" or "VOD"
case "DSP00301":
case "DSP00302":
case 'DSP00301':
case 'DSP00302':
contId = currentRandomData?.showId;
contNm = currentRandomData?.showNm;
break;
// case: "Image Banner"
case "DSP00303":
case 'DSP00303':
contId = currentRandomData?.shptmLnkTpCd;
contNm = currentRandomData?.shptmLnkTpNm;
break;
@@ -124,27 +102,27 @@ export default function RandomUnit({
}
if (
currentRandomData?.shptmLnkTpCd === "DSP00503" || // "Hot Picks"
currentRandomData?.shptmLnkTpCd === "DSP00509" // "Theme"
currentRandomData?.shptmLnkTpCd === 'DSP00503' || // "Hot Picks"
currentRandomData?.shptmLnkTpCd === 'DSP00509' // "Theme"
) {
contNm = contNm + " | " + currentRandomData?.lnkCurationId;
contNm = contNm + ' | ' + currentRandomData?.lnkCurationId;
}
return {
banrNo: `${currentRandomData?.banrDpOrd}`,
banrTpNm: currentRandomData?.vtctpYn
? currentRandomData.vtctpYn === "Y"
? "Vertical"
: "Horizontal"
: "",
? currentRandomData.vtctpYn === 'Y'
? 'Vertical'
: 'Horizontal'
: '',
contId,
contNm,
contTpNm: currentRandomData?.shptmBanrTpNm ?? "",
dspyTpNm: bannerDataRef.current?.shptmDspyTpNm ?? "",
expsOrd: bannerDataRef.current?.banrLctnNo ?? "",
linkTpCd: "",
patncNm: currentRandomData?.patncNm ?? "",
patnrId: currentRandomData?.patnrId ?? "",
contTpNm: currentRandomData?.shptmBanrTpNm ?? '',
dspyTpNm: bannerDataRef.current?.shptmDspyTpNm ?? '',
expsOrd: bannerDataRef.current?.banrLctnNo ?? '',
linkTpCd: '',
patncNm: currentRandomData?.patncNm ?? '',
patnrId: currentRandomData?.patnrId ?? '',
tmplCd: shptmTmplCd,
};
}
@@ -157,26 +135,24 @@ export default function RandomUnit({
const data = randomDataRef.current;
const newParams =
data.shptmBanrTpNm === "LIVE"
data.shptmBanrTpNm === 'LIVE'
? {
showId: data.showId,
showTitle: data.showNm,
displayType: "Fix",
bannerType: "Horizontal",
displayType: 'Fix',
bannerType: 'Horizontal',
}
: {
contentId: data.showId,
contentTitle: data.showNm,
displayType: "Rolling",
bannerType: "Vertical",
displayType: 'Rolling',
bannerType: 'Vertical',
};
if (data && nowMenu === LOG_MENU.HOME_TOP) {
const logParams = {
contextName: LOG_CONTEXT_NAME.HOME,
messageId: bannerClick
? LOG_MESSAGE_ID.BANNER_CLICK
: LOG_MESSAGE_ID.BANNER,
messageId: bannerClick ? LOG_MESSAGE_ID.BANNER_CLICK : LOG_MESSAGE_ID.BANNER,
...newParams,
curationId,
curationTitle,
@@ -189,9 +165,9 @@ export default function RandomUnit({
};
dispatch(sendLogTotalRecommend(logParams));
if (bannerClick) {
console.log("###bannerClick", logParams);
console.log('###bannerClick', logParams);
} else {
console.log("###bannerShow", logParams);
console.log('###bannerShow', logParams);
}
}
},
@@ -204,15 +180,9 @@ export default function RandomUnit({
// 라이브 영상이 1개 라도 순간적인 네트워크 오류나, 일시적인 오류 일수 있으므로, 일정 시간을 주고, 비디오를 다시 호출 한다.
useEffect(() => {
if (
bannerDetailInfos &&
bannerDetailInfos.length > 0 &&
randomNumber !== undefined
) {
if (bannerDetailInfos && bannerDetailInfos.length > 0 && randomNumber !== undefined) {
const indices = bannerDetailInfos
.map((info, index) =>
info && info.shptmBanrTpNm === "LIVE" ? index : null
)
.map((info, index) => (info && info.shptmBanrTpNm === 'LIVE' ? index : null))
.filter((index) => index !== null && index !== randomNumber);
setLiveIndicies(indices);
@@ -224,7 +194,7 @@ export default function RandomUnit({
return dispatch(
pushPanel({
name: panel_names.FEATURED_BRANDS_PANEL,
panelInfo: { from: "gnb", patnrId: randomData.patnrId },
panelInfo: { from: 'gnb', patnrId: randomData.patnrId },
})
);
}, [randomData, dispatch]);
@@ -233,6 +203,11 @@ export default function RandomUnit({
const onFocus = useCallback(() => {
setIsFocused(true);
// video가 플레이 가능한 경우 기존 비디오 즉시 종료료
if (videoPlayerable) {
dispatch(finishVideoPreview());
}
if (handleItemFocus) {
handleItemFocus();
}
@@ -249,9 +224,9 @@ export default function RandomUnit({
setIsFocused(false);
clearTimeout(timerRef.current);
console.log("[RandomUnit] onBlur");
dispatch(finishVideoPreview());
console.log("[RandomUnit] finishVideoPreview");
console.log('[RandomUnit] onBlur');
// dispatch(finishVideoPreview());
console.log('[RandomUnit] finishVideoPreview');
}, [isFocused]);
// DSP00501 : Featured Brands
@@ -265,11 +240,9 @@ export default function RandomUnit({
// DSP00509 : Theme
const categoryData = useMemo(() => {
if (randomData && randomData.shptmLnkTpCd === "DSP00505") {
if (randomData && randomData.shptmLnkTpCd === 'DSP00505') {
if (homeCategory && homeCategory.length > 0) {
const foundCategory = homeCategory.find(
(data) => data.lgCatCd === randomData.lgCatCd
);
const foundCategory = homeCategory.find((data) => data.lgCatCd === randomData.lgCatCd);
if (foundCategory) {
return {
lgCatNm: foundCategory.lgCatNm,
@@ -287,21 +260,21 @@ export default function RandomUnit({
const linkType = randomData.shptmLnkTpCd;
switch (linkType) {
case "DSP00501":
case 'DSP00501':
linkInfo = {
name: panel_names.FEATURED_BRANDS_PANEL,
panelInfo: { from: "gnb", patnrId: randomData.patnrId },
panelInfo: { from: 'gnb', patnrId: randomData.patnrId },
};
break;
case "DSP00502":
case 'DSP00502':
linkInfo = {
name: panel_names.TRENDING_NOW_PANEL,
panelInfo: {},
};
break;
case "DSP00503":
case 'DSP00503':
linkInfo = {
name: panel_names.HOT_PICKS_PANEL,
panelInfo: {
@@ -311,7 +284,7 @@ export default function RandomUnit({
};
break;
case "DSP00504":
case 'DSP00504':
linkInfo = {
name: panel_names.ON_SALE_PANEL,
panelInfo: {
@@ -320,7 +293,7 @@ export default function RandomUnit({
};
break;
case "DSP00505":
case 'DSP00505':
if (Object.keys(categoryData).length > 0) {
linkInfo = {
name: panel_names.CATEGORY_PANEL,
@@ -337,7 +310,7 @@ export default function RandomUnit({
}
break;
case "DSP00506":
case 'DSP00506':
linkInfo = {
name: panel_names.DETAIL_PANEL,
panelInfo: {
@@ -348,29 +321,29 @@ export default function RandomUnit({
};
break;
case "DSP00507":
case 'DSP00507':
linkInfo = {
patnrId: randomData.patnrId,
showId: randomData.showId,
shptmBanrTpNm: "VOD",
shptmBanrTpNm: 'VOD',
lgCatCd: randomData.lgCatCd,
modal: false,
};
break;
case "DSP00508":
case 'DSP00508':
linkInfo = {
name: panel_names.DETAIL_PANEL,
panelInfo: {
patnrId: randomData.patnrId,
curationId: randomData.lnkCurationId,
prdtId: randomData.prdtId,
type: "theme",
type: 'theme',
},
};
break;
case "DSP00509":
case 'DSP00509':
linkInfo = {
name: panel_names.THEME_CURATION_PANEL,
panelInfo: {
@@ -387,14 +360,14 @@ export default function RandomUnit({
break;
}
let action = linkType === "DSP00507" ? startVideoPlayer : pushPanel;
let action = linkType === 'DSP00507' ? startVideoPlayer : pushPanel;
dispatch(action(linkInfo));
sendBannerLog(true);
dispatch(
sendLogTopContents({
...topContentsLogInfo,
inDt: formatGMTString(new Date()) ?? "",
inDt: formatGMTString(new Date()) ?? '',
logTpNo: LOG_TP_NO.TOP_CONTENTS.CLICK,
})
);
@@ -428,17 +401,11 @@ export default function RandomUnit({
dispatch(
sendLogTopContents({
...topContentsLogInfo,
inDt: formatGMTString(new Date()) ?? "",
inDt: formatGMTString(new Date()) ?? '',
logTpNo: LOG_TP_NO.TOP_CONTENTS.CLICK,
})
);
}, [
dispatch,
randomData?.patnrId,
randomData?.prdtId,
topContentsLogInfo,
sendBannerLog,
]);
}, [dispatch, randomData?.patnrId, randomData?.prdtId, topContentsLogInfo, sendBannerLog]);
// 비디오 클릭
const videoClick = useCallback(() => {
@@ -451,7 +418,7 @@ export default function RandomUnit({
panelInfo: {
lastFocusedTargetId,
focusedContainerId: TEMPLATE_CODE_CONF.TOP,
currentSpot: currentSpot?.getAttribute("data-spotlight-id"),
currentSpot: currentSpot?.getAttribute('data-spotlight-id'),
},
})
);
@@ -462,7 +429,7 @@ export default function RandomUnit({
showUrl: randomData.showUrl,
patnrId: randomData.patnrId,
showId: randomData.showId,
shptmBanrTpNm: randomData.showId ? randomData.shptmBanrTpNm : "MEDIA",
shptmBanrTpNm: randomData.showId ? randomData.shptmBanrTpNm : 'MEDIA',
lgCatCd: randomData.lgCatCd,
chanId: randomData.brdcChnlId,
modal: false,
@@ -474,22 +441,14 @@ export default function RandomUnit({
dispatch(
sendLogTopContents({
...topContentsLogInfo,
inDt: formatGMTString(new Date()) ?? "",
inDt: formatGMTString(new Date()) ?? '',
logTpNo: LOG_TP_NO.TOP_CONTENTS.CLICK,
})
);
sendBannerLog(true);
onBlur();
}, [
randomData,
spotlightId,
topContentsLogInfo,
nowMenu,
randomDataRef,
sendBannerLog,
onBlur,
]);
}, [randomData, spotlightId, topContentsLogInfo, nowMenu, randomDataRef, sendBannerLog, onBlur]);
// 투데이즈 딜 가격 정보
const { originalPrice, discountedPrice, discountRate, offerInfo } =
@@ -504,7 +463,7 @@ export default function RandomUnit({
const params = {
...topContentsLogInfo,
entryMenu: _entryMenu,
inDt: formatGMTString(new Date()) ?? "",
inDt: formatGMTString(new Date()) ?? '',
logTpNo: LOG_TP_NO.TOP_CONTENTS.VIEW,
nowMenu: _nowMenu,
};
@@ -544,9 +503,7 @@ export default function RandomUnit({
showUrl: randomData.showUrl,
patnrId: randomData.patnrId,
showId: randomData.showId,
shptmBanrTpNm: randomData.showId
? randomData.shptmBanrTpNm
: "MEDIA",
shptmBanrTpNm: randomData.showId ? randomData.shptmBanrTpNm : 'MEDIA',
lgCatCd: randomData.lgCatCd,
chanId: randomData.brdcChnlId,
modal: true,
@@ -568,7 +525,7 @@ export default function RandomUnit({
}, [isFocused]);
useEffect(() => {
if (isFocused && broadcast?.type === "videoError") {
if (isFocused && broadcast?.type === 'videoError') {
clearTimeout(timerRef.current);
setVideoError(true);
dispatch(finishVideoPreview());
@@ -595,23 +552,15 @@ export default function RandomUnit({
return (
<>
<Container
className={classNames(
css.rollingWrap,
isHorizontal && css.isHorizontalWrap
)}
className={classNames(css.rollingWrap, isHorizontal && css.isHorizontalWrap)}
onFocus={shelfFocus}
>
{randomData?.shptmBanrTpNm == "Image Banner" ? (
{randomData?.shptmBanrTpNm == 'Image Banner' ? (
<SpottableComponent
className={classNames(
css.itemBox,
isHorizontal && css.isHorizontal
)}
className={classNames(css.itemBox, isHorizontal && css.isHorizontal)}
onClick={imageBannerClick}
spotlightId={spotlightId}
aria-label={
randomData.prdtNm ? randomData.prdtNm : randomData.tmnlImgNm
}
aria-label={randomData.prdtNm ? randomData.prdtNm : randomData.tmnlImgNm}
>
<div className={css.imgBanner}>
<CustomImage
@@ -622,32 +571,21 @@ export default function RandomUnit({
/>
</div>
</SpottableComponent>
) : randomData?.shptmBanrTpNm == "LIVE" ||
randomData?.shptmBanrTpNm == "VOD" ? (
) : randomData?.shptmBanrTpNm == 'LIVE' || randomData?.shptmBanrTpNm == 'VOD' ? (
<SpottableComponent
className={classNames(
css.itemBox,
isHorizontal && css.isHorizontal
)}
className={classNames(css.itemBox, isHorizontal && css.isHorizontal)}
onClick={videoError === true ? videoErrorClick : videoClick}
onFocus={onFocus}
onBlur={onBlur}
spotlightId={spotlightId}
aria-label={
randomData.shptmBanrTpNm == "LIVE"
? "LIVE " + randomData.showNm
: randomData.showNm
randomData.shptmBanrTpNm == 'LIVE' ? 'LIVE ' + randomData.showNm : randomData.showNm
}
alt={"LIVE"}
alt={'LIVE'}
>
{randomData.shptmBanrTpNm == "LIVE" && videoError === false && (
{randomData.shptmBanrTpNm == 'LIVE' && videoError === false && (
<p className={css.liveIcon}>
<CustomImage
delay={0}
src={liveShow}
animationSpeed="fast"
ariaLabel="LIVE icon"
/>
<CustomImage delay={0} src={liveShow} animationSpeed="fast" ariaLabel="LIVE icon" />
</p>
)}
@@ -656,26 +594,16 @@ export default function RandomUnit({
<div className={css.errorContents}>
<div>
{randomData.patncLogoPath && (
<img
className={css.errorlogo}
src={randomData.patncLogoPath}
/>
<img className={css.errorlogo} src={randomData.patncLogoPath} />
)}
<p className={css.errorText}>
{$L("Click the screen to see more products!")}
</p>
<p className={css.errorText}>{$L('Click the screen to see more products!')}</p>
</div>
</div>
)}
{/* 배너 메인 이미지 */}
{videoError === false && (
<div
className={classNames(
css.itemBox,
isHorizontal && css.isHorizontal
)}
>
<div className={classNames(css.itemBox, isHorizontal && css.isHorizontal)}>
{randomData.tmnlImgPath ? (
<CustomImage
delay={0}
@@ -686,9 +614,7 @@ export default function RandomUnit({
) : (
<CustomImage
delay={0}
src={
randomData.vtctpYn === "Y" ? emptyVerImage : emptyHorImage
}
src={randomData.vtctpYn === 'Y' ? emptyVerImage : emptyHorImage}
animationSpeed="fast"
ariaLabel={randomData.tmnImgNm}
/>
@@ -699,7 +625,7 @@ export default function RandomUnit({
{/* 플레이 버튼 [>] */}
{videoError === false && (
<div className={css.btnPlay}>
{randomData.tmnlImgPath == null ? "" : <img src={btnPlay} />}
{randomData.tmnlImgPath == null ? '' : <img src={btnPlay} />}
</div>
)}
@@ -723,15 +649,13 @@ export default function RandomUnit({
className={classNames(
css.itemBox,
css.todaysDeals,
countryCode === "RU" ? css.ru : "",
countryCode === "DE" ? css.de : "",
countryCode === 'RU' ? css.ru : '',
countryCode === 'DE' ? css.de : '',
isHorizontal && css.isHorizontal
)}
onClick={todayDealClick}
spotlightId={spotlightId}
aria-label={
randomData.prdtNm ? randomData.prdtNm : randomData.tmnlImgNm
}
aria-label={randomData.prdtNm ? randomData.prdtNm : randomData.tmnlImgNm}
>
<div className={css.productInfo}>
<div className={css.todaysDealTitle}>{$L("TODAY's DEALS")}</div>
@@ -742,7 +666,7 @@ export default function RandomUnit({
}}
/>
<div className={css.accBox}>
{parseFloat(originalPrice?.replace("$", "")) === 0
{parseFloat(originalPrice?.replace('$', '')) === 0
? randomData?.offerInfo
: discountRate
? discountedPrice
@@ -751,10 +675,9 @@ export default function RandomUnit({
<span className={css.saleAccBox}>{originalPrice}</span>
)}
</div>
{isHorizontal &&
parseFloat(originalPrice?.replace("$", "")) !== 0 && (
<span className={css.saleAccBox}>{originalPrice}</span>
)}
{isHorizontal && parseFloat(originalPrice?.replace('$', '')) !== 0 && (
<span className={css.saleAccBox}>{originalPrice}</span>
)}
</div>
<div className={css.itemImgBox}>

View File

@@ -269,8 +269,20 @@
}
}
// .videoModal {
// &::after {
// .focused(@boxShadow:0, @borderRadius: 12px);
// border: 6px solid @PRIMARY_COLOR_RED;
// top: -4px;
// right: -4px;
// bottom: -4px;
// left: -4px;
// }
// }
.videoModal {
&::after {
// 포커스를 받았을 때만 붉은색 테두리 표시
&:focus-within::after {
.focused(@boxShadow:0, @borderRadius: 12px);
border: 6px solid @PRIMARY_COLOR_RED;
top: -4px;
@@ -278,4 +290,4 @@
bottom: -4px;
left: -4px;
}
}
}

View File

@@ -1,62 +1,50 @@
import React, {
useCallback,
useEffect,
useMemo,
useRef,
useState,
} from "react";
import React, { useCallback, useEffect, useMemo, useRef, useState } from 'react';
import classNames from "classnames";
import { useDispatch, useSelector } from "react-redux";
import classNames from 'classnames';
import { useDispatch, useSelector } from 'react-redux';
import Spotlight from "@enact/spotlight";
import SpotlightContainerDecorator from "@enact/spotlight/SpotlightContainerDecorator";
import Spottable from "@enact/spotlight/Spottable";
import { getContainerId } from "@enact/spotlight/src/container";
import Spotlight from '@enact/spotlight';
import SpotlightContainerDecorator from '@enact/spotlight/SpotlightContainerDecorator';
import Spottable from '@enact/spotlight/Spottable';
import { getContainerId } from '@enact/spotlight/src/container';
import btnPlay from "../../../../assets/images/btn/btn-play-thumb-nor.png";
import defaultLogoImg from "../../../../assets/images/ic-tab-partners-default@3x.png";
import emptyHorImage from "../../../../assets/images/img-home-banner-empty-hor.png";
import emptyVerImage from "../../../../assets/images/img-home-banner-empty-ver.png";
import defaultImageItem from "../../../../assets/images/img-thumb-empty-product@3x.png";
import liveShow from "../../../../assets/images/tag-liveshow.png";
import { setBannerIndex, updateHomeInfo } from "../../../actions/homeActions";
import {
sendLogTopContents,
sendLogTotalRecommend,
} from "../../../actions/logActions";
import { pushPanel } from "../../../actions/panelActions";
import { startVideoPlayer } from "../../../actions/playActions";
import CustomImage from "../../../components/CustomImage/CustomImage";
import usePriceInfo from "../../../hooks/usePriceInfo";
import btnPlay from '../../../../assets/images/btn/btn-play-thumb-nor.png';
import defaultLogoImg from '../../../../assets/images/ic-tab-partners-default@3x.png';
import emptyHorImage from '../../../../assets/images/img-home-banner-empty-hor.png';
import emptyVerImage from '../../../../assets/images/img-home-banner-empty-ver.png';
import defaultImageItem from '../../../../assets/images/img-thumb-empty-product@3x.png';
import liveShow from '../../../../assets/images/tag-liveshow.png';
import { setBannerIndex, updateHomeInfo } from '../../../actions/homeActions';
import { sendLogTopContents, sendLogTotalRecommend } from '../../../actions/logActions';
import { pushPanel } from '../../../actions/panelActions';
import { startVideoPlayer } from '../../../actions/playActions';
import CustomImage from '../../../components/CustomImage/CustomImage';
import usePriceInfo from '../../../hooks/usePriceInfo';
import {
LOG_CONTEXT_NAME,
LOG_MENU,
LOG_MESSAGE_ID,
LOG_TP_NO,
panel_names,
} from "../../../utils/Config";
import { $L, formatGMTString } from "../../../utils/helperMethods";
import { TEMPLATE_CODE_CONF } from "../HomePanel";
import css from "./RollingUnit.module.less";
} from '../../../utils/Config';
import { $L, formatGMTString } from '../../../utils/helperMethods';
import { TEMPLATE_CODE_CONF } from '../HomePanel';
import css from './RollingUnit.module.less';
const SpottableComponent = Spottable("div");
const SpottableComponent = Spottable('div');
const Container = SpotlightContainerDecorator(
{ enterTo: "last-focused", preserveId: true },
"div"
);
const Container = SpotlightContainerDecorator({ enterTo: 'last-focused', preserveId: true }, 'div');
const LINK_TYPES = {
FEATURED_BRANDS: "DSP00501",
TRENDING_NOW: "DSP00502",
HOT_PICKS: "DSP00503",
ON_SALE: "DSP00504",
CATEGORY: "DSP00505",
PRODUCT_DETAIL: "DSP00506",
VOD: "DSP00507",
SHOW_DETAIL: "DSP00508",
THEME: "DSP00509",
FEATURED_BRANDS: 'DSP00501',
TRENDING_NOW: 'DSP00502',
HOT_PICKS: 'DSP00503',
ON_SALE: 'DSP00504',
CATEGORY: 'DSP00505',
PRODUCT_DETAIL: 'DSP00506',
VOD: 'DSP00507',
SHOW_DETAIL: 'DSP00508',
THEME: 'DSP00509',
};
const createPanelInfo = (data, categoryData = {}) => ({
@@ -78,6 +66,7 @@ export default function RollingUnit({
isHorizontal,
handleItemFocus,
handleShelfFocus,
videoPlayerable = false,
}) {
const rollingData = bannerData.bannerDetailInfos;
const rollingDataLength = bannerData.bannerDetailInfos.length;
@@ -86,23 +75,17 @@ export default function RollingUnit({
const { curationId, curationTitle } = useSelector((state) => state.home);
const curtNm = useSelector((state) => state.home?.bannerData?.curtNm);
const shptmTmplCd = useSelector(
(state) => state.home?.bannerData?.shptmTmplCd
);
const shptmTmplCd = useSelector((state) => state.home?.bannerData?.shptmTmplCd);
const nowMenu = useSelector((state) => state.common.menu.nowMenu);
const entryMenu = useSelector((state) => state.common.menu.entryMenu);
const homeCategory = useSelector(
(state) => state.home.menuData?.data?.homeCategory
);
const homeCategory = useSelector((state) => state.home.menuData?.data?.homeCategory);
const countryCode = useSelector((state) => state.common.httpHeader.cntry_cd);
const bannerId = `banner-${bannerData.banrLctnNo}`;
const savedIndex = useSelector((state) => state.home.bannerIndices[bannerId]);
const [startIndex, setStartIndex] = useState(
savedIndex !== undefined ? savedIndex : 0
);
const [startIndex, setStartIndex] = useState(savedIndex !== undefined ? savedIndex : 0);
const lastIndexRef = useRef(rollingDataLength - 1);
const doRollingRef = useRef(false);
const [unitHasFocus, setUnitHasFocus] = useState(false);
@@ -124,14 +107,14 @@ export default function RollingUnit({
switch (currentRollingData?.shptmBanrTpCd) {
// case: "LIVE" or "VOD"
case "DSP00301":
case "DSP00302":
case 'DSP00301':
case 'DSP00302':
contId = currentRollingData?.showId;
contNm = currentRollingData?.showNm;
break;
// case: "Image Banner"
case "DSP00303":
case 'DSP00303':
contId = currentRollingData?.shptmLnkTpCd;
contNm = currentRollingData?.shptmLnkTpNm;
break;
@@ -144,27 +127,27 @@ export default function RollingUnit({
}
if (
currentRollingData?.shptmLnkTpCd === "DSP00503" || // "Hot Picks"
currentRollingData?.shptmLnkTpCd === "DSP00509" // "Theme"
currentRollingData?.shptmLnkTpCd === 'DSP00503' || // "Hot Picks"
currentRollingData?.shptmLnkTpCd === 'DSP00509' // "Theme"
) {
contNm = contNm + " | " + currentRollingData?.lnkCurationId;
contNm = contNm + ' | ' + currentRollingData?.lnkCurationId;
}
return {
banrNo: `${currentRollingData?.banrDpOrd}`,
banrTpNm: currentRollingData?.vtctpYn
? currentRollingData.vtctpYn === "Y"
? "Vertical"
: "Horizontal"
: "",
? currentRollingData.vtctpYn === 'Y'
? 'Vertical'
: 'Horizontal'
: '',
contId,
contNm,
contTpNm: currentRollingData?.shptmBanrTpNm ?? "",
dspyTpNm: bannerDataRef.current?.shptmDspyTpNm ?? "",
expsOrd: bannerDataRef.current?.banrLctnNo ?? "",
linkTpCd: "",
patncNm: currentRollingData?.patncNm ?? "",
patnrId: currentRollingData?.patnrId ?? "",
contTpNm: currentRollingData?.shptmBanrTpNm ?? '',
dspyTpNm: bannerDataRef.current?.shptmDspyTpNm ?? '',
expsOrd: bannerDataRef.current?.banrLctnNo ?? '',
linkTpCd: '',
patncNm: currentRollingData?.patncNm ?? '',
patnrId: currentRollingData?.patnrId ?? '',
tmplCd: shptmTmplCd,
};
}
@@ -175,19 +158,17 @@ export default function RollingUnit({
(bannerClick) => {
const data = rollingDataRef.current[startIndex];
const newParams =
bannerData.banrLctnNo === "2"
bannerData.banrLctnNo === '2'
? {
bannerType: "Horizontal",
bannerType: 'Horizontal',
}
: {
bannerType: "Vertical",
bannerType: 'Vertical',
};
if (rollingDataRef.current && nowMenu === LOG_MENU.HOME_TOP) {
const logParams = {
contextName: LOG_CONTEXT_NAME.HOME,
messageId: bannerClick
? LOG_MESSAGE_ID.BANNER_CLICK
: LOG_MESSAGE_ID.BANNER,
messageId: bannerClick ? LOG_MESSAGE_ID.BANNER_CLICK : LOG_MESSAGE_ID.BANNER,
curationId: curationId,
curationTitle: curationTitle,
contentType: data.shptmBanrTpNm,
@@ -195,7 +176,7 @@ export default function RollingUnit({
contentTitle: data.showNm,
productId: data.prdtId,
productTitle: data.prdtNm,
displayType: "button",
displayType: 'button',
partner: data.patncNm,
brand: data.brndNm,
location: bannerData.banrLctnNo,
@@ -203,9 +184,9 @@ export default function RollingUnit({
};
dispatch(sendLogTotalRecommend(logParams));
if (bannerClick) {
console.log("###bannerClick", logParams);
console.log('###bannerClick', logParams);
} else {
console.log("###bannerShow", logParams);
console.log('###bannerShow', logParams);
}
}
},
@@ -223,16 +204,14 @@ export default function RollingUnit({
const deltaTime = time - previousTimeRef.current;
if (deltaTime >= 10000 && doRollingRef.current) {
setStartIndex((prevIndex) =>
prevIndex === lastIndexRef.current ? 0 : prevIndex + 1
);
setStartIndex((prevIndex) => (prevIndex === lastIndexRef.current ? 0 : prevIndex + 1));
previousTimeRef.current = time;
}
} else {
previousTimeRef.current = time;
}
if (typeof window === "object") {
if (typeof window === 'object') {
requestRef.current = window.requestAnimationFrame(animate);
}
}, []);
@@ -270,7 +249,7 @@ export default function RollingUnit({
// 인디케이터 아래키 누를시 [<]
const prevKeyDown = (event) => {
if (event.key === "ArrowDown") {
if (event.key === 'ArrowDown') {
setNextFocus(true);
setContentsFocus(true);
}
@@ -278,7 +257,7 @@ export default function RollingUnit({
// 인디케이터 아래키 누를시 [>]
const nextKeyDown = (event) => {
if (event.key === "ArrowDown") {
if (event.key === 'ArrowDown') {
setPrevFocus(true);
setContentsFocus(true);
}
@@ -362,7 +341,7 @@ export default function RollingUnit({
dispatch(
sendLogTopContents({
...topContentsLogInfo,
inDt: formatGMTString(new Date()) ?? "",
inDt: formatGMTString(new Date()) ?? '',
logTpNo: LOG_TP_NO.TOP_CONTENTS.CLICK,
})
);
@@ -372,23 +351,17 @@ export default function RollingUnit({
switch (linkType) {
case LINK_TYPES.FEATURED_BRANDS:
handlePushPanel(panel_names.FEATURED_BRANDS_PANEL, {
from: "gnb",
from: 'gnb',
patnrId: currentData.patnrId,
});
break;
case LINK_TYPES.TRENDING_NOW:
handlePushPanel(
panel_names.TRENDING_NOW_PANEL,
createPanelInfo(currentData)
);
handlePushPanel(panel_names.TRENDING_NOW_PANEL, createPanelInfo(currentData));
break;
case LINK_TYPES.HOT_PICKS:
handlePushPanel(
panel_names.HOT_PICKS_PANEL,
createPanelInfo(currentData)
);
handlePushPanel(panel_names.HOT_PICKS_PANEL, createPanelInfo(currentData));
break;
case LINK_TYPES.ON_SALE:
@@ -399,10 +372,7 @@ export default function RollingUnit({
case LINK_TYPES.CATEGORY:
if (Object.keys(categoryData).length > 0) {
handlePushPanel(
panel_names.CATEGORY_PANEL,
createPanelInfo(currentData, categoryData)
);
handlePushPanel(panel_names.CATEGORY_PANEL, createPanelInfo(currentData, categoryData));
}
break;
@@ -430,7 +400,7 @@ export default function RollingUnit({
dispatch(
sendLogTopContents({
...topContentsLogInfo,
inDt: formatGMTString(new Date()) ?? "",
inDt: formatGMTString(new Date()) ?? '',
logTpNo: LOG_TP_NO.TOP_CONTENTS.CLICK,
})
);
@@ -456,7 +426,7 @@ export default function RollingUnit({
panelInfo: {
lastFocusedTargetId,
focusedContainerId: TEMPLATE_CODE_CONF.TOP,
currentSpot: currentSpot?.getAttribute("data-spotlight-id"),
currentSpot: currentSpot?.getAttribute('data-spotlight-id'),
},
})
);
@@ -472,7 +442,7 @@ export default function RollingUnit({
showUrl: currentData.showUrl,
patnrId: currentData.patnrId,
showId: currentData.showId,
shptmBanrTpNm: currentData.showId ? currentData.shptmBanrTpNm : "MEDIA",
shptmBanrTpNm: currentData.showId ? currentData.shptmBanrTpNm : 'MEDIA',
lgCatCd: currentData.lgCatCd,
chanId: currentData.brdcChnlId,
modal: false,
@@ -484,18 +454,11 @@ export default function RollingUnit({
dispatch(
sendLogTopContents({
...topContentsLogInfo,
inDt: formatGMTString(new Date()) ?? "",
inDt: formatGMTString(new Date()) ?? '',
logTpNo: LOG_TP_NO.TOP_CONTENTS.CLICK,
})
);
}, [
rollingData,
startIndex,
bannerId,
dispatch,
handleStartVideoPlayer,
topContentsLogInfo,
]);
}, [rollingData, startIndex, bannerId, dispatch, handleStartVideoPlayer, topContentsLogInfo]);
// 10초 롤링
useEffect(() => {
@@ -504,20 +467,20 @@ export default function RollingUnit({
previousTimeRef.current = undefined;
if (rollingDataLength <= 1 || unitHasFocus) {
doRollingRef.current = false;
if (typeof window === "object") {
if (typeof window === 'object') {
window.cancelAnimationFrame(requestRef.current);
}
return;
}
doRollingRef.current = true;
if (typeof window === "object") {
if (typeof window === 'object') {
requestRef.current = window.requestAnimationFrame(animate);
}
return () => {
doRollingRef.current = false;
if (typeof window === "object") {
if (typeof window === 'object') {
window.cancelAnimationFrame(requestRef.current);
}
};
@@ -531,7 +494,7 @@ export default function RollingUnit({
const params = {
...topContentsLogInfo,
entryMenu: _entryMenu,
inDt: formatGMTString(new Date()) ?? "",
inDt: formatGMTString(new Date()) ?? '',
logTpNo: LOG_TP_NO.TOP_CONTENTS.VIEW,
nowMenu: _nowMenu,
};
@@ -557,10 +520,7 @@ export default function RollingUnit({
return (
<Container
className={classNames(
css.rollingWrap,
isHorizontal && css.isHorizontalWrap
)}
className={classNames(css.rollingWrap, isHorizontal && css.isHorizontalWrap)}
spotlightId={`container-${spotlightId}`}
onFocus={shelfFocus}
>
@@ -570,15 +530,14 @@ export default function RollingUnit({
onClick={handlePrev}
onFocus={indicatorFocus}
onBlur={indicatorBlur}
spotlightId={spotlightId + "Prev"}
spotlightId={spotlightId + 'Prev'}
spotlightDisabled={prevFocus}
onKeyDown={prevKeyDown}
aria-label="Move to left Button"
/>
) : null}
{rollingData &&
rollingData[startIndex].shptmBanrTpNm === "Image Banner" ? (
{rollingData && rollingData[startIndex].shptmBanrTpNm === 'Image Banner' ? (
<SpottableComponent
className={classNames(css.itemBox, isHorizontal && css.isHorizontal)}
onClick={imageBannerClick}
@@ -596,7 +555,7 @@ export default function RollingUnit({
<img src={rollingData[startIndex].tmnlImgPath} />
</div>
</SpottableComponent>
) : rollingData[startIndex].shptmBanrTpNm === "LIVE" ? (
) : rollingData[startIndex].shptmBanrTpNm === 'LIVE' ? (
<SpottableComponent
className={classNames(css.itemBox, isHorizontal && css.isHorizontal)}
onClick={videoClick}
@@ -604,31 +563,17 @@ export default function RollingUnit({
onBlur={onBlur}
spotlightId={spotlightId}
spotlightDisabled={contentsFocus}
aria-label={"LIVE " + rollingData[startIndex].showNm}
aria-label={'LIVE ' + rollingData[startIndex].showNm}
>
<p className={css.liveIcon}>
<CustomImage
delay={0}
src={liveShow}
animationSpeed="fast"
ariaLabel="LIVE icon"
/>
<CustomImage delay={0} src={liveShow} animationSpeed="fast" ariaLabel="LIVE icon" />
</p>
<div
className={classNames(
css.itemBox,
isHorizontal && css.isHorizontal
)}
>
<div className={classNames(css.itemBox, isHorizontal && css.isHorizontal)}>
{rollingData[startIndex].tmnlImgPath == null ? (
<CustomImage
delay={0}
src={
rollingData[startIndex].vtctpYn === "Y"
? emptyVerImage
: emptyHorImage
}
src={rollingData[startIndex].vtctpYn === 'Y' ? emptyVerImage : emptyHorImage}
ariaLabel={rollingData[startIndex].tmnlImgNm}
fallbackSrc={isHorizontal ? emptyHorImage : emptyVerImage}
/>
@@ -643,13 +588,9 @@ export default function RollingUnit({
<div className={css.btnPlay}>
{rollingData[startIndex].tmnlImgPath == null ? (
""
''
) : (
<CustomImage
delay={0}
src={btnPlay}
ariaLabel="Play video Button"
/>
<CustomImage delay={0} src={btnPlay} ariaLabel="Play video Button" />
)}
</div>
</div>
@@ -664,7 +605,7 @@ export default function RollingUnit({
/>
</p>
</SpottableComponent>
) : rollingData[startIndex].shptmBanrTpNm === "VOD" ? (
) : rollingData[startIndex].shptmBanrTpNm === 'VOD' ? (
<SpottableComponent
className={classNames(css.itemBox, isHorizontal && css.isHorizontal)}
onClick={videoClick}
@@ -674,20 +615,11 @@ export default function RollingUnit({
spotlightDisabled={contentsFocus}
aria-label={rollingData[startIndex].showNm}
>
<div
className={classNames(
css.itemBox,
isHorizontal && css.isHorizontal
)}
>
<div className={classNames(css.itemBox, isHorizontal && css.isHorizontal)}>
{rollingData[startIndex].tmnlImgPath == null ? (
<CustomImage
delay={0}
src={
rollingData[startIndex].vtctpYn === "Y"
? emptyVerImage
: emptyHorImage
}
src={rollingData[startIndex].vtctpYn === 'Y' ? emptyVerImage : emptyHorImage}
ariaLabel={rollingData[startIndex].tmnlImgNm}
/>
) : (
@@ -700,13 +632,9 @@ export default function RollingUnit({
<div className={css.btnPlay}>
{rollingData[startIndex].tmnlImgPath == null ? (
""
''
) : (
<CustomImage
delay={0}
src={btnPlay}
ariaLabel="Play video Button"
/>
<CustomImage delay={0} src={btnPlay} ariaLabel="Play video Button" />
)}
</div>
@@ -728,8 +656,8 @@ export default function RollingUnit({
className={classNames(
css.itemBox,
css.todaysDeals,
countryCode === "RU" ? css.ru : "",
countryCode === "DE" ? css.de : "",
countryCode === 'RU' ? css.ru : '',
countryCode === 'DE' ? css.de : '',
isHorizontal && css.isHorizontal
)}
onClick={imageBannerClick}
@@ -752,7 +680,7 @@ export default function RollingUnit({
}}
/>
<div className={css.accBox}>
{parseFloat(originalPrice?.replace("$", "")) === 0
{parseFloat(originalPrice?.replace('$', '')) === 0
? rollingData[startIndex].offerInfo
: discountRate
? discountedPrice
@@ -761,10 +689,9 @@ export default function RollingUnit({
<span className={css.saleAccBox}>{originalPrice}</span>
)}
</div>
{isHorizontal &&
parseFloat(originalPrice?.replace("$", "")) !== 0 && (
<span className={css.saleAccBox}>{originalPrice}</span>
)}
{isHorizontal && parseFloat(originalPrice?.replace('$', '')) !== 0 && (
<span className={css.saleAccBox}>{originalPrice}</span>
)}
</div>
<div className={css.itemImgBox}>
@@ -786,7 +713,7 @@ export default function RollingUnit({
onClick={handleNext}
onFocus={indicatorFocus}
onBlur={indicatorBlur}
spotlightId={spotlightId + "Next"}
spotlightId={spotlightId + 'Next'}
spotlightDisabled={nextFocus}
onKeyDown={nextKeyDown}
aria-label="Move to right Button"

View File

@@ -36,6 +36,8 @@ import {
getChatLog,
getSubTitle,
startVideoPlayer,
pauseModalVideo,
resumeModalVideo,
} from '../../actions/playActions';
import { convertUtcToLocal } from '../../components/MediaPlayer/util';
import TPanel from '../../components/TPanel/TPanel';
@@ -308,6 +310,53 @@ const PlayerPanel = ({ isTabActivated, panelInfo, isOnTop, spotlightId, ...props
}
}, [panelInfo?.modal, panelInfo?.shptmBanrTpNm]);
// useEffect(()=>{
// console.log('[PlayerPanel] isOnTop:', {
// isOnTop,
// panelInfo
// });
// if (panelInfo && panelInfo.modal) {
// if (!isOnTop) {
// console.log('[PlayerPanel] Not on top - pausing video');
// dispatch(pauseModalVideo());
// } else if (isOnTop && panelInfo.isPaused) {
// console.log('[PlayerPanel] Back on top - resuming video');
// dispatch(resumeModalVideo());
// }
// }
// },[isOnTop, panelInfo])
// PlayerPanel.jsx의 라인 313-327 useEffect 수정
useEffect(() => {
console.log('[PlayerPanel] isOnTop:', {
isOnTop,
panelInfo,
});
if (panelInfo && panelInfo.modal) {
if (!isOnTop) {
console.log('[PlayerPanel] Not on top - pausing video');
dispatch(pauseModalVideo());
} else if (isOnTop && panelInfo.isPaused) {
console.log('[PlayerPanel] Back on top - resuming video');
dispatch(resumeModalVideo());
}
}
}, [isOnTop, panelInfo]);
// 새로운 useEffect 추가 (라인 328 이후)
useEffect(() => {
if (panelInfo?.modal && videoPlayer.current) {
if (panelInfo.isPaused) {
console.log('[PlayerPanel] Executing pause via videoPlayer.current');
videoPlayer.current.pause();
} else if (panelInfo.isPaused === false) {
console.log('[PlayerPanel] Executing play via videoPlayer.current');
videoPlayer.current.play();
}
}
}, [panelInfo?.isPaused, panelInfo?.modal]);
// creating live log params
useEffect(() => {
if (currentLiveShowInfo && Object.keys(currentLiveShowInfo).length > 0) {