[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:
2025-11-20 12:29:36 +09:00
parent e21f6a1072
commit 8644527502
5 changed files with 268 additions and 95 deletions

View File

@@ -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(() => {