Skip to content

Commit

Permalink
Support "Near Me" queries
Browse files Browse the repository at this point in the history
- 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.
  • Loading branch information
Yen Truong committed Nov 17, 2021
1 parent ced7e2c commit 032f446
Show file tree
Hide file tree
Showing 5 changed files with 72 additions and 34 deletions.
37 changes: 8 additions & 29 deletions sample-app/src/components/LocationBias.tsx
Original file line number Diff line number Diff line change
@@ -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,
Expand All @@ -15,11 +12,6 @@ interface Props {
}
}

const defaultGeolocationOptions = {
enableHighAccuracy: false,
timeout: 6000,
maximumAge: 300000,
};

const defaultCSSClasses = {
container: 'LocationBias',
Expand All @@ -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;

Expand All @@ -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 (
<div className={cssClasses.container}>
<span className={cssClasses.location}>
Expand All @@ -67,7 +43,10 @@ export default function LocationBias(props: Props) {
{attributionMessage}
</span>
)}
<button className={cssClasses.button} onClick={handleGeolocationClick}>
<button
className={cssClasses.button}
onClick={() => executeSearchWithUserLocation(answersActions, isVertical, geolocationOptions, false)}
>
Update your location
</button>
</div>
Expand Down
5 changes: 2 additions & 3 deletions sample-app/src/components/SearchBar.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -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.'

Expand All @@ -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 () {
Expand Down
3 changes: 2 additions & 1 deletion sample-app/src/pages/UniversalSearchPage.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand All @@ -17,7 +18,7 @@ export default function UniversalSearchPage(props: { universalResultsConfig: Uni
vertical: {}
})
answersActions.setVerticalKey('');
answersActions.executeUniversalQuery();
executeSearchWithUserLocation(answersActions, false, {}, true);
}, [answersActions]);

return (
Expand Down
3 changes: 2 additions & 1 deletion sample-app/src/pages/VerticalSearchPage.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -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 = [
{
Expand Down Expand Up @@ -68,7 +69,7 @@ export default function VerticalSearchPage(props: {
universal: {}
});
answersActions.setVerticalKey(props.verticalKey);
answersActions.executeVerticalQuery();
executeSearchWithUserLocation(answersActions, true, {}, true);
}, [answersActions, props.verticalKey]);

return (
Expand Down
58 changes: 58 additions & 0 deletions sample-app/src/utils/geolocationutils.tsx
Original file line number Diff line number Diff line change
@@ -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();
}
}

0 comments on commit 032f446

Please sign in to comment.