From 1bf490c46c76855b6fd7c665857dbb95e736336c Mon Sep 17 00:00:00 2001 From: optrader Date: Thu, 13 Nov 2025 20:45:32 +0900 Subject: [PATCH] [251113] fix: ProductAllSection ProductVideo version=1 MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit ๐Ÿ• ์ปค๋ฐ‹ ์‹œ๊ฐ„: 2025. 11. 13. 20:45:31 ๐Ÿ“Š ๋ณ€๊ฒฝ ํ†ต๊ณ„: โ€ข ์ด ํŒŒ์ผ: 8๊ฐœ โ€ข ์ถ”๊ฐ€: +110์ค„ โ€ข ์‚ญ์ œ: -39์ค„ ๐Ÿ“ ์ˆ˜์ •๋œ ํŒŒ์ผ: ~ com.twin.app.shoptime/src/actions/mediaActions.js ~ com.twin.app.shoptime/src/actions/playActions.js ~ com.twin.app.shoptime/src/components/VideoPlayer/MediaPlayer.v2.jsx ~ com.twin.app.shoptime/src/views/DetailPanel/DetailPanel.jsx ~ com.twin.app.shoptime/src/views/DetailPanel/ProductAllSection/ProductAllSection.jsx ~ com.twin.app.shoptime/src/views/DetailPanel/ProductContentSection/ProductVideo/ProductVideo.jsx ~ com.twin.app.shoptime/src/views/HomePanel/HomePanel.jsx ~ com.twin.app.shoptime/src/views/MediaPanel/MediaPanel.jsx ๐Ÿ”ง ์ฃผ์š” ๋ณ€๊ฒฝ ๋‚ด์šฉ: โ€ข ํ•ต์‹ฌ ๋น„์ฆˆ๋‹ˆ์Šค ๋กœ์ง ๊ฐœ์„  โ€ข UI ์ปดํฌ๋„ŒํŠธ ์•„ํ‚คํ…์ฒ˜ ๊ฐœ์„  โ€ข ์ค‘๊ฐ„ ๊ทœ๋ชจ ๊ธฐ๋Šฅ ๊ฐœ์„  โ€ข ๋ชจ๋“ˆ ๊ตฌ์กฐ ๊ฐœ์„  --- .../src/actions/mediaActions.js | 12 ++-- .../src/actions/playActions.js | 20 +++---- .../components/VideoPlayer/MediaPlayer.v2.jsx | 20 +++++++ .../src/views/DetailPanel/DetailPanel.jsx | 9 +++ .../ProductAllSection/ProductAllSection.jsx | 58 ++++++++++++++++--- .../ProductVideo/ProductVideo.jsx | 12 ++-- .../src/views/HomePanel/HomePanel.jsx | 16 ++--- .../src/views/MediaPanel/MediaPanel.jsx | 2 +- 8 files changed, 110 insertions(+), 39 deletions(-) diff --git a/com.twin.app.shoptime/src/actions/mediaActions.js b/com.twin.app.shoptime/src/actions/mediaActions.js index 32d5ca84..abe44e77 100644 --- a/com.twin.app.shoptime/src/actions/mediaActions.js +++ b/com.twin.app.shoptime/src/actions/mediaActions.js @@ -90,18 +90,18 @@ export const finishMediaPreview = () => (dispatch, getState) => { }; /** - * ๊ฐ•์ œ๋กœ modal MediaPanel์„ ์ข…๋ฃŒํ•ฉ๋‹ˆ๋‹ค (์Šคํƒ ์–ด๋””์— ์žˆ๋“ ) + * ๊ฐ•์ œ๋กœ DetailPanel ProductVideo MediaPanel์„ ์ข…๋ฃŒํ•ฉ๋‹ˆ๋‹ค (modal/fullscreen ๋ชจ๋‘) */ export const finishModalMediaForce = () => (dispatch, getState) => { const panels = getState().panels.panels; - const hasModalMediaPanel = panels.some( - (panel) => panel.name === panel_names.MEDIA_PANEL && panel.panelInfo?.modal + const hasProductVideoPanel = panels.some( + (panel) => + panel.name === panel_names.MEDIA_PANEL && + (panel.panelInfo?.modal || panel.panelInfo?.modalContainerId === 'product-video-player') ); - if (hasModalMediaPanel) { - // console.log('[finishModalMediaForce] Force closing modal MediaPanel'); - + if (hasProductVideoPanel) { if (startMediaFocusTimer) { clearTimeout(startMediaFocusTimer); startMediaFocusTimer = null; diff --git a/com.twin.app.shoptime/src/actions/playActions.js b/com.twin.app.shoptime/src/actions/playActions.js index 12c64e23..463074a6 100644 --- a/com.twin.app.shoptime/src/actions/playActions.js +++ b/com.twin.app.shoptime/src/actions/playActions.js @@ -326,10 +326,10 @@ export const shrinkVideoTo1px = () => (dispatch, getState) => { }, }; - console.log('[HomePanel] shrinkVideoTo1px: saving shrinkInfo', { - shrinkInfo: updatedPlayerState.shrinkInfo, - modalStyle: panelInfo.modalStyle, - }); + // console.log('[HomePanel] shrinkVideoTo1px: saving shrinkInfo', { + // shrinkInfo: updatedPlayerState.shrinkInfo, + // modalStyle: panelInfo.modalStyle, + // }); dispatch( updatePanel({ @@ -359,11 +359,11 @@ export const expandVideoFrom1px = () => (dispatch, getState) => { const panelInfo = shrunkModalPlayerPanel.panelInfo; const shrinkInfo = panelInfo.playerState?.shrinkInfo; - console.log('[HomePanel] expandVideoFrom1px: expanding video', { - hasShrinkInfo: !!shrinkInfo, - hasModalStyle: !!shrinkInfo?.modalStyle, - hasModalContainerId: !!shrinkInfo?.modalContainerId, - }); + // console.log('[HomePanel] expandVideoFrom1px: expanding video', { + // hasShrinkInfo: !!shrinkInfo, + // hasModalStyle: !!shrinkInfo?.modalStyle, + // hasModalContainerId: !!shrinkInfo?.modalContainerId, + // }); const updatedPanelInfo = { ...panelInfo, @@ -378,7 +378,7 @@ export const expandVideoFrom1px = () => (dispatch, getState) => { }), }; - console.log('[HomePanel] expandVideoFrom1px: updated panelInfo shouldShrinkTo1px=false, modalStyle restored, skipModalStyleRecalculation=true'); + // console.log('[HomePanel] expandVideoFrom1px: updated panelInfo shouldShrinkTo1px=false, modalStyle restored, skipModalStyleRecalculation=true'); dispatch( updatePanel({ diff --git a/com.twin.app.shoptime/src/components/VideoPlayer/MediaPlayer.v2.jsx b/com.twin.app.shoptime/src/components/VideoPlayer/MediaPlayer.v2.jsx index eac53ba0..7480d61e 100644 --- a/com.twin.app.shoptime/src/components/VideoPlayer/MediaPlayer.v2.jsx +++ b/com.twin.app.shoptime/src/components/VideoPlayer/MediaPlayer.v2.jsx @@ -112,6 +112,7 @@ const MediaPlayerV2 = forwardRef((props, ref) => { onLoadedData, onLoadedMetadata, onDurationChange, + setApiProvider, // Spotlight spotlightId = 'mediaPlayerV2', @@ -470,6 +471,23 @@ const MediaPlayerV2 = forwardRef((props, ref) => { getVideoNode: () => videoRef.current, }), [play, pause, seek, getMediaState, showControls, hideControls, toggleControls, controlsVisible]); + // ========== setApiProvider ํ˜ธ์ถœ ========== + useEffect(() => { + if (setApiProvider && typeof setApiProvider === 'function') { + setApiProvider({ + play, + pause, + seek, + getMediaState, + showControls, + hideControls, + toggleControls, + areControlsVisible: () => controlsVisible, + getVideoNode: () => videoRef.current, + }); + } + }, [setApiProvider, play, pause, seek, getMediaState, showControls, hideControls, toggleControls]); + // ========== Video Props ========== const videoProps = useMemo(() => { const baseProps = { @@ -671,11 +689,13 @@ MediaPlayerV2.propTypes = { style: PropTypes.object, modalClassName: PropTypes.string, modalScale: PropTypes.number, + setApiProvider: PropTypes.func, // ํŒจ๋„ ์ •๋ณด panelInfo: PropTypes.shape({ modal: PropTypes.bool, modalContainerId: PropTypes.string, + modalClassName: PropTypes.string, isPaused: PropTypes.bool, showUrl: PropTypes.string, thumbnailUrl: PropTypes.string, diff --git a/com.twin.app.shoptime/src/views/DetailPanel/DetailPanel.jsx b/com.twin.app.shoptime/src/views/DetailPanel/DetailPanel.jsx index 3909f40b..42bd6c2a 100644 --- a/com.twin.app.shoptime/src/views/DetailPanel/DetailPanel.jsx +++ b/com.twin.app.shoptime/src/views/DetailPanel/DetailPanel.jsx @@ -22,6 +22,7 @@ import { getMainCategoryDetail, getMainYouMayLike, } from '../../actions/mainActions'; +import { finishModalMediaForce } from '../../actions/mediaActions'; import { popPanel, updatePanel, @@ -141,11 +142,19 @@ export default function DetailPanel({ panelInfo, isOnTop, spotlightId }) { Spotlight.focus('spotlightId_backBtn'); }, []); + useEffect(() => { + return () => { + dispatch(finishModalMediaForce()); + }; + }, [dispatch]); + const onClick = useCallback( (isCancelClick) => (ev) => { fp.pipe( () => { dispatch(clearAllToasts()); // BuyOption Toast ํฌํ•จ ๋ชจ๋“  ํ† ์ŠคํŠธ ์ œ๊ฑฐ + dispatch(pauseFullscreenVideo()); // PLAYER_PANEL ๋น„๋””์˜ค ์ค‘์ง€ + dispatch(finishModalMediaForce()); // MEDIA_PANEL(ProductVideo) ๊ฐ•์ œ ์ข…๋ฃŒ dispatch(finishVideoPreview()); dispatch(popPanel(panel_names.DETAIL_PANEL)); }, diff --git a/com.twin.app.shoptime/src/views/DetailPanel/ProductAllSection/ProductAllSection.jsx b/com.twin.app.shoptime/src/views/DetailPanel/ProductAllSection/ProductAllSection.jsx index 63ca9a53..ca4e8058 100644 --- a/com.twin.app.shoptime/src/views/DetailPanel/ProductAllSection/ProductAllSection.jsx +++ b/com.twin.app.shoptime/src/views/DetailPanel/ProductAllSection/ProductAllSection.jsx @@ -27,7 +27,7 @@ import arrowDown import indicatorDefaultImage from '../../../../assets/images/img-thumb-empty-144@3x.png'; // import { pushPanel } from '../../../actions/panelActions'; -import { minimizeModalMedia } from '../../../actions/mediaActions'; +import { minimizeModalMedia, restoreModalMedia } from '../../../actions/mediaActions'; import { pauseFullscreenVideo } from '../../../actions/playActions'; import { resetShowAllReviews } from '../../../actions/productActions'; import { @@ -197,6 +197,9 @@ export default function ProductAllSection({ // ProductAllSection ์ดˆ๊ธฐ ๋กœ๋”ฉ ์‹œ Skeleton ํ‘œ์‹œ๋ฅผ ์œ„ํ•œ ์ƒํƒœ const [isInitialLoading, setIsInitialLoading] = useState(true); + // ์Šคํฌ๋กค ์œ„์น˜์— ๋”ฐ๋ฅธ MediaPanel ์ œ์–ด ์ƒํƒœ + const [shouldMinimizeMedia, setShouldMinimizeMedia] = useState(false); + useEffect(() => { const toggleQRCode = () => { if (isShowQRCode) { @@ -498,7 +501,7 @@ export default function ProductAllSection({ // ProductVideo V1 ์ „์šฉ - MediaPanel minimize ํฌํ•จ const handleScrollToImagesV1 = useCallback(() => { // 1. MediaPanel์„ 1px๋กœ ์ถ•์†Œํ•˜์—ฌ ํฌ์ปค์Šค ์ถฉ๋Œ ๋ฐฉ์ง€ - dispatch(minimizeModalMedia()); + // dispatch(minimizeModalMedia()); // 2. ์Šคํฌ๋กค ์ด๋™ scrollToSection('scroll-marker-after-video'); @@ -657,6 +660,7 @@ export default function ProductAllSection({ [scrollToSection] ); const scrollPositionRef = useRef(0); + const prevScrollPositionRef = useRef(0); // ์ด์ „ ์Šคํฌ๋กค ์œ„์น˜ ์ถ”์  const handleArrowClickAlternative = useCallback(() => { const currentHeight = scrollPositionRef.current; @@ -677,17 +681,40 @@ export default function ProductAllSection({ } }, [documentHeight, scrollTop]); - const handleScroll = useCallback( + // const handleScroll = useCallback( + // (e) => { + // scrollPositionRef.current = e.scrollTop; + // if (documentHeight) { + // const isAtBottom = + // scrollPositionRef.current + 944 >= + // documentHeight + (youMayAlsoLikelRef.current?.scrollHeight || 0); + // if (isAtBottom !== isBottom) { + // setIsBottom(isAtBottom); + // } + // } + // }, + // [documentHeight, isBottom] + // ); + + // ์Šคํฌ๋กค ๋ฉˆ์ถ”์—ˆ์„ ๋•Œ๋งŒ ํ˜ธ์ถœ (์„ฑ๋Šฅ ์ตœ์ ํ™”) + const handleScrollStop = useCallback( (e) => { - scrollPositionRef.current = e.scrollTop; + const currentScrollTop = e.scrollTop; + scrollPositionRef.current = currentScrollTop; + if (documentHeight) { const isAtBottom = - scrollPositionRef.current + 944 >= + currentScrollTop + 944 >= documentHeight + (youMayAlsoLikelRef.current?.scrollHeight || 0); if (isAtBottom !== isBottom) { setIsBottom(isAtBottom); } } + + console.log('๐Ÿ“ [ProductAllSection] ์Šคํฌ๋กค ๋ฉˆ์ถค - ์œ„์น˜:', currentScrollTop); + const shouldMinimize = currentScrollTop > 0; + console.log('๐Ÿ“ [ProductAllSection] setShouldMinimizeMedia ํ˜ธ์ถœ:', shouldMinimize); + setShouldMinimizeMedia(shouldMinimize); // state ์—…๋ฐ์ดํŠธ๋งŒ }, [documentHeight, isBottom] ); @@ -710,6 +737,18 @@ export default function ProductAllSection({ ); }, [hasReviews, hasYouMayAlsoLike]); + // ์Šคํฌ๋กค ์œ„์น˜์— ๋”ฐ๋ฅธ MediaPanel ์ œ์–ด (๋น„๋””์˜ค ์žฌ์ƒ ์ค‘์—๋Š” ์ž๋™ ์ œ์–ด ์•ˆํ•จ - unmount ์‹œ์—๋งŒ ์ •๋ฆฌ) + // useEffect(() => { + // console.log('๐Ÿ“ [ProductAllSection] useEffect ์‹คํ–‰ - shouldMinimizeMedia:', shouldMinimizeMedia); + // if (shouldMinimizeMedia) { + // console.log('๐Ÿ“ [ProductAllSection] MediaPanel minimize ํ˜ธ์ถœ (true)'); + // dispatch(minimizeModalMedia()); + // } else { + // console.log('๐Ÿ“ [ProductAllSection] MediaPanel restore ํ˜ธ์ถœ (false)'); + // dispatch(restoreModalMedia()); + // } + // }, [shouldMinimizeMedia, dispatch]); + //spot๊ด€๋ จ useEffect(() => { if (onReady && isOnRender) { @@ -768,9 +807,11 @@ export default function ProductAllSection({ // }; // }, []); - // ์ปดํฌ๋„ŒํŠธ unmount ์‹œ ๋ชจ๋“  timer cleanup + // ์ปดํฌ๋„ŒํŠธ unmount ์‹œ timer cleanup useEffect(() => { return () => { + console.log('[ProductAllSection] unmount - cleanup ์‹œ์ž‘'); + // QR code timer cleanup if (timerRef.current) { clearTimeout(timerRef.current); @@ -788,7 +829,7 @@ export default function ProductAllSection({ scrollToImagesTimeoutRef.current = null; } - // console.log('[ProductAllSection] All timers cleaned up on unmount'); + console.log('[ProductAllSection] cleanup ์™„๋ฃŒ on unmount'); }; }, []); @@ -1019,7 +1060,8 @@ export default function ProductAllSection({ spotlightId="main-content-scroller" spotlightDisabled={false} spotlightRestrict="none" - onScroll={handleScroll} + // onScroll={handleScroll} + onScrollStop={handleScrollStop} onClick={handleScrollContainerClick} >
diff --git a/com.twin.app.shoptime/src/views/DetailPanel/ProductContentSection/ProductVideo/ProductVideo.jsx b/com.twin.app.shoptime/src/views/DetailPanel/ProductContentSection/ProductVideo/ProductVideo.jsx index 4644d06b..d6609d7a 100644 --- a/com.twin.app.shoptime/src/views/DetailPanel/ProductContentSection/ProductVideo/ProductVideo.jsx +++ b/com.twin.app.shoptime/src/views/DetailPanel/ProductContentSection/ProductVideo/ProductVideo.jsx @@ -114,17 +114,17 @@ export default function ProductVideo({ setFocused(true); console.log('[ProductVideo] Calling restoreModalMedia'); // ProductVideo์— ํฌ์ปค์Šค๊ฐ€ ๋Œ์•„์˜ค๋ฉด ๋น„๋””์˜ค ๋ณต์› - dispatch(restoreModalMedia()); + // dispatch(restoreModalMedia()); } }, [canPlayVideo, dispatch]); const videoContainerOnBlur = useCallback(() => { console.log('[ProductVideo] onBlur called - canPlayVideo:', canPlayVideo); - if (canPlayVideo) { - setFocused(false); - // ํฌ์ปค์Šค๋ฅผ ์žƒ์œผ๋ฉด ๋ชจ๋‹ฌ MediaPanel์„ ์ตœ์†Œํ™”ํ•˜์—ฌ 1px ์ƒํƒœ๋กœ ์œ ์ง€ - dispatch(minimizeModalMedia()); - } + // if (canPlayVideo) { + // setFocused(false); + // // ํฌ์ปค์Šค๋ฅผ ์žƒ์œผ๋ฉด ๋ชจ๋‹ฌ MediaPanel์„ ์ตœ์†Œํ™”ํ•˜์—ฌ 1px ์ƒํƒœ๋กœ ์œ ์ง€ + // dispatch(minimizeModalMedia()); + // } }, [canPlayVideo, dispatch]); // Spotlight Down ํ‚ค ํ•ธ๋“ค๋Ÿฌ - ๋น„๋””์˜ค ๋‹ค์Œ ์ด๋ฏธ์ง€๋กœ ์Šคํฌ๋กค diff --git a/com.twin.app.shoptime/src/views/HomePanel/HomePanel.jsx b/com.twin.app.shoptime/src/views/HomePanel/HomePanel.jsx index 123fdc68..a2268e33 100644 --- a/com.twin.app.shoptime/src/views/HomePanel/HomePanel.jsx +++ b/com.twin.app.shoptime/src/views/HomePanel/HomePanel.jsx @@ -466,7 +466,7 @@ const HomePanel = ({ isOnTop }) => { // shouldShrinkTo1px๊ฐ€ false๊ฐ€ ๋˜๋ฉด interval ์ฆ‰์‹œ ์ •๋ฆฌ if (!playerPanelShouldShrink && expandIntervalRef.current) { - console.log('[HomePanel] shouldShrinkTo1px=false - clearing expand interval'); + // console.log('[HomePanel] shouldShrinkTo1px=false - clearing expand interval'); clearInterval(expandIntervalRef.current); expandIntervalRef.current = null; expandAttemptRef.current = 0; @@ -489,20 +489,20 @@ const HomePanel = ({ isOnTop }) => { // โœ… ์ตœ์ƒ๋‹จ ๋„๋‹ฌ: 1px ๋น„๋””์˜ค ๋ณต๊ตฌ ์‹œ๋„ (shouldShrinkRef.current๋กœ ์ฝ์Œ) if (currentScrollTop <= 1) { if (shouldShrinkRef.current && !expandIntervalRef.current) { - console.log('[HomePanel] At top (scrollTop <= 1) - starting video expansion'); + // console.log('[HomePanel] At top (scrollTop <= 1) - starting video expansion'); expandAttemptRef.current = 0; // Interval ์‹œ์ž‘: 200ms๋งˆ๋‹ค ๋ณต๊ตฌ ์‹œ๋„ expandIntervalRef.current = setInterval(() => { // ์ข…๋ฃŒ ์กฐ๊ฑด: ์ตœ๋Œ€ 3ํšŒ ์‹œ๋„ if (expandAttemptRef.current >= 3) { - console.log('[HomePanel] Max expansion attempts reached (3)'); + // console.log('[HomePanel] Max expansion attempts reached (3)'); clearInterval(expandIntervalRef.current); expandIntervalRef.current = null; return; } - console.log('[HomePanel] Expansion attempt', expandAttemptRef.current + 1); + // console.log('[HomePanel] Expansion attempt', expandAttemptRef.current + 1); dispatch(expandVideoFrom1px()); expandAttemptRef.current++; }, 200); @@ -510,7 +510,7 @@ const HomePanel = ({ isOnTop }) => { } // ์ตœ์ƒ๋‹จ ๋ฒ—์–ด๋‚จ: interval ์ •๋ฆฌ else if (currentScrollTop > 1 && expandIntervalRef.current) { - console.log('[HomePanel] Left top - clearing expand interval'); + // console.log('[HomePanel] Left top - clearing expand interval'); clearInterval(expandIntervalRef.current); expandIntervalRef.current = null; } @@ -518,7 +518,7 @@ const HomePanel = ({ isOnTop }) => { // ๊ธฐ์กด ๋กœ์ง: ์•„๋ž˜๋กœ ์Šคํฌ๋กค if (currentScrollTop > prevScrollTop) { // ์•„๋ž˜๋กœ ์Šคํฌ๋กค: ๋น„๋””์˜ค๋ฅผ 1px๋กœ ์ถ•์†Œ - console.log('[HomePanel] Scrolling down - shrinking video'); + // console.log('[HomePanel] Scrolling down - shrinking video'); dispatch(shrinkVideoTo1px()); // ๊ธฐ์กด ํƒ€์ด๋จธ ์ทจ์†Œ if (scrollExpandTimerRef.current) { @@ -529,14 +529,14 @@ const HomePanel = ({ isOnTop }) => { // ๊ธฐ์กด ๋กœ์ง: ์œ„๋กœ ์Šคํฌ๋กค (0์ด ์•„๋‹Œ) else if (currentScrollTop < prevScrollTop && currentScrollTop > 1) { // ์œ„๋กœ ์Šคํฌ๋กค (์ตœ์ƒ๋‹จ ์•„๋‹˜): 1์ดˆ ํ›„ ๋ณต๊ตฌ - console.log('[HomePanel] Scrolling up - will expand after 1s'); + // console.log('[HomePanel] Scrolling up - will expand after 1s'); // ๊ธฐ์กด ํƒ€์ด๋จธ ์ทจ์†Œ if (scrollExpandTimerRef.current) { clearTimeout(scrollExpandTimerRef.current); } // 1์ดˆ ํ›„ ์ž๋™์œผ๋กœ ํฌ๊ธฐ ์กฐ์ • scrollExpandTimerRef.current = setTimeout(() => { - console.log('[HomePanel] 1s passed - auto expanding video'); + // console.log('[HomePanel] 1s passed - auto expanding video'); dispatch(expandVideoFrom1px()); scrollExpandTimerRef.current = null; }, 1000); diff --git a/com.twin.app.shoptime/src/views/MediaPanel/MediaPanel.jsx b/com.twin.app.shoptime/src/views/MediaPanel/MediaPanel.jsx index a6cf7421..17ce6b55 100644 --- a/com.twin.app.shoptime/src/views/MediaPanel/MediaPanel.jsx +++ b/com.twin.app.shoptime/src/views/MediaPanel/MediaPanel.jsx @@ -507,7 +507,7 @@ const MediaPanel = ({ isTabActivated, panelInfo, isOnTop, spotlightId, ...props loop={panelInfo.continuousPlay || false} style={panelInfo.modal ? modalStyle : {}} modalScale={panelInfo.modal ? modalScale : 1} - modalClassName={panelInfo.modal && panelInfo.modalClassName} + modalClassName={panelInfo.modal ? panelInfo.modalClassName : undefined} onError={mediainfoHandler} onTimeUpdate={mediainfoHandler} onLoadedData={mediainfoHandler}