PLT-7: Refactoring frontend (chunk 6)
- User settings
Этот коммит содержится в:
@@ -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);
|
||||
Ссылка в новой задаче
Block a user