MM-54581 Fixed a bug where converted GM showed up in incorrect team as well when someone else converted it (#24633)
* Fixed a bug where converted GM showed up in incorrect team as well when someone else converted it * Created new redux action * Fixed test * Unexported fjunction * More tests * Using old state instead new state as data exists in old, not new state
Этот коммит содержится в:
коммит произвёл
GitHub
родитель
8e7e5c8775
Коммит
45ccd50f8c
@@ -622,9 +622,22 @@ export function handleChannelUpdatedEvent(msg) {
|
|||||||
return (doDispatch, doGetState) => {
|
return (doDispatch, doGetState) => {
|
||||||
const channel = JSON.parse(msg.data.channel);
|
const channel = JSON.parse(msg.data.channel);
|
||||||
|
|
||||||
doDispatch({type: ChannelTypes.RECEIVED_CHANNEL, data: channel});
|
const actions = [{type: ChannelTypes.RECEIVED_CHANNEL, data: channel}];
|
||||||
|
|
||||||
|
// handling the case of GM converted to private channel.
|
||||||
const state = doGetState();
|
const state = doGetState();
|
||||||
|
const existingChannel = getChannel(state, channel.id);
|
||||||
|
|
||||||
|
// if the updated channel exists in store
|
||||||
|
if (existingChannel) {
|
||||||
|
// and it was a GM, converted to a private channel
|
||||||
|
if (existingChannel.type === General.GM_CHANNEL && channel.type === General.PRIVATE_CHANNEL) {
|
||||||
|
actions.push({type: ChannelTypes.GM_CONVERTED_TO_CHANNEL, data: channel});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
doDispatch(batchActions(actions));
|
||||||
|
|
||||||
if (channel.id === getCurrentChannelId(state)) {
|
if (channel.id === getCurrentChannelId(state)) {
|
||||||
// using channel's team_id to ensure we always redirect to current channel even if channel's team changes.
|
// using channel's team_id to ensure we always redirect to current channel even if channel's team changes.
|
||||||
getHistory().replace(`${getRelativeTeamUrl(state, channel.team_id)}/channels/${channel.name}`);
|
getHistory().replace(`${getRelativeTeamUrl(state, channel.team_id)}/channels/${channel.name}`);
|
||||||
|
|||||||
@@ -1,7 +1,7 @@
|
|||||||
// Copyright (c) 2015-present Mattermost, Inc. All Rights Reserved.
|
// Copyright (c) 2015-present Mattermost, Inc. All Rights Reserved.
|
||||||
// See LICENSE.txt for license information.
|
// See LICENSE.txt for license information.
|
||||||
|
|
||||||
import {ChannelTypes, UserTypes, CloudTypes} from 'mattermost-redux/action_types';
|
import {UserTypes, CloudTypes} from 'mattermost-redux/action_types';
|
||||||
import {getGroup} from 'mattermost-redux/actions/groups';
|
import {getGroup} from 'mattermost-redux/actions/groups';
|
||||||
import {
|
import {
|
||||||
getMentionsAndStatusesForPosts,
|
getMentionsAndStatusesForPosts,
|
||||||
@@ -636,6 +636,11 @@ describe('handleChannelUpdatedEvent', () => {
|
|||||||
entities: {
|
entities: {
|
||||||
channels: {
|
channels: {
|
||||||
currentChannelId: 'channel',
|
currentChannelId: 'channel',
|
||||||
|
channels: {
|
||||||
|
channel: {
|
||||||
|
id: 'channel',
|
||||||
|
},
|
||||||
|
},
|
||||||
},
|
},
|
||||||
teams: {
|
teams: {
|
||||||
currentTeamId: 'team',
|
currentTeamId: 'team',
|
||||||
@@ -656,10 +661,56 @@ describe('handleChannelUpdatedEvent', () => {
|
|||||||
const msg = {data: {channel: JSON.stringify(channel)}};
|
const msg = {data: {channel: JSON.stringify(channel)}};
|
||||||
|
|
||||||
testStore.dispatch(handleChannelUpdatedEvent(msg));
|
testStore.dispatch(handleChannelUpdatedEvent(msg));
|
||||||
|
expect(testStore.getActions()).toEqual([{
|
||||||
|
type: 'BATCHING_REDUCER.BATCH',
|
||||||
|
meta: {batch: true},
|
||||||
|
payload: [
|
||||||
|
{
|
||||||
|
type: 'RECEIVED_CHANNEL',
|
||||||
|
data: {
|
||||||
|
id: 'channel',
|
||||||
|
team_id: 'team',
|
||||||
|
},
|
||||||
|
},
|
||||||
|
],
|
||||||
|
}]);
|
||||||
|
});
|
||||||
|
|
||||||
expect(testStore.getActions()).toEqual([
|
test('when GM is converted to private channel', () => {
|
||||||
{type: ChannelTypes.RECEIVED_CHANNEL, data: channel},
|
const state = initialState;
|
||||||
]);
|
state.entities.channels.channels.channel.type = Constants.GM_CHANNEL;
|
||||||
|
|
||||||
|
const testStore = configureStore(state);
|
||||||
|
const channel = {
|
||||||
|
id: 'channel',
|
||||||
|
team_id: 'team',
|
||||||
|
type: Constants.PRIVATE_CHANNEL,
|
||||||
|
};
|
||||||
|
|
||||||
|
const msg = {data: {channel: JSON.stringify(channel)}};
|
||||||
|
testStore.dispatch(handleChannelUpdatedEvent(msg));
|
||||||
|
expect(testStore.getActions()).toEqual([{
|
||||||
|
type: 'BATCHING_REDUCER.BATCH',
|
||||||
|
meta: {batch: true},
|
||||||
|
payload: [
|
||||||
|
{
|
||||||
|
type: 'RECEIVED_CHANNEL',
|
||||||
|
data: {
|
||||||
|
id: 'channel',
|
||||||
|
team_id: 'team',
|
||||||
|
type: 'P',
|
||||||
|
},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
type: 'GM_CONVERTED_TO_CHANNEL',
|
||||||
|
data: {
|
||||||
|
id: 'channel',
|
||||||
|
team_id: 'team',
|
||||||
|
type: 'P',
|
||||||
|
},
|
||||||
|
},
|
||||||
|
],
|
||||||
|
}]);
|
||||||
});
|
});
|
||||||
|
|
||||||
test('should not change URL when current channel is updated', () => {
|
test('should not change URL when current channel is updated', () => {
|
||||||
|
|||||||
@@ -95,4 +95,6 @@ export default keyMirror({
|
|||||||
DECREMENT_PINNED_POST_COUNT: null,
|
DECREMENT_PINNED_POST_COUNT: null,
|
||||||
|
|
||||||
INCREMENT_FILE_COUNT: null,
|
INCREMENT_FILE_COUNT: null,
|
||||||
|
|
||||||
|
GM_CONVERTED_TO_CHANNEL: null,
|
||||||
});
|
});
|
||||||
|
|||||||
@@ -4,6 +4,7 @@
|
|||||||
import {combineReducers} from 'redux';
|
import {combineReducers} from 'redux';
|
||||||
|
|
||||||
import type {ChannelCategory} from '@mattermost/types/channel_categories';
|
import type {ChannelCategory} from '@mattermost/types/channel_categories';
|
||||||
|
import type {Channel} from '@mattermost/types/channels';
|
||||||
import type {Team} from '@mattermost/types/teams';
|
import type {Team} from '@mattermost/types/teams';
|
||||||
import type {IDMappedObjects, RelationOneToOne} from '@mattermost/types/utilities';
|
import type {IDMappedObjects, RelationOneToOne} from '@mattermost/types/utilities';
|
||||||
|
|
||||||
@@ -92,7 +93,38 @@ export function byId(state: IDMappedObjects<ChannelCategory> = {}, action: Gener
|
|||||||
|
|
||||||
return changed ? nextState : state;
|
return changed ? nextState : state;
|
||||||
}
|
}
|
||||||
|
case ChannelTypes.GM_CONVERTED_TO_CHANNEL: {
|
||||||
|
// For GM to Private channel conversion feature
|
||||||
|
// In the case when someone converts your GM to a private channel and moves it to a team
|
||||||
|
// you're not currently on, we need to remove the channel from "direct messages" category
|
||||||
|
// and add it to "channels" category of target team. Even though the server sends a websocket event about updated category data,
|
||||||
|
// it does so only for the team the channel got moved into, and not every team a user is part of, for performance reasons.
|
||||||
|
// For every other team, we update the state here. Everything is correct on server side, but we update state here
|
||||||
|
// to avoid re-fetching all categories again for all teams.
|
||||||
|
|
||||||
|
const receivedChannel = action.data as Channel;
|
||||||
|
const newState: IDMappedObjects<ChannelCategory> = {};
|
||||||
|
const categoryIDs = Object.keys(state);
|
||||||
|
|
||||||
|
categoryIDs.forEach((categoryID) => {
|
||||||
|
if (categoryID.startsWith('channels_') && state[categoryID].team_id === receivedChannel.team_id && state[categoryID].channel_ids.indexOf(receivedChannel.id) < 0) {
|
||||||
|
// We don't need to worry about adding the channel in the right order as this is only
|
||||||
|
// an intermediate step, meant to handle the edge case of missing the upcoming "update category"
|
||||||
|
// websocket message, triggered on conversion of GM to private channel.
|
||||||
|
newState[categoryID] = {
|
||||||
|
...state[categoryID],
|
||||||
|
channel_ids: [...state[categoryID].channel_ids, receivedChannel.id],
|
||||||
|
};
|
||||||
|
} else {
|
||||||
|
newState[categoryID] = {
|
||||||
|
...state[categoryID],
|
||||||
|
channel_ids: state[categoryID].channel_ids.filter((channelID) => channelID !== receivedChannel.id),
|
||||||
|
};
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
return newState;
|
||||||
|
}
|
||||||
case UserTypes.LOGOUT_SUCCESS:
|
case UserTypes.LOGOUT_SUCCESS:
|
||||||
return {};
|
return {};
|
||||||
default:
|
default:
|
||||||
|
|||||||
Ссылка в новой задаче
Block a user