From 728e503976bcf3d365aa7736d5bb3cd247d754bb Mon Sep 17 00:00:00 2001 From: optrader Date: Sat, 25 Oct 2025 09:46:45 +0000 Subject: [PATCH] [251025] feat: usePanelHistory && panelHistoryMiddleware MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit ๐Ÿ• ์ปค๋ฐ‹ ์‹œ๊ฐ„: 2025. 10. 25. 09:46:43 ๐Ÿ“Š ๋ณ€๊ฒฝ ํ†ต๊ณ„: โ€ข ์ด ํŒŒ์ผ: 8๊ฐœ โ€ข ์ถ”๊ฐ€: +195์ค„ โ€ข ์‚ญ์ œ: -84์ค„ ๐Ÿ“ ์ถ”๊ฐ€๋œ ํŒŒ์ผ: + com.twin.app.shoptime/src/actions/panelNavigationActions.js ๐Ÿ“ ์ˆ˜์ •๋œ ํŒŒ์ผ: ~ com.twin.app.shoptime/src/App/App.js ~ com.twin.app.shoptime/src/actions/panelHistoryActions.js ~ com.twin.app.shoptime/src/hooks/usePanelHistory/usePanelHistory.js ~ com.twin.app.shoptime/src/middleware/panelHistoryMiddleware.js ~ com.twin.app.shoptime/src/reducers/panelHistoryReducer.js ~ com.twin.app.shoptime/src/views/HomePanel/BestSeller/BestSeller.jsx ~ com.twin.app.shoptime/src/views/SearchPanel/SearchResultsNew/ItemCard.jsx ๐Ÿ”ง ์ฃผ์š” ๋ณ€๊ฒฝ ๋‚ด์šฉ: โ€ข ํ•ต์‹ฌ ๋น„์ฆˆ๋‹ˆ์Šค ๋กœ์ง ๊ฐœ์„  โ€ข ์ค‘๊ฐ„ ๊ทœ๋ชจ ๊ธฐ๋Šฅ ๊ฐœ์„  โ€ข ๋ชจ๋“ˆ ๊ตฌ์กฐ ๊ฐœ์„  --- com.twin.app.shoptime/src/App/App.js | 12 ++ .../src/actions/panelHistoryActions.js | 11 +- .../src/actions/panelNavigationActions.js | 185 ++++++++++++++++++ .../hooks/usePanelHistory/usePanelHistory.js | 33 ++-- .../src/middleware/panelHistoryMiddleware.js | 128 +++++++++++- .../src/reducers/panelHistoryReducer.js | 61 +++--- .../views/HomePanel/BestSeller/BestSeller.jsx | 1 + .../SearchPanel/SearchResultsNew/ItemCard.jsx | 29 +-- 8 files changed, 378 insertions(+), 82 deletions(-) create mode 100644 com.twin.app.shoptime/src/actions/panelNavigationActions.js diff --git a/com.twin.app.shoptime/src/App/App.js b/com.twin.app.shoptime/src/App/App.js index 36fdebce..abe73fa1 100644 --- a/com.twin.app.shoptime/src/App/App.js +++ b/com.twin.app.shoptime/src/App/App.js @@ -42,6 +42,7 @@ import { getShoptimeTerms } from '../actions/empActions'; import { getHomeMenu, getHomeTerms } from '../actions/homeActions'; import { getMyRecommandedKeyword, getMyUpcomingAlertShow } from '../actions/myPageActions'; import { pushPanel } from '../actions/panelActions'; +import { enqueuePanelHistory } from '../actions/panelHistoryActions'; import NotSupportedVersion from '../components/NotSupportedVersion/NotSupportedVersion'; import ToastContainer from '../components/TToast/ToastContainer'; import usePrevious from '../hooks/usePrevious'; @@ -387,6 +388,17 @@ function AppBase(props) { console.log('[App.js] initService,httpHeaderRef.current', httpHeaderRef.current); console.log('[App.js] haveyInit', haveyInit); + // ์•ฑ ์ดˆ๊ธฐํ™” ์‹œ HomePanel ์ž๋™ ๊ธฐ๋ก + console.log('[App.js] Recording initial HomePanel on app start'); + dispatch(enqueuePanelHistory( + 'homepanel', + {}, + 'APP_INITIALIZE', + new Date().toISOString(), + false, // fromGNB: false (์•ฑ ์ดˆ๊ธฐํ™”) + false // fromResetPanel: false (์•ฑ ์ดˆ๊ธฐํ™”) + )); + if (httpHeaderRef.current) { if (haveyInit) { dispatch(changeAppStatus({ connectionFailed: false })); diff --git a/com.twin.app.shoptime/src/actions/panelHistoryActions.js b/com.twin.app.shoptime/src/actions/panelHistoryActions.js index 44bb0634..f26aa647 100644 --- a/com.twin.app.shoptime/src/actions/panelHistoryActions.js +++ b/com.twin.app.shoptime/src/actions/panelHistoryActions.js @@ -9,16 +9,21 @@ import { types } from './actionTypes'; * Panel์„ ํžˆ์Šคํ† ๋ฆฌ์— ์ถ”๊ฐ€ * @param {string} panelName - Panel ์ด๋ฆ„ * @param {Object} panelInfo - Panel ์ƒํƒœ ์ •๋ณด - * @param {string} action - ๋ฐœ์ƒํ•œ Redux ์•ก์…˜ ('PUSH' | 'POP' | 'UPDATE' | 'RESET') + * @param {string} action - ๋ฐœ์ƒํ•œ Redux ์•ก์…˜ ('PUSH' | 'POP' | 'UPDATE' | 'RESET' | 'APP_INITIALIZE') + * @param {string} timestamp - ํƒ€์ž„์Šคํƒฌํ”„ (์„ ํƒ์‚ฌํ•ญ) + * @param {boolean} fromGNB - GNB๋ฅผ ํ†ตํ•ด ์ด๋™ํ–ˆ๋Š”์ง€ ์—ฌ๋ถ€ + * @param {boolean} fromResetPanel - RESET_PANELS๋ฅผ ํ†ตํ•ด ์ด๋™ํ–ˆ๋Š”์ง€ ์—ฌ๋ถ€ * @returns {Object} Redux action */ -export const enqueuePanelHistory = (panelName, panelInfo = {}, action = 'PUSH') => ({ +export const enqueuePanelHistory = (panelName, panelInfo = {}, action = 'PUSH', timestamp, fromGNB = false, fromResetPanel = false) => ({ type: types.ENQUEUE_PANEL_HISTORY, payload: { panelName, panelInfo, action, - timestamp: new Date().toISOString(), + timestamp: timestamp || new Date().toISOString(), + fromGNB, + fromResetPanel, }, }); diff --git a/com.twin.app.shoptime/src/actions/panelNavigationActions.js b/com.twin.app.shoptime/src/actions/panelNavigationActions.js new file mode 100644 index 00000000..69efe36a --- /dev/null +++ b/com.twin.app.shoptime/src/actions/panelNavigationActions.js @@ -0,0 +1,185 @@ +/** + * src/actions/panelNavigationActions.js + * Panel navigation ์ˆœ์ฐจ ์ฒ˜๋ฆฌ๋ฅผ ์œ„ํ•œ ์•ก์…˜ ํฌ๋ฆฌ์—์ดํ„ฐ + * + * Chrome 68 ํ˜ธํ™˜์„ฑ์„ ์œ„ํ•œ callback-free ์ˆœ์ฐจ ๋„ค๋น„๊ฒŒ์ด์…˜ + */ + +import { pushPanel, updatePanel } from './panelActions'; +import { panel_names } from '../utils/Config'; + +/** + * ์ƒํ’ˆ ํด๋ฆญ ์‹œ ์ˆœ์ฐจ ๋„ค๋น„๊ฒŒ์ด์…˜ (Search โ†’ Detail) + * @param {string} patnrId - ํŒŒํŠธ๋„ˆ ID + * @param {string} prdtId - ์ƒํ’ˆ ID + * @param {string} searchQuery - ๊ฒ€์ƒ‰์–ด + * @param {string} currentSpot - ํ˜„์žฌ spotlight ID + * @param {Object} additionalInfo - ์ถ”๊ฐ€ ํŒจ๋„ ์ •๋ณด + * @returns {Function} Redux thunk function + */ +export const navigateToDetailPanel = ( + patnrId, + prdtId, + searchQuery, + currentSpot, + additionalInfo = {} +) => (dispatch, getState) => { + // ํ˜„์žฌ ์ƒํƒœ์—์„œ lastPanelAction ์นด์šดํŠธ ์ €์žฅ + const currentActionCount = getState().panels.lastPanelAction || 0; + + console.log('[PanelNavigation] Starting navigation to detail:', { + patnrId, + prdtId, + searchQuery, + currentSpot, + currentActionCount + }); + + // 1. ๋จผ์ € ํ˜„์žฌ ํŒจ๋„(์˜ˆ: SearchPanel) ์—…๋ฐ์ดํŠธ + dispatch(updatePanel({ + name: panel_names.SEARCH_PANEL, + panelInfo: { + searchVal: searchQuery, + currentSpot, + tab: 0, + ...additionalInfo + } + })); + + // 2. Redux store ๊ตฌ๋…ํ•˜์—ฌ ์ƒํƒœ ๋ณ€ํ™” ๊ฐ์ง€ + // ์ง์ ‘ store ์ ‘๊ทผ ๋Œ€์‹  ํƒ€์ด๋จธ ๊ธฐ๋ฐ˜ ๋ฐฉ์‹ ์‚ฌ์šฉ (Chrome 68 ํ˜ธํ™˜) + const storeUnsubscribe = (() => { + let isUnsubscribed = false; + + const checkStateChange = () => { + if (isUnsubscribed) return; + + const newState = getState(); + const newActionCount = newState.panels.lastPanelAction || 0; + + // updatePanel์ด ์™„๋ฃŒ๋˜๋ฉด (action count๊ฐ€ ๋ณ€๊ฒฝ๋˜๋ฉด) + if (newActionCount !== currentActionCount) { + console.log('[PanelNavigation] UpdatePanel completed, pushing DetailPanel'); + + // ๊ตฌ๋… ํ•ด์ œ + isUnsubscribed = true; + + // 3. DetailPanel push + dispatch(pushPanel({ + name: panel_names.DETAIL_PANEL, + panelInfo: { + patnrId, + prdtId, + fromSearch: true, + searchQuery, + ...additionalInfo + } + })); + } + }; + + // ์ฆ‰์‹œ ํ•œ๋ฒˆ ํ™•์ธํ•˜๊ณ , ๊ทธ ํ›„ ์ฃผ๊ธฐ์ ์œผ๋กœ ํ™•์ธ + setTimeout(checkStateChange, 0); + const intervalId = setInterval(checkStateChange, 16); // 60fps + + return () => { + isUnsubscribed = true; + clearInterval(intervalId); + }; + })(); + + // ํƒ€์ž„์•„์›ƒ ๋ฐฉ์–ด (์ตœ๋Œ€ 1์ดˆ ๋Œ€๊ธฐ) + setTimeout(() => { + storeUnsubscribe(); + console.log('[PanelNavigation] Timeout fallback, pushing DetailPanel'); + + dispatch(pushPanel({ + name: panel_names.DETAIL_PANEL, + panelInfo: { + patnrId, + prdtId, + fromSearch: true, + searchQuery, + ...additionalInfo + } + })); + }, 1000); +}; + +/** + * HomePanel์—์„œ ์ƒํ’ˆ ํด๋ฆญ ์‹œ ์ˆœ์ฐจ ๋„ค๋น„๊ฒŒ์ด์…˜ + * @param {string} patnrId - ํŒŒํŠธ๋„ˆ ID + * @param {string} prdtId - ์ƒํ’ˆ ID + * @param {Object} additionalInfo - ์ถ”๊ฐ€ ํŒจ๋„ ์ •๋ณด + * @returns {Function} Redux thunk function + */ +export const navigateToDetailFromHome = ( + patnrId, + prdtId, + additionalInfo = {} +) => (dispatch, getState) => { + const currentActionCount = getState().panels.lastPanelAction || 0; + + console.log('[PanelNavigation] Starting navigation from home:', { + patnrId, + prdtId, + currentActionCount + }); + + dispatch(updatePanel({ + name: panel_names.HOME_PANEL, + panelInfo: { + lastSelectedProduct: { patnrId, prdtId }, + ...additionalInfo + } + })); + + const storeUnsubscribe = (() => { + let isUnsubscribed = false; + + const checkStateChange = () => { + if (isUnsubscribed) return; + + const newState = getState(); + const newActionCount = newState.panels.lastPanelAction || 0; + + if (newActionCount !== currentActionCount) { + console.log('[PanelNavigation] HomePanel update completed, pushing DetailPanel'); + isUnsubscribed = true; + + dispatch(pushPanel({ + name: panel_names.DETAIL_PANEL, + panelInfo: { + patnrId, + prdtId, + fromHome: true, + ...additionalInfo + } + })); + } + }; + + setTimeout(checkStateChange, 0); + const intervalId = setInterval(checkStateChange, 16); + + return () => { + isUnsubscribed = true; + clearInterval(intervalId); + }; + })(); + + setTimeout(() => { + storeUnsubscribe(); + console.log('[PanelNavigation] Timeout fallback from home'); + + dispatch(pushPanel({ + name: panel_names.DETAIL_PANEL, + panelInfo: { + patnrId, + prdtId, + fromHome: true, + ...additionalInfo + } + })); + }, 1000); +}; \ No newline at end of file diff --git a/com.twin.app.shoptime/src/hooks/usePanelHistory/usePanelHistory.js b/com.twin.app.shoptime/src/hooks/usePanelHistory/usePanelHistory.js index 0f9b12b1..c647a6c9 100644 --- a/com.twin.app.shoptime/src/hooks/usePanelHistory/usePanelHistory.js +++ b/com.twin.app.shoptime/src/hooks/usePanelHistory/usePanelHistory.js @@ -35,17 +35,11 @@ export const usePanelHistory = () => { * [current, previous, older, oldest, ...] */ const getHistory = useCallback(() => { - const { history, head, size } = panelHistory; - if (size === 0) return []; + const { history } = panelHistory; + if (history.length === 0) return []; - const sorted = []; - for (let i = 0; i < size; i++) { - const index = (head - i + 10) % 10; - if (history[index]) { - sorted.push(history[index]); - } - } - return sorted; + // history๋Š” ์˜ค๋ž˜๋œ ์ˆœ์„œ์ด๋ฏ€๋กœ, ์ตœ์‹  ์ˆœ์œผ๋กœ ๋’ค์ง‘๊ธฐ + return [...history].reverse(); }, [panelHistory]); /** @@ -55,10 +49,11 @@ export const usePanelHistory = () => { */ const getHistoryAt = useCallback( (distance) => { - const { history, head, size } = panelHistory; - if (distance >= size || distance < 0) return null; + const { history } = panelHistory; + if (distance >= history.length || distance < 0) return null; - const index = (head - distance + 10) % 10; + // history๋Š” ์˜ค๋ž˜๋œ ์ˆœ์„œ, distance๋Š” ์ตœ์‹ ๋ถ€ํ„ฐ ๊ณ„์‚ฐ + const index = history.length - 1 - distance; return history[index] || null; }, [panelHistory] @@ -76,7 +71,7 @@ export const usePanelHistory = () => { return { pattern: 'no-panel', confidence: 0 }; } - // ๐Ÿ”ฝ Back navigation: A โ†’ B โ†’ A + // Back navigation: A โ†’ B โ†’ A if ( current && older && @@ -92,7 +87,7 @@ export const usePanelHistory = () => { }; } - // ๐Ÿ”ฝ Detail flow: Search/Home โ†’ Detail + // Detail flow: Search/Home โ†’ Detail if (current.panelName === 'detailpanel' && previous) { if (previous.panelName === 'searchpanel') { return { @@ -108,7 +103,7 @@ export const usePanelHistory = () => { } } - // ๐Ÿ”ฝ Modal overlay: base โ†’ modal + // Modal overlay: base โ†’ modal const modalPanels = ['imagepanel', 'mediapanel', 'playerpanel']; if (modalPanels.includes(current.panelName) && previous) { return { @@ -119,7 +114,7 @@ export const usePanelHistory = () => { }; } - // ๐Ÿ”ฝ Search flow: Home โ†’ Search + // Search flow: Home โ†’ Search if (current.panelName === 'searchpanel' && previous?.panelName === 'homepanel') { return { pattern: 'home-to-search', @@ -127,7 +122,7 @@ export const usePanelHistory = () => { }; } - // ๐Ÿ”ฝ Cart flow: Product โ†’ Cart + // Cart flow: Product โ†’ Cart if (current.panelName === 'cartpanel' && previous) { return { pattern: 'product-to-cart', @@ -135,7 +130,7 @@ export const usePanelHistory = () => { }; } - // ๐Ÿ”ฝ Checkout flow: Cart โ†’ Checkout + // Checkout flow: Cart โ†’ Checkout if (current.panelName === 'checkoutpanel' && previous?.panelName === 'cartpanel') { return { pattern: 'cart-to-checkout', diff --git a/com.twin.app.shoptime/src/middleware/panelHistoryMiddleware.js b/com.twin.app.shoptime/src/middleware/panelHistoryMiddleware.js index 473d6315..380141e5 100644 --- a/com.twin.app.shoptime/src/middleware/panelHistoryMiddleware.js +++ b/com.twin.app.shoptime/src/middleware/panelHistoryMiddleware.js @@ -3,7 +3,7 @@ * Panel history ์ž๋™ ์ถ”์  middleware * * Panel action (PUSH, POP, UPDATE, RESET)์„ ๊ฐ์ง€ํ•˜๊ณ  - * ์ž๋™์œผ๋กœ panel history์— ๊ธฐ๋กํ•ฉ๋‹ˆ๋‹ค. + * ์ž๋™์œผ๋กœ panel history์— ๊ธฐ๋ก */ import { types } from '../actions/actionTypes'; @@ -17,6 +17,37 @@ import { enqueuePanelHistory, clearPanelHistory } from '../actions/panelHistoryA * @returns {Function} middleware function */ export const panelHistoryMiddleware = (store) => (next) => (action) => { + // ๋ชจ๋“  PANEL ๊ด€๋ จ ์•ก์…˜ ๋กœ๊น… + if (action.type && ( + action.type.includes('PANEL') || + action.type === 'CLEAR_PANEL_HISTORY' + )) { + const caller = new Error().stack.split('\n')[1]?.trim(); + console.log(`[PANEL DEBUG] ${action.type} from: ${caller}`); + console.log(' Payload:', action.payload); + } + + // GNB ํ˜ธ์ถœ ์‹๋ณ„์„ ์œ„ํ•œ helper ํ•จ์ˆ˜ + const isGNBCall = () => { + const stack = new Error().stack; + if (!stack) return false; + + const stackLines = stack.split('\n'); + for (const line of stackLines) { + if (line.includes('TabLayout.jsx') || + line.includes('TIconButton.jsx')) { + return true; + } + } + return false; + }; + + // ์ž„์‹œ: CLEAR_PANEL_HISTORY ๋ฌด๋ ฅํ™” + if (action.type === 'CLEAR_PANEL_HISTORY') { + console.log('[PANEL DEBUG] CLEAR_PANEL_HISTORY BLOCKED!'); + return action; // ์•ก์…˜์„ ์ „๋‹ฌํ•˜์ง€ ์•Š์Œ + } + // ์›๋ž˜ ์•ก์…˜ ์‹คํ–‰ const result = next(action); @@ -32,7 +63,19 @@ export const panelHistoryMiddleware = (store) => (next) => (action) => { const { name: panelName, panelInfo = {} } = action.payload; if (panelName) { - store.dispatch(enqueuePanelHistory(panelName, panelInfo, 'PUSH')); + const isGNB = isGNBCall(); + console.log('[PANEL] PUSH_PANEL:', { panelName, panelInfo, isGNB, timestamp: new Date().toISOString() }); + store.dispatch(enqueuePanelHistory(panelName, panelInfo, 'PUSH', new Date().toISOString(), isGNB, false)); + + // PanelHistory ์ƒํƒœ ๋กœ๊ทธ (state ์—…๋ฐ์ดํŠธ ํ›„) + const logPanelHistoryAfter = () => { + const stateAfter = store.getState(); + const panelHistoryAfter = stateAfter.panelHistory; + console.log('[PANEL_HISTORY] After PUSH_PANEL:', panelHistoryAfter); + }; + + // state ์—…๋ฐ์ดํŠธ๊ฐ€ ์™„๋ฃŒ๋œ ํ›„ ๋กœ๊ทธ + Promise.resolve().then(logPanelHistoryAfter); } } break; @@ -44,7 +87,19 @@ export const panelHistoryMiddleware = (store) => (next) => (action) => { if (panels.length > 0) { const topPanel = panels[panels.length - 1]; if (topPanel && topPanel.name) { - store.dispatch(enqueuePanelHistory(topPanel.name, topPanel.panelInfo || {}, 'POP')); + const isGNB = isGNBCall(); + console.log('[PANEL] POP_PANEL:', { panelName: topPanel.name, panelInfo: topPanel.panelInfo || {}, isGNB, timestamp: new Date().toISOString() }); + store.dispatch(enqueuePanelHistory(topPanel.name, topPanel.panelInfo || {}, 'POP', new Date().toISOString(), isGNB, false)); + + // PanelHistory ์ƒํƒœ ๋กœ๊ทธ (state ์—…๋ฐ์ดํŠธ ํ›„) + const logPanelHistoryAfter = () => { + const stateAfter = store.getState(); + const panelHistoryAfter = stateAfter.panelHistory; + console.log('[PANEL_HISTORY] After POP_PANEL:', panelHistoryAfter); + }; + + // state ์—…๋ฐ์ดํŠธ๊ฐ€ ์™„๋ฃŒ๋œ ํ›„ ๋กœ๊ทธ + Promise.resolve().then(logPanelHistoryAfter); } } break; @@ -56,15 +111,76 @@ export const panelHistoryMiddleware = (store) => (next) => (action) => { const { name: panelName, panelInfo = {} } = action.payload; if (panelName) { - store.dispatch(enqueuePanelHistory(panelName, panelInfo, 'UPDATE')); + const isGNB = isGNBCall(); + console.log('[PANEL] UPDATE_PANEL:', { panelName, panelInfo, isGNB, timestamp: new Date().toISOString() }); + store.dispatch(enqueuePanelHistory(panelName, panelInfo, 'UPDATE', new Date().toISOString(), isGNB, false)); + + // PanelHistory ์ƒํƒœ ๋กœ๊ทธ (state ์—…๋ฐ์ดํŠธ ํ›„) + const logPanelHistoryAfter = () => { + const stateAfter = store.getState(); + const panelHistoryAfter = stateAfter.panelHistory; + console.log('[PANEL_HISTORY] After UPDATE_PANEL:', panelHistoryAfter); + }; + + // state ์—…๋ฐ์ดํŠธ๊ฐ€ ์™„๋ฃŒ๋œ ํ›„ ๋กœ๊ทธ + Promise.resolve().then(logPanelHistoryAfter); } } break; } - // RESET_PANELS: ํžˆ์Šคํ† ๋ฆฌ ์ดˆ๊ธฐํ™” + // RESET_PANELS: GNB ๋„ค๋น„๊ฒŒ์ด์…˜ ๋˜๋Š” ์™„์ „ ์ดˆ๊ธฐํ™” case types.RESET_PANELS: { - store.dispatch(clearPanelHistory()); + console.log('[PANEL] RESET_PANELS:', { + payload: action.payload, + timestamp: new Date().toISOString() + }); + console.log('[PANEL_HISTORY] Before RESET_PANELS:', store.getState().panelHistory); + + // ๋ชจ๋“  RESET_PANELS๋ฅผ ๊ธฐ๋ก + const isGNB = isGNBCall(); + + if (action.payload && action.payload.length > 0) { + // payload๊ฐ€ ์žˆ๋Š” ๊ฒฝ์šฐ (GNB ํƒญ, ํŒจ๋„ ๋‚ด ๋„ค๋น„๊ฒŒ์ด์…˜ ๋“ฑ) + const firstPanel = action.payload[0]; + if (firstPanel && firstPanel.name) { + console.log('[PANEL_DEBUG] RESET_PANELS to:', firstPanel.name, { isGNB, fromResetPanel: true }); + // RESET ์ด๋™์„ ํžˆ์Šคํ† ๋ฆฌ์— ๊ธฐ๋ก + store.dispatch(enqueuePanelHistory( + firstPanel.name, + firstPanel.panelInfo || {}, + 'RESET', + new Date().toISOString(), + isGNB, // TabLayout/TIconButton์ด๋ฉด true + true // fromResetPanel: ํ•ญ์ƒ true + )); + } + } else { + // ์™„์ „ ์ดˆ๊ธฐํ™” (payload๊ฐ€ ์—†๋Š” ๊ฒฝ์šฐ - HomePanel, ์•ฑ ์ดˆ๊ธฐํ™” ๋“ฑ) + console.log('[PANEL_DEBUG] Complete panel history reset (payload empty)', { isGNB, fromResetPanel: true }); + store.dispatch(clearPanelHistory()); + + // HomePanel ์ดˆ๊ธฐํ™” ๊ธฐ๋ก (์•ฑ ์‹œ์ž‘ ์‹œ) + console.log('[PANEL_DEBUG] Recording initial HomePanel'); + store.dispatch(enqueuePanelHistory( + 'homepanel', + {}, + 'APP_INITIALIZE', + new Date().toISOString(), + isGNB, // TIconButton Home ๋ฒ„ํŠผ์ด๋ฉด true + true // fromResetPanel: true + )); + } + + // PanelHistory ์ƒํƒœ ๋กœ๊ทธ (์ดˆ๊ธฐํ™” ํ›„) + const logPanelHistoryAfter = () => { + const stateAfter = store.getState(); + const panelHistoryAfter = stateAfter.panelHistory; + console.log('[PANEL_HISTORY] After RESET_PANELS:', panelHistoryAfter); + }; + + // state ์—…๋ฐ์ดํŠธ๊ฐ€ ์™„๋ฃŒ๋œ ํ›„ ๋กœ๊ทธ + Promise.resolve().then(logPanelHistoryAfter); break; } diff --git a/com.twin.app.shoptime/src/reducers/panelHistoryReducer.js b/com.twin.app.shoptime/src/reducers/panelHistoryReducer.js index c282a0de..45992716 100644 --- a/com.twin.app.shoptime/src/reducers/panelHistoryReducer.js +++ b/com.twin.app.shoptime/src/reducers/panelHistoryReducer.js @@ -10,51 +10,49 @@ import { types } from '../actions/actionTypes'; // ์ดˆ๊ธฐ ์ƒํƒœ const initialState = { - history: new Array(10).fill(null), // 10๊ฐœ ring buffer - head: -1, // ํ˜„์žฌ ์œ„์น˜ (-1 ~ 9) - size: 0, // ํ˜„์žฌ ์ €์žฅ๋œ ๊ฐœ์ˆ˜ (0 ~ 10) + history: [], // ์ตœ๋Œ€ 10๊ฐœ์˜ ํžˆ์Šคํ† ๋ฆฌ ๋ฐฐ์—ด (์˜ค๋ž˜๋œ ์ˆœ์„œ) current: null, // ํ˜„์žฌ panel entry previous: null, // ์ด์ „ panel entry hasTransition: false, // panel ์ „ํ™˜ ์—ฌ๋ถ€ }; /** - * Panel์„ Ring buffer์— ์ถ”๊ฐ€ + * Panel์„ ํžˆ์Šคํ† ๋ฆฌ ๋ฐฐ์—ด์— ์ถ”๊ฐ€ * @param {Object} state - ํ˜„์žฌ ์ƒํƒœ * @param {string} panelName - ์ถ”๊ฐ€ํ•  panel ์ด๋ฆ„ * @param {Object} panelInfo - panel ์ •๋ณด * @param {string} action - Redux ์•ก์…˜ ํƒ€์ž… ('PUSH' | 'POP' | 'UPDATE') * @param {string} timestamp - ISO 8601 timestamp + * @param {boolean} fromGNB - GNB๋ฅผ ํ†ตํ•ด ์ด๋™ํ–ˆ๋Š”์ง€ ์—ฌ๋ถ€ + * @param {boolean} fromResetPanel - RESET_PANELS๋ฅผ ํ†ตํ•ด ์ด๋™ํ–ˆ๋Š”์ง€ ์—ฌ๋ถ€ * @returns {Object} ์ƒˆ๋กœ์šด ์ƒํƒœ */ -const enqueuePanel = (state, panelName, panelInfo, action, timestamp) => { - // State ๋ถˆ๋ณ€์„ฑ ์œ ์ง€ - const newHistory = state.history.map((item) => item); - - // ๋‹ค์Œ ์œ„์น˜ ๊ณ„์‚ฐ (ring buffer ์ˆœํ™˜) - const newHead = (state.head + 1) % 10; - +const enqueuePanel = (state, panelName, panelInfo, action, timestamp, fromGNB = false, fromResetPanel = false) => { // ์ƒˆ entry ์ƒ์„ฑ const newEntry = { panelName, panelInfo: { ...panelInfo }, // Deep copy timestamp, action, + fromGNB, // GNB ์ด๋™ ์—ฌ๋ถ€ + fromResetPanel, // RESET_PANELS ์ด๋™ ์—ฌ๋ถ€ }; - // Ring buffer์— ์ €์žฅ - newHistory[newHead] = newEntry; - - // Size ์—…๋ฐ์ดํŠธ (์ตœ๋Œ€ 10) - const newSize = Math.min(state.size + 1, 10); + // ์ตœ๋Œ€ 10๊ฐœ ์œ ์ง€ํ•˜๋ฉฐ ์ƒˆ๋กœ์šด ํžˆ์Šคํ† ๋ฆฌ ์ƒ์„ฑ + let newHistory; + if (state.history.length >= 10) { + // 10๊ฐœ๊ฐ€ ์ฐจ๋ฉด ์ฒซ ๋ฒˆ์งธ ์š”์†Œ ์ œ๊ฑฐํ•˜๊ณ  ์ถ”๊ฐ€ + newHistory = [...state.history.slice(1), newEntry]; + } else { + // 10๊ฐœ ๋ฏธ๋งŒ์ด๋ฉด ๊ทธ๋ƒฅ ์ถ”๊ฐ€ + newHistory = [...state.history, newEntry]; + } // ์ด์ „ panel ์ถ”์ถœ const previousEntry = state.current; return { history: newHistory, - head: newHead, - size: newSize, current: newEntry, previous: previousEntry, hasTransition: previousEntry !== null && previousEntry.panelName !== newEntry.panelName, @@ -68,7 +66,7 @@ export const panelHistoryReducer = (state = initialState, action) => { switch (action.type) { // ๐Ÿ”ฝ Panel history์— ์ƒˆ entry ์ถ”๊ฐ€ case types.ENQUEUE_PANEL_HISTORY: { - const { panelName, panelInfo = {}, action: actionType, timestamp } = action.payload; + const { panelName, panelInfo = {}, action: actionType, timestamp, fromGNB = false, fromResetPanel = false } = action.payload; // ์ž…๋ ฅ๊ฐ’ ๊ฒ€์ฆ if (!panelName || typeof panelName !== 'string') { @@ -85,12 +83,14 @@ export const panelHistoryReducer = (state = initialState, action) => { panelInfo: { ...panelInfo }, // panelInfo๋งŒ ์—…๋ฐ์ดํŠธ timestamp, action: actionType, // ์ตœ์‹  action ๊ธฐ๋ก + fromGNB: fromGNB || state.current.fromGNB, // GNB ํ”Œ๋ž˜๊ทธ ์œ ์ง€ ๋˜๋Š” ์—…๋ฐ์ดํŠธ + fromResetPanel: fromResetPanel || state.current.fromResetPanel, // ResetPanel ํ”Œ๋ž˜๊ทธ ์œ ์ง€ ๋˜๋Š” ์—…๋ฐ์ดํŠธ }, }; } // ๐Ÿ”ฝ ๋‹ค๋ฅธ panelName์ด๋ฉด ์ƒˆ๋กœ์šด ๋ฒ„ํผ ํ•ญ๋ชฉ ์ถ”๊ฐ€ - return enqueuePanel(state, panelName, panelInfo, actionType, timestamp); + return enqueuePanel(state, panelName, panelInfo, actionType, timestamp, fromGNB, fromResetPanel); } // ๐Ÿ”ฝ History ์ดˆ๊ธฐํ™” @@ -98,10 +98,8 @@ export const panelHistoryReducer = (state = initialState, action) => { return { ...initialState }; } - // ๐Ÿ”ฝ Panel reset ์‹œ history๋„ ๋ฆฌ์…‹ - case types.RESET_PANELS: { - return { ...initialState }; - } + // ๐Ÿ”ฝ RESET_PANELS๋Š” middleware์—์„œ ์ง์ ‘ ์ฒ˜๋ฆฌํ•˜๋ฏ€๋กœ ์—ฌ๊ธฐ์„œ๋Š” ์ดˆ๊ธฐํ™”ํ•˜์ง€ ์•Š์Œ + // GNB ๋„ค๋น„๊ฒŒ์ด์…˜์„ ์œ„ํ•ด ํžˆ์Šคํ† ๋ฆฌ๋ฅผ ์œ ์ง€ํ•ด์•ผ ํ•จ // ๐Ÿ”ฝ Panel history ๋ช…์‹œ์  ๋ฆฌ์…‹ case types.RESET_PANEL_HISTORY: { @@ -120,18 +118,11 @@ export const selectPanelHistory = (state) => state.panelHistory; export const selectCurrentPanel = (state) => state.panelHistory.current; export const selectPreviousPanel = (state) => state.panelHistory.previous; export const selectPanelHistoryList = (state) => { - const { history, head, size } = state.panelHistory; - if (size === 0) return []; + const { history } = state.panelHistory; + if (history.length === 0) return []; - // ์ตœ์‹ ์ˆœ์œผ๋กœ ์ •๋ ฌ๋œ ํžˆ์Šคํ† ๋ฆฌ ๋ฐ˜ํ™˜ - const sorted = []; - for (let i = 0; i < size; i++) { - const index = (head - i + 10) % 10; - if (history[index]) { - sorted.push(history[index]); - } - } - return sorted; + // ์ตœ์‹ ์ˆœ์œผ๋กœ ์ •๋ ฌ๋œ ํžˆ์Šคํ† ๋ฆฌ ๋ฐ˜ํ™˜ (์˜ค๋ž˜๋œ ์ˆœ์„œ๋ฅผ ๋’ค์ง‘๊ธฐ) + return [...history].reverse(); }; export default panelHistoryReducer; diff --git a/com.twin.app.shoptime/src/views/HomePanel/BestSeller/BestSeller.jsx b/com.twin.app.shoptime/src/views/HomePanel/BestSeller/BestSeller.jsx index 7a2f6428..b022b460 100644 --- a/com.twin.app.shoptime/src/views/HomePanel/BestSeller/BestSeller.jsx +++ b/com.twin.app.shoptime/src/views/HomePanel/BestSeller/BestSeller.jsx @@ -20,6 +20,7 @@ import { pushPanel, updatePanel, } from '../../../actions/panelActions'; +import { navigateToDetailFromHome } from '../../../actions/panelNavigationActions'; import SectionTitle from '../../../components/SectionTitle/SectionTitle'; import Tag from '../../../components/TItemCard/Tag'; import TItemCard from '../../../components/TItemCard/TItemCard'; diff --git a/com.twin.app.shoptime/src/views/SearchPanel/SearchResultsNew/ItemCard.jsx b/com.twin.app.shoptime/src/views/SearchPanel/SearchResultsNew/ItemCard.jsx index b759d688..c1f97ff0 100644 --- a/com.twin.app.shoptime/src/views/SearchPanel/SearchResultsNew/ItemCard.jsx +++ b/com.twin.app.shoptime/src/views/SearchPanel/SearchResultsNew/ItemCard.jsx @@ -5,6 +5,7 @@ import { useDispatch } from 'react-redux'; import Spotlight from '@enact/spotlight'; import { pushPanel, updatePanel } from '../../../actions/panelActions'; +import { navigateToDetailPanel } from '../../../actions/panelNavigationActions'; import TItemCardNew from '../../../components/TItemCard/TItemCard.new'; import TScroller from '../../../components/TScroller/TScroller'; import { panel_names } from '../../../utils/Config'; @@ -19,25 +20,15 @@ const ItemCard = ({ onClick, itemInfo, searchQuery }) => { if (onClick) { onClick(ev); } - dispatch( - updatePanel({ - name: panel_names.SEARCH_PANEL, - panelInfo: { - searchVal: searchQuery, - currentSpot, - tab: 0, - }, - }) - ); - dispatch( - pushPanel({ - name: panel_names.DETAIL_PANEL, - panelInfo: { - patnrId: patnrId, - prdtId: prdtId, - }, - }) - ); + + // ์ˆœ์ฐจ ๋„ค๋น„๊ฒŒ์ด์…˜ ์‚ฌ์šฉ (Chrome 68 ํ˜ธํ™˜) + dispatch(navigateToDetailPanel( + patnrId, + prdtId, + searchQuery, + currentSpot, + { tab: 0 } + )); }, [onClick, dispatch, searchQuery] );