Skip to content

Commit

Permalink
feat: Disable some message menu items for encrypted messages. (#32559)
Browse files Browse the repository at this point in the history
  • Loading branch information
yash-rajpal authored Jun 19, 2024
1 parent cd97aac commit 45dc3d5
Show file tree
Hide file tree
Showing 19 changed files with 113 additions and 33 deletions.
6 changes: 6 additions & 0 deletions .changeset/plenty-buses-kneel.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
---
'@rocket.chat/i18n': minor
'@rocket.chat/meteor': minor
---

Disable "Reply in direct message", "Copy link" and "Forward message" message menu items for encrypted messages as they don't apply to encrypted messages and also disable apps menu items and show a warning.
3 changes: 2 additions & 1 deletion apps/meteor/app/ui-utils/client/lib/MessageAction.ts
Original file line number Diff line number Diff line change
Expand Up @@ -25,7 +25,7 @@ export type MessageActionContext =

type MessageActionType = 'communication' | 'interaction' | 'duplication' | 'apps' | 'management';

type MessageActionConditionProps = {
export type MessageActionConditionProps = {
message: IMessage;
user: IUser | undefined;
room: IRoom;
Expand Down Expand Up @@ -65,6 +65,7 @@ export type MessageActionConfig = {
) => any;
condition?: (props: MessageActionConditionProps) => Promise<boolean> | boolean;
type?: MessageActionType;
disabled?: (props: MessageActionConditionProps) => boolean;
};

class MessageAction {
Expand Down
11 changes: 10 additions & 1 deletion apps/meteor/app/ui-utils/client/lib/messageActionDefault.ts
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
import type { IMessage } from '@rocket.chat/core-typings';
import { isRoomFederated } from '@rocket.chat/core-typings';
import { isE2EEMessage, isRoomFederated } from '@rocket.chat/core-typings';
import { Meteor } from 'meteor/meteor';
import moment from 'moment';

Expand Down Expand Up @@ -63,6 +63,9 @@ Meteor.startup(async () => {
},
order: 0,
group: 'menu',
disabled({ message }) {
return isE2EEMessage(message);
},
});

MessageAction.addButton({
Expand All @@ -87,6 +90,9 @@ Meteor.startup(async () => {
},
order: 0,
group: 'message',
disabled({ message }) {
return isE2EEMessage(message);
},
});

MessageAction.addButton({
Expand Down Expand Up @@ -139,6 +145,9 @@ Meteor.startup(async () => {
},
order: 5,
group: 'menu',
disabled({ message }) {
return isE2EEMessage(message);
},
});

MessageAction.addButton({
Expand Down
2 changes: 1 addition & 1 deletion apps/meteor/client/components/GenericMenu/GenericMenu.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -41,7 +41,7 @@ const GenericMenu = ({ title, icon = 'menu', disabled, onAction, ...props }: Gen

const hasIcon = itemsList.some(({ icon }) => icon);
const handleItems = (items: GenericMenuItemProps[]) =>
hasIcon ? items.map((item) => ({ ...item, gap: !item.icon && !item.status })) : items;
hasIcon ? items.map((item) => ({ ...item, gap: item.gap ?? (!item.icon && !item.status) })) : items;

const isMenuEmpty = !(sections && sections.length > 0) && !(items && items.length > 0);

Expand Down
5 changes: 3 additions & 2 deletions apps/meteor/client/components/GenericMenu/GenericMenuItem.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -12,14 +12,15 @@ export type GenericMenuItemProps = {
disabled?: boolean;
description?: ReactNode;
gap?: boolean;
tooltip?: string;
};

const GenericMenuItem = ({ icon, content, addon, status, gap }: GenericMenuItemProps) => (
const GenericMenuItem = ({ icon, content, addon, status, gap, tooltip }: GenericMenuItemProps) => (
<>
{gap && <MenuItemColumn />}
{icon && <MenuItemIcon name={icon} />}
{status && <MenuItemColumn>{status}</MenuItemColumn>}
{content && <MenuItemContent>{content}</MenuItemContent>}
{content && <MenuItemContent title={tooltip}>{content}</MenuItemContent>}
{addon && <MenuItemInput>{addon}</MenuItemInput>}
</>
);
Expand Down
Original file line number Diff line number Diff line change
@@ -1,8 +1,9 @@
import { useUniqueId } from '@rocket.chat/fuselage-hooks';
import { useTranslation } from '@rocket.chat/ui-contexts';
import type { MouseEvent, ReactElement } from 'react';
import React from 'react';

import type { MessageActionConfig } from '../../../../app/ui-utils/client/lib/MessageAction';
import type { MessageActionConditionProps, MessageActionConfig } from '../../../../app/ui-utils/client/lib/MessageAction';
import GenericMenu from '../../GenericMenu/GenericMenu';
import type { GenericMenuItemProps } from '../../GenericMenu/GenericMenuItem';

Expand All @@ -19,11 +20,13 @@ type MessageActionSection = {
type MessageActionMenuProps = {
onChangeMenuVisibility: (visible: boolean) => void;
options: MessageActionConfigOption[];
context: MessageActionConditionProps;
isMessageEncrypted: boolean;
};

const MessageActionMenu = ({ options, onChangeMenuVisibility }: MessageActionMenuProps): ReactElement => {
const MessageActionMenu = ({ options, onChangeMenuVisibility, context, isMessageEncrypted }: MessageActionMenuProps): ReactElement => {
const t = useTranslation();

const id = useUniqueId();
const groupOptions = options
.map((option) => ({
variant: option.color === 'alert' ? 'danger' : '',
Expand All @@ -32,6 +35,9 @@ const MessageActionMenu = ({ options, onChangeMenuVisibility }: MessageActionMen
content: t(option.label),
onClick: option.action,
type: option.type,
...(option.disabled && { disabled: option?.disabled?.(context) }),
...(option.disabled &&
option?.disabled?.(context) && { tooltip: t('Action_not_available_encrypted_content', { action: t(option.label) }) }),
}))
.reduce((acc, option) => {
const group = option.type ? option.type : '';
Expand All @@ -44,7 +50,31 @@ const MessageActionMenu = ({ options, onChangeMenuVisibility }: MessageActionMen
acc.push(newSection);

return acc;
}, [] as unknown as MessageActionSection[]);
}, [] as unknown as MessageActionSection[])
.map((section) => {
if (section.id !== 'apps') {
return section;
}

if (!isMessageEncrypted) {
return section;
}

return {
id: 'apps',
title: t('Apps'),
items: [
{
content: t('Unavailable'),
type: 'apps',
id,
disabled: true,
gap: false,
tooltip: t('Action_not_available_encrypted_content', { action: t('Apps') }),
},
],
};
});

return (
<GenericMenu
Expand Down
11 changes: 9 additions & 2 deletions apps/meteor/client/components/message/toolbar/MessageToolbar.tsx
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
import { useToolbar } from '@react-aria/toolbar';
import type { IMessage, IRoom, ISubscription, ITranslatedMessage } from '@rocket.chat/core-typings';
import { isThreadMessage, isRoomFederated, isVideoConfMessage } from '@rocket.chat/core-typings';
import { isThreadMessage, isRoomFederated, isVideoConfMessage, isE2EEMessage } from '@rocket.chat/core-typings';
import { MessageToolbar as FuselageMessageToolbar, MessageToolbarItem } from '@rocket.chat/fuselage';
import { useFeaturePreview } from '@rocket.chat/ui-client';
import { useUser, useSettings, useTranslation, useMethod, useLayoutHiddenActions } from '@rocket.chat/ui-contexts';
Expand Down Expand Up @@ -125,9 +125,14 @@ const MessageToolbar = ({
onClick={(e): void => action.action(e, { message, tabbar: toolbox, room, chat, autoTranslateOptions })}
key={action.id}
icon={action.icon}
title={t(action.label)}
title={
action?.disabled?.({ message, room, user, subscription, settings: mapSettings, chat, context })
? t('Action_not_available_encrypted_content', { action: t(action.label) })
: t(action.label)
}
data-qa-id={action.label}
data-qa-type='message-action-menu'
disabled={action?.disabled?.({ message, room, user, subscription, settings: mapSettings, chat, context })}
/>
))}
{actionsQueryResult.isSuccess && actionsQueryResult.data.menu.length > 0 && (
Expand All @@ -138,6 +143,8 @@ const MessageToolbar = ({
}))}
onChangeMenuVisibility={onChangeMenuVisibility}
data-qa-type='message-action-menu-options'
context={{ message, room, user, subscription, settings: mapSettings, chat, context }}
isMessageEncrypted={isE2EEMessage(message)}
/>
)}
</FuselageMessageToolbar>
Expand Down
2 changes: 1 addition & 1 deletion apps/meteor/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -242,7 +242,7 @@
"@rocket.chat/favicon": "workspace:^",
"@rocket.chat/forked-matrix-appservice-bridge": "^4.0.2",
"@rocket.chat/forked-matrix-bot-sdk": "^0.6.0-beta.3",
"@rocket.chat/fuselage": "^0.54.2",
"@rocket.chat/fuselage": "^0.54.3",
"@rocket.chat/fuselage-hooks": "^0.33.1",
"@rocket.chat/fuselage-polyfills": "~0.31.25",
"@rocket.chat/fuselage-toastbar": "^0.31.26",
Expand Down
25 changes: 25 additions & 0 deletions apps/meteor/tests/e2e/e2e-encryption.spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -213,6 +213,31 @@ test.describe.serial('e2e-encryption', () => {
await expect(poHomeChannel.content.mainThreadMessageText.locator('.rcx-icon--name-key')).toBeVisible();
});

test('expect create a private encrypted channel and check disabled message menu actions on an encrypted message', async ({ page }) => {
const channelName = faker.string.uuid();

await poHomeChannel.sidenav.createEncryptedChannel(channelName);

await expect(page).toHaveURL(`/group/${channelName}`);

await poHomeChannel.dismissToast();

await expect(poHomeChannel.content.encryptedRoomHeaderIcon).toBeVisible();

await poHomeChannel.content.sendMessage('This is an encrypted message.');

await expect(poHomeChannel.content.lastUserMessageBody).toHaveText('This is an encrypted message.');
await expect(poHomeChannel.content.lastUserMessage.locator('.rcx-icon--name-key')).toBeVisible();

await page.locator('[data-qa-type="message"]').last().hover();
await expect(page.locator('role=button[name="Forward message"]')).toBeDisabled();

await poHomeChannel.content.openLastMessageMenu();

await expect(page.locator('role=menuitem[name="Reply in direct message"]')).toHaveClass(/disabled/);
await expect(page.locator('role=menuitem[name="Copy link"]')).toHaveClass(/disabled/);
});

test('expect create a private channel, encrypt it and send an encrypted message', async ({ page }) => {
const channelName = faker.string.uuid();

Expand Down
2 changes: 1 addition & 1 deletion ee/packages/ui-theming/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@
"private": true,
"devDependencies": {
"@rocket.chat/css-in-js": "~0.31.25",
"@rocket.chat/fuselage": "^0.54.2",
"@rocket.chat/fuselage": "^0.54.3",
"@rocket.chat/fuselage-hooks": "^0.33.1",
"@rocket.chat/icons": "^0.36.0",
"@rocket.chat/ui-contexts": "workspace:~",
Expand Down
2 changes: 1 addition & 1 deletion packages/fuselage-ui-kit/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -66,7 +66,7 @@
"@rocket.chat/apps-engine": "alpha",
"@rocket.chat/core-typings": "workspace:^",
"@rocket.chat/eslint-config": "workspace:^",
"@rocket.chat/fuselage": "^0.54.2",
"@rocket.chat/fuselage": "^0.54.3",
"@rocket.chat/fuselage-hooks": "^0.33.1",
"@rocket.chat/fuselage-polyfills": "~0.31.25",
"@rocket.chat/icons": "^0.36.0",
Expand Down
2 changes: 1 addition & 1 deletion packages/gazzodown/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@
"@babel/core": "~7.22.20",
"@rocket.chat/core-typings": "workspace:^",
"@rocket.chat/css-in-js": "~0.31.25",
"@rocket.chat/fuselage": "^0.54.2",
"@rocket.chat/fuselage": "^0.54.3",
"@rocket.chat/fuselage-tokens": "^0.33.1",
"@rocket.chat/message-parser": "workspace:^",
"@rocket.chat/styled": "~0.31.25",
Expand Down
1 change: 1 addition & 0 deletions packages/i18n/src/locales/en.i18n.json
Original file line number Diff line number Diff line change
Expand Up @@ -304,6 +304,7 @@
"Action_required": "Action required",
"Action_Available_After_Custom_Content_Added": "This action will become available after the custom content has been added",
"Action_Available_After_Custom_Content_Added_And_Visible": "This action will become available after the custom content has been added and made visible to everyone",
"Action_not_available_encrypted_content": "{{action}} not available on encrypted content",
"Activate": "Activate",
"Active": "Active",
"Active_users": "Active users",
Expand Down
2 changes: 1 addition & 1 deletion packages/ui-avatar/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@
"private": true,
"devDependencies": {
"@babel/core": "~7.22.20",
"@rocket.chat/fuselage": "^0.54.2",
"@rocket.chat/fuselage": "^0.54.3",
"@rocket.chat/ui-contexts": "workspace:^",
"@types/babel__core": "~7.20.3",
"@types/react": "~17.0.69",
Expand Down
2 changes: 1 addition & 1 deletion packages/ui-client/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@
"@babel/core": "~7.22.20",
"@react-aria/toolbar": "^3.0.0-beta.1",
"@rocket.chat/css-in-js": "~0.31.25",
"@rocket.chat/fuselage": "^0.54.2",
"@rocket.chat/fuselage": "^0.54.3",
"@rocket.chat/fuselage-hooks": "^0.33.1",
"@rocket.chat/icons": "^0.36.0",
"@rocket.chat/mock-providers": "workspace:^",
Expand Down
2 changes: 1 addition & 1 deletion packages/ui-composer/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@
"@babel/core": "~7.22.20",
"@react-aria/toolbar": "^3.0.0-beta.1",
"@rocket.chat/eslint-config": "workspace:^",
"@rocket.chat/fuselage": "^0.54.2",
"@rocket.chat/fuselage": "^0.54.3",
"@rocket.chat/icons": "^0.36.0",
"@storybook/addon-actions": "~6.5.16",
"@storybook/addon-docs": "~6.5.16",
Expand Down
2 changes: 1 addition & 1 deletion packages/ui-video-conf/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@
"@babel/core": "~7.22.20",
"@rocket.chat/css-in-js": "~0.31.25",
"@rocket.chat/eslint-config": "workspace:^",
"@rocket.chat/fuselage": "^0.54.2",
"@rocket.chat/fuselage": "^0.54.3",
"@rocket.chat/fuselage-hooks": "^0.33.1",
"@rocket.chat/icons": "^0.36.0",
"@rocket.chat/styled": "~0.31.25",
Expand Down
2 changes: 1 addition & 1 deletion packages/uikit-playground/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,7 @@
"@codemirror/tooltip": "^0.19.16",
"@lezer/highlight": "^1.1.6",
"@rocket.chat/css-in-js": "~0.31.25",
"@rocket.chat/fuselage": "^0.54.2",
"@rocket.chat/fuselage": "^0.54.3",
"@rocket.chat/fuselage-hooks": "^0.33.1",
"@rocket.chat/fuselage-polyfills": "~0.31.25",
"@rocket.chat/fuselage-toastbar": "^0.31.26",
Expand Down
Loading

0 comments on commit 45dc3d5

Please sign in to comment.