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

Commit

Permalink
Merge branch 'hughns/graduate-qr-signin-from-labs' of https://github.…
Browse files Browse the repository at this point in the history
…com/matrix-org/matrix-react-sdk into hughns/graduate-qr-signin-from-labs
  • Loading branch information
hughns committed Feb 14, 2023
2 parents 98d85bb + 9b12f9d commit d710061
Show file tree
Hide file tree
Showing 61 changed files with 1,846 additions and 990 deletions.
29 changes: 28 additions & 1 deletion cypress/e2e/spaces/spaces.spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,7 @@ limitations under the License.
/// <reference types="cypress" />

import type { MatrixClient } from "matrix-js-sdk/src/client";
import type { Preset } from "matrix-js-sdk/src/@types/partials";
import type { ICreateRoomOpts } from "matrix-js-sdk/src/@types/requests";
import { HomeserverInstance } from "../../plugins/utils/homeserver";
import Chainable = Cypress.Chainable;
Expand All @@ -32,7 +33,7 @@ function openSpaceContextMenu(spaceName: string): Chainable<JQuery> {
return cy.get(".mx_SpacePanel_contextMenu");
}

function spaceCreateOptions(spaceName: string): ICreateRoomOpts {
function spaceCreateOptions(spaceName: string, roomIds: string[] = []): ICreateRoomOpts {
return {
creation_content: {
type: "m.space",
Expand All @@ -44,6 +45,7 @@ function spaceCreateOptions(spaceName: string): ICreateRoomOpts {
name: spaceName,
},
},
...roomIds.map(spaceChildInitialState),
],
};
}
Expand Down Expand Up @@ -283,4 +285,29 @@ describe("Spaces", () => {
cy.checkA11y(undefined, axeOptions);
cy.get(".mx_SpacePanel").percySnapshotElement("Space panel expanded", { widths: [258] });
});

it("should not soft crash when joining a room from space hierarchy which has a link in its topic", () => {
cy.getBot(homeserver, { displayName: "BotBob" }).then({ timeout: 10000 }, async (bot) => {
const { room_id: roomId } = await bot.createRoom({
preset: "public_chat" as Preset,
name: "Test Room",
topic: "This is a topic https://github.com/matrix-org/matrix-react-sdk/pull/10060 with a link",
});
const { room_id: spaceId } = await bot.createRoom(spaceCreateOptions("Test Space", [roomId]));
await bot.invite(spaceId, user.userId);
});

cy.getSpacePanelButton("Test Space").should("exist");
cy.wait(500); // without this we can end up clicking too quickly and it ends up having no effect
cy.viewSpaceByName("Test Space");
cy.contains(".mx_AccessibleButton", "Accept").click();

cy.contains(".mx_SpaceHierarchy_roomTile.mx_AccessibleButton", "Test Room").within(() => {
cy.contains("Join").should("exist").realHover().click();
cy.contains("View", { timeout: 5000 }).should("exist").click();
});

// Assert we get shown the new room intro, and thus not the soft crash screen
cy.get(".mx_NewRoomIntro").should("exist");
});
});
1 change: 1 addition & 0 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -86,6 +86,7 @@
"jszip": "^3.7.0",
"katex": "^0.16.0",
"linkify-element": "4.0.0-beta.4",
"linkify-react": "4.0.0-beta.4",
"linkify-string": "4.0.0-beta.4",
"linkifyjs": "4.0.0-beta.4",
"lodash": "^4.17.20",
Expand Down
3 changes: 3 additions & 0 deletions res/css/_components.pcss
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,7 @@
@import "./components/views/beacon/_ShareLatestLocation.pcss";
@import "./components/views/beacon/_StyledLiveBeaconIcon.pcss";
@import "./components/views/context_menus/_KebabContextMenu.pcss";
@import "./components/views/dialogs/polls/_PollListItem.pcss";
@import "./components/views/elements/_FilterDropdown.pcss";
@import "./components/views/elements/_LearnMore.pcss";
@import "./components/views/location/_EnableLiveShare.pcss";
Expand Down Expand Up @@ -161,6 +162,8 @@
@import "./views/dialogs/_UserSettingsDialog.pcss";
@import "./views/dialogs/_VerifyEMailDialog.pcss";
@import "./views/dialogs/_WidgetCapabilitiesPromptDialog.pcss";
@import "./views/dialogs/polls/_PollHistoryDialog.pcss";
@import "./views/dialogs/polls/_PollHistoryList.pcss";
@import "./views/dialogs/security/_AccessSecretStorageDialog.pcss";
@import "./views/dialogs/security/_CreateCrossSigningDialog.pcss";
@import "./views/dialogs/security/_CreateKeyBackupDialog.pcss";
Expand Down
40 changes: 40 additions & 0 deletions res/css/components/views/dialogs/polls/_PollListItem.pcss
Original file line number Diff line number Diff line change
@@ -0,0 +1,40 @@
/*
Copyright 2023 The Matrix.org Foundation C.I.C.
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
*/

