UNPKG

mattermost-redux

Version:

Common code (API client, Redux stores, logic, utility functions) for building a Mattermost client

1,050 lines (1,049 loc) 59.6 kB
"use strict"; // Copyright (c) 2015-present Mattermost, Inc. All Rights Reserved. // See LICENSE.txt for license information. Object.defineProperty(exports, "__esModule", { value: true }); exports.moveHistoryIndexForward = exports.moveHistoryIndexBack = exports.resetHistoryIndex = exports.addMessageIntoHistory = exports.doPostActionWithCookie = exports.doPostAction = exports.getOpenGraphMetadata = exports.unflagPost = exports.selectFocusedPostId = exports.selectPost = exports.removePost = exports.getNeededCustomEmojis = exports.getNeededAtMentionedUsernames = exports.getProfilesAndStatusesForPosts = exports.getThreadsForPosts = exports.getPostsAround = exports.getPostsAfter = exports.getPostsBefore = exports.getPostsSince = exports.getPostsUnread = exports.getPosts = exports.getPostThread = exports.flagPost = exports.getReactionsForPost = exports.getCustomEmojiForReaction = exports.removeReaction = exports.addReaction = exports.unpinPost = exports.pinPost = exports.setUnreadPost = exports.editPost = exports.deletePost = exports.resetCreatePostRequest = exports.createPostImmediately = exports.createPost = exports.getPost = exports.postRemoved = exports.postDeleted = exports.receivedPostsInThread = exports.receivedPostsInChannel = exports.receivedPostsSince = exports.receivedPostsBefore = exports.receivedPostsAfter = exports.receivedPosts = exports.receivedNewPost = exports.receivedPost = void 0; var tslib_1 = require("tslib"); var client_1 = require("../client"); var constants_1 = require("../constants"); var action_types_1 = require("../action_types"); var channels_1 = require("../selectors/entities/channels"); var emojis_1 = require("../selectors/entities/emojis"); var general_1 = require("../selectors/entities/general"); var Selectors = tslib_1.__importStar(require("../selectors/entities/posts")); var users_1 = require("../selectors/entities/users"); var emoji_utils_1 = require("../utils/emoji_utils"); var post_list_1 = require("../utils/post_list"); var emojis_2 = require("./emojis"); var errors_1 = require("./errors"); var helpers_1 = require("./helpers"); var preferences_1 = require("./preferences"); var users_2 = require("./users"); var actions_1 = require("../types/actions"); // receivedPost should be dispatched after a single post from the server. This typically happens when an existing post // is updated. function receivedPost(post) { return { type: action_types_1.PostTypes.RECEIVED_POST, data: post, }; } exports.receivedPost = receivedPost; // receivedNewPost should be dispatched when receiving a newly created post or when sending a request to the server // to make a new post. function receivedNewPost(post) { return { type: action_types_1.PostTypes.RECEIVED_NEW_POST, data: post, }; } exports.receivedNewPost = receivedNewPost; // receivedPosts should be dispatched when receiving multiple posts from the server that may or may not be ordered. // This will typically be used alongside other actions like receivedPostsAfter which require the posts to be ordered. function receivedPosts(posts) { return { type: action_types_1.PostTypes.RECEIVED_POSTS, data: posts, }; } exports.receivedPosts = receivedPosts; // receivedPostsAfter should be dispatched when receiving an ordered list of posts that come before a given post. function receivedPostsAfter(posts, channelId, afterPostId, recent) { if (recent === void 0) { recent = false; } return { type: action_types_1.PostTypes.RECEIVED_POSTS_AFTER, channelId: channelId, data: posts, afterPostId: afterPostId, recent: recent, }; } exports.receivedPostsAfter = receivedPostsAfter; // receivedPostsBefore should be dispatched when receiving an ordered list of posts that come after a given post. function receivedPostsBefore(posts, channelId, beforePostId, oldest) { if (oldest === void 0) { oldest = false; } return { type: action_types_1.PostTypes.RECEIVED_POSTS_BEFORE, channelId: channelId, data: posts, beforePostId: beforePostId, oldest: oldest, }; } exports.receivedPostsBefore = receivedPostsBefore; // receivedPostsSince should be dispatched when receiving a list of posts that have been updated since a certain time. // Due to how the API endpoint works, some of these posts will be ordered, but others will not, so this needs special // handling from the reducers. function receivedPostsSince(posts, channelId) { return { type: action_types_1.PostTypes.RECEIVED_POSTS_SINCE, channelId: channelId, data: posts, }; } exports.receivedPostsSince = receivedPostsSince; // receivedPostsInChannel should be dispatched when receiving a list of ordered posts within a channel when the // the adjacent posts are not known. function receivedPostsInChannel(posts, channelId, recent, oldest) { if (recent === void 0) { recent = false; } if (oldest === void 0) { oldest = false; } return { type: action_types_1.PostTypes.RECEIVED_POSTS_IN_CHANNEL, channelId: channelId, data: posts, recent: recent, oldest: oldest, }; } exports.receivedPostsInChannel = receivedPostsInChannel; // receivedPostsInThread should be dispatched when receiving a list of unordered posts in a thread. function receivedPostsInThread(posts, rootId) { return { type: action_types_1.PostTypes.RECEIVED_POSTS_IN_THREAD, data: posts, rootId: rootId, }; } exports.receivedPostsInThread = receivedPostsInThread; // postDeleted should be dispatched when a post has been deleted and should be replaced with a "message deleted" // placeholder. This typically happens when a post is deleted by another user. function postDeleted(post) { return { type: action_types_1.PostTypes.POST_DELETED, data: post, }; } exports.postDeleted = postDeleted; // postRemoved should be dispatched when a post should be immediately removed. This typically happens when a post is // deleted by the current user. function postRemoved(post) { return { type: action_types_1.PostTypes.POST_REMOVED, data: post, }; } exports.postRemoved = postRemoved; function getPost(postId) { var _this = this; return function (dispatch, getState) { return tslib_1.__awaiter(_this, void 0, void 0, function () { var post, error_1; return tslib_1.__generator(this, function (_a) { switch (_a.label) { case 0: _a.trys.push([0, 2, , 3]); return [4 /*yield*/, client_1.Client4.getPost(postId)]; case 1: post = _a.sent(); getProfilesAndStatusesForPosts([post], dispatch, getState); return [3 /*break*/, 3]; case 2: error_1 = _a.sent(); helpers_1.forceLogoutIfNecessary(error_1, dispatch, getState); dispatch(actions_1.batchActions([ { type: action_types_1.PostTypes.GET_POSTS_FAILURE, error: error_1 }, errors_1.logError(error_1), ])); return [2 /*return*/, { error: error_1 }]; case 3: dispatch(actions_1.batchActions([ receivedPost(post), { type: action_types_1.PostTypes.GET_POSTS_SUCCESS, }, ])); return [2 /*return*/, { data: post }]; } }); }); }; } exports.getPost = getPost; function createPost(post, files) { var _this = this; if (files === void 0) { files = []; } return function (dispatch, getState) { return tslib_1.__awaiter(_this, void 0, void 0, function () { var state, currentUserId, timestamp, pendingPostId, newPost, fileIds; return tslib_1.__generator(this, function (_a) { state = getState(); currentUserId = state.entities.users.currentUserId; timestamp = Date.now(); pendingPostId = post.pending_post_id || currentUserId + ":" + timestamp; if (Selectors.isPostIdSending(state, pendingPostId)) { return [2 /*return*/, { data: true }]; } newPost = tslib_1.__assign(tslib_1.__assign({}, post), { pending_post_id: pendingPostId, create_at: timestamp, update_at: timestamp, reply_count: 0 }); if (post.root_id) { newPost.reply_count = Selectors.getPostRepliesCount(state, post.root_id) + 1; } // We are retrying a pending post that had files if (newPost.file_ids && !files.length) { files = newPost.file_ids.map(function (id) { return state.entities.files.files[id]; }); // eslint-disable-line } if (files.length) { fileIds = files.map(function (file) { return file.id; }); newPost = tslib_1.__assign(tslib_1.__assign({}, newPost), { file_ids: fileIds }); dispatch({ type: action_types_1.FileTypes.RECEIVED_FILES_FOR_POST, postId: pendingPostId, data: files, }); } dispatch({ type: action_types_1.PostTypes.RECEIVED_NEW_POST, data: tslib_1.__assign(tslib_1.__assign({}, newPost), { id: pendingPostId }), meta: { offline: { effect: function () { return client_1.Client4.createPost(tslib_1.__assign(tslib_1.__assign({}, newPost), { create_at: 0 })); }, commit: function (result, payload) { var actions = [ receivedPost(payload), { type: action_types_1.PostTypes.CREATE_POST_SUCCESS, }, { type: action_types_1.ChannelTypes.INCREMENT_TOTAL_MSG_COUNT, data: { channelId: newPost.channel_id, amount: 1, }, }, { type: action_types_1.ChannelTypes.DECREMENT_UNREAD_MSG_COUNT, data: { channelId: newPost.channel_id, amount: 1, }, }, ]; if (files) { actions.push({ type: action_types_1.FileTypes.RECEIVED_FILES_FOR_POST, postId: payload.id, data: files, }); } dispatch(actions_1.batchActions(actions)); }, maxRetry: 0, offlineRollback: true, rollback: function (result, error) { var data = tslib_1.__assign(tslib_1.__assign({}, newPost), { id: pendingPostId, failed: true, update_at: Date.now() }); dispatch({ type: action_types_1.PostTypes.CREATE_POST_FAILURE, error: error }); // If the failure was because: the root post was deleted or // TownSquareIsReadOnly=true then remove the post if (error.server_error_id === 'api.post.create_post.root_id.app_error' || error.server_error_id === 'api.post.create_post.town_square_read_only' || error.server_error_id === 'plugin.message_will_be_posted.dismiss_post') { dispatch(removePost(data)); } else { dispatch(receivedPost(data)); } }, }, }, }); return [2 /*return*/, { data: true }]; }); }); }; } exports.createPost = createPost; function createPostImmediately(post, files) { var _this = this; if (files === void 0) { files = []; } return function (dispatch, getState) { return tslib_1.__awaiter(_this, void 0, void 0, function () { var state, currentUserId, timestamp, pendingPostId, newPost, fileIds, created, error_2, actions; return tslib_1.__generator(this, function (_a) { switch (_a.label) { case 0: state = getState(); currentUserId = state.entities.users.currentUserId; timestamp = Date.now(); pendingPostId = currentUserId + ":" + timestamp; newPost = tslib_1.__assign(tslib_1.__assign({}, post), { pending_post_id: pendingPostId, create_at: timestamp, update_at: timestamp, reply_count: 0 }); if (post.root_id) { newPost.reply_count = Selectors.getPostRepliesCount(state, post.root_id) + 1; } if (files.length) { fileIds = files.map(function (file) { return file.id; }); newPost = tslib_1.__assign(tslib_1.__assign({}, newPost), { file_ids: fileIds }); dispatch({ type: action_types_1.FileTypes.RECEIVED_FILES_FOR_POST, postId: pendingPostId, data: files, }); } dispatch(receivedNewPost(tslib_1.__assign(tslib_1.__assign({}, newPost), { id: pendingPostId }))); _a.label = 1; case 1: _a.trys.push([1, 3, , 4]); return [4 /*yield*/, client_1.Client4.createPost(tslib_1.__assign(tslib_1.__assign({}, newPost), { create_at: 0 }))]; case 2: created = _a.sent(); newPost.id = created.id; newPost.reply_count = created.reply_count; return [3 /*break*/, 4]; case 3: error_2 = _a.sent(); helpers_1.forceLogoutIfNecessary(error_2, dispatch, getState); dispatch(actions_1.batchActions([ { type: action_types_1.PostTypes.CREATE_POST_FAILURE, data: newPost, error: error_2 }, removePost(tslib_1.__assign(tslib_1.__assign({}, newPost), { id: pendingPostId })), errors_1.logError(error_2), ])); return [2 /*return*/, { error: error_2 }]; case 4: actions = [ receivedPost(newPost), { type: action_types_1.PostTypes.CREATE_POST_SUCCESS, }, { type: action_types_1.ChannelTypes.INCREMENT_TOTAL_MSG_COUNT, data: { channelId: newPost.channel_id, amount: 1, }, }, { type: action_types_1.ChannelTypes.DECREMENT_UNREAD_MSG_COUNT, data: { channelId: newPost.channel_id, amount: 1, }, }, ]; if (files) { actions.push({ type: action_types_1.FileTypes.RECEIVED_FILES_FOR_POST, postId: newPost.id, data: files, }); } dispatch(actions_1.batchActions(actions)); return [2 /*return*/, { data: newPost }]; } }); }); }; } exports.createPostImmediately = createPostImmediately; function resetCreatePostRequest() { return { type: action_types_1.PostTypes.CREATE_POST_RESET_REQUEST }; } exports.resetCreatePostRequest = resetCreatePostRequest; function deletePost(post) { return function (dispatch, getState) { var state = getState(); var delPost = tslib_1.__assign({}, post); if (delPost.type === constants_1.Posts.POST_TYPES.COMBINED_USER_ACTIVITY && delPost.system_post_ids) { delPost.system_post_ids.forEach(function (systemPostId) { var systemPost = Selectors.getPost(state, systemPostId); if (systemPost) { dispatch(deletePost(systemPost)); } }); } else { dispatch({ type: action_types_1.PostTypes.POST_DELETED, data: delPost, meta: { offline: { effect: function () { return client_1.Client4.deletePost(post.id); }, commit: { type: 'do_nothing' }, rollback: receivedPost(delPost), }, }, }); } return { data: true }; }; } exports.deletePost = deletePost; function editPost(post) { return helpers_1.bindClientFunc({ clientFunc: client_1.Client4.patchPost, onRequest: action_types_1.PostTypes.EDIT_POST_REQUEST, onSuccess: [action_types_1.PostTypes.RECEIVED_POST, action_types_1.PostTypes.EDIT_POST_SUCCESS], onFailure: action_types_1.PostTypes.EDIT_POST_FAILURE, params: [ post, ], }); } exports.editPost = editPost; function getUnreadPostData(unreadChan, state) { var member = channels_1.getMyChannelMember(state, unreadChan.channel_id); var delta = member ? member.msg_count - unreadChan.msg_count : unreadChan.msg_count; var data = { teamId: unreadChan.team_id, channelId: unreadChan.channel_id, msgCount: unreadChan.msg_count, mentionCount: unreadChan.mention_count, lastViewedAt: unreadChan.last_viewed_at, deltaMsgs: delta, }; return data; } function setUnreadPost(userId, postId) { var _this = this; return function (dispatch, getState) { return tslib_1.__awaiter(_this, void 0, void 0, function () { var state, post, unreadChan, error_3, data; return tslib_1.__generator(this, function (_a) { switch (_a.label) { case 0: state = getState(); post = Selectors.getPost(state, postId); _a.label = 1; case 1: _a.trys.push([1, 3, , 4]); if (post_list_1.isCombinedUserActivityPost(postId)) { return [2 /*return*/, {}]; } return [4 /*yield*/, client_1.Client4.markPostAsUnread(userId, postId)]; case 2: unreadChan = _a.sent(); dispatch({ type: action_types_1.ChannelTypes.ADD_MANUALLY_UNREAD, data: { channelId: post.channel_id, }, }); return [3 /*break*/, 4]; case 3: error_3 = _a.sent(); helpers_1.forceLogoutIfNecessary(error_3, dispatch, getState); dispatch(errors_1.logError(error_3)); dispatch({ type: action_types_1.ChannelTypes.REMOVE_MANUALLY_UNREAD, data: { channelId: post.channel_id, }, }); return [2 /*return*/, { error: error_3 }]; case 4: state = getState(); data = getUnreadPostData(unreadChan, state); dispatch({ type: action_types_1.ChannelTypes.POST_UNREAD_SUCCESS, data: data, }); return [2 /*return*/, { data: data }]; } }); }); }; } exports.setUnreadPost = setUnreadPost; function pinPost(postId) { var _this = this; return function (dispatch, getState) { return tslib_1.__awaiter(_this, void 0, void 0, function () { var posts, error_4, actions, post; return tslib_1.__generator(this, function (_a) { switch (_a.label) { case 0: dispatch({ type: action_types_1.PostTypes.EDIT_POST_REQUEST }); _a.label = 1; case 1: _a.trys.push([1, 3, , 4]); return [4 /*yield*/, client_1.Client4.pinPost(postId)]; case 2: posts = _a.sent(); return [3 /*break*/, 4]; case 3: error_4 = _a.sent(); helpers_1.forceLogoutIfNecessary(error_4, dispatch, getState); dispatch(actions_1.batchActions([ { type: action_types_1.PostTypes.EDIT_POST_FAILURE, error: error_4 }, errors_1.logError(error_4), ])); return [2 /*return*/, { error: error_4 }]; case 4: actions = [ { type: action_types_1.PostTypes.EDIT_POST_SUCCESS, }, ]; post = Selectors.getPost(getState(), postId); if (post) { actions.push(receivedPost(tslib_1.__assign(tslib_1.__assign({}, post), { is_pinned: true, update_at: Date.now() })), { type: action_types_1.ChannelTypes.INCREMENT_PINNED_POST_COUNT, id: post.channel_id, }); } dispatch(actions_1.batchActions(actions)); return [2 /*return*/, { data: posts }]; } }); }); }; } exports.pinPost = pinPost; function unpinPost(postId) { var _this = this; return function (dispatch, getState) { return tslib_1.__awaiter(_this, void 0, void 0, function () { var posts, error_5, actions, post; return tslib_1.__generator(this, function (_a) { switch (_a.label) { case 0: dispatch({ type: action_types_1.PostTypes.EDIT_POST_REQUEST }); _a.label = 1; case 1: _a.trys.push([1, 3, , 4]); return [4 /*yield*/, client_1.Client4.unpinPost(postId)]; case 2: posts = _a.sent(); return [3 /*break*/, 4]; case 3: error_5 = _a.sent(); helpers_1.forceLogoutIfNecessary(error_5, dispatch, getState); dispatch(actions_1.batchActions([ { type: action_types_1.PostTypes.EDIT_POST_FAILURE, error: error_5 }, errors_1.logError(error_5), ])); return [2 /*return*/, { error: error_5 }]; case 4: actions = [ { type: action_types_1.PostTypes.EDIT_POST_SUCCESS, }, ]; post = Selectors.getPost(getState(), postId); if (post) { actions.push(receivedPost(tslib_1.__assign(tslib_1.__assign({}, post), { is_pinned: false, update_at: Date.now() })), { type: action_types_1.ChannelTypes.DECREMENT_PINNED_POST_COUNT, id: post.channel_id, }); } dispatch(actions_1.batchActions(actions)); return [2 /*return*/, { data: posts }]; } }); }); }; } exports.unpinPost = unpinPost; function addReaction(postId, emojiName) { var _this = this; return function (dispatch, getState) { return tslib_1.__awaiter(_this, void 0, void 0, function () { var currentUserId, reaction, error_6; return tslib_1.__generator(this, function (_a) { switch (_a.label) { case 0: currentUserId = getState().entities.users.currentUserId; _a.label = 1; case 1: _a.trys.push([1, 3, , 4]); return [4 /*yield*/, client_1.Client4.addReaction(currentUserId, postId, emojiName)]; case 2: reaction = _a.sent(); return [3 /*break*/, 4]; case 3: error_6 = _a.sent(); helpers_1.forceLogoutIfNecessary(error_6, dispatch, getState); dispatch(errors_1.logError(error_6)); return [2 /*return*/, { error: error_6 }]; case 4: dispatch({ type: action_types_1.PostTypes.RECEIVED_REACTION, data: reaction, }); return [2 /*return*/, { data: true }]; } }); }); }; } exports.addReaction = addReaction; function removeReaction(postId, emojiName) { var _this = this; return function (dispatch, getState) { return tslib_1.__awaiter(_this, void 0, void 0, function () { var currentUserId, error_7; return tslib_1.__generator(this, function (_a) { switch (_a.label) { case 0: currentUserId = getState().entities.users.currentUserId; _a.label = 1; case 1: _a.trys.push([1, 3, , 4]); return [4 /*yield*/, client_1.Client4.removeReaction(currentUserId, postId, emojiName)]; case 2: _a.sent(); return [3 /*break*/, 4]; case 3: error_7 = _a.sent(); helpers_1.forceLogoutIfNecessary(error_7, dispatch, getState); dispatch(errors_1.logError(error_7)); return [2 /*return*/, { error: error_7 }]; case 4: dispatch({ type: action_types_1.PostTypes.REACTION_DELETED, data: { user_id: currentUserId, post_id: postId, emoji_name: emojiName }, }); return [2 /*return*/, { data: true }]; } }); }); }; } exports.removeReaction = removeReaction; function getCustomEmojiForReaction(name) { var _this = this; return function (dispatch, getState) { return tslib_1.__awaiter(_this, void 0, void 0, function () { var nonExistentEmoji, customEmojisByName; return tslib_1.__generator(this, function (_a) { nonExistentEmoji = getState().entities.emojis.nonExistentEmoji; customEmojisByName = emojis_1.getCustomEmojisByName(getState()); if (emojis_2.systemEmojis.has(name)) { return [2 /*return*/, { data: true }]; } if (nonExistentEmoji.has(name)) { return [2 /*return*/, { data: true }]; } if (customEmojisByName.has(name)) { return [2 /*return*/, { data: true }]; } return [2 /*return*/, dispatch(emojis_2.getCustomEmojiByName(name))]; }); }); }; } exports.getCustomEmojiForReaction = getCustomEmojiForReaction; function getReactionsForPost(postId) { var _this = this; return function (dispatch, getState) { return tslib_1.__awaiter(_this, void 0, void 0, function () { var reactions, error_8, nonExistentEmoji_1, customEmojisByName_1, emojisToLoad_1; return tslib_1.__generator(this, function (_a) { switch (_a.label) { case 0: _a.trys.push([0, 2, , 3]); return [4 /*yield*/, client_1.Client4.getReactionsForPost(postId)]; case 1: reactions = _a.sent(); return [3 /*break*/, 3]; case 2: error_8 = _a.sent(); helpers_1.forceLogoutIfNecessary(error_8, dispatch, getState); dispatch(errors_1.logError(error_8)); return [2 /*return*/, { error: error_8 }]; case 3: if (reactions && reactions.length > 0) { nonExistentEmoji_1 = getState().entities.emojis.nonExistentEmoji; customEmojisByName_1 = emojis_1.getCustomEmojisByName(getState()); emojisToLoad_1 = new Set(); reactions.forEach(function (r) { var name = r.emoji_name; if (emojis_2.systemEmojis.has(name)) { // It's a system emoji, go the next match return; } if (nonExistentEmoji_1.has(name)) { // We've previously confirmed this is not a custom emoji return; } if (customEmojisByName_1.has(name)) { // We have the emoji, go to the next match return; } emojisToLoad_1.add(name); }); dispatch(emojis_2.getCustomEmojisByName(Array.from(emojisToLoad_1))); } dispatch(actions_1.batchActions([ { type: action_types_1.PostTypes.RECEIVED_REACTIONS, data: reactions, postId: postId, }, ])); return [2 /*return*/, reactions]; } }); }); }; } exports.getReactionsForPost = getReactionsForPost; function flagPost(postId) { var _this = this; return function (dispatch, getState) { return tslib_1.__awaiter(_this, void 0, void 0, function () { var currentUserId, preference; return tslib_1.__generator(this, function (_a) { currentUserId = getState().entities.users.currentUserId; preference = { user_id: currentUserId, category: constants_1.Preferences.CATEGORY_FLAGGED_POST, name: postId, value: 'true', }; client_1.Client4.trackEvent('action', 'action_posts_flag'); return [2 /*return*/, preferences_1.savePreferences(currentUserId, [preference])(dispatch)]; }); }); }; } exports.flagPost = flagPost; function getPostThread(rootId, fetchThreads, collapsedThreads, collapsedThreadsExtended) { var _this = this; if (fetchThreads === void 0) { fetchThreads = true; } if (collapsedThreads === void 0) { collapsedThreads = false; } if (collapsedThreadsExtended === void 0) { collapsedThreadsExtended = false; } return function (dispatch, getState) { return tslib_1.__awaiter(_this, void 0, void 0, function () { var posts, error_9; return tslib_1.__generator(this, function (_a) { switch (_a.label) { case 0: dispatch({ type: action_types_1.PostTypes.GET_POST_THREAD_REQUEST }); _a.label = 1; case 1: _a.trys.push([1, 3, , 4]); return [4 /*yield*/, client_1.Client4.getPostThread(rootId, fetchThreads, collapsedThreads, collapsedThreadsExtended)]; case 2: posts = _a.sent(); getProfilesAndStatusesForPosts(posts.posts, dispatch, getState); return [3 /*break*/, 4]; case 3: error_9 = _a.sent(); helpers_1.forceLogoutIfNecessary(error_9, dispatch, getState); dispatch(actions_1.batchActions([ { type: action_types_1.PostTypes.GET_POST_THREAD_FAILURE, error: error_9 }, errors_1.logError(error_9), ])); return [2 /*return*/, { error: error_9 }]; case 4: dispatch(actions_1.batchActions([ receivedPosts(posts), receivedPostsInThread(posts, rootId), { type: action_types_1.PostTypes.GET_POST_THREAD_SUCCESS, }, ])); return [2 /*return*/, { data: posts }]; } }); }); }; } exports.getPostThread = getPostThread; function getPosts(channelId, page, perPage, fetchThreads, collapsedThreads, collapsedThreadsExtended) { var _this = this; if (page === void 0) { page = 0; } if (perPage === void 0) { perPage = constants_1.Posts.POST_CHUNK_SIZE; } if (fetchThreads === void 0) { fetchThreads = true; } if (collapsedThreads === void 0) { collapsedThreads = false; } if (collapsedThreadsExtended === void 0) { collapsedThreadsExtended = false; } return function (dispatch, getState) { return tslib_1.__awaiter(_this, void 0, void 0, function () { var posts, error_10; return tslib_1.__generator(this, function (_a) { switch (_a.label) { case 0: _a.trys.push([0, 2, , 3]); return [4 /*yield*/, client_1.Client4.getPosts(channelId, page, perPage, fetchThreads, collapsedThreads, collapsedThreadsExtended)]; case 1: posts = _a.sent(); getProfilesAndStatusesForPosts(posts.posts, dispatch, getState); return [3 /*break*/, 3]; case 2: error_10 = _a.sent(); helpers_1.forceLogoutIfNecessary(error_10, dispatch, getState); dispatch(errors_1.logError(error_10)); return [2 /*return*/, { error: error_10 }]; case 3: dispatch(actions_1.batchActions([ receivedPosts(posts), receivedPostsInChannel(posts, channelId, page === 0, posts.prev_post_id === ''), ])); return [2 /*return*/, { data: posts }]; } }); }); }; } exports.getPosts = getPosts; function getPostsUnread(channelId, fetchThreads, collapsedThreads, collapsedThreadsExtended) { var _this = this; if (fetchThreads === void 0) { fetchThreads = true; } if (collapsedThreads === void 0) { collapsedThreads = false; } if (collapsedThreadsExtended === void 0) { collapsedThreadsExtended = false; } return function (dispatch, getState) { return tslib_1.__awaiter(_this, void 0, void 0, function () { var userId, posts, error_11; return tslib_1.__generator(this, function (_a) { switch (_a.label) { case 0: userId = users_1.getCurrentUserId(getState()); _a.label = 1; case 1: _a.trys.push([1, 3, , 4]); return [4 /*yield*/, client_1.Client4.getPostsUnread(channelId, userId, client_1.DEFAULT_LIMIT_BEFORE, client_1.DEFAULT_LIMIT_AFTER, fetchThreads, collapsedThreads, collapsedThreadsExtended)]; case 2: posts = _a.sent(); getProfilesAndStatusesForPosts(posts.posts, dispatch, getState); return [3 /*break*/, 4]; case 3: error_11 = _a.sent(); helpers_1.forceLogoutIfNecessary(error_11, dispatch, getState); dispatch(errors_1.logError(error_11)); return [2 /*return*/, { error: error_11 }]; case 4: dispatch(actions_1.batchActions([ receivedPosts(posts), receivedPostsInChannel(posts, channelId, posts.next_post_id === '', posts.prev_post_id === ''), ])); dispatch({ type: action_types_1.PostTypes.RECEIVED_POSTS, data: posts, channelId: channelId, }); return [2 /*return*/, { data: posts }]; } }); }); }; } exports.getPostsUnread = getPostsUnread; function getPostsSince(channelId, since, fetchThreads, collapsedThreads, collapsedThreadsExtended) { var _this = this; if (fetchThreads === void 0) { fetchThreads = true; } if (collapsedThreads === void 0) { collapsedThreads = false; } if (collapsedThreadsExtended === void 0) { collapsedThreadsExtended = false; } return function (dispatch, getState) { return tslib_1.__awaiter(_this, void 0, void 0, function () { var posts, error_12; return tslib_1.__generator(this, function (_a) { switch (_a.label) { case 0: _a.trys.push([0, 2, , 3]); return [4 /*yield*/, client_1.Client4.getPostsSince(channelId, since, fetchThreads, collapsedThreads, collapsedThreadsExtended)]; case 1: posts = _a.sent(); getProfilesAndStatusesForPosts(posts.posts, dispatch, getState); return [3 /*break*/, 3]; case 2: error_12 = _a.sent(); helpers_1.forceLogoutIfNecessary(error_12, dispatch, getState); dispatch(errors_1.logError(error_12)); return [2 /*return*/, { error: error_12 }]; case 3: dispatch(actions_1.batchActions([ receivedPosts(posts), receivedPostsSince(posts, channelId), { type: action_types_1.PostTypes.GET_POSTS_SINCE_SUCCESS, }, ])); return [2 /*return*/, { data: posts }]; } }); }); }; } exports.getPostsSince = getPostsSince; function getPostsBefore(channelId, postId, page, perPage, fetchThreads, collapsedThreads, collapsedThreadsExtended) { var _this = this; if (page === void 0) { page = 0; } if (perPage === void 0) { perPage = constants_1.Posts.POST_CHUNK_SIZE; } if (fetchThreads === void 0) { fetchThreads = true; } if (collapsedThreads === void 0) { collapsedThreads = false; } if (collapsedThreadsExtended === void 0) { collapsedThreadsExtended = false; } return function (dispatch, getState) { return tslib_1.__awaiter(_this, void 0, void 0, function () { var posts, error_13; return tslib_1.__generator(this, function (_a) { switch (_a.label) { case 0: _a.trys.push([0, 2, , 3]); return [4 /*yield*/, client_1.Client4.getPostsBefore(channelId, postId, page, perPage, fetchThreads, collapsedThreads, collapsedThreadsExtended)]; case 1: posts = _a.sent(); getProfilesAndStatusesForPosts(posts.posts, dispatch, getState); return [3 /*break*/, 3]; case 2: error_13 = _a.sent(); helpers_1.forceLogoutIfNecessary(error_13, dispatch, getState); dispatch(errors_1.logError(error_13)); return [2 /*return*/, { error: error_13 }]; case 3: dispatch(actions_1.batchActions([ receivedPosts(posts), receivedPostsBefore(posts, channelId, postId, posts.prev_post_id === ''), ])); return [2 /*return*/, { data: posts }]; } }); }); }; } exports.getPostsBefore = getPostsBefore; function getPostsAfter(channelId, postId, page, perPage, fetchThreads, collapsedThreads, collapsedThreadsExtended) { var _this = this; if (page === void 0) { page = 0; } if (perPage === void 0) { perPage = constants_1.Posts.POST_CHUNK_SIZE; } if (fetchThreads === void 0) { fetchThreads = true; } if (collapsedThreads === void 0) { collapsedThreads = false; } if (collapsedThreadsExtended === void 0) { collapsedThreadsExtended = false; } return function (dispatch, getState) { return tslib_1.__awaiter(_this, void 0, void 0, function () { var posts, error_14; return tslib_1.__generator(this, function (_a) { switch (_a.label) { case 0: _a.trys.push([0, 2, , 3]); return [4 /*yield*/, client_1.Client4.getPostsAfter(channelId, postId, page, perPage, fetchThreads, collapsedThreads, collapsedThreadsExtended)]; case 1: posts = _a.sent(); getProfilesAndStatusesForPosts(posts.posts, dispatch, getState); return [3 /*break*/, 3]; case 2: error_14 = _a.sent(); helpers_1.forceLogoutIfNecessary(error_14, dispatch, getState); dispatch(errors_1.logError(error_14)); return [2 /*return*/, { error: error_14 }]; case 3: dispatch(actions_1.batchActions([ receivedPosts(posts), receivedPostsAfter(posts, channelId, postId, posts.next_post_id === ''), ])); return [2 /*return*/, { data: posts }]; } }); }); }; } exports.getPostsAfter = getPostsAfter; function getPostsAround(channelId, postId, perPage, fetchThreads, collapsedThreads, collapsedThreadsExtended) { var _this = this; if (perPage === void 0) { perPage = constants_1.Posts.POST_CHUNK_SIZE / 2; } if (fetchThreads === void 0) { fetchThreads = true; } if (collapsedThreads === void 0) { collapsedThreads = false; } if (collapsedThreadsExtended === void 0) { collapsedThreadsExtended = false; } return function (dispatch, getState) { return tslib_1.__awaiter(_this, void 0, void 0, function () { var after, thread, before, error_15, posts; var _a; return tslib_1.__generator(this, function (_b) { switch (_b.label) { case 0: _b.trys.push([0, 2, , 3]); return [4 /*yield*/, Promise.all([ client_1.Client4.getPostsAfter(channelId, postId, 0, perPage, fetchThreads, collapsedThreads, collapsedThreadsExtended), client_1.Client4.getPostThread(postId, fetchThreads, collapsedThreads, collapsedThreadsExtended), client_1.Client4.getPostsBefore(channelId, postId, 0, perPage, fetchThreads, collapsedThreads, collapsedThreadsExtended), ])]; case 1: _a = tslib_1.__read.apply(void 0, [_b.sent(), 3]), after = _a[0], thread = _a[1], before = _a[2]; return [3 /*break*/, 3]; case 2: error_15 = _b.sent(); helpers_1.forceLogoutIfNecessary(error_15, dispatch, getState); dispatch(errors_1.logError(error_15)); return [2 /*return*/, { error: error_15 }]; case 3: posts = { posts: tslib_1.__assign(tslib_1.__assign(tslib_1.__assign({}, after.posts), thread.posts), before.posts), order: tslib_1.__spread(after.order, [ postId ], before.order), next_post_id: after.next_post_id, prev_post_id: before.prev_post_id, }; getProfilesAndStatusesForPosts(posts.posts, dispatch, getState); dispatch(actions_1.batchActions([ receivedPosts(posts), receivedPostsInChannel(posts, channelId, after.next_post_id === '', before.prev_post_id === ''), ])); return [2 /*return*/, { data: posts }]; } }); }); }; } exports.getPostsAround = getPostsAround; // getThreadsForPosts is intended for an array of posts that have been batched // (see the actions/websocket_actions/handleNewPostEvents function in the webapp) function getThreadsForPosts(posts, fetchThreads) { if (fetchThreads === void 0) { fetchThreads = true; } return function (dispatch, getState) { if (!Array.isArray(posts) || !posts.length) { return { data: true }; } var state = getState(); var promises = []; posts.forEach(function (post) { if (!post.root_id) { return; } var rootPost = Selectors.getPost(state, post.root_id); if (!rootPost) { promises.push(dispatch(getPostThread(post.root_id, fetchThreads))); } }); return Promise.all(promises); }; } exports.getThreadsForPosts = getThreadsForPosts; // Note that getProfilesAndStatusesForPosts can take either an array of posts or a map of ids to posts function getProfilesAndStatusesForPosts(postsArrayOrMap, dispatch, getState) { if (!postsArrayOrMap) { // Some API methods return {error} for no results return Promise.resolve(); } var posts = Object.values(postsArrayOrMap); if (posts.length === 0) { return Promise.resolve(); } var state = getState(); var _a = state.entities.users, currentUserId = _a.currentUserId, profiles = _a.profiles, statuses = _a.statuses; // Statuses and profiles of the users who made the posts var userIdsToLoad = new Set(); var statusesToLoad = new Set(); Object.values(posts).forEach(function (post) { var userId = post.user_id; if (!statuses[userId]) { statusesToLoad.add(userId); } if (userId === currentUserId) { return; } if (!profiles[userId]) { userIdsToLoad.add(userId); } }); var promises = []; if (userIdsToLoad.size > 0) { promises.push(users_2.getProfilesByIds(Array.from(userIdsToLoad))(dispatch, getState)); } if (statusesToLoad.size > 0) { promises.push(users_2.getStatusesByIds(Array.from(statusesToLoad))(dispatch, getState)); } // Profiles of users mentioned in the posts var usernamesToLoad = getNeededAtMentionedUsernames(state, posts); if (usernamesToLoad.size > 0) { promises.push(users_2.getProfilesByUsernames(Array.from(usernamesToLoad))(dispatch, getState)); } // Emojis used in the posts var emojisToLoad = getNeededCustomEmojis(state, posts); if (emojisToLoad && emojisToLoad.size > 0) { promises.push(emojis_2.getCustomEmojisByName(Array.from(emojisToLoad))(dispatch, getState)); } return Promise.all(promises); } exports.getProfilesAndStatusesForPosts = getProfilesAndStatusesForPosts; function getNeededAtMentionedUsernames(state, posts) { var usersByUsername; // Populate this lazily since it's relatively expensive var usernamesToLoad = new Set(); posts.forEach(function (post) { if (!post.message.includes('@')) { return; } if (!usersByUsername) { usersByUsername = users_1.getUsersByUsername(state); } var pattern = /\B@(([a-z0-9_.-]*[a-z0-9_])[.-]*)/gi; var match; while ((match = pattern.exec(post.message)) !== null) { // match[1] is the matched mention including trailing punctuation // match[2] is the matched mention without trailing punctuation if (constants_1.General.SPECIAL_MENTIONS.indexOf(match[2]) !== -1) { continue; } if (usersByUsername[match[1]] || usersByUsername[match[2]]) { // We have the user, go to the next match continue; } // If there's no trailing punctuation, this will only add 1 item to the set usernamesToLoad.add(match[1]); usernamesToLoad.add(match[2]); } }); return usernamesToLoad; } exports.getNeededAtMentionedUsernames = getNeededAtMentionedUsernames; function buildPostAttachmentText(attachments) { var attachmentText = ''; attachments.forEach(function (a) { if (a.fields && a.fields.length) { a.fields.forEach(function (f) { attachmentText += ' ' + (f.value || ''); }); } if (a.pretext) { attachmentText += ' ' + a.pretext;