diff --git a/.changeset/wet-tomatoes-happen.md b/.changeset/wet-tomatoes-happen.md new file mode 100644 index 00000000000..fc9c9f1bdab --- /dev/null +++ b/.changeset/wet-tomatoes-happen.md @@ -0,0 +1,6 @@ +--- +"@rocket.chat/meteor": patch +"@rocket.chat/model-typings": patch +--- + +fix: "Discussions" filter is prioritized in admin "Rooms" page diff --git a/apps/meteor/app/api/server/lib/rooms.ts b/apps/meteor/app/api/server/lib/rooms.ts index 716f44acd37..14d3e20502f 100644 --- a/apps/meteor/app/api/server/lib/rooms.ts +++ b/apps/meteor/app/api/server/lib/rooms.ts @@ -27,7 +27,6 @@ export async function findAdminRooms({ const name = filter?.trim(); const discussion = types?.includes('discussions'); const includeTeams = types?.includes('teams'); - const showOnlyTeams = types.length === 1 && types.includes('teams'); const typesToRemove = ['discussions', 'teams']; const showTypes = Array.isArray(types) ? types.filter((type): type is RoomType => !typesToRemove.includes(type)) : []; const options: FindOptions = { @@ -36,14 +35,7 @@ export async function findAdminRooms({ limit: count, }; - let result; - if (name && showTypes.length) { - result = Rooms.findByNameOrFnameContainingAndTypes(name, showTypes, discussion, includeTeams, showOnlyTeams, options); - } else if (showTypes.length) { - result = Rooms.findByTypes(showTypes, discussion, includeTeams, showOnlyTeams, options); - } else { - result = Rooms.findByNameOrFnameContaining(name, discussion, includeTeams, showOnlyTeams, options); - } + const result = Rooms.findByNameOrFnameContainingAndTypes(name, showTypes, discussion, includeTeams, options); const { cursor, totalCount } = result; diff --git a/apps/meteor/app/api/server/v1/rooms.ts b/apps/meteor/app/api/server/v1/rooms.ts index c0462c5e78d..f277ecb0f9d 100644 --- a/apps/meteor/app/api/server/v1/rooms.ts +++ b/apps/meteor/app/api/server/v1/rooms.ts @@ -398,7 +398,7 @@ API.v1.addRoute( await findAdminRooms({ uid: this.userId, filter: filter || '', - types: types || [], + types: (types && !Array.isArray(types) ? [types] : types) ?? [], pagination: { offset, count, diff --git a/apps/meteor/client/views/admin/rooms/FilterByTypeAndText.tsx b/apps/meteor/client/views/admin/rooms/FilterByTypeAndText.tsx index 8f93ee0c669..2496cb87502 100644 --- a/apps/meteor/client/views/admin/rooms/FilterByTypeAndText.tsx +++ b/apps/meteor/client/views/admin/rooms/FilterByTypeAndText.tsx @@ -4,17 +4,17 @@ import { useTranslation } from '@rocket.chat/ui-contexts'; import type { ReactElement, Dispatch, SetStateAction } from 'react'; import React, { useCallback, useState, useEffect } from 'react'; -const DEFAULT_TYPES = ['d', 'p', 'c', 'teams']; +const DEFAULT_TYPES = ['d', 'p', 'c', 'l', 'discussions', 'teams']; const FilterByTypeAndText = ({ setFilter, ...props }: { setFilter?: Dispatch> }): ReactElement => { const [text, setText] = useState(''); const [types, setTypes] = useState({ - d: false, - c: false, - p: false, - l: false, - discussions: false, - teams: false, + d: true, + c: true, + p: true, + l: true, + discussions: true, + teams: true, }); const t = useTranslation(); diff --git a/apps/meteor/client/views/admin/rooms/RoomsTable.tsx b/apps/meteor/client/views/admin/rooms/RoomsTable.tsx index 37430317df6..0c36105d89b 100644 --- a/apps/meteor/client/views/admin/rooms/RoomsTable.tsx +++ b/apps/meteor/client/views/admin/rooms/RoomsTable.tsx @@ -29,7 +29,7 @@ type RoomFilters = { text: string; }; -const DEFAULT_TYPES = ['d', 'p', 'c', 'teams']; +const DEFAULT_TYPES = ['d', 'p', 'c', 'l', 'discussions', 'teams']; const roomTypeI18nMap = { l: 'Omnichannel', diff --git a/apps/meteor/server/models/raw/Rooms.ts b/apps/meteor/server/models/raw/Rooms.ts index 64553ad977f..466858ea258 100644 --- a/apps/meteor/server/models/raw/Rooms.ts +++ b/apps/meteor/server/models/raw/Rooms.ts @@ -164,26 +164,11 @@ export class RoomsRaw extends BaseRaw implements IRoomsModel { types: Array, discussion = false, teams = false, - showOnlyTeams = false, options: FindOptions = {}, ): FindPaginated> { const nameRegex = new RegExp(escapeRegExp(name).trim(), 'i'); - const onlyTeamsQuery: Filter = showOnlyTeams ? { teamMain: { $exists: true } } : {}; - - const teamCondition: Filter = teams - ? {} - : { - teamMain: { - $exists: false, - }, - }; - - const query: Filter = { - t: { - $in: types, - }, - prid: { $exists: discussion }, + const nameCondition: Filter = { $or: [ { name: nameRegex, federated: { $ne: true } }, { fname: nameRegex }, @@ -192,71 +177,27 @@ export class RoomsRaw extends BaseRaw implements IRoomsModel { usernames: nameRegex, }, ], - ...teamCondition, - ...onlyTeamsQuery, }; - return this.findPaginated(query, options); - } - - findByTypes( - types: Array, - discussion = false, - teams = false, - onlyTeams = false, - options: FindOptions = {}, - ): FindPaginated> { - const teamCondition = teams - ? {} - : { - teamMain: { - $exists: false, - }, - }; - - const onlyTeamsCondition = onlyTeams ? { teamMain: { $exists: true } } : {}; const query: Filter = { - t: { - $in: types, - }, - prid: { $exists: discussion }, - ...teamCondition, - ...onlyTeamsCondition, - }; - return this.findPaginated(query, options); - } - - findByNameOrFnameContaining( - name: NonNullable, - discussion = false, - teams = false, - onlyTeams = false, - options: FindOptions = {}, - ): FindPaginated> { - const nameRegex = new RegExp(escapeRegExp(name).trim(), 'i'); - - const teamCondition = teams - ? {} - : { - teamMain: { - $exists: false, - }, - }; - - const onlyTeamsCondition = onlyTeams ? { $and: [{ teamMain: { $exists: true } }, { teamMain: true }] } : {}; - - const query: Filter = { - prid: { $exists: discussion }, - $or: [ - { name: nameRegex, federated: { $ne: true } }, - { fname: nameRegex }, - { - t: 'd', - usernames: nameRegex, - }, + $and: [ + name ? nameCondition : {}, + (types && types.length) || discussion || teams + ? { + $or: [ + { + t: { + $in: types, + }, + }, + ...(discussion ? [{ prid: { $exists: true } }] : []), + ...(teams ? [{ teamMain: { $exists: true } }] : []), + ], + } + : {}, ], - ...teamCondition, - ...onlyTeamsCondition, + ...(!discussion ? { prid: { $exists: false } } : {}), + ...(!teams ? { teamMain: { $exists: false } } : {}), }; return this.findPaginated(query, options); diff --git a/apps/meteor/tests/end-to-end/api/09-rooms.js b/apps/meteor/tests/end-to-end/api/09-rooms.js index 48fba2ae9f2..d540fcacd57 100644 --- a/apps/meteor/tests/end-to-end/api/09-rooms.js +++ b/apps/meteor/tests/end-to-end/api/09-rooms.js @@ -1306,10 +1306,23 @@ describe('[Rooms]', function () { const suffix = `test-${Date.now()}`; const fnameRoom = `Ελληνικά-${suffix}`; const nameRoom = `Ellinika-${suffix}`; + const discussionRoomName = `${nameRoom}-discussion`; + + let testGroup; before((done) => { updateSetting('UI_Allow_room_names_with_special_chars', true).then(() => { - createRoom({ type: 'p', name: fnameRoom }).end(done); + createRoom({ type: 'p', name: fnameRoom }).end((err, res) => { + testGroup = res.body.group; + request + .post(api('rooms.createDiscussion')) + .set(credentials) + .send({ + prid: testGroup._id, + t_name: discussionRoomName, + }) + .end(done); + }); }); }); @@ -1400,6 +1413,73 @@ describe('[Rooms]', function () { .end(done); }); }); + it('should filter by only rooms types', (done) => { + request + .get(api('rooms.adminRooms')) + .set(credentials) + .query({ + types: ['p'], + }) + .expect(200) + .expect((res) => { + expect(res.body).to.have.property('success', true); + expect(res.body).to.have.property('rooms').and.to.be.an('array'); + expect(res.body.rooms).to.have.lengthOf.at.least(1); + expect(res.body.rooms[0].t).to.be.equal('p'); + expect(res.body.rooms.find((room) => room.name === nameRoom)).to.exist; + expect(res.body.rooms.find((room) => room.name === discussionRoomName)).to.not.exist; + }) + .end(done); + }); + it('should filter by only name', (done) => { + request + .get(api('rooms.adminRooms')) + .set(credentials) + .query({ + filter: nameRoom, + }) + .expect(200) + .expect((res) => { + expect(res.body).to.have.property('success', true); + expect(res.body).to.have.property('rooms').and.to.be.an('array'); + expect(res.body.rooms).to.have.lengthOf(1); + expect(res.body.rooms[0].name).to.be.equal(nameRoom); + }) + .end(done); + }); + it('should filter by type and name at the same query', (done) => { + request + .get(api('rooms.adminRooms')) + .set(credentials) + .query({ + filter: nameRoom, + types: ['p'], + }) + .expect(200) + .expect((res) => { + expect(res.body).to.have.property('success', true); + expect(res.body).to.have.property('rooms').and.to.be.an('array'); + expect(res.body.rooms).to.have.lengthOf(1); + expect(res.body.rooms[0].name).to.be.equal(nameRoom); + }) + .end(done); + }); + it('should return an empty array when filter by wrong type and correct room name', (done) => { + request + .get(api('rooms.adminRooms')) + .set(credentials) + .query({ + filter: nameRoom, + types: ['c'], + }) + .expect(200) + .expect((res) => { + expect(res.body).to.have.property('success', true); + expect(res.body).to.have.property('rooms').and.to.be.an('array'); + expect(res.body.rooms).to.have.lengthOf(0); + }) + .end(done); + }); }); describe('update group dms name', () => { diff --git a/packages/model-typings/src/models/IRoomsModel.ts b/packages/model-typings/src/models/IRoomsModel.ts index d3813c09523..8987a660aa8 100644 --- a/packages/model-typings/src/models/IRoomsModel.ts +++ b/packages/model-typings/src/models/IRoomsModel.ts @@ -32,25 +32,8 @@ export interface IRoomsModel extends IBaseModel { findByNameOrFnameContainingAndTypes( name: NonNullable, types: Array, - discussion: boolean, - teams: boolean, - showOnlyTeams: boolean, - options?: FindOptions, - ): FindPaginated>; - - findByTypes( - types: Array, - discussion: boolean, - teams: boolean, - onlyTeams: boolean, - options?: FindOptions, - ): FindPaginated>; - - findByNameOrFnameContaining( - name: NonNullable, - discussion: boolean, - teams: boolean, - onlyTeams: boolean, + discussion?: boolean, + teams?: boolean, options?: FindOptions, ): FindPaginated>;