[251024] fix: SearchInputOverlay Recent Searches

🕐 커밋 시간: 2025. 10. 24. 10:51:08

📊 변경 통계:
  • 총 파일: 7개
  • 추가: +146줄
  • 삭제: -52줄

📁 추가된 파일:
  + com.twin.app.shoptime/src/hooks/useSearchHistory.js
  + com.twin.app.shoptime/src/utils/searchHistory.js

📝 수정된 파일:
  ~ com.twin.app.shoptime/src/views/SearchPanel/SearchInputOverlay.jsx
  ~ com.twin.app.shoptime/src/views/SearchPanel/SearchPanel.new.jsx
  ~ com.twin.app.shoptime/src/views/SearchPanel/SearchPanel.new.v2.jsx
  ~ com.twin.app.shoptime/src/views/SearchPanel/VoiceInputOverlay/VoiceInputOverlay.jsx

🔧 주요 변경 내용:
  • 핵심 비즈니스 로직 개선
  • 공통 유틸리티 함수 최적화
  • 중간 규모 기능 개선
  • 모듈 구조 개선
This commit is contained in:
2025-10-24 10:51:11 +09:00
parent ec6c3aa4cd
commit 2ae50602c7
6 changed files with 422 additions and 52 deletions

View File

@@ -0,0 +1,134 @@
import { useCallback, useEffect, useMemo, useState } from 'react';
import { useDispatch } from 'react-redux';
import { getSearch, getShopperHouseSearch } from '../actions/searchActions';
import {
readSearchHistory,
writeSearchHistory,
addSearchHistory,
removeSearchHistory,
clearAllSearchHistory,
migrateVoiceSearchHistory,
} from '../utils/searchHistory';
export const useSearchHistory = () => {
const dispatch = useDispatch();
const [searchHistory, setSearchHistory] = useState(() => {
// 초기 마이그레이션 실행
const migrated = migrateVoiceSearchHistory();
return migrated;
});
// 일반 검색 기록 필터링
const normalSearches = useMemo(() => {
return searchHistory
.filter(item => item.type === 'normal')
.slice(0, 8); // SearchPanel에서 최대 8개 표시
}, [searchHistory]);
// 음성 검색 기록 필터링
const voiceSearches = useMemo(() => {
return searchHistory
.filter(item => item.type === 'voice')
.slice(0, 6); // SearchInputOverlay에서 최대 6개 표시
}, [searchHistory]);
// 최근 검색어 (전체, 최신순)
const recentSearches = useMemo(() => {
return searchHistory.slice(0, 10); // 전체 최신 10개
}, [searchHistory]);
// 일반 검색 기록 추가
const addNormalSearch = useCallback((query) => {
if (!query || typeof query !== 'string' || !query.trim()) {
console.warn('[useSearchHistory] Invalid query for normal search');
return;
}
const newHistory = addSearchHistory(query.trim(), 'normal');
setSearchHistory(newHistory);
}, []);
// 음성 검색 기록 추가
const addVoiceSearch = useCallback((query, searchId = null) => {
if (!query || typeof query !== 'string' || !query.trim()) {
console.warn('[useSearchHistory] Invalid query for voice search');
return;
}
const newHistory = addSearchHistory(query.trim(), 'voice', searchId);
setSearchHistory(newHistory);
}, []);
// 검색 기록에서 실행
const executeSearchFromHistory = useCallback((historyItem) => {
if (!historyItem || !historyItem.query) {
console.warn('[useSearchHistory] Invalid history item');
return;
}
console.log(`[useSearchHistory] Executing ${historyItem.type} search: "${historyItem.query}"`);
if (historyItem.type === 'normal') {
// 일반 검색 API 호출
dispatch(getSearch({
service: 'com.lgshop.app',
query: historyItem.query,
domain: 'theme,show,item'
}));
} else if (historyItem.type === 'voice') {
// 음성 검색 API 호출 (searchId 사용)
dispatch(getShopperHouseSearch(historyItem.query, historyItem.searchId));
} else {
console.warn('[useSearchHistory] Unknown search type:', historyItem.type);
}
}, [dispatch]);
// 특정 검색 기록 삭제
const removeHistoryItem = useCallback((timestamp) => {
const newHistory = removeSearchHistory(timestamp);
setSearchHistory(newHistory);
}, []);
// 전체 검색 기록 삭제
const clearHistory = useCallback(() => {
const newHistory = clearAllSearchHistory();
setSearchHistory(newHistory);
}, []);
// 외부에서 searchHistory가 업데이트되었을 때 동기화
const refreshHistory = useCallback(() => {
const currentHistory = readSearchHistory();
setSearchHistory(currentHistory);
}, []);
// 컴포넌트 마운트 시 localStorage 동기화
useEffect(() => {
const handleStorageChange = (e) => {
if (e.key === 'searchHistory') {
refreshHistory();
}
};
window.addEventListener('storage', handleStorageChange);
return () => {
window.removeEventListener('storage', handleStorageChange);
};
}, [refreshHistory]);
return {
// 데이터
searchHistory,
normalSearches,
voiceSearches,
recentSearches,
// 액션 함수
addNormalSearch,
addVoiceSearch,
executeSearchFromHistory,
removeHistoryItem,
clearHistory,
refreshHistory,
};
};