.mx_PollListItem {
width: 100%;
display: grid;
justify-content: left;
align-items: center;
grid-gap: $spacing-8;
grid-template-columns: auto auto auto;
grid-template-rows: auto;

color: $primary-content;
}

.mx_PollListItem_icon {
height: 14px;
width: 14px;
color: $quaternary-content;
padding-left: $spacing-8;
}

.mx_PollListItem_question {
white-space: nowrap;
overflow: hidden;
text-overflow: ellipsis;
}
23 changes: 23 additions & 0 deletions res/css/views/dialogs/polls/_PollHistoryDialog.pcss
Original file line number Diff line number Diff line change
@@ -0,0 +1,23 @@
/*
Copyright 2023 The Matrix.org Foundation C.I.C.
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
*/

.mx_PollHistoryDialog_content {
height: 600px;
width: 100%;

display: flex;
flex-direction: column;
}
44 changes: 44 additions & 0 deletions res/css/views/dialogs/polls/_PollHistoryList.pcss
Original file line number Diff line number Diff line change
@@ -0,0 +1,44 @@
/*
Copyright 2023 The Matrix.org Foundation C.I.C.
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
*/

.mx_PollHistoryList {
display: flex;
flex-direction: column;
flex: 1 1 auto;
max-height: 100%;
}

.mx_PollHistoryList_list {
overflow: auto;
list-style: none;
margin-block: 0;
padding-inline: 0;
flex: 1 1 0;
align-content: flex-start;
display: grid;
grid-gap: $spacing-20;
padding-right: $spacing-64;
margin: $spacing-32 0;
}

.mx_PollHistoryList_noResults {
height: 100%;
width: 100%;
display: flex;
align-items: center;
justify-content: center;
color: $secondary-content;
}
8 changes: 8 additions & 0 deletions res/css/views/messages/_MPollBody.pcss
Original file line number Diff line number Diff line change
Expand Up @@ -150,8 +150,16 @@ limitations under the License.
}

.mx_MPollBody_totalVotes {
display: flex;
flex-direction: inline;
justify-content: start;
color: $secondary-content;
font-size: $font-12px;

.mx_Spinner {
flex: 0;
margin-left: $spacing-8;
}
}
}

Expand Down
2 changes: 1 addition & 1 deletion sonar-project.properties
Original file line number Diff line number Diff line change
Expand Up @@ -10,5 +10,5 @@ sonar.exclusions=__mocks__,docs

sonar.typescript.tsconfigPath=./tsconfig.json
sonar.javascript.lcov.reportPaths=coverage/lcov.info
sonar.coverage.exclusions=test/**/*,cypress/**/*
sonar.coverage.exclusions=test/**/*,cypress/**/*,src/components/views/dialogs/devtools/**/*
sonar.testExecutionReportPaths=coverage/jest-sonar-report.xml
13 changes: 13 additions & 0 deletions src/DateUtils.ts
Original file line number Diff line number Diff line change
Expand Up @@ -269,3 +269,16 @@ export function formatPreciseDuration(durationMs: number): string {
}
return _t("%(value)ss", { value: seconds });
}

