UNPKG

mattermost-redux

Version:

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

1,241 lines 55.1 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.selectChannel = selectChannel; exports.createChannel = createChannel; exports.createDirectChannel = createDirectChannel; exports.markGroupChannelOpen = markGroupChannelOpen; exports.createGroupChannel = createGroupChannel; exports.patchChannel = patchChannel; exports.setMyChannelAutotranslation = setMyChannelAutotranslation; exports.updateChannelPrivacy = updateChannelPrivacy; exports.convertGroupMessageToPrivateChannel = convertGroupMessageToPrivateChannel; exports.updateChannelNotifyProps = updateChannelNotifyProps; exports.getChannelByNameAndTeamName = getChannelByNameAndTeamName; exports.getChannel = getChannel; exports.getChannelAndMyMember = getChannelAndMyMember; exports.getChannelTimezones = getChannelTimezones; exports.fetchChannelsAndMembers = fetchChannelsAndMembers; exports.fetchAllMyChannelMembers = fetchAllMyChannelMembers; exports.fetchAllMyTeamsChannels = fetchAllMyTeamsChannels; exports.getChannelMembers = getChannelMembers; exports.leaveChannel = leaveChannel; exports.joinChannel = joinChannel; exports.deleteChannel = deleteChannel; exports.unarchiveChannel = unarchiveChannel; exports.updateApproximateViewTime = updateApproximateViewTime; exports.unsetActiveChannelOnServer = unsetActiveChannelOnServer; exports.readMultipleChannels = readMultipleChannels; exports.getChannels = getChannels; exports.getArchivedChannels = getArchivedChannels; exports.getAllChannelsWithCount = getAllChannelsWithCount; exports.getAllChannels = getAllChannels; exports.autocompleteChannels = autocompleteChannels; exports.autocompleteChannelsForSearch = autocompleteChannelsForSearch; exports.searchChannels = searchChannels; exports.searchAllChannels = searchAllChannels; exports.searchGroupChannels = searchGroupChannels; exports.getChannelStats = getChannelStats; exports.getChannelsMemberCount = getChannelsMemberCount; exports.addChannelMember = addChannelMember; exports.addChannelMembers = addChannelMembers; exports.removeChannelMember = removeChannelMember; exports.markChannelAsRead = markChannelAsRead; exports.markMultipleChannelsAsRead = markMultipleChannelsAsRead; exports.markChannelAsViewedOnServer = markChannelAsViewedOnServer; exports.actionsToMarkChannelAsRead = actionsToMarkChannelAsRead; exports.actionsToMarkChannelAsUnread = actionsToMarkChannelAsUnread; exports.getChannelMembersByIds = getChannelMembersByIds; exports.getChannelMember = getChannelMember; exports.getMyChannelMember = getMyChannelMember; exports.loadMyChannelMemberAndRole = loadMyChannelMemberAndRole; exports.favoriteChannel = favoriteChannel; exports.unfavoriteChannel = unfavoriteChannel; exports.updateChannelScheme = updateChannelScheme; exports.updateChannelMemberSchemeRoles = updateChannelMemberSchemeRoles; exports.membersMinusGroupMembers = membersMinusGroupMembers; exports.getChannelModerations = getChannelModerations; exports.patchChannelModerations = patchChannelModerations; exports.getChannelMemberCountsByGroup = getChannelMemberCountsByGroup; exports.fetchMissingChannels = fetchMissingChannels; exports.fetchIsRestrictedDM = fetchIsRestrictedDM; exports.getChannelAccessControlAttributes = getChannelAccessControlAttributes; 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 channel_categories_1 = require("mattermost-redux/constants/channel_categories"); const channels_1 = require("mattermost-redux/constants/channels"); const channel_categories_2 = require("mattermost-redux/selectors/entities/channel_categories"); const channels_2 = require("mattermost-redux/selectors/entities/channels"); const teams_1 = require("mattermost-redux/selectors/entities/teams"); const data_loader_1 = require("mattermost-redux/utils/data_loader"); const channel_categories_3 = require("./channel_categories"); const errors_1 = require("./errors"); const helpers_1 = require("./helpers"); const posts_1 = require("./posts"); const preferences_1 = require("./preferences"); const roles_1 = require("./roles"); const users_1 = require("./users"); const constants_1 = require("../constants"); function selectChannel(channelId) { return { type: action_types_1.ChannelTypes.SELECT_CHANNEL, data: channelId, }; } function createChannel(channel, userId) { return async (dispatch, getState) => { let created; try { created = await client_1.Client4.createChannel(channel); } catch (error) { (0, helpers_1.forceLogoutIfNecessary)(error, dispatch, getState); dispatch({ type: action_types_1.ChannelTypes.CREATE_CHANNEL_FAILURE, error, }); dispatch((0, errors_1.logError)(error)); return { error }; } const member = { channel_id: created.id, user_id: userId, roles: `${constants_1.General.CHANNEL_USER_ROLE} ${constants_1.General.CHANNEL_ADMIN_ROLE}`, last_viewed_at: 0, msg_count: 0, mention_count: 0, notify_props: { desktop: 'default', mark_unread: 'all' }, last_update_at: created.create_at, }; const actions = []; const { channels, myMembers } = getState().entities.channels; if (!channels[created.id]) { actions.push({ type: action_types_1.ChannelTypes.RECEIVED_CHANNEL, data: created }); } if (!myMembers[created.id]) { actions.push({ type: action_types_1.ChannelTypes.RECEIVED_MY_CHANNEL_MEMBER, data: member }); dispatch((0, roles_1.loadRolesIfNeeded)(member.roles.split(' '))); } dispatch((0, redux_batched_actions_1.batchActions)([ ...actions, { type: action_types_1.ChannelTypes.CREATE_CHANNEL_SUCCESS, }, ])); dispatch((0, channel_categories_3.addChannelToInitialCategory)(created, true)); return { data: created }; }; } function createDirectChannel(userId, otherUserId) { return async (dispatch, getState) => { dispatch({ type: action_types_1.ChannelTypes.CREATE_CHANNEL_REQUEST, data: null }); let created; try { created = await client_1.Client4.createDirectChannel([userId, otherUserId]); } catch (error) { (0, helpers_1.forceLogoutIfNecessary)(error, dispatch, getState); dispatch({ type: action_types_1.ChannelTypes.CREATE_CHANNEL_FAILURE, error }); dispatch((0, errors_1.logError)(error)); return { error }; } const member = { channel_id: created.id, user_id: userId, roles: `${constants_1.General.CHANNEL_USER_ROLE}`, last_viewed_at: 0, msg_count: 0, mention_count: 0, notify_props: { desktop: 'default', mark_unread: 'all' }, last_update_at: created.create_at, }; const preferences = [ { user_id: userId, category: constants_1.Preferences.CATEGORY_DIRECT_CHANNEL_SHOW, name: otherUserId, value: 'true' }, { user_id: userId, category: constants_1.Preferences.CATEGORY_CHANNEL_OPEN_TIME, name: created.id, value: new Date().getTime().toString() }, ]; dispatch((0, preferences_1.savePreferences)(userId, preferences)); dispatch((0, redux_batched_actions_1.batchActions)([ { type: action_types_1.ChannelTypes.RECEIVED_CHANNEL, data: created, }, { type: action_types_1.ChannelTypes.RECEIVED_MY_CHANNEL_MEMBER, data: member, }, { type: action_types_1.PreferenceTypes.RECEIVED_PREFERENCES, data: preferences, }, { type: action_types_1.ChannelTypes.CREATE_CHANNEL_SUCCESS, }, { type: action_types_1.UserTypes.RECEIVED_PROFILES_LIST_IN_CHANNEL, id: created.id, data: [{ id: userId }, { id: otherUserId }], }, ])); dispatch((0, channel_categories_3.addChannelToInitialCategory)(created)); dispatch((0, roles_1.loadRolesIfNeeded)(member.roles.split(' '))); return { data: created }; }; } function markGroupChannelOpen(channelId) { return async (dispatch, getState) => { const { currentUserId } = getState().entities.users; const preferences = [ { user_id: currentUserId, category: constants_1.Preferences.CATEGORY_GROUP_CHANNEL_SHOW, name: channelId, value: 'true' }, { user_id: currentUserId, category: constants_1.Preferences.CATEGORY_CHANNEL_OPEN_TIME, name: channelId, value: new Date().getTime().toString() }, ]; return dispatch((0, preferences_1.savePreferences)(currentUserId, preferences)); }; } function createGroupChannel(userIds) { return async (dispatch, getState) => { dispatch({ type: action_types_1.ChannelTypes.CREATE_CHANNEL_REQUEST, data: null }); const { currentUserId } = getState().entities.users; let created; try { created = await client_1.Client4.createGroupChannel(userIds); } catch (error) { (0, helpers_1.forceLogoutIfNecessary)(error, dispatch, getState); dispatch({ type: action_types_1.ChannelTypes.CREATE_CHANNEL_FAILURE, error }); dispatch((0, errors_1.logError)(error)); return { error }; } let member = { channel_id: created.id, user_id: currentUserId, roles: `${constants_1.General.CHANNEL_USER_ROLE}`, last_viewed_at: 0, msg_count: 0, mention_count: 0, msg_count_root: 0, mention_count_root: 0, notify_props: { desktop: 'default', mark_unread: 'all' }, last_update_at: created.create_at, }; // Check the channel previous existency: if the channel already have // posts is because it existed before. if (created.total_msg_count > 0) { const storeMember = (0, channels_2.getMyChannelMember)(getState(), created.id); if (storeMember) { member = storeMember; } else { try { member = await client_1.Client4.getMyChannelMember(created.id); } catch (error) { // Log the error and keep going with the generated membership. dispatch((0, errors_1.logError)(error)); } } } dispatch(markGroupChannelOpen(created.id)); const profilesInChannel = userIds.map((id) => ({ id })); profilesInChannel.push({ id: currentUserId }); // currentUserId is optionally in userIds, but the reducer will get rid of a duplicate dispatch((0, redux_batched_actions_1.batchActions)([ { type: action_types_1.ChannelTypes.RECEIVED_CHANNEL, data: created, }, { type: action_types_1.ChannelTypes.RECEIVED_MY_CHANNEL_MEMBER, data: member, }, { type: action_types_1.ChannelTypes.CREATE_CHANNEL_SUCCESS, }, { type: action_types_1.UserTypes.RECEIVED_PROFILES_LIST_IN_CHANNEL, id: created.id, data: profilesInChannel, }, ])); dispatch((0, channel_categories_3.addChannelToInitialCategory)(created)); dispatch((0, roles_1.loadRolesIfNeeded)((member && member.roles && member.roles.split(' ')) || [])); return { data: created }; }; } function patchChannel(channelId, patch) { return (0, helpers_1.bindClientFunc)({ clientFunc: client_1.Client4.patchChannel, onSuccess: [action_types_1.ChannelTypes.RECEIVED_CHANNEL], params: [channelId, patch], }); } function setMyChannelAutotranslation(channelId, enabled) { return async (dispatch, getState) => { const state = getState(); const myChannelMember = (0, channels_2.getMyChannelMember)(state, channelId); if (!myChannelMember) { return { data: false, error: 'Channel member not found' }; } const updatedMember = { ...myChannelMember, autotranslation_disabled: !enabled, }; try { await client_1.Client4.setMyChannelAutotranslation(channelId, enabled); } catch (error) { (0, helpers_1.forceLogoutIfNecessary)(error, dispatch, getState); dispatch((0, errors_1.logError)(error)); return { data: undefined, error }; } // We check before updating the store const becameEnabled = (0, channels_2.hasAutotranslationBecomeEnabled)(state, updatedMember); dispatch({ type: action_types_1.ChannelTypes.RECEIVED_MY_CHANNEL_MEMBER, data: updatedMember, }); // If autotranslation changed, delete posts for this channel if (becameEnabled) { await dispatch((0, posts_1.resetReloadPostsInChannel)(channelId)); } return { data: true, error: undefined }; }; } function updateChannelPrivacy(channelId, privacy) { return (0, helpers_1.bindClientFunc)({ clientFunc: client_1.Client4.updateChannelPrivacy, onSuccess: [action_types_1.ChannelTypes.RECEIVED_CHANNEL], params: [channelId, privacy], }); } function convertGroupMessageToPrivateChannel(channelID, teamID, displayName, name) { return async (dispatch, getState) => { let response; try { response = await client_1.Client4.convertGroupMessageToPrivateChannel(channelID, teamID, displayName, name); } catch (error) { (0, helpers_1.forceLogoutIfNecessary)(error, dispatch, getState); dispatch((0, errors_1.logError)(error)); return { error }; } dispatch({ type: action_types_1.ChannelTypes.RECEIVED_CHANNEL, data: response.data, }); // move the channel from direct message category to the default "channels" category const channelsCategory = (0, channel_categories_2.getCategoryInTeamByType)(getState(), teamID, channel_categories_1.CategoryTypes.CHANNELS); if (!channelsCategory) { return {}; } return { data: response.data, }; }; } function updateChannelNotifyProps(userId, channelId, props) { return async (dispatch, getState) => { const notifyProps = { user_id: userId, channel_id: channelId, ...props, }; try { await client_1.Client4.updateChannelNotifyProps(notifyProps); } catch (error) { (0, helpers_1.forceLogoutIfNecessary)(error, dispatch, getState); dispatch((0, errors_1.logError)(error)); return { error }; } const member = getState().entities.channels.myMembers[channelId] || {}; const currentNotifyProps = member.notify_props || {}; dispatch({ type: action_types_1.ChannelTypes.RECEIVED_CHANNEL_PROPS, data: { channel_id: channelId, notifyProps: { ...currentNotifyProps, ...notifyProps }, }, }); return { data: true }; }; } function getChannelByNameAndTeamName(teamName, channelName, includeDeleted = false) { return async (dispatch, getState) => { let data; try { data = await client_1.Client4.getChannelByNameAndTeamName(teamName, channelName, includeDeleted); } catch (error) { (0, helpers_1.forceLogoutIfNecessary)(error, dispatch, getState); dispatch({ type: action_types_1.ChannelTypes.CHANNELS_FAILURE, error }); dispatch((0, errors_1.logError)(error)); return { error }; } dispatch({ type: action_types_1.ChannelTypes.RECEIVED_CHANNEL, data, }); return { data }; }; } function getChannel(channelId, asContentReviewer = false) { return async (dispatch, getState) => { let data; try { data = await client_1.Client4.getChannel(channelId, asContentReviewer); } catch (error) { (0, helpers_1.forceLogoutIfNecessary)(error, dispatch, getState); dispatch({ type: action_types_1.ChannelTypes.CHANNELS_FAILURE, error }); dispatch((0, errors_1.logError)(error)); return { error }; } dispatch({ type: action_types_1.ChannelTypes.RECEIVED_CHANNEL, data, }); return { data }; }; } function getChannelAndMyMember(channelId) { return async (dispatch, getState) => { let channel; let member; try { const channelRequest = client_1.Client4.getChannel(channelId); const memberRequest = client_1.Client4.getMyChannelMember(channelId); channel = await channelRequest; member = await memberRequest; } catch (error) { (0, helpers_1.forceLogoutIfNecessary)(error, dispatch, getState); dispatch({ type: action_types_1.ChannelTypes.CHANNELS_FAILURE, error }); dispatch((0, errors_1.logError)(error)); return { error }; } dispatch((0, redux_batched_actions_1.batchActions)([ { type: action_types_1.ChannelTypes.RECEIVED_CHANNEL, data: channel, }, { type: action_types_1.ChannelTypes.RECEIVED_MY_CHANNEL_MEMBER, data: member, }, ])); dispatch((0, roles_1.loadRolesIfNeeded)(member.roles.split(' '))); return { data: { channel, member } }; }; } function getChannelTimezones(channelId) { return async (dispatch, getState) => { let channelTimezones; try { const channelTimezonesRequest = client_1.Client4.getChannelTimezones(channelId); channelTimezones = await channelTimezonesRequest; } catch (error) { (0, helpers_1.forceLogoutIfNecessary)(error, dispatch, getState); dispatch((0, errors_1.logError)(error)); return { error }; } return { data: channelTimezones }; }; } function fetchChannelsAndMembers(teamId) { return async (dispatch, getState) => { let channels = []; let channelMembers = []; try { [channels, channelMembers] = await Promise.all([ client_1.Client4.getMyChannels(teamId), client_1.Client4.getMyChannelMembers(teamId), ]); } catch (error) { (0, helpers_1.forceLogoutIfNecessary)(error, dispatch, getState); dispatch((0, errors_1.logError)(error)); return { error: error }; } dispatch((0, redux_batched_actions_1.batchActions)([ { type: action_types_1.ChannelTypes.RECEIVED_CHANNELS, teamId, data: channels, }, { type: action_types_1.ChannelTypes.RECEIVED_MY_CHANNEL_MEMBERS, data: channelMembers, }, ])); const roles = new Set(); for (const member of channelMembers) { for (const role of member.roles.split(' ')) { roles.add(role); } } if (roles.size > 0) { dispatch((0, roles_1.loadRolesIfNeeded)(roles)); } return { data: { channels, channelMembers } }; }; } function fetchAllMyChannelMembers() { return async (dispatch, getState) => { const state = getState(); const { currentUserId } = state.entities.users; let channelMembers = []; try { // The server exposes a streaming API if page is set to -1 // We don't need to paginate through the responses, and thefore pageSize doesn't matter channelMembers = await client_1.Client4.getAllChannelsMembers(currentUserId, -1); } catch (error) { (0, helpers_1.forceLogoutIfNecessary)(error, dispatch, getState); dispatch((0, errors_1.logError)(error)); return { error }; } const roles = new Set(); for (const member of channelMembers) { for (const role of member.roles.split(' ')) { roles.add(role); } } if (roles.size > 0) { dispatch((0, roles_1.loadRolesIfNeeded)(roles)); } dispatch({ type: action_types_1.ChannelTypes.RECEIVED_MY_CHANNEL_MEMBERS, data: channelMembers, currentUserId, }); return { data: channelMembers }; }; } function fetchAllMyTeamsChannels() { return async (dispatch, getState) => { let channels; try { channels = await client_1.Client4.getAllTeamsChannels(); } catch (error) { (0, helpers_1.forceLogoutIfNecessary)(error, dispatch, getState); dispatch((0, errors_1.logError)(error)); return { error }; } dispatch({ type: action_types_1.ChannelTypes.RECEIVED_CHANNELS, data: channels, }); return { data: channels }; }; } function getChannelMembers(channelId, page = 0, perPage = constants_1.General.CHANNELS_CHUNK_SIZE) { return async (dispatch, getState) => { let channelMembers; try { const channelMembersRequest = client_1.Client4.getChannelMembers(channelId, page, perPage); channelMembers = await channelMembersRequest; } catch (error) { (0, helpers_1.forceLogoutIfNecessary)(error, dispatch, getState); dispatch((0, errors_1.logError)(error)); return { error }; } const userIds = channelMembers.map((cm) => cm.user_id); dispatch((0, users_1.getMissingProfilesByIds)(userIds)); dispatch({ type: action_types_1.ChannelTypes.RECEIVED_CHANNEL_MEMBERS, data: channelMembers, }); return { data: channelMembers }; }; } function leaveChannel(channelId) { return async (dispatch, getState) => { const state = getState(); const { currentUserId } = state.entities.users; const { channels, myMembers } = state.entities.channels; const channel = channels[channelId]; const member = myMembers[channelId]; dispatch({ type: action_types_1.ChannelTypes.LEAVE_CHANNEL, data: { id: channelId, user_id: currentUserId, team_id: channel.team_id, type: channel.type, }, }); (async function removeFromChannelWrapper() { try { await client_1.Client4.removeFromChannel(currentUserId, channelId); } catch { dispatch((0, redux_batched_actions_1.batchActions)([ { type: action_types_1.ChannelTypes.RECEIVED_CHANNEL, data: channel, }, { type: action_types_1.ChannelTypes.RECEIVED_MY_CHANNEL_MEMBER, data: member, }, ])); // The category here may not be the one in which the channel was originally located, // much less the order in which it was placed. Treating this as a transient issue // for the user to resolve by refreshing or leaving again. dispatch((0, channel_categories_3.addChannelToInitialCategory)(channel, false)); } }()); return { data: true }; }; } function joinChannel(userId, teamId, channelId, channelName) { return async (dispatch, getState) => { if (!channelId && !channelName) { return { data: null }; } let member; let channel; try { if (channelId) { member = await client_1.Client4.addToChannel(userId, channelId); channel = await client_1.Client4.getChannel(channelId); } else { channel = await client_1.Client4.getChannelByName(teamId, channelName, true); if ((channel.type === constants_1.General.GM_CHANNEL) || (channel.type === constants_1.General.DM_CHANNEL)) { member = await client_1.Client4.getChannelMember(channel.id, userId); } else { member = await client_1.Client4.addToChannel(userId, channel.id); } } } 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.ChannelTypes.RECEIVED_CHANNEL, data: channel, }, { type: action_types_1.ChannelTypes.RECEIVED_MY_CHANNEL_MEMBER, data: member, }, ])); dispatch((0, channel_categories_3.addChannelToInitialCategory)(channel)); if (member) { dispatch((0, roles_1.loadRolesIfNeeded)(member.roles.split(' '))); } return { data: { channel, member } }; }; } function deleteChannel(channelId) { return async (dispatch, getState) => { try { await client_1.Client4.deleteChannel(channelId); } catch (error) { (0, helpers_1.forceLogoutIfNecessary)(error, dispatch, getState); dispatch((0, errors_1.logError)(error)); return { error }; } dispatch({ type: action_types_1.ChannelTypes.DELETE_CHANNEL_SUCCESS, data: { id: channelId } }); return { data: true }; }; } function unarchiveChannel(channelId) { return async (dispatch, getState) => { try { await client_1.Client4.unarchiveChannel(channelId); } catch (error) { (0, helpers_1.forceLogoutIfNecessary)(error, dispatch, getState); dispatch((0, errors_1.logError)(error)); return { error }; } dispatch({ type: action_types_1.ChannelTypes.UNARCHIVED_CHANNEL_SUCCESS, data: { id: channelId } }); return { data: true }; }; } function updateApproximateViewTime(channelId) { return async (dispatch, getState) => { const { currentUserId } = getState().entities.users; const { myPreferences } = getState().entities.preferences; const viewTimePref = myPreferences[`${constants_1.Preferences.CATEGORY_CHANNEL_APPROXIMATE_VIEW_TIME}--${channelId}`]; const viewTime = viewTimePref ? parseInt(viewTimePref.value, 10) : 0; if (viewTime < new Date().getTime() - (3 * 60 * 60 * 1000)) { const preferences = [ { user_id: currentUserId, category: constants_1.Preferences.CATEGORY_CHANNEL_APPROXIMATE_VIEW_TIME, name: channelId, value: new Date().getTime().toString() }, ]; dispatch((0, preferences_1.savePreferences)(currentUserId, preferences)); } return { data: true }; }; } function unsetActiveChannelOnServer() { return async (dispatch, getState) => { try { // The view channel api in the server handles the active channel await client_1.Client4.viewMyChannel(''); } catch (error) { (0, helpers_1.forceLogoutIfNecessary)(error, dispatch, getState); dispatch((0, errors_1.logError)(error)); return { data: false }; } return { data: true }; }; } function readMultipleChannels(channelIds) { return async (dispatch, getState) => { let response; try { response = await client_1.Client4.readMultipleChannels(channelIds); } catch (error) { (0, helpers_1.forceLogoutIfNecessary)(error, dispatch, getState); dispatch((0, errors_1.logError)(error)); return { error }; } dispatch(markMultipleChannelsAsRead(response.last_viewed_at_times)); return { data: true }; }; } function getChannels(teamId, page = 0, perPage = constants_1.General.CHANNELS_CHUNK_SIZE) { return async (dispatch, getState) => { dispatch({ type: action_types_1.ChannelTypes.GET_CHANNELS_REQUEST, data: null }); let channels; try { channels = await client_1.Client4.getChannels(teamId, page, perPage); } catch (error) { (0, helpers_1.forceLogoutIfNecessary)(error, dispatch, getState); dispatch({ type: action_types_1.ChannelTypes.GET_CHANNELS_FAILURE, error }); dispatch((0, errors_1.logError)(error)); return { error }; } dispatch((0, redux_batched_actions_1.batchActions)([ { type: action_types_1.ChannelTypes.RECEIVED_CHANNELS, teamId, data: channels, }, { type: action_types_1.ChannelTypes.GET_CHANNELS_SUCCESS, }, ])); return { data: channels }; }; } function getArchivedChannels(teamId, page = 0, perPage = constants_1.General.CHANNELS_CHUNK_SIZE) { return async (dispatch, getState) => { let channels; try { channels = await client_1.Client4.getArchivedChannels(teamId, page, perPage); } catch (error) { (0, helpers_1.forceLogoutIfNecessary)(error, dispatch, getState); return { error }; } dispatch({ type: action_types_1.ChannelTypes.RECEIVED_CHANNELS, teamId, data: channels, }); return { data: channels }; }; } function getAllChannelsWithCount(page = 0, perPage = constants_1.General.CHANNELS_CHUNK_SIZE, notAssociatedToGroup = '', excludeDefaultChannels = false, includeDeleted = false, excludePolicyConstrained = false, accessControlPolicyEnforced = false, excludeAccessControlPolicyEnforced = false) { return async (dispatch, getState) => { dispatch({ type: action_types_1.ChannelTypes.GET_ALL_CHANNELS_REQUEST, data: null }); let payload; try { payload = await client_1.Client4.getAllChannels(page, perPage, notAssociatedToGroup, excludeDefaultChannels, true, includeDeleted, excludePolicyConstrained, accessControlPolicyEnforced, excludeAccessControlPolicyEnforced); } catch (error) { (0, helpers_1.forceLogoutIfNecessary)(error, dispatch, getState); dispatch({ type: action_types_1.ChannelTypes.GET_ALL_CHANNELS_FAILURE, error }); dispatch((0, errors_1.logError)(error)); return { error }; } dispatch((0, redux_batched_actions_1.batchActions)([ { type: action_types_1.ChannelTypes.RECEIVED_ALL_CHANNELS, data: payload.channels, }, { type: action_types_1.ChannelTypes.GET_ALL_CHANNELS_SUCCESS, }, { type: action_types_1.ChannelTypes.RECEIVED_TOTAL_CHANNEL_COUNT, data: payload.total_count, }, ])); return { data: payload }; }; } function getAllChannels(page = 0, perPage = constants_1.General.CHANNELS_CHUNK_SIZE, notAssociatedToGroup = '', excludeDefaultChannels = false, excludePolicyConstrained = false, excludeAccessControlPolicyEnforced = false, accessControlPolicyEnforced = false) { return async (dispatch, getState) => { dispatch({ type: action_types_1.ChannelTypes.GET_ALL_CHANNELS_REQUEST, data: null }); let channels; try { channels = await client_1.Client4.getAllChannels(page, perPage, notAssociatedToGroup, excludeDefaultChannels, false, false, excludePolicyConstrained, accessControlPolicyEnforced, excludeAccessControlPolicyEnforced); } catch (error) { (0, helpers_1.forceLogoutIfNecessary)(error, dispatch, getState); dispatch({ type: action_types_1.ChannelTypes.GET_ALL_CHANNELS_FAILURE, error }); dispatch((0, errors_1.logError)(error)); return { error }; } dispatch((0, redux_batched_actions_1.batchActions)([ { type: action_types_1.ChannelTypes.RECEIVED_ALL_CHANNELS, data: channels, }, { type: action_types_1.ChannelTypes.GET_ALL_CHANNELS_SUCCESS, }, ])); return { data: channels }; }; } function autocompleteChannels(teamId, term) { return async (dispatch, getState) => { dispatch({ type: action_types_1.ChannelTypes.GET_CHANNELS_REQUEST, data: null }); let channels; try { channels = await client_1.Client4.autocompleteChannels(teamId, term); } catch (error) { (0, helpers_1.forceLogoutIfNecessary)(error, dispatch, getState); dispatch({ type: action_types_1.ChannelTypes.GET_CHANNELS_FAILURE, error }); dispatch((0, errors_1.logError)(error)); return { error }; } dispatch((0, redux_batched_actions_1.batchActions)([ { type: action_types_1.ChannelTypes.RECEIVED_CHANNELS, teamId, data: channels, }, { type: action_types_1.ChannelTypes.GET_CHANNELS_SUCCESS, }, ])); return { data: channels }; }; } function autocompleteChannelsForSearch(teamId, term) { return async (dispatch, getState) => { dispatch({ type: action_types_1.ChannelTypes.GET_CHANNELS_REQUEST, data: null }); let channels; try { channels = await client_1.Client4.autocompleteChannelsForSearch(teamId, term); } catch (error) { (0, helpers_1.forceLogoutIfNecessary)(error, dispatch, getState); dispatch({ type: action_types_1.ChannelTypes.GET_CHANNELS_FAILURE, error }); dispatch((0, errors_1.logError)(error)); return { error }; } dispatch((0, redux_batched_actions_1.batchActions)([ { type: action_types_1.ChannelTypes.RECEIVED_CHANNELS, teamId, data: channels, }, { type: action_types_1.ChannelTypes.GET_CHANNELS_SUCCESS, }, ])); return { data: channels }; }; } function searchChannels(teamId, term) { return async (dispatch, getState) => { dispatch({ type: action_types_1.ChannelTypes.GET_CHANNELS_REQUEST, data: null }); let channels; try { channels = await client_1.Client4.searchChannels(teamId, term); } catch (error) { (0, helpers_1.forceLogoutIfNecessary)(error, dispatch, getState); dispatch({ type: action_types_1.ChannelTypes.GET_CHANNELS_FAILURE, error }); dispatch((0, errors_1.logError)(error)); return { error }; } dispatch((0, redux_batched_actions_1.batchActions)([ { type: action_types_1.ChannelTypes.RECEIVED_CHANNELS, teamId, data: channels, }, { type: action_types_1.ChannelTypes.GET_CHANNELS_SUCCESS, }, ])); return { data: channels }; }; } function searchAllChannels(term, opts = {}) { return async (dispatch, getState) => { dispatch({ type: action_types_1.ChannelTypes.GET_ALL_CHANNELS_REQUEST, data: null }); let response; try { response = await client_1.Client4.searchAllChannels(term, opts); } catch (error) { if (opts.signal?.aborted) { return { error }; } (0, helpers_1.forceLogoutIfNecessary)(error, dispatch, getState); dispatch({ type: action_types_1.ChannelTypes.GET_ALL_CHANNELS_FAILURE, error }); dispatch((0, errors_1.logError)(error)); return { error }; } const channels = 'channels' in response ? response.channels : response; dispatch((0, redux_batched_actions_1.batchActions)([ { type: action_types_1.ChannelTypes.RECEIVED_ALL_CHANNELS, data: channels, }, { type: action_types_1.ChannelTypes.GET_ALL_CHANNELS_SUCCESS, }, ])); return { data: response }; }; } function searchGroupChannels(term) { return (0, helpers_1.bindClientFunc)({ clientFunc: client_1.Client4.searchGroupChannels, params: [term], }); } function getChannelStats(channelId, includeFileCount) { return async (dispatch, getState) => { let stat; try { stat = await client_1.Client4.getChannelStats(channelId, includeFileCount); } catch (error) { (0, helpers_1.forceLogoutIfNecessary)(error, dispatch, getState); dispatch((0, errors_1.logError)(error)); return { error }; } dispatch({ type: action_types_1.ChannelTypes.RECEIVED_CHANNEL_STATS, data: stat, }); return { data: stat }; }; } function getChannelsMemberCount(channelIds) { return async (dispatch, getState) => { let channelsMemberCount; try { channelsMemberCount = await client_1.Client4.getChannelsMemberCount(channelIds); } catch (error) { (0, helpers_1.forceLogoutIfNecessary)(error, dispatch, getState); dispatch((0, errors_1.logError)(error)); return { error }; } dispatch({ type: action_types_1.ChannelTypes.RECEIVED_CHANNELS_MEMBER_COUNT, data: channelsMemberCount, }); return { data: channelsMemberCount }; }; } function addChannelMember(channelId, userId, postRootId = '') { return async (dispatch, getState) => { let member; try { member = await client_1.Client4.addToChannel(userId, channelId, postRootId); } catch (error) { (0, helpers_1.forceLogoutIfNecessary)(error, dispatch, getState); dispatch((0, errors_1.logError)(error)); return { error }; } const membersInChannel = getState().entities.channels.membersInChannel[channelId]; if (!(membersInChannel && userId in membersInChannel)) { dispatch((0, redux_batched_actions_1.batchActions)([ { type: action_types_1.UserTypes.RECEIVED_PROFILE_IN_CHANNEL, data: { id: channelId, user_id: userId }, }, { type: action_types_1.ChannelTypes.RECEIVED_CHANNEL_MEMBER, data: member, }, { type: action_types_1.ChannelTypes.ADD_CHANNEL_MEMBER_SUCCESS, id: channelId, }, ], 'ADD_CHANNEL_MEMBER.BATCH')); } return { data: member }; }; } function addChannelMembers(channelId, userIds, postRootId = '') { const batchSize = 1000; return async (dispatch, getState) => { const channelMembers = []; try { for (let i = 0; i < userIds.length; i += batchSize) { // eslint-disable-next-line no-await-in-loop const cm = await client_1.Client4.addToChannels(userIds.slice(i, i + batchSize), channelId, postRootId); channelMembers.push(...cm); } } catch (error) { (0, helpers_1.forceLogoutIfNecessary)(error, dispatch, getState); dispatch((0, errors_1.logError)(error)); return { error }; } const ids = channelMembers.map((member) => ({ id: member.user_id })); dispatch((0, redux_batched_actions_1.batchActions)([ { type: action_types_1.UserTypes.RECEIVED_PROFILES_IN_CHANNEL, id: channelId, data: ids, }, { type: action_types_1.ChannelTypes.RECEIVED_CHANNEL_MEMBERS, data: channelMembers, }, { type: action_types_1.ChannelTypes.ADD_CHANNEL_MEMBER_SUCCESS, id: channelId, count: channelMembers.length, }, ], 'ADD_CHANNEL_MEMBERS.BATCH')); return { data: channelMembers }; }; } function removeChannelMember(channelId, userId) { return async (dispatch, getState) => { try { await client_1.Client4.removeFromChannel(userId, channelId); } 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.UserTypes.RECEIVED_PROFILE_NOT_IN_CHANNEL, data: { id: channelId, user_id: userId }, }, { type: action_types_1.ChannelTypes.REMOVE_CHANNEL_MEMBER_SUCCESS, id: channelId, }, ], 'REMOVE_CHANNEL_MEMBER.BATCH')); return { data: true }; }; } function markChannelAsRead(channelId, skipUpdateViewTime = false) { return (dispatch, getState) => { if (skipUpdateViewTime) { dispatch(updateApproximateViewTime(channelId)); } dispatch(markChannelAsViewedOnServer(channelId)); const actions = actionsToMarkChannelAsRead(getState, channelId); if (actions.length > 0) { dispatch((0, redux_batched_actions_1.batchActions)(actions)); } return { data: true }; }; } function markMultipleChannelsAsRead(channelTimes) { return (dispatch, getState) => { const actions = []; for (const id of Object.keys(channelTimes)) { actions.push(...actionsToMarkChannelAsRead(getState, id, channelTimes[id])); } if (actions.length > 0) { dispatch((0, redux_batched_actions_1.batchActions)(actions)); } return { data: true }; }; } function markChannelAsViewedOnServer(channelId) { return async (dispatch, getState) => { try { await client_1.Client4.viewMyChannel(channelId); } catch (error) { (0, helpers_1.forceLogoutIfNecessary)(error, dispatch, getState); dispatch((0, errors_1.logError)(error)); return { error }; } // const actions: AnyAction[] = []; // for (const id of Object.keys(response.last_viewed_at_times)) { // actions.push({ // type: ChannelTypes.RECEIVED_LAST_VIEWED_AT, // data: { // channel_id: channelId, // last_viewed_at: response.last_viewed_at_times[id], // }, // }); // } return { data: true }; }; } function actionsToMarkChannelAsRead(getState, channelId, viewedAt = Date.now()) { const state = getState(); const { channels, messageCounts, myMembers } = state.entities.channels; // Update channel member objects to set all mentions and posts as viewed const channel = channels[channelId]; const messageCount = messageCounts[channelId]; // Update team member objects to set mentions and posts in channel as viewed const channelMember = myMembers[channelId]; const actions = []; if (channel && channelMember) { actions.push({ type: action_types_1.ChannelTypes.DECREMENT_UNREAD_MSG_COUNT, data: { teamId: channel.team_id, channelId, amount: messageCount.total - channelMember.msg_count, amountRoot: messageCount.root - channelMember.msg_count_root, }, }); actions.push({ type: action_types_1.ChannelTypes.DECREMENT_UNREAD_MENTION_COUNT, data: { teamId: channel.team_id, channelId, amount: channelMember.mention_count, amountRoot: channelMember.mention_count_root, amountUrgent: channelMember.urgent_mention_count, }, }); actions.push({ type: action_types_1.ChannelTypes.RECEIVED_LAST_VIEWED_AT, data: { channel_id: channelId, last_viewed_at: viewedAt, }, }); } if (channel && (0, channels_2.isManuallyUnread)(getState(), channelId)) { actions.push({ type: action_types_1.ChannelTypes.REMOVE_MANUALLY_UNREAD, data: { channelId }, }); } return actions; } function actionsToMarkChannelAsUnread(getState, teamId, channelId, mentions, fetchedChannelMember = false, isRoot = false, priority = '') { const state = getState(); const { myMembers } = state.entities.channels; const { currentUserId } = state.entities.users; const actions = [{ type: action_types_1.ChannelTypes.INCREMENT_UNREAD_MSG_COUNT, data: { teamId, channelId, amount: 1, amountRoot: isRoot ? 1 : 0, onlyMentions: myMembers[channelId] && myMembers[channelId].notify_props && myMembers[channelId].notify_props.mark_unread === channels_1.MarkUnread.MENTION, fetchedChannelMember, }, }]; if (!fetchedChannelMember) { actions.push({ type: action_types_1.ChannelTypes.INCREMENT_TOTAL_MSG_COUNT, data: { channelId, amountRoot: isRoot ? 1 : 0, amount: 1, }, }); } if (mentions && mentions.indexOf(currentUserId) !== -1) { actions.push({ type: action_types_1.ChannelTypes.INCREMENT_UNREAD_MENTION_COUNT, data: { teamId, channelId, amountRoot: isRoot ? 1 : 0, amount: 1, amountUrgent: priority === 'urgent' ? 1 : 0, fetchedChannelMember, }, }); } return actions; } function getChannelMembersByIds(channelId, userIds) { return (0, helpers_1.bindClientFunc)({ clientFunc: client_1.Client4.getChannelMembersByIds, onSuccess: action_types_1.ChannelTypes.RECEIVED_CHANNEL_MEMBERS, params: [ channelId, userIds, ], }); } function getChannelMember(channelId, userId) { return (0, helpers_1.bindClientFunc)({ clientFunc: client_1.Client4.getChannelMember, onSuccess: action_types_1.ChannelTypes.RECEIVED_CHANNEL_MEMBER, params: [ channelId, userId, ], }); } function getMyChannelMember(channelId) { return (0, helpers_1.bindClientFunc)({ clientFunc: client_1.Client4.getMyChannelMember, onSuccess: action_types_1.ChannelTypes.RECEIVED_MY_CHANNEL_MEMBER, params: [ channelId, ], }); } function loadMyChannelMemberAndRole(channelId) { return async (dispatch) => { const result = await dispatch(getMyChannelMember(channelId)); const roles = result.data?.roles.split(' '); if (roles && roles.length > 0) { dispatch((0, roles_1.loadRolesIfNeeded)(roles)); } return { data: true }; }; } // favoriteChannel moves the provided channel into the current team's Favorites category. function favoriteChannel(channelId) { return async (dispatch, getState) => { const state = getState(); const channel = (0, channels_2.getChannel)(state, channelId); const category = (0, channel_categories_2.getCategoryInTeamByType)(state, channel?.team_id || (0, teams_1.getCurrentTeamId)(state), channel_categories_1.CategoryTypes.FAVORITES); if (!category) { return { data: false }; } return dispatch((0, channel_categories_3.addChannelToCategory)(category.id, channelId)); }; } // unfavoriteChannel moves the provided channel into the current team's Channels/DMs category. function unfavoriteChannel(channelId) { return async (dispatch, getState) => { const state = getState(); const channel = (0, channels_2.getChannel)(state, channelId); if (!channel) { return { data: false }; } const category = (0, channel_categories_2.getCategoryInTeamByType)(state, channel.team_id || (0, teams_1.getCurrentTeamId)(state), channel.type === constants_1.General.DM_CHANNEL || channel.type === constants_1.General.GM_CHANNEL ? channel_categories_1.CategoryTypes.DIRECT_MESSAGES : cha