@empathyco/x-components
Version:
Empathy X Components
142 lines (139 loc) • 6.47 kB
JavaScript
import { isArrayEmpty } from '../../../../utils/array.js';
import { normalizeString } from '../../../../utils/normalize.js';
/** Regex for splitting a query into its words. */
const SPLIT_WORDS_REGEX = /[\s-]/;
/**
* Class implementation for the {@link HistoryQueriesActions.addQueryToHistory} action.
*
* @public
*/
class AddQueryToHistoryAction {
/**
* Default implementation for the {@link HistoryQueriesActions.addQueryToHistory}.
*
* @param context - The {@link https://vuex.vuejs.org/guide/actions.html | context} of the
* actions, provided by Vuex.
* @param query - The query to try to add to the history.
* @returns A `void` promise that resolves when the history queries finishes updating.
*/
addQueryToHistory({ state, dispatch }, query) {
const normalizedQuery = normalizeString(query);
if (!normalizedQuery) {
return;
}
if (isArrayEmpty(state.historyQueries)) {
return dispatch('setHistoryQueries', [this.createHistoryQuery(query)]);
}
const newHistory = this.createNewHistory(state.historyQueries, normalizedQuery);
if (newHistory) {
newHistory.unshift(this.createHistoryQuery(query));
return dispatch('setHistoryQueries', newHistory);
}
}
/**
* Creates a new `HistoryQuery`.
*
* @param query - The query for the new `HistoryQuery`.
* @returns A new `HistoryQuery`.
* @internal
*/
createHistoryQuery(query) {
return {
query: query.trim(),
timestamp: Date.now(),
modelName: 'HistoryQuery',
};
}
/**
* Creates a new history from the old one to store the new query. Depending on the comparison
* between the new query, and the last one in the history, it can return three different things:
* - If it the last stored query should be replaced with the new one
* ({@link AddQueryToHistoryAction.isReplaceAction} returns true), it returns a copy of the old
* history, without the new query and the first item.
* - If the new query should be simply added to the history
* ({@link AddQueryToHistoryAction.isAddAction} returns true), It returns a copy of the old
* history but without the new query if it was present.
* - In any other case, the query shouldn't be saved, so this method returns `null`.
*
* @param currentHistory - The current history of queries.
* @param normalizedQuery - The normalized version of the new query, to be stored on the history.
* @returns A subset of the current history of queries ready to add the new `HistoryQuery`, or
* null if the new query shouldn't be saved.
* @internal
*/
createNewHistory(currentHistory, normalizedQuery) {
const normalizedLastQuery = normalizeString(currentHistory[0].query);
const queriesTuple = [normalizedLastQuery, normalizedQuery];
const newWords = normalizedQuery.split(SPLIT_WORDS_REGEX);
const lastWords = normalizedLastQuery.split(SPLIT_WORDS_REGEX);
const wordsTuple = [lastWords, newWords];
return this.isReplaceAction(wordsTuple, queriesTuple)
? // TODO EX-1815 This replace does not take into account yet queries that end in numbers
this.removeNewQueryFromHistory(currentHistory.slice(1), normalizedQuery)
: this.isAddAction(wordsTuple, queriesTuple)
? this.removeNewQueryFromHistory(currentHistory, normalizedQuery)
: null;
}
/**
* Creates a copy of the current history, but removing the new query to store from it. It uses a
* normalized version of the queries for comparing.
*
* @param currentHistory - The current history queries.
* @param normalizedQuery - The normalized version of the new query to add to the history.
* @returns A copy of the current history but without the new query into it.
* @internal
*/
removeNewQueryFromHistory(currentHistory, normalizedQuery) {
return currentHistory.filter(historyQuery => normalizeString(historyQuery.query) !== normalizedQuery);
}
/**
* Calculates if the new query should be added to the history.
*
* @param wordsTuple - A tuple containing the old, and the new words arrays.
* @param queriesTuple - A tuple containing the old and the new queries.
* @returns `true` when the new query should be added. `false` otherwise.
* @internal
*/
isAddAction([lastWords, newWords], [lastQuery, newQuery]) {
return newWords.length !== lastWords.length || !lastQuery.includes(newQuery);
}
/**
* Calculates if the new query should replace the last query in the history.
*
* @param wordsTuple - A tuple containing the old, and the new words arrays.
* @param queriesTuple - A tuple containing the old and the new queries.
* @returns `true` when the new query should replace the last one. `false` otherwise.
* @internal
*/
isReplaceAction([lastWords, newWords], [lastQuery, newQuery]) {
return lastQuery === newQuery || this.isQueryBeingRefined(lastWords, newWords);
}
/**
* Returns if the new query is a refined version of the last one. A refined version means to be
* more specific. I.e. `shoes` is a refined query of `shoe`, `lego star wars` is a refined query
* of `lego st`.
*
* @param lastWords - An array containing the words of the last query.
* @param newWords - An array containing the words of the new query.
* @returns `true` if the new query is refining the old one. `false` otherwise.
* @internal
*/
isQueryBeingRefined(lastWords, newWords) {
const refinedWordIndex = lastWords.length - 1;
const lastRefinedWord = lastWords[refinedWordIndex];
const newRefinedWord = newWords[refinedWordIndex];
return (!!lastRefinedWord &&
!!newRefinedWord &&
newRefinedWord !== lastRefinedWord &&
newRefinedWord.includes(lastRefinedWord));
}
}
const addQueryToHistoryAction = new AddQueryToHistoryAction();
/**
* {@inheritDoc AddQueryToHistoryAction.addQueryToHistory}
*
* @public
*/
const addQueryToHistory = addQueryToHistoryAction.addQueryToHistory.bind(addQueryToHistoryAction);
export { AddQueryToHistoryAction, addQueryToHistory };
//# sourceMappingURL=add-query-to-history.action.js.map