[GH-30077] New actions for websocket group member update (#30086)
* Added: Update on `groups` & `profiles` on websocket.
Этот коммит содержится в:
коммит произвёл
GitHub
родитель
12cbe5e839
Коммит
7770c03919
@@ -1449,30 +1449,39 @@ function handleGroupUpdatedEvent(msg) {
|
|||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
function handleMyGroupUpdate(groupMember) {
|
||||||
|
dispatch(batchActions([
|
||||||
|
{
|
||||||
|
type: GroupTypes.ADD_MY_GROUP,
|
||||||
|
id: groupMember.group_id,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
type: GroupTypes.RECEIVED_MEMBER_TO_ADD_TO_GROUP,
|
||||||
|
data: groupMember,
|
||||||
|
id: groupMember.group_id,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
type: UserTypes.RECEIVED_PROFILES_FOR_GROUP,
|
||||||
|
data: [groupMember],
|
||||||
|
id: groupMember.group_id,
|
||||||
|
},
|
||||||
|
]));
|
||||||
|
}
|
||||||
|
|
||||||
export function handleGroupAddedMemberEvent(msg) {
|
export function handleGroupAddedMemberEvent(msg) {
|
||||||
return async (doDispatch, doGetState) => {
|
return async (doDispatch, doGetState) => {
|
||||||
const state = doGetState();
|
const state = doGetState();
|
||||||
const currentUserId = getCurrentUserId(state);
|
const currentUserId = getCurrentUserId(state);
|
||||||
const groupInfo = JSON.parse(msg.data.group_member);
|
const groupMember = JSON.parse(msg.data.group_member);
|
||||||
|
|
||||||
if (currentUserId === groupInfo.user_id) {
|
if (currentUserId === groupMember.user_id) {
|
||||||
const group = getGroup(state, groupInfo.group_id);
|
const group = getGroup(state, groupMember.group_id);
|
||||||
if (group) {
|
if (group) {
|
||||||
dispatch(
|
handleMyGroupUpdate(groupMember);
|
||||||
{
|
|
||||||
type: GroupTypes.ADD_MY_GROUP,
|
|
||||||
id: groupInfo.group_id,
|
|
||||||
},
|
|
||||||
);
|
|
||||||
} else {
|
} else {
|
||||||
const {error} = await doDispatch(fetchGroup(groupInfo.group_id, true));
|
const {error} = await doDispatch(fetchGroup(groupMember.group_id, true));
|
||||||
if (!error) {
|
if (!error) {
|
||||||
dispatch(
|
handleMyGroupUpdate(groupMember);
|
||||||
{
|
|
||||||
type: GroupTypes.ADD_MY_GROUP,
|
|
||||||
id: groupInfo.group_id,
|
|
||||||
},
|
|
||||||
);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -1486,13 +1495,23 @@ function handleGroupDeletedMemberEvent(msg) {
|
|||||||
const data = JSON.parse(msg.data.group_member);
|
const data = JSON.parse(msg.data.group_member);
|
||||||
|
|
||||||
if (currentUserId === data.user_id) {
|
if (currentUserId === data.user_id) {
|
||||||
dispatch(
|
dispatch(batchActions([
|
||||||
{
|
{
|
||||||
type: GroupTypes.REMOVE_MY_GROUP,
|
type: GroupTypes.REMOVE_MY_GROUP,
|
||||||
data,
|
data,
|
||||||
id: data.group_id,
|
id: data.group_id,
|
||||||
},
|
},
|
||||||
);
|
{
|
||||||
|
type: UserTypes.RECEIVED_PROFILES_LIST_TO_REMOVE_FROM_GROUP,
|
||||||
|
data: [data],
|
||||||
|
id: data.group_id,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
type: GroupTypes.RECEIVED_MEMBER_TO_REMOVE_FROM_GROUP,
|
||||||
|
data,
|
||||||
|
id: data.group_id,
|
||||||
|
},
|
||||||
|
]));
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -270,8 +270,34 @@ describe('handleGroupAddedMemberEvent', () => {
|
|||||||
|
|
||||||
testStore.dispatch(handleGroupAddedMemberEvent(msg));
|
testStore.dispatch(handleGroupAddedMemberEvent(msg));
|
||||||
expect(store.dispatch).toHaveBeenCalledWith({
|
expect(store.dispatch).toHaveBeenCalledWith({
|
||||||
type: 'ADD_MY_GROUP',
|
meta: {batch: true},
|
||||||
id: 'group-1',
|
payload: [
|
||||||
|
{
|
||||||
|
type: 'ADD_MY_GROUP',
|
||||||
|
id: 'group-1',
|
||||||
|
},
|
||||||
|
{
|
||||||
|
data: {
|
||||||
|
create_at: 1691178673417,
|
||||||
|
delete_at: 0,
|
||||||
|
group_id: 'group-1',
|
||||||
|
user_id: 'currentUserId',
|
||||||
|
},
|
||||||
|
id: 'group-1',
|
||||||
|
type: 'RECEIVED_MEMBER_TO_ADD_TO_GROUP',
|
||||||
|
},
|
||||||
|
{
|
||||||
|
data: [{
|
||||||
|
create_at: 1691178673417,
|
||||||
|
delete_at: 0,
|
||||||
|
group_id: 'group-1',
|
||||||
|
user_id: 'currentUserId',
|
||||||
|
}],
|
||||||
|
id: 'group-1',
|
||||||
|
type: 'RECEIVED_PROFILES_FOR_GROUP',
|
||||||
|
},
|
||||||
|
],
|
||||||
|
type: 'BATCHING_REDUCER.BATCH',
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
|
|||||||
@@ -37,6 +37,9 @@ export default keyMirror({
|
|||||||
RECEIVED_GROUP_NOT_ASSOCIATED_TO_CHANNEL: null,
|
RECEIVED_GROUP_NOT_ASSOCIATED_TO_CHANNEL: null,
|
||||||
RECEIVED_GROUPS_NOT_ASSOCIATED_TO_CHANNEL: null,
|
RECEIVED_GROUPS_NOT_ASSOCIATED_TO_CHANNEL: null,
|
||||||
|
|
||||||
|
RECEIVED_MEMBER_TO_REMOVE_FROM_GROUP: null,
|
||||||
|
RECEIVED_MEMBER_TO_ADD_TO_GROUP: null,
|
||||||
|
|
||||||
PATCHED_GROUP_TEAM: null,
|
PATCHED_GROUP_TEAM: null,
|
||||||
PATCHED_GROUP_CHANNEL: null,
|
PATCHED_GROUP_CHANNEL: null,
|
||||||
|
|
||||||
|
|||||||
@@ -4,9 +4,8 @@
|
|||||||
import type {AnyAction} from 'redux';
|
import type {AnyAction} from 'redux';
|
||||||
import {batchActions} from 'redux-batched-actions';
|
import {batchActions} from 'redux-batched-actions';
|
||||||
|
|
||||||
import type {GroupPatch, SyncablePatch, GroupCreateWithUserIds, CustomGroupPatch, GroupSearchParams, GetGroupsParams, GetGroupsForUserParams, Group} from '@mattermost/types/groups';
|
import type {GroupPatch, SyncablePatch, GroupCreateWithUserIds, CustomGroupPatch, GroupSearchParams, GetGroupsParams, GetGroupsForUserParams, Group, GroupMember} from '@mattermost/types/groups';
|
||||||
import {SyncableType, GroupSource} from '@mattermost/types/groups';
|
import {SyncableType, GroupSource} from '@mattermost/types/groups';
|
||||||
import type {UserProfile} from '@mattermost/types/users';
|
|
||||||
|
|
||||||
import {ChannelTypes, GroupTypes, UserTypes} from 'mattermost-redux/action_types';
|
import {ChannelTypes, GroupTypes, UserTypes} from 'mattermost-redux/action_types';
|
||||||
import {Client4} from 'mattermost-redux/client';
|
import {Client4} from 'mattermost-redux/client';
|
||||||
@@ -344,7 +343,7 @@ export function createGroupWithUserIds(group: GroupCreateWithUserIds): ActionFun
|
|||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
export function addUsersToGroup(groupId: string, userIds: string[]): ActionFuncAsync<UserProfile[]> {
|
export function addUsersToGroup(groupId: string, userIds: string[]): ActionFuncAsync<GroupMember[]> {
|
||||||
return async (dispatch, getState) => {
|
return async (dispatch, getState) => {
|
||||||
let data;
|
let data;
|
||||||
try {
|
try {
|
||||||
@@ -366,7 +365,7 @@ export function addUsersToGroup(groupId: string, userIds: string[]): ActionFuncA
|
|||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
export function removeUsersFromGroup(groupId: string, userIds: string[]): ActionFuncAsync<UserProfile[]> {
|
export function removeUsersFromGroup(groupId: string, userIds: string[]): ActionFuncAsync<GroupMember[]> {
|
||||||
return async (dispatch, getState) => {
|
return async (dispatch, getState) => {
|
||||||
let data;
|
let data;
|
||||||
try {
|
try {
|
||||||
|
|||||||
@@ -1,6 +1,8 @@
|
|||||||
// 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 type {Group} from '@mattermost/types/groups';
|
||||||
|
|
||||||
import {GroupTypes} from 'mattermost-redux/action_types';
|
import {GroupTypes} from 'mattermost-redux/action_types';
|
||||||
import reducer from 'mattermost-redux/reducers/entities/groups';
|
import reducer from 'mattermost-redux/reducers/entities/groups';
|
||||||
|
|
||||||
@@ -341,4 +343,100 @@ describe('reducers/entities/groups', () => {
|
|||||||
expect(newState.syncables).toEqual(expectedState.syncables);
|
expect(newState.syncables).toEqual(expectedState.syncables);
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
describe('Groups', () => {
|
||||||
|
const groupTemplate: Omit<Group, 'id' | 'member_count' | 'member_ids'> = {
|
||||||
|
name: 'Test Group',
|
||||||
|
display_name: 'Test Group',
|
||||||
|
description: 'Test Description',
|
||||||
|
source: '',
|
||||||
|
create_at: 1,
|
||||||
|
update_at: 1,
|
||||||
|
delete_at: 1,
|
||||||
|
has_syncables: false,
|
||||||
|
scheme_admin: false,
|
||||||
|
allow_reference: false,
|
||||||
|
remote_id: null,
|
||||||
|
};
|
||||||
|
const groupId = 'ge63nq31sbfy3duzq5f7yqn7ii';
|
||||||
|
const userId = 'o3tdawqxot8kikzq8bk54zggbc';
|
||||||
|
const stateTemplate = {
|
||||||
|
syncables: {},
|
||||||
|
stats: {},
|
||||||
|
myGroups: [],
|
||||||
|
};
|
||||||
|
|
||||||
|
const receivedMemberToAddAction = {
|
||||||
|
type: GroupTypes.RECEIVED_MEMBER_TO_ADD_TO_GROUP,
|
||||||
|
data: {
|
||||||
|
user_id: userId,
|
||||||
|
group_id: groupId,
|
||||||
|
},
|
||||||
|
id: groupId,
|
||||||
|
};
|
||||||
|
const receivedMemberToDelAction = {...receivedMemberToAddAction, type: GroupTypes.RECEIVED_MEMBER_TO_REMOVE_FROM_GROUP};
|
||||||
|
|
||||||
|
it('GroupTypes.RECEIVED_MEMBER_TO_ADD_TO_GROUP, with member list', () => {
|
||||||
|
const state = {
|
||||||
|
...stateTemplate,
|
||||||
|
groups: {
|
||||||
|
[userId]: {...groupTemplate, id: userId, member_ids: [], member_count: 0},
|
||||||
|
[groupId]: {...groupTemplate, id: groupId, member_ids: [], member_count: 0},
|
||||||
|
},
|
||||||
|
};
|
||||||
|
|
||||||
|
const expectedState = {
|
||||||
|
...stateTemplate,
|
||||||
|
groups: {
|
||||||
|
[userId]: {...groupTemplate, id: userId, member_ids: [], member_count: 0},
|
||||||
|
[groupId]: {...groupTemplate, id: groupId, member_ids: [userId], member_count: 1},
|
||||||
|
},
|
||||||
|
};
|
||||||
|
|
||||||
|
const newState = reducer(state, receivedMemberToAddAction);
|
||||||
|
expect(newState.groups).toEqual(expectedState.groups);
|
||||||
|
});
|
||||||
|
it('GroupTypes.RECEIVED_MEMBER_TO_ADD_TO_GROUP, doublon', () => {
|
||||||
|
const state = {
|
||||||
|
...stateTemplate,
|
||||||
|
groups: {
|
||||||
|
[groupId]: {...groupTemplate, id: groupId, member_ids: [userId], member_count: 1},
|
||||||
|
},
|
||||||
|
};
|
||||||
|
|
||||||
|
const newState = reducer(state, receivedMemberToAddAction);
|
||||||
|
expect(newState.groups).toEqual(state.groups);
|
||||||
|
});
|
||||||
|
|
||||||
|
it('GroupTypes.RECEIVED_MEMBER_TO_REMOVE_FROM_GROUP, with member list', () => {
|
||||||
|
const state = {
|
||||||
|
...stateTemplate,
|
||||||
|
groups: {
|
||||||
|
[userId]: {...groupTemplate, id: userId, member_ids: [], member_count: 0},
|
||||||
|
[groupId]: {...groupTemplate, id: groupId, member_ids: [userId], member_count: 1},
|
||||||
|
},
|
||||||
|
};
|
||||||
|
|
||||||
|
const expectedState = {
|
||||||
|
...stateTemplate,
|
||||||
|
groups: {
|
||||||
|
[userId]: {...groupTemplate, id: userId, member_ids: [], member_count: 0},
|
||||||
|
[groupId]: {...groupTemplate, id: groupId, member_ids: [], member_count: 0},
|
||||||
|
},
|
||||||
|
};
|
||||||
|
|
||||||
|
const newState = reducer(state, receivedMemberToDelAction);
|
||||||
|
expect(newState.groups).toEqual(expectedState.groups);
|
||||||
|
});
|
||||||
|
it('GroupTypes.RECEIVED_MEMBER_TO_REMOVE_FROM_GROUP, Not existant', () => {
|
||||||
|
const state = {
|
||||||
|
...stateTemplate,
|
||||||
|
groups: {
|
||||||
|
[groupId]: {...groupTemplate, id: groupId, member_ids: [groupId], member_count: 1},
|
||||||
|
},
|
||||||
|
};
|
||||||
|
|
||||||
|
const newState = reducer(state, receivedMemberToDelAction);
|
||||||
|
expect(newState.groups).toEqual(state.groups);
|
||||||
|
});
|
||||||
|
});
|
||||||
});
|
});
|
||||||
|
|||||||
@@ -3,7 +3,7 @@
|
|||||||
|
|
||||||
import {combineReducers} from 'redux';
|
import {combineReducers} from 'redux';
|
||||||
|
|
||||||
import type {GroupChannel, GroupSyncablesState, GroupTeam, Group} from '@mattermost/types/groups';
|
import type {GroupChannel, GroupSyncablesState, GroupTeam, Group, GroupMember} from '@mattermost/types/groups';
|
||||||
|
|
||||||
import type {MMReduxAction} from 'mattermost-redux/action_types';
|
import type {MMReduxAction} from 'mattermost-redux/action_types';
|
||||||
import {GroupTypes} from 'mattermost-redux/action_types';
|
import {GroupTypes} from 'mattermost-redux/action_types';
|
||||||
@@ -243,6 +243,46 @@ function groups(state: Record<string, Group> = {}, action: MMReduxAction) {
|
|||||||
|
|
||||||
return nextState;
|
return nextState;
|
||||||
}
|
}
|
||||||
|
case GroupTypes.RECEIVED_MEMBER_TO_REMOVE_FROM_GROUP: {
|
||||||
|
const dataInfo: GroupMember = action.data;
|
||||||
|
|
||||||
|
const group = state[dataInfo.group_id];
|
||||||
|
|
||||||
|
if (Array.isArray(group?.member_ids)) {
|
||||||
|
const newMemberIds = new Set(group.member_ids);
|
||||||
|
newMemberIds.delete(dataInfo.user_id);
|
||||||
|
const newGroup = {...group,
|
||||||
|
member_ids: [...newMemberIds],
|
||||||
|
member_count: newMemberIds.size,
|
||||||
|
};
|
||||||
|
return {
|
||||||
|
...state,
|
||||||
|
[group.id]: newGroup,
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
return state;
|
||||||
|
}
|
||||||
|
case GroupTypes.RECEIVED_MEMBER_TO_ADD_TO_GROUP: {
|
||||||
|
const {group_id: groupId, user_id: userId}: GroupMember = action.data;
|
||||||
|
|
||||||
|
const group = state[groupId];
|
||||||
|
|
||||||
|
if (Array.isArray(group?.member_ids)) {
|
||||||
|
const newMemberIds = new Set(group.member_ids);
|
||||||
|
newMemberIds.add(userId);
|
||||||
|
const newGroup = {...group,
|
||||||
|
member_ids: [...newMemberIds],
|
||||||
|
member_count: newMemberIds.size,
|
||||||
|
};
|
||||||
|
return {
|
||||||
|
...state,
|
||||||
|
[group.id]: newGroup,
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
return state;
|
||||||
|
}
|
||||||
default:
|
default:
|
||||||
return state;
|
return state;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -77,6 +77,7 @@ import type {
|
|||||||
GetGroupsParams,
|
GetGroupsParams,
|
||||||
GetGroupsForUserParams,
|
GetGroupsForUserParams,
|
||||||
GroupStats,
|
GroupStats,
|
||||||
|
GroupMember,
|
||||||
} from '@mattermost/types/groups';
|
} from '@mattermost/types/groups';
|
||||||
import type {PostActionResponse} from '@mattermost/types/integration_actions';
|
import type {PostActionResponse} from '@mattermost/types/integration_actions';
|
||||||
import type {
|
import type {
|
||||||
@@ -3745,14 +3746,14 @@ export default class Client4 {
|
|||||||
};
|
};
|
||||||
|
|
||||||
addUsersToGroup = (groupId: string, userIds: string[]) => {
|
addUsersToGroup = (groupId: string, userIds: string[]) => {
|
||||||
return this.doFetch<UserProfile[]>(
|
return this.doFetch<GroupMember[]>(
|
||||||
`${this.getGroupRoute(groupId)}/members`,
|
`${this.getGroupRoute(groupId)}/members`,
|
||||||
{method: 'post', body: JSON.stringify({user_ids: userIds})},
|
{method: 'post', body: JSON.stringify({user_ids: userIds})},
|
||||||
);
|
);
|
||||||
};
|
};
|
||||||
|
|
||||||
removeUsersFromGroup = (groupId: string, userIds: string[]) => {
|
removeUsersFromGroup = (groupId: string, userIds: string[]) => {
|
||||||
return this.doFetch<UserProfile[]>(
|
return this.doFetch<GroupMember[]>(
|
||||||
`${this.getGroupRoute(groupId)}/members`,
|
`${this.getGroupRoute(groupId)}/members`,
|
||||||
{method: 'delete', body: JSON.stringify({user_ids: userIds})},
|
{method: 'delete', body: JSON.stringify({user_ids: userIds})},
|
||||||
);
|
);
|
||||||
|
|||||||
@@ -182,6 +182,13 @@ export type GroupSearchParams = GetGroupsParams & {
|
|||||||
include_channel_member_count?: string;
|
include_channel_member_count?: string;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
export type GroupMember = {
|
||||||
|
group_id: string;
|
||||||
|
user_id: string;
|
||||||
|
create_at: number;
|
||||||
|
deleted_at: number;
|
||||||
|
}
|
||||||
|
|
||||||
export type GroupMembership = {
|
export type GroupMembership = {
|
||||||
user_id: string;
|
user_id: string;
|
||||||
roles: string;
|
roles: string;
|
||||||
|
|||||||
Ссылка в новой задаче
Block a user