Merge branch 'master' into PLT-12-log
Этот коммит содержится в:
@@ -18,7 +18,8 @@
|
||||
"es6": true
|
||||
},
|
||||
"globals": {
|
||||
"React": false
|
||||
"React": false,
|
||||
"ReactBootstrap": false
|
||||
},
|
||||
"rules": {
|
||||
"comma-dangle": [2, "never"],
|
||||
|
||||
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
|
||||
};
|
||||
@@ -20,12 +20,11 @@ export default class FileUpload extends React.Component {
|
||||
}
|
||||
|
||||
fileUploadSuccess(channelId, data) {
|
||||
var parsedData = $.parseJSON(data);
|
||||
this.props.onFileUpload(parsedData.filenames, parsedData.client_ids, channelId);
|
||||
this.props.onFileUpload(data.filenames, data.client_ids, channelId);
|
||||
|
||||
var requests = this.state.requests;
|
||||
for (var j = 0; j < parsedData.client_ids.length; j++) {
|
||||
delete requests[parsedData.client_ids[j]];
|
||||
for (var j = 0; j < data.client_ids.length; j++) {
|
||||
delete requests[data.client_ids[j]];
|
||||
}
|
||||
this.setState({requests: requests});
|
||||
}
|
||||
@@ -53,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
|
||||
};
|
||||
@@ -152,7 +152,7 @@ export default class Post extends React.Component {
|
||||
return (
|
||||
<div>
|
||||
<div
|
||||
id={post.id}
|
||||
id={'post_' + post.id}
|
||||
className={'post ' + sameUserClass + ' ' + rootUser + ' ' + postType + ' ' + currentUserCss}
|
||||
>
|
||||
{profilePic}
|
||||
|
||||
@@ -189,9 +189,15 @@ export default class PostList extends React.Component {
|
||||
this.scrollToBottom(true);
|
||||
|
||||
// the user clicked 'load more messages'
|
||||
} else if (this.gotMorePosts) {
|
||||
var lastPost = oldPosts[oldOrder[prevState.numToDisplay]];
|
||||
$('#' + lastPost.id)[0].scrollIntoView();
|
||||
} else if (this.gotMorePosts && oldOrder.length > 0) {
|
||||
let index;
|
||||
if (prevState.numToDisplay >= oldOrder.length) {
|
||||
index = oldOrder.length - 1;
|
||||
} else {
|
||||
index = prevState.numToDisplay;
|
||||
}
|
||||
const lastPost = oldPosts[oldOrder[index]];
|
||||
$('#post_' + lastPost.id)[0].scrollIntoView();
|
||||
this.gotMorePosts = false;
|
||||
} else {
|
||||
this.scrollTo(this.prevScrollTop);
|
||||
|
||||
@@ -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>
|
||||
|
||||
@@ -32,7 +32,7 @@ export default class SignupUserComplete extends React.Component {
|
||||
handleSubmit(e) {
|
||||
e.preventDefault();
|
||||
|
||||
this.state.user.username = React.findDOMNode(this.refs.name).value.trim();
|
||||
this.state.user.username = React.findDOMNode(this.refs.name).value.trim().toLowerCase();
|
||||
if (!this.state.user.username) {
|
||||
this.setState({nameError: 'This field is required', emailError: '', passwordError: '', serverError: ''});
|
||||
return;
|
||||
|
||||
@@ -35,7 +35,7 @@ export default class TeamImportTab extends React.Component {
|
||||
var uploadHelpText = (
|
||||
<div>
|
||||
<p>{'Slack does not allow you to export files, images, private groups or direct messages stored in Slack. Therefore, Slack import to Mattermost only supports importing of text messages in your Slack team\'\s public channels.'}</p>
|
||||
<p>{'The Slack import to Mattermost is in "Preview". Slack bot posts and channels with underscores do not yet import.'}</p>
|
||||
<p>{'The Slack import to Mattermost is in "Preview". Slack bot posts do not yet import.'}</p>
|
||||
</div>
|
||||
);
|
||||
|
||||
|
||||
@@ -22,7 +22,7 @@ export default class TeamSignupUsernamePage extends React.Component {
|
||||
submitNext(e) {
|
||||
e.preventDefault();
|
||||
|
||||
var name = React.findDOMNode(this.refs.name).value.trim();
|
||||
var name = React.findDOMNode(this.refs.name).value.trim().toLowerCase();
|
||||
|
||||
var usernameError = Utils.isValidUsername(name);
|
||||
if (usernameError === 'Cannot use a reserved word as a username.') {
|
||||
|
||||
@@ -40,7 +40,7 @@ export default class UserSettingsGeneralTab extends React.Component {
|
||||
e.preventDefault();
|
||||
|
||||
var user = this.props.user;
|
||||
var username = this.state.username.trim();
|
||||
var username = this.state.username.trim().toLowerCase();
|
||||
|
||||
var usernameError = utils.isValidUsername(username);
|
||||
if (usernameError === 'Cannot use a reserved word as a username.') {
|
||||
|
||||
@@ -105,6 +105,14 @@ export default class ViewImageModal extends React.Component {
|
||||
this.loadImage(this.state.imgId);
|
||||
}.bind(this));
|
||||
|
||||
$('#' + this.props.modalId).on('hidden.bs.modal', function onModalHide() {
|
||||
if (this.refs.video) {
|
||||
var video = React.findDOMNode(this.refs.video);
|
||||
video.pause();
|
||||
video.currentTime = 0;
|
||||
}
|
||||
}.bind(this));
|
||||
|
||||
$(React.findDOMNode(this.refs.modal)).click(function onModalClick(e) {
|
||||
if (e.target === this || e.target === React.findDOMNode(this.refs.imageBody)) {
|
||||
$('.image_modal').modal('hide');
|
||||
@@ -211,6 +219,16 @@ export default class ViewImageModal extends React.Component {
|
||||
/>
|
||||
</a>
|
||||
);
|
||||
} else if (fileType === 'video' || fileType === 'audio') {
|
||||
content = (
|
||||
<video
|
||||
ref='video'
|
||||
data-setup='{}'
|
||||
controls='controls'
|
||||
>
|
||||
<source src={Utils.getWindowLocationOrigin() + '/api/v1/files/get' + filename} />
|
||||
</video>
|
||||
);
|
||||
} else {
|
||||
// non-image files include a section providing details about the file
|
||||
var infoString = 'File type ' + fileInfo.ext.toUpperCase();
|
||||
|
||||
@@ -3,22 +3,21 @@
|
||||
"version": "0.0.1",
|
||||
"private": true,
|
||||
"dependencies": {
|
||||
"autolinker": "^0.18.1",
|
||||
"flux": "^2.1.1",
|
||||
"keymirror": "^0.1.1",
|
||||
"object-assign": "^3.0.0",
|
||||
"react": "^0.13.3",
|
||||
"react-zeroclipboard-mixin": "^0.1.0",
|
||||
"twemoji": "^1.4.1"
|
||||
"autolinker": "0.18.1",
|
||||
"flux": "2.1.1",
|
||||
"keymirror": "0.1.1",
|
||||
"object-assign": "3.0.0",
|
||||
"react-zeroclipboard-mixin": "0.1.0",
|
||||
"twemoji": "1.4.1"
|
||||
},
|
||||
"devDependencies": {
|
||||
"browserify": "^11.0.1",
|
||||
"envify": "^3.4.0",
|
||||
"babelify": "^6.1.3",
|
||||
"uglify-js": "^2.4.24",
|
||||
"watchify": "^3.3.1",
|
||||
"eslint": "^1.3.1",
|
||||
"eslint-plugin-react": "^3.3.1"
|
||||
"browserify": "11.0.1",
|
||||
"envify": "3.4.0",
|
||||
"babelify": "6.1.3",
|
||||
"uglify-js": "2.4.24",
|
||||
"watchify": "3.3.1",
|
||||
"eslint": "1.3.1",
|
||||
"eslint-plugin-react": "3.3.1"
|
||||
},
|
||||
"scripts": {
|
||||
"start": "watchify --extension=jsx -o ../static/js/bundle.js -v -d ./**/*.jsx",
|
||||
|
||||
@@ -17,7 +17,6 @@ var RenameChannelModal = require('../components/rename_channel_modal.jsx');
|
||||
var EditPostModal = require('../components/edit_post_modal.jsx');
|
||||
var DeletePostModal = require('../components/delete_post_modal.jsx');
|
||||
var MoreChannelsModal = require('../components/more_channels.jsx');
|
||||
var NewChannelModal = require('../components/new_channel.jsx');
|
||||
var PostDeletedModal = require('../components/post_deleted_modal.jsx');
|
||||
var ChannelNotificationsModal = require('../components/channel_notifications.jsx');
|
||||
var UserSettingsModal = require('../components/user_settings_modal.jsx');
|
||||
@@ -153,11 +152,6 @@ function setupChannelPage(teamName, teamType, teamId, channelName, channelId) {
|
||||
document.getElementById('direct_channel_modal')
|
||||
);
|
||||
|
||||
React.render(
|
||||
<NewChannelModal />,
|
||||
document.getElementById('new_channel_modal')
|
||||
);
|
||||
|
||||
React.render(
|
||||
<PostListContainer />,
|
||||
document.getElementById('post-list')
|
||||
|
||||
@@ -828,14 +828,12 @@ export function isValidUsername(name) {
|
||||
} else if (name.length < 3 || name.length > 15) {
|
||||
error = 'Must be between 3 and 15 characters';
|
||||
} else if (!(/^[a-z0-9\.\-\_]+$/).test(name)) {
|
||||
error = "Must contain only lowercase letters, numbers, and the symbols '.', '-', and '_'.";
|
||||
error = "Must contain only letters, numbers, and the symbols '.', '-', and '_'.";
|
||||
} else if (!(/[a-z]/).test(name.charAt(0))) {
|
||||
error = 'First character must be a letter.';
|
||||
} else {
|
||||
var lowerName = name.toLowerCase().trim();
|
||||
|
||||
for (var i = 0; i < Constants.RESERVED_USERNAMES.length; i++) {
|
||||
if (lowerName === Constants.RESERVED_USERNAMES[i]) {
|
||||
if (name === Constants.RESERVED_USERNAMES[i]) {
|
||||
error = 'Cannot use a reserved word as a username.';
|
||||
break;
|
||||
}
|
||||
@@ -1127,3 +1125,14 @@ export function importSlack(file, success, error) {
|
||||
|
||||
client.importSlack(formData, success, error);
|
||||
}
|
||||
|
||||
export function getTeamURLFromAddressBar() {
|
||||
return window.location.href.split('/channels')[0];
|
||||
}
|
||||
|
||||
export function getShortenedTeamURL() {
|
||||
const teamURL = getTeamURLFromAddressBar();
|
||||
if (teamURL.length > 24) {
|
||||
return teamURL.substring(0, 10) + '...' + teamURL.substring(teamURL.length - 12, teamURL.length - 1) + '/';
|
||||
}
|
||||
}
|
||||
|
||||
33
web/sass-files/sass/partials/_forms.scss
Обычный файл
33
web/sass-files/sass/partials/_forms.scss
Обычный файл
@@ -0,0 +1,33 @@
|
||||
.form-horizontal {
|
||||
.modal-intro {
|
||||
margin: -10px 0 30px;
|
||||
}
|
||||
.form__label {
|
||||
text-align: left;
|
||||
padding-right: 3px;
|
||||
font-weight: bold;
|
||||
font-size: 1.1em;
|
||||
&.light {
|
||||
color: #999;
|
||||
font-size: 1.05em;
|
||||
font-style: italic;
|
||||
padding-top: 2px;
|
||||
}
|
||||
}
|
||||
.input__help {
|
||||
color: #777;
|
||||
margin: 10px 0 0 10px;
|
||||
&.error {
|
||||
color: #a94442;
|
||||
}
|
||||
}
|
||||
.form-control {
|
||||
font-weight: normal;
|
||||
}
|
||||
.form-group {
|
||||
margin-bottom: 25px;
|
||||
&.less {
|
||||
margin-bottom: 10px;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -145,6 +145,8 @@
|
||||
li a {
|
||||
padding: 3px 20px;
|
||||
color: #555;
|
||||
text-overflow: ellipsis;
|
||||
overflow: hidden;
|
||||
}
|
||||
}
|
||||
.dropdown__icon {
|
||||
|
||||
@@ -123,6 +123,7 @@
|
||||
}
|
||||
.more-channel-table {
|
||||
margin: 0;
|
||||
table-layout: fixed;
|
||||
p {
|
||||
font-size: 0.9em;
|
||||
overflow: hidden;
|
||||
@@ -154,7 +155,7 @@
|
||||
&.td--action {
|
||||
text-align: right;
|
||||
padding: 8px 15px 8px 8px;
|
||||
width: 70px;
|
||||
width: 80px;
|
||||
vertical-align: middle;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -12,7 +12,10 @@
|
||||
}
|
||||
.dropdown-menu {
|
||||
max-height: 300px;
|
||||
overflow: auto;
|
||||
overflow-x: hidden;
|
||||
overflow-y: auto;
|
||||
max-width: 200px;
|
||||
width: 200px;
|
||||
}
|
||||
.search__form {
|
||||
margin: 0;
|
||||
@@ -49,7 +52,7 @@
|
||||
left: 0;
|
||||
right: 0;
|
||||
width: 72%;
|
||||
color: #777;
|
||||
color: #fff;
|
||||
background: #2389D7;
|
||||
@include border-radius(50px);
|
||||
margin: 0 auto;
|
||||
|
||||
@@ -29,6 +29,7 @@
|
||||
@import "partials/videos";
|
||||
@import "partials/settings";
|
||||
@import "partials/modal";
|
||||
@import "partials/forms";
|
||||
@import "partials/mentions";
|
||||
@import "partials/command-box";
|
||||
@import "partials/error";
|
||||
|
||||
5
web/static/css/bootstrap-3.3.1.min.css
поставляемый
5
web/static/css/bootstrap-3.3.1.min.css
поставляемый
Различия файлов скрыты, потому что одна или несколько строк слишком длинны
Разница между файлами не показана из-за своего большого размера
Загрузить разницу
5
web/static/css/bootstrap-3.3.5.min.css
поставляемый
Обычный файл
5
web/static/css/bootstrap-3.3.5.min.css
поставляемый
Обычный файл
Различия файлов скрыты, потому что одна или несколько строк слишком длинны
7
web/static/js/bootstrap-3.3.1.min.js
поставляемый
7
web/static/js/bootstrap-3.3.1.min.js
поставляемый
Различия файлов скрыты, потому что одна или несколько строк слишком длинны
471
web/static/js/bootstrap-3.3.1.js → web/static/js/bootstrap-3.3.5.js
поставляемый
471
web/static/js/bootstrap-3.3.1.js → web/static/js/bootstrap-3.3.5.js
поставляемый
Разница между файлами не показана из-за своего большого размера
Загрузить разницу
7
web/static/js/bootstrap-3.3.5.min.js
поставляемый
Обычный файл
7
web/static/js/bootstrap-3.3.5.min.js
поставляемый
Обычный файл
Различия файлов скрыты, потому что одна или несколько строк слишком длинны
1024
web/static/js/jasny-bootstrap.js
Обычный файл
1024
web/static/js/jasny-bootstrap.js
Обычный файл
Разница между файлами не показана из-за своего большого размера
Загрузить разницу
6
web/static/js/jasny-bootstrap.min.js
поставляемый
Обычный файл
6
web/static/js/jasny-bootstrap.min.js
поставляемый
Обычный файл
Различия файлов скрыты, потому что одна или несколько строк слишком длинны
15678
web/static/js/react-bootstrap-0.25.1.js
Обычный файл
15678
web/static/js/react-bootstrap-0.25.1.js
Обычный файл
Разница между файлами не показана из-за своего большого размера
Загрузить разницу
14
web/static/js/react-bootstrap-0.25.1.min.js
поставляемый
Обычный файл
14
web/static/js/react-bootstrap-0.25.1.min.js
поставляемый
Обычный файл
Различия файлов скрыты, потому что одна или несколько строк слишком длинны
16
web/static/js/react-with-addons-0.13.1.min.js
поставляемый
16
web/static/js/react-with-addons-0.13.1.min.js
поставляемый
Различия файлов скрыты, потому что одна или несколько строк слишком длинны
@@ -1,5 +1,5 @@
|
||||
/**
|
||||
* React (with addons) v0.13.1
|
||||
* React (with addons) v0.13.3
|
||||
*/
|
||||
(function(f){if(typeof exports==="object"&&typeof module!=="undefined"){module.exports=f()}else if(typeof define==="function"&&define.amd){define([],f)}else{var g;if(typeof window!=="undefined"){g=window}else if(typeof global!=="undefined"){g=global}else if(typeof self!=="undefined"){g=self}else{g=this}g.React = f()}})(function(){var define,module,exports;return (function e(t,n,r){function s(o,u){if(!n[o]){if(!t[o]){var a=typeof require=="function"&&require;if(!u&&a)return a(o,!0);if(i)return i(o,!0);var f=new Error("Cannot find module '"+o+"'");throw f.code="MODULE_NOT_FOUND",f}var l=n[o]={exports:{}};t[o][0].call(l.exports,function(e){var n=t[o][1][e];return s(n?n:e)},l,l.exports,e,t,n,r)}return n[o].exports}var i=typeof require=="function"&&require;for(var o=0;o<r.length;o++)s(r[o]);return s})({1:[function(_dereq_,module,exports){
|
||||
/**
|
||||
@@ -710,7 +710,9 @@ var isUnitlessNumber = {
|
||||
columnCount: true,
|
||||
flex: true,
|
||||
flexGrow: true,
|
||||
flexPositive: true,
|
||||
flexShrink: true,
|
||||
flexNegative: true,
|
||||
fontWeight: true,
|
||||
lineClamp: true,
|
||||
lineHeight: true,
|
||||
@@ -723,7 +725,9 @@ var isUnitlessNumber = {
|
||||
|
||||
// SVG-related properties
|
||||
fillOpacity: true,
|
||||
strokeOpacity: true
|
||||
strokeDashoffset: true,
|
||||
strokeOpacity: true,
|
||||
strokeWidth: true
|
||||
};
|
||||
|
||||
/**
|
||||
@@ -3788,6 +3792,7 @@ var HTMLDOMPropertyConfig = {
|
||||
headers: null,
|
||||
height: MUST_USE_ATTRIBUTE,
|
||||
hidden: MUST_USE_ATTRIBUTE | HAS_BOOLEAN_VALUE,
|
||||
high: null,
|
||||
href: null,
|
||||
hrefLang: null,
|
||||
htmlFor: null,
|
||||
@@ -3798,6 +3803,7 @@ var HTMLDOMPropertyConfig = {
|
||||
lang: null,
|
||||
list: MUST_USE_ATTRIBUTE,
|
||||
loop: MUST_USE_PROPERTY | HAS_BOOLEAN_VALUE,
|
||||
low: null,
|
||||
manifest: MUST_USE_ATTRIBUTE,
|
||||
marginHeight: null,
|
||||
marginWidth: null,
|
||||
@@ -3812,6 +3818,7 @@ var HTMLDOMPropertyConfig = {
|
||||
name: null,
|
||||
noValidate: HAS_BOOLEAN_VALUE,
|
||||
open: HAS_BOOLEAN_VALUE,
|
||||
optimum: null,
|
||||
pattern: null,
|
||||
placeholder: null,
|
||||
poster: null,
|
||||
@@ -3825,6 +3832,7 @@ var HTMLDOMPropertyConfig = {
|
||||
rowSpan: null,
|
||||
sandbox: null,
|
||||
scope: null,
|
||||
scoped: HAS_BOOLEAN_VALUE,
|
||||
scrolling: null,
|
||||
seamless: MUST_USE_ATTRIBUTE | HAS_BOOLEAN_VALUE,
|
||||
selected: MUST_USE_PROPERTY | HAS_BOOLEAN_VALUE,
|
||||
@@ -3866,7 +3874,9 @@ var HTMLDOMPropertyConfig = {
|
||||
itemID: MUST_USE_ATTRIBUTE,
|
||||
itemRef: MUST_USE_ATTRIBUTE,
|
||||
// property is supported for OpenGraph in meta tags.
|
||||
property: null
|
||||
property: null,
|
||||
// IE-only attribute that controls focus behavior
|
||||
unselectable: MUST_USE_ATTRIBUTE
|
||||
},
|
||||
DOMAttributeNames: {
|
||||
acceptCharset: 'accept-charset',
|
||||
@@ -4475,7 +4485,7 @@ if ("production" !== "development") {
|
||||
if (typeof __REACT_DEVTOOLS_GLOBAL_HOOK__ === 'undefined') {
|
||||
console.debug(
|
||||
'Download the React DevTools for a better development experience: ' +
|
||||
'http://fb.me/react-devtools'
|
||||
'https://fb.me/react-devtools'
|
||||
);
|
||||
}
|
||||
}
|
||||
@@ -4502,7 +4512,7 @@ if ("production" !== "development") {
|
||||
if (!expectedFeatures[i]) {
|
||||
console.error(
|
||||
'One or more ES5 shim/shams expected by React are not available: ' +
|
||||
'http://fb.me/react-warning-polyfills'
|
||||
'https://fb.me/react-warning-polyfills'
|
||||
);
|
||||
break;
|
||||
}
|
||||
@@ -4510,7 +4520,7 @@ if ("production" !== "development") {
|
||||
}
|
||||
}
|
||||
|
||||
React.version = '0.13.1';
|
||||
React.version = '0.13.3';
|
||||
|
||||
module.exports = React;
|
||||
|
||||
@@ -6229,7 +6239,7 @@ var ReactClass = {
|
||||
("production" !== "development" ? warning(
|
||||
this instanceof Constructor,
|
||||
'Something is calling a React component directly. Use a factory or ' +
|
||||
'JSX instead. See: http://fb.me/react-legacyfactory'
|
||||
'JSX instead. See: https://fb.me/react-legacyfactory'
|
||||
) : null);
|
||||
}
|
||||
|
||||
@@ -6439,20 +6449,38 @@ ReactComponent.prototype.forceUpdate = function(callback) {
|
||||
*/
|
||||
if ("production" !== "development") {
|
||||
var deprecatedAPIs = {
|
||||
getDOMNode: 'getDOMNode',
|
||||
isMounted: 'isMounted',
|
||||
replaceProps: 'replaceProps',
|
||||
replaceState: 'replaceState',
|
||||
setProps: 'setProps'
|
||||
getDOMNode: [
|
||||
'getDOMNode',
|
||||
'Use React.findDOMNode(component) instead.'
|
||||
],
|
||||
isMounted: [
|
||||
'isMounted',
|
||||
'Instead, make sure to clean up subscriptions and pending requests in ' +
|
||||
'componentWillUnmount to prevent memory leaks.'
|
||||
],
|
||||
replaceProps: [
|
||||
'replaceProps',
|
||||
'Instead call React.render again at the top level.'
|
||||
],
|
||||
replaceState: [
|
||||
'replaceState',
|
||||
'Refactor your code to use setState instead (see ' +
|
||||
'https://github.com/facebook/react/issues/3236).'
|
||||
],
|
||||
setProps: [
|
||||
'setProps',
|
||||
'Instead call React.render again at the top level.'
|
||||
]
|
||||
};
|
||||
var defineDeprecationWarning = function(methodName, displayName) {
|
||||
var defineDeprecationWarning = function(methodName, info) {
|
||||
try {
|
||||
Object.defineProperty(ReactComponent.prototype, methodName, {
|
||||
get: function() {
|
||||
("production" !== "development" ? warning(
|
||||
false,
|
||||
'%s(...) is deprecated in plain JavaScript React classes.',
|
||||
displayName
|
||||
'%s(...) is deprecated in plain JavaScript React classes. %s',
|
||||
info[0],
|
||||
info[1]
|
||||
) : null);
|
||||
return undefined;
|
||||
}
|
||||
@@ -6802,6 +6830,14 @@ var ReactCompositeComponentMixin = {
|
||||
'Did you mean to define a state property instead?',
|
||||
this.getName() || 'a component'
|
||||
) : null);
|
||||
("production" !== "development" ? warning(
|
||||
!inst.getDefaultProps ||
|
||||
inst.getDefaultProps.isReactClassApproved,
|
||||
'getDefaultProps was defined on %s, a plain JavaScript class. ' +
|
||||
'This is only supported for classes created using React.createClass. ' +
|
||||
'Use a static property to define defaultProps instead.',
|
||||
this.getName() || 'a component'
|
||||
) : null);
|
||||
("production" !== "development" ? warning(
|
||||
!inst.propTypes,
|
||||
'propTypes was defined as an instance property on %s. Use a static ' +
|
||||
@@ -6838,6 +6874,7 @@ var ReactCompositeComponentMixin = {
|
||||
this._pendingReplaceState = false;
|
||||
this._pendingForceUpdate = false;
|
||||
|
||||
var childContext;
|
||||
var renderedElement;
|
||||
|
||||
var previouslyMounting = ReactLifeCycle.currentlyMountingInstance;
|
||||
@@ -6852,7 +6889,8 @@ var ReactCompositeComponentMixin = {
|
||||
}
|
||||
}
|
||||
|
||||
renderedElement = this._renderValidatedComponent();
|
||||
childContext = this._getValidatedChildContext(context);
|
||||
renderedElement = this._renderValidatedComponent(childContext);
|
||||
} finally {
|
||||
ReactLifeCycle.currentlyMountingInstance = previouslyMounting;
|
||||
}
|
||||
@@ -6866,7 +6904,7 @@ var ReactCompositeComponentMixin = {
|
||||
this._renderedComponent,
|
||||
rootID,
|
||||
transaction,
|
||||
this._processChildContext(context)
|
||||
this._mergeChildContext(context, childContext)
|
||||
);
|
||||
if (inst.componentDidMount) {
|
||||
transaction.getReactMountReady().enqueue(inst.componentDidMount, inst);
|
||||
@@ -6996,7 +7034,7 @@ var ReactCompositeComponentMixin = {
|
||||
* @return {object}
|
||||
* @private
|
||||
*/
|
||||
_processChildContext: function(currentContext) {
|
||||
_getValidatedChildContext: function(currentContext) {
|
||||
var inst = this._instance;
|
||||
var childContext = inst.getChildContext && inst.getChildContext();
|
||||
if (childContext) {
|
||||
@@ -7021,6 +7059,13 @@ var ReactCompositeComponentMixin = {
|
||||
name
|
||||
) : invariant(name in inst.constructor.childContextTypes));
|
||||
}
|
||||
return childContext;
|
||||
}
|
||||
return null;
|
||||
},
|
||||
|
||||
_mergeChildContext: function(currentContext, childContext) {
|
||||
if (childContext) {
|
||||
return assign({}, currentContext, childContext);
|
||||
}
|
||||
return currentContext;
|
||||
@@ -7280,6 +7325,10 @@ var ReactCompositeComponentMixin = {
|
||||
return inst.state;
|
||||
}
|
||||
|
||||
if (replace && queue.length === 1) {
|
||||
return queue[0];
|
||||
}
|
||||
|
||||
var nextState = assign({}, replace ? queue[0] : inst.state);
|
||||
for (var i = replace ? 1 : 0; i < queue.length; i++) {
|
||||
var partial = queue[i];
|
||||
@@ -7349,13 +7398,14 @@ var ReactCompositeComponentMixin = {
|
||||
_updateRenderedComponent: function(transaction, context) {
|
||||
var prevComponentInstance = this._renderedComponent;
|
||||
var prevRenderedElement = prevComponentInstance._currentElement;
|
||||
var nextRenderedElement = this._renderValidatedComponent();
|
||||
var childContext = this._getValidatedChildContext();
|
||||
var nextRenderedElement = this._renderValidatedComponent(childContext);
|
||||
if (shouldUpdateReactComponent(prevRenderedElement, nextRenderedElement)) {
|
||||
ReactReconciler.receiveComponent(
|
||||
prevComponentInstance,
|
||||
nextRenderedElement,
|
||||
transaction,
|
||||
this._processChildContext(context)
|
||||
this._mergeChildContext(context, childContext)
|
||||
);
|
||||
} else {
|
||||
// These two IDs are actually the same! But nothing should rely on that.
|
||||
@@ -7371,7 +7421,7 @@ var ReactCompositeComponentMixin = {
|
||||
this._renderedComponent,
|
||||
thisID,
|
||||
transaction,
|
||||
context
|
||||
this._mergeChildContext(context, childContext)
|
||||
);
|
||||
this._replaceNodeWithMarkupByID(prevComponentID, nextMarkup);
|
||||
}
|
||||
@@ -7409,11 +7459,12 @@ var ReactCompositeComponentMixin = {
|
||||
/**
|
||||
* @private
|
||||
*/
|
||||
_renderValidatedComponent: function() {
|
||||
_renderValidatedComponent: function(childContext) {
|
||||
var renderedComponent;
|
||||
var previousContext = ReactContext.current;
|
||||
ReactContext.current = this._processChildContext(
|
||||
this._currentElement._context
|
||||
ReactContext.current = this._mergeChildContext(
|
||||
this._currentElement._context,
|
||||
childContext
|
||||
);
|
||||
ReactCurrentOwner.current = this;
|
||||
try {
|
||||
@@ -7778,6 +7829,7 @@ var ReactDOM = mapObject({
|
||||
|
||||
// SVG
|
||||
circle: 'circle',
|
||||
clipPath: 'clipPath',
|
||||
defs: 'defs',
|
||||
ellipse: 'ellipse',
|
||||
g: 'g',
|
||||
@@ -7927,11 +7979,13 @@ function assertValidProps(props) {
|
||||
'Can only set one of `children` or `props.dangerouslySetInnerHTML`.'
|
||||
) : invariant(props.children == null));
|
||||
("production" !== "development" ? invariant(
|
||||
props.dangerouslySetInnerHTML.__html != null,
|
||||
typeof props.dangerouslySetInnerHTML === 'object' &&
|
||||
'__html' in props.dangerouslySetInnerHTML,
|
||||
'`props.dangerouslySetInnerHTML` must be in the form `{__html: ...}`. ' +
|
||||
'Please visit http://fb.me/react-invariant-dangerously-set-inner-html ' +
|
||||
'Please visit https://fb.me/react-invariant-dangerously-set-inner-html ' +
|
||||
'for more information.'
|
||||
) : invariant(props.dangerouslySetInnerHTML.__html != null));
|
||||
) : invariant(typeof props.dangerouslySetInnerHTML === 'object' &&
|
||||
'__html' in props.dangerouslySetInnerHTML));
|
||||
}
|
||||
if ("production" !== "development") {
|
||||
("production" !== "development" ? warning(
|
||||
@@ -8239,6 +8293,8 @@ ReactDOMComponent.Mixin = {
|
||||
if (propKey === STYLE) {
|
||||
if (nextProp) {
|
||||
nextProp = this._previousStyleCopy = assign({}, nextProp);
|
||||
} else {
|
||||
this._previousStyleCopy = null;
|
||||
}
|
||||
if (lastProp) {
|
||||
// Unset styles on `lastProp` but not on `nextProp`.
|
||||
@@ -10721,7 +10777,7 @@ function warnAndMonitorForKeyUse(message, element, parentType) {
|
||||
|
||||
("production" !== "development" ? warning(
|
||||
false,
|
||||
message + '%s%s See http://fb.me/react-warning-keys for more information.',
|
||||
message + '%s%s See https://fb.me/react-warning-keys for more information.',
|
||||
parentOrOwnerAddendum,
|
||||
childOwnerAddendum
|
||||
) : null);
|
||||
@@ -10845,9 +10901,9 @@ function warnForPropsMutation(propName, element) {
|
||||
|
||||
("production" !== "development" ? warning(
|
||||
false,
|
||||
'Don\'t set .props.%s of the React component%s. ' +
|
||||
'Instead, specify the correct value when ' +
|
||||
'initially creating the element.%s',
|
||||
'Don\'t set .props.%s of the React component%s. Instead, specify the ' +
|
||||
'correct value when initially creating the element or use ' +
|
||||
'React.cloneElement to make a new element with updated props.%s',
|
||||
propName,
|
||||
elementInfo,
|
||||
ownerInfo
|
||||
@@ -15236,6 +15292,7 @@ var ReactUpdates = _dereq_(100);
|
||||
var SyntheticEvent = _dereq_(108);
|
||||
|
||||
var assign = _dereq_(29);
|
||||
var emptyObject = _dereq_(130);
|
||||
|
||||
var topLevelTypes = EventConstants.topLevelTypes;
|
||||
|
||||
@@ -15577,6 +15634,9 @@ assign(
|
||||
);
|
||||
|
||||
ReactShallowRenderer.prototype.render = function(element, context) {
|
||||
if (!context) {
|
||||
context = emptyObject;
|
||||
}
|
||||
var transaction = ReactUpdates.ReactReconcileTransaction.getPooled();
|
||||
this._render(element, transaction, context);
|
||||
ReactUpdates.ReactReconcileTransaction.release(transaction);
|
||||
@@ -15717,7 +15777,7 @@ for (eventType in topLevelTypes) {
|
||||
|
||||
module.exports = ReactTestUtils;
|
||||
|
||||
},{"100":100,"108":108,"16":16,"18":18,"21":21,"29":29,"31":31,"33":33,"43":43,"63":63,"65":65,"72":72,"73":73,"77":77}],96:[function(_dereq_,module,exports){
|
||||
},{"100":100,"108":108,"130":130,"16":16,"18":18,"21":21,"29":29,"31":31,"33":33,"43":43,"63":63,"65":65,"72":72,"73":73,"77":77}],96:[function(_dereq_,module,exports){
|
||||
/**
|
||||
* Copyright 2013-2015, Facebook, Inc.
|
||||
* All rights reserved.
|
||||
@@ -16762,6 +16822,7 @@ var MUST_USE_ATTRIBUTE = DOMProperty.injection.MUST_USE_ATTRIBUTE;
|
||||
|
||||
var SVGDOMPropertyConfig = {
|
||||
Properties: {
|
||||
clipPath: MUST_USE_ATTRIBUTE,
|
||||
cx: MUST_USE_ATTRIBUTE,
|
||||
cy: MUST_USE_ATTRIBUTE,
|
||||
d: MUST_USE_ATTRIBUTE,
|
||||
@@ -16807,6 +16868,7 @@ var SVGDOMPropertyConfig = {
|
||||
y: MUST_USE_ATTRIBUTE
|
||||
},
|
||||
DOMAttributeNames: {
|
||||
clipPath: 'clip-path',
|
||||
fillOpacity: 'fill-opacity',
|
||||
fontFamily: 'font-family',
|
||||
fontSize: 'font-size',
|
||||
@@ -19713,6 +19775,7 @@ var shouldWrap = {
|
||||
// Force wrapping for SVG elements because if they get created inside a <div>,
|
||||
// they will be initialized in the wrong namespace (and will not display).
|
||||
'circle': true,
|
||||
'clipPath': true,
|
||||
'defs': true,
|
||||
'ellipse': true,
|
||||
'g': true,
|
||||
@@ -19755,6 +19818,7 @@ var markupWrap = {
|
||||
'th': trWrap,
|
||||
|
||||
'circle': svgWrap,
|
||||
'clipPath': svgWrap,
|
||||
'defs': svgWrap,
|
||||
'ellipse': svgWrap,
|
||||
'g': svgWrap,
|
||||
@@ -20100,6 +20164,7 @@ assign(
|
||||
function isInternalComponentType(type) {
|
||||
return (
|
||||
typeof type === 'function' &&
|
||||
typeof type.prototype !== 'undefined' &&
|
||||
typeof type.prototype.mountComponent === 'function' &&
|
||||
typeof type.prototype.receiveComponent === 'function'
|
||||
);
|
||||
@@ -21355,11 +21420,14 @@ module.exports = traverseAllChildren;
|
||||
* @providesModule update
|
||||
*/
|
||||
|
||||
/* global hasOwnProperty:true */
|
||||
|
||||
'use strict';
|
||||
|
||||
var assign = _dereq_(29);
|
||||
var keyOf = _dereq_(157);
|
||||
var invariant = _dereq_(150);
|
||||
var hasOwnProperty = {}.hasOwnProperty;
|
||||
|
||||
function shallowCopy(x) {
|
||||
if (Array.isArray(x)) {
|
||||
@@ -21419,7 +21487,7 @@ function update(value, spec) {
|
||||
COMMAND_SET
|
||||
) : invariant(typeof spec === 'object'));
|
||||
|
||||
if (spec.hasOwnProperty(COMMAND_SET)) {
|
||||
if (hasOwnProperty.call(spec, COMMAND_SET)) {
|
||||
("production" !== "development" ? invariant(
|
||||
Object.keys(spec).length === 1,
|
||||
'Cannot have more than one key in an object with %s',
|
||||
@@ -21431,7 +21499,7 @@ function update(value, spec) {
|
||||
|
||||
var nextValue = shallowCopy(value);
|
||||
|
||||
if (spec.hasOwnProperty(COMMAND_MERGE)) {
|
||||
if (hasOwnProperty.call(spec, COMMAND_MERGE)) {
|
||||
var mergeObj = spec[COMMAND_MERGE];
|
||||
("production" !== "development" ? invariant(
|
||||
mergeObj && typeof mergeObj === 'object',
|
||||
@@ -21448,21 +21516,21 @@ function update(value, spec) {
|
||||
assign(nextValue, spec[COMMAND_MERGE]);
|
||||
}
|
||||
|
||||
if (spec.hasOwnProperty(COMMAND_PUSH)) {
|
||||
if (hasOwnProperty.call(spec, COMMAND_PUSH)) {
|
||||
invariantArrayCase(value, spec, COMMAND_PUSH);
|
||||
spec[COMMAND_PUSH].forEach(function(item) {
|
||||
nextValue.push(item);
|
||||
});
|
||||
}
|
||||
|
||||
if (spec.hasOwnProperty(COMMAND_UNSHIFT)) {
|
||||
if (hasOwnProperty.call(spec, COMMAND_UNSHIFT)) {
|
||||
invariantArrayCase(value, spec, COMMAND_UNSHIFT);
|
||||
spec[COMMAND_UNSHIFT].forEach(function(item) {
|
||||
nextValue.unshift(item);
|
||||
});
|
||||
}
|
||||
|
||||
if (spec.hasOwnProperty(COMMAND_SPLICE)) {
|
||||
if (hasOwnProperty.call(spec, COMMAND_SPLICE)) {
|
||||
("production" !== "development" ? invariant(
|
||||
Array.isArray(value),
|
||||
'Expected %s target to be an array; got %s',
|
||||
@@ -21488,7 +21556,7 @@ function update(value, spec) {
|
||||
});
|
||||
}
|
||||
|
||||
if (spec.hasOwnProperty(COMMAND_APPLY)) {
|
||||
if (hasOwnProperty.call(spec, COMMAND_APPLY)) {
|
||||
("production" !== "development" ? invariant(
|
||||
typeof spec[COMMAND_APPLY] === 'function',
|
||||
'update(): expected spec of %s to be a function; got %s.',
|
||||
18
web/static/js/react-with-addons-0.13.3.min.js
поставляемый
Обычный файл
18
web/static/js/react-with-addons-0.13.3.min.js
поставляемый
Обычный файл
Различия файлов скрыты, потому что одна или несколько строк слишком длинны
@@ -52,6 +52,7 @@
|
||||
<script>
|
||||
window.setup_channel_page('{{ .Props.TeamDisplayName }}', '{{ .Props.TeamType }}', '{{ .Props.TeamId }}', '{{ .Props.ChannelName }}', '{{ .Props.ChannelId }}');
|
||||
$('body').tooltip( {selector: '[data-toggle=tooltip]'} );
|
||||
$('.modal-body').css('max-height', $(window).height() * 0.7);
|
||||
$('.modal-body').perfectScrollbar();
|
||||
</script>
|
||||
</body>
|
||||
|
||||
@@ -18,12 +18,13 @@
|
||||
<link rel="manifest" href="/static/config/manifest.json">
|
||||
<!-- Android add to homescreen -->
|
||||
|
||||
<link rel="stylesheet" href="/static/css/bootstrap-3.3.1.min.css">
|
||||
<link rel="stylesheet" href="/static/css/bootstrap-3.3.5.min.css">
|
||||
<link rel="stylesheet" href="/static/css/jasny-bootstrap.min.css" rel="stylesheet">
|
||||
|
||||
<script src="/static/js/react-with-addons-0.13.1.js"></script>
|
||||
<script src="/static/js/react-with-addons-0.13.3.js"></script>
|
||||
<script src="/static/js/jquery-1.11.1.js"></script>
|
||||
<script src="/static/js/bootstrap-3.3.1.js"></script>
|
||||
<script src="/static/js/bootstrap-3.3.5.js"></script>
|
||||
<script src="/static/js/react-bootstrap-0.25.1.js"></script>
|
||||
|
||||
<link id="favicon" rel="icon" href="/static/images/favicon.ico" type="image/x-icon">
|
||||
<link rel="shortcut icon" href="/static/images/favicon.ico" type="image/x-icon">
|
||||
|
||||
@@ -36,9 +36,14 @@ func TearDown() {
|
||||
func TestStatic(t *testing.T) {
|
||||
Setup()
|
||||
|
||||
resp, _ := http.Get(URL + "/static/images/favicon.ico")
|
||||
// add a short delay to make sure the server is ready to receive requests
|
||||
time.Sleep(1 * time.Second)
|
||||
|
||||
if resp.StatusCode != http.StatusOK {
|
||||
resp, err := http.Get(URL + "/static/images/favicon.ico")
|
||||
|
||||
if err != nil {
|
||||
t.Fatalf("got error while trying to get static files %v", err)
|
||||
} else if resp.StatusCode != http.StatusOK {
|
||||
t.Fatalf("couldn't get static files %v", resp.StatusCode)
|
||||
}
|
||||
}
|
||||
|
||||
Ссылка в новой задаче
Block a user