Files
shoptime/com.twin.app.shoptime/src/middleware/panelHistoryMiddleware.js

320 lines
11 KiB
JavaScript

/**
* src/middleware/panelHistoryMiddleware.js
* Panel history 자동 추적 middleware
*
* Panel action (PUSH, POP, UPDATE, RESET)을 감지하고
* 자동으로 panel history에 기록
*
* ⚠️ [251122] DEBUG_MODE = false로 설정되어 모든 로그 출력 비활성화됨
* - 로그가 필요하면 DEBUG_MODE를 true로 변경하면 됨
* - middleware 동작 자체는 영향받지 않음 (로그만 출력 안됨)
*/
import { types } from '../actions/actionTypes';
import { enqueuePanelHistory, clearPanelHistory } from '../actions/panelHistoryActions';
import { calculateIsPanelOnTop } from '../utils/panelUtils'; // 🎯 isOnTop 유틸리티 함수 import
// DEBUG_MODE - true인 경우에만 로그 출력
// ⚠️ [251122] panelHistory 로그 비활성화 - 로그 생성 차단
const DEBUG_MODE = true;
/**
* Panel history middleware
* 모든 panel action을 감지하여 panelHistory reducer로 자동 기록
*
* @param {Object} store - Redux store
* @returns {Function} middleware function
*/
export const panelHistoryMiddleware = (store) => (next) => (action) => {
// 모든 PANEL 관련 액션 로깅
if (
DEBUG_MODE &&
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') {
if (DEBUG_MODE) console.log('[PANEL DEBUG] CLEAR_PANEL_HISTORY BLOCKED!');
return action; // 액션을 전달하지 않음
}
// 원래 액션 실행
const result = next(action);
// Panel action 후 history 업데이트
const state = store.getState();
const panels = state.panels?.panels || [];
// 🎯 [isOnTop 계산] 유틸리티 함수 사용 (MainView 로직과 정확히 일치)
// calculateIsPanelOnTop 함수는 MainView의 renderTopPanel 로직을 기반으로 정확한 isOnTop 상태 계산
const calculateIsOnTop = (targetPanelName) => {
return calculateIsPanelOnTop(panels, targetPanelName);
};
try {
switch (action.type) {
// PUSH_PANEL: 새 panel 추가
case types.PUSH_PANEL: {
if (action.payload) {
const { name: panelName, panelInfo = {} } = action.payload;
if (panelName) {
const isGNB = isGNBCall();
const isOnTop = calculateIsOnTop(panelName); // 🎯 isOnTop 계산
if (DEBUG_MODE)
console.log('[PANEL] PUSH_PANEL:', {
panelName,
panelInfo,
isGNB,
isOnTop,
timestamp: new Date().toISOString(),
});
store.dispatch(
enqueuePanelHistory(
panelName,
panelInfo,
'PUSH',
new Date().toISOString(),
isGNB,
false,
isOnTop
)
);
// PanelHistory 상태 로그 (state 업데이트 후)
const logPanelHistoryAfter = () => {
if (DEBUG_MODE) {
const stateAfter = store.getState();
const panelHistoryAfter = stateAfter.panelHistory;
const panelsAfter = stateAfter.panels.panels;
console.log('[PANEL_HISTORY] After PUSH_PANEL:', {
panelHistory: panelHistoryAfter,
panels: panelsAfter,
});
}
};
// state 업데이트가 완료된 후 로그
Promise.resolve().then(logPanelHistoryAfter);
}
}
break;
}
// POP_PANEL: panel 제거 후 이전 panel 기록
case types.POP_PANEL: {
// POP 후 top panel을 기록 (이전 패널로 돌아감)
if (panels.length > 0) {
const topPanel = panels[panels.length - 1];
if (DEBUG_MODE) {
console.log('[PANEL-TRACE] POP_PANEL middleware stack', {
stack: panels.map((p) => p.name),
topPanel: topPanel?.name,
payload: action.payload,
caller: new Error().stack?.split('\n')[2]?.trim(),
});
}
if (topPanel && topPanel.name) {
const isGNB = isGNBCall();
const isOnTop = calculateIsOnTop(topPanel.name); // 🎯 isOnTop 계산
if (DEBUG_MODE)
console.log('[PANEL] POP_PANEL:', {
panelName: topPanel.name,
panelInfo: topPanel.panelInfo || {},
isGNB,
isOnTop,
timestamp: new Date().toISOString(),
});
store.dispatch(
enqueuePanelHistory(
topPanel.name,
topPanel.panelInfo || {},
'POP',
new Date().toISOString(),
isGNB,
false,
isOnTop
)
);
// PanelHistory 상태 로그 (state 업데이트 후)
const logPanelHistoryAfter = () => {
if (DEBUG_MODE) {
const stateAfter = store.getState();
const panelHistoryAfter = stateAfter.panelHistory;
const panelsAfter = stateAfter.panels.panels;
console.log('[PANEL_HISTORY] After POP_PANEL:', {
panelHistory: panelHistoryAfter,
panels: panelsAfter,
});
}
};
// state 업데이트가 완료된 후 로그
Promise.resolve().then(logPanelHistoryAfter);
}
}
break;
}
// UPDATE_PANEL: panel 정보 업데이트 기록
case types.UPDATE_PANEL: {
if (action.payload) {
const { name: panelName, panelInfo = {} } = action.payload;
if (panelName) {
const isGNB = isGNBCall();
const isOnTop = calculateIsOnTop(panelName); // 🎯 isOnTop 계산
if (DEBUG_MODE)
console.log('[PANEL] UPDATE_PANEL:', {
panelName,
panelInfo,
isGNB,
isOnTop,
timestamp: new Date().toISOString(),
});
store.dispatch(
enqueuePanelHistory(
panelName,
panelInfo,
'UPDATE',
new Date().toISOString(),
isGNB,
false,
isOnTop
)
);
// PanelHistory 상태 로그 (state 업데이트 후)
const logPanelHistoryAfter = () => {
if (DEBUG_MODE) {
const stateAfter = store.getState();
const panelHistoryAfter = stateAfter.panelHistory;
const panelsAfter = stateAfter.panels.panels;
console.log('[PANEL_HISTORY] After UPDATE_PANEL:', {
panelHistory: panelHistoryAfter,
panels: panelsAfter,
});
}
};
// state 업데이트가 완료된 후 로그
Promise.resolve().then(logPanelHistoryAfter);
}
}
break;
}
// RESET_PANELS: GNB 네비게이션 또는 완전 초기화
case types.RESET_PANELS: {
if (DEBUG_MODE)
console.log('[PANEL] RESET_PANELS:', {
payload: action.payload,
timestamp: new Date().toISOString(),
});
if (DEBUG_MODE)
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) {
const isOnTop = calculateIsOnTop(firstPanel.name); // 🎯 isOnTop 계산
if (DEBUG_MODE)
console.log('[PANEL_DEBUG] RESET_PANELS to:', firstPanel.name, {
isGNB,
isOnTop,
fromResetPanel: true,
});
// RESET 이동을 히스토리에 기록
store.dispatch(
enqueuePanelHistory(
firstPanel.name,
firstPanel.panelInfo || {},
'RESET',
new Date().toISOString(),
isGNB, // TabLayout/TIconButton이면 true
true, // fromResetPanel: 항상 true
isOnTop // 🎯 isOnTop 정보 추가
)
);
}
} else {
// 완전 초기화 (payload가 없는 경우 - HomePanel, 앱 초기화 등)
if (DEBUG_MODE)
console.log('[PANEL_DEBUG] Complete panel history reset (payload empty)', {
isGNB,
fromResetPanel: true,
});
store.dispatch(clearPanelHistory());
// HomePanel 초기화 기록 (앱 시작 시)
if (DEBUG_MODE) console.log('[PANEL_DEBUG] Recording initial HomePanel');
const isOnTop = calculateIsOnTop('homepanel'); // 🎯 isOnTop 계산
store.dispatch(
enqueuePanelHistory(
'homepanel',
{},
'APP_INITIALIZE',
new Date().toISOString(),
isGNB, // TIconButton Home 버튼이면 true
true, // fromResetPanel: true
isOnTop // 🎯 isOnTop 정보 추가
)
);
}
// PanelHistory 상태 로그 (초기화 후)
const logPanelHistoryAfter = () => {
if (DEBUG_MODE) {
const stateAfter = store.getState();
const panelHistoryAfter = stateAfter.panelHistory;
const panelsAfter = stateAfter.panels.panels;
console.log('[PANEL_HISTORY] After RESET_PANELS:', {
panelHistory: panelHistoryAfter,
panels: panelsAfter,
});
}
};
// state 업데이트가 완료된 후 로그
Promise.resolve().then(logPanelHistoryAfter);
break;
}
default:
// Other actions are ignored
break;
}
} catch (error) {
if (DEBUG_MODE) console.error('[panelHistoryMiddleware] 오류:', error);
// Middleware 오류가 앱 전체에 영향 주지 않도록 처리
}
return result;
};
export default panelHistoryMiddleware;