Skip to content
Merged
Show file tree
Hide file tree
Changes from 13 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
3 changes: 2 additions & 1 deletion playwright/e2e/crypto/crypto.spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -158,7 +158,8 @@ test.describe("Cryptography", function () {
await page.getByRole("textbox", { name: "Send an unencrypted message…" }).press("Enter");
await checkDMRoom(page);
const bobRoomId = await bobJoin(page, bob);
await expect(page.locator(".mx_MessageComposer_e2eIcon")).toMatchScreenshot("composer-e2e-icon-normal.png");
// We no longer show the grey badge in the composer, check that it is not there.
await expect(page.locator(".mx_MessageComposer_e2eIcon")).toHaveCount(0);

await testMessages(page, bob, bobRoomId);
await verify(app, bob);
Expand Down
Binary file not shown.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Lack of ellipsis here isn't great, makes it look broken. Not a new issue though

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Hrmm the problem here is that it's a contenteditable div, so we are adding the placeholder text ourself in the css.
We do this by setting width and height to 0 and overflow: visible, so that the cursor is on the left of the placeholder, rather than pushed to the end.

This is then incompatible with getting the ellipsis to work. Not sure I see an alternative path forward.

Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
6 changes: 3 additions & 3 deletions src/components/views/rooms/E2EIcon.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -74,18 +74,18 @@ const E2EIcon: React.FC<Props> = ({

let content: JSX.Element;
if (onClick) {
content = <AccessibleButton onClick={onClick} className={classes} style={style} />;
content = <AccessibleButton onClick={onClick} className={classes} style={style} data-testid="e2e-icon" />;
} else {
// Verified and warning icon have a transparent cutout, so add a white background.
// The normal icon already has the correct shape and size, so reuse that.
if (status === E2EStatus.Verified || status === E2EStatus.Warning) {
content = (
<div className={classes} style={style}>
<div className={classes} style={style} data-testid="e2e-icon">
<div className="mx_E2EIcon_normal" />
</div>
);
} else {
content = <div className={classes} style={style} />;
content = <div className={classes} style={style} data-testid="e2e-icon" />;
}
}

Expand Down
34 changes: 25 additions & 9 deletions src/components/views/rooms/MessageComposer.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,7 @@ import {
import { type Optional } from "matrix-events-sdk";
import { Tooltip } from "@vector-im/compound-web";
import { logger } from "matrix-js-sdk/src/logger";
import { LockOffIcon } from "@vector-im/compound-design-tokens/assets/web/icons";

import { _t } from "../../../languageHandler";
import { MatrixClientPeg } from "../../../MatrixClientPeg";
Expand All @@ -36,7 +37,7 @@ import VoiceRecordComposerTile from "./VoiceRecordComposerTile";
import { VoiceRecordingStore } from "../../../stores/VoiceRecordingStore";
import { RecordingState } from "../../../audio/VoiceRecording";
import type ResizeNotifier from "../../../utils/ResizeNotifier";
import { type E2EStatus } from "../../../utils/ShieldUtils";
import { E2EStatus } from "../../../utils/ShieldUtils";
import SendMessageComposer, { type SendMessageComposer as SendMessageComposerClass } from "./SendMessageComposer";
import { type ComposerInsertPayload } from "../../../dispatcher/payloads/ComposerInsertPayload";
import { Action } from "../../../dispatcher/actions";
Expand Down Expand Up @@ -525,12 +526,27 @@ export class MessageComposer extends React.Component<IProps, IState> {
};

public render(): React.ReactNode {
const hasE2EIcon = Boolean(!this.state.isWysiwygLabEnabled && this.props.e2eStatus);
const e2eIcon = hasE2EIcon && (
<div className="mx_MessageComposer_e2eIconWrapper">
<E2EIcon key="e2eIcon" status={this.props.e2eStatus!} className="mx_MessageComposer_e2eIcon" />
</div>
);
let leftIcon: false | JSX.Element = false;
if (!this.state.isWysiwygLabEnabled) {
if (!this.props.e2eStatus) {
leftIcon = (
<div className="mx_MessageComposer_e2eIconWrapper">
<LockOffIcon
width={12}
height={12}
color="var(--cpd-color-icon-info-primary)"
className="mx_E2EIcon mx_MessageComposer_e2eIcon"
/>
</div>
);
} else if (this.props.e2eStatus !== E2EStatus.Normal) {
leftIcon = (
<div className="mx_MessageComposer_e2eIconWrapper">
<E2EIcon key="e2eIcon" status={this.props.e2eStatus} className="mx_MessageComposer_e2eIcon" />
</div>
);
}
}

const controls: ReactNode[] = [];
const menuPosition = this.getMenuPosition();
Expand Down Expand Up @@ -640,7 +656,7 @@ export class MessageComposer extends React.Component<IProps, IState> {
const classes = classNames({
"mx_MessageComposer": true,
"mx_MessageComposer--compact": this.props.compact,
"mx_MessageComposer_e2eStatus": hasE2EIcon,
"mx_MessageComposer_e2eStatus": leftIcon,
"mx_MessageComposer_wysiwyg": this.state.isWysiwygLabEnabled,
});

Expand All @@ -654,7 +670,7 @@ export class MessageComposer extends React.Component<IProps, IState> {
permalinkCreator={this.props.permalinkCreator}
/>
<div className="mx_MessageComposer_row">
{e2eIcon}
{leftIcon}
{composer}
<div className="mx_MessageComposer_actions">
{controls}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -8,12 +8,13 @@ Please see LICENSE files in the repository root for full details.

import React, { type JSX, type RefObject, useMemo, type ReactNode } from "react";
import { type IEventRelation } from "matrix-js-sdk/src/matrix";
import LockOffIcon from "@vector-im/compound-design-tokens/assets/web/icons/lock-off";

import { useWysiwygSendActionHandler } from "./hooks/useWysiwygSendActionHandler";
import { WysiwygComposer } from "./components/WysiwygComposer";
import { PlainTextComposer } from "./components/PlainTextComposer";
import { type ComposerFunctions } from "./types";
import { type E2EStatus } from "../../../../utils/ShieldUtils";
import { E2EStatus } from "../../../../utils/ShieldUtils";
import E2EIcon from "../E2EIcon";
import { type MenuProps } from "../../../structures/ContextMenu";
import { Emoji } from "./components/Emoji";
Expand Down Expand Up @@ -55,11 +56,25 @@ export default function SendWysiwygComposer({
[props.eventRelation],
);

let leftIcon: false | JSX.Element = false;
if (!e2eStatus) {
leftIcon = (
<LockOffIcon
data-testid="e2e-icon"
width={12}
height={12}
color="var(--cpd-color-icon-info-primary)"
className="mx_E2EIcon"
/>
);
} else if (e2eStatus !== E2EStatus.Normal) {
leftIcon = <E2EIcon status={e2eStatus} />;
}
return (
<ComposerContext.Provider value={defaultContextValue}>
<Composer
className="mx_SendWysiwygComposer"
leftComponent={e2eStatus && <E2EIcon status={e2eStatus} />}
leftComponent={leftIcon}
rightComponent={<Emoji menuPosition={menuPosition} />}
{...props}
>
Expand Down
3 changes: 2 additions & 1 deletion test/unit-tests/components/structures/RoomView-test.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -413,7 +413,7 @@

deferred.resolve(true);
await waitFor(() => expect(container.querySelector(".mx_RoomView_messagePanel")).not.toBeNull());
expect(asFragment()).toMatchSnapshot();

Check failure on line 416 in test/unit-tests/components/structures/RoomView-test.tsx

View workflow job for this annotation

GitHub Actions / Jest (2)

RoomView › should not display the timeline when the room encryption is loading

expect(received).toMatchSnapshot() Snapshot name: `RoomView should not display the timeline when the room encryption is loading 2` - Snapshot - 0 + Received + 1 @@ -236,10 +236,11 @@ tabindex="0" > <div aria-labelledby="«r3e»" class="mx_E2EIcon mx_E2EIcon_verified mx_MessageComposer_e2eIcon" + data-testid="e2e-icon" > <div class="mx_E2EIcon_normal" /> </div> at Object.toMatchSnapshot (test/unit-tests/components/structures/RoomView-test.tsx:416:30)
});

it("updates live timeline when a timeline reset happens", async () => {
Expand Down Expand Up @@ -442,7 +442,8 @@
);

const { container } = await renderRoomView();
await waitFor(() => expect(container.querySelector(".mx_E2EIcon_normal")).toBeInTheDocument());
// We no longer show the grey shield for encrypted rooms, so it should not be there.
await waitFor(() => expect(container.querySelector(".mx_E2EIcon_normal")).not.toBeInTheDocument());

const verificationStatus = new UserVerificationStatus(true, true, false);
jest.spyOn(cli.getCrypto()!, "getUserVerificationStatus").mockResolvedValue(verificationStatus);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -787,7 +787,7 @@ exports[`RoomView for a local room in state NEW should match the snapshot 1`] =
</div>
<div
aria-label="Message composer"
class="mx_MessageComposer"
class="mx_MessageComposer mx_MessageComposer_e2eStatus"
role="region"
>
<div
Expand All @@ -796,6 +796,23 @@ exports[`RoomView for a local room in state NEW should match the snapshot 1`] =
<div
class="mx_MessageComposer_row"
>
<div
class="mx_MessageComposer_e2eIconWrapper"
>
<svg
class="mx_E2EIcon mx_MessageComposer_e2eIcon"
color="var(--cpd-color-icon-info-primary)"
fill="currentColor"
height="12"
viewBox="0 0 24 24"
width="12"
xmlns="http://www.w3.org/2000/svg"
>
<path
d="M6 22q-.825 0-1.412-.587A1.93 1.93 0 0 1 4 20V10q0-.825.588-1.412a2 2 0 0 1 .702-.463L1.333 4.167a1 1 0 0 1 1.414-1.414L7 7.006v-.012l13 13v.012l1.247 1.247a1 1 0 1 1-1.414 1.414l-.896-.896A1.94 1.94 0 0 1 18 22zm14-4.834V10q0-.825-.587-1.412A1.93 1.93 0 0 0 18 8h-1V6q0-2.075-1.463-3.537Q14.075 1 12 1T8.463 2.463a4.9 4.9 0 0 0-1.22 1.946L9 6.166V6q0-1.25.875-2.125A2.9 2.9 0 0 1 12 3q1.25 0 2.125.875T15 6v2h-4.166z"
/>
</svg>
</div>
<div
class="mx_SendMessageComposer"
>
Expand Down Expand Up @@ -1164,7 +1181,7 @@ exports[`RoomView for a local room in state NEW that is encrypted should match t
</div>
<div
aria-label="Message composer"
class="mx_MessageComposer"
class="mx_MessageComposer mx_MessageComposer_e2eStatus"
role="region"
>
<div
Expand All @@ -1173,6 +1190,23 @@ exports[`RoomView for a local room in state NEW that is encrypted should match t
<div
class="mx_MessageComposer_row"
>
<div
class="mx_MessageComposer_e2eIconWrapper"
>
<svg
class="mx_E2EIcon mx_MessageComposer_e2eIcon"
color="var(--cpd-color-icon-info-primary)"
fill="currentColor"
height="12"
viewBox="0 0 24 24"
width="12"
xmlns="http://www.w3.org/2000/svg"
>
<path
d="M6 22q-.825 0-1.412-.587A1.93 1.93 0 0 1 4 20V10q0-.825.588-1.412a2 2 0 0 1 .702-.463L1.333 4.167a1 1 0 0 1 1.414-1.414L7 7.006v-.012l13 13v.012l1.247 1.247a1 1 0 1 1-1.414 1.414l-.896-.896A1.94 1.94 0 0 1 18 22zm14-4.834V10q0-.825-.587-1.412A1.93 1.93 0 0 0 18 8h-1V6q0-2.075-1.463-3.537Q14.075 1 12 1T8.463 2.463a4.9 4.9 0 0 0-1.22 1.946L9 6.166V6q0-1.25.875-2.125A2.9 2.9 0 0 1 12 3q1.25 0 2.125.875T15 6v2h-4.166z"
/>
</svg>
</div>
<div
class="mx_SendMessageComposer"
>
Expand Down Expand Up @@ -2221,7 +2255,7 @@ exports[`RoomView video rooms should render joined video room view 1`] = `
</div>
<div
aria-label="Message composer"
class="mx_MessageComposer mx_MessageComposer--compact"
class="mx_MessageComposer mx_MessageComposer--compact mx_MessageComposer_e2eStatus"
role="region"
>
<div
Expand All @@ -2230,6 +2264,23 @@ exports[`RoomView video rooms should render joined video room view 1`] = `
<div
class="mx_MessageComposer_row"
>
<div
class="mx_MessageComposer_e2eIconWrapper"
>
<svg
class="mx_E2EIcon mx_MessageComposer_e2eIcon"
color="var(--cpd-color-icon-info-primary)"
fill="currentColor"
height="12"
viewBox="0 0 24 24"
width="12"
xmlns="http://www.w3.org/2000/svg"
>
<path
d="M6 22q-.825 0-1.412-.587A1.93 1.93 0 0 1 4 20V10q0-.825.588-1.412a2 2 0 0 1 .702-.463L1.333 4.167a1 1 0 0 1 1.414-1.414L7 7.006v-.012l13 13v.012l1.247 1.247a1 1 0 1 1-1.414 1.414l-.896-.896A1.94 1.94 0 0 1 18 22zm14-4.834V10q0-.825-.587-1.412A1.93 1.93 0 0 0 18 8h-1V6q0-2.075-1.463-3.537Q14.075 1 12 1T8.463 2.463a4.9 4.9 0 0 0-1.22 1.946L9 6.166V6q0-1.25.875-2.125A2.9 2.9 0 0 1 12 3q1.25 0 2.125.875T15 6v2h-4.166z"
/>
</svg>
</div>
<div
class="mx_SendMessageComposer"
>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,7 @@ import {
import { setSelection } from "../../../../../../src/components/views/rooms/wysiwyg_composer/utils/selection";
import { createMocks } from "./utils";
import { ScopedRoomContextProvider } from "../../../../../../src/contexts/ScopedRoomContext.tsx";
import { E2EStatus } from "../../../../../../src/utils/ShieldUtils.ts";

jest.mock("../../../../../../src/components/views/rooms/EmojiButton", () => ({
EmojiButton: ({ addEmoji }: { addEmoji: (emoji: string) => void }) => {
Expand Down Expand Up @@ -69,6 +70,7 @@ describe("SendWysiwygComposer", () => {
disabled = false,
isRichTextEnabled = true,
placeholder?: string,
e2eStatus?: E2EStatus,
) => {
return render(
<MatrixClientContext.Provider value={mockClient}>
Expand All @@ -80,6 +82,7 @@ describe("SendWysiwygComposer", () => {
isRichTextEnabled={isRichTextEnabled}
menuPosition={aboveLeftOf({ top: 0, bottom: 0, right: 0 })}
placeholder={placeholder}
e2eStatus={e2eStatus}
/>
</ScopedRoomContextProvider>
</MatrixClientContext.Provider>,
Expand Down Expand Up @@ -322,4 +325,23 @@ describe("SendWysiwygComposer", () => {
});
},
);

describe.each([{ isRichTextEnabled: true }, { isRichTextEnabled: false }])(
"Left icon when %s",
({ isRichTextEnabled }) => {
it.each([
[E2EStatus.Verified, "mx_E2EIcon_verified"],
[E2EStatus.Warning, "mx_E2EIcon_warning"],
[undefined, undefined],
])("Should render left icon when e2eStatus is %s", async (e2eStatus, expectedClass) => {
// When
customRender(jest.fn(), jest.fn(), false, isRichTextEnabled, undefined, e2eStatus);
await waitFor(() => expect(screen.getByRole("textbox")).toHaveAttribute("contentEditable", "true"));
const leftIcon = screen.getByTestId("e2e-icon");
// Then
expect(leftIcon).toBeInTheDocument();
expect(leftIcon).toHaveClass(expectedClass ? `mx_E2EIcon ${expectedClass}` : `mx_E2EIcon`);
});
},
);
});
Loading