/**
* Formats a timestamp to a short date
* (eg 25/12/22 in uk locale)
* localised by system locale
* @param timestamp - epoch timestamp
* @returns {string} formattedDate
*/
export const formatLocalDateShort = (timestamp: number): string =>
new Intl.DateTimeFormat(
undefined, // locales
{ day: "2-digit", month: "2-digit", year: "2-digit" },
).format(timestamp);
14 changes: 12 additions & 2 deletions src/HtmlUtils.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -17,16 +17,17 @@ See the License for the specific language governing permissions and
limitations under the License.
*/

import React, { ReactNode } from "react";
import React, { ReactElement, ReactNode } from "react";
import sanitizeHtml from "sanitize-html";
import cheerio from "cheerio";
import classNames from "classnames";
import EMOJIBASE_REGEX from "emojibase-regex";
import { split } from "lodash";
import { merge, split } from "lodash";
import katex from "katex";
import { decode } from "html-entities";
import { IContent } from "matrix-js-sdk/src/models/event";
import { Optional } from "matrix-events-sdk";
import _Linkify from "linkify-react";

import {
_linkifyElement,
Expand Down Expand Up @@ -682,6 +683,15 @@ export function topicToHtml(
);
}

/* Wrapper around linkify-react merging in our default linkify options */
export function Linkify({ as, options, children }: React.ComponentProps<typeof _Linkify>): ReactElement {
return (
<_Linkify as={as} options={merge({}, linkifyMatrixOptions, options)}>
{children}
</_Linkify>
);
}

