From cb0764b3ac5ebd51cc52810570000790dfcd1789 Mon Sep 17 00:00:00 2001 From: optrader Date: Wed, 19 Nov 2025 10:15:18 +0900 Subject: [PATCH] [251119] fix: PlayerPanel - 1 MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit ๐Ÿ• ์ปค๋ฐ‹ ์‹œ๊ฐ„: 2025. 11. 19. 10:15:17 ๐Ÿ“Š ๋ณ€๊ฒฝ ํ†ต๊ณ„: โ€ข ์ด ํŒŒ์ผ: 4๊ฐœ โ€ข ์ถ”๊ฐ€: +141์ค„ โ€ข ์‚ญ์ œ: -38์ค„ ๐Ÿ“ ์ˆ˜์ •๋œ ํŒŒ์ผ: ~ com.twin.app.shoptime/src/views/PlayerPanel/PlayerOverlay/PlayerOverlayContents.jsx ~ com.twin.app.shoptime/src/views/PlayerPanel/PlayerPanel.jsx ~ com.twin.app.shoptime/src/views/PlayerPanel/PlayerTabContents/TabContents/ShopNowContents.jsx ~ com.twin.app.shoptime/src/views/PlayerPanel/PlayerTabContents/TabContents/YouMayLikeContents.jsx ๐Ÿ”ง ์ฃผ์š” ๋ณ€๊ฒฝ ๋‚ด์šฉ: โ€ข ์ค‘๊ฐ„ ๊ทœ๋ชจ ๊ธฐ๋Šฅ ๊ฐœ์„  --- .../PlayerOverlay/PlayerOverlayContents.jsx | 2 +- .../src/views/PlayerPanel/PlayerPanel.jsx | 161 +++++++++++++----- .../TabContents/ShopNowContents.jsx | 12 ++ .../TabContents/YouMayLikeContents.jsx | 8 + 4 files changed, 143 insertions(+), 40 deletions(-) diff --git a/com.twin.app.shoptime/src/views/PlayerPanel/PlayerOverlay/PlayerOverlayContents.jsx b/com.twin.app.shoptime/src/views/PlayerPanel/PlayerOverlay/PlayerOverlayContents.jsx index 36052be0..a2fcff54 100644 --- a/com.twin.app.shoptime/src/views/PlayerPanel/PlayerOverlay/PlayerOverlayContents.jsx +++ b/com.twin.app.shoptime/src/views/PlayerPanel/PlayerOverlay/PlayerOverlayContents.jsx @@ -18,7 +18,7 @@ import css from './PlayerOverlayContents.module.less'; const SpottableBtn = Spottable('button'); -const Container = SpotlightContainerDecorator({ enterTo: 'default-element' }, 'div'); +const Container = SpotlightContainerDecorator({ enterTo: 'last-focused' }, 'div'); function PlayerOverlayContents({ type, diff --git a/com.twin.app.shoptime/src/views/PlayerPanel/PlayerPanel.jsx b/com.twin.app.shoptime/src/views/PlayerPanel/PlayerPanel.jsx index 95189c96..dc6d7c2f 100644 --- a/com.twin.app.shoptime/src/views/PlayerPanel/PlayerPanel.jsx +++ b/com.twin.app.shoptime/src/views/PlayerPanel/PlayerPanel.jsx @@ -64,7 +64,7 @@ import TabContainerV2 from './PlayerTabContents/v2/TabContainer.v2'; // import ShopNowButton from './PlayerTabContents/v2/ShopNowButton'; const Container = SpotlightContainerDecorator( - { enterTo: 'default-element', preserveld: true }, + { enterTo: 'last-focused', preserveId: true }, 'div' ); @@ -172,6 +172,7 @@ const PlayerPanel = ({ isTabActivated, panelInfo, isOnTop, spotlightId, ...props }); const videoPlayer = useRef(null); + const prevModal = usePrevious(panelInfo?.modal); const [playListInfo, setPlayListInfo] = USE_STATE('playListInfo', ''); const [shopNowInfo, setShopNowInfo] = USE_STATE('shopNowInfo'); const [backupInitialIndex, setBackupInitialIndex] = USE_STATE('backupInitialIndex', 0); @@ -369,6 +370,25 @@ const PlayerPanel = ({ isTabActivated, panelInfo, isOnTop, spotlightId, ...props } }, [panelInfo?.isPaused]); + // Modal ์ƒํƒœ ๋ณ€ํ™” ๊ฐ์ง€ (true โ†’ false) + useEffect(() => { + if (prevModal !== undefined && prevModal === true && panelInfo?.modal === false) { + console.log('[PlayerPanel] ๐Ÿ”„ Modal ์ƒํƒœ ๋ณ€ํ™”: true โ†’ false (์ „์ฒดํ™”๋ฉด ๋ชจ๋“œ๋กœ ๋ณต๊ท€)'); + console.log('[PlayerPanel] ๐ŸŽฏ ํฌ์ปค์Šค ๋ณต์› ์ค€๋น„ - lastFocusedTargetId:', panelInfo?.lastFocusedTargetId); + + // DetailPanel์—์„œ ๋ณต๊ท€ ์‹œ ํฌ์ปค์Šค ๋ณต์› + const lastFocusedTargetId = panelInfo?.lastFocusedTargetId; + + if (lastFocusedTargetId) { + // ShopNowContents๊ฐ€ ๋ Œ๋”๋ง๋  ๋•Œ๊นŒ์ง€ ๋Œ€๊ธฐ ํ›„ ํฌ์ปค์Šค ๋ณต์› + setTimeout(() => { + console.log('[PlayerPanel] ๐Ÿ” 800ms ํ›„ ํฌ์ปค์Šค ๋ณต์› ์‹œ๋„:', lastFocusedTargetId); + Spotlight.focus(lastFocusedTargetId); + }, 800); + } + } + }, [panelInfo?.modal, prevModal, panelInfo?.lastFocusedTargetId]); + // creating live log params useEffect(() => { if (currentLiveShowInfo && Object.keys(currentLiveShowInfo).length > 0) { @@ -1867,6 +1887,8 @@ const PlayerPanel = ({ isTabActivated, panelInfo, isOnTop, spotlightId, ...props const [initialEnterV2, setInitialEnterV2] = USE_STATE('initialEnterV2', true); const timerId = useRef(null); const timerIdV2 = useRef(null); + const timerIdTabAutoAdvance = useRef(null); + const prevTabIndexV2 = useRef(null); const showSideContents = useMemo(() => { return ( @@ -1949,45 +1971,76 @@ const PlayerPanel = ({ isTabActivated, panelInfo, isOnTop, spotlightId, ...props const resetTimerV2 = useCallback( (timeout) => { - // console.log('[TabContainerV2] resetTimerV2 ํ˜ธ์ถœ', timeout); + console.log('[TabContainerV2] resetTimerV2 ํ˜ธ์ถœ', timeout); if (timerIdV2.current) { - // console.log('[TabContainerV2] ๊ธฐ์กด ํƒ€์ด๋จธ ํด๋ฆฌ์–ด'); + console.log('[TabContainerV2] ๊ธฐ์กด ํƒ€์ด๋จธ ํด๋ฆฌ์–ด'); clearTimerV2(); } if (initialEnterV2) { - // console.log('[TabContainerV2] initialEnterV2 false๋กœ ๋ณ€๊ฒฝ'); + console.log('[TabContainerV2] initialEnterV2 false๋กœ ๋ณ€๊ฒฝ'); setInitialEnterV2(false); } timerIdV2.current = setTimeout(() => { - // console.log('[TabContainerV2] ํƒ€์ด๋จธ ์‹คํ–‰ - belowContentsVisible false๋กœ ๋ณ€๊ฒฝ'); + console.log('[TabContainerV2] ํƒ€์ด๋จธ ์‹คํ–‰ - belowContentsVisible false๋กœ ๋ณ€๊ฒฝ (30์ดˆ ๊ฒฝ๊ณผ)'); setBelowContentsVisible(false); }, timeout); }, [clearTimerV2, initialEnterV2, setInitialEnterV2, setBelowContentsVisible] ); + const clearTimerTabAutoAdvance = useCallback(() => { + clearTimeout(timerIdTabAutoAdvance.current); + timerIdTabAutoAdvance.current = null; + }, []); + + const resetTimerTabAutoAdvance = useCallback( + (timeout) => { + if (timerIdTabAutoAdvance.current) { + clearTimerTabAutoAdvance(); + } + + timerIdTabAutoAdvance.current = setTimeout(() => { + setTabIndexV2(2); + }, timeout); + }, + [clearTimerTabAutoAdvance] + ); + // Redux๋กœ ์˜ค๋ฒ„๋ ˆ์ด ์ˆจ๊น€ useEffect(() => { if (shouldHideOverlays) { console.log('[PlayerPanel] shouldHideOverlays true - ์˜ค๋ฒ„๋ ˆ์ด ์ˆจ๊น€'); setSideContentsVisible(false); + console.log('[setBelowContentsVisible] Redux๋กœ ์˜ค๋ฒ„๋ ˆ์ด ์ˆจ๊น€ - false๋กœ ๋ณ€๊ฒฝ'); setBelowContentsVisible(false); if (videoPlayer.current?.hideControls) { videoPlayer.current.hideControls(); } + // ๋ชจ๋“  ํƒ€์ด๋จธ ํด๋ฆฌ์–ด + if (timerId.current) { + clearTimer(); + } + if (timerIdV2.current) { + clearTimerV2(); + } + if (timerIdTabAutoAdvance.current) { + clearTimerTabAutoAdvance(); + } + dispatch(resetPlayerOverlays()); } - }, [shouldHideOverlays, dispatch]); + }, [shouldHideOverlays, dispatch, clearTimer, clearTimerV2, clearTimerTabAutoAdvance]); // Redux๋กœ ์˜ค๋ฒ„๋ ˆ์ด ํ‘œ์‹œ useEffect(() => { if (shouldShowOverlays) { console.log('[PlayerPanel] shouldShowOverlays true - ์˜ค๋ฒ„๋ ˆ์ด ํ‘œ์‹œ'); setSideContentsVisible(true); + console.log('[setBelowContentsVisible] Redux๋กœ ์˜ค๋ฒ„๋ ˆ์ด ํ‘œ์‹œ - true๋กœ ๋ณ€๊ฒฝ'); setBelowContentsVisible(true); if (videoPlayer.current?.showControls) { @@ -2001,12 +2054,25 @@ const PlayerPanel = ({ isTabActivated, panelInfo, isOnTop, spotlightId, ...props // PlayerPanel์ด ์ตœ์ƒ๋‹จ์ด ๋  ๋•Œ ์˜ค๋ฒ„๋ ˆ์ด ํ‘œ์‹œ (DetailPanel์—์„œ ๋ณต๊ท€) useEffect(() => { if (isOnTop && !panelInfo.modal && !videoVerticalVisible) { - console.log('[PlayerPanel] isOnTop true - ์˜ค๋ฒ„๋ ˆ์ด ํ‘œ์‹œ'); + console.log('[PlayerPanel] โœ… DetailPanel์—์„œ ๋ณต๊ท€ํ•จ! - ์˜ค๋ฒ„๋ ˆ์ด ํ‘œ์‹œ'); setSideContentsVisible(true); + console.log('[setBelowContentsVisible] DetailPanel์—์„œ ๋ณต๊ท€ - true๋กœ ๋ณ€๊ฒฝ'); setBelowContentsVisible(true); // VideoPlayer๊ฐ€ belowContentsVisible prop์„ ๊ฐ์ง€ํ•ด์„œ ์ž๋™์œผ๋กœ controls ํ‘œ์‹œํ•จ + + // DetailPanel์—์„œ ๋ณต๊ท€ ์‹œ ํฌ์ปค์Šค ๋ณต์› ์‹œ๋„ + const lastFocusedTargetId = panelInfo?.lastFocusedTargetId; + console.log('[PlayerPanel] ๐ŸŽฏ DetailPanel ๋ณต๊ท€ - lastFocusedTargetId:', lastFocusedTargetId); + + if (lastFocusedTargetId) { + // ShopNowContents๊ฐ€ ๋ Œ๋”๋ง๋  ๋•Œ๊นŒ์ง€ ์ž ์‹œ ๋Œ€๊ธฐ ํ›„ ํฌ์ปค์Šค ๋ณต์› + setTimeout(() => { + console.log('[PlayerPanel] ๐Ÿ” 500ms ํ›„ ํฌ์ปค์Šค ๋ณต์› ์‹œ๋„:', lastFocusedTargetId); + Spotlight.focus(lastFocusedTargetId); + }, 500); + } } - }, [isOnTop, panelInfo.modal, videoVerticalVisible]); + }, [isOnTop, panelInfo.modal, videoVerticalVisible, panelInfo?.lastFocusedTargetId]); useEffect(() => { // tabContainerVersion === 1์ผ ๋•Œ๋งŒ ์‹คํ–‰ @@ -2055,54 +2121,71 @@ const PlayerPanel = ({ isTabActivated, panelInfo, isOnTop, spotlightId, ...props }; }, [sideContentsVisible]); - // TabContainerV2 ์ž๋™ ๋‹ซ๊ธฐ + // TabContainerV2 ์ž๋™ ๋‹ซ๊ธฐ (tabIndex 1 โ†’ 2 ๋ณ€๊ฒฝ ๊ฐ์ง€) useEffect(() => { // tabContainerVersion === 2์ผ ๋•Œ๋งŒ ์‹คํ–‰ - if (tabContainerVersion !== 2) return; - - // console.log('[TabContainerV2] useEffect ์‹œ์ž‘', { - // showBelowContents, - // videoVerticalVisible, - // initialEnterV2, - // }); - - const node = document.querySelector(`[data-spotlight-id=${TAB_CONTAINER_V2_SPOTLIGHT_ID}]`); - // console.log('[TabContainerV2] DOM node:', node); - - if (!showBelowContents || !node || videoVerticalVisible) { - // console.log('[TabContainerV2] early return'); + if (tabContainerVersion !== 2) { + prevTabIndexV2.current = tabIndexV2; return; } - // NOTE ํƒญ์ด ํ‘œ์‹œ๋  ๋•Œ๋งˆ๋‹ค ํƒ€์ด๋จธ ์‹œ์ž‘ (์ฒซ ์ง„์ž…์€ 30์ดˆ, ์ดํ›„์—๋Š” REGULAR_TIMEOUT) - resetTimerV2(initialEnterV2 ? INITIAL_TIMEOUT : REGULAR_TIMEOUT); + // tabIndexV2๊ฐ€ 1์—์„œ 2๋กœ ์ •ํ™•ํ•˜๊ฒŒ ๋ณ€๊ฒฝ๋˜๋Š” ์‹œ์ ๋งŒ ๊ฐ์ง€ + const isTransitionedTo2 = prevTabIndexV2.current === 1 && tabIndexV2 === 2; + prevTabIndexV2.current = tabIndexV2; - const handleEvent = (e) => { - // console.log('[TabContainerV2] ์ด๋ฒคํŠธ ๋ฐœ์ƒ:', e.type); - resetTimerV2(REGULAR_TIMEOUT); - }; - TARGET_EVENTS.forEach((event) => { - // console.log('[TabContainerV2] ์ด๋ฒคํŠธ ๋ฆฌ์Šค๋„ˆ ๋“ฑ๋ก:', event); - node.addEventListener(event, handleEvent); - }); + if (!isTransitionedTo2) { + if (timerIdV2.current) { + console.log('[TabContainerV2] ํƒ€์ด๋จธ ํด๋ฆฌ์–ด - tabIndex๊ฐ€ 2๊ฐ€ ์•„๋‹˜', tabIndexV2); + clearTimerV2(); + } + return; + } + + console.log('[TabContainerV2] tabIndex 1 โ†’ 2 ๊ฐ์ง€, ํƒ€์ด๋จธ ์‹œ์ž‘'); + + if (!belowContentsVisible || videoVerticalVisible) { + console.log('[TabContainerV2] early return - belowContentsVisible ๋˜๋Š” videoVerticalVisible ์กฐ๊ฑด ๋ถˆ๋งŒ์กฑ'); + return; + } + + // tabIndex 1 โ†’ 2๋กœ ๋ณ€๊ฒฝ๋œ ์ •ํ™•ํ•œ ์‹œ์ ์— 30์ดˆ ํƒ€์ด๋จธ ์‹œ์ž‘ + console.log('[TabContainerV2] 30์ดˆ ํƒ€์ด๋จธ ์‹œ์ž‘'); + resetTimerV2(REGULAR_TIMEOUT); return () => { - // console.log('[TabContainerV2] cleanup'); - TARGET_EVENTS.forEach((event) => node.removeEventListener(event, handleEvent)); - if (timerIdV2.current) { clearTimerV2(); } }; }, [ - showBelowContents, - videoVerticalVisible, tabContainerVersion, + tabIndexV2, + belowContentsVisible, + videoVerticalVisible, resetTimerV2, - initialEnterV2, clearTimerV2, ]); + // TabIndex 1 ์ž๋™ ๋‹ค์Œ ๋‹จ๊ณ„๋กœ ์ด๋™ + useEffect(() => { + // tabIndex === 1์ผ ๋•Œ๋งŒ ์‹คํ–‰ + if (tabIndexV2 !== 1 || !belowContentsVisible || videoVerticalVisible) { + if (timerIdTabAutoAdvance.current) { + clearTimerTabAutoAdvance(); + } + return; + } + + // 10์ดˆ ํ›„ tabIndex๋ฅผ 2๋กœ ๋ณ€๊ฒฝ + resetTimerTabAutoAdvance(10000); + + return () => { + if (timerIdTabAutoAdvance.current) { + clearTimerTabAutoAdvance(); + } + }; + }, [tabIndexV2, belowContentsVisible, videoVerticalVisible, resetTimerTabAutoAdvance, clearTimerTabAutoAdvance]); + useLayoutEffect(() => { const videoContainer = document.querySelector(`.${css.videoContainer}`); @@ -2383,7 +2466,7 @@ const PlayerPanel = ({ isTabActivated, panelInfo, isOnTop, spotlightId, ...props tabIndex={tabIndexV2} onShopNowButtonClick={() => setTabIndexV2(0)} onLiveChannelButtonClick={() => setTabIndexV2(2)} - onLiveNext={handleIndicatorDownClick} + onLiveNext={() => setTabIndexV2(1)} onTabClose={(newTabIndex) => setTabIndexV2(newTabIndex)} tabVisible={belowContentsVisible} /> diff --git a/com.twin.app.shoptime/src/views/PlayerPanel/PlayerTabContents/TabContents/ShopNowContents.jsx b/com.twin.app.shoptime/src/views/PlayerPanel/PlayerTabContents/TabContents/ShopNowContents.jsx index 26bc5910..c0bb83a3 100644 --- a/com.twin.app.shoptime/src/views/PlayerPanel/PlayerTabContents/TabContents/ShopNowContents.jsx +++ b/com.twin.app.shoptime/src/views/PlayerPanel/PlayerTabContents/TabContents/ShopNowContents.jsx @@ -151,6 +151,11 @@ export default function ShopNowContents({ const isYouMayLikeStart = shopNowInfo && index === shopNowInfo.length; const handleItemClick = () => { + // ํ˜„์žฌ ํฌ์ปค์Šค๋œ ์š”์†Œ์˜ spotlightId ์ €์žฅ + const currentFocusedElement = Spotlight.getCurrent(); + const currentSpotlightId = currentFocusedElement?.getAttribute('data-spotlight-id'); + console.log('[ShopNowContents] ํ˜„์žฌ ํฌ์ปค์Šค๋œ spotlightId:', currentSpotlightId); + // DetailPanel push ์ „์— VideoPlayer ์˜ค๋ฒ„๋ ˆ์ด ์ˆจ๊น€ dispatch(hidePlayerOverlays()); @@ -165,6 +170,7 @@ export default function ShopNowContents({ patnrId, prdtId, launchedFromPlayer: true, + lastFocusedTargetId: currentSpotlightId, // ํ˜„์žฌ ํฌ์ปค์Šค๋œ spotlightId ์ €์žฅ }, }) ); @@ -223,6 +229,11 @@ export default function ShopNowContents({ const { originalPrice, discountedPrice, discountRate } = priceInfoMap[index] || {}; const handleItemClick = () => { + // ํ˜„์žฌ ํฌ์ปค์Šค๋œ ์š”์†Œ์˜ spotlightId ์ €์žฅ + const currentFocusedElement = Spotlight.getCurrent(); + const currentSpotlightId = currentFocusedElement?.getAttribute('data-spotlight-id'); + console.log('[ShopNowContents] ํ˜„์žฌ ํฌ์ปค์Šค๋œ spotlightId:', currentSpotlightId); + const params = { tabTitle: tabTitle[tabIndex], productId: prdtId, @@ -253,6 +264,7 @@ export default function ShopNowContents({ patnrId, prdtId, launchedFromPlayer: true, + lastFocusedTargetId: currentSpotlightId, // ํ˜„์žฌ ํฌ์ปค์Šค๋œ spotlightId ์ €์žฅ }, }) ); diff --git a/com.twin.app.shoptime/src/views/PlayerPanel/PlayerTabContents/TabContents/YouMayLikeContents.jsx b/com.twin.app.shoptime/src/views/PlayerPanel/PlayerTabContents/TabContents/YouMayLikeContents.jsx index 9c8a2421..61e2f2c6 100644 --- a/com.twin.app.shoptime/src/views/PlayerPanel/PlayerTabContents/TabContents/YouMayLikeContents.jsx +++ b/com.twin.app.shoptime/src/views/PlayerPanel/PlayerTabContents/TabContents/YouMayLikeContents.jsx @@ -2,6 +2,8 @@ import React, { useCallback, useEffect, useMemo, useState } from 'react'; import { useDispatch, useSelector } from 'react-redux'; +import Spotlight from '@enact/spotlight'; + import { pushPanel } from '../../../../actions/panelActions'; import TItemCard, { TYPES } from '../../../../components/TItemCard/TItemCard'; import TVirtualGridList from '../../../../components/TVirtualGridList/TVirtualGridList'; @@ -51,6 +53,11 @@ export default function YouMayLikeContents({ youmaylikeInfos[index]; const handleItemClick = () => { + // ํ˜„์žฌ ํฌ์ปค์Šค๋œ ์š”์†Œ์˜ spotlightId ์ €์žฅ + const currentFocusedElement = Spotlight.getCurrent(); + const currentSpotlightId = currentFocusedElement?.getAttribute('data-spotlight-id'); + console.log('[YouMayLikeContents] ํ˜„์žฌ ํฌ์ปค์Šค๋œ spotlightId:', currentSpotlightId); + const params = { tabTitle: 'You May Also Like', productId: prdtId, @@ -79,6 +86,7 @@ export default function YouMayLikeContents({ patnrId, prdtId, launchedFromPlayer: true, + lastFocusedTargetId: currentSpotlightId, // ํ˜„์žฌ ํฌ์ปค์Šค๋œ spotlightId ์ €์žฅ }, }) );