adds predictive posting on the client
Этот коммит содержится в:
@@ -29,6 +29,7 @@ type Post struct {
|
|||||||
Props StringMap `json:"props"`
|
Props StringMap `json:"props"`
|
||||||
Hashtags string `json:"hashtags"`
|
Hashtags string `json:"hashtags"`
|
||||||
Filenames StringArray `json:"filenames"`
|
Filenames StringArray `json:"filenames"`
|
||||||
|
PendingPostId string `json:"pending_post_id" db:"-"`
|
||||||
}
|
}
|
||||||
|
|
||||||
func (o *Post) ToJson() string {
|
func (o *Post) ToJson() string {
|
||||||
|
|||||||
@@ -65,10 +65,17 @@ module.exports = React.createClass({
|
|||||||
post.channel_id = this.state.channelId;
|
post.channel_id = this.state.channelId;
|
||||||
post.filenames = this.state.previews;
|
post.filenames = this.state.previews;
|
||||||
|
|
||||||
client.createPost(post, ChannelStore.getCurrent(),
|
var time = utils.getTimestamp();
|
||||||
|
post.pending_post_id = user_id + ":"+ time;
|
||||||
|
post.user_id = user_id;
|
||||||
|
post.create_at = time;
|
||||||
|
post.root_id = this.state.rootId;
|
||||||
|
post.parent_id = this.state.parentId;
|
||||||
|
|
||||||
|
var channel = ChannelStore.getCurrent();
|
||||||
|
|
||||||
|
client.createPost(post, channel,
|
||||||
function(data) {
|
function(data) {
|
||||||
PostStore.storeDraft(data.channel_id, null);
|
|
||||||
this.setState({messageText: '', submitting: false, postError: null, previews: [], serverError: null});
|
|
||||||
this.resizePostHolder();
|
this.resizePostHolder();
|
||||||
AsyncClient.getPosts(true);
|
AsyncClient.getPosts(true);
|
||||||
|
|
||||||
@@ -79,13 +86,25 @@ module.exports = React.createClass({
|
|||||||
ChannelStore.setChannelMember(member);
|
ChannelStore.setChannelMember(member);
|
||||||
}.bind(this),
|
}.bind(this),
|
||||||
function(err) {
|
function(err) {
|
||||||
var state = {};
|
var state = {}
|
||||||
state.serverError = err.message;
|
|
||||||
|
if (err.message === "Invalid RootId parameter") {
|
||||||
|
if ($('#post_deleted').length > 0) $('#post_deleted').modal('show');
|
||||||
|
PostStore.removePendingPost(post.pending_post_id);
|
||||||
|
} else {
|
||||||
|
post.did_fail = true;
|
||||||
|
PostStore.updatePendingPost(post);
|
||||||
|
}
|
||||||
|
|
||||||
state.submitting = false;
|
state.submitting = false;
|
||||||
this.setState(state);
|
this.setState(state);
|
||||||
}.bind(this)
|
}.bind(this)
|
||||||
);
|
);
|
||||||
|
|
||||||
|
post.is_loading = true;
|
||||||
|
PostStore.storePendingPost(post);
|
||||||
|
PostStore.storeDraft(channel.id, user_id, null);
|
||||||
|
this.setState({messageText: '', submitting: false, postError: null, previews: [], serverError: null});
|
||||||
}
|
}
|
||||||
|
|
||||||
$('.post-list-holder-by-time').perfectScrollbar('update');
|
$('.post-list-holder-by-time').perfectScrollbar('update');
|
||||||
|
|||||||
@@ -7,6 +7,10 @@ var PostInfo = require('./post_info.jsx');
|
|||||||
var AppDispatcher = require('../dispatcher/app_dispatcher.jsx');
|
var AppDispatcher = require('../dispatcher/app_dispatcher.jsx');
|
||||||
var Constants = require('../utils/constants.jsx');
|
var Constants = require('../utils/constants.jsx');
|
||||||
var UserStore = require('../stores/user_store.jsx');
|
var UserStore = require('../stores/user_store.jsx');
|
||||||
|
var PostStore = require('../stores/post_store.jsx');
|
||||||
|
var ChannelStore = require('../stores/channel_store.jsx');
|
||||||
|
var client = require('../utils/client.jsx');
|
||||||
|
var AsyncClient = require('../utils/async_client.jsx');
|
||||||
var ActionTypes = Constants.ActionTypes;
|
var ActionTypes = Constants.ActionTypes;
|
||||||
|
|
||||||
module.exports = React.createClass({
|
module.exports = React.createClass({
|
||||||
@@ -32,6 +36,31 @@ module.exports = React.createClass({
|
|||||||
this.refs.info.forceUpdate();
|
this.refs.info.forceUpdate();
|
||||||
this.refs.header.forceUpdate();
|
this.refs.header.forceUpdate();
|
||||||
},
|
},
|
||||||
|
retryPost: 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();
|
||||||
|
},
|
||||||
getInitialState: function() {
|
getInitialState: function() {
|
||||||
return { };
|
return { };
|
||||||
},
|
},
|
||||||
@@ -79,7 +108,7 @@ module.exports = React.createClass({
|
|||||||
: null }
|
: null }
|
||||||
<div className="post__content">
|
<div className="post__content">
|
||||||
<PostHeader ref="header" post={post} sameRoot={this.props.sameRoot} commentCount={commentCount} handleCommentClick={this.handleCommentClick} isLastComment={this.props.isLastComment} />
|
<PostHeader ref="header" post={post} sameRoot={this.props.sameRoot} commentCount={commentCount} handleCommentClick={this.handleCommentClick} isLastComment={this.props.isLastComment} />
|
||||||
<PostBody post={post} sameRoot={this.props.sameRoot} parentPost={parentPost} posts={posts} handleCommentClick={this.handleCommentClick} />
|
<PostBody post={post} sameRoot={this.props.sameRoot} parentPost={parentPost} posts={posts} handleCommentClick={this.handleCommentClick} retryPost={this.retryPost} />
|
||||||
<PostInfo ref="info" post={post} sameRoot={this.props.sameRoot} commentCount={commentCount} handleCommentClick={this.handleCommentClick} allowReply="true" />
|
<PostInfo ref="info" post={post} sameRoot={this.props.sameRoot} commentCount={commentCount} handleCommentClick={this.handleCommentClick} allowReply="true" />
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|||||||
@@ -59,22 +59,34 @@ module.exports = React.createClass({
|
|||||||
postClass += " post-comment";
|
postClass += " post-comment";
|
||||||
}
|
}
|
||||||
|
|
||||||
|
var loading;
|
||||||
|
if (post.did_fail) {
|
||||||
|
postClass += " post-fail";
|
||||||
|
loading = <a className="post-retry pull-right" href="#" onClick={this.props.retryPost}>Retry</a>;
|
||||||
|
} else if (post.is_loading) {
|
||||||
|
postClass += " post-waiting";
|
||||||
|
loading = <img className="post-loading-gif pull-right" src="/static/images/load.gif"/>;
|
||||||
|
}
|
||||||
|
|
||||||
var embed;
|
var embed;
|
||||||
if (filenames.length === 0 && this.state.links) {
|
if (filenames.length === 0 && this.state.links) {
|
||||||
embed = utils.getEmbed(this.state.links[0]);
|
embed = utils.getEmbed(this.state.links[0]);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
var fileAttachmentHolder = '';
|
||||||
|
if (filenames && filenames.length > 0) {
|
||||||
|
fileAttachmentHolder = (<FileAttachmentList
|
||||||
|
filenames={filenames}
|
||||||
|
modalId={'view_image_modal_' + post.id}
|
||||||
|
channelId={post.channel_id}
|
||||||
|
userId={post.user_id} />);
|
||||||
|
}
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div className="post-body">
|
<div className="post-body">
|
||||||
{comment}
|
{comment}
|
||||||
<p key={post.id+"_message"} className={postClass}><span>{inner}</span></p>
|
<p key={post.id+"_message"} className={postClass}>{loading}<span>{inner}</span></p>
|
||||||
{ filenames && filenames.length > 0 ?
|
{fileAttachmentHolder}
|
||||||
<FileAttachmentList
|
|
||||||
filenames={filenames}
|
|
||||||
modalId={"view_image_modal_" + post.id}
|
|
||||||
channelId={post.channel_id}
|
|
||||||
userId={post.user_id} />
|
|
||||||
: "" }
|
|
||||||
{embed}
|
{embed}
|
||||||
</div>
|
</div>
|
||||||
);
|
);
|
||||||
|
|||||||
@@ -20,8 +20,16 @@ function getStateFromStores() {
|
|||||||
|
|
||||||
if (channel == null) channel = {};
|
if (channel == null) channel = {};
|
||||||
|
|
||||||
|
var post_list = PostStore.getCurrentPosts();
|
||||||
|
var pending_post_list = PostStore.getPendingPosts(channel.id);
|
||||||
|
|
||||||
|
if (pending_post_list) {
|
||||||
|
post_list.order = pending_post_list.order.concat(post_list.order);
|
||||||
|
for (var pid in pending_post_list.posts) { post_list.posts[pid] = pending_post_list.posts[pid] };
|
||||||
|
}
|
||||||
|
|
||||||
return {
|
return {
|
||||||
post_list: PostStore.getCurrentPosts(),
|
post_list: post_list,
|
||||||
channel: channel
|
channel: channel
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
@@ -186,8 +194,16 @@ module.exports = React.createClass({
|
|||||||
if (msg.action == "posted") {
|
if (msg.action == "posted") {
|
||||||
var post = JSON.parse(msg.props.post);
|
var post = JSON.parse(msg.props.post);
|
||||||
|
|
||||||
var post_list = PostStore.getPosts(msg.channel_id);
|
if (post.pending_post_id !== "") {
|
||||||
if (!post_list) return;
|
PostStore.removePendingPost(post.channel_id, post.pending_post_id);
|
||||||
|
}
|
||||||
|
|
||||||
|
post.pending_post_id = "";
|
||||||
|
|
||||||
|
postList.posts[post.id] = post;
|
||||||
|
if (postList.order.indexOf(post.id) === -1) {
|
||||||
|
postList.order.unshift(post.id);
|
||||||
|
}
|
||||||
|
|
||||||
post_list.posts[post.id] = post;
|
post_list.posts[post.id] = post;
|
||||||
if (post_list.order.indexOf(post.id) === -1) {
|
if (post_list.order.indexOf(post.id) === -1) {
|
||||||
@@ -456,7 +472,9 @@ module.exports = React.createClass({
|
|||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (post.create_at > last_viewed && !rendered_last_viewed) {
|
var userId = UserStore.getCurrentId();
|
||||||
|
|
||||||
|
if (post.user_id !== userId && post.create_at > last_viewed && !rendered_last_viewed) {
|
||||||
rendered_last_viewed = true;
|
rendered_last_viewed = true;
|
||||||
postCtls.push(
|
postCtls.push(
|
||||||
<div key="unviewed" className="new-separator">
|
<div key="unviewed" className="new-separator">
|
||||||
|
|||||||
@@ -19,7 +19,6 @@ var MENTION_DATA_CHANGE_EVENT = 'mention_data_change';
|
|||||||
var ADD_MENTION_EVENT = 'add_mention';
|
var ADD_MENTION_EVENT = 'add_mention';
|
||||||
|
|
||||||
var PostStore = assign({}, EventEmitter.prototype, {
|
var PostStore = assign({}, EventEmitter.prototype, {
|
||||||
|
|
||||||
emitChange: function emitChange() {
|
emitChange: function emitChange() {
|
||||||
this.emit(CHANGE_EVENT);
|
this.emit(CHANGE_EVENT);
|
||||||
},
|
},
|
||||||
@@ -104,6 +103,62 @@ var PostStore = assign({}, EventEmitter.prototype, {
|
|||||||
this.pStorePosts(channelId, posts);
|
this.pStorePosts(channelId, posts);
|
||||||
this.emitChange();
|
this.emitChange();
|
||||||
},
|
},
|
||||||
|
storePendingPost: function(post) {
|
||||||
|
var post_list = this.getPendingPosts(post.channel_id);
|
||||||
|
if (!post_list) {
|
||||||
|
post_list = {posts: {}, order: []};
|
||||||
|
}
|
||||||
|
|
||||||
|
post_list.posts[post.pending_post_id] = post;
|
||||||
|
post_list.order.unshift(post.pending_post_id);
|
||||||
|
this._storePendingPosts(post.channel_id, post_list);
|
||||||
|
this.emitChange();
|
||||||
|
},
|
||||||
|
_storePendingPosts: function(channelId, posts) {
|
||||||
|
BrowserStore.setItem("pending_posts_" + channelId, posts);
|
||||||
|
},
|
||||||
|
getPendingPosts: function(channelId) {
|
||||||
|
return BrowserStore.getItem("pending_posts_" + channelId);
|
||||||
|
},
|
||||||
|
removePendingPost: function(channelId, pending_post_id) {
|
||||||
|
this._removePendingPost(channelId, pending_post_id);
|
||||||
|
this.emitChange();
|
||||||
|
},
|
||||||
|
_removePendingPost: function(channelId, pending_post_id) {
|
||||||
|
var post_list = this.getPendingPosts(channelId);
|
||||||
|
if (!post_list) return;
|
||||||
|
|
||||||
|
if (pending_post_id in post_list.posts) delete post_list.posts[pending_post_id];
|
||||||
|
var index = post_list.order.indexOf(pending_post_id);
|
||||||
|
if (index >= 0) post_list.order.splice(index, 1);
|
||||||
|
|
||||||
|
this._storePendingPosts(channelId, post_list);
|
||||||
|
},
|
||||||
|
clearPendingPosts: function(channelId) {
|
||||||
|
BrowserStore.removeItem("pending_posts_" + channelId)
|
||||||
|
},
|
||||||
|
removeNonFailedPendingPosts: function(channelId) {
|
||||||
|
var post_list = this.getPendingPosts(channelId);
|
||||||
|
if (!post_list) return;
|
||||||
|
|
||||||
|
var posts = post_list.posts;
|
||||||
|
|
||||||
|
for (var id in posts) {
|
||||||
|
if (!posts[id].did_fail) this._removePendingPost(channelId, id);
|
||||||
|
}
|
||||||
|
},
|
||||||
|
updatePendingPost: function(post) {
|
||||||
|
var post_list = this.getPendingPosts(post.channel_id);
|
||||||
|
if (!post_list) {
|
||||||
|
post_list = {posts: {}, order: []};
|
||||||
|
}
|
||||||
|
|
||||||
|
if (post_list.order.indexOf(post.pending_post_id) === -1) return;
|
||||||
|
|
||||||
|
post_list.posts[post.pending_post_id] = post;
|
||||||
|
this._storePendingPosts(post.channel_id, post_list);
|
||||||
|
this.emitChange();
|
||||||
|
},
|
||||||
pStorePosts: function pStorePosts(channelId, posts) {
|
pStorePosts: function pStorePosts(channelId, posts) {
|
||||||
BrowserStore.setItem('posts_' + channelId, posts);
|
BrowserStore.setItem('posts_' + channelId, posts);
|
||||||
},
|
},
|
||||||
|
|||||||
@@ -377,6 +377,8 @@ module.exports.getPosts = function(force, id, maxPosts) {
|
|||||||
post_list: data
|
post_list: data
|
||||||
});
|
});
|
||||||
|
|
||||||
|
PostStore.removeNonFailedPendingPosts(channelId);
|
||||||
|
|
||||||
module.exports.getProfiles();
|
module.exports.getProfiles();
|
||||||
},
|
},
|
||||||
function(err) {
|
function(err) {
|
||||||
|
|||||||
@@ -461,3 +461,21 @@ body.ios {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.post-waiting {
|
||||||
|
color:lightgrey;
|
||||||
|
}
|
||||||
|
|
||||||
|
.post-loading-gif {
|
||||||
|
height:10px;
|
||||||
|
width:10px;
|
||||||
|
margin-top:6px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.post-fail {
|
||||||
|
color:red;
|
||||||
|
}
|
||||||
|
|
||||||
|
.post-retry {
|
||||||
|
color:darkgrey;
|
||||||
|
}
|
||||||
|
|||||||
Ссылка в новой задаче
Block a user