From 3a50dd22833350b9150570bdaba969c6e52c8b19 Mon Sep 17 00:00:00 2001 From: nkdengineer Date: Mon, 10 Feb 2025 14:47:46 +0700 Subject: [PATCH 1/5] fix: extra saved search action menu briefly appears at the top left corner --- .../Search/SavedSearchItemThreeDotMenu.tsx | 17 +++++++++-------- 1 file changed, 9 insertions(+), 8 deletions(-) diff --git a/src/pages/Search/SavedSearchItemThreeDotMenu.tsx b/src/pages/Search/SavedSearchItemThreeDotMenu.tsx index 4d92ef295f2d1..6f8ec73f00231 100644 --- a/src/pages/Search/SavedSearchItemThreeDotMenu.tsx +++ b/src/pages/Search/SavedSearchItemThreeDotMenu.tsx @@ -20,17 +20,18 @@ function SavedSearchItemThreeDotMenu({menuItems, isDisabledItem, hideProductTrai { + const target = e.target; + target?.measureInWindow((x, y, width) => { + setThreeDotsMenuPosition({ + horizontal: x + width, + vertical: y, + }); + }); + }} > { - threeDotsMenuContainerRef.current?.measureInWindow((x, y, width) => { - setThreeDotsMenuPosition({ - horizontal: x + width, - vertical: y, - }); - }); - }} anchorPosition={threeDotsMenuPosition} renderProductTrainingTooltipContent={renderTooltipContent} shouldShowProductTrainingTooltip={shouldRenderTooltip} From 3f7a3150af0284104d1eb3576a567d1d9c145291 Mon Sep 17 00:00:00 2001 From: nkdengineer Date: Mon, 10 Feb 2025 15:15:37 +0700 Subject: [PATCH 2/5] fix lint --- src/pages/Search/SavedSearchItemThreeDotMenu.tsx | 8 ++++++-- 1 file changed, 6 insertions(+), 2 deletions(-) diff --git a/src/pages/Search/SavedSearchItemThreeDotMenu.tsx b/src/pages/Search/SavedSearchItemThreeDotMenu.tsx index 6f8ec73f00231..2a0733e40c7e2 100644 --- a/src/pages/Search/SavedSearchItemThreeDotMenu.tsx +++ b/src/pages/Search/SavedSearchItemThreeDotMenu.tsx @@ -1,4 +1,5 @@ import React, {useRef, useState} from 'react'; +import type {LayoutChangeEvent, LayoutRectangle, NativeSyntheticEvent} from 'react-native'; import {View} from 'react-native'; import type {PopoverMenuItem} from '@components/PopoverMenu'; import ThreeDotsMenu from '@components/ThreeDotsMenu'; @@ -12,6 +13,9 @@ type SavedSearchItemThreeDotMenuProps = { renderTooltipContent: () => React.JSX.Element; shouldRenderTooltip: boolean; }; + +type LayoutChangeEventWithTarget = NativeSyntheticEvent<{layout: LayoutRectangle; target: HTMLElement}>; + function SavedSearchItemThreeDotMenu({menuItems, isDisabledItem, hideProductTrainingTooltip, renderTooltipContent, shouldRenderTooltip}: SavedSearchItemThreeDotMenuProps) { const threeDotsMenuContainerRef = useRef(null); const [threeDotsMenuPosition, setThreeDotsMenuPosition] = useState({horizontal: 0, vertical: 0}); @@ -20,8 +24,8 @@ function SavedSearchItemThreeDotMenu({menuItems, isDisabledItem, hideProductTrai { - const target = e.target; + onLayout={(e: LayoutChangeEvent) => { + const target = e.target || (e as LayoutChangeEventWithTarget).nativeEvent.target; target?.measureInWindow((x, y, width) => { setThreeDotsMenuPosition({ horizontal: x + width, From cad3b97c0d32f566e6dbaba80b7d45736b250629 Mon Sep 17 00:00:00 2001 From: nkdengineer Date: Thu, 13 Feb 2025 11:37:50 +0700 Subject: [PATCH 3/5] Fix all three dots menu --- src/components/ThreeDotsMenu/types.ts | 5 ++- .../CardSection/CardSectionActions/index.tsx | 33 ++++++++++--------- .../Subscription/TaxExemptActions/index.tsx | 29 ++++++++-------- src/pages/workspace/WorkspacesListRow.tsx | 30 +++++++++-------- .../accounting/PolicyAccountingPage.tsx | 23 ++++++++----- 5 files changed, 68 insertions(+), 52 deletions(-) diff --git a/src/components/ThreeDotsMenu/types.ts b/src/components/ThreeDotsMenu/types.ts index 6b645d8e972cf..dc7f411f3af1b 100644 --- a/src/components/ThreeDotsMenu/types.ts +++ b/src/components/ThreeDotsMenu/types.ts @@ -1,4 +1,4 @@ -import type {StyleProp, ViewStyle} from 'react-native'; +import type {LayoutRectangle, NativeSyntheticEvent, StyleProp, ViewStyle} from 'react-native'; import type {PopoverMenuItem} from '@components/PopoverMenu'; import type {TranslationPaths} from '@src/languages/types'; import type {AnchorPosition} from '@src/styles'; @@ -49,4 +49,7 @@ type ThreeDotsMenuProps = { shouldShowProductTrainingTooltip?: boolean; }; +type LayoutChangeEventWithTarget = NativeSyntheticEvent<{layout: LayoutRectangle; target: HTMLElement}>; + +export type {LayoutChangeEventWithTarget}; export default ThreeDotsMenuProps; diff --git a/src/pages/settings/Subscription/CardSection/CardSectionActions/index.tsx b/src/pages/settings/Subscription/CardSection/CardSectionActions/index.tsx index 663ea38f81d7c..99d20cf01b0d7 100644 --- a/src/pages/settings/Subscription/CardSection/CardSectionActions/index.tsx +++ b/src/pages/settings/Subscription/CardSection/CardSectionActions/index.tsx @@ -1,8 +1,10 @@ -import React, {useCallback, useMemo, useRef, useState} from 'react'; +import React, {useMemo, useRef, useState} from 'react'; +import type {LayoutChangeEvent} from 'react-native'; import {View} from 'react-native'; import * as Expensicons from '@components/Icon/Expensicons'; import ThreeDotsMenu from '@components/ThreeDotsMenu'; import type ThreeDotsMenuProps from '@components/ThreeDotsMenu/types'; +import type {LayoutChangeEventWithTarget} from '@components/ThreeDotsMenu/types'; import useLocalize from '@hooks/useLocalize'; import useResponsiveLayout from '@hooks/useResponsiveLayout'; import Navigation from '@navigation/Navigation'; @@ -37,22 +39,23 @@ function CardSectionActions() { [translate], ); - const calculateAndSetThreeDotsMenuPosition = useCallback(() => { - if (shouldUseNarrowLayout) { - return; - } - threeDotsMenuContainerRef.current?.measureInWindow((x, y, width, height) => { - setThreeDotsMenuPosition({ - horizontal: x + width, - vertical: y + height, - }); - }); - }, [shouldUseNarrowLayout]); - return ( - + { + if (shouldUseNarrowLayout) { + return; + } + const target = e.target || (e as LayoutChangeEventWithTarget).nativeEvent.target; + target?.measureInWindow((x, y, width) => { + setThreeDotsMenuPosition({ + horizontal: x + width, + vertical: y, + }); + }); + }} + > { - if (shouldUseNarrowLayout) { - return; - } - threeDotsMenuContainerRef.current?.measureInWindow((x, y, width, height) => { - setThreeDotsMenuPosition({ - horizontal: x + width, - vertical: y + height, - }); - }); - }, [shouldUseNarrowLayout]); - return ( { + if (shouldUseNarrowLayout) { + return; + } + const target = e.target || (e as LayoutChangeEventWithTarget).nativeEvent.target; + target?.measureInWindow((x, y, width) => { + setThreeDotsMenuPosition({ + horizontal: x + width, + vertical: y, + }); + }); + }} > - - { - if (shouldUseNarrowLayout) { - return; - } - threeDotsMenuContainerRef.current?.measureInWindow((x, y, width, height) => { - setThreeDotsMenuPosition({ - horizontal: x + width, - vertical: y + height, - }); + { + if (shouldUseNarrowLayout) { + return; + } + const target = e.target || (e as LayoutChangeEventWithTarget).nativeEvent.target; + target?.measureInWindow((x, y, width) => { + setThreeDotsMenuPosition({ + horizontal: x + width, + vertical: y, }); - }} + }); + }} + > + ) : ( - - { - threeDotsMenuContainerRef.current?.measureInWindow((x, y, width, height) => { - setThreeDotsMenuPosition({ - horizontal: x + width, - vertical: y + height, - }); + { + const target = e.target || (e as LayoutChangeEventWithTarget).nativeEvent.target; + target?.measureInWindow((x, y, width) => { + setThreeDotsMenuPosition({ + horizontal: x + width, + vertical: y, }); - }} + }); + }} + > + Date: Fri, 14 Feb 2025 10:22:49 +0700 Subject: [PATCH 4/5] fix lint --- src/ROUTES.ts | 21 ++++++++++++++++--- .../Subscription/TaxExemptActions/index.tsx | 8 +++---- .../accounting/PolicyAccountingPage.tsx | 14 ++++++------- 3 files changed, 29 insertions(+), 14 deletions(-) diff --git a/src/ROUTES.ts b/src/ROUTES.ts index b98717b51f5dd..1f3bf956de85d 100644 --- a/src/ROUTES.ts +++ b/src/ROUTES.ts @@ -1743,7 +1743,12 @@ const ROUTES = { }, POLICY_ACCOUNTING_XERO_ORGANIZATION: { route: 'settings/workspaces/:policyID/accounting/xero/organization/:currentOrganizationID', - getRoute: (policyID: string, currentOrganizationID: string) => `settings/workspaces/${policyID}/accounting/xero/organization/${currentOrganizationID}` as const, + getRoute: (policyID: string | undefined, currentOrganizationID: string) => { + if (!policyID) { + Log.warn('Invalid policyID is used to build the POLICY_ACCOUNTING_XERO_ORGANIZATION route'); + } + return `settings/workspaces/${policyID}/accounting/xero/organization/${currentOrganizationID}` as const; + }, }, POLICY_ACCOUNTING_XERO_TRACKING_CATEGORIES: { route: 'settings/workspaces/:policyID/accounting/xero/import/tracking-categories', @@ -1837,7 +1842,12 @@ const ROUTES = { MISSING_PERSONAL_DETAILS: 'missing-personal-details', POLICY_ACCOUNTING_NETSUITE_SUBSIDIARY_SELECTOR: { route: 'settings/workspaces/:policyID/accounting/netsuite/subsidiary-selector', - getRoute: (policyID: string) => `settings/workspaces/${policyID}/accounting/netsuite/subsidiary-selector` as const, + getRoute: (policyID: string | undefined) => { + if (!policyID) { + Log.warn('Invalid policyID is used to build the POLICY_ACCOUNTING_NETSUITE_SUBSIDIARY_SELECTOR route'); + } + return `settings/workspaces/${policyID}/accounting/netsuite/subsidiary-selector` as const; + }, }, POLICY_ACCOUNTING_NETSUITE_EXISTING_CONNECTIONS: { route: 'settings/workspaces/:policyID/accounting/netsuite/existing-connections', @@ -2043,7 +2053,12 @@ const ROUTES = { }, POLICY_ACCOUNTING_SAGE_INTACCT_ENTITY: { route: 'settings/workspaces/:policyID/accounting/sage-intacct/entity', - getRoute: (policyID: string) => `settings/workspaces/${policyID}/accounting/sage-intacct/entity` as const, + getRoute: (policyID: string | undefined) => { + if (!policyID) { + Log.warn('Invalid policyID is used to build the POLICY_ACCOUNTING_SAGE_INTACCT_ENTITY route'); + } + return `settings/workspaces/${policyID}/accounting/sage-intacct/entity` as const; + }, }, POLICY_ACCOUNTING_SAGE_INTACCT_IMPORT: { route: 'settings/workspaces/:policyID/accounting/sage-intacct/import', diff --git a/src/pages/settings/Subscription/TaxExemptActions/index.tsx b/src/pages/settings/Subscription/TaxExemptActions/index.tsx index f3509de75ab2e..54efe0562e928 100644 --- a/src/pages/settings/Subscription/TaxExemptActions/index.tsx +++ b/src/pages/settings/Subscription/TaxExemptActions/index.tsx @@ -9,8 +9,8 @@ import useLocalize from '@hooks/useLocalize'; import useResponsiveLayout from '@hooks/useResponsiveLayout'; import useThemeStyles from '@hooks/useThemeStyles'; import type {AnchorPosition} from '@styles/index'; -import * as Report from '@userActions/Report'; -import * as Subscription from '@userActions/Subscription'; +import {navigateToConciergeChat} from '@userActions/Report'; +import {requestTaxExempt} from '@userActions/Subscription'; import CONST from '@src/CONST'; const anchorAlignment = { @@ -32,8 +32,8 @@ function TaxExemptActions() { numberOfLinesTitle: 2, text: translate('subscription.details.taxExempt'), onSelected: () => { - Subscription.requestTaxExempt(); - Report.navigateToConciergeChat(); + requestTaxExempt(); + navigateToConciergeChat(); }, }, ], diff --git a/src/pages/workspace/accounting/PolicyAccountingPage.tsx b/src/pages/workspace/accounting/PolicyAccountingPage.tsx index 947278f933e0f..237c22ca0c6d5 100644 --- a/src/pages/workspace/accounting/PolicyAccountingPage.tsx +++ b/src/pages/workspace/accounting/PolicyAccountingPage.tsx @@ -104,7 +104,7 @@ function PolicyAccountingPage({policy}: PolicyAccountingPageProps) { const shouldShowEnterCredentials = connectedIntegration && !!synchronizationError && isAuthenticationError(policy, connectedIntegration); - const policyID = policy?.id ?? '-1'; + const policyID = policy?.id; // Get the last successful date of the integration. Then, if `connectionSyncProgress` is the same integration displayed and the state is 'jobDone', get the more recent update time of the two. const successfulDate = getIntegrationLastSuccessfulDate( connectedIntegration ? policy?.connections?.[connectedIntegration] : undefined, @@ -276,7 +276,7 @@ function PolicyAccountingPage({policy}: PolicyAccountingPageProps) { }, [connectedIntegration, currentXeroOrganization?.id, policy, policyID, styles.fontWeightNormal, styles.sectionMenuItemTopDescription, tenants.length, translate]); const connectionsMenuItems: MenuItemData[] = useMemo(() => { - if (isEmptyObject(policy?.connections) && !isSyncInProgress) { + if (isEmptyObject(policy?.connections) && !isSyncInProgress && policyID) { return accountingIntegrations .map((integration) => { const integrationData = getAccountingIntegrationData(integration, policyID, translate); @@ -329,7 +329,7 @@ function PolicyAccountingPage({policy}: PolicyAccountingPageProps) { .filter(Boolean) as MenuItemData[]; } - if (!connectedIntegration) { + if (!connectedIntegration || !policyID) { return []; } const shouldHideConfigurationOptions = isConnectionUnverified(policy, connectedIntegration); @@ -453,7 +453,7 @@ function PolicyAccountingPage({policy}: PolicyAccountingPageProps) { ]); const otherIntegrationsItems = useMemo(() => { - if (isEmptyObject(policy?.connections) && !isSyncInProgress) { + if ((isEmptyObject(policy?.connections) && !isSyncInProgress) || !policyID) { return; } const otherIntegrations = accountingIntegrations.filter( @@ -594,7 +594,7 @@ function PolicyAccountingPage({policy}: PolicyAccountingPageProps) { /> ))} - {hasUnsupportedNDIntegration && hasSyncError && ( + {hasUnsupportedNDIntegration && hasSyncError && !!policyID && ( )} - {hasUnsupportedNDIntegration && !hasSyncError && ( + {hasUnsupportedNDIntegration && !hasSyncError && !!policyID && ( { - if (connectedIntegration) { + if (connectedIntegration && policyID) { removePolicyConnection(policyID, connectedIntegration); } setIsDisconnectModalOpen(false); From 7fb7cc93890dea89658673dee0c3f4ec4c1a7dfa Mon Sep 17 00:00:00 2001 From: nkdengineer Date: Fri, 14 Feb 2025 11:27:05 +0700 Subject: [PATCH 5/5] fix lint --- src/ROUTES.ts | 4 ++-- src/pages/workspace/accounting/PolicyAccountingPage.tsx | 4 ++-- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/src/ROUTES.ts b/src/ROUTES.ts index 1f3bf956de85d..dcd343bb932a4 100644 --- a/src/ROUTES.ts +++ b/src/ROUTES.ts @@ -1743,8 +1743,8 @@ const ROUTES = { }, POLICY_ACCOUNTING_XERO_ORGANIZATION: { route: 'settings/workspaces/:policyID/accounting/xero/organization/:currentOrganizationID', - getRoute: (policyID: string | undefined, currentOrganizationID: string) => { - if (!policyID) { + getRoute: (policyID: string | undefined, currentOrganizationID: string | undefined) => { + if (!policyID || !currentOrganizationID) { Log.warn('Invalid policyID is used to build the POLICY_ACCOUNTING_XERO_ORGANIZATION route'); } return `settings/workspaces/${policyID}/accounting/xero/organization/${currentOrganizationID}` as const; diff --git a/src/pages/workspace/accounting/PolicyAccountingPage.tsx b/src/pages/workspace/accounting/PolicyAccountingPage.tsx index 237c22ca0c6d5..784d21de98123 100644 --- a/src/pages/workspace/accounting/PolicyAccountingPage.tsx +++ b/src/pages/workspace/accounting/PolicyAccountingPage.tsx @@ -211,7 +211,7 @@ function PolicyAccountingPage({policy}: PolicyAccountingPageProps) { if (!(tenants.length > 1)) { return; } - Navigation.navigate(ROUTES.POLICY_ACCOUNTING_XERO_ORGANIZATION.getRoute(policyID, currentXeroOrganization?.id ?? '-1')); + Navigation.navigate(ROUTES.POLICY_ACCOUNTING_XERO_ORGANIZATION.getRoute(policyID, currentXeroOrganization?.id)); }, pendingAction: settingsPendingAction([CONST.XERO_CONFIG.TENANT_ID], policy?.connections?.xero?.config?.pendingFields), brickRoadIndicator: areSettingsInErrorFields([CONST.XERO_CONFIG.TENANT_ID], policy?.connections?.xero?.config?.errorFields) @@ -650,7 +650,7 @@ function PolicyAccountingPage({policy}: PolicyAccountingPageProps) { /> {translate('workspace.accounting.needAnotherAccounting')} - Navigation.navigate(ROUTES.REPORT_WITH_ID.getRoute(chatReportID ?? ''))}>{chatTextLink} + Navigation.navigate(ROUTES.REPORT_WITH_ID.getRoute(chatReportID))}>{chatTextLink} )}