From 38d1003842293bf08c0e94a632ffaeb8963bbe3e Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?J=C3=BAlia=20Jaeger=20Foresti?= <60678893+juliajforesti@users.noreply.github.com> Date: Mon, 10 Jul 2023 19:01:26 -0300 Subject: [PATCH 001/342] regression: add missing translations on MenuV2 replace (#29777) --- .../header/actions/hooks/useAdministrationMenu.tsx | 9 +++++---- .../sidebar/header/actions/hooks/useCreateRoomMenu.tsx | 7 ++++--- .../sidebar/header/actions/hooks/useSortMenu.tsx | 10 +++++++--- 3 files changed, 16 insertions(+), 10 deletions(-) diff --git a/apps/meteor/client/sidebar/header/actions/hooks/useAdministrationMenu.tsx b/apps/meteor/client/sidebar/header/actions/hooks/useAdministrationMenu.tsx index 5145ac30c0f4..f6179bb93d26 100644 --- a/apps/meteor/client/sidebar/header/actions/hooks/useAdministrationMenu.tsx +++ b/apps/meteor/client/sidebar/header/actions/hooks/useAdministrationMenu.tsx @@ -1,5 +1,5 @@ import { useMutableCallback } from '@rocket.chat/fuselage-hooks'; -import { useAtLeastOnePermission, usePermission } from '@rocket.chat/ui-contexts'; +import { useAtLeastOnePermission, usePermission, useTranslation } from '@rocket.chat/ui-contexts'; import { AccountBox } from '../../../../../app/ui-utils/client'; import type { IAppAccountBoxItem, AccountBoxItem } from '../../../../../app/ui-utils/client/lib/AccountBox'; @@ -39,6 +39,7 @@ const ADMIN_PERMISSIONS = [ ]; export const useAdministrationMenu = () => { + const t = useTranslation(); const getAccountBoxItems = useMutableCallback(() => AccountBox.getItems()); const accountBoxItems = useReactiveValue(getAccountBoxItems); @@ -66,9 +67,9 @@ export const useAdministrationMenu = () => { const auditItems = useAuditItems({ showAudit: hasAuditPermission, showAuditLog: hasAuditLogPermission }); const sections = [ - { title: 'Administration', items: administrationItems, permission: showAdmin }, - { title: 'Apps', items: appItems, permission: showApps }, - { title: 'Audit', items: auditItems, permission: showAudit }, + { title: t('Administration'), items: administrationItems, permission: showAdmin }, + { title: t('Apps'), items: appItems, permission: showApps }, + { title: t('Audit'), items: auditItems, permission: showAudit }, ]; return sections.filter(({ permission }) => permission); diff --git a/apps/meteor/client/sidebar/header/actions/hooks/useCreateRoomMenu.tsx b/apps/meteor/client/sidebar/header/actions/hooks/useCreateRoomMenu.tsx index 7e4cbe8d138e..f220267fac05 100644 --- a/apps/meteor/client/sidebar/header/actions/hooks/useCreateRoomMenu.tsx +++ b/apps/meteor/client/sidebar/header/actions/hooks/useCreateRoomMenu.tsx @@ -1,4 +1,4 @@ -import { useAtLeastOnePermission, useSetting } from '@rocket.chat/ui-contexts'; +import { useAtLeastOnePermission, useSetting, useTranslation } from '@rocket.chat/ui-contexts'; import { useIsEnterprise } from '../../../../hooks/useIsEnterprise'; import { useCreateRoomItems } from './useCreateRoomItems'; @@ -7,6 +7,7 @@ import { useMatrixFederationItems } from './useMatrixFederationItems.tsx'; const CREATE_ROOM_PERMISSIONS = ['create-c', 'create-p', 'create-d', 'start-discussion', 'start-discussion-other-user']; export const useCreateRoom = () => { + const t = useTranslation(); const showCreate = useAtLeastOnePermission(CREATE_ROOM_PERMISSIONS); const { data } = useIsEnterprise(); @@ -16,8 +17,8 @@ export const useCreateRoom = () => { const matrixFederationSearchItems = useMatrixFederationItems({ isMatrixEnabled }); const sections = [ - { title: 'Create_new', items: createRoomItems, permission: showCreate }, - { title: 'Explore', items: matrixFederationSearchItems, permission: showCreate && isMatrixEnabled }, + { title: t('Create_new'), items: createRoomItems, permission: showCreate }, + { title: t('Explore'), items: matrixFederationSearchItems, permission: showCreate && isMatrixEnabled }, ]; return sections.filter((section) => section.permission); diff --git a/apps/meteor/client/sidebar/header/actions/hooks/useSortMenu.tsx b/apps/meteor/client/sidebar/header/actions/hooks/useSortMenu.tsx index 8a3f6a56e590..bea1d999997e 100644 --- a/apps/meteor/client/sidebar/header/actions/hooks/useSortMenu.tsx +++ b/apps/meteor/client/sidebar/header/actions/hooks/useSortMenu.tsx @@ -1,16 +1,20 @@ +import { useTranslation } from '@rocket.chat/ui-contexts'; + import { useGroupingListItems } from './useGroupingListItems'; import { useSortModeItems } from './useSortModeItems'; import { useViewModeItems } from './useViewModeItems'; export const useSortMenu = () => { + const t = useTranslation(); + const viewModeItems = useViewModeItems(); const sortModeItems = useSortModeItems(); const groupingListItems = useGroupingListItems(); const sections = [ - { title: 'Display', items: viewModeItems }, - { title: 'Sort_By', items: sortModeItems }, - { title: 'Group_by', items: groupingListItems }, + { title: t('Display'), items: viewModeItems }, + { title: t('Sort_By'), items: sortModeItems }, + { title: t('Group_by'), items: groupingListItems }, ]; return sections; From cadec3a504e57a4535244abc183181a3869b9b5d Mon Sep 17 00:00:00 2001 From: Guilherme Gazzo Date: Mon, 10 Jul 2023 22:24:36 -0300 Subject: [PATCH 002/342] chore: create FeaturePreview Component (#29759) Co-authored-by: Tasso Evangelista --- .../components/message/toolbox/Toolbox.tsx | 2 +- .../hooks/useFeaturePreviewList.spec.tsx | 89 --- .../sidebar/header/hooks/useAccountItems.tsx | 2 +- .../AccountFeaturePreviewBadge.tsx | 3 +- .../AccountFeaturePreviewPage.tsx | 4 +- .../client/views/account/sidebarItems.tsx | 2 +- apps/meteor/jest.client.config.ts | 2 +- apps/meteor/package.json | 3 +- ee/packages/api-client/package.json | 4 +- ee/packages/ddp-client/package.json | 6 +- ee/packages/omnichannel-services/package.json | 4 +- ee/packages/pdf-worker/package.json | 8 +- ee/packages/presence/package.json | 2 +- ee/packages/ui-theming/package.json | 4 +- packages/account-utils/package.json | 4 +- packages/agenda/package.json | 4 +- packages/base64/package.json | 2 +- packages/cas-validate/package.json | 4 +- packages/core-services/package.json | 4 +- packages/cron/package.json | 4 +- packages/gazzodown/package.json | 6 +- packages/i18n/package.json | 4 +- packages/log-format/package.json | 4 +- packages/mock-providers/package.json | 4 +- packages/model-typings/package.json | 4 +- packages/models/package.json | 4 +- packages/node-poplib/package.json | 4 +- packages/random/package.json | 4 +- packages/rest-typings/package.json | 6 +- packages/server-fetch/package.json | 4 +- packages/sha256/package.json | 2 +- packages/tools/package.json | 4 +- packages/ui-client/jest.config.ts | 25 + packages/ui-client/package.json | 12 +- .../FeaturePreview/FeaturePreview.spec.tsx | 63 ++ .../FeaturePreview/FeaturePreview.tsx | 26 + .../src}/hooks/useFeaturePreview.spec.tsx | 135 ++-- .../ui-client/src}/hooks/useFeaturePreview.ts | 0 .../src/hooks/useFeaturePreviewList.spec.tsx | 73 ++ .../src}/hooks/useFeaturePreviewList.ts | 0 packages/ui-client/src/index.ts | 2 + packages/ui-client/tsconfig.json | 5 +- packages/ui-composer/package.json | 4 +- packages/ui-contexts/package.json | 4 +- packages/ui-video-conf/package.json | 4 +- packages/uikit-playground/package.json | 4 +- packages/web-ui-registration/package.json | 4 +- yarn.lock | 749 +++--------------- 48 files changed, 434 insertions(+), 883 deletions(-) delete mode 100644 apps/meteor/client/hooks/useFeaturePreviewList.spec.tsx create mode 100644 packages/ui-client/jest.config.ts create mode 100644 packages/ui-client/src/components/FeaturePreview/FeaturePreview.spec.tsx create mode 100644 packages/ui-client/src/components/FeaturePreview/FeaturePreview.tsx rename {apps/meteor/client => packages/ui-client/src}/hooks/useFeaturePreview.spec.tsx (63%) rename {apps/meteor/client => packages/ui-client/src}/hooks/useFeaturePreview.ts (100%) create mode 100644 packages/ui-client/src/hooks/useFeaturePreviewList.spec.tsx rename {apps/meteor/client => packages/ui-client/src}/hooks/useFeaturePreviewList.ts (100%) diff --git a/apps/meteor/client/components/message/toolbox/Toolbox.tsx b/apps/meteor/client/components/message/toolbox/Toolbox.tsx index 2c9a3ccae95b..ee1a0bc2c1b8 100644 --- a/apps/meteor/client/components/message/toolbox/Toolbox.tsx +++ b/apps/meteor/client/components/message/toolbox/Toolbox.tsx @@ -1,6 +1,7 @@ import type { IMessage, IRoom, ISubscription, ITranslatedMessage } from '@rocket.chat/core-typings'; import { isThreadMessage, isRoomFederated } from '@rocket.chat/core-typings'; import { MessageToolbox, MessageToolboxItem } from '@rocket.chat/fuselage'; +import { useFeaturePreview } from '@rocket.chat/ui-client'; import { useUser, useSettings, useTranslation } from '@rocket.chat/ui-contexts'; import { useQuery } from '@tanstack/react-query'; import type { ReactElement } from 'react'; @@ -10,7 +11,6 @@ import type { MessageActionContext } from '../../../../app/ui-utils/client/lib/M import { MessageAction } from '../../../../app/ui-utils/client/lib/MessageAction'; import { sdk } from '../../../../app/utils/client/lib/SDKClient'; import { useEmojiPickerData } from '../../../contexts/EmojiPickerContext'; -import { useFeaturePreview } from '../../../hooks/useFeaturePreview'; import EmojiElement from '../../../views/composer/EmojiPicker/EmojiElement'; import { useIsSelecting } from '../../../views/room/MessageList/contexts/SelectedMessagesContext'; import { useAutoTranslate } from '../../../views/room/MessageList/hooks/useAutoTranslate'; diff --git a/apps/meteor/client/hooks/useFeaturePreviewList.spec.tsx b/apps/meteor/client/hooks/useFeaturePreviewList.spec.tsx deleted file mode 100644 index 2fbcdb4cb6d2..000000000000 --- a/apps/meteor/client/hooks/useFeaturePreviewList.spec.tsx +++ /dev/null @@ -1,89 +0,0 @@ -import { renderHook } from '@testing-library/react-hooks'; -import React from 'react'; - -import { MockedSettingsContext, MockedUserContext } from './useFeaturePreview.spec'; -import { useFeaturePreviewList, defaultFeaturesPreview } from './useFeaturePreviewList'; - -it('should return the number of unseen features and Accounts_AllowFeaturePreview enabled ', () => { - const { result } = renderHook( - () => { - return useFeaturePreviewList(); - }, - { - wrapper: ({ children }) => ( - - {children} - - ), - }, - ); - - expect(result.all[0]).toEqual( - expect.objectContaining({ - featurePreviewEnabled: true, - unseenFeatures: defaultFeaturesPreview.length, - }), - ); -}); - -it('should return the number of unseen features and Accounts_AllowFeaturePreview disabled ', () => { - const { result } = renderHook( - () => { - return useFeaturePreviewList(); - }, - { - wrapper: ({ children }) => ( - - {children} - - ), - }, - ); - - expect(result.all[0]).toEqual( - expect.objectContaining({ - featurePreviewEnabled: false, - unseenFeatures: 0, - }), - ); -}); - -it('should return 0 unseen features', () => { - const { result } = renderHook( - () => { - return useFeaturePreviewList(); - }, - { - wrapper: ({ children }) => ( - - - {children} - - - ), - }, - ); - - expect(result.all[0]).toEqual( - expect.objectContaining({ - featurePreviewEnabled: true, - unseenFeatures: 0, - }), - ); -}); diff --git a/apps/meteor/client/sidebar/header/hooks/useAccountItems.tsx b/apps/meteor/client/sidebar/header/hooks/useAccountItems.tsx index 5e6f4c7d183a..e4757c2d48b1 100644 --- a/apps/meteor/client/sidebar/header/hooks/useAccountItems.tsx +++ b/apps/meteor/client/sidebar/header/hooks/useAccountItems.tsx @@ -1,10 +1,10 @@ import { Badge } from '@rocket.chat/fuselage'; import { useMutableCallback } from '@rocket.chat/fuselage-hooks'; +import { defaultFeaturesPreview, useFeaturePreviewList } from '@rocket.chat/ui-client'; import { useLogout, useRoute, useTranslation } from '@rocket.chat/ui-contexts'; import React from 'react'; import type { GenericMenuItemProps } from '../../../components/GenericMenu/GenericMenuItem'; -import { useFeaturePreviewList, defaultFeaturesPreview } from '../../../hooks/useFeaturePreviewList'; export const useAccountItems = (): GenericMenuItemProps[] => { const t = useTranslation(); diff --git a/apps/meteor/client/views/account/featurePreview/AccountFeaturePreviewBadge.tsx b/apps/meteor/client/views/account/featurePreview/AccountFeaturePreviewBadge.tsx index 631a2b76c625..95d7d2d12353 100644 --- a/apps/meteor/client/views/account/featurePreview/AccountFeaturePreviewBadge.tsx +++ b/apps/meteor/client/views/account/featurePreview/AccountFeaturePreviewBadge.tsx @@ -1,9 +1,8 @@ import { Badge } from '@rocket.chat/fuselage'; +import { useFeaturePreviewList } from '@rocket.chat/ui-client'; import { useTranslation } from '@rocket.chat/ui-contexts'; import React from 'react'; -import { useFeaturePreviewList } from '../../../hooks/useFeaturePreviewList'; - const AccountFeaturePreviewBadge = () => { const t = useTranslation(); const { unseenFeatures } = useFeaturePreviewList(); diff --git a/apps/meteor/client/views/account/featurePreview/AccountFeaturePreviewPage.tsx b/apps/meteor/client/views/account/featurePreview/AccountFeaturePreviewPage.tsx index 414aa2377490..d190fd00dc17 100644 --- a/apps/meteor/client/views/account/featurePreview/AccountFeaturePreviewPage.tsx +++ b/apps/meteor/client/views/account/featurePreview/AccountFeaturePreviewPage.tsx @@ -11,6 +11,8 @@ import { StatesTitle, Accordion, } from '@rocket.chat/fuselage'; +import type { FeaturePreviewProps } from '@rocket.chat/ui-client'; +import { useFeaturePreviewList } from '@rocket.chat/ui-client'; import type { TranslationKey } from '@rocket.chat/ui-contexts'; import { useToastMessageDispatch, useTranslation, useEndpoint } from '@rocket.chat/ui-contexts'; import type { ChangeEvent } from 'react'; @@ -18,8 +20,6 @@ import React, { useEffect, Fragment } from 'react'; import { useForm } from 'react-hook-form'; import Page from '../../../components/Page'; -import type { FeaturePreviewProps } from '../../../hooks/useFeaturePreviewList'; -import { useFeaturePreviewList } from '../../../hooks/useFeaturePreviewList'; const AccountFeaturePreviewPage = () => { const t = useTranslation(); diff --git a/apps/meteor/client/views/account/sidebarItems.tsx b/apps/meteor/client/views/account/sidebarItems.tsx index 4087774d98cc..a2d0720fe6e2 100644 --- a/apps/meteor/client/views/account/sidebarItems.tsx +++ b/apps/meteor/client/views/account/sidebarItems.tsx @@ -1,8 +1,8 @@ +import { defaultFeaturesPreview } from '@rocket.chat/ui-client'; import React from 'react'; import { hasPermission, hasAtLeastOnePermission } from '../../../app/authorization/client'; import { settings } from '../../../app/settings/client'; -import { defaultFeaturesPreview } from '../../hooks/useFeaturePreviewList'; import { createSidebarItems } from '../../lib/createSidebarItems'; import AccountFeaturePreviewBadge from './featurePreview/AccountFeaturePreviewBadge'; diff --git a/apps/meteor/jest.client.config.ts b/apps/meteor/jest.client.config.ts index 9523d726d9e9..db3a03ecf03f 100644 --- a/apps/meteor/jest.client.config.ts +++ b/apps/meteor/jest.client.config.ts @@ -3,7 +3,7 @@ export default { testEnvironment: 'jsdom', modulePathIgnorePatterns: ['/dist/'], - testMatch: ['/client/hooks/**.spec.[jt]s?(x)', './client/hooks/**.spec.ts', '/client/hooks/**.spec.ts'], + testMatch: ['/client/hooks/**.spec.[jt]s?(x)', '/client/components/**.spec.[jt]s?(x)'], transform: { '^.+\\.(t|j)sx?$': '@swc/jest', }, diff --git a/apps/meteor/package.json b/apps/meteor/package.json index 80289d401c38..99385d0b56f3 100644 --- a/apps/meteor/package.json +++ b/apps/meteor/package.json @@ -73,6 +73,7 @@ "@playwright/test": "^1.22.2", "@rocket.chat/eslint-config": "workspace:^", "@rocket.chat/livechat": "workspace:^", + "@rocket.chat/mock-providers": "workspace:^", "@settlin/spacebars-loader": "^1.0.9", "@storybook/addon-essentials": "~6.5.16", "@storybook/addon-interactions": "~6.5.16", @@ -177,7 +178,7 @@ "eslint-plugin-you-dont-need-lodash-underscore": "~6.12.0", "fast-glob": "^3.2.12", "i18next": "^20.6.1", - "jest": "^29.6.1", + "jest": "~29.6.1", "jsdom-global": "^3.0.2", "mocha": "^9.2.2", "nyc": "^15.1.0", diff --git a/ee/packages/api-client/package.json b/ee/packages/api-client/package.json index f6c673ce22ff..f876bc176cb2 100644 --- a/ee/packages/api-client/package.json +++ b/ee/packages/api-client/package.json @@ -4,10 +4,10 @@ "devDependencies": { "@swc/core": "^1.3.66", "@swc/jest": "^0.2.26", - "@types/jest": "~29.5.2", + "@types/jest": "~29.5.3", "@types/strict-uri-encode": "^2.0.0", "eslint": "~8.43.0", - "jest": "~29.5.0", + "jest": "~29.6.1", "jest-fetch-mock": "^3.0.3", "ts-jest": "~29.0.5", "typescript": "~5.1.3" diff --git a/ee/packages/ddp-client/package.json b/ee/packages/ddp-client/package.json index 8a8c73c84436..aa24abd7ead5 100644 --- a/ee/packages/ddp-client/package.json +++ b/ee/packages/ddp-client/package.json @@ -4,11 +4,11 @@ "devDependencies": { "@swc/core": "^1.3.66", "@swc/jest": "^0.2.26", - "@types/jest": "^29.5.2", + "@types/jest": "~29.5.3", "@types/ws": "^8.5.5", "eslint": "^8.43.0", - "jest": "^29.5.0", - "jest-environment-jsdom": "~29.5.0", + "jest": "~29.6.1", + "jest-environment-jsdom": "~29.6.1", "jest-websocket-mock": "^2.4.0", "typescript": "~5.1.3", "ws": "^8.13.0" diff --git a/ee/packages/omnichannel-services/package.json b/ee/packages/omnichannel-services/package.json index ded11e4f6da8..3978a5090a6f 100644 --- a/ee/packages/omnichannel-services/package.json +++ b/ee/packages/omnichannel-services/package.json @@ -4,9 +4,9 @@ "private": true, "devDependencies": { "@rocket.chat/eslint-config": "workspace:^", - "@types/jest": "~29.5.2", + "@types/jest": "~29.5.3", "eslint": "~8.43.0", - "jest": "~29.5.0", + "jest": "~29.6.1", "ts-jest": "~29.0.5", "typescript": "~5.1.3" }, diff --git a/ee/packages/pdf-worker/package.json b/ee/packages/pdf-worker/package.json index 95d4c834eaf1..f6d21946ed8d 100644 --- a/ee/packages/pdf-worker/package.json +++ b/ee/packages/pdf-worker/package.json @@ -8,12 +8,12 @@ "@testing-library/jest-dom": "^5.16.5", "@testing-library/react": "~13.4.0", "@types/emojione": "^2.2.6", - "@types/jest": "~29.5.2", - "@types/react-dom": "^18.2.5", + "@types/jest": "~29.5.3", + "@types/react-dom": "~17.0.20", "@types/testing-library__jest-dom": "~5.14.6", "eslint": "~8.43.0", - "jest": "~29.5.0", - "jest-environment-jsdom": "~29.5.0", + "jest": "~29.6.1", + "jest-environment-jsdom": "~29.6.1", "react-dom": "^18.2.0", "ts-jest": "~29.0.5", "typescript": "~5.1.3" diff --git a/ee/packages/presence/package.json b/ee/packages/presence/package.json index 3d9f0602ffa4..637170c811a3 100644 --- a/ee/packages/presence/package.json +++ b/ee/packages/presence/package.json @@ -12,7 +12,7 @@ "@types/node": "^14.18.51", "babel-jest": "^29.0.3", "eslint": "~8.43.0", - "jest": "~29.5.0", + "jest": "~29.6.1", "typescript": "~5.1.3" }, "scripts": { diff --git a/ee/packages/ui-theming/package.json b/ee/packages/ui-theming/package.json index b16a3c816849..e75c37bba089 100644 --- a/ee/packages/ui-theming/package.json +++ b/ee/packages/ui-theming/package.json @@ -18,14 +18,14 @@ "@storybook/manager-webpack4": "~6.5.16", "@storybook/react": "~6.5.16", "@storybook/testing-library": "~0.0.13", - "@types/jest": "~29.5.2", + "@types/jest": "~29.5.3", "@types/react": "~17.0.62", "eslint": "~8.43.0", "eslint-plugin-anti-trojan-source": "~1.1.1", "eslint-plugin-react": "~7.32.2", "eslint-plugin-react-hooks": "~4.6.0", "eslint-plugin-testing-library": "^5.11.0", - "jest": "~29.5.0", + "jest": "~29.6.1", "react": "~17.0.2", "react-docgen-typescript-plugin": "~1.0.5", "ts-jest": "~29.0.5", diff --git a/packages/account-utils/package.json b/packages/account-utils/package.json index 235f0314647b..d55c4835098b 100644 --- a/packages/account-utils/package.json +++ b/packages/account-utils/package.json @@ -3,9 +3,9 @@ "version": "0.0.1", "private": true, "devDependencies": { - "@types/jest": "~29.5.2", + "@types/jest": "~29.5.3", "eslint": "~8.43.0", - "jest": "~29.5.0", + "jest": "~29.6.1", "ts-jest": "~29.0.5", "typescript": "~5.1.3" }, diff --git a/packages/agenda/package.json b/packages/agenda/package.json index 434566005aee..c5b92a2ce430 100644 --- a/packages/agenda/package.json +++ b/packages/agenda/package.json @@ -13,9 +13,9 @@ }, "devDependencies": { "@types/debug": "^4.1.8", - "@types/jest": "~29.5.2", + "@types/jest": "~29.5.3", "eslint": "~8.43.0", - "jest": "~29.5.0", + "jest": "~29.6.1", "ts-jest": "~29.0.5", "typescript": "~5.1.3" }, diff --git a/packages/base64/package.json b/packages/base64/package.json index cf8d03b65c04..744c101512e7 100644 --- a/packages/base64/package.json +++ b/packages/base64/package.json @@ -19,7 +19,7 @@ "@typescript-eslint/eslint-plugin": "~5.60.0", "@typescript-eslint/parser": "~5.60.0", "eslint": "~8.43.0", - "jest": "~29.5.0", + "jest": "~29.6.1", "ts-jest": "~29.0.5", "typescript": "~5.1.3" }, diff --git a/packages/cas-validate/package.json b/packages/cas-validate/package.json index 2ee9412e5665..ee34be0210d1 100644 --- a/packages/cas-validate/package.json +++ b/packages/cas-validate/package.json @@ -4,9 +4,9 @@ "version": "0.0.1", "private": true, "devDependencies": { - "@types/jest": "~29.5.2", + "@types/jest": "~29.5.3", "eslint": "~8.43.0", - "jest": "~29.5.0", + "jest": "~29.6.1", "ts-jest": "~29.0.5", "typescript": "~5.1.3" }, diff --git a/packages/core-services/package.json b/packages/core-services/package.json index e0292eeb4f3c..3ef8d4bdb8bc 100644 --- a/packages/core-services/package.json +++ b/packages/core-services/package.json @@ -9,10 +9,10 @@ "@rocket.chat/eslint-config": "workspace:^", "@types/babel__core": "^7", "@types/babel__preset-env": "^7", - "@types/jest": "^29.5.1", + "@types/jest": "~29.5.3", "babel-jest": "^29.5.0", "eslint": "~8.43.0", - "jest": "^29.5.0", + "jest": "~29.6.1", "mongodb": "^4.12.1", "prettier": "~2.8.8", "typescript": "~5.1.3" diff --git a/packages/cron/package.json b/packages/cron/package.json index 9f009d83baa6..b6e1a006e59d 100644 --- a/packages/cron/package.json +++ b/packages/cron/package.json @@ -3,9 +3,9 @@ "version": "0.0.1", "private": true, "devDependencies": { - "@types/jest": "^27.4.1", + "@types/jest": "~29.5.3", "eslint": "^8.43.0", - "jest": "~29.5.0", + "jest": "~29.6.1", "ts-jest": "~29.0.5", "typescript": "~5.1.3" }, diff --git a/packages/gazzodown/package.json b/packages/gazzodown/package.json index cb195dd48654..46b0d6396122 100644 --- a/packages/gazzodown/package.json +++ b/packages/gazzodown/package.json @@ -25,7 +25,7 @@ "@swc/jest": "^0.2.26", "@testing-library/jest-dom": "^5.16.5", "@testing-library/react": "~12.1.5", - "@types/jest": "~29.5.2", + "@types/jest": "~29.5.3", "@types/katex": "~0.16.0", "@types/react": "~17.0.62", "@types/react-dom": "~17.0.20", @@ -39,8 +39,8 @@ "eslint-plugin-react-hooks": "~4.6.0", "eslint-plugin-storybook": "~0.6.12", "identity-obj-proxy": "^3.0.0", - "jest": "~29.5.0", - "jest-environment-jsdom": "~29.5.0", + "jest": "~29.6.1", + "jest-environment-jsdom": "~29.6.1", "katex": "~0.16.7", "outdent": "^0.8.0", "react-docgen-typescript-plugin": "~1.0.5", diff --git a/packages/i18n/package.json b/packages/i18n/package.json index bc913f9e10b1..020b4aae293b 100644 --- a/packages/i18n/package.json +++ b/packages/i18n/package.json @@ -8,10 +8,10 @@ "@babel/preset-typescript": "~7.22.5", "@types/babel__core": "~7.20.1", "@types/babel__preset-env": "~7.9.2", - "@types/jest": "^29.5.2", + "@types/jest": "~29.5.3", "babel-jest": "^29.5.0", "eslint": "^8.43.0", - "jest": "^29.5.0", + "jest": "~29.6.1", "ts-jest": "~29.0.5", "tsup": "^6.7.0", "typescript": "~5.1.3" diff --git a/packages/log-format/package.json b/packages/log-format/package.json index 497a3913a8f9..c3b0a1aa404a 100644 --- a/packages/log-format/package.json +++ b/packages/log-format/package.json @@ -5,9 +5,9 @@ "devDependencies": { "@types/chalk": "^2.2.0", "@types/ejson": "^2.2.0", - "@types/jest": "^27.4.1", + "@types/jest": "~29.5.3", "eslint": "^8.43.0", - "jest": "~29.5.0", + "jest": "~29.6.1", "ts-jest": "~29.0.5", "typescript": "~5.1.3" }, diff --git a/packages/mock-providers/package.json b/packages/mock-providers/package.json index 9cbe0db45962..1251cde3399f 100644 --- a/packages/mock-providers/package.json +++ b/packages/mock-providers/package.json @@ -5,9 +5,9 @@ "devDependencies": { "@rocket.chat/ui-contexts": "workspace:*", "@tanstack/react-query": "^4.16.1", - "@types/jest": "^27.4.1", + "@types/jest": "~29.5.3", "eslint": "^8.12.0", - "jest": "~29.5.0", + "jest": "~29.6.1", "react": "~17.0.2", "ts-jest": "~29.0.5", "typescript": "~5.0.2" diff --git a/packages/model-typings/package.json b/packages/model-typings/package.json index 5f4b0a104ede..b167704d770f 100644 --- a/packages/model-typings/package.json +++ b/packages/model-typings/package.json @@ -3,10 +3,10 @@ "version": "0.0.4", "private": true, "devDependencies": { - "@types/jest": "~29.5.2", + "@types/jest": "~29.5.3", "@types/node-rsa": "^1.1.1", "eslint": "~8.43.0", - "jest": "~29.5.0", + "jest": "~29.6.1", "mongodb": "^4.12.1", "ts-jest": "~29.0.5", "typescript": "~5.1.3" diff --git a/packages/models/package.json b/packages/models/package.json index 1a7e6cf3acc6..d69d02185c13 100644 --- a/packages/models/package.json +++ b/packages/models/package.json @@ -3,9 +3,9 @@ "version": "0.0.4", "private": true, "devDependencies": { - "@types/jest": "~29.5.2", + "@types/jest": "~29.5.3", "eslint": "~8.43.0", - "jest": "~29.5.0", + "jest": "~29.6.1", "ts-jest": "~29.0.5", "typescript": "~5.1.3" }, diff --git a/packages/node-poplib/package.json b/packages/node-poplib/package.json index 0d51c8c9f1a0..879a6345e484 100644 --- a/packages/node-poplib/package.json +++ b/packages/node-poplib/package.json @@ -3,9 +3,9 @@ "version": "0.0.1", "private": true, "devDependencies": { - "@types/jest": "~29.5.2", + "@types/jest": "~29.5.3", "eslint": "~8.43.0", - "jest": "~29.5.0", + "jest": "~29.6.1", "ts-jest": "~29.0.5", "typescript": "~5.1.3" }, diff --git a/packages/random/package.json b/packages/random/package.json index f436db859d99..297e2b72acff 100644 --- a/packages/random/package.json +++ b/packages/random/package.json @@ -20,8 +20,8 @@ "@typescript-eslint/eslint-plugin": "~5.60.0", "@typescript-eslint/parser": "~5.60.0", "eslint": "~8.43.0", - "jest": "~29.5.0", - "jest-environment-jsdom": "~29.5.0", + "jest": "~29.6.1", + "jest-environment-jsdom": "~29.6.1", "ts-jest": "~29.0.5", "typescript": "~5.1.3" }, diff --git a/packages/rest-typings/package.json b/packages/rest-typings/package.json index 2c58dc3fd395..470e2c756aa7 100644 --- a/packages/rest-typings/package.json +++ b/packages/rest-typings/package.json @@ -3,10 +3,10 @@ "version": "6.2.9", "devDependencies": { "@rocket.chat/eslint-config": "workspace:^", - "@types/jest": "~29.5.2", + "@types/jest": "~29.5.3", "eslint": "~8.43.0", - "jest": "~29.5.0", - "jest-environment-jsdom": "~29.5.0", + "jest": "~29.6.1", + "jest-environment-jsdom": "~29.6.1", "mongodb": "^4.12.1", "ts-jest": "~29.0.5", "typescript": "~5.1.3" diff --git a/packages/server-fetch/package.json b/packages/server-fetch/package.json index 35c1a5a0747f..e79d03be02b0 100644 --- a/packages/server-fetch/package.json +++ b/packages/server-fetch/package.json @@ -3,9 +3,9 @@ "version": "0.0.1", "private": true, "devDependencies": { - "@types/jest": "^27.4.1", + "@types/jest": "~29.5.3", "eslint": "^8.43.0", - "jest": "~29.5.0", + "jest": "~29.6.1", "ts-jest": "~29.0.5", "typescript": "~5.1.3" }, diff --git a/packages/sha256/package.json b/packages/sha256/package.json index b57662c664c3..e8dd62ab8d1c 100644 --- a/packages/sha256/package.json +++ b/packages/sha256/package.json @@ -19,7 +19,7 @@ "@typescript-eslint/eslint-plugin": "~5.60.0", "@typescript-eslint/parser": "~5.60.0", "eslint": "~8.43.0", - "jest": "~29.5.0", + "jest": "~29.6.1", "ts-jest": "~29.0.5", "typescript": "~5.1.3" }, diff --git a/packages/tools/package.json b/packages/tools/package.json index 833a627023ef..50bca996be6a 100644 --- a/packages/tools/package.json +++ b/packages/tools/package.json @@ -3,9 +3,9 @@ "version": "0.0.1", "private": true, "devDependencies": { - "@types/jest": "~29.5.2", + "@types/jest": "~29.5.3", "eslint": "~8.43.0", - "jest": "~29.5.0", + "jest": "~29.6.1", "ts-jest": "~29.0.5", "typescript": "~5.1.3" }, diff --git a/packages/ui-client/jest.config.ts b/packages/ui-client/jest.config.ts new file mode 100644 index 000000000000..636d50c6a980 --- /dev/null +++ b/packages/ui-client/jest.config.ts @@ -0,0 +1,25 @@ +export default { + errorOnDeprecated: true, + + testEnvironment: 'jsdom', + modulePathIgnorePatterns: ['/dist/'], + testMatch: ['/src/**/**.spec.[jt]s?(x)'], + transform: { + '^.+\\.(t|j)sx?$': [ + '@swc/jest', + { + jsc: { + transform: { + react: { + runtime: 'automatic', + }, + }, + }, + }, + ], + }, + moduleNameMapper: { + '\\.css$': 'identity-obj-proxy', + '^react($|/.+)': '/../../node_modules/react$1', + }, +}; diff --git a/packages/ui-client/package.json b/packages/ui-client/package.json index 8fbb4d6acd21..25dca29e286a 100644 --- a/packages/ui-client/package.json +++ b/packages/ui-client/package.json @@ -8,6 +8,7 @@ "@rocket.chat/fuselage": "next", "@rocket.chat/fuselage-hooks": "next", "@rocket.chat/icons": "next", + "@rocket.chat/mock-providers": "workspace:^", "@rocket.chat/ui-contexts": "workspace:~", "@storybook/addon-actions": "~6.5.16", "@storybook/addon-docs": "~6.5.16", @@ -19,8 +20,11 @@ "@storybook/manager-webpack4": "~6.5.16", "@storybook/react": "~6.5.16", "@storybook/testing-library": "~0.0.13", + "@swc/jest": "^0.2.26", + "@testing-library/react": "^12.1.2", + "@testing-library/react-hooks": "^8.0.1", "@types/babel__core": "~7.20.1", - "@types/jest": "~29.5.2", + "@types/jest": "~29.5.3", "@types/react": "~17.0.62", "@types/react-dom": "~17.0.20", "eslint": "~8.43.0", @@ -29,8 +33,9 @@ "eslint-plugin-react-hooks": "~4.6.0", "eslint-plugin-storybook": "~0.6.12", "eslint-plugin-testing-library": "~5.11.0", - "jest": "~29.5.0", - "react": "~17.0.2", + "jest": "~29.6.1", + "react": "^17.0.2", + "react-dom": "^17.0.2", "react-hook-form": "^7.30.0", "ts-jest": "~29.0.5", "typescript": "~5.1.3" @@ -39,6 +44,7 @@ "lint": "eslint --ext .js,.jsx,.ts,.tsx .", "lint:fix": "eslint --ext .js,.jsx,.ts,.tsx . --fix", "test": "jest", + "testunit": "jest", "build": "rm -rf dist && tsc -p tsconfig-build.json", "storybook": "start-storybook -p 6006", "dev": "tsc -p tsconfig-build.json --watch --preserveWatchOutput" diff --git a/packages/ui-client/src/components/FeaturePreview/FeaturePreview.spec.tsx b/packages/ui-client/src/components/FeaturePreview/FeaturePreview.spec.tsx new file mode 100644 index 000000000000..11c218855feb --- /dev/null +++ b/packages/ui-client/src/components/FeaturePreview/FeaturePreview.spec.tsx @@ -0,0 +1,63 @@ +/* eslint-disable import/order */ +import { render, screen } from '@testing-library/react'; +import '@testing-library/jest-dom'; + +import { MockedSettingsContext } from '@rocket.chat/mock-providers/src/MockedSettingsContext'; +import { MockedUserContext } from '@rocket.chat/mock-providers/src/MockedUserContext'; +import { FeaturePreview, FeaturePreviewOff, FeaturePreviewOn } from './FeaturePreview'; + +test('should renders off if the feature is disabled', async () => { + render( + + on + off + , + { + wrapper: ({ children }) => ( + + + {children} + + + ), + }, + ); + + expect(screen.getByText('off')).toBeInTheDocument(); +}); + +test('should renders on if the feature is enabled', async () => { + render( + + on + off + , + { + wrapper: ({ children }) => ( + + + {children} + + + ), + }, + ); + + expect(screen.getByText('on')).toBeInTheDocument(); +}); diff --git a/packages/ui-client/src/components/FeaturePreview/FeaturePreview.tsx b/packages/ui-client/src/components/FeaturePreview/FeaturePreview.tsx new file mode 100644 index 000000000000..09ec0700cb79 --- /dev/null +++ b/packages/ui-client/src/components/FeaturePreview/FeaturePreview.tsx @@ -0,0 +1,26 @@ +/* eslint-disable react/no-multi-comp */ +import type { ReactElement, ReactNode } from 'react'; +import { Children, Suspense, cloneElement } from 'react'; + +import { useFeaturePreview } from '../../hooks/useFeaturePreview'; +import { FeaturesAvailable } from '../../hooks/useFeaturePreviewList'; + +export const FeaturePreview = ({ feature, children }: { feature: FeaturesAvailable; children: ReactElement[] }) => { + const featureToggleEnabled = useFeaturePreview(feature); + + const toggledChildren = Children.map(children, (child) => + cloneElement(child, { + featureToggleEnabled, + }), + ); + + return {toggledChildren}; +}; + +export const FeaturePreviewOn = ({ children, featureToggleEnabled }: { children: ReactNode; featureToggleEnabled?: boolean }) => ( + <>{featureToggleEnabled && children} +); + +export const FeaturePreviewOff = ({ children, featureToggleEnabled }: { children: ReactNode; featureToggleEnabled?: boolean }) => ( + <>{!featureToggleEnabled && children} +); diff --git a/apps/meteor/client/hooks/useFeaturePreview.spec.tsx b/packages/ui-client/src/hooks/useFeaturePreview.spec.tsx similarity index 63% rename from apps/meteor/client/hooks/useFeaturePreview.spec.tsx rename to packages/ui-client/src/hooks/useFeaturePreview.spec.tsx index 0d0cbc5eca52..3afd8fdb0392 100644 --- a/apps/meteor/client/hooks/useFeaturePreview.spec.tsx +++ b/packages/ui-client/src/hooks/useFeaturePreview.spec.tsx @@ -45,96 +45,79 @@ const settingContextValue: ContextType = { }; it('should return false if featurePreviewEnabled is false', () => { - const { result } = renderHook( - () => { - return useFeaturePreview('quickReactions'); - }, - { - wrapper: ({ children }) => ( - - {children} - - ), - }, - ); + const { result } = renderHook(() => useFeaturePreview('quickReactions'), { + wrapper: ({ children }) => ( + + {children} + + ), + }); expect(result.all[0]).toBe(false); }); it('should return false if featurePreviewEnabled is true but feature is not in userPreferences', () => { - const { result } = renderHook( - () => { - return useFeaturePreview('quickReactions'); - }, - { - wrapper: ({ children }) => ( - useFeaturePreview('quickReactions'), { + wrapper: ({ children }) => ( + + - - {children} - - - ), - }, - ); + {children} + + + ), + }); expect(result.all[0]).toBe(false); }); it('should return true if featurePreviewEnabled is true and feature is in userPreferences', () => { - const { result } = renderHook( - () => { - return useFeaturePreview('quickReactions'); - }, - { - wrapper: ({ children }) => ( - useFeaturePreview('quickReactions'), { + wrapper: ({ children }) => ( + + - - {children} - - - ), - }, - ); + {children} + + + ), + }); expect(result.all[0]).toBe(true); }); -const createUserContextValue = ({ userPreferences }: { userPreferences?: Record }): ContextType => { - return { - ...userContextValue, - ...(userPreferences && { queryPreference: (id) => [() => () => undefined, () => userPreferences[id as unknown as string] as any] }), - }; -}; +const createUserContextValue = ({ userPreferences }: { userPreferences?: Record }): ContextType => ({ + ...userContextValue, + ...(userPreferences && { queryPreference: (id) => [() => () => undefined, () => userPreferences[id as unknown as string] as any] }), +}); const createSettingContextValue = ({ settings }: { settings?: Record }): ContextType => { const cache = new Map(); @@ -162,9 +145,7 @@ export const MockedSettingsContext = ({ }: { children: React.ReactNode; settings?: Record; -}) => { - return {children}; -}; +}) => {children}; export const MockedUserContext = ({ userPreferences, @@ -172,6 +153,4 @@ export const MockedUserContext = ({ }: { children: React.ReactNode; userPreferences?: Record; -}) => { - return {children}; -}; +}) => {children}; diff --git a/apps/meteor/client/hooks/useFeaturePreview.ts b/packages/ui-client/src/hooks/useFeaturePreview.ts similarity index 100% rename from apps/meteor/client/hooks/useFeaturePreview.ts rename to packages/ui-client/src/hooks/useFeaturePreview.ts diff --git a/packages/ui-client/src/hooks/useFeaturePreviewList.spec.tsx b/packages/ui-client/src/hooks/useFeaturePreviewList.spec.tsx new file mode 100644 index 000000000000..5e1aacd7197b --- /dev/null +++ b/packages/ui-client/src/hooks/useFeaturePreviewList.spec.tsx @@ -0,0 +1,73 @@ +import { renderHook } from '@testing-library/react-hooks'; + +import { MockedSettingsContext, MockedUserContext } from './useFeaturePreview.spec'; +import { useFeaturePreviewList, defaultFeaturesPreview } from './useFeaturePreviewList'; + +it('should return the number of unseen features and Accounts_AllowFeaturePreview enabled ', () => { + const { result } = renderHook(() => useFeaturePreviewList(), { + wrapper: ({ children }) => ( + + {children} + + ), + }); + + expect(result.all[0]).toEqual( + expect.objectContaining({ + featurePreviewEnabled: true, + unseenFeatures: defaultFeaturesPreview.length, + }), + ); +}); + +it('should return the number of unseen features and Accounts_AllowFeaturePreview disabled ', () => { + const { result } = renderHook(() => useFeaturePreviewList(), { + wrapper: ({ children }) => ( + + {children} + + ), + }); + + expect(result.all[0]).toEqual( + expect.objectContaining({ + featurePreviewEnabled: false, + unseenFeatures: 0, + }), + ); +}); + +it('should return 0 unseen features', () => { + const { result } = renderHook(() => useFeaturePreviewList(), { + wrapper: ({ children }) => ( + + + {children} + + + ), + }); + + expect(result.all[0]).toEqual( + expect.objectContaining({ + featurePreviewEnabled: true, + unseenFeatures: 0, + }), + ); +}); diff --git a/apps/meteor/client/hooks/useFeaturePreviewList.ts b/packages/ui-client/src/hooks/useFeaturePreviewList.ts similarity index 100% rename from apps/meteor/client/hooks/useFeaturePreviewList.ts rename to packages/ui-client/src/hooks/useFeaturePreviewList.ts diff --git a/packages/ui-client/src/index.ts b/packages/ui-client/src/index.ts index 07635cbbc8e7..0e4454d38dbf 100644 --- a/packages/ui-client/src/index.ts +++ b/packages/ui-client/src/index.ts @@ -1 +1,3 @@ export * from './components'; +export * from './hooks/useFeaturePreview'; +export * from './hooks/useFeaturePreviewList'; diff --git a/packages/ui-client/tsconfig.json b/packages/ui-client/tsconfig.json index c7077d14134e..e2be47cf5499 100644 --- a/packages/ui-client/tsconfig.json +++ b/packages/ui-client/tsconfig.json @@ -2,8 +2,7 @@ "extends": "../../tsconfig.base.client.json", "compilerOptions": { "rootDir": "./src", - "outDir": "./dist", + "outDir": "./dist" }, - "include": ["./src/**/*"], - "exclude": ["./dist/**/*"], + "include": ["./src/**/*"] } diff --git a/packages/ui-composer/package.json b/packages/ui-composer/package.json index 74790595a41c..659b2b7af448 100644 --- a/packages/ui-composer/package.json +++ b/packages/ui-composer/package.json @@ -15,12 +15,12 @@ "@storybook/react": "~6.5.16", "@storybook/testing-library": "~0.0.13", "@types/babel__core": "~7.20.1", - "@types/jest": "~29.5.2", + "@types/jest": "~29.5.3", "eslint": "~8.43.0", "eslint-plugin-react": "~7.32.2", "eslint-plugin-react-hooks": "~4.6.0", "eslint-plugin-storybook": "~0.6.12", - "jest": "~29.5.0", + "jest": "~29.6.1", "react-docgen-typescript-plugin": "~1.0.5", "ts-jest": "~29.0.5", "typescript": "~5.1.3" diff --git a/packages/ui-contexts/package.json b/packages/ui-contexts/package.json index 11c7a722d974..94a12d58abd6 100644 --- a/packages/ui-contexts/package.json +++ b/packages/ui-contexts/package.json @@ -7,13 +7,13 @@ "@rocket.chat/emitter": "next", "@rocket.chat/fuselage-hooks": "next", "@rocket.chat/rest-typings": "workspace:^", - "@types/jest": "~29.5.2", + "@types/jest": "~29.5.3", "@types/react": "~17.0.62", "@types/react-dom": "~17.0.20", "@types/use-sync-external-store": "^0.0.3", "eslint": "~8.43.0", "eslint-plugin-react-hooks": "^4.6.0", - "jest": "~29.5.0", + "jest": "~29.6.1", "mongodb": "^4.12.1", "react": "~17.0.2", "ts-jest": "~29.0.5", diff --git a/packages/ui-video-conf/package.json b/packages/ui-video-conf/package.json index 0df5acf7d269..e60ad8140d77 100644 --- a/packages/ui-video-conf/package.json +++ b/packages/ui-video-conf/package.json @@ -19,12 +19,12 @@ "@storybook/react": "~6.5.16", "@storybook/testing-library": "~0.0.13", "@types/babel__core": "~7.20.1", - "@types/jest": "~29.5.2", + "@types/jest": "~29.5.3", "eslint": "~8.43.0", "eslint-plugin-react": "~7.32.2", "eslint-plugin-react-hooks": "~4.6.0", "eslint-plugin-storybook": "~0.6.12", - "jest": "~29.5.0", + "jest": "~29.6.1", "react-docgen-typescript-plugin": "~1.0.5", "ts-jest": "~29.0.5", "typescript": "~5.1.3" diff --git a/packages/uikit-playground/package.json b/packages/uikit-playground/package.json index 39933f5e16ed..e0dd396776e0 100644 --- a/packages/uikit-playground/package.json +++ b/packages/uikit-playground/package.json @@ -36,9 +36,9 @@ "use-subscription": "^1.8.0" }, "devDependencies": { - "@types/react": "^17.0.62", + "@types/react": "~17.0.62", "@types/react-beautiful-dnd": "^13.1.4", - "@types/react-dom": "^17.0.20", + "@types/react-dom": "~17.0.20", "@types/use-subscription": "^1.0.0", "@typescript-eslint/eslint-plugin": "~5.60.0", "@typescript-eslint/parser": "~5.60.0", diff --git a/packages/web-ui-registration/package.json b/packages/web-ui-registration/package.json index 2eadd13319c3..d5b37ffba90c 100644 --- a/packages/web-ui-registration/package.json +++ b/packages/web-ui-registration/package.json @@ -8,9 +8,9 @@ "@rocket.chat/ui-contexts": "workspace:^", "@tanstack/react-query": "^4.16.1", "@testing-library/react": "^13.3.0", - "@types/jest": "~29.5.2", + "@types/jest": "~29.5.3", "eslint": "~8.43.0", - "jest": "~29.5.0", + "jest": "~29.6.1", "react-hook-form": "^7.34.2", "ts-jest": "~29.0.5", "typescript": "~5.1.3" diff --git a/yarn.lock b/yarn.lock index 364eca70ff70..4ba47faae660 100644 --- a/yarn.lock +++ b/yarn.lock @@ -3938,7 +3938,7 @@ __metadata: languageName: node linkType: hard -"@babel/traverse@npm:^7.1.6, @babel/traverse@npm:^7.12.11, @babel/traverse@npm:^7.12.9, @babel/traverse@npm:^7.13.0, @babel/traverse@npm:^7.19.0, @babel/traverse@npm:^7.20.7, @babel/traverse@npm:^7.21.0, @babel/traverse@npm:^7.21.2, @babel/traverse@npm:^7.21.4, @babel/traverse@npm:^7.7.2": +"@babel/traverse@npm:^7.1.6, @babel/traverse@npm:^7.12.11, @babel/traverse@npm:^7.12.9, @babel/traverse@npm:^7.13.0, @babel/traverse@npm:^7.19.0, @babel/traverse@npm:^7.20.7, @babel/traverse@npm:^7.21.0, @babel/traverse@npm:^7.21.2, @babel/traverse@npm:^7.21.4": version: 7.21.4 resolution: "@babel/traverse@npm:7.21.4" dependencies: @@ -5319,20 +5319,6 @@ __metadata: languageName: node linkType: hard -"@jest/console@npm:^29.5.0": - version: 29.5.0 - resolution: "@jest/console@npm:29.5.0" - dependencies: - "@jest/types": ^29.5.0 - "@types/node": "*" - chalk: ^4.0.0 - jest-message-util: ^29.5.0 - jest-util: ^29.5.0 - slash: ^3.0.0 - checksum: 9f4f4b8fabd1221361b7f2e92d4a90f5f8c2e2b29077249996ab3c8b7f765175ffee795368f8d6b5b2bb3adb32dc09319f7270c7c787b0d259e624e00e0f64a5 - languageName: node - linkType: hard - "@jest/console@npm:^29.6.1": version: 29.6.1 resolution: "@jest/console@npm:29.6.1" @@ -5347,47 +5333,6 @@ __metadata: languageName: node linkType: hard -"@jest/core@npm:^29.5.0": - version: 29.5.0 - resolution: "@jest/core@npm:29.5.0" - dependencies: - "@jest/console": ^29.5.0 - "@jest/reporters": ^29.5.0 - "@jest/test-result": ^29.5.0 - "@jest/transform": ^29.5.0 - "@jest/types": ^29.5.0 - "@types/node": "*" - ansi-escapes: ^4.2.1 - chalk: ^4.0.0 - ci-info: ^3.2.0 - exit: ^0.1.2 - graceful-fs: ^4.2.9 - jest-changed-files: ^29.5.0 - jest-config: ^29.5.0 - jest-haste-map: ^29.5.0 - jest-message-util: ^29.5.0 - jest-regex-util: ^29.4.3 - jest-resolve: ^29.5.0 - jest-resolve-dependencies: ^29.5.0 - jest-runner: ^29.5.0 - jest-runtime: ^29.5.0 - jest-snapshot: ^29.5.0 - jest-util: ^29.5.0 - jest-validate: ^29.5.0 - jest-watcher: ^29.5.0 - micromatch: ^4.0.4 - pretty-format: ^29.5.0 - slash: ^3.0.0 - strip-ansi: ^6.0.0 - peerDependencies: - node-notifier: ^8.0.1 || ^9.0.0 || ^10.0.0 - peerDependenciesMeta: - node-notifier: - optional: true - checksum: 9e8f5243fe82d5a57f3971e1b96f320058df7c315328a3a827263f3b17f64be10c80f4a9c1b1773628b64d2de6d607c70b5b2d5bf13e7f5ad04223e9ef6aac06 - languageName: node - linkType: hard - "@jest/core@npm:^29.6.1": version: 29.6.1 resolution: "@jest/core@npm:29.6.1" @@ -5438,18 +5383,6 @@ __metadata: languageName: node linkType: hard -"@jest/environment@npm:^29.5.0": - version: 29.5.0 - resolution: "@jest/environment@npm:29.5.0" - dependencies: - "@jest/fake-timers": ^29.5.0 - "@jest/types": ^29.5.0 - "@types/node": "*" - jest-mock: ^29.5.0 - checksum: 921de6325cd4817dec6685e5ff299b499b6379f3f9cf489b4b13588ee1f3820a0c77b49e6a087996b6de8f629f6f5251e636cba08d1bdb97d8071cc7d033c88a - languageName: node - linkType: hard - "@jest/environment@npm:^29.6.1": version: 29.6.1 resolution: "@jest/environment@npm:29.6.1" @@ -5480,16 +5413,6 @@ __metadata: languageName: node linkType: hard -"@jest/expect@npm:^29.5.0": - version: 29.5.0 - resolution: "@jest/expect@npm:29.5.0" - dependencies: - expect: ^29.5.0 - jest-snapshot: ^29.5.0 - checksum: bd10e295111547e6339137107d83986ab48d46561525393834d7d2d8b2ae9d5626653f3f5e48e5c3fa742ac982e97bdf1f541b53b9e1d117a247b08e938527f6 - languageName: node - linkType: hard - "@jest/expect@npm:^29.6.1": version: 29.6.1 resolution: "@jest/expect@npm:29.6.1" @@ -5500,20 +5423,6 @@ __metadata: languageName: node linkType: hard -"@jest/fake-timers@npm:^29.5.0": - version: 29.5.0 - resolution: "@jest/fake-timers@npm:29.5.0" - dependencies: - "@jest/types": ^29.5.0 - "@sinonjs/fake-timers": ^10.0.2 - "@types/node": "*" - jest-message-util: ^29.5.0 - jest-mock: ^29.5.0 - jest-util: ^29.5.0 - checksum: 69930c6922341f244151ec0d27640852ec96237f730fc024da1f53143d31b43cde75d92f9d8e5937981cdce3b31416abc3a7090a0d22c2377512c4a6613244ee - languageName: node - linkType: hard - "@jest/fake-timers@npm:^29.6.1": version: 29.6.1 resolution: "@jest/fake-timers@npm:29.6.1" @@ -5528,18 +5437,6 @@ __metadata: languageName: node linkType: hard -"@jest/globals@npm:^29.5.0": - version: 29.5.0 - resolution: "@jest/globals@npm:29.5.0" - dependencies: - "@jest/environment": ^29.5.0 - "@jest/expect": ^29.5.0 - "@jest/types": ^29.5.0 - jest-mock: ^29.5.0 - checksum: b309ab8f21b571a7c672608682e84bbdd3d2b554ddf81e4e32617fec0a69094a290ab42e3c8b2c66ba891882bfb1b8b2736720ea1285b3ad646d55c2abefedd9 - languageName: node - linkType: hard - "@jest/globals@npm:^29.6.1": version: 29.6.1 resolution: "@jest/globals@npm:29.6.1" @@ -5552,43 +5449,6 @@ __metadata: languageName: node linkType: hard -"@jest/reporters@npm:^29.5.0": - version: 29.5.0 - resolution: "@jest/reporters@npm:29.5.0" - dependencies: - "@bcoe/v8-coverage": ^0.2.3 - "@jest/console": ^29.5.0 - "@jest/test-result": ^29.5.0 - "@jest/transform": ^29.5.0 - "@jest/types": ^29.5.0 - "@jridgewell/trace-mapping": ^0.3.15 - "@types/node": "*" - chalk: ^4.0.0 - collect-v8-coverage: ^1.0.0 - exit: ^0.1.2 - glob: ^7.1.3 - graceful-fs: ^4.2.9 - istanbul-lib-coverage: ^3.0.0 - istanbul-lib-instrument: ^5.1.0 - istanbul-lib-report: ^3.0.0 - istanbul-lib-source-maps: ^4.0.0 - istanbul-reports: ^3.1.3 - jest-message-util: ^29.5.0 - jest-util: ^29.5.0 - jest-worker: ^29.5.0 - slash: ^3.0.0 - string-length: ^4.0.1 - strip-ansi: ^6.0.0 - v8-to-istanbul: ^9.0.1 - peerDependencies: - node-notifier: ^8.0.1 || ^9.0.0 || ^10.0.0 - peerDependenciesMeta: - node-notifier: - optional: true - checksum: 481268aac9a4a75cc49c4df1273d6b111808dec815e9d009dad717c32383ebb0cebac76e820ad1ab44e207540e1c2fe1e640d44c4f262de92ab1933e057fdeeb - languageName: node - linkType: hard - "@jest/reporters@npm:^29.6.1": version: 29.6.1 resolution: "@jest/reporters@npm:29.6.1" @@ -5653,17 +5513,6 @@ __metadata: languageName: node linkType: hard -"@jest/source-map@npm:^29.4.3": - version: 29.4.3 - resolution: "@jest/source-map@npm:29.4.3" - dependencies: - "@jridgewell/trace-mapping": ^0.3.15 - callsites: ^3.0.0 - graceful-fs: ^4.2.9 - checksum: 2301d225145f8123540c0be073f35a80fd26a2f5e59550fd68525d8cea580fb896d12bf65106591ffb7366a8a19790076dbebc70e0f5e6ceb51f81827ed1f89c - languageName: node - linkType: hard - "@jest/source-map@npm:^29.6.0": version: 29.6.0 resolution: "@jest/source-map@npm:29.6.0" @@ -5675,18 +5524,6 @@ __metadata: languageName: node linkType: hard -"@jest/test-result@npm:^29.5.0": - version: 29.5.0 - resolution: "@jest/test-result@npm:29.5.0" - dependencies: - "@jest/console": ^29.5.0 - "@jest/types": ^29.5.0 - "@types/istanbul-lib-coverage": ^2.0.0 - collect-v8-coverage: ^1.0.0 - checksum: 2e8ff5242227ab960c520c3ea0f6544c595cc1c42fa3873c158e9f4f685f4ec9670ec08a4af94ae3885c0005a43550a9595191ffbc27a0965df27d9d98bbf901 - languageName: node - linkType: hard - "@jest/test-result@npm:^29.6.1": version: 29.6.1 resolution: "@jest/test-result@npm:29.6.1" @@ -5699,18 +5536,6 @@ __metadata: languageName: node linkType: hard -"@jest/test-sequencer@npm:^29.5.0": - version: 29.5.0 - resolution: "@jest/test-sequencer@npm:29.5.0" - dependencies: - "@jest/test-result": ^29.5.0 - graceful-fs: ^4.2.9 - jest-haste-map: ^29.5.0 - slash: ^3.0.0 - checksum: eca34b4aeb2fda6dfb7f9f4b064c858a7adf64ec5c6091b6f4ed9d3c19549177cbadcf1c615c4c182688fa1cf085c8c55c3ca6eea40719a34554b0bf071d842e - languageName: node - linkType: hard - "@jest/test-sequencer@npm:^29.6.1": version: 29.6.1 resolution: "@jest/test-sequencer@npm:29.6.1" @@ -9426,9 +9251,9 @@ __metadata: version: 0.0.0-use.local resolution: "@rocket.chat/account-utils@workspace:packages/account-utils" dependencies: - "@types/jest": ~29.5.2 + "@types/jest": ~29.5.3 eslint: ~8.43.0 - jest: ~29.5.0 + jest: ~29.6.1 ts-jest: ~29.0.5 typescript: ~5.1.3 languageName: unknown @@ -9439,13 +9264,13 @@ __metadata: resolution: "@rocket.chat/agenda@workspace:packages/agenda" dependencies: "@types/debug": ^4.1.8 - "@types/jest": ~29.5.2 + "@types/jest": ~29.5.3 cron: ~1.8.2 date.js: ~0.3.3 debug: ~4.1.1 eslint: ~8.43.0 human-interval: ^2.0.1 - jest: ~29.5.0 + jest: ~29.6.1 moment-timezone: ~0.5.43 mongodb: ^4.12.1 ts-jest: ~29.0.5 @@ -9461,11 +9286,11 @@ __metadata: "@rocket.chat/rest-typings": "workspace:^" "@swc/core": ^1.3.66 "@swc/jest": ^0.2.26 - "@types/jest": ~29.5.2 + "@types/jest": ~29.5.3 "@types/strict-uri-encode": ^2.0.0 eslint: ~8.43.0 filter-obj: ^3.0.0 - jest: ~29.5.0 + jest: ~29.6.1 jest-fetch-mock: ^3.0.3 query-string: ^7.1.3 split-on-first: ^3.0.0 @@ -9551,7 +9376,7 @@ __metadata: "@typescript-eslint/eslint-plugin": ~5.60.0 "@typescript-eslint/parser": ~5.60.0 eslint: ~8.43.0 - jest: ~29.5.0 + jest: ~29.6.1 ts-jest: ~29.0.5 typescript: ~5.1.3 languageName: unknown @@ -9561,10 +9386,10 @@ __metadata: version: 0.0.0-use.local resolution: "@rocket.chat/cas-validate@workspace:packages/cas-validate" dependencies: - "@types/jest": ~29.5.2 + "@types/jest": ~29.5.3 cheerio: 1.0.0-rc.10 eslint: ~8.43.0 - jest: ~29.5.0 + jest: ~29.6.1 ts-jest: ~29.0.5 typescript: ~5.1.3 languageName: unknown @@ -9588,11 +9413,11 @@ __metadata: "@types/babel__core": ^7 "@types/babel__preset-env": ^7 "@types/fibers": ^3.1.1 - "@types/jest": ^29.5.1 + "@types/jest": ~29.5.3 babel-jest: ^29.5.0 eslint: ~8.43.0 fibers: ^5.0.3 - jest: ^29.5.0 + jest: ~29.6.1 mongodb: ^4.12.1 prettier: ~2.8.8 typescript: ~5.1.3 @@ -9623,9 +9448,9 @@ __metadata: "@rocket.chat/core-typings": "workspace:^" "@rocket.chat/models": "workspace:^" "@rocket.chat/random": "workspace:^" - "@types/jest": ^27.4.1 + "@types/jest": ~29.5.3 eslint: ^8.43.0 - jest: ~29.5.0 + jest: ~29.6.1 mongodb: ^4.12.1 ts-jest: ~29.0.5 typescript: ~5.1.3 @@ -9675,11 +9500,11 @@ __metadata: "@rocket.chat/rest-typings": "workspace:^" "@swc/core": ^1.3.66 "@swc/jest": ^0.2.26 - "@types/jest": ^29.5.2 + "@types/jest": ~29.5.3 "@types/ws": ^8.5.5 eslint: ^8.43.0 - jest: ^29.5.0 - jest-environment-jsdom: ~29.5.0 + jest: ~29.6.1 + jest-environment-jsdom: ~29.6.1 jest-websocket-mock: ^2.4.0 typescript: ~5.1.3 ws: ^8.13.0 @@ -9979,7 +9804,7 @@ __metadata: "@swc/jest": ^0.2.26 "@testing-library/jest-dom": ^5.16.5 "@testing-library/react": ~12.1.5 - "@types/jest": ~29.5.2 + "@types/jest": ~29.5.3 "@types/katex": ~0.16.0 "@types/react": ~17.0.62 "@types/react-dom": ~17.0.20 @@ -9994,8 +9819,8 @@ __metadata: eslint-plugin-storybook: ~0.6.12 highlight.js: ^11.5.1 identity-obj-proxy: ^3.0.0 - jest: ~29.5.0 - jest-environment-jsdom: ~29.5.0 + jest: ~29.6.1 + jest-environment-jsdom: ~29.6.1 katex: ~0.16.7 outdent: ^0.8.0 react-docgen-typescript-plugin: ~1.0.5 @@ -10026,10 +9851,10 @@ __metadata: "@babel/preset-typescript": ~7.22.5 "@types/babel__core": ~7.20.1 "@types/babel__preset-env": ~7.9.2 - "@types/jest": ^29.5.2 + "@types/jest": ~29.5.3 babel-jest: ^29.5.0 eslint: ^8.43.0 - jest: ^29.5.0 + jest: ~29.6.1 ts-jest: ~29.0.5 tsup: ^6.7.0 typescript: ~5.1.3 @@ -10154,11 +9979,11 @@ __metadata: dependencies: "@types/chalk": ^2.2.0 "@types/ejson": ^2.2.0 - "@types/jest": ^27.4.1 + "@types/jest": ~29.5.3 chalk: ^4.0.0 ejson: ^2.2.3 eslint: ^8.43.0 - jest: ~29.5.0 + jest: ~29.6.1 ts-jest: ~29.0.5 typescript: ~5.1.3 languageName: unknown @@ -10256,6 +10081,7 @@ __metadata: "@rocket.chat/logo": next "@rocket.chat/memo": next "@rocket.chat/message-parser": next + "@rocket.chat/mock-providers": "workspace:^" "@rocket.chat/model-typings": "workspace:^" "@rocket.chat/models": "workspace:^" "@rocket.chat/mp3-encoder": 0.24.0 @@ -10454,7 +10280,7 @@ __metadata: imap: ^0.8.19 ip-range-check: ^0.2.0 is-svg: ^4.3.2 - jest: ^29.6.1 + jest: ~29.6.1 jquery: ^3.6.0 jschardet: ^3.0.0 jsdom: ^16.7.0 @@ -10569,15 +10395,15 @@ __metadata: languageName: unknown linkType: soft -"@rocket.chat/mock-providers@workspace:packages/mock-providers": +"@rocket.chat/mock-providers@workspace:^, @rocket.chat/mock-providers@workspace:packages/mock-providers": version: 0.0.0-use.local resolution: "@rocket.chat/mock-providers@workspace:packages/mock-providers" dependencies: "@rocket.chat/ui-contexts": "workspace:*" "@tanstack/react-query": ^4.16.1 - "@types/jest": ^27.4.1 + "@types/jest": ~29.5.3 eslint: ^8.12.0 - jest: ~29.5.0 + jest: ~29.6.1 react: ~17.0.2 ts-jest: ~29.0.5 typescript: ~5.0.2 @@ -10592,10 +10418,10 @@ __metadata: resolution: "@rocket.chat/model-typings@workspace:packages/model-typings" dependencies: "@rocket.chat/core-typings": "workspace:^" - "@types/jest": ~29.5.2 + "@types/jest": ~29.5.3 "@types/node-rsa": ^1.1.1 eslint: ~8.43.0 - jest: ~29.5.0 + jest: ~29.6.1 mongodb: ^4.12.1 ts-jest: ~29.0.5 typescript: ~5.1.3 @@ -10607,9 +10433,9 @@ __metadata: resolution: "@rocket.chat/models@workspace:packages/models" dependencies: "@rocket.chat/model-typings": "workspace:^" - "@types/jest": ~29.5.2 + "@types/jest": ~29.5.3 eslint: ~8.43.0 - jest: ~29.5.0 + jest: ~29.6.1 ts-jest: ~29.0.5 typescript: ~5.1.3 languageName: unknown @@ -10638,14 +10464,14 @@ __metadata: "@rocket.chat/rest-typings": "workspace:^" "@rocket.chat/string-helpers": next "@rocket.chat/tools": "workspace:^" - "@types/jest": ~29.5.2 + "@types/jest": ~29.5.3 "@types/node": ^14.18.51 ejson: ^2.2.3 emoji-toolkit: ^7.0.1 eslint: ~8.43.0 eventemitter3: ^4.0.7 fibers: ^5.0.3 - jest: ~29.5.0 + jest: ~29.6.1 mem: ^8.1.1 moment-timezone: ^0.5.43 mongo-message-queue: ^1.0.0 @@ -10725,15 +10551,15 @@ __metadata: "@testing-library/jest-dom": ^5.16.5 "@testing-library/react": ~13.4.0 "@types/emojione": ^2.2.6 - "@types/jest": ~29.5.2 + "@types/jest": ~29.5.3 "@types/react": ~17.0.62 - "@types/react-dom": ^18.2.5 + "@types/react-dom": ~17.0.20 "@types/testing-library__jest-dom": ~5.14.6 emoji-assets: ^7.0.1 emoji-toolkit: ^7.0.1 eslint: ~8.43.0 - jest: ~29.5.0 - jest-environment-jsdom: ~29.5.0 + jest: ~29.6.1 + jest-environment-jsdom: ~29.6.1 moment: ^2.29.4 moment-timezone: ^0.5.43 react: ^18.2.0 @@ -10747,9 +10573,9 @@ __metadata: version: 0.0.0-use.local resolution: "@rocket.chat/poplib@workspace:packages/node-poplib" dependencies: - "@types/jest": ~29.5.2 + "@types/jest": ~29.5.3 eslint: ~8.43.0 - jest: ~29.5.0 + jest: ~29.6.1 ts-jest: ~29.0.5 typescript: ~5.1.3 languageName: unknown @@ -10801,7 +10627,7 @@ __metadata: "@types/node": ^14.18.51 babel-jest: ^29.0.3 eslint: ~8.43.0 - jest: ~29.5.0 + jest: ~29.6.1 mongodb: ^4.12.1 typescript: ~5.1.3 languageName: unknown @@ -10859,8 +10685,8 @@ __metadata: "@typescript-eslint/eslint-plugin": ~5.60.0 "@typescript-eslint/parser": ~5.60.0 eslint: ~8.43.0 - jest: ~29.5.0 - jest-environment-jsdom: ~29.5.0 + jest: ~29.6.1 + jest-environment-jsdom: ~29.6.1 ts-jest: ~29.0.5 typescript: ~5.1.3 languageName: unknown @@ -10896,12 +10722,12 @@ __metadata: "@rocket.chat/eslint-config": "workspace:^" "@rocket.chat/message-parser": next "@rocket.chat/ui-kit": next - "@types/jest": ~29.5.2 + "@types/jest": ~29.5.3 ajv: ^8.11.0 ajv-formats: ^2.1.1 eslint: ~8.43.0 - jest: ~29.5.0 - jest-environment-jsdom: ~29.5.0 + jest: ~29.6.1 + jest-environment-jsdom: ~29.6.1 mongodb: ^4.12.1 ts-jest: ~29.0.5 typescript: ~5.1.3 @@ -10925,12 +10751,12 @@ __metadata: version: 0.0.0-use.local resolution: "@rocket.chat/server-fetch@workspace:packages/server-fetch" dependencies: - "@types/jest": ^27.4.1 + "@types/jest": ~29.5.3 "@types/proxy-from-env": ^1.0.1 eslint: ^8.43.0 http-proxy-agent: ^5.0.0 https-proxy-agent: ^5.0.1 - jest: ~29.5.0 + jest: ~29.6.1 node-fetch: 2.3.0 proxy-from-env: ^1.1.0 ts-jest: ~29.0.5 @@ -10948,7 +10774,7 @@ __metadata: "@typescript-eslint/eslint-plugin": ~5.60.0 "@typescript-eslint/parser": ~5.60.0 eslint: ~8.43.0 - jest: ~29.5.0 + jest: ~29.6.1 ts-jest: ~29.0.5 typescript: ~5.1.3 languageName: unknown @@ -11025,9 +10851,9 @@ __metadata: version: 0.0.0-use.local resolution: "@rocket.chat/tools@workspace:packages/tools" dependencies: - "@types/jest": ~29.5.2 + "@types/jest": ~29.5.3 eslint: ~8.43.0 - jest: ~29.5.0 + jest: ~29.6.1 moment-timezone: ^0.5.43 ts-jest: ~29.0.5 typescript: ~5.1.3 @@ -11043,6 +10869,7 @@ __metadata: "@rocket.chat/fuselage": next "@rocket.chat/fuselage-hooks": next "@rocket.chat/icons": next + "@rocket.chat/mock-providers": "workspace:^" "@rocket.chat/ui-contexts": "workspace:~" "@storybook/addon-actions": ~6.5.16 "@storybook/addon-docs": ~6.5.16 @@ -11054,8 +10881,11 @@ __metadata: "@storybook/manager-webpack4": ~6.5.16 "@storybook/react": ~6.5.16 "@storybook/testing-library": ~0.0.13 + "@swc/jest": ^0.2.26 + "@testing-library/react": ^12.1.2 + "@testing-library/react-hooks": ^8.0.1 "@types/babel__core": ~7.20.1 - "@types/jest": ~29.5.2 + "@types/jest": ~29.5.3 "@types/react": ~17.0.62 "@types/react-dom": ~17.0.20 eslint: ~8.43.0 @@ -11064,8 +10894,9 @@ __metadata: eslint-plugin-react-hooks: ~4.6.0 eslint-plugin-storybook: ~0.6.12 eslint-plugin-testing-library: ~5.11.0 - jest: ~29.5.0 - react: ~17.0.2 + jest: ~29.6.1 + react: ^17.0.2 + react-dom: ^17.0.2 react-hook-form: ^7.30.0 ts-jest: ~29.0.5 typescript: ~5.1.3 @@ -11095,12 +10926,12 @@ __metadata: "@storybook/react": ~6.5.16 "@storybook/testing-library": ~0.0.13 "@types/babel__core": ~7.20.1 - "@types/jest": ~29.5.2 + "@types/jest": ~29.5.3 eslint: ~8.43.0 eslint-plugin-react: ~7.32.2 eslint-plugin-react-hooks: ~4.6.0 eslint-plugin-storybook: ~0.6.12 - jest: ~29.5.0 + jest: ~29.6.1 react-docgen-typescript-plugin: ~1.0.5 ts-jest: ~29.0.5 typescript: ~5.1.3 @@ -11120,13 +10951,13 @@ __metadata: "@rocket.chat/emitter": next "@rocket.chat/fuselage-hooks": next "@rocket.chat/rest-typings": "workspace:^" - "@types/jest": ~29.5.2 + "@types/jest": ~29.5.3 "@types/react": ~17.0.62 "@types/react-dom": ~17.0.20 "@types/use-sync-external-store": ^0.0.3 eslint: ~8.43.0 eslint-plugin-react-hooks: ^4.6.0 - jest: ~29.5.0 + jest: ~29.6.1 mongodb: ^4.12.1 react: ~17.0.2 ts-jest: ~29.0.5 @@ -11171,14 +11002,14 @@ __metadata: "@storybook/manager-webpack4": ~6.5.16 "@storybook/react": ~6.5.16 "@storybook/testing-library": ~0.0.13 - "@types/jest": ~29.5.2 + "@types/jest": ~29.5.3 "@types/react": ~17.0.62 eslint: ~8.43.0 eslint-plugin-anti-trojan-source: ~1.1.1 eslint-plugin-react: ~7.32.2 eslint-plugin-react-hooks: ~4.6.0 eslint-plugin-testing-library: ^5.11.0 - jest: ~29.5.0 + jest: ~29.6.1 react: ~17.0.2 react-docgen-typescript-plugin: ~1.0.5 ts-jest: ~29.0.5 @@ -11213,12 +11044,12 @@ __metadata: "@storybook/react": ~6.5.16 "@storybook/testing-library": ~0.0.13 "@types/babel__core": ~7.20.1 - "@types/jest": ~29.5.2 + "@types/jest": ~29.5.3 eslint: ~8.43.0 eslint-plugin-react: ~7.32.2 eslint-plugin-react-hooks: ~4.6.0 eslint-plugin-storybook: ~0.6.12 - jest: ~29.5.0 + jest: ~29.6.1 react-docgen-typescript-plugin: ~1.0.5 ts-jest: ~29.0.5 typescript: ~5.1.3 @@ -11252,9 +11083,9 @@ __metadata: "@rocket.chat/logo": next "@rocket.chat/styled": next "@rocket.chat/ui-contexts": "workspace:~" - "@types/react": ^17.0.62 + "@types/react": ~17.0.62 "@types/react-beautiful-dnd": ^13.1.4 - "@types/react-dom": ^17.0.20 + "@types/react-dom": ~17.0.20 "@types/use-subscription": ^1.0.0 "@typescript-eslint/eslint-plugin": ~5.60.0 "@typescript-eslint/parser": ~5.60.0 @@ -11286,9 +11117,9 @@ __metadata: "@rocket.chat/ui-contexts": "workspace:^" "@tanstack/react-query": ^4.16.1 "@testing-library/react": ^13.3.0 - "@types/jest": ~29.5.2 + "@types/jest": ~29.5.3 eslint: ~8.43.0 - jest: ~29.5.0 + jest: ~29.6.1 react-hook-form: ^7.34.2 ts-jest: ~29.0.5 typescript: ~5.1.3 @@ -13024,6 +12855,20 @@ __metadata: languageName: node linkType: hard +"@testing-library/react@npm:^12.1.2, @testing-library/react@npm:~12.1.5": + version: 12.1.5 + resolution: "@testing-library/react@npm:12.1.5" + dependencies: + "@babel/runtime": ^7.12.5 + "@testing-library/dom": ^8.0.0 + "@types/react-dom": <18.0.0 + peerDependencies: + react: <18.0.0 + react-dom: <18.0.0 + checksum: 4abd0490405e709a7df584a0db604e508a4612398bb1326e8fa32dd9393b15badc826dcf6d2f7525437886d507871f719f127b9860ed69ddd204d1fa834f576a + languageName: node + linkType: hard + "@testing-library/react@npm:^13.3.0, @testing-library/react@npm:~13.4.0": version: 13.4.0 resolution: "@testing-library/react@npm:13.4.0" @@ -13038,20 +12883,6 @@ __metadata: languageName: node linkType: hard -"@testing-library/react@npm:~12.1.5": - version: 12.1.5 - resolution: "@testing-library/react@npm:12.1.5" - dependencies: - "@babel/runtime": ^7.12.5 - "@testing-library/dom": ^8.0.0 - "@types/react-dom": <18.0.0 - peerDependencies: - react: <18.0.0 - react-dom: <18.0.0 - checksum: 4abd0490405e709a7df584a0db604e508a4612398bb1326e8fa32dd9393b15badc826dcf6d2f7525437886d507871f719f127b9860ed69ddd204d1fa834f576a - languageName: node - linkType: hard - "@testing-library/user-event@npm:^13.2.1, @testing-library/user-event@npm:~13.5.0": version: 13.5.0 resolution: "@testing-library/user-event@npm:13.5.0" @@ -13714,23 +13545,13 @@ __metadata: languageName: node linkType: hard -"@types/jest@npm:^27.4.1": - version: 27.5.2 - resolution: "@types/jest@npm:27.5.2" - dependencies: - jest-matcher-utils: ^27.0.0 - pretty-format: ^27.0.0 - checksum: 7e11c6826aa429ad990dc262e4e4b54aa36573287fddf15773e4137f07d11d3105f0dd9f1baff73252160a057df23f5529bb83b1bf83cd3f45f9460a5ca5c22e - languageName: node - linkType: hard - -"@types/jest@npm:^29.5.1, @types/jest@npm:^29.5.2, @types/jest@npm:~29.5.2": - version: 29.5.2 - resolution: "@types/jest@npm:29.5.2" +"@types/jest@npm:~29.5.3": + version: 29.5.3 + resolution: "@types/jest@npm:29.5.3" dependencies: expect: ^29.0.0 pretty-format: ^29.0.0 - checksum: 7d205599ea3cccc262bad5cc173d3242d6bf8138c99458509230e4ecef07a52d6ddcde5a1dbd49ace655c0af51d2dbadef3748697292ea4d86da19d9e03e19c0 + checksum: e36bb92e0b9e5ea7d6f8832baa42f087fc1697f6cd30ec309a07ea4c268e06ec460f1f0cfd2581daf5eff5763475190ec1ad8ac6520c49ccfe4f5c0a48bfa676 languageName: node linkType: hard @@ -14254,15 +14075,6 @@ __metadata: languageName: node linkType: hard -"@types/react-dom@npm:^17.0.20, @types/react-dom@npm:~17.0.20": - version: 17.0.20 - resolution: "@types/react-dom@npm:17.0.20" - dependencies: - "@types/react": ^17 - checksum: 525439fb14a033fc5dbe74711ecc50ec82273a528df9656594066a6219401e975101dafffd15d9a1a57a9442d52ea0c92eaacae09554dde27cd792e773f67467 - languageName: node - linkType: hard - "@types/react-dom@npm:^18.0.0": version: 18.0.10 resolution: "@types/react-dom@npm:18.0.10" @@ -14272,12 +14084,12 @@ __metadata: languageName: node linkType: hard -"@types/react-dom@npm:^18.2.5": - version: 18.2.5 - resolution: "@types/react-dom@npm:18.2.5" +"@types/react-dom@npm:~17.0.20": + version: 17.0.20 + resolution: "@types/react-dom@npm:17.0.20" dependencies: - "@types/react": "*" - checksum: c48209f8c60cb9054f3deee5365bc9fd6dadd8f901b67f1612a334057b2671518fc5145f14aca63ff276a926ccb5358308a6cf58ec700178f382bb3ebde96d91 + "@types/react": ^17 + checksum: 525439fb14a033fc5dbe74711ecc50ec82273a528df9656594066a6219401e975101dafffd15d9a1a57a9442d52ea0c92eaacae09554dde27cd792e773f67467 languageName: node linkType: hard @@ -14304,7 +14116,7 @@ __metadata: languageName: node linkType: hard -"@types/react@npm:^17.0.62, @types/react@npm:~17.0.62": +"@types/react@npm:~17.0.62": version: 17.0.62 resolution: "@types/react@npm:17.0.62" dependencies: @@ -20980,13 +20792,6 @@ __metadata: languageName: node linkType: hard -"diff-sequences@npm:^27.5.1": - version: 27.5.1 - resolution: "diff-sequences@npm:27.5.1" - checksum: a00db5554c9da7da225db2d2638d85f8e41124eccbd56cbaefb3b276dcbb1c1c2ad851c32defe2055a54a4806f030656cbf6638105fd6ce97bb87b90b32a33ca - languageName: node - linkType: hard - "diff-sequences@npm:^28.1.1": version: 28.1.1 resolution: "diff-sequences@npm:28.1.1" @@ -22899,7 +22704,7 @@ __metadata: languageName: node linkType: hard -"expect@npm:^29.0.0, expect@npm:^29.5.0": +"expect@npm:^29.0.0": version: 29.5.0 resolution: "expect@npm:29.5.0" dependencies: @@ -27451,34 +27256,6 @@ __metadata: languageName: node linkType: hard -"jest-circus@npm:^29.5.0": - version: 29.5.0 - resolution: "jest-circus@npm:29.5.0" - dependencies: - "@jest/environment": ^29.5.0 - "@jest/expect": ^29.5.0 - "@jest/test-result": ^29.5.0 - "@jest/types": ^29.5.0 - "@types/node": "*" - chalk: ^4.0.0 - co: ^4.6.0 - dedent: ^0.7.0 - is-generator-fn: ^2.0.0 - jest-each: ^29.5.0 - jest-matcher-utils: ^29.5.0 - jest-message-util: ^29.5.0 - jest-runtime: ^29.5.0 - jest-snapshot: ^29.5.0 - jest-util: ^29.5.0 - p-limit: ^3.1.0 - pretty-format: ^29.5.0 - pure-rand: ^6.0.0 - slash: ^3.0.0 - stack-utils: ^2.0.3 - checksum: 44ff5d06acedae6de6c866e20e3b61f83e29ab94cf9f960826e7e667de49c12dd9ab9dffd7fa3b7d1f9688a8b5bfb1ebebadbea69d9ed0d3f66af4a0ff8c2b27 - languageName: node - linkType: hard - "jest-circus@npm:^29.6.1": version: 29.6.1 resolution: "jest-circus@npm:29.6.1" @@ -27507,33 +27284,6 @@ __metadata: languageName: node linkType: hard -"jest-cli@npm:^29.5.0": - version: 29.5.0 - resolution: "jest-cli@npm:29.5.0" - dependencies: - "@jest/core": ^29.5.0 - "@jest/test-result": ^29.5.0 - "@jest/types": ^29.5.0 - chalk: ^4.0.0 - exit: ^0.1.2 - graceful-fs: ^4.2.9 - import-local: ^3.0.2 - jest-config: ^29.5.0 - jest-util: ^29.5.0 - jest-validate: ^29.5.0 - prompts: ^2.0.1 - yargs: ^17.3.1 - peerDependencies: - node-notifier: ^8.0.1 || ^9.0.0 || ^10.0.0 - peerDependenciesMeta: - node-notifier: - optional: true - bin: - jest: bin/jest.js - checksum: 39897bbbc0f0d8a6b975ab12fd13887eaa28d92e3dee9e0173a5cb913ae8cc2ae46e090d38c6d723e84d9d6724429cd08685b4e505fa447d31ca615630c7dbba - languageName: node - linkType: hard - "jest-cli@npm:^29.6.1": version: 29.6.1 resolution: "jest-cli@npm:29.6.1" @@ -27561,44 +27311,6 @@ __metadata: languageName: node linkType: hard -"jest-config@npm:^29.5.0": - version: 29.5.0 - resolution: "jest-config@npm:29.5.0" - dependencies: - "@babel/core": ^7.11.6 - "@jest/test-sequencer": ^29.5.0 - "@jest/types": ^29.5.0 - babel-jest: ^29.5.0 - chalk: ^4.0.0 - ci-info: ^3.2.0 - deepmerge: ^4.2.2 - glob: ^7.1.3 - graceful-fs: ^4.2.9 - jest-circus: ^29.5.0 - jest-environment-node: ^29.5.0 - jest-get-type: ^29.4.3 - jest-regex-util: ^29.4.3 - jest-resolve: ^29.5.0 - jest-runner: ^29.5.0 - jest-util: ^29.5.0 - jest-validate: ^29.5.0 - micromatch: ^4.0.4 - parse-json: ^5.2.0 - pretty-format: ^29.5.0 - slash: ^3.0.0 - strip-json-comments: ^3.1.1 - peerDependencies: - "@types/node": "*" - ts-node: ">=9.0.0" - peerDependenciesMeta: - "@types/node": - optional: true - ts-node: - optional: true - checksum: c37c4dab964c54ab293d4e302d40b09687037ac9d00b88348ec42366970747feeaf265e12e3750cd3660b40c518d4031335eda11ac10b70b10e60797ebbd4b9c - languageName: node - linkType: hard - "jest-config@npm:^29.6.1": version: 29.6.1 resolution: "jest-config@npm:29.6.1" @@ -27637,18 +27349,6 @@ __metadata: languageName: node linkType: hard -"jest-diff@npm:^27.5.1": - version: 27.5.1 - resolution: "jest-diff@npm:27.5.1" - dependencies: - chalk: ^4.0.0 - diff-sequences: ^27.5.1 - jest-get-type: ^27.5.1 - pretty-format: ^27.5.1 - checksum: 8be27c1e1ee57b2bb2bef9c0b233c19621b4c43d53a3c26e2c00a4e805eb4ea11fe1694a06a9fb0e80ffdcfdc0d2b1cb0b85920b3f5c892327ecd1e7bd96b865 - languageName: node - linkType: hard - "jest-diff@npm:^28.0.2": version: 28.1.3 resolution: "jest-diff@npm:28.1.3" @@ -27694,19 +27394,6 @@ __metadata: languageName: node linkType: hard -"jest-each@npm:^29.5.0": - version: 29.5.0 - resolution: "jest-each@npm:29.5.0" - dependencies: - "@jest/types": ^29.5.0 - chalk: ^4.0.0 - jest-get-type: ^29.4.3 - jest-util: ^29.5.0 - pretty-format: ^29.5.0 - checksum: b8b297534d25834c5d4e31e4c687359787b1e402519e42664eb704cc3a12a7a91a017565a75acb02e8cf9afd3f4eef3350bd785276bec0900184641b765ff7a5 - languageName: node - linkType: hard - "jest-each@npm:^29.6.1": version: 29.6.1 resolution: "jest-each@npm:29.6.1" @@ -27720,38 +27407,24 @@ __metadata: languageName: node linkType: hard -"jest-environment-jsdom@npm:~29.5.0": - version: 29.5.0 - resolution: "jest-environment-jsdom@npm:29.5.0" +"jest-environment-jsdom@npm:~29.6.1": + version: 29.6.1 + resolution: "jest-environment-jsdom@npm:29.6.1" dependencies: - "@jest/environment": ^29.5.0 - "@jest/fake-timers": ^29.5.0 - "@jest/types": ^29.5.0 + "@jest/environment": ^29.6.1 + "@jest/fake-timers": ^29.6.1 + "@jest/types": ^29.6.1 "@types/jsdom": ^20.0.0 "@types/node": "*" - jest-mock: ^29.5.0 - jest-util: ^29.5.0 + jest-mock: ^29.6.1 + jest-util: ^29.6.1 jsdom: ^20.0.0 peerDependencies: canvas: ^2.5.0 peerDependenciesMeta: canvas: optional: true - checksum: 3df7fc85275711f20b483ac8cd8c04500704ed0f69791eb55c574b38f5a39470f03d775cf20c1025bc1884916ac0573aa2fa4db1bb74225bc7fdd95ba97ad0da - languageName: node - linkType: hard - -"jest-environment-node@npm:^29.5.0": - version: 29.5.0 - resolution: "jest-environment-node@npm:29.5.0" - dependencies: - "@jest/environment": ^29.5.0 - "@jest/fake-timers": ^29.5.0 - "@jest/types": ^29.5.0 - "@types/node": "*" - jest-mock: ^29.5.0 - jest-util: ^29.5.0 - checksum: 57981911cc20a4219b0da9e22b2e3c9f31b505e43f78e61c899e3227ded455ce1a3a9483842c69cfa4532f02cfb536ae0995bf245f9211608edacfc1e478d411 + checksum: e8a9bff00a011235b004699f34bc85b18fdac82049513410cbf2dc1c2dd332bc1b4f108976412df1d29f2fa8bf0360aaf84eb0f5b4db1db2fb7fc7155dc14be7 languageName: node linkType: hard @@ -27779,13 +27452,6 @@ __metadata: languageName: node linkType: hard -"jest-get-type@npm:^27.5.1": - version: 27.5.1 - resolution: "jest-get-type@npm:27.5.1" - checksum: 63064ab70195c21007d897c1157bf88ff94a790824a10f8c890392e7d17eda9c3900513cb291ca1c8d5722cad79169764e9a1279f7c8a9c4cd6e9109ff04bbc0 - languageName: node - linkType: hard - "jest-get-type@npm:^28.0.2": version: 28.0.2 resolution: "jest-get-type@npm:28.0.2" @@ -27871,16 +27537,6 @@ __metadata: languageName: node linkType: hard -"jest-leak-detector@npm:^29.5.0": - version: 29.5.0 - resolution: "jest-leak-detector@npm:29.5.0" - dependencies: - jest-get-type: ^29.4.3 - pretty-format: ^29.5.0 - checksum: 0fb845da7ac9cdfc9b3b2e35f6f623a41c547d7dc0103ceb0349013459d00de5870b5689a625e7e37f9644934b40e8f1dcdd5422d14d57470600350364676313 - languageName: node - linkType: hard - "jest-leak-detector@npm:^29.6.1": version: 29.6.1 resolution: "jest-leak-detector@npm:29.6.1" @@ -27891,18 +27547,6 @@ __metadata: languageName: node linkType: hard -"jest-matcher-utils@npm:^27.0.0": - version: 27.5.1 - resolution: "jest-matcher-utils@npm:27.5.1" - dependencies: - chalk: ^4.0.0 - jest-diff: ^27.5.1 - jest-get-type: ^27.5.1 - pretty-format: ^27.5.1 - checksum: bb2135fc48889ff3fe73888f6cc7168ddab9de28b51b3148f820c89fdfd2effdcad005f18be67d0b9be80eda208ad47290f62f03d0a33f848db2dd0273c8217a - languageName: node - linkType: hard - "jest-matcher-utils@npm:^29.5.0": version: 29.5.0 resolution: "jest-matcher-utils@npm:29.5.0" @@ -27971,17 +27615,6 @@ __metadata: languageName: node linkType: hard -"jest-mock@npm:^29.5.0": - version: 29.5.0 - resolution: "jest-mock@npm:29.5.0" - dependencies: - "@jest/types": ^29.5.0 - "@types/node": "*" - jest-util: ^29.5.0 - checksum: 2a9cf07509948fa8608898c445f04fe4dd6e2049ff431e5531eee028c808d3ba3c67f226ac87b0cf383feaa1055776900d197c895e89783016886ac17a4ff10c - languageName: node - linkType: hard - "jest-mock@npm:^29.6.1": version: 29.6.1 resolution: "jest-mock@npm:29.6.1" @@ -28019,16 +27652,6 @@ __metadata: languageName: node linkType: hard -"jest-resolve-dependencies@npm:^29.5.0": - version: 29.5.0 - resolution: "jest-resolve-dependencies@npm:29.5.0" - dependencies: - jest-regex-util: ^29.4.3 - jest-snapshot: ^29.5.0 - checksum: 479d2e5365d58fe23f2b87001e2e0adcbffe0147700e85abdec8f14b9703b0a55758c1929a9989e3f5d5e954fb88870ea4bfa04783523b664562fcf5f10b0edf - languageName: node - linkType: hard - "jest-resolve-dependencies@npm:^29.6.1": version: 29.6.1 resolution: "jest-resolve-dependencies@npm:29.6.1" @@ -28039,23 +27662,6 @@ __metadata: languageName: node linkType: hard -"jest-resolve@npm:^29.5.0": - version: 29.5.0 - resolution: "jest-resolve@npm:29.5.0" - dependencies: - chalk: ^4.0.0 - graceful-fs: ^4.2.9 - jest-haste-map: ^29.5.0 - jest-pnp-resolver: ^1.2.2 - jest-util: ^29.5.0 - jest-validate: ^29.5.0 - resolve: ^1.20.0 - resolve.exports: ^2.0.0 - slash: ^3.0.0 - checksum: 9a125f3cf323ceef512089339d35f3ee37f79fe16a831fb6a26773ea6a229b9e490d108fec7af334142e91845b5996de8e7cdd85a4d8d617078737d804e29c8f - languageName: node - linkType: hard - "jest-resolve@npm:^29.6.1": version: 29.6.1 resolution: "jest-resolve@npm:29.6.1" @@ -28073,35 +27679,6 @@ __metadata: languageName: node linkType: hard -"jest-runner@npm:^29.5.0": - version: 29.5.0 - resolution: "jest-runner@npm:29.5.0" - dependencies: - "@jest/console": ^29.5.0 - "@jest/environment": ^29.5.0 - "@jest/test-result": ^29.5.0 - "@jest/transform": ^29.5.0 - "@jest/types": ^29.5.0 - "@types/node": "*" - chalk: ^4.0.0 - emittery: ^0.13.1 - graceful-fs: ^4.2.9 - jest-docblock: ^29.4.3 - jest-environment-node: ^29.5.0 - jest-haste-map: ^29.5.0 - jest-leak-detector: ^29.5.0 - jest-message-util: ^29.5.0 - jest-resolve: ^29.5.0 - jest-runtime: ^29.5.0 - jest-util: ^29.5.0 - jest-watcher: ^29.5.0 - jest-worker: ^29.5.0 - p-limit: ^3.1.0 - source-map-support: 0.5.13 - checksum: 437dea69c5dddca22032259787bac74790d5a171c9d804711415f31e5d1abfb64fa52f54a9015bb17a12b858fd0cf3f75ef6f3c9e94255a8596e179f707229c4 - languageName: node - linkType: hard - "jest-runner@npm:^29.6.1": version: 29.6.1 resolution: "jest-runner@npm:29.6.1" @@ -28131,36 +27708,6 @@ __metadata: languageName: node linkType: hard -"jest-runtime@npm:^29.5.0": - version: 29.5.0 - resolution: "jest-runtime@npm:29.5.0" - dependencies: - "@jest/environment": ^29.5.0 - "@jest/fake-timers": ^29.5.0 - "@jest/globals": ^29.5.0 - "@jest/source-map": ^29.4.3 - "@jest/test-result": ^29.5.0 - "@jest/transform": ^29.5.0 - "@jest/types": ^29.5.0 - "@types/node": "*" - chalk: ^4.0.0 - cjs-module-lexer: ^1.0.0 - collect-v8-coverage: ^1.0.0 - glob: ^7.1.3 - graceful-fs: ^4.2.9 - jest-haste-map: ^29.5.0 - jest-message-util: ^29.5.0 - jest-mock: ^29.5.0 - jest-regex-util: ^29.4.3 - jest-resolve: ^29.5.0 - jest-snapshot: ^29.5.0 - jest-util: ^29.5.0 - slash: ^3.0.0 - strip-bom: ^4.0.0 - checksum: 7af27bd9d54cf1c5735404cf8d76c6509d5610b1ec0106a21baa815c1aff15d774ce534ac2834bc440dccfe6348bae1885fd9a806f23a94ddafdc0f5bae4b09d - languageName: node - linkType: hard - "jest-runtime@npm:^29.6.1": version: 29.6.1 resolution: "jest-runtime@npm:29.6.1" @@ -28201,37 +27748,6 @@ __metadata: languageName: node linkType: hard -"jest-snapshot@npm:^29.5.0": - version: 29.5.0 - resolution: "jest-snapshot@npm:29.5.0" - dependencies: - "@babel/core": ^7.11.6 - "@babel/generator": ^7.7.2 - "@babel/plugin-syntax-jsx": ^7.7.2 - "@babel/plugin-syntax-typescript": ^7.7.2 - "@babel/traverse": ^7.7.2 - "@babel/types": ^7.3.3 - "@jest/expect-utils": ^29.5.0 - "@jest/transform": ^29.5.0 - "@jest/types": ^29.5.0 - "@types/babel__traverse": ^7.0.6 - "@types/prettier": ^2.1.5 - babel-preset-current-node-syntax: ^1.0.0 - chalk: ^4.0.0 - expect: ^29.5.0 - graceful-fs: ^4.2.9 - jest-diff: ^29.5.0 - jest-get-type: ^29.4.3 - jest-matcher-utils: ^29.5.0 - jest-message-util: ^29.5.0 - jest-util: ^29.5.0 - natural-compare: ^1.4.0 - pretty-format: ^29.5.0 - semver: ^7.3.5 - checksum: fe5df54122ed10eed625de6416a45bc4958d5062b018f05b152bf9785ab7f355dcd55e40cf5da63895bf8278f8d7b2bb4059b2cfbfdee18f509d455d37d8aa2b - languageName: node - linkType: hard - "jest-snapshot@npm:^29.6.1": version: 29.6.1 resolution: "jest-snapshot@npm:29.6.1" @@ -28303,20 +27819,6 @@ __metadata: languageName: node linkType: hard -"jest-validate@npm:^29.5.0": - version: 29.5.0 - resolution: "jest-validate@npm:29.5.0" - dependencies: - "@jest/types": ^29.5.0 - camelcase: ^6.2.0 - chalk: ^4.0.0 - jest-get-type: ^29.4.3 - leven: ^3.1.0 - pretty-format: ^29.5.0 - checksum: 43ca5df7cb75572a254ac3e92fbbe7be6b6a1be898cc1e887a45d55ea003f7a112717d814a674d37f9f18f52d8de40873c8f084f17664ae562736c78dd44c6a1 - languageName: node - linkType: hard - "jest-validate@npm:^29.6.1": version: 29.6.1 resolution: "jest-validate@npm:29.6.1" @@ -28331,22 +27833,6 @@ __metadata: languageName: node linkType: hard -"jest-watcher@npm:^29.5.0": - version: 29.5.0 - resolution: "jest-watcher@npm:29.5.0" - dependencies: - "@jest/test-result": ^29.5.0 - "@jest/types": ^29.5.0 - "@types/node": "*" - ansi-escapes: ^4.2.1 - chalk: ^4.0.0 - emittery: ^0.13.1 - jest-util: ^29.5.0 - string-length: ^4.0.1 - checksum: 62303ac7bdc7e61a8b4239a239d018f7527739da2b2be6a81a7be25b74ca769f1c43ee8558ce8e72bb857245c46d6e03af331227ffb00a57280abb2a928aa776 - languageName: node - linkType: hard - "jest-watcher@npm:^29.6.1": version: 29.6.1 resolution: "jest-watcher@npm:29.6.1" @@ -28419,26 +27905,7 @@ __metadata: languageName: node linkType: hard -"jest@npm:^29.5.0, jest@npm:~29.5.0": - version: 29.5.0 - resolution: "jest@npm:29.5.0" - dependencies: - "@jest/core": ^29.5.0 - "@jest/types": ^29.5.0 - import-local: ^3.0.2 - jest-cli: ^29.5.0 - peerDependencies: - node-notifier: ^8.0.1 || ^9.0.0 || ^10.0.0 - peerDependenciesMeta: - node-notifier: - optional: true - bin: - jest: bin/jest.js - checksum: a8ff2eb0f421623412236e23cbe67c638127fffde466cba9606bc0c0553b4c1e5cb116d7e0ef990b5d1712851652c8ee461373b578df50857fe635b94ff455d5 - languageName: node - linkType: hard - -"jest@npm:^29.6.1": +"jest@npm:~29.6.1": version: 29.6.1 resolution: "jest@npm:29.6.1" dependencies: @@ -34514,7 +33981,7 @@ __metadata: languageName: node linkType: hard -"pretty-format@npm:^27.0.0, pretty-format@npm:^27.0.2, pretty-format@npm:^27.5.1": +"pretty-format@npm:^27.0.2": version: 27.5.1 resolution: "pretty-format@npm:27.5.1" dependencies: From 54579fb41f9e29b26219172206ed26f9dfb7919e Mon Sep 17 00:00:00 2001 From: Guilherme Gazzo Date: Tue, 11 Jul 2023 14:17:13 -0300 Subject: [PATCH 003/342] chore: stop importing action manager as global (#29766) --- .../ui-message/client/ActionButtonSyncer.ts | 56 ----- .../app/ui-message/client/ActionManager.js | 21 +- .../client/actionButtons/dropdownAction.ts | 28 --- .../client/actionButtons/messageAction.ts | 33 --- .../client/actionButtons/messageBox.ts | 42 ---- .../ui-message/client/actionButtons/tabbar.ts | 33 --- .../app/ui-utils/client/lib/MessageAction.ts | 2 +- .../app/ui-utils/client/lib/messageBox.ts | 2 +- .../UIKit/hooks/useUIKitHandleAction.tsx | 11 +- .../UIKit/hooks/useUIKitHandleClose.tsx | 26 +-- .../UIKit/hooks/useUIKitStateManager.tsx | 7 +- .../components/message/toolbox/Toolbox.tsx | 43 ++-- .../client/hooks/useAppActionButtons.ts | 201 ++++++++++++++++++ .../client/hooks/useAppSlashCommands.ts | 45 ++++ .../client/hooks/useAppUiKitInteraction.ts | 27 +++ .../client/hooks/useUiKitActionManager.ts | 11 + .../providers/ActionManagerProvider.tsx | 7 + .../actions/hooks/useAdministrationMenu.tsx | 6 +- .../header/actions/hooks/useAppsItems.tsx | 45 ++-- .../components/MarketplaceRequestBadge.tsx | 4 +- .../marketplace/hooks/useAppRequestStats.ts | 2 +- .../client/views/modal/uikit/UiKitModal.tsx | 13 +- .../uikit/hooks/useActionManagerState.ts | 7 +- .../ActionsToolbarDropdown.tsx | 12 +- .../room/contextualBar/Apps/AppsWithData.tsx | 17 +- .../views/room/hooks/useAppsContextualBar.ts | 7 +- .../views/room/providers/ToolboxProvider.tsx | 5 +- .../client/apps/communication/websockets.js | 3 +- ee/packages/ddp-client/src/types/streams.ts | 3 +- 29 files changed, 407 insertions(+), 312 deletions(-) delete mode 100644 apps/meteor/app/ui-message/client/ActionButtonSyncer.ts delete mode 100644 apps/meteor/app/ui-message/client/actionButtons/dropdownAction.ts delete mode 100644 apps/meteor/app/ui-message/client/actionButtons/messageAction.ts delete mode 100644 apps/meteor/app/ui-message/client/actionButtons/messageBox.ts delete mode 100644 apps/meteor/app/ui-message/client/actionButtons/tabbar.ts create mode 100644 apps/meteor/client/hooks/useAppActionButtons.ts create mode 100644 apps/meteor/client/hooks/useAppSlashCommands.ts create mode 100644 apps/meteor/client/hooks/useAppUiKitInteraction.ts create mode 100644 apps/meteor/client/hooks/useUiKitActionManager.ts diff --git a/apps/meteor/app/ui-message/client/ActionButtonSyncer.ts b/apps/meteor/app/ui-message/client/ActionButtonSyncer.ts deleted file mode 100644 index b737c112ec93..000000000000 --- a/apps/meteor/app/ui-message/client/ActionButtonSyncer.ts +++ /dev/null @@ -1,56 +0,0 @@ -import { Meteor } from 'meteor/meteor'; -import type { IUIActionButton } from '@rocket.chat/apps-engine/definition/ui'; -import { UIActionButtonContext } from '@rocket.chat/apps-engine/definition/ui'; - -import * as TabBar from './actionButtons/tabbar'; -import * as MessageAction from './actionButtons/messageAction'; -import * as MessageBox from './actionButtons/messageBox'; -import * as DropdownAction from './actionButtons/dropdownAction'; -import { sdk } from '../../utils/client/lib/SDKClient'; - -let registeredButtons: Array = []; - -const addButton = async (button: IUIActionButton): Promise => { - switch (button.context) { - case UIActionButtonContext.MESSAGE_ACTION: - MessageAction.onAdded(button); - break; - case UIActionButtonContext.ROOM_ACTION: - TabBar.onAdded(button); - break; - case UIActionButtonContext.MESSAGE_BOX_ACTION: - MessageBox.onAdded(button); - break; - case UIActionButtonContext.USER_DROPDOWN_ACTION: - await DropdownAction.onAdded(button); - break; - } - - registeredButtons.push(Object.freeze(button)); -}; - -const removeButton = async (button: IUIActionButton): Promise => { - switch (button.context) { - case UIActionButtonContext.MESSAGE_ACTION: - MessageAction.onRemoved(button); - break; - case UIActionButtonContext.ROOM_ACTION: - TabBar.onRemoved(button); - break; - case UIActionButtonContext.MESSAGE_BOX_ACTION: - MessageBox.onRemoved(button); - break; - case UIActionButtonContext.USER_DROPDOWN_ACTION: - await DropdownAction.onRemoved(button); - break; - } -}; - -export const loadButtons = (): Promise => - sdk.rest.get('/apps/actionButtons').then((value) => { - registeredButtons.forEach((button) => removeButton(button)); - registeredButtons = []; - value.map(addButton); - }); - -Meteor.startup(() => loadButtons()); diff --git a/apps/meteor/app/ui-message/client/ActionManager.js b/apps/meteor/app/ui-message/client/ActionManager.js index 48165b99f436..78fd90e52e9b 100644 --- a/apps/meteor/app/ui-message/client/ActionManager.js +++ b/apps/meteor/app/ui-message/client/ActionManager.js @@ -1,18 +1,17 @@ import { UIKitIncomingInteractionType } from '@rocket.chat/apps-engine/definition/uikit'; -import { Meteor } from 'meteor/meteor'; import { Random } from '@rocket.chat/random'; import { Emitter } from '@rocket.chat/emitter'; import { UIKitInteractionTypes } from '@rocket.chat/core-typings'; +import { lazy } from 'react'; -import Notifications from '../../notifications/client/lib/Notifications'; -import { CachedCollectionManager } from '../../ui-cached-collection/client'; -import { t } from '../../utils/client'; +import { t } from '../../utils/lib/i18n'; import * as banners from '../../../client/lib/banners'; import { dispatchToastMessage } from '../../../client/lib/toast'; -import { imperativeModal } from '../../../client/lib/imperativeModal'; -import UiKitModal from '../../../client/views/modal/uikit/UiKitModal'; import { sdk } from '../../utils/client/lib/SDKClient'; import { router } from '../../../client/providers/RouterProvider'; +import { imperativeModal } from '../../../client/lib/imperativeModal'; + +const UiKitModal = lazy(() => import('../../../client/views/modal/uikit/UiKitModal')); const events = new Emitter(); @@ -45,7 +44,7 @@ export const generateTriggerId = (appId) => { return triggerId; }; -const handlePayloadUserInteraction = (type, { /* appId,*/ triggerId, ...data }) => { +export const handlePayloadUserInteraction = (type, { /* appId,*/ triggerId, ...data }) => { if (!triggersId.has(triggerId)) { return; } @@ -256,11 +255,3 @@ export const getUserInteractionPayloadByViewId = (viewId) => { return instance.payload; }; - -Meteor.startup(() => - CachedCollectionManager.onLogin(() => - Notifications.onUser('uiInteraction', ({ type, ...data }) => { - handlePayloadUserInteraction(type, data); - }), - ), -); diff --git a/apps/meteor/app/ui-message/client/actionButtons/dropdownAction.ts b/apps/meteor/app/ui-message/client/actionButtons/dropdownAction.ts deleted file mode 100644 index 07ce832e59f2..000000000000 --- a/apps/meteor/app/ui-message/client/actionButtons/dropdownAction.ts +++ /dev/null @@ -1,28 +0,0 @@ -import type { IUIActionButton } from '@rocket.chat/apps-engine/definition/ui'; - -import { AccountBox } from '../../../ui-utils/client/lib/AccountBox'; - -export const onAdded = async (button: IUIActionButton): Promise => { - const { appId, actionId, labelI18n, context } = button; - await AccountBox.addItem({ - ...button, - name: button.labelI18n, - appId, - actionId, - labelI18n, - context, - isAppButtonItem: true, - }); -}; -export const onRemoved = async (button: IUIActionButton): Promise => { - const { appId, actionId, labelI18n, context } = button; - await AccountBox.deleteItem({ - ...button, - name: button.labelI18n, - appId, - actionId, - labelI18n, - context, - isAppButtonItem: true, - }); -}; diff --git a/apps/meteor/app/ui-message/client/actionButtons/messageAction.ts b/apps/meteor/app/ui-message/client/actionButtons/messageAction.ts deleted file mode 100644 index 29a45ded6a1d..000000000000 --- a/apps/meteor/app/ui-message/client/actionButtons/messageAction.ts +++ /dev/null @@ -1,33 +0,0 @@ -import type { IUIActionButton } from '@rocket.chat/apps-engine/definition/ui'; - -import { Utilities } from '../../../../ee/lib/misc/Utilities'; -import { MessageAction } from '../../../ui-utils/client'; -import { messageArgs } from '../../../../client/lib/utils/messageArgs'; -import { triggerActionButtonAction } from '../ActionManager'; -import { applyButtonFilters } from './lib/applyButtonFilters'; - -const getIdForActionButton = ({ appId, actionId }: IUIActionButton): string => `${appId}/${actionId}`; - -// eslint-disable-next-line no-void -export const onAdded = (button: IUIActionButton): void => - MessageAction.addButton({ - id: getIdForActionButton(button), - icon: '' as any, - label: Utilities.getI18nKeyForApp(button.labelI18n, button.appId), - context: button.when?.messageActionContext || ['message', 'message-mobile', 'threads', 'starred'], - condition({ room }) { - return applyButtonFilters(button, room); - }, - action(_, props) { - const { message = messageArgs(this).msg } = props; - void triggerActionButtonAction({ - rid: message.rid, - mid: message._id, - actionId: button.actionId, - appId: button.appId, - payload: { context: button.context }, - }); - }, - }); - -export const onRemoved = (button: IUIActionButton): void => MessageAction.removeButton(getIdForActionButton(button)); diff --git a/apps/meteor/app/ui-message/client/actionButtons/messageBox.ts b/apps/meteor/app/ui-message/client/actionButtons/messageBox.ts deleted file mode 100644 index d07ecbc23ad8..000000000000 --- a/apps/meteor/app/ui-message/client/actionButtons/messageBox.ts +++ /dev/null @@ -1,42 +0,0 @@ -import type { IUIActionButton } from '@rocket.chat/apps-engine/definition/ui'; - -import { ChatRoom } from '../../../models/client'; -import { messageBox } from '../../../ui-utils/client'; -import { applyButtonFilters } from './lib/applyButtonFilters'; -import { triggerActionButtonAction } from '../ActionManager'; -import { Utilities } from '../../../../ee/lib/misc/Utilities'; -import { RoomManager } from '../../../../client/lib/RoomManager'; -import { asReactiveSource } from '../../../../client/lib/tracker'; - -const getIdForActionButton = ({ appId, actionId }: IUIActionButton): string => `${appId}/${actionId}`; - -export const onAdded = (button: IUIActionButton): void => - // eslint-disable-next-line no-void - void messageBox.actions.add('Apps', Utilities.getI18nKeyForApp(button.labelI18n, button.appId), { - id: getIdForActionButton(button), - // icon: button.icon || '', - condition() { - return applyButtonFilters( - button, - ChatRoom.findOne( - asReactiveSource( - (cb) => RoomManager.on('changed', cb), - () => RoomManager.opened, - ), - ), - ); - }, - action(params) { - void triggerActionButtonAction({ - rid: params.rid, - tmid: params.tmid, - actionId: button.actionId, - appId: button.appId, - payload: { context: button.context, message: params.chat.composer?.text }, - }); - }, - }); - -export const onRemoved = (button: IUIActionButton): void => - // eslint-disable-next-line no-void - void messageBox.actions.remove('Apps', new RegExp(getIdForActionButton(button))); diff --git a/apps/meteor/app/ui-message/client/actionButtons/tabbar.ts b/apps/meteor/app/ui-message/client/actionButtons/tabbar.ts deleted file mode 100644 index 26352cd9b59a..000000000000 --- a/apps/meteor/app/ui-message/client/actionButtons/tabbar.ts +++ /dev/null @@ -1,33 +0,0 @@ -import type { IUIActionButton } from '@rocket.chat/apps-engine/definition/ui'; - -import { addAction, deleteAction } from '../../../../client/views/room/lib/Toolbox'; -import { Utilities } from '../../../../ee/lib/misc/Utilities'; -import { triggerActionButtonAction } from '../ActionManager'; -import { applyButtonFilters } from './lib/applyButtonFilters'; - -const getIdForActionButton = ({ appId, actionId }: IUIActionButton): string => `${appId}/${actionId}`; - -export const onAdded = (button: IUIActionButton): void => - // eslint-disable-next-line no-void - void addAction(getIdForActionButton(button), ({ room }) => - applyButtonFilters(button, room) - ? { - id: button.actionId, - icon: undefined, // Apps won't provide icons for now - order: 300, // Make sure the button only shows up inside the room toolbox - title: Utilities.getI18nKeyForApp(button.labelI18n, button.appId), - // Filters were applied in the applyButtonFilters function - // if the code made it this far, the button should be shown - groups: ['group', 'channel', 'live', 'team', 'direct', 'direct_multiple'], - action: (): any => - triggerActionButtonAction({ - rid: room._id, - actionId: button.actionId, - appId: button.appId, - payload: { context: button.context }, - }), - } - : null, - ); - -export const onRemoved = (button: IUIActionButton): boolean => deleteAction(getIdForActionButton(button)); diff --git a/apps/meteor/app/ui-utils/client/lib/MessageAction.ts b/apps/meteor/app/ui-utils/client/lib/MessageAction.ts index 47fdbb311765..6fd101864bc8 100644 --- a/apps/meteor/app/ui-utils/client/lib/MessageAction.ts +++ b/apps/meteor/app/ui-utils/client/lib/MessageAction.ts @@ -66,7 +66,7 @@ export type MessageActionConfig = { chat, autoTranslateOptions, }: { - message?: IMessage & Partial; + message: IMessage & Partial; tabbar: ToolboxContextValue; room?: IRoom; chat: ContextType; diff --git a/apps/meteor/app/ui-utils/client/lib/messageBox.ts b/apps/meteor/app/ui-utils/client/lib/messageBox.ts index e5cd105741de..3f3c545af57e 100644 --- a/apps/meteor/app/ui-utils/client/lib/messageBox.ts +++ b/apps/meteor/app/ui-utils/client/lib/messageBox.ts @@ -3,7 +3,7 @@ import type { TranslationKey } from '@rocket.chat/ui-contexts'; import type { ChatAPI } from '../../../../client/lib/chats/ChatAPI'; -type MessageBoxAction = { +export type MessageBoxAction = { label: TranslationKey; id: string; icon?: string; diff --git a/apps/meteor/client/UIKit/hooks/useUIKitHandleAction.tsx b/apps/meteor/client/UIKit/hooks/useUIKitHandleAction.tsx index f347f1717dfb..1a2370ccad18 100644 --- a/apps/meteor/client/UIKit/hooks/useUIKitHandleAction.tsx +++ b/apps/meteor/client/UIKit/hooks/useUIKitHandleAction.tsx @@ -5,17 +5,19 @@ import { UIKitIncomingInteractionContainerType } from '@rocket.chat/apps-engine/ // import React, { Context, FC, useMemo } from 'react'; import type { UiKitPayload, UIKitActionEvent } from '@rocket.chat/core-typings'; import { useMutableCallback } from '@rocket.chat/fuselage-hooks'; + +import { useUiKitActionManager } from '../../hooks/useUiKitActionManager'; // import { UIKitIncomingInteractionContainerType } from '@rocket.chat/apps-engine/definition/uikit/UIKitIncomingInteractionContainer'; // import { useEndpoint } from '@rocket.chat/ui-contexts'; -import * as ActionManager from '../../../app/ui-message/client/ActionManager'; -const useUIKitHandleAction = (state: S): ((event: UIKitActionEvent) => Promise) => - useMutableCallback(async ({ blockId, value, appId, actionId }) => { +const useUIKitHandleAction = (state: S): ((event: UIKitActionEvent) => Promise) => { + const actionManager = useUiKitActionManager(); + return useMutableCallback(async ({ blockId, value, appId, actionId }) => { if (!appId) { throw new Error('useUIKitHandleAction - invalid appId'); } - return ActionManager.triggerBlockAction({ + return actionManager.triggerBlockAction({ container: { type: UIKitIncomingInteractionContainerType.VIEW, id: state.viewId || state.appId, @@ -26,5 +28,6 @@ const useUIKitHandleAction = (state: S): ((event: UIKitA blockId, }); }); +}; export { useUIKitHandleAction }; diff --git a/apps/meteor/client/UIKit/hooks/useUIKitHandleClose.tsx b/apps/meteor/client/UIKit/hooks/useUIKitHandleClose.tsx index 85b5c24eb000..b676d23987ea 100644 --- a/apps/meteor/client/UIKit/hooks/useUIKitHandleClose.tsx +++ b/apps/meteor/client/UIKit/hooks/useUIKitHandleClose.tsx @@ -6,28 +6,30 @@ import type { UIKitInteractionType } from '@rocket.chat/apps-engine/definition/u import type { UiKitPayload } from '@rocket.chat/core-typings'; import { useMutableCallback } from '@rocket.chat/fuselage-hooks'; import { useToastMessageDispatch } from '@rocket.chat/ui-contexts'; + +import { useUiKitActionManager } from '../../hooks/useUiKitActionManager'; // import { UIKitIncomingInteractionContainerType } from '@rocket.chat/apps-engine/definition/uikit/UIKitIncomingInteractionContainer'; // import { useEndpoint } from '@rocket.chat/ui-contexts'; -import * as ActionManager from '../../../app/ui-message/client/ActionManager'; - // eslint-disable-next-line @typescript-eslint/no-unused-vars const emptyFn = (_error: any, _result: UIKitInteractionType | void): void => undefined; const useUIKitHandleClose = (state: S, fn = emptyFn): (() => Promise) => { + const actionManager = useUiKitActionManager(); const dispatchToastMessage = useToastMessageDispatch(); return useMutableCallback(() => - ActionManager.triggerCancel({ - appId: state.appId, - viewId: state.viewId, - view: { - ...state, - id: state.viewId, - // state: groupStateByBlockId(values), - }, - isCleared: true, - }) + actionManager + .triggerCancel({ + appId: state.appId, + viewId: state.viewId, + view: { + ...state, + id: state.viewId, + // state: groupStateByBlockId(values), + }, + isCleared: true, + }) .then((result) => fn(undefined, result)) .catch((error) => { dispatchToastMessage({ type: 'error', message: error }); diff --git a/apps/meteor/client/UIKit/hooks/useUIKitStateManager.tsx b/apps/meteor/client/UIKit/hooks/useUIKitStateManager.tsx index 2ada5f661787..26b329f2ea60 100644 --- a/apps/meteor/client/UIKit/hooks/useUIKitStateManager.tsx +++ b/apps/meteor/client/UIKit/hooks/useUIKitStateManager.tsx @@ -3,9 +3,10 @@ import { isErrorType } from '@rocket.chat/core-typings'; import { useSafely } from '@rocket.chat/fuselage-hooks'; import { useEffect, useState } from 'react'; -import * as ActionManager from '../../../app/ui-message/client/ActionManager'; +import { useUiKitActionManager } from '../../hooks/useUiKitActionManager'; const useUIKitStateManager = (initialState: S): S => { + const actionManager = useUiKitActionManager(); const [state, setState] = useSafely(useState(initialState)); const { viewId } = state; @@ -22,10 +23,10 @@ const useUIKitStateManager = (initialState: S): S => { setState(rest as any); }; - ActionManager.on(viewId, handleUpdate); + actionManager.on(viewId, handleUpdate); return (): void => { - ActionManager.off(viewId, handleUpdate); + actionManager.off(viewId, handleUpdate); }; }, [setState, viewId]); diff --git a/apps/meteor/client/components/message/toolbox/Toolbox.tsx b/apps/meteor/client/components/message/toolbox/Toolbox.tsx index ee1a0bc2c1b8..f957c75a81c3 100644 --- a/apps/meteor/client/components/message/toolbox/Toolbox.tsx +++ b/apps/meteor/client/components/message/toolbox/Toolbox.tsx @@ -2,15 +2,15 @@ import type { IMessage, IRoom, ISubscription, ITranslatedMessage } from '@rocket import { isThreadMessage, isRoomFederated } from '@rocket.chat/core-typings'; import { MessageToolbox, MessageToolboxItem } from '@rocket.chat/fuselage'; import { useFeaturePreview } from '@rocket.chat/ui-client'; -import { useUser, useSettings, useTranslation } from '@rocket.chat/ui-contexts'; +import { useUser, useSettings, useTranslation, useMethod } from '@rocket.chat/ui-contexts'; import { useQuery } from '@tanstack/react-query'; import type { ReactElement } from 'react'; import React, { memo, useMemo } from 'react'; import type { MessageActionContext } from '../../../../app/ui-utils/client/lib/MessageAction'; import { MessageAction } from '../../../../app/ui-utils/client/lib/MessageAction'; -import { sdk } from '../../../../app/utils/client/lib/SDKClient'; import { useEmojiPickerData } from '../../../contexts/EmojiPickerContext'; +import { useMessageActionAppsActionButtons } from '../../../hooks/useAppActionButtons'; import EmojiElement from '../../../views/composer/EmojiPicker/EmojiElement'; import { useIsSelecting } from '../../../views/room/MessageList/contexts/SelectedMessagesContext'; import { useAutoTranslate } from '../../../views/room/MessageList/hooks/useAutoTranslate'; @@ -52,6 +52,8 @@ const Toolbox = ({ message, messageContext, room, subscription }: ToolboxProps): const quickReactionsEnabled = useFeaturePreview('quickReactions'); + const setReaction = useMethod('setReaction'); + const context = getMessageContext(message, room, messageContext); const mapSettings = useMemo(() => Object.fromEntries(settings.map((setting) => [setting._id, setting.value])), [settings]); @@ -59,6 +61,8 @@ const Toolbox = ({ message, messageContext, room, subscription }: ToolboxProps): const chat = useChat(); const { quickReactions, addRecentEmoji } = useEmojiPickerData(); + const actionButtonApps = useMessageActionAppsActionButtons(context); + const actionsQueryResult = useQuery(['rooms', room._id, 'messages', message._id, 'actions'] as const, async () => { const messageActions = await MessageAction.getButtons( { message, room, user: user ?? undefined, subscription, settings: mapSettings, chat }, @@ -87,7 +91,7 @@ const Toolbox = ({ message, messageContext, room, subscription }: ToolboxProps): const isReactionAllowed = actionsQueryResult.data?.message.find(({ id }) => id === 'reaction-message'); const handleSetReaction = (emoji: string) => { - sdk.call('setReaction', `:${emoji}:`, message._id); + setReaction(`:${emoji}:`, message._id); addRecentEmoji(emoji); }; @@ -98,24 +102,23 @@ const Toolbox = ({ message, messageContext, room, subscription }: ToolboxProps): quickReactions.slice(0, 3).map(({ emoji, image }) => { return handleSetReaction(emoji)} />; })} - {actionsQueryResult.data?.message.map((action) => ( - action.action(e, { message, tabbar: toolbox, room, chat, autoTranslateOptions })} - key={action.id} - icon={action.icon} - title={t(action.label)} - data-qa-id={action.label} - data-qa-type='message-action-menu' - /> - ))} - {(actionsQueryResult.data?.menu.length ?? 0) > 0 && ( + {actionsQueryResult.isSuccess && + actionsQueryResult.data.message.map((action) => ( + action.action(e, { message, tabbar: toolbox, room, chat, autoTranslateOptions })} + key={action.id} + icon={action.icon} + title={t(action.label)} + data-qa-id={action.label} + data-qa-type='message-action-menu' + /> + ))} + {actionsQueryResult.isSuccess && actionsQueryResult.data.menu.length > 0 && ( ({ - ...action, - action: (e): void => action.action(e, { message, tabbar: toolbox, room, chat, autoTranslateOptions }), - })) ?? [] - } + options={[...actionsQueryResult.data?.menu, ...(actionButtonApps.data ?? [])].filter(Boolean).map((action) => ({ + ...action, + action: (e): void => action.action(e, { message, tabbar: toolbox, room, chat, autoTranslateOptions }), + }))} data-qa-type='message-action-menu-options' /> )} diff --git a/apps/meteor/client/hooks/useAppActionButtons.ts b/apps/meteor/client/hooks/useAppActionButtons.ts new file mode 100644 index 000000000000..1f539c49b002 --- /dev/null +++ b/apps/meteor/client/hooks/useAppActionButtons.ts @@ -0,0 +1,201 @@ +import type { IUIActionButton, UIActionButtonContext } from '@rocket.chat/apps-engine/definition/ui'; +import { useEndpoint, useStream, useUserId } from '@rocket.chat/ui-contexts'; +import type { UseQueryResult } from '@tanstack/react-query'; +import { useQuery, useQueryClient } from '@tanstack/react-query'; +import { useEffect, useRef, useMemo } from 'react'; + +import { applyButtonFilters } from '../../app/ui-message/client/actionButtons/lib/applyButtonFilters'; +import type { MessageActionConfig, MessageActionContext } from '../../app/ui-utils/client/lib/MessageAction'; +import type { MessageBoxAction } from '../../app/ui-utils/client/lib/messageBox'; +import { Utilities } from '../../ee/lib/misc/Utilities'; +import type { GenericMenuItemProps } from '../components/GenericMenu/GenericMenuItem'; +import { useRoom } from '../views/room/contexts/RoomContext'; +import type { ToolboxAction } from '../views/room/lib/Toolbox'; +import { useUiKitActionManager } from './useUiKitActionManager'; + +const getIdForActionButton = ({ appId, actionId }: IUIActionButton): string => `${appId}/${actionId}`; + +export const useAppActionButtons = (context?: `${UIActionButtonContext}`) => { + const stream = useRef<() => void>(); + const queryClient = useQueryClient(); + + const apps = useStream('apps'); + const uid = useUserId(); + + useEffect(() => () => stream.current?.(), []); + + useQuery(['apps', 'stream', 'actionButtons', uid], () => { + if (!uid) { + return []; + } + stream.current?.(); + stream.current = apps('actions/changed', () => { + queryClient.invalidateQueries(['apps', 'actionButtons']); + }); + + return []; + }); + + const getActionButtons = useEndpoint('GET', '/apps/actionButtons'); + + const result = useQuery(['apps', 'actionButtons'], () => getActionButtons(), { + ...(context && { + select: (data) => data.filter((button) => button.context === context), + }), + }); + return result; +}; + +export const useMessageboxAppsActionButtons = () => { + const result = useAppActionButtons('messageBoxAction'); + const actionManager = useUiKitActionManager(); + const room = useRoom(); + + const data = useMemo( + () => + result.data + ?.filter((action) => { + return applyButtonFilters(action, room); + }) + .map((action) => { + const item: MessageBoxAction = { + id: getIdForActionButton(action), + label: Utilities.getI18nKeyForApp(action.labelI18n, action.appId), + action: (params) => { + void actionManager.triggerActionButtonAction({ + rid: params.rid, + tmid: params.tmid, + actionId: action.actionId, + appId: action.appId, + payload: { context: action.context, message: params.chat.composer?.text }, + }); + }, + }; + + return item; + }), + [actionManager, result.data, room], + ); + return { + ...result, + data, + } as UseQueryResult; +}; + +export const useUserDropdownAppsActionButtons = () => { + const result = useAppActionButtons('userDropdownAction'); + const actionManager = useUiKitActionManager(); + + const data = useMemo( + () => + result.data + ?.filter((action) => { + return applyButtonFilters(action); + }) + .map((action, key) => { + return { + id: action.actionId + key, + // icon: action.icon as GenericMenuItemProps['icon'], + content: action.labelI18n, + onClick: () => { + actionManager.triggerActionButtonAction({ + actionId: action.actionId, + appId: action.appId, + payload: { context: action.context }, + }); + }, + }; + }), + [actionManager, result.data], + ); + return { + ...result, + data, + } as UseQueryResult; +}; + +export const useRoomActionAppsActionButtons = (context?: MessageActionContext) => { + const result = useAppActionButtons('roomAction'); + const actionManager = useUiKitActionManager(); + const room = useRoom(); + const data = useMemo( + () => + result.data + ?.filter((action) => { + if (context && ['group', 'channel', 'live', 'team', 'direct', 'direct_multiple'].includes(context)) { + return false; + } + return applyButtonFilters(action, room); + }) + .map((action) => { + const item: [string, ToolboxAction] = [ + action.actionId, + { + id: action.actionId, + icon: undefined as any, // Apps won't provide icons for now + order: 300, // Make sure the button only shows up inside the room toolbox + title: Utilities.getI18nKeyForApp(action.labelI18n, action.appId), + groups: ['group', 'channel', 'live', 'team', 'direct', 'direct_multiple'], + // Filters were applied in the applyButtonFilters function + // if the code made it this far, the button should be shown + action: () => + void actionManager.triggerActionButtonAction({ + rid: room._id, + actionId: action.actionId, + appId: action.appId, + payload: { context: action.context }, + }), + }, + ]; + return item; + }), + [actionManager, context, result.data, room], + ); + return { + ...result, + data, + } as UseQueryResult<[string, ToolboxAction][]>; +}; + +export const useMessageActionAppsActionButtons = (context?: MessageActionContext) => { + const result = useAppActionButtons('messageAction'); + const actionManager = useUiKitActionManager(); + const room = useRoom(); + + const data = useMemo( + () => + result.data + ?.filter((action) => { + if ( + context && + !(action.when?.messageActionContext || ['message', 'message-mobile', 'threads', 'starred']).includes(context as any) + ) { + return false; + } + return applyButtonFilters(action, room); + }) + .map((action) => { + const item: MessageActionConfig = { + icon: undefined as any, + id: getIdForActionButton(action), + label: Utilities.getI18nKeyForApp(action.labelI18n, action.appId), + action: (_, params) => { + void actionManager.triggerActionButtonAction({ + rid: params.message.rid, + tmid: params.message.tmid, + actionId: action.actionId, + appId: action.appId, + payload: { context: action.context }, + }); + }, + }; + + return item; + }), + [actionManager, context, result.data, room], + ); + return { + ...result, + data, + } as UseQueryResult; +}; diff --git a/apps/meteor/client/hooks/useAppSlashCommands.ts b/apps/meteor/client/hooks/useAppSlashCommands.ts new file mode 100644 index 000000000000..548084d4a00e --- /dev/null +++ b/apps/meteor/client/hooks/useAppSlashCommands.ts @@ -0,0 +1,45 @@ +import { useEndpoint, useStream, useUserId } from '@rocket.chat/ui-contexts'; +import { useQuery, useQueryClient } from '@tanstack/react-query'; +import { useEffect } from 'react'; + +import { slashCommands } from '../../app/utils/lib/slashCommand'; + +const stop = (...args: (() => void)[]) => args.forEach((s) => s()); + +export const useAppSlashCommands = () => { + const queryClient = useQueryClient(); + + const apps = useStream('apps'); + const uid = useUserId(); + + useEffect(() => { + if (!uid) { + return; + } + return stop( + apps('command/added', () => { + queryClient.invalidateQueries(['apps', 'slashCommands']); + }), + + apps('command/updated', () => { + queryClient.invalidateQueries(['apps', 'slashCommands']); + }), + + apps('command/removed', () => { + queryClient.invalidateQueries(['apps', 'slashCommands']); + }), + + apps('command/disabled', () => { + queryClient.invalidateQueries(['apps', 'slashCommands']); + }), + ); + }, [apps, queryClient, uid]); + + const getSlashCommands = useEndpoint('GET', '/v1/commands.list'); + + useQuery(['apps', 'slashCommands'], () => getSlashCommands(), { + onSuccess(data) { + data.commands.forEach((command) => slashCommands.add(command)); + }, + }); +}; diff --git a/apps/meteor/client/hooks/useAppUiKitInteraction.ts b/apps/meteor/client/hooks/useAppUiKitInteraction.ts new file mode 100644 index 000000000000..b78a35456736 --- /dev/null +++ b/apps/meteor/client/hooks/useAppUiKitInteraction.ts @@ -0,0 +1,27 @@ +import type { UIKitInteractionType } from '@rocket.chat/apps-engine/definition/uikit'; +import { useStream, useUserId } from '@rocket.chat/ui-contexts'; +import { useEffect } from 'react'; + +export const useAppUiKitInteraction = ( + handlePayloadUserInteraction: ( + type: UIKitInteractionType, + data: { + triggerId: string; + appId: string; + }, + ) => void, +) => { + const notifyUser = useStream('notify-user'); + + const uid = useUserId(); + + useEffect(() => { + if (!uid) { + return; + } + + return notifyUser(`${uid}/uiInteraction`, ({ type, ...data }) => { + handlePayloadUserInteraction(type, data); + }); + }, [notifyUser, uid, handlePayloadUserInteraction]); +}; diff --git a/apps/meteor/client/hooks/useUiKitActionManager.ts b/apps/meteor/client/hooks/useUiKitActionManager.ts new file mode 100644 index 000000000000..d6bdab88004f --- /dev/null +++ b/apps/meteor/client/hooks/useUiKitActionManager.ts @@ -0,0 +1,11 @@ +import { useContext } from 'react'; + +import { ActionManagerContext } from '../contexts/ActionManagerContext'; + +export const useUiKitActionManager = () => { + const actionManager = useContext(ActionManagerContext); + if (!actionManager) { + throw new Error('ActionManagerContext is not provided'); + } + return actionManager; +}; diff --git a/apps/meteor/client/providers/ActionManagerProvider.tsx b/apps/meteor/client/providers/ActionManagerProvider.tsx index b76d99b06af9..dd84eec234ee 100644 --- a/apps/meteor/client/providers/ActionManagerProvider.tsx +++ b/apps/meteor/client/providers/ActionManagerProvider.tsx @@ -3,12 +3,19 @@ import React from 'react'; import * as ActionManager from '../../app/ui-message/client/ActionManager'; import { ActionManagerContext } from '../contexts/ActionManagerContext'; +import { useAppActionButtons } from '../hooks/useAppActionButtons'; +import { useAppSlashCommands } from '../hooks/useAppSlashCommands'; +import { useAppUiKitInteraction } from '../hooks/useAppUiKitInteraction'; type ActionManagerProviderProps = { children?: ReactNode; }; const ActionManagerProvider = ({ children }: ActionManagerProviderProps): ReactElement => { + useAppActionButtons(); + useAppSlashCommands(); + useAppUiKitInteraction(ActionManager.handlePayloadUserInteraction); + return {children}; }; diff --git a/apps/meteor/client/sidebar/header/actions/hooks/useAdministrationMenu.tsx b/apps/meteor/client/sidebar/header/actions/hooks/useAdministrationMenu.tsx index f6179bb93d26..72b9b90419a5 100644 --- a/apps/meteor/client/sidebar/header/actions/hooks/useAdministrationMenu.tsx +++ b/apps/meteor/client/sidebar/header/actions/hooks/useAdministrationMenu.tsx @@ -59,11 +59,7 @@ export const useAdministrationMenu = () => { const showApps = hasAccessMarketplacePermission || hasManageAppsPermission || !!appBoxItems.length; const administrationItems = useAdministrationItems({ accountBoxItems: adminBoxItems, showWorkspace }); - const appItems = useAppsItems({ - appBoxItems, - appsManagementAllowed: hasManageAppsPermission, - showMarketplace: hasAccessMarketplacePermission || hasManageAppsPermission, - }); + const appItems = useAppsItems(); const auditItems = useAuditItems({ showAudit: hasAuditPermission, showAuditLog: hasAuditLogPermission }); const sections = [ diff --git a/apps/meteor/client/sidebar/header/actions/hooks/useAppsItems.tsx b/apps/meteor/client/sidebar/header/actions/hooks/useAppsItems.tsx index a6c77c55d284..6ecd34b62d5d 100644 --- a/apps/meteor/client/sidebar/header/actions/hooks/useAppsItems.tsx +++ b/apps/meteor/client/sidebar/header/actions/hooks/useAppsItems.tsx @@ -1,21 +1,21 @@ import { Badge, Skeleton } from '@rocket.chat/fuselage'; -import { useTranslation, useRoute } from '@rocket.chat/ui-contexts'; +import { useTranslation, useRoute, usePermission } from '@rocket.chat/ui-contexts'; import React from 'react'; -import { triggerActionButtonAction } from '../../../../../app/ui-message/client/ActionManager'; -import type { IAppAccountBoxItem } from '../../../../../app/ui-utils/client/lib/AccountBox'; import type { GenericMenuItemProps } from '../../../../components/GenericMenu/GenericMenuItem'; +import { useUserDropdownAppsActionButtons } from '../../../../hooks/useAppActionButtons'; import { useAppRequestStats } from '../../../../views/marketplace/hooks/useAppRequestStats'; -type useAppsItemsProps = { - appBoxItems: IAppAccountBoxItem[]; - appsManagementAllowed?: boolean; - showMarketplace?: boolean; -}; - -export const useAppsItems = ({ appBoxItems, appsManagementAllowed, showMarketplace }: useAppsItemsProps): GenericMenuItemProps[] => { +export const useAppsItems = (): GenericMenuItemProps[] => { const t = useTranslation(); + const appBoxItems = useUserDropdownAppsActionButtons(); + + const hasManageAppsPermission = usePermission('manage-apps'); + const hasAccessMarketplacePermission = usePermission('access-marketplace'); + + const showMarketplace = hasAccessMarketplacePermission || hasManageAppsPermission; + const marketplaceRoute = useRoute('marketplace'); const page = 'list'; @@ -46,33 +46,16 @@ export const useAppsItems = ({ appBoxItems, appsManagementAllowed, showMarketpla addon: ( <> {appRequestStats.isLoading && } - {appRequestStats.isSuccess && appRequestStats.data.data.totalUnseen > 0 && ( - {appRequestStats.data.data.totalUnseen} + {appRequestStats.isSuccess && appRequestStats.data.totalUnseen > 0 && ( + {appRequestStats.data.totalUnseen} )} ), }; - const appItems: GenericMenuItemProps[] = appBoxItems.map((item: IAppAccountBoxItem, key: number) => { - const action = () => { - triggerActionButtonAction({ - rid: '', - mid: '', - actionId: item.actionId, - appId: item.appId, - payload: { context: item.context }, - }); - }; - return { - id: item.actionId + key, - icon: item.icon as GenericMenuItemProps['icon'], - content: (t.has(item.name) && t(item.name)) || item.name, - onClick: action, - }; - }); return [ ...(showMarketplace ? marketPlaceItems : []), - ...(appsManagementAllowed ? [appsManagementItem] : []), - ...(appBoxItems.length ? appItems : []), + ...(hasManageAppsPermission ? [appsManagementItem] : []), + ...(appBoxItems.isSuccess ? appBoxItems.data : []), ]; }; diff --git a/apps/meteor/client/views/marketplace/components/MarketplaceRequestBadge.tsx b/apps/meteor/client/views/marketplace/components/MarketplaceRequestBadge.tsx index 58c055b7f6bc..acfc04e5f33f 100644 --- a/apps/meteor/client/views/marketplace/components/MarketplaceRequestBadge.tsx +++ b/apps/meteor/client/views/marketplace/components/MarketplaceRequestBadge.tsx @@ -11,11 +11,11 @@ const MarketplaceRequestBadge = () => { if (requestStatsResult.isError) return null; - if (!requestStatsResult.data.data.totalUnseen) { + if (!requestStatsResult.data.totalUnseen) { return null; } - return {requestStatsResult.data.data.totalUnseen}; + return {requestStatsResult.data.totalUnseen}; }; export default MarketplaceRequestBadge; diff --git a/apps/meteor/client/views/marketplace/hooks/useAppRequestStats.ts b/apps/meteor/client/views/marketplace/hooks/useAppRequestStats.ts index 70e692eba5e8..af25282b7e53 100644 --- a/apps/meteor/client/views/marketplace/hooks/useAppRequestStats.ts +++ b/apps/meteor/client/views/marketplace/hooks/useAppRequestStats.ts @@ -8,7 +8,7 @@ export const useAppRequestStats = () => { return useQuery({ queryKey: ['app-requests-stats'], - queryFn: async () => fetchRequestStats(), + queryFn: async () => (await fetchRequestStats()).data, refetchOnWindowFocus: false, retry: false, enabled: canManageApp, diff --git a/apps/meteor/client/views/modal/uikit/UiKitModal.tsx b/apps/meteor/client/views/modal/uikit/UiKitModal.tsx index 6bdefce4ff9e..c9c17af46f32 100644 --- a/apps/meteor/client/views/modal/uikit/UiKitModal.tsx +++ b/apps/meteor/client/views/modal/uikit/UiKitModal.tsx @@ -6,7 +6,7 @@ import type { LayoutBlock } from '@rocket.chat/ui-kit'; import type { ContextType, ReactElement, ReactEventHandler } from 'react'; import React from 'react'; -import * as ActionManager from '../../../../app/ui-message/client/ActionManager'; +import { useUiKitActionManager } from '../../../hooks/useUiKitActionManager'; import { detectEmoji } from '../../../lib/utils/detectEmoji'; import ModalBlock from './ModalBlock'; import type { ActionManagerState } from './hooks/useActionManagerState'; @@ -14,6 +14,7 @@ import { useActionManagerState } from './hooks/useActionManagerState'; import { useValues } from './hooks/useValues'; const UiKitModal = (props: ActionManagerState): ReactElement => { + const actionManager = useUiKitActionManager(); const state = useActionManagerState(props); const { appId, viewId, mid: _mid, errors, view } = state; @@ -37,7 +38,7 @@ const UiKitModal = (props: ActionManagerState): ReactElement => { }; const debouncedBlockAction = useDebouncedCallback((actionId, appId, value, blockId, mid) => { - ActionManager.triggerBlockAction({ + actionManager.triggerBlockAction({ container: { type: UIKitIncomingInteractionContainerType.VIEW, id: viewId, @@ -57,7 +58,7 @@ const UiKitModal = (props: ActionManagerState): ReactElement => { if (Array.isArray(dispatchActionConfig) && dispatchActionConfig.includes('on_character_entered')) { debouncedBlockAction(actionId, appId, value, blockId, mid); } else { - ActionManager.triggerBlockAction({ + actionManager.triggerBlockAction({ container: { type: UIKitIncomingInteractionContainerType.VIEW, id: viewId, @@ -86,7 +87,7 @@ const UiKitModal = (props: ActionManagerState): ReactElement => { const handleSubmit = useMutableCallback((e) => { prevent(e); - ActionManager.triggerSubmitView({ + actionManager.triggerSubmitView({ viewId, appId, payload: { @@ -101,7 +102,7 @@ const UiKitModal = (props: ActionManagerState): ReactElement => { const handleCancel = useMutableCallback((e) => { prevent(e); - ActionManager.triggerCancel({ + actionManager.triggerCancel({ viewId, appId, view: { @@ -113,7 +114,7 @@ const UiKitModal = (props: ActionManagerState): ReactElement => { }); const handleClose = useMutableCallback(() => { - ActionManager.triggerCancel({ + actionManager.triggerCancel({ viewId, appId, view: { diff --git a/apps/meteor/client/views/modal/uikit/hooks/useActionManagerState.ts b/apps/meteor/client/views/modal/uikit/hooks/useActionManagerState.ts index 8f6ee7bb26d2..d2d6d0112015 100644 --- a/apps/meteor/client/views/modal/uikit/hooks/useActionManagerState.ts +++ b/apps/meteor/client/views/modal/uikit/hooks/useActionManagerState.ts @@ -1,7 +1,7 @@ import type { IUIKitSurface } from '@rocket.chat/apps-engine/definition/uikit'; import { useEffect, useState } from 'react'; -import * as ActionManager from '../../../../../app/ui-message/client/ActionManager'; +import { useUiKitActionManager } from '../../../../hooks/useUiKitActionManager'; export type ActionManagerState = { viewId: string; @@ -13,6 +13,7 @@ export type ActionManagerState = { }; export const useActionManagerState = (initialState: ActionManagerState) => { + const actionManager = useUiKitActionManager(); const [state, setState] = useState(initialState); const { viewId } = state; @@ -27,10 +28,10 @@ export const useActionManagerState = (initialState: ActionManagerState) => { setState({ ...data, type, errors }); }; - ActionManager.on(viewId, handleUpdate); + actionManager.on(viewId, handleUpdate); return () => { - ActionManager.off(viewId, handleUpdate); + actionManager.off(viewId, handleUpdate); }; }, [viewId]); diff --git a/apps/meteor/client/views/room/components/body/composer/messageBox/MessageBoxActionsToolbar/ActionsToolbarDropdown.tsx b/apps/meteor/client/views/room/components/body/composer/messageBox/MessageBoxActionsToolbar/ActionsToolbarDropdown.tsx index 406160124696..923996adb838 100644 --- a/apps/meteor/client/views/room/components/body/composer/messageBox/MessageBoxActionsToolbar/ActionsToolbarDropdown.tsx +++ b/apps/meteor/client/views/room/components/body/composer/messageBox/MessageBoxActionsToolbar/ActionsToolbarDropdown.tsx @@ -5,6 +5,7 @@ import type { ComponentProps, ReactNode } from 'react'; import React, { useRef, Fragment } from 'react'; import { messageBox } from '../../../../../../../../app/ui-utils/client'; +import { useMessageboxAppsActionButtons } from '../../../../../../../hooks/useAppActionButtons'; import type { ChatAPI } from '../../../../../../../lib/chats/ChatAPI'; import { useDropdownVisibility } from '../../../../../../../sidebar/header/hooks/useDropdownVisibility'; import { useChat } from '../../../../../contexts/ChatContext'; @@ -35,7 +36,16 @@ const ActionsToolbarDropdown = ({ isRecording, rid, tmid, actions, ...props }: A const { isVisible, toggle } = useDropdownVisibility({ reference, target }); - const groups = messageBox.actions.get(); + const apps = useMessageboxAppsActionButtons(); + + const groups = { + ...(apps.isSuccess && + apps.data.length > 0 && { + Apps: apps.data, + }), + ...messageBox.actions.get(), + }; + const messageBoxActions = Object.entries(groups).map(([name, group]) => { const items = group.map((item) => ({ icon: item.icon, diff --git a/apps/meteor/client/views/room/contextualBar/Apps/AppsWithData.tsx b/apps/meteor/client/views/room/contextualBar/Apps/AppsWithData.tsx index f1f9152c01ea..b50817dae536 100644 --- a/apps/meteor/client/views/room/contextualBar/Apps/AppsWithData.tsx +++ b/apps/meteor/client/views/room/contextualBar/Apps/AppsWithData.tsx @@ -16,7 +16,7 @@ import type { Block } from '@rocket.chat/ui-kit'; import type { Dispatch, SyntheticEvent, ContextType } from 'react'; import React, { memo, useState, useEffect, useReducer } from 'react'; -import { triggerBlockAction, triggerCancel, triggerSubmitView, on, off } from '../../../../../app/ui-message/client/ActionManager'; +import { useUiKitActionManager } from '../../../../hooks/useUiKitActionManager'; import { useTabBarClose } from '../../contexts/ToolboxContext'; import Apps from './Apps'; @@ -101,6 +101,7 @@ const AppsWithData = ({ payload: IUIKitContextualBarInteraction; appId: string; }): JSX.Element => { + const actionManager = useUiKitActionManager(); const closeTabBar = useTabBarClose(); const [state, setState] = useState(payload); @@ -118,10 +119,10 @@ const AppsWithData = ({ setState(data as IUIKitContextualBarInteraction); }; - on(viewId, handleUpdate); + actionManager.on(viewId, handleUpdate); return (): void => { - off(viewId, handleUpdate); + actionManager.off(viewId, handleUpdate); }; }, [state, viewId]); @@ -141,7 +142,7 @@ const AppsWithData = ({ }; const debouncedBlockAction = useDebouncedCallback(({ actionId, appId, value, blockId }: ActionParams) => { - triggerBlockAction({ + actionManager.triggerBlockAction({ container: { type: UIKitIncomingInteractionContainerType.VIEW, id: viewId, @@ -158,7 +159,7 @@ const AppsWithData = ({ if (Array.isArray(dispatchActionConfig) && dispatchActionConfig.includes(InputElementDispatchAction.ON_CHARACTER_ENTERED)) { await debouncedBlockAction({ actionId, appId, value, blockId }); } else { - await triggerBlockAction({ + await actionManager.triggerBlockAction({ container: { type: UIKitIncomingInteractionContainerType.VIEW, id: viewId, @@ -187,7 +188,7 @@ const AppsWithData = ({ const handleSubmit = useMutableCallback((e) => { prevent(e); closeTabBar(); - triggerSubmitView({ + actionManager.triggerSubmitView({ viewId, appId, payload: { @@ -203,7 +204,7 @@ const AppsWithData = ({ const handleCancel = useMutableCallback((e) => { prevent(e); closeTabBar(); - return triggerCancel({ + return actionManager.triggerCancel({ appId, viewId, view: { @@ -217,7 +218,7 @@ const AppsWithData = ({ const handleClose = useMutableCallback((e) => { prevent(e); closeTabBar(); - return triggerCancel({ + return actionManager.triggerCancel({ appId, viewId, view: { diff --git a/apps/meteor/client/views/room/hooks/useAppsContextualBar.ts b/apps/meteor/client/views/room/hooks/useAppsContextualBar.ts index 238ee807744f..6afa6c3a6f84 100644 --- a/apps/meteor/client/views/room/hooks/useAppsContextualBar.ts +++ b/apps/meteor/client/views/room/hooks/useAppsContextualBar.ts @@ -2,7 +2,7 @@ import type { IUIKitContextualBarInteraction } from '@rocket.chat/apps-engine/de import { useRouteParameter } from '@rocket.chat/ui-contexts'; import { useEffect, useState } from 'react'; -import { getUserInteractionPayloadByViewId } from '../../../../app/ui-message/client/ActionManager'; +import { useUiKitActionManager } from '../../../hooks/useUiKitActionManager'; import { useRoom } from '../contexts/RoomContext'; type AppsContextualBarData = { @@ -14,6 +14,7 @@ type AppsContextualBarData = { export const useAppsContextualBar = (): AppsContextualBarData | undefined => { const [payload, setPayload] = useState(); + const actionManager = useUiKitActionManager(); const [appId, setAppId] = useState(); const { _id: roomId } = useRoom(); @@ -22,7 +23,7 @@ export const useAppsContextualBar = (): AppsContextualBarData | undefined => { useEffect(() => { if (viewId) { - setPayload(getUserInteractionPayloadByViewId(viewId) as IUIKitContextualBarInteraction); + setPayload(actionManager.getUserInteractionPayloadByViewId(viewId) as IUIKitContextualBarInteraction); } if (payload?.appId) { @@ -33,7 +34,7 @@ export const useAppsContextualBar = (): AppsContextualBarData | undefined => { setPayload(undefined); setAppId(undefined); }; - }, [viewId, payload?.appId]); + }, [viewId, payload?.appId, actionManager]); if (viewId && payload && appId) { return { diff --git a/apps/meteor/client/views/room/providers/ToolboxProvider.tsx b/apps/meteor/client/views/room/providers/ToolboxProvider.tsx index 27fe6abbb176..865c9c248bf3 100644 --- a/apps/meteor/client/views/room/providers/ToolboxProvider.tsx +++ b/apps/meteor/client/views/room/providers/ToolboxProvider.tsx @@ -4,6 +4,7 @@ import { useUserId, useSetting, useRouter, useRouteParameter } from '@rocket.cha import type { ReactNode } from 'react'; import React, { useMemo } from 'react'; +import { useRoomActionAppsActionButtons } from '../../../hooks/useAppActionButtons'; import type { ToolboxContextValue } from '../contexts/ToolboxContext'; import { ToolboxContext } from '../contexts/ToolboxContext'; import type { Store } from '../lib/Toolbox/generator'; @@ -103,9 +104,11 @@ const ToolboxProvider = ({ children, room }: { children: ReactNode; room: IRoom [listen, list, activeTabBar, open, close, openRoomInfo], ); + const appActions = useRoomActionAppsActionButtons(); + return ( - {actions + {[...actions, ...(appActions.data ?? [])] .filter( ([, action]) => uid || (allowAnonymousRead && action.hasOwnProperty('anonymous') && (action as ToolboxActionConfig).anonymous), ) diff --git a/apps/meteor/ee/client/apps/communication/websockets.js b/apps/meteor/ee/client/apps/communication/websockets.js index efb76a46f7f6..e8060539a665 100644 --- a/apps/meteor/ee/client/apps/communication/websockets.js +++ b/apps/meteor/ee/client/apps/communication/websockets.js @@ -1,7 +1,6 @@ import { Emitter } from '@rocket.chat/emitter'; import { CachedCollectionManager } from '../../../../app/ui-cached-collection/client'; -import { loadButtons } from '../../../../app/ui-message/client/ActionButtonSyncer'; import { slashCommands } from '../../../../app/utils/client'; import { sdk } from '../../../../app/utils/client/lib/SDKClient'; @@ -72,5 +71,5 @@ export class AppWebsocketReceiver extends Emitter { delete slashCommands.commands[command]; }; - onActionsChanged = () => loadButtons(); + // onActionsChanged = () => loadButtons(); } diff --git a/ee/packages/ddp-client/src/types/streams.ts b/ee/packages/ddp-client/src/types/streams.ts index 5892371afb1d..fc2fc381570e 100644 --- a/ee/packages/ddp-client/src/types/streams.ts +++ b/ee/packages/ddp-client/src/types/streams.ts @@ -1,4 +1,5 @@ import type { ISetting as AppsSetting } from '@rocket.chat/apps-engine/definition/settings'; +import type { IUIKitInteraction } from '@rocket.chat/apps-engine/definition/uikit'; import type { AppStatus } from '@rocket.chat/apps-engine/definition/AppStatus'; import type { IMessage, @@ -136,7 +137,7 @@ export interface StreamerEvents { { key: `${string}/notification`; args: [INotificationDesktop] }, { key: `${string}/voip.events`; args: [VoipEventDataSignature] }, { key: `${string}/call.hangup`; args: [{ roomId: string }] }, - { key: `${string}/uiInteraction`; args: [unknown] }, + { key: `${string}/uiInteraction`; args: [IUIKitInteraction] }, { key: `${string}/video-conference`; args: [{ action: string; params: { callId: VideoConference['_id']; uid: IUser['_id']; rid: IRoom['_id'] } }]; From bd231a06ba509f17882fb8aaf4c25d418c28fe2c Mon Sep 17 00:00:00 2001 From: Guilherme Gazzo Date: Tue, 11 Jul 2023 18:22:38 -0300 Subject: [PATCH 004/342] refactor: Remove Accountbox usage (#29786) --- apps/meteor/app/lib/README.md | 13 --- apps/meteor/app/livechat/client/ui.js | 12 +-- apps/meteor/app/ui-utils/client/index.ts | 1 - .../app/ui-utils/client/lib/AccountBox.ts | 32 ------- .../actions/hooks/useAdministrationItems.tsx | 84 +++++++++++++------ .../actions/hooks/useAdministrationMenu.tsx | 70 ++-------------- .../header/actions/hooks/useAuditItems.tsx | 19 +++-- .../sidebar/header/hooks/useStatusItems.tsx | 10 +-- apps/meteor/client/startup/iframeCommands.ts | 2 +- 9 files changed, 86 insertions(+), 157 deletions(-) diff --git a/apps/meteor/app/lib/README.md b/apps/meteor/app/lib/README.md index 708a9a21790e..5201c4cadf0e 100644 --- a/apps/meteor/app/lib/README.md +++ b/apps/meteor/app/lib/README.md @@ -47,19 +47,6 @@ settingsRegistry.addGroup('Settings_Group', function() { * `enableQuery` - Only enable this setting if the correspondent setting has the value specified * `alert` - Shows an alert message with the given text -### AccountBox - -You can add items to the left upper corner drop menu: -```javascript -AccountBox.addItem({ - name: 'Livechat', - icon: 'icon-chat-empty', - class: 'livechat-manager', - condition: () => { - return RocketChat.authz.hasPermission('view-livechat-manager'); - } -}); -``` ### Functions n/a diff --git a/apps/meteor/app/livechat/client/ui.js b/apps/meteor/app/livechat/client/ui.js index 614b410cda2e..5d8cc1696d44 100644 --- a/apps/meteor/app/livechat/client/ui.js +++ b/apps/meteor/app/livechat/client/ui.js @@ -1,14 +1,4 @@ -import { settings } from '../../settings/client'; -import { hasAllPermission } from '../../authorization/client'; -import { AccountBox, MessageTypes } from '../../ui-utils/client'; - -AccountBox.addItem({ - name: 'Omnichannel', - icon: 'headset', - href: '/omnichannel/current', - sideNav: 'omnichannelFlex', - condition: () => settings.get('Livechat_enabled') && hasAllPermission('view-livechat-manager'), -}); +import { MessageTypes } from '../../ui-utils/client'; MessageTypes.registerType({ id: 'livechat-close', diff --git a/apps/meteor/app/ui-utils/client/index.ts b/apps/meteor/app/ui-utils/client/index.ts index 83a1057a7287..aaa5a0825706 100644 --- a/apps/meteor/app/ui-utils/client/index.ts +++ b/apps/meteor/app/ui-utils/client/index.ts @@ -1,6 +1,5 @@ import './lib/messageActionDefault'; -export { AccountBox } from './lib/AccountBox'; export { MessageAction } from './lib/MessageAction'; export { messageBox } from './lib/messageBox'; export { readMessage } from './lib/readMessages'; diff --git a/apps/meteor/app/ui-utils/client/lib/AccountBox.ts b/apps/meteor/app/ui-utils/client/lib/AccountBox.ts index 3ed3a60b3bf6..c7bda9df1659 100644 --- a/apps/meteor/app/ui-utils/client/lib/AccountBox.ts +++ b/apps/meteor/app/ui-utils/client/lib/AccountBox.ts @@ -1,12 +1,9 @@ import type { IUIActionButton, IUActionButtonWhen } from '@rocket.chat/apps-engine/definition/ui/IUIActionButtonDescriptor'; import type { UserStatus } from '@rocket.chat/core-typings'; -import { ReactiveVar } from 'meteor/reactive-var'; -import { Tracker } from 'meteor/tracker'; import type { TranslationKey, LocationPathname } from '@rocket.chat/ui-contexts'; import type { Icon } from '@rocket.chat/fuselage'; import type { ComponentProps } from 'react'; -import { applyDropdownActionButtonFilters } from '../../../ui-message/client/actionButtons/lib/applyButtonFilters'; import { sdk } from '../../../utils/client/lib/SDKClient'; export interface IAppAccountBoxItem extends IUIActionButton { @@ -30,38 +27,9 @@ export type AccountBoxItem = { export const isAppAccountBoxItem = (item: IAppAccountBoxItem | AccountBoxItem): item is IAppAccountBoxItem => 'isAppButtonItem' in item; class AccountBoxBase { - private items = new ReactiveVar([]); - public setStatus(status: UserStatus, statusText?: string): any { return sdk.rest.post('/v1/users.setStatus', { status, message: statusText }); } - - public async addItem(newItem: IAppAccountBoxItem): Promise { - Tracker.nonreactive(() => { - const actual = this.items.get(); - actual.push(newItem); - this.items.set(actual); - }); - } - - public async deleteItem(item: IAppAccountBoxItem): Promise { - Tracker.nonreactive(() => { - const actual = this.items.get(); - const itemIndex = actual.findIndex((actualItem: IAppAccountBoxItem) => actualItem.appId === item.appId); - actual.splice(itemIndex, 1); - this.items.set(actual); - }); - } - - public getItems(): (IAppAccountBoxItem | AccountBoxItem)[] { - return this.items.get().filter((item: IAppAccountBoxItem | AccountBoxItem) => { - if ('condition' in item) { - return item.condition(); - } - - return applyDropdownActionButtonFilters(item); - }); - } } export const AccountBox = new AccountBoxBase(); diff --git a/apps/meteor/client/sidebar/header/actions/hooks/useAdministrationItems.tsx b/apps/meteor/client/sidebar/header/actions/hooks/useAdministrationItems.tsx index 97bcb291a37e..c0dc83fb62c1 100644 --- a/apps/meteor/client/sidebar/header/actions/hooks/useAdministrationItems.tsx +++ b/apps/meteor/client/sidebar/header/actions/hooks/useAdministrationItems.tsx @@ -1,8 +1,16 @@ -import { useTranslation, useRoute, useMethod, useSetModal, useRole, useRouter } from '@rocket.chat/ui-contexts'; +import { + useTranslation, + useRoute, + useMethod, + useSetModal, + useRole, + useRouter, + useAtLeastOnePermission, + usePermission, +} from '@rocket.chat/ui-contexts'; import { useQuery } from '@tanstack/react-query'; import React from 'react'; -import type { AccountBoxItem } from '../../../../../app/ui-utils/client/lib/AccountBox'; import type { UpgradeTabVariant } from '../../../../../lib/upgradeTab'; import { getUpgradeTabLabel, isFullyFeature } from '../../../../../lib/upgradeTab'; import Emoji from '../../../../components/Emoji'; @@ -10,17 +18,45 @@ import type { GenericMenuItemProps } from '../../../../components/GenericMenu/Ge import RegisterWorkspaceModal from '../../../../views/admin/cloud/modals/RegisterWorkspaceModal'; import { useUpgradeTabParams } from '../../../../views/hooks/useUpgradeTabParams'; -type useAdministrationItemProps = { - accountBoxItems: AccountBoxItem[]; - showWorkspace: boolean; -}; -export const useAdministrationItems = ({ accountBoxItems, showWorkspace }: useAdministrationItemProps): GenericMenuItemProps[] => { +const ADMIN_PERMISSIONS = [ + 'view-statistics', + 'run-import', + 'view-user-administration', + 'view-room-administration', + 'create-invite-links', + 'manage-cloud', + 'view-logs', + 'manage-sounds', + 'view-federation-data', + 'manage-email-inbox', + 'manage-emoji', + 'manage-outgoing-integrations', + 'manage-own-outgoing-integrations', + 'manage-incoming-integrations', + 'manage-own-incoming-integrations', + 'manage-oauth-apps', + 'access-mailer', + 'manage-user-status', + 'access-permissions', + 'access-setting-permissions', + 'view-privileged-setting', + 'edit-privileged-setting', + 'manage-selected-settings', + 'view-engagement-dashboard', + 'view-moderation-console', +]; + +export const useAdministrationItems = (): GenericMenuItemProps[] => { const router = useRouter(); const t = useTranslation(); + const shouldShowAdminMenu = useAtLeastOnePermission(ADMIN_PERMISSIONS); + const { tabType, trialEndDate, isLoading } = useUpgradeTabParams(); const shouldShowEmoji = isFullyFeature(tabType); + const label = getUpgradeTabLabel(tabType); + const isAdmin = useRole('admin'); const setModal = useSetModal(); @@ -36,8 +72,18 @@ export const useAdministrationItems = ({ accountBoxItems, showWorkspace }: useAd const adminRoute = useRoute('admin-index'); const upgradeRoute = useRoute('upgrade'); const cloudRoute = useRoute('cloud'); + + const omnichannel = usePermission('view-livechat-manager'); + const showUpgradeItem = !isLoading && tabType; + const omnichannelItem: GenericMenuItemProps = { + id: 'omnichannel', + content: t('Omnichannel'), + icon: 'headset', + onClick: () => router.navigate('/omnichannel/current'), + }; + const upgradeItem: GenericMenuItemProps = { id: 'showUpgradeItem', content: ( @@ -71,24 +117,10 @@ export const useAdministrationItems = ({ accountBoxItems, showWorkspace }: useAd }, }; - const accountBoxItem: GenericMenuItemProps[] = accountBoxItems.map((item, key) => { - const action = () => { - if (item.href) { - router.navigate(item.href); - } - }; - return { - id: `account-box-item-${key}`, - content: t(item.name), - icon: item.icon, - onClick: action, - }; - }); - return [ - ...(showUpgradeItem ? [upgradeItem] : []), - ...(isAdmin ? [adminItem] : []), - ...(showWorkspace ? [workspaceItem] : []), - ...(accountBoxItems.length ? accountBoxItem : []), - ]; + showUpgradeItem && upgradeItem, + isAdmin && adminItem, + omnichannel && omnichannelItem, + shouldShowAdminMenu && workspaceItem, + ].filter(Boolean) as GenericMenuItemProps[]; }; diff --git a/apps/meteor/client/sidebar/header/actions/hooks/useAdministrationMenu.tsx b/apps/meteor/client/sidebar/header/actions/hooks/useAdministrationMenu.tsx index 72b9b90419a5..5be021cf0b7e 100644 --- a/apps/meteor/client/sidebar/header/actions/hooks/useAdministrationMenu.tsx +++ b/apps/meteor/client/sidebar/header/actions/hooks/useAdministrationMenu.tsx @@ -1,72 +1,20 @@ -import { useMutableCallback } from '@rocket.chat/fuselage-hooks'; -import { useAtLeastOnePermission, usePermission, useTranslation } from '@rocket.chat/ui-contexts'; +import { useTranslation } from '@rocket.chat/ui-contexts'; -import { AccountBox } from '../../../../../app/ui-utils/client'; -import type { IAppAccountBoxItem, AccountBoxItem } from '../../../../../app/ui-utils/client/lib/AccountBox'; -import { isAppAccountBoxItem } from '../../../../../app/ui-utils/client/lib/AccountBox'; -import { useHasLicenseModule } from '../../../../../ee/client/hooks/useHasLicenseModule'; -import { useReactiveValue } from '../../../../hooks/useReactiveValue'; +import type { GenericMenuItemProps } from '../../../../components/GenericMenu/GenericMenuItem'; import { useAdministrationItems } from './useAdministrationItems'; import { useAppsItems } from './useAppsItems'; import { useAuditItems } from './useAuditItems'; -const ADMIN_PERMISSIONS = [ - 'view-statistics', - 'run-import', - 'view-user-administration', - 'view-room-administration', - 'create-invite-links', - 'manage-cloud', - 'view-logs', - 'manage-sounds', - 'view-federation-data', - 'manage-email-inbox', - 'manage-emoji', - 'manage-outgoing-integrations', - 'manage-own-outgoing-integrations', - 'manage-incoming-integrations', - 'manage-own-incoming-integrations', - 'manage-oauth-apps', - 'access-mailer', - 'manage-user-status', - 'access-permissions', - 'access-setting-permissions', - 'view-privileged-setting', - 'edit-privileged-setting', - 'manage-selected-settings', - 'view-engagement-dashboard', - 'view-moderation-console', -]; - export const useAdministrationMenu = () => { const t = useTranslation(); - const getAccountBoxItems = useMutableCallback(() => AccountBox.getItems()); - const accountBoxItems = useReactiveValue(getAccountBoxItems); - - const hasAuditLicense = useHasLicenseModule('auditing') === true; - const hasManageAppsPermission = usePermission('manage-apps'); - const hasAccessMarketplacePermission = usePermission('access-marketplace'); - const hasAdminPermission = useAtLeastOnePermission(ADMIN_PERMISSIONS); - const hasAuditPermission = usePermission('can-audit') && hasAuditLicense; - const hasAuditLogPermission = usePermission('can-audit-log') && hasAuditLicense; - - const appBoxItems = accountBoxItems.filter((item): item is IAppAccountBoxItem => isAppAccountBoxItem(item)); - const adminBoxItems = accountBoxItems.filter((item): item is AccountBoxItem => !isAppAccountBoxItem(item)); - const showAdmin = hasAdminPermission || !!adminBoxItems.length; - const showAudit = hasAuditPermission || hasAuditLogPermission; - const showWorkspace = hasAdminPermission; - const showApps = hasAccessMarketplacePermission || hasManageAppsPermission || !!appBoxItems.length; - - const administrationItems = useAdministrationItems({ accountBoxItems: adminBoxItems, showWorkspace }); + const administrationItems = useAdministrationItems(); const appItems = useAppsItems(); - const auditItems = useAuditItems({ showAudit: hasAuditPermission, showAuditLog: hasAuditLogPermission }); - - const sections = [ - { title: t('Administration'), items: administrationItems, permission: showAdmin }, - { title: t('Apps'), items: appItems, permission: showApps }, - { title: t('Audit'), items: auditItems, permission: showAudit }, - ]; + const auditItems = useAuditItems(); - return sections.filter(({ permission }) => permission); + return [ + administrationItems.length && { title: t('Administration'), items: administrationItems }, + appItems.length && { title: t('Apps'), items: appItems }, + auditItems.length && { title: t('Audit'), items: auditItems }, + ].filter(Boolean) as Array<{ title: string; items: GenericMenuItemProps[] }>; }; diff --git a/apps/meteor/client/sidebar/header/actions/hooks/useAuditItems.tsx b/apps/meteor/client/sidebar/header/actions/hooks/useAuditItems.tsx index 3ed3ba94dcb9..07b2b9bb11c3 100644 --- a/apps/meteor/client/sidebar/header/actions/hooks/useAuditItems.tsx +++ b/apps/meteor/client/sidebar/header/actions/hooks/useAuditItems.tsx @@ -1,18 +1,23 @@ -import { useTranslation, useRoute } from '@rocket.chat/ui-contexts'; +import { useTranslation, useRoute, usePermission } from '@rocket.chat/ui-contexts'; +import { useHasLicenseModule } from '../../../../../ee/client/hooks/useHasLicenseModule'; import type { GenericMenuItemProps } from '../../../../components/GenericMenu/GenericMenuItem'; -type useAuditItemsProps = { - showAudit: boolean; - showAuditLog: boolean; -}; +export const useAuditItems = (): GenericMenuItemProps[] => { + const hasAuditLicense = useHasLicenseModule('auditing') === true; + + const hasAuditPermission = usePermission('can-audit') && hasAuditLicense; + const hasAuditLogPermission = usePermission('can-audit-log') && hasAuditLicense; -export const useAuditItems = ({ showAudit, showAuditLog }: useAuditItemsProps): GenericMenuItemProps[] => { const t = useTranslation(); const auditHomeRoute = useRoute('audit-home'); const auditSettingsRoute = useRoute('audit-log'); + if (!hasAuditPermission && !hasAuditLogPermission) { + return []; + } + const auditMessageItem: GenericMenuItemProps = { id: 'messages', icon: 'document-eye', @@ -26,5 +31,5 @@ export const useAuditItems = ({ showAudit, showAuditLog }: useAuditItemsProps): onClick: () => auditSettingsRoute.push(), }; - return [...(showAudit ? [auditMessageItem] : []), ...(showAuditLog ? [auditLogItem] : [])]; + return [hasAuditPermission && auditMessageItem, hasAuditLogPermission && auditLogItem].filter(Boolean) as GenericMenuItemProps[]; }; diff --git a/apps/meteor/client/sidebar/header/hooks/useStatusItems.tsx b/apps/meteor/client/sidebar/header/hooks/useStatusItems.tsx index 8621846456f1..ca45e53bf5e2 100644 --- a/apps/meteor/client/sidebar/header/hooks/useStatusItems.tsx +++ b/apps/meteor/client/sidebar/header/hooks/useStatusItems.tsx @@ -2,10 +2,9 @@ import type { IUser, ValueOf } from '@rocket.chat/core-typings'; import { UserStatus as UserStatusEnum } from '@rocket.chat/core-typings'; import { Box } from '@rocket.chat/fuselage'; import type { TranslationKey } from '@rocket.chat/ui-contexts'; -import { useSetting, useTranslation } from '@rocket.chat/ui-contexts'; +import { useEndpoint, useSetting, useTranslation } from '@rocket.chat/ui-contexts'; import React from 'react'; -import { AccountBox } from '../../../../app/ui-utils/client'; import { userStatus } from '../../../../app/user-status/client'; import { callbacks } from '../../../../lib/callbacks'; import type { GenericMenuItemProps } from '../../../components/GenericMenu/GenericMenuItem'; @@ -27,9 +26,10 @@ const translateStatusName = (t: ReturnType, status: (type export const useStatusItems = (user: IUser): GenericMenuItemProps[] => { const t = useTranslation(); const presenceDisabled = useSetting('Presence_broadcast_disabled'); + const setStatus = useEndpoint('POST', '/v1/users.setStatus'); - const setStatus = (status: (typeof userStatus.list)['']): void => { - AccountBox.setStatus(status.statusType, !isDefaultStatus(status.id) ? status.name : ''); + const setStatusAction = (status: (typeof userStatus.list)['']): void => { + setStatus({ status: status.statusType, message: !isDefaultStatus(status.id) ? status.name : '' }); void callbacks.run('userStatusManuallySet', status); }; @@ -64,7 +64,7 @@ export const useStatusItems = (user: IUser): GenericMenuItemProps[] => { id: status.id, status: , content: , - onClick: () => setStatus(status), + onClick: () => setStatusAction(status), disabled: presenceDisabled, }; }); diff --git a/apps/meteor/client/startup/iframeCommands.ts b/apps/meteor/client/startup/iframeCommands.ts index 3ecf578587bb..25836a38f5ca 100644 --- a/apps/meteor/client/startup/iframeCommands.ts +++ b/apps/meteor/client/startup/iframeCommands.ts @@ -5,7 +5,7 @@ import { Meteor } from 'meteor/meteor'; import { ServiceConfiguration } from 'meteor/service-configuration'; import { settings } from '../../app/settings/client'; -import { AccountBox } from '../../app/ui-utils/client'; +import { AccountBox } from '../../app/ui-utils/client/lib/AccountBox'; import { sdk } from '../../app/utils/client/lib/SDKClient'; import { callbacks } from '../../lib/callbacks'; import { capitalize, ltrim, rtrim } from '../../lib/utils/stringUtils'; From 1246a21648a22e03665270dbf7b6d6b2eed25f50 Mon Sep 17 00:00:00 2001 From: csuadev <72958726+csuadev@users.noreply.github.com> Date: Wed, 12 Jul 2023 11:11:03 -0500 Subject: [PATCH 005/342] feat: Add missing variants to UIKit button (#29654) Co-authored-by: Tiago Evangelista Pinto <17487063+tiagoevanp@users.noreply.github.com> --- .changeset/cuddly-ties-bake.md | 6 ++ .../src/elements/ButtonElement.tsx | 37 +++++---- .../src/Payload/BlocksTree.ts | 35 ++++++++ .../src/Payload/action/button.ts | 81 +++++++++++++++++++ .../src/Payload/section/button.ts | 67 +++++++++++++++ 5 files changed, 209 insertions(+), 17 deletions(-) create mode 100644 .changeset/cuddly-ties-bake.md diff --git a/.changeset/cuddly-ties-bake.md b/.changeset/cuddly-ties-bake.md new file mode 100644 index 000000000000..d912d2969d75 --- /dev/null +++ b/.changeset/cuddly-ties-bake.md @@ -0,0 +1,6 @@ +--- +"@rocket.chat/fuselage-ui-kit": minor +"@rocket.chat/uikit-playground": minor +--- + +feat: Add missing variants to UIKit button diff --git a/packages/fuselage-ui-kit/src/elements/ButtonElement.tsx b/packages/fuselage-ui-kit/src/elements/ButtonElement.tsx index 8f68a5bef4d6..c7fe05971e27 100644 --- a/packages/fuselage-ui-kit/src/elements/ButtonElement.tsx +++ b/packages/fuselage-ui-kit/src/elements/ButtonElement.tsx @@ -13,28 +13,28 @@ const ButtonElement = ({ surfaceRenderer, }: ButtonElementProps): ReactElement => { const [{ loading }, action] = useUiKitState(block, context); + const { style, url, text, value, secondary } = block; - if (block.url) { + if (url) { return ( ); @@ -42,18 +42,21 @@ const ButtonElement = ({ return ( ); diff --git a/packages/uikit-playground/src/Payload/BlocksTree.ts b/packages/uikit-playground/src/Payload/BlocksTree.ts index 67843793790b..1b561d5448d8 100644 --- a/packages/uikit-playground/src/Payload/BlocksTree.ts +++ b/packages/uikit-playground/src/Payload/BlocksTree.ts @@ -2,8 +2,12 @@ import type { Item } from '../Components/DropDown/types'; import { actionWithButtonDefault, actionWithButtonPrimary, + actionWithButtonSecondary, actionWithButtonDanger, actionWithButtonAsLink, + actionWithButtonWarning, + actionWithButtonSuccess, + actionWithButtonSecondaryWithVariant, actionWithMenu, // actionWithImage, // actionWithSingleLineInput, @@ -42,6 +46,9 @@ import { sectionWithButtonDefault, sectionWithButtonPrimary, sectionWithButtonDanger, + sectionWithButtonWarning, + sectionWithButtonSuccess, + sectionWithButtonSecondaryWithVariant, sectionWithButtonAsLink, sectionWithImage, sectionWithMenu, @@ -63,10 +70,26 @@ const BlocksTree: Item = [ label: 'primary', payload: actionWithButtonPrimary, }, + { + label: 'secondary', + payload: actionWithButtonSecondary, + }, { label: 'danger', payload: actionWithButtonDanger, }, + { + label: 'warning', + payload: actionWithButtonWarning, + }, + { + label: 'success', + payload: actionWithButtonSuccess, + }, + { + label: 'secondary with variant', + payload: actionWithButtonSecondaryWithVariant, + }, { label: 'as Link', payload: actionWithButtonAsLink, @@ -136,6 +159,18 @@ const BlocksTree: Item = [ label: 'danger', payload: sectionWithButtonDanger, }, + { + label: 'warning', + payload: sectionWithButtonWarning, + }, + { + label: 'success', + payload: sectionWithButtonSuccess, + }, + { + label: 'secondary with variant', + payload: sectionWithButtonSecondaryWithVariant, + }, { label: 'as Link', payload: sectionWithButtonAsLink, diff --git a/packages/uikit-playground/src/Payload/action/button.ts b/packages/uikit-playground/src/Payload/action/button.ts index b4e79b740008..cc54168bb1f2 100644 --- a/packages/uikit-playground/src/Payload/action/button.ts +++ b/packages/uikit-playground/src/Payload/action/button.ts @@ -39,6 +39,26 @@ export const actionWithButtonPrimary: readonly LayoutBlock[] = [ }, ]; +export const actionWithButtonSecondary: readonly LayoutBlock[] = [ + { + type: 'actions', + elements: [ + { + type: 'button', + appId: 'app-id', + blockId: 'block-id', + actionId: 'action-id', + text: { + type: 'plain_text', + text: 'Click Me', + emoji: true, + }, + secondary: true, + }, + ], + }, +]; + export const actionWithButtonDanger: readonly LayoutBlock[] = [ { type: 'actions', @@ -59,6 +79,67 @@ export const actionWithButtonDanger: readonly LayoutBlock[] = [ }, ]; +export const actionWithButtonSuccess: readonly LayoutBlock[] = [ + { + type: 'actions', + elements: [ + { + type: 'button', + appId: 'app-id', + blockId: 'block-id', + actionId: 'action-id', + text: { + type: 'plain_text', + text: 'Click Me', + emoji: true, + }, + style: 'success', + }, + ], + }, +]; + +export const actionWithButtonWarning: readonly LayoutBlock[] = [ + { + type: 'actions', + elements: [ + { + type: 'button', + appId: 'app-id', + blockId: 'block-id', + actionId: 'action-id', + text: { + type: 'plain_text', + text: 'Click Me', + emoji: true, + }, + style: 'warning', + }, + ], + }, +]; + +export const actionWithButtonSecondaryWithVariant: readonly LayoutBlock[] = [ + { + type: 'actions', + elements: [ + { + type: 'button', + appId: 'app-id', + blockId: 'block-id', + actionId: 'action-id', + text: { + type: 'plain_text', + text: 'Click Me', + emoji: true, + }, + style: 'danger', + secondary: true, + }, + ], + }, +]; + export const actionWithButtonAsLink: readonly LayoutBlock[] = [ { type: 'actions', diff --git a/packages/uikit-playground/src/Payload/section/button.ts b/packages/uikit-playground/src/Payload/section/button.ts index 63ed1140e648..f3538ca40780 100644 --- a/packages/uikit-playground/src/Payload/section/button.ts +++ b/packages/uikit-playground/src/Payload/section/button.ts @@ -65,6 +65,73 @@ export const sectionWithButtonDanger: readonly LayoutBlock[] = [ }, ]; +export const sectionWithButtonSuccess: readonly LayoutBlock[] = [ + { + type: 'section', + text: { + type: 'mrkdwn', + text: 'This is a section block with a button.', + }, + accessory: { + type: 'button', + appId: 'app-id', + blockId: 'block-id', + actionId: 'action-id', + text: { + type: 'plain_text', + text: 'Click Me', + emoji: true, + }, + style: 'success', + }, + }, +]; + +export const sectionWithButtonWarning: readonly LayoutBlock[] = [ + { + type: 'section', + text: { + type: 'mrkdwn', + text: 'This is a section block with a button.', + }, + accessory: { + type: 'button', + appId: 'app-id', + blockId: 'block-id', + actionId: 'action-id', + text: { + type: 'plain_text', + text: 'Click Me', + emoji: true, + }, + style: 'warning', + }, + }, +]; + +export const sectionWithButtonSecondaryWithVariant: readonly LayoutBlock[] = [ + { + type: 'section', + text: { + type: 'mrkdwn', + text: 'This is a section block with a button.', + }, + accessory: { + type: 'button', + appId: 'app-id', + blockId: 'block-id', + actionId: 'action-id', + text: { + type: 'plain_text', + text: 'Click Me', + emoji: true, + }, + style: 'danger', + secondary: true, + }, + }, +]; + export const sectionWithButtonAsLink: readonly LayoutBlock[] = [ { type: 'section', From 52a1aa94eb94128cd02a950d73f17b250c24ab20 Mon Sep 17 00:00:00 2001 From: Murtaza Patrawala <34130764+murtaza98@users.noreply.github.com> Date: Thu, 13 Jul 2023 12:55:31 +0530 Subject: [PATCH 006/342] chore: Improve system messages for omni-visitor abandonment feature (#29724) Co-authored-by: Kevin Aleman <11577696+KevLehman@users.noreply.github.com> --- .changeset/hip-mugs-promise.md | 5 ++ .../server/lib/VisitorInactivityMonitor.ts | 60 ++++++++++++------- .../rocketchat-i18n/i18n/en.i18n.json | 3 +- 3 files changed, 47 insertions(+), 21 deletions(-) create mode 100644 .changeset/hip-mugs-promise.md diff --git a/.changeset/hip-mugs-promise.md b/.changeset/hip-mugs-promise.md new file mode 100644 index 000000000000..7100fec026e3 --- /dev/null +++ b/.changeset/hip-mugs-promise.md @@ -0,0 +1,5 @@ +--- +"@rocket.chat/meteor": patch +--- + +improve: System messages for omni-visitor abandonment feature diff --git a/apps/meteor/ee/app/livechat-enterprise/server/lib/VisitorInactivityMonitor.ts b/apps/meteor/ee/app/livechat-enterprise/server/lib/VisitorInactivityMonitor.ts index 845b52be2d16..e4650fc21f3d 100644 --- a/apps/meteor/ee/app/livechat-enterprise/server/lib/VisitorInactivityMonitor.ts +++ b/apps/meteor/ee/app/livechat-enterprise/server/lib/VisitorInactivityMonitor.ts @@ -1,4 +1,4 @@ -import type { IOmnichannelRoom, IUser } from '@rocket.chat/core-typings'; +import type { ILivechatVisitor, IOmnichannelRoom, IUser } from '@rocket.chat/core-typings'; import { LivechatVisitors, LivechatRooms, LivechatDepartment, Users } from '@rocket.chat/models'; import { OmnichannelEEService } from '@rocket.chat/core-services'; import { cronJobs } from '@rocket.chat/cron'; @@ -70,14 +70,13 @@ export class VisitorInactivityMonitor { _initializeMessageCache() { this.messageCache.clear(); - this.messageCache.set('default', settings.get('Livechat_abandoned_rooms_closed_custom_message') || i18n.t('Closed_automatically')); } async _getDepartmentAbandonedCustomMessage(departmentId: string) { this.logger.debug(`Getting department abandoned custom message for department ${departmentId}`); - if (this.messageCache.has('departmentId')) { + if (this.messageCache.has(departmentId)) { this.logger.debug(`Using cached department abandoned custom message for department ${departmentId}`); - return this.messageCache.get('departmentId'); + return this.messageCache.get(departmentId); } const department = await LivechatDepartment.findOneById(departmentId); if (!department) { @@ -91,7 +90,7 @@ export class VisitorInactivityMonitor { async closeRooms(room: IOmnichannelRoom) { this.logger.debug(`Closing room ${room._id}`); - let comment = this.messageCache.get('default'); + let comment = await this.getDefaultAbandonedCustomMessage('close', room.v._id); if (room.departmentId) { comment = (await this._getDepartmentAbandonedCustomMessage(room.departmentId)) || comment; } @@ -105,22 +104,8 @@ export class VisitorInactivityMonitor { async placeRoomOnHold(room: IOmnichannelRoom) { this.logger.debug(`Placing room ${room._id} on hold`); - const timeout = settings.get('Livechat_visitor_inactivity_timeout'); - const { v: { _id: visitorId } = {} } = room; - if (!visitorId) { - this.logger.debug(`Room ${room._id} does not have a visitor`); - throw new Error('error-invalid_visitor'); - } - - const visitor = await LivechatVisitors.findOneById(visitorId); - if (!visitor) { - this.logger.debug(`Room ${room._id} does not have a visitor`); - throw new Error('error-invalid_visitor'); - } - - const guest = visitor.name || visitor.username; - const comment = i18n.t('Omnichannel_On_Hold_due_to_inactivity', { guest, timeout }); + const comment = await this.getDefaultAbandonedCustomMessage('on-hold', room.v._id); const result = await Promise.allSettled([ OmnichannelEEService.placeRoomOnHold(room, comment, this.user), @@ -170,4 +155,39 @@ export class VisitorInactivityMonitor { this._initializeMessageCache(); } + + private async getDefaultAbandonedCustomMessage(abandonmentAction: 'close' | 'on-hold', visitorId: string) { + const visitor = await LivechatVisitors.findOneById>(visitorId, { + projection: { + name: 1, + username: 1, + }, + }); + if (!visitor) { + this.logger.error({ + msg: 'Error getting default abandoned custom message: visitor not found', + visitorId, + }); + throw new Error('error-invalid_visitor'); + } + + const timeout = settings.get('Livechat_visitor_inactivity_timeout'); + + const guest = visitor.name || visitor.username; + + if (abandonmentAction === 'on-hold') { + return i18n.t('Omnichannel_On_Hold_due_to_inactivity', { + guest, + timeout, + }); + } + + return ( + settings.get('Livechat_abandoned_rooms_closed_custom_message') || + i18n.t('Omnichannel_chat_closed_due_to_inactivity', { + guest, + timeout, + }) + ); + } } diff --git a/apps/meteor/packages/rocketchat-i18n/i18n/en.i18n.json b/apps/meteor/packages/rocketchat-i18n/i18n/en.i18n.json index 6fe395013797..9f313e58c564 100644 --- a/apps/meteor/packages/rocketchat-i18n/i18n/en.i18n.json +++ b/apps/meteor/packages/rocketchat-i18n/i18n/en.i18n.json @@ -3083,6 +3083,7 @@ "Livechat_offline": "Omnichannel offline", "Livechat_offline_message_sent": "Livechat offline message sent", "Livechat_OfflineMessageToChannel_enabled": "Send Livechat offline messages to a channel", + "Omnichannel_chat_closed_due_to_inactivity": "The chat was automatically closed because we haven't received any reply from {{guest}} in {{timeout}} seconds", "Omnichannel_on_hold_chat_resumed": "On Hold Chat Resumed: {{comment}}", "Omnichannel_on_hold_chat_automatically": "The chat was automatically resumed from On Hold upon receiving a new message from {{guest}}", "Omnichannel_on_hold_chat_resumed_manually": "The chat was manually resumed from On Hold by {{user}}", @@ -5947,4 +5948,4 @@ "Uninstall_grandfathered_app": "Uninstall {{appName}}?", "App_will_lose_grandfathered_status": "**This {{context}} app will lose its grandfathered status.** \n \nWorkspaces on Community Edition can have up to {{limit}} {{context}} apps enabled. Grandfathered apps count towards the limit but the limit is not applied to them.", "Theme_Appearence": "Theme Appearence" -} \ No newline at end of file +} From f945af67eb38aca9a116450bed39091acbc54229 Mon Sep 17 00:00:00 2001 From: Douglas Fabris Date: Thu, 13 Jul 2023 13:38:53 -0300 Subject: [PATCH 007/342] feat: `Navbar lvl 1` (#29671) Co-authored-by: Guilherme Gazzo <5263975+ggazzo@users.noreply.github.com> --- .../components/Navbar/Navbar.stories.tsx | 13 +++ .../client/components/Navbar/Navbar.tsx | 13 +++ .../client/components/Navbar/NavbarAction.tsx | 10 +++ .../client/components/Navbar/NavbarBadge.tsx | 11 +++ apps/meteor/client/components/Navbar/index.ts | 3 + apps/meteor/client/navbar/Navbar.tsx | 22 +++++ .../actions/NavbarAdministrationAction.tsx | 34 ++++++++ .../navbar/actions/NavbarAuditAction.tsx | 35 ++++++++ .../navbar/actions/NavbarHomeAction.tsx | 35 ++++++++ .../actions/NavbarMarketplaceAction.tsx | 45 ++++++++++ .../navbar/actions/NavbarUserAction.tsx | 20 +++++ apps/meteor/client/navbar/index.ts | 1 + apps/meteor/client/sidebar/header/Header.tsx | 46 +++++++++++ .../client/sidebar/header/HeaderUnstable.tsx | 33 ++++++++ .../sidebar/header/UserAvatarWithStatus.tsx | 6 ++ .../header/UserAvatarWithStatusUnstable.tsx | 3 + .../meteor/client/sidebar/header/UserMenu.tsx | 36 ++++++-- .../actions/hooks/useAdministrationItems.tsx | 8 +- .../header/actions/hooks/useAppsItems.tsx | 6 ++ .../header/actions/hooks/useAuditItems.tsx | 6 ++ apps/meteor/client/sidebar/header/index.tsx | 49 ++++------- .../root/MainLayout/LayoutWithSidebar.tsx | 16 +++- apps/meteor/package.json | 1 + .../rocketchat-i18n/i18n/en.i18n.json | 2 + packages/ui-client/src/components/index.ts | 1 + .../src/hooks/useFeaturePreviewList.ts | 10 ++- yarn.lock | 82 +++++++++++++++++++ 27 files changed, 500 insertions(+), 47 deletions(-) create mode 100644 apps/meteor/client/components/Navbar/Navbar.stories.tsx create mode 100644 apps/meteor/client/components/Navbar/Navbar.tsx create mode 100644 apps/meteor/client/components/Navbar/NavbarAction.tsx create mode 100644 apps/meteor/client/components/Navbar/NavbarBadge.tsx create mode 100644 apps/meteor/client/components/Navbar/index.ts create mode 100644 apps/meteor/client/navbar/Navbar.tsx create mode 100644 apps/meteor/client/navbar/actions/NavbarAdministrationAction.tsx create mode 100644 apps/meteor/client/navbar/actions/NavbarAuditAction.tsx create mode 100644 apps/meteor/client/navbar/actions/NavbarHomeAction.tsx create mode 100644 apps/meteor/client/navbar/actions/NavbarMarketplaceAction.tsx create mode 100644 apps/meteor/client/navbar/actions/NavbarUserAction.tsx create mode 100644 apps/meteor/client/navbar/index.ts create mode 100644 apps/meteor/client/sidebar/header/Header.tsx create mode 100644 apps/meteor/client/sidebar/header/HeaderUnstable.tsx create mode 100644 apps/meteor/client/sidebar/header/UserAvatarWithStatusUnstable.tsx diff --git a/apps/meteor/client/components/Navbar/Navbar.stories.tsx b/apps/meteor/client/components/Navbar/Navbar.stories.tsx new file mode 100644 index 000000000000..a08e88a784a7 --- /dev/null +++ b/apps/meteor/client/components/Navbar/Navbar.stories.tsx @@ -0,0 +1,13 @@ +import { Box } from '@rocket.chat/fuselage'; +import type { ComponentMeta, ComponentStory } from '@storybook/react'; +import React from 'react'; + +import { Navbar } from './Navbar'; + +export default { + title: 'Components/Navbar', + component: Box, + decorators: [(story) => {story()}], +} as ComponentMeta; + +export const Default: ComponentStory = (_args) => ; diff --git a/apps/meteor/client/components/Navbar/Navbar.tsx b/apps/meteor/client/components/Navbar/Navbar.tsx new file mode 100644 index 000000000000..22b9329e3f06 --- /dev/null +++ b/apps/meteor/client/components/Navbar/Navbar.tsx @@ -0,0 +1,13 @@ +import { Box, ButtonGroup } from '@rocket.chat/fuselage'; +import type { FC } from 'react'; +import React from 'react'; + +export const Navbar: FC = ({ children }) => { + return ( + + + {children} + + + ); +}; diff --git a/apps/meteor/client/components/Navbar/NavbarAction.tsx b/apps/meteor/client/components/Navbar/NavbarAction.tsx new file mode 100644 index 000000000000..470f754d861a --- /dev/null +++ b/apps/meteor/client/components/Navbar/NavbarAction.tsx @@ -0,0 +1,10 @@ +import type { FC } from 'react'; +import React from 'react'; + +export const NavbarAction: FC = ({ children, ...props }) => { + return ( +
  • + {children} +
  • + ); +}; diff --git a/apps/meteor/client/components/Navbar/NavbarBadge.tsx b/apps/meteor/client/components/Navbar/NavbarBadge.tsx new file mode 100644 index 000000000000..2ab490ca0c77 --- /dev/null +++ b/apps/meteor/client/components/Navbar/NavbarBadge.tsx @@ -0,0 +1,11 @@ +import { Badge } from '@rocket.chat/fuselage'; +import type { AllHTMLAttributes } from 'react'; +import React from 'react'; + +export const NavbarBadge = (props: Omit, 'is'>) => { + return ( +
    + +
    + ); +}; diff --git a/apps/meteor/client/components/Navbar/index.ts b/apps/meteor/client/components/Navbar/index.ts new file mode 100644 index 000000000000..7e752b2a8844 --- /dev/null +++ b/apps/meteor/client/components/Navbar/index.ts @@ -0,0 +1,3 @@ +export * from './Navbar'; +export * from './NavbarAction'; +export * from './NavbarBadge'; diff --git a/apps/meteor/client/navbar/Navbar.tsx b/apps/meteor/client/navbar/Navbar.tsx new file mode 100644 index 000000000000..36f82be603f2 --- /dev/null +++ b/apps/meteor/client/navbar/Navbar.tsx @@ -0,0 +1,22 @@ +import React from 'react'; + +import { Navbar as NavbarComponent } from '../components/Navbar'; +import NavbarAdministrationAction from './actions/NavbarAdministrationAction'; +import NavbarAuditAction from './actions/NavbarAuditAction'; +import NavbarHomeAction from './actions/NavbarHomeAction'; +import NavbarMarketplaceAction from './actions/NavbarMarketplaceAction'; +import NavbarUserAction from './actions/NavbarUserAction'; + +const Navbar = () => { + return ( + + + + + + + + ); +}; + +export default Navbar; diff --git a/apps/meteor/client/navbar/actions/NavbarAdministrationAction.tsx b/apps/meteor/client/navbar/actions/NavbarAdministrationAction.tsx new file mode 100644 index 000000000000..3431b9e928ad --- /dev/null +++ b/apps/meteor/client/navbar/actions/NavbarAdministrationAction.tsx @@ -0,0 +1,34 @@ +import { useTranslation, useRouter } from '@rocket.chat/ui-contexts'; +import type { AllHTMLAttributes } from 'react'; +import React from 'react'; + +import GenericMenu from '../../components/GenericMenu/GenericMenu'; +import { useHandleMenuAction } from '../../components/GenericMenu/hooks/useHandleMenuAction'; +import { NavbarAction } from '../../components/Navbar'; +import { useAdministrationItems } from '../../sidebar/header/actions/hooks/useAdministrationItems'; + +const NavbarAdministrationAction = (props: AllHTMLAttributes) => { + const t = useTranslation(); + + const administrationItems = useAdministrationItems(); + + const handleAction = useHandleMenuAction(administrationItems); + + const router = useRouter(); + + return ( + + + + ); +}; + +export default NavbarAdministrationAction; diff --git a/apps/meteor/client/navbar/actions/NavbarAuditAction.tsx b/apps/meteor/client/navbar/actions/NavbarAuditAction.tsx new file mode 100644 index 000000000000..fc613be29b99 --- /dev/null +++ b/apps/meteor/client/navbar/actions/NavbarAuditAction.tsx @@ -0,0 +1,35 @@ +import { useTranslation, useRouter } from '@rocket.chat/ui-contexts'; +import type { AllHTMLAttributes } from 'react'; +import React from 'react'; + +import GenericMenu from '../../components/GenericMenu/GenericMenu'; +import { useHandleMenuAction } from '../../components/GenericMenu/hooks/useHandleMenuAction'; +import { NavbarAction } from '../../components/Navbar'; +import { useAuditItems } from '../../sidebar/header/actions/hooks/useAuditItems'; + +const NavbarAuditAction = (props: AllHTMLAttributes) => { + const t = useTranslation(); + + const router = useRouter(); + const routerName = router.getRouteName(); + + const auditItems = useAuditItems(); + + const handleAction = useHandleMenuAction(auditItems); + + return ( + + + + ); +}; + +export default NavbarAuditAction; diff --git a/apps/meteor/client/navbar/actions/NavbarHomeAction.tsx b/apps/meteor/client/navbar/actions/NavbarHomeAction.tsx new file mode 100644 index 000000000000..accd68817de9 --- /dev/null +++ b/apps/meteor/client/navbar/actions/NavbarHomeAction.tsx @@ -0,0 +1,35 @@ +import { IconButton } from '@rocket.chat/fuselage'; +import { useMutableCallback } from '@rocket.chat/fuselage-hooks'; +import { useRouter, useLayout, useSetting, useTranslation } from '@rocket.chat/ui-contexts'; +import type { HTMLAttributes, VFC } from 'react'; +import React from 'react'; + +import { NavbarAction } from '../../components/Navbar'; + +const NavbarHomeAction: VFC, 'is'>> = (props) => { + const t = useTranslation(); + const router = useRouter(); + const { sidebar } = useLayout(); + const showHome = useSetting('Layout_Show_Home_Button'); + + const routeName = router.getLocationPathname(); + + const handleHome = useMutableCallback(() => { + sidebar.toggle(); + router.navigate('/home'); + }); + + return showHome ? ( + + routeName?.startsWith(name))} + title={t('Home')} + medium + icon='home' + onClick={handleHome} + /> + + ) : null; +}; + +export default NavbarHomeAction; diff --git a/apps/meteor/client/navbar/actions/NavbarMarketplaceAction.tsx b/apps/meteor/client/navbar/actions/NavbarMarketplaceAction.tsx new file mode 100644 index 000000000000..8f54b8260b2b --- /dev/null +++ b/apps/meteor/client/navbar/actions/NavbarMarketplaceAction.tsx @@ -0,0 +1,45 @@ +import { IconButton } from '@rocket.chat/fuselage'; +import { useTranslation, useRouter } from '@rocket.chat/ui-contexts'; +import type { AllHTMLAttributes } from 'react'; +import React from 'react'; + +import GenericMenu from '../../components/GenericMenu/GenericMenu'; +import { useHandleMenuAction } from '../../components/GenericMenu/hooks/useHandleMenuAction'; +import { NavbarAction } from '../../components/Navbar'; +import { useAppsItems } from '../../sidebar/header/actions/hooks/useAppsItems'; + +const NavbarMarketplaceAction = (props: AllHTMLAttributes) => { + const t = useTranslation(); + const router = useRouter(); + const routeName = router.getRouteName(); + + const appItems = useAppsItems(); + + const handleAction = useHandleMenuAction(appItems); + + const showApps = appItems.length > 0; + + if (!showApps) { + return ( + + + + ); + } + + return ( + + + + ); +}; + +export default NavbarMarketplaceAction; diff --git a/apps/meteor/client/navbar/actions/NavbarUserAction.tsx b/apps/meteor/client/navbar/actions/NavbarUserAction.tsx new file mode 100644 index 000000000000..3b0a6cf99578 --- /dev/null +++ b/apps/meteor/client/navbar/actions/NavbarUserAction.tsx @@ -0,0 +1,20 @@ +import { Margins } from '@rocket.chat/fuselage'; +import { useUser } from '@rocket.chat/ui-contexts'; +import type { AllHTMLAttributes } from 'react'; +import React from 'react'; + +import { NavbarAction } from '../../components/Navbar'; +import UserAvatarWithStatusUnstable from '../../sidebar/header/UserAvatarWithStatusUnstable'; +import UserMenu from '../../sidebar/header/UserMenu'; + +const NavbarUserAction = (props: AllHTMLAttributes) => { + const user = useUser(); + + return ( + + {user ? : } + + ); +}; + +export default NavbarUserAction; diff --git a/apps/meteor/client/navbar/index.ts b/apps/meteor/client/navbar/index.ts new file mode 100644 index 000000000000..86d1b79b3ea2 --- /dev/null +++ b/apps/meteor/client/navbar/index.ts @@ -0,0 +1 @@ +export { default } from './Navbar'; diff --git a/apps/meteor/client/sidebar/header/Header.tsx b/apps/meteor/client/sidebar/header/Header.tsx new file mode 100644 index 000000000000..8b1c838f8d62 --- /dev/null +++ b/apps/meteor/client/sidebar/header/Header.tsx @@ -0,0 +1,46 @@ +import { Sidebar } from '@rocket.chat/fuselage'; +import { useUser, useTranslation } from '@rocket.chat/ui-contexts'; +import type { ReactElement } from 'react'; +import React, { memo } from 'react'; + +import UserAvatarWithStatus from './UserAvatarWithStatus'; +import UserMenu from './UserMenu'; +import Administration from './actions/Administration'; +import CreateRoom from './actions/CreateRoom'; +import Directory from './actions/Directory'; +import Home from './actions/Home'; +import Login from './actions/Login'; +import Search from './actions/Search'; +import Sort from './actions/Sort'; + +/** + * @deprecated Feature preview + * @description Should be removed when the feature became part of the core + * @memberof navigationBar + */ + +const Header = (): ReactElement => { + const t = useTranslation(); + const user = useUser(); + + return ( + + {user ? : } + + + + {user && ( + <> + + + + + + )} + {!user && } + + + ); +}; + +export default memo(Header); diff --git a/apps/meteor/client/sidebar/header/HeaderUnstable.tsx b/apps/meteor/client/sidebar/header/HeaderUnstable.tsx new file mode 100644 index 000000000000..319d3fb25e16 --- /dev/null +++ b/apps/meteor/client/sidebar/header/HeaderUnstable.tsx @@ -0,0 +1,33 @@ +import { Sidebar } from '@rocket.chat/fuselage'; +import { useUserId, useTranslation } from '@rocket.chat/ui-contexts'; +import type { ReactElement } from 'react'; +import React, { memo } from 'react'; + +import CreateRoom from './actions/CreateRoom'; +import Directory from './actions/Directory'; +import Login from './actions/Login'; +import Search from './actions/Search'; +import Sort from './actions/Sort'; + +const HeaderUnstable = (): ReactElement => { + const t = useTranslation(); + const uid = useUserId(); + + return ( + + + + {uid && ( + <> + + + + + )} + {!uid && } + + + ); +}; + +export default memo(HeaderUnstable); diff --git a/apps/meteor/client/sidebar/header/UserAvatarWithStatus.tsx b/apps/meteor/client/sidebar/header/UserAvatarWithStatus.tsx index f1abaa6a6269..4a9f4be60408 100644 --- a/apps/meteor/client/sidebar/header/UserAvatarWithStatus.tsx +++ b/apps/meteor/client/sidebar/header/UserAvatarWithStatus.tsx @@ -14,6 +14,12 @@ const anon = { avatarETag: undefined, } as const; +/** + * @deprecated Feature preview + * @description Should be moved to the core when the feature is ready + * @memberof navigationBar + */ + const UserAvatarWithStatus = () => { const user = useUser(); const presenceDisabled = useSetting('Presence_broadcast_disabled'); diff --git a/apps/meteor/client/sidebar/header/UserAvatarWithStatusUnstable.tsx b/apps/meteor/client/sidebar/header/UserAvatarWithStatusUnstable.tsx new file mode 100644 index 000000000000..229985610da3 --- /dev/null +++ b/apps/meteor/client/sidebar/header/UserAvatarWithStatusUnstable.tsx @@ -0,0 +1,3 @@ +import UserAvatarWithStatus from './UserAvatarWithStatus'; + +export default UserAvatarWithStatus; diff --git a/apps/meteor/client/sidebar/header/UserMenu.tsx b/apps/meteor/client/sidebar/header/UserMenu.tsx index 6347f6fad92d..0b44c09daff5 100644 --- a/apps/meteor/client/sidebar/header/UserMenu.tsx +++ b/apps/meteor/client/sidebar/header/UserMenu.tsx @@ -1,10 +1,12 @@ import type { IUser } from '@rocket.chat/core-typings'; +import { FeaturePreview, FeaturePreviewOn, FeaturePreviewOff } from '@rocket.chat/ui-client'; import React, { useState, memo } from 'react'; import GenericMenu from '../../components/GenericMenu/GenericMenu'; import type { GenericMenuItemProps } from '../../components/GenericMenu/GenericMenuItem'; import { useHandleMenuAction } from '../../components/GenericMenu/hooks/useHandleMenuAction'; import UserAvatarWithStatus from './UserAvatarWithStatus'; +import UserAvatarWithStatusUnstable from './UserAvatarWithStatusUnstable'; import { useUserMenu } from './hooks/useUserMenu'; const UserMenu = ({ user }: { user: IUser }) => { @@ -16,15 +18,31 @@ const UserMenu = ({ user }: { user: IUser }) => { const handleAction = useHandleMenuAction(items, () => setIsOpen(false)); return ( - } - selectionMode='multiple' - sections={sections} - title='User menu' - onAction={handleAction} - isOpen={isOpen} - onOpenChange={setIsOpen} - /> + + + } + selectionMode='multiple' + sections={sections} + title='User menu' + onAction={handleAction} + isOpen={isOpen} + onOpenChange={setIsOpen} + /> + + + } + medium + selectionMode='multiple' + sections={sections} + title='User menu' + onAction={handleAction} + isOpen={isOpen} + onOpenChange={setIsOpen} + /> + + ); }; diff --git a/apps/meteor/client/sidebar/header/actions/hooks/useAdministrationItems.tsx b/apps/meteor/client/sidebar/header/actions/hooks/useAdministrationItems.tsx index c0dc83fb62c1..3d4d640fecf2 100644 --- a/apps/meteor/client/sidebar/header/actions/hooks/useAdministrationItems.tsx +++ b/apps/meteor/client/sidebar/header/actions/hooks/useAdministrationItems.tsx @@ -46,6 +46,12 @@ const ADMIN_PERMISSIONS = [ 'view-moderation-console', ]; +/** + * @deprecated Feature preview + * @description Should be moved to navbar when the feature became part of the core + * @memberof navigationBar + */ + export const useAdministrationItems = (): GenericMenuItemProps[] => { const router = useRouter(); const t = useTranslation(); @@ -119,8 +125,8 @@ export const useAdministrationItems = (): GenericMenuItemProps[] => { return [ showUpgradeItem && upgradeItem, + shouldShowAdminMenu && workspaceItem, isAdmin && adminItem, omnichannel && omnichannelItem, - shouldShowAdminMenu && workspaceItem, ].filter(Boolean) as GenericMenuItemProps[]; }; diff --git a/apps/meteor/client/sidebar/header/actions/hooks/useAppsItems.tsx b/apps/meteor/client/sidebar/header/actions/hooks/useAppsItems.tsx index 6ecd34b62d5d..3717f33fb195 100644 --- a/apps/meteor/client/sidebar/header/actions/hooks/useAppsItems.tsx +++ b/apps/meteor/client/sidebar/header/actions/hooks/useAppsItems.tsx @@ -6,6 +6,12 @@ import type { GenericMenuItemProps } from '../../../../components/GenericMenu/Ge import { useUserDropdownAppsActionButtons } from '../../../../hooks/useAppActionButtons'; import { useAppRequestStats } from '../../../../views/marketplace/hooks/useAppRequestStats'; +/** + * @deprecated Feature preview + * @description Should be moved to navbar when the feature became part of the core + * @memberof navigationBar + */ + export const useAppsItems = (): GenericMenuItemProps[] => { const t = useTranslation(); diff --git a/apps/meteor/client/sidebar/header/actions/hooks/useAuditItems.tsx b/apps/meteor/client/sidebar/header/actions/hooks/useAuditItems.tsx index 07b2b9bb11c3..f506255806bf 100644 --- a/apps/meteor/client/sidebar/header/actions/hooks/useAuditItems.tsx +++ b/apps/meteor/client/sidebar/header/actions/hooks/useAuditItems.tsx @@ -3,6 +3,12 @@ import { useTranslation, useRoute, usePermission } from '@rocket.chat/ui-context import { useHasLicenseModule } from '../../../../../ee/client/hooks/useHasLicenseModule'; import type { GenericMenuItemProps } from '../../../../components/GenericMenu/GenericMenuItem'; +/** + * @deprecated Feature preview + * @description Should be moved to navbar when the feature became part of the core + * @memberof navigationBar + */ + export const useAuditItems = (): GenericMenuItemProps[] => { const hasAuditLicense = useHasLicenseModule('auditing') === true; diff --git a/apps/meteor/client/sidebar/header/index.tsx b/apps/meteor/client/sidebar/header/index.tsx index 95b225c0a242..6fa03502d76a 100644 --- a/apps/meteor/client/sidebar/header/index.tsx +++ b/apps/meteor/client/sidebar/header/index.tsx @@ -1,42 +1,21 @@ -import { Sidebar } from '@rocket.chat/fuselage'; -import { useUser, useTranslation } from '@rocket.chat/ui-contexts'; +import { FeaturePreview, FeaturePreviewOn, FeaturePreviewOff } from '@rocket.chat/ui-client'; import type { ReactElement } from 'react'; -import React, { memo } from 'react'; +import React, { lazy, memo } from 'react'; -import UserAvatarWithStatus from './UserAvatarWithStatus'; -import UserMenu from './UserMenu'; -import Administration from './actions/Administration'; -import CreateRoom from './actions/CreateRoom'; -import Directory from './actions/Directory'; -import Home from './actions/Home'; -import Login from './actions/Login'; -import Search from './actions/Search'; -import Sort from './actions/Sort'; - -const HeaderWithData = (): ReactElement => { - const t = useTranslation(); - const user = useUser(); +const Header = lazy(() => import('./Header')); +const HeaderUnstable = lazy(() => import('./HeaderUnstable')); +const HeaderWrapper = (): ReactElement => { return ( - <> - - {user ? : } - - - - {user && ( - <> - - - - - - )} - {!user && } - - - + + +
    + + + + + ); }; -export default memo(HeaderWithData); +export default memo(HeaderWrapper); diff --git a/apps/meteor/client/views/root/MainLayout/LayoutWithSidebar.tsx b/apps/meteor/client/views/root/MainLayout/LayoutWithSidebar.tsx index ee50ad826785..901ced6e7305 100644 --- a/apps/meteor/client/views/root/MainLayout/LayoutWithSidebar.tsx +++ b/apps/meteor/client/views/root/MainLayout/LayoutWithSidebar.tsx @@ -1,10 +1,12 @@ import { Box } from '@rocket.chat/fuselage'; +import { FeaturePreview, FeaturePreviewOff, FeaturePreviewOn } from '@rocket.chat/ui-client'; import { useLayout, useSetting, useCurrentModal, useRoute, useCurrentRoutePath } from '@rocket.chat/ui-contexts'; import { PaletteStyleTag } from '@rocket.chat/ui-theming/src/PaletteStyleTag'; import { SidebarPaletteStyleTag } from '@rocket.chat/ui-theming/src/SidebarPaletteStyleTag'; import type { ReactElement, ReactNode } from 'react'; import React, { useEffect, useRef } from 'react'; +import Navbar from '../../../navbar'; import Sidebar from '../../../sidebar'; const LayoutWithSidebar = ({ children }: { children: ReactNode }): ReactElement => { @@ -48,7 +50,19 @@ const LayoutWithSidebar = ({ children }: { children: ReactNode }): ReactElement > - {!removeSidenav ? : null} + {!removeSidenav ? ( + <> + + + + + + <> + + + + + ) : null}
    {children}
    diff --git a/apps/meteor/package.json b/apps/meteor/package.json index 99385d0b56f3..7235923ab100 100644 --- a/apps/meteor/package.json +++ b/apps/meteor/package.json @@ -75,6 +75,7 @@ "@rocket.chat/livechat": "workspace:^", "@rocket.chat/mock-providers": "workspace:^", "@settlin/spacebars-loader": "^1.0.9", + "@storybook/addon-a11y": "6.5.16", "@storybook/addon-essentials": "~6.5.16", "@storybook/addon-interactions": "~6.5.16", "@storybook/addon-postcss": "~2.0.0", diff --git a/apps/meteor/packages/rocketchat-i18n/i18n/en.i18n.json b/apps/meteor/packages/rocketchat-i18n/i18n/en.i18n.json index 9f313e58c564..5b23d85fc71d 100644 --- a/apps/meteor/packages/rocketchat-i18n/i18n/en.i18n.json +++ b/apps/meteor/packages/rocketchat-i18n/i18n/en.i18n.json @@ -3569,6 +3569,8 @@ "Name_optional": "Name (optional)", "Name_Placeholder": "Please enter your name...", "Navigation": "Navigation", + "Navigation_bar": "Navigation bar", + "Navigation_bar_description": "Introducing the navigation bar — a higher-level navigation designed to help users quickly find what they need. With its compact design and intuitive organization, this streamlined sidebar optimizes screen space while providing easy access to essential software features and sections.", "Navigation_History": "Navigation History", "Next": "Next", "Never": "Never", diff --git a/packages/ui-client/src/components/index.ts b/packages/ui-client/src/components/index.ts index 5a0f1463be80..e40cfd9a5575 100644 --- a/packages/ui-client/src/components/index.ts +++ b/packages/ui-client/src/components/index.ts @@ -8,3 +8,4 @@ export * from './TooltipComponent'; export * as UserStatus from './UserStatus'; export { default as Card } from './Card'; export * from './Header'; +export * from './FeaturePreview/FeaturePreview'; diff --git a/packages/ui-client/src/hooks/useFeaturePreviewList.ts b/packages/ui-client/src/hooks/useFeaturePreviewList.ts index b4e9a1dee6d3..87a89e9d5a67 100644 --- a/packages/ui-client/src/hooks/useFeaturePreviewList.ts +++ b/packages/ui-client/src/hooks/useFeaturePreviewList.ts @@ -1,7 +1,7 @@ import type { TranslationKey } from '@rocket.chat/ui-contexts'; import { useUserPreference, useSetting } from '@rocket.chat/ui-contexts'; -export type FeaturesAvailable = 'quickReactions'; +export type FeaturesAvailable = 'quickReactions' | 'navigationBar'; export type FeaturePreviewProps = { name: FeaturesAvailable; @@ -21,6 +21,14 @@ export const defaultFeaturesPreview: FeaturePreviewProps[] = [ imageUrl: 'images/featurePreview/quick-reactions.png', value: false, }, + { + name: 'navigationBar', + i18n: 'Navigation_bar', + description: 'Navigation_bar_description', + group: 'Navigation', + imageUrl: 'images/featurePreview/quick-reactions.png', + value: false, + }, ]; export const useFeaturePreviewList = () => { diff --git a/yarn.lock b/yarn.lock index 4ba47faae660..a4da00ac674e 100644 --- a/yarn.lock +++ b/yarn.lock @@ -10105,6 +10105,7 @@ __metadata: "@rocket.chat/web-ui-registration": "workspace:^" "@settlin/spacebars-loader": ^1.0.9 "@slack/rtm-api": ^6.0.0 + "@storybook/addon-a11y": 6.5.16 "@storybook/addon-essentials": ~6.5.16 "@storybook/addon-interactions": ~6.5.16 "@storybook/addon-postcss": ~2.0.0 @@ -11292,6 +11293,38 @@ __metadata: languageName: node linkType: hard +"@storybook/addon-a11y@npm:6.5.16": + version: 6.5.16 + resolution: "@storybook/addon-a11y@npm:6.5.16" + dependencies: + "@storybook/addons": 6.5.16 + "@storybook/api": 6.5.16 + "@storybook/channels": 6.5.16 + "@storybook/client-logger": 6.5.16 + "@storybook/components": 6.5.16 + "@storybook/core-events": 6.5.16 + "@storybook/csf": 0.0.2--canary.4566f4d.1 + "@storybook/theming": 6.5.16 + axe-core: ^4.2.0 + core-js: ^3.8.2 + global: ^4.4.0 + lodash: ^4.17.21 + react-sizeme: ^3.0.1 + regenerator-runtime: ^0.13.7 + ts-dedent: ^2.0.0 + util-deprecate: ^1.0.2 + peerDependencies: + react: ^16.8.0 || ^17.0.0 || ^18.0.0 + react-dom: ^16.8.0 || ^17.0.0 || ^18.0.0 + peerDependenciesMeta: + react: + optional: true + react-dom: + optional: true + checksum: 05ce7f696254782b521a5e946f7d58b207d854e69ce8b624a14de0192ce558f640b9dae821e122911059a24d2f9907e783e8dce5fb9288d5cfbdf0833aaab503 + languageName: node + linkType: hard + "@storybook/addon-actions@npm:6.5.16, @storybook/addon-actions@npm:~6.5.16": version: 6.5.16 resolution: "@storybook/addon-actions@npm:6.5.16" @@ -16572,6 +16605,13 @@ __metadata: languageName: node linkType: hard +"axe-core@npm:^4.2.0": + version: 4.7.2 + resolution: "axe-core@npm:4.7.2" + checksum: 5d86fa0f45213b0e54cbb5d713ce885c4a8fe3a72b92dd915a47aa396d6fd149c4a87fec53aa978511f6d941402256cfeb26f2db35129e370f25a453c688655a + languageName: node + linkType: hard + "axios@npm:^0.21.0, axios@npm:^0.21.1": version: 0.21.4 resolution: "axios@npm:0.21.4" @@ -17010,6 +17050,13 @@ __metadata: languageName: node linkType: hard +"batch-processor@npm:1.0.0": + version: 1.0.0 + resolution: "batch-processor@npm:1.0.0" + checksum: 5519b024f6cd0e95a543bb3edf0ae19e5badae0c32b30b41839b4469bbb1f91e14fc04bff3759cd9c2621aa9e16def48c938783e9027e7ec977fba62d537a468 + languageName: node + linkType: hard + "batch@npm:0.6.1": version: 0.6.1 resolution: "batch@npm:0.6.1" @@ -21278,6 +21325,15 @@ __metadata: languageName: node linkType: hard +"element-resize-detector@npm:^1.2.2": + version: 1.2.4 + resolution: "element-resize-detector@npm:1.2.4" + dependencies: + batch-processor: 1.0.0 + checksum: 81c47b7e229c303889d3a9d78ec3f3232e88a6682f1e2424fb0632d9b4f503b2ca011e6954321060604da07749a5a972b6a175fdf6c6806093a3b80a304cde7b + languageName: node + linkType: hard + "elliptic@npm:^6.5.3, elliptic@npm:^6.5.4": version: 6.5.4 resolution: "elliptic@npm:6.5.4" @@ -35116,6 +35172,18 @@ __metadata: languageName: node linkType: hard +"react-sizeme@npm:^3.0.1": + version: 3.0.2 + resolution: "react-sizeme@npm:3.0.2" + dependencies: + element-resize-detector: ^1.2.2 + invariant: ^2.2.4 + shallowequal: ^1.1.0 + throttle-debounce: ^3.0.1 + checksum: 97cb852c24bbd50acb310da89df564e0d069415f6635676dae3d3bdc583ece88090c0f2ee88a6b0dc36d2793af4a11e83bf6bbb41b86225dd0cf338e8f7e8552 + languageName: node + linkType: hard + "react-split-pane@npm:^0.1.92": version: 0.1.92 resolution: "react-split-pane@npm:0.1.92" @@ -36971,6 +37039,13 @@ __metadata: languageName: node linkType: hard +"shallowequal@npm:^1.1.0": + version: 1.1.0 + resolution: "shallowequal@npm:1.1.0" + checksum: f4c1de0837f106d2dbbfd5d0720a5d059d1c66b42b580965c8f06bb1db684be8783538b684092648c981294bf817869f743a066538771dbecb293df78f765e00 + languageName: node + linkType: hard + "sharp@npm:^0.30.7": version: 0.30.7 resolution: "sharp@npm:0.30.7" @@ -38964,6 +39039,13 @@ __metadata: languageName: node linkType: hard +"throttle-debounce@npm:^3.0.1": + version: 3.0.1 + resolution: "throttle-debounce@npm:3.0.1" + checksum: e34ef638e8df3a9154249101b68afcbf2652a139c803415ef8a2f6a8bc577bcd4d79e4bb914ad3cd206523ac78b9fb7e80885bfa049f64fbb1927f99d98b5736 + languageName: node + linkType: hard + "through2@npm:^2.0.0, through2@npm:~2.0.3": version: 2.0.5 resolution: "through2@npm:2.0.5" From 841ec6678e293f567a8351cf91a14ab165a272a8 Mon Sep 17 00:00:00 2001 From: Yash Rajpal <58601732+yash-rajpal@users.noreply.github.com> Date: Fri, 14 Jul 2023 13:13:43 +0530 Subject: [PATCH 008/342] chore(gazzodown): remove `ui-client` and `ui-contexts` peer deps from `gazzodown` (#29820) --- apps/meteor/client/components/GazzodownText.tsx | 9 +++++++-- packages/gazzodown/package.json | 4 ---- packages/gazzodown/src/MarkupInteractionContext.ts | 3 +++ packages/gazzodown/src/mentions/UserMentionElement.tsx | 9 +++------ yarn.lock | 4 ---- 5 files changed, 13 insertions(+), 16 deletions(-) diff --git a/apps/meteor/client/components/GazzodownText.tsx b/apps/meteor/client/components/GazzodownText.tsx index e9a0f39ddb5f..7912e44ad925 100644 --- a/apps/meteor/client/components/GazzodownText.tsx +++ b/apps/meteor/client/components/GazzodownText.tsx @@ -2,7 +2,7 @@ import type { IRoom } from '@rocket.chat/core-typings'; import type { ChannelMention, UserMention } from '@rocket.chat/gazzodown'; import { MarkupInteractionContext } from '@rocket.chat/gazzodown'; import { escapeRegExp } from '@rocket.chat/string-helpers'; -import { useLayout, useRouter, useUserPreference } from '@rocket.chat/ui-contexts'; +import { useLayout, useRouter, useSetting, useUserPreference, useUserId } from '@rocket.chat/ui-contexts'; import type { UIEvent } from 'react'; import React, { useCallback, memo, useMemo } from 'react'; @@ -47,6 +47,8 @@ const GazzodownText = ({ mentions, channels, searchText, children }: GazzodownTe const convertAsciiToEmoji = useUserPreference('convertAsciiEmoji', true); const useEmoji = Boolean(useUserPreference('useEmojis')); + const useRealName = Boolean(useSetting('UI_Use_Real_Name')); + const ownUserId = useUserId(); const chat = useChat(); @@ -80,7 +82,7 @@ const GazzodownText = ({ mentions, channels, searchText, children }: GazzodownTe const goToRoom = useGoToRoom(); - const { isEmbedded } = useLayout(); + const { isEmbedded, isMobile } = useLayout(); const resolveChannelMention = useCallback((mention: string) => channels?.find(({ name }) => name === mention), [channels]); @@ -117,6 +119,9 @@ const GazzodownText = ({ mentions, channels, searchText, children }: GazzodownTe onChannelMentionClick, convertAsciiToEmoji, useEmoji, + useRealName, + isMobile, + ownUserId, }} > {children} diff --git a/packages/gazzodown/package.json b/packages/gazzodown/package.json index 46b0d6396122..e7d0cdd277f2 100644 --- a/packages/gazzodown/package.json +++ b/packages/gazzodown/package.json @@ -10,8 +10,6 @@ "@rocket.chat/fuselage-tokens": "next", "@rocket.chat/message-parser": "next", "@rocket.chat/styled": "next", - "@rocket.chat/ui-client": "workspace:^", - "@rocket.chat/ui-contexts": "workspace:^", "@storybook/addon-actions": "~6.5.16", "@storybook/addon-docs": "~6.5.16", "@storybook/addon-essentials": "~6.5.16", @@ -71,8 +69,6 @@ "@rocket.chat/fuselage-tokens": "*", "@rocket.chat/message-parser": "*", "@rocket.chat/styled": "*", - "@rocket.chat/ui-client": "*", - "@rocket.chat/ui-contexts": "*", "katex": "*", "react": "*" }, diff --git a/packages/gazzodown/src/MarkupInteractionContext.ts b/packages/gazzodown/src/MarkupInteractionContext.ts index 1f2e3b59c9bf..eed1c2cd4236 100644 --- a/packages/gazzodown/src/MarkupInteractionContext.ts +++ b/packages/gazzodown/src/MarkupInteractionContext.ts @@ -16,6 +16,9 @@ type MarkupInteractionContextValue = { onChannelMentionClick?: (mentionedChannel: ChannelMention) => ((e: UIEvent) => void) | undefined; convertAsciiToEmoji?: boolean; useEmoji?: boolean; + useRealName?: boolean; + isMobile?: boolean; + ownUserId?: string | null; }; export const MarkupInteractionContext = createContext({}); diff --git a/packages/gazzodown/src/mentions/UserMentionElement.tsx b/packages/gazzodown/src/mentions/UserMentionElement.tsx index 65a238e0de1e..15b8e226af81 100644 --- a/packages/gazzodown/src/mentions/UserMentionElement.tsx +++ b/packages/gazzodown/src/mentions/UserMentionElement.tsx @@ -1,5 +1,4 @@ import { Message } from '@rocket.chat/fuselage'; -import { useLayout, useSetting, useUserId } from '@rocket.chat/ui-contexts'; import { memo, ReactElement, useContext, useMemo } from 'react'; import { MarkupInteractionContext } from '../MarkupInteractionContext'; @@ -9,14 +8,12 @@ type UserMentionElementProps = { }; const UserMentionElement = ({ mention }: UserMentionElementProps): ReactElement => { - const { resolveUserMention, onUserMentionClick } = useContext(MarkupInteractionContext); + const { resolveUserMention, onUserMentionClick, isMobile, ownUserId, useRealName } = useContext(MarkupInteractionContext); const resolved = useMemo(() => resolveUserMention?.(mention), [mention, resolveUserMention]); const handleClick = useMemo(() => (resolved ? onUserMentionClick?.(resolved) : undefined), [resolved, onUserMentionClick]); - const { isMobile } = useLayout(); - const uid = useUserId(); - const showRealName = useSetting('UI_Use_Real_Name') && !isMobile; + const showRealName = useRealName && !isMobile; if (mention === 'all') { return all; @@ -32,7 +29,7 @@ const UserMentionElement = ({ mention }: UserMentionElementProps): ReactElement return ( Date: Fri, 14 Jul 2023 11:54:54 -0300 Subject: [PATCH 009/342] regression: Apply right filters to action buttons and convert `applyButtonFilters` to `useApplyButtonFilters` (#29822) --- apps/meteor/.mocharc.client.js | 2 +- .../actionButtons/lib/applyButtonFilters.ts | 58 --- .../client/hooks/useAppActionButtons.ts | 25 +- .../client/hooks/useApplyButtonFilters.ts | 65 +++ .../providers/AuthorizationProvider.tsx | 5 +- .../actions/hooks/useAppsItems.spec.tsx | 488 ++++++++++++++++++ apps/meteor/jest.client.config.ts | 6 +- .../src/MockedAuthorizationContext.tsx | 19 +- .../src/MockedServerContext.tsx | 1 + .../ui-contexts/src/AuthorizationContext.ts | 8 +- 10 files changed, 599 insertions(+), 78 deletions(-) delete mode 100644 apps/meteor/app/ui-message/client/actionButtons/lib/applyButtonFilters.ts create mode 100644 apps/meteor/client/hooks/useApplyButtonFilters.ts create mode 100644 apps/meteor/client/sidebar/header/actions/hooks/useAppsItems.spec.tsx diff --git a/apps/meteor/.mocharc.client.js b/apps/meteor/.mocharc.client.js index 7eff846f683c..6e522ee67363 100644 --- a/apps/meteor/.mocharc.client.js +++ b/apps/meteor/.mocharc.client.js @@ -38,5 +38,5 @@ module.exports = { 'tests/unit/lib/**/*.tests.ts', 'tests/unit/client/**/*.test.ts', ], - exclude: ['client/hooks/*.spec.{ts,tsx}'], + exclude: ['client/hooks/*.spec.{ts,tsx}', 'client/sidebar/header/actions/hooks/*.spec.{ts,tsx}'], }; diff --git a/apps/meteor/app/ui-message/client/actionButtons/lib/applyButtonFilters.ts b/apps/meteor/app/ui-message/client/actionButtons/lib/applyButtonFilters.ts deleted file mode 100644 index be38eaa6a22c..000000000000 --- a/apps/meteor/app/ui-message/client/actionButtons/lib/applyButtonFilters.ts +++ /dev/null @@ -1,58 +0,0 @@ -/* Style disabled as having some arrow functions in one-line hurts readability */ -/* eslint-disable arrow-body-style */ - -import { Meteor } from 'meteor/meteor'; -import type { IUIActionButton } from '@rocket.chat/apps-engine/definition/ui'; -import { RoomTypeFilter } from '@rocket.chat/apps-engine/definition/ui'; -import type { IRoom } from '@rocket.chat/core-typings'; -import { - isDirectMessageRoom, - isMultipleDirectMessageRoom, - isOmnichannelRoom, - isPrivateDiscussion, - isPrivateTeamRoom, - isPublicDiscussion, - isPublicTeamRoom, -} from '@rocket.chat/core-typings'; - -import { hasAtLeastOnePermission, hasPermission, hasRole, hasAnyRole } from '../../../../authorization/client'; - -const applyAuthFilter = (button: IUIActionButton, room?: IRoom, ignoreSubscriptions = false): boolean => { - const { hasAllPermissions, hasOnePermission, hasAllRoles, hasOneRole } = button.when || {}; - - const userId = Meteor.userId(); - - const hasAllPermissionsResult = hasAllPermissions ? hasPermission(hasAllPermissions) : true; - const hasOnePermissionResult = hasOnePermission ? hasAtLeastOnePermission(hasOnePermission) : true; - const hasAllRolesResult = hasAllRoles - ? !!userId && hasAllRoles.every((role) => hasRole(userId, role, room?._id, ignoreSubscriptions)) - : true; - const hasOneRoleResult = hasOneRole ? !!userId && hasAnyRole(userId, hasOneRole, room?._id, ignoreSubscriptions) : true; - - return hasAllPermissionsResult && hasOnePermissionResult && hasAllRolesResult && hasOneRoleResult; -}; - -const enumToFilter: { [k in RoomTypeFilter]: (room: IRoom) => boolean } = { - [RoomTypeFilter.PUBLIC_CHANNEL]: (room) => room.t === 'c', - [RoomTypeFilter.PRIVATE_CHANNEL]: (room) => room.t === 'p', - [RoomTypeFilter.PUBLIC_TEAM]: isPublicTeamRoom, - [RoomTypeFilter.PRIVATE_TEAM]: isPrivateTeamRoom, - [RoomTypeFilter.PUBLIC_DISCUSSION]: isPublicDiscussion, - [RoomTypeFilter.PRIVATE_DISCUSSION]: isPrivateDiscussion, - [RoomTypeFilter.DIRECT]: isDirectMessageRoom, - [RoomTypeFilter.DIRECT_MULTIPLE]: isMultipleDirectMessageRoom, - [RoomTypeFilter.LIVE_CHAT]: isOmnichannelRoom, -}; - -const applyRoomFilter = (button: IUIActionButton, room: IRoom): boolean => { - const { roomTypes } = button.when || {}; - return !roomTypes || roomTypes.some((filter): boolean => enumToFilter[filter]?.(room)); -}; - -export const applyButtonFilters = (button: IUIActionButton, room?: IRoom): boolean => { - return applyAuthFilter(button, room) && (!room || applyRoomFilter(button, room)); -}; - -export const applyDropdownActionButtonFilters = (button: IUIActionButton): boolean => { - return applyAuthFilter(button, undefined, true); -}; diff --git a/apps/meteor/client/hooks/useAppActionButtons.ts b/apps/meteor/client/hooks/useAppActionButtons.ts index 1f539c49b002..6d5aac3e92a3 100644 --- a/apps/meteor/client/hooks/useAppActionButtons.ts +++ b/apps/meteor/client/hooks/useAppActionButtons.ts @@ -4,13 +4,13 @@ import type { UseQueryResult } from '@tanstack/react-query'; import { useQuery, useQueryClient } from '@tanstack/react-query'; import { useEffect, useRef, useMemo } from 'react'; -import { applyButtonFilters } from '../../app/ui-message/client/actionButtons/lib/applyButtonFilters'; import type { MessageActionConfig, MessageActionContext } from '../../app/ui-utils/client/lib/MessageAction'; import type { MessageBoxAction } from '../../app/ui-utils/client/lib/messageBox'; import { Utilities } from '../../ee/lib/misc/Utilities'; import type { GenericMenuItemProps } from '../components/GenericMenu/GenericMenuItem'; import { useRoom } from '../views/room/contexts/RoomContext'; import type { ToolboxAction } from '../views/room/lib/Toolbox'; +import { useApplyButtonFilters, useApplyButtonAuthFilter } from './useApplyButtonFilters'; import { useUiKitActionManager } from './useUiKitActionManager'; const getIdForActionButton = ({ appId, actionId }: IUIActionButton): string => `${appId}/${actionId}`; @@ -49,13 +49,14 @@ export const useAppActionButtons = (context?: `${UIActionButtonContext}`) => { export const useMessageboxAppsActionButtons = () => { const result = useAppActionButtons('messageBoxAction'); const actionManager = useUiKitActionManager(); - const room = useRoom(); + + const applyButtonFilters = useApplyButtonFilters(); const data = useMemo( () => result.data ?.filter((action) => { - return applyButtonFilters(action, room); + return applyButtonFilters(action); }) .map((action) => { const item: MessageBoxAction = { @@ -74,7 +75,7 @@ export const useMessageboxAppsActionButtons = () => { return item; }), - [actionManager, result.data, room], + [actionManager, applyButtonFilters, result.data], ); return { ...result, @@ -86,6 +87,8 @@ export const useUserDropdownAppsActionButtons = () => { const result = useAppActionButtons('userDropdownAction'); const actionManager = useUiKitActionManager(); + const applyButtonFilters = useApplyButtonAuthFilter(); + const data = useMemo( () => result.data @@ -106,7 +109,7 @@ export const useUserDropdownAppsActionButtons = () => { }, }; }), - [actionManager, result.data], + [actionManager, applyButtonFilters, result.data], ); return { ...result, @@ -117,6 +120,7 @@ export const useUserDropdownAppsActionButtons = () => { export const useRoomActionAppsActionButtons = (context?: MessageActionContext) => { const result = useAppActionButtons('roomAction'); const actionManager = useUiKitActionManager(); + const applyButtonFilters = useApplyButtonFilters(); const room = useRoom(); const data = useMemo( () => @@ -125,7 +129,7 @@ export const useRoomActionAppsActionButtons = (context?: MessageActionContext) = if (context && ['group', 'channel', 'live', 'team', 'direct', 'direct_multiple'].includes(context)) { return false; } - return applyButtonFilters(action, room); + return applyButtonFilters(action); }) .map((action) => { const item: [string, ToolboxAction] = [ @@ -149,7 +153,7 @@ export const useRoomActionAppsActionButtons = (context?: MessageActionContext) = ]; return item; }), - [actionManager, context, result.data, room], + [actionManager, applyButtonFilters, context, result.data, room._id], ); return { ...result, @@ -160,8 +164,7 @@ export const useRoomActionAppsActionButtons = (context?: MessageActionContext) = export const useMessageActionAppsActionButtons = (context?: MessageActionContext) => { const result = useAppActionButtons('messageAction'); const actionManager = useUiKitActionManager(); - const room = useRoom(); - + const applyButtonFilters = useApplyButtonFilters(); const data = useMemo( () => result.data @@ -172,7 +175,7 @@ export const useMessageActionAppsActionButtons = (context?: MessageActionContext ) { return false; } - return applyButtonFilters(action, room); + return applyButtonFilters(action); }) .map((action) => { const item: MessageActionConfig = { @@ -192,7 +195,7 @@ export const useMessageActionAppsActionButtons = (context?: MessageActionContext return item; }), - [actionManager, context, result.data, room], + [actionManager, applyButtonFilters, context, result.data], ); return { ...result, diff --git a/apps/meteor/client/hooks/useApplyButtonFilters.ts b/apps/meteor/client/hooks/useApplyButtonFilters.ts new file mode 100644 index 000000000000..742f33489deb --- /dev/null +++ b/apps/meteor/client/hooks/useApplyButtonFilters.ts @@ -0,0 +1,65 @@ +import type { IUIActionButton } from '@rocket.chat/apps-engine/definition/ui'; +import { RoomTypeFilter } from '@rocket.chat/apps-engine/definition/ui'; +import type { IRoom } from '@rocket.chat/core-typings'; +import { + isDirectMessageRoom, + isMultipleDirectMessageRoom, + isOmnichannelRoom, + isPrivateDiscussion, + isPrivateTeamRoom, + isPublicDiscussion, + isPublicTeamRoom, +} from '@rocket.chat/core-typings'; +import { AuthorizationContext, useUserId } from '@rocket.chat/ui-contexts'; +import { useCallback, useContext } from 'react'; + +import { useRoom } from '../views/room/contexts/RoomContext'; + +const enumToFilter: { [k in RoomTypeFilter]: (room: IRoom) => boolean } = { + [RoomTypeFilter.PUBLIC_CHANNEL]: (room) => room.t === 'c', + [RoomTypeFilter.PRIVATE_CHANNEL]: (room) => room.t === 'p', + [RoomTypeFilter.PUBLIC_TEAM]: isPublicTeamRoom, + [RoomTypeFilter.PRIVATE_TEAM]: isPrivateTeamRoom, + [RoomTypeFilter.PUBLIC_DISCUSSION]: isPublicDiscussion, + [RoomTypeFilter.PRIVATE_DISCUSSION]: isPrivateDiscussion, + [RoomTypeFilter.DIRECT]: isDirectMessageRoom, + [RoomTypeFilter.DIRECT_MULTIPLE]: isMultipleDirectMessageRoom, + [RoomTypeFilter.LIVE_CHAT]: isOmnichannelRoom, +}; + +const applyRoomFilter = (button: IUIActionButton, room: IRoom): boolean => { + const { roomTypes } = button.when || {}; + return !roomTypes || roomTypes.some((filter): boolean => enumToFilter[filter]?.(room)); +}; + +export const useApplyButtonFilters = (): ((button: IUIActionButton) => boolean) => { + const room = useRoom(); + if (!room) { + throw new Error('useApplyButtonFilters must be used inside a room context'); + } + const applyAuthFilter = useApplyButtonAuthFilter(); + return useCallback( + (button: IUIActionButton) => applyAuthFilter(button) && (!room || applyRoomFilter(button, room)), + [applyAuthFilter, room], + ); +}; + +export const useApplyButtonAuthFilter = (): ((button: IUIActionButton) => boolean) => { + const uid = useUserId(); + + const { queryAllPermissions, queryAtLeastOnePermission, queryRole } = useContext(AuthorizationContext); + + return useCallback( + (button: IUIActionButton, room?: IRoom) => { + const { hasAllPermissions, hasOnePermission, hasAllRoles, hasOneRole } = button.when || {}; + + const hasAllPermissionsResult = hasAllPermissions ? queryAllPermissions(hasAllPermissions)[1]() : true; + const hasOnePermissionResult = hasOnePermission ? queryAtLeastOnePermission(hasOnePermission)[1]() : true; + const hasAllRolesResult = hasAllRoles ? !!uid && hasAllRoles.every((role) => queryRole(role, room?._id)) : true; + const hasOneRoleResult = hasOneRole ? !!uid && hasOneRole.some((role) => queryRole(role, room?._id)[1]()) : true; + + return hasAllPermissionsResult && hasOnePermissionResult && hasAllRolesResult && hasOneRoleResult; + }, + [queryAllPermissions, queryAtLeastOnePermission, queryRole, uid], + ); +}; diff --git a/apps/meteor/client/providers/AuthorizationProvider.tsx b/apps/meteor/client/providers/AuthorizationProvider.tsx index 8fb0e69d12a5..64d936b5cd65 100644 --- a/apps/meteor/client/providers/AuthorizationProvider.tsx +++ b/apps/meteor/client/providers/AuthorizationProvider.tsx @@ -20,7 +20,10 @@ const contextValue = { queryPermission: createReactiveSubscriptionFactory((permission, scope, scopeRoles) => hasPermission(permission, scope, scopeRoles)), queryAtLeastOnePermission: createReactiveSubscriptionFactory((permissions, scope) => hasAtLeastOnePermission(permissions, scope)), queryAllPermissions: createReactiveSubscriptionFactory((permissions, scope) => hasAllPermission(permissions, scope)), - queryRole: createReactiveSubscriptionFactory((role) => !!Meteor.userId() && hasRole(Meteor.userId() as string, role)), + queryRole: createReactiveSubscriptionFactory( + (role, scope?, ignoreSubscriptions = false) => + !!Meteor.userId() && hasRole(Meteor.userId() as string, role, scope, ignoreSubscriptions), + ), roleStore: new RoleStore(), }; diff --git a/apps/meteor/client/sidebar/header/actions/hooks/useAppsItems.spec.tsx b/apps/meteor/client/sidebar/header/actions/hooks/useAppsItems.spec.tsx new file mode 100644 index 000000000000..a520444389a2 --- /dev/null +++ b/apps/meteor/client/sidebar/header/actions/hooks/useAppsItems.spec.tsx @@ -0,0 +1,488 @@ +/* eslint-disable react/no-multi-comp */ +import { MockedAuthorizationContext } from '@rocket.chat/mock-providers/src/MockedAuthorizationContext'; +import { MockedServerContext } from '@rocket.chat/mock-providers/src/MockedServerContext'; +import { MockedSettingsContext } from '@rocket.chat/mock-providers/src/MockedSettingsContext'; +import { MockedUserContext } from '@rocket.chat/mock-providers/src/MockedUserContext'; +import { QueryClient, QueryClientProvider } from '@tanstack/react-query'; +import { renderHook } from '@testing-library/react-hooks'; +import React from 'react'; + +import { ActionManagerContext } from '../../../../contexts/ActionManagerContext'; +import { useAppsItems } from './useAppsItems'; + +it('should return and empty array if the user does not have `manage-apps` and `access-marketplace` permission', () => { + const { result } = renderHook( + () => { + return useAppsItems(); + }, + { + wrapper: ({ children }) => ( + + { + if (args.method === 'GET' && args.pathPattern === '/apps/actionButtons') { + return [] as any; + } + }} + > + + + {children} + + + + + ), + }, + ); + + expect(result.all[0]).toEqual([]); +}); + +it('should return `marketplace` and `installed` items if the user has `access-marketplace` permission', () => { + const { result } = renderHook( + () => { + return useAppsItems(); + }, + { + wrapper: ({ children }) => ( + + { + if (args.method === 'GET' && args.pathPattern === '/apps/actionButtons') { + return [] as any; + } + }} + > + + + + {children} + + + + + + ), + }, + ); + + expect(result.current[0]).toEqual( + expect.objectContaining({ + id: 'marketplace', + }), + ); + expect(result.current[1]).toEqual( + expect.objectContaining({ + id: 'installed', + }), + ); +}); + +it('should return `marketplace` and `installed` items if the user has `manage-apps` permission', () => { + const { result } = renderHook( + () => { + return useAppsItems(); + }, + { + wrapper: ({ children }) => ( + + { + if (args.method === 'GET' && args.pathPattern === '/apps/app-request/stats') { + return { + data: { + totalSeen: 0, + totalUnseen: 1, + }, + } as any; + } + if (args.method === 'GET' && args.pathPattern === '/apps/actionButtons') { + return [] as any; + } + + throw new Error('Method not mocked'); + }} + > + + + + {children} + + + + + + ), + }, + ); + + expect(result.current[0]).toEqual( + expect.objectContaining({ + id: 'marketplace', + }), + ); + expect(result.current[1]).toEqual( + expect.objectContaining({ + id: 'installed', + }), + ); + + expect(result.current[2]).toEqual( + expect.objectContaining({ + id: 'requested-apps', + }), + ); +}); + +it('should return one action from the server with no conditions', async () => { + const { result, waitForValueToChange } = renderHook( + () => { + return useAppsItems(); + }, + { + wrapper: ({ children }) => ( + + { + if (args.method === 'GET' && args.pathPattern === '/apps/app-request/stats') { + return { + data: { + totalSeen: 0, + totalUnseen: 1, + }, + } as any; + } + if (args.method === 'GET' && args.pathPattern === '/apps/actionButtons') { + return [ + { + appId: 'APP_ID', + actionId: 'ACTION_ID', + labelI18n: 'LABEL_I18N', + context: 'userDropdownAction', + }, + ] as any; + } + + throw new Error('Method not mocked'); + }} + > + + + + {children} + + + + + + ), + }, + ); + + expect(result.current[0]).toEqual( + expect.objectContaining({ + id: 'marketplace', + }), + ); + expect(result.current[1]).toEqual( + expect.objectContaining({ + id: 'installed', + }), + ); + + await waitForValueToChange(() => result.current[3]); + + expect(result.current[3]).toEqual( + expect.objectContaining({ + id: 'APP_ID_ACTION_ID', + }), + ); +}); + +describe('User Dropdown actions with role conditions', () => { + it('should return the action if the user has admin role', async () => { + const { result, waitForValueToChange } = renderHook( + () => { + return useAppsItems(); + }, + { + wrapper: ({ children }) => ( + + { + if (args.method === 'GET' && args.pathPattern === '/apps/app-request/stats') { + return { + data: { + totalSeen: 0, + totalUnseen: 1, + }, + } as any; + } + if (args.method === 'GET' && args.pathPattern === '/apps/actionButtons') { + return [ + { + appId: 'APP_ID', + actionId: 'ACTION_ID', + labelI18n: 'LABEL_I18N', + context: 'userDropdownAction', + when: { + hasOneRole: ['admin'], + }, + }, + ] as any; + } + + throw new Error('Method not mocked'); + }} + > + + + + {children} + + + + + + ), + }, + ); + + await waitForValueToChange(() => { + return queryClient.isFetching(); + }); + + expect(result.current[0]).toEqual( + expect.objectContaining({ + id: 'marketplace', + }), + ); + expect(result.current[1]).toEqual( + expect.objectContaining({ + id: 'installed', + }), + ); + + expect(result.current[3]).toEqual( + expect.objectContaining({ + id: 'APP_ID_ACTION_ID', + }), + ); + }); + + it('should return filter the action if the user doesn`t have admin role', async () => { + const { result, waitForValueToChange } = renderHook( + () => { + return useAppsItems(); + }, + { + wrapper: ({ children }) => ( + + { + if (args.method === 'GET' && args.pathPattern === '/apps/app-request/stats') { + return { + data: { + totalSeen: 0, + totalUnseen: 1, + }, + } as any; + } + if (args.method === 'GET' && args.pathPattern === '/apps/actionButtons') { + return [ + { + appId: 'APP_ID', + actionId: 'ACTION_ID', + labelI18n: 'LABEL_I18N', + context: 'userDropdownAction', + when: { + hasOneRole: ['admin'], + }, + }, + ] as any; + } + + throw new Error('Method not mocked'); + }} + > + + + + {children} + + + + + + ), + }, + ); + + await waitForValueToChange(() => { + return queryClient.isFetching(); + }); + + expect(result.current[0]).toEqual( + expect.objectContaining({ + id: 'marketplace', + }), + ); + expect(result.current[1]).toEqual( + expect.objectContaining({ + id: 'installed', + }), + ); + + expect(result.current[2]).toEqual( + expect.objectContaining({ + id: 'requested-apps', + }), + ); + + expect(result.current[3]).toEqual(undefined); + }); +}); + +describe('User Dropdown actions with permission conditions', () => { + it('should return the action if the user has manage-apps permission', async () => { + const { result, waitForValueToChange } = renderHook( + () => { + return useAppsItems(); + }, + { + wrapper: ({ children }) => ( + + { + if (args.method === 'GET' && args.pathPattern === '/apps/app-request/stats') { + return { + data: { + totalSeen: 0, + totalUnseen: 1, + }, + } as any; + } + if (args.method === 'GET' && args.pathPattern === '/apps/actionButtons') { + return [ + { + appId: 'APP_ID', + actionId: 'ACTION_ID', + labelI18n: 'LABEL_I18N', + context: 'userDropdownAction', + when: { + hasOnePermission: ['manage-apps'], + }, + }, + ] as any; + } + + throw new Error('Method not mocked'); + }} + > + + + + {children} + + + + + + ), + }, + ); + + await waitForValueToChange(() => { + return queryClient.isFetching(); + }); + + expect(result.current[0]).toEqual( + expect.objectContaining({ + id: 'marketplace', + }), + ); + expect(result.current[1]).toEqual( + expect.objectContaining({ + id: 'installed', + }), + ); + + expect(result.current[3]).toEqual( + expect.objectContaining({ + id: 'APP_ID_ACTION_ID', + }), + ); + }); + + it('should return filter the action if the user doesn`t have `any` permission', async () => { + const { result, waitForValueToChange } = renderHook( + () => { + return useAppsItems(); + }, + { + wrapper: ({ children }) => ( + + { + if (args.method === 'GET' && args.pathPattern === '/apps/app-request/stats') { + return { + data: { + totalSeen: 0, + totalUnseen: 1, + }, + } as any; + } + if (args.method === 'GET' && args.pathPattern === '/apps/actionButtons') { + return [ + { + appId: 'APP_ID', + actionId: 'ACTION_ID', + labelI18n: 'LABEL_I18N', + context: 'userDropdownAction', + when: { + hasOnePermission: ['any'], + }, + }, + ] as any; + } + + throw new Error('Method not mocked'); + }} + > + + + + {children} + + + + + + ), + }, + ); + + await waitForValueToChange(() => { + return queryClient.isFetching(); + }); + + expect(result.current[3]).toEqual(undefined); + }); +}); + +export const MockedUiKitActionManager = ({ children }: { children: React.ReactNode }) => { + return {children}; +}; + +const queryClient = new QueryClient({ + defaultOptions: { + queries: { + // ✅ turns retries off + retry: false, + }, + }, +}); +afterEach(() => { + queryClient.clear(); +}); diff --git a/apps/meteor/jest.client.config.ts b/apps/meteor/jest.client.config.ts index db3a03ecf03f..bcd9f8f5432a 100644 --- a/apps/meteor/jest.client.config.ts +++ b/apps/meteor/jest.client.config.ts @@ -3,7 +3,11 @@ export default { testEnvironment: 'jsdom', modulePathIgnorePatterns: ['/dist/'], - testMatch: ['/client/hooks/**.spec.[jt]s?(x)', '/client/components/**.spec.[jt]s?(x)'], + testMatch: [ + '/client/hooks/**.spec.[jt]s?(x)', + '/client/components/**.spec.[jt]s?(x)', + '/client/sidebar/header/actions/hooks/**/**.spec.[jt]s?(x)', + ], transform: { '^.+\\.(t|j)sx?$': '@swc/jest', }, diff --git a/packages/mock-providers/src/MockedAuthorizationContext.tsx b/packages/mock-providers/src/MockedAuthorizationContext.tsx index 18f791f1dd98..4d2a5c05a473 100644 --- a/packages/mock-providers/src/MockedAuthorizationContext.tsx +++ b/packages/mock-providers/src/MockedAuthorizationContext.tsx @@ -1,14 +1,25 @@ import React from 'react'; import { AuthorizationContext } from '@rocket.chat/ui-contexts'; -export const MockedAuthorizationContext = ({ permissions = [], children }: { permissions: string[]; children: React.ReactNode }) => { +export const MockedAuthorizationContext = ({ + permissions = [], + roles = [], + children, +}: { + permissions: string[]; + roles?: string[]; + children: React.ReactNode; +}) => { return ( [() => (): void => undefined, (): boolean => permissions.includes(id)], - queryAtLeastOnePermission: () => [() => (): void => undefined, (): boolean => false], - queryAllPermissions: () => [() => (): void => undefined, (): boolean => false], - queryRole: () => [() => (): void => undefined, (): boolean => false], + queryAtLeastOnePermission: (ids: string[]) => [ + () => (): void => undefined, + (): boolean => ids.some((id) => permissions.includes(id)), + ], + queryAllPermissions: (ids: string[]) => [() => (): void => undefined, (): boolean => ids.every((id) => permissions.includes(id))], + queryRole: (id: string) => [() => (): void => undefined, (): boolean => roles.includes(id)], roleStore: { roles: {}, emit: (): void => undefined, diff --git a/packages/mock-providers/src/MockedServerContext.tsx b/packages/mock-providers/src/MockedServerContext.tsx index f3c13004ed77..6b23664f8167 100644 --- a/packages/mock-providers/src/MockedServerContext.tsx +++ b/packages/mock-providers/src/MockedServerContext.tsx @@ -33,6 +33,7 @@ export const MockedServerContext = ({ }) => { return handleRequest(args); }, + getStream: () => () => undefined, } as any } > diff --git a/packages/ui-contexts/src/AuthorizationContext.ts b/packages/ui-contexts/src/AuthorizationContext.ts index a321c408672f..d077568e772e 100644 --- a/packages/ui-contexts/src/AuthorizationContext.ts +++ b/packages/ui-contexts/src/AuthorizationContext.ts @@ -1,4 +1,4 @@ -import type { IRole } from '@rocket.chat/core-typings'; +import type { IRole, IRoom } from '@rocket.chat/core-typings'; import type { IEmitter } from '@rocket.chat/emitter'; import { createContext } from 'react'; import type { ObjectId } from 'mongodb'; @@ -27,7 +27,11 @@ export type AuthorizationContextValue = { scope?: string | ObjectId, scopedRoles?: IRole['_id'][], ): [subscribe: (onStoreChange: () => void) => () => void, getSnapshot: () => boolean]; - queryRole(role: string | ObjectId): [subscribe: (onStoreChange: () => void) => () => void, getSnapshot: () => boolean]; + queryRole( + role: string | ObjectId, + scope?: IRoom['_id'], + ignoreSubscriptions?: boolean, + ): [subscribe: (onStoreChange: () => void) => () => void, getSnapshot: () => boolean]; roleStore: RoleStore; }; From 355e50d86cc6915a79604c2e4168f737027b64d3 Mon Sep 17 00:00:00 2001 From: Yash Rajpal <58601732+yash-rajpal@users.noreply.github.com> Date: Mon, 17 Jul 2023 16:10:47 +0530 Subject: [PATCH 010/342] refactor: replace useForm in favor of RHF on Prune Messages (#29704) Co-authored-by: Tasso Evangelista <2263066+tassoevan@users.noreply.github.com> --- .../PruneMessages/PruneMessages.stories.tsx | 18 ++- .../PruneMessages/PruneMessages.tsx | 53 ++----- .../PruneMessagesDateTimeRow.tsx | 35 ++-- .../PruneMessages/PruneMessagesWithData.tsx | 150 ++++++++++-------- apps/meteor/package.json | 2 +- 5 files changed, 130 insertions(+), 128 deletions(-) diff --git a/apps/meteor/client/views/room/contextualBar/PruneMessages/PruneMessages.stories.tsx b/apps/meteor/client/views/room/contextualBar/PruneMessages/PruneMessages.stories.tsx index a894bca693ab..e0b653f85026 100644 --- a/apps/meteor/client/views/room/contextualBar/PruneMessages/PruneMessages.stories.tsx +++ b/apps/meteor/client/views/room/contextualBar/PruneMessages/PruneMessages.stories.tsx @@ -1,5 +1,6 @@ import type { ComponentMeta, ComponentStory } from '@storybook/react'; import React from 'react'; +import { FormProvider, useForm } from 'react-hook-form'; import { Contextualbar } from '../../../../components/Contextualbar'; import PruneMessages from './PruneMessages'; @@ -11,7 +12,21 @@ export default { layout: 'fullscreen', actions: { argTypesRegex: '^on.*' }, }, - decorators: [(fn) => {fn()}], + decorators: [ + (fn) => { + const methods = useForm({ + defaultValues: { + pinned: true, + }, + }); + + return ( + + {fn()} + + ); + }, + ], } as ComponentMeta; const Template: ComponentStory = (args) => ; @@ -20,6 +35,5 @@ export const Default = Template.bind({}); export const WithCallout = Template.bind({}); WithCallout.args = { - values: { pinned: true }, callOutText: 'This is a callout', }; diff --git a/apps/meteor/client/views/room/contextualBar/PruneMessages/PruneMessages.tsx b/apps/meteor/client/views/room/contextualBar/PruneMessages/PruneMessages.tsx index 3dd4f7862609..9f64bbc6504b 100644 --- a/apps/meteor/client/views/room/contextualBar/PruneMessages/PruneMessages.tsx +++ b/apps/meteor/client/views/room/contextualBar/PruneMessages/PruneMessages.tsx @@ -3,6 +3,7 @@ import { useUniqueId } from '@rocket.chat/fuselage-hooks'; import { useTranslation } from '@rocket.chat/ui-contexts'; import type { ReactElement } from 'react'; import React from 'react'; +import { useFormContext, Controller } from 'react-hook-form'; import { ContextualbarHeader, @@ -14,36 +15,18 @@ import { } from '../../../../components/Contextualbar'; import UserAutoCompleteMultiple from '../../../../components/UserAutoCompleteMultiple'; import PruneMessagesDateTimeRow from './PruneMessagesDateTimeRow'; -import type { initialValues } from './PruneMessagesWithData'; type PruneMessagesProps = { callOutText?: string; validateText?: string; users: string[]; - values: Record; - handlers: Record void>; onClickClose: () => void; onClickPrune: () => void; }; -const PruneMessages = ({ callOutText, validateText, values, handlers, onClickClose, onClickPrune }: PruneMessagesProps): ReactElement => { +const PruneMessages = ({ callOutText, validateText, onClickClose, onClickPrune }: PruneMessagesProps): ReactElement => { const t = useTranslation(); - - const { newerDate, newerTime, olderDate, olderTime, users, inclusive, pinned, discussion, threads, attached } = - values as typeof initialValues; - - const { - handleNewerDate, - handleNewerTime, - handleOlderDate, - handleOlderTime, - handleInclusive, - handlePinned, - handleDiscussion, - handleThreads, - handleAttached, - handleUsers, - } = handlers; + const { control, register } = useFormContext(); const inclusiveCheckboxId = useUniqueId(); const pinnedCheckboxId = useUniqueId(); @@ -59,47 +42,45 @@ const PruneMessages = ({ callOutText, validateText, values, handlers, onClickClo {onClickClose && } - - + + {t('Only_from_users')} - + ( + + )} + /> - + {t('Inclusive')} - + {t('RetentionPolicy_DoNotPrunePinned')} - + {t('RetentionPolicy_DoNotPruneDiscussion')} - + {t('RetentionPolicy_DoNotPruneThreads')} - + {t('Files_only')} diff --git a/apps/meteor/client/views/room/contextualBar/PruneMessages/PruneMessagesDateTimeRow.tsx b/apps/meteor/client/views/room/contextualBar/PruneMessages/PruneMessagesDateTimeRow.tsx index bfb48f2859c8..9ca08a593d61 100644 --- a/apps/meteor/client/views/room/contextualBar/PruneMessages/PruneMessagesDateTimeRow.tsx +++ b/apps/meteor/client/views/room/contextualBar/PruneMessages/PruneMessagesDateTimeRow.tsx @@ -1,29 +1,26 @@ import { Field, InputBox, Box, Margins } from '@rocket.chat/fuselage'; import type { ReactElement } from 'react'; import React from 'react'; +import { useFormContext } from 'react-hook-form'; type PruneMessagesDateTimeRowProps = { label: string; - dateTime: { - date: string; - time: string; - }; - handleDateTime: { - date: (eventOrValue: unknown) => void; - time: (eventOrValue: unknown) => void; - }; + field: 'newer' | 'older'; }; -const PruneMessagesDateTimeRow = ({ label, dateTime, handleDateTime }: PruneMessagesDateTimeRowProps): ReactElement => ( - - {label} - - - - - - - -); +const PruneMessagesDateTimeRow = ({ label, field }: PruneMessagesDateTimeRowProps): ReactElement => { + const { register } = useFormContext(); + return ( + + {label} + + + + + + + + ); +}; export default PruneMessagesDateTimeRow; diff --git a/apps/meteor/client/views/room/contextualBar/PruneMessages/PruneMessagesWithData.tsx b/apps/meteor/client/views/room/contextualBar/PruneMessages/PruneMessagesWithData.tsx index abbde3da6250..b294df0af5d8 100644 --- a/apps/meteor/client/views/room/contextualBar/PruneMessages/PruneMessagesWithData.tsx +++ b/apps/meteor/client/views/room/contextualBar/PruneMessages/PruneMessagesWithData.tsx @@ -4,10 +4,10 @@ import { useMutableCallback } from '@rocket.chat/fuselage-hooks'; import { useSetModal, useToastMessageDispatch, useUserRoom, useEndpoint, useTranslation } from '@rocket.chat/ui-contexts'; import moment from 'moment'; import type { ReactElement } from 'react'; -import React, { useCallback, useEffect, useState } from 'react'; +import React, { useCallback, useMemo, useState } from 'react'; +import { useForm, FormProvider } from 'react-hook-form'; import GenericModal from '../../../../components/GenericModal'; -import { useForm } from '../../../../hooks/useForm'; import type { ToolboxContextValue } from '../../contexts/ToolboxContext'; import PruneMessages from './PruneMessages'; @@ -18,10 +18,14 @@ const getTimeZoneOffset = (): string => { }; export const initialValues = { - newerDate: '', - newerTime: '', - olderDate: '', - olderTime: '', + newer: { + date: '', + time: '', + }, + older: { + date: '', + time: '', + }, users: [], inclusive: false, pinned: false, @@ -41,18 +45,31 @@ const PruneMessagesWithData = ({ rid, tabBar }: { rid: IRoom['_id']; tabBar: Too const dispatchToastMessage = useToastMessageDispatch(); const pruneMessagesAction = useEndpoint('POST', '/v1/rooms.cleanHistory'); - const [fromDate, setFromDate] = useState(new Date('0001-01-01T00:00:00Z')); - const [toDate, setToDate] = useState(new Date('9999-12-31T23:59:59Z')); - const [callOutText, setCallOutText] = useState(); - const [validateText, setValidateText] = useState(); const [counter, setCounter] = useState(0); - const { values, handlers, reset } = useForm(initialValues); - const { newerDate, newerTime, olderDate, olderTime, users, inclusive, pinned, discussion, threads, attached } = - values as typeof initialValues; + const methods = useForm({ defaultValues: initialValues }); + + const { + newer: { date: newerDate, time: newerTime }, + older: { date: olderDate, time: olderTime }, + users, + inclusive, + pinned, + discussion, + threads, + attached, + } = methods.watch(); + + const fromDate = useMemo(() => { + return new Date(`${newerDate || '0001-01-01'}T${newerTime || '00:00'}:00${getTimeZoneOffset()}`); + }, [newerDate, newerTime]); + + const toDate = useMemo(() => { + return new Date(`${olderDate || '9999-12-31'}T${olderTime || '23:59'}:59${getTimeZoneOffset()}`); + }, [olderDate, olderTime]); const handlePrune = useMutableCallback((): void => { - const handlePruneAction = async (): Promise => { + const handlePruneAction = async () => { const limit = DEFAULT_PRUNE_LIMIT; try { @@ -80,10 +97,10 @@ const PruneMessagesWithData = ({ rid, tabBar }: { rid: IRoom['_id']; tabBar: Too } dispatchToastMessage({ type: 'success', message: t('__count__message_pruned', { count }) }); - closeModal(); - reset(); + methods.reset(); } catch (error: unknown) { dispatchToastMessage({ type: 'error', message: error }); + } finally { closeModal(); } }; @@ -101,17 +118,7 @@ const PruneMessagesWithData = ({ rid, tabBar }: { rid: IRoom['_id']; tabBar: Too ); }); - useEffect(() => { - if (newerDate) { - setFromDate(new Date(`${newerDate}T${newerTime || '00:00'}:00${getTimeZoneOffset()}`)); - } - - if (olderDate) { - setToDate(new Date(`${olderDate}T${olderTime || '24:00'}:00${getTimeZoneOffset()}`)); - } - }, [newerDate, newerTime, olderDate, olderTime]); - - useEffect(() => { + const callOutText = useMemo(() => { const exceptPinned = pinned ? ` ${t('except_pinned', {})}` : ''; const ifFrom = users.length ? ` ${t('if_they_are_from', { @@ -122,73 +129,76 @@ const PruneMessagesWithData = ({ rid, tabBar }: { rid: IRoom['_id']; tabBar: Too const filesOrMessages = t(attached ? 'files' : 'messages', {}); if (newerDate && olderDate) { - setCallOutText( + return ( t('Prune_Warning_between', { postProcess: 'sprintf', sprintf: [filesOrMessages, name, moment(fromDate).format('L LT'), moment(toDate).format('L LT')], }) + - exceptPinned + - ifFrom, + exceptPinned + + ifFrom ); - } else if (newerDate) { - setCallOutText( + } + + if (newerDate) { + return ( t('Prune_Warning_after', { postProcess: 'sprintf', sprintf: [filesOrMessages, name, moment(fromDate).format('L LT')], }) + - exceptPinned + - ifFrom, + exceptPinned + + ifFrom ); - } else if (olderDate) { - setCallOutText( + } + + if (olderDate) { + return ( t('Prune_Warning_before', { postProcess: 'sprintf', sprintf: [filesOrMessages, name, moment(toDate).format('L LT')], }) + - exceptPinned + - ifFrom, - ); - } else { - setCallOutText( - t('Prune_Warning_all', { - postProcess: 'sprintf', - sprintf: [filesOrMessages, room && isDirectMessageRoom(room) && (room.name || room.usernames?.join(' x '))], - }) + - exceptPinned + - ifFrom, + exceptPinned + + ifFrom ); } + return ( + t('Prune_Warning_all', { + postProcess: 'sprintf', + sprintf: [filesOrMessages, room && ((isDirectMessageRoom(room) && room.usernames?.join(' x ')) || room.fname || room.name)], + }) + + exceptPinned + + ifFrom + ); + }, [attached, fromDate, newerDate, olderDate, pinned, room, t, toDate, users]); + + const validateText = useMemo(() => { if (fromDate > toDate) { - return setValidateText( - t('Newer_than_may_not_exceed_Older_than', { - postProcess: 'sprintf', - sprintf: [], - }), - ); + return t('Newer_than_may_not_exceed_Older_than', { + postProcess: 'sprintf', + sprintf: [], + }); } + if (isNaN(fromDate.getTime()) || isNaN(toDate.getTime())) { - return setValidateText( - t('error-invalid-date', { - postProcess: 'sprintf', - sprintf: [], - }), - ); + return t('error-invalid-date', { + postProcess: 'sprintf', + sprintf: [], + }); } - setValidateText(undefined); - }, [newerDate, olderDate, fromDate, toDate, attached, t, pinned, users, room]); + return undefined; + }, [fromDate, t, toDate]); return ( - + + + ); }; diff --git a/apps/meteor/package.json b/apps/meteor/package.json index 7235923ab100..ffef9505b2e6 100644 --- a/apps/meteor/package.json +++ b/apps/meteor/package.json @@ -49,7 +49,7 @@ "version": "node .scripts/version.js", "set-version": "node .scripts/set-version.js", "release": "meteor npm run set-version --silent", - "storybook": "cross-env NODE_OPTIONS=--max-old-space-size=8192 start-storybook -p 6006", + "storybook": "cross-env NODE_OPTIONS=--max-old-space-size=8192 start-storybook -p 6006 --no-version-updates", "docker:start": "docker-compose up" }, "license": "MIT", From 4643466994b9846c6887bcd7cae7438b74d5c139 Mon Sep 17 00:00:00 2001 From: Tasso Evangelista Date: Mon, 17 Jul 2023 18:44:48 -0300 Subject: [PATCH 011/342] regression: Router not capturing search string parameters (#29846) --- .../meteor/client/providers/RouterProvider.tsx | 2 +- .../externals/meteor/kadira-flow-router.d.ts | 18 ++++++++++++------ 2 files changed, 13 insertions(+), 7 deletions(-) diff --git a/apps/meteor/client/providers/RouterProvider.tsx b/apps/meteor/client/providers/RouterProvider.tsx index af0448ce34e8..0dd7ee31deed 100644 --- a/apps/meteor/client/providers/RouterProvider.tsx +++ b/apps/meteor/client/providers/RouterProvider.tsx @@ -126,7 +126,7 @@ const routesSubscribers = new Set<() => void>(); const updateFlowRouter = () => { if (FlowRouter._initialized) { FlowRouter._updateCallbacks(); - FlowRouter._page.dispatch({ path: FlowRouter._current.path, params: {} }); + FlowRouter._page.dispatch(new FlowRouter._page.Context(FlowRouter._current.path)); return; } diff --git a/apps/meteor/definition/externals/meteor/kadira-flow-router.d.ts b/apps/meteor/definition/externals/meteor/kadira-flow-router.d.ts index 8dfc0de5a01b..946d3d074e26 100644 --- a/apps/meteor/definition/externals/meteor/kadira-flow-router.d.ts +++ b/apps/meteor/definition/externals/meteor/kadira-flow-router.d.ts @@ -164,14 +164,20 @@ declare module 'meteor/kadira:flow-router' { _current: Current; } + namespace page { + function start(): void; + function stop(): void; + function show(path: string): void; + function dispatch(ctx: page.Context): void; + + class Context { + constructor(path: string, state?: object): Context; + } + } + const FlowRouter: Router & { Route: typeof Route; Router: typeof Router; - _page: { - start(): void; - stop(): void; - show(path: string): void; - dispatch(ctx: { path: string; params: any }): void; - }; + _page: typeof page; }; } From 53822be591971229e961db19749b9e1f571e9c02 Mon Sep 17 00:00:00 2001 From: Douglas Fabris Date: Mon, 17 Jul 2023 23:20:49 -0300 Subject: [PATCH 012/342] test: Cover sidebar audit/administration items hooks (#29821) Co-authored-by: Guilherme Gazzo --- .../client/hooks/useAppActionButtons.ts | 4 +- .../hooks/useAdministrationItems.spec.tsx | 203 ++++++++++++++++++ .../actions/hooks/useAdministrationItems.tsx | 15 +- .../actions/hooks/useAuditItems.spec.tsx | 171 +++++++++++++++ .../client/views/hooks/useUpgradeTabParams.ts | 2 +- .../{jest.client.config.ts => jest.config.ts} | 0 apps/meteor/package.json | 2 +- .../mock-providers/src/MockedModalContext.tsx | 20 ++ .../src/MockedServerContext.tsx | 16 +- packages/mock-providers/src/index.ts | 5 + .../src/hooks/useFeaturePreviewList.spec.tsx | 33 +++ 11 files changed, 456 insertions(+), 15 deletions(-) create mode 100644 apps/meteor/client/sidebar/header/actions/hooks/useAdministrationItems.spec.tsx create mode 100644 apps/meteor/client/sidebar/header/actions/hooks/useAuditItems.spec.tsx rename apps/meteor/{jest.client.config.ts => jest.config.ts} (100%) create mode 100644 packages/mock-providers/src/MockedModalContext.tsx create mode 100644 packages/mock-providers/src/index.ts diff --git a/apps/meteor/client/hooks/useAppActionButtons.ts b/apps/meteor/client/hooks/useAppActionButtons.ts index 6d5aac3e92a3..4eb28a393d1b 100644 --- a/apps/meteor/client/hooks/useAppActionButtons.ts +++ b/apps/meteor/client/hooks/useAppActionButtons.ts @@ -95,9 +95,9 @@ export const useUserDropdownAppsActionButtons = () => { ?.filter((action) => { return applyButtonFilters(action); }) - .map((action, key) => { + .map((action) => { return { - id: action.actionId + key, + id: `${action.appId}_${action.actionId}`, // icon: action.icon as GenericMenuItemProps['icon'], content: action.labelI18n, onClick: () => { diff --git a/apps/meteor/client/sidebar/header/actions/hooks/useAdministrationItems.spec.tsx b/apps/meteor/client/sidebar/header/actions/hooks/useAdministrationItems.spec.tsx new file mode 100644 index 000000000000..a050e0f48060 --- /dev/null +++ b/apps/meteor/client/sidebar/header/actions/hooks/useAdministrationItems.spec.tsx @@ -0,0 +1,203 @@ +import { MockedAuthorizationContext } from '@rocket.chat/mock-providers/src/MockedAuthorizationContext'; +import { MockedModalContext } from '@rocket.chat/mock-providers/src/MockedModalContext'; +import { MockedServerContext } from '@rocket.chat/mock-providers/src/MockedServerContext'; +import { MockedSettingsContext } from '@rocket.chat/mock-providers/src/MockedSettingsContext'; +import { MockedUserContext } from '@rocket.chat/mock-providers/src/MockedUserContext'; +import { QueryClientProvider, QueryClient } from '@tanstack/react-query'; +import { renderHook } from '@testing-library/react-hooks'; +import React from 'react'; + +import { useAdministrationItems } from './useAdministrationItems'; + +const queryClient = new QueryClient({ + defaultOptions: { + queries: { + // ✅ turns retries off + retry: false, + }, + }, +}); + +beforeEach(() => { + queryClient.clear(); +}); + +it('should not show upgrade item if has license and not have trial', async () => { + const { result, waitFor } = renderHook(() => useAdministrationItems(), { + wrapper: ({ children }) => ( + + { + if (args.method === 'GET' && args.pathPattern === '/v1/licenses.get') { + return { + licenses: [ + { + modules: ['testModule'], + meta: { trial: false }, + }, + ], + } as any; + } + + if (args.method === 'GET' && args.pathPattern === '/v1/cloud.registrationStatus') { + return { + registrationStatus: { + workspaceRegistered: false, + }, + }; + } + }} + > + + + + {children} + + + + + + ), + }); + + await waitFor(() => Boolean(result.all.length > 1)); + expect(result.current).toEqual([]); +}); + +it('should return an upgrade item if not have license or if have a trial', async () => { + const { result, waitFor } = renderHook(() => useAdministrationItems(), { + wrapper: ({ children }) => ( + + { + if (args.method === 'GET' && args.pathPattern === '/v1/licenses.get') { + return { + licenses: [ + { + modules: [], + }, + ], + } as any; + } + + if (args.method === 'GET' && args.pathPattern === '/v1/cloud.registrationStatus') { + return { + registrationStatus: { + workspaceRegistered: false, + }, + }; + } + }} + > + + + + {children} + + + + + + ), + }); + + await waitFor(() => { + return !queryClient.isFetching(); + }); + + expect(result.current[0]).toEqual( + expect.objectContaining({ + id: 'showUpgradeItem', + }), + ); +}); + +it('should return omnichannel item if has `view-livechat-manager` permission ', async () => { + const { result, waitFor } = renderHook(() => useAdministrationItems(), { + wrapper: ({ children }) => ( + + { + if (args.method === 'GET' && args.pathPattern === '/v1/licenses.get') { + return { + licenses: [ + { + modules: [], + }, + ], + } as any; + } + + if (args.method === 'GET' && args.pathPattern === '/v1/cloud.registrationStatus') { + return { + registrationStatus: { + workspaceRegistered: false, + }, + }; + } + }} + > + + + + {children} + + + + + + ), + }); + + await waitFor(() => Boolean(result.current.length)); + expect(result.current[0]).toEqual( + expect.objectContaining({ + id: 'omnichannel', + }), + ); +}); + +it('should show administration item if has at least one admin permission', async () => { + const { result, waitFor } = renderHook(() => useAdministrationItems(), { + wrapper: ({ children }) => ( + + { + if (args.method === 'GET' && args.pathPattern === '/v1/licenses.get') { + return { + licenses: [ + { + modules: [], + }, + ], + } as any; + } + + if (args.method === 'GET' && args.pathPattern === '/v1/cloud.registrationStatus') { + return { + registrationStatus: { + workspaceRegistered: false, + }, + }; + } + }} + > + + + + {children} + + + + + + ), + }); + + await waitFor(() => Boolean(result.current.length)); + expect(result.current[0]).toEqual( + expect.objectContaining({ + id: 'workspace', + }), + ); +}); diff --git a/apps/meteor/client/sidebar/header/actions/hooks/useAdministrationItems.tsx b/apps/meteor/client/sidebar/header/actions/hooks/useAdministrationItems.tsx index 3d4d640fecf2..ea678413a16b 100644 --- a/apps/meteor/client/sidebar/header/actions/hooks/useAdministrationItems.tsx +++ b/apps/meteor/client/sidebar/header/actions/hooks/useAdministrationItems.tsx @@ -1,20 +1,19 @@ import { useTranslation, useRoute, - useMethod, useSetModal, useRole, useRouter, useAtLeastOnePermission, usePermission, } from '@rocket.chat/ui-contexts'; -import { useQuery } from '@tanstack/react-query'; import React from 'react'; import type { UpgradeTabVariant } from '../../../../../lib/upgradeTab'; import { getUpgradeTabLabel, isFullyFeature } from '../../../../../lib/upgradeTab'; import Emoji from '../../../../components/Emoji'; import type { GenericMenuItemProps } from '../../../../components/GenericMenu/GenericMenuItem'; +import { useRegistrationStatus } from '../../../../hooks/useRegistrationStatus'; import RegisterWorkspaceModal from '../../../../views/admin/cloud/modals/RegisterWorkspaceModal'; import { useUpgradeTabParams } from '../../../../views/hooks/useUpgradeTabParams'; @@ -53,8 +52,8 @@ const ADMIN_PERMISSIONS = [ */ export const useAdministrationItems = (): GenericMenuItemProps[] => { - const router = useRouter(); const t = useTranslation(); + const router = useRouter(); const shouldShowAdminMenu = useAtLeastOnePermission(ADMIN_PERMISSIONS); @@ -66,9 +65,13 @@ export const useAdministrationItems = (): GenericMenuItemProps[] => { const isAdmin = useRole('admin'); const setModal = useSetModal(); - const checkCloudRegisterStatus = useMethod('cloud:checkRegisterStatus'); - const result = useQuery(['admin/cloud/register-status'], async () => checkCloudRegisterStatus()); - const { workspaceRegistered } = result.data || {}; + // TODO: DEPRECATE IT + // const checkCloudRegisterStatus = useMethod('cloud:checkRegisterStatus'); + // const result = useQuery(['admin/cloud/register-status'], async () => checkCloudRegisterStatus()); + // const { workspaceRegistered } = result.data || {}; + + const { data: registrationStatusData } = useRegistrationStatus(); + const workspaceRegistered = registrationStatusData?.registrationStatus?.workspaceRegistered ?? false; const handleRegisterWorkspaceClick = (): void => { const handleModalClose = (): void => setModal(null); diff --git a/apps/meteor/client/sidebar/header/actions/hooks/useAuditItems.spec.tsx b/apps/meteor/client/sidebar/header/actions/hooks/useAuditItems.spec.tsx new file mode 100644 index 000000000000..1cf43158db7c --- /dev/null +++ b/apps/meteor/client/sidebar/header/actions/hooks/useAuditItems.spec.tsx @@ -0,0 +1,171 @@ +import { MockedAuthorizationContext } from '@rocket.chat/mock-providers/src/MockedAuthorizationContext'; +import { MockedServerContext } from '@rocket.chat/mock-providers/src/MockedServerContext'; +import { MockedSettingsContext } from '@rocket.chat/mock-providers/src/MockedSettingsContext'; +import { MockedUserContext } from '@rocket.chat/mock-providers/src/MockedUserContext'; +import { QueryClientProvider, QueryClient } from '@tanstack/react-query'; +import { renderHook } from '@testing-library/react-hooks'; +import React from 'react'; + +import { useAuditItems } from './useAuditItems'; + +const queryClient = new QueryClient({ + defaultOptions: { + queries: { + // ✅ turns retries off + retry: false, + }, + }, +}); + +it('should return an empty array if doesn`t have license', async () => { + const { result, waitFor } = renderHook(() => useAuditItems(), { + wrapper: ({ children }) => ( + + { + if (methodName === 'license:getModules') { + return [] as any; + } + + throw new Error('Method not mocked'); + }} + > + + + {children} + + + + + ), + }); + + await waitFor(() => Boolean(result.all.length > 1)); + expect(result.current).toEqual([]); +}); + +it('should return an empty array if have license and not have permissions', async () => { + const { result, waitFor } = renderHook(() => useAuditItems(), { + wrapper: ({ children }) => ( + + { + if (methodName === 'license:getModules') { + return ['auditing'] as any; + } + + throw new Error('Method not mocked'); + }} + > + + + {children} + + + + + ), + }); + + await waitFor(() => Boolean(result.all.length > 1)); + expect(result.current).toEqual([]); +}); + +it('should return auditItems if have license and permissions', async () => { + const { result, waitFor } = renderHook(() => useAuditItems(), { + wrapper: ({ children }) => ( + + { + if (methodName === 'license:getModules') { + return ['auditing'] as any; + } + + throw new Error('Method not mocked'); + }} + > + + + {children} + + + + + ), + }); + + await waitFor(() => Boolean(result.current.length)); + expect(result.current[0]).toEqual( + expect.objectContaining({ + id: 'messages', + }), + ); + + expect(result.current[1]).toEqual( + expect.objectContaining({ + id: 'auditLog', + }), + ); +}); + +it('should return auditMessages item if have license and can-audit permission', async () => { + const { result, waitFor } = renderHook(() => useAuditItems(), { + wrapper: ({ children }) => ( + + { + if (methodName === 'license:getModules') { + return ['auditing'] as any; + } + + throw new Error('Method not mocked'); + }} + > + + + {children} + + + + + ), + }); + + await waitFor(() => Boolean(result.current.length)); + expect(result.current[0]).toEqual( + expect.objectContaining({ + id: 'messages', + }), + ); +}); + +it('should return audiLogs item if have license and can-audit-log permission', async () => { + const { result, waitFor } = renderHook(() => useAuditItems(), { + wrapper: ({ children }) => ( + + { + if (methodName === 'license:getModules') { + return ['auditing'] as any; + } + + throw new Error('Method not mocked'); + }} + > + + + {children} + + + + + ), + }); + + await waitFor(() => Boolean(result.current.length)); + expect(result.current[0]).toEqual( + expect.objectContaining({ + id: 'auditLog', + }), + ); +}); diff --git a/apps/meteor/client/views/hooks/useUpgradeTabParams.ts b/apps/meteor/client/views/hooks/useUpgradeTabParams.ts index 13cb2850d836..e051b69db8fa 100644 --- a/apps/meteor/client/views/hooks/useUpgradeTabParams.ts +++ b/apps/meteor/client/views/hooks/useUpgradeTabParams.ts @@ -13,7 +13,7 @@ export const useUpgradeTabParams = (): { tabType: UpgradeTabVariant | false; tri const { data: registrationStatusData, isSuccess: isSuccessRegistrationStatus } = useRegistrationStatus(); const registered = registrationStatusData?.registrationStatus?.workspaceRegistered ?? false; - const hasValidLicense = licensesData?.licenses.some((licence) => licence.modules.length > 0) ?? false; + const hasValidLicense = licensesData?.licenses.some((license) => license.modules.length > 0) ?? false; const hadExpiredTrials = cloudWorkspaceHadTrial ?? false; const trialLicense = licensesData?.licenses?.find(({ meta }) => meta?.trial); diff --git a/apps/meteor/jest.client.config.ts b/apps/meteor/jest.config.ts similarity index 100% rename from apps/meteor/jest.client.config.ts rename to apps/meteor/jest.config.ts diff --git a/apps/meteor/package.json b/apps/meteor/package.json index ffef9505b2e6..f3e19d15a50c 100644 --- a/apps/meteor/package.json +++ b/apps/meteor/package.json @@ -39,7 +39,7 @@ "testapi": "TS_NODE_COMPILER_OPTIONS='{\"module\": \"commonjs\"}' mocha --config ./.mocharc.api.js", "testunit": "npm run .testunit:definition && npm run .testunit:client && npm run .testunit:server", ".testunit:server": "TS_NODE_COMPILER_OPTIONS='{\"module\": \"commonjs\"}' mocha --config ./.mocharc.js", - ".testunit:client": "TS_NODE_COMPILER_OPTIONS='{\"module\": \"commonjs\"}' mocha --config ./.mocharc.client.js --exit", + ".testunit:client": "jest && TS_NODE_COMPILER_OPTIONS='{\"module\": \"commonjs\"}' mocha --config ./.mocharc.client.js --exit", ".testunit:definition": "TS_NODE_COMPILER_OPTIONS='{\"module\": \"commonjs\"}' mocha --config ./.mocharc.definition.js", "testunit-watch": "TS_NODE_COMPILER_OPTIONS='{\"module\": \"commonjs\"}' mocha --watch --config ./.mocharc.js", "test": "npm run testapi && npm run testui", diff --git a/packages/mock-providers/src/MockedModalContext.tsx b/packages/mock-providers/src/MockedModalContext.tsx new file mode 100644 index 000000000000..4654b55ba135 --- /dev/null +++ b/packages/mock-providers/src/MockedModalContext.tsx @@ -0,0 +1,20 @@ +import type { ReactNode } from 'react'; +import React from 'react'; +import { ModalContext } from '@rocket.chat/ui-contexts'; + +export const MockedModalContext = ({ children }: { children: React.ReactNode }) => { + const [currentModal, setCurrentModal] = React.useState(null); + + return ( + + {children} + + ); +}; diff --git a/packages/mock-providers/src/MockedServerContext.tsx b/packages/mock-providers/src/MockedServerContext.tsx index 6b23664f8167..32b9fb6068f5 100644 --- a/packages/mock-providers/src/MockedServerContext.tsx +++ b/packages/mock-providers/src/MockedServerContext.tsx @@ -1,20 +1,25 @@ import React from 'react'; import type { Serialized } from '@rocket.chat/core-typings'; import type { Method, OperationParams, OperationResult, PathPattern, UrlParams } from '@rocket.chat/rest-typings'; -import type { ServerMethodName, ServerMethodParameters } from '@rocket.chat/ui-contexts'; +import type { ServerMethodName, ServerMethodParameters, ServerMethodReturn } from '@rocket.chat/ui-contexts'; import { ServerContext } from '@rocket.chat/ui-contexts'; import { QueryClient, QueryClientProvider } from '@tanstack/react-query'; export const MockedServerContext = ({ handleRequest, + handleMethod, children, }: { - handleRequest: (args: { + handleRequest?: (args: { method: TMethod; pathPattern: TPathPattern; keys: UrlParams; params: OperationParams; }) => Promise>>; + handleMethod?: ( + methodName: MethodName, + ...args: ServerMethodParameters + ) => Promise>; children: React.ReactNode; }): any => { const [queryClient] = React.useState(() => new QueryClient()); @@ -23,15 +28,16 @@ export const MockedServerContext = ({ value={ { absoluteUrl: (path: string) => `http://localhost:3000/${path}`, - callMethod: (_methodName: MethodName, ..._args: ServerMethodParameters) => - Promise.reject('mock not implemented'), + callMethod: (methodName: MethodName, ...args: ServerMethodParameters) => { + return handleMethod?.(methodName, ...args); + }, callEndpoint: async (args: { method: TMethod; pathPattern: TPathPattern; keys: UrlParams; params: OperationParams; }) => { - return handleRequest(args); + return handleRequest?.(args); }, getStream: () => () => undefined, } as any diff --git a/packages/mock-providers/src/index.ts b/packages/mock-providers/src/index.ts new file mode 100644 index 000000000000..602316cbdd72 --- /dev/null +++ b/packages/mock-providers/src/index.ts @@ -0,0 +1,5 @@ +export * from './MockedAuthorizationContext'; +export * from './MockedModalContext'; +export * from './MockedServerContext'; +export * from './MockedSettingsContext'; +export * from './MockedUserContext'; diff --git a/packages/ui-client/src/hooks/useFeaturePreviewList.spec.tsx b/packages/ui-client/src/hooks/useFeaturePreviewList.spec.tsx index 5e1aacd7197b..2de617554115 100644 --- a/packages/ui-client/src/hooks/useFeaturePreviewList.spec.tsx +++ b/packages/ui-client/src/hooks/useFeaturePreviewList.spec.tsx @@ -71,3 +71,36 @@ it('should return 0 unseen features', () => { }), ); }); + +it('should ignore removed feature previews', () => { + const { result } = renderHook(() => useFeaturePreviewList(), { + wrapper: ({ children }) => ( + + + {children} + + + ), + }); + + expect(result.current).toEqual( + expect.objectContaining({ + featurePreviewEnabled: true, + unseenFeatures: defaultFeaturesPreview.length, + features: defaultFeaturesPreview, + }), + ); +}); From 3fb21241667a4b5f244c26c813c2cc5394154b3d Mon Sep 17 00:00:00 2001 From: Anik Dhabal Babu <81948346+anikdhabal@users.noreply.github.com> Date: Tue, 18 Jul 2023 19:57:01 +0530 Subject: [PATCH 013/342] fix: misleading of 'total' in team members & members fixed (#29090) Co-authored-by: Douglas Fabris <27704687+dougfabris@users.noreply.github.com> Co-authored-by: Hugo Costa <20212776+hugocostadev@users.noreply.github.com> --- .changeset/lazy-ghosts-design.md | 5 +++++ .../views/room/contextualBar/RoomMembers/RoomMembers.tsx | 8 ++------ apps/meteor/packages/rocketchat-i18n/i18n/en.i18n.json | 1 + 3 files changed, 8 insertions(+), 6 deletions(-) create mode 100644 .changeset/lazy-ghosts-design.md diff --git a/.changeset/lazy-ghosts-design.md b/.changeset/lazy-ghosts-design.md new file mode 100644 index 000000000000..080e9986cebb --- /dev/null +++ b/.changeset/lazy-ghosts-design.md @@ -0,0 +1,5 @@ +--- +'@rocket.chat/meteor': patch +--- + +Fixed misleading of 'total' in team members list inside Channel diff --git a/apps/meteor/client/views/room/contextualBar/RoomMembers/RoomMembers.tsx b/apps/meteor/client/views/room/contextualBar/RoomMembers/RoomMembers.tsx index 780f17084d59..95f2c43d1a95 100644 --- a/apps/meteor/client/views/room/contextualBar/RoomMembers/RoomMembers.tsx +++ b/apps/meteor/client/views/room/contextualBar/RoomMembers/RoomMembers.tsx @@ -120,15 +120,11 @@ const RoomMembers = ({ {!loading && members.length <= 0 && } - {!loading && members && members.length > 0 && ( + {!loading && members.length > 0 && ( <> - {t('Showing')}: {members.length} - - - - {t('Total')}: {total} + {t('Showing_current_of_total', { current: members.length, total })} diff --git a/apps/meteor/packages/rocketchat-i18n/i18n/en.i18n.json b/apps/meteor/packages/rocketchat-i18n/i18n/en.i18n.json index 5b23d85fc71d..154bf6af2d6e 100644 --- a/apps/meteor/packages/rocketchat-i18n/i18n/en.i18n.json +++ b/apps/meteor/packages/rocketchat-i18n/i18n/en.i18n.json @@ -4640,6 +4640,7 @@ "Show_video": "Show video", "Showing": "Showing", "Showing_archived_results": "

    Showing %s archived results

    ", + "Showing_current_of_total":"Showing {{current}} of {{total}}", "Showing_online_users": "Showing: {{total_showing}}, Online: {{online}}, Total: {{total}} users", "Showing_results": "

    Showing %s results

    ", "Showing_results_of": "Showing results %s - %s of %s", From 817d29b4765ff63ca7ce9e0356a77ec9278a1a24 Mon Sep 17 00:00:00 2001 From: Tiago Evangelista Pinto Date: Tue, 18 Jul 2023 11:27:10 -0300 Subject: [PATCH 014/342] regression: Calling inexistent functions AppMenu on action change (#29834) Co-authored-by: Tasso Evangelista Co-authored-by: Guilherme Gazzo --- apps/meteor/client/views/marketplace/AppMenu.js | 4 +++- apps/meteor/ee/client/apps/communication/websockets.js | 3 --- 2 files changed, 3 insertions(+), 4 deletions(-) diff --git a/apps/meteor/client/views/marketplace/AppMenu.js b/apps/meteor/client/views/marketplace/AppMenu.js index b243dde200bd..6f90781e6101 100644 --- a/apps/meteor/client/views/marketplace/AppMenu.js +++ b/apps/meteor/client/views/marketplace/AppMenu.js @@ -14,8 +14,10 @@ import semver from 'semver'; import WarningModal from '../../components/WarningModal'; import IframeModal from './IframeModal'; import UninstallGrandfatheredAppModal from './components/UninstallGrandfatheredAppModal/UninstallGrandfatheredAppModal'; -import { appEnabledStatuses, handleAPIError, appButtonProps, warnEnableDisableApp } from './helpers'; +import { appEnabledStatuses, appButtonProps } from './helpers'; +import { handleAPIError } from './helpers/handleAPIError'; import { marketplaceActions } from './helpers/marketplaceActions'; +import { warnEnableDisableApp } from './helpers/warnEnableDisableApp'; import { useAppInstallationHandler } from './hooks/useAppInstallationHandler'; import { useAppsCountQuery } from './hooks/useAppsCountQuery'; import { useOpenAppPermissionsReviewModal } from './hooks/useOpenAppPermissionsReviewModal'; diff --git a/apps/meteor/ee/client/apps/communication/websockets.js b/apps/meteor/ee/client/apps/communication/websockets.js index e8060539a665..932fd062168f 100644 --- a/apps/meteor/ee/client/apps/communication/websockets.js +++ b/apps/meteor/ee/client/apps/communication/websockets.js @@ -35,7 +35,6 @@ export class AppWebsocketReceiver extends Emitter { sdk.stream('apps', [AppEvents.COMMAND_UPDATED], this.onCommandAddedOrUpdated); sdk.stream('apps', [AppEvents.COMMAND_REMOVED], this.onCommandRemovedOrDisabled); sdk.stream('apps', [AppEvents.COMMAND_DISABLED], this.onCommandRemovedOrDisabled); - sdk.stream('apps', [AppEvents.ACTIONS_CHANGED], this.onActionsChanged); } registerListener(event, listener) { @@ -70,6 +69,4 @@ export class AppWebsocketReceiver extends Emitter { onCommandRemovedOrDisabled = (command) => { delete slashCommands.commands[command]; }; - - // onActionsChanged = () => loadButtons(); } From 682d0bc05a5c56a7f2364d25d761b35947f004cc Mon Sep 17 00:00:00 2001 From: Anik Dhabal Babu <81948346+anikdhabal@users.noreply.github.com> Date: Tue, 18 Jul 2023 21:07:00 +0530 Subject: [PATCH 015/342] fix: Time format of Retention Policy (#29651) Co-authored-by: Douglas Fabris <27704687+dougfabris@users.noreply.github.com> --- .changeset/friendly-glasses-mate.md | 5 +++++ .../client/components/InfoPanel/RetentionPolicyCallout.tsx | 2 +- 2 files changed, 6 insertions(+), 1 deletion(-) create mode 100644 .changeset/friendly-glasses-mate.md diff --git a/.changeset/friendly-glasses-mate.md b/.changeset/friendly-glasses-mate.md new file mode 100644 index 000000000000..6a7a7b4f8546 --- /dev/null +++ b/.changeset/friendly-glasses-mate.md @@ -0,0 +1,5 @@ +--- +'@rocket.chat/meteor': minor +--- + +fix: Time format of Retention Policy diff --git a/apps/meteor/client/components/InfoPanel/RetentionPolicyCallout.tsx b/apps/meteor/client/components/InfoPanel/RetentionPolicyCallout.tsx index 9e261c88af77..27202afa496c 100644 --- a/apps/meteor/client/components/InfoPanel/RetentionPolicyCallout.tsx +++ b/apps/meteor/client/components/InfoPanel/RetentionPolicyCallout.tsx @@ -13,7 +13,7 @@ type RetentionPolicyCalloutProps = { const RetentionPolicyCallout: FC = ({ filesOnlyDefault, excludePinnedDefault, maxAgeDefault }) => { const t = useTranslation(); - const time = useFormattedRelativeTime(maxAgeDefault * 1000 * 60 * 60 * 24); + const time = useFormattedRelativeTime(maxAgeDefault); return ( From 0cb3c71d86b06b3880751c0c7d3325ed8e778e49 Mon Sep 17 00:00:00 2001 From: Douglas Fabris Date: Tue, 18 Jul 2023 13:35:52 -0300 Subject: [PATCH 016/342] chore: Normalize `Contextualbar` footer buttons (#29789) --- .../admin/customEmoji/AddCustomEmoji.tsx | 84 ++-- .../admin/customEmoji/EditCustomEmoji.tsx | 93 ++-- .../admin/customSounds/AddCustomSound.tsx | 72 ++-- .../views/admin/customSounds/EditSound.tsx | 82 ++-- .../customUserStatus/CustomUserStatusForm.tsx | 80 ++-- .../client/views/admin/rooms/EditRoom.tsx | 274 ++++++------ .../client/views/admin/users/AddUser.js | 25 +- .../client/views/admin/users/EditUser.js | 27 +- .../client/views/admin/users/InviteUsers.tsx | 45 +- .../client/views/admin/users/UserForm.js | 400 +++++++++--------- .../Info/EditRoomInfo/EditChannel.js | 40 +- 11 files changed, 611 insertions(+), 611 deletions(-) diff --git a/apps/meteor/client/views/admin/customEmoji/AddCustomEmoji.tsx b/apps/meteor/client/views/admin/customEmoji/AddCustomEmoji.tsx index 8accf5c5e56c..6d12c25f5239 100644 --- a/apps/meteor/client/views/admin/customEmoji/AddCustomEmoji.tsx +++ b/apps/meteor/client/views/admin/customEmoji/AddCustomEmoji.tsx @@ -3,7 +3,7 @@ import { useTranslation } from '@rocket.chat/ui-contexts'; import type { ReactElement, ChangeEvent } from 'react'; import React, { useCallback, useState } from 'react'; -import { ContextualbarScrollableContent } from '../../../components/Contextualbar'; +import { ContextualbarScrollableContent, ContextualbarFooter } from '../../../components/Contextualbar'; import { useEndpointUpload } from '../../../hooks/useEndpointUpload'; import { useFileInput } from '../../../hooks/useFileInput'; @@ -75,48 +75,48 @@ const AddCustomEmoji = ({ close, onChange, ...props }: AddCustomEmojiProps): Rea }; return ( - - - {t('Name')} - - - - {errors.name && {t('error-the-field-is-required', { field: t('Name') })}} - - - {t('Aliases')} - - - - {errors.aliases && {t('Custom_Emoji_Error_Same_Name_And_Alias')}} - - - - {t('Custom_Emoji')} - - - {errors.emoji && {t('error-the-field-is-required', { field: t('Custom_Emoji') })}} - {newEmojiPreview && ( - - - - - - )} - - - - - - - - - - + + {errors.emoji && {t('error-the-field-is-required', { field: t('Custom_Emoji') })}} + {newEmojiPreview && ( + + + + + + )} + +
    + + + + + + + ); }; diff --git a/apps/meteor/client/views/admin/customEmoji/EditCustomEmoji.tsx b/apps/meteor/client/views/admin/customEmoji/EditCustomEmoji.tsx index 19081fe6e901..188802bb7c87 100644 --- a/apps/meteor/client/views/admin/customEmoji/EditCustomEmoji.tsx +++ b/apps/meteor/client/views/admin/customEmoji/EditCustomEmoji.tsx @@ -3,7 +3,7 @@ import { useSetModal, useToastMessageDispatch, useAbsoluteUrl, useTranslation } import type { FC, ChangeEvent } from 'react'; import React, { useCallback, useState, useMemo, useEffect } from 'react'; -import { ContextualbarScrollableContent } from '../../../components/Contextualbar'; +import { ContextualbarScrollableContent, ContextualbarFooter } from '../../../components/Contextualbar'; import GenericModal from '../../../components/GenericModal'; import { useEndpointAction } from '../../../hooks/useEndpointAction'; import { useEndpointUpload } from '../../../hooks/useEndpointUpload'; @@ -130,50 +130,53 @@ const EditCustomEmoji: FC = ({ close, onChange, data, ...p }; return ( - - - - {t('Name')} - - - - {errors.name && {t('error-the-field-is-required', { field: t('Name') })}} - - - {t('Aliases')} - - - - {errors.aliases && {t('Custom_Emoji_Error_Same_Name_And_Alias')}} - - - - {t('Custom_Emoji')} - - - {newEmojiPreview && ( - - - - - - )} - - - - - - - - - - - + <> + + + + {t('Name')} + + + + {errors.name && {t('error-the-field-is-required', { field: t('Name') })}} + + + {t('Aliases')} + + + + {errors.aliases && {t('Custom_Emoji_Error_Same_Name_And_Alias')}} + + + + {t('Custom_Emoji')} + + + {newEmojiPreview && ( + + + + + + )} + + + + + + + + + + + + + ); }; diff --git a/apps/meteor/client/views/admin/customSounds/AddCustomSound.tsx b/apps/meteor/client/views/admin/customSounds/AddCustomSound.tsx index 986422ea7c7c..35feae9113cc 100644 --- a/apps/meteor/client/views/admin/customSounds/AddCustomSound.tsx +++ b/apps/meteor/client/views/admin/customSounds/AddCustomSound.tsx @@ -3,7 +3,7 @@ import { useToastMessageDispatch, useMethod, useTranslation } from '@rocket.chat import type { ReactElement, FormEvent } from 'react'; import React, { useState, useCallback } from 'react'; -import { ContextualbarScrollableContent } from '../../../components/Contextualbar'; +import { ContextualbarScrollableContent, ContextualbarFooter } from '../../../components/Contextualbar'; import { useFileInput } from '../../../hooks/useFileInput'; import { validate, createSoundData } from './lib'; @@ -81,41 +81,41 @@ const AddCustomSound = ({ goToNew, close, onChange, ...props }: AddCustomSoundPr }, [dispatchToastMessage, goToNew, name, onChange, saveAction, sound, t]); return ( - - - {t('Name')} - - ): void => setName(e.currentTarget.value)} - placeholder={t('Name')} - /> - - - - {t('Sound_File_mp3')} - - - - {sound?.name || t('None')} - - - - - - - - - - - - + <> + + + {t('Name')} + + ): void => setName(e.currentTarget.value)} + placeholder={t('Name')} + /> + + + + {t('Sound_File_mp3')} + + + + {sound?.name || t('None')} + + + + + + + + + + + ); }; diff --git a/apps/meteor/client/views/admin/customSounds/EditSound.tsx b/apps/meteor/client/views/admin/customSounds/EditSound.tsx index fdf45739cf7a..19e3958c74a0 100644 --- a/apps/meteor/client/views/admin/customSounds/EditSound.tsx +++ b/apps/meteor/client/views/admin/customSounds/EditSound.tsx @@ -3,7 +3,7 @@ import { useSetModal, useToastMessageDispatch, useMethod, useTranslation } from import type { ReactElement, SyntheticEvent } from 'react'; import React, { useCallback, useState, useMemo, useEffect } from 'react'; -import { ContextualbarScrollableContent } from '../../../components/Contextualbar'; +import { ContextualbarScrollableContent, ContextualbarFooter } from '../../../components/Contextualbar'; import GenericModal from '../../../components/GenericModal'; import { useFileInput } from '../../../hooks/useFileInput'; import { validate, createSoundData } from './lib'; @@ -117,49 +117,43 @@ function EditSound({ close, onChange, data, ...props }: EditSoundProps): ReactEl const [clickUpload] = useFileInput(handleChangeFile, 'audio/mp3'); return ( - - - {t('Name')} - - ): void => setName(e.currentTarget.value)} - placeholder={t('Name')} - /> - - - - - {t('Sound_File_mp3')} - - - - {sound?.name || 'none'} - - - - - - - - - - - - - - - - - - - - + <> + + + {t('Name')} + + ): void => setName(e.currentTarget.value)} + placeholder={t('Name')} + /> + + + + {t('Sound_File_mp3')} + + + + {sound?.name || 'none'} + + + + + + + + + + + + + + ); } diff --git a/apps/meteor/client/views/admin/customUserStatus/CustomUserStatusForm.tsx b/apps/meteor/client/views/admin/customUserStatus/CustomUserStatusForm.tsx index 2c21db402199..f85172533ee5 100644 --- a/apps/meteor/client/views/admin/customUserStatus/CustomUserStatusForm.tsx +++ b/apps/meteor/client/views/admin/customUserStatus/CustomUserStatusForm.tsx @@ -1,12 +1,13 @@ import type { IUserStatus } from '@rocket.chat/core-typings'; import type { SelectOption } from '@rocket.chat/fuselage'; import { FieldGroup, Button, ButtonGroup, TextInput, Field, Select, Icon } from '@rocket.chat/fuselage'; +import { useUniqueId } from '@rocket.chat/fuselage-hooks'; import { useSetModal, useRoute, useToastMessageDispatch, useTranslation, useEndpoint } from '@rocket.chat/ui-contexts'; import type { ReactElement } from 'react'; import React, { useCallback } from 'react'; import { useForm, Controller } from 'react-hook-form'; -import { ContextualbarScrollableContent } from '../../../components/Contextualbar'; +import { ContextualbarScrollableContent, ContextualbarFooter } from '../../../components/Contextualbar'; import GenericModal from '../../../components/GenericModal'; type CustomUserStatusFormProps = { @@ -21,6 +22,7 @@ const CustomUserStatusForm = ({ onClose, onReload, status }: CustomUserStatusFor const setModal = useSetModal(); const route = useRoute('user-status'); const dispatchToastMessage = useToastMessageDispatch(); + const formId = useUniqueId(); const { register, @@ -86,51 +88,47 @@ const CustomUserStatusForm = ({ onClose, onReload, status }: CustomUserStatusFor ]; return ( - - - - {t('Name')} - - - - {errors?.name && {t('error-the-field-is-required', { field: t('Name') })}} - - - {t('Presence')} - - } + /> + {errors?.statusType && {t('error-the-field-is-required', { field: t('Presence') })}} + + + + + + + + {_id && ( + + + )} - - + + ); }; diff --git a/apps/meteor/client/views/admin/rooms/EditRoom.tsx b/apps/meteor/client/views/admin/rooms/EditRoom.tsx index f8dd530f17ed..8f91054ebdf7 100644 --- a/apps/meteor/client/views/admin/rooms/EditRoom.tsx +++ b/apps/meteor/client/views/admin/rooms/EditRoom.tsx @@ -7,7 +7,7 @@ import type { ReactElement } from 'react'; import React, { useState, useMemo } from 'react'; import { RoomSettingsEnum } from '../../../../definition/IRoomTypeConfig'; -import { ContextualbarScrollableContent } from '../../../components/Contextualbar'; +import { ContextualbarScrollableContent, ContextualbarFooter } from '../../../components/Contextualbar'; import GenericModal from '../../../components/GenericModal'; import RoomAvatarEditor from '../../../components/avatar/RoomAvatarEditor'; import { useEndpointAction } from '../../../hooks/useEndpointAction'; @@ -208,156 +208,152 @@ const EditRoom = ({ room, onChange, onDelete }: EditRoomProps): ReactElement => }); return ( - e.preventDefault())}> - {room.t !== 'd' && ( - - - - )} - - {t('Name')} - - - - - {room.t !== 'd' && ( - <> - {room.u && ( - - {t('Owner')} - - {room.u?.username} - - - )} - {canViewDescription && ( - - {t('Description')} - - - - - )} - {canViewAnnouncement && ( - - {t('Announcement')} - - - - - )} - {canViewTopic && ( - - {t('Topic')} - - - - - )} - {canViewType && ( - - - {t('Private')} + <> + e.preventDefault())}> + {room.t !== 'd' && ( + + + + )} + + {t('Name')} + + + + + {room.t !== 'd' && ( + <> + {room.u && ( + + {t('Owner')} - + {room.u?.username} - - {t('Just_invited_people_can_access_this_channel')} - - )} - {canViewReadOnly && ( - - - {t('Read_only')} + + )} + {canViewDescription && ( + + {t('Description')} - + - - {t('Only_authorized_users_can_write_new_messages')} - - )} - {readOnly && ( - - - {t('React_when_read_only')} + + )} + {canViewAnnouncement && ( + + {t('Announcement')} - + - - {t('React_when_read_only_changed_successfully')} - - )} - {canViewArchived && ( - - - {t('Room_archivation_state_true')} + + )} + {canViewTopic && ( + + {t('Topic')} - + - - - )} - - )} - - - {t('Default')} - - - - - - - - {t('Favorite')} - - - - - - - - {t('Featured')} - - - - - - - - - - - - + + )} + {canViewType && ( + + + {t('Private')} + + + + + {t('Just_invited_people_can_access_this_channel')} + + )} + {canViewReadOnly && ( + + + {t('Read_only')} + + + + + {t('Only_authorized_users_can_write_new_messages')} + + )} + {readOnly && ( + + + {t('React_when_read_only')} + + + + + {t('React_when_read_only_changed_successfully')} + + )} + {canViewArchived && ( + + + {t('Room_archivation_state_true')} + + + + + + )} + + )} + + + {t('Default')} + + + + + + + + {t('Favorite')} + + + - - - - - + + + + - - - + + + ); }; diff --git a/apps/meteor/client/views/admin/users/AddUser.js b/apps/meteor/client/views/admin/users/AddUser.js index e7d439317cb0..1260ededbb0b 100644 --- a/apps/meteor/client/views/admin/users/AddUser.js +++ b/apps/meteor/client/views/admin/users/AddUser.js @@ -1,10 +1,11 @@ -import { Field, Box, Button } from '@rocket.chat/fuselage'; +import { ButtonGroup, Button } from '@rocket.chat/fuselage'; import { useMutableCallback } from '@rocket.chat/fuselage-hooks'; import { useEndpoint, useRoute, useSetting, useTranslation } from '@rocket.chat/ui-contexts'; import { useQuery } from '@tanstack/react-query'; import React, { useMemo, useCallback, useState } from 'react'; import { parseCSV } from '../../../../lib/utils/parseCSV'; +import { ContextualbarFooter } from '../../../components/Contextualbar'; import { useEndpointAction } from '../../../hooks/useEndpointAction'; import { useForm } from '../../../hooks/useForm'; import UserForm from './UserForm'; @@ -119,18 +120,16 @@ const AddUser = ({ onReload, ...props }) => { const append = useMemo( () => ( - - - - - - - - + + + + + + ), [hasUnsavedChanges, reset, t, handleSave], ); diff --git a/apps/meteor/client/views/admin/users/EditUser.js b/apps/meteor/client/views/admin/users/EditUser.js index ae216dd4114a..59e37674685b 100644 --- a/apps/meteor/client/views/admin/users/EditUser.js +++ b/apps/meteor/client/views/admin/users/EditUser.js @@ -1,8 +1,9 @@ -import { Box, Field, Margins, Button } from '@rocket.chat/fuselage'; +import { ButtonGroup, Button } from '@rocket.chat/fuselage'; import { useMutableCallback } from '@rocket.chat/fuselage-hooks'; import { useRoute, useTranslation } from '@rocket.chat/ui-contexts'; import React, { useMemo, useState, useCallback } from 'react'; +import { ContextualbarFooter } from '../../../components/Contextualbar'; import UserAvatarEditor from '../../../components/avatar/UserAvatarEditor'; import { useEndpointAction } from '../../../hooks/useEndpointAction'; import { useEndpointUpload } from '../../../hooks/useEndpointUpload'; @@ -127,20 +128,16 @@ function EditUser({ data, roles, onReload, ...props }) { const append = useMemo( () => ( - - - - - - - - - - + + + + + + ), [handleSave, canSaveOrReset, reset, t], ); diff --git a/apps/meteor/client/views/admin/users/InviteUsers.tsx b/apps/meteor/client/views/admin/users/InviteUsers.tsx index 70b9878bd989..cad5e8c93041 100644 --- a/apps/meteor/client/views/admin/users/InviteUsers.tsx +++ b/apps/meteor/client/views/admin/users/InviteUsers.tsx @@ -1,10 +1,21 @@ -import { Box, Button, Icon, States, StatesAction, StatesActions, StatesSubtitle, StatesTitle, TextAreaInput } from '@rocket.chat/fuselage'; +import { + Box, + Button, + ButtonGroup, + Icon, + States, + StatesAction, + StatesActions, + StatesSubtitle, + StatesTitle, + TextAreaInput, +} from '@rocket.chat/fuselage'; import { useTranslation, useRoute } from '@rocket.chat/ui-contexts'; import type { ReactElement, ChangeEvent, ComponentProps } from 'react'; import React, { useCallback, useState } from 'react'; import { validateEmail } from '../../../../lib/emailValidator'; -import { ContextualbarScrollableContent } from '../../../components/Contextualbar'; +import { ContextualbarScrollableContent, ContextualbarFooter } from '../../../components/Contextualbar'; import { useSendInvitationEmailMutation } from './hooks/useSendInvitationEmailMutation'; import { useSmtpConfig } from './hooks/useSmtpConfig'; @@ -39,18 +50,24 @@ const InviteUsers = (props: InviteUsersProps): ReactElement => { } return ( - - - {t('Send_invitation_email')} - - - {t('Send_invitation_email_info')} - - ): void => setText(e.currentTarget.value)} /> - - + <> + + + {t('Send_invitation_email')} + + + {t('Send_invitation_email_info')} + + ): void => setText(e.currentTarget.value)} /> + + + + + + + ); }; diff --git a/apps/meteor/client/views/admin/users/UserForm.js b/apps/meteor/client/views/admin/users/UserForm.js index b0ce0cfdfbf6..696ed87d28dc 100644 --- a/apps/meteor/client/views/admin/users/UserForm.js +++ b/apps/meteor/client/views/admin/users/UserForm.js @@ -68,213 +68,162 @@ export default function UserForm({ formValues, formHandlers, availableRoles, app }, [watch, handleCustomFields]); return ( - e.preventDefault(), [])} autoComplete='off'> - - {prepend} - {useMemo( - () => ( - - {t('Name')} - - - - {errors && errors.name && {errors.name}} - - ), - [t, name, handleName, errors], - )} - {useMemo( - () => ( - - {t('Username')} - - } - /> - - {errors && errors.username && {errors.username}} - - ), - [t, username, handleUsername, errors], - )} - {useMemo( - () => ( - - {t('Email')} - - 0 ? 'error' : undefined} - onChange={handleEmail} - addon={} - /> - - {errors && errors.email && {errors.email}} - - - - {t('Verified')} - - - - - - ), - [t, email, handleEmail, verified, handleVerified, errors], - )} - {useMemo( - () => ( - - {t('StatusMessage')} - - } /> - - - ), - [t, statusText, handleStatusText], - )} - {useMemo( - () => ( - - {t('Bio')} - - } - /> - - - ), - [bio, handleBio, t], - )} - {useMemo( - () => ( - - {t('Nickname')} - - } - /> - - - ), - [nickname, handleNickname, t], - )} - - - e.preventDefault(), [])} autoComplete='off'> - {useMemo( - () => - !setRandomPassword && ( + <> + e.preventDefault(), [])} autoComplete='off'> + + {prepend} + {useMemo( + () => ( + + {t('Name')} + + + + {errors && errors.name && {errors.name}} + + ), + [t, name, handleName, errors], + )} + {useMemo( + () => ( - {t('Password')} + {t('Username')} - } - autoComplete='new-password' + value={username} + onChange={handleUsername} + addon={} /> - {errors && errors.password && {errors.password}} + {errors && errors.username && {errors.username}} ), - [t, password, handlePassword, errors, setRandomPassword], - )} - {useMemo( - () => ( - - - - - {t('Require_password_change')} - - ( + + {t('Email')} + + 0 ? 'error' : undefined} + onChange={handleEmail} + addon={} /> - - - - ), - [t, setRandomPassword, requirePasswordChange, handleRequirePasswordChange], - )} - {useMemo( - () => ( - - - - - {t('Set_random_password_and_send_by_email')} + + {errors && errors.email && {errors.email}} + + + + {t('Verified')} + + - - - - {!isSmtpEnabled && ( - - )} - - ), - [t, setRandomPassword, handleSetRandomPassword, isSmtpEnabled], - )} - {useMemo( - () => ( - - {t('Roles')} - - - - - ), - [availableRoles, handleRoles, roles, t], - )} - {useMemo( - () => - handleJoinDefaultChannels && ( + + + ), + [t, email, handleEmail, verified, handleVerified, errors], + )} + {useMemo( + () => ( + + {t('StatusMessage')} + + } /> + + + ), + [t, statusText, handleStatusText], + )} + {useMemo( + () => ( + + {t('Bio')} + + } + /> + + + ), + [bio, handleBio, t], + )} + {useMemo( + () => ( + + {t('Nickname')} + + } + /> + + + ), + [nickname, handleNickname, t], + )} + + + e.preventDefault(), [])} autoComplete='off'> + {useMemo( + () => + !setRandomPassword && ( + + {t('Password')} + + } + autoComplete='new-password' + /> + + {errors && errors.password && {errors.password}} + + ), + [t, password, handlePassword, errors, setRandomPassword], + )} + {useMemo( + () => ( - {t('Join_default_channels')} + {t('Require_password_change')} - + ), - [handleJoinDefaultChannels, t, joinDefaultChannels], - )} - {useMemo( - () => - handleSendWelcomeEmail && ( + [t, setRandomPassword, requirePasswordChange, handleRequirePasswordChange], + )} + {useMemo( + () => ( - {t('Send_welcome_email')} + {t('Set_random_password_and_send_by_email')} - + {!isSmtpEnabled && ( @@ -282,21 +231,74 @@ export default function UserForm({ formValues, formHandlers, availableRoles, app )} ), - [handleSendWelcomeEmail, t, sendWelcomeEmail, isSmtpEnabled], - )} - {useMemo( - () => - customFieldsMetadata && ( - <> - - {t('Custom_Fields')} - - + [t, setRandomPassword, handleSetRandomPassword, isSmtpEnabled], + )} + {useMemo( + () => ( + + {t('Roles')} + + + + ), - [customFieldsMetadata, control, t], - )} - {append} - - + [availableRoles, handleRoles, roles, t], + )} + {useMemo( + () => + handleJoinDefaultChannels && ( + + + + + {t('Join_default_channels')} + + + + + + ), + [handleJoinDefaultChannels, t, joinDefaultChannels], + )} + {useMemo( + () => + handleSendWelcomeEmail && ( + + + + + {t('Send_welcome_email')} + + + + + {!isSmtpEnabled && ( + + )} + + ), + [handleSendWelcomeEmail, t, sendWelcomeEmail, isSmtpEnabled], + )} + {useMemo( + () => + customFieldsMetadata && ( + <> + + {t('Custom_Fields')} + + + ), + [customFieldsMetadata, control, t], + )} + + + {append} + ); } diff --git a/apps/meteor/client/views/room/contextualBar/Info/EditRoomInfo/EditChannel.js b/apps/meteor/client/views/room/contextualBar/Info/EditRoomInfo/EditChannel.js index 90234643f353..0b46f7077902 100644 --- a/apps/meteor/client/views/room/contextualBar/Info/EditRoomInfo/EditChannel.js +++ b/apps/meteor/client/views/room/contextualBar/Info/EditRoomInfo/EditChannel.js @@ -37,6 +37,7 @@ import { ContextualbarTitle, ContextualbarClose, ContextualbarScrollableContent, + ContextualbarFooter, } from '../../../../../components/Contextualbar'; import GenericModal from '../../../../../components/GenericModal'; import RawText from '../../../../../components/RawText'; @@ -304,7 +305,6 @@ function EditChannel({ room, onClickClose, onClickBack }) { {room.teamId ? t('edit-team') : t('edit-room')} {onClickClose && } - e.preventDefault())}> @@ -489,29 +489,23 @@ function EditChannel({ room, onClickClose, onClickBack }) { )} - - - - - - - - - - - - - - - + + + + + + + + + ); } From 6f820d5fc03b4d2eb28a250db425c6d84804429e Mon Sep 17 00:00:00 2001 From: Tasso Evangelista Date: Tue, 18 Jul 2023 13:45:04 -0300 Subject: [PATCH 017/342] refactor: Spread REST endpoints and methods types across EE code (#29849) Co-authored-by: kodiakhq[bot] <49736102+kodiakhq[bot]@users.noreply.github.com> --- apps/meteor/client/main.ts | 1 - .../api-enterprise/server/canned-responses.ts | 36 ++++++ .../server/api/business-hours.ts | 16 +++ .../livechat-enterprise/server/api/units.ts | 21 ++++ .../CannedResponseEditWithData.tsx | 1 - apps/meteor/ee/definition/.eslintrc.json | 3 - apps/meteor/ee/definition/index.ts | 2 - apps/meteor/ee/definition/methods/index.ts | 1 - apps/meteor/ee/definition/methods/license.ts | 10 -- apps/meteor/ee/definition/rest/index.ts | 7 -- apps/meteor/ee/definition/rest/v1/chat.ts | 34 ------ .../definition/rest/v1/engagementDashboard.ts | 111 ------------------ .../rest/v1/omnichannel/businessHours.ts | 15 --- .../rest/v1/omnichannel/businessUnits.ts | 25 ---- .../rest/v1/omnichannel/cannedResponses.ts | 36 ------ .../definition/rest/v1/omnichannel/index.ts | 3 - apps/meteor/ee/definition/rest/v1/roles.ts | 84 ------------- .../rest/v1/sessions/SessionsPaginateProps.ts | 28 ----- .../rest/v1/sessions/SessionsProps.ts | 18 --- .../ee/definition/rest/v1/sessions/index.ts | 4 - .../definition/rest/v1/sessions/sessions.ts | 29 ----- apps/meteor/ee/server/api/chat.ts | 16 +++ .../api/engagementDashboard/channels.ts | 27 +++++ .../api/engagementDashboard/messages.ts | 38 ++++++ .../server/api/engagementDashboard/users.ts | 60 ++++++++++ apps/meteor/ee/server/api/roles.ts | 85 +++++++++++++- apps/meteor/ee/server/api/sessions.ts | 70 ++++++++++- 27 files changed, 366 insertions(+), 415 deletions(-) delete mode 100644 apps/meteor/ee/definition/.eslintrc.json delete mode 100644 apps/meteor/ee/definition/index.ts delete mode 100644 apps/meteor/ee/definition/methods/index.ts delete mode 100644 apps/meteor/ee/definition/methods/license.ts delete mode 100644 apps/meteor/ee/definition/rest/index.ts delete mode 100644 apps/meteor/ee/definition/rest/v1/chat.ts delete mode 100644 apps/meteor/ee/definition/rest/v1/engagementDashboard.ts delete mode 100644 apps/meteor/ee/definition/rest/v1/omnichannel/businessHours.ts delete mode 100644 apps/meteor/ee/definition/rest/v1/omnichannel/businessUnits.ts delete mode 100644 apps/meteor/ee/definition/rest/v1/omnichannel/cannedResponses.ts delete mode 100644 apps/meteor/ee/definition/rest/v1/omnichannel/index.ts delete mode 100644 apps/meteor/ee/definition/rest/v1/roles.ts delete mode 100644 apps/meteor/ee/definition/rest/v1/sessions/SessionsPaginateProps.ts delete mode 100644 apps/meteor/ee/definition/rest/v1/sessions/SessionsProps.ts delete mode 100644 apps/meteor/ee/definition/rest/v1/sessions/index.ts delete mode 100644 apps/meteor/ee/definition/rest/v1/sessions/sessions.ts diff --git a/apps/meteor/client/main.ts b/apps/meteor/client/main.ts index ed98c06c3297..6bcf2789e819 100644 --- a/apps/meteor/client/main.ts +++ b/apps/meteor/client/main.ts @@ -1,4 +1,3 @@ -import '../ee/definition'; import '../ee/client/ecdh'; import './polyfills'; diff --git a/apps/meteor/ee/app/api-enterprise/server/canned-responses.ts b/apps/meteor/ee/app/api-enterprise/server/canned-responses.ts index 676c3e5fe6a5..55a5cd55fbe3 100644 --- a/apps/meteor/ee/app/api-enterprise/server/canned-responses.ts +++ b/apps/meteor/ee/app/api-enterprise/server/canned-responses.ts @@ -1,10 +1,46 @@ import { Meteor } from 'meteor/meteor'; import { isPOSTCannedResponsesProps, isDELETECannedResponsesProps, isCannedResponsesProps } from '@rocket.chat/rest-typings'; +import type { ILivechatDepartment, IOmnichannelCannedResponse, IUser } from '@rocket.chat/core-typings'; +import type { PaginatedResult, PaginatedRequest } from '@rocket.chat/rest-typings'; import { API } from '../../../../app/api/server'; import { findAllCannedResponses, findAllCannedResponsesFilter, findOneCannedResponse } from './lib/canned-responses'; import { getPaginationItems } from '../../../../app/api/server/helpers/getPaginationItems'; +declare module '@rocket.chat/rest-typings' { + // eslint-disable-next-line @typescript-eslint/naming-convention + interface Endpoints { + '/v1/canned-responses': { + GET: ( + params: PaginatedRequest<{ + shortcut?: string; + text?: string; + scope?: string; + createdBy?: IUser['username']; + tags?: any; + departmentId?: ILivechatDepartment['_id']; + }>, + ) => PaginatedResult<{ + cannedResponses: IOmnichannelCannedResponse[]; + }>; + POST: (params: { + _id?: IOmnichannelCannedResponse['_id']; + shortcut: string; + text: string; + scope: string; + tags?: any; + departmentId?: ILivechatDepartment['_id']; + }) => void; + DELETE: (params: { _id: IOmnichannelCannedResponse['_id'] }) => void; + }; + '/v1/canned-responses/:_id': { + GET: () => { + cannedResponse: IOmnichannelCannedResponse; + }; + }; + } +} + API.v1.addRoute( 'canned-responses.get', { authRequired: true, permissionsRequired: ['view-canned-responses'] }, diff --git a/apps/meteor/ee/app/livechat-enterprise/server/api/business-hours.ts b/apps/meteor/ee/app/livechat-enterprise/server/api/business-hours.ts index 15d350798ac9..f0f7ea5646e6 100644 --- a/apps/meteor/ee/app/livechat-enterprise/server/api/business-hours.ts +++ b/apps/meteor/ee/app/livechat-enterprise/server/api/business-hours.ts @@ -1,7 +1,23 @@ +import type { ILivechatBusinessHour } from '@rocket.chat/core-typings'; + import { API } from '../../../../../app/api/server'; import { getPaginationItems } from '../../../../../app/api/server/helpers/getPaginationItems'; import { findBusinessHours } from '../business-hour/lib/business-hour'; +declare module '@rocket.chat/rest-typings' { + // eslint-disable-next-line @typescript-eslint/naming-convention + interface Endpoints { + '/v1/livechat/business-hours': { + GET: (params: { name?: string; offset: number; count: number; sort: Record }) => { + businessHours: ILivechatBusinessHour[]; + count: number; + offset: number; + total: number; + }; + }; + } +} + API.v1.addRoute( 'livechat/business-hours', { authRequired: true, permissionsRequired: ['view-livechat-business-hours'] }, diff --git a/apps/meteor/ee/app/livechat-enterprise/server/api/units.ts b/apps/meteor/ee/app/livechat-enterprise/server/api/units.ts index ca7eff11aced..77e39f4780e5 100644 --- a/apps/meteor/ee/app/livechat-enterprise/server/api/units.ts +++ b/apps/meteor/ee/app/livechat-enterprise/server/api/units.ts @@ -1,9 +1,30 @@ +import type { ILivechatUnitMonitor, IOmnichannelBusinessUnit } from '@rocket.chat/core-typings'; +import type { PaginatedResult } from '@rocket.chat/rest-typings'; + import { API } from '../../../../../app/api/server'; import { findUnits, findUnitById, findUnitMonitors } from './lib/units'; import { LivechatEnterprise } from '../lib/LivechatEnterprise'; import { findAllDepartmentsAvailable, findAllDepartmentsByUnit } from '../lib/Department'; import { getPaginationItems } from '../../../../../app/api/server/helpers/getPaginationItems'; +declare module '@rocket.chat/rest-typings' { + // eslint-disable-next-line @typescript-eslint/naming-convention + interface Endpoints { + '/v1/livechat/units/:unitId/monitors': { + GET: (params: { unitId: string }) => { monitors: ILivechatUnitMonitor[] }; + }; + '/v1/livechat/units': { + GET: (params: { text: string }) => PaginatedResult & { units: IOmnichannelBusinessUnit[] }; + POST: (params: { unitData: string; unitMonitors: string; unitDepartments: string }) => Omit; + }; + '/v1/livechat/units/:id': { + GET: () => IOmnichannelBusinessUnit | null; + POST: (params: { unitData: string; unitMonitors: string; unitDepartments: string }) => Omit; + DELETE: () => number; + }; + } +} + API.v1.addRoute( 'livechat/units/:unitId/monitors', { authRequired: true, permissionsRequired: ['manage-livechat-monitors'] }, diff --git a/apps/meteor/ee/client/omnichannel/cannedResponses/CannedResponseEditWithData.tsx b/apps/meteor/ee/client/omnichannel/cannedResponses/CannedResponseEditWithData.tsx index c2295685454a..73e39694adea 100644 --- a/apps/meteor/ee/client/omnichannel/cannedResponses/CannedResponseEditWithData.tsx +++ b/apps/meteor/ee/client/omnichannel/cannedResponses/CannedResponseEditWithData.tsx @@ -6,7 +6,6 @@ import React from 'react'; import { FormSkeleton } from '../../../../client/components/Skeleton'; import { AsyncStatePhase } from '../../../../client/hooks/useAsyncState'; import { useEndpointData } from '../../../../client/hooks/useEndpointData'; -import '../../../definition/rest'; import CannedResponseEdit from './CannedResponseEdit'; import CannedResponseEditWithDepartmentData from './CannedResponseEditWithDepartmentData'; diff --git a/apps/meteor/ee/definition/.eslintrc.json b/apps/meteor/ee/definition/.eslintrc.json deleted file mode 100644 index 03828d003760..000000000000 --- a/apps/meteor/ee/definition/.eslintrc.json +++ /dev/null @@ -1,3 +0,0 @@ -{ - "extends": "../../client/.eslintrc.json" -} diff --git a/apps/meteor/ee/definition/index.ts b/apps/meteor/ee/definition/index.ts deleted file mode 100644 index ea24a13c33ae..000000000000 --- a/apps/meteor/ee/definition/index.ts +++ /dev/null @@ -1,2 +0,0 @@ -import './methods'; -import './rest'; diff --git a/apps/meteor/ee/definition/methods/index.ts b/apps/meteor/ee/definition/methods/index.ts deleted file mode 100644 index 3986165b2481..000000000000 --- a/apps/meteor/ee/definition/methods/index.ts +++ /dev/null @@ -1 +0,0 @@ -import './license'; diff --git a/apps/meteor/ee/definition/methods/license.ts b/apps/meteor/ee/definition/methods/license.ts deleted file mode 100644 index 9b5784596f8c..000000000000 --- a/apps/meteor/ee/definition/methods/license.ts +++ /dev/null @@ -1,10 +0,0 @@ -import '@rocket.chat/ui-contexts'; -import type { ILicenseTag } from '../../app/license/definition/ILicenseTag'; - -declare module '@rocket.chat/ui-contexts' { - // eslint-disable-next-line @typescript-eslint/naming-convention - export interface ServerMethods { - 'license:getModules': () => string[]; - 'license:getTags': () => ILicenseTag[]; - } -} diff --git a/apps/meteor/ee/definition/rest/index.ts b/apps/meteor/ee/definition/rest/index.ts deleted file mode 100644 index 1f206f8e8c52..000000000000 --- a/apps/meteor/ee/definition/rest/index.ts +++ /dev/null @@ -1,7 +0,0 @@ -// TODO: chapter day backend - move to rest ee definitions to another package - -import './v1/engagementDashboard'; -import './v1/omnichannel'; -import './v1/sessions'; -import './v1/chat'; -import './v1/roles'; diff --git a/apps/meteor/ee/definition/rest/v1/chat.ts b/apps/meteor/ee/definition/rest/v1/chat.ts deleted file mode 100644 index dee7cadc6542..000000000000 --- a/apps/meteor/ee/definition/rest/v1/chat.ts +++ /dev/null @@ -1,34 +0,0 @@ -import type { IMessage, ReadReceipt } from '@rocket.chat/core-typings'; -import Ajv from 'ajv'; - -const ajv = new Ajv({ - coerceTypes: true, -}); - -type GetMessageReadReceiptsProps = { - messageId: IMessage['_id']; -}; - -const getMessageReadReceiptsPropsSchema = { - type: 'object', - properties: { - messageId: { - type: 'string', - }, - }, - required: ['messageId'], - additionalProperties: false, -}; - -export const isGetMessageReadReceiptsProps = ajv.compile(getMessageReadReceiptsPropsSchema); - -declare module '@rocket.chat/rest-typings' { - // eslint-disable-next-line @typescript-eslint/naming-convention - interface Endpoints { - '/v1/chat.getMessageReadReceipts': { - GET: (params: GetMessageReadReceiptsProps) => { - receipts: ReadReceipt[]; - }; - }; - } -} diff --git a/apps/meteor/ee/definition/rest/v1/engagementDashboard.ts b/apps/meteor/ee/definition/rest/v1/engagementDashboard.ts deleted file mode 100644 index 4a7a504f7731..000000000000 --- a/apps/meteor/ee/definition/rest/v1/engagementDashboard.ts +++ /dev/null @@ -1,111 +0,0 @@ -import type { IDirectMessageRoom, IRoom, IUser } from '@rocket.chat/core-typings'; - -declare module '@rocket.chat/rest-typings' { - // eslint-disable-next-line @typescript-eslint/naming-convention - interface Endpoints { - '/v1/engagement-dashboard/channels/list': { - GET: (params: { start: string; end: string; offset?: number; count?: number }) => { - channels: { - room: { - _id: IRoom['_id']; - name: IRoom['name'] | IRoom['fname']; - ts: IRoom['ts']; - t: IRoom['t']; - _updatedAt: IRoom['_updatedAt']; - usernames?: IDirectMessageRoom['usernames']; - }; - messages: number; - lastWeekMessages: number; - diffFromLastWeek: number; - }[]; - count: number; - offset: number; - total: number; - }; - }; - '/v1/engagement-dashboard/messages/origin': { - GET: (params: { start: string; end: string }) => { - origins: { - t: IRoom['t']; - messages: number; - }[]; - }; - }; - '/v1/engagement-dashboard/messages/top-five-popular-channels': { - GET: (params: { start: string; end: string }) => { - channels: { - t: IRoom['t']; - messages: number; - name: IRoom['name'] | IRoom['fname']; - usernames?: IDirectMessageRoom['usernames']; - }[]; - }; - }; - '/v1/engagement-dashboard/messages/messages-sent': { - GET: (params: { start: string; end: string }) => { - days: { day: Date; messages: number }[]; - period: { - count: number; - variation: number; - }; - yesterday: { - count: number; - variation: number; - }; - }; - }; - '/v1/engagement-dashboard/users/active-users': { - GET: (params: { start: string; end: string }) => { - month: { - day: number; - month: number; - year: number; - usersList: IUser['_id'][]; - users: number; - }[]; - }; - }; - '/v1/engagement-dashboard/users/chat-busier/weekly-data': { - GET: (params: { start: string }) => { - month: { - users: number; - day: number; - month: number; - year: number; - }[]; - }; - }; - '/v1/engagement-dashboard/users/chat-busier/hourly-data': { - GET: (params: { start: string }) => { - hours: { - users: number; - hour: number; - }[]; - }; - }; - '/v1/engagement-dashboard/users/users-by-time-of-the-day-in-a-week': { - GET: (params: { start: string; end: string }) => { - week: { - users: number; - hour: number; - day: number; - month: number; - year: number; - }[]; - }; - }; - '/v1/engagement-dashboard/users/new-users': { - GET: (params: { start: string; end: string }) => { - days: { day: Date; users: number }[]; - period: { - count: number; - variation: number; - }; - yesterday: { - count: number; - variation: number; - }; - }; - }; - } -} diff --git a/apps/meteor/ee/definition/rest/v1/omnichannel/businessHours.ts b/apps/meteor/ee/definition/rest/v1/omnichannel/businessHours.ts deleted file mode 100644 index 4de8be18bda6..000000000000 --- a/apps/meteor/ee/definition/rest/v1/omnichannel/businessHours.ts +++ /dev/null @@ -1,15 +0,0 @@ -import type { ILivechatBusinessHour } from '@rocket.chat/core-typings'; - -declare module '@rocket.chat/rest-typings' { - // eslint-disable-next-line @typescript-eslint/naming-convention - interface Endpoints { - '/v1/livechat/business-hours': { - GET: (params: { name?: string; offset: number; count: number; sort: Record }) => { - businessHours: ILivechatBusinessHour[]; - count: number; - offset: number; - total: number; - }; - }; - } -} diff --git a/apps/meteor/ee/definition/rest/v1/omnichannel/businessUnits.ts b/apps/meteor/ee/definition/rest/v1/omnichannel/businessUnits.ts deleted file mode 100644 index eff9f042132b..000000000000 --- a/apps/meteor/ee/definition/rest/v1/omnichannel/businessUnits.ts +++ /dev/null @@ -1,25 +0,0 @@ -import type { ILivechatUnitMonitor, IOmnichannelBusinessUnit } from '@rocket.chat/core-typings'; -import type { PaginatedResult } from '@rocket.chat/rest-typings'; - -declare module '@rocket.chat/rest-typings' { - // eslint-disable-next-line @typescript-eslint/naming-convention - interface Endpoints { - '/v1/livechat/units.list': { - GET: (params: { text: string }) => PaginatedResult & { - units: IOmnichannelBusinessUnit[]; - }; - }; - '/v1/livechat/units/:unitId/monitors': { - GET: (params: { unitId: string }) => { monitors: ILivechatUnitMonitor[] }; - }; - '/v1/livechat/units': { - GET: (params: { text: string }) => PaginatedResult & { units: IOmnichannelBusinessUnit[] }; - POST: (params: { unitData: string; unitMonitors: string; unitDepartments: string }) => Omit; - }; - '/v1/livechat/units/:id': { - GET: () => IOmnichannelBusinessUnit | null; - POST: (params: { unitData: string; unitMonitors: string; unitDepartments: string }) => Omit; - DELETE: () => number; - }; - } -} diff --git a/apps/meteor/ee/definition/rest/v1/omnichannel/cannedResponses.ts b/apps/meteor/ee/definition/rest/v1/omnichannel/cannedResponses.ts deleted file mode 100644 index f2c0fd042bfb..000000000000 --- a/apps/meteor/ee/definition/rest/v1/omnichannel/cannedResponses.ts +++ /dev/null @@ -1,36 +0,0 @@ -import type { ILivechatDepartment, IOmnichannelCannedResponse, IUser } from '@rocket.chat/core-typings'; -import type { PaginatedResult, PaginatedRequest } from '@rocket.chat/rest-typings'; - -declare module '@rocket.chat/rest-typings' { - // eslint-disable-next-line @typescript-eslint/naming-convention - interface Endpoints { - '/v1/canned-responses': { - GET: ( - params: PaginatedRequest<{ - shortcut?: string; - text?: string; - scope?: string; - createdBy?: IUser['username']; - tags?: any; - departmentId?: ILivechatDepartment['_id']; - }>, - ) => PaginatedResult<{ - cannedResponses: IOmnichannelCannedResponse[]; - }>; - POST: (params: { - _id?: IOmnichannelCannedResponse['_id']; - shortcut: string; - text: string; - scope: string; - tags?: any; - departmentId?: ILivechatDepartment['_id']; - }) => void; - DELETE: (params: { _id: IOmnichannelCannedResponse['_id'] }) => void; - }; - '/v1/canned-responses/:_id': { - GET: () => { - cannedResponse: IOmnichannelCannedResponse; - }; - }; - } -} diff --git a/apps/meteor/ee/definition/rest/v1/omnichannel/index.ts b/apps/meteor/ee/definition/rest/v1/omnichannel/index.ts deleted file mode 100644 index 324b4087895a..000000000000 --- a/apps/meteor/ee/definition/rest/v1/omnichannel/index.ts +++ /dev/null @@ -1,3 +0,0 @@ -import './businessHours'; -import './businessUnits'; -import './cannedResponses'; diff --git a/apps/meteor/ee/definition/rest/v1/roles.ts b/apps/meteor/ee/definition/rest/v1/roles.ts deleted file mode 100644 index 49ea8c5d929e..000000000000 --- a/apps/meteor/ee/definition/rest/v1/roles.ts +++ /dev/null @@ -1,84 +0,0 @@ -import type { IRole } from '@rocket.chat/core-typings'; -import Ajv from 'ajv'; - -const ajv = new Ajv({ - coerceTypes: true, -}); - -type RoleCreateProps = Pick & Partial>; - -const roleCreatePropsSchema = { - type: 'object', - properties: { - name: { - type: 'string', - }, - description: { - type: 'string', - nullable: true, - }, - scope: { - type: 'string', - enum: ['Users', 'Subscriptions'], - nullable: true, - }, - mandatory2fa: { - type: 'boolean', - nullable: true, - }, - }, - required: ['name'], - additionalProperties: false, -}; - -export const isRoleCreateProps = ajv.compile(roleCreatePropsSchema); - -type RoleUpdateProps = { - roleId: IRole['_id']; - name: IRole['name']; -} & Partial; - -const roleUpdatePropsSchema = { - type: 'object', - properties: { - roleId: { - type: 'string', - }, - name: { - type: 'string', - }, - description: { - type: 'string', - nullable: true, - }, - scope: { - type: 'string', - enum: ['Users', 'Subscriptions'], - nullable: true, - }, - mandatory2fa: { - type: 'boolean', - nullable: true, - }, - }, - required: ['roleId', 'name'], - additionalProperties: false, -}; - -export const isRoleUpdateProps = ajv.compile(roleUpdatePropsSchema); - -declare module '@rocket.chat/rest-typings' { - // eslint-disable-next-line @typescript-eslint/naming-convention - interface Endpoints { - '/v1/roles.create': { - POST: (params: RoleCreateProps) => { - role: IRole; - }; - }; - '/v1/roles.update': { - POST: (role: RoleUpdateProps) => { - role: IRole; - }; - }; - } -} diff --git a/apps/meteor/ee/definition/rest/v1/sessions/SessionsPaginateProps.ts b/apps/meteor/ee/definition/rest/v1/sessions/SessionsPaginateProps.ts deleted file mode 100644 index 20a7b145ee01..000000000000 --- a/apps/meteor/ee/definition/rest/v1/sessions/SessionsPaginateProps.ts +++ /dev/null @@ -1,28 +0,0 @@ -import type { PaginatedRequest } from '@rocket.chat/rest-typings'; -import Ajv from 'ajv'; - -const ajv = new Ajv({ coerceTypes: true }); - -export type SessionsPaginateProps = PaginatedRequest<{ - filter?: string; -}>; - -export const isSessionsPaginateProps = ajv.compile({ - type: 'object', - properties: { - offset: { - type: 'number', - }, - count: { - type: 'number', - }, - filter: { - type: 'string', - }, - sort: { - type: 'string', - }, - }, - required: [], - additionalProperties: false, -}); diff --git a/apps/meteor/ee/definition/rest/v1/sessions/SessionsProps.ts b/apps/meteor/ee/definition/rest/v1/sessions/SessionsProps.ts deleted file mode 100644 index e014db56a9e7..000000000000 --- a/apps/meteor/ee/definition/rest/v1/sessions/SessionsProps.ts +++ /dev/null @@ -1,18 +0,0 @@ -import Ajv from 'ajv'; - -const ajv = new Ajv({ coerceTypes: true }); - -export type SessionsProps = { - sessionId: string; -}; - -export const isSessionsProps = ajv.compile({ - type: 'object', - properties: { - sessionId: { - type: 'string', - }, - }, - required: ['sessionId'], - additionalProperties: false, -}); diff --git a/apps/meteor/ee/definition/rest/v1/sessions/index.ts b/apps/meteor/ee/definition/rest/v1/sessions/index.ts deleted file mode 100644 index 445c557a74cf..000000000000 --- a/apps/meteor/ee/definition/rest/v1/sessions/index.ts +++ /dev/null @@ -1,4 +0,0 @@ -import './sessions'; - -export * from './SessionsProps'; -export * from './SessionsPaginateProps'; diff --git a/apps/meteor/ee/definition/rest/v1/sessions/sessions.ts b/apps/meteor/ee/definition/rest/v1/sessions/sessions.ts deleted file mode 100644 index 8f006e437af5..000000000000 --- a/apps/meteor/ee/definition/rest/v1/sessions/sessions.ts +++ /dev/null @@ -1,29 +0,0 @@ -import type { ISession, DeviceManagementSession, DeviceManagementPopulatedSession } from '@rocket.chat/core-typings'; -import type { PaginatedResult } from '@rocket.chat/rest-typings'; - -import type { SessionsPaginateProps } from './SessionsPaginateProps'; -import type { SessionsProps } from './SessionsProps'; - -declare module '@rocket.chat/rest-typings' { - // eslint-disable-next-line @typescript-eslint/naming-convention - interface Endpoints { - '/v1/sessions/list': { - GET: (params: SessionsPaginateProps) => PaginatedResult<{ sessions: Array }>; - }; - '/v1/sessions/info': { - GET: (params: SessionsProps) => DeviceManagementSession; - }; - '/v1/sessions/logout.me': { - POST: (params: SessionsProps) => Pick; - }; - '/v1/sessions/list.all': { - GET: (params: SessionsPaginateProps) => PaginatedResult<{ sessions: Array }>; - }; - '/v1/sessions/info.admin': { - GET: (params: SessionsProps) => DeviceManagementPopulatedSession; - }; - '/v1/sessions/logout': { - POST: (params: SessionsProps) => Pick; - }; - } -} diff --git a/apps/meteor/ee/server/api/chat.ts b/apps/meteor/ee/server/api/chat.ts index faeca9455bb0..43eaa3cccf9c 100644 --- a/apps/meteor/ee/server/api/chat.ts +++ b/apps/meteor/ee/server/api/chat.ts @@ -1,8 +1,24 @@ import { Meteor } from 'meteor/meteor'; +import type { IMessage, ReadReceipt } from '@rocket.chat/core-typings'; import { API } from '../../../app/api/server/api'; import { hasLicense } from '../../app/license/server/license'; +type GetMessageReadReceiptsProps = { + messageId: IMessage['_id']; +}; + +declare module '@rocket.chat/rest-typings' { + // eslint-disable-next-line @typescript-eslint/naming-convention + interface Endpoints { + '/v1/chat.getMessageReadReceipts': { + GET: (params: GetMessageReadReceiptsProps) => { + receipts: ReadReceipt[]; + }; + }; + } +} + API.v1.addRoute( 'chat.getMessageReadReceipts', { authRequired: true }, diff --git a/apps/meteor/ee/server/api/engagementDashboard/channels.ts b/apps/meteor/ee/server/api/engagementDashboard/channels.ts index 8c2834253e06..b9d454ccf3e7 100644 --- a/apps/meteor/ee/server/api/engagementDashboard/channels.ts +++ b/apps/meteor/ee/server/api/engagementDashboard/channels.ts @@ -1,10 +1,37 @@ import { check, Match } from 'meteor/check'; +import type { IDirectMessageRoom, IRoom } from '@rocket.chat/core-typings'; import { API } from '../../../../app/api/server'; import { findAllChannelsWithNumberOfMessages } from '../../lib/engagementDashboard/channels'; import { isDateISOString, mapDateForAPI } from '../../lib/engagementDashboard/date'; import { getPaginationItems } from '../../../../app/api/server/helpers/getPaginationItems'; +declare module '@rocket.chat/rest-typings' { + // eslint-disable-next-line @typescript-eslint/naming-convention + interface Endpoints { + '/v1/engagement-dashboard/channels/list': { + GET: (params: { start: string; end: string; offset?: number; count?: number }) => { + channels: { + room: { + _id: IRoom['_id']; + name: IRoom['name'] | IRoom['fname']; + ts: IRoom['ts']; + t: IRoom['t']; + _updatedAt: IRoom['_updatedAt']; + usernames?: IDirectMessageRoom['usernames']; + }; + messages: number; + lastWeekMessages: number; + diffFromLastWeek: number; + }[]; + count: number; + offset: number; + total: number; + }; + }; + } +} + API.v1.addRoute( 'engagement-dashboard/channels/list', { diff --git a/apps/meteor/ee/server/api/engagementDashboard/messages.ts b/apps/meteor/ee/server/api/engagementDashboard/messages.ts index 2aac85c38b5c..826e03f40af1 100644 --- a/apps/meteor/ee/server/api/engagementDashboard/messages.ts +++ b/apps/meteor/ee/server/api/engagementDashboard/messages.ts @@ -1,4 +1,5 @@ import { check, Match } from 'meteor/check'; +import type { IDirectMessageRoom, IRoom } from '@rocket.chat/core-typings'; import { API } from '../../../../app/api/server'; import { @@ -8,6 +9,43 @@ import { } from '../../lib/engagementDashboard/messages'; import { isDateISOString, transformDatesForAPI } from '../../lib/engagementDashboard/date'; +declare module '@rocket.chat/rest-typings' { + // eslint-disable-next-line @typescript-eslint/naming-convention + interface Endpoints { + '/v1/engagement-dashboard/messages/origin': { + GET: (params: { start: string; end: string }) => { + origins: { + t: IRoom['t']; + messages: number; + }[]; + }; + }; + '/v1/engagement-dashboard/messages/top-five-popular-channels': { + GET: (params: { start: string; end: string }) => { + channels: { + t: IRoom['t']; + messages: number; + name: IRoom['name'] | IRoom['fname']; + usernames?: IDirectMessageRoom['usernames']; + }[]; + }; + }; + '/v1/engagement-dashboard/messages/messages-sent': { + GET: (params: { start: string; end: string }) => { + days: { day: Date; messages: number }[]; + period: { + count: number; + variation: number; + }; + yesterday: { + count: number; + variation: number; + }; + }; + }; + } +} + API.v1.addRoute( 'engagement-dashboard/messages/messages-sent', { diff --git a/apps/meteor/ee/server/api/engagementDashboard/users.ts b/apps/meteor/ee/server/api/engagementDashboard/users.ts index a5f921b24fa4..23b31a0d5236 100644 --- a/apps/meteor/ee/server/api/engagementDashboard/users.ts +++ b/apps/meteor/ee/server/api/engagementDashboard/users.ts @@ -1,4 +1,5 @@ import { check, Match } from 'meteor/check'; +import type { IUser } from '@rocket.chat/core-typings'; import { API } from '../../../../app/api/server'; import { @@ -10,6 +11,65 @@ import { } from '../../lib/engagementDashboard/users'; import { isDateISOString, transformDatesForAPI } from '../../lib/engagementDashboard/date'; +declare module '@rocket.chat/rest-typings' { + // eslint-disable-next-line @typescript-eslint/naming-convention + interface Endpoints { + '/v1/engagement-dashboard/users/active-users': { + GET: (params: { start: string; end: string }) => { + month: { + day: number; + month: number; + year: number; + usersList: IUser['_id'][]; + users: number; + }[]; + }; + }; + '/v1/engagement-dashboard/users/chat-busier/weekly-data': { + GET: (params: { start: string }) => { + month: { + users: number; + day: number; + month: number; + year: number; + }[]; + }; + }; + '/v1/engagement-dashboard/users/chat-busier/hourly-data': { + GET: (params: { start: string }) => { + hours: { + users: number; + hour: number; + }[]; + }; + }; + '/v1/engagement-dashboard/users/users-by-time-of-the-day-in-a-week': { + GET: (params: { start: string; end: string }) => { + week: { + users: number; + hour: number; + day: number; + month: number; + year: number; + }[]; + }; + }; + '/v1/engagement-dashboard/users/new-users': { + GET: (params: { start: string; end: string }) => { + days: { day: Date; users: number }[]; + period: { + count: number; + variation: number; + }; + yesterday: { + count: number; + variation: number; + }; + }; + }; + } +} + API.v1.addRoute( 'engagement-dashboard/users/new-users', { diff --git a/apps/meteor/ee/server/api/roles.ts b/apps/meteor/ee/server/api/roles.ts index 5cad626fe99b..b3b666bd536a 100644 --- a/apps/meteor/ee/server/api/roles.ts +++ b/apps/meteor/ee/server/api/roles.ts @@ -1,13 +1,96 @@ import { Roles } from '@rocket.chat/models'; +import type { IRole } from '@rocket.chat/core-typings'; +import Ajv from 'ajv'; import { API } from '../../../app/api/server/api'; import { hasPermissionAsync } from '../../../app/authorization/server/functions/hasPermission'; import { settings } from '../../../app/settings/server/index'; import { isEnterprise } from '../../app/license/server'; -import { isRoleCreateProps, isRoleUpdateProps } from '../../definition/rest/v1/roles'; import { insertRoleAsync } from '../lib/roles/insertRole'; import { updateRole } from '../lib/roles/updateRole'; +const ajv = new Ajv({ + coerceTypes: true, +}); + +type RoleCreateProps = Pick & Partial>; + +const roleCreatePropsSchema = { + type: 'object', + properties: { + name: { + type: 'string', + }, + description: { + type: 'string', + nullable: true, + }, + scope: { + type: 'string', + enum: ['Users', 'Subscriptions'], + nullable: true, + }, + mandatory2fa: { + type: 'boolean', + nullable: true, + }, + }, + required: ['name'], + additionalProperties: false, +}; + +export const isRoleCreateProps = ajv.compile(roleCreatePropsSchema); + +type RoleUpdateProps = { + roleId: IRole['_id']; + name: IRole['name']; +} & Partial; + +const roleUpdatePropsSchema = { + type: 'object', + properties: { + roleId: { + type: 'string', + }, + name: { + type: 'string', + }, + description: { + type: 'string', + nullable: true, + }, + scope: { + type: 'string', + enum: ['Users', 'Subscriptions'], + nullable: true, + }, + mandatory2fa: { + type: 'boolean', + nullable: true, + }, + }, + required: ['roleId', 'name'], + additionalProperties: false, +}; + +export const isRoleUpdateProps = ajv.compile(roleUpdatePropsSchema); + +declare module '@rocket.chat/rest-typings' { + // eslint-disable-next-line @typescript-eslint/naming-convention + interface Endpoints { + '/v1/roles.create': { + POST: (params: RoleCreateProps) => { + role: IRole; + }; + }; + '/v1/roles.update': { + POST: (role: RoleUpdateProps) => { + role: IRole; + }; + }; + } +} + API.v1.addRoute( 'roles.create', { authRequired: true }, diff --git a/apps/meteor/ee/server/api/sessions.ts b/apps/meteor/ee/server/api/sessions.ts index 24ad74eca446..05106b79f037 100644 --- a/apps/meteor/ee/server/api/sessions.ts +++ b/apps/meteor/ee/server/api/sessions.ts @@ -1,19 +1,85 @@ import { Users, Sessions } from '@rocket.chat/models'; -import type { IUser } from '@rocket.chat/core-typings'; +import type { IUser, ISession, DeviceManagementSession, DeviceManagementPopulatedSession } from '@rocket.chat/core-typings'; import { escapeRegExp } from '@rocket.chat/string-helpers'; +import type { PaginatedResult, PaginatedRequest } from '@rocket.chat/rest-typings'; +import Ajv from 'ajv'; -import { isSessionsPaginateProps, isSessionsProps } from '../../definition/rest/v1/sessions'; import { API } from '../../../app/api/server/api'; import { hasLicense } from '../../app/license/server/license'; import { Notifications } from '../../../app/notifications/server'; import { getPaginationItems } from '../../../app/api/server/helpers/getPaginationItems'; +const ajv = new Ajv({ coerceTypes: true }); + +type SessionsProps = { + sessionId: string; +}; + +const isSessionsProps = ajv.compile({ + type: 'object', + properties: { + sessionId: { + type: 'string', + }, + }, + required: ['sessionId'], + additionalProperties: false, +}); + +type SessionsPaginateProps = PaginatedRequest<{ + filter?: string; +}>; + +const isSessionsPaginateProps = ajv.compile({ + type: 'object', + properties: { + offset: { + type: 'number', + }, + count: { + type: 'number', + }, + filter: { + type: 'string', + }, + sort: { + type: 'string', + }, + }, + required: [], + additionalProperties: false, +}); + const validateSortKeys = (sortKeys: string[]): boolean => { const validSortKeys = ['loginAt', 'device.name', 'device.os.name', 'device.os.version', '_user.name', '_user.username']; return sortKeys.every((s) => validSortKeys.includes(s)); }; +declare module '@rocket.chat/rest-typings' { + // eslint-disable-next-line @typescript-eslint/naming-convention + interface Endpoints { + '/v1/sessions/list': { + GET: (params: SessionsPaginateProps) => PaginatedResult<{ sessions: Array }>; + }; + '/v1/sessions/info': { + GET: (params: SessionsProps) => DeviceManagementSession; + }; + '/v1/sessions/logout.me': { + POST: (params: SessionsProps) => Pick; + }; + '/v1/sessions/list.all': { + GET: (params: SessionsPaginateProps) => PaginatedResult<{ sessions: Array }>; + }; + '/v1/sessions/info.admin': { + GET: (params: SessionsProps) => DeviceManagementPopulatedSession; + }; + '/v1/sessions/logout': { + POST: (params: SessionsProps) => Pick; + }; + } +} + API.v1.addRoute( 'sessions/list', { authRequired: true, validateParams: isSessionsPaginateProps }, From 8eaaafd0befeaeb146db39702727e70e5223d86a Mon Sep 17 00:00:00 2001 From: Douglas Fabris Date: Tue, 18 Jul 2023 13:55:44 -0300 Subject: [PATCH 018/342] chore: Deprecate `cloud:checkRegisterStatus` method (#29853) --- apps/meteor/app/cloud/server/methods.ts | 4 +++ .../actions/hooks/useAdministrationItems.tsx | 5 ---- apps/meteor/client/startup/startup.ts | 4 ++- .../views/admin/cloud/RegisterWorkspace.tsx | 25 ++++++++----------- 4 files changed, 18 insertions(+), 20 deletions(-) diff --git a/apps/meteor/app/cloud/server/methods.ts b/apps/meteor/app/cloud/server/methods.ts index 797ed964c171..e6a6402fe560 100644 --- a/apps/meteor/app/cloud/server/methods.ts +++ b/apps/meteor/app/cloud/server/methods.ts @@ -40,6 +40,10 @@ declare module '@rocket.chat/ui-contexts' { } Meteor.methods({ + /** + * @deprecated this method is deprecated and will be removed soon. + * Prefer using cloud.registrationStatus rest api. + */ async 'cloud:checkRegisterStatus'() { const uid = Meteor.userId(); diff --git a/apps/meteor/client/sidebar/header/actions/hooks/useAdministrationItems.tsx b/apps/meteor/client/sidebar/header/actions/hooks/useAdministrationItems.tsx index ea678413a16b..531545b20a43 100644 --- a/apps/meteor/client/sidebar/header/actions/hooks/useAdministrationItems.tsx +++ b/apps/meteor/client/sidebar/header/actions/hooks/useAdministrationItems.tsx @@ -65,11 +65,6 @@ export const useAdministrationItems = (): GenericMenuItemProps[] => { const isAdmin = useRole('admin'); const setModal = useSetModal(); - // TODO: DEPRECATE IT - // const checkCloudRegisterStatus = useMethod('cloud:checkRegisterStatus'); - // const result = useQuery(['admin/cloud/register-status'], async () => checkCloudRegisterStatus()); - // const { workspaceRegistered } = result.data || {}; - const { data: registrationStatusData } = useRegistrationStatus(); const workspaceRegistered = registrationStatusData?.registrationStatus?.workspaceRegistered ?? false; diff --git a/apps/meteor/client/startup/startup.ts b/apps/meteor/client/startup/startup.ts index 9295eacce245..440b55ce5e6d 100644 --- a/apps/meteor/client/startup/startup.ts +++ b/apps/meteor/client/startup/startup.ts @@ -71,7 +71,9 @@ Meteor.startup(() => { return; } - const { connectToCloud, workspaceRegistered } = await sdk.call('cloud:checkRegisterStatus'); + const { + registrationStatus: { connectToCloud, workspaceRegistered }, + } = await sdk.rest.get('/v1/cloud.registrationStatus'); c.stop(); if (connectToCloud === true && workspaceRegistered !== true) { diff --git a/apps/meteor/client/views/admin/cloud/RegisterWorkspace.tsx b/apps/meteor/client/views/admin/cloud/RegisterWorkspace.tsx index 9e4e479f16f9..694d437de8d3 100644 --- a/apps/meteor/client/views/admin/cloud/RegisterWorkspace.tsx +++ b/apps/meteor/client/views/admin/cloud/RegisterWorkspace.tsx @@ -1,10 +1,9 @@ import { Box, Tag } from '@rocket.chat/fuselage'; -import { useMutableCallback } from '@rocket.chat/fuselage-hooks'; -import { useSetModal, useMethod, useTranslation } from '@rocket.chat/ui-contexts'; -import { useQuery } from '@tanstack/react-query'; +import { useSetModal, useTranslation } from '@rocket.chat/ui-contexts'; import React from 'react'; import Page from '../../../components/Page'; +import { useRegistrationStatus } from '../../../hooks/useRegistrationStatus'; import ManualWorkspaceRegistrationModal from './ManualWorkspaceRegistrationModal'; import RegisterWorkspaceCards from './components/RegisterWorkspaceCards'; import RegisterWorkspaceMenu from './components/RegisterWorkspaceMenu'; @@ -15,30 +14,28 @@ const RegisterWorkspace = () => { const t = useTranslation(); const setModal = useSetModal(); - const checkCloudRegisterStatus = useMethod('cloud:checkRegisterStatus'); - const result = useQuery(['admin/cloud/register-status'], async () => checkCloudRegisterStatus()); - const reload = useMutableCallback(() => result.refetch()); + const { data: registrationStatusData, isLoading, isError, refetch } = useRegistrationStatus(); + const isWorkspaceRegistered = registrationStatusData?.registrationStatus?.workspaceRegistered ?? false; + const isConnectedToCloud = registrationStatusData?.registrationStatus?.connectToCloud ?? false; - if (result.isLoading || result.isError) { + if (isLoading || isError) { return null; } - const { connectToCloud: isConnectedToCloud, workspaceRegistered: isWorkspaceRegistered } = result.data; - const handleRegisterWorkspaceClick = (): void => { const handleModalClose = (): void => { setModal(null); - reload(); + refetch(); }; if (isWorkspaceRegistered) { - setModal(); - } else setModal(); + setModal(); + } else setModal(); }; const handleManualWorkspaceRegistrationButton = (): void => { const handleModalClose = (): void => { setModal(null); - reload(); + refetch(); }; setModal(); }; @@ -70,7 +67,7 @@ const RegisterWorkspace = () => { isWorkspaceRegistered={isWorkspaceRegistered} isConnectedToCloud={isConnectedToCloud} onClick={handleRegisterWorkspaceClick} - onStatusChange={reload} + onStatusChange={refetch} onClickOfflineRegistration={handleManualWorkspaceRegistrationButton} /> From b31a880af8b164913108654736b0ce45f7f2f613 Mon Sep 17 00:00:00 2001 From: Douglas Fabris Date: Tue, 18 Jul 2023 15:48:03 -0300 Subject: [PATCH 019/342] test: Cover `sortMenu` hook (#29858) --- .../hooks/useGroupingListItems.spec.tsx | 25 +++++++++++++++ .../actions/hooks/useSortModeItems.spec.tsx | 19 ++++++++++++ .../actions/hooks/useViewModeItems.spec.tsx | 31 +++++++++++++++++++ 3 files changed, 75 insertions(+) create mode 100644 apps/meteor/client/sidebar/header/actions/hooks/useGroupingListItems.spec.tsx create mode 100644 apps/meteor/client/sidebar/header/actions/hooks/useSortModeItems.spec.tsx create mode 100644 apps/meteor/client/sidebar/header/actions/hooks/useViewModeItems.spec.tsx diff --git a/apps/meteor/client/sidebar/header/actions/hooks/useGroupingListItems.spec.tsx b/apps/meteor/client/sidebar/header/actions/hooks/useGroupingListItems.spec.tsx new file mode 100644 index 000000000000..b5779d825202 --- /dev/null +++ b/apps/meteor/client/sidebar/header/actions/hooks/useGroupingListItems.spec.tsx @@ -0,0 +1,25 @@ +import { renderHook } from '@testing-library/react-hooks'; + +import { useGroupingListItems } from './useGroupingListItems'; + +it('should render groupingList items', async () => { + const { result } = renderHook(() => useGroupingListItems()); + + expect(result.current[0]).toEqual( + expect.objectContaining({ + id: 'unread', + }), + ); + + expect(result.current[1]).toEqual( + expect.objectContaining({ + id: 'favorites', + }), + ); + + expect(result.current[2]).toEqual( + expect.objectContaining({ + id: 'types', + }), + ); +}); diff --git a/apps/meteor/client/sidebar/header/actions/hooks/useSortModeItems.spec.tsx b/apps/meteor/client/sidebar/header/actions/hooks/useSortModeItems.spec.tsx new file mode 100644 index 000000000000..143d228fe7ca --- /dev/null +++ b/apps/meteor/client/sidebar/header/actions/hooks/useSortModeItems.spec.tsx @@ -0,0 +1,19 @@ +import { renderHook } from '@testing-library/react-hooks'; + +import { useSortModeItems } from './useSortModeItems'; + +it('should render sortMode items', async () => { + const { result } = renderHook(() => useSortModeItems()); + + expect(result.current[0]).toEqual( + expect.objectContaining({ + id: 'activity', + }), + ); + + expect(result.current[1]).toEqual( + expect.objectContaining({ + id: 'name', + }), + ); +}); diff --git a/apps/meteor/client/sidebar/header/actions/hooks/useViewModeItems.spec.tsx b/apps/meteor/client/sidebar/header/actions/hooks/useViewModeItems.spec.tsx new file mode 100644 index 000000000000..6c6dd7532e7e --- /dev/null +++ b/apps/meteor/client/sidebar/header/actions/hooks/useViewModeItems.spec.tsx @@ -0,0 +1,31 @@ +import { renderHook } from '@testing-library/react-hooks'; + +import { useViewModeItems } from './useViewModeItems'; + +it('should render viewMode items', async () => { + const { result } = renderHook(() => useViewModeItems()); + + expect(result.current[0]).toEqual( + expect.objectContaining({ + id: 'extended', + }), + ); + + expect(result.current[1]).toEqual( + expect.objectContaining({ + id: 'medium', + }), + ); + + expect(result.current[2]).toEqual( + expect.objectContaining({ + id: 'condensed', + }), + ); + + expect(result.current[3]).toEqual( + expect.objectContaining({ + id: 'avatars', + }), + ); +}); From 29b78ee655c4cb86bc9a295aca1bb0f957c78bba Mon Sep 17 00:00:00 2001 From: Guilherme Gazzo Date: Tue, 18 Jul 2023 16:09:57 -0300 Subject: [PATCH 020/342] ci: Report Jest coverage (#29848) --- .github/workflows/ci-test-unit.yml | 5 + apps/meteor/.mocharc.client.js | 6 +- .../useToggleReactionMutation.spec.tsx | 106 ++++++++++++++++++ apps/meteor/jest.config.ts | 2 + codecov.yml | 10 ++ ee/packages/ddp-client/jest.config.ts | 1 + turbo.json | 2 +- 7 files changed, 130 insertions(+), 2 deletions(-) create mode 100644 apps/meteor/client/components/message/content/reactions/useToggleReactionMutation.spec.tsx diff --git a/.github/workflows/ci-test-unit.yml b/.github/workflows/ci-test-unit.yml index f294ac2dda7b..03c6bc2352ab 100644 --- a/.github/workflows/ci-test-unit.yml +++ b/.github/workflows/ci-test-unit.yml @@ -31,3 +31,8 @@ jobs: - name: Unit Test run: yarn testunit + + - uses: codecov/codecov-action@v3 + with: + flags: unit + verbose: true diff --git a/apps/meteor/.mocharc.client.js b/apps/meteor/.mocharc.client.js index 6e522ee67363..029564d7729a 100644 --- a/apps/meteor/.mocharc.client.js +++ b/apps/meteor/.mocharc.client.js @@ -38,5 +38,9 @@ module.exports = { 'tests/unit/lib/**/*.tests.ts', 'tests/unit/client/**/*.test.ts', ], - exclude: ['client/hooks/*.spec.{ts,tsx}', 'client/sidebar/header/actions/hooks/*.spec.{ts,tsx}'], + exclude: [ + 'client/hooks/*.spec.{ts,tsx}', + 'client/sidebar/header/actions/hooks/*.spec.{ts,tsx}', + 'client/components/message/content/reactions/**.spec.{ts,tsx}', + ], }; diff --git a/apps/meteor/client/components/message/content/reactions/useToggleReactionMutation.spec.tsx b/apps/meteor/client/components/message/content/reactions/useToggleReactionMutation.spec.tsx new file mode 100644 index 000000000000..1ef4d84ddc91 --- /dev/null +++ b/apps/meteor/client/components/message/content/reactions/useToggleReactionMutation.spec.tsx @@ -0,0 +1,106 @@ +import { MockedAuthorizationContext } from '@rocket.chat/mock-providers/src/MockedAuthorizationContext'; +import { MockedServerContext } from '@rocket.chat/mock-providers/src/MockedServerContext'; +import { MockedSettingsContext } from '@rocket.chat/mock-providers/src/MockedSettingsContext'; +import { MockedUserContext } from '@rocket.chat/mock-providers/src/MockedUserContext'; +import { QueryClientProvider, QueryClient } from '@tanstack/react-query'; +import { renderHook, act } from '@testing-library/react-hooks'; +import React from 'react'; + +import { useToggleReactionMutation } from './useToggleReactionMutation'; + +const queryClient = new QueryClient({ + defaultOptions: { + queries: { + // ✅ turns retries off + retry: false, + }, + }, + logger: { + log: (..._) => { + // Log debugging information + }, + warn: (..._) => { + // Log warning + }, + error: (..._) => { + // Log error + }, + }, +}); + +beforeEach(() => { + queryClient.clear(); +}); + +it('should be call rest `POST /v1/chat.react` method', async () => { + const fn = jest.fn(); + const { result, waitFor } = renderHook(() => useToggleReactionMutation(), { + wrapper: ({ children }) => ( + + { + if (method === 'POST' && pathPattern === '/v1/chat.react') { + fn(params); + return { success: true } as any; + } + + throw new Error(`Endpoint not mocked: ${method} ${pathPattern}`); + }} + > + + + {children} + + + + + ), + }); + + act(async () => { + await result.current.mutateAsync({ mid: 'MID', reaction: 'smile' }); + }); + + await waitFor(() => result.current.isLoading === false); + + expect(fn).toHaveBeenCalledWith({ + messageId: 'MID', + reaction: 'smile', + }); +}); + +it('should not work for non-logged in users', async () => { + const fn = jest.fn(); + const { result, waitForValueToChange } = renderHook(() => useToggleReactionMutation(), { + wrapper: ({ children }) => ( + + { + if (method === 'POST' && pathPattern === '/v1/chat.react') { + fn(params); + return { success: true } as any; + } + + throw new Error(`Endpoint not mocked: ${method} ${pathPattern}`); + }} + > + + {children} + + + + ), + }); + + act(() => { + return result.current.mutate({ mid: 'MID', reaction: 'smile' }); + }); + + await waitForValueToChange(() => result.current.status); + + expect(fn).not.toHaveBeenCalled(); + + expect(result.current.status).toBe('error'); + + expect(result.current.error).toEqual(new Error('Not logged in')); +}); diff --git a/apps/meteor/jest.config.ts b/apps/meteor/jest.config.ts index bcd9f8f5432a..9232c2065dad 100644 --- a/apps/meteor/jest.config.ts +++ b/apps/meteor/jest.config.ts @@ -6,6 +6,7 @@ export default { testMatch: [ '/client/hooks/**.spec.[jt]s?(x)', '/client/components/**.spec.[jt]s?(x)', + 'client/components/message/content/reactions/**.spec.[jt]s?(x)', '/client/sidebar/header/actions/hooks/**/**.spec.[jt]s?(x)', ], transform: { @@ -15,4 +16,5 @@ export default { '\\.css$': 'identity-obj-proxy', '^react($|/.+)': '/node_modules/react$1', }, + collectCoverage: true, }; diff --git a/codecov.yml b/codecov.yml index d9198cbf2443..0a3501678c1e 100644 --- a/codecov.yml +++ b/codecov.yml @@ -5,5 +5,15 @@ coverage: default: target: auto threshold: 1% + client: + target: auto + flags: + - client +flags: + client: + paths: + - apps/meteor/client + carryforward: true + comment: layout: 'reach, diff, flags' diff --git a/ee/packages/ddp-client/jest.config.ts b/ee/packages/ddp-client/jest.config.ts index 0739147f43fe..eb3f38119a73 100644 --- a/ee/packages/ddp-client/jest.config.ts +++ b/ee/packages/ddp-client/jest.config.ts @@ -10,4 +10,5 @@ export default { moduleNameMapper: { '\\.css$': 'identity-obj-proxy', }, + collectCoverage: true, }; diff --git a/turbo.json b/turbo.json index e617f46d0bea..061b9fe02582 100644 --- a/turbo.json +++ b/turbo.json @@ -11,7 +11,7 @@ }, "testunit": { "dependsOn": ["build"], - "outputs": [] + "outputs": ["coverage/**"] }, "lint": { "dependsOn": ["build"], From dd2b98ee1c2ac70f825c41357f04d74a9bd697ac Mon Sep 17 00:00:00 2001 From: "lingohub[bot]" <69908207+lingohub[bot]@users.noreply.github.com> Date: Tue, 18 Jul 2023 21:17:16 +0000 Subject: [PATCH 021/342] =?UTF-8?q?i18n:=20Language=20update=20from=20Ling?= =?UTF-8?q?oHub=20=F0=9F=A4=96=20on=202023-07-13Z=20(#29817)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Co-authored-by: Douglas Fabris <27704687+dougfabris@users.noreply.github.com> --- apps/meteor/packages/rocketchat-i18n/i18n/af.i18n.json | 1 + apps/meteor/packages/rocketchat-i18n/i18n/ar.i18n.json | 1 + apps/meteor/packages/rocketchat-i18n/i18n/az.i18n.json | 1 + apps/meteor/packages/rocketchat-i18n/i18n/be-BY.i18n.json | 1 + apps/meteor/packages/rocketchat-i18n/i18n/bg.i18n.json | 1 + apps/meteor/packages/rocketchat-i18n/i18n/bs.i18n.json | 1 + apps/meteor/packages/rocketchat-i18n/i18n/ca.i18n.json | 1 + apps/meteor/packages/rocketchat-i18n/i18n/cs.i18n.json | 1 + apps/meteor/packages/rocketchat-i18n/i18n/cy.i18n.json | 1 + apps/meteor/packages/rocketchat-i18n/i18n/da.i18n.json | 1 + apps/meteor/packages/rocketchat-i18n/i18n/de-AT.i18n.json | 1 + apps/meteor/packages/rocketchat-i18n/i18n/de.i18n.json | 1 + apps/meteor/packages/rocketchat-i18n/i18n/el.i18n.json | 1 + apps/meteor/packages/rocketchat-i18n/i18n/en.i18n.json | 3 +-- apps/meteor/packages/rocketchat-i18n/i18n/eo.i18n.json | 1 + apps/meteor/packages/rocketchat-i18n/i18n/es.i18n.json | 1 + apps/meteor/packages/rocketchat-i18n/i18n/eu.i18n.json | 1 + apps/meteor/packages/rocketchat-i18n/i18n/fa.i18n.json | 1 + apps/meteor/packages/rocketchat-i18n/i18n/fi.i18n.json | 1 + apps/meteor/packages/rocketchat-i18n/i18n/fr.i18n.json | 1 + apps/meteor/packages/rocketchat-i18n/i18n/gl.i18n.json | 1 + apps/meteor/packages/rocketchat-i18n/i18n/he.i18n.json | 1 + apps/meteor/packages/rocketchat-i18n/i18n/hi-IN.i18n.json | 1 + apps/meteor/packages/rocketchat-i18n/i18n/hr.i18n.json | 1 + apps/meteor/packages/rocketchat-i18n/i18n/hu.i18n.json | 1 + apps/meteor/packages/rocketchat-i18n/i18n/id.i18n.json | 1 + apps/meteor/packages/rocketchat-i18n/i18n/it.i18n.json | 1 + apps/meteor/packages/rocketchat-i18n/i18n/ja.i18n.json | 1 + apps/meteor/packages/rocketchat-i18n/i18n/ka-GE.i18n.json | 1 + apps/meteor/packages/rocketchat-i18n/i18n/km.i18n.json | 1 + apps/meteor/packages/rocketchat-i18n/i18n/ko.i18n.json | 1 + apps/meteor/packages/rocketchat-i18n/i18n/ku.i18n.json | 1 + apps/meteor/packages/rocketchat-i18n/i18n/lo.i18n.json | 1 + apps/meteor/packages/rocketchat-i18n/i18n/lt.i18n.json | 1 + apps/meteor/packages/rocketchat-i18n/i18n/lv.i18n.json | 1 + apps/meteor/packages/rocketchat-i18n/i18n/mn.i18n.json | 1 + apps/meteor/packages/rocketchat-i18n/i18n/ms-MY.i18n.json | 1 + apps/meteor/packages/rocketchat-i18n/i18n/nl.i18n.json | 1 + apps/meteor/packages/rocketchat-i18n/i18n/no.i18n.json | 1 + apps/meteor/packages/rocketchat-i18n/i18n/pl.i18n.json | 1 + apps/meteor/packages/rocketchat-i18n/i18n/pt-BR.i18n.json | 1 + apps/meteor/packages/rocketchat-i18n/i18n/pt.i18n.json | 1 + apps/meteor/packages/rocketchat-i18n/i18n/ro.i18n.json | 1 + apps/meteor/packages/rocketchat-i18n/i18n/ru.i18n.json | 1 + apps/meteor/packages/rocketchat-i18n/i18n/sk-SK.i18n.json | 1 + apps/meteor/packages/rocketchat-i18n/i18n/sl-SI.i18n.json | 1 + apps/meteor/packages/rocketchat-i18n/i18n/sq.i18n.json | 1 + apps/meteor/packages/rocketchat-i18n/i18n/sr.i18n.json | 1 + apps/meteor/packages/rocketchat-i18n/i18n/sv.i18n.json | 1 + apps/meteor/packages/rocketchat-i18n/i18n/ta-IN.i18n.json | 1 + apps/meteor/packages/rocketchat-i18n/i18n/th-TH.i18n.json | 1 + apps/meteor/packages/rocketchat-i18n/i18n/tr.i18n.json | 1 + apps/meteor/packages/rocketchat-i18n/i18n/ug.i18n.json | 1 + apps/meteor/packages/rocketchat-i18n/i18n/uk.i18n.json | 1 + apps/meteor/packages/rocketchat-i18n/i18n/vi-VN.i18n.json | 1 + apps/meteor/packages/rocketchat-i18n/i18n/zh-HK.i18n.json | 1 + apps/meteor/packages/rocketchat-i18n/i18n/zh-TW.i18n.json | 1 + apps/meteor/packages/rocketchat-i18n/i18n/zh.i18n.json | 1 + 58 files changed, 58 insertions(+), 2 deletions(-) diff --git a/apps/meteor/packages/rocketchat-i18n/i18n/af.i18n.json b/apps/meteor/packages/rocketchat-i18n/i18n/af.i18n.json index cc915ea21186..858c2f4cf898 100644 --- a/apps/meteor/packages/rocketchat-i18n/i18n/af.i18n.json +++ b/apps/meteor/packages/rocketchat-i18n/i18n/af.i18n.json @@ -1877,6 +1877,7 @@ "OTR_is_only_available_when_both_users_are_online": "OTR is slegs beskikbaar wanneer beide gebruikers aanlyn is", "Outgoing_WebHook": "Uitgaande WebHook", "Outgoing_WebHook_Description": "Kry data uit Rocket.Chat in real-time.", + "Outlook_Calendar_Enabled": "enabled", "Override_URL_to_which_files_are_uploaded_This_url_also_used_for_downloads_unless_a_CDN_is_given": "Hersien URL aan watter lêers opgelaai word. Hierdie url word ook vir aflaai gebruik as 'n CDN nie gegee word nie", "Page_title": "Bladsy titel", "Page_URL": "Bladsy-URL", diff --git a/apps/meteor/packages/rocketchat-i18n/i18n/ar.i18n.json b/apps/meteor/packages/rocketchat-i18n/i18n/ar.i18n.json index f2691bb674b5..110c20b10792 100644 --- a/apps/meteor/packages/rocketchat-i18n/i18n/ar.i18n.json +++ b/apps/meteor/packages/rocketchat-i18n/i18n/ar.i18n.json @@ -3287,6 +3287,7 @@ "Outgoing": "صادر", "Outgoing_WebHook": "خطاف الويب الصادر", "Outgoing_WebHook_Description": "الحصول على البيانات من Rocket.Chat في الوقت الفعلي.", + "Outlook_Calendar_Enabled": "تم التمكين", "Output_format": "تنسيق الإخراج", "Override_URL_to_which_files_are_uploaded_This_url_also_used_for_downloads_unless_a_CDN_is_given": "تجاوز عنوان URL الذي يتم تحميل الملفات إليه. يستخدم عنوان URL هذا أيضًا للتنزيلات ما لم يتم إعطاء CDN.", "Page_title": "عنوان الصفحة", diff --git a/apps/meteor/packages/rocketchat-i18n/i18n/az.i18n.json b/apps/meteor/packages/rocketchat-i18n/i18n/az.i18n.json index a44c25a1b95b..32f4af142ab4 100644 --- a/apps/meteor/packages/rocketchat-i18n/i18n/az.i18n.json +++ b/apps/meteor/packages/rocketchat-i18n/i18n/az.i18n.json @@ -1877,6 +1877,7 @@ "OTR_is_only_available_when_both_users_are_online": "OTR yalnız hər iki istifadəçi online olduğunda istifadə edilə bilər", "Outgoing_WebHook": "Giden WebHook", "Outgoing_WebHook_Description": "Real-time Rocket.Chat-dən məlumat əldə edin.", + "Outlook_Calendar_Enabled": "Etkin", "Override_URL_to_which_files_are_uploaded_This_url_also_used_for_downloads_unless_a_CDN_is_given": "Dosyaların yükləndiyi URL'yi silin. Bu url də bir CDN verilmədiyi təqdirdə yükləmələr üçün də istifadə olunur", "Page_title": "Səhifə başlığı", "Page_URL": "Səhifə URL", diff --git a/apps/meteor/packages/rocketchat-i18n/i18n/be-BY.i18n.json b/apps/meteor/packages/rocketchat-i18n/i18n/be-BY.i18n.json index e448574f6b48..06c1ca5ce34d 100644 --- a/apps/meteor/packages/rocketchat-i18n/i18n/be-BY.i18n.json +++ b/apps/meteor/packages/rocketchat-i18n/i18n/be-BY.i18n.json @@ -1893,6 +1893,7 @@ "OTR_is_only_available_when_both_users_are_online": "OTR даступны толькі тады, калі абодва карыстальніка знаходзяцца на сайце", "Outgoing_WebHook": "выходны WebHook", "Outgoing_WebHook_Description": "Атрымаць дадзеныя з Rocket.Chat ў рэжыме рэальнага часу.", + "Outlook_Calendar_Enabled": "Уключана", "Override_URL_to_which_files_are_uploaded_This_url_also_used_for_downloads_unless_a_CDN_is_given": "Override URL, да якога файлы будуць загружаныя. Гэтага URL выкарыстоўваецца таксама для загрузкі, калі толькі ў CDN даецца", "Page_title": "тытульны ліст", "Page_URL": "URL старонкі", diff --git a/apps/meteor/packages/rocketchat-i18n/i18n/bg.i18n.json b/apps/meteor/packages/rocketchat-i18n/i18n/bg.i18n.json index 35a907b728d3..7a50677816c4 100644 --- a/apps/meteor/packages/rocketchat-i18n/i18n/bg.i18n.json +++ b/apps/meteor/packages/rocketchat-i18n/i18n/bg.i18n.json @@ -1874,6 +1874,7 @@ "OTR_is_only_available_when_both_users_are_online": "OTR е налице само когато и двамата потребители са онлайн", "Outgoing_WebHook": "Изходяща WebHook", "Outgoing_WebHook_Description": "Извличайте данни от Rocket.Chat в реално време.", + "Outlook_Calendar_Enabled": "Позволено", "Override_URL_to_which_files_are_uploaded_This_url_also_used_for_downloads_unless_a_CDN_is_given": "Замяна на URL адреса, към който се качват файловете. Този URL адрес също се използва за изтегляне, освен ако не е даден CDN", "Page_title": "Заглавие на страница", "Page_URL": "URL адрес на страницата", diff --git a/apps/meteor/packages/rocketchat-i18n/i18n/bs.i18n.json b/apps/meteor/packages/rocketchat-i18n/i18n/bs.i18n.json index 4776295db449..46d8f5f93d21 100644 --- a/apps/meteor/packages/rocketchat-i18n/i18n/bs.i18n.json +++ b/apps/meteor/packages/rocketchat-i18n/i18n/bs.i18n.json @@ -1870,6 +1870,7 @@ "OTR_is_only_available_when_both_users_are_online": "SP je dostupan samo ako su oba korisnika online", "Outgoing_WebHook": "Odlazni WebHook", "Outgoing_WebHook_Description": "Dobijte podatke iz Rocket.Chat u stvarnom vremenu.", + "Outlook_Calendar_Enabled": "Omogućeno", "Override_URL_to_which_files_are_uploaded_This_url_also_used_for_downloads_unless_a_CDN_is_given": "URL mjesta na koji su postavljene datoteke. Ovaj url se također koristi za preuzimanje, osim ako je zadan CDN", "Page_title": "Naslov stranice", "Page_URL": "URL stranice", diff --git a/apps/meteor/packages/rocketchat-i18n/i18n/ca.i18n.json b/apps/meteor/packages/rocketchat-i18n/i18n/ca.i18n.json index ddb2d9d3e259..b501f88a5000 100644 --- a/apps/meteor/packages/rocketchat-i18n/i18n/ca.i18n.json +++ b/apps/meteor/packages/rocketchat-i18n/i18n/ca.i18n.json @@ -3248,6 +3248,7 @@ "Out_of_seats": "Sense llocs", "Outgoing_WebHook": "WebHook sortint", "Outgoing_WebHook_Description": "Extreu dades de Rocket.Chat en temps real.", + "Outlook_Calendar_Enabled": "Activat", "Output_format": "Format de sortida", "Override_URL_to_which_files_are_uploaded_This_url_also_used_for_downloads_unless_a_CDN_is_given": "Reemplaça la URL a la qual es carreguen els fitxers. Aquest URL també es fa servir per a baixades a no ser que es proporcioni un CDN", "Page_title": "Titol de la pàgina", diff --git a/apps/meteor/packages/rocketchat-i18n/i18n/cs.i18n.json b/apps/meteor/packages/rocketchat-i18n/i18n/cs.i18n.json index 4d5b1232d9d2..435f09f3d185 100644 --- a/apps/meteor/packages/rocketchat-i18n/i18n/cs.i18n.json +++ b/apps/meteor/packages/rocketchat-i18n/i18n/cs.i18n.json @@ -2723,6 +2723,7 @@ "OTR_is_only_available_when_both_users_are_online": "OTR je k dispozici pouze pokud jsou oba uživatelé online", "Outgoing_WebHook": "Odchozí Webhook", "Outgoing_WebHook_Description": "Získávejte data z Rocket.Chat v reálném čase.", + "Outlook_Calendar_Enabled": "Povoleno", "Output_format": "Výstupní formát", "Override_URL_to_which_files_are_uploaded_This_url_also_used_for_downloads_unless_a_CDN_is_given": "Přepsat URL, na které jsou nahrané soubory. Tato adresa se také používá pro stahování pokud není nastavena CDN", "Page_title": "Titulku stránky", diff --git a/apps/meteor/packages/rocketchat-i18n/i18n/cy.i18n.json b/apps/meteor/packages/rocketchat-i18n/i18n/cy.i18n.json index 0c3bb7349d5d..eae2511cf07a 100644 --- a/apps/meteor/packages/rocketchat-i18n/i18n/cy.i18n.json +++ b/apps/meteor/packages/rocketchat-i18n/i18n/cy.i18n.json @@ -1872,6 +1872,7 @@ "OTR_is_only_available_when_both_users_are_online": "Dim ond pan fydd y ddau ddefnyddiwr ar-lein mae OTR ar gael", "Outgoing_WebHook": "WebHook Allanol", "Outgoing_WebHook_Description": "Cael data allan o Rocket.Chat mewn amser real.", + "Outlook_Calendar_Enabled": "Wedi'i alluogi", "Override_URL_to_which_files_are_uploaded_This_url_also_used_for_downloads_unless_a_CDN_is_given": "Anwybyddu'r URL y mae ffeiliau wedi'u llwytho i fyny. Defnyddiwyd yr url hwn hefyd i'w lawrlwytho oni bai bod CDN yn cael ei roi", "Page_title": "Teitl y dudalen", "Page_URL": "Tudalen URL", diff --git a/apps/meteor/packages/rocketchat-i18n/i18n/da.i18n.json b/apps/meteor/packages/rocketchat-i18n/i18n/da.i18n.json index e45395e5ab8c..065fd1a6527d 100644 --- a/apps/meteor/packages/rocketchat-i18n/i18n/da.i18n.json +++ b/apps/meteor/packages/rocketchat-i18n/i18n/da.i18n.json @@ -2736,6 +2736,7 @@ "OTR_is_only_available_when_both_users_are_online": "OTR er kun tilgængelig, når begge brugere er online", "Outgoing_WebHook": "Udgående WebHook", "Outgoing_WebHook_Description": "Få data ud af Rocket.Chat i realtid.", + "Outlook_Calendar_Enabled": "Aktiveret", "Output_format": "Outputformat", "Override_URL_to_which_files_are_uploaded_This_url_also_used_for_downloads_unless_a_CDN_is_given": "Overstyr URL til hvilke filer der uploades. Denne url bruges også til download, medmindre en CDN er givet", "Page_title": "Sidetitel", diff --git a/apps/meteor/packages/rocketchat-i18n/i18n/de-AT.i18n.json b/apps/meteor/packages/rocketchat-i18n/i18n/de-AT.i18n.json index 465367b67ae8..fa2d8612b1f4 100644 --- a/apps/meteor/packages/rocketchat-i18n/i18n/de-AT.i18n.json +++ b/apps/meteor/packages/rocketchat-i18n/i18n/de-AT.i18n.json @@ -1880,6 +1880,7 @@ "OTR_is_only_available_when_both_users_are_online": "OTR ist nur möglich, wenn beide Benutzer online sind.", "Outgoing_WebHook": "Ausgehender WebHook", "Outgoing_WebHook_Description": "Holen Sie Daten in Echtzeit aus Rocket.Chat.", + "Outlook_Calendar_Enabled": "aktiviert", "Override_URL_to_which_files_are_uploaded_This_url_also_used_for_downloads_unless_a_CDN_is_given": "URL, wo die Dateien hochgeladen werden. Die URL wird auch für Downloads verwendet, wenn keine CDN angegeben wird.", "Page_title": "Seitentitel", "Page_URL": "Seiten-URL", diff --git a/apps/meteor/packages/rocketchat-i18n/i18n/de.i18n.json b/apps/meteor/packages/rocketchat-i18n/i18n/de.i18n.json index 9645db821afc..65c79f66f5af 100644 --- a/apps/meteor/packages/rocketchat-i18n/i18n/de.i18n.json +++ b/apps/meteor/packages/rocketchat-i18n/i18n/de.i18n.json @@ -3700,6 +3700,7 @@ "Outgoing": "Ausgehend", "Outgoing_WebHook": "Ausgehender Webhook", "Outgoing_WebHook_Description": "Daten aus Rocket.Chat heraus versenden.", + "Outlook_Calendar_Enabled": "aktiviert", "Output_format": "Ausgabeformat", "Override_URL_to_which_files_are_uploaded_This_url_also_used_for_downloads_unless_a_CDN_is_given": "URL, unter der die Dateien hochgeladen werden. Die URL wird auch für Downloads verwendet, wenn kein CDN angegeben wird", "Owner": "Eigentümer", diff --git a/apps/meteor/packages/rocketchat-i18n/i18n/el.i18n.json b/apps/meteor/packages/rocketchat-i18n/i18n/el.i18n.json index 74c40707b40d..f81634b9ce3f 100644 --- a/apps/meteor/packages/rocketchat-i18n/i18n/el.i18n.json +++ b/apps/meteor/packages/rocketchat-i18n/i18n/el.i18n.json @@ -1885,6 +1885,7 @@ "OTR_is_only_available_when_both_users_are_online": "OTR είναι διαθέσιμη μόνο όταν και οι δύο χρήστες είναι online", "Outgoing_WebHook": "Έξοδος WebHook", "Outgoing_WebHook_Description": "Αποκτήστε δεδομένα από το Rocket.Chat σε πραγματικό χρόνο.", + "Outlook_Calendar_Enabled": "Ενεργοποιήθηκε", "Override_URL_to_which_files_are_uploaded_This_url_also_used_for_downloads_unless_a_CDN_is_given": "Παράκαμψη URL στο οποίο φορτώνονται τα αρχεία. Αυτή η διεύθυνση URL που χρησιμοποιείται επίσης για λήψεις εκτός εάν ένα CDN δίνεται", "Page_title": "Τίτλος σελίδας", "Page_URL": "Διεύθυνση Ιστοσελίδας", diff --git a/apps/meteor/packages/rocketchat-i18n/i18n/en.i18n.json b/apps/meteor/packages/rocketchat-i18n/i18n/en.i18n.json index 154bf6af2d6e..0140332a73b5 100644 --- a/apps/meteor/packages/rocketchat-i18n/i18n/en.i18n.json +++ b/apps/meteor/packages/rocketchat-i18n/i18n/en.i18n.json @@ -3849,7 +3849,6 @@ "Outgoing_WebHook_Description": "Get data out of Rocket.Chat in real-time.", "Outlook_authentication": "Outlook authentication", "Outlook_authentication_disabled": "Outlook authentication disabled", - "Outlook_authentication_description": "Disable this to clear any outlook credentials stored in this machine.", "Outlook_calendar": "Outlook calendar", "Outlook_calendar_event": "Outlook calendar event", @@ -5951,4 +5950,4 @@ "Uninstall_grandfathered_app": "Uninstall {{appName}}?", "App_will_lose_grandfathered_status": "**This {{context}} app will lose its grandfathered status.** \n \nWorkspaces on Community Edition can have up to {{limit}} {{context}} apps enabled. Grandfathered apps count towards the limit but the limit is not applied to them.", "Theme_Appearence": "Theme Appearence" -} +} \ No newline at end of file diff --git a/apps/meteor/packages/rocketchat-i18n/i18n/eo.i18n.json b/apps/meteor/packages/rocketchat-i18n/i18n/eo.i18n.json index 8006cd075d7a..e649f7f35387 100644 --- a/apps/meteor/packages/rocketchat-i18n/i18n/eo.i18n.json +++ b/apps/meteor/packages/rocketchat-i18n/i18n/eo.i18n.json @@ -1877,6 +1877,7 @@ "OTR_is_only_available_when_both_users_are_online": "OTR estas nur havebla kiam ambaŭ uzantoj estas interrete", "Outgoing_WebHook": "Eliranta WebHook", "Outgoing_WebHook_Description": "Akiri datumojn el Rocket.Chat en reala tempo.", + "Outlook_Calendar_Enabled": "Enabled", "Override_URL_to_which_files_are_uploaded_This_url_also_used_for_downloads_unless_a_CDN_is_given": "Anstataŭigi URL al kiu dosieroj estas alŝutitaj. Ĉi tiu url ankaŭ uzis por malŝarĝoj krom se ĝi ricevas CDN", "Page_title": "Paĝo-titolo", "Page_URL": "Paĝo URL", diff --git a/apps/meteor/packages/rocketchat-i18n/i18n/es.i18n.json b/apps/meteor/packages/rocketchat-i18n/i18n/es.i18n.json index 7e2219b43fb2..739768a79124 100644 --- a/apps/meteor/packages/rocketchat-i18n/i18n/es.i18n.json +++ b/apps/meteor/packages/rocketchat-i18n/i18n/es.i18n.json @@ -3262,6 +3262,7 @@ "Outgoing": "Saliente", "Outgoing_WebHook": "Webhook saliente", "Outgoing_WebHook_Description": "Obtener datos de Rocket.Chat en tiempo real.", + "Outlook_Calendar_Enabled": "Habilitado", "Output_format": "Formato de salida", "Override_URL_to_which_files_are_uploaded_This_url_also_used_for_downloads_unless_a_CDN_is_given": "Reemplazar la URL a la que se suben los archivos. Esta URL también se usa para descargas a menos que se proporcione una CDN", "Page_title": "Título de página", diff --git a/apps/meteor/packages/rocketchat-i18n/i18n/eu.i18n.json b/apps/meteor/packages/rocketchat-i18n/i18n/eu.i18n.json index 8b1e7125e15f..7585a29458f2 100644 --- a/apps/meteor/packages/rocketchat-i18n/i18n/eu.i18n.json +++ b/apps/meteor/packages/rocketchat-i18n/i18n/eu.i18n.json @@ -96,6 +96,7 @@ "No_channels_yet": "Oraindik ez zara inongo kanalen partaide", "No_discussions_yet": "Ez dago eztabaidarik", "Options": "Aukerak", + "Outlook_Calendar_Enabled": "Gaituta", "Please_fill_name_and_email": "Sartu izena eta posta elektronikoa mesedez", "Private_Groups": "Talde Pribatuak", "Private_Groups_list": "Talde Pribatuen Zerrenda", diff --git a/apps/meteor/packages/rocketchat-i18n/i18n/fa.i18n.json b/apps/meteor/packages/rocketchat-i18n/i18n/fa.i18n.json index c23985c99cbc..a66a48d1b61b 100644 --- a/apps/meteor/packages/rocketchat-i18n/i18n/fa.i18n.json +++ b/apps/meteor/packages/rocketchat-i18n/i18n/fa.i18n.json @@ -2181,6 +2181,7 @@ "OTR_is_only_available_when_both_users_are_online": "تنها زمانی در دسترس است که دو طرف آنلاین باشند.", "Outgoing_WebHook": "خروجی WebHook", "Outgoing_WebHook_Description": "دریافت اطلاعات از Rocket.Chat در زمان واقعی.", + "Outlook_Calendar_Enabled": "فعال", "Override_URL_to_which_files_are_uploaded_This_url_also_used_for_downloads_unless_a_CDN_is_given": "URL نادیده گرفتن که فایل های آپلود شده است. این URL نیز برای دریافت مگر اینکه یک CDN استفاده شده است", "Page_title": "عنوان صفحه", "Page_URL": "آدرس صفحه", diff --git a/apps/meteor/packages/rocketchat-i18n/i18n/fi.i18n.json b/apps/meteor/packages/rocketchat-i18n/i18n/fi.i18n.json index f3deb4a3063e..087fa6a0b27a 100644 --- a/apps/meteor/packages/rocketchat-i18n/i18n/fi.i18n.json +++ b/apps/meteor/packages/rocketchat-i18n/i18n/fi.i18n.json @@ -3754,6 +3754,7 @@ "Outgoing": "Lähtevät", "Outgoing_WebHook": "Lähtevä WebHook", "Outgoing_WebHook_Description": "Hanki tiedot chatsovelluksesta reaaliaikaisesti.", + "Outlook_Calendar_Enabled": "Käytössä", "Output_format": "Tulostusmuoto", "Override_URL_to_which_files_are_uploaded_This_url_also_used_for_downloads_unless_a_CDN_is_given": "Ohitus-URL, johon tiedostot ladataan. Tätä URL-osoitetta käytetään myös vastakkaisen suuntaiseen lataamiseen, ellei CDN:ää ole annettu", "Owner": "Omistaja", diff --git a/apps/meteor/packages/rocketchat-i18n/i18n/fr.i18n.json b/apps/meteor/packages/rocketchat-i18n/i18n/fr.i18n.json index 5d956f706e85..776c3cfd7ae6 100644 --- a/apps/meteor/packages/rocketchat-i18n/i18n/fr.i18n.json +++ b/apps/meteor/packages/rocketchat-i18n/i18n/fr.i18n.json @@ -3280,6 +3280,7 @@ "Outgoing": "Sortant", "Outgoing_WebHook": "Webhook sortant", "Outgoing_WebHook_Description": "Obtenez des données de Rocket.Chat en temps réel.", + "Outlook_Calendar_Enabled": "Activé", "Output_format": "Format de sortie", "Override_URL_to_which_files_are_uploaded_This_url_also_used_for_downloads_unless_a_CDN_is_given": "Modifier l'URL vers laquelle les fichiers sont chargés. Cette URL est également utilisée pour les téléchargements sauf si un CDN est indiqué", "Page_title": "Titre de la page", diff --git a/apps/meteor/packages/rocketchat-i18n/i18n/gl.i18n.json b/apps/meteor/packages/rocketchat-i18n/i18n/gl.i18n.json index 09dc9a004067..6952be4a7824 100644 --- a/apps/meteor/packages/rocketchat-i18n/i18n/gl.i18n.json +++ b/apps/meteor/packages/rocketchat-i18n/i18n/gl.i18n.json @@ -231,6 +231,7 @@ "Online": "En liña", "Only_you_can_see_this_message": "So ti podes ver esta mensaxe", "Options": "Opcións", + "Outlook_Calendar_Enabled": "Activado", "Phone": "Teléfono", "Pin_Message": "Fixar mensaxe", "pin-message": "Fixar mensaxe", diff --git a/apps/meteor/packages/rocketchat-i18n/i18n/he.i18n.json b/apps/meteor/packages/rocketchat-i18n/i18n/he.i18n.json index ee0c52ca4a39..80a8a97845cf 100644 --- a/apps/meteor/packages/rocketchat-i18n/i18n/he.i18n.json +++ b/apps/meteor/packages/rocketchat-i18n/i18n/he.i18n.json @@ -1021,6 +1021,7 @@ "others": "אחרים", "OTR": "OTR", "OTR_is_only_available_when_both_users_are_online": "OTR זמינה רק כאשר המשתמשים הוא במצב מקוון", + "Outlook_Calendar_Enabled": "מופעל", "Override_URL_to_which_files_are_uploaded_This_url_also_used_for_downloads_unless_a_CDN_is_given": "כתובת אתר דרוס שאליו קבצים מועלים. url זה משמש גם עבור הורדות אלא אם CDN ניתן", "Password": "ססמה", "Password_Change_Disabled": "מנהל המערכת שלך ביטל את האפשרות לשנות סיסמאות", diff --git a/apps/meteor/packages/rocketchat-i18n/i18n/hi-IN.i18n.json b/apps/meteor/packages/rocketchat-i18n/i18n/hi-IN.i18n.json index 0e74af00275e..33e3f43083f5 100644 --- a/apps/meteor/packages/rocketchat-i18n/i18n/hi-IN.i18n.json +++ b/apps/meteor/packages/rocketchat-i18n/i18n/hi-IN.i18n.json @@ -191,6 +191,7 @@ "New_messages": "नए संदेश", "No": "नहीं", "Options": "विकल्प", + "Outlook_Calendar_Enabled": "सक्रिय", "Please_answer_survey": "कृपया इस चैट के बारे में त्वरित सर्वेक्षण का उत्तर देने के लिए एक क्षण लें", "Please_fill_name_and_email": "कृपया नाम और ईमेल भरें", "Public": "जनता", diff --git a/apps/meteor/packages/rocketchat-i18n/i18n/hr.i18n.json b/apps/meteor/packages/rocketchat-i18n/i18n/hr.i18n.json index 0db25d33568a..4a09d901d5df 100644 --- a/apps/meteor/packages/rocketchat-i18n/i18n/hr.i18n.json +++ b/apps/meteor/packages/rocketchat-i18n/i18n/hr.i18n.json @@ -2010,6 +2010,7 @@ "OTR_is_only_available_when_both_users_are_online": "SP je dostupan samo ako su oba korisnika online", "Outgoing_WebHook": "Odlazni WebHook", "Outgoing_WebHook_Description": "Dobijte podatke iz Rocket.Chat u stvarnom vremenu.", + "Outlook_Calendar_Enabled": "Omogućeno", "Override_URL_to_which_files_are_uploaded_This_url_also_used_for_downloads_unless_a_CDN_is_given": "URL mjesta na koji su postavljene datoteke. Ovaj url se također koristi za preuzimanje, osim ako je zadan CDN", "Page_title": "Naslov stranice", "Page_URL": "URL stranice", diff --git a/apps/meteor/packages/rocketchat-i18n/i18n/hu.i18n.json b/apps/meteor/packages/rocketchat-i18n/i18n/hu.i18n.json index 6cbd626f7526..d3ef06c2355a 100644 --- a/apps/meteor/packages/rocketchat-i18n/i18n/hu.i18n.json +++ b/apps/meteor/packages/rocketchat-i18n/i18n/hu.i18n.json @@ -3616,6 +3616,7 @@ "Outgoing": "Kimenő", "Outgoing_WebHook": "Kimenő webhorog", "Outgoing_WebHook_Description": "Adatok lekérése a Rocket.Chatből valós időben.", + "Outlook_Calendar_Enabled": "Engedélyezve", "Output_format": "Kimeneti formátum", "Override_URL_to_which_files_are_uploaded_This_url_also_used_for_downloads_unless_a_CDN_is_given": "Az URL felülbírálása, ahová a fájlok feltöltésre kerülnek. Ez az URL letöltésekhez is használva van, kivéve ha tartalomkézbesítési hálózat van megadva.", "Owner": "Tulajdonos", diff --git a/apps/meteor/packages/rocketchat-i18n/i18n/id.i18n.json b/apps/meteor/packages/rocketchat-i18n/i18n/id.i18n.json index 8a42c8f07318..636095f71f85 100644 --- a/apps/meteor/packages/rocketchat-i18n/i18n/id.i18n.json +++ b/apps/meteor/packages/rocketchat-i18n/i18n/id.i18n.json @@ -1885,6 +1885,7 @@ "OTR_is_only_available_when_both_users_are_online": "OTR hanya tersedia jika kedua pengguna sedang online", "Outgoing_WebHook": "WebHook keluar", "Outgoing_WebHook_Description": "Dapatkan data dari Rocket.Chat secara real-time.", + "Outlook_Calendar_Enabled": "Diaktifkan", "Override_URL_to_which_files_are_uploaded_This_url_also_used_for_downloads_unless_a_CDN_is_given": "URL Override yang file-upload. url ini juga digunakan untuk download kecuali CDN diberikan", "Page_title": "Judul halaman", "Page_URL": "Halaman URL", diff --git a/apps/meteor/packages/rocketchat-i18n/i18n/it.i18n.json b/apps/meteor/packages/rocketchat-i18n/i18n/it.i18n.json index decd72db4baa..e4745f79926a 100644 --- a/apps/meteor/packages/rocketchat-i18n/i18n/it.i18n.json +++ b/apps/meteor/packages/rocketchat-i18n/i18n/it.i18n.json @@ -1946,6 +1946,7 @@ "OTR_is_only_available_when_both_users_are_online": "OTR è disponibile solo se entrambi gli utenti sono on-line", "Outgoing_WebHook": "WebHook in uscita", "Outgoing_WebHook_Description": "Ottieni i dati da Rocket.Chat in tempo reale.", + "Outlook_Calendar_Enabled": "Abilitato", "Override_URL_to_which_files_are_uploaded_This_url_also_used_for_downloads_unless_a_CDN_is_given": "URL sostitutiva in cui vengono caricati i file. Questo URL è utilizzato anche per i download a meno che una CDN sia impostata", "Page_title": "Titolo pagina", "Page_URL": "URL pagina", diff --git a/apps/meteor/packages/rocketchat-i18n/i18n/ja.i18n.json b/apps/meteor/packages/rocketchat-i18n/i18n/ja.i18n.json index 18bd26b34c07..e5be1cb91ce5 100644 --- a/apps/meteor/packages/rocketchat-i18n/i18n/ja.i18n.json +++ b/apps/meteor/packages/rocketchat-i18n/i18n/ja.i18n.json @@ -3254,6 +3254,7 @@ "Outgoing": "発信中", "Outgoing_WebHook": "発信Webhook", "Outgoing_WebHook_Description": "Rocket.Chatからリアルタイムでデータを取得します。", + "Outlook_Calendar_Enabled": "有効", "Output_format": "出力形式", "Override_URL_to_which_files_are_uploaded_This_url_also_used_for_downloads_unless_a_CDN_is_given": "アップロードされたファイルのURLを上書きします。このURLは、CDNからURLが提供されない場合にダウンロードでも使用されます", "Page_title": "ページタイトル", diff --git a/apps/meteor/packages/rocketchat-i18n/i18n/ka-GE.i18n.json b/apps/meteor/packages/rocketchat-i18n/i18n/ka-GE.i18n.json index e95360251310..05d519a1f6c2 100644 --- a/apps/meteor/packages/rocketchat-i18n/i18n/ka-GE.i18n.json +++ b/apps/meteor/packages/rocketchat-i18n/i18n/ka-GE.i18n.json @@ -2562,6 +2562,7 @@ "OTR_is_only_available_when_both_users_are_online": "OTR ხელმისაწვდომია მხოლოდ როდესაც ორივე მომხმარებელი ონლაინ არის", "Outgoing_WebHook": "გამავალი WebHook", "Outgoing_WebHook_Description": "მიიღეთ მონაცემები Rocket.Chat– დან რეალურ დროში.", + "Outlook_Calendar_Enabled": "ჩართულია", "Override_URL_to_which_files_are_uploaded_This_url_also_used_for_downloads_unless_a_CDN_is_given": "შეცვალეთ ლინკი რომელზეც ფაილები აიტვირთება. ეს ლინკი ასევე გამოიყენება გადმოწერებისთვის თუ სხვა CDN არ არის მოწოდებული", "Page_title": "გვერდის სათაური", "Page_URL": "გვერდის URL", diff --git a/apps/meteor/packages/rocketchat-i18n/i18n/km.i18n.json b/apps/meteor/packages/rocketchat-i18n/i18n/km.i18n.json index 43f5ee0e1cd4..0dbd1d7c092c 100644 --- a/apps/meteor/packages/rocketchat-i18n/i18n/km.i18n.json +++ b/apps/meteor/packages/rocketchat-i18n/i18n/km.i18n.json @@ -2187,6 +2187,7 @@ "OTR_is_only_available_when_both_users_are_online": "ប្រវត្តិគឺអាចប្រើបានតែនៅពេលដែលអ្នកប្រើប្រាស់ទាំងពីរគឺមាននៅលើបណ្ដាញ", "Outgoing_WebHook": "WebHook ចេញ", "Outgoing_WebHook_Description": "ទទួលបានទិន្នន័យចេញពី Rocket.Chat ក្នុងពេលវេលាពិតប្រាកដ។", + "Outlook_Calendar_Enabled": "បានបើក", "Override_URL_to_which_files_are_uploaded_This_url_also_used_for_downloads_unless_a_CDN_is_given": "URL ដែលបានបដិសេធទៅនឹងឯកសារដែលបានផ្ទុកឡើង។ URL នេះបានប្រើផងដែរសម្រាប់ការទាញយកបានទេលុះត្រាតែ CDN មួយត្រូវបានផ្ដល់", "Page_title": "ចំណងជើងទំព័រ", "Page_URL": "URL ទំព័រ", diff --git a/apps/meteor/packages/rocketchat-i18n/i18n/ko.i18n.json b/apps/meteor/packages/rocketchat-i18n/i18n/ko.i18n.json index f660099b04c7..2efde93240dd 100644 --- a/apps/meteor/packages/rocketchat-i18n/i18n/ko.i18n.json +++ b/apps/meteor/packages/rocketchat-i18n/i18n/ko.i18n.json @@ -2781,6 +2781,7 @@ "OTR_is_only_available_when_both_users_are_online": "두 사용자가 온라인 상태일 때만 비밀 대화를 사용할 수 있습니다.", "Outgoing_WebHook": "나가는 WebHook", "Outgoing_WebHook_Description": "Rocket.Chat에서 실시간으로 데이터를 가져옵니다.", + "Outlook_Calendar_Enabled": "사용", "Output_format": "출력 형식", "Override_URL_to_which_files_are_uploaded_This_url_also_used_for_downloads_unless_a_CDN_is_given": "파일이 업로드되는 재정의 URL. CDN이 지정되지 않으면, 다운로드 URL로 설정됩니다.", "Page_title": "페이지 제목", diff --git a/apps/meteor/packages/rocketchat-i18n/i18n/ku.i18n.json b/apps/meteor/packages/rocketchat-i18n/i18n/ku.i18n.json index da57c23dec1c..a48e1bab4f1f 100644 --- a/apps/meteor/packages/rocketchat-i18n/i18n/ku.i18n.json +++ b/apps/meteor/packages/rocketchat-i18n/i18n/ku.i18n.json @@ -1872,6 +1872,7 @@ "OTR_is_only_available_when_both_users_are_online": "OTR bi tenê dikarî de derbasdar e ku her du users are bike", "Outgoing_WebHook": "Outgoing WebHook", "Outgoing_WebHook_Description": "Agahdariya ji Rocket-ê dihêle.", + "Outlook_Calendar_Enabled": "çalake", "Override_URL_to_which_files_are_uploaded_This_url_also_used_for_downloads_unless_a_CDN_is_given": "URL Redkirin ji bo ku wêneyên barkirî bi. Ev url jî ji bo downloads eger CDN bikaranîn dayîn", "Page_title": "Title title", "Page_URL": "URL", diff --git a/apps/meteor/packages/rocketchat-i18n/i18n/lo.i18n.json b/apps/meteor/packages/rocketchat-i18n/i18n/lo.i18n.json index 25ea4a1dd26f..7e332c00d551 100644 --- a/apps/meteor/packages/rocketchat-i18n/i18n/lo.i18n.json +++ b/apps/meteor/packages/rocketchat-i18n/i18n/lo.i18n.json @@ -1914,6 +1914,7 @@ "OTR_is_only_available_when_both_users_are_online": "OTR ແມ່ນມີພຽງແຕ່ໃນເວລາທີ່ຜູ້ໃຊ້ທັງສອງອອນໄລນ໌", "Outgoing_WebHook": "Outgoing WebHook", "Outgoing_WebHook_Description": "ໄດ້ຮັບຂໍ້ມູນອອກຈາກ RocketChat ໃນເວລາທີ່ແທ້ຈິງ.", + "Outlook_Calendar_Enabled": "ເປີດການໃຊ້ງານ", "Override_URL_to_which_files_are_uploaded_This_url_also_used_for_downloads_unless_a_CDN_is_given": "URL ແທນທີ່ໄຟລ໌ໄດ້ຖືກອັບໂຫລດ. url ນີ້ຍັງສາມາດໃຊ້ສໍາລັບການດາວໂຫລດເວັ້ນເສຍແຕ່ວ່າແຄນາດາຈະໄດ້ຮັບ", "Page_title": "Page title", "Page_URL": "ຫນ້າ URL", diff --git a/apps/meteor/packages/rocketchat-i18n/i18n/lt.i18n.json b/apps/meteor/packages/rocketchat-i18n/i18n/lt.i18n.json index 43d780cd153d..ba328eb2a2f5 100644 --- a/apps/meteor/packages/rocketchat-i18n/i18n/lt.i18n.json +++ b/apps/meteor/packages/rocketchat-i18n/i18n/lt.i18n.json @@ -1932,6 +1932,7 @@ "OTR_is_only_available_when_both_users_are_online": "OTR galima tik tada, kai abu vartotojai yra prisijungę", "Outgoing_WebHook": "Išeinantis WebHook", "Outgoing_WebHook_Description": "Gauti duomenis iš \"Rocket.Chat\" realiuoju laiku.", + "Outlook_Calendar_Enabled": "Įjungtas", "Override_URL_to_which_files_are_uploaded_This_url_also_used_for_downloads_unless_a_CDN_is_given": "Nepaisyti URL, į kurį įkelti failai. Šis URL taip pat naudojamas atsisiuntimui, jei nėra CDN", "Page_title": "Puslapio pavadinimas", "Page_URL": "Puslapio URL", diff --git a/apps/meteor/packages/rocketchat-i18n/i18n/lv.i18n.json b/apps/meteor/packages/rocketchat-i18n/i18n/lv.i18n.json index b8e0f5d84a66..9b5187f13b17 100644 --- a/apps/meteor/packages/rocketchat-i18n/i18n/lv.i18n.json +++ b/apps/meteor/packages/rocketchat-i18n/i18n/lv.i18n.json @@ -1890,6 +1890,7 @@ "OTR_is_only_available_when_both_users_are_online": "OTR ir pieejams tikai tad, ja abi lietotāji ir tiešsaistē", "Outgoing_WebHook": "Izejošais WebHook", "Outgoing_WebHook_Description": "Reāllaikā iegūt datus no Rocket.Chat.", + "Outlook_Calendar_Enabled": "Iespējots", "Override_URL_to_which_files_are_uploaded_This_url_also_used_for_downloads_unless_a_CDN_is_given": "Pārrakstīt URL, uz kuru faili tika augšupielādēti. Šis URL tiek izmantots arī lejupielādei, ja nav norādīts CDN", "Page_title": "Lapas nosaukums", "Page_URL": "Lapas URL", diff --git a/apps/meteor/packages/rocketchat-i18n/i18n/mn.i18n.json b/apps/meteor/packages/rocketchat-i18n/i18n/mn.i18n.json index a78f07857973..1900ae9e2554 100644 --- a/apps/meteor/packages/rocketchat-i18n/i18n/mn.i18n.json +++ b/apps/meteor/packages/rocketchat-i18n/i18n/mn.i18n.json @@ -1872,6 +1872,7 @@ "OTR_is_only_available_when_both_users_are_online": "OTR нь хоёулаа онлайн байх үед л боломжтой", "Outgoing_WebHook": "Гарах WebHook", "Outgoing_WebHook_Description": "Рокет.Chat-ээс өгөгдлийг бодит цагт авах.", + "Outlook_Calendar_Enabled": "Идэвхжүүлсэн", "Override_URL_to_which_files_are_uploaded_This_url_also_used_for_downloads_unless_a_CDN_is_given": "Файлуудыг байршуулсан URL-ыг хэтрүүлэх. Энэ url нь CDN өгөгдөөгүй бол татан авахад хэрэглэгддэг", "Page_title": "Хуудасны гарчиг", "Page_URL": "Хуудасны URL", diff --git a/apps/meteor/packages/rocketchat-i18n/i18n/ms-MY.i18n.json b/apps/meteor/packages/rocketchat-i18n/i18n/ms-MY.i18n.json index 0749c30b656f..9c3437af7dda 100644 --- a/apps/meteor/packages/rocketchat-i18n/i18n/ms-MY.i18n.json +++ b/apps/meteor/packages/rocketchat-i18n/i18n/ms-MY.i18n.json @@ -1884,6 +1884,7 @@ "OTR_is_only_available_when_both_users_are_online": "OTR hanya tersedia apabila kedua-dua pengguna sedang online", "Outgoing_WebHook": "WebHook keluar", "Outgoing_WebHook_Description": "Dapatkan data dari Rocket.Chat secara real-time.", + "Outlook_Calendar_Enabled": "didayakan", "Override_URL_to_which_files_are_uploaded_This_url_also_used_for_downloads_unless_a_CDN_is_given": "URL Override untuk fail yang dimuat naik. url ini juga digunakan untuk muat turun melainkan CDN diberikan", "Page_title": "Tajuk halaman", "Page_URL": "URL halaman", diff --git a/apps/meteor/packages/rocketchat-i18n/i18n/nl.i18n.json b/apps/meteor/packages/rocketchat-i18n/i18n/nl.i18n.json index fdc52656a9c8..b132f58ad1c6 100644 --- a/apps/meteor/packages/rocketchat-i18n/i18n/nl.i18n.json +++ b/apps/meteor/packages/rocketchat-i18n/i18n/nl.i18n.json @@ -3273,6 +3273,7 @@ "Outgoing": "Uitgaande", "Outgoing_WebHook": "Uitgaande WebHook", "Outgoing_WebHook_Description": "Haal uit Rocket.Chat in realtime.", + "Outlook_Calendar_Enabled": "Ingeschakeld", "Output_format": "Uitvoerformaat", "Override_URL_to_which_files_are_uploaded_This_url_also_used_for_downloads_unless_a_CDN_is_given": "Overschrijf de URL waarnaar bestanden worden geüpload. Deze url wordt ook gebruikt voor downloads, tenzij een CDN is opgegeven", "Page_title": "Pagina titel", diff --git a/apps/meteor/packages/rocketchat-i18n/i18n/no.i18n.json b/apps/meteor/packages/rocketchat-i18n/i18n/no.i18n.json index e3ce45d342d8..5f078e63f3b6 100644 --- a/apps/meteor/packages/rocketchat-i18n/i18n/no.i18n.json +++ b/apps/meteor/packages/rocketchat-i18n/i18n/no.i18n.json @@ -1964,6 +1964,7 @@ "OTR_is_only_available_when_both_users_are_online": "OTR er bare tilgjengelig når begge brukerne er online", "Outgoing_WebHook": "Utgående WebHook", "Outgoing_WebHook_Description": "Få data ut av Rocket.Chat i sanntid.", + "Outlook_Calendar_Enabled": "aktivert", "Override_URL_to_which_files_are_uploaded_This_url_also_used_for_downloads_unless_a_CDN_is_given": "Overstyr URL-adressen til hvilke filer som lastes opp. Denne nettadressen brukes også til nedlastinger med mindre en CDN er gitt", "Page_title": "Side tittel", "Page_URL": "Side URL", diff --git a/apps/meteor/packages/rocketchat-i18n/i18n/pl.i18n.json b/apps/meteor/packages/rocketchat-i18n/i18n/pl.i18n.json index ee9d3c9a95f3..4d97d2559461 100644 --- a/apps/meteor/packages/rocketchat-i18n/i18n/pl.i18n.json +++ b/apps/meteor/packages/rocketchat-i18n/i18n/pl.i18n.json @@ -3554,6 +3554,7 @@ "Outgoing": "Wychodzący", "Outgoing_WebHook": "Wychodzący WebHook", "Outgoing_WebHook_Description": "Uzyskaj dane z Rocket.Chat w czasie rzeczywistym.", + "Outlook_Calendar_Enabled": "Włączone", "Output_format": "Format wyjściowy", "Override_URL_to_which_files_are_uploaded_This_url_also_used_for_downloads_unless_a_CDN_is_given": "Zastąp adres URL, do którego są przesyłane pliki. Ten url również wykorzystywane do pobrania, chyba że podano CDN", "Owner": "Właściciel", diff --git a/apps/meteor/packages/rocketchat-i18n/i18n/pt-BR.i18n.json b/apps/meteor/packages/rocketchat-i18n/i18n/pt-BR.i18n.json index efbe4b426654..7d85f1ae67e7 100644 --- a/apps/meteor/packages/rocketchat-i18n/i18n/pt-BR.i18n.json +++ b/apps/meteor/packages/rocketchat-i18n/i18n/pt-BR.i18n.json @@ -3310,6 +3310,7 @@ "Outgoing": "Enviado", "Outgoing_WebHook": "WebHook de saída", "Outgoing_WebHook_Description": "Obtenha dados de Rocket.Chat em tempo real.", + "Outlook_Calendar_Enabled": "Habilitado", "Output_format": "Formato de saída", "Override_URL_to_which_files_are_uploaded_This_url_also_used_for_downloads_unless_a_CDN_is_given": "Substituir URL para os arquivos que foram carregados. Este URL também será usado para downloads, a menos que um CDN seja fornecido", "Page_title": "Título da página", diff --git a/apps/meteor/packages/rocketchat-i18n/i18n/pt.i18n.json b/apps/meteor/packages/rocketchat-i18n/i18n/pt.i18n.json index 3e2ea1db9c0f..e1e827e21cdd 100644 --- a/apps/meteor/packages/rocketchat-i18n/i18n/pt.i18n.json +++ b/apps/meteor/packages/rocketchat-i18n/i18n/pt.i18n.json @@ -2189,6 +2189,7 @@ "OTR_is_only_available_when_both_users_are_online": "OTR só está disponível quando os utilizadores estão online", "Outgoing_WebHook": "WebHook de saída", "Outgoing_WebHook_Description": "Obtenha os dados de Rocket.Chat em tempo real.", + "Outlook_Calendar_Enabled": "Habilitado", "Override_URL_to_which_files_are_uploaded_This_url_also_used_for_downloads_unless_a_CDN_is_given": "Substituir URL para a qual os arquivos são carregados. Esta url também será usada para downloads, a menos que um CDN seja fornecido", "Page_title": "Título da página", "Page_URL": "URL da página", diff --git a/apps/meteor/packages/rocketchat-i18n/i18n/ro.i18n.json b/apps/meteor/packages/rocketchat-i18n/i18n/ro.i18n.json index 4a9bf52c9456..8066a2a0bac5 100644 --- a/apps/meteor/packages/rocketchat-i18n/i18n/ro.i18n.json +++ b/apps/meteor/packages/rocketchat-i18n/i18n/ro.i18n.json @@ -1876,6 +1876,7 @@ "OTR_is_only_available_when_both_users_are_online": "OTR este disponibilă numai atunci când ambii utilizatori sunt online", "Outgoing_WebHook": "WebHook de ieșire", "Outgoing_WebHook_Description": "Obțineți date din Rocket.Chat în timp real.", + "Outlook_Calendar_Enabled": "Activat", "Override_URL_to_which_files_are_uploaded_This_url_also_used_for_downloads_unless_a_CDN_is_given": "URL-ul supracontrol la care sunt încărcate fișiere. Această adresă URL, de asemenea, utilizat pentru download-uri decât dacă o CDN este dată", "Page_title": "Titlul paginii", "Page_URL": "Adresa URL a paginii", diff --git a/apps/meteor/packages/rocketchat-i18n/i18n/ru.i18n.json b/apps/meteor/packages/rocketchat-i18n/i18n/ru.i18n.json index 42f750b956db..31c421a22be2 100644 --- a/apps/meteor/packages/rocketchat-i18n/i18n/ru.i18n.json +++ b/apps/meteor/packages/rocketchat-i18n/i18n/ru.i18n.json @@ -3433,6 +3433,7 @@ "Outgoing": "Исходящие", "Outgoing_WebHook": "Исходящий WebHook", "Outgoing_WebHook_Description": "Получать данные из Rocket.Chat в режиме реального времени", + "Outlook_Calendar_Enabled": "Включено", "Output_format": "Формат вывода", "Override_URL_to_which_files_are_uploaded_This_url_also_used_for_downloads_unless_a_CDN_is_given": "Переопределить URL-адрес, на который загружены файлы. Этот URL-адрес также используется для загрузок до тех пор, пока не указан CDN.", "Page_title": "Заголовок страницы", diff --git a/apps/meteor/packages/rocketchat-i18n/i18n/sk-SK.i18n.json b/apps/meteor/packages/rocketchat-i18n/i18n/sk-SK.i18n.json index eb54defefe86..7504f0812efa 100644 --- a/apps/meteor/packages/rocketchat-i18n/i18n/sk-SK.i18n.json +++ b/apps/meteor/packages/rocketchat-i18n/i18n/sk-SK.i18n.json @@ -1886,6 +1886,7 @@ "OTR_is_only_available_when_both_users_are_online": "OTR je k dispozícii iba vtedy, ak sú obaja používatelia online", "Outgoing_WebHook": "Odchádzajúci WebHook", "Outgoing_WebHook_Description": "Získajte dáta z Rocket.Chat v reálnom čase.", + "Outlook_Calendar_Enabled": "Povolené", "Override_URL_to_which_files_are_uploaded_This_url_also_used_for_downloads_unless_a_CDN_is_given": "Prepísať adresu URL, do ktorej sú nahrané súbory. Táto adresa URL sa tiež používa na sťahovanie, ak nie je zadaná žiadosť o CDN", "Page_title": "Názov stránky", "Page_URL": "Adresa URL stránky", diff --git a/apps/meteor/packages/rocketchat-i18n/i18n/sl-SI.i18n.json b/apps/meteor/packages/rocketchat-i18n/i18n/sl-SI.i18n.json index 954c54830717..89e48c8937a5 100644 --- a/apps/meteor/packages/rocketchat-i18n/i18n/sl-SI.i18n.json +++ b/apps/meteor/packages/rocketchat-i18n/i18n/sl-SI.i18n.json @@ -1866,6 +1866,7 @@ "OTR_is_only_available_when_both_users_are_online": "OTR na voljo samo, ko sta oba uporabnika dosegljiva", "Outgoing_WebHook": "Odhodni WebHook", "Outgoing_WebHook_Description": "Pridobite podatke iz aplikacije Rocket.Chat v realnem času.", + "Outlook_Calendar_Enabled": "Omogočeno", "Override_URL_to_which_files_are_uploaded_This_url_also_used_for_downloads_unless_a_CDN_is_given": "Prevozi URL, na katerega so naložene datoteke. Ta URL je bil uporabljen tudi za prenose, razen če je podan CDN", "Page_title": "Naslov strani", "Page_URL": "URL strani", diff --git a/apps/meteor/packages/rocketchat-i18n/i18n/sq.i18n.json b/apps/meteor/packages/rocketchat-i18n/i18n/sq.i18n.json index fdd705dd663b..a98a96b6a10c 100644 --- a/apps/meteor/packages/rocketchat-i18n/i18n/sq.i18n.json +++ b/apps/meteor/packages/rocketchat-i18n/i18n/sq.i18n.json @@ -1876,6 +1876,7 @@ "OTR_is_only_available_when_both_users_are_online": "OTR është në dispozicion vetëm kur të dy përdoruesit janë në internet", "Outgoing_WebHook": "WebHook që po largohet", "Outgoing_WebHook_Description": "Merrni të dhëna nga Rocket.Chat në kohë reale.", + "Outlook_Calendar_Enabled": "enabled", "Override_URL_to_which_files_are_uploaded_This_url_also_used_for_downloads_unless_a_CDN_is_given": "Override URL të cilat fotografi janë ngarkuar. Kjo url përdorur edhe për shkarkime, përveç nëse një CDN është dhënë", "Page_title": "Titulli i faqes", "Page_URL": "URL e faqes", diff --git a/apps/meteor/packages/rocketchat-i18n/i18n/sr.i18n.json b/apps/meteor/packages/rocketchat-i18n/i18n/sr.i18n.json index 725a094d4e2c..21eb34b49b3a 100644 --- a/apps/meteor/packages/rocketchat-i18n/i18n/sr.i18n.json +++ b/apps/meteor/packages/rocketchat-i18n/i18n/sr.i18n.json @@ -1708,6 +1708,7 @@ "OTR": "ОТР", "Outgoing_WebHook": "Одлазни ВебХоок", "Outgoing_WebHook_Description": "Добијте податке из Роцкет.Цхат у реалном времену.", + "Outlook_Calendar_Enabled": "Омогућено", "Override_URL_to_which_files_are_uploaded_This_url_also_used_for_downloads_unless_a_CDN_is_given": "Замени УРЛ адреса за датотеке које су уплоадед. Овај УРЛ се користи за преузимање, осим ако ЦДН дат", "Page_title": "Наслов странице", "Page_URL": "УРЛ адреса странице", diff --git a/apps/meteor/packages/rocketchat-i18n/i18n/sv.i18n.json b/apps/meteor/packages/rocketchat-i18n/i18n/sv.i18n.json index 873952ba420c..584725906cd2 100644 --- a/apps/meteor/packages/rocketchat-i18n/i18n/sv.i18n.json +++ b/apps/meteor/packages/rocketchat-i18n/i18n/sv.i18n.json @@ -3759,6 +3759,7 @@ "Outgoing": "Utgående", "Outgoing_WebHook": "Utgående WebHook", "Outgoing_WebHook_Description": "Hämta data från Rocket.Chat i realtid.", + "Outlook_Calendar_Enabled": "Aktiverad", "Output_format": "Utdataformat", "Override_URL_to_which_files_are_uploaded_This_url_also_used_for_downloads_unless_a_CDN_is_given": "Åsidosätt URL till vilken filer som laddas upp. Denna url används också för nedladdning såvida inte en CDN anges", "Owner": "Ägare", diff --git a/apps/meteor/packages/rocketchat-i18n/i18n/ta-IN.i18n.json b/apps/meteor/packages/rocketchat-i18n/i18n/ta-IN.i18n.json index fb9d1cffe1af..02682e3aeea0 100644 --- a/apps/meteor/packages/rocketchat-i18n/i18n/ta-IN.i18n.json +++ b/apps/meteor/packages/rocketchat-i18n/i18n/ta-IN.i18n.json @@ -1877,6 +1877,7 @@ "OTR_is_only_available_when_both_users_are_online": "இரண்டு பயனர்கள் ஆன்லைனில் இருக்கும் போது OTR மட்டுமே உள்ளது", "Outgoing_WebHook": "வெளிச்செல்லும் WebHook", "Outgoing_WebHook_Description": "நிகழ்நேரத்தில் ராக்கெட்.சட்டை வெளியே தரவு கிடைக்கும்.", + "Outlook_Calendar_Enabled": "இயக்கப்பட்டது", "Override_URL_to_which_files_are_uploaded_This_url_also_used_for_downloads_unless_a_CDN_is_given": "மீறு URL ஐ கோப்புகளை பதிவேற்றப்படும் வேண்டும். ஒரு வலம்புரி வரை இறக்கம் பயன்படுத்தப்படும் இந்த URL வழங்கப்படும்", "Page_title": "பக்க தலைப்பு", "Page_URL": "பக்க URL", diff --git a/apps/meteor/packages/rocketchat-i18n/i18n/th-TH.i18n.json b/apps/meteor/packages/rocketchat-i18n/i18n/th-TH.i18n.json index 7574fb516b5f..98c7f58e3130 100644 --- a/apps/meteor/packages/rocketchat-i18n/i18n/th-TH.i18n.json +++ b/apps/meteor/packages/rocketchat-i18n/i18n/th-TH.i18n.json @@ -1870,6 +1870,7 @@ "OTR_is_only_available_when_both_users_are_online": "OTR จะใช้ได้เฉพาะเมื่อผู้ใช้ทั้งสองออนไลน์เท่านั้น", "Outgoing_WebHook": "WebHook ขาออก", "Outgoing_WebHook_Description": "รับข้อมูลจาก Rocket.Chat แบบเรียลไทม์", + "Outlook_Calendar_Enabled": "เปิดใช้งานแล้ว", "Override_URL_to_which_files_are_uploaded_This_url_also_used_for_downloads_unless_a_CDN_is_given": "แทนที่ URL ที่อัปโหลดไฟล์ URL นี้ใช้สำหรับการดาวน์โหลดเว้นแต่จะได้รับ CDN", "Page_title": "ชื่อหน้า", "Page_URL": "URL ของหน้าเว็บ", diff --git a/apps/meteor/packages/rocketchat-i18n/i18n/tr.i18n.json b/apps/meteor/packages/rocketchat-i18n/i18n/tr.i18n.json index 4b5a57995436..5c78d413e7f6 100644 --- a/apps/meteor/packages/rocketchat-i18n/i18n/tr.i18n.json +++ b/apps/meteor/packages/rocketchat-i18n/i18n/tr.i18n.json @@ -2245,6 +2245,7 @@ "OTR_is_only_available_when_both_users_are_online": "Kayıt Dışı, yalnızca her iki kullanıcı da çevrimiçi ise kullanılabilir.", "Outgoing_WebHook": "Giden WebHook", "Outgoing_WebHook_Description": "Gerçek zamanlı olarak Rocket.Chat'ten veri alın.", + "Outlook_Calendar_Enabled": "Etkin", "Override_URL_to_which_files_are_uploaded_This_url_also_used_for_downloads_unless_a_CDN_is_given": "dosyaların yüklendiği hangi geçersiz kıl URL. Ayrıca CDN sürece indirme için kullanılan bu url verilir", "Page_title": "Sayfa başlığı", "Page_URL": "Sayfa URL'si", diff --git a/apps/meteor/packages/rocketchat-i18n/i18n/ug.i18n.json b/apps/meteor/packages/rocketchat-i18n/i18n/ug.i18n.json index e72486a80a6f..a007147fb810 100644 --- a/apps/meteor/packages/rocketchat-i18n/i18n/ug.i18n.json +++ b/apps/meteor/packages/rocketchat-i18n/i18n/ug.i18n.json @@ -792,6 +792,7 @@ "others": "باشقا", "OTR": "خاتىرىلەنمەيدىغان دىيالوگ", "OTR_is_only_available_when_both_users_are_online": "خاتىرىلەنمەيدىغان دىيالوگ پەقەت ئىككى تەرەپ توردا بولغاندا ئاندىن ئىشلەتكىلى بولىدۇ", + "Outlook_Calendar_Enabled": "ئىشلىتىشكە باشلىدى", "Override_URL_to_which_files_are_uploaded_This_url_also_used_for_downloads_unless_a_CDN_is_given": "مۇ چۈشۈرۈش ئۇلانمىسى بولۇپ ئىشلىتىلىدۇ .urlنى تەڭشىمىگەن بولسىڭىز ، CDNنى قايتا يېزىپ ئۇنى ھۆججەت چىقىرىش ئادرېسى قىلىپ بېكىتىڭ. ئەگەر URL", "Password": "پارول", "Password_Change_Disabled": "باشقۇرغۇچىڭىز ئاللىبۇرۇن پارولنى ئۆزگەرتىش ئىقتىدارىنى چەكلىدىRocket.Chatسىزنىڭ", diff --git a/apps/meteor/packages/rocketchat-i18n/i18n/uk.i18n.json b/apps/meteor/packages/rocketchat-i18n/i18n/uk.i18n.json index 9919c95fc36e..c7a1d9f5e576 100644 --- a/apps/meteor/packages/rocketchat-i18n/i18n/uk.i18n.json +++ b/apps/meteor/packages/rocketchat-i18n/i18n/uk.i18n.json @@ -2406,6 +2406,7 @@ "OTR_is_only_available_when_both_users_are_online": "OTR доступний тільки тоді, коли обидва користувачі знаходяться в мережі", "Outgoing_WebHook": "Вихідний WebHook", "Outgoing_WebHook_Description": "Отримайте дані поза Rocket.Chat в режимі реального часу.", + "Outlook_Calendar_Enabled": "Включено", "Override_URL_to_which_files_are_uploaded_This_url_also_used_for_downloads_unless_a_CDN_is_given": "Override URL, до якого файли завантажуються. Цей URL-адресу також використовується для завантаження, якщо тільки в CDN дається", "Page_title": "Назва сторінки", "Page_URL": "URL-адреса сторінки", diff --git a/apps/meteor/packages/rocketchat-i18n/i18n/vi-VN.i18n.json b/apps/meteor/packages/rocketchat-i18n/i18n/vi-VN.i18n.json index d531bd034f62..d2f19f8d73ce 100644 --- a/apps/meteor/packages/rocketchat-i18n/i18n/vi-VN.i18n.json +++ b/apps/meteor/packages/rocketchat-i18n/i18n/vi-VN.i18n.json @@ -1977,6 +1977,7 @@ "OTR_is_only_available_when_both_users_are_online": "OTR chỉ khả dụng khi cả người dùng trực tuyến", "Outgoing_WebHook": "WebHook gửi đi", "Outgoing_WebHook_Description": "Lấy dữ liệu ra khỏi Rocket.Chat theo thời gian thực.", + "Outlook_Calendar_Enabled": "Đã bật", "Override_URL_to_which_files_are_uploaded_This_url_also_used_for_downloads_unless_a_CDN_is_given": "Ghi đè URL mà tệp được tải lên. Url này cũng được sử dụng cho việc tải xuống trừ khi có CDN", "Page_title": "Tiêu đề trang", "Page_URL": "URL Trang", diff --git a/apps/meteor/packages/rocketchat-i18n/i18n/zh-HK.i18n.json b/apps/meteor/packages/rocketchat-i18n/i18n/zh-HK.i18n.json index ed085a4b9630..49ec4bee3d66 100644 --- a/apps/meteor/packages/rocketchat-i18n/i18n/zh-HK.i18n.json +++ b/apps/meteor/packages/rocketchat-i18n/i18n/zh-HK.i18n.json @@ -1901,6 +1901,7 @@ "OTR_is_only_available_when_both_users_are_online": "OTR仅在两个用户在线时才可用", "Outgoing_WebHook": "即将离任的WebHook", "Outgoing_WebHook_Description": "实时获取Rocket.Chat数据。", + "Outlook_Calendar_Enabled": "已啟用", "Override_URL_to_which_files_are_uploaded_This_url_also_used_for_downloads_unless_a_CDN_is_given": "覆盖文件上传到的URL。此网址也用于下载,除非提供CDN", "Page_title": "页面标题", "Page_URL": "页面URL", diff --git a/apps/meteor/packages/rocketchat-i18n/i18n/zh-TW.i18n.json b/apps/meteor/packages/rocketchat-i18n/i18n/zh-TW.i18n.json index a23e2152c5cf..6e9480b1bb5a 100644 --- a/apps/meteor/packages/rocketchat-i18n/i18n/zh-TW.i18n.json +++ b/apps/meteor/packages/rocketchat-i18n/i18n/zh-TW.i18n.json @@ -3159,6 +3159,7 @@ "Outgoing": "傳出", "Outgoing_WebHook": "外部的 WebHook", "Outgoing_WebHook_Description": "即時獲取 Rocket.Chat 資料。", + "Outlook_Calendar_Enabled": "啟用", "Output_format": "輸出格式", "Override_URL_to_which_files_are_uploaded_This_url_also_used_for_downloads_unless_a_CDN_is_given": "哪些檔案被上傳覆蓋網址。該網址也可用於下載,除非CDN放出", "Page_title": "頁面標題", diff --git a/apps/meteor/packages/rocketchat-i18n/i18n/zh.i18n.json b/apps/meteor/packages/rocketchat-i18n/i18n/zh.i18n.json index 17b890b82245..84b95cc7cf02 100644 --- a/apps/meteor/packages/rocketchat-i18n/i18n/zh.i18n.json +++ b/apps/meteor/packages/rocketchat-i18n/i18n/zh.i18n.json @@ -2843,6 +2843,7 @@ "OTR_is_only_available_when_both_users_are_online": "OTR 只能在双方均在线时使用", "Outgoing_WebHook": "出站 WebHook", "Outgoing_WebHook_Description": "实时获取Rocket.Chat数据。", + "Outlook_Calendar_Enabled": "已启用", "Output_format": "输出格式", "Override_URL_to_which_files_are_uploaded_This_url_also_used_for_downloads_unless_a_CDN_is_given": "重写 URL 为文件上传地址。如果没有设置 CDN,url 也会被当作下载链接。", "Page_title": "页面标题", From ffea6f4b74745dab85cbbf25ce26edb3f473f491 Mon Sep 17 00:00:00 2001 From: Douglas Fabris Date: Tue, 18 Jul 2023 23:09:46 -0300 Subject: [PATCH 022/342] regression: Remove wrong navigation bar feature preview image (#29859) --- packages/ui-client/src/hooks/useFeaturePreviewList.ts | 1 - 1 file changed, 1 deletion(-) diff --git a/packages/ui-client/src/hooks/useFeaturePreviewList.ts b/packages/ui-client/src/hooks/useFeaturePreviewList.ts index 87a89e9d5a67..03d31c938e76 100644 --- a/packages/ui-client/src/hooks/useFeaturePreviewList.ts +++ b/packages/ui-client/src/hooks/useFeaturePreviewList.ts @@ -26,7 +26,6 @@ export const defaultFeaturesPreview: FeaturePreviewProps[] = [ i18n: 'Navigation_bar', description: 'Navigation_bar_description', group: 'Navigation', - imageUrl: 'images/featurePreview/quick-reactions.png', value: false, }, ]; From 287878c1eea0fd1140ca75a17dbd58e82eb7a10b Mon Sep 17 00:00:00 2001 From: Tasso Evangelista Date: Tue, 18 Jul 2023 23:35:45 -0300 Subject: [PATCH 023/342] chore(lint): Reinforce ESLint for React (#29857) --- apps/meteor/.eslintrc.json | 25 +------- .../ComposerBoxPopupPreview.tsx | 2 +- apps/meteor/client/.eslintrc.json | 25 +------- .../DefaultParentRoomField.tsx | 2 +- .../InfoPanel/InfoPanel.stories.tsx | 2 +- .../Omnichannel/modals/ForwardChatModal.tsx | 30 +++++----- .../urlPreviews/OEmbedPreviewContent.tsx | 2 +- .../content/urlPreviews/UrlImagePreview.tsx | 2 +- .../client/sidebar/Item/Condensed.stories.tsx | 6 +- apps/meteor/client/sidebar/Item/Condensed.tsx | 6 +- .../client/sidebar/Item/Extended.stories.tsx | 6 +- apps/meteor/client/sidebar/Item/Extended.tsx | 6 +- .../client/sidebar/Item/Medium.stories.tsx | 6 +- apps/meteor/client/sidebar/Item/Medium.tsx | 6 +- .../FederatedRoomListItem.tsx | 2 +- .../MatrixFederationManageServerModal.tsx | 2 +- .../actions/OmnichannelLivechatToggle.tsx | 2 +- .../AccountTokensTable/AccountTokensTable.tsx | 10 ++-- .../RegisterWorkspaceSetupStepOneModal.tsx | 2 +- .../moderation/ModerationConsoleTable.tsx | 18 ++---- .../permissions/CustomRoleUpsellModal.tsx | 2 +- .../views/admin/permissions/RoleForm.tsx | 2 +- .../client/views/admin/rooms/RoomsTable.tsx | 19 ++---- .../client/views/admin/settings/Section.tsx | 2 +- .../groups/voip/VoipExtensionsPage.tsx | 2 +- .../tabs/users/UsersTable/UsersTable.tsx | 4 +- apps/meteor/client/views/meet/CallPage.tsx | 2 +- apps/meteor/client/views/meet/MeetPage.tsx | 2 +- .../omnichannel/agents/AgentInfoActions.tsx | 4 +- .../MessageList/ContactHistoryMessage.tsx | 4 +- .../departments/EditDepartment.tsx | 12 ++-- .../directory/ChatsContextualBar.tsx | 2 +- .../directory/ContactContextualBar.tsx | 2 +- .../omnichannel/directory/calls/CallTable.tsx | 6 +- .../Omnichannel/OmnichannelRoomHeader.tsx | 2 +- apps/meteor/client/views/room/RoomOpener.tsx | 6 +- .../ExportMessages/MailExportForm.tsx | 2 +- .../contextualBar/Info/RoomInfo/RoomInfo.tsx | 2 +- .../NotificationPreferencesForm.tsx | 2 +- .../views/setupWizard/steps/AdminInfoStep.tsx | 2 +- .../teams/contextualBar/info/TeamsInfo.tsx | 16 +++-- .../cannedResponses/CannedResponsesTable.tsx | 2 +- .../components/cannedResponseForm.tsx | 2 +- .../priorities/PriorityEditForm.tsx | 2 +- .../omnichannel/slaPolicies/SlaTable.tsx | 2 +- .../client/omnichannel/units/UnitsTable.tsx | 6 +- .../DeviceManagementAccountTable.tsx | 10 ++-- .../DeviceManagementAdminTable.tsx | 16 ++--- .../channels/ChannelsOverview.tsx | 2 +- .../messages/MessagesPerChannelSection.tsx | 2 +- apps/meteor/tests/e2e/.eslintrc.json | 4 +- ee/packages/pdf-worker/.eslintrc.json | 2 +- ee/packages/ui-theming/.eslintrc.json | 2 +- .../ui-theming/src/PaletteStyleTag.tsx | 3 +- .../ui-theming/src/SidebarPaletteStyleTag.tsx | 2 +- .../ui-theming/src/hooks/useThemeMode.ts | 13 ++++- package.json | 2 +- packages/eslint-config/react.js | 32 ++++++++++ packages/fuselage-ui-kit/.eslintrc.js | 7 ++- packages/gazzodown/.eslintrc.json | 1 + packages/mock-providers/.eslintrc.json | 2 +- packages/ui-client/.eslintrc.json | 23 ++------ packages/ui-composer/.eslintrc.json | 21 +------ packages/ui-contexts/.eslintrc.json | 2 +- packages/ui-video-conf/.eslintrc.json | 23 ++------ packages/web-ui-registration/.eslintrc.json | 2 +- .../components/LoginSwitchLanguageFooter.tsx | 2 +- yarn.lock | 58 +++++++++---------- 68 files changed, 238 insertions(+), 266 deletions(-) create mode 100644 packages/eslint-config/react.js diff --git a/apps/meteor/.eslintrc.json b/apps/meteor/.eslintrc.json index 31d63e993611..a338ba226b3b 100644 --- a/apps/meteor/.eslintrc.json +++ b/apps/meteor/.eslintrc.json @@ -1,5 +1,5 @@ { - "extends": ["@rocket.chat/eslint-config", "plugin:you-dont-need-lodash-underscore/compatible"], + "extends": ["@rocket.chat/eslint-config", "@rocket.chat/eslint-config/react", "plugin:you-dont-need-lodash-underscore/compatible"], "globals": { "__meteor_bootstrap__": false, "__meteor_runtime_config__": false, @@ -7,13 +7,7 @@ "chrome": false, "jscolor": false }, - "plugins": ["react", "react-hooks"], "rules": { - "react/jsx-uses-react": "error", - "react/jsx-uses-vars": "error", - "react/jsx-no-undef": "error", - "react/jsx-fragments": ["error", "syntax"], - "react-hooks/rules-of-hooks": "error", "react-hooks/exhaustive-deps": [ "warn", { @@ -21,11 +15,6 @@ } ] }, - "settings": { - "react": { - "version": "detect" - } - }, "overrides": [ { "files": ["**/*.ts", "**/*.tsx"], @@ -33,12 +22,7 @@ "Atomics": "readonly", "SharedArrayBuffer": "readonly" }, - "plugins": ["react"], "rules": { - "react/jsx-uses-react": "error", - "react/jsx-uses-vars": "error", - "react/jsx-no-undef": "error", - "react/jsx-fragments": ["error", "syntax"], "@typescript-eslint/no-misused-promises": [ "error", { @@ -53,12 +37,7 @@ "parserOptions": { "project": ["./tsconfig.json"] }, - "excludedFiles": [".scripts/*.ts"], - "settings": { - "react": { - "version": "detect" - } - } + "excludedFiles": [".scripts/*.ts"] }, { "files": ["**/*.tests.js", "**/*.tests.ts", "**/*.spec.ts"], diff --git a/apps/meteor/app/ui-message/client/popup/components/composerBoxPopupPreview/ComposerBoxPopupPreview.tsx b/apps/meteor/app/ui-message/client/popup/components/composerBoxPopupPreview/ComposerBoxPopupPreview.tsx index 45188e62122c..89c7de840820 100644 --- a/apps/meteor/app/ui-message/client/popup/components/composerBoxPopupPreview/ComposerBoxPopupPreview.tsx +++ b/apps/meteor/app/ui-message/client/popup/components/composerBoxPopupPreview/ComposerBoxPopupPreview.tsx @@ -19,7 +19,7 @@ const ComposerBoxPopupPreview = forwardRef< tmid?: string; suspended?: boolean; } ->(({ focused, items, rid, tmid, select, suspended }, ref) => { +>(function ComposerBoxPopupPreview({ focused, items, rid, tmid, select, suspended }, ref) { const id = useUniqueId(); const chat = useChat(); const executeSlashCommandPreviewMethod = useMethod('executeSlashCommandPreview'); diff --git a/apps/meteor/client/.eslintrc.json b/apps/meteor/client/.eslintrc.json index 286fbb957017..f772d4366e0e 100644 --- a/apps/meteor/client/.eslintrc.json +++ b/apps/meteor/client/.eslintrc.json @@ -1,6 +1,6 @@ { "root": true, - "extends": ["@rocket.chat/eslint-config/original", "prettier", "plugin:you-dont-need-lodash-underscore/compatible"], + "extends": ["@rocket.chat/eslint-config/original", "@rocket.chat/eslint-config/react", "prettier", "plugin:you-dont-need-lodash-underscore/compatible"], "parser": "@babel/eslint-parser", "plugins": ["react", "react-hooks", "prettier", "testing-library", "anti-trojan-source"], "rules": { @@ -29,14 +29,6 @@ } ], "prettier/prettier": 2, - "react/display-name": "error", - "react/jsx-uses-react": "error", - "react/jsx-uses-vars": "error", - "react/jsx-no-undef": "error", - "react/jsx-fragments": ["error", "syntax"], - "react/jsx-key": ["error", { "checkFragmentShorthand": true, "checkKeyMustBeforeSpread": true, "warnOnDuplicates": true }], - "react/no-multi-comp": "error", - "react-hooks/rules-of-hooks": "error", "react-hooks/exhaustive-deps": [ "warn", { @@ -50,9 +42,6 @@ "node": { "extensions": [".js", ".ts", ".tsx"] } - }, - "react": { - "version": "detect" } }, "env": { @@ -63,7 +52,6 @@ { "files": ["**/*.ts", "**/*.tsx"], "extends": ["@rocket.chat/eslint-config"], - "plugins": ["react", "react-hooks"], "rules": { "@typescript-eslint/naming-convention": [ "error", @@ -136,14 +124,6 @@ } ], "prettier/prettier": 2, - "react/display-name": "error", - "react/jsx-uses-react": "error", - "react/jsx-uses-vars": "error", - "react/jsx-no-undef": "error", - "react/jsx-fragments": ["error", "syntax"], - "react/jsx-key": ["error", { "checkFragmentShorthand": true, "checkKeyMustBeforeSpread": true, "warnOnDuplicates": true }], - "react/no-multi-comp": "error", - "react-hooks/rules-of-hooks": "error", "react-hooks/exhaustive-deps": [ "warn", { @@ -160,9 +140,6 @@ "node": { "extensions": [".js", ".ts", ".tsx"] } - }, - "react": { - "version": "detect" } } }, diff --git a/apps/meteor/client/components/CreateDiscussion/DefaultParentRoomField.tsx b/apps/meteor/client/components/CreateDiscussion/DefaultParentRoomField.tsx index 88de1d35051f..0a2717a65552 100644 --- a/apps/meteor/client/components/CreateDiscussion/DefaultParentRoomField.tsx +++ b/apps/meteor/client/components/CreateDiscussion/DefaultParentRoomField.tsx @@ -23,7 +23,7 @@ const DefaultParentRoomField = ({ defaultParentRoom }: { defaultParentRoom: stri } if (!value || !value.room) { - return {t('Error')}; + return {t('Error')}; } return ( diff --git a/apps/meteor/client/components/InfoPanel/InfoPanel.stories.tsx b/apps/meteor/client/components/InfoPanel/InfoPanel.stories.tsx index 9d29007f9b90..4e8e44b1f932 100644 --- a/apps/meteor/client/components/InfoPanel/InfoPanel.stories.tsx +++ b/apps/meteor/client/components/InfoPanel/InfoPanel.stories.tsx @@ -24,7 +24,7 @@ export const Default: ComponentStory = () => ( - + diff --git a/apps/meteor/client/components/Omnichannel/modals/ForwardChatModal.tsx b/apps/meteor/client/components/Omnichannel/modals/ForwardChatModal.tsx index 9ff01a4b7d6c..4754e0bcbd63 100644 --- a/apps/meteor/client/components/Omnichannel/modals/ForwardChatModal.tsx +++ b/apps/meteor/client/components/Omnichannel/modals/ForwardChatModal.tsx @@ -101,24 +101,22 @@ const ForwardChatModal = ({ - + {t('Forward_to_department')} - { - { - setValue('department', value); - }} - flexGrow={1} - endReached={endReached} - /> - } + { + setValue('department', value); + }} + flexGrow={1} + endReached={endReached} + /> diff --git a/apps/meteor/client/components/message/content/urlPreviews/OEmbedPreviewContent.tsx b/apps/meteor/client/components/message/content/urlPreviews/OEmbedPreviewContent.tsx index 2955a1eb5b39..97a01c552523 100644 --- a/apps/meteor/client/components/message/content/urlPreviews/OEmbedPreviewContent.tsx +++ b/apps/meteor/client/components/message/content/urlPreviews/OEmbedPreviewContent.tsx @@ -39,7 +39,7 @@ const OEmbedPreviewContent = ({ {showSiteName && } - {showFooterSeparator && {'|'}} + {showFooterSeparator && |} {showAuthorName && } diff --git a/apps/meteor/client/components/message/content/urlPreviews/UrlImagePreview.tsx b/apps/meteor/client/components/message/content/urlPreviews/UrlImagePreview.tsx index 1227faee6eeb..f56adef8f12d 100644 --- a/apps/meteor/client/components/message/content/urlPreviews/UrlImagePreview.tsx +++ b/apps/meteor/client/components/message/content/urlPreviews/UrlImagePreview.tsx @@ -9,7 +9,7 @@ const UrlImagePreview = ({ url }: Pick): ReactElement const { maxHeight: oembedMaxHeight } = useOembedLayout(); return ( - + ); diff --git a/apps/meteor/client/sidebar/Item/Condensed.stories.tsx b/apps/meteor/client/sidebar/Item/Condensed.stories.tsx index bda4d9769276..915c4646f082 100644 --- a/apps/meteor/client/sidebar/Item/Condensed.stories.tsx +++ b/apps/meteor/client/sidebar/Item/Condensed.stories.tsx @@ -26,7 +26,11 @@ export default { const Template: ComponentStory = (args) => ( {}} + titleIcon={ + + + + } avatar={} /> ); diff --git a/apps/meteor/client/sidebar/Item/Condensed.tsx b/apps/meteor/client/sidebar/Item/Condensed.tsx index 9a8988a6ed0c..527d38a0879c 100644 --- a/apps/meteor/client/sidebar/Item/Condensed.tsx +++ b/apps/meteor/client/sidebar/Item/Condensed.tsx @@ -48,7 +48,11 @@ const Condensed: FC = ({ icon, title = '', avatar, actions, href )} - {actions && {{actions}}} + {actions && ( + + {actions} + + )} ); }; diff --git a/apps/meteor/client/sidebar/Item/Extended.stories.tsx b/apps/meteor/client/sidebar/Item/Extended.stories.tsx index b621ea3396b4..04c993bc963c 100644 --- a/apps/meteor/client/sidebar/Item/Extended.stories.tsx +++ b/apps/meteor/client/sidebar/Item/Extended.stories.tsx @@ -51,7 +51,11 @@ const Template: ComponentStory = (args) => ( } - titleIcon={{}} + titleIcon={ + + + + } avatar={} /> ); diff --git a/apps/meteor/client/sidebar/Item/Extended.tsx b/apps/meteor/client/sidebar/Item/Extended.tsx index a23b55ff10de..c896037d7f3d 100644 --- a/apps/meteor/client/sidebar/Item/Extended.tsx +++ b/apps/meteor/client/sidebar/Item/Extended.tsx @@ -78,7 +78,11 @@ const Extended: VFC = ({ - {actions && {{actions}}} + {actions && ( + + {actions} + + )} ); }; diff --git a/apps/meteor/client/sidebar/Item/Medium.stories.tsx b/apps/meteor/client/sidebar/Item/Medium.stories.tsx index 9fe712d95f87..d7698356f1b1 100644 --- a/apps/meteor/client/sidebar/Item/Medium.stories.tsx +++ b/apps/meteor/client/sidebar/Item/Medium.stories.tsx @@ -26,7 +26,11 @@ export default { const Template: ComponentStory = (args) => ( {}} + titleIcon={ + + + + } avatar={} /> ); diff --git a/apps/meteor/client/sidebar/Item/Medium.tsx b/apps/meteor/client/sidebar/Item/Medium.tsx index 16de09d151f8..2c97b890988f 100644 --- a/apps/meteor/client/sidebar/Item/Medium.tsx +++ b/apps/meteor/client/sidebar/Item/Medium.tsx @@ -46,7 +46,11 @@ const Medium: VFC = ({ icon, title = '', avatar, actions, href, bad )} - {actions && {{actions}}} + {actions && ( + + {actions} + + )} ); }; diff --git a/apps/meteor/client/sidebar/header/MatrixFederationSearch/FederatedRoomListItem.tsx b/apps/meteor/client/sidebar/header/MatrixFederationSearch/FederatedRoomListItem.tsx index 6afe313f636d..90d7885b4a62 100644 --- a/apps/meteor/client/sidebar/header/MatrixFederationSearch/FederatedRoomListItem.tsx +++ b/apps/meteor/client/sidebar/header/MatrixFederationSearch/FederatedRoomListItem.tsx @@ -39,7 +39,7 @@ const FederatedRoomListItem: VFC = ({ {/* Currently canJoin is only false when the ammount of members is too big. This property will be used in the future in case the matrix room is knock only. When that happens, the check for this should be based on the limit setting. */} {!canJoin && ( - + {t('Cant_join')} )} diff --git a/apps/meteor/client/sidebar/header/MatrixFederationSearch/MatrixFederationManageServerModal.tsx b/apps/meteor/client/sidebar/header/MatrixFederationSearch/MatrixFederationManageServerModal.tsx index 5dff5a642ec1..56752d5c3a68 100644 --- a/apps/meteor/client/sidebar/header/MatrixFederationSearch/MatrixFederationManageServerModal.tsx +++ b/apps/meteor/client/sidebar/header/MatrixFederationSearch/MatrixFederationManageServerModal.tsx @@ -56,7 +56,7 @@ const MatrixFederationAddServerModal: VFC = const { data, isLoading: isLoadingServerList } = useMatrixServerList(); return ( - + {t('Manage_servers')} diff --git a/apps/meteor/client/sidebar/sections/actions/OmnichannelLivechatToggle.tsx b/apps/meteor/client/sidebar/sections/actions/OmnichannelLivechatToggle.tsx index bf5867df254c..c0abb70b230d 100644 --- a/apps/meteor/client/sidebar/sections/actions/OmnichannelLivechatToggle.tsx +++ b/apps/meteor/client/sidebar/sections/actions/OmnichannelLivechatToggle.tsx @@ -23,7 +23,7 @@ export const OmnichannelLivechatToggle = (props: Omit { const headers = useMemo( () => [ - {t('API_Personal_Access_Token_Name')}, - isMedium && {t('Created_at')}, - {t('Last_token_part')}, - {t('Two Factor Authentication')}, - , + {t('API_Personal_Access_Token_Name')}, + isMedium && {t('Created_at')}, + {t('Last_token_part')}, + {t('Two Factor Authentication')}, + , ].filter(Boolean), [isMedium, t], ); diff --git a/apps/meteor/client/views/admin/cloud/modals/RegisterWorkspaceSetupModal/RegisterWorkspaceSetupStepOneModal.tsx b/apps/meteor/client/views/admin/cloud/modals/RegisterWorkspaceSetupModal/RegisterWorkspaceSetupStepOneModal.tsx index 1f00584c0b64..09548b3349a9 100644 --- a/apps/meteor/client/views/admin/cloud/modals/RegisterWorkspaceSetupModal/RegisterWorkspaceSetupStepOneModal.tsx +++ b/apps/meteor/client/views/admin/cloud/modals/RegisterWorkspaceSetupModal/RegisterWorkspaceSetupStepOneModal.tsx @@ -90,7 +90,7 @@ const RegisterWorkspaceSetupStepOneModal = ({ I agree with Terms and Conditions - and {''} + and Privacy Policy diff --git a/apps/meteor/client/views/admin/moderation/ModerationConsoleTable.tsx b/apps/meteor/client/views/admin/moderation/ModerationConsoleTable.tsx index c318f9fb8a3e..0c826fd1f176 100644 --- a/apps/meteor/client/views/admin/moderation/ModerationConsoleTable.tsx +++ b/apps/meteor/client/views/admin/moderation/ModerationConsoleTable.tsx @@ -74,7 +74,7 @@ const ModerationConsoleTable: FC = () => { const headers = useMemo( () => [ { ), { > {t('Moderation_Reported_message')} , - + {t('Room')} , - + {t('Moderation_Report_date')} , - + {t('Moderation_Report_plural')} , - , + , ], [sortDirection, sortBy, setSort, t, isDesktopOrLarger], ); diff --git a/apps/meteor/client/views/admin/permissions/CustomRoleUpsellModal.tsx b/apps/meteor/client/views/admin/permissions/CustomRoleUpsellModal.tsx index bbe841226bed..91224cd8ed3b 100644 --- a/apps/meteor/client/views/admin/permissions/CustomRoleUpsellModal.tsx +++ b/apps/meteor/client/views/admin/permissions/CustomRoleUpsellModal.tsx @@ -25,7 +25,7 @@ const CustomRoleUpsellModal: VFC = ({ onClose }) => variant='warning' icon={null} > - + {t('Custom_roles_upsell_add_custom_roles_workspace')} diff --git a/apps/meteor/client/views/admin/permissions/RoleForm.tsx b/apps/meteor/client/views/admin/permissions/RoleForm.tsx index 6fcae6b620b2..346b1064db1f 100644 --- a/apps/meteor/client/views/admin/permissions/RoleForm.tsx +++ b/apps/meteor/client/views/admin/permissions/RoleForm.tsx @@ -42,7 +42,7 @@ const RoleForm = ({ className, editing = false, isProtected = false, isDisabled - {'Leave the description field blank if you dont want to show the role'} + Leave the description field blank if you dont want to show the role {t('Scope')} diff --git a/apps/meteor/client/views/admin/rooms/RoomsTable.tsx b/apps/meteor/client/views/admin/rooms/RoomsTable.tsx index 0c36105d89ba..0f7d02bea814 100644 --- a/apps/meteor/client/views/admin/rooms/RoomsTable.tsx +++ b/apps/meteor/client/views/admin/rooms/RoomsTable.tsx @@ -119,14 +119,14 @@ const RoomsTable = ({ reload }: { reload: MutableRefObject<() => void> }): React const headers = useMemo( () => [ - + {t('Name')} , - + {t('Type')} , void> }): React {t('Users')} , mediaQuery && ( - + {t('Msgs')} ), mediaQuery && ( void> }): React ), mediaQuery && ( diff --git a/apps/meteor/client/views/admin/settings/groups/voip/VoipExtensionsPage.tsx b/apps/meteor/client/views/admin/settings/groups/voip/VoipExtensionsPage.tsx index a27ea9ac1564..978cd02a7f08 100644 --- a/apps/meteor/client/views/admin/settings/groups/voip/VoipExtensionsPage.tsx +++ b/apps/meteor/client/views/admin/settings/groups/voip/VoipExtensionsPage.tsx @@ -89,7 +89,7 @@ const VoipExtensionsPage = () => { {username ? ( - + diff --git a/apps/meteor/client/views/directory/tabs/users/UsersTable/UsersTable.tsx b/apps/meteor/client/views/directory/tabs/users/UsersTable/UsersTable.tsx index 768ba65a9b77..c67d408aa2a3 100644 --- a/apps/meteor/client/views/directory/tabs/users/UsersTable/UsersTable.tsx +++ b/apps/meteor/client/views/directory/tabs/users/UsersTable/UsersTable.tsx @@ -36,12 +36,12 @@ const UsersTable = ({ workspace = 'local' }): ReactElement => { const headers = useMemo( () => [ - + {t('Name')} , mediaQuery && canViewFullOtherUserInfo && ( = ({ size='x124' /> - {'Calling...'} + Calling... { className='rcx-message__avatar' size='x124' /> -

    {'Call Ended!'}

    +

    Call Ended!

    void }): ReactElement => { return ( <> - - + + ); }; diff --git a/apps/meteor/client/views/omnichannel/contactHistory/MessageList/ContactHistoryMessage.tsx b/apps/meteor/client/views/omnichannel/contactHistory/MessageList/ContactHistoryMessage.tsx index 7978903afe9c..88cd8ebdf0eb 100644 --- a/apps/meteor/client/views/omnichannel/contactHistory/MessageList/ContactHistoryMessage.tsx +++ b/apps/meteor/client/views/omnichannel/contactHistory/MessageList/ContactHistoryMessage.tsx @@ -51,7 +51,7 @@ const ContactHistoryMessage: FC<{ @@ -79,7 +79,7 @@ const ContactHistoryMessage: FC<{ diff --git a/apps/meteor/client/views/omnichannel/departments/EditDepartment.tsx b/apps/meteor/client/views/omnichannel/departments/EditDepartment.tsx index 6cb32e877397..a5821382ac5b 100644 --- a/apps/meteor/client/views/omnichannel/departments/EditDepartment.tsx +++ b/apps/meteor/client/views/omnichannel/departments/EditDepartment.tsx @@ -343,7 +343,7 @@ function EditDepartment({ data, id, title, allowedToForwardData }: EditDepartmen )} @@ -360,7 +360,7 @@ function EditDepartment({ data, id, title, allowedToForwardData }: EditDepartmen )} @@ -377,7 +377,7 @@ function EditDepartment({ data, id, title, allowedToForwardData }: EditDepartmen )} @@ -394,8 +394,8 @@ function EditDepartment({ data, id, title, allowedToForwardData }: EditDepartmen )} /> @@ -412,7 +412,7 @@ function EditDepartment({ data, id, title, allowedToForwardData }: EditDepartmen departmentId={id ?? ''} value={value} handler={onChange} - label={'List_of_departments_for_forward'} + label='List_of_departments_for_forward' /> )} /> diff --git a/apps/meteor/client/views/omnichannel/directory/ChatsContextualBar.tsx b/apps/meteor/client/views/omnichannel/directory/ChatsContextualBar.tsx index aa92b72310eb..af02cc59d64f 100644 --- a/apps/meteor/client/views/omnichannel/directory/ChatsContextualBar.tsx +++ b/apps/meteor/client/views/omnichannel/directory/ChatsContextualBar.tsx @@ -62,7 +62,7 @@ const ChatsContextualBar: FC<{ chatReload?: () => void }> = ({ chatReload }) => <> {t('Room_Info')} - + )} {bar === 'edit' && ( diff --git a/apps/meteor/client/views/omnichannel/directory/ContactContextualBar.tsx b/apps/meteor/client/views/omnichannel/directory/ContactContextualBar.tsx index 6a51b6fa4b03..d31648aeaae2 100644 --- a/apps/meteor/client/views/omnichannel/directory/ContactContextualBar.tsx +++ b/apps/meteor/client/views/omnichannel/directory/ContactContextualBar.tsx @@ -38,7 +38,7 @@ const ContactContextualBar = () => { const header = useMemo(() => HEADER_OPTIONS[bar] || HEADER_OPTIONS.info, [bar]); return ( - + {t(header.title)} diff --git a/apps/meteor/client/views/omnichannel/directory/calls/CallTable.tsx b/apps/meteor/client/views/omnichannel/directory/calls/CallTable.tsx index 5aa474893476..89ae577f4bb8 100644 --- a/apps/meteor/client/views/omnichannel/directory/calls/CallTable.tsx +++ b/apps/meteor/client/views/omnichannel/directory/calls/CallTable.tsx @@ -60,14 +60,14 @@ const CallTable = () => { {t('Phone')} - + {t('Queue')} - + {t('Started_At')} = ({ slots: parentSl start: (!!isMobile || currentRouteName === 'omnichannel-directory' || currentRouteName === 'omnichannel-current-chats') && ( {isMobile && } - {} + ), posContent: , diff --git a/apps/meteor/client/views/room/RoomOpener.tsx b/apps/meteor/client/views/room/RoomOpener.tsx index fc7eba8d7f15..43c2bbed9027 100644 --- a/apps/meteor/client/views/room/RoomOpener.tsx +++ b/apps/meteor/client/views/room/RoomOpener.tsx @@ -29,7 +29,11 @@ const RoomOpener = ({ type, reference }: RoomOpenerProps): ReactElement => { return ( }> {isLoading && } - {isSuccess && {}} + {isSuccess && ( + + + + )} {isError && (() => { if (error instanceof RoomNotFoundError) { diff --git a/apps/meteor/client/views/room/contextualBar/ExportMessages/MailExportForm.tsx b/apps/meteor/client/views/room/contextualBar/ExportMessages/MailExportForm.tsx index 078ea3bd797a..31d327351ae8 100644 --- a/apps/meteor/client/views/room/contextualBar/ExportMessages/MailExportForm.tsx +++ b/apps/meteor/client/views/room/contextualBar/ExportMessages/MailExportForm.tsx @@ -139,7 +139,7 @@ const MailExportForm: FC = ({ onCancel, rid }) => { - {errorMessage && {errorMessage}} + {errorMessage && {errorMessage}} diff --git a/apps/meteor/client/views/room/contextualBar/Info/RoomInfo/RoomInfo.tsx b/apps/meteor/client/views/room/contextualBar/Info/RoomInfo/RoomInfo.tsx index bcfe97d6b816..3cb5d5f5ee58 100644 --- a/apps/meteor/client/views/room/contextualBar/Info/RoomInfo/RoomInfo.tsx +++ b/apps/meteor/client/views/room/contextualBar/Info/RoomInfo/RoomInfo.tsx @@ -77,7 +77,7 @@ const RoomInfo = ({ room, icon, onClickBack, onClickClose, onClickEnterRoom, onC - + {actions} diff --git a/apps/meteor/client/views/room/contextualBar/NotificationPreferences/NotificationPreferencesForm.tsx b/apps/meteor/client/views/room/contextualBar/NotificationPreferences/NotificationPreferencesForm.tsx index 8fa434f55255..7be5f929d12b 100644 --- a/apps/meteor/client/views/room/contextualBar/NotificationPreferences/NotificationPreferencesForm.tsx +++ b/apps/meteor/client/views/room/contextualBar/NotificationPreferences/NotificationPreferencesForm.tsx @@ -111,7 +111,7 @@ const NotificationPreferencesForm = ({ notificationOptions, handlePlaySound }: N )} /> - + { return ( password.length > 0} - passwordRulesHint={''} + passwordRulesHint='' validateUsername={validateUsername} validateEmail={validateEmail} currentStep={currentStep} diff --git a/apps/meteor/client/views/teams/contextualBar/info/TeamsInfo.tsx b/apps/meteor/client/views/teams/contextualBar/info/TeamsInfo.tsx index 1863434fcf4d..501baf796f7d 100644 --- a/apps/meteor/client/views/teams/contextualBar/info/TeamsInfo.tsx +++ b/apps/meteor/client/views/teams/contextualBar/info/TeamsInfo.tsx @@ -134,7 +134,7 @@ const TeamsInfo = ({ - + {actions} @@ -148,7 +148,7 @@ const TeamsInfo = ({ - + @@ -163,21 +163,27 @@ const TeamsInfo = ({ {room.description && ( {t('Description')} - {} + + + )} {room.announcement && ( {t('Announcement')} - {} + + + )} {room.topic && ( {t('Topic')} - {} + + + )} diff --git a/apps/meteor/ee/client/omnichannel/cannedResponses/CannedResponsesTable.tsx b/apps/meteor/ee/client/omnichannel/cannedResponses/CannedResponsesTable.tsx index 8a563e94bcc5..efc64a8a4e09 100644 --- a/apps/meteor/ee/client/omnichannel/cannedResponses/CannedResponsesTable.tsx +++ b/apps/meteor/ee/client/omnichannel/cannedResponses/CannedResponsesTable.tsx @@ -113,7 +113,7 @@ const CannedResponsesTable = () => { {t('Created_by')} {t('Shortcut')} v?.trim() !== '' }} render={({ field: { value, onChange } }): ReactElement => ( void> }) => { > {t('Estimated_wait_time')} - + {t('Remove')} diff --git a/apps/meteor/ee/client/omnichannel/units/UnitsTable.tsx b/apps/meteor/ee/client/omnichannel/units/UnitsTable.tsx index b354e52aa35b..2cc8b55c78f1 100644 --- a/apps/meteor/ee/client/omnichannel/units/UnitsTable.tsx +++ b/apps/meteor/ee/client/omnichannel/units/UnitsTable.tsx @@ -57,11 +57,11 @@ const UnitsTable = ({ reload }: { reload: MutableRefObject<() => void> }) => { ); const headers = ( <> - + {t('Name')} void> }) => { > {t('Visibility')} - + {t('Remove')} diff --git a/apps/meteor/ee/client/views/account/deviceManagement/DeviceManagementAccountTable/DeviceManagementAccountTable.tsx b/apps/meteor/ee/client/views/account/deviceManagement/DeviceManagementAccountTable/DeviceManagementAccountTable.tsx index b3f5fde30912..fe480928e543 100644 --- a/apps/meteor/ee/client/views/account/deviceManagement/DeviceManagementAccountTable/DeviceManagementAccountTable.tsx +++ b/apps/meteor/ee/client/views/account/deviceManagement/DeviceManagementAccountTable/DeviceManagementAccountTable.tsx @@ -36,17 +36,17 @@ const DeviceManagementAccountTable = (): ReactElement => { const headers = useMemo( () => [ - + {t('Client')} , - + {t('OS')} , - + {t('Last_login')} , - mediaQuery && {t('Device_ID')}, - , + mediaQuery && {t('Device_ID')}, + , ], [t, mediaQuery, sortDirection, sortBy, setSort], ); diff --git a/apps/meteor/ee/client/views/admin/deviceManagement/DeviceManagementAdminTable/DeviceManagementAdminTable.tsx b/apps/meteor/ee/client/views/admin/deviceManagement/DeviceManagementAdminTable/DeviceManagementAdminTable.tsx index db1c78f53ced..428242f987d0 100644 --- a/apps/meteor/ee/client/views/admin/deviceManagement/DeviceManagementAdminTable/DeviceManagementAdminTable.tsx +++ b/apps/meteor/ee/client/views/admin/deviceManagement/DeviceManagementAdminTable/DeviceManagementAdminTable.tsx @@ -52,24 +52,24 @@ const DeviceManagementAdminTable = ({ reloadRef }: { reloadRef: MutableRefObject const headers = useMemo( () => [ - + {t('Client')} , - {t('Version')}, - + {t('Version')}, + {t('OS')} , - + {t('User')} , mediaQuery && ( - + {t('Last_login')} ), - mediaQuery && {t('Device_ID')}, - mediaQuery && {t('IP_Address')}, - , + mediaQuery && {t('Device_ID')}, + mediaQuery && {t('IP_Address')}, + , ], [t, mediaQuery, setSort, sortDirection, sortBy], ); diff --git a/apps/meteor/ee/client/views/admin/engagementDashboard/channels/ChannelsOverview.tsx b/apps/meteor/ee/client/views/admin/engagementDashboard/channels/ChannelsOverview.tsx index 83c208dd0b2e..e34c34d62816 100644 --- a/apps/meteor/ee/client/views/admin/engagementDashboard/channels/ChannelsOverview.tsx +++ b/apps/meteor/ee/client/views/admin/engagementDashboard/channels/ChannelsOverview.tsx @@ -69,7 +69,7 @@ const ChannelsOverview = (): ReactElement => { - {'#'} + # {t('Channel')} {t('Created')} {t('Last_active')} diff --git a/apps/meteor/ee/client/views/admin/engagementDashboard/messages/MessagesPerChannelSection.tsx b/apps/meteor/ee/client/views/admin/engagementDashboard/messages/MessagesPerChannelSection.tsx index d151484116f0..1a7c0a0aeeee 100644 --- a/apps/meteor/ee/client/views/admin/engagementDashboard/messages/MessagesPerChannelSection.tsx +++ b/apps/meteor/ee/client/views/admin/engagementDashboard/messages/MessagesPerChannelSection.tsx @@ -182,7 +182,7 @@ const MessagesPerChannelSection = (): ReactElement => {
    - {'#'} + # {t('Channel')} {t('Number_of_messages')} diff --git a/apps/meteor/tests/e2e/.eslintrc.json b/apps/meteor/tests/e2e/.eslintrc.json index df41734e7908..f22ce1e25a79 100644 --- a/apps/meteor/tests/e2e/.eslintrc.json +++ b/apps/meteor/tests/e2e/.eslintrc.json @@ -1,8 +1,8 @@ { "root": true, - "extends": ["@rocket.chat/eslint-config/original", "prettier", "plugin:@typescript-eslint/recommended"], + "extends": ["@rocket.chat/eslint-config/original", "@rocket.chat/eslint-config/react", "prettier", "plugin:@typescript-eslint/recommended"], "parser": "@typescript-eslint/parser", - "plugins": ["react", "react-hooks", "prettier", "testing-library", "anti-trojan-source", "no-floating-promise"], + "plugins": ["prettier", "testing-library", "anti-trojan-source", "no-floating-promise"], "rules": { "@typescript-eslint/no-unused-vars": [ "error", diff --git a/ee/packages/pdf-worker/.eslintrc.json b/ee/packages/pdf-worker/.eslintrc.json index ea50ca58bed9..98b4582994d2 100644 --- a/ee/packages/pdf-worker/.eslintrc.json +++ b/ee/packages/pdf-worker/.eslintrc.json @@ -1,5 +1,5 @@ { - "extends": ["@rocket.chat/eslint-config"], + "extends": ["@rocket.chat/eslint-config", "@rocket.chat/eslint-config/react"], "parser": "@typescript-eslint/parser", "ignorePatterns": ["**/dist"] } diff --git a/ee/packages/ui-theming/.eslintrc.json b/ee/packages/ui-theming/.eslintrc.json index a83aeda48e66..4c413c4080b3 100644 --- a/ee/packages/ui-theming/.eslintrc.json +++ b/ee/packages/ui-theming/.eslintrc.json @@ -1,4 +1,4 @@ { - "extends": ["@rocket.chat/eslint-config"], + "extends": ["@rocket.chat/eslint-config", "@rocket.chat/eslint-config/react"], "ignorePatterns": ["**/dist"] } diff --git a/ee/packages/ui-theming/src/PaletteStyleTag.tsx b/ee/packages/ui-theming/src/PaletteStyleTag.tsx index a63e2a20b766..8aa3f2c04ce2 100644 --- a/ee/packages/ui-theming/src/PaletteStyleTag.tsx +++ b/ee/packages/ui-theming/src/PaletteStyleTag.tsx @@ -1,4 +1,3 @@ -import type { ReactElement } from 'react'; import { memo } from 'react'; import { createPortal } from 'react-dom'; @@ -9,7 +8,7 @@ import { darkPalette } from './paletteDark'; import { useThemeMode } from './hooks/useThemeMode'; import { useCreateStyleContainer } from './hooks/useCreateStyleContainer'; -export const PaletteStyleTag = memo((): ReactElement | null => { +export const PaletteStyleTag = memo(function PaletteStyleTag() { const [, , theme] = useThemeMode(); const palette = diff --git a/ee/packages/ui-theming/src/SidebarPaletteStyleTag.tsx b/ee/packages/ui-theming/src/SidebarPaletteStyleTag.tsx index 02b9ee090a49..9af4d3e80f7b 100644 --- a/ee/packages/ui-theming/src/SidebarPaletteStyleTag.tsx +++ b/ee/packages/ui-theming/src/SidebarPaletteStyleTag.tsx @@ -7,7 +7,7 @@ import { darkPalette } from './paletteDark'; import { convertToCss } from './helpers/convertToCss'; import { useCreateStyleContainer } from './hooks/useCreateStyleContainer'; -export const SidebarPaletteStyleTag = memo((): ReactElement | null => { +export const SidebarPaletteStyleTag = memo(function SidebarPaletteStyleTag(): ReactElement | null { // Commented code below: sidebar palette currently the same in both themes. // const [, , theme] = useThemeMode(); diff --git a/ee/packages/ui-theming/src/hooks/useThemeMode.ts b/ee/packages/ui-theming/src/hooks/useThemeMode.ts index 84deebb28374..a467345f810a 100644 --- a/ee/packages/ui-theming/src/hooks/useThemeMode.ts +++ b/ee/packages/ui-theming/src/hooks/useThemeMode.ts @@ -1,6 +1,6 @@ import { useDarkMode } from '@rocket.chat/fuselage-hooks'; import { useEndpoint, useUserPreference } from '@rocket.chat/ui-contexts'; -import { useCallback } from 'react'; +import { useCallback, useState } from 'react'; type ThemeMode = 'light' | 'dark' | 'auto'; @@ -15,8 +15,15 @@ export const useThemeMode = (): [ThemeMode, (value: ThemeMode) => () => void, 'l const saveUserPreferences = useEndpoint('POST', '/v1/users.setPreferences'); - const setTheme = (value: ThemeMode): (() => void) => - useCallback(() => saveUserPreferences({ data: { themeAppearence: value } }), [value]); + const [updaters] = useState( + (): Record void> => ({ + light: () => saveUserPreferences({ data: { themeAppearence: 'light' } }), + dark: () => saveUserPreferences({ data: { themeAppearence: 'dark' } }), + auto: () => saveUserPreferences({ data: { themeAppearence: 'auto' } }), + }), + ); + + const setTheme = useCallback((value: ThemeMode): (() => void) => updaters[value], [updaters]); return [theme, setTheme, useDarkMode(theme === 'auto' ? undefined : theme === 'dark') ? 'dark' : 'light']; }; diff --git a/package.json b/package.json index a47523eefcea..0465f5ff5607 100644 --- a/package.json +++ b/package.json @@ -21,7 +21,7 @@ "@types/chart.js": "^2.9.37", "@types/js-yaml": "^4.0.5", "husky": "^7.0.4", - "turbo": "~1.10.7" + "turbo": "latest" }, "workspaces": [ "apps/*", diff --git a/packages/eslint-config/react.js b/packages/eslint-config/react.js new file mode 100644 index 000000000000..84a784cb7733 --- /dev/null +++ b/packages/eslint-config/react.js @@ -0,0 +1,32 @@ +/** @type {import('eslint').ESLint.ConfigData} */ +const config = { + plugins: ['react', 'react-hooks'], + rules: { + 'react-hooks/exhaustive-deps': 'error', + 'react-hooks/rules-of-hooks': 'error', + 'react/display-name': 'error', + 'react/jsx-curly-brace-presence': 'error', + 'react/jsx-fragments': ['error', 'syntax'], + 'react/jsx-key': ['error', { checkFragmentShorthand: true, checkKeyMustBeforeSpread: true, warnOnDuplicates: true }], + 'react/jsx-no-undef': 'error', + 'react/jsx-uses-react': 'error', + 'react/jsx-uses-vars': 'error', + 'react/no-multi-comp': 'error', + }, + settings: { + react: { + version: 'detect', + }, + }, + overrides: [ + { + files: ['**/*.stories.js', '**/*.stories.jsx', '**/*.stories.ts', '**/*.stories.tsx', '**/*.spec.tsx'], + rules: { + 'react/display-name': 'off', + 'react/no-multi-comp': 'off', + }, + }, + ], +}; + +module.exports = config; diff --git a/packages/fuselage-ui-kit/.eslintrc.js b/packages/fuselage-ui-kit/.eslintrc.js index 72e21b1a70e0..07c7ef53e979 100644 --- a/packages/fuselage-ui-kit/.eslintrc.js +++ b/packages/fuselage-ui-kit/.eslintrc.js @@ -1,3 +1,6 @@ -module.exports = { - extends: '@rocket.chat/eslint-config', +/** @type {import('eslint').ESLint.ConfigData} */ +const config = { + extends: ['@rocket.chat/eslint-config', '@rocket.chat/eslint-config/react'], }; + +module.exports = config; diff --git a/packages/gazzodown/.eslintrc.json b/packages/gazzodown/.eslintrc.json index f51d169592b8..35f767f96c01 100644 --- a/packages/gazzodown/.eslintrc.json +++ b/packages/gazzodown/.eslintrc.json @@ -3,6 +3,7 @@ "plugin:@typescript-eslint/recommended", "plugin:@typescript-eslint/eslint-recommended", "@rocket.chat/eslint-config/original", + "@rocket.chat/eslint-config/react", "prettier", "plugin:anti-trojan-source/recommended", "plugin:react/jsx-runtime", diff --git a/packages/mock-providers/.eslintrc.json b/packages/mock-providers/.eslintrc.json index a83aeda48e66..4c413c4080b3 100644 --- a/packages/mock-providers/.eslintrc.json +++ b/packages/mock-providers/.eslintrc.json @@ -1,4 +1,4 @@ { - "extends": ["@rocket.chat/eslint-config"], + "extends": ["@rocket.chat/eslint-config", "@rocket.chat/eslint-config/react"], "ignorePatterns": ["**/dist"] } diff --git a/packages/ui-client/.eslintrc.json b/packages/ui-client/.eslintrc.json index ccfb12f9b975..d5db8560f3e8 100644 --- a/packages/ui-client/.eslintrc.json +++ b/packages/ui-client/.eslintrc.json @@ -3,13 +3,14 @@ "plugin:@typescript-eslint/recommended", "plugin:@typescript-eslint/eslint-recommended", "@rocket.chat/eslint-config/original", + "@rocket.chat/eslint-config/react", "prettier", "plugin:anti-trojan-source/recommended", "plugin:react/jsx-runtime", "plugin:storybook/recommended" ], "parser": "@typescript-eslint/parser", - "plugins": ["@typescript-eslint", "react", "react-hooks", "prettier"], + "plugins": ["@typescript-eslint", "prettier"], "rules": { "func-call-spacing": "off", "import/named": "error", @@ -33,23 +34,13 @@ "no-useless-constructor": "off", "no-use-before-define": "off", "prefer-arrow-callback": ["error", { "allowNamedFunctions": true }], - "prettier/prettier": 2, - "react/display-name": "error", - "react/jsx-uses-vars": "error", - "react/jsx-no-undef": "error", - "react/jsx-fragments": ["error", "syntax"], - "react/no-multi-comp": "error", - "react-hooks/rules-of-hooks": "error", - "react-hooks/exhaustive-deps": "warn" + "prettier/prettier": 2 }, "settings": { "import/resolver": { "node": { "extensions": [".js", ".ts", ".tsx"] } - }, - "react": { - "version": "detect" } }, "ignorePatterns": ["**/dist"], @@ -69,12 +60,6 @@ ], "@typescript-eslint/prefer-optional-chain": "warn" } - }, - { - "files": ["*.stories.tsx"], - "rules": { - "react/no-multi-comp": "off" - } - } + } ] } diff --git a/packages/ui-composer/.eslintrc.json b/packages/ui-composer/.eslintrc.json index 1fc7e5497093..3a40997760d0 100644 --- a/packages/ui-composer/.eslintrc.json +++ b/packages/ui-composer/.eslintrc.json @@ -3,13 +3,14 @@ "plugin:@typescript-eslint/recommended", "plugin:@typescript-eslint/eslint-recommended", "@rocket.chat/eslint-config/original", + "@rocket.chat/eslint-config/react", "prettier", "plugin:anti-trojan-source/recommended", "plugin:react/jsx-runtime", "plugin:storybook/recommended" ], "parser": "@typescript-eslint/parser", - "plugins": ["@typescript-eslint", "react", "react-hooks", "prettier"], + "plugins": ["@typescript-eslint", "prettier"], "rules": { "func-call-spacing": "off", "import/named": "error", @@ -33,23 +34,13 @@ "no-useless-constructor": "off", "no-use-before-define": "off", "prefer-arrow-callback": ["error", { "allowNamedFunctions": true }], - "prettier/prettier": 2, - "react/display-name": "error", - "react/jsx-uses-vars": "error", - "react/jsx-no-undef": "error", - "react/jsx-fragments": ["error", "syntax"], - "react/no-multi-comp": "error", - "react-hooks/rules-of-hooks": "error", - "react-hooks/exhaustive-deps": "warn" + "prettier/prettier": 2 }, "settings": { "import/resolver": { "node": { "extensions": [".js", ".ts", ".tsx"] } - }, - "react": { - "version": "detect" } }, "ignorePatterns": ["**/dist"], @@ -70,12 +61,6 @@ ], "@typescript-eslint/prefer-optional-chain": "warn" } - }, - { - "files": ["*.stories.tsx"], - "rules": { - "react/no-multi-comp": "off" - } } ] } diff --git a/packages/ui-contexts/.eslintrc.json b/packages/ui-contexts/.eslintrc.json index 5b4e017cb820..5fe546755bb7 100644 --- a/packages/ui-contexts/.eslintrc.json +++ b/packages/ui-contexts/.eslintrc.json @@ -1,4 +1,4 @@ { - "extends": ["@rocket.chat/eslint-config", "plugin:react-hooks/recommended"], + "extends": ["@rocket.chat/eslint-config", "@rocket.chat/eslint-config/react", "plugin:react-hooks/recommended"], "ignorePatterns": ["**/dist"] } diff --git a/packages/ui-video-conf/.eslintrc.json b/packages/ui-video-conf/.eslintrc.json index 662dc3790993..cbaa27e73b96 100644 --- a/packages/ui-video-conf/.eslintrc.json +++ b/packages/ui-video-conf/.eslintrc.json @@ -3,13 +3,14 @@ "plugin:@typescript-eslint/recommended", "plugin:@typescript-eslint/eslint-recommended", "@rocket.chat/eslint-config/original", + "@rocket.chat/eslint-config/react", "prettier", "plugin:anti-trojan-source/recommended", "plugin:react/jsx-runtime", "plugin:storybook/recommended" ], "parser": "@typescript-eslint/parser", - "plugins": ["@typescript-eslint", "react", "react-hooks", "prettier"], + "plugins": ["@typescript-eslint", "prettier"], "rules": { "func-call-spacing": "off", "import/named": "error", @@ -33,23 +34,13 @@ "no-useless-constructor": "off", "no-use-before-define": "off", "prefer-arrow-callback": ["error", { "allowNamedFunctions": true }], - "prettier/prettier": 2, - "react/display-name": "error", - "react/jsx-uses-vars": "error", - "react/jsx-no-undef": "error", - "react/jsx-fragments": ["error", "syntax"], - "react/no-multi-comp": "error", - "react-hooks/rules-of-hooks": "error", - "react-hooks/exhaustive-deps": "warn" + "prettier/prettier": 2 }, "settings": { "import/resolver": { "node": { "extensions": [".js", ".ts", ".tsx"] } - }, - "react": { - "version": "detect" } }, "ignorePatterns": ["**/dist"], @@ -70,12 +61,6 @@ ], "@typescript-eslint/prefer-optional-chain": "warn" } - }, - { - "files": ["*.stories.tsx"], - "rules": { - "react/no-multi-comp": "off" - } - } + } ] } diff --git a/packages/web-ui-registration/.eslintrc.json b/packages/web-ui-registration/.eslintrc.json index a83aeda48e66..4c413c4080b3 100644 --- a/packages/web-ui-registration/.eslintrc.json +++ b/packages/web-ui-registration/.eslintrc.json @@ -1,4 +1,4 @@ { - "extends": ["@rocket.chat/eslint-config"], + "extends": ["@rocket.chat/eslint-config", "@rocket.chat/eslint-config/react"], "ignorePatterns": ["**/dist"] } diff --git a/packages/web-ui-registration/src/components/LoginSwitchLanguageFooter.tsx b/packages/web-ui-registration/src/components/LoginSwitchLanguageFooter.tsx index 2c81b79f8694..9a23039f9336 100644 --- a/packages/web-ui-registration/src/components/LoginSwitchLanguageFooter.tsx +++ b/packages/web-ui-registration/src/components/LoginSwitchLanguageFooter.tsx @@ -29,7 +29,7 @@ const LoginSwitchLanguageFooter = (): ReactElement | null => { return Array.from(potentialSuggestions).filter( (language) => language && language !== currentLanguage && Boolean(languages.find(({ key }) => key === language)), ); - }, [serverLanguage, browserLanguage, currentLanguage]); + }, [serverLanguage, currentLanguage, languages]); const handleSwitchLanguageClick = (language: string) => (): void => { loadLanguage(language); diff --git a/yarn.lock b/yarn.lock index f9b00db29e95..ecde951e5d8b 100644 --- a/yarn.lock +++ b/yarn.lock @@ -36255,7 +36255,7 @@ __metadata: "@types/chart.js": ^2.9.37 "@types/js-yaml": ^4.0.5 husky: ^7.0.4 - turbo: ~1.10.7 + turbo: latest languageName: unknown linkType: soft @@ -39671,58 +39671,58 @@ __metadata: languageName: node linkType: hard -"turbo-darwin-64@npm:1.10.7": - version: 1.10.7 - resolution: "turbo-darwin-64@npm:1.10.7" +"turbo-darwin-64@npm:1.10.8": + version: 1.10.8 + resolution: "turbo-darwin-64@npm:1.10.8" conditions: os=darwin & cpu=x64 languageName: node linkType: hard -"turbo-darwin-arm64@npm:1.10.7": - version: 1.10.7 - resolution: "turbo-darwin-arm64@npm:1.10.7" +"turbo-darwin-arm64@npm:1.10.8": + version: 1.10.8 + resolution: "turbo-darwin-arm64@npm:1.10.8" conditions: os=darwin & cpu=arm64 languageName: node linkType: hard -"turbo-linux-64@npm:1.10.7": - version: 1.10.7 - resolution: "turbo-linux-64@npm:1.10.7" +"turbo-linux-64@npm:1.10.8": + version: 1.10.8 + resolution: "turbo-linux-64@npm:1.10.8" conditions: os=linux & cpu=x64 languageName: node linkType: hard -"turbo-linux-arm64@npm:1.10.7": - version: 1.10.7 - resolution: "turbo-linux-arm64@npm:1.10.7" +"turbo-linux-arm64@npm:1.10.8": + version: 1.10.8 + resolution: "turbo-linux-arm64@npm:1.10.8" conditions: os=linux & cpu=arm64 languageName: node linkType: hard -"turbo-windows-64@npm:1.10.7": - version: 1.10.7 - resolution: "turbo-windows-64@npm:1.10.7" +"turbo-windows-64@npm:1.10.8": + version: 1.10.8 + resolution: "turbo-windows-64@npm:1.10.8" conditions: os=win32 & cpu=x64 languageName: node linkType: hard -"turbo-windows-arm64@npm:1.10.7": - version: 1.10.7 - resolution: "turbo-windows-arm64@npm:1.10.7" +"turbo-windows-arm64@npm:1.10.8": + version: 1.10.8 + resolution: "turbo-windows-arm64@npm:1.10.8" conditions: os=win32 & cpu=arm64 languageName: node linkType: hard -"turbo@npm:~1.10.7": - version: 1.10.7 - resolution: "turbo@npm:1.10.7" +"turbo@npm:latest": + version: 1.10.8 + resolution: "turbo@npm:1.10.8" dependencies: - turbo-darwin-64: 1.10.7 - turbo-darwin-arm64: 1.10.7 - turbo-linux-64: 1.10.7 - turbo-linux-arm64: 1.10.7 - turbo-windows-64: 1.10.7 - turbo-windows-arm64: 1.10.7 + turbo-darwin-64: 1.10.8 + turbo-darwin-arm64: 1.10.8 + turbo-linux-64: 1.10.8 + turbo-linux-arm64: 1.10.8 + turbo-windows-64: 1.10.8 + turbo-windows-arm64: 1.10.8 dependenciesMeta: turbo-darwin-64: optional: true @@ -39738,7 +39738,7 @@ __metadata: optional: true bin: turbo: bin/turbo - checksum: 58329caf13b5fef284ccfc21bd1f841023122371d75d2202be809a385aade84fd886cf5c2093323c115c497d503c9c34e1f7ae09e13d06d1dcb4b649883f60dd + checksum: 9fd2f9b17461a688e200329c4d910969e828b22c375e88759cd0e59f96db7aac115d289ed20af42db23030b4a4ae0334d1a37bf0a5caf2fdc8a618476742238f languageName: node linkType: hard From 5b3b7c55e072e4ba1f4c53cff60fba43d4ceb010 Mon Sep 17 00:00:00 2001 From: Hugo Costa Date: Wed, 19 Jul 2023 12:34:04 -0300 Subject: [PATCH 024/342] test: remove unnecessary and flaky e2e test (#29843) --- .../tests/e2e/engagement-dashboard.spec.ts | 38 ------------------- 1 file changed, 38 deletions(-) delete mode 100644 apps/meteor/tests/e2e/engagement-dashboard.spec.ts diff --git a/apps/meteor/tests/e2e/engagement-dashboard.spec.ts b/apps/meteor/tests/e2e/engagement-dashboard.spec.ts deleted file mode 100644 index a6389e62dfa2..000000000000 --- a/apps/meteor/tests/e2e/engagement-dashboard.spec.ts +++ /dev/null @@ -1,38 +0,0 @@ -import { IS_EE } from './config/constants'; -import { Users } from './fixtures/userStates'; -import { test, expect } from './utils/test'; - -test.skip(!IS_EE, 'Engagement Dashboard > Enterprise Only'); - -test.use({ storageState: Users.admin.state }); - -test.describe('engagement-dashboard', () => { - test.beforeEach(async ({ page }) => { - await page.goto('/admin/engagement-dashboard'); - await page.route('**/api/v1/engagement-dashboard/**', (route) => route.abort()); - }); - test('expect to trigger fallback error component', async ({ page }) => { - await test.step('expect to show 4 fallback errors components inside widget at Users Tab', async () => { - await expect(page.locator('role=tab[name="Users"][selected]')).toBeVisible(); - - await page.waitForSelector('[data-qa="EngagementDashboardCardErrorBoundary"]'); - await expect(page.locator('[data-qa="EngagementDashboardCardErrorBoundary"]')).toHaveCount(4); - }); - - await test.step('expect to show 2 fallback errors components inside widget at Messages Tab', async () => { - await page.locator('role=tab[name="Messages"]').click(); - await expect(page.locator('role=tab[name="Messages"][selected]')).toBeVisible(); - - await page.waitForSelector('[data-qa="EngagementDashboardCardErrorBoundary"]'); - await expect(page.locator('[data-qa="EngagementDashboardCardErrorBoundary"]')).toHaveCount(2); - }); - - await test.step('expect to show a fallback error component inside widget at Channels Tab', async () => { - await page.locator('role=tab[name="Channels"]').click(); - await expect(page.locator('role=tab[name="Channels"][selected]')).toBeVisible(); - - await page.waitForSelector('[data-qa="EngagementDashboardCardErrorBoundary"]'); - await expect(page.locator('[data-qa="EngagementDashboardCardErrorBoundary"]')).toBeVisible(); - }); - }); -}); From 4c26133d506e8a2b63436036ee40b83a8ef8d2e1 Mon Sep 17 00:00:00 2001 From: Guilherme Gazzo Date: Wed, 19 Jul 2023 17:22:52 -0300 Subject: [PATCH 025/342] test: use jest as the default unit test for client code (#29864) Co-authored-by: kodiakhq[bot] <49736102+kodiakhq[bot]@users.noreply.github.com> --- apps/meteor/.mocharc.client.js | 12 +- .../variants/ThreadMessagePreview.spec.tsx | 99 --------------- .../variants/room/RoomMessageContent.spec.tsx | 88 ------------- .../hooks/useAutoTranslate.spec.ts | 117 ------------------ .../body/composer/MessageComposer.spec.ts | 14 --- apps/meteor/jest.config.ts | 7 +- ee/packages/api-client/jest.config.ts | 1 + 7 files changed, 3 insertions(+), 335 deletions(-) delete mode 100644 apps/meteor/client/components/message/variants/ThreadMessagePreview.spec.tsx delete mode 100644 apps/meteor/client/components/message/variants/room/RoomMessageContent.spec.tsx delete mode 100644 apps/meteor/client/views/room/MessageList/hooks/useAutoTranslate.spec.ts delete mode 100644 apps/meteor/client/views/room/components/body/composer/MessageComposer.spec.ts diff --git a/apps/meteor/.mocharc.client.js b/apps/meteor/.mocharc.client.js index 029564d7729a..16e771dfbc60 100644 --- a/apps/meteor/.mocharc.client.js +++ b/apps/meteor/.mocharc.client.js @@ -32,15 +32,5 @@ module.exports = { timeout: 5000, exit: false, slow: 200, - spec: [ - 'client/**/*.spec.{ts,tsx}', - 'tests/unit/client/**/*.spec.{ts,tsx}', - 'tests/unit/lib/**/*.tests.ts', - 'tests/unit/client/**/*.test.ts', - ], - exclude: [ - 'client/hooks/*.spec.{ts,tsx}', - 'client/sidebar/header/actions/hooks/*.spec.{ts,tsx}', - 'client/components/message/content/reactions/**.spec.{ts,tsx}', - ], + spec: ['tests/unit/client/**/*.spec.{ts,tsx}', 'tests/unit/lib/**/*.tests.ts', 'tests/unit/client/**/*.test.ts'], }; diff --git a/apps/meteor/client/components/message/variants/ThreadMessagePreview.spec.tsx b/apps/meteor/client/components/message/variants/ThreadMessagePreview.spec.tsx deleted file mode 100644 index e5deb61442a5..000000000000 --- a/apps/meteor/client/components/message/variants/ThreadMessagePreview.spec.tsx +++ /dev/null @@ -1,99 +0,0 @@ -import type { IMessage, IThreadMessage } from '@rocket.chat/core-typings'; -import { QueryClientProvider } from '@tanstack/react-query'; -import { render, screen } from '@testing-library/react'; -import { expect } from 'chai'; -import proxyquire from 'proxyquire'; -import type { ReactNode } from 'react'; -import React from 'react'; - -import FakeRoomProvider from '../../../../tests/mocks/client/FakeRoomProvider'; -import RouterContextMock from '../../../../tests/mocks/client/RouterContextMock'; -import { createFakeMessageWithMd } from '../../../../tests/mocks/data'; -import { queryClient } from '../../../lib/queryClient'; -import type * as ThreadMessagePreviewModule from './ThreadMessagePreview'; - -describe('ThreadMessagePreview', () => { - const fakeMessage = createFakeMessageWithMd({ - msg: 'message', - }); - - const loadMock = (stubs?: Record) => { - return proxyquire.noCallThru().load('./ThreadMessagePreview', { - '../../../views/room/MessageList/hooks/useParentMessage': { - useParentMessage: () => '', - }, - '../../../views/room/MessageList/hooks/useMessageBody': { - useMessageBody: () =>

    Parent Message

    , - }, - '../../../../app/ui-utils/client': { - MessageTypes: { - getType: () => false, - }, - }, - './threadPreview/ThreadMessagePreviewBody': ({ message }: { message: IMessage }) => {message.msg}, - ...stubs, - }).default; - }; - - const ProvidersMock = ({ children }: { children: ReactNode }) => { - return ( - - - {children} - - - ); - }; - - it('should render the message when exists', () => { - const ThreadMessagePreview = loadMock(); - - render(, { wrapper: ProvidersMock }); - - expect(screen.getByText(fakeMessage.msg)).to.exist; - }); - - it('should render ignored message', () => { - const ThreadMessagePreview = loadMock(); - - const message = { ...fakeMessage, ignored: true }; - render(, { wrapper: ProvidersMock }); - - expect(screen.getByText('Message_Ignored')).to.exist; - }); - - it('should render parent message', () => { - const ThreadMessagePreview = loadMock({ - '../../../views/room/MessageList/hooks/useParentMessage': { - useParentMessage: () => ({ - isSuccess: true, - }), - }, - }); - - render(, { wrapper: ProvidersMock }); - - expect(screen.getByText('Parent Message')).to.exist; - }); - - it('should render parent system message', () => { - const ThreadMessagePreview = loadMock({ - '../../../views/room/MessageList/hooks/useParentMessage': { - useParentMessage: () => ({ - isSuccess: true, - }), - }, - '../../../../app/ui-utils/client': { - MessageTypes: { - getType: () => ({ - message: 'System Message', - }), - }, - }, - }); - - render(, { wrapper: ProvidersMock }); - - expect(screen.getByText('System Message')).to.exist; - }); -}); diff --git a/apps/meteor/client/components/message/variants/room/RoomMessageContent.spec.tsx b/apps/meteor/client/components/message/variants/room/RoomMessageContent.spec.tsx deleted file mode 100644 index 49d4426df695..000000000000 --- a/apps/meteor/client/components/message/variants/room/RoomMessageContent.spec.tsx +++ /dev/null @@ -1,88 +0,0 @@ -import { QueryClientProvider } from '@tanstack/react-query'; -import { render, screen } from '@testing-library/react'; -import { expect } from 'chai'; -import proxyquire from 'proxyquire'; -import type { ReactNode } from 'react'; -import React, { useMemo } from 'react'; - -import FakeChatProvider from '../../../../../tests/mocks/client/FakeChatProvider'; -import FakeRoomProvider from '../../../../../tests/mocks/client/FakeRoomProvider'; -import RouterContextMock from '../../../../../tests/mocks/client/RouterContextMock'; -import { createFakeMessageWithMd } from '../../../../../tests/mocks/data'; -import { UserPresenceContext } from '../../../../contexts/UserPresenceContext'; -import { queryClient } from '../../../../lib/queryClient'; -import type * as RoomMessageContentModule from './RoomMessageContent'; - -describe('RoomMessageContent', () => { - const fakeMessage = createFakeMessageWithMd({ - msg: 'message', - }); - - const { default: RoomMessageContent } = proxyquire.noCallThru().load('./RoomMessageContent', { - '../../content/UiKitSurface': () => null, - '../../content/Attachments': () => null, - '../../MessageContentBody': () => fakeMessage.msg, - '../../content/DiscussionMetrics': () => null, - '../../content/MessageActions': () => null, - '../../hooks/useNormalizedMessage': { useNormalizedMessage: (args: any) => ({ md: fakeMessage.md, ...args }) }, - }); - - const ProvidersMock = ({ children }: { children: ReactNode }) => { - return ( - - - - - ({ - queryUserData: () => ({ - subscribe: () => () => undefined, - get: () => undefined, - }), - }), - [], - )} - > - {children} - - - - - - ); - }; - - it('should render the message when exists', () => { - render(, { - wrapper: ProvidersMock, - }); - - expect(screen.getByText(fakeMessage.msg)).to.exist; - }); - - it('should render the message when has an empty message blocks', () => { - render(, { - wrapper: ProvidersMock, - }); - - expect(screen.getByText(fakeMessage.msg)).to.exist; - }); - - it('should render the message when replies is undefined', () => { - render( - , - { - wrapper: ProvidersMock, - }, - ); - - expect(screen.getByText(fakeMessage.msg)).to.exist; - expect(screen.getByTitle('Replies')).to.include.text('0'); - }); -}); diff --git a/apps/meteor/client/views/room/MessageList/hooks/useAutoTranslate.spec.ts b/apps/meteor/client/views/room/MessageList/hooks/useAutoTranslate.spec.ts deleted file mode 100644 index 707ef7543276..000000000000 --- a/apps/meteor/client/views/room/MessageList/hooks/useAutoTranslate.spec.ts +++ /dev/null @@ -1,117 +0,0 @@ -import { renderHook } from '@testing-library/react-hooks'; -import { expect } from 'chai'; -import proxyquire from 'proxyquire'; - -const COMPONENT_PATH = './useAutoTranslate'; -const defaultConfig = { - '@rocket.chat/ui-contexts': { - useSetting: () => true, - }, - '../../../../../app/autotranslate/client': { - AutoTranslate: { - getLanguage: () => 'lang', - }, - }, - '../../../../lib/rooms/roomCoordinator': { - roomCoordinator: { - isLivechatRoom: () => false, - }, - }, -}; - -describe('room/MessageList/hooks/useAutoTranslate', () => { - it('should return enabled false and undefined language if no subscription and setting disabled', () => { - const { useAutoTranslate } = proxyquire.noCallThru().load(COMPONENT_PATH, { - ...defaultConfig, - '@rocket.chat/ui-contexts': { - useSetting: () => false, - }, - }); - - const { result } = renderHook(() => useAutoTranslate()); - - expect(result.current.autoTranslateEnabled).to.be.equal(false); - expect(result.current.autoTranslateLanguage).to.be.undefined; - - expect(result.current.showAutoTranslate({ u: { _id: 2 } })).to.be.equal(false); - expect(result.current.showAutoTranslate({ u: { _id: 1 }, translations: { lang: 'translated' } })).to.be.equal(false); - expect(result.current.showAutoTranslate({ u: { _id: 2 }, translations: { lang: 'translated' } })).to.be.equal(false); - }); - - it('should return enabled false and undefined language if no subscription', () => { - const { useAutoTranslate } = proxyquire.noCallThru().load(COMPONENT_PATH, defaultConfig); - - const { result } = renderHook(() => useAutoTranslate()); - - expect(result.current.autoTranslateEnabled).to.be.equal(false); - expect(result.current.autoTranslateLanguage).to.be.undefined; - - expect(result.current.showAutoTranslate({ u: { _id: 2 } })).to.be.equal(false); - expect(result.current.showAutoTranslate({ u: { _id: 1 }, translations: { lang: 'translated' } })).to.be.equal(false); - expect(result.current.showAutoTranslate({ u: { _id: 2 }, translations: { lang: 'translated' } })).to.be.equal(false); - }); - - it('should return enabled true and the auto translate language if has subscription', () => { - const { useAutoTranslate } = proxyquire.noCallThru().load(COMPONENT_PATH, defaultConfig); - - const { result } = renderHook(() => useAutoTranslate({ autoTranslate: true, autoTranslateLanguage: 'lang', u: { _id: 1 } })); - - expect(result.current.autoTranslateEnabled).to.be.equal(true); - expect(result.current.autoTranslateLanguage).to.be.equal('lang'); - - expect(result.current.showAutoTranslate({ u: { _id: 2 } })).to.be.equal(false); - expect(result.current.showAutoTranslate({ u: { _id: 1 }, translations: { lang: 'translated' } })).to.be.equal(false); - expect(result.current.showAutoTranslate({ u: { _id: 2 }, translations: { lang: 'translated' } })).to.be.equal(true); - }); - - it('should return enabled true and the default auto translate language if is livechat room', () => { - const { useAutoTranslate } = proxyquire.noCallThru().load(COMPONENT_PATH, { - ...defaultConfig, - '../../../../../app/autotranslate/client': { - AutoTranslate: { - getLanguage: () => 'default', - }, - }, - '../../../../lib/rooms/roomCoordinator': { - roomCoordinator: { - isLivechatRoom: () => true, - }, - }, - }); - - const { result } = renderHook(() => useAutoTranslate({ autoTranslate: false, autoTranslateLanguage: 'default', u: { _id: 1 } })); - - expect(result.current.autoTranslateEnabled).to.be.equal(true); - expect(result.current.autoTranslateLanguage).to.be.equal('default'); - - expect(result.current.showAutoTranslate({ u: { _id: 2 } })).to.be.equal(false); - expect(result.current.showAutoTranslate({ u: { _id: 1 }, translations: { default: 'translated' } })).to.be.equal(false); - expect(result.current.showAutoTranslate({ u: { _id: 2 }, translations: { default: 'translated' } })).to.be.equal(true); - }); - - it('should return enabled false if no auto translate language', () => { - const { useAutoTranslate } = proxyquire.noCallThru().load(COMPONENT_PATH, defaultConfig); - - const { result } = renderHook(() => useAutoTranslate({ autoTranslate: true, autoTranslateLanguage: undefined, u: { _id: 1 } })); - - expect(result.current.autoTranslateEnabled).to.be.equal(false); - expect(result.current.autoTranslateLanguage).to.be.equal(undefined); - - expect(result.current.showAutoTranslate({ u: { _id: 2 } })).to.be.equal(false); - expect(result.current.showAutoTranslate({ u: { _id: 1 }, translations: { lang: 'translated' } })).to.be.equal(false); - expect(result.current.showAutoTranslate({ u: { _id: 2 }, translations: { lang: 'translated' } })).to.be.equal(false); - }); - - it('should return enabled false and language undefined if auto translate is false and has auto translate language', () => { - const { useAutoTranslate } = proxyquire.noCallThru().load(COMPONENT_PATH, defaultConfig); - - const { result } = renderHook(() => useAutoTranslate({ autoTranslate: false, autoTranslateLanguage: 'lang', u: { _id: 1 } })); - - expect(result.current.autoTranslateEnabled).to.be.equal(false); - expect(result.current.autoTranslateLanguage).to.be.equal(undefined); - - expect(result.current.showAutoTranslate({ u: { _id: 2 } })).to.be.equal(false); - expect(result.current.showAutoTranslate({ u: { _id: 1 }, translations: { lang: 'translated' } })).to.be.equal(false); - expect(result.current.showAutoTranslate({ u: { _id: 2 }, translations: { lang: 'translated' } })).to.be.equal(false); - }); -}); diff --git a/apps/meteor/client/views/room/components/body/composer/MessageComposer.spec.ts b/apps/meteor/client/views/room/components/body/composer/MessageComposer.spec.ts deleted file mode 100644 index 3ec8fbec36d8..000000000000 --- a/apps/meteor/client/views/room/components/body/composer/MessageComposer.spec.ts +++ /dev/null @@ -1,14 +0,0 @@ -// Anonymous read enabled: true -// Anonymous write enabled: true - -// Room public: joinCodeRequired: true -// Room public: joinCodeRequired: false - -// federation: enabled - -// read only: true and has no permission to post -// read only: true but has permission to post - -// omnichannel: onhold -// omnichannel: custom composers -// omnichannel: inquiry diff --git a/apps/meteor/jest.config.ts b/apps/meteor/jest.config.ts index 9232c2065dad..af14e7240e09 100644 --- a/apps/meteor/jest.config.ts +++ b/apps/meteor/jest.config.ts @@ -3,12 +3,7 @@ export default { testEnvironment: 'jsdom', modulePathIgnorePatterns: ['/dist/'], - testMatch: [ - '/client/hooks/**.spec.[jt]s?(x)', - '/client/components/**.spec.[jt]s?(x)', - 'client/components/message/content/reactions/**.spec.[jt]s?(x)', - '/client/sidebar/header/actions/hooks/**/**.spec.[jt]s?(x)', - ], + testMatch: ['/client/**/**.spec.[jt]s?(x)'], transform: { '^.+\\.(t|j)sx?$': '@swc/jest', }, diff --git a/ee/packages/api-client/jest.config.ts b/ee/packages/api-client/jest.config.ts index 2e2575dc1209..455fa3a054f2 100644 --- a/ee/packages/api-client/jest.config.ts +++ b/ee/packages/api-client/jest.config.ts @@ -9,4 +9,5 @@ export default { moduleNameMapper: { '\\.css$': 'identity-obj-proxy', }, + collectCoverage: true, }; From 174c28d40b3d5a52023ee2dca2e81dd77ff33fa5 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?J=C3=BAlia=20Jaeger=20Foresti?= <60678893+juliajforesti@users.noreply.github.com> Date: Wed, 19 Jul 2023 18:01:19 -0300 Subject: [PATCH 026/342] chore: bump fuselage (#29872) --- .../HeaderToolbox/HeaderToolboxAction.tsx | 2 +- yarn.lock | 35 +++++++++++-------- 2 files changed, 22 insertions(+), 15 deletions(-) diff --git a/packages/ui-client/src/components/Header/HeaderToolbox/HeaderToolboxAction.tsx b/packages/ui-client/src/components/Header/HeaderToolbox/HeaderToolboxAction.tsx index d8d97e063ba8..48db84657959 100644 --- a/packages/ui-client/src/components/Header/HeaderToolbox/HeaderToolboxAction.tsx +++ b/packages/ui-client/src/components/Header/HeaderToolbox/HeaderToolboxAction.tsx @@ -13,7 +13,7 @@ const HeaderToolboxAction = forwardRef(function HeaderTo data-toolbox={index} key={id} icon={icon} - tiny + small position='relative' overflow='visible' {...(tooltip ? { 'data-tooltip': tooltip, 'title': '' } : { title })} diff --git a/yarn.lock b/yarn.lock index ecde951e5d8b..23ec581c7f14 100644 --- a/yarn.lock +++ b/yarn.lock @@ -9457,7 +9457,7 @@ __metadata: languageName: unknown linkType: soft -"@rocket.chat/css-in-js@npm:^0.31.23, @rocket.chat/css-in-js@npm:~0.31.23-dev.160": +"@rocket.chat/css-in-js@npm:^0.31.23, @rocket.chat/css-in-js@npm:~0.31.23-dev.160, @rocket.chat/css-in-js@npm:~0.31.23-dev.165": version: 0.31.23 resolution: "@rocket.chat/css-in-js@npm:0.31.23" dependencies: @@ -9483,7 +9483,7 @@ __metadata: languageName: node linkType: hard -"@rocket.chat/css-supports@npm:^0.31.23, @rocket.chat/css-supports@npm:~0.31.23-dev.160": +"@rocket.chat/css-supports@npm:^0.31.23, @rocket.chat/css-supports@npm:~0.31.23-dev.160, @rocket.chat/css-supports@npm:~0.31.23-dev.165": version: 0.31.23 resolution: "@rocket.chat/css-supports@npm:0.31.23" dependencies: @@ -9692,13 +9692,20 @@ __metadata: languageName: node linkType: hard -"@rocket.chat/fuselage-tokens@npm:next, @rocket.chat/fuselage-tokens@npm:~0.32.0-dev.336": +"@rocket.chat/fuselage-tokens@npm:next": version: 0.32.0-dev.336 resolution: "@rocket.chat/fuselage-tokens@npm:0.32.0-dev.336" checksum: 2cf52211b3a3ee9a6111db46bc05918244190be7a6fef482011dccc0cc9a0777362dd91010e9df45c67b2c3bcaf00e35a7fbdddfdb5e7f683a19baa1fa8211d0 languageName: node linkType: hard +"@rocket.chat/fuselage-tokens@npm:~0.32.0-dev.341": + version: 0.32.0-dev.341 + resolution: "@rocket.chat/fuselage-tokens@npm:0.32.0-dev.341" + checksum: f8944cbae4ea3a90c4127627e4f90ab1becc916c72ec54e1f16fd8de3e2bb8c0c6ca1dc1aa87ae7d2dc7d105f325dbbe65c262eca5719f6d237e7b2aa35a321e + languageName: node + linkType: hard + "@rocket.chat/fuselage-ui-kit@workspace:^, @rocket.chat/fuselage-ui-kit@workspace:packages/fuselage-ui-kit, @rocket.chat/fuselage-ui-kit@workspace:~": version: 0.0.0-use.local resolution: "@rocket.chat/fuselage-ui-kit@workspace:packages/fuselage-ui-kit" @@ -9755,14 +9762,14 @@ __metadata: linkType: soft "@rocket.chat/fuselage@npm:next": - version: 0.32.0-dev.386 - resolution: "@rocket.chat/fuselage@npm:0.32.0-dev.386" - dependencies: - "@rocket.chat/css-in-js": ~0.31.23-dev.160 - "@rocket.chat/css-supports": ~0.31.23-dev.160 - "@rocket.chat/fuselage-tokens": ~0.32.0-dev.336 - "@rocket.chat/memo": ~0.31.23-dev.160 - "@rocket.chat/styled": ~0.31.23-dev.160 + version: 0.32.0-dev.391 + resolution: "@rocket.chat/fuselage@npm:0.32.0-dev.391" + dependencies: + "@rocket.chat/css-in-js": ~0.31.23-dev.165 + "@rocket.chat/css-supports": ~0.31.23-dev.165 + "@rocket.chat/fuselage-tokens": ~0.32.0-dev.341 + "@rocket.chat/memo": ~0.31.23-dev.165 + "@rocket.chat/styled": ~0.31.23-dev.165 invariant: ^2.2.4 react-aria: ~3.23.1 react-keyed-flatten-children: ^1.3.0 @@ -9774,7 +9781,7 @@ __metadata: react: ^17.0.2 react-dom: ^17.0.2 react-virtuoso: 1.2.4 - checksum: 3753fe5939b7fdead82076b824959bc7ed537349f810c104432d2ab1f7aa166a374642664e82e2cdc9efa3d1b4e1807c5ed12777cf7e875de24fca4f0ed58149 + checksum: 2d32285d0354bbadb876a01ad04437b28a44ef22ed76468972a99923c976e32859a9ce66f83cbf650db15e8737bbe3f4938ac6975fed8d8462f9329be37d3c0e languageName: node linkType: hard @@ -9998,7 +10005,7 @@ __metadata: languageName: node linkType: hard -"@rocket.chat/memo@npm:^0.31.23, @rocket.chat/memo@npm:~0.31.23-dev.160": +"@rocket.chat/memo@npm:^0.31.23, @rocket.chat/memo@npm:~0.31.23-dev.160, @rocket.chat/memo@npm:~0.31.23-dev.165": version: 0.31.23 resolution: "@rocket.chat/memo@npm:0.31.23" checksum: 070debb940749a2e4463cf767dd65c6967cea664a5bd67c22a812d611f6c3c46d6fe4bb0bf329e43dcd927493413add37c45ae3b05ec08f0b24e9d7385caebdd @@ -10824,7 +10831,7 @@ __metadata: languageName: node linkType: hard -"@rocket.chat/styled@npm:~0.31.23-dev.160": +"@rocket.chat/styled@npm:~0.31.23-dev.160, @rocket.chat/styled@npm:~0.31.23-dev.165": version: 0.31.23 resolution: "@rocket.chat/styled@npm:0.31.23" dependencies: From 1c27dc1e9beb10a15c658558c79376339be93fe5 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?J=C3=BAlia=20Jaeger=20Foresti?= <60678893+juliajforesti@users.noreply.github.com> Date: Thu, 20 Jul 2023 14:40:29 -0300 Subject: [PATCH 027/342] chore: sidebar actions pressed state (#29877) --- apps/meteor/client/sidebar/header/actions/Directory.tsx | 9 ++++++++- apps/meteor/client/sidebar/header/actions/Home.tsx | 4 +++- ee/packages/ui-theming/src/sidebarPalette.ts | 2 +- ee/packages/ui-theming/src/sidebarPaletteDark.ts | 2 +- 4 files changed, 13 insertions(+), 4 deletions(-) diff --git a/apps/meteor/client/sidebar/header/actions/Directory.tsx b/apps/meteor/client/sidebar/header/actions/Directory.tsx index cdfdcfc1eaf1..f5caacf069a3 100644 --- a/apps/meteor/client/sidebar/header/actions/Directory.tsx +++ b/apps/meteor/client/sidebar/header/actions/Directory.tsx @@ -14,7 +14,14 @@ const Directory = (props: DirectoryProps) => { router.navigate('/directory'); }); - return ; + return ( + + ); }; export default Directory; diff --git a/apps/meteor/client/sidebar/header/actions/Home.tsx b/apps/meteor/client/sidebar/header/actions/Home.tsx index 2c4bbec40f51..0a67a9dbca26 100644 --- a/apps/meteor/client/sidebar/header/actions/Home.tsx +++ b/apps/meteor/client/sidebar/header/actions/Home.tsx @@ -13,7 +13,9 @@ const SidebarHeaderActionHome: VFC, 'is'>> = (p router.navigate('/home'); }); - return showHome ? : null; + return showHome ? ( + + ) : null; }; export default SidebarHeaderActionHome; diff --git a/ee/packages/ui-theming/src/sidebarPalette.ts b/ee/packages/ui-theming/src/sidebarPalette.ts index c25ad6272db8..9512af1e467c 100644 --- a/ee/packages/ui-theming/src/sidebarPalette.ts +++ b/ee/packages/ui-theming/src/sidebarPalette.ts @@ -53,7 +53,7 @@ export const palette = [ list: [ { name: 'button-background-secondary-default', token: '', color: '#0D0F11' }, { name: 'button-background-secondary-hover', token: '', color: '#3A404B' }, - { name: 'button-background-secondary-press', token: '', color: '#2C313A' }, + { name: 'button-background-secondary-press', token: '', color: '#4C5362' }, { name: 'button-background-secondary-focus', token: '', color: '#0D0F11' }, { name: 'button-background-secondary-keyfocus', token: '', color: '#2F343D' }, { name: 'button-background-secondary-disabled', token: '', color: '#2F343D' }, diff --git a/ee/packages/ui-theming/src/sidebarPaletteDark.ts b/ee/packages/ui-theming/src/sidebarPaletteDark.ts index b2e7cc4e9831..2b7801f118fb 100644 --- a/ee/packages/ui-theming/src/sidebarPaletteDark.ts +++ b/ee/packages/ui-theming/src/sidebarPaletteDark.ts @@ -55,7 +55,7 @@ export const palette = [ list: [ { name: 'button-background-secondary-default', token: '', color: '#0D0F11' }, { name: 'button-background-secondary-hover', token: '', color: '#3A404B' }, - { name: 'button-background-secondary-press', token: '', color: '#2C313A' }, + { name: 'button-background-secondary-press', token: '', color: '#4C5362' }, { name: 'button-background-secondary-focus', token: '', color: '#0D0F11' }, { name: 'button-background-secondary-keyfocus', token: '', color: '#2F343D' }, { name: 'button-background-secondary-disabled', token: '', color: '#2F343D' }, From 54ef89c9a7a065747f1172b767e0da30743df164 Mon Sep 17 00:00:00 2001 From: csuadev <72958726+csuadev@users.noreply.github.com> Date: Thu, 20 Jul 2023 14:11:01 -0500 Subject: [PATCH 028/342] fix: show requested filters only on requested apps view (#29819) Co-authored-by: Tasso Evangelista <2263066+tassoevan@users.noreply.github.com> --- .changeset/silver-mugs-unite.md | 5 +++ .../marketplace/AppsPage/AppsPageContent.tsx | 39 ++++++++++++++----- 2 files changed, 34 insertions(+), 10 deletions(-) create mode 100644 .changeset/silver-mugs-unite.md diff --git a/.changeset/silver-mugs-unite.md b/.changeset/silver-mugs-unite.md new file mode 100644 index 000000000000..be74b1bef215 --- /dev/null +++ b/.changeset/silver-mugs-unite.md @@ -0,0 +1,5 @@ +--- +"@rocket.chat/meteor": patch +--- + +fix: show requested filters only on requested apps view diff --git a/apps/meteor/client/views/marketplace/AppsPage/AppsPageContent.tsx b/apps/meteor/client/views/marketplace/AppsPage/AppsPageContent.tsx index e9b91b663d8d..b652aba5ca27 100644 --- a/apps/meteor/client/views/marketplace/AppsPage/AppsPageContent.tsx +++ b/apps/meteor/client/views/marketplace/AppsPage/AppsPageContent.tsx @@ -55,17 +55,36 @@ const AppsPageContent = (): ReactElement => { }); const statusFilterOnSelected = useRadioToggle(setStatusFilterStructure); - const [sortFilterStructure, setSortFilterStructure] = useState({ - label: t('Sort_By'), - items: [ - { id: 'urf', label: t('Unread_Requested_First'), checked: false }, - { id: 'url', label: t('Unread_Requested_Last'), checked: false }, - { id: 'az', label: 'A-Z', checked: false }, - { id: 'za', label: 'Z-A', checked: false }, - { id: 'mru', label: t('Most_recent_updated'), checked: true }, - { id: 'lru', label: t('Least_recent_updated'), checked: false }, - ], + const baseFilterStructureItems = [ + { id: 'az', label: 'A-Z', checked: false }, + { id: 'za', label: 'Z-A', checked: false }, + { id: 'mru', label: t('Most_recent_updated'), checked: true }, + { id: 'lru', label: t('Least_recent_updated'), checked: false }, + ]; + + const requestedFilterItems = [ + { id: 'urf', label: t('Unread_Requested_First'), checked: false }, + { id: 'url', label: t('Unread_Requested_Last'), checked: false }, + ]; + + const createFilterStructureItems = () => { + return isRequested ? [...requestedFilterItems, ...baseFilterStructureItems] : baseFilterStructureItems; + }; + + const [sortFilterStructure, setSortFilterStructure] = useState(() => { + return { + label: t('Sort_By'), + items: createFilterStructureItems(), + }; }); + + useEffect(() => { + setSortFilterStructure({ + label: t('Sort_By'), + items: createFilterStructureItems(), + }); + }, [isRequested]); + const sortFilterOnSelected = useRadioToggle(setSortFilterStructure); const getAppsData = useCallback((): appsDataType => { From 2047ab23d59b5219d18028591c067aa24b2f8f32 Mon Sep 17 00:00:00 2001 From: Marcos Spessatto Defendi Date: Thu, 20 Jul 2023 16:11:33 -0300 Subject: [PATCH 029/342] chore: move some message functions to message service (#29793) Co-authored-by: Guilherme Gazzo Co-authored-by: kodiakhq[bot] <49736102+kodiakhq[bot]@users.noreply.github.com> --- .../server/services/messages/service.ts | 22 ++++++++++++++++++- .../src/types/IMessageService.ts | 6 ++++- 2 files changed, 26 insertions(+), 2 deletions(-) diff --git a/apps/meteor/server/services/messages/service.ts b/apps/meteor/server/services/messages/service.ts index 2f15cdbadb51..e5d30130e331 100644 --- a/apps/meteor/server/services/messages/service.ts +++ b/apps/meteor/server/services/messages/service.ts @@ -1,10 +1,14 @@ -import type { IMessage, MessageTypesValues, IUser } from '@rocket.chat/core-typings'; +import type { IMessage, MessageTypesValues, IUser, IRoom } from '@rocket.chat/core-typings'; import type { IMessageService } from '@rocket.chat/core-services'; import { ServiceClassInternal } from '@rocket.chat/core-services'; import { Messages } from '@rocket.chat/models'; import { executeSendMessage } from '../../../app/lib/server/methods/sendMessage'; import { settings } from '../../../app/settings/server'; +import { sendMessage } from '../../../app/lib/server/functions/sendMessage'; +import { deleteMessage } from '../../../app/lib/server/functions/deleteMessage'; +import { updateMessage } from '../../../app/lib/server/functions/updateMessage'; +import { executeSetReaction } from '../../../app/reactions/server/setReaction'; export class MessageService extends ServiceClassInternal implements IMessageService { protected name = 'message'; @@ -13,6 +17,22 @@ export class MessageService extends ServiceClassInternal implements IMessageServ return executeSendMessage(fromId, { rid, msg }); } + async sendMessageWithValidation(user: IUser, message: Partial, room: Partial, upsert = false): Promise { + return sendMessage(user, message, room, upsert); + } + + async deleteMessage(user: IUser, message: IMessage): Promise { + return deleteMessage(message, user); + } + + async updateMessage(message: IMessage, user: IUser, originalMsg?: IMessage): Promise { + return updateMessage(message, user, originalMsg); + } + + async reactToMessage(userId: string, reaction: string, messageId: IMessage['_id'], shouldReact?: boolean): Promise { + return executeSetReaction(userId, reaction, messageId, shouldReact); + } + async saveSystemMessage( type: MessageTypesValues, rid: string, diff --git a/packages/core-services/src/types/IMessageService.ts b/packages/core-services/src/types/IMessageService.ts index cf02b9e664be..ea8f207df67d 100644 --- a/packages/core-services/src/types/IMessageService.ts +++ b/packages/core-services/src/types/IMessageService.ts @@ -1,4 +1,4 @@ -import type { IMessage, MessageTypesValues, IUser } from '@rocket.chat/core-typings'; +import type { IMessage, MessageTypesValues, IUser, IRoom } from '@rocket.chat/core-typings'; export interface IMessageService { sendMessage({ fromId, rid, msg }: { fromId: string; rid: string; msg: string }): Promise; @@ -9,4 +9,8 @@ export interface IMessageService { user: Pick, extraData?: Partial, ): Promise; + sendMessageWithValidation(user: IUser, message: Partial, room: Partial, upsert?: boolean): Promise; + deleteMessage(user: IUser, message: IMessage): Promise; + updateMessage(message: IMessage, user: IUser, originalMsg?: IMessage): Promise; + reactToMessage(userId: string, reaction: string, messageId: IMessage['_id'], shouldReact?: boolean): Promise; } From 92b690d206b02e8b80276173a1d8ad940359d385 Mon Sep 17 00:00:00 2001 From: Disha Bhardwaj <67470541+bhardwajdisha@users.noreply.github.com> Date: Fri, 21 Jul 2023 01:56:29 +0530 Subject: [PATCH 030/342] fix: Wrong toast message while creating a new custom sound with an existing name (#28301) Co-authored-by: Douglas Fabris <27704687+dougfabris@users.noreply.github.com> --- .changeset/empty-ants-enjoy.md | 5 +++++ .../client/views/admin/customSounds/AddCustomSound.tsx | 4 +++- 2 files changed, 8 insertions(+), 1 deletion(-) create mode 100644 .changeset/empty-ants-enjoy.md diff --git a/.changeset/empty-ants-enjoy.md b/.changeset/empty-ants-enjoy.md new file mode 100644 index 000000000000..4a55f82d0abf --- /dev/null +++ b/.changeset/empty-ants-enjoy.md @@ -0,0 +1,5 @@ +--- +'@rocket.chat/meteor': minor +--- + +fix: Wrong toast message while creating a new custom sound with an existing name diff --git a/apps/meteor/client/views/admin/customSounds/AddCustomSound.tsx b/apps/meteor/client/views/admin/customSounds/AddCustomSound.tsx index 35feae9113cc..e69186ed5521 100644 --- a/apps/meteor/client/views/admin/customSounds/AddCustomSound.tsx +++ b/apps/meteor/client/views/admin/customSounds/AddCustomSound.tsx @@ -72,7 +72,9 @@ const AddCustomSound = ({ goToNew, close, onChange, ...props }: AddCustomSoundPr const handleSave = useCallback(async () => { try { const result = await saveAction(name, sound); - dispatchToastMessage({ type: 'success', message: t('Custom_Sound_Saved_Successfully') }); + if (result) { + dispatchToastMessage({ type: 'success', message: t('Custom_Sound_Saved_Successfully') }); + } result && goToNew(result); onChange(); } catch (error) { From e6b9a72da55299ecafd3b4657abad3dd5b4e3b68 Mon Sep 17 00:00:00 2001 From: Marcos Spessatto Defendi Date: Thu, 20 Jul 2023 18:16:33 -0300 Subject: [PATCH 031/342] chore: move some room functions to room service (#29794) Co-authored-by: Guilherme Gazzo --- .../app/lib/server/functions/createRoom.ts | 4 +- .../server/functions/removeUserFromRoom.ts | 2 +- apps/meteor/server/services/room/service.ts | 52 +++++++++++++++++-- .../core-services/src/types/IRoomService.ts | 25 ++++++++- 4 files changed, 76 insertions(+), 7 deletions(-) diff --git a/apps/meteor/app/lib/server/functions/createRoom.ts b/apps/meteor/app/lib/server/functions/createRoom.ts index 6d0048659501..836cb8917cbc 100644 --- a/apps/meteor/app/lib/server/functions/createRoom.ts +++ b/apps/meteor/app/lib/server/functions/createRoom.ts @@ -5,10 +5,10 @@ import { Message, Team } from '@rocket.chat/core-services'; import type { ICreateRoomParams, ISubscriptionExtraData } from '@rocket.chat/core-services'; import { Rooms, Subscriptions, Users } from '@rocket.chat/models'; -import { Apps } from '../../../../ee/server/apps'; +import { Apps } from '../../../../ee/server/apps/orchestrator'; import { addUserRolesAsync } from '../../../../server/lib/roles/addUserRoles'; import { callbacks } from '../../../../lib/callbacks'; -import { getValidRoomName } from '../../../utils/server'; +import { getValidRoomName } from '../../../utils/server/lib/getValidRoomName'; import { createDirectRoom } from './createDirectRoom'; const isValidName = (name: unknown): name is string => { diff --git a/apps/meteor/app/lib/server/functions/removeUserFromRoom.ts b/apps/meteor/app/lib/server/functions/removeUserFromRoom.ts index bca939bc520b..91c20ab6715c 100644 --- a/apps/meteor/app/lib/server/functions/removeUserFromRoom.ts +++ b/apps/meteor/app/lib/server/functions/removeUserFromRoom.ts @@ -5,7 +5,7 @@ import type { IUser } from '@rocket.chat/core-typings'; import { Message, Team } from '@rocket.chat/core-services'; import { Subscriptions, Rooms } from '@rocket.chat/models'; -import { AppEvents, Apps } from '../../../../ee/server/apps'; +import { AppEvents, Apps } from '../../../../ee/server/apps/orchestrator'; import { callbacks } from '../../../../lib/callbacks'; export const removeUserFromRoom = async function ( diff --git a/apps/meteor/server/services/room/service.ts b/apps/meteor/server/services/room/service.ts index 88a94edee987..d34e5bd84dde 100644 --- a/apps/meteor/server/services/room/service.ts +++ b/apps/meteor/server/services/room/service.ts @@ -1,10 +1,15 @@ -import type { IRoom, IUser } from '@rocket.chat/core-typings'; +import type { AtLeast, IRoom, IUser } from '@rocket.chat/core-typings'; import { Users } from '@rocket.chat/models'; import { ServiceClassInternal, Authorization } from '@rocket.chat/core-services'; import type { ICreateRoomParams, IRoomService } from '@rocket.chat/core-services'; import { createRoom } from '../../../app/lib/server/functions/createRoom'; // TODO remove this import import { createDirectMessage } from '../../methods/createDirectMessage'; +import { addUserToRoom } from '../../../app/lib/server/functions/addUserToRoom'; +import { removeUserFromRoom } from '../../../app/lib/server/functions/removeUserFromRoom'; +import { getValidRoomName } from '../../../app/utils/server/lib/getValidRoomName'; +import { saveRoomTopic } from '../../../app/channel-settings/server/functions/saveRoomTopic'; +import { roomCoordinator } from '../../lib/rooms/roomCoordinator'; export class RoomService extends ServiceClassInternal implements IRoomService { protected name = 'room'; @@ -34,10 +39,14 @@ export class RoomService extends ServiceClassInternal implements IRoomService { Users.findOneById(from, { projection: { _id: 1 } }), ]); - if (!toUser || !fromUser) { + if (!toUser?.username || !fromUser) { throw new Error('error-invalid-user'); } - return createDirectMessage([toUser.username], fromUser._id); + return this.createDirectMessageWithMultipleUsers([toUser.username], fromUser._id); + } + + async createDirectMessageWithMultipleUsers(members: string[], creatorId: string): Promise<{ rid: string }> { + return createDirectMessage(members, creatorId); } async addMember(uid: string, rid: string): Promise { @@ -48,4 +57,41 @@ export class RoomService extends ServiceClassInternal implements IRoomService { return true; } + + async addUserToRoom( + roomId: string, + user: Pick | string, + inviter?: Pick, + silenced?: boolean, + ): Promise { + return addUserToRoom(roomId, user, inviter, silenced); + } + + async removeUserFromRoom(roomId: string, user: IUser, options?: { byUser: Pick }): Promise { + return removeUserFromRoom(roomId, user, options); + } + + async getValidRoomName( + displayName: string, + roomId = '', + options: { allowDuplicates?: boolean; nameValidationRegex?: string } = {}, + ): Promise { + return getValidRoomName(displayName, roomId, options); + } + + async saveRoomTopic( + roomId: string, + roomTopic: string | undefined, + user: { + username: string; + _id: string; + }, + sendMessage = true, + ): Promise { + await saveRoomTopic(roomId, roomTopic, user, sendMessage); + } + + async getRouteLink(room: AtLeast): Promise { + return roomCoordinator.getRouteLink(room.t as string, { rid: room._id, name: room.name }); + } } diff --git a/packages/core-services/src/types/IRoomService.ts b/packages/core-services/src/types/IRoomService.ts index b8f4ccde0ec2..e69707e18a36 100644 --- a/packages/core-services/src/types/IRoomService.ts +++ b/packages/core-services/src/types/IRoomService.ts @@ -1,4 +1,4 @@ -import type { IRoom } from '@rocket.chat/core-typings'; +import type { AtLeast, IRoom, IUser } from '@rocket.chat/core-typings'; export interface ISubscriptionExtraData { open: boolean; @@ -29,4 +29,27 @@ export interface IRoomService { addMember(uid: string, rid: string): Promise; create(uid: string, params: ICreateRoomParams): Promise; createDirectMessage(data: { to: string; from: string }): Promise<{ rid: string }>; + createDirectMessageWithMultipleUsers(members: string[], creatorId: string): Promise<{ rid: string }>; + addUserToRoom( + roomId: string, + user: Pick | string, + inviter?: Pick, + silenced?: boolean, + ): Promise; + removeUserFromRoom(roomId: string, user: IUser, options?: { byUser: Pick }): Promise; + getValidRoomName( + displayName: string, + roomId?: string, + options?: { allowDuplicates?: boolean; nameValidationRegex?: string }, + ): Promise; + saveRoomTopic( + roomId: string, + roomTopic: string | undefined, + user: { + username: string; + _id: string; + }, + sendMessage?: boolean, + ): Promise; + getRouteLink(room: AtLeast): Promise; } From e9a42ba779392e3d3975449d4662210c74fc764b Mon Sep 17 00:00:00 2001 From: Douglas Fabris Date: Thu, 20 Jul 2023 19:17:20 -0300 Subject: [PATCH 032/342] chore: Reduce complexity to infer IconName type (#29881) --- .../client/messageBox/messageBoxFormatting.ts | 5 ++--- apps/meteor/app/ui-utils/client/lib/AccountBox.ts | 5 ++--- apps/meteor/app/ui-utils/client/lib/MessageAction.ts | 6 +++--- .../client/components/GenericModal/GenericModal.tsx | 6 +++--- .../components/GenericNoResults/GenericNoResults.tsx | 5 ++--- .../client/components/InfoPanel/InfoPanelAction.tsx | 3 ++- .../client/components/InfoPanel/InfoPanelTitle.tsx | 5 +++-- .../components/Sidebar/SidebarNavigationItem.tsx | 4 ++-- apps/meteor/client/components/UpsellModal.tsx | 4 ++-- .../client/components/UserInfo/UserInfoAction.tsx | 3 ++- .../components/message/content/MessageActions.tsx | 4 ++-- .../message/content/actions/MessageAction.tsx | 8 ++++---- apps/meteor/client/lib/banners.ts | 5 ++--- apps/meteor/client/lib/createSidebarItems.ts | 4 ++-- apps/meteor/client/sidebar/Item/Condensed.tsx | 4 ++-- apps/meteor/client/sidebar/Item/Extended.tsx | 4 ++-- apps/meteor/client/views/banners/UiKitBanner.tsx | 5 +++-- apps/meteor/client/views/hooks/useActionSpread.ts | 6 +++--- .../views/omnichannel/agents/AgentInfoAction.tsx | 4 ++-- .../room/components/contextualBar/MessageListTab.tsx | 6 +++--- .../components/NotificationByDevice.tsx | 5 +++-- .../room/contextualBar/OTR/components/OTRStates.tsx | 6 +++--- apps/meteor/client/views/room/lib/Toolbox/index.tsx | 5 +++-- .../WebdavFilePickerGrid/WebdavFilePickerGrid.tsx | 4 ++-- .../WebdavFilePickerModal/WebdavFilePickerTable.tsx | 4 ++-- .../WebdavFilePickerModal/lib/getNodeIconType.ts | 10 ++++++++-- .../views/teams/contextualBar/info/TeamsInfo.tsx | 5 ++--- apps/meteor/definition/IRoomTypeConfig.ts | 5 ++--- .../client/deviceManagement/components/DeviceIcon.tsx | 5 +++-- .../ui-client/src/components/Header/HeaderIcon.tsx | 5 +++-- packages/ui-video-conf/src/VideoConfButton.tsx | 5 +++-- packages/ui-video-conf/src/VideoConfController.tsx | 4 ++-- .../web-ui-registration/src/LoginServicesButton.tsx | 5 +++-- 33 files changed, 87 insertions(+), 77 deletions(-) diff --git a/apps/meteor/app/ui-message/client/messageBox/messageBoxFormatting.ts b/apps/meteor/app/ui-message/client/messageBox/messageBoxFormatting.ts index bc3fb8a011cc..bd8a9e149cca 100644 --- a/apps/meteor/app/ui-message/client/messageBox/messageBoxFormatting.ts +++ b/apps/meteor/app/ui-message/client/messageBox/messageBoxFormatting.ts @@ -1,13 +1,12 @@ -import type { Icon } from '@rocket.chat/fuselage'; import type { TranslationKey } from '@rocket.chat/ui-contexts'; -import type { ComponentProps } from 'react'; +import type { Keys as IconName } from '@rocket.chat/icons'; import { settings } from '../../../settings/client'; export type FormattingButton = | { label: TranslationKey; - icon: ComponentProps['name']; + icon: IconName; pattern: string; // text?: () => string | undefined; command?: string; diff --git a/apps/meteor/app/ui-utils/client/lib/AccountBox.ts b/apps/meteor/app/ui-utils/client/lib/AccountBox.ts index c7bda9df1659..4bc39fdbaa68 100644 --- a/apps/meteor/app/ui-utils/client/lib/AccountBox.ts +++ b/apps/meteor/app/ui-utils/client/lib/AccountBox.ts @@ -1,8 +1,7 @@ import type { IUIActionButton, IUActionButtonWhen } from '@rocket.chat/apps-engine/definition/ui/IUIActionButtonDescriptor'; import type { UserStatus } from '@rocket.chat/core-typings'; import type { TranslationKey, LocationPathname } from '@rocket.chat/ui-contexts'; -import type { Icon } from '@rocket.chat/fuselage'; -import type { ComponentProps } from 'react'; +import type { Keys as IconName } from '@rocket.chat/icons'; import { sdk } from '../../../utils/client/lib/SDKClient'; @@ -18,7 +17,7 @@ export interface IAppAccountBoxItem extends IUIActionButton { export type AccountBoxItem = { name: TranslationKey; - icon: ComponentProps['name']; + icon: IconName; href: LocationPathname; sideNav?: string; condition: () => boolean; diff --git a/apps/meteor/app/ui-utils/client/lib/MessageAction.ts b/apps/meteor/app/ui-utils/client/lib/MessageAction.ts index 6fd101864bc8..d909bda53eaa 100644 --- a/apps/meteor/app/ui-utils/client/lib/MessageAction.ts +++ b/apps/meteor/app/ui-utils/client/lib/MessageAction.ts @@ -1,11 +1,11 @@ -import type { ComponentProps, ContextType } from 'react'; +import type { ContextType } from 'react'; import mem from 'mem'; import { Meteor } from 'meteor/meteor'; import { ReactiveVar } from 'meteor/reactive-var'; import { Tracker } from 'meteor/tracker'; -import type { Icon } from '@rocket.chat/fuselage'; import type { IMessage, IUser, ISubscription, IRoom, SettingValue, Serialized, ITranslatedMessage } from '@rocket.chat/core-typings'; import type { TranslationKey } from '@rocket.chat/ui-contexts'; +import type { Keys as IconName } from '@rocket.chat/icons'; import { Messages, ChatRoom, Subscriptions } from '../../../models/client'; import { roomCoordinator } from '../../../../client/lib/rooms/roomCoordinator'; @@ -48,7 +48,7 @@ type MessageActionConditionProps = { export type MessageActionConfig = { id: string; - icon: ComponentProps['name']; + icon: IconName; variant?: 'danger' | 'success' | 'warning'; label: TranslationKey; order?: number; diff --git a/apps/meteor/client/components/GenericModal/GenericModal.tsx b/apps/meteor/client/components/GenericModal/GenericModal.tsx index 09a9598dafc2..bf5d808f5b5b 100644 --- a/apps/meteor/client/components/GenericModal/GenericModal.tsx +++ b/apps/meteor/client/components/GenericModal/GenericModal.tsx @@ -1,5 +1,5 @@ -import type { Icon } from '@rocket.chat/fuselage'; import { Button, Modal } from '@rocket.chat/fuselage'; +import type { Keys as IconName } from '@rocket.chat/icons'; import { useTranslation } from '@rocket.chat/ui-contexts'; import type { FC, ComponentProps, ReactElement, ReactNode } from 'react'; import React from 'react'; @@ -15,14 +15,14 @@ type GenericModalProps = RequiredModalProps & { cancelText?: ReactNode; confirmText?: ReactNode; title?: string | ReactElement; - icon?: ComponentProps['name'] | ReactElement | null; + icon?: IconName | ReactElement | null; confirmDisabled?: boolean; tagline?: ReactNode; onCancel?: () => Promise | void; onClose?: () => Promise | void; } & Omit, 'title'>; -const iconMap: Record['name']> = { +const iconMap: Record = { danger: 'modal-warning', warning: 'modal-warning', info: 'info', diff --git a/apps/meteor/client/components/GenericNoResults/GenericNoResults.tsx b/apps/meteor/client/components/GenericNoResults/GenericNoResults.tsx index f9d0803a98d6..5a630898ab13 100644 --- a/apps/meteor/client/components/GenericNoResults/GenericNoResults.tsx +++ b/apps/meteor/client/components/GenericNoResults/GenericNoResults.tsx @@ -1,11 +1,10 @@ -import type { Icon } from '@rocket.chat/fuselage'; import { States, StatesIcon, StatesTitle, StatesSubtitle, StatesActions, StatesAction } from '@rocket.chat/fuselage'; +import type { Keys as IconName } from '@rocket.chat/icons'; import { useTranslation } from '@rocket.chat/ui-contexts'; -import type { ComponentProps } from 'react'; import React from 'react'; type GenericNoResultsProps = { - icon?: ComponentProps['name']; + icon?: IconName; title?: string; description?: string; buttonTitle?: string; diff --git a/apps/meteor/client/components/InfoPanel/InfoPanelAction.tsx b/apps/meteor/client/components/InfoPanel/InfoPanelAction.tsx index d185c6ac5e68..17b555e0b820 100644 --- a/apps/meteor/client/components/InfoPanel/InfoPanelAction.tsx +++ b/apps/meteor/client/components/InfoPanel/InfoPanelAction.tsx @@ -1,9 +1,10 @@ import { Icon, Button } from '@rocket.chat/fuselage'; +import type { Keys as IconName } from '@rocket.chat/icons'; import type { ComponentProps, ReactElement, ReactNode } from 'react'; import React from 'react'; type InfoPanelActionProps = Omit, 'label'> & { - icon?: ComponentProps['name']; + icon?: IconName; label: ReactNode; }; diff --git a/apps/meteor/client/components/InfoPanel/InfoPanelTitle.tsx b/apps/meteor/client/components/InfoPanel/InfoPanelTitle.tsx index 298a01bd2045..2714b5fec0e3 100644 --- a/apps/meteor/client/components/InfoPanel/InfoPanelTitle.tsx +++ b/apps/meteor/client/components/InfoPanel/InfoPanelTitle.tsx @@ -1,5 +1,6 @@ import { Box, Icon } from '@rocket.chat/fuselage'; -import type { ComponentProps, FC, ReactNode } from 'react'; +import type { Keys as IconName } from '@rocket.chat/icons'; +import type { FC, ReactNode } from 'react'; import React from 'react'; type InfoPanelTitleProps = { @@ -7,7 +8,7 @@ type InfoPanelTitleProps = { icon: ReactNode; }; -const isValidIcon = (icon: ReactNode): icon is ComponentProps['name'] => typeof icon === 'string'; +const isValidIcon = (icon: ReactNode): icon is IconName => typeof icon === 'string'; const InfoPanelTitle: FC = ({ title, icon }) => ( diff --git a/apps/meteor/client/components/Sidebar/SidebarNavigationItem.tsx b/apps/meteor/client/components/Sidebar/SidebarNavigationItem.tsx index ed6cd3e91d3b..4bfacbedbdfc 100644 --- a/apps/meteor/client/components/Sidebar/SidebarNavigationItem.tsx +++ b/apps/meteor/client/components/Sidebar/SidebarNavigationItem.tsx @@ -1,5 +1,5 @@ import { Box, Icon, Tag } from '@rocket.chat/fuselage'; -import type { IconProps } from '@rocket.chat/fuselage'; +import type { Keys as IconName } from '@rocket.chat/icons'; import type { FC, ReactElement } from 'react'; import React, { memo } from 'react'; @@ -8,7 +8,7 @@ import SidebarGenericItem from './SidebarGenericItem'; type SidebarNavigationItemProps = { permissionGranted?: (() => boolean) | boolean; pathSection: string; - icon?: IconProps['name']; + icon?: IconName; label?: string; tag?: string; currentPath?: string; diff --git a/apps/meteor/client/components/UpsellModal.tsx b/apps/meteor/client/components/UpsellModal.tsx index 08cb1171e9ee..ad34db316784 100644 --- a/apps/meteor/client/components/UpsellModal.tsx +++ b/apps/meteor/client/components/UpsellModal.tsx @@ -1,5 +1,5 @@ -import type { Icon } from '@rocket.chat/fuselage'; import { Box, Button, Modal } from '@rocket.chat/fuselage'; +import type { Keys as IconName } from '@rocket.chat/icons'; import { useTranslation } from '@rocket.chat/ui-contexts'; import type { ReactNode, ReactElement, ComponentProps } from 'react'; import React from 'react'; @@ -12,7 +12,7 @@ type UpsellModalProps = { title: string | ReactElement; subtitle?: string | ReactElement; description?: string | ReactElement; - icon?: ComponentProps['name']; + icon?: IconName; img: ComponentProps['src']; onCancel?: () => void; onClose?: () => void; diff --git a/apps/meteor/client/components/UserInfo/UserInfoAction.tsx b/apps/meteor/client/components/UserInfo/UserInfoAction.tsx index 958606e3570e..9ed7bb5c527a 100644 --- a/apps/meteor/client/components/UserInfo/UserInfoAction.tsx +++ b/apps/meteor/client/components/UserInfo/UserInfoAction.tsx @@ -1,9 +1,10 @@ import { Button, Icon } from '@rocket.chat/fuselage'; +import type { Keys as IconName } from '@rocket.chat/icons'; import type { ReactElement, ComponentProps } from 'react'; import React from 'react'; type UserInfoActionProps = { - icon: ComponentProps['name']; + icon: IconName; } & ComponentProps; const UserInfoAction = ({ icon, label, ...props }: UserInfoActionProps): ReactElement => ( diff --git a/apps/meteor/client/components/message/content/MessageActions.tsx b/apps/meteor/client/components/message/content/MessageActions.tsx index 2ee6c110f6ac..d38fbb6c64df 100644 --- a/apps/meteor/client/components/message/content/MessageActions.tsx +++ b/apps/meteor/client/components/message/content/MessageActions.tsx @@ -1,7 +1,7 @@ import type { IMessage } from '@rocket.chat/core-typings'; -import type { IconProps } from '@rocket.chat/fuselage'; import { Box, ButtonGroup } from '@rocket.chat/fuselage'; import { useMutableCallback } from '@rocket.chat/fuselage-hooks'; +import type { Keys as IconName } from '@rocket.chat/icons'; import type { TranslationKey } from '@rocket.chat/ui-contexts'; import type { ReactElement } from 'react'; import React from 'react'; @@ -10,7 +10,7 @@ import { actionLinks } from '../../../lib/actionLinks'; import MessageAction from './actions/MessageAction'; type MessageActionOptions = { - icon: IconProps['name']; + icon: IconName; i18nLabel?: TranslationKey; label?: string; methodId: string; diff --git a/apps/meteor/client/components/message/content/actions/MessageAction.tsx b/apps/meteor/client/components/message/content/actions/MessageAction.tsx index 9bd565f40fc8..a98e605e6129 100644 --- a/apps/meteor/client/components/message/content/actions/MessageAction.tsx +++ b/apps/meteor/client/components/message/content/actions/MessageAction.tsx @@ -1,20 +1,20 @@ -import type { IconProps } from '@rocket.chat/fuselage'; import { Icon, Button } from '@rocket.chat/fuselage'; +import type { Keys as IconName } from '@rocket.chat/icons'; import type { TranslationKey } from '@rocket.chat/ui-contexts'; import { useTranslation } from '@rocket.chat/ui-contexts'; import type { ReactElement } from 'react'; import React from 'react'; -const resolveLegacyIcon = (legacyIcon: IconProps['name'] | `icon-${IconProps['name'] | 'videocam'}`): IconProps['name'] => { +const resolveLegacyIcon = (legacyIcon: IconName | `icon-${IconName | 'videocam'}`): IconName => { if (legacyIcon === 'icon-videocam') { return 'video'; } - return legacyIcon?.replace(/^icon-/, '') as IconProps['name']; + return legacyIcon?.replace(/^icon-/, '') as IconName; }; type MessageActionProps = { - icon: IconProps['name']; + icon: IconName; i18nLabel?: TranslationKey; label?: string; methodId: string; diff --git a/apps/meteor/client/lib/banners.ts b/apps/meteor/client/lib/banners.ts index dfbc9ad462ec..89310da2e3c7 100644 --- a/apps/meteor/client/lib/banners.ts +++ b/apps/meteor/client/lib/banners.ts @@ -1,7 +1,6 @@ import type { UiKitBannerPayload } from '@rocket.chat/core-typings'; import { Emitter } from '@rocket.chat/emitter'; -import type { Icon } from '@rocket.chat/fuselage'; -import type { ComponentProps } from 'react'; +import type { Keys as IconName } from '@rocket.chat/icons'; export type LegacyBannerPayload = { id: string; @@ -9,7 +8,7 @@ export type LegacyBannerPayload = { title?: string | (() => string); text?: string | (() => string); html?: string | (() => string); - icon?: ComponentProps['name']; + icon?: IconName; modifiers?: ('large' | 'danger')[]; timer?: number; action?: () => Promise | void; diff --git a/apps/meteor/client/lib/createSidebarItems.ts b/apps/meteor/client/lib/createSidebarItems.ts index 8d836cc293ce..3e8485416267 100644 --- a/apps/meteor/client/lib/createSidebarItems.ts +++ b/apps/meteor/client/lib/createSidebarItems.ts @@ -1,11 +1,11 @@ -import type { IconProps } from '@rocket.chat/fuselage'; +import type { Keys as IconName } from '@rocket.chat/icons'; import type { LocationPathname } from '@rocket.chat/ui-contexts'; import type { ReactElement } from 'react'; export type Item = { i18nLabel: string; href?: LocationPathname | `https://go.rocket.chat/i/${string}`; - icon?: IconProps['name']; + icon?: IconName; tag?: 'Alpha' | 'Beta'; permissionGranted?: () => boolean; pathSection?: string; diff --git a/apps/meteor/client/sidebar/Item/Condensed.tsx b/apps/meteor/client/sidebar/Item/Condensed.tsx index 527d38a0879c..e964958f635f 100644 --- a/apps/meteor/client/sidebar/Item/Condensed.tsx +++ b/apps/meteor/client/sidebar/Item/Condensed.tsx @@ -1,6 +1,6 @@ -import type { IconProps } from '@rocket.chat/fuselage'; import { IconButton, Sidebar } from '@rocket.chat/fuselage'; import { useMutableCallback, usePrefersReducedMotion } from '@rocket.chat/fuselage-hooks'; +import type { Keys as IconName } from '@rocket.chat/icons'; import type { FC, ReactElement } from 'react'; import React, { memo, useState } from 'react'; @@ -8,7 +8,7 @@ type CondensedProps = { title: ReactElement | string; titleIcon?: ReactElement; avatar: ReactElement | boolean; - icon?: IconProps['name']; + icon?: IconName; actions?: ReactElement; href?: string; unread?: boolean; diff --git a/apps/meteor/client/sidebar/Item/Extended.tsx b/apps/meteor/client/sidebar/Item/Extended.tsx index c896037d7f3d..c997a48b5b4a 100644 --- a/apps/meteor/client/sidebar/Item/Extended.tsx +++ b/apps/meteor/client/sidebar/Item/Extended.tsx @@ -1,13 +1,13 @@ -import type { IconProps } from '@rocket.chat/fuselage'; import { Sidebar, IconButton } from '@rocket.chat/fuselage'; import { useMutableCallback, usePrefersReducedMotion } from '@rocket.chat/fuselage-hooks'; +import type { Keys as IconName } from '@rocket.chat/icons'; import type { VFC } from 'react'; import React, { memo, useState } from 'react'; import { useShortTimeAgo } from '../../hooks/useTimeAgo'; type ExtendedProps = { - icon?: IconProps['name']; + icon?: IconName; title?: React.ReactNode; avatar?: React.ReactNode | boolean; actions?: React.ReactNode; diff --git a/apps/meteor/client/views/banners/UiKitBanner.tsx b/apps/meteor/client/views/banners/UiKitBanner.tsx index d7f55f370e48..1ea0b6bf0727 100644 --- a/apps/meteor/client/views/banners/UiKitBanner.tsx +++ b/apps/meteor/client/views/banners/UiKitBanner.tsx @@ -1,8 +1,9 @@ import type { UIKitActionEvent, UiKitBannerProps } from '@rocket.chat/core-typings'; import { Banner, Icon } from '@rocket.chat/fuselage'; import { kitContext, bannerParser, UiKitBanner as renderUiKitBannerBlocks } from '@rocket.chat/fuselage-ui-kit'; +import type { Keys as IconName } from '@rocket.chat/icons'; import type { LayoutBlock } from '@rocket.chat/ui-kit'; -import type { FC, ComponentProps, ReactElement, ContextType } from 'react'; +import type { FC, ReactElement, ContextType } from 'react'; import React, { useMemo } from 'react'; import { useUIKitHandleAction } from '../../UIKit/hooks/useUIKitHandleAction'; @@ -19,7 +20,7 @@ const UiKitBanner: FC = ({ payload }) => { const icon = useMemo(() => { if (state.icon) { - return ['name']} size={20} />; + return ; } return null; diff --git a/apps/meteor/client/views/hooks/useActionSpread.ts b/apps/meteor/client/views/hooks/useActionSpread.ts index 513956800ec9..8cf69f1b62a3 100644 --- a/apps/meteor/client/views/hooks/useActionSpread.ts +++ b/apps/meteor/client/views/hooks/useActionSpread.ts @@ -1,10 +1,10 @@ -import type { Icon } from '@rocket.chat/fuselage'; -import type { ComponentProps, ReactNode } from 'react'; +import type { Keys as IconName } from '@rocket.chat/icons'; +import type { ReactNode } from 'react'; import { useMemo } from 'react'; export type Action = { label: ReactNode; - icon?: ComponentProps['name']; + icon?: IconName; action: () => void; }; diff --git a/apps/meteor/client/views/omnichannel/agents/AgentInfoAction.tsx b/apps/meteor/client/views/omnichannel/agents/AgentInfoAction.tsx index 660cb6c3af14..9cb6e51170b7 100644 --- a/apps/meteor/client/views/omnichannel/agents/AgentInfoAction.tsx +++ b/apps/meteor/client/views/omnichannel/agents/AgentInfoAction.tsx @@ -1,10 +1,10 @@ -import type { IconProps } from '@rocket.chat/fuselage'; import { Button, Icon } from '@rocket.chat/fuselage'; +import type { Keys as IconName } from '@rocket.chat/icons'; import type { FC, HtmlHTMLAttributes } from 'react'; import React from 'react'; type AgentInfoActionProps = { - icon: IconProps['name']; + icon: IconName; label?: string; title?: string; } & Omit, 'is'>; diff --git a/apps/meteor/client/views/room/components/contextualBar/MessageListTab.tsx b/apps/meteor/client/views/room/components/contextualBar/MessageListTab.tsx index eb4a7c8dbc60..e30dd3f3cd93 100644 --- a/apps/meteor/client/views/room/components/contextualBar/MessageListTab.tsx +++ b/apps/meteor/client/views/room/components/contextualBar/MessageListTab.tsx @@ -1,9 +1,9 @@ import type { IMessage } from '@rocket.chat/core-typings'; -import type { Icon } from '@rocket.chat/fuselage'; import { Box, MessageDivider, Throbber } from '@rocket.chat/fuselage'; +import type { Keys as IconName } from '@rocket.chat/icons'; import { useTranslation, useUserPreference } from '@rocket.chat/ui-contexts'; import type { UseQueryResult } from '@tanstack/react-query'; -import type { ReactElement, ComponentProps, ReactNode } from 'react'; +import type { ReactElement, ReactNode } from 'react'; import React, { useCallback } from 'react'; import { Virtuoso } from 'react-virtuoso'; @@ -29,7 +29,7 @@ import { useRoomSubscription } from '../../contexts/RoomContext'; import { useTabBarClose } from '../../contexts/ToolboxContext'; type MessageListTabProps = { - iconName: ComponentProps['name']; + iconName: IconName; title: ReactNode; emptyResultMessage: string; context: MessageActionContext; diff --git a/apps/meteor/client/views/room/contextualBar/NotificationPreferences/components/NotificationByDevice.tsx b/apps/meteor/client/views/room/contextualBar/NotificationPreferences/components/NotificationByDevice.tsx index 85470d855902..2a6d3e9fbe39 100644 --- a/apps/meteor/client/views/room/contextualBar/NotificationPreferences/components/NotificationByDevice.tsx +++ b/apps/meteor/client/views/room/contextualBar/NotificationPreferences/components/NotificationByDevice.tsx @@ -1,10 +1,11 @@ import { Box, Accordion, Icon, FieldGroup } from '@rocket.chat/fuselage'; -import type { ComponentProps, ReactElement, ReactNode } from 'react'; +import type { Keys as IconName } from '@rocket.chat/icons'; +import type { ReactElement, ReactNode } from 'react'; import React, { memo } from 'react'; type NotificationByDeviceProps = { device: string; - icon: ComponentProps['name']; + icon: IconName; children: ReactNode; }; diff --git a/apps/meteor/client/views/room/contextualBar/OTR/components/OTRStates.tsx b/apps/meteor/client/views/room/contextualBar/OTR/components/OTRStates.tsx index e8db458ea326..0794c829b58a 100644 --- a/apps/meteor/client/views/room/contextualBar/OTR/components/OTRStates.tsx +++ b/apps/meteor/client/views/room/contextualBar/OTR/components/OTRStates.tsx @@ -1,13 +1,13 @@ -import type { Icon } from '@rocket.chat/fuselage'; import { States, StatesAction, StatesActions, StatesIcon, StatesSubtitle, StatesTitle } from '@rocket.chat/fuselage'; +import type { Keys as IconName } from '@rocket.chat/icons'; import { useTranslation } from '@rocket.chat/ui-contexts'; -import type { ReactElement, ComponentProps } from 'react'; +import type { ReactElement } from 'react'; import React from 'react'; type OTRStatesProps = { title: string; description: string; - icon: ComponentProps['name']; + icon: IconName; onClickStart: () => void; }; diff --git a/apps/meteor/client/views/room/lib/Toolbox/index.tsx b/apps/meteor/client/views/room/lib/Toolbox/index.tsx index 6dde302f7e93..e4cecc3b95ca 100644 --- a/apps/meteor/client/views/room/lib/Toolbox/index.tsx +++ b/apps/meteor/client/views/room/lib/Toolbox/index.tsx @@ -1,5 +1,6 @@ import type { IRoom } from '@rocket.chat/core-typings'; -import type { Box, Option, Icon } from '@rocket.chat/fuselage'; +import type { Box, Option } from '@rocket.chat/fuselage'; +import type { Keys as IconName } from '@rocket.chat/icons'; import type { TranslationKey } from '@rocket.chat/ui-contexts'; import type { ReactNode, MouseEvent, ComponentProps, ComponentType } from 'react'; @@ -23,7 +24,7 @@ export type OptionRenderer = (props: OptionRendererProps) => ReactNode; export type ToolboxActionConfig = { 'id': string; - 'icon'?: ComponentProps['name']; + 'icon'?: IconName; 'title': TranslationKey; 'anonymous'?: boolean; 'data-tooltip'?: string; diff --git a/apps/meteor/client/views/room/webdav/WebdavFilePickerModal/WebdavFilePickerGrid/WebdavFilePickerGrid.tsx b/apps/meteor/client/views/room/webdav/WebdavFilePickerModal/WebdavFilePickerGrid/WebdavFilePickerGrid.tsx index fda0e0da1107..5b6baf7c76a6 100644 --- a/apps/meteor/client/views/room/webdav/WebdavFilePickerModal/WebdavFilePickerGrid/WebdavFilePickerGrid.tsx +++ b/apps/meteor/client/views/room/webdav/WebdavFilePickerModal/WebdavFilePickerGrid/WebdavFilePickerGrid.tsx @@ -1,7 +1,7 @@ import type { IWebdavNode } from '@rocket.chat/core-typings'; import { css } from '@rocket.chat/css-in-js'; import { Box, Icon, Skeleton, Palette } from '@rocket.chat/fuselage'; -import type { ReactElement, ComponentProps } from 'react'; +import type { ReactElement } from 'react'; import React from 'react'; import GenericNoResults from '../../../../../components/GenericNoResults'; @@ -38,7 +38,7 @@ const WebdavFilePickerGrid = ({ webdavNodes, onNodeClick, isLoading }: WebdavFil return ( onNodeClick(webdavNode)}> - ['name']} /> + {webdavNode.basename} ); diff --git a/apps/meteor/client/views/room/webdav/WebdavFilePickerModal/WebdavFilePickerTable.tsx b/apps/meteor/client/views/room/webdav/WebdavFilePickerModal/WebdavFilePickerTable.tsx index 0345058448e8..e8fc95c45337 100644 --- a/apps/meteor/client/views/room/webdav/WebdavFilePickerModal/WebdavFilePickerTable.tsx +++ b/apps/meteor/client/views/room/webdav/WebdavFilePickerModal/WebdavFilePickerTable.tsx @@ -1,7 +1,7 @@ import type { IWebdavNode } from '@rocket.chat/core-typings'; import { Box, Icon } from '@rocket.chat/fuselage'; import { useTranslation } from '@rocket.chat/ui-contexts'; -import type { ReactElement, ComponentProps } from 'react'; +import type { ReactElement } from 'react'; import React from 'react'; import GenericNoResults from '../../../../components/GenericNoResults'; @@ -78,7 +78,7 @@ const WebdavFilePickerTable = ({ return ( onNodeClick(webdavNode)} tabIndex={index} role='link' action> - ['name']} /> + {webdavNode.basename} diff --git a/apps/meteor/client/views/room/webdav/WebdavFilePickerModal/lib/getNodeIconType.ts b/apps/meteor/client/views/room/webdav/WebdavFilePickerModal/lib/getNodeIconType.ts index 5ecf53cfa77d..13c9e506dab0 100644 --- a/apps/meteor/client/views/room/webdav/WebdavFilePickerModal/lib/getNodeIconType.ts +++ b/apps/meteor/client/views/room/webdav/WebdavFilePickerModal/lib/getNodeIconType.ts @@ -1,5 +1,11 @@ -export const getNodeIconType = (basename: string, fileType: string, mime?: string): { icon: string; type: string; extension?: string } => { - let icon = 'clip'; +import type { Keys as IconName } from '@rocket.chat/icons'; + +export const getNodeIconType = ( + basename: string, + fileType: string, + mime?: string, +): { icon: IconName; type: string; extension?: string } => { + let icon: IconName = 'clip'; let type = ''; let extension = basename?.split('.').pop(); diff --git a/apps/meteor/client/views/teams/contextualBar/info/TeamsInfo.tsx b/apps/meteor/client/views/teams/contextualBar/info/TeamsInfo.tsx index 501baf796f7d..16b75e8134f3 100644 --- a/apps/meteor/client/views/teams/contextualBar/info/TeamsInfo.tsx +++ b/apps/meteor/client/views/teams/contextualBar/info/TeamsInfo.tsx @@ -1,8 +1,7 @@ import type { IRoom } from '@rocket.chat/core-typings'; -import type { Icon } from '@rocket.chat/fuselage'; import { Box, Button, Callout, Option, Menu } from '@rocket.chat/fuselage'; import { useTranslation } from '@rocket.chat/ui-contexts'; -import type { ReactElement, ComponentProps } from 'react'; +import type { ReactElement } from 'react'; import React, { useMemo } from 'react'; import { @@ -118,7 +117,7 @@ const TeamsInfo = ({ const actions = useMemo(() => { const mapAction = ([key, { label, icon, action }]: [string, Action]): ReactElement => ( - ['name']} /> + ); return [...actionsDefinition.map(mapAction), menu].filter(Boolean); diff --git a/apps/meteor/definition/IRoomTypeConfig.ts b/apps/meteor/definition/IRoomTypeConfig.ts index 1cf746ad1fe1..a5910aeb6e86 100644 --- a/apps/meteor/definition/IRoomTypeConfig.ts +++ b/apps/meteor/definition/IRoomTypeConfig.ts @@ -9,8 +9,7 @@ import type { ISubscription, IOmnichannelRoom, } from '@rocket.chat/core-typings'; -import type { ComponentProps } from 'react'; -import type { Icon } from '@rocket.chat/fuselage'; +import type { Keys as IconName } from '@rocket.chat/icons'; import type { IRouterPaths, RouteName } from '@rocket.chat/ui-contexts'; export type RoomIdentification = { rid?: IRoom['_id']; name?: string; tab?: string }; @@ -81,7 +80,7 @@ export interface IRoomTypeClientDirectives { getAvatarPath: ( room: AtLeast & { username?: IRoom['_id'] }, ) => string; - getIcon?: (room: Partial) => ComponentProps['name']; + getIcon?: (room: Partial) => IconName; extractOpenRoomParams?: (routeParams: Record) => { type: RoomType; reference: string }; findRoom: (identifier: string) => IRoom | undefined; showJoinLink: (roomId: string) => boolean; diff --git a/apps/meteor/ee/client/deviceManagement/components/DeviceIcon.tsx b/apps/meteor/ee/client/deviceManagement/components/DeviceIcon.tsx index a1ae3f49891c..efc4a249c44f 100644 --- a/apps/meteor/ee/client/deviceManagement/components/DeviceIcon.tsx +++ b/apps/meteor/ee/client/deviceManagement/components/DeviceIcon.tsx @@ -1,8 +1,9 @@ import { Box, Icon } from '@rocket.chat/fuselage'; -import type { ComponentProps, ReactElement } from 'react'; +import type { Keys as IconName } from '@rocket.chat/icons'; +import type { ReactElement } from 'react'; import React from 'react'; -const iconMap: Record['name']> = { +const iconMap: Record = { 'browser': 'desktop', 'mobile': 'mobile', 'desktop-app': 'desktop', diff --git a/packages/ui-client/src/components/Header/HeaderIcon.tsx b/packages/ui-client/src/components/Header/HeaderIcon.tsx index 0cf8edf9358c..055e5d74eff5 100644 --- a/packages/ui-client/src/components/Header/HeaderIcon.tsx +++ b/packages/ui-client/src/components/Header/HeaderIcon.tsx @@ -1,8 +1,9 @@ import { Box, Icon } from '@rocket.chat/fuselage'; -import type { ComponentProps, FC, ReactElement } from 'react'; +import type { Keys as IconName } from '@rocket.chat/icons'; +import type { FC, ReactElement } from 'react'; import { isValidElement } from 'react'; -type HeaderIconProps = { icon: ReactElement | { name: ComponentProps['name']; color?: string } | null }; +type HeaderIconProps = { icon: ReactElement | { name: IconName; color?: string } | null }; const HeaderIcon: FC = ({ icon }) => icon && ( diff --git a/packages/ui-video-conf/src/VideoConfButton.tsx b/packages/ui-video-conf/src/VideoConfButton.tsx index 8ca73c154146..9273b4b1c559 100644 --- a/packages/ui-video-conf/src/VideoConfButton.tsx +++ b/packages/ui-video-conf/src/VideoConfButton.tsx @@ -1,8 +1,9 @@ -import { Button, Icon, IconProps } from '@rocket.chat/fuselage'; +import { Button, Icon } from '@rocket.chat/fuselage'; +import type { Keys as IconName } from '@rocket.chat/icons'; import type { ReactNode, ReactElement, ButtonHTMLAttributes } from 'react'; type VideoConfButtonProps = { - icon?: IconProps['name']; + icon?: IconName; primary?: boolean; secondary?: boolean; danger?: boolean; diff --git a/packages/ui-video-conf/src/VideoConfController.tsx b/packages/ui-video-conf/src/VideoConfController.tsx index 3dcb991818eb..b13ce13df55f 100644 --- a/packages/ui-video-conf/src/VideoConfController.tsx +++ b/packages/ui-video-conf/src/VideoConfController.tsx @@ -1,10 +1,10 @@ import { IconButton } from '@rocket.chat/fuselage'; -import type { IconProps } from '@rocket.chat/fuselage'; import { useUniqueId } from '@rocket.chat/fuselage-hooks'; +import type { Keys as IconName } from '@rocket.chat/icons'; import type { ReactElement, ButtonHTMLAttributes } from 'react'; type VideoConfControllerProps = { - icon: IconProps['name']; + icon: IconName; active?: boolean; secondary?: boolean; disabled?: boolean; diff --git a/packages/web-ui-registration/src/LoginServicesButton.tsx b/packages/web-ui-registration/src/LoginServicesButton.tsx index 2390f6b25a68..8f8a8d3ecd56 100644 --- a/packages/web-ui-registration/src/LoginServicesButton.tsx +++ b/packages/web-ui-registration/src/LoginServicesButton.tsx @@ -1,8 +1,9 @@ import { Button, Icon } from '@rocket.chat/fuselage'; import type { LoginService } from '@rocket.chat/ui-contexts'; import { useLoginWithService, useTranslation } from '@rocket.chat/ui-contexts'; -import type { ReactElement, ComponentProps, SetStateAction, Dispatch } from 'react'; +import type { ReactElement, SetStateAction, Dispatch } from 'react'; import { useCallback } from 'react'; +import type { Keys as IconName } from '@rocket.chat/icons'; import type { LoginErrors } from './LoginForm'; @@ -43,7 +44,7 @@ const LoginServicesButton = ({ display='flex' justifyContent='center' > - {icon && ['name']} />} + {icon && } {buttonLabelText || t('Sign_in_with__provider__', { provider: title })} ); From ad08c26b465ec3bac3c73377c0d10c160a0aeacf Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Henrique=20Guimar=C3=A3es=20Ribeiro?= Date: Thu, 20 Jul 2023 23:23:21 -0300 Subject: [PATCH 033/342] feat: Create upsell options in the admin sidebar and reorganize it (#29731) --- .changeset/tidy-bears-camp.md | 5 + .../server/constant/permissions.ts | 2 + .../client/components/GenericUpsellModal.tsx | 105 +++++++++++++++ .../client/components/UpsellModal.stories.tsx | 12 +- apps/meteor/client/components/UpsellModal.tsx | 75 ----------- .../views/admin/AdministrationRouter.tsx | 2 +- .../admin/customEmoji/CustomEmojiRoute.tsx | 2 +- .../admin/customSounds/CustomSoundsPage.tsx | 2 +- .../FederationDashboardPage.tsx | 2 +- .../views/admin/info/InformationPage.tsx | 2 +- .../views/admin/info/InformationRoute.tsx | 2 +- .../moderation/ModerationConsolePage.tsx | 2 +- .../views/admin/oauthApps/OAuthAppsPage.tsx | 2 +- apps/meteor/client/views/admin/routes.tsx | 66 ++++++---- .../meteor/client/views/admin/sidebarItems.ts | 122 ++++++++++-------- .../views/admin/viewLogs/ViewLogsPage.tsx | 2 +- .../marketplace/UnlimitedAppsUpsellModal.tsx | 31 +---- .../components/MarketplaceHeader.tsx | 3 +- .../ee/client/startup/deviceManagement.ts | 21 --- .../ee/client/startup/engagementDashboard.ts | 39 ------ apps/meteor/ee/client/startup/index.ts | 1 - .../DeviceManagementAdminRoute.tsx | 47 ++++++- .../EngagementDashboardPage.tsx | 2 +- .../EngagementDashboardRoute.tsx | 83 ++++++++---- .../ee/server/lib/deviceManagement/startup.ts | 1 - .../server/lib/engagementDashboard/startup.ts | 6 - .../ee/server/startup/engagementDashboard.ts | 3 +- .../rocketchat-i18n/i18n/en.i18n.json | 20 +-- .../public/images/device-management.png | Bin 0 -> 40365 bytes apps/meteor/public/images/engagement.png | Bin 0 -> 42933 bytes apps/meteor/server/startup/migrations/v302.ts | 9 ++ .../tests/e2e/administration-menu.spec.ts | 2 +- apps/meteor/tests/e2e/administration.spec.ts | 4 +- 33 files changed, 367 insertions(+), 310 deletions(-) create mode 100644 .changeset/tidy-bears-camp.md create mode 100644 apps/meteor/client/components/GenericUpsellModal.tsx delete mode 100644 apps/meteor/client/components/UpsellModal.tsx delete mode 100644 apps/meteor/ee/client/startup/engagementDashboard.ts create mode 100644 apps/meteor/public/images/device-management.png create mode 100644 apps/meteor/public/images/engagement.png create mode 100644 apps/meteor/server/startup/migrations/v302.ts diff --git a/.changeset/tidy-bears-camp.md b/.changeset/tidy-bears-camp.md new file mode 100644 index 000000000000..3c2013f79023 --- /dev/null +++ b/.changeset/tidy-bears-camp.md @@ -0,0 +1,5 @@ +--- +"@rocket.chat/meteor": minor +--- + +Introduced upsells for the engagement dashboard and device management admin sidebar items in CE workspaces. Additionally, restructured the admin sidebar items to enhance organization. diff --git a/apps/meteor/app/authorization/server/constant/permissions.ts b/apps/meteor/app/authorization/server/constant/permissions.ts index 0090d10fd880..fc917028c33f 100644 --- a/apps/meteor/app/authorization/server/constant/permissions.ts +++ b/apps/meteor/app/authorization/server/constant/permissions.ts @@ -69,6 +69,8 @@ export const permissions = [ { _id: 'view-c-room', roles: ['admin', 'user', 'bot', 'app', 'anonymous'] }, { _id: 'user-generate-access-token', roles: ['admin'] }, { _id: 'view-d-room', roles: ['admin', 'user', 'bot', 'app', 'guest'] }, + { _id: 'view-device-management', roles: ['admin'] }, + { _id: 'view-engagement-dashboard', roles: ['admin'] }, { _id: 'view-full-other-user-info', roles: ['admin'] }, { _id: 'view-history', roles: ['admin', 'user', 'anonymous'] }, { _id: 'view-joined-room', roles: ['guest', 'bot', 'app', 'anonymous'] }, diff --git a/apps/meteor/client/components/GenericUpsellModal.tsx b/apps/meteor/client/components/GenericUpsellModal.tsx new file mode 100644 index 000000000000..7e24219dafb3 --- /dev/null +++ b/apps/meteor/client/components/GenericUpsellModal.tsx @@ -0,0 +1,105 @@ +import { Box, Button, Modal } from '@rocket.chat/fuselage'; +import type { Keys as IconName } from '@rocket.chat/icons'; +import { useRouter, useSetModal, useSetting, useTranslation } from '@rocket.chat/ui-contexts'; +import type { ReactNode, ReactElement, ComponentProps } from 'react'; +import React, { useCallback, useEffect, useRef } from 'react'; + +type UpsellModalProps = { + children?: ReactNode; + tagline?: ReactNode; + cancelText?: ReactNode; + confirmText?: ReactNode; + title: string | ReactElement; + subtitle?: string | ReactElement; + description?: string | ReactElement; + icon?: IconName; + img: ComponentProps['src']; + onCancel?: () => void; + onClose?: () => void; + onConfirm?: () => void; + onCloseEffect?: () => void; +}; + +const GenericUpsellModal = ({ + tagline, + title, + subtitle, + img, + cancelText, + confirmText, + icon, + description, + onCancel, + onConfirm, + onClose = onCancel, + onCloseEffect, +}: UpsellModalProps) => { + const t = useTranslation(); + const cloudWorkspaceHadTrial = Boolean(useSetting('Cloud_Workspace_Had_Trial')); + + const router = useRouter(); + const setModal = useSetModal(); + + const handleModalClose = useCallback(() => { + setModal(null); + }, [setModal]); + + const handleConfirmModal = useCallback(() => { + handleModalClose(); + router.navigate({ + pathname: '/admin/upgrade/go-fully-featured-registered', + }); + }, [handleModalClose, router]); + + const talkToSales = 'https://go.rocket.chat/i/contact-sales'; + const handleCancelModal = useCallback(() => { + handleModalClose(); + window.open(talkToSales, '_blank'); + }, [handleModalClose]); + + const onCloseRef = useRef(onCloseEffect ?? handleModalClose); + onCloseRef.current = onCloseEffect ?? handleModalClose; + + useEffect(() => { + return () => { + const onClose = onCloseRef.current; + onClose?.(); + }; + }, []); + + return ( + + + {icon && } + + {tagline ?? t('Enterprise_capability')} + {title} + + + + + + {subtitle && ( + + {subtitle} + + )} + + {description && {description}} + + + + + + + + + + ); +}; + +export default GenericUpsellModal; diff --git a/apps/meteor/client/components/UpsellModal.stories.tsx b/apps/meteor/client/components/UpsellModal.stories.tsx index 177efdab2876..0e72c7c89647 100644 --- a/apps/meteor/client/components/UpsellModal.stories.tsx +++ b/apps/meteor/client/components/UpsellModal.stories.tsx @@ -2,15 +2,15 @@ import { action } from '@storybook/addon-actions'; import type { ComponentMeta, ComponentStory } from '@storybook/react'; import React from 'react'; -import UpsellModal from './UpsellModal'; +import GenericUpsellModal from './GenericUpsellModal'; export default { - title: 'Components/UpsellModal', - component: UpsellModal, -} as ComponentMeta; + title: 'Components/GenericUpsellModal', + component: GenericUpsellModal, +} as ComponentMeta; -export const Example: ComponentStory = () => ( - = () => ( + ['src']; - onCancel?: () => void; - onClose?: () => void; - onConfirm: () => void; -}; - -const UpsellModal = ({ - tagline, - title, - subtitle, - img, - cancelText, - confirmText, - icon, - description, - onCancel, - onConfirm, - onClose = onCancel, -}: UpsellModalProps) => { - const t = useTranslation(); - - return ( - - - {icon && } - - {tagline ?? t('Enterprise_capability')} - {title} - - - - - - {subtitle && ( - - {subtitle} - - )} -
    - {description && {description}} -
    - - - {onCancel && ( - - )} - {onConfirm && ( - - )} - - -
    - ); -}; - -export default UpsellModal; diff --git a/apps/meteor/client/views/admin/AdministrationRouter.tsx b/apps/meteor/client/views/admin/AdministrationRouter.tsx index 93ea650d0d33..9a86f6ea61f7 100644 --- a/apps/meteor/client/views/admin/AdministrationRouter.tsx +++ b/apps/meteor/client/views/admin/AdministrationRouter.tsx @@ -49,7 +49,7 @@ const AdministrationRouter = ({ children }: AdministrationRouterProps): ReactEle return; } - const defaultRoutePath = getAdminSidebarItems().find(firstSidebarPage)?.href ?? '/admin/info'; + const defaultRoutePath = getAdminSidebarItems().find(firstSidebarPage)?.href ?? '/admin/workspace'; if (isGoRocketChatLink(defaultRoutePath)) { window.open(defaultRoutePath, '_blank'); diff --git a/apps/meteor/client/views/admin/customEmoji/CustomEmojiRoute.tsx b/apps/meteor/client/views/admin/customEmoji/CustomEmojiRoute.tsx index 848e0421a014..26b74063e025 100644 --- a/apps/meteor/client/views/admin/customEmoji/CustomEmojiRoute.tsx +++ b/apps/meteor/client/views/admin/customEmoji/CustomEmojiRoute.tsx @@ -45,7 +45,7 @@ const CustomEmojiRoute = (): ReactElement => { return ( - + diff --git a/apps/meteor/client/views/admin/customSounds/CustomSoundsPage.tsx b/apps/meteor/client/views/admin/customSounds/CustomSoundsPage.tsx index 1225d6789592..82191963cbd2 100644 --- a/apps/meteor/client/views/admin/customSounds/CustomSoundsPage.tsx +++ b/apps/meteor/client/views/admin/customSounds/CustomSoundsPage.tsx @@ -41,7 +41,7 @@ const CustomSoundsPage = () => { return ( - + diff --git a/apps/meteor/client/views/admin/federationDashboard/FederationDashboardPage.tsx b/apps/meteor/client/views/admin/federationDashboard/FederationDashboardPage.tsx index 43d517fee432..8b33a2734a6e 100644 --- a/apps/meteor/client/views/admin/federationDashboard/FederationDashboardPage.tsx +++ b/apps/meteor/client/views/admin/federationDashboard/FederationDashboardPage.tsx @@ -12,7 +12,7 @@ function FederationDashboardPage(): ReactElement { return ( - + diff --git a/apps/meteor/client/views/admin/info/InformationPage.tsx b/apps/meteor/client/views/admin/info/InformationPage.tsx index 2bf9d5504f37..d801e2379622 100644 --- a/apps/meteor/client/views/admin/info/InformationPage.tsx +++ b/apps/meteor/client/views/admin/info/InformationPage.tsx @@ -42,7 +42,7 @@ const InformationPage = memo(function InformationPage({ return ( - + {canViewStatistics && ( - - - - - ); -}; diff --git a/apps/meteor/client/views/admin/info/FederationCard/components/FederationModal/InviteUsers.tsx b/apps/meteor/client/views/admin/info/FederationCard/components/FederationModal/InviteUsers.tsx deleted file mode 100644 index 0e35f6e5bac4..000000000000 --- a/apps/meteor/client/views/admin/info/FederationCard/components/FederationModal/InviteUsers.tsx +++ /dev/null @@ -1,44 +0,0 @@ -import { Box, ButtonGroup, Button, Banner } from '@rocket.chat/fuselage'; -import { useRouter, useTranslation } from '@rocket.chat/ui-contexts'; -import type { FC, ReactElement } from 'react'; -import React from 'react'; - -const InviteUsers: FC<{ onClose: () => void }> = ({ onClose }): ReactElement => { - const t = useTranslation(); - const router = useRouter(); - const handleDirectory = (): void => { - onClose(); - router.navigate('/directory/users'); - }; - - return ( - - - {t('Federation_Inviting_users_from_another_server')} - - {t('Federation_Search_users_you_want_to_connect')} - -
      -
    • {t('Federation_Username')}
    • -
    • {t('Federation_Email')}
    • -
    -
    - - {t('Federation_You_will_invite_users_without_login_access')} - - - - - - - {t('Federation_Invite_Users_To_Private_Rooms')} - -

    {t('Federation_Channels_Will_Be_Replicated')}

    -
    -
    - ); -}; - -export default InviteUsers; diff --git a/apps/meteor/client/views/admin/info/FederationCard/components/FederationModal/Types.ts b/apps/meteor/client/views/admin/info/FederationCard/components/FederationModal/Types.ts deleted file mode 100644 index 9bdaf131f4ee..000000000000 --- a/apps/meteor/client/views/admin/info/FederationCard/components/FederationModal/Types.ts +++ /dev/null @@ -1,36 +0,0 @@ -import type { SectionStatus } from '../Section'; - -export enum DNSRecordType { - SRV = 'srv', - TXT = 'txt', -} - -export enum DNSRecordName { - HOST = 'host', - NAME = 'name', - PORT = 'port', - PRIORITY = 'priority', - PROTOCOL = 'protocol', - SERVICE = 'service', - TARGET = 'target', - TTL = 'ttl', - WEIGHT = 'weight', -} - -export enum TXTRecordValue { - PUBLIC_KEY = DNSRecordName.HOST, - PROTOCOL = DNSRecordName.PROTOCOL, -} - -export type DNSRecord = { - status: SectionStatus; - title: string; - expectedValue: string; - value?: string; - hideErrorString?: boolean; -}; - -export type ResolvedDNS = { - [DNSRecordType.SRV]: Record; - [DNSRecordType.TXT]: Record; -}; diff --git a/apps/meteor/client/views/admin/info/FederationCard/components/FederationModal/index.ts b/apps/meteor/client/views/admin/info/FederationCard/components/FederationModal/index.ts deleted file mode 100644 index 352515dd36a8..000000000000 --- a/apps/meteor/client/views/admin/info/FederationCard/components/FederationModal/index.ts +++ /dev/null @@ -1 +0,0 @@ -export * from './FederationModal'; diff --git a/apps/meteor/client/views/admin/info/FederationCard/components/Section.tsx b/apps/meteor/client/views/admin/info/FederationCard/components/Section.tsx deleted file mode 100644 index 7b005771e085..000000000000 --- a/apps/meteor/client/views/admin/info/FederationCard/components/Section.tsx +++ /dev/null @@ -1,30 +0,0 @@ -import { Box } from '@rocket.chat/fuselage'; -import { Card } from '@rocket.chat/ui-client'; -import type { FC } from 'react'; -import React, { memo } from 'react'; - -import getStatusIcon from './SectionStatusIcon'; - -export enum SectionStatus { - UNKNOWN, - SUCCESS, - FAILED, -} - -const Section: FC<{ - status: SectionStatus; - title: string; - subtitle?: string; - children?: React.ReactNode; -}> = ({ status, title, subtitle, children }) => ( - - {getStatusIcon(status)} - - {title} - {subtitle && {subtitle}} - {children} - - -); - -export default memo(Section); diff --git a/apps/meteor/client/views/admin/info/FederationCard/components/SectionStatusIcon.tsx b/apps/meteor/client/views/admin/info/FederationCard/components/SectionStatusIcon.tsx deleted file mode 100644 index 6f925f78f898..000000000000 --- a/apps/meteor/client/views/admin/info/FederationCard/components/SectionStatusIcon.tsx +++ /dev/null @@ -1,50 +0,0 @@ -import { Box } from '@rocket.chat/fuselage'; -import type { ReactElement } from 'react'; -import React from 'react'; - -import { SectionStatus } from './Section'; - -export default function getStatusIcon(type: SectionStatus): ReactElement { - let svg; - - switch (type) { - case SectionStatus.SUCCESS: - svg = ( - - - - ); - break; - case SectionStatus.FAILED: - svg = ( - - - - ); - break; - case SectionStatus.UNKNOWN: - svg = ( - - - - ); - break; - } - - return {svg}; -} diff --git a/apps/meteor/client/views/admin/info/FederationCard/components/index.ts b/apps/meteor/client/views/admin/info/FederationCard/components/index.ts deleted file mode 100644 index 38b88b10dd4a..000000000000 --- a/apps/meteor/client/views/admin/info/FederationCard/components/index.ts +++ /dev/null @@ -1,2 +0,0 @@ -export { default as CardHeader } from './CardHeader'; -export { default as Section } from './Section'; diff --git a/apps/meteor/client/views/admin/info/FederationCard/index.ts b/apps/meteor/client/views/admin/info/FederationCard/index.ts deleted file mode 100644 index 4d1b8eb071ad..000000000000 --- a/apps/meteor/client/views/admin/info/FederationCard/index.ts +++ /dev/null @@ -1 +0,0 @@ -export { default } from './FederationCard'; diff --git a/apps/meteor/client/views/admin/info/InformationPage.tsx b/apps/meteor/client/views/admin/info/InformationPage.tsx index d801e2379622..4a9bbfd0e62e 100644 --- a/apps/meteor/client/views/admin/info/InformationPage.tsx +++ b/apps/meteor/client/views/admin/info/InformationPage.tsx @@ -5,10 +5,10 @@ import { useTranslation } from '@rocket.chat/ui-contexts'; import React, { memo } from 'react'; import SeatsCard from '../../../../ee/client/views/admin/info/SeatsCard'; +import { useSeatsCap } from '../../../../ee/client/views/admin/users/useSeatsCap'; import Page from '../../../components/Page'; import { useIsEnterprise } from '../../../hooks/useIsEnterprise'; import DeploymentCard from './DeploymentCard'; -import FederationCard from './FederationCard'; import LicenseCard from './LicenseCard'; import UsageCard from './UsageCard'; @@ -31,6 +31,9 @@ const InformationPage = memo(function InformationPage({ }: InformationPageProps) { const t = useTranslation(); + const seatsCap = useSeatsCap(); + const showSeatCap = seatsCap && seatsCap.maxActiveUsers === Infinity; + const { data } = useIsEnterprise(); if (!info) { @@ -85,21 +88,22 @@ const InformationPage = memo(function InformationPage({ )} - + - - + + + + + {!showSeatCap && ( + + + + )} - + - - - - - -
    diff --git a/apps/meteor/client/views/admin/info/LicenseCard.tsx b/apps/meteor/client/views/admin/info/LicenseCard.tsx index 916fca4c2e09..8865ed990a4a 100644 --- a/apps/meteor/client/views/admin/info/LicenseCard.tsx +++ b/apps/meteor/client/views/admin/info/LicenseCard.tsx @@ -1,4 +1,4 @@ -import { ButtonGroup, Button, Skeleton, Margins } from '@rocket.chat/fuselage'; +import { ButtonGroup, Button, Skeleton } from '@rocket.chat/fuselage'; import { useMutableCallback } from '@rocket.chat/fuselage-hooks'; import { Card } from '@rocket.chat/ui-client'; import { useSetModal, useSetting, useTranslation } from '@rocket.chat/ui-contexts'; @@ -51,29 +51,27 @@ const LicenseCard = (): ReactElement => { {t('Features')} - - {isLoading ? ( - <> - - - - - - ) : ( - <> - - - - - - - )} - + {isLoading ? ( + <> + + + + + + ) : ( + <> + + + + + + + )} - + {isAirGapped ? ( -)); diff --git a/packages/livechat/src/components/Composer/ComposerAction/index.tsx b/packages/livechat/src/components/Composer/ComposerAction/index.tsx new file mode 100644 index 000000000000..059d47e2bf8d --- /dev/null +++ b/packages/livechat/src/components/Composer/ComposerAction/index.tsx @@ -0,0 +1,26 @@ +import type { ComponentChildren } from 'preact'; +import type { CSSProperties } from 'preact/compat'; +import { memo } from 'preact/compat'; + +import { createClassName } from '../../../helpers/createClassName'; +import styles from './styles.scss'; + +type ComposerActionProps = { + text: string; + onClick: () => void; + className?: string; + style?: CSSProperties; + children?: ComponentChildren; +}; + +export const ComposerAction = memo(({ text, onClick, className, style = {}, children }: ComposerActionProps) => ( + +)); diff --git a/packages/livechat/src/components/Composer/ComposerActions/index.js b/packages/livechat/src/components/Composer/ComposerActions/index.js deleted file mode 100644 index 41e27cc013bd..000000000000 --- a/packages/livechat/src/components/Composer/ComposerActions/index.js +++ /dev/null @@ -1,8 +0,0 @@ -import { createClassName, memo } from '../../helpers'; -import styles from './styles.scss'; - -export const ComposerActions = memo(({ className, style = {}, children }) => ( -
    - {children} -
    -)); diff --git a/packages/livechat/src/components/Composer/ComposerActions/index.tsx b/packages/livechat/src/components/Composer/ComposerActions/index.tsx new file mode 100644 index 000000000000..f6b0cb8acca7 --- /dev/null +++ b/packages/livechat/src/components/Composer/ComposerActions/index.tsx @@ -0,0 +1,18 @@ +import type { ComponentChildren } from 'preact'; +import type { CSSProperties } from 'preact/compat'; +import { memo } from 'preact/compat'; + +import { createClassName } from '../../../helpers/createClassName'; +import styles from './styles.scss'; + +type ComposerActionsProps = { + className?: string; + style?: CSSProperties; + children?: ComponentChildren; +}; + +export const ComposerActions = memo(({ className, style = {}, children }: ComposerActionsProps) => ( +
    + {children} +
    +)); diff --git a/packages/livechat/src/components/Composer/index.js b/packages/livechat/src/components/Composer/index.tsx similarity index 55% rename from packages/livechat/src/components/Composer/index.js rename to packages/livechat/src/components/Composer/index.tsx index 792e0109920c..944d02311aa0 100644 --- a/packages/livechat/src/components/Composer/index.js +++ b/packages/livechat/src/components/Composer/index.tsx @@ -1,11 +1,14 @@ +import type { ComponentChildren } from 'preact'; import { Component } from 'preact'; +import type { CSSProperties } from 'preact/compat'; -import { createClassName, parse } from '../helpers'; +import { createClassName } from '../../helpers/createClassName'; +import { parse } from '../../helpers/parse'; import styles from './styles.scss'; -const findLastTextNode = (node) => { +const findLastTextNode = (node: Node): Text | null => { if (node.nodeType === Node.TEXT_NODE) { - return node; + return node as Text; } const children = node.childNodes; for (let i = children.length - 1; i >= 0; i--) { @@ -14,10 +17,11 @@ const findLastTextNode = (node) => { return textNode; } } + return null; }; -const replaceCaret = (el) => { +const replaceCaret = (el: Element) => { // Place the caret at the end of the element const target = findLastTextNode(el); // do not move caret if element was not focused @@ -27,36 +31,59 @@ const replaceCaret = (el) => { const sel = window.getSelection(); range.setStart(target, target.nodeValue.length); range.collapse(true); - sel.removeAllRanges(); - sel.addRange(range); + sel?.removeAllRanges(); + sel?.addRange(range); if (el instanceof HTMLElement) { el.focus(); } } }; -export class Composer extends Component { - handleRef = (el) => { +type ComposerProps = { + className?: string; + style?: CSSProperties; + value?: string; + onChange?: (value: string) => void; + onSubmit?: (value: string) => void; + onUpload?: (files: (File | null)[]) => void; + handleEmojiClick?: () => void; + placeholder?: string; + pre?: ComponentChildren; + post?: ComponentChildren; + notifyEmojiSelect?: (cb: (emoji: string) => void) => void; + limitTextLength?: number; +}; + +type ComposerState = { + inputLock: boolean; +}; + +export class Composer extends Component { + private el: HTMLElement | null = null; + + handleRef = (el: HTMLDivElement | null) => { this.el = el; }; - handleInput = (onChange) => () => { + handleInput = (onChange?: (value: string) => void) => () => { if (this.state.inputLock) { return; } - onChange && onChange(this.el.innerText); + onChange?.(this.el?.innerText ?? ''); }; - handleKeypress = (onSubmit) => (event) => { + handleKeypress = (onSubmit?: (value: string) => void) => (event: KeyboardEvent) => { if (event.which === 13 && !event.shiftKey) { event.preventDefault(); - onSubmit && onSubmit(this.el.innerText); - this.el.innerText = ''; + if (this.el) { + onSubmit?.(this.el.innerText); + this.el.innerText = ''; + } } }; - handlePaste = (onUpload) => async (event) => { - if (!event.clipboardData || !event.clipboardData.items) { + handlePaste = (onUpload?: (files: (File | null)[]) => void) => async (event: ClipboardEvent) => { + if (!event.clipboardData?.items) { return; } @@ -66,21 +93,21 @@ export class Composer extends Component { const files = items.filter((item) => item.kind === 'file' && /^image\//.test(item.type)).map((item) => item.getAsFile()); if (files.length) { - onUpload && onUpload(files); + onUpload?.(files); return; } const texts = await Promise.all( items .filter((item) => item.kind === 'string' && /^text\/plain/.test(item.type)) - .map((item) => new Promise((resolve) => item.getAsString(resolve))), + .map((item) => new Promise((resolve) => item.getAsString(resolve))), ); texts.forEach((text) => this.pasteText(parse(text))); }; - handleDrop = (onUpload) => async (event) => { - if (!event.dataTransfer || !event.dataTransfer.items) { + handleDrop = (onUpload?: (files: (File | null)[]) => void) => async (event: DragEvent) => { + if (!event.dataTransfer?.items) { return; } @@ -90,32 +117,36 @@ export class Composer extends Component { const files = items.filter((item) => item.kind === 'file' && /^image\//.test(item.type)).map((item) => item.getAsFile()); if (files.length) { - onUpload && onUpload(files); + onUpload?.(files); return; } const texts = await Promise.all( items .filter((item) => item.kind === 'string' && /^text\/plain/.test(item.type)) - .map((item) => new Promise((resolve) => item.getAsString(resolve))), + .map((item) => new Promise((resolve) => item.getAsString(resolve))), ); texts.forEach((text) => this.pasteText(parse(text))); }; handleClick = () => { const { handleEmojiClick } = this.props; - handleEmojiClick && handleEmojiClick(); + handleEmojiClick?.(); }; - pasteText = (plainText) => { - this.el.focus(); + pasteText = (plainText: string) => { + this.el?.focus(); if (document.queryCommandSupported('insertText')) { document.execCommand('insertText', false, plainText); return; } - const range = document.getSelection().getRangeAt(0); + const range = document.getSelection()?.getRangeAt(0); + if (!range) { + return; + } + range.deleteContents(); const textNode = document.createTextNode(plainText); range.insertNode(textNode); @@ -123,11 +154,13 @@ export class Composer extends Component { range.collapse(false); const selection = window.getSelection(); - selection.removeAllRanges(); - selection.addRange(range); + selection?.removeAllRanges(); + selection?.addRange(range); }; - constructor(props) { + private value: string | undefined; + + constructor(props: ComposerProps) { super(props); this.state = { inputLock: false, @@ -142,8 +175,8 @@ export class Composer extends Component { // we only update composer if value length changed from 0 to 1 or 1 to 0 // everything else is managed by this.el - shouldComponentUpdate({ value: nextValue }) { - const { value, limitTextLength } = this.props; + shouldComponentUpdate({ value: nextValue = '' }: ComposerProps) { + const { value = '', limitTextLength } = this.props; const nextValueEmpty = !nextValue || nextValue.length === 0; const valueEmpty = !value || value.length === 0; @@ -166,36 +199,49 @@ export class Composer extends Component { } if (this.props.value !== el.innerHTML) { - this.value = this.props.value; + this.value = this.props.value ?? ''; el.innerHTML = this.value; } replaceCaret(el); } - handleNotifyEmojiSelect(emoji) { + handleNotifyEmojiSelect(emoji: string) { + if (!this.el) { + throw new Error('Composer: handleNotifyEmojiSelect called but el is not defined'); + } + const { onChange } = this.props; const caretPosition = this.getCaretPosition(this.el); - const oldText = this.el.innerText; - const newText = `${oldText.substr(0, caretPosition)}${emoji} ${oldText.substr(caretPosition)}`; + const oldText = this.el?.innerText ?? ''; + const newText = `${oldText.slice(0, caretPosition)}${emoji} ${oldText.slice(caretPosition)}`; this.el.innerHTML = newText; this.moveCursorToEndAndFocus(caretPosition + emoji.length + 1); - onChange && onChange(this.el.innerText); + onChange?.(this.el.innerText); } - moveCursorToEndAndFocus(endIndex) { + moveCursorToEndAndFocus(endIndex: number) { + if (!this.el) { + throw new Error('Composer: moveCursorToEndAndFocus called but el is not defined'); + } + const setPos = document.createRange(); const set = window.getSelection(); setPos.setStart(this.el.childNodes[0], endIndex); setPos.collapse(true); - set.removeAllRanges(); - set.addRange(setPos); + set?.removeAllRanges(); + set?.addRange(setPos); } - getCaretPosition(element) { + getCaretPosition(element: HTMLElement) { const doc = element.ownerDocument || element.document; const win = doc.defaultView || doc.parentWindow; - if (typeof win.getSelection !== 'undefined' && win.getSelection().rangeCount > 0) { - const range = win.getSelection().getRangeAt(0); + + if (!win) { + throw new Error('Composer: getCaretPosition called but win is not defined'); + } + + if (typeof win.getSelection !== 'undefined' && (win.getSelection()?.rangeCount ?? 0) > 0) { + const range = win.getSelection()!.getRangeAt(0); const preCaretRange = range.cloneRange(); preCaretRange.selectNodeContents(element); preCaretRange.setEnd(range.endContainer, range.endOffset); @@ -203,41 +249,39 @@ export class Composer extends Component { } if (doc.selection && doc.selection.type !== 'Control') { - const textRange = doc.selection.createRange(); - const preCaretTextRange = doc.body.createTextRange(); - preCaretTextRange.moveToElementText(element); - preCaretTextRange.setEndPoint('EndToEnd', textRange); - return preCaretTextRange.text.length; + const textRange = doc.selection.createRange!(); + const preCaretTextRange = doc.body.createTextRange?.(); + preCaretTextRange?.moveToElementText?.(element); + preCaretTextRange?.setEndPoint?.('EndToEnd', textRange); + return preCaretTextRange?.text?.length ?? 0; } return 0; } - handleInputLock(locked) { + handleInputLock(locked: boolean) { this.setState({ inputLock: locked }); return 0; } - render = ({ pre, post, value, placeholder, onChange, onSubmit, onUpload, className, style }) => ( + render = ({ pre, post, value, placeholder, onChange, onSubmit, onUpload, className, style }: ComposerProps) => (
    {pre}
    { this.handleInputLock(true); }} onCompositionEnd={() => { this.handleInputLock(false); - onChange && onChange(this.el.innerText); + onChange?.(this.el?.innerText ?? ''); }} className={createClassName(styles, 'composer__input')} > diff --git a/packages/livechat/src/components/FilesDropTarget/index.js b/packages/livechat/src/components/FilesDropTarget/index.js index 2796cc79e8dc..589cdd8ecde9 100644 --- a/packages/livechat/src/components/FilesDropTarget/index.js +++ b/packages/livechat/src/components/FilesDropTarget/index.js @@ -1,6 +1,6 @@ import { Component } from 'preact'; -import { createClassName } from '../helpers'; +import { createClassName } from '../../helpers/createClassName'; import styles from './styles.scss'; const escapeForRegExp = (string) => string.replace(/[.*+?^${}()|[\]\\]/g, '\\$&'); diff --git a/packages/livechat/src/components/Footer/index.js b/packages/livechat/src/components/Footer/index.js index 78b023d5d872..9eebf7c471e3 100644 --- a/packages/livechat/src/components/Footer/index.js +++ b/packages/livechat/src/components/Footer/index.js @@ -1,8 +1,8 @@ import { RocketChatLogo } from '@rocket.chat/logo'; import { withTranslation } from 'react-i18next'; +import { createClassName } from '../../helpers/createClassName'; import { PopoverMenu } from '../Menu'; -import { createClassName } from '../helpers'; import styles from './styles.scss'; export const Footer = ({ children, className, ...props }) => ( diff --git a/packages/livechat/src/components/Form/DateInput/index.js b/packages/livechat/src/components/Form/DateInput/index.js index 1eae79fae6da..2bf0d0f24596 100644 --- a/packages/livechat/src/components/Form/DateInput/index.js +++ b/packages/livechat/src/components/Form/DateInput/index.js @@ -1,4 +1,6 @@ -import { createClassName, memo } from '../../helpers'; +import { memo } from 'preact/compat'; + +import { createClassName } from '../../../helpers/createClassName'; import styles from './styles.scss'; const DateInput = ({ name, value, placeholder, disabled, small, error, onChange, onInput, className, style = {} }) => ( diff --git a/packages/livechat/src/components/Form/FormField/index.js b/packages/livechat/src/components/Form/FormField/index.js index ba85500462e6..965fc4714ad9 100644 --- a/packages/livechat/src/components/Form/FormField/index.js +++ b/packages/livechat/src/components/Form/FormField/index.js @@ -1,6 +1,6 @@ import { cloneElement } from 'preact'; -import { createClassName } from '../../helpers'; +import { createClassName } from '../../../helpers/createClassName'; import styles from './styles.scss'; export const FormField = ({ required = false, label = '', description = '', error = '', className = undefined, style = {}, children }) => ( diff --git a/packages/livechat/src/components/Form/PasswordInput/index.js b/packages/livechat/src/components/Form/PasswordInput/index.js index dbdc592fe916..926a0a2f9821 100644 --- a/packages/livechat/src/components/Form/PasswordInput/index.js +++ b/packages/livechat/src/components/Form/PasswordInput/index.js @@ -1,4 +1,6 @@ -import { createClassName, memo } from '../../helpers'; +import { memo } from 'preact/compat'; + +import { createClassName } from '../../../helpers/createClassName'; import styles from './styles.scss'; export const PasswordInput = memo(({ name, value, placeholder, disabled, small, error, onChange, onInput, className, style = {} }) => ( diff --git a/packages/livechat/src/components/Form/SelectInput/index.js b/packages/livechat/src/components/Form/SelectInput/index.js index a92e6a19fe44..b0abc228ab3d 100644 --- a/packages/livechat/src/components/Form/SelectInput/index.js +++ b/packages/livechat/src/components/Form/SelectInput/index.js @@ -1,7 +1,7 @@ import { Component } from 'preact'; +import { createClassName } from '../../../helpers/createClassName'; import ArrowIcon from '../../../icons/arrowDown.svg'; -import { createClassName } from '../../helpers'; import styles from './styles.scss'; export class SelectInput extends Component { diff --git a/packages/livechat/src/components/Form/TextInput/index.js b/packages/livechat/src/components/Form/TextInput/index.js deleted file mode 100644 index ac8091afd63b..000000000000 --- a/packages/livechat/src/components/Form/TextInput/index.js +++ /dev/null @@ -1,33 +0,0 @@ -import { createClassName, memo } from '../../helpers'; -import styles from './styles.scss'; - -export const TextInput = memo( - ({ name, value, placeholder, disabled, small, multiline = false, rows = 1, error, onChange, onInput, className, style = {}, ...props }) => - multiline ? ( -