[SHOPITME-3334] Featured Brands / Show Room / Detail Page 에서 이전키 동작 안하는 현상

Changed files:
1. ImagePanel.jsx
2. ImageOverlayContents.jsx
3. ImageSideContents.jsx
4. ListEmptyContents.jsx
5. RoomThemeList.jsx
6. ShowNowProductList.jsx

Detail note:
1. refactoring and additional defensive logic
This commit is contained in:
younghoon100.park
2024-11-29 15:10:30 +09:00
parent 1fb0138d87
commit d22aac17a6
6 changed files with 133 additions and 162 deletions

View File

@@ -12,7 +12,7 @@ import css from "./ImageOverlayContents.module.less";
const SpottableButton = Spottable("button");
export default memo(function ImageOverlayContents({
const ImageOverlayContents = ({
overlayContentsVisible,
panelInfo,
selectedRoomThemeInfosLength,
@@ -23,17 +23,14 @@ export default memo(function ImageOverlayContents({
sideContentsVisible,
themeNm,
...rest
}) {
}) => {
const dispatch = useDispatch();
const handleBackClick = useCallback(
// eslint-disable-next-line
(e) => {
// e.stopPropagation();
if (sideContentsVisible) {
return;
}
if (sideContentsVisible) return;
dispatch(popPanel());
},
@@ -44,9 +41,7 @@ export default memo(function ImageOverlayContents({
(e) => {
e.stopPropagation();
if (sideContentsVisible) {
return;
}
if (sideContentsVisible) return;
const previousPageNumber =
selectedThemeExpsOrd === 1
@@ -67,9 +62,7 @@ export default memo(function ImageOverlayContents({
(e) => {
e.stopPropagation();
if (sideContentsVisible) {
return;
}
if (sideContentsVisible) return;
const nextPageNumber =
selectedThemeExpsOrd === selectedRoomThemeInfosLength
@@ -181,4 +174,6 @@ export default memo(function ImageOverlayContents({
/>
</div>
);
});
};
export default memo(ImageOverlayContents);

View File

@@ -1,4 +1,10 @@
import React, { useCallback, useEffect, useRef, useState } from "react";
import React, {
useCallback,
useEffect,
useMemo,
useRef,
useState,
} from "react";
import classNames from "classnames";
import { useDispatch, useSelector } from "react-redux";
@@ -6,6 +12,7 @@ import { useDispatch, useSelector } from "react-redux";
import { Job } from "@enact/core/util";
import Spotlight from "@enact/spotlight";
import SpotlightContainerDecorator from "@enact/spotlight/SpotlightContainerDecorator";
import { getContainerId } from "@enact/spotlight/src/container";
import { sendLogCuration, sendLogGNB } from "../../actions/logActions";
import { updatePanel } from "../../actions/panelActions";
@@ -39,7 +46,7 @@ const findSelector = (selector, maxAttempts = 5, currentAttempts = 0) => {
}
};
export default function ImagePanel({ panelInfo, spotlightId, ...rest }) {
const ImagePanel = ({ panelInfo, spotlightId }) => {
const dispatch = useDispatch();
const brandShowroomInfo = useSelector(
@@ -54,32 +61,36 @@ export default function ImagePanel({ panelInfo, spotlightId, ...rest }) {
useState(null);
const [selectedThemeExpsOrd, setSelectedThemeExpsOrd] = useState(null);
const [sideContentsVisible, setSideContentsVisible] = useState(true);
const [spotlightDisabled, setSpotlightDisabled] = useState(true);
const initialFocusTimeoutJob = useRef(new Job((func) => func(), 100));
const themeViewTimer = useRef(null);
const initialFocusTimeoutJob = useRef(new Job((func) => func(), 0));
const themeDisplayTimerRef = useRef(null);
const memoizedRoomThemeInfo = useMemo(
() =>
brandShowroomInfo?.find(({ roomId }) => roomId === panelInfo?.roomId) ||
null,
[brandShowroomInfo, panelInfo?.roomId]
);
useEffect(() => {
let sideCotnentsTimer;
const handleAction = () => clearTimeout(sideCotnentsTimer);
let sideContentsTimer;
const handleAction = () => clearTimeout(sideContentsTimer);
window.addEventListener("click", handleAction);
window.addEventListener("keydown", handleAction);
sideCotnentsTimer = setTimeout(() => setSideContentsVisible(false), 10000);
sideContentsTimer = setTimeout(() => setSideContentsVisible(false), 10000);
return () => {
window.removeEventListener("click", handleAction);
window.removeEventListener("keydown", handleAction);
clearTimeout(sideCotnentsTimer);
clearTimeout(sideContentsTimer);
};
}, []);
useEffect(() => {
let overlayContentsTimer;
const handleAction = () => clearTimeout(overlayContentsTimer);
if (!sideContentsVisible) {
@@ -100,112 +111,80 @@ export default function ImagePanel({ panelInfo, spotlightId, ...rest }) {
};
}, [sideContentsVisible]);
// effect: set selectedThemeExpsOrd on initial render
useEffect(() => {
if (panelInfo?.themeExpsOrd) {
setSelectedThemeExpsOrd(panelInfo?.themeExpsOrd);
}
}, []);
// effect: set selectedRoomThemeInfo and otherRoomThemeInfos based on selectedThemeExpsOrd
useEffect(() => {
if (memoizedRoomThemeInfo && selectedThemeExpsOrd) {
const { roomThemeInfos } = memoizedRoomThemeInfo;
if (roomThemeInfos) {
setSelectedRoomThemeInfosLength(roomThemeInfos.length);
setSelectedRoomThemeInfo(
roomThemeInfos.find(
({ themeExpsOrd }) => themeExpsOrd === selectedThemeExpsOrd
)
);
setOtherRoomThemeInfos(
roomThemeInfos.filter(
({ themeExpsOrd }) => themeExpsOrd !== selectedThemeExpsOrd
)
);
}
}
}, [memoizedRoomThemeInfo, selectedThemeExpsOrd]);
// effect: curation log
useEffect(() => {
if (panelInfo && selectedRoomThemeInfo) {
const { linkTpCd = "", patncNm, patnrId } = panelInfo;
const params = {
curationId: selectedRoomThemeInfo?.themeId,
curationNm: selectedRoomThemeInfo?.themeNm,
expsOrd: `${selectedRoomThemeInfo?.themeExpsOrd}`,
logTpNo: LOG_TP_NO.CURATION.SHOWROOM,
linkTpCd: panelInfo?.linkTpCd,
patncNm: panelInfo?.patncNm,
patnrId: panelInfo?.patnrId,
linkTpCd,
patncNm,
patnrId,
};
themeViewTimer.current = setTimeout(() => {
dispatch(sendLogCuration(params));
}, 3000);
themeDisplayTimerRef.current = setTimeout(
() => dispatch(sendLogCuration(params)),
3000
);
return () => clearTimeout(themeViewTimer.current);
return () => clearTimeout(themeDisplayTimerRef.current);
}
}, [panelInfo, selectedRoomThemeInfo]);
useEffect(() => {
if (panelInfo) {
setOtherRoomThemeInfos(
brandShowroomInfo
.find(({ roomId }) => roomId === panelInfo?.roomId)
?.roomThemeInfos //
.filter(({ themeId }) => themeId !== panelInfo?.themeId)
);
setSelectedRoomThemeInfo(
brandShowroomInfo
.find(({ roomId }) => roomId === panelInfo?.roomId)
?.roomThemeInfos //
.find(({ themeId }) => themeId === panelInfo?.themeId)
);
setSelectedRoomThemeInfosLength(
brandShowroomInfo //
.find(({ roomId }) => roomId === panelInfo?.roomId)?.roomThemeInfos
?.length
);
setSelectedThemeExpsOrd(panelInfo?.themeExpsOrd);
}
}, [brandShowroomInfo, panelInfo]);
useEffect(() => {
setOtherRoomThemeInfos(
brandShowroomInfo
.find(({ roomId }) => roomId === panelInfo?.roomId)
?.roomThemeInfos //
.filter(({ themeExpsOrd }) => themeExpsOrd !== selectedThemeExpsOrd)
);
setSelectedRoomThemeInfo(
brandShowroomInfo
.find(({ roomId }) => roomId === panelInfo?.roomId)
?.roomThemeInfos //
.find(({ themeExpsOrd }) => themeExpsOrd === selectedThemeExpsOrd)
);
}, [brandShowroomInfo, panelInfo?.roomId, selectedThemeExpsOrd]);
}, [selectedRoomThemeInfo]);
// effect: focus logic
useEffect(() => {
if (!isInitialFocusOccurred) {
let targetId;
if (panelInfo?.lastFocusedTargetId) {
targetId = panelInfo.lastFocusedTargetId;
}
//
else if (panelInfo?.targetId) {
targetId = panelInfo.targetId;
}
//
else {
targetId = "spotlightId-arrow";
}
const targetId =
panelInfo?.lastFocusedTargetId ??
panelInfo?.targetId ??
"spotlightId-arrow";
initialFocusTimeoutJob.current.start(() => {
const initialFocusTarget = findSelector(
`[data-spotlight-id="${targetId}"]`
);
setSpotlightDisabled(false);
if (initialFocusTarget) {
Spotlight.focus(initialFocusTarget);
}
const focusTarget = findSelector(`[data-spotlight-id="${targetId}"]`);
if (focusTarget) Spotlight.focus(focusTarget);
setIsInitialFocusOccurred(true);
});
}
}, [
isInitialFocusOccurred,
panelInfo?.lastFocusedTargetId,
panelInfo?.targetId,
]);
}, [isInitialFocusOccurred]);
// effect: unmount
useEffect(() => {
return () => {
const lastFocusedTarget = Spotlight.getCurrent();
if (lastFocusedTarget) {
const lastFocusedTargetId =
lastFocusedTarget.getAttribute("data-spotlight-id");
const lastFocusedTargetId = getContainerId(Spotlight.getCurrent());
if (lastFocusedTargetId) {
dispatch(
updatePanel({
name: panel_names.IMAGE_PANEL,
@@ -216,7 +195,7 @@ export default function ImagePanel({ panelInfo, spotlightId, ...rest }) {
);
}
};
}, [dispatch]);
}, []);
const handleClick = useCallback(
(e) => {
@@ -224,23 +203,21 @@ export default function ImagePanel({ panelInfo, spotlightId, ...rest }) {
if (e.target.id === "dark-gradient") {
setSideContentsVisible(false);
}
} else {
setOverlayContentsVisible((prev) => !prev);
}
} else setOverlayContentsVisible((prev) => !prev);
},
[sideContentsVisible]
);
const handleSeletedTab = useCallback((nowMenu) => {
dispatch(sendLogGNB(nowMenu));
}, []);
const handleSelectedTab = useCallback(
(nowMenu) => dispatch(sendLogGNB(nowMenu)),
[]
);
return (
<TPanel
className={css.tPanel}
isTabActivated={false}
spotlightId={spotlightId}
spotlightDisabled={spotlightDisabled}
>
{selectedRoomThemeInfo &&
selectedRoomThemeInfosLength &&
@@ -271,12 +248,15 @@ export default function ImagePanel({ panelInfo, spotlightId, ...rest }) {
{sideContentsVisible && (
<ImageSideContents
otherRoomThemeInfos={otherRoomThemeInfos}
handleSeletedTab={handleSeletedTab}
handleSelectedTab={handleSelectedTab}
selectedRoomThemeInfo={selectedRoomThemeInfo}
setSelectedThemeExpsOrd={setSelectedThemeExpsOrd}
/>
)}
</Container>
)}
</TPanel>
);
}
};
export default ImagePanel;

View File

@@ -12,33 +12,30 @@ import ShopNowProductList from "./ShopNowProductList/ShopNowProductList";
const tabList = [$L("SHOP NOW"), $L("ROOM THEME")];
export default memo(function ImageSideContents({
const ImageSideContents = ({
otherRoomThemeInfos,
handleSeletedTab,
handleSelectedTab,
selectedRoomThemeInfo,
}) {
setSelectedThemeExpsOrd,
}) => {
const [tabIndex, setTabIndex] = useState(0);
const { roomThemeProducts } = selectedRoomThemeInfo;
useEffect(() => {
if (handleSeletedTab) {
if (handleSelectedTab) {
const nowMenu =
tabIndex === 0
? LOG_MENU.SHOW_ROOM_SHOP_NOW
: LOG_MENU.SHOW_ROOM_ROOM_THEME;
handleSeletedTab(nowMenu);
handleSelectedTab(nowMenu);
}
}, [tabIndex, handleSeletedTab]);
}, [tabIndex, handleSelectedTab]);
const handleItemClick = useCallback(
({ index }) => {
if (index === tabIndex) {
return;
}
setTabIndex(index);
if (index !== tabIndex) setTabIndex(index);
},
[tabIndex]
);
@@ -60,10 +57,15 @@ export default memo(function ImageSideContents({
<ListEmptyContents tabIndex={tabIndex} />
)
) : otherRoomThemeInfos.length > 0 ? (
<RoomThemeList otherRoomThemeInfos={otherRoomThemeInfos} />
<RoomThemeList
otherRoomThemeInfos={otherRoomThemeInfos}
setSelectedThemeExpsOrd={setSelectedThemeExpsOrd}
/>
) : (
<ListEmptyContents tabIndex={tabIndex} />
)}
</div>
);
});
};
export default memo(ImageSideContents);

View File

@@ -9,7 +9,7 @@ const STRING_CONF = {
THEME: "Theme",
};
export default memo(function ListEmptyContents({ tabIndex = 0 }) {
const ListEmptyContents = ({ tabIndex = 0 }) => {
return (
<figure className={css.container}>
<img src={ImgContentsLoading} alt="" />
@@ -21,4 +21,6 @@ export default memo(function ListEmptyContents({ tabIndex = 0 }) {
</figcaption>
</figure>
);
});
};
export default memo(ListEmptyContents);

View File

@@ -1,4 +1,4 @@
import React, { useCallback, useEffect } from "react";
import React, { memo, useCallback, useEffect } from "react";
import { useDispatch } from "react-redux";
@@ -9,29 +9,29 @@ import useScrollTo from "../../../../hooks/useScrollTo";
import { panel_names } from "../../../../utils/Config";
import css from "./RoomThemeList.module.less";
export default function RoomThemeList({ otherRoomThemeInfos }) {
const RoomThemeList = ({ otherRoomThemeInfos, setSelectedThemeExpsOrd }) => {
const dispatch = useDispatch();
const { getScrollTo, scrollTop } = useScrollTo();
const dispatch = useDispatch();
useEffect(() => {
scrollTop({ animate: false });
}, [scrollTop, otherRoomThemeInfos]);
useEffect(() => scrollTop({ animate: false }), [otherRoomThemeInfos]);
const handleClick = useCallback(
(themeExpsOrd, themeId) => () => {
(themeExpsOrd, themeId, themeNm) => () => {
if (setSelectedThemeExpsOrd) setSelectedThemeExpsOrd(themeExpsOrd);
dispatch(
updatePanel({
name: panel_names.IMAGE_PANEL,
panelInfo: {
themeId,
themeExpsOrd,
themeNm,
y: 0,
},
})
);
},
[dispatch]
[setSelectedThemeExpsOrd]
);
const renderItem = useCallback(
@@ -44,7 +44,7 @@ export default function RoomThemeList({ otherRoomThemeInfos }) {
className={css.tItemCard}
imageAlt={themeNm}
imageSource={themeImgUrl}
onClick={handleClick(themeExpsOrd, themeId)}
onClick={handleClick(themeExpsOrd, themeId, themeNm)}
productId={themeId}
productName={themeNm}
type={TYPES.horizontal}
@@ -70,4 +70,6 @@ export default function RoomThemeList({ otherRoomThemeInfos }) {
)}
</div>
);
}
};
export default memo(RoomThemeList);

View File

@@ -1,4 +1,4 @@
import React, { useCallback, useEffect, useRef } from "react";
import React, { memo, useCallback, useEffect, useRef } from "react";
import { useDispatch, useSelector } from "react-redux";
@@ -12,10 +12,9 @@ import { panel_names } from "../../../../utils/Config";
import { getTranslate3dValueByDirection } from "../../../../utils/helperMethods";
import css from "./ShopNowProductList.module.less";
export default function ShopNowProductList({ selectedRoomThemeInfo }) {
const { getScrollTo, scrollTop } = useScrollTo();
const ShopNowProductList = ({ selectedRoomThemeInfo }) => {
const dispatch = useDispatch();
const { getScrollTo, scrollTop } = useScrollTo();
const panelInfo = useSelector((state) => state.panels.panels[1]?.panelInfo);
@@ -24,9 +23,7 @@ export default function ShopNowProductList({ selectedRoomThemeInfo }) {
const { roomThemeProducts } = selectedRoomThemeInfo;
useEffect(() => {
if (!panelInfo?.y) {
return;
}
if (!panelInfo?.y) return;
const scrollTopJobValue = scrollTopJob.current;
const { y } = panelInfo;
@@ -36,9 +33,7 @@ export default function ShopNowProductList({ selectedRoomThemeInfo }) {
return () => scrollTopJobValue.stop();
}, [panelInfo, scrollTop]);
useEffect(() => {
scrollTop({ animate: false });
}, [scrollTop, selectedRoomThemeInfo]);
useEffect(() => scrollTop({ animate: false }), [selectedRoomThemeInfo]);
const handleClick = useCallback(
(patnrId, prdtId) => (e) => {
@@ -72,15 +67,8 @@ export default function ShopNowProductList({ selectedRoomThemeInfo }) {
const renderItem = useCallback(
({ index, ...rest }) => {
const {
offerInfo,
patnrId,
// prdtExpsOrd,
prdtId,
prdtImgUrl,
prdtNm,
priceInfo,
} = roomThemeProducts[index];
const { offerInfo, patnrId, prdtId, prdtImgUrl, prdtNm, priceInfo } =
roomThemeProducts[index];
return (
<TItemCard
@@ -115,4 +103,6 @@ export default function ShopNowProductList({ selectedRoomThemeInfo }) {
)}
</div>
);
}
};
export default memo(ShopNowProductList);