diff --git a/apps/meteor/app/models/client/models/CachedChatSubscription.ts b/apps/meteor/app/models/client/models/CachedChatSubscription.ts index bc4791ab421ffed6d6241d1f19701a65a2361768..02798f1952a0dcfc6e8f1096da4f31056c535715 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 5ca1be79ee06396517059474c4764ceda7ef347b..c8640c45d6e05b072dc7fffe546de5817fbb2d97 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 6aeeda72490a42d354fe78973c423fdfa97758b2..0d56d1247b814de9b3466147865e5beb15ecfc5c 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 5054b4087d94c240dab503025d324a08c4139b4c..64c1c946d06d29d2b034c7ee47a4fd7efdfbf596 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 9e7791f1896d4ee85da065fd3c455f10a73ea785..77194185cc8b689b468f21600e340b797724a661 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 dd1cceb4699e3f9e3312314aab759e923b317348..f557c5a339e4dbe1d0adf5fb828d94b06fc67c6d 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 f4112b21d5369818c98c7119bbd9b9b8461f64c5..f1f079150bbee0c86f42f8989466572a641deb86 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 0e9c45a6a382e93c456ef003ba330aa42d64bea7..2403af0a87cb8cb7287761e62e80a15db2e06acf 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 3c85ea0a130734c1a4c47a3bc24088fb79357899..168a5ca98a5b6426c52adeb46fdb388e2f99aef1 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 7206e4c09578360f26024218de0638bb50eea78a..527031a6ae4488e2942c4da22594281d107f6adf 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 fc28f970b99be27ef309cfefdf0f807ad9452ff0..13ab355547617828d7cac172701fd8f21e692f0f 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 1df48100157b0b0821b220eab947c69b70007cab..2341995afe45cdc805423dd337f894ee318ef12d 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 9989a17cc81f6a6bee265748ecef3ea0f4441701..745bd82d2837d7b24ccd58dbd255ec6b59422819 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 f6192a94926253677a27832cfc38777bd7d5ce69..078ea3bd797a1ff1cb366adb1d301d7454fc76d9 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 a3eaa7dd494b51469533603a56ff9acba95477a8..4ee42cec17bfaadc415eae2e2754b8b2a49ea793 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 347c5f5d5eb6fcb10aa0b8086fd52234ebc6b964..eb728871d0604308be7aac1268ddba62de553a9b 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 1ef76603413f70ff603c70126a5ef175e2f5e977..3ab5c428d54efb19e45e37a814d47f808372b76b 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 ce4089719799f13b25040a5fa64b70931a3315ee..abbde3da6250f61eb43c721d347d2d84fa835a08 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 a25a78e4a7b94daf1294693809d7f04b9a39b135..078f6651c5cf0f60c240ef82fcd2d5ec4abd4f2a 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 6b1403b0d80587f655f8279202b84cd482dd812a..3233fc7e19607b8e01fc87e6f1c373cfdfca4236 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 a45b0d7f2c9be716f5d0ab9832b48b34b99620f8..6736050acb701733c380b2ca007b4e56edcb5399 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 cb931078bd2321be4c68cddb258d9337afd2a8a6..a547a15f0c3041a9d6878a1be63161f92de0cc05 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 7b3d3523dc3c9ec5d00dcfa57cb02eebd86e65f3..0000000000000000000000000000000000000000 --- 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 0000000000000000000000000000000000000000..8084336a57ab7f4dd9fb14eb458fb2b621cad5b6 --- /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 edad41b112b88c931355fe11aeabf9cccb5e4dbc..46ada122f19c8db68da7c38ef49337712c056d27 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 0000000000000000000000000000000000000000..13e9d95b3df1bee90533e2534b9fc4081125b697 --- /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 16926a211c20a0ba696cb40a3b759c41c790932c..0000000000000000000000000000000000000000 --- 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 af6bb9aa9fbe63fe65fc5ba26bb3763999da0c8c..7e01b4feee04078a9019b3f7d8f8a56a149bdb3d 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 6516b0f0702f799329268eb860dac4de80be859e..0000000000000000000000000000000000000000 --- 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 011ec7eb9d76bdd01ea44d106c6098aa2bfeb1e2..de6c43536fc75d5e9f400fb60417ea62e18cc9c9 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 b4cc9762148cf09fbc741e32051afc1d30903965..64472d0e1e91d06c7de879da14b88f26f08c2e94 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 37d14f36c8c5e7772f077896ea0ce0b01561811b..aeed1ae4aa8cee0aca040f2249afee9067471fff 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 4895e4b7fd10ea7bb8d95a5f0bedcea55430852a..465e9fc65f79775f4dfd8ab7287a001938580232 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 178908f010de0eee6a27fa7e92c55b5998a5a941..f5bda3c8369ae9852a8e5fe4a9842d61aa5019af 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 69ae14e5d75d610f5101933a37c68c3c3be4a203..a4ee2ba5f5bd527227d7ba7533455421f21f91b8 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 d6a18bd88c880935d623e1801a9059ffc42b5552..523d59b3d5943f04731908cbbcb222a5fffd244c 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 fb1a37da9ae90b26bbb744eca476787e2ffc6b3a..71cff5c914619194d636a981e9802f6e7d994bc5 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