UNPKG

mattermost-redux

Version:

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

405 lines (404 loc) 18.3 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.setCategoryCollapsed = setCategoryCollapsed; exports.setCategorySorting = setCategorySorting; exports.patchCategory = patchCategory; exports.setCategoryMuted = setCategoryMuted; exports.fetchMyCategories = fetchMyCategories; exports.addChannelToInitialCategory = addChannelToInitialCategory; exports.addChannelToCategory = addChannelToCategory; exports.moveChannelToCategory = moveChannelToCategory; exports.moveChannelsToCategory = moveChannelsToCategory; exports.moveCategory = moveCategory; exports.receivedCategoryOrder = receivedCategoryOrder; exports.createCategory = createCategory; exports.renameCategory = renameCategory; exports.deleteCategory = deleteCategory; const redux_batched_actions_1 = require("redux-batched-actions"); const channel_categories_1 = require("@mattermost/types/channel_categories"); const action_types_1 = require("mattermost-redux/action_types"); const errors_1 = require("mattermost-redux/actions/errors"); const helpers_1 = require("mattermost-redux/actions/helpers"); const client_1 = require("mattermost-redux/client"); const channel_categories_2 = require("mattermost-redux/constants/channel_categories"); const channel_categories_3 = require("mattermost-redux/selectors/entities/channel_categories"); const users_1 = require("mattermost-redux/selectors/entities/users"); const array_utils_1 = require("mattermost-redux/utils/array_utils"); const constants_1 = require("../constants"); function setCategoryCollapsed(categoryId, collapsed) { return patchCategory(categoryId, { collapsed, }); } function setCategorySorting(categoryId, sorting) { return patchCategory(categoryId, { sorting, }); } function patchCategory(categoryId, patch) { return async (dispatch, getState) => { const state = getState(); const currentUserId = (0, users_1.getCurrentUserId)(state); const category = (0, channel_categories_3.getCategory)(state, categoryId); const patchedCategory = { ...category, ...patch, }; dispatch({ type: action_types_1.ChannelCategoryTypes.RECEIVED_CATEGORY, data: patchedCategory, }); try { await client_1.Client4.updateChannelCategory(currentUserId, category.team_id, patchedCategory); } catch (error) { dispatch({ type: action_types_1.ChannelCategoryTypes.RECEIVED_CATEGORY, data: category, }); (0, helpers_1.forceLogoutIfNecessary)(error, dispatch, getState); dispatch((0, errors_1.logError)(error)); return { error }; } return { data: patchedCategory }; }; } function setCategoryMuted(categoryId, muted) { return async (dispatch, getState) => { const state = getState(); const category = (0, channel_categories_3.getCategory)(state, categoryId); const result = await dispatch(updateCategory({ ...category, muted, })); if ('error' in result) { return result; } const updated = result.data; dispatch((0, redux_batched_actions_1.batchActions)([ { type: action_types_1.ChannelCategoryTypes.RECEIVED_CATEGORY, data: updated, }, ...(updated.channel_ids.map((channelId) => ({ type: action_types_1.ChannelTypes.SET_CHANNEL_MUTED, data: { channelId, muted, }, }))), ])); return { data: true }; }; } function updateCategory(category) { return async (dispatch, getState) => { const state = getState(); const currentUserId = (0, users_1.getCurrentUserId)(state); let updatedCategory; try { updatedCategory = await client_1.Client4.updateChannelCategory(currentUserId, category.team_id, category); } catch (error) { (0, helpers_1.forceLogoutIfNecessary)(error, dispatch, getState); dispatch((0, errors_1.logError)(error)); return { error }; } // The updated category will be added to the state after receiving the corresponding websocket event. return { data: updatedCategory }; }; } function fetchMyCategories(teamId, isWebSocket) { return async (dispatch, getState) => { const currentUserId = (0, users_1.getCurrentUserId)(getState()); let data; try { data = await client_1.Client4.getChannelCategories(currentUserId, teamId); } catch (error) { (0, helpers_1.forceLogoutIfNecessary)(error, dispatch, getState); dispatch((0, errors_1.logError)(error)); return { error }; } return dispatch((0, redux_batched_actions_1.batchActions)([ { type: action_types_1.ChannelCategoryTypes.RECEIVED_CATEGORIES, data: data.categories, isWebSocket, }, { type: action_types_1.ChannelCategoryTypes.RECEIVED_CATEGORY_ORDER, data: { teamId, order: data.order, }, }, ])); }; } // addChannelToInitialCategory returns an action that can be dispatched to add a newly-joined or newly-created channel // to its either the Channels or Direct Messages category based on the type of channel. New DM and GM channels are // added to the Direct Messages category on each team. // // Unless setOnServer is true, this only affects the categories on this client. If it is set to true, this updates // categories on the server too. function addChannelToInitialCategory(channel, setOnServer = false) { return async (dispatch, getState) => { const state = getState(); const categories = Object.values((0, channel_categories_3.getAllCategoriesByIds)(state)); if (channel.type === constants_1.General.DM_CHANNEL || channel.type === constants_1.General.GM_CHANNEL) { const allDmCategories = categories.filter((category) => category.type === channel_categories_2.CategoryTypes.DIRECT_MESSAGES); // Get all the categories in which channel exists const channelInCategories = categories.filter((category) => { return category.channel_ids.findIndex((channelId) => channelId === channel.id) !== -1; }); // Skip DM categories where channel already exists in a different category const dmCategories = allDmCategories.filter((dmCategory) => { return channelInCategories.findIndex((category) => dmCategory.team_id === category.team_id) === -1; }); return dispatch({ type: action_types_1.ChannelCategoryTypes.RECEIVED_CATEGORIES, data: dmCategories.map((category) => ({ ...category, channel_ids: (0, array_utils_1.insertWithoutDuplicates)(category.channel_ids, channel.id, 0), })), }); } // Add the new channel to the Channels category on the channel's team if (categories.some((category) => category.channel_ids.some((channelId) => channelId === channel.id))) { return { data: false }; } const channelsCategory = (0, channel_categories_3.getCategoryInTeamByType)(state, channel.team_id, channel_categories_2.CategoryTypes.CHANNELS); if (!channelsCategory) { // No categories were found for this team, so the categories for this team haven't been loaded yet. // The channel will have been added to the category by the server, so we'll get it once the categories // are actually loaded. return { data: false }; } if (setOnServer) { return dispatch(addChannelToCategory(channelsCategory.id, channel.id)); } return dispatch({ type: action_types_1.ChannelCategoryTypes.RECEIVED_CATEGORY, data: { ...channelsCategory, channel_ids: (0, array_utils_1.insertWithoutDuplicates)(channelsCategory.channel_ids, channel.id, 0), }, }); }; } // addChannelToCategory returns an action that can be dispatched to add a channel to a given category without specifying // its order. The channel will be removed from its previous category (if any) on the given category's team and it will be // placed first in its new category. function addChannelToCategory(categoryId, channelId) { return moveChannelToCategory(categoryId, channelId, 0, false); } // moveChannelToCategory returns an action that moves a channel into a category and puts it at the given index at the // category. The channel will also be removed from its previous category (if any) on that category's team. The category's // order will also be set to manual by default. function moveChannelToCategory(categoryId, channelId, newIndex, setManualSorting = true) { return async (dispatch, getState) => { const state = getState(); const targetCategory = (0, channel_categories_3.getCategory)(state, categoryId); const currentUserId = (0, users_1.getCurrentUserId)(state); // The default sorting needs to behave like alphabetical sorting until the point that the user rearranges their // channels at which point, it becomes manual. Other than that, we never change the sorting method automatically. let sorting = targetCategory.sorting; if (setManualSorting && targetCategory.type !== channel_categories_2.CategoryTypes.DIRECT_MESSAGES && targetCategory.sorting === channel_categories_1.CategorySorting.Default) { sorting = channel_categories_1.CategorySorting.Manual; } // Add the channel to the new category const categories = [{ ...targetCategory, sorting, channel_ids: (0, array_utils_1.insertWithoutDuplicates)(targetCategory.channel_ids, channelId, newIndex), }]; // And remove it from the old category const sourceCategory = (0, channel_categories_3.getCategoryInTeamWithChannel)(getState(), targetCategory.team_id, channelId); if (sourceCategory && sourceCategory.id !== targetCategory.id) { categories.push({ ...sourceCategory, channel_ids: (0, array_utils_1.removeItem)(sourceCategory.channel_ids, channelId), }); } const result = dispatch({ type: action_types_1.ChannelCategoryTypes.RECEIVED_CATEGORIES, data: categories, }); try { await client_1.Client4.updateChannelCategories(currentUserId, targetCategory.team_id, categories); } catch (error) { (0, helpers_1.forceLogoutIfNecessary)(error, dispatch, getState); dispatch((0, errors_1.logError)(error)); const originalCategories = [targetCategory]; if (sourceCategory && sourceCategory.id !== targetCategory.id) { originalCategories.push(sourceCategory); } dispatch({ type: action_types_1.ChannelCategoryTypes.RECEIVED_CATEGORIES, data: originalCategories, }); return { error }; } return result; }; } function moveChannelsToCategory(categoryId, channelIds, newIndex, setManualSorting = true) { return async (dispatch, getState) => { const state = getState(); const targetCategory = (0, channel_categories_3.getCategory)(state, categoryId); const currentUserId = (0, users_1.getCurrentUserId)(state); // The default sorting needs to behave like alphabetical sorting until the point that the user rearranges their // channels at which point, it becomes manual. Other than that, we never change the sorting method automatically. let sorting = targetCategory.sorting; if (setManualSorting && targetCategory.type !== channel_categories_2.CategoryTypes.DIRECT_MESSAGES && targetCategory.sorting === channel_categories_1.CategorySorting.Default) { sorting = channel_categories_1.CategorySorting.Manual; } // Add the channels to the new category let categories = { [targetCategory.id]: { ...targetCategory, sorting, channel_ids: (0, array_utils_1.insertMultipleWithoutDuplicates)(targetCategory.channel_ids, channelIds, newIndex), }, }; // Needed if we have to revert categories and for checking for favourites let unmodifiedCategories = { [targetCategory.id]: targetCategory }; let sourceCategories = {}; // And remove it from the old categories channelIds.forEach((channelId) => { const sourceCategory = (0, channel_categories_3.getCategoryInTeamWithChannel)(getState(), targetCategory.team_id, channelId); if (sourceCategory && sourceCategory.id !== targetCategory.id) { unmodifiedCategories = { ...unmodifiedCategories, [sourceCategory.id]: sourceCategory, }; sourceCategories = { ...sourceCategories, [channelId]: sourceCategory.id }; categories = { ...categories, [sourceCategory.id]: { ...(categories[sourceCategory.id] || sourceCategory), channel_ids: (0, array_utils_1.removeItem)((categories[sourceCategory.id] || sourceCategory).channel_ids, channelId), }, }; } }); const categoriesArray = Object.values(categories).reduce((allCategories, category) => { allCategories.push(category); return allCategories; }, []); const result = dispatch({ type: action_types_1.ChannelCategoryTypes.RECEIVED_CATEGORIES, data: categoriesArray, }); try { await client_1.Client4.updateChannelCategories(currentUserId, targetCategory.team_id, categoriesArray); } catch (error) { (0, helpers_1.forceLogoutIfNecessary)(error, dispatch, getState); dispatch((0, errors_1.logError)(error)); const originalCategories = Object.values(unmodifiedCategories).reduce((allCategories, category) => { allCategories.push(category); return allCategories; }, []); dispatch({ type: action_types_1.ChannelCategoryTypes.RECEIVED_CATEGORIES, data: originalCategories, }); return { error }; } return result; }; } function moveCategory(teamId, categoryId, newIndex) { return async (dispatch, getState) => { const state = getState(); const order = (0, channel_categories_3.getCategoryIdsForTeam)(state, teamId); const currentUserId = (0, users_1.getCurrentUserId)(state); const newOrder = (0, array_utils_1.insertWithoutDuplicates)(order, categoryId, newIndex); // Optimistically update the category order const result = dispatch({ type: action_types_1.ChannelCategoryTypes.RECEIVED_CATEGORY_ORDER, data: { teamId, order: newOrder, }, }); try { await client_1.Client4.updateChannelCategoryOrder(currentUserId, teamId, newOrder); } catch (error) { (0, helpers_1.forceLogoutIfNecessary)(error, dispatch, getState); dispatch((0, errors_1.logError)(error)); // Restore original order dispatch({ type: action_types_1.ChannelCategoryTypes.RECEIVED_CATEGORY_ORDER, data: { teamId, order, }, }); return { error }; } return result; }; } function receivedCategoryOrder(teamId, order) { return { type: action_types_1.ChannelCategoryTypes.RECEIVED_CATEGORY_ORDER, data: { teamId, order, }, }; } function createCategory(teamId, displayName, channelIds = []) { return async (dispatch, getState) => { const currentUserId = (0, users_1.getCurrentUserId)(getState()); let newCategory; try { newCategory = await client_1.Client4.createChannelCategory(currentUserId, teamId, { team_id: teamId, user_id: currentUserId, display_name: displayName, channel_ids: channelIds, }); } catch (error) { (0, helpers_1.forceLogoutIfNecessary)(error, dispatch, getState); dispatch((0, errors_1.logError)(error)); return { error }; } // The new category will be added to the state after receiving the corresponding websocket event. return { data: newCategory }; }; } function renameCategory(categoryId, displayName) { return patchCategory(categoryId, { display_name: displayName, }); } function deleteCategory(categoryId) { return async (dispatch, getState) => { const state = getState(); const category = (0, channel_categories_3.getCategory)(state, categoryId); const currentUserId = (0, users_1.getCurrentUserId)(state); try { await client_1.Client4.deleteChannelCategory(currentUserId, category.team_id, category.id); } catch (error) { (0, helpers_1.forceLogoutIfNecessary)(error, dispatch, getState); dispatch((0, errors_1.logError)(error)); return { error }; } // The category will be deleted from the state after receiving the corresponding websocket event. return { data: true }; }; }