From 24e9dc2633afce7c7170d95ce10f3ab35183b8bf Mon Sep 17 00:00:00 2001 From: rocketchat-github-ci Date: Mon, 11 Dec 2023 12:38:18 +0000 Subject: [PATCH 001/329] Bump 6.5.1 --- .changeset/bump-patch-1702298298384.md | 5 +++++ yarn.lock | 16 ++++++++-------- 2 files changed, 13 insertions(+), 8 deletions(-) create mode 100644 .changeset/bump-patch-1702298298384.md diff --git a/.changeset/bump-patch-1702298298384.md b/.changeset/bump-patch-1702298298384.md new file mode 100644 index 00000000000..e1eaa7980af --- /dev/null +++ b/.changeset/bump-patch-1702298298384.md @@ -0,0 +1,5 @@ +--- +'@rocket.chat/meteor': patch +--- + +Bump @rocket.chat/meteor version. diff --git a/yarn.lock b/yarn.lock index 47d8ecd8a26..601e120fd54 100644 --- a/yarn.lock +++ b/yarn.lock @@ -8217,9 +8217,9 @@ __metadata: "@rocket.chat/icons": "*" "@rocket.chat/prettier-config": "*" "@rocket.chat/styled": "*" - "@rocket.chat/ui-contexts": 3.0.0-rc.14 + "@rocket.chat/ui-contexts": 3.0.0 "@rocket.chat/ui-kit": "*" - "@rocket.chat/ui-video-conf": 3.0.0-rc.14 + "@rocket.chat/ui-video-conf": 3.0.0 "@tanstack/react-query": "*" react: "*" react-dom: "*" @@ -8301,14 +8301,14 @@ __metadata: ts-jest: ~29.0.5 typescript: ~5.2.2 peerDependencies: - "@rocket.chat/core-typings": 6.5.0-rc.14 + "@rocket.chat/core-typings": 6.5.0 "@rocket.chat/css-in-js": "*" "@rocket.chat/fuselage": "*" "@rocket.chat/fuselage-tokens": "*" "@rocket.chat/message-parser": "*" "@rocket.chat/styled": "*" - "@rocket.chat/ui-client": 3.0.0-rc.14 - "@rocket.chat/ui-contexts": 3.0.0-rc.14 + "@rocket.chat/ui-client": 3.0.0 + "@rocket.chat/ui-contexts": 3.0.0 katex: "*" react: "*" languageName: unknown @@ -9470,7 +9470,7 @@ __metadata: "@rocket.chat/fuselage": "*" "@rocket.chat/fuselage-hooks": "*" "@rocket.chat/icons": "*" - "@rocket.chat/ui-contexts": 3.0.0-rc.14 + "@rocket.chat/ui-contexts": 3.0.0 react: ~17.0.2 languageName: unknown linkType: soft @@ -9623,7 +9623,7 @@ __metadata: "@rocket.chat/fuselage-hooks": "*" "@rocket.chat/icons": "*" "@rocket.chat/styled": "*" - "@rocket.chat/ui-contexts": 3.0.0-rc.14 + "@rocket.chat/ui-contexts": 3.0.0 react: ^17.0.2 react-dom: ^17.0.2 languageName: unknown @@ -9709,7 +9709,7 @@ __metadata: peerDependencies: "@rocket.chat/layout": "*" "@rocket.chat/tools": "*" - "@rocket.chat/ui-contexts": 3.0.0-rc.14 + "@rocket.chat/ui-contexts": 3.0.0 "@tanstack/react-query": "*" react: "*" react-hook-form: "*" -- GitLab From 055cb0fa3e0c3305c0f406e99de9fad0ddf4455d Mon Sep 17 00:00:00 2001 From: Matheus Barbosa Silva <36537004+matheusbsilva137@users.noreply.github.com> Date: Tue, 5 Dec 2023 12:52:29 -0300 Subject: [PATCH 002/329] fix: New `custom-roles` license module isn't properly checked (#31153) --- .changeset/fresh-radios-whisper.md | 5 +++++ .../client/views/admin/permissions/EditRolePage.tsx | 2 +- .../views/admin/permissions/EditRolePageWithData.tsx | 8 ++++---- .../views/admin/permissions/PermissionsContextBar.tsx | 9 ++++----- apps/meteor/ee/server/api/roles.ts | 4 ++-- apps/meteor/packages/rocketchat-i18n/i18n/en.i18n.json | 2 +- 6 files changed, 17 insertions(+), 13 deletions(-) create mode 100644 .changeset/fresh-radios-whisper.md diff --git a/.changeset/fresh-radios-whisper.md b/.changeset/fresh-radios-whisper.md new file mode 100644 index 00000000000..cba234524dc --- /dev/null +++ b/.changeset/fresh-radios-whisper.md @@ -0,0 +1,5 @@ +--- +"@rocket.chat/meteor": patch +--- + +Fixed issue with the new `custom-roles` license module not being checked throughout the application diff --git a/apps/meteor/client/views/admin/permissions/EditRolePage.tsx b/apps/meteor/client/views/admin/permissions/EditRolePage.tsx index b7948d57125..8980d64d17e 100644 --- a/apps/meteor/client/views/admin/permissions/EditRolePage.tsx +++ b/apps/meteor/client/views/admin/permissions/EditRolePage.tsx @@ -74,7 +74,7 @@ const EditRolePage = ({ role, isEnterprise }: { role?: IRole; isEnterprise: bool } }; - const deleteRoleMessage = isEnterprise ? t('Delete_Role_Warning') : t('Delete_Role_Warning_Community_Edition'); + const deleteRoleMessage = isEnterprise ? t('Delete_Role_Warning') : t('Delete_Role_Warning_Not_Enterprise'); setModal( {t('error-invalid-role')}; } - if (isLoading || !data) { + if (hasCustomRolesModule === 'loading') { return ; } - return ; + return ; }; export default EditRolePageWithData; diff --git a/apps/meteor/client/views/admin/permissions/PermissionsContextBar.tsx b/apps/meteor/client/views/admin/permissions/PermissionsContextBar.tsx index b472e57d7ea..76759306754 100644 --- a/apps/meteor/client/views/admin/permissions/PermissionsContextBar.tsx +++ b/apps/meteor/client/views/admin/permissions/PermissionsContextBar.tsx @@ -3,8 +3,8 @@ import { useRouteParameter, useRoute, useTranslation, useSetModal } from '@rocke import type { ReactElement } from 'react'; import React, { useEffect } from 'react'; +import { useHasLicenseModule } from '../../../../ee/client/hooks/useHasLicenseModule'; import { Contextualbar, ContextualbarHeader, ContextualbarTitle, ContextualbarClose } from '../../../components/Contextualbar'; -import { useIsEnterprise } from '../../../hooks/useIsEnterprise'; import CustomRoleUpsellModal from './CustomRoleUpsellModal'; import EditRolePageWithData from './EditRolePageWithData'; @@ -14,21 +14,20 @@ const PermissionsContextBar = (): ReactElement | null => { const context = useRouteParameter('context'); const router = useRoute('admin-permissions'); const setModal = useSetModal(); - const { data } = useIsEnterprise(); - const isEnterprise = !!data?.isEnterprise; + const hasCustomRolesModule = useHasLicenseModule('custom-roles') === true; const handleCloseContextualbar = useMutableCallback(() => { router.push({}); }); useEffect(() => { - if (context !== 'new' || isEnterprise) { + if (context !== 'new' || hasCustomRolesModule) { return; } setModal( setModal(null)} />); handleCloseContextualbar(); - }, [context, isEnterprise, handleCloseContextualbar, setModal]); + }, [context, hasCustomRolesModule, handleCloseContextualbar, setModal]); return ( (context && ( diff --git a/apps/meteor/ee/server/api/roles.ts b/apps/meteor/ee/server/api/roles.ts index c10c32c3ee1..7a71613b354 100644 --- a/apps/meteor/ee/server/api/roles.ts +++ b/apps/meteor/ee/server/api/roles.ts @@ -96,7 +96,7 @@ API.v1.addRoute( { authRequired: true }, { async post() { - if (!License.hasValidLicense()) { + if (!License.hasModule('custom-roles')) { throw new Meteor.Error('error-action-not-allowed', 'This is an enterprise feature'); } @@ -154,7 +154,7 @@ API.v1.addRoute( const role = await Roles.findOne(roleId); - if (!License.hasValidLicense() && !role?.protected) { + if (!License.hasModule('custom-roles') && !role?.protected) { throw new Meteor.Error('error-action-not-allowed', 'This is an enterprise feature'); } diff --git a/apps/meteor/packages/rocketchat-i18n/i18n/en.i18n.json b/apps/meteor/packages/rocketchat-i18n/i18n/en.i18n.json index 8bcb4239464..5045e7127f6 100644 --- a/apps/meteor/packages/rocketchat-i18n/i18n/en.i18n.json +++ b/apps/meteor/packages/rocketchat-i18n/i18n/en.i18n.json @@ -1558,7 +1558,7 @@ "Delete_message": "Delete message", "Delete_my_account": "Delete my account", "Delete_Role_Warning": "This cannot be undone", - "Delete_Role_Warning_Community_Edition": "This cannot be undone. Note that it's not possible to create new custom roles in a Community workspace", + "Delete_Role_Warning_Not_Enterprise": "This cannot be undone. You won't be able to create a new custom role, since that feature is no longer available for your current plan.", "Delete_Room_Warning": "Deleting a room will delete all messages posted within the room. This cannot be undone.", "Delete_User_Warning": "Deleting a user will delete all messages from that user as well. This cannot be undone.", "Delete_User_Warning_Delete": "Deleting a user will delete all messages from that user as well. This cannot be undone.", -- GitLab From f2699e4988413a8e5a5e51895767cf226f58e07b Mon Sep 17 00:00:00 2001 From: Yash Rajpal <58601732+yash-rajpal@users.noreply.github.com> Date: Wed, 22 Nov 2023 20:50:45 +0530 Subject: [PATCH 003/329] fix: Desktop notification routing for direct rooms (#30816) --- .changeset/spicy-kiwis-argue.md | 5 +++ .../server/functions/notifications/desktop.ts | 6 ++-- .../app/ui/client/lib/KonchatNotification.ts | 34 +++++++------------ .../PreferencesNotificationsSection.tsx | 3 +- apps/meteor/definition/IRoomTypeConfig.ts | 2 +- .../server/lib/rooms/roomCoordinator.ts | 10 +++--- .../server/lib/rooms/roomTypes/direct.ts | 6 +++- .../server/lib/rooms/roomTypes/livechat.ts | 2 +- .../meteor/server/lib/rooms/roomTypes/voip.ts | 2 +- packages/core-typings/src/INotification.ts | 1 + 10 files changed, 38 insertions(+), 33 deletions(-) create mode 100644 .changeset/spicy-kiwis-argue.md diff --git a/.changeset/spicy-kiwis-argue.md b/.changeset/spicy-kiwis-argue.md new file mode 100644 index 00000000000..520fdfe2201 --- /dev/null +++ b/.changeset/spicy-kiwis-argue.md @@ -0,0 +1,5 @@ +--- +'@rocket.chat/meteor': patch +--- + +Fix desktop notification routing for direct rooms diff --git a/apps/meteor/app/lib/server/functions/notifications/desktop.ts b/apps/meteor/app/lib/server/functions/notifications/desktop.ts index ac43cce997b..2d06cc51f22 100644 --- a/apps/meteor/app/lib/server/functions/notifications/desktop.ts +++ b/apps/meteor/app/lib/server/functions/notifications/desktop.ts @@ -30,7 +30,9 @@ export async function notifyDesktopUser({ duration: number; notificationMessage: string; }): Promise { - const { title, text } = await roomCoordinator.getRoomDirectives(room.t).getNotificationDetails(room, user, notificationMessage, userId); + const { title, text, name } = await roomCoordinator + .getRoomDirectives(room.t) + .getNotificationDetails(room, user, notificationMessage, userId); const payload = { title: title || '', @@ -42,11 +44,11 @@ export async function notifyDesktopUser({ tmid: message.tmid, sender: message.u, type: room.t, - name: room.name, message: { msg: message.msg, t: message.t, }, + name, }, }; diff --git a/apps/meteor/app/ui/client/lib/KonchatNotification.ts b/apps/meteor/app/ui/client/lib/KonchatNotification.ts index 1af8ca18d33..dbffdf2a80a 100644 --- a/apps/meteor/app/ui/client/lib/KonchatNotification.ts +++ b/apps/meteor/app/ui/client/lib/KonchatNotification.ts @@ -1,4 +1,4 @@ -import type { IMessage, IRoom, IUser, RoomType } from '@rocket.chat/core-typings'; +import type { INotificationDesktop, IRoom, IUser } from '@rocket.chat/core-typings'; import { Random } from '@rocket.chat/random'; import { Meteor } from 'meteor/meteor'; import { ReactiveVar } from 'meteor/reactive-var'; @@ -22,25 +22,6 @@ declare global { } } -export type NotificationEvent = { - icon?: string; - title: string; - text: string; - duration?: number; - payload: { - _id?: IMessage['_id']; - rid?: IRoom['_id']; - tmid?: IMessage['_id']; - sender?: Pick; - type?: RoomType; - name?: string; - message?: { - msg: string; - t?: string; - }; - }; -}; - class KonchatNotification { public notificationStatus = new ReactiveVar(undefined); @@ -52,7 +33,7 @@ class KonchatNotification { } } - public async notify(notification: NotificationEvent) { + public async notify(notification: INotificationDesktop) { if (typeof window.Notification === 'undefined' || Notification.permission !== 'granted') { return; } @@ -146,11 +127,20 @@ class KonchatNotification { }, search: { ...router.getSearchParameters(), jump: notification.payload._id }, }); + case 'l': + return router.navigate({ + pattern: '/live/:id/:tab?/:context?', + params: { + id: notification.payload.rid, + tab: 'room-info', + }, + search: { ...router.getSearchParameters(), jump: notification.payload._id }, + }); } }; } - public async showDesktop(notification: NotificationEvent) { + public async showDesktop(notification: INotificationDesktop) { if (!notification.payload.rid) { return; } diff --git a/apps/meteor/client/views/account/preferences/PreferencesNotificationsSection.tsx b/apps/meteor/client/views/account/preferences/PreferencesNotificationsSection.tsx index 2643eab0ebc..1dfcdf917c2 100644 --- a/apps/meteor/client/views/account/preferences/PreferencesNotificationsSection.tsx +++ b/apps/meteor/client/views/account/preferences/PreferencesNotificationsSection.tsx @@ -1,3 +1,4 @@ +import type { INotificationDesktop } from '@rocket.chat/core-typings'; import type { SelectOption } from '@rocket.chat/fuselage'; import { Accordion, Field, FieldLabel, FieldRow, FieldHint, Select, FieldGroup, ToggleSwitch, Button, Box } from '@rocket.chat/fuselage'; import { useUniqueId } from '@rocket.chat/fuselage-hooks'; @@ -45,7 +46,7 @@ const PreferencesNotificationsSection = () => { const onSendNotification = useCallback(() => { KonchatNotification.notify({ - payload: { sender: { _id: 'rocket.cat', username: 'rocket.cat' } }, + payload: { sender: { _id: 'rocket.cat', username: 'rocket.cat' }, rid: 'GENERAL' } as INotificationDesktop['payload'], title: t('Desktop_Notification_Test'), text: t('This_is_a_desktop_notification'), }); diff --git a/apps/meteor/definition/IRoomTypeConfig.ts b/apps/meteor/definition/IRoomTypeConfig.ts index a5910aeb6e8..8ac27be14ca 100644 --- a/apps/meteor/definition/IRoomTypeConfig.ts +++ b/apps/meteor/definition/IRoomTypeConfig.ts @@ -105,7 +105,7 @@ export interface IRoomTypeServerDirectives { sender: AtLeast, notificationMessage: string, userId: string, - ) => Promise<{ title: string | undefined; text: string }>; + ) => Promise<{ title: string | undefined; text: string; name: string | undefined }>; getMsgSender: (senderId: IUser['_id']) => Promise; includeInRoomSearch: () => boolean; getReadReceiptsExtraData: (message: IMessage) => Partial; diff --git a/apps/meteor/server/lib/rooms/roomCoordinator.ts b/apps/meteor/server/lib/rooms/roomCoordinator.ts index d291f036542..c640b8fec4b 100644 --- a/apps/meteor/server/lib/rooms/roomCoordinator.ts +++ b/apps/meteor/server/lib/rooms/roomCoordinator.ts @@ -3,6 +3,7 @@ import { Users } from '@rocket.chat/models'; import { settings } from '../../../app/settings/server'; import type { IRoomTypeConfig, IRoomTypeServerDirectives, RoomSettingsEnum, RoomMemberActions } from '../../../definition/IRoomTypeConfig'; +import { getUserDisplayName } from '../../../lib/getUserDisplayName'; import { RoomCoordinator } from '../../../lib/rooms/coordinator'; class RoomCoordinatorServer extends RoomCoordinator { @@ -40,13 +41,14 @@ class RoomCoordinatorServer extends RoomCoordinator { sender: AtLeast, notificationMessage: string, userId: string, - ): Promise<{ title: string | undefined; text: string }> { + ): Promise<{ title: string | undefined; text: string; name: string | undefined }> { const title = `#${await this.roomName(room, userId)}`; - const name = settings.get('UI_Use_Real_Name') ? sender.name : sender.username; + const useRealName = settings.get('UI_Use_Real_Name'); + const senderName = getUserDisplayName(sender.name, sender.username, useRealName); - const text = `${name}: ${notificationMessage}`; + const text = `${senderName}: ${notificationMessage}`; - return { title, text }; + return { title, text, name: room.name }; }, getMsgSender(senderId: IUser['_id']): Promise { return Users.findOneById(senderId); diff --git a/apps/meteor/server/lib/rooms/roomTypes/direct.ts b/apps/meteor/server/lib/rooms/roomTypes/direct.ts index ad1913345b8..4f28d93040e 100644 --- a/apps/meteor/server/lib/rooms/roomTypes/direct.ts +++ b/apps/meteor/server/lib/rooms/roomTypes/direct.ts @@ -94,16 +94,20 @@ roomCoordinator.add(DirectMessageRoomType, { async getNotificationDetails(room, sender, notificationMessage, userId) { const useRealName = settings.get('UI_Use_Real_Name'); + const displayRoomName = await this.roomName(room, userId); + if (this.isGroupChat(room)) { return { - title: await this.roomName(room, userId), + title: displayRoomName, text: `${(useRealName && sender.name) || sender.username}: ${notificationMessage}`, + name: room.name || displayRoomName, }; } return { title: (useRealName && sender.name) || sender.username, text: notificationMessage, + name: room.name || displayRoomName, }; }, diff --git a/apps/meteor/server/lib/rooms/roomTypes/livechat.ts b/apps/meteor/server/lib/rooms/roomTypes/livechat.ts index 92d722ac2bb..fb1415b02f8 100644 --- a/apps/meteor/server/lib/rooms/roomTypes/livechat.ts +++ b/apps/meteor/server/lib/rooms/roomTypes/livechat.ts @@ -35,7 +35,7 @@ roomCoordinator.add(LivechatRoomType, { const title = `[Omnichannel] ${roomName}`; const text = notificationMessage; - return { title, text }; + return { title, text, name: roomName }; }, async getMsgSender(senderId) { diff --git a/apps/meteor/server/lib/rooms/roomTypes/voip.ts b/apps/meteor/server/lib/rooms/roomTypes/voip.ts index f3a7fdb123b..7925cd65e30 100644 --- a/apps/meteor/server/lib/rooms/roomTypes/voip.ts +++ b/apps/meteor/server/lib/rooms/roomTypes/voip.ts @@ -16,7 +16,7 @@ roomCoordinator.add(VoipRoomType, { const title = `[Omnichannel] ${this.roomName(room, userId)}`; const text = notificationMessage; - return { title, text }; + return { title, text, name: room.name }; }, async getMsgSender(senderId) { diff --git a/packages/core-typings/src/INotification.ts b/packages/core-typings/src/INotification.ts index 46c3341eea2..11746e01ddd 100644 --- a/packages/core-typings/src/INotification.ts +++ b/packages/core-typings/src/INotification.ts @@ -51,6 +51,7 @@ export interface INotification { export interface INotificationDesktop { title: string; text: string; + icon?: string; duration?: number; payload: { _id: IMessage['_id']; -- GitLab From 65177444227196e48952b7cc7b80f077762033f5 Mon Sep 17 00:00:00 2001 From: Guilherme Gazzo Date: Fri, 8 Dec 2023 16:12:30 -0300 Subject: [PATCH 004/329] fix: remove @rocket.chat/license from ddp-client deps (#31189) --- .../supportedVersionsToken.ts | 2 +- .../client/hooks/useShouldPreventAction.ts | 2 +- .../client/lib/utils/isOverLicenseLimits.ts | 2 +- .../SubscriptionCalloutLimits.tsx | 2 +- .../components/cards/PlanCard.tsx | 2 +- .../cards/PlanCard/PlanCardPremium.tsx | 2 +- .../cards/PlanCard/PlanCardTrial.tsx | 2 +- .../ee/app/license/server/lib/getAppCount.ts | 2 +- .../license/server/license.internalService.ts | 3 ++- apps/meteor/ee/app/license/server/methods.ts | 3 ++- apps/meteor/ee/app/license/server/startup.ts | 2 +- .../meteor/ee/app/settings/server/settings.ts | 4 ++-- .../ee/client/hooks/useHasLicenseModule.ts | 2 +- apps/meteor/ee/client/lib/onToggledFeature.ts | 2 +- ...getInstallationSourceFromAppStorageItem.ts | 2 +- ee/packages/ddp-client/src/types/streams.ts | 2 +- .../license/src/MockedLicenseBuilder.ts | 7 ++---- ee/packages/license/src/deprecated.ts | 3 ++- ee/packages/license/src/events/deprecated.ts | 3 ++- ee/packages/license/src/events/emitter.ts | 4 ++-- ee/packages/license/src/events/listeners.ts | 5 ++--- .../src/events/overwriteClassOnLicense.ts | 3 ++- ee/packages/license/src/index.ts | 14 ++---------- ee/packages/license/src/isItemAllowed.ts | 4 +--- ee/packages/license/src/license.ts | 22 +++++++++++-------- ee/packages/license/src/modules.ts | 3 ++- ee/packages/license/src/showLicense.ts | 3 ++- ee/packages/license/src/tags.ts | 3 ++- ee/packages/license/src/token.ts | 3 +-- ee/packages/license/src/v2/bundles.ts | 2 +- ee/packages/license/src/v2/convertToV3.ts | 5 ++--- .../src/validation/filterBehaviorsResult.ts | 2 +- .../getCurrentValueForLicenseLimit.ts | 4 ++-- .../src/validation/getModulesToDisable.ts | 3 +-- .../src/validation/getResultingBehavior.ts | 5 +---- .../src/validation/isBehaviorsInResult.ts | 2 +- ee/packages/license/src/validation/logKind.ts | 2 +- .../license/src/validation/runValidation.ts | 5 ++--- .../src/validation/validateDefaultLimits.ts | 5 ++--- .../src/validation/validateLicenseLimits.ts | 5 ++--- .../src/validation/validateLicensePeriods.ts | 6 ++--- .../src/validation/validateLicenseUrl.ts | 5 ++--- .../license/src/validation/validateLimit.ts | 2 +- .../license/src/validation/validateLimits.ts | 5 ++--- packages/core-services/src/Events.ts | 2 +- packages/core-typings/src/index.ts | 1 + .../core-typings/src/license}/ILicenseTag.ts | 0 .../core-typings/src/license}/ILicenseV2.ts | 0 .../core-typings/src/license}/ILicenseV3.ts | 0 .../src/license}/LicenseBehavior.ts | 0 .../core-typings/src/license}/LicenseInfo.ts | 0 .../core-typings/src/license}/LicenseLimit.ts | 0 .../src/license}/LicenseModule.ts | 0 .../src/license}/LicensePeriod.ts | 0 .../src/license}/LicenseValidationOptions.ts | 0 .../core-typings/src/license}/LimitContext.ts | 3 +-- .../core-typings/src/license}/events.ts | 0 packages/core-typings/src/license/index.ts | 11 ++++++++++ packages/rest-typings/package.json | 1 - packages/rest-typings/src/v1/licenses.ts | 2 +- yarn.lock | 1 - 61 files changed, 92 insertions(+), 100 deletions(-) rename {ee/packages/license/src/definition => packages/core-typings/src/license}/ILicenseTag.ts (100%) rename {ee/packages/license/src/definition => packages/core-typings/src/license}/ILicenseV2.ts (100%) rename {ee/packages/license/src/definition => packages/core-typings/src/license}/ILicenseV3.ts (100%) rename {ee/packages/license/src/definition => packages/core-typings/src/license}/LicenseBehavior.ts (100%) rename {ee/packages/license/src/definition => packages/core-typings/src/license}/LicenseInfo.ts (100%) rename {ee/packages/license/src/definition => packages/core-typings/src/license}/LicenseLimit.ts (100%) rename {ee/packages/license/src/definition => packages/core-typings/src/license}/LicenseModule.ts (100%) rename {ee/packages/license/src/definition => packages/core-typings/src/license}/LicensePeriod.ts (100%) rename {ee/packages/license/src/definition => packages/core-typings/src/license}/LicenseValidationOptions.ts (100%) rename {ee/packages/license/src/definition => packages/core-typings/src/license}/LimitContext.ts (79%) rename {ee/packages/license/src/definition => packages/core-typings/src/license}/events.ts (100%) create mode 100644 packages/core-typings/src/license/index.ts diff --git a/apps/meteor/app/cloud/server/functions/supportedVersionsToken/supportedVersionsToken.ts b/apps/meteor/app/cloud/server/functions/supportedVersionsToken/supportedVersionsToken.ts index 0b04d10e355..dd933107d57 100644 --- a/apps/meteor/app/cloud/server/functions/supportedVersionsToken/supportedVersionsToken.ts +++ b/apps/meteor/app/cloud/server/functions/supportedVersionsToken/supportedVersionsToken.ts @@ -12,7 +12,7 @@ import { buildVersionUpdateMessage } from '../../../../version-check/server/func import { generateWorkspaceBearerHttpHeader } from '../getWorkspaceAccessToken'; import { supportedVersionsChooseLatest } from './supportedVersionsChooseLatest'; -declare module '@rocket.chat/license' { +declare module '@rocket.chat/core-typings' { interface ILicenseV3 { supportedVersions?: SignedSupportedVersions; } diff --git a/apps/meteor/client/hooks/useShouldPreventAction.ts b/apps/meteor/client/hooks/useShouldPreventAction.ts index edcbd5abe67..99b831a7928 100644 --- a/apps/meteor/client/hooks/useShouldPreventAction.ts +++ b/apps/meteor/client/hooks/useShouldPreventAction.ts @@ -1,4 +1,4 @@ -import type { LicenseLimitKind } from '@rocket.chat/license'; +import type { LicenseLimitKind } from '@rocket.chat/core-typings'; import { useLicense } from './useLicense'; diff --git a/apps/meteor/client/lib/utils/isOverLicenseLimits.ts b/apps/meteor/client/lib/utils/isOverLicenseLimits.ts index 20e0a2b7ca4..957500c1ac3 100644 --- a/apps/meteor/client/lib/utils/isOverLicenseLimits.ts +++ b/apps/meteor/client/lib/utils/isOverLicenseLimits.ts @@ -1,4 +1,4 @@ -import type { LicenseLimitKind } from '@rocket.chat/license'; +import type { LicenseLimitKind } from '@rocket.chat/core-typings'; type Limits = Record< LicenseLimitKind, diff --git a/apps/meteor/client/views/admin/subscription/SubscriptionCalloutLimits.tsx b/apps/meteor/client/views/admin/subscription/SubscriptionCalloutLimits.tsx index 074e349081b..2adc3d30603 100644 --- a/apps/meteor/client/views/admin/subscription/SubscriptionCalloutLimits.tsx +++ b/apps/meteor/client/views/admin/subscription/SubscriptionCalloutLimits.tsx @@ -1,5 +1,5 @@ +import type { LicenseBehavior } from '@rocket.chat/core-typings'; import { Callout } from '@rocket.chat/fuselage'; -import type { LicenseBehavior } from '@rocket.chat/license/src/definition/LicenseBehavior'; import { validateWarnLimit } from '@rocket.chat/license/src/validation/validateLimit'; import { ExternalLink } from '@rocket.chat/ui-client'; import React from 'react'; diff --git a/apps/meteor/client/views/admin/subscription/components/cards/PlanCard.tsx b/apps/meteor/client/views/admin/subscription/components/cards/PlanCard.tsx index c9e1e06ff8c..066a4fc1f16 100644 --- a/apps/meteor/client/views/admin/subscription/components/cards/PlanCard.tsx +++ b/apps/meteor/client/views/admin/subscription/components/cards/PlanCard.tsx @@ -1,4 +1,4 @@ -import type { ILicenseV3 } from '@rocket.chat/license'; +import type { ILicenseV3 } from '@rocket.chat/core-typings'; import type { ReactElement } from 'react'; import React from 'react'; diff --git a/apps/meteor/client/views/admin/subscription/components/cards/PlanCard/PlanCardPremium.tsx b/apps/meteor/client/views/admin/subscription/components/cards/PlanCard/PlanCardPremium.tsx index cd7227bda09..056aab08178 100644 --- a/apps/meteor/client/views/admin/subscription/components/cards/PlanCard/PlanCardPremium.tsx +++ b/apps/meteor/client/views/admin/subscription/components/cards/PlanCard/PlanCardPremium.tsx @@ -1,5 +1,5 @@ +import type { ILicenseV3 } from '@rocket.chat/core-typings'; import { Box, Icon, Skeleton } from '@rocket.chat/fuselage'; -import type { ILicenseV3 } from '@rocket.chat/license'; import { ExternalLink } from '@rocket.chat/ui-client'; import type { ReactElement } from 'react'; import React from 'react'; diff --git a/apps/meteor/client/views/admin/subscription/components/cards/PlanCard/PlanCardTrial.tsx b/apps/meteor/client/views/admin/subscription/components/cards/PlanCard/PlanCardTrial.tsx index f1d3001bd50..95ce21e6791 100644 --- a/apps/meteor/client/views/admin/subscription/components/cards/PlanCard/PlanCardTrial.tsx +++ b/apps/meteor/client/views/admin/subscription/components/cards/PlanCard/PlanCardTrial.tsx @@ -1,5 +1,5 @@ +import type { ILicenseV3 } from '@rocket.chat/core-typings'; import { Box, Tag } from '@rocket.chat/fuselage'; -import type { ILicenseV3 } from '@rocket.chat/license'; import { ExternalLink } from '@rocket.chat/ui-client'; import differenceInDays from 'date-fns/differenceInDays'; import type { ReactElement } from 'react'; diff --git a/apps/meteor/ee/app/license/server/lib/getAppCount.ts b/apps/meteor/ee/app/license/server/lib/getAppCount.ts index a05813f596b..f408143218d 100644 --- a/apps/meteor/ee/app/license/server/lib/getAppCount.ts +++ b/apps/meteor/ee/app/license/server/lib/getAppCount.ts @@ -1,5 +1,5 @@ import { Apps } from '@rocket.chat/core-services'; -import type { LicenseAppSources } from '@rocket.chat/license'; +import type { LicenseAppSources } from '@rocket.chat/core-typings'; import { getInstallationSourceFromAppStorageItem } from '../../../../../lib/apps/getInstallationSourceFromAppStorageItem'; diff --git a/apps/meteor/ee/app/license/server/license.internalService.ts b/apps/meteor/ee/app/license/server/license.internalService.ts index 6c179bf9797..9998378191d 100644 --- a/apps/meteor/ee/app/license/server/license.internalService.ts +++ b/apps/meteor/ee/app/license/server/license.internalService.ts @@ -1,6 +1,7 @@ import type { ILicense } from '@rocket.chat/core-services'; import { api, ServiceClassInternal } from '@rocket.chat/core-services'; -import { License, type LicenseModule } from '@rocket.chat/license'; +import { type LicenseModule } from '@rocket.chat/core-typings'; +import { License } from '@rocket.chat/license'; import { guestPermissions } from '../../authorization/lib/guestPermissions'; import { resetEnterprisePermissions } from '../../authorization/server/resetEnterprisePermissions'; diff --git a/apps/meteor/ee/app/license/server/methods.ts b/apps/meteor/ee/app/license/server/methods.ts index 39d14326a79..dbdfe7aea68 100644 --- a/apps/meteor/ee/app/license/server/methods.ts +++ b/apps/meteor/ee/app/license/server/methods.ts @@ -1,4 +1,5 @@ -import { License, type ILicenseTag, type LicenseModule } from '@rocket.chat/license'; +import { type ILicenseTag, type LicenseModule } from '@rocket.chat/core-typings'; +import { License } from '@rocket.chat/license'; import type { ServerMethods } from '@rocket.chat/ui-contexts'; import { check } from 'meteor/check'; import { Meteor } from 'meteor/meteor'; diff --git a/apps/meteor/ee/app/license/server/startup.ts b/apps/meteor/ee/app/license/server/startup.ts index 1f05ae27ce9..e483cb8d733 100644 --- a/apps/meteor/ee/app/license/server/startup.ts +++ b/apps/meteor/ee/app/license/server/startup.ts @@ -1,5 +1,5 @@ import { api } from '@rocket.chat/core-services'; -import type { LicenseLimitKind } from '@rocket.chat/license'; +import type { LicenseLimitKind } from '@rocket.chat/core-typings'; import { License } from '@rocket.chat/license'; import { Subscriptions, Users, Settings, LivechatVisitors } from '@rocket.chat/models'; import { wrapExceptions } from '@rocket.chat/tools'; diff --git a/apps/meteor/ee/app/settings/server/settings.ts b/apps/meteor/ee/app/settings/server/settings.ts index 76ce7c15155..d404dfa1741 100644 --- a/apps/meteor/ee/app/settings/server/settings.ts +++ b/apps/meteor/ee/app/settings/server/settings.ts @@ -1,5 +1,5 @@ -import type { ISetting, SettingValue } from '@rocket.chat/core-typings'; -import { License, type LicenseModule } from '@rocket.chat/license'; +import type { ISetting, SettingValue, LicenseModule } from '@rocket.chat/core-typings'; +import { License } from '@rocket.chat/license'; import { Settings } from '@rocket.chat/models'; import { Meteor } from 'meteor/meteor'; diff --git a/apps/meteor/ee/client/hooks/useHasLicenseModule.ts b/apps/meteor/ee/client/hooks/useHasLicenseModule.ts index 4e44029c60f..f2d75ab2374 100644 --- a/apps/meteor/ee/client/hooks/useHasLicenseModule.ts +++ b/apps/meteor/ee/client/hooks/useHasLicenseModule.ts @@ -1,4 +1,4 @@ -import type { LicenseModule } from '@rocket.chat/license'; +import type { LicenseModule } from '@rocket.chat/core-typings'; import { useLicenseBase } from '../../../client/hooks/useLicense'; diff --git a/apps/meteor/ee/client/lib/onToggledFeature.ts b/apps/meteor/ee/client/lib/onToggledFeature.ts index ae2e4ad9f4a..3f47b35bdbe 100644 --- a/apps/meteor/ee/client/lib/onToggledFeature.ts +++ b/apps/meteor/ee/client/lib/onToggledFeature.ts @@ -1,4 +1,4 @@ -import type { LicenseModule } from '@rocket.chat/license'; +import type { LicenseModule } from '@rocket.chat/core-typings'; import { QueryObserver } from '@tanstack/react-query'; import { queryClient } from '../../../client/lib/queryClient'; diff --git a/apps/meteor/lib/apps/getInstallationSourceFromAppStorageItem.ts b/apps/meteor/lib/apps/getInstallationSourceFromAppStorageItem.ts index 8ac29d19157..d8fd5a48f79 100644 --- a/apps/meteor/lib/apps/getInstallationSourceFromAppStorageItem.ts +++ b/apps/meteor/lib/apps/getInstallationSourceFromAppStorageItem.ts @@ -1,5 +1,5 @@ import type { IAppStorageItem } from '@rocket.chat/apps-engine/server/storage'; -import type { LicenseAppSources } from '@rocket.chat/license'; +import type { LicenseAppSources } from '@rocket.chat/core-typings'; /** * There have been reports of apps not being correctly migrated from versions prior to 6.0 diff --git a/ee/packages/ddp-client/src/types/streams.ts b/ee/packages/ddp-client/src/types/streams.ts index abadd53c185..d8d21bf519b 100644 --- a/ee/packages/ddp-client/src/types/streams.ts +++ b/ee/packages/ddp-client/src/types/streams.ts @@ -24,8 +24,8 @@ import type { IImportProgress, IBanner, UiKit, + LicenseLimitKind, } from '@rocket.chat/core-typings'; -import type { LicenseLimitKind } from '@rocket.chat/license'; type ClientAction = 'inserted' | 'updated' | 'removed' | 'changed'; diff --git a/ee/packages/license/src/MockedLicenseBuilder.ts b/ee/packages/license/src/MockedLicenseBuilder.ts index da9e833e06c..4c3cab7b660 100644 --- a/ee/packages/license/src/MockedLicenseBuilder.ts +++ b/ee/packages/license/src/MockedLicenseBuilder.ts @@ -1,8 +1,5 @@ -import type { ILicenseTag } from './definition/ILicenseTag'; -import type { ILicenseV3 } from './definition/ILicenseV3'; -import type { LicenseLimit } from './definition/LicenseLimit'; -import type { LicenseModule } from './definition/LicenseModule'; -import type { LicensePeriod, Timestamp } from './definition/LicensePeriod'; +import type { ILicenseTag, ILicenseV3, LicenseLimit, LicenseModule, LicensePeriod, Timestamp } from '@rocket.chat/core-typings'; + import { encrypt } from './token'; export class MockedLicenseBuilder { diff --git a/ee/packages/license/src/deprecated.ts b/ee/packages/license/src/deprecated.ts index bbd137cc812..e4d7bd22293 100644 --- a/ee/packages/license/src/deprecated.ts +++ b/ee/packages/license/src/deprecated.ts @@ -1,4 +1,5 @@ -import type { ILicenseV3, LicenseLimitKind } from './definition/ILicenseV3'; +import type { ILicenseV3, LicenseLimitKind } from '@rocket.chat/core-typings'; + import type { LicenseManager } from './license'; import { getModules } from './modules'; import { defaultLimits } from './validation/validateDefaultLimits'; diff --git a/ee/packages/license/src/events/deprecated.ts b/ee/packages/license/src/events/deprecated.ts index 8ebfe472929..b120815c0aa 100644 --- a/ee/packages/license/src/events/deprecated.ts +++ b/ee/packages/license/src/events/deprecated.ts @@ -1,4 +1,5 @@ -import type { LicenseModule } from '../definition/LicenseModule'; +import type { LicenseModule } from '@rocket.chat/core-typings'; + import type { LicenseManager } from '../license'; import { hasModule } from '../modules'; diff --git a/ee/packages/license/src/events/emitter.ts b/ee/packages/license/src/events/emitter.ts index dde10d68563..b4406abaa81 100644 --- a/ee/packages/license/src/events/emitter.ts +++ b/ee/packages/license/src/events/emitter.ts @@ -1,5 +1,5 @@ -import type { BehaviorWithContext } from '../definition/LicenseBehavior'; -import type { LicenseModule } from '../definition/LicenseModule'; +import type { BehaviorWithContext, LicenseModule } from '@rocket.chat/core-typings'; + import type { LicenseManager } from '../license'; import { logger } from '../logger'; diff --git a/ee/packages/license/src/events/listeners.ts b/ee/packages/license/src/events/listeners.ts index add2f397ba8..7b7eaf0baed 100644 --- a/ee/packages/license/src/events/listeners.ts +++ b/ee/packages/license/src/events/listeners.ts @@ -1,6 +1,5 @@ -import type { LicenseLimitKind } from '../definition/ILicenseV3'; -import type { BehaviorWithContext, LicenseBehavior } from '../definition/LicenseBehavior'; -import type { LicenseModule } from '../definition/LicenseModule'; +import type { LicenseLimitKind, LicenseModule, BehaviorWithContext, LicenseBehavior } from '@rocket.chat/core-typings'; + import type { LicenseManager } from '../license'; import { hasModule } from '../modules'; diff --git a/ee/packages/license/src/events/overwriteClassOnLicense.ts b/ee/packages/license/src/events/overwriteClassOnLicense.ts index 00a690d8f41..7ca686b58a5 100644 --- a/ee/packages/license/src/events/overwriteClassOnLicense.ts +++ b/ee/packages/license/src/events/overwriteClassOnLicense.ts @@ -1,4 +1,5 @@ -import type { LicenseModule } from '../definition/LicenseModule'; +import type { LicenseModule } from '@rocket.chat/core-typings'; + import type { LicenseManager } from '../license'; import { onLicense } from './deprecated'; diff --git a/ee/packages/license/src/index.ts b/ee/packages/license/src/index.ts index cf0bc0c74f4..cb07323d467 100644 --- a/ee/packages/license/src/index.ts +++ b/ee/packages/license/src/index.ts @@ -1,6 +1,5 @@ -import type { LicenseLimitKind } from './definition/ILicenseV3'; -import type { LicenseInfo } from './definition/LicenseInfo'; -import type { LimitContext } from './definition/LimitContext'; +import type { LicenseLimitKind, LicenseInfo, LimitContext } from '@rocket.chat/core-typings'; + import { getAppsConfig, getMaxActiveUsers, getUnmodifiedLicenseAndModules } from './deprecated'; import { onLicense } from './events/deprecated'; import { @@ -28,15 +27,6 @@ import { getCurrentValueForLicenseLimit, setLicenseLimitCounter } from './valida import { validateFormat } from './validation/validateFormat'; export { DuplicatedLicenseError } from './errors/DuplicatedLicenseError'; -export * from './definition/ILicenseTag'; -export * from './definition/ILicenseV2'; -export * from './definition/ILicenseV3'; -export * from './definition/LicenseBehavior'; -export * from './definition/LicenseInfo'; -export * from './definition/LicenseLimit'; -export * from './definition/LicenseModule'; -export * from './definition/LicensePeriod'; -export * from './definition/LimitContext'; export * from './MockedLicenseBuilder'; // eslint-disable-next-line @typescript-eslint/naming-convention diff --git a/ee/packages/license/src/isItemAllowed.ts b/ee/packages/license/src/isItemAllowed.ts index 16787cdf9c4..3eb9651a8fc 100644 --- a/ee/packages/license/src/isItemAllowed.ts +++ b/ee/packages/license/src/isItemAllowed.ts @@ -1,6 +1,4 @@ -import type { LicenseLimitKind } from './definition/ILicenseV3'; -import type { LicenseBehavior } from './definition/LicenseBehavior'; -import type { LicenseValidationOptions } from './definition/LicenseValidationOptions'; +import type { LicenseLimitKind, LicenseBehavior, LicenseValidationOptions } from '@rocket.chat/core-typings'; const isItemAllowed = (item: T, allowList?: T[]): boolean => { return !allowList || allowList.includes(item); diff --git a/ee/packages/license/src/license.ts b/ee/packages/license/src/license.ts index 5d4a33024f8..a8d8f1bca51 100644 --- a/ee/packages/license/src/license.ts +++ b/ee/packages/license/src/license.ts @@ -1,14 +1,18 @@ +import type { + ILicenseTag, + LicenseEvents, + ILicenseV2, + ILicenseV3, + LicenseLimitKind, + BehaviorWithContext, + LicenseBehavior, + LicenseInfo, + LicenseModule, + LicenseValidationOptions, + LimitContext, +} from '@rocket.chat/core-typings'; import { Emitter } from '@rocket.chat/emitter'; -import { type ILicenseTag } from './definition/ILicenseTag'; -import type { ILicenseV2 } from './definition/ILicenseV2'; -import type { ILicenseV3, LicenseLimitKind } from './definition/ILicenseV3'; -import type { BehaviorWithContext, LicenseBehavior } from './definition/LicenseBehavior'; -import type { LicenseInfo } from './definition/LicenseInfo'; -import type { LicenseModule } from './definition/LicenseModule'; -import type { LicenseValidationOptions } from './definition/LicenseValidationOptions'; -import type { LimitContext } from './definition/LimitContext'; -import type { LicenseEvents } from './definition/events'; import { getLicenseLimit } from './deprecated'; import { DuplicatedLicenseError } from './errors/DuplicatedLicenseError'; import { InvalidLicenseError } from './errors/InvalidLicenseError'; diff --git a/ee/packages/license/src/modules.ts b/ee/packages/license/src/modules.ts index 6931fb7a6a5..7a6f41c07ec 100644 --- a/ee/packages/license/src/modules.ts +++ b/ee/packages/license/src/modules.ts @@ -1,4 +1,5 @@ -import type { LicenseModule } from './definition/LicenseModule'; +import type { LicenseModule } from '@rocket.chat/core-typings'; + import { moduleRemoved, moduleValidated } from './events/emitter'; import type { LicenseManager } from './license'; diff --git a/ee/packages/license/src/showLicense.ts b/ee/packages/license/src/showLicense.ts index 3dda60a43b7..cbf70801985 100644 --- a/ee/packages/license/src/showLicense.ts +++ b/ee/packages/license/src/showLicense.ts @@ -1,4 +1,5 @@ -import type { ILicenseV3 } from './definition/ILicenseV3'; +import type { ILicenseV3 } from '@rocket.chat/core-typings'; + import type { LicenseManager } from './license'; import { getModules } from './modules'; diff --git a/ee/packages/license/src/tags.ts b/ee/packages/license/src/tags.ts index 33434cae116..d918c4cd0cf 100644 --- a/ee/packages/license/src/tags.ts +++ b/ee/packages/license/src/tags.ts @@ -1,4 +1,5 @@ -import type { ILicenseTag } from './definition/ILicenseTag'; +import type { ILicenseTag } from '@rocket.chat/core-typings'; + import { type LicenseManager } from './license'; export function addTag(this: LicenseManager, tag: ILicenseTag) { diff --git a/ee/packages/license/src/token.ts b/ee/packages/license/src/token.ts index 8e34d7cf262..09ad5a940b5 100644 --- a/ee/packages/license/src/token.ts +++ b/ee/packages/license/src/token.ts @@ -1,9 +1,8 @@ import crypto from 'crypto'; +import type { ILicenseV3 } from '@rocket.chat/core-typings'; import { verify, sign, getPairs } from '@rocket.chat/jwt'; -import type { ILicenseV3 } from './definition/ILicenseV3'; - const PUBLIC_LICENSE_KEY_V2 = 'LS0tLS1CRUdJTiBQVUJMSUMgS0VZLS0tLS0KTUlJQ0lqQU5CZ2txaGtpRzl3MEJBUUVGQUFPQ0FnOEFNSUlDQ2dLQ0FnRUFxV1Nza2Q5LzZ6Ung4a3lQY2ljcwpiMzJ3Mnd4VnV3N3lCVDk2clEvOEQreU1lQ01POXdTU3BIYS85bkZ5d293RXRpZ3B0L3dyb1BOK1ZHU3didHdQCkZYQmVxRWxCbmRHRkFsODZlNStFbGlIOEt6L2hHbkNtSk5tWHB4RUsyUkUwM1g0SXhzWVg3RERCN010eC9pcXMKY2pCL091dlNCa2ppU2xlUzdibE5JVC9kQTdLNC9DSjNvaXUwMmJMNEV4Y2xDSGVwenFOTWVQM3dVWmdweE9uZgpOT3VkOElYWUs3M3pTY3VFOEUxNTdZd3B6Q0twVmFIWDdaSmY4UXVOc09PNVcvYUlqS2wzTDYyNjkrZUlPRXJHCndPTm1hSG56Zmc5RkxwSmh6Z3BPMzhhVm43NnZENUtLakJhaldza1krNGEyZ1NRbUtOZUZxYXFPb3p5RUZNMGUKY0ZXWlZWWjNMZWg0dkVNb1lWUHlJeng5Nng4ZjIveW1QbmhJdXZRdjV3TjRmeWVwYTdFWTVVQ2NwNzF6OGtmUAo0RmNVelBBMElEV3lNaWhYUi9HNlhnUVFaNEdiL3FCQmh2cnZpSkNGemZZRGNKZ0w3RmVnRllIUDNQR0wwN1FnCnZMZXZNSytpUVpQcnhyYnh5U3FkUE9rZ3VyS2pWclhUVXI0QTlUZ2lMeUlYNVVsSnEzRS9SVjdtZk9xWm5MVGEKU0NWWEhCaHVQbG5DR1pSMDFUb1RDZktoTUcxdTBDRm5MMisxNWhDOWZxT21XdjlRa2U0M3FsSjBQZ0YzVkovWAp1eC9tVHBuazlnbmJHOUpIK21mSDM5Um9GdlROaW5Zd1NNdll6dXRWT242OXNPemR3aERsYTkwbDNBQ2g0eENWCks3Sk9YK3VIa29OdTNnMmlWeGlaVU0wQ0F3RUFBUT09Ci0tLS0tRU5EIFBVQkxJQyBLRVktLS0tLQo='; diff --git a/ee/packages/license/src/v2/bundles.ts b/ee/packages/license/src/v2/bundles.ts index 0ff1cd6c41a..1e384fff0bd 100644 --- a/ee/packages/license/src/v2/bundles.ts +++ b/ee/packages/license/src/v2/bundles.ts @@ -1,4 +1,4 @@ -import type { LicenseModule } from '../definition/LicenseModule'; +import type { LicenseModule } from '@rocket.chat/core-typings'; interface IBundle { [key: string]: LicenseModule[]; diff --git a/ee/packages/license/src/v2/convertToV3.ts b/ee/packages/license/src/v2/convertToV3.ts index 94e94a868e5..a7b67ada2c6 100644 --- a/ee/packages/license/src/v2/convertToV3.ts +++ b/ee/packages/license/src/v2/convertToV3.ts @@ -3,9 +3,8 @@ * Transform a License V2 into a V3 representation. */ -import type { ILicenseV2 } from '../definition/ILicenseV2'; -import type { ILicenseV3 } from '../definition/ILicenseV3'; -import type { LicenseModule } from '../definition/LicenseModule'; +import type { ILicenseV2, ILicenseV3, LicenseModule } from '@rocket.chat/core-typings'; + import { isBundle, getBundleFromModule, getBundleModules } from './bundles'; import { getTagColor } from './getTagColor'; diff --git a/ee/packages/license/src/validation/filterBehaviorsResult.ts b/ee/packages/license/src/validation/filterBehaviorsResult.ts index e51dbac20a5..e0c80f6dcb8 100644 --- a/ee/packages/license/src/validation/filterBehaviorsResult.ts +++ b/ee/packages/license/src/validation/filterBehaviorsResult.ts @@ -1,4 +1,4 @@ -import type { BehaviorWithContext, LicenseBehavior } from '../definition/LicenseBehavior'; +import type { BehaviorWithContext, LicenseBehavior } from '@rocket.chat/core-typings'; export const filterBehaviorsResult = (result: BehaviorWithContext[], expectedBehaviors: LicenseBehavior[]) => result.filter(({ behavior }) => expectedBehaviors.includes(behavior)); diff --git a/ee/packages/license/src/validation/getCurrentValueForLicenseLimit.ts b/ee/packages/license/src/validation/getCurrentValueForLicenseLimit.ts index 6dd3f143fc7..01d4b78b77f 100644 --- a/ee/packages/license/src/validation/getCurrentValueForLicenseLimit.ts +++ b/ee/packages/license/src/validation/getCurrentValueForLicenseLimit.ts @@ -1,5 +1,5 @@ -import type { LicenseLimitKind } from '../definition/ILicenseV3'; -import type { LimitContext } from '../definition/LimitContext'; +import type { LicenseLimitKind, LimitContext } from '@rocket.chat/core-typings'; + import type { LicenseManager } from '../license'; import { logger } from '../logger'; import { applyPendingLicense, hasPendingLicense } from '../pendingLicense'; diff --git a/ee/packages/license/src/validation/getModulesToDisable.ts b/ee/packages/license/src/validation/getModulesToDisable.ts index d42426e8af2..cdab814db02 100644 --- a/ee/packages/license/src/validation/getModulesToDisable.ts +++ b/ee/packages/license/src/validation/getModulesToDisable.ts @@ -1,5 +1,4 @@ -import type { BehaviorWithContext, LicenseBehavior } from '../definition/LicenseBehavior'; -import type { LicenseModule } from '../definition/LicenseModule'; +import type { BehaviorWithContext, LicenseBehavior, LicenseModule } from '@rocket.chat/core-typings'; const filterValidationResult = (result: BehaviorWithContext[], expectedBehavior: LicenseBehavior) => result.filter(({ behavior }) => behavior === expectedBehavior) as BehaviorWithContext[]; diff --git a/ee/packages/license/src/validation/getResultingBehavior.ts b/ee/packages/license/src/validation/getResultingBehavior.ts index 22ca02bfd22..a860c0985aa 100644 --- a/ee/packages/license/src/validation/getResultingBehavior.ts +++ b/ee/packages/license/src/validation/getResultingBehavior.ts @@ -1,7 +1,4 @@ -import type { LicenseLimitKind } from '../definition/ILicenseV3'; -import type { BehaviorWithContext } from '../definition/LicenseBehavior'; -import type { LicenseLimit } from '../definition/LicenseLimit'; -import type { LicensePeriod } from '../definition/LicensePeriod'; +import type { LicenseLimitKind, BehaviorWithContext, LicenseLimit, LicensePeriod } from '@rocket.chat/core-typings'; export const getResultingBehavior = ( data: LicenseLimit | LicensePeriod | Partial>, diff --git a/ee/packages/license/src/validation/isBehaviorsInResult.ts b/ee/packages/license/src/validation/isBehaviorsInResult.ts index 7e6ed89db8e..18a0251cb3c 100644 --- a/ee/packages/license/src/validation/isBehaviorsInResult.ts +++ b/ee/packages/license/src/validation/isBehaviorsInResult.ts @@ -1,4 +1,4 @@ -import type { BehaviorWithContext, LicenseBehavior } from '../definition/LicenseBehavior'; +import type { BehaviorWithContext, LicenseBehavior } from '@rocket.chat/core-typings'; export const isBehaviorsInResult = (result: BehaviorWithContext[], expectedBehaviors: LicenseBehavior[]) => result.some(({ behavior }) => expectedBehaviors.includes(behavior)); diff --git a/ee/packages/license/src/validation/logKind.ts b/ee/packages/license/src/validation/logKind.ts index 99a7c9d6d41..cd2968d9e4b 100644 --- a/ee/packages/license/src/validation/logKind.ts +++ b/ee/packages/license/src/validation/logKind.ts @@ -1,4 +1,4 @@ -import type { LicenseBehavior } from '../definition/LicenseBehavior'; +import type { LicenseBehavior } from '@rocket.chat/core-typings'; export const logKind = (behavior: LicenseBehavior) => { switch (behavior) { diff --git a/ee/packages/license/src/validation/runValidation.ts b/ee/packages/license/src/validation/runValidation.ts index 922b4c49162..52704b535ab 100644 --- a/ee/packages/license/src/validation/runValidation.ts +++ b/ee/packages/license/src/validation/runValidation.ts @@ -1,6 +1,5 @@ -import type { ILicenseV3 } from '../definition/ILicenseV3'; -import type { BehaviorWithContext } from '../definition/LicenseBehavior'; -import type { LicenseValidationOptions } from '../definition/LicenseValidationOptions'; +import type { ILicenseV3, BehaviorWithContext, LicenseValidationOptions } from '@rocket.chat/core-typings'; + import type { LicenseManager } from '../license'; import { validateLicenseLimits } from './validateLicenseLimits'; import { validateLicensePeriods } from './validateLicensePeriods'; diff --git a/ee/packages/license/src/validation/validateDefaultLimits.ts b/ee/packages/license/src/validation/validateDefaultLimits.ts index d8b03f216c2..4f48d4e7ebe 100644 --- a/ee/packages/license/src/validation/validateDefaultLimits.ts +++ b/ee/packages/license/src/validation/validateDefaultLimits.ts @@ -1,6 +1,5 @@ -import type { BehaviorWithContext } from '../definition/LicenseBehavior'; -import type { LicenseLimit } from '../definition/LicenseLimit'; -import type { LicenseValidationOptions } from '../definition/LicenseValidationOptions'; +import type { BehaviorWithContext, LicenseLimit, LicenseValidationOptions } from '@rocket.chat/core-typings'; + import type { LicenseManager } from '../license'; import { validateLimits } from './validateLimits'; diff --git a/ee/packages/license/src/validation/validateLicenseLimits.ts b/ee/packages/license/src/validation/validateLicenseLimits.ts index bc6212e11d9..4ac916d16e6 100644 --- a/ee/packages/license/src/validation/validateLicenseLimits.ts +++ b/ee/packages/license/src/validation/validateLicenseLimits.ts @@ -1,6 +1,5 @@ -import type { ILicenseV3 } from '../definition/ILicenseV3'; -import type { BehaviorWithContext } from '../definition/LicenseBehavior'; -import type { LicenseValidationOptions } from '../definition/LicenseValidationOptions'; +import type { ILicenseV3, BehaviorWithContext, LicenseValidationOptions } from '@rocket.chat/core-typings'; + import type { LicenseManager } from '../license'; import { validateLimits } from './validateLimits'; diff --git a/ee/packages/license/src/validation/validateLicensePeriods.ts b/ee/packages/license/src/validation/validateLicensePeriods.ts index fb27f72d0a8..4229d243564 100644 --- a/ee/packages/license/src/validation/validateLicensePeriods.ts +++ b/ee/packages/license/src/validation/validateLicensePeriods.ts @@ -1,7 +1,5 @@ -import type { ILicenseV3 } from '../definition/ILicenseV3'; -import type { BehaviorWithContext } from '../definition/LicenseBehavior'; -import type { Timestamp } from '../definition/LicensePeriod'; -import type { LicenseValidationOptions } from '../definition/LicenseValidationOptions'; +import type { ILicenseV3, BehaviorWithContext, Timestamp, LicenseValidationOptions } from '@rocket.chat/core-typings'; + import { isBehaviorAllowed } from '../isItemAllowed'; import { logger } from '../logger'; import { getResultingBehavior } from './getResultingBehavior'; diff --git a/ee/packages/license/src/validation/validateLicenseUrl.ts b/ee/packages/license/src/validation/validateLicenseUrl.ts index 416b107511c..aeba5634a26 100644 --- a/ee/packages/license/src/validation/validateLicenseUrl.ts +++ b/ee/packages/license/src/validation/validateLicenseUrl.ts @@ -1,8 +1,7 @@ import crypto from 'crypto'; -import type { ILicenseV3 } from '../definition/ILicenseV3'; -import type { BehaviorWithContext } from '../definition/LicenseBehavior'; -import type { LicenseValidationOptions } from '../definition/LicenseValidationOptions'; +import type { ILicenseV3, BehaviorWithContext, LicenseValidationOptions } from '@rocket.chat/core-typings'; + import { isBehaviorAllowed } from '../isItemAllowed'; import type { LicenseManager } from '../license'; import { logger } from '../logger'; diff --git a/ee/packages/license/src/validation/validateLimit.ts b/ee/packages/license/src/validation/validateLimit.ts index 21649d2e9f5..be0e2d04000 100644 --- a/ee/packages/license/src/validation/validateLimit.ts +++ b/ee/packages/license/src/validation/validateLimit.ts @@ -1,4 +1,4 @@ -import type { LicenseBehavior } from '../definition/LicenseBehavior'; +import type { LicenseBehavior } from '@rocket.chat/core-typings'; /** * The difference between validateLimit and validateWarnLimit is that the first one diff --git a/ee/packages/license/src/validation/validateLimits.ts b/ee/packages/license/src/validation/validateLimits.ts index 1cae603716b..b3b42a024e6 100644 --- a/ee/packages/license/src/validation/validateLimits.ts +++ b/ee/packages/license/src/validation/validateLimits.ts @@ -1,6 +1,5 @@ -import type { ILicenseV3, LicenseLimitKind } from '../definition/ILicenseV3'; -import type { BehaviorWithContext } from '../definition/LicenseBehavior'; -import type { LicenseValidationOptions } from '../definition/LicenseValidationOptions'; +import type { ILicenseV3, LicenseLimitKind, BehaviorWithContext, LicenseValidationOptions } from '@rocket.chat/core-typings'; + import { isLimitAllowed, isBehaviorAllowed } from '../isItemAllowed'; import type { LicenseManager } from '../license'; import { logger } from '../logger'; diff --git a/packages/core-services/src/Events.ts b/packages/core-services/src/Events.ts index c5f36d92165..b1d187a3962 100644 --- a/packages/core-services/src/Events.ts +++ b/packages/core-services/src/Events.ts @@ -33,8 +33,8 @@ import type { IBanner, ILivechatVisitor, UiKit, + LicenseLimitKind, } from '@rocket.chat/core-typings'; -import type { LicenseLimitKind } from '@rocket.chat/license'; import type { AutoUpdateRecord } from './types/IMeteor'; diff --git a/packages/core-typings/src/index.ts b/packages/core-typings/src/index.ts index 02929270c7a..f18ba328899 100644 --- a/packages/core-typings/src/index.ts +++ b/packages/core-typings/src/index.ts @@ -33,6 +33,7 @@ export * from './IRocketChatAssets'; export * from './IPushToken'; export * from './IPushNotificationConfig'; export * from './SlashCommands'; +export * from './license'; export * from './IUserDataFile'; export * from './IUserSession'; diff --git a/ee/packages/license/src/definition/ILicenseTag.ts b/packages/core-typings/src/license/ILicenseTag.ts similarity index 100% rename from ee/packages/license/src/definition/ILicenseTag.ts rename to packages/core-typings/src/license/ILicenseTag.ts diff --git a/ee/packages/license/src/definition/ILicenseV2.ts b/packages/core-typings/src/license/ILicenseV2.ts similarity index 100% rename from ee/packages/license/src/definition/ILicenseV2.ts rename to packages/core-typings/src/license/ILicenseV2.ts diff --git a/ee/packages/license/src/definition/ILicenseV3.ts b/packages/core-typings/src/license/ILicenseV3.ts similarity index 100% rename from ee/packages/license/src/definition/ILicenseV3.ts rename to packages/core-typings/src/license/ILicenseV3.ts diff --git a/ee/packages/license/src/definition/LicenseBehavior.ts b/packages/core-typings/src/license/LicenseBehavior.ts similarity index 100% rename from ee/packages/license/src/definition/LicenseBehavior.ts rename to packages/core-typings/src/license/LicenseBehavior.ts diff --git a/ee/packages/license/src/definition/LicenseInfo.ts b/packages/core-typings/src/license/LicenseInfo.ts similarity index 100% rename from ee/packages/license/src/definition/LicenseInfo.ts rename to packages/core-typings/src/license/LicenseInfo.ts diff --git a/ee/packages/license/src/definition/LicenseLimit.ts b/packages/core-typings/src/license/LicenseLimit.ts similarity index 100% rename from ee/packages/license/src/definition/LicenseLimit.ts rename to packages/core-typings/src/license/LicenseLimit.ts diff --git a/ee/packages/license/src/definition/LicenseModule.ts b/packages/core-typings/src/license/LicenseModule.ts similarity index 100% rename from ee/packages/license/src/definition/LicenseModule.ts rename to packages/core-typings/src/license/LicenseModule.ts diff --git a/ee/packages/license/src/definition/LicensePeriod.ts b/packages/core-typings/src/license/LicensePeriod.ts similarity index 100% rename from ee/packages/license/src/definition/LicensePeriod.ts rename to packages/core-typings/src/license/LicensePeriod.ts diff --git a/ee/packages/license/src/definition/LicenseValidationOptions.ts b/packages/core-typings/src/license/LicenseValidationOptions.ts similarity index 100% rename from ee/packages/license/src/definition/LicenseValidationOptions.ts rename to packages/core-typings/src/license/LicenseValidationOptions.ts diff --git a/ee/packages/license/src/definition/LimitContext.ts b/packages/core-typings/src/license/LimitContext.ts similarity index 79% rename from ee/packages/license/src/definition/LimitContext.ts rename to packages/core-typings/src/license/LimitContext.ts index 9dfc6d36be7..65be71d3cf2 100644 --- a/ee/packages/license/src/definition/LimitContext.ts +++ b/packages/core-typings/src/license/LimitContext.ts @@ -1,5 +1,4 @@ -import type { IUser } from '@rocket.chat/core-typings'; - +import type { IUser } from '../IUser'; import type { LicenseLimitKind } from './ILicenseV3'; export type LimitContext = { extraCount?: number } & (T extends 'roomsPerGuest' diff --git a/ee/packages/license/src/definition/events.ts b/packages/core-typings/src/license/events.ts similarity index 100% rename from ee/packages/license/src/definition/events.ts rename to packages/core-typings/src/license/events.ts diff --git a/packages/core-typings/src/license/index.ts b/packages/core-typings/src/license/index.ts new file mode 100644 index 00000000000..55c252c1fb9 --- /dev/null +++ b/packages/core-typings/src/license/index.ts @@ -0,0 +1,11 @@ +export * from './events'; +export * from './ILicenseTag'; +export * from './ILicenseV2'; +export * from './ILicenseV3'; +export * from './LicenseBehavior'; +export * from './LicenseInfo'; +export * from './LicenseLimit'; +export * from './LicenseModule'; +export * from './LicensePeriod'; +export * from './LicenseValidationOptions'; +export * from './LimitContext'; diff --git a/packages/rest-typings/package.json b/packages/rest-typings/package.json index 9d06aad70c5..e9c8386ca05 100644 --- a/packages/rest-typings/package.json +++ b/packages/rest-typings/package.json @@ -26,7 +26,6 @@ "dependencies": { "@rocket.chat/apps-engine": "1.41.0", "@rocket.chat/core-typings": "workspace:^", - "@rocket.chat/license": "workspace:^", "@rocket.chat/message-parser": "~0.31.27", "@rocket.chat/ui-kit": "~0.32.1", "ajv": "^8.11.0", diff --git a/packages/rest-typings/src/v1/licenses.ts b/packages/rest-typings/src/v1/licenses.ts index 4eb1ac19684..4a18bc113a6 100644 --- a/packages/rest-typings/src/v1/licenses.ts +++ b/packages/rest-typings/src/v1/licenses.ts @@ -1,4 +1,4 @@ -import type { ILicenseV2, ILicenseV3, LicenseInfo } from '@rocket.chat/license'; +import type { ILicenseV2, ILicenseV3, LicenseInfo } from '@rocket.chat/core-typings'; import Ajv from 'ajv'; const ajv = new Ajv({ diff --git a/yarn.lock b/yarn.lock index 601e120fd54..16795ab32c6 100644 --- a/yarn.lock +++ b/yarn.lock @@ -9276,7 +9276,6 @@ __metadata: "@rocket.chat/apps-engine": 1.41.0 "@rocket.chat/core-typings": "workspace:^" "@rocket.chat/eslint-config": "workspace:^" - "@rocket.chat/license": "workspace:^" "@rocket.chat/message-parser": ~0.31.27 "@rocket.chat/ui-kit": ~0.32.1 "@types/jest": ~29.5.7 -- GitLab From f1bc8cfa5fae29d660e325fc6f446e2f8b8a0b20 Mon Sep 17 00:00:00 2001 From: Guilherme Gazzo Date: Fri, 8 Dec 2023 17:02:51 -0300 Subject: [PATCH 005/329] fix: Analytics index creation after migration (#31193) --- apps/meteor/server/models/raw/BaseRaw.ts | 20 +++++++++++-------- apps/meteor/server/startup/migrations/v304.ts | 1 + .../model-typings/src/models/IBaseModel.ts | 2 ++ 3 files changed, 15 insertions(+), 8 deletions(-) diff --git a/apps/meteor/server/models/raw/BaseRaw.ts b/apps/meteor/server/models/raw/BaseRaw.ts index bfa5a81ddcf..ce3e0f224e1 100644 --- a/apps/meteor/server/models/raw/BaseRaw.ts +++ b/apps/meteor/server/models/raw/BaseRaw.ts @@ -66,23 +66,27 @@ export abstract class BaseRaw< * @param trash Trash collection instance * @param options Model options */ - constructor(private db: Db, protected name: string, protected trash?: Collection, options?: ModelOptions) { + constructor(private db: Db, protected name: string, protected trash?: Collection, private options?: ModelOptions) { this.collectionName = options?.collectionNameResolver ? options.collectionNameResolver(name) : getCollectionName(name); this.col = this.db.collection(this.collectionName, options?.collection || {}); + void this.createIndexes().catch((e) => { + console.warn(`Some indexes for collection '${this.collectionName}' could not be created:\n\t${e.message}`); + }); + + this.preventSetUpdatedAt = options?.preventSetUpdatedAt ?? false; + } + + public async createIndexes() { const indexes = this.modelIndexes(); - if (options?._updatedAtIndexOptions) { - indexes?.push({ ...options._updatedAtIndexOptions, key: { _updatedAt: 1 } }); + if (this.options?._updatedAtIndexOptions) { + indexes?.push({ ...this.options._updatedAtIndexOptions, key: { _updatedAt: 1 } }); } if (indexes?.length) { - this.col.createIndexes(indexes).catch((e) => { - console.warn(`Some indexes for collection '${this.collectionName}' could not be created:\n\t${e.message}`); - }); + return this.col.createIndexes(indexes); } - - this.preventSetUpdatedAt = options?.preventSetUpdatedAt ?? false; } protected modelIndexes(): IndexDescription[] | undefined { diff --git a/apps/meteor/server/startup/migrations/v304.ts b/apps/meteor/server/startup/migrations/v304.ts index e5d6484446f..db9aa44b4ee 100644 --- a/apps/meteor/server/startup/migrations/v304.ts +++ b/apps/meteor/server/startup/migrations/v304.ts @@ -7,5 +7,6 @@ addMigration({ name: 'Drop wrong index from analytics collection', async up() { await Analytics.col.dropIndex('room._id_1_date_1'); + await Analytics.createIndexes(); }, }); diff --git a/packages/model-typings/src/models/IBaseModel.ts b/packages/model-typings/src/models/IBaseModel.ts index e0ae05cba0c..36f34a00850 100644 --- a/packages/model-typings/src/models/IBaseModel.ts +++ b/packages/model-typings/src/models/IBaseModel.ts @@ -45,6 +45,8 @@ export interface IBaseModel< > { col: Collection; + createIndexes(): Promise; + getCollectionName(): string; findOneAndUpdate(query: Filter, update: UpdateFilter | T, options?: FindOneAndUpdateOptions): Promise>; -- GitLab From 55c95e45e725ab9d4831b81df028a02c4eeb8924 Mon Sep 17 00:00:00 2001 From: Debdut Chakraborty Date: Sat, 9 Dec 2023 04:52:10 +0530 Subject: [PATCH 006/329] fix: initial stuck client if root_url is wrong (#30913) --- apps/meteor/server/startup/initialData.js | 30 +++++++++++++++++++++++ 1 file changed, 30 insertions(+) diff --git a/apps/meteor/server/startup/initialData.js b/apps/meteor/server/startup/initialData.js index 61fbdf9f332..58fc3bf9157 100644 --- a/apps/meteor/server/startup/initialData.js +++ b/apps/meteor/server/startup/initialData.js @@ -13,6 +13,36 @@ import { validateEmail } from '../../lib/emailValidator'; import { addUserRolesAsync } from '../lib/roles/addUserRoles'; Meteor.startup(async () => { + const dynamicImport = { + 'dynamic-import': { + useLocationOrigin: true, + }, + }; + + if (!Meteor.settings) { + Meteor.settings = { + public: { + packages: { + 'dynamic-import': dynamicImport, + }, + }, + }; + } + + if (!Meteor.settings.public) { + Meteor.settings.public = { + packages: { + 'dynamic-import': dynamicImport, + }, + }; + } + + if (!Meteor.settings.public.packages) { + Meteor.settings.public.packages = dynamicImport; + } + + Meteor.settings.public.packages['dynamic-import'] = dynamicImport['dynamic-import']; + if (!settings.get('Initial_Channel_Created')) { const exists = await Rooms.findOneById('GENERAL', { projection: { _id: 1 } }); if (!exists) { -- GitLab From a4c3760d6003de13bdc8d4a6fb9875896541b20f Mon Sep 17 00:00:00 2001 From: Guilherme Gazzo Date: Mon, 11 Dec 2023 09:53:39 -0300 Subject: [PATCH 007/329] fix: Registration Data (#31185) --- .../cloud/server/functions/buildRegistrationData.ts | 10 ++++++++-- .../setupWizard/providers/SetupWizardProvider.tsx | 4 ---- apps/meteor/server/cron/statistics.ts | 5 ----- apps/meteor/server/settings/general.ts | 1 + 4 files changed, 9 insertions(+), 11 deletions(-) diff --git a/apps/meteor/app/cloud/server/functions/buildRegistrationData.ts b/apps/meteor/app/cloud/server/functions/buildRegistrationData.ts index fcbd3e9d74b..cc1af6fb176 100644 --- a/apps/meteor/app/cloud/server/functions/buildRegistrationData.ts +++ b/apps/meteor/app/cloud/server/functions/buildRegistrationData.ts @@ -61,7 +61,13 @@ export async function buildWorkspaceRegistrationData('Country'); + const language = settings.get('Language'); + const organizationType = settings.get('Organization_Type'); + const industry = settings.get('Industry'); + const orgSize = settings.get('Organization_Size'); + const workspaceType = settings.get('Server_Type'); + const seats = await Users.getActiveLocalUserCount(); const [macs] = await LivechatRooms.getMACStatisticsForPeriod(moment.utc().format('YYYY-MM')); @@ -94,7 +100,7 @@ export async function buildWorkspaceRegistrationData { const cronStatistics = await statistics.save(); - if (!settings.get('Statistics_reporting')) { - return; - } - try { const token = await getWorkspaceAccessToken(); const headers = { ...(token && { Authorization: `Bearer ${token}` }) }; diff --git a/apps/meteor/server/settings/general.ts b/apps/meteor/server/settings/general.ts index c0a02d2dba3..784aaf1358c 100644 --- a/apps/meteor/server/settings/general.ts +++ b/apps/meteor/server/settings/general.ts @@ -287,6 +287,7 @@ export const createGeneralSettings = () => await this.section('Reporting', async function () { return this.add('Statistics_reporting', true, { type: 'boolean', + hidden: true, }); }); await this.section('Notifications', async function () { -- GitLab From 2a64314efcd0da94bd5a7789e6e5743185199007 Mon Sep 17 00:00:00 2001 From: Pierre Lehnen <55164754+pierre-lehnen-rc@users.noreply.github.com> Date: Mon, 11 Dec 2023 14:29:27 -0300 Subject: [PATCH 008/329] chore: rename sublime project file (#31206) --- .sublime-project => Rocket.Chat.sublime-project | 0 1 file changed, 0 insertions(+), 0 deletions(-) rename .sublime-project => Rocket.Chat.sublime-project (100%) diff --git a/.sublime-project b/Rocket.Chat.sublime-project similarity index 100% rename from .sublime-project rename to Rocket.Chat.sublime-project -- GitLab From f4664f00a013bf76c88cabe47f1313dd85c9991f Mon Sep 17 00:00:00 2001 From: Kevin Aleman Date: Mon, 11 Dec 2023 13:52:41 -0600 Subject: [PATCH 009/329] fix: Remove non-monitor users from monitors list before saving unit (#31204) --- .changeset/chilled-mails-greet.md | 5 +++++ .../server/lib/LivechatEnterprise.ts | 13 ++++++++++- .../tests/end-to-end/api/livechat/14-units.ts | 22 +++++++++++++++++++ 3 files changed, 39 insertions(+), 1 deletion(-) create mode 100644 .changeset/chilled-mails-greet.md diff --git a/.changeset/chilled-mails-greet.md b/.changeset/chilled-mails-greet.md new file mode 100644 index 00000000000..8ecfe5eb5c5 --- /dev/null +++ b/.changeset/chilled-mails-greet.md @@ -0,0 +1,5 @@ +--- +"@rocket.chat/meteor": patch +--- + +Fixed an issue that caused Omnichannel Business Units to be saved even when the "monitors" list passed on the endpoint included users without monitor role diff --git a/apps/meteor/ee/app/livechat-enterprise/server/lib/LivechatEnterprise.ts b/apps/meteor/ee/app/livechat-enterprise/server/lib/LivechatEnterprise.ts index ec228e420ab..43ceb5a1091 100644 --- a/apps/meteor/ee/app/livechat-enterprise/server/lib/LivechatEnterprise.ts +++ b/apps/meteor/ee/app/livechat-enterprise/server/lib/LivechatEnterprise.ts @@ -113,7 +113,18 @@ export const LivechatEnterprise = { ancestors = unit.ancestors || []; } - return LivechatUnit.createOrUpdateUnit(_id, unitData, ancestors, unitMonitors, unitDepartments); + const validUserMonitors = await Users.findUsersInRolesWithQuery( + 'livechat-monitor', + { _id: { $in: unitMonitors.map(({ monitorId }) => monitorId) } }, + { projection: { _id: 1, username: 1 } }, + ).toArray(); + + const monitors = validUserMonitors.map(({ _id: monitorId, username }) => ({ + monitorId, + username, + })) as { monitorId: string; username: string }[]; + + return LivechatUnit.createOrUpdateUnit(_id, unitData, ancestors, monitors, unitDepartments); }, async removeTag(_id: string) { diff --git a/apps/meteor/tests/end-to-end/api/livechat/14-units.ts b/apps/meteor/tests/end-to-end/api/livechat/14-units.ts index 4cd26d6a122..3a03d9e93e2 100644 --- a/apps/meteor/tests/end-to-end/api/livechat/14-units.ts +++ b/apps/meteor/tests/end-to-end/api/livechat/14-units.ts @@ -148,6 +148,28 @@ import { IS_EE } from '../../../e2e/config/constants'; // cleanup await deleteUser(user); }); + + it('should return a unit with no monitors if a user who is not a monitor is passed', async () => { + await updatePermission('manage-livechat-units', ['admin']); + const user = await createUser(); + const department = await createDepartment(); + + const { body } = await request + .post(api('livechat/units')) + .set(credentials) + .send({ + unitData: { name: 'test', visibility: 'public', enabled: true, description: 'test' }, + unitMonitors: [{ monitorId: user._id, username: user.username }], + unitDepartments: [{ departmentId: department._id }], + }) + .expect(200); + + expect(body).to.have.property('numMonitors', 0); + expect(body).to.have.property('name', 'test'); + + // cleanup + await deleteUser(user); + }); }); describe('[GET] livechat/units/:id', () => { -- GitLab From a2750336f741b13962761a67a0af0c0fa5dba0fa Mon Sep 17 00:00:00 2001 From: Douglas Fabris Date: Mon, 11 Dec 2023 18:17:32 -0300 Subject: [PATCH 010/329] chore: Replace `useForm` in favor of RHF on `BusinessHours` (#31197) --- .../views/admin/settings/MemoizedSetting.tsx | 2 +- .../client/views/admin/settings/Setting.tsx | 7 +- .../views/omnichannel/additionalForms.tsx | 4 +- .../businessHours/BusinessHoursForm.js | 31 ----- .../BusinessHoursForm.stories.tsx | 15 --- .../businessHours/BusinessHoursForm.tsx | 123 +++++++++++++++++ .../BusinessHoursFormContainer.js | 59 -------- ...sPage.js => BusinessHoursMultiplePage.tsx} | 19 +-- .../businessHours/BusinessHoursRouter.js | 42 ------ .../businessHours/BusinessHoursRouter.tsx | 35 +++++ .../businessHours/BusinessHoursTimeZone.js | 37 ----- .../BusinessHoursTimeZone.stories.tsx | 20 --- .../businessHours/EditBusinessHours.tsx | 115 ++++++++++++++++ .../businessHours/EditBusinessHoursPage.js | 126 ------------------ .../EditBusinessHoursWithData.tsx | 50 +++++++ .../businessHours/NewBusinessHoursPage.js | 97 -------------- .../businessHours/TimeRangeFieldsAssembler.js | 30 ----- .../businessHours/TimeRangeInput.js | 36 ----- .../businessHours/mapBusinessHoursForm.js | 17 --- .../businessHours/mapBusinessHoursForm.ts | 14 ++ .../businessHours/useIsSingleBusinessHours.ts | 7 + .../server/api/business-hours.ts | 3 +- .../ee/client/omnichannel/BusinessHoursRow.js | 60 --------- .../omnichannel/RemoveBusinessHourButton.js | 46 ------- .../additionalForms/BusinessHoursMultiple.js | 40 ------ .../additionalForms/BusinessHoursMultiple.tsx | 80 +++++++++++ .../BusinessHoursMultipleContainer.js | 25 ---- .../businessHours/BusinessHoursRow.tsx | 51 +++++++ .../BusinessHoursTable.stories.tsx | 0 .../BusinessHoursTable.tsx} | 18 +-- .../businessHours/useRemoveBusinessHour.tsx | 32 +++++ 31 files changed, 531 insertions(+), 710 deletions(-) delete mode 100644 apps/meteor/client/views/omnichannel/businessHours/BusinessHoursForm.js create mode 100644 apps/meteor/client/views/omnichannel/businessHours/BusinessHoursForm.tsx delete mode 100644 apps/meteor/client/views/omnichannel/businessHours/BusinessHoursFormContainer.js rename apps/meteor/client/views/omnichannel/businessHours/{BusinessHoursPage.js => BusinessHoursMultiplePage.tsx} (52%) delete mode 100644 apps/meteor/client/views/omnichannel/businessHours/BusinessHoursRouter.js create mode 100644 apps/meteor/client/views/omnichannel/businessHours/BusinessHoursRouter.tsx delete mode 100644 apps/meteor/client/views/omnichannel/businessHours/BusinessHoursTimeZone.js delete mode 100644 apps/meteor/client/views/omnichannel/businessHours/BusinessHoursTimeZone.stories.tsx create mode 100644 apps/meteor/client/views/omnichannel/businessHours/EditBusinessHours.tsx delete mode 100644 apps/meteor/client/views/omnichannel/businessHours/EditBusinessHoursPage.js create mode 100644 apps/meteor/client/views/omnichannel/businessHours/EditBusinessHoursWithData.tsx delete mode 100644 apps/meteor/client/views/omnichannel/businessHours/NewBusinessHoursPage.js delete mode 100644 apps/meteor/client/views/omnichannel/businessHours/TimeRangeFieldsAssembler.js delete mode 100644 apps/meteor/client/views/omnichannel/businessHours/TimeRangeInput.js delete mode 100644 apps/meteor/client/views/omnichannel/businessHours/mapBusinessHoursForm.js create mode 100644 apps/meteor/client/views/omnichannel/businessHours/mapBusinessHoursForm.ts create mode 100644 apps/meteor/client/views/omnichannel/businessHours/useIsSingleBusinessHours.ts delete mode 100644 apps/meteor/ee/client/omnichannel/BusinessHoursRow.js delete mode 100644 apps/meteor/ee/client/omnichannel/RemoveBusinessHourButton.js delete mode 100644 apps/meteor/ee/client/omnichannel/additionalForms/BusinessHoursMultiple.js create mode 100644 apps/meteor/ee/client/omnichannel/additionalForms/BusinessHoursMultiple.tsx delete mode 100644 apps/meteor/ee/client/omnichannel/additionalForms/BusinessHoursMultipleContainer.js create mode 100644 apps/meteor/ee/client/omnichannel/businessHours/BusinessHoursRow.tsx rename apps/meteor/ee/client/omnichannel/{ => businessHours}/BusinessHoursTable.stories.tsx (100%) rename apps/meteor/ee/client/omnichannel/{BusinessHoursTable.js => businessHours/BusinessHoursTable.tsx} (82%) create mode 100644 apps/meteor/ee/client/omnichannel/businessHours/useRemoveBusinessHour.tsx diff --git a/apps/meteor/client/views/admin/settings/MemoizedSetting.tsx b/apps/meteor/client/views/admin/settings/MemoizedSetting.tsx index a7433afc66c..b3a29b8a303 100644 --- a/apps/meteor/client/views/admin/settings/MemoizedSetting.tsx +++ b/apps/meteor/client/views/admin/settings/MemoizedSetting.tsx @@ -101,8 +101,8 @@ const MemoizedSetting = ({ {callout} )} + {showUpgradeButton} - {showUpgradeButton} ); }; diff --git a/apps/meteor/client/views/admin/settings/Setting.tsx b/apps/meteor/client/views/admin/settings/Setting.tsx index eb0413d53d8..d9076e5fb4f 100644 --- a/apps/meteor/client/views/admin/settings/Setting.tsx +++ b/apps/meteor/client/views/admin/settings/Setting.tsx @@ -2,7 +2,6 @@ import type { ISettingColor, SettingEditor, SettingValue } from '@rocket.chat/co import { isSettingColor, isSetting } from '@rocket.chat/core-typings'; import { Button } from '@rocket.chat/fuselage'; import { useDebouncedCallback } from '@rocket.chat/fuselage-hooks'; -import { ExternalLink } from '@rocket.chat/ui-client'; import { useSettingStructure, useTranslation } from '@rocket.chat/ui-contexts'; import type { ReactElement } from 'react'; import React, { useEffect, useMemo, useState, useCallback } from 'react'; @@ -113,9 +112,9 @@ function Setting({ className = undefined, settingId, sectionChanged }: SettingPr const showUpgradeButton = useMemo( () => shouldDisableEnterprise ? ( - - - + ) : undefined, [shouldDisableEnterprise, t], ); diff --git a/apps/meteor/client/views/omnichannel/additionalForms.tsx b/apps/meteor/client/views/omnichannel/additionalForms.tsx index 16f6c9bcb44..9978aba5586 100644 --- a/apps/meteor/client/views/omnichannel/additionalForms.tsx +++ b/apps/meteor/client/views/omnichannel/additionalForms.tsx @@ -1,4 +1,4 @@ -import BusinessHoursMultipleContainer from '../../../ee/client/omnichannel/additionalForms/BusinessHoursMultipleContainer'; +import BusinessHoursMultiple from '../../../ee/client/omnichannel/additionalForms/BusinessHoursMultiple'; import ContactManager from '../../../ee/client/omnichannel/additionalForms/ContactManager'; import CurrentChatTags from '../../../ee/client/omnichannel/additionalForms/CurrentChatTags'; import CustomFieldsAdditionalForm from '../../../ee/client/omnichannel/additionalForms/CustomFieldsAdditionalForm'; @@ -18,7 +18,7 @@ export { MaxChatsPerAgentDisplay, EeNumberInput, EeTextAreaInput, - BusinessHoursMultipleContainer, + BusinessHoursMultiple, EeTextInput, ContactManager, CurrentChatTags, diff --git a/apps/meteor/client/views/omnichannel/businessHours/BusinessHoursForm.js b/apps/meteor/client/views/omnichannel/businessHours/BusinessHoursForm.js deleted file mode 100644 index ba01289ffe4..00000000000 --- a/apps/meteor/client/views/omnichannel/businessHours/BusinessHoursForm.js +++ /dev/null @@ -1,31 +0,0 @@ -import { Field, MultiSelect } from '@rocket.chat/fuselage'; -import { useTranslation } from '@rocket.chat/ui-contexts'; -import React, { useMemo } from 'react'; - -import TimeRangeFieldsAssembler from './TimeRangeFieldsAssembler'; - -export const DAYS_OF_WEEK = ['Monday', 'Tuesday', 'Wednesday', 'Thursday', 'Friday', 'Saturday', 'Sunday']; - -const BusinessHoursForm = ({ values, handlers, className = undefined }) => { - const t = useTranslation(); - - const daysOptions = useMemo(() => DAYS_OF_WEEK.map((day) => [day, t(day)]), [t]); - - const { daysOpen, daysTime } = values; - - const { handleDaysOpen, handleDaysTime } = handlers; - - return ( - <> - - {t('Open_days_of_the_week')} - - - - - - - ); -}; - -export default BusinessHoursForm; diff --git a/apps/meteor/client/views/omnichannel/businessHours/BusinessHoursForm.stories.tsx b/apps/meteor/client/views/omnichannel/businessHours/BusinessHoursForm.stories.tsx index d2861a33fa5..3bfbcb7aa4f 100644 --- a/apps/meteor/client/views/omnichannel/businessHours/BusinessHoursForm.stories.tsx +++ b/apps/meteor/client/views/omnichannel/businessHours/BusinessHoursForm.stories.tsx @@ -1,5 +1,4 @@ import { Box } from '@rocket.chat/fuselage'; -import { action } from '@storybook/addon-actions'; import type { ComponentMeta, ComponentStory } from '@storybook/react'; import React from 'react'; @@ -19,17 +18,3 @@ export default { export const Default: ComponentStory = (args) => ; Default.storyName = 'BusinessHoursForm'; -Default.args = { - values: { - daysOpen: ['Monday', 'Tuesday', 'Saturday'], - daysTime: { - Monday: { start: '00:00', finish: '08:00' }, - Tuesday: { start: '00:00', finish: '08:00' }, - Saturday: { start: '00:00', finish: '08:00' }, - }, - }, - handlers: { - handleDaysOpen: action('handleDaysOpen'), - handleDaysTime: action('handleDaysTime'), - }, -}; diff --git a/apps/meteor/client/views/omnichannel/businessHours/BusinessHoursForm.tsx b/apps/meteor/client/views/omnichannel/businessHours/BusinessHoursForm.tsx new file mode 100644 index 00000000000..71a3a5b27ed --- /dev/null +++ b/apps/meteor/client/views/omnichannel/businessHours/BusinessHoursForm.tsx @@ -0,0 +1,123 @@ +import type { SelectOption } from '@rocket.chat/fuselage'; +import { InputBox, Field, MultiSelect, FieldGroup, Box, Select, FieldLabel, FieldRow } from '@rocket.chat/fuselage'; +import { useUniqueId } from '@rocket.chat/fuselage-hooks'; +import type { TranslationKey } from '@rocket.chat/ui-contexts'; +import { useTranslation } from '@rocket.chat/ui-contexts'; +import React, { useMemo } from 'react'; +import { useFormContext, Controller, useFieldArray } from 'react-hook-form'; + +import { useTimezoneNameList } from '../../../hooks/useTimezoneNameList'; +import { BusinessHoursMultiple } from '../additionalForms'; +import { defaultWorkHours, DAYS_OF_WEEK } from './mapBusinessHoursForm'; + +type mappedDayTime = { + day: string; + start: { + time: string; + }; + finish: { + time: string; + }; + open: boolean; +}; + +export type BusinessHoursFormData = { + name: string; + timezoneName: string; + daysOpen: string[]; + daysTime: mappedDayTime[]; + departmentsToApplyBusinessHour: string; + active: boolean; + departments: { + value: string; + label: string; + }[]; +}; + +// TODO: replace `Select` in favor `SelectFiltered` +// TODO: add time validation for start and finish not be equal on UI +// TODO: add time validation for start not be higher than finish on UI +const BusinessHoursForm = ({ type }: { type?: 'default' | 'custom' }) => { + const t = useTranslation(); + const timeZones = useTimezoneNameList(); + const timeZonesOptions: SelectOption[] = useMemo(() => timeZones.map((name) => [name, t(name as TranslationKey)]), [t, timeZones]); + const daysOptions: SelectOption[] = useMemo(() => DAYS_OF_WEEK.map((day) => [day, t(day as TranslationKey)]), [t]); + + const { watch, control } = useFormContext(); + const { daysTime } = watch(); + const { fields: daysTimeFields, replace } = useFieldArray({ control, name: 'daysTime' }); + + const timezoneField = useUniqueId(); + const daysOpenField = useUniqueId(); + const daysTimeField = useUniqueId(); + + const handleChangeDaysTime = (values: string[]) => { + const newValues = values + .map((item) => daysTime.find(({ day }) => day === item) || defaultWorkHours(true).find(({ day }) => day === item)) + .filter((item): item is mappedDayTime => Boolean(item)); + replace(newValues); + }; + + return ( + + {type === 'custom' && } + + {t('Timezone')} + + } + /> + + + + + ); }; diff --git a/apps/meteor/client/views/admin/emailInbox/EmailInboxForm.tsx b/apps/meteor/client/views/admin/emailInbox/EmailInboxForm.tsx index 2dc1f372fc8..5101cb160de 100644 --- a/apps/meteor/client/views/admin/emailInbox/EmailInboxForm.tsx +++ b/apps/meteor/client/views/admin/emailInbox/EmailInboxForm.tsx @@ -17,7 +17,7 @@ import { FieldError, FieldHint, } from '@rocket.chat/fuselage'; -import { useMutableCallback } from '@rocket.chat/fuselage-hooks'; +import { useMutableCallback, useUniqueId } from '@rocket.chat/fuselage-hooks'; import { useSetModal, useToastMessageDispatch, useRoute, useEndpoint, useTranslation } from '@rocket.chat/ui-contexts'; import type { ReactElement } from 'react'; import React, { useCallback } from 'react'; @@ -41,7 +41,6 @@ const EmailInboxForm = ({ inboxData }: { inboxData?: IEmailInboxPayload }): Reac const emailAlreadyExistsAction = useEndpoint('GET', '/v1/email-inbox.search'); const { - register, control, handleSubmit, formState: { errors, isDirty }, @@ -166,6 +165,26 @@ const EmailInboxForm = ({ inboxData }: { inboxData?: IEmailInboxPayload }): Reac return t('Email_already_exists'); }); + const activeField = useUniqueId(); + const nameField = useUniqueId(); + const emailField = useUniqueId(); + const descriptionField = useUniqueId(); + const senderInfoField = useUniqueId(); + const departmentField = useUniqueId(); + + const smtpServerField = useUniqueId(); + const smtpPortField = useUniqueId(); + const smtpUsernameField = useUniqueId(); + const smtpPasswordField = useUniqueId(); + const smtpSecureField = useUniqueId(); + + const imapServerField = useUniqueId(); + const imapPortField = useUniqueId(); + const imapUsernameField = useUniqueId(); + const imapPasswordField = useUniqueId(); + const imapRetriesField = useUniqueId(); + const imapSecureField = useUniqueId(); + return ( @@ -173,185 +192,385 @@ const EmailInboxForm = ({ inboxData }: { inboxData?: IEmailInboxPayload }): Reac - - {t('Active')} + + {t('Active')} ( - + )} /> - + - {t('Name')}* + + {t('Name')} + - ( + + )} /> - {errors.name && {errors.name?.message}} + {errors.name && ( + + {errors.name?.message} + + )} - {t('Email')}* + + {t('Email')} + - checkEmailExists(value), - })} - error={errors.email?.message} + }} + render={({ field }) => ( + + )} /> - {errors.email && {errors.email?.message}} + {errors.email && ( + + {errors.email?.message} + + )} - {t('Description')} + {t('Description')} - + } + /> - {t('Sender_Info')} + {t('Sender_Info')} - + } + /> - {t('Will_Appear_In_From')} + {t('Will_Appear_In_From')} - {t('Department')} + {t('Department')} } + render={({ field: { onChange, onBlur, name, value } }) => ( + + )} /> - {t('Only_Members_Selected_Department_Can_View_Channel')} + {t('Only_Members_Selected_Department_Can_View_Channel')} - {t('Server')}* + + {t('Server')} + - ( + + )} /> - {errors.smtpServer && {errors.smtpServer?.message}} + {errors.smtpServer && ( + + {errors.smtpServer?.message} + + )} - {t('Port')}* + + {t('Port')} + - ( + + )} /> - {errors.smtpPort && {errors.smtpPort?.message}} + {errors.smtpPort && ( + + {errors.smtpPort?.message} + + )} - {t('Username')}* + + {t('Username')} + - ( + + )} /> - {errors.smtpUsername && {errors.smtpUsername?.message}} + {errors.smtpUsername && ( + + {errors.smtpUsername?.message} + + )} - {t('Password')}* + + {t('Password')} + - ( + + )} /> - {errors.smtpPassword && {errors.smtpPassword?.message}} + {errors.smtpPassword && ( + + {errors.smtpPassword?.message} + + )} - - {t('Connect_SSL_TLS')} + + {t('Connect_SSL_TLS')} ( - - )} + render={({ field: { value, ...field } }) => } /> - + - {t('Server')}* + + {t('Server')} + - ( + + )} /> - {errors.imapServer && {errors.imapServer?.message}} + {errors.imapServer && ( + + {errors.imapServer?.message} + + )} - {t('Port')}* + + {t('Port')} + - ( + + )} /> - {errors.imapPort && {errors.imapPort?.message}} + {errors.imapPort && ( + + {errors.imapPort?.message} + + )} - {t('Username')}* + + {t('Username')} + - ( + + )} /> - {errors.imapUsername && {errors.imapUsername?.message}} + {errors.imapUsername && ( + + {errors.imapUsername?.message} + + )} - {t('Password')}* + + {t('Password')} + - ( + + )} /> - {errors.imapPassword && {errors.imapPassword?.message}} + {errors.imapPassword && ( + + {errors.imapPassword?.message} + + )} - {t('Max_Retry')}* + + {t('Max_Retry')} + - ( + + )} /> - {errors.imapRetries && {errors.imapRetries?.message}} + {errors.imapRetries && ( + + {errors.imapRetries?.message} + + )} - - {t('Connect_SSL_TLS')} + + {t('Connect_SSL_TLS')} ( - - )} + render={({ field: { value, ...field } }) => } /> - + diff --git a/apps/meteor/client/views/admin/integrations/incoming/IncomingWebhookForm.tsx b/apps/meteor/client/views/admin/integrations/incoming/IncomingWebhookForm.tsx index 0c1d8276466..bd43c76b213 100644 --- a/apps/meteor/client/views/admin/integrations/incoming/IncomingWebhookForm.tsx +++ b/apps/meteor/client/views/admin/integrations/incoming/IncomingWebhookForm.tsx @@ -139,16 +139,14 @@ const IncomingWebhookForm = ({ webhookData }: { webhookData?: Serialized - + {t('Enabled')} - - } - /> - - + } + /> + {t('Name')} @@ -276,30 +274,26 @@ const IncomingWebhookForm = ({ webhookData }: { webhookData?: Serialized - + {t('Override_Destination_Channel')} - - ( - - )} - /> - - + ( + + )} + /> + - + {t('Script_Enabled')} - - } - /> - - + } + /> + {t('Script_Engine')} diff --git a/apps/meteor/client/views/admin/integrations/outgoing/OutgoingWebhookForm.tsx b/apps/meteor/client/views/admin/integrations/outgoing/OutgoingWebhookForm.tsx index 63ad5b35e2a..a14cdd2ada4 100644 --- a/apps/meteor/client/views/admin/integrations/outgoing/OutgoingWebhookForm.tsx +++ b/apps/meteor/client/views/admin/integrations/outgoing/OutgoingWebhookForm.tsx @@ -144,16 +144,14 @@ const OutgoingWebhookForm = () => { {t('Event_Trigger_Description')} - + {t('Enabled')} - - } - /> - - + } + /> + {t('Name')} @@ -265,16 +263,14 @@ const OutgoingWebhookForm = () => { )} - + {t('Impersonate_user')} - - } - /> - - + } + /> + @@ -391,16 +387,14 @@ const OutgoingWebhookForm = () => { )} - + {t('Script_Enabled')} - - } - /> - - + } + /> + {t('Script_Engine')} @@ -449,23 +443,21 @@ const OutgoingWebhookForm = () => { - + {t('Integration_Retry_Failed_Url_Calls')} - - ( - - )} - /> - - + ( + + )} + /> + {t('Integration_Retry_Failed_Url_Calls_Description')} @@ -495,38 +487,34 @@ const OutgoingWebhookForm = () => { {event === 'sendMessage' && ( - + {t('Integration_Word_Trigger_Placement')} - - ( - - )} - /> - - + ( + + )} + /> + {t('Integration_Word_Trigger_Placement_Description')} - + {t('Integration_Run_When_Message_Is_Edited')} - - ( - - )} - /> - - + ( + + )} + /> + {t('Integration_Run_When_Message_Is_Edited_Description')} diff --git a/apps/meteor/client/views/admin/mailer/MailerPage.tsx b/apps/meteor/client/views/admin/mailer/MailerPage.tsx index 474cf0be657..9f0c8e7627a 100644 --- a/apps/meteor/client/views/admin/mailer/MailerPage.tsx +++ b/apps/meteor/client/views/admin/mailer/MailerPage.tsx @@ -100,6 +100,7 @@ const MailerPage = () => { + {t('Dry_run')} { )} /> - {t('Dry_run')} {t('Dry_run_description')} diff --git a/apps/meteor/client/views/admin/oauthApps/EditOauthApp.tsx b/apps/meteor/client/views/admin/oauthApps/EditOauthApp.tsx index a5759fde6cc..5a392731d0b 100644 --- a/apps/meteor/client/views/admin/oauthApps/EditOauthApp.tsx +++ b/apps/meteor/client/views/admin/oauthApps/EditOauthApp.tsx @@ -101,15 +101,15 @@ const EditOauthApp = ({ onChange, data, ...props }: EditOauthAppProps): ReactEle - - {t('Active')} + + {t('Active')} } /> - + {t('Application_Name')} diff --git a/apps/meteor/client/views/admin/oauthApps/OAuthAddApp.tsx b/apps/meteor/client/views/admin/oauthApps/OAuthAddApp.tsx index ccb4205b581..3a2590060c3 100644 --- a/apps/meteor/client/views/admin/oauthApps/OAuthAddApp.tsx +++ b/apps/meteor/client/views/admin/oauthApps/OAuthAddApp.tsx @@ -56,15 +56,15 @@ const OAuthAddApp = (): ReactElement => { - - {t('Active')} + + {t('Active')} } /> - + {t('Application_Name')} diff --git a/apps/meteor/client/views/admin/permissions/RoleForm.tsx b/apps/meteor/client/views/admin/permissions/RoleForm.tsx index e1517d7721f..2a063ce7d05 100644 --- a/apps/meteor/client/views/admin/permissions/RoleForm.tsx +++ b/apps/meteor/client/views/admin/permissions/RoleForm.tsx @@ -1,5 +1,5 @@ import type { SelectOption } from '@rocket.chat/fuselage'; -import { Box, Field, FieldLabel, FieldRow, FieldError, FieldHint, TextInput, Select, ToggleSwitch } from '@rocket.chat/fuselage'; +import { Field, FieldLabel, FieldRow, FieldError, FieldHint, TextInput, Select, ToggleSwitch } from '@rocket.chat/fuselage'; import { useTranslation } from '@rocket.chat/ui-contexts'; import type { ReactElement } from 'react'; import React, { useMemo } from 'react'; @@ -57,16 +57,14 @@ const RoleForm = ({ className, editing = false, isProtected = false, isDisabled - + {t('Users must use Two Factor Authentication')} - - } - /> - - + } + /> + ); diff --git a/apps/meteor/client/views/admin/rooms/EditRoom.tsx b/apps/meteor/client/views/admin/rooms/EditRoom.tsx index 0f824d71f5c..0ac1a387df2 100644 --- a/apps/meteor/client/views/admin/rooms/EditRoom.tsx +++ b/apps/meteor/client/views/admin/rooms/EditRoom.tsx @@ -216,132 +216,118 @@ const EditRoom = ({ room, onChange, onDelete }: EditRoomProps) => { )} {canViewType && ( - + {t('Private')} - - ( - onChange(value === 'p' ? 'c' : 'p')} - aria-describedby={`${roomTypeField}-hint`} - /> - )} - /> - - + ( + onChange(value === 'p' ? 'c' : 'p')} + aria-describedby={`${roomTypeField}-hint`} + /> + )} + /> + {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')} - - ( - - )} - /> - - + ( + + )} + /> + - + {t('Featured')} - - ( - - )} - /> - - + ( + + )} + /> + diff --git a/apps/meteor/client/views/admin/settings/ResetSettingButton.tsx b/apps/meteor/client/views/admin/settings/ResetSettingButton.tsx index 90616464c0e..c4cf47c109f 100644 --- a/apps/meteor/client/views/admin/settings/ResetSettingButton.tsx +++ b/apps/meteor/client/views/admin/settings/ResetSettingButton.tsx @@ -7,7 +7,7 @@ import React from 'react'; function ResetSettingButton(props: ComponentProps): ReactElement { const t = useTranslation(); - return ; + return ; } export default ResetSettingButton; diff --git a/apps/meteor/client/views/admin/settings/inputs/BooleanSettingInput.tsx b/apps/meteor/client/views/admin/settings/inputs/BooleanSettingInput.tsx index 308e781e8e4..bb6b78999ef 100644 --- a/apps/meteor/client/views/admin/settings/inputs/BooleanSettingInput.tsx +++ b/apps/meteor/client/views/admin/settings/inputs/BooleanSettingInput.tsx @@ -31,18 +31,11 @@ function BooleanSettingInput({ return ( - {label} - {hasResetButton && } + + {hasResetButton && } ); } diff --git a/apps/meteor/client/views/admin/users/AdminUserForm.tsx b/apps/meteor/client/views/admin/users/AdminUserForm.tsx index 1912150b4a4..a8575470509 100644 --- a/apps/meteor/client/views/admin/users/AdminUserForm.tsx +++ b/apps/meteor/client/views/admin/users/AdminUserForm.tsx @@ -263,16 +263,14 @@ const UserForm = ({ userData, onReload, ...props }: AdminUserFormProps) => { )} - + {t('Verified')} - - } - /> - - + } + /> + {t('StatusMessage')} @@ -370,45 +368,41 @@ const UserForm = ({ userData, onReload, ...props }: AdminUserFormProps) => { )} - + {t('Require_password_change')} - - ( - - )} - /> - - + ( + + )} + /> + - + {t('Set_random_password_and_send_by_email')} - - ( - - )} - /> - - + ( + + )} + /> + {!isSmtpEnabled && ( { {errors?.roles && {errors.roles.message}} - + {t('Join_default_channels')} - - ( - - )} - /> - - + ( + + )} + /> + - + {t('Send_welcome_email')} - - ( - - )} - /> - - + ( + + )} + /> + {!isSmtpEnabled && ( @@ -66,9 +70,10 @@ const RegisterWorkspaceSetupStepOneModal = ({ {t('RegisterWorkspace_Setup_Subtitle')} - {t('RegisterWorkspace_Setup_Label')} + {t('RegisterWorkspace_Setup_Label')} { setEmail((e.target as HTMLInputElement).value); }} @@ -85,16 +90,18 @@ const RegisterWorkspaceSetupStepOneModal = ({ {t('RegisterWorkspace_Setup_No_Account_Subtitle')} - - setTerms(!terms)} /> - - - I agree with Terms and Conditions - and - Privacy Policy - - - + + + + + I agree with Terms and Conditions + and + Privacy Policy + + + setTerms(!terms)} /> + + diff --git a/apps/meteor/client/views/admin/workspace/WorkspaceRoute.tsx b/apps/meteor/client/views/admin/workspace/WorkspaceRoute.tsx index ed10912965d..7cadf4b7ce1 100644 --- a/apps/meteor/client/views/admin/workspace/WorkspaceRoute.tsx +++ b/apps/meteor/client/views/admin/workspace/WorkspaceRoute.tsx @@ -30,6 +30,10 @@ const WorkspaceRoute = (): ReactElement => { statisticsQuery.refetch(); }; + const handleClickDownloadInfo = (): void => { + downloadJsonAs(statisticsQuery.data, 'statistics'); + }; + if (serverInfoQuery.isError || instancesQuery.isError || statisticsQuery.isError) { return ( @@ -47,10 +51,6 @@ const WorkspaceRoute = (): ReactElement => { ); } - const handleClickDownloadInfo = (): void => { - downloadJsonAs(statisticsQuery.data, 'statistics'); - }; - return ( { - + {t('Livechat_enable_message_character_limit')} - - ( - - )} - /> - - + ( + + )} + /> + {t('Message_Characther_Limit')} @@ -100,44 +97,38 @@ const AppearanceForm = () => { - + {t('Show_agent_info')} - - } - /> - - + } + /> + - + {t('Show_agent_email')} - - } - /> - - + } + /> + - + {t('Display_offline_form')} - - } - /> - - + } + /> + {t('Offline_form_unavailable_message')} @@ -204,44 +195,38 @@ const AppearanceForm = () => { - + {t('Enabled')} - - } - /> - - + } + /> + - + {t('Show_name_field')} - - ( - - )} - /> - - + ( + + )} + /> + - + {t('Show_email_field')} - - ( - - )} - /> - - + ( + + )} + /> + {t('Livechat_registration_form_message')} diff --git a/apps/meteor/client/views/omnichannel/customFields/EditCustomFields.tsx b/apps/meteor/client/views/omnichannel/customFields/EditCustomFields.tsx index 5a96076bd1b..d6bde05c8f3 100644 --- a/apps/meteor/client/views/omnichannel/customFields/EditCustomFields.tsx +++ b/apps/meteor/client/views/omnichannel/customFields/EditCustomFields.tsx @@ -2,7 +2,6 @@ import type { ILivechatCustomField, Serialized } from '@rocket.chat/core-typings import type { SelectOption } from '@rocket.chat/fuselage'; import { FieldError, - Box, Button, ButtonGroup, Field, @@ -170,28 +169,24 @@ const EditCustomFields = ({ customFieldData }: { customFieldData?: Serialized - + {t('Visible')} - - } - /> - - + } + /> + - + {t('Searchable')} - - } - /> - - + } + /> + {t('Validation')} diff --git a/apps/meteor/client/views/omnichannel/departments/DepartmentTags/index.tsx b/apps/meteor/client/views/omnichannel/departments/DepartmentTags.tsx similarity index 79% rename from apps/meteor/client/views/omnichannel/departments/DepartmentTags/index.tsx rename to apps/meteor/client/views/omnichannel/departments/DepartmentTags.tsx index cead6a0fb15..53c1afeb4ba 100644 --- a/apps/meteor/client/views/omnichannel/departments/DepartmentTags/index.tsx +++ b/apps/meteor/client/views/omnichannel/departments/DepartmentTags.tsx @@ -1,15 +1,15 @@ -import { Button, Chip, FieldRow, FieldHint, TextInput } from '@rocket.chat/fuselage'; +import { Button, Chip, FieldRow, TextInput } from '@rocket.chat/fuselage'; import { useTranslation } from '@rocket.chat/ui-contexts'; -import type { FormEvent } from 'react'; +import type { ComponentProps, FormEvent } from 'react'; import React, { useCallback, useState } from 'react'; type DepartmentTagsProps = { error: string; value: string[]; onChange: (tags: string[]) => void; -}; +} & ComponentProps; -export const DepartmentTags = ({ error, value: tags, onChange }: DepartmentTagsProps) => { +const DepartmentTags = ({ error, value: tags, onChange, ...props }: DepartmentTagsProps) => { const t = useTranslation(); const [tagText, setTagText] = useState(''); @@ -35,6 +35,7 @@ export const DepartmentTags = ({ error, value: tags, onChange }: DepartmentTagsP placeholder={t('Enter_a_tag')} value={tagText} onChange={(e: FormEvent) => setTagText(e.currentTarget.value)} + {...props} /> + { + textAreaRef?.current && textAreaRef.current.focus(); + setVisible(false); + }} + /> + + + + + + + + ); }; -export default memo(MarkdownTextEditor); +export default memo(CannedResponsesComposer); diff --git a/apps/meteor/ee/client/omnichannel/cannedResponses/components/CannedResponsesComposer/CannedResponsesComposerPreview.tsx b/apps/meteor/ee/client/omnichannel/cannedResponses/components/CannedResponsesComposer/CannedResponsesComposerPreview.tsx new file mode 100644 index 00000000000..840793db7e3 --- /dev/null +++ b/apps/meteor/ee/client/omnichannel/cannedResponses/components/CannedResponsesComposer/CannedResponsesComposerPreview.tsx @@ -0,0 +1,17 @@ +import { Box } from '@rocket.chat/fuselage'; +import type { FC } from 'react'; +import React, { memo } from 'react'; + +import MarkdownText from '../../../../../../client/components/MarkdownText'; + +const CannedResponsesComposerPreview: FC<{ text: string }> = ({ text }) => { + const textM = text.split(/\n/).join(' \n'); + + return ( + + + + ); +}; + +export default memo(CannedResponsesComposerPreview); diff --git a/apps/meteor/ee/client/omnichannel/cannedResponses/components/MarkdownTextEditor/InsertPlaceholderDropdown.tsx b/apps/meteor/ee/client/omnichannel/cannedResponses/components/CannedResponsesComposer/InsertPlaceholderDropdown.tsx similarity index 100% rename from apps/meteor/ee/client/omnichannel/cannedResponses/components/MarkdownTextEditor/InsertPlaceholderDropdown.tsx rename to apps/meteor/ee/client/omnichannel/cannedResponses/components/CannedResponsesComposer/InsertPlaceholderDropdown.tsx diff --git a/apps/meteor/ee/client/omnichannel/cannedResponses/components/MarkdownTextEditor/MarkdownTextEditor.stories.tsx b/apps/meteor/ee/client/omnichannel/cannedResponses/components/MarkdownTextEditor/MarkdownTextEditor.stories.tsx deleted file mode 100644 index 9923fbf5e7c..00000000000 --- a/apps/meteor/ee/client/omnichannel/cannedResponses/components/MarkdownTextEditor/MarkdownTextEditor.stories.tsx +++ /dev/null @@ -1,12 +0,0 @@ -import type { ComponentMeta, ComponentStory } from '@storybook/react'; -import React from 'react'; - -import MarkdownTextEditor from '.'; - -export default { - title: 'Enterprise/Omnichannel/MarkdownTextEditor', - component: MarkdownTextEditor, -} as ComponentMeta; - -export const Default: ComponentStory = (args) => ; -Default.storyName = 'MarkdownTextEditor'; diff --git a/apps/meteor/ee/client/omnichannel/cannedResponses/components/PreviewText.tsx b/apps/meteor/ee/client/omnichannel/cannedResponses/components/PreviewText.tsx deleted file mode 100644 index fa043065b35..00000000000 --- a/apps/meteor/ee/client/omnichannel/cannedResponses/components/PreviewText.tsx +++ /dev/null @@ -1,26 +0,0 @@ -import { Box } from '@rocket.chat/fuselage'; -import type { FC } from 'react'; -import React, { memo } from 'react'; - -import MarkdownText from '../../../../../client/components/MarkdownText'; - -const PreviewText: FC<{ text: string }> = ({ text }) => { - const textM = text.split(/\n/).join(' \n'); - - return ( - - - - ); -}; - -export default memo(PreviewText); diff --git a/apps/meteor/ee/client/omnichannel/cannedResponses/components/TextEditor/IconButton.tsx b/apps/meteor/ee/client/omnichannel/cannedResponses/components/TextEditor/IconButton.tsx deleted file mode 100644 index 155994d6e2a..00000000000 --- a/apps/meteor/ee/client/omnichannel/cannedResponses/components/TextEditor/IconButton.tsx +++ /dev/null @@ -1,30 +0,0 @@ -import { IconButton as Icon } from '@rocket.chat/fuselage'; -import type { ComponentProps, ReactElement } from 'react'; -import React, { memo } from 'react'; - -type IconButtonProps = { - name: ComponentProps['icon']; - action: () => void; - title?: string; -}; - -const IconButton = ({ name, action, title }: IconButtonProps): ReactElement => ( - { - e.stopPropagation(); - e.preventDefault(); - action(); - }} - /> -); -export default memo(IconButton); diff --git a/apps/meteor/ee/client/omnichannel/cannedResponses/components/TextEditor/TextButton.tsx b/apps/meteor/ee/client/omnichannel/cannedResponses/components/TextEditor/TextButton.tsx deleted file mode 100644 index a0cb3bf1390..00000000000 --- a/apps/meteor/ee/client/omnichannel/cannedResponses/components/TextEditor/TextButton.tsx +++ /dev/null @@ -1,31 +0,0 @@ -import { Button } from '@rocket.chat/fuselage'; -import type { TranslationKey } from '@rocket.chat/ui-contexts'; -import { useTranslation } from '@rocket.chat/ui-contexts'; -import React, { forwardRef, memo } from 'react'; - -type TextButtonProps = { - text: TranslationKey; - action: () => void; -}; - -const TextButton = forwardRef(function TextButton({ text, action }, ref) { - const t = useTranslation(); - - return ( - - ); -}); -export default memo(TextButton); diff --git a/apps/meteor/ee/client/omnichannel/cannedResponses/components/TextEditor/TextEditor.stories.tsx b/apps/meteor/ee/client/omnichannel/cannedResponses/components/TextEditor/TextEditor.stories.tsx deleted file mode 100644 index c7604c045a9..00000000000 --- a/apps/meteor/ee/client/omnichannel/cannedResponses/components/TextEditor/TextEditor.stories.tsx +++ /dev/null @@ -1,40 +0,0 @@ -import { Divider } from '@rocket.chat/fuselage'; -import type { ComponentMeta, ComponentStory } from '@storybook/react'; -import React, { useRef } from 'react'; - -import TextEditor from '.'; - -export default { - title: 'Enterprise/Omnichannel/TextEditor', - component: TextEditor, -} as ComponentMeta; - -export const Default: ComponentStory = () => { - // eslint-disable-next-line react-hooks/rules-of-hooks - const textAreaRef = useRef(null); - - const action = () => { - if (!textAreaRef.current) { - return; - } - - const text = textAreaRef.current.value; - const startPos = textAreaRef.current.selectionStart; - const endPos = textAreaRef.current.selectionEnd; - textAreaRef.current.value = `${text.slice(0, startPos)}*${text.slice(startPos, endPos)}*${text.slice(endPos)}`; - textAreaRef.current.focus(); - textAreaRef.current.setSelectionRange(startPos + 1, endPos + 1); - }; - - return ( - - - - - - - - - ); -}; -Default.storyName = 'TextEditor'; diff --git a/apps/meteor/ee/client/omnichannel/cannedResponses/components/TextEditor/Textarea.tsx b/apps/meteor/ee/client/omnichannel/cannedResponses/components/TextEditor/Textarea.tsx deleted file mode 100644 index 69b7124bbb1..00000000000 --- a/apps/meteor/ee/client/omnichannel/cannedResponses/components/TextEditor/Textarea.tsx +++ /dev/null @@ -1,11 +0,0 @@ -import { TextAreaInput } from '@rocket.chat/fuselage'; -import type { ComponentProps } from 'react'; -import React, { forwardRef } from 'react'; - -type TextareaProps = ComponentProps; - -const Textarea = forwardRef(function Textarea(props, ref) { - return ; -}); - -export default Textarea; diff --git a/apps/meteor/ee/client/omnichannel/cannedResponses/components/TextEditor/Toolbox.tsx b/apps/meteor/ee/client/omnichannel/cannedResponses/components/TextEditor/Toolbox.tsx deleted file mode 100644 index 436c5305818..00000000000 --- a/apps/meteor/ee/client/omnichannel/cannedResponses/components/TextEditor/Toolbox.tsx +++ /dev/null @@ -1,19 +0,0 @@ -import { Box } from '@rocket.chat/fuselage'; -import type { FC } from 'react'; -import React from 'react'; - -import IconButton from './IconButton'; -import TextButton from './TextButton'; - -const Toolbox: FC = ({ children }) => ( - <> - - {children} - - -); - -export default Object.assign(Toolbox, { - IconButton, - TextButton, -}); diff --git a/apps/meteor/ee/client/omnichannel/cannedResponses/components/TextEditor/index.tsx b/apps/meteor/ee/client/omnichannel/cannedResponses/components/TextEditor/index.tsx deleted file mode 100644 index c9c54378515..00000000000 --- a/apps/meteor/ee/client/omnichannel/cannedResponses/components/TextEditor/index.tsx +++ /dev/null @@ -1,22 +0,0 @@ -import { Box } from '@rocket.chat/fuselage'; -import type { FC } from 'react'; -import React from 'react'; - -import Textarea from './Textarea'; -import Toolbox from './Toolbox'; - -type TextEditorType = { - Toolbox?: FC; - Textarea?: FC; -}; - -const TextEditor: FC = ({ children }) => ( - - {children} - -); - -export default Object.assign(TextEditor, { - Toolbox, - Textarea, -}); diff --git a/apps/meteor/ee/client/omnichannel/cannedResponses/components/cannedResponseForm.tsx b/apps/meteor/ee/client/omnichannel/cannedResponses/components/cannedResponseForm.tsx index 1b0f85ac6a4..25ad5cf4d43 100644 --- a/apps/meteor/ee/client/omnichannel/cannedResponses/components/cannedResponseForm.tsx +++ b/apps/meteor/ee/client/omnichannel/cannedResponses/components/cannedResponseForm.tsx @@ -7,10 +7,9 @@ import { useFormContext, Controller } from 'react-hook-form'; import AutoCompleteDepartment from '../../../../../client/components/AutoCompleteDepartment'; import Tags from '../../../../../client/components/Omnichannel/Tags'; -import MarkdownTextEditor from './MarkdownTextEditor'; -import PreviewText from './PreviewText'; +import CannedResponsesComposer from './CannedResponsesComposer/CannedResponsesComposer'; +import CannedResponsesComposerPreview from './CannedResponsesComposer/CannedResponsesComposerPreview'; -// TODO: refactor Message field to get proper validation // TODO: refactor Tags field to get proper validation const CannedResponseForm = () => { const t = useTranslation(); @@ -31,6 +30,7 @@ const CannedResponseForm = () => { const [preview, setPreview] = useState(false); const shortcutField = useUniqueId(); + const messageField = useUniqueId(); const publicRadioField = useUniqueId(); const departmentRadioField = useUniqueId(); const privateRadioField = useUniqueId(); @@ -62,27 +62,40 @@ const CannedResponseForm = () => { )} - - - {t('Message')} - {text !== '' && ( - setPreview(!preview)}> - {preview ? t('Editor') : t('Preview')} - - )} - + + {t('Message')} + {text !== '' && ( + setPreview(!preview)}> + {preview ? t('Editor') : t('Preview')} + + )} {preview ? ( - + ) : ( } + rules={{ required: t('The_field_is_required', t('Message')) }} + render={({ field: { value, onChange, name, onBlur } }) => ( + + )} /> )} - {errors?.text && {errors.text.message}} + {errors?.text && ( + + {errors.text.message} + + )} } /> diff --git a/packages/ui-composer/src/MessageComposer/MessageComposerInput.tsx b/packages/ui-composer/src/MessageComposer/MessageComposerInput.tsx index c6204d1c6c1..870a633f9e5 100644 --- a/packages/ui-composer/src/MessageComposer/MessageComposerInput.tsx +++ b/packages/ui-composer/src/MessageComposer/MessageComposerInput.tsx @@ -15,13 +15,12 @@ type MessageComposerInputProps = ComponentProps; const MessageComposerInput = forwardRef(function MessageComposerInput( props: MessageComposerInputProps, - ref: Ref, + ref: Ref, ): ReactElement { return ( ); -- GitLab From f15577f1025b25e79c80508caa7c84cac2309d50 Mon Sep 17 00:00:00 2001 From: Douglas Fabris Date: Thu, 14 Dec 2023 11:54:36 -0300 Subject: [PATCH 028/329] regression: `ResetButton` misaligned on `BooleanSettingInput` (#31236) --- .../views/admin/settings/inputs/BooleanSettingInput.tsx | 8 +++++--- 1 file changed, 5 insertions(+), 3 deletions(-) diff --git a/apps/meteor/client/views/admin/settings/inputs/BooleanSettingInput.tsx b/apps/meteor/client/views/admin/settings/inputs/BooleanSettingInput.tsx index bb6b78999ef..a08cefc5402 100644 --- a/apps/meteor/client/views/admin/settings/inputs/BooleanSettingInput.tsx +++ b/apps/meteor/client/views/admin/settings/inputs/BooleanSettingInput.tsx @@ -1,4 +1,4 @@ -import { FieldLabel, FieldRow, ToggleSwitch } from '@rocket.chat/fuselage'; +import { Box, FieldLabel, FieldRow, ToggleSwitch } from '@rocket.chat/fuselage'; import type { ReactElement, SyntheticEvent } from 'react'; import React from 'react'; @@ -34,8 +34,10 @@ function BooleanSettingInput({ {label} - - {hasResetButton && } + + {hasResetButton && } + + ); } -- GitLab From 2af401c14415d6f2ddac73b42e5bdde9740f9355 Mon Sep 17 00:00:00 2001 From: Douglas Fabris Date: Thu, 14 Dec 2023 12:41:35 -0300 Subject: [PATCH 029/329] chore: `Usercard` small visual changes (#31242) --- .../client/components/UserCard/UserCard.tsx | 20 ++++++++++--------- 1 file changed, 11 insertions(+), 9 deletions(-) diff --git a/apps/meteor/client/components/UserCard/UserCard.tsx b/apps/meteor/client/components/UserCard/UserCard.tsx index 83edd9e3d8c..5678f360af3 100644 --- a/apps/meteor/client/components/UserCard/UserCard.tsx +++ b/apps/meteor/client/components/UserCard/UserCard.tsx @@ -1,5 +1,5 @@ import { css } from '@rocket.chat/css-in-js'; -import { Box, IconButton, Skeleton } from '@rocket.chat/fuselage'; +import { Box, Button, IconButton, Skeleton } from '@rocket.chat/fuselage'; import { useTranslation } from '@rocket.chat/ui-contexts'; import type { ReactNode, ComponentProps, MouseEvent } from 'react'; import React, { forwardRef } from 'react'; @@ -69,7 +69,7 @@ const UserCard = forwardRef(function UserCard( const isLayoutEmbedded = useEmbeddedLayout(); return ( - + {!isLoading && username ? ( @@ -88,7 +88,7 @@ const UserCard = forwardRef(function UserCard( )} - + {isLoading ? : } {nickname && ( @@ -113,13 +113,15 @@ const UserCard = forwardRef(function UserCard( {typeof bio === 'string' ? : bio} )} - {!isLoading && open && !isLayoutEmbedded && {t('See_full_profile')}} + {!isLoading && open && !isLayoutEmbedded && ( +
+ +
+ )}
- {onClose && ( - - - - )} + {onClose && } ); }); -- GitLab From 1e49ed7a6702d98da035d904cb1dc1f8700f9167 Mon Sep 17 00:00:00 2001 From: Aleksander Nicacio da Silva Date: Thu, 14 Dec 2023 16:23:07 -0300 Subject: [PATCH 030/329] chore: omnichannel improved e2e tests (#31118) --- .../Omnichannel/modals/CloseChatModal.tsx | 2 +- .../Omnichannel/modals/ForwardChatModal.tsx | 7 +- .../modals/ReturnChatQueueModal.tsx | 2 +- .../views/omnichannel/agents/AgentEdit.tsx | 20 +- .../views/omnichannel/agents/AgentInfo.tsx | 2 +- .../omnichannel/agents/AgentInfoAction.tsx | 2 +- .../agents/AgentsTable/AgentsTable.tsx | 2 +- .../agents/AgentsTable/AgentsTableRow.tsx | 2 +- .../agents/hooks/useRemoveAgent.tsx | 10 +- .../currentChats/CurrentChatsPage.tsx | 2 +- .../omnichannel/currentChats/FilterByText.tsx | 24 +- .../currentChats/RemoveChatButton.tsx | 9 +- .../RemoveDepartmentModal.tsx | 7 +- .../directory/chats/contextualBar/ChatInfo.js | 2 +- .../modals/PlaceChatOnHoldModal.tsx | 2 +- .../components/cannedResponseForm.tsx | 1 + .../omnichannel/monitors/MonitorsTable.tsx | 16 +- .../ee/client/omnichannel/units/UnitEdit.tsx | 2 +- .../client/omnichannel/units/UnitsTable.tsx | 7 +- .../omnichannel/units/useRemoveUnit.tsx | 10 +- .../omnichannel/omnichannel-agents.spec.ts | 98 +++- .../omnichannel-chat-transfers.spec.ts | 487 ++++++++++++++++++ .../omnichannel-close-inquiry.spec.ts | 6 +- .../omnichannel-current-chats.spec.ts | 329 +++++++++++- .../omnichannel-custom-fields.spec.ts | 2 +- .../omnichannel-departaments-ce.spec.ts | 5 +- .../omnichannel-departaments.spec.ts | 184 ++++--- .../omnichannel/omnichannel-livechat.spec.ts | 4 +- .../omnichannel-manager-role.spec.ts | 253 +++++++++ .../omnichannel-manual-selection.spec.ts | 99 ++++ .../omnichannel-monitor-role.spec.ts | 275 ++++++++++ .../omnichannel/omnichannel-monitors.spec.ts | 96 ++++ .../omnichannel-priorities-sidebar.spec.ts | 8 +- .../omnichannel/omnichannel-takeChat.spec.ts | 16 +- ...channel-transfer-to-another-agents.spec.ts | 130 ++--- .../e2e/omnichannel/omnichannel-units.spec.ts | 170 ++++++ .../page-objects/fragments/home-content.ts | 16 +- .../fragments/home-omnichannel-content.ts | 48 +- .../fragments/omnichannel-sidenav.ts | 28 + .../e2e/page-objects/home-omnichannel.ts | 16 + apps/meteor/tests/e2e/page-objects/index.ts | 2 + .../omnichannel-administration.ts | 14 + .../e2e/page-objects/omnichannel-agents.ts | 63 ++- .../omnichannel-canned-responses.ts | 13 + .../page-objects/omnichannel-current-chats.ts | 106 ++-- .../page-objects/omnichannel-departments.ts | 12 +- .../e2e/page-objects/omnichannel-monitors.ts | 38 ++ .../omnichannel-transfer-chat-modal.ts | 44 ++ .../e2e/page-objects/omnichannel-units.ts | 99 ++++ .../tests/e2e/utils/omnichannel/agents.ts | 30 ++ .../e2e/utils/omnichannel/departments.ts | 57 ++ .../tests/e2e/utils/omnichannel/managers.ts | 19 + .../tests/e2e/utils/omnichannel/monitors.ts | 30 ++ .../tests/e2e/utils/omnichannel/rooms.ts | 98 ++-- .../tests/e2e/utils/omnichannel/tags.ts | 6 + .../tests/e2e/utils/omnichannel/units.ts | 55 ++ .../tests/e2e/utils/omnichannel/utils.ts | 18 + 57 files changed, 2788 insertions(+), 317 deletions(-) create mode 100644 apps/meteor/tests/e2e/omnichannel/omnichannel-chat-transfers.spec.ts create mode 100644 apps/meteor/tests/e2e/omnichannel/omnichannel-manager-role.spec.ts create mode 100644 apps/meteor/tests/e2e/omnichannel/omnichannel-manual-selection.spec.ts create mode 100644 apps/meteor/tests/e2e/omnichannel/omnichannel-monitor-role.spec.ts create mode 100644 apps/meteor/tests/e2e/omnichannel/omnichannel-monitors.spec.ts create mode 100644 apps/meteor/tests/e2e/omnichannel/omnichannel-units.spec.ts create mode 100644 apps/meteor/tests/e2e/page-objects/omnichannel-administration.ts create mode 100644 apps/meteor/tests/e2e/page-objects/omnichannel-canned-responses.ts create mode 100644 apps/meteor/tests/e2e/page-objects/omnichannel-monitors.ts create mode 100644 apps/meteor/tests/e2e/page-objects/omnichannel-transfer-chat-modal.ts create mode 100644 apps/meteor/tests/e2e/page-objects/omnichannel-units.ts create mode 100644 apps/meteor/tests/e2e/utils/omnichannel/agents.ts create mode 100644 apps/meteor/tests/e2e/utils/omnichannel/departments.ts create mode 100644 apps/meteor/tests/e2e/utils/omnichannel/managers.ts create mode 100644 apps/meteor/tests/e2e/utils/omnichannel/monitors.ts create mode 100644 apps/meteor/tests/e2e/utils/omnichannel/tags.ts create mode 100644 apps/meteor/tests/e2e/utils/omnichannel/units.ts create mode 100644 apps/meteor/tests/e2e/utils/omnichannel/utils.ts diff --git a/apps/meteor/client/components/Omnichannel/modals/CloseChatModal.tsx b/apps/meteor/client/components/Omnichannel/modals/CloseChatModal.tsx index d16172ddc7f..5d0b8e40083 100644 --- a/apps/meteor/client/components/Omnichannel/modals/CloseChatModal.tsx +++ b/apps/meteor/client/components/Omnichannel/modals/CloseChatModal.tsx @@ -138,7 +138,7 @@ const CloseChatModal = ({ if (commentRequired || tagRequired || canSendTranscript) { return ( - }> + }> {t('Wrap_up_conversation')} diff --git a/apps/meteor/client/components/Omnichannel/modals/ForwardChatModal.tsx b/apps/meteor/client/components/Omnichannel/modals/ForwardChatModal.tsx index a4d095fdb90..5e65c43a957 100644 --- a/apps/meteor/client/components/Omnichannel/modals/ForwardChatModal.tsx +++ b/apps/meteor/client/components/Omnichannel/modals/ForwardChatModal.tsx @@ -82,7 +82,11 @@ const ForwardChatModal = ({ }, [register]); return ( - } {...props}> + } + {...props} + data-qa-id='forward-chat-modal' + > {t('Forward_chat')} @@ -100,6 +104,7 @@ const ForwardChatModal = ({ options={departments} maxWidth='100%' placeholder={t('Select_an_option')} + data-qa-id='forward-to-department' onChange={(value: string): void => { setValue('department', value); }} diff --git a/apps/meteor/client/components/Omnichannel/modals/ReturnChatQueueModal.tsx b/apps/meteor/client/components/Omnichannel/modals/ReturnChatQueueModal.tsx index 476368aed97..b4f78961865 100644 --- a/apps/meteor/client/components/Omnichannel/modals/ReturnChatQueueModal.tsx +++ b/apps/meteor/client/components/Omnichannel/modals/ReturnChatQueueModal.tsx @@ -12,7 +12,7 @@ const ReturnChatQueueModal: FC = ({ onCancel, onMoveC const t = useTranslation(); return ( - + {t('Return_to_the_queue')} diff --git a/apps/meteor/client/views/omnichannel/agents/AgentEdit.tsx b/apps/meteor/client/views/omnichannel/agents/AgentEdit.tsx index e23baf346f7..afe7b162ea0 100644 --- a/apps/meteor/client/views/omnichannel/agents/AgentEdit.tsx +++ b/apps/meteor/client/views/omnichannel/agents/AgentEdit.tsx @@ -112,7 +112,7 @@ const AgentEdit = ({ agentData, userDepartments, availableDepartments }: AgentEd const voipExtensionField = useUniqueId(); return ( - + {t('Edit_User')} router.navigate('/omnichannel/agents')} /> @@ -122,7 +122,7 @@ const AgentEdit = ({ agentData, userDepartments, availableDepartments }: AgentEd
{username && ( - + )} @@ -132,7 +132,7 @@ const AgentEdit = ({ agentData, userDepartments, availableDepartments }: AgentEd } + render={({ field }) => } /> @@ -145,7 +145,7 @@ const AgentEdit = ({ agentData, userDepartments, availableDepartments }: AgentEd render={({ field }) => ( } @@ -163,7 +163,7 @@ const AgentEdit = ({ agentData, userDepartments, availableDepartments }: AgentEd render={({ field }) => ( } @@ -181,7 +181,7 @@ const AgentEdit = ({ agentData, userDepartments, availableDepartments }: AgentEd render={({ field }) => ( ( + + ) => setText(event.currentTarget.value)} /> + ) => setText(event.currentTarget.value)} + /> ); diff --git a/apps/meteor/client/views/omnichannel/directory/chats/contextualBar/ChatInfo.js b/apps/meteor/client/views/omnichannel/directory/chats/contextualBar/ChatInfo.js index c1ced190443..ab42a510cff 100644 --- a/apps/meteor/client/views/omnichannel/directory/chats/contextualBar/ChatInfo.js +++ b/apps/meteor/client/views/omnichannel/directory/chats/contextualBar/ChatInfo.js @@ -173,7 +173,7 @@ function ChatInfo({ id, route }) { - diff --git a/apps/meteor/ee/app/livechat-enterprise/client/components/modals/PlaceChatOnHoldModal.tsx b/apps/meteor/ee/app/livechat-enterprise/client/components/modals/PlaceChatOnHoldModal.tsx index 624fe58b3c4..c70f32c11c0 100644 --- a/apps/meteor/ee/app/livechat-enterprise/client/components/modals/PlaceChatOnHoldModal.tsx +++ b/apps/meteor/ee/app/livechat-enterprise/client/components/modals/PlaceChatOnHoldModal.tsx @@ -13,7 +13,7 @@ const PlaceChatOnHoldModal: FC = ({ onCancel, onOnHol const t = useTranslation(); return ( - + {t('Omnichannel_onHold_Chat')} diff --git a/apps/meteor/ee/client/omnichannel/cannedResponses/components/cannedResponseForm.tsx b/apps/meteor/ee/client/omnichannel/cannedResponses/components/cannedResponseForm.tsx index 25ad5cf4d43..636bc8a44d2 100644 --- a/apps/meteor/ee/client/omnichannel/cannedResponses/components/cannedResponseForm.tsx +++ b/apps/meteor/ee/client/omnichannel/cannedResponses/components/cannedResponseForm.tsx @@ -116,6 +116,7 @@ const CannedResponseForm = () => { disabled={hasMonitorPermission && !hasManagerPermission} checked={value === 'global'} aria-describedby={`${publicRadioField}-hint`} + data-qa-id='canned-response-public-radio' /> )} /> diff --git a/apps/meteor/ee/client/omnichannel/monitors/MonitorsTable.tsx b/apps/meteor/ee/client/omnichannel/monitors/MonitorsTable.tsx index 21eba51275b..d403cbda98f 100644 --- a/apps/meteor/ee/client/omnichannel/monitors/MonitorsTable.tsx +++ b/apps/meteor/ee/client/omnichannel/monitors/MonitorsTable.tsx @@ -104,7 +104,15 @@ const MonitorsTable = () => { setModal(); }; - setModal( setModal()} confirmText={t('Delete')} />); + setModal( + setModal()} + confirmText={t('Delete')} + />, + ); }; const headers = useMemo( @@ -129,7 +137,7 @@ const MonitorsTable = () => { {t('Username')} - void} /> + void} /> @@ -157,11 +165,11 @@ const MonitorsTable = () => { )} {isSuccess && data.monitors.length > 0 && ( <> - + {headers} {data.monitors?.map((monitor) => ( - + {monitor.name} {monitor.username} {monitor.email} diff --git a/apps/meteor/ee/client/omnichannel/units/UnitEdit.tsx b/apps/meteor/ee/client/omnichannel/units/UnitEdit.tsx index bc113cec656..85f8076dc69 100644 --- a/apps/meteor/ee/client/omnichannel/units/UnitEdit.tsx +++ b/apps/meteor/ee/client/omnichannel/units/UnitEdit.tsx @@ -151,7 +151,7 @@ const UnitEdit = ({ unitData, unitMonitors, unitDepartments }: UnitEditProps) => const monitorsField = useUniqueId(); return ( - + {_id ? t('Edit_Unit') : t('New_Unit')} router.navigate('/omnichannel/units')}> diff --git a/apps/meteor/ee/client/omnichannel/units/UnitsTable.tsx b/apps/meteor/ee/client/omnichannel/units/UnitsTable.tsx index 673eb136fd7..17c8abcbec9 100644 --- a/apps/meteor/ee/client/omnichannel/units/UnitsTable.tsx +++ b/apps/meteor/ee/client/omnichannel/units/UnitsTable.tsx @@ -71,7 +71,7 @@ const UnitsTable = () => { <> {((isSuccess && data?.units.length > 0) || queryHasChanged) && setFilter(text)} />} {isLoading && ( - + {headers} @@ -92,11 +92,11 @@ const UnitsTable = () => { )} {isSuccess && data?.units.length > 0 && ( <> - + {headers} {data.units.map(({ _id, name, visibility }) => ( - + {name} {visibility} @@ -104,6 +104,7 @@ const UnitsTable = () => { icon='trash' small title={t('Remove')} + data-qa-id={`remove-unit-${name}`} onClick={(e) => { e.stopPropagation(); handleDelete(_id); diff --git a/apps/meteor/ee/client/omnichannel/units/useRemoveUnit.tsx b/apps/meteor/ee/client/omnichannel/units/useRemoveUnit.tsx index a4052c55b8e..61910dc37bc 100644 --- a/apps/meteor/ee/client/omnichannel/units/useRemoveUnit.tsx +++ b/apps/meteor/ee/client/omnichannel/units/useRemoveUnit.tsx @@ -28,7 +28,15 @@ export const useRemoveUnit = () => { } }; - setModal( setModal()} confirmText={t('Delete')} />); + setModal( + setModal()} + confirmText={t('Delete')} + />, + ); }); return handleDelete; diff --git a/apps/meteor/tests/e2e/omnichannel/omnichannel-agents.spec.ts b/apps/meteor/tests/e2e/omnichannel/omnichannel-agents.spec.ts index 263c5660ec1..658296fb338 100644 --- a/apps/meteor/tests/e2e/omnichannel/omnichannel-agents.spec.ts +++ b/apps/meteor/tests/e2e/omnichannel/omnichannel-agents.spec.ts @@ -1,12 +1,21 @@ +import { IS_EE } from '../config/constants'; import { Users } from '../fixtures/userStates'; import { OmnichannelAgents } from '../page-objects'; +import { createDepartment } from '../utils/omnichannel/departments'; import { test, expect } from '../utils/test'; test.use({ storageState: Users.admin.state }); -test.describe.serial('omnichannel-agents', () => { +test.describe.serial('OC - Manage Agents', () => { let poOmnichannelAgents: OmnichannelAgents; + let department: Awaited>; + // Create agent and department + test.beforeEach(async ({ api }) => { + department = await createDepartment(api); + }); + + // Create page object and redirect to home test.beforeEach(async ({ page }) => { poOmnichannelAgents = new OmnichannelAgents(page); @@ -14,31 +23,86 @@ test.describe.serial('omnichannel-agents', () => { await poOmnichannelAgents.sidenav.linkAgents.click(); }); - test('expect add "user1" as agent', async ({ page }) => { - await poOmnichannelAgents.inputUsername.type('user1'); - await page.locator('role=option[name="user1"]').click(); - await poOmnichannelAgents.btnAdd.click(); + // Ensure that there is no leftover data even if test fails + test.afterEach(async ({ api }) => { + await await api.delete('/livechat/users/agent/user1'); + await api.post('/settings/Omnichannel_enable_department_removal', { value: true }).then((res) => expect(res.status()).toBe(200)); + await department.delete(); + await api.post('/settings/Omnichannel_enable_department_removal', { value: false }).then((res) => expect(res.status()).toBe(200)); + }); - await poOmnichannelAgents.inputSearch.fill('user1'); - await expect(poOmnichannelAgents.firstRowInTable).toBeVisible(); + test('OC - Manage Agents - Add, search and remove using table', async ({ page }) => { + await test.step('expect "user1" be first ', async () => { + await poOmnichannelAgents.inputUsername.type('user'); + await expect(page.locator('role=option[name="user1"]')).toContainText('user1'); + + await poOmnichannelAgents.inputUsername.fill(''); + }); + + await test.step('expect add "user1" as agent', async () => { + await poOmnichannelAgents.selectUsername('user1'); + await poOmnichannelAgents.btnAdd.click(); + + await poOmnichannelAgents.inputSearch.fill('user1'); + await expect(poOmnichannelAgents.firstRowInTable).toBeVisible(); + await expect(poOmnichannelAgents.firstRowInTable).toHaveText('user1'); + }); + + await test.step('expect remove "user1" as agent', async () => { + await poOmnichannelAgents.inputSearch.fill('user1'); + await poOmnichannelAgents.btnDeletefirstRowInTable.click(); + await poOmnichannelAgents.btnModalRemove.click(); + + await poOmnichannelAgents.inputSearch.fill('user1'); + await expect(poOmnichannelAgents.findRowByUsername('user1')).not.toBeVisible(); + }); }); - test('expect update "user1" status', async ({ page }) => { + test('OC - Manage Agents [CE]- Edit and Remove', async () => { + test.skip(IS_EE, 'Community Edition Only'); + + await poOmnichannelAgents.selectUsername('user1'); + await poOmnichannelAgents.btnAdd.click(); + await poOmnichannelAgents.inputSearch.fill('user1'); await poOmnichannelAgents.firstRowInTable.click(); - await poOmnichannelAgents.btnEdit.click(); - await poOmnichannelAgents.btnStatus.click(); - await page.locator(`.rcx-option__content:has-text("Not available")`).click(); - await poOmnichannelAgents.btnSave.click(); + + await test.step('expect max chats fields to be hidden', async () => { + await expect(poOmnichannelAgents.inputMaxChats).toBeHidden(); + }); + + await test.step('expect update "user1" information', async () => { + await poOmnichannelAgents.selectStatus('Not available'); + await poOmnichannelAgents.selectDepartment(department.data.name); + await poOmnichannelAgents.btnSave.click(); + }); + + await test.step('expect removing "user1" via sidebar', async () => { + await poOmnichannelAgents.inputSearch.fill('user1'); + await poOmnichannelAgents.firstRowInTable.click(); + await poOmnichannelAgents.btnRemove.click(); + }); }); - test('expect remove "user1" as agent', async () => { - await poOmnichannelAgents.inputSearch.fill('user1'); - await poOmnichannelAgents.btnDeletefirstRowInTable.click(); - await poOmnichannelAgents.btnModalRemove.click(); + test('OC - Manage Agents [EE] - Edit ', async () => { + test.skip(!IS_EE, 'Enterprise Only'); + + await poOmnichannelAgents.selectUsername('user1'); + await poOmnichannelAgents.btnAdd.click(); await poOmnichannelAgents.inputSearch.fill('user1'); - await expect(poOmnichannelAgents.firstRowInTable).toBeHidden(); + await poOmnichannelAgents.findRowByUsername('user1').click(); + await poOmnichannelAgents.btnEdit.click(); + + await test.step('expect max chats field to be visible', async () => { + await expect(poOmnichannelAgents.inputMaxChats).toBeVisible(); + }); + + await test.step('expect update "user1" information', async () => { + await poOmnichannelAgents.inputMaxChats.click(); + await poOmnichannelAgents.inputMaxChats.fill('2'); + await poOmnichannelAgents.btnSave.click(); + }); }); }); diff --git a/apps/meteor/tests/e2e/omnichannel/omnichannel-chat-transfers.spec.ts b/apps/meteor/tests/e2e/omnichannel/omnichannel-chat-transfers.spec.ts new file mode 100644 index 00000000000..fd5715c691f --- /dev/null +++ b/apps/meteor/tests/e2e/omnichannel/omnichannel-chat-transfers.spec.ts @@ -0,0 +1,487 @@ +import { Page } from '@playwright/test'; + +import { IS_EE } from '../config/constants'; +import { createAuxContext } from '../fixtures/createAuxContext'; +import { Users } from '../fixtures/userStates'; +import { HomeOmnichannel } from '../page-objects'; +import { createAgent, makeAgentAvailable } from '../utils/omnichannel/agents'; +import { addAgentToDepartment, createDepartment } from '../utils/omnichannel/departments'; +import { createManager } from '../utils/omnichannel/managers'; +import { createMonitor } from '../utils/omnichannel/monitors'; +import { createConversation } from '../utils/omnichannel/rooms'; +import { createOrUpdateUnit } from '../utils/omnichannel/units'; +import { expect, test } from '../utils/test'; + +const wrapSession = async ({ page }: { page: Page }) => ({ page, poHomeOmnichannel: new HomeOmnichannel(page) }); + +test.use({ storageState: Users.user3.state }); + +test.skip(!IS_EE, 'Enterprise Edition Only'); + +test.describe('OC - Chat transfers [Monitor role]', () => { + let departments: Awaited>[]; + let conversations: Awaited>[]; + let agents: Awaited>[]; + let monitors: Awaited>[]; + let units: Awaited>[]; + let sessions: { page: Page; poHomeOmnichannel: HomeOmnichannel }[]; + + let poOmnichannel: HomeOmnichannel; + + // Create agents + test.beforeAll(async ({ api }) => { + agents = await Promise.all([createAgent(api, 'user1'), createAgent(api, 'user2'), createAgent(api, 'rocketchat.internal.admin.test')]); + + (await Promise.all(agents.map(({ data: agent }) => makeAgentAvailable(api, agent._id)))).forEach((res) => { + expect(res.status()).toBe(200); + }); + }); + + // Create departments + test.beforeAll(async ({ api }) => { + departments = await Promise.all([createDepartment(api), createDepartment(api)]); + }); + + // Add agents to departments + test.beforeAll(async ({ api }) => { + const [departmentA, departmentB] = departments.map(({ data }) => data); + + const promises = await Promise.all([ + addAgentToDepartment(api, { department: departmentA, agentId: 'user1' }), + addAgentToDepartment(api, { department: departmentA, agentId: 'rocketchat.internal.admin.test' }), + addAgentToDepartment(api, { department: departmentB, agentId: 'user2' }), + ]); + + promises.forEach((res) => expect(res.status()).toBe(200)); + }); + + // Create conversations + test.beforeAll(async ({ api }) => { + const [departmentA] = departments.map(({ data }) => data); + + conversations = await Promise.all([ + createConversation(api, { + agentId: `user1`, + departmentId: departmentA._id, + }), + createConversation(api, { + agentId: `user1`, + departmentId: departmentA._id, + }), + createConversation(api, { + agentId: `user1`, + departmentId: departmentA._id, + }), + ]); + }); + + // Create monitors + test.beforeAll(async ({ api }) => { + monitors = await Promise.all([createMonitor(api, 'user3')]); + }); + + // Create units + test.beforeAll(async ({ api }) => { + const [departmentA, departmentB] = departments.map(({ data }) => data); + + units = await Promise.all([ + createOrUpdateUnit(api, { + monitors: [{ monitorId: 'user3', username: 'user3' }], + departments: [{ departmentId: departmentA._id }], + }), + createOrUpdateUnit(api, { + monitors: [{ monitorId: 'rocketchat.internal.admin.test', username: 'rocketchat.internal.admin.test' }], + departments: [{ departmentId: departmentB._id }], + }), + ]); + }); + + // Create sessions + test.beforeEach(async ({ browser }) => { + sessions = await Promise.all([ + createAuxContext(browser, Users.user1).then(wrapSession), + createAuxContext(browser, Users.user2).then(wrapSession), + createAuxContext(browser, Users.admin).then(wrapSession), + ]); + }); + + test.beforeEach(async ({ page }) => { + poOmnichannel = new HomeOmnichannel(page); + + await page.goto('/omnichannel/current'); + }); + + // Close sessions + test.afterEach(async () => { + await Promise.all(sessions.map(({ page }) => page.close())); + }); + + test.afterAll(async () => { + await Promise.all([ + ...conversations.map((conversation) => conversation.delete()), + ...monitors.map((monitor) => monitor.delete()), + ...agents.map((agent) => agent.delete()), + ...units.map((unit) => unit.delete()), + ...departments.map((department) => department.delete()), + ]); + }); + + test(`OC - Chat transfers [Monitor role] - Transfer to another department`, async ({ page }) => { + const [, departmentB] = departments.map(({ data }) => data); + const [roomA] = conversations.map(({ data }) => data.room); + const [agentA, agentB] = sessions; + + await test.step('expect room a to bot be visible for user2', async () => { + await expect(agentB.poHomeOmnichannel.sidenav.getSidebarItemByName(roomA.fname)).not.toBeVisible(); + }); + + await test.step('expect to be able to join chats from same unit', async () => { + await poOmnichannel.currentChats.findRowByName(roomA.fname).click(); + await expect(page).toHaveURL(`/omnichannel/current/${roomA._id}`); + await poOmnichannel.content.btnForwardChat.click(); + }); + + await test.step('expect agent and department fields to be visible and enabled', async () => { + await expect(poOmnichannel.content.forwardChatModal.inputFowardUser).toBeEnabled(); + await expect(poOmnichannel.content.forwardChatModal.inputFowardDepartment).toBeEnabled(); + await expect(poOmnichannel.content.forwardChatModal.btnForward).toBeDisabled(); + }); + + await test.step('expect to transfer from dep a to dep b', async () => { + await poOmnichannel.content.forwardChatModal.selectDepartment(departmentB.name); + await poOmnichannel.content.forwardChatModal.inputComment.type('any_comment'); + await expect(poOmnichannel.content.forwardChatModal.btnForward).toBeEnabled(); + await poOmnichannel.content.forwardChatModal.btnForward.click(); + // await expect(agentA.poHomeOmnichannel.toastSuccess).toBeVisible(); + }); + + await test.step('expect conversation to have been assigned to user 2', async () => { + await expect(agentA.page).toHaveURL(`/home`); + await expect(agentA.poHomeOmnichannel.sidenav.getSidebarItemByName(roomA.fname)).not.toBeVisible(); + await expect(agentB.poHomeOmnichannel.sidenav.getSidebarItemByName(roomA.fname)).toBeVisible(); + }); + + await test.step('expect user 1 to have left the conversation', async () => { + await agentB.poHomeOmnichannel.sidenav.getSidebarItemByName(roomA.fname).click(); + await agentB.poHomeOmnichannel.content.findSystemMessage( + `Transfer: user3 transferred the chat to the department ${departmentB.name}}`, + ); + await agentB.poHomeOmnichannel.content.findSystemMessage('left the channel'); + }); + }); + + test(`OC - Chat transfers [Monitor role] - Transfer to another agent, different department`, async ({ page }) => { + const [, roomB] = conversations.map(({ data }) => data.room); + const [agentA, agentB] = sessions; + + await test.step('expect room a to bot be visible for user2', async () => { + await expect(agentB.poHomeOmnichannel.sidenav.getSidebarItemByName(roomB.fname)).not.toBeVisible(); + }); + + await test.step('expect to be able to join chats from same unit', async () => { + await poOmnichannel.currentChats.findRowByName(roomB.fname).click(); + await expect(page).toHaveURL(`/omnichannel/current/${roomB._id}`); + await poOmnichannel.content.btnForwardChat.click(); + }); + + await test.step('expect agent and department fields to be visible and enabled', async () => { + await expect(poOmnichannel.content.forwardChatModal.inputFowardUser).toBeEnabled(); + await expect(poOmnichannel.content.forwardChatModal.inputFowardDepartment).toBeEnabled(); + await expect(poOmnichannel.content.forwardChatModal.btnForward).toBeDisabled(); + }); + + await test.step('expect to transfer from user1 to user2', async () => { + await poOmnichannel.content.forwardChatModal.selectUser(`user2`); + await poOmnichannel.content.forwardChatModal.inputComment.type('any_comment'); + await expect(poOmnichannel.content.forwardChatModal.btnForward).toBeEnabled(); + await poOmnichannel.content.forwardChatModal.btnForward.click(); + // await expect(agentA.poHomeOmnichannel.toastSuccess).toBeVisible(); + }); + + await test.step('expect conversation to have been assigned to user 2', async () => { + await expect(agentA.page).toHaveURL(`/home`); + await expect(agentA.poHomeOmnichannel.sidenav.getSidebarItemByName(roomB.fname)).not.toBeVisible(); + await expect(agentB.poHomeOmnichannel.sidenav.getSidebarItemByName(roomB.fname)).toBeVisible(); + }); + + await test.step('expect user 1 to have left the conversation', async () => { + await agentB.poHomeOmnichannel.sidenav.getSidebarItemByName(roomB.fname).click(); + await expect( + agentB.poHomeOmnichannel.content.findSystemMessage( + `New Chat Transfer: user3 transferred the chat to user2 with a comment: any_comment`, + ), + ).toBeVisible(); + await expect(agentB.poHomeOmnichannel.content.findSystemMessage('left the channel')).toBeVisible(); + }); + }); + + test(`OC - Chat transfers [Monitor role] - Transfer to another agent, same department`, async ({ page }) => { + const [, , roomC] = conversations.map(({ data }) => data.room); + const [agentA, , agentC] = sessions; + + await test.step('expect room a to bot be visible for user2', async () => { + await expect(agentC.poHomeOmnichannel.sidenav.getSidebarItemByName(roomC.fname)).not.toBeVisible(); + }); + + await test.step('expect to be able to join chats from same unit', async () => { + await poOmnichannel.currentChats.findRowByName(roomC.fname).click(); + await expect(page).toHaveURL(`/omnichannel/current/${roomC._id}`); + await poOmnichannel.content.btnForwardChat.click(); + }); + + await test.step('expect agent and department fields to be visible and enabled', async () => { + await expect(poOmnichannel.content.forwardChatModal.inputFowardUser).toBeEnabled(); + await expect(poOmnichannel.content.forwardChatModal.inputFowardDepartment).toBeEnabled(); + await expect(poOmnichannel.content.forwardChatModal.btnForward).toBeDisabled(); + }); + + await test.step('expect to transfer from dep a to dep b', async () => { + await poOmnichannel.content.forwardChatModal.selectUser('rocketchat.internal.admin.test'); + await poOmnichannel.content.forwardChatModal.inputComment.type('any_comment'); + await expect(poOmnichannel.content.forwardChatModal.btnForward).toBeEnabled(); + await poOmnichannel.content.forwardChatModal.btnForward.click(); + // await expect(agentA.poHomeOmnichannel.toastSuccess).toBeVisible(); + }); + + await test.step('expect conversation to have been assigned to user 2', async () => { + await expect(agentA.page).toHaveURL(`/home`); + await expect(agentA.poHomeOmnichannel.sidenav.getSidebarItemByName(roomC.fname)).not.toBeVisible(); + await expect(agentC.poHomeOmnichannel.sidenav.getSidebarItemByName(roomC.fname)).toBeVisible(); + }); + + await test.step('expect user 1 to have left the conversation', async () => { + await agentC.poHomeOmnichannel.sidenav.getSidebarItemByName(roomC.fname).click(); + await expect( + agentC.poHomeOmnichannel.content.findSystemMessage( + `New Chat Transfer: user3 transferred the chat to RocketChat Internal Admin Test with a comment: any_comment`, + ), + ).toBeVisible(); + await expect(agentC.poHomeOmnichannel.content.findSystemMessage('left the channel')).toBeVisible(); + }); + }); +}); + +test.describe('OC - Chat transfers [Manager role]', () => { + let departments: Awaited>[]; + let conversations: Awaited>[]; + let agents: Awaited>[]; + let managers: Awaited>[]; + let sessions: { page: Page; poHomeOmnichannel: HomeOmnichannel }[]; + + let poOmnichannel: HomeOmnichannel; + + // Create agents + test.beforeAll(async ({ api }) => { + agents = await Promise.all([createAgent(api, 'user1'), createAgent(api, 'user2'), createAgent(api, 'rocketchat.internal.admin.test')]); + + (await Promise.all(agents.map(({ data: agent }) => makeAgentAvailable(api, agent._id)))).forEach((res) => { + expect(res.status()).toBe(200); + }); + }); + + // Create managers + test.beforeAll(async ({ api }) => { + managers = await Promise.all([createManager(api, 'user3')]); + }); + + // Create departments + test.beforeAll(async ({ api }) => { + departments = await Promise.all([createDepartment(api), createDepartment(api)]); + }); + + // Add agents to departments + test.beforeAll(async ({ api }) => { + const [departmentA, departmentB] = departments.map(({ data }) => data); + + const promises = await Promise.all([ + addAgentToDepartment(api, { department: departmentA, agentId: 'user1' }), + addAgentToDepartment(api, { department: departmentA, agentId: 'rocketchat.internal.admin.test' }), + addAgentToDepartment(api, { department: departmentB, agentId: 'user2' }), + ]); + + promises.forEach((res) => expect(res.status()).toBe(200)); + }); + + // Create conversations + test.beforeAll(async ({ api }) => { + const [departmentA] = departments.map(({ data }) => data); + + conversations = await Promise.all([ + createConversation(api, { + agentId: `user1`, + departmentId: departmentA._id, + }), + createConversation(api, { + agentId: `user1`, + departmentId: departmentA._id, + }), + createConversation(api, { + agentId: `user1`, + departmentId: departmentA._id, + }), + ]); + }); + + // Create sessions + test.beforeEach(async ({ browser }) => { + sessions = await Promise.all([ + createAuxContext(browser, Users.user1).then(wrapSession), + createAuxContext(browser, Users.user2).then(wrapSession), + createAuxContext(browser, Users.admin).then(wrapSession), + ]); + }); + + test.beforeEach(async ({ page }) => { + poOmnichannel = new HomeOmnichannel(page); + + await page.goto('/omnichannel/current'); + }); + + // Close sessions + test.afterEach(async () => { + await Promise.all(sessions.map(({ page }) => page.close())); + }); + + test.afterAll(async () => { + await Promise.all([ + ...conversations.map((conversation) => conversation.delete()), + ...managers.map((manager) => manager.delete()), + ...agents.map((agent) => agent.delete()), + ...departments.map((department) => department.delete()), + ]); + }); + + test(`OC - Chat transfers [Manager role] - Transfer to another department`, async ({ page }) => { + const [, departmentB] = departments.map(({ data }) => data); + const [roomA] = conversations.map(({ data }) => data.room); + const [agentA, agentB] = sessions; + + await test.step('expect room a to bot be visible for user2', async () => { + await expect(agentB.poHomeOmnichannel.sidenav.getSidebarItemByName(roomA.fname)).not.toBeVisible(); + }); + + await test.step('expect to be able to join chats from same unit', async () => { + await poOmnichannel.currentChats.findRowByName(roomA.fname).click(); + await expect(page).toHaveURL(`/omnichannel/current/${roomA._id}`); + await poOmnichannel.content.btnForwardChat.click(); + }); + + await test.step('expect agent and department fields to be visible and enabled', async () => { + await expect(poOmnichannel.content.forwardChatModal.inputFowardUser).toBeEnabled(); + await expect(poOmnichannel.content.forwardChatModal.inputFowardDepartment).toBeEnabled(); + await expect(poOmnichannel.content.forwardChatModal.btnForward).toBeDisabled(); + }); + + await test.step('expect to transfer from dep a to dep b', async () => { + await poOmnichannel.content.forwardChatModal.selectDepartment(departmentB.name); + await poOmnichannel.content.forwardChatModal.inputComment.type('any_comment'); + await expect(poOmnichannel.content.forwardChatModal.btnForward).toBeEnabled(); + await poOmnichannel.content.forwardChatModal.btnForward.click(); + // await expect(agentA.poHomeOmnichannel.toastSuccess).toBeVisible(); + }); + + await test.step('expect conversation to have been assigned to user 2', async () => { + await expect(agentA.page).toHaveURL(`/home`); + await expect(agentA.poHomeOmnichannel.sidenav.getSidebarItemByName(roomA.fname)).not.toBeVisible(); + await expect(agentB.poHomeOmnichannel.sidenav.getSidebarItemByName(roomA.fname)).toBeVisible(); + }); + + await test.step('expect user 1 to have left the conversation', async () => { + await agentB.poHomeOmnichannel.sidenav.getSidebarItemByName(roomA.fname).click(); + await agentB.poHomeOmnichannel.content.findSystemMessage( + `Transfer: user3 transferred the chat to the department ${departmentB.name}}`, + ); + await agentB.poHomeOmnichannel.content.findSystemMessage('left the channel'); + }); + }); + + test(`OC - Chat transfers [Manager role] - Transfer to another agent, different department`, async ({ page }) => { + const [, roomB] = conversations.map(({ data }) => data.room); + const [agentA, agentB] = sessions; + + await test.step('expect room a to bot be visible for user2', async () => { + await expect(agentB.poHomeOmnichannel.sidenav.getSidebarItemByName(roomB.fname)).not.toBeVisible(); + }); + + await test.step('expect to be able to join chats from same unit', async () => { + await poOmnichannel.currentChats.findRowByName(roomB.fname).click(); + await expect(page).toHaveURL(`/omnichannel/current/${roomB._id}`); + await poOmnichannel.content.btnForwardChat.click(); + }); + + await test.step('expect agent and department fields to be visible and enabled', async () => { + await expect(poOmnichannel.content.forwardChatModal.inputFowardUser).toBeEnabled(); + await expect(poOmnichannel.content.forwardChatModal.inputFowardDepartment).toBeEnabled(); + await expect(poOmnichannel.content.forwardChatModal.btnForward).toBeDisabled(); + }); + + await test.step('expect to transfer from user1 to user2', async () => { + await poOmnichannel.content.forwardChatModal.selectUser(`user2`); + await poOmnichannel.content.forwardChatModal.inputComment.type('any_comment'); + await expect(poOmnichannel.content.forwardChatModal.btnForward).toBeEnabled(); + await poOmnichannel.content.forwardChatModal.btnForward.click(); + // await expect(agentA.poHomeOmnichannel.toastSuccess).toBeVisible(); + }); + + await test.step('expect conversation to have been assigned to user 2', async () => { + await expect(agentA.page).toHaveURL(`/home`); + await expect(agentA.poHomeOmnichannel.sidenav.getSidebarItemByName(roomB.fname)).not.toBeVisible(); + await expect(agentB.poHomeOmnichannel.sidenav.getSidebarItemByName(roomB.fname)).toBeVisible(); + }); + + await test.step('expect user 1 to have left the conversation', async () => { + await agentB.poHomeOmnichannel.sidenav.getSidebarItemByName(roomB.fname).click(); + await expect( + agentB.poHomeOmnichannel.content.findSystemMessage( + `New Chat Transfer: user3 transferred the chat to user2 with a comment: any_comment`, + ), + ).toBeVisible(); + await expect(agentB.poHomeOmnichannel.content.findSystemMessage('left the channel')).toBeVisible(); + }); + }); + + test(`OC - Chat transfers [Manager role] - Transfer to another agent, same department`, async ({ page }) => { + const [, , roomC] = conversations.map(({ data }) => data.room); + const [agentA, , agentC] = sessions; + + await test.step('expect room a to bot be visible for user2', async () => { + await expect(agentC.poHomeOmnichannel.sidenav.getSidebarItemByName(roomC.fname)).not.toBeVisible(); + }); + + await test.step('expect to be able to join chats from same unit', async () => { + await poOmnichannel.currentChats.findRowByName(roomC.fname).click(); + await expect(page).toHaveURL(`/omnichannel/current/${roomC._id}`); + await poOmnichannel.content.btnForwardChat.click(); + }); + + await test.step('expect agent and department fields to be visible and enabled', async () => { + await expect(poOmnichannel.content.forwardChatModal.inputFowardUser).toBeEnabled(); + await expect(poOmnichannel.content.forwardChatModal.inputFowardDepartment).toBeEnabled(); + await expect(poOmnichannel.content.forwardChatModal.btnForward).toBeDisabled(); + }); + + await test.step('expect to transfer from dep a to dep b', async () => { + await poOmnichannel.content.forwardChatModal.selectUser('rocketchat.internal.admin.test'); + await poOmnichannel.content.forwardChatModal.inputComment.type('any_comment'); + await expect(poOmnichannel.content.forwardChatModal.btnForward).toBeEnabled(); + await poOmnichannel.content.forwardChatModal.btnForward.click(); + // await expect(agentA.poHomeOmnichannel.toastSuccess).toBeVisible(); + }); + + await test.step('expect conversation to have been assigned to user 2', async () => { + await expect(agentA.page).toHaveURL(`/home`); + await expect(agentA.poHomeOmnichannel.sidenav.getSidebarItemByName(roomC.fname)).not.toBeVisible(); + await expect(agentC.poHomeOmnichannel.sidenav.getSidebarItemByName(roomC.fname)).toBeVisible(); + }); + + await test.step('expect user 1 to have left the conversation', async () => { + await agentC.poHomeOmnichannel.sidenav.getSidebarItemByName(roomC.fname).click(); + await expect( + agentC.poHomeOmnichannel.content.findSystemMessage( + `New Chat Transfer: user3 transferred the chat to RocketChat Internal Admin Test with a comment: any_comment`, + ), + ).toBeVisible(); + await expect(agentC.poHomeOmnichannel.content.findSystemMessage('left the channel')).toBeVisible(); + }); + }); +}); diff --git a/apps/meteor/tests/e2e/omnichannel/omnichannel-close-inquiry.spec.ts b/apps/meteor/tests/e2e/omnichannel/omnichannel-close-inquiry.spec.ts index 85c523c2066..e77484cbd4f 100644 --- a/apps/meteor/tests/e2e/omnichannel/omnichannel-close-inquiry.spec.ts +++ b/apps/meteor/tests/e2e/omnichannel/omnichannel-close-inquiry.spec.ts @@ -49,7 +49,7 @@ test.describe('Omnichannel close inquiry', () => { await test.step('Expect to have 1 omnichannel assigned to agent 1', async () => { await agent.poHomeOmnichannel.sidenav.openQueuedOmnichannelChat(newUser.name); - await expect(agent.poHomeOmnichannel.content.takeOmnichannelChatButton).toBeVisible(); + await expect(agent.poHomeOmnichannel.content.btnTakeChat).toBeVisible(); }); await test.step('Expect to be able to close an inquiry conversation', async () => { @@ -62,8 +62,8 @@ test.describe('Omnichannel close inquiry', () => { await test.step('Expect to inquiry be closed when navigate back', async () => { await agent.poHomeOmnichannel.sidenav.openAdministrationByLabel('Omnichannel'); await agent.poHomeOmnichannel.omnisidenav.linkCurrentChats.click(); - await agent.poHomeOmnichannel.currentChats.openChat(newUser.name); - await expect(agent.poHomeOmnichannel.content.takeOmnichannelChatButton).not.toBeVisible(); + await agent.poHomeOmnichannel.currentChats.findRowByName(newUser.name).click(); + await expect(agent.poHomeOmnichannel.content.btnTakeChat).not.toBeVisible(); }); }); }); diff --git a/apps/meteor/tests/e2e/omnichannel/omnichannel-current-chats.spec.ts b/apps/meteor/tests/e2e/omnichannel/omnichannel-current-chats.spec.ts index 8138ab839b7..a3d105136ef 100644 --- a/apps/meteor/tests/e2e/omnichannel/omnichannel-current-chats.spec.ts +++ b/apps/meteor/tests/e2e/omnichannel/omnichannel-current-chats.spec.ts @@ -1,15 +1,326 @@ +import { faker } from '@faker-js/faker'; +import { Page } from '@playwright/test'; + +import { IS_EE } from '../config/constants'; import { Users } from '../fixtures/userStates'; +import { OmnichannelCurrentChats } from '../page-objects'; +import { createAgent, makeAgentAvailable } from '../utils/omnichannel/agents'; +import { addAgentToDepartment, createDepartment } from '../utils/omnichannel/departments'; +import { createConversation, updateRoom } from '../utils/omnichannel/rooms'; +import { createTag } from '../utils/omnichannel/tags'; import { test, expect } from '../utils/test'; +const visitorA = faker.person.firstName(); +const visitorB = faker.person.firstName(); +const visitorC = faker.person.firstName(); + +test.skip(!IS_EE, 'OC - Current Chats > Enterprise Only'); + test.use({ storageState: Users.admin.state }); -test.describe.parallel('Omnichannel current chats', () => { - test.beforeEach(async ({ page }) =>{ - await page.goto('/omnichannel/current') - }) +test.describe('OC - Current Chats [Auto Selection]', async () => { + let poCurrentChats: OmnichannelCurrentChats; + let departments: Awaited>[]; + let conversations: Awaited>[]; + let agents: Awaited>[]; + + // Allow manual on hold + test.beforeAll(async ({ api }) => { + const responses = await Promise.all([ + api.post('/settings/Livechat_allow_manual_on_hold', { value: true }), + api.post('/settings/Livechat_allow_manual_on_hold_upon_agent_engagement_only', { value: false }), + ]); + responses.forEach((res) => expect(res.status()).toBe(200)); + }); + + // Create departments + test.beforeAll(async ({ api }) => { + departments = await Promise.all([createDepartment(api), createDepartment(api)]); + }); + + // Create agents + test.beforeAll(async ({ api }) => { + agents = await Promise.all([createAgent(api, 'user1'), createAgent(api, 'user2')]); + + const agentsStatuses = await Promise.all(agents.map(({ data: agent }) => makeAgentAvailable(api, agent._id))); + + agentsStatuses.forEach((res) => expect(res.status()).toBe(200)); + }); + + // Add agents to departments + test.beforeAll(async ({ api }) => { + const [departmentA, departmentB] = departments.map(({ data }) => data); + + const promises = await Promise.all([ + addAgentToDepartment(api, { department: departmentA, agentId: 'user1' }), + addAgentToDepartment(api, { department: departmentB, agentId: 'user2' }), + ]); + + promises.forEach((res) => expect(res.status()).toBe(200)); + }); + + // Create tags + test.beforeAll(async ({ api }) => { + const promises = await Promise.all([createTag(api, 'tagA'), createTag(api, 'tagB')]); + + promises.forEach((res) => expect(res.status()).toBe(200)); + }); + + // Create rooms + test.beforeAll(async ({ api }) => { + const [departmentA, departmentB] = departments.map(({ data }) => data); + + conversations = await Promise.all([ + createConversation(api, { + visitorName: visitorA, + visitorToken: visitorA, + agentId: `user1`, + departmentId: departmentA._id, + }), + createConversation(api, { + visitorName: visitorB, + visitorToken: visitorB, + agentId: `user2`, + departmentId: departmentB._id, + }), + createConversation(api, { + visitorName: visitorC, + visitorToken: visitorC, + }), + ]); + + const [conversationA, conversationB] = conversations.map(({ data }) => data); + + await Promise.all([ + updateRoom(api, { + roomId: conversationA.room._id, + visitorId: conversationA.visitor._id, + tags: ['tagA'], + }), + updateRoom(api, { + roomId: conversationB.room._id, + visitorId: conversationB.visitor._id, + tags: ['tagB'], + }), + ]); + }); + + test.beforeEach(async ({ page }: { page: Page }) => { + poCurrentChats = new OmnichannelCurrentChats(page); + + await page.goto('/omnichannel'); + await poCurrentChats.sidenav.linkCurrentChats.click(); + }); + + test.afterAll(async ({ api }) => { + await Promise.all([ + // Delete conversations + ...conversations.map((conversation) => conversation.delete()), + // // Delete departments + ...departments.map((department) => department.delete()), + // Delete agents + ...agents.map((agent) => agent.delete()), + // Reset setting + api.post('/settings/Livechat_allow_manual_on_hold', { value: false }), + api.post('/settings/Livechat_allow_manual_on_hold_upon_agent_engagement_only', { value: true }), + // TODO: remove tags + ]); + }); + + // Change conversation A to on hold and close conversation B + test.beforeAll(async ({ api }) => { + const [conversationA, , conversationC] = conversations.map(({ data }) => data); + + const statesPromises = await Promise.all([ + api.post('/livechat/room.onHold', { roomId: conversationA.room._id }), + api.post('/livechat/room.close', { rid: conversationC.room._id, token: visitorC }), + ]); + + statesPromises.forEach((res) => expect(res.status()).toBe(200)); + }); + + test.skip('OC - Current chats - Accessibility violations', async ({ makeAxeBuilder }) => { + const results = await makeAxeBuilder().analyze(); + expect(results.violations).toEqual([]); + }); + + test('OC - Current chats - Filters', async () => { + const [departmentA, departmentB] = departments.map(({ data }) => data); + + await test.step('expect to filter by guest', async () => { + await expect(poCurrentChats.findRowByName(visitorA)).toBeVisible(); + await expect(poCurrentChats.findRowByName(visitorB)).toBeVisible(); + + await poCurrentChats.inputGuest.fill(visitorA); + await expect(poCurrentChats.findRowByName(visitorA)).toBeVisible(); + await expect(poCurrentChats.findRowByName(visitorB)).not.toBeVisible(); + + await poCurrentChats.inputGuest.fill(''); + await expect(poCurrentChats.findRowByName(visitorA)).toBeVisible(); + await expect(poCurrentChats.findRowByName(visitorB)).toBeVisible(); + }); + + await test.step('expect to filter by server', async () => { + await expect(poCurrentChats.findRowByName(visitorA)).toBeVisible(); + await expect(poCurrentChats.findRowByName(visitorB)).toBeVisible(); + + await poCurrentChats.selectServedBy('user1'); + await expect(poCurrentChats.findRowByName(visitorA)).toBeVisible(); + await expect(poCurrentChats.findRowByName(visitorB)).not.toBeVisible(); + + await poCurrentChats.selectServedBy('user2'); + await expect(poCurrentChats.findRowByName(visitorB)).toBeVisible(); + await expect(poCurrentChats.findRowByName(visitorA)).not.toBeVisible(); + + await poCurrentChats.selectServedBy('all'); + await expect(poCurrentChats.findRowByName(visitorA)).toBeVisible(); + await expect(poCurrentChats.findRowByName(visitorB)).toBeVisible(); + }); + + await test.step('expect to filter by status', async () => { + await poCurrentChats.selectStatus('closed'); + await expect(poCurrentChats.findRowByName(visitorA)).not.toBeVisible(); + await expect(poCurrentChats.findRowByName(visitorB)).not.toBeVisible(); + await expect(poCurrentChats.findRowByName(visitorC)).toBeVisible(); + + await poCurrentChats.selectStatus('opened'); + await expect(poCurrentChats.findRowByName(visitorA)).not.toBeVisible(); + await expect(poCurrentChats.findRowByName(visitorB)).toBeVisible(); + await expect(poCurrentChats.findRowByName(visitorC)).not.toBeVisible(); + + await poCurrentChats.selectStatus('onhold'); + await expect(poCurrentChats.findRowByName(visitorA)).toBeVisible(); + await expect(poCurrentChats.findRowByName(visitorB)).not.toBeVisible(); + await expect(poCurrentChats.findRowByName(visitorC)).not.toBeVisible(); + + await poCurrentChats.selectStatus('all'); + await expect(poCurrentChats.findRowByName(visitorA)).toBeVisible(); + await expect(poCurrentChats.findRowByName(visitorB)).toBeVisible(); + await expect(poCurrentChats.findRowByName(visitorC)).toBeVisible(); + }); + + await test.step('expect to filter by department', async () => { + await poCurrentChats.selectDepartment(departmentA.name); + await expect(poCurrentChats.findRowByName(visitorA)).toBeVisible(); + await expect(poCurrentChats.findRowByName(visitorB)).not.toBeVisible(); + await expect(poCurrentChats.findRowByName(visitorC)).not.toBeVisible(); + + await poCurrentChats.selectDepartment(departmentB.name); + await expect(poCurrentChats.findRowByName(visitorA)).not.toBeVisible(); + await expect(poCurrentChats.findRowByName(visitorB)).toBeVisible(); + await expect(poCurrentChats.findRowByName(visitorC)).not.toBeVisible(); + + await poCurrentChats.selectDepartment('All'); + await expect(poCurrentChats.findRowByName(visitorA)).toBeVisible(); + await expect(poCurrentChats.findRowByName(visitorB)).toBeVisible(); + await expect(poCurrentChats.findRowByName(visitorC)).toBeVisible(); + }); + + await test.step('expect to filter by tags', async () => { + await poCurrentChats.addTag('tagA'); + await expect(poCurrentChats.findRowByName(visitorA)).toBeVisible(); + await expect(poCurrentChats.findRowByName(visitorB)).not.toBeVisible(); + + await poCurrentChats.addTag('tagB'); + await expect(poCurrentChats.findRowByName(visitorA)).toBeVisible(); + await expect(poCurrentChats.findRowByName(visitorB)).toBeVisible(); + + await poCurrentChats.removeTag('tagA'); + await expect(poCurrentChats.findRowByName(visitorB)).toBeVisible(); + await expect(poCurrentChats.findRowByName(visitorA)).not.toBeVisible(); + + await poCurrentChats.removeTag('tagB'); + await expect(poCurrentChats.findRowByName(visitorB)).toBeVisible(); + await expect(poCurrentChats.findRowByName(visitorA)).toBeVisible(); + }); + + // TODO: Unit test await test.step('expect to filter by period', async () => {}); + + // TODO: Unit test await test.step('expect to filter by custom fields', async () => {}); + + // TODO: Unit test await test.step('expect to filter clear all', async () => {}); + }); + + test('OC - Current chats - Basic navigation', async ({ page }) => { + await test.step('expect to be return using return button', async () => { + const { room: roomA } = conversations[0].data; + await poCurrentChats.findRowByName(visitorA).click(); + await expect(page).toHaveURL(`/omnichannel/current/${roomA._id}`); + await poCurrentChats.content.btnReturn.click(); + await expect(page).toHaveURL(`/omnichannel/current`); + }); + }); + + test('OC - Current chats - Access in progress conversation from another agent', async ({ page }) => { + await test.step('expect to be able to join', async () => { + const { room: roomB, visitor: visitorB } = conversations[1].data; + await poCurrentChats.findRowByName(visitorB.name).click(); + await expect(page).toHaveURL(`/omnichannel/current/${roomB._id}`); + await expect(poCurrentChats.content.btnJoinRoom).toBeVisible(); + await poCurrentChats.content.btnJoinRoom.click(); + await expect(poCurrentChats.content.btnJoinRoom).not.toBeVisible(); + }); + }); + + test('OC - Current chats - Remove conversations', async () => { + await test.step('expect to be able to remove conversation from table', async () => { + await poCurrentChats.btnRemoveByName(visitorC).click(); + await expect(poCurrentChats.modalConfirmRemove).toBeVisible(); + await poCurrentChats.btnConfirmRemove.click(); + await expect(poCurrentChats.modalConfirmRemove).not.toBeVisible(); + await expect(poCurrentChats.findRowByName(visitorC)).not.toBeVisible(); + }); + + // TODO: await test.step('expect to be able to close all closes conversations', async () => {}); + }); +}); + +test.describe('OC - Current Chats [Manual Selection]', () => { + let queuedConversation: Awaited>; + let poCurrentChats: OmnichannelCurrentChats; + let agent: Awaited>; + + test.beforeAll(async ({ api }) => { + const res = await api.post('/settings/Livechat_Routing_Method', { value: 'Manual_Selection' }); + expect(res.status()).toBe(200); + }); + + test.beforeAll(async ({ api }) => { + agent = await createAgent(api, 'rocketchat.internal.admin.test'); + + const agentStatus = await makeAgentAvailable(api, agent.data._id); + + expect(agentStatus.status()).toBe(200); + }); + + test.beforeEach(async ({ page }: { page: Page }) => { + poCurrentChats = new OmnichannelCurrentChats(page); + + await page.goto('/omnichannel'); + await poCurrentChats.sidenav.linkCurrentChats.click(); + }); + + test('OC - Current chats - Access queued conversation', async ({ page, api }) => { + queuedConversation = await createConversation(api, { visitorToken: 'visitorQueued' }); + + await test.step('expect to be able to take it', async () => { + const { room, visitor } = queuedConversation.data; + await poCurrentChats.inputGuest.fill(visitor.name); + await poCurrentChats.findRowByName(visitor.name).click(); + await expect(page).toHaveURL(`/omnichannel/current/${room._id}`); + await expect(poCurrentChats.content.btnTakeChat).toBeVisible(); + await poCurrentChats.content.btnTakeChat.click(); + await expect(poCurrentChats.content.btnTakeChat).not.toBeVisible(); + }); + }); + + test.afterAll(async ({ api }) => { + const res = await api.post('/settings/Livechat_Routing_Method', { value: 'Auto_Selection' }); + expect(res.status()).toBe(200); + }); - test.skip('should not have any accessibility violations', async ({ makeAxeBuilder }) => { - const results = await makeAxeBuilder().analyze(); - expect(results.violations).toEqual([]); - }) -}) + test.afterAll(async () => { + await queuedConversation.delete(); + await agent.delete(); + }); +}); diff --git a/apps/meteor/tests/e2e/omnichannel/omnichannel-custom-fields.spec.ts b/apps/meteor/tests/e2e/omnichannel/omnichannel-custom-fields.spec.ts index 472e9bbcc80..6749f690546 100644 --- a/apps/meteor/tests/e2e/omnichannel/omnichannel-custom-fields.spec.ts +++ b/apps/meteor/tests/e2e/omnichannel/omnichannel-custom-fields.spec.ts @@ -4,7 +4,7 @@ import { test, expect } from '../utils/test'; test.use({ storageState: Users.admin.state }); -test.describe.parallel('omnichannel-customFields', () => { +test.describe('omnichannel-customFields', () => { let poOmnichannelCustomFields: OmnichannelCustomFields; const newField = 'any_field'; test.beforeEach(async ({ page }) => { diff --git a/apps/meteor/tests/e2e/omnichannel/omnichannel-departaments-ce.spec.ts b/apps/meteor/tests/e2e/omnichannel/omnichannel-departaments-ce.spec.ts index dc30077b671..5f5db5c6916 100644 --- a/apps/meteor/tests/e2e/omnichannel/omnichannel-departaments-ce.spec.ts +++ b/apps/meteor/tests/e2e/omnichannel/omnichannel-departaments-ce.spec.ts @@ -8,7 +8,7 @@ import { test, expect } from '../utils/test'; test.use({ storageState: Users.admin.state }); -test.describe.serial('omnichannel-departments', () => { +test.describe.serial('OC - Manage Departments (CE)', () => { test.skip(IS_EE, 'Community Edition Only'); let poOmnichannelDepartments: OmnichannelDepartments; @@ -25,7 +25,7 @@ test.describe.serial('omnichannel-departments', () => { await poOmnichannelDepartments.sidenav.linkDepartments.click(); }); - test('CE departments', async () => { + test('OC - Manage Departments (CE) - Create department', async () => { await test.step('expect create new department', async () => { await poOmnichannelDepartments.headingButtonNew('Create department').click(); await poOmnichannelDepartments.btnEnabled.click(); @@ -37,6 +37,7 @@ test.describe.serial('omnichannel-departments', () => { await poOmnichannelDepartments.inputSearch.fill(departmentName); await expect(poOmnichannelDepartments.firstRowInTable).toBeVisible(); }); + await test.step('expect to not be possible adding a second department ', async () => { await poOmnichannelDepartments.headingButtonNew('Create department').click(); diff --git a/apps/meteor/tests/e2e/omnichannel/omnichannel-departaments.spec.ts b/apps/meteor/tests/e2e/omnichannel/omnichannel-departaments.spec.ts index a6d3207a95e..0e368565cdc 100644 --- a/apps/meteor/tests/e2e/omnichannel/omnichannel-departaments.spec.ts +++ b/apps/meteor/tests/e2e/omnichannel/omnichannel-departaments.spec.ts @@ -4,6 +4,7 @@ import type { Page } from '@playwright/test'; import { IS_EE } from '../config/constants'; import { Users } from '../fixtures/userStates'; import { OmnichannelDepartments } from '../page-objects'; +import { createDepartment, deleteDepartment } from '../utils/omnichannel/departments'; import { test, expect } from '../utils/test'; const ERROR = { @@ -14,14 +15,12 @@ const ERROR = { test.use({ storageState: Users.admin.state }); -test.describe.serial('omnichannel-departments', () => { +test.describe('OC - Manage Departments', () => { test.skip(!IS_EE, 'Enterprise Edition Only'); - let poOmnichannelDepartments: OmnichannelDepartments; - let departmentName: string; + let poOmnichannelDepartments: OmnichannelDepartments; test.beforeAll(async ({ api }) => { - departmentName = faker.string.uuid(); // turn on department removal const statusCode = (await api.post('/settings/Omnichannel_enable_department_removal', { value: true })).status(); await expect(statusCode).toBe(200); @@ -40,17 +39,13 @@ test.describe.serial('omnichannel-departments', () => { await poOmnichannelDepartments.sidenav.linkDepartments.click(); }); - test('Manage departments', async ({ page }) => { - await test.step('expect page to be empty', async () => { - await page.goto('/omnichannel/departments/edit/this-department-dont-exist'); - await expect(poOmnichannelDepartments.btnEnabled).not.toBeVisible(); - await page.goBack(); - }); + test('OC - Manage Departments - Create department', async () => { + const departmentName = faker.string.uuid(); + + await poOmnichannelDepartments.headingButtonNew('Create department').click(); await test.step('expect name and email to be required', async () => { - await poOmnichannelDepartments.headingButtonNew('Create department').click(); await expect(poOmnichannelDepartments.invalidInputEmail).not.toBeVisible(); - await poOmnichannelDepartments.inputName.fill('any_text'); await poOmnichannelDepartments.inputName.fill(''); await expect(poOmnichannelDepartments.invalidInputName).toBeVisible(); @@ -82,90 +77,116 @@ test.describe.serial('omnichannel-departments', () => { await expect(poOmnichannelDepartments.firstRowInTable).toBeVisible(); }); - await test.step('expect update department name', async () => { + await test.step('expect to delete department', async () => { await poOmnichannelDepartments.search(departmentName); + await poOmnichannelDepartments.selectedDepartmentMenu(departmentName).click(); + await poOmnichannelDepartments.menuDeleteOption.click(); + + await test.step('expect confirm delete department', async () => { + await expect(poOmnichannelDepartments.modalConfirmDelete).toBeVisible(); + + await test.step('expect delete to be disabled when name is incorrect', async () => { + await expect(poOmnichannelDepartments.btnModalConfirmDelete).toBeDisabled(); + await poOmnichannelDepartments.inputModalConfirmDelete.fill('someramdomname'); + await expect(poOmnichannelDepartments.btnModalConfirmDelete).toBeDisabled(); + }); + + await test.step('expect to successfuly delete if department name is correct', async () => { + await expect(poOmnichannelDepartments.btnModalConfirmDelete).toBeDisabled(); + await poOmnichannelDepartments.inputModalConfirmDelete.fill(departmentName); + await expect(poOmnichannelDepartments.btnModalConfirmDelete).toBeEnabled(); + await poOmnichannelDepartments.btnModalConfirmDelete.click(); + }); + }); + + await test.step('expect department to have been deleted', async () => { + await poOmnichannelDepartments.search(departmentName); + await expect(poOmnichannelDepartments.firstRowInTable).toHaveCount(0); + }); + }); + }); + + test('OC - Manage Departments - Edit department', async ({ api }) => { + const department = await test.step('expect create new department', async () => { + const { data: department } = await createDepartment(api); + + await poOmnichannelDepartments.search(department.name); + await expect(poOmnichannelDepartments.firstRowInTable).toBeVisible(); + + return department; + }); + + await test.step('expect update department name', async () => { + await poOmnichannelDepartments.search(department.name); await poOmnichannelDepartments.firstRowInTableMenu.click(); await poOmnichannelDepartments.menuEditOption.click(); - await poOmnichannelDepartments.inputName.fill(`edited-${departmentName}`); + await poOmnichannelDepartments.inputName.fill(`edited-${department.name}`); await poOmnichannelDepartments.btnSave.click(); await poOmnichannelDepartments.btnCloseToastSuccess.click(); - await poOmnichannelDepartments.search(`edited-${departmentName}`); + await poOmnichannelDepartments.search(`edited-${department.name}`); await expect(poOmnichannelDepartments.firstRowInTable).toBeVisible(); }); - await test.step('expect archive department', async () => { + await test.step('expect to delete department', async () => { + const deleteRes = await deleteDepartment(api, { id: department._id }); + await expect(deleteRes.status()).toBe(200); + }); + }); + + test('OC - Manage Departments - Archive department', async ({ api }) => { + const department = await test.step('expect create new department', async () => { + const { data: department } = await createDepartment(api); + + await poOmnichannelDepartments.search(department.name); await expect(poOmnichannelDepartments.firstRowInTable).toBeVisible(); - await poOmnichannelDepartments.search(`edited-${departmentName}`); + return department; + }); - await poOmnichannelDepartments.firstRowInTableMenu.click(); + await test.step('expect archive department', async () => { + await poOmnichannelDepartments.search(department.name); + await expect(poOmnichannelDepartments.firstRowInTable).toBeVisible(); + await poOmnichannelDepartments.firstRowInTableMenu.click(); await poOmnichannelDepartments.menuArchiveOption.click(); - await expect(poOmnichannelDepartments.toastSuccess).toBeVisible(); - }); - await test.step('expect archived department to not be editable', async () => { await poOmnichannelDepartments.archivedDepartmentsTab.click(); + await poOmnichannelDepartments.search(department.name); + await expect(poOmnichannelDepartments.firstRowInTable).toBeVisible(); + }); + await test.step('expect archived department to not be editable', async () => { await poOmnichannelDepartments.firstRowInTableMenu.click(); - - await expect(poOmnichannelDepartments.btnEnabled).not.toBeVisible(); - - await poOmnichannelDepartments.allDepartmentsTab.click(); + await expect(poOmnichannelDepartments.menuEditOption).not.toBeVisible(); }); await test.step('expect unarchive department', async () => { - await poOmnichannelDepartments.archivedDepartmentsTab.click(); - - await poOmnichannelDepartments.search(`edited-${departmentName}`); - - await poOmnichannelDepartments.firstRowInTableMenu.click(); - await poOmnichannelDepartments.menuUnarchiveOption.click(); - await expect(poOmnichannelDepartments.firstRowInTable).toHaveCount(0); }); - await test.step('expect delete department', async () => { - await poOmnichannelDepartments.allDepartmentsTab.click(); - - await poOmnichannelDepartments.search(`edited-${departmentName}`); - - await poOmnichannelDepartments.selectedDepartmentMenu(`edited-${departmentName}`).click(); - - await poOmnichannelDepartments.menuDeleteOption.click(); - - await poOmnichannelDepartments.inputModalConfirmDelete.fill(`edited-${departmentName}`); - - await poOmnichannelDepartments.btnModalConfirmDelete.click(); - - await poOmnichannelDepartments.search(`edited-${departmentName}`); - - await expect(poOmnichannelDepartments.firstRowInTable).toHaveCount(0); + await test.step('expect to delete department', async () => { + const deleteRes = await deleteDepartment(api, { id: department._id }); + await expect(deleteRes.status()).toBe(200); }); }); - test('Tags', async () => { - const tagsDepartmentName = faker.string.uuid(); - - await test.step('expect create new department', async () => { - await poOmnichannelDepartments.headingButtonNew('Create department').click(); - await poOmnichannelDepartments.btnEnabled.click(); - await poOmnichannelDepartments.inputName.fill(tagsDepartmentName); - await poOmnichannelDepartments.inputEmail.fill(faker.internet.email()); - await poOmnichannelDepartments.btnSave.click(); - await poOmnichannelDepartments.btnCloseToastSuccess.click(); + test('OC - Manage Departments - Request tag(s) before closing conversation', async ({ api }) => { + const department = await test.step('expect create new department', async () => { + const { data: department } = await createDepartment(api); - await poOmnichannelDepartments.search(tagsDepartmentName); + await poOmnichannelDepartments.search(department.name); await expect(poOmnichannelDepartments.firstRowInTable).toBeVisible(); + + return department; }); await test.step('expect save form button be disabled', async () => { - await poOmnichannelDepartments.search(tagsDepartmentName); + await poOmnichannelDepartments.search(department.name); await poOmnichannelDepartments.firstRowInTableMenu.click(); await poOmnichannelDepartments.menuEditOption.click(); await expect(poOmnichannelDepartments.btnSave).toBeDisabled(); @@ -173,13 +194,14 @@ test.describe.serial('omnichannel-departments', () => { }); await test.step('Disabled tags state', async () => { - await poOmnichannelDepartments.search(tagsDepartmentName); + await poOmnichannelDepartments.search(department.name); await poOmnichannelDepartments.firstRowInTableMenu.click(); await poOmnichannelDepartments.menuEditOption.click(); await test.step('expect to have department tags toggle button', async () => { await expect(poOmnichannelDepartments.toggleRequestTags).toBeVisible(); }); + await test.step('expect have no add tag to department', async () => { await expect(poOmnichannelDepartments.inputTags).not.toBeVisible(); await expect(poOmnichannelDepartments.btnTagsAdd).not.toBeVisible(); @@ -190,7 +212,7 @@ test.describe.serial('omnichannel-departments', () => { await test.step('Enabled tags state', async () => { const tagName = faker.string.sample(5); - await poOmnichannelDepartments.search(tagsDepartmentName); + await poOmnichannelDepartments.search(department.name); await poOmnichannelDepartments.firstRowInTableMenu.click(); await poOmnichannelDepartments.menuEditOption.click(); @@ -232,4 +254,42 @@ test.describe.serial('omnichannel-departments', () => { }); }); }); + + test('OC - Manage Departments - Toggle department removal', async ({ api }) => { + const department = await test.step('expect create new department', async () => { + const { data: department } = await createDepartment(api); + + await poOmnichannelDepartments.search(department.name); + await expect(poOmnichannelDepartments.firstRowInTable).toBeVisible(); + + return department; + }); + + await test.step('expect to be able to delete department', async () => { + await poOmnichannelDepartments.search(department.name); + await poOmnichannelDepartments.selectedDepartmentMenu(department.name).click(); + await expect(poOmnichannelDepartments.menuDeleteOption).toBeEnabled(); + }); + + await test.step('expect to disable department removal setting', async () => { + const statusCode = (await api.post('/settings/Omnichannel_enable_department_removal', { value: false })).status(); + await expect(statusCode).toBe(200); + }); + + await test.step('expect not to be able to delete department', async () => { + await poOmnichannelDepartments.search(department.name); + await poOmnichannelDepartments.selectedDepartmentMenu(department.name).click(); + await expect(poOmnichannelDepartments.menuDeleteOption).toBeDisabled(); + }); + + await test.step('expect to enable department removal setting', async () => { + const statusCode = (await api.post('/settings/Omnichannel_enable_department_removal', { value: true })).status(); + await expect(statusCode).toBe(200); + }); + + await test.step('expect to delete department', async () => { + const deleteRes = await deleteDepartment(api, { id: department._id }); + await expect(deleteRes.status()).toBe(200); + }); + }); }); diff --git a/apps/meteor/tests/e2e/omnichannel/omnichannel-livechat.spec.ts b/apps/meteor/tests/e2e/omnichannel/omnichannel-livechat.spec.ts index 24049333766..fe1e6453677 100644 --- a/apps/meteor/tests/e2e/omnichannel/omnichannel-livechat.spec.ts +++ b/apps/meteor/tests/e2e/omnichannel/omnichannel-livechat.spec.ts @@ -87,8 +87,8 @@ test.describe.serial('OC - Livechat', () => { await test.step('expect livechat conversation to be closed by agent', async () => { await poHomeOmnichannel.content.btnCloseChat.click(); - await poHomeOmnichannel.content.omnichannelCloseChatModal.inputComment.fill('this_is_a_test_comment'); - await poHomeOmnichannel.content.omnichannelCloseChatModal.btnConfirm.click(); + await poHomeOmnichannel.content.closeChatModal.inputComment.fill('this_is_a_test_comment'); + await poHomeOmnichannel.content.closeChatModal.btnConfirm.click(); await expect(poHomeOmnichannel.toastSuccess).toBeVisible(); }); }); diff --git a/apps/meteor/tests/e2e/omnichannel/omnichannel-manager-role.spec.ts b/apps/meteor/tests/e2e/omnichannel/omnichannel-manager-role.spec.ts new file mode 100644 index 00000000000..38dcee88cf5 --- /dev/null +++ b/apps/meteor/tests/e2e/omnichannel/omnichannel-manager-role.spec.ts @@ -0,0 +1,253 @@ +import { faker } from '@faker-js/faker'; +import { Page } from '@playwright/test'; + +import { IS_EE } from '../config/constants'; +import { Users } from '../fixtures/userStates'; +import { HomeOmnichannel } from '../page-objects'; +import { createAgent, makeAgentAvailable } from '../utils/omnichannel/agents'; +import { createDepartment } from '../utils/omnichannel/departments'; +import { createManager } from '../utils/omnichannel/managers'; +import { createConversation } from '../utils/omnichannel/rooms'; +import { test, expect } from '../utils/test'; + +const MANAGER = 'user3'; +const ROOM_A = faker.person.fullName(); +const ROOM_B = faker.person.fullName(); +const ROOM_C = faker.person.fullName(); + +test.use({ storageState: Users.user3.state }); + +test.describe('OC - Manager Role', () => { + test.skip(!IS_EE, 'Enterprise Edition Only'); + + let departments: Awaited>[]; + let conversations: Awaited>[]; + let agents: Awaited>[]; + let poOmnichannel: HomeOmnichannel; + + let manager: Awaited>; + + // Allow manual on hold + test.beforeAll(async ({ api }) => { + const responses = await Promise.all([ + api.post('/settings/Livechat_allow_manual_on_hold', { value: true }), + api.post('/settings/Livechat_allow_manual_on_hold_upon_agent_engagement_only', { value: false }), + ]); + responses.forEach((res) => expect(res.status()).toBe(200)); + }); + + // Create agents + test.beforeAll(async ({ api }) => { + agents = await Promise.all([createAgent(api, 'user1'), createAgent(api, 'user2'), createAgent(api, MANAGER)]); + + const agentsStatuses = await Promise.all(agents.slice(0, 1).map(({ data: agent }) => makeAgentAvailable(api, agent._id))); + + agentsStatuses.forEach((res) => expect(res.status()).toBe(200)); + }); + + // Create departments + test.beforeAll(async ({ api }) => { + departments = await Promise.all([createDepartment(api), createDepartment(api)]); + }); + + // Create manager + test.beforeAll(async ({ api }) => { + manager = await createManager(api, MANAGER); + }); + + // Create conversations + test.beforeAll(async ({ api }) => { + const [departmentA, departmentB] = departments.map(({ data }) => data); + + conversations = await Promise.all([ + createConversation(api, { + visitorName: ROOM_A, + visitorToken: 'roomA', + agentId: `user1`, + departmentId: departmentA._id, + }), + createConversation(api, { + visitorName: ROOM_B, + visitorToken: 'roomB', + agentId: `user2`, + departmentId: departmentB._id, + }), + createConversation(api, { + visitorName: ROOM_C, + visitorToken: 'roomC', + agentId: `user2`, + }), + ]); + }); + + // Delete all created data + test.afterAll(async ({ api }) => { + await Promise.all([ + ...agents.map((agent) => agent.delete()), + ...departments.map((department) => department.delete()), + ...conversations.map((conversation) => conversation.delete()), + manager.delete(), + // Reset setting + api.post('/settings/Livechat_allow_manual_on_hold', { value: false }), + api.post('/settings/Livechat_allow_manual_on_hold_upon_agent_engagement_only', { value: true }), + ]); + }); + + test.beforeEach(async ({ page }: { page: Page }) => { + poOmnichannel = new HomeOmnichannel(page); + + await page.goto('/omnichannel'); + }); + + test('OC - Manager Role - Basic permissions', async () => { + await test.step('expect agent to not have access to omnichannel administration', async () => { + await expect(poOmnichannel.omnisidenav.linkCurrentChats).toBeVisible(); + await expect(poOmnichannel.omnisidenav.linkAnalytics).toBeVisible(); + await expect(poOmnichannel.omnisidenav.linkRealTimeMonitoring).toBeVisible(); + await expect(poOmnichannel.omnisidenav.linkAgents).toBeVisible(); + await expect(poOmnichannel.omnisidenav.linkDepartments).toBeVisible(); + await expect(poOmnichannel.omnisidenav.linkBusinessHours).toBeVisible(); + await expect(poOmnichannel.omnisidenav.linkReports).toBeVisible(); + await expect(poOmnichannel.omnisidenav.linkCannedResponses).toBeVisible(); + }); + }); + + test('OC - Manager Role - Current Chats', async ({ page }) => { + const [conversationA] = conversations; + const { room: roomA } = conversationA.data; + + await test.step('expect to be able to view all chats', async () => { + await expect(poOmnichannel.currentChats.findRowByName(ROOM_A)).toBeVisible(); + await expect(poOmnichannel.currentChats.findRowByName(ROOM_B)).toBeVisible(); + await expect(poOmnichannel.currentChats.findRowByName(ROOM_C)).toBeVisible(); + }); + + await test.step('expect to be able to join chats', async () => { + await poOmnichannel.currentChats.findRowByName(ROOM_A).click(); + await expect(page).toHaveURL(`/omnichannel/current/${roomA._id}`); + await expect(poOmnichannel.content.btnJoinRoom).toBeVisible(); + await expect(poOmnichannel.content.inputMessage).not.toBeVisible(); + + await poOmnichannel.content.btnJoinRoom.click(); + await expect(poOmnichannel.content.lastSystemMessageBody).toHaveText('joined the channel'); + await expect(poOmnichannel.content.btnJoinRoom).not.toBeVisible(); + await expect(poOmnichannel.content.inputMessage).toBeVisible(); + }); + + await test.step('expect to be able to put a conversation from another agent on hold', async () => { + await poOmnichannel.content.btnOnHold.click({ clickCount: 2 }); + await expect(poOmnichannel.content.modalOnHold).toBeVisible(); + await poOmnichannel.content.btnOnHoldConfirm.click(); + await expect(poOmnichannel.content.lastSystemMessageBody).toHaveText( + `Chat On Hold: The chat was manually placed On Hold by ${MANAGER}`, + ); + await expect(poOmnichannel.content.inputMessage).not.toBeVisible(); + await expect(poOmnichannel.content.btnResume).toBeVisible(); + }); + + await test.step('expect to be able resume a conversation from another agent on hold', async () => { + await poOmnichannel.content.btnResume.click(); + await expect(poOmnichannel.content.btnResume).not.toBeVisible(); + await expect(poOmnichannel.content.inputMessage).toBeVisible(); + await expect(poOmnichannel.content.btnOnHold).toBeVisible(); + }); + + await test.step('expect to be able to close a conversation from another agent', async () => { + await poOmnichannel.content.btnCloseChat.click(); + await poOmnichannel.content.inputModalClosingComment.type('any_comment'); + await poOmnichannel.content.btnModalConfirm.click(); + await expect(poOmnichannel.toastSuccess).toBeVisible(); + await page.waitForURL('/omnichannel/current'); + }); + + await test.step('expect to be able to remove closed rooms', async () => { + await poOmnichannel.currentChats.btnRemoveByName(ROOM_A).click(); + await expect(poOmnichannel.currentChats.modalConfirmRemove).toBeVisible(); + await poOmnichannel.currentChats.btnConfirmRemove.click(); + await expect(poOmnichannel.currentChats.modalConfirmRemove).not.toBeVisible(); + await expect(poOmnichannel.currentChats.findRowByName(ROOM_A)).not.toBeVisible(); + }); + }); + + test('OC - Manager Role - Add/remove agents', async ({ page }) => { + await poOmnichannel.agents.sidenav.linkAgents.click(); + + await test.step('expect add "user1" as agent', async () => { + await poOmnichannel.agents.inputUsername.type('user1'); + await page.locator('role=option[name="user1"]').click(); + await poOmnichannel.agents.btnAdd.click(); + + await poOmnichannel.agents.inputSearch.fill('user1'); + await expect(poOmnichannel.agents.findRowByName('user1')).toBeVisible(); + }); + + await test.step('expect remove "user1" as agent', async () => { + await poOmnichannel.agents.inputSearch.fill('user1'); + await poOmnichannel.agents.btnDeletefirstRowInTable.click(); + await poOmnichannel.agents.btnModalRemove.click(); + + await poOmnichannel.agents.inputSearch.fill(''); + await poOmnichannel.agents.inputSearch.type('user1'); + await expect(poOmnichannel.agents.findRowByName('user1')).toBeHidden(); + }); + }); + + test('OC - Manager Role - Add/remove managers', async ({ page }) => { + await poOmnichannel.omnisidenav.linkManagers.click(); + + await test.step('expect add "user1" as manager', async () => { + await poOmnichannel.managers.inputUsername.type('user1'); + await page.locator('role=option[name="user1"]').click(); + await poOmnichannel.managers.btnAdd.click(); + + await expect(poOmnichannel.managers.firstRowInTable('user1')).toBeVisible(); + }); + + await test.step('expect search for manager', async () => { + await poOmnichannel.managers.search('user1'); + await expect(poOmnichannel.managers.firstRowInTable('user1')).toBeVisible(); + + await poOmnichannel.managers.search('NonExistingUser'); + await expect(poOmnichannel.managers.firstRowInTable('user1')).toBeHidden(); + + await poOmnichannel.managers.clearSearch(); + }); + + await test.step('expect remove "user1" as manager', async () => { + await poOmnichannel.managers.search('user1'); + await poOmnichannel.managers.btnDeleteSelectedAgent('user1').click(); + await poOmnichannel.managers.btnModalRemove.click(); + + await expect(poOmnichannel.managers.firstRowInTable('user1')).toBeHidden(); + }); + }); + + test('OC - Manager Role - Add/remove monitors', async () => { + await poOmnichannel.omnisidenav.linkMonitors.click(); + + await test.step('expect to add agent as monitor', async () => { + await expect(poOmnichannel.monitors.findRowByName('user1')).not.toBeVisible(); + await poOmnichannel.monitors.selectMonitor('user1'); + await poOmnichannel.monitors.btnAddMonitor.click(); + await expect(poOmnichannel.monitors.findRowByName('user1')).toBeVisible(); + }); + + await test.step('expect to remove agent from monitor', async () => { + await poOmnichannel.monitors.btnRemoveByName('user1').click(); + await expect(poOmnichannel.monitors.modalConfirmRemove).toBeVisible(); + await poOmnichannel.monitors.btnConfirmRemove.click(); + await expect(poOmnichannel.monitors.findRowByName('user1')).not.toBeVisible(); + }); + }); + + test('OC - Manager Role - Permission revoked', async ({ page }) => { + await poOmnichannel.omnisidenav.linkCurrentChats.click(); + + await test.step('expect not to be able to see current chats once role is removed', async () => { + const res = await manager.delete(); + await expect(res.status()).toBe(200); + await page.reload(); + await expect(page.locator('p >> text="You are not authorized to view this page."')).toBeVisible(); + }); + }); +}); diff --git a/apps/meteor/tests/e2e/omnichannel/omnichannel-manual-selection.spec.ts b/apps/meteor/tests/e2e/omnichannel/omnichannel-manual-selection.spec.ts new file mode 100644 index 00000000000..12443722160 --- /dev/null +++ b/apps/meteor/tests/e2e/omnichannel/omnichannel-manual-selection.spec.ts @@ -0,0 +1,99 @@ +import { Page } from '@playwright/test'; + +import { createAuxContext } from '../fixtures/createAuxContext'; +import { Users } from '../fixtures/userStates'; +import { HomeOmnichannel } from '../page-objects'; +import { createAgent, makeAgentAvailable } from '../utils/omnichannel/agents'; +import { createConversation } from '../utils/omnichannel/rooms'; +import { test, expect } from '../utils/test'; + +test.use({ storageState: Users.user3.state }); + +test.describe('OC - Manual Selection', () => { + let poOmnichannel: HomeOmnichannel; + let agents: Awaited>[]; + let agentB: { page: Page; poHomeOmnichannel: HomeOmnichannel }; + + // Change routing method to manual selection + test.beforeAll(async ({ api }) => { + const res = await api.post('/settings/Livechat_Routing_Method', { value: 'Manual_Selection' }); + expect(res.status()).toBe(200); + }); + + // Create agent and make it available + test.beforeAll(async ({ api }) => { + agents = await Promise.all([await createAgent(api, 'user3'), await createAgent(api, 'user1')]); + (await Promise.all(agents.map(({ data: agent }) => makeAgentAvailable(api, agent._id)))).forEach((res) => { + expect(res.status()).toBe(200); + }); + }); + + // Create page object and redirect to home + test.beforeEach(async ({ page }: { page: Page }) => { + poOmnichannel = new HomeOmnichannel(page); + await page.goto('/home'); + }); + + // Create agent b session + test.beforeEach(async ({ browser }) => { + agentB = await createAuxContext(browser, Users.user1).then(({ page }) => ({ page, poHomeOmnichannel: new HomeOmnichannel(page) })); + }); + + // Delete all data + test.afterAll(async ({ api }) => { + await Promise.all([ + agentB.page.close(), + ...agents.map(agent => agent.delete()), + api.post('/settings/Livechat_Routing_Method', { value: 'Auto_Selection' }), + ]); + }); + + test('OC - Manual Selection - Queue', async ({ page, api }) => { + const { data: { room } } = await createConversation(api); + + await test.step('expect not be able to see queue when livechat is disabled', async () => { + await poOmnichannel.sidenav.switchOmnichannelStatus('offline'); + await agentB.poHomeOmnichannel.sidenav.switchOmnichannelStatus('offline'); + await expect(poOmnichannel.sidenav.getSidebarItemByName(room.fname)).not.toBeVisible(); + await expect(agentB.poHomeOmnichannel.sidenav.getSidebarItemByName(room.fname)).not.toBeVisible(); + await poOmnichannel.sidenav.switchOmnichannelStatus('online'); + await agentB.poHomeOmnichannel.sidenav.switchOmnichannelStatus('online'); + await expect(poOmnichannel.sidenav.getSidebarItemByName(room.fname)).toBeVisible(); + await expect(agentB.poHomeOmnichannel.sidenav.getSidebarItemByName(room.fname)).toBeVisible(); + }); + + await test.step('expect to be able join chat in read mode', async () => { + await poOmnichannel.sidenav.getSidebarItemByName(room.fname).click(); + await expect(poOmnichannel.content.inputMessage).not.toBeVisible(); + await expect(poOmnichannel.content.btnTakeChat).toBeVisible(); + }); + + await test.step('expect to be able take chat', async () => { + await poOmnichannel.content.btnTakeChat.click(); + await expect(poOmnichannel.content.lastSystemMessageBody).toHaveText('joined the channel'); + await expect(poOmnichannel.content.inputMessage).toBeVisible(); + await expect(poOmnichannel.content.btnTakeChat).not.toBeVisible(); + await expect(poOmnichannel.sidenav.getSidebarItemByName(room.fname)).toBeVisible(); + }); + + await test.step('expect chat to leave the queue', async () => { + await page.waitForTimeout(250); + await expect(agentB.poHomeOmnichannel.sidenav.getSidebarItemByName(room.fname)).not.toBeVisible(); + }); + + await test.step('expect to be able return to queue', async () => { + await poOmnichannel.content.btnReturnToQueue.click(); + await poOmnichannel.content.btnReturnToQueueConfirm.click(); + await expect(page).toHaveURL('/home'); + }); + + await test.step('expect chat to be back in queue', async () => { + await expect(poOmnichannel.sidenav.getSidebarItemByName(room.fname)).toBeVisible(); + await expect(agentB.poHomeOmnichannel.sidenav.getSidebarItemByName(room.fname)).toBeVisible(); + + await poOmnichannel.sidenav.getSidebarItemByName(room.fname).click(); + await expect(poOmnichannel.content.inputMessage).not.toBeVisible(); + await expect(poOmnichannel.content.btnTakeChat).toBeVisible(); + }); + }); +}); diff --git a/apps/meteor/tests/e2e/omnichannel/omnichannel-monitor-role.spec.ts b/apps/meteor/tests/e2e/omnichannel/omnichannel-monitor-role.spec.ts new file mode 100644 index 00000000000..3d4469a76b0 --- /dev/null +++ b/apps/meteor/tests/e2e/omnichannel/omnichannel-monitor-role.spec.ts @@ -0,0 +1,275 @@ +import { faker } from '@faker-js/faker'; +import { Page } from '@playwright/test'; + +import { IS_EE } from '../config/constants'; +import { Users } from '../fixtures/userStates'; +import { HomeOmnichannel } from '../page-objects'; +import { createAgent, makeAgentAvailable } from '../utils/omnichannel/agents'; +import { createDepartment } from '../utils/omnichannel/departments'; +import { createMonitor } from '../utils/omnichannel/monitors'; +import { createConversation } from '../utils/omnichannel/rooms'; +import { createOrUpdateUnit, fetchUnitMonitors } from '../utils/omnichannel/units'; +import { test, expect } from '../utils/test'; + +const MONITOR = 'user3'; +const MONITOR_ADMIN = 'rocketchat.internal.admin.test'; +const ROOM_A = faker.person.fullName(); +const ROOM_B = faker.person.fullName(); +const ROOM_C = faker.person.fullName(); +const ROOM_D = faker.person.fullName(); + +test.use({ storageState: Users.user3.state }); + +test.describe('OC - Monitor Role', () => { + test.skip(!IS_EE, 'Enterprise Edition Only'); + + let departments: Awaited>[]; + let conversations: Awaited>[]; + let agents: Awaited>[]; + let monitors: Awaited>[]; + let units: Awaited>[]; + let poOmnichannel: HomeOmnichannel; + + // Reset user3 roles + test.beforeAll(async ({ api }) => { + const res = await api.post('/users.update', { + data: { roles: ['user'] }, + userId: 'user3', + }); + await expect(res.status()).toBe(200); + }); + + // Allow manual on hold + test.beforeAll(async ({ api }) => { + const responses = await Promise.all([ + api.post('/settings/Livechat_allow_manual_on_hold', { value: true }), + api.post('/settings/Livechat_allow_manual_on_hold_upon_agent_engagement_only', { value: false }), + ]); + responses.forEach((res) => expect(res.status()).toBe(200)); + }); + + // Create agents + test.beforeAll(async ({ api }) => { + agents = await Promise.all([createAgent(api, 'user1'), createAgent(api, 'user2')]); + + const agentsStatuses = await Promise.all(agents.map(({ data: agent }) => makeAgentAvailable(api, agent._id))); + + agentsStatuses.forEach((res) => expect(res.status()).toBe(200)); + }); + + // Create departments + test.beforeAll(async ({ api }) => { + departments = await Promise.all([createDepartment(api), createDepartment(api), createDepartment(api)]); + }); + + // Create conversations + test.beforeAll(async ({ api }) => { + const [departmentA, departmentB] = departments.map(({ data }) => data); + + conversations = await Promise.all([ + createConversation(api, { + visitorName: ROOM_A, + agentId: `user1`, + departmentId: departmentA._id, + }), + createConversation(api, { + visitorName: ROOM_B, + agentId: `user2`, + departmentId: departmentA._id, + }), + createConversation(api, { + visitorName: ROOM_C, + agentId: `user2`, + departmentId: departmentB._id, + }), + createConversation(api, { + visitorName: ROOM_D, + }), + ]); + }); + + // Create monitors + test.beforeAll(async ({ api }) => { + monitors = await Promise.all([createMonitor(api, MONITOR), createMonitor(api, MONITOR_ADMIN)]); + }); + + // Create units + test.beforeAll(async ({ api }) => { + const [departmentA, departmentB, departmentC] = departments.map(({ data }) => data); + + units = await Promise.all([ + createOrUpdateUnit(api, { + monitors: [{ monitorId: MONITOR, username: MONITOR }], + departments: [{ departmentId: departmentA._id }, { departmentId: departmentB._id }], + }), + createOrUpdateUnit(api, { + monitors: [{ monitorId: MONITOR_ADMIN, username: MONITOR_ADMIN }], + departments: [{ departmentId: departmentC._id }], + }), + ]); + }); + + // Delete all created data + test.afterAll(async ({ api }) => { + await Promise.all([ + ...agents.map((agent) => agent.delete()), + ...departments.map((department) => department.delete()), + ...conversations.map((conversation) => conversation.delete()), + ...units.map((unit) => unit.delete()), + ...monitors.map((monitor) => monitor.delete()), + // Reset setting + api.post('/settings/Livechat_allow_manual_on_hold', { value: false }), + api.post('/settings/Livechat_allow_manual_on_hold_upon_agent_engagement_only', { value: true }), + ]); + }); + + test.beforeEach(async ({ page }: { page: Page }) => { + poOmnichannel = new HomeOmnichannel(page); + + await page.goto('/omnichannel'); + }); + + test('OC - Monitor Role - Basic permissions', async () => { + await test.step('expect agent to not have access to omnichannel administration', async () => { + await expect(poOmnichannel.omnisidenav.linkCurrentChats).toBeVisible(); + await expect(poOmnichannel.omnisidenav.linkAnalytics).toBeVisible(); + await expect(poOmnichannel.omnisidenav.linkRealTimeMonitoring).toBeVisible(); + await expect(poOmnichannel.omnisidenav.linkAgents).toBeVisible(); + await expect(poOmnichannel.omnisidenav.linkDepartments).toBeVisible(); + await expect(poOmnichannel.omnisidenav.linkBusinessHours).toBeVisible(); + await expect(poOmnichannel.omnisidenav.linkReports).toBeVisible(); + await expect(poOmnichannel.omnisidenav.linkCannedResponses).toBeVisible(); + }); + + // await test.step('expect to be able to see contact center', async () => {}); + + // await test.step('expect to be able to see queue', async () => {}); + + // await test.step('expect to be able to edit custom fields', async () => {}); + }); + + test('OC - Monitor Role - Canned responses', async () => { + // TODO: move to unit test + await test.step('expect not to be able to create public canned responses (administration)', async () => { + await poOmnichannel.omnisidenav.linkCannedResponses.click(); + await poOmnichannel.cannedResponses.btnNew.click(); + await expect(poOmnichannel.cannedResponses.radioPublic).toBeDisabled(); + }); + }); + + test('OC - Monitor Role - Current Chats', async ({ page }) => { + const [conversationA] = conversations; + const { room: roomA } = conversationA.data; + + await test.step('expect to be able to view only chats from same unit', async () => { + await expect(poOmnichannel.currentChats.findRowByName(ROOM_A)).toBeVisible(); + await expect(poOmnichannel.currentChats.findRowByName(ROOM_B)).toBeVisible(); + await expect(poOmnichannel.currentChats.findRowByName(ROOM_C)).toBeVisible(); + await expect(poOmnichannel.currentChats.findRowByName(ROOM_D)).not.toBeVisible(); + }); + + await test.step('expect to be able to join chats from same unit', async () => { + await poOmnichannel.currentChats.findRowByName(ROOM_A).click(); + await expect(page).toHaveURL(`/omnichannel/current/${roomA._id}`); + await expect(poOmnichannel.content.btnJoinRoom).toBeVisible(); + await expect(poOmnichannel.content.inputMessage).not.toBeVisible(); + + await poOmnichannel.content.btnJoinRoom.click(); + await expect(poOmnichannel.content.lastSystemMessageBody).toHaveText('joined the channel'); + await expect(poOmnichannel.content.btnJoinRoom).not.toBeVisible(); + await expect(poOmnichannel.content.inputMessage).toBeVisible(); + }); + + await test.step('expect to be able to put a conversation from another agent on hold', async () => { + await poOmnichannel.content.btnOnHold.click({ clickCount: 2 }); + await expect(poOmnichannel.content.modalOnHold).toBeVisible(); + await poOmnichannel.content.btnOnHoldConfirm.click(); + await expect(poOmnichannel.content.lastSystemMessageBody).toHaveText( + `Chat On Hold: The chat was manually placed On Hold by ${MONITOR}`, + ); + await expect(poOmnichannel.content.inputMessage).not.toBeVisible(); + await expect(poOmnichannel.content.btnResume).toBeVisible(); + }); + + await test.step('expect to be able resume a conversation from another agent on hold', async () => { + await poOmnichannel.content.btnResume.click(); + await expect(poOmnichannel.content.btnResume).not.toBeVisible(); + await expect(poOmnichannel.content.inputMessage).toBeVisible(); + await expect(poOmnichannel.content.btnOnHold).toBeVisible(); + }); + + // await test.step('expect to be able to edit room information from another agent', async () => {); + + await test.step('expect to be able to close a conversation from another agent', async () => { + await poOmnichannel.content.btnCloseChat.click(); + await poOmnichannel.content.inputModalClosingComment.type('any_comment'); + await poOmnichannel.content.btnModalConfirm.click(); + await expect(poOmnichannel.toastSuccess).toBeVisible(); + await page.waitForURL('/omnichannel/current'); + }); + + await test.step('expect not to be able to remove closed room', async () => { + await expect(poOmnichannel.currentChats.findRowByName(ROOM_A)).toBeVisible(); + await expect(poOmnichannel.currentChats.btnRemoveByName(ROOM_A)).not.toBeVisible(); + }); + }); + + test('OC - Monitor Role - Permission revoked', async ({ page, api }) => { + const [unitA, unitB] = units; + const [monitor] = monitors; + + await poOmnichannel.omnisidenav.linkCurrentChats.click(); + + await test.step('expect not to be able to see chats from removed department', async () => { + await test.step('expect rooms from both departments to be visible', async () => { + await expect(poOmnichannel.currentChats.findRowByName(ROOM_B)).toBeVisible(); + await expect(poOmnichannel.currentChats.findRowByName(ROOM_C)).toBeVisible(); + await expect(poOmnichannel.currentChats.findRowByName(ROOM_D)).not.toBeVisible(); + }); + + await test.step('expect to remove departmentB from unit', async () => { + const [departmentA] = departments.map(({ data }) => data); + + await createOrUpdateUnit(api, { + id: unitA.data._id, + monitors: [{ monitorId: MONITOR, username: MONITOR }], + departments: [{ departmentId: departmentA._id }], + }); + + await page.reload(); + }); + + await test.step('expect to have only room B visible', async () => { + await expect(poOmnichannel.currentChats.findRowByName(ROOM_B)).toBeVisible(); + await expect(poOmnichannel.currentChats.findRowByName(ROOM_C)).not.toBeVisible(); + await expect(poOmnichannel.currentChats.findRowByName(ROOM_D)).not.toBeVisible(); + }); + }); + + await test.step('expect not to be able to see conversations once unit is removed', async () => { + const res = await unitA.delete(); + await expect(res.status()).toBe(200); + await page.reload(); + await expect(page.locator('text="No chats yet"')).toBeVisible(); + }); + + await test.step('expect to be able to see all conversations once all units are removed', async () => { + const res = await unitB.delete(); + await expect(res.status()).toBe(200); + await page.reload(); + await expect(poOmnichannel.currentChats.findRowByName(ROOM_D)).toBeVisible(); + }); + + await test.step('expect not to be able to see current chats once role is removed', async () => { + const res = await monitor.delete(); + await expect(res.status()).toBe(200); + await page.reload(); + await expect(page.locator('p >> text="You are not authorized to view this page."')).toBeVisible(); + }); + + await test.step('expect monitor to be automaticaly removed from unit once monitor is removed', async () => { + const { data: monitors } = await fetchUnitMonitors(api, unitA.data._id); + await expect(monitors).toHaveLength(0); + }); + }); +}); diff --git a/apps/meteor/tests/e2e/omnichannel/omnichannel-monitors.spec.ts b/apps/meteor/tests/e2e/omnichannel/omnichannel-monitors.spec.ts new file mode 100644 index 00000000000..c3e2ef16b2c --- /dev/null +++ b/apps/meteor/tests/e2e/omnichannel/omnichannel-monitors.spec.ts @@ -0,0 +1,96 @@ +import { IS_EE } from '../config/constants'; +import { Users } from '../fixtures/userStates'; +import { OmnichannelMonitors } from '../page-objects'; +import { test, expect } from '../utils/test'; + +test.use({ storageState: Users.user1.state }); + +test.skip(!IS_EE, 'OC - Manage Monitors > Enterprise Only'); + +test.describe.serial('OC - Manage Monitors', () => { + let poMonitors: OmnichannelMonitors; + + test.beforeAll(async ({ api }) => { + await Promise.all([ + api.post('/livechat/users/agent', { username: 'user1' }), + api.post('/livechat/users/agent', { username: 'user2' }), + api.post('/livechat/users/manager', { username: 'user1' }), + ]); + }); + + test.afterAll(async ({ api }) => { + await Promise.all([ + api.delete('/livechat/users/agent/user1'), + api.delete('/livechat/users/manager/user1'), + api.delete('/livechat/users/agent/user2'), + api.delete('/livechat/users/manager/user2'), + ]); + }); + + test.beforeEach(async ({ page }) => { + poMonitors = new OmnichannelMonitors(page); + + await page.goto('/omnichannel'); + await page.locator('.main-content').waitFor(); + await poMonitors.sidenav.linkMonitors.click(); + }); + + test('OC - Manager Monitors - Add monitor', async () => { + await test.step('expect to add agent as monitor', async () => { + await expect(poMonitors.findRowByName('user1')).not.toBeVisible(); + await poMonitors.selectMonitor('user1'); + await poMonitors.btnAddMonitor.click(); + await expect(poMonitors.findRowByName('user1')).toBeVisible(); + }); + + await test.step('expect to remove agent from monitor', async () => { + await poMonitors.btnRemoveByName('user1').click(); + await expect(poMonitors.modalConfirmRemove).toBeVisible(); + await poMonitors.btnConfirmRemove.click(); + await expect(poMonitors.findRowByName('user1')).not.toBeVisible(); + }); + }); + + test('OC - Manager Monitors - Search', async () => { + await test.step('expect to add 2 monitors', async () => { + await poMonitors.selectMonitor('user1'); + await poMonitors.btnAddMonitor.click(); + + await poMonitors.selectMonitor('user2'); + await poMonitors.btnAddMonitor.click(); + + await expect(poMonitors.findRowByName('user1')).toBeVisible(); + await expect(poMonitors.findRowByName('user2')).toBeVisible(); + }); + + await test.step('expect to search monitor', async () => { + await expect(poMonitors.findRowByName('user1')).toBeVisible(); + await expect(poMonitors.findRowByName('user2')).toBeVisible(); + + await poMonitors.inputSearch.fill('user1'); + await expect(poMonitors.findRowByName('user1')).toBeVisible(); + await expect(poMonitors.findRowByName('user2')).not.toBeVisible(); + + await poMonitors.inputSearch.fill('user2'); + await expect(poMonitors.findRowByName('user1')).not.toBeVisible(); + await expect(poMonitors.findRowByName('user2')).toBeVisible(); + + await poMonitors.inputSearch.fill(''); + await expect(poMonitors.findRowByName('user1')).toBeVisible(); + await expect(poMonitors.findRowByName('user2')).toBeVisible(); + }); + + await test.step('expect to remove monitors', async () => { + await poMonitors.btnRemoveByName('user1').click(); + await expect(poMonitors.modalConfirmRemove).toBeVisible(); + await poMonitors.btnConfirmRemove.click(); + + await poMonitors.btnRemoveByName('user2').click(); + await expect(poMonitors.modalConfirmRemove).toBeVisible(); + await poMonitors.btnConfirmRemove.click(); + + await expect(poMonitors.findRowByName('user1')).not.toBeVisible(); + await expect(poMonitors.findRowByName('user2')).not.toBeVisible(); + }); + }); +}); diff --git a/apps/meteor/tests/e2e/omnichannel/omnichannel-priorities-sidebar.spec.ts b/apps/meteor/tests/e2e/omnichannel/omnichannel-priorities-sidebar.spec.ts index d1572f7b5ce..f9550ebbac0 100644 --- a/apps/meteor/tests/e2e/omnichannel/omnichannel-priorities-sidebar.spec.ts +++ b/apps/meteor/tests/e2e/omnichannel/omnichannel-priorities-sidebar.spec.ts @@ -2,7 +2,7 @@ import { faker } from '@faker-js/faker'; import { IS_EE } from '../config/constants'; import { Users } from '../fixtures/userStates'; -import { HomeChannel } from '../page-objects'; +import { HomeOmnichannel } from '../page-objects'; import { OmnichannelRoomInfo } from '../page-objects/omnichannel-room-info'; import { createConversation } from '../utils/omnichannel/rooms'; import { test, expect } from '../utils/test'; @@ -20,7 +20,7 @@ test.skip(!IS_EE, 'Omnichannel Priorities > Enterprise Only'); test.use({ storageState: Users.user1.state }); test.describe.serial('OC - Priorities [Sidebar]', () => { - let poHomeChannel: HomeChannel; + let poHomeChannel: HomeOmnichannel; let poRoomInfo: OmnichannelRoomInfo; test.beforeAll(async ({ api }) => { @@ -34,7 +34,7 @@ test.describe.serial('OC - Priorities [Sidebar]', () => { }); test.beforeEach(async ({ page }) => { - poHomeChannel = new HomeChannel(page); + poHomeChannel = new HomeOmnichannel(page); poRoomInfo = new OmnichannelRoomInfo(page); }); @@ -82,7 +82,7 @@ test.describe.serial('OC - Priorities [Sidebar]', () => { }); await test.step('expect to change subscription priority using sidebar menu', async () => { - await poHomeChannel.content.takeOmnichannelChatButton.click(); + await poHomeChannel.content.btnTakeChat.click(); await systemMessage.locator('text="joined the channel"').waitFor(); await page.waitForTimeout(500); diff --git a/apps/meteor/tests/e2e/omnichannel/omnichannel-takeChat.spec.ts b/apps/meteor/tests/e2e/omnichannel/omnichannel-takeChat.spec.ts index 4738158ca23..90087d968be 100644 --- a/apps/meteor/tests/e2e/omnichannel/omnichannel-takeChat.spec.ts +++ b/apps/meteor/tests/e2e/omnichannel/omnichannel-takeChat.spec.ts @@ -3,14 +3,14 @@ import type { Page } from '@playwright/test'; import { createAuxContext } from '../fixtures/createAuxContext'; import { Users } from '../fixtures/userStates'; -import { OmnichannelLiveChat, HomeChannel } from '../page-objects'; +import { OmnichannelLiveChat, HomeOmnichannel } from '../page-objects'; import { test, expect } from '../utils/test'; test.describe('omnichannel-takeChat', () => { let poLiveChat: OmnichannelLiveChat; let newVisitor: { email: string; name: string }; - let agent: { page: Page; poHomeChannel: HomeChannel }; + let agent: { page: Page; poHomeChannel: HomeOmnichannel }; test.beforeAll(async ({ api, browser }) => { await Promise.all([ @@ -20,7 +20,7 @@ test.describe('omnichannel-takeChat', () => { ]); const { page } = await createAuxContext(browser, Users.user1); - agent = { page, poHomeChannel: new HomeChannel(page) }; + agent = { page, poHomeChannel: new HomeOmnichannel(page) }; }); test.afterAll(async ({ api }) => { @@ -52,11 +52,11 @@ test.describe('omnichannel-takeChat', () => { test('expect "user1" to be able to take the chat from the queue', async () => { await agent.poHomeChannel.sidenav.openQueuedOmnichannelChat(newVisitor.name); - await expect(agent.poHomeChannel.content.takeOmnichannelChatButton).toBeVisible(); - await agent.poHomeChannel.content.takeOmnichannelChatButton.click(); + await expect(agent.poHomeChannel.content.btnTakeChat).toBeVisible(); + await agent.poHomeChannel.content.btnTakeChat.click(); await agent.poHomeChannel.sidenav.openChat(newVisitor.name); - await expect(agent.poHomeChannel.content.takeOmnichannelChatButton).not.toBeVisible(); + await expect(agent.poHomeChannel.content.btnTakeChat).not.toBeVisible(); await expect(agent.poHomeChannel.content.inputMessage).toBeVisible(); }); @@ -65,8 +65,8 @@ test.describe('omnichannel-takeChat', () => { await agent.poHomeChannel.sidenav.switchStatus('offline'); await agent.poHomeChannel.sidenav.openQueuedOmnichannelChat(newVisitor.name); - await expect(agent.poHomeChannel.content.takeOmnichannelChatButton).toBeVisible(); - await agent.poHomeChannel.content.takeOmnichannelChatButton.click(); + await expect(agent.poHomeChannel.content.btnTakeChat).toBeVisible(); + await agent.poHomeChannel.content.btnTakeChat.click(); // expect to see error message await expect(agent.page.locator('text=Agent status is offline or Omnichannel service is not active')).toBeVisible(); diff --git a/apps/meteor/tests/e2e/omnichannel/omnichannel-transfer-to-another-agents.spec.ts b/apps/meteor/tests/e2e/omnichannel/omnichannel-transfer-to-another-agents.spec.ts index dc31f54be93..272cde5c2c4 100644 --- a/apps/meteor/tests/e2e/omnichannel/omnichannel-transfer-to-another-agents.spec.ts +++ b/apps/meteor/tests/e2e/omnichannel/omnichannel-transfer-to-another-agents.spec.ts @@ -1,92 +1,98 @@ -import { faker } from '@faker-js/faker'; import type { Page } from '@playwright/test'; import { createAuxContext } from '../fixtures/createAuxContext'; import { Users } from '../fixtures/userStates'; -import { OmnichannelLiveChat, HomeOmnichannel } from '../page-objects'; +import { HomeOmnichannel } from '../page-objects'; +import { createAgent } from '../utils/omnichannel/agents'; +import { createManager } from '../utils/omnichannel/managers'; +import { createConversation } from '../utils/omnichannel/rooms'; import { test, expect } from '../utils/test'; -test.describe('omnichannel-transfer-to-another-agent', () => { - let poLiveChat: OmnichannelLiveChat; - let newVisitor: { email: string; name: string }; +test.describe('OC - Chat transfers [Agent role]', () => { + let sessions: { page: Page; poHomeOmnichannel: HomeOmnichannel }[]; + let agents: Awaited>[]; + let managers: Awaited>[]; + let conversations: Awaited>[]; - let agent1: { page: Page; poHomeOmnichannel: HomeOmnichannel }; - let agent2: { page: Page; poHomeOmnichannel: HomeOmnichannel }; - test.beforeAll(async ({ api, browser }) => { - await Promise.all([ - api.post('/livechat/users/agent', { username: 'user1' }).then((res) => expect(res.status()).toBe(200)), - api.post('/livechat/users/agent', { username: 'user2' }).then((res) => expect(res.status()).toBe(200)), - api.post('/livechat/users/manager', { username: 'user1' }).then((res) => expect(res.status()).toBe(200)), - api.post('/settings/Livechat_enabled_when_agent_idle', { value: false }).then((res) => expect(res.status()).toBe(200)), - ]); + // Create agents and managers + test.beforeAll(async ({ api }) => { + agents = await Promise.all([createAgent(api, 'user1'), createAgent(api, 'user2')]); + + managers = await Promise.all([createManager(api, 'user1')]); + }); - const { page } = await createAuxContext(browser, Users.user1); - agent1 = { page, poHomeOmnichannel: new HomeOmnichannel(page) }; + // Livechat when agent idle + test.beforeAll(async ({ api }) => { + await api.post('/settings/Livechat_enabled_when_agent_idle', { value: false }).then((res) => expect(res.status()).toBe(200)); + }); - const { page: page2 } = await createAuxContext(browser, Users.user2); - agent2 = { page: page2, poHomeOmnichannel: new HomeOmnichannel(page2) }; + // Create agent sessions + test.beforeAll(async ({ browser }) => { + sessions = await Promise.all([ + createAuxContext(browser, Users.user1).then(({ page }) => ({ page, poHomeOmnichannel: new HomeOmnichannel(page) })), + createAuxContext(browser, Users.user2).then(({ page }) => ({ page, poHomeOmnichannel: new HomeOmnichannel(page) })), + ]); }); + // Delete all data test.afterAll(async ({ api }) => { await Promise.all([ - api.delete('/livechat/users/agent/user1').then((res) => expect(res.status()).toBe(200)), - api.delete('/livechat/users/manager/user1').then((res) => expect(res.status()).toBe(200)), - api.delete('/livechat/users/agent/user2').then((res) => expect(res.status()).toBe(200)), + ...conversations.map((conversation) => conversation.delete()), + ...agents.map((agent) => agent.delete()), + ...managers.map((manager) => manager.delete()), api.post('/settings/Livechat_enabled_when_agent_idle', { value: true }).then((res) => expect(res.status()).toBe(200)), ]); + }); + + // Make "user-1" online & "user-2" offline so that chat can be automatically routed to "user-1" + test.beforeEach(async () => { + const [agentA, agentB] = sessions; + await agentA.poHomeOmnichannel.sidenav.switchStatus('online'); + await agentB.poHomeOmnichannel.sidenav.switchStatus('offline'); + }); - await agent1.page.close(); - await agent2.page.close(); + // Close sessions + test.afterEach(async () => { + await Promise.all(sessions.map(({ page }) => page.close())); }); - test.beforeEach(async ({ page, api }) => { - // make "user-1" online & "user-2" offline so that chat can be automatically routed to "user-1" - await agent1.poHomeOmnichannel.sidenav.switchStatus('online'); - await agent2.poHomeOmnichannel.sidenav.switchStatus('offline'); - - // start a new chat for each test - newVisitor = { - name: faker.person.firstName(), - email: faker.internet.email(), - }; - poLiveChat = new OmnichannelLiveChat(page, api); - await page.goto('/livechat'); - await poLiveChat.openLiveChat(); - await poLiveChat.sendMessage(newVisitor, false); - await poLiveChat.onlineAgentMessage.type('this_a_test_message_from_visitor'); - await poLiveChat.btnSendMessageToOnlineAgent.click(); + + // Start a new chat for each test + test.beforeEach(async ({ api }) => { + conversations = [await createConversation(api)]; }); - test('transfer omnichannel chat to another agent', async () => { - await test.step('Expect to have 1 omnichannel assigned to agent 1', async () => { - await agent1.poHomeOmnichannel.sidenav.getSidebarItemByName(newVisitor.name).click(); + test('OC - Chat transfers [Agent role] - Transfer omnichannel chat to another agent', async () => { + const [agentA, agentB] = sessions; + const [{ visitor }] = conversations.map(({ data }) => data); + + await test.step('expect to have 1 omnichannel assigned to agent 1', async () => { + await agentA.poHomeOmnichannel.sidenav.getSidebarItemByName(visitor.name).click(); }); - await test.step('Expect to not be able to transfer chat to "user-2" when that user is offline', async () => { - await agent2.poHomeOmnichannel.sidenav.switchStatus('offline'); + await test.step('expect to not be able to transfer chat to "user-2" when that user is offline', async () => { + await agentB.poHomeOmnichannel.sidenav.switchStatus('offline'); - await agent1.poHomeOmnichannel.content.btnForwardChat.click(); - await agent1.poHomeOmnichannel.content.inputModalAgentUserName.click(); - await agent1.poHomeOmnichannel.content.inputModalAgentUserName.type('user2'); - await expect(agent1.page.locator('text=Empty')).toBeVisible(); + await agentA.poHomeOmnichannel.content.btnForwardChat.click(); + await agentA.poHomeOmnichannel.content.forwardChatModal.inputFowardUser.click(); + await agentA.poHomeOmnichannel.content.forwardChatModal.inputFowardUser.type('user2'); + await expect(agentA.page.locator('text=Empty')).toBeVisible(); - await agent1.page.goto('/'); + await agentA.page.goto('/'); }); - await test.step('Expect to be able to transfer an omnichannel to conversation to agent 2 as agent 1 when agent 2 is online', async () => { - await agent2.poHomeOmnichannel.sidenav.switchStatus('online'); - - await agent1.poHomeOmnichannel.sidenav.getSidebarItemByName(newVisitor.name).click(); - await agent1.poHomeOmnichannel.content.btnForwardChat.click(); - await agent1.poHomeOmnichannel.content.inputModalAgentUserName.click(); - await agent1.poHomeOmnichannel.content.inputModalAgentUserName.type('user2'); - await agent1.page.locator('.rcx-option .rcx-option__wrapper >> text="user2 (@user2)"').click(); - await agent1.poHomeOmnichannel.content.inputModalAgentForwardComment.type('any_comment'); - await agent1.poHomeOmnichannel.content.btnModalConfirm.click(); - await expect(agent1.poHomeOmnichannel.toastSuccess).toBeVisible(); + await test.step('expect to be able to transfer an omnichannel to conversation to agent 2 as agent 1 when agent 2 is online', async () => { + await agentB.poHomeOmnichannel.sidenav.switchStatus('online'); + + await agentA.poHomeOmnichannel.sidenav.getSidebarItemByName(visitor.name).click(); + await agentA.poHomeOmnichannel.content.btnForwardChat.click(); + await agentA.poHomeOmnichannel.content.forwardChatModal.selectUser('user2'); + await agentA.poHomeOmnichannel.content.forwardChatModal.inputComment.type('any_comment'); + await agentA.poHomeOmnichannel.content.forwardChatModal.btnForward.click(); + await expect(agentA.poHomeOmnichannel.toastSuccess).toBeVisible(); }); - await test.step('Expect to have 1 omnichannel assigned to agent 2', async () => { - await agent2.poHomeOmnichannel.sidenav.getSidebarItemByName(newVisitor.name).click(); + await test.step('expect to have 1 omnichannel assigned to agent 2', async () => { + await agentB.poHomeOmnichannel.sidenav.getSidebarItemByName(visitor.name).click(); }); }); }); diff --git a/apps/meteor/tests/e2e/omnichannel/omnichannel-units.spec.ts b/apps/meteor/tests/e2e/omnichannel/omnichannel-units.spec.ts new file mode 100644 index 00000000000..4f713cb7af7 --- /dev/null +++ b/apps/meteor/tests/e2e/omnichannel/omnichannel-units.spec.ts @@ -0,0 +1,170 @@ +import { faker } from '@faker-js/faker'; +import type { Page } from '@playwright/test'; + +import { IS_EE } from '../config/constants'; +import { Users } from '../fixtures/userStates'; +import { OmnichannelUnits } from '../page-objects'; +import { createAgent } from '../utils/omnichannel/agents'; +import { createDepartment } from '../utils/omnichannel/departments'; +import { createMonitor } from '../utils/omnichannel/monitors'; +import { createOrUpdateUnit } from '../utils/omnichannel/units'; +import { test, expect } from '../utils/test'; + +test.use({ storageState: Users.admin.state }); + +test.describe('OC - Manage Units', () => { + test.skip(!IS_EE, 'OC - Manage Units > Enterprise Edition Only'); + + let poOmnichannelUnits: OmnichannelUnits; + + let department: Awaited>; + + let agent: Awaited>; + + let monitor: Awaited>; + + test.beforeAll(async ({ api }) => { + department = await createDepartment(api); + }); + + test.beforeAll(async ({ api }) => { + agent = await createAgent(api, 'user2'); + }); + + test.beforeAll(async ({ api }) => { + monitor = await createMonitor(api, 'user2'); + }); + + test.afterAll(async () => { + await department.delete(); + await monitor.delete(); + await agent.delete(); + }); + + test.beforeEach(async ({ page }: { page: Page }) => { + poOmnichannelUnits = new OmnichannelUnits(page); + + await page.goto('/omnichannel'); + await poOmnichannelUnits.sidenav.linkUnits.click(); + }); + + test('OC - Manage Units - Create Unit', async ({ page }) => { + const unitName = faker.string.uuid(); + + await test.step('expect correct form default state', async () => { + await poOmnichannelUnits.btnCreateUnit.click(); + await expect(poOmnichannelUnits.contextualBar).toBeVisible(); + await expect(poOmnichannelUnits.btnSave).toBeDisabled(); + await expect(poOmnichannelUnits.btnCancel).toBeEnabled(); + await poOmnichannelUnits.btnCancel.click(); + await expect(poOmnichannelUnits.contextualBar).not.toBeVisible(); + }); + + await test.step('expect to create new unit', async () => { + await poOmnichannelUnits.btnCreateUnit.click(); + await poOmnichannelUnits.inputName.fill(unitName); + await poOmnichannelUnits.selectVisibility('public'); + await poOmnichannelUnits.selectDepartment(department.data); + await poOmnichannelUnits.selectMonitor('user2'); + await poOmnichannelUnits.btnSave.click(); + await expect(poOmnichannelUnits.contextualBar).not.toBeVisible(); + + await test.step('expect unit to have been created', async () => { + await poOmnichannelUnits.search(unitName); + await expect(poOmnichannelUnits.findRowByName(unitName)).toBeVisible(); + }); + }); + + await test.step('expect to delete unit', async () => { + await test.step('expect confirm delete unit', async () => { + await test.step('expect to be able to cancel delete', async () => { + await poOmnichannelUnits.btnDeleteByName(unitName).click(); + await expect(poOmnichannelUnits.confirmDeleteModal).toBeVisible(); + await poOmnichannelUnits.btnCancelDeleteModal.click(); + await expect(poOmnichannelUnits.confirmDeleteModal).not.toBeVisible(); + }); + + await test.step('expect to confirm delete', async () => { + await poOmnichannelUnits.btnDeleteByName(unitName).click(); + await expect(poOmnichannelUnits.confirmDeleteModal).toBeVisible(); + await poOmnichannelUnits.btnConfirmDeleteModal.click(); + await expect(poOmnichannelUnits.confirmDeleteModal).not.toBeVisible(); + }); + }); + + await test.step('expect to have been deleted', async () => { + if (await poOmnichannelUnits.inputSearch.isVisible()) { + await poOmnichannelUnits.search(unitName); + await expect(poOmnichannelUnits.findRowByName(unitName)).not.toBeVisible(); + } else { + await expect(page.locator('h3 >> text="No units yet"')).toBeVisible(); + } + }); + }); + }); + + test('OC - Manage Units - Edit unit', async ({ api, page }) => { + const edittedUnitName = faker.string.uuid(); + + const unit = await test.step('expect to create new unit', async () => { + const { data: unit } = await createOrUpdateUnit(api, { + name: faker.string.uuid(), + visibility: 'public', + monitors: [{ monitorId: monitor.data._id, username: 'user2' }], + departments: [{ departmentId: department.data._id }], + }); + + return unit; + }); + + await test.step('expect to edit unit', async () => { + await poOmnichannelUnits.search(unit.name); + await poOmnichannelUnits.findRowByName(unit.name).click(); + await expect(poOmnichannelUnits.contextualBar).toBeVisible(); + await poOmnichannelUnits.inputName.fill(edittedUnitName); + await poOmnichannelUnits.btnSave.click(); + }); + + await test.step('expect unit to have been edited', async () => { + await poOmnichannelUnits.sidenav.linkPriorities.click(); + await poOmnichannelUnits.sidenav.linkUnits.click(); // refresh the page + await poOmnichannelUnits.search(edittedUnitName); + await expect(poOmnichannelUnits.findRowByName(edittedUnitName)).toBeVisible(); + }); + + await test.step('expect to delete unit', async () => { + await poOmnichannelUnits.findRowByName(edittedUnitName).click(); + await expect(poOmnichannelUnits.contextualBar).toBeVisible(); + + await test.step('expect confirm delete unit', async () => { + await test.step('expect to be able to cancel delete', async () => { + await poOmnichannelUnits.btnDelete.click(); + await expect(poOmnichannelUnits.confirmDeleteModal).toBeVisible(); + await poOmnichannelUnits.btnCancelDeleteModal.click(); + await expect(poOmnichannelUnits.confirmDeleteModal).not.toBeVisible(); + }); + + await test.step('expect to confirm delete', async () => { + await poOmnichannelUnits.btnDelete.click(); + await expect(poOmnichannelUnits.confirmDeleteModal).toBeVisible(); + await poOmnichannelUnits.btnConfirmDeleteModal.click(); + await expect(poOmnichannelUnits.confirmDeleteModal).not.toBeVisible(); + }); + + await expect(poOmnichannelUnits.contextualBar).not.toBeVisible(); + }); + + await test.step('expect to have been deleted', async () => { + await poOmnichannelUnits.sidenav.linkPriorities.click(); + await poOmnichannelUnits.sidenav.linkUnits.click(); // refresh the page + + if (await poOmnichannelUnits.inputSearch.isVisible()) { + await poOmnichannelUnits.search(edittedUnitName); + await expect(poOmnichannelUnits.findRowByName(edittedUnitName)).not.toBeVisible(); + } else { + await expect(page.locator('h3 >> text="No units yet"')).toBeVisible(); + } + }); + }); + }); +}); diff --git a/apps/meteor/tests/e2e/page-objects/fragments/home-content.ts b/apps/meteor/tests/e2e/page-objects/fragments/home-content.ts index 2ba8cd6428d..434fe6ba95e 100644 --- a/apps/meteor/tests/e2e/page-objects/fragments/home-content.ts +++ b/apps/meteor/tests/e2e/page-objects/fragments/home-content.ts @@ -196,14 +196,6 @@ export class HomeContent { return this.page.locator('.rcx-vertical-bar button:has-text("Create")'); } - get inputModalAgentUserName(): Locator { - return this.page.locator('#modal-root input[placeholder="Username, name or e-mail"]'); - } - - get inputModalAgentForwardComment(): Locator { - return this.page.locator('[data-qa-id="ForwardChatModalTextAreaInputComment"]'); - } - async pickEmoji(emoji: string, section = 'Smileys & People') { await this.page.locator('role=toolbar[name="Composer Primary Actions"] >> role=button[name="Emoji"]').click(); @@ -245,10 +237,6 @@ export class HomeContent { await this.page.locator('//main//aside >> [name="alsoSendThreadToChannel"]').setChecked(isChecked); } - get takeOmnichannelChatButton(): Locator { - return this.page.locator('role=button[name="Take it!"]'); - } - get lastSystemMessageBody(): Locator { return this.page.locator('[data-qa-type="system-message-body"]').last(); } @@ -288,4 +276,8 @@ export class HomeContent { get btnAnonymousTalk(): Locator { return this.page.locator('role=button[name="Or talk as anonymous"]'); } + + findSystemMessage(text: string): Locator { + return this.page.locator(`[data-qa-type="system-message-body"] >> text="${text}"`); + } } diff --git a/apps/meteor/tests/e2e/page-objects/fragments/home-omnichannel-content.ts b/apps/meteor/tests/e2e/page-objects/fragments/home-omnichannel-content.ts index 307f94273d3..dd5246528ed 100644 --- a/apps/meteor/tests/e2e/page-objects/fragments/home-omnichannel-content.ts +++ b/apps/meteor/tests/e2e/page-objects/fragments/home-omnichannel-content.ts @@ -1,14 +1,38 @@ import type { Locator, Page } from '@playwright/test'; +import { OmnichannelTransferChatModal } from '../omnichannel-transfer-chat-modal'; import { HomeContent } from './home-content'; import { OmnichannelCloseChatModal } from './omnichannel-close-chat-modal'; export class HomeOmnichannelContent extends HomeContent { - readonly omnichannelCloseChatModal: OmnichannelCloseChatModal; + readonly closeChatModal: OmnichannelCloseChatModal; + + readonly forwardChatModal: OmnichannelTransferChatModal; constructor(page: Page) { super(page); - this.omnichannelCloseChatModal = new OmnichannelCloseChatModal(page); + this.closeChatModal = new OmnichannelCloseChatModal(page); + this.forwardChatModal = new OmnichannelTransferChatModal(page); + } + + get btnReturnToQueue(): Locator { + return this.page.locator('role=button[name="Move to the queue"]'); + } + + get modalReturnToQueue(): Locator { + return this.page.locator('[data-qa-id="return-to-queue-modal"]'); + } + + get btnReturnToQueueConfirm():Locator { + return this.modalReturnToQueue.locator('role=button[name="Confirm"]'); + } + + get btnReturnToQueueCancel():Locator { + return this.modalReturnToQueue.locator('role=button[name="Cancel"]'); + } + + get btnTakeChat(): Locator { + return this.page.locator('role=button[name="Take it!"]'); } get inputMessage(): Locator { @@ -22,4 +46,24 @@ export class HomeOmnichannelContent extends HomeContent { get btnCloseChat(): Locator { return this.page.locator('[data-qa-id="ToolBoxAction-balloon-close-top-right"]'); } + + get btnReturn(): Locator { + return this.page.locator('[data-qa-id="ToolBoxAction-back"]'); + } + + get btnResume(): Locator { + return this.page.locator('role=button[name="Resume"]'); + } + + get modalOnHold(): Locator { + return this.page.locator('[data-qa-id="on-hold-modal"]'); + } + + get btnEditRoomInfo(): Locator { + return this.page.locator('button[data-qa-id="room-info-edit"]'); + } + + get btnOnHoldConfirm(): Locator { + return this.modalOnHold.locator('role=button[name="Place chat On-Hold"]'); + } } diff --git a/apps/meteor/tests/e2e/page-objects/fragments/omnichannel-sidenav.ts b/apps/meteor/tests/e2e/page-objects/fragments/omnichannel-sidenav.ts index 6b10bcd47b2..5d606131d01 100644 --- a/apps/meteor/tests/e2e/page-objects/fragments/omnichannel-sidenav.ts +++ b/apps/meteor/tests/e2e/page-objects/fragments/omnichannel-sidenav.ts @@ -38,4 +38,32 @@ export class OmnichannelSidenav { get linkPriorities(): Locator { return this.page.locator('a[href="/omnichannel/priorities"]'); } + + get linkMonitors(): Locator { + return this.page.locator('a[href="/omnichannel/monitors"]'); + } + + get linkBusinessHours(): Locator { + return this.page.locator('a[href="/omnichannel/businessHours"]'); + } + + get linkAnalytics(): Locator { + return this.page.locator('a[href="/omnichannel/analytics"]'); + } + + get linkRealTimeMonitoring(): Locator { + return this.page.locator('a[href="/omnichannel/realtime-monitoring"]'); + } + + get linkReports(): Locator { + return this.page.locator('a[href="/omnichannel/reports"]'); + } + + get linkCannedResponses(): Locator { + return this.page.locator('a[href="/omnichannel/canned-responses"]'); + } + + get linkUnits(): Locator { + return this.page.locator('a[href="/omnichannel/units"]'); + } } diff --git a/apps/meteor/tests/e2e/page-objects/home-omnichannel.ts b/apps/meteor/tests/e2e/page-objects/home-omnichannel.ts index 983d2392a47..e93e90b0b49 100644 --- a/apps/meteor/tests/e2e/page-objects/home-omnichannel.ts +++ b/apps/meteor/tests/e2e/page-objects/home-omnichannel.ts @@ -1,7 +1,11 @@ import type { Locator, Page } from '@playwright/test'; import { HomeOmnichannelContent, HomeSidenav, HomeFlextab, OmnichannelSidenav } from './fragments'; +import { OmnichannelAgents } from './omnichannel-agents'; +import { OmnichannelCannedResponses } from './omnichannel-canned-responses'; import { OmnichannelCurrentChats } from './omnichannel-current-chats'; +import { OmnichannelManager } from './omnichannel-manager'; +import { OmnichannelMonitors } from './omnichannel-monitors'; import { OmnichannelTranscript } from './omnichannel-transcript'; import { OmnichannelTriggers } from './omnichannel-triggers'; @@ -22,6 +26,14 @@ export class HomeOmnichannel { readonly transcript: OmnichannelTranscript; + readonly cannedResponses: OmnichannelCannedResponses; + + readonly agents: OmnichannelAgents; + + readonly managers: OmnichannelManager; + + readonly monitors: OmnichannelMonitors; + constructor(page: Page) { this.page = page; this.content = new HomeOmnichannelContent(page); @@ -31,6 +43,10 @@ export class HomeOmnichannel { this.omnisidenav = new OmnichannelSidenav(page); this.currentChats = new OmnichannelCurrentChats(page); this.transcript = new OmnichannelTranscript(page); + this.cannedResponses = new OmnichannelCannedResponses(page); + this.agents = new OmnichannelAgents(page); + this.managers = new OmnichannelManager(page); + this.monitors = new OmnichannelMonitors(page); } get toastSuccess(): Locator { diff --git a/apps/meteor/tests/e2e/page-objects/index.ts b/apps/meteor/tests/e2e/page-objects/index.ts index b6f1ca1275a..dfe90bc641c 100644 --- a/apps/meteor/tests/e2e/page-objects/index.ts +++ b/apps/meteor/tests/e2e/page-objects/index.ts @@ -11,5 +11,7 @@ export * from './omnichannel-current-chats'; export * from './omnichannel-livechat'; export * from './omnichannel-manager'; export * from './omnichannel-custom-fields'; +export * from './omnichannel-units'; export * from './home-omnichannel'; +export * from './omnichannel-monitors'; export * from './utils'; diff --git a/apps/meteor/tests/e2e/page-objects/omnichannel-administration.ts b/apps/meteor/tests/e2e/page-objects/omnichannel-administration.ts new file mode 100644 index 00000000000..01b8a6ed6ed --- /dev/null +++ b/apps/meteor/tests/e2e/page-objects/omnichannel-administration.ts @@ -0,0 +1,14 @@ +import type { Page } from '@playwright/test'; + +import { OmnichannelSidenav } from './fragments'; + +export class OmnichannelAdministration { + protected readonly page: Page; + + readonly sidenav: OmnichannelSidenav; + + constructor(page: Page) { + this.page = page; + this.sidenav = new OmnichannelSidenav(page); + } +} diff --git a/apps/meteor/tests/e2e/page-objects/omnichannel-agents.ts b/apps/meteor/tests/e2e/page-objects/omnichannel-agents.ts index fe000717dad..c6e9f5a0d0e 100644 --- a/apps/meteor/tests/e2e/page-objects/omnichannel-agents.ts +++ b/apps/meteor/tests/e2e/page-objects/omnichannel-agents.ts @@ -7,13 +7,19 @@ export class OmnichannelAgents { readonly sidenav: OmnichannelSidenav; + readonly editCtxBar: Locator; + + readonly infoCtxBar: Locator; + constructor(page: Page) { this.page = page; this.sidenav = new OmnichannelSidenav(page); + this.editCtxBar = page.locator('[data-qa-id="agent-edit-contextual-bar"]'); + this.infoCtxBar = page.locator('[data-qa-id="agent-info-contextual-bar"]'); } get inputUsername(): Locator { - return this.page.locator('input').first(); + return this.page.locator('[data-qa-id="UserAutoComplete"]'); } get inputSearch(): Locator { @@ -25,26 +31,67 @@ export class OmnichannelAgents { } get firstRowInTable() { - return this.page.locator('[data-qa="GenericTableAgentInfoBody"] .rcx-table__row--action .rcx-table__cell:first-child'); + return this.page.locator('[data-qa-id="agents-table"] tr:first-child td:first-child'); } get btnDeletefirstRowInTable() { - return this.page.locator('button[title="Remove"]'); + return this.page.locator('[data-qa-id="agents-table"] tr:first-child').locator('role=button[name="Remove"]'); + } + + get modalRemoveAgent(): Locator { + return this.page.locator('[data-qa-id="remove-agent-modal"]'); } get btnModalRemove(): Locator { - return this.page.locator('#modal-root dialog .rcx-modal__inner .rcx-modal__footer .rcx-button--danger'); + return this.modalRemoveAgent.locator('role=button[name="Delete"]'); } get btnEdit(): Locator { - return this.page.locator('[data-qa="AgentInfoAction-Edit"]'); + return this.infoCtxBar.locator('[data-qa="agent-info-action-edit"]'); } - get btnStatus(): Locator { - return this.page.locator('[data-qa="AgentEditTextInput-Status"]'); + get btnRemove(): Locator { + return this.infoCtxBar.locator('role=button[name="Remove"]'); } get btnSave(): Locator { - return this.page.locator('[data-qa="AgentEditButtonSave"]'); + return this.editCtxBar.locator('[data-qa-id="agent-edit-save"]'); + } + + get inputMaxChats(): Locator { + return this.editCtxBar.locator('input[name="maxNumberSimultaneousChat"]'); + } + + get inputStatus(): Locator { + return this.page.locator('[data-qa-id="agent-edit-status"]'); + } + + get inputDepartment(): Locator { + return this.editCtxBar.locator('[data-qa-id="agent-edit-departments"]'); + } + + async selectDepartment(name: string) { + await this.inputDepartment.click(); + await this.inputDepartment.type(name); + await this.page.locator(`.rcx-option__content:has-text("${name}")`).click(); + } + + async selectStatus(status: string) { + await this.inputStatus.click(); + await this.page.locator(`.rcx-option__content:has-text("${status}")`).click(); + } + + async selectUsername(username: string) { + await this.inputUsername.fill(''); + await this.inputUsername.type(username); + await this.page.locator(`role=option[name="${username}"]`).click(); + } + + findRowByUsername(username: string) { + return this.page.locator(`[data-qa-id="${username}"]`); + } + + findRowByName(name: string) { + return this.page.locator('tr', { has: this.page.locator(`td >> text="${name}"`) }); } } diff --git a/apps/meteor/tests/e2e/page-objects/omnichannel-canned-responses.ts b/apps/meteor/tests/e2e/page-objects/omnichannel-canned-responses.ts new file mode 100644 index 00000000000..0537090cc0c --- /dev/null +++ b/apps/meteor/tests/e2e/page-objects/omnichannel-canned-responses.ts @@ -0,0 +1,13 @@ +import type { Locator } from '@playwright/test'; + +import { OmnichannelAdministration } from './omnichannel-administration'; + +export class OmnichannelCannedResponses extends OmnichannelAdministration { + get radioPublic(): Locator { + return this.page.locator('[data-qa-id="canned-response-public-radio"]').first(); + } + + get btnNew(): Locator { + return this.page.locator('role=button[name="Create canned response"]').first(); + } +} diff --git a/apps/meteor/tests/e2e/page-objects/omnichannel-current-chats.ts b/apps/meteor/tests/e2e/page-objects/omnichannel-current-chats.ts index b353285897a..46773e0ca43 100644 --- a/apps/meteor/tests/e2e/page-objects/omnichannel-current-chats.ts +++ b/apps/meteor/tests/e2e/page-objects/omnichannel-current-chats.ts @@ -1,110 +1,112 @@ import type { Locator, Page } from '@playwright/test'; -import { OmnichannelSidenav } from './fragments'; +import { HomeOmnichannelContent } from './fragments'; +import { OmnichannelAdministration } from './omnichannel-administration'; -export class OmnichannelCurrentChats { - private readonly page: Page; - - readonly sidenav: OmnichannelSidenav; +export class OmnichannelCurrentChats extends OmnichannelAdministration { + readonly content: HomeOmnichannelContent; constructor(page: Page) { - this.page = page; - this.sidenav = new OmnichannelSidenav(page); - } - - get btnToastClose(): Locator { - return this.page.locator('.rcx-toastbar').locator('button'); - } - - get currentChatsLink(): Locator { - return this.page.locator('a[href="omnichannel/current"]'); + super(page); + this.content = new HomeOmnichannelContent(page); } - get guestField(): Locator { + get inputGuest(): Locator { return this.page.locator('[data-qa="current-chats-guest"]'); } - get servedByField(): Locator { - return this.page.locator('[data-qa="autocomplete-agent"]'); + get inputServedBy(): Locator { + return this.page.locator('[data-qa="autocomplete-agent"] input'); } - get statusField(): Locator { + get inputStatus(): Locator { return this.page.locator('[data-qa="current-chats-status"]'); } - get fromField(): Locator { + get inputFrom(): Locator { return this.page.locator('[data-qa="current-chats-from"]'); } - get toField(): Locator { + get inputTo(): Locator { return this.page.locator('[data-qa="current-chats-to"]'); } - get departmentField(): Locator { - return this.page.locator('[data-qa="autocomplete-department"]'); + get inputDepartment(): Locator { + return this.page.locator('[data-qa="autocomplete-department"] input'); } - get tagsField(): Locator { - return this.page.locator('[data-qa="current-chats-tags"]'); + get inputTags(): Locator { + return this.page.locator('[data-qa="current-chats-tags"] [role="listbox"]'); } - get formOptions(): Locator { + get btnFilterOptions(): Locator { return this.page.locator('[data-qa="current-chats-options"]'); } - get clearFiltersOption(): Locator { + get optionClearFilter(): Locator { return this.page.locator('[data-qa="current-chats-options-clearFilters"]'); } - get removeAllClosedOption(): Locator { + get optionRemoveAllClosed(): Locator { return this.page.locator('[data-qa="current-chats-options-removeAllClosed"]'); } - get customFieldsOption(): Locator { - return this.page.locator('[data-qa="current-chats-options-customFields"]'); + get modalConfirmRemove(): Locator { + return this.page.locator('[data-qa-id="current-chats-modal-remove"]'); + } + + get modalConfirmRemoveAllClosed(): Locator { + return this.page.locator('[data-qa-id="current-chats-modal-remove-all-closed"]'); } - get table(): Locator { - return this.page.locator('.rcx-table'); + get btnConfirmRemove(): Locator { + return this.modalConfirmRemove.locator('role=button[name="Delete"]'); } - get tableHeaderName(): Locator { - return this.page.locator('[data-qa="current-chats-header-name"]'); + get btnConfirmRemoveAllClosed(): Locator { + return this.modalConfirmRemoveAllClosed.locator('role=button[name="Delete"]'); } - get tableHeaderDepartment(): Locator { - return this.page.locator('[data-qa="current-chats-header-department"]'); + get optionCustomFields(): Locator { + return this.page.locator('[data-qa="current-chats-options-customFields"]'); } - get tableHeaderServedBy(): Locator { - return this.page.locator('[data-qa="current-chats-header-servedBy"]'); + async selectServedBy(option: string) { + await this.inputServedBy.click(); + await this.inputServedBy.fill(option); + await this.page.locator(`[role='option'][value='${option}']`).click(); } - get tableHeaderStartedAt(): Locator { - return this.page.locator('[data-qa="current-chats-header-startedAt"]'); + async addTag(option: string) { + await this.inputTags.click(); + await this.page.locator(`[role='option'][value='${option}']`).click(); + await this.inputTags.click(); } - get tableHeaderLastMessage(): Locator { - return this.page.locator('[data-qa="current-chats-header-lastMessage"]'); + async removeTag(option: string) { + await this.page.locator(`role=option[name='${option}']`).click(); } - get tableHeaderStatus(): Locator { - return this.page.locator('[data-qa="current-chats-header-status"]'); + async selectDepartment(option: string) { + await this.inputDepartment.click(); + await this.inputDepartment.fill(option); + await this.page.locator(`role=option[name='${option}']`).click(); } - get tableHeaderRemove(): Locator { - return this.page.locator('[data-qa="current-chats-header-remove"]'); + async selectStatus(option: string) { + await this.inputStatus.click(); + await this.page.locator(`[role='option'][data-key='${option}']`).click(); } - get tableHeader(): Locator { - return this.page.locator('[data-qa=""]'); + btnRemoveByName(name: string): Locator { + return this.findRowByName(name).locator('role=button[name="Remove"]'); } - async openChat(name: string): Promise { - return this.page.locator(`table >> text="${name}"`).click(); + findRowByName(name: string) { + return this.page.locator(`tr[data-qa-id="${name}"]`); } - async doOpenOptions(): Promise { - await this.formOptions.click(); + findRowByServer(name: string) { + return this.page.locator('tr', { has: this.page.locator(`[data-qa="current-chats-cell-servedBy"] >> text=${name}`) }); } } diff --git a/apps/meteor/tests/e2e/page-objects/omnichannel-departments.ts b/apps/meteor/tests/e2e/page-objects/omnichannel-departments.ts index a731ae01eac..c1012442f11 100644 --- a/apps/meteor/tests/e2e/page-objects/omnichannel-departments.ts +++ b/apps/meteor/tests/e2e/page-objects/omnichannel-departments.ts @@ -110,11 +110,19 @@ export class OmnichannelDepartments { } get inputModalConfirmDelete() { - return this.page.locator('#modal-root .rcx-modal input'); + return this.modalConfirmDelete.locator('input[name="confirmDepartmentName"]'); + } + + get modalConfirmDelete() { + return this.page.locator('[data-qa-id="delete-department-modal"]'); } get btnModalConfirmDelete() { - return this.page.locator('#modal-root .rcx-modal .rcx-modal__footer .rcx-button--danger'); + return this.modalConfirmDelete.locator('role=button[name="Delete"]'); + } + + get btnModalCancelDelete() { + return this.modalConfirmDelete.locator('role=button[name="Cancel"]'); } get upgradeDepartmentsModal() { diff --git a/apps/meteor/tests/e2e/page-objects/omnichannel-monitors.ts b/apps/meteor/tests/e2e/page-objects/omnichannel-monitors.ts new file mode 100644 index 00000000000..b72594024aa --- /dev/null +++ b/apps/meteor/tests/e2e/page-objects/omnichannel-monitors.ts @@ -0,0 +1,38 @@ +import type { Locator } from '@playwright/test'; + +import { OmnichannelAdministration } from './omnichannel-administration'; + +export class OmnichannelMonitors extends OmnichannelAdministration { + get modalConfirmRemove(): Locator { + return this.page.locator('[data-qa-id="manage-monitors-confirm-remove"]'); + } + + get btnConfirmRemove(): Locator { + return this.modalConfirmRemove.locator('role=button[name="Delete"]'); + } + + get btnAddMonitor(): Locator { + return this.page.locator('role=button[name="Add monitor"]'); + } + + get inputMonitor(): Locator { + return this.page.locator('input[name="monitor"]'); + } + + get inputSearch(): Locator { + return this.page.locator('input[placeholder="Search"]'); + } + + findRowByName(name: string): Locator { + return this.page.locator(`tr[data-qa-id="${name}"]`); + } + + btnRemoveByName(name: string): Locator { + return this.findRowByName(name).locator('role=button[name="Remove"]'); + } + + async selectMonitor(name: string) { + await this.inputMonitor.fill(name); + await this.page.locator(`li[role="option"]`, { has: this.page.locator(`[data-username='${name}']`) }).click(); + } +} diff --git a/apps/meteor/tests/e2e/page-objects/omnichannel-transfer-chat-modal.ts b/apps/meteor/tests/e2e/page-objects/omnichannel-transfer-chat-modal.ts new file mode 100644 index 00000000000..8bb44c46638 --- /dev/null +++ b/apps/meteor/tests/e2e/page-objects/omnichannel-transfer-chat-modal.ts @@ -0,0 +1,44 @@ +import type { Locator, Page } from '@playwright/test'; + +export class OmnichannelTransferChatModal { + private readonly page: Page; + + private readonly dialog: Locator; + + constructor(page: Page) { + this.page = page; + this.dialog = page.locator('[data-qa-id="forward-chat-modal"]'); + } + + get inputComment(): Locator { + return this.dialog.locator('textarea[name="comment"]'); + } + + get inputFowardDepartment(): Locator { + return this.dialog.locator('[data-qa-id="forward-to-department"] input'); + } + + get inputFowardUser(): Locator { + return this.dialog.locator('[data-qa="autocomplete-agent"] input'); + } + + get btnForward(): Locator { + return this.dialog.locator('role=button[name="Forward"]'); + } + + get btnCancel(): Locator { + return this.dialog.locator('role=button[name="Cancel"]'); + } + + async selectDepartment(name: string) { + await this.inputFowardDepartment.click(); + await this.inputFowardDepartment.fill(name); + await this.page.locator(`li[role="option"][title="${name}"]`).click(); + } + + async selectUser(name: string, id?: string) { + await this.inputFowardUser.click(); + await this.inputFowardUser.fill(name); + await this.page.locator(`li[role="option"][value="${id || name}"]`).click(); + } +} diff --git a/apps/meteor/tests/e2e/page-objects/omnichannel-units.ts b/apps/meteor/tests/e2e/page-objects/omnichannel-units.ts new file mode 100644 index 00000000000..5aa41afe449 --- /dev/null +++ b/apps/meteor/tests/e2e/page-objects/omnichannel-units.ts @@ -0,0 +1,99 @@ +import type { Locator } from '@playwright/test'; + +import { OmnichannelAdministration } from './omnichannel-administration'; + +export class OmnichannelUnits extends OmnichannelAdministration { + get inputSearch() { + return this.page.locator('[placeholder="Search"]'); + } + + async search(text: string) { + await this.inputSearch.fill(text); + } + + findRowByName(name: string) { + return this.page.locator(`tr[data-qa-id="${name}"]`); + } + + btnRemoveByName(name: string) { + return this.findRowByName(name).locator('role=button[name="remove"]'); + } + + get inputName() { + return this.page.locator('[name="name"]'); + } + + get inputDepartments() { + return this.page.locator('[name="departments"]').locator('input[placeholder="Select an option"]'); + } + + get inputMonitors() { + return this.page.locator('[name="monitors"]'); + } + + get inputVisibility(): Locator { + return this.page.locator('button', { has: this.page.locator('select[name="visibility"]') }); + } + + private selectOption(name: string) { + return this.page.locator(`[role=option][value="${name}"]`); + } + + async selectDepartment({ name, _id }: { name: string; _id: string }) { + await this.inputDepartments.click(); + await this.inputDepartments.fill(name); + await this.selectOption(_id).click(); + await this.contextualBar.click({ position: { x: 0, y: 0 } }); + } + + async selectMonitor(option: string) { + await this.inputMonitors.click(); + await this.selectOption(option).click(); + await this.contextualBar.click({ position: { x: 0, y: 0 } }); + } + + async selectVisibility(option: string) { + await this.inputVisibility.click(); + await this.page.locator(`li.rcx-option[data-key="${option}"]`).click(); + } + + get btnCreateUnit() { + return this.page.locator('header').locator('role=button[name="Create unit"]'); + } + + get btnCreateUnitEmptyState() { + return this.page.locator('.rcx-states').locator('role=button[name="Create unit"]'); + } + + get contextualBar() { + return this.page.locator('div[data-qa-id="units-contextual-bar"]'); + } + + get btnSave() { + return this.contextualBar.locator('role=button[name="Save"]'); + } + + get btnCancel() { + return this.contextualBar.locator('role=button[name="Cancel"]'); + } + + get btnDelete() { + return this.contextualBar.locator('role=button[name="Delete"]'); + } + + btnDeleteByName(name: string) { + return this.page.locator(`button[data-qa-id="remove-unit-${name}"]`); + } + + get confirmDeleteModal() { + return this.page.locator('dialog[data-qa-id="units-confirm-delete-modal"]'); + } + + get btnCancelDeleteModal() { + return this.confirmDeleteModal.locator('role=button[name="Cancel"]'); + } + + get btnConfirmDeleteModal() { + return this.confirmDeleteModal.locator('role=button[name="Delete"]'); + } +} diff --git a/apps/meteor/tests/e2e/utils/omnichannel/agents.ts b/apps/meteor/tests/e2e/utils/omnichannel/agents.ts new file mode 100644 index 00000000000..43a4fb86f14 --- /dev/null +++ b/apps/meteor/tests/e2e/utils/omnichannel/agents.ts @@ -0,0 +1,30 @@ +import { BaseTest } from '../test'; + +export const makeAgentAvailable = async (api: BaseTest['api'], agentId: string) => { + await api.post('/users.setStatus', { + userId: agentId, + message: '', + status: 'online', + }); + + return api.post('/livechat/agent.status', { + agentId, + status: 'available', + }); +}; + +export const createAgent = async (api: BaseTest['api'], username: string) => { + const response = await api.post('/livechat/users/agent', { username }); + + if (response.status() !== 200) { + throw new Error(`Failed to create agent [http status: ${response.status()}]`); + } + + const { user: agent } = await response.json(); + + return { + response, + data: agent, + delete: async () => api.delete(`/livechat/users/agent/${agent._id}`), + }; +}; diff --git a/apps/meteor/tests/e2e/utils/omnichannel/departments.ts b/apps/meteor/tests/e2e/utils/omnichannel/departments.ts new file mode 100644 index 00000000000..2b4fefd666c --- /dev/null +++ b/apps/meteor/tests/e2e/utils/omnichannel/departments.ts @@ -0,0 +1,57 @@ +import { faker } from '@faker-js/faker'; +import { ILivechatDepartment } from '@rocket.chat/core-typings'; + +import { BaseTest } from '../test'; + +type CreateDepartmentParams = { name?: string; maxNumberSimultaneousChat?: number }; + +export const createDepartment = async (api: BaseTest['api'], { name = '', maxNumberSimultaneousChat }: CreateDepartmentParams = {}) => { + const response = await api.post('/livechat/department', { + department: { + name: name || faker.string.uuid(), + enabled: true, + description: '', + showOnRegistration: false, + showOnOfflineForm: false, + requestTagBeforeClosingChat: false, + email: faker.internet.email(), + chatClosingTags: [], + offlineMessageChannelName: '', + abandonedRoomsCloseCustomMessage: '', + waitingQueueMessage: '', + departmentsAllowedToForward: [], + fallbackForwardDepartment: '', + maxNumberSimultaneousChat, + }, + }); + + if (response.status() !== 200) { + throw Error(`Unable to create department [http status: ${response.status()}]`); + } + + const { department } = await response.json(); + + return { + response, + data: department, + delete: async () => api.delete(`/livechat/department/${department._id}`), + }; +}; + +export const addAgentToDepartment = async ( + api: BaseTest['api'], + { department, agentId, username }: { department: ILivechatDepartment; agentId: string; username?: string }, +) => + api.post(`/livechat/department/${department._id}/agents`, { + remove: [], + upsert: [ + { + agentId, + username: username || agentId, + count: 0, + order: 0, + }, + ], + }); + +export const deleteDepartment = async (api: BaseTest['api'], { id }: { id: string }) => api.delete(`/livechat/department/${id}`); diff --git a/apps/meteor/tests/e2e/utils/omnichannel/managers.ts b/apps/meteor/tests/e2e/utils/omnichannel/managers.ts new file mode 100644 index 00000000000..3fb5f04dcc9 --- /dev/null +++ b/apps/meteor/tests/e2e/utils/omnichannel/managers.ts @@ -0,0 +1,19 @@ +import { BaseTest } from '../test'; + +export const createManager = async (api: BaseTest['api'], username: string) => { + const response = await api.post('/livechat/users/manager', { + username, + }); + + if (response.status() !== 200) { + throw Error(`Unable to create manager [http status: ${response.status()}]`); + } + + const { user: manager } = await response.json(); + + return { + response, + data: manager, + delete: async () => api.delete(`/livechat/users/manager/${manager._id}`), + }; +}; diff --git a/apps/meteor/tests/e2e/utils/omnichannel/monitors.ts b/apps/meteor/tests/e2e/utils/omnichannel/monitors.ts new file mode 100644 index 00000000000..308e6f4460a --- /dev/null +++ b/apps/meteor/tests/e2e/utils/omnichannel/monitors.ts @@ -0,0 +1,30 @@ +import { BaseTest } from '../test'; +import { parseMeteorResponse } from './utils'; + +const removeMonitor = async (api: BaseTest['api'], id: string) => + api.post('/method.call/livechat:removeMonitor', { + message: JSON.stringify({ msg: 'method', id: '33', method: 'livechat:removeMonitor', params: [id] }), + }); + +export const createMonitor = async (api: BaseTest['api'], id: string) => { + const response = await api.post('/method.call/livechat:addMonitor', { + message: JSON.stringify({ + msg: 'method', + id: '17', + method: 'livechat:addMonitor', + params: [id], + }), + }); + + if (response.status() !== 200) { + throw new Error(`Failed to create monitor [http status: ${response.status()}]`); + } + + const monitor = await parseMeteorResponse<{ _id: string; username: string }>(response); + + return { + response, + data: monitor, + delete: async () => removeMonitor(api, monitor._id), + }; +}; diff --git a/apps/meteor/tests/e2e/utils/omnichannel/rooms.ts b/apps/meteor/tests/e2e/utils/omnichannel/rooms.ts index bce8c7a4478..c07c5376130 100644 --- a/apps/meteor/tests/e2e/utils/omnichannel/rooms.ts +++ b/apps/meteor/tests/e2e/utils/omnichannel/rooms.ts @@ -4,9 +4,11 @@ import { BaseTest } from '../test'; type UpdateRoomParams = { roomId: string; visitorId: string; tags: string[] }; +type CloseRoomParams = { roomId: string; visitorToken: string }; + type CreateRoomParams = { tags?: string[]; visitorToken: string; agentId?: string }; -type CreateVisitorParams = { token: string; department?: string; name: string; email: string }; +type CreateVisitorParams = { token: string; departmentId?: string; name?: string; email?: string }; type CreateConversationParams = { visitorName?: string; visitorToken?: string; agentId?: string; departmentId?: string }; @@ -19,12 +21,21 @@ export const updateRoom = async (api: BaseTest['api'], { roomId, visitorId, tags throw Error('Unable to update room info, missing visitor id'); } - return api.post('/livechat/room.saveInfo', { + const response = await api.post('/livechat/room.saveInfo', { guestData: { _id: visitorId }, roomData: { _id: roomId, tags }, }); + + if (response.status() !== 200) { + throw Error(`Unable to update room info [http status: ${response.status()}]`); + } + + return response; }; +export const closeRoom = async (api: BaseTest['api'], { roomId, visitorToken }: CloseRoomParams) => + api.post('/livechat/room.close', { rid: roomId, token: visitorToken }); + export const createRoom = async (api: BaseTest['api'], { visitorToken, agentId }: CreateRoomParams) => { const response = await api.get('/livechat/room', { token: visitorToken, @@ -32,69 +43,86 @@ export const createRoom = async (api: BaseTest['api'], { visitorToken, agentId } }); if (response.status() !== 200) { - throw Error(`Unable to create room [http status: ${response.status()}]`); + throw Error(`Unable to create room [http status: ${response.status()}]`, { cause: await response.json() }); } - const data = await response.json(); + const { room } = await response.json(); return { response, - data: data.room, - delete: async () => { - await api.post('/livechat/room.close', { rid: data.room._id, token: visitorToken }); - await api.post('/method.call/livechat:removeRoom', { + data: room, + async delete() { + await closeRoom(api, { roomId: room._id, visitorToken }); + return api.post('/method.call/livechat:removeRoom', { message: JSON.stringify({ msg: 'method', id: '16', method: 'livechat:removeRoom', - params: [data.room._id], + params: [room._id], }), }); }, }; }; -export const createVisitor = async (api: BaseTest['api'], params: CreateVisitorParams) => - api.post('/livechat/visitor', { visitor: params }); +export const createVisitor = async (api: BaseTest['api'], { name, token, departmentId }: CreateVisitorParams) => { + const response = await api.post('/livechat/visitor', { + visitor: { + name: name || faker.person.fullName(), + email: faker.internet.email(), + token, + ...(departmentId && { department: departmentId }), + }, + }); + + if (response.status() !== 200) { + throw Error(`Unable to create visitor [http status: ${response.status()}]`); + } + + const { visitor } = await response.json(); + + return { + response, + data: visitor, + delete: async () => api.delete(`/livechat/visitor/${token}`), + }; +}; export const sendMessageToRoom = async ( api: BaseTest['api'], { visitorToken, roomId, message }: { visitorToken: string; roomId: string; message?: string }, -) => - api.post(`/livechat/message`, { +) => { + const response = await api.post(`/livechat/message`, { token: visitorToken, rid: roomId, msg: message || faker.lorem.sentence(), }); + if (response.status() !== 200) { + throw Error(`Unable to send message to room [http status: ${response.status()}]`); + } + + return response; +}; + export const createConversation = async ( api: BaseTest['api'], - { visitorName, visitorToken, agentId, departmentId }: CreateConversationParams, + { visitorName, visitorToken, agentId, departmentId }: CreateConversationParams = {}, ) => { const token = visitorToken || faker.string.uuid(); - const visitorRes = await createVisitor(api, { - name: visitorName || faker.person.firstName(), - email: faker.internet.email(), - token, - ...(departmentId && { department: departmentId }), - }); - if (visitorRes.status() !== 200) { - throw Error(`Unable to create visitor [http status: ${visitorRes.status()}]`); - } - - const { data: room } = await createRoom(api, { visitorToken: token, agentId }); - - const messageRes = await sendMessageToRoom(api, { visitorToken: token, roomId: room._id }); - - if (messageRes.status() !== 200) { - throw Error(`Unable to send message to room [http status: ${messageRes.status()}]`); - } - - const { visitor } = await visitorRes.json(); + const { data: visitor, delete: deleteVisitor } = await createVisitor(api, { token, name: visitorName, departmentId }); + const { data: room, delete: deleteRoom } = await createRoom(api, { visitorToken: token, agentId }); + await sendMessageToRoom(api, { visitorToken: token, roomId: room._id }); return { - room, - visitor, + data: { + room, + visitor, + }, + delete: async () => { + await deleteRoom(); + await deleteVisitor(); + }, }; }; diff --git a/apps/meteor/tests/e2e/utils/omnichannel/tags.ts b/apps/meteor/tests/e2e/utils/omnichannel/tags.ts new file mode 100644 index 00000000000..2df9e57e1c7 --- /dev/null +++ b/apps/meteor/tests/e2e/utils/omnichannel/tags.ts @@ -0,0 +1,6 @@ +import { BaseTest } from '../test'; + +export const createTag = async (api: BaseTest['api'], name: string) => + api.post('/method.call/livechat:saveTag', { + message: JSON.stringify({ msg: 'method', id: '33', method: 'livechat:saveTag', params: [null, { name, description: '' }, []] }), + }); diff --git a/apps/meteor/tests/e2e/utils/omnichannel/units.ts b/apps/meteor/tests/e2e/utils/omnichannel/units.ts new file mode 100644 index 00000000000..fa872a37eae --- /dev/null +++ b/apps/meteor/tests/e2e/utils/omnichannel/units.ts @@ -0,0 +1,55 @@ +import { faker } from '@faker-js/faker'; +import { IOmnichannelBusinessUnit } from '@rocket.chat/core-typings'; + +import { BaseTest } from '../test'; +import { parseMeteorResponse } from './utils'; + +type CreateUnitParams = { + id?: string | null; + name?: string; + visibility?: 'public' | 'private'; + monitors?: { monitorId: string; username: string }[]; + departments?: { departmentId: string }[]; +}; + +const removeUnit = async (api: BaseTest['api'], id: string) => + api.post('/method.call/omnichannel:removeUnit', { + message: JSON.stringify({ msg: 'method', id: '35', method: 'livechat:removeUnit', params: [id] }), + }); + +export const createOrUpdateUnit = async ( + api: BaseTest['api'], + { id = null, name, visibility, monitors, departments }: CreateUnitParams = {}, +) => { + const response = await api.post('/method.call/livechat:saveUnit', { + message: JSON.stringify({ + msg: 'method', + id: '34', + method: 'livechat:saveUnit', + params: [id, { name: name || faker.commerce.department(), visibility: visibility || 'public' }, monitors, departments], + }), + }); + + const unit = await parseMeteorResponse(response); + + return { + response, + data: unit, + delete: async () => removeUnit(api, unit?._id), + }; +}; + +export const fetchUnitMonitors = async (api: BaseTest['api'], unitId: string) => { + const response = await api.get(`/livechat/units/${unitId}/monitors`); + + if (response.status() !== 200) { + throw new Error(`Failed to fetch unit monitors [http status: ${response.status()}]`); + } + + const { monitors } = await response.json(); + + return { + response, + data: monitors, + }; +}; diff --git a/apps/meteor/tests/e2e/utils/omnichannel/utils.ts b/apps/meteor/tests/e2e/utils/omnichannel/utils.ts new file mode 100644 index 00000000000..edecd3ea4ce --- /dev/null +++ b/apps/meteor/tests/e2e/utils/omnichannel/utils.ts @@ -0,0 +1,18 @@ +import { APIResponse } from '@playwright/test'; +import { Serialized } from '@rocket.chat/core-typings'; + +export const parseMeteorResponse = async (response: APIResponse): Promise> => { + const { message, success } = await response.json(); + + if (!success) { + throw new Error(message); + } + + const { result, error } = JSON.parse(message); + + if (error) { + throw new Error(JSON.stringify(error)); + } + + return result; +}; -- GitLab From 334a723e5ba553f7893f8ef36c0c2eaa76f88172 Mon Sep 17 00:00:00 2001 From: Pierre Lehnen <55164754+pierre-lehnen-rc@users.noreply.github.com> Date: Thu, 14 Dec 2023 17:04:04 -0300 Subject: [PATCH 031/329] fix: mobile users receives two push notifications when a direct call is started (#31164) --- .changeset/spotty-suns-grin.md | 5 +++++ .../server/functions/notifications/mobile.js | 14 ++++++++++++++ .../server/lib/sendNotificationsOnMessage.js | 18 ++++++++++++------ .../isRoomCompatibleWithVideoConfRinging.ts | 4 ++++ .../services/video-conference/service.ts | 3 ++- 5 files changed, 37 insertions(+), 7 deletions(-) create mode 100644 .changeset/spotty-suns-grin.md create mode 100644 apps/meteor/server/lib/isRoomCompatibleWithVideoConfRinging.ts diff --git a/.changeset/spotty-suns-grin.md b/.changeset/spotty-suns-grin.md new file mode 100644 index 00000000000..8045ebbd1b4 --- /dev/null +++ b/.changeset/spotty-suns-grin.md @@ -0,0 +1,5 @@ +--- +'@rocket.chat/meteor': patch +--- + +Improved the experience of receiving conference calls on the mobile app by disabling the push notification for the "new call" message if a push is already being sent to trigger the phone's ringing tone. diff --git a/apps/meteor/app/lib/server/functions/notifications/mobile.js b/apps/meteor/app/lib/server/functions/notifications/mobile.js index b4139bea6f1..a0aa3fa2402 100644 --- a/apps/meteor/app/lib/server/functions/notifications/mobile.js +++ b/apps/meteor/app/lib/server/functions/notifications/mobile.js @@ -1,6 +1,7 @@ import { Subscriptions } from '@rocket.chat/models'; import { i18n } from '../../../../../server/lib/i18n'; +import { isRoomCompatibleWithVideoConfRinging } from '../../../../../server/lib/isRoomCompatibleWithVideoConfRinging'; import { roomCoordinator } from '../../../../../server/lib/rooms/roomCoordinator'; import { settings } from '../../../../settings/server'; @@ -73,6 +74,9 @@ export function shouldNotifyMobile({ hasReplyToThread, roomType, isThread, + isVideoConf, + userPreferences, + roomUids, }) { if (settings.get('Push_enable') !== true) { return false; @@ -86,6 +90,16 @@ export function shouldNotifyMobile({ return false; } + // If the user is going to receive a ringing push notification, do not send another push for the message generated by that videoconference + if ( + isVideoConf && + settings.get('VideoConf_Mobile_Ringing') && + isRoomCompatibleWithVideoConfRinging(roomType, roomUids) && + (userPreferences?.enableMobileRinging ?? settings.get(`Accounts_Default_User_Preferences_enableMobileRinging`)) + ) { + return false; + } + if (!mobilePushNotifications) { if (settings.get('Accounts_Default_User_Preferences_pushNotifications') === 'all' && (!isThread || hasReplyToThread)) { return true; diff --git a/apps/meteor/app/lib/server/lib/sendNotificationsOnMessage.js b/apps/meteor/app/lib/server/lib/sendNotificationsOnMessage.js index ce262e4e675..4ed882cfe87 100644 --- a/apps/meteor/app/lib/server/lib/sendNotificationsOnMessage.js +++ b/apps/meteor/app/lib/server/lib/sendNotificationsOnMessage.js @@ -48,12 +48,13 @@ export const sendNotification = async ({ subscription.receiver = [ await Users.findOneById(subscription.u._id, { projection: { - active: 1, - emails: 1, - language: 1, - status: 1, - statusConnection: 1, - username: 1, + 'active': 1, + 'emails': 1, + 'language': 1, + 'status': 1, + 'statusConnection': 1, + 'username': 1, + 'settings.preferences.enableMobileRinging': 1, }, }), ]; @@ -68,6 +69,7 @@ export const sendNotification = async ({ } const isThread = !!message.tmid && !message.tshow; + const isVideoConf = message.t === 'videoconf'; notificationMessage = await parseMessageTextPerUser(notificationMessage, message, receiver); @@ -112,6 +114,9 @@ export const sendNotification = async ({ hasReplyToThread, roomType, isThread, + isVideoConf, + userPreferences: receiver.settings?.preferences, + roomUids: room.uids, }) ) { queueItems.push({ @@ -189,6 +194,7 @@ const project = { 'receiver.status': 1, 'receiver.statusConnection': 1, 'receiver.username': 1, + 'receiver.settings.preferences.enableMobileRinging': 1, }, }; diff --git a/apps/meteor/server/lib/isRoomCompatibleWithVideoConfRinging.ts b/apps/meteor/server/lib/isRoomCompatibleWithVideoConfRinging.ts new file mode 100644 index 00000000000..3bd4f814b8d --- /dev/null +++ b/apps/meteor/server/lib/isRoomCompatibleWithVideoConfRinging.ts @@ -0,0 +1,4 @@ +import type { IRoom } from '@rocket.chat/core-typings'; + +export const isRoomCompatibleWithVideoConfRinging = (roomType: IRoom['t'], roomUids: IRoom['uids']): boolean => + Boolean(roomType === 'd' && roomUids && roomUids.length <= 2); diff --git a/apps/meteor/server/services/video-conference/service.ts b/apps/meteor/server/services/video-conference/service.ts index 6bb5b36bf3d..e6de3e9e253 100644 --- a/apps/meteor/server/services/video-conference/service.ts +++ b/apps/meteor/server/services/video-conference/service.ts @@ -46,6 +46,7 @@ import { callbacks } from '../../../lib/callbacks'; import { availabilityErrors } from '../../../lib/videoConference/constants'; import { readSecondaryPreferred } from '../../database/readSecondaryPreferred'; import { i18n } from '../../lib/i18n'; +import { isRoomCompatibleWithVideoConfRinging } from '../../lib/isRoomCompatibleWithVideoConfRinging'; import { videoConfProviders } from '../../lib/videoConfProviders'; import { videoConfTypes } from '../../lib/videoConfTypes'; import { broadcastMessageSentEvent } from '../../modules/watchers/lib/messages'; @@ -74,7 +75,7 @@ export class VideoConfService extends ServiceClassInternal implements IVideoConf } if (type === 'direct') { - if (room.t !== 'd' || !room.uids || room.uids.length > 2) { + if (!isRoomCompatibleWithVideoConfRinging(room.t, room.uids)) { throw new Error('type-and-room-not-compatible'); } -- GitLab From b4e174deb58c955dd2d78ab63f0ed80829fce01e Mon Sep 17 00:00:00 2001 From: Douglas Fabris Date: Thu, 14 Dec 2023 17:10:22 -0300 Subject: [PATCH 032/329] chore: Replace `useForm` in favor of RHF on AppSettings (#31244) --- .../views/admin/settings/MemoizedSetting.tsx | 3 +- .../settings/inputs/AssetSettingInput.tsx | 11 +-- .../settings/inputs/BooleanSettingInput.tsx | 30 ++++--- .../settings/inputs/CodeSettingInput.tsx | 24 ++--- .../settings/inputs/ColorSettingInput.tsx | 22 ++--- .../settings/inputs/FontSettingInput.tsx | 22 ++--- .../settings/inputs/GenericSettingInput.tsx | 22 ++--- .../admin/settings/inputs/IntSettingInput.tsx | 22 ++--- .../settings/inputs/LanguageSettingInput.tsx | 22 ++--- .../settings/inputs/LookupSettingInput.tsx | 22 ++--- .../inputs/MultiSelectSettingInput.tsx | 48 +++++----- .../settings/inputs/PasswordSettingInput.tsx | 22 ++--- .../inputs/RelativeUrlSettingInput.tsx | 22 ++--- .../settings/inputs/RoomPickSettingInput.tsx | 22 ++--- .../settings/inputs/SelectSettingInput.tsx | 22 ++--- .../inputs/SelectTimezoneSettingInput.tsx | 22 ++--- .../settings/inputs/StringSettingInput.tsx | 22 ++--- .../AppDetailsPage/AppDetailsPage.tsx | 88 ++++++++++--------- .../tabs/AppSettings/AppSetting.tsx | 50 +++++------ .../tabs/AppSettings/AppSettings.tsx | 44 ++-------- .../tabs/AppSettings/AppSettingsAssembler.tsx | 24 ----- 21 files changed, 275 insertions(+), 311 deletions(-) delete mode 100644 apps/meteor/client/views/marketplace/AppDetailsPage/tabs/AppSettings/AppSettingsAssembler.tsx diff --git a/apps/meteor/client/views/admin/settings/MemoizedSetting.tsx b/apps/meteor/client/views/admin/settings/MemoizedSetting.tsx index b3a29b8a303..5534e4cef7b 100644 --- a/apps/meteor/client/views/admin/settings/MemoizedSetting.tsx +++ b/apps/meteor/client/views/admin/settings/MemoizedSetting.tsx @@ -59,6 +59,7 @@ type MemoizedSettingProps = { sectionChanged?: boolean; hasResetButton?: boolean; disabled?: boolean; + required?: boolean; showUpgradeButton?: ReactNode; actionText?: string; }; @@ -92,8 +93,8 @@ const MemoizedSetting = ({ editor={editor} onChangeValue={onChangeValue} onChangeEditor={onChangeEditor} - {...inputProps} disabled={disabled} + {...inputProps} /> {hint && type !== 'code' && {hint}} {callout && ( diff --git a/apps/meteor/client/views/admin/settings/inputs/AssetSettingInput.tsx b/apps/meteor/client/views/admin/settings/inputs/AssetSettingInput.tsx index f3122a23295..226215b97e5 100644 --- a/apps/meteor/client/views/admin/settings/inputs/AssetSettingInput.tsx +++ b/apps/meteor/client/views/admin/settings/inputs/AssetSettingInput.tsx @@ -1,4 +1,4 @@ -import { Button, FieldLabel, FieldRow, Icon } from '@rocket.chat/fuselage'; +import { Button, Field, FieldLabel, FieldRow, Icon } from '@rocket.chat/fuselage'; import { Random } from '@rocket.chat/random'; import { useToastMessageDispatch, useEndpoint, useTranslation, useUpload } from '@rocket.chat/ui-contexts'; import type { ChangeEventHandler, DragEvent, ReactElement, SyntheticEvent } from 'react'; @@ -11,10 +11,11 @@ type AssetSettingInputProps = { label: string; value?: { url: string }; asset?: any; + required?: boolean; fileConstraints?: { extensions: string[] }; }; -function AssetSettingInput({ _id, label, value, asset, fileConstraints }: AssetSettingInputProps): ReactElement { +function AssetSettingInput({ _id, label, value, asset, required, fileConstraints }: AssetSettingInputProps): ReactElement { const t = useTranslation(); const dispatchToastMessage = useToastMessageDispatch(); @@ -57,8 +58,8 @@ function AssetSettingInput({ _id, label, value, asset, fileConstraints }: AssetS }; return ( - <> - + + {label} @@ -94,7 +95,7 @@ function AssetSettingInput({ _id, label, value, asset, fileConstraints }: AssetS - + ); } diff --git a/apps/meteor/client/views/admin/settings/inputs/BooleanSettingInput.tsx b/apps/meteor/client/views/admin/settings/inputs/BooleanSettingInput.tsx index a08cefc5402..7dc45f6a539 100644 --- a/apps/meteor/client/views/admin/settings/inputs/BooleanSettingInput.tsx +++ b/apps/meteor/client/views/admin/settings/inputs/BooleanSettingInput.tsx @@ -1,4 +1,4 @@ -import { Box, FieldLabel, FieldRow, ToggleSwitch } from '@rocket.chat/fuselage'; +import { Box, Field, FieldLabel, FieldRow, ToggleSwitch } from '@rocket.chat/fuselage'; import type { ReactElement, SyntheticEvent } from 'react'; import React from 'react'; @@ -9,6 +9,7 @@ type BooleanSettingInputProps = { label: string; disabled?: boolean; readonly?: boolean; + required?: boolean; value: boolean; hasResetButton: boolean; onChangeValue: (value: boolean) => void; @@ -19,6 +20,7 @@ function BooleanSettingInput({ label, disabled, readonly, + required, value, hasResetButton, onChangeValue, @@ -30,15 +32,23 @@ function BooleanSettingInput({ }; return ( - - - {label} - - - {hasResetButton && } - - - + + + + {label} + + + {hasResetButton && } + + + + ); } diff --git a/apps/meteor/client/views/admin/settings/inputs/CodeSettingInput.tsx b/apps/meteor/client/views/admin/settings/inputs/CodeSettingInput.tsx index 85698b66e2b..0424e862bd0 100644 --- a/apps/meteor/client/views/admin/settings/inputs/CodeSettingInput.tsx +++ b/apps/meteor/client/views/admin/settings/inputs/CodeSettingInput.tsx @@ -1,4 +1,4 @@ -import { Box, FieldLabel, FieldHint, Flex } from '@rocket.chat/fuselage'; +import { FieldLabel, FieldHint, FieldRow, Field } from '@rocket.chat/fuselage'; import type { ReactElement } from 'react'; import React from 'react'; @@ -16,6 +16,7 @@ type CodeSettingInputProps = { readonly: boolean; autocomplete: boolean; disabled: boolean; + required?: boolean; hasResetButton: boolean; onChangeValue: (value: string) => void; onResetButtonClick: () => void; @@ -31,6 +32,7 @@ function CodeSettingInput({ readonly, autocomplete, disabled, + required, hasResetButton, onChangeValue, onResetButtonClick, @@ -40,16 +42,14 @@ function CodeSettingInput({ }; return ( - <> - - - - {label} - - {hasResetButton && } - - {hint && {hint}} - + + + + {label} + + {hasResetButton && } + + {hint && {hint}} - + ); } diff --git a/apps/meteor/client/views/admin/settings/inputs/ColorSettingInput.tsx b/apps/meteor/client/views/admin/settings/inputs/ColorSettingInput.tsx index 2b2e463346b..a4f61b81154 100644 --- a/apps/meteor/client/views/admin/settings/inputs/ColorSettingInput.tsx +++ b/apps/meteor/client/views/admin/settings/inputs/ColorSettingInput.tsx @@ -1,4 +1,4 @@ -import { Box, FieldLabel, FieldRow, FieldHint, Flex, InputBox, Margins, TextInput, Select } from '@rocket.chat/fuselage'; +import { FieldLabel, FieldRow, FieldHint, Flex, InputBox, Margins, TextInput, Select, Field } from '@rocket.chat/fuselage'; import type { TranslationKey } from '@rocket.chat/ui-contexts'; import { useTranslation } from '@rocket.chat/ui-contexts'; import type { ReactElement } from 'react'; @@ -16,6 +16,7 @@ type ColorSettingInputProps = { readonly?: boolean; autocomplete?: boolean; disabled?: boolean; + required?: boolean; hasResetButton?: boolean; onChangeValue?: (value: string) => void; onChangeEditor?: (value: string) => void; @@ -31,6 +32,7 @@ function ColorSettingInput({ readonly, autocomplete, disabled, + required, hasResetButton, onChangeValue, onChangeEditor, @@ -53,15 +55,13 @@ function ColorSettingInput({ ); return ( - <> - - - - {label} - - {hasResetButton && } - - + + + + {label} + + {hasResetButton && } + @@ -107,7 +107,7 @@ function ColorSettingInput({ Variable name: {_id.replace(/theme-color-/, '@')} - + ); } diff --git a/apps/meteor/client/views/admin/settings/inputs/FontSettingInput.tsx b/apps/meteor/client/views/admin/settings/inputs/FontSettingInput.tsx index 35b255b4e75..1c1c45913bc 100644 --- a/apps/meteor/client/views/admin/settings/inputs/FontSettingInput.tsx +++ b/apps/meteor/client/views/admin/settings/inputs/FontSettingInput.tsx @@ -1,4 +1,4 @@ -import { Box, FieldLabel, FieldRow, Flex, TextInput } from '@rocket.chat/fuselage'; +import { Field, FieldLabel, FieldRow, TextInput } from '@rocket.chat/fuselage'; import type { FormEventHandler, ReactElement } from 'react'; import React from 'react'; @@ -12,6 +12,7 @@ type FontSettingInputProps = { readonly?: boolean; autocomplete?: boolean; disabled?: boolean; + required?: boolean; hasResetButton?: boolean; onChangeValue?: (value: string) => void; onResetButtonClick?: () => void; @@ -24,6 +25,7 @@ function FontSettingInput({ readonly, autocomplete, disabled, + required, hasResetButton, onChangeValue, onResetButtonClick, @@ -33,15 +35,13 @@ function FontSettingInput({ }; return ( - <> - - - - {label} - - {hasResetButton && } - - + + + + {label} + + {hasResetButton && } + - + ); } diff --git a/apps/meteor/client/views/admin/settings/inputs/GenericSettingInput.tsx b/apps/meteor/client/views/admin/settings/inputs/GenericSettingInput.tsx index 32425a57c69..daabea2b1d0 100644 --- a/apps/meteor/client/views/admin/settings/inputs/GenericSettingInput.tsx +++ b/apps/meteor/client/views/admin/settings/inputs/GenericSettingInput.tsx @@ -1,4 +1,4 @@ -import { Box, FieldLabel, FieldRow, Flex, TextInput } from '@rocket.chat/fuselage'; +import { Field, FieldLabel, FieldRow, TextInput } from '@rocket.chat/fuselage'; import type { FormEventHandler, ReactElement } from 'react'; import React from 'react'; @@ -12,6 +12,7 @@ type GenericSettingInputProps = { readonly?: boolean; autocomplete?: boolean; disabled?: boolean; + required?: boolean; hasResetButton?: boolean; onChangeValue?: (value: string) => void; onResetButtonClick?: () => void; @@ -24,6 +25,7 @@ function GenericSettingInput({ readonly, autocomplete, disabled, + required, hasResetButton, onChangeValue, onResetButtonClick, @@ -33,15 +35,13 @@ function GenericSettingInput({ }; return ( - <> - - - - {label} - - {hasResetButton && } - - + + + + {label} + + {hasResetButton && } + - + ); } diff --git a/apps/meteor/client/views/admin/settings/inputs/IntSettingInput.tsx b/apps/meteor/client/views/admin/settings/inputs/IntSettingInput.tsx index cd5abd54f48..16100f12846 100644 --- a/apps/meteor/client/views/admin/settings/inputs/IntSettingInput.tsx +++ b/apps/meteor/client/views/admin/settings/inputs/IntSettingInput.tsx @@ -1,4 +1,4 @@ -import { Box, FieldLabel, FieldRow, Flex, InputBox } from '@rocket.chat/fuselage'; +import { Field, FieldLabel, FieldRow, InputBox } from '@rocket.chat/fuselage'; import type { FormEventHandler, ReactElement } from 'react'; import React from 'react'; @@ -12,6 +12,7 @@ type IntSettingInputProps = { readonly?: boolean; autocomplete?: boolean; disabled?: boolean; + required?: boolean; hasResetButton?: boolean; onChangeValue?: (value: string | number) => void; onResetButtonClick?: () => void; @@ -25,6 +26,7 @@ function IntSettingInput({ readonly, autocomplete, disabled, + required, onChangeValue, hasResetButton, onResetButtonClick, @@ -34,15 +36,13 @@ function IntSettingInput({ }; return ( - <> - - - - {label} - - {hasResetButton && } - - + + + + {label} + + {hasResetButton && } + - + ); } diff --git a/apps/meteor/client/views/admin/settings/inputs/LanguageSettingInput.tsx b/apps/meteor/client/views/admin/settings/inputs/LanguageSettingInput.tsx index 8bfe977aaf3..f91642a5eed 100644 --- a/apps/meteor/client/views/admin/settings/inputs/LanguageSettingInput.tsx +++ b/apps/meteor/client/views/admin/settings/inputs/LanguageSettingInput.tsx @@ -1,4 +1,4 @@ -import { Box, FieldLabel, FieldRow, Flex, Select } from '@rocket.chat/fuselage'; +import { Field, FieldLabel, FieldRow, Select } from '@rocket.chat/fuselage'; import { useLanguages } from '@rocket.chat/ui-contexts'; import type { ReactElement } from 'react'; import React from 'react'; @@ -13,6 +13,7 @@ type LanguageSettingInputProps = { readonly?: boolean; autocomplete?: boolean; disabled?: boolean; + required?: boolean; hasResetButton?: boolean; onChangeValue?: (value: string | number) => void; onResetButtonClick?: () => void; @@ -26,6 +27,7 @@ function LanguageSettingInput({ readonly, autocomplete, disabled, + required, hasResetButton, onChangeValue, onResetButtonClick, @@ -37,15 +39,13 @@ function LanguageSettingInput({ }; return ( - <> - - - - {label} - - {hasResetButton && } - - + + + + {label} + + {hasResetButton && } + [key, label])} /> - + ); } diff --git a/apps/meteor/client/views/admin/settings/inputs/MultiSelectSettingInput.tsx b/apps/meteor/client/views/admin/settings/inputs/MultiSelectSettingInput.tsx index ff2f0f7d181..c0d12ee401c 100644 --- a/apps/meteor/client/views/admin/settings/inputs/MultiSelectSettingInput.tsx +++ b/apps/meteor/client/views/admin/settings/inputs/MultiSelectSettingInput.tsx @@ -1,4 +1,4 @@ -import { FieldLabel, Flex, Box, MultiSelectFiltered, MultiSelect } from '@rocket.chat/fuselage'; +import { FieldLabel, MultiSelectFiltered, MultiSelect, Field, FieldRow } from '@rocket.chat/fuselage'; import type { TranslationKey } from '@rocket.chat/ui-contexts'; import { useTranslation } from '@rocket.chat/ui-contexts'; import type { ReactElement } from 'react'; @@ -16,6 +16,7 @@ type MultiSelectSettingInputProps = { readonly?: boolean; autocomplete?: boolean; disabled?: boolean; + required?: boolean; hasResetButton?: boolean; onChangeValue?: (value: string[]) => void; onResetButtonClick?: () => void; @@ -28,6 +29,7 @@ function MultiSelectSettingInput({ placeholder, readonly, disabled, + required, values = [], hasResetButton, onChangeValue, @@ -42,28 +44,28 @@ function MultiSelectSettingInput({ }; const Component = autocomplete ? MultiSelectFiltered : MultiSelect; return ( - <> - - - - {label} - - {hasResetButton && } - - - [key, t(i18nLabel)])} - /> - + + + + {label} + + {hasResetButton && } + + + [key, t(i18nLabel)])} + /> + + ); } diff --git a/apps/meteor/client/views/admin/settings/inputs/PasswordSettingInput.tsx b/apps/meteor/client/views/admin/settings/inputs/PasswordSettingInput.tsx index b7d2c1d48d4..249fb7e8c90 100644 --- a/apps/meteor/client/views/admin/settings/inputs/PasswordSettingInput.tsx +++ b/apps/meteor/client/views/admin/settings/inputs/PasswordSettingInput.tsx @@ -1,4 +1,4 @@ -import { Box, FieldLabel, FieldRow, Flex, PasswordInput } from '@rocket.chat/fuselage'; +import { Field, FieldLabel, FieldRow, PasswordInput } from '@rocket.chat/fuselage'; import type { EventHandler, ReactElement, SyntheticEvent } from 'react'; import React from 'react'; @@ -12,6 +12,7 @@ type PasswordSettingInputProps = { readonly?: boolean; autocomplete?: boolean; disabled?: boolean; + required?: boolean; hasResetButton?: boolean; onChangeValue?: (value: string) => void; onResetButtonClick?: () => void; @@ -25,6 +26,7 @@ function PasswordSettingInput({ readonly, autocomplete, disabled, + required, hasResetButton, onChangeValue, onResetButtonClick, @@ -34,15 +36,13 @@ function PasswordSettingInput({ }; return ( - <> - - - - {label} - - {hasResetButton && } - - + + + + {label} + + {hasResetButton && } + - + ); } diff --git a/apps/meteor/client/views/admin/settings/inputs/RelativeUrlSettingInput.tsx b/apps/meteor/client/views/admin/settings/inputs/RelativeUrlSettingInput.tsx index b9458170675..681232e4180 100644 --- a/apps/meteor/client/views/admin/settings/inputs/RelativeUrlSettingInput.tsx +++ b/apps/meteor/client/views/admin/settings/inputs/RelativeUrlSettingInput.tsx @@ -1,4 +1,4 @@ -import { Box, FieldLabel, Flex, UrlInput } from '@rocket.chat/fuselage'; +import { Field, FieldLabel, FieldRow, UrlInput } from '@rocket.chat/fuselage'; import { useAbsoluteUrl } from '@rocket.chat/ui-contexts'; import type { EventHandler, ReactElement, SyntheticEvent } from 'react'; import React from 'react'; @@ -13,6 +13,7 @@ type RelativeUrlSettingInputProps = { readonly?: boolean; autocomplete?: boolean; disabled?: boolean; + required?: boolean; hasResetButton?: boolean; onChangeValue?: (value: string) => void; onResetButtonClick?: () => void; @@ -26,6 +27,7 @@ function RelativeUrlSettingInput({ readonly, autocomplete, disabled, + required, hasResetButton, onChangeValue, onResetButtonClick, @@ -37,15 +39,13 @@ function RelativeUrlSettingInput({ }; return ( - <> - - - - {label} - - {hasResetButton && } - - + + + + {label} + + {hasResetButton && } + - + ); } diff --git a/apps/meteor/client/views/admin/settings/inputs/RoomPickSettingInput.tsx b/apps/meteor/client/views/admin/settings/inputs/RoomPickSettingInput.tsx index 15423742ff9..fab66ff7a06 100644 --- a/apps/meteor/client/views/admin/settings/inputs/RoomPickSettingInput.tsx +++ b/apps/meteor/client/views/admin/settings/inputs/RoomPickSettingInput.tsx @@ -1,5 +1,5 @@ import type { SettingValueRoomPick } from '@rocket.chat/core-typings'; -import { Box, FieldLabel, FieldRow, Flex } from '@rocket.chat/fuselage'; +import { Field, FieldLabel, FieldRow } from '@rocket.chat/fuselage'; import type { ReactElement } from 'react'; import React from 'react'; @@ -13,6 +13,7 @@ type RoomPickSettingInputProps = { placeholder?: string; readonly?: boolean; disabled?: boolean; + required?: boolean; hasResetButton?: boolean; onChangeValue: (value: SettingValueRoomPick) => void; onResetButtonClick?: () => void; @@ -25,6 +26,7 @@ function RoomPickSettingInput({ placeholder, readonly, disabled, + required, hasResetButton, onChangeValue, onResetButtonClick, @@ -39,15 +41,13 @@ function RoomPickSettingInput({ }; return ( - <> - - - - {label} - - {hasResetButton && } - - + + + + {label} + + {hasResetButton && } + - + ); } diff --git a/apps/meteor/client/views/admin/settings/inputs/SelectSettingInput.tsx b/apps/meteor/client/views/admin/settings/inputs/SelectSettingInput.tsx index a6fa88f7ffe..f12104f07d0 100644 --- a/apps/meteor/client/views/admin/settings/inputs/SelectSettingInput.tsx +++ b/apps/meteor/client/views/admin/settings/inputs/SelectSettingInput.tsx @@ -1,4 +1,4 @@ -import { Box, FieldLabel, FieldRow, Flex, Select } from '@rocket.chat/fuselage'; +import { Field, FieldLabel, FieldRow, Select } from '@rocket.chat/fuselage'; import type { TranslationKey } from '@rocket.chat/ui-contexts'; import { useTranslation } from '@rocket.chat/ui-contexts'; import type { ReactElement } from 'react'; @@ -15,6 +15,7 @@ type SelectSettingInputProps = { readonly?: boolean; autocomplete?: boolean; disabled?: boolean; + required?: boolean; hasResetButton?: boolean; onChangeValue?: (value: string) => void; onResetButtonClick?: () => void; @@ -28,6 +29,7 @@ function SelectSettingInput({ readonly, autocomplete, disabled, + required, values = [], hasResetButton, onChangeValue, @@ -40,15 +42,13 @@ function SelectSettingInput({ }; return ( - <> - - - - {label} - - {hasResetButton && } - - + + + + {label} + + {hasResetButton && } + [key, key])} /> - + ); } diff --git a/apps/meteor/client/views/admin/settings/inputs/StringSettingInput.tsx b/apps/meteor/client/views/admin/settings/inputs/StringSettingInput.tsx index 3d0ba78a127..1803f54a363 100644 --- a/apps/meteor/client/views/admin/settings/inputs/StringSettingInput.tsx +++ b/apps/meteor/client/views/admin/settings/inputs/StringSettingInput.tsx @@ -1,4 +1,4 @@ -import { Box, FieldLabel, FieldRow, Flex, TextAreaInput, TextInput } from '@rocket.chat/fuselage'; +import { Field, FieldLabel, FieldRow, TextAreaInput, TextInput } from '@rocket.chat/fuselage'; import type { EventHandler, ReactElement, SyntheticEvent } from 'react'; import React from 'react'; @@ -15,6 +15,7 @@ type StringSettingInputProps = { error?: string; autocomplete?: boolean; disabled?: boolean; + required?: boolean; hasResetButton?: boolean; onChangeValue?: (value: string) => void; onResetButtonClick?: () => void; @@ -25,6 +26,7 @@ function StringSettingInput({ label, name, disabled, + required, multiline, placeholder, readonly, @@ -40,15 +42,13 @@ function StringSettingInput({ }; return ( - <> - - - - {label} - - {hasResetButton && } - - + + + + {label} + + {hasResetButton && } + {multiline ? ( )} - + ); } diff --git a/apps/meteor/client/views/marketplace/AppDetailsPage/AppDetailsPage.tsx b/apps/meteor/client/views/marketplace/AppDetailsPage/AppDetailsPage.tsx index 46803e1b6ed..28eb55245a4 100644 --- a/apps/meteor/client/views/marketplace/AppDetailsPage/AppDetailsPage.tsx +++ b/apps/meteor/client/views/marketplace/AppDetailsPage/AppDetailsPage.tsx @@ -4,11 +4,11 @@ import { Button, ButtonGroup, Box } from '@rocket.chat/fuselage'; import { useMutableCallback } from '@rocket.chat/fuselage-hooks'; import { useTranslation, useRouteParameter, useToastMessageDispatch, usePermission, useRouter } from '@rocket.chat/ui-contexts'; import type { ReactElement } from 'react'; -import React, { useState, useCallback, useRef } from 'react'; +import React, { useMemo, useCallback } from 'react'; +import { FormProvider, useForm } from 'react-hook-form'; -import type { ISettings } from '../../../../ee/client/apps/@types/IOrchestrator'; import { AppClientOrchestratorInstance } from '../../../../ee/client/apps/orchestrator'; -import { Page, PageHeader, PageScrollableContentWithShadow } from '../../../components/Page'; +import { Page, PageFooter, PageHeader, PageScrollableContentWithShadow } from '../../../components/Page'; import { handleAPIError } from '../helpers/handleAPIError'; import { useAppInfo } from '../hooks/useAppInfo'; import AppDetailsPageHeader from './AppDetailsPageHeader'; @@ -23,18 +23,12 @@ import AppSettings from './tabs/AppSettings'; const AppDetailsPage = ({ id }: { id: App['id'] }): ReactElement => { const t = useTranslation(); - const dispatchToastMessage = useToastMessageDispatch(); const router = useRouter(); - - const [hasUnsavedChanges, setHasUnsavedChanges] = useState(false); - const [isSaving, setIsSaving] = useState(false); - - const settingsRef = useRef>({}); + const dispatchToastMessage = useToastMessageDispatch(); const isAdminUser = usePermission('manage-apps'); const tab = useRouteParameter('tab'); const context = useRouteParameter('context'); - const appData = useAppInfo(id, context || ''); const handleReturn = useMutableCallback((): void => { @@ -49,39 +43,41 @@ const AppDetailsPage = ({ id }: { id: App['id'] }): ReactElement => { }); const { installed, settings, privacyPolicySummary, permissions, tosLink, privacyLink, name } = appData || {}; - const isSecurityVisible = Boolean(privacyPolicySummary || permissions || tosLink || privacyLink); - const saveAppSettings = useCallback(async () => { - const { current } = settingsRef; - setIsSaving(true); - try { - await AppClientOrchestratorInstance.setAppSettings( - id, - (Object.values(settings || {}) as ISetting[]).map((value) => ({ - ...value, - value: current?.[value.id], - })), - ); + const saveAppSettings = useCallback( + async (data) => { + try { + await AppClientOrchestratorInstance.setAppSettings( + id, + (Object.values(settings || {}) as ISetting[]).map((setting) => ({ + ...setting, + value: data[setting.id], + })), + ); - dispatchToastMessage({ type: 'success', message: `${name} settings saved succesfully` }); - } catch (e: any) { - handleAPIError(e); - } - setIsSaving(false); - }, [dispatchToastMessage, id, name, settings]); + dispatchToastMessage({ type: 'success', message: `${name} settings saved succesfully` }); + } catch (e: any) { + handleAPIError(e); + } + }, + [dispatchToastMessage, id, name, settings], + ); + + const reducedSettings = useMemo(() => { + return Object.values(settings || {}).reduce((ret, { id, value, packageValue }) => ({ ...ret, [id]: value ?? packageValue }), {}); + }, [settings]); + + const methods = useForm({ values: reducedSettings }); + const { + handleSubmit, + reset, + formState: { isDirty, isSubmitting, isSubmitted }, + } = methods; return ( - - - {installed && isAdminUser && ( - - )} - - + {!appData && } @@ -107,17 +103,25 @@ const AppDetailsPage = ({ id }: { id: App['id'] }): ReactElement => { )} {tab === 'releases' && } {Boolean(tab === 'settings' && settings && Object.values(settings).length) && ( - + + + )} {tab === 'logs' && } )} + + + + {installed && isAdminUser && ( + + )} + + ); }; diff --git a/apps/meteor/client/views/marketplace/AppDetailsPage/tabs/AppSettings/AppSetting.tsx b/apps/meteor/client/views/marketplace/AppDetailsPage/tabs/AppSettings/AppSetting.tsx index 378d855f027..d63c44d1668 100644 --- a/apps/meteor/client/views/marketplace/AppDetailsPage/tabs/AppSettings/AppSetting.tsx +++ b/apps/meteor/client/views/marketplace/AppDetailsPage/tabs/AppSettings/AppSetting.tsx @@ -1,8 +1,9 @@ import type { ISettingSelectValue } from '@rocket.chat/apps-engine/definition/settings'; -import type { ISettingBase, SettingValue } from '@rocket.chat/core-typings'; +import type { ISetting } from '@rocket.chat/apps-engine/definition/settings/ISetting'; import { useRouteParameter, useTranslation } from '@rocket.chat/ui-contexts'; import type { ReactElement } from 'react'; import React, { useMemo, useCallback } from 'react'; +import { Controller, useFormContext } from 'react-hook-form'; import { Utilities } from '../../../../../../ee/lib/misc/Utilities'; import MarkdownText from '../../../../../components/MarkdownText'; @@ -48,28 +49,16 @@ const useAppTranslation = (appId: string): AppTranslationFunction => { }); }; -type AppSettingProps = { - appSetting: { - id: string; - type: ISettingBase['type']; - i18nLabel: string; - i18nDescription?: string; - values?: ISettingSelectValue[]; - required: boolean; - }; - onChange: (value: SettingValue) => void; - value: SettingValue; -}; -const AppSetting = ({ appSetting, onChange, value, ...props }: AppSettingProps): ReactElement => { +const AppSetting = ({ id, type, i18nLabel, i18nDescription, values, value, packageValue, ...props }: ISetting): ReactElement => { const appId = useRouteParameter('id'); const tApp = useAppTranslation(appId || ''); - const { id, type, i18nLabel, i18nDescription, values, required } = appSetting; - - const label = (i18nLabel && tApp(i18nLabel)) + (required ? ' *' : '') || id || tApp(id); + const label = (i18nLabel && tApp(i18nLabel)) || id || tApp(id); const hint = useMemo(() => i18nDescription && , [i18nDescription, tApp]); - let translatedValues; + const { control } = useFormContext(); + + let translatedValues: ISettingSelectValue[]; if (values?.length) { translatedValues = values.map((selectFieldEntry) => { const { key, i18nLabel } = selectFieldEntry; @@ -86,15 +75,22 @@ const AppSetting = ({ appSetting, onChange, value, ...props }: AppSettingProps): } return ( - ( + + )} /> ); }; diff --git a/apps/meteor/client/views/marketplace/AppDetailsPage/tabs/AppSettings/AppSettings.tsx b/apps/meteor/client/views/marketplace/AppDetailsPage/tabs/AppSettings/AppSettings.tsx index 57e98fe77e8..edfae67251f 100644 --- a/apps/meteor/client/views/marketplace/AppDetailsPage/tabs/AppSettings/AppSettings.tsx +++ b/apps/meteor/client/views/marketplace/AppDetailsPage/tabs/AppSettings/AppSettings.tsx @@ -1,50 +1,24 @@ -import type { ISetting } from '@rocket.chat/apps-engine/definition/settings'; -import type { SettingValue } from '@rocket.chat/core-typings'; -import { Box } from '@rocket.chat/fuselage'; +import { Box, FieldGroup } from '@rocket.chat/fuselage'; import { useTranslation } from '@rocket.chat/ui-contexts'; -import type { FC, MutableRefObject } from 'react'; -import React, { useMemo, useEffect } from 'react'; +import React from 'react'; import type { ISettings } from '../../../../../../ee/client/apps/@types/IOrchestrator'; -import { useForm } from '../../../../../hooks/useForm'; -import AppSettingsAssembler from './AppSettingsAssembler'; +import AppSetting from './AppSetting'; -type AppSettingsProps = { - settings: ISettings; - setHasUnsavedChanges: (hasUnsavedChanges: boolean) => void; - settingsRef: MutableRefObject>; -}; - -const AppSettings: FC = ({ settings, setHasUnsavedChanges, settingsRef }) => { +const AppSettings = ({ settings }: { settings: ISettings }) => { const t = useTranslation(); - const stringifiedSettings = JSON.stringify(settings); - - const reducedSettings = useMemo(() => { - const settings: AppSettingsProps['settings'] = JSON.parse(stringifiedSettings); - return Object.values(settings).reduce((ret, { id, value, packageValue }) => ({ ...ret, [id]: value ?? packageValue }), {}); - }, [stringifiedSettings]); - - const { values, handlers, hasUnsavedChanges } = useForm(reducedSettings) as { - values: Record; - handlers: Record void>; - hasUnsavedChanges: boolean; - }; - const stringifiedValues = JSON.stringify(values); - - useEffect(() => { - const values = JSON.parse(stringifiedValues); - setHasUnsavedChanges(hasUnsavedChanges); - settingsRef.current = values; - }, [hasUnsavedChanges, stringifiedValues, setHasUnsavedChanges, settingsRef]); - return ( <> {t('Settings')} - + + {Object.values(settings).map((field) => ( + + ))} + ); diff --git a/apps/meteor/client/views/marketplace/AppDetailsPage/tabs/AppSettings/AppSettingsAssembler.tsx b/apps/meteor/client/views/marketplace/AppDetailsPage/tabs/AppSettings/AppSettingsAssembler.tsx deleted file mode 100644 index fd60fcd6de8..00000000000 --- a/apps/meteor/client/views/marketplace/AppDetailsPage/tabs/AppSettings/AppSettingsAssembler.tsx +++ /dev/null @@ -1,24 +0,0 @@ -import type { SettingValue } from '@rocket.chat/core-typings'; -import { Box } from '@rocket.chat/fuselage'; -import { capitalize } from '@rocket.chat/string-helpers'; -import type { ReactElement } from 'react'; -import React from 'react'; - -import type { ISettings } from '../../../../../../ee/client/apps/@types/IOrchestrator'; -import AppSetting from './AppSetting'; - -type AppSettingsAssemblerProps = { - settings: ISettings; - values: Record; - handlers: Record void>; -}; -const AppSettingsAssembler = ({ settings, values, handlers }: AppSettingsAssemblerProps): ReactElement => ( - - {Object.values(settings).map((current) => { - const { id } = current; - return ; - })} - -); - -export default AppSettingsAssembler; -- GitLab From 4644f72a6330f0153519ff19c6c486b51025e705 Mon Sep 17 00:00:00 2001 From: Diego Sampaio Date: Thu, 14 Dec 2023 17:16:54 -0300 Subject: [PATCH 033/329] refactor: convert markdown parser callback (#31224) --- apps/meteor/server/hooks/index.ts | 1 - .../server/hooks/messages/markdownParser.ts | 56 ----------- .../hooks/BeforeSaveMarkdownParser.ts | 43 +++++++++ .../server/services/messages/service.ts | 30 ++++++ .../hooks/BeforeSaveMarkdownParser.tests.ts | 93 +++++++++++++++++++ 5 files changed, 166 insertions(+), 57 deletions(-) delete mode 100644 apps/meteor/server/hooks/messages/markdownParser.ts create mode 100644 apps/meteor/server/services/messages/hooks/BeforeSaveMarkdownParser.ts create mode 100644 apps/meteor/tests/unit/server/services/messages/hooks/BeforeSaveMarkdownParser.tests.ts diff --git a/apps/meteor/server/hooks/index.ts b/apps/meteor/server/hooks/index.ts index 28de5899853..64d500b223f 100644 --- a/apps/meteor/server/hooks/index.ts +++ b/apps/meteor/server/hooks/index.ts @@ -1,2 +1 @@ -import './messages/markdownParser'; import './sauMonitorHooks'; diff --git a/apps/meteor/server/hooks/messages/markdownParser.ts b/apps/meteor/server/hooks/messages/markdownParser.ts deleted file mode 100644 index 770eb142f92..00000000000 --- a/apps/meteor/server/hooks/messages/markdownParser.ts +++ /dev/null @@ -1,56 +0,0 @@ -import { isE2EEMessage, isOTRMessage } from '@rocket.chat/core-typings'; -import type { Options, Root } from '@rocket.chat/message-parser'; -import { parse } from '@rocket.chat/message-parser'; - -import { settings } from '../../../app/settings/server'; -import { callbacks } from '../../../lib/callbacks'; -import { SystemLogger } from '../../lib/logger/system'; - -if (process.env.DISABLE_MESSAGE_PARSER !== 'true') { - callbacks.add( - 'beforeSaveMessage', - (message) => { - if (isE2EEMessage(message) || isOTRMessage(message)) { - return message; - } - try { - if (message.msg) { - message.md = messageTextToAstMarkdown(message.msg); - } - - if (message.attachments?.[0]?.description !== undefined) { - message.attachments[0].descriptionMd = messageTextToAstMarkdown(message.attachments[0].description); - } - } catch (e) { - SystemLogger.error(e); // errors logged while the parser is at experimental stage - } - - return message; - }, - callbacks.priority.MEDIUM, - 'markdownParser', - ); -} - -const messageTextToAstMarkdown = (messageText: string): Root => { - const customDomains = settings.get('Message_CustomDomain_AutoLink') - ? settings - .get('Message_CustomDomain_AutoLink') - .split(',') - .map((domain) => domain.trim()) - : []; - - const parseOptions: Options = { - colors: settings.get('HexColorPreview_Enabled'), - emoticons: true, - customDomains, - ...(settings.get('Katex_Enabled') && { - katex: { - dollarSyntax: settings.get('Katex_Dollar_Syntax'), - parenthesisSyntax: settings.get('Katex_Parenthesis_Syntax'), - }, - }), - }; - - return parse(messageText, parseOptions); -}; diff --git a/apps/meteor/server/services/messages/hooks/BeforeSaveMarkdownParser.ts b/apps/meteor/server/services/messages/hooks/BeforeSaveMarkdownParser.ts new file mode 100644 index 00000000000..c4e9afe898e --- /dev/null +++ b/apps/meteor/server/services/messages/hooks/BeforeSaveMarkdownParser.ts @@ -0,0 +1,43 @@ +import { isE2EEMessage, isOTRMessage } from '@rocket.chat/core-typings'; +import type { IMessage } from '@rocket.chat/core-typings'; +import { parse } from '@rocket.chat/message-parser'; + +type ParserConfig = { + colors?: boolean; + emoticons?: boolean; + customDomains?: string[]; + katex?: { + dollarSyntax: boolean; + parenthesisSyntax: boolean; + }; +}; + +export class BeforeSaveMarkdownParser { + constructor(private enabled: boolean = true) { + // no op + } + + async parseMarkdown({ message, config }: { message: IMessage; config: ParserConfig }): Promise { + if (!this.enabled) { + return message; + } + + if (isE2EEMessage(message) || isOTRMessage(message)) { + return message; + } + + try { + if (message.msg) { + message.md = parse(message.msg, config); + } + + if (message.attachments?.[0]?.description !== undefined) { + message.attachments[0].descriptionMd = parse(message.attachments[0].description, config); + } + } catch (e) { + console.error(e); // errors logged while the parser is at experimental stage + } + + return message; + } +} diff --git a/apps/meteor/server/services/messages/service.ts b/apps/meteor/server/services/messages/service.ts index 09304e0532a..885152c594e 100644 --- a/apps/meteor/server/services/messages/service.ts +++ b/apps/meteor/server/services/messages/service.ts @@ -14,9 +14,12 @@ import { broadcastMessageSentEvent } from '../../modules/watchers/lib/messages'; import { BeforeSaveBadWords } from './hooks/BeforeSaveBadWords'; import { BeforeSaveCheckMAC } from './hooks/BeforeSaveCheckMAC'; import { BeforeSaveJumpToMessage } from './hooks/BeforeSaveJumpToMessage'; +import { BeforeSaveMarkdownParser } from './hooks/BeforeSaveMarkdownParser'; import { BeforeSavePreventMention } from './hooks/BeforeSavePreventMention'; import { BeforeSaveSpotify } from './hooks/BeforeSaveSpotify'; +const disableMarkdownParser = ['yes', 'true'].includes(String(process.env.DISABLE_MESSAGE_PARSER).toLowerCase()); + export class MessageService extends ServiceClassInternal implements IMessageService { protected name = 'message'; @@ -28,6 +31,8 @@ export class MessageService extends ServiceClassInternal implements IMessageServ private jumpToMessage: BeforeSaveJumpToMessage; + private markdownParser: BeforeSaveMarkdownParser; + private checkMAC: BeforeSaveCheckMAC; async created() { @@ -48,6 +53,8 @@ export class MessageService extends ServiceClassInternal implements IMessageServ return (user && getUserAvatarURL(user)) || ''; }, }); + + this.markdownParser = new BeforeSaveMarkdownParser(!disableMarkdownParser); this.checkMAC = new BeforeSaveCheckMAC(); await this.configureBadWords(); @@ -124,6 +131,8 @@ export class MessageService extends ServiceClassInternal implements IMessageServ // TODO looks like this one was not being used (so I'll left it commented) // await this.joinDiscussionOnMessage({ message, room, user }); + message = await this.markdownParser.parseMarkdown({ message, config: this.getMarkdownConfig() }); + message = await this.badWords.filterBadWords({ message }); message = await this.spotify.convertSpotifyLinks({ message }); message = await this.jumpToMessage.createAttachmentForMessageURLs({ @@ -147,6 +156,27 @@ export class MessageService extends ServiceClassInternal implements IMessageServ return message; } + private getMarkdownConfig() { + const customDomains = settings.get('Message_CustomDomain_AutoLink') + ? settings + .get('Message_CustomDomain_AutoLink') + .split(',') + .map((domain) => domain.trim()) + : []; + + return { + colors: settings.get('HexColorPreview_Enabled'), + emoticons: true, + customDomains, + ...(settings.get('Katex_Enabled') && { + katex: { + dollarSyntax: settings.get('Katex_Dollar_Syntax'), + parenthesisSyntax: settings.get('Katex_Parenthesis_Syntax'), + }, + }), + }; + } + private isEditedOrOld(message: IMessage): boolean { return isEditedMessage(message) || !message.ts || Math.abs(Date.now() - message.ts.getTime()) > 60000; } diff --git a/apps/meteor/tests/unit/server/services/messages/hooks/BeforeSaveMarkdownParser.tests.ts b/apps/meteor/tests/unit/server/services/messages/hooks/BeforeSaveMarkdownParser.tests.ts new file mode 100644 index 00000000000..86cc3103f07 --- /dev/null +++ b/apps/meteor/tests/unit/server/services/messages/hooks/BeforeSaveMarkdownParser.tests.ts @@ -0,0 +1,93 @@ +import { expect } from 'chai'; + +import { BeforeSaveMarkdownParser } from '../../../../../../server/services/messages/hooks/BeforeSaveMarkdownParser'; + +const createMessage = (msg?: string, extra: any = {}) => ({ + _id: 'random', + rid: 'GENERAL', + ts: new Date(), + u: { + _id: 'userId', + username: 'username', + }, + _updatedAt: new Date(), + msg: msg as string, + ...extra, +}); + +describe('Markdown parser', () => { + it('should do nothing if markdown parser is disabled', async () => { + const markdownParser = new BeforeSaveMarkdownParser(false); + + const message = await markdownParser.parseMarkdown({ + message: createMessage('hey'), + config: {}, + }); + + expect(message).to.not.have.property('md'); + }); + + it('should do nothing for OTR messages', async () => { + const markdownParser = new BeforeSaveMarkdownParser(true); + + const message = await markdownParser.parseMarkdown({ + message: createMessage('hey', { t: 'otr' }), + config: {}, + }); + + const messageAck = await markdownParser.parseMarkdown({ + message: createMessage('hey', { t: 'otr-ack' }), + config: {}, + }); + + expect(message).to.not.have.property('md'); + expect(messageAck).to.not.have.property('md'); + }); + + it('should do nothing for E2E messages', async () => { + const markdownParser = new BeforeSaveMarkdownParser(true); + + const message = await markdownParser.parseMarkdown({ + message: createMessage('hey', { t: 'e2e' }), + config: {}, + }); + + expect(message).to.not.have.property('md'); + }); + + it('should parse markdown', async () => { + const markdownParser = new BeforeSaveMarkdownParser(true); + + const message = await markdownParser.parseMarkdown({ + message: createMessage('hey'), + config: {}, + }); + + expect(message).to.have.property('md'); + }); + + it('should parse markdown on the first attachment only', async () => { + const markdownParser = new BeforeSaveMarkdownParser(true); + + const message = await markdownParser.parseMarkdown({ + message: createMessage('hey', { + attachments: [ + { + description: 'hey ho', + }, + { + description: 'lets go', + }, + ], + }), + config: {}, + }); + + expect(message).to.have.property('md'); + + const [attachment1, attachment2] = message.attachments || []; + + expect(attachment1).to.have.property('descriptionMd'); + expect(attachment2).to.not.have.property('descriptionMd'); + }); +}); -- GitLab From 68ff7e5c5b0b6bd44370fc33a25a9ee42a46025f Mon Sep 17 00:00:00 2001 From: Guilherme Gazzo Date: Thu, 14 Dec 2023 17:22:25 -0300 Subject: [PATCH 034/329] fix: stop refetching banner data (#31235) --- .changeset/gold-stingrays-compete.md | 5 +++++ apps/meteor/client/views/cloud/CloudAnnouncementsRegion.tsx | 2 +- 2 files changed, 6 insertions(+), 1 deletion(-) create mode 100644 .changeset/gold-stingrays-compete.md diff --git a/.changeset/gold-stingrays-compete.md b/.changeset/gold-stingrays-compete.md new file mode 100644 index 00000000000..8cc6c3aa6e8 --- /dev/null +++ b/.changeset/gold-stingrays-compete.md @@ -0,0 +1,5 @@ +--- +"@rocket.chat/meteor": patch +--- + +fix: stop refetching banner data each 5 minutes diff --git a/apps/meteor/client/views/cloud/CloudAnnouncementsRegion.tsx b/apps/meteor/client/views/cloud/CloudAnnouncementsRegion.tsx index 7352b54e757..2f353a4f970 100644 --- a/apps/meteor/client/views/cloud/CloudAnnouncementsRegion.tsx +++ b/apps/meteor/client/views/cloud/CloudAnnouncementsRegion.tsx @@ -16,7 +16,7 @@ const CloudAnnouncementsRegion = () => { select: (data) => data.banners, enabled: !!uid, staleTime: 0, - refetchInterval: 1000 * 60 * 5, + refetchInterval: 1000 * 60 * 60 * 24, }); const subscribeToNotifyLoggedIn = useStream('notify-logged'); -- GitLab From c407aaf47e543b343f9588300f57904af0ae72da Mon Sep 17 00:00:00 2001 From: Diego Sampaio Date: Thu, 14 Dec 2023 17:47:45 -0300 Subject: [PATCH 035/329] chore: use `findOneAndUpdate` from `BaseRaw` in all places (#31243) --- apps/meteor/server/models/raw/BaseRaw.ts | 1 + apps/meteor/server/models/raw/LivechatDepartmentAgents.ts | 4 ++-- apps/meteor/server/models/raw/LivechatInquiry.ts | 2 +- apps/meteor/server/models/raw/Messages.ts | 2 +- apps/meteor/server/models/raw/NotificationQueue.ts | 2 +- apps/meteor/server/models/raw/Nps.ts | 2 +- apps/meteor/server/models/raw/Users.js | 4 ++-- apps/meteor/server/services/nps/service.ts | 2 +- 8 files changed, 10 insertions(+), 9 deletions(-) diff --git a/apps/meteor/server/models/raw/BaseRaw.ts b/apps/meteor/server/models/raw/BaseRaw.ts index ce3e0f224e1..e43bac39b33 100644 --- a/apps/meteor/server/models/raw/BaseRaw.ts +++ b/apps/meteor/server/models/raw/BaseRaw.ts @@ -141,6 +141,7 @@ export abstract class BaseRaw< } public findOneAndUpdate(query: Filter, update: UpdateFilter | T, options?: FindOneAndUpdateOptions): Promise> { + this.setUpdatedAt(update); return this.col.findOneAndUpdate(query, update, options || {}); } diff --git a/apps/meteor/server/models/raw/LivechatDepartmentAgents.ts b/apps/meteor/server/models/raw/LivechatDepartmentAgents.ts index ec4e6094893..91f3f4e22e3 100644 --- a/apps/meteor/server/models/raw/LivechatDepartmentAgents.ts +++ b/apps/meteor/server/models/raw/LivechatDepartmentAgents.ts @@ -238,7 +238,7 @@ export class LivechatDepartmentAgentsRaw extends BaseRaw implemen async findNextAndLock(queueSortBy: OmnichannelSortingMechanismSettingType, department?: string): Promise { const date = new Date(); - const result = await this.col.findOneAndUpdate( + const result = await this.findOneAndUpdate( { status: LivechatInquiryStatus.QUEUED, ...(department ? { department } : { department: { $exists: false } }), diff --git a/apps/meteor/server/models/raw/Messages.ts b/apps/meteor/server/models/raw/Messages.ts index 464555c9478..16366a189bc 100644 --- a/apps/meteor/server/models/raw/Messages.ts +++ b/apps/meteor/server/models/raw/Messages.ts @@ -1632,7 +1632,7 @@ export class MessagesRaw extends BaseRaw implements IMessagesModel { drid, }; - return this.col.findOneAndUpdate( + return this.findOneAndUpdate( query, { $set: { diff --git a/apps/meteor/server/models/raw/NotificationQueue.ts b/apps/meteor/server/models/raw/NotificationQueue.ts index 9602b029e07..0832844a21b 100644 --- a/apps/meteor/server/models/raw/NotificationQueue.ts +++ b/apps/meteor/server/models/raw/NotificationQueue.ts @@ -71,7 +71,7 @@ export class NotificationQueueRaw extends BaseRaw implements INot async findNextInQueueOrExpired(expired: Date): Promise { const now = new Date(); - const result = await this.col.findOneAndUpdate( + const result = await this.findOneAndUpdate( { $and: [ { diff --git a/apps/meteor/server/models/raw/Nps.ts b/apps/meteor/server/models/raw/Nps.ts index 82fb39ac330..1a12f68bff2 100644 --- a/apps/meteor/server/models/raw/Nps.ts +++ b/apps/meteor/server/models/raw/Nps.ts @@ -27,7 +27,7 @@ export class NpsRaw extends BaseRaw implements INpsModel { status: NPSStatus.SENDING, }, }; - const { value } = await this.col.findOneAndUpdate(query, update, { sort: { expireAt: 1 } }); + const { value } = await this.findOneAndUpdate(query, update, { sort: { expireAt: 1 } }); return value; } diff --git a/apps/meteor/server/models/raw/Users.js b/apps/meteor/server/models/raw/Users.js index 5b399323f14..2e9d38faeb8 100644 --- a/apps/meteor/server/models/raw/Users.js +++ b/apps/meteor/server/models/raw/Users.js @@ -1651,7 +1651,7 @@ export class UsersRaw extends BaseRaw { }, }; - const user = await this.col.findOneAndUpdate(query, update, { sort, returnDocument: 'after' }); + const user = await this.findOneAndUpdate(query, update, { sort, returnDocument: 'after' }); if (user && user.value) { return { agentId: user.value._id, @@ -1681,7 +1681,7 @@ export class UsersRaw extends BaseRaw { }, }; - const user = await this.col.findOneAndUpdate(query, update, { sort, returnDocument: 'after' }); + const user = await this.findOneAndUpdate(query, update, { sort, returnDocument: 'after' }); if (user?.value) { return { agentId: user.value._id, diff --git a/apps/meteor/server/services/nps/service.ts b/apps/meteor/server/services/nps/service.ts index a3ee9b7eebe..9554a472fb0 100644 --- a/apps/meteor/server/services/nps/service.ts +++ b/apps/meteor/server/services/nps/service.ts @@ -82,7 +82,7 @@ export class NPSService extends ServiceClassInternal implements INPSService { const sending = await Promise.all( votesToSend.map(async (vote) => { - const { value } = await NpsVote.col.findOneAndUpdate( + const { value } = await NpsVote.findOneAndUpdate( { _id: vote._id, status: INpsVoteStatus.NEW, -- GitLab From 44dd24da73090f0b96aac415ef757e4e934db55d Mon Sep 17 00:00:00 2001 From: gabriellsh <40830821+gabriellsh@users.noreply.github.com> Date: Thu, 14 Dec 2023 18:33:22 -0300 Subject: [PATCH 036/329] feat: Bot warning when mentioning users not in a channel (#30464) Co-authored-by: Guilherme Jun Grillo <48109548+guijun13@users.noreply.github.com> --- .changeset/fresh-socks-fix.md | 5 + apps/meteor/app/lib/server/index.ts | 1 + .../server/lib/sendNotificationsOnMessage.js | 47 +---- .../server/startup/mentionUserNotInChannel.ts | 142 +++++++++++++ .../rocketchat-i18n/i18n/en.i18n.json | 7 + .../modules/core-apps/mention.module.ts | 125 ++++++++++++ apps/meteor/server/startup/coreApps.ts | 2 + .../meteor/tests/e2e/message-mentions.spec.ts | 193 ++++++++++++++++++ 8 files changed, 476 insertions(+), 46 deletions(-) create mode 100644 .changeset/fresh-socks-fix.md create mode 100644 apps/meteor/app/lib/server/startup/mentionUserNotInChannel.ts create mode 100644 apps/meteor/server/modules/core-apps/mention.module.ts diff --git a/.changeset/fresh-socks-fix.md b/.changeset/fresh-socks-fix.md new file mode 100644 index 00000000000..004677e019b --- /dev/null +++ b/.changeset/fresh-socks-fix.md @@ -0,0 +1,5 @@ +--- +"@rocket.chat/meteor": minor +--- + +Mentioning users that are not in the channel now dispatches a warning message with actions diff --git a/apps/meteor/app/lib/server/index.ts b/apps/meteor/app/lib/server/index.ts index c2d4bdda747..80aaa2a64a9 100644 --- a/apps/meteor/app/lib/server/index.ts +++ b/apps/meteor/app/lib/server/index.ts @@ -21,6 +21,7 @@ import './methods/createToken'; import './methods/deleteMessage'; import './methods/deleteUserOwnAccount'; import './methods/executeSlashCommandPreview'; +import './startup/mentionUserNotInChannel'; import './methods/getChannelHistory'; import './methods/getRoomJoinCode'; import './methods/getRoomRoles'; diff --git a/apps/meteor/app/lib/server/lib/sendNotificationsOnMessage.js b/apps/meteor/app/lib/server/lib/sendNotificationsOnMessage.js index 4ed882cfe87..395ddfe6d46 100644 --- a/apps/meteor/app/lib/server/lib/sendNotificationsOnMessage.js +++ b/apps/meteor/app/lib/server/lib/sendNotificationsOnMessage.js @@ -1,6 +1,4 @@ -import { Room } from '@rocket.chat/core-services'; import { Subscriptions, Users } from '@rocket.chat/models'; -import { Meteor } from 'meteor/meteor'; import moment from 'moment'; import { callbacks } from '../../../../lib/callbacks'; @@ -347,50 +345,7 @@ export async function sendAllNotifications(message, room) { return message; } - const { sender, hasMentionToAll, hasMentionToHere, notificationMessage, mentionIds, mentionIdsWithoutGroups } = - await sendMessageNotifications(message, room); - - // on public channels, if a mentioned user is not member of the channel yet, he will first join the channel and then be notified based on his preferences. - if (room.t === 'c') { - // get subscriptions from users already in room (to not send them a notification) - const mentions = [...mentionIdsWithoutGroups]; - const cursor = Subscriptions.findByRoomIdAndUserIds(room._id, mentionIdsWithoutGroups, { - projection: { 'u._id': 1 }, - }); - - for await (const subscription of cursor) { - const index = mentions.indexOf(subscription.u._id); - if (index !== -1) { - mentions.splice(index, 1); - } - } - - const users = await Promise.all( - mentions.map(async (userId) => { - await Room.join({ room, user: { _id: userId } }); - - return userId; - }), - ).catch((error) => { - throw new Meteor.Error(error); - }); - - const subscriptions = await Subscriptions.findByRoomIdAndUserIds(room._id, users).toArray(); - users.forEach((userId) => { - const subscription = subscriptions.find((subscription) => subscription.u._id === userId); - - void sendNotification({ - subscription, - sender, - hasMentionToAll, - hasMentionToHere, - message, - notificationMessage, - room, - mentionIds, - }); - }); - } + await sendMessageNotifications(message, room); return message; } diff --git a/apps/meteor/app/lib/server/startup/mentionUserNotInChannel.ts b/apps/meteor/app/lib/server/startup/mentionUserNotInChannel.ts new file mode 100644 index 00000000000..8b883645147 --- /dev/null +++ b/apps/meteor/app/lib/server/startup/mentionUserNotInChannel.ts @@ -0,0 +1,142 @@ +import { api } from '@rocket.chat/core-services'; +import type { IMessage } from '@rocket.chat/core-typings'; +import { isDirectMessageRoom, isEditedMessage, isRoomFederated } from '@rocket.chat/core-typings'; +import { Subscriptions, Rooms, Users, Settings } from '@rocket.chat/models'; +import type { ActionsBlock } from '@rocket.chat/ui-kit'; +import moment from 'moment'; + +import { callbacks } from '../../../../lib/callbacks'; +import { getUserDisplayName } from '../../../../lib/getUserDisplayName'; +import { isTruthy } from '../../../../lib/isTruthy'; +import { i18n } from '../../../../server/lib/i18n'; +import { hasPermissionAsync } from '../../../authorization/server/functions/hasPermission'; + +const APP_ID = 'mention-core'; +const getBlocks = (mentions: IMessage['mentions'], messageId: string, lng: string | undefined) => { + const stringifiedMentions = JSON.stringify(mentions); + return { + addUsersBlock: { + type: 'button', + appId: APP_ID, + blockId: messageId, + value: stringifiedMentions, + actionId: 'add-users', + text: { + type: 'plain_text', + text: i18n.t('Add_them', undefined, lng), + }, + }, + dismissBlock: { + type: 'button', + appId: APP_ID, + blockId: messageId, + value: stringifiedMentions, + actionId: 'dismiss', + text: { + type: 'plain_text', + text: i18n.t('Do_nothing', undefined, lng), + }, + }, + dmBlock: { + type: 'button', + appId: APP_ID, + value: stringifiedMentions, + blockId: messageId, + actionId: 'share-message', + text: { + type: 'plain_text', + text: i18n.t('Let_them_know', undefined, lng), + }, + }, + } as const; +}; + +callbacks.add( + 'beforeSaveMessage', + async (message) => { + // TODO: check if I need to test this 60 second rule. + // If the message was edited, or is older than 60 seconds (imported) + // the notifications will be skipped, so we can also skip this validation + if (isEditedMessage(message) || (message.ts && Math.abs(moment(message.ts).diff(moment())) > 60000) || !message.mentions) { + return message; + } + + const mentions = message.mentions.filter(({ _id }) => _id !== 'all' && _id !== 'here'); + if (!mentions.length) { + return message; + } + + const room = await Rooms.findOneById(message.rid); + if (!room || isDirectMessageRoom(room) || isRoomFederated(room) || room.t === 'l') { + return message; + } + + const subs = await Subscriptions.findByRoomIdAndUserIds( + message.rid, + mentions.map(({ _id }) => _id), + { projection: { u: 1 } }, + ).toArray(); + + // get all users that are mentioned but not in the channel + const mentionsUsersNotInChannel = mentions.filter(({ _id }) => !subs.some((sub) => sub.u._id === _id)); + + if (!mentionsUsersNotInChannel.length) { + return message; + } + + const canAddUsersToThisRoom = await hasPermissionAsync(message.u._id, 'add-user-to-joined-room', message.rid); + const canAddToAnyRoom = await (room.t === 'c' + ? hasPermissionAsync(message.u._id, 'add-user-to-any-c-room') + : hasPermissionAsync(message.u._id, 'add-user-to-any-p-room')); + const canDMUsers = await hasPermissionAsync(message.u._id, 'create-d'); // TODO: Perhaps check if user has DM with mentioned user (might be too expensive) + const canAddUsers = canAddUsersToThisRoom || canAddToAnyRoom; + const { language } = (await Users.findOneById(message.u._id)) || {}; + + const actionBlocks = getBlocks(mentionsUsersNotInChannel, message._id, language); + const elements: ActionsBlock['elements'] = [ + canAddUsers && actionBlocks.addUsersBlock, + (canAddUsers || canDMUsers) && actionBlocks.dismissBlock, + canDMUsers && actionBlocks.dmBlock, + ].filter(isTruthy); + + const messageLabel = canAddUsers + ? 'You_mentioned___mentions__but_theyre_not_in_this_room' + : 'You_mentioned___mentions__but_theyre_not_in_this_room_You_can_ask_a_room_admin_to_add_them'; + + const { value: useRealName } = (await Settings.findOneById('UI_Use_Real_Name')) || {}; + + const usernamesOrNames = mentionsUsersNotInChannel.map( + ({ username, name }) => `*${getUserDisplayName(name, username, Boolean(useRealName))}*`, + ); + + const mentionsText = usernamesOrNames.join(', '); + + // TODO: Mentions style + void api.broadcast('notify.ephemeralMessage', message.u._id, message.rid, { + msg: '', + mentions: mentionsUsersNotInChannel, + tmid: message.tmid, + blocks: [ + { + appId: APP_ID, + type: 'section', + text: { + type: 'mrkdwn', + text: i18n.t(messageLabel, { mentions: mentionsText }, language), + }, + } as const, + Boolean(elements.length) && + ({ + type: 'actions', + appId: APP_ID, + elements, + } as const), + ].filter(isTruthy), + private: true, + }); + + return message; + }, + callbacks.priority.LOW, + 'mention-user-not-in-channel', +); diff --git a/apps/meteor/packages/rocketchat-i18n/i18n/en.i18n.json b/apps/meteor/packages/rocketchat-i18n/i18n/en.i18n.json index 1680bab15cb..de0e3cfed58 100644 --- a/apps/meteor/packages/rocketchat-i18n/i18n/en.i18n.json +++ b/apps/meteor/packages/rocketchat-i18n/i18n/en.i18n.json @@ -328,6 +328,7 @@ "add-team-channel_description": "Permission to add a channel to a team", "add-team-member": "Add Team Member", "add-team-member_description": "Permission to add members to a team", + "Add_them": "Add them", "add-user": "Add User", "add-user_description": "Permission to add new users to the server via users screen", "add-user-to-any-c-room": "Add User to Any Public Channel", @@ -1705,6 +1706,7 @@ "Do_not_display_unread_counter": "Do not display any counter of this channel", "Do_not_provide_this_code_to_anyone": "Do not provide this code to anyone.", "Do_Nothing": "Do Nothing", + "Do_nothing": "Do nothing", "Do_you_have_any_notes_for_this_conversation": "Do you have any notes for this conversation?", "Do_you_want_to_accept": "Do you want to accept?", "Do_you_want_to_change_to_s_question": "Do you want to change to %s?", @@ -3074,6 +3076,7 @@ "leave-p": "Leave Private Groups", "leave-p_description": "Permission to leave private groups", "Lets_get_you_new_one": "Let's get you a new one!", + "Let_them_know": "Let them know", "License": "License", "Line": "Line", "Link": "Link", @@ -5841,6 +5844,9 @@ "You_have_not_verified_your_email": "You have not verified your email.", "You_have_successfully_unsubscribed": "You have successfully unsubscribed from our Mailling List.", "You_must_join_to_view_messages_in_this_channel": "You must join to view messages in this channel", + "You_mentioned___mentions__but_theyre_not_in_this_room": "You mentioned {{mentions}}, but they're not in this room.", + "You_mentioned___mentions__but_theyre_not_in_this_room_You_can_ask_a_room_admin_to_add_them": "You mentioned {{mentions}}, but they're not in this room. You can ask a room admin to add them.", + "You_mentioned___mentions__but_theyre_not_in_this_room_You_let_them_know_via_dm": "You mentioned {{mentions}}, but they're not in this room. You let them know via DM.", "You_need_confirm_email": "You need to confirm your email to login!", "You_need_install_an_extension_to_allow_screen_sharing": "You need install an extension to allow screen sharing", "You_need_to_change_your_password": "You need to change your password", @@ -5879,6 +5885,7 @@ "Your_TOTP_has_been_reset": "Your Two Factor TOTP has been reset.", "Your_web_browser_blocked_Rocket_Chat_from_opening_tab": "Your web browser blocked Rocket.Chat from opening a new tab.", "Your_workspace_is_ready": "Your workspace is ready to use 🎉", + "Youre_not_a_part_of__channel__and_I_mentioned_you_there": "You're not a part of {{channel}} and I mentioned you there", "Zapier": "Zapier", "registration.page.login.errors.wrongCredentials": "User not found or incorrect password", "registration.page.login.errors.invalidEmail": "Invalid Email", diff --git a/apps/meteor/server/modules/core-apps/mention.module.ts b/apps/meteor/server/modules/core-apps/mention.module.ts new file mode 100644 index 00000000000..ac615def8a5 --- /dev/null +++ b/apps/meteor/server/modules/core-apps/mention.module.ts @@ -0,0 +1,125 @@ +import { api } from '@rocket.chat/core-services'; +import type { IUiKitCoreApp } from '@rocket.chat/core-services'; +import type { IMessage } from '@rocket.chat/core-typings'; +import { Subscriptions, Messages } from '@rocket.chat/models'; +import { Meteor } from 'meteor/meteor'; + +import { processWebhookMessage } from '../../../app/lib/server/functions/processWebhookMessage'; +import { addUsersToRoomMethod } from '../../../app/lib/server/methods/addUsersToRoom'; +import { i18n } from '../../lib/i18n'; +import { roomCoordinator } from '../../lib/rooms/roomCoordinator'; + +const retrieveMentionsFromPayload = (stringifiedMentions: string): Exclude => { + try { + const mentions = JSON.parse(stringifiedMentions); + if (!Array.isArray(mentions) || !mentions.length || !('username' in mentions[0])) { + throw new Error('Invalid payload'); + } + return mentions; + } catch (error) { + throw new Error('Invalid payload'); + } +}; + +export class MentionModule implements IUiKitCoreApp { + appId = 'mention-core'; + + async blockAction(payload: any): Promise { + const { + actionId, + payload: { value: stringifiedMentions, blockId: referenceMessageId }, + } = payload; + + const mentions = retrieveMentionsFromPayload(stringifiedMentions); + + const usernames = mentions.map(({ username }) => username); + + const message = await Messages.findOneById(referenceMessageId, { projection: { _id: 1, tmid: 1 } }); + + if (!message) { + throw new Error('Mention bot - Failed to retrieve message information'); + } + + const joinedUsernames = `@${usernames.join(', @')}`; + + if (actionId === 'dismiss') { + void api.broadcast('notify.ephemeralMessage', payload.user._id, payload.room, { + msg: i18n.t( + 'You_mentioned___mentions__but_theyre_not_in_this_room', + { + mentions: joinedUsernames, + }, + payload.user.language, + ), + _id: payload.message, + tmid: message.tmid, + mentions, + }); + return; + } + + if (actionId === 'add-users') { + void addUsersToRoomMethod(payload.user._id, { rid: payload.room, users: usernames as string[] }, payload.user); + void api.broadcast('notify.ephemeralMessage', payload.user._id, payload.room, { + msg: i18n.t( + 'You_mentioned___mentions__but_theyre_not_in_this_room', + { + mentions: joinedUsernames, + }, + payload.user.language, + ), + tmid: message.tmid, + _id: payload.message, + mentions, + }); + return; + } + + if (actionId === 'share-message') { + const sub = await Subscriptions.findOneByRoomIdAndUserId(payload.room, payload.user._id, { projection: { t: 1, rid: 1, name: 1 } }); + // this should exist since the event is fired from withing the room (e.g the user sent a message) + if (!sub) { + throw new Error('Mention bot - Failed to retrieve room information'); + } + + const roomPath = roomCoordinator.getRouteLink(sub.t, { rid: sub.rid, name: sub.name }); + if (!roomPath) { + throw new Error('Mention bot - Failed to retrieve path to room'); + } + + const messageText = i18n.t( + 'Youre_not_a_part_of__channel__and_I_mentioned_you_there', + { + channel: `#${sub.name}`, + }, + payload.user.language, + ); + + const link = new URL(Meteor.absoluteUrl(roomPath)); + link.searchParams.set('msg', message._id); + const text = `[ ](${link.toString()})\n${messageText}`; + + // forwards message to all DMs + await processWebhookMessage( + { + roomId: mentions.map(({ _id }) => _id), + text, + }, + payload.user, + ); + + void api.broadcast('notify.ephemeralMessage', payload.user._id, payload.room, { + msg: i18n.t( + 'You_mentioned___mentions__but_theyre_not_in_this_room_You_let_them_know_via_dm', + { + mentions: joinedUsernames, + }, + payload.user.language, + ), + tmid: message.tmid, + _id: payload.message, + mentions, + }); + } + } +} diff --git a/apps/meteor/server/startup/coreApps.ts b/apps/meteor/server/startup/coreApps.ts index 14b2234f660..9638d99ab55 100644 --- a/apps/meteor/server/startup/coreApps.ts +++ b/apps/meteor/server/startup/coreApps.ts @@ -1,5 +1,6 @@ import { BannerModule } from '../modules/core-apps/banner.module'; import { CloudAnnouncementsModule } from '../modules/core-apps/cloudAnnouncements.module'; +import { MentionModule } from '../modules/core-apps/mention.module'; import { Nps } from '../modules/core-apps/nps.module'; import { VideoConfModule } from '../modules/core-apps/videoconf.module'; import { registerCoreApp } from '../services/uikit-core-app/service'; @@ -8,3 +9,4 @@ registerCoreApp(new CloudAnnouncementsModule()); registerCoreApp(new Nps()); registerCoreApp(new BannerModule()); registerCoreApp(new VideoConfModule()); +registerCoreApp(new MentionModule()); diff --git a/apps/meteor/tests/e2e/message-mentions.spec.ts b/apps/meteor/tests/e2e/message-mentions.spec.ts index aa3e5b73036..7645d5b1447 100644 --- a/apps/meteor/tests/e2e/message-mentions.spec.ts +++ b/apps/meteor/tests/e2e/message-mentions.spec.ts @@ -1,9 +1,26 @@ +import { faker } from '@faker-js/faker'; + import { Users } from './fixtures/userStates'; import { HomeChannel } from './page-objects'; +import { createTargetPrivateChannel } from './utils'; import { test, expect } from './utils/test'; + test.use({ storageState: Users.admin.state }); +const getMentionText = (username: string, kind?: number): string => { + if (kind === 1) { + return `You mentioned ${username}, but they're not in this room.`; + } + if (kind === 2) { + return `You mentioned ${username}, but they're not in this room. You can ask a room admin to add them.`; + } + if (kind === 3) { + return `You mentioned ${username}, but they're not in this room. You let them know via DM.`; + } + return `Hello @${username}, how are you`; +}; + test.describe.serial('message-mentions', () => { let poHomeChannel: HomeChannel; @@ -20,4 +37,180 @@ test.describe.serial('message-mentions', () => { await expect(poHomeChannel.content.messagePopupUsers.locator('role=listitem >> text="all"')).toBeVisible(); await expect(poHomeChannel.content.messagePopupUsers.locator('role=listitem >> text="here"')).toBeVisible(); }); + + test.describe('users not in channel', () => { + let targetChannel: string; + let targetChannel2: string; + test.beforeAll(async ({ api }) => { + targetChannel = await createTargetPrivateChannel(api); + }); + + test('all actions', async ({ page }) => { + const adminPage = new HomeChannel(page); + const mentionText = getMentionText(Users.user1.data.username, 1); + + await test.step('receive bot message', async () => { + await adminPage.sidenav.openChat(targetChannel); + await adminPage.content.sendMessage(getMentionText(Users.user1.data.username)); + await expect(adminPage.content.lastUserMessage.locator('.rcx-message-block')).toContainText(mentionText); + }); + + await test.step('show "Do nothing" action', async () => { + await expect(adminPage.content.lastUserMessage.locator('button >> text="Do nothing"')).toBeVisible(); + }); + await test.step('show "Add them" action', async () => { + await expect(adminPage.content.lastUserMessage.locator('button >> text="Add them"')).toBeVisible(); + }); + await test.step('show "Let them know" action', async () => { + await expect(adminPage.content.lastUserMessage.locator('button >> text="Let them know"')).toBeVisible(); + }); + + await test.step('dismiss', async () => { + await adminPage.content.lastUserMessage.locator('button >> text="Do nothing"').click(); + }); + + await test.step('receive second bot message', async () => { + await adminPage.content.sendMessage(getMentionText(Users.user1.data.username)); + await expect(adminPage.content.lastUserMessage.locator('.rcx-message-block')).toContainText(mentionText); + }); + await test.step('send message to users', async () => { + await adminPage.content.lastUserMessage.locator('button >> text="Let them know"').click(); + await expect(adminPage.content.lastUserMessageBody).toContainText(getMentionText(Users.user1.data.username, 3)); + }); + + await test.step('receive third bot message', async () => { + await adminPage.content.sendMessage(getMentionText(Users.user1.data.username)); + await expect(adminPage.content.lastUserMessage.locator('.rcx-message-block')).toContainText(mentionText); + }); + await test.step('add users to room', async () => { + await adminPage.content.lastUserMessage.locator('button >> text="Add them"').click(); + await expect(adminPage.content.lastSystemMessageBody).toContainText('added'); + }); + }); + + test.describe(() => { + test.use({ storageState: Users.user1.state }); + + test('dismiss and share message actions', async ({ page }) => { + const mentionText = getMentionText(Users.user2.data.username, 1); + const userPage = new HomeChannel(page); + + await test.step('receive bot message', async () => { + await userPage.sidenav.openChat(targetChannel); + await userPage.content.sendMessage(getMentionText(Users.user2.data.username)); + await expect(userPage.content.lastUserMessage.locator('.rcx-message-block')).toContainText(mentionText); + }); + + await test.step('show "Do nothing" action', async () => { + await expect(userPage.content.lastUserMessage.locator('button >> text="Do nothing"')).toBeVisible(); + }); + await test.step('show "Let them know" action', async () => { + await expect(userPage.content.lastUserMessage.locator('button >> text="Let them know"')).toBeVisible(); + }); + await test.step('not show "Add them action', async () => { + await expect(userPage.content.lastUserMessage.locator('button >> text="Add them"')).not.toBeVisible(); + }); + + await test.step('dismiss', async () => { + await userPage.content.lastUserMessage.locator('button >> text="Do nothing"').click(); + }); + + await test.step('receive second bot message', async () => { + await userPage.sidenav.openChat(targetChannel); + await userPage.content.sendMessage(getMentionText(Users.user2.data.username)); + await expect(userPage.content.lastUserMessage.locator('.rcx-message-block')).toContainText(mentionText); + }); + await test.step('send message to users', async () => { + await userPage.content.lastUserMessage.locator('button >> text="Let them know"').click(); + await expect(userPage.content.lastUserMessageBody).toContainText(getMentionText(Users.user2.data.username, 3)); + }); + }); + }) + + test.describe(() => { + test.use({ storageState: Users.user1.state }); + test.beforeAll(async ({ api }) => { + expect((await api.post('/permissions.update', { permissions: [{ '_id': 'create-d', 'roles': ['admin'] }] })).status()).toBe(200); + }); + + test.afterAll(async ({ api }) => { + expect((await api.post('/permissions.update', { permissions: [{ '_id': 'create-d', 'roles': ['admin', 'user', 'bot', 'app'] }] })).status()).toBe(200); + }); + + test('dismiss and add users actions', async ({ page }) => { + const mentionText = getMentionText(Users.user2.data.username, 1); + const userPage = new HomeChannel(page); + + await test.step('create private room', async () => { + targetChannel2 = faker.string.uuid(); + + await poHomeChannel.sidenav.openNewByLabel('Channel'); + await poHomeChannel.sidenav.inputChannelName.type(targetChannel2); + await poHomeChannel.sidenav.btnCreate.click(); + + await expect(page).toHaveURL(`/group/${targetChannel2}`); + }) + + await test.step('receive bot message', async () => { + await userPage.sidenav.openChat(targetChannel2); + await userPage.content.sendMessage(getMentionText(Users.user2.data.username)); + await expect(userPage.content.lastUserMessage.locator('.rcx-message-block')).toContainText(mentionText); + }); + await test.step('show "Do nothing" action', async () => { + await expect(userPage.content.lastUserMessage.locator('button >> text="Do nothing"')).toBeVisible(); + }); + await test.step('show "Add them" action', async () => { + await expect(userPage.content.lastUserMessage.locator('button >> text="Add them"')).toBeVisible(); + }); + await test.step('not show "Let them know" action', async () => { + await expect(userPage.content.lastUserMessage.locator('button >> text="Let them know"')).not.toBeVisible(); + }); + + await test.step('dismiss', async () => { + await userPage.content.lastUserMessage.locator('button >> text="Do nothing"').click(); + }); + + await test.step('receive second bot message', async () => { + await userPage.sidenav.openChat(targetChannel2); + await userPage.content.sendMessage(getMentionText(Users.user2.data.username)); + await expect(userPage.content.lastUserMessage.locator('.rcx-message-block')).toContainText(mentionText); + }); + await test.step('add users to room', async () => { + await userPage.content.lastUserMessage.locator('button >> text="Add them"').click(); + await expect(userPage.content.lastSystemMessageBody).toContainText('added'); + }); + }); + }); + + test.describe(() => { + test.use({ storageState: Users.user2.state }); + test.beforeAll(async ({ api }) => { + expect((await api.post('/permissions.update', { permissions: [{ '_id': 'create-d', 'roles': ['admin'] }] })).status()).toBe(200); + }); + + test.afterAll(async ({ api }) => { + expect((await api.post('/permissions.update', { permissions: [{ '_id': 'create-d', 'roles': ['admin', 'user', 'bot', 'app'] }] })).status()).toBe(200); + }); + test('no actions', async ({ page }) => { + const userPage = new HomeChannel(page); + + await test.step('receive bot message', async () => { + await userPage.sidenav.openChat(targetChannel2); + await userPage.content.sendMessage(getMentionText(Users.user3.data.username)); + await expect(userPage.content.lastUserMessage.locator('.rcx-message-block')).toContainText(getMentionText(Users.user3.data.username, 2)); + }); + + await test.step('not show "Do nothing" action', async () => { + await expect(userPage.content.lastUserMessage.locator('button >> text="Do nothing"')).not.toBeVisible(); + }); + await test.step('not show "Add them" action', async () => { + await expect(userPage.content.lastUserMessage.locator('button >> text="Add them"')).not.toBeVisible(); + }); + await test.step('not show "Let them know" action', async () => { + await expect(userPage.content.lastUserMessage.locator('button >> text="Let them know"')).not.toBeVisible(); + }); + }); + }) + + }) }); -- GitLab From 13ef78afa3aef35599319fb78c22d582409be37a Mon Sep 17 00:00:00 2001 From: Douglas Fabris Date: Fri, 15 Dec 2023 11:27:54 -0300 Subject: [PATCH 037/329] regression: AlsoSendToChannel checkbox alignment (#31246) --- .../contextualBar/Threads/components/ThreadChat.tsx | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/apps/meteor/client/views/room/contextualBar/Threads/components/ThreadChat.tsx b/apps/meteor/client/views/room/contextualBar/Threads/components/ThreadChat.tsx index b5ce5ea5c94..510d4f3e801 100644 --- a/apps/meteor/client/views/room/contextualBar/Threads/components/ThreadChat.tsx +++ b/apps/meteor/client/views/room/contextualBar/Threads/components/ThreadChat.tsx @@ -110,17 +110,17 @@ const ThreadChat = ({ mainMessage }: ThreadChatProps) => { onUploadFiles={handleUploadFiles} tshow={sendToChannel} > - - - - {t('Also_send_to_channel')} - + + setSendToChannel((checked) => !checked)} name='alsoSendThreadToChannel' /> + + {t('Also_send_to_channel')} + -- GitLab From 83bcf04664446677f0400e5ca491c9866f08d259 Mon Sep 17 00:00:00 2001 From: Yash Rajpal <58601732+yash-rajpal@users.noreply.github.com> Date: Fri, 15 Dec 2023 21:04:13 +0530 Subject: [PATCH 038/329] fix: connected users search on spotlight for user mentions (#31134) Co-authored-by: Matheus Barbosa Silva <36537004+matheusbsilva137@users.noreply.github.com> Co-authored-by: Guilherme Jun Grillo <48109548+guijun13@users.noreply.github.com> --- .changeset/pretty-eyes-sleep.md | 5 +++++ apps/meteor/server/lib/spotlight.js | 4 ++-- 2 files changed, 7 insertions(+), 2 deletions(-) create mode 100644 .changeset/pretty-eyes-sleep.md diff --git a/.changeset/pretty-eyes-sleep.md b/.changeset/pretty-eyes-sleep.md new file mode 100644 index 00000000000..04b276b18ff --- /dev/null +++ b/.changeset/pretty-eyes-sleep.md @@ -0,0 +1,5 @@ +--- +'@rocket.chat/meteor': patch +--- + +Fixed issue searching connected users on spotlight diff --git a/apps/meteor/server/lib/spotlight.js b/apps/meteor/server/lib/spotlight.js index 62fcdc3a66b..62218bd5928 100644 --- a/apps/meteor/server/lib/spotlight.js +++ b/apps/meteor/server/lib/spotlight.js @@ -236,7 +236,7 @@ export class Spotlight { } } - if (users.length === 0 && canListOutsiders) { + if (users.length === 0 && canListOutsiders && text) { const exactMatch = await Users.findOneByUsernameIgnoringCase(text, { projection: options.projection, readPreference: options.readPreference, @@ -265,7 +265,7 @@ export class Spotlight { if (await this._searchOutsiderUsers(searchParams)) { return users; } - } else if (await this._searchConnectedUsers(userId, searchParams)) { + } else if (await this._searchConnectedUsers(userId, searchParams, 'd')) { return users; } -- GitLab From c19d64093bc11842d9b12ef2c38424c70a4335a7 Mon Sep 17 00:00:00 2001 From: Rodrigo Nascimento Date: Fri, 15 Dec 2023 14:42:09 -0300 Subject: [PATCH 039/329] chore: Add slide window config to round trip metric (#31247) --- .../app/metrics/server/lib/collectMetrics.ts | 15 ++++----------- apps/meteor/app/metrics/server/lib/metrics.ts | 3 +++ apps/meteor/package.json | 2 +- yarn.lock | 13 +++++++++++-- 4 files changed, 19 insertions(+), 14 deletions(-) diff --git a/apps/meteor/app/metrics/server/lib/collectMetrics.ts b/apps/meteor/app/metrics/server/lib/collectMetrics.ts index 8213e27ed4f..136686f49c9 100644 --- a/apps/meteor/app/metrics/server/lib/collectMetrics.ts +++ b/apps/meteor/app/metrics/server/lib/collectMetrics.ts @@ -175,17 +175,10 @@ const updatePrometheusConfig = async (): Promise => { clearInterval(resetTimer); if (is.resetInterval) { resetTimer = setInterval(() => { - client.register - .getMetricsAsArray() - .then((metrics) => { - metrics.forEach((metric) => { - // @ts-expect-error Property 'hashMap' does not exist on type 'metric'. - metric.hashMap = {}; - }); - }) - .catch((err) => { - SystemLogger.error({ msg: 'Error while collecting metrics', err }); - }); + client.register.getMetricsAsArray().forEach((metric) => { + // @ts-expect-error Property 'hashMap' does not exist on type 'metric'. + metric.hashMap = {}; + }); }, is.resetInterval); } diff --git a/apps/meteor/app/metrics/server/lib/metrics.ts b/apps/meteor/app/metrics/server/lib/metrics.ts index 11954322a41..00bbedce287 100644 --- a/apps/meteor/app/metrics/server/lib/metrics.ts +++ b/apps/meteor/app/metrics/server/lib/metrics.ts @@ -67,6 +67,9 @@ export const metrics = { name: 'rocketchat_messages_roundtrip_time_summary', help: 'time spent by a message from save to receive back', percentiles, + maxAgeSeconds: 60, + ageBuckets: 5, + // pruneAgedBuckets: true, // Type not added to prom-client on 14.2 https://github.com/siimon/prom-client/pull/558 }), ddpSessions: new client.Gauge({ diff --git a/apps/meteor/package.json b/apps/meteor/package.json index 92f2bfdf8b1..5c26b2af58c 100644 --- a/apps/meteor/package.json +++ b/apps/meteor/package.json @@ -388,7 +388,7 @@ "pdfjs-dist": "^2.13.216", "pino": "^8.15.0", "postis": "^2.2.0", - "prom-client": "^14.0.1", + "prom-client": "^14.2.0", "prometheus-gc-stats": "^0.6.5", "proxy-from-env": "^1.1.0", "psl": "^1.8.0", diff --git a/yarn.lock b/yarn.lock index 9dbe7a2e732..da83e044e00 100644 --- a/yarn.lock +++ b/yarn.lock @@ -9722,7 +9722,7 @@ __metadata: postcss-url: ^10.1.3 postis: ^2.2.0 prettier: ~2.8.8 - prom-client: ^14.0.1 + prom-client: ^14.2.0 prometheus-gc-stats: ^0.6.5 proxy-from-env: ^1.1.0 proxyquire: ^2.1.3 @@ -33535,7 +33535,7 @@ __metadata: languageName: node linkType: hard -"prom-client@npm:^14.0.0, prom-client@npm:^14.0.1": +"prom-client@npm:^14.0.0": version: 14.0.1 resolution: "prom-client@npm:14.0.1" dependencies: @@ -33544,6 +33544,15 @@ __metadata: languageName: node linkType: hard +"prom-client@npm:^14.2.0": + version: 14.2.0 + resolution: "prom-client@npm:14.2.0" + dependencies: + tdigest: ^0.1.1 + checksum: d4c04e57616c72643dd02862d0d4bde09cf8869a19d0aef5e7b785e6e27d02439b66cdc165e3492f62d579fa91579183820870cc757a09b99399d2d02f46b9f1 + languageName: node + linkType: hard + "prometheus-gc-stats@npm:^0.6.5": version: 0.6.5 resolution: "prometheus-gc-stats@npm:0.6.5" -- GitLab From b1426ed098e3e98d1314ed04f8522a7bc1eee1e9 Mon Sep 17 00:00:00 2001 From: Marcos Spessatto Defendi Date: Fri, 15 Dec 2023 15:09:58 -0300 Subject: [PATCH 040/329] ci: collect coverage in any branch except for releases (#31229) Co-authored-by: Guilherme Gazzo --- .github/actions/meteor-build/action.yml | 8 +------- .github/workflows/ci.yml | 4 ++-- 2 files changed, 3 insertions(+), 9 deletions(-) diff --git a/.github/actions/meteor-build/action.yml b/.github/actions/meteor-build/action.yml index 0229e5bb5ba..e5b5d774078 100644 --- a/.github/actions/meteor-build/action.yml +++ b/.github/actions/meteor-build/action.yml @@ -103,9 +103,8 @@ runs: working-directory: ./apps/meteor run: meteor reset - - name: Build Rocket.Chat From Pull Request + - name: Build Rocket.Chat shell: bash - if: startsWith(github.ref, 'refs/pull/') == true env: METEOR_PROFILE: 1000 BABEL_ENV: ${{ inputs.coverage == 'true' && 'coverage' || '' }} @@ -118,11 +117,6 @@ runs: yarn build:ci -- --directory /tmp/dist - - name: Build Rocket.Chat - shell: bash - if: startsWith(github.ref, 'refs/pull/') != true - run: yarn build:ci -- --directory /tmp/dist - - name: Prepare build shell: bash run: | diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index be796470323..fdcea4c4213 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -183,7 +183,7 @@ jobs: - uses: ./.github/actions/meteor-build with: node-version: ${{ needs.release-versions.outputs.node-version }} - coverage: true + coverage: ${{ github.event_name != 'release' }} build-prod: name: 📦 Meteor Build - official @@ -207,7 +207,7 @@ jobs: - uses: ./.github/actions/meteor-build with: node-version: ${{ needs.release-versions.outputs.node-version }} - coverage: false + coverage: ${{ github.event_name != 'release' }} build-gh-docker-coverage: name: 🚢 Build Docker Images for Testing -- GitLab From 8faec71cc4bad6ffc0e1ad2226dfd3966568b37d Mon Sep 17 00:00:00 2001 From: Diego Sampaio Date: Fri, 15 Dec 2023 15:22:04 -0300 Subject: [PATCH 041/329] refactor: convert canned response callback (#31211) --- .../server/hooks/cannedResponses.ts | 20 +++ .../hooks/onMessageSentParsePlaceholder.ts | 74 -------- .../ee/app/canned-responses/server/index.ts | 2 +- .../messages/BeforeSaveCannedResponse.ts | 93 ++++++++++ .../BeforeSaveCannedResponse.tests.ts | 169 ++++++++++++++++++ .../server/services/messages/service.ts | 13 +- 6 files changed, 291 insertions(+), 80 deletions(-) create mode 100644 apps/meteor/ee/app/canned-responses/server/hooks/cannedResponses.ts delete mode 100644 apps/meteor/ee/app/canned-responses/server/hooks/onMessageSentParsePlaceholder.ts create mode 100644 apps/meteor/ee/server/hooks/messages/BeforeSaveCannedResponse.ts create mode 100644 apps/meteor/ee/tests/unit/server/hooks/messages/BeforeSaveCannedResponse.tests.ts diff --git a/apps/meteor/ee/app/canned-responses/server/hooks/cannedResponses.ts b/apps/meteor/ee/app/canned-responses/server/hooks/cannedResponses.ts new file mode 100644 index 00000000000..20b94619eb5 --- /dev/null +++ b/apps/meteor/ee/app/canned-responses/server/hooks/cannedResponses.ts @@ -0,0 +1,20 @@ +import { License } from '@rocket.chat/license'; + +import { settings } from '../../../../../app/settings/server'; +import { BeforeSaveCannedResponse } from '../../../../server/hooks/messages/BeforeSaveCannedResponse'; + +void License.onToggledFeature('canned-responses', { + up: () => { + // when the license is enabled, we need to check if the feature is enabled + BeforeSaveCannedResponse.enabled = settings.get('Canned_Responses_Enable'); + }, + down: () => { + // when the license is disabled, we can just disable the feature + BeforeSaveCannedResponse.enabled = false; + }, +}); + +// we also need to check if the feature is enabled via setting, which is only possible when there is a license +settings.watch('Canned_Responses_Enable', (value) => { + BeforeSaveCannedResponse.enabled = value; +}); diff --git a/apps/meteor/ee/app/canned-responses/server/hooks/onMessageSentParsePlaceholder.ts b/apps/meteor/ee/app/canned-responses/server/hooks/onMessageSentParsePlaceholder.ts deleted file mode 100644 index 1cb016a33be..00000000000 --- a/apps/meteor/ee/app/canned-responses/server/hooks/onMessageSentParsePlaceholder.ts +++ /dev/null @@ -1,74 +0,0 @@ -import type { IMessage, IOmnichannelRoom } from '@rocket.chat/core-typings'; -import { isOmnichannelRoom } from '@rocket.chat/core-typings'; -import { LivechatVisitors, Rooms, Users } from '@rocket.chat/models'; -import get from 'lodash.get'; - -import { settings } from '../../../../../app/settings/server'; -import { callbacks } from '../../../../../lib/callbacks'; - -const placeholderFields = { - 'contact.name': { - from: 'visitor', - dataKey: 'name', - }, - 'contact.email': { - from: 'visitor', - dataKey: 'visitorEmails[0].address', - }, - 'contact.phone': { - from: 'visitor', - dataKey: 'phone[0].phoneNumber', - }, - 'agent.name': { - from: 'agent', - dataKey: 'name', - }, - 'agent.email': { - from: 'agent', - dataKey: 'emails[0].address', - }, -}; - -const replaceAll = (text: string, old: string, replace: string): string => text.replace(new RegExp(old, 'g'), replace); - -const handleBeforeSaveMessage = async (message: IMessage, room?: IOmnichannelRoom | null): Promise => { - if (!message.msg || message.msg === '') { - return message; - } - - room = room?._id ? room : await Rooms.findOneById(message.rid); - if (!room || !isOmnichannelRoom(room)) { - return message; - } - - let messageText = message.msg; - const agentId = room?.servedBy?._id; - if (!agentId) { - return message; - } - const visitorId = room?.v?._id; - const agent = (await Users.findOneById(agentId, { projection: { name: 1, _id: 1, emails: 1 } })) || {}; - const visitor = visitorId && ((await LivechatVisitors.findOneEnabledById(visitorId, {})) || {}); - - Object.keys(placeholderFields).map((field) => { - const templateKey = `{{${field}}}`; - const placeholderConfig = placeholderFields[field as keyof typeof placeholderFields]; - const from = placeholderConfig.from === 'agent' ? agent : visitor; - const data = get(from, placeholderConfig.dataKey, ''); - messageText = replaceAll(messageText, templateKey, data); - - return messageText; - }); - - message.msg = messageText; - return message; -}; - -settings.watch('Canned_Responses_Enable', (value) => { - if (!value) { - callbacks.remove('beforeSaveMessage', 'canned-responses-replace-placeholders'); - return; - } - - callbacks.add('beforeSaveMessage', handleBeforeSaveMessage, callbacks.priority.HIGH * 2, 'canned-responses-replace-placeholders'); -}); diff --git a/apps/meteor/ee/app/canned-responses/server/index.ts b/apps/meteor/ee/app/canned-responses/server/index.ts index 99254b03738..01ee26b38f9 100644 --- a/apps/meteor/ee/app/canned-responses/server/index.ts +++ b/apps/meteor/ee/app/canned-responses/server/index.ts @@ -5,7 +5,7 @@ await License.onLicense('canned-responses', async () => { await import('./permissions'); await import('./hooks/onRemoveAgentDepartment'); await import('./hooks/onSaveAgentDepartment'); - await import('./hooks/onMessageSentParsePlaceholder'); + await import('./hooks/cannedResponses'); await import('./methods/saveCannedResponse'); await import('./methods/removeCannedResponse'); diff --git a/apps/meteor/ee/server/hooks/messages/BeforeSaveCannedResponse.ts b/apps/meteor/ee/server/hooks/messages/BeforeSaveCannedResponse.ts new file mode 100644 index 00000000000..eca9e6a4eca --- /dev/null +++ b/apps/meteor/ee/server/hooks/messages/BeforeSaveCannedResponse.ts @@ -0,0 +1,93 @@ +import { isILivechatVisitor, isOmnichannelRoom } from '@rocket.chat/core-typings'; +import type { IMessage, IRoom, IUser } from '@rocket.chat/core-typings'; +import { LivechatVisitors, Users } from '@rocket.chat/models'; +import get from 'lodash.get'; +import mem from 'mem'; + +const placeholderFields = { + 'contact.name': { + from: 'visitor', + dataKey: 'name', + }, + 'contact.email': { + from: 'visitor', + dataKey: 'visitorEmails[0].address', + }, + 'contact.phone': { + from: 'visitor', + dataKey: 'phone[0].phoneNumber', + }, + 'agent.name': { + from: 'agent', + dataKey: 'name', + }, + 'agent.email': { + from: 'agent', + dataKey: 'emails[0].address', + }, +}; + +export class BeforeSaveCannedResponse { + static enabled = false; + + private getUser = mem((userId: string) => Users.findOneById(userId, { projection: { name: 1, _id: 1, emails: 1 } }), { + maxAge: 1000 * 30, + }); + + private getVisitor = mem((visitorId: string) => LivechatVisitors.findOneEnabledById(visitorId), { + maxAge: 1000 * 30, + }); + + async replacePlaceholders({ + message, + room, + user, + }: { + message: IMessage; + room: IRoom; + user: Pick; + }): Promise { + // If the feature is disabled, return the message as is + if (!BeforeSaveCannedResponse.enabled) { + return message; + } + + if (!message.msg || message.msg === '') { + return message; + } + + if (!isOmnichannelRoom(room)) { + return message; + } + + // do not replace placeholders for visitors + if (!user || isILivechatVisitor(user)) { + return message; + } + + const agentId = room?.servedBy?._id; + if (!agentId) { + return message; + } + + const getAgent = (agentId: string) => { + if (agentId === user._id) { + return user; + } + + return this.getUser(agentId); + }; + + message.msg = await Object.keys(placeholderFields).reduce(async (messageText, field) => { + const placeholderConfig = placeholderFields[field as keyof typeof placeholderFields]; + + const from = placeholderConfig.from === 'agent' ? await getAgent(agentId) : await this.getVisitor(room.v._id); + + const data = get(from, placeholderConfig.dataKey, ''); + + return (await messageText).replace(new RegExp(`{{${field}}}`, 'g'), data); + }, Promise.resolve(message.msg)); + + return message; + } +} diff --git a/apps/meteor/ee/tests/unit/server/hooks/messages/BeforeSaveCannedResponse.tests.ts b/apps/meteor/ee/tests/unit/server/hooks/messages/BeforeSaveCannedResponse.tests.ts new file mode 100644 index 00000000000..56c4b782794 --- /dev/null +++ b/apps/meteor/ee/tests/unit/server/hooks/messages/BeforeSaveCannedResponse.tests.ts @@ -0,0 +1,169 @@ +import { registerModel } from '@rocket.chat/models'; +import { expect } from 'chai'; +import { before, describe, it } from 'mocha'; +import sinon from 'sinon'; + +import { BaseRaw } from '../../../../../../server/models/raw/BaseRaw'; +import { BeforeSaveCannedResponse } from '../../../../../server/hooks/messages/BeforeSaveCannedResponse'; + +const createMessage = (msg?: string, extra: any = {}) => ({ + _id: 'msg-id', + rid: 'GENERAL', + ts: new Date(), + u: { + _id: 'user-id', + username: 'user', + }, + _updatedAt: new Date(), + msg: msg as string, + ...extra, +}); + +const createRoom = (extra: any = {}) => ({ + _id: 'GENERAL', + name: 'general', + ...extra, +}); + +const createUser = (extra: any = {}) => ({ + _id: 'user-id', + name: 'User Name', + username: 'user', + emails: [{ address: 'user@user.com' }], + ...extra, +}); + +class LivechatVisitorsModel extends BaseRaw { + findOneEnabledById() { + return {}; + } +} + +class UsersModel extends BaseRaw { + async findOneById() { + return { + name: 'John Doe Agent', + }; + } +} + +const db = { + collection: () => ({}), +}; + +describe('Omnichannel canned responses', () => { + before(() => { + registerModel('ILivechatVisitorsModel', () => new LivechatVisitorsModel(db as unknown as any, 'visitor')); + registerModel('IUsersModel', () => new UsersModel(db as unknown as any, 'user')); + }); + + it('should do nothing if canned response is disabled', async () => { + BeforeSaveCannedResponse.enabled = false; + + const canned = new BeforeSaveCannedResponse(); + + const message = await canned.replacePlaceholders({ + message: createMessage('{{agent.name}}'), + room: createRoom({ t: 'l', servedBy: { _id: 'agent' }, v: { _id: 'visitor' } }), + user: createUser(), + }); + + expect(message).to.have.property('msg', '{{agent.name}}'); + }); + + it('should do nothing if not an omnichannel room', async () => { + BeforeSaveCannedResponse.enabled = true; + + const canned = new BeforeSaveCannedResponse(); + + const message = await canned.replacePlaceholders({ + message: createMessage('{{agent.name}}'), + room: createRoom(), + user: createUser(), + }); + + expect(message).to.have.property('msg', '{{agent.name}}'); + }); + + it('should do nothing if the message is from a visitor', async () => { + BeforeSaveCannedResponse.enabled = true; + + const canned = new BeforeSaveCannedResponse(); + + const message = await canned.replacePlaceholders({ + message: createMessage('{{agent.name}}'), + room: createRoom({ t: 'l', servedBy: { _id: 'agent' }, v: { _id: 'visitor' } }), + user: createUser({ token: 'visitor-token' }), + }); + + expect(message).to.have.property('msg', '{{agent.name}}'); + }); + + it('should do nothing if room is not served by an agent', async () => { + BeforeSaveCannedResponse.enabled = true; + + const canned = new BeforeSaveCannedResponse(); + + const message = await canned.replacePlaceholders({ + message: createMessage('{{agent.name}}'), + room: createRoom({ t: 'l', v: { _id: 'visitor' } }), + user: createUser(), + }); + + expect(message).to.have.property('msg', '{{agent.name}}'); + }); + + it('should do nothing for an empty message', async () => { + BeforeSaveCannedResponse.enabled = true; + + const canned = new BeforeSaveCannedResponse(); + + const message = await canned.replacePlaceholders({ + message: createMessage(''), + room: createRoom({ t: 'l', servedBy: { _id: 'agent' }, v: { _id: 'visitor' } }), + user: createUser(), + }); + + expect(message).to.have.property('msg', ''); + }); + + it('should replace {{agent.name}} without finding the user from DB (sender is the agent of room)', async () => { + BeforeSaveCannedResponse.enabled = true; + + const usersModel = new UsersModel(db as unknown as any, 'user'); + const spy = sinon.spy(usersModel, 'findOneById'); + + registerModel('IUsersModel', () => usersModel); + + const canned = new BeforeSaveCannedResponse(); + + const message = await canned.replacePlaceholders({ + message: createMessage('{{agent.name}}'), + room: createRoom({ t: 'l', servedBy: { _id: 'agent' }, v: { _id: 'visitor' } }), + user: createUser({ _id: 'agent', name: 'User As Agent' }), + }); + + expect(message).to.have.property('msg', 'User As Agent'); + expect(spy.called).to.be.false; + }); + + it('should replace {{agent.name}} when canned response is enabled', async () => { + BeforeSaveCannedResponse.enabled = true; + + const usersModel = new UsersModel(db as unknown as any, 'user'); + const spy = sinon.spy(usersModel, 'findOneById'); + + registerModel('IUsersModel', () => usersModel); + + const canned = new BeforeSaveCannedResponse(); + + const message = await canned.replacePlaceholders({ + message: createMessage('{{agent.name}}'), + room: createRoom({ t: 'l', servedBy: { _id: 'agent' }, v: { _id: 'visitor' } }), + user: createUser(), + }); + + expect(message).to.have.property('msg', 'John Doe Agent'); + expect(spy.called).to.be.true; + }); +}); diff --git a/apps/meteor/server/services/messages/service.ts b/apps/meteor/server/services/messages/service.ts index 885152c594e..85d71d08ae1 100644 --- a/apps/meteor/server/services/messages/service.ts +++ b/apps/meteor/server/services/messages/service.ts @@ -10,6 +10,7 @@ import { executeSendMessage } from '../../../app/lib/server/methods/sendMessage' import { executeSetReaction } from '../../../app/reactions/server/setReaction'; import { settings } from '../../../app/settings/server'; import { getUserAvatarURL } from '../../../app/utils/server/getUserAvatarURL'; +import { BeforeSaveCannedResponse } from '../../../ee/server/hooks/messages/BeforeSaveCannedResponse'; import { broadcastMessageSentEvent } from '../../modules/watchers/lib/messages'; import { BeforeSaveBadWords } from './hooks/BeforeSaveBadWords'; import { BeforeSaveCheckMAC } from './hooks/BeforeSaveCheckMAC'; @@ -31,6 +32,8 @@ export class MessageService extends ServiceClassInternal implements IMessageServ private jumpToMessage: BeforeSaveJumpToMessage; + private cannedResponse: BeforeSaveCannedResponse; + private markdownParser: BeforeSaveMarkdownParser; private checkMAC: BeforeSaveCheckMAC; @@ -53,7 +56,7 @@ export class MessageService extends ServiceClassInternal implements IMessageServ return (user && getUserAvatarURL(user)) || ''; }, }); - + this.cannedResponse = new BeforeSaveCannedResponse(); this.markdownParser = new BeforeSaveMarkdownParser(!disableMarkdownParser); this.checkMAC = new BeforeSaveCheckMAC(); @@ -121,18 +124,18 @@ export class MessageService extends ServiceClassInternal implements IMessageServ async beforeSave({ message, - room: _room, + room, user, }: { message: IMessage; room: IRoom; - user: Pick; + user: Pick; }): Promise { // TODO looks like this one was not being used (so I'll left it commented) // await this.joinDiscussionOnMessage({ message, room, user }); + message = await this.cannedResponse.replacePlaceholders({ message, room, user }); message = await this.markdownParser.parseMarkdown({ message, config: this.getMarkdownConfig() }); - message = await this.badWords.filterBadWords({ message }); message = await this.spotify.convertSpotifyLinks({ message }); message = await this.jumpToMessage.createAttachmentForMessageURLs({ @@ -147,7 +150,7 @@ export class MessageService extends ServiceClassInternal implements IMessageServ if (!this.isEditedOrOld(message)) { await Promise.all([ - this.checkMAC.isWithinLimits({ message, room: _room }), + this.checkMAC.isWithinLimits({ message, room }), this.preventMention.preventMention({ message, user, mention: 'all', permission: 'mention-all' }), this.preventMention.preventMention({ message, user, mention: 'here', permission: 'mention-here' }), ]); -- GitLab From 1be2b76b4d37fca14a078448fa2f63aea774903f Mon Sep 17 00:00:00 2001 From: Douglas Fabris Date: Fri, 15 Dec 2023 16:22:03 -0300 Subject: [PATCH 042/329] chore: Remove deprecated `useForm` (#31245) --- apps/meteor/client/hooks/useForm.ts | 195 ---------------------------- 1 file changed, 195 deletions(-) delete mode 100644 apps/meteor/client/hooks/useForm.ts diff --git a/apps/meteor/client/hooks/useForm.ts b/apps/meteor/client/hooks/useForm.ts deleted file mode 100644 index d84aca63fc0..00000000000 --- a/apps/meteor/client/hooks/useForm.ts +++ /dev/null @@ -1,195 +0,0 @@ -import { capitalize } from '@rocket.chat/string-helpers'; -import type { ChangeEvent } from 'react'; -import { useCallback, useReducer, useMemo } from 'react'; - -type Field = { - name: string; - currentValue: unknown; - initialValue: unknown; - changed: boolean; -}; - -type FormState> = { - fields: Field[]; - values: Values; - hasUnsavedChanges: boolean; -}; - -type FormAction> = { - (prevState: FormState): FormState; -}; - -const reduceForm = >(state: FormState, action: FormAction): FormState => - action(state); - -const initForm = >(initialValues: Values): FormState => { - const fields = []; - - for (const [fieldName, initialValue] of Object.entries(initialValues)) { - fields.push({ - name: fieldName, - currentValue: initialValue, - initialValue, - changed: false, - }); - } - - return { - fields, - values: { ...initialValues }, - hasUnsavedChanges: false, - }; -}; - -const valueChanged = - >(fieldName: string, newValue: unknown): FormAction => - (state: FormState): FormState => { - let { fields } = state; - const field = fields.find(({ name }) => name === fieldName); - - if (!field || field.currentValue === newValue) { - return state; - } - - const newField = { - ...field, - currentValue: newValue, - changed: JSON.stringify(newValue) !== JSON.stringify(field.initialValue), - }; - - fields = state.fields.map((field) => { - if (field.name === fieldName) { - return newField; - } - - return field; - }); - - return { - ...state, - fields, - values: { - ...state.values, - [newField.name]: newField.currentValue, - }, - hasUnsavedChanges: newField.changed || fields.some((field) => field.changed), - }; - }; - -const formCommitted = - >(): FormAction => - (state: FormState): FormState => ({ - ...state, - fields: state.fields.map((field) => ({ - ...field, - initialValue: field.currentValue, - changed: false, - })), - hasUnsavedChanges: false, - }); - -const formReset = - >(): FormAction => - (state: FormState): FormState => ({ - ...state, - fields: state.fields.map((field) => ({ - ...field, - currentValue: field.initialValue, - changed: false, - })), - values: state.fields.reduce( - (values, field) => ({ - ...values, - [field.name]: field.initialValue, - }), - {} as Values, - ), - hasUnsavedChanges: false, - }); - -const isChangeEvent = (x: any): x is ChangeEvent => - (typeof x === 'object' || typeof x === 'function') && typeof x?.currentTarget !== 'undefined'; - -const getValue = (eventOrValue: ChangeEvent | unknown): unknown => { - if (!isChangeEvent(eventOrValue)) { - return eventOrValue; - } - - const target = eventOrValue.currentTarget; - - if (target instanceof HTMLTextAreaElement) { - return target.value; - } - - if (target instanceof HTMLSelectElement) { - return target.value; - } - - if (!(target instanceof HTMLInputElement)) { - return undefined; - } - - if (target.type === 'checkbox' || target.type === 'radio') { - return target.checked; - } - - return target.value; -}; - -/** - * @deprecated prefer react-hook-form's `useForm` - */ -export const useForm = < - Reducer extends ( - state: FormState>, - action: FormAction>, - ) => FormState>, ->( - initialValues: Parameters[0]['values'], - onChange: (...args: unknown[]) => void = (): void => undefined, -): { - values: Parameters[0]['values']; - handlers: Record void>; - hasUnsavedChanges: boolean; - commit: () => void; - reset: () => void; -} => { - const [state, dispatch] = useReducer(reduceForm, initialValues, initForm); - - const commit = useCallback(() => { - dispatch(formCommitted()); - }, []); - - const reset = useCallback(() => { - dispatch(formReset()); - }, []); - - const handlers = useMemo void>>( - () => - state.fields.reduce( - (handlers, { name, initialValue }) => ({ - ...handlers, - [`handle${capitalize(name)}`]: (eventOrValue: ChangeEvent | unknown): void => { - const newValue = getValue(eventOrValue); - dispatch(valueChanged(name, newValue)); - onChange({ - initialValue, - value: newValue, - key: name, - values: state.values, - }); - }, - }), - {}, - ), - [onChange, state.fields, state.values], - ); - - return { - handlers, - values: state.values, - hasUnsavedChanges: state.hasUnsavedChanges, - commit, - reset, - }; -}; -- GitLab From 6ba8bcc2caba0de09f98f6046f003b239cefc60f Mon Sep 17 00:00:00 2001 From: Hugo Costa Date: Fri, 15 Dec 2023 16:40:51 -0300 Subject: [PATCH 043/329] fix: account verify email (#31227) --- .changeset/tame-drinks-yell.md | 5 +++++ apps/meteor/app/2fa/server/loginHandler.ts | 21 ++++++++------------- apps/meteor/client/main.ts | 2 ++ apps/meteor/client/startup/accounts.ts | 22 +++++++++++++++------- apps/meteor/client/startup/index.ts | 1 - 5 files changed, 30 insertions(+), 21 deletions(-) create mode 100644 .changeset/tame-drinks-yell.md diff --git a/.changeset/tame-drinks-yell.md b/.changeset/tame-drinks-yell.md new file mode 100644 index 00000000000..6cb19babccf --- /dev/null +++ b/.changeset/tame-drinks-yell.md @@ -0,0 +1,5 @@ +--- +"@rocket.chat/meteor": patch +--- + +Fixed verify the account through email link diff --git a/apps/meteor/app/2fa/server/loginHandler.ts b/apps/meteor/app/2fa/server/loginHandler.ts index feb1923c879..26c9869a132 100644 --- a/apps/meteor/app/2fa/server/loginHandler.ts +++ b/apps/meteor/app/2fa/server/loginHandler.ts @@ -26,19 +26,14 @@ Accounts.registerLoginHandler('totp', function (options) { callbacks.add( 'onValidateLogin', async (login) => { - if (login.methodName === 'verifyEmail') { - throw new Meteor.Error('verify-email', 'E-mail verified'); - } - - if (login.type === 'resume' || login.type === 'proxy' || (login.type === 'password' && login.methodName === 'resetPassword')) { - return login; - } - // CAS login doesn't yet support 2FA. - if (login.type === 'cas') { - return login; - } - - if (!login.user) { + if ( + !login.user || + login.type === 'resume' || + login.type === 'proxy' || + login.type === 'cas' || + (login.type === 'password' && login.methodName === 'resetPassword') || + login.methodName === 'verifyEmail' + ) { return login; } diff --git a/apps/meteor/client/main.ts b/apps/meteor/client/main.ts index 33451598078..4183195fb26 100644 --- a/apps/meteor/client/main.ts +++ b/apps/meteor/client/main.ts @@ -1,3 +1,5 @@ +import './startup/accounts'; + import { FlowRouter } from 'meteor/kadira:flow-router'; FlowRouter.wait(); diff --git a/apps/meteor/client/startup/accounts.ts b/apps/meteor/client/startup/accounts.ts index ed99ea8238b..3be110bc0a0 100644 --- a/apps/meteor/client/startup/accounts.ts +++ b/apps/meteor/client/startup/accounts.ts @@ -1,18 +1,26 @@ import { Accounts } from 'meteor/accounts-base'; import { Meteor } from 'meteor/meteor'; +import { Tracker } from 'meteor/tracker'; +import { mainReady } from '../../app/ui-utils/client'; import { sdk } from '../../app/utils/client/lib/SDKClient'; import { t } from '../../app/utils/lib/i18n'; import { dispatchToastMessage } from '../lib/toast'; Accounts.onEmailVerificationLink((token: string) => { Accounts.verifyEmail(token, (error) => { - if (error instanceof Meteor.Error && error.error === 'verify-email') { - dispatchToastMessage({ type: 'success', message: t('Email_verified') }); - void sdk.call('afterVerifyEmail'); - return; - } - - dispatchToastMessage({ type: 'error', message: error }); + Tracker.autorun(() => { + if (mainReady.get()) { + if (error) { + dispatchToastMessage({ type: 'error', message: error }); + throw new Meteor.Error('verify-email', 'E-mail not verified'); + } else { + Tracker.nonreactive(() => { + void sdk.call('afterVerifyEmail'); + }); + dispatchToastMessage({ type: 'success', message: t('Email_verified') }); + } + } + }); }); }); diff --git a/apps/meteor/client/startup/index.ts b/apps/meteor/client/startup/index.ts index 8d85beb18df..61eaa0da16e 100644 --- a/apps/meteor/client/startup/index.ts +++ b/apps/meteor/client/startup/index.ts @@ -1,6 +1,5 @@ import '../lib/rooms/roomTypes'; import './absoluteUrl'; -import './accounts'; import './actionButtons'; import './afterLogoutCleanUp'; import './appRoot'; -- GitLab From ca2f4c505d97d2a236dc2f71d5fc9807bb4fa4cc Mon Sep 17 00:00:00 2001 From: gabriellsh <40830821+gabriellsh@users.noreply.github.com> Date: Mon, 18 Dec 2023 09:54:42 -0300 Subject: [PATCH 044/329] fix: Sending messages triggering stream unsub/sub (#31254) --- .../room/body/hooks/useGoToHomeOnRemoved.ts | 19 ++++++++----------- 1 file changed, 8 insertions(+), 11 deletions(-) diff --git a/apps/meteor/client/views/room/body/hooks/useGoToHomeOnRemoved.ts b/apps/meteor/client/views/room/body/hooks/useGoToHomeOnRemoved.ts index 068c97a2de4..9cb94e8c93f 100644 --- a/apps/meteor/client/views/room/body/hooks/useGoToHomeOnRemoved.ts +++ b/apps/meteor/client/views/room/body/hooks/useGoToHomeOnRemoved.ts @@ -1,12 +1,12 @@ import { isOmnichannelRoom, type IRoom } from '@rocket.chat/core-typings'; -import { useRoute, useStream, useToastMessageDispatch, useTranslation } from '@rocket.chat/ui-contexts'; +import { useRouter, useStream, useToastMessageDispatch, useTranslation } from '@rocket.chat/ui-contexts'; import { useQueryClient } from '@tanstack/react-query'; import { useEffect } from 'react'; import { useOmnichannelCloseRoute } from '../../../../hooks/omnichannel/useOmnichannelCloseRoute'; -export function useGoToHomeOnRemoved(room: IRoom, userId: string | undefined): void { - const homeRouter = useRoute('home'); +export function useGoToHomeOnRemoved(room: IRoom, userId?: string): void { + const router = useRouter(); const queryClient = useQueryClient(); const dispatchToastMessage = useToastMessageDispatch(); const subscribeToNotifyUser = useStream('notify-user'); @@ -18,11 +18,11 @@ export function useGoToHomeOnRemoved(room: IRoom, userId: string | undefined): v return; } - const unSubscribeFromNotifyUser = subscribeToNotifyUser(`${userId}/subscriptions-changed`, (event, subscription) => { + return subscribeToNotifyUser(`${userId}/subscriptions-changed`, (event, subscription) => { if (event === 'removed' && subscription.rid === room._id) { queryClient.invalidateQueries(['rooms', room._id]); - if (isOmnichannelRoom(room)) { + if (isOmnichannelRoom({ t: room.t })) { navigateHome(); return; } @@ -34,23 +34,20 @@ export function useGoToHomeOnRemoved(room: IRoom, userId: string | undefined): v }), }); - homeRouter.push({}); + router.navigate('/home'); } }); - - return unSubscribeFromNotifyUser; }, [ userId, - homeRouter, + router, subscribeToNotifyUser, room._id, + room.t, room?.fname, room?.name, t, dispatchToastMessage, queryClient, - room.t, - room, navigateHome, ]); } -- GitLab From 56981d87a6485e71070b49e6cf23f3c802a21b50 Mon Sep 17 00:00:00 2001 From: Diego Sampaio Date: Mon, 18 Dec 2023 16:51:20 -0300 Subject: [PATCH 045/329] chore: move `mention-user-not-in-channel` to afterSaveMessage (#31263) --- .../server/startup/mentionUserNotInChannel.ts | 19 +++++++++---------- 1 file changed, 9 insertions(+), 10 deletions(-) diff --git a/apps/meteor/app/lib/server/startup/mentionUserNotInChannel.ts b/apps/meteor/app/lib/server/startup/mentionUserNotInChannel.ts index 8b883645147..160defcb94e 100644 --- a/apps/meteor/app/lib/server/startup/mentionUserNotInChannel.ts +++ b/apps/meteor/app/lib/server/startup/mentionUserNotInChannel.ts @@ -1,7 +1,7 @@ import { api } from '@rocket.chat/core-services'; import type { IMessage } from '@rocket.chat/core-typings'; -import { isDirectMessageRoom, isEditedMessage, isRoomFederated } from '@rocket.chat/core-typings'; -import { Subscriptions, Rooms, Users, Settings } from '@rocket.chat/models'; +import { isDirectMessageRoom, isEditedMessage, isOmnichannelRoom, isRoomFederated } from '@rocket.chat/core-typings'; +import { Subscriptions, Users } from '@rocket.chat/models'; import type { ActionsBlock } from '@rocket.chat/ui-kit'; import moment from 'moment'; @@ -10,6 +10,7 @@ import { getUserDisplayName } from '../../../../lib/getUserDisplayName'; import { isTruthy } from '../../../../lib/isTruthy'; import { i18n } from '../../../../server/lib/i18n'; import { hasPermissionAsync } from '../../../authorization/server/functions/hasPermission'; +import { settings } from '../../../settings/server'; const APP_ID = 'mention-core'; const getBlocks = (mentions: IMessage['mentions'], messageId: string, lng: string | undefined) => { @@ -52,8 +53,8 @@ const getBlocks = (mentions: IMessage['mentions'], messageId: string, lng: strin }; callbacks.add( - 'beforeSaveMessage', - async (message) => { + 'afterSaveMessage', + async (message, room) => { // TODO: check if I need to test this 60 second rule. // If the message was edited, or is older than 60 seconds (imported) // the notifications will be skipped, so we can also skip this validation @@ -66,8 +67,7 @@ callbacks.add( return message; } - const room = await Rooms.findOneById(message.rid); - if (!room || isDirectMessageRoom(room) || isRoomFederated(room) || room.t === 'l') { + if (isDirectMessageRoom(room) || isRoomFederated(room) || isOmnichannelRoom(room)) { return message; } @@ -90,6 +90,7 @@ callbacks.add( : hasPermissionAsync(message.u._id, 'add-user-to-any-p-room')); const canDMUsers = await hasPermissionAsync(message.u._id, 'create-d'); // TODO: Perhaps check if user has DM with mentioned user (might be too expensive) const canAddUsers = canAddUsersToThisRoom || canAddToAnyRoom; + const { language } = (await Users.findOneById(message.u._id)) || {}; const actionBlocks = getBlocks(mentionsUsersNotInChannel, message._id, language); @@ -103,11 +104,9 @@ callbacks.add( ? 'You_mentioned___mentions__but_theyre_not_in_this_room' : 'You_mentioned___mentions__but_theyre_not_in_this_room_You_can_ask_a_room_admin_to_add_them'; - const { value: useRealName } = (await Settings.findOneById('UI_Use_Real_Name')) || {}; + const useRealName = settings.get('UI_Use_Real_Name'); - const usernamesOrNames = mentionsUsersNotInChannel.map( - ({ username, name }) => `*${getUserDisplayName(name, username, Boolean(useRealName))}*`, - ); + const usernamesOrNames = mentionsUsersNotInChannel.map(({ username, name }) => `*${getUserDisplayName(name, username, useRealName)}*`); const mentionsText = usernamesOrNames.join(', '); -- GitLab From 4a7411be04e7df052a6c8f81cbf392b47e588640 Mon Sep 17 00:00:00 2001 From: Diego Sampaio Date: Mon, 18 Dec 2023 20:22:07 -0300 Subject: [PATCH 046/329] chore: stop using callbacks for parsing mentions on messages (#31188) --- .../importer-slack/server/SlackImporter.ts | 2 +- .../app/lib/server/functions/sendMessage.js | 3 +- .../meteor/app/mentions/lib/MentionsParser.js | 135 ----------------- .../meteor/app/mentions/lib/MentionsParser.ts | 140 ++++++++++++++++++ apps/meteor/app/mentions/server/Mentions.js | 89 ----------- apps/meteor/app/mentions/server/Mentions.ts | 84 +++++++++++ apps/meteor/app/mentions/server/index.ts | 1 - .../client/lib/normalizeThreadTitle.ts | 2 +- .../client/components/GazzodownText.tsx | 2 +- .../messages/hooks/BeforeSaveMentions.ts} | 34 ++--- .../server/services/messages/service.ts | 2 + .../tests/unit/app/mentions/client.tests.js | 63 +------- .../tests/unit/app/mentions/server.tests.js | 67 +-------- .../core-typings/src/IMessage/IMessage.ts | 13 +- .../gazzodown/src/MarkupInteractionContext.ts | 14 +- 15 files changed, 264 insertions(+), 387 deletions(-) delete mode 100644 apps/meteor/app/mentions/lib/MentionsParser.js create mode 100644 apps/meteor/app/mentions/lib/MentionsParser.ts delete mode 100644 apps/meteor/app/mentions/server/Mentions.js create mode 100644 apps/meteor/app/mentions/server/Mentions.ts rename apps/meteor/{app/mentions/server/server.ts => server/services/messages/hooks/BeforeSaveMentions.ts} (66%) diff --git a/apps/meteor/app/importer-slack/server/SlackImporter.ts b/apps/meteor/app/importer-slack/server/SlackImporter.ts index 7d56c7784b7..0ef81c69a1e 100644 --- a/apps/meteor/app/importer-slack/server/SlackImporter.ts +++ b/apps/meteor/app/importer-slack/server/SlackImporter.ts @@ -408,7 +408,7 @@ export class SlackImporter extends Importer { parseMentions(newMessage: IImportMessage): void { const mentionsParser = new MentionsParser({ pattern: () => '[0-9a-zA-Z]+', - useRealName: () => settings.get('UI_Use_Real_Name'), + useRealName: () => settings.get('UI_Use_Real_Name'), me: () => 'me', }); diff --git a/apps/meteor/app/lib/server/functions/sendMessage.js b/apps/meteor/app/lib/server/functions/sendMessage.js index e2f45d38fcb..4886b13afba 100644 --- a/apps/meteor/app/lib/server/functions/sendMessage.js +++ b/apps/meteor/app/lib/server/functions/sendMessage.js @@ -249,9 +249,10 @@ export const sendMessage = async function (user, message, room, upsert = false, parseUrlsInMessage(message, previewUrls); + message = await Message.beforeSave({ message, room, user }); + message = await callbacks.run('beforeSaveMessage', message, room); - message = await Message.beforeSave({ message, room, user }); if (message) { if (message.t === 'otr') { const otrStreamer = notifications.streamRoomMessage; diff --git a/apps/meteor/app/mentions/lib/MentionsParser.js b/apps/meteor/app/mentions/lib/MentionsParser.js deleted file mode 100644 index 87329ac9f12..00000000000 --- a/apps/meteor/app/mentions/lib/MentionsParser.js +++ /dev/null @@ -1,135 +0,0 @@ -import { escapeHTML } from '@rocket.chat/string-helpers'; - -const userTemplateDefault = ({ prefix, className, mention, title, label, type = 'username' }) => - `${prefix}${label}`; -const roomTemplateDefault = ({ prefix, reference, mention }) => - `${prefix}${`#${mention}`}`; -export class MentionsParser { - constructor({ pattern, useRealName, me, roomTemplate = roomTemplateDefault, userTemplate = userTemplateDefault }) { - this.pattern = pattern; - this.useRealName = useRealName; - this.me = me; - this.userTemplate = userTemplate; - this.roomTemplate = roomTemplate; - } - - set me(m) { - this._me = m; - } - - get me() { - return typeof this._me === 'function' ? this._me() : this._me; - } - - set pattern(p) { - this._pattern = p; - } - - get pattern() { - return typeof this._pattern === 'function' ? this._pattern() : this._pattern; - } - - set useRealName(s) { - this._useRealName = s; - } - - get useRealName() { - return typeof this._useRealName === 'function' ? this._useRealName() : this._useRealName; - } - - get userMentionRegex() { - return new RegExp(`(^|\\s|>)@(${this.pattern}(@(${this.pattern}))?(:([0-9a-zA-Z-_.]+))?)`, 'gm'); - } - - get channelMentionRegex() { - return new RegExp(`(^|\\s|>)#(${this.pattern}(@(${this.pattern}))?)`, 'gm'); - } - - replaceUsers = (msg, { mentions, temp }, me) => - msg.replace(this.userMentionRegex, (match, prefix, mention) => { - const classNames = ['mention-link']; - - if (mention === 'all') { - classNames.push('mention-link--all'); - classNames.push('mention-link--group'); - } else if (mention === 'here') { - classNames.push('mention-link--here'); - classNames.push('mention-link--group'); - } else if (mention === me) { - classNames.push('mention-link--me'); - classNames.push('mention-link--user'); - } else { - classNames.push('mention-link--user'); - } - - const className = classNames.join(' '); - - if (mention === 'all' || mention === 'here') { - return this.userTemplate({ prefix, className, mention, label: mention, type: 'group' }); - } - - const filterUser = ({ username, type }) => (!type || type === 'user') && username === mention; - const filterTeam = ({ name, type }) => type === 'team' && name === mention; - - const [mentionObj] = (mentions || []).filter((m) => filterUser(m) || filterTeam(m)); - - const label = temp - ? mention && escapeHTML(mention) - : mentionObj && escapeHTML(mentionObj.type === 'team' || this.useRealName ? mentionObj.name : mentionObj.username); - - if (!label) { - return match; - } - - return this.userTemplate({ - prefix, - className, - mention, - label, - type: mentionObj?.type === 'team' ? 'team' : 'username', - title: this.useRealName ? mention : label, - }); - }); - - replaceChannels = (msg, { temp, channels }) => - msg.replace(/'/g, "'").replace(this.channelMentionRegex, (match, prefix, mention) => { - if ( - !temp && - !( - channels && - channels.find((c) => { - return c.dname ? c.dname === mention : c.name === mention; - }) - ) - ) { - return match; - } - - const channel = - channels && - channels.find(({ name, dname }) => { - return dname ? dname === mention : name === mention; - }); - const reference = channel ? channel._id : mention; - return this.roomTemplate({ prefix, reference, channel, mention }); - }); - - getUserMentions(str) { - return (str.match(this.userMentionRegex) || []).map((match) => match.trim()); - } - - getChannelMentions(str) { - return (str.match(this.channelMentionRegex) || []).map((match) => match.trim()); - } - - parse(message) { - let msg = (message && message.html) || ''; - if (!msg.trim()) { - return message; - } - msg = this.replaceUsers(msg, message, this.me); - msg = this.replaceChannels(msg, message, this.me); - message.html = msg; - return message; - } -} diff --git a/apps/meteor/app/mentions/lib/MentionsParser.ts b/apps/meteor/app/mentions/lib/MentionsParser.ts new file mode 100644 index 00000000000..c4180eecf84 --- /dev/null +++ b/apps/meteor/app/mentions/lib/MentionsParser.ts @@ -0,0 +1,140 @@ +import type { IMessage } from '@rocket.chat/core-typings'; +import { escapeHTML } from '@rocket.chat/string-helpers'; + +export type MentionsParserArgs = { + pattern: () => string; + useRealName?: () => boolean; + me?: () => string; + roomTemplate?: (args: { prefix: string; reference: string; mention: string; channel?: any }) => string; + userTemplate?: (args: { prefix: string; className: string; mention: string; title?: string; label: string; type?: string }) => string; +}; + +const userTemplateDefault = ({ + prefix, + className, + mention, + title = '', + label, + type = 'username', +}: { + prefix: string; + className: string; + mention: string; + title?: string; + label?: string; + type?: string; +}) => `${prefix}${label}`; + +const roomTemplateDefault = ({ prefix, reference, mention }: { prefix: string; reference: string; mention: string }) => + `${prefix}${`#${mention}`}`; + +export class MentionsParser { + me: () => string; + + pattern: MentionsParserArgs['pattern']; + + userTemplate: (args: { prefix: string; className: string; mention: string; title?: string; label: string; type?: string }) => string; + + roomTemplate: (args: { prefix: string; reference: string; mention: string; channel?: any }) => string; + + useRealName: () => boolean; + + constructor({ pattern, useRealName, me, roomTemplate = roomTemplateDefault, userTemplate = userTemplateDefault }: MentionsParserArgs) { + this.pattern = pattern; + this.useRealName = useRealName || (() => false); + this.me = me || (() => ''); + this.userTemplate = userTemplate; + this.roomTemplate = roomTemplate; + } + + get userMentionRegex() { + return new RegExp(`(^|\\s|>)@(${this.pattern()}(@(${this.pattern()}))?(:([0-9a-zA-Z-_.]+))?)`, 'gm'); + } + + get channelMentionRegex() { + return new RegExp(`(^|\\s|>)#(${this.pattern()}(@(${this.pattern()}))?)`, 'gm'); + } + + replaceUsers = (msg: string, { mentions, temp }: IMessage, me: string) => + msg.replace(this.userMentionRegex, (match, prefix, mention) => { + const classNames = ['mention-link']; + + if (mention === 'all') { + classNames.push('mention-link--all'); + classNames.push('mention-link--group'); + } else if (mention === 'here') { + classNames.push('mention-link--here'); + classNames.push('mention-link--group'); + } else if (mention === me) { + classNames.push('mention-link--me'); + classNames.push('mention-link--user'); + } else { + classNames.push('mention-link--user'); + } + + const className = classNames.join(' '); + + if (mention === 'all' || mention === 'here') { + return this.userTemplate({ prefix, className, mention, label: mention, type: 'group' }); + } + + const filterUser = ({ username, type }: { username?: string; type?: string }) => (!type || type === 'user') && username === mention; + const filterTeam = ({ name, type }: { name?: string; type?: string }) => type === 'team' && name === mention; + + const [mentionObj] = (mentions || []).filter((m) => m && (filterUser(m) || filterTeam(m))); + + const label = temp + ? mention && escapeHTML(mention) + : mentionObj && escapeHTML((mentionObj.type === 'team' || this.useRealName() ? mentionObj.name : mentionObj.username) || ''); + + if (!label) { + return match; + } + + return this.userTemplate({ + prefix, + className, + mention, + label, + type: mentionObj?.type === 'team' ? 'team' : 'username', + title: this.useRealName() ? mention : label, + }); + }); + + replaceChannels = (msg: string, { temp, channels }: IMessage) => + msg.replace(/'/g, "'").replace(this.channelMentionRegex, (match, prefix, mention) => { + if ( + !temp && + !channels?.find((c) => { + return c.name === mention; + }) + ) { + return match; + } + + const channel = channels?.find(({ name }) => { + return name === mention; + }); + const reference = channel ? channel._id : mention; + return this.roomTemplate({ prefix, reference, channel, mention }); + }); + + getUserMentions(str: string) { + return (str.match(this.userMentionRegex) || []).map((match) => match.trim()); + } + + getChannelMentions(str: string) { + return (str.match(this.channelMentionRegex) || []).map((match) => match.trim()); + } + + parse(message: IMessage) { + let msg = message?.html || ''; + if (!msg.trim()) { + return message; + } + msg = this.replaceUsers(msg, message, this.me()); + msg = this.replaceChannels(msg, message); + message.html = msg; + return message; + } +} diff --git a/apps/meteor/app/mentions/server/Mentions.js b/apps/meteor/app/mentions/server/Mentions.js deleted file mode 100644 index 91518fd7485..00000000000 --- a/apps/meteor/app/mentions/server/Mentions.js +++ /dev/null @@ -1,89 +0,0 @@ -/* - * Mentions is a named function that will process Mentions - * @param {Object} message - The message object - */ -import { MentionsParser } from '../lib/MentionsParser'; - -export default class MentionsServer extends MentionsParser { - constructor(args) { - super(args); - this.messageMaxAll = args.messageMaxAll; - this.getChannel = args.getChannel; - this.getChannels = args.getChannels; - this.getUsers = args.getUsers; - this.getUser = args.getUser; - this.getTotalChannelMembers = args.getTotalChannelMembers; - this.onMaxRoomMembersExceeded = args.onMaxRoomMembersExceeded || (() => {}); - } - - set getUsers(m) { - this._getUsers = m; - } - - get getUsers() { - return typeof this._getUsers === 'function' ? this._getUsers : async () => this._getUsers; - } - - set getChannels(m) { - this._getChannels = m; - } - - get getChannels() { - return typeof this._getChannels === 'function' ? this._getChannels : () => this._getChannels; - } - - set getChannel(m) { - this._getChannel = m; - } - - get getChannel() { - return typeof this._getChannel === 'function' ? this._getChannel : () => this._getChannel; - } - - set messageMaxAll(m) { - this._messageMaxAll = m; - } - - get messageMaxAll() { - return typeof this._messageMaxAll === 'function' ? this._messageMaxAll() : this._messageMaxAll; - } - - async getUsersByMentions({ msg, rid, u: sender }) { - let mentions = this.getUserMentions(msg); - const mentionsAll = []; - const userMentions = []; - - for await (const m of mentions) { - const mention = m.trim().substr(1); - if (mention !== 'all' && mention !== 'here') { - userMentions.push(mention); - continue; - } - if (this.messageMaxAll > 0 && (await this.getTotalChannelMembers(rid)) > this.messageMaxAll) { - await this.onMaxRoomMembersExceeded({ sender, rid }); - continue; - } - mentionsAll.push({ - _id: mention, - username: mention, - }); - } - mentions = userMentions.length ? await this.getUsers(userMentions) : []; - return [...mentionsAll, ...mentions]; - } - - async getChannelbyMentions({ msg }) { - const channels = this.getChannelMentions(msg); - return this.getChannels(channels.map((c) => c.trim().substr(1))); - } - - async execute(message) { - const mentionsAll = await this.getUsersByMentions(message); - const channels = await this.getChannelbyMentions(message); - - message.mentions = mentionsAll; - message.channels = channels; - - return message; - } -} diff --git a/apps/meteor/app/mentions/server/Mentions.ts b/apps/meteor/app/mentions/server/Mentions.ts new file mode 100644 index 00000000000..9eda56fea21 --- /dev/null +++ b/apps/meteor/app/mentions/server/Mentions.ts @@ -0,0 +1,84 @@ +/* + * Mentions is a named function that will process Mentions + * @param {Object} message - The message object + */ +import type { IMessage, IRoom, IUser } from '@rocket.chat/core-typings'; + +import { type MentionsParserArgs, MentionsParser } from '../lib/MentionsParser'; + +type MentionsServerArgs = MentionsParserArgs & { + messageMaxAll: () => number; + getChannels: (c: string[]) => Promise[]>; + getUsers: (u: string[]) => Promise<{ type: 'team' | 'user'; _id: string; username?: string; name?: string }[]>; + getUser: (u: string) => Promise; + getTotalChannelMembers: (rid: string) => Promise; + onMaxRoomMembersExceeded: ({ sender, rid }: { sender: IMessage['u']; rid: string }) => Promise; +}; + +export class MentionsServer extends MentionsParser { + messageMaxAll: MentionsServerArgs['messageMaxAll']; + + getChannels: MentionsServerArgs['getChannels']; + + getUsers: MentionsServerArgs['getUsers']; + + getUser: MentionsServerArgs['getUser']; + + getTotalChannelMembers: MentionsServerArgs['getTotalChannelMembers']; + + onMaxRoomMembersExceeded: MentionsServerArgs['onMaxRoomMembersExceeded']; + + constructor(args: MentionsServerArgs) { + super(args); + + this.messageMaxAll = args.messageMaxAll; + this.getChannels = args.getChannels; + this.getUsers = args.getUsers; + this.getUser = args.getUser; + this.getTotalChannelMembers = args.getTotalChannelMembers; + this.onMaxRoomMembersExceeded = + args.onMaxRoomMembersExceeded || + (() => { + /* do nothing */ + }); + } + + async getUsersByMentions({ msg, rid, u: sender }: Pick): Promise { + const mentions = this.getUserMentions(msg); + const mentionsAll: { _id: string; username: string }[] = []; + const userMentions = []; + + for await (const m of mentions) { + const mention = m.trim().substr(1); + if (mention !== 'all' && mention !== 'here') { + userMentions.push(mention); + continue; + } + if (this.messageMaxAll() > 0 && (await this.getTotalChannelMembers(rid)) > this.messageMaxAll()) { + await this.onMaxRoomMembersExceeded({ sender, rid }); + continue; + } + mentionsAll.push({ + _id: mention, + username: mention, + }); + } + + return [...mentionsAll, ...(userMentions.length ? await this.getUsers(userMentions) : [])]; + } + + async getChannelbyMentions({ msg }: Pick) { + const channels = this.getChannelMentions(msg); + return this.getChannels(channels.map((c) => c.trim().substr(1))); + } + + async execute(message: IMessage) { + const mentionsAll = await this.getUsersByMentions(message); + const channels = await this.getChannelbyMentions(message); + + message.mentions = mentionsAll; + message.channels = channels; + + return message; + } +} diff --git a/apps/meteor/app/mentions/server/index.ts b/apps/meteor/app/mentions/server/index.ts index a04af05b9db..b16d62185a6 100644 --- a/apps/meteor/app/mentions/server/index.ts +++ b/apps/meteor/app/mentions/server/index.ts @@ -1,3 +1,2 @@ import './getMentionedTeamMembers'; import './methods/getUserMentionsByChannel'; -import './server'; diff --git a/apps/meteor/app/threads/client/lib/normalizeThreadTitle.ts b/apps/meteor/app/threads/client/lib/normalizeThreadTitle.ts index 41a55aefc5c..70a2a6008e5 100644 --- a/apps/meteor/app/threads/client/lib/normalizeThreadTitle.ts +++ b/apps/meteor/app/threads/client/lib/normalizeThreadTitle.ts @@ -15,7 +15,7 @@ export function normalizeThreadTitle({ ...message }: Readonly) { return filteredMessage; } const uid = Meteor.userId(); - const me = uid && Users.findOne(uid, { fields: { username: 1 } })?.username; + const me = (uid && Users.findOne(uid, { fields: { username: 1 } })?.username) || ''; const pattern = settings.get('UTF8_User_Names_Validation'); const useRealName = settings.get('UI_Use_Real_Name'); diff --git a/apps/meteor/client/components/GazzodownText.tsx b/apps/meteor/client/components/GazzodownText.tsx index 76868f84e3a..fe86cbd86fd 100644 --- a/apps/meteor/client/components/GazzodownText.tsx +++ b/apps/meteor/client/components/GazzodownText.tsx @@ -15,7 +15,7 @@ import { useMessageListHighlights } from './message/list/MessageListContext'; type GazzodownTextProps = { children: JSX.Element; mentions?: { - type: 'user' | 'team'; + type?: 'user' | 'team'; _id: string; username?: string; name?: string; diff --git a/apps/meteor/app/mentions/server/server.ts b/apps/meteor/server/services/messages/hooks/BeforeSaveMentions.ts similarity index 66% rename from apps/meteor/app/mentions/server/server.ts rename to apps/meteor/server/services/messages/hooks/BeforeSaveMentions.ts index 13765e99d85..bcf022587e8 100644 --- a/apps/meteor/app/mentions/server/server.ts +++ b/apps/meteor/server/services/messages/hooks/BeforeSaveMentions.ts @@ -1,23 +1,20 @@ -import { api, Team } from '@rocket.chat/core-services'; -import type { IUser, IRoom, ITeam } from '@rocket.chat/core-typings'; +import { api, Team, MeteorError } from '@rocket.chat/core-services'; +import type { IMessage, IUser, IRoom } from '@rocket.chat/core-typings'; import { Subscriptions, Users, Rooms } from '@rocket.chat/models'; -import { Meteor } from 'meteor/meteor'; -import { callbacks } from '../../../lib/callbacks'; -import { i18n } from '../../../server/lib/i18n'; -import { settings } from '../../settings/server'; -import MentionsServer from './Mentions'; +import { MentionsServer } from '../../../../app/mentions/server/Mentions'; +import { settings } from '../../../../app/settings/server'; +import { i18n } from '../../../lib/i18n'; -export class MentionQueries { - async getUsers( - usernames: string[], - ): Promise<((Pick & { type: 'user' }) | (Pick & { type: 'team' }))[]> { +class MentionQueries { + async getUsers(usernames: string[]): Promise<{ type: 'team' | 'user'; _id: string; username?: string; name?: string }[]> { const uniqueUsernames = [...new Set(usernames)]; + const teams = await Team.listByNames(uniqueUsernames, { projection: { name: 1 } }); - const users = await Users.find( + const users = await Users.find>( { username: { $in: uniqueUsernames } }, - { projection: { _id: true, username: true, name: 1 } }, + { projection: { _id: 1, username: 1, name: 1 } }, ).toArray(); const taggedUsers = users.map((user) => ({ @@ -65,27 +62,26 @@ export class MentionQueries { const queries = new MentionQueries(); -const mention = new MentionsServer({ +export const mentionServer = new MentionsServer({ pattern: () => settings.get('UTF8_User_Names_Validation'), messageMaxAll: () => settings.get('Message_MaxAll'), getUsers: async (usernames: string[]) => queries.getUsers(usernames), getUser: async (userId: string) => queries.getUser(userId), getTotalChannelMembers: (rid: string) => queries.getTotalChannelMembers(rid), getChannels: (channels: string[]) => queries.getChannels(channels), - async onMaxRoomMembersExceeded({ sender, rid }: { sender: IUser; rid: string }) { + async onMaxRoomMembersExceeded({ sender, rid }: { sender: IMessage['u']; rid: string }): Promise { // Get the language of the user for the error notification. - const { language } = await this.getUser(sender._id); - const msg = i18n.t('Group_mentions_disabled_x_members', { total: this.messageMaxAll, lng: language }); + const { language } = (await this.getUser(sender._id)) || {}; + const msg = i18n.t('Group_mentions_disabled_x_members', { total: this.messageMaxAll(), lng: language }); void api.broadcast('notify.ephemeralMessage', sender._id, rid, { msg, }); // Also throw to stop propagation of 'sendMessage'. - throw new Meteor.Error('error-action-not-allowed', msg, { + throw new MeteorError('error-action-not-allowed', msg, { method: 'filterATAllTag', action: msg, }); }, }); -callbacks.add('beforeSaveMessage', async (message) => mention.execute(message), callbacks.priority.HIGH, 'mentions'); diff --git a/apps/meteor/server/services/messages/service.ts b/apps/meteor/server/services/messages/service.ts index 85d71d08ae1..22b05029a65 100644 --- a/apps/meteor/server/services/messages/service.ts +++ b/apps/meteor/server/services/messages/service.ts @@ -16,6 +16,7 @@ import { BeforeSaveBadWords } from './hooks/BeforeSaveBadWords'; import { BeforeSaveCheckMAC } from './hooks/BeforeSaveCheckMAC'; import { BeforeSaveJumpToMessage } from './hooks/BeforeSaveJumpToMessage'; import { BeforeSaveMarkdownParser } from './hooks/BeforeSaveMarkdownParser'; +import { mentionServer } from './hooks/BeforeSaveMentions'; import { BeforeSavePreventMention } from './hooks/BeforeSavePreventMention'; import { BeforeSaveSpotify } from './hooks/BeforeSaveSpotify'; @@ -134,6 +135,7 @@ export class MessageService extends ServiceClassInternal implements IMessageServ // TODO looks like this one was not being used (so I'll left it commented) // await this.joinDiscussionOnMessage({ message, room, user }); + message = await mentionServer.execute(message); message = await this.cannedResponse.replacePlaceholders({ message, room, user }); message = await this.markdownParser.parseMarkdown({ message, config: this.getMarkdownConfig() }); message = await this.badWords.filterBadWords({ message }); diff --git a/apps/meteor/tests/unit/app/mentions/client.tests.js b/apps/meteor/tests/unit/app/mentions/client.tests.js index 9981eee4aab..70ac7b94320 100644 --- a/apps/meteor/tests/unit/app/mentions/client.tests.js +++ b/apps/meteor/tests/unit/app/mentions/client.tests.js @@ -5,73 +5,12 @@ import { MentionsParser } from '../../../../app/mentions/lib/MentionsParser'; let mentionsParser; beforeEach(() => { mentionsParser = new MentionsParser({ - pattern: '[0-9a-zA-Z-_.]+', + pattern: () => '[0-9a-zA-Z-_.]+', me: () => 'me', }); }); describe('Mention', () => { - describe('get pattern', () => { - const regexp = '[0-9a-zA-Z-_.]+'; - beforeEach(() => { - mentionsParser.pattern = () => regexp; - }); - - describe('by function', () => { - it(`should be equal to ${regexp}`, () => { - expect(regexp).to.be.equal(mentionsParser.pattern); - }); - }); - - describe('by const', () => { - it(`should be equal to ${regexp}`, () => { - expect(regexp).to.be.equal(mentionsParser.pattern); - }); - }); - }); - - describe('get useRealName', () => { - beforeEach(() => { - mentionsParser.useRealName = () => true; - }); - - describe('by function', () => { - it('should be true', () => { - expect(true).to.be.equal(mentionsParser.useRealName); - }); - }); - - describe('by const', () => { - it('should be true', () => { - expect(true).to.be.equal(mentionsParser.useRealName); - }); - }); - }); - - describe('get me', () => { - const me = 'me'; - - describe('by function', () => { - beforeEach(() => { - mentionsParser.me = () => me; - }); - - it(`should be equal to ${me}`, () => { - expect(me).to.be.equal(mentionsParser.me); - }); - }); - - describe('by const', () => { - beforeEach(() => { - mentionsParser.me = me; - }); - - it(`should be equal to ${me}`, () => { - expect(me).to.be.equal(mentionsParser.me); - }); - }); - }); - describe('getUserMentions', () => { describe('for simple text, no mentions', () => { const result = []; diff --git a/apps/meteor/tests/unit/app/mentions/server.tests.js b/apps/meteor/tests/unit/app/mentions/server.tests.js index b0d82f02195..335f72af491 100644 --- a/apps/meteor/tests/unit/app/mentions/server.tests.js +++ b/apps/meteor/tests/unit/app/mentions/server.tests.js @@ -1,12 +1,12 @@ import { expect } from 'chai'; -import MentionsServer from '../../../../app/mentions/server/Mentions'; +import { MentionsServer } from '../../../../app/mentions/server/Mentions'; let mention; beforeEach(() => { mention = new MentionsServer({ - pattern: '[0-9a-zA-Z-_.]+', + pattern: () => '[0-9a-zA-Z-_.]+', messageMaxAll: () => 4, // || RocketChat.settings.get('Message_MaxAll') getUsers: async (usernames) => [ @@ -224,67 +224,4 @@ describe('Mention Server', () => { expect(result).to.be.deep.equal(expected); }); }); - - describe('getters and setters', () => { - describe('messageMaxAll', () => { - const mention = new MentionsServer({}); - describe('constant', () => { - it('should return the informed value', () => { - mention.messageMaxAll = 4; - expect(mention.messageMaxAll).to.be.deep.equal(4); - }); - }); - describe('function', () => { - it('should return the informed value', () => { - mention.messageMaxAll = () => 4; - expect(mention.messageMaxAll).to.be.deep.equal(4); - }); - }); - }); - describe('getUsers', () => { - const mention = new MentionsServer({}); - describe('constant', () => { - it('should return the informed value', async () => { - mention.getUsers = 4; - expect(await mention.getUsers()).to.be.deep.equal(4); - }); - }); - describe('function', () => { - it('should return the informed value', async () => { - mention.getUsers = () => 4; - expect(await mention.getUsers()).to.be.deep.equal(4); - }); - }); - }); - describe('getChannels', () => { - const mention = new MentionsServer({}); - describe('constant', () => { - it('should return the informed value', () => { - mention.getChannels = 4; - expect(mention.getChannels()).to.be.deep.equal(4); - }); - }); - describe('function', () => { - it('should return the informed value', () => { - mention.getChannels = () => 4; - expect(mention.getChannels()).to.be.deep.equal(4); - }); - }); - }); - describe('getChannel', () => { - const mention = new MentionsServer({}); - describe('constant', () => { - it('should return the informed value', () => { - mention.getChannel = true; - expect(mention.getChannel()).to.be.deep.equal(true); - }); - }); - describe('function', () => { - it('should return the informed value', () => { - mention.getChannel = () => true; - expect(mention.getChannel()).to.be.deep.equal(true); - }); - }); - }); - }); }); diff --git a/packages/core-typings/src/IMessage/IMessage.ts b/packages/core-typings/src/IMessage/IMessage.ts index 3ed5e470585..4d684573589 100644 --- a/packages/core-typings/src/IMessage/IMessage.ts +++ b/packages/core-typings/src/IMessage/IMessage.ts @@ -13,8 +13,6 @@ import type { IUser } from '../IUser'; import type { FileProp } from './MessageAttachment/Files/FileProp'; import type { MessageAttachment } from './MessageAttachment/MessageAttachment'; -type MentionType = 'user' | 'team'; - type MessageUrl = { url: string; source?: string; @@ -121,15 +119,20 @@ export type TokenExtra = { noHtml?: string; }; +export type MessageMention = { + type?: 'user' | 'team'; // mentions for 'all' and 'here' doesn't have type + _id: string; + name?: string; + username?: string; +}; + export interface IMessage extends IRocketChatRecord { rid: RoomID; msg: string; tmid?: string; tshow?: boolean; ts: Date; - mentions?: ({ - type: MentionType; - } & Pick)[]; + mentions?: MessageMention[]; groupable?: boolean; channels?: Pick[]; diff --git a/packages/gazzodown/src/MarkupInteractionContext.ts b/packages/gazzodown/src/MarkupInteractionContext.ts index 40acaf63480..70fc32d3349 100644 --- a/packages/gazzodown/src/MarkupInteractionContext.ts +++ b/packages/gazzodown/src/MarkupInteractionContext.ts @@ -1,19 +1,19 @@ -import type { IRoom, IUser } from '@rocket.chat/core-typings'; +import type { MessageMention } from '@rocket.chat/core-typings'; import type * as MessageParser from '@rocket.chat/message-parser'; import { createContext, FormEvent, UIEvent } from 'react'; -export type UserMention = Pick; -export type ChannelMention = Pick; +export type UserMention = MessageMention; +export type ChannelMention = MessageMention; type MarkupInteractionContextValue = { detectEmoji?: (text: string) => { name: string; className: string; image?: string; content: string }[]; highlightRegex?: () => RegExp; markRegex?: () => RegExp; onTaskChecked?: (task: MessageParser.Task) => ((e: FormEvent) => void) | undefined; - resolveUserMention?: (mention: string) => UserMention | undefined; - onUserMentionClick?: (mentionedUser: UserMention) => ((e: UIEvent) => void) | undefined; - resolveChannelMention?: (mention: string) => ChannelMention | undefined; - onChannelMentionClick?: (mentionedChannel: ChannelMention) => ((e: UIEvent) => void) | undefined; + resolveUserMention?: (mention: string) => MessageMention | undefined; + onUserMentionClick?: (mentionedUser: MessageMention) => ((e: UIEvent) => void) | undefined; + resolveChannelMention?: (mention: string) => MessageMention | undefined; + onChannelMentionClick?: (mentionedChannel: MessageMention) => ((e: UIEvent) => void) | undefined; convertAsciiToEmoji?: boolean; useEmoji?: boolean; useRealName?: boolean; -- GitLab From f126ecd56a4c15670bcdb7826d7ff94c347bcbbe Mon Sep 17 00:00:00 2001 From: Raman Chaudhary <97114586+ChaudharyRaman@users.noreply.github.com> Date: Tue, 19 Dec 2023 19:22:09 +0530 Subject: [PATCH 047/329] fix: Resolved Search List Issue when pressing ENTER (#31269) Co-authored-by: Guilherme Gazzo <5263975+ggazzo@users.noreply.github.com> --- .changeset/forty-adults-kneel.md | 5 ++++ .../client/sidebar/search/SearchList.tsx | 23 +++++++++++-------- 2 files changed, 18 insertions(+), 10 deletions(-) create mode 100644 .changeset/forty-adults-kneel.md diff --git a/.changeset/forty-adults-kneel.md b/.changeset/forty-adults-kneel.md new file mode 100644 index 00000000000..cafbd2bc07c --- /dev/null +++ b/.changeset/forty-adults-kneel.md @@ -0,0 +1,5 @@ +--- +"@rocket.chat/meteor": patch +--- + +fix: Resolved Search List Issue when pressing ENTER diff --git a/apps/meteor/client/sidebar/search/SearchList.tsx b/apps/meteor/client/sidebar/search/SearchList.tsx index e63be149cc7..82d6d12c621 100644 --- a/apps/meteor/client/sidebar/search/SearchList.tsx +++ b/apps/meteor/client/sidebar/search/SearchList.tsx @@ -255,14 +255,14 @@ const SearchList = forwardRef(function SearchList({ onClose }: SearchListProps, }); const resetCursor = useMutableCallback(() => { - itemIndexRef.current = 0; - listRef.current?.scrollToIndex({ index: itemIndexRef.current }); - - selectedElement.current = boxRef.current?.querySelector('a.rcx-sidebar-item'); - - if (selectedElement.current) { - toggleSelectionState(selectedElement.current, undefined, cursorRef?.current || undefined); - } + setTimeout(() => { + itemIndexRef.current = 0; + listRef.current?.scrollToIndex({ index: itemIndexRef.current }); + selectedElement.current = boxRef.current?.querySelector('a.rcx-sidebar-item'); + if (selectedElement.current) { + toggleSelectionState(selectedElement.current, undefined, cursorRef?.current || undefined); + } + }, 0); }); usePreventDefault(boxRef); @@ -303,9 +303,12 @@ const SearchList = forwardRef(function SearchList({ onClose }: SearchListProps, listRef.current?.scrollToIndex({ index: itemIndexRef.current }); selectedElement.current = currentElement; }, - Enter: () => { - if (selectedElement.current) { + Enter: (event) => { + event.preventDefault(); + if (selectedElement.current && items.length > 0) { selectedElement.current.click(); + } else { + onClose(); } }, }); -- GitLab From 90e81fa8799b1b2357636d8b0048d37f78aa94d3 Mon Sep 17 00:00:00 2001 From: Douglas Fabris Date: Tue, 19 Dec 2023 11:41:39 -0300 Subject: [PATCH 048/329] regression: `PageHeader` back button must be an `IconButton` (#31266) --- .../client/components/Page/PageHeader.tsx | 8 ++----- .../views/admin/emailInbox/EmailInboxPage.tsx | 14 +++++------- .../views/admin/import/NewImportPage.tsx | 9 +------- .../views/admin/import/PrepareImportPage.tsx | 9 +------- .../incoming/EditIncomingWebhook.tsx | 5 +---- .../outgoing/EditOutgoingWebhook.tsx | 5 +---- .../history/OutgoingWebhookHistoryPage.tsx | 8 +++---- .../views/admin/oauthApps/OAuthAppsPage.tsx | 22 ++++++++----------- .../departments/EditDepartment.tsx | 15 ++++--------- .../cannedResponses/CannedResponseEdit.tsx | 16 +++++++------- 10 files changed, 36 insertions(+), 75 deletions(-) diff --git a/apps/meteor/client/components/Page/PageHeader.tsx b/apps/meteor/client/components/Page/PageHeader.tsx index e955ac20a52..25d20381e52 100644 --- a/apps/meteor/client/components/Page/PageHeader.tsx +++ b/apps/meteor/client/components/Page/PageHeader.tsx @@ -1,4 +1,4 @@ -import { Box, Button } from '@rocket.chat/fuselage'; +import { Box, IconButton } from '@rocket.chat/fuselage'; import { HeaderToolbox, useDocumentTitle } from '@rocket.chat/ui-client'; import { useLayout, useTranslation } from '@rocket.chat/ui-contexts'; import type { FC, ComponentProps, ReactNode } from 'react'; @@ -35,14 +35,10 @@ const PageHeader: FC = ({ children = undefined, title, onClickB )} + {onClickBack && } {title} - {onClickBack && ( - - )} {children} diff --git a/apps/meteor/client/views/admin/emailInbox/EmailInboxPage.tsx b/apps/meteor/client/views/admin/emailInbox/EmailInboxPage.tsx index 97078208b09..12eb42cf789 100644 --- a/apps/meteor/client/views/admin/emailInbox/EmailInboxPage.tsx +++ b/apps/meteor/client/views/admin/emailInbox/EmailInboxPage.tsx @@ -1,5 +1,5 @@ import { Button } from '@rocket.chat/fuselage'; -import { useRoute, useRouteParameter, useTranslation } from '@rocket.chat/ui-contexts'; +import { useRouteParameter, useRouter, useTranslation } from '@rocket.chat/ui-contexts'; import type { ReactElement } from 'react'; import React from 'react'; @@ -10,21 +10,17 @@ import EmailInboxTable from './EmailInboxTable'; const EmailInboxPage = (): ReactElement => { const t = useTranslation(); + const router = useRouter(); + const id = useRouteParameter('_id'); const context = useRouteParameter('context'); - const emailInboxRoute = useRoute('admin-email-inboxes'); return ( - - {context && ( - - )} + router.navigate('/admin/email-inboxes') : undefined}> {!context && ( - )} diff --git a/apps/meteor/client/views/admin/import/NewImportPage.tsx b/apps/meteor/client/views/admin/import/NewImportPage.tsx index 44771e44f65..82422c1f27c 100644 --- a/apps/meteor/client/views/admin/import/NewImportPage.tsx +++ b/apps/meteor/client/views/admin/import/NewImportPage.tsx @@ -44,10 +44,6 @@ function NewImportPage() { const formatMemorySize = useFormatMemorySize(); - const handleBackToImportsButtonClick = () => { - router.navigate('/admin/import'); - }; - const handleImporterKeyChange = (importerKey: Key) => { if (typeof importerKey !== 'string') { return; @@ -189,11 +185,8 @@ function NewImportPage() { return ( - + router.navigate('/admin/import')}> - {importer && ( diff --git a/apps/meteor/client/views/admin/integrations/incoming/EditIncomingWebhook.tsx b/apps/meteor/client/views/admin/integrations/incoming/EditIncomingWebhook.tsx index d76f4fb65da..87a29dcce5c 100644 --- a/apps/meteor/client/views/admin/integrations/incoming/EditIncomingWebhook.tsx +++ b/apps/meteor/client/views/admin/integrations/incoming/EditIncomingWebhook.tsx @@ -77,11 +77,8 @@ const EditIncomingWebhook = ({ webhookData }: { webhookData?: Serialized - + router.navigate('/admin/integrations/webhook-incoming')}> - {webhookData?._id && ( {webhookData?._id && ( )} diff --git a/apps/meteor/client/views/admin/integrations/outgoing/history/OutgoingWebhookHistoryPage.tsx b/apps/meteor/client/views/admin/integrations/outgoing/history/OutgoingWebhookHistoryPage.tsx index cacc5757819..8b7b96d711f 100644 --- a/apps/meteor/client/views/admin/integrations/outgoing/history/OutgoingWebhookHistoryPage.tsx +++ b/apps/meteor/client/views/admin/integrations/outgoing/history/OutgoingWebhookHistoryPage.tsx @@ -106,11 +106,11 @@ const OutgoingWebhookHistoryPage = (props: ComponentProps) => { return ( - + router.navigate(`/admin/integrations/edit/outgoing/${id}`)} + > - diff --git a/apps/meteor/client/views/admin/oauthApps/OAuthAppsPage.tsx b/apps/meteor/client/views/admin/oauthApps/OAuthAppsPage.tsx index c5cd7f5fc96..cc9e174948e 100644 --- a/apps/meteor/client/views/admin/oauthApps/OAuthAppsPage.tsx +++ b/apps/meteor/client/views/admin/oauthApps/OAuthAppsPage.tsx @@ -1,5 +1,5 @@ -import { Button } from '@rocket.chat/fuselage'; -import { useRouteParameter, useRoute, useTranslation } from '@rocket.chat/ui-contexts'; +import { Button, ButtonGroup } from '@rocket.chat/fuselage'; +import { useRouteParameter, useTranslation, useRouter } from '@rocket.chat/ui-contexts'; import type { ReactElement } from 'react'; import React from 'react'; @@ -10,8 +10,7 @@ import OAuthAppsTable from './OAuthAppsTable'; const OAuthAppsPage = (): ReactElement => { const t = useTranslation(); - - const router = useRoute('admin-oauth-apps'); + const router = useRouter(); const context = useRouteParameter('context'); const id = useRouteParameter('id'); @@ -19,16 +18,13 @@ const OAuthAppsPage = (): ReactElement => { return ( - - {context && ( - - )} + router.navigate('/admin/third-party-login') : undefined}> {!context && ( - + + + )} diff --git a/apps/meteor/client/views/omnichannel/departments/EditDepartment.tsx b/apps/meteor/client/views/omnichannel/departments/EditDepartment.tsx index 8b5c2cc80a6..0f64e41d242 100644 --- a/apps/meteor/client/views/omnichannel/departments/EditDepartment.tsx +++ b/apps/meteor/client/views/omnichannel/departments/EditDepartment.tsx @@ -17,7 +17,7 @@ import { FieldHint, } from '@rocket.chat/fuselage'; import { useDebouncedValue, useMutableCallback, useUniqueId } from '@rocket.chat/fuselage-hooks'; -import { useToastMessageDispatch, useRoute, useMethod, useEndpoint, useTranslation } from '@rocket.chat/ui-contexts'; +import { useToastMessageDispatch, useMethod, useEndpoint, useTranslation, useRouter } from '@rocket.chat/ui-contexts'; import { useQueryClient } from '@tanstack/react-query'; import React, { useMemo, useState } from 'react'; import { Controller, useForm } from 'react-hook-form'; @@ -100,7 +100,7 @@ const getInitialValues = ({ department, agents, allowedToForwardData }: InitialV function EditDepartment({ data, id, title, allowedToForwardData }: EditDepartmentProps) { const t = useTranslation(); - const departmentsRoute = useRoute('omnichannel-departments'); + const router = useRouter(); const queryClient = useQueryClient(); const { department, agents = [] } = data || {}; @@ -195,16 +195,12 @@ function EditDepartment({ data, id, title, allowedToForwardData }: EditDepartmen } queryClient.invalidateQueries(['/v1/livechat/department/:_id', id]); dispatchToastMessage({ type: 'success', message: t('Saved') }); - departmentsRoute.push({}); + router.navigate('/omnichannel/departments'); } catch (error) { dispatchToastMessage({ type: 'error', message: error }); } }); - const handleReturn = useMutableCallback(() => { - departmentsRoute.push({}); - }); - const isFormValid = isValid && isDirty; const formId = useUniqueId(); @@ -222,11 +218,8 @@ function EditDepartment({ data, id, title, allowedToForwardData }: EditDepartmen return ( - + router.navigate('/omnichannel/departments')}> - diff --git a/apps/meteor/ee/client/omnichannel/cannedResponses/CannedResponseEdit.tsx b/apps/meteor/ee/client/omnichannel/cannedResponses/CannedResponseEdit.tsx index 0cdbcae396f..3c903d57151 100644 --- a/apps/meteor/ee/client/omnichannel/cannedResponses/CannedResponseEdit.tsx +++ b/apps/meteor/ee/client/omnichannel/cannedResponses/CannedResponseEdit.tsx @@ -66,17 +66,17 @@ const CannedResponseEdit = ({ cannedResponseData }: CannedResponseEditProps) => return ( - - - - {cannedResponseData?._id && ( + router.navigate('/omnichannel/canned-responses')} + > + {cannedResponseData?._id && ( + - )} - + + )} -- GitLab From 8fd76f6798684dfe187acd06c7d5b0abb9736f99 Mon Sep 17 00:00:00 2001 From: gabriellsh <40830821+gabriellsh@users.noreply.github.com> Date: Tue, 19 Dec 2023 15:17:27 -0300 Subject: [PATCH 049/329] chore: Login api types (#31274) --- packages/rest-typings/src/v1/auth.ts | 15 +++++++++++++-- 1 file changed, 13 insertions(+), 2 deletions(-) diff --git a/packages/rest-typings/src/v1/auth.ts b/packages/rest-typings/src/v1/auth.ts index c09876b264a..cec772eaace 100644 --- a/packages/rest-typings/src/v1/auth.ts +++ b/packages/rest-typings/src/v1/auth.ts @@ -1,6 +1,12 @@ import Ajv from 'ajv'; -export type LoginProps = { user: Record | string; username: string; email: string; password: string; code: string }; +type Password = string | { hashed: string }; + +type EmailLogin = { email: string }; +type UsernameLogin = { username: string }; +type UserLogin = { user: UsernameLogin | EmailLogin | string }; + +export type LoginProps = (EmailLogin | UsernameLogin | UserLogin) & { code?: string; password?: Password }; type LogoutResponse = { message: string; @@ -24,7 +30,12 @@ const loginPropsSchema = { properties: { user: { type: 'object', nullable: true }, username: { type: 'string', nullable: true }, - password: { type: 'string', nullable: true }, + password: { + oneOf: [ + { type: 'string', nullable: true }, + { type: 'object', nullable: true }, + ], + }, email: { type: 'string', nullable: true }, code: { type: 'string', nullable: true }, }, -- GitLab From 4f2101db779a3db0838a1fd5fb1ce210147b1a05 Mon Sep 17 00:00:00 2001 From: Felipe <84182706+felipe-rod123@users.noreply.github.com> Date: Tue, 19 Dec 2023 16:14:55 -0300 Subject: [PATCH 050/329] fix: start workspace pt-br translation not showing correctly on register page (#31253) --- apps/meteor/packages/rocketchat-i18n/i18n/de.i18n.json | 2 +- apps/meteor/packages/rocketchat-i18n/i18n/es.i18n.json | 2 +- apps/meteor/packages/rocketchat-i18n/i18n/fi.i18n.json | 2 +- apps/meteor/packages/rocketchat-i18n/i18n/fr.i18n.json | 2 +- apps/meteor/packages/rocketchat-i18n/i18n/gl.i18n.json | 2 +- apps/meteor/packages/rocketchat-i18n/i18n/hu.i18n.json | 2 +- apps/meteor/packages/rocketchat-i18n/i18n/ja.i18n.json | 2 +- apps/meteor/packages/rocketchat-i18n/i18n/nl.i18n.json | 2 +- apps/meteor/packages/rocketchat-i18n/i18n/pl.i18n.json | 2 +- apps/meteor/packages/rocketchat-i18n/i18n/pt-BR.i18n.json | 2 +- apps/meteor/packages/rocketchat-i18n/i18n/ru.i18n.json | 2 +- apps/meteor/packages/rocketchat-i18n/i18n/sv.i18n.json | 2 +- apps/meteor/packages/rocketchat-i18n/i18n/zh-TW.i18n.json | 2 +- 13 files changed, 13 insertions(+), 13 deletions(-) diff --git a/apps/meteor/packages/rocketchat-i18n/i18n/de.i18n.json b/apps/meteor/packages/rocketchat-i18n/i18n/de.i18n.json index e8e385da980..de81f4a1a00 100644 --- a/apps/meteor/packages/rocketchat-i18n/i18n/de.i18n.json +++ b/apps/meteor/packages/rocketchat-i18n/i18n/de.i18n.json @@ -5426,7 +5426,7 @@ "onboarding.component.form.action.pasteHere": "Hier einfügen...", "onboarding.component.form.termsAndConditions": "Ich bin mit den Nutzungsvereinbarung und den Datenschutzbestimmungen einverstanden", "onboarding.component.emailCodeFallback": "Keine E-Mail erhalten? Noch einemal versenden oder E-Mailadresse ändern", - "onboarding.page.form.title": "<1>Starten wir Ihren Arbeitsbereich", + "onboarding.page.form.title": "Starten wir Ihren Arbeitsbereich", "onboarding.page.emailConfirmed.title": "E-Mail bestätigt", "onboarding.page.emailConfirmed.subtitle": "Sie können zu Ihrer Rocket.Chat-Anwendung zurückkehren - wir haben Ihren Arbeitsbereich bereits gestartet.", "onboarding.page.checkYourEmail.title": "Bitte prüfe Deine E-Mail", diff --git a/apps/meteor/packages/rocketchat-i18n/i18n/es.i18n.json b/apps/meteor/packages/rocketchat-i18n/i18n/es.i18n.json index e732242050b..908e6d6a9f7 100644 --- a/apps/meteor/packages/rocketchat-i18n/i18n/es.i18n.json +++ b/apps/meteor/packages/rocketchat-i18n/i18n/es.i18n.json @@ -4831,7 +4831,7 @@ "onboarding.component.form.action.completeRegistration": "Completar registro.", "onboarding.component.form.termsAndConditions": "Acepto los <1>términos y condiciones y la <3>política de privacidad", "onboarding.component.emailCodeFallback": "¿No has recibido el correo electrónico? <1>Volver a enviar o <3>cambiar correo electrónico", - "onboarding.page.form.title": "Vamos a <1>Iniciar tu espacio de trabajo", + "onboarding.page.form.title": "Vamos a Iniciar tu espacio de trabajo", "onboarding.page.emailConfirmed.title": "Correo electrónico confirmado", "onboarding.page.emailConfirmed.subtitle": "Puedes volver a la aplicación de Rocket.Chat. Ya hemos iniciado tu espacio de trabajo.", "onboarding.page.checkYourEmail.title": "Comprueba tu correo electrónico", diff --git a/apps/meteor/packages/rocketchat-i18n/i18n/fi.i18n.json b/apps/meteor/packages/rocketchat-i18n/i18n/fi.i18n.json index 33dff04a621..191fe4a82ea 100644 --- a/apps/meteor/packages/rocketchat-i18n/i18n/fi.i18n.json +++ b/apps/meteor/packages/rocketchat-i18n/i18n/fi.i18n.json @@ -5574,7 +5574,7 @@ "onboarding.component.form.action.confirm": "Vahvista", "onboarding.component.form.termsAndConditions": "Hyväksyn käyttöehdot <1>ja <3>tietosuojaselosteen", "onboarding.component.emailCodeFallback": "Etkö saanut sähköpostia? <1>Lähetä uudelleen tai <3>muuta sähköpostia", - "onboarding.page.form.title": " <1>Käynnistetään työtilasi", + "onboarding.page.form.title": " Käynnistetään työtilasi", "onboarding.page.emailConfirmed.title": "Sähköposti vahvistettu!", "onboarding.page.emailConfirmed.subtitle": "Voit palata chatsovellukseen, olemme jo käynnistäneet työtilasi.", "onboarding.page.checkYourEmail.title": "Tarkista sähköpostisi", diff --git a/apps/meteor/packages/rocketchat-i18n/i18n/fr.i18n.json b/apps/meteor/packages/rocketchat-i18n/i18n/fr.i18n.json index fd7f87718e8..816a042332a 100644 --- a/apps/meteor/packages/rocketchat-i18n/i18n/fr.i18n.json +++ b/apps/meteor/packages/rocketchat-i18n/i18n/fr.i18n.json @@ -4816,7 +4816,7 @@ "onboarding.component.form.action.pasteHere": "Coller ici...", "onboarding.component.form.termsAndConditions": "J'accepte les <1>Conditions d'utilisation et la <3>Politique de confidentialité", "onboarding.component.emailCodeFallback": "Vous n'avez pas reçu d'e-mail ? <1>Renvoyer ou <3>Modifier l'adresse mail", - "onboarding.page.form.title": "<1>Lancez votre espace de travail", + "onboarding.page.form.title": "Lancez votre espace de travail", "onboarding.page.emailConfirmed.title": "E-mail confirmé !", "onboarding.page.emailConfirmed.subtitle": "Vous pouvez retourner à votre application Rocket.Chat : nous avons déjà lancé votre espace de travail.", "onboarding.page.checkYourEmail.title": "Vérifiez votre messagerie", diff --git a/apps/meteor/packages/rocketchat-i18n/i18n/gl.i18n.json b/apps/meteor/packages/rocketchat-i18n/i18n/gl.i18n.json index 168339ec271..a17dba96f25 100644 --- a/apps/meteor/packages/rocketchat-i18n/i18n/gl.i18n.json +++ b/apps/meteor/packages/rocketchat-i18n/i18n/gl.i18n.json @@ -326,7 +326,7 @@ "onboarding.component.form.action.confirm": "Confirmar", "onboarding.component.form.termsAndConditions": "Acepto os <1>Termos e condicións e a <3>Política de privacidade", "onboarding.component.emailCodeFallback": "Non recibiches o correo electrónico? <1>Reenviar ou <3>Cambiar correo electrónico", - "onboarding.page.form.title": "Imos <1>Iniciar o teu espazo de traballo", + "onboarding.page.form.title": "Imos Iniciar o teu espazo de traballo", "onboarding.page.emailConfirmed.title": "Correo electrónico confirmado!", "onboarding.page.emailConfirmed.subtitle": "Podes volver á túa aplicación Rocket.Chat: xa lanzamos o teu espazo de traballo.", "onboarding.page.checkYourEmail.title": "Comprobe o seu correo electrónico", diff --git a/apps/meteor/packages/rocketchat-i18n/i18n/hu.i18n.json b/apps/meteor/packages/rocketchat-i18n/i18n/hu.i18n.json index 31bb9653e67..670e353850f 100644 --- a/apps/meteor/packages/rocketchat-i18n/i18n/hu.i18n.json +++ b/apps/meteor/packages/rocketchat-i18n/i18n/hu.i18n.json @@ -5355,7 +5355,7 @@ "onboarding.component.form.action.pasteHere": "Beillesztés ide...", "onboarding.component.form.termsAndConditions": "Elfogadom a <1>használati feltételeket és az <3>adatvédelmi irányelveket", "onboarding.component.emailCodeFallback": "Nem kapott levelet? <1>Újraküldés vagy <3>e-mail-cím megváltoztatása", - "onboarding.page.form.title": "<1>Indítsuk el a munkaterületét", + "onboarding.page.form.title": "Indítsuk el a munkaterületét", "onboarding.page.emailConfirmed.title": "E-mail-cím megerősítve!", "onboarding.page.emailConfirmed.subtitle": "Visszatérhet a Rocket.Chat alkalmazásához – már elindítottuk a munkaterületét.", "onboarding.page.checkYourEmail.title": "Nézze meg a leveleit", diff --git a/apps/meteor/packages/rocketchat-i18n/i18n/ja.i18n.json b/apps/meteor/packages/rocketchat-i18n/i18n/ja.i18n.json index 3d897682d64..80b1dcafa5d 100644 --- a/apps/meteor/packages/rocketchat-i18n/i18n/ja.i18n.json +++ b/apps/meteor/packages/rocketchat-i18n/i18n/ja.i18n.json @@ -4766,7 +4766,7 @@ "onboarding.component.form.action.pasteHere": "ここに貼り付け...", "onboarding.component.form.termsAndConditions": "<1>使用と<3>プライバシーポリシーに同意します", "onboarding.component.emailCodeFallback": "メールを受け取っていませんか?返信または <3>メールを変更してください", - "onboarding.page.form.title": "ワークスペースを<1>起動しましょう", + "onboarding.page.form.title": "ワークスペースを起動しましょう", "onboarding.page.emailConfirmed.title": "メールを確認しました!", "onboarding.page.emailConfirmed.subtitle": "Rocket.Chatアプリケーションに戻ることができます。すでにワークスペースを起動しています。", "onboarding.page.checkYourEmail.title": "メールのチェック", diff --git a/apps/meteor/packages/rocketchat-i18n/i18n/nl.i18n.json b/apps/meteor/packages/rocketchat-i18n/i18n/nl.i18n.json index 79e780d6520..25b0a24ca53 100644 --- a/apps/meteor/packages/rocketchat-i18n/i18n/nl.i18n.json +++ b/apps/meteor/packages/rocketchat-i18n/i18n/nl.i18n.json @@ -4804,7 +4804,7 @@ "onboarding.component.form.action.pasteHere": "Plak hier...", "onboarding.component.form.termsAndConditions": "Ik ga akkoord met de <1>Algemene voorwaarden en <3>Privacybeleid", "onboarding.component.emailCodeFallback": "Geen e-mail ontvangen? <1>Opnieuw verzenden of <3>E-mailadres wijzigen", - "onboarding.page.form.title": "<1>Lanceer uw werkruimte", + "onboarding.page.form.title": "Lanceer uw werkruimte", "onboarding.page.emailConfirmed.title": "E-mail bevestigd!", "onboarding.page.emailConfirmed.subtitle": "U kunt terugkeren naar uw Rocket.Chat-toepassing - we hebben uw werkruimte al gelanceerd.", "onboarding.page.checkYourEmail.title": "Controleer je e-mail", diff --git a/apps/meteor/packages/rocketchat-i18n/i18n/pl.i18n.json b/apps/meteor/packages/rocketchat-i18n/i18n/pl.i18n.json index 1906edf4708..3c315fcd5b9 100644 --- a/apps/meteor/packages/rocketchat-i18n/i18n/pl.i18n.json +++ b/apps/meteor/packages/rocketchat-i18n/i18n/pl.i18n.json @@ -5265,7 +5265,7 @@ "onboarding.component.form.action.pasteHere": "Wklej tutaj...", "onboarding.component.form.termsAndConditions": "Zgadzam się z <1>zasadami i warunkami i <3>Polityką prywatności.", "onboarding.component.emailCodeFallback": "Nie otrzymałeś emaila? <1>Wyślij ponownie lub <3>Zmień e-mail.", - "onboarding.page.form.title": "<1> Uruchom <1> swoją przestrzeń roboczą", + "onboarding.page.form.title": "Uruchom swoją przestrzeń roboczą", "onboarding.page.emailConfirmed.title": "Email potwierdzony!", "onboarding.page.emailConfirmed.subtitle": "Możesz wrócić do swojej aplikacji Rocket.Chat - uruchomiliśmy już Twój obszar roboczy.", "onboarding.page.checkYourEmail.title": "Sprawdź swój email", 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 fd2dc66f738..24aeb2bbb6c 100644 --- a/apps/meteor/packages/rocketchat-i18n/i18n/pt-BR.i18n.json +++ b/apps/meteor/packages/rocketchat-i18n/i18n/pt-BR.i18n.json @@ -4904,7 +4904,7 @@ "onboarding.component.form.action.pasteHere": "Cole aqui...", "onboarding.component.form.termsAndConditions": "Eu concordo com os <1>Termos e condições e a <3>Política de privacidade", "onboarding.component.emailCodeFallback": "Não recebeu e-mail? <1>Reenviar ou <3>Alterar e-mail", - "onboarding.page.form.title": "Vamos <1>Iniciar seu espaço de trabalho", + "onboarding.page.form.title": "Vamos iniciar seu espaço de trabalho", "onboarding.page.emailConfirmed.title": "E-mail confirmado!", "onboarding.page.emailConfirmed.subtitle": "Você pode retornar para seu aplicativo Rocket.Chat - nós já iniciamos seu espaço de trabalho.", "onboarding.page.checkYourEmail.title": "Verifique seu e-mail", diff --git a/apps/meteor/packages/rocketchat-i18n/i18n/ru.i18n.json b/apps/meteor/packages/rocketchat-i18n/i18n/ru.i18n.json index 46702496d0d..e1699812a3d 100644 --- a/apps/meteor/packages/rocketchat-i18n/i18n/ru.i18n.json +++ b/apps/meteor/packages/rocketchat-i18n/i18n/ru.i18n.json @@ -5002,7 +5002,7 @@ "onboarding.component.form.action.pasteHere": "Вставить сюда...", "onboarding.component.form.termsAndConditions": "Я принимаю <1>Положения и условия и <3>Политику конфиденциальности", "onboarding.component.emailCodeFallback": "Не получили электронное письмо? <1>Отправить повторно или <3>Изменить адрес электронной почты", - "onboarding.page.form.title": "<1>Запустим ваше рабочее пространство", + "onboarding.page.form.title": "Запустим ваше рабочее пространство", "onboarding.page.emailConfirmed.title": "Адрес электронной почты подтвержден!", "onboarding.page.emailConfirmed.subtitle": "Вы можете вернуться в приложение Rocket.Chat — мы уже запустили ваше рабочее пространство.", "onboarding.page.checkYourEmail.title": "Проверьте адрес электронной почты", diff --git a/apps/meteor/packages/rocketchat-i18n/i18n/sv.i18n.json b/apps/meteor/packages/rocketchat-i18n/i18n/sv.i18n.json index a4a86f4d154..01ef2e16e50 100644 --- a/apps/meteor/packages/rocketchat-i18n/i18n/sv.i18n.json +++ b/apps/meteor/packages/rocketchat-i18n/i18n/sv.i18n.json @@ -5576,7 +5576,7 @@ "onboarding.component.form.action.confirm": "Bekräfta", "onboarding.component.form.termsAndConditions": "Jag godkänner <1>villkoren och <3>integritetspolicyn", "onboarding.component.emailCodeFallback": "Fick du inget e-postmeddelande? <1>Skicka igen eller <3>ändra e-postadressen", - "onboarding.page.form.title": "Nu <1>startar vi arbetsytan", + "onboarding.page.form.title": "Nu startar vi arbetsytan", "onboarding.page.emailConfirmed.title": "E-postadressen har bekräftats.", "onboarding.page.emailConfirmed.subtitle": "Du kan gå tillbaka till Rocket.Chat-applikationen. Vi har startat din arbetsyta.", "onboarding.page.checkYourEmail.title": "Titta i inkorgen", 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 e0d34d8edfc..c1a4796794d 100644 --- a/apps/meteor/packages/rocketchat-i18n/i18n/zh-TW.i18n.json +++ b/apps/meteor/packages/rocketchat-i18n/i18n/zh-TW.i18n.json @@ -4520,7 +4520,7 @@ "onboarding.component.form.action.pasteHere": "貼在這裡...", "onboarding.component.form.termsAndConditions": "我同意<1>條款及條件和<3>隱私權政策", "onboarding.component.emailCodeFallback": "沒有收到電子郵件?<1>重新傳送或<3>變更電子郵件", - "onboarding.page.form.title": "<1>啟動您的工作空間吧!", + "onboarding.page.form.title": "啟動您的工作空間吧!", "onboarding.page.emailConfirmed.title": "電子郵件已確認!", "onboarding.page.emailConfirmed.subtitle": "您可以返回 Rocket.Chat 應用程式 – 我們已啟動您的工作空間。", "onboarding.page.checkYourEmail.title": "請查看您的電子郵件", -- GitLab From d20c76505654320ac898ed5a45c34d36fcffe6c4 Mon Sep 17 00:00:00 2001 From: Diego Sampaio Date: Tue, 19 Dec 2023 16:39:51 -0300 Subject: [PATCH 051/329] chore: remove `beforeSaveMessage` callback (#31273) --- apps/meteor/app/lib/client/methods/sendMessage.ts | 1 - apps/meteor/app/lib/server/functions/sendMessage.js | 2 -- apps/meteor/app/lib/server/functions/updateMessage.ts | 2 -- apps/meteor/app/message-pin/server/pinMessage.ts | 5 ----- apps/meteor/client/methods/updateMessage.ts | 2 -- apps/meteor/lib/callbacks.ts | 1 - 6 files changed, 13 deletions(-) diff --git a/apps/meteor/app/lib/client/methods/sendMessage.ts b/apps/meteor/app/lib/client/methods/sendMessage.ts index 65da03ac0e6..e824c9e491a 100644 --- a/apps/meteor/app/lib/client/methods/sendMessage.ts +++ b/apps/meteor/app/lib/client/methods/sendMessage.ts @@ -41,7 +41,6 @@ Meteor.methods({ return; } - message = await callbacks.run('beforeSaveMessage', message); await onClientMessageReceived(message as IMessage).then((message) => { ChatMessage.insert(message); return callbacks.run('afterSaveMessage', message, room); diff --git a/apps/meteor/app/lib/server/functions/sendMessage.js b/apps/meteor/app/lib/server/functions/sendMessage.js index 4886b13afba..9ae5ff7e8b1 100644 --- a/apps/meteor/app/lib/server/functions/sendMessage.js +++ b/apps/meteor/app/lib/server/functions/sendMessage.js @@ -251,8 +251,6 @@ export const sendMessage = async function (user, message, room, upsert = false, message = await Message.beforeSave({ message, room, user }); - message = await callbacks.run('beforeSaveMessage', message, room); - if (message) { if (message.t === 'otr') { const otrStreamer = notifications.streamRoomMessage; diff --git a/apps/meteor/app/lib/server/functions/updateMessage.ts b/apps/meteor/app/lib/server/functions/updateMessage.ts index 88c0b829e77..8abb8b4b0a0 100644 --- a/apps/meteor/app/lib/server/functions/updateMessage.ts +++ b/apps/meteor/app/lib/server/functions/updateMessage.ts @@ -55,8 +55,6 @@ export const updateMessage = async function ( return; } - message = await callbacks.run('beforeSaveMessage', message); - // TODO remove type cast message = await Message.beforeSave({ message: message as IMessage, room, user }); diff --git a/apps/meteor/app/message-pin/server/pinMessage.ts b/apps/meteor/app/message-pin/server/pinMessage.ts index 652f465188c..ff38c7e8d4b 100644 --- a/apps/meteor/app/message-pin/server/pinMessage.ts +++ b/apps/meteor/app/message-pin/server/pinMessage.ts @@ -7,7 +7,6 @@ import { check } from 'meteor/check'; import { Meteor } from 'meteor/meteor'; import { Apps, AppEvents } from '../../../ee/server/apps/orchestrator'; -import { callbacks } from '../../../lib/callbacks'; import { isTruthy } from '../../../lib/isTruthy'; import { broadcastMessageSentEvent } from '../../../server/modules/watchers/lib/messages'; import { canAccessRoomAsync, roomAccessAttributes } from '../../authorization/server'; @@ -109,8 +108,6 @@ Meteor.methods({ username: me.username, }; - originalMessage = await callbacks.run('beforeSaveMessage', originalMessage); - originalMessage = await Message.beforeSave({ message: originalMessage, room, user: me }); await Messages.setPinnedByIdAndUserId(originalMessage._id, originalMessage.pinnedBy, originalMessage.pinned); @@ -212,8 +209,6 @@ Meteor.methods({ throw new Meteor.Error('not-authorized', 'Not Authorized', { method: 'unpinMessage' }); } - originalMessage = await callbacks.run('beforeSaveMessage', originalMessage); - originalMessage = await Message.beforeSave({ message: originalMessage, room, user: me }); if (isTheLastMessage(room, message)) { diff --git a/apps/meteor/client/methods/updateMessage.ts b/apps/meteor/client/methods/updateMessage.ts index 27489d281f4..deb2878072c 100644 --- a/apps/meteor/client/methods/updateMessage.ts +++ b/apps/meteor/client/methods/updateMessage.ts @@ -8,7 +8,6 @@ import { hasAtLeastOnePermission, hasPermission } from '../../app/authorization/ import { ChatMessage } from '../../app/models/client'; import { settings } from '../../app/settings/client'; import { t } from '../../app/utils/lib/i18n'; -import { callbacks } from '../../lib/callbacks'; import { dispatchToastMessage } from '../lib/toast'; Meteor.methods({ @@ -74,7 +73,6 @@ Meteor.methods({ username: me.username, }; - message = (await callbacks.run('beforeSaveMessage', message)) as IEditedMessage; const messageObject: Partial = { editedAt: message.editedAt, editedBy: message.editedBy, diff --git a/apps/meteor/lib/callbacks.ts b/apps/meteor/lib/callbacks.ts index eae3dc1dfb1..dcaaf3d15ae 100644 --- a/apps/meteor/lib/callbacks.ts +++ b/apps/meteor/lib/callbacks.ts @@ -122,7 +122,6 @@ type ChainedCallbackSignatures = { 'livechat.onLoadConfigApi': (config: { room: IOmnichannelRoom }) => Record; - 'beforeSaveMessage': (message: IMessage, room?: IRoom) => IMessage; 'afterCreateUser': (user: IUser) => IUser; 'afterDeleteRoom': (rid: IRoom['_id']) => IRoom['_id']; 'livechat:afterOnHold': (room: Pick) => Pick; -- GitLab From f6a525a444047cd8d9a3a3f08d711a06d558a821 Mon Sep 17 00:00:00 2001 From: gabriellsh <40830821+gabriellsh@users.noreply.github.com> Date: Tue, 19 Dec 2023 19:01:49 -0300 Subject: [PATCH 052/329] fix: SDK login methods not saving token (#31278) --- .changeset/real-items-tan.md | 5 +++ ee/packages/ddp-client/src/DDPSDK.ts | 2 +- ee/packages/ddp-client/src/types/Account.ts | 34 ++++++++++++++++----- 3 files changed, 33 insertions(+), 8 deletions(-) create mode 100644 .changeset/real-items-tan.md diff --git a/.changeset/real-items-tan.md b/.changeset/real-items-tan.md new file mode 100644 index 00000000000..302ac3eacfe --- /dev/null +++ b/.changeset/real-items-tan.md @@ -0,0 +1,5 @@ +--- +"@rocket.chat/ddp-client": patch +--- + +SDK login methods not saving token diff --git a/ee/packages/ddp-client/src/DDPSDK.ts b/ee/packages/ddp-client/src/DDPSDK.ts index bfd80980bb8..61e0dc6fe28 100644 --- a/ee/packages/ddp-client/src/DDPSDK.ts +++ b/ee/packages/ddp-client/src/DDPSDK.ts @@ -105,7 +105,7 @@ export class DDPSDK implements SDK { } return { 'X-User-Id': account.uid, - 'X-Auth-Token': account.user?.token, + 'X-Auth-Token': account.user.token, }; } })({ baseUrl: url }); diff --git a/ee/packages/ddp-client/src/types/Account.ts b/ee/packages/ddp-client/src/types/Account.ts index b5dde5e5ac3..654ae2c2fb4 100644 --- a/ee/packages/ddp-client/src/types/Account.ts +++ b/ee/packages/ddp-client/src/types/Account.ts @@ -23,7 +23,7 @@ export class AccountImpl uid: string | undefined; user: { id: string; - username: string; + username?: string; token?: string; tokenExpires?: Date; }; @@ -32,7 +32,7 @@ export class AccountImpl { uid?: string; - user?: { id: string; username: string; token?: string; tokenExpires?: Date }; + user?: { id: string; username?: string; token?: string; tokenExpires?: Date }; constructor(private readonly client: ClientStream) { super(); @@ -60,8 +60,24 @@ export class AccountImpl }); } + private saveCredentials(id: string, token: string, tokenExpires: string) { + this.user = { + ...this.user, + token, + tokenExpires: new Date(tokenExpires), + id, + }; + this.uid = id; + this.emit('uid', this.uid); + this.emit('user', this.user); + } + async loginWithPassword(username: string, password: string): Promise { - const { uid } = await this.client.callAsyncWithOptions( + const { + id, + token: resultToken, + tokenExpires: { $date }, + } = await this.client.callAsyncWithOptions( 'login', { wait: true, @@ -71,8 +87,8 @@ export class AccountImpl password: { digest: password, algorithm: 'sha-256' }, }, ); - this.uid = uid; - this.emit('uid', this.uid); + + this.saveCredentials(id, resultToken, $date); } async loginWithToken(token: string) { @@ -86,8 +102,12 @@ export class AccountImpl }, ); - this.uid = result.id; - this.emit('uid', this.uid); + const { + id, + token: resultToken, + tokenExpires: { $date }, + } = result; + this.saveCredentials(id, resultToken, $date); return result; } -- GitLab From 86f1f3cabcbdf94cc8585a7f41c486d6ea81d468 Mon Sep 17 00:00:00 2001 From: Hardik Bhatia <98163873+hardikbhatia777@users.noreply.github.com> Date: Wed, 20 Dec 2023 17:13:32 +0530 Subject: [PATCH 053/329] fix: Store custom status in localStorage (#31237) --- apps/meteor/client/sidebar/header/EditStatusModal.tsx | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) diff --git a/apps/meteor/client/sidebar/header/EditStatusModal.tsx b/apps/meteor/client/sidebar/header/EditStatusModal.tsx index 18ee86a19c2..ba250d92707 100644 --- a/apps/meteor/client/sidebar/header/EditStatusModal.tsx +++ b/apps/meteor/client/sidebar/header/EditStatusModal.tsx @@ -1,6 +1,6 @@ import type { IUser } from '@rocket.chat/core-typings'; import { Field, TextInput, FieldGroup, Modal, Button, Box, FieldLabel, FieldRow, FieldError, FieldHint } from '@rocket.chat/fuselage'; -import { useMutableCallback } from '@rocket.chat/fuselage-hooks'; +import { useLocalStorage, useMutableCallback } from '@rocket.chat/fuselage-hooks'; import { useToastMessageDispatch, useSetting, useTranslation, useEndpoint } from '@rocket.chat/ui-contexts'; import type { ReactElement, ChangeEvent, ComponentProps, FormEvent } from 'react'; import React, { useState, useCallback } from 'react'; @@ -17,9 +17,11 @@ type EditStatusModalProps = { const EditStatusModal = ({ onClose, userStatus, userStatusText }: EditStatusModalProps): ReactElement => { const allowUserStatusMessageChange = useSetting('Accounts_AllowUserStatusMessageChange'); const dispatchToastMessage = useToastMessageDispatch(); + const [customStatus, setCustomStatus] = useLocalStorage('Local_Custom_Status', ''); + const initialStatusText = customStatus || userStatusText; const t = useTranslation(); - const [statusText, setStatusText] = useState(userStatusText); + const [statusText, setStatusText] = useState(initialStatusText); const [statusType, setStatusType] = useState(userStatus); const [statusTextError, setStatusTextError] = useState(); @@ -40,6 +42,7 @@ const EditStatusModal = ({ onClose, userStatus, userStatusText }: EditStatusModa const handleSaveStatus = useCallback(async () => { try { await setUserStatus({ message: statusText, status: statusType }); + setCustomStatus(statusText); dispatchToastMessage({ type: 'success', message: t('StatusMessage_Changed_Successfully') }); } catch (error) { dispatchToastMessage({ type: 'error', message: error }); -- GitLab From 74cad8411a29a6062cd43bf3d757e809efe472a1 Mon Sep 17 00:00:00 2001 From: Douglas Fabris Date: Wed, 20 Dec 2023 10:06:32 -0300 Subject: [PATCH 054/329] fix: Can't create public channels with joinCode (#31277) --- .changeset/thick-nails-explode.md | 5 ++ .../views/room/composer/ComposerContainer.tsx | 10 +-- .../composer/ComposerJoinWithPassword.tsx | 73 +++++++++---------- .../Info/EditRoomInfo/EditRoomInfo.tsx | 5 +- 4 files changed, 46 insertions(+), 47 deletions(-) create mode 100644 .changeset/thick-nails-explode.md diff --git a/.changeset/thick-nails-explode.md b/.changeset/thick-nails-explode.md new file mode 100644 index 00000000000..5cf9020e191 --- /dev/null +++ b/.changeset/thick-nails-explode.md @@ -0,0 +1,5 @@ +--- +'@rocket.chat/meteor': patch +--- + +Fixed the problem of not being possible to add a join code to a public room diff --git a/apps/meteor/client/views/room/composer/ComposerContainer.tsx b/apps/meteor/client/views/room/composer/ComposerContainer.tsx index 66e72de2a0f..3988c080bff 100644 --- a/apps/meteor/client/views/room/composer/ComposerContainer.tsx +++ b/apps/meteor/client/views/room/composer/ComposerContainer.tsx @@ -1,4 +1,5 @@ import { isOmnichannelRoom, isRoomFederated, isVoipRoom } from '@rocket.chat/core-typings'; +import { usePermission } from '@rocket.chat/ui-contexts'; import type { ReactElement } from 'react'; import React, { memo } from 'react'; @@ -18,19 +19,14 @@ import { useMessageComposerIsReadOnly } from './hooks/useMessageComposerIsReadOn const ComposerContainer = ({ children, ...props }: ComposerMessageProps): ReactElement => { const room = useRoom(); - - const mustJoinWithCode = !props.subscription && room.joinCodeRequired; + const canJoinWithoutCode = usePermission('join-without-join-code'); + const mustJoinWithCode = !props.subscription && room.joinCodeRequired && !canJoinWithoutCode; const isAnonymous = useMessageComposerIsAnonymous(); - const isBlockedOrBlocker = useMessageComposerIsBlocked({ subscription: props.subscription }); - const isReadOnly = useMessageComposerIsReadOnly(room._id, props.subscription); - const isOmnichannel = isOmnichannelRoom(room); - const isFederation = isRoomFederated(room); - const isVoip = isVoipRoom(room); if (isOmnichannel) { diff --git a/apps/meteor/client/views/room/composer/ComposerJoinWithPassword.tsx b/apps/meteor/client/views/room/composer/ComposerJoinWithPassword.tsx index 75230688309..bef2dfe3d35 100644 --- a/apps/meteor/client/views/room/composer/ComposerJoinWithPassword.tsx +++ b/apps/meteor/client/views/room/composer/ComposerJoinWithPassword.tsx @@ -1,53 +1,50 @@ -import { TextInput } from '@rocket.chat/fuselage'; +import { PasswordInput } from '@rocket.chat/fuselage'; import { MessageFooterCallout, MessageFooterCalloutAction, MessageFooterCalloutContent } from '@rocket.chat/ui-composer'; -import { useTranslation, useEndpoint } from '@rocket.chat/ui-contexts'; -import type { ChangeEvent, ReactElement, FormEventHandler } from 'react'; -import React, { useCallback, useState } from 'react'; +import { useTranslation, useEndpoint, useToastMessageDispatch } from '@rocket.chat/ui-contexts'; +import type { ReactElement } from 'react'; +import React from 'react'; +import { Controller, useForm } from 'react-hook-form'; import { useRoom } from '../contexts/RoomContext'; const ComposerJoinWithPassword = (): ReactElement => { - const room = useRoom(); - const [joinCode, setJoinPassword] = useState(''); - - const [error, setError] = useState(''); - const t = useTranslation(); - - const joinEndpoint = useEndpoint('POST', '/v1/channels.join'); - - const join = useCallback>( - async (e) => { - e.preventDefault(); - try { - await joinEndpoint({ - roomId: room._id, - joinCode, - }); - } catch (error: any) { - setError(error.error); - } - }, - [joinEndpoint, room._id, joinCode], - ); - - const handleChange = useCallback((e: ChangeEvent) => { - setJoinPassword(e.target.value); - setError(''); - }, []); + const room = useRoom(); + const dispatchToastMessage = useToastMessageDispatch(); + + const joinChannelEndpoint = useEndpoint('POST', '/v1/channels.join'); + const { + control, + handleSubmit, + setError, + formState: { errors, isDirty }, + } = useForm({ defaultValues: { joinCode: '' } }); + + const handleJoinChannel = async ({ joinCode }: { joinCode: string }) => { + try { + await joinChannelEndpoint({ + roomId: room._id, + joinCode, + }); + } catch (error: any) { + setError('joinCode', { type: error.errorType, message: error.error }); + dispatchToastMessage({ type: 'error', message: error }); + } + }; return ( - + {t('you_are_in_preview')} - ( + + )} /> - + {t('Join_with_password')} diff --git a/apps/meteor/client/views/room/contextualBar/Info/EditRoomInfo/EditRoomInfo.tsx b/apps/meteor/client/views/room/contextualBar/Info/EditRoomInfo/EditRoomInfo.tsx index bd472f4a790..9b372c72ac8 100644 --- a/apps/meteor/client/views/room/contextualBar/Info/EditRoomInfo/EditRoomInfo.tsx +++ b/apps/meteor/client/views/room/contextualBar/Info/EditRoomInfo/EditRoomInfo.tsx @@ -2,6 +2,7 @@ import type { IRoomWithRetentionPolicy } from '@rocket.chat/core-typings'; import { isRoomFederated } from '@rocket.chat/core-typings'; import type { SelectOption } from '@rocket.chat/fuselage'; import { + FieldError, Field, FieldRow, FieldLabel, @@ -97,7 +98,7 @@ const EditRoomInfo = ({ room, onClickClose, onClickBack }: EditRoomInfoProps) => const handleArchive = useArchiveRoom(room); - const handleUpdateRoomData = useMutableCallback(async ({ hideSysMes, ...formData }) => { + const handleUpdateRoomData = useMutableCallback(async ({ hideSysMes, joinCodeRequired, ...formData }) => { const data = getDirtyFields(formData, dirtyFields); delete data.archived; @@ -169,7 +170,7 @@ const EditRoomInfo = ({ room, onClickClose, onClickBack }: EditRoomInfoProps) => render={({ field }) => } /> - {errors.roomName && {errors.roomName.message}} + {errors.roomName && {errors.roomName.message}} {canViewDescription && ( -- GitLab From b42aa49fbd752ec29534500b13cdd75f6810c679 Mon Sep 17 00:00:00 2001 From: Kevin Aleman Date: Wed, 20 Dec 2023 08:58:37 -0600 Subject: [PATCH 055/329] fix: Fix wrong callback usage on after/before user actions (#31264) --- .changeset/sour-kids-heal.md | 6 ++++ .../ee/app/authorization/server/callback.ts | 32 +++++++++++++++++-- .../api/livechat/19-business-hours.ts | 1 + 3 files changed, 36 insertions(+), 3 deletions(-) create mode 100644 .changeset/sour-kids-heal.md diff --git a/.changeset/sour-kids-heal.md b/.changeset/sour-kids-heal.md new file mode 100644 index 00000000000..9dbeff968bb --- /dev/null +++ b/.changeset/sour-kids-heal.md @@ -0,0 +1,6 @@ +--- +"@rocket.chat/meteor": patch +"@rocket.chat/ddp-client": patch +--- + +Fixed a problem where chained callbacks' return value was being overrided by some callbacks returning something different, causing callbacks with lower priority to operate on invalid values diff --git a/apps/meteor/ee/app/authorization/server/callback.ts b/apps/meteor/ee/app/authorization/server/callback.ts index 4da07733ab5..707d45d96e0 100644 --- a/apps/meteor/ee/app/authorization/server/callback.ts +++ b/apps/meteor/ee/app/authorization/server/callback.ts @@ -12,10 +12,36 @@ License.onInstall(() => { callbacks.priority.HIGH, 'validateUserRoles', ); - callbacks.add('afterSaveUser', () => License.shouldPreventAction('activeUsers'), callbacks.priority.HIGH, 'validateUserRoles'); - callbacks.add('afterDeleteUser', () => License.shouldPreventAction('activeUsers'), callbacks.priority.HIGH, 'validateUserRoles'); + callbacks.add( + 'afterSaveUser', + async (user) => { + await License.shouldPreventAction('activeUsers'); - callbacks.add('afterDeactivateUser', () => License.shouldPreventAction('activeUsers'), callbacks.priority.HIGH, 'validateUserStatus'); + return user; + }, + callbacks.priority.HIGH, + 'validateUserRoles', + ); + callbacks.add( + 'afterDeleteUser', + async (user) => { + await License.shouldPreventAction('activeUsers'); + + return user; + }, + callbacks.priority.HIGH, + 'validateUserRoles', + ); + + callbacks.add( + 'afterDeactivateUser', + async (user) => { + await License.shouldPreventAction('activeUsers'); + return user; + }, + callbacks.priority.HIGH, + 'validateUserStatus', + ); callbacks.add( 'beforeActivateUser', diff --git a/apps/meteor/tests/end-to-end/api/livechat/19-business-hours.ts b/apps/meteor/tests/end-to-end/api/livechat/19-business-hours.ts index 0585c20bf12..936c6525f9f 100644 --- a/apps/meteor/tests/end-to-end/api/livechat/19-business-hours.ts +++ b/apps/meteor/tests/end-to-end/api/livechat/19-business-hours.ts @@ -806,6 +806,7 @@ describe('LIVECHAT - business hours', function () { }); it('should verify if agent becomes unavailable to take chats when user is deactivated', async () => { + await makeAgentAvailable(await login(agent.username, password)); await setUserActiveStatus(agent._id, false); const latestAgent = await getUserByUsername(agent.username); -- GitLab From e923ee4df0b4ad89312a3e143e09883b05b55781 Mon Sep 17 00:00:00 2001 From: Matheus Barbosa Silva <36537004+matheusbsilva137@users.noreply.github.com> Date: Wed, 20 Dec 2023 14:51:59 -0300 Subject: [PATCH 056/329] chore: Update "Proposed changes" PR template's section (#31282) --- .github/PULL_REQUEST_TEMPLATE.md | 6 ++---- 1 file changed, 2 insertions(+), 4 deletions(-) diff --git a/.github/PULL_REQUEST_TEMPLATE.md b/.github/PULL_REQUEST_TEMPLATE.md index 6e14d4935ea..0b2af2ef788 100644 --- a/.github/PULL_REQUEST_TEMPLATE.md +++ b/.github/PULL_REQUEST_TEMPLATE.md @@ -22,15 +22,13 @@ --> ## Proposed changes (including videos or screenshots) - - - ## Issue(s) -- GitLab From d6a836056445594545e2537f0b08107a0da558a2 Mon Sep 17 00:00:00 2001 From: Kevin Aleman Date: Thu, 21 Dec 2023 09:33:37 -0600 Subject: [PATCH 057/329] fix: Remove `ls` & `lr` from subscription creation on Omni rooms (#31283) --- .changeset/sharp-rings-smash.md | 6 ++++++ .../livechat/client/startup/notifyUnreadRooms.js | 16 +++++++++------- apps/meteor/app/livechat/server/lib/Helper.ts | 4 +--- 3 files changed, 16 insertions(+), 10 deletions(-) create mode 100644 .changeset/sharp-rings-smash.md diff --git a/.changeset/sharp-rings-smash.md b/.changeset/sharp-rings-smash.md new file mode 100644 index 00000000000..0e4360ff46d --- /dev/null +++ b/.changeset/sharp-rings-smash.md @@ -0,0 +1,6 @@ +--- +"@rocket.chat/meteor": patch +--- + +Fixed a problem with the subscription creation on Omnichannel rooms. +Rooms were being created as seen, causing sound notifications to not work diff --git a/apps/meteor/app/livechat/client/startup/notifyUnreadRooms.js b/apps/meteor/app/livechat/client/startup/notifyUnreadRooms.js index 3aaace847f6..a758c72cadd 100644 --- a/apps/meteor/app/livechat/client/startup/notifyUnreadRooms.js +++ b/apps/meteor/app/livechat/client/startup/notifyUnreadRooms.js @@ -1,9 +1,8 @@ -import { Users } from '@rocket.chat/models'; import { Meteor } from 'meteor/meteor'; import { Tracker } from 'meteor/tracker'; import { CustomSounds } from '../../../custom-sounds/client'; -import { Subscriptions } from '../../../models/client'; +import { Subscriptions, Users } from '../../../models/client'; import { settings } from '../../../settings/client'; import { getUserPreference } from '../../../utils/client'; @@ -16,17 +15,20 @@ Meteor.startup(() => { return; } - const subs = await Subscriptions.find({ t: 'l', ls: { $exists: 0 }, open: true }).count(); + const subs = await Subscriptions.find({ t: 'l', ls: { $exists: false }, open: true }).count(); if (subs === 0) { audio && audio.pause(); return; } - const user = await Users.findOne(Meteor.userId(), { - projection: { - 'settings.preferences.newRoomNotification': 1, + const user = await Users.findOne( + { _id: Meteor.userId() }, + { + projection: { + 'settings.preferences.newRoomNotification': 1, + }, }, - }); + ); const newRoomNotification = getUserPreference(user, 'newRoomNotification'); diff --git a/apps/meteor/app/livechat/server/lib/Helper.ts b/apps/meteor/app/livechat/server/lib/Helper.ts index 78351cdc668..3702eb1536a 100644 --- a/apps/meteor/app/livechat/server/lib/Helper.ts +++ b/apps/meteor/app/livechat/server/lib/Helper.ts @@ -257,10 +257,8 @@ export const createLivechatSubscription = async ( status, }, ts: new Date(), - lr: new Date(), - ls: new Date(), ...(department && { department }), - }; + } as InsertionModel; return Subscriptions.insertOne(subscriptionData); }; -- GitLab From e22d6f08f83b2e540eab7d7de78026703a30ffb0 Mon Sep 17 00:00:00 2001 From: Douglas Fabris Date: Thu, 21 Dec 2023 14:29:35 -0300 Subject: [PATCH 058/329] chore: Show `BusinessHourDisabledPage` when setting is disabled (#31295) --- .../BusinessHoursDisabledPage.tsx | 36 +++++++++++++++++++ .../businessHours/BusinessHoursRouter.tsx | 11 ++++-- .../rocketchat-i18n/i18n/en.i18n.json | 4 +++ 3 files changed, 48 insertions(+), 3 deletions(-) create mode 100644 apps/meteor/client/views/omnichannel/businessHours/BusinessHoursDisabledPage.tsx diff --git a/apps/meteor/client/views/omnichannel/businessHours/BusinessHoursDisabledPage.tsx b/apps/meteor/client/views/omnichannel/businessHours/BusinessHoursDisabledPage.tsx new file mode 100644 index 00000000000..fcee31bbc3b --- /dev/null +++ b/apps/meteor/client/views/omnichannel/businessHours/BusinessHoursDisabledPage.tsx @@ -0,0 +1,36 @@ +import { States, StatesIcon, StatesTitle, StatesSubtitle, StatesActions, StatesAction, StatesLink, Box } from '@rocket.chat/fuselage'; +import { useRole, useRouter, useTranslation } from '@rocket.chat/ui-contexts'; +import React from 'react'; + +import { Page, PageHeader, PageContent } from '../../../components/Page'; + +const BusinessHoursDisabledPage = () => { + const t = useTranslation(); + const router = useRouter(); + const isAdmin = useRole('admin'); + + return ( + + + + + + + {t('Business_hours_is_disabled')} + {t('Business_hours_is_disabled_description')} + {isAdmin && ( + + router.navigate('/admin/settings/Omnichannel')}>{t('Enable_business_hours')} + + )} + + {t('Learn_more_about_business_hours')} + + + + + + ); +}; + +export default BusinessHoursDisabledPage; diff --git a/apps/meteor/client/views/omnichannel/businessHours/BusinessHoursRouter.tsx b/apps/meteor/client/views/omnichannel/businessHours/BusinessHoursRouter.tsx index f25b00a87c1..8dd39aa07d5 100644 --- a/apps/meteor/client/views/omnichannel/businessHours/BusinessHoursRouter.tsx +++ b/apps/meteor/client/views/omnichannel/businessHours/BusinessHoursRouter.tsx @@ -1,26 +1,31 @@ import { LivechatBusinessHourTypes } from '@rocket.chat/core-typings'; -import { useRouteParameter, useRouter } from '@rocket.chat/ui-contexts'; +import { useRouteParameter, useRouter, useSetting } from '@rocket.chat/ui-contexts'; import React, { useEffect } from 'react'; +import BusinessHoursDisabledPage from './BusinessHoursDisabledPage'; import BusinessHoursMultiplePage from './BusinessHoursMultiplePage'; import EditBusinessHours from './EditBusinessHours'; import EditBusinessHoursWithData from './EditBusinessHoursWithData'; import { useIsSingleBusinessHours } from './useIsSingleBusinessHours'; const BusinessHoursRouter = () => { + const router = useRouter(); const context = useRouteParameter('context'); const id = useRouteParameter('id'); const type = useRouteParameter('type') as LivechatBusinessHourTypes; + const businessHoursEnabled = useSetting('Livechat_enable_business_hours'); const isSingleBH = useIsSingleBusinessHours(); - const router = useRouter(); - useEffect(() => { if (isSingleBH) { router.navigate('/omnichannel/businessHours/edit/default'); } }, [isSingleBH, router, context, type]); + if (!businessHoursEnabled) { + return ; + } + if (context === 'edit' || isSingleBH) { return type ? : null; } diff --git a/apps/meteor/packages/rocketchat-i18n/i18n/en.i18n.json b/apps/meteor/packages/rocketchat-i18n/i18n/en.i18n.json index de0e3cfed58..72ee9842e0b 100644 --- a/apps/meteor/packages/rocketchat-i18n/i18n/en.i18n.json +++ b/apps/meteor/packages/rocketchat-i18n/i18n/en.i18n.json @@ -22,6 +22,7 @@ "This_room_encryption_has_been_disabled_by__username_": "This room's encryption has been disabled by {{username}}", "Third_party_login": "Third-party login", "Enabled_E2E_Encryption_for_this_room": "enabled E2E Encryption for this room", + "Enable_business_hours": "Enable business hours", "disabled": "disabled", "Disabled_E2E_Encryption_for_this_room": "disabled E2E Encryption for this room", "@username": "@username", @@ -839,6 +840,8 @@ "Business_Hour_Removed": "Business Hour Removed", "Business_Hours": "Business Hours", "Business_hours_enabled": "Business hours enabled", + "Business_hours_is_disabled": "Business hours is disabled", + "Business_hours_is_disabled_description": "Enable business hours at the workspace admin panel to let customers know when you're available and when can they expect a response.", "Business_hours_updated": "Business hours updated", "busy": "busy", "Busy": "Busy", @@ -3047,6 +3050,7 @@ "Lead_capture_phone_regex": "Lead capture phone regex", "Learn_more": "Learn more", "Learn_more_about_agents": "Learn more about agents", + "Learn_more_about_business_hours": "Learn more about business hours", "Learn_more_about_canned_responses": "Learn more about canned responses", "Learn_more_about_contacts": "Learn more about contacts", "Learn_more_about_current_chats": "Learn more about current chats", -- GitLab From 077d04f0a854c0c2445031ea748b686c3a4749de Mon Sep 17 00:00:00 2001 From: Douglas Fabris Date: Thu, 21 Dec 2023 15:35:30 -0300 Subject: [PATCH 059/329] fix: Remove trial param from checking cancel subscription button (#31297) --- .changeset/lucky-apricots-change.md | 5 +++++ .../client/views/admin/subscription/SubscriptionPage.tsx | 2 +- 2 files changed, 6 insertions(+), 1 deletion(-) create mode 100644 .changeset/lucky-apricots-change.md diff --git a/.changeset/lucky-apricots-change.md b/.changeset/lucky-apricots-change.md new file mode 100644 index 00000000000..12d93b4fb65 --- /dev/null +++ b/.changeset/lucky-apricots-change.md @@ -0,0 +1,5 @@ +--- +"@rocket.chat/meteor": patch +--- + +Fixed an issue allowing admin user cancelling subscription when license's trial param is provided diff --git a/apps/meteor/client/views/admin/subscription/SubscriptionPage.tsx b/apps/meteor/client/views/admin/subscription/SubscriptionPage.tsx index 40fd57fb70b..1208a6eea9d 100644 --- a/apps/meteor/client/views/admin/subscription/SubscriptionPage.tsx +++ b/apps/meteor/client/views/admin/subscription/SubscriptionPage.tsx @@ -177,7 +177,7 @@ const SubscriptionPage = () => { )} - {Boolean(licensesData?.trial || licensesData?.license?.information.cancellable) && ( + {Boolean(licensesData?.license?.information.cancellable) && ( -- GitLab From eee67dc4125a0fbade69d2581fc442174c3b9ea5 Mon Sep 17 00:00:00 2001 From: Aleksander Nicacio da Silva Date: Thu, 21 Dec 2023 18:11:04 -0300 Subject: [PATCH 060/329] fix: toolbox submenu not being displayed in small resolutions (#31288) --- .changeset/green-turkeys-fry.md | 5 +++++ .../client/views/room/Header/RoomToolbox/RoomToolbox.tsx | 2 +- 2 files changed, 6 insertions(+), 1 deletion(-) create mode 100644 .changeset/green-turkeys-fry.md diff --git a/.changeset/green-turkeys-fry.md b/.changeset/green-turkeys-fry.md new file mode 100644 index 00000000000..64443653ba8 --- /dev/null +++ b/.changeset/green-turkeys-fry.md @@ -0,0 +1,5 @@ +--- +'@rocket.chat/meteor': patch +--- + +Fixed toolbox sub-menu not being displayed when in smaller resolutions diff --git a/apps/meteor/client/views/room/Header/RoomToolbox/RoomToolbox.tsx b/apps/meteor/client/views/room/Header/RoomToolbox/RoomToolbox.tsx index e17ecd855d8..00c9d9cdeec 100644 --- a/apps/meteor/client/views/room/Header/RoomToolbox/RoomToolbox.tsx +++ b/apps/meteor/client/views/room/Header/RoomToolbox/RoomToolbox.tsx @@ -91,7 +91,7 @@ const RoomToolbox = ({ className }: RoomToolboxProps) => { {featuredActions.map(mapToToolboxItem)} {featuredActions.length > 0 && } {visibleActions.map(mapToToolboxItem)} - {(normalActions.length > 6 || roomToolboxExpanded) && ( + {(normalActions.length > 6 || !roomToolboxExpanded) && ( )} -- GitLab From d70abcc64acfefd5efc5eb90a99d3afa7fc6cd53 Mon Sep 17 00:00:00 2001 From: Martin Schoeler Date: Fri, 22 Dec 2023 04:46:16 -0300 Subject: [PATCH 061/329] fix: Wrong "Open" translation in portuguese (#31294) --- .../client/views/omnichannel/currentChats/CurrentChatsPage.tsx | 2 +- .../client/views/omnichannel/currentChats/FilterByText.tsx | 2 +- apps/meteor/packages/rocketchat-i18n/i18n/en.i18n.json | 1 + apps/meteor/packages/rocketchat-i18n/i18n/pt-BR.i18n.json | 1 + 4 files changed, 4 insertions(+), 2 deletions(-) diff --git a/apps/meteor/client/views/omnichannel/currentChats/CurrentChatsPage.tsx b/apps/meteor/client/views/omnichannel/currentChats/CurrentChatsPage.tsx index 9453ecde066..e48908d4df9 100644 --- a/apps/meteor/client/views/omnichannel/currentChats/CurrentChatsPage.tsx +++ b/apps/meteor/client/views/omnichannel/currentChats/CurrentChatsPage.tsx @@ -172,7 +172,7 @@ const CurrentChatsPage = ({ id, onRowClick }: { id?: string; onRowClick: (_id: s const { _id, fname, servedBy, ts, lm, department, open, onHold, priorityWeight } = room; const getStatusText = (open: boolean, onHold: boolean): string => { if (!open) return t('Closed'); - return onHold ? t('On_Hold_Chats') : t('Open'); + return onHold ? t('On_Hold_Chats') : t('Room_Status_Open'); }; return ( diff --git a/apps/meteor/client/views/omnichannel/currentChats/FilterByText.tsx b/apps/meteor/client/views/omnichannel/currentChats/FilterByText.tsx index 41ba0bb6bf1..1830500c81b 100644 --- a/apps/meteor/client/views/omnichannel/currentChats/FilterByText.tsx +++ b/apps/meteor/client/views/omnichannel/currentChats/FilterByText.tsx @@ -28,7 +28,7 @@ const FilterByText: FilterByTextType = ({ setFilter, reload, customFields, setCu const statusOptions: [string, string][] = [ ['all', t('All')], ['closed', t('Closed')], - ['opened', t('Open')], + ['opened', t('Room_Status_Open')], ['onhold', t('On_Hold_Chats')], ]; diff --git a/apps/meteor/packages/rocketchat-i18n/i18n/en.i18n.json b/apps/meteor/packages/rocketchat-i18n/i18n/en.i18n.json index 72ee9842e0b..7a5f56a4981 100644 --- a/apps/meteor/packages/rocketchat-i18n/i18n/en.i18n.json +++ b/apps/meteor/packages/rocketchat-i18n/i18n/en.i18n.json @@ -4459,6 +4459,7 @@ "Room_password_changed_successfully": "Room password changed successfully", "room_removed_read_only": "Room added writing permission by {{user_by}}", "room_set_read_only": "Room set as Read Only by {{user_by}}", + "Room_Status_Open": "Open", "room_removed_read_only_permission": "removed read only permission", "room_set_read_only_permission": "set room to read only", "Room_topic_changed_successfully": "Room topic changed successfully", 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 24aeb2bbb6c..90ed0d6db38 100644 --- a/apps/meteor/packages/rocketchat-i18n/i18n/pt-BR.i18n.json +++ b/apps/meteor/packages/rocketchat-i18n/i18n/pt-BR.i18n.json @@ -3686,6 +3686,7 @@ "Room_password_changed_successfully": "A senha da sala foi alterada com sucesso", "room_removed_read_only": "Permissão de escrita adicionada à sala por {{user_by}}", "room_set_read_only": "Sala definida como somente leitura por {{user_by}}", + "Room_Status_Open": "Aberto", "Room_topic_changed_successfully": "Tópico da sala alterado com sucesso", "Room_type_changed_successfully": "Tipo de sala alterado com sucesso", "Room_type_of_default_rooms_cant_be_changed": "Esta é uma sala padrão e o tipo não pode ser alterado; consulte o seu administrador.", -- GitLab From 5ec231f0ae544b3bac45775ee04fc7f03a61bbea Mon Sep 17 00:00:00 2001 From: gabriellsh <40830821+gabriellsh@users.noreply.github.com> Date: Fri, 22 Dec 2023 11:26:41 -0300 Subject: [PATCH 062/329] refactor: SDK reconnection resume login attempt (#31301) --- ee/packages/ddp-client/src/DDPSDK.ts | 3 +++ ee/packages/ddp-client/src/types/Account.ts | 24 ++++++++++----------- 2 files changed, 14 insertions(+), 13 deletions(-) diff --git a/ee/packages/ddp-client/src/DDPSDK.ts b/ee/packages/ddp-client/src/DDPSDK.ts index 61e0dc6fe28..445ce90d7c7 100644 --- a/ee/packages/ddp-client/src/DDPSDK.ts +++ b/ee/packages/ddp-client/src/DDPSDK.ts @@ -113,6 +113,9 @@ export class DDPSDK implements SDK { const sdk = new DDPSDK(connection, stream, account, timeoutControl, rest); connection.on('connected', () => { + if (account.user?.token) { + account.loginWithToken(account.user.token); + } [...stream.subscriptions.entries()].forEach(([, sub]) => { ddp.subscribeWithId(sub.id, sub.name, sub.params); }); diff --git a/ee/packages/ddp-client/src/types/Account.ts b/ee/packages/ddp-client/src/types/Account.ts index 654ae2c2fb4..c8a51e40278 100644 --- a/ee/packages/ddp-client/src/types/Account.ts +++ b/ee/packages/ddp-client/src/types/Account.ts @@ -2,13 +2,20 @@ import { Emitter } from '@rocket.chat/emitter'; import type { ClientStream } from './ClientStream'; +type User = { + id: string; + username?: string; + token?: string; + tokenExpires?: Date; +} & Record; + export interface Account extends Emitter<{ uid: string | undefined; - user: Record | undefined; + user?: User; }> { uid?: string; - user?: Record; + user?: User; loginWithPassword(username: string, password: string): Promise; loginWithToken(token: string): Promise<{ id: string; @@ -21,12 +28,7 @@ export interface Account export class AccountImpl extends Emitter<{ uid: string | undefined; - user: { - id: string; - username?: string; - token?: string; - tokenExpires?: Date; - }; + user: User; }> implements Account { @@ -36,11 +38,6 @@ export class AccountImpl constructor(private readonly client: ClientStream) { super(); - this.client.on('connected', () => { - if (this.user?.token) { - this.loginWithToken(this.user.token); - } - }); client.onCollection('users', (data) => { if (data.collection !== 'users') { @@ -117,6 +114,7 @@ export class AccountImpl wait: true, }); this.uid = undefined; + this.user = undefined; this.emit('uid', this.uid); } } -- GitLab From b6b719856f22f554fac69af6b92152428941bfc0 Mon Sep 17 00:00:00 2001 From: Kevin Aleman Date: Fri, 22 Dec 2023 08:38:07 -0600 Subject: [PATCH 063/329] fix: Avoid creating a visitor when `token` is an empty string (#31287) --- .changeset/thin-socks-brush.md | 5 +++++ apps/meteor/app/livechat/server/api/v1/visitor.ts | 5 +++++ apps/meteor/tests/end-to-end/api/livechat/09-visitors.ts | 4 ++++ 3 files changed, 14 insertions(+) create mode 100644 .changeset/thin-socks-brush.md diff --git a/.changeset/thin-socks-brush.md b/.changeset/thin-socks-brush.md new file mode 100644 index 00000000000..a1da5c81e68 --- /dev/null +++ b/.changeset/thin-socks-brush.md @@ -0,0 +1,5 @@ +--- +"@rocket.chat/meteor": patch +--- + +Removed an old behavior that allowed visitors to be created with an empty token on `livechat/visitor` endpoint. diff --git a/apps/meteor/app/livechat/server/api/v1/visitor.ts b/apps/meteor/app/livechat/server/api/v1/visitor.ts index 59be77d298f..3d78280c510 100644 --- a/apps/meteor/app/livechat/server/api/v1/visitor.ts +++ b/apps/meteor/app/livechat/server/api/v1/visitor.ts @@ -30,6 +30,11 @@ API.v1.addRoute('livechat/visitor', { }); const { customFields, id, token, name, email, department, phone, username, connectionData } = this.bodyParams.visitor; + + if (!token?.trim()) { + throw new Meteor.Error('error-invalid-token', 'Token cannot be empty', { method: 'livechat/visitor' }); + } + const guest = { token, ...(id && { id }), diff --git a/apps/meteor/tests/end-to-end/api/livechat/09-visitors.ts b/apps/meteor/tests/end-to-end/api/livechat/09-visitors.ts index 6fa4206b6e5..372f7ddf5d7 100644 --- a/apps/meteor/tests/end-to-end/api/livechat/09-visitors.ts +++ b/apps/meteor/tests/end-to-end/api/livechat/09-visitors.ts @@ -44,6 +44,10 @@ describe('LIVECHAT - visitors', function () { const { body } = await request.post(api('livechat/visitor')).send({ visitor: {} }); expect(body).to.have.property('success', false); }); + it('should fail when token is an empty string', async () => { + const { body } = await request.post(api('livechat/visitor')).send({ visitor: { token: '' } }); + expect(body).to.have.property('success', false); + }); it('should create a visitor', async () => { const { body } = await request.post(api('livechat/visitor')).send({ visitor: { token: 'test' } }); expect(body).to.have.property('success', true); -- GitLab From d400f296e6a7876358acf0c3ab5b0c10965e6e7b Mon Sep 17 00:00:00 2001 From: gabriellsh <40830821+gabriellsh@users.noreply.github.com> Date: Fri, 22 Dec 2023 11:45:37 -0300 Subject: [PATCH 064/329] test: Add unit tests to SDK account (#31298) --- .../ddp-client/__tests__/Account.spec.ts | 83 +++++++++++++++++++ .../ddp-client/__tests__/Connection.spec.ts | 2 +- .../ddp-client/__tests__/DDPSDK.spec.ts | 45 +++++++++- .../ddp-client/__tests__/helpers/index.ts | 4 +- ee/packages/ddp-client/src/ClientStream.ts | 5 ++ 5 files changed, 133 insertions(+), 6 deletions(-) create mode 100644 ee/packages/ddp-client/__tests__/Account.spec.ts diff --git a/ee/packages/ddp-client/__tests__/Account.spec.ts b/ee/packages/ddp-client/__tests__/Account.spec.ts new file mode 100644 index 00000000000..948d041ebf2 --- /dev/null +++ b/ee/packages/ddp-client/__tests__/Account.spec.ts @@ -0,0 +1,83 @@ +import WS from 'jest-websocket-mock'; + +import { DDPSDK } from '../src/DDPSDK'; +import { handleConnection, handleMethod } from './helpers'; + +let server: WS; + +beforeEach(async () => { + server = new WS('ws://localhost:1234/websocket'); +}); + +afterEach(() => { + server.close(); + WS.clean(); +}); + +describe('login', () => { + it('should save credentials to user object - loginWithToken', async () => { + const sdk = DDPSDK.create('ws://localhost:1234'); + + await handleConnection(server, sdk.connection.connect()); + + const messageResult = { + id: 123, + token: 'token', + tokenExpires: { $date: 99999999 }, + }; + + await handleMethod(server, 'login', [{ resume: 'token' }], JSON.stringify(messageResult), sdk.account.loginWithToken('token')); + + const { user } = sdk.account; + expect(user?.token).toBe(messageResult.token); + expect((user?.tokenExpires as Date)?.toISOString()).toBe(new Date(messageResult.tokenExpires.$date).toISOString()); + expect(user?.id).toBe(messageResult.id); + }); + + it('should save credentials to user object - loginWithPassword', async () => { + const sdk = DDPSDK.create('ws://localhost:1234'); + + await handleConnection(server, sdk.connection.connect()); + + const messageResult = { + id: 123, + token: 'token', + tokenExpires: { $date: 99999999 }, + }; + + await handleMethod( + server, + 'login', + [{ user: { username: 'username' }, password: { digest: 'password', algorithm: 'sha-256' } }], + JSON.stringify(messageResult), + sdk.account.loginWithPassword('username', 'password'), + ); + + const { user } = sdk.account; + expect(user?.token).toBe(messageResult.token); + expect((user?.tokenExpires as Date)?.toISOString()).toBe(new Date(messageResult.tokenExpires.$date).toISOString()); + expect(user?.id).toBe(messageResult.id); + }); + + it('should logout', async () => { + const sdk = DDPSDK.create('ws://localhost:1234'); + + await handleConnection(server, sdk.connection.connect()); + + const messageResult = { + id: 123, + token: 'token', + tokenExpires: { $date: 99999999 }, + }; + + const cb = jest.fn(); + sdk.account.once('uid', cb); + + await handleMethod(server, 'logout', [], JSON.stringify(messageResult), sdk.account.logout()); + + expect(cb).toHaveBeenCalledTimes(1); + + const { user } = sdk.account; + expect(user).toBeUndefined(); + }); +}); diff --git a/ee/packages/ddp-client/__tests__/Connection.spec.ts b/ee/packages/ddp-client/__tests__/Connection.spec.ts index 78ac9a2d395..c535a6c58cc 100644 --- a/ee/packages/ddp-client/__tests__/Connection.spec.ts +++ b/ee/packages/ddp-client/__tests__/Connection.spec.ts @@ -136,7 +136,7 @@ it('should queue messages if the connection is not ready', async () => { expect(connection.queue.size).toBe(0); - await handleMethod(server, 'method', ['arg1', 'arg2']); + await handleMethod(server, 'method', ['arg1', 'arg2'], '1'); }); it('should throw an error if a reconnect is called while a connection is in progress', async () => { diff --git a/ee/packages/ddp-client/__tests__/DDPSDK.spec.ts b/ee/packages/ddp-client/__tests__/DDPSDK.spec.ts index f24a4500b66..cd5b30113de 100644 --- a/ee/packages/ddp-client/__tests__/DDPSDK.spec.ts +++ b/ee/packages/ddp-client/__tests__/DDPSDK.spec.ts @@ -205,13 +205,52 @@ it('should create and connect to a stream', async () => { sdk.connection.close(); }); +it.skip('should try to loginWithToken after reconnection', async () => { + const sdk = DDPSDK.create('ws://localhost:1234'); + + await handleConnection(server, sdk.connection.connect()); + + const messageResult = { + id: 123, + token: 'token1234', + tokenExpires: { $date: 99999999 }, + }; + + const { loginWithToken } = sdk.account; + const loginFn = jest.fn((token: string) => loginWithToken.apply(sdk.account, [token])); + sdk.account.loginWithToken = loginFn; + + await handleMethod(server, 'login', [{ resume: 'token' }], JSON.stringify(messageResult), sdk.account.loginWithToken('token')); + + expect(sdk.account.user?.token).toBe(messageResult.token); + expect(loginFn).toHaveBeenCalledTimes(1); + + // Fake timers are used to avoid waiting for the reconnect timeout + jest.useFakeTimers(); + + server.close(); + WS.clean(); + server = new WS('ws://localhost:1234/websocket'); + + const reconnect = new Promise((resolve) => sdk.connection.once('reconnecting', () => resolve(undefined))); + const connecting = new Promise((resolve) => sdk.connection.once('connecting', () => resolve(undefined))); + const connected = new Promise((resolve) => sdk.connection.once('connected', () => resolve(undefined))); + await handleConnection(server, jest.advanceTimersByTimeAsync(1000), reconnect, connecting, connected); + + jest.advanceTimersByTimeAsync(1000); + expect(loginFn).toHaveBeenCalledTimes(2); + + jest.useRealTimers(); + sdk.connection.close(); +}); + describe('Method call and Disconnection cases', () => { it('should handle properly if the message was sent after disconnection', async () => { const sdk = DDPSDK.create('ws://localhost:1234'); await handleConnection(server, sdk.connection.connect()); - const [result] = await handleMethod(server, 'method', ['args1'], sdk.client.callAsync('method', 'args1')); + const [result] = await handleMethod(server, 'method', ['args1'], '1', sdk.client.callAsync('method', 'args1')); expect(result).toBe(1); // Fake timers are used to avoid waiting for the reconnect timeout @@ -231,7 +270,7 @@ describe('Method call and Disconnection cases', () => { await handleConnection(server, jest.advanceTimersByTimeAsync(1000), reconnect, connecting, connected); - const [result2] = await handleMethod(server, 'method', ['args2'], callResult); + const [result2] = await handleMethod(server, 'method', ['args2'], '1', callResult); expect(util.inspect(callResult).includes('pending')).toBe(false); expect(result2).toBe(1); @@ -263,7 +302,7 @@ describe('Method call and Disconnection cases', () => { expect(util.inspect(callResult).includes('pending')).toBe(true); - const [result] = await handleMethod(server, 'method', ['args2'], callResult); + const [result] = await handleMethod(server, 'method', ['args2'], '1', callResult); expect(result).toBe(1); diff --git a/ee/packages/ddp-client/__tests__/helpers/index.ts b/ee/packages/ddp-client/__tests__/helpers/index.ts index f13545c7aee..dc5da3cd60c 100644 --- a/ee/packages/ddp-client/__tests__/helpers/index.ts +++ b/ee/packages/ddp-client/__tests__/helpers/index.ts @@ -35,9 +35,9 @@ const handleConnectionButNoResponse = async (server: WS, method: string, params: }); }; -export const handleMethod = async (server: WS, method: string, params: string[], ...client: Promise[]) => { +export const handleMethod = async (server: WS, method: string, params: any[], responseResult: string, ...client: Promise[]) => { const result = await handleConnectionButNoResponse(server, method, params); - return Promise.all([server.send(`{"msg":"result","id":"${result.id}","result":1}`), ...client]).then((result) => { + return Promise.all([server.send(`{"msg":"result","id":"${result.id}","result":${responseResult}}`), ...client]).then((result) => { result.shift(); return result; }); diff --git a/ee/packages/ddp-client/src/ClientStream.ts b/ee/packages/ddp-client/src/ClientStream.ts index 032496c256b..166e59a61f6 100644 --- a/ee/packages/ddp-client/src/ClientStream.ts +++ b/ee/packages/ddp-client/src/ClientStream.ts @@ -12,6 +12,11 @@ export class ClientStreamImpl extends Emitter implements ClientStream { subscriptions = new Map(); constructor(private ddp: DDPClient, readonly dispatcher: DDPDispatcher = new DDPDispatcher()) { + ddp.onConnection(({ msg }) => { + if (msg === 'connected') { + this.emit('connected'); + } + }); super(); } -- GitLab From 46fa2ff2cfded59538e982f14e5e3fdc9fe92fcc Mon Sep 17 00:00:00 2001 From: Tasso Evangelista Date: Fri, 22 Dec 2023 11:48:00 -0300 Subject: [PATCH 065/329] fix(otr): exceeding API calls when sending OTR messages (#31208) --- .changeset/popular-beds-heal.md | 7 + .../{sendMessage.js => sendMessage.ts} | 131 +++++++++--------- apps/meteor/app/otr/client/OTR.ts | 41 ++---- apps/meteor/app/otr/client/OTRRoom.ts | 95 ++++++++----- apps/meteor/app/otr/lib/IOTR.ts | 4 +- .../app/otr/server/methods/updateOTRAck.ts | 7 +- .../components/message/StatusIndicators.tsx | 4 +- .../hooks/roomActions/useOTRRoomAction.ts | 7 +- .../lib/parseMessageTextToAstMarkdown.ts | 3 +- .../client/lib/utils/getUidDirectMessage.ts | 8 +- apps/meteor/client/startup/otr.ts | 78 ++++++----- .../room/contextualBar/OTR/OTRWithData.tsx | 16 ++- .../server/hooks/messages/markdownParser.ts | 4 +- .../services/video-conference/service.ts | 7 +- ee/packages/ddp-client/src/types/streams.ts | 2 +- .../core-typings/src/IMessage/IMessage.ts | 14 +- 16 files changed, 228 insertions(+), 200 deletions(-) create mode 100644 .changeset/popular-beds-heal.md rename apps/meteor/app/lib/server/functions/{sendMessage.js => sendMessage.ts} (67%) diff --git a/.changeset/popular-beds-heal.md b/.changeset/popular-beds-heal.md new file mode 100644 index 00000000000..03f4dd735d8 --- /dev/null +++ b/.changeset/popular-beds-heal.md @@ -0,0 +1,7 @@ +--- +'@rocket.chat/ddp-client': patch +'@rocket.chat/core-typings': patch +'@rocket.chat/meteor': patch +--- + +Exceeding API calls when sending OTR messages diff --git a/apps/meteor/app/lib/server/functions/sendMessage.js b/apps/meteor/app/lib/server/functions/sendMessage.ts similarity index 67% rename from apps/meteor/app/lib/server/functions/sendMessage.js rename to apps/meteor/app/lib/server/functions/sendMessage.ts index 72247d4b187..fb19aad99c8 100644 --- a/apps/meteor/app/lib/server/functions/sendMessage.js +++ b/apps/meteor/app/lib/server/functions/sendMessage.ts @@ -1,4 +1,5 @@ import { Message } from '@rocket.chat/core-services'; +import type { IMessage, IRoom } from '@rocket.chat/core-typings'; import { Messages } from '@rocket.chat/models'; import { Match, check } from 'meteor/check'; @@ -12,6 +13,8 @@ import notifications from '../../../notifications/server/lib/Notifications'; import { settings } from '../../../settings/server'; import { parseUrlsInMessage } from './parseUrlsInMessage'; +// TODO: most of the types here are wrong, but I don't want to change them now + /** * IMPORTANT * @@ -49,13 +52,13 @@ const validPartialURLParam = Match.Where((value) => { return true; }); -const objectMaybeIncluding = (types) => - Match.Where((value) => { +const objectMaybeIncluding = (types: any) => + Match.Where((value: any) => { Object.keys(types).forEach((field) => { if (value[field] != null) { try { check(value[field], types[field]); - } catch (error) { + } catch (error: any) { error.path = field; throw error; } @@ -65,7 +68,7 @@ const objectMaybeIncluding = (types) => return true; }); -const validateAttachmentsFields = (attachmentField) => { +const validateAttachmentsFields = (attachmentField: any) => { check( attachmentField, objectMaybeIncluding({ @@ -80,7 +83,7 @@ const validateAttachmentsFields = (attachmentField) => { } }; -const validateAttachmentsActions = (attachmentActions) => { +const validateAttachmentsActions = (attachmentActions: any) => { check( attachmentActions, objectMaybeIncluding({ @@ -96,7 +99,7 @@ const validateAttachmentsActions = (attachmentActions) => { ); }; -const validateAttachment = (attachment) => { +const validateAttachment = (attachment: any) => { check( attachment, objectMaybeIncluding({ @@ -138,9 +141,9 @@ const validateAttachment = (attachment) => { } }; -const validateBodyAttachments = (attachments) => attachments.map(validateAttachment); +const validateBodyAttachments = (attachments: any[]) => attachments.map(validateAttachment); -export const validateMessage = async (message, room, user) => { +export const validateMessage = async (message: any, room: any, user: any) => { check( message, objectMaybeIncluding({ @@ -170,7 +173,11 @@ export const validateMessage = async (message, room, user) => { } }; -export const prepareMessageObject = function (message, rid, user) { +export function prepareMessageObject( + message: Partial, + rid: IRoom['_id'], + user: { _id: string; username?: string; name?: string }, +): asserts message is IMessage { if (!message.ts) { message.ts = new Date(); } @@ -182,7 +189,7 @@ export const prepareMessageObject = function (message, rid, user) { const { _id, username, name } = user; message.u = { _id, - username, + username: username as string, // FIXME: this is wrong but I don't want to change it now name, }; message.rid = rid; @@ -194,26 +201,12 @@ export const prepareMessageObject = function (message, rid, user) { if (message.ts == null) { message.ts = new Date(); } -}; - -/** - * Clean up the message object before saving on db - * @param {IMessage} message - */ -function cleanupMessageObject(message) { - ['customClass'].forEach((field) => delete message[field]); } /** * Validates and sends the message object. - * @param {IUser} user - * @param {AtLeast} message - * @param {IRoom} room - * @param {boolean} [upsert=false] - * @param {string[]} [previewUrls] - * @returns {Promise} */ -export const sendMessage = async function (user, message, room, upsert = false, previewUrls = undefined) { +export const sendMessage = async function (user: any, message: any, room: any, upsert = false, previewUrls?: string[]) { if (!user || !message || !room._id) { return false; } @@ -221,20 +214,28 @@ export const sendMessage = async function (user, message, room, upsert = false, await validateMessage(message, room, user); prepareMessageObject(message, room._id, user); + if (message.t === 'otr') { + notifications.streamRoomMessage.emit(message.rid, message, user, room); + return message; + } + if (settings.get('Message_Read_Receipt_Enabled')) { message.unread = true; } // For the Rocket.Chat Apps :) - if (Apps && Apps.isLoaded()) { - const prevent = await Apps.getBridges()?.getListenerBridge().messageEvent('IPreMessageSentPrevent', message); + if (Apps?.isLoaded()) { + const listenerBridge = Apps.getBridges()?.getListenerBridge(); + + const prevent = await listenerBridge?.messageEvent('IPreMessageSentPrevent', message); if (prevent) { return; } - let result; - result = await Apps.getBridges()?.getListenerBridge().messageEvent('IPreMessageSentExtend', message); - result = await Apps.getBridges()?.getListenerBridge().messageEvent('IPreMessageSentModify', result); + const result = await listenerBridge?.messageEvent( + 'IPreMessageSentModify', + await listenerBridge?.messageEvent('IPreMessageSentExtend', message), + ); if (typeof result === 'object') { message = Object.assign(message, result); @@ -244,50 +245,46 @@ export const sendMessage = async function (user, message, room, upsert = false, } } - cleanupMessageObject(message); - parseUrlsInMessage(message, previewUrls); message = await Message.beforeSave({ message, room, user }); message = await callbacks.run('beforeSaveMessage', message, room); - if (message) { - if (message.t === 'otr') { - const otrStreamer = notifications.streamRoomMessage; - otrStreamer.emit(message.rid, message, user, room); - } else if (message._id && upsert) { - const { _id } = message; - delete message._id; - await Messages.updateOne( - { - _id, - 'u._id': message.u._id, - }, - { $set: message }, - { upsert: true }, - ); - message._id = _id; - } else { - const messageAlreadyExists = message._id && (await Messages.findOneById(message._id, { projection: { _id: 1 } })); - if (messageAlreadyExists) { - return; - } - const result = await Messages.insertOne(message); - message._id = result.insertedId; - } - if (Apps && Apps.isLoaded()) { - // This returns a promise, but it won't mutate anything about the message - // so, we don't really care if it is successful or fails - void Apps.getBridges()?.getListenerBridge().messageEvent('IPostMessageSent', message); - } + if (!message) { + return; + } - /* - Defer other updates as their return is not interesting to the user - */ + if (message._id && upsert) { + const { _id } = message; + delete message._id; + await Messages.updateOne( + { + _id, + 'u._id': message.u._id, + }, + { $set: message }, + { upsert: true }, + ); + message._id = _id; + } else { + const messageAlreadyExists = message._id && (await Messages.findOneById(message._id, { projection: { _id: 1 } })); + if (messageAlreadyExists) { + return; + } + const { insertedId } = await Messages.insertOne(message); + message._id = insertedId; + } - // Execute all callbacks - await callbacks.run('afterSaveMessage', message, room); - return message; + if (Apps?.isLoaded()) { + // This returns a promise, but it won't mutate anything about the message + // so, we don't really care if it is successful or fails + void Apps.getBridges()?.getListenerBridge().messageEvent('IPostMessageSent', message); } + + /* Defer other updates as their return is not interesting to the user */ + + // Execute all callbacks + await callbacks.run('afterSaveMessage', message, room); + return message; }; diff --git a/apps/meteor/app/otr/client/OTR.ts b/apps/meteor/app/otr/client/OTR.ts index 55e0a1289a6..a855381bd9c 100644 --- a/apps/meteor/app/otr/client/OTR.ts +++ b/apps/meteor/app/otr/client/OTR.ts @@ -1,51 +1,28 @@ -import { Meteor } from 'meteor/meteor'; -import { ReactiveVar } from 'meteor/reactive-var'; +import type { IRoom, IUser } from '@rocket.chat/core-typings'; -import { Subscriptions } from '../../models/client'; import type { IOTR } from '../lib/IOTR'; import { OTRRoom } from './OTRRoom'; class OTR implements IOTR { - private enabled: ReactiveVar; - private instancesByRoomId: { [rid: string]: OTRRoom }; constructor() { - this.enabled = new ReactiveVar(false); this.instancesByRoomId = {}; } - isEnabled(): boolean { - return this.enabled.get(); - } - - setEnabled(enabled: boolean): void { - this.enabled.set(enabled); - } - - getInstanceByRoomId(roomId: string): OTRRoom | undefined { - const userId = Meteor.userId(); - if (!userId) { - return; - } - if (!this.enabled.get()) { - return; - } - - if (this.instancesByRoomId[roomId]) { - return this.instancesByRoomId[roomId]; + getInstanceByRoomId(uid: IUser['_id'], rid: IRoom['_id']): OTRRoom | undefined { + if (this.instancesByRoomId[rid]) { + return this.instancesByRoomId[rid]; } - const subscription = Subscriptions.findOne({ - rid: roomId, - }); + const otrRoom = OTRRoom.create(uid, rid); - if (!subscription || subscription.t !== 'd') { - return; + if (!otrRoom) { + return undefined; } - this.instancesByRoomId[roomId] = new OTRRoom(userId, roomId); - return this.instancesByRoomId[roomId]; + this.instancesByRoomId[rid] = otrRoom; + return this.instancesByRoomId[rid]; } } diff --git a/apps/meteor/app/otr/client/OTRRoom.ts b/apps/meteor/app/otr/client/OTRRoom.ts index d0d1ae84cd3..e0fc3c463de 100644 --- a/apps/meteor/app/otr/client/OTRRoom.ts +++ b/apps/meteor/app/otr/client/OTRRoom.ts @@ -1,4 +1,4 @@ -import type { IMessage } from '@rocket.chat/core-typings'; +import type { IRoom, IMessage, IUser } from '@rocket.chat/core-typings'; import { Random } from '@rocket.chat/random'; import EJSON from 'ejson'; import { Meteor } from 'meteor/meteor'; @@ -11,7 +11,6 @@ import { Presence } from '../../../client/lib/presence'; import { dispatchToastMessage } from '../../../client/lib/toast'; import { getUidDirectMessage } from '../../../client/lib/utils/getUidDirectMessage'; import { goToRoomById } from '../../../client/lib/utils/goToRoomById'; -import { Notifications } from '../../notifications/client'; import { sdk } from '../../utils/client/lib/SDKClient'; import { t } from '../../utils/lib/i18n'; import type { IOnUserStreamData, IOTRAlgorithm, IOTRDecrypt, IOTRRoom } from '../lib/IOTR'; @@ -48,15 +47,25 @@ export class OTRRoom implements IOTRRoom { private isFirstOTR: boolean; - constructor(userId: string, roomId: string) { - this._userId = userId; - this._roomId = roomId; + protected constructor(uid: IUser['_id'], rid: IRoom['_id'], peerId: IUser['_id']) { + this._userId = uid; + this._roomId = rid; this._keyPair = null; this._sessionKey = null; - this.peerId = getUidDirectMessage(roomId) as string; + this.peerId = peerId; this.isFirstOTR = true; } + public static create(uid: IUser['_id'], rid: IRoom['_id']): OTRRoom | undefined { + const peerId = getUidDirectMessage(rid); + + if (!peerId) { + return undefined; + } + + return new OTRRoom(uid, rid, peerId); + } + getPeerId(): string { return this.peerId; } @@ -75,58 +84,68 @@ export class OTRRoom implements IOTRRoom { async handshake(refresh?: boolean): Promise { this.setState(OtrRoomState.ESTABLISHING); - try { - await this.generateKeyPair(); - this.peerId && - Notifications.notifyUser(this.peerId, 'otr', 'handshake', { - roomId: this._roomId, - userId: this._userId, - publicKey: EJSON.stringify(this._exportedPublicKey), - refresh, - }); - if (refresh) { - const user = Meteor.user(); - if (!user) { - return; - } - await sdk.call('sendSystemMessages', this._roomId, user.username, otrSystemMessages.USER_REQUESTED_OTR_KEY_REFRESH); - this.isFirstOTR = false; + + await this.generateKeyPair(); + sdk.publish('notify-user', [ + `${this.peerId}/otr`, + 'handshake', + { + roomId: this._roomId, + userId: this._userId, + publicKey: EJSON.stringify(this._exportedPublicKey), + refresh, + }, + ]); + + if (refresh) { + const user = Meteor.user(); + if (!user) { + return; } - } catch (e) { - throw e; + await sdk.call('sendSystemMessages', this._roomId, user.username, otrSystemMessages.USER_REQUESTED_OTR_KEY_REFRESH); + this.isFirstOTR = false; } } acknowledge(): void { void sdk.rest.post('/v1/statistics.telemetry', { params: [{ eventName: 'otrStats', timestamp: Date.now(), rid: this._roomId }] }); - this.peerId && - Notifications.notifyUser(this.peerId, 'otr', 'acknowledge', { + sdk.publish('notify-user', [ + `${this.peerId}/otr`, + 'acknowledge', + { roomId: this._roomId, userId: this._userId, publicKey: EJSON.stringify(this._exportedPublicKey), - }); + }, + ]); } deny(): void { this.reset(); this.setState(OtrRoomState.DECLINED); - this.peerId && - Notifications.notifyUser(this.peerId, 'otr', 'deny', { + sdk.publish('notify-user', [ + `${this.peerId}/otr`, + 'deny', + { roomId: this._roomId, userId: this._userId, - }); + }, + ]); } end(): void { this.isFirstOTR = true; this.reset(); this.setState(OtrRoomState.NOT_STARTED); - this.peerId && - Notifications.notifyUser(this.peerId, 'otr', 'end', { + sdk.publish('notify-user', [ + `${this.peerId}/otr`, + 'end', + { roomId: this._roomId, userId: this._userId, - }); + }, + ]); } reset(): void { @@ -142,14 +161,14 @@ export class OTRRoom implements IOTRRoom { } this._userOnlineComputation = Tracker.autorun(() => { - const $room = $(`#chat-window-${this._roomId}`); - const $title = $('.rc-header__title', $room); + const $room = document.querySelector(`#chat-window-${this._roomId}`); + const $title = $room?.querySelector('.rc-header__title'); if (this.getState() === OtrRoomState.ESTABLISHED) { - if ($room.length && $title.length && !$('.otr-icon', $title).length) { + if ($room && $title && !$title.querySelector('.otr-icon')) { $title.prepend(""); } - } else if ($title.length) { - $('.otr-icon', $title).remove(); + } else if ($title) { + $title.querySelector('.otr-icon')?.remove(); } }); try { diff --git a/apps/meteor/app/otr/lib/IOTR.ts b/apps/meteor/app/otr/lib/IOTR.ts index 051752ab947..9e7cd4ca6b8 100644 --- a/apps/meteor/app/otr/lib/IOTR.ts +++ b/apps/meteor/app/otr/lib/IOTR.ts @@ -36,9 +36,7 @@ export interface IOTRRoom { } export interface IOTR { - isEnabled(): boolean; - setEnabled(enabled: boolean): void; - getInstanceByRoomId(roomId: IRoom['_id']): OTRRoom | undefined; + getInstanceByRoomId(userId: IUser['_id'], roomId: IRoom['_id']): OTRRoom | undefined; } export interface IOTRAlgorithm extends EcKeyAlgorithm, EcdhKeyDeriveParams {} diff --git a/apps/meteor/app/otr/server/methods/updateOTRAck.ts b/apps/meteor/app/otr/server/methods/updateOTRAck.ts index eaba2906ed0..745c70aa453 100644 --- a/apps/meteor/app/otr/server/methods/updateOTRAck.ts +++ b/apps/meteor/app/otr/server/methods/updateOTRAck.ts @@ -1,3 +1,4 @@ +import type { IOTRMessage } from '@rocket.chat/core-typings'; import type { ServerMethods } from '@rocket.chat/ui-contexts'; import { Meteor } from 'meteor/meteor'; @@ -6,7 +7,7 @@ import notifications from '../../../notifications/server/lib/Notifications'; declare module '@rocket.chat/ui-contexts' { // eslint-disable-next-line @typescript-eslint/naming-convention interface ServerMethods { - updateOTRAck({ message, ack }: { message: any; ack: any }): void; + updateOTRAck({ message, ack }: { message: IOTRMessage; ack: string }): void; } } @@ -15,7 +16,7 @@ Meteor.methods({ if (!Meteor.userId()) { throw new Meteor.Error('error-invalid-user', 'Invalid user', { method: 'updateOTRAck' }); } - const otrStreamer = notifications.streamRoomMessage; - otrStreamer.emit(message.rid, { ...message, otr: { ack } }); + const acknowlegedMessage: IOTRMessage = { ...message, otrAck: ack }; + notifications.streamRoomMessage.emit(message.rid, acknowlegedMessage); }, }); diff --git a/apps/meteor/client/components/message/StatusIndicators.tsx b/apps/meteor/client/components/message/StatusIndicators.tsx index 48ec47e5e28..f47d25e7c7b 100644 --- a/apps/meteor/client/components/message/StatusIndicators.tsx +++ b/apps/meteor/client/components/message/StatusIndicators.tsx @@ -1,5 +1,5 @@ import type { IMessage, ITranslatedMessage } from '@rocket.chat/core-typings'; -import { isEditedMessage, isE2EEMessage, isOTRMessage } from '@rocket.chat/core-typings'; +import { isEditedMessage, isE2EEMessage, isOTRMessage, isOTRAckMessage } from '@rocket.chat/core-typings'; import { MessageStatusIndicator, MessageStatusIndicatorItem } from '@rocket.chat/fuselage'; import { useUserId, useTranslation } from '@rocket.chat/ui-contexts'; import type { ReactElement } from 'react'; @@ -18,7 +18,7 @@ const StatusIndicators = ({ message }: StatusIndicatorsProps): ReactElement => { const following = useShowFollowing({ message }); const isEncryptedMessage = isE2EEMessage(message); - const isOtrMessage = isOTRMessage(message); + const isOtrMessage = isOTRMessage(message) || isOTRAckMessage(message); const uid = useUserId(); diff --git a/apps/meteor/client/hooks/roomActions/useOTRRoomAction.ts b/apps/meteor/client/hooks/roomActions/useOTRRoomAction.ts index b199631aa7d..1a50283c747 100644 --- a/apps/meteor/client/hooks/roomActions/useOTRRoomAction.ts +++ b/apps/meteor/client/hooks/roomActions/useOTRRoomAction.ts @@ -1,9 +1,8 @@ import { isRoomFederated } from '@rocket.chat/core-typings'; import { useSetting } from '@rocket.chat/ui-contexts'; -import { lazy, useEffect, useMemo } from 'react'; +import { lazy, useMemo } from 'react'; import { useTranslation } from 'react-i18next'; -import otr from '../../../app/otr/client/OTR'; import { useRoom } from '../../views/room/contexts/RoomContext'; import type { RoomToolboxActionConfig } from '../../views/room/contexts/RoomToolboxContext'; @@ -16,10 +15,6 @@ export const useOTRRoomAction = () => { const capable = !!global.crypto; const { t } = useTranslation(); - useEffect(() => { - otr.setEnabled(enabled && capable); - }, [enabled, capable]); - return useMemo((): RoomToolboxActionConfig | undefined => { if (!enabled || !capable) { return undefined; diff --git a/apps/meteor/client/lib/parseMessageTextToAstMarkdown.ts b/apps/meteor/client/lib/parseMessageTextToAstMarkdown.ts index 15a88c847fc..41cbfed9575 100644 --- a/apps/meteor/client/lib/parseMessageTextToAstMarkdown.ts +++ b/apps/meteor/client/lib/parseMessageTextToAstMarkdown.ts @@ -3,6 +3,7 @@ import { isFileAttachment, isE2EEMessage, isOTRMessage, + isOTRAckMessage, isQuoteAttachment, isTranslatedAttachment, isTranslatedMessage, @@ -46,7 +47,7 @@ export const parseMessageTextToAstMarkdown = < return { ...msg, md: - isE2EEMessage(message) || isOTRMessage(message) || translated + isE2EEMessage(message) || isOTRMessage(message) || isOTRAckMessage(message) || translated ? textToMessageToken(text, parseOptions) : msg.md ?? textToMessageToken(text, parseOptions), ...(msg.attachments && { diff --git a/apps/meteor/client/lib/utils/getUidDirectMessage.ts b/apps/meteor/client/lib/utils/getUidDirectMessage.ts index cb5ef83c825..761b849f788 100644 --- a/apps/meteor/client/lib/utils/getUidDirectMessage.ts +++ b/apps/meteor/client/lib/utils/getUidDirectMessage.ts @@ -3,12 +3,12 @@ import { Meteor } from 'meteor/meteor'; import { ChatRoom } from '../../../app/models/client'; -export const getUidDirectMessage = (rid: IRoom['_id'], userId: IUser['_id'] | null = Meteor.userId()): string | undefined => { - const room = ChatRoom.findOne({ _id: rid }, { fields: { uids: 1 } }); +export const getUidDirectMessage = (rid: IRoom['_id'], uid: IUser['_id'] | null = Meteor.userId()): string | undefined => { + const room = ChatRoom.findOne({ _id: rid }, { fields: { t: 1, uids: 1 } }); - if (!room?.uids || room.uids.length > 2) { + if (!room || room.t !== 'd' || !room.uids || room.uids.length > 2) { return undefined; } - return room.uids.filter((uid) => uid !== userId)[0]; + return room.uids.filter((_uid) => _uid !== uid)[0]; }; diff --git a/apps/meteor/client/startup/otr.ts b/apps/meteor/client/startup/otr.ts index d0603a8fdfd..084f4311c49 100644 --- a/apps/meteor/client/startup/otr.ts +++ b/apps/meteor/client/startup/otr.ts @@ -1,8 +1,7 @@ -import type { IMessage, AtLeast } from '@rocket.chat/core-typings'; +import { isOTRMessage } from '@rocket.chat/core-typings'; import { Meteor } from 'meteor/meteor'; import { Tracker } from 'meteor/tracker'; -import { Notifications } from '../../app/notifications/client'; import OTR from '../../app/otr/client/OTR'; import { OtrRoomState } from '../../app/otr/lib/OtrRoomState'; import { sdk } from '../../app/utils/client/lib/SDKClient'; @@ -12,45 +11,57 @@ import { onClientMessageReceived } from '../lib/onClientMessageReceived'; Meteor.startup(() => { Tracker.autorun(() => { - if (Meteor.userId()) { - Notifications.onUser('otr', (type, data) => { - if (!data.roomId || !data.userId || data.userId === Meteor.userId()) { - return; - } - const instanceByRoomId = OTR.getInstanceByRoomId(data.roomId); - - if (!instanceByRoomId) { - return; - } + const uid = Meteor.userId(); - instanceByRoomId.onUserStream(type, data); - }); + if (!uid) { + return; } + + sdk.stream('notify-user', [`${uid}/otr`], (type, data) => { + if (!data.roomId || !data.userId || data.userId === uid) { + return; + } + + const otrRoom = OTR.getInstanceByRoomId(uid, data.roomId); + otrRoom?.onUserStream(type, data); + }); }); - onClientBeforeSendMessage.use(async (message: AtLeast) => { - const instanceByRoomId = OTR.getInstanceByRoomId(message.rid); + onClientBeforeSendMessage.use(async (message) => { + const uid = Meteor.userId(); + + if (!uid) { + return message; + } - if (message.rid && instanceByRoomId && instanceByRoomId.getState() === OtrRoomState.ESTABLISHED) { - const msg = await instanceByRoomId.encrypt(message); + const otrRoom = OTR.getInstanceByRoomId(uid, message.rid); + + if (otrRoom && otrRoom.getState() === OtrRoomState.ESTABLISHED) { + const msg = await otrRoom.encrypt(message); return { ...message, msg, t: 'otr' }; } return message; }); - onClientMessageReceived.use(async (message: IMessage & { notification?: boolean }) => { - const instanceByRoomId = OTR.getInstanceByRoomId(message.rid); + onClientMessageReceived.use(async (message) => { + const uid = Meteor.userId(); - if (message.rid && instanceByRoomId && instanceByRoomId.getState() === OtrRoomState.ESTABLISHED) { - if (message?.notification) { - message.msg = t('Encrypted_message'); - return message; - } - if (message.t !== 'otr') { - return message; - } + if (!uid) { + return message; + } - const decrypted = await instanceByRoomId.decrypt(message.msg); + if (!isOTRMessage(message)) { + return message; + } + + if ('notification' in message) { + return { ...message, msg: t('Encrypted_message') }; + } + + const otrRoom = OTR.getInstanceByRoomId(uid, message.rid); + + if (otrRoom && otrRoom.getState() === OtrRoomState.ESTABLISHED) { + const decrypted = await otrRoom.decrypt(message.msg); if (typeof decrypted === 'string') { return { ...message, msg: decrypted }; } @@ -59,13 +70,16 @@ Meteor.startup(() => { if (ts) message.ts = ts; if (message.otrAck) { - const otrAck = await instanceByRoomId.decrypt(message.otrAck); + const otrAck = await otrRoom.decrypt(message.otrAck); if (typeof otrAck === 'string') { return { ...message, msg: otrAck }; } - if (ack === otrAck.text) message.t = 'otr-ack'; + + if (ack === otrAck.text) { + return { ...message, _id, t: 'otr-ack', msg }; + } } else if (userId !== Meteor.userId()) { - const encryptedAck = await instanceByRoomId.encryptText(ack); + const encryptedAck = await otrRoom.encryptText(ack); void sdk.call('updateOTRAck', { message, ack: encryptedAck }); } diff --git a/apps/meteor/client/views/room/contextualBar/OTR/OTRWithData.tsx b/apps/meteor/client/views/room/contextualBar/OTR/OTRWithData.tsx index 91257c31173..a4c642bdea6 100644 --- a/apps/meteor/client/views/room/contextualBar/OTR/OTRWithData.tsx +++ b/apps/meteor/client/views/room/contextualBar/OTR/OTRWithData.tsx @@ -1,18 +1,26 @@ +import { useUserId } from '@rocket.chat/ui-contexts'; import type { ReactElement } from 'react'; import React, { useCallback, useEffect, useMemo } from 'react'; -import ORTInstance from '../../../../../app/otr/client/OTR'; +import OTR from '../../../../../app/otr/client/OTR'; import { OtrRoomState } from '../../../../../app/otr/lib/OtrRoomState'; import { usePresence } from '../../../../hooks/usePresence'; import { useReactiveValue } from '../../../../hooks/useReactiveValue'; import { useRoom } from '../../contexts/RoomContext'; import { useRoomToolbox } from '../../contexts/RoomToolboxContext'; -import OTR from './OTR'; +import OTRComponent from './OTR'; const OTRWithData = (): ReactElement => { + const uid = useUserId(); const room = useRoom(); const { closeTab } = useRoomToolbox(); - const otr = useMemo(() => ORTInstance.getInstanceByRoomId(room._id), [room._id]); + const otr = useMemo(() => { + if (!uid) { + return; + } + + return OTR.getInstanceByRoomId(uid, room._id); + }, [uid, room._id]); const otrState = useReactiveValue(useCallback(() => (otr ? otr.getState() : OtrRoomState.ERROR), [otr])); const peerUserPresence = usePresence(otr?.getPeerId()); const userStatus = peerUserPresence?.status; @@ -47,7 +55,7 @@ const OTRWithData = (): ReactElement => { }, [otr, otrState]); return ( - { - if (isE2EEMessage(message) || isOTRMessage(message)) { + if (isE2EEMessage(message) || isOTRMessage(message) || isOTRAckMessage(message)) { return message; } try { diff --git a/apps/meteor/server/services/video-conference/service.ts b/apps/meteor/server/services/video-conference/service.ts index 1240e9ec727..dd58d4b293f 100644 --- a/apps/meteor/server/services/video-conference/service.ts +++ b/apps/meteor/server/services/video-conference/service.ts @@ -495,13 +495,18 @@ export class VideoConfService extends ServiceClassInternal implements IVideoConf msg: '', groupable: false, blocks: customBlocks || [this.buildVideoConfBlock(call._id)], - }; + } satisfies Partial; const room = await Rooms.findOneById(call.rid); const appId = videoConfProviders.getProviderAppId(call.providerName); const user = createdBy || (appId && (await Users.findOneByAppId(appId))) || (await Users.findOneById('rocket.cat')); const message = await sendMessage(user, record, room, false); + + if (!message) { + throw new Error('failed-to-create-message'); + } + return message._id; } diff --git a/ee/packages/ddp-client/src/types/streams.ts b/ee/packages/ddp-client/src/types/streams.ts index d8d21bf519b..9e7a4e6a004 100644 --- a/ee/packages/ddp-client/src/types/streams.ts +++ b/ee/packages/ddp-client/src/types/streams.ts @@ -56,7 +56,7 @@ export interface StreamerEvents { // { key: `${string}/${string}`; args: [id: string] }, ]; - 'room-messages': [{ key: '__my_messages__'; args: [IMessage] }, { key: string; args: [IMessage] }]; + 'room-messages': [{ key: '__my_messages__'; args: [IMessage] }, { key: string; args: [message: IMessage, user?: IUser, room?: IRoom] }]; 'notify-all': [ { diff --git a/packages/core-typings/src/IMessage/IMessage.ts b/packages/core-typings/src/IMessage/IMessage.ts index 3ed5e470585..5e7218bcd42 100644 --- a/packages/core-typings/src/IMessage/IMessage.ts +++ b/packages/core-typings/src/IMessage/IMessage.ts @@ -354,16 +354,22 @@ export type IE2EEMessage = IMessage & { e2e: 'pending' | 'done'; }; -export type IOTRMessage = IMessage & { - t: 'otr' | 'otr-ack'; -}; +export interface IOTRMessage extends IMessage { + t: 'otr'; + otrAck?: string; +} + +export interface IOTRAckMessage extends IMessage { + t: 'otr-ack'; +} export type IVideoConfMessage = IMessage & { t: 'videoconf'; }; export const isE2EEMessage = (message: IMessage): message is IE2EEMessage => message.t === 'e2e'; -export const isOTRMessage = (message: IMessage): message is IOTRMessage => message.t === 'otr' || message.t === 'otr-ack'; +export const isOTRMessage = (message: IMessage): message is IOTRMessage => message.t === 'otr'; +export const isOTRAckMessage = (message: IMessage): message is IOTRAckMessage => message.t === 'otr-ack'; export const isVideoConfMessage = (message: IMessage): message is IVideoConfMessage => message.t === 'videoconf'; export type IMessageWithPendingFileImport = IMessage & { -- GitLab From 974812770e53f561ed370a88cecae2a82aa9c564 Mon Sep 17 00:00:00 2001 From: gabriellsh <40830821+gabriellsh@users.noreply.github.com> Date: Mon, 18 Dec 2023 09:54:42 -0300 Subject: [PATCH 066/329] fix: Sending messages triggering stream unsub/sub (#31254) --- .../room/body/hooks/useGoToHomeOnRemoved.ts | 19 ++++++++----------- 1 file changed, 8 insertions(+), 11 deletions(-) diff --git a/apps/meteor/client/views/room/body/hooks/useGoToHomeOnRemoved.ts b/apps/meteor/client/views/room/body/hooks/useGoToHomeOnRemoved.ts index 068c97a2de4..9cb94e8c93f 100644 --- a/apps/meteor/client/views/room/body/hooks/useGoToHomeOnRemoved.ts +++ b/apps/meteor/client/views/room/body/hooks/useGoToHomeOnRemoved.ts @@ -1,12 +1,12 @@ import { isOmnichannelRoom, type IRoom } from '@rocket.chat/core-typings'; -import { useRoute, useStream, useToastMessageDispatch, useTranslation } from '@rocket.chat/ui-contexts'; +import { useRouter, useStream, useToastMessageDispatch, useTranslation } from '@rocket.chat/ui-contexts'; import { useQueryClient } from '@tanstack/react-query'; import { useEffect } from 'react'; import { useOmnichannelCloseRoute } from '../../../../hooks/omnichannel/useOmnichannelCloseRoute'; -export function useGoToHomeOnRemoved(room: IRoom, userId: string | undefined): void { - const homeRouter = useRoute('home'); +export function useGoToHomeOnRemoved(room: IRoom, userId?: string): void { + const router = useRouter(); const queryClient = useQueryClient(); const dispatchToastMessage = useToastMessageDispatch(); const subscribeToNotifyUser = useStream('notify-user'); @@ -18,11 +18,11 @@ export function useGoToHomeOnRemoved(room: IRoom, userId: string | undefined): v return; } - const unSubscribeFromNotifyUser = subscribeToNotifyUser(`${userId}/subscriptions-changed`, (event, subscription) => { + return subscribeToNotifyUser(`${userId}/subscriptions-changed`, (event, subscription) => { if (event === 'removed' && subscription.rid === room._id) { queryClient.invalidateQueries(['rooms', room._id]); - if (isOmnichannelRoom(room)) { + if (isOmnichannelRoom({ t: room.t })) { navigateHome(); return; } @@ -34,23 +34,20 @@ export function useGoToHomeOnRemoved(room: IRoom, userId: string | undefined): v }), }); - homeRouter.push({}); + router.navigate('/home'); } }); - - return unSubscribeFromNotifyUser; }, [ userId, - homeRouter, + router, subscribeToNotifyUser, room._id, + room.t, room?.fname, room?.name, t, dispatchToastMessage, queryClient, - room.t, - room, navigateHome, ]); } -- GitLab From 4b2bdd550846f5ab71145abcfb11431c9725539b Mon Sep 17 00:00:00 2001 From: Pierre Lehnen <55164754+pierre-lehnen-rc@users.noreply.github.com> Date: Wed, 13 Dec 2023 16:14:07 -0300 Subject: [PATCH 067/329] fix: file type filter not working on room's file list (#31228) --- .changeset/violet-pears-cry.md | 5 +++++ apps/meteor/server/models/raw/Uploads.ts | 2 +- 2 files changed, 6 insertions(+), 1 deletion(-) create mode 100644 .changeset/violet-pears-cry.md diff --git a/.changeset/violet-pears-cry.md b/.changeset/violet-pears-cry.md new file mode 100644 index 00000000000..8e10ad50dd5 --- /dev/null +++ b/.changeset/violet-pears-cry.md @@ -0,0 +1,5 @@ +--- +'@rocket.chat/meteor': patch +--- + +Fixed the filter for file type in the list of room files diff --git a/apps/meteor/server/models/raw/Uploads.ts b/apps/meteor/server/models/raw/Uploads.ts index 1c02fffab2a..8bbbbef85ba 100644 --- a/apps/meteor/server/models/raw/Uploads.ts +++ b/apps/meteor/server/models/raw/Uploads.ts @@ -50,9 +50,9 @@ export class UploadsRaw extends BaseUploadModelRaw implements IUploadsModel { findPaginatedWithoutThumbs(query: Filter = {}, options?: FindOptions): FindPaginated>> { return this.findPaginated( { + typeGroup: { $ne: 'thumb' }, ...query, _hidden: { $ne: true }, - typeGroup: { $ne: 'thumb' }, }, options, ); -- GitLab From 1233bffa8cb1ce5da0a7b2de9e4eb691a7068153 Mon Sep 17 00:00:00 2001 From: gabriellsh <40830821+gabriellsh@users.noreply.github.com> Date: Fri, 22 Dec 2023 13:01:39 -0300 Subject: [PATCH 068/329] chore: Add missing changesets (#31303) --- .changeset/remove-license-31189.md | 5 +++++ .changeset/resume-login-31301.md | 5 +++++ .changeset/save-token-31278.md | 5 +++++ 3 files changed, 15 insertions(+) create mode 100644 .changeset/remove-license-31189.md create mode 100644 .changeset/resume-login-31301.md create mode 100644 .changeset/save-token-31278.md diff --git a/.changeset/remove-license-31189.md b/.changeset/remove-license-31189.md new file mode 100644 index 00000000000..e510f263feb --- /dev/null +++ b/.changeset/remove-license-31189.md @@ -0,0 +1,5 @@ +--- +'@rocket.chat/ddp-client': patch +--- + +removed @rocket.chat/license as a dependency of ddp client diff --git a/.changeset/resume-login-31301.md b/.changeset/resume-login-31301.md new file mode 100644 index 00000000000..2ec78cd6c31 --- /dev/null +++ b/.changeset/resume-login-31301.md @@ -0,0 +1,5 @@ +--- +'@rocket.chat/ddp-client': patch +--- + +fixed an issue with the ddp client reconnection not resuming the login diff --git a/.changeset/save-token-31278.md b/.changeset/save-token-31278.md new file mode 100644 index 00000000000..49f22037f4c --- /dev/null +++ b/.changeset/save-token-31278.md @@ -0,0 +1,5 @@ +--- +'@rocket.chat/ddp-client': patch +--- + +fixed an issue with the ddp client account not saving credentials correctly -- GitLab From dc111e2e841a83207a30ec5889391a8b71af22dd Mon Sep 17 00:00:00 2001 From: Hugo Costa Date: Fri, 22 Dec 2023 15:19:35 -0300 Subject: [PATCH 069/329] fix: country select (#31305) --- .changeset/lucky-cycles-leave.md | 5 +++++ apps/meteor/package.json | 2 +- yarn.lock | 10 +++++----- 3 files changed, 11 insertions(+), 6 deletions(-) create mode 100644 .changeset/lucky-cycles-leave.md diff --git a/.changeset/lucky-cycles-leave.md b/.changeset/lucky-cycles-leave.md new file mode 100644 index 00000000000..608db68031b --- /dev/null +++ b/.changeset/lucky-cycles-leave.md @@ -0,0 +1,5 @@ +--- +"@rocket.chat/meteor": patch +--- + +Fixed Country select component at Organization form from `onboarding-ui` package diff --git a/apps/meteor/package.json b/apps/meteor/package.json index d1f40d4a811..5b5660e90c7 100644 --- a/apps/meteor/package.json +++ b/apps/meteor/package.json @@ -257,7 +257,7 @@ "@rocket.chat/models": "workspace:^", "@rocket.chat/mp3-encoder": "0.24.0", "@rocket.chat/omnichannel-services": "workspace:^", - "@rocket.chat/onboarding-ui": "~0.33.2", + "@rocket.chat/onboarding-ui": "~0.33.3", "@rocket.chat/password-policies": "workspace:^", "@rocket.chat/pdf-worker": "workspace:^", "@rocket.chat/poplib": "workspace:^", diff --git a/yarn.lock b/yarn.lock index 16795ab32c6..7b4b70b69dc 100644 --- a/yarn.lock +++ b/yarn.lock @@ -8612,7 +8612,7 @@ __metadata: "@rocket.chat/models": "workspace:^" "@rocket.chat/mp3-encoder": 0.24.0 "@rocket.chat/omnichannel-services": "workspace:^" - "@rocket.chat/onboarding-ui": ~0.33.2 + "@rocket.chat/onboarding-ui": ~0.33.3 "@rocket.chat/password-policies": "workspace:^" "@rocket.chat/pdf-worker": "workspace:^" "@rocket.chat/poplib": "workspace:^" @@ -9056,9 +9056,9 @@ __metadata: languageName: unknown linkType: soft -"@rocket.chat/onboarding-ui@npm:~0.33.2": - version: 0.33.2 - resolution: "@rocket.chat/onboarding-ui@npm:0.33.2" +"@rocket.chat/onboarding-ui@npm:~0.33.3": + version: 0.33.3 + resolution: "@rocket.chat/onboarding-ui@npm:0.33.3" dependencies: i18next: ~21.6.16 react-hook-form: ~7.27.1 @@ -9073,7 +9073,7 @@ __metadata: react: 17.0.2 react-dom: 17.0.2 react-i18next: ~11.15.4 - checksum: abbfde9f8ac5655b9dbd30872aabe585f2814d58c1c66d672b4aaf3ef3f69d0e31e626924bab19b052d242a0abdb9c7a0d3ff907a7f21742ec15a6dc367951af + checksum: 18c7e0a78a171086a22aeccde772357f0c06bf561955f3898be0009f2f609698ae67154b91af9841d9a4ae7eb8b2e7ec4ec3e47e8221b35e57fe4ac3adb0d2b7 languageName: node linkType: hard -- GitLab From caa7707bba2fd53a823487256dfa90f00fcc40e2 Mon Sep 17 00:00:00 2001 From: Tiago Evangelista Pinto Date: Fri, 22 Dec 2023 17:19:24 -0300 Subject: [PATCH 070/329] fix: Loading state for `Marketplace` related lists (#31299) --- .changeset/late-pots-travel.md | 5 ++++ apps/meteor/client/contexts/AppsContext.tsx | 9 +++----- apps/meteor/client/providers/AppsProvider.tsx | 23 ++++++++++++++++--- .../marketplace/AppsPage/AppsPageContent.tsx | 12 +++++----- .../AppsPage/AppsPageContentBody.tsx | 6 ++--- .../marketplace/hooks/useFilteredApps.ts | 5 ++-- 6 files changed, 40 insertions(+), 20 deletions(-) create mode 100644 .changeset/late-pots-travel.md diff --git a/.changeset/late-pots-travel.md b/.changeset/late-pots-travel.md new file mode 100644 index 00000000000..cd2131ac65b --- /dev/null +++ b/.changeset/late-pots-travel.md @@ -0,0 +1,5 @@ +--- +"@rocket.chat/meteor": minor +--- + +fix: Loading state for `Marketplace` related lists diff --git a/apps/meteor/client/contexts/AppsContext.tsx b/apps/meteor/client/contexts/AppsContext.tsx index 769609c733b..b1732b6444b 100644 --- a/apps/meteor/client/contexts/AppsContext.tsx +++ b/apps/meteor/client/contexts/AppsContext.tsx @@ -5,9 +5,9 @@ import { AsyncStatePhase } from '../lib/asyncState'; import type { App } from '../views/marketplace/types'; export type AppsContextValue = { - installedApps: AsyncState<{ apps: App[] }>; - marketplaceApps: AsyncState<{ apps: App[] }>; - privateApps: AsyncState<{ apps: App[] }>; + installedApps: Omit, 'error'>; + marketplaceApps: Omit, 'error'>; + privateApps: Omit, 'error'>; reload: () => Promise; }; @@ -15,17 +15,14 @@ export const AppsContext = createContext({ installedApps: { phase: AsyncStatePhase.LOADING, value: undefined, - error: undefined, }, marketplaceApps: { phase: AsyncStatePhase.LOADING, value: undefined, - error: undefined, }, privateApps: { phase: AsyncStatePhase.LOADING, value: undefined, - error: undefined, }, reload: () => Promise.resolve(), }); diff --git a/apps/meteor/client/providers/AppsProvider.tsx b/apps/meteor/client/providers/AppsProvider.tsx index bb35c01c4ae..1a5d6727525 100644 --- a/apps/meteor/client/providers/AppsProvider.tsx +++ b/apps/meteor/client/providers/AppsProvider.tsx @@ -8,12 +8,26 @@ import { AppClientOrchestratorInstance } from '../../ee/client/apps/orchestrator import { AppsContext } from '../contexts/AppsContext'; import { useIsEnterprise } from '../hooks/useIsEnterprise'; import { useInvalidateLicense } from '../hooks/useLicense'; +import type { AsyncState } from '../lib/asyncState'; import { AsyncStatePhase } from '../lib/asyncState'; import { useInvalidateAppsCountQueryCallback } from '../views/marketplace/hooks/useAppsCountQuery'; import type { App } from '../views/marketplace/types'; const sortByName = (apps: App[]): App[] => apps.sort((a, b) => (a.name.toLowerCase() > b.name.toLowerCase() ? 1 : -1)); +const getAppState = ( + loading: boolean, + apps: App[] | undefined, +): Omit< + AsyncState<{ + apps: App[]; + }>, + 'error' +> => ({ + phase: loading ? AsyncStatePhase.LOADING : AsyncStatePhase.RESOLVED, + value: { apps: apps || [] }, +}); + const AppsProvider: FC = ({ children }) => { const isAdminUser = usePermission('manage-apps'); @@ -129,13 +143,16 @@ const AppsProvider: FC = ({ children }) => { }, ); + const [marketplaceAppsData, installedAppsData, privateAppsData] = store.data || []; + const { isLoading } = store; + return ( { await Promise.all([queryClient.invalidateQueries(['marketplace'])]); }, diff --git a/apps/meteor/client/views/marketplace/AppsPage/AppsPageContent.tsx b/apps/meteor/client/views/marketplace/AppsPage/AppsPageContent.tsx index 43feaa8b299..e72a30e6a5c 100644 --- a/apps/meteor/client/views/marketplace/AppsPage/AppsPageContent.tsx +++ b/apps/meteor/client/views/marketplace/AppsPage/AppsPageContent.tsx @@ -132,16 +132,16 @@ const AppsPageContent = (): ReactElement => { context, }); - const noInstalledApps = appsResult.phase === AsyncStatePhase.RESOLVED && !isMarketplace && appsResult.value.totalAppsLength === 0; + const noInstalledApps = appsResult.phase === AsyncStatePhase.RESOLVED && !isMarketplace && appsResult.value?.totalAppsLength === 0; const noMarketplaceOrInstalledAppMatches = - appsResult.phase === AsyncStatePhase.RESOLVED && (isMarketplace || isPremium) && appsResult.value.count === 0; + appsResult.phase === AsyncStatePhase.RESOLVED && (isMarketplace || isPremium) && appsResult.value?.count === 0; const noInstalledAppMatches = appsResult.phase === AsyncStatePhase.RESOLVED && context === 'installed' && - appsResult.value.totalAppsLength !== 0 && - appsResult.value.count === 0; + appsResult.value?.totalAppsLength !== 0 && + appsResult.value?.count === 0; const noAppRequests = context === 'requested' && appsResult?.value?.count === 0; @@ -194,13 +194,13 @@ const AppsPageContent = (): ReactElement => { } if (noMarketplaceOrInstalledAppMatches) { - return ; + return ; } if (noInstalledAppMatches) { return ( diff --git a/apps/meteor/client/views/marketplace/AppsPage/AppsPageContentBody.tsx b/apps/meteor/client/views/marketplace/AppsPage/AppsPageContentBody.tsx index e9a76d8a67b..56c3ca26f9a 100644 --- a/apps/meteor/client/views/marketplace/AppsPage/AppsPageContentBody.tsx +++ b/apps/meteor/client/views/marketplace/AppsPage/AppsPageContentBody.tsx @@ -11,7 +11,7 @@ import FeaturedAppsSections from './FeaturedAppsSections'; type AppsPageContentBodyProps = { isMarketplace: boolean; isFiltered: boolean; - appsResult: { items: App[] } & { shouldShowSearchText: boolean } & PaginatedResult & { allApps: App[] } & { totalAppsLength: number }; + appsResult?: { items: App[] } & { shouldShowSearchText: boolean } & PaginatedResult & { allApps: App[] } & { totalAppsLength: number }; itemsPerPage: 25 | 50 | 100; current: number; onSetItemsPerPage: React.Dispatch>; @@ -43,8 +43,8 @@ const AppsPageContentBody = ({ {noErrorsOcurred && ( - {isMarketplace && !isFiltered && } - + {isMarketplace && !isFiltered && } + )} diff --git a/apps/meteor/client/views/marketplace/hooks/useFilteredApps.ts b/apps/meteor/client/views/marketplace/hooks/useFilteredApps.ts index 437c8d35207..c90052fd78e 100644 --- a/apps/meteor/client/views/marketplace/hooks/useFilteredApps.ts +++ b/apps/meteor/client/views/marketplace/hooks/useFilteredApps.ts @@ -39,8 +39,9 @@ export const useFilteredApps = ({ sortingMethod: string; status: string; context?: string; -}): AsyncState< - { items: App[] } & { shouldShowSearchText: boolean } & PaginatedResult & { allApps: App[] } & { totalAppsLength: number } +}): Omit< + AsyncState<{ items: App[] } & { shouldShowSearchText: boolean } & PaginatedResult & { allApps: App[] } & { totalAppsLength: number }>, + 'error' > => { const value = useMemo(() => { if (appsData.value === undefined) { -- GitLab From 5bb039037015d7d4cd3dcd255ac89e63dbe5c84c Mon Sep 17 00:00:00 2001 From: njgrc <145460309+njgrc@users.noreply.github.com> Date: Wed, 20 Dec 2023 19:46:53 -0300 Subject: [PATCH 071/329] chore: Quality of life improvements --- apps/meteor/app/2fa/client/TOTPPassword.js | 11 +++- apps/meteor/app/2fa/server/code/EmailCheck.ts | 37 +++++++----- apps/meteor/app/2fa/server/code/ICodeCheck.ts | 5 +- .../2fa/server/code/PasswordCheckFallback.ts | 4 ++ apps/meteor/app/2fa/server/code/TOTPCheck.ts | 4 ++ apps/meteor/app/2fa/server/code/index.ts | 9 +++ apps/meteor/app/api/server/v1/misc.ts | 9 +-- apps/meteor/app/api/server/v1/oauthapps.ts | 4 ++ apps/meteor/client/lib/2fa/utils.ts | 6 ++ apps/meteor/client/lib/userData.ts | 2 +- .../rocketchat-i18n/i18n/en.i18n.json | 3 + apps/meteor/server/models/raw/Users.js | 58 ++++++++++++------- apps/meteor/server/settings/accounts.ts | 15 ++++- apps/meteor/server/settings/file-upload.ts | 2 +- .../tests/e2e/fixtures/collections/users.ts | 2 +- .../tests/end-to-end/api/18-oauthapps.js | 26 +++++++++ packages/core-typings/src/IUser.ts | 3 +- .../model-typings/src/models/IUsersModel.ts | 10 +++- 18 files changed, 156 insertions(+), 54 deletions(-) diff --git a/apps/meteor/app/2fa/client/TOTPPassword.js b/apps/meteor/app/2fa/client/TOTPPassword.js index 3fccb646bad..20269744fa7 100644 --- a/apps/meteor/app/2fa/client/TOTPPassword.js +++ b/apps/meteor/app/2fa/client/TOTPPassword.js @@ -2,7 +2,7 @@ import { Accounts } from 'meteor/accounts-base'; import { Meteor } from 'meteor/meteor'; import { process2faReturn } from '../../../client/lib/2fa/process2faReturn'; -import { isTotpInvalidError, reportError } from '../../../client/lib/2fa/utils'; +import { isTotpInvalidError, isTotpMaxAttemptsError, reportError } from '../../../client/lib/2fa/utils'; import { dispatchToastMessage } from '../../../client/lib/toast'; import { t } from '../../utils/lib/i18n'; @@ -47,6 +47,14 @@ Meteor.loginWithPassword = function (email, password, cb) { emailOrUsername: email, onCode: (code) => { Meteor.loginWithPasswordAndTOTP(email, password, code, (error) => { + if (isTotpMaxAttemptsError(error)) { + dispatchToastMessage({ + type: 'error', + message: t('totp-max-attempts'), + }); + cb(); + return; + } if (isTotpInvalidError(error)) { dispatchToastMessage({ type: 'error', @@ -55,7 +63,6 @@ Meteor.loginWithPassword = function (email, password, cb) { cb(); return; } - cb(error); }); }, diff --git a/apps/meteor/app/2fa/server/code/EmailCheck.ts b/apps/meteor/app/2fa/server/code/EmailCheck.ts index bf2e4aa170c..123df96ee26 100644 --- a/apps/meteor/app/2fa/server/code/EmailCheck.ts +++ b/apps/meteor/app/2fa/server/code/EmailCheck.ts @@ -69,26 +69,26 @@ ${t('If_you_didnt_try_to_login_in_your_account_please_ignore_this_email')} return false; } - if (!user.services || !Array.isArray(user.services?.emailCode)) { + if (!user.services?.emailCode) { return false; } // Remove non digits codeFromEmail = codeFromEmail.replace(/([^\d])/g, ''); - await Users.removeExpiredEmailCodesOfUserId(user._id); + const { code, expire } = user.services.emailCode; - for await (const { code, expire } of user.services.emailCode) { - if (expire < new Date()) { - continue; - } + if (expire < new Date()) { + return false; + } - if (await bcrypt.compare(codeFromEmail, code)) { - await Users.removeEmailCodeByUserIdAndCode(user._id, code); - return true; - } + if (await bcrypt.compare(codeFromEmail, code)) { + await Users.removeEmailCodeOfUserId(user._id); + return true; } + await Users.incrementInvalidEmailCodeAttempt(user._id); + return false; } @@ -109,7 +109,7 @@ ${t('If_you_didnt_try_to_login_in_your_account_please_ignore_this_email')} } public async processInvalidCode(user: IUser): Promise { - await Users.removeExpiredEmailCodesOfUserId(user._id); + await Users.removeExpiredEmailCodeOfUserId(user._id); // Generate new code if the there isn't any code with more than 5 minutes to expire const expireWithDelta = new Date(); @@ -119,13 +119,15 @@ ${t('If_you_didnt_try_to_login_in_your_account_please_ignore_this_email')} const emailOrUsername = user.username || emails[0]; - const hasValidCode = user.services?.emailCode?.filter(({ expire }) => expire > expireWithDelta); - if (hasValidCode?.length) { + const hasValidCode = + user.services?.emailCode?.expire && + user.services?.emailCode?.expire > expireWithDelta && + !(await this.maxFaildedAttemtpsReached(user)); + if (hasValidCode) { return { emailOrUsername, codeGenerated: false, - codeCount: hasValidCode.length, - codeExpires: hasValidCode.map((i) => i.expire), + codeExpires: user.services?.emailCode?.expire, }; } @@ -136,4 +138,9 @@ ${t('If_you_didnt_try_to_login_in_your_account_please_ignore_this_email')} emailOrUsername, }; } + + public async maxFaildedAttemtpsReached(user: IUser) { + const maxAttempts = settings.get('Accounts_TwoFactorAuthentication_Max_Invalid_Email_Code_Attempts'); + return (await Users.maxInvalidEmailCodeAttemptsReached(user._id, maxAttempts)) as boolean; + } } diff --git a/apps/meteor/app/2fa/server/code/ICodeCheck.ts b/apps/meteor/app/2fa/server/code/ICodeCheck.ts index f0e165735ce..1cd1ba68e0d 100644 --- a/apps/meteor/app/2fa/server/code/ICodeCheck.ts +++ b/apps/meteor/app/2fa/server/code/ICodeCheck.ts @@ -2,8 +2,7 @@ import type { IUser } from '@rocket.chat/core-typings'; export interface IProcessInvalidCodeResult { codeGenerated: boolean; - codeCount?: number; - codeExpires?: Date[]; + codeExpires?: Date; emailOrUsername?: string; } @@ -15,4 +14,6 @@ export interface ICodeCheck { verify(user: IUser, code: string, force?: boolean): Promise; processInvalidCode(user: IUser): Promise; + + maxFaildedAttemtpsReached(user: IUser): Promise; } diff --git a/apps/meteor/app/2fa/server/code/PasswordCheckFallback.ts b/apps/meteor/app/2fa/server/code/PasswordCheckFallback.ts index 10d2f01fadb..6c441127f79 100644 --- a/apps/meteor/app/2fa/server/code/PasswordCheckFallback.ts +++ b/apps/meteor/app/2fa/server/code/PasswordCheckFallback.ts @@ -41,4 +41,8 @@ export class PasswordCheckFallback implements ICodeCheck { codeGenerated: false, }; } + + public async maxFaildedAttemtpsReached(_user: IUser): Promise { + return false; + } } diff --git a/apps/meteor/app/2fa/server/code/TOTPCheck.ts b/apps/meteor/app/2fa/server/code/TOTPCheck.ts index 2ed91d7ec2a..3aa2604c796 100644 --- a/apps/meteor/app/2fa/server/code/TOTPCheck.ts +++ b/apps/meteor/app/2fa/server/code/TOTPCheck.ts @@ -38,4 +38,8 @@ export class TOTPCheck implements ICodeCheck { codeGenerated: false, }; } + + public async maxFaildedAttemtpsReached(_user: IUser): Promise { + return false; + } } diff --git a/apps/meteor/app/2fa/server/code/index.ts b/apps/meteor/app/2fa/server/code/index.ts index 250fd5b158c..1fbe658e568 100644 --- a/apps/meteor/app/2fa/server/code/index.ts +++ b/apps/meteor/app/2fa/server/code/index.ts @@ -217,6 +217,15 @@ export async function checkCodeForUser({ user, code, method, options = {}, conne const valid = await selectedMethod.verify(existingUser, code, options.requireSecondFactor); if (!valid) { + const tooManyFailedAttempts = await selectedMethod.maxFaildedAttemtpsReached(existingUser); + if (tooManyFailedAttempts) { + throw new Meteor.Error('totp-max-attempts', 'TOTP Maximun Failed Attempts Reached', { + method: selectedMethod.name, + ...data, + availableMethods, + }); + } + throw new Meteor.Error('totp-invalid', 'TOTP Invalid', { method: selectedMethod.name, ...data, diff --git a/apps/meteor/app/api/server/v1/misc.ts b/apps/meteor/app/api/server/v1/misc.ts index ae5a79719cc..7b6c964a50b 100644 --- a/apps/meteor/app/api/server/v1/misc.ts +++ b/apps/meteor/app/api/server/v1/misc.ts @@ -538,7 +538,7 @@ API.v1.addRoute( this.token || crypto .createHash('md5') - .update(this.requestIp + this.request.headers['user-agent']) + .update(this.requestIp + this.user._id) .digest('hex'); const rateLimiterInput = { @@ -594,12 +594,7 @@ API.v1.addRoute( const { method, params, id } = data; - const connectionId = - this.token || - crypto - .createHash('md5') - .update(this.requestIp + this.request.headers['user-agent']) - .digest('hex'); + const connectionId = this.token || crypto.createHash('md5').update(this.requestIp).digest('hex'); const rateLimiterInput = { userId: this.userId || undefined, diff --git a/apps/meteor/app/api/server/v1/oauthapps.ts b/apps/meteor/app/api/server/v1/oauthapps.ts index ca6a2c3a56b..034a73f5410 100644 --- a/apps/meteor/app/api/server/v1/oauthapps.ts +++ b/apps/meteor/app/api/server/v1/oauthapps.ts @@ -27,6 +27,10 @@ API.v1.addRoute( { authRequired: true, validateParams: isOauthAppsGetParams }, { async get() { + if (!(await hasPermissionAsync(this.userId, 'manage-oauth-apps'))) { + return API.v1.unauthorized(); + } + const oauthApp = await OAuthApps.findOneAuthAppByIdOrClientId(this.queryParams); if (!oauthApp) { diff --git a/apps/meteor/client/lib/2fa/utils.ts b/apps/meteor/client/lib/2fa/utils.ts index 4abd3f436e6..e57037a1489 100644 --- a/apps/meteor/client/lib/2fa/utils.ts +++ b/apps/meteor/client/lib/2fa/utils.ts @@ -12,6 +12,12 @@ export const isTotpInvalidError = (error: unknown): error is Meteor.Error & ({ e (error as { error?: unknown } | undefined)?.error === 'totp-invalid' || (error as { errorType?: unknown } | undefined)?.errorType === 'totp-invalid'; +export const isTotpMaxAttemptsError = ( + error: unknown, +): error is Meteor.Error & ({ error: 'totp-max-attempts' } | { errorType: 'totp-max-attempts' }) => + (error as { error?: unknown } | undefined)?.error === 'totp-max-attempts' || + (error as { errorType?: unknown } | undefined)?.errorType === 'totp-max-attempts'; + const isLoginCancelledError = (error: unknown): error is Meteor.Error => error instanceof Meteor.Error && error.error === Accounts.LoginCancelledError.numericError; diff --git a/apps/meteor/client/lib/userData.ts b/apps/meteor/client/lib/userData.ts index 9e67f4034b1..5ca61d131f6 100644 --- a/apps/meteor/client/lib/userData.ts +++ b/apps/meteor/client/lib/userData.ts @@ -122,7 +122,7 @@ export const synchronizeUserData = async (uid: IUser['_id']): Promise ({ expire: new Date(expire), ...data })) || [], + ...(emailCode ? { ...emailCode, expire: new Date(emailCode.expire) } : {}), ...(email2fa ? { email2fa: { ...email2fa, changedAt: new Date(email2fa.changedAt) } } : {}), ...(email?.verificationTokens && { email: { diff --git a/apps/meteor/packages/rocketchat-i18n/i18n/en.i18n.json b/apps/meteor/packages/rocketchat-i18n/i18n/en.i18n.json index 889fa2bce7e..1cf864edb0a 100644 --- a/apps/meteor/packages/rocketchat-i18n/i18n/en.i18n.json +++ b/apps/meteor/packages/rocketchat-i18n/i18n/en.i18n.json @@ -282,6 +282,8 @@ "Accounts_TwoFactorAuthentication_MaxDelta_Description": "The Maximum Delta determines how many tokens are valid at any given time. Tokens are generated every 30 seconds, and are valid for (30 * Maximum Delta) seconds. \nExample: With a Maximum Delta set to 10, each token can be used up to 300 seconds before or after it's timestamp. This is useful when the client's clock is not properly synced with the server.", "Accounts_TwoFactorAuthentication_RememberFor": "Remember Two Factor for (seconds)", "Accounts_TwoFactorAuthentication_RememberFor_Description": "Do not request two factor authorization code if it was already provided before in the given time.", + "Accounts_TwoFactorAuthentication_Max_Invalid_Email_Code_Attempts": "Maximun Invalid Email OTP Codes Allowed", + "Accounts_TwoFactorAuthentication_Max_Invalid_Email_Code_Attempts_Description": "The system allows a maximum number of invalid email OTP codes, after which a new code is automatically generated. We highly recommend using this setting along with 'Block failed login attempts by Username'.", "Accounts_UseDefaultBlockedDomainsList": "Use Default Blocked Domains List", "Accounts_UseDNSDomainCheck": "Use DNS Domain Check", "API_EmbedDisabledFor": "Disable Embed for Users", @@ -5183,6 +5185,7 @@ "totp-disabled": "You do not have 2FA login enabled for your user", "totp-invalid": "Code or password invalid", "totp-required": "TOTP Required", + "totp-max-attempts": "Maximum OTP failed attempts reached. A new code will be generated.", "Transcript": "Transcript", "Transcript_Enabled": "Ask Visitor if They Would Like a Transcript After Chat Closed", "Transcript_message": "Message to Show When Asking About Transcript", diff --git a/apps/meteor/server/models/raw/Users.js b/apps/meteor/server/models/raw/Users.js index c8cee8f2f6b..8bcc2608150 100644 --- a/apps/meteor/server/models/raw/Users.js +++ b/apps/meteor/server/models/raw/Users.js @@ -1937,45 +1937,63 @@ export class UsersRaw extends BaseRaw { ); } - removeExpiredEmailCodesOfUserId(userId) { + removeExpiredEmailCodeOfUserId(userId) { return this.updateOne( - { _id: userId }, + { '_id': userId, 'services.emailCode.expire': { $lt: new Date() } }, { - $pull: { - 'services.emailCode': { - expire: { $lt: new Date() }, - }, - }, + $unset: { 'services.emailCode': 1 }, }, ); } - removeEmailCodeByUserIdAndCode(userId, code) { + removeEmailCodeOfUserId(userId) { return this.updateOne( { _id: userId }, { - $pull: { - 'services.emailCode': { - code, - }, + $unset: { 'services.emailCode': 1 }, + }, + ); + } + + incrementInvalidEmailCodeAttempt(userId) { + return this.findOneAndUpdate( + { _id: userId }, + { + $inc: { 'services.emailCode.attempts': 1 }, + }, + { + returnDocument: 'after', + projection: { + 'services.emailCode.attempts': 1, + }, + }, + ); + } + + async maxInvalidEmailCodeAttemptsReached(userId, maxAttempts) { + const result = await this.findOne( + { + '_id': userId, + 'services.emailCode.attempts': { $gte: maxAttempts }, + }, + { + projection: { + _id: 1, }, }, ); + return !!result?._id; } addEmailCodeByUserId(userId, code, expire) { return this.updateOne( { _id: userId }, { - $push: { + $set: { 'services.emailCode': { - $each: [ - { - code, - expire, - }, - ], - $slice: -5, + code, + expire, + attempts: 0, }, }, }, diff --git a/apps/meteor/server/settings/accounts.ts b/apps/meteor/server/settings/accounts.ts index ba031c9210d..2b1dac892c0 100644 --- a/apps/meteor/server/settings/accounts.ts +++ b/apps/meteor/server/settings/accounts.ts @@ -57,6 +57,19 @@ export const createAccountSettings = () => ], }); + await this.add('Accounts_TwoFactorAuthentication_Max_Invalid_Email_Code_Attempts', 5, { + type: 'int', + enableQuery: [ + enable2FA, + { + _id: 'Accounts_TwoFactorAuthentication_By_Email_Enabled', + value: true, + }, + ], + i18nLabel: 'Accounts_TwoFactorAuthentication_Max_Invalid_Email_Code_Attempts', + i18nDescription: 'Accounts_TwoFactorAuthentication_Max_Invalid_Email_Code_Attempts_Description', + }); + await this.add('Accounts_TwoFactorAuthentication_RememberFor', 1800, { type: 'int', enableQuery: enable2FA, @@ -72,7 +85,7 @@ export const createAccountSettings = () => const enableQueryCollectData = { _id: 'Block_Multiple_Failed_Logins_Enabled', value: true }; await this.section('Login_Attempts', async function () { - await this.add('Block_Multiple_Failed_Logins_Enabled', false, { + await this.add('Block_Multiple_Failed_Logins_Enabled', true, { type: 'boolean', }); diff --git a/apps/meteor/server/settings/file-upload.ts b/apps/meteor/server/settings/file-upload.ts index fce9a005980..643c46ed848 100644 --- a/apps/meteor/server/settings/file-upload.ts +++ b/apps/meteor/server/settings/file-upload.ts @@ -31,7 +31,7 @@ export const createFileUploadSettings = () => i18nDescription: 'FileUpload_ProtectFilesDescription', }); - await this.add('FileUpload_Restrict_to_room_members', false, { + await this.add('FileUpload_Restrict_to_room_members', true, { type: 'boolean', enableQuery: { _id: 'FileUpload_ProtectFiles', diff --git a/apps/meteor/tests/e2e/fixtures/collections/users.ts b/apps/meteor/tests/e2e/fixtures/collections/users.ts index 661f096c875..cc437597a5e 100644 --- a/apps/meteor/tests/e2e/fixtures/collections/users.ts +++ b/apps/meteor/tests/e2e/fixtures/collections/users.ts @@ -42,7 +42,7 @@ export function createUserFixture(user: IUserState): UserFixture { }, ], }, - emailCode: [{ code: '', expire: new Date() }], + emailCode: { code: '', attempts: 0, expire: new Date() }, }, createdAt: new Date(), _updatedAt: new Date(), diff --git a/apps/meteor/tests/end-to-end/api/18-oauthapps.js b/apps/meteor/tests/end-to-end/api/18-oauthapps.js index e2100e0c1f7..83017a81104 100644 --- a/apps/meteor/tests/end-to-end/api/18-oauthapps.js +++ b/apps/meteor/tests/end-to-end/api/18-oauthapps.js @@ -63,6 +63,32 @@ describe('[OAuthApps]', function () { }) .end(done); }); + it('should return a 403 Forbidden error when the user does not have the necessary permission by client id', (done) => { + updatePermission('manage-oauth-apps', []).then(() => { + request + .get(api('oauth-apps.get?clientId=zapier')) + .set(credentials) + .expect(403) + .expect((res) => { + expect(res.body).to.have.property('success', false); + expect(res.body.error).to.be.equal('unauthorized'); + }) + .end(done); + }); + }); + it('should return a 403 Forbidden error when the user does not have the necessary permission by app id', (done) => { + updatePermission('manage-oauth-apps', []).then(() => { + request + .get(api('oauth-apps.get?appId=zapier')) + .set(credentials) + .expect(403) + .expect((res) => { + expect(res.body).to.have.property('success', false); + expect(res.body.error).to.be.equal('unauthorized'); + }) + .end(done); + }); + }); }); describe('[/oauth-apps.create]', () => { diff --git a/packages/core-typings/src/IUser.ts b/packages/core-typings/src/IUser.ts index ce14c4020d6..98785805714 100644 --- a/packages/core-typings/src/IUser.ts +++ b/packages/core-typings/src/IUser.ts @@ -29,6 +29,7 @@ export interface IUserEmailVerificationToken { export interface IUserEmailCode { code: string; expire: Date; + attempts: number; } type LoginToken = IMeteorLoginToken | IPersonalAccessToken; @@ -75,7 +76,7 @@ export interface IUserServices { enabled: boolean; changedAt: Date; }; - emailCode?: IUserEmailCode[]; + emailCode?: IUserEmailCode; saml?: { inResponseTo?: string; provider?: string; diff --git a/packages/model-typings/src/models/IUsersModel.ts b/packages/model-typings/src/models/IUsersModel.ts index f14f5bc90d0..f9a2b1c45a2 100644 --- a/packages/model-typings/src/models/IUsersModel.ts +++ b/packages/model-typings/src/models/IUsersModel.ts @@ -9,7 +9,7 @@ import type { AtLeast, ILivechatAgentStatus, } from '@rocket.chat/core-typings'; -import type { Document, UpdateResult, FindCursor, FindOptions, Filter, InsertOneResult, DeleteResult } from 'mongodb'; +import type { Document, UpdateResult, FindCursor, FindOptions, Filter, InsertOneResult, DeleteResult, ModifyResult } from 'mongodb'; import type { FindPaginated, IBaseModel } from './IBaseModel'; @@ -279,8 +279,10 @@ export interface IUsersModel extends IBaseModel { disableEmail2FAByUserId(userId: string): Promise; findByIdsWithPublicE2EKey(userIds: string[], options?: FindOptions): FindCursor; resetE2EKey(userId: string): Promise; - removeExpiredEmailCodesOfUserId(userId: string): Promise; - removeEmailCodeByUserIdAndCode(userId: string, code: string): Promise; + removeExpiredEmailCodeOfUserId(userId: string): Promise; + removeEmailCodeByUserId(userId: string): Promise; + increaseInvalidEmailCodeAttempt(userId: string): Promise; + maxInvalidEmailCodeAttemptsReached(userId: string, maxAttemtps: number): Promise; addEmailCodeByUserId(userId: string, code: string, expire: Date): Promise; findActiveUsersInRoles(roles: string[], options?: FindOptions): FindCursor; countActiveUsersInRoles(roles: string[], options?: FindOptions): Promise; @@ -387,4 +389,6 @@ export interface IUsersModel extends IBaseModel { options: FindOptions, ): Promise<{ sortedResults: (T & { departments: string[] })[]; totalCount: { total: number }[] }[]>; countByRole(roleName: string): Promise; + removeEmailCodeOfUserId(userId: string): Promise; + incrementInvalidEmailCodeAttempt(userId: string): Promise>; } -- GitLab From 792d93deb6fb7c97100b168e05a130ed8a723471 Mon Sep 17 00:00:00 2001 From: Rodrigo Nascimento Date: Fri, 15 Dec 2023 14:42:09 -0300 Subject: [PATCH 072/329] chore: Add slide window config to round trip metric (#31247) --- .../app/metrics/server/lib/collectMetrics.ts | 15 ++++----------- apps/meteor/app/metrics/server/lib/metrics.ts | 3 +++ apps/meteor/package.json | 2 +- yarn.lock | 13 +++++++++++-- 4 files changed, 19 insertions(+), 14 deletions(-) diff --git a/apps/meteor/app/metrics/server/lib/collectMetrics.ts b/apps/meteor/app/metrics/server/lib/collectMetrics.ts index 8213e27ed4f..136686f49c9 100644 --- a/apps/meteor/app/metrics/server/lib/collectMetrics.ts +++ b/apps/meteor/app/metrics/server/lib/collectMetrics.ts @@ -175,17 +175,10 @@ const updatePrometheusConfig = async (): Promise => { clearInterval(resetTimer); if (is.resetInterval) { resetTimer = setInterval(() => { - client.register - .getMetricsAsArray() - .then((metrics) => { - metrics.forEach((metric) => { - // @ts-expect-error Property 'hashMap' does not exist on type 'metric'. - metric.hashMap = {}; - }); - }) - .catch((err) => { - SystemLogger.error({ msg: 'Error while collecting metrics', err }); - }); + client.register.getMetricsAsArray().forEach((metric) => { + // @ts-expect-error Property 'hashMap' does not exist on type 'metric'. + metric.hashMap = {}; + }); }, is.resetInterval); } diff --git a/apps/meteor/app/metrics/server/lib/metrics.ts b/apps/meteor/app/metrics/server/lib/metrics.ts index 11954322a41..00bbedce287 100644 --- a/apps/meteor/app/metrics/server/lib/metrics.ts +++ b/apps/meteor/app/metrics/server/lib/metrics.ts @@ -67,6 +67,9 @@ export const metrics = { name: 'rocketchat_messages_roundtrip_time_summary', help: 'time spent by a message from save to receive back', percentiles, + maxAgeSeconds: 60, + ageBuckets: 5, + // pruneAgedBuckets: true, // Type not added to prom-client on 14.2 https://github.com/siimon/prom-client/pull/558 }), ddpSessions: new client.Gauge({ diff --git a/apps/meteor/package.json b/apps/meteor/package.json index 5b5660e90c7..6f2e9628339 100644 --- a/apps/meteor/package.json +++ b/apps/meteor/package.json @@ -390,7 +390,7 @@ "photoswipe": "^4.1.3", "pino": "^8.15.0", "postis": "^2.2.0", - "prom-client": "^14.0.1", + "prom-client": "^14.2.0", "prometheus-gc-stats": "^0.6.5", "proxy-from-env": "^1.1.0", "psl": "^1.8.0", diff --git a/yarn.lock b/yarn.lock index 7b4b70b69dc..e90e17ebfda 100644 --- a/yarn.lock +++ b/yarn.lock @@ -8865,7 +8865,7 @@ __metadata: postcss-url: ^10.1.3 postis: ^2.2.0 prettier: ~2.8.8 - prom-client: ^14.0.1 + prom-client: ^14.2.0 prometheus-gc-stats: ^0.6.5 proxy-from-env: ^1.1.0 proxyquire: ^2.1.3 @@ -32374,7 +32374,7 @@ __metadata: languageName: node linkType: hard -"prom-client@npm:^14.0.0, prom-client@npm:^14.0.1": +"prom-client@npm:^14.0.0": version: 14.0.1 resolution: "prom-client@npm:14.0.1" dependencies: @@ -32383,6 +32383,15 @@ __metadata: languageName: node linkType: hard +"prom-client@npm:^14.2.0": + version: 14.2.0 + resolution: "prom-client@npm:14.2.0" + dependencies: + tdigest: ^0.1.1 + checksum: d4c04e57616c72643dd02862d0d4bde09cf8869a19d0aef5e7b785e6e27d02439b66cdc165e3492f62d579fa91579183820870cc757a09b99399d2d02f46b9f1 + languageName: node + linkType: hard + "prometheus-gc-stats@npm:^0.6.5": version: 0.6.5 resolution: "prometheus-gc-stats@npm:0.6.5" -- GitLab From 2c577efc5f7c3d1a5cbd4f6fef5337cd375d471f Mon Sep 17 00:00:00 2001 From: Diego Sampaio Date: Fri, 22 Dec 2023 18:57:27 -0300 Subject: [PATCH 073/329] chore: add changeset --- .changeset/fair-suns-study.md | 5 +++++ 1 file changed, 5 insertions(+) create mode 100644 .changeset/fair-suns-study.md diff --git a/.changeset/fair-suns-study.md b/.changeset/fair-suns-study.md new file mode 100644 index 00000000000..64dcc2ec7ee --- /dev/null +++ b/.changeset/fair-suns-study.md @@ -0,0 +1,5 @@ +--- +'@rocket.chat/meteor': patch +--- + +Security improvements -- GitLab From 899031337c1fef9f1477bd49e565eef16be46326 Mon Sep 17 00:00:00 2001 From: Diego Sampaio Date: Sat, 23 Dec 2023 07:45:02 -0300 Subject: [PATCH 074/329] test: fix file upload test --- apps/meteor/tests/end-to-end/api/09-rooms.js | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/apps/meteor/tests/end-to-end/api/09-rooms.js b/apps/meteor/tests/end-to-end/api/09-rooms.js index 533c0b63da4..c717e526d20 100644 --- a/apps/meteor/tests/end-to-end/api/09-rooms.js +++ b/apps/meteor/tests/end-to-end/api/09-rooms.js @@ -174,7 +174,8 @@ describe('[Rooms]', function () { await request.get(fileOldUrl).set(credentials).expect('Content-Type', 'image/png').expect(200); }); - it('should be able to get the file when no access to the room', async () => { + it('should be able to get the file when no access to the room if setting allows it', async () => { + await updateSetting('FileUpload_Restrict_to_room_members', false); await request.get(fileNewUrl).set(userCredentials).expect('Content-Type', 'image/png').expect(200); await request.get(fileOldUrl).set(userCredentials).expect('Content-Type', 'image/png').expect(200); }); -- GitLab From 4e49fcc7d940599e37a773bdd2299b0afee32f70 Mon Sep 17 00:00:00 2001 From: Pierre Lehnen <55164754+pierre-lehnen-rc@users.noreply.github.com> Date: Thu, 14 Dec 2023 17:04:04 -0300 Subject: [PATCH 075/329] fix: mobile users receives two push notifications when a direct call is started (#31164) --- .changeset/spotty-suns-grin.md | 5 +++++ .../server/functions/notifications/mobile.js | 14 ++++++++++++++ .../server/lib/sendNotificationsOnMessage.js | 18 ++++++++++++------ .../isRoomCompatibleWithVideoConfRinging.ts | 4 ++++ .../services/video-conference/service.ts | 3 ++- 5 files changed, 37 insertions(+), 7 deletions(-) create mode 100644 .changeset/spotty-suns-grin.md create mode 100644 apps/meteor/server/lib/isRoomCompatibleWithVideoConfRinging.ts diff --git a/.changeset/spotty-suns-grin.md b/.changeset/spotty-suns-grin.md new file mode 100644 index 00000000000..8045ebbd1b4 --- /dev/null +++ b/.changeset/spotty-suns-grin.md @@ -0,0 +1,5 @@ +--- +'@rocket.chat/meteor': patch +--- + +Improved the experience of receiving conference calls on the mobile app by disabling the push notification for the "new call" message if a push is already being sent to trigger the phone's ringing tone. diff --git a/apps/meteor/app/lib/server/functions/notifications/mobile.js b/apps/meteor/app/lib/server/functions/notifications/mobile.js index b4139bea6f1..a0aa3fa2402 100644 --- a/apps/meteor/app/lib/server/functions/notifications/mobile.js +++ b/apps/meteor/app/lib/server/functions/notifications/mobile.js @@ -1,6 +1,7 @@ import { Subscriptions } from '@rocket.chat/models'; import { i18n } from '../../../../../server/lib/i18n'; +import { isRoomCompatibleWithVideoConfRinging } from '../../../../../server/lib/isRoomCompatibleWithVideoConfRinging'; import { roomCoordinator } from '../../../../../server/lib/rooms/roomCoordinator'; import { settings } from '../../../../settings/server'; @@ -73,6 +74,9 @@ export function shouldNotifyMobile({ hasReplyToThread, roomType, isThread, + isVideoConf, + userPreferences, + roomUids, }) { if (settings.get('Push_enable') !== true) { return false; @@ -86,6 +90,16 @@ export function shouldNotifyMobile({ return false; } + // If the user is going to receive a ringing push notification, do not send another push for the message generated by that videoconference + if ( + isVideoConf && + settings.get('VideoConf_Mobile_Ringing') && + isRoomCompatibleWithVideoConfRinging(roomType, roomUids) && + (userPreferences?.enableMobileRinging ?? settings.get(`Accounts_Default_User_Preferences_enableMobileRinging`)) + ) { + return false; + } + if (!mobilePushNotifications) { if (settings.get('Accounts_Default_User_Preferences_pushNotifications') === 'all' && (!isThread || hasReplyToThread)) { return true; diff --git a/apps/meteor/app/lib/server/lib/sendNotificationsOnMessage.js b/apps/meteor/app/lib/server/lib/sendNotificationsOnMessage.js index ce262e4e675..4ed882cfe87 100644 --- a/apps/meteor/app/lib/server/lib/sendNotificationsOnMessage.js +++ b/apps/meteor/app/lib/server/lib/sendNotificationsOnMessage.js @@ -48,12 +48,13 @@ export const sendNotification = async ({ subscription.receiver = [ await Users.findOneById(subscription.u._id, { projection: { - active: 1, - emails: 1, - language: 1, - status: 1, - statusConnection: 1, - username: 1, + 'active': 1, + 'emails': 1, + 'language': 1, + 'status': 1, + 'statusConnection': 1, + 'username': 1, + 'settings.preferences.enableMobileRinging': 1, }, }), ]; @@ -68,6 +69,7 @@ export const sendNotification = async ({ } const isThread = !!message.tmid && !message.tshow; + const isVideoConf = message.t === 'videoconf'; notificationMessage = await parseMessageTextPerUser(notificationMessage, message, receiver); @@ -112,6 +114,9 @@ export const sendNotification = async ({ hasReplyToThread, roomType, isThread, + isVideoConf, + userPreferences: receiver.settings?.preferences, + roomUids: room.uids, }) ) { queueItems.push({ @@ -189,6 +194,7 @@ const project = { 'receiver.status': 1, 'receiver.statusConnection': 1, 'receiver.username': 1, + 'receiver.settings.preferences.enableMobileRinging': 1, }, }; diff --git a/apps/meteor/server/lib/isRoomCompatibleWithVideoConfRinging.ts b/apps/meteor/server/lib/isRoomCompatibleWithVideoConfRinging.ts new file mode 100644 index 00000000000..3bd4f814b8d --- /dev/null +++ b/apps/meteor/server/lib/isRoomCompatibleWithVideoConfRinging.ts @@ -0,0 +1,4 @@ +import type { IRoom } from '@rocket.chat/core-typings'; + +export const isRoomCompatibleWithVideoConfRinging = (roomType: IRoom['t'], roomUids: IRoom['uids']): boolean => + Boolean(roomType === 'd' && roomUids && roomUids.length <= 2); diff --git a/apps/meteor/server/services/video-conference/service.ts b/apps/meteor/server/services/video-conference/service.ts index dd58d4b293f..f032606c2d8 100644 --- a/apps/meteor/server/services/video-conference/service.ts +++ b/apps/meteor/server/services/video-conference/service.ts @@ -47,6 +47,7 @@ import { callbacks } from '../../../lib/callbacks'; import { availabilityErrors } from '../../../lib/videoConference/constants'; import { readSecondaryPreferred } from '../../database/readSecondaryPreferred'; import { i18n } from '../../lib/i18n'; +import { isRoomCompatibleWithVideoConfRinging } from '../../lib/isRoomCompatibleWithVideoConfRinging'; import { videoConfProviders } from '../../lib/videoConfProviders'; import { videoConfTypes } from '../../lib/videoConfTypes'; @@ -74,7 +75,7 @@ export class VideoConfService extends ServiceClassInternal implements IVideoConf } if (type === 'direct') { - if (room.t !== 'd' || !room.uids || room.uids.length > 2) { + if (!isRoomCompatibleWithVideoConfRinging(room.t, room.uids)) { throw new Error('type-and-room-not-compatible'); } -- GitLab From f7086db493ae1e492a53da4721dc9750349cb2af Mon Sep 17 00:00:00 2001 From: rocketchat-github-ci Date: Tue, 26 Dec 2023 15:10:37 +0000 Subject: [PATCH 076/329] Release 6.6.0 [no ci] --- .changeset/bump-patch-1702298298384.md | 5 -- .changeset/fair-suns-study.md | 5 -- .changeset/fresh-radios-whisper.md | 5 -- .changeset/gold-stingrays-compete.md | 5 -- .changeset/lucky-apricots-change.md | 5 -- .changeset/lucky-cycles-leave.md | 5 -- .changeset/nasty-islands-trade.md | 6 --- .changeset/new-avocados-sort.md | 7 --- .changeset/popular-beds-heal.md | 7 --- .changeset/real-items-tan.md | 5 -- .changeset/remove-license-31189.md | 5 -- .changeset/resume-login-31301.md | 5 -- .changeset/save-token-31278.md | 5 -- .changeset/sharp-rings-smash.md | 6 --- .changeset/sour-kids-heal.md | 6 --- .changeset/spicy-kiwis-argue.md | 5 -- .changeset/spotty-suns-grin.md | 5 -- .changeset/tame-drinks-yell.md | 5 -- .changeset/violet-pears-cry.md | 5 -- apps/meteor/.docker/Dockerfile.rhel | 2 +- apps/meteor/CHANGELOG.md | 50 +++++++++++++++++++ apps/meteor/app/utils/rocketchat.info | 2 +- apps/meteor/ee/server/services/CHANGELOG.md | 12 +++++ apps/meteor/ee/server/services/package.json | 2 +- apps/meteor/package.json | 2 +- ee/apps/account-service/CHANGELOG.md | 12 +++++ ee/apps/account-service/package.json | 2 +- ee/apps/authorization-service/CHANGELOG.md | 12 +++++ ee/apps/authorization-service/package.json | 2 +- ee/apps/ddp-streamer/CHANGELOG.md | 14 ++++++ ee/apps/ddp-streamer/package.json | 2 +- ee/apps/omnichannel-transcript/CHANGELOG.md | 12 +++++ ee/apps/omnichannel-transcript/package.json | 2 +- ee/apps/presence-service/CHANGELOG.md | 11 ++++ ee/apps/presence-service/package.json | 2 +- ee/apps/queue-worker/CHANGELOG.md | 11 ++++ ee/apps/queue-worker/package.json | 2 +- ee/apps/stream-hub-service/CHANGELOG.md | 10 ++++ ee/apps/stream-hub-service/package.json | 2 +- ee/packages/api-client/CHANGELOG.md | 9 ++++ ee/packages/api-client/package.json | 2 +- ee/packages/ddp-client/CHANGELOG.md | 14 ++++++ ee/packages/ddp-client/package.json | 2 +- ee/packages/license/CHANGELOG.md | 7 +++ ee/packages/license/package.json | 2 +- ee/packages/omnichannel-services/CHANGELOG.md | 13 +++++ ee/packages/omnichannel-services/package.json | 2 +- ee/packages/pdf-worker/CHANGELOG.md | 7 +++ ee/packages/pdf-worker/package.json | 2 +- ee/packages/presence/CHANGELOG.md | 9 ++++ ee/packages/presence/package.json | 2 +- package.json | 2 +- packages/core-services/CHANGELOG.md | 10 ++++ packages/core-services/package.json | 2 +- packages/core-typings/CHANGELOG.md | 6 +++ packages/core-typings/package.json | 2 +- packages/cron/CHANGELOG.md | 8 +++ packages/cron/package.json | 2 +- packages/fuselage-ui-kit/CHANGELOG.md | 8 +++ packages/fuselage-ui-kit/package.json | 6 +-- packages/gazzodown/CHANGELOG.md | 9 ++++ packages/gazzodown/package.json | 8 +-- packages/instance-status/CHANGELOG.md | 6 +++ packages/instance-status/package.json | 2 +- packages/livechat/CHANGELOG.md | 6 +++ packages/livechat/package.json | 2 +- packages/model-typings/CHANGELOG.md | 7 +++ packages/model-typings/package.json | 2 +- packages/models/CHANGELOG.md | 6 +++ packages/models/package.json | 2 +- packages/rest-typings/CHANGELOG.md | 11 ++++ packages/rest-typings/package.json | 2 +- packages/ui-client/CHANGELOG.md | 6 +++ packages/ui-client/package.json | 4 +- packages/ui-contexts/CHANGELOG.md | 15 ++++++ packages/ui-contexts/package.json | 2 +- packages/ui-video-conf/CHANGELOG.md | 6 +++ packages/ui-video-conf/package.json | 4 +- packages/uikit-playground/CHANGELOG.md | 7 +++ packages/uikit-playground/package.json | 2 +- packages/web-ui-registration/CHANGELOG.md | 6 +++ packages/web-ui-registration/package.json | 4 +- 82 files changed, 361 insertions(+), 143 deletions(-) delete mode 100644 .changeset/bump-patch-1702298298384.md delete mode 100644 .changeset/fair-suns-study.md delete mode 100644 .changeset/fresh-radios-whisper.md delete mode 100644 .changeset/gold-stingrays-compete.md delete mode 100644 .changeset/lucky-apricots-change.md delete mode 100644 .changeset/lucky-cycles-leave.md delete mode 100644 .changeset/nasty-islands-trade.md delete mode 100644 .changeset/new-avocados-sort.md delete mode 100644 .changeset/popular-beds-heal.md delete mode 100644 .changeset/real-items-tan.md delete mode 100644 .changeset/remove-license-31189.md delete mode 100644 .changeset/resume-login-31301.md delete mode 100644 .changeset/save-token-31278.md delete mode 100644 .changeset/sharp-rings-smash.md delete mode 100644 .changeset/sour-kids-heal.md delete mode 100644 .changeset/spicy-kiwis-argue.md delete mode 100644 .changeset/spotty-suns-grin.md delete mode 100644 .changeset/tame-drinks-yell.md delete mode 100644 .changeset/violet-pears-cry.md diff --git a/.changeset/bump-patch-1702298298384.md b/.changeset/bump-patch-1702298298384.md deleted file mode 100644 index e1eaa7980af..00000000000 --- a/.changeset/bump-patch-1702298298384.md +++ /dev/null @@ -1,5 +0,0 @@ ---- -'@rocket.chat/meteor': patch ---- - -Bump @rocket.chat/meteor version. diff --git a/.changeset/fair-suns-study.md b/.changeset/fair-suns-study.md deleted file mode 100644 index 64dcc2ec7ee..00000000000 --- a/.changeset/fair-suns-study.md +++ /dev/null @@ -1,5 +0,0 @@ ---- -'@rocket.chat/meteor': patch ---- - -Security improvements diff --git a/.changeset/fresh-radios-whisper.md b/.changeset/fresh-radios-whisper.md deleted file mode 100644 index cba234524dc..00000000000 --- a/.changeset/fresh-radios-whisper.md +++ /dev/null @@ -1,5 +0,0 @@ ---- -"@rocket.chat/meteor": patch ---- - -Fixed issue with the new `custom-roles` license module not being checked throughout the application diff --git a/.changeset/gold-stingrays-compete.md b/.changeset/gold-stingrays-compete.md deleted file mode 100644 index 8cc6c3aa6e8..00000000000 --- a/.changeset/gold-stingrays-compete.md +++ /dev/null @@ -1,5 +0,0 @@ ---- -"@rocket.chat/meteor": patch ---- - -fix: stop refetching banner data each 5 minutes diff --git a/.changeset/lucky-apricots-change.md b/.changeset/lucky-apricots-change.md deleted file mode 100644 index 12d93b4fb65..00000000000 --- a/.changeset/lucky-apricots-change.md +++ /dev/null @@ -1,5 +0,0 @@ ---- -"@rocket.chat/meteor": patch ---- - -Fixed an issue allowing admin user cancelling subscription when license's trial param is provided diff --git a/.changeset/lucky-cycles-leave.md b/.changeset/lucky-cycles-leave.md deleted file mode 100644 index 608db68031b..00000000000 --- a/.changeset/lucky-cycles-leave.md +++ /dev/null @@ -1,5 +0,0 @@ ---- -"@rocket.chat/meteor": patch ---- - -Fixed Country select component at Organization form from `onboarding-ui` package diff --git a/.changeset/nasty-islands-trade.md b/.changeset/nasty-islands-trade.md deleted file mode 100644 index b6df94282dd..00000000000 --- a/.changeset/nasty-islands-trade.md +++ /dev/null @@ -1,6 +0,0 @@ ---- -'@rocket.chat/rest-typings': minor -'@rocket.chat/meteor': minor ---- - -fix Federation Regression, builds service correctly diff --git a/.changeset/new-avocados-sort.md b/.changeset/new-avocados-sort.md deleted file mode 100644 index 1f8b90ed008..00000000000 --- a/.changeset/new-avocados-sort.md +++ /dev/null @@ -1,7 +0,0 @@ ---- -"@rocket.chat/meteor": patch ---- - -fix: Wrong `Message Roundtrip Time` metric - -Removes the wrong metric gauge named `rocketchat_messages_roundtrip_time` and replace it by a new summary metric named `rocketchat_messages_roundtrip_time_summary`. Add new percentiles `0.5, 0.95 and 1` to all summary metrics. diff --git a/.changeset/popular-beds-heal.md b/.changeset/popular-beds-heal.md deleted file mode 100644 index 03f4dd735d8..00000000000 --- a/.changeset/popular-beds-heal.md +++ /dev/null @@ -1,7 +0,0 @@ ---- -'@rocket.chat/ddp-client': patch -'@rocket.chat/core-typings': patch -'@rocket.chat/meteor': patch ---- - -Exceeding API calls when sending OTR messages diff --git a/.changeset/real-items-tan.md b/.changeset/real-items-tan.md deleted file mode 100644 index 302ac3eacfe..00000000000 --- a/.changeset/real-items-tan.md +++ /dev/null @@ -1,5 +0,0 @@ ---- -"@rocket.chat/ddp-client": patch ---- - -SDK login methods not saving token diff --git a/.changeset/remove-license-31189.md b/.changeset/remove-license-31189.md deleted file mode 100644 index e510f263feb..00000000000 --- a/.changeset/remove-license-31189.md +++ /dev/null @@ -1,5 +0,0 @@ ---- -'@rocket.chat/ddp-client': patch ---- - -removed @rocket.chat/license as a dependency of ddp client diff --git a/.changeset/resume-login-31301.md b/.changeset/resume-login-31301.md deleted file mode 100644 index 2ec78cd6c31..00000000000 --- a/.changeset/resume-login-31301.md +++ /dev/null @@ -1,5 +0,0 @@ ---- -'@rocket.chat/ddp-client': patch ---- - -fixed an issue with the ddp client reconnection not resuming the login diff --git a/.changeset/save-token-31278.md b/.changeset/save-token-31278.md deleted file mode 100644 index 49f22037f4c..00000000000 --- a/.changeset/save-token-31278.md +++ /dev/null @@ -1,5 +0,0 @@ ---- -'@rocket.chat/ddp-client': patch ---- - -fixed an issue with the ddp client account not saving credentials correctly diff --git a/.changeset/sharp-rings-smash.md b/.changeset/sharp-rings-smash.md deleted file mode 100644 index 0e4360ff46d..00000000000 --- a/.changeset/sharp-rings-smash.md +++ /dev/null @@ -1,6 +0,0 @@ ---- -"@rocket.chat/meteor": patch ---- - -Fixed a problem with the subscription creation on Omnichannel rooms. -Rooms were being created as seen, causing sound notifications to not work diff --git a/.changeset/sour-kids-heal.md b/.changeset/sour-kids-heal.md deleted file mode 100644 index 9dbeff968bb..00000000000 --- a/.changeset/sour-kids-heal.md +++ /dev/null @@ -1,6 +0,0 @@ ---- -"@rocket.chat/meteor": patch -"@rocket.chat/ddp-client": patch ---- - -Fixed a problem where chained callbacks' return value was being overrided by some callbacks returning something different, causing callbacks with lower priority to operate on invalid values diff --git a/.changeset/spicy-kiwis-argue.md b/.changeset/spicy-kiwis-argue.md deleted file mode 100644 index 520fdfe2201..00000000000 --- a/.changeset/spicy-kiwis-argue.md +++ /dev/null @@ -1,5 +0,0 @@ ---- -'@rocket.chat/meteor': patch ---- - -Fix desktop notification routing for direct rooms diff --git a/.changeset/spotty-suns-grin.md b/.changeset/spotty-suns-grin.md deleted file mode 100644 index 8045ebbd1b4..00000000000 --- a/.changeset/spotty-suns-grin.md +++ /dev/null @@ -1,5 +0,0 @@ ---- -'@rocket.chat/meteor': patch ---- - -Improved the experience of receiving conference calls on the mobile app by disabling the push notification for the "new call" message if a push is already being sent to trigger the phone's ringing tone. diff --git a/.changeset/tame-drinks-yell.md b/.changeset/tame-drinks-yell.md deleted file mode 100644 index 6cb19babccf..00000000000 --- a/.changeset/tame-drinks-yell.md +++ /dev/null @@ -1,5 +0,0 @@ ---- -"@rocket.chat/meteor": patch ---- - -Fixed verify the account through email link diff --git a/.changeset/violet-pears-cry.md b/.changeset/violet-pears-cry.md deleted file mode 100644 index 8e10ad50dd5..00000000000 --- a/.changeset/violet-pears-cry.md +++ /dev/null @@ -1,5 +0,0 @@ ---- -'@rocket.chat/meteor': patch ---- - -Fixed the filter for file type in the list of room files diff --git a/apps/meteor/.docker/Dockerfile.rhel b/apps/meteor/.docker/Dockerfile.rhel index c97c3dfc370..84cf1f409b5 100644 --- a/apps/meteor/.docker/Dockerfile.rhel +++ b/apps/meteor/.docker/Dockerfile.rhel @@ -1,6 +1,6 @@ FROM registry.access.redhat.com/ubi8/nodejs-12 -ENV RC_VERSION 6.5.0 +ENV RC_VERSION 6.6.0 MAINTAINER buildmaster@rocket.chat diff --git a/apps/meteor/CHANGELOG.md b/apps/meteor/CHANGELOG.md index 874fc20d33b..82395720492 100644 --- a/apps/meteor/CHANGELOG.md +++ b/apps/meteor/CHANGELOG.md @@ -1,5 +1,55 @@ # @rocket.chat/meteor +## 6.6.0 + +### Minor Changes + +- 4b0fa27e53: fix Federation Regression, builds service correctly + +### Patch Changes + +- 24e9dc2633: Bump @rocket.chat/meteor version. +- Bump @rocket.chat/meteor version. +- 2c577efc5f: Security improvements +- 055cb0fa3e: Fixed issue with the new `custom-roles` license module not being checked throughout the application +- 68ff7e5c5b: fix: stop refetching banner data each 5 minutes +- 077d04f0a8: Fixed an issue allowing admin user cancelling subscription when license's trial param is provided +- dc111e2e84: Fixed Country select component at Organization form from `onboarding-ui` package +- 816dd50548: fix: Wrong `Message Roundtrip Time` metric + + Removes the wrong metric gauge named `rocketchat_messages_roundtrip_time` and replace it by a new summary metric named `rocketchat_messages_roundtrip_time_summary`. Add new percentiles `0.5, 0.95 and 1` to all summary metrics. + +- 46fa2ff2cf: Exceeding API calls when sending OTR messages +- d6a8360564: Fixed a problem with the subscription creation on Omnichannel rooms. + Rooms were being created as seen, causing sound notifications to not work +- b42aa49fbd: Fixed a problem where chained callbacks' return value was being overrided by some callbacks returning something different, causing callbacks with lower priority to operate on invalid values +- f2699e4988: Fix desktop notification routing for direct rooms +- 4e49fcc7d9: Improved the experience of receiving conference calls on the mobile app by disabling the push notification for the "new call" message if a push is already being sent to trigger the phone's ringing tone. +- 6ba8bcc2ca: Fixed verify the account through email link +- 4b2bdd5508: Fixed the filter for file type in the list of room files +- Updated dependencies [4b0fa27e53] +- Updated dependencies [46fa2ff2cf] + - @rocket.chat/rest-typings@6.6.0 + - @rocket.chat/core-typings@6.6.0 + - @rocket.chat/api-client@0.1.19 + - @rocket.chat/omnichannel-services@0.1.1 + - @rocket.chat/presence@0.1.1 + - @rocket.chat/core-services@0.3.1 + - @rocket.chat/ui-contexts@4.0.0 + - @rocket.chat/license@0.1.1 + - @rocket.chat/pdf-worker@0.0.25 + - @rocket.chat/cron@0.0.21 + - @rocket.chat/gazzodown@4.0.0 + - @rocket.chat/model-typings@0.2.1 + - @rocket.chat/ui-theming@0.1.1 + - @rocket.chat/fuselage-ui-kit@4.0.0 + - @rocket.chat/ui-client@4.0.0 + - @rocket.chat/ui-video-conf@4.0.0 + - @rocket.chat/web-ui-registration@4.0.0 + - @rocket.chat/server-cloud-communication@0.0.1 + - @rocket.chat/models@0.0.25 + - @rocket.chat/instance-status@0.0.25 + ## 6.5.0 ### Minor Changes diff --git a/apps/meteor/app/utils/rocketchat.info b/apps/meteor/app/utils/rocketchat.info index 2cd7972686e..378fba14ca4 100644 --- a/apps/meteor/app/utils/rocketchat.info +++ b/apps/meteor/app/utils/rocketchat.info @@ -1,3 +1,3 @@ { - "version": "6.5.0" + "version": "6.6.0" } diff --git a/apps/meteor/ee/server/services/CHANGELOG.md b/apps/meteor/ee/server/services/CHANGELOG.md index 07a89f2d723..281d34fabf6 100644 --- a/apps/meteor/ee/server/services/CHANGELOG.md +++ b/apps/meteor/ee/server/services/CHANGELOG.md @@ -1,5 +1,17 @@ # rocketchat-services +## 1.1.19 + +### Patch Changes + +- Updated dependencies [4b0fa27e53] +- Updated dependencies [46fa2ff2cf] + - @rocket.chat/rest-typings@6.6.0 + - @rocket.chat/core-typings@6.6.0 + - @rocket.chat/core-services@0.3.1 + - @rocket.chat/model-typings@0.2.1 + - @rocket.chat/models@0.0.25 + ## 1.1.18 ### Patch Changes diff --git a/apps/meteor/ee/server/services/package.json b/apps/meteor/ee/server/services/package.json index 468e444d56f..c44ad6cf584 100644 --- a/apps/meteor/ee/server/services/package.json +++ b/apps/meteor/ee/server/services/package.json @@ -1,7 +1,7 @@ { "name": "rocketchat-services", "private": true, - "version": "1.1.18", + "version": "1.1.19", "description": "Rocket.Chat Authorization service", "main": "index.js", "scripts": { diff --git a/apps/meteor/package.json b/apps/meteor/package.json index 6f2e9628339..8e43d9394e9 100644 --- a/apps/meteor/package.json +++ b/apps/meteor/package.json @@ -1,7 +1,7 @@ { "name": "@rocket.chat/meteor", "description": "The Ultimate Open Source WebChat Platform", - "version": "6.5.0", + "version": "6.6.0", "private": true, "author": { "name": "Rocket.Chat", diff --git a/ee/apps/account-service/CHANGELOG.md b/ee/apps/account-service/CHANGELOG.md index 0cc9b7bc148..90936d44891 100644 --- a/ee/apps/account-service/CHANGELOG.md +++ b/ee/apps/account-service/CHANGELOG.md @@ -1,5 +1,17 @@ # @rocket.chat/account-service +## 0.3.1 + +### Patch Changes + +- Updated dependencies [4b0fa27e53] +- Updated dependencies [46fa2ff2cf] + - @rocket.chat/rest-typings@6.6.0 + - @rocket.chat/core-typings@6.6.0 + - @rocket.chat/core-services@0.3.1 + - @rocket.chat/model-typings@0.2.1 + - @rocket.chat/models@0.0.25 + ## 0.3.0 ### Minor Changes diff --git a/ee/apps/account-service/package.json b/ee/apps/account-service/package.json index 33e668e52e5..b40c269b172 100644 --- a/ee/apps/account-service/package.json +++ b/ee/apps/account-service/package.json @@ -1,7 +1,7 @@ { "name": "@rocket.chat/account-service", "private": true, - "version": "0.3.0", + "version": "0.3.1", "description": "Rocket.Chat Account service", "scripts": { "build": "tsc -p tsconfig.json", diff --git a/ee/apps/authorization-service/CHANGELOG.md b/ee/apps/authorization-service/CHANGELOG.md index 90cad488c77..9264ecfb205 100644 --- a/ee/apps/authorization-service/CHANGELOG.md +++ b/ee/apps/authorization-service/CHANGELOG.md @@ -1,5 +1,17 @@ # @rocket.chat/authorization-service +## 0.3.1 + +### Patch Changes + +- Updated dependencies [4b0fa27e53] +- Updated dependencies [46fa2ff2cf] + - @rocket.chat/rest-typings@6.6.0 + - @rocket.chat/core-typings@6.6.0 + - @rocket.chat/core-services@0.3.1 + - @rocket.chat/model-typings@0.2.1 + - @rocket.chat/models@0.0.25 + ## 0.3.0 ### Minor Changes diff --git a/ee/apps/authorization-service/package.json b/ee/apps/authorization-service/package.json index 5ec688afa7a..6d6797fab89 100644 --- a/ee/apps/authorization-service/package.json +++ b/ee/apps/authorization-service/package.json @@ -1,7 +1,7 @@ { "name": "@rocket.chat/authorization-service", "private": true, - "version": "0.3.0", + "version": "0.3.1", "description": "Rocket.Chat Authorization service", "scripts": { "build": "tsc -p tsconfig.json", diff --git a/ee/apps/ddp-streamer/CHANGELOG.md b/ee/apps/ddp-streamer/CHANGELOG.md index 2790871dc26..a2023d9b9f9 100644 --- a/ee/apps/ddp-streamer/CHANGELOG.md +++ b/ee/apps/ddp-streamer/CHANGELOG.md @@ -1,5 +1,19 @@ # @rocket.chat/ddp-streamer +## 0.2.1 + +### Patch Changes + +- Updated dependencies [4b0fa27e53] +- Updated dependencies [46fa2ff2cf] + - @rocket.chat/rest-typings@6.6.0 + - @rocket.chat/core-typings@6.6.0 + - @rocket.chat/core-services@0.3.1 + - @rocket.chat/ui-contexts@4.0.0 + - @rocket.chat/model-typings@0.2.1 + - @rocket.chat/models@0.0.25 + - @rocket.chat/instance-status@0.0.25 + ## 0.2.0 ### Minor Changes diff --git a/ee/apps/ddp-streamer/package.json b/ee/apps/ddp-streamer/package.json index f5641474c55..fa36b8fb0ea 100644 --- a/ee/apps/ddp-streamer/package.json +++ b/ee/apps/ddp-streamer/package.json @@ -1,7 +1,7 @@ { "name": "@rocket.chat/ddp-streamer", "private": true, - "version": "0.2.0", + "version": "0.2.1", "description": "Rocket.Chat DDP-Streamer service", "scripts": { "build": "tsc -p tsconfig.json", diff --git a/ee/apps/omnichannel-transcript/CHANGELOG.md b/ee/apps/omnichannel-transcript/CHANGELOG.md index d7ad0294e40..b1db814549e 100644 --- a/ee/apps/omnichannel-transcript/CHANGELOG.md +++ b/ee/apps/omnichannel-transcript/CHANGELOG.md @@ -1,5 +1,17 @@ # @rocket.chat/omnichannel-transcript +## 0.3.1 + +### Patch Changes + +- Updated dependencies [46fa2ff2cf] + - @rocket.chat/core-typings@6.6.0 + - @rocket.chat/omnichannel-services@0.1.1 + - @rocket.chat/core-services@0.3.1 + - @rocket.chat/pdf-worker@0.0.25 + - @rocket.chat/model-typings@0.2.1 + - @rocket.chat/models@0.0.25 + ## 0.3.0 ### Minor Changes diff --git a/ee/apps/omnichannel-transcript/package.json b/ee/apps/omnichannel-transcript/package.json index b30ef724a49..421eebe9fbf 100644 --- a/ee/apps/omnichannel-transcript/package.json +++ b/ee/apps/omnichannel-transcript/package.json @@ -1,7 +1,7 @@ { "name": "@rocket.chat/omnichannel-transcript", "private": true, - "version": "0.3.0", + "version": "0.3.1", "description": "Rocket.Chat service", "scripts": { "build": "tsc -p tsconfig.json", diff --git a/ee/apps/presence-service/CHANGELOG.md b/ee/apps/presence-service/CHANGELOG.md index d8f7c18a73d..850e9c8252b 100644 --- a/ee/apps/presence-service/CHANGELOG.md +++ b/ee/apps/presence-service/CHANGELOG.md @@ -1,5 +1,16 @@ # @rocket.chat/presence-service +## 0.3.1 + +### Patch Changes + +- Updated dependencies [46fa2ff2cf] + - @rocket.chat/core-typings@6.6.0 + - @rocket.chat/presence@0.1.1 + - @rocket.chat/core-services@0.3.1 + - @rocket.chat/model-typings@0.2.1 + - @rocket.chat/models@0.0.25 + ## 0.3.0 ### Minor Changes diff --git a/ee/apps/presence-service/package.json b/ee/apps/presence-service/package.json index 3d3490d169a..b0001eb9c89 100644 --- a/ee/apps/presence-service/package.json +++ b/ee/apps/presence-service/package.json @@ -1,7 +1,7 @@ { "name": "@rocket.chat/presence-service", "private": true, - "version": "0.3.0", + "version": "0.3.1", "description": "Rocket.Chat Presence service", "scripts": { "build": "tsc -p tsconfig.json", diff --git a/ee/apps/queue-worker/CHANGELOG.md b/ee/apps/queue-worker/CHANGELOG.md index ae9269affed..f2c03035ff3 100644 --- a/ee/apps/queue-worker/CHANGELOG.md +++ b/ee/apps/queue-worker/CHANGELOG.md @@ -1,5 +1,16 @@ # @rocket.chat/queue-worker +## 0.3.1 + +### Patch Changes + +- Updated dependencies [46fa2ff2cf] + - @rocket.chat/core-typings@6.6.0 + - @rocket.chat/omnichannel-services@0.1.1 + - @rocket.chat/core-services@0.3.1 + - @rocket.chat/model-typings@0.2.1 + - @rocket.chat/models@0.0.25 + ## 0.3.0 ### Minor Changes diff --git a/ee/apps/queue-worker/package.json b/ee/apps/queue-worker/package.json index 0e26ceea7b2..25d536e8d11 100644 --- a/ee/apps/queue-worker/package.json +++ b/ee/apps/queue-worker/package.json @@ -1,7 +1,7 @@ { "name": "@rocket.chat/queue-worker", "private": true, - "version": "0.3.0", + "version": "0.3.1", "description": "Rocket.Chat service", "scripts": { "build": "tsc -p tsconfig.json", diff --git a/ee/apps/stream-hub-service/CHANGELOG.md b/ee/apps/stream-hub-service/CHANGELOG.md index 6bfff89143b..4519137539e 100644 --- a/ee/apps/stream-hub-service/CHANGELOG.md +++ b/ee/apps/stream-hub-service/CHANGELOG.md @@ -1,5 +1,15 @@ # @rocket.chat/stream-hub-service +## 0.3.1 + +### Patch Changes + +- Updated dependencies [46fa2ff2cf] + - @rocket.chat/core-typings@6.6.0 + - @rocket.chat/core-services@0.3.1 + - @rocket.chat/model-typings@0.2.1 + - @rocket.chat/models@0.0.25 + ## 0.3.0 ### Minor Changes diff --git a/ee/apps/stream-hub-service/package.json b/ee/apps/stream-hub-service/package.json index 04cb745254b..3e39bde7f50 100644 --- a/ee/apps/stream-hub-service/package.json +++ b/ee/apps/stream-hub-service/package.json @@ -1,7 +1,7 @@ { "name": "@rocket.chat/stream-hub-service", "private": true, - "version": "0.3.0", + "version": "0.3.1", "description": "Rocket.Chat Stream Hub service", "scripts": { "build": "tsc -p tsconfig.json", diff --git a/ee/packages/api-client/CHANGELOG.md b/ee/packages/api-client/CHANGELOG.md index 1e42f8d3fbf..6137628abfb 100644 --- a/ee/packages/api-client/CHANGELOG.md +++ b/ee/packages/api-client/CHANGELOG.md @@ -1,5 +1,14 @@ # @rocket.chat/api-client +## 0.1.19 + +### Patch Changes + +- Updated dependencies [4b0fa27e53] +- Updated dependencies [46fa2ff2cf] + - @rocket.chat/rest-typings@6.6.0 + - @rocket.chat/core-typings@6.6.0 + ## 0.1.18 ### Patch Changes diff --git a/ee/packages/api-client/package.json b/ee/packages/api-client/package.json index 5530e42393e..6010be23c26 100644 --- a/ee/packages/api-client/package.json +++ b/ee/packages/api-client/package.json @@ -1,6 +1,6 @@ { "name": "@rocket.chat/api-client", - "version": "0.1.18", + "version": "0.1.19", "devDependencies": { "@swc/core": "^1.3.95", "@swc/jest": "^0.2.29", diff --git a/ee/packages/ddp-client/CHANGELOG.md b/ee/packages/ddp-client/CHANGELOG.md index 2f84d3e0764..ce3374bfa38 100644 --- a/ee/packages/ddp-client/CHANGELOG.md +++ b/ee/packages/ddp-client/CHANGELOG.md @@ -1,5 +1,19 @@ # @rocket.chat/ddp-client +## 0.2.10 + +### Patch Changes + +- 46fa2ff2cf: Exceeding API calls when sending OTR messages +- f6a525a444: SDK login methods not saving token +- 1233bffa8c: removed @rocket.chat/license as a dependency of ddp client +- 1233bffa8c: fixed an issue with the ddp client reconnection not resuming the login +- 1233bffa8c: fixed an issue with the ddp client account not saving credentials correctly +- b42aa49fbd: Fixed a problem where chained callbacks' return value was being overrided by some callbacks returning something different, causing callbacks with lower priority to operate on invalid values +- Updated dependencies [4b0fa27e53] + - @rocket.chat/rest-typings@6.6.0 + - @rocket.chat/api-client@0.1.19 + ## 0.2.9 ### Patch Changes diff --git a/ee/packages/ddp-client/package.json b/ee/packages/ddp-client/package.json index 680d970743c..375aa7d40c2 100644 --- a/ee/packages/ddp-client/package.json +++ b/ee/packages/ddp-client/package.json @@ -1,6 +1,6 @@ { "name": "@rocket.chat/ddp-client", - "version": "0.2.9", + "version": "0.2.10", "devDependencies": { "@swc/core": "^1.3.95", "@swc/jest": "^0.2.29", diff --git a/ee/packages/license/CHANGELOG.md b/ee/packages/license/CHANGELOG.md index bb7835e90d1..ec8bb0b9cc2 100644 --- a/ee/packages/license/CHANGELOG.md +++ b/ee/packages/license/CHANGELOG.md @@ -1,5 +1,12 @@ # @rocket.chat/license +## 0.1.1 + +### Patch Changes + +- Updated dependencies [46fa2ff2cf] + - @rocket.chat/core-typings@6.6.0 + ## 0.1.0 ### Minor Changes diff --git a/ee/packages/license/package.json b/ee/packages/license/package.json index e2d253f7d11..890a8c15642 100644 --- a/ee/packages/license/package.json +++ b/ee/packages/license/package.json @@ -1,6 +1,6 @@ { "name": "@rocket.chat/license", - "version": "0.1.0", + "version": "0.1.1", "private": true, "devDependencies": { "@swc/core": "^1.3.95", diff --git a/ee/packages/omnichannel-services/CHANGELOG.md b/ee/packages/omnichannel-services/CHANGELOG.md index 0e1170004d2..67fea3a375b 100644 --- a/ee/packages/omnichannel-services/CHANGELOG.md +++ b/ee/packages/omnichannel-services/CHANGELOG.md @@ -1,5 +1,18 @@ # @rocket.chat/omnichannel-services +## 0.1.1 + +### Patch Changes + +- Updated dependencies [4b0fa27e53] +- Updated dependencies [46fa2ff2cf] + - @rocket.chat/rest-typings@6.6.0 + - @rocket.chat/core-typings@6.6.0 + - @rocket.chat/core-services@0.3.1 + - @rocket.chat/pdf-worker@0.0.25 + - @rocket.chat/model-typings@0.2.1 + - @rocket.chat/models@0.0.25 + ## 0.1.0 ### Minor Changes diff --git a/ee/packages/omnichannel-services/package.json b/ee/packages/omnichannel-services/package.json index 8fb37352ed8..be89ade5855 100644 --- a/ee/packages/omnichannel-services/package.json +++ b/ee/packages/omnichannel-services/package.json @@ -1,6 +1,6 @@ { "name": "@rocket.chat/omnichannel-services", - "version": "0.1.0", + "version": "0.1.1", "private": true, "devDependencies": { "@rocket.chat/eslint-config": "workspace:^", diff --git a/ee/packages/pdf-worker/CHANGELOG.md b/ee/packages/pdf-worker/CHANGELOG.md index b64c1ce32bd..9b93a6fc28a 100644 --- a/ee/packages/pdf-worker/CHANGELOG.md +++ b/ee/packages/pdf-worker/CHANGELOG.md @@ -1,5 +1,12 @@ # @rocket.chat/pdf-worker +## 0.0.25 + +### Patch Changes + +- Updated dependencies [46fa2ff2cf] + - @rocket.chat/core-typings@6.6.0 + ## 0.0.24 ### Patch Changes diff --git a/ee/packages/pdf-worker/package.json b/ee/packages/pdf-worker/package.json index 5c151bdc150..dd1dc2b3b0f 100644 --- a/ee/packages/pdf-worker/package.json +++ b/ee/packages/pdf-worker/package.json @@ -1,6 +1,6 @@ { "name": "@rocket.chat/pdf-worker", - "version": "0.0.24", + "version": "0.0.25", "private": true, "devDependencies": { "@storybook/addon-essentials": "~6.5.16", diff --git a/ee/packages/presence/CHANGELOG.md b/ee/packages/presence/CHANGELOG.md index 29759422a23..2fa9873957f 100644 --- a/ee/packages/presence/CHANGELOG.md +++ b/ee/packages/presence/CHANGELOG.md @@ -1,5 +1,14 @@ # @rocket.chat/presence +## 0.1.1 + +### Patch Changes + +- Updated dependencies [46fa2ff2cf] + - @rocket.chat/core-typings@6.6.0 + - @rocket.chat/core-services@0.3.1 + - @rocket.chat/models@0.0.25 + ## 0.1.0 ### Minor Changes diff --git a/ee/packages/presence/package.json b/ee/packages/presence/package.json index 0c3d7d3bde3..cb8eab27052 100644 --- a/ee/packages/presence/package.json +++ b/ee/packages/presence/package.json @@ -1,6 +1,6 @@ { "name": "@rocket.chat/presence", - "version": "0.1.0", + "version": "0.1.1", "private": true, "devDependencies": { "@babel/core": "~7.22.20", diff --git a/package.json b/package.json index dc4244dfd6f..c5b5dea5122 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "rocket.chat", - "version": "6.5.0", + "version": "6.6.0", "description": "Rocket.Chat Monorepo", "main": "index.js", "private": true, diff --git a/packages/core-services/CHANGELOG.md b/packages/core-services/CHANGELOG.md index fbb68e3b6a0..06a627d64d6 100644 --- a/packages/core-services/CHANGELOG.md +++ b/packages/core-services/CHANGELOG.md @@ -1,5 +1,15 @@ # @rocket.chat/core-services +## 0.3.1 + +### Patch Changes + +- Updated dependencies [4b0fa27e53] +- Updated dependencies [46fa2ff2cf] + - @rocket.chat/rest-typings@6.6.0 + - @rocket.chat/core-typings@6.6.0 + - @rocket.chat/models@0.0.25 + ## 0.3.0 ### Minor Changes diff --git a/packages/core-services/package.json b/packages/core-services/package.json index 533deb715e2..db298d2c1a5 100644 --- a/packages/core-services/package.json +++ b/packages/core-services/package.json @@ -1,6 +1,6 @@ { "name": "@rocket.chat/core-services", - "version": "0.3.0", + "version": "0.3.1", "private": true, "devDependencies": { "@babel/core": "~7.22.20", diff --git a/packages/core-typings/CHANGELOG.md b/packages/core-typings/CHANGELOG.md index 8229232f747..832d3339c69 100644 --- a/packages/core-typings/CHANGELOG.md +++ b/packages/core-typings/CHANGELOG.md @@ -1,5 +1,11 @@ # @rocket.chat/core-typings +## 6.6.0 + +### Patch Changes + +- 46fa2ff2cf: Exceeding API calls when sending OTR messages + ## 6.5.0 ### Minor Changes diff --git a/packages/core-typings/package.json b/packages/core-typings/package.json index 3eec6d26088..1a3ae6b97c7 100644 --- a/packages/core-typings/package.json +++ b/packages/core-typings/package.json @@ -1,7 +1,7 @@ { "$schema": "https://json.schemastore.org/package", "name": "@rocket.chat/core-typings", - "version": "6.5.0", + "version": "6.6.0", "devDependencies": { "@rocket.chat/eslint-config": "workspace:^", "eslint": "~8.45.0", diff --git a/packages/cron/CHANGELOG.md b/packages/cron/CHANGELOG.md index 02a65ed1146..c78c3b2182d 100644 --- a/packages/cron/CHANGELOG.md +++ b/packages/cron/CHANGELOG.md @@ -1,5 +1,13 @@ # @rocket.chat/cron +## 0.0.21 + +### Patch Changes + +- Updated dependencies [46fa2ff2cf] + - @rocket.chat/core-typings@6.6.0 + - @rocket.chat/models@0.0.25 + ## 0.0.20 ### Patch Changes diff --git a/packages/cron/package.json b/packages/cron/package.json index 2797c3cb34b..dcaae8a7ae8 100644 --- a/packages/cron/package.json +++ b/packages/cron/package.json @@ -1,6 +1,6 @@ { "name": "@rocket.chat/cron", - "version": "0.0.20", + "version": "0.0.21", "private": true, "devDependencies": { "@types/jest": "~29.5.7", diff --git a/packages/fuselage-ui-kit/CHANGELOG.md b/packages/fuselage-ui-kit/CHANGELOG.md index 99cdbb4bd79..f4cdb41f153 100644 --- a/packages/fuselage-ui-kit/CHANGELOG.md +++ b/packages/fuselage-ui-kit/CHANGELOG.md @@ -1,5 +1,13 @@ # Change Log +## 4.0.0 + +### Patch Changes + +- @rocket.chat/ui-contexts@4.0.0 +- @rocket.chat/gazzodown@4.0.0 +- @rocket.chat/ui-video-conf@4.0.0 + ## 3.0.0 ### Patch Changes diff --git a/packages/fuselage-ui-kit/package.json b/packages/fuselage-ui-kit/package.json index cc1a105e350..d5534df8fba 100644 --- a/packages/fuselage-ui-kit/package.json +++ b/packages/fuselage-ui-kit/package.json @@ -1,7 +1,7 @@ { "name": "@rocket.chat/fuselage-ui-kit", "private": true, - "version": "3.0.0", + "version": "4.0.0", "description": "UiKit elements for Rocket.Chat Apps built under Fuselage design system", "homepage": "https://rocketchat.github.io/Rocket.Chat.Fuselage/", "author": { @@ -48,9 +48,9 @@ "@rocket.chat/icons": "*", "@rocket.chat/prettier-config": "*", "@rocket.chat/styled": "*", - "@rocket.chat/ui-contexts": "3.0.0", + "@rocket.chat/ui-contexts": "4.0.0", "@rocket.chat/ui-kit": "*", - "@rocket.chat/ui-video-conf": "3.0.0", + "@rocket.chat/ui-video-conf": "4.0.0", "@tanstack/react-query": "*", "react": "*", "react-dom": "*" diff --git a/packages/gazzodown/CHANGELOG.md b/packages/gazzodown/CHANGELOG.md index aa6e24f891d..eeb63aa997a 100644 --- a/packages/gazzodown/CHANGELOG.md +++ b/packages/gazzodown/CHANGELOG.md @@ -1,5 +1,14 @@ # @rocket.chat/gazzodown +## 4.0.0 + +### Patch Changes + +- Updated dependencies [46fa2ff2cf] + - @rocket.chat/core-typings@6.6.0 + - @rocket.chat/ui-contexts@4.0.0 + - @rocket.chat/ui-client@4.0.0 + ## 3.0.0 ### Patch Changes diff --git a/packages/gazzodown/package.json b/packages/gazzodown/package.json index faa009c231d..c5d203171df 100644 --- a/packages/gazzodown/package.json +++ b/packages/gazzodown/package.json @@ -1,6 +1,6 @@ { "name": "@rocket.chat/gazzodown", - "version": "3.0.0", + "version": "4.0.0", "private": true, "devDependencies": { "@babel/core": "~7.22.20", @@ -65,14 +65,14 @@ "/dist" ], "peerDependencies": { - "@rocket.chat/core-typings": "6.5.0", + "@rocket.chat/core-typings": "6.6.0", "@rocket.chat/css-in-js": "*", "@rocket.chat/fuselage": "*", "@rocket.chat/fuselage-tokens": "*", "@rocket.chat/message-parser": "*", "@rocket.chat/styled": "*", - "@rocket.chat/ui-client": "3.0.0", - "@rocket.chat/ui-contexts": "3.0.0", + "@rocket.chat/ui-client": "4.0.0", + "@rocket.chat/ui-contexts": "4.0.0", "katex": "*", "react": "*" }, diff --git a/packages/instance-status/CHANGELOG.md b/packages/instance-status/CHANGELOG.md index 3bb16663fe3..803d8b0ccd9 100644 --- a/packages/instance-status/CHANGELOG.md +++ b/packages/instance-status/CHANGELOG.md @@ -1,5 +1,11 @@ # @rocket.chat/instance-status +## 0.0.25 + +### Patch Changes + +- @rocket.chat/models@0.0.25 + ## 0.0.24 ### Patch Changes diff --git a/packages/instance-status/package.json b/packages/instance-status/package.json index 5072f71164e..34a98562d49 100644 --- a/packages/instance-status/package.json +++ b/packages/instance-status/package.json @@ -1,6 +1,6 @@ { "name": "@rocket.chat/instance-status", - "version": "0.0.24", + "version": "0.0.25", "private": true, "devDependencies": { "@rocket.chat/eslint-config": "workspace:^", diff --git a/packages/livechat/CHANGELOG.md b/packages/livechat/CHANGELOG.md index fa45e1d0b9d..1f190238139 100644 --- a/packages/livechat/CHANGELOG.md +++ b/packages/livechat/CHANGELOG.md @@ -1,5 +1,11 @@ # @rocket.chat/livechat Change Log +## 1.14.10 + +### Patch Changes + +- @rocket.chat/gazzodown@4.0.0 + ## 1.14.9 ### Patch Changes diff --git a/packages/livechat/package.json b/packages/livechat/package.json index c83ae42ec9a..f3ed1fb4c54 100644 --- a/packages/livechat/package.json +++ b/packages/livechat/package.json @@ -1,6 +1,6 @@ { "name": "@rocket.chat/livechat", - "version": "1.14.9", + "version": "1.14.10", "files": [ "/build" ], diff --git a/packages/model-typings/CHANGELOG.md b/packages/model-typings/CHANGELOG.md index 869b9d2d54c..f5a6884dea4 100644 --- a/packages/model-typings/CHANGELOG.md +++ b/packages/model-typings/CHANGELOG.md @@ -1,5 +1,12 @@ # @rocket.chat/model-typings +## 0.2.1 + +### Patch Changes + +- Updated dependencies [46fa2ff2cf] + - @rocket.chat/core-typings@6.6.0 + ## 0.2.0 ### Minor Changes diff --git a/packages/model-typings/package.json b/packages/model-typings/package.json index 2209995e21b..7aa61455019 100644 --- a/packages/model-typings/package.json +++ b/packages/model-typings/package.json @@ -1,6 +1,6 @@ { "name": "@rocket.chat/model-typings", - "version": "0.2.0", + "version": "0.2.1", "private": true, "devDependencies": { "@types/jest": "~29.5.7", diff --git a/packages/models/CHANGELOG.md b/packages/models/CHANGELOG.md index e5fdb024c33..8861ba27c49 100644 --- a/packages/models/CHANGELOG.md +++ b/packages/models/CHANGELOG.md @@ -1,5 +1,11 @@ # @rocket.chat/models +## 0.0.25 + +### Patch Changes + +- @rocket.chat/model-typings@0.2.1 + ## 0.0.24 ### Patch Changes diff --git a/packages/models/package.json b/packages/models/package.json index 8856ceab3d4..3b19c6415c0 100644 --- a/packages/models/package.json +++ b/packages/models/package.json @@ -1,6 +1,6 @@ { "name": "@rocket.chat/models", - "version": "0.0.24", + "version": "0.0.25", "private": true, "devDependencies": { "@types/jest": "~29.5.7", diff --git a/packages/rest-typings/CHANGELOG.md b/packages/rest-typings/CHANGELOG.md index 3bc136c490b..9c3da7c3327 100644 --- a/packages/rest-typings/CHANGELOG.md +++ b/packages/rest-typings/CHANGELOG.md @@ -1,5 +1,16 @@ # @rocket.chat/rest-typings +## 6.6.0 + +### Minor Changes + +- 4b0fa27e53: fix Federation Regression, builds service correctly + +### Patch Changes + +- Updated dependencies [46fa2ff2cf] + - @rocket.chat/core-typings@6.6.0 + ## 6.5.0 ### Minor Changes diff --git a/packages/rest-typings/package.json b/packages/rest-typings/package.json index e9c8386ca05..f6762b4b992 100644 --- a/packages/rest-typings/package.json +++ b/packages/rest-typings/package.json @@ -1,6 +1,6 @@ { "name": "@rocket.chat/rest-typings", - "version": "6.5.0", + "version": "6.6.0", "devDependencies": { "@rocket.chat/eslint-config": "workspace:^", "@types/jest": "~29.5.7", diff --git a/packages/ui-client/CHANGELOG.md b/packages/ui-client/CHANGELOG.md index 430507f15f5..0f7304ff4c4 100644 --- a/packages/ui-client/CHANGELOG.md +++ b/packages/ui-client/CHANGELOG.md @@ -1,5 +1,11 @@ # @rocket.chat/ui-client +## 4.0.0 + +### Patch Changes + +- @rocket.chat/ui-contexts@4.0.0 + ## 3.0.0 ### Patch Changes diff --git a/packages/ui-client/package.json b/packages/ui-client/package.json index f5011147776..fbaf921c308 100644 --- a/packages/ui-client/package.json +++ b/packages/ui-client/package.json @@ -1,6 +1,6 @@ { "name": "@rocket.chat/ui-client", - "version": "3.0.0", + "version": "4.0.0", "private": true, "devDependencies": { "@babel/core": "~7.22.20", @@ -61,7 +61,7 @@ "@rocket.chat/fuselage": "*", "@rocket.chat/fuselage-hooks": "*", "@rocket.chat/icons": "*", - "@rocket.chat/ui-contexts": "3.0.0", + "@rocket.chat/ui-contexts": "4.0.0", "react": "~17.0.2" }, "volta": { diff --git a/packages/ui-contexts/CHANGELOG.md b/packages/ui-contexts/CHANGELOG.md index 6e0f2022b88..446996bf67c 100644 --- a/packages/ui-contexts/CHANGELOG.md +++ b/packages/ui-contexts/CHANGELOG.md @@ -1,5 +1,20 @@ # @rocket.chat/ui-contexts +## 4.0.0 + +### Patch Changes + +- Updated dependencies [4b0fa27e53] +- Updated dependencies [46fa2ff2cf] +- Updated dependencies [f6a525a444] +- Updated dependencies [1233bffa8c] +- Updated dependencies [1233bffa8c] +- Updated dependencies [1233bffa8c] +- Updated dependencies [b42aa49fbd] + - @rocket.chat/rest-typings@6.6.0 + - @rocket.chat/ddp-client@0.2.10 + - @rocket.chat/core-typings@6.6.0 + ## 3.0.0 ### Patch Changes diff --git a/packages/ui-contexts/package.json b/packages/ui-contexts/package.json index aa8dd18a8b7..f2f258a111d 100644 --- a/packages/ui-contexts/package.json +++ b/packages/ui-contexts/package.json @@ -1,6 +1,6 @@ { "name": "@rocket.chat/ui-contexts", - "version": "3.0.0", + "version": "4.0.0", "private": true, "devDependencies": { "@rocket.chat/core-typings": "workspace:^", diff --git a/packages/ui-video-conf/CHANGELOG.md b/packages/ui-video-conf/CHANGELOG.md index 8a18b550da0..24e7e1304f5 100644 --- a/packages/ui-video-conf/CHANGELOG.md +++ b/packages/ui-video-conf/CHANGELOG.md @@ -1,5 +1,11 @@ # @rocket.chat/ui-video-conf +## 4.0.0 + +### Patch Changes + +- @rocket.chat/ui-contexts@4.0.0 + ## 3.0.0 ### Patch Changes diff --git a/packages/ui-video-conf/package.json b/packages/ui-video-conf/package.json index 108a7ec0cde..794691ec0b2 100644 --- a/packages/ui-video-conf/package.json +++ b/packages/ui-video-conf/package.json @@ -1,6 +1,6 @@ { "name": "@rocket.chat/ui-video-conf", - "version": "3.0.0", + "version": "4.0.0", "private": true, "devDependencies": { "@babel/core": "~7.22.20", @@ -35,7 +35,7 @@ "@rocket.chat/fuselage-hooks": "*", "@rocket.chat/icons": "*", "@rocket.chat/styled": "*", - "@rocket.chat/ui-contexts": "3.0.0", + "@rocket.chat/ui-contexts": "4.0.0", "react": "^17.0.2", "react-dom": "^17.0.2" }, diff --git a/packages/uikit-playground/CHANGELOG.md b/packages/uikit-playground/CHANGELOG.md index 260935c50f2..4745f4596d4 100644 --- a/packages/uikit-playground/CHANGELOG.md +++ b/packages/uikit-playground/CHANGELOG.md @@ -1,5 +1,12 @@ # @rocket.chat/uikit-playground +## 0.2.10 + +### Patch Changes + +- @rocket.chat/ui-contexts@4.0.0 +- @rocket.chat/fuselage-ui-kit@4.0.0 + ## 0.2.9 ### Patch Changes diff --git a/packages/uikit-playground/package.json b/packages/uikit-playground/package.json index 6afbd34e675..83015854f23 100644 --- a/packages/uikit-playground/package.json +++ b/packages/uikit-playground/package.json @@ -1,7 +1,7 @@ { "name": "@rocket.chat/uikit-playground", "private": true, - "version": "0.2.9", + "version": "0.2.10", "type": "module", "scripts": { "dev": "vite", diff --git a/packages/web-ui-registration/CHANGELOG.md b/packages/web-ui-registration/CHANGELOG.md index 76b400173d1..b91a7be2e80 100644 --- a/packages/web-ui-registration/CHANGELOG.md +++ b/packages/web-ui-registration/CHANGELOG.md @@ -1,5 +1,11 @@ # @rocket.chat/web-ui-registration +## 4.0.0 + +### Patch Changes + +- @rocket.chat/ui-contexts@4.0.0 + ## 3.0.0 ### Patch Changes diff --git a/packages/web-ui-registration/package.json b/packages/web-ui-registration/package.json index 122b0f06430..8dcf0de9a9d 100644 --- a/packages/web-ui-registration/package.json +++ b/packages/web-ui-registration/package.json @@ -1,6 +1,6 @@ { "name": "@rocket.chat/web-ui-registration", - "version": "3.0.0", + "version": "4.0.0", "private": true, "homepage": "https://rocket.chat", "main": "./dist/index.js", @@ -51,7 +51,7 @@ "peerDependencies": { "@rocket.chat/layout": "*", "@rocket.chat/tools": "*", - "@rocket.chat/ui-contexts": "3.0.0", + "@rocket.chat/ui-contexts": "4.0.0", "@tanstack/react-query": "*", "react": "*", "react-hook-form": "*", -- GitLab From 631f6a4fa68c7a9f7129d6b643c77a35cd217955 Mon Sep 17 00:00:00 2001 From: Sayan4444 <112304873+Sayan4444@users.noreply.github.com> Date: Tue, 26 Dec 2023 21:58:49 +0530 Subject: [PATCH 077/329] fix: Language selection box is empty which should be default (#31312) Co-authored-by: Douglas Fabris <27704687+dougfabris@users.noreply.github.com> --- .changeset/chilled-cooks-end.md | 5 +++++ .../views/account/preferences/useAccountPreferencesValues.ts | 2 +- 2 files changed, 6 insertions(+), 1 deletion(-) create mode 100644 .changeset/chilled-cooks-end.md diff --git a/.changeset/chilled-cooks-end.md b/.changeset/chilled-cooks-end.md new file mode 100644 index 00000000000..820a3bf679f --- /dev/null +++ b/.changeset/chilled-cooks-end.md @@ -0,0 +1,5 @@ +--- +"@rocket.chat/meteor": patch +--- + +Fixed an issue displaying the language selection preference empty when it should display 'Default' on the initial value diff --git a/apps/meteor/client/views/account/preferences/useAccountPreferencesValues.ts b/apps/meteor/client/views/account/preferences/useAccountPreferencesValues.ts index b99a2a6c1eb..a85ef275638 100644 --- a/apps/meteor/client/views/account/preferences/useAccountPreferencesValues.ts +++ b/apps/meteor/client/views/account/preferences/useAccountPreferencesValues.ts @@ -39,7 +39,7 @@ export type AccountPreferencesData = { }; export const useAccountPreferencesValues = (): AccountPreferencesData => { - const language = useUserPreference('language'); + const language = useUserPreference('language') || ''; const userDontAskAgainList = useUserPreference<{ action: string; label: string }[]>('dontAskAgainList') || []; const dontAskAgainList = userDontAskAgainList.map(({ action }) => action); const enableAutoAway = useUserPreference('enableAutoAway'); -- GitLab From c2b224fd82f655f4edf6b4a7d5baea2c1166978a Mon Sep 17 00:00:00 2001 From: Diego Sampaio Date: Tue, 26 Dec 2023 14:01:22 -0300 Subject: [PATCH 078/329] Revert "Release 6.6.0" This reverts commit f7086db493ae1e492a53da4721dc9750349cb2af. --- .changeset/bump-patch-1702298298384.md | 5 ++ .changeset/fair-suns-study.md | 5 ++ .changeset/fresh-radios-whisper.md | 5 ++ .changeset/gold-stingrays-compete.md | 5 ++ .changeset/lucky-apricots-change.md | 5 ++ .changeset/lucky-cycles-leave.md | 5 ++ .changeset/nasty-islands-trade.md | 6 +++ .changeset/new-avocados-sort.md | 7 +++ .changeset/popular-beds-heal.md | 7 +++ .changeset/real-items-tan.md | 5 ++ .changeset/remove-license-31189.md | 5 ++ .changeset/resume-login-31301.md | 5 ++ .changeset/save-token-31278.md | 5 ++ .changeset/sharp-rings-smash.md | 6 +++ .changeset/sour-kids-heal.md | 6 +++ .changeset/spicy-kiwis-argue.md | 5 ++ .changeset/spotty-suns-grin.md | 5 ++ .changeset/tame-drinks-yell.md | 5 ++ .changeset/violet-pears-cry.md | 5 ++ apps/meteor/.docker/Dockerfile.rhel | 2 +- apps/meteor/CHANGELOG.md | 50 ------------------- apps/meteor/app/utils/rocketchat.info | 2 +- apps/meteor/ee/server/services/CHANGELOG.md | 12 ----- apps/meteor/ee/server/services/package.json | 2 +- apps/meteor/package.json | 2 +- ee/apps/account-service/CHANGELOG.md | 12 ----- ee/apps/account-service/package.json | 2 +- ee/apps/authorization-service/CHANGELOG.md | 12 ----- ee/apps/authorization-service/package.json | 2 +- ee/apps/ddp-streamer/CHANGELOG.md | 14 ------ ee/apps/ddp-streamer/package.json | 2 +- ee/apps/omnichannel-transcript/CHANGELOG.md | 12 ----- ee/apps/omnichannel-transcript/package.json | 2 +- ee/apps/presence-service/CHANGELOG.md | 11 ---- ee/apps/presence-service/package.json | 2 +- ee/apps/queue-worker/CHANGELOG.md | 11 ---- ee/apps/queue-worker/package.json | 2 +- ee/apps/stream-hub-service/CHANGELOG.md | 10 ---- ee/apps/stream-hub-service/package.json | 2 +- ee/packages/api-client/CHANGELOG.md | 9 ---- ee/packages/api-client/package.json | 2 +- ee/packages/ddp-client/CHANGELOG.md | 14 ------ ee/packages/ddp-client/package.json | 2 +- ee/packages/license/CHANGELOG.md | 7 --- ee/packages/license/package.json | 2 +- ee/packages/omnichannel-services/CHANGELOG.md | 13 ----- ee/packages/omnichannel-services/package.json | 2 +- ee/packages/pdf-worker/CHANGELOG.md | 7 --- ee/packages/pdf-worker/package.json | 2 +- ee/packages/presence/CHANGELOG.md | 9 ---- ee/packages/presence/package.json | 2 +- package.json | 2 +- packages/core-services/CHANGELOG.md | 10 ---- packages/core-services/package.json | 2 +- packages/core-typings/CHANGELOG.md | 6 --- packages/core-typings/package.json | 2 +- packages/cron/CHANGELOG.md | 8 --- packages/cron/package.json | 2 +- packages/fuselage-ui-kit/CHANGELOG.md | 8 --- packages/fuselage-ui-kit/package.json | 6 +-- packages/gazzodown/CHANGELOG.md | 9 ---- packages/gazzodown/package.json | 8 +-- packages/instance-status/CHANGELOG.md | 6 --- packages/instance-status/package.json | 2 +- packages/livechat/CHANGELOG.md | 6 --- packages/livechat/package.json | 2 +- packages/model-typings/CHANGELOG.md | 7 --- packages/model-typings/package.json | 2 +- packages/models/CHANGELOG.md | 6 --- packages/models/package.json | 2 +- packages/rest-typings/CHANGELOG.md | 11 ---- packages/rest-typings/package.json | 2 +- packages/ui-client/CHANGELOG.md | 6 --- packages/ui-client/package.json | 4 +- packages/ui-contexts/CHANGELOG.md | 15 ------ packages/ui-contexts/package.json | 2 +- packages/ui-video-conf/CHANGELOG.md | 6 --- packages/ui-video-conf/package.json | 4 +- packages/uikit-playground/CHANGELOG.md | 7 --- packages/uikit-playground/package.json | 2 +- packages/web-ui-registration/CHANGELOG.md | 6 --- packages/web-ui-registration/package.json | 4 +- 82 files changed, 143 insertions(+), 361 deletions(-) create mode 100644 .changeset/bump-patch-1702298298384.md create mode 100644 .changeset/fair-suns-study.md create mode 100644 .changeset/fresh-radios-whisper.md create mode 100644 .changeset/gold-stingrays-compete.md create mode 100644 .changeset/lucky-apricots-change.md create mode 100644 .changeset/lucky-cycles-leave.md create mode 100644 .changeset/nasty-islands-trade.md create mode 100644 .changeset/new-avocados-sort.md create mode 100644 .changeset/popular-beds-heal.md create mode 100644 .changeset/real-items-tan.md create mode 100644 .changeset/remove-license-31189.md create mode 100644 .changeset/resume-login-31301.md create mode 100644 .changeset/save-token-31278.md create mode 100644 .changeset/sharp-rings-smash.md create mode 100644 .changeset/sour-kids-heal.md create mode 100644 .changeset/spicy-kiwis-argue.md create mode 100644 .changeset/spotty-suns-grin.md create mode 100644 .changeset/tame-drinks-yell.md create mode 100644 .changeset/violet-pears-cry.md diff --git a/.changeset/bump-patch-1702298298384.md b/.changeset/bump-patch-1702298298384.md new file mode 100644 index 00000000000..e1eaa7980af --- /dev/null +++ b/.changeset/bump-patch-1702298298384.md @@ -0,0 +1,5 @@ +--- +'@rocket.chat/meteor': patch +--- + +Bump @rocket.chat/meteor version. diff --git a/.changeset/fair-suns-study.md b/.changeset/fair-suns-study.md new file mode 100644 index 00000000000..64dcc2ec7ee --- /dev/null +++ b/.changeset/fair-suns-study.md @@ -0,0 +1,5 @@ +--- +'@rocket.chat/meteor': patch +--- + +Security improvements diff --git a/.changeset/fresh-radios-whisper.md b/.changeset/fresh-radios-whisper.md new file mode 100644 index 00000000000..cba234524dc --- /dev/null +++ b/.changeset/fresh-radios-whisper.md @@ -0,0 +1,5 @@ +--- +"@rocket.chat/meteor": patch +--- + +Fixed issue with the new `custom-roles` license module not being checked throughout the application diff --git a/.changeset/gold-stingrays-compete.md b/.changeset/gold-stingrays-compete.md new file mode 100644 index 00000000000..8cc6c3aa6e8 --- /dev/null +++ b/.changeset/gold-stingrays-compete.md @@ -0,0 +1,5 @@ +--- +"@rocket.chat/meteor": patch +--- + +fix: stop refetching banner data each 5 minutes diff --git a/.changeset/lucky-apricots-change.md b/.changeset/lucky-apricots-change.md new file mode 100644 index 00000000000..12d93b4fb65 --- /dev/null +++ b/.changeset/lucky-apricots-change.md @@ -0,0 +1,5 @@ +--- +"@rocket.chat/meteor": patch +--- + +Fixed an issue allowing admin user cancelling subscription when license's trial param is provided diff --git a/.changeset/lucky-cycles-leave.md b/.changeset/lucky-cycles-leave.md new file mode 100644 index 00000000000..608db68031b --- /dev/null +++ b/.changeset/lucky-cycles-leave.md @@ -0,0 +1,5 @@ +--- +"@rocket.chat/meteor": patch +--- + +Fixed Country select component at Organization form from `onboarding-ui` package diff --git a/.changeset/nasty-islands-trade.md b/.changeset/nasty-islands-trade.md new file mode 100644 index 00000000000..b6df94282dd --- /dev/null +++ b/.changeset/nasty-islands-trade.md @@ -0,0 +1,6 @@ +--- +'@rocket.chat/rest-typings': minor +'@rocket.chat/meteor': minor +--- + +fix Federation Regression, builds service correctly diff --git a/.changeset/new-avocados-sort.md b/.changeset/new-avocados-sort.md new file mode 100644 index 00000000000..1f8b90ed008 --- /dev/null +++ b/.changeset/new-avocados-sort.md @@ -0,0 +1,7 @@ +--- +"@rocket.chat/meteor": patch +--- + +fix: Wrong `Message Roundtrip Time` metric + +Removes the wrong metric gauge named `rocketchat_messages_roundtrip_time` and replace it by a new summary metric named `rocketchat_messages_roundtrip_time_summary`. Add new percentiles `0.5, 0.95 and 1` to all summary metrics. diff --git a/.changeset/popular-beds-heal.md b/.changeset/popular-beds-heal.md new file mode 100644 index 00000000000..03f4dd735d8 --- /dev/null +++ b/.changeset/popular-beds-heal.md @@ -0,0 +1,7 @@ +--- +'@rocket.chat/ddp-client': patch +'@rocket.chat/core-typings': patch +'@rocket.chat/meteor': patch +--- + +Exceeding API calls when sending OTR messages diff --git a/.changeset/real-items-tan.md b/.changeset/real-items-tan.md new file mode 100644 index 00000000000..302ac3eacfe --- /dev/null +++ b/.changeset/real-items-tan.md @@ -0,0 +1,5 @@ +--- +"@rocket.chat/ddp-client": patch +--- + +SDK login methods not saving token diff --git a/.changeset/remove-license-31189.md b/.changeset/remove-license-31189.md new file mode 100644 index 00000000000..e510f263feb --- /dev/null +++ b/.changeset/remove-license-31189.md @@ -0,0 +1,5 @@ +--- +'@rocket.chat/ddp-client': patch +--- + +removed @rocket.chat/license as a dependency of ddp client diff --git a/.changeset/resume-login-31301.md b/.changeset/resume-login-31301.md new file mode 100644 index 00000000000..2ec78cd6c31 --- /dev/null +++ b/.changeset/resume-login-31301.md @@ -0,0 +1,5 @@ +--- +'@rocket.chat/ddp-client': patch +--- + +fixed an issue with the ddp client reconnection not resuming the login diff --git a/.changeset/save-token-31278.md b/.changeset/save-token-31278.md new file mode 100644 index 00000000000..49f22037f4c --- /dev/null +++ b/.changeset/save-token-31278.md @@ -0,0 +1,5 @@ +--- +'@rocket.chat/ddp-client': patch +--- + +fixed an issue with the ddp client account not saving credentials correctly diff --git a/.changeset/sharp-rings-smash.md b/.changeset/sharp-rings-smash.md new file mode 100644 index 00000000000..0e4360ff46d --- /dev/null +++ b/.changeset/sharp-rings-smash.md @@ -0,0 +1,6 @@ +--- +"@rocket.chat/meteor": patch +--- + +Fixed a problem with the subscription creation on Omnichannel rooms. +Rooms were being created as seen, causing sound notifications to not work diff --git a/.changeset/sour-kids-heal.md b/.changeset/sour-kids-heal.md new file mode 100644 index 00000000000..9dbeff968bb --- /dev/null +++ b/.changeset/sour-kids-heal.md @@ -0,0 +1,6 @@ +--- +"@rocket.chat/meteor": patch +"@rocket.chat/ddp-client": patch +--- + +Fixed a problem where chained callbacks' return value was being overrided by some callbacks returning something different, causing callbacks with lower priority to operate on invalid values diff --git a/.changeset/spicy-kiwis-argue.md b/.changeset/spicy-kiwis-argue.md new file mode 100644 index 00000000000..520fdfe2201 --- /dev/null +++ b/.changeset/spicy-kiwis-argue.md @@ -0,0 +1,5 @@ +--- +'@rocket.chat/meteor': patch +--- + +Fix desktop notification routing for direct rooms diff --git a/.changeset/spotty-suns-grin.md b/.changeset/spotty-suns-grin.md new file mode 100644 index 00000000000..8045ebbd1b4 --- /dev/null +++ b/.changeset/spotty-suns-grin.md @@ -0,0 +1,5 @@ +--- +'@rocket.chat/meteor': patch +--- + +Improved the experience of receiving conference calls on the mobile app by disabling the push notification for the "new call" message if a push is already being sent to trigger the phone's ringing tone. diff --git a/.changeset/tame-drinks-yell.md b/.changeset/tame-drinks-yell.md new file mode 100644 index 00000000000..6cb19babccf --- /dev/null +++ b/.changeset/tame-drinks-yell.md @@ -0,0 +1,5 @@ +--- +"@rocket.chat/meteor": patch +--- + +Fixed verify the account through email link diff --git a/.changeset/violet-pears-cry.md b/.changeset/violet-pears-cry.md new file mode 100644 index 00000000000..8e10ad50dd5 --- /dev/null +++ b/.changeset/violet-pears-cry.md @@ -0,0 +1,5 @@ +--- +'@rocket.chat/meteor': patch +--- + +Fixed the filter for file type in the list of room files diff --git a/apps/meteor/.docker/Dockerfile.rhel b/apps/meteor/.docker/Dockerfile.rhel index 84cf1f409b5..c97c3dfc370 100644 --- a/apps/meteor/.docker/Dockerfile.rhel +++ b/apps/meteor/.docker/Dockerfile.rhel @@ -1,6 +1,6 @@ FROM registry.access.redhat.com/ubi8/nodejs-12 -ENV RC_VERSION 6.6.0 +ENV RC_VERSION 6.5.0 MAINTAINER buildmaster@rocket.chat diff --git a/apps/meteor/CHANGELOG.md b/apps/meteor/CHANGELOG.md index 82395720492..874fc20d33b 100644 --- a/apps/meteor/CHANGELOG.md +++ b/apps/meteor/CHANGELOG.md @@ -1,55 +1,5 @@ # @rocket.chat/meteor -## 6.6.0 - -### Minor Changes - -- 4b0fa27e53: fix Federation Regression, builds service correctly - -### Patch Changes - -- 24e9dc2633: Bump @rocket.chat/meteor version. -- Bump @rocket.chat/meteor version. -- 2c577efc5f: Security improvements -- 055cb0fa3e: Fixed issue with the new `custom-roles` license module not being checked throughout the application -- 68ff7e5c5b: fix: stop refetching banner data each 5 minutes -- 077d04f0a8: Fixed an issue allowing admin user cancelling subscription when license's trial param is provided -- dc111e2e84: Fixed Country select component at Organization form from `onboarding-ui` package -- 816dd50548: fix: Wrong `Message Roundtrip Time` metric - - Removes the wrong metric gauge named `rocketchat_messages_roundtrip_time` and replace it by a new summary metric named `rocketchat_messages_roundtrip_time_summary`. Add new percentiles `0.5, 0.95 and 1` to all summary metrics. - -- 46fa2ff2cf: Exceeding API calls when sending OTR messages -- d6a8360564: Fixed a problem with the subscription creation on Omnichannel rooms. - Rooms were being created as seen, causing sound notifications to not work -- b42aa49fbd: Fixed a problem where chained callbacks' return value was being overrided by some callbacks returning something different, causing callbacks with lower priority to operate on invalid values -- f2699e4988: Fix desktop notification routing for direct rooms -- 4e49fcc7d9: Improved the experience of receiving conference calls on the mobile app by disabling the push notification for the "new call" message if a push is already being sent to trigger the phone's ringing tone. -- 6ba8bcc2ca: Fixed verify the account through email link -- 4b2bdd5508: Fixed the filter for file type in the list of room files -- Updated dependencies [4b0fa27e53] -- Updated dependencies [46fa2ff2cf] - - @rocket.chat/rest-typings@6.6.0 - - @rocket.chat/core-typings@6.6.0 - - @rocket.chat/api-client@0.1.19 - - @rocket.chat/omnichannel-services@0.1.1 - - @rocket.chat/presence@0.1.1 - - @rocket.chat/core-services@0.3.1 - - @rocket.chat/ui-contexts@4.0.0 - - @rocket.chat/license@0.1.1 - - @rocket.chat/pdf-worker@0.0.25 - - @rocket.chat/cron@0.0.21 - - @rocket.chat/gazzodown@4.0.0 - - @rocket.chat/model-typings@0.2.1 - - @rocket.chat/ui-theming@0.1.1 - - @rocket.chat/fuselage-ui-kit@4.0.0 - - @rocket.chat/ui-client@4.0.0 - - @rocket.chat/ui-video-conf@4.0.0 - - @rocket.chat/web-ui-registration@4.0.0 - - @rocket.chat/server-cloud-communication@0.0.1 - - @rocket.chat/models@0.0.25 - - @rocket.chat/instance-status@0.0.25 - ## 6.5.0 ### Minor Changes diff --git a/apps/meteor/app/utils/rocketchat.info b/apps/meteor/app/utils/rocketchat.info index 378fba14ca4..2cd7972686e 100644 --- a/apps/meteor/app/utils/rocketchat.info +++ b/apps/meteor/app/utils/rocketchat.info @@ -1,3 +1,3 @@ { - "version": "6.6.0" + "version": "6.5.0" } diff --git a/apps/meteor/ee/server/services/CHANGELOG.md b/apps/meteor/ee/server/services/CHANGELOG.md index 281d34fabf6..07a89f2d723 100644 --- a/apps/meteor/ee/server/services/CHANGELOG.md +++ b/apps/meteor/ee/server/services/CHANGELOG.md @@ -1,17 +1,5 @@ # rocketchat-services -## 1.1.19 - -### Patch Changes - -- Updated dependencies [4b0fa27e53] -- Updated dependencies [46fa2ff2cf] - - @rocket.chat/rest-typings@6.6.0 - - @rocket.chat/core-typings@6.6.0 - - @rocket.chat/core-services@0.3.1 - - @rocket.chat/model-typings@0.2.1 - - @rocket.chat/models@0.0.25 - ## 1.1.18 ### Patch Changes diff --git a/apps/meteor/ee/server/services/package.json b/apps/meteor/ee/server/services/package.json index c44ad6cf584..468e444d56f 100644 --- a/apps/meteor/ee/server/services/package.json +++ b/apps/meteor/ee/server/services/package.json @@ -1,7 +1,7 @@ { "name": "rocketchat-services", "private": true, - "version": "1.1.19", + "version": "1.1.18", "description": "Rocket.Chat Authorization service", "main": "index.js", "scripts": { diff --git a/apps/meteor/package.json b/apps/meteor/package.json index 8e43d9394e9..6f2e9628339 100644 --- a/apps/meteor/package.json +++ b/apps/meteor/package.json @@ -1,7 +1,7 @@ { "name": "@rocket.chat/meteor", "description": "The Ultimate Open Source WebChat Platform", - "version": "6.6.0", + "version": "6.5.0", "private": true, "author": { "name": "Rocket.Chat", diff --git a/ee/apps/account-service/CHANGELOG.md b/ee/apps/account-service/CHANGELOG.md index 90936d44891..0cc9b7bc148 100644 --- a/ee/apps/account-service/CHANGELOG.md +++ b/ee/apps/account-service/CHANGELOG.md @@ -1,17 +1,5 @@ # @rocket.chat/account-service -## 0.3.1 - -### Patch Changes - -- Updated dependencies [4b0fa27e53] -- Updated dependencies [46fa2ff2cf] - - @rocket.chat/rest-typings@6.6.0 - - @rocket.chat/core-typings@6.6.0 - - @rocket.chat/core-services@0.3.1 - - @rocket.chat/model-typings@0.2.1 - - @rocket.chat/models@0.0.25 - ## 0.3.0 ### Minor Changes diff --git a/ee/apps/account-service/package.json b/ee/apps/account-service/package.json index b40c269b172..33e668e52e5 100644 --- a/ee/apps/account-service/package.json +++ b/ee/apps/account-service/package.json @@ -1,7 +1,7 @@ { "name": "@rocket.chat/account-service", "private": true, - "version": "0.3.1", + "version": "0.3.0", "description": "Rocket.Chat Account service", "scripts": { "build": "tsc -p tsconfig.json", diff --git a/ee/apps/authorization-service/CHANGELOG.md b/ee/apps/authorization-service/CHANGELOG.md index 9264ecfb205..90cad488c77 100644 --- a/ee/apps/authorization-service/CHANGELOG.md +++ b/ee/apps/authorization-service/CHANGELOG.md @@ -1,17 +1,5 @@ # @rocket.chat/authorization-service -## 0.3.1 - -### Patch Changes - -- Updated dependencies [4b0fa27e53] -- Updated dependencies [46fa2ff2cf] - - @rocket.chat/rest-typings@6.6.0 - - @rocket.chat/core-typings@6.6.0 - - @rocket.chat/core-services@0.3.1 - - @rocket.chat/model-typings@0.2.1 - - @rocket.chat/models@0.0.25 - ## 0.3.0 ### Minor Changes diff --git a/ee/apps/authorization-service/package.json b/ee/apps/authorization-service/package.json index 6d6797fab89..5ec688afa7a 100644 --- a/ee/apps/authorization-service/package.json +++ b/ee/apps/authorization-service/package.json @@ -1,7 +1,7 @@ { "name": "@rocket.chat/authorization-service", "private": true, - "version": "0.3.1", + "version": "0.3.0", "description": "Rocket.Chat Authorization service", "scripts": { "build": "tsc -p tsconfig.json", diff --git a/ee/apps/ddp-streamer/CHANGELOG.md b/ee/apps/ddp-streamer/CHANGELOG.md index a2023d9b9f9..2790871dc26 100644 --- a/ee/apps/ddp-streamer/CHANGELOG.md +++ b/ee/apps/ddp-streamer/CHANGELOG.md @@ -1,19 +1,5 @@ # @rocket.chat/ddp-streamer -## 0.2.1 - -### Patch Changes - -- Updated dependencies [4b0fa27e53] -- Updated dependencies [46fa2ff2cf] - - @rocket.chat/rest-typings@6.6.0 - - @rocket.chat/core-typings@6.6.0 - - @rocket.chat/core-services@0.3.1 - - @rocket.chat/ui-contexts@4.0.0 - - @rocket.chat/model-typings@0.2.1 - - @rocket.chat/models@0.0.25 - - @rocket.chat/instance-status@0.0.25 - ## 0.2.0 ### Minor Changes diff --git a/ee/apps/ddp-streamer/package.json b/ee/apps/ddp-streamer/package.json index fa36b8fb0ea..f5641474c55 100644 --- a/ee/apps/ddp-streamer/package.json +++ b/ee/apps/ddp-streamer/package.json @@ -1,7 +1,7 @@ { "name": "@rocket.chat/ddp-streamer", "private": true, - "version": "0.2.1", + "version": "0.2.0", "description": "Rocket.Chat DDP-Streamer service", "scripts": { "build": "tsc -p tsconfig.json", diff --git a/ee/apps/omnichannel-transcript/CHANGELOG.md b/ee/apps/omnichannel-transcript/CHANGELOG.md index b1db814549e..d7ad0294e40 100644 --- a/ee/apps/omnichannel-transcript/CHANGELOG.md +++ b/ee/apps/omnichannel-transcript/CHANGELOG.md @@ -1,17 +1,5 @@ # @rocket.chat/omnichannel-transcript -## 0.3.1 - -### Patch Changes - -- Updated dependencies [46fa2ff2cf] - - @rocket.chat/core-typings@6.6.0 - - @rocket.chat/omnichannel-services@0.1.1 - - @rocket.chat/core-services@0.3.1 - - @rocket.chat/pdf-worker@0.0.25 - - @rocket.chat/model-typings@0.2.1 - - @rocket.chat/models@0.0.25 - ## 0.3.0 ### Minor Changes diff --git a/ee/apps/omnichannel-transcript/package.json b/ee/apps/omnichannel-transcript/package.json index 421eebe9fbf..b30ef724a49 100644 --- a/ee/apps/omnichannel-transcript/package.json +++ b/ee/apps/omnichannel-transcript/package.json @@ -1,7 +1,7 @@ { "name": "@rocket.chat/omnichannel-transcript", "private": true, - "version": "0.3.1", + "version": "0.3.0", "description": "Rocket.Chat service", "scripts": { "build": "tsc -p tsconfig.json", diff --git a/ee/apps/presence-service/CHANGELOG.md b/ee/apps/presence-service/CHANGELOG.md index 850e9c8252b..d8f7c18a73d 100644 --- a/ee/apps/presence-service/CHANGELOG.md +++ b/ee/apps/presence-service/CHANGELOG.md @@ -1,16 +1,5 @@ # @rocket.chat/presence-service -## 0.3.1 - -### Patch Changes - -- Updated dependencies [46fa2ff2cf] - - @rocket.chat/core-typings@6.6.0 - - @rocket.chat/presence@0.1.1 - - @rocket.chat/core-services@0.3.1 - - @rocket.chat/model-typings@0.2.1 - - @rocket.chat/models@0.0.25 - ## 0.3.0 ### Minor Changes diff --git a/ee/apps/presence-service/package.json b/ee/apps/presence-service/package.json index b0001eb9c89..3d3490d169a 100644 --- a/ee/apps/presence-service/package.json +++ b/ee/apps/presence-service/package.json @@ -1,7 +1,7 @@ { "name": "@rocket.chat/presence-service", "private": true, - "version": "0.3.1", + "version": "0.3.0", "description": "Rocket.Chat Presence service", "scripts": { "build": "tsc -p tsconfig.json", diff --git a/ee/apps/queue-worker/CHANGELOG.md b/ee/apps/queue-worker/CHANGELOG.md index f2c03035ff3..ae9269affed 100644 --- a/ee/apps/queue-worker/CHANGELOG.md +++ b/ee/apps/queue-worker/CHANGELOG.md @@ -1,16 +1,5 @@ # @rocket.chat/queue-worker -## 0.3.1 - -### Patch Changes - -- Updated dependencies [46fa2ff2cf] - - @rocket.chat/core-typings@6.6.0 - - @rocket.chat/omnichannel-services@0.1.1 - - @rocket.chat/core-services@0.3.1 - - @rocket.chat/model-typings@0.2.1 - - @rocket.chat/models@0.0.25 - ## 0.3.0 ### Minor Changes diff --git a/ee/apps/queue-worker/package.json b/ee/apps/queue-worker/package.json index 25d536e8d11..0e26ceea7b2 100644 --- a/ee/apps/queue-worker/package.json +++ b/ee/apps/queue-worker/package.json @@ -1,7 +1,7 @@ { "name": "@rocket.chat/queue-worker", "private": true, - "version": "0.3.1", + "version": "0.3.0", "description": "Rocket.Chat service", "scripts": { "build": "tsc -p tsconfig.json", diff --git a/ee/apps/stream-hub-service/CHANGELOG.md b/ee/apps/stream-hub-service/CHANGELOG.md index 4519137539e..6bfff89143b 100644 --- a/ee/apps/stream-hub-service/CHANGELOG.md +++ b/ee/apps/stream-hub-service/CHANGELOG.md @@ -1,15 +1,5 @@ # @rocket.chat/stream-hub-service -## 0.3.1 - -### Patch Changes - -- Updated dependencies [46fa2ff2cf] - - @rocket.chat/core-typings@6.6.0 - - @rocket.chat/core-services@0.3.1 - - @rocket.chat/model-typings@0.2.1 - - @rocket.chat/models@0.0.25 - ## 0.3.0 ### Minor Changes diff --git a/ee/apps/stream-hub-service/package.json b/ee/apps/stream-hub-service/package.json index 3e39bde7f50..04cb745254b 100644 --- a/ee/apps/stream-hub-service/package.json +++ b/ee/apps/stream-hub-service/package.json @@ -1,7 +1,7 @@ { "name": "@rocket.chat/stream-hub-service", "private": true, - "version": "0.3.1", + "version": "0.3.0", "description": "Rocket.Chat Stream Hub service", "scripts": { "build": "tsc -p tsconfig.json", diff --git a/ee/packages/api-client/CHANGELOG.md b/ee/packages/api-client/CHANGELOG.md index 6137628abfb..1e42f8d3fbf 100644 --- a/ee/packages/api-client/CHANGELOG.md +++ b/ee/packages/api-client/CHANGELOG.md @@ -1,14 +1,5 @@ # @rocket.chat/api-client -## 0.1.19 - -### Patch Changes - -- Updated dependencies [4b0fa27e53] -- Updated dependencies [46fa2ff2cf] - - @rocket.chat/rest-typings@6.6.0 - - @rocket.chat/core-typings@6.6.0 - ## 0.1.18 ### Patch Changes diff --git a/ee/packages/api-client/package.json b/ee/packages/api-client/package.json index 6010be23c26..5530e42393e 100644 --- a/ee/packages/api-client/package.json +++ b/ee/packages/api-client/package.json @@ -1,6 +1,6 @@ { "name": "@rocket.chat/api-client", - "version": "0.1.19", + "version": "0.1.18", "devDependencies": { "@swc/core": "^1.3.95", "@swc/jest": "^0.2.29", diff --git a/ee/packages/ddp-client/CHANGELOG.md b/ee/packages/ddp-client/CHANGELOG.md index ce3374bfa38..2f84d3e0764 100644 --- a/ee/packages/ddp-client/CHANGELOG.md +++ b/ee/packages/ddp-client/CHANGELOG.md @@ -1,19 +1,5 @@ # @rocket.chat/ddp-client -## 0.2.10 - -### Patch Changes - -- 46fa2ff2cf: Exceeding API calls when sending OTR messages -- f6a525a444: SDK login methods not saving token -- 1233bffa8c: removed @rocket.chat/license as a dependency of ddp client -- 1233bffa8c: fixed an issue with the ddp client reconnection not resuming the login -- 1233bffa8c: fixed an issue with the ddp client account not saving credentials correctly -- b42aa49fbd: Fixed a problem where chained callbacks' return value was being overrided by some callbacks returning something different, causing callbacks with lower priority to operate on invalid values -- Updated dependencies [4b0fa27e53] - - @rocket.chat/rest-typings@6.6.0 - - @rocket.chat/api-client@0.1.19 - ## 0.2.9 ### Patch Changes diff --git a/ee/packages/ddp-client/package.json b/ee/packages/ddp-client/package.json index 375aa7d40c2..680d970743c 100644 --- a/ee/packages/ddp-client/package.json +++ b/ee/packages/ddp-client/package.json @@ -1,6 +1,6 @@ { "name": "@rocket.chat/ddp-client", - "version": "0.2.10", + "version": "0.2.9", "devDependencies": { "@swc/core": "^1.3.95", "@swc/jest": "^0.2.29", diff --git a/ee/packages/license/CHANGELOG.md b/ee/packages/license/CHANGELOG.md index ec8bb0b9cc2..bb7835e90d1 100644 --- a/ee/packages/license/CHANGELOG.md +++ b/ee/packages/license/CHANGELOG.md @@ -1,12 +1,5 @@ # @rocket.chat/license -## 0.1.1 - -### Patch Changes - -- Updated dependencies [46fa2ff2cf] - - @rocket.chat/core-typings@6.6.0 - ## 0.1.0 ### Minor Changes diff --git a/ee/packages/license/package.json b/ee/packages/license/package.json index 890a8c15642..e2d253f7d11 100644 --- a/ee/packages/license/package.json +++ b/ee/packages/license/package.json @@ -1,6 +1,6 @@ { "name": "@rocket.chat/license", - "version": "0.1.1", + "version": "0.1.0", "private": true, "devDependencies": { "@swc/core": "^1.3.95", diff --git a/ee/packages/omnichannel-services/CHANGELOG.md b/ee/packages/omnichannel-services/CHANGELOG.md index 67fea3a375b..0e1170004d2 100644 --- a/ee/packages/omnichannel-services/CHANGELOG.md +++ b/ee/packages/omnichannel-services/CHANGELOG.md @@ -1,18 +1,5 @@ # @rocket.chat/omnichannel-services -## 0.1.1 - -### Patch Changes - -- Updated dependencies [4b0fa27e53] -- Updated dependencies [46fa2ff2cf] - - @rocket.chat/rest-typings@6.6.0 - - @rocket.chat/core-typings@6.6.0 - - @rocket.chat/core-services@0.3.1 - - @rocket.chat/pdf-worker@0.0.25 - - @rocket.chat/model-typings@0.2.1 - - @rocket.chat/models@0.0.25 - ## 0.1.0 ### Minor Changes diff --git a/ee/packages/omnichannel-services/package.json b/ee/packages/omnichannel-services/package.json index be89ade5855..8fb37352ed8 100644 --- a/ee/packages/omnichannel-services/package.json +++ b/ee/packages/omnichannel-services/package.json @@ -1,6 +1,6 @@ { "name": "@rocket.chat/omnichannel-services", - "version": "0.1.1", + "version": "0.1.0", "private": true, "devDependencies": { "@rocket.chat/eslint-config": "workspace:^", diff --git a/ee/packages/pdf-worker/CHANGELOG.md b/ee/packages/pdf-worker/CHANGELOG.md index 9b93a6fc28a..b64c1ce32bd 100644 --- a/ee/packages/pdf-worker/CHANGELOG.md +++ b/ee/packages/pdf-worker/CHANGELOG.md @@ -1,12 +1,5 @@ # @rocket.chat/pdf-worker -## 0.0.25 - -### Patch Changes - -- Updated dependencies [46fa2ff2cf] - - @rocket.chat/core-typings@6.6.0 - ## 0.0.24 ### Patch Changes diff --git a/ee/packages/pdf-worker/package.json b/ee/packages/pdf-worker/package.json index dd1dc2b3b0f..5c151bdc150 100644 --- a/ee/packages/pdf-worker/package.json +++ b/ee/packages/pdf-worker/package.json @@ -1,6 +1,6 @@ { "name": "@rocket.chat/pdf-worker", - "version": "0.0.25", + "version": "0.0.24", "private": true, "devDependencies": { "@storybook/addon-essentials": "~6.5.16", diff --git a/ee/packages/presence/CHANGELOG.md b/ee/packages/presence/CHANGELOG.md index 2fa9873957f..29759422a23 100644 --- a/ee/packages/presence/CHANGELOG.md +++ b/ee/packages/presence/CHANGELOG.md @@ -1,14 +1,5 @@ # @rocket.chat/presence -## 0.1.1 - -### Patch Changes - -- Updated dependencies [46fa2ff2cf] - - @rocket.chat/core-typings@6.6.0 - - @rocket.chat/core-services@0.3.1 - - @rocket.chat/models@0.0.25 - ## 0.1.0 ### Minor Changes diff --git a/ee/packages/presence/package.json b/ee/packages/presence/package.json index cb8eab27052..0c3d7d3bde3 100644 --- a/ee/packages/presence/package.json +++ b/ee/packages/presence/package.json @@ -1,6 +1,6 @@ { "name": "@rocket.chat/presence", - "version": "0.1.1", + "version": "0.1.0", "private": true, "devDependencies": { "@babel/core": "~7.22.20", diff --git a/package.json b/package.json index c5b5dea5122..dc4244dfd6f 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "rocket.chat", - "version": "6.6.0", + "version": "6.5.0", "description": "Rocket.Chat Monorepo", "main": "index.js", "private": true, diff --git a/packages/core-services/CHANGELOG.md b/packages/core-services/CHANGELOG.md index 06a627d64d6..fbb68e3b6a0 100644 --- a/packages/core-services/CHANGELOG.md +++ b/packages/core-services/CHANGELOG.md @@ -1,15 +1,5 @@ # @rocket.chat/core-services -## 0.3.1 - -### Patch Changes - -- Updated dependencies [4b0fa27e53] -- Updated dependencies [46fa2ff2cf] - - @rocket.chat/rest-typings@6.6.0 - - @rocket.chat/core-typings@6.6.0 - - @rocket.chat/models@0.0.25 - ## 0.3.0 ### Minor Changes diff --git a/packages/core-services/package.json b/packages/core-services/package.json index db298d2c1a5..533deb715e2 100644 --- a/packages/core-services/package.json +++ b/packages/core-services/package.json @@ -1,6 +1,6 @@ { "name": "@rocket.chat/core-services", - "version": "0.3.1", + "version": "0.3.0", "private": true, "devDependencies": { "@babel/core": "~7.22.20", diff --git a/packages/core-typings/CHANGELOG.md b/packages/core-typings/CHANGELOG.md index 832d3339c69..8229232f747 100644 --- a/packages/core-typings/CHANGELOG.md +++ b/packages/core-typings/CHANGELOG.md @@ -1,11 +1,5 @@ # @rocket.chat/core-typings -## 6.6.0 - -### Patch Changes - -- 46fa2ff2cf: Exceeding API calls when sending OTR messages - ## 6.5.0 ### Minor Changes diff --git a/packages/core-typings/package.json b/packages/core-typings/package.json index 1a3ae6b97c7..3eec6d26088 100644 --- a/packages/core-typings/package.json +++ b/packages/core-typings/package.json @@ -1,7 +1,7 @@ { "$schema": "https://json.schemastore.org/package", "name": "@rocket.chat/core-typings", - "version": "6.6.0", + "version": "6.5.0", "devDependencies": { "@rocket.chat/eslint-config": "workspace:^", "eslint": "~8.45.0", diff --git a/packages/cron/CHANGELOG.md b/packages/cron/CHANGELOG.md index c78c3b2182d..02a65ed1146 100644 --- a/packages/cron/CHANGELOG.md +++ b/packages/cron/CHANGELOG.md @@ -1,13 +1,5 @@ # @rocket.chat/cron -## 0.0.21 - -### Patch Changes - -- Updated dependencies [46fa2ff2cf] - - @rocket.chat/core-typings@6.6.0 - - @rocket.chat/models@0.0.25 - ## 0.0.20 ### Patch Changes diff --git a/packages/cron/package.json b/packages/cron/package.json index dcaae8a7ae8..2797c3cb34b 100644 --- a/packages/cron/package.json +++ b/packages/cron/package.json @@ -1,6 +1,6 @@ { "name": "@rocket.chat/cron", - "version": "0.0.21", + "version": "0.0.20", "private": true, "devDependencies": { "@types/jest": "~29.5.7", diff --git a/packages/fuselage-ui-kit/CHANGELOG.md b/packages/fuselage-ui-kit/CHANGELOG.md index f4cdb41f153..99cdbb4bd79 100644 --- a/packages/fuselage-ui-kit/CHANGELOG.md +++ b/packages/fuselage-ui-kit/CHANGELOG.md @@ -1,13 +1,5 @@ # Change Log -## 4.0.0 - -### Patch Changes - -- @rocket.chat/ui-contexts@4.0.0 -- @rocket.chat/gazzodown@4.0.0 -- @rocket.chat/ui-video-conf@4.0.0 - ## 3.0.0 ### Patch Changes diff --git a/packages/fuselage-ui-kit/package.json b/packages/fuselage-ui-kit/package.json index d5534df8fba..cc1a105e350 100644 --- a/packages/fuselage-ui-kit/package.json +++ b/packages/fuselage-ui-kit/package.json @@ -1,7 +1,7 @@ { "name": "@rocket.chat/fuselage-ui-kit", "private": true, - "version": "4.0.0", + "version": "3.0.0", "description": "UiKit elements for Rocket.Chat Apps built under Fuselage design system", "homepage": "https://rocketchat.github.io/Rocket.Chat.Fuselage/", "author": { @@ -48,9 +48,9 @@ "@rocket.chat/icons": "*", "@rocket.chat/prettier-config": "*", "@rocket.chat/styled": "*", - "@rocket.chat/ui-contexts": "4.0.0", + "@rocket.chat/ui-contexts": "3.0.0", "@rocket.chat/ui-kit": "*", - "@rocket.chat/ui-video-conf": "4.0.0", + "@rocket.chat/ui-video-conf": "3.0.0", "@tanstack/react-query": "*", "react": "*", "react-dom": "*" diff --git a/packages/gazzodown/CHANGELOG.md b/packages/gazzodown/CHANGELOG.md index eeb63aa997a..aa6e24f891d 100644 --- a/packages/gazzodown/CHANGELOG.md +++ b/packages/gazzodown/CHANGELOG.md @@ -1,14 +1,5 @@ # @rocket.chat/gazzodown -## 4.0.0 - -### Patch Changes - -- Updated dependencies [46fa2ff2cf] - - @rocket.chat/core-typings@6.6.0 - - @rocket.chat/ui-contexts@4.0.0 - - @rocket.chat/ui-client@4.0.0 - ## 3.0.0 ### Patch Changes diff --git a/packages/gazzodown/package.json b/packages/gazzodown/package.json index c5d203171df..faa009c231d 100644 --- a/packages/gazzodown/package.json +++ b/packages/gazzodown/package.json @@ -1,6 +1,6 @@ { "name": "@rocket.chat/gazzodown", - "version": "4.0.0", + "version": "3.0.0", "private": true, "devDependencies": { "@babel/core": "~7.22.20", @@ -65,14 +65,14 @@ "/dist" ], "peerDependencies": { - "@rocket.chat/core-typings": "6.6.0", + "@rocket.chat/core-typings": "6.5.0", "@rocket.chat/css-in-js": "*", "@rocket.chat/fuselage": "*", "@rocket.chat/fuselage-tokens": "*", "@rocket.chat/message-parser": "*", "@rocket.chat/styled": "*", - "@rocket.chat/ui-client": "4.0.0", - "@rocket.chat/ui-contexts": "4.0.0", + "@rocket.chat/ui-client": "3.0.0", + "@rocket.chat/ui-contexts": "3.0.0", "katex": "*", "react": "*" }, diff --git a/packages/instance-status/CHANGELOG.md b/packages/instance-status/CHANGELOG.md index 803d8b0ccd9..3bb16663fe3 100644 --- a/packages/instance-status/CHANGELOG.md +++ b/packages/instance-status/CHANGELOG.md @@ -1,11 +1,5 @@ # @rocket.chat/instance-status -## 0.0.25 - -### Patch Changes - -- @rocket.chat/models@0.0.25 - ## 0.0.24 ### Patch Changes diff --git a/packages/instance-status/package.json b/packages/instance-status/package.json index 34a98562d49..5072f71164e 100644 --- a/packages/instance-status/package.json +++ b/packages/instance-status/package.json @@ -1,6 +1,6 @@ { "name": "@rocket.chat/instance-status", - "version": "0.0.25", + "version": "0.0.24", "private": true, "devDependencies": { "@rocket.chat/eslint-config": "workspace:^", diff --git a/packages/livechat/CHANGELOG.md b/packages/livechat/CHANGELOG.md index 1f190238139..fa45e1d0b9d 100644 --- a/packages/livechat/CHANGELOG.md +++ b/packages/livechat/CHANGELOG.md @@ -1,11 +1,5 @@ # @rocket.chat/livechat Change Log -## 1.14.10 - -### Patch Changes - -- @rocket.chat/gazzodown@4.0.0 - ## 1.14.9 ### Patch Changes diff --git a/packages/livechat/package.json b/packages/livechat/package.json index f3ed1fb4c54..c83ae42ec9a 100644 --- a/packages/livechat/package.json +++ b/packages/livechat/package.json @@ -1,6 +1,6 @@ { "name": "@rocket.chat/livechat", - "version": "1.14.10", + "version": "1.14.9", "files": [ "/build" ], diff --git a/packages/model-typings/CHANGELOG.md b/packages/model-typings/CHANGELOG.md index f5a6884dea4..869b9d2d54c 100644 --- a/packages/model-typings/CHANGELOG.md +++ b/packages/model-typings/CHANGELOG.md @@ -1,12 +1,5 @@ # @rocket.chat/model-typings -## 0.2.1 - -### Patch Changes - -- Updated dependencies [46fa2ff2cf] - - @rocket.chat/core-typings@6.6.0 - ## 0.2.0 ### Minor Changes diff --git a/packages/model-typings/package.json b/packages/model-typings/package.json index 7aa61455019..2209995e21b 100644 --- a/packages/model-typings/package.json +++ b/packages/model-typings/package.json @@ -1,6 +1,6 @@ { "name": "@rocket.chat/model-typings", - "version": "0.2.1", + "version": "0.2.0", "private": true, "devDependencies": { "@types/jest": "~29.5.7", diff --git a/packages/models/CHANGELOG.md b/packages/models/CHANGELOG.md index 8861ba27c49..e5fdb024c33 100644 --- a/packages/models/CHANGELOG.md +++ b/packages/models/CHANGELOG.md @@ -1,11 +1,5 @@ # @rocket.chat/models -## 0.0.25 - -### Patch Changes - -- @rocket.chat/model-typings@0.2.1 - ## 0.0.24 ### Patch Changes diff --git a/packages/models/package.json b/packages/models/package.json index 3b19c6415c0..8856ceab3d4 100644 --- a/packages/models/package.json +++ b/packages/models/package.json @@ -1,6 +1,6 @@ { "name": "@rocket.chat/models", - "version": "0.0.25", + "version": "0.0.24", "private": true, "devDependencies": { "@types/jest": "~29.5.7", diff --git a/packages/rest-typings/CHANGELOG.md b/packages/rest-typings/CHANGELOG.md index 9c3da7c3327..3bc136c490b 100644 --- a/packages/rest-typings/CHANGELOG.md +++ b/packages/rest-typings/CHANGELOG.md @@ -1,16 +1,5 @@ # @rocket.chat/rest-typings -## 6.6.0 - -### Minor Changes - -- 4b0fa27e53: fix Federation Regression, builds service correctly - -### Patch Changes - -- Updated dependencies [46fa2ff2cf] - - @rocket.chat/core-typings@6.6.0 - ## 6.5.0 ### Minor Changes diff --git a/packages/rest-typings/package.json b/packages/rest-typings/package.json index f6762b4b992..e9c8386ca05 100644 --- a/packages/rest-typings/package.json +++ b/packages/rest-typings/package.json @@ -1,6 +1,6 @@ { "name": "@rocket.chat/rest-typings", - "version": "6.6.0", + "version": "6.5.0", "devDependencies": { "@rocket.chat/eslint-config": "workspace:^", "@types/jest": "~29.5.7", diff --git a/packages/ui-client/CHANGELOG.md b/packages/ui-client/CHANGELOG.md index 0f7304ff4c4..430507f15f5 100644 --- a/packages/ui-client/CHANGELOG.md +++ b/packages/ui-client/CHANGELOG.md @@ -1,11 +1,5 @@ # @rocket.chat/ui-client -## 4.0.0 - -### Patch Changes - -- @rocket.chat/ui-contexts@4.0.0 - ## 3.0.0 ### Patch Changes diff --git a/packages/ui-client/package.json b/packages/ui-client/package.json index fbaf921c308..f5011147776 100644 --- a/packages/ui-client/package.json +++ b/packages/ui-client/package.json @@ -1,6 +1,6 @@ { "name": "@rocket.chat/ui-client", - "version": "4.0.0", + "version": "3.0.0", "private": true, "devDependencies": { "@babel/core": "~7.22.20", @@ -61,7 +61,7 @@ "@rocket.chat/fuselage": "*", "@rocket.chat/fuselage-hooks": "*", "@rocket.chat/icons": "*", - "@rocket.chat/ui-contexts": "4.0.0", + "@rocket.chat/ui-contexts": "3.0.0", "react": "~17.0.2" }, "volta": { diff --git a/packages/ui-contexts/CHANGELOG.md b/packages/ui-contexts/CHANGELOG.md index 446996bf67c..6e0f2022b88 100644 --- a/packages/ui-contexts/CHANGELOG.md +++ b/packages/ui-contexts/CHANGELOG.md @@ -1,20 +1,5 @@ # @rocket.chat/ui-contexts -## 4.0.0 - -### Patch Changes - -- Updated dependencies [4b0fa27e53] -- Updated dependencies [46fa2ff2cf] -- Updated dependencies [f6a525a444] -- Updated dependencies [1233bffa8c] -- Updated dependencies [1233bffa8c] -- Updated dependencies [1233bffa8c] -- Updated dependencies [b42aa49fbd] - - @rocket.chat/rest-typings@6.6.0 - - @rocket.chat/ddp-client@0.2.10 - - @rocket.chat/core-typings@6.6.0 - ## 3.0.0 ### Patch Changes diff --git a/packages/ui-contexts/package.json b/packages/ui-contexts/package.json index f2f258a111d..aa8dd18a8b7 100644 --- a/packages/ui-contexts/package.json +++ b/packages/ui-contexts/package.json @@ -1,6 +1,6 @@ { "name": "@rocket.chat/ui-contexts", - "version": "4.0.0", + "version": "3.0.0", "private": true, "devDependencies": { "@rocket.chat/core-typings": "workspace:^", diff --git a/packages/ui-video-conf/CHANGELOG.md b/packages/ui-video-conf/CHANGELOG.md index 24e7e1304f5..8a18b550da0 100644 --- a/packages/ui-video-conf/CHANGELOG.md +++ b/packages/ui-video-conf/CHANGELOG.md @@ -1,11 +1,5 @@ # @rocket.chat/ui-video-conf -## 4.0.0 - -### Patch Changes - -- @rocket.chat/ui-contexts@4.0.0 - ## 3.0.0 ### Patch Changes diff --git a/packages/ui-video-conf/package.json b/packages/ui-video-conf/package.json index 794691ec0b2..108a7ec0cde 100644 --- a/packages/ui-video-conf/package.json +++ b/packages/ui-video-conf/package.json @@ -1,6 +1,6 @@ { "name": "@rocket.chat/ui-video-conf", - "version": "4.0.0", + "version": "3.0.0", "private": true, "devDependencies": { "@babel/core": "~7.22.20", @@ -35,7 +35,7 @@ "@rocket.chat/fuselage-hooks": "*", "@rocket.chat/icons": "*", "@rocket.chat/styled": "*", - "@rocket.chat/ui-contexts": "4.0.0", + "@rocket.chat/ui-contexts": "3.0.0", "react": "^17.0.2", "react-dom": "^17.0.2" }, diff --git a/packages/uikit-playground/CHANGELOG.md b/packages/uikit-playground/CHANGELOG.md index 4745f4596d4..260935c50f2 100644 --- a/packages/uikit-playground/CHANGELOG.md +++ b/packages/uikit-playground/CHANGELOG.md @@ -1,12 +1,5 @@ # @rocket.chat/uikit-playground -## 0.2.10 - -### Patch Changes - -- @rocket.chat/ui-contexts@4.0.0 -- @rocket.chat/fuselage-ui-kit@4.0.0 - ## 0.2.9 ### Patch Changes diff --git a/packages/uikit-playground/package.json b/packages/uikit-playground/package.json index 83015854f23..6afbd34e675 100644 --- a/packages/uikit-playground/package.json +++ b/packages/uikit-playground/package.json @@ -1,7 +1,7 @@ { "name": "@rocket.chat/uikit-playground", "private": true, - "version": "0.2.10", + "version": "0.2.9", "type": "module", "scripts": { "dev": "vite", diff --git a/packages/web-ui-registration/CHANGELOG.md b/packages/web-ui-registration/CHANGELOG.md index b91a7be2e80..76b400173d1 100644 --- a/packages/web-ui-registration/CHANGELOG.md +++ b/packages/web-ui-registration/CHANGELOG.md @@ -1,11 +1,5 @@ # @rocket.chat/web-ui-registration -## 4.0.0 - -### Patch Changes - -- @rocket.chat/ui-contexts@4.0.0 - ## 3.0.0 ### Patch Changes diff --git a/packages/web-ui-registration/package.json b/packages/web-ui-registration/package.json index 8dcf0de9a9d..122b0f06430 100644 --- a/packages/web-ui-registration/package.json +++ b/packages/web-ui-registration/package.json @@ -1,6 +1,6 @@ { "name": "@rocket.chat/web-ui-registration", - "version": "4.0.0", + "version": "3.0.0", "private": true, "homepage": "https://rocket.chat", "main": "./dist/index.js", @@ -51,7 +51,7 @@ "peerDependencies": { "@rocket.chat/layout": "*", "@rocket.chat/tools": "*", - "@rocket.chat/ui-contexts": "4.0.0", + "@rocket.chat/ui-contexts": "3.0.0", "@tanstack/react-query": "*", "react": "*", "react-hook-form": "*", -- GitLab From 991ecaec8e80997a8c2a3042ff45226a5a3e8ead Mon Sep 17 00:00:00 2001 From: Diego Sampaio Date: Tue, 26 Dec 2023 14:03:07 -0300 Subject: [PATCH 079/329] chore: fix changeset --- .changeset/nasty-islands-trade.md | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/.changeset/nasty-islands-trade.md b/.changeset/nasty-islands-trade.md index b6df94282dd..021fd157c33 100644 --- a/.changeset/nasty-islands-trade.md +++ b/.changeset/nasty-islands-trade.md @@ -1,6 +1,6 @@ --- -'@rocket.chat/rest-typings': minor -'@rocket.chat/meteor': minor +'@rocket.chat/rest-typings': patch +'@rocket.chat/meteor': patch --- fix Federation Regression, builds service correctly -- GitLab From e12727ef5489b09bdd0f4d88430ac2de7020b3e1 Mon Sep 17 00:00:00 2001 From: rocketchat-github-ci Date: Tue, 26 Dec 2023 17:05:19 +0000 Subject: [PATCH 080/329] Release 6.5.1 [no ci] --- .changeset/bump-patch-1702298298384.md | 5 -- .changeset/fair-suns-study.md | 5 -- .changeset/fresh-radios-whisper.md | 5 -- .changeset/gold-stingrays-compete.md | 5 -- .changeset/lucky-apricots-change.md | 5 -- .changeset/lucky-cycles-leave.md | 5 -- .changeset/nasty-islands-trade.md | 6 --- .changeset/new-avocados-sort.md | 7 --- .changeset/popular-beds-heal.md | 7 --- .changeset/real-items-tan.md | 5 -- .changeset/remove-license-31189.md | 5 -- .changeset/resume-login-31301.md | 5 -- .changeset/save-token-31278.md | 5 -- .changeset/sharp-rings-smash.md | 6 --- .changeset/sour-kids-heal.md | 6 --- .changeset/spicy-kiwis-argue.md | 5 -- .changeset/spotty-suns-grin.md | 5 -- .changeset/tame-drinks-yell.md | 5 -- .changeset/violet-pears-cry.md | 5 -- apps/meteor/.docker/Dockerfile.rhel | 2 +- apps/meteor/CHANGELOG.md | 47 +++++++++++++++++++ apps/meteor/app/utils/rocketchat.info | 2 +- apps/meteor/ee/server/services/CHANGELOG.md | 12 +++++ apps/meteor/ee/server/services/package.json | 2 +- apps/meteor/package.json | 2 +- ee/apps/account-service/CHANGELOG.md | 12 +++++ ee/apps/account-service/package.json | 2 +- ee/apps/authorization-service/CHANGELOG.md | 12 +++++ ee/apps/authorization-service/package.json | 2 +- ee/apps/ddp-streamer/CHANGELOG.md | 14 ++++++ ee/apps/ddp-streamer/package.json | 2 +- ee/apps/omnichannel-transcript/CHANGELOG.md | 12 +++++ ee/apps/omnichannel-transcript/package.json | 2 +- ee/apps/presence-service/CHANGELOG.md | 11 +++++ ee/apps/presence-service/package.json | 2 +- ee/apps/queue-worker/CHANGELOG.md | 11 +++++ ee/apps/queue-worker/package.json | 2 +- ee/apps/stream-hub-service/CHANGELOG.md | 10 ++++ ee/apps/stream-hub-service/package.json | 2 +- ee/packages/api-client/CHANGELOG.md | 9 ++++ ee/packages/api-client/package.json | 2 +- ee/packages/ddp-client/CHANGELOG.md | 14 ++++++ ee/packages/ddp-client/package.json | 2 +- ee/packages/license/CHANGELOG.md | 7 +++ ee/packages/license/package.json | 2 +- ee/packages/omnichannel-services/CHANGELOG.md | 13 +++++ ee/packages/omnichannel-services/package.json | 2 +- ee/packages/pdf-worker/CHANGELOG.md | 7 +++ ee/packages/pdf-worker/package.json | 2 +- ee/packages/presence/CHANGELOG.md | 9 ++++ ee/packages/presence/package.json | 2 +- package.json | 2 +- packages/core-services/CHANGELOG.md | 10 ++++ packages/core-services/package.json | 2 +- packages/core-typings/CHANGELOG.md | 6 +++ packages/core-typings/package.json | 2 +- packages/cron/CHANGELOG.md | 8 ++++ packages/cron/package.json | 2 +- packages/fuselage-ui-kit/CHANGELOG.md | 8 ++++ packages/fuselage-ui-kit/package.json | 6 +-- packages/gazzodown/CHANGELOG.md | 9 ++++ packages/gazzodown/package.json | 8 ++-- packages/instance-status/CHANGELOG.md | 6 +++ packages/instance-statu