Skip to content
This repository has been archived by the owner on Sep 11, 2024. It is now read-only.

Directly convert Matrix and room Ids to pills #10267

Merged
merged 5 commits into from
Mar 2, 2023
Merged
Show file tree
Hide file tree
Changes from 3 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
38 changes: 18 additions & 20 deletions src/components/views/messages/TextualBody.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,7 @@ See the License for the specific language governing permissions and
limitations under the License.
*/

import React, { createRef, SyntheticEvent, MouseEvent, ReactNode } from "react";
import React, { createRef, SyntheticEvent, MouseEvent } from "react";
import ReactDOM from "react-dom";
import highlight from "highlight.js";
import { MsgType } from "matrix-js-sdk/src/@types/event";
Expand Down Expand Up @@ -86,21 +86,21 @@ export default class TextualBody extends React.Component<IBodyProps, IState> {
}

private applyFormatting(): void {
// Function is only called from render / componentDidMount → contentRef is set
const content = this.contentRef.current!;

const showLineNumbers = SettingsStore.getValue("showCodeLineNumbers");
this.activateSpoilers([this.contentRef.current]);
this.activateSpoilers([content]);

// pillifyLinks BEFORE linkifyElement because plain room/user URLs in the composer
Copy link
Contributor Author

Choose a reason for hiding this comment

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

From element-hq/element-meta#1030

The RTE team have their own issue tracking this version of the work - we will not implement anything in the RTE, instead they will aim to match the behaviour we're implementing. As both composers will likely live alongside each other - this feels like the best approach

// are still sent as plaintext URLs. If these are ever pillified in the composer,
// we should be pillify them here by doing the linkifying BEFORE the pillifying.
pillifyLinks([this.contentRef.current], this.props.mxEvent, this.pills);
HtmlUtils.linkifyElement(this.contentRef.current);
HtmlUtils.linkifyElement(content);
pillifyLinks([content], this.props.mxEvent, this.pills);

this.calculateUrlPreview();

// tooltipifyLinks AFTER calculateUrlPreview because the DOM inside the tooltip
// container is empty before the internal component has mounted so calculateUrlPreview
// won't find any anchors
tooltipifyLinks([this.contentRef.current], this.pills, this.tooltips);
tooltipifyLinks([content], this.pills, this.tooltips);

if (this.props.mxEvent.getContent().format === "org.matrix.custom.html") {
// Handle expansion and add buttons
Expand Down Expand Up @@ -578,18 +578,16 @@ export default class TextualBody extends React.Component<IBodyProps, IState> {

// only strip reply if this is the original replying event, edits thereafter do not have the fallback
const stripReply = !mxEvent.replacingEvent() && !!getParentEventId(mxEvent);
let body: ReactNode;
if (!body) {
isEmote = content.msgtype === MsgType.Emote;
isNotice = content.msgtype === MsgType.Notice;
body = HtmlUtils.bodyToHtml(content, this.props.highlights, {
disableBigEmoji: isEmote || !SettingsStore.getValue<boolean>("TextualBody.enableBigEmoji"),
// Part of Replies fallback support
stripReplyFallback: stripReply,
ref: this.contentRef,
returnString: false,
});
}
isEmote = content.msgtype === MsgType.Emote;
isNotice = content.msgtype === MsgType.Notice;
let body = HtmlUtils.bodyToHtml(content, this.props.highlights, {
disableBigEmoji: isEmote || !SettingsStore.getValue<boolean>("TextualBody.enableBigEmoji"),
// Part of Replies fallback support
stripReplyFallback: stripReply,
ref: this.contentRef,
returnString: false,
});

if (this.props.replacingEventId) {
body = (
<>
Expand Down
67 changes: 33 additions & 34 deletions test/components/views/messages/TextualBody-test.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -19,7 +19,7 @@ import { MatrixClient, MatrixEvent } from "matrix-js-sdk/src/matrix";
import { MockedObject } from "jest-mock";
import { render } from "@testing-library/react";

import { getMockClientWithEventEmitter, mkEvent, mkStubRoom } from "../../../test-utils";
import { getMockClientWithEventEmitter, mkEvent, mkMessage, mkStubRoom } from "../../../test-utils";
import { MatrixClientPeg } from "../../../../src/MatrixClientPeg";
import * as languageHandler from "../../../../src/languageHandler";
import DMRoomMap from "../../../../src/utils/DMRoomMap";
Expand All @@ -28,6 +28,15 @@ import MatrixClientContext from "../../../../src/contexts/MatrixClientContext";
import { RoomPermalinkCreator } from "../../../../src/utils/permalinks/Permalinks";
import { MediaEventHelper } from "../../../../src/utils/MediaEventHelper";

const mkRoomTextMessage = (body: string): MatrixEvent => {
return mkMessage({
msg: body,
room: "room_id",
user: "sender",
event: true,
});
};

describe("<TextualBody />", () => {
afterEach(() => {
jest.spyOn(MatrixClientPeg, "get").mockRestore();
Expand All @@ -38,9 +47,11 @@ describe("<TextualBody />", () => {
beforeEach(() => {
defaultMatrixClient = getMockClientWithEventEmitter({
getRoom: () => defaultRoom,
getRooms: () => [defaultRoom],
getAccountData: (): MatrixEvent | undefined => undefined,
isGuest: () => false,
mxcUrlToHttp: (s: string) => s,
getUserId: () => "@user:example.com",
});
});

Expand Down Expand Up @@ -116,17 +127,7 @@ describe("<TextualBody />", () => {
});

it("simple message renders as expected", () => {
const ev = mkEvent({
type: "m.room.message",
room: "room_id",
user: "sender",
content: {
body: "this is a plaintext message",
msgtype: "m.text",
},
event: true,
});

const ev = mkRoomTextMessage("this is a plaintext message");
const { container } = getComponent({ mxEvent: ev });
expect(container).toHaveTextContent(ev.getContent().body);
const content = container.querySelector(".mx_EventTile_body");
Expand All @@ -135,17 +136,7 @@ describe("<TextualBody />", () => {

// If pills were rendered within a Portal/same shadow DOM then it'd be easier to test
it("linkification get applied correctly into the DOM", () => {
const ev = mkEvent({
type: "m.room.message",
room: "room_id",
user: "sender",
content: {
body: "Visit https://matrix.org/",
msgtype: "m.text",
},
event: true,
});

const ev = mkRoomTextMessage("Visit https://matrix.org/");
const { container } = getComponent({ mxEvent: ev });
expect(container).toHaveTextContent(ev.getContent().body);
const content = container.querySelector(".mx_EventTile_body");
Expand All @@ -155,6 +146,24 @@ describe("<TextualBody />", () => {
"https://matrix.org/</a></span>",
);
});

it("pillification of MXIDs get applied correctly into the DOM", () => {
const ev = mkRoomTextMessage("Chat with @user:example.com");
const { container } = getComponent({ mxEvent: ev });
const content = container.querySelector(".mx_EventTile_body");
expect(content.innerHTML).toMatchInlineSnapshot(
`"Chat with <span><bdi><a class="mx_Pill mx_UserPill"><img class="mx_BaseAvatar mx_BaseAvatar_image" src="mxc://avatar.url/image.png" style="width: 16px; height: 16px;" alt="" data-testid="avatar-img" aria-hidden="true"><span class="mx_Pill_linkText">Member</span></a></bdi></span>"`,
);
});

it("pillification of room aliases get applied correctly into the DOM", () => {
const ev = mkRoomTextMessage("Visit #room:example.com");
const { container } = getComponent({ mxEvent: ev });
const content = container.querySelector(".mx_EventTile_body");
expect(content.innerHTML).toMatchInlineSnapshot(
`"Visit <span><bdi><a class="mx_Pill mx_RoomPill" href="https://matrix.to/#/#room:example.com"><span class="mx_Pill_linkText">#room:example.com</span></a></bdi></span>"`,
);
});
});

describe("renders formatted m.text correctly", () => {
Expand Down Expand Up @@ -382,17 +391,7 @@ describe("<TextualBody />", () => {
});
DMRoomMap.makeShared();

const ev = mkEvent({
type: "m.room.message",
room: "room_id",
user: "sender",
content: {
body: "Visit https://matrix.org/",
msgtype: "m.text",
},
event: true,
});

const ev = mkRoomTextMessage("Visit https://matrix.org/");
const { container, rerender } = getComponent(
{ mxEvent: ev, showUrlPreview: true, onHeightChanged: jest.fn() },
matrixClient,
Expand Down