UNPKG

mattermost-redux

Version:

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

206 lines (205 loc) 10.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.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 }; }; }