diff --git a/com.twin.app.shoptime/src/actions/actionTypes.js b/com.twin.app.shoptime/src/actions/actionTypes.js index 30f5f742..fa86146f 100644 --- a/com.twin.app.shoptime/src/actions/actionTypes.js +++ b/com.twin.app.shoptime/src/actions/actionTypes.js @@ -6,6 +6,7 @@ export const types = { POP_PANEL: 'POP_PANEL', UPDATE_PANEL: 'UPDATE_PANEL', RESET_PANELS: 'RESET_PANELS', + FOCUS_PANEL: 'FOCUS_PANEL', // ๐Ÿ”ฝ [251114] ๋ช…์‹œ์  ํฌ์ปค์Šค ์ด๋™ // ๐Ÿ”ฝ [์‹ ๊ทœ] panel history actions ENQUEUE_PANEL_HISTORY: 'ENQUEUE_PANEL_HISTORY', @@ -82,12 +83,12 @@ export const types = { CLEAR_CART: 'CLEAR_CART', //cart api action GET_MY_INFO_CART_SEARCH: 'GET_MY_INFO_CART_SEARCH', - INSERT_MY_INFO_CART : "INSERT_MY_INFO_CART", - DELETE_MY_INFO_CART : "DELETE_MY_INFO_CART", - DELETE_ALL_MY_INFO_CART : "DELETE_ALL_MY_INFO_CART", - UPDATE_MY_INFO_CART : "UPDATE_MY_INFO_CART", + INSERT_MY_INFO_CART: 'INSERT_MY_INFO_CART', + DELETE_MY_INFO_CART: 'DELETE_MY_INFO_CART', + DELETE_ALL_MY_INFO_CART: 'DELETE_ALL_MY_INFO_CART', + UPDATE_MY_INFO_CART: 'UPDATE_MY_INFO_CART', //cart checkbox toggle action - TOGGLE_CHECK_CART : "TOGGLE_CHECK_CART", + TOGGLE_CHECK_CART: 'TOGGLE_CHECK_CART', // appData actions ADD_MAIN_INDEX: 'ADD_MAIN_INDEX', @@ -315,7 +316,7 @@ export const types = { SET_MODAL_BORDER: 'SET_MODAL_BORDER', SET_BANNER_VISIBILITY: 'SET_BANNER_VISIBILITY', - // ๐Ÿ”ฝ [์ถ”๊ฐ€] JustForYou ์ƒํ’ˆ ๊ด€๋ฆฌ ๋ถ€๋ถ„ + // ๐Ÿ”ฝ [์ถ”๊ฐ€] JustForYou ์ƒํ’ˆ ๊ด€๋ฆฌ ๋ถ€๋ถ„ JUSTFORYOU: 'JUSTFORYOU', // ๐Ÿ”ฝ Voice Conductor ๊ด€๋ จ ์•ก์…˜ ํƒ€์ž… diff --git a/com.twin.app.shoptime/src/actions/mediaActions.js b/com.twin.app.shoptime/src/actions/mediaActions.js index 9b4e4e7b..4d4a04f7 100644 --- a/com.twin.app.shoptime/src/actions/mediaActions.js +++ b/com.twin.app.shoptime/src/actions/mediaActions.js @@ -23,15 +23,16 @@ export const startMediaPlayer = const topPanel = panels[panels.length - 1]; let panelWorkingAction = pushPanel; - console.log('[startMediaPlayer] ========== Called =========='); - console.log('[startMediaPlayer] Current panels:', JSON.stringify(panels, null, 2)); - console.log('[startMediaPlayer] topPanel:', JSON.stringify(topPanel, null, 2)); + console.log('[startMediaPlayer]-LoadingVideo ๐Ÿš€ ์‹œ์ž‘:', { + showUrl: rest?.showUrl?.substring(0, 50), + showNm: rest?.showNm, + prdtId: rest?.prdtId, + modal, + modalContainerId, + }); if (topPanel && topPanel.name === panel_names.MEDIA_PANEL) { panelWorkingAction = updatePanel; - console.log('[startMediaPlayer] Using updatePanel (existing MediaPanel)'); - } else { - console.log('[startMediaPanel] Using pushPanel (new MediaPanel)'); } const allParams = { @@ -42,8 +43,6 @@ export const startMediaPlayer = ...rest, }; - console.log('[startMediaPlayer] All parameters:', JSON.stringify(allParams, null, 2)); - dispatch( panelWorkingAction( { @@ -54,7 +53,7 @@ export const startMediaPlayer = ) ); - console.log('[startMediaPlayer] Panel action dispatched'); + console.log('[startMediaPlayer]-LoadingVideo โœ… MediaPanel dispatch ์™„๋ฃŒ'); if (modal && modalContainerId && !spotlightDisable) { Spotlight.setPointerMode(false); diff --git a/com.twin.app.shoptime/src/actions/panelActions.js b/com.twin.app.shoptime/src/actions/panelActions.js index 54b2c5e5..2312e85a 100644 --- a/com.twin.app.shoptime/src/actions/panelActions.js +++ b/com.twin.app.shoptime/src/actions/panelActions.js @@ -1,4 +1,5 @@ -import { types } from "./actionTypes"; +import { types } from './actionTypes'; +import Spotlight from '@enact/spotlight'; /* name: panel_names.PLAYER_PANEL, @@ -27,3 +28,85 @@ export const resetPanels = (panels) => ({ type: types.RESET_PANELS, payload: panels, }); + +/** + * [251114] ๋ช…์‹œ์  ํฌ์ปค์Šค ์ด๋™ + * Panel์˜ ๋น„๋™๊ธฐ ์ž‘์—…(useEffect, ํƒ€์ด๋จธ ๋“ฑ)์ด ํฌ์ปค์Šค๋ฅผ ํƒˆ์ทจํ•˜๋Š” ๊ฒƒ์„ ๋ฐฉ์ง€ + * @param {string} panelName - ๋Œ€์ƒ Panel ์ด๋ฆ„ + * @param {string} focusTarget - ํฌ์ปค์Šคํ•  ์š”์†Œ ID + * @returns {Function} Redux thunk + */ +export const focusPanel = (panelName, focusTarget) => { + return (dispatch, getState) => { + const state = getState(); + const panels = state.panels.panels; + + console.log('[focusPanel] ํฌ์ปค์Šค ์ด๋™ ์‹œ๋„', { + panelName, + focusTarget, + currentPanels: panels.map((p) => p.name), + timestamp: Date.now(), + }); + + // ์•ˆ์ „์„ฑ ์ฒดํฌ 1: Panel์ด ์กด์žฌํ•˜๊ณ  ์ตœ์ƒ๋‹จ ๋˜๋Š” ๊ทธ ์•„๋ž˜์— ์žˆ๋Š”๊ฐ€? + const targetPanelIndex = panels.findIndex((p) => p.name === panelName); + const targetPanel = panels[targetPanelIndex]; + const topPanel = panels[panels.length - 1]; + + if (!targetPanel) { + console.warn(`[focusPanel] โŒ Panel์„ ์ฐพ์„ ์ˆ˜ ์—†์Œ: ${panelName}`); + return; + } + + // Panel์ด ์ตœ์ƒ๋‹จ ๋˜๋Š” ๊ทธ ์•„๋ž˜ ๋ ˆ์ด์–ด์— ์žˆ๋Š”์ง€ ํ™•์ธ + // MediaPanel(์ตœ์ƒ๋‹จ) ์œ„์— ๋‹ค๋ฅธ Modal์ด ์žˆ๋Š” ๊ฒฝ์šฐ๋Š” ํ—ˆ์šฉํ•˜์ง€ ์•Š์Œ + const panelsAboveTarget = panels.slice(targetPanelIndex + 1); + const hasBlockingModalAbove = panelsAboveTarget.some( + (panel) => panel?.panelInfo?.modal === true && panel.name !== panelName + ); + + if (hasBlockingModalAbove) { + const blockingModal = panelsAboveTarget.find((panel) => panel?.panelInfo?.modal === true); + console.warn( + `[focusPanel] โš ๏ธ ์ƒ์œ„์— Modal์ด ์žˆ์Œ. ` + + `${panelName}(${targetPanelIndex}์ธต)์— ํฌ์ปค์Šคํ•  ์ˆ˜ ์—†์Œ. ` + + `์ƒ๋‹จ Modal: ${blockingModal?.name}(${panelsAboveTarget.indexOf(blockingModal) + targetPanelIndex + 1}์ธต)` + ); + return; + } + + console.log( + `[focusPanel] โœ… Panel ์œ„์น˜ ํ™•์ธ: ${panelName}(${targetPanelIndex}์ธต), ` + + `์ „์ฒด Panel: ${panels.length}์ธต` + ); + + // ํฌ์ปค์Šค ์ด๋™ + setTimeout(() => { + const element = document.getElementById(focusTarget); + + if (!element) { + console.warn(`[focusPanel] โŒ ์š”์†Œ๋ฅผ ์ฐพ์„ ์ˆ˜ ์—†์Œ: ${focusTarget}`); + return; + } + + if (element.offsetParent === null) { + console.warn(`[focusPanel] โš ๏ธ ์š”์†Œ๊ฐ€ ์ˆจ๊ฒจ์ ธ์žˆ์Œ: ${focusTarget}`); + return; + } + + // โœ… ํฌ์ปค์Šค ์ด๋™ + Spotlight.focus(focusTarget); + console.log(`[focusPanel] โœ… ํฌ์ปค์Šค ์ด๋™ ์„ฑ๊ณต: ${panelName} โ†’ ${focusTarget}`); + + // Reducer์— ๋ฐ˜์˜ + dispatch({ + type: types.FOCUS_PANEL, + payload: { + panelName, + focusTarget, + timestamp: Date.now(), + }, + }); + }, 0); + }; +}; diff --git a/com.twin.app.shoptime/src/reducers/panelReducer.js b/com.twin.app.shoptime/src/reducers/panelReducer.js index 50049037..fd16d391 100644 --- a/com.twin.app.shoptime/src/reducers/panelReducer.js +++ b/com.twin.app.shoptime/src/reducers/panelReducer.js @@ -5,6 +5,7 @@ const initialState = { // ๊ธฐ์กด ์ƒํƒœ - ์™„์ „ํžˆ ํ˜ธํ™˜๋จ panels: [], lastPanelAction: '', //"", "push", "pop", "update", "reset", "previewPush", "previewPop", "previewUpdate" + lastFocusTarget: null, // [251114] ๋งˆ์ง€๋ง‰ ํฌ์ปค์Šค ๋Œ€์ƒ (panelName + elementId) // [251106] ํŒจ๋„ ์•ก์…˜ ํ ๊ด€๋ จ ์ƒํƒœ - ๊ธฐ์กด ๊ธฐ๋Šฅ์— ์ „ํ˜€ ์˜ํ–ฅ ์—†์Œ panelActionQueue: [], // ์ฒ˜๋ฆฌ ๋Œ€๊ธฐ ์ค‘์ธ ํŒจ๋„ ์•ก์…˜ ํ @@ -13,7 +14,7 @@ const initialState = { queueStats: { totalProcessed: 0, // ์ด ์ฒ˜๋ฆฌ๋œ ์•ก์…˜ ์ˆ˜ failedCount: 0, // ์‹คํŒจํ•œ ์•ก์…˜ ์ˆ˜ - averageProcessingTime: 0 // ํ‰๊ท  ์ฒ˜๋ฆฌ ์‹œ๊ฐ„ + averageProcessingTime: 0, // ํ‰๊ท  ์ฒ˜๋ฆฌ ์‹œ๊ฐ„ }, // [251106] ๋น„๋™๊ธฐ ์•ก์…˜ ๊ด€๋ จ ์ƒํƒœ @@ -30,7 +31,7 @@ export const panelsReducer = (state = initialState, action) => { case types.PUSH_PANEL: { console.log('[panelReducer] ๐Ÿ”ต PUSH_PANEL START', { newPanelName: action.payload.name, - currentPanels: state.panels.map(p => p.name), + currentPanels: state.panels.map((p) => p.name), duplicatable: action.duplicatable, }); @@ -76,7 +77,7 @@ export const panelsReducer = (state = initialState, action) => { } console.log('[panelReducer] ๐Ÿ”ต PUSH_PANEL END', { - resultPanels: newState.map(p => p.name), + resultPanels: newState.map((p) => p.name), lastAction, }); @@ -90,7 +91,7 @@ export const panelsReducer = (state = initialState, action) => { case types.POP_PANEL: { console.log('[panelReducer] ๐Ÿ”ด POP_PANEL START', { targetPanel: action.payload || 'last_panel', - currentPanels: state.panels.map(p => p.name), + currentPanels: state.panels.map((p) => p.name), }); let lastAction = 'pop'; @@ -113,7 +114,7 @@ export const panelsReducer = (state = initialState, action) => { } console.log('[panelReducer] ๐Ÿ”ด POP_PANEL END', { - resultPanels: resultPanels.map(p => p.name), + resultPanels: resultPanels.map((p) => p.name), lastAction, }); @@ -159,7 +160,7 @@ export const panelsReducer = (state = initialState, action) => { } case types.RESET_PANELS: { console.log('[panelReducer] ๐ŸŸข RESET_PANELS START', { - currentPanels: state.panels.map(p => p.name), + currentPanels: state.panels.map((p) => p.name), payloadProvided: !!action.payload, }); @@ -171,7 +172,7 @@ export const panelsReducer = (state = initialState, action) => { : []; console.log('[panelReducer] ๐ŸŸข RESET_PANELS END', { - resultPanels: updatedPanels.map(p => p.name), + resultPanels: updatedPanels.map((p) => p.name), }); return { @@ -267,7 +268,8 @@ export const panelsReducer = (state = initialState, action) => { const processingTime = Date.now() - startTime; const newTotalProcessed = state.queueStats.totalProcessed + 1; const newAverageTime = - (state.queueStats.averageProcessingTime * state.queueStats.totalProcessed + processingTime) / + (state.queueStats.averageProcessingTime * state.queueStats.totalProcessed + + processingTime) / newTotalProcessed; console.log('[panelReducer] โœ… QUEUE_ITEM_PROCESSED', { @@ -352,7 +354,7 @@ export const panelsReducer = (state = initialState, action) => { ...action.payload, status: 'running', startTime: Date.now(), - } + }, }, queueError: null, // ์—๋Ÿฌ ์ดˆ๊ธฐํ™” }; @@ -381,16 +383,19 @@ export const panelsReducer = (state = initialState, action) => { result: action.payload.result, executionTime, completedAt: action.payload.timestamp, - } + }, ].slice(-100), // ์ตœ๊ทผ 100๊ฐœ๋งŒ ์œ ์ง€ queueError: null, queueStats: { ...state.queueStats, totalProcessed: state.queueStats.totalProcessed + 1, - averageProcessingTime: Math.round( - ((state.queueStats.averageProcessingTime * state.queueStats.totalProcessed) + executionTime) / - (state.queueStats.totalProcessed + 1) * 100 - ) / 100, + averageProcessingTime: + Math.round( + ((state.queueStats.averageProcessingTime * state.queueStats.totalProcessed + + executionTime) / + (state.queueStats.totalProcessed + 1)) * + 100 + ) / 100, }, }; } @@ -419,7 +424,7 @@ export const panelsReducer = (state = initialState, action) => { error: action.payload.error, executionTime, failedAt: action.payload.timestamp, - } + }, ].slice(-100), // ์ตœ๊ทผ 100๊ฐœ๋งŒ ์œ ์ง€ queueError: { actionId: action.payload.actionId, @@ -433,6 +438,24 @@ export const panelsReducer = (state = initialState, action) => { }; } + // [251114] ๋ช…์‹œ์  ํฌ์ปค์Šค ์ด๋™ + case types.FOCUS_PANEL: { + console.log('[panelReducer] ๐ŸŽฏ FOCUS_PANEL', { + panelName: action.payload.panelName, + focusTarget: action.payload.focusTarget, + timestamp: action.payload.timestamp, + }); + + return { + ...state, + lastFocusTarget: { + panelName: action.payload.panelName, + focusTarget: action.payload.focusTarget, + timestamp: action.payload.timestamp, + }, + }; + } + default: return state; } diff --git a/com.twin.app.shoptime/src/utils/SpotlightIds.js b/com.twin.app.shoptime/src/utils/SpotlightIds.js index da7fcd6e..0d45a646 100644 --- a/com.twin.app.shoptime/src/utils/SpotlightIds.js +++ b/com.twin.app.shoptime/src/utils/SpotlightIds.js @@ -1,59 +1,60 @@ export const SpotlightIds = { - TPANEL: "tpanel", - TBODY: "tbody", - TPOPUP: "tpopup", - HOME_TBODY: "home_tbody", - TITEM_CARD: "tItemCard", - TAB_LAYOUT: "tablayout", + TPANEL: 'tpanel', + TBODY: 'tbody', + TPOPUP: 'tpopup', + HOME_TBODY: 'home_tbody', + TITEM_CARD: 'tItemCard', + TAB_LAYOUT: 'tablayout', // homePanel - HOME_CATEGORY_NAV: "homeCategoryNav", + HOME_CATEGORY_NAV: 'homeCategoryNav', // FeaturedBrandsPanel - BRAND_VERTICAL_PAGENATOR: "brandVerticalPagenator", - BRAND_QUICK_MENU: "brandQuickMenu", - BRAND_TOP_BUTTON: "brandTopButton", + BRAND_VERTICAL_PAGENATOR: 'brandVerticalPagenator', + BRAND_QUICK_MENU: 'brandQuickMenu', + BRAND_TOP_BUTTON: 'brandTopButton', // TrendingNowPanel - TRENDING_NOW_VERTICAL_PAGINATOR: "trendingNowVerticalPaginator", - TRENDING_NOW_POPULAR_SHOW: "trendingNowPopularShow", - TRENDING_NOW_POPULAR_VIDEO: "trendingNowPopularVideo", - TRENDING_NOW_POPULAR_GRID_LIST: "trendingNowVirtualGridList", - TRENDING_NOW_PREV_INDICATOR: "trendingNowPrevIndicator", - TRENDING_NOW_NEXT_INDICATOR: "trendingNowNextIndicator", - TRENDING_NOW_BEST_SELLER: "trendingNowBestSeller", - TRENDING_NOW_TOP_BUTTON: "trendingNowTopButton", + TRENDING_NOW_VERTICAL_PAGINATOR: 'trendingNowVerticalPaginator', + TRENDING_NOW_POPULAR_SHOW: 'trendingNowPopularShow', + TRENDING_NOW_POPULAR_VIDEO: 'trendingNowPopularVideo', + TRENDING_NOW_POPULAR_GRID_LIST: 'trendingNowVirtualGridList', + TRENDING_NOW_PREV_INDICATOR: 'trendingNowPrevIndicator', + TRENDING_NOW_NEXT_INDICATOR: 'trendingNowNextIndicator', + TRENDING_NOW_BEST_SELLER: 'trendingNowBestSeller', + TRENDING_NOW_TOP_BUTTON: 'trendingNowTopButton', // myPagePanel - MY_PAGE_FAVORITES_BOX: "myPageFavoritesBox", - MY_PAGE_REMINDRES_BOX: "myPageRemindresBox", - MY_PAGE_MY_ORDER_BOX: "myPageMyOrderBox", - MY_PAGE_MY_ORDER_TAB_CONTAINER: "myPageMyOrderTabContainer", + MY_PAGE_FAVORITES_BOX: 'myPageFavoritesBox', + MY_PAGE_REMINDRES_BOX: 'myPageRemindresBox', + MY_PAGE_MY_ORDER_BOX: 'myPageMyOrderBox', + MY_PAGE_MY_ORDER_TAB_CONTAINER: 'myPageMyOrderTabContainer', // categoryPanel - CATEGORY_CONTENTS_BOX: "categoryContentsBox", - CATEGORY_TAB_CONTAINER: "categorytabContainer", - SHOW_PRODUCTS_BOX: "showProductsBox", - SHOW_CONTENTS_BOX: "showContentsBox", + CATEGORY_CONTENTS_BOX: 'categoryContentsBox', + CATEGORY_TAB_CONTAINER: 'categorytabContainer', + SHOW_PRODUCTS_BOX: 'showProductsBox', + SHOW_CONTENTS_BOX: 'showContentsBox', // video player - PLAYER_SKIPINTRO: "skipintro", - PLAYER_TITLE_LAYER: "playerTitleLayer", - PLAYER_SLIDER: "playerslider", - PLAYER_TAB_BUTTON: "playerTabArrow", - PLAYER_BACK_BUTTON: "player-back-button", - PLAYER_SUBTITLE_BUTTON: "player-subtitlebutton", + PLAYER_SKIPINTRO: 'skipintro', + PLAYER_TITLE_LAYER: 'playerTitleLayer', + PLAYER_SLIDER: 'playerslider', + PLAYER_TAB_BUTTON: 'playerTabArrow', + PLAYER_BACK_BUTTON: 'player-back-button', + PLAYER_SUBTITLE_BUTTON: 'player-subtitlebutton', // searchPanel - SEARCH_THEME: "search_theme", - SEARCH_SHOW: "search_show", - SEARCH_ITEM: "search_item", - SEARCH_BESTSELLER: "search_bestseller", - SEARCH_TAB_CONTAINER: "searchtabContainer", + SEARCH_THEME: 'search_theme', + SEARCH_SHOW: 'search_show', + SEARCH_ITEM: 'search_item', + SEARCH_BESTSELLER: 'search_bestseller', + SEARCH_TAB_CONTAINER: 'searchtabContainer', // pin Code Popup - PINCODE_CONTAINER: "pincodeContainer", + PINCODE_CONTAINER: 'pincodeContainer', // detailPanel - DETAIL_BUYNOW: "detail_buynow", - DETAIL_SHOPBYMOBILE: "detail_shop_by_mobile", + DETAIL_BUYNOW: 'detail_buynow', + DETAIL_SHOPBYMOBILE: 'detail_shop_by_mobile', + DETAIL_PRODUCTVIDEO: 'product-video-player', }; diff --git a/com.twin.app.shoptime/src/utils/focusPanelGuide.js b/com.twin.app.shoptime/src/utils/focusPanelGuide.js new file mode 100644 index 00000000..235fd833 --- /dev/null +++ b/com.twin.app.shoptime/src/utils/focusPanelGuide.js @@ -0,0 +1,286 @@ +/** + * [251114] focusPanel ์•ก์…˜ ์‚ฌ์šฉ ๊ฐ€์ด๋“œ + * + * Panel์˜ ๋น„๋™๊ธฐ ์ž‘์—…(useEffect, ํƒ€์ด๋จธ ๋“ฑ)์ด ํฌ์ปค์Šค๋ฅผ ํƒˆ์ทจํ•˜๋Š” ๋ฌธ์ œ๋ฅผ ํ•ด๊ฒฐํ•˜๊ธฐ ์œ„ํ•œ + * ๋ช…์‹œ์  ํฌ์ปค์Šค ์ œ์–ด ์‹œ์Šคํ…œ์ž…๋‹ˆ๋‹ค. + * + * ๋ฌธ์ œ ์ƒํ™ฉ: + * - updatePanel ํ˜ธ์ถœ โ†’ Panel์˜ useEffect ์‹œ์ž‘ + * - pushPanel ํ˜ธ์ถœ โ†’ ์ƒˆ Panel์— ํฌ์ปค์Šค ์„ค์ • + * - Panel์˜ useEffect ์™„๋ฃŒ โ†’ ๊ธฐ์กด Panel์ด ํฌ์ปค์Šค ํƒˆ์ทจ (์›ํ•˜์ง€ ์•Š๋Š” ๋™์ž‘) + * + * ํ•ด๊ฒฐ์ฑ…: + * - updatePanel, pushPanel ๋“ฑ์œผ๋กœ Panel์„ ์ œ์–ดํ•  ๋•Œ + * - focusPanel()๋กœ ๋ช…์‹œ์ ์œผ๋กœ ํฌ์ปค์Šค ๋Œ€์ƒ์„ ์ง€์ • + * - Panel์˜ useEffect๋Š” ๋” ์ด์ƒ ์ž๋™ ํฌ์ปค์Šค ์ด๋™ ์•ˆ ํ•จ + */ + +import { focusPanel } from '../actions/panelActions'; +import { panel_names } from './Config'; + +// ============================================================================ +// ์˜ˆ์‹œ 1: ๋‹จ์ผ Panel ํฌ์ปค์Šค ์ œ์–ด (๊ฐ€์žฅ ๊ฐ„๋‹จ) +// ============================================================================ + +export const example1_singlePanelFocus = (dispatch) => { + console.log('โœ… ์˜ˆ์‹œ 1: ๋‹จ์ผ Panel ํฌ์ปค์Šค ์ œ์–ด'); + + // DETAIL_PANEL ํ‘œ์‹œ + ํฌ์ปค์Šค ์„ค์ • + dispatch(pushPanel({ name: panel_names.DETAIL_PANEL })); + dispatch(focusPanel(panel_names.DETAIL_PANEL, 'detail-buy-button')); +}; + +// ============================================================================ +// ์˜ˆ์‹œ 2: 2์ค‘ ๊ตฌ์กฐ (PLAYER + DETAIL)์—์„œ ํฌ์ปค์Šค ์ œ์–ด +// ============================================================================ + +export const example2_twoLayerFocus = (dispatch) => { + console.log('โœ… ์˜ˆ์‹œ 2: 2์ค‘ ๊ตฌ์กฐ ํฌ์ปค์Šค ์ œ์–ด'); + + // ์ƒํ™ฉ: [PLAYER_PANEL]์ด ์ด๋ฏธ ์žˆ๋Š” ์ƒํƒœ + + // 1๋‹จ๊ณ„: DETAIL_PANEL ์ถ”๊ฐ€ + dispatch(pushPanel({ name: panel_names.DETAIL_PANEL })); + + // 2๋‹จ๊ณ„: DETAIL_PANEL์˜ ํŠน์ • ์š”์†Œ์— ํฌ์ปค์Šค + dispatch(focusPanel(panel_names.DETAIL_PANEL, 'detail-buy-button')); + + // ๋˜๋Š” PLAYER_PANEL์˜ ํ”Œ๋ ˆ์ด๋ฒ„ํŠผ์— ํฌ์ปค์Šค + // dispatch(focusPanel(panel_names.PLAYER_PANEL, 'player-play-button')); +}; + +// ============================================================================ +// ์˜ˆ์‹œ 3: 3์ค‘ ๊ตฌ์กฐ (PLAYER + DETAIL + MEDIA)์—์„œ ํฌ์ปค์Šค ์ œ์–ด +// ============================================================================ + +export const example3_threeLayerFocus = (dispatch) => { + console.log('โœ… ์˜ˆ์‹œ 3: 3์ค‘ ๊ตฌ์กฐ ํฌ์ปค์Šค ์ œ์–ด'); + + // ์ƒํ™ฉ: [PLAYER_PANEL, DETAIL_PANEL]์ด ์ด๋ฏธ ์žˆ๋Š” ์ƒํƒœ + + // 1๋‹จ๊ณ„: MEDIA_PANEL ์ถ”๊ฐ€ (modal=true) + dispatch( + pushPanel({ + name: panel_names.MEDIA_PANEL, + panelInfo: { modal: true }, + }) + ); + + // 2๋‹จ๊ณ„: MEDIA_PANEL์˜ ๋‹ซ๊ธฐ ๋ฒ„ํŠผ์— ํฌ์ปค์Šค + dispatch(focusPanel(panel_names.MEDIA_PANEL, 'media-close-button')); + + // ๊ฒฐ๊ณผ: + // - PLAYER_PANEL (๋ฐฑ๊ทธ๋ผ์šด๋“œ) + // - DETAIL_PANEL (์ค‘๊ฐ„, ๋ณด์ž„) + // - MEDIA_PANEL (๋งจ ์œ„, ๋ชจ๋‹ฌ, ํฌ์ปค์Šค ๋ฐ›์Œ) +}; + +// ============================================================================ +// ์˜ˆ์‹œ 4: updatePanel๊ณผ ํ•จ๊ป˜ ์‚ฌ์šฉ +// ============================================================================ + +import { updatePanel } from '../actions/panelActions'; + +export const example4_updateWithFocus = (dispatch) => { + console.log('โœ… ์˜ˆ์‹œ 4: updatePanel๊ณผ focusPanel ํ•จ๊ป˜ ์‚ฌ์šฉ'); + + // โŒ ๋ฌธ์ œ๊ฐ€ ๋  ์ˆ˜ ์žˆ๋Š” ๋ฐฉ์‹: + // dispatch(updatePanel({ + // name: 'DETAIL_PANEL', + // panelInfo: { productId: '123', isLoading: false } + // })); + // DETAIL_PANEL์˜ useEffect๊ฐ€ ๋‚˜์ค‘์— ํฌ์ปค์Šค๋ฅผ ํƒˆ์ทจํ•  ์ˆ˜ ์žˆ์Œ + + // โœ… ์˜ฌ๋ฐ”๋ฅธ ๋ฐฉ์‹: + dispatch( + updatePanel({ + name: panel_names.DETAIL_PANEL, + panelInfo: { productId: '123', isLoading: false }, + }) + ); + + // ๋ช…์‹œ์ ์œผ๋กœ ํฌ์ปค์Šค ์ง€์ • + dispatch(focusPanel(panel_names.DETAIL_PANEL, 'detail-product-title')); +}; + +// ============================================================================ +// ์˜ˆ์‹œ 5: API ํ˜ธ์ถœ ํ›„ Panel ์ œ์–ด ๋ฐ ํฌ์ปค์Šค +// ============================================================================ + +export const example5_apiAndFocus = (dispatch, getState) => { + console.log('โœ… ์˜ˆ์‹œ 5: API ํ˜ธ์ถœ ํ›„ Panel ์ œ์–ด ๋ฐ ํฌ์ปค์Šค'); + + // API ํ˜ธ์ถœ ์‹œ๋ฎฌ๋ ˆ์ด์…˜ + fetch('/api/products/123') + .then((res) => res.json()) + .then((data) => { + // API ์„ฑ๊ณต โ†’ Panel ์—…๋ฐ์ดํŠธ + dispatch( + updatePanel({ + name: panel_names.DETAIL_PANEL, + panelInfo: { + product: data, + isLoading: false, + }, + }) + ); + + // ๋ช…์‹œ์ ์œผ๋กœ ํฌ์ปค์Šค ์ง€์ • (DETAIL_PANEL์˜ useEffect๋ณด๋‹ค ๋จผ์ € ์‹คํ–‰๋˜์ง€๋งŒ + // focusPanel์€ ๋น„๋™๊ธฐ๋กœ ์ฒ˜๋ฆฌ๋˜๋ฏ€๋กœ ๋จผ์ € ๋๋‚˜๋”๋ผ๋„ ์•ˆ์ „) + dispatch(focusPanel(panel_names.DETAIL_PANEL, 'detail-add-to-cart-button')); + }) + .catch((error) => { + // API ์‹คํŒจ โ†’ ์—๋Ÿฌ Panel ํ‘œ์‹œ + dispatch( + pushPanel({ + name: panel_names.ERROR_PANEL, + panelInfo: { error: error.message }, + }) + ); + + // ์—๋Ÿฌ Panel์˜ ํ™•์ธ ๋ฒ„ํŠผ์— ํฌ์ปค์Šค + dispatch(focusPanel(panel_names.ERROR_PANEL, 'error-ok-button')); + }); +}; + +// ============================================================================ +// ์˜ˆ์‹œ 6: Queue์™€ ํ•จ๊ป˜ ์‚ฌ์šฉ (์ˆœ์„œ ๋ณด์žฅ) +// ============================================================================ + +import { pushPanelQueued, updatePanelQueued } from '../actions/queuedPanelActions'; + +export const example6_queueWithFocus = (dispatch) => { + console.log('โœ… ์˜ˆ์‹œ 6: Panel Queue์™€ focusPanel ํ•จ๊ป˜ ์‚ฌ์šฉ'); + + // Panel ์ œ์–ด๋Š” Queue๋กœ ์ˆœ์„œ ๋ณด์žฅ + dispatch( + updatePanelQueued({ + name: panel_names.DETAIL_PANEL, + panelInfo: { productId: '456', isLoading: false }, + }) + ); + + dispatch( + pushPanelQueued({ + name: panel_names.MEDIA_PANEL, + panelInfo: { modal: true }, + }) + ); + + // ํฌ์ปค์Šค๋Š” ๋ช…์‹œ์ ์œผ๋กœ ์ง€์ • + dispatch(focusPanel(panel_names.MEDIA_PANEL, 'media-close-button')); +}; + +// ============================================================================ +// ์˜ˆ์‹œ 7: Panel์—์„œ focusPanel ํ˜ธ์ถœ +// ============================================================================ + +/* +// DetailPanel.jsx์—์„œ: + +import { useDispatch } from 'react-redux'; +import { focusPanel } from '../actions/panelActions'; +import { panel_names } from '../utils/Config'; + +function DetailPanel({ panelInfo }) { + const dispatch = useDispatch(); + + const handleProductSelect = (productId) => { + // ์„ ํƒํ•œ ์ƒํ’ˆ์œผ๋กœ Panel ์—…๋ฐ์ดํŠธ + dispatch(updatePanel({ + name: panel_names.DETAIL_PANEL, + panelInfo: { productId } + })); + + // ํŠน์ • ์š”์†Œ์— ํฌ์ปค์Šค (Panel์˜ useEffect๊ฐ€ ํฌ์ปค์Šค๋ฅผ ํƒˆ์ทจํ•˜์ง€ ์•Š์Œ) + dispatch(focusPanel( + panel_names.DETAIL_PANEL, + 'detail-product-description' + )); + }; + + return ( +
+ +
+ ); +} +*/ + +// ============================================================================ +// ์˜ˆ์‹œ 8: ์—๋Ÿฌ ์ฒ˜๋ฆฌ +// ============================================================================ + +export const example8_errorHandling = (dispatch) => { + console.log('โœ… ์˜ˆ์‹œ 8: ์—๋Ÿฌ ์ฒ˜๋ฆฌ'); + + // focusPanel์€ ์•ˆ์ „์„ฑ ์ฒดํฌ๋ฅผ ์ˆ˜ํ–‰ํ•ฉ๋‹ˆ๋‹ค: + + // 1. Panel์ด ์กด์žฌํ•˜์ง€ ์•Š์œผ๋ฉด: + // [focusPanel] โŒ Panel์„ ์ฐพ์„ ์ˆ˜ ์—†์Œ: INVALID_PANEL + + // 2. ์ƒ์œ„์— Modal์ด ์žˆ์œผ๋ฉด (์ƒˆ๋กœ์šด ๋กœ์ง): + // [focusPanel] โš ๏ธ ์ƒ์œ„์— Modal์ด ์žˆ์Œ. DETAIL_PANEL(1์ธต)์— ํฌ์ปค์Šคํ•  ์ˆ˜ ์—†์Œ. + // ์ƒ๋‹จ Modal: ANOTHER_MODAL_PANEL(3์ธต) + + // 3. ์š”์†Œ๋ฅผ ์ฐพ์„ ์ˆ˜ ์—†์œผ๋ฉด: + // [focusPanel] โŒ ์š”์†Œ๋ฅผ ์ฐพ์„ ์ˆ˜ ์—†์Œ: invalid-id + + // 4. ์š”์†Œ๊ฐ€ ์ˆจ๊ฒจ์ ธ์žˆ์œผ๋ฉด: + // [focusPanel] โš ๏ธ ์š”์†Œ๊ฐ€ ์ˆจ๊ฒจ์ ธ์žˆ์Œ: hidden-element + + // โœ… ์„ฑ๊ณต ๋กœ๊ทธ (์ƒˆ๋กœ ์ถ”๊ฐ€): + // [focusPanel] โœ… Panel ์œ„์น˜ ํ™•์ธ: DETAIL_PANEL(1์ธต), ์ „์ฒด Panel: 3์ธต + + // ๋ชจ๋“  ๊ฒฝ์šฐ์— console์— ์ƒ์„ธํ•œ ๋กœ๊ทธ๊ฐ€ ์ถœ๋ ฅ๋˜๋ฏ€๋กœ ๋””๋ฒ„๊น…์ด ์‰ฌ์›€ +}; + +// ============================================================================ +// ํ•ต์‹ฌ ์›์น™ +// ============================================================================ + +/* +1. updatePanel/pushPanel ํ˜ธ์ถœ ํ›„ focusPanel ํ˜ธ์ถœ + dispatch(updatePanel(...)); + dispatch(focusPanel(panelName, elementId)); + +2. Panel์˜ useEffect์—์„œ๋Š” focusPanel์„ ํ˜ธ์ถœํ•˜์ง€ ๋ง ๊ฒƒ + - Panel์˜ ๋กœ์ง์€ updatePanel์˜ panelInfo ๋ณ€๊ฒฝ๋งŒ ๊ฐ์ง€ + - ํฌ์ปค์Šค ์ œ์–ด๋Š” ์•ก์…˜ ํ˜ธ์ถœ์ฒ˜์—์„œ ๋ช…์‹œ์ ์œผ๋กœ ์ง€์ • + +3. focusPanel์€ ๋งค๋ฒˆ ์•ˆ์ „์„ฑ์„ ์ฒดํฌํ•จ + - Panel ์กด์žฌ ์—ฌ๋ถ€ + - Panel ๋ ˆ์ด์–ด ์œ„์น˜ (์ตœ์ƒ๋‹จ ๋˜๋Š” ๊ทธ ์•„๋ž˜ ํŒจ๋„๋งŒ ํ—ˆ์šฉ) + - ์ƒ์œ„ Modal ์กด์žฌ ์—ฌ๋ถ€ (์ƒ์œ„์— Modal์ด ์žˆ์œผ๋ฉด ํฌ์ปค์Šค ์ฐจ๋‹จ) + - ์š”์†Œ ์กด์žฌ ์—ฌ๋ถ€ + - ์š”์†Œ ๊ฐ€์‹œ์„ฑ + +4. 2์ค‘/3์ค‘ ๊ตฌ์กฐ์—์„œ๋„ ์•ˆ์ „ํ•จ + - ์ตœ์ƒ๋‹จ ๋˜๋Š” ๊ทธ ์•„๋ž˜ ๋ ˆ์ด์–ด ํŒจ๋„์—๋งŒ ํฌ์ปค์Šค ํ—ˆ์šฉ + - ์ƒ์œ„์— Modal์ด ์žˆ๋Š” ๊ฒฝ์šฐ์—๋งŒ ํฌ์ปค์Šค ์ฐจ๋‹จ + - [PLAYER_PANEL, DETAIL_PANEL, MEDIA_PANEL] ๊ตฌ์กฐ์—์„œ: + โ€ข MEDIA_PANEL์— ํฌ์ปค์Šค: โœ… ๊ฐ€๋Šฅ (์ตœ์ƒ๋‹จ) + โ€ข DETAIL_PANEL์— ํฌ์ปค์Šค: โœ… ๊ฐ€๋Šฅ (MEDIA_PANEL์ด Modal์ด์–ด๋„ ํ•˜์œ„ ํŒจ๋„์ด๋ฏ€๋กœ ๊ฐ€๋Šฅ) + โ€ข PLAYER_PANEL์— ํฌ์ปค์Šค: โœ… ๊ฐ€๋Šฅ (์ตœํ•˜์œ„ ํŒจ๋„) + - ๋ Œ๋”๋ง๋˜์ง€ ์•Š์€ ํŒจ๋„์˜ ์š”์†Œ์— ์ ‘๊ทผ ๋ถˆ๊ฐ€ +*/ + +// ============================================================================ +// Redux DevTools์—์„œ ํ™•์ธ +// ============================================================================ + +/* +Redux DevTools๋ฅผ ์—ด๋ฉด: + +FOCUS_PANEL ์•ก์…˜์ด ๋‚˜ํƒ€๋‚˜๊ณ , payload์—: +{ + panelName: 'DETAIL_PANEL', + focusTarget: 'detail-buy-button', + timestamp: 1731014400000 +} + +state.panels.lastFocusTarget์—๋„ ๊ฐ™์€ ์ •๋ณด๊ฐ€ ์ €์žฅ๋˜์–ด +๋งˆ์ง€๋ง‰ ํฌ์ปค์Šค ์ƒํƒœ๋ฅผ ์ถ”์ ํ•  ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค. +*/ diff --git a/com.twin.app.shoptime/src/views/DetailPanel/DetailPanel.jsx b/com.twin.app.shoptime/src/views/DetailPanel/DetailPanel.jsx index 5b72bc1b..9773a2c8 100644 --- a/com.twin.app.shoptime/src/views/DetailPanel/DetailPanel.jsx +++ b/com.twin.app.shoptime/src/views/DetailPanel/DetailPanel.jsx @@ -662,8 +662,10 @@ export default function DetailPanel({ panelInfo, isOnTop, spotlightId }) { ); const handleProductAllSectionReady = useCallback(() => { + console.log('############## ShopByMobile focus'); const spotTime = setTimeout(() => { - Spotlight.focus(SpotlightIds.DETAIL_SHOPBYMOBILE); + // Spotlight.focus(SpotlightIds.DETAIL_SHOPBYMOBILE); + Spotlight.focus(SpotlightIds.DETAIL_PRODUCTVIDEO); }, 100); return () => { clearTimeout(spotTime); @@ -726,7 +728,7 @@ export default function DetailPanel({ panelInfo, isOnTop, spotlightId }) { console.log('[DetailPanel] MediaPanel modal=true detected - focusing ProductVideo'); const focusTimer = setTimeout(() => { Spotlight.focus('product-video-player'); - }, 500); + }, 2500); return () => clearTimeout(focusTimer); } }, [panels]); 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 8b911566..dacf4177 100644 --- a/com.twin.app.shoptime/src/views/DetailPanel/ProductAllSection/ProductAllSection.jsx +++ b/com.twin.app.shoptime/src/views/DetailPanel/ProductAllSection/ProductAllSection.jsx @@ -341,7 +341,57 @@ export default function ProductAllSection({ dispatch(resetShowAllReviews()); }, []); // ๋นˆ dependency array = ๋งˆ์šดํŠธ ์‹œ์—๋งŒ ์‹คํ–‰ - // โš ๏ธ REMOVED: ์ƒํ’ˆ ๋ณ€๊ฒฝ ์‹œ ํ•„ํ„ฐ ์ดˆ๊ธฐํ™” ๋กœ์ง ์ œ๊ฑฐ + // ์ž„์‹œ: ๋ฌด์กฐ๊ฑด 1.5์ดˆ ํ›„์— product-video-player์— ํฌ์ปค์Šค + useEffect(() => { + console.log( + '[ProductAllSection] ํฌ์ปค์Šค ์‹œ๋„ ์ „ - hasVideo:', + hasVideo, + 'productVideoVersion:', + productVideoVersion + ); + const timer = setTimeout(() => { + console.log('[ProductAllSection] ํฌ์ปค์Šค ํ˜ธ์ถœ ์‹œ๋„: product-video-player'); + + // DOM์— ์š”์†Œ๊ฐ€ ์กด์žฌํ•˜๋Š”์ง€ ํ™•์ธ + const element = + document.querySelector('[data-spotlight-id="product-video-player"]') || + document.getElementById('product-video-player') || + document.querySelector('[spotlight-id="product-video-player"]'); + + console.log('[ProductAllSection] DOM ์š”์†Œ ํ™•์ธ:', { + element: element, + elementExists: !!element, + elementTag: element?.tagName, + elementId: element?.id, + elementSpotlightId: + element?.getAttribute('data-spotlight-id') || element?.getAttribute('spotlight-id'), + }); + + try { + Spotlight.focus('product-video-player'); + console.log('[ProductAllSection] ํฌ์ปค์Šค ํ˜ธ์ถœ ์„ฑ๊ณต'); + + // ํฌ์ปค์Šค ํ›„ ํ˜„์žฌ ํฌ์ปค์Šค๋œ ์š”์†Œ ํ™•์ธ + setTimeout(() => { + const activeElement = document.activeElement; + console.log('[ProductAllSection] ํฌ์ปค์Šค ํ›„ activeElement:', { + activeElement: activeElement, + activeElementTag: activeElement?.tagName, + activeElementId: activeElement?.id, + activeElementSpotlightId: + activeElement?.getAttribute('data-spotlight-id') || + activeElement?.getAttribute('spotlight-id'), + }); + }, 100); + } catch (error) { + console.error('[ProductAllSection] ํฌ์ปค์Šค ํ˜ธ์ถœ ์‹คํŒจ:', error); + } + }, 1500); // 1.5์ดˆ = 1500ms + + return () => { + clearTimeout(timer); + }; + }, []); // ์ด๋ฏธ useReviews ํ›…์—์„œ ๋™์ผํ•œ ๊ธฐ๋Šฅ์„ ์ˆ˜ํ–‰ํ•˜๊ณ  ์žˆ์Œ // ProductAllSection์—์„œ ์ค‘๋ณต์œผ๋กœ ํ˜ธ์ถœํ•˜๋ฉด UserReviewPanel ์ง„์ž… ์‹œ // reviewListData๊ฐ€ ๋ฐ˜๋ณต์ ์œผ๋กœ ์ดˆ๊ธฐํ™”๋˜์–ด Chrome์—์„œ ์ง„์ž… ๋ถˆ๊ฐ€ ๋ฐœ์ƒ @@ -859,10 +909,16 @@ export default function ProductAllSection({ prevMediaPanelModalStateRef.current === false ) { console.log( - '[ProductAllSection] ๐Ÿ”„ MediaPanel์ด ์ „์ฒดํ™”๋ฉด์—์„œ ๋ชจ๋‹ฌ๋กœ ๋ณต๊ท€ - ProductVideo๋กœ ํฌ์ปค์Šค ๋ณต๊ตฌ' + '[ProductAllSection] ๐Ÿ”„ MediaPanel์ด ์ „์ฒดํ™”๋ฉด์—์„œ ๋ชจ๋‹ฌ๋กœ ๋ณต๊ท€ - ProductVideo๋กœ ํฌ์ปค์Šค ๋ณต๊ตฌ ์‹œ๋„' ); const focusTimer = setTimeout(() => { - Spotlight.focus('product-video-player'); + console.log('[ProductAllSection] MediaPanel ๋ณต๊ท€ ํ›„ ํฌ์ปค์Šค ํ˜ธ์ถœ: product-video-player'); + try { + Spotlight.focus('product-video-player'); + console.log('[ProductAllSection] MediaPanel ๋ณต๊ท€ ํ›„ ํฌ์ปค์Šค ํ˜ธ์ถœ ์„ฑ๊ณต'); + } catch (error) { + console.error('[ProductAllSection] MediaPanel ๋ณต๊ท€ ํ›„ ํฌ์ปค์Šค ํ˜ธ์ถœ ์‹คํŒจ:', error); + } }, 100); return () => clearTimeout(focusTimer); } @@ -1148,6 +1204,7 @@ export default function ProductAllSection({ onVideoPlaying={() => setIsVideoPlaying(true)} onScrollToImages={handleScrollToImagesV1} onFocus={() => console.log('[ProductVideo V1] Focused')} + data-spotlight-id="product-video-player-container" /> ) : ( { - if (autoPlay && canPlayVideo && !hasAutoPlayed && productInfo) { - console.log('[ProductVideo] Auto-playing video'); + // MediaPanel์ด ์ด๋ฏธ modal๋กœ ์žฌ์ƒ ์ค‘์ธ์ง€ ํ™•์ธ + const isMediaPanelAlreadyPlaying = + topPanel?.name === panel_names.MEDIA_PANEL && topPanel?.panelInfo?.modal === true; + + if (autoPlay && canPlayVideo && !hasAutoPlayed && productInfo && !isMediaPanelAlreadyPlaying) { + console.log('[ProductVideo]-LoadingVideo ๐ŸŽฏ AutoPlay ์‹œ์ž‘:', { + prdtId: productInfo?.prdtId, + prdtNm: productInfo?.prdtNm, + prdtMediaUrl: productInfo?.prdtMediaUrl?.substring(0, 50), + }); setHasAutoPlayed(true); // ์งง์€ ๋”œ๋ ˆ์ด ํ›„ ์žฌ์ƒ ์‹œ์ž‘ (์ปดํฌ๋„ŒํŠธ ๋งˆ์šดํŠธ ์™„๋ฃŒ ํ›„) @@ -121,6 +130,8 @@ export default function ProductVideo({ dispatch, modalClassNameChange, continuousPlay, + topPanel, + panels, ]); // ๋น„๋””์˜ค ์žฌ์ƒ ๊ฐ€๋Šฅ ์—ฌ๋ถ€ ์ฒดํฌ diff --git a/com.twin.app.shoptime/src/views/DetailPanel/ProductContentSection/YouMayAlsoLike/YouMayAlsoLike.jsx b/com.twin.app.shoptime/src/views/DetailPanel/ProductContentSection/YouMayAlsoLike/YouMayAlsoLike.jsx index 5777d5b6..ff01c8b1 100644 --- a/com.twin.app.shoptime/src/views/DetailPanel/ProductContentSection/YouMayAlsoLike/YouMayAlsoLike.jsx +++ b/com.twin.app.shoptime/src/views/DetailPanel/ProductContentSection/YouMayAlsoLike/YouMayAlsoLike.jsx @@ -1,40 +1,20 @@ -import React, { - useCallback, - useEffect, - useMemo, - useRef, - useState, -} from 'react'; +import React, { useCallback, useEffect, useMemo, useRef, useState } from 'react'; -import { - useDispatch, - useSelector, -} from 'react-redux'; +import { useDispatch, useSelector } from 'react-redux'; import { Job } from '@enact/core/util'; -import SpotlightContainerDecorator - from '@enact/spotlight/SpotlightContainerDecorator'; +import SpotlightContainerDecorator from '@enact/spotlight/SpotlightContainerDecorator'; import Spottable from '@enact/spotlight/Spottable'; import { clearThemeDetail } from '../../../../actions/homeActions'; -import { - popPanel, - pushPanel, - updatePanel, -} from '../../../../actions/panelActions'; +import { popPanel, pushPanel, updatePanel } from '../../../../actions/panelActions'; import { finishVideoPreview } from '../../../../actions/playActions'; import THeader from '../../../../components/THeader/THeader'; import TItemCardNew from '../../../../components/TItemCard/TItemCard.new'; -import TVerticalPagenator - from '../../../../components/TVerticalPagenator/TVerticalPagenator'; -import TVirtualGridList - from '../../../../components/TVirtualGridList/TVirtualGridList'; +import TVerticalPagenator from '../../../../components/TVerticalPagenator/TVerticalPagenator'; +import TVirtualGridList from '../../../../components/TVirtualGridList/TVirtualGridList'; import useScrollTo from '../../../../hooks/useScrollTo'; -import { - LOG_CONTEXT_NAME, - LOG_MESSAGE_ID, - panel_names, -} from '../../../../utils/Config'; +import { LOG_CONTEXT_NAME, LOG_MESSAGE_ID, panel_names } from '../../../../utils/Config'; import { $L } from '../../../../utils/helperMethods'; import css from './YouMayAlsoLike.module.less'; @@ -186,9 +166,11 @@ export default function YouMayAlsoLike({ ); cursorOpen.current.stop(); }; + // prdtId๊ฐ€ ์—†๋Š” ๊ฒฝ์šฐ๋ฅผ ๋Œ€๋น„ํ•œ ์•ˆ์ •์ ์ธ key ์ƒ์„ฑ + const itemKey = prdtId ? `${patnrId}-${prdtId}` : `product-${index}`; return ( { console.log('[MediaPanel] isOnTop:', { isOnTop, panelInfo, }); - if (panelInfo && panelInfo.modal) { + if (panelInfo && panelInfo.modal && panelInfo?.shptmBanrTpNm !== 'MEDIA') { if (!isOnTop) { console.log('[MediaPanel] Not on top - pausing video'); dispatch(pauseModalMedia()); @@ -875,7 +876,7 @@ const MediaPanel = React.forwardRef( [dispatch] ); - const onClickBack = useCallback( + const handleClickBack = useCallback( (ev, isEnd) => { //modal๋กœ๋ถ€ํ„ฐ Full ์ „ํ™˜๋œ ๊ฒฝ์šฐ ๋‹ค์‹œ preview ๋ชจ๋“œ๋กœ ๋Œ์•„๊ฐ. if (panelInfo.modalContainerId && !panelInfo.modal) { @@ -904,6 +905,11 @@ const MediaPanel = React.forwardRef( '[MediaPanel] Back button pressed - returning to modal, focus will be handled by ProductVideo' ); + setTimeout(() => { + console.log('[MediaPanel] focusPanel '); + dispatch(focusPanel('DETAIL_PANEL', 'product-video-player')); + }, 100); + ev?.stopPropagation(); // ev?.preventDefault(); return; @@ -921,6 +927,7 @@ const MediaPanel = React.forwardRef( } ev?.stopPropagation(); // ev?.preventDefault(); + return; } }, @@ -1036,8 +1043,9 @@ const MediaPanel = React.forwardRef( }, [topPanel]); const cannotPlay = useMemo(() => { - return !isOnTop && topPanel?.name === panel_names.MEDIA_PANEL; - }, [topPanel, isOnTop]); + // URL์ด ์žˆ์œผ๋ฉด ํ•ญ์ƒ ์žฌ์ƒ ๊ฐ€๋Šฅ (isOnTop ์กฐ๊ฑด ์ œ๊ฑฐ) + return false; + }, []); const getPlayer = useCallback((ref) => { videoPlayer.current = ref; @@ -1678,8 +1686,24 @@ const MediaPanel = React.forwardRef( return null; } - return playListInfo && playListInfo[selectedIndex]?.showUrl; - }, [playListInfo, selectedIndex, broadcast]); + // MEDIA ํƒ€์ž…์ผ ๋•Œ: panelInfo.showUrl ์‚ฌ์šฉ + if (panelInfo?.shptmBanrTpNm === 'MEDIA') { + console.log('[MediaPanel]-LoadingVideo ๐Ÿ“บ MEDIA URL:', { + showUrl: panelInfo?.showUrl?.substring(0, 50), + prdtId: panelInfo?.prdtId, + }); + return panelInfo?.showUrl; + } + + // ๊ธฐํƒ€ ํƒ€์ž…: playListInfo ์‚ฌ์šฉ + const url = playListInfo && playListInfo[selectedIndex]?.showUrl; + if (url) { + console.log('[MediaPanel]-LoadingVideo ๐ŸŽฌ PlayList URL:', { + url: url.substring(0, 50), + }); + } + return url; + }, [playListInfo, selectedIndex, broadcast, panelInfo?.shptmBanrTpNm, panelInfo?.showUrl]); const isYoutube = useMemo(() => { if (currentPlayingUrl && currentPlayingUrl.includes('youtu')) { @@ -1698,11 +1722,17 @@ const MediaPanel = React.forwardRef( const isReadyToPlay = useMemo(() => { if (!currentPlayingUrl) { + console.log('[MediaPanel]-LoadingVideo โŒ isReadyToPlay = false (no URL)'); return false; } if (!Config.DEBUG_VIDEO_SUBTITLE_TEST && currentSubtitleUrl && !currentSubtitleBlob) { + console.log('[MediaPanel]-LoadingVideo โŒ isReadyToPlay = false (subtitle not loaded):', { + currentSubtitleUrl, + currentSubtitleBlob: !!currentSubtitleBlob, + }); return false; } + console.log('[MediaPanel]-LoadingVideo โœ… isReadyToPlay = true'); return true; }, [currentPlayingUrl, currentSubtitleUrl, currentSubtitleBlob, broadcast]); @@ -2168,7 +2198,7 @@ const MediaPanel = React.forwardRef( isTabActivated={false} {...props} className={containerClassName} - handleCancel={onClickBack} + handleCancel={handleClickBack} spotlightId={spotlightId} > + {(() => { + if (isReadyToPlay) { + console.log('[MediaPanel]-LoadingVideo ๐ŸŽฌ VideoPlayer ๋ Œ๋”๋ง:', { + src: currentPlayingUrl?.substring(0, 50), + disabled: panelInfo.modal, + cannotPlay, + isYoutube, + videoComponent: + (typeof window === 'object' && !window.PalmSystem) || isYoutube + ? 'TReactPlayer' + : 'Media', + }); + } else { + console.log('[MediaPanel]-LoadingVideo ๐Ÿšซ VideoPlayer ๋ Œ๋”๋ง ์Šคํ‚ต๋จ'); + } + return null; + })()} {isReadyToPlay && (