From 22550bdb39799621163d2bab196c47a83627f6fe Mon Sep 17 00:00:00 2001 From: optrader Date: Tue, 11 Nov 2025 14:07:14 +0900 Subject: [PATCH] [251111] feat: views - MediaPlayer.jsx, ProductAllSection.jsx, Product... MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit ๐Ÿ• ์ปค๋ฐ‹ ์‹œ๊ฐ„: 2025. 11. 11. 14:07:12 ๐Ÿ“Š ๋ณ€๊ฒฝ ํ†ต๊ณ„: โ€ข ์ด ํŒŒ์ผ: 3๊ฐœ โ€ข ์ถ”๊ฐ€: +211์ค„ โ€ข ์‚ญ์ œ: -37์ค„ ๐Ÿ“ ์ˆ˜์ •๋œ ํŒŒ์ผ: ~ com.twin.app.shoptime/src/components/VideoPlayer/MediaPlayer.jsx ~ com.twin.app.shoptime/src/views/DetailPanel/ProductAllSection/ProductAllSection.jsx ~ com.twin.app.shoptime/src/views/DetailPanel/ProductContentSection/ProductVideo/ProductVideo.v2.jsx ๐Ÿ”ง ์ฃผ์š” ๋ณ€๊ฒฝ ๋‚ด์šฉ: โ€ข UI ์ปดํฌ๋„ŒํŠธ ์•„ํ‚คํ…์ฒ˜ ๊ฐœ์„  โ€ข ๋Œ€๊ทœ๋ชจ ๊ธฐ๋Šฅ ๊ฐœ๋ฐœ --- .../components/VideoPlayer/MediaPlayer.jsx | 2 +- .../ProductAllSection/ProductAllSection.jsx | 37 +++- .../ProductVideo/ProductVideo.v2.jsx | 209 +++++++++++++++--- 3 files changed, 211 insertions(+), 37 deletions(-) diff --git a/com.twin.app.shoptime/src/components/VideoPlayer/MediaPlayer.jsx b/com.twin.app.shoptime/src/components/VideoPlayer/MediaPlayer.jsx index 70d691bc..fff746a0 100644 --- a/com.twin.app.shoptime/src/components/VideoPlayer/MediaPlayer.jsx +++ b/com.twin.app.shoptime/src/components/VideoPlayer/MediaPlayer.jsx @@ -65,7 +65,7 @@ const isEnter = is('enter'); const isLeft = is('left'); const isRight = is('right'); -const jumpBackKeyCode = 37; +const jumpBackKeyCode = 3 const jumpForwardKeyCode = 39; const controlsHandleAboveSelectionKeys = [13, 16777221, jumpBackKeyCode, jumpForwardKeyCode]; const getControlsHandleAboveHoldConfig = ({ frequency, time }) => ({ 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 39af927f..812b75fd 100644 --- a/com.twin.app.shoptime/src/views/DetailPanel/ProductAllSection/ProductAllSection.jsx +++ b/com.twin.app.shoptime/src/views/DetailPanel/ProductAllSection/ProductAllSection.jsx @@ -71,7 +71,7 @@ import ProductDescription from '../ProductContentSection/ProductDescription/ProductDescription'; import ProductDetail from '../ProductContentSection/ProductDetail/ProductDetail.new'; -import { ProductVideoV2 } from '../ProductContentSection/ProductVideo'; +import { ProductVideoV2 } from '../ProductContentSection/ProductVideo/ProductVideo.v2.jsx'; import ProductVideo from '../ProductContentSection/ProductVideo/ProductVideo'; import UserReviews from '../ProductContentSection/UserReviews/UserReviews'; // import ViewAllReviewsButton from '../ProductContentSection/UserReviews/ViewAllReviewsButton'; @@ -405,6 +405,39 @@ export default function ProductAllSection({ dispatch(clearAllToasts()) },[dispatch]) + // ์Šคํฌ๋กค ์ปจํ…Œ์ด๋„ˆ์˜ ํด๋ฆญ ์ด๋ฒคํŠธ ์ถ”์ ์šฉ ๋กœ๊น… + const handleScrollContainerClick = useCallback((e) => { + console.log('๐Ÿ“ฑ [ProductAllSection] TScrollerDetail onClick ๊ฐ์ง€๋จ', { + eventType: e.type, + target: e.target?.className, + currentTarget: e.currentTarget?.className, + bubbles: e.bubbles, + defaultPrevented: e.defaultPrevented, + timestamp: new Date().getTime(), + eventPath: e.composedPath?.().slice(0, 5).map(el => ({ + tag: el.tagName, + className: el.className, + id: el.id + })) + }); + }, []); + + // ContentContainer ๋ ˆ๋ฒจ ํด๋ฆญ ์ด๋ฒคํŠธ ์ถ”์  + const handleContentContainerClick = useCallback((e) => { + console.log('๐ŸŽฏ [ProductAllSection] ContentContainer onClick ๊ฐ์ง€๋จ', { + eventType: e.type, + target: e.target?.className, + currentTarget: e.currentTarget?.className, + bubbles: e.bubbles, + defaultPrevented: e.defaultPrevented, + eventPath: e.composedPath?.().slice(0, 8).map(el => ({ + tag: el.tagName, + className: el.className, + id: el.id + })) + }); + }, []); + // ADD TO CART ๋ฒ„ํŠผ ํด๋ฆญ ํ•ธ๋“ค๋Ÿฌ const handleAddToCartClick = useCallback(() => { // console.log('[AddToCart] Add To Cart button clicked'); @@ -966,6 +999,7 @@ export default function ProductAllSection({
{/* */} diff --git a/com.twin.app.shoptime/src/views/DetailPanel/ProductContentSection/ProductVideo/ProductVideo.v2.jsx b/com.twin.app.shoptime/src/views/DetailPanel/ProductContentSection/ProductVideo/ProductVideo.v2.jsx index 34f153e2..0eccfe70 100644 --- a/com.twin.app.shoptime/src/views/DetailPanel/ProductContentSection/ProductVideo/ProductVideo.v2.jsx +++ b/com.twin.app.shoptime/src/views/DetailPanel/ProductContentSection/ProductVideo/ProductVideo.v2.jsx @@ -49,7 +49,7 @@ const YOUTUBECONFIG = { }, }; -export default function ProductVideoV2({ +export function ProductVideoV2({ productInfo, videoUrl, thumbnailUrl, @@ -141,10 +141,14 @@ export default function ProductVideoV2({ }, [canPlayVideo, isPlaying]); // ์ธ๋„ค์ผ ํด๋ฆญ ํ•ธ๋“ค๋Ÿฌ - ๋น„๋””์˜ค ์žฌ์ƒ ์‹œ์ž‘ + Redux dispatch + MediaPlayer ๋ฉ”์„œ๋“œ ํ˜ธ์ถœ - const handleThumbnailClick = useCallback(() => { + const handleThumbnailClick = useCallback((e) => { console.log('๐ŸŽฌ [handleThumbnailClick] ์ธ๋„ค์ผ ํด๋ฆญ๋จ', { canPlayVideo, isPlaying, + eventType: e?.type, + target: e?.target?.className, + bubbles: e?.bubbles, + timestamp: new Date().getTime(), }); if (canPlayVideo && !isPlaying) { // console.log('[BgVideo] ProductVideoV2 - Starting video playback'); @@ -276,6 +280,9 @@ export default function ProductVideoV2({ eventType: e.type, target: e.target?.className, currentTarget: e.currentTarget?.className, + bubbles: e.bubbles, + cancelable: e.cancelable, + composed: e.composed, }); // ๋น„๋””์˜ค ์žฌ์ƒ ์ค‘์ด๊ณ  ์ „์ฒดํ™”๋ฉด์ด ์•„๋‹ ๋•Œ๋งŒ ์ž‘๋™ @@ -300,24 +307,14 @@ export default function ProductVideoV2({ // ๋งˆ์šฐ์Šค ๋‹ค์šด (ํด๋ฆญ) ์ด๋ฒคํŠธ - capture phase์—์„œ ์ฒ˜๋ฆฌ const handleVideoPlayerMouseDown = useCallback( (e) => { + // โš ๏ธ ์ด ํ•จ์ˆ˜๋Š” ์‚ฌ์šฉ๋˜์ง€ ์•Š์œผ๋ฏ€๋กœ ์ œ๊ฑฐ ์˜ˆ์ • + // videoPlayerWrapper์˜ onMouseDownCapture์—์„œ ์ฒ˜๋ฆฌ๋จ console.log('๐ŸŽฌ [ProductVideoV2] handleVideoPlayerMouseDown ์‹คํ–‰๋จ', { isPlaying, isFullscreen, eventType: e.type, target: e.target?.className, }); - - // ๋น„๋””์˜ค ์žฌ์ƒ ์ค‘์ด๊ณ  ์ „์ฒดํ™”๋ฉด์ด ์•„๋‹ ๋•Œ๋งŒ ์ž‘๋™ - if (!isPlaying || isFullscreen) { - console.log('๐ŸŽฌ [ProductVideoV2] MouseDown ์กฐ๊ฑด ๋ถˆ๋งŒ์กฑ'); - return; - } - - // capture phase์—์„œ ์ด๋ฒคํŠธ ์ฒ˜๋ฆฌ - e.preventDefault(); - e.stopPropagation(); - - console.log('๐ŸŽฌ [ProductVideoV2] โœ… MouseDown detected at capture phase'); }, [isPlaying, isFullscreen] ); @@ -349,6 +346,34 @@ export default function ProductVideoV2({ } }, [isPlaying, isFullscreen, handleFullscreenKeyDown]); + // ์ „์—ญ ํด๋ฆญ ์ด๋ฒคํŠธ ๊ฐ์‹œ (debugging์šฉ - ๋ชจ๋“  ํด๋ฆญ์ด document์— ๋„๋‹ฌํ•˜๋Š”์ง€ ํ™•์ธ) + useEffect(() => { + const handleDocumentClick = (e) => { + // ProductVideoV2 ๊ด€๋ จ ์š”์†Œ์ธ ๊ฒฝ์šฐ๋งŒ ๋กœ๊น… + const isVideoElement = e.target?.closest('[class*="videoContainer"]') || + e.target?.closest('[class*="videoPlayer"]') || + e.target?.closest('[class*="videoThumbnail"]'); + + if (isVideoElement) { + console.log('๐Ÿ“„ [Document Level] ์ „์—ญ ํด๋ฆญ ๊ฐ์ง€๋จ', { + eventPhase: e.eventPhase, + bubbles: e.bubbles, + target: e.target?.className, + eventPath: e.composedPath?.().slice(0, 6).map(el => ({ + tag: el.tagName, + className: el.className, + id: el.id + })) + }); + } + }; + + document.addEventListener('click', handleDocumentClick, true); + return () => { + document.removeEventListener('click', handleDocumentClick, true); + }; + }, []); + // autoPlay ๊ธฐ๋Šฅ: ์ปดํฌ๋„ŒํŠธ ๋งˆ์šดํŠธ ํ›„ 500ms ํ›„ ์ž๋™ ์žฌ์ƒ useEffect(() => { if (autoPlay && canPlayVideo && !isPlaying) { @@ -397,6 +422,101 @@ export default function ProductVideoV2({ }; }, [dispatch, isPlaying]); + // VideoContainer์˜ ๋ชจ๋“  ํด๋ฆญ ๊ฐ์‹œ (Hooks๋Š” early return ์ „์— ์ •์˜๋˜์–ด์•ผ ํ•จ) + const handleVideoContainerClick = useCallback( + (e) => { + console.log('๐ŸŽฅ [ProductVideoV2] videoContainer onClick ๊ฐ์ง€๋จ', { + isPlaying, + isFullscreen, + eventType: e.type, + target: e.target?.className, + currentTarget: e.currentTarget?.className, + bubbles: e.bubbles, + defaultPrevented: e.defaultPrevented, + timestamp: new Date().getTime(), + }); + + if (isPlaying && !isFullscreen) { + console.log('๐ŸŽฅ [ProductVideoV2] videoContainer ํด๋ฆญ โ†’ ์ง์ ‘ ์ „์ฒดํ™”๋ฉด ํ† ๊ธ€ ์‹คํ–‰', { + direct: true, + }); + e.preventDefault?.(); + e.stopPropagation?.(); + toggleFullscreen(); + } + }, + [isPlaying, isFullscreen, toggleFullscreen] + ); + + const containerProps = useMemo(() => { + const baseProps = { + onClick: handleVideoContainerClick, // ๋ชจ๋“  ์ƒํƒœ์—์„œ ๋™์ผํ•˜๊ฒŒ ์ ์šฉ + }; + + if (isFullscreen) { + return { + ...baseProps, + spotlightRestrict: 'self-only', // ํฌ์ปค์Šค๊ฐ€ ๋ฐ–์œผ๋กœ ๋‚˜๊ฐ€์ง€ ์•Š๋„๋ก + spotlightId: 'product-video-v2-fullscreen', + onSpotlightDown: handleSpotlightDown, // ์ „์ฒดํ™”๋ฉด์—์„œ๋„ Down ํ‚ค ๋™์ž‘ + }; + } else if (isPlaying) { + return { + ...baseProps, + spotlightId: 'product-video-v2-playing', + onKeyDown: handleContainerKeyDown, // ์ผ๋ฐ˜ ๋ชจ๋“œ: ์ปจํ…Œ์ด๋„ˆ์—์„œ ์ง์ ‘ ์ฒ˜๋ฆฌ + onSpotlightDown: handleSpotlightDown, // ์ผ๋ฐ˜ ์žฌ์ƒ์—์„œ๋„ Down ํ‚ค ๋™์ž‘ + }; + } else { + return baseProps; + } + }, [isFullscreen, isPlaying, handleVideoContainerClick, handleSpotlightDown, handleContainerKeyDown]); + + // โš ๏ธ ๊ฐ„๋‹จํ•œ ํ•ด๊ฒฐ์ฑ…: ๋น„๋””์˜ค ์˜์—ญ ํด๋ฆญ ์‹œ ์ง์ ‘ ๋™์ž‘ ์‹คํ–‰ + const handleContainerClickFallback = useCallback((e) => { + const isThumbnailArea = e.target?.closest('[class*="videoThumbnail"]') || + e.target?.closest('[class*="playButton"]'); + + const isVideoPlayerArea = e.target?.closest('[class*="videoPlayer"]') || + e.target?.closest('[class*="VideoPlayer"]'); + + // ์ธ๋„ค์ผ ํด๋ฆญ: ๋น„๋””์˜ค ์žฌ์ƒ ์‹œ์ž‘ + if (isThumbnailArea && !isPlaying) { + console.log('๐ŸŽฌ [handleContainerClickFallback] ์ธ๋„ค์ผ ํด๋ฆญ โ†’ ๋น„๋””์˜ค ์žฌ์ƒ ์‹œ์ž‘'); + handleThumbnailClick(e); + } + + // ๋น„๋””์˜ค ์žฌ์ƒ ์ค‘ ํด๋ฆญ: ์ „์ฒดํ™”๋ฉด ํ† ๊ธ€ + if (isVideoPlayerArea && isPlaying && !isFullscreen) { + console.log('๐ŸŽฌ [handleContainerClickFallback] ๋น„๋””์˜ค ํด๋ฆญ โ†’ ์ „์ฒดํ™”๋ฉด ํ† ๊ธ€'); + toggleFullscreen(); + } + }, [isPlaying, isFullscreen, handleThumbnailClick, toggleFullscreen]); + + // โš ๏ธ ํ•ต์‹ฌ: TScrollerDetail์ด capture phase์—์„œ ์ด๋ฒคํŠธ๋ฅผ ๋ง‰์œผ๋ฏ€๋กœ, + // ProductVideoV2 ์ปจํ…Œ์ด๋„ˆ ์ „์ฒด์—์„œ capture phase๋ฅผ ๋‹ค์‹œ ์—ด์–ด์คŒ (Hooks๋Š” early return ์ „์—) + const handleContainerMouseDownCapture = useCallback((e) => { + // ๋น„๋””์˜ค ์˜์—ญ์ธ์ง€ ํ™•์ธ + const isVideoArea = e.currentTarget === containerRef.current; + + console.log('๐Ÿ”“ [ProductVideoV2 Container] onMouseDownCapture - TScrollerDetail ์ฐจ๋‹จ ์šฐํšŒ', { + isVideoArea, + target: e.target?.className, + isPlaying, + isFullscreen, + }); + + toggleFullscreen(); + + // TScrollerDetail์˜ onClick ์‹คํ–‰์„ ๋ง‰๊ธฐ ์œ„ํ•ด preventDefault ํ˜ธ์ถœ + if (isPlaying && !isFullscreen) { + console.log('๐Ÿ”“ [ProductVideoV2] TScrollerDetail onClick ์‹คํ–‰ ์ฐจ๋‹จ'); + e.preventDefault(); + e.stopPropagation(); + } + }, [isPlaying, isFullscreen]); + + // Early return: ๋น„๋””์˜ค ์žฌ์ƒ ๋ถˆ๊ฐ€๋Šฅํ•œ ๊ฒฝ์šฐ if (!canPlayVideo) return null; // ์ปจํ…Œ์ด๋„ˆ ์ปดํฌ๋„ŒํŠธ ๊ฒฐ์ • @@ -409,22 +529,6 @@ export default function ProductVideoV2({ ? SpottableComponent : 'div'; - const containerProps = isFullscreen - ? { - spotlightRestrict: 'self-only', // ํฌ์ปค์Šค๊ฐ€ ๋ฐ–์œผ๋กœ ๋‚˜๊ฐ€์ง€ ์•Š๋„๋ก - spotlightId: 'product-video-v2-fullscreen', - onSpotlightDown: handleSpotlightDown, // ์ „์ฒดํ™”๋ฉด์—์„œ๋„ Down ํ‚ค ๋™์ž‘ - // ์ „์ฒดํ™”๋ฉด ๋ชจ๋“œ: window ๋ ˆ๋ฒจ์—์„œ ์ด๋ฒคํŠธ ์ฒ˜๋ฆฌ - } - : isPlaying - ? { - spotlightId: 'product-video-v2-playing', - onKeyDown: handleContainerKeyDown, // ์ผ๋ฐ˜ ๋ชจ๋“œ: ์ปจํ…Œ์ด๋„ˆ์—์„œ ์ง์ ‘ ์ฒ˜๋ฆฌ - onSpotlightDown: handleSpotlightDown, // ์ผ๋ฐ˜ ์žฌ์ƒ์—์„œ๋„ Down ํ‚ค ๋™์ž‘ - // ์ผ๋ฐ˜ ์žฌ์ƒ ๋ชจ๋“œ: ์ปจํ…Œ์ด๋„ˆ๊ฐ€ ํฌ์ปค์Šค ๋ฐ›์Œ - } - : {}; - // VideoPlayer ์ปดํฌ๋„ŒํŠธ ์ƒ์„ฑ ํ•จ์ˆ˜ const renderVideoPlayer = () => { if (!isPlaying) return null; @@ -447,14 +551,26 @@ export default function ProductVideoV2({ } }} onMouseDownCapture={(e) => { + // โš ๏ธ ํ•ต์‹ฌ: VideoPlayer ๋‚ด๋ถ€(์‹ค์ œ ๋น„๋””์˜ค ์˜์—ญ)์—์„œ๋งŒ ์ด๋ฒคํŠธ๋ฅผ ์‚ด๋ฆฌ๊ณ , + // ๊ทธ ์™ธ wrapper ์˜์—ญ์—์„œ๋งŒ preventDefault + const isVideoElement = e.target?.closest('[class*="videoPlayer"]') || + e.target?.closest('video') || + e.target?.closest('[class*="react-player"]'); + console.log('๐ŸŽฌ [videoPlayerWrapper] onMouseDownCapture ์‹คํ–‰๋จ', { isFullscreen, isPlaying, + isVideoElement, + target: e.target?.className, }); - if (!isFullscreen && isPlaying) { + + // VideoPlayer๊ฐ€ ์•„๋‹Œ wrapper ์˜์—ญ์—์„œ๋งŒ preventDefault + if (!isFullscreen && isPlaying && !isVideoElement) { e.preventDefault(); - e.stopPropagation(); - console.log('๐ŸŽฌ [videoPlayerWrapper] MouseDown at wrapper - capturing'); + console.log('๐Ÿ›‘ [videoPlayerWrapper] preventDefault - wrapper ๋ฐฐ๊ฒฝ ์˜์—ญ'); + } else if (!isFullscreen && isPlaying && isVideoElement) { + // ์‹ค์ œ ๋น„๋””์˜ค ์˜์—ญ์ด๋ฉด ์ด๋ฒคํŠธ๋ฅผ ์ „ํŒŒ์‹œ์ผœ click์ด ์ •์ƒ ๋ฐœ์ƒํ•˜๋„๋ก + console.log('โœ… [videoPlayerWrapper] ์ด๋ฒคํŠธ ์ „ํŒŒ ํ—ˆ์šฉ - ๋น„๋””์˜ค ์š”์†Œ'); } }} > @@ -508,12 +624,20 @@ export default function ProductVideoV2({ {...containerProps} ref={containerRef} className={`${css.videoContainer} ${isFullscreen ? css.fullscreen : ''}`} + onMouseDownCapture={handleContainerMouseDownCapture} + onClick={handleContainerClickFallback} > {!isPlaying ? ( // ์ธ๋„ค์ผ + ์žฌ์ƒ ๋ฒ„ํŠผ ํ‘œ์‹œ { + console.log('๐ŸŽฌ [SpottableComponent] onClick ์‹คํ–‰๋จ', { + eventType: e.type, + target: e.target?.className, + }); + handleThumbnailClick(e); + }} onFocus={videoContainerOnFocus} onBlur={videoContainerOnBlur} spotlightId="product-video-v2-thumbnail" @@ -545,11 +669,26 @@ export default function ProductVideoV2({ handleVideoPlayerClick(e); }} onMouseDownCapture={(e) => { + // โš ๏ธ ํ•ต์‹ฌ: VideoPlayer ์˜์—ญ ๋‚ด์—์„œ๋งŒ ์ด๋ฒคํŠธ๋ฅผ ์‚ด๋ฆฌ๊ณ , + // ๊ทธ ์™ธ ์˜์—ญ(์Šคํฌ๋กค ์˜์—ญ)์—์„œ๋งŒ preventDefault + const isVideoPlayerArea = e.target?.closest('[class*="videoPlayer"]') || + e.target?.closest('[class*="VideoPlayer"]'); + console.log('๐ŸŽฌ [normalContainerRef] onMouseDownCapture ์‹คํ–‰๋จ', { isPlaying, isFullscreen, + isVideoPlayerArea, + target: e.target?.className, }); - handleVideoPlayerMouseDown(e); + + // VideoPlayer๊ฐ€ ์•„๋‹Œ ์˜์—ญ(์Šคํฌ๋กค ์˜์—ญ)์—์„œ๋งŒ preventDefault + if (!isVideoPlayerArea) { + e.preventDefault(); + console.log('๐Ÿ›‘ [normalContainerRef] preventDefault - ์Šคํฌ๋กค ์˜์—ญ์—์„œ์˜ ํด๋ฆญ'); + } else { + // VideoPlayer ์˜์—ญ์ด๋ฉด ์ด๋ฒคํŠธ๋ฅผ ์ „ํŒŒ์‹œ์ผœ click์ด ์ •์ƒ ๋ฐœ์ƒํ•˜๋„๋ก + console.log('โœ… [normalContainerRef] ์ด๋ฒคํŠธ ์ „ํŒŒ ํ—ˆ์šฉ - VideoPlayer ์˜์—ญ'); + } }} > {renderVideoPlayer()}