Files
mattermost-mobile/app/queries/servers/channel.ts
Elias Nahum fe916ec740 Refactor category channels to react to setting changes and apply the correct order (#7170)
* Refactor category channels to react to setting changes and apply the correct order

* feedback review
2023-03-03 15:53:29 +02:00

719 lines
27 KiB
TypeScript

// Copyright (c) 2015-present Mattermost, Inc. All Rights Reserved.
// See LICENSE.txt for license information.
/* eslint-disable max-lines */
import {Database, Model, Q, Query, Relation} from '@nozbe/watermelondb';
import {of as of$, Observable} from 'rxjs';
import {map as map$, switchMap, distinctUntilChanged} from 'rxjs/operators';
import {General, Permissions} from '@constants';
import {MM_TABLES} from '@constants/database';
import {sanitizeLikeString} from '@helpers/database';
import {hasPermission} from '@utils/role';
import {getUserIdFromChannelName} from '@utils/user';
import {prepareDeletePost} from './post';
import {queryRoles} from './role';
import {observeCurrentChannelId, getCurrentChannelId, observeCurrentUserId} from './system';
import {observeTeammateNameDisplay} from './user';
import type ServerDataOperator from '@database/operator/server_data_operator';
import type {Clause} from '@nozbe/watermelondb/QueryDescription';
import type ChannelModel from '@typings/database/models/servers/channel';
import type ChannelInfoModel from '@typings/database/models/servers/channel_info';
import type ChannelMembershipModel from '@typings/database/models/servers/channel_membership';
import type MyChannelModel from '@typings/database/models/servers/my_channel';
import type MyChannelSettingsModel from '@typings/database/models/servers/my_channel_settings';
import type UserModel from '@typings/database/models/servers/user';
const {SERVER: {CHANNEL, MY_CHANNEL, CHANNEL_MEMBERSHIP, MY_CHANNEL_SETTINGS, CHANNEL_INFO, USER, TEAM}} = MM_TABLES;
export function prepareMissingChannelsForAllTeams(operator: ServerDataOperator, channels: Channel[], channelMembers: ChannelMembership[], isCRTEnabled?: boolean): Array<Promise<Model[]>> {
const channelInfos: ChannelInfo[] = [];
const channelMap: Record<string, Channel> = {};
for (const c of channels) {
channelMap[c.id] = c;
channelInfos.push({
id: c.id,
header: c.header,
purpose: c.purpose,
guest_count: 0,
member_count: 0,
pinned_post_count: 0,
});
}
const memberships = channelMembers.map((cm) => {
const channel = channelMap[cm.channel_id];
return {
...cm,
id: cm.channel_id,
last_post_at: channel.last_post_at,
last_root_post_at: channel.last_root_post_at,
};
});
try {
const channelRecords = operator.handleChannel({channels, prepareRecordsOnly: true});
const channelInfoRecords = operator.handleChannelInfo({channelInfos, prepareRecordsOnly: true});
const membershipRecords = operator.handleChannelMembership({channelMemberships: memberships, prepareRecordsOnly: true});
const myChannelRecords = operator.handleMyChannel({channels, myChannels: memberships, prepareRecordsOnly: true, isCRTEnabled});
const myChannelSettingsRecords = operator.handleMyChannelSettings({settings: memberships, prepareRecordsOnly: true});
return [channelRecords, channelInfoRecords, membershipRecords, myChannelRecords, myChannelSettingsRecords];
} catch {
return [];
}
}
export const prepareMyChannelsForTeam = async (operator: ServerDataOperator, teamId: string, channels: Channel[], channelMembers: ChannelMembership[], isCRTEnabled?: boolean, isGraphQL = false) => {
const {database} = operator;
const channelsQuery = isGraphQL ? queryAllChannels(database) : queryAllChannelsForTeam(database, teamId);
const allChannelsForTeam = (await channelsQuery.fetch()).
reduce((map: Record<string, ChannelModel>, channel) => {
map[channel.id] = channel;
return map;
}, {});
const channelInfosQuery = isGraphQL ? queryAllChannelsInfo(database) : queryAllChannelsInfoForTeam(database, teamId);
const allChannelsInfoForTeam = (await channelInfosQuery.fetch()).
reduce((map: Record<string, ChannelInfoModel>, info) => {
map[info.id] = info;
return map;
}, {});
const channelInfos: ChannelInfo[] = [];
const memberships = channelMembers.map((cm) => {
return {...cm, id: cm.channel_id};
});
for (const c of channels) {
const storedChannel = allChannelsForTeam[c.id];
let storedInfo: ChannelInfoModel | undefined;
let member_count = 0;
let guest_count = 0;
let pinned_post_count = 0;
if (storedChannel) {
storedInfo = allChannelsInfoForTeam[c.id];
if (storedInfo) {
member_count = storedInfo.memberCount;
guest_count = storedInfo.guestCount;
pinned_post_count = storedInfo.pinnedPostCount;
}
}
channelInfos.push({
id: c.id,
header: c.header,
purpose: c.purpose,
guest_count,
member_count,
pinned_post_count,
});
}
try {
const channelRecords = operator.handleChannel({channels, prepareRecordsOnly: true});
const channelInfoRecords = operator.handleChannelInfo({channelInfos, prepareRecordsOnly: true});
const membershipRecords = operator.handleChannelMembership({channelMemberships: channelMembers, prepareRecordsOnly: true});
const myChannelRecords = operator.handleMyChannel({channels, myChannels: memberships, prepareRecordsOnly: true, isCRTEnabled});
const myChannelSettingsRecords = operator.handleMyChannelSettings({settings: memberships, prepareRecordsOnly: true});
return [channelRecords, channelInfoRecords, membershipRecords, myChannelRecords, myChannelSettingsRecords];
} catch {
return [];
}
};
export const prepareDeleteChannel = async (channel: ChannelModel): Promise<Model[]> => {
const preparedModels: Model[] = [channel.prepareDestroyPermanently()];
const relations: Array<Relation<Model|MyChannelModel> | undefined> = [channel.membership, channel.info, channel.categoryChannel];
await Promise.all(relations.map(async (relation) => {
try {
const model = await relation?.fetch();
if (model) {
preparedModels.push(model.prepareDestroyPermanently());
if ('settings' in model) {
const settings = await model.settings?.fetch();
if (settings) {
preparedModels.push(settings.prepareDestroyPermanently());
}
}
}
} catch {
// Record not found, do nothing
}
}));
const associatedChildren: Array<Query<Model> | undefined> = [
channel.members,
channel.drafts,
channel.postsInChannel,
];
await Promise.all(associatedChildren.map(async (children) => {
const models = await children?.fetch();
models?.forEach((model) => preparedModels.push(model.prepareDestroyPermanently()));
}));
const posts = await channel.posts?.fetch();
if (posts?.length) {
for await (const post of posts) {
const preparedPost = await prepareDeletePost(post);
preparedModels.push(...preparedPost);
}
}
return preparedModels;
};
export const queryAllChannels = (database: Database) => {
return database.get<ChannelModel>(CHANNEL).query();
};
export const queryAllChannelsForTeam = (database: Database, teamId: string) => {
return database.get<ChannelModel>(CHANNEL).query(Q.where('team_id', teamId));
};
export const queryAllChannelsInfo = (database: Database) => {
return database.get<ChannelInfoModel>(CHANNEL_INFO).query();
};
export const queryAllChannelsInfoForTeam = (database: Database, teamId: string) => {
return database.get<ChannelInfoModel>(CHANNEL_INFO).query(
Q.on(CHANNEL, Q.where('team_id', teamId)),
);
};
export const queryAllMyChannel = (database: Database) => {
return database.get<MyChannelModel>(MY_CHANNEL).query();
};
export const queryAllMyChannelsForTeam = (database: Database, teamId: string) => {
return database.get<MyChannelModel>(MY_CHANNEL).query(
Q.on(CHANNEL, Q.where('team_id', Q.oneOf([teamId, '']))),
);
};
export const getMyChannel = async (database: Database, channelId: string) => {
try {
const member = await database.get<MyChannelModel>(MY_CHANNEL).find(channelId);
return member;
} catch {
return undefined;
}
};
export const observeMyChannel = (database: Database, channelId: string) => {
return database.get<MyChannelModel>(MY_CHANNEL).query(Q.where('id', channelId), Q.take(1)).observe().pipe(
switchMap((result) => (result.length ? result[0].observe() : of$(undefined))),
);
};
export const observeMyChannelRoles = (database: Database, channelId: string) => {
return observeMyChannel(database, channelId).pipe(
switchMap((v) => of$(v?.roles)),
distinctUntilChanged(),
);
};
export const getChannelById = async (database: Database, channelId: string) => {
try {
const channel = await database.get<ChannelModel>(CHANNEL).find(channelId);
return channel;
} catch {
return undefined;
}
};
export const observeChannel = (database: Database, channelId: string) => {
return database.get<ChannelModel>(CHANNEL).query(Q.where('id', channelId), Q.take(1)).observe().pipe(
switchMap((result) => (result.length ? result[0].observe() : of$(undefined))),
);
};
export const getChannelByName = async (database: Database, teamId: string, channelName: string) => {
const clauses: Clause[] = [];
if (teamId) {
clauses.push(Q.on(TEAM, 'id', teamId));
}
clauses.push(Q.where('name', channelName));
const channels = await database.get<ChannelModel>(CHANNEL).query(...clauses).fetch();
// Check done to force types
if (channels.length) {
return channels[0];
}
return undefined;
};
export const queryChannelsById = (database: Database, channelIds: string[]) => {
return database.get<ChannelModel>(CHANNEL).query(Q.where('id', Q.oneOf(channelIds)));
};
export const getChannelInfo = async (database: Database, channelId: string) => {
try {
const info = await database.get<ChannelInfoModel>(CHANNEL_INFO).find(channelId);
return info;
} catch {
return undefined;
}
};
export const getDefaultChannelForTeam = async (database: Database, teamId: string, ignoreId?: string) => {
let channel: ChannelModel|undefined;
let canIJoinPublicChannelsInTeam = false;
const roles = await queryRoles(database).fetch();
if (roles.length) {
canIJoinPublicChannelsInTeam = hasPermission(roles, Permissions.JOIN_PUBLIC_CHANNELS);
}
const clauses = [
Q.where('team_id', teamId),
Q.where('delete_at', Q.eq(0)),
Q.where('type', General.OPEN_CHANNEL),
];
if (ignoreId) {
clauses.push(Q.where('channel_id', Q.notEq(ignoreId)));
}
const myChannels = await database.get<ChannelModel>(CHANNEL).query(
Q.on(MY_CHANNEL, 'id', Q.notEq('')),
Q.and(...clauses),
Q.sortBy('display_name', Q.asc),
).fetch();
const defaultChannel = myChannels.find((c) => c.name === General.DEFAULT_CHANNEL);
const myFirstTeamChannel = myChannels[0];
if (defaultChannel || canIJoinPublicChannelsInTeam) {
channel = defaultChannel;
} else {
channel = myFirstTeamChannel || defaultChannel;
}
return channel;
};
export const getCurrentChannel = async (database: Database) => {
const currentChannelId = await getCurrentChannelId(database);
if (currentChannelId) {
return getChannelById(database, currentChannelId);
}
return undefined;
};
export const getCurrentChannelInfo = async (database: Database) => {
const currentChannelId = await getCurrentChannelId(database);
if (currentChannelId) {
const info = await getChannelInfo(database, currentChannelId);
return info;
}
return undefined;
};
export const observeCurrentChannel = (database: Database) => {
return observeCurrentChannelId(database).pipe(
switchMap((id) => database.get<ChannelModel>(CHANNEL).query(Q.where('id', id), Q.take(1)).observe().pipe(
switchMap((result) => (result.length ? result[0].observe() : of$(undefined))),
),
));
};
export async function deleteChannelMembership(operator: ServerDataOperator, userId: string, channelId: string, prepareRecordsOnly = false) {
try {
const channelMembership = await operator.database.get<ChannelMembershipModel>(CHANNEL_MEMBERSHIP).query(Q.where('user_id', Q.eq(userId)), Q.where('channel_id', Q.eq(channelId))).fetch();
const models: Model[] = [];
for (const membership of channelMembership) {
models.push(membership.prepareDestroyPermanently());
}
if (models.length && !prepareRecordsOnly) {
await operator.batchRecords(models, 'deleteChannelMembership');
}
return {models};
} catch (error) {
return {error};
}
}
export const addChannelMembership = async (operator: ServerDataOperator, userId: string, channelId: string) => {
try {
await operator.handleChannelMembership({channelMemberships: [{channel_id: channelId, user_id: userId}], prepareRecordsOnly: false});
return {};
} catch (error) {
return {error};
}
};
export const queryUsersOnChannel = (database: Database, channelId: string) => {
return database.get<UserModel>(USER).query(Q.on(CHANNEL_MEMBERSHIP, Q.where('channel_id', channelId)));
};
export const getMembersCountByChannelsId = async (database: Database, channelsId: string[]) => {
const result = channelsId.reduce((r: Record<string, number>, cId) => {
r[cId] = 0;
return r;
}, {});
const q = await database.get<ChannelMembershipModel>(CHANNEL_MEMBERSHIP).query(Q.where('channel_id', Q.oneOf(channelsId))).fetch();
return q.reduce((r: Record<string, number>, m) => {
if (r[m.channelId]) {
r[m.channelId] += 1;
return r;
}
r[m.channelId] = 1;
return r;
}, result);
};
export const queryChannelsByTypes = (database: Database, channelTypes: ChannelType[]) => {
return database.get<ChannelModel>(CHANNEL).query(
Q.where('type', Q.oneOf(channelTypes)));
};
export const queryUserChannelsByTypes = (database: Database, userId: string, channelTypes: ChannelType[]) => {
return database.get<ChannelModel>(CHANNEL).query(
Q.where('type', Q.oneOf(channelTypes)),
Q.on(CHANNEL_MEMBERSHIP, Q.where('user_id', userId)));
};
export const queryTeamDefaultChannel = (database: Database, teamId: string) => {
return database.get<ChannelModel>(CHANNEL).query(
Q.where('team_id', teamId),
Q.where('name', General.DEFAULT_CHANNEL),
);
};
export const queryMyChannelsByTeam = (database: Database, teamId: string, includeDeleted = false) => {
const conditions: Q.Where[] = [Q.where('team_id', Q.eq(teamId))];
if (!includeDeleted) {
conditions.push(Q.where('delete_at', Q.eq(0)));
}
return database.get<MyChannelModel>(MY_CHANNEL).query(
Q.on(CHANNEL, Q.and(
...conditions,
)),
);
};
export const observeChannelInfo = (database: Database, channelId: string) => {
return database.get<ChannelInfoModel>(CHANNEL_INFO).query(Q.where('id', channelId), Q.take(1)).observe().pipe(
switchMap((result) => (result.length ? result[0].observe() : of$(undefined))),
);
};
export const queryAllMyChannelSettings = (database: Database) => {
return database.get<MyChannelSettingsModel>(MY_CHANNEL_SETTINGS).query();
};
export const queryMyChannelSettingsByIds = (database: Database, ids: string[]) => {
return database.get<MyChannelSettingsModel>(MY_CHANNEL_SETTINGS).
query(
Q.where('id', Q.oneOf(ids)),
);
};
export const observeAllMyChannelNotifyProps = (database: Database) => {
return queryAllMyChannelSettings(database).observeWithColumns(['notify_props']).pipe(
map$((settings) => settings.reduce<Record<string, Partial<ChannelNotifyProps>>>((obj, setting) => {
obj[setting.id] = setting.notifyProps;
return obj;
}, {})),
);
};
export const observeNotifyPropsByChannels = (database: Database, channels: ChannelModel[]|MyChannelModel[]) => {
const ids = channels.map((c) => c.id);
return queryMyChannelSettingsByIds(database, ids).observeWithColumns(['notify_props']).pipe(
map$((settings) => settings.reduce<Record<string, Partial<ChannelNotifyProps>>>((obj, setting) => {
obj[setting.id] = setting.notifyProps;
return obj;
}, {})),
);
};
export const queryMyChannelUnreads = (database: Database, currentTeamId: string) => {
return database.get<MyChannelModel>(MY_CHANNEL).query(
Q.on(
CHANNEL,
Q.and(
Q.or(
Q.where('team_id', Q.eq(currentTeamId)),
Q.where('team_id', Q.eq('')),
),
Q.where('delete_at', Q.eq(0)),
),
),
Q.or(
Q.where('is_unread', Q.eq(true)),
Q.where('mentions_count', Q.gte(0)),
),
Q.sortBy('last_post_at', Q.desc),
);
};
export const observeArchivedDirectChannels = (database: Database, currentUserId: string) => {
const deactivated = database.get<UserModel>(USER).query(
Q.where('delete_at', Q.gt(0)),
).observe();
return deactivated.pipe(
switchMap((users) => {
const usersMap = new Map(users.map((u) => [u.id, u]));
return database.get<ChannelModel>(CHANNEL).query(
Q.on(
CHANNEL_MEMBERSHIP,
Q.and(
Q.where('user_id', Q.notEq(currentUserId)),
Q.where('user_id', Q.oneOf(Array.from(usersMap.keys()))),
),
),
Q.where('type', 'D'),
).observe().pipe(
switchMap((channels) => {
// eslint-disable-next-line max-nested-callbacks
return of$(new Map(channels.map((c) => {
const teammateId = getUserIdFromChannelName(currentUserId, c.name);
const user = usersMap.get(teammateId);
return [c.id, user];
})));
}),
);
}),
);
};
export function observeMyChannelMentionCount(database: Database, teamId?: string, columns = ['mentions_count', 'is_unread']): Observable<number> {
const conditions: Q.Where[] = [
Q.where('delete_at', Q.eq(0)),
];
if (teamId) {
conditions.push(Q.where('team_id', Q.eq(teamId)));
}
return database.get<MyChannelModel>(MY_CHANNEL).query(
Q.on(CHANNEL, Q.and(
...conditions,
)),
Q.on(MY_CHANNEL_SETTINGS, Q.where('notify_props', Q.notLike('%"mark_unread":"mention"%'))),
).
observeWithColumns(columns).
pipe(
switchMap((val) => of$(val.reduce((acc, v) => {
return acc + v.mentionsCount;
}, 0))),
distinctUntilChanged(),
);
}
export function queryMyRecentChannels(database: Database, take: number) {
const count: Q.Clause[] = [];
if (take > 0) {
count.push(Q.take(take));
}
return queryAllMyChannel(database).extend(
Q.on(CHANNEL, Q.where('delete_at', Q.eq(0))),
Q.sortBy('last_viewed_at', Q.desc),
...count,
);
}
export const observeDirectChannelsByTerm = (database: Database, term: string, take = 20, matchStart = false) => {
const onlyDMs = term.startsWith('@') ? "AND c.type='D'" : '';
const value = sanitizeLikeString(term.startsWith('@') ? term.substring(1) : term);
let username = `u.username LIKE '${value}%'`;
let displayname = `c.display_name LIKE '${value}%'`;
if (!matchStart) {
username = `u.username LIKE '%${value}%' AND u.username NOT LIKE '${value}%'`;
displayname = `(c.display_name LIKE '%${value}%' AND c.display_name NOT LIKE '${value}%')`;
}
const currentUserId = observeCurrentUserId(database);
return currentUserId.pipe(
switchMap((uId) => {
return database.get<MyChannelModel>(MY_CHANNEL).query(
Q.unsafeSqlQuery(`SELECT DISTINCT my.* FROM ${MY_CHANNEL} my
INNER JOIN ${CHANNEL} c ON c.id=my.id AND c.team_id='' AND c.delete_at=0 ${onlyDMs}
INNER JOIN ${CHANNEL_MEMBERSHIP} cm ON cm.channel_id=my.id
LEFT JOIN ${USER} u ON u.id=cm.user_id AND (CASE WHEN c.type = 'D' THEN cm.user_id != '${uId}' ELSE 1 END)
WHERE ${displayname} OR CASE WHEN c.type = 'G' THEN 0 ELSE ${username} END
ORDER BY CASE c.type WHEN 'D' THEN 0 ELSE 1 END ASC, my.last_viewed_at DESC
LIMIT ${take}`),
).observe();
}),
);
};
export const observeNotDirectChannelsByTerm = (database: Database, term: string, take = 20, matchStart = false) => {
const teammateNameSetting = observeTeammateNameDisplay(database);
const value = sanitizeLikeString(term.startsWith('@') ? term.substring(1) : term);
let username = `u.username LIKE '${value}%'`;
let nickname = `u.nickname LIKE '${value}%'`;
let displayname = `(u.first_name || ' ' || u.last_name) LIKE '${value}%'`;
if (!matchStart) {
username = `(u.username LIKE '%${value}%' AND u.username NOT LIKE '${value}%')`;
nickname = `(u.nickname LIKE '%${value}%' AND u.nickname NOT LIKE '${value}%')`;
displayname = `((u.first_name || ' ' || u.last_name) LIKE '%${value}%' AND (u.first_name || ' ' || u.last_name) NOT LIKE '${value}%')`;
}
return teammateNameSetting.pipe(
switchMap((setting) => {
let sortBy = '';
switch (setting) {
case General.TEAMMATE_NAME_DISPLAY.SHOW_NICKNAME_FULLNAME:
sortBy = "ORDER BY CASE u.nickname WHEN '' THEN 1 ELSE 0 END, CASE (u.first_name || ' ' || u.last_name) WHEN '' THEN 1 ELSE 0 END, u.username";
break;
case General.TEAMMATE_NAME_DISPLAY.SHOW_FULLNAME:
sortBy = "ORDER BY CASE (u.first_name || ' ' || u.last_name) WHEN '' THEN 1 ELSE 0 END, u.username";
break;
default:
sortBy = 'ORDER BY u.username';
break;
}
return database.get<UserModel>(USER).query(
Q.unsafeSqlQuery(`SELECT DISTINCT u.* FROM User u
LEFT JOIN ChannelMembership cm ON cm.user_id=u.id
LEFT JOIN Channel c ON c.id=cm.id AND c.type='${General.DM_CHANNEL}'
WHERE cm.user_id IS NULL AND (${displayname} OR ${username} OR ${nickname}) AND u.delete_at=0
${sortBy} LIMIT ${take}`),
).observe();
}),
);
};
export const observeJoinedChannelsByTerm = (database: Database, term: string, take = 20, matchStart = false) => {
if (term.startsWith('@')) {
return of$([]);
}
const value = sanitizeLikeString(term);
let displayname = `c.display_name LIKE '${value}%'`;
if (!matchStart) {
displayname = `c.display_name LIKE '%${value}%' AND c.display_name NOT LIKE '${value}%'`;
}
return database.get<MyChannelModel>(MY_CHANNEL).query(
Q.unsafeSqlQuery(`SELECT DISTINCT my.* FROM ${MY_CHANNEL} my
INNER JOIN ${CHANNEL} c ON c.id=my.id AND c.delete_at=0 AND c.team_id !='' AND ${displayname}
ORDER BY my.last_viewed_at DESC
LIMIT ${take}`),
).observe();
};
export const observeArchiveChannelsByTerm = (database: Database, term: string, take = 20) => {
if (term.startsWith('@')) {
return of$([]);
}
const value = sanitizeLikeString(term);
const displayname = `%${value}%`;
return database.get<MyChannelModel>(MY_CHANNEL).query(
Q.on(CHANNEL, Q.and(
Q.where('delete_at', Q.gt(0)),
Q.where('team_id', Q.notEq('')),
Q.where('display_name', Q.like(displayname)),
)),
Q.sortBy('last_viewed_at'),
Q.take(take),
).observe();
};
export const observeChannelSettings = (database: Database, channelId: string) => {
return database.get<MyChannelSettingsModel>(MY_CHANNEL_SETTINGS).query(Q.where('id', channelId), Q.take(1)).observe().pipe(
switchMap((result) => (result.length ? result[0].observe() : of$(undefined))),
);
};
export const observeIsMutedSetting = (database: Database, channelId: string) => {
return observeChannelSettings(database, channelId).pipe(switchMap((s) => of$(s?.notifyProps?.mark_unread === General.MENTION)));
};
export const observeChannelsByLastPostAt = (database: Database, myChannels: MyChannelModel[]) => {
const ids = myChannels.map((c) => c.id);
const idsStr = `'${ids.join("','")}'`;
return database.get<ChannelModel>(CHANNEL).query(
Q.unsafeSqlQuery(`SELECT DISTINCT c.* FROM ${CHANNEL} c INNER JOIN
${MY_CHANNEL} mc ON mc.id=c.id AND c.id IN (${idsStr})
ORDER BY CASE mc.last_post_at WHEN 0 THEN c.create_at ELSE mc.last_post_at END DESC`),
).observe();
};
export const queryChannelsForAutocomplete = (database: Database, matchTerm: string, isSearch: boolean, teamId: string) => {
const likeTerm = `%${sanitizeLikeString(matchTerm)}%`;
const clauses: Q.Clause[] = [];
if (isSearch) {
clauses.push(
Q.experimentalJoinTables([CHANNEL_MEMBERSHIP]),
Q.experimentalNestedJoin(CHANNEL_MEMBERSHIP, USER),
);
}
const orConditions: Q.Where[] = [
Q.where('display_name', Q.like(matchTerm)),
Q.where('name', Q.like(likeTerm)),
];
if (isSearch) {
orConditions.push(
Q.and(
Q.where('type', Q.oneOf([General.DM_CHANNEL, General.GM_CHANNEL])),
Q.on(CHANNEL_MEMBERSHIP, Q.on(USER,
Q.or(
// eslint-disable-next-line @typescript-eslint/ban-ts-comment
// @ts-ignore: condition type error
Q.unsafeSqlExpr(`first_name || ' ' || last_name LIKE '${likeTerm}'`),
Q.where('nickname', Q.like(likeTerm)),
Q.where('email', Q.like(likeTerm)),
Q.where('username', Q.like(likeTerm)),
),
)),
),
);
}
const teamsToSearch = [teamId];
if (isSearch) {
teamsToSearch.push('');
}
const andConditions: Q.Where[] = [
Q.where('team_id', Q.oneOf(teamsToSearch)),
];
if (!isSearch) {
andConditions.push(
Q.where('type', Q.oneOf([General.OPEN_CHANNEL, General.PRIVATE_CHANNEL])),
Q.where('delete_at', 0),
);
}
clauses.push(
...andConditions,
Q.or(...orConditions),
Q.sortBy('display_name', Q.asc),
Q.sortBy('name', Q.asc),
Q.take(25),
);
return database.get<ChannelModel>(CHANNEL).query(...clauses);
};
export const queryChannelMembers = (database: Database, channelId: string) => {
return database.get<ChannelMembershipModel>(CHANNEL_MEMBERSHIP).query(
Q.where('channel_id', channelId),
);
};
export const observeChannelMembers = (database: Database, channelId: string) => {
return queryChannelMembers(database, channelId).observe();
};