From 0c056bed123c0eff882e120b692b04428fa5cf40 Mon Sep 17 00:00:00 2001 From: Jakub Butkiewicz Date: Fri, 16 Feb 2024 12:59:10 +0100 Subject: [PATCH 001/103] ref: move SignInOrAvatarWithOptionalStatus to TS --- ...js => SignInOrAvatarWithOptionalStatus.tsx} | 18 ++++-------------- 1 file changed, 4 insertions(+), 14 deletions(-) rename src/pages/home/sidebar/{SignInOrAvatarWithOptionalStatus.js => SignInOrAvatarWithOptionalStatus.tsx} (61%) diff --git a/src/pages/home/sidebar/SignInOrAvatarWithOptionalStatus.js b/src/pages/home/sidebar/SignInOrAvatarWithOptionalStatus.tsx similarity index 61% rename from src/pages/home/sidebar/SignInOrAvatarWithOptionalStatus.js rename to src/pages/home/sidebar/SignInOrAvatarWithOptionalStatus.tsx index 0ea6195cd713..2a9356d78232 100644 --- a/src/pages/home/sidebar/SignInOrAvatarWithOptionalStatus.js +++ b/src/pages/home/sidebar/SignInOrAvatarWithOptionalStatus.tsx @@ -1,6 +1,3 @@ -/* eslint-disable rulesdir/onyx-props-must-have-default */ -import lodashGet from 'lodash/get'; -import PropTypes from 'prop-types'; import React from 'react'; import useCurrentUserPersonalDetails from '@hooks/useCurrentUserPersonalDetails'; import * as Session from '@userActions/Session'; @@ -8,18 +5,13 @@ import AvatarWithOptionalStatus from './AvatarWithOptionalStatus'; import PressableAvatarWithIndicator from './PressableAvatarWithIndicator'; import SignInButton from './SignInButton'; -const propTypes = { - /** Whether the create menu is open or not */ - isCreateMenuOpen: PropTypes.bool, +type SignInOrAvatarWithOptionalStatusProps = { + isCreateMenuOpen?: boolean; }; -const defaultProps = { - isCreateMenuOpen: false, -}; - -function SignInOrAvatarWithOptionalStatus({isCreateMenuOpen}) { +function SignInOrAvatarWithOptionalStatus({isCreateMenuOpen = false}: SignInOrAvatarWithOptionalStatusProps) { const currentUserPersonalDetails = useCurrentUserPersonalDetails(); - const emojiStatus = lodashGet(currentUserPersonalDetails, 'status.emojiCode', ''); + const emojiStatus = currentUserPersonalDetails.status?.emojiCode ?? ''; if (Session.isAnonymousUser()) { return ; @@ -35,7 +27,5 @@ function SignInOrAvatarWithOptionalStatus({isCreateMenuOpen}) { return ; } -SignInOrAvatarWithOptionalStatus.propTypes = propTypes; -SignInOrAvatarWithOptionalStatus.defaultProps = defaultProps; SignInOrAvatarWithOptionalStatus.displayName = 'SignInOrAvatarWithOptionalStatus'; export default SignInOrAvatarWithOptionalStatus; From 6bd5633ec4631b4a1ef76b03c5a7e576d7d15ca8 Mon Sep 17 00:00:00 2001 From: Jakub Butkiewicz Date: Fri, 16 Feb 2024 13:04:18 +0100 Subject: [PATCH 002/103] ref: move SignInButton to TS --- src/pages/home/sidebar/{SignInButton.js => SignInButton.tsx} | 1 - 1 file changed, 1 deletion(-) rename src/pages/home/sidebar/{SignInButton.js => SignInButton.tsx} (95%) diff --git a/src/pages/home/sidebar/SignInButton.js b/src/pages/home/sidebar/SignInButton.tsx similarity index 95% rename from src/pages/home/sidebar/SignInButton.js rename to src/pages/home/sidebar/SignInButton.tsx index f89deb6f65b2..1dc65bfd5050 100644 --- a/src/pages/home/sidebar/SignInButton.js +++ b/src/pages/home/sidebar/SignInButton.tsx @@ -1,4 +1,3 @@ -/* eslint-disable rulesdir/onyx-props-must-have-default */ import React from 'react'; import {View} from 'react-native'; import Button from '@components/Button'; From 5ed0bc2c9fc59b181d3067e88b7c10c36e0ed497 Mon Sep 17 00:00:00 2001 From: Jakub Butkiewicz Date: Fri, 16 Feb 2024 13:05:47 +0100 Subject: [PATCH 003/103] ref: removed SidebarNavigationContext --- src/pages/home/sidebar/SidebarNavigationContext.js | 0 1 file changed, 0 insertions(+), 0 deletions(-) delete mode 100644 src/pages/home/sidebar/SidebarNavigationContext.js diff --git a/src/pages/home/sidebar/SidebarNavigationContext.js b/src/pages/home/sidebar/SidebarNavigationContext.js deleted file mode 100644 index e69de29bb2d1..000000000000 From 5443274f38ae358c00e9013f8100f7195ebe77e5 Mon Sep 17 00:00:00 2001 From: Jakub Butkiewicz Date: Fri, 16 Feb 2024 16:19:53 +0100 Subject: [PATCH 004/103] ref: move SidebarLinksData to TS --- src/libs/ReportUtils.ts | 2 +- src/libs/SidebarUtils.ts | 14 +- ...debarLinksData.js => SidebarLinksData.tsx} | 296 +++++++----------- 3 files changed, 127 insertions(+), 185 deletions(-) rename src/pages/home/sidebar/{SidebarLinksData.js => SidebarLinksData.tsx} (53%) diff --git a/src/libs/ReportUtils.ts b/src/libs/ReportUtils.ts index b5da21c0f67e..40581f46e76a 100644 --- a/src/libs/ReportUtils.ts +++ b/src/libs/ReportUtils.ts @@ -3824,7 +3824,7 @@ function shouldReportBeInOptionList({ report: OnyxEntry; currentReportId: string; isInGSDMode: boolean; - betas: Beta[]; + betas: OnyxEntry; policies: OnyxCollection; excludeEmptyChats: boolean; doesReportHaveViolations: boolean; diff --git a/src/libs/SidebarUtils.ts b/src/libs/SidebarUtils.ts index 5fe646c5ad13..7b757d7fc5b8 100644 --- a/src/libs/SidebarUtils.ts +++ b/src/libs/SidebarUtils.ts @@ -79,10 +79,10 @@ let hasInitialReportActions = false; */ function getOrderedReportIDs( currentReportId: string | null, - allReports: Record, - betas: Beta[], - policies: Record, - priorityMode: ValueOf, + allReports: OnyxEntry>, + betas: OnyxEntry, + policies: OnyxEntry>, + priorityMode: OnyxEntry>, allReportActions: OnyxCollection, transactionViolations: OnyxCollection, currentPolicyID = '', @@ -110,7 +110,7 @@ function getOrderedReportIDs( const isInGSDMode = priorityMode === CONST.PRIORITY_MODE.GSD; const isInDefaultMode = !isInGSDMode; - const allReportsDictValues = Object.values(allReports); + const allReportsDictValues = Object.values(allReports ?? {}); // Filter out all the reports that shouldn't be displayed let reportsToDisplay = allReportsDictValues.filter((report) => { @@ -118,7 +118,7 @@ function getOrderedReportIDs( const parentReportActions = allReportActions?.[parentReportActionsKey]; const parentReportAction = parentReportActions?.find((action) => action && report && action?.reportActionID === report?.parentReportActionID); const doesReportHaveViolations = - betas.includes(CONST.BETAS.VIOLATIONS) && !!parentReportAction && ReportUtils.doesTransactionThreadHaveViolations(report, transactionViolations, parentReportAction); + betas?.includes(CONST.BETAS.VIOLATIONS) && !!parentReportAction && ReportUtils.doesTransactionThreadHaveViolations(report, transactionViolations, parentReportAction); return ReportUtils.shouldReportBeInOptionList({ report, currentReportId: currentReportId ?? '', @@ -126,7 +126,7 @@ function getOrderedReportIDs( betas, policies, excludeEmptyChats: true, - doesReportHaveViolations, + doesReportHaveViolations: !!doesReportHaveViolations, }); }); diff --git a/src/pages/home/sidebar/SidebarLinksData.js b/src/pages/home/sidebar/SidebarLinksData.tsx similarity index 53% rename from src/pages/home/sidebar/SidebarLinksData.js rename to src/pages/home/sidebar/SidebarLinksData.tsx index 3bd538e8beab..b128fcaf33ec 100644 --- a/src/pages/home/sidebar/SidebarLinksData.js +++ b/src/pages/home/sidebar/SidebarLinksData.tsx @@ -1,154 +1,114 @@ +import {useIsFocused} from '@react-navigation/native'; import {deepEqual} from 'fast-equals'; -import lodashGet from 'lodash/get'; -import lodashMap from 'lodash/map'; -import PropTypes from 'prop-types'; import React, {useCallback, useEffect, useMemo, useRef} from 'react'; import {View} from 'react-native'; +import type {OnyxCollection, OnyxEntry} from 'react-native-onyx'; import {withOnyx} from 'react-native-onyx'; -import _ from 'underscore'; -import networkPropTypes from '@components/networkPropTypes'; -import {withNetwork} from '@components/OnyxProvider'; -import withCurrentReportID from '@components/withCurrentReportID'; -import withCurrentUserPersonalDetails from '@components/withCurrentUserPersonalDetails'; -import withNavigationFocus from '@components/withNavigationFocus'; +import type {EdgeInsets} from 'react-native-safe-area-context'; +import type {ValueOf} from 'type-fest'; import useActiveWorkspace from '@hooks/useActiveWorkspace'; +import useCurrentReportID from '@hooks/useCurrentReportID'; +import useCurrentUserPersonalDetails from '@hooks/useCurrentUserPersonalDetails'; import useLocalize from '@hooks/useLocalize'; +import useNetwork from '@hooks/useNetwork'; import usePrevious from '@hooks/usePrevious'; import useThemeStyles from '@hooks/useThemeStyles'; -import compose from '@libs/compose'; import {getPolicyMembersByIdWithoutCurrentUser} from '@libs/PolicyUtils'; import * as ReportUtils from '@libs/ReportUtils'; import SidebarUtils from '@libs/SidebarUtils'; -import reportPropTypes from '@pages/reportPropTypes'; import * as Policy from '@userActions/Policy'; import CONST from '@src/CONST'; import ONYXKEYS from '@src/ONYXKEYS'; -import SidebarLinks, {basePropTypes} from './SidebarLinks'; +import type * as OnyxTypes from '@src/types/onyx'; +import type {Message} from '@src/types/onyx/ReportAction'; +import SidebarLinks from './SidebarLinks'; -const propTypes = { - ...basePropTypes, - - /* Onyx Props */ - /** List of reports */ - chatReports: PropTypes.objectOf(reportPropTypes), - - /** All report actions for all reports */ - - /** Object of report actions for this report */ - allReportActions: PropTypes.objectOf( - PropTypes.arrayOf( - PropTypes.shape({ - error: PropTypes.string, - message: PropTypes.arrayOf( - PropTypes.shape({ - moderationDecision: PropTypes.shape({ - decision: PropTypes.string, - }), - }), - ), - }), - ), - ), - - /** Whether the reports are loading. When false it means they are ready to be used. */ - isLoadingApp: PropTypes.bool, - - /** The chat priority mode */ - priorityMode: PropTypes.string, - - /** Beta features list */ - betas: PropTypes.arrayOf(PropTypes.string), - - network: networkPropTypes.isRequired, - - /** The policies which the user has access to */ - // eslint-disable-next-line react/forbid-prop-types - policies: PropTypes.object, - - // eslint-disable-next-line react/forbid-prop-types - policyMembers: PropTypes.object, - - /** Session info for the currently logged in user. */ - session: PropTypes.shape({ - /** Currently logged in user accountID */ - accountID: PropTypes.number, - }), - /** All of the transaction violations */ - transactionViolations: PropTypes.shape({ - violations: PropTypes.arrayOf( - PropTypes.shape({ - /** The transaction ID */ - transactionID: PropTypes.number, - - /** The transaction violation type */ - type: PropTypes.string, - - /** The transaction violation message */ - message: PropTypes.string, - - /** The transaction violation data */ - data: PropTypes.shape({ - /** The transaction violation data field */ - field: PropTypes.string, - - /** The transaction violation data value */ - value: PropTypes.string, - }), - }), - ), - }), +type SidebarLinksDataOnyxProps = { + chatReports: OnyxEntry< + Pick< + OnyxTypes.Report, + | 'reportID' + | 'participantAccountIDs' + | 'hasDraft' + | 'isPinned' + | 'isHidden' + | 'notificationPreference' + | 'errorFields' + | 'lastMessageText' + | 'lastVisibleActionCreated' + | 'iouReportID' + | 'total' + | 'nonReimbursableTotal' + | 'hasOutstandingChildRequest' + | 'isWaitingOnBankAccount' + | 'statusNum' + | 'stateNum' + | 'chatType' + | 'type' + | 'policyID' + | 'visibility' + | 'lastReadTime' + | 'reportName' + | 'policyName' + | 'oldPolicyName' + | 'ownerAccountID' + | 'currency' + | 'managerID' + | 'parentReportActionID' + | 'parentReportID' + | 'isDeletedParentAction' + > & {isUnreadWithMention: boolean} + >; + isLoadingApp: OnyxEntry; + priorityMode: OnyxEntry>; + betas: OnyxEntry; + allReportActions: OnyxEntry>>; + policies: OnyxEntry>; + policyMembers: OnyxCollection; + transactionViolations: OnyxCollection; }; -const defaultProps = { - chatReports: {}, - allReportActions: {}, - isLoadingApp: true, - priorityMode: CONST.PRIORITY_MODE.DEFAULT, - betas: [], - policies: {}, - policyMembers: {}, - session: { - accountID: '', - }, - transactionViolations: {}, +type SidebarLinksDataProps = SidebarLinksDataOnyxProps & { + onLinkClick: (reportID: number) => void; + insets: EdgeInsets; }; function SidebarLinksData({ - isFocused, allReportActions, betas, chatReports, - currentReportID, insets, - isLoadingApp, + isLoadingApp = true, onLinkClick, policies, - priorityMode, - network, + priorityMode = CONST.PRIORITY_MODE.DEFAULT, policyMembers, - session: {accountID}, + // session: {accountID}, transactionViolations, -}) { +}: SidebarLinksDataProps) { + const {currentReportID} = useCurrentReportID() ?? {}; + const {accountID} = useCurrentUserPersonalDetails(); + const network = useNetwork(); + const isFocused = useIsFocused(); const styles = useThemeStyles(); const {activeWorkspaceID} = useActiveWorkspace(); const {translate} = useLocalize(); const prevPriorityMode = usePrevious(priorityMode); - const policyMemberAccountIDs = getPolicyMembersByIdWithoutCurrentUser(policyMembers, activeWorkspaceID, accountID); // eslint-disable-next-line react-hooks/exhaustive-deps - useEffect(() => Policy.openWorkspace(activeWorkspaceID, policyMemberAccountIDs), [activeWorkspaceID]); + useEffect(() => Policy.openWorkspace(activeWorkspaceID ?? '', policyMemberAccountIDs), [activeWorkspaceID]); - const reportIDsRef = useRef(null); + const reportIDsRef = useRef(null); const isLoading = isLoadingApp; const optionListItems = useMemo(() => { const reportIDs = SidebarUtils.getOrderedReportIDs( null, - chatReports, + chatReports as OnyxEntry>, betas, - policies, + policies as OnyxEntry>, priorityMode, - allReportActions, + allReportActions as OnyxEntry>, transactionViolations, activeWorkspaceID, policyMemberAccountIDs, @@ -161,7 +121,7 @@ function SidebarLinksData({ // 1. We need to update existing reports only once while loading because they are updated several times during loading and causes this regression: https://github.com/Expensify/App/issues/24596#issuecomment-1681679531 // 2. If the user is offline, we need to update the reports unconditionally, since the loading of report data might be stuck in this case. // 3. Changing priority mode to Most Recent will call OpenApp. If there is an existing reports and the priority mode is updated, we want to immediately update the list instead of waiting the OpenApp request to complete - if (!isLoading || !reportIDsRef.current || network.isOffline || (reportIDsRef.current && prevPriorityMode !== priorityMode)) { + if (!isLoading || !reportIDsRef.current || !!network.isOffline || (reportIDsRef.current && prevPriorityMode !== priorityMode)) { reportIDsRef.current = reportIDs; } return reportIDsRef.current || []; @@ -173,14 +133,14 @@ function SidebarLinksData({ // the current report is missing from the list, which should very rarely happen. In this // case we re-generate the list a 2nd time with the current report included. const optionListItemsWithCurrentReport = useMemo(() => { - if (currentReportID && !_.contains(optionListItems, currentReportID)) { + if (currentReportID && !optionListItems?.includes(currentReportID)) { return SidebarUtils.getOrderedReportIDs( currentReportID, - chatReports, + chatReports as OnyxEntry>, betas, - policies, + policies as OnyxEntry>, priorityMode, - allReportActions, + allReportActions as OnyxEntry>, transactionViolations, activeWorkspaceID, policyMemberAccountIDs, @@ -191,7 +151,7 @@ function SidebarLinksData({ const currentReportIDRef = useRef(currentReportID); currentReportIDRef.current = currentReportID; - const isActiveReport = useCallback((reportID) => currentReportIDRef.current === reportID, []); + const isActiveReport = useCallback((reportID: string) => currentReportIDRef.current === reportID, []); return ( +const chatReportSelector = (report: OnyxEntry) => report && { reportID: report.reportID, participantAccountIDs: report.participantAccountIDs, @@ -233,7 +190,7 @@ const chatReportSelector = (report) => isHidden: report.isHidden, notificationPreference: report.notificationPreference, errorFields: { - addWorkspaceRoom: report.errorFields && report.errorFields.addWorkspaceRoom, + addWorkspaceRoom: report.errorFields?.addWorkspaceRoom, }, lastMessageText: report.lastMessageText, lastVisibleActionCreated: report.lastVisibleActionCreated, @@ -264,78 +221,63 @@ const chatReportSelector = (report) => isUnreadWithMention: ReportUtils.isUnreadWithMention(report), }; -/** - * @param {Object} [reportActions] - * @returns {Object|undefined} - */ -const reportActionsSelector = (reportActions) => +const reportActionsSelector = (reportActions: OnyxEntry) => reportActions && - lodashMap(reportActions, (reportAction) => { - const {reportActionID, parentReportActionID, actionName, errors = []} = reportAction; - const decision = lodashGet(reportAction, 'message[0].moderationDecision.decision'); + Object.values(reportActions).map((reportAction) => { + const {reportActionID, actionName, errors} = reportAction; + const decision = reportAction.message?.[0].moderationDecision?.decision; return { reportActionID, - parentReportActionID, actionName, errors, message: [ { moderationDecision: {decision}, }, - ], + ] as Message[], }; }); -/** - * @param {Object} [policy] - * @returns {Object|undefined} - */ -const policySelector = (policy) => +const policySelector = (policy: OnyxEntry) => policy && { type: policy.type, name: policy.name, avatar: policy.avatar, }; -export default compose( - withCurrentReportID, - withCurrentUserPersonalDetails, - withNavigationFocus, - withNetwork(), - withOnyx({ - chatReports: { - key: ONYXKEYS.COLLECTION.REPORT, - selector: chatReportSelector, - initialValue: {}, - }, - isLoadingApp: { - key: ONYXKEYS.IS_LOADING_APP, - }, - priorityMode: { - key: ONYXKEYS.NVP_PRIORITY_MODE, - initialValue: CONST.PRIORITY_MODE.DEFAULT, - }, - betas: { - key: ONYXKEYS.BETAS, - initialValue: [], - }, - allReportActions: { - key: ONYXKEYS.COLLECTION.REPORT_ACTIONS, - selector: reportActionsSelector, - initialValue: {}, - }, - policies: { - key: ONYXKEYS.COLLECTION.POLICY, - selector: policySelector, - initialValue: {}, - }, - policyMembers: { - key: ONYXKEYS.COLLECTION.POLICY_MEMBERS, - }, - transactionViolations: { - key: ONYXKEYS.COLLECTION.TRANSACTION_VIOLATIONS, - initialValue: {}, - }, - }), -)(SidebarLinksData); +export default withOnyx({ + chatReports: { + key: ONYXKEYS.COLLECTION.REPORT, + selector: chatReportSelector, + initialValue: {}, + }, + isLoadingApp: { + key: ONYXKEYS.IS_LOADING_APP, + }, + priorityMode: { + key: ONYXKEYS.NVP_PRIORITY_MODE, + initialValue: CONST.PRIORITY_MODE.DEFAULT, + }, + betas: { + key: ONYXKEYS.BETAS, + initialValue: [], + }, + allReportActions: { + key: ONYXKEYS.COLLECTION.REPORT_ACTIONS, + selector: reportActionsSelector, + initialValue: {}, + }, + policies: { + key: ONYXKEYS.COLLECTION.POLICY, + selector: policySelector, + initialValue: {}, + }, + policyMembers: { + key: ONYXKEYS.COLLECTION.POLICY_MEMBERS, + }, + transactionViolations: { + key: ONYXKEYS.COLLECTION.TRANSACTION_VIOLATIONS, + initialValue: {}, + }, +})(SidebarLinksData); From bc5cd4df25c3dbd9e8a07e14a8abc44d61ec32b0 Mon Sep 17 00:00:00 2001 From: Jakub Butkiewicz Date: Fri, 16 Feb 2024 16:46:36 +0100 Subject: [PATCH 005/103] ref: move SidebarLinks to TS --- src/components/LHNOptionsList/types.ts | 2 +- .../{SidebarLinks.js => SidebarLinks.tsx} | 64 +++++++++---------- src/pages/home/sidebar/SidebarLinksData.tsx | 4 +- 3 files changed, 32 insertions(+), 38 deletions(-) rename src/pages/home/sidebar/{SidebarLinks.js => SidebarLinks.tsx} (78%) diff --git a/src/components/LHNOptionsList/types.ts b/src/components/LHNOptionsList/types.ts index 58bea97f04c9..f3d6bde9d41c 100644 --- a/src/components/LHNOptionsList/types.ts +++ b/src/components/LHNOptionsList/types.ts @@ -45,7 +45,7 @@ type CustomLHNOptionsListProps = { contentContainerStyles?: StyleProp; /** Sections for the section list */ - data: string[]; + data: string[] | null; /** Callback to fire when a row is selected */ onSelectRow?: (optionItem: OptionData, popoverAnchor: RefObject) => void; diff --git a/src/pages/home/sidebar/SidebarLinks.js b/src/pages/home/sidebar/SidebarLinks.tsx similarity index 78% rename from src/pages/home/sidebar/SidebarLinks.js rename to src/pages/home/sidebar/SidebarLinks.tsx index 9431bae68d8a..52165c148727 100644 --- a/src/pages/home/sidebar/SidebarLinks.js +++ b/src/pages/home/sidebar/SidebarLinks.tsx @@ -1,10 +1,9 @@ -/* eslint-disable rulesdir/onyx-props-must-have-default */ -import lodashGet from 'lodash/get'; -import PropTypes from 'prop-types'; import React, {useCallback, useEffect, useMemo, useRef} from 'react'; import {InteractionManager, StyleSheet, View} from 'react-native'; +import type {OnyxEntry} from 'react-native-onyx'; import {withOnyx} from 'react-native-onyx'; -import _ from 'underscore'; +import type {EdgeInsets} from 'react-native-safe-area-context'; +import type {ValueOf} from 'type-fest'; import Breadcrumbs from '@components/Breadcrumbs'; import LHNOptionsList from '@components/LHNOptionsList/LHNOptionsList'; import OptionsListSkeletonView from '@components/OptionsListSkeletonView'; @@ -16,37 +15,33 @@ import KeyboardShortcut from '@libs/KeyboardShortcut'; import Navigation from '@libs/Navigation/Navigation'; import onyxSubscribe from '@libs/onyxSubscribe'; import * as ReportActionContextMenu from '@pages/home/report/ContextMenu/ReportActionContextMenu'; -import safeAreaInsetPropTypes from '@pages/safeAreaInsetPropTypes'; import * as App from '@userActions/App'; import CONST from '@src/CONST'; import ONYXKEYS from '@src/ONYXKEYS'; import ROUTES from '@src/ROUTES'; +import type {Modal, Policy, Report} from '@src/types/onyx'; -const basePropTypes = { - /** Toggles the navigation menu open and closed */ - onLinkClick: PropTypes.func.isRequired, - - /** Safe area insets required for mobile devices margins */ - insets: safeAreaInsetPropTypes.isRequired, +type SidebarLinksOnyxProps = { + activePolicy: OnyxEntry; }; -const propTypes = { - ...basePropTypes, - - optionListItems: PropTypes.arrayOf(PropTypes.string).isRequired, - - isLoading: PropTypes.bool.isRequired, - - // eslint-disable-next-line react/require-default-props - priorityMode: PropTypes.oneOf(_.values(CONST.PRIORITY_MODE)), - - isActiveReport: PropTypes.func.isRequired, +type SidebarLinksProps = SidebarLinksOnyxProps & { + onLinkClick: () => void; + insets: EdgeInsets; + optionListItems: string[] | null; + isLoading: OnyxEntry; + priorityMode?: OnyxEntry>; + isActiveReport: (reportID: string) => boolean; + isCreateMenuOpen?: boolean; + + // eslint-disable-next-line react/no-unused-prop-types -- its used in withOnyx + activeWorkspaceID: string | undefined; }; -function SidebarLinks({onLinkClick, insets, optionListItems, isLoading, priorityMode = CONST.PRIORITY_MODE.DEFAULT, isActiveReport, isCreateMenuOpen, activePolicy}) { +function SidebarLinks({onLinkClick, insets, optionListItems, isLoading, priorityMode = CONST.PRIORITY_MODE.DEFAULT, isActiveReport, isCreateMenuOpen, activePolicy}: SidebarLinksProps) { const styles = useThemeStyles(); const StyleUtils = useStyleUtils(); - const modal = useRef({}); + const modal = useRef({}); const {translate, updateLocale} = useLocalize(); const {isSmallScreenWidth} = useWindowDimensions(); @@ -67,7 +62,7 @@ function SidebarLinks({onLinkClick, insets, optionListItems, isLoading, priority const unsubscribeOnyxModal = onyxSubscribe({ key: ONYXKEYS.MODAL, callback: (modalArg) => { - if (_.isNull(modalArg) || typeof modalArg !== 'object') { + if (modalArg === null || typeof modalArg !== 'object') { return; } modal.current = modalArg; @@ -105,18 +100,19 @@ function SidebarLinks({onLinkClick, insets, optionListItems, isLoading, priority /** * Show Report page with selected report id - * - * @param {Object} option - * @param {String} option.reportID */ const showReportPage = useCallback( - (option) => { + (option: Report) => { // Prevent opening Report page when clicking LHN row quickly after clicking FAB icon // or when clicking the active LHN row on large screens // or when continuously clicking different LHNs, only apply to small screen // since getTopmostReportId always returns on other devices const reportActionID = Navigation.getTopmostReportActionId(); - if (isCreateMenuOpen || (option.reportID === Navigation.getTopmostReportId() && !reportActionID) || (isSmallScreenWidth && isActiveReport(option.reportID) && !reportActionID)) { + if ( + !!isCreateMenuOpen || + (option.reportID === Navigation.getTopmostReportId() && !reportActionID) || + (isSmallScreenWidth && isActiveReport(option.reportID) && !reportActionID) + ) { return; } Navigation.navigate(ROUTES.REPORT_WITH_ID.getRoute(option.reportID)); @@ -137,7 +133,7 @@ function SidebarLinks({onLinkClick, insets, optionListItems, isLoading, priority activePolicy ? { type: CONST.BREADCRUMB_TYPE.STRONG, - text: lodashGet(activePolicy, 'name', ''), + text: activePolicy.name ?? '', } : { type: CONST.BREADCRUMB_TYPE.ROOT, @@ -158,7 +154,7 @@ function SidebarLinks({onLinkClick, insets, optionListItems, isLoading, priority optionMode={viewMode} onFirstItemRendered={App.setSidebarLoaded} /> - {isLoading && optionListItems.length === 0 && ( + {isLoading && optionListItems?.length === 0 && ( @@ -168,12 +164,10 @@ function SidebarLinks({onLinkClick, insets, optionListItems, isLoading, priority ); } -SidebarLinks.propTypes = propTypes; SidebarLinks.displayName = 'SidebarLinks'; -export default withOnyx({ +export default withOnyx({ activePolicy: { key: ({activeWorkspaceID}) => `${ONYXKEYS.COLLECTION.POLICY}${activeWorkspaceID}`, }, })(SidebarLinks); -export {basePropTypes}; diff --git a/src/pages/home/sidebar/SidebarLinksData.tsx b/src/pages/home/sidebar/SidebarLinksData.tsx index b128fcaf33ec..ddd232e99275 100644 --- a/src/pages/home/sidebar/SidebarLinksData.tsx +++ b/src/pages/home/sidebar/SidebarLinksData.tsx @@ -69,7 +69,7 @@ type SidebarLinksDataOnyxProps = { }; type SidebarLinksDataProps = SidebarLinksDataOnyxProps & { - onLinkClick: (reportID: number) => void; + onLinkClick: () => void; insets: EdgeInsets; }; @@ -101,7 +101,7 @@ function SidebarLinksData({ const reportIDsRef = useRef(null); const isLoading = isLoadingApp; - const optionListItems = useMemo(() => { + const optionListItems: string[] | null = useMemo(() => { const reportIDs = SidebarUtils.getOrderedReportIDs( null, chatReports as OnyxEntry>, From 3fb3715847c5c82958147940cdcb68073456aedf Mon Sep 17 00:00:00 2001 From: Jakub Butkiewicz Date: Fri, 16 Feb 2024 16:54:54 +0100 Subject: [PATCH 006/103] ref: move PressableAvatarWithIndicator to TS --- ...or.js => PressableAvatarWithIndicator.tsx} | 52 ++++++------------- 1 file changed, 17 insertions(+), 35 deletions(-) rename src/pages/home/sidebar/{PressableAvatarWithIndicator.js => PressableAvatarWithIndicator.tsx} (56%) diff --git a/src/pages/home/sidebar/PressableAvatarWithIndicator.js b/src/pages/home/sidebar/PressableAvatarWithIndicator.tsx similarity index 56% rename from src/pages/home/sidebar/PressableAvatarWithIndicator.js rename to src/pages/home/sidebar/PressableAvatarWithIndicator.tsx index 63c5936e957b..e07b6e856823 100644 --- a/src/pages/home/sidebar/PressableAvatarWithIndicator.js +++ b/src/pages/home/sidebar/PressableAvatarWithIndicator.tsx @@ -1,44 +1,30 @@ -/* eslint-disable rulesdir/onyx-props-must-have-default */ -import lodashGet from 'lodash/get'; -import PropTypes from 'prop-types'; import React, {useCallback} from 'react'; +import type {OnyxEntry} from 'react-native-onyx'; import {withOnyx} from 'react-native-onyx'; import AvatarWithIndicator from '@components/AvatarWithIndicator'; import OfflineWithFeedback from '@components/OfflineWithFeedback'; import PressableWithoutFeedback from '@components/Pressable/PressableWithoutFeedback'; -import withCurrentUserPersonalDetails from '@components/withCurrentUserPersonalDetails'; +import useCurrentUserPersonalDetails from '@hooks/useCurrentUserPersonalDetails'; import useLocalize from '@hooks/useLocalize'; -import compose from '@libs/compose'; import Navigation from '@libs/Navigation/Navigation'; import * as UserUtils from '@libs/UserUtils'; -import personalDetailsPropType from '@pages/personalDetailsPropType'; import CONST from '@src/CONST'; import ONYXKEYS from '@src/ONYXKEYS'; import ROUTES from '@src/ROUTES'; -const propTypes = { - /** Whether the create menu is open or not */ - isCreateMenuOpen: PropTypes.bool, - - /** The personal details of the person who is logged in */ - currentUserPersonalDetails: personalDetailsPropType, - +type PressableAvatarWithIndicatorOnyxProps = { /** Indicates whether the app is loading initial data */ - isLoading: PropTypes.bool, + isLoading: OnyxEntry; }; -const defaultProps = { - isCreateMenuOpen: false, - currentUserPersonalDetails: { - pendingFields: {avatar: ''}, - accountID: '', - avatar: '', - }, - isLoading: true, +type PressableAvatarWithIndicatorProps = PressableAvatarWithIndicatorOnyxProps & { + /** Whether the create menu is open or not */ + isCreateMenuOpen: boolean; }; -function PressableAvatarWithIndicator({isCreateMenuOpen, currentUserPersonalDetails, isLoading}) { +function PressableAvatarWithIndicator({isCreateMenuOpen = false, isLoading = true}: PressableAvatarWithIndicatorProps) { const {translate} = useLocalize(); + const currentUserPersonalDetails = useCurrentUserPersonalDetails(); const showSettingsPage = useCallback(() => { if (isCreateMenuOpen) { @@ -55,26 +41,22 @@ function PressableAvatarWithIndicator({isCreateMenuOpen, currentUserPersonalDeta role={CONST.ROLE.BUTTON} onPress={showSettingsPage} > - + ); } -PressableAvatarWithIndicator.propTypes = propTypes; -PressableAvatarWithIndicator.defaultProps = defaultProps; PressableAvatarWithIndicator.displayName = 'PressableAvatarWithIndicator'; -export default compose( - withCurrentUserPersonalDetails, - withOnyx({ - isLoading: { - key: ONYXKEYS.IS_LOADING_APP, - }, - }), -)(PressableAvatarWithIndicator); + +export default withOnyx({ + isLoading: { + key: ONYXKEYS.IS_LOADING_APP, + }, +})(PressableAvatarWithIndicator); From 5fa814c327d1e9767afa896e3304487885039547 Mon Sep 17 00:00:00 2001 From: Jakub Butkiewicz Date: Fri, 16 Feb 2024 16:57:26 +0100 Subject: [PATCH 007/103] ref: move AvatarWithOptionalStatus to TS --- ...alStatus.js => AvatarWithOptionalStatus.tsx} | 17 ++++------------- 1 file changed, 4 insertions(+), 13 deletions(-) rename src/pages/home/sidebar/{AvatarWithOptionalStatus.js => AvatarWithOptionalStatus.tsx} (81%) diff --git a/src/pages/home/sidebar/AvatarWithOptionalStatus.js b/src/pages/home/sidebar/AvatarWithOptionalStatus.tsx similarity index 81% rename from src/pages/home/sidebar/AvatarWithOptionalStatus.js rename to src/pages/home/sidebar/AvatarWithOptionalStatus.tsx index e1ff3982a0cc..5597d46c29bc 100644 --- a/src/pages/home/sidebar/AvatarWithOptionalStatus.js +++ b/src/pages/home/sidebar/AvatarWithOptionalStatus.tsx @@ -1,5 +1,3 @@ -/* eslint-disable rulesdir/onyx-props-must-have-default */ -import PropTypes from 'prop-types'; import React, {useCallback} from 'react'; import {View} from 'react-native'; import PressableWithoutFeedback from '@components/Pressable/PressableWithoutFeedback'; @@ -12,20 +10,15 @@ import CONST from '@src/CONST'; import ROUTES from '@src/ROUTES'; import PressableAvatarWithIndicator from './PressableAvatarWithIndicator'; -const propTypes = { +type AvatarWithOptionalStatusProps = { /** Whether the create menu is open or not */ - isCreateMenuOpen: PropTypes.bool, + isCreateMenuOpen: boolean; /** Emoji status */ - emojiStatus: PropTypes.string, + emojiStatus: string; }; -const defaultProps = { - isCreateMenuOpen: false, - emojiStatus: '', -}; - -function AvatarWithOptionalStatus({emojiStatus, isCreateMenuOpen}) { +function AvatarWithOptionalStatus({emojiStatus = '', isCreateMenuOpen = false}: AvatarWithOptionalStatusProps) { const styles = useThemeStyles(); const {translate} = useLocalize(); @@ -61,7 +54,5 @@ function AvatarWithOptionalStatus({emojiStatus, isCreateMenuOpen}) { ); } -AvatarWithOptionalStatus.propTypes = propTypes; -AvatarWithOptionalStatus.defaultProps = defaultProps; AvatarWithOptionalStatus.displayName = 'AvatarWithOptionalStatus'; export default AvatarWithOptionalStatus; From 6733581a39b700a147c1708f606cdc6fede9b1ee Mon Sep 17 00:00:00 2001 From: Jakub Butkiewicz Date: Fri, 16 Feb 2024 17:02:16 +0100 Subject: [PATCH 008/103] ref: move BaseSidebarScreen to TS --- src/pages/home/sidebar/SidebarLinks.tsx | 2 +- src/pages/home/sidebar/SidebarLinksData.tsx | 2 +- .../{BaseSidebarScreen.js => BaseSidebarScreen.tsx} | 3 +-- 3 files changed, 3 insertions(+), 4 deletions(-) rename src/pages/home/sidebar/SidebarScreen/{BaseSidebarScreen.js => BaseSidebarScreen.tsx} (94%) diff --git a/src/pages/home/sidebar/SidebarLinks.tsx b/src/pages/home/sidebar/SidebarLinks.tsx index 52165c148727..f01ed07ea476 100644 --- a/src/pages/home/sidebar/SidebarLinks.tsx +++ b/src/pages/home/sidebar/SidebarLinks.tsx @@ -27,7 +27,7 @@ type SidebarLinksOnyxProps = { type SidebarLinksProps = SidebarLinksOnyxProps & { onLinkClick: () => void; - insets: EdgeInsets; + insets: EdgeInsets | undefined; optionListItems: string[] | null; isLoading: OnyxEntry; priorityMode?: OnyxEntry>; diff --git a/src/pages/home/sidebar/SidebarLinksData.tsx b/src/pages/home/sidebar/SidebarLinksData.tsx index ddd232e99275..ab84f69e824b 100644 --- a/src/pages/home/sidebar/SidebarLinksData.tsx +++ b/src/pages/home/sidebar/SidebarLinksData.tsx @@ -70,7 +70,7 @@ type SidebarLinksDataOnyxProps = { type SidebarLinksDataProps = SidebarLinksDataOnyxProps & { onLinkClick: () => void; - insets: EdgeInsets; + insets: EdgeInsets | undefined; }; function SidebarLinksData({ diff --git a/src/pages/home/sidebar/SidebarScreen/BaseSidebarScreen.js b/src/pages/home/sidebar/SidebarScreen/BaseSidebarScreen.tsx similarity index 94% rename from src/pages/home/sidebar/SidebarScreen/BaseSidebarScreen.js rename to src/pages/home/sidebar/SidebarScreen/BaseSidebarScreen.tsx index 9188a859d175..314b3921cc0b 100644 --- a/src/pages/home/sidebar/SidebarScreen/BaseSidebarScreen.js +++ b/src/pages/home/sidebar/SidebarScreen/BaseSidebarScreen.tsx @@ -17,7 +17,7 @@ const startTimer = () => { Performance.markStart(CONST.TIMING.SWITCH_REPORT); }; -function BaseSidebarScreen(props) { +function BaseSidebarScreen() { const styles = useThemeStyles(); useEffect(() => { Performance.markStart(CONST.TIMING.SIDEBAR_LOADED); @@ -37,7 +37,6 @@ function BaseSidebarScreen(props) { )} From 2fad64eddb709a5bdde140f53ce65beb63b64578 Mon Sep 17 00:00:00 2001 From: Jakub Butkiewicz Date: Tue, 20 Feb 2024 10:02:39 +0100 Subject: [PATCH 009/103] ref: move FloatingActionButtonAndPopover to TS --- src/libs/actions/Policy.ts | 6 +- src/libs/actions/Task.ts | 2 +- ....js => FloatingActionButtonAndPopover.tsx} | 139 +++++++----------- 3 files changed, 58 insertions(+), 89 deletions(-) rename src/pages/home/sidebar/SidebarScreen/{FloatingActionButtonAndPopover.js => FloatingActionButtonAndPopover.tsx} (70%) diff --git a/src/libs/actions/Policy.ts b/src/libs/actions/Policy.ts index 9e6745bbc291..911837cd3bf5 100644 --- a/src/libs/actions/Policy.ts +++ b/src/libs/actions/Policy.ts @@ -81,7 +81,7 @@ type OptimisticCustomUnits = { outputCurrency: string; }; -type PoliciesRecord = Record>; +type PoliciesRecord = Record; type NewCustomUnit = { customUnitID: string; @@ -206,8 +206,8 @@ function updateLastAccessedWorkspace(policyID: OnyxEntry) { /** * Check if the user has any active free policies (aka workspaces) */ -function hasActiveFreePolicy(policies: Array> | PoliciesRecord): boolean { - const adminFreePolicies = Object.values(policies).filter((policy) => policy && policy.type === CONST.POLICY.TYPE.FREE && policy.role === CONST.POLICY.ROLE.ADMIN); +function hasActiveFreePolicy(policies: OnyxEntry): boolean { + const adminFreePolicies = Object.values(policies ?? {}).filter((policy) => policy && policy.type === CONST.POLICY.TYPE.FREE && policy.role === CONST.POLICY.ROLE.ADMIN); if (adminFreePolicies.length === 0) { return false; diff --git a/src/libs/actions/Task.ts b/src/libs/actions/Task.ts index 28cecf460a5f..b05e7cd6b2d9 100644 --- a/src/libs/actions/Task.ts +++ b/src/libs/actions/Task.ts @@ -645,7 +645,7 @@ function setParentReportID(parentReportID: string) { /** * Clears out the task info from the store and navigates to the NewTaskDetails page */ -function clearOutTaskInfoAndNavigate(reportID: string) { +function clearOutTaskInfoAndNavigate(reportID?: string) { clearOutTaskInfo(); if (reportID && reportID !== '0') { setParentReportID(reportID); diff --git a/src/pages/home/sidebar/SidebarScreen/FloatingActionButtonAndPopover.js b/src/pages/home/sidebar/SidebarScreen/FloatingActionButtonAndPopover.tsx similarity index 70% rename from src/pages/home/sidebar/SidebarScreen/FloatingActionButtonAndPopover.js rename to src/pages/home/sidebar/SidebarScreen/FloatingActionButtonAndPopover.tsx index 0df490fa4466..83cc719f2e90 100644 --- a/src/pages/home/sidebar/SidebarScreen/FloatingActionButtonAndPopover.js +++ b/src/pages/home/sidebar/SidebarScreen/FloatingActionButtonAndPopover.tsx @@ -1,17 +1,16 @@ -import PropTypes from 'prop-types'; +import {useIsFocused, useNavigation} from '@react-navigation/native'; +import type {ForwardedRef} from 'react'; import React, {forwardRef, useCallback, useEffect, useImperativeHandle, useRef, useState} from 'react'; import {View} from 'react-native'; +import type {OnyxEntry} from 'react-native-onyx'; import {withOnyx} from 'react-native-onyx'; import FloatingActionButton from '@components/FloatingActionButton'; import * as Expensicons from '@components/Icon/Expensicons'; import PopoverMenu from '@components/PopoverMenu'; -import withNavigation from '@components/withNavigation'; -import withNavigationFocus from '@components/withNavigationFocus'; -import withWindowDimensions, {windowDimensionsPropTypes} from '@components/withWindowDimensions'; import useLocalize from '@hooks/useLocalize'; import usePrevious from '@hooks/usePrevious'; import useThemeStyles from '@hooks/useThemeStyles'; -import compose from '@libs/compose'; +import useWindowDimensions from '@hooks/useWindowDimensions'; import interceptAnonymousUser from '@libs/interceptAnonymousUser'; import Navigation from '@libs/Navigation/Navigation'; import * as ReportUtils from '@libs/ReportUtils'; @@ -22,74 +21,53 @@ import * as Task from '@userActions/Task'; import CONST from '@src/CONST'; import ONYXKEYS from '@src/ONYXKEYS'; import ROUTES from '@src/ROUTES'; +import type * as OnyxTypes from '@src/types/onyx'; -/** - * @param {Object} [policy] - * @returns {Object|undefined} - */ -const policySelector = (policy) => - policy && { - type: policy.type, - role: policy.role, - isPolicyExpenseChatEnabled: policy.isPolicyExpenseChatEnabled, - pendingAction: policy.pendingAction, - }; - -const propTypes = { - ...windowDimensionsPropTypes, +type FloatingActionButtonAndPopoverOnyxProps = { + /** The list of policies the user has access to. */ + allPolicies: OnyxEntry>; + isLoading: OnyxEntry; +}; +type FloatingActionButtonAndPopoverProps = FloatingActionButtonAndPopoverOnyxProps & { /* Callback function when the menu is shown */ - onShowCreateMenu: PropTypes.func, + onShowCreateMenu: () => void; /* Callback function before the menu is hidden */ - onHideCreateMenu: PropTypes.func, - - /** The list of policies the user has access to. */ - allPolicies: PropTypes.shape({ - /** The policy name */ - name: PropTypes.string, - }), - - /** Indicated whether the report data is loading */ - isLoading: PropTypes.bool, - - /** Forwarded ref to FloatingActionButtonAndPopover */ - innerRef: PropTypes.oneOfType([PropTypes.func, PropTypes.object]), + onHideCreateMenu: () => void; }; -const defaultProps = { - onHideCreateMenu: () => {}, - onShowCreateMenu: () => {}, - allPolicies: {}, - isLoading: false, - innerRef: null, + +type FloatingActionButtonAndPopoverRef = { + hideCreateMenu: () => void; }; /** * Responsible for rendering the {@link PopoverMenu}, and the accompanying * FAB that can open or close the menu. - * @param {Object} props - * @returns {JSX.Element} */ -function FloatingActionButtonAndPopover(props) { +function FloatingActionButtonAndPopover( + {onHideCreateMenu = () => {}, onShowCreateMenu = () => {}, isLoading, allPolicies}: FloatingActionButtonAndPopoverProps, + ref: ForwardedRef, +) { + console.log('allPolices', allPolicies); const styles = useThemeStyles(); const {translate} = useLocalize(); const [isCreateMenuActive, setIsCreateMenuActive] = useState(false); - const fabRef = useRef(null); + const fabRef = useRef(null); + const {isSmallScreenWidth, windowHeight} = useWindowDimensions(); + const isFocused = useIsFocused(); - const prevIsFocused = usePrevious(props.isFocused); + const prevIsFocused = usePrevious(isFocused); /** * Check if LHN status changed from active to inactive. * Used to close already opened FAB menu when open any other pages (i.e. Press Command + K on web). - * - * @param {Object} prevProps - * @return {Boolean} */ const didScreenBecomeInactive = useCallback( () => // When any other page is opened over LHN - !props.isFocused && prevIsFocused, - [props.isFocused, prevIsFocused], + !isFocused && prevIsFocused, + [isFocused, prevIsFocused], ); /** @@ -97,14 +75,14 @@ function FloatingActionButtonAndPopover(props) { */ const showCreateMenu = useCallback( () => { - if (!props.isFocused && props.isSmallScreenWidth) { + if (!isFocused && isSmallScreenWidth) { return; } setIsCreateMenuActive(true); - props.onShowCreateMenu(); + onShowCreateMenu(); }, // eslint-disable-next-line react-hooks/exhaustive-deps - [props.isFocused, props.isSmallScreenWidth], + [isFocused, isSmallScreenWidth], ); /** @@ -118,7 +96,7 @@ function FloatingActionButtonAndPopover(props) { return; } setIsCreateMenuActive(false); - props.onHideCreateMenu(); + onHideCreateMenu(); }, // eslint-disable-next-line react-hooks/exhaustive-deps [isCreateMenuActive], @@ -133,7 +111,7 @@ function FloatingActionButtonAndPopover(props) { hideCreateMenu(); }, [didScreenBecomeInactive, hideCreateMenu]); - useImperativeHandle(props.innerRef, () => ({ + useImperativeHandle(ref, () => ({ hideCreateMenu() { hideCreateMenu(); }, @@ -151,10 +129,10 @@ function FloatingActionButtonAndPopover(props) { interceptAnonymousUser(() => Navigation.navigate(ROUTES.TEACHERS_UNITE)), }, - ...(!props.isLoading && !Policy.hasActiveFreePolicy(props.allPolicies) + ...(!isLoading && !Policy.hasActiveFreePolicy(allPolicies) ? [ { displayInDefaultIconColor: true, - contentFit: 'contain', + contentFit: 'contain' as const, icon: Expensicons.NewWorkspace, iconWidth: 46, iconHeight: 40, @@ -219,31 +197,22 @@ function FloatingActionButtonAndPopover(props) { ); } -FloatingActionButtonAndPopover.propTypes = propTypes; -FloatingActionButtonAndPopover.defaultProps = defaultProps; FloatingActionButtonAndPopover.displayName = 'FloatingActionButtonAndPopover'; -const FloatingActionButtonAndPopoverWithRef = forwardRef((props, ref) => ( - -)); - -FloatingActionButtonAndPopoverWithRef.displayName = 'FloatingActionButtonAndPopoverWithRef'; - -export default compose( - withNavigation, - withNavigationFocus, - withWindowDimensions, - withOnyx({ - allPolicies: { - key: ONYXKEYS.COLLECTION.POLICY, - selector: policySelector, - }, - isLoading: { - key: ONYXKEYS.IS_LOADING_APP, - }, - }), -)(FloatingActionButtonAndPopoverWithRef); +const policySelector = (policy: OnyxEntry) => + policy && { + type: policy.type, + role: policy.role, + isPolicyExpenseChatEnabled: policy.isPolicyExpenseChatEnabled, + pendingAction: policy.pendingAction, + }; + +export default withOnyx({ + allPolicies: { + key: ONYXKEYS.COLLECTION.POLICY, + selector: policySelector, + }, + isLoading: { + key: ONYXKEYS.IS_LOADING_APP, + }, +})(forwardRef(FloatingActionButtonAndPopover)); From c9158cbd92eaed9d32315fb38fda611e4c182079 Mon Sep 17 00:00:00 2001 From: Jakub Butkiewicz Date: Tue, 20 Feb 2024 14:48:56 +0100 Subject: [PATCH 010/103] fix: wip --- src/libs/actions/Policy.ts | 2 +- .../sidebar/BottomTabBarFloatingActionButton/index.tsx | 1 - .../home/sidebar/SidebarScreen/BaseSidebarScreen.tsx | 2 -- .../SidebarScreen/FloatingActionButtonAndPopover.tsx | 9 ++++----- .../home/sidebar/SidebarScreen/{index.js => index.tsx} | 8 +++++--- src/pages/home/sidebar/SidebarScreen/sidebarPropTypes.js | 7 ------- 6 files changed, 10 insertions(+), 19 deletions(-) rename src/pages/home/sidebar/SidebarScreen/{index.js => index.tsx} (75%) delete mode 100644 src/pages/home/sidebar/SidebarScreen/sidebarPropTypes.js diff --git a/src/libs/actions/Policy.ts b/src/libs/actions/Policy.ts index e780001a7ecc..261e15af8d48 100644 --- a/src/libs/actions/Policy.ts +++ b/src/libs/actions/Policy.ts @@ -81,7 +81,7 @@ type OptimisticCustomUnits = { outputCurrency: string; }; -type PoliciesRecord = Record; +type PoliciesRecord = Record; type NewCustomUnit = { customUnitID: string; diff --git a/src/pages/home/sidebar/BottomTabBarFloatingActionButton/index.tsx b/src/pages/home/sidebar/BottomTabBarFloatingActionButton/index.tsx index 788dd4ae5bc8..33b89be8fd17 100644 --- a/src/pages/home/sidebar/BottomTabBarFloatingActionButton/index.tsx +++ b/src/pages/home/sidebar/BottomTabBarFloatingActionButton/index.tsx @@ -32,7 +32,6 @@ function BottomTabBarFloatingActionButton() { return ( diff --git a/src/pages/home/sidebar/SidebarScreen/BaseSidebarScreen.tsx b/src/pages/home/sidebar/SidebarScreen/BaseSidebarScreen.tsx index 314b3921cc0b..b3901e1ae06f 100644 --- a/src/pages/home/sidebar/SidebarScreen/BaseSidebarScreen.tsx +++ b/src/pages/home/sidebar/SidebarScreen/BaseSidebarScreen.tsx @@ -7,7 +7,6 @@ import Performance from '@libs/Performance'; import SidebarLinksData from '@pages/home/sidebar/SidebarLinksData'; import Timing from '@userActions/Timing'; import CONST from '@src/CONST'; -import sidebarPropTypes from './sidebarPropTypes'; /** * Function called when a pinned chat is selected. @@ -44,7 +43,6 @@ function BaseSidebarScreen() { ); } -BaseSidebarScreen.propTypes = sidebarPropTypes; BaseSidebarScreen.displayName = 'BaseSidebarScreen'; export default BaseSidebarScreen; diff --git a/src/pages/home/sidebar/SidebarScreen/FloatingActionButtonAndPopover.tsx b/src/pages/home/sidebar/SidebarScreen/FloatingActionButtonAndPopover.tsx index 83cc719f2e90..01d5ddaff475 100644 --- a/src/pages/home/sidebar/SidebarScreen/FloatingActionButtonAndPopover.tsx +++ b/src/pages/home/sidebar/SidebarScreen/FloatingActionButtonAndPopover.tsx @@ -1,4 +1,4 @@ -import {useIsFocused, useNavigation} from '@react-navigation/native'; +import {useIsFocused} from '@react-navigation/native'; import type {ForwardedRef} from 'react'; import React, {forwardRef, useCallback, useEffect, useImperativeHandle, useRef, useState} from 'react'; import {View} from 'react-native'; @@ -25,13 +25,13 @@ import type * as OnyxTypes from '@src/types/onyx'; type FloatingActionButtonAndPopoverOnyxProps = { /** The list of policies the user has access to. */ - allPolicies: OnyxEntry>; + allPolicies: OnyxEntry>>; isLoading: OnyxEntry; }; type FloatingActionButtonAndPopoverProps = FloatingActionButtonAndPopoverOnyxProps & { /* Callback function when the menu is shown */ - onShowCreateMenu: () => void; + onShowCreateMenu?: () => void; /* Callback function before the menu is hidden */ onHideCreateMenu: () => void; @@ -49,7 +49,6 @@ function FloatingActionButtonAndPopover( {onHideCreateMenu = () => {}, onShowCreateMenu = () => {}, isLoading, allPolicies}: FloatingActionButtonAndPopoverProps, ref: ForwardedRef, ) { - console.log('allPolices', allPolicies); const styles = useThemeStyles(); const {translate} = useLocalize(); const [isCreateMenuActive, setIsCreateMenuActive] = useState(false); @@ -200,7 +199,7 @@ function FloatingActionButtonAndPopover( FloatingActionButtonAndPopover.displayName = 'FloatingActionButtonAndPopover'; const policySelector = (policy: OnyxEntry) => - policy && { + !!policy && { type: policy.type, role: policy.role, isPolicyExpenseChatEnabled: policy.isPolicyExpenseChatEnabled, diff --git a/src/pages/home/sidebar/SidebarScreen/index.js b/src/pages/home/sidebar/SidebarScreen/index.tsx similarity index 75% rename from src/pages/home/sidebar/SidebarScreen/index.js rename to src/pages/home/sidebar/SidebarScreen/index.tsx index 7086e8a8561a..f017750c912c 100755 --- a/src/pages/home/sidebar/SidebarScreen/index.js +++ b/src/pages/home/sidebar/SidebarScreen/index.tsx @@ -1,10 +1,13 @@ import React from 'react'; +import type {LayoutChangeEvent} from 'react-native'; import useWindowDimensions from '@hooks/useWindowDimensions'; import FreezeWrapper from '@libs/Navigation/FreezeWrapper'; import BaseSidebarScreen from './BaseSidebarScreen'; -import sidebarPropTypes from './sidebarPropTypes'; -function SidebarScreen(props) { +type SidebarScreenProps = { + onLayout: (event: LayoutChangeEvent) => void; +}; +function SidebarScreen(props: SidebarScreenProps) { const {isSmallScreenWidth} = useWindowDimensions(); return ( @@ -17,7 +20,6 @@ function SidebarScreen(props) { ); } -SidebarScreen.propTypes = sidebarPropTypes; SidebarScreen.displayName = 'SidebarScreen'; export default SidebarScreen; diff --git a/src/pages/home/sidebar/SidebarScreen/sidebarPropTypes.js b/src/pages/home/sidebar/SidebarScreen/sidebarPropTypes.js deleted file mode 100644 index 61a9194bb1e5..000000000000 --- a/src/pages/home/sidebar/SidebarScreen/sidebarPropTypes.js +++ /dev/null @@ -1,7 +0,0 @@ -import PropTypes from 'prop-types'; - -const sidebarPropTypes = { - /** Callback when onLayout of sidebar is called */ - onLayout: PropTypes.func, -}; -export default sidebarPropTypes; From dc58fff4022813cc70e994bc2f1f97f4e427cebf Mon Sep 17 00:00:00 2001 From: Jakub Butkiewicz Date: Tue, 20 Feb 2024 15:42:06 +0100 Subject: [PATCH 011/103] fix: wip --- .../FloatingActionButtonAndPopover.tsx | 16 ++++++++++------ 1 file changed, 10 insertions(+), 6 deletions(-) diff --git a/src/pages/home/sidebar/SidebarScreen/FloatingActionButtonAndPopover.tsx b/src/pages/home/sidebar/SidebarScreen/FloatingActionButtonAndPopover.tsx index 01d5ddaff475..9291401d4569 100644 --- a/src/pages/home/sidebar/SidebarScreen/FloatingActionButtonAndPopover.tsx +++ b/src/pages/home/sidebar/SidebarScreen/FloatingActionButtonAndPopover.tsx @@ -26,6 +26,8 @@ import type * as OnyxTypes from '@src/types/onyx'; type FloatingActionButtonAndPopoverOnyxProps = { /** The list of policies the user has access to. */ allPolicies: OnyxEntry>>; + + /** Wheater app is in loading state */ isLoading: OnyxEntry; }; @@ -199,12 +201,14 @@ function FloatingActionButtonAndPopover( FloatingActionButtonAndPopover.displayName = 'FloatingActionButtonAndPopover'; const policySelector = (policy: OnyxEntry) => - !!policy && { - type: policy.type, - role: policy.role, - isPolicyExpenseChatEnabled: policy.isPolicyExpenseChatEnabled, - pendingAction: policy.pendingAction, - }; + policy + ? { + type: policy.type, + role: policy.role, + isPolicyExpenseChatEnabled: policy.isPolicyExpenseChatEnabled, + pendingAction: policy.pendingAction, + } + : null; export default withOnyx({ allPolicies: { From 4bb8acb56b6f3995a58eebf0f96cb975d6a94baa Mon Sep 17 00:00:00 2001 From: Jakub Butkiewicz Date: Wed, 21 Feb 2024 12:42:45 +0100 Subject: [PATCH 012/103] fix: removed unused props spreading --- src/pages/home/sidebar/SidebarScreen/index.tsx | 11 ++--------- 1 file changed, 2 insertions(+), 9 deletions(-) diff --git a/src/pages/home/sidebar/SidebarScreen/index.tsx b/src/pages/home/sidebar/SidebarScreen/index.tsx index f017750c912c..e448a9cad332 100755 --- a/src/pages/home/sidebar/SidebarScreen/index.tsx +++ b/src/pages/home/sidebar/SidebarScreen/index.tsx @@ -1,21 +1,14 @@ import React from 'react'; -import type {LayoutChangeEvent} from 'react-native'; import useWindowDimensions from '@hooks/useWindowDimensions'; import FreezeWrapper from '@libs/Navigation/FreezeWrapper'; import BaseSidebarScreen from './BaseSidebarScreen'; -type SidebarScreenProps = { - onLayout: (event: LayoutChangeEvent) => void; -}; -function SidebarScreen(props: SidebarScreenProps) { +function SidebarScreen() { const {isSmallScreenWidth} = useWindowDimensions(); return ( - + ); } From 7064cfe6e4487972ff995c5886994cd45f80e5ea Mon Sep 17 00:00:00 2001 From: ruben-rebelo Date: Wed, 21 Feb 2024 15:52:22 +0000 Subject: [PATCH 013/103] [TS migration] Migrate postTestBuildComment to typescript --- ...uildComment.js => postTestBuildComment.ts} | 21 ++++++++++++------- 1 file changed, 14 insertions(+), 7 deletions(-) rename tests/unit/{postTestBuildComment.js => postTestBuildComment.ts} (83%) diff --git a/tests/unit/postTestBuildComment.js b/tests/unit/postTestBuildComment.ts similarity index 83% rename from tests/unit/postTestBuildComment.js rename to tests/unit/postTestBuildComment.ts index ff77ca190c08..df0a623aaced 100644 --- a/tests/unit/postTestBuildComment.js +++ b/tests/unit/postTestBuildComment.ts @@ -1,10 +1,11 @@ import * as core from '@actions/core'; import {when} from 'jest-when'; +import type {Writable} from 'type-fest'; import ghAction from '../../.github/actions/javascript/postTestBuildComment/postTestBuildComment'; import GithubUtils from '../../.github/libs/GithubUtils'; const mockGetInput = jest.fn(); -const mockCreateComment = jest.fn(); +const createCommentMock = jest.spyOn(GithubUtils, 'createComment'); const mockListComments = jest.fn(); const mockGraphql = jest.fn(); jest.spyOn(GithubUtils, 'octokit', 'get').mockReturnValue({ @@ -12,7 +13,11 @@ jest.spyOn(GithubUtils, 'octokit', 'get').mockReturnValue({ listComments: mockListComments, }, }); -jest.spyOn(GithubUtils, 'paginate', 'get').mockReturnValue((endpoint, params) => endpoint(params).then(({data}) => data)); + +jest.spyOn(GithubUtils, 'paginate', 'get').mockReturnValue((endpoint: (params: Record) => Promise<{data: TData}>, params: Record) => + endpoint(params).then((response) => response.data), +); + jest.spyOn(GithubUtils, 'graphql', 'get').mockReturnValue(mockGraphql); jest.mock('@actions/github', () => ({ @@ -49,11 +54,12 @@ const message = `:test_tube::test_tube: Use the links below to test this adhoc b :eyes: [View the workflow run that generated this build](https://github.com/Expensify/App/actions/runs/1234) :eyes: `; +const asMutable = (value: T): Writable => value as Writable; + describe('Post test build comments action tests', () => { beforeAll(() => { // Mock core module - core.getInput = mockGetInput; - GithubUtils.createComment = mockCreateComment; + asMutable(core).getInput = mockGetInput; }); test('Test GH action', async () => { @@ -66,11 +72,12 @@ describe('Post test build comments action tests', () => { when(core.getInput).calledWith('IOS_LINK').mockReturnValue('https://expensify.app/IOS_LINK'); when(core.getInput).calledWith('WEB_LINK').mockReturnValue('https://expensify.app/WEB_LINK'); when(core.getInput).calledWith('DESKTOP_LINK').mockReturnValue('https://expensify.app/DESKTOP_LINK'); - GithubUtils.createComment.mockResolvedValue(true); + createCommentMock.mockResolvedValue(true); mockListComments.mockResolvedValue({ data: [ { body: ':test_tube::test_tube: Use the links below to test this adhoc build on Android, iOS, Desktop, and Web. Happy testing!', + // eslint-disable-next-line @typescript-eslint/naming-convention node_id: 'IC_abcd', }, ], @@ -86,7 +93,7 @@ describe('Post test build comments action tests', () => { } } `); - expect(GithubUtils.createComment).toBeCalledTimes(1); - expect(GithubUtils.createComment).toBeCalledWith('App', 12, message); + expect(createCommentMock).toBeCalledTimes(1); + expect(createCommentMock).toBeCalledWith('App', 12, message); }); }); From 0ead9f440a8ff6d5bd8167a27dc0bf2e3def6ca8 Mon Sep 17 00:00:00 2001 From: ruben-rebelo Date: Wed, 21 Feb 2024 15:58:26 +0000 Subject: [PATCH 014/103] [TS migration][postTestBuildComment] Added last ts issue fix --- tests/unit/postTestBuildComment.ts | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/tests/unit/postTestBuildComment.ts b/tests/unit/postTestBuildComment.ts index df0a623aaced..808e85d5c8a8 100644 --- a/tests/unit/postTestBuildComment.ts +++ b/tests/unit/postTestBuildComment.ts @@ -63,7 +63,9 @@ describe('Post test build comments action tests', () => { }); test('Test GH action', async () => { - when(core.getInput).calledWith('PR_NUMBER', {required: true}).mockReturnValue(12); + when(core.getInput) + .calledWith('PR_NUMBER', {required: true}) + .mockReturnValue(12 as unknown as string); when(core.getInput).calledWith('ANDROID', {required: true}).mockReturnValue('success'); when(core.getInput).calledWith('IOS', {required: true}).mockReturnValue('success'); when(core.getInput).calledWith('WEB', {required: true}).mockReturnValue('success'); From 09e448f7ddbe7992a943a63abc17f960e71cd647 Mon Sep 17 00:00:00 2001 From: Jakub Butkiewicz Date: Wed, 21 Feb 2024 17:37:35 +0100 Subject: [PATCH 015/103] fix: added ugly fix for onyx selectors --- src/pages/home/sidebar/SidebarLinksData.tsx | 74 +++++++++---------- .../FloatingActionButtonAndPopover.tsx | 10 ++- 2 files changed, 43 insertions(+), 41 deletions(-) diff --git a/src/pages/home/sidebar/SidebarLinksData.tsx b/src/pages/home/sidebar/SidebarLinksData.tsx index 29f58d679760..182f413272f0 100644 --- a/src/pages/home/sidebar/SidebarLinksData.tsx +++ b/src/pages/home/sidebar/SidebarLinksData.tsx @@ -23,42 +23,42 @@ import type * as OnyxTypes from '@src/types/onyx'; import type {Message} from '@src/types/onyx/ReportAction'; import SidebarLinks from './SidebarLinks'; +type PickedReport = Pick< + OnyxTypes.Report, + | 'reportID' + | 'participantAccountIDs' + | 'hasDraft' + | 'isPinned' + | 'isHidden' + | 'notificationPreference' + | 'errorFields' + | 'lastMessageText' + | 'lastVisibleActionCreated' + | 'iouReportID' + | 'total' + | 'nonReimbursableTotal' + | 'hasOutstandingChildRequest' + | 'isWaitingOnBankAccount' + | 'statusNum' + | 'stateNum' + | 'chatType' + | 'type' + | 'policyID' + | 'visibility' + | 'lastReadTime' + | 'reportName' + | 'policyName' + | 'oldPolicyName' + | 'ownerAccountID' + | 'currency' + | 'managerID' + | 'parentReportActionID' + | 'parentReportID' + | 'isDeletedParentAction' +>; + type SidebarLinksDataOnyxProps = { - chatReports: OnyxCollection< - Pick< - OnyxTypes.Report, - | 'reportID' - | 'participantAccountIDs' - | 'hasDraft' - | 'isPinned' - | 'isHidden' - | 'notificationPreference' - | 'errorFields' - | 'lastMessageText' - | 'lastVisibleActionCreated' - | 'iouReportID' - | 'total' - | 'nonReimbursableTotal' - | 'hasOutstandingChildRequest' - | 'isWaitingOnBankAccount' - | 'statusNum' - | 'stateNum' - | 'chatType' - | 'type' - | 'policyID' - | 'visibility' - | 'lastReadTime' - | 'reportName' - | 'policyName' - | 'oldPolicyName' - | 'ownerAccountID' - | 'currency' - | 'managerID' - | 'parentReportActionID' - | 'parentReportID' - | 'isDeletedParentAction' - > & {isUnreadWithMention: boolean} - >; + chatReports: OnyxCollection; isLoadingApp: OnyxEntry; priorityMode: OnyxEntry>; betas: OnyxEntry; @@ -248,7 +248,7 @@ const policySelector = (policy: OnyxEntry) => export default withOnyx({ chatReports: { key: ONYXKEYS.COLLECTION.REPORT, - selector: chatReportSelector, + selector: chatReportSelector as unknown as (report: OnyxEntry) => OnyxCollection, initialValue: {}, }, isLoadingApp: { @@ -269,7 +269,7 @@ export default withOnyx({ }, policies: { key: ONYXKEYS.COLLECTION.POLICY, - selector: policySelector, + selector: policySelector as unknown as (policy: OnyxEntry) => OnyxCollection>, initialValue: {}, }, policyMembers: { diff --git a/src/pages/home/sidebar/SidebarScreen/FloatingActionButtonAndPopover.tsx b/src/pages/home/sidebar/SidebarScreen/FloatingActionButtonAndPopover.tsx index 2a0682b776cb..208a9c0a9deb 100644 --- a/src/pages/home/sidebar/SidebarScreen/FloatingActionButtonAndPopover.tsx +++ b/src/pages/home/sidebar/SidebarScreen/FloatingActionButtonAndPopover.tsx @@ -1,5 +1,5 @@ import {useIsFocused} from '@react-navigation/native'; -import type {ForwardedRef} from 'react'; +import type {ForwardedRef, RefAttributes} from 'react'; import React, {forwardRef, useCallback, useEffect, useImperativeHandle, useRef, useState} from 'react'; import {View} from 'react-native'; import type {OnyxEntry} from 'react-native-onyx'; @@ -170,7 +170,7 @@ function FloatingActionButtonAndPopover( text: translate('sidebarScreen.saveTheWorld'), onSelected: () => interceptAnonymousUser(() => Navigation.navigate(ROUTES.TEACHERS_UNITE)), }, - ...(!isLoading && !Policy.hasActiveFreePolicy(allPolicies) + ...(!isLoading && !Policy.hasActiveFreePolicy(allPolicies as Record) ? [ { displayInDefaultIconColor: true, @@ -211,10 +211,12 @@ const policySelector = (policy: OnyxEntry) => } : null; -export default withOnyx({ +export default withOnyx, FloatingActionButtonAndPopoverOnyxProps>({ allPolicies: { key: ONYXKEYS.COLLECTION.POLICY, - selector: policySelector, + selector: policySelector as unknown as ( + policy: OnyxEntry, + ) => OnyxEntry>>, }, isLoading: { key: ONYXKEYS.IS_LOADING_APP, From a5ea70b6898e063ef27b2e11884cfaf716d633e7 Mon Sep 17 00:00:00 2001 From: ruben-rebelo Date: Thu, 22 Feb 2024 09:05:21 +0000 Subject: [PATCH 016/103] [TS migration][postTestBuildComment] Updated asMutable to the util --- src/utils/asMutable.ts | 5 +++++ tests/unit/postTestBuildComment.ts | 4 +--- 2 files changed, 6 insertions(+), 3 deletions(-) create mode 100644 src/utils/asMutable.ts diff --git a/src/utils/asMutable.ts b/src/utils/asMutable.ts new file mode 100644 index 000000000000..57c49058cd14 --- /dev/null +++ b/src/utils/asMutable.ts @@ -0,0 +1,5 @@ +import type {Writable} from 'type-fest'; + +const asMutable = (value: T): Writable => value as Writable; + +export default asMutable; diff --git a/tests/unit/postTestBuildComment.ts b/tests/unit/postTestBuildComment.ts index 808e85d5c8a8..1c1e9ad35564 100644 --- a/tests/unit/postTestBuildComment.ts +++ b/tests/unit/postTestBuildComment.ts @@ -1,6 +1,6 @@ import * as core from '@actions/core'; import {when} from 'jest-when'; -import type {Writable} from 'type-fest'; +import asMutable from '@src/utils/asMutable'; import ghAction from '../../.github/actions/javascript/postTestBuildComment/postTestBuildComment'; import GithubUtils from '../../.github/libs/GithubUtils'; @@ -54,8 +54,6 @@ const message = `:test_tube::test_tube: Use the links below to test this adhoc b :eyes: [View the workflow run that generated this build](https://github.com/Expensify/App/actions/runs/1234) :eyes: `; -const asMutable = (value: T): Writable => value as Writable; - describe('Post test build comments action tests', () => { beforeAll(() => { // Mock core module From dfad42627dc4711bd9311b30b397f4468c414ee3 Mon Sep 17 00:00:00 2001 From: ruben-rebelo Date: Thu, 22 Feb 2024 09:17:26 +0000 Subject: [PATCH 017/103] [TS migration][postTestBuildComment] Changed file location and name --- src/{utils/asMutable.ts => types/utils/AsMutable.ts} | 0 tests/unit/postTestBuildComment.ts | 2 +- 2 files changed, 1 insertion(+), 1 deletion(-) rename src/{utils/asMutable.ts => types/utils/AsMutable.ts} (100%) diff --git a/src/utils/asMutable.ts b/src/types/utils/AsMutable.ts similarity index 100% rename from src/utils/asMutable.ts rename to src/types/utils/AsMutable.ts diff --git a/tests/unit/postTestBuildComment.ts b/tests/unit/postTestBuildComment.ts index 1c1e9ad35564..26c0711a34cd 100644 --- a/tests/unit/postTestBuildComment.ts +++ b/tests/unit/postTestBuildComment.ts @@ -1,6 +1,6 @@ import * as core from '@actions/core'; import {when} from 'jest-when'; -import asMutable from '@src/utils/asMutable'; +import asMutable from '@src/types/utils/AsMutable'; import ghAction from '../../.github/actions/javascript/postTestBuildComment/postTestBuildComment'; import GithubUtils from '../../.github/libs/GithubUtils'; From fe351822d7fcf92f73ea1cf27f81ddee245d2391 Mon Sep 17 00:00:00 2001 From: Jakub Butkiewicz Date: Fri, 23 Feb 2024 16:24:33 +0100 Subject: [PATCH 018/103] fix: tests --- src/pages/home/sidebar/SidebarLinksData.tsx | 106 ++++++++++++-------- 1 file changed, 64 insertions(+), 42 deletions(-) diff --git a/src/pages/home/sidebar/SidebarLinksData.tsx b/src/pages/home/sidebar/SidebarLinksData.tsx index 182f413272f0..5a5e7a8e0db2 100644 --- a/src/pages/home/sidebar/SidebarLinksData.tsx +++ b/src/pages/home/sidebar/SidebarLinksData.tsx @@ -6,8 +6,9 @@ import type {OnyxCollection, OnyxEntry} from 'react-native-onyx'; import {withOnyx} from 'react-native-onyx'; import type {EdgeInsets} from 'react-native-safe-area-context'; import type {ValueOf} from 'type-fest'; +import type {CurrentReportIDContextValue} from '@components/withCurrentReportID'; +import withCurrentReportID from '@components/withCurrentReportID'; import useActiveWorkspace from '@hooks/useActiveWorkspace'; -import useCurrentReportID from '@hooks/useCurrentReportID'; import useCurrentUserPersonalDetails from '@hooks/useCurrentUserPersonalDetails'; import useLocalize from '@hooks/useLocalize'; import useNetwork from '@hooks/useNetwork'; @@ -58,21 +59,40 @@ type PickedReport = Pick< >; type SidebarLinksDataOnyxProps = { + /** List of reports */ chatReports: OnyxCollection; + + /** Wheather the reports are loading. When false it means they are ready to be used. */ isLoadingApp: OnyxEntry; + + /** The chat priority mode */ priorityMode: OnyxEntry>; + + /** Beta features list */ betas: OnyxEntry; + + /** All report actions for all reports */ allReportActions: OnyxEntry>>; + + /** The policies which the user has access to */ policies: OnyxCollection>; - policyMembers: OnyxCollection; + + /** All of the transaction violations */ transactionViolations: OnyxCollection; -}; -type SidebarLinksDataProps = SidebarLinksDataOnyxProps & { - onLinkClick: () => void; - insets: EdgeInsets | undefined; + /** All policy members */ + policyMembers: OnyxCollection; }; +type SidebarLinksDataProps = CurrentReportIDContextValue & + SidebarLinksDataOnyxProps & { + /** Toggles the navigation menu open and closed */ + onLinkClick: () => void; + + /** Safe area insets required for mobile devices margins */ + insets: EdgeInsets | undefined; + }; + function SidebarLinksData({ allReportActions, betas, @@ -84,8 +104,8 @@ function SidebarLinksData({ priorityMode = CONST.PRIORITY_MODE.DEFAULT, policyMembers, transactionViolations, + currentReportID, }: SidebarLinksDataProps) { - const {currentReportID} = useCurrentReportID() ?? {}; const {accountID} = useCurrentUserPersonalDetails(); const network = useNetwork(); const isFocused = useIsFocused(); @@ -245,38 +265,40 @@ const policySelector = (policy: OnyxEntry) => avatar: policy.avatar, }; -export default withOnyx({ - chatReports: { - key: ONYXKEYS.COLLECTION.REPORT, - selector: chatReportSelector as unknown as (report: OnyxEntry) => OnyxCollection, - initialValue: {}, - }, - isLoadingApp: { - key: ONYXKEYS.IS_LOADING_APP, - }, - priorityMode: { - key: ONYXKEYS.NVP_PRIORITY_MODE, - initialValue: CONST.PRIORITY_MODE.DEFAULT, - }, - betas: { - key: ONYXKEYS.BETAS, - initialValue: [], - }, - allReportActions: { - key: ONYXKEYS.COLLECTION.REPORT_ACTIONS, - selector: reportActionsSelector, - initialValue: {}, - }, - policies: { - key: ONYXKEYS.COLLECTION.POLICY, - selector: policySelector as unknown as (policy: OnyxEntry) => OnyxCollection>, - initialValue: {}, - }, - policyMembers: { - key: ONYXKEYS.COLLECTION.POLICY_MEMBERS, - }, - transactionViolations: { - key: ONYXKEYS.COLLECTION.TRANSACTION_VIOLATIONS, - initialValue: {}, - }, -})(SidebarLinksData); +export default withCurrentReportID( + withOnyx({ + chatReports: { + key: ONYXKEYS.COLLECTION.REPORT, + selector: chatReportSelector as unknown as (report: OnyxEntry) => OnyxCollection, + initialValue: {}, + }, + isLoadingApp: { + key: ONYXKEYS.IS_LOADING_APP, + }, + priorityMode: { + key: ONYXKEYS.NVP_PRIORITY_MODE, + initialValue: CONST.PRIORITY_MODE.DEFAULT, + }, + betas: { + key: ONYXKEYS.BETAS, + initialValue: [], + }, + allReportActions: { + key: ONYXKEYS.COLLECTION.REPORT_ACTIONS, + selector: reportActionsSelector, + initialValue: {}, + }, + policies: { + key: ONYXKEYS.COLLECTION.POLICY, + selector: policySelector as unknown as (policy: OnyxEntry) => OnyxCollection>, + initialValue: {}, + }, + policyMembers: { + key: ONYXKEYS.COLLECTION.POLICY_MEMBERS, + }, + transactionViolations: { + key: ONYXKEYS.COLLECTION.TRANSACTION_VIOLATIONS, + initialValue: {}, + }, + })(SidebarLinksData), +); From 57370376e524abafc3d6e9e0c97bb649df40ffdf Mon Sep 17 00:00:00 2001 From: Jakub Butkiewicz Date: Tue, 27 Feb 2024 14:01:08 +0100 Subject: [PATCH 019/103] fix: resolve comments --- src/components/LHNOptionsList/types.ts | 2 +- src/libs/ReportUtils.ts | 9 +-- src/libs/SidebarUtils.ts | 28 +++++---- src/libs/actions/Policy.ts | 2 +- .../home/sidebar/AvatarWithOptionalStatus.tsx | 4 +- src/pages/home/sidebar/SidebarLinks.tsx | 27 +++++---- src/pages/home/sidebar/SidebarLinksData.tsx | 59 +++++-------------- .../FloatingActionButtonAndPopover.tsx | 19 +++--- .../SignInOrAvatarWithOptionalStatus.tsx | 1 + 9 files changed, 67 insertions(+), 84 deletions(-) diff --git a/src/components/LHNOptionsList/types.ts b/src/components/LHNOptionsList/types.ts index f3d6bde9d41c..58bea97f04c9 100644 --- a/src/components/LHNOptionsList/types.ts +++ b/src/components/LHNOptionsList/types.ts @@ -45,7 +45,7 @@ type CustomLHNOptionsListProps = { contentContainerStyles?: StyleProp; /** Sections for the section list */ - data: string[] | null; + data: string[]; /** Callback to fire when a row is selected */ onSelectRow?: (optionItem: OptionData, popoverAnchor: RefObject) => void; diff --git a/src/libs/ReportUtils.ts b/src/libs/ReportUtils.ts index b19cd837a033..b23da56bcd8f 100644 --- a/src/libs/ReportUtils.ts +++ b/src/libs/ReportUtils.ts @@ -704,8 +704,8 @@ function isDraftExpenseReport(report: OnyxEntry | EmptyObject): boolean /** * Checks if the supplied report has a common policy member with the array passed in params. */ -function hasParticipantInArray(report: Report, policyMemberAccountIDs: number[]) { - if (!report.participantAccountIDs) { +function hasParticipantInArray(report: OnyxEntry, policyMemberAccountIDs: number[]) { + if (!report?.participantAccountIDs) { return false; } @@ -921,9 +921,10 @@ function isConciergeChatReport(report: OnyxEntry): boolean { * Checks if the supplied report belongs to workspace based on the provided params. If the report's policyID is _FAKE_ or has no value, it means this report is a DM. * In this case report and workspace members must be compared to determine whether the report belongs to the workspace. */ -function doesReportBelongToWorkspace(report: Report, policyMemberAccountIDs: number[], policyID?: string) { +function doesReportBelongToWorkspace(report: OnyxEntry, policyMemberAccountIDs: number[], policyID?: string) { return ( - isConciergeChatReport(report) || (report.policyID === CONST.POLICY.ID_FAKE || !report.policyID ? hasParticipantInArray(report, policyMemberAccountIDs) : report.policyID === policyID) + isConciergeChatReport(report) || + (report?.policyID === CONST.POLICY.ID_FAKE || !report?.policyID ? hasParticipantInArray(report, policyMemberAccountIDs) : report?.policyID === policyID) ); } diff --git a/src/libs/SidebarUtils.ts b/src/libs/SidebarUtils.ts index 44a17a6d50a0..463c796fb5ce 100644 --- a/src/libs/SidebarUtils.ts +++ b/src/libs/SidebarUtils.ts @@ -64,9 +64,9 @@ function compareStringDates(a: string, b: string): 0 | 1 | -1 { */ function getOrderedReportIDs( currentReportId: string | null, - allReports: OnyxEntry>, + allReports: OnyxCollection, betas: OnyxEntry, - policies: OnyxEntry>, + policies: OnyxCollection, priorityMode: OnyxEntry>, allReportActions: OnyxCollection, transactionViolations: OnyxCollection, @@ -83,7 +83,7 @@ function getOrderedReportIDs( const parentReportActions = allReportActions?.[parentReportActionsKey]; const parentReportAction = parentReportActions?.find((action) => action && report && action?.reportActionID === report?.parentReportActionID); const doesReportHaveViolations = - betas?.includes(CONST.BETAS.VIOLATIONS) && !!parentReportAction && ReportUtils.doesTransactionThreadHaveViolations(report, transactionViolations, parentReportAction); + !!betas?.includes(CONST.BETAS.VIOLATIONS) && !!parentReportAction && ReportUtils.doesTransactionThreadHaveViolations(report, transactionViolations, parentReportAction); return ReportUtils.shouldReportBeInOptionList({ report, currentReportId: currentReportId ?? '', @@ -91,7 +91,7 @@ function getOrderedReportIDs( betas, policies, excludeEmptyChats: true, - doesReportHaveViolations: !!doesReportHaveViolations, + doesReportHaveViolations, }); }); @@ -114,7 +114,7 @@ function getOrderedReportIDs( // - Sorted by reportDisplayName in GSD (focus) view mode const pinnedAndGBRReports: Report[] = []; const draftReports: Report[] = []; - const nonArchivedReports: Report[] = []; + const nonArchivedReports: Array> = []; const archivedReports: Report[] = []; if (currentPolicyID || policyMemberAccountIDs.length > 0) { @@ -125,16 +125,18 @@ function getOrderedReportIDs( // Normally, the spread operator would be used here to clone the report and prevent the need to reassign the params. // However, this code needs to be very performant to handle thousands of reports, so in the interest of speed, we're just going to disable this lint rule and add // the reportDisplayName property to the report object directly. - // eslint-disable-next-line no-param-reassign - report.displayName = ReportUtils.getReportName(report); + if (report) { + // eslint-disable-next-line no-param-reassign + report.displayName = ReportUtils.getReportName(report); + } - const isPinned = report.isPinned ?? false; - const reportAction = ReportActionsUtils.getReportAction(report.parentReportID ?? '', report.parentReportActionID ?? ''); - if (isPinned || ReportUtils.requiresAttentionFromCurrentUser(report, reportAction)) { + const isPinned = report?.isPinned ?? false; + const reportAction = ReportActionsUtils.getReportAction(report?.parentReportID ?? '', report?.parentReportActionID ?? ''); + if ((isPinned || ReportUtils.requiresAttentionFromCurrentUser(report, reportAction)) && report) { pinnedAndGBRReports.push(report); - } else if (report.hasDraft) { + } else if (report?.hasDraft) { draftReports.push(report); - } else if (ReportUtils.isArchivedRoom(report)) { + } else if (ReportUtils.isArchivedRoom(report) && report) { archivedReports.push(report); } else { nonArchivedReports.push(report); @@ -160,7 +162,7 @@ function getOrderedReportIDs( // Now that we have all the reports grouped and sorted, they must be flattened into an array and only return the reportID. // The order the arrays are concatenated in matters and will determine the order that the groups are displayed in the sidebar. - const LHNReports = [...pinnedAndGBRReports, ...draftReports, ...nonArchivedReports, ...archivedReports].map((report) => report.reportID); + const LHNReports = [...pinnedAndGBRReports, ...draftReports, ...nonArchivedReports, ...archivedReports].map((report) => report?.reportID ?? ''); return LHNReports; } diff --git a/src/libs/actions/Policy.ts b/src/libs/actions/Policy.ts index 850d21e4858e..0296b1a8ea6f 100644 --- a/src/libs/actions/Policy.ts +++ b/src/libs/actions/Policy.ts @@ -84,7 +84,7 @@ type OptimisticCustomUnits = { outputCurrency: string; }; -type PoliciesRecord = Record; +type PoliciesRecord = Record>; type NewCustomUnit = { customUnitID: string; diff --git a/src/pages/home/sidebar/AvatarWithOptionalStatus.tsx b/src/pages/home/sidebar/AvatarWithOptionalStatus.tsx index 5597d46c29bc..0e1dedaf3651 100644 --- a/src/pages/home/sidebar/AvatarWithOptionalStatus.tsx +++ b/src/pages/home/sidebar/AvatarWithOptionalStatus.tsx @@ -12,10 +12,10 @@ import PressableAvatarWithIndicator from './PressableAvatarWithIndicator'; type AvatarWithOptionalStatusProps = { /** Whether the create menu is open or not */ - isCreateMenuOpen: boolean; + isCreateMenuOpen?: boolean; /** Emoji status */ - emojiStatus: string; + emojiStatus?: string; }; function AvatarWithOptionalStatus({emojiStatus = '', isCreateMenuOpen = false}: AvatarWithOptionalStatusProps) { diff --git a/src/pages/home/sidebar/SidebarLinks.tsx b/src/pages/home/sidebar/SidebarLinks.tsx index b0dd6f5ac067..5e0e37b1a164 100644 --- a/src/pages/home/sidebar/SidebarLinks.tsx +++ b/src/pages/home/sidebar/SidebarLinks.tsx @@ -26,19 +26,30 @@ type SidebarLinksOnyxProps = { }; type SidebarLinksProps = SidebarLinksOnyxProps & { + /** Toggles the navigation menu open and closed */ onLinkClick: () => void; - insets: EdgeInsets | undefined; - optionListItems: string[] | null; + + /** Safe area insets required for mobile devices margins */ + insets: EdgeInsets; + + /** List of options to display */ + optionListItems: string[]; + + /** Wheather the reports are loading. When false it means they are ready to be used. */ isLoading: OnyxEntry; + + /** The chat priority mode */ priorityMode?: OnyxEntry>; + + /** Method to change currently active report */ isActiveReport: (reportID: string) => boolean; - isCreateMenuOpen?: boolean; + /** ID of currently active workspace */ // eslint-disable-next-line react/no-unused-prop-types -- its used in withOnyx activeWorkspaceID: string | undefined; }; -function SidebarLinks({onLinkClick, insets, optionListItems, isLoading, priorityMode = CONST.PRIORITY_MODE.DEFAULT, isActiveReport, isCreateMenuOpen, activePolicy}: SidebarLinksProps) { +function SidebarLinks({onLinkClick, insets, optionListItems, isLoading, priorityMode = CONST.PRIORITY_MODE.DEFAULT, isActiveReport, activePolicy}: SidebarLinksProps) { const styles = useThemeStyles(); const StyleUtils = useStyleUtils(); const modal = useRef({}); @@ -105,17 +116,13 @@ function SidebarLinks({onLinkClick, insets, optionListItems, isLoading, priority // or when continuously clicking different LHNs, only apply to small screen // since getTopmostReportId always returns on other devices const reportActionID = Navigation.getTopmostReportActionId(); - if ( - !!isCreateMenuOpen || - (option.reportID === Navigation.getTopmostReportId() && !reportActionID) || - (isSmallScreenWidth && isActiveReport(option.reportID) && !reportActionID) - ) { + if ((option.reportID === Navigation.getTopmostReportId() && !reportActionID) || (isSmallScreenWidth && isActiveReport(option.reportID) && !reportActionID)) { return; } Navigation.navigate(ROUTES.REPORT_WITH_ID.getRoute(option.reportID)); onLinkClick(); }, - [isCreateMenuOpen, isSmallScreenWidth, isActiveReport, onLinkClick], + [isSmallScreenWidth, isActiveReport, onLinkClick], ); const viewMode = priorityMode === CONST.PRIORITY_MODE.GSD ? CONST.OPTION_MODE.COMPACT : CONST.OPTION_MODE.DEFAULT; diff --git a/src/pages/home/sidebar/SidebarLinksData.tsx b/src/pages/home/sidebar/SidebarLinksData.tsx index ee99b0c9cf59..6e71e30ac1b6 100644 --- a/src/pages/home/sidebar/SidebarLinksData.tsx +++ b/src/pages/home/sidebar/SidebarLinksData.tsx @@ -24,43 +24,13 @@ import type * as OnyxTypes from '@src/types/onyx'; import type {Message} from '@src/types/onyx/ReportAction'; import SidebarLinks from './SidebarLinks'; -type PickedReport = Pick< - OnyxTypes.Report, - | 'reportID' - | 'participantAccountIDs' - | 'hasDraft' - | 'isPinned' - | 'isHidden' - | 'notificationPreference' - | 'errorFields' - | 'lastMessageText' - | 'lastVisibleActionCreated' - | 'iouReportID' - | 'total' - | 'nonReimbursableTotal' - | 'hasOutstandingChildRequest' - | 'isWaitingOnBankAccount' - | 'statusNum' - | 'stateNum' - | 'chatType' - | 'type' - | 'policyID' - | 'visibility' - | 'lastReadTime' - | 'reportName' - | 'policyName' - | 'oldPolicyName' - | 'ownerAccountID' - | 'currency' - | 'managerID' - | 'parentReportActionID' - | 'parentReportID' - | 'isDeletedParentAction' ->; +type ChatReportSelector = ReturnType & {isUnreadWithMention: boolean}; +type PolicySelector = ReturnType; +type ReportActionsSelector = ReturnType; type SidebarLinksDataOnyxProps = { /** List of reports */ - chatReports: OnyxCollection; + chatReports: OnyxCollection; /** Wheather the reports are loading. When false it means they are ready to be used. */ isLoadingApp: OnyxEntry; @@ -72,10 +42,10 @@ type SidebarLinksDataOnyxProps = { betas: OnyxEntry; /** All report actions for all reports */ - allReportActions: OnyxEntry>>; + allReportActions: OnyxEntry; /** The policies which the user has access to */ - policies: OnyxCollection>; + policies: OnyxCollection; /** All of the transaction violations */ transactionViolations: OnyxCollection; @@ -90,7 +60,7 @@ type SidebarLinksDataProps = CurrentReportIDContextValue & onLinkClick: () => void; /** Safe area insets required for mobile devices margins */ - insets: EdgeInsets | undefined; + insets: EdgeInsets; }; function SidebarLinksData({ @@ -120,7 +90,7 @@ function SidebarLinksData({ const reportIDsRef = useRef(null); const isLoading = isLoadingApp; - const optionListItems: string[] | null = useMemo(() => { + const optionListItems: string[] = useMemo(() => { const reportIDs = SidebarUtils.getOrderedReportIDs( null, chatReports as OnyxEntry>, @@ -133,7 +103,7 @@ function SidebarLinksData({ policyMemberAccountIDs, ); - if (deepEqual(reportIDsRef.current, reportIDs)) { + if (reportIDsRef.current && deepEqual(reportIDsRef.current, reportIDs)) { return reportIDsRef.current; } @@ -170,7 +140,7 @@ function SidebarLinksData({ const currentReportIDRef = useRef(currentReportID); currentReportIDRef.current = currentReportID; - const isActiveReport = useCallback((reportID: string) => currentReportIDRef.current === reportID, []); + const isActiveReport = useCallback((reportID: string): boolean => currentReportIDRef.current === reportID, []); return ( ) => report && { @@ -270,7 +239,8 @@ export default withCurrentReportID( withOnyx({ chatReports: { key: ONYXKEYS.COLLECTION.REPORT, - selector: chatReportSelector as unknown as (report: OnyxEntry) => OnyxCollection, + // This assertion is needed because the selector in withOnyx expects that the return type will be the same as type in ONYXKEYS but in this case it's not, this is a bug in withOnyx but it's impossible to fix it, when useOnyx will be introduce it will be fixed. + selector: chatReportSelector as unknown as (report: OnyxEntry) => OnyxCollection, initialValue: {}, }, isLoadingApp: { @@ -291,7 +261,8 @@ export default withCurrentReportID( }, policies: { key: ONYXKEYS.COLLECTION.POLICY, - selector: policySelector as unknown as (policy: OnyxEntry) => OnyxCollection>, + // This assertion is needed because the selector in withOnyx expects that the return type will be the same as type in ONYXKEYS but in this case it's not, this is a bug in withOnyx but it's impossible to fix it, when useOnyx will be introduce it will be fixed. + selector: policySelector as unknown as (policy: OnyxEntry) => OnyxCollection, initialValue: {}, }, policyMembers: { @@ -303,3 +274,5 @@ export default withCurrentReportID( }, })(SidebarLinksData), ); + +export type {PolicySelector}; diff --git a/src/pages/home/sidebar/SidebarScreen/FloatingActionButtonAndPopover.tsx b/src/pages/home/sidebar/SidebarScreen/FloatingActionButtonAndPopover.tsx index 208a9c0a9deb..c080272acbd2 100644 --- a/src/pages/home/sidebar/SidebarScreen/FloatingActionButtonAndPopover.tsx +++ b/src/pages/home/sidebar/SidebarScreen/FloatingActionButtonAndPopover.tsx @@ -2,7 +2,7 @@ import {useIsFocused} from '@react-navigation/native'; import type {ForwardedRef, RefAttributes} from 'react'; import React, {forwardRef, useCallback, useEffect, useImperativeHandle, useRef, useState} from 'react'; import {View} from 'react-native'; -import type {OnyxEntry} from 'react-native-onyx'; +import type {OnyxCollection, OnyxEntry} from 'react-native-onyx'; import {withOnyx} from 'react-native-onyx'; import FloatingActionButton from '@components/FloatingActionButton'; import * as Expensicons from '@components/Icon/Expensicons'; @@ -14,6 +14,7 @@ import useWindowDimensions from '@hooks/useWindowDimensions'; import interceptAnonymousUser from '@libs/interceptAnonymousUser'; import Navigation from '@libs/Navigation/Navigation'; import * as ReportUtils from '@libs/ReportUtils'; +import type {PolicySelector} from '@pages/home/sidebar/SidebarLinksData'; import * as App from '@userActions/App'; import * as IOU from '@userActions/IOU'; import * as Policy from '@userActions/Policy'; @@ -25,7 +26,7 @@ import type * as OnyxTypes from '@src/types/onyx'; type FloatingActionButtonAndPopoverOnyxProps = { /** The list of policies the user has access to. */ - allPolicies: OnyxEntry>>; + allPolicies: OnyxCollection; /** Wheater app is in loading state */ isLoading: OnyxEntry; @@ -36,7 +37,7 @@ type FloatingActionButtonAndPopoverProps = FloatingActionButtonAndPopoverOnyxPro onShowCreateMenu?: () => void; /* Callback function before the menu is hidden */ - onHideCreateMenu: () => void; + onHideCreateMenu?: () => void; }; type FloatingActionButtonAndPopoverRef = { @@ -48,7 +49,7 @@ type FloatingActionButtonAndPopoverRef = { * FAB that can open or close the menu. */ function FloatingActionButtonAndPopover( - {onHideCreateMenu = () => {}, onShowCreateMenu = () => {}, isLoading, allPolicies}: FloatingActionButtonAndPopoverProps, + {onHideCreateMenu, onShowCreateMenu, isLoading, allPolicies}: FloatingActionButtonAndPopoverProps, ref: ForwardedRef, ) { const styles = useThemeStyles(); @@ -80,7 +81,7 @@ function FloatingActionButtonAndPopover( return; } setIsCreateMenuActive(true); - onShowCreateMenu(); + onShowCreateMenu?.(); }, // eslint-disable-next-line react-hooks/exhaustive-deps [isFocused, isSmallScreenWidth], @@ -97,7 +98,7 @@ function FloatingActionButtonAndPopover( return; } setIsCreateMenuActive(false); - onHideCreateMenu(); + onHideCreateMenu?.(); }, // eslint-disable-next-line react-hooks/exhaustive-deps [isCreateMenuActive], @@ -170,7 +171,7 @@ function FloatingActionButtonAndPopover( text: translate('sidebarScreen.saveTheWorld'), onSelected: () => interceptAnonymousUser(() => Navigation.navigate(ROUTES.TEACHERS_UNITE)), }, - ...(!isLoading && !Policy.hasActiveFreePolicy(allPolicies as Record) + ...(!isLoading && !Policy.hasActiveFreePolicy(allPolicies as OnyxEntry>) ? [ { displayInDefaultIconColor: true, @@ -214,9 +215,7 @@ const policySelector = (policy: OnyxEntry) => export default withOnyx, FloatingActionButtonAndPopoverOnyxProps>({ allPolicies: { key: ONYXKEYS.COLLECTION.POLICY, - selector: policySelector as unknown as ( - policy: OnyxEntry, - ) => OnyxEntry>>, + selector: policySelector as unknown as (policy: OnyxEntry) => OnyxCollection, }, isLoading: { key: ONYXKEYS.IS_LOADING_APP, diff --git a/src/pages/home/sidebar/SignInOrAvatarWithOptionalStatus.tsx b/src/pages/home/sidebar/SignInOrAvatarWithOptionalStatus.tsx index 2a9356d78232..e8c90bb6eb08 100644 --- a/src/pages/home/sidebar/SignInOrAvatarWithOptionalStatus.tsx +++ b/src/pages/home/sidebar/SignInOrAvatarWithOptionalStatus.tsx @@ -6,6 +6,7 @@ import PressableAvatarWithIndicator from './PressableAvatarWithIndicator'; import SignInButton from './SignInButton'; type SignInOrAvatarWithOptionalStatusProps = { + /** Whether the create menu is open or not */ isCreateMenuOpen?: boolean; }; From 14f652c6b05c7f4e91d618cf1d5005f9585eefc3 Mon Sep 17 00:00:00 2001 From: Ruben Rebelo <39693995+ruben-rebelo@users.noreply.github.com> Date: Wed, 28 Feb 2024 08:33:16 +0000 Subject: [PATCH 020/103] Rename AsMutable.ts to asMutable.ts --- src/types/utils/{AsMutable.ts => asMutable.ts} | 0 1 file changed, 0 insertions(+), 0 deletions(-) rename src/types/utils/{AsMutable.ts => asMutable.ts} (100%) diff --git a/src/types/utils/AsMutable.ts b/src/types/utils/asMutable.ts similarity index 100% rename from src/types/utils/AsMutable.ts rename to src/types/utils/asMutable.ts From 5a33f683158f2282b2134478411c30671d4b8492 Mon Sep 17 00:00:00 2001 From: ruben-rebelo Date: Wed, 28 Feb 2024 08:35:38 +0000 Subject: [PATCH 021/103] [TS migration][postTestBuildComments] Feedback --- tests/unit/postTestBuildComment.ts | 8 +++----- 1 file changed, 3 insertions(+), 5 deletions(-) diff --git a/tests/unit/postTestBuildComment.ts b/tests/unit/postTestBuildComment.ts index 26c0711a34cd..24028ee4f1bd 100644 --- a/tests/unit/postTestBuildComment.ts +++ b/tests/unit/postTestBuildComment.ts @@ -1,6 +1,6 @@ import * as core from '@actions/core'; import {when} from 'jest-when'; -import asMutable from '@src/types/utils/AsMutable'; +import asMutable from '@src/types/utils/asMutable'; import ghAction from '../../.github/actions/javascript/postTestBuildComment/postTestBuildComment'; import GithubUtils from '../../.github/libs/GithubUtils'; @@ -61,9 +61,7 @@ describe('Post test build comments action tests', () => { }); test('Test GH action', async () => { - when(core.getInput) - .calledWith('PR_NUMBER', {required: true}) - .mockReturnValue(12 as unknown as string); + when(core.getInput).calledWith('PR_NUMBER', {required: true}).mockReturnValue(12); when(core.getInput).calledWith('ANDROID', {required: true}).mockReturnValue('success'); when(core.getInput).calledWith('IOS', {required: true}).mockReturnValue('success'); when(core.getInput).calledWith('WEB', {required: true}).mockReturnValue('success'); @@ -94,6 +92,6 @@ describe('Post test build comments action tests', () => { } `); expect(createCommentMock).toBeCalledTimes(1); - expect(createCommentMock).toBeCalledWith('App', 12, message); + expect(createCommentMock).toBeCalledWith('App', '12', message); }); }); From e01e18bccbd26929b3a49d211a523c4723989d92 Mon Sep 17 00:00:00 2001 From: Jakub Butkiewicz Date: Thu, 29 Feb 2024 09:54:53 +0100 Subject: [PATCH 022/103] fix: resolve comments --- src/libs/SidebarUtils.ts | 2 +- src/pages/home/sidebar/SidebarLinksData.tsx | 4 ++-- .../sidebar/SidebarScreen/FloatingActionButtonAndPopover.tsx | 1 + 3 files changed, 4 insertions(+), 3 deletions(-) diff --git a/src/libs/SidebarUtils.ts b/src/libs/SidebarUtils.ts index 463c796fb5ce..41a0f4b87a6c 100644 --- a/src/libs/SidebarUtils.ts +++ b/src/libs/SidebarUtils.ts @@ -136,7 +136,7 @@ function getOrderedReportIDs( pinnedAndGBRReports.push(report); } else if (report?.hasDraft) { draftReports.push(report); - } else if (ReportUtils.isArchivedRoom(report) && report) { + } else if (report && ReportUtils.isArchivedRoom(report)) { archivedReports.push(report); } else { nonArchivedReports.push(report); diff --git a/src/pages/home/sidebar/SidebarLinksData.tsx b/src/pages/home/sidebar/SidebarLinksData.tsx index 6e71e30ac1b6..b4d118c5278b 100644 --- a/src/pages/home/sidebar/SidebarLinksData.tsx +++ b/src/pages/home/sidebar/SidebarLinksData.tsx @@ -239,7 +239,7 @@ export default withCurrentReportID( withOnyx({ chatReports: { key: ONYXKEYS.COLLECTION.REPORT, - // This assertion is needed because the selector in withOnyx expects that the return type will be the same as type in ONYXKEYS but in this case it's not, this is a bug in withOnyx but it's impossible to fix it, when useOnyx will be introduce it will be fixed. + // This assertion is needed because the selector in withOnyx expects that the return type will be the same as type in ONYXKEYS but for collection keys the selector is executed for each collection item. This is a bug in withOnyx typings that we don't have a solution yet, when useOnyx hook is introduced it will be fixed. selector: chatReportSelector as unknown as (report: OnyxEntry) => OnyxCollection, initialValue: {}, }, @@ -261,7 +261,7 @@ export default withCurrentReportID( }, policies: { key: ONYXKEYS.COLLECTION.POLICY, - // This assertion is needed because the selector in withOnyx expects that the return type will be the same as type in ONYXKEYS but in this case it's not, this is a bug in withOnyx but it's impossible to fix it, when useOnyx will be introduce it will be fixed. + // This assertion is needed because the selector in withOnyx expects that the return type will be the same as type in ONYXKEYS but for collection keys the selector is executed for each collection item. This is a bug in withOnyx typings that we don't have a solution yet, when useOnyx hook is introduced it will be fixed. selector: policySelector as unknown as (policy: OnyxEntry) => OnyxCollection, initialValue: {}, }, diff --git a/src/pages/home/sidebar/SidebarScreen/FloatingActionButtonAndPopover.tsx b/src/pages/home/sidebar/SidebarScreen/FloatingActionButtonAndPopover.tsx index c080272acbd2..a550dbd91657 100644 --- a/src/pages/home/sidebar/SidebarScreen/FloatingActionButtonAndPopover.tsx +++ b/src/pages/home/sidebar/SidebarScreen/FloatingActionButtonAndPopover.tsx @@ -215,6 +215,7 @@ const policySelector = (policy: OnyxEntry) => export default withOnyx, FloatingActionButtonAndPopoverOnyxProps>({ allPolicies: { key: ONYXKEYS.COLLECTION.POLICY, + // This assertion is needed because the selector in withOnyx expects that the return type will be the same as type in ONYXKEYS but for collection keys the selector is executed for each collection item. This is a bug in withOnyx typings that we don't have a solution yet, when useOnyx hook is introduced it will be fixed. selector: policySelector as unknown as (policy: OnyxEntry) => OnyxCollection, }, isLoading: { From 3484b06e87d161c3534329418a047a29cd28db82 Mon Sep 17 00:00:00 2001 From: Jakub Butkiewicz Date: Thu, 29 Feb 2024 10:28:10 +0100 Subject: [PATCH 023/103] fix: typecheck --- tests/utils/LHNTestUtils.tsx | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/tests/utils/LHNTestUtils.tsx b/tests/utils/LHNTestUtils.tsx index 80f28002f975..f6bd01bbee74 100644 --- a/tests/utils/LHNTestUtils.tsx +++ b/tests/utils/LHNTestUtils.tsx @@ -282,7 +282,6 @@ function MockedSidebarLinks({currentReportID = ''}: MockedSidebarLinksProps) { return ( {}} insets={{ top: 0, @@ -290,7 +289,7 @@ function MockedSidebarLinks({currentReportID = ''}: MockedSidebarLinksProps) { right: 0, bottom: 0, }} - isSmallScreenWidth={false} + // @ts-expect-error - normally this comes from withCurrentReportID hoc , but here we are just mocking this currentReportID={currentReportID} /> From 5e5289ba2f1724241b0f97ba0f669a1d27492392 Mon Sep 17 00:00:00 2001 From: Jakub Butkiewicz Date: Tue, 5 Mar 2024 10:40:52 +0100 Subject: [PATCH 024/103] fix: typecheck --- src/libs/SidebarUtils.ts | 2 +- src/pages/home/sidebar/SidebarLinksData.tsx | 47 ++++++++++----------- 2 files changed, 24 insertions(+), 25 deletions(-) diff --git a/src/libs/SidebarUtils.ts b/src/libs/SidebarUtils.ts index 6be95b393472..81d8f5a46d53 100644 --- a/src/libs/SidebarUtils.ts +++ b/src/libs/SidebarUtils.ts @@ -74,7 +74,7 @@ function getOrderedReportIDs( betas: OnyxEntry, policies: OnyxCollection, priorityMode: OnyxEntry>, - allReportActions: OnyxCollection, + allReportActions: OnyxCollection, transactionViolations: OnyxCollection, currentPolicyID = '', policyMemberAccountIDs: number[] = [], diff --git a/src/pages/home/sidebar/SidebarLinksData.tsx b/src/pages/home/sidebar/SidebarLinksData.tsx index 042f3b392d8a..1d0aa9e996ac 100644 --- a/src/pages/home/sidebar/SidebarLinksData.tsx +++ b/src/pages/home/sidebar/SidebarLinksData.tsx @@ -26,9 +26,13 @@ import type * as OnyxTypes from '@src/types/onyx'; import type {Message} from '@src/types/onyx/ReportAction'; import SidebarLinks from './SidebarLinks'; -type ChatReportSelector = ReturnType & {isUnreadWithMention: boolean}; -type PolicySelector = ReturnType; -type ReportActionsSelector = ReturnType; +type ChatReportSelector = OnyxTypes.Report & {isUnreadWithMention: boolean}; +type PolicySelector = Pick; +type TransactionSelector = Pick< + OnyxTypes.Transaction, + 'reportID' | 'iouRequestType' | 'comment' | 'receipt' | 'merchant' | 'modifiedMerchant' | 'created' | 'modifiedCreated' | 'amount' | 'modifiedAmount' +>; +type ReportActionsSelector = Array>; type SidebarLinksDataOnyxProps = { /** List of reports */ @@ -44,7 +48,7 @@ type SidebarLinksDataOnyxProps = { betas: OnyxEntry; /** All transactions f */ - allTransactions: OnyxEntry; + allTransactions: OnyxCollection; /** All report actions for all reports */ allReportActions: OnyxEntry; @@ -82,6 +86,7 @@ function SidebarLinksData({ transactionViolations, currentReportID, }: SidebarLinksDataProps) { + console.log(allReportActions); const {accountID} = useCurrentUserPersonalDetails(); const network = useNetwork(); const isFocused = useIsFocused(); @@ -101,7 +106,7 @@ function SidebarLinksData({ return reportKeys.reduce((errorsMap, reportKey) => { const report = chatReports?.[reportKey] ?? null; const allReportsActions = allReportActions?.[reportKey.replace(ONYXKEYS.COLLECTION.REPORT, ONYXKEYS.COLLECTION.REPORT_ACTIONS)]; - const errors = OptionsListUtils.getAllReportErrors(report, allReportsActions, allTransactions) || {}; + const errors = OptionsListUtils.getAllReportErrors(report, allReportsActions, allTransactions as OnyxCollection) || {}; if (Object.keys(errors).length === 0) { return errorsMap; } @@ -118,7 +123,7 @@ function SidebarLinksData({ betas, policies as OnyxEntry>, priorityMode, - allReportActions, + allReportActions as OnyxCollection, transactionViolations, activeWorkspaceID, policyMemberAccountIDs, @@ -166,7 +171,7 @@ function SidebarLinksData({ betas, policies as OnyxEntry>, priorityMode, - allReportActions, + allReportActions as OnyxCollection, transactionViolations, activeWorkspaceID, policyMemberAccountIDs, @@ -222,15 +227,14 @@ SidebarLinksData.displayName = 'SidebarLinksData'; * This function (and the few below it), narrow down the data from Onyx to just the properties that we want to trigger a re-render of the component. This helps minimize re-rendering * and makes the entire component more performant because it's not re-rendering when a bunch of properties change which aren't ever used in the UI. */ -const chatReportSelector = (report: OnyxEntry) => - report && { +const chatReportSelector = (report: OnyxEntry): ChatReportSelector => + (report && { reportID: report.reportID, participantAccountIDs: report.participantAccountIDs, hasDraft: report.hasDraft, isPinned: report.isPinned, isHidden: report.isHidden, notificationPreference: report.notificationPreference, - errors: report.errors, errorFields: { addWorkspaceRoom: report.errorFields?.addWorkspaceRoom, }, @@ -252,9 +256,6 @@ const chatReportSelector = (report: OnyxEntry) => reportName: report.reportName, policyName: report.policyName, oldPolicyName: report.oldPolicyName, - isPolicyExpenseChat: report.isPolicyExpenseChat, - isOwnPolicyExpenseChat: report.isOwnPolicyExpenseChat, - isCancelledIOU: report.isCancelledIOU, // Other less obvious properites considered for sorting: ownerAccountID: report.ownerAccountID, currency: report.currency, @@ -264,7 +265,7 @@ const chatReportSelector = (report: OnyxEntry) => parentReportID: report.parentReportID, isDeletedParentAction: report.isDeletedParentAction, isUnreadWithMention: ReportUtils.isUnreadWithMention(report), - }; + }) as ChatReportSelector; const reportActionsSelector = (reportActions: OnyxEntry) => reportActions && @@ -285,15 +286,15 @@ const reportActionsSelector = (reportActions: OnyxEntry }; }); -const policySelector = (policy: OnyxEntry) => - policy && { +const policySelector = (policy: OnyxEntry): PolicySelector => + (policy && { type: policy.type, name: policy.name, avatar: policy.avatar, - }; + }) as PolicySelector; -const transactionSelector = (transaction) => - transaction && { +const transactionSelector = (transaction: OnyxEntry): TransactionSelector => + (transaction && { reportID: transaction.reportID, iouRequestType: transaction.iouRequestType, comment: transaction.comment, @@ -304,14 +305,13 @@ const transactionSelector = (transaction) => modifiedAmount: transaction.modifiedAmount, created: transaction.created, modifiedCreated: transaction.modifiedCreated, - }; + }) as TransactionSelector; export default withCurrentReportID( withOnyx({ chatReports: { key: ONYXKEYS.COLLECTION.REPORT, - // This assertion is needed because the selector in withOnyx expects that the return type will be the same as type in ONYXKEYS but for collection keys the selector is executed for each collection item. This is a bug in withOnyx typings that we don't have a solution yet, when useOnyx hook is introduced it will be fixed. - selector: chatReportSelector as unknown as (report: OnyxEntry) => OnyxCollection, + selector: chatReportSelector, initialValue: {}, }, isLoadingApp: { @@ -337,8 +337,7 @@ export default withCurrentReportID( }, policies: { key: ONYXKEYS.COLLECTION.POLICY, - // This assertion is needed because the selector in withOnyx expects that the return type will be the same as type in ONYXKEYS but for collection keys the selector is executed for each collection item. This is a bug in withOnyx typings that we don't have a solution yet, when useOnyx hook is introduced it will be fixed. - selector: policySelector as unknown as (policy: OnyxEntry) => OnyxCollection, + selector: policySelector, initialValue: {}, }, policyMembers: { From ac55d2f98c493345f0064dc3f6014155aef57a37 Mon Sep 17 00:00:00 2001 From: Jakub Butkiewicz Date: Tue, 5 Mar 2024 11:33:16 +0100 Subject: [PATCH 025/103] fix: typecheck --- src/libs/SidebarUtils.ts | 39 ++++++++++++++++++--------------------- 1 file changed, 18 insertions(+), 21 deletions(-) diff --git a/src/libs/SidebarUtils.ts b/src/libs/SidebarUtils.ts index 81d8f5a46d53..8d53e992cb2d 100644 --- a/src/libs/SidebarUtils.ts +++ b/src/libs/SidebarUtils.ts @@ -70,11 +70,11 @@ function filterDisplayName(displayName: string): string { */ function getOrderedReportIDs( currentReportId: string | null, - allReports: OnyxCollection, - betas: OnyxEntry, - policies: OnyxCollection, - priorityMode: OnyxEntry>, - allReportActions: OnyxCollection, + allReports: Record, + betas: Beta[], + policies: Record, + priorityMode: ValueOf, + allReportActions: OnyxCollection, transactionViolations: OnyxCollection, currentPolicyID = '', policyMemberAccountIDs: number[] = [], @@ -83,17 +83,17 @@ function getOrderedReportIDs( ): string[] { const isInGSDMode = priorityMode === CONST.PRIORITY_MODE.GSD; const isInDefaultMode = !isInGSDMode; - const allReportsDictValues = Object.values(allReports ?? {}); + const allReportsDictValues = Object.values(allReports); const reportIDsWithViolations = new Set(); // Filter out all the reports that shouldn't be displayed let reportsToDisplay = allReportsDictValues.filter((report) => { const parentReportActionsKey = `${ONYXKEYS.COLLECTION.REPORT_ACTIONS}${report?.parentReportID}`; - const parentReportAction = allReportActions?.[parentReportActionsKey]?.[report?.parentReportActionID ?? '']; + const parentReportAction = allReportActions?.[parentReportActionsKey]?.[report.parentReportActionID ?? '']; const doesReportHaveViolations = canUseViolations && !!parentReportAction && ReportUtils.doesTransactionThreadHaveViolations(report, transactionViolations, parentReportAction); if (doesReportHaveViolations) { - reportIDsWithViolations.add(report?.reportID ?? ''); + reportIDsWithViolations.add(report.reportID); } return ReportUtils.shouldReportBeInOptionList({ report, @@ -124,14 +124,14 @@ function getOrderedReportIDs( // 4. Archived reports // - Sorted by lastVisibleActionCreated in default (most recent) view mode // - Sorted by reportDisplayName in GSD (focus) view mode - const pinnedAndBrickRoadReports: Array> = []; + const pinnedAndBrickRoadReports: Report[] = []; const draftReports: Report[] = []; - const nonArchivedReports: Array> = []; + const nonArchivedReports: Report[] = []; const archivedReports: Report[] = []; if (currentPolicyID || policyMemberAccountIDs.length > 0) { reportsToDisplay = reportsToDisplay.filter( - (report) => report?.reportID === currentReportId || ReportUtils.doesReportBelongToWorkspace(report, policyMemberAccountIDs, currentPolicyID), + (report) => report.reportID === currentReportId || ReportUtils.doesReportBelongToWorkspace(report, policyMemberAccountIDs, currentPolicyID), ); } // There are a few properties that need to be calculated for the report which are used when sorting reports. @@ -140,20 +140,17 @@ function getOrderedReportIDs( // However, this code needs to be very performant to handle thousands of reports, so in the interest of speed, we're just going to disable this lint rule and add // the reportDisplayName property to the report object directly. // eslint-disable-next-line no-param-reassign - if (report) { - // eslint-disable-next-line no-param-reassign - report.displayName = filterDisplayName(ReportUtils.getReportName(report)); - } + report.displayName = filterDisplayName(ReportUtils.getReportName(report)); - const hasRBR = (!!report && report.reportID in reportIDsWithErrors) || reportIDsWithViolations.has(report?.reportID ?? ''); + const hasRBR = report.reportID in reportIDsWithErrors || reportIDsWithViolations.has(report.reportID); - const isPinned = report?.isPinned ?? false; - const reportAction = ReportActionsUtils.getReportAction(report?.parentReportID ?? '', report?.parentReportActionID ?? ''); + const isPinned = report.isPinned ?? false; + const reportAction = ReportActionsUtils.getReportAction(report.parentReportID ?? '', report.parentReportActionID ?? ''); if (isPinned || hasRBR || ReportUtils.requiresAttentionFromCurrentUser(report, reportAction)) { pinnedAndBrickRoadReports.push(report); - } else if (report?.hasDraft) { + } else if (report.hasDraft) { draftReports.push(report); - } else if (report && ReportUtils.isArchivedRoom(report)) { + } else if (ReportUtils.isArchivedRoom(report)) { archivedReports.push(report); } else { nonArchivedReports.push(report); @@ -179,7 +176,7 @@ function getOrderedReportIDs( // Now that we have all the reports grouped and sorted, they must be flattened into an array and only return the reportID. // The order the arrays are concatenated in matters and will determine the order that the groups are displayed in the sidebar. - const LHNReports = [...pinnedAndBrickRoadReports, ...draftReports, ...nonArchivedReports, ...archivedReports].map((report) => report?.reportID ?? ''); + const LHNReports = [...pinnedAndBrickRoadReports, ...draftReports, ...nonArchivedReports, ...archivedReports].map((report) => report.reportID); return LHNReports; } From 379485eaa374db1612b022d35aab75e67e8f818b Mon Sep 17 00:00:00 2001 From: Jakub Butkiewicz Date: Tue, 5 Mar 2024 15:13:04 +0100 Subject: [PATCH 026/103] fix: typecheck --- src/libs/OptionsListUtils.ts | 6 ++- src/libs/SidebarUtils.ts | 39 ++++++++-------- src/pages/home/sidebar/SidebarLinksData.tsx | 51 ++++++++++----------- 3 files changed, 51 insertions(+), 45 deletions(-) diff --git a/src/libs/OptionsListUtils.ts b/src/libs/OptionsListUtils.ts index 07f0df962455..1a7ea79e5d59 100644 --- a/src/libs/OptionsListUtils.ts +++ b/src/libs/OptionsListUtils.ts @@ -481,7 +481,11 @@ function getSearchText( /** * Get an object of error messages keyed by microtime by combining all error objects related to the report. */ -function getAllReportErrors(report: OnyxEntry, reportActions: OnyxEntry, transactions: OnyxCollection = allTransactions): OnyxCommon.Errors { +function getAllReportErrors( + report: OnyxEntry, + reportActions: OnyxEntry | ReportAction[] | undefined, + transactions: OnyxCollection = allTransactions, +): OnyxCommon.Errors { const reportErrors = report?.errors ?? {}; const reportErrorFields = report?.errorFields ?? {}; const reportActionErrors: OnyxCommon.ErrorFields = Object.values(reportActions ?? {}).reduce( diff --git a/src/libs/SidebarUtils.ts b/src/libs/SidebarUtils.ts index 8d53e992cb2d..c22226553ffc 100644 --- a/src/libs/SidebarUtils.ts +++ b/src/libs/SidebarUtils.ts @@ -70,10 +70,10 @@ function filterDisplayName(displayName: string): string { */ function getOrderedReportIDs( currentReportId: string | null, - allReports: Record, - betas: Beta[], - policies: Record, - priorityMode: ValueOf, + allReports: OnyxCollection, + betas: OnyxEntry, + policies: OnyxCollection, + priorityMode: OnyxEntry>, allReportActions: OnyxCollection, transactionViolations: OnyxCollection, currentPolicyID = '', @@ -83,17 +83,17 @@ function getOrderedReportIDs( ): string[] { const isInGSDMode = priorityMode === CONST.PRIORITY_MODE.GSD; const isInDefaultMode = !isInGSDMode; - const allReportsDictValues = Object.values(allReports); + const allReportsDictValues = Object.values(allReports ?? {}); const reportIDsWithViolations = new Set(); // Filter out all the reports that shouldn't be displayed let reportsToDisplay = allReportsDictValues.filter((report) => { const parentReportActionsKey = `${ONYXKEYS.COLLECTION.REPORT_ACTIONS}${report?.parentReportID}`; - const parentReportAction = allReportActions?.[parentReportActionsKey]?.[report.parentReportActionID ?? '']; + const parentReportAction = allReportActions?.[parentReportActionsKey]?.[report?.parentReportActionID ?? '']; const doesReportHaveViolations = canUseViolations && !!parentReportAction && ReportUtils.doesTransactionThreadHaveViolations(report, transactionViolations, parentReportAction); if (doesReportHaveViolations) { - reportIDsWithViolations.add(report.reportID); + reportIDsWithViolations.add(report?.reportID ?? ''); } return ReportUtils.shouldReportBeInOptionList({ report, @@ -124,14 +124,14 @@ function getOrderedReportIDs( // 4. Archived reports // - Sorted by lastVisibleActionCreated in default (most recent) view mode // - Sorted by reportDisplayName in GSD (focus) view mode - const pinnedAndBrickRoadReports: Report[] = []; - const draftReports: Report[] = []; - const nonArchivedReports: Report[] = []; - const archivedReports: Report[] = []; + const pinnedAndBrickRoadReports: Array> = []; + const draftReports: Array> = []; + const nonArchivedReports: Array> = []; + const archivedReports: Array> = []; if (currentPolicyID || policyMemberAccountIDs.length > 0) { reportsToDisplay = reportsToDisplay.filter( - (report) => report.reportID === currentReportId || ReportUtils.doesReportBelongToWorkspace(report, policyMemberAccountIDs, currentPolicyID), + (report) => report?.reportID === currentReportId || ReportUtils.doesReportBelongToWorkspace(report, policyMemberAccountIDs, currentPolicyID), ); } // There are a few properties that need to be calculated for the report which are used when sorting reports. @@ -140,15 +140,18 @@ function getOrderedReportIDs( // However, this code needs to be very performant to handle thousands of reports, so in the interest of speed, we're just going to disable this lint rule and add // the reportDisplayName property to the report object directly. // eslint-disable-next-line no-param-reassign - report.displayName = filterDisplayName(ReportUtils.getReportName(report)); + if (report) { + // eslint-disable-next-line no-param-reassign + report.displayName = filterDisplayName(ReportUtils.getReportName(report)); + } - const hasRBR = report.reportID in reportIDsWithErrors || reportIDsWithViolations.has(report.reportID); + const hasRBR = (!!report && report?.reportID in reportIDsWithErrors) || reportIDsWithViolations.has(report?.reportID ?? ''); - const isPinned = report.isPinned ?? false; - const reportAction = ReportActionsUtils.getReportAction(report.parentReportID ?? '', report.parentReportActionID ?? ''); + const isPinned = report?.isPinned ?? false; + const reportAction = ReportActionsUtils.getReportAction(report?.parentReportID ?? '', report?.parentReportActionID ?? ''); if (isPinned || hasRBR || ReportUtils.requiresAttentionFromCurrentUser(report, reportAction)) { pinnedAndBrickRoadReports.push(report); - } else if (report.hasDraft) { + } else if (report?.hasDraft) { draftReports.push(report); } else if (ReportUtils.isArchivedRoom(report)) { archivedReports.push(report); @@ -176,7 +179,7 @@ function getOrderedReportIDs( // Now that we have all the reports grouped and sorted, they must be flattened into an array and only return the reportID. // The order the arrays are concatenated in matters and will determine the order that the groups are displayed in the sidebar. - const LHNReports = [...pinnedAndBrickRoadReports, ...draftReports, ...nonArchivedReports, ...archivedReports].map((report) => report.reportID); + const LHNReports = [...pinnedAndBrickRoadReports, ...draftReports, ...nonArchivedReports, ...archivedReports].map((report) => report?.reportID ?? ''); return LHNReports; } diff --git a/src/pages/home/sidebar/SidebarLinksData.tsx b/src/pages/home/sidebar/SidebarLinksData.tsx index 1d0aa9e996ac..f9368bac86d2 100644 --- a/src/pages/home/sidebar/SidebarLinksData.tsx +++ b/src/pages/home/sidebar/SidebarLinksData.tsx @@ -51,7 +51,7 @@ type SidebarLinksDataOnyxProps = { allTransactions: OnyxCollection; /** All report actions for all reports */ - allReportActions: OnyxEntry; + allReportActions: OnyxCollection; /** The policies which the user has access to */ policies: OnyxCollection; @@ -86,7 +86,6 @@ function SidebarLinksData({ transactionViolations, currentReportID, }: SidebarLinksDataProps) { - console.log(allReportActions); const {accountID} = useCurrentUserPersonalDetails(); const network = useNetwork(); const isFocused = useIsFocused(); @@ -105,8 +104,8 @@ function SidebarLinksData({ const reportKeys = Object.keys(chatReports ?? {}); return reportKeys.reduce((errorsMap, reportKey) => { const report = chatReports?.[reportKey] ?? null; - const allReportsActions = allReportActions?.[reportKey.replace(ONYXKEYS.COLLECTION.REPORT, ONYXKEYS.COLLECTION.REPORT_ACTIONS)]; - const errors = OptionsListUtils.getAllReportErrors(report, allReportsActions, allTransactions as OnyxCollection) || {}; + const allReportsActions = allReportActions?.[reportKey.replace(ONYXKEYS.COLLECTION.REPORT, ONYXKEYS.COLLECTION.REPORT_ACTIONS) ?? '']; + const errors = OptionsListUtils.getAllReportErrors(report, allReportsActions as OnyxTypes.ReportAction[], allTransactions as OnyxCollection) || {}; if (Object.keys(errors).length === 0) { return errorsMap; } @@ -119,11 +118,11 @@ function SidebarLinksData({ const optionListItems: string[] = useMemo(() => { const reportIDs = SidebarUtils.getOrderedReportIDs( null, - chatReports as OnyxEntry>, + chatReports, betas, - policies as OnyxEntry>, + policies as OnyxCollection, priorityMode, - allReportActions as OnyxCollection, + allReportActions as OnyxCollection, transactionViolations, activeWorkspaceID, policyMemberAccountIDs, @@ -167,11 +166,11 @@ function SidebarLinksData({ if (currentReportID && !optionListItems?.includes(currentReportID)) { return SidebarUtils.getOrderedReportIDs( currentReportID, - chatReports as OnyxEntry>, + chatReports as OnyxCollection, betas, - policies as OnyxEntry>, + policies as OnyxCollection, priorityMode, - allReportActions as OnyxCollection, + allReportActions as OnyxCollection, transactionViolations, activeWorkspaceID, policyMemberAccountIDs, @@ -268,23 +267,23 @@ const chatReportSelector = (report: OnyxEntry): ChatReportSele }) as ChatReportSelector; const reportActionsSelector = (reportActions: OnyxEntry) => - reportActions && - Object.values(reportActions).map((reportAction) => { - const {reportActionID, actionName, errors, originalMessage} = reportAction; - const decision = reportAction.message?.[0].moderationDecision?.decision; + (reportActions && + Object.values(reportActions).map((reportAction) => { + const {reportActionID, actionName, errors, originalMessage} = reportAction; + const decision = reportAction.message?.[0].moderationDecision?.decision; - return { - reportActionID, - actionName, - errors, - message: [ - { - moderationDecision: {decision}, - } as Message, - ], - originalMessage, - }; - }); + return { + reportActionID, + actionName, + errors, + message: [ + { + moderationDecision: {decision}, + } as Message, + ], + originalMessage, + }; + })) as ReportActionsSelector; const policySelector = (policy: OnyxEntry): PolicySelector => (policy && { From dc64a57d4501a1fc99631e73b226f660875e8f24 Mon Sep 17 00:00:00 2001 From: Jakub Butkiewicz Date: Tue, 5 Mar 2024 16:43:51 +0100 Subject: [PATCH 027/103] fix: types --- src/libs/OptionsListUtils.ts | 6 +-- src/pages/home/sidebar/SidebarLinksData.tsx | 45 ++++++++++++--------- 2 files changed, 28 insertions(+), 23 deletions(-) diff --git a/src/libs/OptionsListUtils.ts b/src/libs/OptionsListUtils.ts index 1a7ea79e5d59..2b3125db47ce 100644 --- a/src/libs/OptionsListUtils.ts +++ b/src/libs/OptionsListUtils.ts @@ -481,11 +481,7 @@ function getSearchText( /** * Get an object of error messages keyed by microtime by combining all error objects related to the report. */ -function getAllReportErrors( - report: OnyxEntry, - reportActions: OnyxEntry | ReportAction[] | undefined, - transactions: OnyxCollection = allTransactions, -): OnyxCommon.Errors { +function getAllReportErrors(report: OnyxEntry, reportActions: OnyxEntry | undefined, transactions: OnyxCollection = allTransactions): OnyxCommon.Errors { const reportErrors = report?.errors ?? {}; const reportErrorFields = report?.errorFields ?? {}; const reportActionErrors: OnyxCommon.ErrorFields = Object.values(reportActions ?? {}).reduce( diff --git a/src/pages/home/sidebar/SidebarLinksData.tsx b/src/pages/home/sidebar/SidebarLinksData.tsx index f9368bac86d2..0f4daa6f5f76 100644 --- a/src/pages/home/sidebar/SidebarLinksData.tsx +++ b/src/pages/home/sidebar/SidebarLinksData.tsx @@ -23,7 +23,6 @@ import * as Policy from '@userActions/Policy'; import CONST from '@src/CONST'; import ONYXKEYS from '@src/ONYXKEYS'; import type * as OnyxTypes from '@src/types/onyx'; -import type {Message} from '@src/types/onyx/ReportAction'; import SidebarLinks from './SidebarLinks'; type ChatReportSelector = OnyxTypes.Report & {isUnreadWithMention: boolean}; @@ -32,7 +31,7 @@ type TransactionSelector = Pick< OnyxTypes.Transaction, 'reportID' | 'iouRequestType' | 'comment' | 'receipt' | 'merchant' | 'modifiedMerchant' | 'created' | 'modifiedCreated' | 'amount' | 'modifiedAmount' >; -type ReportActionsSelector = Array>; +type ReportActionsSelector = Record>; type SidebarLinksDataOnyxProps = { /** List of reports */ @@ -105,7 +104,9 @@ function SidebarLinksData({ return reportKeys.reduce((errorsMap, reportKey) => { const report = chatReports?.[reportKey] ?? null; const allReportsActions = allReportActions?.[reportKey.replace(ONYXKEYS.COLLECTION.REPORT, ONYXKEYS.COLLECTION.REPORT_ACTIONS) ?? '']; - const errors = OptionsListUtils.getAllReportErrors(report, allReportsActions as OnyxTypes.ReportAction[], allTransactions as OnyxCollection) || {}; + + const errors = + OptionsListUtils.getAllReportErrors(report, allReportsActions as OnyxEntry, allTransactions as OnyxCollection) || {}; if (Object.keys(errors).length === 0) { return errorsMap; } @@ -255,6 +256,9 @@ const chatReportSelector = (report: OnyxEntry): ChatReportSele reportName: report.reportName, policyName: report.policyName, oldPolicyName: report.oldPolicyName, + isPolicyExpenseChat: report.isPolicyExpenseChat, + isOwnPolicyExpenseChat: report.isOwnPolicyExpenseChat, + isCancelledIOU: report.isCancelledIOU, // Other less obvious properites considered for sorting: ownerAccountID: report.ownerAccountID, currency: report.currency, @@ -266,24 +270,29 @@ const chatReportSelector = (report: OnyxEntry): ChatReportSele isUnreadWithMention: ReportUtils.isUnreadWithMention(report), }) as ChatReportSelector; -const reportActionsSelector = (reportActions: OnyxEntry) => +const reportActionsSelector = (reportActions: OnyxEntry): ReportActionsSelector => (reportActions && - Object.values(reportActions).map((reportAction) => { - const {reportActionID, actionName, errors, originalMessage} = reportAction; - const decision = reportAction.message?.[0].moderationDecision?.decision; + Object.fromEntries( + Object.entries(reportActions).map(([key, reportAction]) => { + const {reportActionID, actionName, errors, originalMessage} = reportAction; + const decision = reportAction.message?.[0].moderationDecision?.decision; - return { - reportActionID, - actionName, - errors, - message: [ + return [ + key, { - moderationDecision: {decision}, - } as Message, - ], - originalMessage, - }; - })) as ReportActionsSelector; + reportActionID, + actionName, + errors, + message: [ + { + moderationDecision: {decision}, + }, + ], + originalMessage, + }, + ]; + }), + )) as ReportActionsSelector; const policySelector = (policy: OnyxEntry): PolicySelector => (policy && { From 96dc9e0f066cffbffa86fd53c6fc335705923fe5 Mon Sep 17 00:00:00 2001 From: ruben-rebelo Date: Fri, 8 Mar 2024 09:53:21 +0000 Subject: [PATCH 028/103] [TS migration][postTestBuildComment] Issue fixed --- tests/unit/postTestBuildComment.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tests/unit/postTestBuildComment.ts b/tests/unit/postTestBuildComment.ts index 24028ee4f1bd..5d4148134e19 100644 --- a/tests/unit/postTestBuildComment.ts +++ b/tests/unit/postTestBuildComment.ts @@ -61,7 +61,7 @@ describe('Post test build comments action tests', () => { }); test('Test GH action', async () => { - when(core.getInput).calledWith('PR_NUMBER', {required: true}).mockReturnValue(12); + when(core.getInput).calledWith('PR_NUMBER', {required: true}).mockReturnValue('12'); when(core.getInput).calledWith('ANDROID', {required: true}).mockReturnValue('success'); when(core.getInput).calledWith('IOS', {required: true}).mockReturnValue('success'); when(core.getInput).calledWith('WEB', {required: true}).mockReturnValue('success'); From 1e3f18e6304e92479df0c959166bd8a4ccb435d8 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Arkadiusz=20Chrab=C4=85szczewski?= Date: Fri, 8 Mar 2024 14:37:28 +0100 Subject: [PATCH 029/103] init navigation and page --- src/ROUTES.ts | 4 + src/SCREENS.ts | 1 + .../AppNavigator/ModalStackNavigators.tsx | 1 + .../CENTRAL_PANE_TO_RHP_MAPPING.ts | 2 +- src/libs/Navigation/linkingConfig/config.ts | 6 + src/libs/Navigation/types.ts | 4 + .../workspace/categories/EditCategoryPage.tsx | 110 ++++++++++++++++++ 7 files changed, 127 insertions(+), 1 deletion(-) create mode 100644 src/pages/workspace/categories/EditCategoryPage.tsx diff --git a/src/ROUTES.ts b/src/ROUTES.ts index 2ed9fbc3666e..c0996314b064 100644 --- a/src/ROUTES.ts +++ b/src/ROUTES.ts @@ -554,6 +554,10 @@ const ROUTES = { route: 'workspace/:policyID/categories/new', getRoute: (policyID: string) => `workspace/${policyID}/categories/new` as const, }, + WORKSPACE_CATEGORY_EDIT: { + route: 'workspace/:policyID/categories/:categoryName/edit', + getRoute: (policyID: string, categoryName: string) => `workspace/${policyID}/categories/${encodeURI(categoryName)}/edit` as const, + }, WORKSPACE_TAGS: { route: 'workspace/:policyID/tags', getRoute: (policyID: string) => `workspace/${policyID}/tags` as const, diff --git a/src/SCREENS.ts b/src/SCREENS.ts index 6fc61aec61a0..35467864012d 100644 --- a/src/SCREENS.ts +++ b/src/SCREENS.ts @@ -225,6 +225,7 @@ const SCREENS = { SHARE: 'Workspace_Profile_Share', NAME: 'Workspace_Profile_Name', CATEGORY_CREATE: 'Category_Create', + CATEGORY_EDIT: 'Category_Edit', CATEGORY_SETTINGS: 'Category_Settings', CATEGORIES_SETTINGS: 'Categories_Settings', MEMBER_DETAILS: 'Workspace_Member_Details', diff --git a/src/libs/Navigation/AppNavigator/ModalStackNavigators.tsx b/src/libs/Navigation/AppNavigator/ModalStackNavigators.tsx index 978e338796ea..ec867fceae38 100644 --- a/src/libs/Navigation/AppNavigator/ModalStackNavigators.tsx +++ b/src/libs/Navigation/AppNavigator/ModalStackNavigators.tsx @@ -254,6 +254,7 @@ const SettingsModalStackNavigator = createModalStackNavigator require('../../../pages/workspace/members/WorkspaceMemberDetailsPage').default as React.ComponentType, [SCREENS.WORKSPACE.MEMBER_DETAILS_ROLE_SELECTION]: () => require('../../../pages/workspace/members/WorkspaceMemberDetailsRoleSelectionPage').default as React.ComponentType, [SCREENS.WORKSPACE.CATEGORY_CREATE]: () => require('../../../pages/workspace/categories/CreateCategoryPage').default as React.ComponentType, + [SCREENS.WORKSPACE.CATEGORY_EDIT]: () => require('../../../pages/workspace/categories/EditCategoryPage').default as React.ComponentType, [SCREENS.REIMBURSEMENT_ACCOUNT]: () => require('../../../pages/ReimbursementAccount/ReimbursementAccountPage').default as React.ComponentType, [SCREENS.GET_ASSISTANCE]: () => require('../../../pages/GetAssistancePage').default as React.ComponentType, [SCREENS.SETTINGS.TWO_FACTOR_AUTH]: () => require('../../../pages/settings/Security/TwoFactorAuth/TwoFactorAuthPage').default as React.ComponentType, diff --git a/src/libs/Navigation/linkingConfig/CENTRAL_PANE_TO_RHP_MAPPING.ts b/src/libs/Navigation/linkingConfig/CENTRAL_PANE_TO_RHP_MAPPING.ts index 5bc7d52230a8..3a5ed8de34b0 100755 --- a/src/libs/Navigation/linkingConfig/CENTRAL_PANE_TO_RHP_MAPPING.ts +++ b/src/libs/Navigation/linkingConfig/CENTRAL_PANE_TO_RHP_MAPPING.ts @@ -6,7 +6,7 @@ const CENTRAL_PANE_TO_RHP_MAPPING: Partial> = [SCREENS.WORKSPACE.REIMBURSE]: [SCREENS.WORKSPACE.RATE_AND_UNIT, SCREENS.WORKSPACE.RATE_AND_UNIT_RATE, SCREENS.WORKSPACE.RATE_AND_UNIT_UNIT], [SCREENS.WORKSPACE.MEMBERS]: [SCREENS.WORKSPACE.INVITE, SCREENS.WORKSPACE.INVITE_MESSAGE, SCREENS.WORKSPACE.MEMBER_DETAILS, SCREENS.WORKSPACE.MEMBER_DETAILS_ROLE_SELECTION], [SCREENS.WORKSPACE.WORKFLOWS]: [SCREENS.WORKSPACE.WORKFLOWS_APPROVER, SCREENS.WORKSPACE.WORKFLOWS_AUTO_REPORTING_FREQUENCY, SCREENS.WORKSPACE.WORKFLOWS_AUTO_REPORTING_MONTHLY_OFFSET], - [SCREENS.WORKSPACE.CATEGORIES]: [SCREENS.WORKSPACE.CATEGORY_CREATE, SCREENS.WORKSPACE.CATEGORY_SETTINGS, SCREENS.WORKSPACE.CATEGORIES_SETTINGS], + [SCREENS.WORKSPACE.CATEGORIES]: [SCREENS.WORKSPACE.CATEGORY_CREATE, SCREENS.WORKSPACE.CATEGORY_SETTINGS, SCREENS.WORKSPACE.CATEGORIES_SETTINGS, SCREENS.WORKSPACE.CATEGORY_CREATE], }; export default CENTRAL_PANE_TO_RHP_MAPPING; diff --git a/src/libs/Navigation/linkingConfig/config.ts b/src/libs/Navigation/linkingConfig/config.ts index 8a24dc177a80..4e178a9ff278 100644 --- a/src/libs/Navigation/linkingConfig/config.ts +++ b/src/libs/Navigation/linkingConfig/config.ts @@ -290,6 +290,12 @@ const config: LinkingOptions['config'] = { [SCREENS.WORKSPACE.CATEGORY_CREATE]: { path: ROUTES.WORKSPACE_CATEGORY_CREATE.route, }, + [SCREENS.WORKSPACE.CATEGORY_EDIT]: { + path: ROUTES.WORKSPACE_CATEGORY_EDIT.route, + parse: { + categoryName: (categoryName: string) => decodeURI(categoryName), + }, + }, [SCREENS.REIMBURSEMENT_ACCOUNT]: { path: ROUTES.BANK_ACCOUNT_WITH_STEP_TO_OPEN.route, exact: true, diff --git a/src/libs/Navigation/types.ts b/src/libs/Navigation/types.ts index decb905ac52f..6b82f143bb77 100644 --- a/src/libs/Navigation/types.ts +++ b/src/libs/Navigation/types.ts @@ -201,6 +201,10 @@ type SettingsNavigatorParamList = { [SCREENS.WORKSPACE.CATEGORY_CREATE]: { policyID: string; }; + [SCREENS.WORKSPACE.CATEGORY_EDIT]: { + policyID: string; + categoryName: string; + }; [SCREENS.WORKSPACE.CATEGORY_SETTINGS]: { policyID: string; categoryName: string; diff --git a/src/pages/workspace/categories/EditCategoryPage.tsx b/src/pages/workspace/categories/EditCategoryPage.tsx new file mode 100644 index 000000000000..3e4403cef90c --- /dev/null +++ b/src/pages/workspace/categories/EditCategoryPage.tsx @@ -0,0 +1,110 @@ +import type {StackScreenProps} from '@react-navigation/stack'; +import React, {useCallback} from 'react'; +import {Keyboard} from 'react-native'; +import {withOnyx} from 'react-native-onyx'; +import type {OnyxEntry} from 'react-native-onyx'; +import FormProvider from '@components/Form/FormProvider'; +import InputWrapper from '@components/Form/InputWrapper'; +import type {FormInputErrors, FormOnyxValues} from '@components/Form/types'; +import HeaderWithBackButton from '@components/HeaderWithBackButton'; +import ScreenWrapper from '@components/ScreenWrapper'; +import TextInput from '@components/TextInput'; +import useLocalize from '@hooks/useLocalize'; +import useThemeStyles from '@hooks/useThemeStyles'; +import * as ErrorUtils from '@libs/ErrorUtils'; +import Navigation from '@libs/Navigation/Navigation'; +import * as ValidationUtils from '@libs/ValidationUtils'; +import type {SettingsNavigatorParamList} from '@navigation/types'; +import AdminPolicyAccessOrNotFoundWrapper from '@pages/workspace/AdminPolicyAccessOrNotFoundWrapper'; +import PaidPolicyAccessOrNotFoundWrapper from '@pages/workspace/PaidPolicyAccessOrNotFoundWrapper'; +import * as Policy from '@userActions/Policy'; +import CONST from '@src/CONST'; +import ONYXKEYS from '@src/ONYXKEYS'; +import type SCREENS from '@src/SCREENS'; +import INPUT_IDS from '@src/types/form/WorkspaceCategoryCreateForm'; +import type {PolicyCategories} from '@src/types/onyx'; + +type WorkspaceEditCategoryPageOnyxProps = { + /** All policy categories */ + policyCategories: OnyxEntry; +}; + +type EditCategoryPageProps = WorkspaceEditCategoryPageOnyxProps & StackScreenProps; + +function EditCategoryPage({route, policyCategories}: EditCategoryPageProps) { + const styles = useThemeStyles(); + const {translate} = useLocalize(); + + const validate = useCallback( + (values: FormOnyxValues) => { + const errors: FormInputErrors = {}; + const categoryName = values.categoryName.trim(); + + if (!ValidationUtils.isRequiredFulfilled(categoryName)) { + errors.categoryName = 'workspace.categories.categoryRequiredError'; + } else if (policyCategories?.[categoryName]) { + errors.categoryName = 'workspace.categories.existingCategoryError'; + } else if (categoryName === CONST.INVALID_CATEGORY_NAME) { + errors.categoryName = 'workspace.categories.invalidCategoryName'; + } else if ([...categoryName].length > CONST.CATEGORY_NAME_LIMIT) { + // Uses the spread syntax to count the number of Unicode code points instead of the number of UTF-16 code units. + ErrorUtils.addErrorMessage(errors, 'categoryName', ['common.error.characterLimitExceedCounter', {length: [...categoryName].length, limit: CONST.CATEGORY_NAME_LIMIT}]); + } + + return errors; + }, + [policyCategories], + ); + + const createCategory = useCallback( + (values: FormOnyxValues) => { + Policy.createPolicyCategory(route.params.policyID, values.categoryName.trim()); + Keyboard.dismiss(); + Navigation.goBack(); + }, + [route.params.policyID], + ); + + return ( + + + + + + + + + + + ); +} + +EditCategoryPage.displayName = 'EditCategoryPage'; + +export default withOnyx({ + policyCategories: { + key: ({route}) => `${ONYXKEYS.COLLECTION.POLICY_CATEGORIES}${route?.params?.policyID}`, + }, +})(EditCategoryPage); From 618511d58c80d35773660e4a3cb0ab5f840a90bb Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Arkadiusz=20Chrab=C4=85szczewski?= Date: Fri, 8 Mar 2024 14:38:08 +0100 Subject: [PATCH 030/103] init onyx action --- .../RenameWorkspaceCategoriesParams.ts | 10 ++++ src/libs/API/parameters/index.ts | 1 + src/libs/API/types.ts | 2 + src/libs/actions/Policy.ts | 54 +++++++++++++++++++ 4 files changed, 67 insertions(+) create mode 100644 src/libs/API/parameters/RenameWorkspaceCategoriesParams.ts diff --git a/src/libs/API/parameters/RenameWorkspaceCategoriesParams.ts b/src/libs/API/parameters/RenameWorkspaceCategoriesParams.ts new file mode 100644 index 000000000000..4084dbad6394 --- /dev/null +++ b/src/libs/API/parameters/RenameWorkspaceCategoriesParams.ts @@ -0,0 +1,10 @@ +type RenameWorkspaceCategoriesParams = { + policyID: string; + /** + * Stringified JSON object with type of following structure: + * Array<{[oldName: string]: string;}> where value is new category name + */ + categories: string; +}; + +export default RenameWorkspaceCategoriesParams; diff --git a/src/libs/API/parameters/index.ts b/src/libs/API/parameters/index.ts index f529032130bb..8252189d46d9 100644 --- a/src/libs/API/parameters/index.ts +++ b/src/libs/API/parameters/index.ts @@ -150,6 +150,7 @@ export type {default as UpdateWorkspaceDescriptionParams} from './UpdateWorkspac export type {default as UpdateWorkspaceMembersRoleParams} from './UpdateWorkspaceMembersRoleParams'; export type {default as SetWorkspaceCategoriesEnabledParams} from './SetWorkspaceCategoriesEnabledParams'; export type {default as CreateWorkspaceCategoriesParams} from './CreateWorkspaceCategoriesParams'; +export type {default as RenameWorkspaceCategoriesParams} from './RenameWorkspaceCategoriesParams'; export type {default as SetWorkspaceRequiresCategoryParams} from './SetWorkspaceRequiresCategoryParams'; export type {default as SetWorkspaceAutoReportingParams} from './SetWorkspaceAutoReportingParams'; export type {default as SetWorkspaceAutoReportingFrequencyParams} from './SetWorkspaceAutoReportingFrequencyParams'; diff --git a/src/libs/API/types.ts b/src/libs/API/types.ts index 1b41ced4f1d7..9791c5081643 100644 --- a/src/libs/API/types.ts +++ b/src/libs/API/types.ts @@ -116,6 +116,7 @@ const WRITE_COMMANDS = { CREATE_WORKSPACE_FROM_IOU_PAYMENT: 'CreateWorkspaceFromIOUPayment', SET_WORKSPACE_CATEGORIES_ENABLED: 'SetWorkspaceCategoriesEnabled', CREATE_WORKSPACE_CATEGORIES: 'CreateWorkspaceCategories', + RENAME_WORKSPACE_CATEGORY: 'RenameWorkspaceCategory', SET_WORKSPACE_REQUIRES_CATEGORY: 'SetWorkspaceRequiresCategory', CREATE_TASK: 'CreateTask', CANCEL_TASK: 'CancelTask', @@ -269,6 +270,7 @@ type WriteCommandParameters = { [WRITE_COMMANDS.CREATE_WORKSPACE_FROM_IOU_PAYMENT]: Parameters.CreateWorkspaceFromIOUPaymentParams; [WRITE_COMMANDS.SET_WORKSPACE_CATEGORIES_ENABLED]: Parameters.SetWorkspaceCategoriesEnabledParams; [WRITE_COMMANDS.CREATE_WORKSPACE_CATEGORIES]: Parameters.CreateWorkspaceCategoriesParams; + [WRITE_COMMANDS.RENAME_WORKSPACE_CATEGORY]: Parameters.RenameWorkspaceCategoriesParams; [WRITE_COMMANDS.SET_WORKSPACE_REQUIRES_CATEGORY]: Parameters.SetWorkspaceRequiresCategoryParams; [WRITE_COMMANDS.CREATE_TASK]: Parameters.CreateTaskParams; [WRITE_COMMANDS.CANCEL_TASK]: Parameters.CancelTaskParams; diff --git a/src/libs/actions/Policy.ts b/src/libs/actions/Policy.ts index f6a1ec3ec340..34e273d5c5eb 100644 --- a/src/libs/actions/Policy.ts +++ b/src/libs/actions/Policy.ts @@ -2522,6 +2522,59 @@ function createPolicyCategory(policyID: string, categoryName: string) { API.write(WRITE_COMMANDS.CREATE_WORKSPACE_CATEGORIES, parameters, onyxData); } +function renamePolicyCategory(policyID: string, policyCategory: {oldName: string; newName: string}) { + const policyCategoryToUpdate = allPolicyCategories?.[`${ONYXKEYS.COLLECTION.POLICY_CATEGORIES}${policyID}`]?.[policyCategory.oldName] ?? {}; + + const onyxData: OnyxData = { + optimisticData: [ + { + onyxMethod: Onyx.METHOD.MERGE, + key: `${ONYXKEYS.COLLECTION.POLICY_CATEGORIES}${policyID}`, + value: { + [policyCategory.oldName]: { + name: policyCategory.newName, + }, + }, + }, + ], + successData: [ + { + onyxMethod: Onyx.METHOD.MERGE, + key: `${ONYXKEYS.COLLECTION.POLICY_CATEGORIES}${policyID}`, + value: { + errors: { + [policyCategory.oldName]: null, + [policyCategory.newName]: { + ...policyCategoryToUpdate, + name: policyCategory.newName, + }, + }, + }, + }, + ], + failureData: [ + { + onyxMethod: Onyx.METHOD.MERGE, + key: `${ONYXKEYS.COLLECTION.POLICY_CATEGORIES}${policyID}`, + value: { + [policyCategory.oldName]: { + name: policyCategory.oldName, + errors: ErrorUtils.getMicroSecondOnyxError('workspace.categories.genericFailureMessage'), + pendingAction: null, + }, + }, + }, + ], + }; + + const parameters = { + policyID, + categories: JSON.stringify([{[policyCategory.oldName]: policyCategory.newName}]), + }; + + API.write(WRITE_COMMANDS.RENAME_WORKSPACE_CATEGORY, parameters, onyxData); +} + function setWorkspaceRequiresCategory(policyID: string, requiresCategory: boolean) { const onyxData: OnyxData = { optimisticData: [ @@ -2760,5 +2813,6 @@ export { acceptJoinRequest, declineJoinRequest, createPolicyCategory, + renamePolicyCategory, clearCategoryErrors, }; From f06ab7c00d59231101e8803a561e3e8736492034 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Arkadiusz=20Chrab=C4=85szczewski?= Date: Fri, 8 Mar 2024 14:55:42 +0100 Subject: [PATCH 031/103] add navigation to edit page --- src/languages/en.ts | 1 + src/languages/es.ts | 1 + src/pages/workspace/categories/CategorySettingsPage.tsx | 7 +++++++ src/pages/workspace/categories/EditCategoryPage.tsx | 2 +- 4 files changed, 10 insertions(+), 1 deletion(-) diff --git a/src/languages/en.ts b/src/languages/en.ts index 3575854ee7e2..09fd8bc5300c 100755 --- a/src/languages/en.ts +++ b/src/languages/en.ts @@ -1772,6 +1772,7 @@ export default { }, genericFailureMessage: 'An error occurred while updating the category, please try again.', addCategory: 'Add category', + editCategory: 'Edit category', categoryRequiredError: 'Category name is required.', existingCategoryError: 'A category with this name already exists.', invalidCategoryName: 'Invalid category name.', diff --git a/src/languages/es.ts b/src/languages/es.ts index 51a83e55fee2..37641ef49354 100644 --- a/src/languages/es.ts +++ b/src/languages/es.ts @@ -1796,6 +1796,7 @@ export default { }, genericFailureMessage: 'Se ha producido un error al intentar eliminar la categoría. Por favor, inténtalo más tarde.', addCategory: 'Añadir categoría', + editCategory: 'Editar categoría', categoryRequiredError: 'Lo nombre de la categoría es obligatorio.', existingCategoryError: 'Ya existe una categoría con este nombre.', invalidCategoryName: 'Lo nombre de la categoría es invalido.', diff --git a/src/pages/workspace/categories/CategorySettingsPage.tsx b/src/pages/workspace/categories/CategorySettingsPage.tsx index 16f128e5ea1f..8aad48fd94a7 100644 --- a/src/pages/workspace/categories/CategorySettingsPage.tsx +++ b/src/pages/workspace/categories/CategorySettingsPage.tsx @@ -13,12 +13,14 @@ import useLocalize from '@hooks/useLocalize'; import useThemeStyles from '@hooks/useThemeStyles'; import {setWorkspaceCategoryEnabled} from '@libs/actions/Policy'; import * as ErrorUtils from '@libs/ErrorUtils'; +import Navigation from '@libs/Navigation/Navigation'; import type {SettingsNavigatorParamList} from '@navigation/types'; import NotFoundPage from '@pages/ErrorPage/NotFoundPage'; import AdminPolicyAccessOrNotFoundWrapper from '@pages/workspace/AdminPolicyAccessOrNotFoundWrapper'; import PaidPolicyAccessOrNotFoundWrapper from '@pages/workspace/PaidPolicyAccessOrNotFoundWrapper'; import * as Policy from '@userActions/Policy'; import ONYXKEYS from '@src/ONYXKEYS'; +import ROUTES from '@src/ROUTES'; import type SCREENS from '@src/SCREENS'; import type * as OnyxTypes from '@src/types/onyx'; @@ -43,6 +45,10 @@ function CategorySettingsPage({route, policyCategories}: CategorySettingsPagePro setWorkspaceCategoryEnabled(route.params.policyID, {[policyCategory.name]: {name: policyCategory.name, enabled: value}}); }; + const navigateToEditCategory = () => { + Navigation.navigate(ROUTES.WORKSPACE_CATEGORY_EDIT.getRoute(route.params.policyID, policyCategory.name)); + }; + return ( @@ -73,6 +79,7 @@ function CategorySettingsPage({route, policyCategories}: CategorySettingsPagePro diff --git a/src/pages/workspace/categories/EditCategoryPage.tsx b/src/pages/workspace/categories/EditCategoryPage.tsx index 3e4403cef90c..963d2ce70ee0 100644 --- a/src/pages/workspace/categories/EditCategoryPage.tsx +++ b/src/pages/workspace/categories/EditCategoryPage.tsx @@ -74,7 +74,7 @@ function EditCategoryPage({route, policyCategories}: EditCategoryPageProps) { testID={EditCategoryPage.displayName} > Date: Fri, 8 Mar 2024 23:47:46 +0300 Subject: [PATCH 032/103] fix disabled indexes check loop --- src/hooks/useArrowKeyFocusManager.ts | 12 +++++++++--- 1 file changed, 9 insertions(+), 3 deletions(-) diff --git a/src/hooks/useArrowKeyFocusManager.ts b/src/hooks/useArrowKeyFocusManager.ts index 78ffc7f87209..458a39d91ca5 100644 --- a/src/hooks/useArrowKeyFocusManager.ts +++ b/src/hooks/useArrowKeyFocusManager.ts @@ -84,7 +84,10 @@ export default function useArrowKeyFocusManager({ while (disabledIndexes.includes(newFocusedIndex)) { newFocusedIndex -= allowHorizontalArrowKeys ? itemsPerRow : 1; if (newFocusedIndex < 0) { - break; + if (disableCyclicTraversal) { + break; + } + newFocusedIndex = maxIndex; } if (newFocusedIndex === currentFocusedIndex) { // all indexes are disabled @@ -127,8 +130,11 @@ export default function useArrowKeyFocusManager({ newFocusedIndex += allowHorizontalArrowKeys ? itemsPerRow : 1; } - if (newFocusedIndex < 0) { - break; + if (newFocusedIndex > maxIndex) { + if (disableCyclicTraversal) { + break; + } + newFocusedIndex = 0; } if (newFocusedIndex === currentFocusedIndex) { // all indexes are disabled From fb68564bff2541d980ed319597b602da46ebcfc4 Mon Sep 17 00:00:00 2001 From: FitseTLT Date: Sat, 9 Mar 2024 00:09:44 +0300 Subject: [PATCH 033/103] return actualindex --- src/hooks/useArrowKeyFocusManager.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/hooks/useArrowKeyFocusManager.ts b/src/hooks/useArrowKeyFocusManager.ts index 458a39d91ca5..7a674b46c337 100644 --- a/src/hooks/useArrowKeyFocusManager.ts +++ b/src/hooks/useArrowKeyFocusManager.ts @@ -132,7 +132,7 @@ export default function useArrowKeyFocusManager({ if (newFocusedIndex > maxIndex) { if (disableCyclicTraversal) { - break; + return actualIndex; } newFocusedIndex = 0; } From 535cad796c4bd2105c53069d24a92a0e59111892 Mon Sep 17 00:00:00 2001 From: FitseTLT Date: Sat, 9 Mar 2024 00:21:42 +0300 Subject: [PATCH 034/103] use allowNegativeIndexes prop --- src/components/EmojiPicker/EmojiPickerMenu/index.js | 1 + src/hooks/useArrowKeyFocusManager.ts | 7 ++++++- 2 files changed, 7 insertions(+), 1 deletion(-) diff --git a/src/components/EmojiPicker/EmojiPickerMenu/index.js b/src/components/EmojiPicker/EmojiPickerMenu/index.js index 25a40a709658..ecfcdd70336f 100755 --- a/src/components/EmojiPicker/EmojiPickerMenu/index.js +++ b/src/components/EmojiPicker/EmojiPickerMenu/index.js @@ -112,6 +112,7 @@ function EmojiPickerMenu({forwardedRef, onEmojiSelected, activeEmoji}) { disableHorizontalKeys: isFocused, // We pass true without checking visibility of the component because if the popover is not visible this picker won't be mounted isActive: true, + allowNegativeIndexes: true, }); const filterEmojis = _.throttle((searchTerm) => { diff --git a/src/hooks/useArrowKeyFocusManager.ts b/src/hooks/useArrowKeyFocusManager.ts index 7a674b46c337..b11999d61cf3 100644 --- a/src/hooks/useArrowKeyFocusManager.ts +++ b/src/hooks/useArrowKeyFocusManager.ts @@ -12,6 +12,7 @@ type Config = { itemsPerRow?: number; disableCyclicTraversal?: boolean; disableHorizontalKeys?: boolean; + allowNegativeIndexes?: boolean; }; type UseArrowKeyFocusManager = [number, (index: number) => void]; @@ -44,6 +45,7 @@ export default function useArrowKeyFocusManager({ itemsPerRow, disableCyclicTraversal = false, disableHorizontalKeys = false, + allowNegativeIndexes = false, }: Config): UseArrowKeyFocusManager { const allowHorizontalArrowKeys = !!itemsPerRow; const [focusedIndex, setFocusedIndex] = useState(initialFocusedIndex); @@ -85,6 +87,9 @@ export default function useArrowKeyFocusManager({ newFocusedIndex -= allowHorizontalArrowKeys ? itemsPerRow : 1; if (newFocusedIndex < 0) { if (disableCyclicTraversal) { + if (!allowNegativeIndexes) { + return actualIndex; + } break; } newFocusedIndex = maxIndex; @@ -96,7 +101,7 @@ export default function useArrowKeyFocusManager({ } return newFocusedIndex; }); - }, [allowHorizontalArrowKeys, disableCyclicTraversal, disabledIndexes, itemsPerRow, maxIndex]); + }, [allowHorizontalArrowKeys, disableCyclicTraversal, disabledIndexes, itemsPerRow, maxIndex, allowNegativeIndexes]); useKeyboardShortcut(CONST.KEYBOARD_SHORTCUTS.ARROW_UP, arrowUpCallback, arrowConfig); From c79a38421bc47a7ce0b222d80d16787618612c86 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Arkadiusz=20Chrab=C4=85szczewski?= Date: Mon, 11 Mar 2024 09:50:51 +0100 Subject: [PATCH 035/103] fix: use rename action instead create --- src/pages/workspace/categories/EditCategoryPage.tsx | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/src/pages/workspace/categories/EditCategoryPage.tsx b/src/pages/workspace/categories/EditCategoryPage.tsx index 963d2ce70ee0..07ebc274e8a4 100644 --- a/src/pages/workspace/categories/EditCategoryPage.tsx +++ b/src/pages/workspace/categories/EditCategoryPage.tsx @@ -58,11 +58,11 @@ function EditCategoryPage({route, policyCategories}: EditCategoryPageProps) { const createCategory = useCallback( (values: FormOnyxValues) => { - Policy.createPolicyCategory(route.params.policyID, values.categoryName.trim()); + Policy.renamePolicyCategory(route.params.policyID, {oldName: route.params.categoryName, newName: values.categoryName}); Keyboard.dismiss(); Navigation.goBack(); }, - [route.params.policyID], + [route.params.categoryName, route.params.policyID], ); return ( @@ -88,6 +88,7 @@ function EditCategoryPage({route, policyCategories}: EditCategoryPageProps) { Date: Mon, 11 Mar 2024 11:41:20 +0100 Subject: [PATCH 036/103] fix: resolve comment --- src/libs/OptionsListUtils.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/libs/OptionsListUtils.ts b/src/libs/OptionsListUtils.ts index 50481f7f1619..3dd23752d5db 100644 --- a/src/libs/OptionsListUtils.ts +++ b/src/libs/OptionsListUtils.ts @@ -480,7 +480,7 @@ function getSearchText( /** * Get an object of error messages keyed by microtime by combining all error objects related to the report. */ -function getAllReportErrors(report: OnyxEntry, reportActions: OnyxEntry | undefined, transactions: OnyxCollection = allTransactions): OnyxCommon.Errors { +function getAllReportErrors(report: OnyxEntry, reportActions: OnyxEntry, transactions: OnyxCollection = allTransactions): OnyxCommon.Errors { const reportErrors = report?.errors ?? {}; const reportErrorFields = report?.errorFields ?? {}; const reportActionErrors: OnyxCommon.ErrorFields = Object.values(reportActions ?? {}).reduce( From ce878325a4d696c4cb0ec9f1496bba55ec8a815e Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Arkadiusz=20Chrab=C4=85szczewski?= Date: Mon, 11 Mar 2024 13:44:21 +0100 Subject: [PATCH 037/103] feat: reuseable form for create and edit categories --- src/ONYXKEYS.ts | 6 +- .../workspace/categories/CategoryForm.tsx | 77 +++++++++++++++++++ .../categories/CreateCategoryPage.tsx | 53 ++----------- .../workspace/categories/EditCategoryPage.tsx | 59 +++----------- ...CreateForm.ts => WorkspaceCategoryForm.ts} | 4 +- src/types/form/index.ts | 2 +- 6 files changed, 98 insertions(+), 103 deletions(-) create mode 100644 src/pages/workspace/categories/CategoryForm.tsx rename src/types/form/{WorkspaceCategoryCreateForm.ts => WorkspaceCategoryForm.ts} (77%) diff --git a/src/ONYXKEYS.ts b/src/ONYXKEYS.ts index bb1766f40e1f..02f4c2ce9f32 100755 --- a/src/ONYXKEYS.ts +++ b/src/ONYXKEYS.ts @@ -327,8 +327,8 @@ const ONYXKEYS = { ADD_DEBIT_CARD_FORM: 'addDebitCardForm', ADD_DEBIT_CARD_FORM_DRAFT: 'addDebitCardFormDraft', WORKSPACE_SETTINGS_FORM: 'workspaceSettingsForm', - WORKSPACE_CATEGORY_CREATE_FORM: 'workspaceCategoryCreate', - WORKSPACE_CATEGORY_CREATE_FORM_DRAFT: 'workspaceCategoryCreateDraft', + WORKSPACE_CATEGORY_FORM: 'workspaceCategoryForm', + WORKSPACE_CATEGORY_FORM_DRAFT: 'workspaceCategoryFormDraft', WORKSPACE_SETTINGS_FORM_DRAFT: 'workspaceSettingsFormDraft', WORKSPACE_DESCRIPTION_FORM: 'workspaceDescriptionForm', WORKSPACE_DESCRIPTION_FORM_DRAFT: 'workspaceDescriptionFormDraft', @@ -410,7 +410,7 @@ type AllOnyxKeys = DeepValueOf; type OnyxFormValuesMapping = { [ONYXKEYS.FORMS.ADD_DEBIT_CARD_FORM]: FormTypes.AddDebitCardForm; [ONYXKEYS.FORMS.WORKSPACE_SETTINGS_FORM]: FormTypes.WorkspaceSettingsForm; - [ONYXKEYS.FORMS.WORKSPACE_CATEGORY_CREATE_FORM]: FormTypes.WorkspaceCategoryCreateForm; + [ONYXKEYS.FORMS.WORKSPACE_CATEGORY_FORM]: FormTypes.WorkspaceCategoryForm; [ONYXKEYS.FORMS.WORKSPACE_RATE_AND_UNIT_FORM]: FormTypes.WorkspaceRateAndUnitForm; [ONYXKEYS.FORMS.CLOSE_ACCOUNT_FORM]: FormTypes.CloseAccountForm; [ONYXKEYS.FORMS.PROFILE_SETTINGS_FORM]: FormTypes.ProfileSettingsForm; diff --git a/src/pages/workspace/categories/CategoryForm.tsx b/src/pages/workspace/categories/CategoryForm.tsx new file mode 100644 index 000000000000..0409a7075517 --- /dev/null +++ b/src/pages/workspace/categories/CategoryForm.tsx @@ -0,0 +1,77 @@ +import React, {useCallback} from 'react'; +import type {OnyxEntry} from 'react-native-onyx'; +import FormProvider from '@components/Form/FormProvider'; +import InputWrapper from '@components/Form/InputWrapper'; +import type {FormInputErrors, FormOnyxValues} from '@components/Form/types'; +import TextInput from '@components/TextInput'; +import useLocalize from '@hooks/useLocalize'; +import useThemeStyles from '@hooks/useThemeStyles'; +import * as ErrorUtils from '@libs/ErrorUtils'; +import * as ValidationUtils from '@libs/ValidationUtils'; +import CONST from '@src/CONST'; +import ONYXKEYS from '@src/ONYXKEYS'; +import INPUT_IDS from '@src/types/form/WorkspaceCategoryForm'; +import type {PolicyCategories} from '@src/types/onyx'; + +type EditCategoryFormProps = { + /** All policy categories */ + policyCategories: OnyxEntry; + + /** The name of the category */ + categoryName?: string; + + /** Function to call when the form is submitted */ + onSubmit: (values: FormOnyxValues) => void; +}; + +function CategoryForm({onSubmit, policyCategories, categoryName}: EditCategoryFormProps) { + const styles = useThemeStyles(); + const {translate} = useLocalize(); + + const validate = useCallback( + (values: FormOnyxValues) => { + const errors: FormInputErrors = {}; + const newCategoryName = values.categoryName.trim(); + + if (!ValidationUtils.isRequiredFulfilled(newCategoryName)) { + errors.categoryName = 'workspace.categories.categoryRequiredError'; + } else if (policyCategories?.[newCategoryName]) { + errors.categoryName = 'workspace.categories.existingCategoryError'; + } else if (newCategoryName === CONST.INVALID_CATEGORY_NAME) { + errors.categoryName = 'workspace.categories.invalidCategoryName'; + } else if ([...newCategoryName].length > CONST.CATEGORY_NAME_LIMIT) { + // Uses the spread syntax to count the number of Unicode code points instead of the number of UTF-16 code units. + ErrorUtils.addErrorMessage(errors, 'categoryName', ['common.error.characterLimitExceedCounter', {length: [...newCategoryName].length, limit: CONST.CATEGORY_NAME_LIMIT}]); + } + + return errors; + }, + [policyCategories], + ); + + return ( + + + + ); +} + +CategoryForm.displayName = 'CategoryForm'; + +export default CategoryForm; diff --git a/src/pages/workspace/categories/CreateCategoryPage.tsx b/src/pages/workspace/categories/CreateCategoryPage.tsx index cfe28ba292b0..ed216bf31853 100644 --- a/src/pages/workspace/categories/CreateCategoryPage.tsx +++ b/src/pages/workspace/categories/CreateCategoryPage.tsx @@ -3,26 +3,20 @@ import React, {useCallback} from 'react'; import {Keyboard} from 'react-native'; import {withOnyx} from 'react-native-onyx'; import type {OnyxEntry} from 'react-native-onyx'; -import FormProvider from '@components/Form/FormProvider'; -import InputWrapper from '@components/Form/InputWrapper'; -import type {FormInputErrors, FormOnyxValues} from '@components/Form/types'; +import type {FormOnyxValues} from '@components/Form/types'; import HeaderWithBackButton from '@components/HeaderWithBackButton'; import ScreenWrapper from '@components/ScreenWrapper'; -import TextInput from '@components/TextInput'; import useLocalize from '@hooks/useLocalize'; import useThemeStyles from '@hooks/useThemeStyles'; -import * as ErrorUtils from '@libs/ErrorUtils'; import Navigation from '@libs/Navigation/Navigation'; -import * as ValidationUtils from '@libs/ValidationUtils'; import type {SettingsNavigatorParamList} from '@navigation/types'; import AdminPolicyAccessOrNotFoundWrapper from '@pages/workspace/AdminPolicyAccessOrNotFoundWrapper'; import PaidPolicyAccessOrNotFoundWrapper from '@pages/workspace/PaidPolicyAccessOrNotFoundWrapper'; import * as Policy from '@userActions/Policy'; -import CONST from '@src/CONST'; import ONYXKEYS from '@src/ONYXKEYS'; import type SCREENS from '@src/SCREENS'; -import INPUT_IDS from '@src/types/form/WorkspaceCategoryCreateForm'; import type {PolicyCategories} from '@src/types/onyx'; +import CategoryForm from './CategoryForm'; type WorkspaceCreateCategoryPageOnyxProps = { /** All policy categories */ @@ -35,29 +29,8 @@ function CreateCategoryPage({route, policyCategories}: CreateCategoryPageProps) const styles = useThemeStyles(); const {translate} = useLocalize(); - const validate = useCallback( - (values: FormOnyxValues) => { - const errors: FormInputErrors = {}; - const categoryName = values.categoryName.trim(); - - if (!ValidationUtils.isRequiredFulfilled(categoryName)) { - errors.categoryName = 'workspace.categories.categoryRequiredError'; - } else if (policyCategories?.[categoryName]) { - errors.categoryName = 'workspace.categories.existingCategoryError'; - } else if (categoryName === CONST.INVALID_CATEGORY_NAME) { - errors.categoryName = 'workspace.categories.invalidCategoryName'; - } else if ([...categoryName].length > CONST.CATEGORY_NAME_LIMIT) { - // Uses the spread syntax to count the number of Unicode code points instead of the number of UTF-16 code units. - ErrorUtils.addErrorMessage(errors, 'categoryName', ['common.error.characterLimitExceedCounter', {length: [...categoryName].length, limit: CONST.CATEGORY_NAME_LIMIT}]); - } - - return errors; - }, - [policyCategories], - ); - const createCategory = useCallback( - (values: FormOnyxValues) => { + (values: FormOnyxValues) => { Policy.createPolicyCategory(route.params.policyID, values.categoryName.trim()); Keyboard.dismiss(); Navigation.goBack(); @@ -77,24 +50,10 @@ function CreateCategoryPage({route, policyCategories}: CreateCategoryPageProps) title={translate('workspace.categories.addCategory')} onBackButtonPress={Navigation.goBack} /> - - - + policyCategories={policyCategories} + /> diff --git a/src/pages/workspace/categories/EditCategoryPage.tsx b/src/pages/workspace/categories/EditCategoryPage.tsx index 07ebc274e8a4..c42aa44c7215 100644 --- a/src/pages/workspace/categories/EditCategoryPage.tsx +++ b/src/pages/workspace/categories/EditCategoryPage.tsx @@ -3,26 +3,20 @@ import React, {useCallback} from 'react'; import {Keyboard} from 'react-native'; import {withOnyx} from 'react-native-onyx'; import type {OnyxEntry} from 'react-native-onyx'; -import FormProvider from '@components/Form/FormProvider'; -import InputWrapper from '@components/Form/InputWrapper'; -import type {FormInputErrors, FormOnyxValues} from '@components/Form/types'; +import type {FormOnyxValues} from '@components/Form/types'; import HeaderWithBackButton from '@components/HeaderWithBackButton'; import ScreenWrapper from '@components/ScreenWrapper'; -import TextInput from '@components/TextInput'; import useLocalize from '@hooks/useLocalize'; import useThemeStyles from '@hooks/useThemeStyles'; -import * as ErrorUtils from '@libs/ErrorUtils'; import Navigation from '@libs/Navigation/Navigation'; -import * as ValidationUtils from '@libs/ValidationUtils'; import type {SettingsNavigatorParamList} from '@navigation/types'; import AdminPolicyAccessOrNotFoundWrapper from '@pages/workspace/AdminPolicyAccessOrNotFoundWrapper'; import PaidPolicyAccessOrNotFoundWrapper from '@pages/workspace/PaidPolicyAccessOrNotFoundWrapper'; import * as Policy from '@userActions/Policy'; -import CONST from '@src/CONST'; import ONYXKEYS from '@src/ONYXKEYS'; import type SCREENS from '@src/SCREENS'; -import INPUT_IDS from '@src/types/form/WorkspaceCategoryCreateForm'; import type {PolicyCategories} from '@src/types/onyx'; +import CategoryForm from './CategoryForm'; type WorkspaceEditCategoryPageOnyxProps = { /** All policy categories */ @@ -35,29 +29,8 @@ function EditCategoryPage({route, policyCategories}: EditCategoryPageProps) { const styles = useThemeStyles(); const {translate} = useLocalize(); - const validate = useCallback( - (values: FormOnyxValues) => { - const errors: FormInputErrors = {}; - const categoryName = values.categoryName.trim(); - - if (!ValidationUtils.isRequiredFulfilled(categoryName)) { - errors.categoryName = 'workspace.categories.categoryRequiredError'; - } else if (policyCategories?.[categoryName]) { - errors.categoryName = 'workspace.categories.existingCategoryError'; - } else if (categoryName === CONST.INVALID_CATEGORY_NAME) { - errors.categoryName = 'workspace.categories.invalidCategoryName'; - } else if ([...categoryName].length > CONST.CATEGORY_NAME_LIMIT) { - // Uses the spread syntax to count the number of Unicode code points instead of the number of UTF-16 code units. - ErrorUtils.addErrorMessage(errors, 'categoryName', ['common.error.characterLimitExceedCounter', {length: [...categoryName].length, limit: CONST.CATEGORY_NAME_LIMIT}]); - } - - return errors; - }, - [policyCategories], - ); - - const createCategory = useCallback( - (values: FormOnyxValues) => { + const editCategory = useCallback( + (values: FormOnyxValues) => { Policy.renamePolicyCategory(route.params.policyID, {oldName: route.params.categoryName, newName: values.categoryName}); Keyboard.dismiss(); Navigation.goBack(); @@ -77,25 +50,11 @@ function EditCategoryPage({route, policyCategories}: EditCategoryPageProps) { title={translate('workspace.categories.editCategory')} onBackButtonPress={Navigation.goBack} /> - - - + diff --git a/src/types/form/WorkspaceCategoryCreateForm.ts b/src/types/form/WorkspaceCategoryForm.ts similarity index 77% rename from src/types/form/WorkspaceCategoryCreateForm.ts rename to src/types/form/WorkspaceCategoryForm.ts index 051bf705fbf8..4f5f9282373c 100644 --- a/src/types/form/WorkspaceCategoryCreateForm.ts +++ b/src/types/form/WorkspaceCategoryForm.ts @@ -7,12 +7,12 @@ const INPUT_IDS = { type InputID = ValueOf; -type WorkspaceCategoryCreateForm = Form< +type WorkspaceCategoryForm = Form< InputID, { [INPUT_IDS.CATEGORY_NAME]: string; } >; -export type {WorkspaceCategoryCreateForm}; +export type {WorkspaceCategoryForm}; export default INPUT_IDS; diff --git a/src/types/form/index.ts b/src/types/form/index.ts index 3c1462574b9d..e55c5093a5c2 100644 --- a/src/types/form/index.ts +++ b/src/types/form/index.ts @@ -34,7 +34,7 @@ export type {SettingsStatusSetForm} from './SettingsStatusSetForm'; export type {WaypointForm} from './WaypointForm'; export type {WorkspaceInviteMessageForm} from './WorkspaceInviteMessageForm'; export type {WorkspaceRateAndUnitForm} from './WorkspaceRateAndUnitForm'; -export type {WorkspaceCategoryCreateForm} from './WorkspaceCategoryCreateForm'; +export type {WorkspaceCategoryForm} from './WorkspaceCategoryForm'; export type {WorkspaceSettingsForm} from './WorkspaceSettingsForm'; export type {ReportPhysicalCardForm} from './ReportPhysicalCardForm'; export type {WorkspaceDescriptionForm} from './WorkspaceDescriptionForm'; From 7ce94188225948f36f8fa08fe938bb87efc7eeac Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Arkadiusz=20Chrab=C4=85szczewski?= Date: Tue, 12 Mar 2024 10:37:37 +0100 Subject: [PATCH 038/103] fix: renamePolicyCategory params --- src/libs/API/parameters/RenameWorkspaceCategoriesParams.ts | 2 +- src/libs/actions/Policy.ts | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/src/libs/API/parameters/RenameWorkspaceCategoriesParams.ts b/src/libs/API/parameters/RenameWorkspaceCategoriesParams.ts index 4084dbad6394..4ed07858564f 100644 --- a/src/libs/API/parameters/RenameWorkspaceCategoriesParams.ts +++ b/src/libs/API/parameters/RenameWorkspaceCategoriesParams.ts @@ -2,7 +2,7 @@ type RenameWorkspaceCategoriesParams = { policyID: string; /** * Stringified JSON object with type of following structure: - * Array<{[oldName: string]: string;}> where value is new category name + * {[oldName: string]: string;} where value is new category name */ categories: string; }; diff --git a/src/libs/actions/Policy.ts b/src/libs/actions/Policy.ts index 3a81bae87895..2ec9184064bd 100644 --- a/src/libs/actions/Policy.ts +++ b/src/libs/actions/Policy.ts @@ -2646,7 +2646,7 @@ function renamePolicyCategory(policyID: string, policyCategory: {oldName: string const parameters = { policyID, - categories: JSON.stringify([{[policyCategory.oldName]: policyCategory.newName}]), + categories: JSON.stringify({[policyCategory.oldName]: policyCategory.newName}), }; API.write(WRITE_COMMANDS.RENAME_WORKSPACE_CATEGORY, parameters, onyxData); From 804531c76c3ea9ddba233ffb864771da0d6e889f Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Arkadiusz=20Chrab=C4=85szczewski?= Date: Tue, 12 Mar 2024 10:45:24 +0100 Subject: [PATCH 039/103] refactor: move keyboard dismiss and handle go back in form --- src/pages/workspace/categories/CategoryForm.tsx | 13 ++++++++++++- .../workspace/categories/CreateCategoryPage.tsx | 2 -- src/pages/workspace/categories/EditCategoryPage.tsx | 2 -- 3 files changed, 12 insertions(+), 5 deletions(-) diff --git a/src/pages/workspace/categories/CategoryForm.tsx b/src/pages/workspace/categories/CategoryForm.tsx index 0409a7075517..98f69149e4a5 100644 --- a/src/pages/workspace/categories/CategoryForm.tsx +++ b/src/pages/workspace/categories/CategoryForm.tsx @@ -1,4 +1,5 @@ import React, {useCallback} from 'react'; +import {Keyboard} from 'react-native'; import type {OnyxEntry} from 'react-native-onyx'; import FormProvider from '@components/Form/FormProvider'; import InputWrapper from '@components/Form/InputWrapper'; @@ -7,6 +8,7 @@ import TextInput from '@components/TextInput'; import useLocalize from '@hooks/useLocalize'; import useThemeStyles from '@hooks/useThemeStyles'; import * as ErrorUtils from '@libs/ErrorUtils'; +import Navigation from '@libs/Navigation/Navigation'; import * as ValidationUtils from '@libs/ValidationUtils'; import CONST from '@src/CONST'; import ONYXKEYS from '@src/ONYXKEYS'; @@ -49,10 +51,19 @@ function CategoryForm({onSubmit, policyCategories, categoryName}: EditCategoryFo [policyCategories], ); + const handleOnSubmit = useCallback( + (values: FormOnyxValues) => { + onSubmit(values); + Keyboard.dismiss(); + Navigation.goBack(); + }, + [onSubmit], + ); + return ( ) => { Policy.createPolicyCategory(route.params.policyID, values.categoryName.trim()); - Keyboard.dismiss(); - Navigation.goBack(); }, [route.params.policyID], ); diff --git a/src/pages/workspace/categories/EditCategoryPage.tsx b/src/pages/workspace/categories/EditCategoryPage.tsx index c42aa44c7215..e79a70db2014 100644 --- a/src/pages/workspace/categories/EditCategoryPage.tsx +++ b/src/pages/workspace/categories/EditCategoryPage.tsx @@ -32,8 +32,6 @@ function EditCategoryPage({route, policyCategories}: EditCategoryPageProps) { const editCategory = useCallback( (values: FormOnyxValues) => { Policy.renamePolicyCategory(route.params.policyID, {oldName: route.params.categoryName, newName: values.categoryName}); - Keyboard.dismiss(); - Navigation.goBack(); }, [route.params.categoryName, route.params.policyID], ); From ac541ee917e9d15ffebf4514fd72b08cb40a72ee Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Arkadiusz=20Chrab=C4=85szczewski?= Date: Tue, 12 Mar 2024 10:45:51 +0100 Subject: [PATCH 040/103] fix: remove unused imports --- src/pages/workspace/categories/CreateCategoryPage.tsx | 1 - src/pages/workspace/categories/EditCategoryPage.tsx | 1 - 2 files changed, 2 deletions(-) diff --git a/src/pages/workspace/categories/CreateCategoryPage.tsx b/src/pages/workspace/categories/CreateCategoryPage.tsx index 54b1e7809e7b..93effada5ad5 100644 --- a/src/pages/workspace/categories/CreateCategoryPage.tsx +++ b/src/pages/workspace/categories/CreateCategoryPage.tsx @@ -1,6 +1,5 @@ import type {StackScreenProps} from '@react-navigation/stack'; import React, {useCallback} from 'react'; -import {Keyboard} from 'react-native'; import {withOnyx} from 'react-native-onyx'; import type {OnyxEntry} from 'react-native-onyx'; import type {FormOnyxValues} from '@components/Form/types'; diff --git a/src/pages/workspace/categories/EditCategoryPage.tsx b/src/pages/workspace/categories/EditCategoryPage.tsx index e79a70db2014..821593be0f51 100644 --- a/src/pages/workspace/categories/EditCategoryPage.tsx +++ b/src/pages/workspace/categories/EditCategoryPage.tsx @@ -1,6 +1,5 @@ import type {StackScreenProps} from '@react-navigation/stack'; import React, {useCallback} from 'react'; -import {Keyboard} from 'react-native'; import {withOnyx} from 'react-native-onyx'; import type {OnyxEntry} from 'react-native-onyx'; import type {FormOnyxValues} from '@components/Form/types'; From c99e646b42fad21fc70f6e8e0d506fb864f20526 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Arkadiusz=20Chrab=C4=85szczewski?= Date: Tue, 12 Mar 2024 11:09:27 +0100 Subject: [PATCH 041/103] fix: close rhp on edit category --- src/pages/workspace/categories/EditCategoryPage.tsx | 1 + 1 file changed, 1 insertion(+) diff --git a/src/pages/workspace/categories/EditCategoryPage.tsx b/src/pages/workspace/categories/EditCategoryPage.tsx index 821593be0f51..d633cd69ac68 100644 --- a/src/pages/workspace/categories/EditCategoryPage.tsx +++ b/src/pages/workspace/categories/EditCategoryPage.tsx @@ -31,6 +31,7 @@ function EditCategoryPage({route, policyCategories}: EditCategoryPageProps) { const editCategory = useCallback( (values: FormOnyxValues) => { Policy.renamePolicyCategory(route.params.policyID, {oldName: route.params.categoryName, newName: values.categoryName}); + Navigation.goBack(); }, [route.params.categoryName, route.params.policyID], ); From 611d42d486f55316ea37e4dcf98748038ce2a9bb Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Arkadiusz=20Chrab=C4=85szczewski?= Date: Tue, 12 Mar 2024 11:26:51 +0100 Subject: [PATCH 042/103] fix: update optimistic data --- src/libs/actions/Policy.ts | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/src/libs/actions/Policy.ts b/src/libs/actions/Policy.ts index 2ec9184064bd..0b7168941f4e 100644 --- a/src/libs/actions/Policy.ts +++ b/src/libs/actions/Policy.ts @@ -2608,8 +2608,10 @@ function renamePolicyCategory(policyID: string, policyCategory: {oldName: string onyxMethod: Onyx.METHOD.MERGE, key: `${ONYXKEYS.COLLECTION.POLICY_CATEGORIES}${policyID}`, value: { - [policyCategory.oldName]: { + [policyCategory.oldName]: null, + [policyCategory.newName]: { name: policyCategory.newName, + pendingAction: CONST.RED_BRICK_ROAD_PENDING_ACTION.UPDATE, }, }, }, @@ -2634,6 +2636,7 @@ function renamePolicyCategory(policyID: string, policyCategory: {oldName: string onyxMethod: Onyx.METHOD.MERGE, key: `${ONYXKEYS.COLLECTION.POLICY_CATEGORIES}${policyID}`, value: { + [policyCategory.newName]: null, [policyCategory.oldName]: { name: policyCategory.oldName, errors: ErrorUtils.getMicroSecondOnyxError('workspace.categories.genericFailureMessage'), From b6e7b2cb893793934f36baef9a0fc38733c76603 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Arkadiusz=20Chrab=C4=85szczewski?= Date: Tue, 12 Mar 2024 12:04:38 +0100 Subject: [PATCH 043/103] fix: set all fields --- src/libs/actions/Policy.ts | 3 +++ 1 file changed, 3 insertions(+) diff --git a/src/libs/actions/Policy.ts b/src/libs/actions/Policy.ts index 0b7168941f4e..e21b09b6cf52 100644 --- a/src/libs/actions/Policy.ts +++ b/src/libs/actions/Policy.ts @@ -2610,6 +2610,7 @@ function renamePolicyCategory(policyID: string, policyCategory: {oldName: string value: { [policyCategory.oldName]: null, [policyCategory.newName]: { + ...policyCategoryToUpdate, name: policyCategory.newName, pendingAction: CONST.RED_BRICK_ROAD_PENDING_ACTION.UPDATE, }, @@ -2626,6 +2627,7 @@ function renamePolicyCategory(policyID: string, policyCategory: {oldName: string [policyCategory.newName]: { ...policyCategoryToUpdate, name: policyCategory.newName, + pendingAction: null, }, }, }, @@ -2638,6 +2640,7 @@ function renamePolicyCategory(policyID: string, policyCategory: {oldName: string value: { [policyCategory.newName]: null, [policyCategory.oldName]: { + ...policyCategoryToUpdate, name: policyCategory.oldName, errors: ErrorUtils.getMicroSecondOnyxError('workspace.categories.genericFailureMessage'), pendingAction: null, From a379886256c4a905e92b4439822aa8447ba8cce6 Mon Sep 17 00:00:00 2001 From: dukenv0307 Date: Wed, 13 Mar 2024 15:10:04 +0700 Subject: [PATCH 044/103] fix: unselect category when editing a split bill --- src/pages/iou/request/step/IOURequestStepCategory.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/pages/iou/request/step/IOURequestStepCategory.js b/src/pages/iou/request/step/IOURequestStepCategory.js index 1945edbc24c4..38f3f8803c53 100644 --- a/src/pages/iou/request/step/IOURequestStepCategory.js +++ b/src/pages/iou/request/step/IOURequestStepCategory.js @@ -93,7 +93,7 @@ function IOURequestStepCategory({ // In the split flow, when editing we use SPLIT_TRANSACTION_DRAFT to save draft value if (isEditingSplitBill) { - IOU.setDraftSplitTransaction(transaction.transactionID, {category: category.searchText}); + IOU.setDraftSplitTransaction(transaction.transactionID, {category: updatedCategory}); navigateBack(); return; } From 71eb6365db023bfbe3526afa9add8730d87882c4 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Arkadiusz=20Chrab=C4=85szczewski?= Date: Wed, 13 Mar 2024 11:15:01 +0100 Subject: [PATCH 045/103] fix: remove unused imports --- src/pages/workspace/categories/CreateCategoryPage.tsx | 2 -- 1 file changed, 2 deletions(-) diff --git a/src/pages/workspace/categories/CreateCategoryPage.tsx b/src/pages/workspace/categories/CreateCategoryPage.tsx index ceec8dc25c37..80370d2197fa 100644 --- a/src/pages/workspace/categories/CreateCategoryPage.tsx +++ b/src/pages/workspace/categories/CreateCategoryPage.tsx @@ -5,8 +5,6 @@ import type {OnyxEntry} from 'react-native-onyx'; import type {FormOnyxValues} from '@components/Form/types'; import HeaderWithBackButton from '@components/HeaderWithBackButton'; import ScreenWrapper from '@components/ScreenWrapper'; -import TextInput from '@components/TextInput'; -import useAutoFocusInput from '@hooks/useAutoFocusInput'; import useLocalize from '@hooks/useLocalize'; import useThemeStyles from '@hooks/useThemeStyles'; import Navigation from '@libs/Navigation/Navigation'; From 8f73038a1c830b6770958c53f1c75ba932725bc3 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Arkadiusz=20Chrab=C4=85szczewski?= Date: Wed, 13 Mar 2024 11:36:25 +0100 Subject: [PATCH 046/103] refactor(cr): rename variables/types --- src/pages/workspace/categories/CategoryForm.tsx | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/src/pages/workspace/categories/CategoryForm.tsx b/src/pages/workspace/categories/CategoryForm.tsx index 5c2d95a46c3e..ffc725fb4bf2 100644 --- a/src/pages/workspace/categories/CategoryForm.tsx +++ b/src/pages/workspace/categories/CategoryForm.tsx @@ -16,7 +16,7 @@ import ONYXKEYS from '@src/ONYXKEYS'; import INPUT_IDS from '@src/types/form/WorkspaceCategoryForm'; import type {PolicyCategories} from '@src/types/onyx'; -type EditCategoryFormProps = { +type CategoryFormProps = { /** All policy categories */ policyCategories: OnyxEntry; @@ -27,7 +27,7 @@ type EditCategoryFormProps = { onSubmit: (values: FormOnyxValues) => void; }; -function CategoryForm({onSubmit, policyCategories, categoryName}: EditCategoryFormProps) { +function CategoryForm({onSubmit, policyCategories, categoryName}: CategoryFormProps) { const styles = useThemeStyles(); const {translate} = useLocalize(); const {inputCallbackRef} = useAutoFocusInput(); @@ -53,7 +53,7 @@ function CategoryForm({onSubmit, policyCategories, categoryName}: EditCategoryFo [policyCategories], ); - const handleOnSubmit = useCallback( + const submit = useCallback( (values: FormOnyxValues) => { onSubmit(values); Keyboard.dismiss(); @@ -65,7 +65,7 @@ function CategoryForm({onSubmit, policyCategories, categoryName}: EditCategoryFo return ( Date: Wed, 13 Mar 2024 11:45:14 +0100 Subject: [PATCH 047/103] fix(cr): success data errors field & navigation back to route --- src/libs/actions/Policy.ts | 13 ++++++------- src/pages/workspace/categories/EditCategoryPage.tsx | 3 ++- 2 files changed, 8 insertions(+), 8 deletions(-) diff --git a/src/libs/actions/Policy.ts b/src/libs/actions/Policy.ts index 1089e221f902..8f8a2a4b8f99 100644 --- a/src/libs/actions/Policy.ts +++ b/src/libs/actions/Policy.ts @@ -2769,13 +2769,12 @@ function renamePolicyCategory(policyID: string, policyCategory: {oldName: string onyxMethod: Onyx.METHOD.MERGE, key: `${ONYXKEYS.COLLECTION.POLICY_CATEGORIES}${policyID}`, value: { - errors: { - [policyCategory.oldName]: null, - [policyCategory.newName]: { - ...policyCategoryToUpdate, - name: policyCategory.newName, - pendingAction: null, - }, + [policyCategory.oldName]: null, + [policyCategory.newName]: { + ...policyCategoryToUpdate, + name: policyCategory.newName, + errors: null, + pendingAction: null, }, }, }, diff --git a/src/pages/workspace/categories/EditCategoryPage.tsx b/src/pages/workspace/categories/EditCategoryPage.tsx index d633cd69ac68..e085ffb2252e 100644 --- a/src/pages/workspace/categories/EditCategoryPage.tsx +++ b/src/pages/workspace/categories/EditCategoryPage.tsx @@ -13,6 +13,7 @@ import AdminPolicyAccessOrNotFoundWrapper from '@pages/workspace/AdminPolicyAcce import PaidPolicyAccessOrNotFoundWrapper from '@pages/workspace/PaidPolicyAccessOrNotFoundWrapper'; import * as Policy from '@userActions/Policy'; import ONYXKEYS from '@src/ONYXKEYS'; +import ROUTES from '@src/ROUTES'; import type SCREENS from '@src/SCREENS'; import type {PolicyCategories} from '@src/types/onyx'; import CategoryForm from './CategoryForm'; @@ -31,7 +32,7 @@ function EditCategoryPage({route, policyCategories}: EditCategoryPageProps) { const editCategory = useCallback( (values: FormOnyxValues) => { Policy.renamePolicyCategory(route.params.policyID, {oldName: route.params.categoryName, newName: values.categoryName}); - Navigation.goBack(); + Navigation.goBack(ROUTES.WORKSPACE_CATEGORIES.getRoute(route.params.policyID)); }, [route.params.categoryName, route.params.policyID], ); From e7b824973be3a2508174f0d11f5fbd3916e3df38 Mon Sep 17 00:00:00 2001 From: DylanDylann Date: Wed, 13 Mar 2024 18:21:58 +0700 Subject: [PATCH 048/103] update optimistic data --- src/libs/actions/Policy.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/libs/actions/Policy.ts b/src/libs/actions/Policy.ts index 48b9948fc1ff..6d0cc2fd156d 100644 --- a/src/libs/actions/Policy.ts +++ b/src/libs/actions/Policy.ts @@ -440,7 +440,7 @@ function setWorkspaceAutoReporting(policyID: string, enabled: boolean, frequency value: { autoReporting: enabled, harvesting: { - enabled: true, + enabled, }, autoReportingFrequency: frequency, pendingFields: {isAutoApprovalEnabled: CONST.RED_BRICK_ROAD_PENDING_ACTION.UPDATE}, From f069f19910b7acefb19813e4e8af02b950b86fb6 Mon Sep 17 00:00:00 2001 From: Jakub Butkiewicz Date: Wed, 13 Mar 2024 14:57:26 +0100 Subject: [PATCH 049/103] fix: typecheck --- src/pages/home/sidebar/PressableAvatarWithIndicator.tsx | 2 +- src/pages/home/sidebar/SidebarLinks.tsx | 2 +- .../sidebar/SidebarScreen/FloatingActionButtonAndPopover.tsx | 2 +- 3 files changed, 3 insertions(+), 3 deletions(-) diff --git a/src/pages/home/sidebar/PressableAvatarWithIndicator.tsx b/src/pages/home/sidebar/PressableAvatarWithIndicator.tsx index 7210aef47965..53bae3d8a770 100644 --- a/src/pages/home/sidebar/PressableAvatarWithIndicator.tsx +++ b/src/pages/home/sidebar/PressableAvatarWithIndicator.tsx @@ -22,7 +22,7 @@ type PressableAvatarWithIndicatorProps = PressableAvatarWithIndicatorOnyxProps & isSelected: boolean; /** Callback called when the avatar is pressed */ - onPress: () => void; + onPress?: () => void; }; function PressableAvatarWithIndicator({isLoading = true, isSelected = false, onPress}: PressableAvatarWithIndicatorProps) { diff --git a/src/pages/home/sidebar/SidebarLinks.tsx b/src/pages/home/sidebar/SidebarLinks.tsx index 5a2d1416c250..ef81aba715aa 100644 --- a/src/pages/home/sidebar/SidebarLinks.tsx +++ b/src/pages/home/sidebar/SidebarLinks.tsx @@ -43,7 +43,7 @@ type SidebarLinksProps = { activeWorkspaceID: string | undefined; }; -function SidebarLinks({onLinkClick, insets, optionListItems, isLoading, priorityMode = CONST.PRIORITY_MODE.DEFAULT, isActiveReport, isCreateMenuOpen}: SidebarLinksProps) { +function SidebarLinks({onLinkClick, insets, optionListItems, isLoading, priorityMode = CONST.PRIORITY_MODE.DEFAULT, isActiveReport}: SidebarLinksProps) { const styles = useThemeStyles(); const StyleUtils = useStyleUtils(); const modal = useRef({}); diff --git a/src/pages/home/sidebar/SidebarScreen/FloatingActionButtonAndPopover.tsx b/src/pages/home/sidebar/SidebarScreen/FloatingActionButtonAndPopover.tsx index a550dbd91657..90a4fc6fffa0 100644 --- a/src/pages/home/sidebar/SidebarScreen/FloatingActionButtonAndPopover.tsx +++ b/src/pages/home/sidebar/SidebarScreen/FloatingActionButtonAndPopover.tsx @@ -28,7 +28,7 @@ type FloatingActionButtonAndPopoverOnyxProps = { /** The list of policies the user has access to. */ allPolicies: OnyxCollection; - /** Wheater app is in loading state */ + /** Whether app is in loading state */ isLoading: OnyxEntry; }; From ef571f71d3448431a58353e97fb2f4cea1e7b36f Mon Sep 17 00:00:00 2001 From: DylanDylann Date: Wed, 13 Mar 2024 22:22:44 +0700 Subject: [PATCH 050/103] update failure data --- src/libs/actions/Policy.ts | 14 +++++++++++++- 1 file changed, 13 insertions(+), 1 deletion(-) diff --git a/src/libs/actions/Policy.ts b/src/libs/actions/Policy.ts index 6d0cc2fd156d..fee836404fd5 100644 --- a/src/libs/actions/Policy.ts +++ b/src/libs/actions/Policy.ts @@ -433,6 +433,7 @@ function buildAnnounceRoomMembersOnyxData(policyID: string, accountIDs: number[] } function setWorkspaceAutoReporting(policyID: string, enabled: boolean, frequency: ValueOf) { + const policy = ReportUtils.getPolicy(policyID); const optimisticData: OnyxUpdate[] = [ { onyxMethod: Onyx.METHOD.MERGE, @@ -453,7 +454,9 @@ function setWorkspaceAutoReporting(policyID: string, enabled: boolean, frequency onyxMethod: Onyx.METHOD.MERGE, key: `${ONYXKEYS.COLLECTION.POLICY}${policyID}`, value: { - autoReporting: !enabled, + autoReporting: policy.autoReporting, + harvesting: policy.harvesting, + autoReportingFrequency: policy.autoReportingFrequency, pendingFields: {isAutoApprovalEnabled: null, harvesting: null}, }, }, @@ -475,6 +478,8 @@ function setWorkspaceAutoReporting(policyID: string, enabled: boolean, frequency } function setWorkspaceAutoReportingFrequency(policyID: string, frequency: ValueOf) { + const policy = ReportUtils.getPolicy(policyID); + const optimisticData: OnyxUpdate[] = [ { onyxMethod: Onyx.METHOD.MERGE, @@ -491,6 +496,7 @@ function setWorkspaceAutoReportingFrequency(policyID: string, frequency: ValueOf onyxMethod: Onyx.METHOD.MERGE, key: `${ONYXKEYS.COLLECTION.POLICY}${policyID}`, value: { + autoReportingFrequency: policy.autoReportingFrequency, pendingFields: {autoReportingFrequency: null}, }, }, @@ -512,6 +518,7 @@ function setWorkspaceAutoReportingFrequency(policyID: string, frequency: ValueOf function setWorkspaceAutoReportingMonthlyOffset(policyID: string, autoReportingOffset: number | ValueOf) { const value = JSON.stringify({autoReportingOffset: autoReportingOffset.toString()}); + const policy = ReportUtils.getPolicy(policyID); const optimisticData: OnyxUpdate[] = [ { @@ -529,6 +536,7 @@ function setWorkspaceAutoReportingMonthlyOffset(policyID: string, autoReportingO onyxMethod: Onyx.METHOD.MERGE, key: `${ONYXKEYS.COLLECTION.POLICY}${policyID}`, value: { + autoReportingOffset: policy.autoReportingOffset, pendingFields: {autoReportingOffset: null}, }, }, @@ -550,6 +558,7 @@ function setWorkspaceAutoReportingMonthlyOffset(policyID: string, autoReportingO function setWorkspaceApprovalMode(policyID: string, approver: string, approvalMode: ValueOf) { const isAutoApprovalEnabled = approvalMode === CONST.POLICY.APPROVAL_MODE.BASIC; + const policy = ReportUtils.getPolicy(policyID); const value = { approver, @@ -573,6 +582,9 @@ function setWorkspaceApprovalMode(policyID: string, approver: string, approvalMo onyxMethod: Onyx.METHOD.MERGE, key: `${ONYXKEYS.COLLECTION.POLICY}${policyID}`, value: { + approver: policy.approver, + approvalMode: policy.approvalMode, + isAutoApprovalEnabled: policy.isAutoApprovalEnabled, pendingFields: {approvalMode: null}, }, }, From 750ca3fc04625e363663a668574f99b4b27e5606 Mon Sep 17 00:00:00 2001 From: DylanDylann Date: Thu, 14 Mar 2024 00:28:48 +0700 Subject: [PATCH 051/103] add null value as a fallback --- src/libs/actions/Policy.ts | 28 +++++++++++++++------------- 1 file changed, 15 insertions(+), 13 deletions(-) diff --git a/src/libs/actions/Policy.ts b/src/libs/actions/Policy.ts index a2bb039971d7..d7cc3aba9322 100644 --- a/src/libs/actions/Policy.ts +++ b/src/libs/actions/Policy.ts @@ -457,9 +457,11 @@ function setWorkspaceAutoReporting(policyID: string, enabled: boolean, frequency onyxMethod: Onyx.METHOD.MERGE, key: `${ONYXKEYS.COLLECTION.POLICY}${policyID}`, value: { - autoReporting: policy.autoReporting, - harvesting: policy.harvesting, - autoReportingFrequency: policy.autoReportingFrequency, + autoReporting: policy.autoReporting ?? null, + harvesting: { + enabled: policy?.harvesting?.enabled ?? null, + }, + autoReportingFrequency: policy.autoReportingFrequency ?? null, pendingFields: {isAutoApprovalEnabled: null, harvesting: null}, }, }, @@ -499,7 +501,7 @@ function setWorkspaceAutoReportingFrequency(policyID: string, frequency: ValueOf onyxMethod: Onyx.METHOD.MERGE, key: `${ONYXKEYS.COLLECTION.POLICY}${policyID}`, value: { - autoReportingFrequency: policy.autoReportingFrequency, + autoReportingFrequency: policy.autoReportingFrequency ?? null, pendingFields: {autoReportingFrequency: null}, }, }, @@ -539,7 +541,7 @@ function setWorkspaceAutoReportingMonthlyOffset(policyID: string, autoReportingO onyxMethod: Onyx.METHOD.MERGE, key: `${ONYXKEYS.COLLECTION.POLICY}${policyID}`, value: { - autoReportingOffset: policy.autoReportingOffset, + autoReportingOffset: policy.autoReportingOffset ?? null, pendingFields: {autoReportingOffset: null}, }, }, @@ -585,9 +587,9 @@ function setWorkspaceApprovalMode(policyID: string, approver: string, approvalMo onyxMethod: Onyx.METHOD.MERGE, key: `${ONYXKEYS.COLLECTION.POLICY}${policyID}`, value: { - approver: policy.approver, - approvalMode: policy.approvalMode, - isAutoApprovalEnabled: policy.isAutoApprovalEnabled, + approver: policy.approver ?? null, + approvalMode: policy.approvalMode ?? null, + isAutoApprovalEnabled: policy.isAutoApprovalEnabled ?? null, pendingFields: {approvalMode: null}, }, }, @@ -639,8 +641,8 @@ function setWorkspacePayer(policyID: string, reimburserEmail: string, reimburser onyxMethod: Onyx.METHOD.MERGE, key: `${ONYXKEYS.COLLECTION.POLICY}${policyID}`, value: { - reimburserEmail: policy.reimburserEmail, - reimburserAccountID: policy.reimburserAccountID, + reimburserEmail: policy.reimburserEmail ?? null, + reimburserAccountID: policy.reimburserAccountID ?? null, errorFields: {reimburserEmail: ErrorUtils.getMicroSecondOnyxError('workflowsPayerPage.genericErrorMessage')}, pendingFields: {reimburserEmail: null}, }, @@ -689,9 +691,9 @@ function setWorkspaceReimbursement(policyID: string, reimbursementChoice: ValueO onyxMethod: Onyx.METHOD.MERGE, key: `${ONYXKEYS.COLLECTION.POLICY}${policyID}`, value: { - reimbursementChoice: policy.reimbursementChoice, - reimburserAccountID: policy.reimburserAccountID, - reimburserEmail: policy.reimburserEmail, + reimbursementChoice: policy.reimbursementChoice ?? null, + reimburserAccountID: policy.reimburserAccountID ?? null, + reimburserEmail: policy.reimburserEmail ?? null, errorFields: {reimbursementChoice: ErrorUtils.getMicroSecondOnyxError('common.genericErrorMessage')}, pendingFields: {reimbursementChoice: null}, }, From 723b0503ee7b97cf95e1555c2c4a0a6357714f04 Mon Sep 17 00:00:00 2001 From: Tim Golen Date: Wed, 13 Mar 2024 14:49:21 -0600 Subject: [PATCH 052/103] Remove deprecated methods --- ios/Podfile.lock | 2 +- src/libs/ReportUtils.ts | 33 +++++++++++++++++++++++++++++---- src/libs/TransactionUtils.ts | 32 +------------------------------- 3 files changed, 31 insertions(+), 36 deletions(-) diff --git a/ios/Podfile.lock b/ios/Podfile.lock index d0007ec51668..6c67fecc7ffc 100644 --- a/ios/Podfile.lock +++ b/ios/Podfile.lock @@ -1917,4 +1917,4 @@ SPEC CHECKSUMS: PODFILE CHECKSUM: a431c146e1501391834a2f299a74093bac53b530 -COCOAPODS: 1.13.0 +COCOAPODS: 1.12.1 diff --git a/src/libs/ReportUtils.ts b/src/libs/ReportUtils.ts index 8dc1c9967f13..8f8785d36fd3 100644 --- a/src/libs/ReportUtils.ts +++ b/src/libs/ReportUtils.ts @@ -2345,6 +2345,31 @@ function hasMissingSmartscanFields(iouReportID: string): boolean { return TransactionUtils.getAllReportTransactions(iouReportID).some((transaction) => TransactionUtils.hasMissingSmartscanFields(transaction)); } +/** + * Get the transactions related to a report preview with receipts + * Get the details linked to the IOU reportAction + * + * NOTE: This method is only meant to be used inside this action file. Do not export and use it elsewhere. Use withOnyx or Onyx.connect() instead. + */ +function getLinkedTransaction(reportAction: OnyxEntry): Transaction | EmptyObject { + let transactionID = ''; + + if (reportAction?.actionName === CONST.REPORT.ACTIONS.TYPE.IOU) { + transactionID = (reportAction?.originalMessage as IOUMessage)?.IOUTransactionID ?? ''; + } + + return allTransactions?.[`${ONYXKEYS.COLLECTION.TRANSACTION}${transactionID}`] ?? {}; +} + +/** + * Retrieve the particular transaction object given its ID. + * + * NOTE: This method is only meant to be used inside this action file. Do not export and use it elsewhere. Use withOnyx or Onyx.connect() instead. + */ +function getTransaction(transactionID: string): OnyxEntry | EmptyObject { + return allTransactions?.[`${ONYXKEYS.COLLECTION.TRANSACTION}${transactionID}`] ?? {}; +} + /** * Given a parent IOU report action get report name for the LHN. */ @@ -2357,7 +2382,7 @@ function getTransactionReportName(reportAction: OnyxEntry) return Localize.translateLocal(translationKey, {amount: formattedAmount, payer: ''}); } - const transaction = TransactionUtils.getTransaction(originalMessage.IOUTransactionID ?? ''); + const transaction = getTransaction(originalMessage.IOUTransactionID ?? ''); const transactionDetails = getTransactionDetails(!isEmptyObject(transaction) ? transaction : null); const formattedAmount = CurrencyUtils.convertToDisplayString(transactionDetails?.amount ?? 0, transactionDetails?.currency); const isRequestSettled = isSettled(originalMessage.IOUReportID); diff --git a/src/libs/TransactionUtils.ts b/src/libs/TransactionUtils.ts index f8f9ed0e0d47..bc94c8fee8fc 100644 --- a/src/libs/TransactionUtils.ts +++ b/src/libs/TransactionUtils.ts @@ -4,16 +4,13 @@ import Onyx from 'react-native-onyx'; import type {ValueOf} from 'type-fest'; import CONST from '@src/CONST'; import ONYXKEYS from '@src/ONYXKEYS'; -import type {RecentWaypoint, Report, ReportAction, TaxRate, TaxRates, Transaction, TransactionViolation} from '@src/types/onyx'; -import type {IOUMessage} from '@src/types/onyx/OriginalMessage'; +import type {RecentWaypoint, Report, TaxRate, TaxRates, Transaction, TransactionViolation} from '@src/types/onyx'; import type {Comment, Receipt, TransactionChanges, TransactionPendingFieldsKey, Waypoint, WaypointCollection} from '@src/types/onyx/Transaction'; -import type {EmptyObject} from '@src/types/utils/EmptyObject'; import {isEmptyObject} from '@src/types/utils/EmptyObject'; import {isCorporateCard, isExpensifyCard} from './CardUtils'; import DateUtils from './DateUtils'; import * as NumberUtils from './NumberUtils'; import {getCleanedTagName} from './PolicyUtils'; -import type {OptimisticIOUReportAction} from './ReportUtils'; let allTransactions: OnyxCollection = {}; @@ -248,15 +245,6 @@ function getUpdatedTransaction(transaction: Transaction, transactionChanges: Tra return updatedTransaction; } -/** - * Retrieve the particular transaction object given its ID. - * - * @deprecated Use withOnyx() or Onyx.connect() instead - */ -function getTransaction(transactionID: string): OnyxEntry | EmptyObject { - return allTransactions?.[`${ONYXKEYS.COLLECTION.TRANSACTION}${transactionID}`] ?? {}; -} - /** * Return the comment field (referred to as description in the App) from the transaction. * The comment does not have its modifiedComment counterpart. @@ -493,22 +481,6 @@ function hasRoute(transaction: OnyxEntry): boolean { return !!transaction?.routes?.route0?.geometry?.coordinates; } -/** - * Get the transactions related to a report preview with receipts - * Get the details linked to the IOU reportAction - * - * @deprecated Use Onyx.connect() or withOnyx() instead - */ -function getLinkedTransaction(reportAction: OnyxEntry): Transaction | EmptyObject { - let transactionID = ''; - - if (reportAction?.actionName === CONST.REPORT.ACTIONS.TYPE.IOU) { - transactionID = (reportAction?.originalMessage as IOUMessage)?.IOUTransactionID ?? ''; - } - - return allTransactions?.[`${ONYXKEYS.COLLECTION.TRANSACTION}${transactionID}`] ?? {}; -} - function getAllReportTransactions(reportID?: string): Transaction[] { // `reportID` from the `/CreateDistanceRequest` endpoint return's number instead of string for created `transaction`. // For reference, https://github.com/Expensify/App/pull/26536#issuecomment-1703573277. @@ -617,7 +589,6 @@ export { calculateTaxAmount, getEnabledTaxRateCount, getUpdatedTransaction, - getTransaction, getDescription, getHeaderTitleTranslationKey, getRequestType, @@ -638,7 +609,6 @@ export { getTagArrayFromName, getTagForDisplay, getTransactionViolations, - getLinkedTransaction, getAllReportTransactions, hasReceipt, hasEReceipt, From 64867e5ce23be2c57bd1c8ab332bd5ec1a0c17ae Mon Sep 17 00:00:00 2001 From: Tim Golen Date: Wed, 13 Mar 2024 14:55:50 -0600 Subject: [PATCH 053/103] Remove changes to POD lockfile --- ios/Podfile.lock | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/ios/Podfile.lock b/ios/Podfile.lock index 6c67fecc7ffc..d0007ec51668 100644 --- a/ios/Podfile.lock +++ b/ios/Podfile.lock @@ -1917,4 +1917,4 @@ SPEC CHECKSUMS: PODFILE CHECKSUM: a431c146e1501391834a2f299a74093bac53b530 -COCOAPODS: 1.12.1 +COCOAPODS: 1.13.0 From 1fa0d917bea633db625f08559c52d1f62473bbdb Mon Sep 17 00:00:00 2001 From: DylanDylann <141406735+DylanDylann@users.noreply.github.com> Date: Thu, 14 Mar 2024 10:02:50 +0700 Subject: [PATCH 054/103] Update src/libs/actions/Policy.ts Co-authored-by: Carlos Martins --- src/libs/actions/Policy.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/libs/actions/Policy.ts b/src/libs/actions/Policy.ts index d7cc3aba9322..d69594548218 100644 --- a/src/libs/actions/Policy.ts +++ b/src/libs/actions/Policy.ts @@ -459,7 +459,7 @@ function setWorkspaceAutoReporting(policyID: string, enabled: boolean, frequency value: { autoReporting: policy.autoReporting ?? null, harvesting: { - enabled: policy?.harvesting?.enabled ?? null, + enabled: policy.harvesting?.enabled ?? null, }, autoReportingFrequency: policy.autoReportingFrequency ?? null, pendingFields: {isAutoApprovalEnabled: null, harvesting: null}, From 5c9736e55b555cf3dd197c1ff25554f63717cb77 Mon Sep 17 00:00:00 2001 From: Jakub Kosmydel <104823336+kosmydel@users.noreply.github.com> Date: Thu, 14 Mar 2024 07:58:01 +0100 Subject: [PATCH 055/103] fix gap in the settings --- src/pages/settings/InitialSettingsPage.tsx | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/pages/settings/InitialSettingsPage.tsx b/src/pages/settings/InitialSettingsPage.tsx index 811ce38bdd1a..d5a12e7458ed 100755 --- a/src/pages/settings/InitialSettingsPage.tsx +++ b/src/pages/settings/InitialSettingsPage.tsx @@ -439,11 +439,11 @@ function InitialSettingsPage({session, userWallet, bankAccountList, fundList, wa return ( - + {headerContent} {accountMenuItems} {workspaceMenuItems} From 46f357eb8006229e7513c51bf706d70ea5f322fc Mon Sep 17 00:00:00 2001 From: Jakub Kosmydel <104823336+kosmydel@users.noreply.github.com> Date: Thu, 14 Mar 2024 08:19:29 +0100 Subject: [PATCH 056/103] fix profile avatar highlight --- src/styles/index.ts | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/styles/index.ts b/src/styles/index.ts index 55292988503f..3a0ddf446597 100644 --- a/src/styles/index.ts +++ b/src/styles/index.ts @@ -1441,8 +1441,8 @@ const styles = (theme: ThemeColors) => sidebarAvatar: { borderRadius: 28, - height: variables.componentSizeSmall, - width: variables.componentSizeSmall, + height: 28, + width: 28, }, selectedAvatarBorder: { From 082aaf5bac52059fef759d2c7ada564e7701fd08 Mon Sep 17 00:00:00 2001 From: Nicolay Arefyeu Date: Thu, 14 Mar 2024 10:33:08 +0200 Subject: [PATCH 057/103] Fix icons after regression --- src/components/ContextMenuItem.tsx | 2 +- src/components/DistanceRequest/DistanceRequestFooter.tsx | 2 +- src/styles/utils/spacing.ts | 4 ++++ 3 files changed, 6 insertions(+), 2 deletions(-) diff --git a/src/components/ContextMenuItem.tsx b/src/components/ContextMenuItem.tsx index b80d6a138c9e..ef580eaea3b6 100644 --- a/src/components/ContextMenuItem.tsx +++ b/src/components/ContextMenuItem.tsx @@ -101,7 +101,7 @@ function ContextMenuItem( > {({hovered, pressed}) => ( diff --git a/src/components/DistanceRequest/DistanceRequestFooter.tsx b/src/components/DistanceRequest/DistanceRequestFooter.tsx index f86d5369d4ac..624ea940888b 100644 --- a/src/components/DistanceRequest/DistanceRequestFooter.tsx +++ b/src/components/DistanceRequest/DistanceRequestFooter.tsx @@ -96,7 +96,7 @@ function DistanceRequestFooter({waypoints, transaction, mapboxAccessToken, navig onPress={() => navigateToWaypointEditPage(Object.keys(transaction?.comment?.waypoints ?? {}).length)} text={translate('distance.addStop')} isDisabled={numberOfWaypoints === MAX_WAYPOINTS} - innerStyles={[styles.ph10]} + innerStyles={[styles.pl10, styles.pr10]} /> )} diff --git a/src/styles/utils/spacing.ts b/src/styles/utils/spacing.ts index 56000a851e7b..740bf053c2d0 100644 --- a/src/styles/utils/spacing.ts +++ b/src/styles/utils/spacing.ts @@ -485,6 +485,10 @@ export default { paddingRight: 32, }, + pr10: { + paddingRight: 40, + }, + pr15: { paddingRight: 60, }, From b014a72ddf61d7313112f1946bf40c0deccec5c0 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Arkadiusz=20Chrab=C4=85szczewski?= Date: Thu, 14 Mar 2024 09:39:30 +0100 Subject: [PATCH 058/103] fix: add unencodedName --- src/libs/actions/Policy.ts | 3 +++ src/types/onyx/PolicyCategory.ts | 3 +++ 2 files changed, 6 insertions(+) diff --git a/src/libs/actions/Policy.ts b/src/libs/actions/Policy.ts index 8f8a2a4b8f99..a0284c93579f 100644 --- a/src/libs/actions/Policy.ts +++ b/src/libs/actions/Policy.ts @@ -2759,6 +2759,7 @@ function renamePolicyCategory(policyID: string, policyCategory: {oldName: string [policyCategory.newName]: { ...policyCategoryToUpdate, name: policyCategory.newName, + unencodedName: encodeURIComponent(policyCategory.newName), pendingAction: CONST.RED_BRICK_ROAD_PENDING_ACTION.UPDATE, }, }, @@ -2773,6 +2774,7 @@ function renamePolicyCategory(policyID: string, policyCategory: {oldName: string [policyCategory.newName]: { ...policyCategoryToUpdate, name: policyCategory.newName, + unencodedName: encodeURIComponent(policyCategory.newName), errors: null, pendingAction: null, }, @@ -2788,6 +2790,7 @@ function renamePolicyCategory(policyID: string, policyCategory: {oldName: string [policyCategory.oldName]: { ...policyCategoryToUpdate, name: policyCategory.oldName, + unencodedName: encodeURIComponent(policyCategory.oldName), errors: ErrorUtils.getMicroSecondOnyxError('workspace.categories.genericFailureMessage'), pendingAction: null, }, diff --git a/src/types/onyx/PolicyCategory.ts b/src/types/onyx/PolicyCategory.ts index 5cef49afc9d4..0fd498632dbc 100644 --- a/src/types/onyx/PolicyCategory.ts +++ b/src/types/onyx/PolicyCategory.ts @@ -4,6 +4,9 @@ type PolicyCategory = OnyxCommon.OnyxValueWithOfflineFeedback<{ /** Name of a category */ name: string; + /** Unencoded name of a category */ + unencodedName: string; + /** Flag that determines if a category is active and able to be selected */ enabled: boolean; From c4eb117884bd8bbbf528d2f876d0e063234b41f7 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Arkadiusz=20Chrab=C4=85szczewski?= Date: Thu, 14 Mar 2024 09:43:08 +0100 Subject: [PATCH 059/103] refactor: use dismiss modal instead of go back --- src/pages/workspace/categories/CategoryForm.tsx | 2 +- src/pages/workspace/categories/EditCategoryPage.tsx | 1 - 2 files changed, 1 insertion(+), 2 deletions(-) diff --git a/src/pages/workspace/categories/CategoryForm.tsx b/src/pages/workspace/categories/CategoryForm.tsx index ffc725fb4bf2..aaf954f64468 100644 --- a/src/pages/workspace/categories/CategoryForm.tsx +++ b/src/pages/workspace/categories/CategoryForm.tsx @@ -57,7 +57,7 @@ function CategoryForm({onSubmit, policyCategories, categoryName}: CategoryFormPr (values: FormOnyxValues) => { onSubmit(values); Keyboard.dismiss(); - Navigation.goBack(); + Navigation.dismissModal(); }, [onSubmit], ); diff --git a/src/pages/workspace/categories/EditCategoryPage.tsx b/src/pages/workspace/categories/EditCategoryPage.tsx index e085ffb2252e..a7ef4b8b24e6 100644 --- a/src/pages/workspace/categories/EditCategoryPage.tsx +++ b/src/pages/workspace/categories/EditCategoryPage.tsx @@ -32,7 +32,6 @@ function EditCategoryPage({route, policyCategories}: EditCategoryPageProps) { const editCategory = useCallback( (values: FormOnyxValues) => { Policy.renamePolicyCategory(route.params.policyID, {oldName: route.params.categoryName, newName: values.categoryName}); - Navigation.goBack(ROUTES.WORKSPACE_CATEGORIES.getRoute(route.params.policyID)); }, [route.params.categoryName, route.params.policyID], ); From 38476ce6d44541f7d8b40f789cf898dbddb290b7 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Arkadiusz=20Chrab=C4=85szczewski?= Date: Thu, 14 Mar 2024 09:45:23 +0100 Subject: [PATCH 060/103] fix: add shouldEnableMaxHeight in edit category --- src/pages/workspace/categories/EditCategoryPage.tsx | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/pages/workspace/categories/EditCategoryPage.tsx b/src/pages/workspace/categories/EditCategoryPage.tsx index a7ef4b8b24e6..35a8648f1a18 100644 --- a/src/pages/workspace/categories/EditCategoryPage.tsx +++ b/src/pages/workspace/categories/EditCategoryPage.tsx @@ -13,7 +13,6 @@ import AdminPolicyAccessOrNotFoundWrapper from '@pages/workspace/AdminPolicyAcce import PaidPolicyAccessOrNotFoundWrapper from '@pages/workspace/PaidPolicyAccessOrNotFoundWrapper'; import * as Policy from '@userActions/Policy'; import ONYXKEYS from '@src/ONYXKEYS'; -import ROUTES from '@src/ROUTES'; import type SCREENS from '@src/SCREENS'; import type {PolicyCategories} from '@src/types/onyx'; import CategoryForm from './CategoryForm'; @@ -43,6 +42,7 @@ function EditCategoryPage({route, policyCategories}: EditCategoryPageProps) { includeSafeAreaPaddingBottom={false} style={[styles.defaultModalContainer]} testID={EditCategoryPage.displayName} + shouldEnableMaxHeight > Date: Thu, 14 Mar 2024 09:46:23 +0100 Subject: [PATCH 061/103] fix: add edit category to rhp mapping --- src/libs/Navigation/linkingConfig/FULL_SCREEN_TO_RHP_MAPPING.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/libs/Navigation/linkingConfig/FULL_SCREEN_TO_RHP_MAPPING.ts b/src/libs/Navigation/linkingConfig/FULL_SCREEN_TO_RHP_MAPPING.ts index fd108f2c95f3..7561fb44933c 100755 --- a/src/libs/Navigation/linkingConfig/FULL_SCREEN_TO_RHP_MAPPING.ts +++ b/src/libs/Navigation/linkingConfig/FULL_SCREEN_TO_RHP_MAPPING.ts @@ -12,7 +12,7 @@ const FULL_SCREEN_TO_RHP_MAPPING: Partial> = { SCREENS.WORKSPACE.WORKFLOWS_PAYER, ], [SCREENS.WORKSPACE.TAGS]: [SCREENS.WORKSPACE.TAGS_SETTINGS, SCREENS.WORKSPACE.TAGS_EDIT, SCREENS.WORKSPACE.TAG_CREATE], - [SCREENS.WORKSPACE.CATEGORIES]: [SCREENS.WORKSPACE.CATEGORY_CREATE, SCREENS.WORKSPACE.CATEGORY_SETTINGS, SCREENS.WORKSPACE.CATEGORIES_SETTINGS], + [SCREENS.WORKSPACE.CATEGORIES]: [SCREENS.WORKSPACE.CATEGORY_CREATE, SCREENS.WORKSPACE.CATEGORY_SETTINGS, SCREENS.WORKSPACE.CATEGORIES_SETTINGS, SCREENS.WORKSPACE.CATEGORY_EDIT], }; export default FULL_SCREEN_TO_RHP_MAPPING; From 240500aa5b54b765356e7df5c2c26c3d991eee4c Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Arkadiusz=20Chrab=C4=85szczewski?= Date: Thu, 14 Mar 2024 09:59:53 +0100 Subject: [PATCH 062/103] decode unencodedName --- src/libs/actions/Policy.ts | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/src/libs/actions/Policy.ts b/src/libs/actions/Policy.ts index a0284c93579f..e954d19fd1e9 100644 --- a/src/libs/actions/Policy.ts +++ b/src/libs/actions/Policy.ts @@ -2759,7 +2759,7 @@ function renamePolicyCategory(policyID: string, policyCategory: {oldName: string [policyCategory.newName]: { ...policyCategoryToUpdate, name: policyCategory.newName, - unencodedName: encodeURIComponent(policyCategory.newName), + unencodedName: decodeURIComponent(policyCategory.newName), pendingAction: CONST.RED_BRICK_ROAD_PENDING_ACTION.UPDATE, }, }, @@ -2774,7 +2774,7 @@ function renamePolicyCategory(policyID: string, policyCategory: {oldName: string [policyCategory.newName]: { ...policyCategoryToUpdate, name: policyCategory.newName, - unencodedName: encodeURIComponent(policyCategory.newName), + unencodedName: decodeURIComponent(policyCategory.newName), errors: null, pendingAction: null, }, @@ -2790,7 +2790,7 @@ function renamePolicyCategory(policyID: string, policyCategory: {oldName: string [policyCategory.oldName]: { ...policyCategoryToUpdate, name: policyCategory.oldName, - unencodedName: encodeURIComponent(policyCategory.oldName), + unencodedName: decodeURIComponent(policyCategory.oldName), errors: ErrorUtils.getMicroSecondOnyxError('workspace.categories.genericFailureMessage'), pendingAction: null, }, From 2bd4b92f3c70652c16b625a091737a8d7fd47bb4 Mon Sep 17 00:00:00 2001 From: Jakub Butkiewicz Date: Thu, 14 Mar 2024 10:56:52 +0100 Subject: [PATCH 063/103] fix: removed unused comment --- src/libs/SidebarUtils.ts | 1 - 1 file changed, 1 deletion(-) diff --git a/src/libs/SidebarUtils.ts b/src/libs/SidebarUtils.ts index c70770ab44b7..81560e0d0691 100644 --- a/src/libs/SidebarUtils.ts +++ b/src/libs/SidebarUtils.ts @@ -125,7 +125,6 @@ function getOrderedReportIDs( // Normally, the spread operator would be used here to clone the report and prevent the need to reassign the params. // However, this code needs to be very performant to handle thousands of reports, so in the interest of speed, we're just going to disable this lint rule and add // the reportDisplayName property to the report object directly. - // eslint-disable-next-line no-param-reassign if (report) { // eslint-disable-next-line no-param-reassign report.displayName = ReportUtils.getReportName(report); From a3f28058b2d5e07e17fa1ea9ec50641351fafa08 Mon Sep 17 00:00:00 2001 From: tienifr Date: Thu, 14 Mar 2024 17:09:37 +0700 Subject: [PATCH 064/103] fix: cannot parse category name with special characters in url --- src/ROUTES.ts | 2 +- src/libs/Navigation/linkingConfig/config.ts | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/src/ROUTES.ts b/src/ROUTES.ts index ad2d9c10700b..e157c20bd46f 100644 --- a/src/ROUTES.ts +++ b/src/ROUTES.ts @@ -555,7 +555,7 @@ const ROUTES = { }, WORKSPACE_CATEGORY_SETTINGS: { route: 'settings/workspaces/:policyID/categories/:categoryName', - getRoute: (policyID: string, categoryName: string) => `settings/workspaces/${policyID}/categories/${encodeURI(categoryName)}` as const, + getRoute: (policyID: string, categoryName: string) => `settings/workspaces/${policyID}/categories/${encodeURIComponent(categoryName)}` as const, }, WORKSPACE_CATEGORIES_SETTINGS: { route: 'settings/workspaces/:policyID/categories/settings', diff --git a/src/libs/Navigation/linkingConfig/config.ts b/src/libs/Navigation/linkingConfig/config.ts index 04bc53e7b542..2bfd1b98286b 100644 --- a/src/libs/Navigation/linkingConfig/config.ts +++ b/src/libs/Navigation/linkingConfig/config.ts @@ -265,7 +265,7 @@ const config: LinkingOptions['config'] = { [SCREENS.WORKSPACE.CATEGORY_SETTINGS]: { path: ROUTES.WORKSPACE_CATEGORY_SETTINGS.route, parse: { - categoryName: (categoryName: string) => decodeURI(categoryName), + categoryName: (categoryName: string) => decodeURIComponent(categoryName), }, }, [SCREENS.WORKSPACE.CATEGORIES_SETTINGS]: { From 0c233855cbede17a79ddec392a00cb6a9eaa2f74 Mon Sep 17 00:00:00 2001 From: Nicolay Arefyeu Date: Thu, 14 Mar 2024 13:03:19 +0200 Subject: [PATCH 065/103] add min width for button --- src/styles/index.ts | 3 +++ 1 file changed, 3 insertions(+) diff --git a/src/styles/index.ts b/src/styles/index.ts index 55292988503f..514c4142108e 100644 --- a/src/styles/index.ts +++ b/src/styles/index.ts @@ -573,6 +573,7 @@ const styles = (theme: ThemeColors) => buttonSmall: { borderRadius: variables.buttonBorderRadius, minHeight: variables.componentSizeSmall, + minWidth: variables.componentSizeSmall, paddingHorizontal: 12, backgroundColor: theme.buttonDefaultBG, }, @@ -580,6 +581,7 @@ const styles = (theme: ThemeColors) => buttonMedium: { borderRadius: variables.buttonBorderRadius, minHeight: variables.componentSizeNormal, + minWidth: variables.componentSizeNormal, paddingHorizontal: 16, backgroundColor: theme.buttonDefaultBG, }, @@ -587,6 +589,7 @@ const styles = (theme: ThemeColors) => buttonLarge: { borderRadius: variables.buttonBorderRadius, minHeight: variables.componentSizeLarge, + minWidth: variables.componentSizeLarge, paddingHorizontal: 20, backgroundColor: theme.buttonDefaultBG, }, From 6eca76da57d0301385b60393d0f3ab39e1cb72a6 Mon Sep 17 00:00:00 2001 From: Jakub Kosmydel <104823336+kosmydel@users.noreply.github.com> Date: Thu, 14 Mar 2024 13:33:18 +0100 Subject: [PATCH 066/103] Update src/pages/settings/InitialSettingsPage.tsx Co-authored-by: Mykhailo Khutornyi <97676131+mkhutornyi@users.noreply.github.com> --- src/pages/settings/InitialSettingsPage.tsx | 1 + 1 file changed, 1 insertion(+) diff --git a/src/pages/settings/InitialSettingsPage.tsx b/src/pages/settings/InitialSettingsPage.tsx index d5a12e7458ed..7059efab309f 100755 --- a/src/pages/settings/InitialSettingsPage.tsx +++ b/src/pages/settings/InitialSettingsPage.tsx @@ -440,6 +440,7 @@ function InitialSettingsPage({session, userWallet, bankAccountList, fundList, wa return ( From c4af1e9033af5b835d0283e323d741bc01102159 Mon Sep 17 00:00:00 2001 From: Nicolay Arefyeu Date: Thu, 14 Mar 2024 15:07:28 +0200 Subject: [PATCH 067/103] fix icon for crop to 20x20 --- src/components/AvatarCropModal/AvatarCropModal.tsx | 1 - 1 file changed, 1 deletion(-) diff --git a/src/components/AvatarCropModal/AvatarCropModal.tsx b/src/components/AvatarCropModal/AvatarCropModal.tsx index 2998b2258ef1..0aec7d6ab70d 100644 --- a/src/components/AvatarCropModal/AvatarCropModal.tsx +++ b/src/components/AvatarCropModal/AvatarCropModal.tsx @@ -404,7 +404,6 @@ function AvatarCropModal({imageUri = '', imageName = '', imageType = '', onClose >