[251026] Focus Recovery Success
위로 스크롤 시 비디오 자동 복구 기능 완성 - HomePanel의 _onScroll에서 expandVideoFrom1px() 액션 사용 - 1초 딜레이 후 축소된 비디오 자동 복구 - skipModalStyleRecalculation 플래그로 올바른 복구 로직 실행 📊 변경 통계: • 7개 파일 변경 • +3027줄 추가, -2240줄 삭제 🎯 주요 변경: • HomePanel.jsx: 스크롤 복구 타이밍 및 액션 개선 • playActions.js: 비디오 확장 액션 정의 • PlayerPanel.jsx: 복구 로직 개선 • 불필요한 파일 삭제 (PlayerPanel.new.jsx, .module.less)
This commit is contained in:
2924
com.twin.app.shoptime/2025-10-26-focus-1px-to-recovery.txt
Normal file
2924
com.twin.app.shoptime/2025-10-26-focus-1px-to-recovery.txt
Normal file
File diff suppressed because it is too large
Load Diff
@@ -251,7 +251,6 @@ export const shrinkVideoTo1px = () => (dispatch, getState) => {
|
||||
|
||||
if (modalPlayerPanel) {
|
||||
const panelInfo = modalPlayerPanel.panelInfo;
|
||||
console.log('[shrinkVideoTo1px] Shrinking modal video to 1px');
|
||||
|
||||
// 축소 전 배너 정보를 playerState에 저장
|
||||
const updatedPlayerState = {
|
||||
@@ -262,6 +261,9 @@ export const shrinkVideoTo1px = () => (dispatch, getState) => {
|
||||
modalClassName: panelInfo.modalClassName,
|
||||
modalStyle: panelInfo.modalStyle,
|
||||
modalScale: panelInfo.modalScale,
|
||||
// top, left는 따로 저장 (복구 시 재계산하지 않기 위함)
|
||||
top: panelInfo.modalStyle?.top,
|
||||
left: panelInfo.modalStyle?.left,
|
||||
currentBannerId: panelInfo.playerState?.currentBannerId,
|
||||
patnrId: panelInfo.patnrId,
|
||||
showId: panelInfo.showId,
|
||||
@@ -270,6 +272,11 @@ export const shrinkVideoTo1px = () => (dispatch, getState) => {
|
||||
},
|
||||
};
|
||||
|
||||
console.log('[HomePanel] shrinkVideoTo1px: saving shrinkInfo', {
|
||||
shrinkInfo: updatedPlayerState.shrinkInfo,
|
||||
modalStyle: panelInfo.modalStyle,
|
||||
});
|
||||
|
||||
dispatch(
|
||||
updatePanel({
|
||||
name: panel_names.PLAYER_PANEL,
|
||||
@@ -280,6 +287,8 @@ export const shrinkVideoTo1px = () => (dispatch, getState) => {
|
||||
},
|
||||
})
|
||||
);
|
||||
} else {
|
||||
console.log('[HomePanel] shrinkVideoTo1px: No modal PlayerPanel found');
|
||||
}
|
||||
};
|
||||
|
||||
@@ -296,24 +305,35 @@ export const expandVideoFrom1px = () => (dispatch, getState) => {
|
||||
const panelInfo = shrunkModalPlayerPanel.panelInfo;
|
||||
const shrinkInfo = panelInfo.playerState?.shrinkInfo;
|
||||
|
||||
console.log('[expandVideoFrom1px] Expanding modal video from 1px', shrinkInfo);
|
||||
console.log('[HomePanel] expandVideoFrom1px: expanding video', {
|
||||
hasShrinkInfo: !!shrinkInfo,
|
||||
hasModalStyle: !!shrinkInfo?.modalStyle,
|
||||
hasModalContainerId: !!shrinkInfo?.modalContainerId,
|
||||
});
|
||||
|
||||
const updatedPanelInfo = {
|
||||
...panelInfo,
|
||||
shouldShrinkTo1px: false, // 축소 플래그 해제
|
||||
skipModalStyleRecalculation: true, // ← 복구 과정에서 DOM 재계산 스킵
|
||||
// 저장된 정보로 복구
|
||||
...(shrinkInfo && {
|
||||
modalContainerId: shrinkInfo.modalContainerId,
|
||||
modalClassName: shrinkInfo.modalClassName,
|
||||
modalStyle: shrinkInfo.modalStyle,
|
||||
modalScale: shrinkInfo.modalScale,
|
||||
}),
|
||||
};
|
||||
|
||||
console.log('[HomePanel] expandVideoFrom1px: updated panelInfo shouldShrinkTo1px=false, modalStyle restored, skipModalStyleRecalculation=true');
|
||||
|
||||
dispatch(
|
||||
updatePanel({
|
||||
name: panel_names.PLAYER_PANEL,
|
||||
panelInfo: {
|
||||
...panelInfo,
|
||||
shouldShrinkTo1px: false, // 축소 플래그 해제
|
||||
// 저장된 정보로 복구
|
||||
...(shrinkInfo && {
|
||||
modalContainerId: shrinkInfo.modalContainerId,
|
||||
modalClassName: shrinkInfo.modalClassName,
|
||||
modalStyle: shrinkInfo.modalStyle,
|
||||
modalScale: shrinkInfo.modalScale,
|
||||
}),
|
||||
},
|
||||
panelInfo: updatedPanelInfo,
|
||||
})
|
||||
);
|
||||
} else {
|
||||
console.log('[HomePanel] expandVideoFrom1px: No shrunk modal PlayerPanel found');
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
@@ -27,6 +27,7 @@ import { sendLogGNB, sendLogTotalRecommend } from '../../actions/logActions';
|
||||
import { getSubCategory, getTop20Show } from '../../actions/mainActions';
|
||||
import { getHomeOnSaleInfo } from '../../actions/onSaleActions';
|
||||
import { finishVideoPreview, shrinkVideoTo1px, expandVideoFrom1px } from '../../actions/playActions';
|
||||
import { updatePanel } from '../../actions/panelActions';
|
||||
import { getBestSeller } from '../../actions/productActions';
|
||||
import TBody from '../../components/TBody/TBody';
|
||||
import TButton, { TYPES } from '../../components/TButton/TButton';
|
||||
@@ -153,6 +154,8 @@ const HomePanel = ({ isOnTop }) => {
|
||||
|
||||
const cbChangePageRef = useRef(null);
|
||||
const prevScrollTopRef = useRef(0); // 이전 scrollTop 추적
|
||||
const isScrollingUpRef = useRef(false); // 스크롤 위로 감지
|
||||
const scrollExpandTimerRef = useRef(null); // 스크롤 복구 타이머
|
||||
|
||||
const focusedContainerIdRef = usePrevious(focusedContainerId);
|
||||
|
||||
@@ -419,10 +422,24 @@ const HomePanel = ({ isOnTop }) => {
|
||||
// 아래로 스크롤: 비디오를 1px로 축소
|
||||
console.log('[HomePanel] Scrolling down - shrinking video');
|
||||
dispatch(shrinkVideoTo1px());
|
||||
// 기존 타이머 취소
|
||||
if (scrollExpandTimerRef.current) {
|
||||
clearTimeout(scrollExpandTimerRef.current);
|
||||
scrollExpandTimerRef.current = null;
|
||||
}
|
||||
} else if (currentScrollTop < prevScrollTop && currentScrollTop > 0) {
|
||||
// 위로 스크롤 (0이 아닌): 비디오를 원래 크기로 복구
|
||||
console.log('[HomePanel] Scrolling up - expanding video');
|
||||
dispatch(expandVideoFrom1px());
|
||||
// 위로 스크롤 (0이 아닌): 1.5초 후 복구
|
||||
console.log('[HomePanel] Scrolling up - will expand after 1.5s');
|
||||
// 기존 타이머 취소
|
||||
if (scrollExpandTimerRef.current) {
|
||||
clearTimeout(scrollExpandTimerRef.current);
|
||||
}
|
||||
// 1초 후 자동으로 크기 조정
|
||||
scrollExpandTimerRef.current = setTimeout(() => {
|
||||
console.log('[HomePanel] 1s passed - auto expanding video');
|
||||
dispatch(expandVideoFrom1px());
|
||||
scrollExpandTimerRef.current = null;
|
||||
}, 1000);
|
||||
}
|
||||
|
||||
// 이전 scrollTop 업데이트
|
||||
|
||||
@@ -68,7 +68,6 @@ import MediaPanel from '../MediaPanel/MediaPanel';
|
||||
import MyPagePanel from '../MyPagePanel/MyPagePanel';
|
||||
import OnSalePanel from '../OnSalePanel/OnSalePanel';
|
||||
import PlayerPanel from '../PlayerPanel/PlayerPanel';
|
||||
import PlayerPanelNew from '../PlayerPanel/PlayerPanel.new';
|
||||
import SearchPanel from '../SearchPanel/SearchPanel.new.v2';
|
||||
/* VUI_DISABLE_START - VoicePanel import 비활성화 */
|
||||
// import VoicePanel from '../VoicePanel/VoicePanel';
|
||||
@@ -104,7 +103,6 @@ const panelMap = {
|
||||
[Config.panel_names.DEBUG_PANEL]: DebugPanel,
|
||||
[Config.panel_names.DETAIL_PANEL]: DetailPanel,
|
||||
[Config.panel_names.PLAYER_PANEL]: PlayerPanel,
|
||||
[Config.panel_names.PLAYER_PANEL_NEW]: PlayerPanelNew,
|
||||
[Config.panel_names.MEDIA_PANEL]: MediaPanel,
|
||||
[Config.panel_names.CHECKOUT_PANEL]: CheckOutPanel,
|
||||
[Config.panel_names.WELCOME_EVENT_PANEL]: WelcomeEventPanel,
|
||||
|
||||
@@ -1424,12 +1424,46 @@ const PlayerPanel = ({ isTabActivated, panelInfo, isOnTop, spotlightId, ...props
|
||||
]);
|
||||
|
||||
useEffect(() => {
|
||||
// 복구 직후: skipModalStyleRecalculation이 true면 저장된 modalStyle 적용
|
||||
if (panelInfo.skipModalStyleRecalculation && !panelInfo.shouldShrinkTo1px) {
|
||||
console.log('[PlayerPanel] Condition 2.5: Using saved modalStyle from expand');
|
||||
const shrinkInfo = panelInfo.playerState?.shrinkInfo;
|
||||
|
||||
// 저장된 modalStyle 사용 (top, left 포함)
|
||||
if (shrinkInfo?.modalStyle) {
|
||||
setModalStyle(shrinkInfo.modalStyle);
|
||||
setModalScale(panelInfo.modalScale || shrinkInfo.modalScale);
|
||||
} else {
|
||||
setModalStyle(panelInfo.modalStyle);
|
||||
setModalScale(panelInfo.modalScale);
|
||||
}
|
||||
|
||||
// DOM 렌더링 후 플래그 제거
|
||||
if (typeof window !== 'undefined') {
|
||||
window.requestAnimationFrame(() => {
|
||||
window.requestAnimationFrame(() => {
|
||||
console.log('[PlayerPanel] Condition 2.5: Removing skipFlag after DOM render');
|
||||
dispatch(
|
||||
updatePanel({
|
||||
name: panel_names.PLAYER_PANEL,
|
||||
panelInfo: {
|
||||
...panelInfo,
|
||||
skipModalStyleRecalculation: false,
|
||||
},
|
||||
})
|
||||
);
|
||||
});
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
if (
|
||||
panelInfo.modal &&
|
||||
!panelInfo.shouldShrinkTo1px &&
|
||||
panelInfo.modalContainerId &&
|
||||
(lastPanelAction === 'previewPush' || lastPanelAction === 'previewUpdate')
|
||||
) {
|
||||
console.log('[PlayerPanel] Condition 1: Calculating modalStyle from DOM', { lastPanelAction });
|
||||
const node = document.querySelector(`[data-spotlight-id="${panelInfo.modalContainerId}"]`);
|
||||
if (node) {
|
||||
const { width, height, top, left } = node.getBoundingClientRect();
|
||||
@@ -1454,15 +1488,17 @@ const PlayerPanel = ({ isTabActivated, panelInfo, isOnTop, spotlightId, ...props
|
||||
})
|
||||
);
|
||||
} else {
|
||||
console.log('[PlayerPanel] Condition 1: Node not found, using saved modalStyle');
|
||||
setModalStyle(panelInfo.modalStyle);
|
||||
setModalScale(panelInfo.modalScale);
|
||||
console.error('PlayerPanel modalContainerId node not found', panelInfo.modalContainerId);
|
||||
}
|
||||
} else if (panelInfo.shouldShrinkTo1px) {
|
||||
console.log('[PlayerPanel] Condition 2: Shrinking - clearing modalStyle');
|
||||
// 축소 상태: 인라인 스타일 제거 (CSS만 적용)
|
||||
setModalStyle({});
|
||||
setModalScale(1);
|
||||
} else if (isOnTop && !panelInfo.modal && videoPlayer.current) {
|
||||
console.log('[PlayerPanel] Condition 3: Playing fullscreen video');
|
||||
if (videoPlayer.current?.getMediaState()?.paused) {
|
||||
videoPlayer.current.play();
|
||||
}
|
||||
@@ -1471,7 +1507,7 @@ const PlayerPanel = ({ isTabActivated, panelInfo, isOnTop, spotlightId, ...props
|
||||
videoPlayer.current.showControls();
|
||||
}
|
||||
}
|
||||
}, [panelInfo, isOnTop]);
|
||||
}, [panelInfo, isOnTop, dispatch]);
|
||||
|
||||
const smallestOffsetHourIndex = useMemo(() => {
|
||||
if (shopNowInfo) {
|
||||
@@ -2164,20 +2200,22 @@ const PlayerPanel = ({ isTabActivated, panelInfo, isOnTop, spotlightId, ...props
|
||||
};
|
||||
}, [createLogParams, dispatch, showDetailInfo]);
|
||||
|
||||
const containerClassName = classNames(
|
||||
css.videoContainer,
|
||||
panelInfo.modal && css.modal,
|
||||
panelInfo.shouldShrinkTo1px && css.shrinkTo1px,
|
||||
// PlayerPanel이 최상단 아니고, 최상단이 DetailPanel(from Player)이면 비디오 보이도록
|
||||
!isOnTop && isTopPanelDetailFromPlayer && css['background-visible'],
|
||||
// PlayerPanel이 최상단 아니고, 위 조건 아니면 1px로 숨김
|
||||
!isOnTop && !isTopPanelDetailFromPlayer && css.background,
|
||||
!captionEnable && css.hideSubtitle
|
||||
);
|
||||
|
||||
return (
|
||||
<TPanel
|
||||
isTabActivated={false}
|
||||
{...props}
|
||||
className={classNames(
|
||||
css.videoContainer,
|
||||
panelInfo.modal && css.modal,
|
||||
panelInfo.shouldShrinkTo1px && css.shrinkTo1px,
|
||||
// PlayerPanel이 최상단 아니고, 최상단이 DetailPanel(from Player)이면 비디오 보이도록
|
||||
!isOnTop && isTopPanelDetailFromPlayer && css['background-visible'],
|
||||
// PlayerPanel이 최상단 아니고, 위 조건 아니면 1px로 숨김
|
||||
!isOnTop && !isTopPanelDetailFromPlayer && css.background,
|
||||
!captionEnable && css.hideSubtitle
|
||||
)}
|
||||
className={containerClassName}
|
||||
handleCancel={onClickBack}
|
||||
spotlightId={spotlightId}
|
||||
>
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
@@ -1,79 +0,0 @@
|
||||
@import "../../style/CommonStyle.module.less";
|
||||
@import "../../style/utils.module.less";
|
||||
|
||||
@videoBackgroundColor: black;
|
||||
.videoContainer {
|
||||
position: absolute;
|
||||
background-color: @videoBackgroundColor;
|
||||
z-index: 21;
|
||||
|
||||
.playButton {
|
||||
.size(@w: 60px, @h: 60px);
|
||||
background-image: url("../../../assets/images/btn/btn-video-play-nor@3x.png");
|
||||
background-size: cover;
|
||||
|
||||
&:focus {
|
||||
.size(@w: 60px, @h: 60px);
|
||||
border-radius: 50%;
|
||||
background-color: #c70850;
|
||||
opacity: 0.5;
|
||||
}
|
||||
}
|
||||
|
||||
.pauseButton {
|
||||
.size(@w: 60px, @h: 60px);
|
||||
background-image: url("../../../assets/images/btn/btn-voc-pause-nor@3x.png");
|
||||
background-size: cover;
|
||||
|
||||
&:focus {
|
||||
.size(@w: 60px, @h: 60px);
|
||||
border-radius: 50%;
|
||||
background-color: #c70850;
|
||||
opacity: 0.5;
|
||||
}
|
||||
}
|
||||
.toOpenBtn {
|
||||
.size(@w: 147px, @h: 243px);
|
||||
min-width: 60px !important;
|
||||
margin: 192px 0 136px 512px;
|
||||
text-align: center;
|
||||
background-image: url(../../../assets/images/btn/btn-toopen-foc.svg);
|
||||
}
|
||||
|
||||
.videoReduce {
|
||||
.size(@w:78px, @h:78px);
|
||||
background: url("../../../assets/images/btn/btn-video-min-nor@3x.png")
|
||||
no-repeat center center/60px 60px;
|
||||
position: absolute;
|
||||
right: 60px;
|
||||
bottom: 150px;
|
||||
z-index: 3;
|
||||
|
||||
&:focus {
|
||||
.size(@w:78px, @h:78px);
|
||||
background-color: rgba(199, 8, 80, 0.5);
|
||||
border-radius: 50%;
|
||||
}
|
||||
}
|
||||
&.modal,
|
||||
&.background {
|
||||
width: 1px;
|
||||
height: 1px;
|
||||
left: -1px;
|
||||
top: -1px;
|
||||
pointer-events: none;
|
||||
z-index: 1;
|
||||
background-color: @videoBackgroundColor;
|
||||
overflow: visible;
|
||||
.tabContainer,
|
||||
.arrow,
|
||||
.toOpenBtn {
|
||||
display: none;
|
||||
}
|
||||
}
|
||||
&.hideSubtitle {
|
||||
video::cue {
|
||||
visibility: hidden;
|
||||
}
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user