/**
* Linkifies the given string. This is a wrapper around 'linkifyjs/string'.
*
Expand Down
9 changes: 3 additions & 6 deletions src/SlashCommands.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -33,7 +33,7 @@ import dis from "./dispatcher/dispatcher";
import { _t, _td, ITranslatableError, newTranslatableError } from "./languageHandler";
import Modal from "./Modal";
import MultiInviter from "./utils/MultiInviter";
import { linkifyElement, topicToHtml } from "./HtmlUtils";
import { Linkify, topicToHtml } from "./HtmlUtils";
import QuestionDialog from "./components/views/dialogs/QuestionDialog";
import WidgetUtils from "./utils/WidgetUtils";
import { textToHtmlRainbow } from "./utils/colour";
Expand Down Expand Up @@ -501,14 +501,11 @@ export const Commands = [
? ContentHelpers.parseTopicContent(content)
: { text: _t("This room has no topic.") };

const ref = (e): void => {
if (e) linkifyElement(e);
};
const body = topicToHtml(topic.text, topic.html, ref, true);
const body = topicToHtml(topic.text, topic.html, undefined, true);

Modal.createDialog(InfoDialog, {
title: room.name,
description: <div ref={ref}>{body}</div>,
description: <Linkify>{body}</Linkify>,
hasCloseButton: true,
className: "markdown-body",
});
Expand Down
2 changes: 1 addition & 1 deletion src/components/structures/MatrixChat.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -136,9 +136,9 @@ import { SdkContextClass, SDKContext } from "../../contexts/SDKContext";
import { viewUserDeviceSettings } from "../../actions/handlers/viewUserDeviceSettings";
import { cleanUpBroadcasts, VoiceBroadcastResumer } from "../../voice-broadcast";
import GenericToast from "../views/toasts/GenericToast";
import { Linkify } from "../views/elements/Linkify";
import RovingSpotlightDialog, { Filter } from "../views/dialogs/spotlight/SpotlightDialog";
import { findDMForUser } from "../../utils/dm/findDMForUser";
import { Linkify } from "../../HtmlUtils";

// legacy export
export { default as Views } from "../../Views";
Expand Down
52 changes: 35 additions & 17 deletions src/components/structures/SpaceHierarchy.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -51,7 +51,7 @@ import TextWithTooltip from "../views/elements/TextWithTooltip";
import { useStateToggle } from "../../hooks/useStateToggle";
import { getChildOrder } from "../../stores/spaces/SpaceStore";
import AccessibleTooltipButton from "../views/elements/AccessibleTooltipButton";
import { linkifyElement, topicToHtml } from "../../HtmlUtils";
import { Linkify, topicToHtml } from "../../HtmlUtils";
import { useDispatcher } from "../../hooks/useDispatcher";
import { Action } from "../../dispatcher/actions";
import { IState, RovingTabIndexProvider, useRovingTabIndex } from "../../accessibility/RovingTabIndex";
Expand Down Expand Up @@ -210,6 +210,25 @@ const Tile: React.FC<ITileProps> = ({
topic = room.topic;
}

let topicSection: ReactNode | undefined;
if (topic) {
topicSection = (
<Linkify
options={{
attributes: {
onClick(ev: MouseEvent) {
// prevent clicks on links from bubbling up to the room tile
ev.stopPropagation();
},
},
}}
>
{" · "}
{topic}
</Linkify>
);
}

let joinedSection: ReactElement | undefined;
if (joinedRoom) {
joinedSection = <div className="mx_SpaceHierarchy_roomTile_joined">{_t("Joined")}</div>;
Expand All @@ -231,19 +250,9 @@ const Tile: React.FC<ITileProps> = ({
{joinedSection}
{suggestedSection}
</div>
<div
className="mx_SpaceHierarchy_roomTile_info"
ref={(e) => e && linkifyElement(e)}
onClick={(ev) => {
// prevent clicks on links from bubbling up to the room tile
if ((ev.target as HTMLElement).tagName === "A") {
ev.stopPropagation();
}
}}
>
<div className="mx_SpaceHierarchy_roomTile_info">
{description}
{topic && " · "}
{topic}
{topicSection}
</div>
</div>
<div className="mx_SpaceHierarchy_actions">
Expand Down Expand Up @@ -413,9 +422,18 @@ interface IHierarchyLevelProps {
onToggleClick?(parentId: string, childId: string): void;
}

const toLocalRoom = (cli: MatrixClient, room: IHierarchyRoom): IHierarchyRoom => {
export const toLocalRoom = (cli: MatrixClient, room: IHierarchyRoom, hierarchy: RoomHierarchy): IHierarchyRoom => {
const history = cli.getRoomUpgradeHistory(room.room_id, true);
const cliRoom = history[history.length - 1];

// Pick latest room that is actually part of the hierarchy
let cliRoom = null;
for (let idx = history.length - 1; idx >= 0; --idx) {
if (hierarchy.roomMap.get(history[idx].roomId)) {
cliRoom = history[idx];
break;
}
}

if (cliRoom) {
return {
...room,
Expand All @@ -424,7 +442,7 @@ const toLocalRoom = (cli: MatrixClient, room: IHierarchyRoom): IHierarchyRoom =>
name: cliRoom.name,
topic: cliRoom.currentState.getStateEvents(EventType.RoomTopic, "")?.getContent().topic,
avatar_url: cliRoom.getMxcAvatarUrl(),
canonical_alias: cliRoom.getCanonicalAlias(),
canonical_alias: cliRoom.getCanonicalAlias() ?? undefined,
aliases: cliRoom.getAltAliases(),
world_readable:
cliRoom.currentState.getStateEvents(EventType.RoomHistoryVisibility, "")?.getContent()
Expand Down Expand Up @@ -461,7 +479,7 @@ export const HierarchyLevel: React.FC<IHierarchyLevelProps> = ({
(result, ev: IHierarchyRelation) => {
const room = hierarchy.roomMap.get(ev.state_key);
if (room && roomSet.has(room)) {
result[room.room_type === RoomType.Space ? 0 : 1].push(toLocalRoom(cli, room));
result[room.room_type === RoomType.Space ? 0 : 1].push(toLocalRoom(cli, room, hierarchy));
}
return result;
},
Expand Down
Loading

0 comments on commit d710061

Please sign in to comment.