diff options
Diffstat (limited to 'webapp/actions')
-rw-r--r-- | webapp/actions/file_actions.jsx | 29 | ||||
-rw-r--r-- | webapp/actions/global_actions.jsx | 54 | ||||
-rw-r--r-- | webapp/actions/post_actions.jsx | 394 | ||||
-rw-r--r-- | webapp/actions/websocket_actions.jsx | 27 |
4 files changed, 157 insertions, 347 deletions
diff --git a/webapp/actions/file_actions.jsx b/webapp/actions/file_actions.jsx index 204f452d8..628144676 100644 --- a/webapp/actions/file_actions.jsx +++ b/webapp/actions/file_actions.jsx @@ -1,25 +1,24 @@ // Copyright (c) 2016-present Mattermost, Inc. All Rights Reserved. // See License.txt for license information. -import * as AsyncClient from 'utils/async_client.jsx'; -import Client from 'client/web_client.jsx'; +import store from 'stores/redux_store.jsx'; +const dispatch = store.dispatch; +const getState = store.getState; +import {uploadFile as uploadFileRedux} from 'mattermost-redux/actions/files'; export function uploadFile(file, name, channelId, clientId, success, error) { - Client.uploadFile( - file, - name, - channelId, - clientId, + const fileFormData = new FormData(); + fileFormData.append('files', file, name); + fileFormData.append('channel_id', channelId); + fileFormData.append('client_ids', clientId); + + uploadFileRedux(channelId, null, [clientId], fileFormData)(dispatch, getState).then( (data) => { - if (success) { + if (data && success) { success(data); - } - }, - (err) => { - AsyncClient.dispatchError(err, 'uploadFile'); - - if (error) { - error(err); + } else if (data == null && error) { + const serverError = getState().requests.files.uploadFiles.error; + error({id: serverError.server_error_id, ...serverError}); } } ); diff --git a/webapp/actions/global_actions.jsx b/webapp/actions/global_actions.jsx index a1b178d67..13d74c845 100644 --- a/webapp/actions/global_actions.jsx +++ b/webapp/actions/global_actions.jsx @@ -4,14 +4,13 @@ import AppDispatcher from 'dispatcher/app_dispatcher.jsx'; import ChannelStore from 'stores/channel_store.jsx'; -import PostStore from 'stores/post_store.jsx'; import UserStore from 'stores/user_store.jsx'; import BrowserStore from 'stores/browser_store.jsx'; import ErrorStore from 'stores/error_store.jsx'; import TeamStore from 'stores/team_store.jsx'; import SearchStore from 'stores/search_store.jsx'; -import {handleNewPost, loadPosts, loadPostsBefore, loadPostsAfter} from 'actions/post_actions.jsx'; +import {handleNewPost} from 'actions/post_actions.jsx'; import {loadProfilesForSidebar} from 'actions/user_actions.jsx'; import {loadChannelsForCurrentUser} from 'actions/channel_actions.jsx'; import {stopPeriodicStatusUpdates} from 'actions/status_actions.jsx'; @@ -59,7 +58,6 @@ export function emitChannelClickEvent(channel) { getMyChannelMemberPromise.then(() => { getChannelStats(chan.id)(dispatch, getState); viewChannel(chan.id, oldChannelId)(dispatch, getState); - loadPosts(chan.id); // Mark previous and next channel as read ChannelStore.resetCounts([chan.id, oldChannelId]); @@ -106,10 +104,15 @@ export function doFocusPost(channelId, postId, data) { channelId, post_list: data }); + + dispatch({ + type: ActionTypes.RECEIVED_FOCUSED_POST, + data: postId, + channelId + }); + loadChannelsForCurrentUser(); getChannelStats(channelId)(dispatch, getState); - loadPostsBefore(postId, 0, Constants.POST_FOCUS_CONTEXT_RADIUS, true); - loadPostsAfter(postId, 0, Constants.POST_FOCUS_CONTEXT_RADIUS, true); } export function emitPostFocusEvent(postId, onSuccess) { @@ -148,8 +151,10 @@ export function emitCloseRightHandSide() { SearchStore.storeSearchResults(null, false, false); SearchStore.emitSearchChange(); - PostStore.storeSelectedPostId(null); - PostStore.emitSelectedPostChange(false, false); + dispatch({ + type: ActionTypes.SELECT_POST, + postId: '' + }); } export function emitPostFocusRightHandSideFromSearch(post, isMentionSearch) { @@ -188,29 +193,6 @@ export function emitLeaveTeam() { removeUserFromTeam(TeamStore.getCurrentId(), UserStore.getCurrentId())(dispatch, getState); } -export function emitLoadMorePostsEvent() { - const id = ChannelStore.getCurrentId(); - loadMorePostsTop(id, false); -} - -export function emitLoadMorePostsFocusedTopEvent() { - const id = PostStore.getFocusedPostId(); - loadMorePostsTop(id, true); -} - -export function loadMorePostsTop(id, isFocusPost) { - const earliestPostId = PostStore.getEarliestPostFromPage(id).id; - if (PostStore.requestVisibilityIncrease(id, Constants.POST_CHUNK_SIZE)) { - loadPostsBefore(earliestPostId, 0, Constants.POST_CHUNK_SIZE, isFocusPost); - } -} - -export function emitLoadMorePostsFocusedBottomEvent() { - const id = PostStore.getFocusedPostId(); - const latestPostId = PostStore.getLatestPost(id).id; - loadPostsAfter(latestPostId, 0, Constants.POST_CHUNK_SIZE, Boolean(id)); -} - export function emitUserPostedEvent(post) { AppDispatcher.handleServerAction({ type: ActionTypes.CREATE_POST, @@ -225,13 +207,6 @@ export function emitUserCommentedEvent(post) { }); } -export function emitPostDeletedEvent(post) { - AppDispatcher.handleServerAction({ - type: ActionTypes.POST_DELETED, - post - }); -} - export function showDeletePostModal(post, commentCount = 0) { AppDispatcher.handleViewAction({ type: ActionTypes.TOGGLE_DELETE_POST_MODAL, @@ -421,11 +396,6 @@ export function loadDefaultLocale() { return newLocalizationSelected(locale); } -export function viewLoggedIn() { - // Clear pending posts (shouldn't have pending posts if we are loading) - PostStore.clearPendingPosts(); -} - let lastTimeTypingSent = 0; export function emitLocalUserTypingEvent(channelId, parentId) { const t = Date.now(); diff --git a/webapp/actions/post_actions.jsx b/webapp/actions/post_actions.jsx index d55a0d578..1eb1f4feb 100644 --- a/webapp/actions/post_actions.jsx +++ b/webapp/actions/post_actions.jsx @@ -4,14 +4,12 @@ import AppDispatcher from 'dispatcher/app_dispatcher.jsx'; import ChannelStore from 'stores/channel_store.jsx'; -import PostStore from 'stores/post_store.jsx'; import UserStore from 'stores/user_store.jsx'; +import PostStore from 'stores/post_store.jsx'; -import {loadStatusesForChannel} from 'actions/status_actions.jsx'; import {loadNewDMIfNeeded, loadNewGMIfNeeded} from 'actions/user_actions.jsx'; import {trackEvent} from 'actions/diagnostics_actions.jsx'; import {sendDesktopNotification} from 'actions/notification_actions.jsx'; -import * as GlobalActions from 'actions/global_actions.jsx'; import Client from 'client/web_client.jsx'; import * as AsyncClient from 'utils/async_client.jsx'; @@ -25,7 +23,20 @@ import store from 'stores/redux_store.jsx'; const dispatch = store.dispatch; const getState = store.getState; import {getProfilesByIds} from 'mattermost-redux/actions/users'; +import { + createPost as createPostRedux, + getPostThread, + editPost, + deletePost as deletePostRedux, + getPosts, + getPostsBefore, + addReaction as addReactionRedux, + removeReaction as removeReactionRedux +} from 'mattermost-redux/actions/posts'; import {getMyChannelMember} from 'mattermost-redux/actions/channels'; +import {PostTypes} from 'mattermost-redux/action_types'; +import * as Selectors from 'mattermost-redux/selectors/entities/posts'; +import {batchActions} from 'redux-batched-actions'; export function handleNewPost(post, msg) { let websocketMessageProps = {}; @@ -54,19 +65,22 @@ export function handleNewPost(post, msg) { } function completePostReceive(post, websocketMessageProps) { - if (post.root_id && PostStore.getPost(post.channel_id, post.root_id) == null) { - Client.getPost( - post.channel_id, - post.root_id, + if (post.root_id && Selectors.getPost(getState(), post.root_id) != null) { + getPostThread(post.root_id)(dispatch, getState).then( (data) => { - AppDispatcher.handleServerAction({ - type: ActionTypes.RECEIVED_POSTS, - id: post.channel_id, - numRequested: 0, - post_list: data + // Need manual dispatch to remove pending post + dispatch({ + type: PostTypes.RECEIVED_POSTS, + data: { + order: [], + posts: { + [post.id]: post + } + }, + channelId: post.channel_id }); - // Required to update order + // Still needed to update unreads AppDispatcher.handleServerAction({ type: ActionTypes.RECEIVED_POST, post, @@ -74,17 +88,25 @@ function completePostReceive(post, websocketMessageProps) { }); sendDesktopNotification(post, websocketMessageProps); - loadProfilesForPosts(data.posts); - }, - (err) => { - AsyncClient.dispatchError(err, 'getPost'); } ); return; } + dispatch({ + type: PostTypes.RECEIVED_POSTS, + data: { + order: [], + posts: { + [post.id]: post + } + }, + channelId: post.channel_id + }); + + // Still needed to update unreads AppDispatcher.handleServerAction({ type: ActionTypes.RECEIVED_POST, post, @@ -167,138 +189,6 @@ export function getPinnedPosts(channelId = ChannelStore.getCurrentId()) { ); } -export function loadPosts(channelId = ChannelStore.getCurrentId(), isPost = false) { - const postList = PostStore.getAllPosts(channelId); - const latestPostTime = PostStore.getLatestPostFromPageTime(channelId); - - if ( - !postList || Object.keys(postList).length === 0 || - (!isPost && postList.order.length < Constants.POST_CHUNK_SIZE) || - latestPostTime === 0 - ) { - loadPostsPage(channelId, Constants.POST_CHUNK_SIZE, isPost); - return; - } - - Client.getPosts( - channelId, - latestPostTime, - (data) => { - AppDispatcher.handleServerAction({ - type: ActionTypes.RECEIVED_POSTS, - id: channelId, - before: true, - numRequested: 0, - post_list: data, - isPost - }); - - loadProfilesForPosts(data.posts); - loadStatusesForChannel(channelId); - }, - (err) => { - AsyncClient.dispatchError(err, 'loadPosts'); - } - ); -} - -export function loadPostsPage(channelId = ChannelStore.getCurrentId(), max = Constants.POST_CHUNK_SIZE, isPost = false) { - const postList = PostStore.getAllPosts(channelId); - - // if we already have more than POST_CHUNK_SIZE posts, - // let's get the amount we have but rounded up to next multiple of POST_CHUNK_SIZE, - // with a max - let numPosts = Math.min(max, Constants.POST_CHUNK_SIZE); - if (postList && postList.order.length > 0) { - numPosts = Math.min(max, Constants.POST_CHUNK_SIZE * Math.ceil(postList.order.length / Constants.POST_CHUNK_SIZE)); - } - - Client.getPostsPage( - channelId, - 0, - numPosts, - (data) => { - AppDispatcher.handleServerAction({ - type: ActionTypes.RECEIVED_POSTS, - id: channelId, - before: true, - numRequested: numPosts, - checkLatest: true, - checkEarliest: true, - post_list: data, - isPost - }); - - loadProfilesForPosts(data.posts); - loadStatusesForChannel(channelId); - }, - (err) => { - AsyncClient.dispatchError(err, 'loadPostsPage'); - } - ); -} - -export function loadPostsBefore(postId, offset, numPost, isPost) { - const channelId = ChannelStore.getCurrentId(); - if (channelId == null) { - return; - } - - Client.getPostsBefore( - channelId, - postId, - offset, - numPost, - (data) => { - AppDispatcher.handleServerAction({ - type: ActionTypes.RECEIVED_POSTS, - id: channelId, - before: true, - checkEarliest: true, - numRequested: numPost, - post_list: data, - isPost - }); - - loadProfilesForPosts(data.posts); - loadStatusesForChannel(channelId); - }, - (err) => { - AsyncClient.dispatchError(err, 'loadPostsBefore'); - } - ); -} - -export function loadPostsAfter(postId, offset, numPost, isPost) { - const channelId = ChannelStore.getCurrentId(); - if (channelId == null) { - return; - } - - Client.getPostsAfter( - channelId, - postId, - offset, - numPost, - (data) => { - AppDispatcher.handleServerAction({ - type: ActionTypes.RECEIVED_POSTS, - id: channelId, - before: false, - numRequested: numPost, - post_list: data, - isPost - }); - - loadProfilesForPosts(data.posts); - loadStatusesForChannel(channelId); - }, - (err) => { - AsyncClient.dispatchError(err, 'loadPostsAfter'); - } - ); -} - export function loadProfilesForPosts(posts) { const profilesToLoad = {}; for (const pid in posts) { @@ -321,124 +211,37 @@ export function loadProfilesForPosts(posts) { } export function addReaction(channelId, postId, emojiName) { - const reaction = { - post_id: postId, - user_id: UserStore.getCurrentId(), - emoji_name: emojiName - }; - emitEmojiPosted(emojiName); - - AsyncClient.saveReaction(channelId, reaction); + addReactionRedux(postId, emojiName)(dispatch, getState); } export function removeReaction(channelId, postId, emojiName) { - const reaction = { - post_id: postId, - user_id: UserStore.getCurrentId(), - emoji_name: emojiName - }; - - AsyncClient.deleteReaction(channelId, reaction); + removeReactionRedux(postId, emojiName)(dispatch, getState); } -const postQueue = []; - -export function queuePost(post, doLoadPost, success, error) { - postQueue.push( - createPost.bind( - this, - post, - doLoadPost, - (data) => { - if (success) { - success(data); - } - - postSendComplete(); - }, - (err) => { - if (error) { - error(err); - } - - postSendComplete(); - } - ) - ); - - sendFirstPostInQueue(); -} - -// Remove the completed post from the queue and send the next one -function postSendComplete() { - postQueue.shift(); - sendNextPostInQueue(); -} - -// Start sending posts if a new queue has started -function sendFirstPostInQueue() { - if (postQueue.length === 1) { - sendNextPostInQueue(); - } -} +export function createPost(post, files, success) { + createPostRedux(post, files)(dispatch, getState).then(() => { + if (post.root_id) { + PostStore.storeCommentDraft(post.root_id, null); + } else { + PostStore.storeDraft(post.channel_id, null); + } -// Send the next post in the queue if there is one -function sendNextPostInQueue() { - const nextPostAction = postQueue[0]; - if (nextPostAction) { - nextPostAction(); - } + if (success) { + success(); + } + }); } -export function createPost(post, doLoadPost, success, error) { - Client.createPost(post, +export function updatePost(post, success) { + editPost(post)(dispatch, getState).then( (data) => { - if (doLoadPost) { - loadPosts(post.channel_id); - } else { - PostStore.removePendingPost(post.pending_post_id); - } - - AppDispatcher.handleServerAction({ - type: ActionTypes.RECEIVED_POST, - post: data - }); - - if (success) { - success(data); - } - }, - - (err) => { - if (err.id === 'api.post.create_post.root_id.app_error') { - PostStore.removePendingPost(post.pending_post_id); - } else { - post.state = Constants.POST_FAILED; - PostStore.updatePendingPost(post); - } - - if (error) { - error(err); + if (data && success) { + success(); } } ); } -export function updatePost(post, success, isPost) { - Client.updatePost( - post, - () => { - loadPosts(post.channel_id, isPost); - - if (success) { - success(); - } - }, - (err) => { - AsyncClient.dispatchError(err, 'updatePost'); - }); -} - export function emitEmojiPosted(emoji) { AppDispatcher.handleServerAction({ type: ActionTypes.EMOJI_POSTED, @@ -446,29 +249,31 @@ export function emitEmojiPosted(emoji) { }); } -export function deletePost(channelId, post, success, error) { - Client.deletePost( - channelId, - post.id, +export function deletePost(channelId, post, success) { + const {currentUserId} = getState().entities.users; + + let hardDelete = false; + if (post.user_id === currentUserId) { + hardDelete = true; + } + + deletePostRedux(post, hardDelete)(dispatch, getState).then( () => { - GlobalActions.emitRemovePost(post); - if (post.id === PostStore.getSelectedPostId()) { - AppDispatcher.handleServerAction({ - type: ActionTypes.RECEIVED_POST_SELECTED, - postId: null + if (post.id === getState().views.rhs.selectedPostId) { + dispatch({ + type: ActionTypes.SELECT_POST, + postId: '' }); } + dispatch({ + type: PostTypes.REMOVE_POST, + data: post + }); + if (success) { success(); } - }, - (err) => { - AsyncClient.dispatchError(err, 'deletePost'); - - if (error) { - error(err); - } } ); } @@ -500,10 +305,49 @@ export function performSearch(terms, isMentionSearch, success, error) { ); } -export function storePostDraft(channelId, draft) { - AppDispatcher.handleViewAction({ - type: ActionTypes.POST_DRAFT_CHANGED, - channelId, - draft - }); +const POST_INCREASE_AMOUNT = Constants.POST_CHUNK_SIZE / 2; + +// Returns true if there are more posts to load +export function increasePostVisibility(channelId, focusedPostId) { + return async (doDispatch, doGetState) => { + if (doGetState().views.channel.loadingPosts[channelId]) { + return true; + } + + const currentPostVisibility = doGetState().views.channel.postVisibility[channelId]; + + if (currentPostVisibility >= Constants.MAX_POST_VISIBILITY) { + return true; + } + + doDispatch(batchActions([ + { + type: ActionTypes.LOADING_POSTS, + data: true, + channelId + }, + { + type: ActionTypes.INCREASE_POST_VISIBILITY, + data: channelId, + amount: POST_INCREASE_AMOUNT + } + ])); + + const page = Math.floor(currentPostVisibility / POST_INCREASE_AMOUNT); + + let posts; + if (focusedPostId) { + posts = await getPostsBefore(channelId, focusedPostId, page, POST_INCREASE_AMOUNT)(dispatch, getState); + } else { + posts = await getPosts(channelId, page, POST_INCREASE_AMOUNT)(doDispatch, doGetState); + } + + doDispatch({ + type: ActionTypes.LOADING_POSTS, + data: false, + channelId + }); + + return posts.order.length >= POST_INCREASE_AMOUNT; + }; } diff --git a/webapp/actions/websocket_actions.jsx b/webapp/actions/websocket_actions.jsx index b7a0b12a8..1aaecfb71 100644 --- a/webapp/actions/websocket_actions.jsx +++ b/webapp/actions/websocket_actions.jsx @@ -5,7 +5,6 @@ import $ from 'jquery'; import UserStore from 'stores/user_store.jsx'; import TeamStore from 'stores/team_store.jsx'; -import PostStore from 'stores/post_store.jsx'; import PreferenceStore from 'stores/preference_store.jsx'; import ChannelStore from 'stores/channel_store.jsx'; import BrowserStore from 'stores/browser_store.jsx'; @@ -21,7 +20,7 @@ import * as AsyncClient from 'utils/async_client.jsx'; import {getSiteURL} from 'utils/url.jsx'; import * as GlobalActions from 'actions/global_actions.jsx'; -import {handleNewPost, loadPosts, loadProfilesForPosts} from 'actions/post_actions.jsx'; +import {handleNewPost, loadProfilesForPosts} from 'actions/post_actions.jsx'; import {loadProfilesForSidebar} from 'actions/user_actions.jsx'; import {loadChannelsForCurrentUser} from 'actions/channel_actions.jsx'; import * as StatusActions from 'actions/status_actions.jsx'; @@ -36,8 +35,9 @@ const dispatch = store.dispatch; const getState = store.getState; import {batchActions} from 'redux-batched-actions'; import {viewChannel, getChannelAndMyMember, getChannelStats} from 'mattermost-redux/actions/channels'; +import {getPosts} from 'mattermost-redux/actions/posts'; import {setServerVersion} from 'mattermost-redux/actions/general'; -import {ChannelTypes, TeamTypes, UserTypes} from 'mattermost-redux/action_types'; +import {ChannelTypes, TeamTypes, UserTypes, PostTypes} from 'mattermost-redux/action_types'; const MAX_WEBSOCKET_FAILS = 7; @@ -97,7 +97,7 @@ export function reconnect(includeWebSocket = true) { if (Client.teamId) { loadChannelsForCurrentUser(); - loadPosts(ChannelStore.getCurrentId()); + getPosts(ChannelStore.getCurrentId())(dispatch, getState); StatusActions.loadStatusesForChannelAndSidebar(); } @@ -246,8 +246,7 @@ function handleNewPostEvent(msg) { function handlePostEditEvent(msg) { // Store post const post = JSON.parse(msg.data.post); - PostStore.storePost(post, false); - PostStore.emitChange(); + dispatch({type: PostTypes.RECEIVED_POST, data: post}); // Update channel state if (ChannelStore.getCurrentId() === msg.broadcast.channel_id) { @@ -259,7 +258,7 @@ function handlePostEditEvent(msg) { function handlePostDeleteEvent(msg) { const post = JSON.parse(msg.data.post); - GlobalActions.emitPostDeletedEvent(post); + dispatch({type: PostTypes.POST_DELETED, data: post}); } function handleTeamAddedEvent(msg) { @@ -424,19 +423,17 @@ function handleWebrtc(msg) { function handleReactionAddedEvent(msg) { const reaction = JSON.parse(msg.data.reaction); - AppDispatcher.handleServerAction({ - type: ActionTypes.ADDED_REACTION, - postId: reaction.post_id, - reaction + dispatch({ + type: PostTypes.RECEIVED_REACTION, + data: reaction }); } function handleReactionRemovedEvent(msg) { const reaction = JSON.parse(msg.data.reaction); - AppDispatcher.handleServerAction({ - type: ActionTypes.REMOVED_REACTION, - postId: reaction.post_id, - reaction + dispatch({ + type: PostTypes.REACTION_DELETED, + data: reaction }); } |