View File

@@ -0,0 +1,142 @@
import { readLocalStorage, writeLocalStorage } from './helperMethods';
export const SEARCH_HISTORY_KEY = 'searchHistory';
export const MAX_HISTORY_SIZE = 10;
export const readSearchHistory = () => {
try {
const history = readLocalStorage(SEARCH_HISTORY_KEY, []);
// 데이터 형식 검증 및 마이그레이션
return Array.isArray(history) ? history.filter(item =>
item &&
typeof item === 'object' &&
typeof item.query === 'string' &&
typeof item.type === 'string' &&
(item.type === 'normal' || item.type === 'voice')
) : [];
} catch (error) {
console.error('[SearchHistory] Failed to read search history:', error);
return [];
}
};
export const writeSearchHistory = (history) => {
try {
// 배열 형식 검증
if (!Array.isArray(history)) {
console.error('[SearchHistory] Invalid history format, expected array');
return;
}
// 최대 개수 제한 및 정렬 (최신순)
const sortedHistory = history
.filter(item => item && typeof item.query === 'string')
.sort((a, b) => (b.timestamp || 0) - (a.timestamp || 0))
.slice(0, MAX_HISTORY_SIZE);
writeLocalStorage(SEARCH_HISTORY_KEY, sortedHistory);
} catch (error) {
console.error('[SearchHistory] Failed to write search history:', error);
}
};
export const addSearchHistory = (query, type, searchId = null) => {
if (!query || typeof query !== 'string' || !query.trim()) {
console.warn('[SearchHistory] Invalid query provided');
return readSearchHistory();
}
if (!type || (type !== 'normal' && type !== 'voice')) {
console.warn('[SearchHistory] Invalid type provided, must be "normal" or "voice"');
return readSearchHistory();
}
try {
const history = readSearchHistory();
const trimmedQuery = query.trim();
// 동일한 검색어가 있으면 제거 (타입과 상관없이)
const filteredHistory = history.filter(item => item.query !== trimmedQuery);
// 새로운 검색 기록 추가
const newHistory = [
{
query: trimmedQuery,
type,
timestamp: Date.now(),
searchId: type === 'voice' ? searchId : null
},
...filteredHistory
].slice(0, MAX_HISTORY_SIZE);
writeSearchHistory(newHistory);
console.log(`[SearchHistory] Added ${type} search: "${trimmedQuery}"`);
return newHistory;
} catch (error) {
console.error('[SearchHistory] Failed to add search history:', error);
return readSearchHistory();
}
};
export const removeSearchHistory = (timestamp) => {
try {
const history = readSearchHistory();
const filteredHistory = history.filter(item => item.timestamp !== timestamp);
writeSearchHistory(filteredHistory);
console.log(`[SearchHistory] Removed search history item: ${timestamp}`);
return filteredHistory;
} catch (error) {
console.error('[SearchHistory] Failed to remove search history:', error);
return readSearchHistory();
}
};
export const clearAllSearchHistory = () => {
try {
writeSearchHistory([]);
console.log('[SearchHistory] Cleared all search history');
return [];
} catch (error) {
console.error('[SearchHistory] Failed to clear search history:', error);
return readSearchHistory();
}
};
// 기존 음성 검색 기록 마이그레이션 함수
export const migrateVoiceSearchHistory = () => {
try {
const oldVoiceHistoryKey = 'voiceSearchHistory';
const oldVoiceHistory = readLocalStorage(oldVoiceHistoryKey, []);
if (Array.isArray(oldVoiceHistory) && oldVoiceHistory.length > 0) {
console.log('[SearchHistory] Migrating old voice search history...');
const currentHistory = readSearchHistory();
const migratedItems = oldVoiceHistory
.filter(query => typeof query === 'string' && query.trim())
.map(query => ({
query: query.trim(),
type: 'voice',
timestamp: Date.now() - Math.random() * 86400000, // 랜덤한 과거 시간
searchId: null
}));
const combinedHistory = [...migratedItems, ...currentHistory]
.sort((a, b) => (b.timestamp || 0) - (a.timestamp || 0))
.slice(0, MAX_HISTORY_SIZE);
writeSearchHistory(combinedHistory);
// 기존 음성 검색 기록 삭제 (선택적)
// writeLocalStorage(oldVoiceHistoryKey, []);
console.log(`[SearchHistory] Migrated ${migratedItems.length} items from voice search history`);
return combinedHistory;
}
return readSearchHistory();
} catch (error) {
console.error('[SearchHistory] Failed to migrate voice search history:', error);
return readSearchHistory();
}
};

