UNPKG

box-ui-elements-mlh

Version:
59 lines (52 loc) 2.56 kB
/** * @flow * @file Suggested Collaborator utility functions * @author Box */ import type { SuggestedCollab, SuggestedCollabLookup } from '../flowTypes'; import fuzzySearch from '../../../utils/fuzzySearch'; function scoreComparator(optionA: SuggestedCollab, optionB: SuggestedCollab): number { return optionB.userScore - optionA.userScore; } /** * Function to compute suggested collaborators given a list of contacts and cached suggested collaborators. * Used by input components to help augment API results with cached suggestions using a fuzzy search. */ function computeSuggestedCollabs<C: { id: string }>( contacts: Array<C>, suggestedCollabLookup: SuggestedCollabLookup, inputValue: string, maxSuggestions: number = 3, minCharacters: number = 3, maxGaps: number = 2, ): [Array<SuggestedCollab>, Array<C>] { const contactIdSet = new Set(contacts.map(contact => contact.id.toString())); // $FlowFixMe const suggestedCollabs: Array<SuggestedCollab> = Object.values(suggestedCollabLookup); const exactMatches: Array<SuggestedCollab> = suggestedCollabs.filter( suggestedCollab => suggestedCollab.id && contactIdSet.has(suggestedCollab.id.toString()), ); const exactMatchIds = exactMatches.map(suggestedCollab => suggestedCollab.id.toString()); const suggestedFuzzyMatches = exactMatches.length >= maxSuggestions ? [] : suggestedCollabs.filter((option: SuggestedCollab) => { if (!inputValue || exactMatchIds.includes(option.id.toString())) { return false; } const nameMatches = fuzzySearch(inputValue, option.name, minCharacters, maxGaps); const emailAddress = option.email || ''; const emailAlias = emailAddress.substring(0, emailAddress.indexOf('@')); const emailMatches = inputValue.length >= minCharacters && emailAlias.includes(inputValue); return nameMatches || emailMatches; }); // combine both lists preferring exact id matches over fuzzy matches const suggestedResult = [ ...exactMatches.sort(scoreComparator), ...suggestedFuzzyMatches.sort(scoreComparator), ].slice(0, maxSuggestions); const suggestedResultIds = suggestedResult.map(suggestion => suggestion.id); const otherResults = contacts.filter(contact => !suggestedResultIds.includes(contact.id)); return [suggestedResult, otherResults]; } export default computeSuggestedCollabs;