[251024] fix: HowAboutThese ShopperHouse API,DEBUG_MODE=false
🕐 커밋 시간: 2025. 10. 24. 12:18:21 📊 변경 통계: • 총 파일: 5개 • 추가: +51줄 • 삭제: -31줄 📝 수정된 파일: ~ com.twin.app.shoptime/src/views/SearchPanel/HowAboutThese/HowAboutThese.jsx ~ com.twin.app.shoptime/src/views/SearchPanel/HowAboutThese/HowAboutThese.small.jsx ~ com.twin.app.shoptime/src/views/SearchPanel/SearchPanel.new.v2.jsx ~ com.twin.app.shoptime/src/views/SearchPanel/SearchResults.new.v2.jsx ~ com.twin.app.shoptime/src/views/SearchPanel/VoiceInputOverlay/VoiceInputOverlay.jsx 🔧 함수 변경 내용: 📄 com.twin.app.shoptime/src/views/SearchPanel/HowAboutThese/HowAboutThese.jsx (javascript): ✅ Added: HowAboutThese() ❌ Deleted: HowAboutThese() 📄 com.twin.app.shoptime/src/views/SearchPanel/HowAboutThese/HowAboutThese.small.jsx (javascript): ✅ Added: Bubble() 📄 com.twin.app.shoptime/src/views/SearchPanel/SearchPanel.new.v2.jsx (javascript): 🔄 Modified: SpotlightContainerDecorator() 📄 com.twin.app.shoptime/src/views/SearchPanel/SearchResults.new.v2.jsx (javascript): ✅ Added: SafeImage() 📄 com.twin.app.shoptime/src/views/SearchPanel/VoiceInputOverlay/VoiceInputOverlay.jsx (javascript): 🔄 Modified: Spottable(), clearAllTimers()
This commit is contained in:
@@ -1,13 +1,9 @@
|
||||
import React, {
|
||||
useCallback,
|
||||
useMemo,
|
||||
} from 'react';
|
||||
import React, { useCallback, useMemo } from 'react';
|
||||
|
||||
import PropTypes from 'prop-types';
|
||||
import { useDispatch } from 'react-redux';
|
||||
|
||||
import SpotlightContainerDecorator
|
||||
from '@enact/spotlight/SpotlightContainerDecorator';
|
||||
import SpotlightContainerDecorator from '@enact/spotlight/SpotlightContainerDecorator';
|
||||
import Spottable from '@enact/spotlight/Spottable';
|
||||
|
||||
import icShoptime from '../../../../assets/images/icons/ic-shoptime.png';
|
||||
@@ -16,16 +12,16 @@ import css from './HowAboutThese.module.less';
|
||||
|
||||
const OverlayContainer = SpotlightContainerDecorator(
|
||||
{
|
||||
enterTo: "default-element",
|
||||
restrict: "self-only",
|
||||
enterTo: 'default-element',
|
||||
restrict: 'self-only',
|
||||
},
|
||||
"div"
|
||||
'div'
|
||||
);
|
||||
|
||||
const SpottableBubble = Spottable("div");
|
||||
const SpottableSeeMoreButton = Spottable("div");
|
||||
const SpottableBubble = Spottable('div');
|
||||
const SpottableSeeMoreButton = Spottable('div');
|
||||
|
||||
const HowAboutThese = ({ relativeQueries = [], onQueryClick, onClose }) => {
|
||||
const HowAboutThese = ({ relativeQueries = [], searchId = null, onQueryClick, onClose }) => {
|
||||
const dispatch = useDispatch();
|
||||
|
||||
// 기본 relativeQueries가 없는 경우를 위한 fallback
|
||||
@@ -33,18 +29,18 @@ const HowAboutThese = ({ relativeQueries = [], onQueryClick, onClose }) => {
|
||||
return relativeQueries.length > 0
|
||||
? relativeQueries
|
||||
: [
|
||||
"What are some luxury skincare products",
|
||||
"Popular makeup gift sets suitable for mother",
|
||||
"Which anti-aging cosmetics in 50s or 60s",
|
||||
"Elegant fragrance or cosmetic bundles",
|
||||
"What beauty brands offer special gift boxes",
|
||||
'What are some luxury skincare products',
|
||||
'Popular makeup gift sets suitable for mother',
|
||||
'Which anti-aging cosmetics in 50s or 60s',
|
||||
'Elegant fragrance or cosmetic bundles',
|
||||
'What beauty brands offer special gift boxes',
|
||||
];
|
||||
}, [relativeQueries]);
|
||||
|
||||
// 검색어 클릭 핸들러
|
||||
const handleQueryClick = useCallback(
|
||||
(query) => {
|
||||
console.log("[HowAboutThese] Query clicked:", query);
|
||||
console.log('[HowAboutThese] Query clicked:', query, 'searchId:', searchId);
|
||||
|
||||
// 외부에서 전달된 onQueryClick이 있으면 사용
|
||||
if (onQueryClick) {
|
||||
@@ -52,22 +48,20 @@ const HowAboutThese = ({ relativeQueries = [], onQueryClick, onClose }) => {
|
||||
return;
|
||||
}
|
||||
|
||||
// 기본적으로 ShopperHouse API를 통해 재검색
|
||||
dispatch(getShopperHouseSearch(query));
|
||||
// ShopperHouse API를 통해 재검색 (searchId가 존재하면 포함)
|
||||
dispatch(getShopperHouseSearch(query, searchId));
|
||||
|
||||
// 팝업 닫기
|
||||
if (onClose) {
|
||||
onClose();
|
||||
}
|
||||
},
|
||||
[dispatch, onQueryClick, onClose]
|
||||
[dispatch, onQueryClick, onClose, searchId]
|
||||
);
|
||||
|
||||
// "COLLAPSE" 버튼 클릭 핸들러 (Full 버전을 Small로 축소)
|
||||
const handleCollapseClick = useCallback(() => {
|
||||
console.log(
|
||||
"[HowAboutThese] Collapse clicked - 축소하여 Small 버전으로 전환"
|
||||
);
|
||||
console.log('[HowAboutThese] Collapse clicked - 축소하여 Small 버전으로 전환');
|
||||
if (onClose) {
|
||||
onClose();
|
||||
}
|
||||
@@ -94,10 +88,7 @@ const HowAboutThese = ({ relativeQueries = [], onQueryClick, onClose }) => {
|
||||
</div>
|
||||
</div>
|
||||
<div className={css.headerRight}>
|
||||
<SpottableSeeMoreButton
|
||||
className={css.seeMoreButton}
|
||||
onClick={handleCollapseClick}
|
||||
>
|
||||
<SpottableSeeMoreButton className={css.seeMoreButton} onClick={handleCollapseClick}>
|
||||
<span className={css.seeMoreText}>COLLAPSE</span>
|
||||
</SpottableSeeMoreButton>
|
||||
</div>
|
||||
@@ -132,6 +123,7 @@ const HowAboutThese = ({ relativeQueries = [], onQueryClick, onClose }) => {
|
||||
|
||||
HowAboutThese.propTypes = {
|
||||
relativeQueries: PropTypes.array,
|
||||
searchId: PropTypes.string,
|
||||
onQueryClick: PropTypes.func,
|
||||
onClose: PropTypes.func,
|
||||
};
|
||||
|
||||
@@ -19,6 +19,7 @@ const Bubble = ({ query, onClick }) => (
|
||||
|
||||
const HowAboutTheseSmall = ({
|
||||
relativeQueries = [],
|
||||
searchId = null,
|
||||
onQueryClick,
|
||||
onSeeMoreClick,
|
||||
}) => {
|
||||
@@ -29,15 +30,15 @@ const HowAboutTheseSmall = ({
|
||||
relativeQueries.length > 0
|
||||
? relativeQueries
|
||||
: [
|
||||
"What are some luxury skincare products",
|
||||
"Popular makeup gift sets suitable for mother",
|
||||
"Which anti-aging cosmetics in 50s or 60s",
|
||||
'What are some luxury skincare products',
|
||||
'Popular makeup gift sets suitable for mother',
|
||||
'Which anti-aging cosmetics in 50s or 60s',
|
||||
];
|
||||
|
||||
// 검색어 클릭 핸들러
|
||||
const handleQueryClick = useCallback(
|
||||
(query) => {
|
||||
console.log("[HowAboutTheseSmall] Query clicked:", query);
|
||||
console.log('[HowAboutTheseSmall] Query clicked:', query, 'searchId:', searchId);
|
||||
|
||||
// 외부에서 전달된 onQueryClick이 있으면 사용
|
||||
if (onQueryClick) {
|
||||
@@ -45,15 +46,15 @@ const HowAboutTheseSmall = ({
|
||||
return;
|
||||
}
|
||||
|
||||
// 기본적으로 ShopperHouse API를 통해 재검색
|
||||
dispatch(getShopperHouseSearch(query));
|
||||
// ShopperHouse API를 통해 재검색 (searchId가 존재하면 포함)
|
||||
dispatch(getShopperHouseSearch(query, searchId));
|
||||
},
|
||||
[dispatch, onQueryClick]
|
||||
[dispatch, onQueryClick, searchId]
|
||||
);
|
||||
|
||||
// "SEE MORE" 버튼 클릭 핸들러
|
||||
const handleSeeMoreClick = useCallback(() => {
|
||||
console.log("[HowAboutTheseSmall] See More clicked");
|
||||
console.log('[HowAboutTheseSmall] See More clicked');
|
||||
|
||||
// 외부에서 전달된 onSeeMoreClick이 있으면 사용
|
||||
if (onSeeMoreClick) {
|
||||
@@ -62,7 +63,7 @@ const HowAboutTheseSmall = ({
|
||||
}
|
||||
|
||||
// 기본 동작: 확장된 뷰 표시 (나중에 구현)
|
||||
console.log("[HowAboutTheseSmall] TODO: Show expanded view");
|
||||
console.log('[HowAboutTheseSmall] TODO: Show expanded view');
|
||||
}, [onSeeMoreClick]);
|
||||
|
||||
// 첫 번째 다섯개까지 쿼리만 표시 (small 버전)
|
||||
@@ -84,22 +85,14 @@ const HowAboutTheseSmall = ({
|
||||
{/* Bubbles Section */}
|
||||
<Marquee className={css.bubblesContainer} marqueeOn="render">
|
||||
{displayQueries.map((query, index) => (
|
||||
<div
|
||||
className={css.bubble}
|
||||
key={index}
|
||||
onClick={() => handleQueryClick(query)}
|
||||
>
|
||||
<div className={css.bubble} key={index} onClick={() => handleQueryClick(query)}>
|
||||
<div className={css.bubbleText}>{query}</div>
|
||||
</div>
|
||||
))}
|
||||
</Marquee>
|
||||
|
||||
{/* See More Button */}
|
||||
<TButton
|
||||
className={css.seeMoreButton}
|
||||
size="small"
|
||||
onClick={handleSeeMoreClick}
|
||||
>
|
||||
<TButton className={css.seeMoreButton} size="small" onClick={handleSeeMoreClick}>
|
||||
<div className={css.seeMoreText}>SEE MORE</div>
|
||||
</TButton>
|
||||
</div>
|
||||
@@ -108,6 +101,7 @@ const HowAboutTheseSmall = ({
|
||||
|
||||
HowAboutTheseSmall.propTypes = {
|
||||
relativeQueries: PropTypes.array,
|
||||
searchId: PropTypes.string,
|
||||
onQueryClick: PropTypes.func,
|
||||
onSeeMoreClick: PropTypes.func,
|
||||
};
|
||||
|
||||
@@ -108,15 +108,7 @@ const SafeImageComponent = ({ src, alt, className, ...props }) => {
|
||||
};
|
||||
}, []);
|
||||
|
||||
return (
|
||||
<img
|
||||
ref={imgRef}
|
||||
src={src}
|
||||
alt={alt}
|
||||
className={className}
|
||||
{...props}
|
||||
/>
|
||||
);
|
||||
return <img ref={imgRef} src={src} alt={alt} className={className} {...props} />;
|
||||
};
|
||||
|
||||
const ITEMS_PER_PAGE = 9;
|
||||
@@ -150,6 +142,12 @@ export default function SearchPanel({ panelInfo, isOnTop, spotlightId }) {
|
||||
const panels = useSelector((state) => state.panels.panels);
|
||||
// 0hun: 음성 검색 결과에 대한 전역 상태
|
||||
const shopperHouseData = useSelector((state) => state.search.shopperHouseData);
|
||||
// 0hun: 음성 검색 searchId (Redux에서 별도 관리)
|
||||
const shopperHouseSearchId = useSelector((state) => state.search.shopperHouseSearchId);
|
||||
// 0hun: 음성 검색 relativeQueries (Redux에서 별도 관리)
|
||||
const shopperHouseRelativeQueries = useSelector(
|
||||
(state) => state.search.shopperHouseRelativeQueries
|
||||
);
|
||||
// 0hun: 검색 메인, Hot Picks for you 영역에 대한 전역 상태 값
|
||||
const hotPicksForYou = useSelector((state) => state.search.searchMainData.hotPicksForYou);
|
||||
// 0hun: 검색 메인, Popular Brands 영역에 대한 전역 상태 값
|
||||
@@ -192,7 +190,11 @@ export default function SearchPanel({ panelInfo, isOnTop, spotlightId }) {
|
||||
timestamp: new Date().toISOString(),
|
||||
});
|
||||
}
|
||||
}, [shopperHouseData?.results?.[0]?.docs?.length, shopperHouseData?.results?.[0]?.searchId, DEBUG_MODE]);
|
||||
}, [
|
||||
shopperHouseData?.results?.[0]?.docs?.length,
|
||||
shopperHouseData?.results?.[0]?.searchId,
|
||||
DEBUG_MODE,
|
||||
]);
|
||||
|
||||
// 🐛 [DEBUG] SearchPanel 마운트/언마운트 추적 (DEBUG_MODE가 true일 경우에만)
|
||||
useEffect(() => {
|
||||
@@ -255,11 +257,7 @@ export default function SearchPanel({ panelInfo, isOnTop, spotlightId }) {
|
||||
/**
|
||||
* useSearchHistory Hook 적용
|
||||
*/
|
||||
const {
|
||||
normalSearches,
|
||||
addNormalSearch,
|
||||
executeSearchFromHistory,
|
||||
} = useSearchHistory();
|
||||
const { normalSearches, addNormalSearch, executeSearchFromHistory } = useSearchHistory();
|
||||
|
||||
// Voice overlay suggestions (동적으로 변경 가능)
|
||||
const voiceSuggestions = useMemo(
|
||||
@@ -326,8 +324,9 @@ export default function SearchPanel({ panelInfo, isOnTop, spotlightId }) {
|
||||
if (e.key === 'ArrowRight' || e.key === 'Right') {
|
||||
// 커서가 텍스트 끝에 있을 때만 포커스 이동 허용
|
||||
// DOM 쿼리 최적화: 캐싱된 input element 사용
|
||||
const input = inputElementRef.current ||
|
||||
document.querySelector(`[data-spotlight-id="input-field-box"] > input`);
|
||||
const input =
|
||||
inputElementRef.current ||
|
||||
document.querySelector(`[data-spotlight-id="input-field-box"] > input`);
|
||||
if (input) {
|
||||
inputElementRef.current = input; // 캐싱
|
||||
if (position === input.value.length) {
|
||||
@@ -406,8 +405,9 @@ export default function SearchPanel({ panelInfo, isOnTop, spotlightId }) {
|
||||
*/
|
||||
const cursorPosition = useCallback(() => {
|
||||
// ref를 사용하여 캐싱된 input element 사용
|
||||
const input = inputElementRef.current ||
|
||||
document.querySelector(`[data-spotlight-id="input-field-box"] > input`);
|
||||
const input =
|
||||
inputElementRef.current ||
|
||||
document.querySelector(`[data-spotlight-id="input-field-box"] > input`);
|
||||
|
||||
if (input) {
|
||||
inputElementRef.current = input; // 캐싱
|
||||
@@ -639,7 +639,6 @@ export default function SearchPanel({ panelInfo, isOnTop, spotlightId }) {
|
||||
}, 150); // Overlay 닫히는 시간을 고려한 지연
|
||||
}, []);
|
||||
|
||||
|
||||
// ✨ [Phase 3] handleInputIconClick 제거 (돋보기 아이콘 기능 비활성화)
|
||||
// /**
|
||||
// * ✨ [Phase 3] TInput icon click handler (showVirtualKeyboard 제거)
|
||||
@@ -753,6 +752,8 @@ export default function SearchPanel({ panelInfo, isOnTop, spotlightId }) {
|
||||
itemInfo={searchDatas.item}
|
||||
showInfo={searchDatas.show}
|
||||
shopperHouseInfo={shopperHouseData}
|
||||
shopperHouseSearchId={shopperHouseSearchId}
|
||||
shopperHouseRelativeQueries={shopperHouseRelativeQueries}
|
||||
keywordClick={handleKeywordClick}
|
||||
panelInfo={panelInfo}
|
||||
/>
|
||||
@@ -1247,8 +1248,14 @@ export default function SearchPanel({ panelInfo, isOnTop, spotlightId }) {
|
||||
kind={KINDS.withIcon}
|
||||
icon={ICONS.search}
|
||||
text={searchQuery} // ✨ [Phase 8] Overlay에서 입력받은 텍스트만 표시
|
||||
alwaysShowText={currentMode === SEARCH_PANEL_MODES.VOICE_RESULT || currentMode === SEARCH_PANEL_MODES.SEARCH_RESULT} // 🎯 VOICE_RESULT & SEARCH_RESULT 모드에서 항상 텍스트 표시
|
||||
inputFocus={currentMode === SEARCH_PANEL_MODES.VOICE_RESULT || currentMode === SEARCH_PANEL_MODES.SEARCH_RESULT} // ✅ VOICE_RESULT & SEARCH_RESULT 모드에서 TInputSimple 내부 포커스 활성화
|
||||
alwaysShowText={
|
||||
currentMode === SEARCH_PANEL_MODES.VOICE_RESULT ||
|
||||
currentMode === SEARCH_PANEL_MODES.SEARCH_RESULT
|
||||
} // 🎯 VOICE_RESULT & SEARCH_RESULT 모드에서 항상 텍스트 표시
|
||||
inputFocus={
|
||||
currentMode === SEARCH_PANEL_MODES.VOICE_RESULT ||
|
||||
currentMode === SEARCH_PANEL_MODES.SEARCH_RESULT
|
||||
} // ✅ VOICE_RESULT & SEARCH_RESULT 모드에서 TInputSimple 내부 포커스 활성화
|
||||
onKeyDown={handleKeydown}
|
||||
spotlightId={SPOTLIGHT_IDS.SEARCH_INPUT_BOX}
|
||||
forcedSpotlight="recent-keyword-0"
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
import React, { useCallback, useEffect, useMemo, useRef, useState } from 'react';
|
||||
|
||||
import classNames from 'classnames';
|
||||
import { useDispatch } from 'react-redux';
|
||||
|
||||
import Spotlight from '@enact/spotlight';
|
||||
import Spottable from '@enact/spotlight/Spottable';
|
||||
@@ -13,6 +14,7 @@ import TDropDown from '../../components/TDropDown/TDropDown';
|
||||
import TVirtualGridList from '../../components/TVirtualGridList/TVirtualGridList';
|
||||
import { $L } from '../../utils/helperMethods';
|
||||
import { SpotlightIds } from '../../utils/SpotlightIds';
|
||||
import { getShopperHouseSearch } from '../../actions/searchActions';
|
||||
import css from './SearchResults.new.v2.module.less';
|
||||
import ItemCard from './SearchResultsNew/ItemCard';
|
||||
import ShowCard from './SearchResultsNew/ShowCard';
|
||||
@@ -71,8 +73,11 @@ const SearchResultsNew = ({
|
||||
showInfo,
|
||||
themeInfo,
|
||||
shopperHouseInfo,
|
||||
shopperHouseSearchId = null,
|
||||
shopperHouseRelativeQueries = [],
|
||||
keywordClick,
|
||||
}) => {
|
||||
const dispatch = useDispatch();
|
||||
// ShopperHouse 데이터를 ItemCard 형식으로 변환
|
||||
const convertedShopperHouseItems = useMemo(() => {
|
||||
if (!shopperHouseInfo || !shopperHouseInfo.results || shopperHouseInfo.results.length === 0) {
|
||||
@@ -108,6 +113,7 @@ const SearchResultsNew = ({
|
||||
};
|
||||
});
|
||||
}, [shopperHouseInfo]);
|
||||
|
||||
const getButtonTabList = () => {
|
||||
// ShopperHouse 데이터가 있으면 그것을 사용, 없으면 기존 검색 결과 사용
|
||||
const itemLength = convertedShopperHouseItems?.length || itemInfo?.length || 0;
|
||||
@@ -145,25 +151,42 @@ const SearchResultsNew = ({
|
||||
}, []);
|
||||
|
||||
// 쿼리 클릭 핸들러 (Small 버전용)
|
||||
// ShopperHouseAPI를 직접 호출 (일반 검색이 아님)
|
||||
const handleSmallQueryClick = useCallback(
|
||||
(query) => {
|
||||
if (keywordClick) {
|
||||
keywordClick(query);
|
||||
}
|
||||
console.log(
|
||||
'[SearchResultsNew] Small query clicked:',
|
||||
query,
|
||||
'searchId:',
|
||||
shopperHouseSearchId
|
||||
);
|
||||
|
||||
// ShopperHouseAPI 직접 호출
|
||||
dispatch(getShopperHouseSearch(query, shopperHouseSearchId));
|
||||
|
||||
// 쿼리 클릭 시에는 모드 유지 (small 계속 표시)
|
||||
},
|
||||
[keywordClick]
|
||||
[dispatch, shopperHouseSearchId]
|
||||
);
|
||||
|
||||
// 쿼리 클릭 핸들러 (Full 버전용)
|
||||
// ShopperHouseAPI를 직접 호출 (일반 검색이 아님)
|
||||
const handleFullQueryClick = useCallback(
|
||||
(query) => {
|
||||
if (keywordClick) {
|
||||
keywordClick(query);
|
||||
}
|
||||
console.log(
|
||||
'[SearchResultsNew] Full query clicked:',
|
||||
query,
|
||||
'searchId:',
|
||||
shopperHouseSearchId
|
||||
);
|
||||
|
||||
// ShopperHouseAPI 직접 호출
|
||||
dispatch(getShopperHouseSearch(query, shopperHouseSearchId));
|
||||
|
||||
// Full 버전을 Small로 축소
|
||||
setHowAboutTheseMode(HOW_ABOUT_THESE_MODES.SMALL);
|
||||
},
|
||||
[keywordClick]
|
||||
[dispatch, shopperHouseSearchId]
|
||||
);
|
||||
|
||||
// buttonTabList 최적화 - 의존성이 변경될 때만 재계산
|
||||
@@ -279,16 +302,14 @@ const SearchResultsNew = ({
|
||||
[themeInfo?.length, SpottableDiv, SafeImage] // themeInfo 전체 대신 length와 캐싱된 컴포넌트 사용
|
||||
);
|
||||
|
||||
// relativeQueries 가져오기 (Redux에서 제공)
|
||||
// Redux의 shopperHouseRelativeQueries를 사용하여 데이터 유지
|
||||
// relativeQueries 가져오기 (Redux에서 전달받은 props 사용)
|
||||
// Redux에서 별도로 관리되는 shopperHouseRelativeQueries를 직접 사용
|
||||
// ShopperHouseData 초기화에 영향을 받지 않음
|
||||
const relativeQueries = useMemo(() => {
|
||||
if (shopperHouseInfo?.results?.[0]?.relativeQueries) {
|
||||
// Redux에서 받은 relativeQueries 사용
|
||||
return shopperHouseInfo.results[0].relativeQueries;
|
||||
}
|
||||
// 기본값
|
||||
return ['Puppy food', 'Dog toy', 'Fitness'];
|
||||
}, [shopperHouseInfo]);
|
||||
return shopperHouseRelativeQueries && shopperHouseRelativeQueries.length > 0
|
||||
? shopperHouseRelativeQueries
|
||||
: [];
|
||||
}, [shopperHouseRelativeQueries]);
|
||||
|
||||
useEffect(() => {
|
||||
const targetId = panelInfo?.currentSpot
|
||||
@@ -320,7 +341,7 @@ const SearchResultsNew = ({
|
||||
<SpottableDiv spotlightId="how-about-these-small-wrapper">
|
||||
<HowAboutTheseSmall
|
||||
relativeQueries={relativeQueries}
|
||||
onQueryClick={handleSmallQueryClick}
|
||||
searchId={shopperHouseSearchId}
|
||||
onSeeMoreClick={handleShowFullHowAboutThese}
|
||||
/>
|
||||
</SpottableDiv>
|
||||
@@ -331,7 +352,7 @@ const SearchResultsNew = ({
|
||||
<div className={css.howAboutTheseOverlay}>
|
||||
<HowAboutThese
|
||||
relativeQueries={relativeQueries}
|
||||
onQueryClick={handleFullQueryClick}
|
||||
searchId={shopperHouseSearchId}
|
||||
onClose={handleCloseHowAboutThese}
|
||||
/>
|
||||
</div>
|
||||
|
||||
@@ -39,7 +39,7 @@ const SpottableMicButton = Spottable('div');
|
||||
const SpottableDebugButton = Spottable('div');
|
||||
|
||||
// Debug mode constant - 항상 디버그 화면 표시
|
||||
const DEBUG_MODE = true;
|
||||
const DEBUG_MODE = false;
|
||||
|
||||
// Voice overlay 모드 상수
|
||||
export const VOICE_MODES = {
|
||||
@@ -268,38 +268,46 @@ const VoiceInputOverlay = ({
|
||||
}, [webSpeechEventLogs]);
|
||||
|
||||
// 🔍 음성 검색 기록 저장 함수 (새로운 searchHistory 시스템 사용)
|
||||
const addToSearchHistory = useCallback((searchText, searchId = null) => {
|
||||
if (!searchText || searchText.trim().length < 3) return;
|
||||
const addToSearchHistory = useCallback(
|
||||
(searchText, searchId = null) => {
|
||||
if (!searchText || searchText.trim().length < 3) return;
|
||||
|
||||
const trimmedText = searchText.trim();
|
||||
const trimmedText = searchText.trim();
|
||||
|
||||
// 새로운 searchHistory 시스템에 음성검색 기록 저장
|
||||
addVoiceSearch(trimmedText, searchId);
|
||||
// 새로운 searchHistory 시스템에 음성검색 기록 저장
|
||||
addVoiceSearch(trimmedText, searchId);
|
||||
|
||||
// 기존 시스템에도 저장 (호환성 유지)
|
||||
setLegacySearchHistory((prevHistory) => {
|
||||
// 중복 제거 (대소문자 구분 없이)
|
||||
const filtered = prevHistory.filter(
|
||||
(item) => item.toLowerCase() !== trimmedText.toLowerCase()
|
||||
);
|
||||
// 기존 시스템에도 저장 (호환성 유지)
|
||||
setLegacySearchHistory((prevHistory) => {
|
||||
// 중복 제거 (대소문자 구분 없이)
|
||||
const filtered = prevHistory.filter(
|
||||
(item) => item.toLowerCase() !== trimmedText.toLowerCase()
|
||||
);
|
||||
|
||||
// 최신 항목을 맨 앞에 추가
|
||||
const newHistory = [trimmedText, ...filtered].slice(0, MAX_HISTORY_SIZE);
|
||||
// 최신 항목을 맨 앞에 추가
|
||||
const newHistory = [trimmedText, ...filtered].slice(0, MAX_HISTORY_SIZE);
|
||||
|
||||
// localStorage에 저장 (기존 키)
|
||||
writeLocalStorage(SEARCH_HISTORY_KEY, newHistory);
|
||||
// localStorage에 저장 (기존 키)
|
||||
writeLocalStorage(SEARCH_HISTORY_KEY, newHistory);
|
||||
|
||||
if (DEBUG_MODE) {
|
||||
console.log('💾 [VoiceInputOverlay.v2] Legacy search history updated:', newHistory);
|
||||
}
|
||||
|
||||
return newHistory;
|
||||
});
|
||||
|
||||
if (DEBUG_MODE) {
|
||||
console.log('💾 [VoiceInputOverlay.v2] Legacy search history updated:', newHistory);
|
||||
console.log(
|
||||
'🎤 [VoiceInputOverlay] Voice search added to history:',
|
||||
trimmedText,
|
||||
'searchId:',
|
||||
searchId
|
||||
);
|
||||
}
|
||||
|
||||
return newHistory;
|
||||
});
|
||||
|
||||
if (DEBUG_MODE) {
|
||||
console.log('🎤 [VoiceInputOverlay] Voice search added to history:', trimmedText, 'searchId:', searchId);
|
||||
}
|
||||
}, [addVoiceSearch]); // addVoiceSearch dependency 추가
|
||||
},
|
||||
[addVoiceSearch]
|
||||
); // addVoiceSearch dependency 추가
|
||||
|
||||
// WebSpeech config 메모이제이션 (불필요한 재초기화 방지)
|
||||
const webSpeechConfig = useMemo(
|
||||
@@ -1238,7 +1246,9 @@ const VoiceInputOverlay = ({
|
||||
const hasRelativeQueries = !!shopperHouseRelativeQueries;
|
||||
const promptTitle = hasRelativeQueries ? 'How about these ?' : 'Try saying';
|
||||
// relativeQueries가 있으면 사용, 없으면 legacySearchHistory 사용
|
||||
const promptSuggestions = hasRelativeQueries ? shopperHouseRelativeQueries : legacySearchHistory;
|
||||
const promptSuggestions = hasRelativeQueries
|
||||
? shopperHouseRelativeQueries
|
||||
: legacySearchHistory;
|
||||
|
||||
// ✨ [DEBUG] Redux 상태 확인 로그
|
||||
console.log('[VoiceInput]-shopperHouseRelativeQueries');
|
||||
@@ -1278,7 +1288,10 @@ const VoiceInputOverlay = ({
|
||||
console.log('[DEBUG][VoiceInputOverlay] └─ promptTitle:', promptTitle);
|
||||
} else {
|
||||
console.log('[DEBUG][VoiceInputOverlay] ├─ relativeQueries가 없음');
|
||||
console.log('[DEBUG][VoiceInputOverlay] └─ legacySearchHistory 사용:', promptSuggestions);
|
||||
console.log(
|
||||
'[DEBUG][VoiceInputOverlay] └─ legacySearchHistory 사용:',
|
||||
promptSuggestions
|
||||
);
|
||||
}
|
||||
}
|
||||
return (
|
||||
@@ -1357,7 +1370,9 @@ const VoiceInputOverlay = ({
|
||||
}
|
||||
// default 케이스에서도 promptSuggestions 계산
|
||||
const hasRelativeQueries = !!shopperHouseRelativeQueries;
|
||||
const defaultPromptSuggestions = hasRelativeQueries ? shopperHouseRelativeQueries : legacySearchHistory;
|
||||
const defaultPromptSuggestions = hasRelativeQueries
|
||||
? shopperHouseRelativeQueries
|
||||
: legacySearchHistory;
|
||||
return (
|
||||
<VoicePromptScreen
|
||||
suggestions={defaultPromptSuggestions}
|
||||
@@ -1692,7 +1707,7 @@ const VoiceInputOverlay = ({
|
||||
<div className={css.dimBackground} onClick={handleClose} />
|
||||
|
||||
{/* 디버깅용: Voice 상태 표시 */}
|
||||
{debugUI}
|
||||
{DEBUG_MODE && debugUI}
|
||||
|
||||
{/* 모드별 컨텐츠 영역 - Spotlight Container (self-only) */}
|
||||
<OverlayContainer
|
||||
@@ -1775,7 +1790,7 @@ const VoiceInputOverlay = ({
|
||||
</OverlayContainer>
|
||||
|
||||
{/* WebSpeech 이벤트 전용 디버그 - 모드와 상관없이 항상 우측 하단에 표시 */}
|
||||
<WebSpeechEventDebug logs={webSpeechEventLogs} />
|
||||
{DEBUG_MODE && <WebSpeechEventDebug logs={webSpeechEventLogs} />}
|
||||
|
||||
{/* 풀 대시보드 - 토글 버튼으로 열기/닫기 */}
|
||||
<VoiceDebugDashboard
|
||||
|
||||
Reference in New Issue
Block a user