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/team_settings.jsx34
-rw-r--r--web/react/components/edit_channel_modal.jsx11
-rw-r--r--web/react/components/edit_channel_purpose_modal.jsx6
-rw-r--r--web/react/components/edit_post_modal.jsx21
-rw-r--r--web/react/components/invite_member_modal.jsx3
-rw-r--r--web/react/components/login.jsx59
-rw-r--r--web/react/components/navbar_dropdown.jsx2
-rw-r--r--web/react/components/sidebar_right_menu.jsx3
-rw-r--r--web/react/components/signup_team.jsx42
-rw-r--r--web/react/components/team_general_tab.jsx372
-rw-r--r--web/react/components/team_settings.jsx8
-rw-r--r--web/react/components/team_settings_modal.jsx2
-rw-r--r--web/react/components/user_settings/code_theme_chooser.jsx55
-rw-r--r--web/react/components/user_settings/custom_theme_chooser.jsx5
-rw-r--r--web/react/components/user_settings/user_settings_appearance.jsx25
15 files changed, 591 insertions, 57 deletions
diff --git a/web/react/components/admin_console/team_settings.jsx b/web/react/components/admin_console/team_settings.jsx
index 9ecd14a1e..6587184ea 100644
--- a/web/react/components/admin_console/team_settings.jsx
+++ b/web/react/components/admin_console/team_settings.jsx
@@ -32,6 +32,7 @@ export default class TeamSettings extends React.Component {
config.TeamSettings.EnableTeamCreation = ReactDOM.findDOMNode(this.refs.EnableTeamCreation).checked;
config.TeamSettings.EnableUserCreation = ReactDOM.findDOMNode(this.refs.EnableUserCreation).checked;
config.TeamSettings.RestrictTeamNames = ReactDOM.findDOMNode(this.refs.RestrictTeamNames).checked;
+ config.TeamSettings.EnableTeamListing = ReactDOM.findDOMNode(this.refs.EnableTeamListing).checked;
var MaxUsersPerTeam = 50;
if (!isNaN(parseInt(ReactDOM.findDOMNode(this.refs.MaxUsersPerTeam).value, 10))) {
@@ -243,6 +244,39 @@ export default class TeamSettings extends React.Component {
</div>
<div className='form-group'>
+ <label
+ className='control-label col-sm-4'
+ htmlFor='EnableTeamListing'
+ >
+ {'Enable Team Directory: '}
+ </label>
+ <div className='col-sm-8'>
+ <label className='radio-inline'>
+ <input
+ type='radio'
+ name='EnableTeamListing'
+ value='true'
+ ref='EnableTeamListing'
+ defaultChecked={this.props.config.TeamSettings.EnableTeamListing}
+ onChange={this.handleChange}
+ />
+ {'true'}
+ </label>
+ <label className='radio-inline'>
+ <input
+ type='radio'
+ name='EnableTeamListing'
+ value='false'
+ defaultChecked={!this.props.config.TeamSettings.EnableTeamListing}
+ onChange={this.handleChange}
+ />
+ {'false'}
+ </label>
+ <p className='help-text'>{'When true, teams that are configured to show in team directory will show on main page inplace of creating a new team.'}</p>
+ </div>
+ </div>
+
+ <div className='form-group'>
<div className='col-sm-12'>
{serverError}
<button
diff --git a/web/react/components/edit_channel_modal.jsx b/web/react/components/edit_channel_modal.jsx
index 6f3826f75..5b3c74e82 100644
--- a/web/react/components/edit_channel_modal.jsx
+++ b/web/react/components/edit_channel_modal.jsx
@@ -73,7 +73,7 @@ export default class EditChannelModal extends React.Component {
className='modal-title'
ref='title'
>
- Edit Header
+ {'Edit Header'}
</h4>
);
if (this.state.title) {
@@ -82,7 +82,7 @@ export default class EditChannelModal extends React.Component {
className='modal-title'
ref='title'
>
- Edit Header for <span className='name'>{this.state.title}</span>
+ {'Edit Header for '}<span className='name'>{this.state.title}</span>
</h4>
);
}
@@ -105,11 +105,12 @@ export default class EditChannelModal extends React.Component {
data-dismiss='modal'
aria-label='Close'
>
- <span aria-hidden='true'>&times;</span>
+ <span aria-hidden='true'>{'×'}</span>
</button>
{editTitle}
</div>
<div className='modal-body'>
+ <p>{'Edit the text appearing next to the channel name in the channel header.'}</p>
<textarea
className='form-control no-resize'
rows='6'
@@ -125,14 +126,14 @@ export default class EditChannelModal extends React.Component {
className='btn btn-default'
data-dismiss='modal'
>
- Cancel
+ {'Cancel'}
</button>
<button
type='button'
className='btn btn-primary'
onClick={this.handleEdit}
>
- Save
+ {'Save'}
</button>
</div>
</div>
diff --git a/web/react/components/edit_channel_purpose_modal.jsx b/web/react/components/edit_channel_purpose_modal.jsx
index d8102642e..4d162cfe7 100644
--- a/web/react/components/edit_channel_purpose_modal.jsx
+++ b/web/react/components/edit_channel_purpose_modal.jsx
@@ -69,6 +69,11 @@ export default class EditChannelPurposeModal extends React.Component {
title = <span>{'Edit Purpose for '}<span className='name'>{this.props.channel.display_name}</span></span>;
}
+ let channelTerm = 'Channel';
+ if (this.props.channel.channelType === 'P') {
+ channelTerm = 'Group';
+ }
+
return (
<Modal
className='modal-edit-channel-purpose'
@@ -81,6 +86,7 @@ export default class EditChannelPurposeModal extends React.Component {
</Modal.Title>
</Modal.Header>
<Modal.Body>
+ <p>{`Describe how this ${channelTerm} should be used.`}</p>
<textarea
ref='purpose'
className='form-control no-resize'
diff --git a/web/react/components/edit_post_modal.jsx b/web/react/components/edit_post_modal.jsx
index e5bede026..2abb3f151 100644
--- a/web/react/components/edit_post_modal.jsx
+++ b/web/react/components/edit_post_modal.jsx
@@ -6,6 +6,10 @@ var AsyncClient = require('../utils/async_client.jsx');
var Textbox = require('./textbox.jsx');
var BrowserStore = require('../stores/browser_store.jsx');
var PostStore = require('../stores/post_store.jsx');
+var PreferenceStore = require('../stores/preference_store.jsx');
+
+var Constants = require('../utils/constants.jsx');
+var KeyCodes = Constants.KeyCodes;
export default class EditPostModal extends React.Component {
constructor() {
@@ -16,6 +20,8 @@ export default class EditPostModal extends React.Component {
this.handleEditKeyPress = this.handleEditKeyPress.bind(this);
this.handleUserInput = this.handleUserInput.bind(this);
this.handleEditPostEvent = this.handleEditPostEvent.bind(this);
+ this.handleKeyDown = this.handleKeyDown.bind(this);
+ this.onPreferenceChange = this.onPreferenceChange.bind(this);
this.state = {editText: '', title: '', post_id: '', channel_id: '', comments: 0, refocusId: ''};
}
@@ -51,7 +57,7 @@ export default class EditPostModal extends React.Component {
this.setState({editText: editMessage});
}
handleEditKeyPress(e) {
- if (e.which === 13 && !e.shiftKey && !e.altKey) {
+ if (this.state.ctrlSend === 'false' && e.which === KeyCodes.ENTER && !e.shiftKey && !e.altKey) {
e.preventDefault();
ReactDOM.findDOMNode(this.refs.editbox).blur();
this.handleEdit(e);
@@ -72,6 +78,16 @@ export default class EditPostModal extends React.Component {
$(ReactDOM.findDOMNode(this.refs.modal)).modal('show');
}
+ handleKeyDown(e) {
+ if (this.state.ctrlSend === 'true' && e.keyCode === KeyCodes.ENTER && e.ctrlKey === true) {
+ this.handleEdit(e);
+ }
+ }
+ onPreferenceChange() {
+ this.setState({
+ ctrlSend: PreferenceStore.getPreference(Constants.Preferences.CATEGORY_ADVANCED_SETTINGS, 'send_on_ctrl_enter', {value: 'false'}).value
+ });
+ }
componentDidMount() {
var self = this;
@@ -101,9 +117,11 @@ export default class EditPostModal extends React.Component {
});
PostStore.addEditPostListener(this.handleEditPostEvent);
+ PreferenceStore.addChangeListener(this.onPreferenceChange);
}
componentWillUnmount() {
PostStore.removeEditPostListener(this.handleEditPostEvent);
+ PreferenceStore.removeChangeListener(this.onPreferenceChange);
}
render() {
var error = (<div className='form-group'><br /></div>);
@@ -138,6 +156,7 @@ export default class EditPostModal extends React.Component {
<Textbox
onUserInput={this.handleEditInput}
onKeyPress={this.handleEditKeyPress}
+ onKeyDown={this.handleKeyDown}
messageText={this.state.editText}
createMessage='Edit the post...'
id='edit_textbox'
diff --git a/web/react/components/invite_member_modal.jsx b/web/react/components/invite_member_modal.jsx
index 86a4b04cf..bea700725 100644
--- a/web/react/components/invite_member_modal.jsx
+++ b/web/react/components/invite_member_modal.jsx
@@ -4,6 +4,7 @@
var utils = require('../utils/utils.jsx');
var Client = require('../utils/client.jsx');
var UserStore = require('../stores/user_store.jsx');
+var TeamStore = require('../stores/team_store.jsx');
var ConfirmModal = require('./confirm_modal.jsx');
export default class InviteMemberModal extends React.Component {
@@ -292,7 +293,7 @@ export default class InviteMemberModal extends React.Component {
} else {
var teamInviteLink = null;
if (currentUser && this.props.teamType === 'O') {
- var linkUrl = utils.getWindowLocationOrigin() + '/signup_user_complete/?id=' + currentUser.team_id;
+ var linkUrl = utils.getWindowLocationOrigin() + '/signup_user_complete/?id=' + TeamStore.getCurrent().invite_id;
var link =
(
<a
diff --git a/web/react/components/login.jsx b/web/react/components/login.jsx
index 108735caf..c519959af 100644
--- a/web/react/components/login.jsx
+++ b/web/react/components/login.jsx
@@ -101,7 +101,7 @@ export default class Login extends React.Component {
href={'/' + teamName + '/login/gitlab'}
>
<span className='icon' />
- <span>with GitLab</span>
+ <span>{'with GitLab'}</span>
</a>
);
}
@@ -154,7 +154,7 @@ export default class Login extends React.Component {
type='submit'
className='btn btn-primary'
>
- Sign in
+ {'Sign in'}
</button>
</div>
</div>
@@ -166,7 +166,7 @@ export default class Login extends React.Component {
<div>
{loginMessage}
<div className='or__container'>
- <span>or</span>
+ <span>{'or'}</span>
</div>
</div>
);
@@ -176,16 +176,48 @@ 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'}>{'I forgot my password'}</a>
+ </div>
+ );
+ }
+
+ let userSignUp = null;
+ if (this.props.inviteId) {
+ userSignUp = (
+ <div>
+ <span>{'Do not have an account? '}
+ <a
+ href={'/signup_user_complete/?id=' + this.props.inviteId}
+ className='signup-team-login'
+ >
+ {'Create one now'}
+ </a>
+ </span>
+ </div>
+ );
+ }
+
+ let teamSignUp = null;
+ if (global.window.mm_config.EnableTeamCreation === 'true') {
+ teamSignUp = (
+ <div className='margin--extra'>
+ <span>{'Want to create your own team? '}
+ <a
+ href='/'
+ className='signup-team-login'
+ >
+ {'Sign up now'}
+ </a>
+ </span>
</div>
);
}
return (
<div className='signup-team__container'>
- <h5 className='margin--less'>Sign in to:</h5>
+ <h5 className='margin--less'>{'Sign in to:'}</h5>
<h2 className='signup-team__name'>{teamDisplayName}</h2>
- <h2 className='signup-team__subdomain'>on {global.window.mm_config.SiteName}</h2>
+ <h2 className='signup-team__subdomain'>{'on '}{global.window.mm_config.SiteName}</h2>
<form onSubmit={this.handleSubmit}>
{verifiedBox}
<div className={'form-group' + errorClass}>
@@ -193,20 +225,12 @@ export default class Login extends React.Component {
</div>
{loginMessage}
{emailSignup}
+ {userSignUp}
<div className='form-group margin--extra form-group--small'>
<span><a href='/find_team'>{'Find other teams'}</a></span>
</div>
{forgotPassword}
- <div className='margin--extra'>
- <span>{'Want to create your own team? '}
- <a
- href='/'
- className='signup-team-login'
- >
- Sign up now
- </a>
- </span>
- </div>
+ {teamSignUp}
</form>
</div>
);
@@ -219,5 +243,6 @@ Login.defaultProps = {
};
Login.propTypes = {
teamName: React.PropTypes.string,
- teamDisplayName: React.PropTypes.string
+ teamDisplayName: React.PropTypes.string,
+ inviteId: React.PropTypes.string
};
diff --git a/web/react/components/navbar_dropdown.jsx b/web/react/components/navbar_dropdown.jsx
index 2b0f3c40e..dc21fad21 100644
--- a/web/react/components/navbar_dropdown.jsx
+++ b/web/react/components/navbar_dropdown.jsx
@@ -112,7 +112,7 @@ export default class NavbarDropdown extends React.Component {
data-toggle='modal'
data-target='#get_link'
data-title='Team Invite'
- data-value={Utils.getWindowLocationOrigin() + '/signup_user_complete/?id=' + currentUser.team_id}
+ data-value={Utils.getWindowLocationOrigin() + '/signup_user_complete/?id=' + TeamStore.getCurrent().invite_id}
>
{'Get Team Invite Link'}
</a>
diff --git a/web/react/components/sidebar_right_menu.jsx b/web/react/components/sidebar_right_menu.jsx
index fddc98c9d..9350bbd42 100644
--- a/web/react/components/sidebar_right_menu.jsx
+++ b/web/react/components/sidebar_right_menu.jsx
@@ -2,6 +2,7 @@
// See License.txt for license information.
var UserStore = require('../stores/user_store.jsx');
+var TeamStore = require('../stores/team_store.jsx');
var client = require('../utils/client.jsx');
var utils = require('../utils/utils.jsx');
@@ -51,7 +52,7 @@ export default class SidebarRightMenu extends React.Component {
data-toggle='modal'
data-target='#get_link'
data-title='Team Invite'
- data-value={utils.getWindowLocationOrigin() + '/signup_user_complete/?id=' + currentUser.team_id}
+ data-value={utils.getWindowLocationOrigin() + '/signup_user_complete/?id=' + TeamStore.getCurrent().invite_id}
><i className='glyphicon glyphicon-link'></i>Get Team Invite Link</a>
</li>
);
diff --git a/web/react/components/signup_team.jsx b/web/react/components/signup_team.jsx
index 1858703ef..f926f5cbb 100644
--- a/web/react/components/signup_team.jsx
+++ b/web/react/components/signup_team.jsx
@@ -12,6 +12,11 @@ export default class TeamSignUp extends React.Component {
this.updatePage = this.updatePage.bind(this);
+ if (global.window.mm_config.EnableTeamListing === 'true') {
+ this.state = {page: 'team_listing'};
+ return;
+ }
+
var count = 0;
if (global.window.mm_config.EnableSignUpWithEmail === 'true') {
@@ -36,6 +41,38 @@ export default class TeamSignUp extends React.Component {
}
render() {
+ if (this.state.page === 'team_listing') {
+ return (
+ <div>
+ <h3>{'Choose a Team'}</h3>
+ <div className='signup-team-all'>
+ {
+ this.props.teams.map((team) => {
+ return (
+ <div
+ key={'team_' + team.name}
+ className='signup-team-dir'
+ >
+ <a
+ href={'/' + team.name}
+ >
+ <div className='signup-team-dir__group'>
+ <span className='signup-team-dir__name'>{team.display_name}</span>
+ <span
+ className='glyphicon glyphicon-menu-right right signup-team-dir__arrow'
+ aria-hidden='true'
+ />
+ </div>
+ </a>
+ </div>
+ );
+ })
+ }
+ </div>
+ </div>
+ );
+ }
+
if (this.state.page === 'choose') {
return (
<ChoosePage
@@ -51,3 +88,8 @@ export default class TeamSignUp extends React.Component {
}
}
}
+
+TeamSignUp.propTypes = {
+ teams: React.PropTypes.array
+};
+
diff --git a/web/react/components/team_general_tab.jsx b/web/react/components/team_general_tab.jsx
index 923180e27..69ba44664 100644
--- a/web/react/components/team_general_tab.jsx
+++ b/web/react/components/team_general_tab.jsx
@@ -6,29 +6,112 @@ const SettingItemMax = require('./setting_item_max.jsx');
const Client = require('../utils/client.jsx');
const Utils = require('../utils/utils.jsx');
+const TeamStore = require('../stores/team_store.jsx');
export default class GeneralTab extends React.Component {
constructor(props) {
super(props);
this.handleNameSubmit = this.handleNameSubmit.bind(this);
+ this.handleInviteIdSubmit = this.handleInviteIdSubmit.bind(this);
+ this.handleOpenInviteSubmit = this.handleOpenInviteSubmit.bind(this);
+ this.handleTeamListingSubmit = this.handleTeamListingSubmit.bind(this);
this.handleClose = this.handleClose.bind(this);
- this.onUpdateSection = this.onUpdateSection.bind(this);
+ this.onUpdateNameSection = this.onUpdateNameSection.bind(this);
this.updateName = this.updateName.bind(this);
+ this.onUpdateInviteIdSection = this.onUpdateInviteIdSection.bind(this);
+ this.updateInviteId = this.updateInviteId.bind(this);
+ this.onUpdateOpenInviteSection = this.onUpdateOpenInviteSection.bind(this);
+ this.handleOpenInviteRadio = this.handleOpenInviteRadio.bind(this);
+ this.onUpdateTeamListingSection = this.onUpdateTeamListingSection.bind(this);
+ this.handleTeamListingRadio = this.handleTeamListingRadio.bind(this);
+ this.handleGenerateInviteId = this.handleGenerateInviteId.bind(this);
- this.state = {name: this.props.teamDisplayName, serverError: '', clientError: ''};
+ this.state = {
+ name: props.team.display_name,
+ invite_id: props.team.invite_id,
+ allow_open_invite: props.team.allow_open_invite,
+ allow_team_listing: props.team.allow_team_listing,
+ serverError: '',
+ clientError: ''
+ };
}
+
+ handleGenerateInviteId(e) {
+ e.preventDefault();
+
+ var newId = '';
+ for (var i = 0; i < 32; i++) {
+ newId += Math.floor(Math.random() * 16).toString(16);
+ }
+
+ this.setState({invite_id: newId});
+ }
+
+ handleOpenInviteRadio(openInvite) {
+ this.setState({allow_open_invite: openInvite});
+ }
+
+ handleTeamListingRadio(listing) {
+ if (global.window.mm_config.EnableTeamListing !== 'true' && listing) {
+ ReactDOM.findDOMNode(this.refs.teamListingRadioNo).checked = true;
+ this.setState({clientError: 'Team directory has been disabled. Please ask a system admin to enable it.'});
+ } else {
+ this.setState({allow_team_listing: listing});
+ }
+ }
+
+ handleOpenInviteSubmit(e) {
+ e.preventDefault();
+
+ var state = {serverError: '', clientError: ''};
+
+ var data = this.props.team;
+ data.allow_open_invite = this.state.allow_open_invite;
+ Client.updateTeam(data,
+ (team) => {
+ TeamStore.saveTeam(team);
+ TeamStore.emitChange();
+ this.props.updateSection('');
+ },
+ (err) => {
+ state.serverError = err.message;
+ this.setState(state);
+ }
+ );
+ }
+
+ handleTeamListingSubmit(e) {
+ e.preventDefault();
+
+ var state = {serverError: '', clientError: ''};
+
+ var data = this.props.team;
+ data.allow_team_listing = this.state.allow_team_listing;
+ Client.updateTeam(data,
+ (team) => {
+ TeamStore.saveTeam(team);
+ TeamStore.emitChange();
+ this.props.updateSection('');
+ },
+ (err) => {
+ state.serverError = err.message;
+ this.setState(state);
+ }
+ );
+ }
+
handleNameSubmit(e) {
e.preventDefault();
- let state = {serverError: '', clientError: ''};
+ var state = {serverError: '', clientError: ''};
let valid = true;
const name = this.state.name.trim();
if (!name) {
state.clientError = 'This field is required';
valid = false;
- } else if (name === this.props.teamDisplayName) {
+ } else if (name === this.props.team.display_name) {
state.clientError = 'Please choose a new name for your team';
valid = false;
} else {
@@ -41,37 +124,76 @@ export default class GeneralTab extends React.Component {
return;
}
- let data = {};
- data.new_name = name;
+ var data = this.props.team;
+ data.display_name = this.state.name;
+ Client.updateTeam(data,
+ (team) => {
+ TeamStore.saveTeam(team);
+ TeamStore.emitChange();
+ this.props.updateSection('');
+ },
+ (err) => {
+ state.serverError = err.message;
+ this.setState(state);
+ }
+ );
+ }
+
+ handleInviteIdSubmit(e) {
+ e.preventDefault();
+
+ var state = {serverError: '', clientError: ''};
+ let valid = true;
- Client.updateTeamDisplayName(data,
- function nameChangeSuccess() {
+ const inviteId = this.state.invite_id.trim();
+ if (inviteId) {
+ state.clientError = '';
+ } else {
+ state.clientError = 'This field is required';
+ valid = false;
+ }
+
+ this.setState(state);
+
+ if (!valid) {
+ return;
+ }
+
+ var data = this.props.team;
+ data.invite_id = this.state.invite_id;
+ Client.updateTeam(data,
+ (team) => {
+ TeamStore.saveTeam(team);
+ TeamStore.emitChange();
this.props.updateSection('');
- $('#team_settings').modal('hide');
- window.location.reload();
- }.bind(this),
- function nameChangeFail(err) {
+ },
+ (err) => {
state.serverError = err.message;
this.setState(state);
- }.bind(this)
+ }
);
}
+
componentWillReceiveProps(newProps) {
if (newProps.team && newProps.teamDisplayName) {
this.setState({name: newProps.teamDisplayName});
}
}
+
handleClose() {
this.setState({clientError: '', serverError: ''});
this.props.updateSection('');
}
+
componentDidMount() {
$('#team_settings').on('hidden.bs.modal', this.handleClose);
}
+
componentWillUnmount() {
$('#team_settings').off('hidden.bs.modal', this.handleClose);
}
- onUpdateSection(e) {
+
+ onUpdateNameSection(e) {
e.preventDefault();
if (this.props.activeSection === 'name') {
this.props.updateSection('');
@@ -79,10 +201,44 @@ export default class GeneralTab extends React.Component {
this.props.updateSection('name');
}
}
+
+ onUpdateInviteIdSection(e) {
+ e.preventDefault();
+ if (this.props.activeSection === 'invite_id') {
+ this.props.updateSection('');
+ } else {
+ this.props.updateSection('invite_id');
+ }
+ }
+
+ onUpdateOpenInviteSection(e) {
+ e.preventDefault();
+ if (this.props.activeSection === 'open_invite') {
+ this.props.updateSection('');
+ } else {
+ this.props.updateSection('open_invite');
+ }
+ }
+
+ onUpdateTeamListingSection(e) {
+ e.preventDefault();
+ if (this.props.activeSection === 'team_listing') {
+ this.props.updateSection('');
+ } else {
+ this.props.updateSection('team_listing');
+ }
+ }
+
updateName(e) {
e.preventDefault();
this.setState({name: e.target.value});
}
+
+ updateInviteId(e) {
+ e.preventDefault();
+ this.setState({invite_id: e.target.value});
+ }
+
render() {
let clientError = null;
let serverError = null;
@@ -93,10 +249,180 @@ export default class GeneralTab extends React.Component {
serverError = this.state.serverError;
}
+ let teamListingSection;
+ if (this.props.activeSection === 'team_listing') {
+ const inputs = [
+ <div key='userTeamListingOptions'>
+ <div className='radio'>
+ <label>
+ <input
+ name='userTeamListingOptions'
+ type='radio'
+ defaultChecked={this.state.allow_team_listing}
+ onChange={this.handleTeamListingRadio.bind(this, true)}
+ />
+ {'Yes'}
+ </label>
+ <br/>
+ </div>
+ <div className='radio'>
+ <label>
+ <input
+ ref='teamListingRadioNo'
+ name='userTeamListingOptions'
+ type='radio'
+ defaultChecked={!this.state.allow_team_listing}
+ onChange={this.handleTeamListingRadio.bind(this, false)}
+ />
+ {'No'}
+ </label>
+ <br/>
+ </div>
+ <div><br/>{'When allowed the team will appear on the main page as part of team directory.'}</div>
+ </div>
+ ];
+
+ teamListingSection = (
+ <SettingItemMax
+ title='Allow in Team Directory'
+ inputs={inputs}
+ submit={this.handleTeamListingSubmit}
+ server_error={serverError}
+ client_error={clientError}
+ updateSection={this.onUpdateTeamListingSection}
+ />
+ );
+ } else {
+ let describe = '';
+ if (this.state.allow_team_listing === true) {
+ describe = 'Yes';
+ } else {
+ describe = 'No';
+ }
+
+ teamListingSection = (
+ <SettingItemMin
+ title='Allow in Team Directory'
+ describe={describe}
+ updateSection={this.onUpdateTeamListingSection}
+ />
+ );
+ }
+
+ let openInviteSection;
+ if (this.props.activeSection === 'open_invite') {
+ const inputs = [
+ <div key='userOpenInviteOptions'>
+ <div className='radio'>
+ <label>
+ <input
+ name='userOpenInviteOptions'
+ type='radio'
+ defaultChecked={this.state.allow_open_invite}
+ onChange={this.handleOpenInviteRadio.bind(this, true)}
+ />
+ {'Yes'}
+ </label>
+ <br/>
+ </div>
+ <div className='radio'>
+ <label>
+ <input
+ name='userOpenInviteOptions'
+ type='radio'
+ defaultChecked={!this.state.allow_open_invite}
+ onChange={this.handleOpenInviteRadio.bind(this, false)}
+ />
+ {'No'}
+ </label>
+ <br/>
+ </div>
+ <div><br/>{'When allowed the team signup link will be included on the login page and anyone can signup to this team.'}</div>
+ </div>
+ ];
+
+ openInviteSection = (
+ <SettingItemMax
+ title='Allow Open Invitations'
+ inputs={inputs}
+ submit={this.handleOpenInviteSubmit}
+ server_error={serverError}
+ updateSection={this.onUpdateOpenInviteSection}
+ />
+ );
+ } else {
+ let describe = '';
+ if (this.state.allow_open_invite === true) {
+ describe = 'Yes';
+ } else {
+ describe = 'No';
+ }
+
+ openInviteSection = (
+ <SettingItemMin
+ title='Allow Open Invitations'
+ describe={describe}
+ updateSection={this.onUpdateOpenInviteSection}
+ />
+ );
+ }
+
+ let inviteSection;
+
+ if (this.props.activeSection === 'invite_id') {
+ const inputs = [];
+
+ inputs.push(
+ <div
+ key='teamInviteSetting'
+ className='form-group'
+ >
+ <label className='col-sm-5 control-label'>{'Invite Code'}</label>
+ <div className='col-sm-7'>
+ <input
+ className='form-control'
+ type='text'
+ onChange={this.updateInviteId}
+ value={this.state.invite_id}
+ maxLength='32'
+ />
+ </div>
+ <div><br/>{'When allowing open invites this code is used as part of the signup process. Changing this code will invalidate the previous open signup link.'}</div>
+ <div className='help-text'>
+ <button
+ className='btn btn-default'
+ onClick={this.handleGenerateInviteId}
+ >
+ {'Re-Generate'}
+ </button>
+ </div>
+ </div>
+ );
+
+ inviteSection = (
+ <SettingItemMax
+ title={`Invite Code`}
+ inputs={inputs}
+ submit={this.handleInviteIdSubmit}
+ server_error={serverError}
+ client_error={clientError}
+ updateSection={this.onUpdateInviteIdSection}
+ />
+ );
+ } else {
+ inviteSection = (
+ <SettingItemMin
+ title={`Invite Code`}
+ describe={`Click 'Edit' to re-generate invite Code.`}
+ updateSection={this.onUpdateInviteIdSection}
+ />
+ );
+ }
+
let nameSection;
if (this.props.activeSection === 'name') {
- let inputs = [];
+ const inputs = [];
let teamNameLabel = 'Team Name';
if (Utils.isMobile()) {
@@ -127,17 +453,17 @@ export default class GeneralTab extends React.Component {
submit={this.handleNameSubmit}
server_error={serverError}
client_error={clientError}
- updateSection={this.onUpdateSection}
+ updateSection={this.onUpdateNameSection}
/>
);
} else {
- let describe = this.state.name;
+ var describe = this.state.name;
nameSection = (
<SettingItemMin
title={`Team Name`}
describe={describe}
- updateSection={this.onUpdateSection}
+ updateSection={this.onUpdateNameSection}
/>
);
}
@@ -158,16 +484,19 @@ export default class GeneralTab extends React.Component {
ref='title'
>
<i className='modal-back'></i>
- General Settings
+ {'General Settings'}
</h4>
</div>
<div
ref='wrapper'
className='user-settings'
>
- <h3 className='tab-header'>General Settings</h3>
+ <h3 className='tab-header'>{'General Settings'}</h3>
<div className='divider-dark first'/>
{nameSection}
+ {openInviteSection}
+ {teamListingSection}
+ {inviteSection}
<div className='divider-dark'/>
</div>
</div>
@@ -178,6 +507,5 @@ export default class GeneralTab extends React.Component {
GeneralTab.propTypes = {
updateSection: React.PropTypes.func.isRequired,
team: React.PropTypes.object.isRequired,
- activeSection: React.PropTypes.string.isRequired,
- teamDisplayName: React.PropTypes.string.isRequired
+ activeSection: React.PropTypes.string.isRequired
};
diff --git a/web/react/components/team_settings.jsx b/web/react/components/team_settings.jsx
index e14da4f04..09674f1ef 100644
--- a/web/react/components/team_settings.jsx
+++ b/web/react/components/team_settings.jsx
@@ -37,7 +37,6 @@ export default class TeamSettings extends React.Component {
team={this.state.team}
activeSection={this.props.activeSection}
updateSection={this.props.updateSection}
- teamDisplayName={this.props.teamDisplayName}
/>
</div>
);
@@ -72,12 +71,11 @@ export default class TeamSettings extends React.Component {
TeamSettings.defaultProps = {
activeTab: '',
- activeSection: '',
- teamDisplayName: ''
+ activeSection: ''
};
+
TeamSettings.propTypes = {
activeTab: React.PropTypes.string.isRequired,
activeSection: React.PropTypes.string.isRequired,
- updateSection: React.PropTypes.func.isRequired,
- teamDisplayName: React.PropTypes.string.isRequired
+ updateSection: React.PropTypes.func.isRequired
};
diff --git a/web/react/components/team_settings_modal.jsx b/web/react/components/team_settings_modal.jsx
index 5c5995020..17fe31c65 100644
--- a/web/react/components/team_settings_modal.jsx
+++ b/web/react/components/team_settings_modal.jsx
@@ -82,7 +82,6 @@ export default class TeamSettingsModal extends React.Component {
activeTab={this.state.activeTab}
activeSection={this.state.activeSection}
updateSection={this.updateSection}
- teamDisplayName={this.props.teamDisplayName}
/>
</div>
</div>
@@ -95,5 +94,4 @@ export default class TeamSettingsModal extends React.Component {
}
TeamSettingsModal.propTypes = {
- teamDisplayName: React.PropTypes.string.isRequired
};
diff --git a/web/react/components/user_settings/code_theme_chooser.jsx b/web/react/components/user_settings/code_theme_chooser.jsx
new file mode 100644
index 000000000..eef4b24ba
--- /dev/null
+++ b/web/react/components/user_settings/code_theme_chooser.jsx
@@ -0,0 +1,55 @@
+// Copyright (c) 2015 Mattermost, Inc. All Rights Reserved.
+// See License.txt for license information.
+
+var Constants = require('../../utils/constants.jsx');
+
+export default class CodeThemeChooser extends React.Component {
+ constructor(props) {
+ super(props);
+ this.state = {};
+ }
+ render() {
+ const theme = this.props.theme;
+
+ const premadeThemes = [];
+ for (const k in Constants.CODE_THEMES) {
+ if (Constants.CODE_THEMES.hasOwnProperty(k)) {
+ let activeClass = '';
+ if (k === theme.codeTheme) {
+ activeClass = 'active';
+ }
+
+ premadeThemes.push(
+ <div
+ className='col-xs-6 col-sm-3 premade-themes'
+ key={'premade-theme-key' + k}
+ >
+ <div
+ className={activeClass}
+ onClick={() => this.props.updateTheme(k)}
+ >
+ <label>
+ <img
+ className='img-responsive'
+ src={'/static/images/themes/code_themes/' + k + '.png'}
+ />
+ <div className='theme-label'>{Constants.CODE_THEMES[k]}</div>
+ </label>
+ </div>
+ </div>
+ );
+ }
+ }
+
+ return (
+ <div className='row'>
+ {premadeThemes}
+ </div>
+ );
+ }
+}
+
+CodeThemeChooser.propTypes = {
+ theme: React.PropTypes.object.isRequired,
+ updateTheme: React.PropTypes.func.isRequired
+};
diff --git a/web/react/components/user_settings/custom_theme_chooser.jsx b/web/react/components/user_settings/custom_theme_chooser.jsx
index 44b3f4544..095e5b622 100644
--- a/web/react/components/user_settings/custom_theme_chooser.jsx
+++ b/web/react/components/user_settings/custom_theme_chooser.jsx
@@ -40,11 +40,12 @@ export default class CustomThemeChooser extends React.Component {
const theme = {type: 'custom'};
let index = 0;
Constants.THEME_ELEMENTS.forEach((element) => {
- if (index < colors.length) {
+ if (index < colors.length - 1) {
theme[element.id] = colors[index];
}
index++;
});
+ theme.codeTheme = colors[colors.length - 1];
this.props.updateTheme(theme);
}
@@ -78,6 +79,8 @@ export default class CustomThemeChooser extends React.Component {
colors += theme[element.id] + ',';
});
+ colors += theme.codeTheme;
+
const pasteBox = (
<div className='col-sm-12'>
<label className='custom-label'>
diff --git a/web/react/components/user_settings/user_settings_appearance.jsx b/web/react/components/user_settings/user_settings_appearance.jsx
index 8c62a189d..7b4b54e27 100644
--- a/web/react/components/user_settings/user_settings_appearance.jsx
+++ b/web/react/components/user_settings/user_settings_appearance.jsx
@@ -7,6 +7,7 @@ var Utils = require('../../utils/utils.jsx');
const CustomThemeChooser = require('./custom_theme_chooser.jsx');
const PremadeThemeChooser = require('./premade_theme_chooser.jsx');
+const CodeThemeChooser = require('./code_theme_chooser.jsx');
const AppDispatcher = require('../../dispatcher/app_dispatcher.jsx');
const Constants = require('../../utils/constants.jsx');
const ActionTypes = Constants.ActionTypes;
@@ -18,12 +19,14 @@ export default class UserSettingsAppearance extends React.Component {
this.onChange = this.onChange.bind(this);
this.submitTheme = this.submitTheme.bind(this);
this.updateTheme = this.updateTheme.bind(this);
+ this.updateCodeTheme = this.updateCodeTheme.bind(this);
this.handleClose = this.handleClose.bind(this);
this.handleImportModal = this.handleImportModal.bind(this);
this.state = this.getStateFromStores();
this.originalTheme = this.state.theme;
+ this.originalCodeTheme = this.state.theme.codeTheme;
}
componentDidMount() {
UserStore.addChangeListener(this.onChange);
@@ -58,6 +61,10 @@ export default class UserSettingsAppearance extends React.Component {
type = 'custom';
}
+ if (!theme.codeTheme) {
+ theme.codeTheme = Constants.DEFAULT_CODE_THEME;
+ }
+
return {theme, type};
}
onChange() {
@@ -93,6 +100,15 @@ export default class UserSettingsAppearance extends React.Component {
);
}
updateTheme(theme) {
+ if (!theme.codeTheme) {
+ theme.codeTheme = this.state.theme.codeTheme;
+ }
+ this.setState({theme});
+ Utils.applyTheme(theme);
+ }
+ updateCodeTheme(codeTheme) {
+ var theme = this.state.theme;
+ theme.codeTheme = codeTheme;
this.setState({theme});
Utils.applyTheme(theme);
}
@@ -102,6 +118,7 @@ export default class UserSettingsAppearance extends React.Component {
handleClose() {
const state = this.getStateFromStores();
state.serverError = null;
+ state.theme.codeTheme = this.originalCodeTheme;
Utils.applyTheme(state.theme);
@@ -170,7 +187,13 @@ export default class UserSettingsAppearance extends React.Component {
</div>
{custom}
<hr />
- {serverError}
+ <strong className='radio'>{'Code Theme'}</strong>
+ <CodeThemeChooser
+ theme={this.state.theme}
+ updateTheme={this.updateCodeTheme}
+ />
+ <hr />
+ {serverError}
<a
className='btn btn-sm btn-primary'
href='#'