diff --git a/.vscode/settings.json b/.vscode/settings.json index b6244e79e3df1..3fb8fa0aeba02 100644 --- a/.vscode/settings.json +++ b/.vscode/settings.json @@ -16,6 +16,7 @@ "typescript.tsdk": "./node_modules/typescript/lib", "cSpell.words": [ "autotranslate", + "fname", "katex", "listbox", "livechat", @@ -23,6 +24,7 @@ "omnichannel", "photoswipe", "searchbox", - "tmid" + "tmid", + "tshow" ] } diff --git a/apps/meteor/app/theme/client/imports/general/base_old.css b/apps/meteor/app/theme/client/imports/general/base_old.css index 3109c5ef03883..4ff58a82992c4 100644 --- a/apps/meteor/app/theme/client/imports/general/base_old.css +++ b/apps/meteor/app/theme/client/imports/general/base_old.css @@ -1051,38 +1051,6 @@ } } -.rc-old .message-popup-position { - position: relative; - - .message-popup { - display: flex; - flex-direction: column; - - max-height: 20rem; - align-items: stretch; - - .message-popup-items { - overflow-y: auto; - - .popup-slash-command { - display: flex; - flex-flow: row wrap; - - &-format { - flex-grow: 1; - } - - &-description { - text-align: right; - - font-style: italic; - flex-grow: 2; - } - } - } - } -} - .rc-old .message-popup-items.preview-items { display: flex; overflow-y: auto; @@ -1111,16 +1079,6 @@ box-shadow: 0 -1px 10px 0 rgba(0, 0, 0, 0.2), 0 1px 1px rgba(0, 0, 0, 0.16); } -.rc-old .message-popup-title { - padding: 0 20px; - - border-width: 0 0 1px; - - font-size: 14px; - font-weight: 300; - line-height: 32px; -} - .rc-old .popup-item { padding: 0 20px; diff --git a/apps/meteor/app/ui-message/client/index.ts b/apps/meteor/app/ui-message/client/index.ts index 4f3cf35981961..61c8b385ebe75 100644 --- a/apps/meteor/app/ui-message/client/index.ts +++ b/apps/meteor/app/ui-message/client/index.ts @@ -1,6 +1 @@ -import './popup/customMessagePopups'; -import './popup/messagePopup'; -import './popup/messagePopupChannel'; -import './popup/messagePopupConfig'; -import './popup/messagePopupEmoji'; import './popup/messagePopupSlashCommandPreview'; diff --git a/apps/meteor/app/ui-message/client/messageBox/createComposerAPI.ts b/apps/meteor/app/ui-message/client/messageBox/createComposerAPI.ts index 33d93f40a6616..f648566203ec9 100644 --- a/apps/meteor/app/ui-message/client/messageBox/createComposerAPI.ts +++ b/apps/meteor/app/ui-message/client/messageBox/createComposerAPI.ts @@ -243,8 +243,42 @@ export const createComposerAPI = (input: HTMLTextAreaElement, storageID: string) setText(Meteor._localStorage.getItem(storageID) ?? ''); + // Gets the text that is connected to the cursor and replaces it with the given text + const replaceText = (text: string, selection: { readonly start: number; readonly end: number }): void => { + const { selectionStart, selectionEnd } = input; + + // Selects the text that is connected to the cursor + input.setSelectionRange(selection.start ?? 0, selection.end ?? text.length); + const textAreaTxt = input.value; + + if (!document.execCommand || !document.execCommand('insertText', false, text)) { + input.value = textAreaTxt.substring(0, selection.start) + text + textAreaTxt.substring(selection.end); + } + + input.selectionStart = selectionStart + text.length; + input.selectionEnd = selectionStart + text.length; + if (selectionStart !== selectionEnd) { + input.selectionStart = selectionStart; + } + + triggerEvent(input, 'input'); + triggerEvent(input, 'change'); + + focus(); + }; + return { + replaceText, insertNewLine, + blur: () => input.blur(), + + substring: (start: number, end?: number) => { + return input.value.substring(start, end); + }, + + getCursorPosition: () => { + return input.selectionStart; + }, setCursorToEnd: () => { input.selectionEnd = input.value.length; input.selectionStart = input.selectionEnd; diff --git a/apps/meteor/app/ui-message/client/popup/ComposerBoxPopup.tsx b/apps/meteor/app/ui-message/client/popup/ComposerBoxPopup.tsx new file mode 100644 index 0000000000000..fe8fdfcd0ad9b --- /dev/null +++ b/apps/meteor/app/ui-message/client/popup/ComposerBoxPopup.tsx @@ -0,0 +1,68 @@ +import { Box, Option, OptionSkeleton, Tile } from '@rocket.chat/fuselage'; +import { useUniqueId } from '@rocket.chat/fuselage-hooks'; +import type { UseQueryResult } from '@tanstack/react-query'; +import type { ReactElement } from 'react'; +import React from 'react'; + +type ComposerBoxPopupProps< + T extends { + _id: string; + sort?: number; + }, +> = { + title?: string; + focused?: T; + items: UseQueryResult[]; + select: (item: T) => void; + renderItem?: ({ item }: { item: T }) => ReactElement; +}; + +export const ComposerBoxPopup = < + T extends { + _id: string; + sort?: number; + }, +>({ + title, + items, + focused, + select, + renderItem = ({ item }: { item: T }) => <>{JSON.stringify(item)}, +}: ComposerBoxPopupProps): ReactElement | null => { + const id = useUniqueId(); + return ( + + + {title && ( + + {title} + + )} + + {items + .flatMap((item) => { + if (item.isSuccess) { + return item.data; + } + return []; + }) + .sort((a, b) => (('sort' in a && a.sort) || 0) - (('sort' in b && b.sort) || 0)) + .map((item, index) => { + return ( + + ); + })} + {items.some((item) => item.isLoading && item.fetchStatus !== 'idle') && } + + + + ); +}; diff --git a/apps/meteor/app/ui-message/client/popup/components/ComposerBoxPopupCannedResponse.tsx b/apps/meteor/app/ui-message/client/popup/components/ComposerBoxPopupCannedResponse.tsx new file mode 100644 index 0000000000000..fb97c7cc813f7 --- /dev/null +++ b/apps/meteor/app/ui-message/client/popup/components/ComposerBoxPopupCannedResponse.tsx @@ -0,0 +1,21 @@ +import React from 'react'; +import { OptionColumn, OptionContent } from '@rocket.chat/fuselage'; + +export type ComposerBoxPopupCannedResponseProps = { + _id: string; + text: string; + shortcut: string; +}; + +const ComposerPopupCannedResponse = ({ shortcut, text }: ComposerBoxPopupCannedResponseProps) => { + return ( + <> + + {shortcut} + + {text} + + ); +}; + +export default ComposerPopupCannedResponse; diff --git a/apps/meteor/app/ui-message/client/popup/components/ComposerBoxPopupEmoji.tsx b/apps/meteor/app/ui-message/client/popup/components/ComposerBoxPopupEmoji.tsx new file mode 100644 index 0000000000000..a30be39fea212 --- /dev/null +++ b/apps/meteor/app/ui-message/client/popup/components/ComposerBoxPopupEmoji.tsx @@ -0,0 +1,21 @@ +import React from 'react'; +import { OptionColumn, OptionContent } from '@rocket.chat/fuselage'; + +import Emoji from '../../../../../client/components/Emoji'; + +export type ComposerBoxPopupEmojiProps = { + _id: string; +}; + +const ComposerPopupEmoji = ({ _id }: ComposerBoxPopupEmojiProps) => { + return ( + <> + + + + {_id} + + ); +}; + +export default ComposerPopupEmoji; diff --git a/apps/meteor/app/ui-message/client/popup/components/ComposerBoxPopupRoom.tsx b/apps/meteor/app/ui-message/client/popup/components/ComposerBoxPopupRoom.tsx new file mode 100644 index 0000000000000..9b04981dd332a --- /dev/null +++ b/apps/meteor/app/ui-message/client/popup/components/ComposerBoxPopupRoom.tsx @@ -0,0 +1,22 @@ +import React from 'react'; +import { OptionColumn, OptionContent } from '@rocket.chat/fuselage'; +import type { IRoom } from '@rocket.chat/core-typings'; + +import { RoomIcon } from '../../../../../client/components/RoomIcon'; + +export type ComposerBoxPopupRoomProps = Pick; + +const ComposerBoxPopupRoom = ({ fname, name, ...props }: ComposerBoxPopupRoomProps) => { + return ( + <> + + + + + {fname ?? name} + + + ); +}; + +export default ComposerBoxPopupRoom; diff --git a/apps/meteor/app/ui-message/client/popup/components/ComposerBoxPopupSlashCommand.tsx b/apps/meteor/app/ui-message/client/popup/components/ComposerBoxPopupSlashCommand.tsx new file mode 100644 index 0000000000000..3cb2638027b02 --- /dev/null +++ b/apps/meteor/app/ui-message/client/popup/components/ComposerBoxPopupSlashCommand.tsx @@ -0,0 +1,21 @@ +import React from 'react'; +import { OptionColumn, OptionContent } from '@rocket.chat/fuselage'; + +export type ComposerBoxPopupSlashCommandProps = { + _id: string; + description?: string; + params?: string; +}; + +const ComposerPopupSlashCommand = ({ _id, description, params }: ComposerBoxPopupSlashCommandProps) => { + return ( + <> + + {_id} {params} + + {description} + + ); +}; + +export default ComposerPopupSlashCommand; diff --git a/apps/meteor/app/ui-message/client/popup/components/ComposerBoxPopupUser.tsx b/apps/meteor/app/ui-message/client/popup/components/ComposerBoxPopupUser.tsx new file mode 100644 index 0000000000000..30c4777300748 --- /dev/null +++ b/apps/meteor/app/ui-message/client/popup/components/ComposerBoxPopupUser.tsx @@ -0,0 +1,50 @@ +import React from 'react'; +import { useTranslation } from '@rocket.chat/ui-contexts'; +import { OptionAvatar, OptionColumn, OptionContent } from '@rocket.chat/fuselage'; + +import UserAvatar from '../../../../../client/components/avatar/UserAvatar'; +import ReactiveUserStatus from '../../../../../client/components/UserStatus/ReactiveUserStatus'; + +export type ComposerBoxPopupUserProps = { + _id: string; + system?: boolean; + outside?: boolean; + suggestion?: boolean; + username: string; + name?: string; + nickname?: string; + status?: string; + sort?: number; +}; + +const ComposerBoxPopupUser = ({ _id, system, username, name, nickname, outside, suggestion }: ComposerBoxPopupUserProps) => { + const t = useTranslation(); + return ( + <> + {!system && ( + <> + + + + + + + + {name ?? username} {nickname && ({nickname})} + + + )} + + {system && ( + + {username} {name} + + )} + + {outside && {t('Not_in_channel')}} + {suggestion && {t('Suggestion_from_recent_messages')}} + + ); +}; + +export default ComposerBoxPopupUser; diff --git a/apps/meteor/app/ui-message/client/popup/customMessagePopups.js b/apps/meteor/app/ui-message/client/popup/customMessagePopups.js deleted file mode 100644 index b86518a1b313e..0000000000000 --- a/apps/meteor/app/ui-message/client/popup/customMessagePopups.js +++ /dev/null @@ -1,29 +0,0 @@ -import { ReactiveVar } from 'meteor/reactive-var'; -import { Tracker } from 'meteor/tracker'; - -export const customMessagePopups = new ReactiveVar([]); - -const nonReactiveGetFunc = () => Tracker.nonreactive(() => customMessagePopups.get()); - -export const addMessagePopup = (configGetter, name) => { - customMessagePopups.set([ - ...nonReactiveGetFunc(), - { - configGetter, - name, - }, - ]); -}; - -export const removeMessagePopup = (popupName) => { - const customMessagePopupsList = nonReactiveGetFunc(); - const element = customMessagePopupsList.findIndex(({ name }) => name === popupName); - if (element < 0) { - return; - } - const listWithRemovedElement = [ - ...customMessagePopupsList.slice(0, element), - ...customMessagePopupsList.slice(element + 1, customMessagePopupsList.length), - ]; - customMessagePopups.set([...listWithRemovedElement]); -}; diff --git a/apps/meteor/app/ui-message/client/popup/hooks/useComposerBoxPopup.ts b/apps/meteor/app/ui-message/client/popup/hooks/useComposerBoxPopup.ts new file mode 100644 index 0000000000000..a997a4b8b7805 --- /dev/null +++ b/apps/meteor/app/ui-message/client/popup/hooks/useComposerBoxPopup.ts @@ -0,0 +1,256 @@ +import { useMutableCallback } from '@rocket.chat/fuselage-hooks'; +import type { UseQueryResult } from '@tanstack/react-query'; +import type { ReactElement } from 'react'; +import { useEffect, useCallback, useState } from 'react'; + +import { useChat } from '../../../../../client/views/room/contexts/ChatContext'; +import { useComposerBoxPopupQueries } from './useComposerBoxPopupQueries'; + +export type ComposerBoxPopupOptions = { + title?: string; + getItemsFromLocal: (filter: string) => Promise; + getItemsFromServer: (filter: string) => Promise; + focused?: T | undefined; + blurOnSelectItem?: boolean; + closeOnEsc?: boolean; + + trigger?: string; + triggerAnywhere?: boolean; + + suffix?: string; + prefix?: string; + + matchSelectorRegex?: RegExp; + + getValue(item: T): string; + + renderItem?: ({ item }: { item: T }) => ReactElement; +}; + +type ComposerBoxPopupResult = + | { + popup: ComposerBoxPopupOptions; + items: UseQueryResult[]; + focused: T | undefined; + ariaActiveDescendant: string | undefined; + select: (item: T) => void; + callbackRef: (node: HTMLElement) => void; + } + | { + popup: undefined; + items: undefined; + focused: undefined; + ariaActiveDescendant: undefined; + callbackRef: (node: HTMLElement) => void; + select: undefined; + }; + +const keys = { + TAB: 9, + ENTER: 13, + ESC: 27, + ARROW_LEFT: 37, + ARROW_UP: 38, + ARROW_RIGHT: 39, + ARROW_DOWN: 40, +}; + +export const useComposerBoxPopup = ({ + configurations, +}: { + configurations: ComposerBoxPopupOptions[]; +}): ComposerBoxPopupResult => { + const [popup, setPopup] = useState | undefined>(undefined); + const [focused, setFocused] = useState(undefined); + const [filter, setFilter] = useState(''); + + const items = useComposerBoxPopupQueries(filter, popup) as unknown as UseQueryResult[]; + + const chat = useChat(); + + const ariaActiveDescendant = focused ? `popup-item-${focused._id}` : undefined; + + useEffect(() => { + if (!popup) { + return; + } + setFocused((focused) => { + const sortedItems = items + .filter((item) => item.isSuccess) + .flatMap((item) => item.data as T[]) + .sort((a, b) => (('sort' in a && a.sort) || 0) - (('sort' in b && b.sort) || 0)); + return sortedItems.find((item) => item._id === focused?._id) ?? sortedItems[0]; + }); + }, [items, popup]); + + const select = useMutableCallback((item: T) => { + if (!popup) { + throw new Error('No popup is open'); + } + + const value = chat?.composer?.substring(0, chat?.composer?.selection.start); + const selector = + popup.matchSelectorRegex ?? + (popup.triggerAnywhere ? new RegExp(`(?:^| |\n)(${popup.trigger})([^\\s]*$)`) : new RegExp(`(?:^)(${popup.trigger})([^\\s]*$)`)); + + const result = value?.match(selector); + if (!result || !value) { + return; + } + + chat?.composer?.replaceText((popup.prefix ?? popup.trigger ?? '') + popup.getValue(item) + (popup.suffix ?? ''), { + start: value.lastIndexOf(result[1] + result[2]), + end: chat?.composer?.selection.start, + }); + + setPopup(undefined); + setFocused(undefined); + }); + + const setConfigByInput = useMutableCallback((): ComposerBoxPopupOptions | undefined => { + const value = chat?.composer?.substring(0, chat?.composer?.selection.start); + + if (!value) { + setPopup(undefined); + setFocused(undefined); + return; + } + + const configuration = configurations.find(({ trigger, matchSelectorRegex, triggerAnywhere }) => { + const selector = + matchSelectorRegex ?? (triggerAnywhere ? new RegExp(`(?:^| |\n)(${trigger})[^\\s]*$`) : new RegExp(`(?:^)(${trigger})[^\\s]*$`)); + const result = selector.test(value); + return result; + }); + + setPopup(configuration); + if (!configuration) { + setFocused(undefined); + setFilter(''); + } + if (configuration) { + const selector = + configuration.matchSelectorRegex ?? + (configuration.triggerAnywhere + ? new RegExp(`(?:^| |\n)(${configuration.trigger})([^\\s]*$)`) + : new RegExp(`(?:^)(${configuration.trigger})([^\\s]*$)`)); + const result = value.match(selector); + setFilter(result ? result[2] : ''); + } + return configuration; + }); + + const onFocus = useMutableCallback(() => { + if (popup) { + return; + } + setConfigByInput(); + }); + + const keyup = useMutableCallback((event: KeyboardEvent) => { + if (!setConfigByInput()) { + return; + } + + if (!popup) { + return; + } + + if (popup.closeOnEsc === true && event.which === keys.ESC) { + setPopup(undefined); + setFocused(undefined); + event.preventDefault(); + event.stopPropagation(); + } + }); + + const keydown = useMutableCallback((event: KeyboardEvent) => { + if (!popup) { + return; + } + + if (event.which === keys.ENTER || event.which === keys.TAB) { + if (!focused) { + return; + } + + select(focused); + + event.preventDefault(); + event.stopPropagation(); + return true; + } + if (event.which === keys.ARROW_UP && !(event.shiftKey || event.ctrlKey || event.altKey || event.metaKey)) { + setFocused((focused) => { + const list = items + .filter((item) => item.isSuccess) + .flatMap((item) => item.data as T[]) + .sort((a, b) => (('sort' in a && a.sort) || 0) - (('sort' in b && b.sort) || 0)); + + if (!list) { + return; + } + + const focusedIndex = list.findIndex((item) => item === focused); + + return (focusedIndex > -1 ? list[focusedIndex - 1] : list[list.length - 1]) as T; + }); + event.preventDefault(); + event.stopPropagation(); + return true; + } + if (event.which === keys.ARROW_DOWN && !(event.shiftKey || event.ctrlKey || event.altKey || event.metaKey)) { + setFocused((focused) => { + const list = items + .filter((item) => item.isSuccess) + .flatMap((item) => item.data as T[]) + .sort((a, b) => (('sort' in a && a.sort) || 0) - (('sort' in b && b.sort) || 0)); + + if (!list) { + return undefined; + } + + const focusedIndex = list.findIndex((item) => item === focused); + + return (focusedIndex < list.length - 1 ? list[focusedIndex + 1] : list[0]) as T; + }); + event.preventDefault(); + event.stopPropagation(); + return true; + } + }); + + const callbackRef = useCallback( + (node: HTMLElement | null) => { + if (!node) { + return; + } + + node.addEventListener('keyup', keyup); + node.addEventListener('keydown', keydown); + node.addEventListener('focus', onFocus); + }, + [keyup, keydown, onFocus], + ); + + if (!popup) { + return { + callbackRef, + focused: undefined, + items: undefined, + ariaActiveDescendant: undefined, + popup: undefined, + select: undefined, + }; + } + + return { + focused, + items, + ariaActiveDescendant, + popup, + select, + + callbackRef, + }; +}; diff --git a/apps/meteor/app/ui-message/client/popup/hooks/useComposerBoxPopupQueries.ts b/apps/meteor/app/ui-message/client/popup/hooks/useComposerBoxPopupQueries.ts new file mode 100644 index 0000000000000..7931c9400250e --- /dev/null +++ b/apps/meteor/app/ui-message/client/popup/hooks/useComposerBoxPopupQueries.ts @@ -0,0 +1,37 @@ +import type { QueriesResults } from '@tanstack/react-query'; +import { useQueries } from '@tanstack/react-query'; +import { useEffect, useState } from 'react'; + +import type { ComposerBoxPopupOptions } from './useComposerBoxPopup'; + +export const useComposerBoxPopupQueries = ( + filter: string, + popup?: ComposerBoxPopupOptions, +) => { + const [counter, setCounter] = useState(0); + + useEffect(() => { + setCounter(0); + }, [popup, filter]); + + return useQueries({ + queries: [ + popup?.getItemsFromLocal && { + keepPreviousData: true, + queryKey: ['message-popup', 'local', filter, popup], + queryFn: () => popup.getItemsFromLocal(filter), + onSuccess: (args: T[]) => { + if (args.length < 5) { + setCounter(1); + } + }, + }, + popup?.getItemsFromServer && { + keepPreviousData: true, + queryKey: ['message-popup', 'server', filter, popup], + queryFn: () => popup.getItemsFromServer(filter), + enabled: counter > 0, + }, + ].filter(Boolean) as any, + }) as QueriesResults; +}; diff --git a/apps/meteor/app/ui-message/client/popup/messagePopup.html b/apps/meteor/app/ui-message/client/popup/messagePopup.html deleted file mode 100644 index daaf069824512..0000000000000 --- a/apps/meteor/app/ui-message/client/popup/messagePopup.html +++ /dev/null @@ -1,38 +0,0 @@ - diff --git a/apps/meteor/app/ui-message/client/popup/messagePopup.js b/apps/meteor/app/ui-message/client/popup/messagePopup.js deleted file mode 100644 index 0db3e314645af..0000000000000 --- a/apps/meteor/app/ui-message/client/popup/messagePopup.js +++ /dev/null @@ -1,326 +0,0 @@ -// This is not supposed to be a complete list -// it is just to improve readability in this file - -import _ from 'underscore'; -import { Meteor } from 'meteor/meteor'; -import { ReactiveVar } from 'meteor/reactive-var'; -import { Template } from 'meteor/templating'; - -import './messagePopup.html'; - -const keys = { - TAB: 9, - ENTER: 13, - ESC: 27, - ARROW_LEFT: 37, - ARROW_UP: 38, - ARROW_RIGHT: 39, - ARROW_DOWN: 40, -}; - -function getCursorPosition(input) { - if (input == null) { - return; - } - if (input.selectionStart != null) { - return input.selectionStart; - } - if (document.selection != null) { - input.focus(); - const sel = document.selection.createRange(); - const selLen = document.selection.createRange().text.length; - sel.moveStart('character', -input.value.length); - return sel.text.length - selLen; - } -} - -function setCursorPosition(input, caretPos) { - if (input == null) { - return; - } - if (input.selectionStart != null) { - input.focus(); - return input.setSelectionRange(caretPos, caretPos); - } - if (document.selection != null) { - const range = input.createTextRange(); - range.move('character', caretPos); - return range.select(); - } -} - -function val(v, d) { - if (v != null) { - return v; - } - return d; -} - -Template.messagePopup.onCreated(function () { - const template = this; - template.textFilter = new ReactiveVar(''); - template.textFilterDelay = val(template.data.textFilterDelay, 0); - template.open = val(template.data.open, new ReactiveVar(false)); - template.hasData = new ReactiveVar(false); - template.value = new ReactiveVar(); - template.trigger = val(template.data.trigger, ''); - template.triggerAnywhere = val(template.data.triggerAnywhere, true); - template.closeOnEsc = val(template.data.closeOnEsc, true); - template.blurOnSelectItem = val(template.data.blurOnSelectItem, false); - template.prefix = val(template.data.prefix, template.trigger); - template.suffix = val(template.data.suffix, ''); - if (template.triggerAnywhere === true) { - template.matchSelectorRegex = val(template.data.matchSelectorRegex, new RegExp(`(?:^| |\n)${template.trigger}[^\\s]*$`)); - } else { - template.matchSelectorRegex = val(template.data.matchSelectorRegex, new RegExp(`(?:^)${template.trigger}[^\\s]*$`)); - } - template.selectorRegex = val(template.data.selectorRegex, new RegExp(`${template.trigger}([^\\s]*)$`)); - template.replaceRegex = val(template.data.replaceRegex, new RegExp(`${template.trigger}[^\\s]*$`)); - template.getValue = val(template.data.getValue, function (_id) { - return _id; - }); - template.up = () => { - const current = template.find('.popup-item.selected'); - const previous = $(current).prev('.popup-item')[0] || template.find('.popup-item:last-child'); - if (previous != null) { - current.className = current.className.replace(/\sselected/, '').replace('sidebar-item__popup-active', ''); - previous.className += ' selected sidebar-item__popup-active'; - previous.scrollIntoView(false); - return template.value.set(previous.getAttribute('data-id')); - } - }; - template.down = () => { - const current = template.find('.popup-item.selected'); - const next = $(current).next('.popup-item')[0] || template.find('.popup-item'); - if (next && next.classList.contains('popup-item')) { - current.className = current.className.replace(/\sselected/, '').replace('sidebar-item__popup-active', ''); - next.className += ' selected sidebar-item__popup-active'; - next.scrollIntoView(false); - return template.value.set(next.getAttribute('data-id')); - } - }; - template.verifySelection = () => { - if (!template.open.curValue) { - return; - } - const current = template.find('.popup-item.selected'); - if (current == null) { - const first = template.find('.popup-item'); - if (first != null) { - first.className += ' selected sidebar-item__popup-active'; - return template.value.set(first.getAttribute('data-id')); - } - return template.value.set(null); - } - }; - template.onInputKeydown = (event) => { - if (template.open.curValue !== true || template.hasData.curValue !== true) { - return; - } - if (event.which === keys.ENTER || event.which === keys.TAB) { - if (template.blurOnSelectItem === true) { - template.input.blur(); - } else { - template.open.set(false); - } - template.enterValue(); - if (template.data.cleanOnEnter) { - template.input.value = ''; - } - event.preventDefault(); - event.stopPropagation(); - return; - } - if (event.which === keys.ARROW_UP) { - template.up(); - event.preventDefault(); - event.stopPropagation(); - return; - } - if (event.which === keys.ARROW_DOWN) { - template.down(); - event.preventDefault(); - event.stopPropagation(); - } - }; - - template.setTextFilter = _.debounce(function (value) { - return template.textFilter.set(value); - }, template.textFilterDelay); - - template.onInputKeyup = (event) => { - if (template.closeOnEsc === true && template.open.curValue === true && event.which === keys.ESC) { - template.open.set(false); - event.preventDefault(); - event.stopPropagation(); - return; - } - const value = template.input.value.substr(0, getCursorPosition(template.input)); - - if (template.matchSelectorRegex.test(value)) { - template.setTextFilter(value.match(template.selectorRegex)[1]); - template.open.set(true); - } else { - template.open.set(false); - } - if (template.open.curValue !== true) { - return; - } - if (event.which !== keys.ARROW_UP && event.which !== keys.ARROW_DOWN) { - return Meteor.defer(function () { - template.verifySelection(); - }); - } - }; - template.onFocus = () => { - template.clickingItem = false; - if (template.open.curValue === true) { - return; - } - const value = template.input.value.substr(0, getCursorPosition(template.input)); - if (template.matchSelectorRegex.test(value)) { - template.setTextFilter(value.match(template.selectorRegex)[1]); - template.open.set(true); - return Meteor.defer(function () { - return template.verifySelection(); - }); - } - return template.open.set(false); - }; - - template.onBlur = () => { - if (template.open.curValue === false) { - return; - } - if (template.clickingItem === true) { - return; - } - return template.open.set(false); - }; - - template.enterValue = function () { - if (template.value.curValue == null) { - return; - } - const { value } = template.input; - const caret = getCursorPosition(template.input); - let firstPartValue = value.substr(0, caret); - const lastPartValue = value.substr(caret); - const getValue = this.getValue(template.value.curValue, template.data.collection, template.records.get(), firstPartValue); - if (!getValue) { - return; - } - firstPartValue = firstPartValue.replace(template.selectorRegex, template.prefix + getValue + template.suffix); - template.input.value = firstPartValue + lastPartValue; - return setCursorPosition(template.input, firstPartValue.length); - }; - template.records = new ReactiveVar([]); - template.autorun(function () { - const filter = template.textFilter.get(); - if (filter != null) { - const filterCallback = (result) => { - template.hasData.set(result && result.length > 0); - template.records.set(result); - return Meteor.defer(function () { - return template.verifySelection(); - }); - }; - const result = template.data.getFilter(template.data.collection, filter, filterCallback); - if (result != null) { - return filterCallback(result); - } - } - }); -}); - -Template.messagePopup.onRendered(function () { - if (this.data.getInput != null) { - this.input = typeof this.data.getInput === 'function' && this.data.getInput(); - } else if (this.data.input) { - this.input = this.parentTemplate().find(this.data.input); - } - if (this.input == null) { - console.error('Input not found for popup'); - } - const self = this; - self.autorun(() => { - const open = self.open.get(); - if ($('.reply-preview').length) { - if (open === true) { - $('.reply-preview').addClass('reply-preview-with-popup'); - setTimeout(() => { - $('#popup').addClass('popup-with-reply-preview'); - }, 50); - } - } - if (open === false) { - $('.reply-preview').removeClass('reply-preview-with-popup'); - $('#popup').removeClass('popup-with-reply-preview'); - } - }); - $(this.input).on('keyup', this.onInputKeyup.bind(this)); - $(this.input).on('keydown', this.onInputKeydown.bind(this)); - $(this.input).on('focus', this.onFocus.bind(this)); - $(this.input).on('blur', this.onBlur.bind(this)); -}); - -Template.messagePopup.onDestroyed(function () { - $(this.input).off('keyup', this.onInputKeyup); - $(this.input).off('keydown', this.onInputKeydown); - $(this.input).off('focus', this.onFocus); - $(this.input).off('blur', this.onBlur); -}); - -Template.messagePopup.events({ - 'mouseenter .popup-item'(e) { - if (e.currentTarget.className.indexOf('selected') > -1) { - return; - } - const template = Template.instance(); - const current = template.find('.popup-item.selected'); - if (current != null) { - current.className = current.className.replace(/\sselected/, '').replace('sidebar-item__popup-active', ''); - } - e.currentTarget.className += ' selected sidebar-item__popup-active'; - return template.value.set(this._id); - }, - 'mousedown .popup-item, touchstart .popup-item'() { - const template = Template.instance(); - template.clickingItem = true; - }, - 'mouseup .popup-item, touchend .popup-item'(e) { - e.stopPropagation(); - const template = Template.instance(); - const wasMenuIconClicked = e.target.classList.contains('sidebar-item__menu-icon'); - template.clickingItem = false; - if (!wasMenuIconClicked) { - template.value.set(this._id); - template.enterValue(); - template.open.set(false); - } - }, -}); - -Template.messagePopup.helpers({ - isOpen() { - return ( - Template.instance().open.get() && - (Template.instance().hasData.get() || - Template.instance().data.emptyTemplate != null || - !Template.instance().parentTemplate(1).subscriptionsReady()) - ); - }, - data() { - const template = Template.instance(); - return Object.assign(template.records.get(), { toolbar: true }); - }, - toolbarData() { - return { ...Template.currentData(), toolbar: true }; - }, - sidebarHeaderHeight() { - return `${document.querySelector('.sidebar__header').offsetHeight}px`; - }, - sidebarWidth() { - return `${document.querySelector('.sidebar').offsetWidth}px`; - }, -}); diff --git a/apps/meteor/app/ui-message/client/popup/messagePopupChannel.html b/apps/meteor/app/ui-message/client/popup/messagePopupChannel.html deleted file mode 100644 index 52ff783395096..0000000000000 --- a/apps/meteor/app/ui-message/client/popup/messagePopupChannel.html +++ /dev/null @@ -1,4 +0,0 @@ - diff --git a/apps/meteor/app/ui-message/client/popup/messagePopupChannel.js b/apps/meteor/app/ui-message/client/popup/messagePopupChannel.js deleted file mode 100644 index a37434b4fefef..0000000000000 --- a/apps/meteor/app/ui-message/client/popup/messagePopupChannel.js +++ /dev/null @@ -1,10 +0,0 @@ -import { Template } from 'meteor/templating'; - -import './messagePopupChannel.html'; -import { roomCoordinator } from '../../../../client/lib/rooms/roomCoordinator'; - -Template.messagePopupChannel.helpers({ - channelIcon() { - return roomCoordinator.getIcon(this); - }, -}); diff --git a/apps/meteor/app/ui-message/client/popup/messagePopupConfig.html b/apps/meteor/app/ui-message/client/popup/messagePopupConfig.html deleted file mode 100644 index 4298e8a7e9be0..0000000000000 --- a/apps/meteor/app/ui-message/client/popup/messagePopupConfig.html +++ /dev/null @@ -1,13 +0,0 @@ - diff --git a/apps/meteor/app/ui-message/client/popup/messagePopupConfig.js b/apps/meteor/app/ui-message/client/popup/messagePopupConfig.js index 24913306b6183..5d19c0d87964e 100644 --- a/apps/meteor/app/ui-message/client/popup/messagePopupConfig.js +++ b/apps/meteor/app/ui-message/client/popup/messagePopupConfig.js @@ -1,31 +1,19 @@ -import _ from 'underscore'; -import { Blaze } from 'meteor/blaze'; import { Meteor } from 'meteor/meteor'; import { Mongo } from 'meteor/mongo'; import { Tracker } from 'meteor/tracker'; import { Session } from 'meteor/session'; -import { Template } from 'meteor/templating'; -import { TAPi18n } from 'meteor/rocketchat:tap-i18n'; -import { escapeRegExp } from '@rocket.chat/string-helpers'; -import { Messages, Subscriptions } from '../../../models/client'; -import { settings } from '../../../settings/client'; -import { hasAllPermission, hasAtLeastOnePermission } from '../../../authorization/client'; -import { EmojiPicker, emoji } from '../../../emoji'; -import { callWithErrorHandling } from '../../../../client/lib/utils/callWithErrorHandling'; -import { t, getUserPreference, slashCommands } from '../../../utils/client'; -import { customMessagePopups } from './customMessagePopups'; -import './messagePopupConfig.html'; -import './messagePopupSlashCommand.html'; -import './messagePopupUser.html'; +import { Messages } from '../../../models/client'; -const reloadUsersFromRoomMessages = (rid, template) => { +export const usersFromRoomMessages = new Mongo.Collection(null); +Tracker.autorun(() => { + const rid = Session.get('openedRoom'); const user = Meteor.userId() && Meteor.users.findOne(Meteor.userId(), { fields: { username: 1 } }); if (!rid || !user) { return; } - template.usersFromRoomMessages.remove({}); + usersFromRoomMessages.remove({}); const uniqueMessageUsersControl = {}; @@ -51,436 +39,11 @@ const reloadUsersFromRoomMessages = (rid, template) => { return notMapped; }) .forEach(({ u: { username, name }, ts }) => - template.usersFromRoomMessages.upsert(username, { + usersFromRoomMessages.upsert(username, { _id: username, username, name, - status: Tracker.nonreactive(() => Session.get(`user_${username}_status`) || 'offline'), ts, }), ); -}; - -const fetchUsersFromServer = _.throttle(async (filterText, records, rid, cb) => { - const usernames = records.map(({ username }) => username); - - const { users } = await callWithErrorHandling('spotlight', filterText, usernames, { users: true, mentions: true }, rid); - - if (!users || users.length <= 0) { - return; - } - - users - // .slice(0, 5) - .forEach(({ username, nickname, name, status, avatarETag, outside }) => { - // if (records.length < 5) { - records.push({ - _id: username, - username, - nickname, - name, - status, - avatarETag, - outside, - sort: 3, - }); - // } - }); - - records.sort(({ sort: sortA }, { sort: sortB }) => sortA - sortB); - - cb && cb(records); -}, 1000); - -const fetchRoomsFromServer = _.throttle(async (filterText, records, rid, cb) => { - if (!hasAllPermission('view-outside-room')) { - cb && cb([]); - return; - } - - const { rooms } = await callWithErrorHandling('spotlight', filterText, null, { rooms: true, mentions: true }, rid); - - if (!rooms || rooms.length <= 0) { - return; - } - - rooms.slice(0, 5).forEach((room) => { - if (records.length < 5) { - records.push(room); - } - }); - - cb && cb(records); -}, 1000); - -const emojiSort = (recents) => (a, b) => { - let idA = a._id; - let idB = a._id; - - if (recents.includes(a._id)) { - idA = recents.indexOf(a._id) + idA; - } - if (recents.includes(b._id)) { - idB = recents.indexOf(b._id) + idB; - } - - if (idA < idB) { - return -1; - } - - if (idA > idB) { - return 1; - } - - return 0; -}; -const exactFinalTone = new RegExp('^tone[1-5]:*$'); -const colorBlind = new RegExp('tone[1-5]:*$'); -const seeColor = new RegExp('_t(?:o|$)(?:n|$)(?:e|$)(?:[1-5]|$)(?::|$)$'); -const getEmojis = (collection, filter) => { - const key = `:${filter}`; - - if (!getUserPreference(Meteor.userId(), 'useEmojis')) { - return []; - } - - if (!emoji.packages.emojione || emoji.packages.emojione.asciiList[key]) { - return []; - } - - const regExp = new RegExp(escapeRegExp(filter), 'i'); - const recents = EmojiPicker.getRecent().map((item) => `:${item}:`); - - return Object.keys(collection) - .map((_id) => { - const data = collection[key]; - return { _id, data }; - }) - .filter( - ({ _id }) => regExp.test(_id) && (exactFinalTone.test(_id.substring(key.length)) || seeColor.test(key) || !colorBlind.test(_id)), - ) - .sort(emojiSort(recents)) - .slice(0, 10); -}; - -const addEmojiToRecents = (emoji) => { - const pickerEl = document.querySelector('.emoji-picker'); - if (!pickerEl) { - return; - } - - const view = Blaze.getView(pickerEl); - if (!view) { - return; - } - - Template._withTemplateInstanceFunc(view.templateInstance, () => { - EmojiPicker.addRecent(emoji.replace(/:/g, '')); - }); -}; - -Template.messagePopupConfig.onCreated(function () { - this.usersFromRoomMessages = new Mongo.Collection(null); - - this.autorun(() => { - const { rid } = this.data; - reloadUsersFromRoomMessages(rid, this); - }); -}); - -Template.messagePopupConfig.helpers({ - customMessagePopups() { - return customMessagePopups.get(); - }, - - getCustomConfig() { - const template = Template.instance(); - return this.configGetter(template); - }, - - popupUserConfig() { - const template = Template.instance(); - const suggestionsCount = settings.get('Number_of_users_autocomplete_suggestions'); - - return { - title: t('People'), - collection: template.usersFromRoomMessages, - template: 'messagePopupUser', - rid: this.rid, - getInput: this.getInput, - textFilterDelay: 500, - trigger: '@', - suffix: ' ', - getFilter: (collection, filter = '', cb) => { - const { rid } = this; - const filterText = filter.trim(); - const filterRegex = filterText !== '' && new RegExp(`${escapeRegExp(filterText)}`, 'i'); - - const items = template.usersFromRoomMessages - .find( - { - ts: { $exists: true }, - ...(filterText && { - $or: [{ username: filterRegex }, { name: filterRegex }], - }), - }, - { - limit: filterText ? 2 : suggestionsCount, - sort: { ts: -1 }, - }, - ) - .fetch() - .map((u) => { - u.suggestion = true; - return u; - }); - - // // If needed, add to list the online users - // if (items.length < 5 && filterRegex) { - // const usernamesAlreadyFetched = items.map(({ username }) => username); - // if (!hasAllPermission('view-outside-room')) { - // const usernamesFromDMs = Subscriptions - // .find( - // { - // t: 'd', - // $and: [ - // { - // ...filterRegex && { - // $or: [ - // { name: filterRegex }, - // { fname: filterRegex }, - // ], - // }, - // }, - // { - // name: { $nin: usernamesAlreadyFetched }, - // }, - // ], - // }, - // { - // fields: { name: 1 }, - // }, - // ) - // .map(({ name }) => name); - // const newItems = Users - // .find( - // { - // username: { - // $in: usernamesFromDMs, - // }, - // }, - // { - // fields: { - // username: 1, - // nickname: 1, - // name: 1, - // status: 1, - // }, - // limit: 5 - usernamesAlreadyFetched.length, - // }, - // ) - // .fetch() - // .map(({ username, name, status, nickname }) => ({ - // _id: username, - // username, - // nickname, - // name, - // status, - // sort: 1, - // })); - - // items.push(...newItems); - // } else { - // const user = Meteor.users.findOne(Meteor.userId(), { fields: { username: 1 } }); - // const newItems = Meteor.users.find({ - // $and: [ - // { - // ...filterRegex && { - // $or: [ - // { username: filterRegex }, - // { name: filterRegex }, - // ], - // }, - // }, - // { - // username: { - // $nin: [ - // user && user.username, - // ...usernamesAlreadyFetched, - // ], - // }, - // }, - // ], - // }, - // { - // fields: { - // username: 1, - // nickname: 1, - // name: 1, - // status: 1, - // }, - // limit: 5 - usernamesAlreadyFetched.length, - // }) - // .fetch() - // .map(({ username, name, status, nickname }) => ({ - // _id: username, - // username, - // nickname, - // name, - // status, - // sort: 1, - // })); - - // items.push(...newItems); - // } - // } - - // Get users from Server - if (items.length < suggestionsCount && filterText !== '') { - fetchUsersFromServer(filterText, items, rid, cb); - } - - if (!filterRegex || filterRegex.test('all')) { - items.push({ - _id: 'all', - username: 'all', - system: true, - name: t('Notify_all_in_this_room'), - sort: 4, - }); - } - - if (!filterRegex || filterRegex.test('here')) { - items.push({ - _id: 'here', - username: 'here', - system: true, - name: t('Notify_active_in_this_room'), - sort: 4, - }); - } - - return items; - }, - getValue: (_id) => _id, - }; - }, - popupChannelConfig() { - return { - title: t('Channels'), - collection: Subscriptions, - trigger: '#', - suffix: ' ', - textFilterDelay: 500, - template: 'messagePopupChannel', - rid: this.rid, - getInput: this.getInput, - getFilter: (collection, filter, cb) => { - const { rid } = this; - const exp = new RegExp(filter, 'i'); - const records = collection - .find( - { - name: exp, - t: { - $in: ['c', 'p'], - }, - }, - { - reactive: 1, - limit: 5, - sort: { - ls: -1, - }, - }, - ) - .fetch(); - - if (records.length < 5 && filter && filter.trim() !== '') { - fetchRoomsFromServer(filter, records, rid, cb); - } - return records; - }, - getValue: (_id, collection, records) => { - const record = _.findWhere(records, { - _id, - }); - return record && record.name; - }, - }; - }, - popupSlashCommandsConfig() { - return { - title: t('Commands'), - collection: slashCommands.commands, - trigger: '/', - suffix: ' ', - triggerAnywhere: false, - template: 'messagePopupSlashCommand', - rid: this.rid, - getInput: this.getInput, - getFilter: (collection, filter) => { - const { rid } = this; - return Object.keys(collection) - .map((command) => { - const item = collection[command]; - return { - _id: command, - params: item.params ? TAPi18n.__(item.params) : '', - description: TAPi18n.__(item.description), - permission: item.permission, - }; - }) - .filter((command) => { - const isMatch = command._id.indexOf(filter) > -1; - - if (!isMatch) { - return false; - } - - if (!command.permission) { - return true; - } - - return hasAtLeastOnePermission(command.permission, rid); - }) - .sort((a, b) => a._id > b._id) - .slice(0, 11); - }, - }; - }, - popupEmojiConfig() { - return { - title: t('Emoji'), - collection: emoji.list, - template: 'messagePopupEmoji', - trigger: ':', - prefix: '', - suffix: ' ', - rid: this.rid, - getInput: this.getInput, - getFilter: getEmojis, - getValue: (emojiText) => { - addEmojiToRecents(emojiText); - return emojiText; - }, - }; - }, - popupReactionEmojiConfig() { - return { - title: t('Emoji'), - collection: emoji.list, - template: 'messagePopupEmoji', - trigger: '\\+:', - prefix: '+', - suffix: ' ', - rid: this.rid, - getInput: this.getInput, - getFilter: getEmojis, - getValue: (emojiText) => { - addEmojiToRecents(emojiText); - return emojiText; - }, - }; - }, }); diff --git a/apps/meteor/app/ui-message/client/popup/messagePopupEmoji.html b/apps/meteor/app/ui-message/client/popup/messagePopupEmoji.html deleted file mode 100644 index c064ff72cbf81..0000000000000 --- a/apps/meteor/app/ui-message/client/popup/messagePopupEmoji.html +++ /dev/null @@ -1,4 +0,0 @@ - diff --git a/apps/meteor/app/ui-message/client/popup/messagePopupEmoji.js b/apps/meteor/app/ui-message/client/popup/messagePopupEmoji.js deleted file mode 100644 index 73f8b547b4d32..0000000000000 --- a/apps/meteor/app/ui-message/client/popup/messagePopupEmoji.js +++ /dev/null @@ -1,9 +0,0 @@ -import { Template } from 'meteor/templating'; -import './messagePopupEmoji.html'; - -Template.messagePopupEmoji.helpers({ - value() { - const { length } = this.data; - return this.data[length - 1]; - }, -}); diff --git a/apps/meteor/app/ui-message/client/popup/messagePopupSlashCommand.html b/apps/meteor/app/ui-message/client/popup/messagePopupSlashCommand.html deleted file mode 100644 index f23ab768ea617..0000000000000 --- a/apps/meteor/app/ui-message/client/popup/messagePopupSlashCommand.html +++ /dev/null @@ -1,6 +0,0 @@ - diff --git a/apps/meteor/app/ui-message/client/popup/messagePopupSlashCommandPreview.js b/apps/meteor/app/ui-message/client/popup/messagePopupSlashCommandPreview.js index 6818eaa09cc40..846a9d133b37e 100644 --- a/apps/meteor/app/ui-message/client/popup/messagePopupSlashCommandPreview.js +++ b/apps/meteor/app/ui-message/client/popup/messagePopupSlashCommandPreview.js @@ -264,10 +264,10 @@ Template.messagePopupSlashCommandPreview.onRendered(function _messagePopupSlashC } this.inputBox = this.data.getInput(); - $(this.inputBox).on('keyup', this.onInputKeyup.bind(this)); - $(this.inputBox).on('keydown', this.onInputKeydown.bind(this)); - $(this.inputBox).on('focus', this.onFocus.bind(this)); - $(this.inputBox).on('blur', this.onBlur.bind(this)); + $(this.inputBox).on('keyup', this.onInputKeyup); + $(this.inputBox).on('keydown', this.onInputKeydown); + $(this.inputBox).on('focus', this.onFocus); + $(this.inputBox).on('blur', this.onBlur); const self = this; self.autorun(() => { diff --git a/apps/meteor/app/ui-message/client/popup/messagePopupUser.html b/apps/meteor/app/ui-message/client/popup/messagePopupUser.html deleted file mode 100644 index d6d8b22c23462..0000000000000 --- a/apps/meteor/app/ui-message/client/popup/messagePopupUser.html +++ /dev/null @@ -1,21 +0,0 @@ - diff --git a/apps/meteor/client/components/RoomIcon/RoomIcon.tsx b/apps/meteor/client/components/RoomIcon/RoomIcon.tsx index 754826bed4c17..b9b4a8008ff93 100644 --- a/apps/meteor/client/components/RoomIcon/RoomIcon.tsx +++ b/apps/meteor/client/components/RoomIcon/RoomIcon.tsx @@ -1,4 +1,4 @@ -import type { IRoom } from '@rocket.chat/core-typings'; +import type { IOmnichannelRoom, IRoom } from '@rocket.chat/core-typings'; import { isOmnichannelRoom } from '@rocket.chat/core-typings'; import { Icon } from '@rocket.chat/fuselage'; import type { ComponentProps, ReactElement } from 'react'; @@ -13,7 +13,7 @@ export const RoomIcon = ({ isIncomingCall, placement = 'default', }: { - room: IRoom; + room: Pick; size?: ComponentProps['size']; isIncomingCall?: boolean; placement?: 'sidebar' | 'default'; @@ -24,8 +24,8 @@ export const RoomIcon = ({ return ; } - if (isOmnichannelRoom(room)) { - return ; + if (isOmnichannelRoom(room as IRoom)) { + return ; } if (isValidElement(iconPropsOrReactNode)) { diff --git a/apps/meteor/client/components/UserAutoCompleteMultiple/UserAutoCompleteMultipleOption.tsx b/apps/meteor/client/components/UserAutoCompleteMultiple/UserAutoCompleteMultipleOption.tsx index afe5ed209a84a..e6762c188abde 100644 --- a/apps/meteor/client/components/UserAutoCompleteMultiple/UserAutoCompleteMultipleOption.tsx +++ b/apps/meteor/client/components/UserAutoCompleteMultiple/UserAutoCompleteMultipleOption.tsx @@ -22,10 +22,13 @@ const UserAutoCompleteMultipleOption = ({ label, ...props }: UserAutoCompleteMul icon={_federated ? 'globe' : undefined} key={username} label={ - <> - {name || username} {!_federated && ({username})} - + ( + <> + {name || username} {!_federated && ({username})} + + ) as any } + children={undefined} /> ); }; diff --git a/apps/meteor/client/components/message/toolbox/DesktopToolboxDropdown.tsx b/apps/meteor/client/components/message/toolbox/DesktopToolboxDropdown.tsx index 3d19f352bb288..c7a18f1900dac 100644 --- a/apps/meteor/client/components/message/toolbox/DesktopToolboxDropdown.tsx +++ b/apps/meteor/client/components/message/toolbox/DesktopToolboxDropdown.tsx @@ -27,8 +27,8 @@ const useDropdownPosition = (reference: RefObject, target: RefObjec container: innerContainer, }); - const left = `${parseFloat(style.left) - boundingRect.left}px`; - const top = `${parseFloat(style.top) - boundingRect.top}px`; + const left = `${parseFloat(String(style?.left ?? '0')) - boundingRect.left}px`; + const top = `${parseFloat(String(style?.top ?? '0')) - boundingRect.top}px`; return useMemo(() => ({ ...style, left, top }), [style, left, top]); }; diff --git a/apps/meteor/client/lib/chats/ChatAPI.ts b/apps/meteor/client/lib/chats/ChatAPI.ts index 20caee431a286..a96b81c44ffc9 100644 --- a/apps/meteor/client/lib/chats/ChatAPI.ts +++ b/apps/meteor/client/lib/chats/ChatAPI.ts @@ -22,6 +22,19 @@ export type ComposerAPI = { insertNewLine(): void; clear(): void; focus(): void; + blur(): void; + + getCursorPosition(): number | undefined; + + substring(start: number, end?: number): string; + + replaceText( + text: string, + selection: { + start: number; + end: number; + }, + ): void; setCursorToEnd(): void; setCursorToStart(): void; diff --git a/apps/meteor/client/views/room/components/body/composer/hooks/useComposeChainEvents.ts b/apps/meteor/client/views/room/components/body/composer/hooks/useComposeChainEvents.ts deleted file mode 100644 index 893009bb6e136..0000000000000 --- a/apps/meteor/client/views/room/components/body/composer/hooks/useComposeChainEvents.ts +++ /dev/null @@ -1,24 +0,0 @@ -/** - * Hook to merge events from multiple hooks - * if returns false, continue with other hooks - * if returns true, stop the chain - * - */ - -import { useCallback } from 'react'; - -export const useComposeChainEvents = (events: Array<(e: E) => boolean>): ((e: E) => boolean) => { - const handler = useCallback( - (event: E) => { - for (const e of events) { - if (e(event)) { - return true; - } - } - return false; - }, - [events], - ); - - return handler; -}; diff --git a/apps/meteor/client/views/room/components/body/composer/messageBox/MessageBox.tsx b/apps/meteor/client/views/room/components/body/composer/messageBox/MessageBox.tsx index b37c2e2bf5fbd..d4d300b990b64 100644 --- a/apps/meteor/client/views/room/components/body/composer/messageBox/MessageBox.tsx +++ b/apps/meteor/client/views/room/components/body/composer/messageBox/MessageBox.tsx @@ -1,6 +1,7 @@ +/* eslint-disable complexity */ import type { IMessage, IRoom, ISubscription } from '@rocket.chat/core-typings'; import { Button, Tag, Box } from '@rocket.chat/fuselage'; -import { useContentBoxSize, useMutableCallback } from '@rocket.chat/fuselage-hooks'; +import { useContentBoxSize, useMergedRefs, useMutableCallback } from '@rocket.chat/fuselage-hooks'; import { MessageComposerAction, MessageComposerToolbarActions, @@ -29,6 +30,8 @@ import { EmojiPicker } from '../../../../../../../app/emoji/client'; import { createComposerAPI } from '../../../../../../../app/ui-message/client/messageBox/createComposerAPI'; import type { FormattingButton } from '../../../../../../../app/ui-message/client/messageBox/messageBoxFormatting'; import { formattingButtons } from '../../../../../../../app/ui-message/client/messageBox/messageBoxFormatting'; +import { ComposerBoxPopup } from '../../../../../../../app/ui-message/client/popup/ComposerBoxPopup'; +import { useComposerBoxPopup } from '../../../../../../../app/ui-message/client/popup/hooks/useComposerBoxPopup'; import { getImageExtensionFromMime } from '../../../../../../../lib/getImageExtensionFromMime'; import { useFormatDateAndTime } from '../../../../../../hooks/useFormatDateAndTime'; import { useReactiveValue } from '../../../../../../hooks/useReactiveValue'; @@ -38,7 +41,8 @@ import { keyCodes } from '../../../../../../lib/utils/keyCodes'; import AudioMessageRecorder from '../../../../../composer/AudioMessageRecorder'; import VideoMessageRecorder from '../../../../../composer/VideoMessageRecorder'; import { useChat } from '../../../../contexts/ChatContext'; -import BlazeTemplate from '../../../BlazeTemplate'; +// import BlazeTemplate from '../../../BlazeTemplate'; +import { useComposerPopup } from '../../../../contexts/ComposerPopupContext'; import ComposerUserActionIndicator from '../ComposerUserActionIndicator'; import { useAutoGrow } from '../RoomComposer/hooks/useAutoGrow'; import MessageBoxDropdown from './MessageBoxDropdown'; @@ -323,21 +327,40 @@ const MessageBox = ({ } }); + const composerPopupConfig = useComposerPopup(); + + const { + popup, + focused, + items, + ariaActiveDescendant, + select, + callbackRef: c, + } = useComposerBoxPopup<{ _id: string; sort?: number }>({ + configurations: composerPopupConfig, + }); + + const mergedRefs = useMergedRefs(c, callbackRef); + return ( <> {chat?.composer?.quotedMessages && } - textareaRef.current} /> - textareaRef.current} /> + + {/* textareaRef.current} /> */} + + {popup && } + {readOnly && ( {t('This_room_is_read_only')} )} + {isRecordingVideo && } {isRecordingAudio && } } + ref={mergedRefs as unknown as Ref} aria-label={t('Message')} name='msg' disabled={isRecording} @@ -347,6 +370,7 @@ const MessageBox = ({ className='rc-message-box__textarea js-input-message' onKeyDown={handler} onPaste={handlePaste} + aria-activedescendant={ariaActiveDescendant} />
diff --git a/apps/meteor/client/views/room/contexts/ComposerPopupContext.ts b/apps/meteor/client/views/room/contexts/ComposerPopupContext.ts new file mode 100644 index 0000000000000..ce53953e108de --- /dev/null +++ b/apps/meteor/client/views/room/contexts/ComposerPopupContext.ts @@ -0,0 +1,50 @@ +import type { ReactElement } from 'react'; +import { useContext, createContext } from 'react'; + +type ComposerPopupItem = T extends { _id: string; sort?: number } ? T : never; + +export type ComposerPopupOption = { + title?: string; + getItemsFromLocal: (filter: string) => Promise; + getItemsFromServer: (filter: string) => Promise; + blurOnSelectItem?: boolean; + closeOnEsc?: boolean; + + trigger?: string; + triggerAnywhere?: boolean; + + suffix?: string; + prefix?: string; + + matchSelectorRegex?: RegExp; + + getValue: (item: ComposerPopupItem) => string; + + renderItem?: ({ item }: { item: T }) => ReactElement; +}; + +export type ComposerPopupContextValue = ComposerPopupOption[]; + +export const ComposerPopupContext = createContext(undefined); + +export const createMessageBoxPopupConfig = ( + partial: Omit, 'getValue'> & Partial, 'getValue'>>, +): ComposerPopupOption => { + return { + blurOnSelectItem: true, + closeOnEsc: true, + triggerAnywhere: true, + suffix: ' ', + prefix: partial.trigger ?? ' ', + getValue: (item) => item._id, + ...partial, + }; +}; + +export const useComposerPopup = () => { + const composerPopupContext = useContext(ComposerPopupContext); + if (!composerPopupContext) { + throw new Error('useComposerPopup must be used within ComposerPopupContext'); + } + return composerPopupContext; +}; diff --git a/apps/meteor/client/views/room/providers/ComposerPopupProvider.tsx b/apps/meteor/client/views/room/providers/ComposerPopupProvider.tsx new file mode 100644 index 0000000000000..467343b01efbe --- /dev/null +++ b/apps/meteor/client/views/room/providers/ComposerPopupProvider.tsx @@ -0,0 +1,337 @@ +import type { IRoom } from '@rocket.chat/core-typings'; +import { isOmnichannelRoom } from '@rocket.chat/core-typings'; +import { escapeRegExp } from '@rocket.chat/string-helpers'; +import { useMethod, useSetting, useTranslation } from '@rocket.chat/ui-contexts'; +import React, { useMemo } from 'react'; +import type { ReactNode } from 'react'; + +import { hasAtLeastOnePermission } from '../../../../app/authorization/client'; +import { emoji, EmojiPicker } from '../../../../app/emoji/client'; +import { Subscriptions } from '../../../../app/models/client'; +import ComposerPopupCannedResponse from '../../../../app/ui-message/client/popup/components/ComposerBoxPopupCannedResponse'; +import type { ComposerBoxPopupEmojiProps } from '../../../../app/ui-message/client/popup/components/ComposerBoxPopupEmoji'; +import ComposerPopupEmoji from '../../../../app/ui-message/client/popup/components/ComposerBoxPopupEmoji'; +import type { ComposerBoxPopupRoomProps } from '../../../../app/ui-message/client/popup/components/ComposerBoxPopupRoom'; +import ComposerBoxPopupRoom from '../../../../app/ui-message/client/popup/components/ComposerBoxPopupRoom'; +import type { ComposerBoxPopupSlashCommandProps } from '../../../../app/ui-message/client/popup/components/ComposerBoxPopupSlashCommand'; +import ComposerPopupSlashCommand from '../../../../app/ui-message/client/popup/components/ComposerBoxPopupSlashCommand'; +import ComposerBoxPopupUser from '../../../../app/ui-message/client/popup/components/ComposerBoxPopupUser'; +import type { ComposerBoxPopupUserProps } from '../../../../app/ui-message/client/popup/components/ComposerBoxPopupUser'; +import { usersFromRoomMessages } from '../../../../app/ui-message/client/popup/messagePopupConfig'; +import { slashCommands } from '../../../../app/utils/client'; +import { CannedResponse } from '../../../../ee/app/canned-responses/client/collections/CannedResponse'; +import type { ComposerPopupContextValue } from '../contexts/ComposerPopupContext'; +import { ComposerPopupContext, createMessageBoxPopupConfig } from '../contexts/ComposerPopupContext'; + +const ComposerPopupProvider = ({ children, room }: { children: ReactNode; room: IRoom }) => { + const { _id: rid } = room; + const userSpotlight = useMethod('spotlight'); + const suggestionsCount = useSetting('Number_of_users_autocomplete_suggestions'); + const cannedResponseEnabled = useSetting('Canned_Responses_Enable'); + const isOmnichannel = isOmnichannelRoom(room); + + const t = useTranslation(); + const value: ComposerPopupContextValue = useMemo(() => { + return [ + createMessageBoxPopupConfig({ + trigger: '@', + title: t('People'), + getItemsFromLocal: async (filter: string) => { + const filterRegex = filter && new RegExp(escapeRegExp(filter), 'i'); + const items: ComposerBoxPopupUserProps[] = []; + + const users = usersFromRoomMessages + .find( + { + ts: { $exists: true }, + ...(filter && { + $or: [{ username: filterRegex }, { name: filterRegex }], + }), + }, + { + limit: suggestionsCount ?? 5, + sort: { ts: -1 }, + }, + ) + .fetch() + .map((u) => { + u.suggestion = true; + return u; + }); + if (!filterRegex || filterRegex.test('all')) { + items.push({ + _id: 'all', + username: 'all', + system: true, + name: t('Notify_all_in_this_room'), + sort: 4, + }); + } + + if (!filterRegex || filterRegex.test('here')) { + items.push({ + _id: 'here', + username: 'here', + system: true, + name: t('Notify_active_in_this_room'), + sort: 4, + }); + } + + return [...users, ...items]; + }, + getItemsFromServer: async (filter: string) => { + const filterRegex = filter && new RegExp(escapeRegExp(filter), 'i'); + const usernames = usersFromRoomMessages + .find( + { + ts: { $exists: true }, + ...(filter && { + $or: [{ username: filterRegex }, { name: filterRegex }], + }), + }, + { + limit: suggestionsCount ?? 5, + sort: { ts: -1 }, + }, + ) + .fetch() + .map((u) => { + return u.username; + }); + const { users = [] } = await userSpotlight(filter, usernames, { users: true, mentions: true }, rid); + + return users.map(({ _id, username, nickname, name, status, avatarETag, outside }) => { + return { + _id, + username, + nickname, + name, + status, + avatarETag, + outside, + sort: 3, + }; + }); + }, + getValue: (item) => item.username, + renderItem: ({ item }) => , + }), + createMessageBoxPopupConfig({ + trigger: '#', + title: t('Channels'), + getItemsFromLocal: async (filter: string) => { + const filterRegex = new RegExp(escapeRegExp(filter), 'i'); + const records = Subscriptions.find( + { + name: filterRegex, + t: { + $in: ['c', 'p'], + }, + }, + { + limit: suggestionsCount ?? 5, + sort: { + ls: -1, + }, + }, + ).fetch(); + return records; + }, + getItemsFromServer: async (filter: string) => { + const { rooms = [] } = await userSpotlight(filter, [], { rooms: true, mentions: true }, rid); + return rooms as unknown as ComposerBoxPopupRoomProps[]; + }, + getValue: (item) => `${item.fname || item.name}`, + renderItem: ({ item }) => , + }) as any, + createMessageBoxPopupConfig({ + trigger: ':', + title: t('Emoji'), + getItemsFromLocal: async (filter: string) => { + const exactFinalTone = new RegExp('^tone[1-5]:*$'); + const colorBlind = new RegExp('tone[1-5]:*$'); + const seeColor = new RegExp('_t(?:o|$)(?:n|$)(?:e|$)(?:[1-5]|$)(?::|$)$'); + + const emojiSort = (recents: string[]) => (a: { _id: string }, b: { _id: string }) => { + let idA = a._id; + let idB = a._id; + + if (recents.includes(a._id)) { + idA = recents.indexOf(a._id) + idA; + } + if (recents.includes(b._id)) { + idB = recents.indexOf(b._id) + idB; + } + + if (idA < idB) { + return -1; + } + + if (idA > idB) { + return 1; + } + + return 0; + }; + const filterRegex = new RegExp(escapeRegExp(filter), 'i'); + const key = `:${filter}`; + + const recents = EmojiPicker.getRecent().map((item) => `:${item}:`); + + const collection = emoji.list; + + return Object.keys(collection) + .map((_id) => { + const data = collection[key]; + return { _id, data }; + }) + .filter( + ({ _id }) => + filterRegex.test(_id) && (exactFinalTone.test(_id.substring(key.length)) || seeColor.test(key) || !colorBlind.test(_id)), + ) + .sort(emojiSort(recents)) + .slice(0, 10); + }, + getItemsFromServer: async () => { + return []; + }, + getValue: (item) => `${item._id.substring(1)}`, + renderItem: ({ item }) => , + }), + createMessageBoxPopupConfig({ + title: t('Emoji'), + trigger: '\\+:', + prefix: '+', + suffix: ' ', + triggerAnywhere: false, + getItemsFromLocal: async (filter: string) => { + const exactFinalTone = new RegExp('^tone[1-5]:*$'); + const colorBlind = new RegExp('tone[1-5]:*$'); + const seeColor = new RegExp('_t(?:o|$)(?:n|$)(?:e|$)(?:[1-5]|$)(?::|$)$'); + + const emojiSort = (recents: string[]) => (a: { _id: string }, b: { _id: string }) => { + let idA = a._id; + let idB = a._id; + + if (recents.includes(a._id)) { + idA = recents.indexOf(a._id) + idA; + } + if (recents.includes(b._id)) { + idB = recents.indexOf(b._id) + idB; + } + + if (idA < idB) { + return -1; + } + + if (idA > idB) { + return 1; + } + + return 0; + }; + const filterRegex = new RegExp(escapeRegExp(filter), 'i'); + const key = `:${filter}`; + + const recents = EmojiPicker.getRecent().map((item) => `:${item}:`); + + const collection = emoji.list; + + return Object.keys(collection) + .map((_id) => { + const data = collection[key]; + return { _id, data }; + }) + .filter( + ({ _id }) => + filterRegex.test(_id) && (exactFinalTone.test(_id.substring(key.length)) || seeColor.test(key) || !colorBlind.test(_id)), + ) + .sort(emojiSort(recents)) + .slice(0, 10); + }, + getItemsFromServer: async () => { + return []; + }, + getValue: (item) => `${item._id}`, + renderItem: ({ item }) => , + }), + + createMessageBoxPopupConfig({ + title: t('Commands'), + trigger: '/', + suffix: ' ', + triggerAnywhere: false, + renderItem: ({ item }) => , + getItemsFromLocal: async (filter: string) => { + return Object.keys(slashCommands.commands) + .map((command) => { + const item = slashCommands.commands[command]; + return { + _id: command, + params: item.params && t.has(item.params) ? t(item.params) : item.params ?? '', + description: t.has(item.description) ? t(item.description) : item.description, + permission: item.permission, + }; + }) + .filter((command) => { + const isMatch = command._id.indexOf(filter) > -1; + + if (!isMatch) { + return false; + } + + if (!command.permission) { + return true; + } + + return hasAtLeastOnePermission(command.permission, rid); + }) + .sort((a, b) => a._id.localeCompare(b._id)) + .slice(0, 11); + }, + getItemsFromServer: async () => [], + }), + cannedResponseEnabled && + isOmnichannel && + createMessageBoxPopupConfig<{ + _id: string; + text: string; + shortcut: string; + }>({ + title: t('Canned_Responses'), + trigger: '!', + triggerAnywhere: true, + renderItem: ({ item }) => , + getItemsFromLocal: async (filter: string) => { + const exp = new RegExp(filter, 'i'); + return CannedResponse.find( + { + shortcut: exp, + }, + { + limit: 12, + sort: { + shortcut: -1, + }, + }, + ) + .fetch() + .map((record) => ({ + _id: record._id, + text: record.text, + shortcut: record.shortcut, + })); + }, + getItemsFromServer: async () => [], + getValue: (item) => { + return item.text; + }, + }), + ].filter(Boolean); + }, [t, cannedResponseEnabled, isOmnichannel, suggestionsCount, userSpotlight, rid]); + + return ; +}; + +export default ComposerPopupProvider; diff --git a/apps/meteor/client/views/room/providers/RoomProvider.tsx b/apps/meteor/client/views/room/providers/RoomProvider.tsx index 0f4de5abae93d..202e513165be2 100644 --- a/apps/meteor/client/views/room/providers/RoomProvider.tsx +++ b/apps/meteor/client/views/room/providers/RoomProvider.tsx @@ -14,6 +14,7 @@ import RoomSkeleton from '../RoomSkeleton'; import { useRoomRolesManagement } from '../components/body/useRoomRolesManagement'; import { RoomAPIContext } from '../contexts/RoomAPIContext'; import { RoomContext } from '../contexts/RoomContext'; +import ComposerPopupProvider from './ComposerPopupProvider'; import ToolboxProvider from './ToolboxProvider'; type RoomProviderProps = { @@ -108,7 +109,9 @@ const RoomProvider = ({ rid, children }: RoomProviderProps): ReactElement => { return ( - {children} + + {children} + ); diff --git a/apps/meteor/definition/externals/meteor/templating.d.ts b/apps/meteor/definition/externals/meteor/templating.d.ts index c919f847d106c..855855ce5f741 100644 --- a/apps/meteor/definition/externals/meteor/templating.d.ts +++ b/apps/meteor/definition/externals/meteor/templating.d.ts @@ -84,23 +84,19 @@ declare module 'meteor/templating' { validate: () => unknown; } >; + error: Blaze.Template>; + loading: Blaze.Template>; + collapseArrow: Blaze.Template>; + rc_modal: Blaze.Template>; + popout: Blaze.Template>; + popover: Blaze.Template>; error: BlazeTemplate; loading: BlazeTemplate; - messagePopup: BlazeTemplate; - messagePopupChannel: BlazeTemplate; - messagePopupConfig: BlazeTemplate<{ - rid: IRoom['_id']; - tmid?: IMessage['_id']; - getInput: () => HTMLTextAreaElement | null; - }>; - messagePopupEmoji: BlazeTemplate; - messagePopupSlashCommand: BlazeTemplate; messagePopupSlashCommandPreview: BlazeTemplate<{ tmid?: IMessage['_id']; rid: IRoom['_id']; getInput: () => HTMLTextAreaElement | null; }>; - messagePopupUser: BlazeTemplate; rc_modal: BlazeTemplate; popout: BlazeTemplate; popover: BlazeTemplate; diff --git a/apps/meteor/ee/app/canned-responses/client/collections/CannedResponse.js b/apps/meteor/ee/app/canned-responses/client/collections/CannedResponse.js deleted file mode 100644 index 61a6add3ec3ba..0000000000000 --- a/apps/meteor/ee/app/canned-responses/client/collections/CannedResponse.js +++ /dev/null @@ -1,3 +0,0 @@ -import { Mongo } from 'meteor/mongo'; - -export const CannedResponse = new Mongo.Collection(null); diff --git a/apps/meteor/ee/app/canned-responses/client/collections/CannedResponse.ts b/apps/meteor/ee/app/canned-responses/client/collections/CannedResponse.ts new file mode 100644 index 0000000000000..6ea1f70e9897e --- /dev/null +++ b/apps/meteor/ee/app/canned-responses/client/collections/CannedResponse.ts @@ -0,0 +1,7 @@ +import { Mongo } from 'meteor/mongo'; + +export const CannedResponse = new Mongo.Collection<{ + _id: string; + shortcut: string; + text: string; +}>(null); diff --git a/apps/meteor/ee/app/canned-responses/client/index.js b/apps/meteor/ee/app/canned-responses/client/index.js index 2de7ff7c62397..d2fadbdb195a3 100644 --- a/apps/meteor/ee/app/canned-responses/client/index.js +++ b/apps/meteor/ee/app/canned-responses/client/index.js @@ -2,5 +2,3 @@ import './tabBar'; import './startup/responses'; import './stylesheets/cannedResponses.css'; import './views/icons.html'; -import './views/popup/addMessagePopupCannedResponse'; -import './views/popup/messagePopupCannedResponse.html'; diff --git a/apps/meteor/ee/app/canned-responses/client/views/popup/addMessagePopupCannedResponse.js b/apps/meteor/ee/app/canned-responses/client/views/popup/addMessagePopupCannedResponse.js deleted file mode 100644 index b002035f7dfaf..0000000000000 --- a/apps/meteor/ee/app/canned-responses/client/views/popup/addMessagePopupCannedResponse.js +++ /dev/null @@ -1,54 +0,0 @@ -import _ from 'underscore'; -import { Tracker } from 'meteor/tracker'; - -import { CannedResponse } from '../../collections/CannedResponse'; -import { addMessagePopup, removeMessagePopup } from '../../../../../../app/ui-message/client/popup/customMessagePopups'; -import { t } from '../../../../../../app/utils'; -import { settings } from '../../../../../../app/settings/client'; - -Tracker.autorun(() => { - const templateFunc = (template) => ({ - title: t('Canned_Responses'), - collection: CannedResponse, - trigger: '!', - prefix: '', - suffix: ' ', - triggerAnywhere: true, - template: 'messagePopupCannedResponse', - rid: template.data.rid, - getInput: template.data.getInput, - textFilterDelay: 500, - getFilter: (collection, filter) => { - const exp = new RegExp(filter, 'i'); - const records = collection - .find( - { - shortcut: exp, - }, - { - reactive: 1, - limit: 12, - sort: { - shortcut: -1, - }, - }, - ) - .fetch(); - - return records; - }, - getValue: (_id, collection, records) => { - const record = _.findWhere(records, { - _id, - }); - return record && record.text; - }, - }); - - const settingValue = settings.get('Canned_Responses_Enable'); - if (settingValue) { - addMessagePopup(templateFunc, 'cannedResponses'); - } else { - removeMessagePopup('cannedResponses'); - } -}); diff --git a/apps/meteor/ee/app/canned-responses/client/views/popup/messagePopupCannedResponse.html b/apps/meteor/ee/app/canned-responses/client/views/popup/messagePopupCannedResponse.html deleted file mode 100644 index 6b9fcd231acf7..0000000000000 --- a/apps/meteor/ee/app/canned-responses/client/views/popup/messagePopupCannedResponse.html +++ /dev/null @@ -1,6 +0,0 @@ - diff --git a/apps/meteor/tests/e2e/message-mentions.spec.ts b/apps/meteor/tests/e2e/message-mentions.spec.ts index 0875229931422..8b83e0535d5dd 100644 --- a/apps/meteor/tests/e2e/message-mentions.spec.ts +++ b/apps/meteor/tests/e2e/message-mentions.spec.ts @@ -16,7 +16,7 @@ test.describe.serial('message-mentions', () => { await poHomeChannel.sidenav.openChat('general'); await poHomeChannel.content.inputMessage.type('@'); - await expect(poHomeChannel.content.messagePopUpItems.locator('strong >> text="all"')).toBeVisible(); - await expect(poHomeChannel.content.messagePopUpItems.locator('strong >> text="here"')).toBeVisible(); + await expect(poHomeChannel.content.messagePopupUsers.locator('role=listitem >> text="all"')).toBeVisible(); + await expect(poHomeChannel.content.messagePopupUsers.locator('role=listitem >> text="here"')).toBeVisible(); }); }); diff --git a/apps/meteor/tests/e2e/page-objects/fragments/home-content.ts b/apps/meteor/tests/e2e/page-objects/fragments/home-content.ts index 15b779e514e6e..8d0dfc290d502 100644 --- a/apps/meteor/tests/e2e/page-objects/fragments/home-content.ts +++ b/apps/meteor/tests/e2e/page-objects/fragments/home-content.ts @@ -13,8 +13,8 @@ export class HomeContent { return this.page.locator('[name="msg"]'); } - get messagePopUpItems(): Locator { - return this.page.locator('.message-popup-items'); + get messagePopupUsers(): Locator { + return this.page.locator('role=menu[name="People"]'); } get lastUserMessage(): Locator { diff --git a/packages/ui-contexts/src/ServerContext/methods.ts b/packages/ui-contexts/src/ServerContext/methods.ts index 4f9c42fda7619..f90f1ecf1b033 100644 --- a/packages/ui-contexts/src/ServerContext/methods.ts +++ b/packages/ui-contexts/src/ServerContext/methods.ts @@ -243,8 +243,9 @@ export interface ServerMethods { | string | string[] | { - users: boolean; - rooms: boolean; + users?: boolean; + rooms?: boolean; + mentions?: boolean; } )[] ) => { @@ -256,6 +257,7 @@ export interface ServerMethods { username: string; outside: boolean; avatarETag?: string; + nickname?: string; }[]; }; 'getPasswordPolicy': (params?: { token: string }) => { diff --git a/yarn.lock b/yarn.lock index 3c767495350da..d6ffac95f1118 100644 --- a/yarn.lock +++ b/yarn.lock @@ -6339,69 +6339,25 @@ __metadata: languageName: unknown linkType: soft -"@rocket.chat/css-in-js@npm:next": - version: 0.31.23-dev.19 - resolution: "@rocket.chat/css-in-js@npm:0.31.23-dev.19" +"@rocket.chat/css-in-js@npm:next, @rocket.chat/css-in-js@npm:~0.31.23-dev.50": + version: 0.31.23-dev.50 + resolution: "@rocket.chat/css-in-js@npm:0.31.23-dev.50" dependencies: "@emotion/hash": ^0.9.0 - "@rocket.chat/css-supports": ~0.31.23-dev.19 - "@rocket.chat/memo": ~0.31.23-dev.19 - "@rocket.chat/stylis-logical-props-middleware": ~0.31.23-dev.19 + "@rocket.chat/css-supports": ~0.31.23-dev.50 + "@rocket.chat/memo": ~0.31.23-dev.50 + "@rocket.chat/stylis-logical-props-middleware": ~0.31.23-dev.50 stylis: ~4.1.3 - checksum: 1487294a3141cf4680eb14b55f80e5ab6bd988c2a8f2b4d9c52e34e72a191e25e32e5a6c3dbc2ecdabe8c3764aac3db02614518018b2a697ecc5cbc41ebff610 + checksum: 87b8093846bf392e9c3a6ba81c60ff63c6b3bbbe5c54e2587f708f28ff311524ca46869bc0d59193e761e89e9656cf4dddbdf3af9d0ab45705289cf4ea0f485f languageName: node linkType: hard -"@rocket.chat/css-in-js@npm:~0.31.23-dev.19, @rocket.chat/css-in-js@npm:~0.31.23-dev.47": - version: 0.31.23-dev.47 - resolution: "@rocket.chat/css-in-js@npm:0.31.23-dev.47" +"@rocket.chat/css-supports@npm:~0.31.23-dev.50": + version: 0.31.23-dev.50 + resolution: "@rocket.chat/css-supports@npm:0.31.23-dev.50" dependencies: - "@emotion/hash": ^0.9.0 - "@rocket.chat/css-supports": ~0.31.23-dev.47 - "@rocket.chat/memo": ~0.31.23-dev.47 - "@rocket.chat/stylis-logical-props-middleware": ~0.31.23-dev.47 - stylis: ~4.1.3 - checksum: f6a6b747d37e0eb1188714959e7fe80b124a504275007f0745d58b01e409cbdbf4a1cc22e191de062634ef6c38018711522ff6c49b4a718e55ef97e880499a95 - languageName: node - linkType: hard - -"@rocket.chat/css-in-js@npm:~0.31.23-dev.46": - version: 0.31.23-dev.46 - resolution: "@rocket.chat/css-in-js@npm:0.31.23-dev.46" - dependencies: - "@emotion/hash": ^0.9.0 - "@rocket.chat/css-supports": ~0.31.23-dev.46 - "@rocket.chat/memo": ~0.31.23-dev.46 - "@rocket.chat/stylis-logical-props-middleware": ~0.31.23-dev.46 - stylis: ~4.1.3 - checksum: 95485e1e2220f19b55abe8d36f71689ab1be1eb97d07fdd09ec327332943ceb46dd182c724ecfd2e811bb67c9175c28ff1eaf7061fe08d1294e51f373e20a0ae - languageName: node - linkType: hard - -"@rocket.chat/css-supports@npm:~0.31.23-dev.19, @rocket.chat/css-supports@npm:~0.31.23-dev.45": - version: 0.31.23-dev.45 - resolution: "@rocket.chat/css-supports@npm:0.31.23-dev.45" - dependencies: - "@rocket.chat/memo": ~0.31.23-dev.19 - checksum: c355919e6479b8d8dd0a524e6258a05f463b50b50f0431dde1206c808d8c6d2a51bb323fb3fd0fa58472782cc3712c6303d3d0eb4c4aedde1bd84f31129430a5 - languageName: node - linkType: hard - -"@rocket.chat/css-supports@npm:~0.31.23-dev.46": - version: 0.31.23-dev.46 - resolution: "@rocket.chat/css-supports@npm:0.31.23-dev.46" - dependencies: - "@rocket.chat/memo": ~0.31.23-dev.46 - checksum: 116532e5f39f09158c444ca8b2c1992a7ad3f37c3660cf146dc81afdb858b3bf25dd2b7c533054ae6b7e0d368c2234ec421649b3624afde4665f01dc583b0aad - languageName: node - linkType: hard - -"@rocket.chat/css-supports@npm:~0.31.23-dev.47": - version: 0.31.23-dev.47 - resolution: "@rocket.chat/css-supports@npm:0.31.23-dev.47" - dependencies: - "@rocket.chat/memo": ~0.31.23-dev.47 - checksum: cc9b409e4c33efb0aa6f46ff8a5c207b6ec0f26d8de8e2b4508549ef64b6fefa0e5fd508bb2040e22c2576711bed67dbb32d9b26c8fc8e6354643a2542023668 + "@rocket.chat/memo": ~0.31.23-dev.50 + checksum: 1dbf337855ead05e3f50c5b9c06637042fee950b0efbedb9234c46c8ea1e7cba3676e59ea7822decea898306adbd47253f6e11cfefaf7419d993e030fa458af4 languageName: node linkType: hard @@ -6449,9 +6405,9 @@ __metadata: linkType: soft "@rocket.chat/emitter@npm:next": - version: 0.31.23-dev.19 - resolution: "@rocket.chat/emitter@npm:0.31.23-dev.19" - checksum: 5adca31f1cca47c213aaaa5675a5a7402289f01f5f73e5665598163d6029d01f38f22e6ce9a6edd0e105f87f501f13035d8fa7ae338d18df7f4224fadbf35184 + version: 0.31.23-dev.50 + resolution: "@rocket.chat/emitter@npm:0.31.23-dev.50" + checksum: ac2531d4b9c7e3f198ff34ecdb244dd7b4f7a83a84a2092ec766bee8c718d20a239aeab8eda594d93441b0b130e8fb0d89a3aec51c1312608b67a9bd052539d0 languageName: node linkType: hard @@ -6542,21 +6498,21 @@ __metadata: languageName: node linkType: hard -"@rocket.chat/fuselage-hooks@npm:next, @rocket.chat/fuselage-hooks@npm:~0.32.0-dev.158": - version: 0.32.0-dev.158 - resolution: "@rocket.chat/fuselage-hooks@npm:0.32.0-dev.158" +"@rocket.chat/fuselage-hooks@npm:next, @rocket.chat/fuselage-hooks@npm:~0.32.0-dev.189": + version: 0.32.0-dev.189 + resolution: "@rocket.chat/fuselage-hooks@npm:0.32.0-dev.189" dependencies: use-sync-external-store: ~1.2.0 peerDependencies: "@rocket.chat/fuselage-tokens": "*" react: ^17.0.2 - checksum: c725f959f46697ecfe077e5b63e1284916454292deb01cc7ee5c09f438245da98d036d411927323f075c84db7bb58a9fc69af61d5bf25eaea8c0bdbf64b762af + checksum: 53a535041c241bfc2ecc795eb3116daae5ba8fe3503a27b47d9401cf6da20b71ae5cc9f6bd37f3de17b118eee5d1b140bef5236efe51d13ada5c48486d4e6b6e languageName: node linkType: hard "@rocket.chat/fuselage-polyfills@npm:next": - version: 0.31.23-dev.19 - resolution: "@rocket.chat/fuselage-polyfills@npm:0.31.23-dev.19" + version: 0.31.23-dev.50 + resolution: "@rocket.chat/fuselage-polyfills@npm:0.31.23-dev.50" dependencies: "@juggle/resize-observer": ^3.4.0 clipboard-polyfill: ^3.0.3 @@ -6564,13 +6520,13 @@ __metadata: focus-visible: ^5.2.0 focus-within-polyfill: ^5.2.1 new-event-polyfill: ^1.0.1 - checksum: f9ce33134119fb893cb84deb6e515e69af23eff9d4c88c2a449ab0e2653460600aceb3ba739ea756e1600a7a78ca68a6d1764a8e2ba6e9465d1da7be887eed4f + checksum: 2079b6903deb1f48b2716c2bc8741c8aa7087377ddafe419790aacdc43af5a28fa68ad78a5f5b8f8dfdcd911068a087ce72832c27805332fecc6202e4d5b5388 languageName: node linkType: hard "@rocket.chat/fuselage-toastbar@npm:next": - version: 0.32.0-dev.219 - resolution: "@rocket.chat/fuselage-toastbar@npm:0.32.0-dev.219" + version: 0.32.0-dev.250 + resolution: "@rocket.chat/fuselage-toastbar@npm:0.32.0-dev.250" peerDependencies: "@rocket.chat/fuselage": "*" "@rocket.chat/fuselage-hooks": "*" @@ -6578,21 +6534,14 @@ __metadata: "@rocket.chat/styled": "*" react: ^17.0.2 react-dom: ^17.0.2 - checksum: cb74a590ae5c8492a5c4139ea97f0a08eb84ed59dc734a80ac1c820eed67d241ae05ffec88cccaba5907d562150d8778763b163b2e263fdeba706707d1d60a09 + checksum: 59bbbb723db8ff2d099d312caa7b704126e56a9277c9e58ac2005ae9fc5f20e483bff70356ca49d5bdeee7f7b5d4690597239695ec19acc653b5cfe05034759b languageName: node linkType: hard -"@rocket.chat/fuselage-tokens@npm:next": - version: 0.32.0-dev.195 - resolution: "@rocket.chat/fuselage-tokens@npm:0.32.0-dev.195" - checksum: 04fa9dfb8c13f33db4f6457ebe95e00a0bc5df7276a71614b8029a1489b79a6b5335f99c40bab382c0a791f4d078efc243007864dbc51da86322b13cc736b1c3 - languageName: node - linkType: hard - -"@rocket.chat/fuselage-tokens@npm:~0.32.0-dev.222": - version: 0.32.0-dev.222 - resolution: "@rocket.chat/fuselage-tokens@npm:0.32.0-dev.222" - checksum: f3ac8c85ced6ec5e0c2b442985f9d94cad9dae22447cd3006cdebdd41bb77d6cb98ecee6b62b835898cc70188dc84a3b5af3b8613a81c1a2f274b250eb83af62 +"@rocket.chat/fuselage-tokens@npm:next, @rocket.chat/fuselage-tokens@npm:~0.32.0-dev.226": + version: 0.32.0-dev.226 + resolution: "@rocket.chat/fuselage-tokens@npm:0.32.0-dev.226" + checksum: b956f4cfd3c032fe35c5682da4b75144e0360a523de958e0afa961c8ee2513071e1ede0ab90d7b212bfa624aa62001aec22316b13ad11f96d6bf00651a78c0f5 languageName: node linkType: hard @@ -6652,14 +6601,14 @@ __metadata: linkType: soft "@rocket.chat/fuselage@npm:next": - version: 0.32.0-dev.272 - resolution: "@rocket.chat/fuselage@npm:0.32.0-dev.272" - dependencies: - "@rocket.chat/css-in-js": ~0.31.23-dev.46 - "@rocket.chat/css-supports": ~0.31.23-dev.46 - "@rocket.chat/fuselage-tokens": ~0.32.0-dev.222 - "@rocket.chat/memo": ~0.31.23-dev.46 - "@rocket.chat/styled": ~0.31.23-dev.46 + version: 0.32.0-dev.276 + resolution: "@rocket.chat/fuselage@npm:0.32.0-dev.276" + dependencies: + "@rocket.chat/css-in-js": ~0.31.23-dev.50 + "@rocket.chat/css-supports": ~0.31.23-dev.50 + "@rocket.chat/fuselage-tokens": ~0.32.0-dev.226 + "@rocket.chat/memo": ~0.31.23-dev.50 + "@rocket.chat/styled": ~0.31.23-dev.50 invariant: ^2.2.4 react-aria: ~3.19.0 react-keyed-flatten-children: ^1.3.0 @@ -6671,7 +6620,7 @@ __metadata: react: ^17.0.2 react-dom: ^17.0.2 react-virtuoso: 1.2.4 - checksum: 102c0a401b7d7fdfa88a6d6da6f64be89761992f740a7ba8611ab1f1bf52813a8c6a2127e8920057b7364f010146ee2b1fe7c39ddb7f4fa2eb85b02950583339 + checksum: 4c921089f2a7abbbb245ccccbaa4f9a11ddd61c015fb70cb3a3d7cae7900127957755c3620de79252af995ee7d3fee31f7516d65b04570bec4f6dfc6a0609ece languageName: node linkType: hard @@ -6737,9 +6686,9 @@ __metadata: linkType: soft "@rocket.chat/icons@npm:next": - version: 0.32.0-dev.246 - resolution: "@rocket.chat/icons@npm:0.32.0-dev.246" - checksum: 0729924cdaa7eb4b0561db802d9cf4ff8848a0019bc1a6dd91878bcf34c161eba0c5eba649f2f5fc78373a666a7079b79f8bd6b5c11e80ae63dbcd150ee1cb14 + version: 0.32.0-dev.258 + resolution: "@rocket.chat/icons@npm:0.32.0-dev.258" + checksum: 2cace37f8c94b10181503847a349bba0966fe49e939396b1a43c34ce9712e513fbeba236263e24c3f76321dd073f63e178693ebc297f926bd75701a357017719 languageName: node linkType: hard @@ -6757,14 +6706,14 @@ __metadata: linkType: soft "@rocket.chat/layout@npm:next": - version: 0.32.0-dev.128 - resolution: "@rocket.chat/layout@npm:0.32.0-dev.128" + version: 0.32.0-dev.159 + resolution: "@rocket.chat/layout@npm:0.32.0-dev.159" peerDependencies: "@rocket.chat/fuselage": "*" react: 17.0.2 react-dom: 17.0.2 react-i18next: ~11.15.4 - checksum: a0db30df4f8220babf0a996a1826fc489bc6bbb59d1c302b61d638e6a113e98bba9f43b1cb30cb6b5ec7f3c46a87dffca83a6533d67b52aa1e7fec270a962f24 + checksum: 0543633f9b03b481f0d0857c024eb3e73a23ca88f0a47a02e51629e556d30dbb587fbc0cdaddc22f7b17b7220dbbae6dc82cae81f73479892e618cbd3848bbb9 languageName: node linkType: hard @@ -6852,46 +6801,32 @@ __metadata: linkType: soft "@rocket.chat/logo@npm:next": - version: 0.32.0-dev.195 - resolution: "@rocket.chat/logo@npm:0.32.0-dev.195" + version: 0.32.0-dev.226 + resolution: "@rocket.chat/logo@npm:0.32.0-dev.226" dependencies: - "@rocket.chat/fuselage-hooks": ~0.32.0-dev.158 - "@rocket.chat/styled": ~0.31.23-dev.19 + "@rocket.chat/fuselage-hooks": ~0.32.0-dev.189 + "@rocket.chat/styled": ~0.31.23-dev.50 tslib: ^2.3.1 peerDependencies: react: 17.0.2 react-dom: 17.0.2 - checksum: 536b87a9438650ced965c5c6aa66e39c187ccaf0461be6772eb6862a5e2e0b7402402820efcadf54d3ad4be18eaf5f0f74b44f317883f8ca771a97204273bc8e - languageName: node - linkType: hard - -"@rocket.chat/memo@npm:next": - version: 0.31.23-dev.19 - resolution: "@rocket.chat/memo@npm:0.31.23-dev.19" - checksum: 2c30df2b2e99b2d6b37d9e695c293724d0c116b521ff33ef747039f37633dc2343051f66e5fd0a05f22f8f6c85fd0a1c2e97b7f256704fdb2178b59621c58634 + checksum: 41853d3650e491c563cdf6bbad30edaaa0814a9f70dc19b28933669adeead19f48bf464fb45a88a43611a0de4252628d88ea94d89252ed3b84234abad91b165d languageName: node linkType: hard -"@rocket.chat/memo@npm:~0.31.23-dev.19, @rocket.chat/memo@npm:~0.31.23-dev.47": - version: 0.31.23-dev.47 - resolution: "@rocket.chat/memo@npm:0.31.23-dev.47" - checksum: 3c90bec9feeb260d6dc1ea64c8a98bbb39fe848f97f066f701939398f882fd2579716fe1f98b7bbc6067a39b6a270f22004aa97ee258b1242a819021a212df81 - languageName: node - linkType: hard - -"@rocket.chat/memo@npm:~0.31.23-dev.46": - version: 0.31.23-dev.46 - resolution: "@rocket.chat/memo@npm:0.31.23-dev.46" - checksum: e69a5cd71661c4f6513986a0f507b649ccbe5b98cd577ea1e0e48fd80bca19de964b0d9fddd20f869048c41bcfa80846911c7353d20be505928ecbcca3c50738 +"@rocket.chat/memo@npm:next, @rocket.chat/memo@npm:~0.31.23-dev.50": + version: 0.31.23-dev.50 + resolution: "@rocket.chat/memo@npm:0.31.23-dev.50" + checksum: 1da7eca7c167c0a4784ea4a701c962109f4f1a69e6d4469faff3f947f3bd5a2e2359c3dfe50404c1338a577d24c33e80b8a3cf63140038e7734ebd741d16ce64 languageName: node linkType: hard "@rocket.chat/message-parser@npm:next": - version: 0.32.0-dev.217 - resolution: "@rocket.chat/message-parser@npm:0.32.0-dev.217" + version: 0.32.0-dev.224 + resolution: "@rocket.chat/message-parser@npm:0.32.0-dev.224" dependencies: tldts: ~5.7.104 - checksum: 0f8d6788775578950da715722fcdc0159c99d36999d279f1a102e5ce946e420f42750bb253958fc5a3f0ff9d56c760ab2a11370e98b101bc0d325fda9fc22281 + checksum: 6fa62867de962af53cfecf45c713faa00c429f5be5b66107333cb29b51a7350198ff0c302db32f13cee136bf2d677d4bc3f7897a013e53dee43ea5b52cf900cd languageName: node linkType: hard @@ -7429,77 +7364,33 @@ __metadata: linkType: soft "@rocket.chat/string-helpers@npm:next": - version: 0.31.23-dev.19 - resolution: "@rocket.chat/string-helpers@npm:0.31.23-dev.19" - dependencies: - tslib: ^2.3.1 - checksum: a3dc6b458de1e50243f40465afc3d022f93134db0b5059947a6d1858620b8d421b21b63dcadb4880d0b9518bb7410a512a56a6a49dad5d127655d6b85be4653c - languageName: node - linkType: hard - -"@rocket.chat/styled@npm:next": - version: 0.31.23-dev.19 - resolution: "@rocket.chat/styled@npm:0.31.23-dev.19" - dependencies: - "@rocket.chat/css-in-js": ~0.31.23-dev.19 - tslib: ^2.3.1 - checksum: c1c4766c511d62c140dadaba9d9fc43572c051f7a4d86121df99ace2955f7a606faa91281987616a8b3d907526652c5d4b5b7e72b4ac0af0474641d16d694b62 - languageName: node - linkType: hard - -"@rocket.chat/styled@npm:~0.31.23-dev.19": - version: 0.31.23-dev.47 - resolution: "@rocket.chat/styled@npm:0.31.23-dev.47" - dependencies: - "@rocket.chat/css-in-js": ~0.31.23-dev.47 - tslib: ^2.3.1 - checksum: 71da37ea3a24d7a8329e9feeffe0fa473e96babda23c5cec1daf4dc71bdf573c06deb6646e39824ed2b7b5fb361332c325cb2044df2def1eb87053b36a0c79b9 - languageName: node - linkType: hard - -"@rocket.chat/styled@npm:~0.31.23-dev.46": - version: 0.31.23-dev.46 - resolution: "@rocket.chat/styled@npm:0.31.23-dev.46" + version: 0.31.23-dev.50 + resolution: "@rocket.chat/string-helpers@npm:0.31.23-dev.50" dependencies: - "@rocket.chat/css-in-js": ~0.31.23-dev.46 tslib: ^2.3.1 - checksum: 5db6281e7588f6acc564805f3b0dfb31372ae1ce4b6b14d0c5c5dd78dba1285ded6280945e40d1faaf7691fd17b757db64b681c4688002d209d3f61a9b639767 + checksum: 864522e92286dc8bf5fb70f388491db4d4c980867a42f5a0f469e6f2cd03509faab17248f6d5bfec2d5268bcb6076c3f7ba9ccece4ac29ad88c4d870bf96098e languageName: node linkType: hard -"@rocket.chat/stylis-logical-props-middleware@npm:~0.31.23-dev.19": - version: 0.31.23-dev.45 - resolution: "@rocket.chat/stylis-logical-props-middleware@npm:0.31.23-dev.45" +"@rocket.chat/styled@npm:next, @rocket.chat/styled@npm:~0.31.23-dev.50": + version: 0.31.23-dev.50 + resolution: "@rocket.chat/styled@npm:0.31.23-dev.50" dependencies: - "@rocket.chat/css-supports": ~0.31.23-dev.45 + "@rocket.chat/css-in-js": ~0.31.23-dev.50 tslib: ^2.3.1 - peerDependencies: - stylis: 4.0.10 - checksum: 7645bcde9fab55860fa6f886b942ae1a6189a663e61241fed513fd532102074e50e8696513e9439345a6272fc681b82bd3f8ab4a7b4cc6bce75a446e66efe1cf - languageName: node - linkType: hard - -"@rocket.chat/stylis-logical-props-middleware@npm:~0.31.23-dev.46": - version: 0.31.23-dev.46 - resolution: "@rocket.chat/stylis-logical-props-middleware@npm:0.31.23-dev.46" - dependencies: - "@rocket.chat/css-supports": ~0.31.23-dev.46 - tslib: ^2.3.1 - peerDependencies: - stylis: 4.0.10 - checksum: a67c52e29e8fa2a0ff22913c239ab7e6c0ceda15b07fc28ee4dbc60937c7d438d6067d663f7a31bfcd90f8fc4fa2c6c428791fa93073232c8afd58206b83856c + checksum: e457a8d57c03ebe593af8409658eec4d386b0c8fdd603829b3bcffc1be9f58ba84073e9613a48b522c49ce425f500f0c2ae72c09c6c8c5343d872aca24ad8bff languageName: node linkType: hard -"@rocket.chat/stylis-logical-props-middleware@npm:~0.31.23-dev.47": - version: 0.31.23-dev.47 - resolution: "@rocket.chat/stylis-logical-props-middleware@npm:0.31.23-dev.47" +"@rocket.chat/stylis-logical-props-middleware@npm:~0.31.23-dev.50": + version: 0.31.23-dev.50 + resolution: "@rocket.chat/stylis-logical-props-middleware@npm:0.31.23-dev.50" dependencies: - "@rocket.chat/css-supports": ~0.31.23-dev.47 + "@rocket.chat/css-supports": ~0.31.23-dev.50 tslib: ^2.3.1 peerDependencies: stylis: 4.0.10 - checksum: e3719d68b34a65a11ef5e6586b6df1ac6976b2fa3fa6b6bec313d202ea7c10c19a3c24b701eef8d067f1beac295956d78fe9da613fe2afb904c0d3d9e775c5a5 + checksum: b54536435b02731e718c4a901e23e650e60229a1a475cf101f4d6ea5f72b6bce08d05ec402b41aa0201e6d7ce8001d9847f2fedb6859f0b380bff7ef0582b004 languageName: node linkType: hard @@ -7616,9 +7507,9 @@ __metadata: linkType: soft "@rocket.chat/ui-kit@npm:next": - version: 0.32.0-dev.180 - resolution: "@rocket.chat/ui-kit@npm:0.32.0-dev.180" - checksum: b7eb01a2bd154bedb1ebe7dd9af8752c6e1cd3031a7e7cff8d74219d692aec12382bf751a2005551bac68a791c207b98812bc9d26318599e7f89ae140ec09e35 + version: 0.32.0-dev.211 + resolution: "@rocket.chat/ui-kit@npm:0.32.0-dev.211" + checksum: 0e7e206a1bd0c4b3cd988f7fae57735b8adb6e4dc5a1ecc021028d9ca9d8264ac20a22f923c9eb7c311d0ab9fb65261783445153a56a73d23febb417302fc2f8 languageName: node linkType: hard