diff --git a/app/api/server/v1/users.js b/app/api/server/v1/users.js index 74344aa94ce..a2138630a03 100644 --- a/app/api/server/v1/users.js +++ b/app/api/server/v1/users.js @@ -903,10 +903,17 @@ API.v1.addRoute('users.resetTOTP', { authRequired: true, twoFactorRequired: true API.v1.addRoute('users.listTeams', { authRequired: true }, { get() { - const { userId } = this.bodyParams; + check(this.queryParams, Match.ObjectIncluding({ + userId: Match.Maybe(String), + })); + const { userId } = this.queryParams; + + if (!userId) { + throw new Meteor.Error('error-invalid-user-id', 'Invalid user id'); + } // If the caller has permission to view all teams, there's no need to filter the teams - const adminId = hasPermission(this.userId, 'view-all-teams') ? '' : this.userId; + const adminId = hasPermission(this.userId, 'view-all-teams') ? undefined : this.userId; const teams = Promise.await(Team.findBySubscribedUserIds(userId, adminId)); diff --git a/app/models/server/raw/TeamMember.ts b/app/models/server/raw/TeamMember.ts index e82e124e5ee..e411980a862 100644 --- a/app/models/server/raw/TeamMember.ts +++ b/app/models/server/raw/TeamMember.ts @@ -74,7 +74,7 @@ export class TeamMemberRaw extends BaseRaw { findByUserIdAndTeamIds(userId: string, teamIds: Array, options: FindOneOptions = {}): Cursor { const query = { - 'u._id': userId, + userId, teamId: { $in: teamIds, }, diff --git a/client/contexts/ServerContext/endpoints/v1/teams.ts b/client/contexts/ServerContext/endpoints/v1/teams.ts index 2fae8fbac8a..70f8a7c10b1 100644 --- a/client/contexts/ServerContext/endpoints/v1/teams.ts +++ b/client/contexts/ServerContext/endpoints/v1/teams.ts @@ -6,6 +6,9 @@ export type TeamsEndpoints = { 'teams.addRooms': { POST: (params: { rooms: IRoom['_id'][]; teamId: string }) => void; }; + 'teams.info': { + GET: (params: { teamId: IRoom['teamId'] }) => { teamInfo: ITeam }; + }; 'teams.listRooms': { GET: (params: { teamId: ITeam['_id']; diff --git a/client/contexts/ServerContext/endpoints/v1/users.ts b/client/contexts/ServerContext/endpoints/v1/users.ts index 27cc45a04c6..cbe8a74c8d4 100644 --- a/client/contexts/ServerContext/endpoints/v1/users.ts +++ b/client/contexts/ServerContext/endpoints/v1/users.ts @@ -1,3 +1,4 @@ +import type { ITeam } from '../../../../../definition/ITeam'; import type { IUser } from '../../../../../definition/IUser'; export type UsersEndpoints = { @@ -7,4 +8,7 @@ export type UsersEndpoints = { 'users.autocomplete': { GET: (params: { selector: string }) => { items: IUser[] }; }; + 'users.listTeams': { + GET: (params: { userId: IUser['_id'] }) => { teams: Array }; + }; }; diff --git a/client/views/room/Header/ParentTeam.js b/client/views/room/Header/ParentTeam.js index 790db61acd5..60c51d2542b 100644 --- a/client/views/room/Header/ParentTeam.js +++ b/client/views/room/Header/ParentTeam.js @@ -1,10 +1,11 @@ import React, { useMemo } from 'react'; -import { roomTypes } from '../../../../app/utils/client'; +import { TEAM_TYPE } from '../../../../definition/ITeam'; import Header from '../../../components/Header'; -import { useUserId, useUserSubscription } from '../../../contexts/UserContext'; +import { useUserId } from '../../../contexts/UserContext'; import { AsyncStatePhase } from '../../../hooks/useAsyncState'; import { useEndpointData } from '../../../hooks/useEndpointData'; +import { goToRoomById } from '../../../lib/utils/goToRoomById'; const ParentTeam = ({ room }) => { const userId = useUserId(); @@ -13,17 +14,15 @@ const ParentTeam = ({ room }) => { 'teams.info', useMemo(() => ({ teamId: room.teamId }), [room.teamId]), ); + const { value: userTeams, phase: userTeamsPhase } = useEndpointData( 'users.listTeams', useMemo(() => ({ userId }), [userId]), ); - const belongsToTeam = userTeams?.teams?.find((team) => team._id === room.teamId); - - const teamMainRoom = useUserSubscription(value?.teamInfo?.roomId); - const teamMainRoomHref = teamMainRoom - ? roomTypes.getRouteLink(teamMainRoom.t, teamMainRoom) - : null; + const belongsToTeam = userTeams?.teams?.find((team) => team._id === room.teamId) || false; + const isTeamPublic = value?.teamInfo.type === TEAM_TYPE.PUBLIC; + const teamMainRoomHref = () => goToRoomById(value?.teamInfo.roomId); if (phase === AsyncStatePhase.LOADING || userTeamsPhase === AsyncStatePhase.LOADING) { return ; @@ -35,9 +34,9 @@ const ParentTeam = ({ room }) => { return ( - - {belongsToTeam && teamMainRoom ? ( - {teamMainRoom?.name} + + {isTeamPublic || belongsToTeam ? ( + {value.teamInfo.name} ) : ( value.teamInfo.name )} diff --git a/client/views/room/Header/RoomTitle.js b/client/views/room/Header/RoomTitle.tsx similarity index 57% rename from client/views/room/Header/RoomTitle.js rename to client/views/room/Header/RoomTitle.tsx index b4ea208cebc..d65d40d26d4 100644 --- a/client/views/room/Header/RoomTitle.js +++ b/client/views/room/Header/RoomTitle.tsx @@ -1,9 +1,10 @@ -import React from 'react'; +import React, { ReactElement } from 'react'; +import { IRoom } from '../../../../definition/IRoom'; import Header from '../../../components/Header'; import HeaderIconWithRoom from './HeaderIconWithRoom'; -const RoomTitle = ({ room }) => ( +const RoomTitle = ({ room }: { room: IRoom }): ReactElement => ( <> {room.name} diff --git a/tests/end-to-end/api/01-users.js b/tests/end-to-end/api/01-users.js index 3d7f93f97e0..d2de761a42b 100644 --- a/tests/end-to-end/api/01-users.js +++ b/tests/end-to-end/api/01-users.js @@ -3094,7 +3094,7 @@ describe('[Users]', function() { }); }); - describe('[/users.listTeams', () => { + describe('[/users.listTeams]', () => { const teamName1 = `team-name-${ Date.now() }`; const teamName2 = `team-name-2-${ Date.now() }`; let testUser; @@ -3184,7 +3184,7 @@ describe('[Users]', function() { it('should list both channels', (done) => { request.get(api('users.listTeams')) .set(credentials) - .send({ + .query({ userId: testUser._id, }) .expect('Content-Type', 'application/json')