From 9a0d54010bdc2e5a17011d917ba0792217c31614 Mon Sep 17 00:00:00 2001 From: Yauheni Pasiukevich Date: Wed, 31 Jan 2024 16:25:00 +0100 Subject: [PATCH 1/5] [TS migration] Migrate 'Task' page to TypeScript --- src/ONYXKEYS.ts | 5 +- .../ReportActionItem/TaskPreview.tsx | 9 +- src/libs/OptionsListUtils.ts | 13 +- src/libs/ReportUtils.ts | 2 +- src/libs/actions/Task.ts | 2 +- .../home/report/withReportOrNotFound.tsx | 8 +- ...Modal.js => TaskAssigneeSelectorModal.tsx} | 169 +++++++----------- ...riptionPage.js => TaskDescriptionPage.tsx} | 108 +++++------ ... => TaskShareDestinationSelectorModal.tsx} | 122 ++++++------- src/pages/tasks/TaskTitlePage.js | 150 ---------------- src/pages/tasks/TaskTitlePage.tsx | 141 +++++++++++++++ src/types/onyx/Form.ts | 6 + src/types/onyx/IsSearchingForReports.ts | 3 + src/types/onyx/Policy.ts | 11 +- src/types/onyx/Task.ts | 2 +- src/types/onyx/index.ts | 4 + 16 files changed, 351 insertions(+), 404 deletions(-) rename src/pages/tasks/{TaskAssigneeSelectorModal.js => TaskAssigneeSelectorModal.tsx} (58%) rename src/pages/tasks/{TaskDescriptionPage.js => TaskDescriptionPage.tsx} (55%) rename src/pages/tasks/{TaskShareDestinationSelectorModal.js => TaskShareDestinationSelectorModal.tsx} (58%) delete mode 100644 src/pages/tasks/TaskTitlePage.js create mode 100644 src/pages/tasks/TaskTitlePage.tsx create mode 100644 src/types/onyx/IsSearchingForReports.ts diff --git a/src/ONYXKEYS.ts b/src/ONYXKEYS.ts index 88b740e0e6c8..13381de11a92 100755 --- a/src/ONYXKEYS.ts +++ b/src/ONYXKEYS.ts @@ -435,6 +435,7 @@ type OnyxValues = { [ONYXKEYS.IS_LOADING_REPORT_DATA]: boolean; [ONYXKEYS.IS_TEST_TOOLS_MODAL_OPEN]: boolean; [ONYXKEYS.IS_LOADING_APP]: boolean; + [ONYXKEYS.IS_SEARCHING_FOR_REPORTS]: boolean; [ONYXKEYS.WALLET_TRANSFER]: OnyxTypes.WalletTransfer; [ONYXKEYS.LAST_ACCESSED_WORKSPACE_POLICY_ID]: string; [ONYXKEYS.SHOULD_SHOW_COMPOSE_INPUT]: boolean; @@ -516,8 +517,8 @@ type OnyxValues = { [ONYXKEYS.FORMS.ROOM_SETTINGS_FORM_DRAFT]: OnyxTypes.Form; [ONYXKEYS.FORMS.NEW_TASK_FORM]: OnyxTypes.Form; [ONYXKEYS.FORMS.NEW_TASK_FORM_DRAFT]: OnyxTypes.Form; - [ONYXKEYS.FORMS.EDIT_TASK_FORM]: OnyxTypes.Form; - [ONYXKEYS.FORMS.EDIT_TASK_FORM_DRAFT]: OnyxTypes.Form; + [ONYXKEYS.FORMS.EDIT_TASK_FORM]: OnyxTypes.EditTaskForm; + [ONYXKEYS.FORMS.EDIT_TASK_FORM_DRAFT]: OnyxTypes.EditTaskForm; [ONYXKEYS.FORMS.MONEY_REQUEST_DESCRIPTION_FORM]: OnyxTypes.Form; [ONYXKEYS.FORMS.MONEY_REQUEST_DESCRIPTION_FORM_DRAFT]: OnyxTypes.Form; [ONYXKEYS.FORMS.MONEY_REQUEST_MERCHANT_FORM]: OnyxTypes.Form; diff --git a/src/components/ReportActionItem/TaskPreview.tsx b/src/components/ReportActionItem/TaskPreview.tsx index 16af68d9677c..a754825a139b 100644 --- a/src/components/ReportActionItem/TaskPreview.tsx +++ b/src/components/ReportActionItem/TaskPreview.tsx @@ -28,14 +28,9 @@ import * as Task from '@userActions/Task'; import CONST from '@src/CONST'; import ONYXKEYS from '@src/ONYXKEYS'; import ROUTES from '@src/ROUTES'; -import type {Policy, Report, ReportAction} from '@src/types/onyx'; +import type {Policy, PolicyRole, Report, ReportAction} from '@src/types/onyx'; import {isEmptyObject} from '@src/types/utils/EmptyObject'; -type PolicyRole = { - /** The role of current user */ - role: Task.PolicyValue | undefined; -}; - type TaskPreviewOnyxProps = { /* Onyx Props */ @@ -153,7 +148,7 @@ export default withCurrentUserPersonalDetails( }, rootParentReportpolicy: { key: ({policyID}) => `${ONYXKEYS.COLLECTION.POLICY}${policyID ?? '0'}`, - selector: (policy: Policy | null) => ({role: policy?.role}), + selector: (policy: OnyxEntry) => ({role: policy?.role}), }, })(TaskPreview), ); diff --git a/src/libs/OptionsListUtils.ts b/src/libs/OptionsListUtils.ts index 4ff5fdb2cef9..4681f84e5713 100644 --- a/src/libs/OptionsListUtils.ts +++ b/src/libs/OptionsListUtils.ts @@ -70,7 +70,7 @@ type Hierarchy = Record; selectedOptions?: Option[]; maxRecentReportsToShow?: number; excludeLogins?: string[]; @@ -1401,7 +1401,8 @@ function getOptions( const {parentReportID, parentReportActionID} = report ?? {}; const canGetParentReport = parentReportID && parentReportActionID && allReportActions; const parentReportAction = canGetParentReport ? allReportActions[parentReportID]?.[parentReportActionID] ?? null : null; - const doesReportHaveViolations = betas.includes(CONST.BETAS.VIOLATIONS) && ReportUtils.doesTransactionThreadHaveViolations(report, transactionViolations, parentReportAction); + const doesReportHaveViolations = + (betas?.includes(CONST.BETAS.VIOLATIONS) && ReportUtils.doesTransactionThreadHaveViolations(report, transactionViolations, parentReportAction)) ?? false; return ReportUtils.shouldReportBeInOptionList({ report, @@ -1738,9 +1739,9 @@ function getIOUConfirmationOptionsFromParticipants(participants: Participant[], * Build the options for the New Group view */ function getFilteredOptions( - reports: Record, + reports: OnyxCollection, personalDetails: OnyxEntry, - betas: Beta[] = [], + betas: OnyxEntry = [], searchValue = '', selectedOptions: Array> = [], excludeLogins: string[] = [], @@ -1785,9 +1786,9 @@ function getFilteredOptions( */ function getShareDestinationOptions( - reports: Record, + reports: Record, personalDetails: OnyxEntry, - betas: Beta[] = [], + betas: Beta[] | null = [], searchValue = '', selectedOptions: Array> = [], excludeLogins: string[] = [], diff --git a/src/libs/ReportUtils.ts b/src/libs/ReportUtils.ts index 5f3efcbcdbb0..276af2c68f18 100644 --- a/src/libs/ReportUtils.ts +++ b/src/libs/ReportUtils.ts @@ -3685,7 +3685,7 @@ function shouldReportBeInOptionList({ report: OnyxEntry; currentReportId: string; isInGSDMode: boolean; - betas: Beta[]; + betas: OnyxEntry; policies: OnyxCollection; excludeEmptyChats: boolean; doesReportHaveViolations: boolean; diff --git a/src/libs/actions/Task.ts b/src/libs/actions/Task.ts index 60c05d0cb677..58cd1f503c5f 100644 --- a/src/libs/actions/Task.ts +++ b/src/libs/actions/Task.ts @@ -384,7 +384,7 @@ function editTask(report: OnyxTypes.Report, {title, description}: OnyxTypes.Task const editTaskReportAction = ReportUtils.buildOptimisticEditedTaskReportAction(currentUserEmail); // Sometimes title or description is undefined, so we need to check for that, and we provide it to multiple functions - const reportName = (title ?? report?.reportName).trim(); + const reportName = (title ?? report?.reportName)?.trim() ?? ''; // Description can be unset, so we default to an empty string if so const reportDescription = (description ?? report.description ?? '').trim(); diff --git a/src/pages/home/report/withReportOrNotFound.tsx b/src/pages/home/report/withReportOrNotFound.tsx index 7214e6b6f3ea..046aefdc36fd 100644 --- a/src/pages/home/report/withReportOrNotFound.tsx +++ b/src/pages/home/report/withReportOrNotFound.tsx @@ -7,9 +7,11 @@ import {withOnyx} from 'react-native-onyx'; import FullscreenLoadingIndicator from '@components/FullscreenLoadingIndicator'; import getComponentDisplayName from '@libs/getComponentDisplayName'; import * as ReportUtils from '@libs/ReportUtils'; +import type {CentralPaneNavigatorParamList} from '@navigation/types'; import NotFoundPage from '@pages/ErrorPage/NotFoundPage'; import * as Report from '@userActions/Report'; import ONYXKEYS from '@src/ONYXKEYS'; +import type SCREENS from '@src/SCREENS'; import type * as OnyxTypes from '@src/types/onyx'; import {isEmptyObject} from '@src/types/utils/EmptyObject'; @@ -24,10 +26,12 @@ type WithReportOrNotFoundOnyxProps = { isLoadingReportData: OnyxEntry; }; -type WithReportOrNotFoundProps = WithReportOrNotFoundOnyxProps & { - route: RouteProp<{params: {reportID: string}}>; +type WithReportOrNotFoundRoute = { + route: RouteProp; }; +type WithReportOrNotFoundProps = WithReportOrNotFoundOnyxProps & WithReportOrNotFoundRoute; + export default function ( shouldRequireReportID = true, ): ( diff --git a/src/pages/tasks/TaskAssigneeSelectorModal.js b/src/pages/tasks/TaskAssigneeSelectorModal.tsx similarity index 58% rename from src/pages/tasks/TaskAssigneeSelectorModal.js rename to src/pages/tasks/TaskAssigneeSelectorModal.tsx index f3627a9bd04d..41f0cff47321 100644 --- a/src/pages/tasks/TaskAssigneeSelectorModal.js +++ b/src/pages/tasks/TaskAssigneeSelectorModal.tsx @@ -1,88 +1,57 @@ -/* eslint-disable es/no-optional-chaining */ -import lodashGet from 'lodash/get'; -import PropTypes from 'prop-types'; +import type {StackScreenProps} from '@react-navigation/stack'; +import debounce from 'lodash/debounce'; import React, {useCallback, useEffect, useMemo, useState} from 'react'; import {View} from 'react-native'; import {withOnyx} from 'react-native-onyx'; -import _ from 'underscore'; +import type {OnyxCollection, OnyxEntry} from 'react-native-onyx'; import FullPageNotFoundView from '@components/BlockingViews/FullPageNotFoundView'; import HeaderWithBackButton from '@components/HeaderWithBackButton'; import {usePersonalDetails} from '@components/OnyxProvider'; import OptionsSelector from '@components/OptionsSelector'; import ScreenWrapper from '@components/ScreenWrapper'; import withCurrentUserPersonalDetails from '@components/withCurrentUserPersonalDetails'; -import withLocalize, {withLocalizePropTypes} from '@components/withLocalize'; +import type {WithCurrentUserPersonalDetailsProps} from '@components/withCurrentUserPersonalDetails'; import useAutoFocusInput from '@hooks/useAutoFocusInput'; +import useLocalize from '@hooks/useLocalize'; import useThemeStyles from '@hooks/useThemeStyles'; -import compose from '@libs/compose'; import Navigation from '@libs/Navigation/Navigation'; import * as OptionsListUtils from '@libs/OptionsListUtils'; import * as ReportUtils from '@libs/ReportUtils'; -import reportPropTypes from '@pages/reportPropTypes'; -import * as Task from '@userActions/Task'; +import type {TaskDetailsNavigatorParamList} from '@navigation/types'; +import * as TaskActions from '@userActions/Task'; import CONST from '@src/CONST'; import ONYXKEYS from '@src/ONYXKEYS'; import ROUTES from '@src/ROUTES'; +import type SCREENS from '@src/SCREENS'; +import type {Beta, PersonalDetails, Policy, PolicyRole, Report, Task} from '@src/types/onyx'; -const propTypes = { +type TaskAssigneeSelectorModalOnyxProps = { /** Beta features list */ - betas: PropTypes.arrayOf(PropTypes.string), + betas: OnyxEntry; /** All reports shared with the user */ - reports: PropTypes.objectOf(reportPropTypes), - - /** URL Route params */ - route: PropTypes.shape({ - /** Params from the URL path */ - params: PropTypes.shape({ - /** reportID passed via route: /r/:reportID/title */ - reportID: PropTypes.string, - }), - }), - - // /** The report currently being looked at */ - // report: reportPropTypes.isRequired, - - /** Current user session */ - session: PropTypes.shape({ - email: PropTypes.string.isRequired, - }), + reports: OnyxCollection; /** Grab the Share destination of the Task */ - task: PropTypes.shape({ - /** Share destination of the Task */ - shareDestination: PropTypes.string, - - /** The task report if it's currently being edited */ - report: reportPropTypes, - }), + task: OnyxEntry; /** The policy of root parent report */ - rootParentReportPolicy: PropTypes.shape({ - /** The role of current user */ - role: PropTypes.string, - }), - - ...withLocalizePropTypes, + rootParentReportPolicy: PolicyRole; }; -const defaultProps = { - betas: [], - reports: {}, - session: {}, - route: {}, - task: {}, - rootParentReportPolicy: {}, -}; +type TaskAssigneeSelectorModalProps = TaskAssigneeSelectorModalOnyxProps & + WithCurrentUserPersonalDetailsProps & + StackScreenProps; -function TaskAssigneeSelectorModal(props) { +function TaskAssigneeSelectorModal({betas, reports, session, route, task, rootParentReportPolicy, currentUserPersonalDetails}: TaskAssigneeSelectorModalProps) { const styles = useThemeStyles(); + const {translate} = useLocalize(); const [searchValue, setSearchValue] = useState(''); const [headerMessage, setHeaderMessage] = useState(''); - const [filteredRecentReports, setFilteredRecentReports] = useState([]); - const [filteredPersonalDetails, setFilteredPersonalDetails] = useState([]); - const [filteredUserToInvite, setFilteredUserToInvite] = useState(null); - const [filteredCurrentUserOption, setFilteredCurrentUserOption] = useState(null); + const [filteredRecentReports, setFilteredRecentReports] = useState([]); + const [filteredPersonalDetails, setFilteredPersonalDetails] = useState([]); + const [filteredUserToInvite, setFilteredUserToInvite] = useState(null); + const [filteredCurrentUserOption, setFilteredCurrentUserOption] = useState(null); const [isLoading, setIsLoading] = React.useState(true); const allPersonalDetails = usePersonalDetails() || CONST.EMPTY_OBJECT; @@ -90,9 +59,9 @@ function TaskAssigneeSelectorModal(props) { const updateOptions = useCallback(() => { const {recentReports, personalDetails, userToInvite, currentUserOption} = OptionsListUtils.getFilteredOptions( - props.reports, + reports, allPersonalDetails, - props.betas, + betas, searchValue.trim(), [], CONST.EXPENSIFY_EMAILS, @@ -107,7 +76,7 @@ function TaskAssigneeSelectorModal(props) { true, ); - setHeaderMessage(OptionsListUtils.getHeaderMessage(recentReports?.length + personalDetails?.length !== 0 || currentUserOption, Boolean(userToInvite), searchValue)); + setHeaderMessage(OptionsListUtils.getHeaderMessage(recentReports?.length + personalDetails?.length !== 0 || Boolean(currentUserOption), Boolean(userToInvite), searchValue)); setFilteredUserToInvite(userToInvite); setFilteredRecentReports(recentReports); @@ -116,10 +85,10 @@ function TaskAssigneeSelectorModal(props) { if (isLoading) { setIsLoading(false); } - }, [props, searchValue, allPersonalDetails, isLoading]); + }, [reports, allPersonalDetails, betas, searchValue, isLoading]); useEffect(() => { - const debouncedSearch = _.debounce(updateOptions, 200); + const debouncedSearch = debounce(updateOptions, 200); debouncedSearch(); return () => { debouncedSearch.cancel(); @@ -131,11 +100,11 @@ function TaskAssigneeSelectorModal(props) { }; const report = useMemo(() => { - if (!props.route.params || !props.route.params.reportID) { + if (!route.params?.reportID || !reports) { return null; } - return props.reports[`${ONYXKEYS.COLLECTION.REPORT}${props.route.params.reportID}`]; - }, [props.reports, props.route.params]); + return reports[`${ONYXKEYS.COLLECTION.REPORT}${route.params.reportID}`]; + }, [reports, route.params]); if (report && !ReportUtils.isTaskReport(report)) { Navigation.isNavigationReady().then(() => { @@ -149,7 +118,7 @@ function TaskAssigneeSelectorModal(props) { if (filteredCurrentUserOption) { sectionsList.push({ - title: props.translate('newTaskPage.assignMe'), + title: translate('newTaskPage.assignMe'), data: [filteredCurrentUserOption], shouldShow: true, indexOffset, @@ -158,7 +127,7 @@ function TaskAssigneeSelectorModal(props) { } sectionsList.push({ - title: props.translate('common.recents'), + title: translate('common.recents'), data: filteredRecentReports, shouldShow: filteredRecentReports?.length > 0, indexOffset, @@ -166,7 +135,7 @@ function TaskAssigneeSelectorModal(props) { indexOffset += filteredRecentReports?.length; sectionsList.push({ - title: props.translate('common.contacts'), + title: translate('common.contacts'), data: filteredPersonalDetails, shouldShow: filteredPersonalDetails?.length > 0, indexOffset, @@ -182,10 +151,10 @@ function TaskAssigneeSelectorModal(props) { } return sectionsList; - }, [filteredCurrentUserOption, filteredPersonalDetails, filteredRecentReports, filteredUserToInvite, props]); + }, [filteredCurrentUserOption, filteredPersonalDetails, filteredRecentReports, filteredUserToInvite, translate]); const selectReport = useCallback( - (option) => { + (option: PersonalDetails) => { if (!option) { return; } @@ -193,23 +162,23 @@ function TaskAssigneeSelectorModal(props) { // Check to see if we're editing a task and if so, update the assignee if (report) { if (option.accountID !== report.managerID) { - const assigneeChatReport = Task.setAssigneeValue(option.login, option.accountID, report.reportID, OptionsListUtils.isCurrentUser(option)); + const assigneeChatReport = TaskActions.setAssigneeValue(option?.login ?? '', option.accountID, report.reportID, OptionsListUtils.isCurrentUser(option)); // Pass through the selected assignee - Task.editTaskAssignee(report, props.session.accountID, option.login, option.accountID, assigneeChatReport); + TaskActions.editTaskAssignee(report, session?.accountID ?? 0, option?.login ?? '', option.accountID, assigneeChatReport); } Navigation.dismissModalWithReport(report); // If there's no report, we're creating a new task } else if (option.accountID) { - Task.setAssigneeValue(option.login, option.accountID, props.task.shareDestination, OptionsListUtils.isCurrentUser(option)); + TaskActions.setAssigneeValue(option?.login ?? '', option.accountID, task?.shareDestination ?? '', OptionsListUtils.isCurrentUser(option)); Navigation.goBack(ROUTES.NEW_TASK); } }, - [props.session.accountID, props.task.shareDestination, report], + [session?.accountID, task?.shareDestination, report], ); const isOpen = ReportUtils.isOpenTaskReport(report); - const canModifyTask = Task.canModifyTask(report, props.currentUserPersonalDetails.accountID, lodashGet(props.rootParentReportPolicy, 'role', '')); + const canModifyTask = TaskActions.canModifyTask(report, currentUserPersonalDetails.accountID, rootParentReportPolicy?.role); const isTaskNonEditable = ReportUtils.isTaskReport(report) && (!canModifyTask || !isOpen); return ( @@ -220,18 +189,19 @@ function TaskAssigneeSelectorModal(props) { {({didScreenTransitionEnd, safeAreaPaddingBottomStyle}) => ( (lodashGet(props.route.params, 'reportID') ? Navigation.dismissModal() : Navigation.goBack(ROUTES.NEW_TASK))} + title={translate('task.assignee')} + onBackButtonPress={() => (route.params?.reportID ? Navigation.dismissModal() : Navigation.goBack(ROUTES.NEW_TASK))} /> ({ + reports: { + key: ONYXKEYS.COLLECTION.REPORT, + }, + betas: { + key: ONYXKEYS.BETAS, + }, + task: { + key: ONYXKEYS.TASK, + }, + rootParentReportPolicy: { + key: ({reports, route}) => { + const report = reports?.[`${ONYXKEYS.COLLECTION.REPORT}${route.params?.reportID || '0'}`]; + const rootParentReport = ReportUtils.getRootParentReport(report); + return `${ONYXKEYS.COLLECTION.POLICY}${rootParentReport ? rootParentReport.policyID : '0'}`; }, - betas: { - key: ONYXKEYS.BETAS, - }, - task: { - key: ONYXKEYS.TASK, - }, - session: { - key: ONYXKEYS.SESSION, - }, - }), - withOnyx({ - rootParentReportPolicy: { - key: ({reports, route}) => { - const report = reports[`${ONYXKEYS.COLLECTION.REPORT}${route.params?.reportID || '0'}`]; - const rootParentReport = ReportUtils.getRootParentReport(report); - return `${ONYXKEYS.COLLECTION.POLICY}${rootParentReport ? rootParentReport.policyID : '0'}`; - }, - selector: (policy) => _.pick(policy, ['role']), - }, - }), -)(TaskAssigneeSelectorModal); + selector: (policy: OnyxEntry) => ({role: policy?.role}), + }, +})(TaskAssigneeSelectorModal); + +export default withCurrentUserPersonalDetails(TaskAssigneeSelectorModalWithOnyx); diff --git a/src/pages/tasks/TaskDescriptionPage.js b/src/pages/tasks/TaskDescriptionPage.tsx similarity index 55% rename from src/pages/tasks/TaskDescriptionPage.js rename to src/pages/tasks/TaskDescriptionPage.tsx index 3a6999d4408a..128ad168edf6 100644 --- a/src/pages/tasks/TaskDescriptionPage.js +++ b/src/pages/tasks/TaskDescriptionPage.tsx @@ -1,81 +1,73 @@ import {useFocusEffect} from '@react-navigation/native'; import ExpensiMark from 'expensify-common/lib/ExpensiMark'; -import lodashGet from 'lodash/get'; -import PropTypes from 'prop-types'; import React, {useCallback, useRef} from 'react'; import {View} from 'react-native'; import {withOnyx} from 'react-native-onyx'; -import _ from 'underscore'; +import type {OnyxEntry} from 'react-native-onyx'; import FullPageNotFoundView from '@components/BlockingViews/FullPageNotFoundView'; import FormProvider from '@components/Form/FormProvider'; import InputWrapper from '@components/Form/InputWrapper'; +import type {OnyxFormValuesFields} from '@components/Form/types'; import HeaderWithBackButton from '@components/HeaderWithBackButton'; +import type {AnimatedTextInputRef} from '@components/RNTextInput'; import ScreenWrapper from '@components/ScreenWrapper'; import TextInput from '@components/TextInput'; import withCurrentUserPersonalDetails from '@components/withCurrentUserPersonalDetails'; -import withLocalize, {withLocalizePropTypes} from '@components/withLocalize'; +import type {WithCurrentUserPersonalDetailsProps} from '@components/withCurrentUserPersonalDetails'; +import useLocalize from '@hooks/useLocalize'; import useThemeStyles from '@hooks/useThemeStyles'; import * as Browser from '@libs/Browser'; -import compose from '@libs/compose'; import Navigation from '@libs/Navigation/Navigation'; import * as ReportUtils from '@libs/ReportUtils'; import StringUtils from '@libs/StringUtils'; import updateMultilineInputRange from '@libs/updateMultilineInputRange'; import withReportOrNotFound from '@pages/home/report/withReportOrNotFound'; -import reportPropTypes from '@pages/reportPropTypes'; +import type {WithReportOrNotFoundProps} from '@pages/home/report/withReportOrNotFound'; import * as Task from '@userActions/Task'; import CONST from '@src/CONST'; import ONYXKEYS from '@src/ONYXKEYS'; +import type {Policy, PolicyRole} from '@src/types/onyx'; +import {isEmptyObject} from '@src/types/utils/EmptyObject'; -const propTypes = { - /** The report currently being looked at */ - report: reportPropTypes, - +type TaskDescriptionOnyxPageProps = { /** The policy of parent report */ - rootParentReportPolicy: PropTypes.shape({ - /** The role of current user */ - role: PropTypes.string, - }), - - /* Onyx Props */ - ...withLocalizePropTypes, + rootParentReportPolicy: OnyxEntry; }; -const defaultProps = { - report: {}, - rootParentReportPolicy: {}, -}; +type TaskDescriptionPageProps = WithReportOrNotFoundProps & WithCurrentUserPersonalDetailsProps & TaskDescriptionOnyxPageProps; const parser = new ExpensiMark(); -function TaskDescriptionPage(props) { + +function TaskDescriptionPage({report, rootParentReportPolicy, currentUserPersonalDetails}: TaskDescriptionPageProps) { const styles = useThemeStyles(); + const {translate} = useLocalize(); const validate = useCallback(() => ({}), []); const submit = useCallback( - (values) => { - // props.report.description might contain CRLF from the server - if (StringUtils.normalizeCRLF(values.description) !== StringUtils.normalizeCRLF(props.report.description)) { + (values: OnyxFormValuesFields) => { + // report.description might contain CRLF from the server + if (StringUtils.normalizeCRLF(values.description) !== StringUtils.normalizeCRLF(report?.description) && !isEmptyObject(report)) { // Set the description of the report in the store and then call EditTask API // to update the description of the report on the server - Task.editTask(props.report, {description: values.description}); + Task.editTask(report, {description: values.description}); } - Navigation.dismissModal(props.report.reportID); + Navigation.dismissModal(report?.reportID); }, - [props], + [report], ); - if (!ReportUtils.isTaskReport(props.report)) { + if (!ReportUtils.isTaskReport(report)) { Navigation.isNavigationReady().then(() => { - Navigation.dismissModal(props.report.reportID); + Navigation.dismissModal(report?.reportID); }); } - const inputRef = useRef(null); - const focusTimeoutRef = useRef(null); + const inputRef = useRef(null); + const focusTimeoutRef = useRef(null); - const isOpen = ReportUtils.isOpenTaskReport(props.report); - const canModifyTask = Task.canModifyTask(props.report, props.currentUserPersonalDetails.accountID, lodashGet(props.rootParentReportPolicy, 'role', '')); - const isTaskNonEditable = ReportUtils.isTaskReport(props.report) && (!canModifyTask || !isOpen); + const isOpen = ReportUtils.isOpenTaskReport(report); + const canModifyTask = Task.canModifyTask(report, currentUserPersonalDetails.accountID, rootParentReportPolicy?.role); + const isTaskNonEditable = ReportUtils.isTaskReport(report) && (!canModifyTask || !isOpen); useFocusEffect( useCallback(() => { @@ -100,13 +92,13 @@ function TaskDescriptionPage(props) { testID={TaskDescriptionPage.displayName} > - + @@ -115,14 +107,14 @@ function TaskDescriptionPage(props) { role={CONST.ROLE.PRESENTATION} inputID="description" name="description" - label={props.translate('newTaskPage.descriptionOptional')} - accessibilityLabel={props.translate('newTaskPage.descriptionOptional')} - defaultValue={parser.htmlToMarkdown((props.report && parser.replace(props.report.description)) || '')} - ref={(el) => { - if (!el) { + label={translate('newTaskPage.descriptionOptional')} + accessibilityLabel={translate('newTaskPage.descriptionOptional')} + defaultValue={parser.htmlToMarkdown((report && parser.replace(report?.description ?? '')) ?? '')} + ref={(element: AnimatedTextInputRef) => { + if (!element) { return; } - inputRef.current = el; + inputRef.current = element; updateMultilineInputRange(inputRef.current); }} autoGrowHeight @@ -136,24 +128,18 @@ function TaskDescriptionPage(props) { ); } -TaskDescriptionPage.propTypes = propTypes; -TaskDescriptionPage.defaultProps = defaultProps; TaskDescriptionPage.displayName = 'TaskDescriptionPage'; -export default compose( - withLocalize, - withCurrentUserPersonalDetails, - withReportOrNotFound(), - withOnyx({ - report: { - key: ({route}) => `${ONYXKEYS.COLLECTION.REPORT}${route.params.reportID}`, +const ComponentWithOnyx = withOnyx({ + rootParentReportPolicy: { + key: ({report}) => { + const rootParentReport = ReportUtils.getRootParentReport(report); + return `${ONYXKEYS.COLLECTION.POLICY}${rootParentReport ? rootParentReport.policyID : '0'}`; }, - rootParentReportPolicy: { - key: ({report}) => { - const rootParentReport = ReportUtils.getRootParentReport(report); - return `${ONYXKEYS.COLLECTION.POLICY}${rootParentReport ? rootParentReport.policyID : '0'}`; - }, - selector: (policy) => _.pick(policy, ['role']), - }, - }), -)(TaskDescriptionPage); + selector: (policy: OnyxEntry) => ({role: policy?.role}), + }, +})(TaskDescriptionPage); + +const ComponentWithCurrentUserPersonalDetails = withCurrentUserPersonalDetails(ComponentWithOnyx); + +export default withReportOrNotFound()(ComponentWithCurrentUserPersonalDetails); diff --git a/src/pages/tasks/TaskShareDestinationSelectorModal.js b/src/pages/tasks/TaskShareDestinationSelectorModal.tsx similarity index 58% rename from src/pages/tasks/TaskShareDestinationSelectorModal.js rename to src/pages/tasks/TaskShareDestinationSelectorModal.tsx index 64fd5f50b61f..dde35476d4fd 100644 --- a/src/pages/tasks/TaskShareDestinationSelectorModal.js +++ b/src/pages/tasks/TaskShareDestinationSelectorModal.tsx @@ -1,87 +1,79 @@ -/* eslint-disable es/no-optional-chaining */ -import PropTypes from 'prop-types'; +import debounce from 'lodash/debounce'; import React, {useCallback, useEffect, useMemo, useState} from 'react'; import {View} from 'react-native'; import {withOnyx} from 'react-native-onyx'; -import _ from 'underscore'; +import type {OnyxCollection, OnyxEntry} from 'react-native-onyx'; import HeaderWithBackButton from '@components/HeaderWithBackButton'; import OptionsSelector from '@components/OptionsSelector'; import ScreenWrapper from '@components/ScreenWrapper'; -import withLocalize, {withLocalizePropTypes} from '@components/withLocalize'; import useAutoFocusInput from '@hooks/useAutoFocusInput'; +import useLocalize from '@hooks/useLocalize'; import useNetwork from '@hooks/useNetwork'; import useThemeStyles from '@hooks/useThemeStyles'; -import * as Report from '@libs/actions/Report'; -import compose from '@libs/compose'; +import * as ReportActions from '@libs/actions/Report'; import Navigation from '@libs/Navigation/Navigation'; import * as OptionsListUtils from '@libs/OptionsListUtils'; import * as ReportUtils from '@libs/ReportUtils'; -import personalDetailsPropType from '@pages/personalDetailsPropType'; -import reportPropTypes from '@pages/reportPropTypes'; import * as Task from '@userActions/Task'; import CONST from '@src/CONST'; import ONYXKEYS from '@src/ONYXKEYS'; import ROUTES from '@src/ROUTES'; +import type {Beta, PersonalDetailsList, Report} from '@src/types/onyx'; -const propTypes = { - /* Onyx Props */ +type TaskShareDestinationSelectorModalOnyxProps = { + betas: OnyxEntry; - /** Beta features list */ - betas: PropTypes.arrayOf(PropTypes.string), + personalDetails: OnyxEntry; - /** All of the personal details for everyone */ - personalDetails: PropTypes.objectOf(personalDetailsPropType), + reports: OnyxCollection; - /** All reports shared with the user */ - reports: PropTypes.objectOf(reportPropTypes), - - /** Whether we are searching for reports in the server */ - isSearchingForReports: PropTypes.bool, - - ...withLocalizePropTypes, + isSearchingForReports: OnyxEntry; }; -const defaultProps = { - betas: [], - personalDetails: {}, - reports: {}, - isSearchingForReports: false, -}; +type TaskShareDestinationSelectorModalProps = TaskShareDestinationSelectorModalOnyxProps; -function TaskShareDestinationSelectorModal(props) { +function TaskShareDestinationSelectorModal({reports, isSearchingForReports, personalDetails, betas}: TaskShareDestinationSelectorModalProps) { const styles = useThemeStyles(); + const {translate} = useLocalize(); const [searchValue, setSearchValue] = useState(''); const [headerMessage, setHeaderMessage] = useState(''); - const [filteredRecentReports, setFilteredRecentReports] = useState([]); + const [filteredRecentReports, setFilteredRecentReports] = useState([]); const {inputCallbackRef} = useAutoFocusInput(); - const {isSearchingForReports} = props; const {isOffline} = useNetwork(); const filteredReports = useMemo(() => { - const reports = {}; - _.keys(props.reports).forEach((reportKey) => { + const filtered: Record = {}; + + if (!reports) { + return filtered; + } + + Object.keys(reports).forEach((reportKey) => { if ( - !ReportUtils.canUserPerformWriteAction(props.reports[reportKey]) || - !ReportUtils.canCreateTaskInReport(props.reports[reportKey]) || - ReportUtils.isCanceledTaskReport(props.reports[reportKey]) + !ReportUtils.canUserPerformWriteAction(reports[reportKey]) || + !ReportUtils.canCreateTaskInReport(reports[reportKey]) || + ReportUtils.isCanceledTaskReport(reports[reportKey]) ) { return; } - reports[reportKey] = props.reports[reportKey]; + + filtered[reportKey] = reports[reportKey]; }); - return reports; - }, [props.reports]); + + return filtered; + }, [reports]); + const updateOptions = useCallback(() => { - const {recentReports} = OptionsListUtils.getShareDestinationOptions(filteredReports, props.personalDetails, props.betas, searchValue.trim(), [], CONST.EXPENSIFY_EMAILS, true); + const {recentReports} = OptionsListUtils.getShareDestinationOptions(filteredReports, personalDetails, betas, searchValue.trim(), [], CONST.EXPENSIFY_EMAILS, true); setHeaderMessage(OptionsListUtils.getHeaderMessage(recentReports?.length !== 0, false, searchValue)); setFilteredRecentReports(recentReports); - }, [props, searchValue, filteredReports]); + }, [filteredReports, personalDetails, betas, searchValue]); useEffect(() => { - const debouncedSearch = _.debounce(updateOptions, 150); + const debouncedSearch = debounce(updateOptions, 150); debouncedSearch(); return () => { debouncedSearch.cancel(); @@ -104,7 +96,7 @@ function TaskShareDestinationSelectorModal(props) { return sections; }; - const selectReport = (option) => { + const selectReport = (option: ReportUtils.OptionData) => { if (!option) { return; } @@ -117,7 +109,7 @@ function TaskShareDestinationSelectorModal(props) { // When search term updates we will fetch any reports const setSearchTermAndSearchInServer = useCallback((text = '') => { - Report.searchInServer(text); + ReportActions.searchInServer(text); setSearchValue(text); }, []); @@ -131,11 +123,12 @@ function TaskShareDestinationSelectorModal(props) { {({didScreenTransitionEnd, safeAreaPaddingBottomStyle}) => ( <> Navigation.goBack(ROUTES.NEW_TASK)} /> ({ + reports: { + key: ONYXKEYS.COLLECTION.REPORT, + }, + personalDetails: { + key: ONYXKEYS.PERSONAL_DETAILS_LIST, + }, + betas: { + key: ONYXKEYS.BETAS, + }, + isSearchingForReports: { + key: ONYXKEYS.IS_SEARCHING_FOR_REPORTS, + initWithStoredValues: false, + }, +})(TaskShareDestinationSelectorModal); diff --git a/src/pages/tasks/TaskTitlePage.js b/src/pages/tasks/TaskTitlePage.js deleted file mode 100644 index 9b393a8a2374..000000000000 --- a/src/pages/tasks/TaskTitlePage.js +++ /dev/null @@ -1,150 +0,0 @@ -import lodashGet from 'lodash/get'; -import PropTypes from 'prop-types'; -import React, {useCallback, useRef} from 'react'; -import {View} from 'react-native'; -import {withOnyx} from 'react-native-onyx'; -import _ from 'underscore'; -import FullPageNotFoundView from '@components/BlockingViews/FullPageNotFoundView'; -import FormProvider from '@components/Form/FormProvider'; -import InputWrapper from '@components/Form/InputWrapper'; -import HeaderWithBackButton from '@components/HeaderWithBackButton'; -import ScreenWrapper from '@components/ScreenWrapper'; -import TextInput from '@components/TextInput'; -import withCurrentUserPersonalDetails from '@components/withCurrentUserPersonalDetails'; -import withLocalize, {withLocalizePropTypes} from '@components/withLocalize'; -import useThemeStyles from '@hooks/useThemeStyles'; -import compose from '@libs/compose'; -import Navigation from '@libs/Navigation/Navigation'; -import * as ReportUtils from '@libs/ReportUtils'; -import withReportOrNotFound from '@pages/home/report/withReportOrNotFound'; -import reportPropTypes from '@pages/reportPropTypes'; -import * as Task from '@userActions/Task'; -import CONST from '@src/CONST'; -import ONYXKEYS from '@src/ONYXKEYS'; - -const propTypes = { - /** The report currently being looked at */ - report: reportPropTypes, - - /** The policy of parent report */ - rootParentReportPolicy: PropTypes.shape({ - /** The role of current user */ - role: PropTypes.string, - }), - - /* Onyx Props */ - ...withLocalizePropTypes, -}; - -const defaultProps = { - report: {}, - rootParentReportPolicy: {}, -}; - -function TaskTitlePage(props) { - const styles = useThemeStyles(); - /** - * @param {Object} values - * @param {String} values.title - * @returns {Object} - An object containing the errors for each inputID - */ - const validate = useCallback((values) => { - const errors = {}; - - if (_.isEmpty(values.title)) { - errors.title = 'newTaskPage.pleaseEnterTaskName'; - } - - return errors; - }, []); - - const submit = useCallback( - (values) => { - if (values.title !== props.report.reportName) { - // Set the title of the report in the store and then call EditTask API - // to update the title of the report on the server - Task.editTask(props.report, {title: values.title}); - } - - Navigation.dismissModal(props.report.reportID); - }, - [props], - ); - - if (!ReportUtils.isTaskReport(props.report)) { - Navigation.isNavigationReady().then(() => { - Navigation.dismissModal(props.report.reportID); - }); - } - - const inputRef = useRef(null); - const isOpen = ReportUtils.isOpenTaskReport(props.report); - const canModifyTask = Task.canModifyTask(props.report, props.currentUserPersonalDetails.accountID, lodashGet(props.rootParentReportPolicy, 'role', '')); - const isTaskNonEditable = ReportUtils.isTaskReport(props.report) && (!canModifyTask || !isOpen); - - return ( - inputRef.current && inputRef.current.focus()} - shouldEnableMaxHeight - testID={TaskTitlePage.displayName} - > - {({didScreenTransitionEnd}) => ( - - - - - { - if (!el) { - return; - } - if (!inputRef.current && didScreenTransitionEnd) { - el.focus(); - } - inputRef.current = el; - }} - /> - - - - )} - - ); -} - -TaskTitlePage.propTypes = propTypes; -TaskTitlePage.defaultProps = defaultProps; -TaskTitlePage.displayName = 'TaskTitlePage'; - -export default compose( - withLocalize, - withCurrentUserPersonalDetails, - withReportOrNotFound(), - withOnyx({ - report: { - key: ({route}) => `${ONYXKEYS.COLLECTION.REPORT}${route.params.reportID}`, - }, - rootParentReportPolicy: { - key: ({report}) => { - const rootParentReport = ReportUtils.getRootParentReport(report); - return `${ONYXKEYS.COLLECTION.POLICY}${rootParentReport ? rootParentReport.policyID : '0'}`; - }, - selector: (policy) => _.pick(policy, ['role']), - }, - }), -)(TaskTitlePage); diff --git a/src/pages/tasks/TaskTitlePage.tsx b/src/pages/tasks/TaskTitlePage.tsx new file mode 100644 index 000000000000..9157d8ef4932 --- /dev/null +++ b/src/pages/tasks/TaskTitlePage.tsx @@ -0,0 +1,141 @@ +import React, {useCallback, useRef} from 'react'; +import {View} from 'react-native'; +import {withOnyx} from 'react-native-onyx'; +import type {OnyxEntry} from 'react-native-onyx'; +import FullPageNotFoundView from '@components/BlockingViews/FullPageNotFoundView'; +import FormProvider from '@components/Form/FormProvider'; +import InputWrapper from '@components/Form/InputWrapper'; +import type {OnyxFormValuesFields} from '@components/Form/types'; +import HeaderWithBackButton from '@components/HeaderWithBackButton'; +import type {AnimatedTextInputRef} from '@components/RNTextInput'; +import ScreenWrapper from '@components/ScreenWrapper'; +import TextInput from '@components/TextInput'; +import withCurrentUserPersonalDetails from '@components/withCurrentUserPersonalDetails'; +import type {WithCurrentUserPersonalDetailsProps} from '@components/withCurrentUserPersonalDetails'; +import useLocalize from '@hooks/useLocalize'; +import useThemeStyles from '@hooks/useThemeStyles'; +import Navigation from '@libs/Navigation/Navigation'; +import * as ReportUtils from '@libs/ReportUtils'; +import withReportOrNotFound from '@pages/home/report/withReportOrNotFound'; +import type {WithReportOrNotFoundProps} from '@pages/home/report/withReportOrNotFound'; +import * as Task from '@userActions/Task'; +import CONST from '@src/CONST'; +import ONYXKEYS from '@src/ONYXKEYS'; +import type {Policy, PolicyRole} from '@src/types/onyx'; +import {isEmptyObject} from '@src/types/utils/EmptyObject'; + +type TaskTitlePageOnyxProps = { + /** The policy of parent report */ + rootParentReportPolicy: OnyxEntry; +}; + +type TaskTitlePageProps = WithReportOrNotFoundProps & WithCurrentUserPersonalDetailsProps & TaskTitlePageOnyxProps; + +type Values = { + title: string; +}; + +type Errors = { + title?: string; +}; + +function TaskTitlePage({report, rootParentReportPolicy, currentUserPersonalDetails}: TaskTitlePageProps) { + const styles = useThemeStyles(); + const {translate} = useLocalize(); + + const validate = useCallback(({title}: Values): Errors => { + const errors: Errors = {}; + + if (!title) { + errors.title = 'newTaskPage.pleaseEnterTaskName'; + } + + return errors; + }, []); + + const submit = useCallback( + (values: OnyxFormValuesFields) => { + if (values.title !== report?.reportName && !isEmptyObject(report)) { + // Set the title of the report in the store and then call EditTask API + // to update the title of the report on the server + Task.editTask(report, {title: values.title}); + } + + Navigation.dismissModal(report?.reportID); + }, + [report], + ); + + if (!ReportUtils.isTaskReport(report)) { + Navigation.isNavigationReady().then(() => { + Navigation.dismissModal(report?.reportID); + }); + } + + const inputRef = useRef(null); + const isOpen = ReportUtils.isOpenTaskReport(report); + const canModifyTask = Task.canModifyTask(report, currentUserPersonalDetails.accountID, rootParentReportPolicy?.role); + const isTaskNonEditable = ReportUtils.isTaskReport(report) && (!canModifyTask || !isOpen); + + return ( + { + inputRef?.current?.focus(); + }} + shouldEnableMaxHeight + testID={TaskTitlePage.displayName} + > + {({didScreenTransitionEnd}) => ( + + + + + { + if (!element) { + return; + } + if (!inputRef.current && didScreenTransitionEnd) { + element.focus(); + } + inputRef.current = element; + }} + /> + + + + )} + + ); +} + +TaskTitlePage.displayName = 'TaskTitlePage'; + +const ComponentWithOnyx = withOnyx({ + rootParentReportPolicy: { + key: ({report}) => { + const rootParentReport = ReportUtils.getRootParentReport(report); + return `${ONYXKEYS.COLLECTION.POLICY}${rootParentReport ? rootParentReport.policyID : '0'}`; + }, + selector: (policy: OnyxEntry) => ({role: policy?.role}), + }, +})(TaskTitlePage); + +const ComponentWithCurrentUserPersonalDetails = withCurrentUserPersonalDetails(ComponentWithOnyx); + +export default withReportOrNotFound()(ComponentWithCurrentUserPersonalDetails); diff --git a/src/types/onyx/Form.ts b/src/types/onyx/Form.ts index 3235f340e723..4ca81ae6542e 100644 --- a/src/types/onyx/Form.ts +++ b/src/types/onyx/Form.ts @@ -63,6 +63,11 @@ type WorkspaceSettingsForm = Form<{ type ReportFieldEditForm = Form>; +type EditTaskForm = Form<{ + title: string; + description: string; +}>; + export default Form; export type { @@ -70,6 +75,7 @@ export type { DateOfBirthForm, PrivateNotesForm, DisplayNameForm, + EditTaskForm, FormValueType, NewRoomForm, BaseForm, diff --git a/src/types/onyx/IsSearchingForReports.ts b/src/types/onyx/IsSearchingForReports.ts new file mode 100644 index 000000000000..1af84277ca1f --- /dev/null +++ b/src/types/onyx/IsSearchingForReports.ts @@ -0,0 +1,3 @@ +type IsSearchingForReports = boolean; + +export default IsSearchingForReports; diff --git a/src/types/onyx/Policy.ts b/src/types/onyx/Policy.ts index f55b3b797bf0..a618ccc0d2cd 100644 --- a/src/types/onyx/Policy.ts +++ b/src/types/onyx/Policy.ts @@ -33,6 +33,8 @@ type DisabledFields = { type AutoReportingOffset = number | ValueOf; +type PolicyValue = ValueOf; + type Policy = { /** The ID of the policy */ id: string; @@ -41,7 +43,7 @@ type Policy = { name: string; /** The current user's role in the policy */ - role: ValueOf; + role: PolicyValue; /** The policy type */ type: ValueOf; @@ -163,6 +165,11 @@ type Policy = { chatReportIDAnnounce?: number; }; +type PolicyRole = { + /** The role of current user */ + role?: PolicyValue; +}; + export default Policy; -export type {Unit, CustomUnit}; +export type {Unit, CustomUnit, PolicyRole}; diff --git a/src/types/onyx/Task.ts b/src/types/onyx/Task.ts index 2aec786d77ad..50a871b7ea07 100644 --- a/src/types/onyx/Task.ts +++ b/src/types/onyx/Task.ts @@ -2,7 +2,7 @@ import type Report from './Report'; type Task = { /** Title of the Task */ - title: string; + title?: string; /** Description of the Task */ description?: string; diff --git a/src/types/onyx/index.ts b/src/types/onyx/index.ts index 939793b3b4a8..7cd8251761df 100644 --- a/src/types/onyx/index.ts +++ b/src/types/onyx/index.ts @@ -13,6 +13,7 @@ import type { AddDebitCardForm, DateOfBirthForm, DisplayNameForm, + EditTaskForm, IKnowATeacherForm, IntroSchoolPrincipalForm, NewRoomForm, @@ -38,6 +39,7 @@ import type {PersonalDetailsList} from './PersonalDetails'; import type PersonalDetails from './PersonalDetails'; import type PlaidData from './PlaidData'; import type Policy from './Policy'; +import type {PolicyRole} from './Policy'; import type {PolicyCategories, PolicyCategory} from './PolicyCategory'; import type {PolicyMembers} from './PolicyMember'; import type PolicyMember from './PolicyMember'; @@ -94,6 +96,7 @@ export type { CustomStatusDraft, DateOfBirthForm, Download, + EditTaskForm, Form, FrequentlyUsedEmoji, Fund, @@ -113,6 +116,7 @@ export type { PersonalDetailsList, PlaidData, Policy, + PolicyRole, PolicyCategories, PolicyCategory, PolicyMember, From 834460892615f54decd9dc5fbbcad11971cffde9 Mon Sep 17 00:00:00 2001 From: Yauheni Pasiukevich Date: Tue, 13 Feb 2024 10:29:27 +0100 Subject: [PATCH 2/5] update types --- .../ReportActionItem/TaskPreview.tsx | 2 +- src/pages/tasks/TaskAssigneeSelectorModal.tsx | 2 +- src/pages/tasks/TaskTitlePage.tsx | 26 +++---------------- src/types/onyx/Policy.ts | 11 ++------ src/types/onyx/index.ts | 2 -- 5 files changed, 8 insertions(+), 35 deletions(-) diff --git a/src/components/ReportActionItem/TaskPreview.tsx b/src/components/ReportActionItem/TaskPreview.tsx index bfc897a01ee4..e23e1354652b 100644 --- a/src/components/ReportActionItem/TaskPreview.tsx +++ b/src/components/ReportActionItem/TaskPreview.tsx @@ -124,6 +124,6 @@ export default withCurrentUserPersonalDetails( withOnyx({ taskReport: { key: ({taskReportID}) => `${ONYXKEYS.COLLECTION.REPORT}${taskReportID}`, - } + }, })(TaskPreview), ); diff --git a/src/pages/tasks/TaskAssigneeSelectorModal.tsx b/src/pages/tasks/TaskAssigneeSelectorModal.tsx index 745118e1fb3c..0d724e78b7b2 100644 --- a/src/pages/tasks/TaskAssigneeSelectorModal.tsx +++ b/src/pages/tasks/TaskAssigneeSelectorModal.tsx @@ -221,7 +221,7 @@ const TaskAssigneeSelectorModalWithOnyx = withOnyx; -}; - -type TaskTitlePageProps = WithReportOrNotFoundProps & WithCurrentUserPersonalDetailsProps & TaskTitlePageOnyxProps; +type TaskTitlePageProps = WithReportOrNotFoundProps & WithCurrentUserPersonalDetailsProps; type Values = { title: string; @@ -39,7 +31,7 @@ type Errors = { title?: string; }; -function TaskTitlePage({report, rootParentReportPolicy, currentUserPersonalDetails}: TaskTitlePageProps) { +function TaskTitlePage({report, currentUserPersonalDetails}: TaskTitlePageProps) { const styles = useThemeStyles(); const {translate} = useLocalize(); @@ -74,7 +66,7 @@ function TaskTitlePage({report, rootParentReportPolicy, currentUserPersonalDetai const inputRef = useRef(null); const isOpen = ReportUtils.isOpenTaskReport(report); - const canModifyTask = Task.canModifyTask(report, currentUserPersonalDetails.accountID, rootParentReportPolicy?.role); + const canModifyTask = Task.canModifyTask(report, currentUserPersonalDetails.accountID); const isTaskNonEditable = ReportUtils.isTaskReport(report) && (!canModifyTask || !isOpen); return ( @@ -126,16 +118,6 @@ function TaskTitlePage({report, rootParentReportPolicy, currentUserPersonalDetai TaskTitlePage.displayName = 'TaskTitlePage'; -const ComponentWithOnyx = withOnyx({ - rootParentReportPolicy: { - key: ({report}) => { - const rootParentReport = ReportUtils.getRootParentReport(report); - return `${ONYXKEYS.COLLECTION.POLICY}${rootParentReport ? rootParentReport.policyID : '0'}`; - }, - selector: (policy: OnyxEntry) => ({role: policy?.role}), - }, -})(TaskTitlePage); - -const ComponentWithCurrentUserPersonalDetails = withCurrentUserPersonalDetails(ComponentWithOnyx); +const ComponentWithCurrentUserPersonalDetails = withCurrentUserPersonalDetails(TaskTitlePage); export default withReportOrNotFound()(ComponentWithCurrentUserPersonalDetails); diff --git a/src/types/onyx/Policy.ts b/src/types/onyx/Policy.ts index 62a3f78b7a33..d5ed5dd36aba 100644 --- a/src/types/onyx/Policy.ts +++ b/src/types/onyx/Policy.ts @@ -45,8 +45,6 @@ type Connection = { type AutoReportingOffset = number | ValueOf; -type PolicyValue = ValueOf; - type Policy = { /** The ID of the policy */ id: string; @@ -55,7 +53,7 @@ type Policy = { name: string; /** The current user's role in the policy */ - role: PolicyValue; + role: ValueOf; /** The policy type */ type: ValueOf; @@ -177,11 +175,6 @@ type Policy = { connections?: Record; }; -type PolicyRole = { - /** The role of current user */ - role?: PolicyValue; -}; - export default Policy; -export type {Unit, CustomUnit, PolicyRole}; +export type {Unit, CustomUnit}; diff --git a/src/types/onyx/index.ts b/src/types/onyx/index.ts index 04a52a25be5f..3d787950ce4b 100644 --- a/src/types/onyx/index.ts +++ b/src/types/onyx/index.ts @@ -45,7 +45,6 @@ import type {PersonalDetailsList} from './PersonalDetails'; import type PersonalDetails from './PersonalDetails'; import type PlaidData from './PlaidData'; import type Policy from './Policy'; -import type {PolicyRole} from './Policy'; import type {PolicyCategories, PolicyCategory} from './PolicyCategory'; import type {PolicyMembers} from './PolicyMember'; import type PolicyMember from './PolicyMember'; @@ -125,7 +124,6 @@ export type { PersonalDetailsList, PlaidData, Policy, - PolicyRole, PolicyCategories, PolicyCategory, PolicyMember, From 6c382322eb4d08836af6539c531fba73cd7f8212 Mon Sep 17 00:00:00 2001 From: Yauheni Pasiukevich Date: Thu, 15 Feb 2024 14:46:18 +0100 Subject: [PATCH 3/5] fix types --- src/ONYXKEYS.ts | 7 ++--- src/pages/tasks/TaskAssigneeSelectorModal.tsx | 30 ++++++++++--------- src/pages/tasks/TaskDescriptionPage.tsx | 18 ++++------- src/pages/tasks/TaskTitlePage.tsx | 18 ++++------- src/types/form/EditTaskForm.ts | 4 +-- 5 files changed, 31 insertions(+), 46 deletions(-) diff --git a/src/ONYXKEYS.ts b/src/ONYXKEYS.ts index 5caa0d40d2d0..982bfa623b4f 100755 --- a/src/ONYXKEYS.ts +++ b/src/ONYXKEYS.ts @@ -1,12 +1,11 @@ -import type { OnyxEntry } from 'react-native-onyx'; -import type { ValueOf } from 'type-fest'; +import type {OnyxEntry} from 'react-native-onyx'; +import type {ValueOf} from 'type-fest'; import type CONST from './CONST'; import type * as FormTypes from './types/form'; import type * as OnyxTypes from './types/onyx'; import type AssertTypesEqual from './types/utils/AssertTypesEqual'; import type DeepValueOf from './types/utils/DeepValueOf'; - /** * This is a file containing constants for all the top level keys in our store */ @@ -396,8 +395,8 @@ type OnyxFormValuesMapping = { [ONYXKEYS.FORMS.HOME_ADDRESS_FORM]: FormTypes.Form; [ONYXKEYS.FORMS.NEW_ROOM_FORM]: FormTypes.NewRoomForm; [ONYXKEYS.FORMS.ROOM_SETTINGS_FORM]: FormTypes.Form; + [ONYXKEYS.FORMS.NEW_TASK_FORM]: FormTypes.Form; [ONYXKEYS.FORMS.EDIT_TASK_FORM]: FormTypes.EditTaskForm; - [ONYXKEYS.FORMS.EDIT_TASK_FORM_DRAFT]: FormTypes.EditTaskForm; [ONYXKEYS.FORMS.MONEY_REQUEST_DESCRIPTION_FORM]: FormTypes.Form; [ONYXKEYS.FORMS.MONEY_REQUEST_MERCHANT_FORM]: FormTypes.Form; [ONYXKEYS.FORMS.MONEY_REQUEST_AMOUNT_FORM]: FormTypes.Form; diff --git a/src/pages/tasks/TaskAssigneeSelectorModal.tsx b/src/pages/tasks/TaskAssigneeSelectorModal.tsx index eba55e357075..3ddd5a26d82b 100644 --- a/src/pages/tasks/TaskAssigneeSelectorModal.tsx +++ b/src/pages/tasks/TaskAssigneeSelectorModal.tsx @@ -1,18 +1,18 @@ /* eslint-disable es/no-optional-chaining */ -import type {StackScreenProps} from '@react-navigation/stack'; -import React, {useCallback, useState, useMemo} from 'react'; +import type {RouteProp} from '@react-navigation/native'; +import {useRoute} from '@react-navigation/native'; +import React, {useCallback, useMemo, useState} from 'react'; import {View} from 'react-native'; import {withOnyx} from 'react-native-onyx'; import type {OnyxCollection, OnyxEntry} from 'react-native-onyx'; -import type { RouteProp} from '@react-navigation/native'; -import {useRoute} from '@react-navigation/native'; import FullPageNotFoundView from '@components/BlockingViews/FullPageNotFoundView'; import HeaderWithBackButton from '@components/HeaderWithBackButton'; import {useBetas, usePersonalDetails, useSession} from '@components/OnyxProvider'; import ScreenWrapper from '@components/ScreenWrapper'; +import SelectionList from '@components/SelectionList'; +import type {RadioItem, User} from '@components/SelectionList/types'; import withCurrentUserPersonalDetails from '@components/withCurrentUserPersonalDetails'; import type {WithCurrentUserPersonalDetailsProps} from '@components/withCurrentUserPersonalDetails'; -import SelectionList from '@components/SelectionList'; import useCurrentUserPersonalDetails from '@hooks/useCurrentUserPersonalDetails'; import useDebouncedState from '@hooks/useDebouncedState'; import useLocalize from '@hooks/useLocalize'; @@ -26,7 +26,7 @@ import CONST from '@src/CONST'; import ONYXKEYS from '@src/ONYXKEYS'; import ROUTES from '@src/ROUTES'; import type SCREENS from '@src/SCREENS'; -import type {PersonalDetails, Report, Task} from '@src/types/onyx'; +import type {Report, Task} from '@src/types/onyx'; type TaskAssigneeSelectorModalOnyxProps = { /** All reports shared with the user */ @@ -40,9 +40,7 @@ type UseOptionsProps = { reports: OnyxCollection; }; -type TaskAssigneeSelectorModalProps = TaskAssigneeSelectorModalOnyxProps & - WithCurrentUserPersonalDetailsProps & - StackScreenProps; +type TaskAssigneeSelectorModalProps = TaskAssigneeSelectorModalOnyxProps & WithCurrentUserPersonalDetailsProps; function useOptions({reports}: UseOptionsProps) { const allPersonalDetails = usePersonalDetails() || CONST.EMPTY_OBJECT; @@ -69,7 +67,11 @@ function useOptions({reports}: UseOptionsProps) { true, ); - const headerMessage = OptionsListUtils.getHeaderMessage(recentReports?.length + personalDetails?.length !== 0 || Boolean(currentUserOption), Boolean(userToInvite), debouncedSearchValue); + const headerMessage = OptionsListUtils.getHeaderMessage( + recentReports?.length + personalDetails?.length !== 0 || Boolean(currentUserOption), + Boolean(userToInvite), + debouncedSearchValue, + ); if (isLoading) { setIsLoading(false); @@ -93,7 +95,7 @@ function TaskAssigneeSelectorModal({reports, task}: TaskAssigneeSelectorModalPro const {translate} = useLocalize(); const session = useSession(); const currentUserPersonalDetails = useCurrentUserPersonalDetails(); - const {userToInvite, recentReports, personalDetails, currentUserOption, isLoading, searchValue, setSearchValue, headerMessage} = useOptions({reports, task}); + const {userToInvite, recentReports, personalDetails, currentUserOption, isLoading, searchValue, setSearchValue, headerMessage} = useOptions({reports}); const onChangeText = (newSearchTerm = '') => { setSearchValue(newSearchTerm); @@ -153,7 +155,7 @@ function TaskAssigneeSelectorModal({reports, task}: TaskAssigneeSelectorModalPro }, [currentUserOption, personalDetails, recentReports, translate, userToInvite]); const selectReport = useCallback( - (option: PersonalDetails) => { + (option: User & RadioItem) => { if (!option) { return; } @@ -161,7 +163,7 @@ function TaskAssigneeSelectorModal({reports, task}: TaskAssigneeSelectorModalPro // Check to see if we're editing a task and if so, update the assignee if (report) { if (option.accountID !== report.managerID) { - const assigneeChatReport = TaskActions.setAssigneeValue(option?.login ?? '', option.accountID, report.reportID, OptionsListUtils.isCurrentUser(option)); + const assigneeChatReport = TaskActions.setAssigneeValue(option?.login ?? '', option?.accountID ?? 0, report.reportID, OptionsListUtils.isCurrentUser(option)); // Pass through the selected assignee TaskActions.editTaskAssignee(report, session?.accountID ?? 0, option?.login ?? '', option.accountID, assigneeChatReport); @@ -176,7 +178,7 @@ function TaskAssigneeSelectorModal({reports, task}: TaskAssigneeSelectorModalPro [session?.accountID, task?.shareDestination, report], ); - const handleBackButtonPress = useCallback(() => (lodashGet(route.params, 'reportID') ? Navigation.dismissModal() : Navigation.goBack(ROUTES.NEW_TASK)), [route.params]); + const handleBackButtonPress = useCallback(() => (route.params?.reportID ? Navigation.dismissModal() : Navigation.goBack(ROUTES.NEW_TASK)), [route.params]); const isOpen = ReportUtils.isOpenTaskReport(report); const canModifyTask = TaskActions.canModifyTask(report, currentUserPersonalDetails.accountID); diff --git a/src/pages/tasks/TaskDescriptionPage.tsx b/src/pages/tasks/TaskDescriptionPage.tsx index 5bbd3ef49166..e08d6380bb18 100644 --- a/src/pages/tasks/TaskDescriptionPage.tsx +++ b/src/pages/tasks/TaskDescriptionPage.tsx @@ -5,7 +5,7 @@ import {View} from 'react-native'; import FullPageNotFoundView from '@components/BlockingViews/FullPageNotFoundView'; import FormProvider from '@components/Form/FormProvider'; import InputWrapper from '@components/Form/InputWrapper'; -import type {OnyxFormValuesFields} from '@components/Form/types'; +import type {FormInputErrors, FormOnyxValues} from '@components/Form/types'; import HeaderWithBackButton from '@components/HeaderWithBackButton'; import type {AnimatedTextInputRef} from '@components/RNTextInput'; import ScreenWrapper from '@components/ScreenWrapper'; @@ -24,29 +24,21 @@ import type {WithReportOrNotFoundProps} from '@pages/home/report/withReportOrNot import * as Task from '@userActions/Task'; import CONST from '@src/CONST'; import ONYXKEYS from '@src/ONYXKEYS'; -import {isEmptyObject} from '@src/types/utils/EmptyObject'; import INPUT_IDS from '@src/types/form/EditTaskForm'; +import {isEmptyObject} from '@src/types/utils/EmptyObject'; type TaskDescriptionPageProps = WithReportOrNotFoundProps & WithCurrentUserPersonalDetailsProps; -type Values = { - description: string; -}; - -type Errors = { - description?: string; -}; - const parser = new ExpensiMark(); function TaskDescriptionPage({report, currentUserPersonalDetails}: TaskDescriptionPageProps) { const styles = useThemeStyles(); const {translate} = useLocalize(); - const validate = useCallback((values: Values): Errors => { + const validate = useCallback((values: FormOnyxValues): FormInputErrors => { const errors = {}; - if (values.description.length > CONST.DESCRIPTION_LIMIT) { + if (values?.description && values.description?.length > CONST.DESCRIPTION_LIMIT) { ErrorUtils.addErrorMessage(errors, 'description', ['common.error.characterLimitExceedCounter', {length: values.description.length, limit: CONST.DESCRIPTION_LIMIT}]); } @@ -54,7 +46,7 @@ function TaskDescriptionPage({report, currentUserPersonalDetails}: TaskDescripti }, []); const submit = useCallback( - (values: OnyxFormValuesFields) => { + (values: FormOnyxValues) => { // report.description might contain CRLF from the server if (StringUtils.normalizeCRLF(values.description) !== StringUtils.normalizeCRLF(report?.description) && !isEmptyObject(report)) { // Set the description of the report in the store and then call EditTask API diff --git a/src/pages/tasks/TaskTitlePage.tsx b/src/pages/tasks/TaskTitlePage.tsx index 9f51b9fcb5e9..009983beac3e 100644 --- a/src/pages/tasks/TaskTitlePage.tsx +++ b/src/pages/tasks/TaskTitlePage.tsx @@ -3,6 +3,7 @@ import {View} from 'react-native'; import FullPageNotFoundView from '@components/BlockingViews/FullPageNotFoundView'; 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 type {AnimatedTextInputRef} from '@components/RNTextInput'; import ScreenWrapper from '@components/ScreenWrapper'; @@ -16,28 +17,19 @@ import * as ReportUtils from '@libs/ReportUtils'; import withReportOrNotFound from '@pages/home/report/withReportOrNotFound'; import type {WithReportOrNotFoundProps} from '@pages/home/report/withReportOrNotFound'; import * as Task from '@userActions/Task'; -import type {FormOnyxValues} from '@components/Form/types'; import CONST from '@src/CONST'; import ONYXKEYS from '@src/ONYXKEYS'; -import {isEmptyObject} from '@src/types/utils/EmptyObject'; import INPUT_IDS from '@src/types/form/EditTaskForm'; +import {isEmptyObject} from '@src/types/utils/EmptyObject'; type TaskTitlePageProps = WithReportOrNotFoundProps & WithCurrentUserPersonalDetailsProps; -type Values = { - title: string; -}; - -type Errors = { - title?: string; -}; - function TaskTitlePage({report, currentUserPersonalDetails}: TaskTitlePageProps) { const styles = useThemeStyles(); const {translate} = useLocalize(); - const validate = useCallback(({title}: Values): Errors => { - const errors: Errors = {}; + const validate = useCallback(({title}: FormOnyxValues): FormInputErrors => { + const errors: FormInputErrors = {}; if (!title) { errors.title = 'newTaskPage.pleaseEnterTaskName'; @@ -98,7 +90,7 @@ function TaskTitlePage({report, currentUserPersonalDetails}: TaskTitlePageProps) name={INPUT_IDS.TITLE} label={translate('task.title')} accessibilityLabel={translate('task.title')} - defaultValue={(report?.reportName) ?? ''} + defaultValue={report?.reportName ?? ''} ref={(element: AnimatedTextInputRef) => { if (!element) { return; diff --git a/src/types/form/EditTaskForm.ts b/src/types/form/EditTaskForm.ts index 05de0310c784..e74fef6e4e86 100644 --- a/src/types/form/EditTaskForm.ts +++ b/src/types/form/EditTaskForm.ts @@ -6,8 +6,8 @@ const INPUT_IDS = { } as const; type EditTaskForm = Form<{ - [INPUT_IDS.TITLE]: string; - [INPUT_IDS.DESCRIPTION]: string; + [INPUT_IDS.TITLE]?: string; + [INPUT_IDS.DESCRIPTION]?: string; }>; export type {EditTaskForm}; From d21a39f8af70eb41b32172a73cbe469dce46d853 Mon Sep 17 00:00:00 2001 From: Yauheni Pasiukevich Date: Fri, 23 Feb 2024 13:20:43 +0100 Subject: [PATCH 4/5] address comments --- src/libs/OptionsListUtils.ts | 2 +- src/libs/actions/Task.ts | 10 ++++- src/pages/tasks/TaskAssigneeSelectorModal.tsx | 44 ++++++++++++++----- src/types/onyx/IsSearchingForReports.ts | 3 -- 4 files changed, 41 insertions(+), 18 deletions(-) delete mode 100644 src/types/onyx/IsSearchingForReports.ts diff --git a/src/libs/OptionsListUtils.ts b/src/libs/OptionsListUtils.ts index 299f50e0176e..7ca3a63e5c92 100644 --- a/src/libs/OptionsListUtils.ts +++ b/src/libs/OptionsListUtils.ts @@ -1834,7 +1834,7 @@ function getFilteredOptions( function getShareDestinationOptions( reports: Record, personalDetails: OnyxEntry, - betas: Beta[] | null = [], + betas: OnyxEntry = [], searchValue = '', selectedOptions: Array> = [], excludeLogins: string[] = [], diff --git a/src/libs/actions/Task.ts b/src/libs/actions/Task.ts index 28c6390e1851..b0f56fe553a7 100644 --- a/src/libs/actions/Task.ts +++ b/src/libs/actions/Task.ts @@ -451,7 +451,13 @@ function editTask(report: OnyxTypes.Report, {title, description}: OnyxTypes.Task API.write(WRITE_COMMANDS.EDIT_TASK, parameters, {optimisticData, successData, failureData}); } -function editTaskAssignee(report: OnyxTypes.Report, ownerAccountID: number, assigneeEmail: string, assigneeAccountID = 0, assigneeChatReport: OnyxEntry = null) { +function editTaskAssignee( + report: OnyxTypes.Report, + ownerAccountID: number, + assigneeEmail: string, + assigneeAccountID: number | null = 0, + assigneeChatReport: OnyxEntry = null, +) { // Create the EditedReportAction on the task const editTaskReportAction = ReportUtils.buildOptimisticEditedTaskReportAction(currentUserEmail); const reportName = report.reportName?.trim(); @@ -460,7 +466,7 @@ function editTaskAssignee(report: OnyxTypes.Report, ownerAccountID: number, assi const assigneeChatReportID = assigneeChatReport ? assigneeChatReport.reportID : '0'; const optimisticReport: OptimisticReport = { reportName, - managerID: assigneeAccountID || report.managerID, + managerID: assigneeAccountID ?? report.managerID, pendingFields: { ...(assigneeAccountID && {managerID: CONST.RED_BRICK_ROAD_PENDING_ACTION.UPDATE}), }, diff --git a/src/pages/tasks/TaskAssigneeSelectorModal.tsx b/src/pages/tasks/TaskAssigneeSelectorModal.tsx index 88a8d4c21512..f9365ba367bc 100644 --- a/src/pages/tasks/TaskAssigneeSelectorModal.tsx +++ b/src/pages/tasks/TaskAssigneeSelectorModal.tsx @@ -10,10 +10,10 @@ import HeaderWithBackButton from '@components/HeaderWithBackButton'; import {useBetas, usePersonalDetails, useSession} from '@components/OnyxProvider'; import ScreenWrapper from '@components/ScreenWrapper'; import SelectionList from '@components/SelectionList'; -import type {RadioItem, User} from '@components/SelectionList/types'; +import type {ListItem} from '@components/SelectionList/types'; +import UserListItem from '@components/SelectionList/UserListItem'; import withCurrentUserPersonalDetails from '@components/withCurrentUserPersonalDetails'; import type {WithCurrentUserPersonalDetailsProps} from '@components/withCurrentUserPersonalDetails'; -import UserListItem from '@components/SelectionList/UserListItem'; import useCurrentUserPersonalDetails from '@hooks/useCurrentUserPersonalDetails'; import useDebouncedState from '@hooks/useDebouncedState'; import useLocalize from '@hooks/useLocalize'; @@ -37,13 +37,13 @@ type TaskAssigneeSelectorModalOnyxProps = { task: OnyxEntry; }; -type UseOptionsProps = { +type UseOptions = { reports: OnyxCollection; }; type TaskAssigneeSelectorModalProps = TaskAssigneeSelectorModalOnyxProps & WithCurrentUserPersonalDetailsProps; -function useOptions({reports}: UseOptionsProps) { +function useOptions({reports}: UseOptions) { const allPersonalDetails = usePersonalDetails() || CONST.EMPTY_OBJECT; const betas = useBetas(); const [isLoading, setIsLoading] = useState(true); @@ -152,11 +152,22 @@ function TaskAssigneeSelectorModal({reports, task}: TaskAssigneeSelectorModalPro }); } - return sectionsList; + return sectionsList.map((section) => ({ + ...section, + data: section.data.map((option) => ({ + ...option, + text: option.text ?? '', + alternateText: option.alternateText ?? undefined, + keyForList: option.keyForList ?? '', + isDisabled: option.isDisabled ?? undefined, + login: option.login ?? undefined, + shouldShowSubscript: option.shouldShowSubscript ?? undefined, + })), + })); }, [currentUserOption, personalDetails, recentReports, translate, userToInvite]); const selectReport = useCallback( - (option: User & RadioItem) => { + (option: ListItem) => { if (!option) { return; } @@ -164,15 +175,25 @@ function TaskAssigneeSelectorModal({reports, task}: TaskAssigneeSelectorModalPro // Check to see if we're editing a task and if so, update the assignee if (report) { if (option.accountID !== report.managerID) { - const assigneeChatReport = TaskActions.setAssigneeValue(option?.login ?? '', option?.accountID ?? 0, report.reportID, OptionsListUtils.isCurrentUser(option)); + const assigneeChatReport = TaskActions.setAssigneeValue( + option?.login ?? '', + option?.accountID ?? -1, + report.reportID, + OptionsListUtils.isCurrentUser({...option, accountID: option?.accountID ?? -1}), + ); // Pass through the selected assignee - TaskActions.editTaskAssignee(report, session?.accountID ?? 0, option?.login ?? '', option.accountID, assigneeChatReport); + TaskActions.editTaskAssignee(report, session?.accountID ?? 0, option?.login ?? '', option?.accountID, assigneeChatReport); } Navigation.dismissModal(report.reportID); // If there's no report, we're creating a new task } else if (option.accountID) { - TaskActions.setAssigneeValue(option?.login ?? '', option.accountID, task?.shareDestination ?? '', OptionsListUtils.isCurrentUser(option)); + TaskActions.setAssigneeValue( + option?.login ?? '', + option.accountID, + task?.shareDestination ?? '', + OptionsListUtils.isCurrentUser({...option, accountID: option?.accountID ?? -1}), + ); Navigation.goBack(ROUTES.NEW_TASK); } }, @@ -190,7 +211,7 @@ function TaskAssigneeSelectorModal({reports, task}: TaskAssigneeSelectorModalPro includeSafeAreaPaddingBottom={false} testID={TaskAssigneeSelectorModal.displayName} > - {({didScreenTransitionEnd, safeAreaPaddingBottomStyle}) => ( + {({didScreenTransitionEnd}) => ( diff --git a/src/types/onyx/IsSearchingForReports.ts b/src/types/onyx/IsSearchingForReports.ts deleted file mode 100644 index 1af84277ca1f..000000000000 --- a/src/types/onyx/IsSearchingForReports.ts +++ /dev/null @@ -1,3 +0,0 @@ -type IsSearchingForReports = boolean; - -export default IsSearchingForReports; From dd2e3bff3cde5f259590426b187a49639fc5bb1d Mon Sep 17 00:00:00 2001 From: Yauheni Pasiukevich Date: Tue, 5 Mar 2024 12:23:02 +0100 Subject: [PATCH 5/5] fix types --- src/libs/OptionsListUtils.ts | 1 - src/pages/tasks/TaskAssigneeSelectorModal.tsx | 7 +-- .../TaskShareDestinationSelectorModal.tsx | 51 +++++++++++-------- 3 files changed, 35 insertions(+), 24 deletions(-) diff --git a/src/libs/OptionsListUtils.ts b/src/libs/OptionsListUtils.ts index 46b92d6f1424..32b7c08c3f91 100644 --- a/src/libs/OptionsListUtils.ts +++ b/src/libs/OptionsListUtils.ts @@ -156,7 +156,6 @@ type SectionForSearchTerm = { section: CategorySection; newIndexOffset: number; }; - type GetOptions = { recentReports: ReportUtils.OptionData[]; personalDetails: ReportUtils.OptionData[]; diff --git a/src/pages/tasks/TaskAssigneeSelectorModal.tsx b/src/pages/tasks/TaskAssigneeSelectorModal.tsx index d43973f4404f..0ffb33b7590b 100644 --- a/src/pages/tasks/TaskAssigneeSelectorModal.tsx +++ b/src/pages/tasks/TaskAssigneeSelectorModal.tsx @@ -69,7 +69,7 @@ function useOptions({reports}: UseOptions) { ); const headerMessage = OptionsListUtils.getHeaderMessage( - (recentReports?.length || 0 + personalDetails?.length || 0) !== 0 || Boolean(currentUserOption), + (recentReports?.length || 0) + (personalDetails?.length || 0) !== 0 || Boolean(currentUserOption), Boolean(userToInvite), debouncedSearchValue, ); @@ -146,6 +146,7 @@ function TaskAssigneeSelectorModal({reports, task}: TaskAssigneeSelectorModalPro if (userToInvite) { sectionsList.push({ + title: '', data: [userToInvite], shouldShow: true, indexOffset, @@ -179,7 +180,7 @@ function TaskAssigneeSelectorModal({reports, task}: TaskAssigneeSelectorModalPro option?.login ?? '', option?.accountID ?? -1, report.reportID, - OptionsListUtils.isCurrentUser({...option, accountID: option?.accountID ?? -1}), + OptionsListUtils.isCurrentUser({...option, accountID: option?.accountID ?? -1, login: option?.login ?? ''}), ); // Pass through the selected assignee @@ -192,7 +193,7 @@ function TaskAssigneeSelectorModal({reports, task}: TaskAssigneeSelectorModalPro option?.login ?? '', option.accountID, task?.shareDestination ?? '', - OptionsListUtils.isCurrentUser({...option, accountID: option?.accountID ?? -1}), + OptionsListUtils.isCurrentUser({...option, accountID: option?.accountID ?? -1, login: option?.login ?? undefined}), ); Navigation.goBack(ROUTES.NEW_TASK); } diff --git a/src/pages/tasks/TaskShareDestinationSelectorModal.tsx b/src/pages/tasks/TaskShareDestinationSelectorModal.tsx index d22304d07bff..5b56e58752ac 100644 --- a/src/pages/tasks/TaskShareDestinationSelectorModal.tsx +++ b/src/pages/tasks/TaskShareDestinationSelectorModal.tsx @@ -5,6 +5,9 @@ import type {OnyxCollection, OnyxEntry} from 'react-native-onyx'; import HeaderWithBackButton from '@components/HeaderWithBackButton'; import {usePersonalDetails} from '@components/OnyxProvider'; import ScreenWrapper from '@components/ScreenWrapper'; +import SelectionList from '@components/SelectionList'; +import UserListItem from '@components/SelectionList/UserListItem'; +import useDebouncedState from '@hooks/useDebouncedState'; import useLocalize from '@hooks/useLocalize'; import useNetwork from '@hooks/useNetwork'; import useThemeStyles from '@hooks/useThemeStyles'; @@ -12,9 +15,6 @@ import * as ReportActions from '@libs/actions/Report'; import Navigation from '@libs/Navigation/Navigation'; import * as OptionsListUtils from '@libs/OptionsListUtils'; import * as ReportUtils from '@libs/ReportUtils'; -import SelectionList from '@components/SelectionList'; -import UserListItem from '@components/SelectionList/UserListItem'; -import useDebouncedState from '@hooks/useDebouncedState'; import * as Task from '@userActions/Task'; import CONST from '@src/CONST'; import ONYXKEYS from '@src/ONYXKEYS'; @@ -32,7 +32,6 @@ type TaskShareDestinationSelectorModalProps = TaskShareDestinationSelectorModalO const selectReportHandler = (option: unknown) => { const optionItem = option as ReportUtils.OptionData; - if (!optionItem || !optionItem?.reportID) { return; } @@ -42,18 +41,15 @@ const selectReportHandler = (option: unknown) => { }; const reportFilter = (reports: OnyxCollection) => - Object.keys(reports ?? {}).reduce( - (filtered, reportKey) => { - const report: OnyxEntry = reports?.[reportKey] ?? null; - if (ReportUtils.canUserPerformWriteAction(report) && ReportUtils.canCreateTaskInReport(report) && !ReportUtils.isCanceledTaskReport(report)) { - return {...filtered, [reportKey]: report}; - } - return filtered; - }, - {}, - ); - -function TaskShareDestinationSelectorModal({reports, isSearchingForReports} : TaskShareDestinationSelectorModalProps) { + Object.keys(reports ?? {}).reduce((filtered, reportKey) => { + const report: OnyxEntry = reports?.[reportKey] ?? null; + if (ReportUtils.canUserPerformWriteAction(report) && ReportUtils.canCreateTaskInReport(report) && !ReportUtils.isCanceledTaskReport(report)) { + return {...filtered, [reportKey]: report}; + } + return filtered; + }, {}); + +function TaskShareDestinationSelectorModal({reports, isSearchingForReports}: TaskShareDestinationSelectorModalProps) { const styles = useThemeStyles(); const [searchValue, debouncedSearchValue, setSearchValue] = useDebouncedState(''); const {translate} = useLocalize(); @@ -69,7 +65,23 @@ function TaskShareDestinationSelectorModal({reports, isSearchingForReports} : Ta const headerMessage = OptionsListUtils.getHeaderMessage(recentReports && recentReports.length !== 0, false, debouncedSearchValue); - const sections = recentReports && recentReports.length > 0 ? [{data: recentReports, shouldShow: true}] : []; + const sections = + recentReports && recentReports.length > 0 + ? [ + { + data: recentReports.map((option) => ({ + ...option, + text: option.text ?? '', + alternateText: option.alternateText ?? undefined, + keyForList: option.keyForList ?? '', + isDisabled: option.isDisabled ?? undefined, + login: option.login ?? undefined, + shouldShowSubscript: option.shouldShowSubscript ?? undefined, + })), + shouldShow: true, + }, + ] + : []; return {sections, headerMessage}; }, [personalDetails, reports, debouncedSearchValue]); @@ -83,7 +95,7 @@ function TaskShareDestinationSelectorModal({reports, isSearchingForReports} : Ta includeSafeAreaPaddingBottom={false} testID="TaskShareDestinationSelectorModal" > - {({didScreenTransitionEnd, safeAreaPaddingBottomStyle}) => ( + {({didScreenTransitionEnd}) => ( <>