Merge pull request #678 from mattermost/mm-1589
PLT-171 MM-1589 New add channel modal using react-bootstrap.
Этот коммит содержится в:
177
web/react/components/change_url_modal.jsx
Обычный файл
177
web/react/components/change_url_modal.jsx
Обычный файл
@@ -0,0 +1,177 @@
|
||||
// Copyright (c) 2015 Spinpunch, Inc. All Rights Reserved.
|
||||
// See License.txt for license information.
|
||||
|
||||
var Modal = ReactBootstrap.Modal;
|
||||
var Utils = require('../utils/utils.jsx');
|
||||
|
||||
export default class ChangeUrlModal extends React.Component {
|
||||
constructor(props) {
|
||||
super(props);
|
||||
|
||||
this.onURLChanged = this.onURLChanged.bind(this);
|
||||
this.doSubmit = this.doSubmit.bind(this);
|
||||
this.doCancel = this.doCancel.bind(this);
|
||||
|
||||
this.state = {
|
||||
currentURL: props.currentURL,
|
||||
urlError: '',
|
||||
userEdit: false
|
||||
};
|
||||
}
|
||||
componentWillReceiveProps(nextProps) {
|
||||
// This check prevents the url being deleted when we re-render
|
||||
// because of user status check
|
||||
if (!this.state.userEdit) {
|
||||
this.setState({
|
||||
currentURL: nextProps.currentURL
|
||||
});
|
||||
}
|
||||
}
|
||||
componentDidUpdate(prevProps) {
|
||||
if (this.props.show === true && prevProps.show === false) {
|
||||
React.findDOMNode(this.refs.urlinput).select();
|
||||
}
|
||||
}
|
||||
onURLChanged(e) {
|
||||
const url = e.target.value.trim();
|
||||
this.setState({currentURL: url.replace(/[^A-Za-z0-9-_]/g, '').toLowerCase(), userEdit: true});
|
||||
}
|
||||
getURLError(url) {
|
||||
let error = []; //eslint-disable-line prefer-const
|
||||
if (url.length < 2) {
|
||||
error.push(<span key='error1'>{'Must be longer than two characters'}<br/></span>);
|
||||
}
|
||||
if (url.charAt(0) === '-' || url.charAt(0) === '_') {
|
||||
error.push(<span key='error2'>{'Must start with a letter or number'}<br/></span>);
|
||||
}
|
||||
if (url.length > 1 && (url.charAt(url.length - 1) === '-' || url.charAt(url.length - 1) === '_')) {
|
||||
error.push(<span key='error3'>{'Must end with a letter or number'}<br/></span>);
|
||||
}
|
||||
if (url.indexOf('__') > -1) {
|
||||
error.push(<span key='error4'>{'Can not contain two underscores in a row.'}<br/></span>);
|
||||
}
|
||||
|
||||
// In case of error we don't detect
|
||||
if (error.length === 0) {
|
||||
error.push(<span key='errorlast'>{'Invalid URL'}<br/></span>);
|
||||
}
|
||||
return error;
|
||||
}
|
||||
doSubmit(e) {
|
||||
e.preventDefault();
|
||||
|
||||
const url = React.findDOMNode(this.refs.urlinput).value;
|
||||
const cleanedURL = Utils.cleanUpUrlable(url);
|
||||
if (cleanedURL !== url || url.length < 2 || url.indexOf('__') > -1) {
|
||||
this.setState({urlError: this.getURLError(url)});
|
||||
return;
|
||||
}
|
||||
this.setState({urlError: '', userEdit: false});
|
||||
this.props.onModalSubmit(url);
|
||||
}
|
||||
doCancel() {
|
||||
this.setState({urlError: '', userEdit: false});
|
||||
this.props.onModalDismissed();
|
||||
}
|
||||
render() {
|
||||
let urlClass = 'input-group input-group--limit';
|
||||
let urlError = null;
|
||||
let serverError = null;
|
||||
|
||||
if (this.state.urlError) {
|
||||
urlClass += ' has-error';
|
||||
urlError = (<p className='input__help error'>{this.state.urlError}</p>);
|
||||
}
|
||||
|
||||
if (this.props.serverError) {
|
||||
serverError = <div className='form-group has-error'><p className='input__help error'>{this.props.serverError}</p></div>;
|
||||
}
|
||||
|
||||
const fullTeamUrl = Utils.getTeamURLFromAddressBar();
|
||||
const teamURL = Utils.getShortenedTeamURL();
|
||||
|
||||
return (
|
||||
<Modal
|
||||
show={this.props.show}
|
||||
onHide={this.doCancel}
|
||||
>
|
||||
<Modal.Header closeButton={true}>
|
||||
<Modal.Title>{this.props.title}</Modal.Title>
|
||||
</Modal.Header>
|
||||
<form
|
||||
role='form'
|
||||
className='form-horizontal'
|
||||
>
|
||||
<Modal.Body>
|
||||
<div className='modal-intro'>{this.props.description}</div>
|
||||
<div className='form-group'>
|
||||
<label className='col-sm-2 form__label control-label'>{this.props.urlLabel}</label>
|
||||
<div className='col-sm-10'>
|
||||
<div className={urlClass}>
|
||||
<span
|
||||
data-toggle='tooltip'
|
||||
title={fullTeamUrl}
|
||||
className='input-group-addon'
|
||||
>
|
||||
{teamURL}
|
||||
</span>
|
||||
<input
|
||||
type='text'
|
||||
ref='urlinput'
|
||||
className='form-control'
|
||||
maxLength='22'
|
||||
onChange={this.onURLChanged}
|
||||
value={this.state.currentURL}
|
||||
autoFocus={true}
|
||||
tabIndex='1'
|
||||
/>
|
||||
</div>
|
||||
{urlError}
|
||||
{serverError}
|
||||
</div>
|
||||
</div>
|
||||
</Modal.Body>
|
||||
<Modal.Footer>
|
||||
<button
|
||||
type='button'
|
||||
className='btn btn-default'
|
||||
onClick={this.doCancel}
|
||||
>
|
||||
{'Close'}
|
||||
</button>
|
||||
<button
|
||||
onClick={this.doSubmit}
|
||||
type='submit'
|
||||
className='btn btn-primary'
|
||||
tabIndex='2'
|
||||
>
|
||||
{this.props.submitButtonText}
|
||||
</button>
|
||||
</Modal.Footer>
|
||||
</form>
|
||||
</Modal>
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
ChangeUrlModal.defaultProps = {
|
||||
show: false,
|
||||
title: 'Change URL',
|
||||
desciption: '',
|
||||
urlLabel: 'URL',
|
||||
submitButtonText: 'Submit',
|
||||
currentURL: '',
|
||||
serverError: ''
|
||||
};
|
||||
|
||||
ChangeUrlModal.propTypes = {
|
||||
show: React.PropTypes.bool.isRequired,
|
||||
title: React.PropTypes.string,
|
||||
description: React.PropTypes.string,
|
||||
urlLabel: React.PropTypes.string,
|
||||
submitButtonText: React.PropTypes.string,
|
||||
currentURL: React.PropTypes.string,
|
||||
serverError: React.PropTypes.string,
|
||||
onModalSubmit: React.PropTypes.func.isRequired,
|
||||
onModalDismissed: React.PropTypes.func.isRequired
|
||||
};
|
||||
@@ -52,7 +52,7 @@ export default class FileUpload extends React.Component {
|
||||
}
|
||||
|
||||
// generate a unique id that can be used by other components to refer back to this upload
|
||||
var clientId = utils.generateId();
|
||||
let clientId = utils.generateId();
|
||||
|
||||
// prepare data to be uploaded
|
||||
var formData = new FormData();
|
||||
|
||||
@@ -1,211 +0,0 @@
|
||||
// Copyright (c) 2015 Spinpunch, Inc. All Rights Reserved.
|
||||
// See License.txt for license information.
|
||||
|
||||
var utils = require('../utils/utils.jsx');
|
||||
var client = require('../utils/client.jsx');
|
||||
var asyncClient = require('../utils/async_client.jsx');
|
||||
var UserStore = require('../stores/user_store.jsx');
|
||||
|
||||
export default class NewChannelModal extends React.Component {
|
||||
constructor() {
|
||||
super();
|
||||
|
||||
this.handleSubmit = this.handleSubmit.bind(this);
|
||||
this.displayNameKeyUp = this.displayNameKeyUp.bind(this);
|
||||
this.handleClose = this.handleClose.bind(this);
|
||||
|
||||
this.state = {channelType: ''};
|
||||
}
|
||||
handleSubmit(e) {
|
||||
e.preventDefault();
|
||||
|
||||
var channel = {};
|
||||
var state = {serverError: ''};
|
||||
|
||||
channel.display_name = React.findDOMNode(this.refs.display_name).value.trim();
|
||||
if (!channel.display_name) {
|
||||
state.displayNameError = 'This field is required';
|
||||
state.inValid = true;
|
||||
} else if (channel.display_name.length > 22) {
|
||||
state.displayNameError = 'This field must be less than 22 characters';
|
||||
state.inValid = true;
|
||||
} else {
|
||||
state.displayNameError = '';
|
||||
}
|
||||
|
||||
channel.name = React.findDOMNode(this.refs.channel_name).value.trim();
|
||||
if (!channel.name) {
|
||||
state.nameError = 'This field is required';
|
||||
state.inValid = true;
|
||||
} else if (channel.name.length > 22) {
|
||||
state.nameError = 'This field must be less than 22 characters';
|
||||
state.inValid = true;
|
||||
} else {
|
||||
var cleanedName = utils.cleanUpUrlable(channel.name);
|
||||
if (cleanedName !== channel.name) {
|
||||
state.nameError = "Must be lowercase alphanumeric characters, allowing '-' but not starting or ending with '-'";
|
||||
state.inValid = true;
|
||||
} else {
|
||||
state.nameError = '';
|
||||
}
|
||||
}
|
||||
|
||||
this.setState(state);
|
||||
|
||||
if (state.inValid) {
|
||||
return;
|
||||
}
|
||||
|
||||
var cu = UserStore.getCurrentUser();
|
||||
channel.team_id = cu.team_id;
|
||||
|
||||
channel.description = React.findDOMNode(this.refs.channel_desc).value.trim();
|
||||
channel.type = this.state.channelType;
|
||||
|
||||
client.createChannel(channel,
|
||||
function success(data) {
|
||||
$(React.findDOMNode(this.refs.modal)).modal('hide');
|
||||
|
||||
asyncClient.getChannel(data.id);
|
||||
utils.switchChannel(data);
|
||||
|
||||
React.findDOMNode(this.refs.display_name).value = '';
|
||||
React.findDOMNode(this.refs.channel_name).value = '';
|
||||
React.findDOMNode(this.refs.channel_desc).value = '';
|
||||
}.bind(this),
|
||||
function error(err) {
|
||||
state.serverError = err.message;
|
||||
state.inValid = true;
|
||||
this.setState(state);
|
||||
}.bind(this)
|
||||
);
|
||||
}
|
||||
displayNameKeyUp() {
|
||||
var displayName = React.findDOMNode(this.refs.display_name).value.trim();
|
||||
var channelName = utils.cleanUpUrlable(displayName);
|
||||
React.findDOMNode(this.refs.channel_name).value = channelName;
|
||||
}
|
||||
componentDidMount() {
|
||||
var self = this;
|
||||
$(React.findDOMNode(this.refs.modal)).on('show.bs.modal', function onModalShow(e) {
|
||||
var button = e.relatedTarget;
|
||||
self.setState({channelType: $(button).attr('data-channeltype')});
|
||||
});
|
||||
$(React.findDOMNode(this.refs.modal)).on('hidden.bs.modal', this.handleClose);
|
||||
}
|
||||
componentWillUnmount() {
|
||||
$(React.findDOMNode(this.refs.modal)).off('hidden.bs.modal', this.handleClose);
|
||||
}
|
||||
handleClose() {
|
||||
$(React.findDOMNode(this)).find('.form-control').each(function clearForms() {
|
||||
this.value = '';
|
||||
});
|
||||
|
||||
this.setState({channelType: '', displayNameError: '', nameError: '', serverError: '', inValid: false});
|
||||
}
|
||||
render() {
|
||||
var displayNameError = null;
|
||||
var nameError = null;
|
||||
var serverError = null;
|
||||
var displayNameClass = 'form-group';
|
||||
var nameClass = 'form-group';
|
||||
|
||||
if (this.state.displayNameError) {
|
||||
displayNameError = <label className='control-label'>{this.state.displayNameError}</label>;
|
||||
displayNameClass += ' has-error';
|
||||
}
|
||||
if (this.state.nameError) {
|
||||
nameError = <label className='control-label'>{this.state.nameError}</label>;
|
||||
nameClass += ' has-error';
|
||||
}
|
||||
if (this.state.serverError) {
|
||||
serverError = <div className='form-group has-error'><label className='control-label'>{this.state.serverError}</label></div>;
|
||||
}
|
||||
|
||||
var channelTerm = 'Channel';
|
||||
if (this.state.channelType === 'P') {
|
||||
channelTerm = 'Group';
|
||||
}
|
||||
|
||||
return (
|
||||
<div
|
||||
className='modal fade'
|
||||
id='new_channel'
|
||||
ref='modal'
|
||||
tabIndex='-1'
|
||||
role='dialog'
|
||||
aria-hidden='true'
|
||||
>
|
||||
<div className='modal-dialog'>
|
||||
<div className='modal-content'>
|
||||
<div className='modal-header'>
|
||||
<button
|
||||
type='button'
|
||||
className='close'
|
||||
data-dismiss='modal'
|
||||
>
|
||||
<span aria-hidden='true'>×</span>
|
||||
<span className='sr-only'>Cancel</span>
|
||||
</button>
|
||||
<h4 className='modal-title'>New {channelTerm}</h4>
|
||||
</div>
|
||||
<form role='form'>
|
||||
<div className='modal-body'>
|
||||
<div className={displayNameClass}>
|
||||
<label className='control-label'>Display Name</label>
|
||||
<input
|
||||
onKeyUp={this.displayNameKeyUp}
|
||||
type='text'
|
||||
ref='display_name'
|
||||
className='form-control'
|
||||
placeholder='Enter display name'
|
||||
maxLength='22'
|
||||
/>
|
||||
{displayNameError}
|
||||
</div>
|
||||
<div className={nameClass}>
|
||||
<label className='control-label'>Handle</label>
|
||||
<input
|
||||
type='text'
|
||||
className='form-control'
|
||||
ref='channel_name'
|
||||
placeholder="lowercase alphanumeric's only"
|
||||
maxLength='22'
|
||||
/>
|
||||
{nameError}
|
||||
</div>
|
||||
<div className='form-group'>
|
||||
<label className='control-label'>Description</label>
|
||||
<textarea
|
||||
className='form-control no-resize'
|
||||
ref='channel_desc'
|
||||
rows='3'
|
||||
placeholder='Description'
|
||||
maxLength='1024'
|
||||
/>
|
||||
</div>
|
||||
{serverError}
|
||||
</div>
|
||||
<div className='modal-footer'>
|
||||
<button
|
||||
type='button'
|
||||
className='btn btn-default'
|
||||
data-dismiss='modal'
|
||||
>
|
||||
Cancel
|
||||
</button>
|
||||
<button
|
||||
onClick={this.handleSubmit}
|
||||
type='submit'
|
||||
className='btn btn-primary'
|
||||
>
|
||||
Create New {channelTerm}
|
||||
</button>
|
||||
</div>
|
||||
</form>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
}
|
||||
206
web/react/components/new_channel_flow.jsx
Обычный файл
206
web/react/components/new_channel_flow.jsx
Обычный файл
@@ -0,0 +1,206 @@
|
||||
// Copyright (c) 2015 Spinpunch, Inc. All Rights Reserved.
|
||||
// See License.txt for license information.
|
||||
|
||||
var Utils = require('../utils/utils.jsx');
|
||||
var AsyncClient = require('../utils/async_client.jsx');
|
||||
var Client = require('../utils/client.jsx');
|
||||
var UserStore = require('../stores/user_store.jsx');
|
||||
|
||||
var NewChannelModal = require('./new_channel_modal.jsx');
|
||||
var ChangeURLModal = require('./change_url_modal.jsx');
|
||||
|
||||
const SHOW_NEW_CHANNEL = 1;
|
||||
const SHOW_EDIT_URL = 2;
|
||||
const SHOW_EDIT_URL_THEN_COMPLETE = 3;
|
||||
|
||||
export default class NewChannelFlow extends React.Component {
|
||||
constructor(props) {
|
||||
super(props);
|
||||
|
||||
this.doSubmit = this.doSubmit.bind(this);
|
||||
this.typeSwitched = this.typeSwitched.bind(this);
|
||||
this.urlChangeRequested = this.urlChangeRequested.bind(this);
|
||||
this.urlChangeSubmitted = this.urlChangeSubmitted.bind(this);
|
||||
this.urlChangeDismissed = this.urlChangeDismissed.bind(this);
|
||||
this.channelDataChanged = this.channelDataChanged.bind(this);
|
||||
|
||||
this.state = {
|
||||
serverError: '',
|
||||
channelType: 'O',
|
||||
flowState: SHOW_NEW_CHANNEL,
|
||||
channelDisplayName: '',
|
||||
channelName: '',
|
||||
channelDescription: '',
|
||||
nameModified: false
|
||||
};
|
||||
}
|
||||
componentWillReceiveProps(nextProps) {
|
||||
// If we are being shown, grab channel type from props and clear
|
||||
if (nextProps.show === true && this.props.show === false) {
|
||||
this.setState({
|
||||
serverError: '',
|
||||
channelType: nextProps.channelType,
|
||||
flowState: SHOW_NEW_CHANNEL,
|
||||
channelDisplayName: '',
|
||||
channelName: '',
|
||||
channelDescription: '',
|
||||
nameModified: false
|
||||
});
|
||||
}
|
||||
}
|
||||
doSubmit() {
|
||||
var channel = {};
|
||||
|
||||
channel.display_name = this.state.channelDisplayName;
|
||||
if (!channel.display_name) {
|
||||
this.setState({serverError: 'Invalid Channel Name'});
|
||||
return;
|
||||
}
|
||||
|
||||
channel.name = this.state.channelName;
|
||||
if (channel.name.length < 2) {
|
||||
this.setState({flowState: SHOW_EDIT_URL_THEN_COMPLETE});
|
||||
return;
|
||||
}
|
||||
|
||||
const cu = UserStore.getCurrentUser();
|
||||
channel.team_id = cu.team_id;
|
||||
channel.description = this.state.channelDescription;
|
||||
channel.type = this.state.channelType;
|
||||
|
||||
Client.createChannel(channel,
|
||||
(data) => {
|
||||
this.props.onModalDismissed();
|
||||
AsyncClient.getChannel(data.id);
|
||||
Utils.switchChannel(data);
|
||||
},
|
||||
(err) => {
|
||||
if (err.message === 'Name must be 2 or more lowercase alphanumeric characters') {
|
||||
this.setState({flowState: SHOW_EDIT_URL_THEN_COMPLETE});
|
||||
}
|
||||
if (err.message === 'A channel with that handle already exists') {
|
||||
this.setState({serverError: 'A channel with that URL already exists'});
|
||||
return;
|
||||
}
|
||||
this.setState({serverError: err.message});
|
||||
}
|
||||
);
|
||||
}
|
||||
typeSwitched() {
|
||||
if (this.state.channelType === 'P') {
|
||||
this.setState({channelType: 'O'});
|
||||
} else {
|
||||
this.setState({channelType: 'P'});
|
||||
}
|
||||
}
|
||||
urlChangeRequested() {
|
||||
this.setState({flowState: SHOW_EDIT_URL});
|
||||
}
|
||||
urlChangeSubmitted(newURL) {
|
||||
if (this.state.flowState === SHOW_EDIT_URL_THEN_COMPLETE) {
|
||||
this.setState({channelName: newURL, nameModified: true}, this.doSubmit);
|
||||
} else {
|
||||
this.setState({flowState: SHOW_NEW_CHANNEL, serverError: '', channelName: newURL, nameModified: true});
|
||||
}
|
||||
}
|
||||
urlChangeDismissed() {
|
||||
this.setState({flowState: SHOW_NEW_CHANNEL});
|
||||
}
|
||||
channelDataChanged(data) {
|
||||
this.setState({
|
||||
channelDisplayName: data.displayName,
|
||||
channelDescription: data.description
|
||||
});
|
||||
if (!this.state.nameModified) {
|
||||
this.setState({channelName: Utils.cleanUpUrlable(data.displayName.trim())});
|
||||
}
|
||||
}
|
||||
render() {
|
||||
const channelData = {
|
||||
name: this.state.channelName,
|
||||
displayName: this.state.channelDisplayName,
|
||||
description: this.state.channelDescription
|
||||
};
|
||||
|
||||
let showChannelModal = false;
|
||||
let showGroupModal = false;
|
||||
let showChangeURLModal = false;
|
||||
|
||||
let changeURLTitle = '';
|
||||
let changeURLSubmitButtonText = '';
|
||||
let channelTerm = '';
|
||||
|
||||
// Only listen to flow state if we are being shown
|
||||
if (this.props.show) {
|
||||
switch (this.state.flowState) {
|
||||
case SHOW_NEW_CHANNEL:
|
||||
if (this.state.channelType === 'O') {
|
||||
showChannelModal = true;
|
||||
channelTerm = 'Channel';
|
||||
} else {
|
||||
showGroupModal = true;
|
||||
channelTerm = 'Group';
|
||||
}
|
||||
break;
|
||||
case SHOW_EDIT_URL:
|
||||
showChangeURLModal = true;
|
||||
changeURLTitle = 'Change ' + channelTerm + ' URL';
|
||||
changeURLSubmitButtonText = 'Change ' + channelTerm + ' URL';
|
||||
break;
|
||||
case SHOW_EDIT_URL_THEN_COMPLETE:
|
||||
showChangeURLModal = true;
|
||||
changeURLTitle = 'Set ' + channelTerm + ' URL';
|
||||
changeURLSubmitButtonText = 'Create ' + channelTerm;
|
||||
break;
|
||||
}
|
||||
}
|
||||
return (
|
||||
<span>
|
||||
<NewChannelModal
|
||||
show={showChannelModal}
|
||||
channelType={'O'}
|
||||
channelData={channelData}
|
||||
serverError={this.state.serverError}
|
||||
onSubmitChannel={this.doSubmit}
|
||||
onModalDismissed={this.props.onModalDismissed}
|
||||
onTypeSwitched={this.typeSwitched}
|
||||
onChangeURLPressed={this.urlChangeRequested}
|
||||
onDataChanged={this.channelDataChanged}
|
||||
/>
|
||||
<NewChannelModal
|
||||
show={showGroupModal}
|
||||
channelType={'P'}
|
||||
channelData={channelData}
|
||||
serverError={this.state.serverError}
|
||||
onSubmitChannel={this.doSubmit}
|
||||
onModalDismissed={this.props.onModalDismissed}
|
||||
onTypeSwitched={this.typeSwitched}
|
||||
onChangeURLPressed={this.urlChangeRequested}
|
||||
onDataChanged={this.channelDataChanged}
|
||||
/>
|
||||
<ChangeURLModal
|
||||
show={showChangeURLModal}
|
||||
title={changeURLTitle}
|
||||
description={'Some characters are not allowed in URLs and may be removed.'}
|
||||
urlLabel={channelTerm + ' URL'}
|
||||
submitButtonText={changeURLSubmitButtonText}
|
||||
currentURL={this.state.channelName}
|
||||
serverError={this.state.serverError}
|
||||
onModalSubmit={this.urlChangeSubmitted}
|
||||
onModalDismissed={this.urlChangeDismissed}
|
||||
/>
|
||||
</span>
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
NewChannelFlow.defaultProps = {
|
||||
show: false,
|
||||
channelType: 'O'
|
||||
};
|
||||
|
||||
NewChannelFlow.propTypes = {
|
||||
show: React.PropTypes.bool.isRequired,
|
||||
channelType: React.PropTypes.string.isRequired,
|
||||
onModalDismissed: React.PropTypes.func.isRequired
|
||||
};
|
||||
198
web/react/components/new_channel_modal.jsx
Обычный файл
198
web/react/components/new_channel_modal.jsx
Обычный файл
@@ -0,0 +1,198 @@
|
||||
// Copyright (c) 2015 Spinpunch, Inc. All Rights Reserved.
|
||||
// See License.txt for license information.
|
||||
|
||||
const Utils = require('../utils/utils.jsx');
|
||||
var Modal = ReactBootstrap.Modal;
|
||||
|
||||
export default class NewChannelModal extends React.Component {
|
||||
constructor(props) {
|
||||
super(props);
|
||||
|
||||
this.handleSubmit = this.handleSubmit.bind(this);
|
||||
this.handleChange = this.handleChange.bind(this);
|
||||
|
||||
this.state = {
|
||||
displayNameError: ''
|
||||
};
|
||||
}
|
||||
componentWillReceiveProps(nextProps) {
|
||||
if (nextProps.show === true && this.props.show === false) {
|
||||
this.setState({
|
||||
displayNameError: ''
|
||||
});
|
||||
}
|
||||
}
|
||||
handleSubmit(e) {
|
||||
e.preventDefault();
|
||||
|
||||
const displayName = React.findDOMNode(this.refs.display_name).value.trim();
|
||||
if (displayName.length < 1) {
|
||||
this.setState({displayNameError: 'This field is required'});
|
||||
return;
|
||||
}
|
||||
|
||||
this.props.onSubmitChannel();
|
||||
}
|
||||
handleChange() {
|
||||
const newData = {
|
||||
displayName: React.findDOMNode(this.refs.display_name).value,
|
||||
description: React.findDOMNode(this.refs.channel_desc).value
|
||||
};
|
||||
this.props.onDataChanged(newData);
|
||||
}
|
||||
render() {
|
||||
var displayNameError = null;
|
||||
var serverError = null;
|
||||
var displayNameClass = 'form-group';
|
||||
|
||||
if (this.state.displayNameError) {
|
||||
displayNameError = <p className='input__help error'>{this.state.displayNameError}</p>;
|
||||
displayNameClass += ' has-error';
|
||||
}
|
||||
|
||||
if (this.props.serverError) {
|
||||
serverError = <div className='form-group has-error'><p className='input__help error'>{this.props.serverError}</p></div>;
|
||||
}
|
||||
|
||||
var channelTerm = '';
|
||||
var channelSwitchText = '';
|
||||
switch (this.props.channelType) {
|
||||
case 'P':
|
||||
channelTerm = 'Group';
|
||||
channelSwitchText = (
|
||||
<div className='modal-intro'>
|
||||
{'Create a new private group with restricted membership. '}
|
||||
<a
|
||||
href='#'
|
||||
onClick={this.props.onTypeSwitched}
|
||||
>
|
||||
{'Create a public channel'}
|
||||
</a>
|
||||
</div>
|
||||
);
|
||||
break;
|
||||
case 'O':
|
||||
channelTerm = 'Channel';
|
||||
channelSwitchText = (
|
||||
<div className='modal-intro'>
|
||||
{'Create a new public channel anyone can join. '}
|
||||
<a
|
||||
href='#'
|
||||
onClick={this.props.onTypeSwitched}
|
||||
>
|
||||
{'Create a private group'}
|
||||
</a>
|
||||
</div>
|
||||
);
|
||||
break;
|
||||
}
|
||||
|
||||
const prettyTeamURL = Utils.getShortenedTeamURL();
|
||||
|
||||
return (
|
||||
<span>
|
||||
<Modal
|
||||
show={this.props.show}
|
||||
onHide={this.props.onModalDismissed}
|
||||
>
|
||||
<Modal.Header closeButton={true}>
|
||||
<Modal.Title>{'New ' + channelTerm}</Modal.Title>
|
||||
</Modal.Header>
|
||||
<form
|
||||
role='form'
|
||||
className='form-horizontal'
|
||||
>
|
||||
<Modal.Body>
|
||||
<div>
|
||||
{channelSwitchText}
|
||||
</div>
|
||||
<div className={displayNameClass}>
|
||||
<label className='col-sm-2 form__label control-label'>{'Name'}</label>
|
||||
<div className='col-sm-10'>
|
||||
<input
|
||||
onChange={this.handleChange}
|
||||
type='text'
|
||||
ref='display_name'
|
||||
className='form-control'
|
||||
placeholder='Ex: "Bugs", "Marketing", "办公室恋情"'
|
||||
maxLength='22'
|
||||
value={this.props.channelData.displayName}
|
||||
autoFocus={true}
|
||||
tabIndex='1'
|
||||
/>
|
||||
{displayNameError}
|
||||
<p className='input__help'>
|
||||
{'Channel URL: ' + prettyTeamURL + this.props.channelData.name + ' ('}
|
||||
<a
|
||||
href='#'
|
||||
onClick={this.props.onChangeURLPressed}
|
||||
>
|
||||
{'change this URL'}
|
||||
</a>
|
||||
{')'}
|
||||
</p>
|
||||
</div>
|
||||
</div>
|
||||
<div className='form-group less'>
|
||||
<div className='col-sm-2'>
|
||||
<label className='form__label control-label'>{'Description'}</label>
|
||||
<label className='form__label light'>{'(optional)'}</label>
|
||||
</div>
|
||||
<div className='col-sm-10'>
|
||||
<textarea
|
||||
className='form-control no-resize'
|
||||
ref='channel_desc'
|
||||
rows='4'
|
||||
placeholder='Description'
|
||||
maxLength='1024'
|
||||
value={this.props.channelData.description}
|
||||
onChange={this.handleChange}
|
||||
tabIndex='2'
|
||||
/>
|
||||
<p className='input__help'>
|
||||
{'The purpose of your channel. To help others decide whether to join.'}
|
||||
</p>
|
||||
{serverError}
|
||||
</div>
|
||||
</div>
|
||||
</Modal.Body>
|
||||
<Modal.Footer>
|
||||
<button
|
||||
type='button'
|
||||
className='btn btn-default'
|
||||
onClick={this.props.onModalDismissed}
|
||||
>
|
||||
{'Cancel'}
|
||||
</button>
|
||||
<button
|
||||
onClick={this.handleSubmit}
|
||||
type='submit'
|
||||
className='btn btn-primary'
|
||||
tabIndex='3'
|
||||
>
|
||||
{'Create New ' + channelTerm}
|
||||
</button>
|
||||
</Modal.Footer>
|
||||
</form>
|
||||
</Modal>
|
||||
</span>
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
NewChannelModal.defaultProps = {
|
||||
show: false,
|
||||
channelType: 'O',
|
||||
serverError: ''
|
||||
};
|
||||
NewChannelModal.propTypes = {
|
||||
show: React.PropTypes.bool.isRequired,
|
||||
channelType: React.PropTypes.string.isRequired,
|
||||
channelData: React.PropTypes.object.isRequired,
|
||||
serverError: React.PropTypes.string,
|
||||
onSubmitChannel: React.PropTypes.func.isRequired,
|
||||
onModalDismissed: React.PropTypes.func.isRequired,
|
||||
onTypeSwitched: React.PropTypes.func.isRequired,
|
||||
onChangeURLPressed: React.PropTypes.func.isRequired,
|
||||
onDataChanged: React.PropTypes.func.isRequired
|
||||
};
|
||||
@@ -12,6 +12,7 @@ var Utils = require('../utils/utils.jsx');
|
||||
var SidebarHeader = require('./sidebar_header.jsx');
|
||||
var SearchBox = require('./search_bar.jsx');
|
||||
var Constants = require('../utils/constants.jsx');
|
||||
var NewChannelFlow = require('./new_channel_flow.jsx');
|
||||
|
||||
export default class Sidebar extends React.Component {
|
||||
constructor(props) {
|
||||
@@ -28,6 +29,7 @@ export default class Sidebar extends React.Component {
|
||||
this.createChannelElement = this.createChannelElement.bind(this);
|
||||
|
||||
this.state = this.getStateFromStores();
|
||||
this.state.modal = '';
|
||||
this.state.loadingDMChannel = -1;
|
||||
}
|
||||
getStateFromStores() {
|
||||
@@ -473,8 +475,18 @@ export default class Sidebar extends React.Component {
|
||||
);
|
||||
}
|
||||
|
||||
let showChannelModal = false;
|
||||
if (this.state.modal !== '') {
|
||||
showChannelModal = true;
|
||||
}
|
||||
|
||||
return (
|
||||
<div>
|
||||
<NewChannelFlow
|
||||
show={showChannelModal}
|
||||
channelType={this.state.modal}
|
||||
onModalDismissed={() => this.setState({modal: ''})}
|
||||
/>
|
||||
<SidebarHeader
|
||||
teamDisplayName={this.props.teamDisplayName}
|
||||
teamType={this.props.teamType}
|
||||
@@ -508,11 +520,9 @@ export default class Sidebar extends React.Component {
|
||||
<a
|
||||
className='add-channel-btn'
|
||||
href='#'
|
||||
data-toggle='modal'
|
||||
data-target='#new_channel'
|
||||
data-channeltype='O'
|
||||
onClick={() => this.setState({modal: 'O'})}
|
||||
>
|
||||
+
|
||||
{'+'}
|
||||
</a>
|
||||
</h4>
|
||||
</li>
|
||||
@@ -537,11 +547,9 @@ export default class Sidebar extends React.Component {
|
||||
<a
|
||||
className='add-channel-btn'
|
||||
href='#'
|
||||
data-toggle='modal'
|
||||
data-target='#new_channel'
|
||||
data-channeltype='P'
|
||||
onClick={() => this.setState({modal: 'P'})}
|
||||
>
|
||||
+
|
||||
{'+'}
|
||||
</a>
|
||||
</h4>
|
||||
</li>
|
||||
|
||||
Ссылка в новой задаче
Block a user