Skip to content

Commit

Permalink
Merge pull request #36886 from pasyukevich/feature/migrate-NewTask
Browse files Browse the repository at this point in the history
[TS migration] Migrate 'NewTask' page to TypeScript
  • Loading branch information
neil-marcellini authored Mar 12, 2024
2 parents 92bbd8b + 4e52920 commit 87fb21e
Show file tree
Hide file tree
Showing 6 changed files with 330 additions and 381 deletions.
12 changes: 6 additions & 6 deletions src/libs/actions/Task.ts
Original file line number Diff line number Diff line change
Expand Up @@ -689,8 +689,8 @@ function clearOutTaskInfoAndNavigate(reportID: string) {
/**
* Get the assignee data
*/
function getAssignee(assigneeAccountID: number, personalDetails: OnyxTypes.PersonalDetailsList): Assignee {
const details = personalDetails[assigneeAccountID];
function getAssignee(assigneeAccountID: number, personalDetails: OnyxEntry<OnyxTypes.PersonalDetailsList>): Assignee {
const details = personalDetails?.[assigneeAccountID];

if (!details) {
return {
Expand All @@ -710,7 +710,7 @@ function getAssignee(assigneeAccountID: number, personalDetails: OnyxTypes.Perso
/**
* Get the share destination data
* */
function getShareDestination(reportID: string, reports: OnyxCollection<OnyxTypes.Report>, personalDetails: OnyxTypes.PersonalDetailsList): ShareDestination {
function getShareDestination(reportID: string, reports: OnyxCollection<OnyxTypes.Report>, personalDetails: OnyxEntry<OnyxTypes.PersonalDetailsList>): ShareDestination {
const report = reports?.[`report_${reportID}`] ?? null;

const participantAccountIDs = report?.participantAccountIDs ?? [];
Expand All @@ -721,8 +721,8 @@ function getShareDestination(reportID: string, reports: OnyxCollection<OnyxTypes
if (ReportUtils.isChatReport(report) && ReportUtils.isDM(report) && ReportUtils.hasSingleParticipant(report)) {
const participantAccountID = report?.participantAccountIDs?.[0] ?? -1;

const displayName = personalDetails[participantAccountID]?.displayName ?? '';
const login = personalDetails[participantAccountID]?.login ?? '';
const displayName = personalDetails?.[participantAccountID]?.displayName ?? '';
const login = personalDetails?.[participantAccountID]?.login ?? '';
subtitle = LocalePhoneNumber.formatPhoneNumber(login || displayName);
} else {
subtitle = ReportUtils.getChatRoomSubtitle(report) ?? '';
Expand Down Expand Up @@ -981,4 +981,4 @@ export {
canModifyTask,
};

export type {PolicyValue};
export type {PolicyValue, Assignee, ShareDestination};
Original file line number Diff line number Diff line change
@@ -1,66 +1,58 @@
import type {StackScreenProps} from '@react-navigation/stack';
import ExpensiMark from 'expensify-common/lib/ExpensiMark';
import PropTypes from 'prop-types';
import React from 'react';
import {View} from 'react-native';
import {withOnyx} from 'react-native-onyx';
import type {OnyxEntry} from 'react-native-onyx';
import FormProvider from '@components/Form/FormProvider';
import InputWrapperWithRef 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 withLocalize, {withLocalizePropTypes} from '@components/withLocalize';
import useAutoFocusInput from '@hooks/useAutoFocusInput';
import useLocalize from '@hooks/useLocalize';
import useThemeStyles from '@hooks/useThemeStyles';
import compose from '@libs/compose';
import * as ErrorUtils from '@libs/ErrorUtils';
import Navigation from '@libs/Navigation/Navigation';
import type {NewTaskNavigatorParamList} from '@libs/Navigation/types';
import updateMultilineInputRange from '@libs/updateMultilineInputRange';
import * as Task from '@userActions/Task';
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 INPUT_IDS from '@src/types/form/NewTaskForm';
import type {Task} from '@src/types/onyx';

const propTypes = {
/** Grab the Share description of the Task */
task: PropTypes.shape({
/** Description of the Task */
description: PropTypes.string,
}),

...withLocalizePropTypes,
type NewTaskDescriptionPageOnyxProps = {
/** Task Creation Data */
task: OnyxEntry<Task>;
};

const defaultProps = {
task: {
description: '',
},
};
type NewTaskDescriptionPageProps = NewTaskDescriptionPageOnyxProps & StackScreenProps<NewTaskNavigatorParamList, typeof SCREENS.NEW_TASK.DESCRIPTION>;

const parser = new ExpensiMark();

function NewTaskDescriptionPage(props) {
function NewTaskDescriptionPage({task}: NewTaskDescriptionPageProps) {
const styles = useThemeStyles();
const {translate} = useLocalize();
const {inputCallbackRef} = useAutoFocusInput();

const onSubmit = (values) => {
Task.setDescriptionValue(values.taskDescription);
const onSubmit = (values: FormOnyxValues<typeof ONYXKEYS.FORMS.NEW_TASK_FORM>) => {
TaskActions.setDescriptionValue(values.taskDescription);
Navigation.goBack(ROUTES.NEW_TASK);
};

/**
* @param {Object} values - form input values passed by the Form component
* @returns {Boolean}
*/
function validate(values) {
const validate = (values: FormOnyxValues<typeof ONYXKEYS.FORMS.NEW_TASK_FORM>): FormInputErrors<typeof ONYXKEYS.FORMS.NEW_TASK_FORM> => {
const errors = {};

if (values.taskDescription.length > CONST.DESCRIPTION_LIMIT) {
ErrorUtils.addErrorMessage(errors, 'taskDescription', ['common.error.characterLimitExceedCounter', {length: values.taskDescription.length, limit: CONST.DESCRIPTION_LIMIT}]);
}

return errors;
}
};

return (
<ScreenWrapper
Expand All @@ -70,33 +62,33 @@ function NewTaskDescriptionPage(props) {
>
<>
<HeaderWithBackButton
title={props.translate('task.description')}
onCloseButtonPress={() => Task.dismissModalAndClearOutTaskInfo()}
title={translate('task.description')}
onCloseButtonPress={() => TaskActions.dismissModalAndClearOutTaskInfo()}
onBackButtonPress={() => Navigation.goBack(ROUTES.NEW_TASK)}
/>
<FormProvider
formID={ONYXKEYS.FORMS.NEW_TASK_FORM}
submitButtonText={props.translate('common.next')}
submitButtonText={translate('common.next')}
style={[styles.mh5, styles.flexGrow1]}
validate={(values) => validate(values)}
onSubmit={(values) => onSubmit(values)}
validate={validate}
onSubmit={onSubmit}
enabledWhenOffline
>
<View style={styles.mb5}>
<InputWrapperWithRef
InputComponent={TextInput}
defaultValue={parser.htmlToMarkdown(parser.replace(props.task.description))}
defaultValue={parser.htmlToMarkdown(parser.replace(task?.description ?? ''))}
inputID={INPUT_IDS.TASK_DESCRIPTION}
label={props.translate('newTaskPage.descriptionOptional')}
accessibilityLabel={props.translate('newTaskPage.descriptionOptional')}
label={translate('newTaskPage.descriptionOptional')}
accessibilityLabel={translate('newTaskPage.descriptionOptional')}
role={CONST.ROLE.PRESENTATION}
ref={(el) => {
inputCallbackRef(el);
updateMultilineInputRange(el);
}}
autoGrowHeight
shouldSubmitForm
containerStyles={[styles.autoGrowHeightMultilineInput]}
containerStyles={styles.autoGrowHeightMultilineInput}
/>
</View>
</FormProvider>
Expand All @@ -106,14 +98,9 @@ function NewTaskDescriptionPage(props) {
}

NewTaskDescriptionPage.displayName = 'NewTaskDescriptionPage';
NewTaskDescriptionPage.propTypes = propTypes;
NewTaskDescriptionPage.defaultProps = defaultProps;

export default compose(
withOnyx({
task: {
key: ONYXKEYS.TASK,
},
}),
withLocalize,
)(NewTaskDescriptionPage);
export default withOnyx<NewTaskDescriptionPageProps, NewTaskDescriptionPageOnyxProps>({
task: {
key: ONYXKEYS.TASK,
},
})(NewTaskDescriptionPage);
Original file line number Diff line number Diff line change
@@ -1,58 +1,52 @@
import type {StackScreenProps} from '@react-navigation/stack';
import ExpensiMark from 'expensify-common/lib/ExpensiMark';
import PropTypes from 'prop-types';
import React, {useEffect, useState} from 'react';
import {View} 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 withLocalize, {withLocalizePropTypes} from '@components/withLocalize';
import useAutoFocusInput from '@hooks/useAutoFocusInput';
import useLocalize from '@hooks/useLocalize';
import useThemeStyles from '@hooks/useThemeStyles';
import compose from '@libs/compose';
import * as ErrorUtils from '@libs/ErrorUtils';
import Navigation from '@libs/Navigation/Navigation';
import * as Task from '@userActions/Task';
import type {NewTaskNavigatorParamList} from '@libs/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 INPUT_IDS from '@src/types/form/NewTaskForm';
import type {Task} from '@src/types/onyx';

const propTypes = {
/** Task title and description data */
task: PropTypes.shape({
title: PropTypes.string,
description: PropTypes.string,
}),

...withLocalizePropTypes,
type NewTaskDetailsPageOnyxProps = {
/** Task Creation Data */
task: OnyxEntry<Task>;
};

const defaultProps = {
task: {},
};
type NewTaskDetailsPageProps = NewTaskDetailsPageOnyxProps & StackScreenProps<NewTaskNavigatorParamList, typeof SCREENS.NEW_TASK.DETAILS>;

const parser = new ExpensiMark();

function NewTaskDetailsPage(props) {
function NewTaskDetailsPage({task}: NewTaskDetailsPageProps) {
const styles = useThemeStyles();
const [taskTitle, setTaskTitle] = useState(props.task.title);
const [taskDescription, setTaskDescription] = useState(props.task.description || '');
const {translate} = useLocalize();
const [taskTitle, setTaskTitle] = useState(task?.title ?? '');
const [taskDescription, setTaskDescription] = useState(task?.description ?? '');

const {inputCallbackRef} = useAutoFocusInput();

useEffect(() => {
setTaskTitle(props.task.title);
setTaskDescription(parser.htmlToMarkdown(parser.replace(props.task.description || '')));
}, [props.task]);
setTaskTitle(task?.title ?? '');
setTaskDescription(parser.htmlToMarkdown(parser.replace(task?.description ?? '')));
}, [task]);

/**
* @param {Object} values - form input values passed by the Form component
* @returns {Boolean}
*/
function validate(values) {
const validate = (values: FormOnyxValues<typeof ONYXKEYS.FORMS.NEW_TASK_FORM>): FormInputErrors<typeof ONYXKEYS.FORMS.NEW_TASK_FORM> => {
const errors = {};

if (!values.taskTitle) {
Expand All @@ -66,14 +60,14 @@ function NewTaskDetailsPage(props) {
}

return errors;
}
};

// On submit, we want to call the assignTask function and wait to validate
// the response
function onSubmit(values) {
Task.setDetailsValue(values.taskTitle, values.taskDescription);
const onSubmit = (values: FormOnyxValues<typeof ONYXKEYS.FORMS.NEW_TASK_FORM>) => {
TaskActions.setDetailsValue(values.taskTitle, values.taskDescription);
Navigation.navigate(ROUTES.NEW_TASK);
}
};

return (
<ScreenWrapper
Expand All @@ -82,45 +76,47 @@ function NewTaskDetailsPage(props) {
testID={NewTaskDetailsPage.displayName}
>
<HeaderWithBackButton
title={props.translate('newTaskPage.assignTask')}
onCloseButtonPress={() => Task.dismissModalAndClearOutTaskInfo()}
title={translate('newTaskPage.assignTask')}
onCloseButtonPress={() => TaskActions.dismissModalAndClearOutTaskInfo()}
shouldShowBackButton
onBackButtonPress={() => Task.dismissModalAndClearOutTaskInfo()}
onBackButtonPress={() => TaskActions.dismissModalAndClearOutTaskInfo()}
/>
<FormProvider
formID={ONYXKEYS.FORMS.NEW_TASK_FORM}
submitButtonText={props.translate('common.next')}
submitButtonText={translate('common.next')}
style={[styles.mh5, styles.flexGrow1]}
validate={(values) => validate(values)}
onSubmit={(values) => onSubmit(values)}
validate={validate}
onSubmit={onSubmit}
enabledWhenOffline
>
<View style={styles.mb5}>
<InputWrapper
InputComponent={TextInput}
ref={inputCallbackRef}
valueType="string"
role={CONST.ROLE.PRESENTATION}
inputID={INPUT_IDS.TASK_TITLE}
label={props.translate('task.title')}
accessibilityLabel={props.translate('task.title')}
label={translate('task.title')}
accessibilityLabel={translate('task.title')}
value={taskTitle}
onValueChange={(value) => setTaskTitle(value)}
onValueChange={setTaskTitle}
autoCorrect={false}
/>
</View>
<View style={styles.mb5}>
<InputWrapper
valueType="string"
InputComponent={TextInput}
role={CONST.ROLE.PRESENTATION}
inputID={INPUT_IDS.TASK_DESCRIPTION}
label={props.translate('newTaskPage.descriptionOptional')}
accessibilityLabel={props.translate('newTaskPage.descriptionOptional')}
label={translate('newTaskPage.descriptionOptional')}
accessibilityLabel={translate('newTaskPage.descriptionOptional')}
autoGrowHeight
shouldSubmitForm
containerStyles={[styles.autoGrowHeightMultilineInput]}
containerStyles={styles.autoGrowHeightMultilineInput}
defaultValue={parser.htmlToMarkdown(parser.replace(taskDescription))}
value={taskDescription}
onValueChange={(value) => setTaskDescription(value)}
onValueChange={setTaskDescription}
/>
</View>
</FormProvider>
Expand All @@ -129,14 +125,9 @@ function NewTaskDetailsPage(props) {
}

NewTaskDetailsPage.displayName = 'NewTaskDetailsPage';
NewTaskDetailsPage.propTypes = propTypes;
NewTaskDetailsPage.defaultProps = defaultProps;

export default compose(
withOnyx({
task: {
key: ONYXKEYS.TASK,
},
}),
withLocalize,
)(NewTaskDetailsPage);
export default withOnyx<NewTaskDetailsPageProps, NewTaskDetailsPageOnyxProps>({
task: {
key: ONYXKEYS.TASK,
},
})(NewTaskDetailsPage);
Loading

0 comments on commit 87fb21e

Please sign in to comment.