From 087e2a024b5e3908b7717a3aeeea4024c96e844a Mon Sep 17 00:00:00 2001 From: Someshwar Tripathi Date: Thu, 22 Aug 2024 00:42:21 +0530 Subject: [PATCH 01/31] Fix integration icons --- src/components/ButtonWithDropdownMenu/types.ts | 1 + src/components/MenuItem.tsx | 5 +++++ src/components/PopoverMenu.tsx | 1 + .../ReportActionItem/ExportWithDropdownMenu.tsx | 1 + src/libs/ReportUtils.ts | 13 ++++++++++--- src/styles/index.ts | 5 +++++ 6 files changed, 23 insertions(+), 3 deletions(-) diff --git a/src/components/ButtonWithDropdownMenu/types.ts b/src/components/ButtonWithDropdownMenu/types.ts index e4b81da94942..a0a7b92ed886 100644 --- a/src/components/ButtonWithDropdownMenu/types.ts +++ b/src/components/ButtonWithDropdownMenu/types.ts @@ -23,6 +23,7 @@ type DropdownOption = { iconWidth?: number; iconHeight?: number; iconDescription?: string; + additionalIconStyles?: StyleProp; onSelected?: () => void; disabled?: boolean; iconFill?: string; diff --git a/src/components/MenuItem.tsx b/src/components/MenuItem.tsx index 6757d0602691..906be5122872 100644 --- a/src/components/MenuItem.tsx +++ b/src/components/MenuItem.tsx @@ -118,6 +118,9 @@ type MenuItemBaseProps = { /** Any additional styles to pass to the icon container. */ iconStyles?: StyleProp; + /** Additional styles to pass to the icon itself */ + additionalIconStyles?: StyleProp; + /** A fallback avatar icon to display when there is an error on loading avatar from remote URL. */ fallbackIcon?: IconAsset; @@ -418,6 +421,7 @@ function MenuItem( tooltipShiftHorizontal = 0, tooltipShiftVertical = 0, renderTooltipContent, + additionalIconStyles, }: MenuItemProps, ref: PressableRef, ) { @@ -625,6 +629,7 @@ function MenuItem( isPaneMenu, ) } + additionalStyles={additionalIconStyles} /> ) : ( ): str function getIntegrationIcon(connectionName?: ConnectionName) { if (connectionName === CONST.POLICY.CONNECTIONS.NAME.XERO) { - return XeroCircle; + return XeroSquare; } if (connectionName === CONST.POLICY.CONNECTIONS.NAME.QBO) { - return QBOCircle; + return QBOSquare; } + if (connectionName === CONST.POLICY.CONNECTIONS.NAME.NETSUITE) { + return NetSuiteSquare; + } + if (connectionName === CONST.POLICY.CONNECTIONS.NAME.SAGE_INTACCT) { + return IntacctSquare; + } + return undefined; } diff --git a/src/styles/index.ts b/src/styles/index.ts index fe65e48bc4a1..195340e92350 100644 --- a/src/styles/index.ts +++ b/src/styles/index.ts @@ -5152,6 +5152,11 @@ const styles = (theme: ThemeColors) => marginLeft: 19, backgroundColor: theme.border, }, + + integrationIcon: { + overflow: 'hidden', + borderRadius: variables.buttonBorderRadius, + }, } satisfies Styles); type ThemeStyles = ReturnType; From da121447c65fa4d198021f11e85b406f01a1ff73 Mon Sep 17 00:00:00 2001 From: Someshwar Tripathi Date: Thu, 22 Aug 2024 02:29:11 +0530 Subject: [PATCH 02/31] Prettier fix --- src/libs/ReportUtils.ts | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/libs/ReportUtils.ts b/src/libs/ReportUtils.ts index 253d13159e10..b0f6593807c0 100644 --- a/src/libs/ReportUtils.ts +++ b/src/libs/ReportUtils.ts @@ -12,7 +12,7 @@ import type {SvgProps} from 'react-native-svg'; import type {OriginalMessageModifiedExpense} from 'src/types/onyx/OriginalMessage'; import type {TupleToUnion, ValueOf} from 'type-fest'; import type {FileObject} from '@components/AttachmentModal'; -import {FallbackAvatar, QBOSquare, XeroSquare, NetSuiteSquare, IntacctSquare} from '@components/Icon/Expensicons'; +import {FallbackAvatar, IntacctSquare, NetSuiteSquare, QBOSquare, XeroSquare} from '@components/Icon/Expensicons'; import * as defaultGroupAvatars from '@components/Icon/GroupDefaultAvatars'; import * as defaultWorkspaceAvatars from '@components/Icon/WorkspaceDefaultAvatars'; import type {MoneyRequestAmountInputProps} from '@components/MoneyRequestAmountInput'; @@ -7670,7 +7670,7 @@ function getIntegrationIcon(connectionName?: ConnectionName) { if (connectionName === CONST.POLICY.CONNECTIONS.NAME.SAGE_INTACCT) { return IntacctSquare; } - + return undefined; } From 2cf750ad3d363e8ae6fe6f5bfda5b9caf48b6ee7 Mon Sep 17 00:00:00 2001 From: Someshwar Tripathi Date: Tue, 27 Aug 2024 23:50:14 +0530 Subject: [PATCH 03/31] Increase icon size --- src/components/ReportActionItem/ExportWithDropdownMenu.tsx | 3 +++ src/styles/variables.ts | 1 + 2 files changed, 4 insertions(+) diff --git a/src/components/ReportActionItem/ExportWithDropdownMenu.tsx b/src/components/ReportActionItem/ExportWithDropdownMenu.tsx index 43f6f610e7df..e030fb12380f 100644 --- a/src/components/ReportActionItem/ExportWithDropdownMenu.tsx +++ b/src/components/ReportActionItem/ExportWithDropdownMenu.tsx @@ -12,6 +12,7 @@ import * as ReportActions from '@libs/actions/Report'; import * as PolicyUtils from '@libs/PolicyUtils'; import * as ReportUtils from '@libs/ReportUtils'; import type {ExportType} from '@pages/home/report/ReportDetailsExportPage'; +import variables from '@styles/variables'; import CONST from '@src/CONST'; import ONYXKEYS from '@src/ONYXKEYS'; import type {Policy, Report} from '@src/types/onyx'; @@ -55,6 +56,8 @@ function ExportWithDropdownMenu({ icon: iconToDisplay, disabled: !canBeExported, displayInDefaultIconColor: true, + iconWidth: variables.iconSizeMenuItem, + iconHeight: variables.iconSizeMenuItem, additionalIconStyles: styles.integrationIcon, }; const options = [ diff --git a/src/styles/variables.ts b/src/styles/variables.ts index c0c058352d00..021671c16985 100644 --- a/src/styles/variables.ts +++ b/src/styles/variables.ts @@ -83,6 +83,7 @@ export default { iconSizeExtraLarge: 40, iconSizeSuperLarge: 60, iconSizeUltraLarge: 80, + iconSizeMenuItem: 32, iconBottomBar: 24, sidebarAvatarSize: 28, iconHeader: 48, From 45b5c4ebcddb807403af31b1d82de9ab55eb91ad Mon Sep 17 00:00:00 2001 From: Someshwar Tripathi Date: Thu, 5 Sep 2024 04:48:25 +0530 Subject: [PATCH 04/31] fix lint --- src/styles/index.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/styles/index.ts b/src/styles/index.ts index 1362cc3fc94d..f36b276be583 100644 --- a/src/styles/index.ts +++ b/src/styles/index.ts @@ -5187,7 +5187,7 @@ const styles = (theme: ThemeColors) => integrationIcon: { overflow: 'hidden', borderRadius: variables.buttonBorderRadius, - } + }, importColumnCard: { backgroundColor: theme.cardBG, From ee74fe13b98d04da5c181d19eda73703c6740dae Mon Sep 17 00:00:00 2001 From: 289Adam289 Date: Wed, 16 Oct 2024 12:33:17 +0200 Subject: [PATCH 05/31] fix amount display value --- src/libs/SearchUtils.ts | 17 +++++++++++++++-- 1 file changed, 15 insertions(+), 2 deletions(-) diff --git a/src/libs/SearchUtils.ts b/src/libs/SearchUtils.ts index ef9f237bd551..662f66a7e27c 100644 --- a/src/libs/SearchUtils.ts +++ b/src/libs/SearchUtils.ts @@ -730,8 +730,12 @@ function buildFilterFormValuesFromQuery( filtersForm[FILTER_KEYS.DATE_AFTER] = filters[filterKey]?.find((filter) => filter.operator === 'gt' && ValidationUtils.isValidDate(filter.value.toString()))?.value.toString(); } if (filterKey === CONST.SEARCH.SYNTAX_FILTER_KEYS.AMOUNT) { - filtersForm[FILTER_KEYS.LESS_THAN] = filters[filterKey]?.find((filter) => filter.operator === 'lt' && validateAmount(filter.value.toString(), 2))?.value.toString(); - filtersForm[FILTER_KEYS.GREATER_THAN] = filters[filterKey]?.find((filter) => filter.operator === 'gt' && validateAmount(filter.value.toString(), 2))?.value.toString(); + filtersForm[FILTER_KEYS.LESS_THAN] = filters[filterKey] + ?.find((filter) => filter.operator === 'lt' && validateAmount(filter.value.toString(), 0, CONST.IOU.AMOUNT_MAX_LENGTH + 2)) + ?.value.toString(); + filtersForm[FILTER_KEYS.GREATER_THAN] = filters[filterKey] + ?.find((filter) => filter.operator === 'gt' && validateAmount(filter.value.toString(), 0, CONST.IOU.AMOUNT_MAX_LENGTH + 2)) + ?.value.toString(); } } @@ -779,6 +783,9 @@ function getDisplayValue(filterName: string, filter: string, personalDetails: On if (filterName === CONST.SEARCH.SYNTAX_FILTER_KEYS.IN) { return ReportUtils.getReportName(reports?.[`${ONYXKEYS.COLLECTION.REPORT}${filter}`]) || filter; } + if (filterName === CONST.SEARCH.SYNTAX_FILTER_KEYS.AMOUNT) { + return CurrencyUtils.convertToFrontendAmountAsString(Number(filter)); + } return filter; } @@ -921,6 +928,12 @@ function findIDFromDisplayValue(filterName: ValueOf CurrencyUtils.convertToBackendAmount(Number(amount)).toString()); + } return filter; } From d51f57d2dc079d18d42d2b80d0e8ae9fbd180937 Mon Sep 17 00:00:00 2001 From: 289Adam289 Date: Wed, 16 Oct 2024 13:13:30 +0200 Subject: [PATCH 06/31] improve validation performance --- src/libs/SearchUtils.ts | 36 ++++++++++++++++++++---------------- 1 file changed, 20 insertions(+), 16 deletions(-) diff --git a/src/libs/SearchUtils.ts b/src/libs/SearchUtils.ts index 662f66a7e27c..573fa7b2f721 100644 --- a/src/libs/SearchUtils.ts +++ b/src/libs/SearchUtils.ts @@ -670,32 +670,35 @@ function buildFilterFormValuesFromQuery( taxRates: Record, ) { const filters = queryJSON.flatFilters; - const filterKeys = Object.keys(filters); + const filterKeys = Object.values(CONST.SEARCH.SYNTAX_FILTER_KEYS); const filtersForm = {} as Partial; const policyID = queryJSON.policyID; for (const filterKey of filterKeys) { + const filterValues = filters[filterKey]?.map((item) => item.value.toString()); + if (filterKey === CONST.SEARCH.SYNTAX_FILTER_KEYS.REPORT_ID || filterKey === CONST.SEARCH.SYNTAX_FILTER_KEYS.MERCHANT || filterKey === CONST.SEARCH.SYNTAX_FILTER_KEYS.DESCRIPTION) { - filtersForm[filterKey] = filters[filterKey]?.[0]?.value.toString(); + filtersForm[filterKey] = filterValues?.[0]; } if (filterKey === CONST.SEARCH.SYNTAX_FILTER_KEYS.EXPENSE_TYPE) { - filtersForm[filterKey] = filters[filterKey] - ?.map((expenseType) => expenseType.value.toString()) - .filter((expenseType) => Object.values(CONST.SEARCH.TRANSACTION_TYPE).includes(expenseType as ValueOf)); + const validExpenseTypes = new Set(Object.values(CONST.SEARCH.TRANSACTION_TYPE)); + filtersForm[filterKey] = filterValues?.filter((expenseType) => validExpenseTypes.has(expenseType as ValueOf)); } if (filterKey === CONST.SEARCH.SYNTAX_FILTER_KEYS.CARD_ID) { - filtersForm[filterKey] = filters[filterKey]?.map((card) => card.value.toString()).filter((card) => Object.keys(cardList).includes(card)); + filtersForm[filterKey] = filterValues?.filter((card) => cardList[card]); } if (filterKey === CONST.SEARCH.SYNTAX_FILTER_KEYS.TAX_RATE) { - filtersForm[filterKey] = filters[filterKey]?.map((tax) => tax.value.toString()).filter((tax) => [...Object.values(taxRates)].flat().includes(tax)); + const allTaxRates = new Set(Object.values(taxRates).flat()); + filtersForm[filterKey] = filterValues?.filter((tax) => allTaxRates.has(tax)); } if (filterKey === CONST.SEARCH.SYNTAX_FILTER_KEYS.IN) { - filtersForm[filterKey] = filters[filterKey]?.map((report) => report.value.toString()).filter((id) => reports?.[`${ONYXKEYS.COLLECTION.REPORT}${id}`]); + filtersForm[filterKey] = filterValues?.filter((id) => reports?.[`${ONYXKEYS.COLLECTION.REPORT}${id}`]); } if (filterKey === CONST.SEARCH.SYNTAX_FILTER_KEYS.FROM || filterKey === CONST.SEARCH.SYNTAX_FILTER_KEYS.TO) { - filtersForm[filterKey] = filters[filterKey]?.map((id) => id.value.toString()).filter((id) => Object.keys(personalDetails).includes(id)); + filtersForm[filterKey] = filterValues?.filter((id) => personalDetails[id]); } if (filterKey === CONST.SEARCH.SYNTAX_FILTER_KEYS.CURRENCY) { - filtersForm[filterKey] = filters[filterKey]?.filter((currency) => Object.keys(currencyList).includes(currency.value.toString())).map((currency) => currency.value.toString()); + const validCurrency = new Set(Object.keys(currencyList)); + filtersForm[filterKey] = filterValues?.filter((currency) => validCurrency.has(currency)); } if (filterKey === CONST.SEARCH.SYNTAX_FILTER_KEYS.TAG) { const tags = policyID @@ -704,20 +707,21 @@ function buildFilterFormValuesFromQuery( .filter((item) => !!item) .map((tagList) => getTagNamesFromTagsLists(tagList ?? {})) .flat(); - filtersForm[filterKey] = filters[filterKey]?.map((tag) => tag.value.toString()).filter((name) => tags.includes(name)); + const uniqueTags = new Set(tags); + filtersForm[filterKey] = filterValues?.filter((name) => uniqueTags.has(name)); } if (filterKey === CONST.SEARCH.SYNTAX_FILTER_KEYS.CATEGORY) { const categories = policyID ? Object.values(policyCategories?.[`${ONYXKEYS.COLLECTION.POLICY_CATEGORIES}${policyID}`] ?? {}).map((category) => category.name) : Object.values(policyCategories ?? {}) - .map((xd) => Object.values(xd ?? {}).map((category) => category.name)) + .map((categoryList) => Object.values(categoryList ?? {}).map((category) => category.name)) .flat(); - filtersForm[filterKey] = filters[filterKey]?.map((category) => category.value.toString()).filter((name) => categories.includes(name)); + const uniqueCategories = new Set(categories); + filtersForm[filterKey] = filterValues?.filter((name) => uniqueCategories.has(name)); } if (filterKey === CONST.SEARCH.SYNTAX_FILTER_KEYS.KEYWORD) { - filtersForm[filterKey] = filters[filterKey] - ?.map((filter) => filter.value.toString()) - .map((filter) => { + filtersForm[filterKey] = filterValues + ?.map((filter) => { if (filter.includes(' ')) { return `"${filter}"`; } From 8acc21fa76a964a8eec80c22400a04d07a5a87cf Mon Sep 17 00:00:00 2001 From: 289Adam289 Date: Wed, 16 Oct 2024 13:20:39 +0200 Subject: [PATCH 07/31] fix parsin special characters bug --- src/libs/SearchParser/searchParser.js | 13 ++----------- src/libs/SearchParser/searchParser.peggy | 3 +-- 2 files changed, 3 insertions(+), 13 deletions(-) diff --git a/src/libs/SearchParser/searchParser.js b/src/libs/SearchParser/searchParser.js index 713996d0cf09..61105cddd156 100644 --- a/src/libs/SearchParser/searchParser.js +++ b/src/libs/SearchParser/searchParser.js @@ -209,7 +209,7 @@ function peg$parse(input, options) { var peg$r0 = /^[:=]/; var peg$r1 = /^[^"\r\n]/; - var peg$r2 = /^[A-Za-z0-9_@.\/#&+\-\\',;%]/; + var peg$r2 = /^[^ "\t\n\r]/; var peg$r3 = /^[ \t\r\n]/; var peg$e0 = peg$otherExpectation("operator"); @@ -245,7 +245,7 @@ function peg$parse(input, options) { var peg$e30 = peg$literalExpectation("\"", false); var peg$e31 = peg$classExpectation(["\"", "\r", "\n"], true, false); var peg$e32 = peg$otherExpectation("word"); - var peg$e33 = peg$classExpectation([["A", "Z"], ["a", "z"], ["0", "9"], "_", "@", ".", "/", "#", "&", "+", "-", "\\", "'", ",", ";", "%"], false, false); + var peg$e33 = peg$classExpectation([" ", "\"", "\t", "\n", "\r"], true, false); var peg$e34 = peg$otherExpectation("whitespace"); var peg$e35 = peg$classExpectation([" ", "\t", "\r", "\n"], false, false); @@ -848,15 +848,6 @@ function peg$parse(input, options) { s1 = peg$FAILED; if (peg$silentFails === 0) { peg$fail(peg$e22); } } - if (s1 === peg$FAILED) { - if (input.substr(peg$currPos, 2) === peg$c11) { - s1 = peg$c11; - peg$currPos += 2; - } else { - s1 = peg$FAILED; - if (peg$silentFails === 0) { peg$fail(peg$e14); } - } - } } } } diff --git a/src/libs/SearchParser/searchParser.peggy b/src/libs/SearchParser/searchParser.peggy index 32d44f24d0d6..81b6e74e5be8 100644 --- a/src/libs/SearchParser/searchParser.peggy +++ b/src/libs/SearchParser/searchParser.peggy @@ -119,7 +119,6 @@ key "key" / "cardID" / "from" / "expenseType" - / "in" ) defaultKey "default key" @@ -137,7 +136,7 @@ identifier quotedString "quote" = "\"" chars:[^"\r\n]* "\"" { return chars.join(""); } alphanumeric "word" - = chars:[A-Za-z0-9_@./#&+\-\\',;%]+ { + = chars:[^ "\t\n\r]+ { return chars.join("").trim().split(",").filter(Boolean); } From 9c1bd328f3d2fcca8a29e43b00fa4575c5e7ea5b Mon Sep 17 00:00:00 2001 From: Bernhard Owen Josephus Date: Thu, 17 Oct 2024 15:10:46 +0800 Subject: [PATCH 08/31] fix pressing enter doesn't select the emoji category --- .../EmojiPicker/EmojiPickerMenu/index.tsx | 15 ++++++--------- 1 file changed, 6 insertions(+), 9 deletions(-) diff --git a/src/components/EmojiPicker/EmojiPickerMenu/index.tsx b/src/components/EmojiPicker/EmojiPickerMenu/index.tsx index afcea4f3856a..99721bf8c649 100755 --- a/src/components/EmojiPicker/EmojiPickerMenu/index.tsx +++ b/src/components/EmojiPicker/EmojiPickerMenu/index.tsx @@ -119,6 +119,7 @@ function EmojiPickerMenu({onEmojiSelected, activeEmoji}: EmojiPickerMenuProps, r setFilteredEmojis(allEmojis); setHeaderIndices(headerRowIndices); setFocusedIndex(-1); + setHighlightFirstEmoji(false); setHighlightEmoji(false); return; } @@ -139,14 +140,6 @@ function EmojiPickerMenu({onEmojiSelected, activeEmoji}: EmojiPickerMenuProps, r return; } - if (!isEnterWhileComposition(keyBoardEvent) && keyBoardEvent.key === CONST.KEYBOARD_SHORTCUTS.ENTER.shortcutKey) { - // On web, avoid this Enter default input action; otherwise, it will add a new line in the subsequently focused composer. - keyBoardEvent.preventDefault(); - // On mWeb, avoid propagating this Enter keystroke to Pressable child component; otherwise, it will trigger the onEmojiSelected callback again. - keyBoardEvent.stopPropagation(); - return; - } - // Enable keyboard movement if tab or enter is pressed or if shift is pressed while the input // is not focused, so that the navigation and tab cycling can be done using the keyboard without // interfering with the input behaviour. @@ -182,9 +175,13 @@ function EmojiPickerMenu({onEmojiSelected, activeEmoji}: EmojiPickerMenuProps, r if ('types' in item || 'name' in item) { const emoji = typeof preferredSkinTone === 'number' && preferredSkinTone !== -1 && item?.types?.at(preferredSkinTone) ? item.types.at(preferredSkinTone) : item.code; onEmojiSelected(emoji ?? '', item); + // On web, avoid this Enter default input action; otherwise, it will add a new line in the subsequently focused composer. + keyBoardEvent.preventDefault(); + // On mWeb, avoid propagating this Enter keystroke to Pressable child component; otherwise, it will trigger the onEmojiSelected callback again. + keyBoardEvent.stopPropagation(); } }, - {shouldPreventDefault: true, shouldStopPropagation: true}, + {shouldPreventDefault: false}, ); /** From 9e475c043a3be55a8aa5995f958cb32b5a53fde6 Mon Sep 17 00:00:00 2001 From: 289Adam289 Date: Thu, 17 Oct 2024 15:23:18 +0200 Subject: [PATCH 09/31] fix reordering of standard filters --- src/components/Search/types.ts | 7 ++--- src/libs/SearchUtils.ts | 49 ++++++++++++++-------------------- 2 files changed, 24 insertions(+), 32 deletions(-) diff --git a/src/components/Search/types.ts b/src/components/Search/types.ts index 4f96090be9d0..d1920b0b6f61 100644 --- a/src/components/Search/types.ts +++ b/src/components/Search/types.ts @@ -58,9 +58,10 @@ type QueryFilter = { type AdvancedFiltersKeys = ValueOf; -type QueryFilters = { - [K in AdvancedFiltersKeys]?: QueryFilter[]; -}; +type QueryFilters = Array<{ + key: ValueOf; + filters: QueryFilter[]; +}>; type SearchQueryString = string; diff --git a/src/libs/SearchUtils.ts b/src/libs/SearchUtils.ts index 573fa7b2f721..4388529b098c 100644 --- a/src/libs/SearchUtils.ts +++ b/src/libs/SearchUtils.ts @@ -489,7 +489,7 @@ function sanitizeString(str: string) { * traverses the AST and returns filters as a QueryFilters object */ function getFilters(queryJSON: SearchQueryJSON) { - const filters = {} as QueryFilters; + const filters = [] as QueryFilters; const filterKeys = Object.values(CONST.SEARCH.SYNTAX_FILTER_KEYS); function traverse(node: ASTNode) { @@ -510,12 +510,8 @@ function getFilters(queryJSON: SearchQueryJSON) { return; } - if (!filters[nodeKey]) { - filters[nodeKey] = []; - } - // the "?? []" is added only for typescript because otherwise TS throws an error, in newer TS versions this should be fixed - const filterArray = filters[nodeKey] ?? []; + const filterArray = []; if (!Array.isArray(node.right)) { filterArray.push({ operator: node.operator, @@ -529,6 +525,7 @@ function getFilters(queryJSON: SearchQueryJSON) { }); }); } + filters.push({key: nodeKey, filters: filterArray}); } if (queryJSON.filters) { @@ -572,13 +569,9 @@ function buildSearchQueryString(queryJSON?: SearchQueryJSON) { const filters = queryJSON.flatFilters; - for (const [, filterKey] of Object.entries(CONST.SEARCH.SYNTAX_FILTER_KEYS)) { - const queryFilter = filters[filterKey]; - - if (queryFilter) { - const filterValueString = buildFilterString(filterKey, queryFilter); - queryParts.push(filterValueString); - } + for (const filter of filters) { + const filterValueString = buildFilterString(filter.key, filter.filters); + queryParts.push(filterValueString); } return queryParts.join(' '); @@ -670,14 +663,14 @@ function buildFilterFormValuesFromQuery( taxRates: Record, ) { const filters = queryJSON.flatFilters; - const filterKeys = Object.values(CONST.SEARCH.SYNTAX_FILTER_KEYS); const filtersForm = {} as Partial; const policyID = queryJSON.policyID; - for (const filterKey of filterKeys) { - const filterValues = filters[filterKey]?.map((item) => item.value.toString()); - + for (const queryFilter of filters) { + const filterKey = queryFilter.key; + const filterList = queryFilter.filters; + const filterValues = filterList.map((item) => item.value.toString()); if (filterKey === CONST.SEARCH.SYNTAX_FILTER_KEYS.REPORT_ID || filterKey === CONST.SEARCH.SYNTAX_FILTER_KEYS.MERCHANT || filterKey === CONST.SEARCH.SYNTAX_FILTER_KEYS.DESCRIPTION) { - filtersForm[filterKey] = filterValues?.[0]; + filtersForm[filterKey] = filterValues.at(0); } if (filterKey === CONST.SEARCH.SYNTAX_FILTER_KEYS.EXPENSE_TYPE) { const validExpenseTypes = new Set(Object.values(CONST.SEARCH.TRANSACTION_TYPE)); @@ -730,16 +723,12 @@ function buildFilterFormValuesFromQuery( .join(' '); } if (filterKey === CONST.SEARCH.SYNTAX_FILTER_KEYS.DATE) { - filtersForm[FILTER_KEYS.DATE_BEFORE] = filters[filterKey]?.find((filter) => filter.operator === 'lt' && ValidationUtils.isValidDate(filter.value.toString()))?.value.toString(); - filtersForm[FILTER_KEYS.DATE_AFTER] = filters[filterKey]?.find((filter) => filter.operator === 'gt' && ValidationUtils.isValidDate(filter.value.toString()))?.value.toString(); + filtersForm[FILTER_KEYS.DATE_BEFORE] = filterList.find((filter) => filter.operator === 'lt' && ValidationUtils.isValidDate(filter.value.toString()))?.value.toString(); + filtersForm[FILTER_KEYS.DATE_AFTER] = filterList.find((filter) => filter.operator === 'gt' && ValidationUtils.isValidDate(filter.value.toString()))?.value.toString(); } if (filterKey === CONST.SEARCH.SYNTAX_FILTER_KEYS.AMOUNT) { - filtersForm[FILTER_KEYS.LESS_THAN] = filters[filterKey] - ?.find((filter) => filter.operator === 'lt' && validateAmount(filter.value.toString(), 0, CONST.IOU.AMOUNT_MAX_LENGTH + 2)) - ?.value.toString(); - filtersForm[FILTER_KEYS.GREATER_THAN] = filters[filterKey] - ?.find((filter) => filter.operator === 'gt' && validateAmount(filter.value.toString(), 0, CONST.IOU.AMOUNT_MAX_LENGTH + 2)) - ?.value.toString(); + filtersForm[FILTER_KEYS.LESS_THAN] = filterList?.find((filter) => filter.operator === 'lt' && validateAmount(filter.value.toString(), 2))?.value.toString(); + filtersForm[FILTER_KEYS.GREATER_THAN] = filterList?.find((filter) => filter.operator === 'gt' && validateAmount(filter.value.toString(), 2))?.value.toString(); } } @@ -825,8 +814,10 @@ function getSearchHeaderTitle( let title = `type:${type} status:${status}`; - Object.keys(filters).forEach((key) => { - const queryFilter = filters[key as ValueOf] ?? []; + for (const filterObject of filters) { + const key = filterObject.key; + const queryFilter = filterObject.filters; + let displayQueryFilters: QueryFilter[] = []; if (key === CONST.SEARCH.SYNTAX_FILTER_KEYS.TAX_RATE) { const taxRateIDs = queryFilter.map((filter) => filter.value.toString()); @@ -850,7 +841,7 @@ function getSearchHeaderTitle( })); } title += buildFilterString(key, displayQueryFilters); - }); + } return title; } From b5f74d48cd60202313498c59e4a36a825a64acff Mon Sep 17 00:00:00 2001 From: 289Adam289 Date: Thu, 17 Oct 2024 16:18:22 +0200 Subject: [PATCH 10/31] fix amount NaN --- src/libs/SearchUtils.ts | 19 ++++++++++++++----- 1 file changed, 14 insertions(+), 5 deletions(-) diff --git a/src/libs/SearchUtils.ts b/src/libs/SearchUtils.ts index 4388529b098c..34f2ac3b2487 100644 --- a/src/libs/SearchUtils.ts +++ b/src/libs/SearchUtils.ts @@ -727,8 +727,12 @@ function buildFilterFormValuesFromQuery( filtersForm[FILTER_KEYS.DATE_AFTER] = filterList.find((filter) => filter.operator === 'gt' && ValidationUtils.isValidDate(filter.value.toString()))?.value.toString(); } if (filterKey === CONST.SEARCH.SYNTAX_FILTER_KEYS.AMOUNT) { - filtersForm[FILTER_KEYS.LESS_THAN] = filterList?.find((filter) => filter.operator === 'lt' && validateAmount(filter.value.toString(), 2))?.value.toString(); - filtersForm[FILTER_KEYS.GREATER_THAN] = filterList?.find((filter) => filter.operator === 'gt' && validateAmount(filter.value.toString(), 2))?.value.toString(); + filtersForm[FILTER_KEYS.LESS_THAN] = filterList + ?.find((filter) => filter.operator === 'lt' && validateAmount(filter.value.toString(), 0, CONST.IOU.AMOUNT_MAX_LENGTH + 2)) + ?.value.toString(); + filtersForm[FILTER_KEYS.GREATER_THAN] = filterList + ?.find((filter) => filter.operator === 'gt' && validateAmount(filter.value.toString(), 0, CONST.IOU.AMOUNT_MAX_LENGTH + 2)) + ?.value.toString(); } } @@ -777,7 +781,8 @@ function getDisplayValue(filterName: string, filter: string, personalDetails: On return ReportUtils.getReportName(reports?.[`${ONYXKEYS.COLLECTION.REPORT}${filter}`]) || filter; } if (filterName === CONST.SEARCH.SYNTAX_FILTER_KEYS.AMOUNT) { - return CurrencyUtils.convertToFrontendAmountAsString(Number(filter)); + const frontendAmount = CurrencyUtils.convertToFrontendAmountAsInteger(Number(filter)); + return Number.isNaN(frontendAmount) ? filter : frontendAmount.toString(); } return filter; } @@ -925,9 +930,13 @@ function findIDFromDisplayValue(filterName: ValueOf CurrencyUtils.convertToBackendAmount(Number(amount)).toString()); + return filter.map((amount) => { + const backendAmount = CurrencyUtils.convertToBackendAmount(Number(amount)); + return Number.isNaN(backendAmount) ? amount : backendAmount.toString(); + }); } return filter; } From 479d3cd2675b1e750df1f75f5bb3bcce319b0741 Mon Sep 17 00:00:00 2001 From: 289Adam289 Date: Thu, 17 Oct 2024 16:30:18 +0200 Subject: [PATCH 11/31] fix no category and no tag --- src/libs/SearchUtils.ts | 2 ++ 1 file changed, 2 insertions(+) diff --git a/src/libs/SearchUtils.ts b/src/libs/SearchUtils.ts index 34f2ac3b2487..5753c4dcb86e 100644 --- a/src/libs/SearchUtils.ts +++ b/src/libs/SearchUtils.ts @@ -701,6 +701,7 @@ function buildFilterFormValuesFromQuery( .map((tagList) => getTagNamesFromTagsLists(tagList ?? {})) .flat(); const uniqueTags = new Set(tags); + uniqueTags.add(CONST.SEARCH.EMPTY_VALUE); filtersForm[filterKey] = filterValues?.filter((name) => uniqueTags.has(name)); } if (filterKey === CONST.SEARCH.SYNTAX_FILTER_KEYS.CATEGORY) { @@ -710,6 +711,7 @@ function buildFilterFormValuesFromQuery( .map((categoryList) => Object.values(categoryList ?? {}).map((category) => category.name)) .flat(); const uniqueCategories = new Set(categories); + uniqueCategories.add(CONST.SEARCH.EMPTY_VALUE); filtersForm[filterKey] = filterValues?.filter((name) => uniqueCategories.has(name)); } if (filterKey === CONST.SEARCH.SYNTAX_FILTER_KEYS.KEYWORD) { From 4ca8fc07e4f90d05e1a32df0a4877f4ccd5fd39c Mon Sep 17 00:00:00 2001 From: 289Adam289 Date: Thu, 17 Oct 2024 16:51:48 +0200 Subject: [PATCH 12/31] remove no support --- src/libs/SearchUtils.ts | 2 -- 1 file changed, 2 deletions(-) diff --git a/src/libs/SearchUtils.ts b/src/libs/SearchUtils.ts index 5753c4dcb86e..34f2ac3b2487 100644 --- a/src/libs/SearchUtils.ts +++ b/src/libs/SearchUtils.ts @@ -701,7 +701,6 @@ function buildFilterFormValuesFromQuery( .map((tagList) => getTagNamesFromTagsLists(tagList ?? {})) .flat(); const uniqueTags = new Set(tags); - uniqueTags.add(CONST.SEARCH.EMPTY_VALUE); filtersForm[filterKey] = filterValues?.filter((name) => uniqueTags.has(name)); } if (filterKey === CONST.SEARCH.SYNTAX_FILTER_KEYS.CATEGORY) { @@ -711,7 +710,6 @@ function buildFilterFormValuesFromQuery( .map((categoryList) => Object.values(categoryList ?? {}).map((category) => category.name)) .flat(); const uniqueCategories = new Set(categories); - uniqueCategories.add(CONST.SEARCH.EMPTY_VALUE); filtersForm[filterKey] = filterValues?.filter((name) => uniqueCategories.has(name)); } if (filterKey === CONST.SEARCH.SYNTAX_FILTER_KEYS.KEYWORD) { From d27e193e20695e5ddd05ea60036d62758709ccb3 Mon Sep 17 00:00:00 2001 From: 289Adam289 Date: Thu, 17 Oct 2024 17:05:35 +0200 Subject: [PATCH 13/31] remove necesary optional chaining --- src/libs/SearchUtils.ts | 20 ++++++++++---------- 1 file changed, 10 insertions(+), 10 deletions(-) diff --git a/src/libs/SearchUtils.ts b/src/libs/SearchUtils.ts index 34f2ac3b2487..7724846cca73 100644 --- a/src/libs/SearchUtils.ts +++ b/src/libs/SearchUtils.ts @@ -674,24 +674,24 @@ function buildFilterFormValuesFromQuery( } if (filterKey === CONST.SEARCH.SYNTAX_FILTER_KEYS.EXPENSE_TYPE) { const validExpenseTypes = new Set(Object.values(CONST.SEARCH.TRANSACTION_TYPE)); - filtersForm[filterKey] = filterValues?.filter((expenseType) => validExpenseTypes.has(expenseType as ValueOf)); + filtersForm[filterKey] = filterValues.filter((expenseType) => validExpenseTypes.has(expenseType as ValueOf)); } if (filterKey === CONST.SEARCH.SYNTAX_FILTER_KEYS.CARD_ID) { - filtersForm[filterKey] = filterValues?.filter((card) => cardList[card]); + filtersForm[filterKey] = filterValues.filter((card) => cardList[card]); } if (filterKey === CONST.SEARCH.SYNTAX_FILTER_KEYS.TAX_RATE) { const allTaxRates = new Set(Object.values(taxRates).flat()); - filtersForm[filterKey] = filterValues?.filter((tax) => allTaxRates.has(tax)); + filtersForm[filterKey] = filterValues.filter((tax) => allTaxRates.has(tax)); } if (filterKey === CONST.SEARCH.SYNTAX_FILTER_KEYS.IN) { - filtersForm[filterKey] = filterValues?.filter((id) => reports?.[`${ONYXKEYS.COLLECTION.REPORT}${id}`]); + filtersForm[filterKey] = filterValues.filter((id) => reports?.[`${ONYXKEYS.COLLECTION.REPORT}${id}`]); } if (filterKey === CONST.SEARCH.SYNTAX_FILTER_KEYS.FROM || filterKey === CONST.SEARCH.SYNTAX_FILTER_KEYS.TO) { - filtersForm[filterKey] = filterValues?.filter((id) => personalDetails[id]); + filtersForm[filterKey] = filterValues.filter((id) => personalDetails[id]); } if (filterKey === CONST.SEARCH.SYNTAX_FILTER_KEYS.CURRENCY) { const validCurrency = new Set(Object.keys(currencyList)); - filtersForm[filterKey] = filterValues?.filter((currency) => validCurrency.has(currency)); + filtersForm[filterKey] = filterValues.filter((currency) => validCurrency.has(currency)); } if (filterKey === CONST.SEARCH.SYNTAX_FILTER_KEYS.TAG) { const tags = policyID @@ -701,7 +701,7 @@ function buildFilterFormValuesFromQuery( .map((tagList) => getTagNamesFromTagsLists(tagList ?? {})) .flat(); const uniqueTags = new Set(tags); - filtersForm[filterKey] = filterValues?.filter((name) => uniqueTags.has(name)); + filtersForm[filterKey] = filterValues.filter((name) => uniqueTags.has(name)); } if (filterKey === CONST.SEARCH.SYNTAX_FILTER_KEYS.CATEGORY) { const categories = policyID @@ -710,7 +710,7 @@ function buildFilterFormValuesFromQuery( .map((categoryList) => Object.values(categoryList ?? {}).map((category) => category.name)) .flat(); const uniqueCategories = new Set(categories); - filtersForm[filterKey] = filterValues?.filter((name) => uniqueCategories.has(name)); + filtersForm[filterKey] = filterValues.filter((name) => uniqueCategories.has(name)); } if (filterKey === CONST.SEARCH.SYNTAX_FILTER_KEYS.KEYWORD) { filtersForm[filterKey] = filterValues @@ -728,10 +728,10 @@ function buildFilterFormValuesFromQuery( } if (filterKey === CONST.SEARCH.SYNTAX_FILTER_KEYS.AMOUNT) { filtersForm[FILTER_KEYS.LESS_THAN] = filterList - ?.find((filter) => filter.operator === 'lt' && validateAmount(filter.value.toString(), 0, CONST.IOU.AMOUNT_MAX_LENGTH + 2)) + .find((filter) => filter.operator === 'lt' && validateAmount(filter.value.toString(), 0, CONST.IOU.AMOUNT_MAX_LENGTH + 2)) ?.value.toString(); filtersForm[FILTER_KEYS.GREATER_THAN] = filterList - ?.find((filter) => filter.operator === 'gt' && validateAmount(filter.value.toString(), 0, CONST.IOU.AMOUNT_MAX_LENGTH + 2)) + .find((filter) => filter.operator === 'gt' && validateAmount(filter.value.toString(), 0, CONST.IOU.AMOUNT_MAX_LENGTH + 2)) ?.value.toString(); } } From a81b7d0d5b7dc96a48baa64b6d1013d1e5f314cd Mon Sep 17 00:00:00 2001 From: 289Adam289 Date: Mon, 21 Oct 2024 16:45:36 +0200 Subject: [PATCH 14/31] add comment --- src/libs/SearchUtils.ts | 1 + 1 file changed, 1 insertion(+) diff --git a/src/libs/SearchUtils.ts b/src/libs/SearchUtils.ts index 7724846cca73..8600f5d58ef8 100644 --- a/src/libs/SearchUtils.ts +++ b/src/libs/SearchUtils.ts @@ -727,6 +727,7 @@ function buildFilterFormValuesFromQuery( filtersForm[FILTER_KEYS.DATE_AFTER] = filterList.find((filter) => filter.operator === 'gt' && ValidationUtils.isValidDate(filter.value.toString()))?.value.toString(); } if (filterKey === CONST.SEARCH.SYNTAX_FILTER_KEYS.AMOUNT) { + // backend amount is an integer and is 2 digit longer than frontend amount filtersForm[FILTER_KEYS.LESS_THAN] = filterList .find((filter) => filter.operator === 'lt' && validateAmount(filter.value.toString(), 0, CONST.IOU.AMOUNT_MAX_LENGTH + 2)) ?.value.toString(); From dec2439c9f4e45ef7802ebee987c0630bfc962b3 Mon Sep 17 00:00:00 2001 From: 289Adam289 Date: Tue, 22 Oct 2024 11:57:13 +0200 Subject: [PATCH 15/31] fix typecheck --- src/components/Search/types.ts | 2 +- src/libs/SearchUtils.ts | 21 +++++++++------------ 2 files changed, 10 insertions(+), 13 deletions(-) diff --git a/src/components/Search/types.ts b/src/components/Search/types.ts index d1920b0b6f61..5db88ae47e8d 100644 --- a/src/components/Search/types.ts +++ b/src/components/Search/types.ts @@ -59,7 +59,7 @@ type QueryFilter = { type AdvancedFiltersKeys = ValueOf; type QueryFilters = Array<{ - key: ValueOf; + key: AdvancedFiltersKeys; filters: QueryFilter[]; }>; diff --git a/src/libs/SearchUtils.ts b/src/libs/SearchUtils.ts index 071b5c7686d8..beec1b4eae16 100644 --- a/src/libs/SearchUtils.ts +++ b/src/libs/SearchUtils.ts @@ -1,7 +1,7 @@ import cloneDeep from 'lodash/cloneDeep'; import type {OnyxCollection} from 'react-native-onyx'; import type {ValueOf} from 'type-fest'; -import type {AdvancedFiltersKeys, ASTNode, QueryFilter, QueryFilters, SearchColumnType, SearchQueryJSON, SearchQueryString, SearchStatus, SortOrder} from '@components/Search/types'; +import type {ASTNode, QueryFilter, QueryFilters, SearchColumnType, SearchQueryJSON, SearchQueryString, SearchStatus, SortOrder} from '@components/Search/types'; import ChatListItem from '@components/SelectionList/ChatListItem'; import ReportListItem from '@components/SelectionList/Search/ReportListItem'; import TransactionListItem from '@components/SelectionList/Search/TransactionListItem'; @@ -18,6 +18,7 @@ import type SearchResults from '@src/types/onyx/SearchResults'; import type {ListItemDataType, ListItemType, SearchDataTypes, SearchPersonalDetails, SearchReport, SearchTransaction} from '@src/types/onyx/SearchResults'; import * as CurrencyUtils from './CurrencyUtils'; import DateUtils from './DateUtils'; +import localeCompare from './LocaleCompare'; import {translateLocal} from './Localize'; import {validateAmount} from './MoneyRequestUtils'; import Navigation from './Navigation/Navigation'; @@ -416,18 +417,14 @@ function getQueryHash(query: SearchQueryJSON): number { orderedQuery += ` ${CONST.SEARCH.SYNTAX_ROOT_KEYS.SORT_BY}:${query.sortBy}`; orderedQuery += ` ${CONST.SEARCH.SYNTAX_ROOT_KEYS.SORT_ORDER}:${query.sortOrder}`; - Object.keys(query.flatFilters) + query.flatFilters.forEach((filter) => { + filter.filters.sort((a, b) => localeCompare(a.value.toString(), b.value.toString())); + }); + + query.flatFilters + .map((filter) => buildFilterString(filter.key, filter.filters)) .sort() - .forEach((key) => { - const filterValues = query.flatFilters?.[key as AdvancedFiltersKeys]; - const sortedFilterValues = filterValues?.sort((queryFilter1, queryFilter2) => { - if (queryFilter1.value > queryFilter2.value) { - return 1; - } - return -1; - }); - orderedQuery += ` ${buildFilterString(key, sortedFilterValues ?? [])}`; - }); + .forEach((filterString) => (orderedQuery += ` ${filterString}`)); return UserUtils.hashText(orderedQuery, 2 ** 32); } From 5321fbb18c2d2811298f51cd24a89dabcf4a5dc6 Mon Sep 17 00:00:00 2001 From: Daniel Gale-Rosen Date: Tue, 22 Oct 2024 15:17:15 -0400 Subject: [PATCH 16/31] add delegate to every request if it exists --- src/libs/Network/enhanceParameters.ts | 13 +++++++++++++ 1 file changed, 13 insertions(+) diff --git a/src/libs/Network/enhanceParameters.ts b/src/libs/Network/enhanceParameters.ts index 01d2185a34c6..2bc9785aa168 100644 --- a/src/libs/Network/enhanceParameters.ts +++ b/src/libs/Network/enhanceParameters.ts @@ -20,6 +20,15 @@ Onyx.connect({ }, }); +// Check if the user is logged in as a delegate and send that if so +let delegate = ''; +Onyx.connect({ + key: ONYXKEYS.ACCOUNT, + callback: (val) => { + delegate = val?.delegatedAccess?.delegate ?? ''; + }, +}) + /** * Does this command require an authToken? */ @@ -57,5 +66,9 @@ export default function enhanceParameters(command: string, parameters: Record Date: Tue, 22 Oct 2024 15:23:34 -0400 Subject: [PATCH 17/31] prettier --- src/libs/Network/enhanceParameters.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/libs/Network/enhanceParameters.ts b/src/libs/Network/enhanceParameters.ts index 2bc9785aa168..1806726fdabb 100644 --- a/src/libs/Network/enhanceParameters.ts +++ b/src/libs/Network/enhanceParameters.ts @@ -27,7 +27,7 @@ Onyx.connect({ callback: (val) => { delegate = val?.delegatedAccess?.delegate ?? ''; }, -}) +}); /** * Does this command require an authToken? From 3096ed194fcc8b08cf18f37a7625412ce55fd823 Mon Sep 17 00:00:00 2001 From: 289Adam289 Date: Wed, 23 Oct 2024 09:48:01 +0200 Subject: [PATCH 18/31] fix bug with amount and date --- src/libs/SearchParser/baseRules.peggy | 2 +- src/libs/SearchUtils.ts | 22 +++++++++++++--------- 2 files changed, 14 insertions(+), 10 deletions(-) diff --git a/src/libs/SearchParser/baseRules.peggy b/src/libs/SearchParser/baseRules.peggy index 95d9b5f6c148..49f929bbe389 100644 --- a/src/libs/SearchParser/baseRules.peggy +++ b/src/libs/SearchParser/baseRules.peggy @@ -25,4 +25,4 @@ alphanumeric "word" logicalAnd = _ { return "and"; } -_ "whitespace" = [ \t\r\n]* \ No newline at end of file +_ "whitespace" = [ \t\r\n]* diff --git a/src/libs/SearchUtils.ts b/src/libs/SearchUtils.ts index e9f2bb437b15..fd07193ceecf 100644 --- a/src/libs/SearchUtils.ts +++ b/src/libs/SearchUtils.ts @@ -684,11 +684,13 @@ function buildFilterFormValuesFromQuery( taxRates: Record, ) { const filters = queryJSON.flatFilters; + console.log(filters); const filtersForm = {} as Partial; const policyID = queryJSON.policyID; for (const queryFilter of filters) { const filterKey = queryFilter.key; const filterList = queryFilter.filters; + console.log(filterKey, filterList); const filterValues = filterList.map((item) => item.value.toString()); if (filterKey === CONST.SEARCH.SYNTAX_FILTER_KEYS.REPORT_ID || filterKey === CONST.SEARCH.SYNTAX_FILTER_KEYS.MERCHANT || filterKey === CONST.SEARCH.SYNTAX_FILTER_KEYS.DESCRIPTION) { filtersForm[filterKey] = filterValues.at(0); @@ -746,17 +748,19 @@ function buildFilterFormValuesFromQuery( .join(' '); } if (filterKey === CONST.SEARCH.SYNTAX_FILTER_KEYS.DATE) { - filtersForm[FILTER_KEYS.DATE_BEFORE] = filterList.find((filter) => filter.operator === 'lt' && ValidationUtils.isValidDate(filter.value.toString()))?.value.toString(); - filtersForm[FILTER_KEYS.DATE_AFTER] = filterList.find((filter) => filter.operator === 'gt' && ValidationUtils.isValidDate(filter.value.toString()))?.value.toString(); + filtersForm[FILTER_KEYS.DATE_BEFORE] = + filterList.find((filter) => filter.operator === 'lt' && ValidationUtils.isValidDate(filter.value.toString()))?.value.toString() ?? filtersForm[FILTER_KEYS.DATE_BEFORE]; + filtersForm[FILTER_KEYS.DATE_AFTER] = + filterList.find((filter) => filter.operator === 'gt' && ValidationUtils.isValidDate(filter.value.toString()))?.value.toString() ?? filtersForm[FILTER_KEYS.DATE_AFTER]; } if (filterKey === CONST.SEARCH.SYNTAX_FILTER_KEYS.AMOUNT) { - // backend amount is an integer and is 2 digit longer than frontend amount - filtersForm[FILTER_KEYS.LESS_THAN] = filterList - .find((filter) => filter.operator === 'lt' && validateAmount(filter.value.toString(), 0, CONST.IOU.AMOUNT_MAX_LENGTH + 2)) - ?.value.toString(); - filtersForm[FILTER_KEYS.GREATER_THAN] = filterList - .find((filter) => filter.operator === 'gt' && validateAmount(filter.value.toString(), 0, CONST.IOU.AMOUNT_MAX_LENGTH + 2)) - ?.value.toString(); + // backend amount is an integer and is 2 digits longer than frontend amount + filtersForm[FILTER_KEYS.LESS_THAN] = + filterList.find((filter) => filter.operator === 'lt' && validateAmount(filter.value.toString(), 0, CONST.IOU.AMOUNT_MAX_LENGTH + 2))?.value.toString() ?? + filtersForm[FILTER_KEYS.LESS_THAN]; + filtersForm[FILTER_KEYS.GREATER_THAN] = + filterList.find((filter) => filter.operator === 'gt' && validateAmount(filter.value.toString(), 0, CONST.IOU.AMOUNT_MAX_LENGTH + 2))?.value.toString() ?? + filtersForm[FILTER_KEYS.GREATER_THAN]; } } From e746d5598efc69bc15e277f1e7e0136ce9dd98e9 Mon Sep 17 00:00:00 2001 From: 289Adam289 Date: Wed, 23 Oct 2024 10:53:43 +0200 Subject: [PATCH 19/31] fix imports --- src/libs/SearchQueryUtils.ts | 14 ++------------ 1 file changed, 2 insertions(+), 12 deletions(-) diff --git a/src/libs/SearchQueryUtils.ts b/src/libs/SearchQueryUtils.ts index c459190440d4..51db9fd56ea6 100644 --- a/src/libs/SearchQueryUtils.ts +++ b/src/libs/SearchQueryUtils.ts @@ -1,25 +1,15 @@ import cloneDeep from 'lodash/cloneDeep'; import type {OnyxCollection} from 'react-native-onyx'; import type {ValueOf} from 'type-fest'; -import type {ASTNode, QueryFilter, QueryFilters, SearchColumnType, SearchQueryJSON, SearchQueryString, SearchStatus, SortOrder} from '@components/Search/types'; -import ChatListItem from '@components/SelectionList/ChatListItem'; -import ReportListItem from '@components/SelectionList/Search/ReportListItem'; -import TransactionListItem from '@components/SelectionList/Search/TransactionListItem'; -import type {ListItem, ReportActionListItemType, ReportListItemType, TransactionListItemType} from '@components/SelectionList/types'; -import * as Expensicons from '@src/components/Icon/Expensicons'; -import type {AdvancedFiltersKeys, ASTNode, QueryFilter, QueryFilters, SearchQueryJSON, SearchQueryString, SearchStatus} from '@components/Search/types'; +import type {ASTNode, QueryFilter, QueryFilters, SearchQueryJSON, SearchQueryString, SearchStatus} from '@components/Search/types'; import CONST from '@src/CONST'; import ONYXKEYS from '@src/ONYXKEYS'; import type {SearchAdvancedFiltersForm} from '@src/types/form'; import FILTER_KEYS from '@src/types/form/SearchAdvancedFiltersForm'; import type * as OnyxTypes from '@src/types/onyx'; -import type SearchResults from '@src/types/onyx/SearchResults'; -import type {ListItemDataType, ListItemType, SearchDataTypes, SearchPersonalDetails, SearchReport, SearchTransaction} from '@src/types/onyx/SearchResults'; +import type {SearchDataTypes} from '@src/types/onyx/SearchResults'; import * as CurrencyUtils from './CurrencyUtils'; -import DateUtils from './DateUtils'; import localeCompare from './LocaleCompare'; -import {translateLocal} from './Localize'; -import type {SearchDataTypes} from '@src/types/onyx/SearchResults'; import {validateAmount} from './MoneyRequestUtils'; import * as PersonalDetailsUtils from './PersonalDetailsUtils'; import {getTagNamesFromTagsLists} from './PolicyUtils'; From f91cafa52b5b45db741fe9103eeae7d655ecdfbc Mon Sep 17 00:00:00 2001 From: I Nyoman Jyotisa Date: Wed, 23 Oct 2024 23:06:45 +0800 Subject: [PATCH 20/31] Update the Onboarding steps for 'Chat and split bills with friends' --- src/CONST.ts | 9 +++++---- 1 file changed, 5 insertions(+), 4 deletions(-) diff --git a/src/CONST.ts b/src/CONST.ts index 440f942e1244..67255981e6ba 100755 --- a/src/CONST.ts +++ b/src/CONST.ts @@ -4825,10 +4825,11 @@ const CONST = { '\n' + 'Here’s how to request money:\n' + '\n' + - '1. Click the green *+* button.\n' + - '2. Choose *Split expense*.\n' + - '3. Scan a receipt or enter an amount.\n' + - '4. Add your friend(s) to the request.\n' + + '1. Hit the green *+* button.\n' + + '2. Choose *Start chat*.\n' + + '3. Enter any email, SMS, or name of who you want to split with.\n' + + '4. From within the chat, hit the *+* button on the message bar, and hit *Split expense*.\n' + + '5. Create the expense by selecting Manual, Scan or Distance.\n' + '\n' + 'Feel free to add more details if you want, or just send it off. Let’s get you paid back!', }, From 77e6643170222883b01501906b7144adac3bd529 Mon Sep 17 00:00:00 2001 From: kirillzyusko Date: Wed, 23 Oct 2024 19:05:35 +0200 Subject: [PATCH 21/31] e2e: properly detect baseline commit (when running from PR) --- .github/workflows/e2ePerformanceTests.yml | 12 ++++++++++-- 1 file changed, 10 insertions(+), 2 deletions(-) diff --git a/.github/workflows/e2ePerformanceTests.yml b/.github/workflows/e2ePerformanceTests.yml index b48c7b2175eb..c92ab83d1178 100644 --- a/.github/workflows/e2ePerformanceTests.yml +++ b/.github/workflows/e2ePerformanceTests.yml @@ -35,8 +35,16 @@ jobs: - name: Determine "baseline ref" (prev merge commit) id: getBaselineRef run: | - previous_merge=$(git rev-list --merges HEAD~1 | head -n 1) - git checkout "$previous_merge" + # Get the name of the current branch + current_branch=$(git rev-parse --abbrev-ref HEAD) + + if [ "$current_branch" = "main" ]; then + # On the main branch, find the previous merge commit + previous_merge=$(git rev-list --merges HEAD~1 | head -n 1) + else + # On a feature branch, find the common ancestor of the current branch and main + previous_merge=$(git merge-base HEAD main) + fi echo "$previous_merge" echo "BASELINE_REF=$previous_merge" >> "$GITHUB_OUTPUT" From fcb3dfe9bf65322cab0e6b5dde806f7ebc45c1fb Mon Sep 17 00:00:00 2001 From: Bernhard Owen Josephus Date: Thu, 24 Oct 2024 12:17:32 +0800 Subject: [PATCH 22/31] fix message is moving when hovered --- .../utils/generators/ReportActionContextMenuStyleUtils.ts | 2 -- 1 file changed, 2 deletions(-) diff --git a/src/styles/utils/generators/ReportActionContextMenuStyleUtils.ts b/src/styles/utils/generators/ReportActionContextMenuStyleUtils.ts index 1894f95a44c1..0bf999e7a8d6 100644 --- a/src/styles/utils/generators/ReportActionContextMenuStyleUtils.ts +++ b/src/styles/utils/generators/ReportActionContextMenuStyleUtils.ts @@ -19,8 +19,6 @@ const getMiniWrapperStyle = (theme: ThemeColors, styles: ThemeStyles): ViewStyle borderRadius: variables.buttonBorderRadius, borderWidth: 1, borderColor: theme.border, - // In Safari, when welcome messages use a code block (triple backticks), they would overlap the context menu below when there is no scrollbar without the transform style. - transform: 'translateZ(0)', }, ]; From 8cd287d40a814bf48105d1bf618b8cce1f201137 Mon Sep 17 00:00:00 2001 From: Bernhard Owen Josephus Date: Thu, 24 Oct 2024 13:48:41 +0800 Subject: [PATCH 23/31] set the header as focused index --- src/components/EmojiPicker/EmojiPickerMenu/index.tsx | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/src/components/EmojiPicker/EmojiPickerMenu/index.tsx b/src/components/EmojiPicker/EmojiPickerMenu/index.tsx index 99721bf8c649..a019c9a6404a 100755 --- a/src/components/EmojiPicker/EmojiPickerMenu/index.tsx +++ b/src/components/EmojiPicker/EmojiPickerMenu/index.tsx @@ -51,6 +51,7 @@ function EmojiPickerMenu({onEmojiSelected, activeEmoji}: EmojiPickerMenuProps, r emojiListRef, spacersIndexes, } = useEmojiPickerMenu(); + console.log('header', headerEmojis, headerRowIndices) // Ref for the emoji search input const searchInputRef = useRef(null); @@ -236,8 +237,9 @@ function EmojiPickerMenu({onEmojiSelected, activeEmoji}: EmojiPickerMenuProps, r const calculatedOffset = Math.floor(headerIndex / CONST.EMOJI_NUM_PER_ROW) * CONST.EMOJI_PICKER_HEADER_HEIGHT; emojiListRef.current?.scrollToOffset({offset: calculatedOffset, animated: true}); + setFocusedIndex(headerIndex); }, - [emojiListRef], + [emojiListRef, setFocusedIndex], ); /** From 668973cfaeeabfa91aaa7b1f2f98f0e2fd9dd12f Mon Sep 17 00:00:00 2001 From: Bernhard Owen Josephus Date: Thu, 24 Oct 2024 13:51:12 +0800 Subject: [PATCH 24/31] remove console --- src/components/EmojiPicker/EmojiPickerMenu/index.tsx | 1 - 1 file changed, 1 deletion(-) diff --git a/src/components/EmojiPicker/EmojiPickerMenu/index.tsx b/src/components/EmojiPicker/EmojiPickerMenu/index.tsx index a019c9a6404a..e79863c7a527 100755 --- a/src/components/EmojiPicker/EmojiPickerMenu/index.tsx +++ b/src/components/EmojiPicker/EmojiPickerMenu/index.tsx @@ -51,7 +51,6 @@ function EmojiPickerMenu({onEmojiSelected, activeEmoji}: EmojiPickerMenuProps, r emojiListRef, spacersIndexes, } = useEmojiPickerMenu(); - console.log('header', headerEmojis, headerRowIndices) // Ref for the emoji search input const searchInputRef = useRef(null); From d3f99c199aa24a6949749ca9974aacf65bfe7772 Mon Sep 17 00:00:00 2001 From: 289Adam289 Date: Thu, 24 Oct 2024 18:30:32 +0200 Subject: [PATCH 25/31] fix issues with quotes --- src/libs/SearchParser/autocompleteParser.js | 270 ++++++++++++------ .../SearchParser/autocompleteParser.peggy | 23 +- src/libs/SearchParser/baseRules.peggy | 10 +- src/libs/SearchParser/searchParser.js | 240 ++++++++++------ src/libs/SearchParser/searchParser.peggy | 11 +- 5 files changed, 358 insertions(+), 196 deletions(-) diff --git a/src/libs/SearchParser/autocompleteParser.js b/src/libs/SearchParser/autocompleteParser.js index ed4a4e4e1afe..5fc13ac07f17 100644 --- a/src/libs/SearchParser/autocompleteParser.js +++ b/src/libs/SearchParser/autocompleteParser.js @@ -185,17 +185,19 @@ function peg$parse(input, options) { var peg$c7 = "expenseType"; var peg$c8 = "type"; var peg$c9 = "status"; - var peg$c10 = "!="; - var peg$c11 = ">="; - var peg$c12 = ">"; - var peg$c13 = "<="; - var peg$c14 = "<"; - var peg$c15 = "\""; + var peg$c10 = ","; + var peg$c11 = "!="; + var peg$c12 = ">="; + var peg$c13 = ">"; + var peg$c14 = "<="; + var peg$c15 = "<"; + var peg$c16 = "\""; var peg$r0 = /^[:=]/; - var peg$r1 = /^[^"\r\n]/; - var peg$r2 = /^[^ "\t\n\r]/; - var peg$r3 = /^[ \t\r\n]/; + var peg$r1 = /^[^ ,"\t\n\r]/; + var peg$r2 = /^[^"\r\n]/; + var peg$r3 = /^[^ ,\t\n\r]/; + var peg$r4 = /^[ \t\r\n]/; var peg$e0 = peg$otherExpectation("key"); var peg$e1 = peg$literalExpectation("in", false); @@ -208,20 +210,22 @@ function peg$parse(input, options) { var peg$e8 = peg$literalExpectation("expenseType", false); var peg$e9 = peg$literalExpectation("type", false); var peg$e10 = peg$literalExpectation("status", false); - var peg$e11 = peg$otherExpectation("operator"); - var peg$e12 = peg$classExpectation([":", "="], false, false); - var peg$e13 = peg$literalExpectation("!=", false); - var peg$e14 = peg$literalExpectation(">=", false); - var peg$e15 = peg$literalExpectation(">", false); - var peg$e16 = peg$literalExpectation("<=", false); - var peg$e17 = peg$literalExpectation("<", false); - var peg$e18 = peg$otherExpectation("quote"); - var peg$e19 = peg$literalExpectation("\"", false); - var peg$e20 = peg$classExpectation(["\"", "\r", "\n"], true, false); - var peg$e21 = peg$otherExpectation("word"); - var peg$e22 = peg$classExpectation([" ", "\"", "\t", "\n", "\r"], true, false); - var peg$e23 = peg$otherExpectation("whitespace"); - var peg$e24 = peg$classExpectation([" ", "\t", "\r", "\n"], false, false); + var peg$e11 = peg$literalExpectation(",", false); + var peg$e12 = peg$otherExpectation("operator"); + var peg$e13 = peg$classExpectation([":", "="], false, false); + var peg$e14 = peg$literalExpectation("!=", false); + var peg$e15 = peg$literalExpectation(">=", false); + var peg$e16 = peg$literalExpectation(">", false); + var peg$e17 = peg$literalExpectation("<=", false); + var peg$e18 = peg$literalExpectation("<", false); + var peg$e19 = peg$otherExpectation("quote"); + var peg$e20 = peg$classExpectation([" ", ",", "\"", "\t", "\n", "\r"], true, false); + var peg$e21 = peg$literalExpectation("\"", false); + var peg$e22 = peg$classExpectation(["\"", "\r", "\n"], true, false); + var peg$e23 = peg$classExpectation([" ", ",", "\t", "\n", "\r"], true, false); + var peg$e24 = peg$otherExpectation("word"); + var peg$e25 = peg$otherExpectation("whitespace"); + var peg$e26 = peg$classExpectation([" ", "\t", "\r", "\n"], false, false); var peg$f0 = function(ranges) { return { autocomplete, ranges }; }; var peg$f1 = function(filters) { return filters.filter(Boolean).flat(); }; @@ -241,21 +245,30 @@ function peg$parse(input, options) { ...value[value.length - 1], }; - return value.map(({ start, length }) => ({ - key, - start, - length, - })); + return value + .filter((filter) => filter.length > 0) + .map(({ start, length }) => ({ + key, + start, + length, + })); }; var peg$f3 = function() { autocomplete = null; }; - var peg$f4 = function(parts) { + var peg$f4 = function(parts, empty) { const ends = location(); const value = parts.flat(); + if (empty) { + value.push(""); + } let count = ends.start.offset; const result = []; value.forEach((filter) => { + let word = filter; + if (word.startsWith('"') && word.endsWith('"') && word.length >= 2) { + word = word.slice(1, -1); + } result.push({ - value: filter, + value: word, start: count, length: filter.length, }); @@ -269,10 +282,10 @@ function peg$parse(input, options) { var peg$f8 = function() { return "gt"; }; var peg$f9 = function() { return "lte"; }; var peg$f10 = function() { return "lt"; }; - var peg$f11 = function(chars) { return chars.join(""); }; - var peg$f12 = function(chars) { - return chars.join("").trim().split(",").filter(Boolean); + var peg$f11 = function(start, inner, end) { + return [...start, '"', ...inner, '"', ...end].join(""); }; + var peg$f12 = function(chars) { return chars.join("").trim(); }; var peg$f13 = function() { return "and"; }; var peg$currPos = options.peg$currPos | 0; var peg$savedPos = peg$currPos; @@ -648,30 +661,63 @@ function peg$parse(input, options) { } function peg$parseidentifier() { - var s0, s1, s2; + var s0, s1, s2, s3, s4; s0 = peg$currPos; - s1 = []; - s2 = peg$parsequotedString(); - if (s2 === peg$FAILED) { - s2 = peg$parsealphanumeric(); + s1 = peg$currPos; + s2 = []; + s3 = peg$parsequotedString(); + if (s3 === peg$FAILED) { + s3 = peg$parsealphanumeric(); } - if (s2 !== peg$FAILED) { - while (s2 !== peg$FAILED) { - s1.push(s2); - s2 = peg$parsequotedString(); - if (s2 === peg$FAILED) { - s2 = peg$parsealphanumeric(); + while (s3 !== peg$FAILED) { + s2.push(s3); + s3 = peg$currPos; + if (input.charCodeAt(peg$currPos) === 44) { + s4 = peg$c10; + peg$currPos++; + } else { + s4 = peg$FAILED; + if (peg$silentFails === 0) { peg$fail(peg$e11); } + } + if (s4 !== peg$FAILED) { + s4 = peg$parsequotedString(); + if (s4 === peg$FAILED) { + s4 = peg$parsealphanumeric(); } + if (s4 === peg$FAILED) { + peg$currPos = s3; + s3 = peg$FAILED; + } else { + s3 = s4; + } + } else { + s3 = s4; } - } else { + } + if (s2.length < 1) { + peg$currPos = s1; s1 = peg$FAILED; + } else { + s1 = s2; } if (s1 !== peg$FAILED) { + if (input.charCodeAt(peg$currPos) === 44) { + s2 = peg$c10; + peg$currPos++; + } else { + s2 = peg$FAILED; + if (peg$silentFails === 0) { peg$fail(peg$e11); } + } + if (s2 === peg$FAILED) { + s2 = null; + } peg$savedPos = s0; - s1 = peg$f4(s1); + s0 = peg$f4(s1, s2); + } else { + peg$currPos = s0; + s0 = peg$FAILED; } - s0 = s1; return s0; } @@ -686,7 +732,7 @@ function peg$parse(input, options) { peg$currPos++; } else { s1 = peg$FAILED; - if (peg$silentFails === 0) { peg$fail(peg$e12); } + if (peg$silentFails === 0) { peg$fail(peg$e13); } } if (s1 !== peg$FAILED) { peg$savedPos = s0; @@ -695,12 +741,12 @@ function peg$parse(input, options) { s0 = s1; if (s0 === peg$FAILED) { s0 = peg$currPos; - if (input.substr(peg$currPos, 2) === peg$c10) { - s1 = peg$c10; + if (input.substr(peg$currPos, 2) === peg$c11) { + s1 = peg$c11; peg$currPos += 2; } else { s1 = peg$FAILED; - if (peg$silentFails === 0) { peg$fail(peg$e13); } + if (peg$silentFails === 0) { peg$fail(peg$e14); } } if (s1 !== peg$FAILED) { peg$savedPos = s0; @@ -709,12 +755,12 @@ function peg$parse(input, options) { s0 = s1; if (s0 === peg$FAILED) { s0 = peg$currPos; - if (input.substr(peg$currPos, 2) === peg$c11) { - s1 = peg$c11; + if (input.substr(peg$currPos, 2) === peg$c12) { + s1 = peg$c12; peg$currPos += 2; } else { s1 = peg$FAILED; - if (peg$silentFails === 0) { peg$fail(peg$e14); } + if (peg$silentFails === 0) { peg$fail(peg$e15); } } if (s1 !== peg$FAILED) { peg$savedPos = s0; @@ -724,11 +770,11 @@ function peg$parse(input, options) { if (s0 === peg$FAILED) { s0 = peg$currPos; if (input.charCodeAt(peg$currPos) === 62) { - s1 = peg$c12; + s1 = peg$c13; peg$currPos++; } else { s1 = peg$FAILED; - if (peg$silentFails === 0) { peg$fail(peg$e15); } + if (peg$silentFails === 0) { peg$fail(peg$e16); } } if (s1 !== peg$FAILED) { peg$savedPos = s0; @@ -737,12 +783,12 @@ function peg$parse(input, options) { s0 = s1; if (s0 === peg$FAILED) { s0 = peg$currPos; - if (input.substr(peg$currPos, 2) === peg$c13) { - s1 = peg$c13; + if (input.substr(peg$currPos, 2) === peg$c14) { + s1 = peg$c14; peg$currPos += 2; } else { s1 = peg$FAILED; - if (peg$silentFails === 0) { peg$fail(peg$e16); } + if (peg$silentFails === 0) { peg$fail(peg$e17); } } if (s1 !== peg$FAILED) { peg$savedPos = s0; @@ -752,11 +798,11 @@ function peg$parse(input, options) { if (s0 === peg$FAILED) { s0 = peg$currPos; if (input.charCodeAt(peg$currPos) === 60) { - s1 = peg$c14; + s1 = peg$c15; peg$currPos++; } else { s1 = peg$FAILED; - if (peg$silentFails === 0) { peg$fail(peg$e17); } + if (peg$silentFails === 0) { peg$fail(peg$e18); } } if (s1 !== peg$FAILED) { peg$savedPos = s0; @@ -771,53 +817,89 @@ function peg$parse(input, options) { peg$silentFails--; if (s0 === peg$FAILED) { s1 = peg$FAILED; - if (peg$silentFails === 0) { peg$fail(peg$e11); } + if (peg$silentFails === 0) { peg$fail(peg$e12); } } return s0; } function peg$parsequotedString() { - var s0, s1, s2, s3; + var s0, s1, s2, s3, s4, s5, s6; peg$silentFails++; s0 = peg$currPos; - if (input.charCodeAt(peg$currPos) === 34) { - s1 = peg$c15; + s1 = []; + s2 = input.charAt(peg$currPos); + if (peg$r1.test(s2)) { peg$currPos++; } else { - s1 = peg$FAILED; - if (peg$silentFails === 0) { peg$fail(peg$e19); } + s2 = peg$FAILED; + if (peg$silentFails === 0) { peg$fail(peg$e20); } } - if (s1 !== peg$FAILED) { - s2 = []; - s3 = input.charAt(peg$currPos); - if (peg$r1.test(s3)) { + while (s2 !== peg$FAILED) { + s1.push(s2); + s2 = input.charAt(peg$currPos); + if (peg$r1.test(s2)) { peg$currPos++; } else { - s3 = peg$FAILED; + s2 = peg$FAILED; if (peg$silentFails === 0) { peg$fail(peg$e20); } } - while (s3 !== peg$FAILED) { - s2.push(s3); - s3 = input.charAt(peg$currPos); - if (peg$r1.test(s3)) { + } + if (input.charCodeAt(peg$currPos) === 34) { + s2 = peg$c16; + peg$currPos++; + } else { + s2 = peg$FAILED; + if (peg$silentFails === 0) { peg$fail(peg$e21); } + } + if (s2 !== peg$FAILED) { + s3 = []; + s4 = input.charAt(peg$currPos); + if (peg$r2.test(s4)) { + peg$currPos++; + } else { + s4 = peg$FAILED; + if (peg$silentFails === 0) { peg$fail(peg$e22); } + } + while (s4 !== peg$FAILED) { + s3.push(s4); + s4 = input.charAt(peg$currPos); + if (peg$r2.test(s4)) { peg$currPos++; } else { - s3 = peg$FAILED; - if (peg$silentFails === 0) { peg$fail(peg$e20); } + s4 = peg$FAILED; + if (peg$silentFails === 0) { peg$fail(peg$e22); } } } if (input.charCodeAt(peg$currPos) === 34) { - s3 = peg$c15; + s4 = peg$c16; peg$currPos++; } else { - s3 = peg$FAILED; - if (peg$silentFails === 0) { peg$fail(peg$e19); } + s4 = peg$FAILED; + if (peg$silentFails === 0) { peg$fail(peg$e21); } } - if (s3 !== peg$FAILED) { + if (s4 !== peg$FAILED) { + s5 = []; + s6 = input.charAt(peg$currPos); + if (peg$r3.test(s6)) { + peg$currPos++; + } else { + s6 = peg$FAILED; + if (peg$silentFails === 0) { peg$fail(peg$e23); } + } + while (s6 !== peg$FAILED) { + s5.push(s6); + s6 = input.charAt(peg$currPos); + if (peg$r3.test(s6)) { + peg$currPos++; + } else { + s6 = peg$FAILED; + if (peg$silentFails === 0) { peg$fail(peg$e23); } + } + } peg$savedPos = s0; - s0 = peg$f11(s2); + s0 = peg$f11(s1, s3, s5); } else { peg$currPos = s0; s0 = peg$FAILED; @@ -829,7 +911,7 @@ function peg$parse(input, options) { peg$silentFails--; if (s0 === peg$FAILED) { s1 = peg$FAILED; - if (peg$silentFails === 0) { peg$fail(peg$e18); } + if (peg$silentFails === 0) { peg$fail(peg$e19); } } return s0; @@ -842,21 +924,21 @@ function peg$parse(input, options) { s0 = peg$currPos; s1 = []; s2 = input.charAt(peg$currPos); - if (peg$r2.test(s2)) { + if (peg$r3.test(s2)) { peg$currPos++; } else { s2 = peg$FAILED; - if (peg$silentFails === 0) { peg$fail(peg$e22); } + if (peg$silentFails === 0) { peg$fail(peg$e23); } } if (s2 !== peg$FAILED) { while (s2 !== peg$FAILED) { s1.push(s2); s2 = input.charAt(peg$currPos); - if (peg$r2.test(s2)) { + if (peg$r3.test(s2)) { peg$currPos++; } else { s2 = peg$FAILED; - if (peg$silentFails === 0) { peg$fail(peg$e22); } + if (peg$silentFails === 0) { peg$fail(peg$e23); } } } } else { @@ -870,7 +952,7 @@ function peg$parse(input, options) { peg$silentFails--; if (s0 === peg$FAILED) { s1 = peg$FAILED; - if (peg$silentFails === 0) { peg$fail(peg$e21); } + if (peg$silentFails === 0) { peg$fail(peg$e24); } } return s0; @@ -894,25 +976,25 @@ function peg$parse(input, options) { peg$silentFails++; s0 = []; s1 = input.charAt(peg$currPos); - if (peg$r3.test(s1)) { + if (peg$r4.test(s1)) { peg$currPos++; } else { s1 = peg$FAILED; - if (peg$silentFails === 0) { peg$fail(peg$e24); } + if (peg$silentFails === 0) { peg$fail(peg$e26); } } while (s1 !== peg$FAILED) { s0.push(s1); s1 = input.charAt(peg$currPos); - if (peg$r3.test(s1)) { + if (peg$r4.test(s1)) { peg$currPos++; } else { s1 = peg$FAILED; - if (peg$silentFails === 0) { peg$fail(peg$e24); } + if (peg$silentFails === 0) { peg$fail(peg$e26); } } } peg$silentFails--; s1 = peg$FAILED; - if (peg$silentFails === 0) { peg$fail(peg$e23); } + if (peg$silentFails === 0) { peg$fail(peg$e25); } return s0; } diff --git a/src/libs/SearchParser/autocompleteParser.peggy b/src/libs/SearchParser/autocompleteParser.peggy index 003d35485d69..bc96c86eb74d 100644 --- a/src/libs/SearchParser/autocompleteParser.peggy +++ b/src/libs/SearchParser/autocompleteParser.peggy @@ -39,11 +39,13 @@ defaultFilter ...value[value.length - 1], }; - return value.map(({ start, length }) => ({ - key, - start, - length, - })); + return value + .filter((filter) => filter.length > 0) + .map(({ start, length }) => ({ + key, + start, + length, + })); } freeTextFilter = _ identifier _ { autocomplete = null; } @@ -63,14 +65,21 @@ autocompleteKey "key" ) identifier - = parts:(quotedString / alphanumeric)+ { + = parts:(quotedString / alphanumeric)|1.., ","| empty:","? { const ends = location(); const value = parts.flat(); + if (empty) { + value.push(""); + } let count = ends.start.offset; const result = []; value.forEach((filter) => { + let word = filter; + if (word.startsWith('"') && word.endsWith('"') && word.length >= 2) { + word = word.slice(1, -1); + } result.push({ - value: filter, + value: word, start: count, length: filter.length, }); diff --git a/src/libs/SearchParser/baseRules.peggy b/src/libs/SearchParser/baseRules.peggy index 49f929bbe389..7605d888ba43 100644 --- a/src/libs/SearchParser/baseRules.peggy +++ b/src/libs/SearchParser/baseRules.peggy @@ -16,13 +16,13 @@ operator "operator" / "<=" { return "lte"; } / "<" { return "lt"; } -quotedString "quote" = "\"" chars:[^"\r\n]* "\"" { return chars.join(""); } - -alphanumeric "word" - = chars:[^ "\t\n\r]+ { - return chars.join("").trim().split(",").filter(Boolean); +quotedString "quote" + = start:[^ ,"\t\n\r]* "\"" inner:[^"\r\n]* "\"" end:[^ ,\t\n\r]* { + return [...start, '"', ...inner, '"', ...end].join(""); } +alphanumeric "word" = chars:[^ ,\t\n\r]+ { return chars.join("").trim(); } + logicalAnd = _ { return "and"; } _ "whitespace" = [ \t\r\n]* diff --git a/src/libs/SearchParser/searchParser.js b/src/libs/SearchParser/searchParser.js index 4c036f1d2c91..1e8d12f16a32 100644 --- a/src/libs/SearchParser/searchParser.js +++ b/src/libs/SearchParser/searchParser.js @@ -200,17 +200,19 @@ function peg$parse(input, options) { var peg$c17 = "sortBy"; var peg$c18 = "sortOrder"; var peg$c19 = "policyID"; - var peg$c20 = "!="; - var peg$c21 = ">="; - var peg$c22 = ">"; - var peg$c23 = "<="; - var peg$c24 = "<"; - var peg$c25 = "\""; + var peg$c20 = ","; + var peg$c21 = "!="; + var peg$c22 = ">="; + var peg$c23 = ">"; + var peg$c24 = "<="; + var peg$c25 = "<"; + var peg$c26 = "\""; var peg$r0 = /^[:=]/; - var peg$r1 = /^[^"\r\n]/; - var peg$r2 = /^[^ "\t\n\r]/; - var peg$r3 = /^[ \t\r\n]/; + var peg$r1 = /^[^ ,"\t\n\r]/; + var peg$r2 = /^[^"\r\n]/; + var peg$r3 = /^[^ ,\t\n\r]/; + var peg$r4 = /^[ \t\r\n]/; var peg$e0 = peg$otherExpectation("key"); var peg$e1 = peg$literalExpectation("date", false); @@ -234,20 +236,22 @@ function peg$parse(input, options) { var peg$e19 = peg$literalExpectation("sortBy", false); var peg$e20 = peg$literalExpectation("sortOrder", false); var peg$e21 = peg$literalExpectation("policyID", false); - var peg$e22 = peg$otherExpectation("operator"); - var peg$e23 = peg$classExpectation([":", "="], false, false); - var peg$e24 = peg$literalExpectation("!=", false); - var peg$e25 = peg$literalExpectation(">=", false); - var peg$e26 = peg$literalExpectation(">", false); - var peg$e27 = peg$literalExpectation("<=", false); - var peg$e28 = peg$literalExpectation("<", false); - var peg$e29 = peg$otherExpectation("quote"); - var peg$e30 = peg$literalExpectation("\"", false); - var peg$e31 = peg$classExpectation(["\"", "\r", "\n"], true, false); - var peg$e32 = peg$otherExpectation("word"); - var peg$e33 = peg$classExpectation([" ", "\"", "\t", "\n", "\r"], true, false); - var peg$e34 = peg$otherExpectation("whitespace"); - var peg$e35 = peg$classExpectation([" ", "\t", "\r", "\n"], false, false); + var peg$e22 = peg$literalExpectation(",", false); + var peg$e23 = peg$otherExpectation("operator"); + var peg$e24 = peg$classExpectation([":", "="], false, false); + var peg$e25 = peg$literalExpectation("!=", false); + var peg$e26 = peg$literalExpectation(">=", false); + var peg$e27 = peg$literalExpectation(">", false); + var peg$e28 = peg$literalExpectation("<=", false); + var peg$e29 = peg$literalExpectation("<", false); + var peg$e30 = peg$otherExpectation("quote"); + var peg$e31 = peg$classExpectation([" ", ",", "\"", "\t", "\n", "\r"], true, false); + var peg$e32 = peg$literalExpectation("\"", false); + var peg$e33 = peg$classExpectation(["\"", "\r", "\n"], true, false); + var peg$e34 = peg$classExpectation([" ", ",", "\t", "\n", "\r"], true, false); + var peg$e35 = peg$otherExpectation("word"); + var peg$e36 = peg$otherExpectation("whitespace"); + var peg$e37 = peg$classExpectation([" ", "\t", "\r", "\n"], false, false); var peg$f0 = function(filters) { return applyDefaults(filters); }; var peg$f1 = function(head, tail) { @@ -287,9 +291,14 @@ function peg$parse(input, options) { return buildFilter(op, field, values); }; var peg$f5 = function(parts) { - const value = parts.flat(); + const value = parts.flat().map((word) => { + if (word.startsWith('"') && word.endsWith('"') && word.length >= 2) { + return word.slice(1,-1); + } + return word; + }); if (value.length > 1) { - return value; + return value.filter((word) => word.length > 0); } return value[0]; }; @@ -299,10 +308,10 @@ function peg$parse(input, options) { var peg$f9 = function() { return "gt"; }; var peg$f10 = function() { return "lte"; }; var peg$f11 = function() { return "lt"; }; - var peg$f12 = function(chars) { return chars.join(""); }; - var peg$f13 = function(chars) { - return chars.join("").trim().split(",").filter(Boolean); + var peg$f12 = function(start, inner, end) { + return [...start, '"', ...inner, '"', ...end].join(""); }; + var peg$f13 = function(chars) { return chars.join("").trim(); }; var peg$f14 = function() { return "and"; }; var peg$currPos = options.peg$currPos | 0; var peg$savedPos = peg$currPos; @@ -840,24 +849,45 @@ function peg$parse(input, options) { } function peg$parseidentifier() { - var s0, s1, s2; + var s0, s1, s2, s3, s4; s0 = peg$currPos; - s1 = []; - s2 = peg$parsequotedString(); - if (s2 === peg$FAILED) { - s2 = peg$parsealphanumeric(); + s1 = peg$currPos; + s2 = []; + s3 = peg$parsequotedString(); + if (s3 === peg$FAILED) { + s3 = peg$parsealphanumeric(); } - if (s2 !== peg$FAILED) { - while (s2 !== peg$FAILED) { - s1.push(s2); - s2 = peg$parsequotedString(); - if (s2 === peg$FAILED) { - s2 = peg$parsealphanumeric(); + while (s3 !== peg$FAILED) { + s2.push(s3); + s3 = peg$currPos; + if (input.charCodeAt(peg$currPos) === 44) { + s4 = peg$c20; + peg$currPos++; + } else { + s4 = peg$FAILED; + if (peg$silentFails === 0) { peg$fail(peg$e22); } + } + if (s4 !== peg$FAILED) { + s4 = peg$parsequotedString(); + if (s4 === peg$FAILED) { + s4 = peg$parsealphanumeric(); + } + if (s4 === peg$FAILED) { + peg$currPos = s3; + s3 = peg$FAILED; + } else { + s3 = s4; } + } else { + s3 = s4; } - } else { + } + if (s2.length < 1) { + peg$currPos = s1; s1 = peg$FAILED; + } else { + s1 = s2; } if (s1 !== peg$FAILED) { peg$savedPos = s0; @@ -878,7 +908,7 @@ function peg$parse(input, options) { peg$currPos++; } else { s1 = peg$FAILED; - if (peg$silentFails === 0) { peg$fail(peg$e23); } + if (peg$silentFails === 0) { peg$fail(peg$e24); } } if (s1 !== peg$FAILED) { peg$savedPos = s0; @@ -887,12 +917,12 @@ function peg$parse(input, options) { s0 = s1; if (s0 === peg$FAILED) { s0 = peg$currPos; - if (input.substr(peg$currPos, 2) === peg$c20) { - s1 = peg$c20; + if (input.substr(peg$currPos, 2) === peg$c21) { + s1 = peg$c21; peg$currPos += 2; } else { s1 = peg$FAILED; - if (peg$silentFails === 0) { peg$fail(peg$e24); } + if (peg$silentFails === 0) { peg$fail(peg$e25); } } if (s1 !== peg$FAILED) { peg$savedPos = s0; @@ -901,12 +931,12 @@ function peg$parse(input, options) { s0 = s1; if (s0 === peg$FAILED) { s0 = peg$currPos; - if (input.substr(peg$currPos, 2) === peg$c21) { - s1 = peg$c21; + if (input.substr(peg$currPos, 2) === peg$c22) { + s1 = peg$c22; peg$currPos += 2; } else { s1 = peg$FAILED; - if (peg$silentFails === 0) { peg$fail(peg$e25); } + if (peg$silentFails === 0) { peg$fail(peg$e26); } } if (s1 !== peg$FAILED) { peg$savedPos = s0; @@ -916,11 +946,11 @@ function peg$parse(input, options) { if (s0 === peg$FAILED) { s0 = peg$currPos; if (input.charCodeAt(peg$currPos) === 62) { - s1 = peg$c22; + s1 = peg$c23; peg$currPos++; } else { s1 = peg$FAILED; - if (peg$silentFails === 0) { peg$fail(peg$e26); } + if (peg$silentFails === 0) { peg$fail(peg$e27); } } if (s1 !== peg$FAILED) { peg$savedPos = s0; @@ -929,12 +959,12 @@ function peg$parse(input, options) { s0 = s1; if (s0 === peg$FAILED) { s0 = peg$currPos; - if (input.substr(peg$currPos, 2) === peg$c23) { - s1 = peg$c23; + if (input.substr(peg$currPos, 2) === peg$c24) { + s1 = peg$c24; peg$currPos += 2; } else { s1 = peg$FAILED; - if (peg$silentFails === 0) { peg$fail(peg$e27); } + if (peg$silentFails === 0) { peg$fail(peg$e28); } } if (s1 !== peg$FAILED) { peg$savedPos = s0; @@ -944,11 +974,11 @@ function peg$parse(input, options) { if (s0 === peg$FAILED) { s0 = peg$currPos; if (input.charCodeAt(peg$currPos) === 60) { - s1 = peg$c24; + s1 = peg$c25; peg$currPos++; } else { s1 = peg$FAILED; - if (peg$silentFails === 0) { peg$fail(peg$e28); } + if (peg$silentFails === 0) { peg$fail(peg$e29); } } if (s1 !== peg$FAILED) { peg$savedPos = s0; @@ -963,53 +993,89 @@ function peg$parse(input, options) { peg$silentFails--; if (s0 === peg$FAILED) { s1 = peg$FAILED; - if (peg$silentFails === 0) { peg$fail(peg$e22); } + if (peg$silentFails === 0) { peg$fail(peg$e23); } } return s0; } function peg$parsequotedString() { - var s0, s1, s2, s3; + var s0, s1, s2, s3, s4, s5, s6; peg$silentFails++; s0 = peg$currPos; - if (input.charCodeAt(peg$currPos) === 34) { - s1 = peg$c25; + s1 = []; + s2 = input.charAt(peg$currPos); + if (peg$r1.test(s2)) { peg$currPos++; } else { - s1 = peg$FAILED; - if (peg$silentFails === 0) { peg$fail(peg$e30); } + s2 = peg$FAILED; + if (peg$silentFails === 0) { peg$fail(peg$e31); } } - if (s1 !== peg$FAILED) { - s2 = []; - s3 = input.charAt(peg$currPos); - if (peg$r1.test(s3)) { + while (s2 !== peg$FAILED) { + s1.push(s2); + s2 = input.charAt(peg$currPos); + if (peg$r1.test(s2)) { peg$currPos++; } else { - s3 = peg$FAILED; + s2 = peg$FAILED; if (peg$silentFails === 0) { peg$fail(peg$e31); } } - while (s3 !== peg$FAILED) { - s2.push(s3); - s3 = input.charAt(peg$currPos); - if (peg$r1.test(s3)) { + } + if (input.charCodeAt(peg$currPos) === 34) { + s2 = peg$c26; + peg$currPos++; + } else { + s2 = peg$FAILED; + if (peg$silentFails === 0) { peg$fail(peg$e32); } + } + if (s2 !== peg$FAILED) { + s3 = []; + s4 = input.charAt(peg$currPos); + if (peg$r2.test(s4)) { + peg$currPos++; + } else { + s4 = peg$FAILED; + if (peg$silentFails === 0) { peg$fail(peg$e33); } + } + while (s4 !== peg$FAILED) { + s3.push(s4); + s4 = input.charAt(peg$currPos); + if (peg$r2.test(s4)) { peg$currPos++; } else { - s3 = peg$FAILED; - if (peg$silentFails === 0) { peg$fail(peg$e31); } + s4 = peg$FAILED; + if (peg$silentFails === 0) { peg$fail(peg$e33); } } } if (input.charCodeAt(peg$currPos) === 34) { - s3 = peg$c25; + s4 = peg$c26; peg$currPos++; } else { - s3 = peg$FAILED; - if (peg$silentFails === 0) { peg$fail(peg$e30); } + s4 = peg$FAILED; + if (peg$silentFails === 0) { peg$fail(peg$e32); } } - if (s3 !== peg$FAILED) { + if (s4 !== peg$FAILED) { + s5 = []; + s6 = input.charAt(peg$currPos); + if (peg$r3.test(s6)) { + peg$currPos++; + } else { + s6 = peg$FAILED; + if (peg$silentFails === 0) { peg$fail(peg$e34); } + } + while (s6 !== peg$FAILED) { + s5.push(s6); + s6 = input.charAt(peg$currPos); + if (peg$r3.test(s6)) { + peg$currPos++; + } else { + s6 = peg$FAILED; + if (peg$silentFails === 0) { peg$fail(peg$e34); } + } + } peg$savedPos = s0; - s0 = peg$f12(s2); + s0 = peg$f12(s1, s3, s5); } else { peg$currPos = s0; s0 = peg$FAILED; @@ -1021,7 +1087,7 @@ function peg$parse(input, options) { peg$silentFails--; if (s0 === peg$FAILED) { s1 = peg$FAILED; - if (peg$silentFails === 0) { peg$fail(peg$e29); } + if (peg$silentFails === 0) { peg$fail(peg$e30); } } return s0; @@ -1034,21 +1100,21 @@ function peg$parse(input, options) { s0 = peg$currPos; s1 = []; s2 = input.charAt(peg$currPos); - if (peg$r2.test(s2)) { + if (peg$r3.test(s2)) { peg$currPos++; } else { s2 = peg$FAILED; - if (peg$silentFails === 0) { peg$fail(peg$e33); } + if (peg$silentFails === 0) { peg$fail(peg$e34); } } if (s2 !== peg$FAILED) { while (s2 !== peg$FAILED) { s1.push(s2); s2 = input.charAt(peg$currPos); - if (peg$r2.test(s2)) { + if (peg$r3.test(s2)) { peg$currPos++; } else { s2 = peg$FAILED; - if (peg$silentFails === 0) { peg$fail(peg$e33); } + if (peg$silentFails === 0) { peg$fail(peg$e34); } } } } else { @@ -1062,7 +1128,7 @@ function peg$parse(input, options) { peg$silentFails--; if (s0 === peg$FAILED) { s1 = peg$FAILED; - if (peg$silentFails === 0) { peg$fail(peg$e32); } + if (peg$silentFails === 0) { peg$fail(peg$e35); } } return s0; @@ -1086,25 +1152,25 @@ function peg$parse(input, options) { peg$silentFails++; s0 = []; s1 = input.charAt(peg$currPos); - if (peg$r3.test(s1)) { + if (peg$r4.test(s1)) { peg$currPos++; } else { s1 = peg$FAILED; - if (peg$silentFails === 0) { peg$fail(peg$e35); } + if (peg$silentFails === 0) { peg$fail(peg$e37); } } while (s1 !== peg$FAILED) { s0.push(s1); s1 = input.charAt(peg$currPos); - if (peg$r3.test(s1)) { + if (peg$r4.test(s1)) { peg$currPos++; } else { s1 = peg$FAILED; - if (peg$silentFails === 0) { peg$fail(peg$e35); } + if (peg$silentFails === 0) { peg$fail(peg$e37); } } } peg$silentFails--; s1 = peg$FAILED; - if (peg$silentFails === 0) { peg$fail(peg$e34); } + if (peg$silentFails === 0) { peg$fail(peg$e36); } return s0; } diff --git a/src/libs/SearchParser/searchParser.peggy b/src/libs/SearchParser/searchParser.peggy index 6429719d9dbd..7d5815d41459 100644 --- a/src/libs/SearchParser/searchParser.peggy +++ b/src/libs/SearchParser/searchParser.peggy @@ -114,10 +114,15 @@ defaultKey "default key" = @("type" / "status" / "sortBy" / "sortOrder" / "policyID") identifier - = parts:(quotedString / alphanumeric)+ { - const value = parts.flat(); + = parts:(quotedString / alphanumeric)|1.., ","| { + const value = parts.flat().map((word) => { + if (word.startsWith('"') && word.endsWith('"') && word.length >= 2) { + return word.slice(1,-1); + } + return word; + }); if (value.length > 1) { - return value; + return value.filter((word) => word.length > 0); } return value[0]; } From 01cf8eb0e8879768619ea7fbfd9b3cd555ecda49 Mon Sep 17 00:00:00 2001 From: Carlos Miceli Date: Fri, 25 Oct 2024 00:55:56 -0300 Subject: [PATCH 26/31] add check to wait to show session expired --- src/pages/LogInWithShortLivedAuthTokenPage.tsx | 9 +++++++-- 1 file changed, 7 insertions(+), 2 deletions(-) diff --git a/src/pages/LogInWithShortLivedAuthTokenPage.tsx b/src/pages/LogInWithShortLivedAuthTokenPage.tsx index fcbeadaa4a47..8eebb8c1ddcd 100644 --- a/src/pages/LogInWithShortLivedAuthTokenPage.tsx +++ b/src/pages/LogInWithShortLivedAuthTokenPage.tsx @@ -1,5 +1,5 @@ import type {StackScreenProps} from '@react-navigation/stack'; -import React, {useEffect} from 'react'; +import React, {useState, useEffect} from 'react'; import {NativeModules} from 'react-native'; import type {OnyxEntry} from 'react-native-onyx'; import {withOnyx} from 'react-native-onyx'; @@ -26,6 +26,9 @@ type LogInWithShortLivedAuthTokenPageProps = LogInWithShortLivedAuthTokenPageOny function LogInWithShortLivedAuthTokenPage({route, account}: LogInWithShortLivedAuthTokenPageProps) { const {email = '', shortLivedAuthToken = '', shortLivedToken = '', authTokenType, exitTo, error} = route?.params ?? {}; + // State to track if authentication flow is still processing + const [isProcessing, setIsProcessing] = useState(true); + useEffect(() => { // We have to check for both shortLivedAuthToken and shortLivedToken, as the old mobile app uses shortLivedToken, and is not being actively updated. const token = shortLivedAuthToken || shortLivedToken; @@ -58,11 +61,13 @@ function LogInWithShortLivedAuthTokenPage({route, account}: LogInWithShortLivedA Navigation.navigate(exitTo as Route); }); } + + setIsProcessing(false); // The only dependencies of the effect are based on props.route // eslint-disable-next-line react-compiler/react-compiler, react-hooks/exhaustive-deps }, [route]); - if (account?.isLoading) { + if (isProcessing || account?.isLoading) { return ; } From afebffa94da67211457d1acdff20ccf86e89ed1b Mon Sep 17 00:00:00 2001 From: maddylewis <38016013+maddylewis@users.noreply.github.com> Date: Fri, 25 Oct 2024 11:52:20 -0400 Subject: [PATCH 27/31] Update redirects.csv updating redirects -- https://github.com/Expensify/Expensify/issues/428744 --- docs/redirects.csv | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/docs/redirects.csv b/docs/redirects.csv index a7d4d94adb5d..d3672618cfad 100644 --- a/docs/redirects.csv +++ b/docs/redirects.csv @@ -585,7 +585,8 @@ https://community.expensify.com/discussion/6699/faq-troubleshooting-known-bank-s https://community.expensify.com/discussion/4730/faq-expenses-are-exporting-to-the-wrong-accounts-whys-that,https://help.expensify.com/articles/expensify-classic/connect-credit-cards/company-cards/Company-Card-Settings https://community.expensify.com/discussion/9000/how-to-integrate-with-deel,https://help.expensify.com/articles/expensify-classic/connections/Deel https://community.expensify.com/categories/expensify-classroom,https://use.expensify.com -https://help.expensify.com/articles/new-expensify/billing-and-subscriptions/adding-payment-card-subscription-overview,https://help.expensify.com/articles/new-expensify/billing-and-subscriptions/add-a-payment-card-and-view-your-subscription +https://help.expensify.com/articles/new-expensify/billing-and-subscriptions/adding-payment-card-subscription-overview,https://help.expensify.com/articles/new-expensify/billing-and-subscriptions/Add-a-payment-card-and-view-your-subscription https://help.expensify.com/articles/expensify-classic/articles/expensify-classic/expenses/Send-Receive-for-Invoices,https://help.expensify.com/articles/expensify-classic/articles/expensify-classic/expenses/Send-and-Receive-Payment-for-Invoices.md https://help.expensify.com/articles/expensify-classic/articles/expensify-classic/expenses/Bulk-Upload-Multiple-Invoices,https://help.expensify.com/articles/expensify-classic/articles/expensify-classic/expenses/Add-Invoices-in-Bulk -https://help.expensify.com/articles/expensify-classic/bank-accounts-and-payments/payments/Pay-Bills,https://help.expensify.com/articles/expensify-classic/bank-accounts-and-payments/payments/Create-and-Pay-Bills \ No newline at end of file +https://help.expensify.com/articles/expensify-classic/bank-accounts-and-payments/payments/Pay-Bills,https://help.expensify.com/articles/expensify-classic/bank-accounts-and-payments/payments/Create-and-Pay-Bills +https://help.expensify.com/articles/new-expensify/billing-and-subscriptions/add-a-payment-card-and-view-your-subscription,https://help.expensify.com/articles/new-expensify/billing-and-subscriptions/Add-a-payment-card-and-view-your-subscription From 705892cd219273c52056c20d01422377bce153a0 Mon Sep 17 00:00:00 2001 From: Wojciech Lewicki Date: Fri, 25 Oct 2024 18:54:43 +0200 Subject: [PATCH 28/31] fix: revert bridgeless on Android --- .../app/src/main/java/com/expensify/chat/MainApplication.kt | 2 +- .../@react-native-firebase+app+12.9.3+002+bridgeless.patch | 4 ++-- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/android/app/src/main/java/com/expensify/chat/MainApplication.kt b/android/app/src/main/java/com/expensify/chat/MainApplication.kt index f476ad89c5b4..942304c80445 100644 --- a/android/app/src/main/java/com/expensify/chat/MainApplication.kt +++ b/android/app/src/main/java/com/expensify/chat/MainApplication.kt @@ -64,7 +64,7 @@ class MainApplication : MultiDexApplication(), ReactApplication { SoLoader.init(this, /* native exopackage */false) if (BuildConfig.IS_NEW_ARCHITECTURE_ENABLED) { // If you opted-in for the New Architecture, we load the native entry point for this app. - load() + load(bridgelessEnabled = false) } if (BuildConfig.DEBUG) { FirebaseCrashlytics.getInstance().setCrashlyticsCollectionEnabled(false) diff --git a/patches/@react-native-firebase+app+12.9.3+002+bridgeless.patch b/patches/@react-native-firebase+app+12.9.3+002+bridgeless.patch index a085cdbcfbe2..54ae4d9a1c58 100644 --- a/patches/@react-native-firebase+app+12.9.3+002+bridgeless.patch +++ b/patches/@react-native-firebase+app+12.9.3+002+bridgeless.patch @@ -1,5 +1,5 @@ diff --git a/node_modules/@react-native-firebase/app/lib/internal/registry/nativeModule.js b/node_modules/@react-native-firebase/app/lib/internal/registry/nativeModule.js -index 03f001c..23d467d 100644 +index 03f001c..358c795 100644 --- a/node_modules/@react-native-firebase/app/lib/internal/registry/nativeModule.js +++ b/node_modules/@react-native-firebase/app/lib/internal/registry/nativeModule.js @@ -65,7 +65,7 @@ function nativeModuleWrapped(namespace, NativeModule, argToPrepend) { @@ -7,7 +7,7 @@ index 03f001c..23d467d 100644 } - const properties = Object.keys(NativeModule); -+ const properties = Object.keys(Object.getPrototypeOf(NativeModule)); ++ const properties = [...Object.keys(Object.getPrototypeOf(NativeModule)), ...Object.keys(NativeModule)]; for (let i = 0, len = properties.length; i < len; i++) { const property = properties[i]; From 4610ac1743752748072c405b4c8ed1c3e227583f Mon Sep 17 00:00:00 2001 From: Carlos Miceli Date: Fri, 25 Oct 2024 14:13:03 -0300 Subject: [PATCH 29/31] ran prettier --- src/pages/LogInWithShortLivedAuthTokenPage.tsx | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/pages/LogInWithShortLivedAuthTokenPage.tsx b/src/pages/LogInWithShortLivedAuthTokenPage.tsx index 8eebb8c1ddcd..ec245f87aa06 100644 --- a/src/pages/LogInWithShortLivedAuthTokenPage.tsx +++ b/src/pages/LogInWithShortLivedAuthTokenPage.tsx @@ -1,5 +1,5 @@ import type {StackScreenProps} from '@react-navigation/stack'; -import React, {useState, useEffect} from 'react'; +import React, {useEffect, useState} from 'react'; import {NativeModules} from 'react-native'; import type {OnyxEntry} from 'react-native-onyx'; import {withOnyx} from 'react-native-onyx'; From f943b4a9eda7e5c6326d6ca8223bf8eb6b78b8b6 Mon Sep 17 00:00:00 2001 From: Carlos Miceli Date: Fri, 25 Oct 2024 14:56:13 -0300 Subject: [PATCH 30/31] fix eslint --- src/pages/LogInWithShortLivedAuthTokenPage.tsx | 17 ++++++----------- 1 file changed, 6 insertions(+), 11 deletions(-) diff --git a/src/pages/LogInWithShortLivedAuthTokenPage.tsx b/src/pages/LogInWithShortLivedAuthTokenPage.tsx index ec245f87aa06..8557b45726d9 100644 --- a/src/pages/LogInWithShortLivedAuthTokenPage.tsx +++ b/src/pages/LogInWithShortLivedAuthTokenPage.tsx @@ -1,8 +1,8 @@ import type {StackScreenProps} from '@react-navigation/stack'; -import React, {useEffect, useState} from 'react'; +import React, {useEffect} from 'react'; import {NativeModules} from 'react-native'; import type {OnyxEntry} from 'react-native-onyx'; -import {withOnyx} from 'react-native-onyx'; +import {useOnyx} from 'react-native-onyx'; import FullScreenLoadingIndicator from '@components/FullscreenLoadingIndicator'; import Log from '@libs/Log'; import Navigation from '@libs/Navigation/Navigation'; @@ -23,11 +23,9 @@ type LogInWithShortLivedAuthTokenPageOnyxProps = { type LogInWithShortLivedAuthTokenPageProps = LogInWithShortLivedAuthTokenPageOnyxProps & StackScreenProps; -function LogInWithShortLivedAuthTokenPage({route, account}: LogInWithShortLivedAuthTokenPageProps) { +function LogInWithShortLivedAuthTokenPage({route}: LogInWithShortLivedAuthTokenPageProps) { const {email = '', shortLivedAuthToken = '', shortLivedToken = '', authTokenType, exitTo, error} = route?.params ?? {}; - - // State to track if authentication flow is still processing - const [isProcessing, setIsProcessing] = useState(true); + const [account] = useOnyx(ONYXKEYS.ACCOUNT); useEffect(() => { // We have to check for both shortLivedAuthToken and shortLivedToken, as the old mobile app uses shortLivedToken, and is not being actively updated. @@ -62,12 +60,11 @@ function LogInWithShortLivedAuthTokenPage({route, account}: LogInWithShortLivedA }); } - setIsProcessing(false); // The only dependencies of the effect are based on props.route // eslint-disable-next-line react-compiler/react-compiler, react-hooks/exhaustive-deps }, [route]); - if (isProcessing || account?.isLoading) { + if (account?.isLoading) { return ; } @@ -76,6 +73,4 @@ function LogInWithShortLivedAuthTokenPage({route, account}: LogInWithShortLivedA LogInWithShortLivedAuthTokenPage.displayName = 'LogInWithShortLivedAuthTokenPage'; -export default withOnyx({ - account: {key: ONYXKEYS.ACCOUNT}, -})(LogInWithShortLivedAuthTokenPage); +export default LogInWithShortLivedAuthTokenPage; From ff28dd9f82fbb47ba3fda9ff894a7423718b28fe Mon Sep 17 00:00:00 2001 From: Carlos Miceli Date: Fri, 25 Oct 2024 15:05:38 -0300 Subject: [PATCH 31/31] code cleanup --- src/pages/LogInWithShortLivedAuthTokenPage.tsx | 10 +--------- 1 file changed, 1 insertion(+), 9 deletions(-) diff --git a/src/pages/LogInWithShortLivedAuthTokenPage.tsx b/src/pages/LogInWithShortLivedAuthTokenPage.tsx index 8557b45726d9..e604f2ccf847 100644 --- a/src/pages/LogInWithShortLivedAuthTokenPage.tsx +++ b/src/pages/LogInWithShortLivedAuthTokenPage.tsx @@ -1,7 +1,6 @@ import type {StackScreenProps} from '@react-navigation/stack'; import React, {useEffect} from 'react'; import {NativeModules} from 'react-native'; -import type {OnyxEntry} from 'react-native-onyx'; import {useOnyx} from 'react-native-onyx'; import FullScreenLoadingIndicator from '@components/FullscreenLoadingIndicator'; import Log from '@libs/Log'; @@ -13,15 +12,9 @@ import ONYXKEYS from '@src/ONYXKEYS'; import type {Route} from '@src/ROUTES'; import ROUTES from '@src/ROUTES'; import type SCREENS from '@src/SCREENS'; -import type {Account} from '@src/types/onyx'; import SessionExpiredPage from './ErrorPage/SessionExpiredPage'; -type LogInWithShortLivedAuthTokenPageOnyxProps = { - /** The details about the account that the user is signing in with */ - account: OnyxEntry; -}; - -type LogInWithShortLivedAuthTokenPageProps = LogInWithShortLivedAuthTokenPageOnyxProps & StackScreenProps; +type LogInWithShortLivedAuthTokenPageProps = StackScreenProps; function LogInWithShortLivedAuthTokenPage({route}: LogInWithShortLivedAuthTokenPageProps) { const {email = '', shortLivedAuthToken = '', shortLivedToken = '', authTokenType, exitTo, error} = route?.params ?? {}; @@ -59,7 +52,6 @@ function LogInWithShortLivedAuthTokenPage({route}: LogInWithShortLivedAuthTokenP Navigation.navigate(exitTo as Route); }); } - // The only dependencies of the effect are based on props.route // eslint-disable-next-line react-compiler/react-compiler, react-hooks/exhaustive-deps }, [route]);