From ff04c19f2de6531af06f0e8c5dbe3e76c3e72aba Mon Sep 17 00:00:00 2001 From: Douglas Fabris Date: Fri, 20 Dec 2024 17:40:51 -0300 Subject: [PATCH] feat: Disable OTR messages selection when exporting messages (#34220) Co-authored-by: gabriellsh <40830821+gabriellsh@users.noreply.github.com> --- .changeset/pretty-islands-wink.md | 5 ++ .../message/variants/RoomMessage.tsx | 8 ++-- .../message/variants/ThreadMessagePreview.tsx | 10 +++- .../contexts/SelectedMessagesContext.tsx | 6 +-- .../ExportMessages/ExportMessages.tsx | 7 +++ apps/meteor/tests/e2e/otr.spec.ts | 46 +++++++++++++++++++ .../page-objects/fragments/home-content.ts | 4 ++ .../fragments/home-flextab-otr.ts | 21 +++++++++ .../page-objects/fragments/home-flextab.ts | 4 ++ packages/i18n/src/locales/en.i18n.json | 1 + 10 files changed, 104 insertions(+), 8 deletions(-) create mode 100644 .changeset/pretty-islands-wink.md create mode 100644 apps/meteor/tests/e2e/otr.spec.ts create mode 100644 apps/meteor/tests/e2e/page-objects/fragments/home-flextab-otr.ts diff --git a/.changeset/pretty-islands-wink.md b/.changeset/pretty-islands-wink.md new file mode 100644 index 000000000000..e9de264504b0 --- /dev/null +++ b/.changeset/pretty-islands-wink.md @@ -0,0 +1,5 @@ +--- +'@rocket.chat/meteor': minor +--- + +Disables OTR messages selection when exporting messages diff --git a/apps/meteor/client/components/message/variants/RoomMessage.tsx b/apps/meteor/client/components/message/variants/RoomMessage.tsx index 90bc2236e837..54d3dcdea539 100644 --- a/apps/meteor/client/components/message/variants/RoomMessage.tsx +++ b/apps/meteor/client/components/message/variants/RoomMessage.tsx @@ -55,8 +55,10 @@ const RoomMessage = ({ const { openUserCard, triggerProps } = useUserCard(); const selecting = useIsSelecting(); + const isOTRMessage = message.t === 'otr' || message.t === 'otr-ack'; + const toggleSelected = useToggleSelect(message._id); - const selected = useIsSelectedMessage(message._id); + const selected = useIsSelectedMessage(message._id, isOTRMessage); useCountSelected(); @@ -70,7 +72,7 @@ const RoomMessage = ({ aria-roledescription={t('message')} tabIndex={0} aria-labelledby={`${message._id}-displayName ${message._id}-time ${message._id}-content ${message._id}-read-status`} - onClick={selecting ? toggleSelected : undefined} + onClick={selecting && !isOTRMessage ? toggleSelected : undefined} isSelected={selected} isEditing={editing} isPending={message.temp} @@ -99,7 +101,7 @@ const RoomMessage = ({ {...triggerProps} /> )} - {selecting && } + {selecting && } {sequential && } diff --git a/apps/meteor/client/components/message/variants/ThreadMessagePreview.tsx b/apps/meteor/client/components/message/variants/ThreadMessagePreview.tsx index f7fa072a7406..146bda990d52 100644 --- a/apps/meteor/client/components/message/variants/ThreadMessagePreview.tsx +++ b/apps/meteor/client/components/message/variants/ThreadMessagePreview.tsx @@ -45,8 +45,10 @@ const ThreadMessagePreview = ({ message, showUserAvatar, sequential, ...props }: const { t } = useTranslation(); const isSelecting = useIsSelecting(); + const isOTRMessage = message.t === 'otr' || message.t === 'otr-ack'; + const toggleSelected = useToggleSelect(message._id); - const isSelected = useIsSelectedMessage(message._id); + const isSelected = useIsSelectedMessage(message._id, isOTRMessage); useCountSelected(); const messageType = parentMessage.isSuccess ? MessageTypes.getType(parentMessage.data) : null; @@ -65,6 +67,10 @@ const ThreadMessagePreview = ({ message, showUserAvatar, sequential, ...props }: return goToThread({ rid: message.rid, tmid: message.tmid, msg: message._id }); } + if (isOTRMessage) { + return; + } + return toggleSelected(); }; @@ -117,7 +123,7 @@ const ThreadMessagePreview = ({ message, showUserAvatar, sequential, ...props }: size='x18' /> )} - {isSelecting && } + {isSelecting && } diff --git a/apps/meteor/client/views/room/MessageList/contexts/SelectedMessagesContext.tsx b/apps/meteor/client/views/room/MessageList/contexts/SelectedMessagesContext.tsx index 800f7155082e..527ea332cc14 100644 --- a/apps/meteor/client/views/room/MessageList/contexts/SelectedMessagesContext.tsx +++ b/apps/meteor/client/views/room/MessageList/contexts/SelectedMessagesContext.tsx @@ -11,7 +11,7 @@ export const SelectedMessageContext = createContext({ selectedMessageStore, } as SelectMessageContextValue); -export const useIsSelectedMessage = (mid: string): boolean => { +export const useIsSelectedMessage = (mid: string, omit?: boolean): boolean => { const { selectedMessageStore } = useContext(SelectedMessageContext); const subscribe = useCallback( @@ -24,14 +24,14 @@ export const useIsSelectedMessage = (mid: string): boolean => { const isSelected = useSyncExternalStore(subscribe, getSnapshot); useEffect(() => { - if (isSelected) { + if (isSelected || omit) { return; } selectedMessageStore.addAvailableMessage(mid); return () => selectedMessageStore.removeAvailableMessage(mid); - }, [mid, selectedMessageStore, isSelected]); + }, [mid, selectedMessageStore, isSelected, omit]); return isSelected; }; diff --git a/apps/meteor/client/views/room/contextualBar/ExportMessages/ExportMessages.tsx b/apps/meteor/client/views/room/contextualBar/ExportMessages/ExportMessages.tsx index 8736c8a1bec3..c05faece42e1 100644 --- a/apps/meteor/client/views/room/contextualBar/ExportMessages/ExportMessages.tsx +++ b/apps/meteor/client/views/room/contextualBar/ExportMessages/ExportMessages.tsx @@ -176,6 +176,13 @@ const ExportMessages = () => {
+ {room.createdOTR && ( + + + {t('OTR_messages_cannot_be_exported')} + + + )} {t('Method')} diff --git a/apps/meteor/tests/e2e/otr.spec.ts b/apps/meteor/tests/e2e/otr.spec.ts new file mode 100644 index 000000000000..b6ff4789e0c2 --- /dev/null +++ b/apps/meteor/tests/e2e/otr.spec.ts @@ -0,0 +1,46 @@ +import { Users } from './fixtures/userStates'; +import { HomeChannel } from './page-objects'; +import { createDirectMessage } from './utils'; +import { test, expect } from './utils/test'; + +test.use({ storageState: Users.admin.state }); + +test.describe.serial('OTR', () => { + let poHomeChannel: HomeChannel; + + test.beforeEach(async ({ page, api }) => { + await createDirectMessage(api); + poHomeChannel = new HomeChannel(page); + + await page.goto('/home'); + }); + + test('should not allow export OTR messages', async ({ browser }) => { + const user1Page = await browser.newPage({ storageState: Users.user1.state }); + const user1Channel = new HomeChannel(user1Page); + + await test.step('log in user1', async () => { + await user1Page.goto(`/direct/${Users.admin.data.username}`); + await user1Channel.content.waitForChannel(); + }); + + await test.step('invite OTR with user1', async () => { + await poHomeChannel.sidenav.openChat(Users.user1.data.username); + await poHomeChannel.tabs.kebab.click({ force: true }); + await poHomeChannel.tabs.btnEnableOTR.click({ force: true }); + await poHomeChannel.tabs.otr.btnStartOTR.click(); + }); + + await test.step('accept handshake with user1', async () => { + await user1Channel.tabs.otr.btnAcceptOTR.click(); + }); + + await poHomeChannel.content.sendMessage('hello OTR'); + await poHomeChannel.tabs.kebab.click({ force: true }); + await poHomeChannel.tabs.btnExportMessages.click(); + await poHomeChannel.content.getMessageByText('hello OTR').click(); + await expect(poHomeChannel.content.btnClearSelection).toBeDisabled(); + + await user1Page.close(); + }); +}); 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 088d8dd3d647..2a66a899f3ce 100644 --- a/apps/meteor/tests/e2e/page-objects/fragments/home-content.ts +++ b/apps/meteor/tests/e2e/page-objects/fragments/home-content.ts @@ -417,4 +417,8 @@ export class HomeContent { await this.page.getByRole('dialog').getByRole('textbox', { name: 'Message' }).fill(text); await this.page.getByRole('dialog').getByRole('button', { name: 'Send', exact: true }).click(); } + + get btnClearSelection() { + return this.page.getByRole('button', { name: 'Clear selection' }); + } } diff --git a/apps/meteor/tests/e2e/page-objects/fragments/home-flextab-otr.ts b/apps/meteor/tests/e2e/page-objects/fragments/home-flextab-otr.ts new file mode 100644 index 000000000000..1a310f76f2f4 --- /dev/null +++ b/apps/meteor/tests/e2e/page-objects/fragments/home-flextab-otr.ts @@ -0,0 +1,21 @@ +import type { Locator, Page } from '@playwright/test'; + +export class HomeFlextabOtr { + private readonly page: Page; + + constructor(page: Page) { + this.page = page; + } + + get otrDialog(): Locator { + return this.page.getByRole('dialog', { name: 'OTR' }); + } + + get btnStartOTR(): Locator { + return this.otrDialog.getByRole('button', { name: 'Start OTR' }); + } + + get btnAcceptOTR(): Locator { + return this.page.getByRole('dialog').getByRole('button', { name: 'Yes' }); + } +} diff --git a/apps/meteor/tests/e2e/page-objects/fragments/home-flextab.ts b/apps/meteor/tests/e2e/page-objects/fragments/home-flextab.ts index a19cf72d5172..52dc165e9a43 100644 --- a/apps/meteor/tests/e2e/page-objects/fragments/home-flextab.ts +++ b/apps/meteor/tests/e2e/page-objects/fragments/home-flextab.ts @@ -4,6 +4,7 @@ import { HomeFlextabChannels } from './home-flextab-channels'; import { HomeFlextabExportMessages } from './home-flextab-exportMessages'; import { HomeFlextabMembers } from './home-flextab-members'; import { HomeFlextabNotificationPreferences } from './home-flextab-notificationPreferences'; +import { HomeFlextabOtr } from './home-flextab-otr'; import { HomeFlextabRoom } from './home-flextab-room'; export class HomeFlextab { @@ -17,6 +18,8 @@ export class HomeFlextab { readonly notificationPreferences: HomeFlextabNotificationPreferences; + readonly otr: HomeFlextabOtr; + readonly exportMessages: HomeFlextabExportMessages; constructor(page: Page) { @@ -25,6 +28,7 @@ export class HomeFlextab { this.room = new HomeFlextabRoom(page); this.channels = new HomeFlextabChannels(page); this.notificationPreferences = new HomeFlextabNotificationPreferences(page); + this.otr = new HomeFlextabOtr(page); this.exportMessages = new HomeFlextabExportMessages(page); } diff --git a/packages/i18n/src/locales/en.i18n.json b/packages/i18n/src/locales/en.i18n.json index e9c452eaa45c..2b955fc6c327 100644 --- a/packages/i18n/src/locales/en.i18n.json +++ b/packages/i18n/src/locales/en.i18n.json @@ -4231,6 +4231,7 @@ "others": "others", "Others": "Others", "OTR": "OTR", + "OTR_messages_cannot_be_exported": "OTR messages cannot be exported", "OTR_unavailable_for_federation": "OTR is unavailable for federated rooms", "OTR_Description": "Off-the-record chats are secure, private and disappear once ended.", "OTR_Chat_Declined_Title": "OTR Chat invite Declined",