mattermost-mobile/app/store/middlewares/helpers.js
Mattermost Build edfa0257c3
Automated cherry pick of #4258 (#4268)
* Set rehydration values to true on clean up

* Remove extra line

* Update app/store/middlewares/helpers.js

Co-authored-by: Elias Nahum <nahumhbl@gmail.com>

* Fix setting of views.root.hydrationComplete

Co-authored-by: Miguel Alatzar <this.migbot@gmail.com>
Co-authored-by: Miguel Alatzar <migbot@users.noreply.github.com>
Co-authored-by: Elias Nahum <nahumhbl@gmail.com>
2020-05-05 19:15:38 -04:00

237 lines
7.9 KiB
JavaScript

// Copyright (c) 2015-present Mattermost, Inc. All Rights Reserved.
// See LICENSE.txt for license information.
export function getLastChannelForTeam(payload) {
if (payload?.views?.team?.lastChannelForTeam) {
const lastChannelForTeam = {...payload.views.team.lastChannelForTeam};
const convertLastChannelForTeam = Object.values(lastChannelForTeam).some((value) => !Array.isArray(value));
if (convertLastChannelForTeam) {
Object.keys(lastChannelForTeam).forEach((id) => {
lastChannelForTeam[id] = [lastChannelForTeam[id]];
});
}
return lastChannelForTeam;
}
return {};
}
export function cleanUpState(payload, keepCurrent = false) {
const nextState = Object.assign({}, payload);
const lastTeamId = payload.views?.team?.lastTeamId;
const lastChannelForTeam = getLastChannelForTeam(payload);
const currentChannelId = lastChannelForTeam[lastTeamId] && lastChannelForTeam[lastTeamId].length ? lastChannelForTeam[lastTeamId][0] : '';
const nextEntities = {
posts: {
posts: {},
postsInChannel: {},
postsInThread: {},
reactions: {},
openGraph: payload.entities.posts.openGraph,
selectedPostId: payload.entities.posts.selectedPostId,
currentFocusedPostId: payload.entities.posts.currentFocusedPostId,
},
files: {
files: {},
fileIdsByPostId: {},
},
};
let retentionPeriod = 0;
if (payload.entities.general && payload.entities.general.dataRetentionPolicy &&
payload.entities.general.dataRetentionPolicy.message_deletion_enabled) {
retentionPeriod = payload.entities.general.dataRetentionPolicy.message_retention_cutoff;
}
const postIdsToKeep = [];
// Keep the last 60 posts in each recently viewed channel
nextEntities.posts.postsInChannel = cleanUpPostsInChannel(payload.entities.posts.postsInChannel, lastChannelForTeam, keepCurrent ? currentChannelId : '');
postIdsToKeep.push(...getAllFromPostsInChannel(nextEntities.posts.postsInChannel));
// Keep any posts that appear in search results
let searchResults = [];
let flaggedPosts = [];
if (payload.entities.search) {
if (payload.entities.search.results?.length) {
const {results} = payload.entities.search;
searchResults = results;
postIdsToKeep.push(...results);
}
if (payload.entities.search.flagged?.length) {
const {flagged} = payload.entities.search;
flaggedPosts = flagged;
postIdsToKeep.push(...flagged);
}
}
const nextSearch = {
...payload.entities.search,
results: searchResults,
flagged: flaggedPosts,
};
postIdsToKeep.forEach((postId) => {
const post = payload.entities.posts.posts[postId];
if (post) {
if (retentionPeriod && post.create_at < retentionPeriod) {
// This post has been removed by data retention, so don't keep it
removeFromPostsInChannel(nextEntities.posts.postsInChannel, post.channel_id, postId);
return;
}
// Keep the post
nextEntities.posts.posts[postId] = post;
// And its reactions
const reaction = payload.entities.posts.reactions[postId];
if (reaction) {
nextEntities.posts.reactions[postId] = reaction;
}
// And its files
const fileIds = payload.entities.files.fileIdsByPostId[postId];
if (fileIds) {
nextEntities.files.fileIdsByPostId[postId] = fileIds;
fileIds.forEach((fileId) => {
nextEntities.files.files[fileId] = payload.entities.files.files[fileId];
});
}
// And its comments
const postsInThread = payload.entities.posts.postsInThread[postId];
if (postsInThread) {
nextEntities.posts.postsInThread[postId] = postsInThread;
}
}
});
// Remove any pending posts that haven't failed
if (payload.entities.posts && payload.entities.posts.pendingPostIds && payload.entities.posts.pendingPostIds.length) {
const nextPendingPostIds = [...payload.entities.posts.pendingPostIds];
payload.entities.posts.pendingPostIds.forEach((id) => {
const posts = nextEntities.posts.posts;
const post = posts[id];
if (post && !post.failed) {
Reflect.deleteProperty(posts, id);
removeFromPostsInChannel(nextEntities.posts.postsInChannel, post.channel_id, id);
removePendingPost(nextPendingPostIds, id);
} else if (!post) {
removePendingPost(nextPendingPostIds, id);
}
});
nextEntities.posts.pendingPostIds = nextPendingPostIds;
}
nextState.views = {
...nextState.views,
root: {
...nextState.views?.root,
// eslint-disable-next-line no-underscore-dangle
hydrationComplete: nextState.views?.root?.hydrationComplete || !nextState._persist,
},
};
// eslint-disable-next-line no-underscore-dangle
nextState._persist = {
rehydrated: true,
};
nextState.entities = {
...nextState.entities,
...nextEntities,
search: nextSearch,
};
return nextState;
}
// cleanUpPostsInChannel returns a copy of postsInChannel where only the most recent posts in each channel are kept
export function cleanUpPostsInChannel(postsInChannel, lastChannelForTeam, currentChannelId, recentPostCount = 60) {
const nextPostsInChannel = {};
for (const channelIds of Object.values(lastChannelForTeam)) {
for (const channelId of channelIds) {
if (nextPostsInChannel[channelId]) {
// This is a DM or GM channel that we've already seen on another team
continue;
}
const postsForChannel = postsInChannel[channelId];
if (!postsForChannel) {
// We don't have anything to keep for this channel
continue;
}
let nextPostsForChannel;
if (channelId === currentChannelId) {
// Keep all of the posts for this channel
nextPostsForChannel = postsForChannel;
} else {
// Only keep the most recent posts for this channel
const recentBlock = postsForChannel.find((block) => block.recent);
if (!recentBlock) {
// We don't have recent posts for this channel
continue;
}
nextPostsForChannel = [{
...recentBlock,
order: recentBlock.order.slice(0, recentPostCount),
}];
}
nextPostsInChannel[channelId] = nextPostsForChannel;
}
}
return nextPostsInChannel;
}
// getAllFromPostsInChannel returns an array of all post IDs found in postsInChannel
export function getAllFromPostsInChannel(postsInChannel) {
const postIds = [];
for (const postsForChannel of Object.values(postsInChannel)) {
for (const block of postsForChannel) {
postIds.push(...block.order);
}
}
return postIds;
}
function removeFromPostsInChannel(postsInChannel, channelId, postId) {
const postsForChannel = postsInChannel[channelId];
if (!postsForChannel) {
return;
}
// Since this has already gone through cleanUpPostsInChannel, we know that there can only be one block to look at
const index = postsForChannel[0].order.indexOf(postId);
if (index !== -1) {
postsForChannel[0].order.splice(index, 1);
}
}
function removePendingPost(pendingPostIds, id) {
const pendingIndex = pendingPostIds.indexOf(id);
if (pendingIndex !== -1) {
pendingPostIds.splice(pendingIndex, 1);
}
}