mattermost-redux
Version:
Common code (API client, Redux stores, logic, utility functions) for building a Mattermost client
206 lines (205 loc) • 10.6 kB
JavaScript
;
// Copyright (c) 2015-present Mattermost, Inc. All Rights Reserved.
// See LICENSE.txt for license information.
Object.defineProperty(exports, "__esModule", { value: true });
exports.addUserIdsForStatusFetchingPoll = addUserIdsForStatusFetchingPoll;
exports.addUserIdsForProfileFetchingPoll = addUserIdsForProfileFetchingPoll;
exports.cleanUpStatusAndProfileFetchingPoll = cleanUpStatusAndProfileFetchingPoll;
exports.extractUserIdsAndMentionsFromPosts = extractUserIdsAndMentionsFromPosts;
exports.batchFetchStatusesProfilesGroupsFromPosts = batchFetchStatusesProfilesGroupsFromPosts;
exports.getUsersFromMentionedUsernamesAndGroups = getUsersFromMentionedUsernamesAndGroups;
const groups_1 = require("mattermost-redux/actions/groups");
const posts_1 = require("mattermost-redux/actions/posts");
const users_1 = require("mattermost-redux/actions/users");
const common_1 = require("mattermost-redux/selectors/entities/common");
const general_1 = require("mattermost-redux/selectors/entities/general");
const users_2 = require("mattermost-redux/selectors/entities/users");
const data_loader_1 = require("mattermost-redux/utils/data_loader");
/**
* Adds list(s) of user id(s) to the status fetching poll. Which gets fetched based on user interval polling duration
* Do not use if status is required immediately.
*/
function addUserIdsForStatusFetchingPoll(userIdsForStatus) {
return (dispatch, getState, { loaders }) => {
if (!loaders.pollingStatusLoader) {
loaders.pollingStatusLoader = new data_loader_1.BackgroundDataLoader({
fetchBatch: (userIds) => dispatch((0, users_1.getStatusesByIds)(userIds)),
maxBatchSize: users_1.maxUserIdsPerStatusesRequest,
});
}
loaders.pollingStatusLoader.queue(userIdsForStatus);
const pollingInterval = (0, general_1.getUsersStatusAndProfileFetchingPollInterval)(getState());
// Escape hatch to fetch immediately or when we haven't received the polling interval from config yet
if (!pollingInterval || pollingInterval <= 0) {
loaders.pollingStatusLoader.fetchBatchNow();
}
else {
// Start the interval if it is not already running
loaders.pollingStatusLoader.startIntervalIfNeeded(pollingInterval);
}
// Now here the interval is already running and we have added the user ids to the poll so we don't need to do anything
return { data: true };
};
}
/**
* Adds list(s) of user id(s) to the profile fetching poll. Which gets fetched based on user interval polling duration
* Do not use if profile is required immediately.
*/
function addUserIdsForProfileFetchingPoll(userIdsForProfile) {
return (dispatch, getState, { loaders }) => {
if (!loaders.pollingProfileLoader) {
loaders.pollingProfileLoader = new data_loader_1.BackgroundDataLoader({
fetchBatch: (userIds) => dispatch((0, users_1.getProfilesByIds)(userIds)),
maxBatchSize: users_1.maxUserIdsPerProfilesRequest,
});
}
loaders.pollingProfileLoader.queue(userIdsForProfile);
const pollingInterval = (0, general_1.getUsersStatusAndProfileFetchingPollInterval)(getState());
// Escape hatch to fetch immediately or when we haven't received the polling interval from config yet
if (!pollingInterval || pollingInterval <= 0) {
loaders.pollingProfileLoader.fetchBatchNow();
}
else {
// Start the interval if it is not already running
loaders.pollingProfileLoader.startIntervalIfNeeded(pollingInterval);
}
// Now here the interval is already running and we have added the user ids to the poll so we don't need to do anything
return { data: true };
};
}
function cleanUpStatusAndProfileFetchingPoll() {
return (dispatch, getState, { loaders }) => {
loaders.pollingStatusLoader?.stopInterval();
loaders.pollingProfileLoader?.stopInterval();
};
}
function extractUserIdsAndMentionsFromPosts(posts) {
return (dispatch, getState) => {
if (posts.length === 0) {
return { data: {
userIdsForProfilePoll: [],
userIdsForStatusPoll: [],
mentionedUsernamesAndGroups: [],
} };
}
const userIdsForProfilePoll = new Set();
const userIdsForStatusPoll = new Set();
const mentionedUsernamesAndGroupsInPosts = new Set();
const state = getState();
const currentUser = (0, common_1.getCurrentUser)(state);
const currentUserId = (0, common_1.getCurrentUserId)(state);
const isUserStatusesConfigEnabled = (0, common_1.getIsUserStatusesConfigEnabled)(state);
const users = (0, common_1.getUsers)(state);
const userStatuses = (0, users_2.getUserStatuses)(state);
posts.forEach((post) => {
if (post.metadata) {
// Add users listed in permalink previews
if (post.metadata.embeds) {
post.metadata.embeds.forEach((embed) => {
if (embed.type === 'permalink' && embed.data) {
const permalinkPostPreviewMetaData = embed.data;
if (permalinkPostPreviewMetaData.post?.user_id && !users[permalinkPostPreviewMetaData.post.user_id] && permalinkPostPreviewMetaData.post.user_id !== currentUserId) {
userIdsForProfilePoll.add(permalinkPostPreviewMetaData.post.user_id);
}
if (permalinkPostPreviewMetaData.post?.user_id && !userStatuses[permalinkPostPreviewMetaData.post.user_id] && permalinkPostPreviewMetaData.post.user_id !== currentUserId && isUserStatusesConfigEnabled) {
userIdsForStatusPoll.add(permalinkPostPreviewMetaData.post.user_id);
}
}
});
}
// Add users listed in the Post Acknowledgement feature
if (post.metadata.acknowledgements) {
post.metadata.acknowledgements.forEach((ack) => {
if (ack.acknowledged_at > 0 && ack.user_id && !users[ack.user_id] && ack.user_id !== currentUserId) {
userIdsForProfilePoll.add(ack.user_id);
}
});
}
}
// This is sufficient to check if the profile is already fetched
// as we receive the websocket events for the profiles changes
if (!users[post.user_id] && post.user_id !== currentUserId) {
userIdsForProfilePoll.add(post.user_id);
}
// This is sufficient to check if the status is already fetched
// as we do the polling for statuses for current channel's channel members every 1 minute in channel_controller
if (!userStatuses[post.user_id] && post.user_id !== currentUserId && isUserStatusesConfigEnabled) {
userIdsForStatusPoll.add(post.user_id);
}
// We need to check for all @mentions in the post, they can be either users or groups
const mentioned = (0, posts_1.getNeededAtMentionedUsernamesAndGroups)(state, [post]);
if (mentioned.size > 0) {
mentioned.forEach((atMention) => {
if (atMention !== currentUser.username) {
mentionedUsernamesAndGroupsInPosts.add(atMention);
}
});
}
});
return { data: {
userIdsForProfilePoll: Array.from(userIdsForProfilePoll),
userIdsForStatusPoll: Array.from(userIdsForStatusPoll),
mentionedUsernamesAndGroups: Array.from(mentionedUsernamesAndGroupsInPosts),
} };
};
}
/**
* Gets in batch the user profiles, user statuses and user groups for the users in the posts list
* This action however doesn't refetch the profiles and statuses except for groups if they are already fetched once
*/
function batchFetchStatusesProfilesGroupsFromPosts(postsArrayOrMap) {
return (dispatch, getState) => {
if (!postsArrayOrMap) {
return { data: false };
}
let posts = [];
if (Array.isArray(postsArrayOrMap)) {
posts = postsArrayOrMap;
}
else if (typeof postsArrayOrMap === 'object' && 'id' in postsArrayOrMap) {
posts = [postsArrayOrMap];
}
else if (typeof postsArrayOrMap === 'object') {
posts = Object.values(postsArrayOrMap);
}
if (posts.length === 0) {
return { data: false };
}
const state = getState();
const { data: result } = dispatch(extractUserIdsAndMentionsFromPosts(posts));
if (!result) {
return { data: false };
}
if (result.userIdsForProfilePoll.length > 0) {
dispatch(addUserIdsForProfileFetchingPoll(result.userIdsForProfilePoll));
}
if (result.userIdsForStatusPoll.length > 0) {
dispatch(addUserIdsForStatusFetchingPoll(result.userIdsForStatusPoll));
}
if (result.mentionedUsernamesAndGroups.length > 0) {
dispatch(getUsersFromMentionedUsernamesAndGroups(result.mentionedUsernamesAndGroups, (0, general_1.getLicense)(state).IsLicensed === 'true'));
}
return { data: true };
};
}
function getUsersFromMentionedUsernamesAndGroups(usernamesAndGroups, isLicensed) {
return async (dispatch) => {
// We run the at-mentioned be it user or group through the user profile search
const { data: userProfiles } = await dispatch((0, users_1.getProfilesByUsernames)(usernamesAndGroups));
const mentionedUsernames = [];
// The user at-mentioned will be the userProfiles
if (userProfiles) {
for (const user of userProfiles) {
if (user && user.username) {
mentionedUsernames.push(user.username);
}
}
}
// Removing usernames from the list will leave only the group names
const mentionedGroups = usernamesAndGroups.filter((name) => !mentionedUsernames.includes(name));
if (isLicensed && mentionedGroups.length > 0) {
await dispatch((0, groups_1.getGroupsByNames)(mentionedGroups));
}
return { data: mentionedGroups };
};
}