From 83717327e3c45bd2cdbabcc5b7e1ef274e0ec4b1 Mon Sep 17 00:00:00 2001
From: Douglas Fabris <devfabris@gmail.com>
Date: Thu, 30 Mar 2023 14:11:07 -0300
Subject: [PATCH] refactor: `AutoComplete` Sanitization (#28666)

Co-authored-by: Tasso Evangelista <2263066+tassoevan@users.noreply.github.com>
---
 .../client/models/CachedChatSubscription.ts   |   2 +-
 .../CreateDiscussion/CreateDiscussion.tsx     |  13 +-
 .../Omnichannel/modals/ForwardChatModal.tsx   |   2 +-
 .../RoomAutoComplete/RoomAutoComplete.tsx     |  19 ++-
 .../UserAndRoomAutoCompleteMultiple.tsx       |  75 +++++-------
 .../UserAutoComplete/UserAutoComplete.tsx     |  54 ++++----
 .../UserAutoCompleteMultiple.tsx              |  45 +++----
 apps/meteor/client/providers/UserProvider.tsx |   4 +-
 .../meteor/client/sidebar/Sidebar.stories.tsx |  12 +-
 .../header/CreateTeam/CreateTeamModal.tsx     |  19 +--
 .../client/sidebar/search/SearchList.tsx      |   4 +-
 .../UsersInRole/UsersInRolePage.tsx           |  26 ++--
 .../views/omnichannel/agents/AddAgent.tsx     |  11 +-
 .../ExportMessages/MailExportForm.tsx         |  16 +--
 .../ChannelToTeamModal/ChannelToTeamModal.tsx |   8 +-
 .../ChannelToTeamSelection.tsx                |   2 +-
 .../PruneMessages/PruneMessages.tsx           |  17 +--
 .../PruneMessages/PruneMessagesWithData.tsx   |  17 +--
 .../RoomMembers/AddUsers/AddUsers.tsx         |   2 +-
 .../RoomMembers/AddUsers/AddUsersWithData.tsx |  13 +-
 .../ShareMessageModal/ShareMessageModal.tsx   |  28 +----
 .../contextualBar/TeamAutocomplete/Avatar.js  |   2 +-
 .../TeamAutocomplete/TeamAutocomplete.js      |  39 ------
 .../TeamAutocomplete/TeamAutocomplete.tsx     |  46 +++++++
 .../AddExistingModal/AddExistingModal.tsx     |  54 ++------
 .../RoomsAvailableForTeamsAutoComplete.tsx    |  69 +++++++++++
 .../channels/AddExistingModal/RoomsInput.tsx  | 115 ------------------
 .../components/forms/RoomAutoComplete.tsx     |  24 +---
 .../forms/UsernamesAutoComplete.tsx           |  27 ----
 .../components/forms/VisitorAutoComplete.tsx  |  25 +---
 .../views/audit/components/tabs/DirectTab.tsx |  15 +--
 .../views/audit/components/tabs/UsersTab.tsx  |   4 +-
 .../tests/e2e/omnichannel-manager.spec.ts     |   2 +-
 packages/ui-contexts/src/UserContext.ts       |   4 +-
 .../src/hooks/useUserSubscriptions.ts         |   4 +-
 packages/ui-contexts/src/index.ts             |   4 +-
 .../src/types}/SubscriptionWithRoom.ts        |   0
 yarn.lock                                     |  80 ++++++------
 38 files changed, 324 insertions(+), 579 deletions(-)
 delete mode 100644 apps/meteor/client/views/teams/contextualBar/TeamAutocomplete/TeamAutocomplete.js
 create mode 100644 apps/meteor/client/views/teams/contextualBar/TeamAutocomplete/TeamAutocomplete.tsx
 create mode 100644 apps/meteor/client/views/teams/contextualBar/channels/AddExistingModal/RoomsAvailableForTeamsAutoComplete.tsx
 delete mode 100644 apps/meteor/client/views/teams/contextualBar/channels/AddExistingModal/RoomsInput.tsx
 delete mode 100644 apps/meteor/ee/client/views/audit/components/forms/UsernamesAutoComplete.tsx
 rename {apps/meteor/client/definitions => packages/ui-contexts/src/types}/SubscriptionWithRoom.ts (100%)

diff --git a/apps/meteor/app/models/client/models/CachedChatSubscription.ts b/apps/meteor/app/models/client/models/CachedChatSubscription.ts
index bc4791ab421..02798f1952a 100644
--- a/apps/meteor/app/models/client/models/CachedChatSubscription.ts
+++ b/apps/meteor/app/models/client/models/CachedChatSubscription.ts
@@ -1,8 +1,8 @@
 import type { IOmnichannelRoom, IRoomWithRetentionPolicy, ISubscription } from '@rocket.chat/core-typings';
 import { DEFAULT_SLA_CONFIG, LivechatPriorityWeight } from '@rocket.chat/core-typings';
+import type { SubscriptionWithRoom } from '@rocket.chat/ui-contexts';
 
 import { CachedCollection } from '../../../ui-cached-collection/client';
-import type { SubscriptionWithRoom } from '../../../../client/definitions/SubscriptionWithRoom';
 import { ChatRoom } from './ChatRoom';
 import { CachedChatRoom } from './CachedChatRoom';
 
