From da1a050a10d888501d5a7ac44b0d6515d38b5e81 Mon Sep 17 00:00:00 2001 From: optrader Date: Sun, 16 Nov 2025 19:59:28 +0900 Subject: [PATCH] [251116] feat: videoPlayIntentRef MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit ๐Ÿ• ์ปค๋ฐ‹ ์‹œ๊ฐ„: 2025. 11. 16. 19:59:28 ๐Ÿ“Š ๋ณ€๊ฒฝ ํ†ต๊ณ„: โ€ข ์ด ํŒŒ์ผ: 4๊ฐœ โ€ข ์ถ”๊ฐ€: +149์ค„ โ€ข ์‚ญ์ œ: -16์ค„ ๐Ÿ“ ์ˆ˜์ •๋œ ํŒŒ์ผ: ~ com.twin.app.shoptime/src/actions/playActions.js ~ com.twin.app.shoptime/src/views/HomePanel/HomeBanner/HomeBanner.jsx ~ com.twin.app.shoptime/src/views/HomePanel/HomeBanner/RandomUnit.jsx ~ com.twin.app.shoptime/src/views/HomePanel/HomePanel.jsx ๐Ÿ”ง ํ•จ์ˆ˜ ๋ณ€๊ฒฝ ๋‚ด์šฉ: ๐Ÿ“„ com.twin.app.shoptime/src/views/HomePanel/HomeBanner/HomeBanner.jsx (javascript): ๐Ÿ”„ Modified: Spottable() ๐Ÿ“„ com.twin.app.shoptime/src/views/HomePanel/HomeBanner/RandomUnit.jsx (javascript): โœ… Added: SpotlightContainerDecorator() ๐Ÿ”ง ์ฃผ์š” ๋ณ€๊ฒฝ ๋‚ด์šฉ: โ€ข ํ•ต์‹ฌ ๋น„์ฆˆ๋‹ˆ์Šค ๋กœ์ง ๊ฐœ์„  --- .../src/actions/playActions.js | 9 +- .../views/HomePanel/HomeBanner/HomeBanner.jsx | 11 +- .../views/HomePanel/HomeBanner/RandomUnit.jsx | 134 +++++++++++++++++- .../src/views/HomePanel/HomePanel.jsx | 3 + 4 files changed, 145 insertions(+), 12 deletions(-) diff --git a/com.twin.app.shoptime/src/actions/playActions.js b/com.twin.app.shoptime/src/actions/playActions.js index a6346ceb..fecf1154 100644 --- a/com.twin.app.shoptime/src/actions/playActions.js +++ b/com.twin.app.shoptime/src/actions/playActions.js @@ -161,11 +161,12 @@ export const startVideoPlayerNew = panelWorkingAction = updatePanel; } - // playerState ์—…๋ฐ์ดํŠธ: ๊ธฐ์กด playerState์™€ ์ƒˆ ๋ฐ์ดํ„ฐ ๋ณ‘ํ•ฉ + // ์ค‘๋ณต ์‹คํ–‰ ๋ฐฉ์ง€: ํ˜„์žฌ PlayerPanel์ด ๊ฐ™์€ ๋ฐฐ๋„ˆ๋ฅผ ์žฌ์ƒ ์ค‘์ด๋ฉด skip const currentPlayerState = topPanel?.panelInfo?.playerState || {}; - - // โœ… ์ •ํ™•ํ•œ ๋น„๊ต: ๊ฐ™์€ ๋ฐฐ๋„ˆ + ๊ฐ™์€ modal ์ƒํƒœ + ๊ฐ™์€ ์œ„์น˜์ผ ๋•Œ๋งŒ skip - if (shouldSkipVideoPlayback(topPanel?.panelInfo, modal, modalContainerId, bannerId)) { + if (currentPlayerState?.currentBannerId === bannerId) { + console.log('[playActions] startVideoPlayerNew: ๋™์ผ ๋ฐฐ๋„ˆ ์žฌ์ƒ ์ค‘์ด๋ผ ์ค‘๋ณต ์‹คํ–‰ SKIP', { + bannerId, + }); return; } diff --git a/com.twin.app.shoptime/src/views/HomePanel/HomeBanner/HomeBanner.jsx b/com.twin.app.shoptime/src/views/HomePanel/HomeBanner/HomeBanner.jsx index 4dd01426..1c2eb567 100644 --- a/com.twin.app.shoptime/src/views/HomePanel/HomeBanner/HomeBanner.jsx +++ b/com.twin.app.shoptime/src/views/HomePanel/HomeBanner/HomeBanner.jsx @@ -51,7 +51,13 @@ const SpottableComponent = Spottable('div'); const Container = SpotlightContainerDecorator({ enterTo: 'last-focused' }, 'div'); const ContainerBasic = SpotlightContainerDecorator({ enterTo: 'last-focused' }, 'div'); -export default function HomeBanner({ firstSpot, spotlightId, handleItemFocus, handleShelfFocus }) { +export default function HomeBanner({ + firstSpot, + spotlightId, + handleItemFocus, + handleShelfFocus, + videoPlayIntentRef, +}) { const dispatch = useDispatch(); useEffect(() => { dispatch(justForYou()); @@ -453,6 +459,7 @@ export default function HomeBanner({ firstSpot, spotlightId, handleItemFocus, ha handleItemFocus={_handleItemFocus} randomNumber={data.randomIndex} videoPlayerable={videoPlayerable} + videoPlayIntentRef={videoPlayIntentRef} /> ) : ( @@ -472,7 +479,7 @@ export default function HomeBanner({ firstSpot, spotlightId, handleItemFocus, ha ); }, - [_handleItemFocus, _handleShelfFocus, bannerDataList] + [_handleItemFocus, _handleShelfFocus, bannerDataList, videoPlayIntentRef] ); // 0๋ฒˆ์งธ ๋ฐฐ๋„ˆ(์˜๊ตฌ ์žฌ์ƒ)๋ฅผ ์œ„ํ•œ ๋ Œ๋”๋ง ํ•จ์ˆ˜ 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 276ff27a..5aa5cba4 100644 --- a/com.twin.app.shoptime/src/views/HomePanel/HomeBanner/RandomUnit.jsx +++ b/com.twin.app.shoptime/src/views/HomePanel/HomeBanner/RandomUnit.jsx @@ -50,6 +50,7 @@ export default function RandomUnit({ handleItemFocus, randomNumber, videoPlayerable = false, + videoPlayIntentRef, }) { const dispatch = useDispatch(); @@ -83,13 +84,16 @@ export default function RandomUnit({ const [isFocused, setIsFocused] = useState(false); const [videoError, setVideoError] = useState(false); const [liveIndicies, setLiveIndicies] = useState([]); + const [videoIntentVersion, setVideoIntentVersion] = useState(0); const defaultFocus = useSelector((state) => state.home.defaultFocus); const isVideoTransitionLocked = useSelector((state) => state.home.videoTransitionLocked); const timerRef = useRef(); const hasAutoPlayStartedRef = useRef(false); const hasPlaybackStartedRef = useRef(false); + const videoIntentTimerRef = useRef(null); const isDefaultAutoPlayTarget = defaultFocus === spotlightId; + const lastProcessedIntentVersionRef = useRef(-1); const bannerDataRef = useRef(bannerData); const randomDataRef = useRef( bannerDetailInfos && randomNumber !== undefined && bannerDetailInfos.length > 0 @@ -273,19 +277,45 @@ export default function RandomUnit({ lastUpdate: videoPlayState.lastUpdate, }); - console.log('[RandomUnit] onFocus called', { - spotlightId, + console.log('[RandomUnit] onFocus - ๋น„๋””์˜ค ์žฌ์ƒ ์˜๋„ ์ €์žฅ', { + bannerId: spotlightId, videoPlayerable, + currentIntent: videoPlayIntentRef.current?.bannerId, 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()); + // ๐Ÿ”ฝ [251116] ์ˆ˜์ •: ์žฌ์ƒ ์˜๋„๋งŒ ์ €์žฅ, ์ฆ‰์‹œ ์‹คํ–‰ โŒ + if (videoPlayerable) { + videoPlayIntentRef.current = { + bannerId: spotlightId, + videoProps: { + bannerId: spotlightId, + showUrl: randomData.showUrl, + patnrId: randomData.patnrId, + showId: randomData.showId, + shptmBanrTpNm: randomData.shptmBanrTpNm, + lgCatCd: randomData.lgCatCd, + chanId: randomData.brdcChnlId, + modal: true, + modalContainerId: spotlightId, + }, + timestamp: Date.now(), + }; + + console.log('[RandomUnit] ๋น„๋””์˜ค ์žฌ์ƒ ์˜๋„ ์ €์žฅ๋จ', { + intentBannerId: spotlightId, + videoUrl: randomData.showUrl, + }); + + setVideoIntentVersion((version) => version + 1); + + // ๊ธฐ์กด finishVideoPreview ํ˜ธ์ถœ ์ œ๊ฑฐ! + // if (videoPlayerable && currentVideoBannerId && currentVideoBannerId !== spotlightId) { + // dispatch(finishVideoPreview()); + // } } // video๊ฐ€ ํ”Œ๋ ˆ์ด ๋ถˆ๊ฐ€๋Šฅํ•œ ๊ฒฝ์šฐ: ์žฌ์ƒ ์ค‘์ธ ๋น„๋””์˜ค๋ฅผ 1px๋กœ ์ถ•์†Œ @@ -618,6 +648,15 @@ export default function RandomUnit({ } }, [randomData]); + // ๐Ÿ”ฝ [251116] ๊ธฐ์กด ๋กœ์ง ๋‹จ์ˆœํ™” - videoPlayIntentRef๊ฐ€ ๋ชจ๋“  ๋น„๋””์˜ค ์ œ์–ด๋ฅผ ๋‹ด๋‹น + useEffect(() => { + if (!isFocused) { + setVideoError(false); + } + }, [isFocused]); + + // ๐Ÿ”ฝ [251116] ๊ธฐ์กด ๋ณต์žกํ•œ ๋น„๋””์˜ค ์‹œ์ž‘ ๋กœ์ง ๋น„ํ™œ์„ฑํ™” + /* useEffect(() => { if (isFocused && !videoError && !hasPlaybackStartedRef.current) { if (timerRef.current) { @@ -665,6 +704,89 @@ export default function RandomUnit({ spotlightId, handleStartVideo, ]); + */ + + // ๐Ÿ”ฝ [251116] ์‹ ๊ทœ: videoPlayIntentRef ๊ธฐ๋ฐ˜ ๋น„๋””์˜ค ์ œ์–ด + useEffect(() => { + if (!videoPlayIntentRef.current) { + return undefined; + } + if (lastProcessedIntentVersionRef.current === videoIntentVersion) { + return undefined; + } + lastProcessedIntentVersionRef.current = videoIntentVersion; + + const intent = videoPlayIntentRef.current; + const currentVideoId = videoPlayState.videoId; + const activeBannerId = currentVideoBannerId; + + console.log('[RandomUnit] videoPlayIntent useEffect', { + intentBannerId: intent.bannerId, + currentVideoId, + activeBannerId, + currentPlayback: videoPlayState.playback, + currentDisplay: videoPlayState.display, + loadingProgress: videoPlayState.loadingProgress, + shouldStart: intent.bannerId !== activeBannerId, + isVideoTransitionLocked, + timestamp: Date.now(), + }); + + if (intent.bannerId === activeBannerId) { + return undefined; + } + + if (videoIntentTimerRef.current) { + clearTimeout(videoIntentTimerRef.current); + } + + videoIntentTimerRef.current = setTimeout(() => { + if (videoPlayIntentRef.current?.bannerId !== intent.bannerId) { + return; + } + + console.log('[RandomUnit] ๋น„๋””์˜ค ๋ณ€๊ฒฝ ์‹คํ–‰', { + from: currentVideoId, + to: intent.bannerId, + }); + + if (activeBannerId) { + dispatch(finishVideoPreview()); + + setTimeout(() => { + dispatch(setVideoTransitionLock(true)); + dispatch(startVideoPlayerNew(intent.videoProps)); + hasPlaybackStartedRef.current = true; + + setTimeout(() => { + dispatch(setVideoTransitionLock(false)); + }, 2000); + }, 100); + } else { + dispatch(setVideoTransitionLock(true)); + dispatch(startVideoPlayerNew(intent.videoProps)); + hasPlaybackStartedRef.current = true; + + setTimeout(() => { + dispatch(setVideoTransitionLock(false)); + }, 2000); + } + }, 300); // ์•ˆ์ •ํ™” ์‹œ๊ฐ„ + + return () => { + if (videoIntentTimerRef.current) { + clearTimeout(videoIntentTimerRef.current); + videoIntentTimerRef.current = null; + } + }; + }, [ + videoPlayState.videoId, + dispatch, + videoPlayIntentRef, + videoIntentVersion, + isVideoTransitionLocked, + currentVideoBannerId, + ]); useEffect(() => { if (isFocused && broadcast?.type === 'videoError') { diff --git a/com.twin.app.shoptime/src/views/HomePanel/HomePanel.jsx b/com.twin.app.shoptime/src/views/HomePanel/HomePanel.jsx index 24f75f6f..f03becbc 100644 --- a/com.twin.app.shoptime/src/views/HomePanel/HomePanel.jsx +++ b/com.twin.app.shoptime/src/views/HomePanel/HomePanel.jsx @@ -89,6 +89,8 @@ const HomePanel = ({ isOnTop }) => { enableLogging: true, logPrefix: '[HomePanel-VideoPlay]', }); + // ๐Ÿ”ฝ ๋น„๋””์˜ค ์žฌ์ƒ ์˜๋„ ๊ณต์œ  ref + const videoPlayIntentRef = useRef(null); // ๐Ÿ”ฝ useVideoMove - ํฌ์ปค์Šค ์ „ํ™˜ ๊ธฐ๋ฐ˜ ๋™์˜์ƒ ์ œ์–ด // [COMMENTED OUT] useVideoMove ๋ฏธ์‚ฌ์šฉ - cleanup() ํ˜ธ์ถœ๋˜์ง€ ์•Š์Œ @@ -314,6 +316,7 @@ const HomePanel = ({ isOnTop }) => { el.shptmApphmDspyOptNm )} handleItemFocus={handleItemFocus(el.shptmApphmDspyOptCd)} + videoPlayIntentRef={videoPlayIntentRef} /> ); }