diff --git a/.changeset/breezy-moons-search.md b/.changeset/breezy-moons-search.md new file mode 100644 index 0000000000000..b73abc9211ff8 --- /dev/null +++ b/.changeset/breezy-moons-search.md @@ -0,0 +1,5 @@ +--- +'@rocket.chat/meteor': patch +--- + +Fixes own account showing twice in navbar room search when searching by username diff --git a/apps/meteor/client/navbar/NavBarSearch/hooks/useSearchItems.spec.ts b/apps/meteor/client/navbar/NavBarSearch/hooks/useSearchItems.spec.ts new file mode 100644 index 0000000000000..a4e132dfbb197 --- /dev/null +++ b/apps/meteor/client/navbar/NavBarSearch/hooks/useSearchItems.spec.ts @@ -0,0 +1,57 @@ +import { mockAppRoot } from '@rocket.chat/mock-providers'; +import type { SubscriptionWithRoom } from '@rocket.chat/ui-contexts'; +import { renderHook, waitFor } from '@testing-library/react'; + +import { useSearchItems } from './useSearchItems'; + +describe('useSearchItems', () => { + it('should deduplicate a user if they already exist in local subscriptions as a self-DM', async () => { + const myUserName = 'rocketchat.internal.admin.test'; + const myUserId = 'user_id_456'; + + const wrapper = mockAppRoot() + .withSubscriptions([ + { + _id: 'local_room_123', + t: 'd', + name: myUserName, + uids: [myUserId], + } as unknown as SubscriptionWithRoom, + ]) + .withEndpoint('GET', '/v1/spotlight', () => ({ + users: [{ _id: myUserId, username: myUserName, name: 'Rocket Chat Test' }], + rooms: [], + })) + .build(); + + const { result } = renderHook(() => useSearchItems(myUserName), { wrapper }); + + expect(result.current.items).toHaveLength(1); + expect(result.current.items[0]._id).toBe('local_room_123'); + + await waitFor(() => { + expect(result.current.isLoading).toBe(false); + }); + + expect(result.current.items).toHaveLength(1); + expect(result.current.items[0]._id).toBe('local_room_123'); + expect(result.current.items[0].name).toBe(myUserName); + }); + + it('should append users from the server if they are NOT duplicates', async () => { + const wrapper = mockAppRoot() + .withSubscriptions([{ _id: 'local_room_123', t: 'd', name: 'general' } as unknown as SubscriptionWithRoom]) + .withEndpoint('GET', '/v1/spotlight', () => ({ + users: [{ _id: 'user_id_456', username: 'john.doe', name: 'John Doe' }], + rooms: [], + })) + .build(); + + const { result } = renderHook(() => useSearchItems('jo'), { wrapper }); + + await waitFor(() => { + expect(result.current.isLoading).toBe(false); + expect(result.current.items).toHaveLength(2); + }); + }); +}); diff --git a/apps/meteor/client/navbar/NavBarSearch/hooks/useSearchItems.ts b/apps/meteor/client/navbar/NavBarSearch/hooks/useSearchItems.ts index 1804e054a95ff..00784c386cd84 100644 --- a/apps/meteor/client/navbar/NavBarSearch/hooks/useSearchItems.ts +++ b/apps/meteor/client/navbar/NavBarSearch/hooks/useSearchItems.ts @@ -133,12 +133,13 @@ export const useSearchItems = (filterText: string): { items: SubscriptionWithRoo // local subscription, checked against the *current* localRooms. The query isn't keyed on // localRooms (to avoid refetching on every subscription change), so subscriptions that load // in after the fetch would otherwise render twice — once from localRooms, once from server. - const isLocalDuplicate = (item: { _id: string; t?: string; uids?: string[] }): boolean => + const isLocalDuplicate = (item: { _id: string; t?: string; uids?: string[]; name: string }): boolean => localRooms.some((room) => { const sameRoom = [room.rid, room._id].includes(item._id); const sameGroupDM = item.t === 'd' && !!item.uids && item.uids.length > 1 && item.uids.includes(room._id); const sameDirectDM = item.t === 'd' && room.t === 'd' && !!room.uids && room.uids.length === 2 && room.uids.includes(item._id); - return sameRoom || sameGroupDM || sameDirectDM; + const sameUserDM = item.t === 'd' && room.t === 'd' && item.name === room.name; + return sameRoom || sameGroupDM || sameDirectDM || sameUserDM; }); // When local subscriptions already fill the limit the server query is disabled, but React Query