mattermost-redux
Version:
Common code (API client, Redux stores, logic, utility functions) for building a Mattermost client
405 lines (404 loc) • 18.3 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.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 };
};
}