From 9db040bcf76a7ee75febbdb6a50c41c5fe5a00ca Mon Sep 17 00:00:00 2001 From: Elias Nahum Date: Sat, 27 Oct 2018 00:50:19 -0300 Subject: [PATCH] Retry fetching teams if no teams are found (#2288) * Retry fetching teams if no teams are found * Remove flow --- app/actions/views/select_team.js | 39 ++++++++++++++++++++------------ app/store/middleware.js | 32 +++++++++++++------------- app/utils/teams.js | 16 +++++++++++++ app/utils/teams.test.js | 38 +++++++++++++++++++++++++++++++ 4 files changed, 95 insertions(+), 30 deletions(-) create mode 100644 app/utils/teams.js create mode 100644 app/utils/teams.test.js diff --git a/app/actions/views/select_team.js b/app/actions/views/select_team.js index 23ba2e8d1..874d6c162 100644 --- a/app/actions/views/select_team.js +++ b/app/actions/views/select_team.js @@ -3,16 +3,18 @@ import {batchActions} from 'redux-batched-actions'; -import {markChannelAsRead, markChannelAsViewed} from 'mattermost-redux/actions/channels'; import {ChannelTypes, TeamTypes} from 'mattermost-redux/action_types'; -import EventEmitter from 'mattermost-redux/utils/event_emitter'; -import {getCurrentChannelId} from 'mattermost-redux/selectors/entities/channels'; +import {markChannelAsRead, markChannelAsViewed} from 'mattermost-redux/actions/channels'; +import {getMyTeams} from 'mattermost-redux/actions/teams'; import {RequestStatus} from 'mattermost-redux/constants'; +import {getCurrentChannelId} from 'mattermost-redux/selectors/entities/channels'; +import {getConfig} from 'mattermost-redux/selectors/entities/general'; +import EventEmitter from 'mattermost-redux/utils/event_emitter'; import {NavigationTypes} from 'app/constants'; +import {selectFirstAvailableTeam} from 'app/utils/teams'; import {setChannelDisplayName} from './channel'; -import {getConfig} from 'mattermost-redux/selectors/entities/general'; export function handleTeamChange(teamId, selectChannel = true) { return async (dispatch, getState) => { @@ -46,21 +48,30 @@ export function selectDefaultTeam() { const {teams: allTeams, myMembers} = state.entities.teams; const teams = Object.keys(myMembers).map((key) => allTeams[key]); - let defaultTeam; - if (ExperimentalPrimaryTeam) { - defaultTeam = teams.find((t) => t.name === ExperimentalPrimaryTeam.toLowerCase()); - } - - if (!defaultTeam) { - defaultTeam = Object.values(teams).sort((a, b) => a.display_name.localeCompare(b.display_name))[0]; - } + let defaultTeam = selectFirstAvailableTeam(teams, ExperimentalPrimaryTeam); if (defaultTeam) { - handleTeamChange(defaultTeam.id)(dispatch, getState); + dispatch(handleTeamChange(defaultTeam.id)); } else if (state.requests.teams.getTeams.status === RequestStatus.FAILURE || state.requests.teams.getMyTeams.status === RequestStatus.FAILURE) { EventEmitter.emit(NavigationTypes.NAVIGATION_ERROR_TEAMS); } else { - EventEmitter.emit(NavigationTypes.NAVIGATION_NO_TEAMS); + // If for some reason we reached this point cause of a failure in rehydration or something + // lets fetch the teams one more time to make sure the user does not belong to any team + const {data, error} = await dispatch(getMyTeams()); + if (error) { + EventEmitter.emit(NavigationTypes.NAVIGATION_ERROR_TEAMS); + return; + } + + if (data) { + defaultTeam = selectFirstAvailableTeam(data, ExperimentalPrimaryTeam); + } + + if (defaultTeam) { + dispatch(handleTeamChange(defaultTeam.id)); + } else { + EventEmitter.emit(NavigationTypes.NAVIGATION_NO_TEAMS); + } } }; } diff --git a/app/store/middleware.js b/app/store/middleware.js index 50ef90bb8..88550369f 100644 --- a/app/store/middleware.js +++ b/app/store/middleware.js @@ -197,7 +197,7 @@ function cleanupState(action, keepCurrent = false) { const {currentChannelId} = payload.entities.channels; const {lastChannelForTeam} = resetPayload.views.team; - const nextEntitites = { + const nextEntities = { posts: { posts: {}, postsInChannel: {}, @@ -224,14 +224,14 @@ function cleanupState(action, keepCurrent = false) { // we need to check that the channel id is not already included // the reason it can be included is cause at least one of the last channels viewed // in a team can be a DM or GM and the id can be duplicate - if (!nextEntitites.posts.postsInChannel[id] && payload.entities.posts.postsInChannel[id]) { + if (!nextEntities.posts.postsInChannel[id] && payload.entities.posts.postsInChannel[id]) { let postIds; if (keepCurrent && currentChannelId === id) { postIds = payload.entities.posts.postsInChannel[id]; } else { postIds = payload.entities.posts.postsInChannel[id].slice(0, 60); } - nextEntitites.posts.postsInChannel[id] = postIds; + nextEntities.posts.postsInChannel[id] = postIds; return result.concat(postIds); } @@ -261,7 +261,7 @@ function cleanupState(action, keepCurrent = false) { if (post) { if (retentionPeriod && post.create_at < retentionPeriod) { - const postsInChannel = nextEntitites.posts.postsInChannel[post.channel_id] || []; + const postsInChannel = nextEntities.posts.postsInChannel[post.channel_id] || []; const index = postsInChannel.indexOf(postId); if (index !== -1) { postsInChannel.splice(index, 1); @@ -269,31 +269,31 @@ function cleanupState(action, keepCurrent = false) { return; } - nextEntitites.posts.posts[postId] = post; + nextEntities.posts.posts[postId] = post; const reaction = payload.entities.posts.reactions[postId]; if (reaction) { - nextEntitites.posts.reactions[postId] = reaction; + nextEntities.posts.reactions[postId] = reaction; } const fileIds = payload.entities.files.fileIdsByPostId[postId]; if (fileIds) { - nextEntitites.files.fileIdsByPostId[postId] = fileIds; + nextEntities.files.fileIdsByPostId[postId] = fileIds; fileIds.forEach((fileId) => { - nextEntitites.files.files[fileId] = payload.entities.files.files[fileId]; + nextEntities.files.files[fileId] = payload.entities.files.files[fileId]; }); } const postsInThread = payload.entities.posts.postsInThread[postId]; if (postsInThread) { - nextEntitites.posts.postsInThread[postId] = postsInThread; + nextEntities.posts.postsInThread[postId] = postsInThread; } } else { // If the post is not in the store we need to remove it from the postsInChannel - const channelIds = Object.keys(nextEntitites.posts.postsInChannel); + const channelIds = Object.keys(nextEntities.posts.postsInChannel); for (let i = 0; i < channelIds.length; i++) { const channelId = channelIds[i]; - const posts = nextEntitites.posts.postsInChannel[channelId]; + const posts = nextEntities.posts.postsInChannel[channelId]; const index = posts.indexOf(postId); if (index !== -1) { posts.splice(index, 1); @@ -307,17 +307,17 @@ function cleanupState(action, keepCurrent = false) { 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 = nextEntitites.posts.posts; + const posts = nextEntities.posts.posts; const post = posts[id]; if (post) { - const postsInChannel = [...nextEntitites.posts.postsInChannel[post.channel_id]] || []; + const postsInChannel = [...nextEntities.posts.postsInChannel[post.channel_id]] || []; if (!post.failed) { Reflect.deleteProperty(posts, id); const index = postsInChannel.indexOf(id); if (index !== -1) { postsInChannel.splice(index, 1); - nextEntitites.posts.postsInChannel[post.channel_id] = postsInChannel; + nextEntities.posts.postsInChannel[post.channel_id] = postsInChannel; } removePendingPost(nextPendingPostIds, id); } @@ -326,13 +326,13 @@ function cleanupState(action, keepCurrent = false) { } }); - nextEntitites.posts.pendingPostIds = nextPendingPostIds; + nextEntities.posts.pendingPostIds = nextPendingPostIds; } const nextState = { app: resetPayload.app, entities: { - ...nextEntitites, + ...nextEntities, channels: payload.entities.channels, emojis: payload.entities.emojis, general: resetPayload.entities.general, diff --git a/app/utils/teams.js b/app/utils/teams.js new file mode 100644 index 000000000..f02bb4d76 --- /dev/null +++ b/app/utils/teams.js @@ -0,0 +1,16 @@ +// Copyright (c) 2015-present Mattermost, Inc. All Rights Reserved. +// See LICENSE.txt for license information. +// @flow + +export function selectFirstAvailableTeam(teams, primaryTeam) { + let defaultTeam; + if (primaryTeam) { + defaultTeam = teams.find((t) => t.name === primaryTeam.toLowerCase()); + } + + if (!defaultTeam) { + defaultTeam = Object.values(teams).sort((a, b) => a.display_name.localeCompare(b.display_name))[0]; + } + + return defaultTeam; +} diff --git a/app/utils/teams.test.js b/app/utils/teams.test.js new file mode 100644 index 000000000..c08a8cd30 --- /dev/null +++ b/app/utils/teams.test.js @@ -0,0 +1,38 @@ +// Copyright (c) 2015-present Mattermost, Inc. All Rights Reserved. +// See LICENSE.txt for license information. + +import {selectFirstAvailableTeam} from 'app/utils/teams'; + +describe('TeamUtils', () => { + describe('selectFirstAvailableTeam', () => { + const myTeams = [{ + id: 'team-id-1', + display_name: 'Zeta Team', + name: 'zeta-team', + }, { + id: 'team-id-2', + display_name: 'Alpha Team', + name: 'alpha-team', + }]; + + it('should return the primary team', () => { + const defaultTeam = selectFirstAvailableTeam(myTeams, 'zeTa-teAM'); + expect(defaultTeam).toBe(myTeams[0]); + }); + + it('should return the first team ordered by display_name', () => { + const defaultTeam = selectFirstAvailableTeam(myTeams); + expect(defaultTeam).toBe(myTeams[1]); + }); + + it('should return the first team ordered by display_name if primary team is not found', () => { + const defaultTeam = selectFirstAvailableTeam(myTeams, 'non-existent-team'); + expect(defaultTeam).toBe(myTeams[1]); + }); + + it('should return undefined is no team is found', () => { + const defaultTeam = selectFirstAvailableTeam([]); + expect(defaultTeam).toBe(undefined); + }); + }); +});