[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) {
|
||||
return async (doDispatch, doGetState) => {
|
||||
const state = doGetState();
|
||||
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) {
|
||||
const group = getGroup(state, groupInfo.group_id);
|
||||
if (currentUserId === groupMember.user_id) {
|
||||
const group = getGroup(state, groupMember.group_id);
|
||||
if (group) {
|
||||
dispatch(
|
||||
{
|
||||
type: GroupTypes.ADD_MY_GROUP,
|
||||
id: groupInfo.group_id,
|
||||
},
|
||||
);
|
||||
handleMyGroupUpdate(groupMember);
|
||||
} else {
|
||||
const {error} = await doDispatch(fetchGroup(groupInfo.group_id, true));
|
||||
const {error} = await doDispatch(fetchGroup(groupMember.group_id, true));
|
||||
if (!error) {
|
||||
dispatch(
|
||||
{
|
||||
type: GroupTypes.ADD_MY_GROUP,
|
||||
id: groupInfo.group_id,
|
||||
},
|
||||
);
|
||||
handleMyGroupUpdate(groupMember);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1486,13 +1495,23 @@ function handleGroupDeletedMemberEvent(msg) {
|
||||
const data = JSON.parse(msg.data.group_member);
|
||||
|
||||
if (currentUserId === data.user_id) {
|
||||
dispatch(
|
||||
dispatch(batchActions([
|
||||
{
|
||||
type: GroupTypes.REMOVE_MY_GROUP,
|
||||
data,
|
||||
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));
|
||||
expect(store.dispatch).toHaveBeenCalledWith({
|
||||
type: 'ADD_MY_GROUP',
|
||||
id: 'group-1',
|
||||
meta: {batch: true},
|
||||
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_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_CHANNEL: null,
|
||||
|
||||
|
||||
@@ -4,9 +4,8 @@
|
||||
import type {AnyAction} from 'redux';
|
||||
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 type {UserProfile} from '@mattermost/types/users';
|
||||
|
||||
import {ChannelTypes, GroupTypes, UserTypes} from 'mattermost-redux/action_types';
|
||||
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) => {
|
||||
let data;
|
||||
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) => {
|
||||
let data;
|
||||
try {
|
||||
|
||||
@@ -1,6 +1,8 @@
|
||||
// Copyright (c) 2015-present Mattermost, Inc. All Rights Reserved.
|
||||
// See LICENSE.txt for license information.
|
||||
|
||||
import type {Group} from '@mattermost/types/groups';
|
||||
|
||||
import {GroupTypes} from 'mattermost-redux/action_types';
|
||||
import reducer from 'mattermost-redux/reducers/entities/groups';
|
||||
|
||||
@@ -341,4 +343,100 @@ describe('reducers/entities/groups', () => {
|
||||
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 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 {GroupTypes} from 'mattermost-redux/action_types';
|
||||
@@ -243,6 +243,46 @@ function groups(state: Record<string, Group> = {}, action: MMReduxAction) {
|
||||
|
||||
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:
|
||||
return state;
|
||||
}
|
||||
|
||||
@@ -77,6 +77,7 @@ import type {
|
||||
GetGroupsParams,
|
||||
GetGroupsForUserParams,
|
||||
GroupStats,
|
||||
GroupMember,
|
||||
} from '@mattermost/types/groups';
|
||||
import type {PostActionResponse} from '@mattermost/types/integration_actions';
|
||||
import type {
|
||||
@@ -3745,14 +3746,14 @@ export default class Client4 {
|
||||
};
|
||||
|
||||
addUsersToGroup = (groupId: string, userIds: string[]) => {
|
||||
return this.doFetch<UserProfile[]>(
|
||||
return this.doFetch<GroupMember[]>(
|
||||
`${this.getGroupRoute(groupId)}/members`,
|
||||
{method: 'post', body: JSON.stringify({user_ids: userIds})},
|
||||
);
|
||||
};
|
||||
|
||||
removeUsersFromGroup = (groupId: string, userIds: string[]) => {
|
||||
return this.doFetch<UserProfile[]>(
|
||||
return this.doFetch<GroupMember[]>(
|
||||
`${this.getGroupRoute(groupId)}/members`,
|
||||
{method: 'delete', body: JSON.stringify({user_ids: userIds})},
|
||||
);
|
||||
|
||||
@@ -182,6 +182,13 @@ export type GroupSearchParams = GetGroupsParams & {
|
||||
include_channel_member_count?: string;
|
||||
}
|
||||
|
||||
export type GroupMember = {
|
||||
group_id: string;
|
||||
user_id: string;
|
||||
create_at: number;
|
||||
deleted_at: number;
|
||||
}
|
||||
|
||||
export type GroupMembership = {
|
||||
user_id: string;
|
||||
roles: string;
|
||||
|
||||
Ссылка в новой задаче
Block a user