[251124] fix: Log정리-4
🕐 커밋 시간: 2025. 11. 24. 12:19:40 📊 변경 통계: • 총 파일: 6개 • 추가: +283줄 • 삭제: -255줄 📝 수정된 파일: ~ com.twin.app.shoptime/src/actions/mainActions.js ~ com.twin.app.shoptime/src/reducers/mainReducer.js ~ com.twin.app.shoptime/src/reducers/searchReducer.js ~ com.twin.app.shoptime/src/views/PlayerPanel/PlayerTabContents/v2/TabContainer.v2.jsx ~ com.twin.app.shoptime/src/views/SearchPanel/SearchPanel.new.v2.jsx ~ com.twin.app.shoptime/src/views/SearchPanel/VoiceInputOverlay/VoiceInputOverlay.jsx 🔧 함수 변경 내용: 📄 com.twin.app.shoptime/src/actions/mainActions.js (javascript): 🔄 Modified: clearSubCategory() 📄 com.twin.app.shoptime/src/views/PlayerPanel/PlayerTabContents/v2/TabContainer.v2.jsx (javascript): 🔄 Modified: Spottable() 📄 com.twin.app.shoptime/src/views/SearchPanel/VoiceInputOverlay/VoiceInputOverlay.jsx (javascript): ✅ Added: Spottable() 🔄 Modified: clearAllTimers() 🔧 주요 변경 내용: • 핵심 비즈니스 로직 개선
This commit is contained in:
@@ -5,13 +5,18 @@ import { CATEGORY_DATA_MAX_RESULTS_LIMIT, LOG_CONTEXT_NAME, LOG_MESSAGE_ID } fro
|
|||||||
import * as HelperMethods from '../utils/helperMethods';
|
import * as HelperMethods from '../utils/helperMethods';
|
||||||
import { types } from './actionTypes';
|
import { types } from './actionTypes';
|
||||||
import { addReservation, changeAppStatus, deleteReservation } from './commonActions';
|
import { addReservation, changeAppStatus, deleteReservation } from './commonActions';
|
||||||
|
import { createDebugHelpers } from '../utils/debug';
|
||||||
|
|
||||||
|
// 디버그 헬퍼 설정
|
||||||
|
const DEBUG_MODE = false;
|
||||||
|
const { dlog, dwarn, derror } = createDebugHelpers(DEBUG_MODE);
|
||||||
|
|
||||||
//IF-LGSP-007
|
//IF-LGSP-007
|
||||||
export const getMainLiveShow = (props) => (dispatch, getState) => {
|
export const getMainLiveShow = (props) => (dispatch, getState) => {
|
||||||
const vodIncFlag = props?.vodIncFlag;
|
const vodIncFlag = props?.vodIncFlag;
|
||||||
|
|
||||||
const onSuccess = (response) => {
|
const onSuccess = (response) => {
|
||||||
console.log('@@ getMainLiveShow onSuccess', response.data);
|
dlog('@@ getMainLiveShow onSuccess', response.data);
|
||||||
|
|
||||||
dispatch({
|
dispatch({
|
||||||
type: types.GET_MAIN_LIVE_SHOW,
|
type: types.GET_MAIN_LIVE_SHOW,
|
||||||
@@ -20,7 +25,7 @@ export const getMainLiveShow = (props) => (dispatch, getState) => {
|
|||||||
};
|
};
|
||||||
|
|
||||||
const onFail = (error) => {
|
const onFail = (error) => {
|
||||||
console.error('@@ getMainLiveShow onFail', error);
|
derror('@@ getMainLiveShow onFail', error);
|
||||||
};
|
};
|
||||||
|
|
||||||
TAxios(dispatch, getState, 'get', URLS.GET_MAIN_LIVE_SHOW, { vodIncFlag }, {}, onSuccess, onFail);
|
TAxios(dispatch, getState, 'get', URLS.GET_MAIN_LIVE_SHOW, { vodIncFlag }, {}, onSuccess, onFail);
|
||||||
@@ -31,7 +36,7 @@ export const setMainLiveUpcomingAlarm = (props) => (dispatch, getState) => {
|
|||||||
const { alamDispFlag, chanId, endDt, patnrId, patncNm, showId, showNm, strtDt } = props;
|
const { alamDispFlag, chanId, endDt, patnrId, patncNm, showId, showNm, strtDt } = props;
|
||||||
|
|
||||||
const onSuccess = (response) => {
|
const onSuccess = (response) => {
|
||||||
console.log('setMainLiveUpcomingAlarm onSuccess', response.data);
|
dlog('setMainLiveUpcomingAlarm onSuccess', response.data);
|
||||||
|
|
||||||
if (alamDispFlag === 'Y') {
|
if (alamDispFlag === 'Y') {
|
||||||
const convertedStrtDt = convertUtcToLocal(strtDt);
|
const convertedStrtDt = convertUtcToLocal(strtDt);
|
||||||
@@ -72,7 +77,7 @@ export const setMainLiveUpcomingAlarm = (props) => (dispatch, getState) => {
|
|||||||
};
|
};
|
||||||
|
|
||||||
const onFail = (error) => {
|
const onFail = (error) => {
|
||||||
console.error('setMainLiveUpcomingAlarm onFail', error);
|
derror('setMainLiveUpcomingAlarm onFail', error);
|
||||||
};
|
};
|
||||||
|
|
||||||
TAxios(
|
TAxios(
|
||||||
@@ -94,7 +99,7 @@ export const getMainCategoryDetail = (props) => (dispatch, getState) => {
|
|||||||
dispatch(changeAppStatus({ showLoadingPanel: { show: true, type: 'wait' } }));
|
dispatch(changeAppStatus({ showLoadingPanel: { show: true, type: 'wait' } }));
|
||||||
|
|
||||||
const onSuccess = (response) => {
|
const onSuccess = (response) => {
|
||||||
console.log('getMainCategoryDetail onSuccess ', response.data);
|
dlog('getMainCategoryDetail onSuccess ', response.data);
|
||||||
|
|
||||||
dispatch({
|
dispatch({
|
||||||
type: types.GET_PRODUCT_DETAIL,
|
type: types.GET_PRODUCT_DETAIL,
|
||||||
@@ -105,7 +110,7 @@ export const getMainCategoryDetail = (props) => (dispatch, getState) => {
|
|||||||
};
|
};
|
||||||
|
|
||||||
const onFail = (error) => {
|
const onFail = (error) => {
|
||||||
console.error('getMainCategoryDetail onFail', error);
|
derror('getMainCategoryDetail onFail', error);
|
||||||
dispatch(changeAppStatus({ showLoadingPanel: { show: false } }));
|
dispatch(changeAppStatus({ showLoadingPanel: { show: false } }));
|
||||||
};
|
};
|
||||||
|
|
||||||
@@ -125,7 +130,7 @@ export const getMainCategoryDetail = (props) => (dispatch, getState) => {
|
|||||||
export const getMainCategoryShowDetail = (props) => (dispatch, getState) => {
|
export const getMainCategoryShowDetail = (props) => (dispatch, getState) => {
|
||||||
const { patnrId, showId, curationId } = props;
|
const { patnrId, showId, curationId } = props;
|
||||||
const onSuccess = (response) => {
|
const onSuccess = (response) => {
|
||||||
console.log('getMainCategoryShowDetail onSuccess ', response.data);
|
dlog('getMainCategoryShowDetail onSuccess ', response.data);
|
||||||
|
|
||||||
dispatch({
|
dispatch({
|
||||||
type: types.GET_MAIN_CATEGORY_SHOW_DETAIL,
|
type: types.GET_MAIN_CATEGORY_SHOW_DETAIL,
|
||||||
@@ -134,7 +139,7 @@ export const getMainCategoryShowDetail = (props) => (dispatch, getState) => {
|
|||||||
};
|
};
|
||||||
|
|
||||||
const onFail = (error) => {
|
const onFail = (error) => {
|
||||||
console.error('getMainCategoryShowDetail onFail', error);
|
derror('getMainCategoryShowDetail onFail', error);
|
||||||
};
|
};
|
||||||
|
|
||||||
TAxios(
|
TAxios(
|
||||||
@@ -165,7 +170,7 @@ export const getSubCategory =
|
|||||||
lastSubCategoryParams &&
|
lastSubCategoryParams &&
|
||||||
JSON.stringify(lastSubCategoryParams) === JSON.stringify(params)
|
JSON.stringify(lastSubCategoryParams) === JSON.stringify(params)
|
||||||
) {
|
) {
|
||||||
console.log('getSubCategory ignore patch');
|
dlog('getSubCategory ignore patch');
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
lastSubCategoryParams = { ...params };
|
lastSubCategoryParams = { ...params };
|
||||||
@@ -176,7 +181,7 @@ export const getSubCategory =
|
|||||||
|
|
||||||
let currentKey = key;
|
let currentKey = key;
|
||||||
const onSuccess = (response) => {
|
const onSuccess = (response) => {
|
||||||
console.log('getSubCategory onSuccess ', response.data);
|
dlog('getSubCategory onSuccess ', response.data);
|
||||||
|
|
||||||
if (pageNo === 1) {
|
if (pageNo === 1) {
|
||||||
getSubCategoryKey = new Date();
|
getSubCategoryKey = new Date();
|
||||||
@@ -220,7 +225,7 @@ export const getSubCategory =
|
|||||||
const canRetry = nextRetryCount < SUB_CATEGORY_RETRY_LIMIT;
|
const canRetry = nextRetryCount < SUB_CATEGORY_RETRY_LIMIT;
|
||||||
|
|
||||||
if (canRetry) {
|
if (canRetry) {
|
||||||
console.warn('getSubCategory retry', {
|
dwarn('getSubCategory retry', {
|
||||||
lgCatCd,
|
lgCatCd,
|
||||||
pageNo,
|
pageNo,
|
||||||
retryCount: nextRetryCount,
|
retryCount: nextRetryCount,
|
||||||
@@ -232,7 +237,7 @@ export const getSubCategory =
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
console.error('getSubCategory onFail', error);
|
derror('getSubCategory onFail', error);
|
||||||
if (pageNo === 1) {
|
if (pageNo === 1) {
|
||||||
lastSubCategoryParams = {};
|
lastSubCategoryParams = {};
|
||||||
}
|
}
|
||||||
@@ -253,7 +258,7 @@ export const getSubCategory =
|
|||||||
export const continueGetSubCategory = (key, pageNo) => (dispatch, getState) => {
|
export const continueGetSubCategory = (key, pageNo) => (dispatch, getState) => {
|
||||||
if (!lastSubCategoryParams) {
|
if (!lastSubCategoryParams) {
|
||||||
// <<<<<<< HEAD
|
// <<<<<<< HEAD
|
||||||
console.warn('No previous category parameters found');
|
dwarn('No previous category parameters found');
|
||||||
// =======
|
// =======
|
||||||
// console.warn("No previous category parameters found");
|
// console.warn("No previous category parameters found");
|
||||||
// >>>>>>> gitlab/develop
|
// >>>>>>> gitlab/develop
|
||||||
@@ -295,7 +300,7 @@ const clearSubCategory = () => ({
|
|||||||
// TOP20 영상 목록 조회 IF-LGSP-069
|
// TOP20 영상 목록 조회 IF-LGSP-069
|
||||||
export const getTop20Show = () => (dispatch, getState) => {
|
export const getTop20Show = () => (dispatch, getState) => {
|
||||||
const onSuccess = (response) => {
|
const onSuccess = (response) => {
|
||||||
console.log('getTop20Show onSuccess ', response.data);
|
dlog('getTop20Show onSuccess ', response.data);
|
||||||
|
|
||||||
dispatch({
|
dispatch({
|
||||||
type: types.GET_TOP_20_SHOW,
|
type: types.GET_TOP_20_SHOW,
|
||||||
@@ -305,7 +310,7 @@ export const getTop20Show = () => (dispatch, getState) => {
|
|||||||
};
|
};
|
||||||
|
|
||||||
const onFail = (error) => {
|
const onFail = (error) => {
|
||||||
console.error('getTop20Show onFail', error);
|
derror('getTop20Show onFail', error);
|
||||||
dispatch(changeAppStatus({ showLoadingPanel: { show: false } }));
|
dispatch(changeAppStatus({ showLoadingPanel: { show: false } }));
|
||||||
};
|
};
|
||||||
|
|
||||||
@@ -375,14 +380,14 @@ export const getMyFavoriteFlag = (params) => (dispatch, getState) => {
|
|||||||
const { patnrId, prdtId } = params;
|
const { patnrId, prdtId } = params;
|
||||||
|
|
||||||
const onSuccess = (response) => {
|
const onSuccess = (response) => {
|
||||||
console.log('getMyFavoriteFlag onSuccess ', response.data);
|
dlog('getMyFavoriteFlag onSuccess ', response.data);
|
||||||
dispatch({
|
dispatch({
|
||||||
type: types.GET_MY_FAVORITE_FLAG,
|
type: types.GET_MY_FAVORITE_FLAG,
|
||||||
payload: response.data.data,
|
payload: response.data.data,
|
||||||
});
|
});
|
||||||
};
|
};
|
||||||
const onFail = (error) => {
|
const onFail = (error) => {
|
||||||
console.error('getMyFavoriteFlag onFail', error);
|
derror('getMyFavoriteFlag onFail', error);
|
||||||
};
|
};
|
||||||
|
|
||||||
TAxios(
|
TAxios(
|
||||||
@@ -401,7 +406,7 @@ export const setMainLikeCategory = (params) => (dispatch, getState) => {
|
|||||||
const { patnrId, prdtId } = params;
|
const { patnrId, prdtId } = params;
|
||||||
|
|
||||||
const onSuccess = (response) => {
|
const onSuccess = (response) => {
|
||||||
console.log('setMainLikeCategory onSuccess ', response.data);
|
dlog('setMainLikeCategory onSuccess ', response.data);
|
||||||
dispatch({
|
dispatch({
|
||||||
type: types.SET_MAIN_LIKE_CATEGORY,
|
type: types.SET_MAIN_LIKE_CATEGORY,
|
||||||
payload: response.data.data,
|
payload: response.data.data,
|
||||||
@@ -428,10 +433,10 @@ export const getHomeFullVideoInfo =
|
|||||||
({ lgCatCd }) =>
|
({ lgCatCd }) =>
|
||||||
(dispatch, getState) => {
|
(dispatch, getState) => {
|
||||||
const onSuccess = (response) => {
|
const onSuccess = (response) => {
|
||||||
console.log('getHomeFullVideoInfo onSuccess', response.data.data.showInfos);
|
dlog('getHomeFullVideoInfo onSuccess', response.data.data.showInfos);
|
||||||
|
|
||||||
// ✨ DEBUG: youmaylikeInfos 데이터 확인
|
// ✨ DEBUG: youmaylikeInfos 데이터 확인
|
||||||
console.log('[DEBUG] GET_HOME_FULL_VIDEO_INFO - API Response:', {
|
dlog('[DEBUG] GET_HOME_FULL_VIDEO_INFO - API Response:', {
|
||||||
youmaylikeInfos: response.data.data.youmaylikeInfos,
|
youmaylikeInfos: response.data.data.youmaylikeInfos,
|
||||||
youmaylikeInfos_length: response.data.data.youmaylikeInfos?.length,
|
youmaylikeInfos_length: response.data.data.youmaylikeInfos?.length,
|
||||||
liveChannelInfos_length: response.data.data.liveChannelInfos?.length,
|
liveChannelInfos_length: response.data.data.liveChannelInfos?.length,
|
||||||
@@ -445,7 +450,7 @@ export const getHomeFullVideoInfo =
|
|||||||
};
|
};
|
||||||
|
|
||||||
const onFail = (error) => {
|
const onFail = (error) => {
|
||||||
console.error('getHomeFullVideoInfo onSuccess', error);
|
derror('getHomeFullVideoInfo onSuccess', error);
|
||||||
};
|
};
|
||||||
|
|
||||||
TAxios(
|
TAxios(
|
||||||
|
|||||||
@@ -1,5 +1,10 @@
|
|||||||
import { types } from '../actions/actionTypes';
|
import { types } from '../actions/actionTypes';
|
||||||
import { CATEGORY_DATA_MAX_RESULTS_LIMIT } from '../utils/Config';
|
import { CATEGORY_DATA_MAX_RESULTS_LIMIT } from '../utils/Config';
|
||||||
|
import { createDebugHelpers } from '../utils/debug';
|
||||||
|
|
||||||
|
// 디버그 헬퍼 설정
|
||||||
|
const DEBUG_MODE = false;
|
||||||
|
const { dlog, dwarn, derror } = createDebugHelpers(DEBUG_MODE);
|
||||||
|
|
||||||
const initialState = {
|
const initialState = {
|
||||||
subCategoryData: {},
|
subCategoryData: {},
|
||||||
@@ -159,7 +164,7 @@ export const mainReducer = (state = initialState, action) => {
|
|||||||
const { data, lgCatCd } = action.payload;
|
const { data, lgCatCd } = action.payload;
|
||||||
|
|
||||||
// ✨ DEBUG: Reducer에서 youmaylikeInfos 저장 확인
|
// ✨ DEBUG: Reducer에서 youmaylikeInfos 저장 확인
|
||||||
console.log('[DEBUG] Reducer - GET_HOME_FULL_VIDEO_INFO:', {
|
dlog('[DEBUG] Reducer - GET_HOME_FULL_VIDEO_INFO:', {
|
||||||
youmaylikeInfos_length: data.youmaylikeInfos?.length,
|
youmaylikeInfos_length: data.youmaylikeInfos?.length,
|
||||||
youmaylikeInfos: data.youmaylikeInfos,
|
youmaylikeInfos: data.youmaylikeInfos,
|
||||||
});
|
});
|
||||||
@@ -197,7 +202,7 @@ export const mainReducer = (state = initialState, action) => {
|
|||||||
};
|
};
|
||||||
}
|
}
|
||||||
case types.CLEAR_SHOPNOW_INFO:
|
case types.CLEAR_SHOPNOW_INFO:
|
||||||
console.log('[DEBUG] Reducer - CLEAR_SHOPNOW_INFO called - youmaylikeInfos will be null');
|
dlog('[DEBUG] Reducer - CLEAR_SHOPNOW_INFO called - youmaylikeInfos will be null');
|
||||||
return {
|
return {
|
||||||
...state,
|
...state,
|
||||||
fullVideolgCatCd: '',
|
fullVideolgCatCd: '',
|
||||||
|
|||||||
@@ -1,4 +1,9 @@
|
|||||||
import { types } from '../actions/actionTypes';
|
import { types } from '../actions/actionTypes';
|
||||||
|
import { createDebugHelpers } from '../utils/debug';
|
||||||
|
|
||||||
|
// 디버그 헬퍼 설정
|
||||||
|
const DEBUG_MODE = false;
|
||||||
|
const { dlog, dwarn, derror } = createDebugHelpers(DEBUG_MODE);
|
||||||
|
|
||||||
const initialState = {
|
const initialState = {
|
||||||
searchDatas: {},
|
searchDatas: {},
|
||||||
@@ -81,8 +86,8 @@ export const searchReducer = (state = initialState, action) => {
|
|||||||
};
|
};
|
||||||
|
|
||||||
case types.RESET_VOICE_SEARCH:
|
case types.RESET_VOICE_SEARCH:
|
||||||
console.log('[VoiceInput]-searchReducer-RESET_VOICE_SEARCH');
|
dlog('[VoiceInput]-searchReducer-RESET_VOICE_SEARCH');
|
||||||
console.log(
|
dlog(
|
||||||
JSON.stringify(
|
JSON.stringify(
|
||||||
{
|
{
|
||||||
action: 'RESET_VOICE_SEARCH',
|
action: 'RESET_VOICE_SEARCH',
|
||||||
@@ -120,7 +125,7 @@ export const searchReducer = (state = initialState, action) => {
|
|||||||
const newPreKey = action.payload?.results?.[0]?.searchId || 'null';
|
const newPreKey = action.payload?.results?.[0]?.searchId || 'null';
|
||||||
const oldPreKey = state.preShopperHouseData?.results?.[0]?.searchId || 'null';
|
const oldPreKey = state.preShopperHouseData?.results?.[0]?.searchId || 'null';
|
||||||
|
|
||||||
console.log('[ShopperHouse]-DIFF (after backup) preShopperHouseKey:', oldPreKey, '→', newPreKey);
|
dlog('[ShopperHouse]-DIFF (after backup) preShopperHouseKey:', oldPreKey, '→', newPreKey);
|
||||||
|
|
||||||
return {
|
return {
|
||||||
...state,
|
...state,
|
||||||
@@ -150,7 +155,16 @@ export const searchReducer = (state = initialState, action) => {
|
|||||||
const preSortingType = state.preShopperHouseData?.results?.[0]?.sortingType || 'null';
|
const preSortingType = state.preShopperHouseData?.results?.[0]?.sortingType || 'null';
|
||||||
|
|
||||||
// [VoiceInput] Redux에 저장 로그
|
// [VoiceInput] Redux에 저장 로그
|
||||||
console.log('[ShopperHouse]-DIFF (after API) shopperHouseKey:', newKey, '| preShopperHouseKey:', preKey, '| sortingType:', sortingType || 'null', '| preSortingType:', preSortingType);
|
dlog(
|
||||||
|
'[ShopperHouse]-DIFF (after API) shopperHouseKey:',
|
||||||
|
newKey,
|
||||||
|
'| preShopperHouseKey:',
|
||||||
|
preKey,
|
||||||
|
'| sortingType:',
|
||||||
|
sortingType || 'null',
|
||||||
|
'| preSortingType:',
|
||||||
|
preSortingType
|
||||||
|
);
|
||||||
|
|
||||||
return {
|
return {
|
||||||
...state,
|
...state,
|
||||||
@@ -167,7 +181,7 @@ export const searchReducer = (state = initialState, action) => {
|
|||||||
}
|
}
|
||||||
|
|
||||||
case types.SET_SHOPPERHOUSE_ERROR:
|
case types.SET_SHOPPERHOUSE_ERROR:
|
||||||
console.log('[VoiceInput] ❌ Redux shopperHouseError 저장:', action.payload);
|
dlog('[VoiceInput] ❌ Redux shopperHouseError 저장:', action.payload);
|
||||||
return {
|
return {
|
||||||
...state,
|
...state,
|
||||||
shopperHouseError: action.payload,
|
shopperHouseError: action.payload,
|
||||||
@@ -177,7 +191,7 @@ export const searchReducer = (state = initialState, action) => {
|
|||||||
};
|
};
|
||||||
|
|
||||||
case types.SHOW_SHOPPERHOUSE_ERROR:
|
case types.SHOW_SHOPPERHOUSE_ERROR:
|
||||||
console.log('[ShopperHouse] 🔴 Redux shopperHouseErrorPopup 표시:', action.payload);
|
dlog('[ShopperHouse] 🔴 Redux shopperHouseErrorPopup 표시:', action.payload);
|
||||||
return {
|
return {
|
||||||
...state,
|
...state,
|
||||||
shopperHouseErrorPopup: {
|
shopperHouseErrorPopup: {
|
||||||
@@ -190,7 +204,7 @@ export const searchReducer = (state = initialState, action) => {
|
|||||||
};
|
};
|
||||||
|
|
||||||
case types.HIDE_SHOPPERHOUSE_ERROR:
|
case types.HIDE_SHOPPERHOUSE_ERROR:
|
||||||
console.log('[ShopperHouse] ✅ Redux shopperHouseErrorPopup 숨김');
|
dlog('[ShopperHouse] ✅ Redux shopperHouseErrorPopup 숨김');
|
||||||
return {
|
return {
|
||||||
...state,
|
...state,
|
||||||
shopperHouseErrorPopup: {
|
shopperHouseErrorPopup: {
|
||||||
@@ -203,16 +217,13 @@ export const searchReducer = (state = initialState, action) => {
|
|||||||
};
|
};
|
||||||
|
|
||||||
case types.CLEAR_SHOPPERHOUSE_DATA:
|
case types.CLEAR_SHOPPERHOUSE_DATA:
|
||||||
console.log('[DEBUG] 🧹 Redux shopperHouseData 초기화 호출 - 호출 스택 추적:');
|
dlog('[DEBUG] 🧹 Redux shopperHouseData 초기화 호출 - 호출 스택 추적:');
|
||||||
console.log(
|
dlog('[DEBUG] 호출 위치:', new Error().stack?.split('\n')[1]?.trim() || '(스택 추적 불가)');
|
||||||
'[DEBUG] 호출 위치:',
|
dlog(
|
||||||
new Error().stack?.split('\n')[1]?.trim() || '(스택 추적 불가)'
|
|
||||||
);
|
|
||||||
console.log(
|
|
||||||
'[VoiceInput] 🧹 Redux shopperHouseData 초기화 (searchId & relativeQueries & preShopperHouseData는 유지)'
|
'[VoiceInput] 🧹 Redux shopperHouseData 초기화 (searchId & relativeQueries & preShopperHouseData는 유지)'
|
||||||
);
|
);
|
||||||
console.log('[VoiceInput]-searchReducer-CLEAR_SHOPPERHOUSE_DATA');
|
dlog('[VoiceInput]-searchReducer-CLEAR_SHOPPERHOUSE_DATA');
|
||||||
console.log(
|
dlog(
|
||||||
JSON.stringify(
|
JSON.stringify(
|
||||||
{
|
{
|
||||||
shopperHouseData_cleared: true,
|
shopperHouseData_cleared: true,
|
||||||
@@ -239,7 +250,7 @@ export const searchReducer = (state = initialState, action) => {
|
|||||||
|
|
||||||
// 🔽 검색 메인 데이터 처리
|
// 🔽 검색 메인 데이터 처리
|
||||||
case types.GET_SEARCH_MAIN: {
|
case types.GET_SEARCH_MAIN: {
|
||||||
console.log('🔍 [searchReducer] GET_SEARCH_MAIN 받은 payload:', action.payload);
|
dlog('🔍 [searchReducer] GET_SEARCH_MAIN 받은 payload:', action.payload);
|
||||||
|
|
||||||
// 여러 가능한 구조 확인
|
// 여러 가능한 구조 확인
|
||||||
let resultData = null;
|
let resultData = null;
|
||||||
@@ -247,15 +258,15 @@ export const searchReducer = (state = initialState, action) => {
|
|||||||
if (action.payload?.result) {
|
if (action.payload?.result) {
|
||||||
// payload.result 구조
|
// payload.result 구조
|
||||||
resultData = action.payload.result;
|
resultData = action.payload.result;
|
||||||
console.log('✅ [searchReducer] payload.result 구조 확인');
|
dlog('✅ [searchReducer] payload.result 구조 확인');
|
||||||
} else if (action.payload?.data?.result) {
|
} else if (action.payload?.data?.result) {
|
||||||
// payload.data.result 구조
|
// payload.data.result 구조
|
||||||
resultData = action.payload.data.result;
|
resultData = action.payload.data.result;
|
||||||
console.log('✅ [searchReducer] payload.data.result 구조 확인');
|
dlog('✅ [searchReducer] payload.data.result 구조 확인');
|
||||||
} else if (action.payload?.data) {
|
} else if (action.payload?.data) {
|
||||||
// payload.data에 직접 데이터가 있는 경우
|
// payload.data에 직접 데이터가 있는 경우
|
||||||
resultData = action.payload.data;
|
resultData = action.payload.data;
|
||||||
console.log('✅ [searchReducer] payload.data 직접 구조 확인');
|
dlog('✅ [searchReducer] payload.data 직접 구조 확인');
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!resultData) {
|
if (!resultData) {
|
||||||
@@ -264,7 +275,7 @@ export const searchReducer = (state = initialState, action) => {
|
|||||||
return state;
|
return state;
|
||||||
}
|
}
|
||||||
|
|
||||||
console.log('[searchReducer] ✅ GET_SEARCH_MAIN success');
|
dlog('[searchReducer] ✅ GET_SEARCH_MAIN success');
|
||||||
|
|
||||||
return {
|
return {
|
||||||
...state,
|
...state,
|
||||||
@@ -278,7 +289,7 @@ export const searchReducer = (state = initialState, action) => {
|
|||||||
}
|
}
|
||||||
|
|
||||||
case types.CLEAR_SEARCH_MAIN_DATA:
|
case types.CLEAR_SEARCH_MAIN_DATA:
|
||||||
console.log('[searchReducer] 🧹 searchMainData 초기화');
|
dlog('[searchReducer] 🧹 searchMainData 초기화');
|
||||||
return {
|
return {
|
||||||
...state,
|
...state,
|
||||||
searchMainData: {
|
searchMainData: {
|
||||||
@@ -291,7 +302,7 @@ export const searchReducer = (state = initialState, action) => {
|
|||||||
|
|
||||||
// 🎯 [Phase 1] SearchPanel 모드 제어 명령
|
// 🎯 [Phase 1] SearchPanel 모드 제어 명령
|
||||||
case types.SWITCH_TO_SEARCH_INPUT_OVERLAY:
|
case types.SWITCH_TO_SEARCH_INPUT_OVERLAY:
|
||||||
console.log('[searchReducer] 🔄 SWITCH_TO_SEARCH_INPUT_OVERLAY 명령 저장', {
|
dlog('[searchReducer] 🔄 SWITCH_TO_SEARCH_INPUT_OVERLAY 명령 저장', {
|
||||||
source: action.payload?.source,
|
source: action.payload?.source,
|
||||||
timestamp: new Date().toISOString(),
|
timestamp: new Date().toISOString(),
|
||||||
});
|
});
|
||||||
|
|||||||
@@ -7,6 +7,12 @@ import Spotlight from '@enact/spotlight';
|
|||||||
import SpotlightContainerDecorator from '@enact/spotlight/SpotlightContainerDecorator';
|
import SpotlightContainerDecorator from '@enact/spotlight/SpotlightContainerDecorator';
|
||||||
import Spottable from '@enact/spotlight/Spottable';
|
import Spottable from '@enact/spotlight/Spottable';
|
||||||
|
|
||||||
|
import { createDebugHelpers } from '../../../../utils/debug';
|
||||||
|
|
||||||
|
// 디버그 헬퍼 설정
|
||||||
|
const DEBUG_MODE = false;
|
||||||
|
const { dlog, dwarn, derror } = createDebugHelpers(DEBUG_MODE);
|
||||||
|
|
||||||
// import icon_arrow_right from '../../../../../assets/images/icons';
|
// import icon_arrow_right from '../../../../../assets/images/icons';
|
||||||
import icon_arrow_dwon from '../../../../../assets/images/player/icon_tabcontainer_arrow_down.png';
|
import icon_arrow_dwon from '../../../../../assets/images/player/icon_tabcontainer_arrow_down.png';
|
||||||
import icon_shop_now from '../../../../../assets/images/player/icon_tabcontainer_shopnow.png';
|
import icon_shop_now from '../../../../../assets/images/player/icon_tabcontainer_shopnow.png';
|
||||||
@@ -74,7 +80,7 @@ export default function TabContainerV2({
|
|||||||
|
|
||||||
// ✨ DEBUG: youmaylikeInfos 데이터 로그
|
// ✨ DEBUG: youmaylikeInfos 데이터 로그
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
console.log('[DEBUG] TabContainerV2 - youmaylikeInfos:', {
|
dlog('[DEBUG] TabContainerV2 - youmaylikeInfos:', {
|
||||||
exists: !!youmaylikeInfos,
|
exists: !!youmaylikeInfos,
|
||||||
length: youmaylikeInfos?.length,
|
length: youmaylikeInfos?.length,
|
||||||
data: youmaylikeInfos,
|
data: youmaylikeInfos,
|
||||||
|
|||||||
@@ -1,37 +1,19 @@
|
|||||||
// src/views/SearchPanel/SearchPanel.new.jsx
|
// src/views/SearchPanel/SearchPanel.new.jsx
|
||||||
import React, {
|
import React, { useCallback, useEffect, useMemo, useRef, useState } from 'react';
|
||||||
useCallback,
|
|
||||||
useEffect,
|
|
||||||
useMemo,
|
|
||||||
useRef,
|
|
||||||
useState,
|
|
||||||
} from 'react';
|
|
||||||
|
|
||||||
import classNames from 'classnames';
|
import classNames from 'classnames';
|
||||||
import {
|
import { useDispatch, useSelector } from 'react-redux';
|
||||||
useDispatch,
|
|
||||||
useSelector,
|
|
||||||
} from 'react-redux';
|
|
||||||
|
|
||||||
import Spotlight from '@enact/spotlight';
|
import Spotlight from '@enact/spotlight';
|
||||||
import SpotlightContainerDecorator
|
import SpotlightContainerDecorator from '@enact/spotlight/SpotlightContainerDecorator';
|
||||||
from '@enact/spotlight/SpotlightContainerDecorator';
|
|
||||||
import Spottable from '@enact/spotlight/Spottable';
|
import Spottable from '@enact/spotlight/Spottable';
|
||||||
|
|
||||||
import micIcon from '../../../assets/images/searchpanel/image-mic.png';
|
import micIcon from '../../../assets/images/searchpanel/image-mic.png';
|
||||||
import hotPicksImage from '../../../assets/images/searchpanel/img-hotpicks.png';
|
import hotPicksImage from '../../../assets/images/searchpanel/img-hotpicks.png';
|
||||||
import hotPicksBrandImage
|
import hotPicksBrandImage from '../../../assets/images/searchpanel/img-search-hotpicks.png';
|
||||||
from '../../../assets/images/searchpanel/img-search-hotpicks.png';
|
import { sendLogGNB, sendLogTotalRecommend } from '../../actions/logActions';
|
||||||
import {
|
|
||||||
sendLogGNB,
|
|
||||||
sendLogTotalRecommend,
|
|
||||||
} from '../../actions/logActions';
|
|
||||||
import { getMyRecommandedKeyword } from '../../actions/myPageActions';
|
import { getMyRecommandedKeyword } from '../../actions/myPageActions';
|
||||||
import {
|
import { popPanel, pushPanel, updatePanel } from '../../actions/panelActions';
|
||||||
popPanel,
|
|
||||||
pushPanel,
|
|
||||||
updatePanel,
|
|
||||||
} from '../../actions/panelActions';
|
|
||||||
import {
|
import {
|
||||||
clearPanelCommand,
|
clearPanelCommand,
|
||||||
clearShopperHouseData,
|
clearShopperHouseData,
|
||||||
@@ -51,36 +33,27 @@ import {
|
|||||||
// showWarningToast,
|
// showWarningToast,
|
||||||
// } from '../../actions/toastActions';
|
// } from '../../actions/toastActions';
|
||||||
import TBody from '../../components/TBody/TBody';
|
import TBody from '../../components/TBody/TBody';
|
||||||
import TItemCardNew, {
|
import TItemCardNew, { removeDotAndColon } from '../../components/TItemCard/TItemCard.new';
|
||||||
removeDotAndColon,
|
|
||||||
} from '../../components/TItemCard/TItemCard.new';
|
|
||||||
import TPanel from '../../components/TPanel/TPanel';
|
import TPanel from '../../components/TPanel/TPanel';
|
||||||
import TVerticalPagenator
|
import TVerticalPagenator from '../../components/TVerticalPagenator/TVerticalPagenator';
|
||||||
from '../../components/TVerticalPagenator/TVerticalPagenator';
|
import TVirtualGridList from '../../components/TVirtualGridList/TVirtualGridList';
|
||||||
import TVirtualGridList
|
|
||||||
from '../../components/TVirtualGridList/TVirtualGridList';
|
|
||||||
import usePanelHistory from '../../hooks/usePanelHistory/usePanelHistory';
|
import usePanelHistory from '../../hooks/usePanelHistory/usePanelHistory';
|
||||||
// import VirtualKeyboardContainer from "../../components/TToast/VirtualKeyboardContainer";
|
// import VirtualKeyboardContainer from "../../components/TToast/VirtualKeyboardContainer";
|
||||||
import usePrevious from '../../hooks/usePrevious';
|
import usePrevious from '../../hooks/usePrevious';
|
||||||
import { useSearchHistory } from '../../hooks/useSearchHistory';
|
import { useSearchHistory } from '../../hooks/useSearchHistory';
|
||||||
import {
|
import { LOG_CONTEXT_NAME, LOG_MENU, LOG_MESSAGE_ID, panel_names } from '../../utils/Config';
|
||||||
LOG_CONTEXT_NAME,
|
|
||||||
LOG_MENU,
|
|
||||||
LOG_MESSAGE_ID,
|
|
||||||
panel_names,
|
|
||||||
} from '../../utils/Config';
|
|
||||||
import NoSearchResults from './NoSearchResults/NoSearchResults';
|
import NoSearchResults from './NoSearchResults/NoSearchResults';
|
||||||
// import NoSearchResults from './NoSearchResults/NoSearchResults';
|
// import NoSearchResults from './NoSearchResults/NoSearchResults';
|
||||||
import SearchInputOverlay from './SearchInputOverlay';
|
import SearchInputOverlay from './SearchInputOverlay';
|
||||||
import css from './SearchPanel.new.module.less';
|
import css from './SearchPanel.new.module.less';
|
||||||
import SearchResultsNew from './SearchResults.new.v2';
|
import SearchResultsNew from './SearchResults.new.v2';
|
||||||
import TInputSimple, {
|
import TInputSimple, { ICONS, KINDS } from './TInput/TInputSimple';
|
||||||
ICONS,
|
import VoiceInputOverlay, { VOICE_MODES } from './VoiceInputOverlay/VoiceInputOverlay';
|
||||||
KINDS,
|
import { createDebugHelpers } from '../../utils/debug';
|
||||||
} from './TInput/TInputSimple';
|
|
||||||
import VoiceInputOverlay, {
|
// 디버그 헬퍼 설정
|
||||||
VOICE_MODES,
|
const DEBUG_MODE = false;
|
||||||
} from './VoiceInputOverlay/VoiceInputOverlay';
|
const { dlog, dwarn, derror } = createDebugHelpers(DEBUG_MODE);
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* ✨ Mode-Based Architecture 도입
|
* ✨ Mode-Based Architecture 도입
|
||||||
@@ -240,7 +213,7 @@ export default function SearchPanel({ panelInfo, isOnTop, spotlightId }) {
|
|||||||
// 🐛 [DEBUG] shopperHouseData 상태 변경 추적 (DEBUG_MODE가 true일 경우에만)
|
// 🐛 [DEBUG] shopperHouseData 상태 변경 추적 (DEBUG_MODE가 true일 경우에만)
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
if (DEBUG_MODE) {
|
if (DEBUG_MODE) {
|
||||||
console.log('[DEBUG] 📊 SearchPanel shopperHouseData 상태 변경:', {
|
dlog('[DEBUG] 📊 SearchPanel shopperHouseData 상태 변경:', {
|
||||||
hasData: !!shopperHouseData,
|
hasData: !!shopperHouseData,
|
||||||
dataLength: shopperHouseData?.results?.[0]?.docs?.length || 0,
|
dataLength: shopperHouseData?.results?.[0]?.docs?.length || 0,
|
||||||
searchId: shopperHouseData?.results?.[0]?.searchId || '(없음)',
|
searchId: shopperHouseData?.results?.[0]?.searchId || '(없음)',
|
||||||
@@ -256,7 +229,7 @@ export default function SearchPanel({ panelInfo, isOnTop, spotlightId }) {
|
|||||||
// 🐛 [DEBUG] SearchPanel 마운트/언마운트 추적 (DEBUG_MODE가 true일 경우에만)
|
// 🐛 [DEBUG] SearchPanel 마운트/언마운트 추적 (DEBUG_MODE가 true일 경우에만)
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
if (DEBUG_MODE) {
|
if (DEBUG_MODE) {
|
||||||
console.log('[DEBUG] 🚀 SearchPanel 마운트됨 - 초기 shopperHouseData 상태:', {
|
dlog('[DEBUG] 🚀 SearchPanel 마운트됨 - 초기 shopperHouseData 상태:', {
|
||||||
hasData: !!shopperHouseData,
|
hasData: !!shopperHouseData,
|
||||||
dataLength: shopperHouseData?.results?.[0]?.docs?.length || 0,
|
dataLength: shopperHouseData?.results?.[0]?.docs?.length || 0,
|
||||||
currentMode,
|
currentMode,
|
||||||
@@ -266,7 +239,7 @@ export default function SearchPanel({ panelInfo, isOnTop, spotlightId }) {
|
|||||||
|
|
||||||
return () => {
|
return () => {
|
||||||
if (DEBUG_MODE) {
|
if (DEBUG_MODE) {
|
||||||
console.log('[DEBUG] 🔚 SearchPanel 언마운트됨 - shopperHouseData 상태:', {
|
dlog('[DEBUG] 🔚 SearchPanel 언마운트됨 - shopperHouseData 상태:', {
|
||||||
hasData: !!shopperHouseData,
|
hasData: !!shopperHouseData,
|
||||||
dataLength: shopperHouseData?.results?.[0]?.docs?.length || 0,
|
dataLength: shopperHouseData?.results?.[0]?.docs?.length || 0,
|
||||||
timestamp: new Date().toISOString(),
|
timestamp: new Date().toISOString(),
|
||||||
@@ -278,7 +251,7 @@ export default function SearchPanel({ panelInfo, isOnTop, spotlightId }) {
|
|||||||
// 🐛 [DEBUG] isOnTop 상태 변경 추적 (DetailPanel <-> SearchPanel 전환, DEBUG_MODE가 true일 경우에만)
|
// 🐛 [DEBUG] isOnTop 상태 변경 추적 (DetailPanel <-> SearchPanel 전환, DEBUG_MODE가 true일 경우에만)
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
if (isOnTopRef.current !== isOnTop && DEBUG_MODE) {
|
if (isOnTopRef.current !== isOnTop && DEBUG_MODE) {
|
||||||
console.log('[DEBUG] 🔄 SearchPanel isOnTop 상태 변경:', {
|
dlog('[DEBUG] 🔄 SearchPanel isOnTop 상태 변경:', {
|
||||||
from: isOnTopRef.current,
|
from: isOnTopRef.current,
|
||||||
to: isOnTop,
|
to: isOnTop,
|
||||||
shopperHouseData_preserved: !!shopperHouseData,
|
shopperHouseData_preserved: !!shopperHouseData,
|
||||||
@@ -317,7 +290,7 @@ export default function SearchPanel({ panelInfo, isOnTop, spotlightId }) {
|
|||||||
// API 실패 시 fallback reference 초기화
|
// API 실패 시 fallback reference 초기화
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
if (shopperHouseErrorPopup?.visible && shopperHouseErrorPopup?.type === 'API_FAILURE') {
|
if (shopperHouseErrorPopup?.visible && shopperHouseErrorPopup?.type === 'API_FAILURE') {
|
||||||
console.log('[SearchPanel] 🧹 API 실패 감지 - fallbackShopperHouseData 초기화');
|
dlog('[SearchPanel] 🧹 API 실패 감지 - fallbackShopperHouseData 초기화');
|
||||||
shopperHouseDataRef.current = null;
|
shopperHouseDataRef.current = null;
|
||||||
}
|
}
|
||||||
}, [shopperHouseErrorPopup?.visible, shopperHouseErrorPopup?.type]);
|
}, [shopperHouseErrorPopup?.visible, shopperHouseErrorPopup?.type]);
|
||||||
@@ -356,7 +329,7 @@ export default function SearchPanel({ panelInfo, isOnTop, spotlightId }) {
|
|||||||
currentPanel?.panelInfo?.currentSpot;
|
currentPanel?.panelInfo?.currentSpot;
|
||||||
|
|
||||||
if (DEBUG_MODE && isReturning) {
|
if (DEBUG_MODE && isReturning) {
|
||||||
console.log('[FOCUS] 🎯 DetailPanel 복귀 감지:', {
|
dlog('[FOCUS] 🎯 DetailPanel 복귀 감지:', {
|
||||||
current: currentPanel?.panelName,
|
current: currentPanel?.panelName,
|
||||||
previous: previousPanel?.panelName,
|
previous: previousPanel?.panelName,
|
||||||
action: currentPanel?.action,
|
action: currentPanel?.action,
|
||||||
@@ -449,9 +422,7 @@ export default function SearchPanel({ panelInfo, isOnTop, spotlightId }) {
|
|||||||
|
|
||||||
// ✨ [Phase 4] Enter/OK 키 처리 - SearchInputOverlay 표시
|
// ✨ [Phase 4] Enter/OK 키 처리 - SearchInputOverlay 표시
|
||||||
if (e.key === 'Enter' || e.keyCode === 13) {
|
if (e.key === 'Enter' || e.keyCode === 13) {
|
||||||
console.log(
|
dlog('[DEBUG] [SearchPanel] TInputSimple에서 Enter/OK 키 감지 → SearchInputOverlay 오픈');
|
||||||
'[DEBUG] [SearchPanel] TInputSimple에서 Enter/OK 키 감지 → SearchInputOverlay 오픈'
|
|
||||||
);
|
|
||||||
e.preventDefault();
|
e.preventDefault();
|
||||||
|
|
||||||
// ✨ [Phase 6] SearchInputOverlay 오픈 후 자동으로 입력 준비 완료
|
// ✨ [Phase 6] SearchInputOverlay 오픈 후 자동으로 입력 준비 완료
|
||||||
@@ -525,7 +496,7 @@ export default function SearchPanel({ panelInfo, isOnTop, spotlightId }) {
|
|||||||
// * 0hun: input에 포커스 발생하여 가상 키보드 활성 시, `isInputModeActive` 상태 Boolean 값 설정
|
// * 0hun: input에 포커스 발생하여 가상 키보드 활성 시, `isInputModeActive` 상태 Boolean 값 설정
|
||||||
// */
|
// */
|
||||||
// const handleInputModeChange = useCallback((isActive) => {
|
// const handleInputModeChange = useCallback((isActive) => {
|
||||||
// console.log(
|
// dlog(
|
||||||
// "[SearchPanel] TInput 입력 모드:",
|
// "[SearchPanel] TInput 입력 모드:",
|
||||||
// isActive ? "활성화 (키보드 표시)" : "비활성화 (키보드 숨김)"
|
// isActive ? "활성화 (키보드 표시)" : "비활성화 (키보드 숨김)"
|
||||||
// );
|
// );
|
||||||
@@ -613,7 +584,7 @@ export default function SearchPanel({ panelInfo, isOnTop, spotlightId }) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
if (DEBUG_MODE) {
|
if (DEBUG_MODE) {
|
||||||
console.log(
|
dlog(
|
||||||
'🎤 [DEBUG][SearchPanel] openVoiceOverlay called, current isVoiceOverlayVisible:',
|
'🎤 [DEBUG][SearchPanel] openVoiceOverlay called, current isVoiceOverlayVisible:',
|
||||||
isVoiceOverlayVisible
|
isVoiceOverlayVisible
|
||||||
);
|
);
|
||||||
@@ -636,7 +607,7 @@ export default function SearchPanel({ panelInfo, isOnTop, spotlightId }) {
|
|||||||
*/
|
*/
|
||||||
const onCancel = useCallback(() => {
|
const onCancel = useCallback(() => {
|
||||||
if (DEBUG_MODE) {
|
if (DEBUG_MODE) {
|
||||||
console.log('[DEBUG]-onCancel called', {
|
dlog('[DEBUG]-onCancel called', {
|
||||||
isOnTop: isOnTopRef.current,
|
isOnTop: isOnTopRef.current,
|
||||||
isVoiceOverlayVisible,
|
isVoiceOverlayVisible,
|
||||||
isSearchOverlayVisible,
|
isSearchOverlayVisible,
|
||||||
@@ -649,7 +620,7 @@ export default function SearchPanel({ panelInfo, isOnTop, spotlightId }) {
|
|||||||
|
|
||||||
if (!isOnTopRef.current) {
|
if (!isOnTopRef.current) {
|
||||||
if (DEBUG_MODE) {
|
if (DEBUG_MODE) {
|
||||||
console.log('[DEBUG]-onCancel: isOnTopRef is false, returning');
|
dlog('[DEBUG]-onCancel: isOnTopRef is false, returning');
|
||||||
}
|
}
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
@@ -657,7 +628,7 @@ export default function SearchPanel({ panelInfo, isOnTop, spotlightId }) {
|
|||||||
// VoiceInputOverlay가 열려있으면 먼저 닫기
|
// VoiceInputOverlay가 열려있으면 먼저 닫기
|
||||||
if (isVoiceOverlayVisible) {
|
if (isVoiceOverlayVisible) {
|
||||||
if (DEBUG_MODE) {
|
if (DEBUG_MODE) {
|
||||||
console.log('[DEBUG]-onCancel: closing VoiceInputOverlay');
|
dlog('[DEBUG]-onCancel: closing VoiceInputOverlay');
|
||||||
}
|
}
|
||||||
setIsVoiceOverlayVisible(false);
|
setIsVoiceOverlayVisible(false);
|
||||||
return;
|
return;
|
||||||
@@ -666,7 +637,7 @@ export default function SearchPanel({ panelInfo, isOnTop, spotlightId }) {
|
|||||||
// SearchInputOverlay가 열려있으면 먼저 닫기
|
// SearchInputOverlay가 열려있으면 먼저 닫기
|
||||||
if (isSearchOverlayVisible) {
|
if (isSearchOverlayVisible) {
|
||||||
if (DEBUG_MODE) {
|
if (DEBUG_MODE) {
|
||||||
console.log('[DEBUG]-onCancel: closing SearchInputOverlay');
|
dlog('[DEBUG]-onCancel: closing SearchInputOverlay');
|
||||||
}
|
}
|
||||||
handleSearchOverlayClose();
|
handleSearchOverlayClose();
|
||||||
return;
|
return;
|
||||||
@@ -674,7 +645,7 @@ export default function SearchPanel({ panelInfo, isOnTop, spotlightId }) {
|
|||||||
|
|
||||||
// ✨ [Phase 5] VOICE_RESULT 모드에서 ESC/뒤로가기 누르면 INITIAL 모드로 돌아가기
|
// ✨ [Phase 5] VOICE_RESULT 모드에서 ESC/뒤로가기 누르면 INITIAL 모드로 돌아가기
|
||||||
if (DEBUG_MODE) {
|
if (DEBUG_MODE) {
|
||||||
console.log('[DEBUG]-VOICE_RESULT check:', {
|
dlog('[DEBUG]-VOICE_RESULT check:', {
|
||||||
currentMode,
|
currentMode,
|
||||||
isVoiceResultMode: currentMode === SEARCH_PANEL_MODES.VOICE_RESULT,
|
isVoiceResultMode: currentMode === SEARCH_PANEL_MODES.VOICE_RESULT,
|
||||||
VOICE_RESULT_value: SEARCH_PANEL_MODES.VOICE_RESULT,
|
VOICE_RESULT_value: SEARCH_PANEL_MODES.VOICE_RESULT,
|
||||||
@@ -683,11 +654,11 @@ export default function SearchPanel({ panelInfo, isOnTop, spotlightId }) {
|
|||||||
|
|
||||||
if (currentMode === SEARCH_PANEL_MODES.VOICE_RESULT) {
|
if (currentMode === SEARCH_PANEL_MODES.VOICE_RESULT) {
|
||||||
if (DEBUG_MODE) {
|
if (DEBUG_MODE) {
|
||||||
console.log(
|
dlog(
|
||||||
'[DEBUG]-VOICE_RESULT: Clearing ShopperHouse data (searchId will be preserved for 2nd search)'
|
'[DEBUG]-VOICE_RESULT: Clearing ShopperHouse data (searchId will be preserved for 2nd search)'
|
||||||
);
|
);
|
||||||
console.log('[VoiceInput]-SearchPanel-onCancel-VOICE_RESULT');
|
dlog('[VoiceInput]-SearchPanel-onCancel-VOICE_RESULT');
|
||||||
console.log('[VoiceInput] 🧹 VOICE_RESULT 모드에서 ESC 누름 - clearShopperHouseData 호출');
|
dlog('[VoiceInput] 🧹 VOICE_RESULT 모드에서 ESC 누름 - clearShopperHouseData 호출');
|
||||||
}
|
}
|
||||||
// 🎯 [포커스 로직 통합] 포커스는 상태 변경에 의해 자동으로 처리됨
|
// 🎯 [포커스 로직 통합] 포커스는 상태 변경에 의해 자동으로 처리됨
|
||||||
setIsShopperHousePending(false);
|
setIsShopperHousePending(false);
|
||||||
@@ -696,17 +667,17 @@ export default function SearchPanel({ panelInfo, isOnTop, spotlightId }) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
if (DEBUG_MODE) {
|
if (DEBUG_MODE) {
|
||||||
console.log('[DEBUG]-onCancel: normal cancel logic', { searchQuery });
|
dlog('[DEBUG]-onCancel: normal cancel logic', { searchQuery });
|
||||||
}
|
}
|
||||||
|
|
||||||
if (searchQuery === null || searchQuery === '') {
|
if (searchQuery === null || searchQuery === '') {
|
||||||
if (DEBUG_MODE) {
|
if (DEBUG_MODE) {
|
||||||
console.log('[DEBUG]-onCancel: popping panel');
|
dlog('[DEBUG]-onCancel: popping panel');
|
||||||
}
|
}
|
||||||
dispatch(popPanel(panel_names.SEARCH_PANEL));
|
dispatch(popPanel(panel_names.SEARCH_PANEL));
|
||||||
} else {
|
} else {
|
||||||
if (DEBUG_MODE) {
|
if (DEBUG_MODE) {
|
||||||
console.log('[DEBUG]-onCancel: resetting search query');
|
dlog('[DEBUG]-onCancel: resetting search query');
|
||||||
}
|
}
|
||||||
setSearchQuery('');
|
setSearchQuery('');
|
||||||
// 🎯 [포커스 로직 통합] 포커스는 상태 변경(searchQuery)에 의해 자동으로 처리됨
|
// 🎯 [포커스 로직 통합] 포커스는 상태 변경(searchQuery)에 의해 자동으로 처리됨
|
||||||
@@ -820,7 +791,7 @@ export default function SearchPanel({ panelInfo, isOnTop, spotlightId }) {
|
|||||||
const analyzeCurrentScenario = useCallback(() => {
|
const analyzeCurrentScenario = useCallback(() => {
|
||||||
// DEBUG: 모든 기본 상태값 출력
|
// DEBUG: 모든 기본 상태값 출력
|
||||||
if (DEBUG_MODE) {
|
if (DEBUG_MODE) {
|
||||||
console.log('[DEBUG] analyzeCurrentScenario 호출됨:', {
|
dlog('[DEBUG] analyzeCurrentScenario 호출됨:', {
|
||||||
// 🎯 기존 isOnTop과 usePanelHistory의 isOnTop 비교
|
// 🎯 기존 isOnTop과 usePanelHistory의 isOnTop 비교
|
||||||
propIsOnTop: isOnTop,
|
propIsOnTop: isOnTop,
|
||||||
historyIsOnTop: currentIsOnTop,
|
historyIsOnTop: currentIsOnTop,
|
||||||
@@ -845,8 +816,8 @@ export default function SearchPanel({ panelInfo, isOnTop, spotlightId }) {
|
|||||||
if (isReturningFromDetailPanel) {
|
if (isReturningFromDetailPanel) {
|
||||||
const currentSpot = currentPanel?.panelInfo?.currentSpot;
|
const currentSpot = currentPanel?.panelInfo?.currentSpot;
|
||||||
if (DEBUG_MODE) {
|
if (DEBUG_MODE) {
|
||||||
console.log('[Focus] usePanelHistory로 DetailPanel 복귀 감지 - 이전 상품으로 포커스 이동');
|
dlog('[Focus] usePanelHistory로 DetailPanel 복귀 감지 - 이전 상품으로 포커스 이동');
|
||||||
console.log('[FOCUS] 🎯 Scenario: DETAIL_PANEL_RETURN (usePanelHistory)', {
|
dlog('[FOCUS] 🎯 Scenario: DETAIL_PANEL_RETURN (usePanelHistory)', {
|
||||||
currentSpot,
|
currentSpot,
|
||||||
mode: currentMode,
|
mode: currentMode,
|
||||||
fromSearchResult: currentMode === SEARCH_PANEL_MODES.SEARCH_RESULT,
|
fromSearchResult: currentMode === SEARCH_PANEL_MODES.SEARCH_RESULT,
|
||||||
@@ -862,7 +833,7 @@ export default function SearchPanel({ panelInfo, isOnTop, spotlightId }) {
|
|||||||
// 🎯 [개선된 fallback] usePanelHistory의 isOnTop 정보 활용
|
// 🎯 [개선된 fallback] usePanelHistory의 isOnTop 정보 활용
|
||||||
// DetailPanel에서 방금 복귀한 상황 (usePanelHistory가 없을 경우를 대비)
|
// DetailPanel에서 방금 복귀한 상황 (usePanelHistory가 없을 경우를 대비)
|
||||||
if (DEBUG_MODE) {
|
if (DEBUG_MODE) {
|
||||||
console.log('[DEBUG] 개선된 DETAIL_PANEL_RETURN 조건 확인 (fallback):', {
|
dlog('[DEBUG] 개선된 DETAIL_PANEL_RETURN 조건 확인 (fallback):', {
|
||||||
// 🎯 여러 isOnTop 소스 비교
|
// 🎯 여러 isOnTop 소스 비교
|
||||||
propIsOnTop: isOnTop,
|
propIsOnTop: isOnTop,
|
||||||
historyIsOnTop: currentIsOnTop,
|
historyIsOnTop: currentIsOnTop,
|
||||||
@@ -889,8 +860,8 @@ export default function SearchPanel({ panelInfo, isOnTop, spotlightId }) {
|
|||||||
) {
|
) {
|
||||||
const usedHistoryOnTop = currentIsOnTop && isOnTopChange?.becameOnTop;
|
const usedHistoryOnTop = currentIsOnTop && isOnTopChange?.becameOnTop;
|
||||||
if (DEBUG_MODE) {
|
if (DEBUG_MODE) {
|
||||||
console.log('[Focus] 개선된 방식으로 DetailPanel 복귀 감지 - 이전 상품으로 포커스 이동');
|
dlog('[Focus] 개선된 방식으로 DetailPanel 복귀 감지 - 이전 상품으로 포커스 이동');
|
||||||
console.log('[FOCUS] 🎯 Scenario: DETAIL_PANEL_RETURN (improved fallback)', {
|
dlog('[FOCUS] 🎯 Scenario: DETAIL_PANEL_RETURN (improved fallback)', {
|
||||||
currentSpot: panelInfo.currentSpot,
|
currentSpot: panelInfo.currentSpot,
|
||||||
mode: currentMode,
|
mode: currentMode,
|
||||||
fromSearchResult: currentMode === SEARCH_PANEL_MODES.SEARCH_RESULT,
|
fromSearchResult: currentMode === SEARCH_PANEL_MODES.SEARCH_RESULT,
|
||||||
@@ -908,7 +879,7 @@ export default function SearchPanel({ panelInfo, isOnTop, spotlightId }) {
|
|||||||
// - 위의 DETAIL_PANEL_RETURN이 아닌 경우 (= currentSpot이 없거나 모드가 검색 결과 아님)
|
// - 위의 DETAIL_PANEL_RETURN이 아닌 경우 (= currentSpot이 없거나 모드가 검색 결과 아님)
|
||||||
if (isOnTop && !isOnTopRef.current) {
|
if (isOnTop && !isOnTopRef.current) {
|
||||||
if (DEBUG_MODE) {
|
if (DEBUG_MODE) {
|
||||||
console.log('[FOCUS] 🎯 Scenario: INITIAL_OPEN', {
|
dlog('[FOCUS] 🎯 Scenario: INITIAL_OPEN', {
|
||||||
currentSpot: panelInfo?.currentSpot,
|
currentSpot: panelInfo?.currentSpot,
|
||||||
mode: currentMode,
|
mode: currentMode,
|
||||||
});
|
});
|
||||||
@@ -927,7 +898,7 @@ export default function SearchPanel({ panelInfo, isOnTop, spotlightId }) {
|
|||||||
currentModeRef.current !== SEARCH_PANEL_MODES.SEARCH_RESULT
|
currentModeRef.current !== SEARCH_PANEL_MODES.SEARCH_RESULT
|
||||||
) {
|
) {
|
||||||
if (DEBUG_MODE) {
|
if (DEBUG_MODE) {
|
||||||
console.log('[FOCUS] 🎯 Scenario: SEARCH_RESULT_LOADED (Mode Changed)', {
|
dlog('[FOCUS] 🎯 Scenario: SEARCH_RESULT_LOADED (Mode Changed)', {
|
||||||
themeCount: searchDatas?.theme?.length || 0,
|
themeCount: searchDatas?.theme?.length || 0,
|
||||||
itemCount: searchDatas?.item?.length || 0,
|
itemCount: searchDatas?.item?.length || 0,
|
||||||
showCount: searchDatas?.show?.length || 0,
|
showCount: searchDatas?.show?.length || 0,
|
||||||
@@ -948,10 +919,11 @@ export default function SearchPanel({ panelInfo, isOnTop, spotlightId }) {
|
|||||||
currentMode === SEARCH_PANEL_MODES.VOICE_RESULT &&
|
currentMode === SEARCH_PANEL_MODES.VOICE_RESULT &&
|
||||||
shopperHouseData &&
|
shopperHouseData &&
|
||||||
// 🎯 [개선] 모드 변경 OR 새로운 데이터 도착 감지
|
// 🎯 [개선] 모드 변경 OR 새로운 데이터 도착 감지
|
||||||
(currentModeRef.current !== SEARCH_PANEL_MODES.VOICE_RESULT || shopperHouseDataRef.current !== shopperHouseData)
|
(currentModeRef.current !== SEARCH_PANEL_MODES.VOICE_RESULT ||
|
||||||
|
shopperHouseDataRef.current !== shopperHouseData)
|
||||||
) {
|
) {
|
||||||
if (DEBUG_MODE) {
|
if (DEBUG_MODE) {
|
||||||
console.log('[FOCUS] 🎯 Scenario: NEW_SEARCH_LOADED (Voice Result Mode)', {
|
dlog('[FOCUS] 🎯 Scenario: NEW_SEARCH_LOADED (Voice Result Mode)', {
|
||||||
itemCount: shopperHouseData?.results?.[0]?.docs?.length || 0,
|
itemCount: shopperHouseData?.results?.[0]?.docs?.length || 0,
|
||||||
prevMode: currentModeRef.current,
|
prevMode: currentModeRef.current,
|
||||||
nextMode: currentMode,
|
nextMode: currentMode,
|
||||||
@@ -966,7 +938,7 @@ export default function SearchPanel({ panelInfo, isOnTop, spotlightId }) {
|
|||||||
// Voice Overlay가 닫힌 상황
|
// Voice Overlay가 닫힌 상황
|
||||||
if (!isVoiceOverlayVisible && isVoiceOverlayVisibleRef.current) {
|
if (!isVoiceOverlayVisible && isVoiceOverlayVisibleRef.current) {
|
||||||
if (DEBUG_MODE) {
|
if (DEBUG_MODE) {
|
||||||
console.log('[FOCUS] 🎯 Scenario: VOICE_OVERLAY_CLOSED', {
|
dlog('[FOCUS] 🎯 Scenario: VOICE_OVERLAY_CLOSED', {
|
||||||
hasShopperHouseData: !!shopperHouseData,
|
hasShopperHouseData: !!shopperHouseData,
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
@@ -974,7 +946,7 @@ export default function SearchPanel({ panelInfo, isOnTop, spotlightId }) {
|
|||||||
// 이렇게 하면 VOICE_OVERLAY_CLOSED 시나리오에서 TInput으로 가는 것을 방지
|
// 이렇게 하면 VOICE_OVERLAY_CLOSED 시나리오에서 TInput으로 가는 것을 방지
|
||||||
if (shopperHouseData && currentMode === SEARCH_PANEL_MODES.VOICE_RESULT) {
|
if (shopperHouseData && currentMode === SEARCH_PANEL_MODES.VOICE_RESULT) {
|
||||||
if (DEBUG_MODE) {
|
if (DEBUG_MODE) {
|
||||||
console.log('[FOCUS] 🔄 VOICE_OVERLAY_CLOSED + new data → NEW_SEARCH_LOADED 우선 처리');
|
dlog('[FOCUS] 🔄 VOICE_OVERLAY_CLOSED + new data → NEW_SEARCH_LOADED 우선 처리');
|
||||||
}
|
}
|
||||||
return 'NEW_SEARCH_LOADED';
|
return 'NEW_SEARCH_LOADED';
|
||||||
}
|
}
|
||||||
@@ -986,7 +958,7 @@ export default function SearchPanel({ panelInfo, isOnTop, spotlightId }) {
|
|||||||
// - 검색이 수행되지 않았거나 SearchPanel이 SEARCH_RESULT 모드가 아닌 경우
|
// - 검색이 수행되지 않았거나 SearchPanel이 SEARCH_RESULT 모드가 아닌 경우
|
||||||
if (!isSearchOverlayVisible && isSearchOverlayVisibleRef.current) {
|
if (!isSearchOverlayVisible && isSearchOverlayVisibleRef.current) {
|
||||||
if (DEBUG_MODE) {
|
if (DEBUG_MODE) {
|
||||||
console.log('[FOCUS] 🎯 Scenario: SEARCH_OVERLAY_CLOSED', {
|
dlog('[FOCUS] 🎯 Scenario: SEARCH_OVERLAY_CLOSED', {
|
||||||
isSearchOverlayVisible,
|
isSearchOverlayVisible,
|
||||||
prevIsSearchOverlayVisible: isSearchOverlayVisibleRef.current,
|
prevIsSearchOverlayVisible: isSearchOverlayVisibleRef.current,
|
||||||
currentMode,
|
currentMode,
|
||||||
@@ -1040,25 +1012,25 @@ export default function SearchPanel({ panelInfo, isOnTop, spotlightId }) {
|
|||||||
if (isReturningFromDetailPanel && currentPanel?.panelInfo?.currentSpot) {
|
if (isReturningFromDetailPanel && currentPanel?.panelInfo?.currentSpot) {
|
||||||
currentSpot = currentPanel.panelInfo.currentSpot;
|
currentSpot = currentPanel.panelInfo.currentSpot;
|
||||||
if (DEBUG_MODE) {
|
if (DEBUG_MODE) {
|
||||||
console.log('[FOCUS] 🎯 usePanelHistory currentSpot 사용:', currentSpot);
|
dlog('[FOCUS] 🎯 usePanelHistory currentSpot 사용:', currentSpot);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
// 2. fallback: 기존 panelInfo.currentSpot 사용
|
// 2. fallback: 기존 panelInfo.currentSpot 사용
|
||||||
else if (panelInfo?.currentSpot) {
|
else if (panelInfo?.currentSpot) {
|
||||||
currentSpot = panelInfo.currentSpot;
|
currentSpot = panelInfo.currentSpot;
|
||||||
if (DEBUG_MODE) {
|
if (DEBUG_MODE) {
|
||||||
console.log('[FOCUS] 🔄 fallback으로 panelInfo.currentSpot 사용:', currentSpot);
|
dlog('[FOCUS] 🔄 fallback으로 panelInfo.currentSpot 사용:', currentSpot);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if (currentSpot && currentSpot.startsWith('searchItemContents')) {
|
if (currentSpot && currentSpot.startsWith('searchItemContents')) {
|
||||||
if (DEBUG_MODE) {
|
if (DEBUG_MODE) {
|
||||||
console.log('[FOCUS] 🎯 DETAIL_PANEL_RETURN: 이전 상품으로 포커스 복원:', currentSpot);
|
dlog('[FOCUS] 🎯 DETAIL_PANEL_RETURN: 이전 상품으로 포커스 복원:', currentSpot);
|
||||||
}
|
}
|
||||||
return currentSpot;
|
return currentSpot;
|
||||||
} else {
|
} else {
|
||||||
if (DEBUG_MODE) {
|
if (DEBUG_MODE) {
|
||||||
console.log(
|
dlog(
|
||||||
'[FOCUS] ⚠️ DETAIL_PANEL_RETURN: currentSpot이 유효하지 않음, fallback으로 이동:',
|
'[FOCUS] ⚠️ DETAIL_PANEL_RETURN: currentSpot이 유효하지 않음, fallback으로 이동:',
|
||||||
{
|
{
|
||||||
currentSpot,
|
currentSpot,
|
||||||
@@ -1094,7 +1066,7 @@ export default function SearchPanel({ panelInfo, isOnTop, spotlightId }) {
|
|||||||
// SearchInputOverlay에서 검색을 실행하면 isSearchOverlayVisible이 false로 설정되고
|
// SearchInputOverlay에서 검색을 실행하면 isSearchOverlayVisible이 false로 설정되고
|
||||||
// 동시에 검색 결과에 따라 모드가 변경되므로, 이 케이스는 검색어 선택 후 닫을 때만 발생
|
// 동시에 검색 결과에 따라 모드가 변경되므로, 이 케이스는 검색어 선택 후 닫을 때만 발생
|
||||||
if (DEBUG_MODE) {
|
if (DEBUG_MODE) {
|
||||||
console.log('[FOCUS] 🎯 Scenario: SEARCH_OVERLAY_CLOSED - TInputSimple으로 포커스');
|
dlog('[FOCUS] 🎯 Scenario: SEARCH_OVERLAY_CLOSED - TInputSimple으로 포커스');
|
||||||
}
|
}
|
||||||
return SPOTLIGHT_IDS.SEARCH_INPUT_BOX;
|
return SPOTLIGHT_IDS.SEARCH_INPUT_BOX;
|
||||||
|
|
||||||
@@ -1118,7 +1090,7 @@ export default function SearchPanel({ panelInfo, isOnTop, spotlightId }) {
|
|||||||
*/
|
*/
|
||||||
const handleTransitionToSearchInput = useCallback(() => {
|
const handleTransitionToSearchInput = useCallback(() => {
|
||||||
if (DEBUG_MODE) {
|
if (DEBUG_MODE) {
|
||||||
console.log('[SearchPanel] 🔄 handleTransitionToSearchInput 호출');
|
dlog('[SearchPanel] 🔄 handleTransitionToSearchInput 호출');
|
||||||
}
|
}
|
||||||
|
|
||||||
// Redux Thunk 액션으로 모든 전환 로직 처리
|
// Redux Thunk 액션으로 모든 전환 로직 처리
|
||||||
@@ -1135,12 +1107,12 @@ export default function SearchPanel({ panelInfo, isOnTop, spotlightId }) {
|
|||||||
* Search overlay close handler
|
* Search overlay close handler
|
||||||
*/
|
*/
|
||||||
const handleSearchOverlayClose = useCallback(() => {
|
const handleSearchOverlayClose = useCallback(() => {
|
||||||
console.log('[DEBUG] 🚪 handleSearchOverlayClose 호출됨 - 직접 확인!', {
|
dlog('[DEBUG] 🚪 handleSearchOverlayClose 호출됨 - 직접 확인!', {
|
||||||
timestamp: new Date().toISOString(),
|
timestamp: new Date().toISOString(),
|
||||||
});
|
});
|
||||||
|
|
||||||
if (DEBUG_MODE) {
|
if (DEBUG_MODE) {
|
||||||
console.log('[DEBUG] 🚪 SearchInputOverlay closing');
|
dlog('[DEBUG] 🚪 SearchInputOverlay closing');
|
||||||
}
|
}
|
||||||
|
|
||||||
const hasSearchResults =
|
const hasSearchResults =
|
||||||
@@ -1149,11 +1121,11 @@ export default function SearchPanel({ panelInfo, isOnTop, spotlightId }) {
|
|||||||
(searchDatas?.show?.length || 0) > 0;
|
(searchDatas?.show?.length || 0) > 0;
|
||||||
|
|
||||||
// 🎯 SearchInputOverlay 닫힘 후 TInputSimple으로 포커스 이동을 위한 플래그 설정
|
// 🎯 SearchInputOverlay 닫힘 후 TInputSimple으로 포커스 이동을 위한 플래그 설정
|
||||||
console.log('[DEBUG] setShouldFocusSearchInput(true) 설정 직전', {
|
dlog('[DEBUG] setShouldFocusSearchInput(true) 설정 직전', {
|
||||||
timestamp: new Date().toISOString(),
|
timestamp: new Date().toISOString(),
|
||||||
});
|
});
|
||||||
setShouldFocusSearchInput(true);
|
setShouldFocusSearchInput(true);
|
||||||
console.log('[DEBUG] setShouldFocusSearchInput(true) 설정됨!', {
|
dlog('[DEBUG] setShouldFocusSearchInput(true) 설정됨!', {
|
||||||
timestamp: new Date().toISOString(),
|
timestamp: new Date().toISOString(),
|
||||||
});
|
});
|
||||||
|
|
||||||
@@ -1171,7 +1143,7 @@ export default function SearchPanel({ panelInfo, isOnTop, spotlightId }) {
|
|||||||
*/
|
*/
|
||||||
const handleVoiceOverlayClose = useCallback(() => {
|
const handleVoiceOverlayClose = useCallback(() => {
|
||||||
if (DEBUG_MODE) {
|
if (DEBUG_MODE) {
|
||||||
console.log(
|
dlog(
|
||||||
'🚪 [DEBUG][SearchPanel] handleVoiceOverlayClose called, setting isVoiceOverlayVisible to FALSE'
|
'🚪 [DEBUG][SearchPanel] handleVoiceOverlayClose called, setting isVoiceOverlayVisible to FALSE'
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
@@ -1213,7 +1185,7 @@ export default function SearchPanel({ panelInfo, isOnTop, spotlightId }) {
|
|||||||
|
|
||||||
// Redux state 업데이트를 위해 약간의 지연 후 API 호출
|
// Redux state 업데이트를 위해 약간의 지연 후 API 호출
|
||||||
setTimeout(() => {
|
setTimeout(() => {
|
||||||
console.log('[HowAboutThese] 🔄 Redux 업데이트 후 API 호출');
|
dlog('[HowAboutThese] 🔄 Redux 업데이트 후 API 호출');
|
||||||
dispatch(getShopperHouseSearch(trimmedQuery, shopperHouseSearchId));
|
dispatch(getShopperHouseSearch(trimmedQuery, shopperHouseSearchId));
|
||||||
}, 50); // 50ms 지연
|
}, 50); // 50ms 지연
|
||||||
|
|
||||||
@@ -1337,40 +1309,40 @@ export default function SearchPanel({ panelInfo, isOnTop, spotlightId }) {
|
|||||||
[hotPicksForYou, dispatch, SafeImage]
|
[hotPicksForYou, dispatch, SafeImage]
|
||||||
);
|
);
|
||||||
|
|
||||||
const handleClick = useCallback((patnrId, prdtId) => {
|
const handleClick = useCallback(
|
||||||
dispatch(
|
(patnrId, prdtId) => {
|
||||||
pushPanel({
|
dispatch(
|
||||||
name: panel_names.DETAIL_PANEL,
|
pushPanel({
|
||||||
panelInfo: { patnrId, prdtId },
|
name: panel_names.DETAIL_PANEL,
|
||||||
})
|
panelInfo: { patnrId, prdtId },
|
||||||
);
|
})
|
||||||
},[dispatch])
|
);
|
||||||
|
},
|
||||||
|
[dispatch]
|
||||||
|
);
|
||||||
|
|
||||||
const renderTsvItem = useCallback(
|
const renderTsvItem = useCallback(
|
||||||
({ index, ...rest }) => {
|
({ index, ...rest }) => {
|
||||||
const { offerInfo, prdtId, imgUrl, patnrId, prdtNm, priceInfo } =
|
const { offerInfo, prdtId, imgUrl, patnrId, prdtNm, priceInfo } = tsvInfo[index];
|
||||||
tsvInfo[index];
|
|
||||||
|
|
||||||
return (
|
|
||||||
<TItemCardNew
|
|
||||||
imageAlt={prdtNm}
|
|
||||||
imageSource={imgUrl}
|
|
||||||
onClick={()=>{handleClick(patnrId,prdtId)}}
|
|
||||||
offerInfo={offerInfo}
|
|
||||||
priceInfo={priceInfo}
|
|
||||||
productId={prdtId}
|
|
||||||
productName={prdtNm}
|
|
||||||
spotlightId={
|
|
||||||
"searchMain-tsvInfo-spotlightId-" + removeDotAndColon(prdtId)
|
|
||||||
}
|
|
||||||
{...rest}
|
|
||||||
/>
|
|
||||||
);
|
|
||||||
},
|
|
||||||
[tsvInfo,handleClick]
|
|
||||||
);
|
|
||||||
|
|
||||||
|
return (
|
||||||
|
<TItemCardNew
|
||||||
|
imageAlt={prdtNm}
|
||||||
|
imageSource={imgUrl}
|
||||||
|
onClick={() => {
|
||||||
|
handleClick(patnrId, prdtId);
|
||||||
|
}}
|
||||||
|
offerInfo={offerInfo}
|
||||||
|
priceInfo={priceInfo}
|
||||||
|
productId={prdtId}
|
||||||
|
productName={prdtNm}
|
||||||
|
spotlightId={'searchMain-tsvInfo-spotlightId-' + removeDotAndColon(prdtId)}
|
||||||
|
{...rest}
|
||||||
|
/>
|
||||||
|
);
|
||||||
|
},
|
||||||
|
[tsvInfo, handleClick]
|
||||||
|
);
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* ✨ [Phase 2] 모드별 콘텐츠 렌더링 (VoiceInputOverlay의 renderModeContent와 동일한 패턴)
|
* ✨ [Phase 2] 모드별 콘텐츠 렌더링 (VoiceInputOverlay의 renderModeContent와 동일한 패턴)
|
||||||
@@ -1561,11 +1533,9 @@ export default function SearchPanel({ panelInfo, isOnTop, spotlightId }) {
|
|||||||
spacing={18}
|
spacing={18}
|
||||||
/>
|
/>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
</SectionContainer>
|
</SectionContainer>
|
||||||
</>
|
</>
|
||||||
)}
|
)}
|
||||||
|
|
||||||
</ContainerBasic>
|
</ContainerBasic>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
@@ -1606,7 +1576,7 @@ export default function SearchPanel({ panelInfo, isOnTop, spotlightId }) {
|
|||||||
let nextMode = SEARCH_PANEL_MODES.INITIAL;
|
let nextMode = SEARCH_PANEL_MODES.INITIAL;
|
||||||
|
|
||||||
if (DEBUG_MODE) {
|
if (DEBUG_MODE) {
|
||||||
console.log('[DEBUG]-MODE DECISION useEffect running', {
|
dlog('[DEBUG]-MODE DECISION useEffect running', {
|
||||||
isVoiceOverlayVisible,
|
isVoiceOverlayVisible,
|
||||||
hasShopperHouseData: !!shopperHouseData,
|
hasShopperHouseData: !!shopperHouseData,
|
||||||
shopperHouseData_detail: shopperHouseData ? 'EXISTS' : 'NULL',
|
shopperHouseData_detail: shopperHouseData ? 'EXISTS' : 'NULL',
|
||||||
@@ -1626,14 +1596,14 @@ export default function SearchPanel({ panelInfo, isOnTop, spotlightId }) {
|
|||||||
// 우선순위 1: 음성 입력 오버레이가 열려있으면 VOICE_INPUT 모드
|
// 우선순위 1: 음성 입력 오버레이가 열려있으면 VOICE_INPUT 모드
|
||||||
if (isVoiceOverlayVisible) {
|
if (isVoiceOverlayVisible) {
|
||||||
if (DEBUG_MODE) {
|
if (DEBUG_MODE) {
|
||||||
console.log('[DEBUG]-MODE: isVoiceOverlayVisible is TRUE → VOICE_INPUT');
|
dlog('[DEBUG]-MODE: isVoiceOverlayVisible is TRUE → VOICE_INPUT');
|
||||||
}
|
}
|
||||||
nextMode = SEARCH_PANEL_MODES.VOICE_INPUT;
|
nextMode = SEARCH_PANEL_MODES.VOICE_INPUT;
|
||||||
}
|
}
|
||||||
// 우선순위 2: 음성 검색 결과가 있으면 VOICE_RESULT 모드
|
// 우선순위 2: 음성 검색 결과가 있으면 VOICE_RESULT 모드
|
||||||
else if (shopperHouseData || isShopperHousePending) {
|
else if (shopperHouseData || isShopperHousePending) {
|
||||||
if (DEBUG_MODE) {
|
if (DEBUG_MODE) {
|
||||||
console.log('[DEBUG]-MODE: shopperHouseData EXISTS or pending → VOICE_RESULT', {
|
dlog('[DEBUG]-MODE: shopperHouseData EXISTS or pending → VOICE_RESULT', {
|
||||||
hasData: !!shopperHouseData,
|
hasData: !!shopperHouseData,
|
||||||
isPending: isShopperHousePending,
|
isPending: isShopperHousePending,
|
||||||
});
|
});
|
||||||
@@ -1648,21 +1618,21 @@ export default function SearchPanel({ panelInfo, isOnTop, spotlightId }) {
|
|||||||
searchDatas?.show?.length > 0
|
searchDatas?.show?.length > 0
|
||||||
) {
|
) {
|
||||||
if (DEBUG_MODE) {
|
if (DEBUG_MODE) {
|
||||||
console.log('[DEBUG]-MODE: searchResults EXISTS → SEARCH_RESULT');
|
dlog('[DEBUG]-MODE: searchResults EXISTS → SEARCH_RESULT');
|
||||||
}
|
}
|
||||||
nextMode = SEARCH_PANEL_MODES.SEARCH_RESULT;
|
nextMode = SEARCH_PANEL_MODES.SEARCH_RESULT;
|
||||||
}
|
}
|
||||||
// 우선순위 4: 검색 입력 오버레이가 열려있으면 SEARCH_INPUT 모드
|
// 우선순위 4: 검색 입력 오버레이가 열려있으면 SEARCH_INPUT 모드
|
||||||
else if (isSearchOverlayVisible) {
|
else if (isSearchOverlayVisible) {
|
||||||
if (DEBUG_MODE) {
|
if (DEBUG_MODE) {
|
||||||
console.log('[DEBUG]-MODE: isSearchOverlayVisible is TRUE → SEARCH_INPUT');
|
dlog('[DEBUG]-MODE: isSearchOverlayVisible is TRUE → SEARCH_INPUT');
|
||||||
}
|
}
|
||||||
nextMode = SEARCH_PANEL_MODES.SEARCH_INPUT;
|
nextMode = SEARCH_PANEL_MODES.SEARCH_INPUT;
|
||||||
}
|
}
|
||||||
// 우선순위 5: 초기 상태 (기본값)
|
// 우선순위 5: 초기 상태 (기본값)
|
||||||
else {
|
else {
|
||||||
if (DEBUG_MODE) {
|
if (DEBUG_MODE) {
|
||||||
console.log('[DEBUG]-MODE: No condition met → INITIAL');
|
dlog('[DEBUG]-MODE: No condition met → INITIAL');
|
||||||
}
|
}
|
||||||
nextMode = SEARCH_PANEL_MODES.INITIAL;
|
nextMode = SEARCH_PANEL_MODES.INITIAL;
|
||||||
}
|
}
|
||||||
@@ -1670,7 +1640,7 @@ export default function SearchPanel({ panelInfo, isOnTop, spotlightId }) {
|
|||||||
// 모드가 변경되었을 때만 업데이트
|
// 모드가 변경되었을 때만 업데이트
|
||||||
if (nextMode !== currentMode) {
|
if (nextMode !== currentMode) {
|
||||||
if (DEBUG_MODE) {
|
if (DEBUG_MODE) {
|
||||||
console.log(`[DEBUG]-VOICE_RESULT 🔀 Mode changed: ${currentMode} → ${nextMode}`, {
|
dlog(`[DEBUG]-VOICE_RESULT 🔀 Mode changed: ${currentMode} → ${nextMode}`, {
|
||||||
isVoiceOverlayVisible,
|
isVoiceOverlayVisible,
|
||||||
shopperHouseData: !!shopperHouseData,
|
shopperHouseData: !!shopperHouseData,
|
||||||
isShopperHousePending,
|
isShopperHousePending,
|
||||||
@@ -1688,7 +1658,7 @@ export default function SearchPanel({ panelInfo, isOnTop, spotlightId }) {
|
|||||||
setCurrentMode(nextMode);
|
setCurrentMode(nextMode);
|
||||||
} else {
|
} else {
|
||||||
if (DEBUG_MODE) {
|
if (DEBUG_MODE) {
|
||||||
console.log('[DEBUG]-MODE: Mode unchanged -', currentMode);
|
dlog('[DEBUG]-MODE: Mode unchanged -', currentMode);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}, [
|
}, [
|
||||||
@@ -1711,11 +1681,11 @@ export default function SearchPanel({ panelInfo, isOnTop, spotlightId }) {
|
|||||||
*/
|
*/
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
if (DEBUG_MODE) {
|
if (DEBUG_MODE) {
|
||||||
console.log('[DEBUG]-searchQuery useEffect:', { searchQuery });
|
dlog('[DEBUG]-searchQuery useEffect:', { searchQuery });
|
||||||
}
|
}
|
||||||
if (!searchQuery) {
|
if (!searchQuery) {
|
||||||
if (DEBUG_MODE) {
|
if (DEBUG_MODE) {
|
||||||
console.log('[DEBUG]-VOICE_RESULT: searchQuery is empty, calling resetSearch');
|
dlog('[DEBUG]-VOICE_RESULT: searchQuery is empty, calling resetSearch');
|
||||||
}
|
}
|
||||||
dispatch(resetSearch());
|
dispatch(resetSearch());
|
||||||
}
|
}
|
||||||
@@ -1757,7 +1727,7 @@ export default function SearchPanel({ panelInfo, isOnTop, spotlightId }) {
|
|||||||
*/
|
*/
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
if (DEBUG_MODE) {
|
if (DEBUG_MODE) {
|
||||||
console.log('[DEBUG][Focus] Focus useEffect 호출됨 - 상태값 확인:', {
|
dlog('[DEBUG][Focus] Focus useEffect 호출됨 - 상태값 확인:', {
|
||||||
isOnTop,
|
isOnTop,
|
||||||
panelInfo: panelInfo,
|
panelInfo: panelInfo,
|
||||||
currentMode,
|
currentMode,
|
||||||
@@ -1809,9 +1779,7 @@ export default function SearchPanel({ panelInfo, isOnTop, spotlightId }) {
|
|||||||
// NEW_SEARCH_LOADED: 음성 검색 결과 로드 시 VoiceInputOverlay와 충돌 방지
|
// NEW_SEARCH_LOADED: 음성 검색 결과 로드 시 VoiceInputOverlay와 충돌 방지
|
||||||
// 다른 시나리오에서는 기존과 같은 지연 시간 (100ms)
|
// 다른 시나리오에서는 기존과 같은 지연 시간 (100ms)
|
||||||
const focusDelay =
|
const focusDelay =
|
||||||
scenario === 'DETAIL_PANEL_RETURN' || scenario === 'NEW_SEARCH_LOADED'
|
scenario === 'DETAIL_PANEL_RETURN' || scenario === 'NEW_SEARCH_LOADED' ? 50 : 100;
|
||||||
? 50
|
|
||||||
: 100;
|
|
||||||
|
|
||||||
unifiedFocusTimerRef.current = setTimeout(() => {
|
unifiedFocusTimerRef.current = setTimeout(() => {
|
||||||
const targetElement = document.querySelector(`[data-spotlight-id="${targetId}"]`);
|
const targetElement = document.querySelector(`[data-spotlight-id="${targetId}"]`);
|
||||||
@@ -1819,7 +1787,7 @@ export default function SearchPanel({ panelInfo, isOnTop, spotlightId }) {
|
|||||||
if (targetElement || targetId === SPOTLIGHT_IDS.SEARCH_INPUT_BOX) {
|
if (targetElement || targetId === SPOTLIGHT_IDS.SEARCH_INPUT_BOX) {
|
||||||
Spotlight.focus(targetId);
|
Spotlight.focus(targetId);
|
||||||
if (DEBUG_MODE) {
|
if (DEBUG_MODE) {
|
||||||
console.log('[FOCUS] ✅ 포커스 이동 완료:', {
|
dlog('[FOCUS] ✅ 포커스 이동 완료:', {
|
||||||
targetId,
|
targetId,
|
||||||
scenario,
|
scenario,
|
||||||
hasElement: !!targetElement,
|
hasElement: !!targetElement,
|
||||||
@@ -1829,7 +1797,7 @@ export default function SearchPanel({ panelInfo, isOnTop, spotlightId }) {
|
|||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
if (DEBUG_MODE) {
|
if (DEBUG_MODE) {
|
||||||
console.log('[FOCUS] ⚠️ 포커스 대상 요소를 찾지 못했습니다:', {
|
dlog('[FOCUS] ⚠️ 포커스 대상 요소를 찾지 못했습니다:', {
|
||||||
targetId,
|
targetId,
|
||||||
scenario,
|
scenario,
|
||||||
timestamp: new Date().toISOString(),
|
timestamp: new Date().toISOString(),
|
||||||
@@ -1842,7 +1810,7 @@ export default function SearchPanel({ panelInfo, isOnTop, spotlightId }) {
|
|||||||
const fallbackElement = document.querySelector(`[data-spotlight-id="${fallbackTarget}"]`);
|
const fallbackElement = document.querySelector(`[data-spotlight-id="${fallbackTarget}"]`);
|
||||||
if (fallbackElement) {
|
if (fallbackElement) {
|
||||||
if (DEBUG_MODE) {
|
if (DEBUG_MODE) {
|
||||||
console.log(
|
dlog(
|
||||||
'[FOCUS] 🔄 DETAIL_PANEL_RETURN fallback: 첫 번째 상품으로 포커스:',
|
'[FOCUS] 🔄 DETAIL_PANEL_RETURN fallback: 첫 번째 상품으로 포커스:',
|
||||||
fallbackTarget
|
fallbackTarget
|
||||||
);
|
);
|
||||||
@@ -1860,7 +1828,7 @@ export default function SearchPanel({ panelInfo, isOnTop, spotlightId }) {
|
|||||||
if (scenario === 'NEW_SEARCH_LOADED' && targetId === 'searchItemContents0') {
|
if (scenario === 'NEW_SEARCH_LOADED' && targetId === 'searchItemContents0') {
|
||||||
setTimeout(() => {
|
setTimeout(() => {
|
||||||
if (DEBUG_MODE) {
|
if (DEBUG_MODE) {
|
||||||
console.log('[FOCUS] 🔄 NEW_SEARCH_LOADED: 1초 후 첫 번째 상품으로 다시 포커스 이동');
|
dlog('[FOCUS] 🔄 NEW_SEARCH_LOADED: 1초 후 첫 번째 상품으로 다시 포커스 이동');
|
||||||
}
|
}
|
||||||
Spotlight.focus('searchItemContents0');
|
Spotlight.focus('searchItemContents0');
|
||||||
}, 500); // 0.5초 후
|
}, 500); // 0.5초 후
|
||||||
@@ -1908,7 +1876,7 @@ export default function SearchPanel({ panelInfo, isOnTop, spotlightId }) {
|
|||||||
* - 자동으로 플래그 초기화
|
* - 자동으로 플래그 초기화
|
||||||
*/
|
*/
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
console.log('[DEBUG] shouldFocusSearchInput useEffect 실행됨!', {
|
dlog('[DEBUG] shouldFocusSearchInput useEffect 실행됨!', {
|
||||||
shouldFocusSearchInput,
|
shouldFocusSearchInput,
|
||||||
timestamp: new Date().toISOString(),
|
timestamp: new Date().toISOString(),
|
||||||
});
|
});
|
||||||
@@ -1916,12 +1884,12 @@ export default function SearchPanel({ panelInfo, isOnTop, spotlightId }) {
|
|||||||
if (shouldFocusSearchInput) {
|
if (shouldFocusSearchInput) {
|
||||||
let focusTimer = null;
|
let focusTimer = null;
|
||||||
|
|
||||||
console.log('[DEBUG] shouldFocusSearchInput === true, 타이머 설정 중...', {
|
dlog('[DEBUG] shouldFocusSearchInput === true, 타이머 설정 중...', {
|
||||||
timestamp: new Date().toISOString(),
|
timestamp: new Date().toISOString(),
|
||||||
});
|
});
|
||||||
|
|
||||||
if (DEBUG_MODE) {
|
if (DEBUG_MODE) {
|
||||||
console.log('[FOCUS] 🎯 SearchInputOverlay 닫힘 후 포커스 관리 useEffect 실행', {
|
dlog('[FOCUS] 🎯 SearchInputOverlay 닫힘 후 포커스 관리 useEffect 실행', {
|
||||||
shouldFocusSearchInput,
|
shouldFocusSearchInput,
|
||||||
timestamp: new Date().toISOString(),
|
timestamp: new Date().toISOString(),
|
||||||
});
|
});
|
||||||
@@ -1929,65 +1897,62 @@ export default function SearchPanel({ panelInfo, isOnTop, spotlightId }) {
|
|||||||
|
|
||||||
// 500ms 후 TInputSimple에 포커스 이동
|
// 500ms 후 TInputSimple에 포커스 이동
|
||||||
focusTimer = setTimeout(() => {
|
focusTimer = setTimeout(() => {
|
||||||
console.log('[DEBUG] ⏰ 500ms 타이머 콜백 실행!', {
|
dlog('[DEBUG] ⏰ 500ms 타이머 콜백 실행!', {
|
||||||
targetId: SPOTLIGHT_IDS.SEARCH_INPUT_BOX,
|
targetId: SPOTLIGHT_IDS.SEARCH_INPUT_BOX,
|
||||||
timestamp: new Date().toISOString(),
|
timestamp: new Date().toISOString(),
|
||||||
});
|
});
|
||||||
|
|
||||||
if (DEBUG_MODE) {
|
if (DEBUG_MODE) {
|
||||||
console.log('[FOCUS] ⏰ 500ms 타이머 콜백 실행 - TInputSimple으로 포커스 이동', {
|
dlog('[FOCUS] ⏰ 500ms 타이머 콜백 실행 - TInputSimple으로 포커스 이동', {
|
||||||
targetId: SPOTLIGHT_IDS.SEARCH_INPUT_BOX,
|
targetId: SPOTLIGHT_IDS.SEARCH_INPUT_BOX,
|
||||||
timestamp: new Date().toISOString(),
|
timestamp: new Date().toISOString(),
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
console.log('[DEBUG] Spotlight.focus() 호출 직전', {
|
dlog('[DEBUG] Spotlight.focus() 호출 직전', {
|
||||||
targetId: SPOTLIGHT_IDS.SEARCH_INPUT_BOX,
|
targetId: SPOTLIGHT_IDS.SEARCH_INPUT_BOX,
|
||||||
});
|
});
|
||||||
|
|
||||||
Spotlight.focus(SPOTLIGHT_IDS.SEARCH_INPUT_BOX);
|
Spotlight.focus(SPOTLIGHT_IDS.SEARCH_INPUT_BOX);
|
||||||
|
|
||||||
console.log('[DEBUG] Spotlight.focus() 호출 완료!', {
|
dlog('[DEBUG] Spotlight.focus() 호출 완료!', {
|
||||||
targetId: SPOTLIGHT_IDS.SEARCH_INPUT_BOX,
|
targetId: SPOTLIGHT_IDS.SEARCH_INPUT_BOX,
|
||||||
timestamp: new Date().toISOString(),
|
timestamp: new Date().toISOString(),
|
||||||
});
|
});
|
||||||
|
|
||||||
if (DEBUG_MODE) {
|
if (DEBUG_MODE) {
|
||||||
console.log('[FOCUS] ✅ Spotlight.focus() 호출 완료', {
|
dlog('[FOCUS] ✅ Spotlight.focus() 호출 완료', {
|
||||||
targetId: SPOTLIGHT_IDS.SEARCH_INPUT_BOX,
|
targetId: SPOTLIGHT_IDS.SEARCH_INPUT_BOX,
|
||||||
timestamp: new Date().toISOString(),
|
timestamp: new Date().toISOString(),
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
console.log('[DEBUG] setShouldFocusSearchInput(false) 호출 직전', {
|
dlog('[DEBUG] setShouldFocusSearchInput(false) 호출 직전', {
|
||||||
timestamp: new Date().toISOString(),
|
timestamp: new Date().toISOString(),
|
||||||
});
|
});
|
||||||
|
|
||||||
// 포커스 이동 완료 후 플래그 초기화
|
// 포커스 이동 완료 후 플래그 초기화
|
||||||
setShouldFocusSearchInput(false);
|
setShouldFocusSearchInput(false);
|
||||||
|
|
||||||
console.log('[DEBUG] setShouldFocusSearchInput(false) 호출 완료!', {
|
dlog('[DEBUG] setShouldFocusSearchInput(false) 호출 완료!', {
|
||||||
timestamp: new Date().toISOString(),
|
timestamp: new Date().toISOString(),
|
||||||
});
|
});
|
||||||
}, 500);
|
}, 500);
|
||||||
|
|
||||||
console.log('[DEBUG] 타이머 설정 완료 - 500ms 후 포커스 이동 예약됨', {
|
dlog('[DEBUG] 타이머 설정 완료 - 500ms 후 포커스 이동 예약됨', {
|
||||||
timestamp: new Date().toISOString(),
|
timestamp: new Date().toISOString(),
|
||||||
});
|
});
|
||||||
|
|
||||||
// Cleanup: 타이머 정리
|
// Cleanup: 타이머 정리
|
||||||
return () => {
|
return () => {
|
||||||
console.log('[DEBUG] shouldFocusSearchInput useEffect cleanup 실행', {
|
dlog('[DEBUG] shouldFocusSearchInput useEffect cleanup 실행', {
|
||||||
timestamp: new Date().toISOString(),
|
timestamp: new Date().toISOString(),
|
||||||
});
|
});
|
||||||
if (focusTimer) {
|
if (focusTimer) {
|
||||||
if (DEBUG_MODE) {
|
if (DEBUG_MODE) {
|
||||||
console.log(
|
dlog('[FOCUS] 🧹 SearchInputOverlay 포커스 관리 useEffect cleanup - 타이머 정리', {
|
||||||
'[FOCUS] 🧹 SearchInputOverlay 포커스 관리 useEffect cleanup - 타이머 정리',
|
timestamp: new Date().toISOString(),
|
||||||
{
|
});
|
||||||
timestamp: new Date().toISOString(),
|
|
||||||
}
|
|
||||||
);
|
|
||||||
}
|
}
|
||||||
clearTimeout(focusTimer);
|
clearTimeout(focusTimer);
|
||||||
}
|
}
|
||||||
@@ -2149,7 +2114,7 @@ export default function SearchPanel({ panelInfo, isOnTop, spotlightId }) {
|
|||||||
} // ✅ INITIAL, VOICE_RESULT & SEARCH_RESULT 모드에서 TInputSimple 내부 포커스 활성화
|
} // ✅ INITIAL, VOICE_RESULT & SEARCH_RESULT 모드에서 TInputSimple 내부 포커스 활성화
|
||||||
onKeyDown={handleKeydown}
|
onKeyDown={handleKeydown}
|
||||||
onMouseDown={() => {
|
onMouseDown={() => {
|
||||||
console.log(
|
dlog(
|
||||||
'[DEBUG] [SearchPanel] TInputSimple에서 마우스 클릭 감지 → SearchInputOverlay 오픈'
|
'[DEBUG] [SearchPanel] TInputSimple에서 마우스 클릭 감지 → SearchInputOverlay 오픈'
|
||||||
);
|
);
|
||||||
setIsSearchOverlayVisible(true);
|
setIsSearchOverlayVisible(true);
|
||||||
|
|||||||
@@ -34,6 +34,7 @@ import { readLocalStorage, writeLocalStorage } from '../../../utils/helperMethod
|
|||||||
import TInputSimple, { ICONS, KINDS } from '../TInput/TInputSimple';
|
import TInputSimple, { ICONS, KINDS } from '../TInput/TInputSimple';
|
||||||
import ApiStatusDisplay from './ApiStatusDisplay';
|
import ApiStatusDisplay from './ApiStatusDisplay';
|
||||||
import VoiceApiError from './modes/VoiceApiError';
|
import VoiceApiError from './modes/VoiceApiError';
|
||||||
|
import { createDebugHelpers } from '../../../utils/debug';
|
||||||
import VoiceListening from './modes/VoiceListening';
|
import VoiceListening from './modes/VoiceListening';
|
||||||
import VoiceNotRecognized from './modes/VoiceNotRecognized';
|
import VoiceNotRecognized from './modes/VoiceNotRecognized';
|
||||||
import VoiceNotRecognizedCircle from './modes/VoiceNotRecognizedCircle';
|
import VoiceNotRecognizedCircle from './modes/VoiceNotRecognizedCircle';
|
||||||
@@ -56,6 +57,7 @@ const SpottableDebugButton = Spottable('div');
|
|||||||
|
|
||||||
// Debug mode constant - 항상 디버그 화면 표시
|
// Debug mode constant - 항상 디버그 화면 표시
|
||||||
const DEBUG_MODE = false;
|
const DEBUG_MODE = false;
|
||||||
|
const { dlog, dwarn, derror } = createDebugHelpers(DEBUG_MODE);
|
||||||
|
|
||||||
// Voice overlay 모드 상수
|
// Voice overlay 모드 상수
|
||||||
export const VOICE_MODES = {
|
export const VOICE_MODES = {
|
||||||
@@ -150,7 +152,7 @@ const VoiceInputOverlay = ({
|
|||||||
externalShopperHouseData = null, // 🎯 [포커스 충돌 해결] 외부 음성 검색 결과 데이터
|
externalShopperHouseData = null, // 🎯 [포커스 충돌 해결] 외부 음성 검색 결과 데이터
|
||||||
}) => {
|
}) => {
|
||||||
if (DEBUG_MODE) {
|
if (DEBUG_MODE) {
|
||||||
console.log('🔄 [DEBUG] VoiceInputOverlay render - isVisible:', isVisible, 'mode:', mode);
|
dlog('🔄 [DEBUG] VoiceInputOverlay render - isVisible:', isVisible, 'mode:', mode);
|
||||||
}
|
}
|
||||||
|
|
||||||
const dispatch = useDispatch();
|
const dispatch = useDispatch();
|
||||||
@@ -198,7 +200,7 @@ const VoiceInputOverlay = ({
|
|||||||
const [webSpeechEventLogs, setWebSpeechEventLogs] = useState(() => {
|
const [webSpeechEventLogs, setWebSpeechEventLogs] = useState(() => {
|
||||||
const persisted = readLocalStorage(VOICE_EVENT_LOGS_KEY, []);
|
const persisted = readLocalStorage(VOICE_EVENT_LOGS_KEY, []);
|
||||||
if (DEBUG_MODE) {
|
if (DEBUG_MODE) {
|
||||||
console.log('📚 [DEBUG] Loaded webSpeechEventLogs from localStorage:', persisted);
|
dlog('📚 [DEBUG] Loaded webSpeechEventLogs from localStorage:', persisted);
|
||||||
}
|
}
|
||||||
return persisted;
|
return persisted;
|
||||||
});
|
});
|
||||||
@@ -222,7 +224,7 @@ const VoiceInputOverlay = ({
|
|||||||
const [legacySearchHistory, setLegacySearchHistory] = useState(() => {
|
const [legacySearchHistory, setLegacySearchHistory] = useState(() => {
|
||||||
const history = readLocalStorage(SEARCH_HISTORY_KEY, DEFAULT_SUGGESTIONS);
|
const history = readLocalStorage(SEARCH_HISTORY_KEY, DEFAULT_SUGGESTIONS);
|
||||||
if (DEBUG_MODE) {
|
if (DEBUG_MODE) {
|
||||||
console.log('📚 [DEBUG] Loaded legacy searchHistory from localStorage:', history);
|
dlog('📚 [DEBUG] Loaded legacy searchHistory from localStorage:', history);
|
||||||
}
|
}
|
||||||
return history;
|
return history;
|
||||||
});
|
});
|
||||||
@@ -231,11 +233,11 @@ const VoiceInputOverlay = ({
|
|||||||
|
|
||||||
// ⭐ isVisible prop 변경 추적
|
// ⭐ isVisible prop 변경 추적
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
console.log('👁️ [DEBUG][VoiceInputOverlay] isVisible prop changed to:', isVisible);
|
dlog('👁️ [DEBUG][VoiceInputOverlay] isVisible prop changed to:', isVisible);
|
||||||
if (isVisible) {
|
if (isVisible) {
|
||||||
console.log('📺 [DEBUG][VoiceInputOverlay] Overlay is now VISIBLE, mode:', currentMode);
|
dlog('📺 [DEBUG][VoiceInputOverlay] Overlay is now VISIBLE, mode:', currentMode);
|
||||||
} else {
|
} else {
|
||||||
console.log('🙈 [DEBUG][VoiceInputOverlay] Overlay is now HIDDEN');
|
dlog('🙈 [DEBUG][VoiceInputOverlay] Overlay is now HIDDEN');
|
||||||
}
|
}
|
||||||
}, [isVisible, currentMode]);
|
}, [isVisible, currentMode]);
|
||||||
|
|
||||||
@@ -268,7 +270,7 @@ const VoiceInputOverlay = ({
|
|||||||
};
|
};
|
||||||
|
|
||||||
if (DEBUG_MODE) {
|
if (DEBUG_MODE) {
|
||||||
console.log(`[WebSpeech Event] ${event}: ${details}`);
|
dlog(`[WebSpeech Event] ${event}: ${details}`);
|
||||||
}
|
}
|
||||||
|
|
||||||
setWebSpeechEventLogs((prev) => {
|
setWebSpeechEventLogs((prev) => {
|
||||||
@@ -281,11 +283,9 @@ const VoiceInputOverlay = ({
|
|||||||
|
|
||||||
// ⭐ currentMode 변경 추적
|
// ⭐ currentMode 변경 추적
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
console.log('🔀 [DEBUG][VoiceInputOverlay] currentMode changed to:', currentMode);
|
dlog('🔀 [DEBUG][VoiceInputOverlay] currentMode changed to:', currentMode);
|
||||||
if (isVisible) {
|
if (isVisible) {
|
||||||
console.log(
|
dlog(`📍 [DEBUG][VoiceInputOverlay] Current state: isVisible=true, mode=${currentMode}`);
|
||||||
`📍 [DEBUG][VoiceInputOverlay] Current state: isVisible=true, mode=${currentMode}`
|
|
||||||
);
|
|
||||||
// 모드 변경 이벤트 로깅
|
// 모드 변경 이벤트 로깅
|
||||||
addWebSpeechEventLog('MODE_CHANGE', `Mode switched to: ${currentMode}`);
|
addWebSpeechEventLog('MODE_CHANGE', `Mode switched to: ${currentMode}`);
|
||||||
}
|
}
|
||||||
@@ -296,7 +296,7 @@ const VoiceInputOverlay = ({
|
|||||||
if (webSpeechEventLogs.length > 0) {
|
if (webSpeechEventLogs.length > 0) {
|
||||||
writeLocalStorage(VOICE_EVENT_LOGS_KEY, webSpeechEventLogs);
|
writeLocalStorage(VOICE_EVENT_LOGS_KEY, webSpeechEventLogs);
|
||||||
if (DEBUG_MODE) {
|
if (DEBUG_MODE) {
|
||||||
console.log(
|
dlog(
|
||||||
'💾 [DEBUG] Saved webSpeechEventLogs to localStorage:',
|
'💾 [DEBUG] Saved webSpeechEventLogs to localStorage:',
|
||||||
webSpeechEventLogs.length,
|
webSpeechEventLogs.length,
|
||||||
'logs'
|
'logs'
|
||||||
@@ -329,14 +329,14 @@ const VoiceInputOverlay = ({
|
|||||||
writeLocalStorage(SEARCH_HISTORY_KEY, newHistory);
|
writeLocalStorage(SEARCH_HISTORY_KEY, newHistory);
|
||||||
|
|
||||||
if (DEBUG_MODE) {
|
if (DEBUG_MODE) {
|
||||||
console.log('💾 [VoiceInputOverlay.v2] Legacy search history updated:', newHistory);
|
dlog('💾 [VoiceInputOverlay.v2] Legacy search history updated:', newHistory);
|
||||||
}
|
}
|
||||||
|
|
||||||
return newHistory;
|
return newHistory;
|
||||||
});
|
});
|
||||||
|
|
||||||
if (DEBUG_MODE) {
|
if (DEBUG_MODE) {
|
||||||
console.log(
|
dlog(
|
||||||
'🎤 [VoiceInputOverlay] Voice search added to history:',
|
'🎤 [VoiceInputOverlay] Voice search added to history:',
|
||||||
trimmedText,
|
trimmedText,
|
||||||
'searchId:',
|
'searchId:',
|
||||||
@@ -383,12 +383,12 @@ const VoiceInputOverlay = ({
|
|||||||
// 🎯 API 실패 감지 - APIERROR 모드로 전환
|
// 🎯 API 실패 감지 - APIERROR 모드로 전환
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
if (shopperHouseErrorPopup?.visible && shopperHouseErrorPopup?.type === 'API_FAILURE') {
|
if (shopperHouseErrorPopup?.visible && shopperHouseErrorPopup?.type === 'API_FAILURE') {
|
||||||
console.log('[VoiceInputOverlay] 🚨 API 실패 감지 - APIERROR 모드로 전환');
|
dlog('[VoiceInputOverlay] 🚨 API 실패 감지 - APIERROR 모드로 전환');
|
||||||
setCurrentMode(VOICE_MODES.APIERROR);
|
setCurrentMode(VOICE_MODES.APIERROR);
|
||||||
|
|
||||||
// 3초 후 자동으로 PROMPT 모드로 복귀
|
// 3초 후 자동으로 PROMPT 모드로 복귀
|
||||||
const timer = setTimeout(() => {
|
const timer = setTimeout(() => {
|
||||||
console.log('[VoiceInputOverlay] ⏰ 3초 후 PROMPT 모드로 복귀');
|
dlog('[VoiceInputOverlay] ⏰ 3초 후 PROMPT 모드로 복귀');
|
||||||
setCurrentMode(VOICE_MODES.PROMPT);
|
setCurrentMode(VOICE_MODES.PROMPT);
|
||||||
}, 3000);
|
}, 3000);
|
||||||
|
|
||||||
@@ -398,15 +398,15 @@ const VoiceInputOverlay = ({
|
|||||||
|
|
||||||
// 🎯 VoiceResponse 에러 핸들러 - 빈 query 처리
|
// 🎯 VoiceResponse 에러 핸들러 - 빈 query 처리
|
||||||
const handleVoiceResponseError = useCallback((error) => {
|
const handleVoiceResponseError = useCallback((error) => {
|
||||||
console.log('[VoiceInputOverlay] ⚠️ VoiceResponse 에러 수신:', error);
|
dlog('[VoiceInputOverlay] ⚠️ VoiceResponse 에러 수신:', error);
|
||||||
|
|
||||||
if (error.type === 'EMPTY_QUERY') {
|
if (error.type === 'EMPTY_QUERY') {
|
||||||
console.log('[VoiceInputOverlay] 🚨 빈 query 에러 - NOTRECOGNIZED 모드로 전환');
|
dlog('[VoiceInputOverlay] 🚨 빈 query 에러 - NOTRECOGNIZED 모드로 전환');
|
||||||
setCurrentMode(VOICE_MODES.NOTRECOGNIZED);
|
setCurrentMode(VOICE_MODES.NOTRECOGNIZED);
|
||||||
|
|
||||||
// 3초 후 자동으로 PROMPT 모드로 복귀
|
// 3초 후 자동으로 PROMPT 모드로 복귀
|
||||||
const timer = setTimeout(() => {
|
const timer = setTimeout(() => {
|
||||||
console.log('[VoiceInputOverlay] ⏰ 3초 후 PROMPT 모드로 복귀 (빈 query 에러)');
|
dlog('[VoiceInputOverlay] ⏰ 3초 후 PROMPT 모드로 복귀 (빈 query 에러)');
|
||||||
setCurrentMode(VOICE_MODES.PROMPT);
|
setCurrentMode(VOICE_MODES.PROMPT);
|
||||||
}, 3000);
|
}, 3000);
|
||||||
|
|
||||||
@@ -421,14 +421,14 @@ const VoiceInputOverlay = ({
|
|||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
shopperHouseSearchIdRef.current = shopperHouseSearchId;
|
shopperHouseSearchIdRef.current = shopperHouseSearchId;
|
||||||
if (shopperHouseSearchId) {
|
if (shopperHouseSearchId) {
|
||||||
console.log('[VoiceInput] 🔄 searchId ref 업데이트:', shopperHouseSearchId);
|
dlog('[VoiceInput] 🔄 searchId ref 업데이트:', shopperHouseSearchId);
|
||||||
}
|
}
|
||||||
}, [shopperHouseSearchId]);
|
}, [shopperHouseSearchId]);
|
||||||
|
|
||||||
// 🔍 DEBUG: shopperHouseData 변경 추적
|
// 🔍 DEBUG: shopperHouseData 변경 추적
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
if (DEBUG_MODE) {
|
if (DEBUG_MODE) {
|
||||||
console.log('🔍 [DEBUG] shopperHouseData changed:', {
|
dlog('🔍 [DEBUG] shopperHouseData changed:', {
|
||||||
shopperHouseData,
|
shopperHouseData,
|
||||||
refValue: shopperHouseDataRef.current,
|
refValue: shopperHouseDataRef.current,
|
||||||
isVisible,
|
isVisible,
|
||||||
@@ -440,7 +440,7 @@ const VoiceInputOverlay = ({
|
|||||||
// 🔄 WebSpeech 에러 재시작 함수
|
// 🔄 WebSpeech 에러 재시작 함수
|
||||||
const restartWebSpeech = useCallback(() => {
|
const restartWebSpeech = useCallback(() => {
|
||||||
if (DEBUG_MODE) {
|
if (DEBUG_MODE) {
|
||||||
console.log('[VoiceInput] 🔄 Restarting WebSpeech after error');
|
dlog('[VoiceInput] 🔄 Restarting WebSpeech after error');
|
||||||
}
|
}
|
||||||
|
|
||||||
// 이벤트 로그 기록
|
// 이벤트 로그 기록
|
||||||
@@ -479,7 +479,7 @@ const VoiceInputOverlay = ({
|
|||||||
// 약간의 지연 후 새로 시작 (안정성을 위해)
|
// 약간의 지연 후 새로 시작 (안정성을 위해)
|
||||||
setTimeout(() => {
|
setTimeout(() => {
|
||||||
if (DEBUG_MODE) {
|
if (DEBUG_MODE) {
|
||||||
console.log('[VoiceInput] ✅ Restart complete - ready for new input');
|
dlog('[VoiceInput] ✅ Restart complete - ready for new input');
|
||||||
}
|
}
|
||||||
}, 300);
|
}, 300);
|
||||||
}, [dispatch, onSearchChange, addWebSpeechEventLog]);
|
}, [dispatch, onSearchChange, addWebSpeechEventLog]);
|
||||||
@@ -489,7 +489,7 @@ const VoiceInputOverlay = ({
|
|||||||
// Bubble 클릭으로 검색이 시작된 경우 WebSpeech 오류를 우회
|
// Bubble 클릭으로 검색이 시작된 경우 WebSpeech 오류를 우회
|
||||||
if (isBubbleClickSearch) {
|
if (isBubbleClickSearch) {
|
||||||
if (DEBUG_MODE) {
|
if (DEBUG_MODE) {
|
||||||
console.log('[VoiceInput] 🔇 WebSpeech 오류 우회 (bubble click search)');
|
dlog('[VoiceInput] 🔇 WebSpeech 오류 우회 (bubble click search)');
|
||||||
}
|
}
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
@@ -540,20 +540,20 @@ const VoiceInputOverlay = ({
|
|||||||
|
|
||||||
// 음성 인식 중지
|
// 음성 인식 중지
|
||||||
if (isListening) {
|
if (isListening) {
|
||||||
console.log('[VoiceInput] 🛑 Stopping listening due to error');
|
dlog('[VoiceInput] 🛑 Stopping listening due to error');
|
||||||
stopListening();
|
stopListening();
|
||||||
}
|
}
|
||||||
|
|
||||||
// 에러 모드로 전환 (NOTRECOGNIZED) - 사용자에게 에러 상태 표시
|
// 에러 모드로 전환 (NOTRECOGNIZED) - 사용자에게 에러 상태 표시
|
||||||
if (isVisible) {
|
if (isVisible) {
|
||||||
console.log('[VoiceInput] 🔀 Switching to NOTRECOGNIZED mode due to error');
|
dlog('[VoiceInput] 🔀 Switching to NOTRECOGNIZED mode due to error');
|
||||||
setCurrentMode(VOICE_MODES.NOTRECOGNIZED);
|
setCurrentMode(VOICE_MODES.NOTRECOGNIZED);
|
||||||
setVoiceInputMode(null);
|
setVoiceInputMode(null);
|
||||||
|
|
||||||
// 일부 에러 타입은 자동 재시작 시도
|
// 일부 에러 타입은 자동 재시작 시도
|
||||||
const autoRetryErrors = ['network', 'aborted', 'service-not-allowed'];
|
const autoRetryErrors = ['network', 'aborted', 'service-not-allowed'];
|
||||||
if (autoRetryErrors.includes(errorType)) {
|
if (autoRetryErrors.includes(errorType)) {
|
||||||
console.log('[VoiceInput] 🔄 Auto-restarting for error type:', errorType);
|
dlog('[VoiceInput] 🔄 Auto-restarting for error type:', errorType);
|
||||||
setTimeout(() => {
|
setTimeout(() => {
|
||||||
restartWebSpeech();
|
restartWebSpeech();
|
||||||
}, 2000); // 2초 후 자동 재시작
|
}, 2000); // 2초 후 자동 재시작
|
||||||
@@ -640,7 +640,7 @@ const VoiceInputOverlay = ({
|
|||||||
// ⚠️ 최적화: 실시간 상태 표시 제거, 결과만 표시
|
// ⚠️ 최적화: 실시간 상태 표시 제거, 결과만 표시
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
if (DEBUG_MODE) {
|
if (DEBUG_MODE) {
|
||||||
console.log('🔍 [DEBUG] shopperHouseError useEffect running:', {
|
dlog('🔍 [DEBUG] shopperHouseError useEffect running:', {
|
||||||
isVisible,
|
isVisible,
|
||||||
hasError: !!shopperHouseError,
|
hasError: !!shopperHouseError,
|
||||||
error: shopperHouseError,
|
error: shopperHouseError,
|
||||||
@@ -650,7 +650,7 @@ const VoiceInputOverlay = ({
|
|||||||
// ✨ 초기화 중에는 오류 처리 안 함
|
// ✨ 초기화 중에는 오류 처리 안 함
|
||||||
if (isInitializingRef.current) {
|
if (isInitializingRef.current) {
|
||||||
if (DEBUG_MODE) {
|
if (DEBUG_MODE) {
|
||||||
console.log('🔍 [DEBUG] Skipping error check - overlay is initializing');
|
dlog('🔍 [DEBUG] Skipping error check - overlay is initializing');
|
||||||
}
|
}
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
@@ -658,7 +658,7 @@ const VoiceInputOverlay = ({
|
|||||||
// 오류가 발생한 경우 - API 상태 표시 업데이트만 (팝업 대신)
|
// 오류가 발생한 경우 - API 상태 표시 업데이트만 (팝업 대신)
|
||||||
if (shopperHouseError && isVisible) {
|
if (shopperHouseError && isVisible) {
|
||||||
if (DEBUG_MODE) {
|
if (DEBUG_MODE) {
|
||||||
console.log('[VoiceInputOverlay] ❌ API error - showing result only:', shopperHouseError);
|
dlog('[VoiceInputOverlay] ❌ API error - showing result only:', shopperHouseError);
|
||||||
}
|
}
|
||||||
|
|
||||||
// ⚠️ 심각한 오류만 표시 (5xx 서버 오류, 커스텀 메시지)
|
// ⚠️ 심각한 오류만 표시 (5xx 서버 오류, 커스텀 메시지)
|
||||||
@@ -676,7 +676,7 @@ const VoiceInputOverlay = ({
|
|||||||
// ShopperHouse API 응답 수신 시 overlay 닫기 및 API 상태 업데이트
|
// ShopperHouse API 응답 수신 시 overlay 닫기 및 API 상태 업데이트
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
if (DEBUG_MODE) {
|
if (DEBUG_MODE) {
|
||||||
console.log('🔍 [DEBUG] shopperHouseData useEffect running:', {
|
dlog('🔍 [DEBUG] shopperHouseData useEffect running:', {
|
||||||
isVisible,
|
isVisible,
|
||||||
hasData: !!shopperHouseData,
|
hasData: !!shopperHouseData,
|
||||||
refValue: shopperHouseDataRef.current,
|
refValue: shopperHouseDataRef.current,
|
||||||
@@ -688,7 +688,7 @@ const VoiceInputOverlay = ({
|
|||||||
// ✨ 초기화 중에는 close 로직 실행 안 함 (중복 닫기 방지)
|
// ✨ 초기화 중에는 close 로직 실행 안 함 (중복 닫기 방지)
|
||||||
if (isInitializingRef.current) {
|
if (isInitializingRef.current) {
|
||||||
if (DEBUG_MODE) {
|
if (DEBUG_MODE) {
|
||||||
console.log('🔍 [DEBUG] Skipping close check - overlay is initializing');
|
dlog('🔍 [DEBUG] Skipping close check - overlay is initializing');
|
||||||
}
|
}
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
@@ -696,7 +696,7 @@ const VoiceInputOverlay = ({
|
|||||||
// 이전 값과 비교하여 새로운 데이터가 들어왔을 때만 닫기
|
// 이전 값과 비교하여 새로운 데이터가 들어왔을 때만 닫기
|
||||||
if (isVisible && shopperHouseData && shopperHouseData !== shopperHouseDataRef.current) {
|
if (isVisible && shopperHouseData && shopperHouseData !== shopperHouseDataRef.current) {
|
||||||
if (DEBUG_MODE) {
|
if (DEBUG_MODE) {
|
||||||
console.log('[VoiceInputOverlay.v2] ShopperHouse data received, closing overlay', {
|
dlog('[VoiceInputOverlay.v2] ShopperHouse data received, closing overlay', {
|
||||||
oldRef: shopperHouseDataRef.current,
|
oldRef: shopperHouseDataRef.current,
|
||||||
newData: shopperHouseData,
|
newData: shopperHouseData,
|
||||||
});
|
});
|
||||||
@@ -708,7 +708,7 @@ const VoiceInputOverlay = ({
|
|||||||
setApiStatusMessage('Success');
|
setApiStatusMessage('Success');
|
||||||
|
|
||||||
if (DEBUG_MODE) {
|
if (DEBUG_MODE) {
|
||||||
console.log('[VoiceInputOverlay] 📍 API Status updated:', {
|
dlog('[VoiceInputOverlay] 📍 API Status updated:', {
|
||||||
status: 'success',
|
status: 'success',
|
||||||
message: 'Success',
|
message: 'Success',
|
||||||
});
|
});
|
||||||
@@ -723,7 +723,7 @@ const VoiceInputOverlay = ({
|
|||||||
// STT 텍스트 수신 시 처리
|
// STT 텍스트 수신 시 처리
|
||||||
// useEffect(() => {
|
// useEffect(() => {
|
||||||
// if (lastSTTText && sttTimestamp && isVisible) {
|
// if (lastSTTText && sttTimestamp && isVisible) {
|
||||||
// console.log('[VoiceInputOverlay.v2] STT text received in overlay:', lastSTTText);
|
// dlog('[VoiceInputOverlay.v2] STT text received in overlay:', lastSTTText);
|
||||||
//
|
//
|
||||||
// // 입력창에 텍스트 표시 (부모 컴포넌트로 전달)
|
// // 입력창에 텍스트 표시 (부모 컴포넌트로 전달)
|
||||||
// if (onSearchChange) {
|
// if (onSearchChange) {
|
||||||
@@ -745,7 +745,7 @@ const VoiceInputOverlay = ({
|
|||||||
if (!ENABLE_WAKE_WORD) return;
|
if (!ENABLE_WAKE_WORD) return;
|
||||||
|
|
||||||
if (DEBUG_MODE) {
|
if (DEBUG_MODE) {
|
||||||
console.log('🎉 [VoiceInputOverlay.v2] Wake word detected! Switching to LISTENING mode');
|
dlog('🎉 [VoiceInputOverlay.v2] Wake word detected! Switching to LISTENING mode');
|
||||||
}
|
}
|
||||||
|
|
||||||
// 기존 타이머 정리
|
// 기존 타이머 정리
|
||||||
@@ -766,7 +766,7 @@ const VoiceInputOverlay = ({
|
|||||||
// 15초 타이머 설정
|
// 15초 타이머 설정
|
||||||
listeningTimerRef.current = setTimeout(() => {
|
listeningTimerRef.current = setTimeout(() => {
|
||||||
if (DEBUG_MODE) {
|
if (DEBUG_MODE) {
|
||||||
console.log('⏰ [VoiceInputOverlay.v2] 15초 타임아웃 - PROMPT 모드로 복귀');
|
dlog('⏰ [VoiceInputOverlay.v2] 15초 타임아웃 - PROMPT 모드로 복귀');
|
||||||
}
|
}
|
||||||
setCurrentMode(VOICE_MODES.PROMPT);
|
setCurrentMode(VOICE_MODES.PROMPT);
|
||||||
setVoiceInputMode(null);
|
setVoiceInputMode(null);
|
||||||
@@ -789,7 +789,7 @@ const VoiceInputOverlay = ({
|
|||||||
|
|
||||||
if (detected) {
|
if (detected) {
|
||||||
if (DEBUG_MODE) {
|
if (DEBUG_MODE) {
|
||||||
console.log('🎉 [VoiceInputOverlay.v2] Wake word detected in PROMPT mode:', text);
|
dlog('🎉 [VoiceInputOverlay.v2] Wake word detected in PROMPT mode:', text);
|
||||||
}
|
}
|
||||||
handleWakeWordDetected();
|
handleWakeWordDetected();
|
||||||
}
|
}
|
||||||
@@ -799,7 +799,7 @@ const VoiceInputOverlay = ({
|
|||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
if (isVisible && voiceVersion === VOICE_VERSION.WEB_SPEECH && isSupported === false) {
|
if (isVisible && voiceVersion === VOICE_VERSION.WEB_SPEECH && isSupported === false) {
|
||||||
if (DEBUG_MODE) {
|
if (DEBUG_MODE) {
|
||||||
console.log('⚠️ [VoiceInputOverlay.v2] WebSpeech not supported, switching to NOINIT mode');
|
dlog('⚠️ [VoiceInputOverlay.v2] WebSpeech not supported, switching to NOINIT mode');
|
||||||
}
|
}
|
||||||
setCurrentMode(VOICE_MODES.NOINIT);
|
setCurrentMode(VOICE_MODES.NOINIT);
|
||||||
}
|
}
|
||||||
@@ -808,7 +808,7 @@ const VoiceInputOverlay = ({
|
|||||||
// 🎤 음성 입력 최종 처리 함수 (15초 타이머 & 3초 silence detection 공통 사용)
|
// 🎤 음성 입력 최종 처리 함수 (15초 타이머 & 3초 silence detection 공통 사용)
|
||||||
const processFinalVoiceInput = useCallback(
|
const processFinalVoiceInput = useCallback(
|
||||||
(source) => {
|
(source) => {
|
||||||
console.log(`[VoiceInput] 🏁 음성 입력 종료 (${source})`);
|
dlog(`[VoiceInput] 🏁 음성 입력 종료 (${source})`);
|
||||||
|
|
||||||
// 모든 타이머 정리 및 ref 초기화
|
// 모든 타이머 정리 및 ref 초기화
|
||||||
clearTimerRef(listeningTimerRef);
|
clearTimerRef(listeningTimerRef);
|
||||||
@@ -833,9 +833,9 @@ const VoiceInputOverlay = ({
|
|||||||
? reduxFinalText.trim()
|
? reduxFinalText.trim()
|
||||||
: interimRefText;
|
: interimRefText;
|
||||||
|
|
||||||
console.log('[VoiceInput] ├─ Redux finalText:', reduxFinalText || '(없음)');
|
dlog('[VoiceInput] ├─ Redux finalText:', reduxFinalText || '(없음)');
|
||||||
console.log('[VoiceInput] ├─ interimRef:', interimRefText || '(없음)');
|
dlog('[VoiceInput] ├─ interimRef:', interimRefText || '(없음)');
|
||||||
console.log('[VoiceInput] └─ 사용할 텍스트:', finalText, `(길이: ${finalText.length})`);
|
dlog('[VoiceInput] └─ 사용할 텍스트:', finalText, `(길이: ${finalText.length})`);
|
||||||
|
|
||||||
if (finalText && finalText.length >= 3) {
|
if (finalText && finalText.length >= 3) {
|
||||||
// STT 텍스트 저장
|
// STT 텍스트 저장
|
||||||
@@ -851,11 +851,11 @@ const VoiceInputOverlay = ({
|
|||||||
|
|
||||||
// ✨ ShopperHouse API 자동 호출 (2차 발화 시 searchId 포함)
|
// ✨ ShopperHouse API 자동 호출 (2차 발화 시 searchId 포함)
|
||||||
const query = finalText.trim();
|
const query = finalText.trim();
|
||||||
console.log('[VoiceInput] 📤 API 요청 전송');
|
dlog('[VoiceInput] 📤 API 요청 전송');
|
||||||
console.log('[VoiceInput] ├─ query:', query);
|
dlog('[VoiceInput] ├─ query:', query);
|
||||||
console.log('[VoiceInput] ├─ ref 값:', shopperHouseSearchIdRef.current);
|
dlog('[VoiceInput] ├─ ref 값:', shopperHouseSearchIdRef.current);
|
||||||
console.log('[VoiceInput] ├─ currentSearchId:', currentSearchId);
|
dlog('[VoiceInput] ├─ currentSearchId:', currentSearchId);
|
||||||
console.log('[VoiceInput] └─ searchId:', currentSearchId || '(없음 - 첫 번째 발화)');
|
dlog('[VoiceInput] └─ searchId:', currentSearchId || '(없음 - 첫 번째 발화)');
|
||||||
|
|
||||||
// API 호출 이벤트 로깅
|
// API 호출 이벤트 로깅
|
||||||
const searchIdInfo = currentSearchId
|
const searchIdInfo = currentSearchId
|
||||||
@@ -878,14 +878,14 @@ const VoiceInputOverlay = ({
|
|||||||
);
|
);
|
||||||
} else {
|
} else {
|
||||||
// 입력이 없거나 너무 짧으면 NOTRECOGNIZED 모드로 전환
|
// 입력이 없거나 너무 짧으면 NOTRECOGNIZED 모드로 전환
|
||||||
console.log('[VoiceInput] ⚠️ 입력 없음 또는 너무 짧음 - NOTRECOGNIZED 모드로 전환');
|
dlog('[VoiceInput] ⚠️ 입력 없음 또는 너무 짧음 - NOTRECOGNIZED 모드로 전환');
|
||||||
console.log('[VoiceInput] └─ finalText 길이:', finalText.length);
|
dlog('[VoiceInput] └─ finalText 길이:', finalText.length);
|
||||||
setCurrentMode(VOICE_MODES.NOTRECOGNIZED);
|
setCurrentMode(VOICE_MODES.NOTRECOGNIZED);
|
||||||
setVoiceInputMode(null);
|
setVoiceInputMode(null);
|
||||||
|
|
||||||
// 3초 후 자동으로 PROMPT 모드로 복귀
|
// 3초 후 자동으로 PROMPT 모드로 복귀
|
||||||
const timer = setTimeout(() => {
|
const timer = setTimeout(() => {
|
||||||
console.log('[VoiceInputOverlay] ⏰ 3초 후 PROMPT 모드로 복귀 (NOTRECOGNIZED)');
|
dlog('[VoiceInputOverlay] ⏰ 3초 후 PROMPT 모드로 복귀 (NOTRECOGNIZED)');
|
||||||
setCurrentMode(VOICE_MODES.PROMPT);
|
setCurrentMode(VOICE_MODES.PROMPT);
|
||||||
}, 3000);
|
}, 3000);
|
||||||
|
|
||||||
@@ -913,13 +913,13 @@ const VoiceInputOverlay = ({
|
|||||||
// currentMode === VOICE_MODES.LISTENING &&
|
// currentMode === VOICE_MODES.LISTENING &&
|
||||||
// !isListening
|
// !isListening
|
||||||
// ) {
|
// ) {
|
||||||
// console.log('🎙️ [VoiceInputOverlay.v2] Auto-starting Web Speech API after mode change...');
|
// dlog('🎙️ [VoiceInputOverlay.v2] Auto-starting Web Speech API after mode change...');
|
||||||
// startListening();
|
// startListening();
|
||||||
//
|
//
|
||||||
// // 15초 타이머 설정
|
// // 15초 타이머 설정
|
||||||
// clearTimerRef(listeningTimerRef);
|
// clearTimerRef(listeningTimerRef);
|
||||||
// listeningTimerRef.current = setTimeout(() => {
|
// listeningTimerRef.current = setTimeout(() => {
|
||||||
// console.log('⏰ [VoiceInputOverlay.v2] 15초 타임아웃 - WebSpeech 자동 종료');
|
// dlog('⏰ [VoiceInputOverlay.v2] 15초 타임아웃 - WebSpeech 자동 종료');
|
||||||
// stopListening();
|
// stopListening();
|
||||||
// setCurrentMode(VOICE_MODES.PROMPT);
|
// setCurrentMode(VOICE_MODES.PROMPT);
|
||||||
// setVoiceInputMode(null);
|
// setVoiceInputMode(null);
|
||||||
@@ -961,7 +961,7 @@ const VoiceInputOverlay = ({
|
|||||||
const trimmedText = interimText?.trim() || '';
|
const trimmedText = interimText?.trim() || '';
|
||||||
if (!hasReached5Chars && trimmedText.length >= 5) {
|
if (!hasReached5Chars && trimmedText.length >= 5) {
|
||||||
// 처음 5글자에 도달했을 때만 활성화
|
// 처음 5글자에 도달했을 때만 활성화
|
||||||
console.log('[VoiceInput] 🎯 5글자 도달! SilenceCheck 활성화');
|
dlog('[VoiceInput] 🎯 5글자 도달! SilenceCheck 활성화');
|
||||||
setHasReached5Chars(true);
|
setHasReached5Chars(true);
|
||||||
setIsSilenceCheckActive(true);
|
setIsSilenceCheckActive(true);
|
||||||
} else if (hasReached5Chars && trimmedText.length < 5) {
|
} else if (hasReached5Chars && trimmedText.length < 5) {
|
||||||
@@ -983,7 +983,7 @@ const VoiceInputOverlay = ({
|
|||||||
// 3초 silence detection: 마지막 입력 후 3초 동안 추가 입력이 없으면 자동 종료
|
// 3초 silence detection: 마지막 입력 후 3초 동안 추가 입력이 없으면 자동 종료
|
||||||
clearTimerRef(silenceDetectionTimerRef);
|
clearTimerRef(silenceDetectionTimerRef);
|
||||||
silenceDetectionTimerRef.current = setTimeout(() => {
|
silenceDetectionTimerRef.current = setTimeout(() => {
|
||||||
console.log('[VoiceInput] 🔇 3초 동안 입력 없음 - 자동 종료');
|
dlog('[VoiceInput] 🔇 3초 동안 입력 없음 - 자동 종료');
|
||||||
addWebSpeechEventLog('SILENCE_3S', 'No input detected for 3 seconds - auto finish');
|
addWebSpeechEventLog('SILENCE_3S', 'No input detected for 3 seconds - auto finish');
|
||||||
processFinalVoiceInput('3초 silence detection');
|
processFinalVoiceInput('3초 silence detection');
|
||||||
}, 3000); // 3초
|
}, 3000); // 3초
|
||||||
@@ -1010,9 +1010,7 @@ const VoiceInputOverlay = ({
|
|||||||
if (currentMode !== VOICE_MODES.PROMPT) return;
|
if (currentMode !== VOICE_MODES.PROMPT) return;
|
||||||
|
|
||||||
if (DEBUG_MODE) {
|
if (DEBUG_MODE) {
|
||||||
console.log(
|
dlog('🎙️ [VoiceInputOverlay.v2] Starting background listening for wake word detection');
|
||||||
'🎙️ [VoiceInputOverlay.v2] Starting background listening for wake word detection'
|
|
||||||
);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// PROMPT 모드에서 백그라운드 리스닝 시작
|
// PROMPT 모드에서 백그라운드 리스닝 시작
|
||||||
@@ -1037,7 +1035,7 @@ const VoiceInputOverlay = ({
|
|||||||
setSilenceSeconds(0);
|
setSilenceSeconds(0);
|
||||||
|
|
||||||
if (DEBUG_MODE) {
|
if (DEBUG_MODE) {
|
||||||
console.log('⏱️ [VoiceInputOverlay.v2] Starting countdown from 15');
|
dlog('⏱️ [VoiceInputOverlay.v2] Starting countdown from 15');
|
||||||
}
|
}
|
||||||
|
|
||||||
// 1초마다 카운트다운
|
// 1초마다 카운트다운
|
||||||
@@ -1045,7 +1043,7 @@ const VoiceInputOverlay = ({
|
|||||||
setCountdown((prev) => {
|
setCountdown((prev) => {
|
||||||
const next = prev - 1;
|
const next = prev - 1;
|
||||||
if (DEBUG_MODE) {
|
if (DEBUG_MODE) {
|
||||||
console.log('⏱️ [VoiceInputOverlay.v2] Countdown:', next);
|
dlog('⏱️ [VoiceInputOverlay.v2] Countdown:', next);
|
||||||
}
|
}
|
||||||
if (next <= 0) {
|
if (next <= 0) {
|
||||||
clearIntervalRef(countdownIntervalRef);
|
clearIntervalRef(countdownIntervalRef);
|
||||||
@@ -1080,7 +1078,7 @@ const VoiceInputOverlay = ({
|
|||||||
setSilenceSeconds((prev) => {
|
setSilenceSeconds((prev) => {
|
||||||
const next = prev + 1;
|
const next = prev + 1;
|
||||||
if (DEBUG_MODE) {
|
if (DEBUG_MODE) {
|
||||||
console.log('🚦 [VoiceInput] Silence Detection Progress:', next);
|
dlog('🚦 [VoiceInput] Silence Detection Progress:', next);
|
||||||
}
|
}
|
||||||
if (next >= 3) {
|
if (next >= 3) {
|
||||||
// 3초 도달하면 interval 정리 (타이머가 곧 종료됨)
|
// 3초 도달하면 interval 정리 (타이머가 곧 종료됨)
|
||||||
@@ -1098,11 +1096,11 @@ const VoiceInputOverlay = ({
|
|||||||
|
|
||||||
// VoiceInputOverlay 언마운트 시에만 초기화
|
// VoiceInputOverlay 언마운트 시에만 초기화
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
console.log('[VoiceInput] 🚀 VoiceInputOverlay 마운트됨 (searchId 유지)');
|
dlog('[VoiceInput] 🚀 VoiceInputOverlay 마운트됨 (searchId 유지)');
|
||||||
|
|
||||||
// Cleanup: 언마운트 시에만 초기화
|
// Cleanup: 언마운트 시에만 초기화
|
||||||
return () => {
|
return () => {
|
||||||
console.log('[VoiceInput] 🔚 VoiceInputOverlay 언마운트 - searchId 초기화');
|
dlog('[VoiceInput] 🔚 VoiceInputOverlay 언마운트 - searchId 초기화');
|
||||||
// dispatch(clearShopperHouseData());
|
// dispatch(clearShopperHouseData());
|
||||||
dispatch(clearSTTText());
|
dispatch(clearSTTText());
|
||||||
};
|
};
|
||||||
@@ -1111,34 +1109,34 @@ const VoiceInputOverlay = ({
|
|||||||
// Overlay가 열릴 때 포커스를 overlay 내부로 이동
|
// Overlay가 열릴 때 포커스를 overlay 내부로 이동
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
if (DEBUG_MODE) {
|
if (DEBUG_MODE) {
|
||||||
console.log('👁️ [DEBUG] isVisible useEffect triggered - isVisible:', isVisible);
|
dlog('👁️ [DEBUG] isVisible useEffect triggered - isVisible:', isVisible);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (isVisible) {
|
if (isVisible) {
|
||||||
if (DEBUG_MODE) {
|
if (DEBUG_MODE) {
|
||||||
console.log('✅ [DEBUG] ===== OVERLAY OPENING =====');
|
dlog('✅ [DEBUG] ===== OVERLAY OPENING =====');
|
||||||
}
|
}
|
||||||
|
|
||||||
// ✨ 초기화 시작 플래그 설정 (close 로직 일시 차단)
|
// ✨ 초기화 시작 플래그 설정 (close 로직 일시 차단)
|
||||||
isInitializingRef.current = true;
|
isInitializingRef.current = true;
|
||||||
if (DEBUG_MODE) {
|
if (DEBUG_MODE) {
|
||||||
console.log('🚧 [DEBUG] isInitializingRef set to TRUE - close logic DISABLED');
|
dlog('🚧 [DEBUG] isInitializingRef set to TRUE - close logic DISABLED');
|
||||||
}
|
}
|
||||||
|
|
||||||
// 현재 포커스된 요소 저장
|
// 현재 포커스된 요소 저장
|
||||||
lastFocusedElement.current = Spotlight.getCurrent();
|
lastFocusedElement.current = Spotlight.getCurrent();
|
||||||
console.log('[lastFocusedElement] 🚀 Current focused element:', Spotlight.getCurrent());
|
dlog('[lastFocusedElement] 🚀 Current focused element:', Spotlight.getCurrent());
|
||||||
|
|
||||||
// ✨ shopperHouseDataRef를 null로 초기화
|
// ✨ shopperHouseDataRef를 null로 초기화
|
||||||
shopperHouseDataRef.current = null;
|
shopperHouseDataRef.current = null;
|
||||||
|
|
||||||
if (DEBUG_MODE) {
|
if (DEBUG_MODE) {
|
||||||
console.log('🔍 [DEBUG] shopperHouseDataRef initialized to NULL');
|
dlog('🔍 [DEBUG] shopperHouseDataRef initialized to NULL');
|
||||||
}
|
}
|
||||||
|
|
||||||
// 모드 초기화 (항상 prompt 모드로 시작)
|
// 모드 초기화 (항상 prompt 모드로 시작)
|
||||||
if (DEBUG_MODE) {
|
if (DEBUG_MODE) {
|
||||||
console.log('🔀 [DEBUG] Setting currentMode to:', mode);
|
dlog('🔀 [DEBUG] Setting currentMode to:', mode);
|
||||||
}
|
}
|
||||||
setCurrentMode(mode);
|
setCurrentMode(mode);
|
||||||
setVoiceInputMode(null);
|
setVoiceInputMode(null);
|
||||||
@@ -1149,12 +1147,12 @@ const VoiceInputOverlay = ({
|
|||||||
// ✨ 초기화 완료 - close 로직 활성화
|
// ✨ 초기화 완료 - close 로직 활성화
|
||||||
isInitializingRef.current = false;
|
isInitializingRef.current = false;
|
||||||
if (DEBUG_MODE) {
|
if (DEBUG_MODE) {
|
||||||
console.log('✅ [DEBUG] Initialization complete, close logic ENABLED');
|
dlog('✅ [DEBUG] Initialization complete, close logic ENABLED');
|
||||||
}
|
}
|
||||||
}, 100);
|
}, 100);
|
||||||
} else {
|
} else {
|
||||||
if (DEBUG_MODE) {
|
if (DEBUG_MODE) {
|
||||||
console.log('❌ [DEBUG] ===== OVERLAY CLOSING =====');
|
dlog('❌ [DEBUG] ===== OVERLAY CLOSING =====');
|
||||||
}
|
}
|
||||||
// Overlay가 닫힐 때 원래 포커스 복원 및 상태 초기화
|
// Overlay가 닫힐 때 원래 포커스 복원 및 상태 초기화
|
||||||
|
|
||||||
@@ -1168,7 +1166,8 @@ const VoiceInputOverlay = ({
|
|||||||
// 🎯 [포커스 충돌 해결] VoiceInputOverlay를 통한 음성 검색 후에는 TInput으로 포커스 복원하지 않음
|
// 🎯 [포커스 충돌 해결] VoiceInputOverlay를 통한 음성 검색 후에는 TInput으로 포커스 복원하지 않음
|
||||||
// SearchResults의 첫 번째 상품으로 포커스가 가도록 SearchPanel에 위임
|
// SearchResults의 첫 번째 상품으로 포커스가 가도록 SearchPanel에 위임
|
||||||
// shopperHouseData가 있으면 (음성 검색 결과가 있으면) 포커스 복원하지 않음
|
// shopperHouseData가 있으면 (음성 검색 결과가 있으면) 포커스 복원하지 않음
|
||||||
const hasVoiceSearchResult = shopperHouseData && shopperHouseData.results && shopperHouseData.results.length > 0;
|
const hasVoiceSearchResult =
|
||||||
|
shopperHouseData && shopperHouseData.results && shopperHouseData.results.length > 0;
|
||||||
if (lastFocusedElement.current && !isVoiceResultMode && !hasVoiceSearchResult) {
|
if (lastFocusedElement.current && !isVoiceResultMode && !hasVoiceSearchResult) {
|
||||||
focusRestoreTimerRef.current = setTimeout(() => {
|
focusRestoreTimerRef.current = setTimeout(() => {
|
||||||
Spotlight.focus(lastFocusedElement.current);
|
Spotlight.focus(lastFocusedElement.current);
|
||||||
@@ -1195,7 +1194,7 @@ const VoiceInputOverlay = ({
|
|||||||
// 🔄 API 에러 재시작 함수
|
// 🔄 API 에러 재시작 함수
|
||||||
const handleApiErrorRestart = useCallback(() => {
|
const handleApiErrorRestart = useCallback(() => {
|
||||||
if (DEBUG_MODE) {
|
if (DEBUG_MODE) {
|
||||||
console.log('[VoiceInput] 🔄 Restarting after API error');
|
dlog('[VoiceInput] 🔄 Restarting after API error');
|
||||||
}
|
}
|
||||||
|
|
||||||
// ShopperHouse 에러 상태 정리 (Redux)
|
// ShopperHouse 에러 상태 정리 (Redux)
|
||||||
@@ -1216,14 +1215,14 @@ const VoiceInputOverlay = ({
|
|||||||
setIsBubbleClickSearch(false); // bubble 클릭 상태 초기화
|
setIsBubbleClickSearch(false); // bubble 클릭 상태 초기화
|
||||||
|
|
||||||
if (DEBUG_MODE) {
|
if (DEBUG_MODE) {
|
||||||
console.log('[VoiceInput] ✅ API error restart complete - ready for new input');
|
dlog('[VoiceInput] ✅ API error restart complete - ready for new input');
|
||||||
}
|
}
|
||||||
}, [dispatch, onSearchChange]);
|
}, [dispatch, onSearchChange]);
|
||||||
|
|
||||||
// API 재시도 함수
|
// API 재시도 함수
|
||||||
const handleApiErrorRetry = useCallback(() => {
|
const handleApiErrorRetry = useCallback(() => {
|
||||||
if (DEBUG_MODE) {
|
if (DEBUG_MODE) {
|
||||||
console.log('[VoiceInput] 🔄 Retrying API call after error');
|
dlog('[VoiceInput] 🔄 Retrying API call after error');
|
||||||
}
|
}
|
||||||
|
|
||||||
// 이전 에러 상태 정리
|
// 이전 에러 상태 정리
|
||||||
@@ -1235,9 +1234,9 @@ const VoiceInputOverlay = ({
|
|||||||
const currentSearchId = shopperHouseSearchIdRef.current;
|
const currentSearchId = shopperHouseSearchIdRef.current;
|
||||||
|
|
||||||
if (DEBUG_MODE) {
|
if (DEBUG_MODE) {
|
||||||
console.log('[VoiceInput] 🔄 Retrying API call');
|
dlog('[VoiceInput] 🔄 Retrying API call');
|
||||||
console.log('[VoiceInput] ├─ query:', query);
|
dlog('[VoiceInput] ├─ query:', query);
|
||||||
console.log('[VoiceInput] └─ searchId:', currentSearchId || '(없음 - 첫 번째 발화)');
|
dlog('[VoiceInput] └─ searchId:', currentSearchId || '(없음 - 첫 번째 발화)');
|
||||||
}
|
}
|
||||||
|
|
||||||
// 다시 API 호출
|
// 다시 API 호출
|
||||||
@@ -1267,7 +1266,7 @@ const VoiceInputOverlay = ({
|
|||||||
// Overlay 닫기 핸들러 (모든 닫기 동작을 통합)
|
// Overlay 닫기 핸들러 (모든 닫기 동작을 통합)
|
||||||
const handleClose = useCallback(() => {
|
const handleClose = useCallback(() => {
|
||||||
if (DEBUG_MODE) {
|
if (DEBUG_MODE) {
|
||||||
console.log('🚪 [DEBUG] handleClose called - closing overlay');
|
dlog('🚪 [DEBUG] handleClose called - closing overlay');
|
||||||
}
|
}
|
||||||
|
|
||||||
// 1. 타이머 정리
|
// 1. 타이머 정리
|
||||||
@@ -1309,7 +1308,7 @@ const VoiceInputOverlay = ({
|
|||||||
const handleSuggestionClick = useCallback(
|
const handleSuggestionClick = useCallback(
|
||||||
(suggestion) => {
|
(suggestion) => {
|
||||||
if (DEBUG_MODE) {
|
if (DEBUG_MODE) {
|
||||||
console.log('💡 [DEBUG] handleSuggestionClick called with:', suggestion);
|
dlog('💡 [DEBUG] handleSuggestionClick called with:', suggestion);
|
||||||
}
|
}
|
||||||
|
|
||||||
// Bubble 클릭으로 검색 시작 상태 설정
|
// Bubble 클릭으로 검색 시작 상태 설정
|
||||||
@@ -1339,9 +1338,9 @@ const VoiceInputOverlay = ({
|
|||||||
const currentSearchId = shopperHouseSearchIdRef.current;
|
const currentSearchId = shopperHouseSearchIdRef.current;
|
||||||
|
|
||||||
if (DEBUG_MODE) {
|
if (DEBUG_MODE) {
|
||||||
console.log('🔍 [DEBUG] Calling ShopperHouse API from bubble click');
|
dlog('🔍 [DEBUG] Calling ShopperHouse API from bubble click');
|
||||||
console.log('[VoiceInput] ├─ query:', query);
|
dlog('[VoiceInput] ├─ query:', query);
|
||||||
console.log('[VoiceInput] └─ searchId:', currentSearchId || '(없음 - 첫 번째 발화)');
|
dlog('[VoiceInput] └─ searchId:', currentSearchId || '(없음 - 첫 번째 발화)');
|
||||||
}
|
}
|
||||||
|
|
||||||
// API 호출 이벤트 로깅 (Bubble 클릭)
|
// API 호출 이벤트 로깅 (Bubble 클릭)
|
||||||
@@ -1379,7 +1378,7 @@ const VoiceInputOverlay = ({
|
|||||||
// Input 창에서 API 호출 핸들러 (돋보기 아이콘 클릭 시에만)
|
// Input 창에서 API 호출 핸들러 (돋보기 아이콘 클릭 시에만)
|
||||||
const handleSearchSubmit = useCallback(() => {
|
const handleSearchSubmit = useCallback(() => {
|
||||||
if (DEBUG_MODE) {
|
if (DEBUG_MODE) {
|
||||||
console.log('[VoiceInputOverlay.v2] Search submit:', searchQuery);
|
dlog('[VoiceInputOverlay.v2] Search submit:', searchQuery);
|
||||||
}
|
}
|
||||||
if (searchQuery && searchQuery.trim()) {
|
if (searchQuery && searchQuery.trim()) {
|
||||||
// ShopperHouse API 호출
|
// ShopperHouse API 호출
|
||||||
@@ -1423,7 +1422,7 @@ const VoiceInputOverlay = ({
|
|||||||
(e) => {
|
(e) => {
|
||||||
if (e.key === 'Enter' || e.keyCode === 13) {
|
if (e.key === 'Enter' || e.keyCode === 13) {
|
||||||
e.preventDefault();
|
e.preventDefault();
|
||||||
console.log(
|
dlog(
|
||||||
'[VoiceInputOverlay] 🔄 TInputSimple에서 Enter 키 감지 → SearchInputOverlay 전환 시작'
|
'[VoiceInputOverlay] 🔄 TInputSimple에서 Enter 키 감지 → SearchInputOverlay 전환 시작'
|
||||||
);
|
);
|
||||||
// 🎯 SearchPanel의 콜백 호출
|
// 🎯 SearchPanel의 콜백 호출
|
||||||
@@ -1439,7 +1438,7 @@ const VoiceInputOverlay = ({
|
|||||||
// ⚠️ [251031] 마우스 클릭 시 프리징 발생 - 추후 원인 분석 후 활성화 필요
|
// ⚠️ [251031] 마우스 클릭 시 프리징 발생 - 추후 원인 분석 후 활성화 필요
|
||||||
// const handleInputMouseDown = useCallback((e) => {
|
// const handleInputMouseDown = useCallback((e) => {
|
||||||
// e.preventDefault();
|
// e.preventDefault();
|
||||||
// console.log('[VoiceInputOverlay] 🖱️ TInputSimple에서 마우스 클릭 감지 → SearchInputOverlay 전환 시작');
|
// dlog('[VoiceInputOverlay] 🖱️ TInputSimple에서 마우스 클릭 감지 → SearchInputOverlay 전환 시작');
|
||||||
// // 🎯 SearchPanel의 콜백 호출
|
// // 🎯 SearchPanel의 콜백 호출
|
||||||
// if (onTransitionToSearchInput) {
|
// if (onTransitionToSearchInput) {
|
||||||
// onTransitionToSearchInput();
|
// onTransitionToSearchInput();
|
||||||
@@ -1450,14 +1449,14 @@ const VoiceInputOverlay = ({
|
|||||||
const handleInputModeChange = useCallback(
|
const handleInputModeChange = useCallback(
|
||||||
(inputMode) => {
|
(inputMode) => {
|
||||||
if (DEBUG_MODE) {
|
if (DEBUG_MODE) {
|
||||||
console.log('[VoiceInputOverlay] TInput 모드 변경:', inputMode);
|
dlog('[VoiceInputOverlay] TInput 모드 변경:', inputMode);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (inputMode === 'input') {
|
if (inputMode === 'input') {
|
||||||
// Input 모드로 전환되면 Overlay 닫기
|
// Input 모드로 전환되면 Overlay 닫기
|
||||||
// SearchPanel의 TInput으로 자연스럽게 전환됨
|
// SearchPanel의 TInput으로 자연스럽게 전환됨
|
||||||
if (DEBUG_MODE) {
|
if (DEBUG_MODE) {
|
||||||
console.log('⌨️ [DEBUG] Input 모드로 전환됨 - VoiceInputOverlay 닫고 SearchPanel로 이동');
|
dlog('⌨️ [DEBUG] Input 모드로 전환됨 - VoiceInputOverlay 닫고 SearchPanel로 이동');
|
||||||
}
|
}
|
||||||
handleClose();
|
handleClose();
|
||||||
}
|
}
|
||||||
@@ -1469,7 +1468,7 @@ const VoiceInputOverlay = ({
|
|||||||
const handleToggleDashboard = useCallback(() => {
|
const handleToggleDashboard = useCallback(() => {
|
||||||
setShowDashboard((prev) => !prev);
|
setShowDashboard((prev) => !prev);
|
||||||
if (DEBUG_MODE) {
|
if (DEBUG_MODE) {
|
||||||
console.log('🔧 [DEBUG] Dashboard toggled:', !showDashboard);
|
dlog('🔧 [DEBUG] Dashboard toggled:', !showDashboard);
|
||||||
}
|
}
|
||||||
}, [showDashboard]);
|
}, [showDashboard]);
|
||||||
|
|
||||||
@@ -1484,7 +1483,7 @@ const VoiceInputOverlay = ({
|
|||||||
// 모드에 따른 컨텐츠 렌더링 - Memoized
|
// 모드에 따른 컨텐츠 렌더링 - Memoized
|
||||||
const renderModeContent = useMemo(() => {
|
const renderModeContent = useMemo(() => {
|
||||||
if (DEBUG_MODE) {
|
if (DEBUG_MODE) {
|
||||||
console.log(
|
dlog(
|
||||||
'🎬 [DEBUG][VoiceInputOverlay] renderModeContent called',
|
'🎬 [DEBUG][VoiceInputOverlay] renderModeContent called',
|
||||||
'| currentMode:',
|
'| currentMode:',
|
||||||
currentMode,
|
currentMode,
|
||||||
@@ -1527,8 +1526,8 @@ const VoiceInputOverlay = ({
|
|||||||
}
|
}
|
||||||
|
|
||||||
// ✨ [DEBUG] Redux 상태 확인 로그
|
// ✨ [DEBUG] Redux 상태 확인 로그
|
||||||
console.log('[VoiceInput]-shopperHouseRelativeQueries');
|
dlog('[VoiceInput]-shopperHouseRelativeQueries');
|
||||||
console.log(
|
dlog(
|
||||||
JSON.stringify(
|
JSON.stringify(
|
||||||
{
|
{
|
||||||
hasRelativeQueries: hasRelativeQueries,
|
hasRelativeQueries: hasRelativeQueries,
|
||||||
@@ -1549,25 +1548,19 @@ const VoiceInputOverlay = ({
|
|||||||
);
|
);
|
||||||
|
|
||||||
if (DEBUG_MODE) {
|
if (DEBUG_MODE) {
|
||||||
console.log(
|
dlog(
|
||||||
'✅ [DEBUG][VoiceInputOverlay] MODE = PROMPT | Rendering VoicePromptScreen',
|
'✅ [DEBUG][VoiceInputOverlay] MODE = PROMPT | Rendering VoicePromptScreen',
|
||||||
hasRelativeQueries ? '(with relativeQueries)' : '(with legacySearchHistory)',
|
hasRelativeQueries ? '(with relativeQueries)' : '(with legacySearchHistory)',
|
||||||
promptSuggestions.length,
|
promptSuggestions.length,
|
||||||
'suggestions'
|
'suggestions'
|
||||||
);
|
);
|
||||||
if (hasRelativeQueries) {
|
if (hasRelativeQueries) {
|
||||||
console.log(
|
dlog('[DEBUG][VoiceInputOverlay] ├─ searchId:', shopperHouseSearchId || '(없음)');
|
||||||
'[DEBUG][VoiceInputOverlay] ├─ searchId:',
|
dlog('[DEBUG][VoiceInputOverlay] ├─ relativeQueries:', promptSuggestions);
|
||||||
shopperHouseSearchId || '(없음)'
|
dlog('[DEBUG][VoiceInputOverlay] └─ promptTitle:', promptTitle);
|
||||||
);
|
|
||||||
console.log('[DEBUG][VoiceInputOverlay] ├─ relativeQueries:', promptSuggestions);
|
|
||||||
console.log('[DEBUG][VoiceInputOverlay] └─ promptTitle:', promptTitle);
|
|
||||||
} else {
|
} else {
|
||||||
console.log('[DEBUG][VoiceInputOverlay] ├─ relativeQueries가 없음');
|
dlog('[DEBUG][VoiceInputOverlay] ├─ relativeQueries가 없음');
|
||||||
console.log(
|
dlog('[DEBUG][VoiceInputOverlay] └─ legacySearchHistory 사용:', promptSuggestions);
|
||||||
'[DEBUG][VoiceInputOverlay] └─ legacySearchHistory 사용:',
|
|
||||||
promptSuggestions
|
|
||||||
);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return (
|
return (
|
||||||
@@ -1581,7 +1574,7 @@ const VoiceInputOverlay = ({
|
|||||||
}
|
}
|
||||||
case VOICE_MODES.LISTENING:
|
case VOICE_MODES.LISTENING:
|
||||||
if (DEBUG_MODE) {
|
if (DEBUG_MODE) {
|
||||||
console.log(
|
dlog(
|
||||||
'🎤 [DEBUG][VoiceInputOverlay] MODE = LISTENING | Rendering VoiceListening (15초 타이머)'
|
'🎤 [DEBUG][VoiceInputOverlay] MODE = LISTENING | Rendering VoiceListening (15초 타이머)'
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
@@ -1595,7 +1588,7 @@ const VoiceInputOverlay = ({
|
|||||||
);
|
);
|
||||||
case VOICE_MODES.RESPONSE:
|
case VOICE_MODES.RESPONSE:
|
||||||
if (DEBUG_MODE) {
|
if (DEBUG_MODE) {
|
||||||
console.log(
|
dlog(
|
||||||
'💬 [DEBUG][VoiceInputOverlay] MODE = RESPONSE | Rendering VoiceResponse with text:',
|
'💬 [DEBUG][VoiceInputOverlay] MODE = RESPONSE | Rendering VoiceResponse with text:',
|
||||||
sttResponseText,
|
sttResponseText,
|
||||||
'isLoading: true (항상 로딩 애니메이션 표시)'
|
'isLoading: true (항상 로딩 애니메이션 표시)'
|
||||||
@@ -1614,12 +1607,12 @@ const VoiceInputOverlay = ({
|
|||||||
);
|
);
|
||||||
case VOICE_MODES.NOINIT:
|
case VOICE_MODES.NOINIT:
|
||||||
if (DEBUG_MODE) {
|
if (DEBUG_MODE) {
|
||||||
console.log('⚠️ [DEBUG][VoiceInputOverlay] MODE = NOINIT | Rendering VoiceNotRecognized');
|
dlog('⚠️ [DEBUG][VoiceInputOverlay] MODE = NOINIT | Rendering VoiceNotRecognized');
|
||||||
}
|
}
|
||||||
return <VoiceNotRecognized prompt={NOINIT_ERROR_MESSAGE} />;
|
return <VoiceNotRecognized prompt={NOINIT_ERROR_MESSAGE} />;
|
||||||
case VOICE_MODES.NOTRECOGNIZED:
|
case VOICE_MODES.NOTRECOGNIZED:
|
||||||
if (DEBUG_MODE) {
|
if (DEBUG_MODE) {
|
||||||
console.log(
|
dlog(
|
||||||
'❌ [DEBUG][VoiceInputOverlay] MODE = NOTRECOGNIZED | Rendering VoiceNotRecognized with error message'
|
'❌ [DEBUG][VoiceInputOverlay] MODE = NOTRECOGNIZED | Rendering VoiceNotRecognized with error message'
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
@@ -1631,7 +1624,7 @@ const VoiceInputOverlay = ({
|
|||||||
);
|
);
|
||||||
case VOICE_MODES.APIERROR:
|
case VOICE_MODES.APIERROR:
|
||||||
if (DEBUG_MODE) {
|
if (DEBUG_MODE) {
|
||||||
console.log(
|
dlog(
|
||||||
'💥 [DEBUG][VoiceInputOverlay] MODE = APIERROR | Rendering VoiceApiError with error details',
|
'💥 [DEBUG][VoiceInputOverlay] MODE = APIERROR | Rendering VoiceApiError with error details',
|
||||||
shopperHouseError
|
shopperHouseError
|
||||||
);
|
);
|
||||||
@@ -1651,7 +1644,7 @@ const VoiceInputOverlay = ({
|
|||||||
return <VoiceNotRecognizedCircle />;
|
return <VoiceNotRecognizedCircle />;
|
||||||
default: {
|
default: {
|
||||||
if (DEBUG_MODE) {
|
if (DEBUG_MODE) {
|
||||||
console.log('🔄 [DEBUG][VoiceInputOverlay] MODE = DEFAULT | Rendering VoicePromptScreen');
|
dlog('🔄 [DEBUG][VoiceInputOverlay] MODE = DEFAULT | Rendering VoicePromptScreen');
|
||||||
}
|
}
|
||||||
// default 케이스에서도 promptSuggestions 계산
|
// default 케이스에서도 promptSuggestions 계산
|
||||||
const hasRelativeQueries = !!shopperHouseRelativeQueries;
|
const hasRelativeQueries = !!shopperHouseRelativeQueries;
|
||||||
@@ -1712,8 +1705,8 @@ const VoiceInputOverlay = ({
|
|||||||
const handleWebSpeechMicClick = useCallback(
|
const handleWebSpeechMicClick = useCallback(
|
||||||
(e) => {
|
(e) => {
|
||||||
if (DEBUG_MODE) {
|
if (DEBUG_MODE) {
|
||||||
console.log('🎤 [DEBUG] handleWebSpeechMicClick called, currentMode:', currentMode);
|
dlog('🎤 [DEBUG] handleWebSpeechMicClick called, currentMode:', currentMode);
|
||||||
console.log('🎤 [DEBUG] CURRENT_WEBSPEECH_VERSION:', CURRENT_WEBSPEECH_VERSION);
|
dlog('🎤 [DEBUG] CURRENT_WEBSPEECH_VERSION:', CURRENT_WEBSPEECH_VERSION);
|
||||||
}
|
}
|
||||||
|
|
||||||
// 이벤트 전파 방지 - dim 레이어의 onClick 실행 방지
|
// 이벤트 전파 방지 - dim 레이어의 onClick 실행 방지
|
||||||
@@ -1730,9 +1723,9 @@ const VoiceInputOverlay = ({
|
|||||||
if (CURRENT_WEBSPEECH_VERSION === 'v1') {
|
if (CURRENT_WEBSPEECH_VERSION === 'v1') {
|
||||||
if (currentMode === VOICE_MODES.PROMPT) {
|
if (currentMode === VOICE_MODES.PROMPT) {
|
||||||
// ✨ PROMPT 모드에서만 LISTENING으로 전환 가능
|
// ✨ PROMPT 모드에서만 LISTENING으로 전환 가능
|
||||||
console.log('\n🎤 ════════════════════════════════════════════════════════════════');
|
dlog('\n🎤 ════════════════════════════════════════════════════════════════');
|
||||||
console.log('🎤 [VoiceInput] MIC BUTTON CLICKED - WebSpeech Initialization Flow (V1)');
|
dlog('🎤 [VoiceInput] MIC BUTTON CLICKED - WebSpeech Initialization Flow (V1)');
|
||||||
console.log('🎤 ════════════════════════════════════════════════════════════════\n');
|
dlog('🎤 ════════════════════════════════════════════════════════════════\n');
|
||||||
|
|
||||||
// 📋 단계별 처리:
|
// 📋 단계별 처리:
|
||||||
// 1. WebSpeech 완전 cleanup (이전 상태 제거)
|
// 1. WebSpeech 완전 cleanup (이전 상태 제거)
|
||||||
@@ -1743,56 +1736,56 @@ const VoiceInputOverlay = ({
|
|||||||
// ============================================================
|
// ============================================================
|
||||||
// 1️⃣ WebSpeech 완전 cleanup (이전 상태 제거)
|
// 1️⃣ WebSpeech 완전 cleanup (이전 상태 제거)
|
||||||
// ============================================================
|
// ============================================================
|
||||||
console.log('[VoiceInput] 📍 [STEP 1] WebSpeech Cleanup');
|
dlog('[VoiceInput] 📍 [STEP 1] WebSpeech Cleanup');
|
||||||
console.log('[VoiceInput] ├─ Dispatching: cleanupWebSpeech()');
|
dlog('[VoiceInput] ├─ Dispatching: cleanupWebSpeech()');
|
||||||
dispatch(cleanupWebSpeech());
|
dispatch(cleanupWebSpeech());
|
||||||
console.log('[VoiceInput] └─ ✅ Cleanup dispatched\n');
|
dlog('[VoiceInput] └─ ✅ Cleanup dispatched\n');
|
||||||
|
|
||||||
// ============================================================
|
// ============================================================
|
||||||
// 2️⃣ Redux STT 데이터 초기화 (searchId는 Redux에서 유지됨)
|
// 2️⃣ Redux STT 데이터 초기화 (searchId는 Redux에서 유지됨)
|
||||||
// ============================================================
|
// ============================================================
|
||||||
console.log('[VoiceInput] 📍 [STEP 2] STT Data Clear');
|
dlog('[VoiceInput] 📍 [STEP 2] STT Data Clear');
|
||||||
console.log('[VoiceInput] ├─ Dispatching: clearSTTText()');
|
dlog('[VoiceInput] ├─ Dispatching: clearSTTText()');
|
||||||
dispatch(clearSTTText());
|
dispatch(clearSTTText());
|
||||||
console.log('[VoiceInput] ├─ Clearing interim text buffer');
|
dlog('[VoiceInput] ├─ Clearing interim text buffer');
|
||||||
|
|
||||||
// ✅ TInput 초기화
|
// ✅ TInput 초기화
|
||||||
if (onSearchChange) {
|
if (onSearchChange) {
|
||||||
onSearchChange({ value: '' });
|
onSearchChange({ value: '' });
|
||||||
console.log('[VoiceInput] ├─ TInput cleared');
|
dlog('[VoiceInput] ├─ TInput cleared');
|
||||||
}
|
}
|
||||||
|
|
||||||
// ✅ Interim text ref 초기화
|
// ✅ Interim text ref 초기화
|
||||||
interimTextRef.current = '';
|
interimTextRef.current = '';
|
||||||
console.log('[VoiceInput] ├─ Interim text ref cleared');
|
dlog('[VoiceInput] ├─ Interim text ref cleared');
|
||||||
|
|
||||||
// 기존 타이머 정리
|
// 기존 타이머 정리
|
||||||
clearTimerRef(listeningTimerRef);
|
clearTimerRef(listeningTimerRef);
|
||||||
clearTimerRef(silenceDetectionTimerRef);
|
clearTimerRef(silenceDetectionTimerRef);
|
||||||
console.log('[VoiceInput] ├─ Previous timers cleared');
|
dlog('[VoiceInput] ├─ Previous timers cleared');
|
||||||
|
|
||||||
// UI 모드 업데이트
|
// UI 모드 업데이트
|
||||||
setVoiceInputMode(VOICE_INPUT_MODE.WEBSPEECH);
|
setVoiceInputMode(VOICE_INPUT_MODE.WEBSPEECH);
|
||||||
setCurrentMode(VOICE_MODES.LISTENING);
|
setCurrentMode(VOICE_MODES.LISTENING);
|
||||||
console.log('[VoiceInput] ├─ UI mode set to WEBSPEECH / LISTENING');
|
dlog('[VoiceInput] ├─ UI mode set to WEBSPEECH / LISTENING');
|
||||||
|
|
||||||
// ✅ LISTENING 모드 진입 시 로그 초기화 (새로운 음성 입력 시작)
|
// ✅ LISTENING 모드 진입 시 로그 초기화 (새로운 음성 입력 시작)
|
||||||
setWebSpeechEventLogs([]);
|
setWebSpeechEventLogs([]);
|
||||||
writeLocalStorage(VOICE_EVENT_LOGS_KEY, []);
|
writeLocalStorage(VOICE_EVENT_LOGS_KEY, []);
|
||||||
console.log('[VoiceInput] └─ ✅ Event logs cleared\n');
|
dlog('[VoiceInput] └─ ✅ Event logs cleared\n');
|
||||||
|
|
||||||
if (DEBUG_MODE) {
|
if (DEBUG_MODE) {
|
||||||
console.log('🧹 [DEBUG] Cleared webSpeechEventLogs on mic click');
|
dlog('🧹 [DEBUG] Cleared webSpeechEventLogs on mic click');
|
||||||
}
|
}
|
||||||
|
|
||||||
// ============================================================
|
// ============================================================
|
||||||
// 3️⃣ WebSpeech 재초기화 (약간의 지연 후)
|
// 3️⃣ WebSpeech 재초기화 (약간의 지연 후)
|
||||||
// ============================================================
|
// ============================================================
|
||||||
console.log('[VoiceInput] ⏳ [STEP 3] Waiting 100ms for Redux state sync...');
|
dlog('[VoiceInput] ⏳ [STEP 3] Waiting 100ms for Redux state sync...');
|
||||||
const reinitializeWebSpeech = setTimeout(() => {
|
const reinitializeWebSpeech = setTimeout(() => {
|
||||||
console.log('[VoiceInput] 📍 [STEP 3] WebSpeech Initialization');
|
dlog('[VoiceInput] 📍 [STEP 3] WebSpeech Initialization');
|
||||||
console.log('[VoiceInput] ├─ Dispatching: initializeWebSpeech()');
|
dlog('[VoiceInput] ├─ Dispatching: initializeWebSpeech()');
|
||||||
console.log('[VoiceInput] │ └─ lang: en-US, continuous: true, interimResults: true');
|
dlog('[VoiceInput] │ └─ lang: en-US, continuous: true, interimResults: true');
|
||||||
|
|
||||||
// Redux 상태 업데이트를 기다리기 위해 약간의 지연
|
// Redux 상태 업데이트를 기다리기 위해 약간의 지연
|
||||||
dispatch(
|
dispatch(
|
||||||
@@ -1803,37 +1796,37 @@ const VoiceInputOverlay = ({
|
|||||||
})
|
})
|
||||||
);
|
);
|
||||||
|
|
||||||
console.log('[VoiceInput] └─ ✅ Initialize dispatched\n');
|
dlog('[VoiceInput] └─ ✅ Initialize dispatched\n');
|
||||||
|
|
||||||
// ============================================================
|
// ============================================================
|
||||||
// 4️⃣ WebSpeech 즉시 시작
|
// 4️⃣ WebSpeech 즉시 시작
|
||||||
// ============================================================
|
// ============================================================
|
||||||
console.log('[VoiceInput] 📍 [STEP 4] WebSpeech Start');
|
dlog('[VoiceInput] 📍 [STEP 4] WebSpeech Start');
|
||||||
console.log('[VoiceInput] ├─ Dispatching: startWebSpeech()');
|
dlog('[VoiceInput] ├─ Dispatching: startWebSpeech()');
|
||||||
dispatch(startWebSpeech());
|
dispatch(startWebSpeech());
|
||||||
console.log('[VoiceInput] └─ ✅ Start dispatched\n');
|
dlog('[VoiceInput] └─ ✅ Start dispatched\n');
|
||||||
|
|
||||||
// ============================================================
|
// ============================================================
|
||||||
// 5️⃣ 15초 타이머 설정 및 준비 완료
|
// 5️⃣ 15초 타이머 설정 및 준비 완료
|
||||||
// ============================================================
|
// ============================================================
|
||||||
console.log('[VoiceInput] 📍 [STEP 5] Setup MaxListeningTime Timer');
|
dlog('[VoiceInput] 📍 [STEP 5] Setup MaxListeningTime Timer');
|
||||||
console.log('[VoiceInput] ├─ Setting 15-second listening timeout');
|
dlog('[VoiceInput] ├─ Setting 15-second listening timeout');
|
||||||
listeningTimerRef.current = setTimeout(() => {
|
listeningTimerRef.current = setTimeout(() => {
|
||||||
console.log('[VoiceInput] ⏲️ [TIMEOUT] 15-second max listening time reached');
|
dlog('[VoiceInput] ⏲️ [TIMEOUT] 15-second max listening time reached');
|
||||||
addWebSpeechEventLog('TIMEOUT_15S', '15 second timeout reached - finishing input');
|
addWebSpeechEventLog('TIMEOUT_15S', '15 second timeout reached - finishing input');
|
||||||
processFinalVoiceInput('15초 타임아웃');
|
processFinalVoiceInput('15초 타임아웃');
|
||||||
}, 15000); // 15초
|
}, 15000); // 15초
|
||||||
|
|
||||||
console.log('[VoiceInput] ├─ ⏲️ Timer started (15000ms)');
|
dlog('[VoiceInput] ├─ ⏲️ Timer started (15000ms)');
|
||||||
console.log('[VoiceInput] └─ ✅ Timer configured\n');
|
dlog('[VoiceInput] └─ ✅ Timer configured\n');
|
||||||
|
|
||||||
// ============================================================
|
// ============================================================
|
||||||
// ✨ INITIALIZATION COMPLETE
|
// ✨ INITIALIZATION COMPLETE
|
||||||
// ============================================================
|
// ============================================================
|
||||||
console.log('🎤 ════════════════════════════════════════════════════════════════');
|
dlog('🎤 ════════════════════════════════════════════════════════════════');
|
||||||
console.log('✨ [VoiceInput] 음성 인식 준비 완료! (Voice Recognition Ready!)');
|
dlog('✨ [VoiceInput] 음성 인식 준비 완료! (Voice Recognition Ready!)');
|
||||||
console.log('✨ [VoiceInput] Waiting for voice input... (max 15 seconds)');
|
dlog('✨ [VoiceInput] Waiting for voice input... (max 15 seconds)');
|
||||||
console.log('🎤 ════════════════════════════════════════════════════════════════\n');
|
dlog('🎤 ════════════════════════════════════════════════════════════════\n');
|
||||||
}, 100);
|
}, 100);
|
||||||
|
|
||||||
// Cleanup: 언마운트 시 타이머 정리
|
// Cleanup: 언마운트 시 타이머 정리
|
||||||
@@ -1843,7 +1836,7 @@ const VoiceInputOverlay = ({
|
|||||||
} else {
|
} else {
|
||||||
// listening 모드 또는 기타 모드에서 클릭 시 -> overlay 닫기
|
// listening 모드 또는 기타 모드에서 클릭 시 -> overlay 닫기
|
||||||
if (DEBUG_MODE) {
|
if (DEBUG_MODE) {
|
||||||
console.log('🎤 [DEBUG] Closing overlay (not in PROMPT mode)');
|
dlog('🎤 [DEBUG] Closing overlay (not in PROMPT mode)');
|
||||||
}
|
}
|
||||||
handleClose();
|
handleClose();
|
||||||
}
|
}
|
||||||
@@ -1852,12 +1845,12 @@ const VoiceInputOverlay = ({
|
|||||||
// V2: Promise 체인 비동기 방식 (미래 - 아직 구현 대기 중)
|
// V2: Promise 체인 비동기 방식 (미래 - 아직 구현 대기 중)
|
||||||
// ============================================================
|
// ============================================================
|
||||||
else if (CURRENT_WEBSPEECH_VERSION === 'v2') {
|
else if (CURRENT_WEBSPEECH_VERSION === 'v2') {
|
||||||
console.log('\n🎤 ════════════════════════════════════════════════════════════════');
|
dlog('\n🎤 ════════════════════════════════════════════════════════════════');
|
||||||
console.log('🎤 [VoiceInput] MIC BUTTON CLICKED - WebSpeech Initialization Flow (V2)');
|
dlog('🎤 [VoiceInput] MIC BUTTON CLICKED - WebSpeech Initialization Flow (V2)');
|
||||||
console.log('🎤 ════════════════════════════════════════════════════════════════\n');
|
dlog('🎤 ════════════════════════════════════════════════════════════════\n');
|
||||||
|
|
||||||
if (DEBUG_MODE) {
|
if (DEBUG_MODE) {
|
||||||
console.log('🎤 [DEBUG] V2 flow triggered');
|
dlog('🎤 [DEBUG] V2 flow triggered');
|
||||||
}
|
}
|
||||||
|
|
||||||
// ⚠️ V2 로직은 여기에 구현됨
|
// ⚠️ V2 로직은 여기에 구현됨
|
||||||
@@ -2135,7 +2128,7 @@ const VoiceInputOverlay = ({
|
|||||||
inputFocus={inputFocused}
|
inputFocus={inputFocused}
|
||||||
onKeyDown={handleInputKeyDown}
|
onKeyDown={handleInputKeyDown}
|
||||||
onMouseDown={() => {
|
onMouseDown={() => {
|
||||||
console.log('[InputSimple] 마우스 클릭 감지');
|
dlog('[InputSimple] 마우스 클릭 감지');
|
||||||
if (onTransitionToSearchInput) {
|
if (onTransitionToSearchInput) {
|
||||||
onTransitionToSearchInput();
|
onTransitionToSearchInput();
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user