Skip to content

Commit

Permalink
Merge pull request Expensify#38797 from software-mansion-labs/ts/war-…
Browse files Browse the repository at this point in the history
…in/custom-status-page-migration

[TS migration] Migrate 'CustomStatus' page to TypeScript
  • Loading branch information
arosiclair authored Mar 29, 2024
2 parents c9b5901 + 56e14fa commit bc2b2c4
Show file tree
Hide file tree
Showing 3 changed files with 84 additions and 101 deletions.
Original file line number Diff line number Diff line change
@@ -1,38 +1,45 @@
import lodashGet from 'lodash/get';
import React, {useCallback} from 'react';
import {withOnyx} from 'react-native-onyx';
import type {OnyxEntry} from 'react-native-onyx';
import DatePicker from '@components/DatePicker';
import FormProvider from '@components/Form/FormProvider';
import InputWrapper from '@components/Form/InputWrapper';
import type {FormOnyxValues} from '@components/Form/types';
import HeaderWithBackButton from '@components/HeaderWithBackButton';
import ScreenWrapper from '@components/ScreenWrapper';
import withLocalize, {withLocalizePropTypes} from '@components/withLocalize';
import useLocalize from '@hooks/useLocalize';
import useThemeStyles from '@hooks/useThemeStyles';
import * as User from '@libs/actions/User';
import compose from '@libs/compose';
import DateUtils from '@libs/DateUtils';
import Navigation from '@libs/Navigation/Navigation';
import * as ValidationUtils from '@libs/ValidationUtils';
import ONYXKEYS from '@src/ONYXKEYS';
import ROUTES from '@src/ROUTES';
import INPUT_IDS from '@src/types/form/SettingsStatusClearDateForm';
import type * as OnyxTypes from '@src/types/onyx';

