From 0cf5ebbad04eb8c78b637bc29bc6bf599b1e5a35 Mon Sep 17 00:00:00 2001 From: 289Adam289 Date: Fri, 31 Jan 2025 13:03:51 +0100 Subject: [PATCH 01/16] working highlighting --- src/libs/runOnLiveMarkdownRuntime/index.native.tsx | 8 ++++++++ src/libs/runOnLiveMarkdownRuntime/index.tsx | 6 ++++++ 2 files changed, 14 insertions(+) create mode 100644 src/libs/runOnLiveMarkdownRuntime/index.native.tsx create mode 100644 src/libs/runOnLiveMarkdownRuntime/index.tsx diff --git a/src/libs/runOnLiveMarkdownRuntime/index.native.tsx b/src/libs/runOnLiveMarkdownRuntime/index.native.tsx new file mode 100644 index 000000000000..8927fe0a936c --- /dev/null +++ b/src/libs/runOnLiveMarkdownRuntime/index.native.tsx @@ -0,0 +1,8 @@ +import {getWorkletRuntime} from '@expensify/react-native-live-markdown'; +import {runOnRuntime} from 'react-native-reanimated'; + +function runOnLiveMarkdownRuntime(worklet: (...args: Args) => ReturnValue) { + return runOnRuntime(getWorkletRuntime(), worklet); +} + +export default runOnLiveMarkdownRuntime; diff --git a/src/libs/runOnLiveMarkdownRuntime/index.tsx b/src/libs/runOnLiveMarkdownRuntime/index.tsx new file mode 100644 index 000000000000..c8e072693323 --- /dev/null +++ b/src/libs/runOnLiveMarkdownRuntime/index.tsx @@ -0,0 +1,6 @@ +// Reanimated does not support runOnRuntime() on web +function runOnLiveMarkdownRuntime(worklet: (...args: Args) => ReturnValue) { + return worklet; +} + +export default runOnLiveMarkdownRuntime; From 85d543554cc3b1a4320de755eb2a95518334ac19 Mon Sep 17 00:00:00 2001 From: 289Adam289 Date: Fri, 31 Jan 2025 13:13:16 +0100 Subject: [PATCH 02/16] add syntax highlighting --- .../Search/SearchAutocompleteInput.tsx | 89 +++++++++++++++++-- .../SearchInputSelectionWrapper/index.tsx | 2 +- .../Search/SearchPageHeaderInput.tsx | 1 + .../Search/SearchRouter/SearchRouter.tsx | 1 + src/libs/SearchAutocompleteUtils.ts | 52 ++++++++--- 5 files changed, 126 insertions(+), 19 deletions(-) diff --git a/src/components/Search/SearchAutocompleteInput.tsx b/src/components/Search/SearchAutocompleteInput.tsx index 215599952c50..3e260a9bda96 100644 --- a/src/components/Search/SearchAutocompleteInput.tsx +++ b/src/components/Search/SearchAutocompleteInput.tsx @@ -1,20 +1,27 @@ +import isEqual from 'lodash/isEqual'; import type {ForwardedRef, ReactNode, RefObject} from 'react'; -import React, {forwardRef, useState} from 'react'; +import React, {forwardRef, useCallback, useEffect, useMemo, useRef, useState} from 'react'; import {View} from 'react-native'; import type {StyleProp, TextInputProps, TextStyle, ViewStyle} from 'react-native'; +import {useOnyx} from 'react-native-onyx'; +import {useSharedValue} from 'react-native-reanimated'; import FormHelpMessage from '@components/FormHelpMessage'; import type {SelectionListHandle} from '@components/SelectionList/types'; import TextInput from '@components/TextInput'; import type {BaseTextInputRef} from '@components/TextInput/BaseTextInput/types'; +import useActiveWorkspace from '@hooks/useActiveWorkspace'; import useCurrentUserPersonalDetails from '@hooks/useCurrentUserPersonalDetails'; import useLocalize from '@hooks/useLocalize'; import useNetwork from '@hooks/useNetwork'; import useThemeStyles from '@hooks/useThemeStyles'; -import {parseForLiveMarkdown} from '@libs/SearchAutocompleteUtils'; +import runOnLiveMarkdownRuntime from '@libs/runOnLiveMarkdownRuntime'; +import {getAutocompleteCategories, getAutocompleteTags, parseForLiveMarkdown} from '@libs/SearchAutocompleteUtils'; import handleKeyPress from '@libs/SearchInputOnKeyPress'; import shouldDelayFocus from '@libs/shouldDelayFocus'; import variables from '@styles/variables'; import CONST from '@src/CONST'; +import ONYXKEYS from '@src/ONYXKEYS'; +import type {SubstitutionMap} from './SearchRouter/getQueryWithSubstitutions'; type SearchAutocompleteInputProps = { /** Value of TextInput */ @@ -59,6 +66,9 @@ type SearchAutocompleteInputProps = { /** Whether the search reports API call is running */ isSearchingForReports?: boolean; + /** Map of autocomplete suggestions. Required for highlighting to work properly */ + substitutionMap: SubstitutionMap; + /** input style */ inputStyle?: StyleProp; } & Pick; @@ -83,6 +93,7 @@ function SearchAutocompleteInput( isSearchingForReports, selection, inputStyle, + substitutionMap, }: SearchAutocompleteInputProps, ref: ForwardedRef, ) { @@ -90,9 +101,75 @@ function SearchAutocompleteInput( const {translate} = useLocalize(); const [isFocused, setIsFocused] = useState(false); const {isOffline} = useNetwork(); + const {activeWorkspaceID} = useActiveWorkspace(); const currentUserPersonalDetails = useCurrentUserPersonalDetails(); + const personalDetailsSharedValue = useSharedValue({login: currentUserPersonalDetails.login, userDisplayName: currentUserPersonalDetails.displayName}); + const lastMap = useRef({}); + const [map, setMap] = useState({}); + + const [currencyList] = useOnyx(ONYXKEYS.CURRENCY_LIST); + const currencyAutocompleteList = Object.keys(currencyList ?? {}); + const currencySharedValue = useSharedValue(currencyAutocompleteList); + + const [allPolicyCategories] = useOnyx(ONYXKEYS.COLLECTION.POLICY_CATEGORIES); + const categoryAutocompleteList = useMemo(() => { + return getAutocompleteCategories(allPolicyCategories, activeWorkspaceID); + }, [activeWorkspaceID, allPolicyCategories]); + const categorySharedValue = useSharedValue(categoryAutocompleteList); + + const [allPoliciesTags] = useOnyx(ONYXKEYS.COLLECTION.POLICY_TAGS); + const tagAutocompleteList = useMemo(() => { + return getAutocompleteTags(allPoliciesTags, activeWorkspaceID); + }, [activeWorkspaceID, allPoliciesTags]); + const tagSharedValue = useSharedValue(tagAutocompleteList); + + useEffect(() => { + if (lastMap.current && !isEqual(lastMap.current, substitutionMap)) { + lastMap.current = substitutionMap; + } + setMap(lastMap.current ?? {}); + }, [substitutionMap, lastMap]); + const offlineMessage: string = isOffline && shouldShowOfflineMessage ? `${translate('common.youAppearToBeOffline')} ${translate('search.resultsAreLimited')}` : ''; + useEffect(() => { + runOnLiveMarkdownRuntime(() => { + 'worklet'; + + personalDetailsSharedValue.set({login: currentUserPersonalDetails.login, userDisplayName: currentUserPersonalDetails.displayName}); + })(); + }, [currentUserPersonalDetails, personalDetailsSharedValue]); + useEffect(() => { + runOnLiveMarkdownRuntime(() => { + 'worklet'; + + currencySharedValue.set(currencyAutocompleteList); + })(); + }, [currencyAutocompleteList, currencySharedValue]); + useEffect(() => { + runOnLiveMarkdownRuntime(() => { + 'worklet'; + + categorySharedValue.set(categoryAutocompleteList); + })(); + }, [categorySharedValue, categoryAutocompleteList]); + useEffect(() => { + runOnLiveMarkdownRuntime(() => { + 'worklet'; + + tagSharedValue.set(tagAutocompleteList); + }); + }, [tagSharedValue, tagAutocompleteList]); + + const parser = useCallback( + (input: string) => { + 'worklet'; + + return parseForLiveMarkdown(input, personalDetailsSharedValue, map, currencySharedValue, categorySharedValue, tagSharedValue); + }, + [personalDetailsSharedValue, map, currencySharedValue, categorySharedValue, tagSharedValue], + ); + const inputWidth = isFullWidth ? styles.w100 : {width: variables.popoverWidth}; return ( @@ -121,7 +198,7 @@ function SearchAutocompleteInput( maxLength={CONST.SEARCH_QUERY_LIMIT} onSubmitEditing={onSubmit} shouldUseDisabledStyles={false} - textInputContainerStyles={[styles.borderNone, styles.pb0]} + textInputContainerStyles={[styles.borderNone, styles.pb0, styles.pr3]} inputStyle={[inputWidth, inputStyle]} onFocus={() => { setIsFocused(true); @@ -138,11 +215,7 @@ function SearchAutocompleteInput( onKeyPress={handleKeyPress(onSubmit)} isMarkdownEnabled multiline={false} - parser={(input: string) => { - 'worklet'; - - return parseForLiveMarkdown(input, currentUserPersonalDetails.login ?? '', currentUserPersonalDetails.displayName ?? ''); - }} + parser={parser} selection={selection} /> diff --git a/src/components/Search/SearchInputSelectionWrapper/index.tsx b/src/components/Search/SearchInputSelectionWrapper/index.tsx index a806cea9afd4..1a0a18ffaa47 100644 --- a/src/components/Search/SearchInputSelectionWrapper/index.tsx +++ b/src/components/Search/SearchInputSelectionWrapper/index.tsx @@ -10,7 +10,7 @@ function SearchInputSelectionWrapper({selection, ...props}: SearchAutocompleteIn return ( , + map: SubstitutionMap, + currencyList: SharedValue, + categoryList: SharedValue, + tagList: SharedValue, +) { 'worklet'; const parsedAutocomplete = parse(input) as SearchAutocompleteResult; const ranges = parsedAutocomplete.ranges; - return ranges.map((range) => { - let type = 'mention-user'; - - if ((range.key === CONST.SEARCH.SYNTAX_FILTER_KEYS.TO || CONST.SEARCH.SYNTAX_FILTER_KEYS.FROM) && (range.value === userLogin || range.value === userDisplayName)) { - type = 'mention-here'; - } - - return {...range, type}; - }) as MarkdownRange[]; + const typeList = Object.values(CONST.SEARCH.DATA_TYPES) as string[]; + const expenseTypeList = Object.values(CONST.SEARCH.TRANSACTION_TYPE) as string[]; + const statusList = Object.values({...CONST.SEARCH.STATUS.TRIP, ...CONST.SEARCH.STATUS.INVOICE, ...CONST.SEARCH.STATUS.CHAT, ...CONST.SEARCH.STATUS.TRIP}) as string[]; + const subMap = map; + return ranges + .filter( + (range) => + !( + range.key === CONST.SEARCH.SYNTAX_FILTER_KEYS.TO || + range.key === CONST.SEARCH.SYNTAX_FILTER_KEYS.FROM || + range.key === CONST.SEARCH.SYNTAX_FILTER_KEYS.IN || + range.key === CONST.SEARCH.SYNTAX_FILTER_KEYS.TAX_RATE || + range.key === CONST.SEARCH.SYNTAX_FILTER_KEYS.CARD_ID + ) || subMap[`${range.key}:${range.value}`] !== undefined, + ) + .filter((range) => range.key !== CONST.SEARCH.SYNTAX_FILTER_KEYS.CURRENCY || currencyList.get().includes(range.value)) + .filter((range) => range.key !== CONST.SEARCH.SYNTAX_ROOT_KEYS.TYPE || typeList.includes(range.value)) + .filter((range) => range.key !== CONST.SEARCH.SYNTAX_FILTER_KEYS.EXPENSE_TYPE || expenseTypeList.includes(range.value)) + .filter((range) => range.key !== CONST.SEARCH.SYNTAX_ROOT_KEYS.STATUS || statusList.includes(range.value)) + .filter((range) => range.key !== CONST.SEARCH.SYNTAX_FILTER_KEYS.CATEGORY || categoryList.get().includes(range.value)) + .filter((range) => range.key !== CONST.SEARCH.SYNTAX_FILTER_KEYS.TAG || tagList.get().includes(range.value)) + .map((range) => { + let type = 'mention-user'; + if ( + (range.key === CONST.SEARCH.SYNTAX_FILTER_KEYS.TO || CONST.SEARCH.SYNTAX_FILTER_KEYS.FROM) && + (range.value === details.get().login || range.value === details.get().userDisplayName) + ) { + type = 'mention-here'; + } + + return {...range, type}; + }) as MarkdownRange[]; } export { From 944fbdd2344a2663746588dc29ed65d064922b40 Mon Sep 17 00:00:00 2001 From: 289Adam289 Date: Fri, 31 Jan 2025 13:35:45 +0100 Subject: [PATCH 03/16] add supporting text --- .../Search/SearchRouter/buildSubstitutionsMap.ts | 2 +- .../Search/SearchRouter/getQueryWithSubstitutions.ts | 2 +- .../Search/SearchRouter/getUpdatedSubstitutionsMap.ts | 2 +- src/components/Search/types.ts | 4 +++- src/libs/SearchAutocompleteUtils.ts | 3 +++ src/libs/SearchParser/autocompleteParser.js | 9 +++++---- src/libs/SearchParser/autocompleteParser.peggy | 9 +++++---- 7 files changed, 19 insertions(+), 12 deletions(-) diff --git a/src/components/Search/SearchRouter/buildSubstitutionsMap.ts b/src/components/Search/SearchRouter/buildSubstitutionsMap.ts index 892aa050ef9d..0a3c49819b38 100644 --- a/src/components/Search/SearchRouter/buildSubstitutionsMap.ts +++ b/src/components/Search/SearchRouter/buildSubstitutionsMap.ts @@ -33,7 +33,7 @@ function buildSubstitutionsMap( ): SubstitutionMap { const parsedQuery = parse(query) as {ranges: SearchAutocompleteQueryRange[]}; - const searchAutocompleteQueryRanges = parsedQuery.ranges; + const searchAutocompleteQueryRanges = parsedQuery.ranges.filter((range) => range.key !== 'syntax'); if (searchAutocompleteQueryRanges.length === 0) { return {}; diff --git a/src/components/Search/SearchRouter/getQueryWithSubstitutions.ts b/src/components/Search/SearchRouter/getQueryWithSubstitutions.ts index 84895efb33a5..b976371135a6 100644 --- a/src/components/Search/SearchRouter/getQueryWithSubstitutions.ts +++ b/src/components/Search/SearchRouter/getQueryWithSubstitutions.ts @@ -21,7 +21,7 @@ const getSubstitutionMapKey = (filterKey: SearchFilterKey, value: string) => `${ function getQueryWithSubstitutions(changedQuery: string, substitutions: SubstitutionMap) { const parsed = parse(changedQuery) as {ranges: SearchAutocompleteQueryRange[]}; - const searchAutocompleteQueryRanges = parsed.ranges; + const searchAutocompleteQueryRanges = parsed.ranges.filter((range) => range.key !== 'syntax'); if (searchAutocompleteQueryRanges.length === 0) { return changedQuery; diff --git a/src/components/Search/SearchRouter/getUpdatedSubstitutionsMap.ts b/src/components/Search/SearchRouter/getUpdatedSubstitutionsMap.ts index ee7bf3850259..a681ae6030f0 100644 --- a/src/components/Search/SearchRouter/getUpdatedSubstitutionsMap.ts +++ b/src/components/Search/SearchRouter/getUpdatedSubstitutionsMap.ts @@ -18,7 +18,7 @@ const getSubstitutionsKey = (filterKey: SearchFilterKey, value: string) => `${fi function getUpdatedSubstitutionsMap(query: string, substitutions: SubstitutionMap): SubstitutionMap { const parsedQuery = parser.parse(query) as {ranges: SearchAutocompleteQueryRange[]}; - const searchAutocompleteQueryRanges = parsedQuery.ranges; + const searchAutocompleteQueryRanges = parsedQuery.ranges.filter((range) => range.key !== 'syntax'); if (searchAutocompleteQueryRanges.length === 0) { return {}; diff --git a/src/components/Search/types.ts b/src/components/Search/types.ts index 0a402358d73e..b5c58c798b9f 100644 --- a/src/components/Search/types.ts +++ b/src/components/Search/types.ts @@ -95,7 +95,8 @@ type SearchFilterKey = | ValueOf | typeof CONST.SEARCH.SYNTAX_ROOT_KEYS.TYPE | typeof CONST.SEARCH.SYNTAX_ROOT_KEYS.STATUS - | typeof CONST.SEARCH.SYNTAX_ROOT_KEYS.POLICY_ID; + | typeof CONST.SEARCH.SYNTAX_ROOT_KEYS.POLICY_ID + | 'syntax'; type UserFriendlyKey = ValueOf; @@ -128,6 +129,7 @@ type SearchAutocompleteResult = { ranges: SearchAutocompleteQueryRange[]; }; +// TODO FIX types type SearchAutocompleteQueryRange = { key: SearchFilterKey; length: number; diff --git a/src/libs/SearchAutocompleteUtils.ts b/src/libs/SearchAutocompleteUtils.ts index a591e08c2314..d9abec7abf3e 100644 --- a/src/libs/SearchAutocompleteUtils.ts +++ b/src/libs/SearchAutocompleteUtils.ts @@ -182,6 +182,9 @@ function parseForLiveMarkdown( ) { type = 'mention-here'; } + if (range.key === 'syntax') { + type = 'syntax'; + } return {...range, type}; }) as MarkdownRange[]; diff --git a/src/libs/SearchParser/autocompleteParser.js b/src/libs/SearchParser/autocompleteParser.js index 5a787c5d8048..16e135deb222 100644 --- a/src/libs/SearchParser/autocompleteParser.js +++ b/src/libs/SearchParser/autocompleteParser.js @@ -282,20 +282,21 @@ function peg$parse(input, options) { start: location().end.offset, length: 0, }; - return; + return {key:"syntax",value:key, start:location().start.offset, length:location().end.offset-location().start.offset}; } autocomplete = { key, ...value[value.length - 1], }; - - return value + const result = value .filter((filter) => filter.length > 0) .map((filter) => ({ key, ...filter, - })); + })) + + return [{key:"syntax",value:key, start:location().start.offset, length:result[0].start - location().start.offset}, ...result]; }; var peg$f3 = function() { autocomplete = null; }; var peg$f4 = function(parts, empty) { diff --git a/src/libs/SearchParser/autocompleteParser.peggy b/src/libs/SearchParser/autocompleteParser.peggy index 928d1751f8ce..93b1b7e52fa7 100644 --- a/src/libs/SearchParser/autocompleteParser.peggy +++ b/src/libs/SearchParser/autocompleteParser.peggy @@ -31,20 +31,21 @@ defaultFilter start: location().end.offset, length: 0, }; - return; + return {key:"syntax",value:key, start:location().start.offset, length:location().end.offset-location().start.offset}; } autocomplete = { key, ...value[value.length - 1], }; - - return value + const result = value .filter((filter) => filter.length > 0) .map((filter) => ({ key, ...filter, - })); + })) + + return [{key:"syntax",value:key, start:location().start.offset, length:result[0].start - location().start.offset}, ...result]; } freeTextFilter = _ (identifier/ ",") _ { autocomplete = null; } From 7eb5861937a61f2f1ff5408dcc7c171bccd987b6 Mon Sep 17 00:00:00 2001 From: 289Adam289 Date: Fri, 31 Jan 2025 13:47:35 +0100 Subject: [PATCH 04/16] grammar formatting --- src/libs/SearchParser/autocompleteParser.js | 23 +++++++++++++---- .../SearchParser/autocompleteParser.peggy | 25 ++++++++++++++----- src/libs/SearchParser/searchParser.js | 18 ++++++------- src/libs/SearchParser/searchParser.peggy | 18 +++++++------ 4 files changed, 56 insertions(+), 28 deletions(-) diff --git a/src/libs/SearchParser/autocompleteParser.js b/src/libs/SearchParser/autocompleteParser.js index 16e135deb222..092a0022889f 100644 --- a/src/libs/SearchParser/autocompleteParser.js +++ b/src/libs/SearchParser/autocompleteParser.js @@ -278,11 +278,16 @@ function peg$parse(input, options) { if (!value) { autocomplete = { key, - value: '', + value: "", start: location().end.offset, length: 0, }; - return {key:"syntax",value:key, start:location().start.offset, length:location().end.offset-location().start.offset}; + return { + key: "syntax", + value: key, + start: location().start.offset, + length: location().end.offset - location().start.offset, + }; } autocomplete = { @@ -294,9 +299,17 @@ function peg$parse(input, options) { .map((filter) => ({ key, ...filter, - })) - - return [{key:"syntax",value:key, start:location().start.offset, length:result[0].start - location().start.offset}, ...result]; + })); + + return [ + { + key: "syntax", + value: key, + start: location().start.offset, + length: result[0].start - location().start.offset, + }, + ...result, + ]; }; var peg$f3 = function() { autocomplete = null; }; var peg$f4 = function(parts, empty) { diff --git a/src/libs/SearchParser/autocompleteParser.peggy b/src/libs/SearchParser/autocompleteParser.peggy index 93b1b7e52fa7..0b8e15f3364a 100644 --- a/src/libs/SearchParser/autocompleteParser.peggy +++ b/src/libs/SearchParser/autocompleteParser.peggy @@ -27,11 +27,16 @@ defaultFilter if (!value) { autocomplete = { key, - value: '', + value: "", start: location().end.offset, length: 0, }; - return {key:"syntax",value:key, start:location().start.offset, length:location().end.offset-location().start.offset}; + return { + key: "syntax", + value: key, + start: location().start.offset, + length: location().end.offset - location().start.offset, + }; } autocomplete = { @@ -43,12 +48,20 @@ defaultFilter .map((filter) => ({ key, ...filter, - })) - - return [{key:"syntax",value:key, start:location().start.offset, length:result[0].start - location().start.offset}, ...result]; + })); + + return [ + { + key: "syntax", + value: key, + start: location().start.offset, + length: result[0].start - location().start.offset, + }, + ...result, + ]; } -freeTextFilter = _ (identifier/ ",") _ { autocomplete = null; } +freeTextFilter = _ (identifier / ",") _ { autocomplete = null; } autocompleteKey "key" = @( diff --git a/src/libs/SearchParser/searchParser.js b/src/libs/SearchParser/searchParser.js index 804a5928a909..aee1033bef31 100644 --- a/src/libs/SearchParser/searchParser.js +++ b/src/libs/SearchParser/searchParser.js @@ -298,7 +298,9 @@ function peg$parse(input, options) { const keywordFilter = buildFilter( "eq", "keyword", - keywords.map((filter) => filter.right).flat() + keywords + .map((filter) => filter.right.replace(/^(['"])(.*)\1$/, "$2")) + .flat() ); if (keywordFilter.right.length > 0) { nonKeywords.push(keywordFilter); @@ -310,20 +312,18 @@ function peg$parse(input, options) { var peg$f2 = function(key, op, value) { updateDefaultValues(key, value); }; - var peg$f3 = function(value) { //handle no-breaking space - let word + var peg$f3 = function(value) { + //handle no-breaking space + let word; if (Array.isArray(value)) { - word = value.join("") - // return buildFilter("eq", "keyword", value.join("")); - }else{ - word = value + word = value.join(""); + } else { + word = value; } if (word.startsWith('"') && word.endsWith('"') && word.length >= 2) { return buildFilter("eq", "keyword", word.slice(1, -1)); } return buildFilter("eq", "keyword", word); - - // return buildFilter("eq", "keyword", value); }; var peg$f4 = function(field, op, values) { return buildFilter(op, field, values); diff --git a/src/libs/SearchParser/searchParser.peggy b/src/libs/SearchParser/searchParser.peggy index 28f5c3d8a96f..3bbe57722b88 100644 --- a/src/libs/SearchParser/searchParser.peggy +++ b/src/libs/SearchParser/searchParser.peggy @@ -66,7 +66,9 @@ filterList const keywordFilter = buildFilter( "eq", "keyword", - keywords.map((filter) => filter.right.replace(/^(['"])(.*)\1$/, '$2')).flat() + keywords + .map((filter) => filter.right.replace(/^(['"])(.*)\1$/, "$2")) + .flat() ); if (keywordFilter.right.length > 0) { nonKeywords.push(keywordFilter); @@ -84,12 +86,13 @@ defaultFilter } freeTextFilter - = _ value:(quotedString / [^ \t\r\n\xA0]+) _ { //handle no-breaking space - let word + = _ value:(quotedString / [^ \t\r\n\xA0]+) _ { + //handle no-breaking space + let word; if (Array.isArray(value)) { - word = value.join("") - }else{ - word = value + word = value.join(""); + } else { + word = value; } if (word.startsWith('"') && word.endsWith('"') && word.length >= 2) { return buildFilter("eq", "keyword", word.slice(1, -1)); @@ -126,8 +129,7 @@ key "key" / posted ) -defaultKey "default key" - = @(type / status / sortBy / sortOrder / policyID) +defaultKey "default key" = @(type / status / sortBy / sortOrder / policyID) identifier = (","+)? parts:(quotedString / alphanumeric)|1.., ","+| empty:(","+)? { From 68b3262906132b97b367e102d50a4564ad073c4c Mon Sep 17 00:00:00 2001 From: 289Adam289 Date: Mon, 3 Feb 2025 16:24:26 +0100 Subject: [PATCH 05/16] fix merge --- .../Search/SearchAutocompleteInput.tsx | 16 +++------------- src/libs/SearchAutocompleteUtils.ts | 9 ++------- 2 files changed, 5 insertions(+), 20 deletions(-) diff --git a/src/components/Search/SearchAutocompleteInput.tsx b/src/components/Search/SearchAutocompleteInput.tsx index 0c2dd8124b46..6a3c9c8c5e5e 100644 --- a/src/components/Search/SearchAutocompleteInput.tsx +++ b/src/components/Search/SearchAutocompleteInput.tsx @@ -2,11 +2,9 @@ import isEqual from 'lodash/isEqual'; import type {ForwardedRef, ReactNode, RefObject} from 'react'; import React, {forwardRef, useCallback, useEffect, useMemo, useRef, useState} from 'react'; import {View} from 'react-native'; -import type {StyleProp, TextInputProps, TextStyle, ViewStyle} from 'react-native'; -import {useOnyx} from 'react-native-onyx'; -import {useSharedValue} from 'react-native-reanimated'; import type {StyleProp, TextInputProps, ViewStyle} from 'react-native'; import {useOnyx} from 'react-native-onyx'; +import {useSharedValue} from 'react-native-reanimated'; import FormHelpMessage from '@components/FormHelpMessage'; import type {SelectionListHandle} from '@components/SelectionList/types'; import TextInput from '@components/TextInput'; @@ -101,7 +99,6 @@ function SearchAutocompleteInput( const {isOffline} = useNetwork(); const {activeWorkspaceID} = useActiveWorkspace(); const currentUserPersonalDetails = useCurrentUserPersonalDetails(); - const personalDetailsSharedValue = useSharedValue({login: currentUserPersonalDetails.login, userDisplayName: currentUserPersonalDetails.displayName}); const lastMap = useRef({}); const [map, setMap] = useState({}); @@ -133,13 +130,6 @@ function SearchAutocompleteInput( const offlineMessage: string = isOffline && shouldShowOfflineMessage ? `${translate('common.youAppearToBeOffline')} ${translate('search.resultsAreLimited')}` : ''; - useEffect(() => { - runOnLiveMarkdownRuntime(() => { - 'worklet'; - - personalDetailsSharedValue.set({login: currentUserPersonalDetails.login, userDisplayName: currentUserPersonalDetails.displayName}); - })(); - }, [currentUserPersonalDetails, personalDetailsSharedValue]); useEffect(() => { runOnLiveMarkdownRuntime(() => { 'worklet'; @@ -166,9 +156,9 @@ function SearchAutocompleteInput( (input: string) => { 'worklet'; - return parseForLiveMarkdown(input, emailList, personalDetailsSharedValue, map, currencySharedValue, categorySharedValue, tagSharedValue); + return parseForLiveMarkdown(input, emailList, currentUserPersonalDetails.displayName ?? '', map, currencySharedValue, categorySharedValue, tagSharedValue); }, - [personalDetailsSharedValue, map, currencySharedValue, categorySharedValue, tagSharedValue, emailList], + [currentUserPersonalDetails.displayName, map, currencySharedValue, categorySharedValue, tagSharedValue, emailList], ); const inputWidth = isFullWidth ? styles.w100 : {width: variables.popoverWidth}; diff --git a/src/libs/SearchAutocompleteUtils.ts b/src/libs/SearchAutocompleteUtils.ts index fc5836a2c1da..e798e88a5514 100644 --- a/src/libs/SearchAutocompleteUtils.ts +++ b/src/libs/SearchAutocompleteUtils.ts @@ -143,7 +143,7 @@ function getAutocompleteQueryWithComma(prevQuery: string, newQuery: string) { function parseForLiveMarkdown( input: string, userLogins: string[], - details: SharedValue<{login: string | undefined; userDisplayName: string | undefined}>, + userDisplayName: string, map: SubstitutionMap, currencyList: SharedValue, categoryList: SharedValue, @@ -177,12 +177,7 @@ function parseForLiveMarkdown( .filter((range) => range.key !== CONST.SEARCH.SYNTAX_FILTER_KEYS.TAG || tagList.get().includes(range.value)) .map((range) => { let type = 'mention-user'; - if ( - (range.key === CONST.SEARCH.SYNTAX_FILTER_KEYS.TO || CONST.SEARCH.SYNTAX_FILTER_KEYS.FROM) && - (range.value === details.get().login || range.value === details.get().userDisplayName) - ) { - type = 'mention-here'; - } + if (range.key === 'syntax') { type = 'syntax'; } From d5acdeee562539eef52d46ef60ab8d9bcd220e64 Mon Sep 17 00:00:00 2001 From: 289Adam289 Date: Mon, 3 Feb 2025 17:14:44 +0100 Subject: [PATCH 06/16] make emailList shared value --- .../Search/SearchAutocompleteInput.tsx | 21 ++++++++++++++----- src/libs/SearchAutocompleteUtils.ts | 4 ++-- 2 files changed, 18 insertions(+), 7 deletions(-) diff --git a/src/components/Search/SearchAutocompleteInput.tsx b/src/components/Search/SearchAutocompleteInput.tsx index 6a3c9c8c5e5e..ab6ecd333dfe 100644 --- a/src/components/Search/SearchAutocompleteInput.tsx +++ b/src/components/Search/SearchAutocompleteInput.tsx @@ -118,6 +118,10 @@ function SearchAutocompleteInput( }, [activeWorkspaceID, allPoliciesTags]); const tagSharedValue = useSharedValue(tagAutocompleteList); + const [loginList] = useOnyx(ONYXKEYS.LOGIN_LIST); + const emailList = Object.keys(loginList ?? {}); + const emailListSharedValue = useSharedValue(emailList); + useEffect(() => { if (lastMap.current && !isEqual(lastMap.current, substitutionMap)) { lastMap.current = substitutionMap; @@ -125,11 +129,16 @@ function SearchAutocompleteInput( setMap(lastMap.current ?? {}); }, [substitutionMap, lastMap]); - const [loginList] = useOnyx(ONYXKEYS.LOGIN_LIST); - const emailList = Object.keys(loginList ?? {}); - const offlineMessage: string = isOffline && shouldShowOfflineMessage ? `${translate('common.youAppearToBeOffline')} ${translate('search.resultsAreLimited')}` : ''; + useEffect(() => { + runOnLiveMarkdownRuntime(() => { + 'worklet'; + + emailListSharedValue.set(emailList); + })(); + }, [emailList, emailListSharedValue]); + useEffect(() => { runOnLiveMarkdownRuntime(() => { 'worklet'; @@ -137,6 +146,7 @@ function SearchAutocompleteInput( currencySharedValue.set(currencyAutocompleteList); })(); }, [currencyAutocompleteList, currencySharedValue]); + useEffect(() => { runOnLiveMarkdownRuntime(() => { 'worklet'; @@ -144,6 +154,7 @@ function SearchAutocompleteInput( categorySharedValue.set(categoryAutocompleteList); })(); }, [categorySharedValue, categoryAutocompleteList]); + useEffect(() => { runOnLiveMarkdownRuntime(() => { 'worklet'; @@ -156,9 +167,9 @@ function SearchAutocompleteInput( (input: string) => { 'worklet'; - return parseForLiveMarkdown(input, emailList, currentUserPersonalDetails.displayName ?? '', map, currencySharedValue, categorySharedValue, tagSharedValue); + return parseForLiveMarkdown(input, currentUserPersonalDetails.displayName ?? '', map, emailListSharedValue, currencySharedValue, categorySharedValue, tagSharedValue); }, - [currentUserPersonalDetails.displayName, map, currencySharedValue, categorySharedValue, tagSharedValue, emailList], + [currentUserPersonalDetails.displayName, map, currencySharedValue, categorySharedValue, tagSharedValue, emailListSharedValue], ); const inputWidth = isFullWidth ? styles.w100 : {width: variables.popoverWidth}; diff --git a/src/libs/SearchAutocompleteUtils.ts b/src/libs/SearchAutocompleteUtils.ts index e798e88a5514..73b35ac0ed21 100644 --- a/src/libs/SearchAutocompleteUtils.ts +++ b/src/libs/SearchAutocompleteUtils.ts @@ -142,9 +142,9 @@ function getAutocompleteQueryWithComma(prevQuery: string, newQuery: string) { */ function parseForLiveMarkdown( input: string, - userLogins: string[], userDisplayName: string, map: SubstitutionMap, + userLogins: SharedValue, currencyList: SharedValue, categoryList: SharedValue, tagList: SharedValue, @@ -182,7 +182,7 @@ function parseForLiveMarkdown( type = 'syntax'; } - if ((range.key === CONST.SEARCH.SYNTAX_FILTER_KEYS.TO || CONST.SEARCH.SYNTAX_FILTER_KEYS.FROM) && (userLogins.includes(range.value) || range.value === userDisplayName)) { + if ((range.key === CONST.SEARCH.SYNTAX_FILTER_KEYS.TO || CONST.SEARCH.SYNTAX_FILTER_KEYS.FROM) && (userLogins.get().includes(range.value) || range.value === userDisplayName)) { type = 'mention-here'; } From 068f04c8aac3de91d1401979843f1c9f081ab56d Mon Sep 17 00:00:00 2001 From: 289Adam289 Date: Tue, 4 Feb 2025 13:07:29 +0100 Subject: [PATCH 07/16] fix changed files lint --- .../Search/SearchRouter/getUpdatedSubstitutionsMap.ts | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/components/Search/SearchRouter/getUpdatedSubstitutionsMap.ts b/src/components/Search/SearchRouter/getUpdatedSubstitutionsMap.ts index a681ae6030f0..fca4ad525600 100644 --- a/src/components/Search/SearchRouter/getUpdatedSubstitutionsMap.ts +++ b/src/components/Search/SearchRouter/getUpdatedSubstitutionsMap.ts @@ -1,5 +1,5 @@ import type {SearchAutocompleteQueryRange, SearchFilterKey} from '@components/Search/types'; -import * as parser from '@libs/SearchParser/autocompleteParser'; +import {parse} from '@libs/SearchParser/autocompleteParser'; import type {SubstitutionMap} from './getQueryWithSubstitutions'; const getSubstitutionsKey = (filterKey: SearchFilterKey, value: string) => `${filterKey}:${value}`; @@ -16,7 +16,7 @@ const getSubstitutionsKey = (filterKey: SearchFilterKey, value: string) => `${fi * return: {} */ function getUpdatedSubstitutionsMap(query: string, substitutions: SubstitutionMap): SubstitutionMap { - const parsedQuery = parser.parse(query) as {ranges: SearchAutocompleteQueryRange[]}; + const parsedQuery = parse(query) as {ranges: SearchAutocompleteQueryRange[]}; const searchAutocompleteQueryRanges = parsedQuery.ranges.filter((range) => range.key !== 'syntax'); From 62b16170858494acd9386bf6fb3d02e68584fde5 Mon Sep 17 00:00:00 2001 From: 289Adam289 Date: Tue, 4 Feb 2025 13:24:42 +0100 Subject: [PATCH 08/16] fix parser --- src/libs/SearchParser/searchParser.peggy | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/libs/SearchParser/searchParser.peggy b/src/libs/SearchParser/searchParser.peggy index a369b8aa754b..cfbf076e381c 100644 --- a/src/libs/SearchParser/searchParser.peggy +++ b/src/libs/SearchParser/searchParser.peggy @@ -98,7 +98,7 @@ freeTextFilter } else { word = value; } - return buildFilter("eq", "keyword", value); + return buildFilter("eq", "keyword", word); } standardFilter From 56a5c8dad6f7e2b40e3d668e2d44ba8834bff2af Mon Sep 17 00:00:00 2001 From: 289Adam289 Date: Tue, 4 Feb 2025 13:26:10 +0100 Subject: [PATCH 09/16] generate serach parser --- src/libs/SearchParser/searchParser.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/libs/SearchParser/searchParser.js b/src/libs/SearchParser/searchParser.js index cbae90c1461b..66846116ca35 100644 --- a/src/libs/SearchParser/searchParser.js +++ b/src/libs/SearchParser/searchParser.js @@ -324,7 +324,7 @@ function peg$parse(input, options) { } else { word = value; } - return buildFilter("eq", "keyword", value); + return buildFilter("eq", "keyword", word); }; var peg$f4 = function(field, op, values) { return buildFilter(op, field, values); From 568d42f85f32eb674d327acd95c82c40cac60bde Mon Sep 17 00:00:00 2001 From: 289Adam289 Date: Tue, 4 Feb 2025 13:56:36 +0100 Subject: [PATCH 10/16] fix types and remove unnecessary ref --- src/CONST.ts | 1 + src/components/Search/SearchAutocompleteInput.tsx | 9 ++++----- src/components/Search/types.ts | 3 +-- src/libs/SearchAutocompleteUtils.ts | 15 ++++++++------- 4 files changed, 14 insertions(+), 14 deletions(-) diff --git a/src/CONST.ts b/src/CONST.ts index 1250092cb910..e5e6c7593532 100755 --- a/src/CONST.ts +++ b/src/CONST.ts @@ -6136,6 +6136,7 @@ const CONST = { LOWER_THAN: 'lt', LOWER_THAN_OR_EQUAL_TO: 'lte', }, + SYNTAX_KEY: 'syntax', SYNTAX_ROOT_KEYS: { TYPE: 'type', STATUS: 'status', diff --git a/src/components/Search/SearchAutocompleteInput.tsx b/src/components/Search/SearchAutocompleteInput.tsx index ca828679cdfe..f59558f2740a 100644 --- a/src/components/Search/SearchAutocompleteInput.tsx +++ b/src/components/Search/SearchAutocompleteInput.tsx @@ -99,7 +99,6 @@ function SearchAutocompleteInput( const {isOffline} = useNetwork(); const {activeWorkspaceID} = useActiveWorkspace(); const currentUserPersonalDetails = useCurrentUserPersonalDetails(); - const lastMap = useRef({}); const [map, setMap] = useState({}); const [currencyList] = useOnyx(ONYXKEYS.CURRENCY_LIST); @@ -123,11 +122,11 @@ function SearchAutocompleteInput( const emailListSharedValue = useSharedValue(emailList); useEffect(() => { - if (lastMap.current && !isEqual(lastMap.current, substitutionMap)) { - lastMap.current = substitutionMap; + if (isEqual(map, substitutionMap)) { + return; } - setMap(lastMap.current ?? {}); - }, [substitutionMap, lastMap]); + setMap(substitutionMap); + }, [substitutionMap, map]); const offlineMessage: string = isOffline && shouldShowOfflineMessage ? `${translate('common.youAppearToBeOffline')} ${translate('search.resultsAreLimited')}` : ''; diff --git a/src/components/Search/types.ts b/src/components/Search/types.ts index b5c58c798b9f..b4c7d9d4dd20 100644 --- a/src/components/Search/types.ts +++ b/src/components/Search/types.ts @@ -96,7 +96,7 @@ type SearchFilterKey = | typeof CONST.SEARCH.SYNTAX_ROOT_KEYS.TYPE | typeof CONST.SEARCH.SYNTAX_ROOT_KEYS.STATUS | typeof CONST.SEARCH.SYNTAX_ROOT_KEYS.POLICY_ID - | 'syntax'; + | typeof CONST.SEARCH.SYNTAX_KEY; type UserFriendlyKey = ValueOf; @@ -129,7 +129,6 @@ type SearchAutocompleteResult = { ranges: SearchAutocompleteQueryRange[]; }; -// TODO FIX types type SearchAutocompleteQueryRange = { key: SearchFilterKey; length: number; diff --git a/src/libs/SearchAutocompleteUtils.ts b/src/libs/SearchAutocompleteUtils.ts index 73b35ac0ed21..e458b186b6a8 100644 --- a/src/libs/SearchAutocompleteUtils.ts +++ b/src/libs/SearchAutocompleteUtils.ts @@ -161,13 +161,14 @@ function parseForLiveMarkdown( return ranges .filter( (range) => - !( - range.key === CONST.SEARCH.SYNTAX_FILTER_KEYS.TO || - range.key === CONST.SEARCH.SYNTAX_FILTER_KEYS.FROM || - range.key === CONST.SEARCH.SYNTAX_FILTER_KEYS.IN || - range.key === CONST.SEARCH.SYNTAX_FILTER_KEYS.TAX_RATE || - range.key === CONST.SEARCH.SYNTAX_FILTER_KEYS.CARD_ID - ) || subMap[`${range.key}:${range.value}`] !== undefined, + !(range.key === CONST.SEARCH.SYNTAX_FILTER_KEYS.IN || range.key === CONST.SEARCH.SYNTAX_FILTER_KEYS.TAX_RATE || range.key === CONST.SEARCH.SYNTAX_FILTER_KEYS.CARD_ID) || + subMap[`${range.key}:${range.value}`] !== undefined, + ) + .filter( + (range) => + !(range.key === CONST.SEARCH.SYNTAX_FILTER_KEYS.TO || range.key === CONST.SEARCH.SYNTAX_FILTER_KEYS.FROM) || + subMap[`${range.key}:${range.value}`] !== undefined || + userLogins.get().includes(range.value), ) .filter((range) => range.key !== CONST.SEARCH.SYNTAX_FILTER_KEYS.CURRENCY || currencyList.get().includes(range.value)) .filter((range) => range.key !== CONST.SEARCH.SYNTAX_ROOT_KEYS.TYPE || typeList.includes(range.value)) From ee8297b9ca09e070be9c36ec14669188a9416fa0 Mon Sep 17 00:00:00 2001 From: 289Adam289 Date: Tue, 4 Feb 2025 15:30:42 +0100 Subject: [PATCH 11/16] fix tests for autocomplete parser --- .../Search/SearchAutocompleteInput.tsx | 2 +- tests/unit/SearchAutocompleteParserTest.ts | 42 +++++++++++++++++-- 2 files changed, 39 insertions(+), 5 deletions(-) diff --git a/src/components/Search/SearchAutocompleteInput.tsx b/src/components/Search/SearchAutocompleteInput.tsx index f59558f2740a..358b82d380a0 100644 --- a/src/components/Search/SearchAutocompleteInput.tsx +++ b/src/components/Search/SearchAutocompleteInput.tsx @@ -1,6 +1,6 @@ import isEqual from 'lodash/isEqual'; import type {ForwardedRef, ReactNode, RefObject} from 'react'; -import React, {forwardRef, useCallback, useEffect, useMemo, useRef, useState} from 'react'; +import React, {forwardRef, useCallback, useEffect, useMemo, useState} from 'react'; import {View} from 'react-native'; import type {StyleProp, TextInputProps, ViewStyle} from 'react-native'; import {useOnyx} from 'react-native-onyx'; diff --git a/tests/unit/SearchAutocompleteParserTest.ts b/tests/unit/SearchAutocompleteParserTest.ts index 995d23eaeff5..e9ef145ed9ad 100644 --- a/tests/unit/SearchAutocompleteParserTest.ts +++ b/tests/unit/SearchAutocompleteParserTest.ts @@ -13,7 +13,9 @@ const tests = [ length: 3, }, ranges: [ + {key: 'syntax', value: 'type', start: 0, length: 5}, {key: 'type', value: 'expense', start: 5, length: 7}, + {key: 'syntax', value: 'status', start: 13, length: 7}, {key: 'status', value: 'all', start: 20, length: 3}, ], }, @@ -23,8 +25,11 @@ const tests = [ expected: { autocomplete: null, ranges: [ + {key: 'syntax', value: 'taxRate', start: 0, length: 9}, {key: 'taxRate', value: 'rate1', start: 9, length: 5}, + {key: 'syntax', value: 'expenseType', start: 15, length: 13}, {key: 'expenseType', value: 'card', start: 28, length: 4}, + {key: 'syntax', value: 'cardID', start: 33, length: 5}, {key: 'cardID', value: 'Big Bank', start: 38, length: 10}, ], }, @@ -34,8 +39,11 @@ const tests = [ expected: { autocomplete: null, ranges: [ + {key: 'syntax', value: 'taxRate', start: 0, length: 8}, {key: 'taxRate', value: 'rate1', start: 8, length: 5}, + {key: 'syntax', value: 'expenseType', start: 14, length: 12}, {key: 'expenseType', value: 'card', start: 26, length: 4}, + {key: 'syntax', value: 'cardID', start: 31, length: 7}, {key: 'cardID', value: 'Big Bank', start: 38, length: 10}, ], }, @@ -50,8 +58,10 @@ const tests = [ value: 'meal & entertainment', }, ranges: [ + {key: 'syntax', value: 'expenseType', start: 11, length: 13}, {key: 'expenseType', length: 4, start: 24, value: 'cash'}, {key: 'expenseType', length: 4, start: 29, value: 'card'}, + {key: 'syntax', value: 'category', start: 80, length: 9}, {key: 'category', length: 6, start: 89, value: 'travel'}, {key: 'category', length: 5, start: 96, value: 'hotel'}, {key: 'category', length: 22, start: 102, value: 'meal & entertainment'}, @@ -68,8 +78,11 @@ const tests = [ value: 'a b', }, ranges: [ + {key: 'syntax', value: 'type', start: 0, length: 5}, {key: 'type', value: 'expense', start: 5, length: 7}, + {key: 'syntax', value: 'status', start: 13, length: 7}, {key: 'status', value: 'all', start: 20, length: 3}, + {key: 'syntax', value: 'category', start: 24, length: 9}, {key: 'category', value: 'a b', start: 33, length: 5}, ], }, @@ -92,7 +105,7 @@ const tests = [ query: 'tag:,,', expected: { autocomplete: null, - ranges: [], + ranges: [{key: 'syntax', value: 'tag', start: 0, length: 4}], }, }, { @@ -105,7 +118,9 @@ const tests = [ length: 3, }, ranges: [ + {key: 'syntax', value: 'in', start: 0, length: 3}, {key: 'in', value: '123456', start: 3, length: 6}, + {key: 'syntax', value: 'currency', start: 10, length: 9}, {key: 'currency', value: 'USD', start: 19, length: 3}, ], }, @@ -120,6 +135,7 @@ const tests = [ length: 4, }, ranges: [ + {key: 'syntax', value: 'tag', start: 0, length: 4}, {key: 'tag', value: 'aa', start: 4, length: 2}, {key: 'tag', value: 'bbb', start: 7, length: 3}, {key: 'tag', value: 'cccc', start: 11, length: 4}, @@ -135,7 +151,7 @@ const tests = [ start: 9, length: 0, }, - ranges: [], + ranges: [{key: 'syntax', value: 'category', start: 0, length: 9}], }, }, { @@ -147,7 +163,10 @@ const tests = [ start: 21, length: 0, }, - ranges: [{key: 'category', value: 'Advertising', start: 9, length: 11}], + ranges: [ + {key: 'syntax', value: 'category', start: 0, length: 9}, + {key: 'category', value: 'Advertising', start: 9, length: 11}, + ], }, }, { @@ -160,6 +179,7 @@ const tests = [ length: 12, }, ranges: [ + {key: 'syntax', value: 'in', start: 0, length: 3}, {key: 'in', value: 'Big Room', start: 3, length: 10}, {key: 'in', value: 'small room', start: 14, length: 12}, ], @@ -174,7 +194,10 @@ const tests = [ start: 12, length: 3, }, - ranges: [{key: 'category', value: 'Car', start: 12, length: 3}], + ranges: [ + {key: 'syntax', value: 'category', start: 0, length: 12}, + {key: 'category', value: 'Car', start: 12, length: 3}, + ], }, }, { @@ -182,7 +205,9 @@ const tests = [ expected: { autocomplete: null, ranges: [ + {key: 'syntax', value: 'type', start: 0, length: 5}, {key: 'type', value: 'expense', start: 5, length: 7}, + {key: 'syntax', value: 'status', start: 13, length: 7}, {key: 'status', value: 'all', start: 20, length: 3}, ], }, @@ -197,11 +222,16 @@ const tests = [ length: 4, }, ranges: [ + {key: 'syntax', value: 'in', start: 0, length: 3}, {key: 'in', value: 'Big Room', start: 3, length: 10}, + {key: 'syntax', value: 'from', start: 14, length: 5}, {key: 'from', value: 'Friend', start: 19, length: 6}, + {key: 'syntax', value: 'category', start: 26, length: 9}, {key: 'category', value: 'Car', start: 35, length: 3}, {key: 'category', value: 'Cell Phone', start: 39, length: 12}, + {key: 'syntax', value: 'status', start: 52, length: 7}, {key: 'status', value: 'all', start: 59, length: 3}, + {key: 'syntax', value: 'expenseType', start: 63, length: 13}, {key: 'expenseType', value: 'card', start: 76, length: 4}, {key: 'expenseType', value: 'cash', start: 81, length: 4}, ], @@ -217,11 +247,15 @@ const tests = [ length: 8, }, ranges: [ + {key: 'syntax', value: 'currency', start: 0, length: 9}, {key: 'currency', value: 'PLN', start: 9, length: 3}, {key: 'currency', value: 'USD', start: 13, length: 3}, + {key: 'syntax', value: 'taxRate', start: 25, length: 9}, {key: 'taxRate', value: 'tax', start: 34, length: 3}, + {key: 'syntax', value: 'tag', start: 66, length: 4}, {key: 'tag', value: 'General Overhead', start: 70, length: 18}, {key: 'tag', value: 'IT', start: 89, length: 2}, + {key: 'syntax', value: 'expenseType', start: 92, length: 13}, {key: 'expenseType', value: 'card', start: 105, length: 4}, {key: 'expenseType', value: 'distance', start: 110, length: 8}, ], From efb018a5445ef87564e51f85dc5bb2076c074541 Mon Sep 17 00:00:00 2001 From: 289Adam289 Date: Tue, 4 Feb 2025 16:03:56 +0100 Subject: [PATCH 12/16] fix types and perf-tests --- .../Search/SearchRouter/getQueryWithSubstitutions.ts | 3 ++- .../Search/SearchRouter/getUpdatedSubstitutionsMap.ts | 3 ++- src/components/Search/types.ts | 5 ++--- tests/perf-test/SearchRouter.perf-test.tsx | 1 + 4 files changed, 7 insertions(+), 5 deletions(-) diff --git a/src/components/Search/SearchRouter/getQueryWithSubstitutions.ts b/src/components/Search/SearchRouter/getQueryWithSubstitutions.ts index b976371135a6..4262f1df8529 100644 --- a/src/components/Search/SearchRouter/getQueryWithSubstitutions.ts +++ b/src/components/Search/SearchRouter/getQueryWithSubstitutions.ts @@ -1,10 +1,11 @@ import type {SearchAutocompleteQueryRange, SearchFilterKey} from '@components/Search/types'; import {parse} from '@libs/SearchParser/autocompleteParser'; import {sanitizeSearchValue} from '@libs/SearchQueryUtils'; +import type CONST from '@src/CONST'; type SubstitutionMap = Record; -const getSubstitutionMapKey = (filterKey: SearchFilterKey, value: string) => `${filterKey}:${value}`; +const getSubstitutionMapKey = (filterKey: SearchFilterKey | typeof CONST.SEARCH.SYNTAX_KEY, value: string) => `${filterKey}:${value}`; /** * Given a plaintext query and a SubstitutionMap object, this function will return a transformed query where: diff --git a/src/components/Search/SearchRouter/getUpdatedSubstitutionsMap.ts b/src/components/Search/SearchRouter/getUpdatedSubstitutionsMap.ts index fca4ad525600..ac9f8a7a807d 100644 --- a/src/components/Search/SearchRouter/getUpdatedSubstitutionsMap.ts +++ b/src/components/Search/SearchRouter/getUpdatedSubstitutionsMap.ts @@ -1,8 +1,9 @@ import type {SearchAutocompleteQueryRange, SearchFilterKey} from '@components/Search/types'; import {parse} from '@libs/SearchParser/autocompleteParser'; +import type CONST from '@src/CONST'; import type {SubstitutionMap} from './getQueryWithSubstitutions'; -const getSubstitutionsKey = (filterKey: SearchFilterKey, value: string) => `${filterKey}:${value}`; +const getSubstitutionsKey = (filterKey: SearchFilterKey | typeof CONST.SEARCH.SYNTAX_KEY, value: string) => `${filterKey}:${value}`; /** * Given a plaintext query and a SubstitutionMap object, diff --git a/src/components/Search/types.ts b/src/components/Search/types.ts index b4c7d9d4dd20..436cc33ad373 100644 --- a/src/components/Search/types.ts +++ b/src/components/Search/types.ts @@ -95,8 +95,7 @@ type SearchFilterKey = | ValueOf | typeof CONST.SEARCH.SYNTAX_ROOT_KEYS.TYPE | typeof CONST.SEARCH.SYNTAX_ROOT_KEYS.STATUS - | typeof CONST.SEARCH.SYNTAX_ROOT_KEYS.POLICY_ID - | typeof CONST.SEARCH.SYNTAX_KEY; + | typeof CONST.SEARCH.SYNTAX_ROOT_KEYS.POLICY_ID; type UserFriendlyKey = ValueOf; @@ -130,7 +129,7 @@ type SearchAutocompleteResult = { }; type SearchAutocompleteQueryRange = { - key: SearchFilterKey; + key: SearchFilterKey | typeof CONST.SEARCH.SYNTAX_KEY; length: number; start: number; value: string; diff --git a/tests/perf-test/SearchRouter.perf-test.tsx b/tests/perf-test/SearchRouter.perf-test.tsx index 089f08b14fb3..ea260eb4b83d 100644 --- a/tests/perf-test/SearchRouter.perf-test.tsx +++ b/tests/perf-test/SearchRouter.perf-test.tsx @@ -117,6 +117,7 @@ function SearchAutocompleteInputWrapper() { value={value} onSearchQueryChange={(searchTerm) => setValue(searchTerm)} isFullWidth={false} + substitutionMap={{}} /> ); From f56ae479fd5f2a8981db8f7809201a5eb04e7106 Mon Sep 17 00:00:00 2001 From: 289Adam289 Date: Wed, 5 Feb 2025 12:26:22 +0100 Subject: [PATCH 13/16] fix perf tests and changed files lint --- tests/perf-test/SearchRouter.perf-test.tsx | 5 +++++ tests/unit/SearchAutocompleteParserTest.ts | 4 ++-- 2 files changed, 7 insertions(+), 2 deletions(-) diff --git a/tests/perf-test/SearchRouter.perf-test.tsx b/tests/perf-test/SearchRouter.perf-test.tsx index ea260eb4b83d..44309ab5e720 100644 --- a/tests/perf-test/SearchRouter.perf-test.tsx +++ b/tests/perf-test/SearchRouter.perf-test.tsx @@ -69,6 +69,11 @@ jest.mock('@react-navigation/native', () => { }; }); +jest.mock('@libs/runOnLiveMarkdownRuntime', () => { + const runOnLiveMarkdownRuntime = (worklet: (...args: Args) => ReturnValue) => worklet; + return runOnLiveMarkdownRuntime; +}); + const getMockedReports = (length = 100) => createCollection( (item) => `${ONYXKEYS.COLLECTION.REPORT}${item.reportID}`, diff --git a/tests/unit/SearchAutocompleteParserTest.ts b/tests/unit/SearchAutocompleteParserTest.ts index e9ef145ed9ad..84fbf70b3aeb 100644 --- a/tests/unit/SearchAutocompleteParserTest.ts +++ b/tests/unit/SearchAutocompleteParserTest.ts @@ -1,5 +1,5 @@ import type {SearchQueryJSON} from '@components/Search/types'; -import * as autocompleteParser from '@libs/SearchParser/autocompleteParser'; +import {parse} from '@libs/SearchParser/autocompleteParser'; import parserCommonTests from '../utils/fixtures/searchParsersCommonQueries'; const tests = [ @@ -265,7 +265,7 @@ const tests = [ describe('autocomplete parser', () => { test.each(tests)(`parsing: $query`, ({query, expected}) => { - const result = autocompleteParser.parse(query) as SearchQueryJSON; + const result = parse(query) as SearchQueryJSON; expect(result).toEqual(expected); }); From 3e3221f25848e106cb5b5e14e6ce2145e171ecdf Mon Sep 17 00:00:00 2001 From: 289Adam289 Date: Wed, 5 Feb 2025 13:34:22 +0100 Subject: [PATCH 14/16] fix names and consts --- src/CONST.ts | 2 +- src/components/Search/SearchAutocompleteInput.tsx | 13 ++----------- src/components/Search/SearchPageHeaderInput.tsx | 5 ++++- src/components/Search/SearchRouter/SearchRouter.tsx | 5 ++++- .../Search/SearchRouter/buildSubstitutionsMap.ts | 2 +- .../SearchRouter/getQueryWithSubstitutions.ts | 8 ++++---- .../SearchRouter/getUpdatedSubstitutionsMap.ts | 8 ++++---- src/components/Search/types.ts | 5 ++++- src/libs/SearchAutocompleteUtils.ts | 4 ++-- src/libs/runOnLiveMarkdownRuntime/index.native.tsx | 2 +- tests/perf-test/SearchRouter.perf-test.tsx | 2 +- 11 files changed, 28 insertions(+), 28 deletions(-) diff --git a/src/CONST.ts b/src/CONST.ts index b687afe4c8fa..9c44e6539071 100755 --- a/src/CONST.ts +++ b/src/CONST.ts @@ -6189,7 +6189,7 @@ const CONST = { LOWER_THAN: 'lt', LOWER_THAN_OR_EQUAL_TO: 'lte', }, - SYNTAX_KEY: 'syntax', + SYNTAX_RANGE_NAME: 'syntax', SYNTAX_ROOT_KEYS: { TYPE: 'type', STATUS: 'status', diff --git a/src/components/Search/SearchAutocompleteInput.tsx b/src/components/Search/SearchAutocompleteInput.tsx index 84756b9806e9..a89c68364ad9 100644 --- a/src/components/Search/SearchAutocompleteInput.tsx +++ b/src/components/Search/SearchAutocompleteInput.tsx @@ -1,4 +1,3 @@ -import isEqual from 'lodash/isEqual'; import type {ForwardedRef, ReactNode, RefObject} from 'react'; import React, {forwardRef, useCallback, useEffect, useLayoutEffect, useMemo, useState} from 'react'; import {View} from 'react-native'; @@ -100,7 +99,6 @@ function SearchAutocompleteInput( const {isOffline} = useNetwork(); const {activeWorkspaceID} = useActiveWorkspace(); const currentUserPersonalDetails = useCurrentUserPersonalDetails(); - const [map, setMap] = useState({}); const [currencyList] = useOnyx(ONYXKEYS.CURRENCY_LIST); const currencyAutocompleteList = Object.keys(currencyList ?? {}); @@ -122,13 +120,6 @@ function SearchAutocompleteInput( const emailList = Object.keys(loginList ?? {}); const emailListSharedValue = useSharedValue(emailList); - useEffect(() => { - if (isEqual(map, substitutionMap)) { - return; - } - setMap(substitutionMap); - }, [substitutionMap, map]); - const offlineMessage: string = isOffline && shouldShowOfflineMessage ? `${translate('common.youAppearToBeOffline')} ${translate('search.resultsAreLimited')}` : ''; useEffect(() => { @@ -167,9 +158,9 @@ function SearchAutocompleteInput( (input: string) => { 'worklet'; - return parseForLiveMarkdown(input, currentUserPersonalDetails.displayName ?? '', map, emailListSharedValue, currencySharedValue, categorySharedValue, tagSharedValue); + return parseForLiveMarkdown(input, currentUserPersonalDetails.displayName ?? '', substitutionMap, emailListSharedValue, currencySharedValue, categorySharedValue, tagSharedValue); }, - [currentUserPersonalDetails.displayName, map, currencySharedValue, categorySharedValue, tagSharedValue, emailListSharedValue], + [currentUserPersonalDetails.displayName, substitutionMap, currencySharedValue, categorySharedValue, tagSharedValue, emailListSharedValue], ); const inputWidth = isFullWidth ? styles.w100 : {width: variables.popoverWidth}; diff --git a/src/components/Search/SearchPageHeaderInput.tsx b/src/components/Search/SearchPageHeaderInput.tsx index f763a22d6aeb..d39c58f9edea 100644 --- a/src/components/Search/SearchPageHeaderInput.tsx +++ b/src/components/Search/SearchPageHeaderInput.tsx @@ -1,4 +1,5 @@ import {useIsFocused} from '@react-navigation/native'; +import isEqual from 'lodash/isEqual'; import React, {useCallback, useEffect, useMemo, useRef, useState} from 'react'; import {View} from 'react-native'; import {useOnyx} from 'react-native-onyx'; @@ -123,7 +124,9 @@ function SearchPageHeaderInput({queryJSON, children}: SearchPageHeaderInputProps setAutocompleteQueryValue(updatedUserQuery); const updatedSubstitutionsMap = getUpdatedSubstitutionsMap(userQuery, autocompleteSubstitutions); - setAutocompleteSubstitutions(updatedSubstitutionsMap); + if (!isEqual(autocompleteSubstitutions, updatedSubstitutionsMap)) { + setAutocompleteSubstitutions(updatedSubstitutionsMap); + } if (updatedUserQuery) { listRef.current?.updateAndScrollToFocusedIndex(0); diff --git a/src/components/Search/SearchRouter/SearchRouter.tsx b/src/components/Search/SearchRouter/SearchRouter.tsx index 51b86812694c..6c6b4dc689d3 100644 --- a/src/components/Search/SearchRouter/SearchRouter.tsx +++ b/src/components/Search/SearchRouter/SearchRouter.tsx @@ -1,4 +1,5 @@ import {useNavigationState} from '@react-navigation/native'; +import isEqual from 'lodash/isEqual'; import React, {useCallback, useEffect, useRef, useState} from 'react'; import {View} from 'react-native'; import type {TextInputProps} from 'react-native'; @@ -185,7 +186,9 @@ function SearchRouter({onRouterClose, shouldHideInputCaret}: SearchRouterProps) setAutocompleteQueryValue(updatedUserQuery); const updatedSubstitutionsMap = getUpdatedSubstitutionsMap(userQuery, autocompleteSubstitutions); - setAutocompleteSubstitutions(updatedSubstitutionsMap); + if (!isEqual(autocompleteSubstitutions, updatedSubstitutionsMap)) { + setAutocompleteSubstitutions(updatedSubstitutionsMap); + } if (updatedUserQuery || textInputValue.length > 0) { listRef.current?.updateAndScrollToFocusedIndex(0); diff --git a/src/components/Search/SearchRouter/buildSubstitutionsMap.ts b/src/components/Search/SearchRouter/buildSubstitutionsMap.ts index 0a3c49819b38..1972a487c92f 100644 --- a/src/components/Search/SearchRouter/buildSubstitutionsMap.ts +++ b/src/components/Search/SearchRouter/buildSubstitutionsMap.ts @@ -33,7 +33,7 @@ function buildSubstitutionsMap( ): SubstitutionMap { const parsedQuery = parse(query) as {ranges: SearchAutocompleteQueryRange[]}; - const searchAutocompleteQueryRanges = parsedQuery.ranges.filter((range) => range.key !== 'syntax'); + const searchAutocompleteQueryRanges = parsedQuery.ranges.filter((range) => range.key !== CONST.SEARCH.SYNTAX_RANGE_NAME); if (searchAutocompleteQueryRanges.length === 0) { return {}; diff --git a/src/components/Search/SearchRouter/getQueryWithSubstitutions.ts b/src/components/Search/SearchRouter/getQueryWithSubstitutions.ts index 4262f1df8529..e20577b7df45 100644 --- a/src/components/Search/SearchRouter/getQueryWithSubstitutions.ts +++ b/src/components/Search/SearchRouter/getQueryWithSubstitutions.ts @@ -1,11 +1,11 @@ -import type {SearchAutocompleteQueryRange, SearchFilterKey} from '@components/Search/types'; +import type {SearchAutocompleteQueryRange, SearchAutocompleteQueryRangeKey} from '@components/Search/types'; import {parse} from '@libs/SearchParser/autocompleteParser'; import {sanitizeSearchValue} from '@libs/SearchQueryUtils'; -import type CONST from '@src/CONST'; +import CONST from '@src/CONST'; type SubstitutionMap = Record; -const getSubstitutionMapKey = (filterKey: SearchFilterKey | typeof CONST.SEARCH.SYNTAX_KEY, value: string) => `${filterKey}:${value}`; +const getSubstitutionMapKey = (filterKey: SearchAutocompleteQueryRangeKey, value: string) => `${filterKey}:${value}`; /** * Given a plaintext query and a SubstitutionMap object, this function will return a transformed query where: @@ -22,7 +22,7 @@ const getSubstitutionMapKey = (filterKey: SearchFilterKey | typeof CONST.SEARCH. function getQueryWithSubstitutions(changedQuery: string, substitutions: SubstitutionMap) { const parsed = parse(changedQuery) as {ranges: SearchAutocompleteQueryRange[]}; - const searchAutocompleteQueryRanges = parsed.ranges.filter((range) => range.key !== 'syntax'); + const searchAutocompleteQueryRanges = parsed.ranges.filter((range) => range.key !== CONST.SEARCH.SYNTAX_RANGE_NAME); if (searchAutocompleteQueryRanges.length === 0) { return changedQuery; diff --git a/src/components/Search/SearchRouter/getUpdatedSubstitutionsMap.ts b/src/components/Search/SearchRouter/getUpdatedSubstitutionsMap.ts index ac9f8a7a807d..aa37f84acc68 100644 --- a/src/components/Search/SearchRouter/getUpdatedSubstitutionsMap.ts +++ b/src/components/Search/SearchRouter/getUpdatedSubstitutionsMap.ts @@ -1,9 +1,9 @@ -import type {SearchAutocompleteQueryRange, SearchFilterKey} from '@components/Search/types'; +import type {SearchAutocompleteQueryRange, SearchAutocompleteQueryRangeKey} from '@components/Search/types'; import {parse} from '@libs/SearchParser/autocompleteParser'; -import type CONST from '@src/CONST'; +import CONST from '@src/CONST'; import type {SubstitutionMap} from './getQueryWithSubstitutions'; -const getSubstitutionsKey = (filterKey: SearchFilterKey | typeof CONST.SEARCH.SYNTAX_KEY, value: string) => `${filterKey}:${value}`; +const getSubstitutionsKey = (filterKey: SearchAutocompleteQueryRangeKey, value: string) => `${filterKey}:${value}`; /** * Given a plaintext query and a SubstitutionMap object, @@ -19,7 +19,7 @@ const getSubstitutionsKey = (filterKey: SearchFilterKey | typeof CONST.SEARCH.SY function getUpdatedSubstitutionsMap(query: string, substitutions: SubstitutionMap): SubstitutionMap { const parsedQuery = parse(query) as {ranges: SearchAutocompleteQueryRange[]}; - const searchAutocompleteQueryRanges = parsedQuery.ranges.filter((range) => range.key !== 'syntax'); + const searchAutocompleteQueryRanges = parsedQuery.ranges.filter((range) => range.key !== CONST.SEARCH.SYNTAX_RANGE_NAME); if (searchAutocompleteQueryRanges.length === 0) { return {}; diff --git a/src/components/Search/types.ts b/src/components/Search/types.ts index e716be38f9d2..887c70944660 100644 --- a/src/components/Search/types.ts +++ b/src/components/Search/types.ts @@ -98,6 +98,8 @@ type SearchFilterKey = | typeof CONST.SEARCH.SYNTAX_ROOT_KEYS.STATUS | typeof CONST.SEARCH.SYNTAX_ROOT_KEYS.POLICY_ID; +type SearchAutocompleteQueryRangeKey = SearchFilterKey | typeof CONST.SEARCH.SYNTAX_RANGE_NAME; + type UserFriendlyKey = ValueOf; type QueryFilters = Array<{ @@ -130,7 +132,7 @@ type SearchAutocompleteResult = { }; type SearchAutocompleteQueryRange = { - key: SearchFilterKey | typeof CONST.SEARCH.SYNTAX_KEY; + key: SearchAutocompleteQueryRangeKey; length: number; start: number; value: string; @@ -159,4 +161,5 @@ export type { SearchAutocompleteResult, PaymentData, SearchAutocompleteQueryRange, + SearchAutocompleteQueryRangeKey, }; diff --git a/src/libs/SearchAutocompleteUtils.ts b/src/libs/SearchAutocompleteUtils.ts index e458b186b6a8..aca441abd700 100644 --- a/src/libs/SearchAutocompleteUtils.ts +++ b/src/libs/SearchAutocompleteUtils.ts @@ -179,8 +179,8 @@ function parseForLiveMarkdown( .map((range) => { let type = 'mention-user'; - if (range.key === 'syntax') { - type = 'syntax'; + if (range.key === CONST.SEARCH.SYNTAX_RANGE_NAME) { + type = CONST.SEARCH.SYNTAX_RANGE_NAME; } if ((range.key === CONST.SEARCH.SYNTAX_FILTER_KEYS.TO || CONST.SEARCH.SYNTAX_FILTER_KEYS.FROM) && (userLogins.get().includes(range.value) || range.value === userDisplayName)) { diff --git a/src/libs/runOnLiveMarkdownRuntime/index.native.tsx b/src/libs/runOnLiveMarkdownRuntime/index.native.tsx index 8927fe0a936c..fe7bd2441cf3 100644 --- a/src/libs/runOnLiveMarkdownRuntime/index.native.tsx +++ b/src/libs/runOnLiveMarkdownRuntime/index.native.tsx @@ -1,7 +1,7 @@ import {getWorkletRuntime} from '@expensify/react-native-live-markdown'; import {runOnRuntime} from 'react-native-reanimated'; -function runOnLiveMarkdownRuntime(worklet: (...args: Args) => ReturnValue) { +function runOnLiveMarkdownRuntime(worklet: (...args: Args) => ReturnType) { return runOnRuntime(getWorkletRuntime(), worklet); } diff --git a/tests/perf-test/SearchRouter.perf-test.tsx b/tests/perf-test/SearchRouter.perf-test.tsx index 44309ab5e720..95807a12cd1b 100644 --- a/tests/perf-test/SearchRouter.perf-test.tsx +++ b/tests/perf-test/SearchRouter.perf-test.tsx @@ -122,7 +122,7 @@ function SearchAutocompleteInputWrapper() { value={value} onSearchQueryChange={(searchTerm) => setValue(searchTerm)} isFullWidth={false} - substitutionMap={{}} + substitutionMap={CONST.EMPTY_OBJECT} /> ); From 11b4639e707201e608fdede1faf1a11a3625e254 Mon Sep 17 00:00:00 2001 From: 289Adam289 Date: Wed, 5 Feb 2025 14:01:07 +0100 Subject: [PATCH 15/16] improve readability - extract logic from parser --- src/libs/SearchAutocompleteUtils.ts | 67 +++++++++++++++++++---------- 1 file changed, 44 insertions(+), 23 deletions(-) diff --git a/src/libs/SearchAutocompleteUtils.ts b/src/libs/SearchAutocompleteUtils.ts index aca441abd700..365134546c2b 100644 --- a/src/libs/SearchAutocompleteUtils.ts +++ b/src/libs/SearchAutocompleteUtils.ts @@ -2,7 +2,7 @@ import type {MarkdownRange} from '@expensify/react-native-live-markdown'; import type {OnyxCollection, OnyxEntry} from 'react-native-onyx'; import type {SharedValue} from 'react-native-reanimated/lib/typescript/commonTypes'; import type {SubstitutionMap} from '@components/Search/SearchRouter/getQueryWithSubstitutions'; -import type {SearchAutocompleteResult} from '@components/Search/types'; +import type {SearchAutocompleteQueryRange, SearchAutocompleteResult} from '@components/Search/types'; import CONST from '@src/CONST'; import ONYXKEYS from '@src/ONYXKEYS'; import type {Policy, PolicyCategories, PolicyTagLists, RecentlyUsedCategories, RecentlyUsedTags} from '@src/types/onyx'; @@ -135,6 +135,48 @@ function getAutocompleteQueryWithComma(prevQuery: string, newQuery: string) { return newQuery; } +function filterOutRangesWithCorrectValue( + range: SearchAutocompleteQueryRange, + userDisplayName: string, + substitutionMap: SubstitutionMap, + userLogins: SharedValue, + currencyList: SharedValue, + categoryList: SharedValue, + tagList: SharedValue, +) { + 'worklet'; + + const typeList = Object.values(CONST.SEARCH.DATA_TYPES) as string[]; + const expenseTypeList = Object.values(CONST.SEARCH.TRANSACTION_TYPE) as string[]; + const statusList = Object.values({...CONST.SEARCH.STATUS.TRIP, ...CONST.SEARCH.STATUS.INVOICE, ...CONST.SEARCH.STATUS.CHAT, ...CONST.SEARCH.STATUS.TRIP}) as string[]; + + switch (range.key) { + case CONST.SEARCH.SYNTAX_FILTER_KEYS.IN: + case CONST.SEARCH.SYNTAX_FILTER_KEYS.TAX_RATE: + case CONST.SEARCH.SYNTAX_FILTER_KEYS.CARD_ID: + return substitutionMap[`${range.key}:${range.value}`] !== undefined; + + case CONST.SEARCH.SYNTAX_FILTER_KEYS.TO: + case CONST.SEARCH.SYNTAX_FILTER_KEYS.FROM: + return substitutionMap[`${range.key}:${range.value}`] !== undefined || userLogins.get().includes(range.value); + + case CONST.SEARCH.SYNTAX_FILTER_KEYS.CURRENCY: + return currencyList.get().includes(range.value); + case CONST.SEARCH.SYNTAX_ROOT_KEYS.TYPE: + return typeList.includes(range.value); + case CONST.SEARCH.SYNTAX_FILTER_KEYS.EXPENSE_TYPE: + return expenseTypeList.includes(range.value); + case CONST.SEARCH.SYNTAX_ROOT_KEYS.STATUS: + return statusList.includes(range.value); + case CONST.SEARCH.SYNTAX_FILTER_KEYS.CATEGORY: + return categoryList.get().includes(range.value); + case CONST.SEARCH.SYNTAX_FILTER_KEYS.TAG: + return tagList.get().includes(range.value); + default: + return true; + } +} + /** * Parses input string using the autocomplete parser and returns array of * markdown ranges that can be used by RNMarkdownTextInput. @@ -153,29 +195,8 @@ function parseForLiveMarkdown( const parsedAutocomplete = parse(input) as SearchAutocompleteResult; const ranges = parsedAutocomplete.ranges; - - const typeList = Object.values(CONST.SEARCH.DATA_TYPES) as string[]; - const expenseTypeList = Object.values(CONST.SEARCH.TRANSACTION_TYPE) as string[]; - const statusList = Object.values({...CONST.SEARCH.STATUS.TRIP, ...CONST.SEARCH.STATUS.INVOICE, ...CONST.SEARCH.STATUS.CHAT, ...CONST.SEARCH.STATUS.TRIP}) as string[]; - const subMap = map; return ranges - .filter( - (range) => - !(range.key === CONST.SEARCH.SYNTAX_FILTER_KEYS.IN || range.key === CONST.SEARCH.SYNTAX_FILTER_KEYS.TAX_RATE || range.key === CONST.SEARCH.SYNTAX_FILTER_KEYS.CARD_ID) || - subMap[`${range.key}:${range.value}`] !== undefined, - ) - .filter( - (range) => - !(range.key === CONST.SEARCH.SYNTAX_FILTER_KEYS.TO || range.key === CONST.SEARCH.SYNTAX_FILTER_KEYS.FROM) || - subMap[`${range.key}:${range.value}`] !== undefined || - userLogins.get().includes(range.value), - ) - .filter((range) => range.key !== CONST.SEARCH.SYNTAX_FILTER_KEYS.CURRENCY || currencyList.get().includes(range.value)) - .filter((range) => range.key !== CONST.SEARCH.SYNTAX_ROOT_KEYS.TYPE || typeList.includes(range.value)) - .filter((range) => range.key !== CONST.SEARCH.SYNTAX_FILTER_KEYS.EXPENSE_TYPE || expenseTypeList.includes(range.value)) - .filter((range) => range.key !== CONST.SEARCH.SYNTAX_ROOT_KEYS.STATUS || statusList.includes(range.value)) - .filter((range) => range.key !== CONST.SEARCH.SYNTAX_FILTER_KEYS.CATEGORY || categoryList.get().includes(range.value)) - .filter((range) => range.key !== CONST.SEARCH.SYNTAX_FILTER_KEYS.TAG || tagList.get().includes(range.value)) + .filter((range) => filterOutRangesWithCorrectValue(range, userDisplayName, map, userLogins, currencyList, categoryList, tagList)) .map((range) => { let type = 'mention-user'; From 829f608354456e7f2295d7c2679c7da6bb030f74 Mon Sep 17 00:00:00 2001 From: 289Adam289 Date: Wed, 5 Feb 2025 14:24:42 +0100 Subject: [PATCH 16/16] fix type --- src/libs/runOnLiveMarkdownRuntime/index.tsx | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/libs/runOnLiveMarkdownRuntime/index.tsx b/src/libs/runOnLiveMarkdownRuntime/index.tsx index c8e072693323..928e7a34ea3f 100644 --- a/src/libs/runOnLiveMarkdownRuntime/index.tsx +++ b/src/libs/runOnLiveMarkdownRuntime/index.tsx @@ -1,5 +1,5 @@ // Reanimated does not support runOnRuntime() on web -function runOnLiveMarkdownRuntime(worklet: (...args: Args) => ReturnValue) { +function runOnLiveMarkdownRuntime(worklet: (...args: Args) => ReturnType) { return worklet; }