PLT-7: Refactoring frontend (chunk 6)

- User settings
Этот коммит содержится в:
Elias Nahum
2016-01-30 18:10:04 -03:00
родитель 7ff7c86461
Коммит 78314c7d4d
23 изменённых файлов: 2266 добавлений и 331 удалений

Просмотреть файл

@@ -13,7 +13,84 @@ import Constants from '../../utils/constants.jsx';
import * as AsyncClient from '../../utils/async_client.jsx';
import * as Utils from '../../utils/utils.jsx';
export default class UserSettingsGeneralTab extends React.Component {
import {intlShape, injectIntl, defineMessages, FormattedMessage} from 'mm-intl';
const holders = defineMessages({
usernameReserved: {
id: 'user.settings.general.usernameReserved',
defaultMessage: 'This username is reserved, please choose a new one.'
},
usernameRestrictions: {
id: 'user.settings.general.usernameRestrictions',
defaultMessage: "'Username must begin with a letter, and contain between {min} to {max} lowercase characters made up of numbers, letters, and the symbols '.', '-' and '_'."
},
validEmail: {
id: 'user.settings.general.validEmail',
defaultMessage: 'Please enter a valid email address'
},
emailMatch: {
id: 'user.settings.general.emailMatch',
defaultMessage: 'The new emails you entered do not match.'
},
checkEmail: {
id: 'user.settings.general.checkEmail',
defaultMessage: 'Check your email at {email} to verify the address.'
},
newAddress: {
id: 'user.settings.general.newAddress',
defaultMessage: 'New Address: {email}<br />Check your email to verify the above address.'
},
checkEmailNoAddress: {
id: 'user.settings.general.checkEmailNoAddress',
defaultMessage: 'Check your email to verify your new address'
},
loginGitlab: {
id: 'user.settings.general.loginGitlab',
defaultMessage: 'Log in done through GitLab'
},
validImage: {
id: 'user.settings.general.validImage',
defaultMessage: 'Only JPG or PNG images may be used for profile pictures'
},
imageTooLarge: {
id: 'user.settings.general.imageTooLarge',
defaultMessage: 'Unable to upload profile image. File is too large.'
},
uploadImage: {
id: 'user.settings.general.uploadImage',
defaultMessage: "Click 'Edit' to upload an image."
},
imageUpdated: {
id: 'user.settings.general.imageUpdated',
defaultMessage: 'Image last updated {date}'
},
fullName: {
id: 'user.settings.general.fullName',
defaultMessage: 'Full Name'
},
nickname: {
id: 'user.settings.general.nickname',
defaultMessage: 'Nickname'
},
username: {
id: 'user.settings.general.username',
defaultMessage: 'Username'
},
email: {
id: 'user.settings.general.email',
defaultMessage: 'Email'
},
profilePicture: {
id: 'user.settings.general.profilePicture',
defaultMessage: 'Profile Picture'
},
close: {
id: 'user.settings.general.close',
defaultMessage: 'Close'
}
});
class UserSettingsGeneralTab extends React.Component {
constructor(props) {
super(props);
this.submitActive = false;
@@ -42,12 +119,13 @@ export default class UserSettingsGeneralTab extends React.Component {
const user = Object.assign({}, this.props.user);
const username = this.state.username.trim().toLowerCase();
const {formatMessage} = this.props.intl;
const usernameError = Utils.isValidUsername(username);
if (usernameError === 'Cannot use a reserved word as a username.') {
this.setState({clientError: 'This username is reserved, please choose a new one.'});
this.setState({clientError: formatMessage(holders.usernameReserved)});
return;
} else if (usernameError) {
this.setState({clientError: 'Username must begin with a letter, and contain between ' + Constants.MIN_USERNAME_LENGTH + ' to ' + Constants.MAX_USERNAME_LENGTH + " lowercase characters made up of numbers, letters, and the symbols '.', '-' and '_'."});
this.setState({clientError: formatMessage(holders.usernameRestrictions, {min: Constants.MIN_USERNAME_LENGTH, max: Constants.MAX_USERNAME_LENGTH})});
return;
}
@@ -99,13 +177,14 @@ export default class UserSettingsGeneralTab extends React.Component {
const email = this.state.email.trim().toLowerCase();
const confirmEmail = this.state.confirmEmail.trim().toLowerCase();
const {formatMessage} = this.props.intl;
if (email === '' || !Utils.isEmail(email)) {
this.setState({emailError: 'Please enter a valid email address.', clientError: '', serverError: ''});
this.setState({emailError: formatMessage(holders.validEmail), clientError: '', serverError: ''});
return;
}
if (email !== confirmEmail) {
this.setState({emailError: 'The new emails you entered do not match.', clientError: '', serverError: ''});
this.setState({emailError: formatMessage(holders.emailMatch), clientError: '', serverError: ''});
return;
}
@@ -125,7 +204,7 @@ export default class UserSettingsGeneralTab extends React.Component {
const verificationEnabled = global.window.mm_config.SendEmailNotifications === 'true' && global.window.mm_config.RequireEmailVerification === 'true' && emailUpdated;
if (verificationEnabled) {
ErrorStore.storeLastError({message: 'Check your email at ' + user.email + ' to verify the address.'});
ErrorStore.storeLastError({message: this.props.intl.formatMessage(holders.checkEmail, {email: user.email})});
ErrorStore.emitChange();
this.setState({emailChangeInProgress: true});
}
@@ -152,13 +231,14 @@ export default class UserSettingsGeneralTab extends React.Component {
return;
}
const {formatMessage} = this.props.intl;
const picture = this.state.picture;
if (picture.type !== 'image/jpeg' && picture.type !== 'image/png') {
this.setState({clientError: 'Only JPG or PNG images may be used for profile pictures.'});
this.setState({clientError: formatMessage(holders.validImage)});
return;
} else if (picture.size > Constants.MAX_FILE_SIZE) {
this.setState({clientError: 'Unable to upload profile image. File is too large.'});
this.setState({clientError: formatMessage(holders.imageTooLarge)});
return;
}
@@ -221,6 +301,7 @@ export default class UserSettingsGeneralTab extends React.Component {
}
render() {
const user = this.props.user;
const {formatMessage, formatHTMLMessage} = this.props.intl;
let clientError = null;
if (this.state.clientError) {
@@ -244,7 +325,12 @@ export default class UserSettingsGeneralTab extends React.Component {
key='firstNameSetting'
className='form-group'
>
<label className='col-sm-5 control-label'>{'First Name'}</label>
<label className='col-sm-5 control-label'>
<FormattedMessage
id='user.settings.general.firstName'
defaultMessage='First Name'
/>
</label>
<div className='col-sm-7'>
<input
className='form-control'
@@ -261,7 +347,12 @@ export default class UserSettingsGeneralTab extends React.Component {
key='lastNameSetting'
className='form-group'
>
<label className='col-sm-5 control-label'>{'Last Name'}</label>
<label className='col-sm-5 control-label'>
<FormattedMessage
id='user.settings.general.lastName'
defaultMessage='Last Name'
/>
</label>
<div className='col-sm-7'>
<input
className='form-control'
@@ -284,20 +375,36 @@ export default class UserSettingsGeneralTab extends React.Component {
href='#'
onClick={notifClick.bind(this)}
>
{'Notifications'}
<FormattedMessage
id='user.settings.general.notificationsLink'
defaultMessage='Notifications'
/>
</a>
);
const extraInfo = (
<span>
{'By default, you will receive mention notifications when someone types your first name. '}
{'Go to '} {notifLink} {'settings to change this default.'}
<FormattedMessage
id='user.settings.general.notificationsExtra1'
defaultMessage='By default, you will receive mention notifications when someone types your first name. Go to '
>
{(intro) => (
<span key='intro'>
{intro}
{notifLink}
<FormattedMessage
id='user.settings.general.notificationsExtra2'
defaultMessage=' settings to change this default.'
/>
</span>
)}
</FormattedMessage>
</span>
);
nameSection = (
<SettingItemMax
title='Full Name'
title={formatMessage(holders.fullName)}
inputs={inputs}
submit={this.submitName}
server_error={serverError}
@@ -322,7 +429,7 @@ export default class UserSettingsGeneralTab extends React.Component {
nameSection = (
<SettingItemMin
title='Full Name'
title={formatMessage(holders.fullName)}
describe={fullName}
updateSection={() => {
this.updateSection('name');
@@ -333,7 +440,12 @@ export default class UserSettingsGeneralTab extends React.Component {
let nicknameSection;
if (this.props.activeSection === 'nickname') {
let nicknameLabel = 'Nickname';
let nicknameLabel = (
<FormattedMessage
id='user.settings.general.nickname'
defaultMessage='Nickname'
/>
);
if (Utils.isMobile()) {
nicknameLabel = '';
}
@@ -357,13 +469,16 @@ export default class UserSettingsGeneralTab extends React.Component {
const extraInfo = (
<span>
{'Use Nickname for a name you might be called that is different from your first name and username. This is most often used when two or more people have similar sounding names and usernames.'}
<FormattedMessage
id='user.settings.general.nicknameExtra'
defaultMessage='Use Nickname for a name you might be called that is different from your first name and username. This is most often used when two or more people have similar sounding names and usernames.'
/>
</span>
);
nicknameSection = (
<SettingItemMax
title='Nickname'
title={formatMessage(holders.nickname)}
inputs={inputs}
submit={this.submitNickname}
server_error={serverError}
@@ -378,7 +493,7 @@ export default class UserSettingsGeneralTab extends React.Component {
} else {
nicknameSection = (
<SettingItemMin
title='Nickname'
title={formatMessage(holders.nickname)}
describe={UserStore.getCurrentUser().nickname}
updateSection={() => {
this.updateSection('nickname');
@@ -389,7 +504,12 @@ export default class UserSettingsGeneralTab extends React.Component {
let usernameSection;
if (this.props.activeSection === 'username') {
let usernameLabel = 'Username';
let usernameLabel = (
<FormattedMessage
id='user.settings.general.username'
defaultMessage='Username'
/>
);
if (Utils.isMobile()) {
usernameLabel = '';
}
@@ -411,11 +531,18 @@ export default class UserSettingsGeneralTab extends React.Component {
</div>
);
const extraInfo = (<span>{'Pick something easy for teammates to recognize and recall.'}</span>);
const extraInfo = (
<span>
<FormattedMessage
id='user.settings.general.usernameInfo'
defaultMessage='Pick something easy for teammates to recognize and recall.'
/>
</span>
);
usernameSection = (
<SettingItemMax
title='Username'
title={formatMessage(holders.username)}
inputs={inputs}
submit={this.submitUsername}
server_error={serverError}
@@ -430,7 +557,7 @@ export default class UserSettingsGeneralTab extends React.Component {
} else {
usernameSection = (
<SettingItemMin
title='Username'
title={formatMessage(holders.username)}
describe={UserStore.getCurrentUser().username}
updateSection={() => {
this.updateSection('username');
@@ -443,16 +570,41 @@ export default class UserSettingsGeneralTab extends React.Component {
if (this.props.activeSection === 'email') {
const emailEnabled = global.window.mm_config.SendEmailNotifications === 'true';
const emailVerificationEnabled = global.window.mm_config.RequireEmailVerification === 'true';
let helpText = 'Email is used for sign-in, notifications, and password reset. Email requires verification if changed.';
let helpText = (
<FormattedMessage
id='user.settings.general.emailHelp1'
defaultMessage='Email is used for sign-in, notifications, and password reset. Email requires verification if changed.'
/>
);
if (!emailEnabled) {
helpText = <div className='setting-list__hint text-danger'>{'Email has been disabled by your system administrator. No notification emails will be sent until it is enabled.'}</div>;
helpText = (
<div className='setting-list__hint text-danger'>
<FormattedMessage
id='user.settings.general.emailHelp2'
defaultMessage='Email has been disabled by your system administrator. No notification emails will be sent until it is enabled.'
/>
</div>
);
} else if (!emailVerificationEnabled) {
helpText = 'Email is used for sign-in, notifications, and password reset.';
helpText = (
<FormattedMessage
id='user.settings.general.emailHelp3'
defaultMessage='Email is used for sign-in, notifications, and password reset.'
/>
);
} else if (this.state.emailChangeInProgress) {
const newEmail = UserStore.getCurrentUser().email;
if (newEmail) {
helpText = 'A verification email was sent to ' + newEmail + '.';
helpText = (
<FormattedMessage
id='user.settings.general.emailHelp4'
defaultMessage='A verification email was sent to {email}.'
values={{
email: newEmail
}}
/>
);
}
}
@@ -462,7 +614,12 @@ export default class UserSettingsGeneralTab extends React.Component {
inputs.push(
<div key='emailSetting'>
<div className='form-group'>
<label className='col-sm-5 control-label'>{'Primary Email'}</label>
<label className='col-sm-5 control-label'>
<FormattedMessage
id='user.settings.general.primaryEmail'
defaultMessage='Primary Email'
/>
</label>
<div className='col-sm-7'>
<input
className='form-control'
@@ -478,7 +635,12 @@ export default class UserSettingsGeneralTab extends React.Component {
inputs.push(
<div key='confirmEmailSetting'>
<div className='form-group'>
<label className='col-sm-5 control-label'>{'Confirm Email'}</label>
<label className='col-sm-5 control-label'>
<FormattedMessage
id='user.settings.general.confirmEmail'
defaultMessage='Confirm Email'
/>
</label>
<div className='col-sm-7'>
<input
className='form-control'
@@ -499,7 +661,12 @@ export default class UserSettingsGeneralTab extends React.Component {
key='oauthEmailInfo'
className='form-group'
>
<div className='setting-list__hint'>{'Log in occurs through GitLab. Email cannot be updated.'}</div>
<div className='setting-list__hint'>
<FormattedMessage
id='user.settings.general.emailCantUpdate'
defaultMessage='Log in occurs through GitLab. Email cannot be updated.'
/>
</div>
{helpText}
</div>
);
@@ -524,20 +691,20 @@ export default class UserSettingsGeneralTab extends React.Component {
if (this.state.emailChangeInProgress) {
const newEmail = UserStore.getCurrentUser().email;
if (newEmail) {
describe = 'New Address: ' + newEmail + '\nCheck your email to verify the above address.';
describe = formatHTMLMessage(holders.newAddress, {email: newEmail});
} else {
describe = 'Check your email to verify your new address';
describe = formatMessage(holders.checkEmailNoAddress);
}
} else {
describe = UserStore.getCurrentUser().email;
}
} else if (this.props.user.auth_service === Constants.GITLAB_SERVICE) {
describe = 'Log in done through GitLab';
describe = formatMessage(holders.loginGitlab);
}
emailSection = (
<SettingItemMin
title='Email'
title={formatMessage(holders.email)}
describe={describe}
updateSection={() => {
this.updateSection('email');
@@ -550,7 +717,7 @@ export default class UserSettingsGeneralTab extends React.Component {
if (this.props.activeSection === 'picture') {
pictureSection = (
<SettingPicture
title='Profile Picture'
title={formatMessage(holders.profilePicture)}
submit={this.submitPicture}
src={'/api/v1/users/' + user.id + '/image?time=' + user.last_picture_update + '&' + Utils.getSessionIndex()}
server_error={serverError}
@@ -566,13 +733,15 @@ export default class UserSettingsGeneralTab extends React.Component {
/>
);
} else {
let minMessage = 'Click \'Edit\' to upload an image.';
let minMessage = formatMessage(holders.uploadImage);
if (user.last_picture_update) {
minMessage = 'Image last updated ' + Utils.displayDate(user.last_picture_update);
minMessage = formatMessage(holders.imageUpdated, {
date: new Date(user.last_picture_update).toLocaleDateString(global.window.mm_locale, {month: 'short', day: '2-digit', year: 'numeric'})
});
}
pictureSection = (
<SettingItemMin
title='Profile Picture'
title={formatMessage(holders.profilePicture)}
describe={minMessage}
updateSection={() => {
this.updateSection('picture');
@@ -588,7 +757,7 @@ export default class UserSettingsGeneralTab extends React.Component {
type='button'
className='close'
data-dismiss='modal'
aria-label='Close'
aria-label={formatMessage(holders.close)}
onClick={this.props.closeModal}
>
<span aria-hidden='true'>{'×'}</span>
@@ -601,11 +770,19 @@ export default class UserSettingsGeneralTab extends React.Component {
className='modal-back'
onClick={this.props.collapseModal}
/>
{'General Settings'}
<FormattedMessage
id='user.settings.general.title'
defaultMessage='General Settings'
/>
</h4>
</div>
<div className='user-settings'>
<h3 className='tab-header'>{'General Settings'}</h3>
<h3 className='tab-header'>
<FormattedMessage
id='user.settings.general.title'
defaultMessage='General Settings'
/>
</h3>
<div className='divider-dark first'/>
{nameSection}
<div className='divider-light'/>
@@ -624,6 +801,7 @@ export default class UserSettingsGeneralTab extends React.Component {
}
UserSettingsGeneralTab.propTypes = {
intl: intlShape.isRequired,
user: React.PropTypes.object.isRequired,
updateSection: React.PropTypes.func.isRequired,
updateTab: React.PropTypes.func.isRequired,
@@ -631,3 +809,5 @@ UserSettingsGeneralTab.propTypes = {
closeModal: React.PropTypes.func.isRequired,
collapseModal: React.PropTypes.func.isRequired
};
export default injectIntl(UserSettingsGeneralTab);