Skip to content

Commit

Permalink
Merge pull request #12142 from Expensify/beaman-newDisplayNamePage
Browse files Browse the repository at this point in the history
Add new Display Name page
  • Loading branch information
stitesExpensify authored Nov 7, 2022
2 parents 0298f24 + 285467a commit ae3f424
Show file tree
Hide file tree
Showing 10 changed files with 188 additions and 2 deletions.
1 change: 0 additions & 1 deletion src/CONST.js
Original file line number Diff line number Diff line change
Expand Up @@ -826,7 +826,6 @@ const CONST = {
INVITE: 'invite',
LEAVE_ROOM: 'leaveRoom',
},
PROFILE_SETTINGS_FORM: 'profileSettingsForm',

// These split the maximum decimal value of a signed 64-bit number (9,223,372,036,854,775,807) into parts where none of them are too big to fit into a 32-bit number, so that we can
// generate them each with a random number generator with only 32-bits of precision.
Expand Down
2 changes: 2 additions & 0 deletions src/ONYXKEYS.js
Original file line number Diff line number Diff line change
Expand Up @@ -176,6 +176,8 @@ export default {
REQUEST_CALL_FORM: 'requestCallForm',
REIMBURSEMENT_ACCOUNT_FORM: 'reimbursementAccount',
WORKSPACE_SETTINGS_FORM: 'workspaceSettingsForm',
PROFILE_SETTINGS_FORM: 'profileSettingsForm',
DISPLAY_NAME_FORM: 'displayNameForm',
},

