[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 useReviews from '../../../hooks/useReviews/useReviews';
|
||||||
import useScrollTo from '../../../hooks/useScrollTo';
|
import useScrollTo from '../../../hooks/useScrollTo';
|
||||||
import { BUYNOW_CONFIG } from '../../../utils/BuyNowConfig';
|
import { BUYNOW_CONFIG } from '../../../utils/BuyNowConfig';
|
||||||
// import { panel_names } from '../../../utils/Config';
|
import { panel_names } from '../../../utils/Config';
|
||||||
import {
|
import {
|
||||||
andThen,
|
andThen,
|
||||||
curry,
|
curry,
|
||||||
@@ -530,6 +530,7 @@ export default function ProductAllSection({
|
|||||||
const descriptionRef = useRef(null);
|
const descriptionRef = useRef(null);
|
||||||
const reviewRef = useRef(null);
|
const reviewRef = useRef(null);
|
||||||
const youMayAlsoLikelRef = useRef(null);
|
const youMayAlsoLikelRef = useRef(null);
|
||||||
|
const prevMediaPanelModalStateRef = useRef(null); // MediaPanel의 이전 modal 상태 추적
|
||||||
|
|
||||||
// 동영상과 이미지를 통합한 렌더링 아이템 리스트 생성 (Indicator.jsx 로직 기반)
|
// 동영상과 이미지를 통합한 렌더링 아이템 리스트 생성 (Indicator.jsx 로직 기반)
|
||||||
const renderItems = useMemo(() => {
|
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
|
// 컴포넌트 unmount 시 timer cleanup
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
return () => {
|
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 { useDispatch, useSelector } from 'react-redux';
|
||||||
|
import Spotlight from '@enact/spotlight';
|
||||||
import Spottable from '@enact/spotlight/Spottable';
|
import Spottable from '@enact/spotlight/Spottable';
|
||||||
import {
|
import {
|
||||||
startMediaPlayer,
|
startMediaPlayer,
|
||||||
@@ -34,22 +35,42 @@ export default function ProductVideo({
|
|||||||
const [modalState, setModalState] = useState(true); // 모달 상태 관리 추가
|
const [modalState, setModalState] = useState(true); // 모달 상태 관리 추가
|
||||||
const [hasAutoPlayed, setHasAutoPlayed] = useState(false); // 자동 재생 완료 여부
|
const [hasAutoPlayed, setHasAutoPlayed] = useState(false); // 자동 재생 완료 여부
|
||||||
const [isVideoPlaying, setIsVideoPlaying] = useState(false); // 비디오 재생 여부 flag
|
const [isVideoPlaying, setIsVideoPlaying] = useState(false); // 비디오 재생 여부 flag
|
||||||
|
const prevModalStateRef = useRef(null); // 이전 modal 상태 추적
|
||||||
|
|
||||||
const topPanel = panels[panels.length - 1];
|
const topPanel = panels[panels.length - 1];
|
||||||
|
|
||||||
// MediaPanel 상태 체크 로직 + 모달 상태 복원
|
// MediaPanel 상태 체크 로직 + 모달 상태 복원 + 포커스 복구
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
if (
|
if (
|
||||||
topPanel &&
|
topPanel &&
|
||||||
topPanel.name === panel_names.MEDIA_PANEL &&
|
topPanel.name === panel_names.MEDIA_PANEL &&
|
||||||
topPanel.panelInfo.modal === false
|
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로 복원
|
// MediaPanel이 닫혔을 때 modalState를 true로 복원
|
||||||
if (!topPanel || topPanel.name !== panel_names.MEDIA_PANEL) {
|
if (!topPanel || topPanel.name !== panel_names.MEDIA_PANEL) {
|
||||||
|
console.log('[ProductVideo] MediaPanel closed - restoring modal state');
|
||||||
setModalState(true);
|
setModalState(true);
|
||||||
|
prevModalStateRef.current = null;
|
||||||
}
|
}
|
||||||
}, [topPanel]);
|
}, [topPanel]);
|
||||||
|
|
||||||
|
|||||||
@@ -18,7 +18,12 @@ import { changeAppStatus } from '../../../actions/commonActions';
|
|||||||
import { updateHomeInfo } from '../../../actions/homeActions';
|
import { updateHomeInfo } from '../../../actions/homeActions';
|
||||||
import { sendLogTopContents, sendLogTotalRecommend } from '../../../actions/logActions';
|
import { sendLogTopContents, sendLogTotalRecommend } from '../../../actions/logActions';
|
||||||
import { pushPanel } from '../../../actions/panelActions';
|
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 CustomImage from '../../../components/CustomImage/CustomImage';
|
||||||
import usePriceInfo from '../../../hooks/usePriceInfo';
|
import usePriceInfo from '../../../hooks/usePriceInfo';
|
||||||
import {
|
import {
|
||||||
@@ -391,6 +396,11 @@ export default function RandomUnit({
|
|||||||
|
|
||||||
let action = linkType === 'DSP00507' ? startVideoPlayer : pushPanel;
|
let action = linkType === 'DSP00507' ? startVideoPlayer : pushPanel;
|
||||||
|
|
||||||
|
const isNavigatingToDetail = linkInfo.name === panel_names.DETAIL_PANEL;
|
||||||
|
if (isNavigatingToDetail && playerPanelInfo?.modal !== false) {
|
||||||
|
dispatch(finishVideoPreview());
|
||||||
|
}
|
||||||
|
|
||||||
dispatch(action(linkInfo));
|
dispatch(action(linkInfo));
|
||||||
sendBannerLog(true);
|
sendBannerLog(true);
|
||||||
dispatch(
|
dispatch(
|
||||||
@@ -410,11 +420,16 @@ export default function RandomUnit({
|
|||||||
randomData?.showId,
|
randomData?.showId,
|
||||||
randomData?.shptmLnkTpCd,
|
randomData?.shptmLnkTpCd,
|
||||||
topContentsLogInfo,
|
topContentsLogInfo,
|
||||||
|
playerPanelInfo?.modal,
|
||||||
sendBannerLog,
|
sendBannerLog,
|
||||||
]);
|
]);
|
||||||
|
|
||||||
// 투데이즈딜 클릭
|
// 투데이즈딜 클릭
|
||||||
const todayDealClick = useCallback(() => {
|
const todayDealClick = useCallback(() => {
|
||||||
|
if (playerPanelInfo?.modal !== false) {
|
||||||
|
dispatch(finishVideoPreview());
|
||||||
|
}
|
||||||
|
|
||||||
dispatch(
|
dispatch(
|
||||||
pushPanel({
|
pushPanel({
|
||||||
name: panel_names.DETAIL_PANEL,
|
name: panel_names.DETAIL_PANEL,
|
||||||
@@ -434,7 +449,14 @@ export default function RandomUnit({
|
|||||||
logTpNo: LOG_TP_NO.TOP_CONTENTS.CLICK,
|
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(() => {
|
const videoClick = useCallback(() => {
|
||||||
@@ -456,7 +478,10 @@ export default function RandomUnit({
|
|||||||
// ✅ modal=true → modal=false로 전환 (또는 초기 로드 시 modal=false)
|
// ✅ modal=true → modal=false로 전환 (또는 초기 로드 시 modal=false)
|
||||||
// playActions의 shouldSkipVideoPlayback이 modal 상태를 확인하므로
|
// playActions의 shouldSkipVideoPlayback이 modal 상태를 확인하므로
|
||||||
// 모드 전환은 중복 방지 로직을 스킵하여 정상 작동
|
// 모드 전환은 중복 방지 로직을 스킵하여 정상 작동
|
||||||
console.log('[RandomUnit] videoClick: current playerPanel modal state:', playerPanelInfo?.modal);
|
console.log(
|
||||||
|
'[RandomUnit] videoClick: current playerPanel modal state:',
|
||||||
|
playerPanelInfo?.modal
|
||||||
|
);
|
||||||
|
|
||||||
dispatch(
|
dispatch(
|
||||||
startVideoPlayerNew({
|
startVideoPlayerNew({
|
||||||
@@ -483,7 +508,17 @@ export default function RandomUnit({
|
|||||||
|
|
||||||
sendBannerLog(true);
|
sendBannerLog(true);
|
||||||
onBlur();
|
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 } =
|
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?.stopPropagation();
|
||||||
// ev?.preventDefault();
|
// ev?.preventDefault();
|
||||||
return;
|
return;
|
||||||
@@ -2041,13 +2047,11 @@ const MediaPanel = React.forwardRef(
|
|||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
if (panelInfoRef.current?.modal && !panelInfo.modal && isOnTop && !videoVerticalVisible) {
|
if (panelInfoRef.current?.modal && !panelInfo.modal && isOnTop && !videoVerticalVisible) {
|
||||||
const focusTimer = setTimeout(() => {
|
console.log(
|
||||||
Spotlight.focus(SpotlightIds.PLAYER_BACK_BUTTON);
|
'[MediaPanel] Modal to fullscreen transition detected - focus will be handled by ProductVideo'
|
||||||
}, 0);
|
);
|
||||||
|
// 포커스를 ProductVideo에 맡김 (ProductVideo.v3.jsx에서 처리)
|
||||||
return () => {
|
// PLAYER_BACK_BUTTON으로 포커스하지 않음
|
||||||
clearTimeout(focusTimer);
|
|
||||||
};
|
|
||||||
}
|
}
|
||||||
}, [panelInfo.modal, isOnTop, videoVerticalVisible]);
|
}, [panelInfo.modal, isOnTop, videoVerticalVisible]);
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user