Files
shoptime/com.twin.app.shoptime/src/views/FeaturedBrandsPanel/FeaturedBrandsPanel.jsx
2025-06-13 15:41:51 +09:00

1022 lines
34 KiB
JavaScript
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

import React, {
useCallback,
useEffect,
useMemo,
useRef,
useState,
} from "react";
import classNames from "classnames";
import { useDispatch, useSelector } from "react-redux";
import { Job } from "@enact/core/util";
import Spotlight from "@enact/spotlight";
import { types } from "../../actions/actionTypes";
import {
getBrandBestSeller,
getBrandCategoryInfo,
getBrandCategoryProductInfo,
getBrandCreatorsInfo,
getBrandLayoutInfo,
getBrandList,
getBrandLiveChannelInfo,
getBrandRecommendedShowInfo,
getBrandSeriesInfo,
getBrandShowroom,
getBrandTSVInfo,
} from "../../actions/brandActions";
import { changeAppStatus, setHidePopup } from "../../actions/commonActions";
import {
sendLogGNB,
sendLogPartners,
sendLogTotalRecommend,
} from "../../actions/logActions";
import { setMainLiveUpcomingAlarm } from "../../actions/mainActions";
import {
deleteMyUpcomingAlertShow,
getMyUpcomingAlertShow,
setMyUpcomingUseAlert,
} from "../../actions/myPageActions";
import { updatePanel } from "../../actions/panelActions";
import TBody from "../../components/TBody/TBody";
import TButton, { TYPES } from "../../components/TButton/TButton";
import TPanel from "../../components/TPanel/TPanel";
import TPopUp from "../../components/TPopUp/TPopUp";
import TVerticalPagenator from "../../components/TVerticalPagenator/TVerticalPagenator";
import usePrevious from "../../hooks/usePrevious";
import {
ACTIVE_POPUP,
LOG_CONTEXT_NAME,
LOG_MENU,
LOG_MESSAGE_ID,
panel_names,
} from "../../utils/Config";
import { $L } from "../../utils/helperMethods";
import { SpotlightIds } from "../../utils/SpotlightIds";
import Banner from "./Banner/Banner";
import FeaturedBestSeller from "./FeaturedBestSeller/FeaturedBestSeller";
import css from "./FeaturedBrandsPanel.module.less";
import FeaturedCategory from "./FeaturedCategory/FeaturedCategory";
import FeaturedCreators from "./FeaturedCreators/FeaturedCreators";
import LiveChannels from "./LiveChannels/LiveChannels";
import QuickMenu from "./QuickMenu/QuickMenu";
import RecommendedShows from "./RecommendedShows/RecommendedShows";
import Series from "./Series/Series";
import Showroom from "./Showroom/Showroom";
import TodaysDeals from "./TodaysDeals/TodaysDeals";
import UpComing from "./UpComing/UpComing";
import { setContainerLastFocusedElement } from "@enact/spotlight/src/container";
const STRING_CONF = {
CANCEL: "CANCEL",
OK: "OK",
YES: "YES",
NO_SHOW_MESSAGE: "This show doesnt exist anymore",
REMINDER_ON_OFF_MESSAGE:
"Reminder Notification is turned off. Select Yes to turn on reminders.",
UPCOMING_TIME_CONFLICT_MESSAGE:
"A Reminder is already set on the selected schedule. Press OK to overwrite the existing Reminder.",
};
const TEMPLATE_CODE_CONF = {
LIVE_CHANNELS: "BRD00101",
UP_COMING: "BRD00102",
TODAYS_DEALS: "BRD00103",
BEST_SELLER: "BRD00104",
RECOMMENDED_SHOWS: "BRD00105",
FEATURED_CREATORS: "BRD00106",
SERIES: "BRD00107",
CATEGORY: "BRD00108",
SHOWROOM: "BRD00109",
};
const DISPATCH_MAP = Object.freeze({
[TEMPLATE_CODE_CONF.LIVE_CHANNELS]: getBrandLiveChannelInfo,
[TEMPLATE_CODE_CONF.TODAYS_DEALS]: getBrandTSVInfo,
[TEMPLATE_CODE_CONF.BEST_SELLER]: getBrandBestSeller,
[TEMPLATE_CODE_CONF.RECOMMENDED_SHOWS]: getBrandRecommendedShowInfo,
[TEMPLATE_CODE_CONF.FEATURED_CREATORS]: getBrandCreatorsInfo,
[TEMPLATE_CODE_CONF.SERIES]: getBrandSeriesInfo,
[TEMPLATE_CODE_CONF.CATEGORY]: getBrandCategoryInfo,
[TEMPLATE_CODE_CONF.SHOWROOM]: getBrandShowroom,
});
const TOP_MARGIN = 36;
const hasTemplateCodeWithValue = (array, value) =>
array?.some((obj) => obj?.shptmBrndOptTpCd === value) ?? false;
const shouldRenderComponent = (data) => {
return (
(Array.isArray(data) && data.length > 0) ||
(typeof data === "object" && Object.keys(data).length > 0)
);
};
const findSelector = (selector, maxAttempts = 5, currentAttempts = 0) => {
try {
if (currentAttempts >= maxAttempts) {
throw new Error("selector not found");
}
const initialSelector = document.querySelector(selector);
if (initialSelector) {
return initialSelector;
} else {
return findSelector(selector, maxAttempts, currentAttempts + 1);
}
} catch (error) {
console.warn(error.message);
}
};
const findAndFocusFirstContainer = (timerRef) => {
if (typeof window === "object") {
const containers = document.querySelectorAll("[data-wheel-point]");
const firstContainer = Array.from(containers) //
.find((container) => container?.getAttribute("data-shelf-order") === "1");
if (firstContainer) {
Spotlight.focus(firstContainer);
clearTimeout(timerRef.current?.timer);
timerRef.current.timer = null;
timerRef.current.attemptCount = 0;
return;
}
if (timerRef.current.attemptCount < 5) {
timerRef.current.attemptCount += 1;
timerRef.current.timer = setTimeout(
() => findAndFocusFirstContainer(timerRef),
200
);
}
}
};
const getMessageByPopupType = (type) => {
switch (type) {
case ACTIVE_POPUP.reminderPopup:
return $L(STRING_CONF.REMINDER_ON_OFF_MESSAGE);
case ACTIVE_POPUP.timeConflictPopup:
return $L(STRING_CONF.UPCOMING_TIME_CONFLICT_MESSAGE);
case ACTIVE_POPUP.noShowPopup:
return $L(STRING_CONF.NO_SHOW_MESSAGE);
default:
return;
}
};
const getMenuByContainerId = (containerId) => {
if (!containerId) return;
const splittedId = containerId.split("-")[0];
switch (splittedId) {
case SpotlightIds.BRAND_QUICK_MENU:
return LOG_MENU.FEATURED_BRANDS_QUICK_MENU;
case TEMPLATE_CODE_CONF.LIVE_CHANNELS:
return LOG_MENU.FEATURED_BRANDS_LIVE_CHANNELS;
case TEMPLATE_CODE_CONF.UP_COMING:
return LOG_MENU.FEATURED_BRANDS_UPCOMING;
case TEMPLATE_CODE_CONF.TODAYS_DEALS:
return LOG_MENU.FEATURED_BRANDS_TODAYS_DEALS;
case TEMPLATE_CODE_CONF.BEST_SELLER:
return LOG_MENU.FEATURED_BRANDS_BEST_SELLER;
case TEMPLATE_CODE_CONF.RECOMMENDED_SHOWS:
return LOG_MENU.FEATURED_BRANDS_RECOMMENDED_SHOWS;
case TEMPLATE_CODE_CONF.FEATURED_CREATORS:
return LOG_MENU.FEATURED_BRANDS_FEATURED_CREATORS;
case TEMPLATE_CODE_CONF.SERIES:
return LOG_MENU.FEATURED_BRANDS_SERIES;
case TEMPLATE_CODE_CONF.CATEGORY:
return LOG_MENU.FEATURED_BRANDS_CATEGORY;
case TEMPLATE_CODE_CONF.SHOWROOM:
return LOG_MENU.FEATURED_BRANDS_SHOWROOM;
default:
return;
}
};
const FeaturedBrandsPanel = ({ isOnTop, panelInfo, spotlightId }) => {
const dispatch = useDispatch();
const cursorVisible = useSelector(
(state) => state.common.appStatus.cursorVisible
);
const activePopup = useSelector((state) => state.common.popup.activePopup);
const popupVisible = useSelector((state) => state.common.popup.popupVisible);
const brandInfo = useSelector(
(state) => state.brand.brandInfoData.data.brandInfo
);
const brandTopImgInfo = useSelector(
(state) => state.brand.brandLayoutInfoData.data.brandTopImgInfo
);
const brandLayoutInfo = useSelector(
(state) => state.brand.brandLayoutInfoData.data.brandLayoutInfo
);
const brandChanInfo = useSelector(
(state) => state.brand.brandLiveChannelInfoData.data.brandChanInfo
);
const brandChannelCnt = useSelector(
(state) => state.brand.brandLiveChannelInfoData.data.brandChannelCnt
);
const brandLiveChannelUpcoming = useSelector(
(state) =>
state.brand.brandLiveChannelInfoData.data.brandLiveChannelUpcoming
);
const brandTsvInfo = useSelector(
(state) => state.brand.brandTsvInfoData.data.brandTsvInfo
);
const brandBestSellerInfo = useSelector(
(state) => state.brand.brandBestSellerData.data.brandBestSellerInfo
);
const brandRecommendedShowCategoryInfo = useSelector(
(state) =>
state.brand.brandRecommendedShowInfoData.data
.brandRecommendedShowCategoryInfo
);
const brandRecommendedShowInfo = useSelector(
(state) =>
state.brand.brandRecommendedShowInfoData.data.brandRecommendedShowInfo
);
const brandCreatorsInfo = useSelector(
(state) => state.brand.brandCreatorsInfoData.data.brandCreatorsInfo
);
const barndCreatorsShowInfo = useSelector(
(state) => state.brand.brandCreatorsInfoData.data.barndCreatorsShowInfo
);
const brandSeriesGroupInfo = useSelector(
(state) => state.brand.brandSeriesInfoData.data.brandSeriesGroupInfo
);
const brandSeriesInfo = useSelector(
(state) => state.brand.brandSeriesInfoData.data.brandSeriesInfo
);
const brandCategoryInfo = useSelector(
(state) => state.brand.brandCategoryInfoData.data.brandCategoryInfo
);
const brandCategoryProductInfo = useSelector(
(state) => state.brand.brandCategoryInfoData.data.brandCategoryProductInfo
);
const brandShowroomInfo = useSelector(
(state) => state.brand.brandShowroomData.data.brandShowroomInfo
);
const [displayTopButton, setDisplayTopButton] = useState(false);
const [focusedContainerId, setFocusedContainerId] = useState(null);
const [isInitialFocusOccurred, setIsInitialFocusOccurred] = useState(null);
const [isLogGNBSent, setIsLogGNBSent] = useState(false);
const [selectedPatncNm, setSelectedPatncNm] = useState(null);
const [selectedPatnrId, setSelectedPatnrId] = useState(null);
const [selectedCatCd, setSelectedCatCd] = useState(null);
const [selectedHstNm, setSelectedHstNm] = useState(null);
const [selectedSeriesId, setSelectedSeriesId] = useState(null);
const [selectedCatCdLv1, setSelectedCatCdLv1] = useState(null);
const [selectedCatCdLv2, setSelectedCatCdLv2] = useState(null);
const brandInfoRef = usePrevious(brandInfo);
const displayTopButtonRef = usePrevious(displayTopButton);
const focusedContainerIdRef = usePrevious(focusedContainerId);
const initialFocusTimeoutJob = useRef(new Job((func) => func(), 0));
const alarmTimer = useRef(null);
const cbChangePageRef = useRef(null);
const lastMenuRef = useRef(null);
const orderableFlexContainerRef = useRef(null);
const prevSelectedPatncNmRef = useRef(null);
const noneTargetTimer = useRef({
timer: null,
attemptCount: 0,
});
const renderedShelfCountRef = useRef(0);
const fromDetail = panelInfo?.from && panelInfo.from === "detail";
const fromGNB = panelInfo?.from && panelInfo.from === "gnb";
const fromUpcoming = panelInfo?.from && panelInfo.from === "upcoming";
const fromQuickMenu = panelInfo?.from && panelInfo.from === "menu";
const hasQuickMenu = useMemo(
() => brandInfo && brandInfo.length > 1,
[brandInfo]
);
const sortedBrandLayoutInfo = useMemo(
() => brandLayoutInfo?.sort((a, b) => a.expsOrd - b.expsOrd) ?? [],
[brandLayoutInfo]
);
const doSendLogGNB = useCallback(
(containerId, shelfOrder) => {
if (
!selectedPatncNm ||
!getMenuByContainerId(containerId) ||
(getMenuByContainerId(containerId) === lastMenuRef.current &&
selectedPatncNm === prevSelectedPatncNmRef.current)
) {
return;
}
const selectedBrand = `${LOG_MENU.FEATURED_BRANDS}/${selectedPatncNm}`;
const currentShelf = `${getMenuByContainerId(containerId)}`;
const menu =
selectedBrand && currentShelf && `${selectedBrand} ${currentShelf}`;
dispatch(sendLogGNB(menu));
dispatch(
sendLogTotalRecommend({
contextName: LOG_CONTEXT_NAME.FEATURED_BRANDS,
messageId: LOG_MESSAGE_ID.SHELF,
partner: selectedPatncNm,
shelfLocation: shelfOrder,
shelfId: containerId,
shelfTitle: currentShelf,
})
);
setIsLogGNBSent(true);
prevSelectedPatncNmRef.current = selectedPatncNm;
lastMenuRef.current = getMenuByContainerId(containerId);
},
[selectedPatncNm, sortedBrandLayoutInfo, selectedPatncNm]
);
const focusOnMount = useCallback((targetId) => {
initialFocusTimeoutJob.current.start(() => {
const focusTarget = findSelector(`[data-spotlight-id="${targetId}"]`);
if (focusTarget) Spotlight.focus(focusTarget);
else findAndFocusFirstContainer(noneTargetTimer);
});
}, []);
const handleItemFocus = useCallback(
(containerId, shelfOrder) => doSendLogGNB(containerId, shelfOrder),
[doSendLogGNB]
);
const handlePopupClick = useCallback(() => {
if (activePopup?.type === ACTIVE_POPUP.reminderPopup) {
dispatch(setMyUpcomingUseAlert({ upcomingAlamUseFlag: "Y" }));
}
if (activePopup?.type === ACTIVE_POPUP.timeConflictPopup) {
const { upcomingAlarmInfo, deletedAlertShows } = activePopup;
dispatch(setMainLiveUpcomingAlarm(upcomingAlarmInfo));
dispatch(deleteMyUpcomingAlertShow({ showList: deletedAlertShows }));
alarmTimer.current = setTimeout(
() => dispatch(getMyUpcomingAlertShow()),
200
);
}
dispatch(setHidePopup());
setTimeout(() => Spotlight.focus());
}, [dispatch, activePopup]);
const handlePopupClose = useCallback(() => {
dispatch(setHidePopup());
setTimeout(() => Spotlight.focus());
}, [dispatch]);
const handleTopButtonClick = useCallback(() => {
if (cbChangePageRef.current) cbChangePageRef.current(0, true);
let target;
if (hasQuickMenu) {
target = document.querySelector(`[data-menu-index="${0}"]`);
} else {
const paginatorItem = document.querySelector(`[data-wheel-point]`);
const spotlightItem = paginatorItem?.querySelector(`[data-spotlight-id]`);
target = spotlightItem?.querySelector(`[tabindex="${-1}"]`);
}
if (target) Spotlight.focus(target);
}, [hasQuickMenu]);
const onFocusedContainerId = useCallback(
(containerId) => setFocusedContainerId(containerId),
[]
);
const renderPageItem = useCallback(() => {
return (
<>
{sortedBrandLayoutInfo.map((el, idx) => {
switch (el.shptmBrndOptTpCd) {
case TEMPLATE_CODE_CONF.LIVE_CHANNELS: {
return (
<React.Fragment key={el.shptmBrndOptTpCd}>
{hasTemplateCodeWithValue(
sortedBrandLayoutInfo,
TEMPLATE_CODE_CONF.LIVE_CHANNELS
) &&
shouldRenderComponent(brandChanInfo) && (
<LiveChannels
brandChanInfo={brandChanInfo}
brandChannelCnt={brandChannelCnt}
handleItemFocus={handleItemFocus}
isOnTop={isOnTop}
order={idx + 1}
shelfOrder={el.expsOrd}
shelfTitle={el.shptmBrndOptTpNm}
spotlightId={TEMPLATE_CODE_CONF.LIVE_CHANNELS}
selectedPatnrId={selectedPatnrId}
/>
)}
</React.Fragment>
);
}
case TEMPLATE_CODE_CONF.UP_COMING: {
return (
<React.Fragment key={el.shptmBrndOptTpCd}>
{hasTemplateCodeWithValue(
sortedBrandLayoutInfo,
TEMPLATE_CODE_CONF.UP_COMING
) &&
shouldRenderComponent(brandLiveChannelUpcoming) && (
<UpComing
brandLiveChannelUpcoming={brandLiveChannelUpcoming}
handleItemFocus={handleItemFocus}
order={idx + 1}
selectedPatnrId={selectedPatnrId}
shelfOrder={el.expsOrd}
shelfTitle={el.shptmBrndOptTpNm}
spotlightId={TEMPLATE_CODE_CONF.UP_COMING}
/>
)}
</React.Fragment>
);
}
case TEMPLATE_CODE_CONF.TODAYS_DEALS: {
return (
<React.Fragment key={el.shptmBrndOptTpCd}>
{hasTemplateCodeWithValue(
sortedBrandLayoutInfo,
TEMPLATE_CODE_CONF.TODAYS_DEALS
) &&
shouldRenderComponent(brandTsvInfo) && (
<TodaysDeals
brandTsvInfo={brandTsvInfo}
handleItemFocus={handleItemFocus}
hasQuickMenu={hasQuickMenu}
order={idx + 1}
shelfOrder={el.expsOrd}
shelfTitle={el.shptmBrndOptTpNm}
spotlightId={TEMPLATE_CODE_CONF.TODAYS_DEALS}
/>
)}
</React.Fragment>
);
}
case TEMPLATE_CODE_CONF.BEST_SELLER: {
return (
<React.Fragment key={el.shptmBrndOptTpCd}>
{hasTemplateCodeWithValue(
sortedBrandLayoutInfo,
TEMPLATE_CODE_CONF.BEST_SELLER
) &&
shouldRenderComponent(brandBestSellerInfo) && (
<FeaturedBestSeller
brandBestSellerInfo={brandBestSellerInfo}
handleItemFocus={handleItemFocus}
order={idx + 1}
shelfOrder={el.expsOrd}
shelfTitle={el.shptmBrndOptTpNm}
spotlightId={TEMPLATE_CODE_CONF.BEST_SELLER}
selectedPatnrId={selectedPatnrId}
/>
)}
</React.Fragment>
);
}
case TEMPLATE_CODE_CONF.RECOMMENDED_SHOWS: {
return (
<React.Fragment key={el.shptmBrndOptTpCd}>
{hasTemplateCodeWithValue(
sortedBrandLayoutInfo,
TEMPLATE_CODE_CONF.RECOMMENDED_SHOWS
) &&
shouldRenderComponent(brandRecommendedShowCategoryInfo) &&
shouldRenderComponent(brandRecommendedShowInfo) && (
<RecommendedShows
brandRecommendedShowCategoryInfo={
brandRecommendedShowCategoryInfo
}
brandRecommendedShowInfo={brandRecommendedShowInfo}
fromGNB={fromGNB}
fromQuickMenu={fromQuickMenu}
handleItemFocus={handleItemFocus}
order={idx + 1}
shelfOrder={el.expsOrd}
shelfTitle={el.shptmBrndOptTpNm}
spotlightId={TEMPLATE_CODE_CONF.RECOMMENDED_SHOWS}
selectedCatCd={selectedCatCd}
selectedPatncNm={selectedPatncNm}
selectedPatnrId={selectedPatnrId}
setSelectedCatCd={setSelectedCatCd}
/>
)}
</React.Fragment>
);
}
case TEMPLATE_CODE_CONF.FEATURED_CREATORS: {
return (
<React.Fragment key={el.shptmBrndOptTpCd}>
{hasTemplateCodeWithValue(
sortedBrandLayoutInfo,
TEMPLATE_CODE_CONF.FEATURED_CREATORS
) &&
shouldRenderComponent(brandCreatorsInfo) &&
shouldRenderComponent(barndCreatorsShowInfo) && (
<FeaturedCreators
brandCreatorsInfo={brandCreatorsInfo}
barndCreatorsShowInfo={barndCreatorsShowInfo}
fromGNB={fromGNB}
fromQuickMenu={fromQuickMenu}
handleItemFocus={handleItemFocus}
order={idx + 1}
shelfOrder={el.expsOrd}
shelfTitle={el.shptmBrndOptTpNm}
selectedHstNm={selectedHstNm}
selectedPatncNm={selectedPatncNm}
selectedPatnrId={selectedPatnrId}
setSelectedHstNm={setSelectedHstNm}
spotlightId={TEMPLATE_CODE_CONF.FEATURED_CREATORS}
/>
)}
</React.Fragment>
);
}
case TEMPLATE_CODE_CONF.SERIES: {
return (
<React.Fragment key={el.shptmBrndOptTpCd}>
{hasTemplateCodeWithValue(
sortedBrandLayoutInfo,
TEMPLATE_CODE_CONF.SERIES
) &&
shouldRenderComponent(brandSeriesGroupInfo) &&
shouldRenderComponent(brandSeriesInfo) && (
<Series
brandSeriesGroupInfo={brandSeriesGroupInfo}
brandSeriesInfo={brandSeriesInfo}
fromGNB={fromGNB}
fromQuickMenu={fromQuickMenu}
handleItemFocus={handleItemFocus}
order={idx + 1}
shelfOrder={el.expsOrd}
shelfTitle={el.shptmBrndOptTpNm}
spotlightId={TEMPLATE_CODE_CONF.SERIES}
selectedPatncNm={selectedPatncNm}
selectedPatnrId={selectedPatnrId}
selectedSeriesId={selectedSeriesId}
setSelectedSeriesId={setSelectedSeriesId}
/>
)}
</React.Fragment>
);
}
case TEMPLATE_CODE_CONF.CATEGORY: {
return (
<React.Fragment key={el.shptmBrndOptTpCd}>
{hasTemplateCodeWithValue(
sortedBrandLayoutInfo,
TEMPLATE_CODE_CONF.CATEGORY
) &&
shouldRenderComponent(brandCategoryInfo) &&
shouldRenderComponent(brandCategoryProductInfo) && (
<FeaturedCategory
brandCategoryInfo={brandCategoryInfo}
brandCategoryProductInfo={brandCategoryProductInfo}
fromGNB={fromGNB}
fromQuickMenu={fromQuickMenu}
handleItemFocus={handleItemFocus}
order={idx + 1}
shelfOrder={el.expsOrd}
shelfTitle={el.shptmBrndOptTpNm}
spotlightId={TEMPLATE_CODE_CONF.CATEGORY}
selectedCatCdLv1={selectedCatCdLv1}
selectedCatCdLv2={selectedCatCdLv2}
selectedPatncNm={selectedPatncNm}
selectedPatnrId={selectedPatnrId}
setSelectedCatCdLv1={setSelectedCatCdLv1}
setSelectedCatCdLv2={setSelectedCatCdLv2}
/>
)}
</React.Fragment>
);
}
case TEMPLATE_CODE_CONF.SHOWROOM: {
return (
<React.Fragment key={el.shptmBrndOptTpCd}>
{hasTemplateCodeWithValue(
sortedBrandLayoutInfo,
TEMPLATE_CODE_CONF.SHOWROOM
) &&
shouldRenderComponent(brandShowroomInfo) && (
<Showroom
brandShowroomInfo={brandShowroomInfo}
fromGNB={fromGNB}
fromQuickMenu={fromQuickMenu}
handleItemFocus={handleItemFocus}
order={idx + 1}
shelfOrder={el.expsOrd}
shelfTitle={el.shptmBrndOptTpNm}
selectedPatnrId={selectedPatnrId}
selectedPatncNm={selectedPatncNm}
spotlightId={TEMPLATE_CODE_CONF.SHOWROOM}
/>
)}
</React.Fragment>
);
}
}
})}
</>
);
}, [
brandBestSellerInfo,
brandCategoryInfo,
brandCategoryProductInfo,
brandChanInfo,
brandChannelCnt,
brandCreatorsInfo,
barndCreatorsShowInfo,
brandLiveChannelUpcoming,
brandRecommendedShowCategoryInfo,
brandRecommendedShowInfo,
brandSeriesGroupInfo,
brandSeriesInfo,
brandShowroomInfo,
brandTsvInfo,
fromGNB,
fromQuickMenu,
handleItemFocus,
hasQuickMenu,
isOnTop,
selectedCatCd,
selectedCatCdLv1,
selectedCatCdLv2,
selectedHstNm,
selectedPatncNm,
selectedPatnrId,
selectedSeriesId,
sortedBrandLayoutInfo,
]);
const resetStates = useCallback(() => {
setSelectedCatCd(null);
setSelectedHstNm(null);
setSelectedSeriesId(null);
setSelectedCatCdLv1(null);
setSelectedCatCdLv2(null);
setContainerLastFocusedElement(null, ["upcoming-list-id"]);
setContainerLastFocusedElement(null, ["featured-category-nav-id"]);
}, []);
// effect: reset the brand store and set the focusedContainerId and fetch brand list on initial render
useEffect(() => {
if (!fromDetail) {
const containerId =
fromUpcoming || panelInfo?.linkTpCd === "8002"
? TEMPLATE_CODE_CONF.LIVE_CHANNELS
: SpotlightIds.BRAND_QUICK_MENU;
setFocusedContainerId(containerId);
dispatch(getBrandList());
dispatch({ type: types.RESET_BRAND_STATE_EXCEPT_BRAND_INFO });
} else setFocusedContainerId(panelInfo?.focusedContainerId);
}, []);
// effect: layout information fetching due to partner id change
useEffect(() => {
if (!fromDetail) {
dispatch({ type: types.RESET_BRAND_LAYOUT_INFO });
dispatch(getBrandLayoutInfo({ patnrId: panelInfo?.patnrId }));
setIsInitialFocusOccurred(false);
setDisplayTopButton(false);
}
}, [panelInfo?.patnrId]);
// effect: set selectedPatnrId and selectedPatncNm
useEffect(() => {
if (brandInfo) {
const patnrId = panelInfo?.patnrId;
const patncNm = brandInfo.find((b) => b?.patnrId === patnrId).patncNm;
setSelectedPatncNm(patncNm);
if (!fromDetail) setSelectedPatnrId(patnrId);
}
}, [brandInfo, panelInfo?.patnrId]);
// effect: data fetching based on brandLayoutInfo and selectedPatnrId
useEffect(() => {
if (sortedBrandLayoutInfo && selectedPatnrId) {
Object.entries(DISPATCH_MAP) //
.forEach(([templateCode, action]) => {
if (hasTemplateCodeWithValue(sortedBrandLayoutInfo, templateCode)) {
dispatch(action({ patnrId: selectedPatnrId }));
}
});
resetStates();
}
}, [sortedBrandLayoutInfo, selectedPatnrId]);
useEffect(() => {
if (selectedCatCd) {
dispatch(
getBrandRecommendedShowInfo({
patnrId: panelInfo?.patnrId,
catCd: selectedCatCd,
})
);
}
}, [selectedCatCd]);
useEffect(() => {
if (selectedHstNm) {
dispatch(
getBrandCreatorsInfo({
patnrId: panelInfo?.patnrId,
hstNm: selectedHstNm,
})
);
}
}, [selectedHstNm]);
useEffect(() => {
if (selectedCatCdLv1 === "") {
dispatch(
getBrandCategoryInfo({
patnrId: panelInfo?.patnrId,
})
);
return;
}
if (selectedCatCdLv1) {
dispatch(
getBrandCategoryProductInfo({
patnrId: panelInfo?.patnrId,
catCdLv1: selectedCatCdLv1,
})
);
}
}, [selectedCatCdLv1]);
// effect: initial or top panel focus logic
useEffect(() => {
if (isOnTop && !isInitialFocusOccurred && selectedPatncNm) {
// case: gnb or quick menu
let targetId = "spotlightId-" + panelInfo?.patnrId;
// case: upcoming or system alarm
if (fromUpcoming || panelInfo?.linkTpCd === "8002") {
targetId = TEMPLATE_CODE_CONF.LIVE_CHANNELS;
if (typeof window === "object" && window.MutationObserver) {
const observer = new window.MutationObserver((mutations) => {
for (let idx = mutations.length; idx--; ) {
if (mutations[idx].addedNodes.length) {
for (let i = mutations[idx].addedNodes.length; i--; ) {
const node = mutations[idx].addedNodes[i];
if (node.getAttribute("data-spotlight-id") === targetId) {
if (cbChangePageRef.current) {
cbChangePageRef.current(targetId, false);
}
Spotlight.focus(node);
break;
}
}
}
}
});
if (orderableFlexContainerRef.current) {
observer.observe(orderableFlexContainerRef.current, {
childList: true,
});
}
return () => observer.disconnect();
}
}
// case: detail
if (fromDetail) {
if (panelInfo?.catCdLv1) setSelectedCatCdLv1(panelInfo.catCdLv1);
if (panelInfo?.catCdLv2) setSelectedCatCdLv2(panelInfo.catCdLv2);
if (panelInfo?.seriesId) setSelectedSeriesId(panelInfo.seriesId);
}
focusOnMount(panelInfo?.lastFocusedTargetId ?? targetId);
setIsInitialFocusOccurred(true);
}
}, [isOnTop, isInitialFocusOccurred, selectedPatncNm]);
// effect: top button display setting logic
useEffect(() => {
if (panelInfo?.displayTopButton) {
setDisplayTopButton(true);
return;
}
if (typeof window === "object" && window.MutationObserver) {
const observer = new window.MutationObserver((mutations) => {
let shouldDisplayTopButton = false;
for (let idx = mutations.length; idx--; ) {
if (mutations[idx].type === "childList") {
renderedShelfCountRef.current =
orderableFlexContainerRef.current?.children.length || 0;
}
if (renderedShelfCountRef.current > 1) {
shouldDisplayTopButton = true;
break;
}
}
if (shouldDisplayTopButton !== displayTopButtonRef.current) {
setDisplayTopButton(shouldDisplayTopButton);
}
});
if (orderableFlexContainerRef.current) {
observer.observe(orderableFlexContainerRef.current, {
childList: true,
});
}
return () => observer.disconnect();
}
}, [panelInfo?.displayTopButton, panelInfo?.patnrId]);
// effect: gnb log
useEffect(() => {
if (!isOnTop) {
prevSelectedPatncNmRef.current = null;
setIsInitialFocusOccurred(false);
}
if (focusedContainerId && isOnTop && selectedPatncNm) {
doSendLogGNB(focusedContainerId);
}
}, [doSendLogGNB, focusedContainerId, isOnTop, selectedPatncNm]);
// effect: partners log
useEffect(() => {
if (
isLogGNBSent &&
isInitialFocusOccurred &&
selectedPatnrId &&
selectedPatncNm
) {
dispatch(
sendLogPartners({
patncNm: selectedPatncNm,
patnrId: selectedPatnrId,
})
);
}
}, [isLogGNBSent, isInitialFocusOccurred, selectedPatnrId, selectedPatncNm]);
// effect: unmount
useEffect(() => {
return () => {
dispatch(
updatePanel({
name: panel_names.FEATURED_BRANDS_PANEL,
panelInfo: {
brandInfo: brandInfoRef.current,
displayTopButton: displayTopButtonRef.current,
focusedContainerId: focusedContainerIdRef.current,
from: "detail",
lastMenu: lastMenuRef.current,
},
})
);
clearTimeout(alarmTimer.current);
clearTimeout(noneTargetTimer.current);
initialFocusTimeoutJob.current.stop();
};
}, []);
return (
<TPanel
className={css.tPanel}
spotlightId={cursorVisible ? spotlightId : null}
>
<TBody
className={css.tBody}
scrollable={false}
spotlightDisabled={!isOnTop}
>
<TVerticalPagenator
cbChangePageRef={cbChangePageRef}
className={css.tVerticalPagenator}
defaultContainerId={panelInfo?.focusedContainerId}
deleteFocus
onFocusedContainerId={onFocusedContainerId}
spotlightId={SpotlightIds.BRAND_VERTICAL_PAGENATOR}
topMargin={TOP_MARGIN}
>
{brandInfo && brandInfo.length > 1 && (
<QuickMenu
brandInfo={brandInfo}
fromGNB={fromGNB}
fromQuickMenu={fromQuickMenu}
handleItemFocus={handleItemFocus}
panelPatnrId={panelInfo?.patnrId}
selectedPatnrId={selectedPatnrId}
spotlightId={SpotlightIds.BRAND_QUICK_MENU}
resetStates={resetStates}
/>
)}
{brandInfo && brandTopImgInfo && (
<Banner
brandInfo={brandInfo}
brandTopImgInfo={brandTopImgInfo}
panelPatnrId={panelInfo?.patnrId}
/>
)}
{sortedBrandLayoutInfo && (
<div
className={css.orderableFlexContainer}
ref={orderableFlexContainerRef}
>
{renderPageItem()}
</div>
)}
{displayTopButton && (
<TButton
className={classNames(css.tButton)}
data-wheel-point
onClick={handleTopButtonClick}
size={null}
spotlightId={SpotlightIds.BRAND_TOP_BUTTON}
type={TYPES.topButton}
/>
)}
</TVerticalPagenator>
</TBody>
{(activePopup?.type === ACTIVE_POPUP.reminderPopup ||
activePopup?.type === ACTIVE_POPUP.timeConflictPopup) && (
<TPopUp
button1Text={$L(STRING_CONF.OK)}
button2Text={$L(STRING_CONF.CANCEL)}
hasButton
hasText
kind="textPopup"
onClick={handlePopupClick}
onClose={handlePopupClose}
open={popupVisible}
text={getMessageByPopupType(activePopup?.type)}
/>
)}
</TPanel>
);
};
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(FeaturedBrandsPanel, propsAreEqual);