feat: E2EE room setup header (#32446)

Co-authored-by: gabriellsh <40830821+gabriellsh@users.noreply.github.com>
Co-authored-by: Hugo Costa <20212776+hugocostadev@users.noreply.github.com>
pull/31821/head^2
Yash Rajpal 2 years ago committed by GitHub
parent 465c8edff5
commit 2ef71e8ea6
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
  1. 6
      .changeset/gold-flowers-shake.md
  2. 2
      apps/meteor/client/ui.ts
  3. 24
      apps/meteor/client/views/room/Header/Header.tsx
  4. 5
      apps/meteor/client/views/room/Header/RoomHeader.tsx
  5. 29
      apps/meteor/client/views/room/Header/RoomHeaderE2EESetup.tsx
  6. 41
      apps/meteor/client/views/room/Header/RoomToolbox/RoomToolboxE2EESetup.tsx
  7. 23
      apps/meteor/tests/e2e/e2e-encryption.spec.ts
  8. 4
      apps/meteor/tests/e2e/page-objects/fragments/home-flextab.ts

@ -0,0 +1,6 @@
---
'@rocket.chat/i18n': minor
'@rocket.chat/meteor': minor
---
Added E2EE room setup header, with just limited functionality and room actions.

@ -78,3 +78,5 @@ export const quickActionHooks = [
useCloseChatQuickAction,
useOnHoldChatQuickAction,
] satisfies (() => QuickActionsActionConfig | undefined)[];
export const roomActionHooksForE2EESetup = [useChannelSettingsRoomAction, useMembersListRoomAction, useE2EERoomAction];

@ -1,15 +1,16 @@
import type { IRoom } from '@rocket.chat/core-typings';
import { isVoipRoom } from '@rocket.chat/core-typings';
import { isDirectMessageRoom, isVoipRoom } from '@rocket.chat/core-typings';
import { HeaderToolbar } from '@rocket.chat/ui-client';
import { useLayout } from '@rocket.chat/ui-contexts';
import { useLayout, useSetting } from '@rocket.chat/ui-contexts';
import type { ReactElement } from 'react';
import React, { lazy, memo, useMemo } from 'react';
import SidebarToggler from '../../../components/SidebarToggler';
const DirectRoomHeader = lazy(() => import('./DirectRoomHeader'));
const OmnichannelRoomHeader = lazy(() => import('./Omnichannel/OmnichannelRoomHeader'));
const VoipRoomHeader = lazy(() => import('./Omnichannel/VoipRoomHeader'));
const RoomHeaderE2EESetup = lazy(() => import('./RoomHeaderE2EESetup'));
const DirectRoomHeader = lazy(() => import('./DirectRoomHeader'));
const RoomHeader = lazy(() => import('./RoomHeader'));
type HeaderProps<T> = {
@ -18,6 +19,9 @@ type HeaderProps<T> = {
const Header = ({ room }: HeaderProps<IRoom>): ReactElement | null => {
const { isMobile, isEmbedded, showTopNavbarEmbeddedLayout } = useLayout();
const encrypted = Boolean(room.encrypted);
const unencryptedMessagesAllowed = useSetting<boolean>('E2E_Allow_Unencrypted_Messages');
const shouldDisplayE2EESetup = encrypted && !unencryptedMessagesAllowed;
const slots = useMemo(
() => ({
@ -34,10 +38,6 @@ const Header = ({ room }: HeaderProps<IRoom>): ReactElement | null => {
return null;
}
if (room.t === 'd' && (room.uids?.length ?? 0) < 3) {
return <DirectRoomHeader slots={slots} room={room} />;
}
if (room.t === 'l') {
return <OmnichannelRoomHeader slots={slots} />;
}
@ -46,7 +46,15 @@ const Header = ({ room }: HeaderProps<IRoom>): ReactElement | null => {
return <VoipRoomHeader slots={slots} room={room} />;
}
return <RoomHeader slots={slots} room={room} topic={room.topic} />;
if (shouldDisplayE2EESetup) {
return <RoomHeaderE2EESetup room={room} topic={room.topic} slots={slots} />;
}
if (isDirectMessageRoom(room) && (room.uids?.length ?? 0) < 3) {
return <DirectRoomHeader slots={slots} room={room} />;
}
return <RoomHeader room={room} topic={room.topic} slots={slots} />;
};
export default memo(Header);

@ -30,9 +30,10 @@ export type RoomHeaderProps = {
pos?: unknown;
};
};
roomToolbox?: JSX.Element;
};
const RoomHeader = ({ room, topic = '', slots = {} }: RoomHeaderProps) => {
const RoomHeader = ({ room, topic = '', slots = {}, roomToolbox }: RoomHeaderProps) => {
const t = useTranslation();
return (
@ -65,7 +66,7 @@ const RoomHeader = ({ room, topic = '', slots = {} }: RoomHeaderProps) => {
<Suspense fallback={null}>
<HeaderToolbar aria-label={t('Toolbox_room_actions')}>
{slots?.toolbox?.pre}
{slots?.toolbox?.content || <RoomToolbox />}
{slots?.toolbox?.content || roomToolbox || <RoomToolbox />}
{slots?.toolbox?.pos}
</HeaderToolbar>
</Suspense>

@ -0,0 +1,29 @@
import { isDirectMessageRoom } from '@rocket.chat/core-typings';
import React, { lazy } from 'react';
import { E2EEState } from '../../../../app/e2e/client/E2EEState';
import { E2ERoomState } from '../../../../app/e2e/client/E2ERoomState';
import { useE2EERoomState } from '../hooks/useE2EERoomState';
import { useE2EEState } from '../hooks/useE2EEState';
import DirectRoomHeader from './DirectRoomHeader';
import RoomHeader from './RoomHeader';
import type { RoomHeaderProps } from './RoomHeader';
const RoomToolboxE2EESetup = lazy(() => import('./RoomToolbox/RoomToolboxE2EESetup'));
const RoomHeaderE2EESetup = ({ room, topic = '', slots = {} }: RoomHeaderProps) => {
const e2eeState = useE2EEState();
const e2eRoomState = useE2EERoomState(room._id);
if (e2eeState === E2EEState.SAVE_PASSWORD || e2eeState === E2EEState.ENTER_PASSWORD || e2eRoomState === E2ERoomState.WAITING_KEYS) {
return <RoomHeader room={room} topic={topic} slots={slots} roomToolbox={<RoomToolboxE2EESetup />} />;
}
if (isDirectMessageRoom(room) && (room.uids?.length ?? 0) < 3) {
return <DirectRoomHeader slots={slots} room={room} />;
}
return <RoomHeader room={room} topic={topic} slots={slots} />;
};
export default RoomHeaderE2EESetup;

@ -0,0 +1,41 @@
import { useStableArray } from '@rocket.chat/fuselage-hooks';
import { HeaderToolbarAction } from '@rocket.chat/ui-client';
import { useTranslation } from '@rocket.chat/ui-contexts';
import React from 'react';
import { roomActionHooksForE2EESetup } from '../../../../ui';
import type { RoomToolboxActionConfig } from '../../contexts/RoomToolboxContext';
import { useRoomToolbox } from '../../contexts/RoomToolboxContext';
const RoomToolboxE2EESetup = () => {
const t = useTranslation();
const toolbox = useRoomToolbox();
const { tab } = toolbox;
const actions = useStableArray(
roomActionHooksForE2EESetup
.map((roomActionHook) => roomActionHook())
.filter((roomAction): roomAction is RoomToolboxActionConfig => !!roomAction),
);
return (
<>
{actions.map(({ id, icon, title, action, disabled, tooltip }, index) => (
<HeaderToolbarAction
key={id}
index={index}
id={id}
icon={icon}
title={t(title)}
pressed={id === tab?.id}
action={action ?? (() => toolbox.openTab(id))}
disabled={disabled}
tooltip={tooltip}
/>
))}
</>
);
};
export default RoomToolboxE2EESetup;

@ -429,11 +429,17 @@ test.describe.serial('e2ee room setup', () => {
await poHomeChannel.dismissToast();
await expect(poHomeChannel.content.encryptedRoomHeaderIcon).toBeVisible();
await poHomeChannel.content.encryptedRoomHeaderIcon.first().waitFor();
await expect(poHomeChannel.content.encryptedRoomHeaderIcon.first()).toBeVisible();
await page.locator('role=button[name="Save E2EE password"]').waitFor();
await expect(page.locator('role=button[name="Save E2EE password"]')).toBeVisible();
await poHomeChannel.tabs.btnE2EERoomSetupDisableE2E.waitFor();
await expect(poHomeChannel.tabs.btnE2EERoomSetupDisableE2E).toBeVisible();
await expect(poHomeChannel.tabs.btnTabMembers).toBeVisible();
await expect(poHomeChannel.tabs.btnRoomInfo).toBeVisible();
await expect(poHomeChannel.content.inputMessage).not.toBeVisible();
await page.locator('role=button[name="Save E2EE password"]').click();
@ -477,11 +483,17 @@ test.describe.serial('e2ee room setup', () => {
await poHomeChannel.dismissToast();
await expect(poHomeChannel.content.encryptedRoomHeaderIcon).toBeVisible();
await expect(poHomeChannel.content.encryptedRoomHeaderIcon.first()).toBeVisible();
await page.locator('role=button[name="Enter your E2E password"]').waitFor();
await expect(page.locator('role=banner >> text="Enter your E2E password"')).toBeVisible();
await poHomeChannel.tabs.btnE2EERoomSetupDisableE2E.waitFor();
await expect(poHomeChannel.tabs.btnE2EERoomSetupDisableE2E).toBeVisible();
await expect(poHomeChannel.tabs.btnTabMembers).toBeVisible();
await expect(poHomeChannel.tabs.btnRoomInfo).toBeVisible();
await expect(poHomeChannel.content.inputMessage).not.toBeVisible();
await page.locator('role=button[name="Enter your E2E password"]').click();
@ -518,7 +530,7 @@ test.describe.serial('e2ee room setup', () => {
await poHomeChannel.dismissToast();
await expect(poHomeChannel.content.encryptedRoomHeaderIcon).toBeVisible();
await expect(poHomeChannel.content.encryptedRoomHeaderIcon.first()).toBeVisible();
await poHomeChannel.content.sendMessage('hello world');
@ -551,5 +563,10 @@ test.describe.serial('e2ee room setup', () => {
await expect(poHomeChannel.content.inputMessage).not.toBeVisible();
await expect(page.locator('.rcx-states__title')).toContainText('Check back later');
await poHomeChannel.tabs.btnE2EERoomSetupDisableE2E.waitFor();
await expect(poHomeChannel.tabs.btnE2EERoomSetupDisableE2E).toBeVisible();
await expect(poHomeChannel.tabs.btnTabMembers).toBeVisible();
await expect(poHomeChannel.tabs.btnRoomInfo).toBeVisible();
});
});

@ -44,6 +44,10 @@ export class HomeFlextab {
return this.page.locator('role=menuitem[name="Notifications Preferences"]');
}
get btnE2EERoomSetupDisableE2E(): Locator {
return this.page.locator('[data-qa-id=ToolBoxAction-key]');
}
get btnDisableE2E(): Locator {
return this.page.locator('role=menuitem[name="Disable E2E"]');
}

Loading…
Cancel
Save