Skip to content

Commit

Permalink
Merge pull request Expensify#35484 from pasyukevich/feature/migrate-t…
Browse files Browse the repository at this point in the history
…ask-edit

[TS migration] Migrate 'Task' page to TypeScript
  • Loading branch information
marcochavezf authored Mar 7, 2024
2 parents 2c7bbd1 + dd2e3bf commit 27b1aa8
Show file tree
Hide file tree
Showing 8 changed files with 202 additions and 232 deletions.
12 changes: 6 additions & 6 deletions src/libs/OptionsListUtils.ts
Original file line number Diff line number Diff line change
Expand Up @@ -107,7 +107,7 @@ type Hierarchy = Record<string, Category & {[key: string]: Hierarchy & Category}

type GetOptionsConfig = {
reportActions?: ReportActions;
betas?: Beta[];
betas?: OnyxEntry<Beta[]>;
selectedOptions?: Option[];
maxRecentReportsToShow?: number;
excludeLogins?: string[];
Expand Down Expand Up @@ -156,7 +156,6 @@ type SectionForSearchTerm = {
section: CategorySection;
newIndexOffset: number;
};

type GetOptions = {
recentReports: ReportUtils.OptionData[];
personalDetails: ReportUtils.OptionData[];
Expand Down Expand Up @@ -1441,7 +1440,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,
Expand Down Expand Up @@ -1805,7 +1805,7 @@ function getIOUConfirmationOptionsFromParticipants(participants: Participant[],
function getFilteredOptions(
reports: OnyxCollection<Report>,
personalDetails: OnyxEntry<PersonalDetailsList>,
betas: Beta[] = [],
betas: OnyxEntry<Beta[]> = [],
searchValue = '',
selectedOptions: Array<Partial<ReportUtils.OptionData>> = [],
excludeLogins: string[] = [],
Expand Down Expand Up @@ -1852,9 +1852,9 @@ function getFilteredOptions(
*/

function getShareDestinationOptions(
reports: Record<string, Report>,
reports: Record<string, Report | null>,
personalDetails: OnyxEntry<PersonalDetailsList>,
betas: Beta[] = [],
betas: OnyxEntry<Beta[]> = [],
searchValue = '',
selectedOptions: Array<Partial<ReportUtils.OptionData>> = [],
excludeLogins: string[] = [],
Expand Down
2 changes: 1 addition & 1 deletion src/libs/ReportUtils.ts
Original file line number Diff line number Diff line change
Expand Up @@ -3992,7 +3992,7 @@ function shouldReportBeInOptionList({
report: OnyxEntry<Report>;
currentReportId: string;
isInGSDMode: boolean;
betas: Beta[];
betas: OnyxEntry<Beta[]>;
policies: OnyxCollection<Policy>;
excludeEmptyChats: boolean;
doesReportHaveViolations: boolean;
Expand Down
10 changes: 8 additions & 2 deletions src/libs/actions/Task.ts
Original file line number Diff line number Diff line change
Expand Up @@ -467,7 +467,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<OnyxTypes.Report> = null) {
function editTaskAssignee(
report: OnyxTypes.Report,
ownerAccountID: number,
assigneeEmail: string,
assigneeAccountID: number | null = 0,
assigneeChatReport: OnyxEntry<OnyxTypes.Report> = null,
) {
// Create the EditedReportAction on the task
const editTaskReportAction = ReportUtils.buildOptimisticEditedTaskReportAction(currentUserEmail);
const reportName = report.reportName?.trim();
Expand All @@ -476,7 +482,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}),
},
Expand Down
Original file line number Diff line number Diff line change
@@ -1,58 +1,49 @@
/* eslint-disable es/no-optional-chaining */
import type {RouteProp} from '@react-navigation/native';
import {useRoute} from '@react-navigation/native';
import lodashGet from 'lodash/get';
import lodashPick from 'lodash/pick';
import PropTypes from 'prop-types';
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 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 {ListItem} from '@components/SelectionList/types';
import UserListItem from '@components/SelectionList/UserListItem';
import withCurrentUserPersonalDetails from '@components/withCurrentUserPersonalDetails';
import type {WithCurrentUserPersonalDetailsProps} from '@components/withCurrentUserPersonalDetails';
import useCurrentUserPersonalDetails from '@hooks/useCurrentUserPersonalDetails';
import useDebouncedState from '@hooks/useDebouncedState';
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 {Report, Task} from '@src/types/onyx';

const propTypes = {
type TaskAssigneeSelectorModalOnyxProps = {
/** All reports shared with the user */
reports: PropTypes.objectOf(reportPropTypes),
reports: OnyxCollection<Report>;

/** 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,
}),

/** The policy of root parent report */
rootParentReportPolicy: PropTypes.shape({
/** The role of current user */
role: PropTypes.string,
}),
task: OnyxEntry<Task>;
};

const defaultProps = {
reports: {},
task: {},
rootParentReportPolicy: {},
type UseOptions = {
reports: OnyxCollection<Report>;
};

function useOptions({reports}) {
type TaskAssigneeSelectorModalProps = TaskAssigneeSelectorModalOnyxProps & WithCurrentUserPersonalDetailsProps;

function useOptions({reports}: UseOptions) {
const allPersonalDetails = usePersonalDetails() || CONST.EMPTY_OBJECT;
const betas = useBetas();
const [isLoading, setIsLoading] = useState(true);
Expand All @@ -78,7 +69,7 @@ function useOptions({reports}) {
);

const headerMessage = OptionsListUtils.getHeaderMessage(
(recentReports.length || 0 + personalDetails.length || 0) !== 0 || currentUserOption,
(recentReports?.length || 0) + (personalDetails?.length || 0) !== 0 || Boolean(currentUserOption),
Boolean(userToInvite),
debouncedSearchValue,
);
Expand All @@ -99,28 +90,28 @@ function useOptions({reports}) {
return {...options, isLoading, searchValue, debouncedSearchValue, setSearchValue};
}

function TaskAssigneeSelectorModal({reports, task, rootParentReportPolicy}) {
function TaskAssigneeSelectorModal({reports, task}: TaskAssigneeSelectorModalProps) {
const styles = useThemeStyles();
const route = useRoute();
const route = useRoute<RouteProp<TaskDetailsNavigatorParamList, typeof SCREENS.TASK.ASSIGNEE>>();
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);
};

const report = useMemo(() => {
if (!route.params || !route.params.reportID) {
const report: OnyxEntry<Report> = useMemo(() => {
if (!route.params?.reportID) {
return null;
}
if (report && !ReportUtils.isTaskReport(report)) {
Navigation.isNavigationReady().then(() => {
Navigation.dismissModal(report.reportID);
});
}
return reports[`${ONYXKEYS.COLLECTION.REPORT}${route.params.reportID}`];
return reports?.[`${ONYXKEYS.COLLECTION.REPORT}${route.params?.reportID}`] ?? null;
}, [reports, route]);

const sections = useMemo(() => {
Expand Down Expand Up @@ -155,66 +146,87 @@ function TaskAssigneeSelectorModal({reports, task, rootParentReportPolicy}) {

if (userToInvite) {
sectionsList.push({
title: '',
data: [userToInvite],
shouldShow: true,
indexOffset,
});
}

return sectionsList;
}, [currentUserOption, personalDetails, recentReports, userToInvite, translate]);
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) => {
(option: ListItem) => {
if (!option) {
return;
}

// 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 ?? -1,
report.reportID,
OptionsListUtils.isCurrentUser({...option, accountID: option?.accountID ?? -1, login: option?.login ?? ''}),
);

// Pass through the selected assignee
Task.editTaskAssignee(report, session.accountID, 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) {
Task.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, login: option?.login ?? undefined}),
);
Navigation.goBack(ROUTES.NEW_TASK);
}
},
[session.accountID, task.shareDestination, report],
[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 = Task.canModifyTask(report, currentUserPersonalDetails.accountID, lodashGet(rootParentReportPolicy, 'role', ''));
const canModifyTask = TaskActions.canModifyTask(report, currentUserPersonalDetails.accountID);
const isTaskNonEditable = ReportUtils.isTaskReport(report) && (!canModifyTask || !isOpen);

return (
<ScreenWrapper
includeSafeAreaPaddingBottom={false}
testID={TaskAssigneeSelectorModal.displayName}
>
{({didScreenTransitionEnd, safeAreaPaddingBottomStyle}) => (
{({didScreenTransitionEnd}) => (
<FullPageNotFoundView shouldShow={isTaskNonEditable}>
<HeaderWithBackButton
title={translate('task.assignee')}
onBackButtonPress={handleBackButtonPress}
/>
<View style={[styles.flex1, styles.w100, styles.pRelative]}>
<SelectionList
sections={didScreenTransitionEnd && !isLoading ? sections : CONST.EMPTY_ARRAY}
sections={didScreenTransitionEnd && !isLoading ? sections : []}
ListItem={UserListItem}
onSelectRow={selectReport}
onChangeText={onChangeText}
textInputValue={searchValue}
headerMessage={headerMessage}
textInputLabel={translate('optionsSelector.nameEmailOrPhoneNumber')}
safeAreaPaddingBottomStyle={safeAreaPaddingBottomStyle}
showLoadingPlaceholder={isLoading || !didScreenTransitionEnd}
/>
</View>
Expand All @@ -225,26 +237,14 @@ function TaskAssigneeSelectorModal({reports, task, rootParentReportPolicy}) {
}

TaskAssigneeSelectorModal.displayName = 'TaskAssigneeSelectorModal';
TaskAssigneeSelectorModal.propTypes = propTypes;
TaskAssigneeSelectorModal.defaultProps = defaultProps;

export default compose(
withOnyx({
reports: {
key: ONYXKEYS.COLLECTION.REPORT,
},
task: {
key: ONYXKEYS.TASK,
},
}),
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) => lodashPick(policy, ['role']),
},
}),
)(TaskAssigneeSelectorModal);
const TaskAssigneeSelectorModalWithOnyx = withOnyx<TaskAssigneeSelectorModalProps, TaskAssigneeSelectorModalOnyxProps>({
reports: {
key: ONYXKEYS.COLLECTION.REPORT,
},
task: {
key: ONYXKEYS.TASK,
},
})(TaskAssigneeSelectorModal);

export default withCurrentUserPersonalDetails(TaskAssigneeSelectorModalWithOnyx);
Loading

0 comments on commit 27b1aa8

Please sign in to comment.