From 9e8cd937908d5d2e730e94f761d6533eb2d95e28 Mon Sep 17 00:00:00 2001 From: Christopher Speller Date: Wed, 18 Nov 2015 17:29:06 -0500 Subject: Implementing Permalinks and jumping to post from search. Performance improvements. --- web/react/stores/channel_store.jsx | 86 ++++--- web/react/stores/post_store.jsx | 509 ++++++++++++++++++++++++------------- web/react/stores/socket_store.jsx | 34 +-- 3 files changed, 408 insertions(+), 221 deletions(-) (limited to 'web/react/stores') diff --git a/web/react/stores/channel_store.jsx b/web/react/stores/channel_store.jsx index 1d481ada4..dec4926f5 100644 --- a/web/react/stores/channel_store.jsx +++ b/web/react/stores/channel_store.jsx @@ -8,8 +8,6 @@ var Utils; import Constants from '../utils/constants.jsx'; const ActionTypes = Constants.ActionTypes; -import BrowserStore from '../stores/browser_store.jsx'; - const CHANGE_EVENT = 'change'; const LEAVE_EVENT = 'leave'; const MORE_CHANGE_EVENT = 'change'; @@ -21,7 +19,38 @@ class ChannelStoreClass extends EventEmitter { this.setMaxListeners(11); + this.emitChange = this.emitChange.bind(this); + this.addChangeListener = this.addChangeListener.bind(this); + this.removeChangeListener = this.removeChangeListener.bind(this); + this.emitMoreChange = this.emitMoreChange.bind(this); + this.addMoreChangeListener = this.addMoreChangeListener.bind(this); + this.removeMoreChangeListener = this.removeMoreChangeListener.bind(this); + this.emitExtraInfoChange = this.emitExtraInfoChange.bind(this); + this.addExtraInfoChangeListener = this.addExtraInfoChangeListener.bind(this); + this.removeExtraInfoChangeListener = this.removeExtraInfoChangeListener.bind(this); + this.emitLeave = this.emitLeave.bind(this); + this.addLeaveListener = this.addLeaveListener.bind(this); + this.removeLeaveListener = this.removeLeaveListener.bind(this); + this.findFirstBy = this.findFirstBy.bind(this); + this.get = this.get.bind(this); + this.getMember = this.getMember.bind(this); + this.getByName = this.getByName.bind(this); + this.pSetPostMode = this.pSetPostMode.bind(this); + this.getPostMode = this.getPostMode.bind(this); + this.currentId = null; + this.postMode = this.POST_MODE_CHANNEL; + this.channels = []; + this.channelMembers = {}; + this.moreChannels = {}; + this.moreChannels.loading = true; + this.extraInfos = {}; + } + get POST_MODE_CHANNEL() { + return 1; + } + get POST_MODE_FOCUS() { + return 2; } emitChange() { this.emit(CHANGE_EVENT); @@ -90,16 +119,6 @@ class ChannelStoreClass extends EventEmitter { setCurrentId(id) { this.currentId = id; } - setLastVisitedName(name) { - if (name == null) { - BrowserStore.removeItem('last_visited_name'); - } else { - BrowserStore.setItem('last_visited_name', name); - } - } - getLastVisitedName() { - return BrowserStore.getItem('last_visited_name'); - } resetCounts(id) { var cm = this.pGetChannelMembers(); for (var cmid in cm) { @@ -192,10 +211,10 @@ class ChannelStoreClass extends EventEmitter { this.pStoreChannels(channels); } pStoreChannels(channels) { - BrowserStore.setItem('channels', channels); + this.channels = channels; } pGetChannels() { - return BrowserStore.getItem('channels', []); + return this.channels; } pStoreChannelMember(channelMember) { var members = this.pGetChannelMembers(); @@ -203,49 +222,58 @@ class ChannelStoreClass extends EventEmitter { this.pStoreChannelMembers(members); } pStoreChannelMembers(channelMembers) { - BrowserStore.setItem('channel_members', channelMembers); + this.channelMembers = channelMembers; } pGetChannelMembers() { - return BrowserStore.getItem('channel_members', {}); + return this.channelMembers; } pStoreMoreChannels(channels) { - BrowserStore.setItem('more_channels', channels); + this.moreChannels = channels; } pGetMoreChannels() { - var channels = BrowserStore.getItem('more_channels'); - - if (channels == null) { - channels = {}; - channels.loading = true; - } - - return channels; + return this.moreChannels; } pStoreExtraInfos(extraInfos) { - BrowserStore.setItem('extra_infos', extraInfos); + this.extraInfos = extraInfos; } pGetExtraInfos() { - return BrowserStore.getItem('extra_infos', {}); + return this.extraInfos; } isDefault(channel) { return channel.name === Constants.DEFAULT_CHANNEL; } + + pSetPostMode(mode) { + this.postMode = mode; + } + + getPostMode() { + return this.postMode; + } } var ChannelStore = new ChannelStoreClass(); -ChannelStore.dispatchToken = AppDispatcher.register(function handleAction(payload) { +ChannelStore.dispatchToken = AppDispatcher.register((payload) => { var action = payload.action; var currentId; switch (action.type) { case ActionTypes.CLICK_CHANNEL: ChannelStore.setCurrentId(action.id); - ChannelStore.setLastVisitedName(action.name); ChannelStore.resetCounts(action.id); + ChannelStore.pSetPostMode(ChannelStore.POST_MODE_CHANNEL); ChannelStore.emitChange(); break; + case ActionTypes.RECIEVED_FOCUSED_POST: { + const post = action.post_list.posts[action.postId]; + ChannelStore.setCurrentId(post.channel_id); + ChannelStore.pSetPostMode(ChannelStore.POST_MODE_FOCUS); + ChannelStore.emitChange(); + break; + } + case ActionTypes.RECIEVED_CHANNELS: ChannelStore.pStoreChannels(action.channels); ChannelStore.pStoreChannelMembers(action.members); diff --git a/web/react/stores/post_store.jsx b/web/react/stores/post_store.jsx index ec01eef18..c76560c25 100644 --- a/web/react/stores/post_store.jsx +++ b/web/react/stores/post_store.jsx @@ -12,9 +12,10 @@ import Constants from '../utils/constants.jsx'; const ActionTypes = Constants.ActionTypes; const CHANGE_EVENT = 'change'; -const SELECTED_POST_CHANGE_EVENT = 'selected_post_change'; +const FOCUSED_POST_CHANGE = 'focused_post_change'; const EDIT_POST_EVENT = 'edit_post'; const POSTS_VIEW_JUMP_EVENT = 'post_list_jump'; +const SELECTED_POST_CHANGE_EVENT = 'selected_post_change'; class PostStoreClass extends EventEmitter { constructor() { @@ -24,10 +25,6 @@ class PostStoreClass extends EventEmitter { this.addChangeListener = this.addChangeListener.bind(this); this.removeChangeListener = this.removeChangeListener.bind(this); - this.emitSelectedPostChange = this.emitSelectedPostChange.bind(this); - this.addSelectedPostChangeListener = this.addSelectedPostChangeListener.bind(this); - this.removeSelectedPostChangeListener = this.removeSelectedPostChangeListener.bind(this); - this.emitEditPost = this.emitEditPost.bind(this); this.addEditPostListener = this.addEditPostListener.bind(this); this.removeEditPostListener = this.removeEditPostListner.bind(this); @@ -36,27 +33,49 @@ class PostStoreClass extends EventEmitter { this.addPostsViewJumpListener = this.addPostsViewJumpListener.bind(this); this.removePostsViewJumpListener = this.removePostsViewJumpListener.bind(this); - this.getCurrentPosts = this.getCurrentPosts.bind(this); + this.emitPostFocused = this.emitPostFocused.bind(this); + this.addPostFocusedListener = this.addPostFocusedListener.bind(this); + this.removePostFocusedListener = this.removePostFocusedListener.bind(this); + + this.makePostsInfo = this.makePostsInfo.bind(this); + + this.getAllPosts = this.getAllPosts.bind(this); + this.getEarliestPost = this.getEarliestPost.bind(this); + this.getLatestPost = this.getLatestPost.bind(this); + this.getVisiblePosts = this.getVisiblePosts.bind(this); + this.getVisibilityAtTop = this.getVisibilityAtTop.bind(this); + this.getVisibilityAtBottom = this.getVisibilityAtBottom.bind(this); + this.requestVisibilityIncrease = this.requestVisibilityIncrease.bind(this); + this.getFocusedPostId = this.getFocusedPostId.bind(this); + this.storePosts = this.storePosts.bind(this); - this.pStorePosts = this.pStorePosts.bind(this); - this.getPosts = this.getPosts.bind(this); - this.getPost = this.getPost.bind(this); this.storePost = this.storePost.bind(this); - this.pStorePost = this.pStorePost.bind(this); + this.storeFocusedPost = this.storeFocusedPost.bind(this); + this.checkBounds = this.checkBounds.bind(this); + + this.clearFocusedPost = this.clearFocusedPost.bind(this); + this.clearChannelVisibility = this.clearChannelVisibility.bind(this); + this.removePost = this.removePost.bind(this); - this.storePendingPost = this.storePendingPost.bind(this); - this.pStorePendingPosts = this.pStorePendingPosts.bind(this); + this.getPendingPosts = this.getPendingPosts.bind(this); - this.storeUnseenDeletedPost = this.storeUnseenDeletedPost.bind(this); - this.storeUnseenDeletedPosts = this.storeUnseenDeletedPosts.bind(this); - this.getUnseenDeletedPosts = this.getUnseenDeletedPosts.bind(this); - this.clearUnseenDeletedPosts = this.clearUnseenDeletedPosts.bind(this); + this.storePendingPost = this.storePendingPost.bind(this); this.removePendingPost = this.removePendingPost.bind(this); - this.pRemovePendingPost = this.pRemovePendingPost.bind(this); this.clearPendingPosts = this.clearPendingPosts.bind(this); this.updatePendingPost = this.updatePendingPost.bind(this); + + this.storeUnseenDeletedPost = this.storeUnseenDeletedPost.bind(this); + this.getUnseenDeletedPosts = this.getUnseenDeletedPosts.bind(this); + this.clearUnseenDeletedPosts = this.clearUnseenDeletedPosts.bind(this); + + // These functions are bad and work should be done to remove this system when the RHS dies this.storeSelectedPost = this.storeSelectedPost.bind(this); this.getSelectedPost = this.getSelectedPost.bind(this); + this.emitSelectedPostChange = this.emitSelectedPostChange.bind(this); + this.addSelectedPostChangeListener = this.addSelectedPostChangeListener.bind(this); + this.removeSelectedPostChangeListener = this.removeSelectedPostChangeListener.bind(this); + this.selectedPost = null; + this.getEmptyDraft = this.getEmptyDraft.bind(this); this.storeCurrentDraft = this.storeCurrentDraft.bind(this); this.getCurrentDraft = this.getCurrentDraft.bind(this); @@ -70,6 +89,9 @@ class PostStoreClass extends EventEmitter { this.getLatestUpdate = this.getLatestUpdate.bind(this); this.getCurrentUsersLatestPost = this.getCurrentUsersLatestPost.bind(this); this.getCommentCount = this.getCommentCount.bind(this); + + this.postsInfo = {}; + this.currentFocusedPostId = null; } emitChange() { this.emit(CHANGE_EVENT); @@ -83,16 +105,16 @@ class PostStoreClass extends EventEmitter { this.removeListener(CHANGE_EVENT, callback); } - emitSelectedPostChange(fromSearch) { - this.emit(SELECTED_POST_CHANGE_EVENT, fromSearch); + emitPostFocused() { + this.emit(FOCUSED_POST_CHANGE); } - addSelectedPostChangeListener(callback) { - this.on(SELECTED_POST_CHANGE_EVENT, callback); + addPostFocusedListener(callback) { + this.on(FOCUSED_POST_CHANGE, callback); } - removeSelectedPostChangeListener(callback) { - this.removeListener(SELECTED_POST_CHANGE_EVENT, callback); + removePostFocusedListener(callback) { + this.removeListener(FOCUSED_POST_CHANGE, callback); } emitEditPost(post) { @@ -131,104 +153,157 @@ class PostStoreClass extends EventEmitter { this.emitPostsViewJump(Constants.PostsViewJumpTypes.SIDEBAR_OPEN, null); } - getCurrentPosts() { - var currentId = ChannelStore.getCurrentId(); + // All this does is makes sure the postsInfo is not null for the specified channel + makePostsInfo(id) { + if (!this.postsInfo.hasOwnProperty(id)) { + this.postsInfo[id] = {}; + } + } - if (currentId != null) { - return this.getPosts(currentId); + getAllPosts(id) { + if (this.postsInfo.hasOwnProperty(id)) { + return Object.assign({}, this.postsInfo[id].postList); } + return null; } - storePosts(channelId, newPostsView) { - if (isPostListNull(newPostsView)) { + + getEarliestPost(id) { + if (this.postsInfo.hasOwnProperty(id)) { + return this.postsInfo[id].postList.posts[this.postsInfo[id].postList.order[this.postsInfo[id].postList.order.length - 1]]; + } + + return null; + } + + getLatestPost(id) { + if (this.postsInfo.hasOwnProperty(id)) { + return this.postsInfo[id].postList.posts[this.postsInfo[id].postList.order[0]]; + } + + return null; + } + + getVisiblePosts(id) { + if (this.postsInfo.hasOwnProperty(id) && this.postsInfo[id].hasOwnProperty('postList')) { + const postList = JSON.parse(JSON.stringify(this.postsInfo[id].postList)); + + // Only limit visibility if we are not focused on a post + if (this.currentFocusedPostId === null) { + postList.order = postList.order.slice(0, this.postsInfo[id].endVisible); + } + + // Add pending posts + if (this.postsInfo[id].hasOwnProperty('pendingPosts')) { + Object.assign(postList.posts, this.postsInfo[id].pendingPosts.posts); + postList.order = this.postsInfo[id].pendingPosts.order.concat(postList.order); + } + + // Add delteted posts + if (this.postsInfo[id].hasOwnProperty('deletedPosts')) { + Object.assign(postList.posts, this.postsInfo[id].deletedPosts); + + for (const postID in this.postsInfo[id].deletedPosts) { + if (this.postsInfo[id].deletedPosts.hasOwnProperty(postID)) { + postList.order.push(postID); + } + } + + // Merge would be faster + postList.order.sort((a, b) => { + if (postList.posts[a].create_at > postList.posts[b].create_at) { + return -1; + } + if (postList.posts[a].create_at < postList.posts[b].create_at) { + return 1; + } + return 0; + }); + } + + return postList; + } + + return null; + } + + getVisibilityAtTop(id) { + if (this.postsInfo.hasOwnProperty(id)) { + return this.postsInfo[id].atTop && this.postsInfo[id].endVisible >= this.postsInfo[id].postList.order.length; + } + + return false; + } + + getVisibilityAtBottom(id) { + if (this.postsInfo.hasOwnProperty(id)) { + return this.postsInfo[id].atBottom; + } + + return false; + } + + // Returns true if posts need to be fetched + requestVisibilityIncrease(id, ammount) { + const endVisible = this.postsInfo[id].endVisible; + const postList = this.postsInfo[id].postList; + if (this.getVisibilityAtTop(id)) { + return false; + } + this.postsInfo[id].endVisible += ammount; + this.emitChange(); + return endVisible + ammount > postList.order.length; + } + + getFocusedPostId() { + return this.currentFocusedPostId; + } + + storePosts(id, newPosts) { + if (isPostListNull(newPosts)) { return; } - var postList = makePostListNonNull(this.getPosts(channelId)); + const combinedPosts = makePostListNonNull(this.getAllPosts(id)); - for (const pid in newPostsView.posts) { - if (newPostsView.posts.hasOwnProperty(pid)) { - const np = newPostsView.posts[pid]; + for (const pid in newPosts.posts) { + if (newPosts.posts.hasOwnProperty(pid)) { + const np = newPosts.posts[pid]; if (np.delete_at === 0) { - postList.posts[pid] = np; - if (postList.order.indexOf(pid) === -1) { - postList.order.push(pid); + combinedPosts.posts[pid] = np; + if (combinedPosts.order.indexOf(pid) === -1) { + combinedPosts.order.push(pid); } } else { - if (pid in postList.posts) { - delete postList.posts[pid]; + if (pid in combinedPosts.posts) { + Reflect.deleteProperty(combinedPosts.posts, pid); } - const index = postList.order.indexOf(pid); + const index = combinedPosts.order.indexOf(pid); if (index !== -1) { - postList.order.splice(index, 1); + combinedPosts.order.splice(index, 1); } } } } - postList.order.sort((a, b) => { - if (postList.posts[a].create_at > postList.posts[b].create_at) { + combinedPosts.order.sort((a, b) => { + if (combinedPosts.posts[a].create_at > combinedPosts.posts[b].create_at) { return -1; } - if (postList.posts[a].create_at < postList.posts[b].create_at) { + if (combinedPosts.posts[a].create_at < combinedPosts.posts[b].create_at) { return 1; } return 0; }); - var latestUpdate = 0; - for (var pid in postList.posts) { - if (postList.posts[pid].update_at > latestUpdate) { - latestUpdate = postList.posts[pid].update_at; - } - } - - this.storeLatestUpdate(channelId, latestUpdate); - this.pStorePosts(channelId, postList); - this.emitChange(); - } - pStorePosts(channelId, posts) { - BrowserStore.setItem('posts_' + channelId, posts); - } - getPosts(channelId) { - return BrowserStore.getItem('posts_' + channelId); + this.makePostsInfo(id); + this.postsInfo[id].postList = combinedPosts; } - getPost(channelId, postId) { - return this.getPosts(channelId).posts[postId]; - } - getCurrentUsersLatestPost(channelId, rootId) { - const userId = UserStore.getCurrentId(); - var postList = makePostListNonNull(this.getPosts(channelId)); - var i = 0; - var len = postList.order.length; - var lastPost = null; - for (i; i < len; i++) { - let post = postList.posts[postList.order[i]]; - if (post.user_id === userId && (post.props && !post.props.from_webhook || !post.props)) { - if (rootId) { - if (post.root_id === rootId || post.id === rootId) { - lastPost = post; - break; - } - } else { - lastPost = post; - break; - } - } - } - - return lastPost; - } storePost(post) { - this.pStorePost(post); - this.emitChange(); - } - pStorePost(post) { - var postList = this.getPosts(post.channel_id); - postList = makePostListNonNull(postList); + const postList = makePostListNonNull(this.getAllPosts(post.channel_id)); if (post.pending_post_id !== '') { this.removePendingPost(post.channel_id, post.pending_post_id); @@ -241,65 +316,117 @@ class PostStoreClass extends EventEmitter { postList.order.unshift(post.id); } - this.pStorePosts(post.channel_id, postList); + this.makePostsInfo(post.channel_id); + this.postsInfo[post.channel_id].postList = postList; + } + + storeFocusedPost(postId, postList) { + const focusedPost = postList.posts[postId]; + if (!focusedPost) { + return; + } + this.currentFocusedPostId = postId; + this.storePosts(postId, postList); + } + + checkBounds(id, numRequested, postList, before) { + if (numRequested > postList.order.length) { + if (before) { + this.postsInfo[id].atTop = true; + } else { + this.postsInfo[id].atBottom = true; + } + } } - removePost(postId, channelId) { - var postList = this.getPosts(channelId); + + clearFocusedPost() { + if (this.currentFocusedPostId != null) { + Reflect.deleteProperty(this.postsInfo, this.currentFocusedPostId); + this.currentFocusedPostId = null; + } + } + + clearChannelVisibility(id, atBottom) { + this.makePostsInfo(id); + this.postsInfo[id].endVisible = Constants.POST_CHUNK_SIZE; + this.postsInfo[id].atTop = false; + this.postsInfo[id].atBottom = atBottom; + } + + removePost(post) { + const channelId = post.channel_id; + this.makePostsInfo(channelId); + const postList = this.postsInfo[channelId].postList; if (isPostListNull(postList)) { return; } - if (postId in postList.posts) { - delete postList.posts[postId]; + if (post.id in postList.posts) { + Reflect.deleteProperty(postList.posts, post.id); } - var index = postList.order.indexOf(postId); + const index = postList.order.indexOf(post.id); if (index !== -1) { postList.order.splice(index, 1); } - this.pStorePosts(channelId, postList); + this.postsInfo[channelId].postList = postList; } + + getPendingPosts(channelId) { + if (this.postsInfo.hasOwnProperty(channelId)) { + return this.postsInfo[channelId].pendingPosts; + } + + return null; + } + storePendingPost(post) { post.state = Constants.POST_LOADING; - var postList = this.getPendingPosts(post.channel_id); - postList = makePostListNonNull(postList); + const postList = makePostListNonNull(this.getPendingPosts(post.channel_id)); postList.posts[post.pending_post_id] = post; postList.order.unshift(post.pending_post_id); - this.pStorePendingPosts(post.channel_id, postList); + + this.makePostsInfo(post.channel_id); + this.postsInfo[post.channel_id].pendingPosts = postList; this.emitChange(); } - pStorePendingPosts(channelId, postList) { - var posts = postList.posts; - // sort failed posts to the bottom - postList.order.sort((a, b) => { - if (posts[a].state === Constants.POST_LOADING && posts[b].state === Constants.POST_FAILED) { - return 1; - } - if (posts[a].state === Constants.POST_FAILED && posts[b].state === Constants.POST_LOADING) { - return -1; - } + removePendingPost(channelId, pendingPostId) { + const postList = makePostListNonNull(this.getPendingPosts(channelId)); - if (posts[a].create_at > posts[b].create_at) { - return -1; - } - if (posts[a].create_at < posts[b].create_at) { - return 1; - } + Reflect.deleteProperty(postList.posts, pendingPostId); + const index = postList.order.indexOf(pendingPostId); + if (index !== -1) { + postList.order.splice(index, 1); + } - return 0; - }); + this.postsInfo[channelId].pendingPosts = postList; + this.emitChange(); + } - BrowserStore.setGlobalItem('pending_posts_' + channelId, postList); + clearPendingPosts(channelId) { + if (this.postsInfo.hasOwnProperty(channelId)) { + Reflect.deleteProperty(this.postsInfo[channelId], 'pendingPosts'); + } } - getPendingPosts(channelId) { - return BrowserStore.getGlobalItem('pending_posts_' + channelId); + + updatePendingPost(post) { + const postList = makePostListNonNull(this.getPendingPosts(post.channel_id)); + + if (postList.order.indexOf(post.pending_post_id) === -1) { + return; + } + + postList.posts[post.pending_post_id] = post; + this.postsInfo[post.channel_id].pendingPosts = postList; + this.emitChange(); } + storeUnseenDeletedPost(post) { - var posts = this.getUnseenDeletedPosts(post.channel_id); + let posts = this.getUnseenDeletedPosts(post.channel_id); if (!posts) { posts = {}; @@ -310,58 +437,68 @@ class PostStoreClass extends EventEmitter { post.filenames = []; posts[post.id] = post; - this.storeUnseenDeletedPosts(post.channel_id, posts); - } - storeUnseenDeletedPosts(channelId, posts) { - BrowserStore.setItem('deleted_posts_' + channelId, posts); + this.postsInfo[post.channel_id].deletedPosts = posts; } + getUnseenDeletedPosts(channelId) { - return BrowserStore.getItem('deleted_posts_' + channelId); + if (this.postsInfo.hasOwnProperty(channelId)) { + return this.postsInfo[channelId].deletedPosts; + } + + return null; } + clearUnseenDeletedPosts(channelId) { - BrowserStore.setItem('deleted_posts_' + channelId, {}); + if (this.postsInfo.hasOwnProperty(channelId)) { + Reflect.deleteProperty(this.postsInfo[channelId], 'deletedPosts'); + } } - removePendingPost(channelId, pendingPostId) { - this.pRemovePendingPost(channelId, pendingPostId); - this.emitChange(); + + storeSelectedPost(postList) { + this.selectedPost = postList; } - pRemovePendingPost(channelId, pendingPostId) { - var postList = this.getPendingPosts(channelId); - postList = makePostListNonNull(postList); - if (pendingPostId in postList.posts) { - delete postList.posts[pendingPostId]; - } - var index = postList.order.indexOf(pendingPostId); - if (index !== -1) { - postList.order.splice(index, 1); - } + getSelectedPost() { + return this.selectedPost; + } - this.pStorePendingPosts(channelId, postList); + emitSelectedPostChange(fromSearch) { + this.emit(SELECTED_POST_CHANGE_EVENT, fromSearch); } - clearPendingPosts() { - BrowserStore.actionOnGlobalItemsWithPrefix('pending_posts_', (key) => { - BrowserStore.removeItem(key); - }); + + addSelectedPostChangeListener(callback) { + this.on(SELECTED_POST_CHANGE_EVENT, callback); } - updatePendingPost(post) { - var postList = this.getPendingPosts(post.channel_id); - postList = makePostListNonNull(postList); - if (postList.order.indexOf(post.pending_post_id) === -1) { - return; + removeSelectedPostChangeListener(callback) { + this.removeListener(SELECTED_POST_CHANGE_EVENT, callback); + } + + getCurrentUsersLatestPost(channelId, rootId) { + const userId = UserStore.getCurrentId(); + var postList = makePostListNonNull(this.getAllPosts(channelId)); + var i = 0; + var len = postList.order.length; + var lastPost = null; + + for (i; i < len; i++) { + const post = postList.posts[postList.order[i]]; + if (post.user_id === userId && (post.props && !post.props.from_webhook || !post.props)) { + if (rootId) { + if (post.root_id === rootId || post.id === rootId) { + lastPost = post; + break; + } + } else { + lastPost = post; + break; + } + } } - postList.posts[post.pending_post_id] = post; - this.pStorePendingPosts(post.channel_id, postList); - this.emitChange(); - } - storeSelectedPost(postList) { - BrowserStore.setItem('select_post', postList); - } - getSelectedPost() { - return BrowserStore.getItem('select_post'); + return lastPost; } + getEmptyDraft() { return {message: '', uploadsInProgress: [], previews: []}; } @@ -402,16 +539,23 @@ class PostStoreClass extends EventEmitter { }); } storeLatestUpdate(channelId, time) { - BrowserStore.setItem('latest_post_' + channelId, time); + if (!this.postsInfo.hasOwnProperty(channelId)) { + this.postsInfo[channelId] = {}; + } + this.postsInfo[channelId].latestPost = time; } getLatestUpdate(channelId) { - return BrowserStore.getItem('latest_post_' + channelId, 0); + if (this.postsInfo.hasOwnProperty(channelId) && this.postsInfo[channelId].hasOwnProperty('latestPost')) { + return this.postsInfo[channelId].latestPost; + } + + return 0; } getCommentCount(post) { const posts = this.getPosts(post.channel_id).posts; let commentCount = 0; - for (let id in posts) { + for (const id in posts) { if (posts.hasOwnProperty(id)) { if (posts[id].root_id === post.id) { commentCount += 1; @@ -429,20 +573,45 @@ PostStore.dispatchToken = AppDispatcher.register((payload) => { var action = payload.action; switch (action.type) { - case ActionTypes.RECIEVED_POSTS: - PostStore.storePosts(action.id, makePostListNonNull(action.post_list)); + case ActionTypes.RECIEVED_POSTS: { + const id = PostStore.currentFocusedPostId == null ? action.id : PostStore.currentFocusedPostId; + PostStore.checkBounds(id, action.numRequested, makePostListNonNull(action.post_list), action.before); + PostStore.storePosts(id, makePostListNonNull(action.post_list)); + PostStore.emitChange(); + break; + } + case ActionTypes.RECIEVED_FOCUSED_POST: + PostStore.clearChannelVisibility(action.postId, false); + PostStore.storeFocusedPost(action.postId, makePostListNonNull(action.post_list)); + PostStore.emitChange(); break; case ActionTypes.RECIEVED_POST: - PostStore.pStorePost(action.post); + PostStore.storePost(action.post); + PostStore.emitChange(); + break; + case ActionTypes.RECIEVED_EDIT_POST: + PostStore.emitEditPost(action); + PostStore.emitChange(); + break; + case ActionTypes.CLICK_CHANNEL: + PostStore.clearFocusedPost(); + PostStore.clearChannelVisibility(action.id, true); + PostStore.clearUnseenDeletedPosts(action.id); + break; + case ActionTypes.CREATE_POST: + PostStore.storePendingPost(action.post); + PostStore.storeDraft(action.post.channel_id, null); + PostStore.jumpPostsViewToBottom(); + break; + case ActionTypes.POST_DELETED: + PostStore.storeUnseenDeletedPost(action.post); + PostStore.removePost(action.post); PostStore.emitChange(); break; case ActionTypes.RECIEVED_POST_SELECTED: PostStore.storeSelectedPost(action.post_list); PostStore.emitSelectedPostChange(action.from_search); break; - case ActionTypes.RECIEVED_EDIT_POST: - PostStore.emitEditPost(action); - break; default: } }); diff --git a/web/react/stores/socket_store.jsx b/web/react/stores/socket_store.jsx index f2936c50a..2e0769cc4 100644 --- a/web/react/stores/socket_store.jsx +++ b/web/react/stores/socket_store.jsx @@ -1,7 +1,6 @@ // Copyright (c) 2015 Mattermost, Inc. All Rights Reserved. // See License.txt for license information. -import AppDispatcher from '../dispatcher/app_dispatcher.jsx'; import UserStore from './user_store.jsx'; import PostStore from './post_store.jsx'; import ChannelStore from './channel_store.jsx'; @@ -11,9 +10,9 @@ import EventEmitter from 'events'; import * as Utils from '../utils/utils.jsx'; import * as AsyncClient from '../utils/async_client.jsx'; +import * as EventHelpers from '../dispatcher/event_helpers.jsx'; import Constants from '../utils/constants.jsx'; -const ActionTypes = Constants.ActionTypes; const SocketEvents = Constants.SocketEvents; const CHANGE_EVENT = 'change'; @@ -91,10 +90,9 @@ class SocketStoreClass extends EventEmitter { }; conn.onmessage = (evt) => { - AppDispatcher.handleServerAction({ - type: ActionTypes.RECIEVED_MSG, - msg: JSON.parse(evt.data) - }); + const msg = JSON.parse(evt.data); + this.handleMessage(msg); + this.emitChange(msg); }; } } @@ -153,12 +151,12 @@ class SocketStoreClass extends EventEmitter { function handleNewPostEvent(msg) { // Store post const post = JSON.parse(msg.props.post); - PostStore.storePost(post); + EventHelpers.emitPostRecievedEvent(post); // Update channel state if (ChannelStore.getCurrentId() === msg.channel_id) { if (window.isActive) { - AsyncClient.updateLastViewedAt(true); + AsyncClient.updateLastViewedAt(); } } else if (UserStore.getCurrentId() !== msg.user_id || post.type !== Constants.POST_TYPE_JOIN_LEAVE) { AsyncClient.getChannel(msg.channel_id); @@ -237,20 +235,17 @@ function handlePostEditEvent(msg) { function handlePostDeleteEvent(msg) { const post = JSON.parse(msg.props.post); - - PostStore.storeUnseenDeletedPost(post); - PostStore.removePost(post, true); - PostStore.emitChange(); + EventHelpers.emitPostDeletedEvent(post); } function handleNewUserEvent() { AsyncClient.getProfiles(); - AsyncClient.getChannelExtraInfo(true); + AsyncClient.getChannelExtraInfo(); } function handleUserAddedEvent(msg) { if (ChannelStore.getCurrentId() === msg.channel_id) { - AsyncClient.getChannelExtraInfo(true); + AsyncClient.getChannelExtraInfo(); } if (UserStore.getCurrentId() === msg.user_id) { @@ -273,7 +268,7 @@ function handleUserRemovedEvent(msg) { $('#removed_from_channel').modal('show'); } } else if (ChannelStore.getCurrentId() === msg.channel_id) { - AsyncClient.getChannelExtraInfo(true); + AsyncClient.getChannelExtraInfo(); } } @@ -286,17 +281,12 @@ function handleChannelViewedEvent(msg) { var SocketStore = new SocketStoreClass(); -SocketStore.dispatchToken = AppDispatcher.register((payload) => { +/*SocketStore.dispatchToken = AppDispatcher.register((payload) => { var action = payload.action; switch (action.type) { - case ActionTypes.RECIEVED_MSG: - SocketStore.handleMessage(action.msg); - SocketStore.emitChange(action.msg); - break; - default: } -}); + });*/ export default SocketStore; -- cgit v1.2.3-1-g7c22