summaryrefslogtreecommitdiffstats
path: root/web/react/components
diff options
context:
space:
mode:
Diffstat (limited to 'web/react/components')
-rw-r--r--web/react/components/admin_console/reset_password_modal.jsx5
-rw-r--r--web/react/components/create_comment.jsx2
-rw-r--r--web/react/components/create_post.jsx2
-rw-r--r--web/react/components/file_upload.jsx6
-rw-r--r--web/react/components/login.jsx17
-rw-r--r--web/react/components/password_reset_form.jsx13
-rw-r--r--web/react/components/posts_view.jsx5
-rw-r--r--web/react/components/sidebar.jsx4
-rw-r--r--web/react/components/signup_user_complete.jsx31
-rw-r--r--web/react/components/team_general_tab.jsx2
-rw-r--r--web/react/components/team_signup_password_page.jsx19
-rw-r--r--web/react/components/team_signup_username_page.jsx15
-rw-r--r--web/react/components/textbox.jsx67
-rw-r--r--web/react/components/unread_channel_indicator.jsx2
-rw-r--r--web/react/components/user_settings/manage_languages.jsx101
-rw-r--r--web/react/components/user_settings/user_settings_display.jsx46
-rw-r--r--web/react/components/user_settings/user_settings_general.jsx2
-rw-r--r--web/react/components/user_settings/user_settings_security.jsx6
-rw-r--r--web/react/components/view_image.jsx3
19 files changed, 256 insertions, 92 deletions
diff --git a/web/react/components/admin_console/reset_password_modal.jsx b/web/react/components/admin_console/reset_password_modal.jsx
index 5ff7c3413..bf7d5f7e5 100644
--- a/web/react/components/admin_console/reset_password_modal.jsx
+++ b/web/react/components/admin_console/reset_password_modal.jsx
@@ -2,6 +2,7 @@
// See License.txt for license information.
import * as Client from '../../utils/client.jsx';
+import Constants from '../../utils/constants.jsx';
var Modal = ReactBootstrap.Modal;
export default class ResetPasswordModal extends React.Component {
@@ -20,8 +21,8 @@ export default class ResetPasswordModal extends React.Component {
e.preventDefault();
var password = ReactDOM.findDOMNode(this.refs.password).value;
- if (!password || password.length < 5) {
- this.setState({serverError: 'Please enter at least 5 characters.'});
+ if (!password || password.length < Constants.MIN_PASSWORD_LENGTH) {
+ this.setState({serverError: 'Please enter at least ' + Constants.MIN_PASSWORD_LENGTH + ' characters.'});
return;
}
diff --git a/web/react/components/create_comment.jsx b/web/react/components/create_comment.jsx
index cae94429c..aa7ab6a7b 100644
--- a/web/react/components/create_comment.jsx
+++ b/web/react/components/create_comment.jsx
@@ -129,7 +129,7 @@ export default class CreateComment extends React.Component {
function handlePostError(err) {
let state = {};
- if (err.message === 'Invalid RootId parameter') {
+ if (err.id === 'api.post.create_post.root_id.app_error') {
PostStore.removePendingPost(post.channel_id, post.pending_post_id);
if ($('#post_deleted').length > 0) {
diff --git a/web/react/components/create_post.jsx b/web/react/components/create_post.jsx
index a476863a3..de971c43f 100644
--- a/web/react/components/create_post.jsx
+++ b/web/react/components/create_post.jsx
@@ -191,7 +191,7 @@ export default class CreatePost extends React.Component {
(err) => {
const state = {};
- if (err.message === 'Invalid RootId parameter') {
+ if (err.id === 'api.post.create_post.root_id.app_error') {
if ($('#post_deleted').length > 0) {
$('#post_deleted').modal('show');
}
diff --git a/web/react/components/file_upload.jsx b/web/react/components/file_upload.jsx
index fef253c52..7e6cc2942 100644
--- a/web/react/components/file_upload.jsx
+++ b/web/react/components/file_upload.jsx
@@ -151,7 +151,11 @@ export default class FileUpload extends React.Component {
});
}
- document.addEventListener('paste', function handlePaste(e) {
+ document.addEventListener('paste', (e) => {
+ if (!e.clipboardData) {
+ return;
+ }
+
var textarea = $(inputDiv.parentNode.parentNode).find('.custom-textarea')[0];
if (textarea !== e.target && !$.contains(textarea, e.target)) {
diff --git a/web/react/components/login.jsx b/web/react/components/login.jsx
index 1d9b3e906..6887489a7 100644
--- a/web/react/components/login.jsx
+++ b/web/react/components/login.jsx
@@ -7,6 +7,8 @@ import LoginLdap from './login_ldap.jsx';
import * as Utils from '../utils/utils.jsx';
import Constants from '../utils/constants.jsx';
+var FormattedMessage = ReactIntl.FormattedMessage;
+
export default class Login extends React.Component {
constructor(props) {
super(props);
@@ -86,7 +88,12 @@ export default class Login extends React.Component {
if (emailSignup) {
forgotPassword = (
<div className='form-group'>
- <a href={'/' + teamName + '/reset_password'}>{'I forgot my password'}</a>
+ <a href={'/' + teamName + '/reset_password'}>
+ <FormattedMessage
+ id='login.forgot_password'
+ defaultMessage='I forgot my password'
+ />
+ </a>
</div>
);
}
@@ -141,7 +148,13 @@ export default class Login extends React.Component {
{ldapLogin}
{userSignUp}
<div className='form-group margin--extra form-group--small'>
- <span><a href='/find_team'>{'Find your other teams'}</a></span>
+ <span>
+ <a href='/find_team'>
+ <FormattedMessage
+ id='login.find_teams'
+ defaultMessage='Find your other teams'
+ />
+ </a></span>
</div>
{forgotPassword}
{teamSignUp}
diff --git a/web/react/components/password_reset_form.jsx b/web/react/components/password_reset_form.jsx
index 812911569..8063db05a 100644
--- a/web/react/components/password_reset_form.jsx
+++ b/web/react/components/password_reset_form.jsx
@@ -1,7 +1,8 @@
// Copyright (c) 2015 Mattermost, Inc. All Rights Reserved.
// See License.txt for license information.
-import * as client from '../utils/client.jsx';
+import * as Client from '../utils/client.jsx';
+import Constants from '../utils/constants.jsx';
export default class PasswordResetForm extends React.Component {
constructor(props) {
@@ -16,8 +17,8 @@ export default class PasswordResetForm extends React.Component {
var state = {};
var password = ReactDOM.findDOMNode(this.refs.password).value.trim();
- if (!password || password.length < 5) {
- state.error = 'Please enter at least 5 characters.';
+ if (!password || password.length < Constants.MIN_PASSWORD_LENGTH) {
+ state.error = 'Please enter at least ' + Constants.MIN_PASSWORD_LENGTH + ' characters.';
this.setState(state);
return;
}
@@ -31,7 +32,7 @@ export default class PasswordResetForm extends React.Component {
data.data = this.props.data;
data.name = this.props.teamName;
- client.resetPassword(data,
+ Client.resetPassword(data,
function resetSuccess() {
this.setState({error: null, updateText: 'Your password has been updated successfully.'});
}.bind(this),
@@ -59,7 +60,7 @@ export default class PasswordResetForm extends React.Component {
return (
<div className='col-sm-12'>
<div className='signup-team__container'>
- <h3>Password Reset</h3>
+ <h3>{'Password Reset'}</h3>
<form onSubmit={this.handlePasswordReset}>
<p>{'Enter a new password for your ' + this.props.teamDisplayName + ' ' + global.window.mm_config.SiteName + ' account.'}</p>
<div className={formClass}>
@@ -77,7 +78,7 @@ export default class PasswordResetForm extends React.Component {
type='submit'
className='btn btn-primary'
>
- Change my password
+ {'Change my password'}
</button>
{updateText}
</form>
diff --git a/web/react/components/posts_view.jsx b/web/react/components/posts_view.jsx
index 7d8c7e265..856403af5 100644
--- a/web/react/components/posts_view.jsx
+++ b/web/react/components/posts_view.jsx
@@ -57,7 +57,10 @@ export default class PostsView extends React.Component {
this.setState({displayNameType: PreferenceStore.get(Preferences.CATEGORY_DISPLAY_SETTINGS, 'name_format', 'false')});
}
isAtBottom() {
- return ((this.refs.postlist.scrollHeight - this.refs.postlist.scrollTop) === this.refs.postlist.clientHeight);
+ // consider the view to be at the bottom if it's within this many pixels of the bottom
+ const atBottomMargin = 10;
+
+ return this.refs.postlist.clientHeight + this.refs.postlist.scrollTop >= this.refs.postlist.scrollHeight - atBottomMargin;
}
handleScroll() {
// HACK FOR RHS -- REMOVE WHEN RHS DIES
diff --git a/web/react/components/sidebar.jsx b/web/react/components/sidebar.jsx
index eaeb7bb91..c902731c9 100644
--- a/web/react/components/sidebar.jsx
+++ b/web/react/components/sidebar.jsx
@@ -506,9 +506,9 @@ export default class Sidebar extends React.Component {
link.rel = 'shortcut icon';
link.id = 'favicon';
if (this.badgesActive) {
- link.href = '/static/images/redfavicon.ico';
+ link.href = '/static/images/favicon/redfavicon-16x16.png';
} else {
- link.href = '/static/images/favicon.ico';
+ link.href = '/static/images/favicon/favicon-16x16.png';
}
var head = document.getElementsByTagName('head')[0];
var oldLink = document.getElementById('favicon');
diff --git a/web/react/components/signup_user_complete.jsx b/web/react/components/signup_user_complete.jsx
index df11fe045..ace0d28ae 100644
--- a/web/react/components/signup_user_complete.jsx
+++ b/web/react/components/signup_user_complete.jsx
@@ -5,6 +5,7 @@ import * as Utils from '../utils/utils.jsx';
import * as client from '../utils/client.jsx';
import UserStore from '../stores/user_store.jsx';
import BrowserStore from '../stores/browser_store.jsx';
+import Constants from '../utils/constants.jsx';
export default class SignupUserComplete extends React.Component {
constructor(props) {
@@ -51,7 +52,7 @@ export default class SignupUserComplete extends React.Component {
return;
} else if (usernameError) {
this.setState({
- nameError: 'Username must begin with a letter, and contain between 3 to 15 lowercase characters made up of numbers, letters, and the symbols \'.\', \'-\' and \'_\'.',
+ nameError: '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 \'_\'.',
emailError: '',
passwordError: '',
serverError: ''
@@ -60,8 +61,8 @@ export default class SignupUserComplete extends React.Component {
}
const providedPassword = ReactDOM.findDOMNode(this.refs.password).value.trim();
- if (!providedPassword || providedPassword.length < 5) {
- this.setState({nameError: '', emailError: '', passwordError: 'Please enter at least 5 characters', serverError: ''});
+ if (!providedPassword || providedPassword.length < Constants.MIN_PASSWORD_LENGTH) {
+ this.setState({nameError: '', emailError: '', passwordError: 'Please enter at least ' + Constants.MIN_PASSWORD_LENGTH + ' characters', serverError: ''});
return;
}
@@ -111,7 +112,7 @@ export default class SignupUserComplete extends React.Component {
client.track('signup', 'signup_user_01_welcome');
if (this.state.wizard === 'finished') {
- return <div>You've already completed the signup process for this invitation or this invitation has expired.</div>;
+ return <div>{"You've already completed the signup process for this invitation or this invitation has expired."}</div>;
}
// set up error labels
@@ -123,9 +124,11 @@ export default class SignupUserComplete extends React.Component {
}
var nameError = null;
+ var nameHelpText = <span className='help-block'>{'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 '_'"}</span>;
var nameDivStyle = 'form-group';
if (this.state.nameError) {
nameError = <label className='control-label'>{this.state.nameError}</label>;
+ nameHelpText = '';
nameDivStyle += ' has-error';
}
@@ -148,7 +151,7 @@ export default class SignupUserComplete extends React.Component {
// set up the email entry and hide it if an email was provided
var yourEmailIs = '';
if (this.state.user.email) {
- yourEmailIs = <span>Your email address is <strong>{this.state.user.email}</strong>. You'll use this address to sign in to {global.window.mm_config.SiteName}.</span>;
+ yourEmailIs = <span>{'Your email address is '}<strong>{this.state.user.email}</strong>{". You'll use this address to sign in to " + global.window.mm_config.SiteName + '.'}</span>;
}
var emailContainerStyle = 'margin--extra';
@@ -158,7 +161,7 @@ export default class SignupUserComplete extends React.Component {
var email = (
<div className={emailContainerStyle}>
- <h5><strong>What's your email address?</strong></h5>
+ <h5><strong>{"What's your email address?"}</strong></h5>
<div className={emailDivStyle}>
<input
type='email'
@@ -208,7 +211,7 @@ export default class SignupUserComplete extends React.Component {
{email}
{yourEmailIs}
<div className='margin--extra'>
- <h5><strong>Choose your username</strong></h5>
+ <h5><strong>{'Choose your username'}</strong></h5>
<div className={nameDivStyle}>
<input
type='text'
@@ -219,11 +222,11 @@ export default class SignupUserComplete extends React.Component {
spellCheck='false'
/>
{nameError}
- <span className='help-block'>Username must begin with a letter, and contain between 3 to 15 lowercase characters made up of numbers, letters, and the symbols '.', '-' and '_'</span>
+ {nameHelpText}
</div>
</div>
<div className='margin--extra'>
- <h5><strong>Choose your password</strong></h5>
+ <h5><strong>{'Choose your password'}</strong></h5>
<div className={passwordDivStyle}>
<input
type='password'
@@ -243,7 +246,7 @@ export default class SignupUserComplete extends React.Component {
onClick={this.handleSubmit}
className='btn-primary btn'
>
- Create Account
+ {'Create Account'}
</button>
</p>
</div>
@@ -255,7 +258,7 @@ export default class SignupUserComplete extends React.Component {
<div>
{signupMessage}
<div className='or__container'>
- <span>or</span>
+ <span>{'or'}</span>
</div>
</div>
);
@@ -268,10 +271,10 @@ export default class SignupUserComplete extends React.Component {
className='signup-team-logo'
src='/static/images/logo.png'
/>
- <h5 className='margin--less'>Welcome to:</h5>
+ <h5 className='margin--less'>{'Welcome to:'}</h5>
<h2 className='signup-team__name'>{this.props.teamDisplayName}</h2>
- <h2 className='signup-team__subdomain'>on {global.window.mm_config.SiteName}</h2>
- <h4 className='color--light'>Let's create your account</h4>
+ <h2 className='signup-team__subdomain'>{'on ' + global.window.mm_config.SiteName}</h2>
+ <h4 className='color--light'>{"Let's create your account"}</h4>
{signupMessage}
{emailSignup}
{serverError}
diff --git a/web/react/components/team_general_tab.jsx b/web/react/components/team_general_tab.jsx
index cc06a940e..b6fb3389f 100644
--- a/web/react/components/team_general_tab.jsx
+++ b/web/react/components/team_general_tab.jsx
@@ -66,7 +66,7 @@ export default class GeneralTab extends React.Component {
handleTeamListingRadio(listing) {
if (global.window.mm_config.EnableTeamListing !== 'true' && listing) {
- this.setState({clientError: 'Team directory has been disabled. Please ask a system admin to enable it.'});
+ this.setState({clientError: 'Team Directory has been disabled. Please ask a System Admin to enable the Team Directory in the System Console team settings.'});
} else {
this.setState({allow_team_listing: listing});
}
diff --git a/web/react/components/team_signup_password_page.jsx b/web/react/components/team_signup_password_page.jsx
index 378c7fe2c..7e11d38c3 100644
--- a/web/react/components/team_signup_password_page.jsx
+++ b/web/react/components/team_signup_password_page.jsx
@@ -4,6 +4,7 @@
import * as Client from '../utils/client.jsx';
import BrowserStore from '../stores/browser_store.jsx';
import UserStore from '../stores/user_store.jsx';
+import Constants from '../utils/constants.jsx';
export default class TeamSignupPasswordPage extends React.Component {
constructor(props) {
@@ -23,8 +24,8 @@ export default class TeamSignupPasswordPage extends React.Component {
e.preventDefault();
var password = ReactDOM.findDOMNode(this.refs.password).value.trim();
- if (!password || password.length < 5) {
- this.setState({passwordError: 'Please enter at least 5 characters'});
+ if (!password || password.length < Constants.MIN_PASSWORD_LENGTH) {
+ this.setState({passwordError: 'Please enter at least ' + Constants.MIN_PASSWORD_LENGTH + ' characters'});
return;
}
@@ -92,15 +93,15 @@ export default class TeamSignupPasswordPage extends React.Component {
className='signup-team-logo'
src='/static/images/logo.png'
/>
- <h2 className='margin--less'>Your password</h2>
- <h5 className='color--light'>Select a password that you'll use to login with your email address:</h5>
+ <h2 className='margin--less'>{'Your password'}</h2>
+ <h5 className='color--light'>{"Select a password that you'll use to login with your email address:"}</h5>
<div className='inner__content margin--extra'>
- <h5><strong>Email</strong></h5>
+ <h5><strong>{'Email'}</strong></h5>
<div className='block--gray form-group'>{this.props.state.team.email}</div>
<div className={passwordDivStyle}>
<div className='row'>
<div className='col-sm-11'>
- <h5><strong>Choose your password</strong></h5>
+ <h5><strong>{'Choose your password'}</strong></h5>
<input
autoFocus={true}
type='password'
@@ -110,7 +111,7 @@ export default class TeamSignupPasswordPage extends React.Component {
maxLength='128'
spellCheck='false'
/>
- <span className='color--light help-block'>Passwords must contain 5 to 50 characters. Your password will be strongest if it contains a mix of symbols, numbers, and upper and lowercase characters.</span>
+ <span className='color--light help-block'>{'Passwords must contain ' + Constants.MIN_PASSWORD_LENGTH + ' to ' + Constants.MAX_PASSWORD_LENGTH + ' characters. Your password will be strongest if it contains a mix of symbols, numbers, and upper and lowercase characters.'}</span>
</div>
</div>
{passwordError}
@@ -125,7 +126,7 @@ export default class TeamSignupPasswordPage extends React.Component {
data-loading-text={'<span class=\'glyphicon glyphicon-refresh glyphicon-refresh-animate\'></span> Creating team...'}
onClick={this.submitNext}
>
- Finish
+ {'Finish'}
</button>
</div>
<p>By proceeding to create your account and use {global.window.mm_config.SiteName}, you agree to our <a href='/static/help/terms.html'>Terms of Service</a> and <a href='/static/help/privacy.html'>Privacy Policy</a>. If you do not agree, you cannot use {global.window.mm_config.SiteName}.</p>
@@ -134,7 +135,7 @@ export default class TeamSignupPasswordPage extends React.Component {
href='#'
onClick={this.submitBack}
>
- Back to previous step
+ {'Back to previous step'}
</a>
</div>
</form>
diff --git a/web/react/components/team_signup_username_page.jsx b/web/react/components/team_signup_username_page.jsx
index de239f169..6ccab6656 100644
--- a/web/react/components/team_signup_username_page.jsx
+++ b/web/react/components/team_signup_username_page.jsx
@@ -3,6 +3,7 @@
import * as Utils from '../utils/utils.jsx';
import * as Client from '../utils/client.jsx';
+import Constants from '../utils/constants.jsx';
export default class TeamSignupUsernamePage extends React.Component {
constructor(props) {
@@ -33,7 +34,7 @@ export default class TeamSignupUsernamePage extends React.Component {
this.setState({nameError: 'This username is reserved, please choose a new one.'});
return;
} else if (usernameError) {
- this.setState({nameError: 'Username must begin with a letter, and contain 3 to 15 characters in total, which may be numbers, lowercase letters, or any of the symbols \'.\', \'-\', or \'_\''});
+ this.setState({nameError: 'Username must begin with a letter, and contain between ' + Constants.MIN_USERNAME_LENGTH + ' to ' + Constants.MAX_USERNAME_LENGTH + ' characters in total, which may be numbers, lowercase letters, or any of the symbols \'.\', \'-\', or \'_\''});
return;
}
@@ -45,9 +46,11 @@ export default class TeamSignupUsernamePage extends React.Component {
Client.track('signup', 'signup_team_06_username');
var nameError = null;
+ var nameHelpText = <span className='color--light help-block'>{'Usernames must begin with a letter and contain between ' + Constants.MIN_USERNAME_LENGTH + ' to ' + Constants.MAX_USERNAME_LENGTH + " characters made up of lowercase letters, numbers, and the symbols '.', '-' and '_'"}</span>;
var nameDivClass = 'form-group';
if (this.state.nameError) {
nameError = <label className='control-label'>{this.state.nameError}</label>;
+ nameHelpText = '';
nameDivClass += ' has-error';
}
@@ -58,13 +61,13 @@ export default class TeamSignupUsernamePage extends React.Component {
className='signup-team-logo'
src='/static/images/logo.png'
/>
- <h2 className='margin--less'>Your username</h2>
+ <h2 className='margin--less'>{'Your username'}</h2>
<h5 className='color--light'>{'Select a memorable username that makes it easy for teammates to identify you:'}</h5>
<div className='inner__content margin--extra'>
<div className={nameDivClass}>
<div className='row'>
<div className='col-sm-11'>
- <h5><strong>Choose your username</strong></h5>
+ <h5><strong>{'Choose your username'}</strong></h5>
<input
autoFocus={true}
type='text'
@@ -75,7 +78,7 @@ export default class TeamSignupUsernamePage extends React.Component {
maxLength='128'
spellCheck='false'
/>
- <span className='color--light help-block'>Usernames must begin with a letter and contain 3 to 15 characters made up of lowercase letters, numbers, and the symbols '.', '-' and '_'</span>
+ {nameHelpText}
</div>
</div>
{nameError}
@@ -86,7 +89,7 @@ export default class TeamSignupUsernamePage extends React.Component {
className='btn btn-primary margin--extra'
onClick={this.submitNext}
>
- Next
+ {'Next'}
<i className='glyphicon glyphicon-chevron-right'></i>
</button>
<div className='margin--extra'>
@@ -94,7 +97,7 @@ export default class TeamSignupUsernamePage extends React.Component {
href='#'
onClick={this.submitBack}
>
- Back to previous step
+ {'Back to previous step'}
</a>
</div>
</form>
diff --git a/web/react/components/textbox.jsx b/web/react/components/textbox.jsx
index b29f304ab..62c0d5218 100644
--- a/web/react/components/textbox.jsx
+++ b/web/react/components/textbox.jsx
@@ -22,8 +22,6 @@ export default class Textbox extends React.Component {
this.handleKeyPress = this.handleKeyPress.bind(this);
this.handleKeyDown = this.handleKeyDown.bind(this);
this.resize = this.resize.bind(this);
- this.handleFocus = this.handleFocus.bind(this);
- this.handleBlur = this.handleBlur.bind(this);
this.showPreview = this.showPreview.bind(this);
this.state = {
@@ -81,51 +79,43 @@ export default class Textbox extends React.Component {
}
resize() {
- const e = this.refs.message.getTextbox();
- const w = ReactDOM.findDOMNode(this.refs.wrapper);
+ const textbox = this.refs.message.getTextbox();
+ const $textbox = $(textbox);
+ const $wrapper = $(ReactDOM.findDOMNode(this.refs.wrapper));
- const prevHeight = $(e).height();
+ const padding = parseInt($textbox.css('padding-bottom'), 10) + parseInt($textbox.css('padding-top'), 10);
+ const borders = parseInt($textbox.css('border-bottom-width'), 10) + parseInt($textbox.css('border-top-width'), 10);
+ const maxHeight = parseInt($textbox.css('max-height'), 10) - borders;
- const lht = parseInt($(e).css('lineHeight'), 10);
- const lines = e.scrollHeight / lht;
- let mod = 15;
+ const prevHeight = $textbox.height();
- if (lines < 2.5 || this.props.messageText === '') {
- mod = 30;
- }
+ // set the height to auto and remove the scrollbar so we can get the actual size of the contents
+ $textbox.css('height', 'auto').css('overflow-y', 'hidden');
+
+ let height = textbox.scrollHeight - padding;
+
+ if (height + padding > maxHeight) {
+ height = maxHeight - padding;
- if (e.scrollHeight - mod < 167) {
- $(e).css({height: 'auto', 'overflow-y': 'hidden'}).height(e.scrollHeight - mod);
- $(w).css({height: 'auto'}).height(e.scrollHeight + 2);
- $(w).closest('.post-body__cell').removeClass('scroll');
- if (this.state.preview) {
- $(ReactDOM.findDOMNode(this.refs.preview)).css({height: 'auto', 'overflow-y': 'auto'}).height(e.scrollHeight - mod);
- }
+ // turn scrollbar on and move over attachment icon to compensate for that
+ $textbox.css('overflow-y', 'scroll');
+ $wrapper.closest('.post-body__cell').addClass('scroll');
} else {
- $(e).css({height: 'auto', 'overflow-y': 'scroll'}).height(167 - mod);
- $(w).css({height: 'auto'}).height(163);
- $(w).closest('.post-body__cell').addClass('scroll');
- if (this.state.preview) {
- $(ReactDOM.findDOMNode(this.refs.preview)).css({height: 'auto', 'overflow-y': 'scroll'}).height(163);
- }
+ $wrapper.closest('.post-body__cell').removeClass('scroll');
}
- if (prevHeight !== $(e).height() && this.props.onHeightChange) {
- this.props.onHeightChange();
- }
- }
+ // set the textarea to be the proper height
+ $textbox.height(height);
+
+ // set the wrapper height to match the height of the textbox including padding and borders
+ $wrapper.height(height + padding + borders);
- handleFocus() {
- const elm = this.refs.message.getTextbox();
- if (elm.title === elm.value) {
- elm.value = '';
+ if (this.state.preview) {
+ $(ReactDOM.findDOMNode(this.refs.preview)).height(height + borders);
}
- }
- handleBlur() {
- const elm = this.refs.message.getTextbox();
- if (elm.value === '') {
- elm.value = elm.title;
+ if (height !== prevHeight && this.props.onHeightChange) {
+ this.props.onHeightChange();
}
}
@@ -178,9 +168,6 @@ export default class Textbox extends React.Component {
onUserInput={this.props.onUserInput}
onKeyPress={this.handleKeyPress}
onKeyDown={this.handleKeyDown}
- onFocus={this.handleFocus}
- onBlur={this.handleBlur}
- onPaste={this.handlePaste}
style={{visibility: this.state.preview ? 'hidden' : 'visible'}}
listComponent={SuggestionList}
providers={this.suggestionProviders}
diff --git a/web/react/components/unread_channel_indicator.jsx b/web/react/components/unread_channel_indicator.jsx
index 6ae06528b..c0c34584f 100644
--- a/web/react/components/unread_channel_indicator.jsx
+++ b/web/react/components/unread_channel_indicator.jsx
@@ -10,7 +10,7 @@ export default class UnreadChannelIndicator extends React.Component {
render() {
let displayValue = 'none';
if (this.props.show) {
- displayValue = 'initial';
+ displayValue = 'block';
}
return (
<div
diff --git a/web/react/components/user_settings/manage_languages.jsx b/web/react/components/user_settings/manage_languages.jsx
new file mode 100644
index 000000000..123165b76
--- /dev/null
+++ b/web/react/components/user_settings/manage_languages.jsx
@@ -0,0 +1,101 @@
+// Copyright (c) 2015 Spinpunch, Inc. All Rights Reserved.
+// See License.txt for license information.
+
+import * as Client from '../../utils/client.jsx';
+import * as Utils from '../../utils/utils.jsx';
+
+export default class ManageLanguage extends React.Component {
+ constructor(props) {
+ super(props);
+
+ this.setupInitialState = this.setupInitialState.bind(this);
+ this.setLanguage = this.setLanguage.bind(this);
+ this.changeLanguage = this.changeLanguage.bind(this);
+ this.submitUser = this.submitUser.bind(this);
+ this.state = this.setupInitialState(props);
+ }
+ setupInitialState(props) {
+ var user = props.user;
+ return {
+ languages: Utils.languages(),
+ locale: user.locale
+ };
+ }
+ setLanguage(e) {
+ this.setState({locale: e.target.value});
+ }
+ changeLanguage(e) {
+ e.preventDefault();
+
+ var user = this.props.user;
+ var locale = this.state.locale;
+
+ user.locale = locale;
+
+ this.submitUser(user);
+ }
+ submitUser(user) {
+ Client.updateUser(user,
+ () => {
+ window.location.reload(true);
+ },
+ (err) => {
+ let serverError;
+ if (err.message) {
+ serverError = err.message;
+ } else {
+ serverError = err;
+ }
+ this.setState({serverError});
+ }
+ );
+ }
+ render() {
+ let serverError;
+ if (this.state.serverError) {
+ serverError = <label className='has-error'>{this.state.serverError}</label>;
+ }
+
+ const options = [];
+ this.state.languages.forEach((lang) => {
+ options.push(
+ <option
+ key={lang.value}
+ value={lang.value}
+ >
+ {lang.name}
+ </option>);
+ });
+
+ return (
+ <div key='changeLanguage'>
+ <br/>
+ <label className='control-label'>{'Change interface language'}</label>
+ <div className='padding-top'>
+ <select
+ ref='language'
+ className='form-control'
+ value={this.state.locale}
+ onChange={this.setLanguage}
+ >
+ {options}
+ </select>
+ {serverError}
+ <div className='padding-top'>
+ <a
+ className={'btn btn-sm btn-primary'}
+ href='#'
+ onClick={this.changeLanguage}
+ >
+ {'Set language'}
+ </a>
+ </div>
+ </div>
+ </div>
+ );
+ }
+}
+
+ManageLanguage.propTypes = {
+ user: React.PropTypes.object
+}; \ No newline at end of file
diff --git a/web/react/components/user_settings/user_settings_display.jsx b/web/react/components/user_settings/user_settings_display.jsx
index 1ff0a2913..f2c2502fb 100644
--- a/web/react/components/user_settings/user_settings_display.jsx
+++ b/web/react/components/user_settings/user_settings_display.jsx
@@ -5,7 +5,9 @@ import {savePreferences} from '../../utils/client.jsx';
import SettingItemMin from '../setting_item_min.jsx';
import SettingItemMax from '../setting_item_max.jsx';
import Constants from '../../utils/constants.jsx';
+const PreReleaseFeatures = Constants.PRE_RELEASE_FEATURES;
import PreferenceStore from '../../stores/preference_store.jsx';
+import ManageLanguages from './manage_languages.jsx';
import * as Utils from '../../utils/utils.jsx';
function getDisplayStateFromStores() {
@@ -78,6 +80,7 @@ export default class UserSettingsDisplay extends React.Component {
let clockSection;
let nameFormatSection;
let fontSection;
+ let languagesSection;
if (this.props.activeSection === 'clock') {
const clockFormat = [false, false];
@@ -292,6 +295,48 @@ export default class UserSettingsDisplay extends React.Component {
);
}
+ if (Utils.isFeatureEnabled(PreReleaseFeatures.LOC_PREVIEW)) {
+ if (this.props.activeSection === 'languages') {
+ var inputs = [];
+ inputs.push(
+ <ManageLanguages
+ user={this.props.user}
+ key='languages-ui'
+ />
+ );
+
+ languagesSection = (
+ <SettingItemMax
+ title={'Language'}
+ width='medium'
+ inputs={inputs}
+ updateSection={(e) => {
+ this.updateSection('');
+ e.preventDefault();
+ }}
+ />
+ );
+ } else {
+ var locale = 'English';
+ Utils.languages().forEach((l) => {
+ if (l.value === this.props.user.locale) {
+ locale = l.name;
+ }
+ });
+
+ languagesSection = (
+ <SettingItemMin
+ title={'Language'}
+ width='medium'
+ describe={locale}
+ updateSection={() => {
+ this.updateSection('languages');
+ }}
+ />
+ );
+ }
+ }
+
return (
<div>
<div className='modal-header'>
@@ -324,6 +369,7 @@ export default class UserSettingsDisplay extends React.Component {
<div className='divider-dark'/>
{nameFormatSection}
<div className='divider-dark'/>
+ {languagesSection}
</div>
</div>
);
diff --git a/web/react/components/user_settings/user_settings_general.jsx b/web/react/components/user_settings/user_settings_general.jsx
index 75a0e6b6a..df7ae4a25 100644
--- a/web/react/components/user_settings/user_settings_general.jsx
+++ b/web/react/components/user_settings/user_settings_general.jsx
@@ -47,7 +47,7 @@ export default class UserSettingsGeneralTab extends React.Component {
this.setState({clientError: 'This username is reserved, please choose a new one.'});
return;
} else if (usernameError) {
- this.setState({clientError: "Username must begin with a letter, and contain between 3 to 15 lowercase characters made up of numbers, letters, and the symbols '.', '-' and '_'."});
+ 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 '_'."});
return;
}
diff --git a/web/react/components/user_settings/user_settings_security.jsx b/web/react/components/user_settings/user_settings_security.jsx
index d1266dd3f..5a21abd19 100644
--- a/web/react/components/user_settings/user_settings_security.jsx
+++ b/web/react/components/user_settings/user_settings_security.jsx
@@ -48,8 +48,8 @@ export default class SecurityTab extends React.Component {
return;
}
- if (newPassword.length < 5) {
- this.setState({passwordError: 'New passwords must be at least 5 characters', serverError: ''});
+ if (newPassword.length < Constants.MIN_PASSWORD_LENGTH) {
+ this.setState({passwordError: 'New passwords must be at least ' + Constants.MIN_PASSWORD_LENGTH + ' characters', serverError: ''});
return;
}
@@ -337,7 +337,7 @@ export default class SecurityTab extends React.Component {
className='security-links theme'
dialogType={AccessHistoryModal}
>
- <i className='fa fa-clock-o'></i>View Access History
+ <i className='fa fa-clock-o'></i>{'View Access History'}
</ToggleModalButton>
<b> </b>
<ToggleModalButton
diff --git a/web/react/components/view_image.jsx b/web/react/components/view_image.jsx
index 31ec91248..d11f8a21c 100644
--- a/web/react/components/view_image.jsx
+++ b/web/react/components/view_image.jsx
@@ -211,7 +211,7 @@ export default class ViewImageModal extends React.Component {
}
const filename = this.props.filenames[this.state.imgId];
- const fileUrl = Utils.getFileUrl(filename);
+ const fileUrl = Utils.getFileUrl(filename, true);
var content;
if (this.state.loaded[this.state.imgId]) {
@@ -377,6 +377,7 @@ function ImagePreview({filename, fileUrl, fileInfo, maxHeight}) {
<a
href={fileUrl}
target='_blank'
+ download={true}
>
<img
style={{maxHeight}}