const propTypes = {
...withLocalizePropTypes,
type DateTime = {
dateTime: string;
};

function SetDatePage({translate, customStatus}) {
type SetDatePageOnyxProps = {
customStatus: OnyxEntry<OnyxTypes.CustomStatusDraft>;
};

type SetDatePageProps = SetDatePageOnyxProps;

function SetDatePage({customStatus}: SetDatePageProps) {
const styles = useThemeStyles();
const customClearAfter = lodashGet(customStatus, 'clearAfter', '');
const {translate} = useLocalize();
const customClearAfter = customStatus?.clearAfter ?? '';

const onSubmit = (v) => {
User.updateDraftCustomStatus({clearAfter: DateUtils.combineDateAndTime(customClearAfter, v.dateTime)});
const onSubmit = (value: DateTime) => {
User.updateDraftCustomStatus({clearAfter: DateUtils.combineDateAndTime(customClearAfter, value.dateTime)});
Navigation.goBack(ROUTES.SETTINGS_STATUS_CLEAR_AFTER);
};

const validate = useCallback((values) => {
const requiredFields = ['dateTime'];
const errors = ValidationUtils.getFieldRequiredErrors(values, requiredFields);
const validate = useCallback((values: FormOnyxValues<typeof ONYXKEYS.FORMS.SETTINGS_STATUS_CLEAR_DATE_FORM>) => {
const errors = ValidationUtils.getFieldRequiredErrors(values, [INPUT_IDS.DATE_TIME]);
const dateError = ValidationUtils.getDatePassedError(values.dateTime);

if (values.dateTime && dateError) {
Expand All @@ -58,31 +65,24 @@ function SetDatePage({translate, customStatus}) {
submitButtonText={translate('common.save')}
validate={validate}
enabledWhenOffline
shouldUseDefaultValue
>
<InputWrapper
InputComponent={DatePicker}
inputID={INPUT_IDS.DATE_TIME}
label={translate('statusPage.date')}
defaultValue={DateUtils.extractDate(customClearAfter)}
minDate={new Date()}
shouldUseDefaultValue
/>
</FormProvider>
</ScreenWrapper>
);
}

SetDatePage.propTypes = propTypes;
SetDatePage.displayName = 'SetDatePage';

export default compose(
withLocalize,
withOnyx({
customStatus: {
key: ONYXKEYS.CUSTOM_STATUS_DRAFT,
},
clearDateForm: {
key: `${ONYXKEYS.FORMS.SETTINGS_STATUS_CLEAR_DATE_FORM}Draft`,
},
}),
)(SetDatePage);
export default withOnyx<SetDatePageProps, SetDatePageOnyxProps>({
customStatus: {
key: ONYXKEYS.CUSTOM_STATUS_DRAFT,
},
})(SetDatePage);
Original file line number Diff line number Diff line change
@@ -1,28 +1,31 @@
import lodashGet from 'lodash/get';
import React from 'react';
import {View} from 'react-native';
import type {OnyxEntry} from 'react-native-onyx';
import {withOnyx} from 'react-native-onyx';
import HeaderWithBackButton from '@components/HeaderWithBackButton';
import ScreenWrapper from '@components/ScreenWrapper';
import TimePicker from '@components/TimePicker/TimePicker';
import withLocalize, {withLocalizePropTypes} from '@components/withLocalize';
import useLocalize from '@hooks/useLocalize';
import useThemeStyles from '@hooks/useThemeStyles';
import * as User from '@libs/actions/User';
import compose from '@libs/compose';
import DateUtils from '@libs/DateUtils';
import Navigation from '@libs/Navigation/Navigation';
import ONYXKEYS from '@src/ONYXKEYS';
import ROUTES from '@src/ROUTES';
import type * as OnyxTypes from '@src/types/onyx';

const propTypes = {
...withLocalizePropTypes,
type SetTimePageOnyxProps = {
customStatus: OnyxEntry<OnyxTypes.CustomStatusDraft>;
};

function SetTimePage({translate, customStatus}) {
type SetTimePageProps = SetTimePageOnyxProps;

function SetTimePage({customStatus}: SetTimePageProps) {
const styles = useThemeStyles();
const clearAfter = lodashGet(customStatus, 'clearAfter', '');
const {translate} = useLocalize();
const clearAfter = customStatus?.clearAfter ?? '';

const onSubmit = (time) => {
const onSubmit = (time: string) => {
const timeToUse = DateUtils.combineDateAndTime(time, clearAfter);

User.updateDraftCustomStatus({clearAfter: timeToUse});
Expand All @@ -40,24 +43,18 @@ function SetTimePage({translate, customStatus}) {
/>
<View style={styles.flex1}>
<TimePicker
inputID="timePicker"
defaultValue={clearAfter}
style={styles.flexGrow1}
onSubmit={onSubmit}
/>
</View>
</ScreenWrapper>
);
}

SetTimePage.propTypes = propTypes;
SetTimePage.displayName = 'SetTimePage';

export default compose(
withLocalize,
withOnyx({
customStatus: {
key: ONYXKEYS.CUSTOM_STATUS_DRAFT,
},
}),
)(SetTimePage);
export default withOnyx<SetTimePageProps, SetTimePageOnyxProps>({
customStatus: {
key: ONYXKEYS.CUSTOM_STATUS_DRAFT,
},
})(SetTimePage);
Original file line number Diff line number Diff line change
@@ -1,59 +1,58 @@
import _ from 'lodash';
import lodashGet from 'lodash/get';
import PropTypes from 'prop-types';
import React, {useCallback, useEffect, useMemo, useState} from 'react';
import {View} from 'react-native';
import {withOnyx} from 'react-native-onyx';
import type {OnyxEntry} from 'react-native-onyx';
import type {ValueOf} from 'type-fest';
import FormProvider from '@components/Form/FormProvider';
import HeaderWithBackButton from '@components/HeaderWithBackButton';
import MenuItemWithTopDescription from '@components/MenuItemWithTopDescription';
import ScreenWrapper from '@components/ScreenWrapper';
import RadioListItem from '@components/SelectionList/RadioListItem';
import Text from '@components/Text';
import withCurrentUserPersonalDetails, {withCurrentUserPersonalDetailsDefaultProps} from '@components/withCurrentUserPersonalDetails';
import withLocalize from '@components/withLocalize';
import useCurrentUserPersonalDetails from '@hooks/useCurrentUserPersonalDetails';
import useLocalize from '@hooks/useLocalize';
import useThemeStyles from '@hooks/useThemeStyles';
import * as User from '@libs/actions/User';
import compose from '@libs/compose';
import DateUtils from '@libs/DateUtils';
import Navigation from '@libs/Navigation/Navigation';
import * as ValidationUtils from '@libs/ValidationUtils';
import personalDetailsPropType from '@pages/personalDetailsPropType';
import CONST from '@src/CONST';
import ONYXKEYS from '@src/ONYXKEYS';
import ROUTES from '@src/ROUTES';
import type * as OnyxTypes from '@src/types/onyx';

const defaultProps = {
...withCurrentUserPersonalDetailsDefaultProps,
type CustomStatusTypes = ValueOf<typeof CONST.CUSTOM_STATUS_TYPES>;

type StatusType = {
value: CustomStatusTypes;
text: string;
keyForList: string;
isSelected: boolean;
};

const propTypes = {
currentUserPersonalDetails: personalDetailsPropType,
customStatus: PropTypes.shape({
clearAfter: PropTypes.string,
}),
type StatusClearAfterPageOnyxProps = {
/** User's custom status */
customStatus: OnyxEntry<OnyxTypes.CustomStatusDraft>;
};

type StatusClearAfterPageProps = StatusClearAfterPageOnyxProps;

/**
* @param {string} data - either a value from CONST.CUSTOM_STATUS_TYPES or a dateTime string in the format YYYY-MM-DD HH:mm
* @returns {string}
* @param data - either a value from CONST.CUSTOM_STATUS_TYPES or a dateTime string in the format YYYY-MM-DD HH:mm
*/
function getSelectedStatusType(data) {
function getSelectedStatusType(data: string): CustomStatusTypes {
switch (data) {
case DateUtils.getEndOfToday():
return CONST.CUSTOM_STATUS_TYPES.AFTER_TODAY;
case CONST.CUSTOM_STATUS_TYPES.NEVER:
case '':
return CONST.CUSTOM_STATUS_TYPES.NEVER;
case false:
return CONST.CUSTOM_STATUS_TYPES.AFTER_TODAY;
default:
return CONST.CUSTOM_STATUS_TYPES.CUSTOM;
}
}

const useValidateCustomDate = (data) => {
const useValidateCustomDate = (data: string) => {
const [customDateError, setCustomDateError] = useState('');
const [customTimeError, setCustomTimeError] = useState('');
const validate = () => {
Expand All @@ -63,8 +62,8 @@ const useValidateCustomDate = (data) => {
setCustomTimeError(timeValidationErrorKey);

return {
dateValidationErrorKey,
timeValidationErrorKey,
dateError: dateValidationErrorKey,
timeError: timeValidationErrorKey,
};
};

Expand All @@ -81,15 +80,17 @@ const useValidateCustomDate = (data) => {
return {customDateError, customTimeError, validateCustomDate};
};

function StatusClearAfterPage({currentUserPersonalDetails, customStatus}) {
function StatusClearAfterPage({customStatus}: StatusClearAfterPageProps) {
const styles = useThemeStyles();
const {translate} = useLocalize();
const clearAfter = lodashGet(currentUserPersonalDetails, 'status.clearAfter', '');
const draftClearAfter = lodashGet(customStatus, 'clearAfter', '');
const currentUserPersonalDetails = useCurrentUserPersonalDetails();
const clearAfter = currentUserPersonalDetails.status?.clearAfter ?? '';

const draftClearAfter = customStatus?.clearAfter ?? '';
const [draftPeriod, setDraftPeriod] = useState(getSelectedStatusType(draftClearAfter || clearAfter));
const statusType = useMemo(
const statusType = useMemo<StatusType[]>(
() =>
_.map(CONST.CUSTOM_STATUS_TYPES, (value, key) => ({
Object.entries(CONST.CUSTOM_STATUS_TYPES).map(([key, value]) => ({
value,
text: translate(`statusPage.timePeriods.${value}`),
keyForList: key,
Expand All @@ -102,8 +103,8 @@ function StatusClearAfterPage({currentUserPersonalDetails, customStatus}) {

const {redBrickDateIndicator, redBrickTimeIndicator} = useMemo(
() => ({
redBrickDateIndicator: customDateError ? CONST.BRICK_ROAD_INDICATOR_STATUS.ERROR : null,
redBrickTimeIndicator: customTimeError ? CONST.BRICK_ROAD_INDICATOR_STATUS.ERROR : null,
redBrickDateIndicator: customDateError ? CONST.BRICK_ROAD_INDICATOR_STATUS.ERROR : undefined,
redBrickTimeIndicator: customTimeError ? CONST.BRICK_ROAD_INDICATOR_STATUS.ERROR : undefined,
}),
[customTimeError, customDateError],
);
Expand All @@ -113,19 +114,19 @@ function StatusClearAfterPage({currentUserPersonalDetails, customStatus}) {
if (dateError || timeError) {
return;
}
let calculatedDraftDate = '';
let calculatedDraftDate: string;
if (draftPeriod === CONST.CUSTOM_STATUS_TYPES.CUSTOM) {
calculatedDraftDate = draftClearAfter;
} else {
const selectedRange = _.find(statusType, (item) => item.isSelected);
calculatedDraftDate = DateUtils.getDateFromStatusType(selectedRange.value);
const selectedRange = statusType.find((item) => item.isSelected);
calculatedDraftDate = DateUtils.getDateFromStatusType(selectedRange?.value ?? CONST.CUSTOM_STATUS_TYPES.NEVER);
}
User.updateDraftCustomStatus({clearAfter: calculatedDraftDate});
Navigation.goBack(ROUTES.SETTINGS_STATUS);
};

const updateMode = useCallback(
(mode) => {
(mode: StatusType) => {
if (mode.value === draftPeriod) {
return;
}
Expand All @@ -134,8 +135,8 @@ function StatusClearAfterPage({currentUserPersonalDetails, customStatus}) {
if (mode.value === CONST.CUSTOM_STATUS_TYPES.CUSTOM) {
User.updateDraftCustomStatus({clearAfter: DateUtils.getOneHourFromNow()});
} else {
const selectedRange = _.find(statusType, (item) => item.value === mode.value);
const calculatedDraftDate = DateUtils.getDateFromStatusType(selectedRange.value);
const selectedRange = statusType.find((item) => item.value === mode.value);
const calculatedDraftDate = DateUtils.getDateFromStatusType(selectedRange?.value ?? CONST.CUSTOM_STATUS_TYPES.NEVER);
User.updateDraftCustomStatus({clearAfter: calculatedDraftDate});
Navigation.goBack(ROUTES.SETTINGS_STATUS);
}
Expand All @@ -156,7 +157,7 @@ function StatusClearAfterPage({currentUserPersonalDetails, customStatus}) {

const timePeriodOptions = useCallback(
() =>
_.map(statusType, (item) => (
statusType.map((item) => (
<RadioListItem
item={item}
onSelectRow={() => updateMode(item)}
Expand Down Expand Up @@ -220,24 +221,9 @@ function StatusClearAfterPage({currentUserPersonalDetails, customStatus}) {
}

StatusClearAfterPage.displayName = 'StatusClearAfterPage';
StatusClearAfterPage.propTypes = propTypes;
StatusClearAfterPage.defaultProps = defaultProps;

export default compose(
withCurrentUserPersonalDetails,
withLocalize,
withOnyx({
timePeriodType: {
key: `${ONYXKEYS.FORMS.SETTINGS_STATUS_SET_CLEAR_AFTER_FORM}Draft`,
},
clearDateForm: {
key: `${ONYXKEYS.FORMS.SETTINGS_STATUS_CLEAR_DATE_FORM}Draft`,
},
customStatus: {
key: ONYXKEYS.CUSTOM_STATUS_DRAFT,
},
preferredLocale: {
key: ONYXKEYS.NVP_PREFERRED_LOCALE,
},
}),
)(StatusClearAfterPage);

export default withOnyx<StatusClearAfterPageProps, StatusClearAfterPageOnyxProps>({
customStatus: {
key: ONYXKEYS.CUSTOM_STATUS_DRAFT,
},
})(StatusClearAfterPage);

0 comments on commit bc2b2c4

Please sign in to comment.