added client predictive commenting

Этот коммит содержится в:
JoramWilander
2015-07-23 12:45:08 -04:00
родитель 32f7b50bb5
Коммит 5596a6b37c
4 изменённых файлов: 97 добавлений и 17 удалений

Просмотреть файл

@@ -2,15 +2,16 @@
// See License.txt for license information. // See License.txt for license information.
var client = require('../utils/client.jsx'); 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 SocketStore = require('../stores/socket_store.jsx');
var ChannelStore = require('../stores/channel_store.jsx'); var ChannelStore = require('../stores/channel_store.jsx');
var UserStore = require('../stores/user_store.jsx');
var PostStore = require('../stores/post_store.jsx'); var PostStore = require('../stores/post_store.jsx');
var Textbox = require('./textbox.jsx'); var Textbox = require('./textbox.jsx');
var MsgTyping = require('./msg_typing.jsx'); var MsgTyping = require('./msg_typing.jsx');
var FileUpload = require('./file_upload.jsx'); var FileUpload = require('./file_upload.jsx');
var FilePreview = require('./file_preview.jsx'); var FilePreview = require('./file_preview.jsx');
var utils = require('../utils/utils.jsx');
var Constants = require('../utils/constants.jsx'); var Constants = require('../utils/constants.jsx');
module.exports = React.createClass({ module.exports = React.createClass({
@@ -39,10 +40,16 @@ module.exports = React.createClass({
return; return;
} }
var user_id = UserStore.getCurrentId();
post.channel_id = this.props.channelId; post.channel_id = this.props.channelId;
post.root_id = this.props.rootId; post.root_id = this.props.rootId;
post.parent_id = this.props.parentId; post.parent_id = this.props.rootId;
post.filenames = this.state.previews; 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}); this.setState({submitting: true, serverError: null});
@@ -69,10 +76,19 @@ module.exports = React.createClass({
$('#post_deleted').modal('show'); $('#post_deleted').modal('show');
} }
} else { } else {
this.setState(state); post.did_fail = true;
PostStore.updatePendingPost(post);
} }
state.submitting = false;
this.setState(state);
}.bind(this) }.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) { commentMsgKeyPress: function(e) {
if (e.which === 13 && !e.shiftKey && !e.altKey) { if (e.which === 13 && !e.shiftKey && !e.altKey) {

Просмотреть файл

@@ -214,20 +214,34 @@ module.exports = React.createClass({
var channelId = ChannelStore.getCurrentId(); var channelId = ChannelStore.getCurrentId();
if (this.state.channelId !== channelId) { if (this.state.channelId !== channelId) {
var draft = PostStore.getCurrentDraft(); var draft = PostStore.getCurrentDraft();
this.setState({
channelId: channelId, messageText: draft['message'], initialText: draft['message'], submitting: false, var previews = [];
serverError: null, postError: null, previews: draft['previews'], uploadsInProgress: draft['uploadsInProgress'] 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() { getInitialState: function() {
PostStore.clearDraftUploads(); PostStore.clearDraftUploads();
PostStore.clearPendingPosts(ChannelStore.getCurrentId());
var draft = PostStore.getCurrentDraft(); var draft = PostStore.getCurrentDraft();
return { var previews = [];
channelId: ChannelStore.getCurrentId(), messageText: draft['message'], uploadsInProgress: draft['uploadsInProgress'], var messageText = '';
previews: draft['previews'], submitting: false, initialText: draft['message'] 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) { getFileCount: function(channelId) {
if (channelId === this.state.channelId) { if (channelId === this.state.channelId) {

Просмотреть файл

@@ -22,16 +22,19 @@ module.exports = React.createClass({
var comments = ""; var comments = "";
var lastCommentClass = this.props.isLastComment ? " comment-icon__container__show" : " comment-icon__container__hide"; 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>; 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 ( return (
<ul className="post-header post-info"> <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"><time className="post-profile-time">{ utils.displayDateTime(post.create_at) }</time></li>
<li className="post-header-col post-header__reply"> <li className="post-header-col post-header__reply">
<div className="dropdown"> <div className="dropdown">
{ isOwner || (this.props.allowReply === "true" && type != "Comment") ? { show_dropdown ?
<div> <div>
<a href="#" className="dropdown-toggle theme" type="button" data-toggle="dropdown" aria-expanded="false" /> <a href="#" className="dropdown-toggle theme" type="button" data-toggle="dropdown" aria-expanded="false" />
<ul className="dropdown-menu" role="menu"> <ul className="dropdown-menu" role="menu">

Просмотреть файл

@@ -12,6 +12,7 @@ var CreateComment = require( './create_comment.jsx' );
var Constants = require('../utils/constants.jsx'); var Constants = require('../utils/constants.jsx');
var FileAttachmentList = require('./file_attachment_list.jsx'); var FileAttachmentList = require('./file_attachment_list.jsx');
var FileUploadOverlay = require('./file_upload_overlay.jsx'); var FileUploadOverlay = require('./file_upload_overlay.jsx');
var client = require('../utils/client.jsx');
var ActionTypes = Constants.ActionTypes; var ActionTypes = Constants.ActionTypes;
RhsHeaderPost = React.createClass({ RhsHeaderPost = React.createClass({
@@ -119,10 +120,36 @@ RootPost = React.createClass({
}); });
CommentPost = 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() { render: function() {
var post = this.props.post; var post = this.props.post;
var commentClass = "post"; var commentClass = "post";
var post = this.props.post;
var currentUserCss = ""; var currentUserCss = "";
if (UserStore.getCurrentId() === post.user_id) { if (UserStore.getCurrentId() === post.user_id) {
@@ -139,6 +166,16 @@ CommentPost = React.createClass({
var message = utils.textToJsx(post.message); var message = utils.textToJsx(post.message);
var timestamp = UserStore.getCurrentUser().update_at; 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 ( return (
<div className={commentClass + " " + currentUserCss}> <div className={commentClass + " " + currentUserCss}>
<div className="post-profile-img__container"> <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"><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"><time className="post-right-comment-time">{ utils.displayDateTime(post.create_at) }</time></li>
<li className="post-header-col post-header__reply"> <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);}}> <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" /> <a href="#" className="dropdown-toggle theme" type="button" data-toggle="dropdown" aria-expanded="false" />
<ul className="dropdown-menu" role="menu"> <ul className="dropdown-menu" role="menu">
@@ -161,7 +198,7 @@ CommentPost = React.createClass({
</li> </li>
</ul> </ul>
<div className="post-body"> <div className="post-body">
<p>{message}</p> <p className={postClass}>{loading}{message}</p>
{ post.filenames && post.filenames.length > 0 ? { post.filenames && post.filenames.length > 0 ?
<FileAttachmentList <FileAttachmentList
filenames={post.filenames} filenames={post.filenames}
@@ -177,7 +214,17 @@ CommentPost = React.createClass({
}); });
function getStateFromStores() { 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({ module.exports = React.createClass({