From 9f3cd6254928aded411936ae60d98121e96cf234 Mon Sep 17 00:00:00 2001 From: optrader Date: Sun, 26 Oct 2025 21:44:13 +0900 Subject: [PATCH] =?UTF-8?q?[251026]=20feat:=20modal=20=EB=AA=A8=EB=93=9C?= =?UTF-8?q?=EC=97=90=EC=84=9C=20=EB=B9=84=EB=94=94=EC=98=A4=201px=20?= =?UTF-8?q?=EC=B6=95=EC=86=8C/=EB=B3=B5=EA=B5=AC=20=EA=B8=B0=EB=8A=A5=20?= =?UTF-8?q?=EA=B5=AC=ED=98=84?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - PlayerPanel useEffect에서 shouldShrinkTo1px 플래그로 인라인 스타일 제거 - shrinkVideoTo1px/expandVideoFrom1px 액션 추가 (배너 정보 playerState에 저장) - PlayerPanel.module.less에 .shrinkTo1px 클래스 추가 - HomePanel onScroll에서 스크롤 방향 감지하여 축소/확대 제어 - RandomUnit onBlur에서의 축소 로직은 주석 처리 (스크롤로 제어) --- .../src/actions/playActions.js | 77 +++++++++++++++++++ .../views/HomePanel/HomeBanner/RandomUnit.jsx | 28 ++++++- .../src/views/HomePanel/HomePanel.jsx | 20 ++++- .../src/views/PlayerPanel/PlayerPanel.jsx | 6 ++ .../views/PlayerPanel/PlayerPanel.module.less | 17 ++++ 5 files changed, 143 insertions(+), 5 deletions(-) diff --git a/com.twin.app.shoptime/src/actions/playActions.js b/com.twin.app.shoptime/src/actions/playActions.js index bf4665ad..a718c9a7 100644 --- a/com.twin.app.shoptime/src/actions/playActions.js +++ b/com.twin.app.shoptime/src/actions/playActions.js @@ -240,6 +240,83 @@ export const resumeFullscreenVideo = () => (dispatch, getState) => { } }; +// 모달 비디오를 1px로 축소 (배너 정보 저장) +export const shrinkVideoTo1px = () => (dispatch, getState) => { + const panels = getState().panels.panels; + + // modal PlayerPanel 찾기 + const modalPlayerPanel = panels.find( + (panel) => panel.name === panel_names.PLAYER_PANEL && panel.panelInfo?.modal + ); + + if (modalPlayerPanel) { + const panelInfo = modalPlayerPanel.panelInfo; + console.log('[shrinkVideoTo1px] Shrinking modal video to 1px'); + + // 축소 전 배너 정보를 playerState에 저장 + const updatedPlayerState = { + ...(panelInfo.playerState || {}), + shrinkInfo: { + // 복구 시 필요한 정보 + modalContainerId: panelInfo.modalContainerId, + modalClassName: panelInfo.modalClassName, + modalStyle: panelInfo.modalStyle, + modalScale: panelInfo.modalScale, + currentBannerId: panelInfo.playerState?.currentBannerId, + patnrId: panelInfo.patnrId, + showId: panelInfo.showId, + shptmBanrTpNm: panelInfo.shptmBanrTpNm, + lgCatCd: panelInfo.lgCatCd, + }, + }; + + dispatch( + updatePanel({ + name: panel_names.PLAYER_PANEL, + panelInfo: { + ...panelInfo, + shouldShrinkTo1px: true, // 축소 플래그 설정 + playerState: updatedPlayerState, + }, + }) + ); + } +}; + +// 축소된 모달 비디오를 원래 크기로 복구 +export const expandVideoFrom1px = () => (dispatch, getState) => { + const panels = getState().panels.panels; + + // 축소된 modal PlayerPanel 찾기 + const shrunkModalPlayerPanel = panels.find( + (panel) => panel.name === panel_names.PLAYER_PANEL && panel.panelInfo?.modal && panel.panelInfo?.shouldShrinkTo1px + ); + + if (shrunkModalPlayerPanel) { + const panelInfo = shrunkModalPlayerPanel.panelInfo; + const shrinkInfo = panelInfo.playerState?.shrinkInfo; + + console.log('[expandVideoFrom1px] Expanding modal video from 1px', shrinkInfo); + + dispatch( + updatePanel({ + name: panel_names.PLAYER_PANEL, + panelInfo: { + ...panelInfo, + shouldShrinkTo1px: false, // 축소 플래그 해제 + // 저장된 정보로 복구 + ...(shrinkInfo && { + modalContainerId: shrinkInfo.modalContainerId, + modalClassName: shrinkInfo.modalClassName, + modalStyle: shrinkInfo.modalStyle, + modalScale: shrinkInfo.modalScale, + }), + }, + }) + ); + } +}; + // 채팅 로그 가져오기 IF-LGSP-371 export const getChatLog = ({ patnrId, showId }) => diff --git a/com.twin.app.shoptime/src/views/HomePanel/HomeBanner/RandomUnit.jsx b/com.twin.app.shoptime/src/views/HomePanel/HomeBanner/RandomUnit.jsx index 85885993..023c53ee 100644 --- a/com.twin.app.shoptime/src/views/HomePanel/HomeBanner/RandomUnit.jsx +++ b/com.twin.app.shoptime/src/views/HomePanel/HomeBanner/RandomUnit.jsx @@ -18,7 +18,7 @@ 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 } from '../../../actions/playActions'; +import { finishVideoPreview, startVideoPlayer, startVideoPlayerNew, shrinkVideoTo1px } from '../../../actions/playActions'; import CustomImage from '../../../components/CustomImage/CustomImage'; import usePriceInfo from '../../../hooks/usePriceInfo'; import { @@ -203,17 +203,34 @@ export default function RandomUnit({ // 포커스 인 const onFocus = useCallback(() => { + console.log('[RandomUnit] onFocus called', { + spotlightId, + videoPlayerable, + currentVideoBannerId, + randomData: randomData?.showId || 'no-show-id', + }); + setIsFocused(true); // video가 플레이 가능한 경우: 다른 배너에서 비디오 재생 중이면 종료 if (videoPlayerable && currentVideoBannerId && currentVideoBannerId !== spotlightId) { + console.log('[RandomUnit] videoPlayerable=true and different banner, finishing video'); dispatch(finishVideoPreview()); } + // video가 플레이 불가능한 경우: 재생 중인 비디오를 1px로 축소 + if (!videoPlayerable && currentVideoBannerId) { + console.log('[RandomUnit] videoPlayerable=false, shrinking video to 1px', { + videoPlayerable, + currentVideoBannerId, + }); + dispatch(shrinkVideoTo1px()); + } + if (handleItemFocus) { handleItemFocus(); } - }, [handleItemFocus, videoPlayerable, spotlightId, currentVideoBannerId]); + }, [handleItemFocus, videoPlayerable, spotlightId, currentVideoBannerId, dispatch, randomData]); const shelfFocus = useCallback(() => { if (handleShelfFocus) { @@ -225,6 +242,13 @@ export default function RandomUnit({ const onBlur = useCallback(() => { setIsFocused(false); clearTimeout(timerRef.current); + + // 현재 비디오가 재생 중이면 1px로 축소 (주석: HomePanel 스크롤로 처리) + // if (currentVideoBannerId && videoPlayerable) { + // console.log('[RandomUnit] onBlur: shrinking video to 1px'); + // dispatch(shrinkVideoTo1px()); + // } + // dispatch(finishVideoPreview()); }, [isFocused]); diff --git a/com.twin.app.shoptime/src/views/HomePanel/HomePanel.jsx b/com.twin.app.shoptime/src/views/HomePanel/HomePanel.jsx index 24ab3c50..82f2c3bd 100644 --- a/com.twin.app.shoptime/src/views/HomePanel/HomePanel.jsx +++ b/com.twin.app.shoptime/src/views/HomePanel/HomePanel.jsx @@ -26,7 +26,7 @@ import { import { sendLogGNB, sendLogTotalRecommend } from '../../actions/logActions'; import { getSubCategory, getTop20Show } from '../../actions/mainActions'; import { getHomeOnSaleInfo } from '../../actions/onSaleActions'; -import { finishVideoPreview } from '../../actions/playActions'; +import { finishVideoPreview, shrinkVideoTo1px, expandVideoFrom1px } from '../../actions/playActions'; import { getBestSeller } from '../../actions/productActions'; import TBody from '../../components/TBody/TBody'; import TButton, { TYPES } from '../../components/TButton/TButton'; @@ -152,6 +152,7 @@ const HomePanel = ({ isOnTop }) => { }, [sortedHomeLayoutInfo]); const cbChangePageRef = useRef(null); + const prevScrollTopRef = useRef(0); // 이전 scrollTop 추적 const focusedContainerIdRef = usePrevious(focusedContainerId); @@ -410,9 +411,22 @@ const HomePanel = ({ isOnTop }) => { }, []); const _onScroll = (e) => { - if (e.scrollTop !== 0) { - dispatch(finishVideoPreview()); + const currentScrollTop = e.scrollTop; + const prevScrollTop = prevScrollTopRef.current; + + // 스크롤 방향 감지 + if (currentScrollTop > prevScrollTop) { + // 아래로 스크롤: 비디오를 1px로 축소 + console.log('[HomePanel] Scrolling down - shrinking video'); + dispatch(shrinkVideoTo1px()); + } else if (currentScrollTop < prevScrollTop && currentScrollTop > 0) { + // 위로 스크롤 (0이 아닌): 비디오를 원래 크기로 복구 + console.log('[HomePanel] Scrolling up - expanding video'); + dispatch(expandVideoFrom1px()); } + + // 이전 scrollTop 업데이트 + prevScrollTopRef.current = currentScrollTop; }; const _onFocusedContainerId = useCallback( diff --git a/com.twin.app.shoptime/src/views/PlayerPanel/PlayerPanel.jsx b/com.twin.app.shoptime/src/views/PlayerPanel/PlayerPanel.jsx index 5d53c5bb..f3f94894 100644 --- a/com.twin.app.shoptime/src/views/PlayerPanel/PlayerPanel.jsx +++ b/com.twin.app.shoptime/src/views/PlayerPanel/PlayerPanel.jsx @@ -1426,6 +1426,7 @@ const PlayerPanel = ({ isTabActivated, panelInfo, isOnTop, spotlightId, ...props useEffect(() => { if ( panelInfo.modal && + !panelInfo.shouldShrinkTo1px && panelInfo.modalContainerId && (lastPanelAction === 'previewPush' || lastPanelAction === 'previewUpdate') ) { @@ -1457,6 +1458,10 @@ const PlayerPanel = ({ isTabActivated, panelInfo, isOnTop, spotlightId, ...props setModalScale(panelInfo.modalScale); console.error('PlayerPanel modalContainerId node not found', panelInfo.modalContainerId); } + } else if (panelInfo.shouldShrinkTo1px) { + // 축소 상태: 인라인 스타일 제거 (CSS만 적용) + setModalStyle({}); + setModalScale(1); } else if (isOnTop && !panelInfo.modal && videoPlayer.current) { if (videoPlayer.current?.getMediaState()?.paused) { videoPlayer.current.play(); @@ -2166,6 +2171,7 @@ const PlayerPanel = ({ isTabActivated, panelInfo, isOnTop, spotlightId, ...props className={classNames( css.videoContainer, panelInfo.modal && css.modal, + panelInfo.shouldShrinkTo1px && css.shrinkTo1px, // PlayerPanel이 최상단 아니고, 최상단이 DetailPanel(from Player)이면 비디오 보이도록 !isOnTop && isTopPanelDetailFromPlayer && css['background-visible'], // PlayerPanel이 최상단 아니고, 위 조건 아니면 1px로 숨김 diff --git a/com.twin.app.shoptime/src/views/PlayerPanel/PlayerPanel.module.less b/com.twin.app.shoptime/src/views/PlayerPanel/PlayerPanel.module.less index 1e0aef3b..3fd808fb 100644 --- a/com.twin.app.shoptime/src/views/PlayerPanel/PlayerPanel.module.less +++ b/com.twin.app.shoptime/src/views/PlayerPanel/PlayerPanel.module.less @@ -55,6 +55,23 @@ border-radius: 50%; } } + &.shrinkTo1px { + /* 모달 비디오를 1px로 축소 */ + width: 1px; + height: 1px; + left: -1px; + top: -1px; + pointer-events: none; + z-index: 1; + background-color: @videoBackgroundColor; + overflow: visible; + .tabContainer, + .arrow, + .toOpenBtn { + display: none; + } + } + &.modal, &.modal-minimized, &.background {