summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorJoramWilander <jwawilander@gmail.com>2015-07-23 12:45:08 -0400
committerJoramWilander <jwawilander@gmail.com>2015-08-18 08:58:31 -0400
commit5596a6b37c440522176fdc05217161d7de7e169c (patch)
tree934ea924e0096d7d49621163c07da46ed67b68ac
parent32f7b50bb5c62d27def3f2e6d2839511c0b8f9a9 (diff)
downloadchat-5596a6b37c440522176fdc05217161d7de7e169c.tar.gz
chat-5596a6b37c440522176fdc05217161d7de7e169c.tar.bz2
chat-5596a6b37c440522176fdc05217161d7de7e169c.zip
added client predictive commenting
-rw-r--r--web/react/components/create_comment.jsx24
-rw-r--r--web/react/components/create_post.jsx30
-rw-r--r--web/react/components/post_info.jsx7
-rw-r--r--web/react/components/post_right.jsx53
4 files changed, 97 insertions, 17 deletions
diff --git a/web/react/components/create_comment.jsx b/web/react/components/create_comment.jsx
index 885efab7a..fbb05c77f 100644
--- a/web/react/components/create_comment.jsx
+++ b/web/react/components/create_comment.jsx
@@ -2,15 +2,16 @@
// See License.txt for license information.
var client = require('../utils/client.jsx');
-var AsyncClient =require('../utils/async_client.jsx');
+var AsyncClient = require('../utils/async_client.jsx');
var SocketStore = require('../stores/socket_store.jsx');
var ChannelStore = require('../stores/channel_store.jsx');
+var UserStore = require('../stores/user_store.jsx');
var PostStore = require('../stores/post_store.jsx');
var Textbox = require('./textbox.jsx');
var MsgTyping = require('./msg_typing.jsx');
var FileUpload = require('./file_upload.jsx');
var FilePreview = require('./file_preview.jsx');
-
+var utils = require('../utils/utils.jsx');
var Constants = require('../utils/constants.jsx');
module.exports = React.createClass({
@@ -39,10 +40,16 @@ module.exports = React.createClass({
return;
}
+ var user_id = UserStore.getCurrentId();
+
post.channel_id = this.props.channelId;
post.root_id = this.props.rootId;
- post.parent_id = this.props.parentId;
+ post.parent_id = this.props.rootId;
post.filenames = this.state.previews;
+ var time = utils.getTimestamp();
+ post.pending_post_id = user_id + ":"+ time;
+ post.user_id = user_id;
+ post.create_at = time;
this.setState({submitting: true, serverError: null});
@@ -69,10 +76,19 @@ module.exports = React.createClass({
$('#post_deleted').modal('show');
}
} else {
- this.setState(state);
+ post.did_fail = true;
+ PostStore.updatePendingPost(post);
}
+
+ state.submitting = false;
+ this.setState(state);
}.bind(this)
);
+
+ post.is_loading = true;
+ PostStore.storePendingPost(post);
+ PostStore.storeCommentDraft(this.props.rootId, null);
+ this.setState({ messageText: '', submitting: false, post_error: null, previews: [], server_error: null, limit_error: null });
},
commentMsgKeyPress: function(e) {
if (e.which === 13 && !e.shiftKey && !e.altKey) {
diff --git a/web/react/components/create_post.jsx b/web/react/components/create_post.jsx
index 76f2bf262..8110f5886 100644
--- a/web/react/components/create_post.jsx
+++ b/web/react/components/create_post.jsx
@@ -214,20 +214,34 @@ module.exports = React.createClass({
var channelId = ChannelStore.getCurrentId();
if (this.state.channelId !== channelId) {
var draft = PostStore.getCurrentDraft();
- this.setState({
- channelId: channelId, messageText: draft['message'], initialText: draft['message'], submitting: false,
- serverError: null, postError: null, previews: draft['previews'], uploadsInProgress: draft['uploadsInProgress']
- });
+
+ var previews = [];
+ var messageText = '';
+ var uploadsInProgress = 0;
+ if (draft && draft['previews'] && draft['message']) {
+ previews = draft['previews'];
+ messageText = draft['message'];
+ uploadsInProgress = draft['uploadsInProgress'];
+ }
+
+ this.setState({channelId: channelId, messageText: messageText, initialText: messageText, submitting: false, serverError: null, postError: null, previews: previews, uploadsInProgress: uploadsInProgress});
}
},
getInitialState: function() {
PostStore.clearDraftUploads();
+ PostStore.clearPendingPosts(ChannelStore.getCurrentId());
var draft = PostStore.getCurrentDraft();
- return {
- channelId: ChannelStore.getCurrentId(), messageText: draft['message'], uploadsInProgress: draft['uploadsInProgress'],
- previews: draft['previews'], submitting: false, initialText: draft['message']
- };
+ var previews = [];
+ var messageText = '';
+ var uploadsInProgress = 0;
+ if (draft && draft["previews"] && draft["message"]) {
+ previews = draft['previews'];
+ messageText = draft['message'];
+ uploadsInProgress = draft['uploadsInProgress'];
+ }
+
+ return { channelId: ChannelStore.getCurrentId(), messageText: messageText, uploadsInProgress: uploadsInProgress, previews: previews, submitting: false, initialText: messageText};
},
getFileCount: function(channelId) {
if (channelId === this.state.channelId) {
diff --git a/web/react/components/post_info.jsx b/web/react/components/post_info.jsx
index 8eaaf4e8c..93d028e18 100644
--- a/web/react/components/post_info.jsx
+++ b/web/react/components/post_info.jsx
@@ -22,16 +22,19 @@ module.exports = React.createClass({
var comments = "";
var lastCommentClass = this.props.isLastComment ? " comment-icon__container__show" : " comment-icon__container__hide";
- if (this.props.commentCount >= 1) {
+ if (this.props.commentCount >= 1 && !post.did_fail && !post.is_loading) {
comments = <a href="#" className={"comment-icon__container theme" + lastCommentClass} onClick={this.props.handleCommentClick}><span className="comment-icon" dangerouslySetInnerHTML={{__html: Constants.COMMENT_ICON }} />{this.props.commentCount}</a>;
}
+ var show_dropdown = isOwner || (this.props.allowReply === "true" && type != "Comment");
+ if (post.did_fail || post.is_loading) show_dropdown = false;
+
return (
<ul className="post-header post-info">
<li className="post-header-col"><time className="post-profile-time">{ utils.displayDateTime(post.create_at) }</time></li>
<li className="post-header-col post-header__reply">
<div className="dropdown">
- { isOwner || (this.props.allowReply === "true" && type != "Comment") ?
+ { show_dropdown ?
<div>
<a href="#" className="dropdown-toggle theme" type="button" data-toggle="dropdown" aria-expanded="false" />
<ul className="dropdown-menu" role="menu">
diff --git a/web/react/components/post_right.jsx b/web/react/components/post_right.jsx
index e46979ff7..69e514e11 100644
--- a/web/react/components/post_right.jsx
+++ b/web/react/components/post_right.jsx
@@ -12,6 +12,7 @@ var CreateComment = require( './create_comment.jsx' );
var Constants = require('../utils/constants.jsx');
var FileAttachmentList = require('./file_attachment_list.jsx');
var FileUploadOverlay = require('./file_upload_overlay.jsx');
+var client = require('../utils/client.jsx');
var ActionTypes = Constants.ActionTypes;
RhsHeaderPost = React.createClass({
@@ -119,10 +120,36 @@ RootPost = React.createClass({
});
CommentPost = React.createClass({
+ retryComment: function(e) {
+ e.preventDefault();
+
+ var post = this.props.post;
+ client.createPost(post, post.channel_id,
+ function(data) {
+ AsyncClient.getPosts(true);
+
+ var member = ChannelStore.getMember(post.channel_id);
+ member.msg_count = channel.total_msg_count;
+ member.last_viewed_at = (new Date).getTime();
+ ChannelStore.setChannelMember(member);
+ }.bind(this),
+ function(err) {
+ post.did_fail = true;
+ PostStore.updatePendingPost(post);
+ this.forceUpdate();
+ }.bind(this)
+ );
+
+ post.did_fail = false;
+ post.is_loading = true;
+ PostStore.updatePendingPost(post);
+ this.forceUpdate();
+ },
render: function() {
var post = this.props.post;
var commentClass = "post";
+ var post = this.props.post;
var currentUserCss = "";
if (UserStore.getCurrentId() === post.user_id) {
@@ -139,6 +166,16 @@ CommentPost = React.createClass({
var message = utils.textToJsx(post.message);
var timestamp = UserStore.getCurrentUser().update_at;
+ var loading;
+ var postClass = "";
+ if (post.did_fail) {
+ postClass += " post-fail";
+ loading = <a className="post-retry pull-right" href="#" onClick={this.retryComment}>Retry</a>;
+ } else if (post.is_loading) {
+ postClass += " post-waiting";
+ loading = <img className="post-loading-gif pull-right" src="/static/images/load.gif"/>;
+ }
+
return (
<div className={commentClass + " " + currentUserCss}>
<div className="post-profile-img__container">
@@ -149,7 +186,7 @@ CommentPost = React.createClass({
<li className="post-header-col"><strong><UserProfile userId={post.user_id} /></strong></li>
<li className="post-header-col"><time className="post-right-comment-time">{ utils.displayDateTime(post.create_at) }</time></li>
<li className="post-header-col post-header__reply">
- { isOwner ?
+ { isOwner && !post.did_fail && !post.is_loading ?
<div className="dropdown" onClick={function(e){$('.post-list-holder-by-time').scrollTop($(".post-list-holder-by-time").scrollTop() + 50);}}>
<a href="#" className="dropdown-toggle theme" type="button" data-toggle="dropdown" aria-expanded="false" />
<ul className="dropdown-menu" role="menu">
@@ -161,7 +198,7 @@ CommentPost = React.createClass({
</li>
</ul>
<div className="post-body">
- <p>{message}</p>
+ <p className={postClass}>{loading}{message}</p>
{ post.filenames && post.filenames.length > 0 ?
<FileAttachmentList
filenames={post.filenames}
@@ -177,7 +214,17 @@ CommentPost = React.createClass({
});
function getStateFromStores() {
- return { post_list: PostStore.getSelectedPost() };
+ var post_list = PostStore.getSelectedPost();
+ if (!post_list || post_list.order.length < 1) return { post_list: {} };
+
+ var channel_id = post_list.posts[post_list.order[0]].channel_id;
+ var pending_post_list = PostStore.getPendingPosts(channel_id);
+
+ if (pending_post_list) {
+ for (var pid in pending_post_list.posts) { post_list.posts[pid] = pending_post_list.posts[pid] };
+ }
+
+ return { post_list: post_list };
}
module.exports = React.createClass({