[251120] fix: PlayerPanel Return Video Playback
🕐 커밋 시간: 2025. 11. 20. 12:29:35 📊 변경 통계: • 총 파일: 5개 • 추가: +270줄 • 삭제: -97줄 📝 수정된 파일: ~ com.twin.app.shoptime/src/actions/panelActions.js ~ com.twin.app.shoptime/src/views/DetailPanel/DetailPanel.jsx ~ com.twin.app.shoptime/src/views/HomePanel/HomePanel.jsx ~ com.twin.app.shoptime/src/views/PlayerPanel/PlayerPanel.jsx ~ com.twin.app.shoptime/src/views/PlayerPanel/PlayerTabContents/TabContents/ShopNowContents.jsx 🔧 주요 변경 내용: • 핵심 비즈니스 로직 개선 • 대규모 기능 개발
This commit is contained in:
@@ -31,6 +31,8 @@ import {
|
||||
finishVideoPreview,
|
||||
pauseFullscreenVideo,
|
||||
resumeFullscreenVideo,
|
||||
pauseModalVideo,
|
||||
resumeModalVideo,
|
||||
} from '../../actions/playActions';
|
||||
import {
|
||||
clearProductDetail,
|
||||
@@ -159,62 +161,175 @@ export default function DetailPanel({ panelInfo, isOnTop, spotlightId }) {
|
||||
}));
|
||||
}, [dispatch]);
|
||||
|
||||
// ✅ [251118] DetailPanel이 사라질 때 HomePanel 활성화
|
||||
// ✅ [251120] DetailPanel이 사라질 때 처리 - sourcePanel에 따라 switch 문으로 처리
|
||||
useEffect(() => {
|
||||
return () => {
|
||||
const sourcePanel = panelInfo?.sourcePanel;
|
||||
const sourceMenu = panelInfo?.sourceMenu;
|
||||
|
||||
// DetailPanel이 unmount되는 시점
|
||||
console.log('[DetailPanel] unmount - HomePanel 활성화 신호 전송');
|
||||
console.log('[DetailPanel] unmount:', {
|
||||
sourcePanel,
|
||||
sourceMenu,
|
||||
timestamp: Date.now(),
|
||||
});
|
||||
|
||||
// HomePanel에서 비디오 재생을 다시 시작하도록 신호 보내기
|
||||
console.log('[TRACE-GRADIENT] 🔶 DetailPanel unmount - Creating new panelInfo');
|
||||
console.log('[TRACE-GRADIENT] 🔶 DetailPanel unmount - Existing panelInfo before update:', JSON.stringify(panelInfo));
|
||||
|
||||
dispatch(updateHomeInfo({
|
||||
name: panel_names.HOME_PANEL,
|
||||
panelInfo: {
|
||||
shouldResumeVideo: true, // ✅ 신호
|
||||
lastDetailPanelClosed: Date.now(), // ✅ 시점 기록
|
||||
showGradientBackground: false, // ✅ 명시적으로 그라데이션 끔기
|
||||
// sourcePanel에 따른 상태 업데이트
|
||||
switch (sourcePanel) {
|
||||
case panel_names.PLAYER_PANEL: {
|
||||
// PlayerPanel에서 온 경우: PlayerPanel에 detailPanelClosed flag 전달
|
||||
console.log('[DetailPanel] unmount - PlayerPanel에 detailPanelClosed flag 전달');
|
||||
dispatch(updatePanel({
|
||||
name: panel_names.PLAYER_PANEL,
|
||||
panelInfo: {
|
||||
detailPanelClosed: true, // ✅ flag
|
||||
detailPanelClosedAt: Date.now(), // ✅ 시점 기록
|
||||
detailPanelClosedFromSource: sourceMenu, // ✅ 출처
|
||||
}
|
||||
}));
|
||||
break;
|
||||
}
|
||||
}));
|
||||
|
||||
case panel_names.HOME_PANEL: {
|
||||
// HomePanel에서 온 경우: HomePanel에 detailPanelClosed flag 전달
|
||||
console.log('[DetailPanel] unmount - HomePanel에 detailPanelClosed flag 전달');
|
||||
console.log('[TRACE-GRADIENT] 🔶 DetailPanel unmount - HomePanel 복귀');
|
||||
|
||||
dispatch(updateHomeInfo({
|
||||
name: panel_names.HOME_PANEL,
|
||||
panelInfo: {
|
||||
detailPanelClosed: true, // ✅ flag
|
||||
detailPanelClosedAt: Date.now(), // ✅ 시점 기록
|
||||
detailPanelClosedFromSource: sourceMenu, // ✅ 출처
|
||||
showGradientBackground: false, // ✅ 명시적으로 그라데이션 끔기
|
||||
}
|
||||
}));
|
||||
break;
|
||||
}
|
||||
|
||||
case panel_names.SEARCH_PANEL: {
|
||||
// SearchPanel에서 온 경우: SearchPanel에 detailPanelClosed flag 전달
|
||||
console.log('[DetailPanel] unmount - SearchPanel에 detailPanelClosed flag 전달');
|
||||
dispatch(
|
||||
updatePanel({
|
||||
name: panel_names.SEARCH_PANEL,
|
||||
panelInfo: {
|
||||
detailPanelClosed: true, // ✅ flag
|
||||
detailPanelClosedAt: Date.now(), // ✅ 시점 기록
|
||||
detailPanelClosedFromSource: sourceMenu, // ✅ 출처
|
||||
},
|
||||
})
|
||||
);
|
||||
break;
|
||||
}
|
||||
|
||||
default:
|
||||
console.warn('[DetailPanel] unmount - 처리되지 않은 sourcePanel:', sourcePanel);
|
||||
break;
|
||||
}
|
||||
};
|
||||
}, [dispatch]);
|
||||
}, [dispatch, panelInfo?.sourcePanel]);
|
||||
|
||||
const onBackClick = useCallback(
|
||||
(isCancelClick) => (ev) => {
|
||||
const sourcePanel = panelInfo?.sourcePanel;
|
||||
const sourceMenu = panelInfo?.sourceMenu;
|
||||
|
||||
fp.pipe(
|
||||
() => {
|
||||
dispatch(clearAllToasts()); // BuyOption Toast 포함 모든 토스트 제거
|
||||
dispatch(pauseFullscreenVideo()); // PLAYER_PANEL 비디오 중지
|
||||
dispatch(finishModalMediaForce()); // MEDIA_PANEL(ProductVideo) 강제 종료
|
||||
dispatch(finishVideoPreview());
|
||||
|
||||
// sourcePanel에 따른 사전 처리
|
||||
switch (sourcePanel) {
|
||||
case panel_names.PLAYER_PANEL:
|
||||
// PlayerPanel에서 온 경우: 플레이어 비디오는 그대로 두고 모달만 정리
|
||||
console.log('[DetailPanel] onBackClick - PlayerPanel 출신: 모달 정리만 수행');
|
||||
dispatch(finishModalMediaForce()); // MEDIA_PANEL(ProductVideo) 강제 종료
|
||||
dispatch(finishVideoPreview());
|
||||
break;
|
||||
|
||||
case panel_names.HOME_PANEL:
|
||||
case panel_names.SEARCH_PANEL:
|
||||
default:
|
||||
// HomePanel, SearchPanel 등에서 온 경우: 백그라운드 비디오 일시 중지
|
||||
console.log('[DetailPanel] onBackClick - source panel:', sourcePanel, '백그라운드 비디오 일시 중지');
|
||||
dispatch(pauseFullscreenVideo()); // PLAYER_PANEL 비디오 중지
|
||||
dispatch(finishModalMediaForce()); // MEDIA_PANEL(ProductVideo) 강제 종료
|
||||
dispatch(finishVideoPreview());
|
||||
break;
|
||||
}
|
||||
|
||||
dispatch(popPanel(panel_names.DETAIL_PANEL));
|
||||
},
|
||||
() => {
|
||||
// 패널 업데이트 조건 체크
|
||||
const shouldUpdatePanel =
|
||||
fp.pipe(
|
||||
() => panels,
|
||||
fp.get('length'),
|
||||
(length) => length === 4
|
||||
)() &&
|
||||
fp.pipe(
|
||||
() => panels,
|
||||
fp.get('1.name'),
|
||||
(name) => name === panel_names.PLAYER_PANEL
|
||||
)();
|
||||
// sourcePanel에 따른 상태 업데이트
|
||||
switch (sourcePanel) {
|
||||
case panel_names.PLAYER_PANEL: {
|
||||
// PlayerPanel에서 온 경우: PlayerPanel에 detailPanelClosed flag 전달
|
||||
const shouldUpdatePanel =
|
||||
fp.pipe(
|
||||
() => panels,
|
||||
fp.get('length'),
|
||||
(length) => length === 3 // PlayerPanel이 [1]에 있고 DetailPanel이 [2]에 있는 상태
|
||||
)() &&
|
||||
fp.pipe(
|
||||
() => panels,
|
||||
fp.get('1.name'),
|
||||
(name) => name === panel_names.PLAYER_PANEL
|
||||
)();
|
||||
|
||||
if (shouldUpdatePanel) {
|
||||
dispatch(
|
||||
updatePanel({
|
||||
name: panel_names.PLAYER_PANEL,
|
||||
if (shouldUpdatePanel) {
|
||||
console.log('[DetailPanel] onBackClick - PlayerPanel에 detailPanelClosed flag 전달');
|
||||
dispatch(
|
||||
updatePanel({
|
||||
name: panel_names.PLAYER_PANEL,
|
||||
panelInfo: {
|
||||
thumbnail: fp.pipe(() => panelInfo, fp.get('thumbnailUrl'))(),
|
||||
detailPanelClosed: true, // ✅ flag
|
||||
detailPanelClosedAt: Date.now(), // ✅ 시점 기록
|
||||
detailPanelClosedFromSource: sourceMenu, // ✅ 출처
|
||||
},
|
||||
})
|
||||
);
|
||||
}
|
||||
break;
|
||||
}
|
||||
|
||||
case panel_names.HOME_PANEL: {
|
||||
// HomePanel에서 온 경우: HomePanel에 detailPanelClosed flag 전달
|
||||
console.log('[DetailPanel] onBackClick - HomePanel에 detailPanelClosed flag 전달');
|
||||
dispatch(updateHomeInfo({
|
||||
name: panel_names.HOME_PANEL,
|
||||
panelInfo: {
|
||||
thumbnail: fp.pipe(() => panelInfo, fp.get('thumbnailUrl'))(),
|
||||
},
|
||||
})
|
||||
);
|
||||
detailPanelClosed: true, // ✅ flag
|
||||
detailPanelClosedAt: Date.now(), // ✅ 시점 기록
|
||||
detailPanelClosedFromSource: sourceMenu, // ✅ 출처
|
||||
showGradientBackground: false,
|
||||
}
|
||||
}));
|
||||
break;
|
||||
}
|
||||
|
||||
case panel_names.SEARCH_PANEL: {
|
||||
// SearchPanel에서 온 경우: SearchPanel에 detailPanelClosed flag 전달
|
||||
console.log('[DetailPanel] onBackClick - SearchPanel에 detailPanelClosed flag 전달');
|
||||
dispatch(
|
||||
updatePanel({
|
||||
name: panel_names.SEARCH_PANEL,
|
||||
panelInfo: {
|
||||
detailPanelClosed: true, // ✅ flag
|
||||
detailPanelClosedAt: Date.now(), // ✅ 시점 기록
|
||||
detailPanelClosedFromSource: sourceMenu, // ✅ 출처
|
||||
},
|
||||
})
|
||||
);
|
||||
break;
|
||||
}
|
||||
|
||||
default:
|
||||
console.warn('[DetailPanel] onBackClick - 처리되지 않은 sourcePanel:', sourcePanel);
|
||||
break;
|
||||
}
|
||||
// PlayerPanel의 isOnTop useEffect가 자동으로 오버레이 표시
|
||||
}
|
||||
)();
|
||||
|
||||
@@ -725,46 +840,48 @@ export default function DetailPanel({ panelInfo, isOnTop, spotlightId }) {
|
||||
|
||||
// 백그라운드 전체화면 비디오 제어: DetailPanel 진입/퇴장 시
|
||||
useEffect(() => {
|
||||
// console.log('[BgVideo] DetailPanel mounted - checking panels:', {
|
||||
// panelsCount: panels?.length,
|
||||
// panels: panels?.map(p => ({ name: p.name, modal: p.panelInfo?.modal }))
|
||||
// });
|
||||
|
||||
// 전체화면 PlayerPanel(modal=false)이 존재하는지 확인
|
||||
const hasFullscreenPlayerPanel = fp.pipe(
|
||||
() => panels,
|
||||
(panelList) =>
|
||||
panelList.some(
|
||||
(panel) => panel.name === panel_names.PLAYER_PANEL && !panel.panelInfo?.modal
|
||||
)
|
||||
)();
|
||||
// PlayerPanel이 존재하는지 확인 (Modal 또는 Fullscreen)
|
||||
const playerPanel = panels.find(
|
||||
(panel) => panel.name === panel_names.PLAYER_PANEL
|
||||
);
|
||||
const hasPlayerPanel = !!playerPanel;
|
||||
const isModal = playerPanel?.panelInfo?.modal;
|
||||
|
||||
// ProductAllSection에 비디오가 있는지 확인
|
||||
const hasProductVideo = fp.pipe(() => productData, fp.get('prdtMediaUrl'), fp.isNotNil)();
|
||||
|
||||
// console.log('[BgVideo] hasFullscreenPlayerPanel:', hasFullscreenPlayerPanel);
|
||||
// console.log('[BgVideo] hasProductVideo:', hasProductVideo);
|
||||
console.log('[BgVideo] DetailPanel - Video Control Check:', {
|
||||
hasPlayerPanel,
|
||||
isModal,
|
||||
hasProductVideo,
|
||||
sourceMenu: panelInfo?.sourceMenu
|
||||
});
|
||||
|
||||
// 전체화면 PlayerPanel이 있고, 제품에 비디오가 있을 때만 백그라운드 비디오 멈춤
|
||||
if (hasFullscreenPlayerPanel && hasProductVideo) {
|
||||
// console.log('[BgVideo] DetailPanel - Product has video, dispatching pauseFullscreenVideo()');
|
||||
dispatch(pauseFullscreenVideo());
|
||||
// PlayerPanel이 있고, 제품에 비디오가 있을 때만 비디오 멈춤
|
||||
if (hasPlayerPanel && hasProductVideo) {
|
||||
console.log('[BgVideo] DetailPanel - Pausing video');
|
||||
if (isModal) {
|
||||
dispatch(pauseModalVideo());
|
||||
} else {
|
||||
dispatch(pauseFullscreenVideo());
|
||||
}
|
||||
} else {
|
||||
console.log('[BgVideo] DetailPanel - Skipping pause:', {
|
||||
reason: !hasFullscreenPlayerPanel ? 'no fullscreen PlayerPanel' : 'no product video',
|
||||
});
|
||||
console.log('[BgVideo] DetailPanel - Skipping pause');
|
||||
}
|
||||
|
||||
return () => {
|
||||
// DetailPanel 언마운트 시: 비디오가 있었고 멈췄던 경우만 재생 재개
|
||||
// console.log('[BgVideo] DetailPanel unmounting');
|
||||
if (hasFullscreenPlayerPanel && hasProductVideo) {
|
||||
// console.log('[BgVideo] DetailPanel - Product had video, dispatching resumeFullscreenVideo()');
|
||||
dispatch(resumeFullscreenVideo());
|
||||
if (hasPlayerPanel && hasProductVideo) {
|
||||
console.log('[BgVideo] DetailPanel - Resuming video');
|
||||
if (isModal) {
|
||||
dispatch(resumeModalVideo());
|
||||
} else {
|
||||
dispatch(resumeFullscreenVideo());
|
||||
}
|
||||
}
|
||||
};
|
||||
// eslint-disable-next-line react-hooks/exhaustive-deps
|
||||
}, []); // 마운트/언마운트 시에만 실행
|
||||
}, [panelInfo?.sourceMenu, productData?.prdtMediaUrl]);
|
||||
|
||||
// MediaPanel modal 상태 변화 감지 -> ProductVideo로 포커스 이동
|
||||
useEffect(() => {
|
||||
|
||||
Reference in New Issue
Block a user