[251014] refactor(views): [251014] ProductAllSection 동영상 포커스 처리

🕐 커밋 시간: 2025. 10. 14. 09:44:35

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

📝 수정된 파일:
  ~ com.twin.app.shoptime/src/views/DetailPanel/ProductAllSection/ProductAllSection.jsx
  ~ com.twin.app.shoptime/src/views/DetailPanel/ProductContentSection/ProductVideo/ProductVideo.jsx

🔧 함수 변경 내용:
  📄 com.twin.app.shoptime/src/views/DetailPanel/ProductAllSection/ProductAllSection.jsx (javascript):
    🔄 Modified: extractProductMeta()

Performance: 코드 최적화로 성능 개선 기대
This commit is contained in:
2025-10-14 09:44:36 +09:00
parent 8c28f3ba8b
commit 9c44a8864e
2 changed files with 82 additions and 91 deletions

View File

@@ -14,7 +14,7 @@ import SpotlightContainerDecorator from '@enact/spotlight/SpotlightContainerDeco
//image
import arrowDown from '../../../../assets/images/icons/ic_arrow_down_3x_new.png';
import indicatorDefaultImage from '../../../../assets/images/img-thumb-empty-144@3x.png';
import { pushPanel } from '../../../actions/panelActions';
// import { pushPanel } from '../../../actions/panelActions';
import { resetShowAllReviews } from '../../../actions/productActions';
import { showToast } from '../../../actions/toastActions';
// ProductInfoSection imports
@@ -143,7 +143,7 @@ export default function ProductAllSection({
const youmaylikeData = useSelector((state) => state.main.youmaylikeData);
// ProductVideo 버전 관리 (1: 기존 modal 방식, 2: 내장 방식)
const [productVideoVersion, setProductVideoVersion] = useState(2);
const [productVideoVersion, setProductVideoVersion] = useState(1);
// const [currentHeight, setCurrentHeight] = useState(0);
//하단부분까지 갔을때 체크용
@@ -157,6 +157,9 @@ export default function ProductAllSection({
// 모든 timeout/timer를 추적하기 위한 ref
const timersRef = useRef([]);
// handleScrollToImages의 timeout을 추적하기 위한 ref
const scrollToImagesTimeoutRef = useRef(null);
useEffect(() => {
const toggleQRCode = () => {
if (isShowQRCode) {
@@ -251,34 +254,6 @@ export default function ProductAllSection({
dispatch(resetShowAllReviews());
}, []); // 빈 dependency array = 마운트 시에만 실행
// [임시 테스트] LayoutSample 클릭 핸들러 - ShowUserReviews와 동일하게 UserReviewPanel 열기
// const handleLayoutSampleClick = useCallback(() => {
// console.log(`[ProductId] LayoutSample clicked - opening UserReviewPanel`, {
// productDataPrdtId: productData && productData.prdtId,
// hasProductData: !!productData,
// reviewTotalCount: stats.totalReviews,
// averageRating: stats.averageRating,
// productData: productData,
// });
// dispatch(
// pushPanel({
// name: panel_names.USER_REVIEW_PANEL,
// panelInfo: {
// prdtId: productData.prdtId,
// productImage:
// (productData.imgUrls600 && productData.imgUrls600[0]) ||
// (productData.imgUrls && productData.imgUrls[0]) ||
// productData.thumbnailUrl ||
// "https://placehold.co/150x150",
// brandLogo: productData.patncLogoPath || "https://placehold.co/50x50",
// productName: productData.prdtNm || "상품명 정보가 없습니다",
// avgRating: stats.averageRating || 5,
// reviewCount: stats.totalReviews || 0,
// },
// })
// );
// }, [dispatch, productData, stats]);
// BUY NOW 버튼 클릭 핸들러 - Toast로 BuyOption 표시
const handleBuyNowClick = useCallback(() => {
// console.log('[BuyNow] Buy Now button clicked');
@@ -298,26 +273,6 @@ export default function ProductAllSection({
// TODO: 장바구니 추가 로직 구현
}, []);
// 디버깅: 실제 이미지 및 동영상 데이터 확인
// useEffect(() => {
// console.log('[ProductId] ProductAllSection productData check:', {
// hasProductData: !!productData,
// productDataPrdtId: productData && productData.prdtId,
// imgUrls600: productData && productData.imgUrls600,
// imgUrls600Length: productData && productData.imgUrls600 && productData.imgUrls600.length,
// imgUrls600Type: Array.isArray(productData && productData.imgUrls600)
// ? 'array'
// : typeof (productData && productData.imgUrls600),
// // 동영상 관련 정보 추가
// prdtMediaUrl: productData && productData.prdtMediaUrl,
// thumbnailUrl960: productData && productData.thumbnailUrl960,
// hasVideo: !!(productData && productData.prdtMediaUrl),
// renderItemsLength: renderItems.length,
// renderItems: renderItems,
// productData: productData,
// });
// }, [productData, renderItems]);
const { revwGrd, orderPhnNo } = useMemo(() => extractProductMeta(productInfo), [productInfo]);
const [favoriteOverride, setFavoriteOverride] = useState(null);
@@ -328,15 +283,7 @@ export default function ProductAllSection({
const [mobileSendPopupOpen, setMobileSendPopupOpen] = useState(false);
// useReviews에서 모든 리뷰 데이터 관리
const reviewTotalCount = stats.totalReviews;
// const reviewData = {
// reviewList: previewReviews,
// reviewDetail: {
// totRvwCnt: stats.totalReviews,
// avgRvwScr: stats.averageRating,
// },
// };
const { getScrollTo, scrollTop } = useScrollTo();
const scrollToSection = useCallback(
@@ -361,6 +308,22 @@ export default function ProductAllSection({
[scrollToSection]
);
// 비디오 다음 이미지로 스크롤하는 핸들러
const handleScrollToImages = useCallback(() => {
scrollToSection('scroll-marker-after-video');
// 기존 timeout이 있으면 클리어
if (scrollToImagesTimeoutRef.current) {
clearTimeout(scrollToImagesTimeoutRef.current);
}
// 250ms 후 ProductDetail로 포커스 이동
scrollToImagesTimeoutRef.current = setTimeout(() => {
Spotlight.move('down');
scrollToImagesTimeoutRef.current = null;
}, 250);
}, [scrollToSection]);
const scrollContainerRef = useRef(null);
const productDetailRef = useRef(null); //높이값 변경때문
const descriptionRef = useRef(null);
@@ -516,6 +479,12 @@ export default function ProductAllSection({
});
timersRef.current = [];
// handleScrollToImages timeout cleanup
if (scrollToImagesTimeoutRef.current) {
clearTimeout(scrollToImagesTimeoutRef.current);
scrollToImagesTimeoutRef.current = null;
}
// console.log('[ProductAllSection] All timers cleaned up on unmount');
};
}, []);
@@ -727,40 +696,47 @@ export default function ProductAllSection({
onFocus={() => handleButtonFocus('product')}
onBlur={handleButtonBlur}
>
{renderItems.length > 0 ? (
renderItems.map((item, index) =>
item.type === 'video' ? (
productVideoVersion === 1 ? (
<ProductVideo
key="product-video-0"
productInfo={productData}
videoUrl={item.url}
thumbnailUrl={item.thumbnail}
/>
) : (
<ProductVideoV2
key="product-video-v2-0"
productInfo={productData}
videoUrl={item.url}
thumbnailUrl={item.thumbnail}
autoPlay={true}
/>
)
) : (
<ProductDetail
key={`product-detail-${index}`}
productInfo={{
...productData,
singleImage: item.url,
imageIndex: item.index,
totalImages: renderItems.length,
}}
{/* 비디오가 있으면 먼저 렌더링 */}
{hasVideo && renderItems[0].type === 'video' && (
<>
{productVideoVersion === 1 ? (
<ProductVideo
key="product-video-0"
productInfo={productData}
videoUrl={renderItems[0].url}
thumbnailUrl={renderItems[0].thumbnail}
onScrollToImages={handleScrollToImages}
/>
)
)
) : (
<ProductDetail productInfo={productData} />
) : (
<ProductVideoV2
key="product-video-v2-0"
productInfo={productData}
videoUrl={renderItems[0].url}
thumbnailUrl={renderItems[0].thumbnail}
autoPlay={true}
onScrollToImages={handleScrollToImages}
/>
)}
<div id="scroll-marker-after-video" className={css.scrollMarker}></div>
</>
)}
{/* 이미지들만 렌더링 (비디오 제외) */}
{renderItems.length > 0
? renderItems
.filter((item) => item.type === 'image')
.map((item, index) => (
<ProductDetail
key={`product-detail-${index}`}
productInfo={{
...productData,
singleImage: item.url,
imageIndex: item.index,
totalImages: renderItems.length,
}}
/>
))
: !hasVideo && <ProductDetail productInfo={productData} />}
</div>
<div
id="product-description-section"

View File

@@ -13,7 +13,7 @@ import css from './ProductVideo.module.less';
const SpottableComponent = Spottable('div');
export default function ProductVideo({ productInfo, videoUrl, thumbnailUrl }) {
export default function ProductVideo({ productInfo, videoUrl, thumbnailUrl, onScrollToImages }) {
const dispatch = useDispatch();
// MediaPanel 상태 체크를 위한 selectors 추가
@@ -70,6 +70,20 @@ export default function ProductVideo({ productInfo, videoUrl, thumbnailUrl }) {
}
}, [canPlayVideo, dispatch]);
// Spotlight Down 키 핸들러 - 비디오 다음 이미지로 스크롤
const handleSpotlightDown = useCallback(
(e) => {
if (canPlayVideo && onScrollToImages) {
e.preventDefault();
e.stopPropagation();
onScrollToImages();
return true; // 이벤트 처리 완료
}
return false; // Spotlight가 기본 동작 수행
},
[canPlayVideo, onScrollToImages]
);
// MediaPanel 비디오 클릭 핸들러 + 모달 토글 기능
const handleVideoClick = useCallback(() => {
console.log('[ProductVideo] ========== handleVideoClick 호출 ==========');
@@ -148,6 +162,7 @@ export default function ProductVideo({ productInfo, videoUrl, thumbnailUrl }) {
onClick={handleVideoClick}
onFocus={videoContainerOnFocus}
onBlur={videoContainerOnBlur}
onSpotlightDown={handleSpotlightDown}
spotlightId="product-video-player"
aria-label={`${productInfo?.prdtNm} 동영상 재생`}
>