mattermost-redux
Version:
Common code (API client, Redux stores, logic, utility functions) for building a Mattermost client
314 lines (273 loc) • 8.84 kB
text/typescript
// Copyright (c) 2015-present Mattermost, Inc. All Rights Reserved.
// See LICENSE.txt for license information.
import {combineReducers} from 'redux';
import {PostTypes, PreferenceTypes, SearchTypes, UserTypes} from 'action_types';
import {Preferences} from '../../constants';
import {PreferenceType} from 'types/preferences';
import {GenericAction} from 'types/actions';
import {Post} from 'types/posts';
import {Dictionary} from 'types/utilities';
import {Search} from 'types/search';
function results(state: string[] = [], action: GenericAction) {
switch (action.type) {
case SearchTypes.RECEIVED_SEARCH_POSTS: {
if (action.isGettingMore) {
return [...new Set(state.concat(action.data.order))];
}
return action.data.order;
}
case PostTypes.POST_REMOVED: {
const postId = action.data ? action.data.id : null;
const index = state.indexOf(postId);
if (index !== -1) {
const newState = [...state];
newState.splice(index, 1);
return newState;
}
return state;
}
case SearchTypes.REMOVE_SEARCH_POSTS:
case UserTypes.LOGOUT_SUCCESS:
return [];
default:
return state;
}
}
function matches(state: Dictionary<string[]> = {}, action: GenericAction) {
switch (action.type) {
case SearchTypes.RECEIVED_SEARCH_POSTS:
if (action.isGettingMore) {
return Object.assign({}, state, action.data.matches);
}
return action.data.matches || {};
case PostTypes.POST_REMOVED: {
if (!state[action.data.id]) {
return state;
}
const newState = {...state};
Reflect.deleteProperty(newState, action.data.id);
return newState;
}
case SearchTypes.REMOVE_SEARCH_POSTS:
case UserTypes.LOGOUT_SUCCESS:
return [];
default:
return state;
}
}
function flagged(state: string[] = [], action: GenericAction) {
switch (action.type) {
case SearchTypes.RECEIVED_SEARCH_FLAGGED_POSTS: {
return action.data.order;
}
case PostTypes.POST_REMOVED: {
const postId = action.data ? action.data.id : null;
const index = state.indexOf(postId);
if (index !== -1) {
const newState = [...state];
newState.splice(index, 1);
return newState;
}
return state;
}
case PreferenceTypes.RECEIVED_PREFERENCES: {
if (action.data) {
const nextState = [...state];
let hasNewFlaggedPosts = false;
action.data.forEach((pref: PreferenceType) => {
if (pref.category === Preferences.CATEGORY_FLAGGED_POST) {
const exists = nextState.find((p) => p === pref.name);
if (!exists) {
hasNewFlaggedPosts = true;
nextState.unshift(pref.name);
}
}
});
return hasNewFlaggedPosts ? nextState : state;
}
return state;
}
case PreferenceTypes.DELETED_PREFERENCES: {
if (action.data) {
const nextState = [...state];
let flaggedPostsRemoved = false;
action.data.forEach((pref: PreferenceType) => {
if (pref.category === Preferences.CATEGORY_FLAGGED_POST) {
const index = state.indexOf(pref.name);
if (index !== -1) {
flaggedPostsRemoved = true;
nextState.splice(index, 1);
}
}
});
return flaggedPostsRemoved ? nextState : state;
}
return state;
}
case SearchTypes.REMOVE_SEARCH_POSTS:
case UserTypes.LOGOUT_SUCCESS:
return [];
default:
return state;
}
}
function removePinnedPost(state: Dictionary<string[]>, post: Post) {
if (post && state[post.channel_id]) {
const postId = post.id;
const channelId = post.channel_id;
const pinnedPosts = [...state[channelId]];
const index = pinnedPosts.indexOf(postId);
if (index !== -1) {
pinnedPosts.splice(index, 1);
return {
...state,
[channelId]: pinnedPosts,
};
}
}
return state;
}
function pinned(state: Dictionary<string[]> = {}, action: GenericAction) {
switch (action.type) {
case SearchTypes.RECEIVED_SEARCH_PINNED_POSTS: {
const {channelId, pinned: posts} = action.data;
return {
...state,
[channelId]: posts.order.reverse(),
};
}
case PostTypes.POST_DELETED:
case PostTypes.POST_REMOVED: {
return removePinnedPost(state, action.data);
}
case PostTypes.RECEIVED_POST: {
const post = action.data;
if (post && post.is_pinned) {
const channelId = post.channel_id;
let pinnedPosts: string[] = [];
if (state[channelId]) {
pinnedPosts = [...state[channelId]];
}
if (!pinnedPosts.includes(post.id)) {
pinnedPosts.unshift(post.id);
}
return {
...state,
[channelId]: pinnedPosts,
};
}
return removePinnedPost(state, action.data);
}
case SearchTypes.REMOVE_SEARCH_PINNED_POSTS: {
const {channelId} = action.data;
const nextState = {...state};
if (nextState[channelId]) {
Reflect.deleteProperty(nextState, channelId);
return nextState;
}
return state;
}
case UserTypes.LOGOUT_SUCCESS:
return [];
default:
return state;
}
}
function recent(state: Dictionary<Search[]> = {}, action: GenericAction) {
const {data, type} = action;
switch (type) {
case SearchTypes.RECEIVED_SEARCH_TERM: {
const nextState = {...state};
const {teamId, params} = data;
const {terms, isOrSearch} = params || {};
const team = [...(nextState[teamId] || [])];
const index = team.findIndex((r) => r.terms === terms);
if (index === -1) {
team.push({terms, isOrSearch});
} else {
team[index] = {terms, isOrSearch};
}
return {
...nextState,
[teamId]: team,
};
}
case SearchTypes.REMOVE_SEARCH_TERM: {
const nextState = {...state};
const {teamId, terms} = data;
const team = [...(nextState[teamId] || [])];
const index = team.findIndex((r) => r.terms === terms);
if (index !== -1) {
team.splice(index, 1);
return {
...nextState,
[teamId]: team,
};
}
return nextState;
}
case UserTypes.LOGOUT_SUCCESS:
return {};
default:
return state;
}
}
function current(state: any = {}, action: GenericAction) {
const {data, type} = action;
switch (type) {
case SearchTypes.RECEIVED_SEARCH_TERM: {
const nextState = {...state};
const {teamId, params, isEnd} = data;
return {
...nextState,
[teamId]: {
params,
isEnd,
},
};
}
case UserTypes.LOGOUT_SUCCESS:
return {};
default:
return state;
}
}
function isSearchingTerm(state = false, action: GenericAction) {
switch (action.type) {
case SearchTypes.SEARCH_POSTS_REQUEST:
return !action.isGettingMore;
case SearchTypes.SEARCH_POSTS_SUCCESS:
return false;
default:
return state;
}
}
function isSearchGettingMore(state = false, action: GenericAction) {
switch (action.type) {
case SearchTypes.SEARCH_POSTS_REQUEST:
return action.isGettingMore;
case SearchTypes.SEARCH_POSTS_SUCCESS:
return false;
default:
return state;
}
}
export default combineReducers({
// An ordered array with posts ids of flagged posts
flagged,
// An Object where every key is a channel id mapping to an ordered array with posts ids of pinned posts
pinned,
// An ordered array with posts ids from the search results
results,
// Object where every key is a post id mapping to an array of matched words in that post
matches,
// Object where every key is a team composed with
// an object where the key is the term and the value indicates is "or" search
recent,
// Object holding the current searches for every team
current,
// Boolean true if we are are searching initally
isSearchingTerm,
// Boolean true if we are getting more search results
isSearchGettingMore,
});