Skip to content
Merged
Show file tree
Hide file tree
Changes from all 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
17 changes: 12 additions & 5 deletions src/components/views/rooms/NewRoomIntro.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -30,6 +30,8 @@ import { privateShouldBeEncrypted } from "../../../utils/rooms";
import { LocalRoom } from "../../../models/LocalRoom";
import { shouldEncryptRoomWithSingle3rdPartyInvite } from "../../../utils/room/shouldEncryptRoomWithSingle3rdPartyInvite";
import { useScopedRoomContext } from "../../../contexts/ScopedRoomContext.tsx";
import { useTopic } from "../../../hooks/room/useTopic";
import { topicToHtml, Linkify } from "../../../HtmlUtils";

function hasExpectedEncryptionSettings(matrixClient: MatrixClient, room: Room): boolean {
const isEncrypted: boolean = matrixClient.isRoomEncrypted(room.roomId);
Expand All @@ -52,6 +54,7 @@ const determineIntroMessage = (room: Room, encryptedSingle3rdPartyInvite: boolea
const NewRoomIntro: React.FC = () => {
const cli = useContext(MatrixClientContext);
const { room, roomId } = useScopedRoomContext("room", "roomId");
const topic = useTopic(room);

if (!room || !roomId) {
throw new Error("Unable to create a NewRoomIntro without room and roomId");
Expand Down Expand Up @@ -106,7 +109,6 @@ const NewRoomIntro: React.FC = () => {
);
} else {
const inRoom = room && room.getMyMembership() === KnownMembership.Join;
const topic = room.currentState.getStateEvents(EventType.RoomTopic, "")?.getContent()?.topic;
const canAddTopic = inRoom && room.currentState.maySendStateEvent(EventType.RoomTopic, cli.getSafeUserId());

const onTopicClick = (): void => {
Expand All @@ -126,18 +128,23 @@ const NewRoomIntro: React.FC = () => {
let topicText;
if (canAddTopic && topic) {
topicText = _t(
"room|intro|topic_edit",
{ topic },
"room|intro|edit_topic",
{},
{
a: (sub) => (
<AccessibleButton element="a" kind="link_inline" onClick={onTopicClick}>
{sub}
</AccessibleButton>
),
topic: () => <Linkify>{topicToHtml(topic?.text, topic?.html)}</Linkify>,
},
);
} else if (topic) {
topicText = _t("room|intro|topic", { topic });
topicText = _t(
"room|intro|display_topic",
{},
{ topic: () => <Linkify>{topicToHtml(topic?.text, topic?.html)}</Linkify> },
);
} else if (canAddTopic) {
topicText = _t(
"room|intro|no_topic",
Expand Down Expand Up @@ -245,7 +252,7 @@ const NewRoomIntro: React.FC = () => {
},
)}
</p>
<p>{topicText}</p>
<p data-testid="topic">{topicText}</p>
{buttons}
</React.Fragment>
);
Expand Down
4 changes: 2 additions & 2 deletions src/i18n/strings/en_EN.json
Original file line number Diff line number Diff line change
Expand Up @@ -1998,7 +1998,9 @@
"inaccessible_subtitle_1": "Try again later, or ask a room or space admin to check if you have access.",
"inaccessible_subtitle_2": "%(errcode)s was returned while trying to access the room or space. If you think you're seeing this message in error, please <issueLink>submit a bug report</issueLink>.",
"intro": {
"display_topic": "Topic: <topic/>",
"dm_caption": "Only the two of you are in this conversation, unless either of you invites anyone to join.",
"edit_topic": "Topic: <topic/> (<a>edit</a>)",
"enable_encryption_prompt": "Enable encryption in settings.",
"encrypted_3pid_dm_pending_join": "Once everyone has joined, you’ll be able to chat",
"no_avatar_label": "Add a photo, so people can easily spot your room.",
Expand All @@ -2008,8 +2010,6 @@
"send_message_start_dm": "Send your first message to invite <displayName/> to chat",
"start_of_dm_history": "This is the beginning of your direct message history with <displayName/>.",
"start_of_room": "This is the start of <roomName/>.",
"topic": "Topic: %(topic)s ",
"topic_edit": "Topic: %(topic)s (<a>edit</a>)",
"unencrypted_warning": "End-to-end encryption isn't enabled",
"user_created": "%(displayName)s created this room.",
"you_created": "You created this room."
Expand Down
76 changes: 73 additions & 3 deletions test/unit-tests/components/views/rooms/NewRoomIntro-test.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -9,16 +9,24 @@ Please see LICENSE files in the repository root for full details.

import React from "react";
import { render, screen } from "jest-matrix-react";
import { type MatrixClient, Room } from "matrix-js-sdk/src/matrix";
import { EventTimeline, type MatrixClient, Room } from "matrix-js-sdk/src/matrix";
import { KnownMembership } from "matrix-js-sdk/src/types";

import { LocalRoom } from "../../../../../src/models/LocalRoom";
import { filterConsole, mkRoomMemberJoinEvent, mkThirdPartyInviteEvent, stubClient } from "../../../../test-utils";
import {
filterConsole,
mkEvent,
mkRoomMemberJoinEvent,
mkThirdPartyInviteEvent,
stubClient,
} from "../../../../test-utils";
import MatrixClientContext from "../../../../../src/contexts/MatrixClientContext";
import NewRoomIntro from "../../../../../src/components/views/rooms/NewRoomIntro";
import { type IRoomState } from "../../../../../src/components/structures/RoomView";
import DMRoomMap from "../../../../../src/utils/DMRoomMap";
import { DirectoryMember } from "../../../../../src/utils/direct-messages";
import { ScopedRoomContextProvider } from "../../../../../src/contexts/ScopedRoomContext.tsx";
import defaultDispatcher from "../../../../../src/dispatcher/dispatcher";

const renderNewRoomIntro = (client: MatrixClient, room: Room | LocalRoom) => {
render(
Expand All @@ -37,11 +45,15 @@ describe("NewRoomIntro", () => {

filterConsole("Room !room:example.com does not have an m.room.create event");

beforeAll(() => {
beforeEach(() => {
client = stubClient();
DMRoomMap.makeShared(client);
});

afterEach(() => {
jest.resetAllMocks();
});

describe("for a DM Room", () => {
beforeEach(() => {
jest.spyOn(DMRoomMap.shared(), "getUserIdForRoomId").mockReturnValue(userId);
Expand Down Expand Up @@ -88,4 +100,62 @@ describe("NewRoomIntro", () => {
screen.getByText((id, element) => element?.tagName === "SPAN" && element?.textContent === expected);
});
});

describe("topic", () => {
let room: Room;

beforeEach(() => {
room = new Room(roomId, client, userId);
room.getLiveTimeline()
.getState(EventTimeline.FORWARDS)
?.setStateEvents([mkRoomMemberJoinEvent(client.getSafeUserId(), room.roomId)]);
jest.spyOn(DMRoomMap.shared(), "getRoomIds").mockReturnValue(new Set([room.roomId]));
});

function addTopicToRoom(topic: string) {
const topicEvent = mkEvent({
type: "m.room.topic",
room: roomId,
user: userId,
content: {
topic,
},
ts: 123,
event: true,
});

room.addLiveEvents([topicEvent], { addToState: true });
}

it("should render the topic", () => {
addTopicToRoom("Test topic");
renderNewRoomIntro(client, room);
screen.getByText("Test topic");
});

it("should render a link in the topic", () => {
addTopicToRoom("This is a link: https://matrix.org/");
renderNewRoomIntro(client, room);
expect(screen.getByTestId("topic")).toMatchSnapshot();
});

it("should be able to add a topic", () => {
addTopicToRoom("Test topic");
jest.spyOn(room, "getMyMembership").mockReturnValue(KnownMembership.Join);
jest.spyOn(room.getLiveTimeline().getState(EventTimeline.FORWARDS)!, "maySendStateEvent").mockReturnValue(
true,
);
const spyDispatcher = jest.spyOn(defaultDispatcher, "dispatch");

renderNewRoomIntro(client, room);
screen.getByRole("button", { name: "edit" }).click();
expect(spyDispatcher).toHaveBeenCalledWith(
{
action: "open_room_settings",
room_id: room.roomId,
},
true,
);
});
});
});
Original file line number Diff line number Diff line change
@@ -0,0 +1,24 @@
// Jest Snapshot v1, https://goo.gl/fbAQLP

exports[`NewRoomIntro topic should render a link in the topic 1`] = `
<p
data-testid="topic"
>
<span>
Topic:
<span
dir="auto"
>
This is a link:
<a
class="linkified"
href="https://matrix.org/"
rel="noreferrer noopener"
target="_blank"
>
https://matrix.org/
</a>
</span>
</span>
</p>
`;
Loading