mattermost-redux
Version:
Common code (API client, Redux stores, logic, utility functions) for building a Mattermost client
279 lines (278 loc) • 11.4 kB
JavaScript
"use strict";
// Copyright (c) 2015-present Mattermost, Inc. All Rights Reserved.
// See LICENSE.txt for license information.
Object.defineProperty(exports, "__esModule", { value: true });
exports.WEBAPP_SEARCH_PER_PAGE = void 0;
exports.getMissingChannelsFromPosts = getMissingChannelsFromPosts;
exports.getMissingChannelsFromFiles = getMissingChannelsFromFiles;
exports.searchPostsWithParams = searchPostsWithParams;
exports.searchPosts = searchPosts;
exports.getMorePostsForSearch = getMorePostsForSearch;
exports.clearSearch = clearSearch;
exports.searchFilesWithParams = searchFilesWithParams;
exports.getMoreFilesForSearch = getMoreFilesForSearch;
exports.getFlaggedPosts = getFlaggedPosts;
exports.getPinnedPosts = getPinnedPosts;
const redux_batched_actions_1 = require("redux-batched-actions");
const action_types_1 = require("mattermost-redux/action_types");
const client_1 = require("mattermost-redux/client");
const users_1 = require("mattermost-redux/selectors/entities/users");
const channels_1 = require("./channels");
const errors_1 = require("./errors");
const files_1 = require("./files");
const helpers_1 = require("./helpers");
const posts_1 = require("./posts");
exports.WEBAPP_SEARCH_PER_PAGE = 20;
function getMissingChannelsFromPosts(posts) {
return async (dispatch, getState) => {
const { channels, membersInChannel, myMembers, } = getState().entities.channels;
const promises = [];
Object.values(posts).forEach((post) => {
const id = post.channel_id;
if (!channels[id]) {
// Fetch channel data independently so a 403 on the membership request (e.g. for public channels
// the user hasn't joined) doesn't prevent the channel from being loaded.
promises.push(dispatch((0, channels_1.getChannel)(id)));
}
if (!myMembers[id]) {
// Best-effort: will 403 for non-member public channels, which is fine.
promises.push(dispatch((0, channels_1.getChannelAndMyMember)(id)));
}
if (!membersInChannel[id]) {
promises.push(dispatch((0, channels_1.getChannelMembers)(id)));
}
});
return Promise.all(promises);
};
}
function getMissingChannelsFromFiles(files) {
return async (dispatch, getState) => {
const { channels, membersInChannel, myMembers, } = getState().entities.channels;
const promises = [];
Object.values(files).forEach((file) => {
const id = file.channel_id;
if (!channels[id]) {
// Fetch channel data independently so a 403 on the membership request (e.g. for public channels
// the user hasn't joined) doesn't prevent the channel from being loaded.
promises.push(dispatch((0, channels_1.getChannel)(id)));
}
if (!myMembers[id]) {
// Best-effort: will 403 for non-member public channels, which is fine.
promises.push(dispatch((0, channels_1.getChannelAndMyMember)(id)));
}
if (!membersInChannel[id]) {
promises.push(dispatch((0, channels_1.getChannelMembers)(id)));
}
});
return Promise.all(promises);
};
}
function searchPostsWithParams(teamId, params) {
return async (dispatch, getState) => {
const isGettingMore = params.page > 0;
dispatch({
type: action_types_1.SearchTypes.SEARCH_POSTS_REQUEST,
isGettingMore,
});
// Reset truncation info for new searches (not pagination)
if (!isGettingMore) {
dispatch({
type: action_types_1.SearchTypes.RECEIVED_SEARCH_TRUNCATION_INFO,
data: {
firstInaccessiblePostTime: 0,
searchType: 'posts',
},
});
}
let posts;
try {
posts = await client_1.Client4.searchPostsWithParams(teamId, params);
const profilesAndStatuses = (0, posts_1.getMentionsAndStatusesForPosts)(posts.posts, dispatch, getState);
const missingChannels = dispatch(getMissingChannelsFromPosts(posts.posts));
const arr = [profilesAndStatuses, missingChannels];
await Promise.all(arr);
}
catch (error) {
(0, helpers_1.forceLogoutIfNecessary)(error, dispatch, getState);
dispatch((0, errors_1.logError)(error));
return { error };
}
dispatch((0, redux_batched_actions_1.batchActions)([
{
type: action_types_1.SearchTypes.RECEIVED_SEARCH_POSTS,
data: posts,
isGettingMore,
},
(0, posts_1.receivedPosts)(posts),
{
type: action_types_1.SearchTypes.RECEIVED_SEARCH_TERM,
data: {
teamId,
params,
isEnd: posts.order.length === 0,
},
},
{
type: action_types_1.SearchTypes.SEARCH_POSTS_SUCCESS,
},
], 'SEARCH_POST_BATCH'));
// Dispatch truncation info separately to avoid typing conflicts
const firstInaccessiblePostTime = posts.first_inaccessible_post_time || 0;
if (firstInaccessiblePostTime > 0) {
dispatch({
type: action_types_1.SearchTypes.RECEIVED_SEARCH_TRUNCATION_INFO,
data: {
firstInaccessiblePostTime,
searchType: 'posts',
},
});
}
return { data: posts };
};
}
function searchPosts(teamId, terms, isOrSearch, includeDeletedChannels) {
return searchPostsWithParams(teamId, { terms, is_or_search: isOrSearch, include_deleted_channels: includeDeletedChannels, page: 0, per_page: exports.WEBAPP_SEARCH_PER_PAGE });
}
function getMorePostsForSearch(teamId) {
return async (dispatch, getState) => {
const { params, isEnd } = getState().entities.search.current[teamId || 'ALL_TEAMS'];
if (!isEnd) {
const newParams = Object.assign({}, params);
newParams.page += 1;
return dispatch(searchPostsWithParams(teamId, newParams));
}
return { data: true };
};
}
function clearSearch() {
return async (dispatch) => {
dispatch({ type: action_types_1.SearchTypes.REMOVE_SEARCH_POSTS });
dispatch({ type: action_types_1.SearchTypes.REMOVE_SEARCH_FILES });
return { data: true };
};
}
function searchFilesWithParams(teamId, params) {
return async (dispatch, getState) => {
const isGettingMore = params.page > 0;
dispatch({
type: action_types_1.SearchTypes.SEARCH_FILES_REQUEST,
isGettingMore,
});
// Reset truncation info for new searches (not pagination)
if (!isGettingMore) {
dispatch({
type: action_types_1.SearchTypes.RECEIVED_SEARCH_TRUNCATION_INFO,
data: {
firstInaccessiblePostTime: 0,
searchType: 'files',
},
});
}
let files;
try {
files = await client_1.Client4.searchFilesWithParams(teamId, params);
await dispatch(getMissingChannelsFromFiles(files.file_infos));
}
catch (error) {
(0, helpers_1.forceLogoutIfNecessary)(error, dispatch, getState);
dispatch((0, errors_1.logError)(error));
return { error };
}
dispatch((0, redux_batched_actions_1.batchActions)([
{
type: action_types_1.SearchTypes.RECEIVED_SEARCH_FILES,
data: files,
isGettingMore,
},
(0, files_1.receivedFiles)(files.file_infos),
{
type: action_types_1.SearchTypes.RECEIVED_SEARCH_TERM,
data: {
teamId,
params,
isFilesEnd: files.order.length === 0,
},
},
{
type: action_types_1.SearchTypes.SEARCH_FILES_SUCCESS,
},
], 'SEARCH_FILE_BATCH'));
return { data: files };
};
}
function getMoreFilesForSearch(teamId) {
return async (dispatch, getState) => {
const { params, isFilesEnd } = getState().entities.search.current[teamId || 'ALL_TEAMS'];
if (!isFilesEnd) {
const newParams = Object.assign({}, params);
newParams.page += 1;
return dispatch(searchFilesWithParams(teamId, newParams));
}
return { data: true };
};
}
function getFlaggedPosts() {
return async (dispatch, getState) => {
const state = getState();
const userId = (0, users_1.getCurrentUserId)(state);
dispatch({ type: action_types_1.SearchTypes.SEARCH_FLAGGED_POSTS_REQUEST });
let posts;
try {
posts = await client_1.Client4.getFlaggedPosts(userId);
await Promise.all([(0, posts_1.getMentionsAndStatusesForPosts)(posts.posts, dispatch, getState), dispatch(getMissingChannelsFromPosts(posts.posts))]);
}
catch (error) {
(0, helpers_1.forceLogoutIfNecessary)(error, dispatch, getState);
dispatch({ type: action_types_1.SearchTypes.SEARCH_FLAGGED_POSTS_FAILURE, error });
dispatch((0, errors_1.logError)(error));
return { error };
}
dispatch((0, redux_batched_actions_1.batchActions)([
{
type: action_types_1.SearchTypes.RECEIVED_SEARCH_FLAGGED_POSTS,
data: posts,
},
(0, posts_1.receivedPosts)(posts),
{
type: action_types_1.SearchTypes.SEARCH_FLAGGED_POSTS_SUCCESS,
},
], 'SEARCH_FLAGGED_POSTS_BATCH'));
return { data: posts };
};
}
function getPinnedPosts(channelId) {
return async (dispatch, getState) => {
dispatch({ type: action_types_1.SearchTypes.SEARCH_PINNED_POSTS_REQUEST });
let result;
try {
result = await client_1.Client4.getPinnedPosts(channelId);
const profilesAndStatuses = (0, posts_1.getMentionsAndStatusesForPosts)(result.posts, dispatch, getState);
const missingChannels = dispatch(getMissingChannelsFromPosts(result.posts));
const arr = [profilesAndStatuses, missingChannels];
await Promise.all(arr);
}
catch (error) {
(0, helpers_1.forceLogoutIfNecessary)(error, dispatch, getState);
dispatch({ type: action_types_1.SearchTypes.SEARCH_PINNED_POSTS_FAILURE, error });
return { error };
}
dispatch((0, redux_batched_actions_1.batchActions)([
{
type: action_types_1.SearchTypes.RECEIVED_SEARCH_PINNED_POSTS,
data: {
pinned: result,
channelId,
},
},
(0, posts_1.receivedPosts)(result),
{
type: action_types_1.SearchTypes.SEARCH_PINNED_POSTS_SUCCESS,
},
], 'SEARCH_PINNED_POSTS_BATCH'));
return { data: result };
};
}
exports.default = {
clearSearch,
searchPosts,
};