UNPKG

mattermost-redux

Version:

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

252 lines (251 loc) 9.2 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.unreadThreadsInTeamReducer = exports.threadsInTeamReducer = void 0; exports.handleReceivedThread = handleReceivedThread; exports.handleFollowChanged = handleFollowChanged; const action_types_1 = require("mattermost-redux/action_types"); // return true only if it's 'newer' than other threads // older threads will be added by scrolling so no need to manually add. // furthermore manually adding older thread will BREAK pagination function shouldAddThreadId(ids, thread, threads) { return ids.some((id) => { const t = threads[id]; return thread.last_reply_at > t.last_reply_at; }); } function handlePostRemoved(state, action) { const post = action.data; if (post.root_id) { return state; } const teams = Object.keys(state). filter((id) => state[id].indexOf(post.id) !== -1); if (!teams?.length) { return state; } const teamState = {}; for (let i = 0; i < teams.length; i++) { const teamId = teams[i]; const index = state[teamId].indexOf(post.id); teamState[teamId] = [ ...state[teamId].slice(0, index), ...state[teamId].slice(index + 1), ]; } return { ...state, ...teamState, }; } // adds thread to all teams in state function handleAllTeamsReceivedThread(state, thread, teamId, extra) { const teamIds = Object.keys(state); let newState = { ...state }; for (const teamId of teamIds) { newState = handleSingleTeamReceivedThread(newState, thread, teamId, extra); } return newState; } // adds thread to single team function handleSingleTeamReceivedThread(state, thread, teamId, extra) { const nextSet = new Set(state[teamId] || []); // thread exists in state if (nextSet.has(thread.id)) { return state; } // check if thread is newer than any of the existing threads const shouldAdd = shouldAddThreadId([...nextSet], thread, extra.threads); if (shouldAdd) { nextSet.add(thread.id); return { ...state, [teamId]: [...nextSet], }; } return state; } function handleReceivedThread(state, action, extra) { const { thread, team_id: teamId } = action.data; if (!teamId) { return handleAllTeamsReceivedThread(state, thread, teamId, extra); } return handleSingleTeamReceivedThread(state, thread, teamId, extra); } // add the thread only if it's 'newer' than other threads // older threads will be added by scrolling so no need to manually add. // furthermore manually adding older thread will BREAK pagination function handleFollowChanged(state, action, extra) { const { id, team_id: teamId, following } = action.data; const nextSet = new Set(state[teamId] || []); const thread = extra.threads[id]; if (!thread) { return state; } // thread exists in state if (nextSet.has(id)) { // remove it if we unfollowed if (!following) { nextSet.delete(id); return { ...state, [teamId]: [...nextSet], }; } return state; } // check if thread is newer than any of the existing threads const shouldAdd = shouldAddThreadId([...nextSet], thread, extra.threads); if (shouldAdd && following) { nextSet.add(thread.id); return { ...state, [teamId]: [...nextSet], }; } return state; } function handleReceiveThreads(state, action) { const nextSet = new Set(state[action.data.team_id] || []); action.data.threads.forEach((thread) => { nextSet.add(thread.id); }); return { ...state, [action.data.team_id]: [...nextSet], }; } function handleLeaveChannel(state, action, extra) { if (!extra.threadsToDelete || extra.threadsToDelete.length === 0) { return state; } const teamId = action.data.team_id; let threadDeleted = false; // Remove entries for any thread in the channel const nextState = { ...state }; for (const thread of extra.threadsToDelete) { if (nextState[teamId]) { const index = nextState[teamId].indexOf(thread.id); if (index !== -1) { nextState[teamId] = [...nextState[teamId].slice(0, index), ...nextState[teamId].slice(index + 1)]; threadDeleted = true; } } } if (!threadDeleted) { // Nothing was actually removed return state; } return nextState; } function handleLeaveTeam(state, action) { const team = action.data; if (!state[team.id]) { return state; } const nextState = { ...state }; Reflect.deleteProperty(nextState, team.id); return nextState; } function handleSingleTeamThreadRead(state, action, teamId, extra) { const { id, newUnreadMentions, newUnreadReplies, } = action.data; const team = state[teamId] || []; const index = team.indexOf(id); // the thread is not in the unread list if (index === -1) { const thread = extra.threads[id]; // the thread is unread if (thread && (newUnreadReplies > 0 || newUnreadMentions > 0)) { // if it's newer add it, we don't care about ordering here since we order on the selector if (shouldAddThreadId(team, thread, extra.threads)) { return { ...state, [teamId]: [ ...team, id, ], }; } } // do nothing when the thread is read return state; } // do nothing when the thread exists and it's unread if (newUnreadReplies > 0 || newUnreadMentions > 0) { return state; } // if the thread is read remove it return { ...state, [teamId]: [ ...team.slice(0, index), ...team.slice(index + 1), ], }; } const threadsInTeamReducer = (state = {}, action, extra) => { switch (action.type) { case action_types_1.ThreadTypes.RECEIVED_THREAD: return handleReceivedThread(state, action, extra); case action_types_1.PostTypes.POST_DELETED: case action_types_1.PostTypes.POST_REMOVED: return handlePostRemoved(state, action); case action_types_1.ThreadTypes.RECEIVED_THREADS: return handleReceiveThreads(state, action); case action_types_1.TeamTypes.LEAVE_TEAM: return handleLeaveTeam(state, action); case action_types_1.UserTypes.LOGOUT_SUCCESS: return {}; case action_types_1.ChannelTypes.RECEIVED_CHANNEL_DELETED: case action_types_1.ChannelTypes.LEAVE_CHANNEL: return handleLeaveChannel(state, action, extra); } return state; }; exports.threadsInTeamReducer = threadsInTeamReducer; const unreadThreadsInTeamReducer = (state = {}, action, extra) => { switch (action.type) { case action_types_1.ThreadTypes.READ_CHANGED_THREAD: { const { teamId } = action.data; if (teamId === '') { const teamIds = Object.keys(state); let newState = { ...state }; for (const teamId of teamIds) { newState = handleSingleTeamThreadRead(newState, action, teamId, extra); } return newState; } return handleSingleTeamThreadRead(state, action, teamId, extra); } case action_types_1.ThreadTypes.RECEIVED_THREAD: if (action.data.thread.unread_replies > 0 || action.data.thread.unread_mentions > 0) { return handleReceivedThread(state, action, extra); } return state; case action_types_1.ThreadTypes.RECEIVED_THREADS: return handleReceiveThreads(state, { ...action, data: { ...action.data, threads: action.data.threads.filter((thread) => thread.unread_replies > 0 || thread.unread_mentions > 0), }, }); case action_types_1.PostTypes.POST_DELETED: case action_types_1.PostTypes.POST_REMOVED: return handlePostRemoved(state, action); case action_types_1.ThreadTypes.RECEIVED_UNREAD_THREADS: return handleReceiveThreads(state, action); case action_types_1.TeamTypes.LEAVE_TEAM: return handleLeaveTeam(state, action); case action_types_1.UserTypes.LOGOUT_SUCCESS: return {}; case action_types_1.ChannelTypes.RECEIVED_CHANNEL_DELETED: case action_types_1.ChannelTypes.LEAVE_CHANNEL: return handleLeaveChannel(state, action, extra); case action_types_1.ThreadTypes.FOLLOW_CHANGED_THREAD: return handleFollowChanged(state, action, extra); } return state; }; exports.unreadThreadsInTeamReducer = unreadThreadsInTeamReducer;