Files
shoptime/com.twin.app.shoptime/src/views/HomePanel/HomeBanner/RandomUnit.jsx
2024-07-12 14:59:12 +09:00

562 lines
17 KiB
JavaScript

import React, {
useCallback,
useEffect,
useMemo,
useRef,
useState,
} from 'react';
import classNames from 'classnames';
import {
useDispatch,
useSelector,
} from 'react-redux';
import SpotlightContainerDecorator
from '@enact/spotlight/SpotlightContainerDecorator';
import Spottable from '@enact/spotlight/Spottable';
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 { sendBroadCast } from "../../../actions/commonActions";
import { pushPanel } from '../../../actions/panelActions';
import {
finishVideoPreview,
startVideoPlayer,
} from '../../../actions/playActions';
import CustomImage from '../../../components/CustomImage/CustomImage';
import useLogService from '../../../hooks/useLogService';
import usePriceInfo from '../../../hooks/usePriceInfo';
import {
LOG_MENU,
LOG_TP_NO,
panel_names,
} from '../../../utils/Config';
import {
$L,
formatGMTString,
} from '../../../utils/helperMethods';
import css from './RandomUnit.module.less';
const SpottableComponent = Spottable("div");
const Container = SpotlightContainerDecorator(
{ enterTo: "last-focused" },
"div"
);
export default function RandomUnit({
bannerData,
spotlightId,
isHorizontal,
handleItemFocus,
randomNumber,
}) {
const bannerDetailInfos = bannerData.bannerDetailInfos;
const { sendLogTopContents } = useLogService();
const dispatch = useDispatch();
const curationId = useSelector((state) => state.home?.bannerData?.curationId);
const curtNm = useSelector((state) => state.home?.bannerData?.curtNm);
const shptmTmplCd = useSelector(
(state) => state.home?.bannerData?.shptmTmplCd
);
const nowMenu = useSelector((state) => state.common.menu.nowMenu);
const homeCategory = useSelector(
(state) => state.home.menuData?.data?.homeCategory
);
const [randomData, setRandomData] = useState("");
const [priceInfos, setpriceInfos] = useState("");
const [isFocused, setIsFocused] = useState(false);
const timerRef = useRef();
const bannerDataRef = useRef(bannerData);
const randomDataRef = useRef(bannerDetailInfos[randomNumber]);
const [videoError, setVideoError] = useState(false);
const [liveIndicies, setLiveIndicies] = useState([]);
// 정상적으로 로딩되면 빈객체로 넘어가고 , 에러가 나면 객체안에 타입이 담겨옵니다.
const broadcast = useSelector((state) => state.common.broadcast);
// 1. 비디오(live) 에러 감지
// 2. 라이브 영상이 2개 이상이면, 그 다음 영상으로 전환
// 3. 라이브 영상이 1개면 그 다음 영상으로 전환을 할 수 없으므로 에러 로고 화면을 보여준다.
// 라이브 영상이 1개 라도 순간적인 네트워크 오류나, 일시적인 오류 일수 있으므로, 일정 시간을 주고, 비디오를 다시 호출 한다.
useEffect(() => {
if (bannerDetailInfos && randomNumber) {
const indices = bannerDetailInfos
.map((info, index) => (info.shptmBanrTpNm === "LIVE" ? index : null))
.filter((index) => index !== null && index !== randomNumber);
setLiveIndicies(indices);
}
}, [bannerDetailInfos, randomNumber]);
// 비디오 에러일시, 클릭 이동
const videoErrorClick = useCallback(() => {
return dispatch(
pushPanel({
name: panel_names.FEATURED_BRANDS_PANEL,
panelInfo: { patnrId: randomData.patnrId },
})
);
}, [randomData, dispatch]);
// 포커스 인
const onFocus = useCallback(() => {
if (handleItemFocus) {
handleItemFocus();
}
setIsFocused(true);
}, [handleItemFocus]);
// 포커스 아웃
const onBlur = useCallback(() => {
setIsFocused(false);
clearTimeout(timerRef.current);
}, [isFocused]);
// DSP00501 : Featured Brands
// DSP00502 : Trending now
// DSP00503 : HOT PICKS
// DSP00504 : ON SALE
// DSP00505 : CATEGORY
// DSP00506 : Product Detail
// DSP00507 : VOD
// DSP00508 : Show Detail
// DSP00509 : Theme
const categoryData = useMemo(() => {
if (randomData && randomData.shptmLnkTpCd === "DSP00505") {
if (homeCategory && homeCategory.length > 0) {
const foundCategory = homeCategory.find(
(data) => data.lgCatCd === randomData.lgCatCd
);
if (foundCategory) {
return {
lgCatNm: foundCategory.lgCatNm,
COUNT: foundCategory.COUNT,
};
}
return;
}
}
}, [homeCategory, randomData.shptmLnkTpCd]);
// 이미지 배너 클릭
const imageBannerClick = () => {
let panelName = "";
if (randomData.shptmLnkTpCd === "DSP00501") {
return dispatch(
pushPanel({
name: panel_names.FEATURED_BRANDS_PANEL,
panelInfo: { patnrId: randomData.patnrId },
})
);
} else if (randomData.shptmLnkTpCd === "DSP00502") {
panelName = panel_names.TRENDING_NOW_PANEL;
} else if (randomData.shptmLnkTpCd === "DSP00503") {
return dispatch(
pushPanel({
name: panel_names.HOT_PICKS_PANEL,
panelInfo: {
patnrId: randomData.patnrId,
curationId: randomData.lnkCurationId,
},
})
);
} else if (randomData.shptmLnkTpCd === "DSP00504") {
return dispatch(
pushPanel({
name: panel_names.ON_SALE_PANEL,
panelInfo: {
lgCatCd: randomData.lgCatCd,
},
})
);
} else if (randomData.shptmLnkTpCd === "DSP00505") {
if (Object.keys(categoryData).length > 0) {
return dispatch(
pushPanel({
name: panel_names.CATEGORY_PANEL,
panelInfo: {
lgCatCd: randomData.lgCatCd,
lgCatNm: categoryData.lgCatNm,
COUNT: categoryData.COUNT,
currentSpot: null,
dropDownTab: 0,
tab: 0,
focusedContainerId: null,
},
})
);
}
} else if (randomData.shptmLnkTpCd === "DSP00506") {
return dispatch(
pushPanel({
name: panel_names.DETAIL_PANEL,
panelInfo: {
patnrId: randomData.patnrId,
prdtId: randomData.prdtId,
curationId: randomData.lnkCurationId,
},
})
);
} else if (randomData.shptmLnkTpCd === "DSP00507") {
return dispatch(
startVideoPlayer({
patnrId: randomData.patnrId,
showId: randomData.showId,
shptmBanrTpNm: "VOD",
lgCatCd: randomData.lgCatCd,
modal: false,
})
);
} else if (randomData.shptmLnkTpCd === "DSP00508") {
return dispatch(
pushPanel({
name: panel_names.DETAIL_PANEL,
panelInfo: {
patnrId: randomData.patnrId,
curationId: randomData.lnkCurationId,
prdtId: randomData.prdtId,
type: "theme",
},
})
);
} else if (randomData.shptmLnkTpCd === "DSP00509") {
return dispatch(
pushPanel({
name: panel_names.THEME_CURATION_PANEL,
panelInfo: {
curationId: randomData.lnkCurationId,
},
})
);
} else {
panelName = panel_names.HOME_PANEL;
}
dispatch(
pushPanel({
name: panelName,
panelInfo: {
patnrId: randomData.patnrId,
prdtId: randomData.prdtId,
},
})
);
};
// 투데이즈딜 클릭
const todayDealClick = useCallback(() => {
dispatch(
pushPanel({
name: panel_names.DETAIL_PANEL,
panelInfo: {
patnrId: randomData.patnrId,
prdtId: randomData.prdtId,
},
})
);
}, [dispatch, randomData?.patnrId, randomData?.prdtId]);
// 비디오 클릭
const videoClick = useCallback(() => {
dispatch(
startVideoPlayer({
showUrl: randomData.showUrl,
patnrId: randomData.patnrId,
showId: randomData.showId,
shptmBanrTpNm: randomData.showId ? randomData.shptmBanrTpNm : "MEDIA",
lgCatCd: randomData.lgCatCd,
chanId: randomData.brdcChnlId,
modal: false,
modalContainerId: spotlightId,
modalClassName: css.videoModal,
})
);
}, [randomData, spotlightId]);
// 투데이즈 딜 가격 정보
const { originalPrice, discountedPrice, discountRate, offerInfo } =
usePriceInfo(priceInfos) || {};
// 로그
useEffect(() => {
if (
bannerDataRef.current &&
randomDataRef.current &&
nowMenu &&
nowMenu === LOG_MENU.HOME_TOP
) {
const params = {
banrNo: `${randomDataRef.current?.banrDpOrd}`,
banrTpNm: randomDataRef.current?.vtctpYn
? randomDataRef.current.vtctpYn === "Y"
? "Vertical"
: "Horizontal"
: "",
contId: curationId ?? "",
contNm: curtNm ?? "",
contTpNm: randomDataRef.current?.shptmBanrTpNm ?? "",
dspyTpNm: bannerDataRef.current?.shptmDspyTpNm ?? "",
expsOrd: bannerDataRef.current?.banrLctnNo ?? "",
inDt: formatGMTString(new Date()),
linkTpCd: "",
logTpNo: LOG_TP_NO.TOP_CONTENTS,
patncNm: randomDataRef.current?.patncNm ?? "",
patnrId: randomDataRef.current?.patnrId ?? "",
tmplCd: shptmTmplCd,
};
return () => sendLogTopContents(params);
}
}, [curationId, curtNm, nowMenu, sendLogTopContents, shptmTmplCd]);
useEffect(() => {
if (bannerData) {
setRandomData(bannerDetailInfos[randomNumber]);
}
}, [bannerData]);
useEffect(() => {
if (randomData && randomData.priceInfo !== null) {
return setpriceInfos(randomData.priceInfo);
}
}, [randomData]);
useEffect(() => {
if (isFocused) {
// 비디오 정상
if (broadcast && Object.keys(broadcast).length === 0) {
setVideoError(false);
timerRef.current = setTimeout(
() =>
dispatch(
startVideoPlayer({
showUrl: randomData.showUrl,
patnrId: randomData.patnrId,
showId: randomData.showId,
shptmBanrTpNm: randomData.showId
? randomData.shptmBanrTpNm
: "MEDIA",
lgCatCd: randomData.lgCatCd,
chanId: randomData.brdcChnlId,
modal: true,
modalContainerId: spotlightId,
modalClassName: css.videoModal,
})
),
1000
);
// 비디오 오류
} else if (broadcast && Object.keys(broadcast).length !== 0) {
dispatch(finishVideoPreview());
if (liveIndicies.length > 0) {
const nextIndex = liveIndicies[0];
setLiveIndicies((prev) => prev.slice(1));
setRandomData(bannerDetailInfos[nextIndex]);
setTimeout(() => {
setVideoError(false);
}, 0);
} else {
setVideoError(true);
}
}
} else {
dispatch(finishVideoPreview());
}
return () => {
clearTimeout(timerRef.current);
};
}, [isFocused, dispatch, broadcast, videoError]);
return (
<>
<Container
className={classNames(
css.rollingWrap,
isHorizontal && css.isHorizontalWrap
)}
>
{randomData?.shptmBanrTpNm == "Image Banner" ? (
<SpottableComponent
className={classNames(
css.itemBox,
isHorizontal && css.isHorizontal
)}
onClick={imageBannerClick}
spotlightId={spotlightId}
aria-label={
randomData.prdtNm ? randomData.prdtNm : randomData.tmnlImgNm
}
>
<div className={css.imgBanner}>
<CustomImage
delay={0}
src={randomData.tmnlImgPath}
animationSpeed="fast"
ariaLabel={randomData.tmnImgNm}
/>
</div>
</SpottableComponent>
) : randomData?.shptmBanrTpNm == "LIVE" ||
randomData?.shptmBanrTpNm == "VOD" ? (
<SpottableComponent
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
}
alt={"LIVE"}
>
{randomData.shptmBanrTpNm == "LIVE" && videoError === false && (
<p className={css.liveIcon}>
<CustomImage
delay={0}
src={liveShow}
animationSpeed="fast"
ariaLabel="LIVE icon"
/>
</p>
)}
{/* 비디오 에러 발생 */}
{videoError === true && (
<div className={css.errorContents}>
<div>
{randomData.patncLogoPath && (
<img
className={css.errorlogo}
src={randomData.patncLogoPath}
/>
)}
<p className={css.errorText}>
Click the screen to see more products!
</p>
</div>
</div>
)}
{/* 배너 메인 이미지 */}
{videoError === false && (
<div
className={classNames(
css.itemBox,
isHorizontal && css.isHorizontal
)}
>
{randomData.tmnlImgPath ? (
<CustomImage
delay={0}
src={randomData.tmnlImgPath}
ariaLabel={randomData.tmnImgNm}
animationSpeed="fast"
/>
) : (
<CustomImage
delay={0}
src={
randomData.vtctpYn === "Y" ? emptyVerImage : emptyHorImage
}
animationSpeed="fast"
ariaLabel={randomData.tmnImgNm}
/>
)}
</div>
)}
{/* 플레이 버튼 [>] */}
{videoError === false && (
<div className={css.btnPlay}>
{randomData.tmnlImgPath == null ? "" : <img src={btnPlay} />}
</div>
)}
{/* 배너 하단 로고 */}
{videoError === false && (
<p className={css.brandIcon}>
{randomData.showId && (
<CustomImage
delay={0}
src={randomData.showId ? randomData.patncLogoPath : null}
fallbackSrc={defaultLogoImg}
animationSpeed="fast"
ariaLabel={randomData.brdcChnlId}
/>
)}
</p>
)}
</SpottableComponent>
) : randomData?.shptmBanrTpNm == "Today's Deals" ? (
<SpottableComponent
className={classNames(
css.itemBox,
css.todaysDeals,
isHorizontal && css.isHorizontal
)}
onClick={todayDealClick}
spotlightId={spotlightId}
aria-label={
randomData.prdtNm ? randomData.prdtNm : randomData.tmnlImgNm
}
>
<div className={css.productInfo}>
<div className={css.todaysDealTitle}>{$L("TODAY's DEALS")}</div>
<div className={css.textBox}>{randomData.prdtNm}</div>
<div className={css.accBox}>
{parseFloat(originalPrice?.replace("$", "")) === 0 ? (
<strong>{randomData.offerInfo}</strong>
) : discountRate ? (
discountedPrice
) : (
originalPrice
)}
</div>
{discountRate && (
<span className={css.saleAccBox}>{originalPrice}</span>
)}
</div>
<div className={css.itemImgBox}>
<CustomImage
delay={0}
src={randomData.tmnlImgPath}
animationSpeed="fast"
fallbackSrc={defaultImageItem}
ariaLabel={randomData.tmnlImgNm}
/>
</div>
</SpottableComponent>
) : null}
</Container>
</>
);
}