diff --git a/apps/meteor/client/components/CreateDiscussion/CreateDiscussion.tsx b/apps/meteor/client/components/CreateDiscussion/CreateDiscussion.tsx
index 5ca1be79ee0..c8640c45d6e 100644
--- a/apps/meteor/client/components/CreateDiscussion/CreateDiscussion.tsx
+++ b/apps/meteor/client/components/CreateDiscussion/CreateDiscussion.tsx
@@ -142,18 +142,7 @@ const CreateDiscussion = ({ onClose, defaultParentRoom, parentMessageId, nameSug
 								control={control}
 								name='usernames'
 								render={({ field: { onChange, value } }) => (
-									<UserAutoCompleteMultiple
-										value={value}
-										onChange={(currentUsername, action) => {
-											const expunged = value?.filter((username) => username !== currentUsername) ?? [];
-											if (action === 'remove') {
-												onChange(expunged);
-												return;
-											}
-											onChange([...expunged, currentUsername]);
-										}}
-										placeholder={t('Username_Placeholder')}
-									/>
+									<UserAutoCompleteMultiple value={value} onChange={onChange} placeholder={t('Username_Placeholder')} />
 								)}
 							/>
 						</Field.Row>
diff --git a/apps/meteor/client/components/Omnichannel/modals/ForwardChatModal.tsx b/apps/meteor/client/components/Omnichannel/modals/ForwardChatModal.tsx
index 6aeeda72490..0d56d1247b8 100644
--- a/apps/meteor/client/components/Omnichannel/modals/ForwardChatModal.tsx
+++ b/apps/meteor/client/components/Omnichannel/modals/ForwardChatModal.tsx
@@ -128,7 +128,7 @@ const ForwardChatModal = ({
 						<UserAutoComplete
 							conditions={conditions}
 							placeholder={t('Username')}
-							onChange={(value: any): void => {
+							onChange={(value) => {
 								setValue('username', value);
 							}}
 							value={getValues().username}
diff --git a/apps/meteor/client/components/RoomAutoComplete/RoomAutoComplete.tsx b/apps/meteor/client/components/RoomAutoComplete/RoomAutoComplete.tsx
index 5054b4087d9..64c1c946d06 100644
--- a/apps/meteor/client/components/RoomAutoComplete/RoomAutoComplete.tsx
+++ b/apps/meteor/client/components/RoomAutoComplete/RoomAutoComplete.tsx
@@ -14,13 +14,9 @@ const generateQuery = (
 	selector: string;
 } => ({ selector: JSON.stringify({ name: term }) });
 
-type RoomAutoCompleteProps<T> = Omit<ComponentProps<typeof AutoComplete>, 'value' | 'filter' | 'onChange'> & {
-	value: T;
-	onChange: (value: TemplateStringsArray) => void;
-};
+type RoomAutoCompleteProps = Omit<ComponentProps<typeof AutoComplete>, 'filter'>;
 
-/* @deprecated */
-const RoomAutoComplete = <T,>(props: RoomAutoCompleteProps<T>): ReactElement => {
+const RoomAutoComplete = ({ value, onChange, ...props }: RoomAutoCompleteProps): ReactElement => {
 	const [filter, setFilter] = useState('');
 	const filterDebounced = useDebouncedValue(filter, 300);
 	const autocomplete = useEndpoint('GET', '/v1/rooms.autocomplete.channelAndPrivate');
@@ -38,18 +34,19 @@ const RoomAutoComplete = <T,>(props: RoomAutoCompleteProps<T>): ReactElement =>
 				  }))
 				: [],
 		[result.data?.items, result.isSuccess],
-	) as unknown as { value: string; label: string }[];
+	);
 
 	return (
 		<AutoComplete
-			value={props.value as any}
-			onChange={props.onChange as any}
+			{...props}
+			value={value}
+			onChange={onChange}
 			filter={filter}
 			setFilter={setFilter}
-			renderSelected={({ value, label }): ReactElement => (
+			renderSelected={({ selected: { value, label } }): ReactElement => (
 				<>
 					<Box margin='none' mi='x2'>
-						<RoomAvatar size='x20' room={{ type: label?.type || 'c', _id: value, ...label }} />{' '}
+						<RoomAvatar size='x20' room={{ type: label?.type || 'c', _id: value, ...label }} />
 					</Box>
 					<Box margin='none' mi='x2'>
 						{label?.name}
diff --git a/apps/meteor/client/components/UserAndRoomAutoCompleteMultiple.tsx/UserAndRoomAutoCompleteMultiple.tsx b/apps/meteor/client/components/UserAndRoomAutoCompleteMultiple.tsx/UserAndRoomAutoCompleteMultiple.tsx
index 9e7791f1896..77194185cc8 100644
--- a/apps/meteor/client/components/UserAndRoomAutoCompleteMultiple.tsx/UserAndRoomAutoCompleteMultiple.tsx
+++ b/apps/meteor/client/components/UserAndRoomAutoCompleteMultiple.tsx/UserAndRoomAutoCompleteMultiple.tsx
@@ -1,29 +1,18 @@
 import { isDirectMessageRoom } from '@rocket.chat/core-typings';
-import { AutoComplete, Box, Option, OptionAvatar, OptionContent, OptionDescription, Chip } from '@rocket.chat/fuselage';
-import { useMutableCallback, useDebouncedValue } from '@rocket.chat/fuselage-hooks';
+import { AutoComplete, Box, Option, OptionAvatar, OptionContent, Chip } from '@rocket.chat/fuselage';
+import { useDebouncedValue } from '@rocket.chat/fuselage-hooks';
 import { escapeRegExp } from '@rocket.chat/string-helpers';
 import { useUser, useUserSubscriptions } from '@rocket.chat/ui-contexts';
 import type { ComponentProps, ReactElement } from 'react';
 import React, { memo, useMemo, useState } from 'react';
 
 import { roomCoordinator } from '../../lib/rooms/roomCoordinator';
+import RoomAvatar from '../avatar/RoomAvatar';
 import UserAvatar from '../avatar/UserAvatar';
 
-type roomType = {
-	label: string;
-	value: string;
-	_id: string;
-	type: string;
-};
-
-type UserAndRoomAutoCompleteMultipleProps = Omit<ComponentProps<typeof AutoComplete>, 'value' | 'filter' | 'onChange'> &
-	Omit<ComponentProps<typeof Option>, 'value' | 'is' | 'className' | 'onChange'> & {
-		onChange: (room: roomType, action?: 'remove') => void;
-		value: any;
-		filter?: string;
-	};
+type UserAndRoomAutoCompleteMultipleProps = Omit<ComponentProps<typeof AutoComplete>, 'filter'>;
 
-const UserAndRoomAutoCompleteMultiple = ({ onChange, ...props }: UserAndRoomAutoCompleteMultipleProps): ReactElement => {
+const UserAndRoomAutoCompleteMultiple = ({ value, onChange, ...props }: UserAndRoomAutoCompleteMultipleProps): ReactElement => {
 	const user = useUser();
 	const [filter, setFilter] = useState('');
 	const debouncedFilter = useDebouncedValue(filter, 1000);
@@ -44,51 +33,43 @@ const UserAndRoomAutoCompleteMultiple = ({ onChange, ...props }: UserAndRoomAuto
 
 	const options = useMemo(
 		() =>
-			rooms.map((subscription) => ({
-				_id: subscription.rid,
-				value: subscription.name,
-				label: subscription.name,
-				t: subscription.t,
+			rooms.map(({ rid, fname, name, avatarETag, t }) => ({
+				value: rid,
+				label: { name: fname || name, avatarETag, type: t },
 			})),
 		[rooms],
 	);
 
-	const onClickRemove = useMutableCallback((e) => {
-		e.stopPropagation();
-		e.preventDefault();
-		const room = options.find((cur) => cur.value === e.currentTarget.value) as unknown as roomType;
-		onChange?.(room, 'remove');
-	});
-
-	const onChangeContent = (name: string, action: any): void => {
-		const room = options.find((cur) => cur.value === name) as unknown as roomType;
-		onChange(room, action);
-	};
-
 	return (
 		<AutoComplete
 			{...props}
-			onChange={onChangeContent as any}
+			value={value}
+			onChange={onChange}
 			filter={filter}
 			setFilter={setFilter}
-			renderSelected={({ value: selected }): ReactElement =>
-				selected?.map((value: any) => (
-					<Chip key={value} {...props} height='x20' value={value} onClick={onClickRemove} mie='x4'>
+			multiple
+			renderSelected={({ selected: { value, label }, onRemove, ...props }): ReactElement => (
+				<Chip {...props} height='x20' value={value} onClick={onRemove} mie='x4'>
+					{label.t === 'd' ? (
 						<UserAvatar size='x20' username={value} />
-						<Box is='span' margin='none' mis='x4'>
-							{value}
-						</Box>
-					</Chip>
-				))
-			}
+					) : (
+						<RoomAvatar size='x20' room={{ type: label?.type, _id: value, ...label }} />
+					)}
+					<Box is='span' margin='none' mis='x4'>
+						{label.name}
+					</Box>
+				</Chip>
+			)}
 			renderItem={({ value, label, ...props }): ReactElement => (
 				<Option key={value} {...props}>
 					<OptionAvatar>
-						<UserAvatar username={value} size='x20' />
+						{label.t === 'd' ? (
+							<UserAvatar size='x20' username={value} />
+						) : (
+							<RoomAvatar size='x20' room={{ type: label?.type, _id: value, ...label }} />
+						)}
 					</OptionAvatar>
-					<OptionContent>
-						{label} <OptionDescription>({value})</OptionDescription>
-					</OptionContent>
+					<OptionContent>{label.name}</OptionContent>
 				</Option>
 			)}
 			options={options}
diff --git a/apps/meteor/client/components/UserAutoComplete/UserAutoComplete.tsx b/apps/meteor/client/components/UserAutoComplete/UserAutoComplete.tsx
index dd1cceb4699..f557c5a339e 100644
--- a/apps/meteor/client/components/UserAutoComplete/UserAutoComplete.tsx
+++ b/apps/meteor/client/components/UserAutoComplete/UserAutoComplete.tsx
@@ -1,9 +1,10 @@
 import { AutoComplete, Option, Box, Chip, Options } from '@rocket.chat/fuselage';
 import { useDebouncedValue } from '@rocket.chat/fuselage-hooks';
+import { useEndpoint } from '@rocket.chat/ui-contexts';
+import { useQuery } from '@tanstack/react-query';
 import type { ComponentProps, ReactElement } from 'react';
 import React, { memo, useMemo, useState } from 'react';
 
-import { useEndpointData } from '../../hooks/useEndpointData';
 import UserAvatar from '../avatar/UserAvatar';
 
 const query = (
@@ -13,49 +14,40 @@ const query = (
 	selector: string;
 } => ({ selector: JSON.stringify({ term, conditions }) });
 
-type UserAutoCompleteProps = Omit<ComponentProps<typeof AutoComplete>, 'value' | 'filter' | 'onChange'> &
-	Omit<ComponentProps<typeof Option>, 'value' | 'onChange'> & {
-		conditions?: { [key: string]: unknown };
-		filter?: string;
-		value: string;
-		onChange?: (value: string) => void;
-	};
+type UserAutoCompleteProps = Omit<ComponentProps<typeof AutoComplete>, 'filter'> & {
+	conditions?: { [key: string]: unknown };
+};
 
-const UserAutoComplete = ({ value, ...props }: UserAutoCompleteProps): ReactElement => {
+const UserAutoComplete = ({ value, onChange, ...props }: UserAutoCompleteProps): ReactElement => {
 	const { conditions = {} } = props;
 	const [filter, setFilter] = useState('');
 	const debouncedFilter = useDebouncedValue(filter, 1000);
-	const { value: data } = useEndpointData(
-		'/v1/users.autocomplete',
-		// eslint-disable-next-line react-hooks/exhaustive-deps
-		{ params: useMemo(() => query(debouncedFilter, conditions), [filter]) },
+	const usersAutoCompleteEndpoint = useEndpoint('GET', '/v1/users.autocomplete');
+
+	const { data } = useQuery(['usersAutoComplete', debouncedFilter, conditions], async () =>
+		usersAutoCompleteEndpoint(query(debouncedFilter, conditions)),
 	);
 
 	const options = useMemo(() => data?.items.map((user) => ({ value: user.username, label: user.name || user.username })) || [], [data]);
 
 	return (
 		<AutoComplete
-			value={value as any}
-			onChange={props.onChange as any}
+			{...props}
+			value={value}
+			onChange={onChange}
 			filter={filter}
 			setFilter={setFilter}
 			data-qa-id='UserAutoComplete'
-			renderSelected={({ value, label }): ReactElement | null => {
-				if (!value) {
-					return null;
-				}
-
-				return (
-					<Chip height='x20' value={value} onClick={(_e: any): void => props.onChange?.(value)} mie='x4'>
-						<UserAvatar size='x20' username={value} />
-						<Box verticalAlign='middle' is='span' margin='none' mi='x4'>
-							{label}
-						</Box>
-					</Chip>
-				);
-			}}
-			renderItem={({ value, ...props }): ReactElement => (
-				<Option key={value} avatar={<UserAvatar size={Options.AvatarSize} username={value} />} {...props} />
+			renderSelected={({ selected: { value, label } }): ReactElement | null => (
+				<Chip height='x20' value={value} mie='x4'>
+					<UserAvatar size='x20' username={value} />
+					<Box verticalAlign='middle' is='span' margin='none' mi='x4'>
+						{label}
+					</Box>
+				</Chip>
+			)}
+			renderItem={({ value, label, ...props }): ReactElement => (
+				<Option key={value} label={label} avatar={<UserAvatar size={Options.AvatarSize} username={value} />} {...props} />
 			)}
 			options={options}
 		/>
diff --git a/apps/meteor/client/components/UserAutoCompleteMultiple/UserAutoCompleteMultiple.tsx b/apps/meteor/client/components/UserAutoCompleteMultiple/UserAutoCompleteMultiple.tsx
index f4112b21d53..f1f079150bb 100644
--- a/apps/meteor/client/components/UserAutoCompleteMultiple/UserAutoCompleteMultiple.tsx
+++ b/apps/meteor/client/components/UserAutoCompleteMultiple/UserAutoCompleteMultiple.tsx
@@ -1,9 +1,10 @@
-import { AutoComplete, Box, Option, OptionAvatar, OptionContent, Chip, OptionDescription } from '@rocket.chat/fuselage';
-import { useMutableCallback, useDebouncedValue } from '@rocket.chat/fuselage-hooks';
+import { AutoComplete, Box, OptionAvatar, Option, OptionContent, Chip, OptionDescription } from '@rocket.chat/fuselage';
+import { useDebouncedValue } from '@rocket.chat/fuselage-hooks';
+import { useEndpoint } from '@rocket.chat/ui-contexts';
+import { useQuery } from '@tanstack/react-query';
 import type { ComponentProps, ReactElement } from 'react';
 import React, { memo, useMemo, useState } from 'react';
 
-import { useEndpointData } from '../../hooks/useEndpointData';
 import UserAvatar from '../avatar/UserAvatar';
 
 const query = (
@@ -12,42 +13,32 @@ const query = (
 	selector: string;
 } => ({ selector: JSON.stringify({ term }) });
 
-type UserAutoCompleteMultipleProps = Omit<ComponentProps<typeof AutoComplete>, 'value' | 'filter' | 'onChange'> &
-	Omit<ComponentProps<typeof Option>, 'value' | 'is' | 'className' | 'onChange'> & {
-		onChange: (value: any, action: 'remove' | undefined) => void;
-		value: any;
-		filter?: string;
-	};
+type UserAutoCompleteMultipleProps = Omit<ComponentProps<typeof AutoComplete>, 'filter'>;
 
+// TODO: useDisplayUsername
 const UserAutoCompleteMultiple = ({ onChange, ...props }: UserAutoCompleteMultipleProps): ReactElement => {
 	const [filter, setFilter] = useState('');
 	const debouncedFilter = useDebouncedValue(filter, 1000);
-	const { value: data } = useEndpointData('/v1/users.autocomplete', { params: useMemo(() => query(debouncedFilter), [debouncedFilter]) });
+	const usersAutoCompleteEndpoint = useEndpoint('GET', '/v1/users.autocomplete');
+	const { data } = useQuery(['usersAutoComplete', debouncedFilter], async () => usersAutoCompleteEndpoint(query(debouncedFilter)));
 
 	const options = useMemo(() => data?.items.map((user) => ({ value: user.username, label: user.name })) || [], [data]);
 
-	const onClickRemove = useMutableCallback((e) => {
-		e.stopPropagation();
-		e.preventDefault();
-		onChange?.(e.currentTarget.value, 'remove');
-	});
-
 	return (
 		<AutoComplete
 			{...props}
-			onChange={onChange as any}
 			filter={filter}
 			setFilter={setFilter}
-			renderSelected={({ value: selected }): ReactElement =>
-				selected?.map((value: any) => (
-					<Chip key={value} {...props} height='x20' value={value} onClick={onClickRemove} mie='x4'>
-						<UserAvatar size='x20' username={value} />
-						<Box is='span' margin='none' mis='x4'>
-							{value}
-						</Box>
-					</Chip>
-				))
-			}
+			onChange={onChange}
+			multiple
+			renderSelected={({ selected: { value, label }, onRemove }): ReactElement => (
+				<Chip {...props} height='x20' value={value} onClick={onRemove} mie='x4'>
+					<UserAvatar size='x20' username={value} />
+					<Box is='span' margin='none' mis='x4'>
+						{label}
+					</Box>
+				</Chip>
+			)}
 			renderItem={({ value, label, ...props }): ReactElement => (
 				<Option data-qa-type='autocomplete-user-option' key={value} {...props}>
 					<OptionAvatar>
diff --git a/apps/meteor/client/providers/UserProvider.tsx b/apps/meteor/client/providers/UserProvider.tsx
index 0e9c45a6a38..2403af0a87c 100644
--- a/apps/meteor/client/providers/UserProvider.tsx
+++ b/apps/meteor/client/providers/UserProvider.tsx
@@ -1,6 +1,6 @@
 import type { IRoom, ISubscription, IUser } from '@rocket.chat/core-typings';
 import { UserContext, useSetting } from '@rocket.chat/ui-contexts';
-import type { LoginService } from '@rocket.chat/ui-contexts';
+import type { LoginService, SubscriptionWithRoom } from '@rocket.chat/ui-contexts';
 import { Meteor } from 'meteor/meteor';
 import type { ContextType, FC } from 'react';
 import React, { useEffect, useMemo } from 'react';
@@ -86,7 +86,7 @@ const UserProvider: FC = ({ children }) => {
 				Subscriptions.findOne(query, { fields, sort }),
 			),
 			queryRoom: createReactiveSubscriptionFactory<IRoom | undefined>((query, fields) => Rooms.findOne(query, { fields })),
-			querySubscriptions: createReactiveSubscriptionFactory<Array<ISubscription> | []>((query, options) => {
+			querySubscriptions: createReactiveSubscriptionFactory<SubscriptionWithRoom[]>((query, options) => {
 				if (userId) {
 					return Subscriptions.find(query, options).fetch();
 				}
diff --git a/apps/meteor/client/sidebar/Sidebar.stories.tsx b/apps/meteor/client/sidebar/Sidebar.stories.tsx
index 3c85ea0a130..168a5ca98a5 100644
--- a/apps/meteor/client/sidebar/Sidebar.stories.tsx
+++ b/apps/meteor/client/sidebar/Sidebar.stories.tsx
@@ -1,5 +1,5 @@
-import type { ISetting, ISubscription } from '@rocket.chat/core-typings';
-import type { LoginService } from '@rocket.chat/ui-contexts';
+import type { ISetting } from '@rocket.chat/core-typings';
+import type { LoginService, SubscriptionWithRoom } from '@rocket.chat/ui-contexts';
 import { UserContext, SettingsContext } from '@rocket.chat/ui-contexts';
 import type { Meta, Story } from '@storybook/react';
 import type { ObjectId } from 'mongodb';
@@ -46,7 +46,7 @@ const userPreferences: Record<string, unknown> = {
 	sidebarSortby: 'activity',
 };
 
-const subscriptions: ISubscription[] = [
+const subscriptions: SubscriptionWithRoom[] = [
 	{
 		_id: '3Bysd8GrmkWBdS9RT',
 		open: true,
@@ -69,6 +69,12 @@ const subscriptions: ISubscription[] = [
 		tunread: [],
 		lowerCaseName: 'general',
 		lowerCaseFName: 'general',
+		estimatedWaitingTimeQueue: 0,
+		livechatData: undefined,
+		priorityWeight: 0,
+		responseBy: undefined,
+		usersCount: 0,
+		waitingResponse: undefined,
 	},
 ];
 
diff --git a/apps/meteor/client/sidebar/header/CreateTeam/CreateTeamModal.tsx b/apps/meteor/client/sidebar/header/CreateTeam/CreateTeamModal.tsx
index 7206e4c0957..527031a6ae4 100644
--- a/apps/meteor/client/sidebar/header/CreateTeam/CreateTeamModal.tsx
+++ b/apps/meteor/client/sidebar/header/CreateTeam/CreateTeamModal.tsx
@@ -1,4 +1,3 @@
-import type { IUser } from '@rocket.chat/core-typings';
 import { Box, Modal, Button, TextInput, Field, ToggleSwitch, FieldGroup, Icon } from '@rocket.chat/fuselage';
 import { useTranslation, useSetting, usePermission, useEndpoint, useToastMessageDispatch } from '@rocket.chat/ui-contexts';
 import type { ReactElement } from 'react';
@@ -15,7 +14,7 @@ type CreateTeamModalInputs = {
 	readOnly: boolean;
 	encrypted: boolean;
 	broadcast: boolean;
-	members?: Exclude<IUser['username'], undefined>[];
+	members?: string[];
 };
 
 const CreateTeamModal = ({ onClose }: { onClose: () => void }): ReactElement => {
@@ -233,21 +232,7 @@ const CreateTeamModal = ({ onClose }: { onClose: () => void }): ReactElement =>
 						<Controller
 							control={control}
 							name='members'
-							render={({ field: { onChange, value } }): ReactElement => (
-								<UserAutoCompleteMultiple
-									value={value}
-									onChange={(member, action): void => {
-										if (!action && value) {
-											if (value.includes(member)) {
-												return;
-											}
-											return onChange([...value, member]);
-										}
-
-										onChange(value?.filter((current) => current !== member));
-									}}
-								/>
-							)}
+							render={({ field: { onChange, value } }): ReactElement => <UserAutoCompleteMultiple value={value} onChange={onChange} />}
 						/>
 					</Field>
 				</FieldGroup>
diff --git a/apps/meteor/client/sidebar/search/SearchList.tsx b/apps/meteor/client/sidebar/search/SearchList.tsx
index fc28f970b99..13ab3555476 100644
--- a/apps/meteor/client/sidebar/search/SearchList.tsx
+++ b/apps/meteor/client/sidebar/search/SearchList.tsx
@@ -1,4 +1,4 @@
-import type { IRoom, ISubscription, RoomType } from '@rocket.chat/core-typings';
+import type { IRoom, ISubscription } from '@rocket.chat/core-typings';
 import { css } from '@rocket.chat/css-in-js';
 import { Sidebar, TextInput, Box, Icon } from '@rocket.chat/fuselage';
 import { useMutableCallback, useDebouncedValue, useAutoFocus, useUniqueId, useMergedRefs } from '@rocket.chat/fuselage-hooks';
@@ -53,7 +53,7 @@ const useSearchItems = (filterText: string): UseQueryResult<(ISubscription & IRo
 		};
 	}, [name, mention]);
 
-	const localRooms: { rid: string; t: RoomType; _id: string; name: string; uids?: string }[] = useUserSubscriptions(query, options);
+	const localRooms = useUserSubscriptions(query, options);
 
 	const usernamesFromClient = [...localRooms?.map(({ t, name }) => (t === 'd' ? name : null))].filter(Boolean) as string[];
 
diff --git a/apps/meteor/client/views/admin/permissions/UsersInRole/UsersInRolePage.tsx b/apps/meteor/client/views/admin/permissions/UsersInRole/UsersInRolePage.tsx
index 1df48100157..2341995afe4 100644
--- a/apps/meteor/client/views/admin/permissions/UsersInRole/UsersInRolePage.tsx
+++ b/apps/meteor/client/views/admin/permissions/UsersInRole/UsersInRolePage.tsx
@@ -1,4 +1,4 @@
-import type { IRole, IRoom, IUser } from '@rocket.chat/core-typings';
+import type { IRole, IRoom } from '@rocket.chat/core-typings';
 import { Box, Field, Margins, ButtonGroup, Button, Callout } from '@rocket.chat/fuselage';
 import { useMutableCallback } from '@rocket.chat/fuselage-hooks';
 import { useToastMessageDispatch, useRoute, useEndpoint, useTranslation } from '@rocket.chat/ui-contexts';
@@ -11,6 +11,11 @@ import RoomAutoComplete from '../../../../components/RoomAutoComplete';
 import UserAutoCompleteMultiple from '../../../../components/UserAutoCompleteMultiple';
 import UsersInRoleTable from './UsersInRoleTable';
 
+type UsersInRolePayload = {
+	rid?: IRoom['_id'];
+	users: string[];
+};
+
 const UsersInRolePage = ({ role }: { role: IRole }): ReactElement => {
 	const t = useTranslation();
 	const reload = useRef<() => void>(() => undefined);
@@ -22,7 +27,7 @@ const UsersInRolePage = ({ role }: { role: IRole }): ReactElement => {
 		formState: { isDirty },
 		reset,
 		getValues,
-	} = useForm<{ rid?: IRoom['_id']; users: IUser['username'][] }>({ defaultValues: { users: [] } });
+	} = useForm<UsersInRolePayload>({ defaultValues: { users: [] } });
 
 	const { _id, name, description } = role;
 	const router = useRoute('admin-permissions');
@@ -37,7 +42,7 @@ const UsersInRolePage = ({ role }: { role: IRole }): ReactElement => {
 		});
 	});
 
-	const handleAdd = useMutableCallback(async ({ users, rid }: { users: IUser['username'][]; rid?: IRoom['_id'] }) => {
+	const handleAdd = useMutableCallback(async ({ users, rid }: UsersInRolePayload) => {
 		try {
 			await Promise.all(
 				users.map(async (user) => {
@@ -85,20 +90,7 @@ const UsersInRolePage = ({ role }: { role: IRole }): ReactElement => {
 									control={control}
 									name='users'
 									render={({ field: { onChange, value } }): ReactElement => (
-										<UserAutoCompleteMultiple
-											value={value}
-											placeholder={t('User')}
-											onChange={(member, action): void => {
-												if (!action && value) {
-													if (value.includes(member)) {
-														return;
-													}
-													return onChange([...value, member]);
-												}
-
-												onChange(value?.filter((current) => current !== member));
-											}}
-										/>
+										<UserAutoCompleteMultiple value={value} placeholder={t('User')} onChange={onChange} />
 									)}
 								/>
 								<ButtonGroup mis='x8' align='end'>
diff --git a/apps/meteor/client/views/omnichannel/agents/AddAgent.tsx b/apps/meteor/client/views/omnichannel/agents/AddAgent.tsx
index 9989a17cc81..745bd82d283 100644
--- a/apps/meteor/client/views/omnichannel/agents/AddAgent.tsx
+++ b/apps/meteor/client/views/omnichannel/agents/AddAgent.tsx
@@ -26,14 +26,21 @@ const AddAgent = ({ reload }: AddAgentProps): ReactElement => {
 			return;
 		}
 		reload();
-		setUsername(username);
+		setUsername('');
 	});
+
+	const handleChange = (value: unknown): void => {
+		if (typeof value === 'string') {
+			setUsername(value);
+		}
+	};
+
 	return (
 		<Box display='flex' alignItems='center' pi='24px'>
 			<Field>
 				<Field.Label>{t('Username')}</Field.Label>
 				<Field.Row>
-					<UserAutoComplete value={username} onChange={(username: string): void => setUsername(username)} />
+					<UserAutoComplete value={username} onChange={handleChange} />
 					<Button disabled={!username} onClick={handleSave} mis='x8' primary>
 						{t('Add')}
 					</Button>
diff --git a/apps/meteor/client/views/room/contextualBar/ExportMessages/MailExportForm.tsx b/apps/meteor/client/views/room/contextualBar/ExportMessages/MailExportForm.tsx
index f6192a94926..078ea3bd797 100644
--- a/apps/meteor/client/views/room/contextualBar/ExportMessages/MailExportForm.tsx
+++ b/apps/meteor/client/views/room/contextualBar/ExportMessages/MailExportForm.tsx
@@ -1,4 +1,4 @@
-import type { IRoom, IUser } from '@rocket.chat/core-typings';
+import type { IRoom } from '@rocket.chat/core-typings';
 import { css } from '@rocket.chat/css-in-js';
 import { Field, TextInput, ButtonGroup, Button, Box, Icon, Callout, FieldGroup } from '@rocket.chat/fuselage';
 import { useMutableCallback } from '@rocket.chat/fuselage-hooks';
@@ -15,7 +15,7 @@ import { SelectedMessageContext, useCountSelected } from '../../MessageList/cont
 type MailExportFormValues = {
 	dateFrom: string;
 	dateTo: string;
-	toUsers: IUser['username'][];
+	toUsers: string[];
 	additionalEmails: string;
 	subject: string;
 };
@@ -63,16 +63,6 @@ const MailExportForm: FC<MailExportFormProps> = ({ onCancel, rid }) => {
 
 	const { handleToUsers, handleAdditionalEmails, handleSubject } = handlers;
 
-	const onChangeUsers = useMutableCallback((value, action) => {
-		if (!action) {
-			if (toUsers.includes(value)) {
-				return;
-			}
-			return handleToUsers([...toUsers, value]);
-		}
-		handleToUsers(toUsers.filter((current) => current !== value));
-	});
-
 	const roomsExport = useEndpoint('POST', '/v1/rooms.export');
 
 	const handleSubmit = async (): Promise<void> => {
@@ -128,7 +118,7 @@ const MailExportForm: FC<MailExportFormProps> = ({ onCancel, rid }) => {
 			<Field>
 				<Field.Label>{t('To_users')}</Field.Label>
 				<Field.Row>
-					<UserAutoCompleteMultiple value={toUsers} onChange={onChangeUsers} />
+					<UserAutoCompleteMultiple value={toUsers} onChange={handleToUsers} />
 				</Field.Row>
 			</Field>
 			<Field>
diff --git a/apps/meteor/client/views/room/contextualBar/Info/ChannelToTeamModal/ChannelToTeamModal.tsx b/apps/meteor/client/views/room/contextualBar/Info/ChannelToTeamModal/ChannelToTeamModal.tsx
index a3eaa7dd494..4ee42cec17b 100644
--- a/apps/meteor/client/views/room/contextualBar/Info/ChannelToTeamModal/ChannelToTeamModal.tsx
+++ b/apps/meteor/client/views/room/contextualBar/Info/ChannelToTeamModal/ChannelToTeamModal.tsx
@@ -22,11 +22,17 @@ const ChannelToTeamModal = ({ onCancel, onConfirm }: ChannelToTeamModalProps) =>
 		return <ChannelToTeamConfirmation onCancel={onCancel} onConfirm={() => onConfirm(teamId)} />;
 	}
 
+	const handleChange = (value: string | string[]) => {
+		if (typeof value === 'string') {
+			setTeamId(value);
+		}
+	};
+
 	return (
 		<ChannelToTeamSelection
 			onCancel={onCancel}
 			onConfirm={() => setStep(CHANNEL_TO_TEAM_STEPS.CONFIRMATION)}
-			onChange={setTeamId}
+			onChange={handleChange}
 			teamId={teamId}
 		/>
 	);
diff --git a/apps/meteor/client/views/room/contextualBar/Info/ChannelToTeamModal/ChannelToTeamSelection.tsx b/apps/meteor/client/views/room/contextualBar/Info/ChannelToTeamModal/ChannelToTeamSelection.tsx
index 347c5f5d5eb..eb728871d06 100644
--- a/apps/meteor/client/views/room/contextualBar/Info/ChannelToTeamModal/ChannelToTeamSelection.tsx
+++ b/apps/meteor/client/views/room/contextualBar/Info/ChannelToTeamModal/ChannelToTeamSelection.tsx
@@ -8,7 +8,7 @@ import TeamAutocomplete from '../../../../teams/contextualBar/TeamAutocomplete';
 
 type ChannelToTeamSelectionProps = {
 	teamId: IRoom['teamId'];
-	onChange: (teamId: IRoom['_id']) => void;
+	onChange: (value: string | string[]) => void;
 	onCancel: () => void;
 	onConfirm: () => void;
 };
diff --git a/apps/meteor/client/views/room/contextualBar/PruneMessages/PruneMessages.tsx b/apps/meteor/client/views/room/contextualBar/PruneMessages/PruneMessages.tsx
index 1ef76603413..3ab5c428d54 100644
--- a/apps/meteor/client/views/room/contextualBar/PruneMessages/PruneMessages.tsx
+++ b/apps/meteor/client/views/room/contextualBar/PruneMessages/PruneMessages.tsx
@@ -1,4 +1,3 @@
-import type { IUser } from '@rocket.chat/core-typings';
 import { Field, ButtonGroup, Button, CheckBox, Callout } from '@rocket.chat/fuselage';
 import { useUniqueId } from '@rocket.chat/fuselage-hooks';
 import { useTranslation } from '@rocket.chat/ui-contexts';
@@ -13,23 +12,14 @@ import type { initialValues } from './PruneMessagesWithData';
 type PruneMessagesProps = {
 	callOutText?: string;
 	validateText?: string;
-	users: IUser['username'][];
+	users: string[];
 	values: Record<string, unknown>;
 	handlers: Record<string, (eventOrValue: unknown) => void>;
 	onClickClose: () => void;
 	onClickPrune: () => void;
-	onChangeUsers: (value: IUser['username'], action?: string) => void;
 };
 
-const PruneMessages = ({
-	callOutText,
-	validateText,
-	values,
-	handlers,
-	onClickClose,
-	onClickPrune,
-	onChangeUsers,
-}: PruneMessagesProps): ReactElement => {
+const PruneMessages = ({ callOutText, validateText, values, handlers, onClickClose, onClickPrune }: PruneMessagesProps): ReactElement => {
 	const t = useTranslation();
 
 	const { newerDate, newerTime, olderDate, olderTime, users, inclusive, pinned, discussion, threads, attached } =
@@ -45,6 +35,7 @@ const PruneMessages = ({
 		handleDiscussion,
 		handleThreads,
 		handleAttached,
+		handleUsers,
 	} = handlers;
 
 	const inclusiveCheckboxId = useUniqueId();
@@ -73,7 +64,7 @@ const PruneMessages = ({
 				/>
 				<Field>
 					<Field.Label flexGrow={0}>{t('Only_from_users')}</Field.Label>
-					<UserAutoCompleteMultiple value={users} onChange={onChangeUsers} placeholder={t('Please_enter_usernames')} />
+					<UserAutoCompleteMultiple value={users} onChange={handleUsers} placeholder={t('Please_enter_usernames')} />
 				</Field>
 				<Field>
 					<Field.Row>
diff --git a/apps/meteor/client/views/room/contextualBar/PruneMessages/PruneMessagesWithData.tsx b/apps/meteor/client/views/room/contextualBar/PruneMessages/PruneMessagesWithData.tsx
index ce408971979..abbde3da625 100644
--- a/apps/meteor/client/views/room/contextualBar/PruneMessages/PruneMessagesWithData.tsx
+++ b/apps/meteor/client/views/room/contextualBar/PruneMessages/PruneMessagesWithData.tsx
@@ -1,4 +1,4 @@
-import type { IRoom, IUser } from '@rocket.chat/core-typings';
+import type { IRoom } from '@rocket.chat/core-typings';
 import { isDirectMessageRoom } from '@rocket.chat/core-typings';
 import { useMutableCallback } from '@rocket.chat/fuselage-hooks';
 import { useSetModal, useToastMessageDispatch, useUserRoom, useEndpoint, useTranslation } from '@rocket.chat/ui-contexts';
@@ -22,7 +22,7 @@ export const initialValues = {
 	newerTime: '',
 	olderDate: '',
 	olderTime: '',
-	users: [] as IUser['username'][],
+	users: [],
 	inclusive: false,
 	pinned: false,
 	discussion: false,
@@ -50,18 +50,6 @@ const PruneMessagesWithData = ({ rid, tabBar }: { rid: IRoom['_id']; tabBar: Too
 	const { values, handlers, reset } = useForm(initialValues);
 	const { newerDate, newerTime, olderDate, olderTime, users, inclusive, pinned, discussion, threads, attached } =
 		values as typeof initialValues;
-	const { handleUsers } = handlers;
-
-	const onChangeUsers = useMutableCallback((value: IUser['username'], action?: string) => {
-		if (!action) {
-			if (users.includes(value)) {
-				return;
-			}
-			return handleUsers([...users, value]);
-		}
-
-		return handleUsers(users.filter((current) => current !== value));
-	});
 
 	const handlePrune = useMutableCallback((): void => {
 		const handlePruneAction = async (): Promise<void> => {
@@ -196,7 +184,6 @@ const PruneMessagesWithData = ({ rid, tabBar }: { rid: IRoom['_id']; tabBar: Too
 			callOutText={callOutText}
 			validateText={validateText}
 			users={users}
-			onChangeUsers={onChangeUsers}
 			values={values}
 			handlers={handlers}
 			onClickClose={onClickClose}
diff --git a/apps/meteor/client/views/room/contextualBar/RoomMembers/AddUsers/AddUsers.tsx b/apps/meteor/client/views/room/contextualBar/RoomMembers/AddUsers/AddUsers.tsx
index a25a78e4a7b..078f6651c5c 100644
--- a/apps/meteor/client/views/room/contextualBar/RoomMembers/AddUsers/AddUsers.tsx
+++ b/apps/meteor/client/views/room/contextualBar/RoomMembers/AddUsers/AddUsers.tsx
@@ -14,7 +14,7 @@ type AddUsersProps = {
 	onClickSave: () => Promise<void>;
 	users: Exclude<IUser['username'], undefined>[];
 	isRoomFederated: boolean;
-	onChange: (value: IUser['username'][], action?: string) => void;
+	onChange: (value: string | string[]) => void;
 };
 
 const AddUsers = ({ onClickClose, onClickBack, onClickSave, users, isRoomFederated, onChange }: AddUsersProps): ReactElement => {
diff --git a/apps/meteor/client/views/room/contextualBar/RoomMembers/AddUsers/AddUsersWithData.tsx b/apps/meteor/client/views/room/contextualBar/RoomMembers/AddUsers/AddUsersWithData.tsx
index 6b1403b0d80..3233fc7e196 100644
--- a/apps/meteor/client/views/room/contextualBar/RoomMembers/AddUsers/AddUsersWithData.tsx
+++ b/apps/meteor/client/views/room/contextualBar/RoomMembers/AddUsers/AddUsersWithData.tsx
@@ -32,16 +32,6 @@ const AddUsersWithData = ({ rid, onClickBack, reload }: AddUsersWithDataProps):
 	const { users } = values as AddUsersInitialProps;
 	const { handleUsers } = handlers;
 
-	const onChangeUsers = useMutableCallback((value, action) => {
-		if (!action) {
-			if (users.includes(value)) {
-				return;
-			}
-			return handleUsers([...users, value]);
-		}
-		handleUsers(users.filter((current) => current !== value));
-	});
-
 	const handleSave = useMutableCallback(async () => {
 		try {
 			await saveAction({ rid, users });
@@ -52,7 +42,6 @@ const AddUsersWithData = ({ rid, onClickBack, reload }: AddUsersWithDataProps):
 			dispatchToastMessage({ type: 'error', message: error as Error });
 		}
 	});
-	const onChangeUsersFn = isRoomFederated(room) ? handleUsers : onChangeUsers;
 
 	return (
 		<AddUsers
@@ -61,7 +50,7 @@ const AddUsersWithData = ({ rid, onClickBack, reload }: AddUsersWithDataProps):
 			onClickSave={handleSave}
 			users={users}
 			isRoomFederated={isRoomFederated(room)}
-			onChange={onChangeUsersFn}
+			onChange={handleUsers}
 		/>
 	);
 };
diff --git a/apps/meteor/client/views/room/modals/ShareMessageModal/ShareMessageModal.tsx b/apps/meteor/client/views/room/modals/ShareMessageModal/ShareMessageModal.tsx
index a45b0d7f2c9..6736050acb7 100644
--- a/apps/meteor/client/views/room/modals/ShareMessageModal/ShareMessageModal.tsx
+++ b/apps/meteor/client/views/room/modals/ShareMessageModal/ShareMessageModal.tsx
@@ -1,6 +1,6 @@
 import type { IMessage, MessageQuoteAttachment } from '@rocket.chat/core-typings';
 import { Modal, Field, FieldGroup, ButtonGroup, Button } from '@rocket.chat/fuselage';
-import { useMutableCallback, useClipboard } from '@rocket.chat/fuselage-hooks';
+import { useClipboard } from '@rocket.chat/fuselage-hooks';
 import { useTranslation, useEndpoint, useToastMessageDispatch, useUserAvatarPath } from '@rocket.chat/ui-contexts';
 import { useMutation } from '@tanstack/react-query';
 import type { ReactElement } from 'react';
@@ -17,13 +17,6 @@ type ShareMessageProps = {
 	message: IMessage;
 };
 
-type roomType = {
-	label: string;
-	value: string;
-	_id: string;
-	type: string;
-};
-
 const ShareMessageModal = ({ onClose, permalink, message }: ShareMessageProps): ReactElement => {
 	const t = useTranslation();
 	const getUserAvatarPath = useUserAvatarPath();
@@ -39,23 +32,15 @@ const ShareMessageModal = ({ onClose, permalink, message }: ShareMessageProps):
 	const rooms = watch('rooms');
 	const sendMessage = useEndpoint('POST', '/v1/chat.postMessage');
 
-	const onChangeUserOrRoom = useMutableCallback((handleRoomsAndUsers: (rooms: roomType[]) => void, room: roomType, action?: string) => {
-		if (!action) {
-			if (rooms.find((cur: roomType) => cur._id === room._id)) return;
-			return handleRoomsAndUsers([...rooms, room]);
-		}
-		handleRoomsAndUsers(rooms.filter((cur: roomType) => cur._id !== room._id));
-	});
-
 	const sendMessageMutation = useMutation({
 		mutationFn: async () => {
 			const optionalMessage = '';
 			const curMsg = await prependReplies(optionalMessage, [message]);
 
 			return Promise.all(
-				rooms.map(async (room: roomType) => {
+				rooms.map(async (roomId) => {
 					const sendPayload = {
-						roomId: room._id,
+						roomId,
 						text: curMsg,
 					};
 
@@ -106,11 +91,8 @@ const ShareMessageModal = ({ onClose, permalink, message }: ShareMessageProps):
 							<Controller
 								name='rooms'
 								control={control}
-								render={({ field }): ReactElement => (
-									<UserAndRoomAutoCompleteMultiple
-										value={field.value.map((room: roomType) => room.value)}
-										onChange={(room, action): void => onChangeUserOrRoom(field.onChange, room, action)}
-									/>
+								render={({ field: { value, onChange } }): ReactElement => (
+									<UserAndRoomAutoCompleteMultiple value={value} onChange={onChange} />
 								)}
 							/>
 						</Field.Row>
diff --git a/apps/meteor/client/views/teams/contextualBar/TeamAutocomplete/Avatar.js b/apps/meteor/client/views/teams/contextualBar/TeamAutocomplete/Avatar.js
index cb931078bd2..a547a15f0c3 100644
--- a/apps/meteor/client/views/teams/contextualBar/TeamAutocomplete/Avatar.js
+++ b/apps/meteor/client/views/teams/contextualBar/TeamAutocomplete/Avatar.js
@@ -3,7 +3,7 @@ import React from 'react';
 
 import RoomAvatar from '../../../../components/avatar/RoomAvatar';
 
-const Avatar = ({ _id, type, avatarETag, test, ...props }) => (
+const Avatar = ({ _id, type, avatarETag, ...props }) => (
 	<RoomAvatar size={Options.AvatarSize} room={{ type, _id, avatarETag }} {...props} />
 );
 
diff --git a/apps/meteor/client/views/teams/contextualBar/TeamAutocomplete/TeamAutocomplete.js b/apps/meteor/client/views/teams/contextualBar/TeamAutocomplete/TeamAutocomplete.js
deleted file mode 100644
index 7b3d3523dc3..00000000000
--- a/apps/meteor/client/views/teams/contextualBar/TeamAutocomplete/TeamAutocomplete.js
+++ /dev/null
@@ -1,39 +0,0 @@
-import { AutoComplete, Option } from '@rocket.chat/fuselage';
-import React, { memo, useMemo, useState } from 'react';
-
-import { useEndpointData } from '../../../../hooks/useEndpointData';
-import Avatar from './Avatar';
-
-const TeamAutocomplete = (props) => {
-	const [filter, setFilter] = useState('');
-
-	const { value: data } = useEndpointData('/v1/teams.autocomplete', { params: useMemo(() => ({ name: filter }), [filter]) });
-
-	const options = useMemo(
-		() =>
-			(data &&
-				data.teams.map(({ name, teamId, _id, avatarETag, t }) => ({
-					value: teamId,
-					label: { name, avatarETag, type: t, _id },
-				}))) ||
-			[],
-		[data],
-	);
-
-	return (
-		<AutoComplete
-			{...props}
-			filter={filter}
-			setFilter={setFilter}
-			renderSelected={({ label }) => (
-				<>
-					<Avatar size='x20' {...label} test='selected' /> {label.name}
-				</>
-			)}
-			renderItem={({ value, label, ...props }) => <Option key={value} {...props} label={label.name} avatar={<Avatar {...label} />} />}
-			options={options}
-		/>
-	);
-};
-
-export default memo(TeamAutocomplete);
diff --git a/apps/meteor/client/views/teams/contextualBar/TeamAutocomplete/TeamAutocomplete.tsx b/apps/meteor/client/views/teams/contextualBar/TeamAutocomplete/TeamAutocomplete.tsx
new file mode 100644
index 00000000000..8084336a57a
--- /dev/null
+++ b/apps/meteor/client/views/teams/contextualBar/TeamAutocomplete/TeamAutocomplete.tsx
@@ -0,0 +1,46 @@
+import { AutoComplete, Option, Box } from '@rocket.chat/fuselage';
+import { useEndpoint } from '@rocket.chat/ui-contexts';
+import { useQuery } from '@tanstack/react-query';
+import type { ComponentProps } from 'react';
+import React, { memo, useMemo, useState } from 'react';
+
+import Avatar from './Avatar';
+
+type TeamAutocompleteProps = Omit<ComponentProps<typeof AutoComplete>, 'filter'>;
+
+const TeamAutocomplete = ({ value, onChange, ...props }: TeamAutocompleteProps) => {
+	const [filter, setFilter] = useState('');
+
+	const teamsAutoCompleteEndpoint = useEndpoint('GET', '/v1/teams.autocomplete');
+	const { data, isSuccess } = useQuery(['teamsAutoComplete', filter], async () => teamsAutoCompleteEndpoint({ name: filter }));
+
+	const options = useMemo(
+		() =>
+			isSuccess
+				? data?.teams.map(({ name, teamId, _id, avatarETag, t }) => ({
+						value: teamId as string,
+						label: { name, avatarETag, type: t, _id },
+				  }))
+				: [],
+		[data, isSuccess],
+	);
+
+	return (
+		<AutoComplete
+			{...props}
+			value={value}
+			onChange={onChange}
+			filter={filter}
+			setFilter={setFilter}
+			renderSelected={({ selected: { value, label } }) => (
+				<Box value={value}>
+					<Avatar size='x20' {...label} /> {label.name}
+				</Box>
+			)}
+			renderItem={({ value, label, ...props }) => <Option key={value} {...props} label={label.name} avatar={<Avatar {...label} />} />}
+			options={options}
+		/>
+	);
+};
+
+export default memo(TeamAutocomplete);
diff --git a/apps/meteor/client/views/teams/contextualBar/channels/AddExistingModal/AddExistingModal.tsx b/apps/meteor/client/views/teams/contextualBar/channels/AddExistingModal/AddExistingModal.tsx
index edad41b112b..46ada122f19 100644
--- a/apps/meteor/client/views/teams/contextualBar/channels/AddExistingModal/AddExistingModal.tsx
+++ b/apps/meteor/client/views/teams/contextualBar/channels/AddExistingModal/AddExistingModal.tsx
@@ -1,27 +1,20 @@
 import type { IRoom, Serialized } from '@rocket.chat/core-typings';
 import { Button, Field, Modal } from '@rocket.chat/fuselage';
 import { useToastMessageDispatch, useEndpoint, useTranslation } from '@rocket.chat/ui-contexts';
-import type { FC } from 'react';
 import React, { memo, useCallback } from 'react';
 
 import { useForm } from '../../../../../hooks/useForm';
-import RoomsInput from './RoomsInput';
-
-type AddExistingModalState = {
-	onAdd: any;
-	rooms: Serialized<IRoom>[];
-	onChange: (value: Serialized<IRoom>, action: 'remove' | undefined) => void;
-	hasUnsavedChanges: boolean;
-};
+import RoomsAvailableForTeamsAutoComplete from './RoomsAvailableForTeamsAutoComplete';
 
 type AddExistingModalProps = {
-	onClose: () => void;
 	teamId: string;
+	onClose: () => void;
 	reload: () => void;
 };
 
-const useAddExistingModalState = (onClose: () => void, teamId: string, reload: () => void): AddExistingModalState => {
+const AddExistingModal = ({ onClose, teamId, reload }: AddExistingModalProps) => {
 	const t = useTranslation();
+
 	const addRoomEndpoint = useEndpoint('POST', '/v1/teams.addRooms');
 	const dispatchToastMessage = useToastMessageDispatch();
 
@@ -29,48 +22,25 @@ const useAddExistingModalState = (onClose: () => void, teamId: string, reload: (
 		rooms: [] as Serialized<IRoom>[],
 	});
 
-	const { rooms } = values as { rooms: Serialized<IRoom>[] };
+	const { rooms } = values as { rooms: string[] };
 	const { handleRooms } = handlers;
 
-	const onChange = useCallback<AddExistingModalState['onChange']>(
-		(value, action) => {
-			if (!action) {
-				if (rooms.some((current) => current._id === value._id)) {
-					return;
-				}
-
-				return handleRooms([...rooms, value]);
-			}
-
-			handleRooms(rooms.filter((current: any) => current._id !== value._id));
-		},
-		[handleRooms, rooms],
-	);
-
-	const onAdd = useCallback(async () => {
+	const handleAddChannels = useCallback(async () => {
 		try {
 			await addRoomEndpoint({
-				rooms: rooms.map((room) => room._id),
+				rooms,
 				teamId,
 			});
 
 			dispatchToastMessage({ type: 'success', message: t('Channels_added') });
-			onClose();
 			reload();
-		} catch (error: unknown) {
+		} catch (error) {
 			dispatchToastMessage({ type: 'error', message: error });
+		} finally {
+			onClose();
 		}
 	}, [addRoomEndpoint, rooms, teamId, onClose, dispatchToastMessage, t, reload]);
 
-	return { onAdd, rooms, onChange, hasUnsavedChanges };
-};
-
-const AddExistingModal: FC<AddExistingModalProps> = ({ onClose, teamId, reload }) => {
-	const t = useTranslation();
-	const { rooms, onAdd, onChange, hasUnsavedChanges } = useAddExistingModalState(onClose, teamId, reload);
-
-	const isAddButtonEnabled = hasUnsavedChanges;
-
 	return (
 		<Modal>
 			<Modal.Header>
@@ -80,13 +50,13 @@ const AddExistingModal: FC<AddExistingModalProps> = ({ onClose, teamId, reload }
 			<Modal.Content>
 				<Field mbe='x24'>
 					<Field.Label>{t('Channels')}</Field.Label>
-					<RoomsInput value={rooms} onChange={onChange} />
+					<RoomsAvailableForTeamsAutoComplete value={rooms} onChange={handleRooms} />
 				</Field>
 			</Modal.Content>
 			<Modal.Footer>
 				<Modal.FooterControllers>
 					<Button onClick={onClose}>{t('Cancel')}</Button>
-					<Button disabled={!isAddButtonEnabled} onClick={onAdd} primary>
+					<Button disabled={!hasUnsavedChanges} onClick={handleAddChannels} primary>
 						{t('Add')}
 					</Button>
 				</Modal.FooterControllers>
diff --git a/apps/meteor/client/views/teams/contextualBar/channels/AddExistingModal/RoomsAvailableForTeamsAutoComplete.tsx b/apps/meteor/client/views/teams/contextualBar/channels/AddExistingModal/RoomsAvailableForTeamsAutoComplete.tsx
new file mode 100644
index 00000000000..13e9d95b3df
--- /dev/null
+++ b/apps/meteor/client/views/teams/contextualBar/channels/AddExistingModal/RoomsAvailableForTeamsAutoComplete.tsx
@@ -0,0 +1,69 @@
+import { AutoComplete, Box, Icon, Option, Options, Chip } from '@rocket.chat/fuselage';
+import { useDebouncedValue } from '@rocket.chat/fuselage-hooks';
+import { useEndpoint } from '@rocket.chat/ui-contexts';
+import { useQuery } from '@tanstack/react-query';
+import type { ComponentProps } from 'react';
+import React, { memo, useMemo, useState } from 'react';
+
+import RoomAvatar from '../../../../../components/avatar/RoomAvatar';
+import { roomCoordinator } from '../../../../../lib/rooms/roomCoordinator';
+
+type RoomsAvailableForTeamsAutoCompleteProps = Omit<ComponentProps<typeof AutoComplete>, 'filter'>;
+
+const RoomsAvailableForTeamsAutoComplete = ({ value, onChange, ...props }: RoomsAvailableForTeamsAutoCompleteProps) => {
+	const [filter, setFilter] = useState('');
+	const debouncedFilter = useDebouncedValue(filter, 1000);
+
+	const roomsAvailableForTeamsAutoCompleteEndpoint = useEndpoint('GET', '/v1/rooms.autocomplete.availableForTeams');
+
+	const { data } = useQuery(
+		['roomsAvailableForTeamsAutoComplete', debouncedFilter],
+		async () => roomsAvailableForTeamsAutoCompleteEndpoint({ name: debouncedFilter }),
+		{ keepPreviousData: true },
+	);
+
+	const options = useMemo(() => {
+		if (!data) {
+			return [];
+		}
+
+		return data.items.map((room) => ({
+			value: room._id,
+			label: {
+				name: roomCoordinator.getRoomName(room.t, { _id: room._id, name: room.name, fname: room.fname, prid: room.prid }),
+				type: room.t,
+				avatarETag: room.avatarETag,
+			},
+		}));
+	}, [data]);
+
+	return (
+		<AutoComplete
+			{...props}
+			multiple
+			value={value}
+			onChange={onChange}
+			filter={filter}
+			setFilter={setFilter}
+			renderSelected={({ selected: { value, label }, onRemove }) => (
+				<Chip key={value} height='x20' value={value} onClick={onRemove} mie='x4'>
+					<Icon name={label.type === 'c' ? 'hash' : 'hashtag-lock'} size='x12' />
+					<Box is='span' margin='none' mis='x4'>
+						{label.name}
+					</Box>
+				</Chip>
+			)}
+			renderItem={({ value, label, ...props }) => (
+				<Option
+					key={value}
+					{...props}
+					label={label.name}
+					avatar={<RoomAvatar room={{ _id: value, type: label.type, avatarETag: label.avatarETag }} size={Options.AvatarSize} />}
+				/>
+			)}
+			options={options}
+		/>
+	);
+};
+
+export default memo(RoomsAvailableForTeamsAutoComplete);
diff --git a/apps/meteor/client/views/teams/contextualBar/channels/AddExistingModal/RoomsInput.tsx b/apps/meteor/client/views/teams/contextualBar/channels/AddExistingModal/RoomsInput.tsx
deleted file mode 100644
index 16926a211c2..00000000000
--- a/apps/meteor/client/views/teams/contextualBar/channels/AddExistingModal/RoomsInput.tsx
+++ /dev/null
@@ -1,115 +0,0 @@
-import type { IRoom, Serialized } from '@rocket.chat/core-typings';
-import type { AutoCompleteProps } from '@rocket.chat/fuselage';
-import { AutoComplete, Box, Icon, Option, Options, Chip } from '@rocket.chat/fuselage';
-import { useDebouncedValue } from '@rocket.chat/fuselage-hooks';
-import type { FC } from 'react';
-import React, { memo, useCallback, useMemo, useState } from 'react';
-
-import RoomAvatar from '../../../../../components/avatar/RoomAvatar';
-import { useEndpointData } from '../../../../../hooks/useEndpointData';
-import { roomCoordinator } from '../../../../../lib/rooms/roomCoordinator';
-
-type RoomsInputProps = {
-	value: Serialized<IRoom>[];
-	onChange: (value: Serialized<IRoom>, action: 'remove' | undefined) => void;
-};
-
-// TODO: Make AutoComplete accept arbitrary kinds of values
-const useRoomsAutoComplete = (
-	name: string,
-): {
-	rooms: Record<IRoom['_id'], Serialized<IRoom>>;
-	options: AutoCompleteProps['options'];
-} => {
-	const params = useMemo(
-		() => ({
-			name,
-		}),
-		[name],
-	);
-	const { value: data } = useEndpointData('/v1/rooms.autocomplete.availableForTeams', { params });
-
-	const options = useMemo<AutoCompleteProps['options']>(() => {
-		if (!data) {
-			return [];
-		}
-
-		return data.items.map((room: Serialized<IRoom>) => ({
-			label: roomCoordinator.getRoomName(room.t, { _id: room._id, name: room.name, fname: room.fname, prid: room.prid }),
-			value: room._id,
-		}));
-	}, [data]);
-
-	const rooms = useMemo<Record<IRoom['_id'], Serialized<IRoom>>>(
-		() =>
-			data?.items.reduce((obj, room) => {
-				obj[room._id] = room;
-				return obj;
-			}, {} as Record<IRoom['_id'], Serialized<IRoom>>) ?? {},
-		[data],
-	);
-
-	return {
-		options,
-		rooms,
-	};
-};
-
-const RoomsInput: FC<RoomsInputProps> = ({ onChange, ...props }) => {
-	const [filter, setFilter] = useState('');
-	const { rooms, options } = useRoomsAutoComplete(useDebouncedValue(filter, 1000));
-
-	const onClickSelected = useCallback(
-		(e) => {
-			e.stopPropagation();
-			e.preventDefault();
-
-			onChange(rooms[e.currentTarget.value], 'remove');
-		},
-		[onChange, rooms],
-	);
-
-	const handleChange = useCallback<AutoCompleteProps['onChange']>(
-		(value, action: 'remove' | undefined) => {
-			onChange(rooms[value as string], action);
-		},
-		[onChange, rooms],
-	);
-
-	const renderSelected = useCallback<FC<{ value?: IRoom[] }>>(
-		({ value: selected }) => (
-			<>
-				{selected?.map((room) => (
-					<Chip key={room._id} height='x20' value={room._id} onClick={onClickSelected} mie='x4'>
-						<Icon name={room.t === 'c' ? 'hash' : 'hashtag-lock'} size='x12' />
-						<Box is='span' margin='none' mis='x4'>
-							{room.name}
-						</Box>
-					</Chip>
-				))}
-			</>
-		),
-		[onClickSelected],
-	);
-
-	const renderItem = useCallback<FC<{ value: IRoom['_id'] }>>(
-		({ value: rid, ...props }) => (
-			<Option key={rooms[rid]._id} {...props} avatar={<RoomAvatar room={rooms[rid]} size={Options.AvatarSize} />} />
-		),
-		[rooms],
-	);
-
-	return (
-		<AutoComplete
-			{...props}
-			filter={filter}
-			options={options}
-			renderSelected={renderSelected}
-			renderItem={renderItem}
-			setFilter={setFilter}
-			onChange={handleChange}
-		/>
-	);
-};
-
-export default memo(RoomsInput);
diff --git a/apps/meteor/ee/client/views/audit/components/forms/RoomAutoComplete.tsx b/apps/meteor/ee/client/views/audit/components/forms/RoomAutoComplete.tsx
index af6bb9aa9fb..7e01b4feee0 100644
--- a/apps/meteor/ee/client/views/audit/components/forms/RoomAutoComplete.tsx
+++ b/apps/meteor/ee/client/views/audit/components/forms/RoomAutoComplete.tsx
@@ -1,6 +1,4 @@
-import type { IRoom } from '@rocket.chat/core-typings';
 import { AutoComplete, Option, Options } from '@rocket.chat/fuselage';
-import { useMutableCallback } from '@rocket.chat/fuselage-hooks';
 import { useEndpoint } from '@rocket.chat/ui-contexts';
 import { useQuery } from '@tanstack/react-query';
 import type { ComponentProps } from 'react';
@@ -8,10 +6,7 @@ import React, { memo, useMemo, useState } from 'react';
 
 import RoomAvatar from '../../../../../../client/components/avatar/RoomAvatar';
 
-type RoomAutoCompleteProps = Omit<ComponentProps<typeof AutoComplete>, 'value' | 'onChange' | 'filter' | 'setFilter'> & {
-	value: IRoom['_id'] | undefined;
-	onChange: (value: IRoom['_id'] | undefined) => void;
-};
+type RoomAutoCompleteProps = Omit<ComponentProps<typeof AutoComplete>, 'filter'>;
 
 const RoomAutoComplete = ({ value, onChange, ...props }: RoomAutoCompleteProps) => {
 	const [filter, setFilter] = useState('');
@@ -31,23 +26,14 @@ const RoomAutoComplete = ({ value, onChange, ...props }: RoomAutoCompleteProps)
 		[roomAutocompleteQueryResult.data],
 	);
 
-	const handleChange = useMutableCallback((value: unknown, action: 'remove' | undefined) => {
-		if (action === 'remove') {
-			onChange(undefined);
-			return;
-		}
-
-		onChange(value as IRoom['_id']);
-	});
-
 	return (
 		<AutoComplete
 			{...props}
-			value={value as any} // TODO: ????
-			onChange={handleChange}
+			value={value}
+			onChange={onChange}
 			filter={filter}
 			setFilter={setFilter}
-			renderSelected={({ value, label }) => (
+			renderSelected={({ selected: { value, label } }) => (
 				<Option key={value} label={label.name} avatar={<RoomAvatar size={Options.AvatarSize} room={{ _id: value, ...label }} />} />
 			)}
 			renderItem={({ value, label, ...props }) => (
@@ -58,7 +44,7 @@ const RoomAutoComplete = ({ value, onChange, ...props }: RoomAutoCompleteProps)
 					avatar={<RoomAvatar size={Options.AvatarSize} room={{ _id: value, ...label }} />}
 				/>
 			)}
-			options={options as any}
+			options={options}
 		/>
 	);
 };
diff --git a/apps/meteor/ee/client/views/audit/components/forms/UsernamesAutoComplete.tsx b/apps/meteor/ee/client/views/audit/components/forms/UsernamesAutoComplete.tsx
deleted file mode 100644
index 6516b0f0702..00000000000
--- a/apps/meteor/ee/client/views/audit/components/forms/UsernamesAutoComplete.tsx
+++ /dev/null
@@ -1,27 +0,0 @@
-import type { IUser } from '@rocket.chat/core-typings';
-import { useMutableCallback } from '@rocket.chat/fuselage-hooks';
-import type { ComponentProps, ReactElement } from 'react';
-import React from 'react';
-
-import UserAutoCompleteMultiple from '../../../../../../client/components/UserAutoCompleteMultiple';
-
-type UsernamesAutoCompleteProps = Omit<ComponentProps<typeof UserAutoCompleteMultiple>, 'value' | 'onChange' | 'filter' | 'setFilter'> & {
-	value: Exclude<IUser['username'], undefined>[] | undefined;
-	onChange: (value: Exclude<IUser['username'], undefined>[] | undefined) => void;
-};
-
-const UsernamesAutoComplete = ({ value, onChange, ...props }: UsernamesAutoCompleteProps): ReactElement => {
-	const handleChange = useMutableCallback((currentUsername: Exclude<IUser['username'], undefined>, action: 'remove' | undefined) => {
-		const expunged = value?.filter((username) => username !== currentUsername) ?? [];
-		if (action === 'remove') {
-			onChange(expunged);
-			return;
-		}
-
-		onChange([...expunged, currentUsername]);
-	});
-
-	return <UserAutoCompleteMultiple value={value} onChange={handleChange} {...props} />;
-};
-
-export default UsernamesAutoComplete;
diff --git a/apps/meteor/ee/client/views/audit/components/forms/VisitorAutoComplete.tsx b/apps/meteor/ee/client/views/audit/components/forms/VisitorAutoComplete.tsx
index 011ec7eb9d7..de6c43536fc 100644
--- a/apps/meteor/ee/client/views/audit/components/forms/VisitorAutoComplete.tsx
+++ b/apps/meteor/ee/client/views/audit/components/forms/VisitorAutoComplete.tsx
@@ -1,18 +1,10 @@
-import type { ILivechatVisitor } from '@rocket.chat/core-typings';
 import { AutoComplete, Option } from '@rocket.chat/fuselage';
-import { useMutableCallback } from '@rocket.chat/fuselage-hooks';
 import { useEndpoint } from '@rocket.chat/ui-contexts';
 import { useQuery } from '@tanstack/react-query';
 import type { ComponentProps, ReactElement } from 'react';
 import React, { memo, useMemo, useState } from 'react';
 
-type VisitorAutoCompleteProps = Omit<
-	ComponentProps<typeof AutoComplete>,
-	'value' | 'filter' | 'setFilter' | 'renderSelected' | 'renderItem' | 'options' | 'onChange'
-> & {
-	value: ILivechatVisitor['_id'] | undefined;
-	onChange: (value: ILivechatVisitor['_id'] | undefined) => void;
-};
+type VisitorAutoCompleteProps = Omit<ComponentProps<typeof AutoComplete>, 'filter'>;
 
 const VisitorAutoComplete = ({ value, onChange, ...props }: VisitorAutoCompleteProps): ReactElement => {
 	const [filter, setFilter] = useState('');
@@ -28,23 +20,14 @@ const VisitorAutoComplete = ({ value, onChange, ...props }: VisitorAutoCompleteP
 		[visitorAutocompleteQueryResult.data],
 	);
 
-	const handleChange = useMutableCallback((value: unknown, action: 'remove' | undefined) => {
-		if (action === 'remove') {
-			onChange(undefined);
-			return;
-		}
-
-		onChange(value as ILivechatVisitor['_id']);
-	});
-
 	return (
 		<AutoComplete
 			{...props}
-			value={value as any} // TODO: ????
-			onChange={handleChange}
+			value={value}
+			onChange={onChange}
 			filter={filter}
 			setFilter={setFilter}
-			renderSelected={({ label }) => <>{label}</>}
+			renderSelected={({ selected: { label } }) => <>{label}</>}
 			renderItem={({ value, ...props }) => <Option key={value} {...props} />}
 			options={options}
 		/>
diff --git a/apps/meteor/ee/client/views/audit/components/tabs/DirectTab.tsx b/apps/meteor/ee/client/views/audit/components/tabs/DirectTab.tsx
index b4cc9762148..64472d0e1e9 100644
--- a/apps/meteor/ee/client/views/audit/components/tabs/DirectTab.tsx
+++ b/apps/meteor/ee/client/views/audit/components/tabs/DirectTab.tsx
@@ -1,6 +1,4 @@
-import type { IUser } from '@rocket.chat/core-typings';
 import { Field } from '@rocket.chat/fuselage';
-import { useMutableCallback } from '@rocket.chat/fuselage-hooks';
 import { useTranslation } from '@rocket.chat/ui-contexts';
 import type { ReactElement } from 'react';
 import React from 'react';
@@ -30,17 +28,6 @@ const DirectTab = ({ form: { control } }: DirectTabProps): ReactElement => {
 		},
 	});
 
-	const handleChange = useMutableCallback((currentUsername: Exclude<IUser['username'], undefined>, action: 'remove' | undefined) => {
-		const { value, onChange } = usersField;
-
-		if (action === 'remove') {
-			onChange(value.filter((username) => username !== currentUsername));
-			return;
-		}
-
-		onChange([...value, currentUsername]);
-	});
-
 	return (
 		<Field flexShrink={1}>
 			<Field.Label>{t('Users')}</Field.Label>
@@ -48,7 +35,7 @@ const DirectTab = ({ form: { control } }: DirectTabProps): ReactElement => {
 				<UserAutoCompleteMultiple
 					value={usersField.value}
 					error={!!usersFieldState.error}
-					onChange={handleChange}
+					onChange={usersField.onChange}
 					placeholder={t('Username_Placeholder')}
 				/>
 			</Field.Row>
diff --git a/apps/meteor/ee/client/views/audit/components/tabs/UsersTab.tsx b/apps/meteor/ee/client/views/audit/components/tabs/UsersTab.tsx
index 37d14f36c8c..aeed1ae4aa8 100644
--- a/apps/meteor/ee/client/views/audit/components/tabs/UsersTab.tsx
+++ b/apps/meteor/ee/client/views/audit/components/tabs/UsersTab.tsx
@@ -5,8 +5,8 @@ import React from 'react';
 import type { UseFormReturn } from 'react-hook-form';
 import { useController } from 'react-hook-form';
 
+import UserAutoCompleteMultiple from '../../../../../../client/components/UserAutoCompleteMultiple';
 import type { AuditFields } from '../../hooks/useAuditForm';
-import UsernamesAutoComplete from '../forms/UsernamesAutoComplete';
 
 type UsersTabProps = {
 	form: UseFormReturn<AuditFields>;
@@ -32,7 +32,7 @@ const UsersTab = ({ form: { control } }: UsersTabProps): ReactElement => {
 		<Field flexShrink={1}>
 			<Field.Label>{t('Users')}</Field.Label>
 			<Field.Row>
-				<UsernamesAutoComplete
+				<UserAutoCompleteMultiple
 					error={!!usersFieldState.error}
 					value={usersField.value}
 					onChange={usersField.onChange}
diff --git a/apps/meteor/tests/e2e/omnichannel-manager.spec.ts b/apps/meteor/tests/e2e/omnichannel-manager.spec.ts
index 4895e4b7fd1..465e9fc65f7 100644
--- a/apps/meteor/tests/e2e/omnichannel-manager.spec.ts
+++ b/apps/meteor/tests/e2e/omnichannel-manager.spec.ts
@@ -16,7 +16,7 @@ test.describe.serial('omnichannel-manager', () => {
 
 	test('Managers', async ({ page }) => {
 		await test.step('expect add "user1" as manager', async () => {
-			await poOmnichannelManagers.inputUsername.type('user1 ', { delay: 1000 });
+			await poOmnichannelManagers.inputUsername.type('user1', { delay: 1000 });
 			await page.keyboard.press('Enter');
 			await poOmnichannelManagers.btnAdd.click();
 
diff --git a/packages/ui-contexts/src/UserContext.ts b/packages/ui-contexts/src/UserContext.ts
index 178908f010d..f5bda3c8369 100644
--- a/packages/ui-contexts/src/UserContext.ts
+++ b/packages/ui-contexts/src/UserContext.ts
@@ -2,6 +2,8 @@ import type { IRoom, ISubscription, IUser } from '@rocket.chat/core-typings';
 import type { ObjectId, Filter, FindOptions as MongoFindOptions, Document } from 'mongodb';
 import { createContext } from 'react';
 
+import type { SubscriptionWithRoom } from './types/SubscriptionWithRoom';
+
 export type SubscriptionQuery =
 	| {
 			rid: string | ObjectId;
@@ -53,7 +55,7 @@ export type UserContextValue = {
 	querySubscriptions: (
 		query: SubscriptionQuery,
 		options?: FindOptions,
-	) => [subscribe: (onStoreChange: () => void) => () => void, getSnapshot: () => Array<ISubscription> | []];
+	) => [subscribe: (onStoreChange: () => void) => () => void, getSnapshot: () => SubscriptionWithRoom[]];
 
 	loginWithPassword: (user: string | object, password: string) => Promise<void>;
 	loginWithToken: (user: string) => Promise<void>;
diff --git a/packages/ui-contexts/src/hooks/useUserSubscriptions.ts b/packages/ui-contexts/src/hooks/useUserSubscriptions.ts
index 69ae14e5d75..a4ee2ba5f5b 100644
--- a/packages/ui-contexts/src/hooks/useUserSubscriptions.ts
+++ b/packages/ui-contexts/src/hooks/useUserSubscriptions.ts
@@ -1,11 +1,11 @@
 import { useContext, useMemo } from 'react';
 import { useSyncExternalStore } from 'use-sync-external-store/shim';
-import type { ISubscription } from '@rocket.chat/core-typings';
+import type { SubscriptionWithRoom } from '@rocket.chat/ui-contexts/src/types/SubscriptionWithRoom';
 
 import type { FindOptions, SubscriptionQuery } from '../UserContext';
 import { UserContext } from '../UserContext';
 
-export const useUserSubscriptions = (query: SubscriptionQuery, options?: FindOptions): Array<ISubscription> | [] => {
+export const useUserSubscriptions = (query: SubscriptionQuery, options?: FindOptions): SubscriptionWithRoom[] => {
 	const { querySubscriptions } = useContext(UserContext);
 	const [subscribe, getSnapshot] = useMemo(() => querySubscriptions(query, options), [querySubscriptions, query, options]);
 	return useSyncExternalStore(subscribe, getSnapshot);
diff --git a/packages/ui-contexts/src/index.ts b/packages/ui-contexts/src/index.ts
index d6a18bd88c8..523d59b3d59 100644
--- a/packages/ui-contexts/src/index.ts
+++ b/packages/ui-contexts/src/index.ts
@@ -91,6 +91,4 @@ export { UploadResult } from './ServerContext';
 export { TranslationKey, TranslationLanguage } from './TranslationContext';
 export { Fields } from './UserContext';
 
-export interface ITest {
-	test: string;
-}
+export { SubscriptionWithRoom } from './types/SubscriptionWithRoom';
diff --git a/apps/meteor/client/definitions/SubscriptionWithRoom.ts b/packages/ui-contexts/src/types/SubscriptionWithRoom.ts
similarity index 100%
rename from apps/meteor/client/definitions/SubscriptionWithRoom.ts
rename to packages/ui-contexts/src/types/SubscriptionWithRoom.ts
diff --git a/yarn.lock b/yarn.lock
index fb1a37da9ae..71cff5c9146 100644
--- a/yarn.lock
+++ b/yarn.lock
@@ -6711,16 +6711,16 @@ __metadata:
   languageName: node
   linkType: hard
 
-"@rocket.chat/css-in-js@npm:~0.31.23-dev.71":
-  version: 0.31.23-dev.71
-  resolution: "@rocket.chat/css-in-js@npm:0.31.23-dev.71"
+"@rocket.chat/css-in-js@npm:~0.31.23-dev.78":
+  version: 0.31.23-dev.78
+  resolution: "@rocket.chat/css-in-js@npm:0.31.23-dev.78"
   dependencies:
     "@emotion/hash": ^0.9.0
-    "@rocket.chat/css-supports": ~0.31.23-dev.71
-    "@rocket.chat/memo": ~0.31.23-dev.71
-    "@rocket.chat/stylis-logical-props-middleware": ~0.31.23-dev.71
+    "@rocket.chat/css-supports": ~0.31.23-dev.78
+    "@rocket.chat/memo": ~0.31.23-dev.78
+    "@rocket.chat/stylis-logical-props-middleware": ~0.31.23-dev.78
     stylis: ~4.1.3
-  checksum: c3f59baab106b18797d4c38df14fb88b68004e3eaa85b46efdc9c8cdbecfeadeb81fd6c3f049e817b19bcb060e7a322aaeaeccfe69287d6d9767018897fd0bdc
+  checksum: 8fd9b5b5d6a6564db1167b1a18648b3422ab524cd1b7867aad9e0cece74a48011fe8ebbbb12168fb028425952948bb97343436f608fd720324f4314424dd9701
   languageName: node
   linkType: hard
 
@@ -6755,12 +6755,12 @@ __metadata:
   languageName: node
   linkType: hard
 
-"@rocket.chat/css-supports@npm:~0.31.23-dev.71":
-  version: 0.31.23-dev.71
-  resolution: "@rocket.chat/css-supports@npm:0.31.23-dev.71"
+"@rocket.chat/css-supports@npm:~0.31.23-dev.78":
+  version: 0.31.23-dev.78
+  resolution: "@rocket.chat/css-supports@npm:0.31.23-dev.78"
   dependencies:
-    "@rocket.chat/memo": ~0.31.23-dev.71
-  checksum: 916151b87340de77feb7de2167e83a70ff9de282a24a24c6971532fd707032c141d47c16cb5f914d572afde48f99e3eb95050a7338d18d9d7d7b8febb5306c12
+    "@rocket.chat/memo": ~0.31.23-dev.78
+  checksum: 40b3c17f9452096450b4af5ca386b65b547243911b60d405766960bae7e57203eb88ce3b8b441ac63737930c9e656b852937729ad21f57bca93b8a492c5c1685
   languageName: node
   linkType: hard
 
@@ -6949,10 +6949,10 @@ __metadata:
   languageName: node
   linkType: hard
 
-"@rocket.chat/fuselage-tokens@npm:~0.32.0-dev.247":
-  version: 0.32.0-dev.247
-  resolution: "@rocket.chat/fuselage-tokens@npm:0.32.0-dev.247"
-  checksum: 5169b77106d4fa7805a54501b93f6c80c731f786e00e4bccefbbb9ba4a3d72ac8e85656df5c30058ecd25d3788bd21131ea7d2cfac9de64dda96b2e5ace44d06
+"@rocket.chat/fuselage-tokens@npm:~0.32.0-dev.254":
+  version: 0.32.0-dev.254
+  resolution: "@rocket.chat/fuselage-tokens@npm:0.32.0-dev.254"
+  checksum: cb58cc402bd4a7d0efd63f90d198088d991ed9cda0e2db703e20a38991ef796cf876f78a2fdefa736deff03b1f37cdfe00843cdd0373a66375fad0e57d24b7ec
   languageName: node
   linkType: hard
 
@@ -7012,14 +7012,14 @@ __metadata:
   linkType: soft
 
 "@rocket.chat/fuselage@npm:next":
-  version: 0.32.0-dev.297
-  resolution: "@rocket.chat/fuselage@npm:0.32.0-dev.297"
-  dependencies:
-    "@rocket.chat/css-in-js": ~0.31.23-dev.71
-    "@rocket.chat/css-supports": ~0.31.23-dev.71
-    "@rocket.chat/fuselage-tokens": ~0.32.0-dev.247
-    "@rocket.chat/memo": ~0.31.23-dev.71
-    "@rocket.chat/styled": ~0.31.23-dev.71
+  version: 0.32.0-dev.304
+  resolution: "@rocket.chat/fuselage@npm:0.32.0-dev.304"
+  dependencies:
+    "@rocket.chat/css-in-js": ~0.31.23-dev.78
+    "@rocket.chat/css-supports": ~0.31.23-dev.78
+    "@rocket.chat/fuselage-tokens": ~0.32.0-dev.254
+    "@rocket.chat/memo": ~0.31.23-dev.78
+    "@rocket.chat/styled": ~0.31.23-dev.78
     invariant: ^2.2.4
     react-aria: ~3.19.0
     react-keyed-flatten-children: ^1.3.0
@@ -7031,7 +7031,7 @@ __metadata:
     react: ^17.0.2
     react-dom: ^17.0.2
     react-virtuoso: 1.2.4
-  checksum: 66ad44a017305b08d55f3ba764ba1415eefd42e005b4c7930e18c1a416544453e602d13c9353d57255afed668ac0acc2477b10208d879659249bd8a058a0cf2c
+  checksum: 85aa58594d70600178765ea0103f5546e9dc2486edcd65e8ece2dfbf81c809378ea0085482998189dcb88ff17a3ce1f607bac50eed0c64cf1a39ea9000aec356
   languageName: node
   linkType: hard
 
@@ -7253,10 +7253,10 @@ __metadata:
   languageName: node
   linkType: hard
 
-"@rocket.chat/memo@npm:~0.31.23-dev.71":
-  version: 0.31.23-dev.71
-  resolution: "@rocket.chat/memo@npm:0.31.23-dev.71"
-  checksum: fc24b57f53f2eb6f6d321612e4ad1db5531c28440a51bcfad7dbe527de3ccc91d4e4beae787912f9536e47da059ab882486b951494305288d223c5583028ff03
+"@rocket.chat/memo@npm:~0.31.23-dev.78":
+  version: 0.31.23-dev.78
+  resolution: "@rocket.chat/memo@npm:0.31.23-dev.78"
+  checksum: 0d649e2e18e21bf46c6864acf81784c4f9f3e8965a0afb16a030f0d2dccfe1d52d6e56deb4402411256741dd3591f752852f7b66013664b160097dca62f6f5c1
   languageName: node
   linkType: hard
 
@@ -8003,13 +8003,13 @@ __metadata:
   languageName: node
   linkType: hard
 
-"@rocket.chat/styled@npm:~0.31.23-dev.71":
-  version: 0.31.23-dev.71
-  resolution: "@rocket.chat/styled@npm:0.31.23-dev.71"
+"@rocket.chat/styled@npm:~0.31.23-dev.78":
+  version: 0.31.23-dev.78
+  resolution: "@rocket.chat/styled@npm:0.31.23-dev.78"
   dependencies:
-    "@rocket.chat/css-in-js": ~0.31.23-dev.71
+    "@rocket.chat/css-in-js": ~0.31.23-dev.78
     tslib: ^2.3.1
-  checksum: 4bf7050817cd7d2b057e5980692b7083481cacb3fe4af74b73e2e7f3c94d5e8b078fd9fea05696b3fb94cd5811f45873731ae24eae53c1767943d427a4e408d9
+  checksum: b2daaf60c610258bf2d4bf06ba1c0f67221a0cead62d2d5b216786f16838e9481803a5f86191bfc9dd0b6d5ed37181c82d0a6e86c16050e2a5f4158f28f2ff0a
   languageName: node
   linkType: hard
 
@@ -8047,15 +8047,15 @@ __metadata:
   languageName: node
   linkType: hard
 
-"@rocket.chat/stylis-logical-props-middleware@npm:~0.31.23-dev.71":
-  version: 0.31.23-dev.71
-  resolution: "@rocket.chat/stylis-logical-props-middleware@npm:0.31.23-dev.71"
+"@rocket.chat/stylis-logical-props-middleware@npm:~0.31.23-dev.78":
+  version: 0.31.23-dev.78
+  resolution: "@rocket.chat/stylis-logical-props-middleware@npm:0.31.23-dev.78"
   dependencies:
-    "@rocket.chat/css-supports": ~0.31.23-dev.71
+    "@rocket.chat/css-supports": ~0.31.23-dev.78
     tslib: ^2.3.1
   peerDependencies:
     stylis: 4.0.10
-  checksum: 8473c06479ab42ed05ce77903fc975fa465e0d3665ee7ff405bdc7bfecbfea869acad68db449a139a3676d20bd605d27358e6a5ee562a694c57439d805cdc056
+  checksum: 033b193ba7f43fe86634a73128d6d44a6e98c8f371ae9b8fb1b099b68394cd2154e9be246a2c4fe214c2df7f712df68a5ae03e6dfda192796cd620828a3d061c
   languageName: node
   linkType: hard
 
@@ -26485,7 +26485,7 @@ __metadata:
   resolution: "lamejs@https://github.com/zhuker/lamejs.git#commit=582bbba6a12f981b984d8fb9e1874499fed85675"
   dependencies:
     use-strict: 1.0.1
-  checksum: ed7f6f1c9629b53c17023eb04b4fc5a222e9c34fcb4a2f61214488fc64e5cfea825e4588d959c5fb20f3a91f0120103fa60307dd43df995d498ff5ddb6200cd9
+  checksum: fa829e0c170a65573e653b4d908a44aaf06a50e1bbade3b1217a300a03ccd59a537e294e2d924a584f9d70c7726a12d4c3af9c675436d48d08be5fb94b5eb400
   languageName: node
   linkType: hard
 
-- 
GitLab