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

Add new Display Name page #12142

Merged
merged 12 commits into from
Nov 7, 2022
1 change: 0 additions & 1 deletion src/CONST.js
Original file line number Diff line number Diff line change
Expand Up @@ -824,7 +824,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 @@ -173,6 +173,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 @@ -302,6 +302,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 @@ -302,6 +302,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 @@ -259,6 +261,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 @@ -285,6 +293,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 @@ -397,5 +429,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