// Whether we should show the compose input or not
Expand Down
1 change: 1 addition & 0 deletions src/ROUTES.js
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,7 @@ export default {
HOME: '',
SETTINGS: 'settings',
SETTINGS_PROFILE: 'settings/profile',
SETTINGS_DISPLAY_NAME: 'settings/profile/display-name',
SETTINGS_PREFERENCES: 'settings/preferences',
SETTINGS_WORKSPACES: 'settings/workspaces',
SETTINGS_SECURITY: 'settings/security',
Expand Down
6 changes: 6 additions & 0 deletions src/languages/en.js
Original file line number Diff line number Diff line change
Expand Up @@ -303,6 +303,12 @@ export default {
offline: 'Offline',
syncing: 'Syncing',
},
displayNamePage: {
headerTitle: 'Display name',
isShownOnProfile: 'Your display name is shown on your profile.',
john: 'John',
doe: 'Doe',
},
addSecondaryLoginPage: {
addPhoneNumber: 'Add phone number',
addEmailAddress: 'Add email address',
Expand Down
6 changes: 6 additions & 0 deletions src/languages/es.js
Original file line number Diff line number Diff line change
Expand Up @@ -304,6 +304,12 @@ export default {
offline: 'Desconectado',
syncing: 'Sincronizando',
},
displayNamePage: {
headerTitle: 'Nombre',
isShownOnProfile: 'Este nombre es visible en su perfil.',
john: 'Juan',
doe: 'Nadie',
},
addSecondaryLoginPage: {
addPhoneNumber: 'Agregar número de teléfono',
addEmailAddress: 'Agregar dirección de email',
Expand Down
7 changes: 7 additions & 0 deletions src/libs/Navigation/AppNavigator/ModalStackNavigators.js
Original file line number Diff line number Diff line change
Expand Up @@ -217,6 +217,13 @@ const SettingsModalStackNavigator = createModalStackNavigator([
},
name: 'Settings_Profile',
},
{
getComponent: () => {
const SettingsDisplayNamePage = require('../../../pages/settings/Profile/DisplayNamePage').default;
return SettingsDisplayNamePage;
},
name: 'Settings_Display_Name',
},
{
getComponent: () => {
const SettingsAddSecondaryLoginPage = require('../../../pages/settings/AddSecondaryLoginPage').default;
Expand Down
4 changes: 4 additions & 0 deletions src/libs/Navigation/linkingConfig.js
Original file line number Diff line number Diff line change
Expand Up @@ -88,6 +88,10 @@ export default {
path: ROUTES.SETTINGS_PROFILE,
exact: true,
},
Settings_Display_Name: {
path: ROUTES.SETTINGS_DISPLAY_NAME,
exact: true,
},
Settings_About: {
path: ROUTES.SETTINGS_ABOUT,
exact: true,
Expand Down
33 changes: 33 additions & 0 deletions src/libs/actions/PersonalDetails.js
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,8 @@ import * as LoginUtils from '../LoginUtils';
import * as ReportUtils from '../ReportUtils';
import Growl from '../Growl';
import * as Localize from '../Localize';
import Navigation from '../Navigation/Navigation';
import ROUTES from '../../ROUTES';

let currentUserEmail = '';
Onyx.connect({
Expand Down Expand Up @@ -261,6 +263,12 @@ function setPersonalDetails(details, shouldGrowl) {
});
}

/**
* @param {String} firstName
* @param {String} lastName
* @param {String} pronouns
* @param {Object} timezone
*/
function updateProfile(firstName, lastName, pronouns, timezone) {
API.write('UpdateProfile', {
firstName,
Expand All @@ -287,6 +295,30 @@ function updateProfile(firstName, lastName, pronouns, timezone) {
});
}

/**
* @param {String} firstName
* @param {String} lastName
*/
function updateDisplayName(firstName, lastName) {
API.write('UpdateDisplayName', {firstName, lastName}, {
optimisticData: [{
onyxMethod: CONST.ONYX.METHOD.MERGE,
key: ONYXKEYS.PERSONAL_DETAILS,
value: {
[currentUserEmail]: {
firstName,
lastName,
displayName: getDisplayName(currentUserEmail, {
firstName,
lastName,
}),
},
},
}],
});
Navigation.navigate(ROUTES.SETTINGS_PROFILE);
}

/**
* Fetches the local currency based on location and sets currency code/symbol to Onyx
*/
Expand Down Expand Up @@ -399,5 +431,6 @@ export {
getMaxCharacterError,
extractFirstAndLastNameFromAvailableDetails,
updateProfile,
updateDisplayName,
clearAvatarErrors,
};
128 changes: 128 additions & 0 deletions src/pages/settings/Profile/DisplayNamePage.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,128 @@
import lodashGet from 'lodash/get';
import React, {Component} from 'react';
import {View} from 'react-native';
import withCurrentUserPersonalDetails, {withCurrentUserPersonalDetailsPropTypes, withCurrentUserPersonalDetailsDefaultProps} from '../../../components/withCurrentUserPersonalDetails';
import ScreenWrapper from '../../../components/ScreenWrapper';
import HeaderWithCloseButton from '../../../components/HeaderWithCloseButton';
import withLocalize, {withLocalizePropTypes} from '../../../components/withLocalize';
import * as Localize from '../../../libs/Localize';
import ROUTES from '../../../ROUTES';
import Form from '../../../components/Form';
import ONYXKEYS from '../../../ONYXKEYS';
import CONST from '../../../CONST';
import * as ValidationUtils from '../../../libs/ValidationUtils';
import TextInput from '../../../components/TextInput';
import Text from '../../../components/Text';
import styles from '../../../styles/styles';
import Navigation from '../../../libs/Navigation/Navigation';
import * as PersonalDetails from '../../../libs/actions/PersonalDetails';
import compose from '../../../libs/compose';

const propTypes = {
...withLocalizePropTypes,
...withCurrentUserPersonalDetailsPropTypes,
};

const defaultProps = {
...withCurrentUserPersonalDetailsDefaultProps,
};

class DisplayNamePage extends Component {
constructor(props) {
super(props);

this.validate = this.validate.bind(this);
this.updateDisplayName = this.updateDisplayName.bind(this);
}

/**
* Submit form to update user's first and last name (and display name)
* @param {Object} values
* @param {String} values.firstName
* @param {String} values.lastName
*/
updateDisplayName(values) {
PersonalDetails.updateDisplayName(
values.firstName.trim(),
values.lastName.trim(),
);
}

/**
* @param {Object} values
* @param {String} values.firstName
* @param {String} values.lastName
* @returns {Object} - An object containing the errors for each inputID
*/
validate(values) {
const errors = {};

const [hasFirstNameError, hasLastNameError] = ValidationUtils.doesFailCharacterLimitAfterTrim(
CONST.FORM_CHARACTER_LIMIT,
[values.firstName, values.lastName],
);

if (hasFirstNameError) {
errors.firstName = Localize.translateLocal('personalDetails.error.characterLimit', {limit: CONST.FORM_CHARACTER_LIMIT});
}

if (hasLastNameError) {
errors.lastName = Localize.translateLocal('personalDetails.error.characterLimit', {limit: CONST.FORM_CHARACTER_LIMIT});
}

return errors;
}

render() {
const currentUserDetails = this.props.currentUserPersonalDetails || {};

return (
<ScreenWrapper>
<HeaderWithCloseButton
title={this.props.translate('displayNamePage.headerTitle')}
shouldShowBackButton
onBackButtonPress={() => Navigation.navigate(ROUTES.SETTINGS_PROFILE)}
onCloseButtonPress={() => Navigation.dismissModal(true)}
/>
<Form
style={[styles.flexGrow1, styles.ph5]}
formID={ONYXKEYS.FORMS.DISPLAY_NAME_FORM}
validate={this.validate}
onSubmit={this.updateDisplayName}
submitButtonText={this.props.translate('common.save')}
enabledWhenOffline
>
<Text style={[styles.mb6]}>
{this.props.translate('displayNamePage.isShownOnProfile')}
</Text>
<View style={styles.mb4}>
<TextInput
inputID="firstName"
name="fname"
label={this.props.translate('common.firstName')}
defaultValue={lodashGet(currentUserDetails, 'firstName', '')}
placeholder={this.props.translate('displayNamePage.john')}
/>
</View>
<View>
<TextInput
inputID="lastName"
name="lname"
label={this.props.translate('common.lastName')}
defaultValue={lodashGet(currentUserDetails, 'lastName', '')}
placeholder={this.props.translate('displayNamePage.doe')}
/>
</View>
</Form>
</ScreenWrapper>
);
}
}

DisplayNamePage.propTypes = propTypes;
DisplayNamePage.defaultProps = defaultProps;

export default compose(
withLocalize,
withCurrentUserPersonalDetails,
)(DisplayNamePage);
2 changes: 1 addition & 1 deletion src/pages/settings/Profile/ProfilePage.js
Original file line number Diff line number Diff line change
Expand Up @@ -241,7 +241,7 @@ class ProfilePage extends Component {
/>
<Form
style={[styles.flexGrow1, styles.ph5]}
formID={CONST.PROFILE_SETTINGS_FORM}
formID={ONYXKEYS.FORMS.PROFILE_SETTINGS_FORM}
validate={this.validate}
onSubmit={this.updatePersonalDetails}
submitButtonText={this.props.translate('common.save')}
Expand Down

0 comments on commit ae3f424

Please sign in to comment.