[251114] fix: HomePanel DetailPanel전환시 비디오 정리
🕐 커밋 시간: 2025. 11. 14. 16:05:09 📊 변경 통계: • 총 파일: 4개 • 추가: +78줄 • 삭제: -12줄 📝 수정된 파일: ~ com.twin.app.shoptime/src/views/DetailPanel/ProductAllSection/ProductAllSection.jsx ~ com.twin.app.shoptime/src/views/DetailPanel/ProductContentSection/ProductVideo/ProductVideo.v3.jsx ~ com.twin.app.shoptime/src/views/HomePanel/HomeBanner/RandomUnit.jsx ~ com.twin.app.shoptime/src/views/MediaPanel/MediaPanel.v3.jsx 🔧 함수 변경 내용: 📄 com.twin.app.shoptime/src/views/DetailPanel/ProductAllSection/ProductAllSection.jsx (javascript): 🔄 Modified: extractProductMeta() 📄 com.twin.app.shoptime/src/views/DetailPanel/ProductContentSection/ProductVideo/ProductVideo.v3.jsx (javascript): 🔄 Modified: Spottable() 📄 com.twin.app.shoptime/src/views/HomePanel/HomeBanner/RandomUnit.jsx (javascript): 🔄 Modified: SpotlightContainerDecorator() 📄 com.twin.app.shoptime/src/views/MediaPanel/MediaPanel.v3.jsx (javascript): 🔄 Modified: normalizeModalStyle()
This commit is contained in:
@@ -24,7 +24,7 @@ import TButton from '../../../components/TButton/TButton';
|
||||
import useReviews from '../../../hooks/useReviews/useReviews';
|
||||
import useScrollTo from '../../../hooks/useScrollTo';
|
||||
import { BUYNOW_CONFIG } from '../../../utils/BuyNowConfig';
|
||||
// import { panel_names } from '../../../utils/Config';
|
||||
import { panel_names } from '../../../utils/Config';
|
||||
import {
|
||||
andThen,
|
||||
curry,
|
||||
@@ -530,6 +530,7 @@ export default function ProductAllSection({
|
||||
const descriptionRef = useRef(null);
|
||||
const reviewRef = useRef(null);
|
||||
const youMayAlsoLikelRef = useRef(null);
|
||||
const prevMediaPanelModalStateRef = useRef(null); // MediaPanel의 이전 modal 상태 추적
|
||||
|
||||
// 동영상과 이미지를 통합한 렌더링 아이템 리스트 생성 (Indicator.jsx 로직 기반)
|
||||
const renderItems = useMemo(() => {
|
||||
@@ -843,6 +844,35 @@ export default function ProductAllSection({
|
||||
// };
|
||||
// }, []);
|
||||
|
||||
// Redux에서 panels 상태 가져오기
|
||||
const panels = useSelector((state) => state.panels.panels);
|
||||
|
||||
// MediaPanel의 전체화면 복귀 감지 및 포커스 복구
|
||||
useEffect(() => {
|
||||
const topPanel = panels[panels.length - 1];
|
||||
const currentModalState = topPanel?.panelInfo?.modal;
|
||||
|
||||
// 전체화면(false) → 모달(true)로 복귀하는 경우만 감지
|
||||
if (
|
||||
topPanel?.name === panel_names.MEDIA_PANEL &&
|
||||
currentModalState === true &&
|
||||
prevMediaPanelModalStateRef.current === false
|
||||
) {
|
||||
console.log(
|
||||
'[ProductAllSection] 🔄 MediaPanel이 전체화면에서 모달로 복귀 - ProductVideo로 포커스 복구'
|
||||
);
|
||||
const focusTimer = setTimeout(() => {
|
||||
Spotlight.focus('product-video-player');
|
||||
}, 100);
|
||||
return () => clearTimeout(focusTimer);
|
||||
}
|
||||
|
||||
// 현재 modal 상태 저장
|
||||
if (topPanel?.name === panel_names.MEDIA_PANEL) {
|
||||
prevMediaPanelModalStateRef.current = currentModalState;
|
||||
}
|
||||
}, [panels]);
|
||||
|
||||
// 컴포넌트 unmount 시 timer cleanup
|
||||
useEffect(() => {
|
||||
return () => {
|
||||
|
||||
@@ -1,5 +1,6 @@
|
||||
import React, { useCallback, useMemo, useState, useEffect } from 'react';
|
||||
import React, { useCallback, useMemo, useState, useEffect, useRef } from 'react';
|
||||
import { useDispatch, useSelector } from 'react-redux';
|
||||
import Spotlight from '@enact/spotlight';
|
||||
import Spottable from '@enact/spotlight/Spottable';
|
||||
import {
|
||||
startMediaPlayer,
|
||||
@@ -34,22 +35,42 @@ export default function ProductVideo({
|
||||
const [modalState, setModalState] = useState(true); // 모달 상태 관리 추가
|
||||
const [hasAutoPlayed, setHasAutoPlayed] = useState(false); // 자동 재생 완료 여부
|
||||
const [isVideoPlaying, setIsVideoPlaying] = useState(false); // 비디오 재생 여부 flag
|
||||
const prevModalStateRef = useRef(null); // 이전 modal 상태 추적
|
||||
|
||||
const topPanel = panels[panels.length - 1];
|
||||
|
||||
// MediaPanel 상태 체크 로직 + 모달 상태 복원
|
||||
// MediaPanel 상태 체크 로직 + 모달 상태 복원 + 포커스 복구
|
||||
useEffect(() => {
|
||||
if (
|
||||
topPanel &&
|
||||
topPanel.name === panel_names.MEDIA_PANEL &&
|
||||
topPanel.panelInfo.modal === false
|
||||
) {
|
||||
return; // MediaPanel이 전체화면 모드일 때는 처리하지 않음
|
||||
// 전체화면 모드: 이전 상태 저장
|
||||
prevModalStateRef.current = false;
|
||||
return;
|
||||
}
|
||||
|
||||
// MediaPanel이 modal=true로 복귀했을 때 포커스 복구
|
||||
if (
|
||||
topPanel &&
|
||||
topPanel.name === panel_names.MEDIA_PANEL &&
|
||||
topPanel.panelInfo.modal === true &&
|
||||
prevModalStateRef.current === false
|
||||
) {
|
||||
console.log('[ProductVideo] MediaPanel returned to modal - restoring focus to ProductVideo');
|
||||
const focusTimer = setTimeout(() => {
|
||||
Spotlight.focus('product-video-player');
|
||||
prevModalStateRef.current = true;
|
||||
}, 50);
|
||||
return () => clearTimeout(focusTimer);
|
||||
}
|
||||
|
||||
// MediaPanel이 닫혔을 때 modalState를 true로 복원
|
||||
if (!topPanel || topPanel.name !== panel_names.MEDIA_PANEL) {
|
||||
console.log('[ProductVideo] MediaPanel closed - restoring modal state');
|
||||
setModalState(true);
|
||||
prevModalStateRef.current = null;
|
||||
}
|
||||
}, [topPanel]);
|
||||
|
||||
|
||||
@@ -18,7 +18,12 @@ import { changeAppStatus } from '../../../actions/commonActions';
|
||||
import { updateHomeInfo } from '../../../actions/homeActions';
|
||||
import { sendLogTopContents, sendLogTotalRecommend } from '../../../actions/logActions';
|
||||
import { pushPanel } from '../../../actions/panelActions';
|
||||
import { finishVideoPreview, startVideoPlayer, startVideoPlayerNew, shrinkVideoTo1px } from '../../../actions/playActions';
|
||||
import {
|
||||
finishVideoPreview,
|
||||
startVideoPlayer,
|
||||
startVideoPlayerNew,
|
||||
shrinkVideoTo1px,
|
||||
} from '../../../actions/playActions';
|
||||
import CustomImage from '../../../components/CustomImage/CustomImage';
|
||||
import usePriceInfo from '../../../hooks/usePriceInfo';
|
||||
import {
|
||||
@@ -391,6 +396,11 @@ export default function RandomUnit({
|
||||
|
||||
let action = linkType === 'DSP00507' ? startVideoPlayer : pushPanel;
|
||||
|
||||
const isNavigatingToDetail = linkInfo.name === panel_names.DETAIL_PANEL;
|
||||
if (isNavigatingToDetail && playerPanelInfo?.modal !== false) {
|
||||
dispatch(finishVideoPreview());
|
||||
}
|
||||
|
||||
dispatch(action(linkInfo));
|
||||
sendBannerLog(true);
|
||||
dispatch(
|
||||
@@ -410,11 +420,16 @@ export default function RandomUnit({
|
||||
randomData?.showId,
|
||||
randomData?.shptmLnkTpCd,
|
||||
topContentsLogInfo,
|
||||
playerPanelInfo?.modal,
|
||||
sendBannerLog,
|
||||
]);
|
||||
|
||||
// 투데이즈딜 클릭
|
||||
const todayDealClick = useCallback(() => {
|
||||
if (playerPanelInfo?.modal !== false) {
|
||||
dispatch(finishVideoPreview());
|
||||
}
|
||||
|
||||
dispatch(
|
||||
pushPanel({
|
||||
name: panel_names.DETAIL_PANEL,
|
||||
@@ -434,7 +449,14 @@ export default function RandomUnit({
|
||||
logTpNo: LOG_TP_NO.TOP_CONTENTS.CLICK,
|
||||
})
|
||||
);
|
||||
}, [dispatch, randomData?.patnrId, randomData?.prdtId, topContentsLogInfo, sendBannerLog]);
|
||||
}, [
|
||||
dispatch,
|
||||
randomData?.patnrId,
|
||||
randomData?.prdtId,
|
||||
topContentsLogInfo,
|
||||
sendBannerLog,
|
||||
playerPanelInfo?.modal,
|
||||
]);
|
||||
|
||||
// 비디오 클릭
|
||||
const videoClick = useCallback(() => {
|
||||
@@ -456,7 +478,10 @@ export default function RandomUnit({
|
||||
// ✅ modal=true → modal=false로 전환 (또는 초기 로드 시 modal=false)
|
||||
// playActions의 shouldSkipVideoPlayback이 modal 상태를 확인하므로
|
||||
// 모드 전환은 중복 방지 로직을 스킵하여 정상 작동
|
||||
console.log('[RandomUnit] videoClick: current playerPanel modal state:', playerPanelInfo?.modal);
|
||||
console.log(
|
||||
'[RandomUnit] videoClick: current playerPanel modal state:',
|
||||
playerPanelInfo?.modal
|
||||
);
|
||||
|
||||
dispatch(
|
||||
startVideoPlayerNew({
|
||||
@@ -483,7 +508,17 @@ export default function RandomUnit({
|
||||
|
||||
sendBannerLog(true);
|
||||
onBlur();
|
||||
}, [randomData, spotlightId, topContentsLogInfo, nowMenu, randomDataRef, sendBannerLog, onBlur, playerPanelInfo?.modal, dispatch]);
|
||||
}, [
|
||||
randomData,
|
||||
spotlightId,
|
||||
topContentsLogInfo,
|
||||
nowMenu,
|
||||
randomDataRef,
|
||||
sendBannerLog,
|
||||
onBlur,
|
||||
playerPanelInfo?.modal,
|
||||
dispatch,
|
||||
]);
|
||||
|
||||
// 투데이즈 딜 가격 정보
|
||||
const { originalPrice, discountedPrice, discountRate, offerInfo } =
|
||||
|
||||
@@ -898,6 +898,12 @@ const MediaPanel = React.forwardRef(
|
||||
})
|
||||
);
|
||||
}
|
||||
|
||||
// 모달 복귀 시 ProductVideo로 포커스 이동 (ProductVideo.v3.jsx에서 처리)
|
||||
console.log(
|
||||
'[MediaPanel] Back button pressed - returning to modal, focus will be handled by ProductVideo'
|
||||
);
|
||||
|
||||
ev?.stopPropagation();
|
||||
// ev?.preventDefault();
|
||||
return;
|
||||
@@ -2041,13 +2047,11 @@ const MediaPanel = React.forwardRef(
|
||||
|
||||
useEffect(() => {
|
||||
if (panelInfoRef.current?.modal && !panelInfo.modal && isOnTop && !videoVerticalVisible) {
|
||||
const focusTimer = setTimeout(() => {
|
||||
Spotlight.focus(SpotlightIds.PLAYER_BACK_BUTTON);
|
||||
}, 0);
|
||||
|
||||
return () => {
|
||||
clearTimeout(focusTimer);
|
||||
};
|
||||
console.log(
|
||||
'[MediaPanel] Modal to fullscreen transition detected - focus will be handled by ProductVideo'
|
||||
);
|
||||
// 포커스를 ProductVideo에 맡김 (ProductVideo.v3.jsx에서 처리)
|
||||
// PLAYER_BACK_BUTTON으로 포커스하지 않음
|
||||
}
|
||||
}, [panelInfo.modal, isOnTop, videoVerticalVisible]);
|
||||
|
||||
|
||||
Reference in New Issue
Block a user