From 032f4466a26b02fd5d81cd77bffa949bc2fff7cb Mon Sep 17 00:00:00 2001 From: Yen Truong Date: Wed, 17 Nov 2021 17:47:44 -0500 Subject: [PATCH 01/21] Support "Near Me" queries - added `executeSearchWithUserLocation` function J=SLAP-1706 TEST=manual - set location to washington DC in when vertical page is loaded, click update my location, and see that the label is updated to my device's location. - set default search query to 'engineers near me' and see that user location is set based on location near my device. --- sample-app/src/components/LocationBias.tsx | 37 +++---------- sample-app/src/components/SearchBar.tsx | 5 +- sample-app/src/pages/UniversalSearchPage.tsx | 3 +- sample-app/src/pages/VerticalSearchPage.tsx | 3 +- sample-app/src/utils/geolocationutils.tsx | 58 ++++++++++++++++++++ 5 files changed, 72 insertions(+), 34 deletions(-) create mode 100644 sample-app/src/utils/geolocationutils.tsx diff --git a/sample-app/src/components/LocationBias.tsx b/sample-app/src/components/LocationBias.tsx index c19a3975..3cf29afd 100644 --- a/sample-app/src/components/LocationBias.tsx +++ b/sample-app/src/components/LocationBias.tsx @@ -1,12 +1,9 @@ import { useAnswersActions, useAnswersState, LocationBiasMethod } from '@yext/answers-headless-react'; +import { executeSearchWithUserLocation } from '../utils/geolocationutils'; interface Props { isVertical: boolean, - geolocationOptions?: { - enableHighAccuracy?: boolean, - timeout?: number, - maximumAge?: number - }, + geolocationOptions?: PositionOptions, cssClasses?: { container: string, location: string, @@ -15,11 +12,6 @@ interface Props { } } -const defaultGeolocationOptions = { - enableHighAccuracy: false, - timeout: 6000, - maximumAge: 300000, -}; const defaultCSSClasses = { container: 'LocationBias', @@ -30,9 +22,9 @@ const defaultCSSClasses = { export default function LocationBias(props: Props) { const answersActions = useAnswersActions(); + const { isVertical, geolocationOptions, cssClasses: customCssClasses } = props; const locationBias = useAnswersState(s => s.location.locationBias) - const geolocationOptions = Object.assign(defaultGeolocationOptions, props.geolocationOptions); - const cssClasses = Object.assign(defaultCSSClasses, props.cssClasses); + const cssClasses = Object.assign(defaultCSSClasses, customCssClasses); if (!locationBias?.displayName) return null; @@ -41,22 +33,6 @@ export default function LocationBias(props: Props) { : locationBias?.method === LocationBiasMethod.Device ? '(based on your device)' : ''; - function handleGeolocationClick() { - if ('geolocation' in navigator) { - navigator.geolocation.getCurrentPosition((position) => { - answersActions.setUserLocation({ - latitude: position.coords.latitude, - longitude: position.coords.longitude, - }) - props.isVertical - ? answersActions.executeVerticalQuery() - : answersActions.executeUniversalQuery() - }, - (err) => console.error(err), - geolocationOptions); - } - } - return (
@@ -67,7 +43,10 @@ export default function LocationBias(props: Props) { {attributionMessage} )} -
diff --git a/sample-app/src/components/SearchBar.tsx b/sample-app/src/components/SearchBar.tsx index b52ddaf5..467315cc 100644 --- a/sample-app/src/components/SearchBar.tsx +++ b/sample-app/src/components/SearchBar.tsx @@ -6,6 +6,7 @@ import '../sass/SearchBar.scss'; import '../sass/Autocomplete.scss'; import LoadingIndicator from './LoadingIndicator'; import { useAutocomplete } from '../hooks/useAutocomplete'; +import { executeSearchWithUserLocation } from '../utils/geolocationutils'; const SCREENREADER_INSTRUCTIONS = 'When autocomplete results are available, use up and down arrows to review and enter to select.' @@ -25,9 +26,7 @@ export default function SearchBar({ placeholder, isVertical, screenReaderInstruc const isLoading = useAnswersState(state => state.searchStatus.isLoading); function executeQuery () { - isVertical - ? answersActions.executeVerticalQuery() - : answersActions.executeUniversalQuery(); + executeSearchWithUserLocation(answersActions, isVertical, {}, true); } function renderSearchButton () { diff --git a/sample-app/src/pages/UniversalSearchPage.tsx b/sample-app/src/pages/UniversalSearchPage.tsx index 5de7abdf..4755b1df 100644 --- a/sample-app/src/pages/UniversalSearchPage.tsx +++ b/sample-app/src/pages/UniversalSearchPage.tsx @@ -3,6 +3,7 @@ import { useLayoutEffect } from 'react'; import { useAnswersActions } from '@yext/answers-headless-react'; import '../sass/UniversalSearchPage.scss'; import { UniversalResultsConfig } from '../universalResultsConfig'; +import { executeSearchWithUserLocation } from '../utils/geolocationutils'; const universalResultsFilterConfig = { show: true @@ -17,7 +18,7 @@ export default function UniversalSearchPage(props: { universalResultsConfig: Uni vertical: {} }) answersActions.setVerticalKey(''); - answersActions.executeUniversalQuery(); + executeSearchWithUserLocation(answersActions, false, {}, true); }, [answersActions]); return ( diff --git a/sample-app/src/pages/VerticalSearchPage.tsx b/sample-app/src/pages/VerticalSearchPage.tsx index f05477eb..759cd9cf 100644 --- a/sample-app/src/pages/VerticalSearchPage.tsx +++ b/sample-app/src/pages/VerticalSearchPage.tsx @@ -10,6 +10,7 @@ import '../sass/VerticalSearchPage.scss'; import { StandardCard } from '../components/cards/StandardCard'; import { useLayoutEffect } from 'react'; import { useAnswersActions } from '@yext/answers-headless-react'; +import { executeSearchWithUserLocation } from '../utils/geolocationutils'; const countryFilterOptions = [ { @@ -68,7 +69,7 @@ export default function VerticalSearchPage(props: { universal: {} }); answersActions.setVerticalKey(props.verticalKey); - answersActions.executeVerticalQuery(); + executeSearchWithUserLocation(answersActions, true, {}, true); }, [answersActions, props.verticalKey]); return ( diff --git a/sample-app/src/utils/geolocationutils.tsx b/sample-app/src/utils/geolocationutils.tsx new file mode 100644 index 00000000..6f3d582d --- /dev/null +++ b/sample-app/src/utils/geolocationutils.tsx @@ -0,0 +1,58 @@ +import { AnswersActions } from "@yext/answers-headless-react"; +import { SearchIntent } from "@yext/answers-headless"; + +const defaultGeolocationOptions: PositionOptions = { + enableHighAccuracy: false, + timeout: 6000, + maximumAge: 300000, +}; + +/** + * Executes universal or vertical search with user location retrieved from nagivator.geolocation API. + * If useLocationOnNearMeOnly is true, will attempt to get user location only if the query's searchIntents + * (retrieved through autocomplete response) contains 'NEAR_ME'. If there's a failure in getting + * user location, a normal search is performed. + */ +export async function executeSearchWithUserLocation( + answersActions: AnswersActions, + isVertical: boolean, + geolocationOptions?: PositionOptions, + useLocationOnNearMeOnly?: boolean +) { + const executeSearch = () => { + isVertical + ? answersActions.executeVerticalQuery() + : answersActions.executeUniversalQuery(); + return; + } + + if (useLocationOnNearMeOnly) { + const results = isVertical + ? await answersActions.executeVerticalAutocomplete() + : await answersActions.executeUniversalAutocomplete(); + if (!results?.inputIntents?.includes(SearchIntent.NearMe)) { + executeSearch(); + return; + } + } + + if ('geolocation' in navigator) { + navigator.geolocation.getCurrentPosition((position: GeolocationPosition) => { + answersActions.setUserLocation({ + latitude: position.coords.latitude, + longitude: position.coords.longitude, + }) + executeSearch(); + }, + (err) => { + console.error(err); + console.error('unable to determine user location.'); + executeSearch(); + }, + Object.assign(defaultGeolocationOptions, geolocationOptions) + ); + } else { + console.warn('unable to determine user location.'); + executeSearch(); + } +} From c4d4bf4714655cb84bf41b65b23a7e5c59519c42 Mon Sep 17 00:00:00 2001 From: Yen Truong Date: Wed, 17 Nov 2021 18:45:34 -0500 Subject: [PATCH 02/21] refactor executeSearch --- sample-app/src/utils/geolocationutils.tsx | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/sample-app/src/utils/geolocationutils.tsx b/sample-app/src/utils/geolocationutils.tsx index 6f3d582d..c1be093b 100644 --- a/sample-app/src/utils/geolocationutils.tsx +++ b/sample-app/src/utils/geolocationutils.tsx @@ -42,17 +42,15 @@ export async function executeSearchWithUserLocation( latitude: position.coords.latitude, longitude: position.coords.longitude, }) - executeSearch(); }, (err) => { console.error(err); console.error('unable to determine user location.'); - executeSearch(); }, Object.assign(defaultGeolocationOptions, geolocationOptions) ); } else { console.warn('unable to determine user location.'); - executeSearch(); } + executeSearch(); } From b398b42e389a81fefe02220088942969e7d16718 Mon Sep 17 00:00:00 2001 From: Yen Truong Date: Wed, 17 Nov 2021 18:47:48 -0500 Subject: [PATCH 03/21] grammar --- sample-app/src/utils/geolocationutils.tsx | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/sample-app/src/utils/geolocationutils.tsx b/sample-app/src/utils/geolocationutils.tsx index c1be093b..13f51156 100644 --- a/sample-app/src/utils/geolocationutils.tsx +++ b/sample-app/src/utils/geolocationutils.tsx @@ -45,12 +45,12 @@ export async function executeSearchWithUserLocation( }, (err) => { console.error(err); - console.error('unable to determine user location.'); + console.error('Unable to determine user\'s location.'); }, Object.assign(defaultGeolocationOptions, geolocationOptions) ); } else { - console.warn('unable to determine user location.'); + console.warn('Unable to determine user\'s location.'); } executeSearch(); } From 4a4d600157a568fd4993942865936cd2b64ae14a Mon Sep 17 00:00:00 2001 From: Yen Truong Date: Thu, 18 Nov 2021 13:07:13 -0500 Subject: [PATCH 04/21] feedback --- sample-app/src/components/LocationBias.tsx | 13 +++++- sample-app/src/components/SearchBar.tsx | 33 +++++++++++---- sample-app/src/hooks/useAutocomplete.tsx | 32 +++++++++------ sample-app/src/pages/UniversalSearchPage.tsx | 16 +++++++- sample-app/src/pages/VerticalSearchPage.tsx | 16 +++++++- sample-app/src/utils/searchhandler.ts | 43 ++++++++++++++++++++ 6 files changed, 128 insertions(+), 25 deletions(-) create mode 100644 sample-app/src/utils/searchhandler.ts diff --git a/sample-app/src/components/LocationBias.tsx b/sample-app/src/components/LocationBias.tsx index 3cf29afd..dde5e9a3 100644 --- a/sample-app/src/components/LocationBias.tsx +++ b/sample-app/src/components/LocationBias.tsx @@ -1,5 +1,5 @@ import { useAnswersActions, useAnswersState, LocationBiasMethod } from '@yext/answers-headless-react'; -import { executeSearchWithUserLocation } from '../utils/geolocationutils'; +import SearchHandler from '../utils/searchhandler'; interface Props { isVertical: boolean, @@ -33,6 +33,15 @@ export default function LocationBias(props: Props) { : locationBias?.method === LocationBiasMethod.Device ? '(based on your device)' : ''; + async function handleGeolocationClick() { + const position = await SearchHandler.getUserLocation(geolocationOptions); + answersActions.setUserLocation({ + latitude: position.coords.latitude, + longitude: position.coords.longitude, + }) + SearchHandler.executeSearch(answersActions, isVertical); + } + return (
@@ -45,7 +54,7 @@ export default function LocationBias(props: Props) { )} diff --git a/sample-app/src/components/SearchBar.tsx b/sample-app/src/components/SearchBar.tsx index 467315cc..1a38fcb7 100644 --- a/sample-app/src/components/SearchBar.tsx +++ b/sample-app/src/components/SearchBar.tsx @@ -6,27 +6,46 @@ import '../sass/SearchBar.scss'; import '../sass/Autocomplete.scss'; import LoadingIndicator from './LoadingIndicator'; import { useAutocomplete } from '../hooks/useAutocomplete'; -import { executeSearchWithUserLocation } from '../utils/geolocationutils'; +import SearchHandler from '../utils/searchhandler'; +import { SearchIntent } from '@yext/answers-headless'; const SCREENREADER_INSTRUCTIONS = 'When autocomplete results are available, use up and down arrows to review and enter to select.' interface Props { placeholder?: string, isVertical: boolean, + geolocationOptions?: PositionOptions, screenReaderInstructionsId: string } /** * Renders a SearchBar that is hooked up with an Autocomplete component */ -export default function SearchBar({ placeholder, isVertical, screenReaderInstructionsId }: Props) { +export default function SearchBar({ + placeholder, + isVertical, + geolocationOptions, + screenReaderInstructionsId +}: Props) { const answersActions = useAnswersActions(); const query = useAnswersState(state => state.query.input); - const [ autocompleteResults, executeAutocomplete ] = useAutocomplete(isVertical); const isLoading = useAnswersState(state => state.searchStatus.isLoading); + const [ + autocompleteResponse, + latestAutocompleteResponseRef, + executeAutocomplete + ] = useAutocomplete(isVertical); - function executeQuery () { - executeSearchWithUserLocation(answersActions, isVertical, {}, true); + async function executeQuery () { + await latestAutocompleteResponseRef.current; + if (autocompleteResponse?.inputIntents.includes(SearchIntent.NearMe)) { + const position = await SearchHandler.getUserLocation(geolocationOptions); + answersActions.setUserLocation({ + latitude: position.coords.latitude, + longitude: position.coords.longitude, + }) + } + SearchHandler.executeSearch(answersActions, true); } function renderSearchButton () { @@ -49,12 +68,12 @@ export default function SearchBar({ placeholder, isVertical, screenReaderInstruc placeholder={placeholder} screenReaderInstructions={SCREENREADER_INSTRUCTIONS} screenReaderInstructionsId={screenReaderInstructionsId} - options={autocompleteResults.map(result => { + options={autocompleteResponse?.results.map(result => { return { value: result.value, render: () => renderWithHighlighting(result) } - })} + }) ?? []} optionIdPrefix='Autocomplete__option' onSubmit={executeQuery} updateInputValue={value => { diff --git a/sample-app/src/hooks/useAutocomplete.tsx b/sample-app/src/hooks/useAutocomplete.tsx index 6c521bdc..37dfdf87 100644 --- a/sample-app/src/hooks/useAutocomplete.tsx +++ b/sample-app/src/hooks/useAutocomplete.tsx @@ -1,19 +1,27 @@ -import { useRef, useState } from "react"; -import { AutocompleteResult, useAnswersActions } from '@yext/answers-headless-react'; +import { MutableRefObject, useRef, useState } from "react"; +import { AutocompleteResponse, useAnswersActions } from '@yext/answers-headless-react'; -export function useAutocomplete(isVertical: boolean): [AutocompleteResult[], () => Promise] { +export function useAutocomplete( + isVertical: boolean +): [ AutocompleteResponse|undefined, MutableRefObject>, () => Promise ] { const answersActions = useAnswersActions(); const autocompleteNetworkIds = useRef({ latestRequest: 0, responseInState: 0 }); - const [ autocompleteResults, setAutocompleteResults ] = useState([]); + const [ autocompleteResponse, setAutocompleteResponse ] = useState(); + const latestAutocompleteResponseRef = useRef>(Promise.resolve()); async function executeAutocomplete () { const requestId = ++autocompleteNetworkIds.current.latestRequest; - const response = isVertical - ? await answersActions.executeVerticalAutocomplete() - : await answersActions.executeUniversalAutocomplete(); - if (requestId >= autocompleteNetworkIds.current.responseInState) { - setAutocompleteResults(response?.results || []); - autocompleteNetworkIds.current.responseInState = requestId; - } + latestAutocompleteResponseRef.current = new Promise(async (resolve) => { + const response = isVertical + ? await answersActions.executeVerticalAutocomplete() + : await answersActions.executeUniversalAutocomplete(); + if (requestId >= autocompleteNetworkIds.current.responseInState) { + setAutocompleteResponse(response); + autocompleteNetworkIds.current.responseInState = requestId; + if (requestId === autocompleteNetworkIds.current.latestRequest) { + resolve(); + } + } + }); } - return [ autocompleteResults, executeAutocomplete ] + return [ autocompleteResponse, latestAutocompleteResponseRef, executeAutocomplete ] }; \ No newline at end of file diff --git a/sample-app/src/pages/UniversalSearchPage.tsx b/sample-app/src/pages/UniversalSearchPage.tsx index 4755b1df..3e1b15a1 100644 --- a/sample-app/src/pages/UniversalSearchPage.tsx +++ b/sample-app/src/pages/UniversalSearchPage.tsx @@ -3,7 +3,8 @@ import { useLayoutEffect } from 'react'; import { useAnswersActions } from '@yext/answers-headless-react'; import '../sass/UniversalSearchPage.scss'; import { UniversalResultsConfig } from '../universalResultsConfig'; -import { executeSearchWithUserLocation } from '../utils/geolocationutils'; +import SearchHandler from '../utils/searchhandler'; +import { SearchIntent } from '@yext/answers-headless'; const universalResultsFilterConfig = { show: true @@ -18,7 +19,18 @@ export default function UniversalSearchPage(props: { universalResultsConfig: Uni vertical: {} }) answersActions.setVerticalKey(''); - executeSearchWithUserLocation(answersActions, false, {}, true); + const executeQuery = async () => { + const searchIntents = await SearchHandler.getSearchIntents(answersActions, false); + if (searchIntents?.includes(SearchIntent.NearMe)) { + const position = await SearchHandler.getUserLocation(); + answersActions.setUserLocation({ + latitude: position.coords.latitude, + longitude: position.coords.longitude, + }) + } + SearchHandler.executeSearch(answersActions, false); + }; + executeQuery(); }, [answersActions]); return ( diff --git a/sample-app/src/pages/VerticalSearchPage.tsx b/sample-app/src/pages/VerticalSearchPage.tsx index 759cd9cf..02ac16dd 100644 --- a/sample-app/src/pages/VerticalSearchPage.tsx +++ b/sample-app/src/pages/VerticalSearchPage.tsx @@ -10,7 +10,8 @@ import '../sass/VerticalSearchPage.scss'; import { StandardCard } from '../components/cards/StandardCard'; import { useLayoutEffect } from 'react'; import { useAnswersActions } from '@yext/answers-headless-react'; -import { executeSearchWithUserLocation } from '../utils/geolocationutils'; +import SearchHandler from '../utils/searchhandler'; +import { SearchIntent } from '@yext/answers-headless'; const countryFilterOptions = [ { @@ -69,7 +70,18 @@ export default function VerticalSearchPage(props: { universal: {} }); answersActions.setVerticalKey(props.verticalKey); - executeSearchWithUserLocation(answersActions, true, {}, true); + const executeQuery = async () => { + const searchIntents = await SearchHandler.getSearchIntents(answersActions, false); + if (searchIntents?.includes(SearchIntent.NearMe)) { + const position = await SearchHandler.getUserLocation(); + answersActions.setUserLocation({ + latitude: position.coords.latitude, + longitude: position.coords.longitude, + }) + } + SearchHandler.executeSearch(answersActions, true); + }; + executeQuery(); }, [answersActions, props.verticalKey]); return ( diff --git a/sample-app/src/utils/searchhandler.ts b/sample-app/src/utils/searchhandler.ts new file mode 100644 index 00000000..eb855045 --- /dev/null +++ b/sample-app/src/utils/searchhandler.ts @@ -0,0 +1,43 @@ +import { AnswersActions } from "../../../lib"; + + +const defaultGeolocationOptions: PositionOptions = { + enableHighAccuracy: false, + timeout: 6000, + maximumAge: 300000, +}; + +export default class SearchHandler { + static async executeSearch(answersActions: AnswersActions, isVertical: boolean) { + isVertical + ? answersActions.executeVerticalQuery() + : answersActions.executeUniversalQuery(); + return; + } + + static async getSearchIntents(answersActions: AnswersActions, isVertical: boolean) { + const results = isVertical + ? await answersActions.executeVerticalAutocomplete() + : await answersActions.executeUniversalAutocomplete(); + return results?.inputIntents; + } + + static async getUserLocation(geolocationOptions?: PositionOptions): Promise { + return new Promise((resolve, reject) => { + if ('geolocation' in navigator) { + navigator.geolocation.getCurrentPosition( + position => resolve(position), + err => { + console.log(err); + console.error('Unable to determine user\'s location.'); + reject(); + }, + Object.assign(defaultGeolocationOptions, geolocationOptions) + ); + } else { + console.error('Unable to determine user\'s location.'); + reject(); + } + }); + } +} \ No newline at end of file From b9e7a9fea229ddfd3d2834cc92f2a1d38befec28 Mon Sep 17 00:00:00 2001 From: Yen Truong Date: Thu, 18 Nov 2021 13:12:52 -0500 Subject: [PATCH 05/21] update latestResponseRef => responseToLatestRequestRef --- sample-app/src/components/SearchBar.tsx | 4 ++-- sample-app/src/hooks/useAutocomplete.tsx | 12 ++++++------ 2 files changed, 8 insertions(+), 8 deletions(-) diff --git a/sample-app/src/components/SearchBar.tsx b/sample-app/src/components/SearchBar.tsx index 1a38fcb7..d86ddb85 100644 --- a/sample-app/src/components/SearchBar.tsx +++ b/sample-app/src/components/SearchBar.tsx @@ -32,12 +32,12 @@ export default function SearchBar({ const isLoading = useAnswersState(state => state.searchStatus.isLoading); const [ autocompleteResponse, - latestAutocompleteResponseRef, + responseToLatestRequestRef, executeAutocomplete ] = useAutocomplete(isVertical); async function executeQuery () { - await latestAutocompleteResponseRef.current; + await responseToLatestRequestRef.current; if (autocompleteResponse?.inputIntents.includes(SearchIntent.NearMe)) { const position = await SearchHandler.getUserLocation(geolocationOptions); answersActions.setUserLocation({ diff --git a/sample-app/src/hooks/useAutocomplete.tsx b/sample-app/src/hooks/useAutocomplete.tsx index 37dfdf87..f8aa5200 100644 --- a/sample-app/src/hooks/useAutocomplete.tsx +++ b/sample-app/src/hooks/useAutocomplete.tsx @@ -7,21 +7,21 @@ export function useAutocomplete( const answersActions = useAnswersActions(); const autocompleteNetworkIds = useRef({ latestRequest: 0, responseInState: 0 }); const [ autocompleteResponse, setAutocompleteResponse ] = useState(); - const latestAutocompleteResponseRef = useRef>(Promise.resolve()); + const responseToLatestRequestRef = useRef>(Promise.resolve()); async function executeAutocomplete () { const requestId = ++autocompleteNetworkIds.current.latestRequest; - latestAutocompleteResponseRef.current = new Promise(async (resolve) => { + responseToLatestRequestRef.current = new Promise(async (resolve) => { const response = isVertical ? await answersActions.executeVerticalAutocomplete() : await answersActions.executeUniversalAutocomplete(); + if (requestId === autocompleteNetworkIds.current.latestRequest) { + resolve(); + } if (requestId >= autocompleteNetworkIds.current.responseInState) { setAutocompleteResponse(response); autocompleteNetworkIds.current.responseInState = requestId; - if (requestId === autocompleteNetworkIds.current.latestRequest) { - resolve(); - } } }); } - return [ autocompleteResponse, latestAutocompleteResponseRef, executeAutocomplete ] + return [ autocompleteResponse, responseToLatestRequestRef, executeAutocomplete ] }; \ No newline at end of file From b1d39cd6e4d78c71e7e4b284a57bafe2fec5f4fc Mon Sep 17 00:00:00 2001 From: Yen Truong Date: Thu, 18 Nov 2021 13:13:25 -0500 Subject: [PATCH 06/21] reordering --- sample-app/src/hooks/useAutocomplete.tsx | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/sample-app/src/hooks/useAutocomplete.tsx b/sample-app/src/hooks/useAutocomplete.tsx index f8aa5200..f41f03ed 100644 --- a/sample-app/src/hooks/useAutocomplete.tsx +++ b/sample-app/src/hooks/useAutocomplete.tsx @@ -14,13 +14,13 @@ export function useAutocomplete( const response = isVertical ? await answersActions.executeVerticalAutocomplete() : await answersActions.executeUniversalAutocomplete(); - if (requestId === autocompleteNetworkIds.current.latestRequest) { - resolve(); - } if (requestId >= autocompleteNetworkIds.current.responseInState) { setAutocompleteResponse(response); autocompleteNetworkIds.current.responseInState = requestId; } + if (requestId === autocompleteNetworkIds.current.latestRequest) { + resolve(); + } }); } return [ autocompleteResponse, responseToLatestRequestRef, executeAutocomplete ] From 5385506e1b075063922f5ed7cded12d47550e500 Mon Sep 17 00:00:00 2001 From: Yen Truong Date: Thu, 18 Nov 2021 13:24:05 -0500 Subject: [PATCH 07/21] doc --- sample-app/src/utils/geolocationutils.tsx | 56 ----------------------- sample-app/src/utils/searchhandler.ts | 6 +++ 2 files changed, 6 insertions(+), 56 deletions(-) delete mode 100644 sample-app/src/utils/geolocationutils.tsx diff --git a/sample-app/src/utils/geolocationutils.tsx b/sample-app/src/utils/geolocationutils.tsx deleted file mode 100644 index 13f51156..00000000 --- a/sample-app/src/utils/geolocationutils.tsx +++ /dev/null @@ -1,56 +0,0 @@ -import { AnswersActions } from "@yext/answers-headless-react"; -import { SearchIntent } from "@yext/answers-headless"; - -const defaultGeolocationOptions: PositionOptions = { - enableHighAccuracy: false, - timeout: 6000, - maximumAge: 300000, -}; - -/** - * Executes universal or vertical search with user location retrieved from nagivator.geolocation API. - * If useLocationOnNearMeOnly is true, will attempt to get user location only if the query's searchIntents - * (retrieved through autocomplete response) contains 'NEAR_ME'. If there's a failure in getting - * user location, a normal search is performed. - */ -export async function executeSearchWithUserLocation( - answersActions: AnswersActions, - isVertical: boolean, - geolocationOptions?: PositionOptions, - useLocationOnNearMeOnly?: boolean -) { - const executeSearch = () => { - isVertical - ? answersActions.executeVerticalQuery() - : answersActions.executeUniversalQuery(); - return; - } - - if (useLocationOnNearMeOnly) { - const results = isVertical - ? await answersActions.executeVerticalAutocomplete() - : await answersActions.executeUniversalAutocomplete(); - if (!results?.inputIntents?.includes(SearchIntent.NearMe)) { - executeSearch(); - return; - } - } - - if ('geolocation' in navigator) { - navigator.geolocation.getCurrentPosition((position: GeolocationPosition) => { - answersActions.setUserLocation({ - latitude: position.coords.latitude, - longitude: position.coords.longitude, - }) - }, - (err) => { - console.error(err); - console.error('Unable to determine user\'s location.'); - }, - Object.assign(defaultGeolocationOptions, geolocationOptions) - ); - } else { - console.warn('Unable to determine user\'s location.'); - } - executeSearch(); -} diff --git a/sample-app/src/utils/searchhandler.ts b/sample-app/src/utils/searchhandler.ts index eb855045..34209b9a 100644 --- a/sample-app/src/utils/searchhandler.ts +++ b/sample-app/src/utils/searchhandler.ts @@ -7,6 +7,12 @@ const defaultGeolocationOptions: PositionOptions = { maximumAge: 300000, }; +/** + * Provide utility functions related to vertical and universal search execution. + * This includes any potential data check or setup related to a query before + * performing a search, such as fetching searchIntents using autocomplete requests, + * and retrieving user's location using nagivator.geolocation API. + */ export default class SearchHandler { static async executeSearch(answersActions: AnswersActions, isVertical: boolean) { isVertical From f6b53ad11b9d419f2c6da5107920bff5ecd041bf Mon Sep 17 00:00:00 2001 From: Yen Truong Date: Thu, 18 Nov 2021 14:50:29 -0500 Subject: [PATCH 08/21] use response to latest request --- sample-app/src/components/SearchBar.tsx | 4 ++-- sample-app/src/hooks/useAutocomplete.tsx | 11 ++++++++--- 2 files changed, 10 insertions(+), 5 deletions(-) diff --git a/sample-app/src/components/SearchBar.tsx b/sample-app/src/components/SearchBar.tsx index d86ddb85..92c0fb82 100644 --- a/sample-app/src/components/SearchBar.tsx +++ b/sample-app/src/components/SearchBar.tsx @@ -37,8 +37,8 @@ export default function SearchBar({ ] = useAutocomplete(isVertical); async function executeQuery () { - await responseToLatestRequestRef.current; - if (autocompleteResponse?.inputIntents.includes(SearchIntent.NearMe)) { + const responseToLatestRequest = await responseToLatestRequestRef.current; + if (responseToLatestRequest?.inputIntents.includes(SearchIntent.NearMe)) { const position = await SearchHandler.getUserLocation(geolocationOptions); answersActions.setUserLocation({ latitude: position.coords.latitude, diff --git a/sample-app/src/hooks/useAutocomplete.tsx b/sample-app/src/hooks/useAutocomplete.tsx index f41f03ed..2d86e54c 100644 --- a/sample-app/src/hooks/useAutocomplete.tsx +++ b/sample-app/src/hooks/useAutocomplete.tsx @@ -3,11 +3,16 @@ import { AutocompleteResponse, useAnswersActions } from '@yext/answers-headless- export function useAutocomplete( isVertical: boolean -): [ AutocompleteResponse|undefined, MutableRefObject>, () => Promise ] { +): [ + AutocompleteResponse|undefined, + MutableRefObject>, + () => Promise + ] +{ const answersActions = useAnswersActions(); const autocompleteNetworkIds = useRef({ latestRequest: 0, responseInState: 0 }); const [ autocompleteResponse, setAutocompleteResponse ] = useState(); - const responseToLatestRequestRef = useRef>(Promise.resolve()); + const responseToLatestRequestRef = useRef>(Promise.resolve(undefined)); async function executeAutocomplete () { const requestId = ++autocompleteNetworkIds.current.latestRequest; responseToLatestRequestRef.current = new Promise(async (resolve) => { @@ -19,7 +24,7 @@ export function useAutocomplete( autocompleteNetworkIds.current.responseInState = requestId; } if (requestId === autocompleteNetworkIds.current.latestRequest) { - resolve(); + resolve(response); } }); } From 0b2a2e13b4ffb4db6b9b87b1793c060ed8778b6c Mon Sep 17 00:00:00 2001 From: Yen Truong Date: Thu, 18 Nov 2021 15:01:44 -0500 Subject: [PATCH 09/21] better error handling --- sample-app/src/components/LocationBias.tsx | 14 +++++++++----- sample-app/src/components/SearchBar.tsx | 14 +++++++++----- sample-app/src/pages/UniversalSearchPage.tsx | 14 +++++++++----- sample-app/src/pages/VerticalSearchPage.tsx | 14 +++++++++----- sample-app/src/utils/searchhandler.ts | 8 +++----- 5 files changed, 39 insertions(+), 25 deletions(-) diff --git a/sample-app/src/components/LocationBias.tsx b/sample-app/src/components/LocationBias.tsx index dde5e9a3..3118b212 100644 --- a/sample-app/src/components/LocationBias.tsx +++ b/sample-app/src/components/LocationBias.tsx @@ -34,11 +34,15 @@ export default function LocationBias(props: Props) { : ''; async function handleGeolocationClick() { - const position = await SearchHandler.getUserLocation(geolocationOptions); - answersActions.setUserLocation({ - latitude: position.coords.latitude, - longitude: position.coords.longitude, - }) + try { + const position = await SearchHandler.getUserLocation(geolocationOptions); + answersActions.setUserLocation({ + latitude: position.coords.latitude, + longitude: position.coords.longitude, + }); + } catch (e) { + console.error(e); + } SearchHandler.executeSearch(answersActions, isVertical); } diff --git a/sample-app/src/components/SearchBar.tsx b/sample-app/src/components/SearchBar.tsx index 92c0fb82..c5341613 100644 --- a/sample-app/src/components/SearchBar.tsx +++ b/sample-app/src/components/SearchBar.tsx @@ -39,11 +39,15 @@ export default function SearchBar({ async function executeQuery () { const responseToLatestRequest = await responseToLatestRequestRef.current; if (responseToLatestRequest?.inputIntents.includes(SearchIntent.NearMe)) { - const position = await SearchHandler.getUserLocation(geolocationOptions); - answersActions.setUserLocation({ - latitude: position.coords.latitude, - longitude: position.coords.longitude, - }) + try { + const position = await SearchHandler.getUserLocation(geolocationOptions); + answersActions.setUserLocation({ + latitude: position.coords.latitude, + longitude: position.coords.longitude, + }); + } catch(e) { + console.error(e); + } } SearchHandler.executeSearch(answersActions, true); } diff --git a/sample-app/src/pages/UniversalSearchPage.tsx b/sample-app/src/pages/UniversalSearchPage.tsx index 3e1b15a1..f1af6c17 100644 --- a/sample-app/src/pages/UniversalSearchPage.tsx +++ b/sample-app/src/pages/UniversalSearchPage.tsx @@ -22,11 +22,15 @@ export default function UniversalSearchPage(props: { universalResultsConfig: Uni const executeQuery = async () => { const searchIntents = await SearchHandler.getSearchIntents(answersActions, false); if (searchIntents?.includes(SearchIntent.NearMe)) { - const position = await SearchHandler.getUserLocation(); - answersActions.setUserLocation({ - latitude: position.coords.latitude, - longitude: position.coords.longitude, - }) + try { + const position = await SearchHandler.getUserLocation(); + answersActions.setUserLocation({ + latitude: position.coords.latitude, + longitude: position.coords.longitude, + }); + } catch (e) { + console.error(e); + } } SearchHandler.executeSearch(answersActions, false); }; diff --git a/sample-app/src/pages/VerticalSearchPage.tsx b/sample-app/src/pages/VerticalSearchPage.tsx index 02ac16dd..7bb28e3d 100644 --- a/sample-app/src/pages/VerticalSearchPage.tsx +++ b/sample-app/src/pages/VerticalSearchPage.tsx @@ -73,11 +73,15 @@ export default function VerticalSearchPage(props: { const executeQuery = async () => { const searchIntents = await SearchHandler.getSearchIntents(answersActions, false); if (searchIntents?.includes(SearchIntent.NearMe)) { - const position = await SearchHandler.getUserLocation(); - answersActions.setUserLocation({ - latitude: position.coords.latitude, - longitude: position.coords.longitude, - }) + try { + const position = await SearchHandler.getUserLocation(); + answersActions.setUserLocation({ + latitude: position.coords.latitude, + longitude: position.coords.longitude, + }); + } catch (e) { + console.error(e); + } } SearchHandler.executeSearch(answersActions, true); }; diff --git a/sample-app/src/utils/searchhandler.ts b/sample-app/src/utils/searchhandler.ts index 34209b9a..6bade492 100644 --- a/sample-app/src/utils/searchhandler.ts +++ b/sample-app/src/utils/searchhandler.ts @@ -34,15 +34,13 @@ export default class SearchHandler { navigator.geolocation.getCurrentPosition( position => resolve(position), err => { - console.log(err); - console.error('Unable to determine user\'s location.'); - reject(); + console.error('Error occured using geolocation API. Unable to determine user\'s location.'); + reject(err); }, Object.assign(defaultGeolocationOptions, geolocationOptions) ); } else { - console.error('Unable to determine user\'s location.'); - reject(); + reject('No access to geolocation API. Unable to determine user\'s location.'); } }); } From 965419c6794f4243a52be12a5c9bd5b1758dcc6f Mon Sep 17 00:00:00 2001 From: Yen Truong Date: Thu, 18 Nov 2021 15:18:06 -0500 Subject: [PATCH 10/21] use isVertical in searchbar --- sample-app/src/components/SearchBar.tsx | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/sample-app/src/components/SearchBar.tsx b/sample-app/src/components/SearchBar.tsx index c5341613..ce1feae5 100644 --- a/sample-app/src/components/SearchBar.tsx +++ b/sample-app/src/components/SearchBar.tsx @@ -49,7 +49,7 @@ export default function SearchBar({ console.error(e); } } - SearchHandler.executeSearch(answersActions, true); + SearchHandler.executeSearch(answersActions, isVertical); } function renderSearchButton () { From 53a286d13ec85e8919c35dbab48fc81d4601420a Mon Sep 17 00:00:00 2001 From: Yen Truong Date: Thu, 18 Nov 2021 15:28:50 -0500 Subject: [PATCH 11/21] always resolve after response returned --- sample-app/src/utils/searchhandler.ts | 2 ++ 1 file changed, 2 insertions(+) diff --git a/sample-app/src/utils/searchhandler.ts b/sample-app/src/utils/searchhandler.ts index 6bade492..b7cef69e 100644 --- a/sample-app/src/utils/searchhandler.ts +++ b/sample-app/src/utils/searchhandler.ts @@ -15,6 +15,8 @@ const defaultGeolocationOptions: PositionOptions = { */ export default class SearchHandler { static async executeSearch(answersActions: AnswersActions, isVertical: boolean) { + console.log('executing search..'); + console.log(isVertical); isVertical ? answersActions.executeVerticalQuery() : answersActions.executeUniversalQuery(); From d982c28ddba385a12595fa9fb96b3d7787d767d4 Mon Sep 17 00:00:00 2001 From: Yen Truong Date: Thu, 18 Nov 2021 15:30:12 -0500 Subject: [PATCH 12/21] remove console log --- sample-app/src/hooks/useAutocomplete.tsx | 4 +--- sample-app/src/utils/searchhandler.ts | 2 -- 2 files changed, 1 insertion(+), 5 deletions(-) diff --git a/sample-app/src/hooks/useAutocomplete.tsx b/sample-app/src/hooks/useAutocomplete.tsx index 2d86e54c..66a5b9fa 100644 --- a/sample-app/src/hooks/useAutocomplete.tsx +++ b/sample-app/src/hooks/useAutocomplete.tsx @@ -23,9 +23,7 @@ export function useAutocomplete( setAutocompleteResponse(response); autocompleteNetworkIds.current.responseInState = requestId; } - if (requestId === autocompleteNetworkIds.current.latestRequest) { - resolve(response); - } + resolve(response); }); } return [ autocompleteResponse, responseToLatestRequestRef, executeAutocomplete ] diff --git a/sample-app/src/utils/searchhandler.ts b/sample-app/src/utils/searchhandler.ts index b7cef69e..6bade492 100644 --- a/sample-app/src/utils/searchhandler.ts +++ b/sample-app/src/utils/searchhandler.ts @@ -15,8 +15,6 @@ const defaultGeolocationOptions: PositionOptions = { */ export default class SearchHandler { static async executeSearch(answersActions: AnswersActions, isVertical: boolean) { - console.log('executing search..'); - console.log(isVertical); isVertical ? answersActions.executeVerticalQuery() : answersActions.executeUniversalQuery(); From 50fbef332cb7c32d04a98f82a2124a0cd2bb2f05 Mon Sep 17 00:00:00 2001 From: Yen Truong Date: Fri, 19 Nov 2021 11:44:37 -0500 Subject: [PATCH 13/21] feedback --- sample-app/src/components/SearchBar.tsx | 26 ++++++-------------- sample-app/src/hooks/useAutocomplete.tsx | 12 ++++----- sample-app/src/pages/UniversalSearchPage.tsx | 14 +---------- sample-app/src/pages/VerticalSearchPage.tsx | 14 +---------- sample-app/src/utils/searchhandler.ts | 23 ++++++++++++++++- 5 files changed, 36 insertions(+), 53 deletions(-) diff --git a/sample-app/src/components/SearchBar.tsx b/sample-app/src/components/SearchBar.tsx index ce1feae5..cf94c592 100644 --- a/sample-app/src/components/SearchBar.tsx +++ b/sample-app/src/components/SearchBar.tsx @@ -7,7 +7,8 @@ import '../sass/Autocomplete.scss'; import LoadingIndicator from './LoadingIndicator'; import { useAutocomplete } from '../hooks/useAutocomplete'; import SearchHandler from '../utils/searchhandler'; -import { SearchIntent } from '@yext/answers-headless'; +import { useRef } from 'react'; +import { AutocompleteResponse } from '@yext/answers-headless'; const SCREENREADER_INSTRUCTIONS = 'When autocomplete results are available, use up and down arrows to review and enter to select.' @@ -30,26 +31,13 @@ export default function SearchBar({ const answersActions = useAnswersActions(); const query = useAnswersState(state => state.query.input); const isLoading = useAnswersState(state => state.searchStatus.isLoading); - const [ - autocompleteResponse, - responseToLatestRequestRef, - executeAutocomplete - ] = useAutocomplete(isVertical); + const responseToLatestRequestRef = useRef>(Promise.resolve(undefined)); + const [ autocompleteResponse, executeAutocomplete] = useAutocomplete(isVertical); async function executeQuery () { const responseToLatestRequest = await responseToLatestRequestRef.current; - if (responseToLatestRequest?.inputIntents.includes(SearchIntent.NearMe)) { - try { - const position = await SearchHandler.getUserLocation(geolocationOptions); - answersActions.setUserLocation({ - latitude: position.coords.latitude, - longitude: position.coords.longitude, - }); - } catch(e) { - console.error(e); - } - } - SearchHandler.executeSearch(answersActions, isVertical); + const intents = responseToLatestRequest?.inputIntents || []; + SearchHandler.executeSearchWithIntents(answersActions, isVertical, intents, geolocationOptions); } function renderSearchButton () { @@ -84,7 +72,7 @@ export default function SearchBar({ answersActions.setQuery(value); }} updateDropdown={() => { - executeAutocomplete(); + responseToLatestRequestRef.current = executeAutocomplete(); }} renderButtons={renderSearchButton} cssClasses={{ diff --git a/sample-app/src/hooks/useAutocomplete.tsx b/sample-app/src/hooks/useAutocomplete.tsx index 66a5b9fa..086b674e 100644 --- a/sample-app/src/hooks/useAutocomplete.tsx +++ b/sample-app/src/hooks/useAutocomplete.tsx @@ -1,21 +1,19 @@ -import { MutableRefObject, useRef, useState } from "react"; +import { useRef, useState } from "react"; import { AutocompleteResponse, useAnswersActions } from '@yext/answers-headless-react'; export function useAutocomplete( isVertical: boolean ): [ AutocompleteResponse|undefined, - MutableRefObject>, - () => Promise + () => Promise ] { const answersActions = useAnswersActions(); const autocompleteNetworkIds = useRef({ latestRequest: 0, responseInState: 0 }); const [ autocompleteResponse, setAutocompleteResponse ] = useState(); - const responseToLatestRequestRef = useRef>(Promise.resolve(undefined)); - async function executeAutocomplete () { + async function executeAutocomplete (): Promise { const requestId = ++autocompleteNetworkIds.current.latestRequest; - responseToLatestRequestRef.current = new Promise(async (resolve) => { + return new Promise(async (resolve) => { const response = isVertical ? await answersActions.executeVerticalAutocomplete() : await answersActions.executeUniversalAutocomplete(); @@ -26,5 +24,5 @@ export function useAutocomplete( resolve(response); }); } - return [ autocompleteResponse, responseToLatestRequestRef, executeAutocomplete ] + return [ autocompleteResponse, executeAutocomplete ] }; \ No newline at end of file diff --git a/sample-app/src/pages/UniversalSearchPage.tsx b/sample-app/src/pages/UniversalSearchPage.tsx index f1af6c17..4bf04d08 100644 --- a/sample-app/src/pages/UniversalSearchPage.tsx +++ b/sample-app/src/pages/UniversalSearchPage.tsx @@ -4,7 +4,6 @@ import { useAnswersActions } from '@yext/answers-headless-react'; import '../sass/UniversalSearchPage.scss'; import { UniversalResultsConfig } from '../universalResultsConfig'; import SearchHandler from '../utils/searchhandler'; -import { SearchIntent } from '@yext/answers-headless'; const universalResultsFilterConfig = { show: true @@ -21,18 +20,7 @@ export default function UniversalSearchPage(props: { universalResultsConfig: Uni answersActions.setVerticalKey(''); const executeQuery = async () => { const searchIntents = await SearchHandler.getSearchIntents(answersActions, false); - if (searchIntents?.includes(SearchIntent.NearMe)) { - try { - const position = await SearchHandler.getUserLocation(); - answersActions.setUserLocation({ - latitude: position.coords.latitude, - longitude: position.coords.longitude, - }); - } catch (e) { - console.error(e); - } - } - SearchHandler.executeSearch(answersActions, false); + SearchHandler.executeSearchWithIntents(answersActions, false, searchIntents || []); }; executeQuery(); }, [answersActions]); diff --git a/sample-app/src/pages/VerticalSearchPage.tsx b/sample-app/src/pages/VerticalSearchPage.tsx index 7bb28e3d..69ee9c36 100644 --- a/sample-app/src/pages/VerticalSearchPage.tsx +++ b/sample-app/src/pages/VerticalSearchPage.tsx @@ -11,7 +11,6 @@ import { StandardCard } from '../components/cards/StandardCard'; import { useLayoutEffect } from 'react'; import { useAnswersActions } from '@yext/answers-headless-react'; import SearchHandler from '../utils/searchhandler'; -import { SearchIntent } from '@yext/answers-headless'; const countryFilterOptions = [ { @@ -72,18 +71,7 @@ export default function VerticalSearchPage(props: { answersActions.setVerticalKey(props.verticalKey); const executeQuery = async () => { const searchIntents = await SearchHandler.getSearchIntents(answersActions, false); - if (searchIntents?.includes(SearchIntent.NearMe)) { - try { - const position = await SearchHandler.getUserLocation(); - answersActions.setUserLocation({ - latitude: position.coords.latitude, - longitude: position.coords.longitude, - }); - } catch (e) { - console.error(e); - } - } - SearchHandler.executeSearch(answersActions, true); + SearchHandler.executeSearchWithIntents(answersActions, true, searchIntents || []); }; executeQuery(); }, [answersActions, props.verticalKey]); diff --git a/sample-app/src/utils/searchhandler.ts b/sample-app/src/utils/searchhandler.ts index 6bade492..451d127a 100644 --- a/sample-app/src/utils/searchhandler.ts +++ b/sample-app/src/utils/searchhandler.ts @@ -1,4 +1,5 @@ -import { AnswersActions } from "../../../lib"; +import { AnswersActions } from "@yext/answers-headless-react"; +import { SearchIntent } from "@yext/answers-headless"; const defaultGeolocationOptions: PositionOptions = { @@ -21,6 +22,26 @@ export default class SearchHandler { return; } + static async executeSearchWithIntents( + answersActions: AnswersActions, + isVertical: boolean, + intents: SearchIntent[], + geolocationOptions?: PositionOptions + ) { + if (intents.includes(SearchIntent.NearMe)) { + try { + const position = await SearchHandler.getUserLocation(geolocationOptions); + answersActions.setUserLocation({ + latitude: position.coords.latitude, + longitude: position.coords.longitude, + }); + } catch(e) { + console.error(e); + } + } + SearchHandler.executeSearch(answersActions, isVertical); + } + static async getSearchIntents(answersActions: AnswersActions, isVertical: boolean) { const results = isVertical ? await answersActions.executeVerticalAutocomplete() From c04ba7463d09a212ad28f2faf7954f043402416e Mon Sep 17 00:00:00 2001 From: Yen Truong Date: Fri, 19 Nov 2021 12:23:32 -0500 Subject: [PATCH 14/21] remove default promise value --- sample-app/src/components/SearchBar.tsx | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/sample-app/src/components/SearchBar.tsx b/sample-app/src/components/SearchBar.tsx index cf94c592..8b502ef7 100644 --- a/sample-app/src/components/SearchBar.tsx +++ b/sample-app/src/components/SearchBar.tsx @@ -31,7 +31,7 @@ export default function SearchBar({ const answersActions = useAnswersActions(); const query = useAnswersState(state => state.query.input); const isLoading = useAnswersState(state => state.searchStatus.isLoading); - const responseToLatestRequestRef = useRef>(Promise.resolve(undefined)); + const responseToLatestRequestRef = useRef>(); const [ autocompleteResponse, executeAutocomplete] = useAutocomplete(isVertical); async function executeQuery () { From 6e9f573d5513074e7f48e41d2e95626b81b0c758 Mon Sep 17 00:00:00 2001 From: Yen Truong Date: Fri, 19 Nov 2021 12:55:14 -0500 Subject: [PATCH 15/21] class => functions --- sample-app/src/components/LocationBias.tsx | 6 +- sample-app/src/components/SearchBar.tsx | 4 +- sample-app/src/pages/UniversalSearchPage.tsx | 6 +- sample-app/src/pages/VerticalSearchPage.tsx | 6 +- sample-app/src/utils/search-operations.ts | 73 ++++++++++++++++++++ sample-app/src/utils/searchhandler.ts | 68 ------------------ 6 files changed, 84 insertions(+), 79 deletions(-) create mode 100644 sample-app/src/utils/search-operations.ts delete mode 100644 sample-app/src/utils/searchhandler.ts diff --git a/sample-app/src/components/LocationBias.tsx b/sample-app/src/components/LocationBias.tsx index 3118b212..0200dae1 100644 --- a/sample-app/src/components/LocationBias.tsx +++ b/sample-app/src/components/LocationBias.tsx @@ -1,5 +1,5 @@ import { useAnswersActions, useAnswersState, LocationBiasMethod } from '@yext/answers-headless-react'; -import SearchHandler from '../utils/searchhandler'; +import { executeSearch, getUserLocation } from '../utils/search-operations'; interface Props { isVertical: boolean, @@ -35,7 +35,7 @@ export default function LocationBias(props: Props) { async function handleGeolocationClick() { try { - const position = await SearchHandler.getUserLocation(geolocationOptions); + const position = await getUserLocation(geolocationOptions); answersActions.setUserLocation({ latitude: position.coords.latitude, longitude: position.coords.longitude, @@ -43,7 +43,7 @@ export default function LocationBias(props: Props) { } catch (e) { console.error(e); } - SearchHandler.executeSearch(answersActions, isVertical); + executeSearch(answersActions, isVertical); } return ( diff --git a/sample-app/src/components/SearchBar.tsx b/sample-app/src/components/SearchBar.tsx index 8b502ef7..9b1c28f8 100644 --- a/sample-app/src/components/SearchBar.tsx +++ b/sample-app/src/components/SearchBar.tsx @@ -6,9 +6,9 @@ import '../sass/SearchBar.scss'; import '../sass/Autocomplete.scss'; import LoadingIndicator from './LoadingIndicator'; import { useAutocomplete } from '../hooks/useAutocomplete'; -import SearchHandler from '../utils/searchhandler'; import { useRef } from 'react'; import { AutocompleteResponse } from '@yext/answers-headless'; +import { executeSearchWithIntents } from '../utils/search-operations'; const SCREENREADER_INSTRUCTIONS = 'When autocomplete results are available, use up and down arrows to review and enter to select.' @@ -37,7 +37,7 @@ export default function SearchBar({ async function executeQuery () { const responseToLatestRequest = await responseToLatestRequestRef.current; const intents = responseToLatestRequest?.inputIntents || []; - SearchHandler.executeSearchWithIntents(answersActions, isVertical, intents, geolocationOptions); + executeSearchWithIntents(answersActions, isVertical, intents, geolocationOptions); } function renderSearchButton () { diff --git a/sample-app/src/pages/UniversalSearchPage.tsx b/sample-app/src/pages/UniversalSearchPage.tsx index 4bf04d08..abe68a90 100644 --- a/sample-app/src/pages/UniversalSearchPage.tsx +++ b/sample-app/src/pages/UniversalSearchPage.tsx @@ -3,7 +3,7 @@ import { useLayoutEffect } from 'react'; import { useAnswersActions } from '@yext/answers-headless-react'; import '../sass/UniversalSearchPage.scss'; import { UniversalResultsConfig } from '../universalResultsConfig'; -import SearchHandler from '../utils/searchhandler'; +import { executeSearchWithIntents, getSearchIntents } from '../utils/search-operations'; const universalResultsFilterConfig = { show: true @@ -19,8 +19,8 @@ export default function UniversalSearchPage(props: { universalResultsConfig: Uni }) answersActions.setVerticalKey(''); const executeQuery = async () => { - const searchIntents = await SearchHandler.getSearchIntents(answersActions, false); - SearchHandler.executeSearchWithIntents(answersActions, false, searchIntents || []); + const searchIntents = await getSearchIntents(answersActions, false); + executeSearchWithIntents(answersActions, false, searchIntents || []); }; executeQuery(); }, [answersActions]); diff --git a/sample-app/src/pages/VerticalSearchPage.tsx b/sample-app/src/pages/VerticalSearchPage.tsx index 69ee9c36..3b099fab 100644 --- a/sample-app/src/pages/VerticalSearchPage.tsx +++ b/sample-app/src/pages/VerticalSearchPage.tsx @@ -10,7 +10,7 @@ import '../sass/VerticalSearchPage.scss'; import { StandardCard } from '../components/cards/StandardCard'; import { useLayoutEffect } from 'react'; import { useAnswersActions } from '@yext/answers-headless-react'; -import SearchHandler from '../utils/searchhandler'; +import { executeSearchWithIntents, getSearchIntents } from '../utils/search-operations'; const countryFilterOptions = [ { @@ -70,8 +70,8 @@ export default function VerticalSearchPage(props: { }); answersActions.setVerticalKey(props.verticalKey); const executeQuery = async () => { - const searchIntents = await SearchHandler.getSearchIntents(answersActions, false); - SearchHandler.executeSearchWithIntents(answersActions, true, searchIntents || []); + const searchIntents = await getSearchIntents(answersActions, false); + executeSearchWithIntents(answersActions, true, searchIntents || []); }; executeQuery(); }, [answersActions, props.verticalKey]); diff --git a/sample-app/src/utils/search-operations.ts b/sample-app/src/utils/search-operations.ts new file mode 100644 index 00000000..b27c648f --- /dev/null +++ b/sample-app/src/utils/search-operations.ts @@ -0,0 +1,73 @@ +import { AnswersActions } from "@yext/answers-headless-react"; +import { SearchIntent } from "@yext/answers-headless"; + +const defaultGeolocationOptions: PositionOptions = { + enableHighAccuracy: false, + timeout: 6000, + maximumAge: 300000, +}; + +/** + * If the provided search intents include a 'NEAR_ME' intent, retrieve and + * store user's location in headless state. Then, execute a search with + * user's location, if that's successfully retrieved, attached to the request. + */ +export async function executeSearchWithIntents( + answersActions: AnswersActions, + isVertical: boolean, + intents: SearchIntent[], + geolocationOptions?: PositionOptions +) { + if (intents.includes(SearchIntent.NearMe)) { + try { + const position = await getUserLocation(geolocationOptions); + answersActions.setUserLocation({ + latitude: position.coords.latitude, + longitude: position.coords.longitude, + }); + } catch(e) { + console.error(e); + } + } + executeSearch(answersActions, isVertical); +} + +/** + * Executes a universal/vertical search + */ +export async function executeSearch(answersActions: AnswersActions, isVertical: boolean) { + isVertical + ? answersActions.executeVerticalQuery() + : answersActions.executeUniversalQuery(); + return; +} + +/** + * Get search intents of the current query stored in headless using autocomplete request. + */ +export async function getSearchIntents(answersActions: AnswersActions, isVertical: boolean) { + const results = isVertical + ? await answersActions.executeVerticalAutocomplete() + : await answersActions.executeUniversalAutocomplete(); + return results?.inputIntents; +} + +/** + * Retrieves user's location using nagivator.geolocation API + */ +export async function getUserLocation(geolocationOptions?: PositionOptions): Promise { + return new Promise((resolve, reject) => { + if ('geolocation' in navigator) { + navigator.geolocation.getCurrentPosition( + position => resolve(position), + err => { + console.error('Error occured using geolocation API. Unable to determine user\'s location.'); + reject(err); + }, + Object.assign(defaultGeolocationOptions, geolocationOptions) + ); + } else { + reject('No access to geolocation API. Unable to determine user\'s location.'); + } + }); +} diff --git a/sample-app/src/utils/searchhandler.ts b/sample-app/src/utils/searchhandler.ts deleted file mode 100644 index 451d127a..00000000 --- a/sample-app/src/utils/searchhandler.ts +++ /dev/null @@ -1,68 +0,0 @@ -import { AnswersActions } from "@yext/answers-headless-react"; -import { SearchIntent } from "@yext/answers-headless"; - - -const defaultGeolocationOptions: PositionOptions = { - enableHighAccuracy: false, - timeout: 6000, - maximumAge: 300000, -}; - -/** - * Provide utility functions related to vertical and universal search execution. - * This includes any potential data check or setup related to a query before - * performing a search, such as fetching searchIntents using autocomplete requests, - * and retrieving user's location using nagivator.geolocation API. - */ -export default class SearchHandler { - static async executeSearch(answersActions: AnswersActions, isVertical: boolean) { - isVertical - ? answersActions.executeVerticalQuery() - : answersActions.executeUniversalQuery(); - return; - } - - static async executeSearchWithIntents( - answersActions: AnswersActions, - isVertical: boolean, - intents: SearchIntent[], - geolocationOptions?: PositionOptions - ) { - if (intents.includes(SearchIntent.NearMe)) { - try { - const position = await SearchHandler.getUserLocation(geolocationOptions); - answersActions.setUserLocation({ - latitude: position.coords.latitude, - longitude: position.coords.longitude, - }); - } catch(e) { - console.error(e); - } - } - SearchHandler.executeSearch(answersActions, isVertical); - } - - static async getSearchIntents(answersActions: AnswersActions, isVertical: boolean) { - const results = isVertical - ? await answersActions.executeVerticalAutocomplete() - : await answersActions.executeUniversalAutocomplete(); - return results?.inputIntents; - } - - static async getUserLocation(geolocationOptions?: PositionOptions): Promise { - return new Promise((resolve, reject) => { - if ('geolocation' in navigator) { - navigator.geolocation.getCurrentPosition( - position => resolve(position), - err => { - console.error('Error occured using geolocation API. Unable to determine user\'s location.'); - reject(err); - }, - Object.assign(defaultGeolocationOptions, geolocationOptions) - ); - } else { - reject('No access to geolocation API. Unable to determine user\'s location.'); - } - }); - } -} \ No newline at end of file From 514988585cba295881497bce6752abc73534cc8c Mon Sep 17 00:00:00 2001 From: Yen Truong Date: Fri, 19 Nov 2021 14:33:56 -0500 Subject: [PATCH 16/21] use spread operator instead of assign --- sample-app/src/utils/search-operations.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/sample-app/src/utils/search-operations.ts b/sample-app/src/utils/search-operations.ts index b27c648f..fb88ed54 100644 --- a/sample-app/src/utils/search-operations.ts +++ b/sample-app/src/utils/search-operations.ts @@ -64,7 +64,7 @@ export async function getUserLocation(geolocationOptions?: PositionOptions): Pro console.error('Error occured using geolocation API. Unable to determine user\'s location.'); reject(err); }, - Object.assign(defaultGeolocationOptions, geolocationOptions) + { ...defaultGeolocationOptions, ...geolocationOptions } ); } else { reject('No access to geolocation API. Unable to determine user\'s location.'); From f6c937a57372b37a14cc3dcae33c9ed8a097e171 Mon Sep 17 00:00:00 2001 From: Yen Truong Date: Mon, 22 Nov 2021 12:58:24 -0500 Subject: [PATCH 17/21] comments, check userLocation in state first --- sample-app/src/components/SearchBar.tsx | 4 ++++ sample-app/src/utils/search-operations.ts | 2 +- 2 files changed, 5 insertions(+), 1 deletion(-) diff --git a/sample-app/src/components/SearchBar.tsx b/sample-app/src/components/SearchBar.tsx index 9b1c28f8..3d6dce51 100644 --- a/sample-app/src/components/SearchBar.tsx +++ b/sample-app/src/components/SearchBar.tsx @@ -31,6 +31,10 @@ export default function SearchBar({ const answersActions = useAnswersActions(); const query = useAnswersState(state => state.query.input); const isLoading = useAnswersState(state => state.searchStatus.isLoading); + /** + * Allow a query search to wait on the response to the autocomplete request right + * before the search execution in order to retrieve the search intents + */ const responseToLatestRequestRef = useRef>(); const [ autocompleteResponse, executeAutocomplete] = useAutocomplete(isVertical); diff --git a/sample-app/src/utils/search-operations.ts b/sample-app/src/utils/search-operations.ts index fb88ed54..35b2641d 100644 --- a/sample-app/src/utils/search-operations.ts +++ b/sample-app/src/utils/search-operations.ts @@ -18,7 +18,7 @@ export async function executeSearchWithIntents( intents: SearchIntent[], geolocationOptions?: PositionOptions ) { - if (intents.includes(SearchIntent.NearMe)) { + if (intents.includes(SearchIntent.NearMe) && !answersActions.state.location.userLocation) { try { const position = await getUserLocation(geolocationOptions); answersActions.setUserLocation({ From 8b9fc4c14517d7d54e56df47206a2dfc15672474 Mon Sep 17 00:00:00 2001 From: Yen Truong Date: Mon, 22 Nov 2021 13:05:27 -0500 Subject: [PATCH 18/21] use userLocation in state first --- sample-app/src/components/SearchBar.tsx | 12 ++++++++---- sample-app/src/pages/UniversalSearchPage.tsx | 8 ++++++-- sample-app/src/pages/VerticalSearchPage.tsx | 8 ++++++-- 3 files changed, 20 insertions(+), 8 deletions(-) diff --git a/sample-app/src/components/SearchBar.tsx b/sample-app/src/components/SearchBar.tsx index 3d6dce51..170f8a8a 100644 --- a/sample-app/src/components/SearchBar.tsx +++ b/sample-app/src/components/SearchBar.tsx @@ -8,7 +8,7 @@ import LoadingIndicator from './LoadingIndicator'; import { useAutocomplete } from '../hooks/useAutocomplete'; import { useRef } from 'react'; import { AutocompleteResponse } from '@yext/answers-headless'; -import { executeSearchWithIntents } from '../utils/search-operations'; +import { executeSearch, executeSearchWithIntents } from '../utils/search-operations'; const SCREENREADER_INSTRUCTIONS = 'When autocomplete results are available, use up and down arrows to review and enter to select.' @@ -39,9 +39,13 @@ export default function SearchBar({ const [ autocompleteResponse, executeAutocomplete] = useAutocomplete(isVertical); async function executeQuery () { - const responseToLatestRequest = await responseToLatestRequestRef.current; - const intents = responseToLatestRequest?.inputIntents || []; - executeSearchWithIntents(answersActions, isVertical, intents, geolocationOptions); + if (answersActions.state.location.userLocation) { + executeSearch(answersActions, isVertical); + } else { + const responseToLatestRequest = await responseToLatestRequestRef.current; + const intents = responseToLatestRequest?.inputIntents || []; + executeSearchWithIntents(answersActions, isVertical, intents, geolocationOptions); + } } function renderSearchButton () { diff --git a/sample-app/src/pages/UniversalSearchPage.tsx b/sample-app/src/pages/UniversalSearchPage.tsx index abe68a90..09328e61 100644 --- a/sample-app/src/pages/UniversalSearchPage.tsx +++ b/sample-app/src/pages/UniversalSearchPage.tsx @@ -19,8 +19,12 @@ export default function UniversalSearchPage(props: { universalResultsConfig: Uni }) answersActions.setVerticalKey(''); const executeQuery = async () => { - const searchIntents = await getSearchIntents(answersActions, false); - executeSearchWithIntents(answersActions, false, searchIntents || []); + if(answersActions.state.location.userLocation) { + answersActions.executeUniversalQuery(); + } else { + const searchIntents = await getSearchIntents(answersActions, false); + executeSearchWithIntents(answersActions, false, searchIntents || []); + } }; executeQuery(); }, [answersActions]); diff --git a/sample-app/src/pages/VerticalSearchPage.tsx b/sample-app/src/pages/VerticalSearchPage.tsx index 3b099fab..9eabb495 100644 --- a/sample-app/src/pages/VerticalSearchPage.tsx +++ b/sample-app/src/pages/VerticalSearchPage.tsx @@ -70,8 +70,12 @@ export default function VerticalSearchPage(props: { }); answersActions.setVerticalKey(props.verticalKey); const executeQuery = async () => { - const searchIntents = await getSearchIntents(answersActions, false); - executeSearchWithIntents(answersActions, true, searchIntents || []); + if(answersActions.state.location.userLocation) { + answersActions.executeVerticalQuery(); + } else { + const searchIntents = await getSearchIntents(answersActions, true); + executeSearchWithIntents(answersActions, true, searchIntents || []); + } }; executeQuery(); }, [answersActions, props.verticalKey]); From 7198c9f46bd84eb562c52504166a50b299fb4d3f Mon Sep 17 00:00:00 2001 From: Yen Truong Date: Mon, 22 Nov 2021 13:29:04 -0500 Subject: [PATCH 19/21] refactor --- sample-app/src/components/SearchBar.tsx | 13 ++++++------- sample-app/src/pages/UniversalSearchPage.tsx | 10 +++++----- sample-app/src/pages/VerticalSearchPage.tsx | 10 +++++----- 3 files changed, 16 insertions(+), 17 deletions(-) diff --git a/sample-app/src/components/SearchBar.tsx b/sample-app/src/components/SearchBar.tsx index 170f8a8a..026c7c5a 100644 --- a/sample-app/src/components/SearchBar.tsx +++ b/sample-app/src/components/SearchBar.tsx @@ -7,8 +7,8 @@ import '../sass/Autocomplete.scss'; import LoadingIndicator from './LoadingIndicator'; import { useAutocomplete } from '../hooks/useAutocomplete'; import { useRef } from 'react'; -import { AutocompleteResponse } from '@yext/answers-headless'; -import { executeSearch, executeSearchWithIntents } from '../utils/search-operations'; +import { AutocompleteResponse, SearchIntent } from '@yext/answers-headless'; +import { executeSearchWithIntents } from '../utils/search-operations'; const SCREENREADER_INSTRUCTIONS = 'When autocomplete results are available, use up and down arrows to review and enter to select.' @@ -39,13 +39,12 @@ export default function SearchBar({ const [ autocompleteResponse, executeAutocomplete] = useAutocomplete(isVertical); async function executeQuery () { - if (answersActions.state.location.userLocation) { - executeSearch(answersActions, isVertical); - } else { + let intents: SearchIntent[] = []; + if (!answersActions.state.location.userLocation) { const responseToLatestRequest = await responseToLatestRequestRef.current; - const intents = responseToLatestRequest?.inputIntents || []; - executeSearchWithIntents(answersActions, isVertical, intents, geolocationOptions); + intents = responseToLatestRequest?.inputIntents || []; } + executeSearchWithIntents(answersActions, isVertical, intents, geolocationOptions); } function renderSearchButton () { diff --git a/sample-app/src/pages/UniversalSearchPage.tsx b/sample-app/src/pages/UniversalSearchPage.tsx index 09328e61..ead821e6 100644 --- a/sample-app/src/pages/UniversalSearchPage.tsx +++ b/sample-app/src/pages/UniversalSearchPage.tsx @@ -1,6 +1,7 @@ import UniversalResults from '../components/UniversalResults'; import { useLayoutEffect } from 'react'; import { useAnswersActions } from '@yext/answers-headless-react'; +import { SearchIntent } from '@yext/answers-headless'; import '../sass/UniversalSearchPage.scss'; import { UniversalResultsConfig } from '../universalResultsConfig'; import { executeSearchWithIntents, getSearchIntents } from '../utils/search-operations'; @@ -19,12 +20,11 @@ export default function UniversalSearchPage(props: { universalResultsConfig: Uni }) answersActions.setVerticalKey(''); const executeQuery = async () => { - if(answersActions.state.location.userLocation) { - answersActions.executeUniversalQuery(); - } else { - const searchIntents = await getSearchIntents(answersActions, false); - executeSearchWithIntents(answersActions, false, searchIntents || []); + let searchIntents: SearchIntent[] = []; + if (!answersActions.state.location.userLocation) { + searchIntents = await getSearchIntents(answersActions, false) || []; } + executeSearchWithIntents(answersActions, false, searchIntents); }; executeQuery(); }, [answersActions]); diff --git a/sample-app/src/pages/VerticalSearchPage.tsx b/sample-app/src/pages/VerticalSearchPage.tsx index 9eabb495..efd955f7 100644 --- a/sample-app/src/pages/VerticalSearchPage.tsx +++ b/sample-app/src/pages/VerticalSearchPage.tsx @@ -10,6 +10,7 @@ import '../sass/VerticalSearchPage.scss'; import { StandardCard } from '../components/cards/StandardCard'; import { useLayoutEffect } from 'react'; import { useAnswersActions } from '@yext/answers-headless-react'; +import { SearchIntent } from '@yext/answers-headless'; import { executeSearchWithIntents, getSearchIntents } from '../utils/search-operations'; const countryFilterOptions = [ @@ -70,12 +71,11 @@ export default function VerticalSearchPage(props: { }); answersActions.setVerticalKey(props.verticalKey); const executeQuery = async () => { - if(answersActions.state.location.userLocation) { - answersActions.executeVerticalQuery(); - } else { - const searchIntents = await getSearchIntents(answersActions, true); - executeSearchWithIntents(answersActions, true, searchIntents || []); + let searchIntents: SearchIntent[] = []; + if (!answersActions.state.location.userLocation) { + searchIntents = await getSearchIntents(answersActions, true) || []; } + executeSearchWithIntents(answersActions, true, searchIntents); }; executeQuery(); }, [answersActions, props.verticalKey]); From 858f7850d375794c928f7eac57a4c3c3001388cc Mon Sep 17 00:00:00 2001 From: Yen Truong Date: Mon, 22 Nov 2021 14:30:59 -0500 Subject: [PATCH 20/21] rename --- sample-app/src/components/SearchBar.tsx | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/sample-app/src/components/SearchBar.tsx b/sample-app/src/components/SearchBar.tsx index 026c7c5a..0cf6c970 100644 --- a/sample-app/src/components/SearchBar.tsx +++ b/sample-app/src/components/SearchBar.tsx @@ -35,14 +35,14 @@ export default function SearchBar({ * Allow a query search to wait on the response to the autocomplete request right * before the search execution in order to retrieve the search intents */ - const responseToLatestRequestRef = useRef>(); + const autocompletePromiseRef = useRef>(); const [ autocompleteResponse, executeAutocomplete] = useAutocomplete(isVertical); async function executeQuery () { let intents: SearchIntent[] = []; if (!answersActions.state.location.userLocation) { - const responseToLatestRequest = await responseToLatestRequestRef.current; - intents = responseToLatestRequest?.inputIntents || []; + const autocompleteResponseBeforeSearch = await autocompletePromiseRef.current; + intents = autocompleteResponseBeforeSearch?.inputIntents || []; } executeSearchWithIntents(answersActions, isVertical, intents, geolocationOptions); } @@ -79,7 +79,7 @@ export default function SearchBar({ answersActions.setQuery(value); }} updateDropdown={() => { - responseToLatestRequestRef.current = executeAutocomplete(); + autocompletePromiseRef.current = executeAutocomplete(); }} renderButtons={renderSearchButton} cssClasses={{ From 4f0ea01f1d21dce6e69c80f6253f1afa15871813 Mon Sep 17 00:00:00 2001 From: Yen Truong Date: Mon, 22 Nov 2021 14:49:25 -0500 Subject: [PATCH 21/21] updateLocationIfNeeded --- sample-app/src/components/SearchBar.tsx | 5 +++-- sample-app/src/pages/UniversalSearchPage.tsx | 9 +++++++-- sample-app/src/pages/VerticalSearchPage.tsx | 9 +++++++-- sample-app/src/utils/search-operations.ts | 10 +++------- 4 files changed, 20 insertions(+), 13 deletions(-) diff --git a/sample-app/src/components/SearchBar.tsx b/sample-app/src/components/SearchBar.tsx index 0cf6c970..6cd54ca3 100644 --- a/sample-app/src/components/SearchBar.tsx +++ b/sample-app/src/components/SearchBar.tsx @@ -8,7 +8,7 @@ import LoadingIndicator from './LoadingIndicator'; import { useAutocomplete } from '../hooks/useAutocomplete'; import { useRef } from 'react'; import { AutocompleteResponse, SearchIntent } from '@yext/answers-headless'; -import { executeSearchWithIntents } from '../utils/search-operations'; +import { executeSearch, updateLocationIfNeeded } from '../utils/search-operations'; const SCREENREADER_INSTRUCTIONS = 'When autocomplete results are available, use up and down arrows to review and enter to select.' @@ -43,8 +43,9 @@ export default function SearchBar({ if (!answersActions.state.location.userLocation) { const autocompleteResponseBeforeSearch = await autocompletePromiseRef.current; intents = autocompleteResponseBeforeSearch?.inputIntents || []; + await updateLocationIfNeeded(answersActions, intents, geolocationOptions); } - executeSearchWithIntents(answersActions, isVertical, intents, geolocationOptions); + executeSearch(answersActions, isVertical); } function renderSearchButton () { diff --git a/sample-app/src/pages/UniversalSearchPage.tsx b/sample-app/src/pages/UniversalSearchPage.tsx index ead821e6..977725a3 100644 --- a/sample-app/src/pages/UniversalSearchPage.tsx +++ b/sample-app/src/pages/UniversalSearchPage.tsx @@ -4,7 +4,11 @@ import { useAnswersActions } from '@yext/answers-headless-react'; import { SearchIntent } from '@yext/answers-headless'; import '../sass/UniversalSearchPage.scss'; import { UniversalResultsConfig } from '../universalResultsConfig'; -import { executeSearchWithIntents, getSearchIntents } from '../utils/search-operations'; +import { + executeSearch, + getSearchIntents, + updateLocationIfNeeded +} from '../utils/search-operations'; const universalResultsFilterConfig = { show: true @@ -23,8 +27,9 @@ export default function UniversalSearchPage(props: { universalResultsConfig: Uni let searchIntents: SearchIntent[] = []; if (!answersActions.state.location.userLocation) { searchIntents = await getSearchIntents(answersActions, false) || []; + updateLocationIfNeeded(answersActions, searchIntents); } - executeSearchWithIntents(answersActions, false, searchIntents); + executeSearch(answersActions, false); }; executeQuery(); }, [answersActions]); diff --git a/sample-app/src/pages/VerticalSearchPage.tsx b/sample-app/src/pages/VerticalSearchPage.tsx index efd955f7..a1108d84 100644 --- a/sample-app/src/pages/VerticalSearchPage.tsx +++ b/sample-app/src/pages/VerticalSearchPage.tsx @@ -11,7 +11,11 @@ import { StandardCard } from '../components/cards/StandardCard'; import { useLayoutEffect } from 'react'; import { useAnswersActions } from '@yext/answers-headless-react'; import { SearchIntent } from '@yext/answers-headless'; -import { executeSearchWithIntents, getSearchIntents } from '../utils/search-operations'; +import { + executeSearch, + getSearchIntents, + updateLocationIfNeeded +} from '../utils/search-operations'; const countryFilterOptions = [ { @@ -74,8 +78,9 @@ export default function VerticalSearchPage(props: { let searchIntents: SearchIntent[] = []; if (!answersActions.state.location.userLocation) { searchIntents = await getSearchIntents(answersActions, true) || []; + await updateLocationIfNeeded(answersActions, searchIntents); } - executeSearchWithIntents(answersActions, true, searchIntents); + executeSearch(answersActions, true); }; executeQuery(); }, [answersActions, props.verticalKey]); diff --git a/sample-app/src/utils/search-operations.ts b/sample-app/src/utils/search-operations.ts index 35b2641d..9c8b2eec 100644 --- a/sample-app/src/utils/search-operations.ts +++ b/sample-app/src/utils/search-operations.ts @@ -8,13 +8,11 @@ const defaultGeolocationOptions: PositionOptions = { }; /** - * If the provided search intents include a 'NEAR_ME' intent, retrieve and - * store user's location in headless state. Then, execute a search with - * user's location, if that's successfully retrieved, attached to the request. + * If the provided search intents include a 'NEAR_ME' intent and there's no existing + * user's location in state, retrieve and store user's location in headless state. */ -export async function executeSearchWithIntents( +export async function updateLocationIfNeeded( answersActions: AnswersActions, - isVertical: boolean, intents: SearchIntent[], geolocationOptions?: PositionOptions ) { @@ -29,7 +27,6 @@ export async function executeSearchWithIntents( console.error(e); } } - executeSearch(answersActions, isVertical); } /** @@ -39,7 +36,6 @@ export async function executeSearch(answersActions: AnswersActions, isVertical: isVertical ? answersActions.executeVerticalQuery() : answersActions.executeUniversalQuery(); - return; } /**