Skip to content

Commit

Permalink
Creating the concept of RedirectChannel and use it before the Default…
Browse files Browse the repository at this point in the history
…Channel when we are redirecting the user (mattermost#799)

* Creating the concept of RedirectChannel and use it before the DefaultChannel when we are redirecting the user

* Addressing PR review comments

* Removing makeGet approach

* Addressing PR review comments

* Fixing tests
  • Loading branch information
jespino authored Apr 16, 2019
1 parent 29e65f5 commit 4771504
Show file tree
Hide file tree
Showing 5 changed files with 273 additions and 24 deletions.
18 changes: 8 additions & 10 deletions src/actions/channels.js
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,8 @@ import {General, Preferences} from 'constants';
import {ChannelTypes, PreferenceTypes, UserTypes} from 'action_types';
import {savePreferences, deletePreferences} from 'actions/preferences';
import {getChannelsIdForTeam} from 'utils/channel_utils';
import {getMyChannelMember as getMyChannelMemberSelector} from 'selectors/entities/channels';
import {getMyChannelMember as getMyChannelMemberSelector, getChannelByName, getRedirectChannelNameForTeam} from 'selectors/entities/channels';
import {getCurrentTeamId} from 'selectors/entities/teams';

import {logError} from './errors';
import {bindClientFunc, forceLogoutIfNecessary} from './helpers';
Expand Down Expand Up @@ -654,7 +655,7 @@ export function joinChannel(userId: string, teamId: string, channelId: string, c

export function deleteChannel(channelId: string): ActionFunc {
return async (dispatch, getState) => {
const state = getState();
let state = getState();
const viewArchivedChannels = state.entities.general.config.ExperimentalViewArchivedChannels === 'true';

try {
Expand All @@ -665,16 +666,13 @@ export function deleteChannel(channelId: string): ActionFunc {
return {error};
}

const entities = getState().entities;
const {channels, currentChannelId} = entities.channels;
state = getState();
const {currentChannelId} = state.entities.channels;
if (channelId === currentChannelId && !viewArchivedChannels) {
const channel = Object.keys(channels).filter((key) => channels[key].name === General.DEFAULT_CHANNEL);
let defaultChannelId = '';
if (channel.length) {
defaultChannelId = channel[0];
const channel = getChannelByName(state, getRedirectChannelNameForTeam(state, getCurrentTeamId(state)));
if (channel && channel.id) {
dispatch({type: ChannelTypes.SELECT_CHANNEL, data: channel.id}, getState);
}

dispatch({type: ChannelTypes.SELECT_CHANNEL, data: defaultChannelId}, getState);
}

dispatch({type: ChannelTypes.DELETE_CHANNEL_SUCCESS, data: {id: channelId, viewArchivedChannels}}, getState);
Expand Down
15 changes: 4 additions & 11 deletions src/actions/websocket.js
Original file line number Diff line number Diff line change
Expand Up @@ -40,7 +40,7 @@ import {
} from 'action_types';
import {General, WebsocketEvents, Preferences} from 'constants';

import {getAllChannels, getChannel, getChannelsInTeam, getCurrentChannelId, getCurrentChannelStats} from 'selectors/entities/channels';
import {getAllChannels, getChannel, getCurrentChannelId, getChannelByName, getRedirectChannelNameForTeam, getCurrentChannelStats} from 'selectors/entities/channels';
import {getConfig} from 'selectors/entities/general';
import {getAllPosts} from 'selectors/entities/posts';
import {getDirectShowPreferences} from 'selectors/entities/preferences';
Expand Down Expand Up @@ -496,24 +496,17 @@ function handleChannelCreatedEvent(msg) {
function handleChannelDeletedEvent(msg) {
return (dispatch, getState) => {
const state = getState();
const channels = getAllChannels(state);
const channelsInTeam = getChannelsInTeam(state);
const currentChannelId = getCurrentChannelId(state);
const currentTeamId = getCurrentTeamId(state);
const config = getConfig(state);
const viewArchivedChannels = config.ExperimentalViewArchivedChannels === 'true';

if (msg.broadcast.team_id === currentTeamId) {
if (msg.data.channel_id === currentChannelId && !viewArchivedChannels) {
let channelId = '';
const teamChannels = Array.from(channelsInTeam[currentTeamId]);
const channel = teamChannels.filter((key) => channels[key].name === General.DEFAULT_CHANNEL);

if (channel.length) {
channelId = channel[0];
const channel = getChannelByName(state, getRedirectChannelNameForTeam(state, currentTeamId));
if (channel && channel.id) {
dispatch({type: ChannelTypes.SELECT_CHANNEL, data: channel.id});
}

dispatch({type: ChannelTypes.SELECT_CHANNEL, data: channelId});
EventEmitter.emit(General.DEFAULT_CHANNEL, '');
}

Expand Down
4 changes: 2 additions & 2 deletions src/actions/websocket.test.js
Original file line number Diff line number Diff line change
Expand Up @@ -220,7 +220,7 @@ describe('Actions.Websocket', () => {

it('Websocket Handle Channel Created', (done) => {
async function test() {
const channel = {id: '95tpi6f4apy39k6zxuo3msxzhy'};
const channel = {id: '95tpi6f4apy39k6zxuo3msxzhy', display_name: 'test'};
store.dispatch({type: ChannelTypes.RECEIVED_CHANNEL, data: channel});
mockServer.emit('message', JSON.stringify({event: WebsocketEvents.CHANNEL_CREATED, data: {channel_id: '95tpi6f4apy39k6zxuo3msxzhy', team_id: TestHelper.basicTeam.id}, broadcast: {omit_users: null, user_id: 't36kso9nwtdhbm8dbkd6g4eeby', channel_id: '', team_id: ''}, seq: 57}));

Expand Down Expand Up @@ -283,7 +283,7 @@ describe('Actions.Websocket', () => {
await store.dispatch(TeamActions.selectTeam(TestHelper.basicTeam));
await store.dispatch(ChannelActions.selectChannel(TestHelper.basicChannel.id));

store.dispatch({type: ChannelTypes.RECEIVED_CHANNEL, data: {id: TestHelper.generateId(), name: General.DEFAULT_CHANNEL, team_id: TestHelper.basicTeam.id}});
store.dispatch({type: ChannelTypes.RECEIVED_CHANNEL, data: {id: TestHelper.generateId(), name: General.DEFAULT_CHANNEL, team_id: TestHelper.basicTeam.id, display_name: General.DEFAULT_CHANNEL}});
store.dispatch({type: ChannelTypes.RECEIVED_CHANNEL, data: TestHelper.basicChannel});

nock(Client4.getUserRoute('me')).
Expand Down
44 changes: 43 additions & 1 deletion src/selectors/entities/channels.js
Original file line number Diff line number Diff line change
Expand Up @@ -23,7 +23,7 @@ import {
} from 'selectors/entities/preferences';
import {getLastPostPerChannel, getAllPosts} from 'selectors/entities/posts';
import {getCurrentTeamId, getCurrentTeamMembership, getMyTeams, getTeamMemberships} from 'selectors/entities/teams';
import {haveICurrentChannelPermission, haveIChannelPermission} from 'selectors/entities/roles';
import {haveICurrentChannelPermission, haveIChannelPermission, haveITeamPermission} from 'selectors/entities/roles';
import {isCurrentUserSystemAdmin, getCurrentUserId} from 'selectors/entities/users';

import {
Expand Down Expand Up @@ -1179,3 +1179,45 @@ export const getSortedDirectChannelWithUnreadsIds: (GlobalState, Channel, boolea
return filterChannels(unreadChannelIds, favoritePreferences, directChannelIds, false, favoritesAtTop);
},
);

export const getDefaultChannelForTeams: (GlobalState) => RelationOneToOne<Team, Channel> = createSelector(
getAllChannels,
(channels: IDMappedObjects<Channel>): RelationOneToOne<Team, Channel> => {
const result = {};
for (const channel of Object.keys(channels).map((key) => channels[key])) {
if (channel && channel.name === General.DEFAULT_CHANNEL) {
result[channel.team_id] = channel;
}
}
return result;
}
);

export const getMyFirstChannelForTeams: (GlobalState) => RelationOneToOne<Team, Channel> = createSelector(
getAllChannels,
getMyChannelMemberships,
getCurrentUser,
(allChannels: IDMappedObjects<Channel>, myChannelMemberships: RelationOneToOne<Channel, ChannelMembership>, currentUser: UserProfile): RelationOneToOne<Team, Channel> => {
const locale = currentUser.locale || General.DEFAULT_LOCALE;
const result = {};
for (const channel of Object.keys(allChannels).map((key) => allChannels[key]).sort(sortChannelsByDisplayName.bind(null, locale))) {
if (!result[channel.team_id] && myChannelMemberships[channel.id]) {
result[channel.team_id] = channel;
}
}
return result;
}
);

export const getRedirectChannelNameForTeam = (state: GlobalState, teamId: string): string => {
const defaultChannelForTeam = getDefaultChannelForTeams(state)[teamId];
const myFirstChannelForTeam = getMyFirstChannelForTeams(state)[teamId];
const canIJoinPublicChannelsInTeam = !hasNewPermissions(state) || haveITeamPermission(state, {team: teamId, permission: Permissions.JOIN_PUBLIC_CHANNELS});
const myChannelMemberships = getMyChannelMemberships(state);

const iAmMemberOfTheTeamDefaultChannel = Boolean(defaultChannelForTeam && myChannelMemberships[defaultChannelForTeam.id]);
if (iAmMemberOfTheTeamDefaultChannel || canIJoinPublicChannelsInTeam) {
return General.DEFAULT_CHANNEL;
}
return (myFirstChannelForTeam && myFirstChannelForTeam.name) || General.DEFAULT_CHANNEL;
};
216 changes: 216 additions & 0 deletions src/selectors/entities/channels.test.js
Original file line number Diff line number Diff line change
Expand Up @@ -1170,6 +1170,222 @@ describe('Selectors.Channels', () => {
});
});

describe('get_redirect_channel_name_for_team selector', () => {
it('getRedirectChannelNameForTeam without advanced permissions', () => {
const modifiedState = {
...testState,
entities: {
...testState.entities,
general: {
...testState.entities.general,
serverVersion: '4.8.0',
},
},
};
assert.equal(Selectors.getRedirectChannelNameForTeam(modifiedState, team1.id), General.DEFAULT_CHANNEL);
});

it('getRedirectChannelNameForTeam with advanced permissions but without JOIN_PUBLIC_CHANNELS permission', () => {
const modifiedState = {
...testState,
entities: {
...testState.entities,
channels: {
...testState.entities.channels,
channels: {
...testState.entities.channels.channels,
'new-not-member-channel': {
id: 'new-not-member-channel',
display_name: '111111',
name: 'new-not-member-channel',
team_id: team1.id,
},
[channel1.id]: {
id: channel1.id,
display_name: 'aaaaaa',
name: 'test-channel',
team_id: team1.id,
},
},
},
roles: {
roles: {
system_user: {permissions: []},
},
},
general: {
...testState.entities.general,
serverVersion: '5.12.0',
},
},
};
assert.equal(Selectors.getRedirectChannelNameForTeam(modifiedState, team1.id), 'test-channel');
});

it('getRedirectChannelNameForTeam with advanced permissions and with JOIN_PUBLIC_CHANNELS permission', () => {
const modifiedState = {
...testState,
entities: {
...testState.entities,
roles: {
roles: {
system_user: {permissions: ['join_public_channels']},
},
},
general: {
...testState.entities.general,
serverVersion: '5.12.0',
},
},
};
assert.equal(Selectors.getRedirectChannelNameForTeam(modifiedState, team1.id), General.DEFAULT_CHANNEL);
});

it('getRedirectChannelNameForTeam with advanced permissions but without JOIN_PUBLIC_CHANNELS permission but being member of town-square', () => {
const modifiedState = {
...testState,
entities: {
...testState.entities,
channels: {
...testState.entities.channels,
channels: {
...testState.entities.channels.channels,
'new-not-member-channel': {
id: 'new-not-member-channel',
display_name: '111111',
name: 'new-not-member-channel',
team_id: team1.id,
},
[channel1.id]: {
id: channel1.id,
display_name: 'Town Square',
name: 'town-square',
team_id: team1.id,
},
},
},
roles: {
roles: {
system_user: {permissions: []},
},
},
general: {
...testState.entities.general,
serverVersion: '5.12.0',
},
},
};
assert.equal(Selectors.getRedirectChannelNameForTeam(modifiedState, team1.id), General.DEFAULT_CHANNEL);
});

it('getRedirectChannelNameForTeam without advanced permissions in not current team', () => {
const modifiedState = {
...testState,
entities: {
...testState.entities,
general: {
...testState.entities.general,
serverVersion: '4.8.0',
},
},
};
assert.equal(Selectors.getRedirectChannelNameForTeam(modifiedState, team2.id), General.DEFAULT_CHANNEL);
});

it('getRedirectChannelNameForTeam with advanced permissions but without JOIN_PUBLIC_CHANNELS permission in not current team', () => {
const modifiedState = {
...testState,
entities: {
...testState.entities,
channels: {
...testState.entities.channels,
channels: {
...testState.entities.channels.channels,
'new-not-member-channel': {
id: 'new-not-member-channel',
display_name: '111111',
name: 'new-not-member-channel',
team_id: team2.id,
},
[channel3.id]: {
id: channel3.id,
display_name: 'aaaaaa',
name: 'test-channel',
team_id: team2.id,
},
},
},
roles: {
roles: {
system_user: {permissions: []},
},
},
general: {
...testState.entities.general,
serverVersion: '5.12.0',
},
},
};
assert.equal(Selectors.getRedirectChannelNameForTeam(modifiedState, team2.id), 'test-channel');
});

it('getRedirectChannelNameForTeam with advanced permissions and with JOIN_PUBLIC_CHANNELS permission in not current team', () => {
const modifiedState = {
...testState,
entities: {
...testState.entities,
roles: {
roles: {
system_user: {permissions: ['join_public_channels']},
},
},
general: {
...testState.entities.general,
serverVersion: '5.12.0',
},
},
};
assert.equal(Selectors.getRedirectChannelNameForTeam(modifiedState, team2.id), General.DEFAULT_CHANNEL);
});

it('getRedirectChannelNameForTeam with advanced permissions but without JOIN_PUBLIC_CHANNELS permission but being member of town-square in not current team', () => {
const modifiedState = {
...testState,
entities: {
...testState.entities,
channels: {
...testState.entities.channels,
channels: {
...testState.entities.channels.channels,
'new-not-member-channel': {
id: 'new-not-member-channel',
display_name: '111111',
name: 'new-not-member-channel',
team_id: team2.id,
},
[channel3.id]: {
id: channel3.id,
display_name: 'Town Square',
name: 'town-square',
team_id: team2.id,
},
},
},
roles: {
roles: {
system_user: {permissions: []},
},
},
general: {
...testState.entities.general,
serverVersion: '5.12.0',
},
},
};
assert.equal(Selectors.getRedirectChannelNameForTeam(modifiedState, team2.id), General.DEFAULT_CHANNEL);
});
});

describe('canManageAnyChannelMembersInCurrentTeam', () => {
it('will return false if channel_user does not have permissions to manage channel members', () => {
const newState = {
Expand Down

0 comments on commit 4771504

Please sign in to comment.