Skip to content

Commit

Permalink
feedback
Browse files Browse the repository at this point in the history
  • Loading branch information
Yen Truong committed Nov 18, 2021
1 parent b398b42 commit 4a4d600
Show file tree
Hide file tree
Showing 6 changed files with 128 additions and 25 deletions.
13 changes: 11 additions & 2 deletions sample-app/src/components/LocationBias.tsx
Original file line number Diff line number Diff line change
@@ -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,
Expand Down Expand Up @@ -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 (
<div className={cssClasses.container}>
<span className={cssClasses.location}>
Expand All @@ -45,7 +54,7 @@ export default function LocationBias(props: Props) {
)}
<button
className={cssClasses.button}
onClick={() => executeSearchWithUserLocation(answersActions, isVertical, geolocationOptions, false)}
onClick={handleGeolocationClick}
>
Update your location
</button>
Expand Down
33 changes: 26 additions & 7 deletions sample-app/src/components/SearchBar.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -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 () {
Expand All @@ -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 => {
Expand Down
32 changes: 20 additions & 12 deletions sample-app/src/hooks/useAutocomplete.tsx
Original file line number Diff line number Diff line change
@@ -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<void>] {
export function useAutocomplete(
isVertical: boolean
): [ AutocompleteResponse|undefined, MutableRefObject<Promise<void>>, () => Promise<void> ] {
const answersActions = useAnswersActions();
const autocompleteNetworkIds = useRef({ latestRequest: 0, responseInState: 0 });
const [ autocompleteResults, setAutocompleteResults ] = useState<AutocompleteResult[]>([]);
const [ autocompleteResponse, setAutocompleteResponse ] = useState<AutocompleteResponse>();
const latestAutocompleteResponseRef = useRef<Promise<void>>(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 ]
};
16 changes: 14 additions & 2 deletions sample-app/src/pages/UniversalSearchPage.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand All @@ -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 (
Expand Down
16 changes: 14 additions & 2 deletions sample-app/src/pages/VerticalSearchPage.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -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 = [
{
Expand Down Expand Up @@ -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 (
Expand Down
43 changes: 43 additions & 0 deletions sample-app/src/utils/searchhandler.ts
Original file line number Diff line number Diff line change
@@ -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<GeolocationPosition> {
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();
}
});
}
}

0 comments on commit 4a4d600

Please sign in to comment.