forked from Ivasoft/mattermost-mobile
[Gekidou] Saves groups + group-channel for constrained channels (#6358)
* Rebases and addresses PR feedback * Rebased and addresses PR feedback * Checks group constraint in action instead * Update method docs, parallel promises * Parallel promises, method docs * Cleans up method docs * Method docs cleanup * Update app/database/operator/server_data_operator/handlers/group.ts Co-authored-by: Elias Nahum <nahumhbl@gmail.com>
This commit is contained in:
@@ -27,6 +27,7 @@ import {showMuteChannelSnackbar} from '@utils/snack_bar';
|
||||
import {PERMALINK_GENERIC_TEAM_NAME_REDIRECT} from '@utils/url';
|
||||
import {displayGroupMessageName, displayUsername} from '@utils/user';
|
||||
|
||||
import {fetchGroupsForChannelIfConstrained} from './groups';
|
||||
import {fetchPostsForChannel} from './post';
|
||||
import {setDirectChannelVisible} from './preference';
|
||||
import {fetchRolesIfNeeded} from './role';
|
||||
@@ -1109,6 +1110,7 @@ export async function switchToChannelById(serverUrl: string, channelId: string,
|
||||
setDirectChannelVisible(serverUrl, channelId);
|
||||
markChannelAsRead(serverUrl, channelId);
|
||||
fetchChannelStats(serverUrl, channelId);
|
||||
fetchGroupsForChannelIfConstrained(serverUrl, channelId);
|
||||
|
||||
DeviceEventEmitter.emit(Events.CHANNEL_SWITCH, false);
|
||||
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
// Copyright (c) 2015-present Mattermost, Inc. All Rights Reserved.
|
||||
// See LICENSE.txt for license information.
|
||||
|
||||
import {getChannelById} from '@app/queries/servers/channel';
|
||||
import {Client} from '@client/rest';
|
||||
import DatabaseManager from '@database/manager';
|
||||
import NetworkManager from '@managers/network_manager';
|
||||
@@ -48,7 +49,16 @@ export const fetchGroupsForChannel = async (serverUrl: string, channelId: string
|
||||
const client = NetworkManager.getClient(serverUrl);
|
||||
const response = await client.getAllGroupsAssociatedToChannel(channelId);
|
||||
|
||||
return operator.handleGroups({groups: response.groups, prepareRecordsOnly: fetchOnly});
|
||||
const [groups, groupChannels] = await Promise.all([
|
||||
operator.handleGroups({groups: response.groups, prepareRecordsOnly: true}),
|
||||
operator.handleGroupChannelsForChannel({groups: response.groups, channelId, prepareRecordsOnly: true}),
|
||||
]);
|
||||
|
||||
if (!fetchOnly) {
|
||||
await operator.batchRecords([...groups, ...groupChannels]);
|
||||
}
|
||||
|
||||
return {groups, groupChannels};
|
||||
} catch (error) {
|
||||
forceLogoutIfNecessary(serverUrl, error as ClientErrorProps);
|
||||
return {error};
|
||||
@@ -141,3 +151,18 @@ export const fetchGroupsForTeamIfConstrained = async (serverUrl: string, teamId:
|
||||
return {error};
|
||||
}
|
||||
};
|
||||
|
||||
export const fetchGroupsForChannelIfConstrained = async (serverUrl: string, channelId: string, fetchOnly = false) => {
|
||||
try {
|
||||
const {database} = DatabaseManager.getServerDatabaseAndOperator(serverUrl);
|
||||
const channel = await getChannelById(database, channelId);
|
||||
|
||||
if (channel?.isGroupConstrained) {
|
||||
return fetchGroupsForChannel(serverUrl, channelId, fetchOnly);
|
||||
}
|
||||
|
||||
return {};
|
||||
} catch (error) {
|
||||
return {error};
|
||||
}
|
||||
};
|
||||
|
||||
@@ -2,21 +2,23 @@
|
||||
// See LICENSE.txt for license information.
|
||||
|
||||
import {MM_TABLES} from '@constants/database';
|
||||
import {transformGroupMembershipRecord, transformGroupRecord, transformGroupTeamRecord} from '@database/operator/server_data_operator/transformers/group';
|
||||
import {transformGroupChannelRecord, transformGroupMembershipRecord, transformGroupRecord, transformGroupTeamRecord} from '@database/operator/server_data_operator/transformers/group';
|
||||
import {getUniqueRawsBy} from '@database/operator/utils/general';
|
||||
import {queryGroupMembershipForMember, queryGroupTeamForTeam} from '@queries/servers/group';
|
||||
import {queryGroupChannelForChannel, queryGroupMembershipForMember, queryGroupTeamForTeam} from '@queries/servers/group';
|
||||
import {generateGroupAssociationId} from '@utils/groups';
|
||||
import {logWarning} from '@utils/log';
|
||||
|
||||
import type {HandleGroupArgs, HandleGroupMembershipForMemberArgs, HandleGroupTeamsForTeamArgs} from '@typings/database/database';
|
||||
import type {HandleGroupArgs, HandleGroupChannelsForChannelArgs, HandleGroupMembershipForMemberArgs, HandleGroupTeamsForTeamArgs} from '@typings/database/database';
|
||||
import type GroupModel from '@typings/database/models/servers/group';
|
||||
import type GroupChannelModel from '@typings/database/models/servers/group_channel';
|
||||
import type GroupMembershipModel from '@typings/database/models/servers/group_membership';
|
||||
import type GroupTeamModel from '@typings/database/models/servers/group_team';
|
||||
|
||||
const {GROUP, GROUP_MEMBERSHIP, GROUP_TEAM} = MM_TABLES.SERVER;
|
||||
const {GROUP, GROUP_CHANNEL, GROUP_MEMBERSHIP, GROUP_TEAM} = MM_TABLES.SERVER;
|
||||
|
||||
export interface GroupHandlerMix {
|
||||
handleGroups: ({groups, prepareRecordsOnly}: HandleGroupArgs) => Promise<GroupModel[]>;
|
||||
handleGroupChannelsForChannel: ({channelId, groups, prepareRecordsOnly}: HandleGroupChannelsForChannelArgs) => Promise<GroupChannelModel[]>;
|
||||
handleGroupMembershipsForMember: ({userId, groups, prepareRecordsOnly}: HandleGroupMembershipForMemberArgs) => Promise<GroupMembershipModel[]>;
|
||||
handleGroupTeamsForTeam: ({teamId, groups, prepareRecordsOnly}: HandleGroupTeamsForTeamArgs) => Promise<GroupTeamModel[]>;
|
||||
}
|
||||
@@ -48,6 +50,63 @@ const GroupHandler = (superclass: any) => class extends superclass implements Gr
|
||||
});
|
||||
};
|
||||
|
||||
/**
|
||||
* handleGroupChannelsForChannel: Handler responsible for the Create/Update operations occurring on the GroupChannel table from the 'Server' schema
|
||||
*
|
||||
* @param {HandleGroupChannelsForChannelArgs}
|
||||
* @returns {Promise<GroupChannelModel[]>}
|
||||
*/
|
||||
handleGroupChannelsForChannel = async ({channelId, groups, prepareRecordsOnly = true}: HandleGroupChannelsForChannelArgs): Promise<GroupChannelModel[]> => {
|
||||
// Get existing group channels
|
||||
const existingGroupChannels = await queryGroupChannelForChannel(this.database, channelId).fetch();
|
||||
|
||||
let records: GroupChannelModel[] = [];
|
||||
let rawValues: GroupChannel[] = [];
|
||||
|
||||
// Nothing to add or remove
|
||||
if (!groups?.length && !existingGroupChannels.length) {
|
||||
return records;
|
||||
} else if (!groups?.length && existingGroupChannels.length) { // No groups - remove all existing ones
|
||||
records = existingGroupChannels.map((gt) => gt.prepareDestroyPermanently());
|
||||
} else if (groups?.length && !existingGroupChannels.length) { // No existing groups - add all new ones
|
||||
rawValues = groups.map((g) => ({id: generateGroupAssociationId(g.id, channelId), channel_id: channelId, group_id: g.id}));
|
||||
} else if (groups?.length && existingGroupChannels.length) { // If both, we only want to save new ones and delete one's no longer in groups
|
||||
const groupsSet: {[key: string]: GroupChannel} = {};
|
||||
|
||||
for (const g of groups) {
|
||||
groupsSet[g.id] = {id: generateGroupAssociationId(g.id, channelId), channel_id: channelId, group_id: g.id};
|
||||
}
|
||||
|
||||
for (const gt of existingGroupChannels) {
|
||||
// Check if existingGroups overlaps with groups
|
||||
if (groupsSet[gt.groupId]) {
|
||||
// If there is an existing group already, we don't need to add it
|
||||
delete groupsSet[gt.groupId];
|
||||
} else {
|
||||
// No group? Remove existing one
|
||||
records.push(gt.prepareDestroyPermanently());
|
||||
}
|
||||
}
|
||||
|
||||
rawValues.push(...Object.values(groupsSet));
|
||||
}
|
||||
|
||||
records.push(...(await this.handleRecords({
|
||||
fieldName: 'id',
|
||||
transformer: transformGroupChannelRecord,
|
||||
rawValues,
|
||||
tableName: GROUP_CHANNEL,
|
||||
prepareRecordsOnly: true,
|
||||
})));
|
||||
|
||||
// Batch update if there are records
|
||||
if (records.length && !prepareRecordsOnly) {
|
||||
await this.batchRecords(records);
|
||||
}
|
||||
|
||||
return records;
|
||||
};
|
||||
|
||||
/**
|
||||
* handleGroupMembershipsForMember: Handler responsible for the Create/Update operations occurring on the GroupMembership table from the 'Server' schema
|
||||
*
|
||||
|
||||
@@ -9,11 +9,13 @@ import {generateGroupAssociationId} from '@utils/groups';
|
||||
|
||||
import type {TransformerArgs} from '@typings/database/database';
|
||||
import type GroupModel from '@typings/database/models/servers/group';
|
||||
import type GroupChannelModel from '@typings/database/models/servers/group_channel';
|
||||
import type GroupMembershipModel from '@typings/database/models/servers/group_membership';
|
||||
import type GroupTeamModel from '@typings/database/models/servers/group_team';
|
||||
|
||||
const {
|
||||
GROUP,
|
||||
GROUP_CHANNEL,
|
||||
GROUP_MEMBERSHIP,
|
||||
GROUP_TEAM,
|
||||
} = MM_TABLES.SERVER;
|
||||
@@ -49,6 +51,32 @@ export const transformGroupRecord = ({action, database, value}: TransformerArgs)
|
||||
}) as Promise<GroupModel>;
|
||||
};
|
||||
|
||||
/**
|
||||
* transformGroupChannelRecord: Prepares a record of the SERVER database 'GroupChannel' table for update or create actions.
|
||||
* @param {TransformerArgs} operator
|
||||
* @param {Database} operator.database
|
||||
* @param {RecordPair} operator.value
|
||||
* @returns {Promise<GroupChannelModel>}
|
||||
*/
|
||||
export const transformGroupChannelRecord = ({action, database, value}: TransformerArgs): Promise<GroupChannelModel> => {
|
||||
const raw = value.raw as GroupChannel;
|
||||
|
||||
// id of group comes from server response
|
||||
const fieldsMapper = (model: GroupChannelModel) => {
|
||||
model._raw.id = raw.id || generateGroupAssociationId(raw.group_id, raw.channel_id);
|
||||
model.groupId = raw.group_id;
|
||||
model.channelId = raw.channel_id;
|
||||
};
|
||||
|
||||
return prepareBaseRecord({
|
||||
action,
|
||||
database,
|
||||
tableName: GROUP_CHANNEL,
|
||||
value,
|
||||
fieldsMapper,
|
||||
}) as Promise<GroupChannelModel>;
|
||||
};
|
||||
|
||||
/**
|
||||
* transformGroupMembershipRecord: Prepares a record of the SERVER database 'GroupMembership' table for update or create actions.
|
||||
* @param {TransformerArgs} operator
|
||||
|
||||
@@ -6,6 +6,7 @@ import {Database, Q} from '@nozbe/watermelondb';
|
||||
import {MM_TABLES} from '@constants/database';
|
||||
|
||||
import type GroupModel from '@typings/database/models/servers/group';
|
||||
import type GroupChannelModel from '@typings/database/models/servers/group_channel';
|
||||
import type GroupMembershipModel from '@typings/database/models/servers/group_membership';
|
||||
import type GroupTeamModel from '@typings/database/models/servers/group_team';
|
||||
|
||||
@@ -37,6 +38,12 @@ export const queryGroupsByNameInChannel = (database: Database, name: string, cha
|
||||
);
|
||||
};
|
||||
|
||||
export const queryGroupChannelForChannel = (database: Database, channelId: string) => {
|
||||
return database.collections.get<GroupChannelModel>(GROUP_CHANNEL).query(
|
||||
Q.where('channel_id', channelId),
|
||||
);
|
||||
};
|
||||
|
||||
export const queryGroupMembershipForMember = (database: Database, userId: string) => {
|
||||
return database.collections.get<GroupMembershipModel>(GROUP_MEMBERSHIP).query(
|
||||
Q.where('user_id', userId),
|
||||
|
||||
12
types/api/groups.d.ts
vendored
12
types/api/groups.d.ts
vendored
@@ -22,19 +22,9 @@ type GroupTeam = {
|
||||
}
|
||||
|
||||
type GroupChannel = {
|
||||
id?: string;
|
||||
channel_id: string;
|
||||
channel_display_name: string;
|
||||
channel_type: string;
|
||||
team_id: string;
|
||||
team_display_name: string;
|
||||
team_type: string;
|
||||
group_id: string;
|
||||
auto_add: boolean;
|
||||
member_count?: number;
|
||||
timezone_count?: number;
|
||||
create_at: number;
|
||||
delete_at: number;
|
||||
update_at: number;
|
||||
}
|
||||
|
||||
type GroupMembership = {
|
||||
|
||||
5
types/database/database.d.ts
vendored
5
types/database/database.d.ts
vendored
@@ -222,6 +222,11 @@ export type HandleGroupArgs = PrepareOnly & {
|
||||
groups?: Group[];
|
||||
};
|
||||
|
||||
export type HandleGroupChannelsForChannelArgs = PrepareOnly & {
|
||||
channelId: string;
|
||||
groups?: Group[];
|
||||
}
|
||||
|
||||
export type HandleGroupMembershipForMemberArgs = PrepareOnly & {
|
||||
userId: string;
|
||||
groups?: Group[];
|
||||
|
||||
Reference in New Issue
Block a user