summaryrefslogtreecommitdiffstats
path: root/web/react
diff options
context:
space:
mode:
Diffstat (limited to 'web/react')
-rw-r--r--web/react/components/confirm_modal.jsx31
-rw-r--r--web/react/components/create_comment.jsx2
-rw-r--r--web/react/components/create_post.jsx6
-rw-r--r--web/react/components/invite_member_modal.jsx120
-rw-r--r--web/react/components/post_body.jsx16
-rw-r--r--web/react/components/user_settings.jsx9
-rw-r--r--web/react/components/view_image.jsx2
-rw-r--r--web/react/utils/async_client.jsx2
-rw-r--r--web/react/utils/constants.jsx1
-rw-r--r--web/react/utils/utils.jsx24
10 files changed, 149 insertions, 64 deletions
diff --git a/web/react/components/confirm_modal.jsx b/web/react/components/confirm_modal.jsx
new file mode 100644
index 000000000..3be13cf9b
--- /dev/null
+++ b/web/react/components/confirm_modal.jsx
@@ -0,0 +1,31 @@
+// Copyright (c) 2015 Spinpunch, Inc. All Rights Reserved.
+// See License.txt for license information.
+
+module.exports = React.createClass({
+ handleConfirm: function() {
+ $('#'+this.props.parent_id).attr('data-confirm', 'true');
+ $('#'+this.props.parent_id).modal('hide');
+ $('#'+this.props.id).modal('hide');
+ },
+ render: function() {
+ return (
+ <div className="modal fade" id={this.props.id} tabIndex="-1" role="dialog" aria-hidden="true">
+ <div className="modal-dialog">
+ <div className="modal-content">
+ <div className="modal-header">
+ <h4 className="modal-title">{this.props.title}</h4>
+ </div>
+ <div className="modal-body">
+ {this.props.message}
+ </div>
+ <div className="modal-footer">
+ <button type="button" className="btn btn-default" data-dismiss="modal">Cancel</button>
+ <button onClick={this.handleConfirm} type="button" className="btn btn-primary">{this.props.confirm_button}</button>
+ </div>
+ </div>
+ </div>
+ </div>
+ );
+ }
+});
+
diff --git a/web/react/components/create_comment.jsx b/web/react/components/create_comment.jsx
index 9bcbad079..cb7aa371c 100644
--- a/web/react/components/create_comment.jsx
+++ b/web/react/components/create_comment.jsx
@@ -145,7 +145,7 @@ module.exports = React.createClass({
onUserInput={this.handleUserInput}
onKeyPress={this.commentMsgKeyPress}
messageText={this.state.messageText}
- createMessage="Create a comment..."
+ createMessage="Add a comment..."
initialText=""
id="reply_textbox"
ref="textbox" />
diff --git a/web/react/components/create_post.jsx b/web/react/components/create_post.jsx
index 191be9bf8..5a0b6f85f 100644
--- a/web/react/components/create_post.jsx
+++ b/web/react/components/create_post.jsx
@@ -31,9 +31,7 @@ module.exports = React.createClass({
post.message = this.state.messageText;
- var repRegex = new RegExp("<br>", "g");
- if (post.message.replace(repRegex, " ").trim().length === 0
- && this.state.previews.length === 0) {
+ if (post.message.trim().length === 0 && this.state.previews.length === 0) {
return;
}
@@ -250,7 +248,7 @@ module.exports = React.createClass({
onUserInput={this.handleUserInput}
onKeyPress={this.postMsgKeyPress}
messageText={this.state.messageText}
- createMessage="Create a post..."
+ createMessage="Write a message..."
channelId={this.state.channel_id}
id="post_textbox"
ref="textbox" />
diff --git a/web/react/components/invite_member_modal.jsx b/web/react/components/invite_member_modal.jsx
index 1d2bbed84..5980664de 100644
--- a/web/react/components/invite_member_modal.jsx
+++ b/web/react/components/invite_member_modal.jsx
@@ -4,8 +4,37 @@
var utils = require('../utils/utils.jsx');
var Client =require('../utils/client.jsx');
var UserStore = require('../stores/user_store.jsx');
+var ConfirmModal = require('./confirm_modal.jsx');
module.exports = React.createClass({
+ componentDidMount: function() {
+ var self = this;
+ $('#invite_member').on('hide.bs.modal', function(e) {
+ if ($('#invite_member').attr('data-confirm') === 'true') {
+ $('#invite_member').attr('data-confirm', 'false');
+ return;
+ }
+
+ var not_empty = false;
+ for (var i = 0; i < self.state.invite_ids.length; i++) {
+ var index = self.state.invite_ids[i];
+ if (self.refs["email"+index].getDOMNode().value.trim() !== '') {
+ not_empty = true;
+ break;
+ }
+ }
+
+ if (not_empty) {
+ $('#confirm_invite_modal').modal('show');
+ e.preventDefault();
+ }
+
+ });
+
+ $('#invite_member').on('hidden.bs.modal', function() {
+ self.clearFields();
+ });
+ },
handleSubmit: function(e) {
var invite_ids = this.state.invite_ids;
var count = invite_ids.length;
@@ -56,22 +85,8 @@ module.exports = React.createClass({
Client.inviteMembers(data,
function() {
+ $(this.refs.modal.getDOMNode()).attr('data-confirm', 'true');
$(this.refs.modal.getDOMNode()).modal('hide');
- for (var i = 0; i < invite_ids.length; i++) {
- var index = invite_ids[i];
- this.refs["email"+index].getDOMNode().value = "";
- if (config.AllowInviteNames) {
- this.refs["first_name"+index].getDOMNode().value = "";
- this.refs["last_name"+index].getDOMNode().value = "";
- }
- }
- this.setState({
- invite_ids: [0],
- id_count: 0,
- email_errors: {},
- first_name_errors: {},
- last_name_errors: {}
- });
}.bind(this),
function(err) {
this.setState({ server_error: err });
@@ -89,6 +104,26 @@ module.exports = React.createClass({
invite_ids.push(count);
this.setState({ invite_ids: invite_ids, id_count: count });
},
+ clearFields: function() {
+ var invite_ids = this.state.invite_ids;
+
+ for (var i = 0; i < invite_ids.length; i++) {
+ var index = invite_ids[i];
+ this.refs["email"+index].getDOMNode().value = "";
+ if (config.AllowInviteNames) {
+ this.refs["first_name"+index].getDOMNode().value = "";
+ this.refs["last_name"+index].getDOMNode().value = "";
+ }
+ }
+
+ this.setState({
+ invite_ids: [0],
+ id_count: 0,
+ email_errors: {},
+ first_name_errors: {},
+ last_name_errors: {}
+ });
+ },
removeInviteFields: function(index) {
var invite_ids = this.state.invite_ids;
var i = invite_ids.indexOf(index);
@@ -147,29 +182,38 @@ module.exports = React.createClass({
var server_error = this.state.server_error ? <div className='form-group has-error'><label className='control-label'>{ this.state.server_error }</label></div> : null;
return (
- <div className="modal fade" ref="modal" id="invite_member" 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" aria-label="Close" data-reactid=".5.0.0.0.0"><span aria-hidden="true" data-reactid=".5.0.0.0.0.0">×</span></button>
- <h4 className="modal-title" id="myModalLabel">Invite New Member</h4>
- </div>
- <div ref="modalBody" className="modal-body">
- <form role="form">
- { invite_sections }
- </form>
- { server_error }
- <button type="button" className="btn btn-default" onClick={this.addInviteFields}>Add another</button>
- <br/>
- <br/>
- <label className='control-label'>People invited automatically join Town Square channel.</label>
- </div>
- <div className="modal-footer">
- <button type="button" className="btn btn-default" data-dismiss="modal">Close</button>
- <button onClick={this.handleSubmit} type="button" className="btn btn-primary">Send Invitations</button>
- </div>
- </div>
- </div>
+ <div>
+ <div className="modal fade" ref="modal" id="invite_member" 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" aria-label="Close" data-reactid=".5.0.0.0.0"><span aria-hidden="true" data-reactid=".5.0.0.0.0.0">×</span></button>
+ <h4 className="modal-title" id="myModalLabel">Invite New Member</h4>
+ </div>
+ <div ref="modalBody" className="modal-body">
+ <form role="form">
+ { invite_sections }
+ </form>
+ { server_error }
+ <button type="button" className="btn btn-default" onClick={this.addInviteFields}>Add another</button>
+ <br/>
+ <br/>
+ <label className='control-label'>People invited automatically join Town Square channel.</label>
+ </div>
+ <div className="modal-footer">
+ <button type="button" className="btn btn-default" data-dismiss="modal">Close</button>
+ <button onClick={this.handleSubmit} type="button" className="btn btn-primary">Send Invitations</button>
+ </div>
+ </div>
+ </div>
+ </div>
+ <ConfirmModal
+ id="confirm_invite_modal"
+ parent_id="invite_member"
+ title="Discard Invitations?"
+ message="You have unsent invitations, are you sure you want to discard them?"
+ confirm_button="Yes, Discard"
+ />
</div>
);
} else {
diff --git a/web/react/components/post_body.jsx b/web/react/components/post_body.jsx
index 55fc32c33..3079917ec 100644
--- a/web/react/components/post_body.jsx
+++ b/web/react/components/post_body.jsx
@@ -85,20 +85,22 @@ module.exports = React.createClass({
var postFiles = [];
var images = [];
if (filenames) {
- for (var i = 0; i < filenames.length && i < Constants.MAX_DISPLAY_FILES; i++) {
+ for (var i = 0; i < filenames.length; i++) {
var fileInfo = utils.splitFileLocation(filenames[i]);
if (Object.keys(fileInfo).length === 0) continue;
var type = utils.getFileType(fileInfo.ext);
if (type === "image") {
- postFiles.push(
- <div className="post-image__column" key={filenames[i]}>
- <a href="#" onClick={this.handleImageClick} data-img-id={images.length.toString()} data-toggle="modal" data-target={"#" + postImageModalId }><div ref={filenames[i]} className="post__load" style={{backgroundImage: 'url(/static/images/load.gif)'}}></div></a>
- </div>
- );
+ if (i < Constants.MAX_DISPLAY_FILES) {
+ postFiles.push(
+ <div className="post-image__column" key={filenames[i]}>
+ <a href="#" onClick={this.handleImageClick} data-img-id={images.length.toString()} data-toggle="modal" data-target={"#" + postImageModalId }><div ref={filenames[i]} className="post__load" style={{backgroundImage: 'url(/static/images/load.gif)'}}></div></a>
+ </div>
+ );
+ }
images.push(filenames[i]);
- } else {
+ } else if (i < Constants.MAX_DISPLAY_FILES) {
postFiles.push(
<div className="post-image__column custom-file" key={fileInfo.name}>
<a href={fileInfo.path+"."+fileInfo.ext} download={fileInfo.name+"."+fileInfo.ext}>
diff --git a/web/react/components/user_settings.jsx b/web/react/components/user_settings.jsx
index a9c2433f2..7d542a8b7 100644
--- a/web/react/components/user_settings.jsx
+++ b/web/react/components/user_settings.jsx
@@ -658,9 +658,10 @@ var SecurityTab = React.createClass({
);
} else {
var d = new Date(this.props.user.last_password_update);
- var hour = d.getHours() < 10 ? "0" + d.getHours() : String(d.getHours());
+ var hour = d.getHours() % 12 ? String(d.getHours() % 12) : "12";
var min = d.getMinutes() < 10 ? "0" + d.getMinutes() : String(d.getMinutes());
- var dateStr = "Last updated " + Constants.MONTHS[d.getMonth()] + " " + d.getDate() + ", " + d.getFullYear() + " at " + hour + ":" + min;
+ var timeOfDay = d.getHours() >= 12 ? " pm" : " am";
+ var dateStr = "Last updated " + Constants.MONTHS[d.getMonth()] + " " + d.getDate() + ", " + d.getFullYear() + " at " + hour + ":" + min + timeOfDay;
passwordSection = (
<SettingItemMin
@@ -1056,7 +1057,7 @@ var AppearanceTab = React.createClass({
themeSection = (
<SettingItemMax
- title="Theme"
+ title="Theme Color"
inputs={inputs}
submit={this.submitTheme}
server_error={server_error}
@@ -1066,7 +1067,7 @@ var AppearanceTab = React.createClass({
} else {
themeSection = (
<SettingItemMin
- title="Theme"
+ title="Theme Color"
describe={this.state.theme}
updateSection={function(){self.props.updateSection("theme");}}
/>
diff --git a/web/react/components/view_image.jsx b/web/react/components/view_image.jsx
index 4cb30e1d3..c573e9dbb 100644
--- a/web/react/components/view_image.jsx
+++ b/web/react/components/view_image.jsx
@@ -124,7 +124,7 @@ module.exports = React.createClass({
<div key={name+"_loading"}>
<img ref="placeholder" className="loader-image" src="/static/images/load.gif" />
{ percentage > 0 ?
- <span className="loader-percent" >{"Downloading " + percentage + "%"}</span>
+ <span className="loader-percent" >{"Previewing " + percentage + "%"}</span>
: ""}
</div>
);
diff --git a/web/react/utils/async_client.jsx b/web/react/utils/async_client.jsx
index bb7ca458f..fc8c23fd5 100644
--- a/web/react/utils/async_client.jsx
+++ b/web/react/utils/async_client.jsx
@@ -15,6 +15,8 @@ var ActionTypes = Constants.ActionTypes;
var callTracker = {};
var dispatchError = function(err, method) {
+ if (err.message === "There appears to be a problem with your internet connection") return;
+
AppDispatcher.handleServerAction({
type: ActionTypes.RECIEVED_ERROR,
err: err,
diff --git a/web/react/utils/constants.jsx b/web/react/utils/constants.jsx
index 0a3b1db3d..deb07409b 100644
--- a/web/react/utils/constants.jsx
+++ b/web/react/utils/constants.jsx
@@ -61,6 +61,7 @@ module.exports = {
"channel",
"internal",
"localhost",
+ "dockerhost",
"stag",
"post",
"cluster",
diff --git a/web/react/utils/utils.jsx b/web/react/utils/utils.jsx
index 628d92342..a5d1e8895 100644
--- a/web/react/utils/utils.jsx
+++ b/web/react/utils/utils.jsx
@@ -28,6 +28,9 @@ module.exports.isTestDomain = function() {
if ((/^localhost/).test(window.location.hostname))
return true;
+ if ((/^dockerhost/).test(window.location.hostname))
+ return true;
+
if ((/^test/).test(window.location.hostname))
return true;
@@ -75,8 +78,14 @@ module.exports.getDomainWithOutSub = function() {
var parts = window.location.host.split(".");
- if (parts.length == 1)
- return "localhost:8065";
+ if (parts.length == 1) {
+ if (parts[0].indexOf("dockerhost") > -1) {
+ return "dockerhost:8065";
+ }
+ else {
+ return "localhost:8065";
+ }
+ }
return parts[1] + "." + parts[2];
}
@@ -357,9 +366,6 @@ module.exports.textToJsx = function(text, options) {
if (options && options['singleline']) {
var repRegex = new RegExp("\n", "g");
text = text.replace(repRegex, " ");
- } else {
- var repRegex = new RegExp("\n", "g");
- text = text.replace(repRegex, "<br>");
}
var searchTerm = ""
@@ -383,7 +389,7 @@ module.exports.textToJsx = function(text, options) {
implicitKeywords[keywordArray[i]] = true;
}
- var lines = text.split("<br>");
+ var lines = text.split("\n");
var urlMatcher = new LinkifyIt();
for (var i = 0; i < lines.length; i++) {
var line = lines[i];
@@ -412,7 +418,7 @@ module.exports.textToJsx = function(text, options) {
highlightSearchClass = " search-highlight";
}
- inner.push(<span key={name+i+z+"_span"}>{prefix}<a className={mClass + highlightSearchClass + " mention-link"} key={name+i+z+"_link"} href="#" onClick={function() {module.exports.searchForTerm(name);}}>@{name}</a>{suffix} </span>);
+ inner.push(<span key={name+i+z+"_span"}>{prefix}<a className={mClass + highlightSearchClass + " mention-link"} key={name+i+z+"_link"} href="#" onClick={function(value) { return function() { module.exports.searchForTerm(value); } }(name)}>@{name}</a>{suffix} </span>);
} else if (urlMatcher.test(word)) {
var match = urlMatcher.match(word)[0];
var link = match.url;
@@ -425,7 +431,7 @@ module.exports.textToJsx = function(text, options) {
} else if (trimWord.match(hashRegex)) {
var suffix = word.match(puncEndRegex);
var prefix = word.match(puncStartRegex);
- var mClass = trimWord in implicitKeywords ? mentionClass : "";
+ var mClass = trimWord in implicitKeywords || trimWord.toLowerCase() in implicitKeywords ? mentionClass : "";
if (searchTerm === trimWord.substring(1).toLowerCase() || searchTerm === trimWord.toLowerCase()) {
highlightSearchClass = " search-highlight";
@@ -433,7 +439,7 @@ module.exports.textToJsx = function(text, options) {
inner.push(<span key={word+i+z+"_span"}>{prefix}<a key={word+i+z+"_hash"} className={"theme " + mClass + highlightSearchClass} href="#" onClick={function(value) { return function() { module.exports.searchForTerm(value); } }(trimWord)}>{trimWord}</a>{suffix} </span>);
- } else if (trimWord in implicitKeywords) {
+ } else if (trimWord in implicitKeywords || trimWord.toLowerCase() in implicitKeywords) {
var suffix = word.match(puncEndRegex);
var prefix = word.match(puncStartRegex);