Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

[TS migration] Migrate 'NewTask' page to TypeScript #36886

Merged
merged 10 commits into from
Mar 12, 2024
14 changes: 7 additions & 7 deletions src/libs/actions/Task.ts
Original file line number Diff line number Diff line change
Expand Up @@ -100,7 +100,7 @@ function createTaskAndNavigate(
assigneeEmail: string,
assigneeAccountID = 0,
assigneeChatReport: OnyxEntry<OnyxTypes.Report> = null,
policyID = CONST.POLICY.OWNER_EMAIL_FAKE,
policyID: string = CONST.POLICY.OWNER_EMAIL_FAKE,
) {
const optimisticTaskReport = ReportUtils.buildOptimisticTaskReport(currentUserAccountID, assigneeAccountID, parentReportID, title, description, policyID);

Expand Down Expand Up @@ -672,8 +672,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 @@ -693,7 +693,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 @@ -704,8 +704,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 @@ -964,4 +964,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);
pasyukevich marked this conversation as resolved.
Show resolved Hide resolved
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
Loading