View File

@@ -9,6 +9,7 @@ import Spottable from '@enact/spotlight/Spottable';
import { getSearch } from '../../actions/searchActions';
import TFullPopup from '../../components/TFullPopup/TFullPopup';
import TInput, { ICONS, KINDS } from '../../components/TInput/TInput';
import { useSearchHistory } from '../../hooks/useSearchHistory';
import css from './SearchInputOverlay.module.less';
// const OverlayContainer = SpotlightContainerDecorator(
@@ -30,8 +31,10 @@ const SearchInputOverlay = ({
handleClick,
}) => {
const dispatch = useDispatch();
const { addNormalSearch, normalSearches } = useSearchHistory();
const recentResultSearches = useMemo(
// - localStorage
const fallbackSearches = useMemo(
() => [
'Puppy food',
'Dog toy',
@@ -43,14 +46,29 @@ const SearchInputOverlay = ({
],
[]
);
// localStorage ( 5) ( )
const recentResultSearches = useMemo(() => {
// normalSearches 5
if (normalSearches && normalSearches.length > 0) {
return normalSearches.slice(0, 5);
}
// normalSearches
return fallbackSearches;
}, [normalSearches, fallbackSearches]);
const handleSearchSubmit = useCallback(
(query) => {
if (searchQuery && searchQuery.trim()) {
// query
if (query && query.trim()) {
//
addNormalSearch(query.trim());
// API
dispatch(
getSearch({
service: 'com.lgshop.app',
query: query,
query: query.trim(),
domain: 'theme,show,item',
})
);
@@ -61,10 +79,21 @@ const SearchInputOverlay = ({
}, 100);
}
},
[dispatch, searchQuery, onClose]
[dispatch, addNormalSearch, onClose]
);
const handleKeydown = useCallback(
(e) => {
// Enter OK
if (e.key === 'Enter' || e.keyCode === 13) {
e.preventDefault();
handleSearchSubmit(searchQuery);
}
},
[searchQuery, handleSearchSubmit]
);
const handleDimClick = useCallback(
(e) => {
onClose();
@@ -105,6 +134,7 @@ const SearchInputOverlay = ({
icon={ICONS.search}
value={searchQuery}
onChange={onSearchChange}
onKeyDown={handleKeydown}
onIconClick={() => {
handleSearchSubmit(searchQuery);
}}
@@ -114,18 +144,26 @@ const SearchInputOverlay = ({
</div>
</div>
{/* 이곳에 작업 */}
{/* 최근 검색어 섹션 - localStorage 기반 일반 검색어만 표시 (최대 5개) */}
<div className={css.overLayRecent}>
{recentResultSearches.map((keyword, index) => (
<SpottableKeyword
key={`recentResult-${index}`}
className={css.keywordButton}
onClick={() => handleClick(keyword)}
spotlightId={`recent-Resultkeyword-${index}`}
>
{keyword}
</SpottableKeyword>
))}
{recentResultSearches.map((item, index) => {
// normalSearches object, fallbackSearches string
const keyword = typeof item === 'object' ? item.query : item;
return (
<SpottableKeyword
key={`recentResult-${index}`}
className={css.keywordButton}
onClick={() => {
//
handleClick(keyword);
}}
spotlightId={`recent-Resultkeyword-${index}`}
>
{keyword}
</SpottableKeyword>
);
})}
</div>
</div>
</div>

View File

@@ -62,7 +62,7 @@ import {
} from '../../utils/Config';
import NoSearchResults from './NoSearchResults/NoSearchResults';
// import NoSearchResults from './NoSearchResults/NoSearchResults';
import SearchInputOverlay from './SearchInpuOverlay';
import SearchInputOverlay from './SearchInputOverlay';
import css from './SearchPanel.new.module.less';
import SearchResultsNew from './SearchResults.new';
import TInput, {

View File

@@ -35,10 +35,11 @@ import TVerticalPagenator from '../../components/TVerticalPagenator/TVerticalPag
import TVirtualGridList from '../../components/TVirtualGridList/TVirtualGridList';
// import VirtualKeyboardContainer from "../../components/TToast/VirtualKeyboardContainer";
import usePrevious from '../../hooks/usePrevious';
import { useSearchHistory } from '../../hooks/useSearchHistory';
import { LOG_CONTEXT_NAME, LOG_MENU, LOG_MESSAGE_ID, panel_names } from '../../utils/Config';
import NoSearchResults from './NoSearchResults/NoSearchResults';
// import NoSearchResults from './NoSearchResults/NoSearchResults';
import SearchInputOverlay from './SearchInpuOverlay';
import SearchInputOverlay from './SearchInputOverlay';
import css from './SearchPanel.new.module.less';
import SearchResultsNew from './SearchResults.new.v2';
import TInputSimple, { ICONS, KINDS } from './TInput/TInputSimple';
@@ -252,10 +253,13 @@ export default function SearchPanel({ panelInfo, isOnTop, spotlightId }) {
const SafeImage = useCallback(SafeImageComponent, []);
/**
* memoized variables
* useSearchHistory Hook 적용
*/
// 가짜 데이터 - 실제로는 Redux store나 API에서 가져와야 함
const recentSearches = useMemo(() => ['Puppy food', 'Dog toy', 'Fitness'], []);
const {
normalSearches,
addNormalSearch,
executeSearchFromHistory,
} = useSearchHistory();
// Voice overlay suggestions (동적으로 변경 가능)
const voiceSuggestions = useMemo(
@@ -373,6 +377,9 @@ export default function SearchPanel({ panelInfo, isOnTop, spotlightId }) {
}
if (query.trim()) {
// 일반 검색 기록 저장
addNormalSearch(query.trim());
dispatch(
getSearch({
domain: 'theme,show,item',
@@ -390,7 +397,7 @@ export default function SearchPanel({ panelInfo, isOnTop, spotlightId }) {
setSearchQuery(query);
},
[searchPerformed, dispatch] // ✨ [Phase 3] dispatch 추가, showVirtualKeyboard 제거
[searchPerformed, dispatch, addNormalSearch] // ✨ [Phase 3] dispatch 추가, showVirtualKeyboard 제거, addNormalSearch 추가
);
/**
@@ -558,6 +565,28 @@ export default function SearchPanel({ panelInfo, isOnTop, spotlightId }) {
[handleSearchSubmit]
);
/**
* 검색 기록에서 키워드 클릭 핸들러
*/
const handleHistoryKeywordClick = useCallback(
(historyItem) => {
if (!historyItem || !historyItem.query) {
return;
}
// 검색어 설정
setSearchQuery(historyItem.query);
// 검색 타입에 따라 다른 API 호출
executeSearchFromHistory(historyItem);
// Overlay 닫기 및 포커스 해제
setIsSearchOverlayVisible(false);
setInputFocus(false);
},
[executeSearchFromHistory]
);
/**
* 키워드 클릭 핸들러 생성 함수
*/
@@ -568,6 +597,16 @@ export default function SearchPanel({ panelInfo, isOnTop, spotlightId }) {
[handleKeywordClick]
);
/**
* 검색 기록 키워드 클릭 핸들러 생성 함수
*/
const createHistoryKeywordClickHandler = useCallback(
(historyItem) => {
return () => handleHistoryKeywordClick(historyItem);
},
[handleHistoryKeywordClick]
);
/**
* Search overlay close handler
*/
@@ -727,8 +766,8 @@ export default function SearchPanel({ panelInfo, isOnTop, spotlightId }) {
default:
return (
<ContainerBasic className={css.contentContainer}>
{/* 최근 검색어 섹션 */}
{recentSearches && recentSearches.length > 0 && (
{/* 최근 검색어 섹션 (일반검색만 표시) */}
{normalSearches && normalSearches.length > 0 && (
<>
<SectionContainer
className={css.section}
@@ -740,14 +779,14 @@ export default function SearchPanel({ panelInfo, isOnTop, spotlightId }) {
<div className={css.sectionTitle}>Your Recent Searches</div>
</div>
<div className={css.keywordList}>
{recentSearches.map((keyword, index) => (
{normalSearches.map((historyItem, index) => (
<SpottableKeyword
key={`recent-${index}`}
key={`recent-${historyItem.timestamp}`}
className={css.keywordButton}
onClick={createKeywordClickHandler(keyword)}
onClick={createHistoryKeywordClickHandler(historyItem)}
spotlightId={`recent-keyword-${index}`}
>
{keyword}
{historyItem.query}
</SpottableKeyword>
))}
</div>
@@ -847,12 +886,13 @@ export default function SearchPanel({ panelInfo, isOnTop, spotlightId }) {
searchDatas?.item?.length,
searchDatas?.show?.length,
shopperHouseData?.results?.[0]?.docs?.length,
recentSearches?.length,
normalSearches?.length,
topSearchs?.length,
popularBrands?.length,
hotPicksForYou?.length,
handleKeywordClick,
createKeywordClickHandler,
createHistoryKeywordClickHandler,
renderItem,
panelInfo?.currentSpot,
]);

View File

@@ -16,6 +16,7 @@ import TFullPopup from '../../../components/TFullPopup/TFullPopup';
import TInput, { ICONS, KINDS } from '../TInput/TInput';
import { useWebSpeech } from '../../../hooks/useWebSpeech';
import { readLocalStorage, writeLocalStorage } from '../../../utils/helperMethods';
import { useSearchHistory } from '../../../hooks/useSearchHistory';
import VoiceListening from './modes/VoiceListening';
import VoiceNotRecognized from './modes/VoiceNotRecognized';
import VoiceNotRecognizedCircle from './modes/VoiceNotRecognizedCircle';
@@ -176,11 +177,14 @@ const VoiceInputOverlay = ({
const [isBubbleClickSearch, setIsBubbleClickSearch] = useState(false);
// 디버그 대시보드 표시 여부
const [showDashboard, setShowDashboard] = useState(false);
// 검색 기록 관리 (localStorage 기반, 최근 5개)
const [searchHistory, setSearchHistory] = useState(() => {
// useSearchHistory Hook 적용 (음성검색 기록 관리)
const { addVoiceSearch, recentSearches } = useSearchHistory();
// 기존 검색 기록 (마이그레이션용)
const [legacySearchHistory, setLegacySearchHistory] = useState(() => {
const history = readLocalStorage(SEARCH_HISTORY_KEY, DEFAULT_SUGGESTIONS);
if (DEBUG_MODE) {
console.log('📚 [DEBUG] Loaded searchHistory from localStorage:', history);
console.log('📚 [DEBUG] Loaded legacy searchHistory from localStorage:', history);
}
return history;
});
@@ -263,13 +267,17 @@ const VoiceInputOverlay = ({
}
}, [webSpeechEventLogs]);
// 🔍 검색 기록 저장 함수 (성능 최적화: stable reference)
const addToSearchHistory = useCallback((searchText) => {
// 🔍 음성 검색 기록 저장 함수 (새로운 searchHistory 시스템 사용)
const addToSearchHistory = useCallback((searchText, searchId = null) => {
if (!searchText || searchText.trim().length < 3) return;
const trimmedText = searchText.trim();
setSearchHistory((prevHistory) => {
// 새로운 searchHistory 시스템에 음성검색 기록 저장
addVoiceSearch(trimmedText, searchId);
// 기존 시스템에도 저장 (호환성 유지)
setLegacySearchHistory((prevHistory) => {
// 중복 제거 (대소문자 구분 없이)
const filtered = prevHistory.filter(
(item) => item.toLowerCase() !== trimmedText.toLowerCase()
@@ -278,16 +286,20 @@ const VoiceInputOverlay = ({
// 최신 항목을 맨 앞에 추가
const newHistory = [trimmedText, ...filtered].slice(0, MAX_HISTORY_SIZE);
// localStorage에 저장
// localStorage에 저장 (기존 키)
writeLocalStorage(SEARCH_HISTORY_KEY, newHistory);
if (DEBUG_MODE) {
console.log('💾 [VoiceInputOverlay.v2] Search history updated:', newHistory);
console.log('💾 [VoiceInputOverlay.v2] Legacy search history updated:', newHistory);
}
return newHistory;
});
}, []); // dependency 없음 (setSearchHistory는 stable)
if (DEBUG_MODE) {
console.log('🎤 [VoiceInputOverlay] Voice search added to history:', trimmedText, 'searchId:', searchId);
}
}, [addVoiceSearch]); // addVoiceSearch dependency 추가
// WebSpeech config 메모이제이션 (불필요한 재초기화 방지)
const webSpeechConfig = useMemo(
@@ -745,13 +757,12 @@ const VoiceInputOverlay = ({
setCurrentMode(VOICE_MODES.RESPONSE);
setVoiceInputMode(null);
// ✨ 검색 기록에 추가
addToSearchHistory(finalText);
// ✨ 검색 기록에 추가 (searchId 포함)
const currentSearchId = shopperHouseSearchIdRef.current;
addToSearchHistory(finalText, currentSearchId);
// ✨ ShopperHouse API 자동 호출 (2차 발화 시 searchId 포함)
const query = finalText.trim();
// ✅ Ref에서 최신 searchId 읽기 (useCallback closure 문제 해결)
const currentSearchId = shopperHouseSearchIdRef.current;
console.log('[VoiceInput] 📤 API 요청 전송');
console.log('[VoiceInput] ├─ query:', query);
console.log('[VoiceInput] ├─ ref 값:', shopperHouseSearchIdRef.current);
@@ -1098,8 +1109,8 @@ const VoiceInputOverlay = ({
onSearchChange({ value: query });
}
// ✨ 검색 기록에 추가
addToSearchHistory(query);
// ✨ 검색 기록에 추가 (searchId 포함)
addToSearchHistory(query, shopperHouseSearchIdRef.current);
// ✨ RESPONSE 모드로 전환을 위한 텍스트 설정
setSttResponseText(query);
@@ -1112,6 +1123,7 @@ const VoiceInputOverlay = ({
if (query && query.length >= 3) {
// ✅ Ref에서 최신 searchId 읽기 (이전 검색이 있는 경우 2차 발화 처리)
const currentSearchId = shopperHouseSearchIdRef.current;
if (DEBUG_MODE) {
console.log('🔍 [DEBUG] Calling ShopperHouse API from bubble click');
console.log('[VoiceInput] ├─ query:', query);
@@ -1225,8 +1237,8 @@ const VoiceInputOverlay = ({
// ✨ relativeQueries가 있으면 'How about these ?' 표시, 없으면 'Try saying' 표시
const hasRelativeQueries = !!shopperHouseRelativeQueries;
const promptTitle = hasRelativeQueries ? 'How about these ?' : 'Try saying';
// relativeQueries가 있으면 사용, 없으면 searchHistory 사용
const promptSuggestions = hasRelativeQueries ? shopperHouseRelativeQueries : searchHistory;
// relativeQueries가 있으면 사용, 없으면 legacySearchHistory 사용
const promptSuggestions = hasRelativeQueries ? shopperHouseRelativeQueries : legacySearchHistory;
// ✨ [DEBUG] Redux 상태 확인 로그
console.log('[VoiceInput]-shopperHouseRelativeQueries');
@@ -1241,7 +1253,7 @@ const VoiceInputOverlay = ({
promptTitle: promptTitle,
promptSuggestions: promptSuggestions,
promptSuggestions_length: promptSuggestions?.length || 0,
searchHistory_length: searchHistory?.length || 0,
searchHistory_length: legacySearchHistory?.length || 0,
shopperHouseSearchId: shopperHouseSearchId || '(없음)',
shopperHouseData_exists: !!shopperHouseData,
},
@@ -1253,7 +1265,7 @@ const VoiceInputOverlay = ({
if (DEBUG_MODE) {
console.log(
'✅ [DEBUG][VoiceInputOverlay] MODE = PROMPT | Rendering VoicePromptScreen',
hasRelativeQueries ? '(with relativeQueries)' : '(with searchHistory)',
hasRelativeQueries ? '(with relativeQueries)' : '(with legacySearchHistory)',
promptSuggestions.length,
'suggestions'
);
@@ -1266,7 +1278,7 @@ const VoiceInputOverlay = ({
console.log('[DEBUG][VoiceInputOverlay] └─ promptTitle:', promptTitle);
} else {
console.log('[DEBUG][VoiceInputOverlay] ├─ relativeQueries가 없음');
console.log('[DEBUG][VoiceInputOverlay] └─ searchHistory 사용:', promptSuggestions);
console.log('[DEBUG][VoiceInputOverlay] └─ legacySearchHistory 사용:', promptSuggestions);
}
}
return (
@@ -1339,22 +1351,26 @@ const VoiceInputOverlay = ({
case VOICE_MODES.MODE_4:
// 추후 MODE_4 컴포넌트 추가
return <VoiceNotRecognizedCircle />;
default:
default: {
if (DEBUG_MODE) {
console.log('🔄 [DEBUG][VoiceInputOverlay] MODE = DEFAULT | Rendering VoicePromptScreen');
}
// default 케이스에서도 promptSuggestions 계산
const hasRelativeQueries = !!shopperHouseRelativeQueries;
const defaultPromptSuggestions = hasRelativeQueries ? shopperHouseRelativeQueries : legacySearchHistory;
return (
<VoicePromptScreen
suggestions={searchHistory}
suggestions={defaultPromptSuggestions}
onSuggestionClick={handleSuggestionClick}
/>
);
}
}
}, [
currentMode,
voiceInputMode,
isListening,
searchHistory,
legacySearchHistory,
handleSuggestionClick,
interimText,
sttResponseText,