Files
shoptime/com.twin.app.shoptime/src/views/TrendingNowPanel/TrendingNowPanel.jsx
optrader 9d8cafc0a9 [251216] fix: TrendingNowPanel PlayerPanel Bg
🕐 커밋 시간: 2025. 12. 16. 14:29:22

📊 변경 통계:
  • 총 파일: 2개
  • 추가: +14줄
  • 삭제: -3줄

📝 수정된 파일:
  ~ com.twin.app.shoptime/src/views/PlayerPanel/PlayerPanel.jsx
  ~ com.twin.app.shoptime/src/views/TrendingNowPanel/TrendingNowPanel.jsx
2025-12-16 14:29:22 +09:00

449 lines
14 KiB
JavaScript

import React, {
useCallback,
useEffect,
useRef,
} from 'react';
import classNames from 'classnames';
import { useDispatch } from 'react-redux';
import Spotlight from '@enact/spotlight';
import SpotlightContainerDecorator
from '@enact/spotlight/SpotlightContainerDecorator';
import { setContainerLastFocusedElement } from '@enact/spotlight/src/container';
import {
sendLogGNB,
sendLogTotalRecommend,
} from '../../actions/logActions';
import { getTop20Show } from '../../actions/mainActions';
import {
pushPanel,
updatePanel,
} from '../../actions/panelActions';
import { finishVideoPreview } from '../../actions/playActions';
import { getBestSeller } from '../../actions/productActions';
import SectionTitle from '../../components/SectionTitle/SectionTitle';
import TBody from '../../components/TBody/TBody';
import TButton, { TYPES } from '../../components/TButton/TButton';
import { removeDotAndColon } from '../../components/TItemCard/TItemCard';
import TItemCardNew from '../../components/TItemCard/TItemCard.new';
import TPanel from '../../components/TPanel/TPanel';
import TVerticalPagenator
from '../../components/TVerticalPagenator/TVerticalPagenator';
import useWhyDidYouUpdate from '../../hooks/useWhyDidYouUpdate';
import {
LOG_CONTEXT_NAME,
LOG_MENU,
LOG_MESSAGE_ID,
panel_names,
} from '../../utils/Config';
import { $L } from '../../utils/helperMethods';
import { SpotlightIds } from '../../utils/SpotlightIds';
import PopularShowIndicator from './PopularShow/PopularShowIndicator';
import css from './TrendingNowPanel.module.less';
const Container = SpotlightContainerDecorator(
{ enterTo: "last-focused" },
"div"
);
const BestSellerContainer = SpotlightContainerDecorator(
{ enterTo: "last-focused" },
"div"
);
const STRING_CONF = {
POPULAR_SHOW: "POPULAR SHOW",
BEST_SELLER: "BEST SELLER",
};
const TrendingNowPanel = ({ panelInfo, spotlightId, isOnTop, ...rest }) => {
const dispatch = useDispatch();
const { USE_SELECTOR, USE_STATE } = useWhyDidYouUpdate(spotlightId, {
panelInfo,
isOnTop,
...rest,
});
const topInfos = USE_SELECTOR(
"topInfos",
(state) => state.main.top20ShowData?.topInfos
);
const bestSeller = USE_SELECTOR(
"bestSeller",
(state) => state.product?.bestSellerData?.bestSeller
);
const [showButton, setShowButton] = USE_STATE("showButton", true);
const [selectedIndex, setSelectedIndex] = USE_STATE("selectedIndex", 0);
const cbChangePageRef = useRef(null);
const isInitialRef = useRef(true);
const currentSentMenuRef = useRef(null);
const focusedContainerIdRef = useRef(panelInfo?.focusedContainerId);
// effect: data fetching when no data exists in the store
useEffect(() => {
if (!topInfos) dispatch(getTop20Show());
if (!bestSeller) dispatch(getBestSeller());
}, []);
// effect: focus handling on initial render
useEffect(() => {
if (isInitialRef.current && !panelInfo?.currentSpot) {
if (topInfos && bestSeller) {
let target;
switch (panelInfo?.pageName) {
case "PS":
target = SpotlightIds.TRENDING_NOW_POPULAR_SHOW;
break;
case "BS":
target = SpotlightIds.TRENDING_NOW_BEST_SELLER;
break;
}
Spotlight.focus(target ?? spotlightId);
isInitialRef.current = false;
}
}
}, [topInfos, bestSeller]);
// effect: set the index when returning to the details or entering through a deep-link
useEffect(() => {
if (panelInfo?.selectedIndex) setSelectedIndex(panelInfo.selectedIndex);
}, []);
// effect: focus recovery when returning to the details
useEffect(() => {
if (isOnTop && panelInfo?.currentSpot) {
Spotlight.focus(panelInfo.currentSpot);
}
}, [isOnTop]);
// effect: for the GNB log
useEffect(() => {
if (isOnTop) {
let menu;
// case: entering through a deep link or the Home, and returning from the player or details
if (panelInfo?.focusedContainerId) {
menu =
panelInfo.focusedContainerId !==
SpotlightIds.TRENDING_NOW_POPULAR_SHOW
? LOG_MENU.TRENDING_NOW_BEST_SELLER
: LOG_MENU.TRENDING_NOW_POPULAR_SHOWS;
}
// case: entering through the GNB
else {
menu = LOG_MENU.TRENDING_NOW_POPULAR_SHOWS;
}
if (menu) dispatch(sendLogGNB(menu));
}
}, [isOnTop]);
const onFocusedContainerId = useCallback((containerId) => {
focusedContainerIdRef.current = containerId;
}, []);
const handleScroll = useCallback((e) => {
setShowButton(e.scrollTop === 0);
if (e.scrollTop !== 0) dispatch(finishVideoPreview());
}, []);
const handleSpotlight = useCallback(
(spotlightId) => (e) => {
e.stopPropagation();
if (spotlightId) Spotlight.focus(spotlightId);
},
[]
);
const handleIndicatorClick = useCallback(
(direction) => () => {
if (!topInfos) return;
const isPrev = direction === "prev";
const isNext = direction === "next";
const isEdgeIndex =
(isPrev && selectedIndex === 1) ||
(isNext && selectedIndex === topInfos.length - 2);
if (isEdgeIndex) Spotlight.focus(SpotlightIds.TRENDING_NOW_POPULAR_VIDEO);
setSelectedIndex((prev) => prev + (isPrev ? -1 : isNext ? 1 : 0));
setContainerLastFocusedElement(null, [
SpotlightIds.TRENDING_NOW_POPULAR_GRID_LIST,
]);
},
[selectedIndex, topInfos]
);
const handleBestSellerClick = useCallback(
({ patnrId, prdtId }) =>
() => {
const currentSpot =
Spotlight.getCurrent()?.getAttribute("data-spotlight-id") || null;
dispatch(
updatePanel({
name: panel_names.TRENDING_NOW_PANEL,
panelInfo: {
currentSpot,
focusedContainerId: focusedContainerIdRef.current,
selectedIndex,
},
})
);
dispatch(
pushPanel({
name: panel_names.DETAIL_PANEL,
panelInfo: { patnrId, prdtId },
})
);
},
[selectedIndex]
);
const handleBestSellerFocus = useCallback(() => {
handleItemFocus(LOG_MENU.TRENDING_NOW_BEST_SELLER, STRING_CONF.BEST_SELLER);
}, [handleItemFocus]);
const handleItemClick = useCallback(() => {
const currentSpot =
Spotlight.getCurrent()?.getAttribute("data-spotlight-id") || null;
dispatch(
updatePanel({
name: panel_names.TRENDING_NOW_PANEL,
panelInfo: {
currentSpot,
focusedContainerId: focusedContainerIdRef.current,
selectedIndex,
},
})
);
}, [selectedIndex]);
const handleItemFocus = useCallback(
(nowMenu, currentShelf) => {
setTimeout(() => dispatch(sendLogGNB(nowMenu)));
if (nowMenu !== currentSentMenuRef.current) {
let order;
let spotlightId;
switch (currentShelf) {
case STRING_CONF.POPULAR_SHOW:
order = 1;
spotlightId = SpotlightIds.TRENDING_NOW_POPULAR_SHOW;
break;
case STRING_CONF.BEST_SELLER:
order = 2;
spotlightId = SpotlightIds.TRENDING_NOW_BEST_SELLER;
break;
}
if (order && spotlightId) {
const params = {
contextName: LOG_CONTEXT_NAME.TRENDING_NOW,
messageId: LOG_MESSAGE_ID.SHELF,
shelfLocation: order,
shelfId: spotlightId,
shelfTitle: currentShelf,
};
dispatch(sendLogTotalRecommend(params));
currentSentMenuRef.current = nowMenu;
}
}
},
[currentSentMenuRef]
);
const handleTopButtonClick = useCallback(() => {
if (cbChangePageRef.current) {
cbChangePageRef.current(0, true);
}
let target = SpotlightIds.TRENDING_NOW_POPULAR_VIDEO;
if (topInfos.length === 0 && bestSeller && bestSeller.length > 0) {
target = `spotlightId-bestSeller-${bestSeller[0]?.prdtId}`;
}
Spotlight.focus(target);
setContainerLastFocusedElement(null, [
SpotlightIds.TRENDING_NOW_BEST_SELLER,
]);
}, [bestSeller, topInfos]);
const getFirstLabel = useCallback((rankOrder) => {
switch (rankOrder) {
case 1:
return `${rankOrder}st,`;
case 2:
return `${rankOrder}nd,`;
case 3:
return `${rankOrder}rd,`;
default:
return `${rankOrder}th,`;
}
}, []);
return (
<div className={css.trendingNowWrap}>
{isOnTop && selectedIndex >= 1 && showButton && (
<TButton
className={classNames(css.button, css.prevBtn)}
onClick={handleIndicatorClick("prev")}
onSpotlightRight={handleSpotlight(
SpotlightIds.TRENDING_NOW_POPULAR_VIDEO
)}
onSpotlightDown={handleSpotlight(
SpotlightIds.TRENDING_NOW_BEST_SELLER
)}
spotlightId={SpotlightIds.TRENDING_NOW_PREV_INDICATOR}
ariaLabel="Move to left"
/>
)}
<TPanel spotlightId={spotlightId}>
<TBody
className={css.trendingNow}
scrollable={false}
spotlightDisabled={!isOnTop}
>
<TVerticalPagenator
className={css.tVerticalPagenator}
spotlightId={SpotlightIds.TRENDING_NOW_VERTICAL_PAGINATOR}
defaultContainerId={panelInfo?.focusedContainerId}
onScroll={handleScroll}
onFocusedContainerId={onFocusedContainerId}
cbChangePageRef={cbChangePageRef}
topMargin={36}
>
{topInfos && topInfos?.length > 0 && (
<Container
className={css.popularContainer}
spotlightId={SpotlightIds.TRENDING_NOW_POPULAR_SHOW}
data-wheel-point={true}
>
<SectionTitle
title={$L(STRING_CONF.POPULAR_SHOW)}
label="POPULAR SHOW, Heading1"
/>
<PopularShowIndicator
topInfos={topInfos}
selectedIndex={selectedIndex}
onSpotlightDown={handleSpotlight(
SpotlightIds.TRENDING_NOW_BEST_SELLER
)}
onSpotlightRight={handleSpotlight(
SpotlightIds.TRENDING_NOW_NEXT_INDICATOR
)}
handleItemFocus={handleItemFocus}
handleItemClick={handleItemClick}
isOnTop={isOnTop}
currentShelf={STRING_CONF.POPULAR_SHOW}
/>
</Container>
)}
{bestSeller && bestSeller?.length > 0 && (
<BestSellerContainer
className={css.bestContainer}
spotlightId={SpotlightIds.TRENDING_NOW_BEST_SELLER}
data-wheel-point
>
<SectionTitle
title={$L(STRING_CONF.BEST_SELLER)}
data-title-index="TNBestSellerTitle"
/>
<div className={css.itemList}>
{bestSeller.map((item, index) => (
<TItemCardNew
className={css.trendingBestItem}
contextName={LOG_CONTEXT_NAME.TRENDING_NOW}
messageId={LOG_MESSAGE_ID.SHELF_CLICK}
shelfLocation={2}
shelfTitle={STRING_CONF.BEST_SELLER}
shelfId={SpotlightIds.TRENDING_NOW_BEST_SELLER}
order={item.rankOrd}
catNm={item.lgCatNm}
patnerName={item.patncNm}
brandName={item.brndNm}
data-wheel-point={index >= 5}
firstLabel={getFirstLabel(item.rankOrd)}
imageAlt={item.prdtId}
imageSource={item.imgUrl}
key={item.prdtId}
isBestSeller
label={index * 1 + 1 + " of " + bestSeller.length}
lastLabel=" go to detail, button"
offerInfo={item.offerInfo}
onFocus={handleBestSellerFocus}
onClick={handleBestSellerClick(item)}
priceInfo={item.priceInfo}
productName={item.prdtNm}
productId={item.prdtId}
rank={item.rankOrd}
spotlightId={
"spotlightId-" +
"bestSeller-" +
removeDotAndColon(item.prdtId)
}
euEnrgLblInfos={item.euEnrgLblInfos}
/>
))}
</div>
</BestSellerContainer>
)}
<Container>
{((topInfos && topInfos?.length > 0) ||
(bestSeller && bestSeller?.length > 10)) && (
<TButton
onClick={handleTopButtonClick}
size={null}
spotlightId={SpotlightIds.TRENDING_NOW_TOP_BUTTON}
ariaLabel="Move to Top"
data-wheel-point
type={TYPES.topButton}
/>
)}
</Container>
</TVerticalPagenator>
</TBody>
</TPanel>
{isOnTop && topInfos &&
topInfos?.length > 0 &&
selectedIndex !== topInfos?.length - 1 &&
showButton && (
<TButton
className={classNames(css.button, css.nextBtn)}
onClick={handleIndicatorClick("next")}
onSpotlightDown={handleSpotlight(
SpotlightIds.TRENDING_NOW_BEST_SELLER
)}
spotlightId={SpotlightIds.TRENDING_NOW_NEXT_INDICATOR}
ariaLabel="Move to right"
/>
)}
</div>
);
};
const propsAreEqual = (prev, next) => {
const keys = Object.keys(prev);
const nextKeys = Object.keys(next);
// if (!next.isOnTop) {
// //ignore event on background
// return true;
// }
if (keys.length !== nextKeys.length) {
return false;
}
for (let i = 0; i < keys.length; i++) {
if (prev[keys[i]] !== next[keys[i]]) {
return false;
}
}
return true;
};
export default React.memo(TrendingNowPanel, propsAreEqual);