Compare commits
11 Commits
hs/identif
...
erikj/join
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
4a66a22c14 | ||
|
|
64e2a843c3 | ||
|
|
fba59381a0 | ||
|
|
e1970df704 | ||
|
|
b54122884c | ||
|
|
0d28df0f67 | ||
|
|
3a39486468 | ||
|
|
0dc295e3b8 | ||
|
|
5a6c9a4c9a | ||
|
|
599112e122 | ||
|
|
170dcd1c0e |
@@ -93,4 +93,41 @@ test.describe("Room list", () => {
|
||||
await filters.getByRole("option", { name: "People" }).click();
|
||||
await expect(roomListView.getByRole("gridcell", { name: "Open room room0" })).toBeVisible();
|
||||
});
|
||||
|
||||
test("unread filter should only match unread rooms that have a count", async ({ page, app, bot }) => {
|
||||
const roomListView = getRoomList(page);
|
||||
// Let's create a new room and invite the bot
|
||||
const room1Id = await app.client.createRoom({
|
||||
name: "Unread Room 1",
|
||||
invite: [bot.credentials?.userId],
|
||||
});
|
||||
await bot.awaitRoomMembership(room1Id);
|
||||
|
||||
// Let's create another room as well
|
||||
const room2Id = await app.client.createRoom({
|
||||
name: "Unread Room 2",
|
||||
invite: [bot.credentials?.userId],
|
||||
});
|
||||
await bot.awaitRoomMembership(room2Id);
|
||||
|
||||
// Let's configure unread room 1 so that we only get notification for mentions and keywords
|
||||
await app.viewRoomById(room1Id);
|
||||
await app.settings.openRoomSettings("Notifications");
|
||||
await page.getByText("@mentions & keywords").click();
|
||||
await app.settings.closeDialog();
|
||||
|
||||
// Let's open a room other than room 1 or room 2
|
||||
await roomListView.getByRole("gridcell", { name: "Open room room29" }).click();
|
||||
|
||||
// Let's make the bot send a new message in both room 1 and room 2
|
||||
await bot.sendMessage(room1Id, "Hello!");
|
||||
await bot.sendMessage(room2Id, "Hello!");
|
||||
|
||||
// Let's activate the unread filter now
|
||||
await page.getByRole("option", { name: "Unread" }).click();
|
||||
|
||||
// Unread filter should only show room 2!!
|
||||
await expect(roomListView.getByRole("gridcell", { name: "Open room Unread Room 2" })).toBeVisible();
|
||||
await expect(roomListView.getByRole("gridcell", { name: "Open room Unread Room 1" })).not.toBeVisible();
|
||||
});
|
||||
});
|
||||
|
||||
@@ -13,6 +13,7 @@ import { selectHomeserver } from "../utils";
|
||||
import { type Credentials, type HomeserverInstance } from "../../plugins/homeserver";
|
||||
import { consentHomeserver } from "../../plugins/homeserver/synapse/consentHomeserver.ts";
|
||||
import { isDendrite } from "../../plugins/homeserver/dendrite";
|
||||
import { createBot } from "../crypto/utils.ts";
|
||||
|
||||
// This test requires fixed credentials for the device signing keys below to work
|
||||
const username = "user1234";
|
||||
@@ -258,6 +259,34 @@ test.describe("Login", () => {
|
||||
|
||||
await expect(h1.locator(".mx_CompleteSecurity_skip")).toHaveCount(0);
|
||||
});
|
||||
|
||||
test("Continues to show verification prompt after cancelling device verification", async ({
|
||||
page,
|
||||
homeserver,
|
||||
credentials,
|
||||
}) => {
|
||||
// Create a different device which is cross-signed, meaning we need to verify this device
|
||||
await createBot(page, homeserver, credentials, true);
|
||||
|
||||
// Wait to avoid homeserver rate limit on logins
|
||||
await page.waitForTimeout(100);
|
||||
|
||||
// Load the page and see that we are asked to verify
|
||||
await page.goto("/#/welcome");
|
||||
await login(page, homeserver, credentials);
|
||||
let h1 = page.getByRole("heading", { name: "Verify this device", level: 1 });
|
||||
await expect(h1).toBeVisible();
|
||||
|
||||
// Click "Verify with another device"
|
||||
await page.getByRole("button", { name: "Verify with another device" }).click();
|
||||
|
||||
// Cancel the new dialog
|
||||
await page.getByRole("button", { name: "Close dialog" }).click();
|
||||
|
||||
// Check that we are still being asked to verify
|
||||
h1 = page.getByRole("heading", { name: "Verify this device", level: 1 });
|
||||
await expect(h1).toBeVisible();
|
||||
});
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
Copyright 2024 New Vector Ltd.
|
||||
Copyright 2024, 2025 New Vector Ltd.
|
||||
Copyright 2022 The Matrix.org Foundation C.I.C.
|
||||
|
||||
SPDX-License-Identifier: AGPL-3.0-only OR GPL-3.0-only OR LicenseRef-Element-Commercial
|
||||
@@ -10,6 +10,7 @@ import { type Locator, type Page } from "@playwright/test";
|
||||
|
||||
import { test, expect } from "../../element-web-test";
|
||||
import { checkRoomSummaryCard, viewRoomSummaryByName } from "./utils";
|
||||
import { isDendrite } from "../../plugins/homeserver/dendrite";
|
||||
|
||||
const ROOM_NAME = "Test room";
|
||||
const ROOM_NAME_LONG =
|
||||
@@ -133,6 +134,17 @@ test.describe("RightPanel", () => {
|
||||
await page.getByLabel("Room info").nth(1).click();
|
||||
await checkRoomSummaryCard(page, ROOM_NAME);
|
||||
});
|
||||
test.describe("room reporting", () => {
|
||||
test.skip(isDendrite, "Dendrite does not implement room reporting");
|
||||
test("should handle reporting a room", async ({ page, app }) => {
|
||||
await viewRoomSummaryByName(page, app, ROOM_NAME);
|
||||
await page.getByRole("menuitem", { name: "Report room" }).click();
|
||||
const dialog = await page.getByRole("dialog", { name: "Report Room" });
|
||||
await dialog.getByLabel("reason").fill("This room should be reported");
|
||||
await dialog.getByRole("button", { name: "Send report" }).click();
|
||||
await expect(page.getByText("Your report was sent.")).toBeVisible();
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
test.describe("in spaces", () => {
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
Copyright 2024 New Vector Ltd.
|
||||
Copyright 2024,2025 New Vector Ltd.
|
||||
Copyright 2023 Suguru Hirahara
|
||||
|
||||
SPDX-License-Identifier: AGPL-3.0-only OR GPL-3.0-only OR LicenseRef-Element-Commercial
|
||||
@@ -50,8 +50,8 @@ test.describe("Appearance user settings tab", () => {
|
||||
// Click "Show advanced" link button
|
||||
await tab.getByRole("button", { name: "Show advanced" }).click();
|
||||
|
||||
await tab.locator(".mx_Checkbox", { hasText: "Use bundled emoji font" }).click();
|
||||
await tab.locator(".mx_Checkbox", { hasText: "Use a system font" }).click();
|
||||
await tab.getByLabel("Use bundled emoji font").click();
|
||||
await tab.getByLabel("Use a system font").click();
|
||||
|
||||
// Assert that the font-family value was removed
|
||||
await expect(page.locator("body")).toHaveCSS("font-family", '""');
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
Copyright 2024 New Vector Ltd.
|
||||
Copyright 2024,2025 New Vector Ltd.
|
||||
Copyright 2022 The Matrix.org Foundation C.I.C.
|
||||
|
||||
SPDX-License-Identifier: AGPL-3.0-only OR GPL-3.0-only OR LicenseRef-Element-Commercial
|
||||
@@ -35,17 +35,18 @@ function spaceCreateOptions(spaceName: string, roomIds: string[] = []): ICreateR
|
||||
name: spaceName,
|
||||
},
|
||||
},
|
||||
...roomIds.map(spaceChildInitialState),
|
||||
...roomIds.map((r) => spaceChildInitialState(r)),
|
||||
],
|
||||
};
|
||||
}
|
||||
|
||||
function spaceChildInitialState(roomId: string): ICreateRoomOpts["initial_state"]["0"] {
|
||||
function spaceChildInitialState(roomId: string, order?: string): ICreateRoomOpts["initial_state"]["0"] {
|
||||
return {
|
||||
type: "m.space.child",
|
||||
state_key: roomId,
|
||||
content: {
|
||||
via: [roomId.split(":")[1]],
|
||||
order,
|
||||
},
|
||||
};
|
||||
}
|
||||
@@ -121,9 +122,10 @@ test.describe("Spaces", () => {
|
||||
await page.getByRole("button", { name: "Skip for now" }).click();
|
||||
|
||||
// Assert rooms exist in the room list
|
||||
await expect(page.getByRole("treeitem", { name: "General", exact: true })).toBeVisible();
|
||||
await expect(page.getByRole("treeitem", { name: "Random", exact: true })).toBeVisible();
|
||||
await expect(page.getByRole("treeitem", { name: "Projects", exact: true })).toBeVisible();
|
||||
const roomList = page.getByRole("tree", { name: "Rooms" });
|
||||
await expect(roomList.getByRole("treeitem", { name: "General", exact: true })).toBeVisible();
|
||||
await expect(roomList.getByRole("treeitem", { name: "Random", exact: true })).toBeVisible();
|
||||
await expect(roomList.getByRole("treeitem", { name: "Projects", exact: true })).toBeVisible();
|
||||
|
||||
// Assert rooms exist in the space explorer
|
||||
await expect(
|
||||
@@ -155,7 +157,7 @@ test.describe("Spaces", () => {
|
||||
|
||||
await page.getByRole("button", { name: "Just me" }).click();
|
||||
|
||||
await page.getByText("Sample Room").click({ force: true }); // force click as checkbox size is zero
|
||||
await page.getByRole("checkbox", { name: "Sample Room" }).click();
|
||||
|
||||
// Temporal implementation as multiple elements with the role "button" and name "Add" are found
|
||||
await page.locator(".mx_AddExistingToSpace_footer").getByRole("button", { name: "Add" }).click();
|
||||
@@ -165,6 +167,50 @@ test.describe("Spaces", () => {
|
||||
).toBeVisible();
|
||||
});
|
||||
|
||||
test(
|
||||
"should allow user to add an existing room to a space after creation",
|
||||
{ tag: "@screenshot" },
|
||||
async ({ page, app, user }) => {
|
||||
await app.client.createRoom({
|
||||
name: "Sample Room",
|
||||
});
|
||||
await app.client.createRoom({
|
||||
name: "A Room that will not be selected",
|
||||
});
|
||||
|
||||
const menu = await openSpaceCreateMenu(page);
|
||||
await menu.getByRole("button", { name: "Private" }).click();
|
||||
|
||||
await menu
|
||||
.locator('.mx_SpaceBasicSettings_avatarContainer input[type="file"]')
|
||||
.setInputFiles("playwright/sample-files/riot.png");
|
||||
await expect(menu.getByRole("textbox", { name: "Address" })).not.toBeVisible();
|
||||
await menu
|
||||
.getByRole("textbox", { name: "Description" })
|
||||
.fill("This is a personal space to mourn Riot.im...");
|
||||
await menu.getByRole("textbox", { name: "Name" }).fill("This is my Riot");
|
||||
await menu.getByRole("textbox", { name: "Name" }).press("Enter");
|
||||
|
||||
await page.getByRole("button", { name: "Just me" }).click();
|
||||
|
||||
await page.getByRole("button", { name: "Skip for now" }).click();
|
||||
|
||||
await page.getByRole("button", { name: "Add room" }).click();
|
||||
await page.getByRole("menuitem", { name: "Add existing room" }).click();
|
||||
|
||||
await page.getByRole("checkbox", { name: "Sample Room" }).click();
|
||||
|
||||
await expect(page.getByRole("dialog", { name: "Avatar Add existing rooms" })).toMatchScreenshot(
|
||||
"add-existing-rooms-dialog.png",
|
||||
);
|
||||
|
||||
await page.getByRole("button", { name: "Add" }).click();
|
||||
await expect(
|
||||
page.locator(".mx_SpaceHierarchy_list").getByRole("treeitem", { name: "Sample Room" }),
|
||||
).toBeVisible();
|
||||
},
|
||||
);
|
||||
|
||||
test("should allow user to invite another to a space", { tag: "@no-webkit" }, async ({ page, app, user, bot }) => {
|
||||
await app.client.createSpace({
|
||||
visibility: "public" as any,
|
||||
@@ -291,4 +337,36 @@ test.describe("Spaces", () => {
|
||||
// Assert we get shown the new room intro, and thus not the soft crash screen
|
||||
await expect(page.locator(".mx_NewRoomIntro")).toBeVisible();
|
||||
});
|
||||
|
||||
test("should render spaces view", { tag: "@screenshot" }, async ({ page, app, user, axe }) => {
|
||||
axe.disableRules([
|
||||
// Disable this check as it triggers on nested roving tab index elements which are in practice fine
|
||||
"nested-interactive",
|
||||
// XXX: We have some known contrast issues here
|
||||
"color-contrast",
|
||||
]);
|
||||
|
||||
const childSpaceId1 = await app.client.createSpace({
|
||||
name: "Child Space 1",
|
||||
initial_state: [],
|
||||
});
|
||||
const childSpaceId2 = await app.client.createSpace({
|
||||
name: "Child Space 2",
|
||||
initial_state: [],
|
||||
});
|
||||
const childSpaceId3 = await app.client.createSpace({
|
||||
name: "Child Space 3",
|
||||
initial_state: [],
|
||||
});
|
||||
await app.client.createSpace({
|
||||
name: "Root Space",
|
||||
initial_state: [
|
||||
spaceChildInitialState(childSpaceId1, "a"),
|
||||
spaceChildInitialState(childSpaceId2, "b"),
|
||||
spaceChildInitialState(childSpaceId3, "c"),
|
||||
],
|
||||
});
|
||||
await app.viewSpaceByName("Root Space");
|
||||
await expect(page.locator(".mx_SpaceRoomView")).toMatchScreenshot("space-room-view.png");
|
||||
});
|
||||
});
|
||||
|
||||
|
Before Width: | Height: | Size: 27 KiB After Width: | Height: | Size: 28 KiB |
|
Before Width: | Height: | Size: 67 KiB After Width: | Height: | Size: 68 KiB |
|
Before Width: | Height: | Size: 16 KiB After Width: | Height: | Size: 16 KiB |
|
After Width: | Height: | Size: 26 KiB |
|
After Width: | Height: | Size: 46 KiB |
@@ -7,7 +7,7 @@ Please see LICENSE files in the repository root for full details.
|
||||
|
||||
import { SynapseContainer as BaseSynapseContainer } from "@element-hq/element-web-playwright-common/lib/testcontainers";
|
||||
|
||||
const TAG = "develop@sha256:4285f51332a658ba6d4871b04d33f49261e6118e751d70fd2894aca97bd587c3";
|
||||
const TAG = "develop@sha256:d19854a3dbbb4d5d24d84767d17e1a623181ae5f2bdda3505819c05a8d3c8611";
|
||||
|
||||
/**
|
||||
* SynapseContainer which freezes the docker digest to stabilise tests,
|
||||
|
||||
@@ -128,7 +128,6 @@
|
||||
@import "./views/dialogs/_AddExistingToSpaceDialog.pcss";
|
||||
@import "./views/dialogs/_AnalyticsLearnMoreDialog.pcss";
|
||||
@import "./views/dialogs/_BugReportDialog.pcss";
|
||||
@import "./views/dialogs/_BulkRedactDialog.pcss";
|
||||
@import "./views/dialogs/_ChangelogDialog.pcss";
|
||||
@import "./views/dialogs/_CompoundDialog.pcss";
|
||||
@import "./views/dialogs/_ConfirmSpaceUserActionDialog.pcss";
|
||||
@@ -153,6 +152,7 @@
|
||||
@import "./views/dialogs/_ModalWidgetDialog.pcss";
|
||||
@import "./views/dialogs/_PollCreateDialog.pcss";
|
||||
@import "./views/dialogs/_RegistrationEmailPromptDialog.pcss";
|
||||
@import "./views/dialogs/_ReportRoomDialog.pcss";
|
||||
@import "./views/dialogs/_RoomSettingsDialog.pcss";
|
||||
@import "./views/dialogs/_RoomSettingsDialogBridges.pcss";
|
||||
@import "./views/dialogs/_RoomUpgradeDialog.pcss";
|
||||
@@ -212,7 +212,6 @@
|
||||
@import "./views/elements/_ServerPicker.pcss";
|
||||
@import "./views/elements/_SettingsFlag.pcss";
|
||||
@import "./views/elements/_Spinner.pcss";
|
||||
@import "./views/elements/_StyledCheckbox.pcss";
|
||||
@import "./views/elements/_StyledRadioButton.pcss";
|
||||
@import "./views/elements/_SyntaxHighlight.pcss";
|
||||
@import "./views/elements/_TagComposer.pcss";
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
Copyright 2024 New Vector Ltd.
|
||||
Copyright 2024,2025 New Vector Ltd.
|
||||
Copyright 2022 The Matrix.org Foundation C.I.C.
|
||||
|
||||
SPDX-License-Identifier: AGPL-3.0-only OR GPL-3.0-only OR LicenseRef-Element-Commercial
|
||||
@@ -16,9 +16,9 @@ Please see LICENSE files in the repository root for full details.
|
||||
.mx_SelectableDeviceTile_checkbox {
|
||||
flex: 1 0;
|
||||
|
||||
.mx_Checkbox_background + div {
|
||||
flex: 1 0;
|
||||
/* override more specific selector */
|
||||
margin-left: $spacing-16 !important;
|
||||
> div {
|
||||
margin-top: auto;
|
||||
margin-bottom: auto;
|
||||
margin-right: var(--cpd-space-1x);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
Copyright 2024 New Vector Ltd.
|
||||
Copyright 2024,2025 New Vector Ltd.
|
||||
Copyright 2021 The Matrix.org Foundation C.I.C.
|
||||
|
||||
SPDX-License-Identifier: AGPL-3.0-only OR GPL-3.0-only OR LicenseRef-Element-Commercial
|
||||
@@ -70,38 +70,26 @@ Please see LICENSE files in the repository root for full details.
|
||||
text-transform: uppercase;
|
||||
color: var(--cpd-color-text-secondary);
|
||||
margin: 20px 0 12px;
|
||||
}
|
||||
|
||||
.mx_QuickSettingsButton_pinToSidebarHeading {
|
||||
padding-left: 24px;
|
||||
position: relative;
|
||||
}
|
||||
|
||||
.mx_Checkbox {
|
||||
margin-bottom: 8px;
|
||||
}
|
||||
|
||||
.mx_QuickSettingsButton_favouritesCheckbox,
|
||||
.mx_QuickSettingsButton_peopleCheckbox {
|
||||
.mx_Checkbox_background + div {
|
||||
padding-left: 22px;
|
||||
position: relative;
|
||||
margin-left: 6px;
|
||||
font-size: $font-15px;
|
||||
line-height: $font-24px;
|
||||
color: var(--cpd-color-text-primary);
|
||||
}
|
||||
display: flex;
|
||||
}
|
||||
|
||||
.mx_QuickSettingsButton_moreOptionsButton {
|
||||
padding-left: 22px;
|
||||
margin-left: 22px;
|
||||
margin-left: var(--cpd-space-7x);
|
||||
font-size: $font-15px;
|
||||
line-height: $font-24px;
|
||||
color: var(--cpd-color-text-primary);
|
||||
position: relative;
|
||||
margin-bottom: 16px;
|
||||
}
|
||||
|
||||
.mx_QuickSettingsButton_option {
|
||||
margin-bottom: var(--cpd-space-3x);
|
||||
label {
|
||||
/* Correctly line up icons and text. */
|
||||
display: flex;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.mx_QuickSettingsButton_ContextMenuWrapper_new_room_list {
|
||||
@@ -111,15 +99,10 @@ Please see LICENSE files in the repository root for full details.
|
||||
}
|
||||
|
||||
.mx_QuickSettingsButton_icon {
|
||||
// TODO remove when all icons have fill=currentColor
|
||||
* {
|
||||
fill: $secondary-content;
|
||||
}
|
||||
margin-right: var(--cpd-space-1x);
|
||||
color: $secondary-content;
|
||||
width: 16px;
|
||||
height: 16px;
|
||||
position: absolute;
|
||||
left: 0;
|
||||
top: 50%;
|
||||
transform: translateY(-50%);
|
||||
width: 18px;
|
||||
height: 18px;
|
||||
margin-top: auto;
|
||||
margin-bottom: auto;
|
||||
}
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
Copyright 2024 New Vector Ltd.
|
||||
Copyright 2024,2025 New Vector Ltd.
|
||||
Copyright 2021 The Matrix.org Foundation C.I.C.
|
||||
|
||||
SPDX-License-Identifier: AGPL-3.0-only OR GPL-3.0-only OR LicenseRef-Element-Commercial
|
||||
@@ -247,15 +247,6 @@ Please see LICENSE files in the repository root for full details.
|
||||
.mx_AccessibleButton_kind_primary_outline {
|
||||
padding: 3px 16px; /* to account for the 1px border */
|
||||
}
|
||||
|
||||
.mx_Checkbox {
|
||||
display: inline-flex;
|
||||
|
||||
label {
|
||||
width: 16px;
|
||||
height: 16px;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
&:hover,
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
Copyright 2024 New Vector Ltd.
|
||||
Copyright 2024,2025 New Vector Ltd.
|
||||
Copyright 2021 The Matrix.org Foundation C.I.C.
|
||||
|
||||
SPDX-License-Identifier: AGPL-3.0-only OR GPL-3.0-only OR LicenseRef-Element-Commercial
|
||||
@@ -32,6 +32,11 @@ Please see LICENSE files in the repository root for full details.
|
||||
.mx_AddExistingToSpace_section {
|
||||
margin-right: 12px;
|
||||
|
||||
ul {
|
||||
list-style: none;
|
||||
padding-left: 0;
|
||||
}
|
||||
|
||||
// provides space for scrollbar so that checkbox and scrollbar do not collide
|
||||
|
||||
&:not(:first-child) {
|
||||
@@ -214,6 +219,12 @@ Please see LICENSE files in the repository root for full details.
|
||||
display: flex;
|
||||
margin-top: 12px;
|
||||
|
||||
form {
|
||||
/* Align checkboxes. */
|
||||
margin-top: auto;
|
||||
margin-bottom: auto;
|
||||
}
|
||||
|
||||
.mx_DecoratedRoomAvatar, /* we can't target .mx_BaseAvatar here as it'll break the decorated avatar styling */ {
|
||||
margin-right: 12px;
|
||||
}
|
||||
@@ -227,8 +238,4 @@ Please see LICENSE files in the repository root for full details.
|
||||
text-overflow: ellipsis;
|
||||
margin-right: 12px;
|
||||
}
|
||||
|
||||
.mx_Checkbox {
|
||||
align-items: center;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,19 +0,0 @@
|
||||
/*
|
||||
Copyright 2024 New Vector Ltd.
|
||||
Copyright 2021 Robin Townsend <robin@robin.town>
|
||||
|
||||
SPDX-License-Identifier: AGPL-3.0-only OR GPL-3.0-only OR LicenseRef-Element-Commercial
|
||||
Please see LICENSE files in the repository root for full details.
|
||||
*/
|
||||
|
||||
.mx_BulkRedactDialog {
|
||||
.mx_Checkbox,
|
||||
.mx_BulkRedactDialog_checkboxMicrocopy {
|
||||
line-height: $font-20px;
|
||||
}
|
||||
|
||||
.mx_BulkRedactDialog_checkboxMicrocopy {
|
||||
margin-left: 26px;
|
||||
color: $secondary-content;
|
||||
}
|
||||
}
|
||||
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
Copyright 2024 New Vector Ltd.
|
||||
Copyright 2024,2025 New Vector Ltd.
|
||||
Copyright 2021 The Matrix.org Foundation C.I.C.
|
||||
|
||||
SPDX-License-Identifier: AGPL-3.0-only OR GPL-3.0-only OR LicenseRef-Element-Commercial
|
||||
@@ -43,11 +43,6 @@ Please see LICENSE files in the repository root for full details.
|
||||
.mx_Field_valid.mx_Field:focus-within {
|
||||
border-color: $input-border-color;
|
||||
}
|
||||
|
||||
.mx_Checkbox input[type="checkbox"]:checked + label > .mx_Checkbox_background {
|
||||
background: $info-plinth-fg-color;
|
||||
border-color: $info-plinth-fg-color;
|
||||
}
|
||||
}
|
||||
|
||||
.mx_ExportDialog_progress {
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
Copyright 2024 New Vector Ltd.
|
||||
Copyright 2024,2025 New Vector Ltd.
|
||||
Copyright 2021 The Matrix.org Foundation C.I.C.
|
||||
|
||||
SPDX-License-Identifier: AGPL-3.0-only OR GPL-3.0-only OR LicenseRef-Element-Commercial
|
||||
@@ -74,10 +74,6 @@ Please see LICENSE files in the repository root for full details.
|
||||
line-height: $font-15px;
|
||||
color: $tertiary-content;
|
||||
}
|
||||
|
||||
.mx_Checkbox {
|
||||
align-items: center;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
16
res/css/views/dialogs/_ReportRoomDialog.pcss
Normal file
@@ -0,0 +1,16 @@
|
||||
/*
|
||||
Copyright 2025 New Vector Ltd.
|
||||
|
||||
SPDX-License-Identifier: AGPL-3.0-only OR GPL-3.0-only OR LicenseRef-Element-Commercial
|
||||
Please see LICENSE files in the repository root for full details.
|
||||
*/
|
||||
|
||||
.mx_ReportRoomDialog {
|
||||
textarea {
|
||||
font: var(--cpd-font-body-md-regular);
|
||||
border: 1px solid var(--cpd-color-border-interactive-primary);
|
||||
background: var(--cpd-color-bg-canvas-default);
|
||||
border-radius: 0.5rem;
|
||||
padding: var(--cpd-space-3x) var(--cpd-space-4x);
|
||||
}
|
||||
}
|
||||
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
Copyright 2024 New Vector Ltd.
|
||||
Copyright 2024,2025 New Vector Ltd.
|
||||
Copyright 2020 The Matrix.org Foundation C.I.C.
|
||||
|
||||
SPDX-License-Identifier: AGPL-3.0-only OR GPL-3.0-only OR LicenseRef-Element-Commercial
|
||||
@@ -19,13 +19,6 @@ Please see LICENSE files in the repository root for full details.
|
||||
margin-top: 20px;
|
||||
font-size: $font-15px;
|
||||
line-height: $font-15px;
|
||||
|
||||
.mx_WidgetCapabilitiesPromptDialog_byline {
|
||||
color: $muted-fg-color;
|
||||
margin-left: 26px;
|
||||
font-size: $font-12px;
|
||||
line-height: $font-12px;
|
||||
}
|
||||
}
|
||||
|
||||
.mx_Dialog_buttons {
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
Copyright 2024 New Vector Ltd.
|
||||
Copyright 2024,2025 New Vector Ltd.
|
||||
Copyright 2022 The Matrix.org Foundation C.I.C.
|
||||
|
||||
SPDX-License-Identifier: AGPL-3.0-only OR GPL-3.0-only OR LicenseRef-Element-Commercial
|
||||
@@ -7,26 +7,5 @@ Please see LICENSE files in the repository root for full details.
|
||||
*/
|
||||
|
||||
.mx_LabelledCheckbox {
|
||||
display: flex;
|
||||
gap: 8px;
|
||||
flex-direction: row;
|
||||
|
||||
.mx_Checkbox {
|
||||
margin-top: 3px; /* visually align with label text */
|
||||
}
|
||||
|
||||
.mx_LabelledCheckbox_labels {
|
||||
flex: 1;
|
||||
|
||||
.mx_LabelledCheckbox_label {
|
||||
vertical-align: middle;
|
||||
}
|
||||
|
||||
.mx_LabelledCheckbox_byline {
|
||||
display: block;
|
||||
padding-top: $spacing-4;
|
||||
color: $muted-fg-color;
|
||||
font-size: $font-11px;
|
||||
}
|
||||
}
|
||||
margin-top: var(--cpd-space-2x);
|
||||
}
|
||||
|
||||
@@ -1,98 +0,0 @@
|
||||
/*
|
||||
Copyright 2024 New Vector Ltd.
|
||||
Copyright 2020 The Matrix.org Foundation C.I.C.
|
||||
|
||||
SPDX-License-Identifier: AGPL-3.0-only OR GPL-3.0-only OR LicenseRef-Element-Commercial
|
||||
Please see LICENSE files in the repository root for full details.
|
||||
*/
|
||||
|
||||
.mx_Checkbox {
|
||||
$size: $font-16px;
|
||||
$border-radius: 0.27rem;
|
||||
|
||||
display: flex;
|
||||
align-items: flex-start;
|
||||
|
||||
input[type="checkbox"] {
|
||||
appearance: none;
|
||||
margin: 0;
|
||||
padding: 0;
|
||||
|
||||
& + label {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
|
||||
flex-grow: 1;
|
||||
}
|
||||
|
||||
& + label > .mx_Checkbox_background {
|
||||
display: inline-flex;
|
||||
position: relative;
|
||||
|
||||
flex-shrink: 0;
|
||||
|
||||
height: $size;
|
||||
width: $size;
|
||||
size: 0.5rem;
|
||||
border: 1px solid var(--cpd-color-border-interactive-primary);
|
||||
box-sizing: border-box;
|
||||
border-radius: $border-radius;
|
||||
|
||||
.mx_Checkbox_checkmark {
|
||||
display: none;
|
||||
|
||||
height: 100%;
|
||||
width: 100%;
|
||||
mask-image: url("@vector-im/compound-design-tokens/icons/check.svg");
|
||||
mask-position: center;
|
||||
mask-size: 100%;
|
||||
mask-repeat: no-repeat;
|
||||
}
|
||||
}
|
||||
|
||||
&:checked + label > .mx_Checkbox_background .mx_Checkbox_checkmark {
|
||||
display: block;
|
||||
}
|
||||
|
||||
& + label > *:not(.mx_Checkbox_background) {
|
||||
margin-left: 10px;
|
||||
}
|
||||
|
||||
&:disabled + label {
|
||||
cursor: not-allowed;
|
||||
}
|
||||
|
||||
&:focus-visible {
|
||||
& + label .mx_Checkbox_background {
|
||||
@mixin unreal-focus;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.mx_Checkbox.mx_Checkbox_kind_solid input[type="checkbox"] {
|
||||
& + label > .mx_Checkbox_background .mx_Checkbox_checkmark {
|
||||
background: var(--cpd-color-icon-on-solid-primary);
|
||||
}
|
||||
|
||||
&:checked + label > .mx_Checkbox_background {
|
||||
background: var(--cpd-color-bg-accent-rest);
|
||||
border-color: var(--cpd-color-bg-accent-rest);
|
||||
}
|
||||
|
||||
&:checked:disabled + label > .mx_Checkbox_background {
|
||||
background: var(--cpd-color-bg-action-primary-disabled);
|
||||
border-color: var(--cpd-color-bg-action-primary-disabled);
|
||||
}
|
||||
}
|
||||
|
||||
.mx_Checkbox.mx_Checkbox_kind_outline input[type="checkbox"] {
|
||||
& + label > .mx_Checkbox_background .mx_Checkbox_checkmark {
|
||||
background: var(--cpd-color-bg-accent-rest);
|
||||
}
|
||||
|
||||
&:checked + label > .mx_Checkbox_background {
|
||||
background: transparent;
|
||||
border-color: var(--cpd-color-bg-accent-rest);
|
||||
}
|
||||
}
|
||||
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
Copyright 2024 New Vector Ltd.
|
||||
Copyright 2024, 2025 New Vector Ltd.
|
||||
Copyright 2020 The Matrix.org Foundation C.I.C.
|
||||
|
||||
SPDX-License-Identifier: AGPL-3.0-only OR GPL-3.0-only OR LicenseRef-Element-Commercial
|
||||
@@ -101,6 +101,6 @@ Please see LICENSE files in the repository root for full details.
|
||||
margin: $spacing-12 0 $spacing-4;
|
||||
}
|
||||
|
||||
.mx_RoomSummaryCard_leave {
|
||||
.mx_RoomSummaryCard_bottomOptions {
|
||||
margin: 0 0 var(--cpd-space-8x);
|
||||
}
|
||||
|
||||
@@ -119,9 +119,6 @@ Please see LICENSE files in the repository root for full details.
|
||||
word-break: break-all;
|
||||
text-overflow: ellipsis;
|
||||
|
||||
/* Don't spill over the container */
|
||||
width: 90%;
|
||||
|
||||
/* E2E icon wrapper */
|
||||
.mx_Flex > span {
|
||||
display: inline-block;
|
||||
@@ -130,15 +127,11 @@ Please see LICENSE files in the repository root for full details.
|
||||
|
||||
.mx_UserInfo_profile_name {
|
||||
height: 30px;
|
||||
text-wrap: nowrap;
|
||||
}
|
||||
|
||||
.mx_UserInfo_profile_mxid {
|
||||
color: var(--cpd-color-text-secondary);
|
||||
height: 28px;
|
||||
max-width: 100%;
|
||||
/* MXIDs are one long "word" */
|
||||
word-break: break-all;
|
||||
}
|
||||
|
||||
.mx_UserInfo_profileStatus {
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
Copyright 2024 New Vector Ltd.
|
||||
Copyright 2024,2025 New Vector Ltd.
|
||||
Copyright 2020 The Matrix.org Foundation C.I.C.
|
||||
|
||||
SPDX-License-Identifier: AGPL-3.0-only OR GPL-3.0-only OR LicenseRef-Element-Commercial
|
||||
@@ -393,8 +393,7 @@ Please see LICENSE files in the repository root for full details.
|
||||
margin-bottom: 4px;
|
||||
}
|
||||
|
||||
.mx_StyledRadioButton,
|
||||
.mx_Checkbox {
|
||||
.mx_StyledRadioButton {
|
||||
margin-top: 8px;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
Copyright 2024 New Vector Ltd.
|
||||
Copyright 2024,2025 New Vector Ltd.
|
||||
Copyright 2021 The Matrix.org Foundation C.I.C.
|
||||
|
||||
SPDX-License-Identifier: AGPL-3.0-only OR GPL-3.0-only OR LicenseRef-Element-Commercial
|
||||
@@ -14,17 +14,12 @@ Please see LICENSE files in the repository root for full details.
|
||||
}
|
||||
}
|
||||
|
||||
.mx_SidebarUserSettingsTab_checkbox {
|
||||
margin-bottom: $spacing-8;
|
||||
/* override checkbox styles */
|
||||
label {
|
||||
align-items: flex-start !important;
|
||||
}
|
||||
|
||||
svg {
|
||||
height: 16px;
|
||||
width: 16px;
|
||||
margin-right: $spacing-8;
|
||||
margin-bottom: -1px;
|
||||
}
|
||||
.mx_SidebarUserSettingsTab_icon {
|
||||
margin-right: var(--cpd-space-2x);
|
||||
margin-top: auto;
|
||||
margin-bottom: auto;
|
||||
}
|
||||
|
||||
.mx_SidebarUserSettingsTab_checkbox label {
|
||||
display: flex;
|
||||
}
|
||||
|
||||
@@ -22,7 +22,6 @@ import {
|
||||
type MatrixCall,
|
||||
} from "matrix-js-sdk/src/webrtc/call";
|
||||
import { logger } from "matrix-js-sdk/src/logger";
|
||||
import { PushProcessor } from "matrix-js-sdk/src/pushprocessor";
|
||||
import { CallEventHandlerEvent } from "matrix-js-sdk/src/webrtc/callEventHandler";
|
||||
|
||||
import { MatrixClientPeg } from "./MatrixClientPeg";
|
||||
@@ -596,7 +595,7 @@ export default class LegacyCallHandler extends TypedEventEmitter<LegacyCallHandl
|
||||
|
||||
switch (newState) {
|
||||
case CallState.Ringing: {
|
||||
const incomingCallPushRule = new PushProcessor(MatrixClientPeg.safeGet()).getPushRuleById(
|
||||
const incomingCallPushRule = MatrixClientPeg.safeGet().pushProcessor.getPushRuleById(
|
||||
RuleId.IncomingCall,
|
||||
);
|
||||
const pushRuleEnabled = incomingCallPushRule?.enabled;
|
||||
|
||||
@@ -406,6 +406,39 @@ export function attemptTokenLogin(
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* Load the pickle key inside the credentials or create it if it does not exist for this device.
|
||||
*
|
||||
* @param credentials Holds the device to load/store the pickle key
|
||||
*
|
||||
* @returns {Promise} promise which resolves to the loaded or generated pickle key or undefined if
|
||||
* none was loaded nor generated
|
||||
*/
|
||||
async function loadOrCreatePickleKey(credentials: IMatrixClientCreds): Promise<string | undefined> {
|
||||
// Try to load the pickle key
|
||||
const userId = credentials.userId;
|
||||
const deviceId = credentials.deviceId;
|
||||
let pickleKey = (await PlatformPeg.get()?.getPickleKey(userId, deviceId ?? "")) ?? undefined;
|
||||
if (!pickleKey) {
|
||||
// Create it if it did not exist
|
||||
pickleKey =
|
||||
userId && deviceId
|
||||
? ((await PlatformPeg.get()?.createPickleKey(userId, deviceId)) ?? undefined)
|
||||
: undefined;
|
||||
if (pickleKey) {
|
||||
logger.log(`Created pickle key for ${credentials.userId}|${credentials.deviceId}`);
|
||||
} else {
|
||||
logger.log("Pickle key not created");
|
||||
}
|
||||
} else {
|
||||
logger.log(
|
||||
`Pickle key already exists for ${credentials.userId}|${credentials.deviceId} do not create a new one`,
|
||||
);
|
||||
}
|
||||
|
||||
return pickleKey;
|
||||
}
|
||||
|
||||
/**
|
||||
* Called after a successful token login or OIDC authorization.
|
||||
* Clear storage then save new credentials in storage
|
||||
@@ -413,6 +446,8 @@ export function attemptTokenLogin(
|
||||
*/
|
||||
async function onSuccessfulDelegatedAuthLogin(credentials: IMatrixClientCreds): Promise<void> {
|
||||
await clearStorage();
|
||||
// SSO does not go through setLoggedIn so we need to load/create the pickle key here too
|
||||
credentials.pickleKey = await loadOrCreatePickleKey(credentials);
|
||||
await persistCredentials(credentials);
|
||||
|
||||
// remember that we just logged in
|
||||
@@ -655,18 +690,8 @@ async function handleLoadSessionFailure(e: unknown): Promise<boolean> {
|
||||
export async function setLoggedIn(credentials: IMatrixClientCreds): Promise<MatrixClient> {
|
||||
credentials.freshLogin = true;
|
||||
stopMatrixClient();
|
||||
const pickleKey =
|
||||
credentials.userId && credentials.deviceId
|
||||
? await PlatformPeg.get()?.createPickleKey(credentials.userId, credentials.deviceId)
|
||||
: null;
|
||||
|
||||
if (pickleKey) {
|
||||
logger.log(`Created pickle key for ${credentials.userId}|${credentials.deviceId}`);
|
||||
} else {
|
||||
logger.log("Pickle key not created");
|
||||
}
|
||||
|
||||
return doSetLoggedIn({ ...credentials, pickleKey: pickleKey ?? undefined }, true, true);
|
||||
credentials.pickleKey = await loadOrCreatePickleKey(credentials);
|
||||
return doSetLoggedIn(credentials, true, true);
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
Copyright 2024 New Vector Ltd.
|
||||
Copyright 2024,2025 New Vector Ltd.
|
||||
Copyright 2019 The Matrix.org Foundation C.I.C.
|
||||
Copyright 2018 New Vector Ltd
|
||||
Copyright 2015, 2016 OpenMarket Ltd
|
||||
@@ -16,13 +16,12 @@ import { KeyBindingAction } from "../KeyboardShortcuts";
|
||||
import { getKeyBindingsManager } from "../../KeyBindingsManager";
|
||||
|
||||
interface IProps extends React.ComponentProps<typeof StyledCheckbox> {
|
||||
label?: string;
|
||||
onChange(): void; // we handle keyup/down ourselves so lose the ChangeEvent
|
||||
onClose(): void; // gets called after onChange on KeyBindingAction.ActivateSelectedButton
|
||||
}
|
||||
|
||||
// Semantic component for representing a styled role=menuitemcheckbox
|
||||
export const StyledMenuItemCheckbox: React.FC<IProps> = ({ children, label, onChange, onClose, ...props }) => {
|
||||
export const StyledMenuItemCheckbox: React.FC<IProps> = ({ children, onChange, onClose, ...props }) => {
|
||||
const [onFocus, isActive, ref] = useRovingTabIndex<HTMLInputElement>();
|
||||
|
||||
const onKeyDown = (e: React.KeyboardEvent): void => {
|
||||
@@ -63,7 +62,6 @@ export const StyledMenuItemCheckbox: React.FC<IProps> = ({ children, label, onCh
|
||||
<StyledCheckbox
|
||||
{...props}
|
||||
role="menuitemcheckbox"
|
||||
aria-label={label}
|
||||
onChange={onChange}
|
||||
onKeyDown={onKeyDown}
|
||||
onKeyUp={onKeyUp}
|
||||
|
||||
@@ -1388,7 +1388,7 @@ export default class MatrixChat extends React.PureComponent<IProps, IState> {
|
||||
// so show the homepage.
|
||||
dis.dispatch<ViewHomePagePayload>({ action: Action.ViewHomePage, justRegistered: true });
|
||||
}
|
||||
} else {
|
||||
} else if (!(await this.shouldForceVerification())) {
|
||||
this.showScreenAfterLogin();
|
||||
}
|
||||
|
||||
@@ -2003,9 +2003,17 @@ export default class MatrixChat extends React.PureComponent<IProps, IState> {
|
||||
};
|
||||
|
||||
// complete security / e2e setup has finished
|
||||
private onCompleteSecurityE2eSetupFinished = (): void => {
|
||||
// This is async but we making this function async to wait for it isn't useful
|
||||
this.onShowPostLoginScreen().catch((e) => {
|
||||
private onCompleteSecurityE2eSetupFinished = async (): Promise<void> => {
|
||||
const forceVerify = await this.shouldForceVerification();
|
||||
if (forceVerify) {
|
||||
const isVerified = await MatrixClientPeg.safeGet().getCrypto()?.isCrossSigningReady();
|
||||
if (!isVerified) {
|
||||
// We must verify but we haven't yet verified - don't continue logging in
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
await this.onShowPostLoginScreen().catch((e) => {
|
||||
logger.error("Exception showing post-login screen", e);
|
||||
});
|
||||
};
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
Copyright 2024 New Vector Ltd.
|
||||
Copyright 2024,2025 New Vector Ltd.
|
||||
Copyright 2021-2023 The Matrix.org Foundation C.I.C.
|
||||
|
||||
SPDX-License-Identifier: AGPL-3.0-only OR GPL-3.0-only OR LicenseRef-Element-Commercial
|
||||
@@ -16,6 +16,7 @@ import React, {
|
||||
useCallback,
|
||||
useContext,
|
||||
useEffect,
|
||||
useId,
|
||||
useMemo,
|
||||
useRef,
|
||||
useState,
|
||||
@@ -116,6 +117,7 @@ const Tile: React.FC<ITileProps> = ({
|
||||
const [showChildren, toggleShowChildren] = useStateToggle(true);
|
||||
const [onFocus, isActive, ref, nodeRef] = useRovingTabIndex();
|
||||
const [busy, setBusy] = useState(false);
|
||||
const checkboxLabelId = useId();
|
||||
|
||||
const onPreviewClick = (ev: ButtonEvent): void => {
|
||||
ev.preventDefault();
|
||||
@@ -172,7 +174,14 @@ const Tile: React.FC<ITileProps> = ({
|
||||
let checkbox: ReactElement | undefined;
|
||||
if (onToggleClick) {
|
||||
if (hasPermissions) {
|
||||
checkbox = <StyledCheckbox checked={!!selected} onChange={onToggleClick} tabIndex={isActive ? 0 : -1} />;
|
||||
checkbox = (
|
||||
<StyledCheckbox
|
||||
role="presentation"
|
||||
aria-labelledby={checkboxLabelId}
|
||||
checked={!!selected}
|
||||
tabIndex={-1}
|
||||
/>
|
||||
);
|
||||
} else {
|
||||
checkbox = (
|
||||
<TextWithTooltip
|
||||
@@ -181,7 +190,12 @@ const Tile: React.FC<ITileProps> = ({
|
||||
ev.stopPropagation();
|
||||
}}
|
||||
>
|
||||
<StyledCheckbox disabled={true} tabIndex={isActive ? 0 : -1} />
|
||||
<StyledCheckbox
|
||||
role="presentation"
|
||||
aria-labelledby={checkboxLabelId}
|
||||
disabled={true}
|
||||
tabIndex={-1}
|
||||
/>
|
||||
</TextWithTooltip>
|
||||
);
|
||||
}
|
||||
@@ -248,7 +262,7 @@ const Tile: React.FC<ITileProps> = ({
|
||||
<div className="mx_SpaceHierarchy_roomTile_item">
|
||||
<div className="mx_SpaceHierarchy_roomTile_avatar">{avatar}</div>
|
||||
<div className="mx_SpaceHierarchy_roomTile_name">
|
||||
{name}
|
||||
<span id={checkboxLabelId}>{name}</span>
|
||||
{joinedSection}
|
||||
{suggestedSection}
|
||||
</div>
|
||||
@@ -330,11 +344,14 @@ const Tile: React.FC<ITileProps> = ({
|
||||
};
|
||||
}
|
||||
|
||||
const shouldToggle = hasPermissions && onToggleClick;
|
||||
|
||||
return (
|
||||
<li
|
||||
className="mx_SpaceHierarchy_roomTileWrapper"
|
||||
role="treeitem"
|
||||
aria-selected={selected}
|
||||
aria-labelledby={checkboxLabelId}
|
||||
aria-expanded={children ? showChildren : undefined}
|
||||
>
|
||||
<AccessibleButton
|
||||
@@ -342,7 +359,7 @@ const Tile: React.FC<ITileProps> = ({
|
||||
mx_SpaceHierarchy_subspace: room.room_type === RoomType.Space,
|
||||
mx_SpaceHierarchy_joining: busy,
|
||||
})}
|
||||
onClick={hasPermissions && onToggleClick ? onToggleClick : onPreviewClick}
|
||||
onClick={shouldToggle ? onToggleClick : onPreviewClick}
|
||||
onKeyDown={onKeyDown}
|
||||
ref={ref}
|
||||
onFocus={onFocus}
|
||||
|
||||
@@ -18,7 +18,7 @@ import SpaceStore from "../../../stores/spaces/SpaceStore";
|
||||
import dispatcher from "../../../dispatcher/dispatcher";
|
||||
import { Action } from "../../../dispatcher/actions";
|
||||
import { useMatrixClientContext } from "../../../contexts/MatrixClientContext";
|
||||
import { useIndexForActiveRoom } from "./useIndexForActiveRoom";
|
||||
import { useStickyRoomList } from "./useStickyRoomList";
|
||||
|
||||
export interface RoomListViewState {
|
||||
/**
|
||||
@@ -97,8 +97,14 @@ export interface RoomListViewState {
|
||||
*/
|
||||
export function useRoomListViewModel(): RoomListViewState {
|
||||
const matrixClient = useMatrixClientContext();
|
||||
const { primaryFilters, activePrimaryFilter, rooms, activateSecondaryFilter, activeSecondaryFilter } =
|
||||
useFilteredRooms();
|
||||
const {
|
||||
primaryFilters,
|
||||
activePrimaryFilter,
|
||||
rooms: filteredRooms,
|
||||
activateSecondaryFilter,
|
||||
activeSecondaryFilter,
|
||||
} = useFilteredRooms();
|
||||
const { activeIndex, rooms } = useStickyRoomList(filteredRooms);
|
||||
|
||||
const currentSpace = useEventEmitterState<Room | null>(
|
||||
SpaceStore.instance,
|
||||
@@ -107,7 +113,6 @@ export function useRoomListViewModel(): RoomListViewState {
|
||||
);
|
||||
const canCreateRoom = hasCreateRoomRights(matrixClient, currentSpace);
|
||||
|
||||
const activeIndex = useIndexForActiveRoom(rooms);
|
||||
const { activeSortOption, sort } = useSorter();
|
||||
const { shouldShowMessagePreview, toggleMessagePreview } = useMessagePreviewToggle();
|
||||
|
||||
|
||||
@@ -145,22 +145,14 @@ export function useFilteredRooms(): FilteredRooms {
|
||||
// SecondaryFilter is an enum for the UI, let's convert it to something
|
||||
// that the store will understand.
|
||||
const secondary = secondaryFiltersToFilterKeyMap.get(filter);
|
||||
|
||||
// Active primary filter may need to be toggled off when applying this secondary filer.
|
||||
let primary = primaryFilter;
|
||||
if (
|
||||
primaryFilter !== undefined &&
|
||||
secondary !== undefined &&
|
||||
!isPrimaryFilterCompatible(primaryFilter, secondary)
|
||||
) {
|
||||
primary = undefined;
|
||||
}
|
||||
|
||||
setActiveSecondaryFilter(filter);
|
||||
setPrimaryFilter(primary);
|
||||
updateRoomsFromStore(filterUndefined([primary, secondary]));
|
||||
|
||||
// Reset any active primary filters.
|
||||
setPrimaryFilter(undefined);
|
||||
|
||||
updateRoomsFromStore(filterUndefined([secondary]));
|
||||
},
|
||||
[activeSecondaryFilter, primaryFilter, updateRoomsFromStore],
|
||||
[activeSecondaryFilter, updateRoomsFromStore],
|
||||
);
|
||||
|
||||
/**
|
||||
|
||||
@@ -1,44 +0,0 @@
|
||||
/*
|
||||
* Copyright 2025 New Vector Ltd.
|
||||
*
|
||||
* SPDX-License-Identifier: AGPL-3.0-only OR GPL-3.0-only OR LicenseRef-Element-Commercial
|
||||
* Please see LICENSE files in the repository root for full details.
|
||||
*/
|
||||
|
||||
import { useCallback, useEffect, useState } from "react";
|
||||
|
||||
import { SdkContextClass } from "../../../contexts/SDKContext";
|
||||
import { useDispatcher } from "../../../hooks/useDispatcher";
|
||||
import dispatcher from "../../../dispatcher/dispatcher";
|
||||
import { Action } from "../../../dispatcher/actions";
|
||||
import type { Room } from "matrix-js-sdk/src/matrix";
|
||||
|
||||
/**
|
||||
* Tracks the index of the active room in the given array of rooms.
|
||||
* @param rooms list of rooms
|
||||
* @returns index of the active room or undefined otherwise.
|
||||
*/
|
||||
export function useIndexForActiveRoom(rooms: Room[]): number | undefined {
|
||||
const [index, setIndex] = useState<number | undefined>(undefined);
|
||||
|
||||
const calculateIndex = useCallback(
|
||||
(newRoomId?: string) => {
|
||||
const activeRoomId = newRoomId ?? SdkContextClass.instance.roomViewStore.getRoomId();
|
||||
const index = rooms.findIndex((room) => room.roomId === activeRoomId);
|
||||
setIndex(index === -1 ? undefined : index);
|
||||
},
|
||||
[rooms],
|
||||
);
|
||||
|
||||
// Re-calculate the index when the active room has changed.
|
||||
useDispatcher(dispatcher, (payload) => {
|
||||
if (payload.action === Action.ActiveRoomChanged) calculateIndex(payload.newRoomId);
|
||||
});
|
||||
|
||||
// Re-calculate the index when the list of rooms has changed.
|
||||
useEffect(() => {
|
||||
calculateIndex();
|
||||
}, [calculateIndex, rooms]);
|
||||
|
||||
return index;
|
||||
}
|
||||
117
src/components/viewmodels/roomlist/useStickyRoomList.tsx
Normal file
@@ -0,0 +1,117 @@
|
||||
/*
|
||||
* Copyright 2025 New Vector Ltd.
|
||||
*
|
||||
* SPDX-License-Identifier: AGPL-3.0-only OR GPL-3.0-only OR LicenseRef-Element-Commercial
|
||||
* Please see LICENSE files in the repository root for full details.
|
||||
*/
|
||||
|
||||
import { useCallback, useEffect, useState } from "react";
|
||||
|
||||
import { SdkContextClass } from "../../../contexts/SDKContext";
|
||||
import { useDispatcher } from "../../../hooks/useDispatcher";
|
||||
import dispatcher from "../../../dispatcher/dispatcher";
|
||||
import { Action } from "../../../dispatcher/actions";
|
||||
import type { Room } from "matrix-js-sdk/src/matrix";
|
||||
import type { Optional } from "matrix-events-sdk";
|
||||
|
||||
function getIndexByRoomId(rooms: Room[], roomId: Optional<string>): number | undefined {
|
||||
const index = rooms.findIndex((room) => room.roomId === roomId);
|
||||
return index === -1 ? undefined : index;
|
||||
}
|
||||
|
||||
function getRoomsWithStickyRoom(
|
||||
rooms: Room[],
|
||||
oldIndex: number | undefined,
|
||||
newIndex: number | undefined,
|
||||
isRoomChange: boolean,
|
||||
): { newRooms: Room[]; newIndex: number | undefined } {
|
||||
const updated = { newIndex, newRooms: rooms };
|
||||
if (isRoomChange) {
|
||||
/*
|
||||
* When opening another room, the index should obviously change.
|
||||
*/
|
||||
return updated;
|
||||
}
|
||||
if (newIndex === undefined || oldIndex === undefined) {
|
||||
/*
|
||||
* If oldIndex is undefined, then there was no active room before.
|
||||
* So nothing to do in regards to sticky room.
|
||||
* Similarly, if newIndex is undefined, there's no active room anymore.
|
||||
*/
|
||||
return updated;
|
||||
}
|
||||
if (newIndex === oldIndex) {
|
||||
/*
|
||||
* If the index hasn't changed, we have nothing to do.
|
||||
*/
|
||||
return updated;
|
||||
}
|
||||
if (oldIndex > rooms.length - 1) {
|
||||
/*
|
||||
* If the old index falls out of the bounds of the rooms array
|
||||
* (usually because rooms were removed), we can no longer place
|
||||
* the active room in the same old index.
|
||||
*/
|
||||
return updated;
|
||||
}
|
||||
|
||||
/*
|
||||
* Making the active room sticky is as simple as removing it from
|
||||
* its new index and placing it in the old index.
|
||||
*/
|
||||
const newRooms = [...rooms];
|
||||
const [newRoom] = newRooms.splice(newIndex, 1);
|
||||
newRooms.splice(oldIndex, 0, newRoom);
|
||||
|
||||
return { newIndex: oldIndex, newRooms };
|
||||
}
|
||||
|
||||
interface StickyRoomListResult {
|
||||
/**
|
||||
* List of rooms with sticky active room.
|
||||
*/
|
||||
rooms: Room[];
|
||||
/**
|
||||
* Index of the active room in the room list.
|
||||
*/
|
||||
activeIndex: number | undefined;
|
||||
}
|
||||
|
||||
/**
|
||||
* - Provides a list of rooms such that the active room is sticky i.e the active room is kept
|
||||
* in the same index even when the order of rooms in the list changes.
|
||||
* - Provides the index of the active room.
|
||||
* @param rooms list of rooms
|
||||
* @see {@link StickyRoomListResult} details what this hook returns..
|
||||
*/
|
||||
export function useStickyRoomList(rooms: Room[]): StickyRoomListResult {
|
||||
const [listState, setListState] = useState<{ index: number | undefined; roomsWithStickyRoom: Room[] }>({
|
||||
index: undefined,
|
||||
roomsWithStickyRoom: rooms,
|
||||
});
|
||||
|
||||
const updateRoomsAndIndex = useCallback(
|
||||
(newRoomId?: string, isRoomChange: boolean = false) => {
|
||||
setListState((current) => {
|
||||
const activeRoomId = newRoomId ?? SdkContextClass.instance.roomViewStore.getRoomId();
|
||||
const newActiveIndex = getIndexByRoomId(rooms, activeRoomId);
|
||||
const oldIndex = current.index;
|
||||
const { newIndex, newRooms } = getRoomsWithStickyRoom(rooms, oldIndex, newActiveIndex, isRoomChange);
|
||||
return { index: newIndex, roomsWithStickyRoom: newRooms };
|
||||
});
|
||||
},
|
||||
[rooms],
|
||||
);
|
||||
|
||||
// Re-calculate the index when the active room has changed.
|
||||
useDispatcher(dispatcher, (payload) => {
|
||||
if (payload.action === Action.ActiveRoomChanged) updateRoomsAndIndex(payload.newRoomId, true);
|
||||
});
|
||||
|
||||
// Re-calculate the index when the list of rooms has changed.
|
||||
useEffect(() => {
|
||||
updateRoomsAndIndex();
|
||||
}, [rooms, updateRoomsAndIndex]);
|
||||
|
||||
return { activeIndex: listState.index, rooms: listState.roomsWithStickyRoom };
|
||||
}
|
||||
@@ -1,12 +1,12 @@
|
||||
/*
|
||||
Copyright 2024 New Vector Ltd.
|
||||
Copyright 2024,2025 New Vector Ltd.
|
||||
Copyright 2021 The Matrix.org Foundation C.I.C.
|
||||
|
||||
SPDX-License-Identifier: AGPL-3.0-only OR GPL-3.0-only OR LicenseRef-Element-Commercial
|
||||
Please see LICENSE files in the repository root for full details.
|
||||
*/
|
||||
|
||||
import React, { type ReactElement, type ReactNode, useContext, useMemo, useRef, useState } from "react";
|
||||
import React, { type ReactElement, type ReactNode, useContext, useId, useMemo, useRef, useState } from "react";
|
||||
import classNames from "classnames";
|
||||
import { type Room, EventType } from "matrix-js-sdk/src/matrix";
|
||||
import { KnownMembership } from "matrix-js-sdk/src/types";
|
||||
@@ -53,8 +53,9 @@ export const Entry: React.FC<{
|
||||
checked: boolean;
|
||||
onChange?(value: boolean): void;
|
||||
}> = ({ room, checked, onChange }) => {
|
||||
const id = useId();
|
||||
return (
|
||||
<label className="mx_AddExistingToSpace_entry">
|
||||
<li id={id} className="mx_AddExistingToSpace_entry" aria-label={room.name}>
|
||||
{room?.isSpaceRoom() ? (
|
||||
<RoomAvatar room={room} size="32px" />
|
||||
) : (
|
||||
@@ -62,11 +63,12 @@ export const Entry: React.FC<{
|
||||
)}
|
||||
<span className="mx_AddExistingToSpace_entry_name">{room.name}</span>
|
||||
<StyledCheckbox
|
||||
aria-labelledby={id}
|
||||
onChange={onChange ? (e) => onChange(e.currentTarget.checked) : undefined}
|
||||
checked={checked}
|
||||
disabled={!onChange}
|
||||
/>
|
||||
</label>
|
||||
</li>
|
||||
);
|
||||
};
|
||||
|
||||
@@ -357,6 +359,7 @@ const defaultRendererFactory =
|
||||
<div className="mx_AddExistingToSpace_section">
|
||||
<h3>{_t(title)}</h3>
|
||||
<LazyRenderList
|
||||
element="ul"
|
||||
itemHeight={ROW_HEIGHT}
|
||||
items={rooms}
|
||||
scrollTop={scrollTop}
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
Copyright 2024 New Vector Ltd.
|
||||
Copyright 2024,2025 New Vector Ltd.
|
||||
Copyright 2021 The Matrix.org Foundation C.I.C.
|
||||
|
||||
SPDX-License-Identifier: AGPL-3.0-only OR GPL-3.0-only OR LicenseRef-Element-Commercial
|
||||
@@ -113,12 +113,13 @@ const BulkRedactDialog: React.FC<Props> = (props) => {
|
||||
<div className="mx_Dialog_content" id="mx_Dialog_content">
|
||||
<p>{_t("user_info|redact|confirm_description_1", { count, user })}</p>
|
||||
<p>{_t("user_info|redact|confirm_description_2")}</p>
|
||||
<StyledCheckbox checked={keepStateEvents} onChange={(e) => setKeepStateEvents(e.target.checked)}>
|
||||
<StyledCheckbox
|
||||
description={_t("user_info|redact|confirm_keep_state_explainer")}
|
||||
checked={keepStateEvents}
|
||||
onChange={(e) => setKeepStateEvents(e.target.checked)}
|
||||
>
|
||||
{_t("user_info|redact|confirm_keep_state_label")}
|
||||
</StyledCheckbox>
|
||||
<div className="mx_BulkRedactDialog_checkboxMicrocopy">
|
||||
{_t("user_info|redact|confirm_keep_state_explainer")}
|
||||
</div>
|
||||
</div>
|
||||
<DialogButtons
|
||||
primaryButton={_t("user_info|redact|confirm_button", { count })}
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
Copyright 2024 New Vector Ltd.
|
||||
Copyright 2024,2025 New Vector Ltd.
|
||||
Copyright 2021 The Matrix.org Foundation C.I.C.
|
||||
|
||||
SPDX-License-Identifier: AGPL-3.0-only OR GPL-3.0-only OR LicenseRef-Element-Commercial
|
||||
@@ -78,7 +78,6 @@ const GenericFeatureFeedbackDialog: React.FC<IProps> = ({
|
||||
}}
|
||||
autoFocus={true}
|
||||
/>
|
||||
|
||||
<StyledCheckbox
|
||||
checked={canContact}
|
||||
onChange={(e) => setCanContact((e.target as HTMLInputElement).checked)}
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
Copyright 2024 New Vector Ltd.
|
||||
Copyright 2024,2025 New Vector Ltd.
|
||||
Copyright 2021 The Matrix.org Foundation C.I.C.
|
||||
|
||||
SPDX-License-Identifier: AGPL-3.0-only OR GPL-3.0-only OR LicenseRef-Element-Commercial
|
||||
@@ -44,22 +44,23 @@ const Entry: React.FC<{
|
||||
}
|
||||
|
||||
return (
|
||||
<label className="mx_ManageRestrictedJoinRuleDialog_entry">
|
||||
<div>
|
||||
<div>
|
||||
{localRoom ? <RoomAvatar room={room} size="20px" /> : <RoomAvatar oobData={room} size="20px" />}
|
||||
<span className="mx_ManageRestrictedJoinRuleDialog_entry_name">{room.name}</span>
|
||||
</div>
|
||||
{description && (
|
||||
<div className="mx_ManageRestrictedJoinRuleDialog_entry_description">{description}</div>
|
||||
)}
|
||||
</div>
|
||||
<div className="mx_ManageRestrictedJoinRuleDialog_entry">
|
||||
<StyledCheckbox
|
||||
onChange={onChange ? (e) => onChange(e.target.checked) : undefined}
|
||||
checked={checked}
|
||||
disabled={!onChange}
|
||||
/>
|
||||
</label>
|
||||
description={description}
|
||||
>
|
||||
<div>
|
||||
{localRoom ? (
|
||||
<RoomAvatar role="none" room={room} size="20px" />
|
||||
) : (
|
||||
<RoomAvatar oobData={room} size="20px" />
|
||||
)}
|
||||
<span className="mx_ManageRestrictedJoinRuleDialog_entry_name">{room.name}</span>
|
||||
</div>
|
||||
</StyledCheckbox>
|
||||
</div>
|
||||
);
|
||||
};
|
||||
|
||||
|
||||
95
src/components/views/dialogs/ReportRoomDialog.tsx
Normal file
@@ -0,0 +1,95 @@
|
||||
/*
|
||||
Copyright 2025 New Vector Ltd.
|
||||
|
||||
SPDX-License-Identifier: AGPL-3.0-only OR GPL-3.0-only OR LicenseRef-Element-Commercial
|
||||
Please see LICENSE files in the repository root for full details.
|
||||
*/
|
||||
|
||||
import React, { type ChangeEventHandler, useCallback, useState } from "react";
|
||||
import { Root, Field, Label, InlineSpinner, ErrorMessage } from "@vector-im/compound-web";
|
||||
|
||||
import { _t } from "../../../languageHandler";
|
||||
import SdkConfig from "../../../SdkConfig";
|
||||
import Markdown from "../../../Markdown";
|
||||
import BaseDialog from "./BaseDialog";
|
||||
import DialogButtons from "../elements/DialogButtons";
|
||||
import { MatrixClientPeg } from "../../../MatrixClientPeg";
|
||||
|
||||
interface IProps {
|
||||
roomId: string;
|
||||
onFinished(complete: boolean): void;
|
||||
}
|
||||
|
||||
/*
|
||||
* A dialog for reporting a room.
|
||||
*/
|
||||
|
||||
export const ReportRoomDialog: React.FC<IProps> = function ({ roomId, onFinished }) {
|
||||
const [error, setErr] = useState<string>();
|
||||
const [busy, setBusy] = useState(false);
|
||||
const [sent, setSent] = useState(false);
|
||||
const [reason, setReason] = useState("");
|
||||
const client = MatrixClientPeg.safeGet();
|
||||
|
||||
const onReasonChange = useCallback<ChangeEventHandler<HTMLTextAreaElement>>((e) => setReason(e.target.value), []);
|
||||
const onCancel = useCallback(() => onFinished(sent), [sent, onFinished]);
|
||||
const onSubmit = useCallback(async () => {
|
||||
setBusy(true);
|
||||
try {
|
||||
await client.reportRoom(roomId, reason);
|
||||
setSent(true);
|
||||
} catch (ex) {
|
||||
if (ex instanceof Error) {
|
||||
setErr(ex.message);
|
||||
} else {
|
||||
setErr("Unknown error");
|
||||
}
|
||||
} finally {
|
||||
setBusy(false);
|
||||
}
|
||||
}, [roomId, reason, client]);
|
||||
|
||||
const adminMessageMD = SdkConfig.getObject("report_event")?.get("admin_message_md", "adminMessageMD");
|
||||
let adminMessage: JSX.Element | undefined;
|
||||
if (adminMessageMD) {
|
||||
const html = new Markdown(adminMessageMD).toHTML({ externalLinks: true });
|
||||
adminMessage = <p dangerouslySetInnerHTML={{ __html: html }} />;
|
||||
}
|
||||
|
||||
return (
|
||||
<BaseDialog
|
||||
className="mx_ReportRoomDialog"
|
||||
onFinished={() => onFinished(sent)}
|
||||
title={_t("report_room|title")}
|
||||
contentId="mx_ReportEventDialog"
|
||||
>
|
||||
{sent && <p>{_t("report_room|sent")}</p>}
|
||||
{!sent && (
|
||||
<Root id="mx_ReportEventDialog" onSubmit={onSubmit}>
|
||||
<p>{_t("report_room|description")}</p>
|
||||
{adminMessage}
|
||||
<Field name="reason">
|
||||
<Label htmlFor="mx_ReportRoomDialog_reason">{_t("room_settings|permissions|ban_reason")}</Label>
|
||||
<textarea
|
||||
id="mx_ReportRoomDialog_reason"
|
||||
placeholder={_t("report_room|reason_placeholder")}
|
||||
rows={5}
|
||||
onChange={onReasonChange}
|
||||
value={reason}
|
||||
disabled={busy}
|
||||
/>
|
||||
{error ? <ErrorMessage>{error}</ErrorMessage> : null}
|
||||
</Field>
|
||||
{busy ? <InlineSpinner /> : null}
|
||||
<DialogButtons
|
||||
primaryButton={_t("action|send_report")}
|
||||
onPrimaryButtonClick={onSubmit}
|
||||
focus={true}
|
||||
onCancel={onCancel}
|
||||
disabled={busy}
|
||||
/>
|
||||
</Root>
|
||||
)}
|
||||
</BaseDialog>
|
||||
);
|
||||
};
|
||||
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
Copyright 2024 New Vector Ltd.
|
||||
Copyright 2024,2025 New Vector Ltd.
|
||||
Copyright 2021 The Matrix.org Foundation C.I.C.
|
||||
|
||||
SPDX-License-Identifier: AGPL-3.0-only OR GPL-3.0-only OR LicenseRef-Element-Commercial
|
||||
@@ -45,14 +45,13 @@ const SpacePreferencesAppearanceTab: React.FC<Pick<IProps, "space">> = ({ space
|
||||
!showPeople,
|
||||
);
|
||||
}}
|
||||
description={_t("space|preferences|show_people_in_space", {
|
||||
spaceName: space.name,
|
||||
})}
|
||||
>
|
||||
{_t("common|people")}
|
||||
</StyledCheckbox>
|
||||
<SettingsSubsectionText>
|
||||
{_t("space|preferences|show_people_in_space", {
|
||||
spaceName: space.name,
|
||||
})}
|
||||
</SettingsSubsectionText>
|
||||
<SettingsSubsectionText />
|
||||
</SettingsSubsection>
|
||||
</SettingsSection>
|
||||
</SettingsTab>
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
Copyright 2024 New Vector Ltd.
|
||||
Copyright 2024,2025 New Vector Ltd.
|
||||
Copyright 2020, 2021 The Matrix.org Foundation C.I.C.
|
||||
|
||||
SPDX-License-Identifier: AGPL-3.0-only OR GPL-3.0-only OR LicenseRef-Element-Commercial
|
||||
@@ -100,16 +100,12 @@ export default class WidgetCapabilitiesPromptDialog extends React.PureComponent<
|
||||
});
|
||||
const checkboxRows = orderedCapabilities.map(([cap, isChecked], i) => {
|
||||
const text = CapabilityText.for(cap, this.props.widgetKind);
|
||||
const byline = text.byline ? (
|
||||
<span className="mx_WidgetCapabilitiesPromptDialog_byline">{text.byline}</span>
|
||||
) : null;
|
||||
|
||||
return (
|
||||
<div className="mx_WidgetCapabilitiesPromptDialog_cap" key={cap + i}>
|
||||
<StyledCheckbox checked={isChecked} onChange={() => this.onToggle(cap)}>
|
||||
<StyledCheckbox checked={isChecked} onChange={() => this.onToggle(cap)} description={text.byline}>
|
||||
{text.primary}
|
||||
</StyledCheckbox>
|
||||
{byline}
|
||||
</div>
|
||||
);
|
||||
});
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
Copyright 2024 New Vector Ltd.
|
||||
Copyright 2024,2025 New Vector Ltd.
|
||||
Copyright 2022 The Matrix.org Foundation C.I.C.
|
||||
|
||||
SPDX-License-Identifier: AGPL-3.0-only OR GPL-3.0-only OR LicenseRef-Element-Commercial
|
||||
@@ -28,13 +28,16 @@ interface IProps {
|
||||
|
||||
const LabelledCheckbox: React.FC<IProps> = ({ value, label, byline, disabled, onChange, className }) => {
|
||||
return (
|
||||
<label className={classnames("mx_LabelledCheckbox", className)}>
|
||||
<StyledCheckbox disabled={disabled} checked={value} onChange={(e) => onChange(e.target.checked)} />
|
||||
<div className="mx_LabelledCheckbox_labels">
|
||||
<div className={classnames("mx_LabelledCheckbox", className)}>
|
||||
<StyledCheckbox
|
||||
description={byline}
|
||||
disabled={disabled}
|
||||
checked={value}
|
||||
onChange={(e) => onChange(e.target.checked)}
|
||||
>
|
||||
<span className="mx_LabelledCheckbox_label">{label}</span>
|
||||
{byline ? <span className="mx_LabelledCheckbox_byline">{byline}</span> : null}
|
||||
</div>
|
||||
</label>
|
||||
</StyledCheckbox>
|
||||
</div>
|
||||
);
|
||||
};
|
||||
|
||||
|
||||
@@ -1,64 +1,51 @@
|
||||
/*
|
||||
Copyright 2024 New Vector Ltd.
|
||||
Copyright 2024,2025 New Vector Ltd.
|
||||
Copyright 2020 The Matrix.org Foundation C.I.C.
|
||||
|
||||
SPDX-License-Identifier: AGPL-3.0-only OR GPL-3.0-only OR LicenseRef-Element-Commercial
|
||||
Please see LICENSE files in the repository root for full details.
|
||||
*/
|
||||
|
||||
import React, { type Ref } from "react";
|
||||
import React, { useId, type ReactNode, type Ref } from "react";
|
||||
import { secureRandomString } from "matrix-js-sdk/src/randomstring";
|
||||
import classnames from "classnames";
|
||||
|
||||
export enum CheckboxStyle {
|
||||
Solid = "solid",
|
||||
Outline = "outline",
|
||||
}
|
||||
import { CheckboxInput, Form, HelpMessage, InlineField, Label } from "@vector-im/compound-web";
|
||||
|
||||
interface IProps extends React.InputHTMLAttributes<HTMLInputElement> {
|
||||
inputRef?: Ref<HTMLInputElement>;
|
||||
kind?: CheckboxStyle;
|
||||
id?: string;
|
||||
description?: ReactNode;
|
||||
}
|
||||
|
||||
export default class StyledCheckbox extends React.PureComponent<IProps> {
|
||||
private id: string;
|
||||
const StyledCheckbox: React.FC<IProps> = ({
|
||||
id: initialId,
|
||||
children: label,
|
||||
className,
|
||||
inputRef,
|
||||
description,
|
||||
...otherProps
|
||||
}) => {
|
||||
const id = initialId || "checkbox_" + secureRandomString(10);
|
||||
const name = useId();
|
||||
const descriptionId = useId();
|
||||
return (
|
||||
<Form.Root>
|
||||
<InlineField
|
||||
className={className}
|
||||
name={name}
|
||||
control={
|
||||
<CheckboxInput
|
||||
ref={inputRef}
|
||||
aria-describedby={description ? descriptionId : undefined}
|
||||
id={id}
|
||||
{...otherProps}
|
||||
/>
|
||||
}
|
||||
>
|
||||
{label && <Label htmlFor={id}>{label}</Label>}
|
||||
{description && <HelpMessage id={descriptionId}>{description}</HelpMessage>}
|
||||
</InlineField>
|
||||
</Form.Root>
|
||||
);
|
||||
};
|
||||
|
||||
public static readonly defaultProps = {
|
||||
className: "",
|
||||
};
|
||||
|
||||
public constructor(props: IProps) {
|
||||
super(props);
|
||||
// 56^10 so unlikely chance of collision.
|
||||
this.id = this.props.id || "checkbox_" + secureRandomString(10);
|
||||
}
|
||||
|
||||
public render(): React.ReactNode {
|
||||
/* eslint @typescript-eslint/no-unused-vars: ["error", { "ignoreRestSiblings": true }] */
|
||||
const { children, className, kind = CheckboxStyle.Solid, inputRef, ...otherProps } = this.props;
|
||||
|
||||
const newClassName = classnames("mx_Checkbox", className, {
|
||||
mx_Checkbox_hasKind: kind,
|
||||
[`mx_Checkbox_kind_${kind}`]: kind,
|
||||
});
|
||||
return (
|
||||
<span className={newClassName}>
|
||||
<input
|
||||
// Pass through the ref - used for keyboard shortcut access to some buttons
|
||||
ref={inputRef}
|
||||
id={this.id}
|
||||
{...otherProps}
|
||||
type="checkbox"
|
||||
/>
|
||||
<label htmlFor={this.id}>
|
||||
{/* Using the div to center the image */}
|
||||
<div className="mx_Checkbox_background">
|
||||
<div className="mx_Checkbox_checkmark" />
|
||||
</div>
|
||||
{!!this.props.children && <div>{this.props.children}</div>}
|
||||
</label>
|
||||
</span>
|
||||
);
|
||||
}
|
||||
}
|
||||
export default StyledCheckbox;
|
||||
|
||||
@@ -9,7 +9,7 @@ Please see LICENSE files in the repository root for full details.
|
||||
import React, { createRef, type SyntheticEvent, type MouseEvent, StrictMode } from "react";
|
||||
import { MsgType, PushRuleKind } from "matrix-js-sdk/src/matrix";
|
||||
import { TooltipProvider } from "@vector-im/compound-web";
|
||||
import { globToRegexp } from "matrix-js-sdk/src/utils";
|
||||
import { PushProcessor } from "matrix-js-sdk/src/pushprocessor";
|
||||
|
||||
import * as HtmlUtils from "../../../HtmlUtils";
|
||||
import { formatDate } from "../../../DateUtils";
|
||||
@@ -110,7 +110,10 @@ export default class TextualBody extends React.Component<IBodyProps, IState> {
|
||||
pushDetails.rule.kind === PushRuleKind.ContentSpecific &&
|
||||
pushDetails.rule.pattern
|
||||
) {
|
||||
this.pillifyNotificationKeywords([content], this.regExpForKeywordPattern(pushDetails.rule.pattern));
|
||||
this.pillifyNotificationKeywords(
|
||||
[content],
|
||||
PushProcessor.getPushRuleGlobRegex(pushDetails.rule.pattern, true),
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -235,12 +238,12 @@ export default class TextualBody extends React.Component<IBodyProps, IState> {
|
||||
continue;
|
||||
}
|
||||
const match = text.match(exp);
|
||||
if (!match || match.length < 3) {
|
||||
if (!match || match.length < 2) {
|
||||
node = node.nextSibling;
|
||||
continue;
|
||||
}
|
||||
const keywordText = match[2];
|
||||
const idx = match.index! + match[1].length;
|
||||
const keywordText = match[1];
|
||||
const idx = match.index!;
|
||||
const before = text.substring(0, idx);
|
||||
const after = text.substring(idx + keywordText.length);
|
||||
|
||||
@@ -265,12 +268,6 @@ export default class TextualBody extends React.Component<IBodyProps, IState> {
|
||||
}
|
||||
}
|
||||
|
||||
private regExpForKeywordPattern(pattern: string): RegExp {
|
||||
// Reflects the push notification pattern-matching implementation at
|
||||
// https://github.com/matrix-org/matrix-js-sdk/blob/dbd7d26968b94700827bac525c39afff2c198e61/src/pushprocessor.ts#L570
|
||||
return new RegExp("(^|\\W)(" + globToRegexp(pattern) + ")(\\W|$)", "i");
|
||||
}
|
||||
|
||||
private findLinks(nodes: ArrayLike<Element>): string[] {
|
||||
let links: string[] = [];
|
||||
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
Copyright 2024 New Vector Ltd.
|
||||
Copyright 2024, 2025 New Vector Ltd.
|
||||
Copyright 2020 The Matrix.org Foundation C.I.C.
|
||||
|
||||
SPDX-License-Identifier: AGPL-3.0-only OR GPL-3.0-only OR LicenseRef-Element-Commercial
|
||||
@@ -77,6 +77,7 @@ import { isVideoRoom as calcIsVideoRoom } from "../../../utils/video-rooms";
|
||||
import { usePinnedEvents } from "../../../hooks/usePinnedEvents";
|
||||
import { ReleaseAnnouncement } from "../../structures/ReleaseAnnouncement.tsx";
|
||||
import { useScopedRoomContext } from "../../../contexts/ScopedRoomContext.tsx";
|
||||
import { ReportRoomDialog } from "../dialogs/ReportRoomDialog.tsx";
|
||||
|
||||
interface IProps {
|
||||
room: Room;
|
||||
@@ -231,6 +232,11 @@ const RoomSummaryCard: React.FC<IProps> = ({
|
||||
room_id: room.roomId,
|
||||
});
|
||||
};
|
||||
const onReportRoomClick = (): void => {
|
||||
Modal.createDialog(ReportRoomDialog, {
|
||||
roomId: room.roomId,
|
||||
});
|
||||
};
|
||||
|
||||
const isRoomEncrypted = useIsEncrypted(cli, room);
|
||||
const roomContext = useScopedRoomContext("e2eStatus", "timelineRenderingType");
|
||||
@@ -439,14 +445,21 @@ const RoomSummaryCard: React.FC<IProps> = ({
|
||||
<MenuItem Icon={SettingsIcon} label={_t("common|settings")} onSelect={onRoomSettingsClick} />
|
||||
|
||||
<Separator />
|
||||
|
||||
<MenuItem
|
||||
className="mx_RoomSummaryCard_leave"
|
||||
Icon={LeaveIcon}
|
||||
kind="critical"
|
||||
label={_t("action|leave_room")}
|
||||
onSelect={onLeaveRoomClick}
|
||||
/>
|
||||
<div className="mx_RoomSummaryCard_bottomOptions">
|
||||
<MenuItem
|
||||
className="mx_RoomSummaryCard_leave"
|
||||
Icon={LeaveIcon}
|
||||
kind="critical"
|
||||
label={_t("action|leave_room")}
|
||||
onSelect={onLeaveRoomClick}
|
||||
/>
|
||||
<MenuItem
|
||||
Icon={ErrorIcon}
|
||||
kind="critical"
|
||||
label={_t("action|report_room")}
|
||||
onSelect={onReportRoomClick}
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
</BaseCard>
|
||||
);
|
||||
|
||||
@@ -1409,30 +1409,24 @@ export const UserInfoHeader: React.FC<{
|
||||
<Flex direction="column" align="center" className="mx_UserInfo_profile">
|
||||
<Heading size="sm" weight="semibold" as="h1" dir="auto">
|
||||
<Flex className="mx_UserInfo_profile_name" direction="row-reverse" align="center">
|
||||
<Tooltip isTriggerInteractive={true} placement="left" label={displayName}>
|
||||
<span>
|
||||
{displayName}
|
||||
</span>
|
||||
</Tooltip>
|
||||
{displayName}
|
||||
</Flex>
|
||||
</Heading>
|
||||
{presenceLabel}
|
||||
{timezoneInfo && (
|
||||
<Flex align="center" className="mx_UserInfo_timezone">
|
||||
<Text size="sm" weight="regular">
|
||||
<Tooltip label={timezoneInfo?.timezone ?? ""}>
|
||||
<span>{timezoneInfo?.friendly ?? ""}</span>
|
||||
</Tooltip>
|
||||
</Text>
|
||||
</Flex>
|
||||
)}
|
||||
{userIdentifier && (
|
||||
<Text size="sm" weight="semibold" className="mx_UserInfo_profile_mxid">
|
||||
<CopyableText getTextToCopy={() => userIdentifier} border={false}>
|
||||
{userIdentifier}
|
||||
</CopyableText>
|
||||
</Text>
|
||||
<Tooltip label={timezoneInfo?.timezone ?? ""}>
|
||||
<Flex align="center" className="mx_UserInfo_timezone">
|
||||
<Text size="sm" weight="regular">
|
||||
{timezoneInfo?.friendly ?? ""}
|
||||
</Text>
|
||||
</Flex>
|
||||
</Tooltip>
|
||||
)}
|
||||
<Text size="sm" weight="semibold" className="mx_UserInfo_profile_mxid">
|
||||
<CopyableText getTextToCopy={() => userIdentifier} border={false}>
|
||||
{userIdentifier}
|
||||
</CopyableText>
|
||||
</Text>
|
||||
</Flex>
|
||||
{!hideVerificationSection && <VerificationSection member={member} devices={devices} />}
|
||||
</Container>
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
Copyright 2024 New Vector Ltd.
|
||||
Copyright 2024,2025 New Vector Ltd.
|
||||
Copyright 2022 The Matrix.org Foundation C.I.C.
|
||||
|
||||
SPDX-License-Identifier: AGPL-3.0-only OR GPL-3.0-only OR LicenseRef-Element-Commercial
|
||||
@@ -10,7 +10,7 @@ import React, { type HTMLProps } from "react";
|
||||
import { Tooltip } from "@vector-im/compound-web";
|
||||
|
||||
import { _t } from "../../../../languageHandler";
|
||||
import StyledCheckbox, { CheckboxStyle } from "../../elements/StyledCheckbox";
|
||||
import StyledCheckbox from "../../elements/StyledCheckbox";
|
||||
|
||||
interface Props extends Omit<HTMLProps<HTMLDivElement>, "className"> {
|
||||
selectedDeviceCount: number;
|
||||
@@ -34,7 +34,6 @@ const FilteredDeviceListHeader: React.FC<Props> = ({
|
||||
{!isSelectDisabled && (
|
||||
<Tooltip label={checkboxLabel} placement="top" isTriggerInteractive={false}>
|
||||
<StyledCheckbox
|
||||
kind={CheckboxStyle.Solid}
|
||||
checked={isAllSelected}
|
||||
onChange={toggleSelectAll}
|
||||
id="device-select-all-checkbox"
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
Copyright 2024 New Vector Ltd.
|
||||
Copyright 2024,2025 New Vector Ltd.
|
||||
Copyright 2022 The Matrix.org Foundation C.I.C.
|
||||
|
||||
SPDX-License-Identifier: AGPL-3.0-only OR GPL-3.0-only OR LicenseRef-Element-Commercial
|
||||
@@ -8,7 +8,7 @@ Please see LICENSE files in the repository root for full details.
|
||||
|
||||
import React from "react";
|
||||
|
||||
import StyledCheckbox, { CheckboxStyle } from "../../elements/StyledCheckbox";
|
||||
import StyledCheckbox from "../../elements/StyledCheckbox";
|
||||
import DeviceTile, { type DeviceTileProps } from "./DeviceTile";
|
||||
|
||||
interface Props extends DeviceTileProps {
|
||||
@@ -21,7 +21,6 @@ const SelectableDeviceTile: React.FC<Props> = ({ children, device, isSelected, o
|
||||
return (
|
||||
<div className="mx_SelectableDeviceTile">
|
||||
<StyledCheckbox
|
||||
kind={CheckboxStyle.Solid}
|
||||
checked={isSelected}
|
||||
onChange={onSelect}
|
||||
className="mx_SelectableDeviceTile_checkbox"
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
Copyright 2024 New Vector Ltd.
|
||||
Copyright 2024,2025 New Vector Ltd.
|
||||
Copyright 2021-2023 The Matrix.org Foundation C.I.C.
|
||||
|
||||
SPDX-License-Identifier: AGPL-3.0-only OR GPL-3.0-only OR LicenseRef-Element-Commercial
|
||||
@@ -14,7 +14,6 @@ import {
|
||||
FavouriteSolidIcon,
|
||||
} from "@vector-im/compound-design-tokens/assets/web/icons";
|
||||
|
||||
import { Icon as HashCircleIcon } from "../../../../../../res/img/element-icons/roomlist/hash-circle.svg";
|
||||
import { _t } from "../../../../../languageHandler";
|
||||
import SettingsStore from "../../../../../settings/SettingsStore";
|
||||
import { SettingLevel } from "../../../../../settings/SettingLevel";
|
||||
@@ -24,7 +23,7 @@ import { MetaSpace } from "../../../../../stores/spaces";
|
||||
import PosthogTrackers from "../../../../../PosthogTrackers";
|
||||
import SettingsTab from "../SettingsTab";
|
||||
import { SettingsSection } from "../../shared/SettingsSection";
|
||||
import { SettingsSubsection, SettingsSubsectionText } from "../../shared/SettingsSubsection";
|
||||
import { SettingsSubsection } from "../../shared/SettingsSubsection";
|
||||
import SdkConfig from "../../../../../SdkConfig";
|
||||
|
||||
type InteractionName = "WebSettingsSidebarTabSpacesCheckbox" | "WebQuickSettingsPinToSidebarCheckbox";
|
||||
@@ -87,14 +86,10 @@ const SidebarUserSettingsTab: React.FC = () => {
|
||||
onChange={onMetaSpaceChangeFactory(MetaSpace.Home, "WebSettingsSidebarTabSpacesCheckbox")}
|
||||
className="mx_SidebarUserSettingsTab_checkbox"
|
||||
disabled={homeEnabled}
|
||||
description={_t("settings|sidebar|metaspaces_home_description")}
|
||||
>
|
||||
<SettingsSubsectionText>
|
||||
<HomeSolidIcon />
|
||||
{_t("common|home")}
|
||||
</SettingsSubsectionText>
|
||||
<SettingsSubsectionText>
|
||||
{_t("settings|sidebar|metaspaces_home_description")}
|
||||
</SettingsSubsectionText>
|
||||
<HomeSolidIcon className="mx_SidebarUserSettingsTab_icon" />
|
||||
{_t("common|home")}
|
||||
</StyledCheckbox>
|
||||
|
||||
<StyledCheckbox
|
||||
@@ -103,13 +98,9 @@ const SidebarUserSettingsTab: React.FC = () => {
|
||||
onChange={onAllRoomsInHomeToggle}
|
||||
className="mx_SidebarUserSettingsTab_checkbox mx_SidebarUserSettingsTab_homeAllRoomsCheckbox"
|
||||
data-testid="mx_SidebarUserSettingsTab_homeAllRoomsCheckbox"
|
||||
description={_t("settings|sidebar|metaspaces_home_all_rooms_description")}
|
||||
>
|
||||
<SettingsSubsectionText>
|
||||
{_t("settings|sidebar|metaspaces_home_all_rooms")}
|
||||
</SettingsSubsectionText>
|
||||
<SettingsSubsectionText>
|
||||
{_t("settings|sidebar|metaspaces_home_all_rooms_description")}
|
||||
</SettingsSubsectionText>
|
||||
{_t("settings|sidebar|metaspaces_home_all_rooms")}
|
||||
</StyledCheckbox>
|
||||
|
||||
{!newRoomListEnabled && (
|
||||
@@ -121,14 +112,10 @@ const SidebarUserSettingsTab: React.FC = () => {
|
||||
"WebSettingsSidebarTabSpacesCheckbox",
|
||||
)}
|
||||
className="mx_SidebarUserSettingsTab_checkbox"
|
||||
description={_t("settings|sidebar|metaspaces_favourites_description")}
|
||||
>
|
||||
<SettingsSubsectionText>
|
||||
<FavouriteSolidIcon />
|
||||
{_t("common|favourites")}
|
||||
</SettingsSubsectionText>
|
||||
<SettingsSubsectionText>
|
||||
{_t("settings|sidebar|metaspaces_favourites_description")}
|
||||
</SettingsSubsectionText>
|
||||
<FavouriteSolidIcon className="mx_SidebarUserSettingsTab_icon" />
|
||||
{_t("common|favourites")}
|
||||
</StyledCheckbox>
|
||||
|
||||
<StyledCheckbox
|
||||
@@ -138,14 +125,10 @@ const SidebarUserSettingsTab: React.FC = () => {
|
||||
"WebSettingsSidebarTabSpacesCheckbox",
|
||||
)}
|
||||
className="mx_SidebarUserSettingsTab_checkbox"
|
||||
description={_t("settings|sidebar|metaspaces_people_description")}
|
||||
>
|
||||
<SettingsSubsectionText>
|
||||
<UserProfileSolidIcon />
|
||||
{_t("common|people")}
|
||||
</SettingsSubsectionText>
|
||||
<SettingsSubsectionText>
|
||||
{_t("settings|sidebar|metaspaces_people_description")}
|
||||
</SettingsSubsectionText>
|
||||
<UserProfileSolidIcon className="mx_SidebarUserSettingsTab_icon" />
|
||||
{_t("common|people")}
|
||||
</StyledCheckbox>
|
||||
</>
|
||||
)}
|
||||
@@ -154,14 +137,9 @@ const SidebarUserSettingsTab: React.FC = () => {
|
||||
checked={!!orphansEnabled}
|
||||
onChange={onMetaSpaceChangeFactory(MetaSpace.Orphans, "WebSettingsSidebarTabSpacesCheckbox")}
|
||||
className="mx_SidebarUserSettingsTab_checkbox"
|
||||
description={_t("settings|sidebar|metaspaces_orphans_description")}
|
||||
>
|
||||
<SettingsSubsectionText>
|
||||
<HashCircleIcon />
|
||||
{_t("settings|sidebar|metaspaces_orphans")}
|
||||
</SettingsSubsectionText>
|
||||
<SettingsSubsectionText>
|
||||
{_t("settings|sidebar|metaspaces_orphans_description")}
|
||||
</SettingsSubsectionText>
|
||||
{_t("settings|sidebar|metaspaces_orphans")}
|
||||
</StyledCheckbox>
|
||||
{SettingsStore.getValue("feature_video_rooms") && (
|
||||
<StyledCheckbox
|
||||
@@ -171,12 +149,10 @@ const SidebarUserSettingsTab: React.FC = () => {
|
||||
"WebSettingsSidebarTabSpacesCheckbox",
|
||||
)}
|
||||
className="mx_SidebarUserSettingsTab_checkbox"
|
||||
description={conferenceSubsectionText}
|
||||
>
|
||||
<SettingsSubsectionText>
|
||||
<VideoCallSolidIcon />
|
||||
{_t("settings|sidebar|metaspaces_video_rooms")}
|
||||
</SettingsSubsectionText>
|
||||
<SettingsSubsectionText>{conferenceSubsectionText}</SettingsSubsectionText>
|
||||
<VideoCallSolidIcon className="mx_SidebarUserSettingsTab_icon" />
|
||||
{_t("settings|sidebar|metaspaces_video_rooms")}
|
||||
</StyledCheckbox>
|
||||
)}
|
||||
</SettingsSubsection>
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
Copyright 2024 New Vector Ltd.
|
||||
Copyright 2024,2025 New Vector Ltd.
|
||||
Copyright 2021-2023 The Matrix.org Foundation C.I.C.
|
||||
|
||||
SPDX-License-Identifier: AGPL-3.0-only OR GPL-3.0-only OR LicenseRef-Element-Commercial
|
||||
@@ -12,6 +12,7 @@ import {
|
||||
OverflowHorizontalIcon,
|
||||
UserProfileSolidIcon,
|
||||
FavouriteSolidIcon,
|
||||
PinSolidIcon,
|
||||
} from "@vector-im/compound-design-tokens/assets/web/icons";
|
||||
|
||||
import { _t } from "../../../languageHandler";
|
||||
@@ -25,7 +26,6 @@ import defaultDispatcher from "../../../dispatcher/dispatcher";
|
||||
import { Action } from "../../../dispatcher/actions";
|
||||
import { UserTab } from "../dialogs/UserTab";
|
||||
import QuickThemeSwitcher from "./QuickThemeSwitcher";
|
||||
import { Icon as PinUprightIcon } from "../../../../res/img/element-icons/room/pin-upright.svg";
|
||||
import Modal from "../../../Modal";
|
||||
import DevtoolsDialog from "../dialogs/DevtoolsDialog";
|
||||
import { SdkContextClass } from "../../../contexts/SDKContext";
|
||||
@@ -89,13 +89,12 @@ const QuickSettingsButton: React.FC<{
|
||||
|
||||
{!newRoomListEnabled && (
|
||||
<>
|
||||
<h4 className="mx_QuickSettingsButton_pinToSidebarHeading">
|
||||
<PinUprightIcon className="mx_QuickSettingsButton_icon" />
|
||||
<h4>
|
||||
<PinSolidIcon className="mx_QuickSettingsButton_icon" />
|
||||
{_t("quick_settings|metaspace_section")}
|
||||
</h4>
|
||||
|
||||
<StyledCheckbox
|
||||
className="mx_QuickSettingsButton_favouritesCheckbox"
|
||||
className="mx_QuickSettingsButton_option"
|
||||
checked={!!favouritesEnabled}
|
||||
onChange={onMetaSpaceChangeFactory(
|
||||
MetaSpace.Favourites,
|
||||
@@ -106,7 +105,7 @@ const QuickSettingsButton: React.FC<{
|
||||
{_t("common|favourites")}
|
||||
</StyledCheckbox>
|
||||
<StyledCheckbox
|
||||
className="mx_QuickSettingsButton_peopleCheckbox"
|
||||
className="mx_QuickSettingsButton_option"
|
||||
checked={!!peopleEnabled}
|
||||
onChange={onMetaSpaceChangeFactory(
|
||||
MetaSpace.People,
|
||||
@@ -117,7 +116,7 @@ const QuickSettingsButton: React.FC<{
|
||||
{_t("common|people")}
|
||||
</StyledCheckbox>
|
||||
<AccessibleButton
|
||||
className="mx_QuickSettingsButton_moreOptionsButton"
|
||||
className="mx_QuickSettingsButton_moreOptionsButton mx_QuickSettingsButton_option"
|
||||
onClick={() => {
|
||||
closeMenu();
|
||||
defaultDispatcher.dispatch({
|
||||
|
||||
@@ -104,6 +104,7 @@
|
||||
"reply": "Reply",
|
||||
"reply_in_thread": "Reply in thread",
|
||||
"report_content": "Report Content",
|
||||
"report_room": "Report room",
|
||||
"resend": "Resend",
|
||||
"reset": "Reset",
|
||||
"resume": "Resume",
|
||||
@@ -1810,6 +1811,12 @@
|
||||
"spam_or_propaganda": "Spam or propaganda",
|
||||
"toxic_behaviour": "Toxic Behaviour"
|
||||
},
|
||||
"report_room": {
|
||||
"description": "Report this room to your homeserver admin. This will send the room's unique ID, but if messages are encrypted, the administrator won't be able to read them or view shared files.",
|
||||
"reason_placeholder": " Reason for reporting...",
|
||||
"sent": "Your report was sent.",
|
||||
"title": "Report Room"
|
||||
},
|
||||
"restore_key_backup_dialog": {
|
||||
"count_of_decryption_failures": "Failed to decrypt %(failedCount)s sessions!",
|
||||
"count_of_successfully_restored_keys": "Successfully restored %(sessionCount)s keys",
|
||||
|
||||
@@ -3147,6 +3147,7 @@
|
||||
"view": "Affiche le salon avec cette adresse",
|
||||
"whois": "Affiche des informations à propos de l’utilisateur"
|
||||
},
|
||||
"sliding_sync_legacy_no_longer_supported": "L'ancienne fonctionnalité Sliding Sync n'est plus prise en charge : veuillez vous déconnecter puis vous reconnecter pour activer la nouvelle fonctionnalité Sliding Sync",
|
||||
"space": {
|
||||
"add_existing_room_space": {
|
||||
"create": "Voulez-vous plutôt ajouter un nouveau salon ?",
|
||||
|
||||
@@ -1235,6 +1235,7 @@
|
||||
"change": "Azonosítási kiszolgáló módosítása",
|
||||
"change_prompt": "Bontja a kapcsolatot a(z) <current /> azonosítási kiszolgálóval, és inkább ehhez kapcsolódik: <new />?",
|
||||
"change_server_prompt": "Ha nem szeretné a(z) <server /> kiszolgálót használnia kapcsolatok kereséséhez, és hogy megtalálják az ismerősei, akkor adjon meg egy másik azonosítási kiszolgálót.",
|
||||
"changed": "Az azonosítási kiszolgáló megváltozott",
|
||||
"checking": "Kiszolgáló ellenőrzése",
|
||||
"description_connected": "Jelenleg a(z) <server></server> kiszolgálót használja a kapcsolatok kereséséhez, és hogy megtalálják az ismerősei. A használt azonosítási kiszolgálót alább tudja megváltoztatni.",
|
||||
"description_disconnected": "Jelenleg nem használ azonosítási kiszolgálót. A kapcsolatok kereséséhez, és hogy megtalálják az ismerősei, adjon hozzá egy azonosítási kiszolgálót.",
|
||||
@@ -4027,7 +4028,7 @@
|
||||
"error_need_to_be_logged_in": "Be kell jelentkeznie.",
|
||||
"error_unable_start_audio_stream_description": "A hangközvetítés indítása sikertelen.",
|
||||
"error_unable_start_audio_stream_title": "Az élő adás indítása sikertelen",
|
||||
"modal_data_warning": "Az ezen a képernyőn látható adatok megosztásra kerülnek ezzel: %(widgetDomain)s",
|
||||
"modal_data_warning": "A lenti adatok megosztásra kerülnek ezzel: %(widgetDomain)s",
|
||||
"modal_title_default": "Előugró kisalkalmazás",
|
||||
"no_name": "Ismeretlen alkalmazás",
|
||||
"open_id_permissions_dialog": {
|
||||
|
||||
@@ -459,8 +459,8 @@
|
||||
"all_chats": "Alle chatter",
|
||||
"analytics": "Statistikk",
|
||||
"and_n_others": {
|
||||
"og %(count)s andre …": "other",
|
||||
"og én annen …": "one"
|
||||
"one": "og en annen...",
|
||||
"other": "og %(count)s andre..."
|
||||
},
|
||||
"appearance": "Utseende",
|
||||
"application": "Applikasjon",
|
||||
@@ -1378,8 +1378,8 @@
|
||||
"other": "Invitere %(user)s og %(count)s andre"
|
||||
},
|
||||
"items_and_n_others": {
|
||||
"<Items/> og %(count)s andre": "other",
|
||||
"<Items/> og én annen": "one"
|
||||
"one": "<Items/> og en annen",
|
||||
"other": "<Items/> og %(count)s andre"
|
||||
},
|
||||
"keyboard": {
|
||||
"activate_button": "Aktiver valgt knapp",
|
||||
@@ -2101,9 +2101,12 @@
|
||||
"no_chats": "Ingen chatter ennå",
|
||||
"no_chats_description": "Kom i gang ved å sende meldinger til noen eller ved å opprette et rom",
|
||||
"no_chats_description_no_room_rights": "Kom i gang med å sende meldinger til noen",
|
||||
"no_favourites": "Du har ikke favorittchat ennå",
|
||||
"no_favourites_description": "Du kan legge til en chat til dine favoritter i chat-innstillingene",
|
||||
"no_people": "Du har ikke direkte chatter med noen ennå",
|
||||
"no_people_description": "Du kan fjerne merket for filtre for å se de andre chattene dine",
|
||||
"no_rooms": "Du er ikke med i noen rom ennå",
|
||||
"no_rooms_description": "Du kan fjerne merket for filtre for å se de andre chattene dine",
|
||||
"no_unread": "Gratulerer! Du har ingen uleste meldinger",
|
||||
"show_chats": "Vis alle chatter"
|
||||
},
|
||||
@@ -2531,7 +2534,9 @@
|
||||
"title": "Avansert"
|
||||
},
|
||||
"delete_key_storage": {
|
||||
"breadcrumb_page": "Slett nøkkellagring",
|
||||
"confirm": "Slett nøkkellagring",
|
||||
"description": "Hvis du sletter nøkkellagring, fjernes den kryptografiske identiteten og meldingsnøklene fra serveren og deaktivere følgende sikkerhetsfunksjoner:",
|
||||
"list_first": "Du vil ikke ha kryptert meldingshistorikk på nye enheter",
|
||||
"list_second": "Du vil miste tilgangen til de krypterte meldingene dine hvis du logger av %(brand)s overalt",
|
||||
"title": "Er du sikker på at du vil slå av nøkkellagring og slette den?"
|
||||
@@ -2541,7 +2546,9 @@
|
||||
"device_not_verified_title": "Enhet er ikke verifisert",
|
||||
"dialog_title": "<strong>Innstillinger:</strong> Kryptering",
|
||||
"key_storage": {
|
||||
"allow_key_storage": "Tillat lagring av nøkler"
|
||||
"allow_key_storage": "Tillat lagring av nøkler",
|
||||
"description": "Lagre din kryptografiske identitet og meldingsnøkler sikkert på serveren. Dette lar deg se meldingsloggen din på alle nye enheter. <a>Lær mer </a>",
|
||||
"title": "Nøkkellager"
|
||||
},
|
||||
"recovery": {
|
||||
"change_recovery_confirm_button": "Bekreft ny gjenopprettingsnøkkel",
|
||||
@@ -3636,12 +3643,12 @@
|
||||
"send_state_sent": "Meldingen ble sendt",
|
||||
"summary": {
|
||||
"banned": {
|
||||
"ble bannlyst": "one",
|
||||
"ble bannlyst %(count)s ganger": "other"
|
||||
"one": "ble utestengt",
|
||||
"other": "ble utestengt %(count)s ganger"
|
||||
},
|
||||
"banned_multiple": {
|
||||
"ble bannlyst": "one",
|
||||
"ble bannlyst %(count)s ganger": "other"
|
||||
"one": "ble utestengt",
|
||||
"other": "ble utestengt %(count)s ganger"
|
||||
},
|
||||
"changed_avatar": {
|
||||
"one": "%(oneUser)sendret profilbildet deres",
|
||||
@@ -3652,8 +3659,8 @@
|
||||
"other": "%(severalUsers)sendret profilbildet deres %(count)s ganger"
|
||||
},
|
||||
"changed_name": {
|
||||
"%(oneUser)s endret navnet sitt": "one",
|
||||
"%(oneUser)sendret navnet sitt %(count)s ganger": "other"
|
||||
"one": "%(oneUser)s endret navn",
|
||||
"other": "%(oneUser)s endret navn %(count)s ganger"
|
||||
},
|
||||
"changed_name_multiple": {
|
||||
"%(severalUsers)s endret navnene sine": "one"
|
||||
@@ -3706,12 +3713,12 @@
|
||||
"other": "ble fjernet %(count)s ganger"
|
||||
},
|
||||
"left": {
|
||||
"%(oneUser)s forlot rommet %(count)s ganger": "other",
|
||||
"%(oneUser)s forlot rommet": "one"
|
||||
"one": "%(oneUser)s forlot",
|
||||
"other": "%(oneUser)s forlot %(count)s ganger"
|
||||
},
|
||||
"left_multiple": {
|
||||
"%(severalUsers)s forlot rommet %(count)s ganger": "other",
|
||||
"%(severalUsers)s forlot rommet": "one"
|
||||
"one": "%(severalUsers)s forlot",
|
||||
"other": "%(severalUsers)s forlot %(count)s ganger"
|
||||
},
|
||||
"no_change": {
|
||||
"one": "%(oneUser)sgjorde ingen endringer",
|
||||
@@ -3762,12 +3769,12 @@
|
||||
"other": "%(severalUsers)sendret serverens ACLer %(count)s ganger"
|
||||
},
|
||||
"unbanned": {
|
||||
"fikk bannlysningen sin opphevet %(count)s ganger": "other",
|
||||
"fikk bannlysningen sin opphevet": "one"
|
||||
"one": "fikk opphevet forbudet",
|
||||
"other": "fikk opphevet forbudet %(count)s ganger"
|
||||
},
|
||||
"unbanned_multiple": {
|
||||
"fikk bannlysningene sine opphevet %(count)s ganger": "other",
|
||||
"fikk bannlysningene sine opphevet": "one"
|
||||
"one": "fikk opphevet forbudet",
|
||||
"other": "fikk opphevet forbudet %(count)s ganger"
|
||||
}
|
||||
},
|
||||
"thread_info_basic": "Fra en tråd",
|
||||
|
||||
@@ -64,6 +64,7 @@
|
||||
"go": "Уперед",
|
||||
"go_back": "Назад",
|
||||
"got_it": "Зрозуміло",
|
||||
"hide": "Сховати",
|
||||
"hide_advanced": "Сховати розширені",
|
||||
"hold": "Утримувати",
|
||||
"ignore": "Ігнорувати",
|
||||
@@ -2100,6 +2101,19 @@
|
||||
"add_space_label": "Додати простір",
|
||||
"breadcrumbs_empty": "Немає недавно відвіданих кімнат",
|
||||
"breadcrumbs_label": "Недавно відвідані кімнати",
|
||||
"empty": {
|
||||
"no_chats": "Ще немає бесід",
|
||||
"no_chats_description": "Почніть користування, надіславши комусь повідомлення або створивши кімнату",
|
||||
"no_chats_description_no_room_rights": "Розпочніть користування, написавши комусь повідомлення",
|
||||
"no_favourites": "У вас ще немає обраних бесід",
|
||||
"no_favourites_description": "Ви можете додати бесіду до обраних у її налаштуваннях",
|
||||
"no_people": "У вас ще немає особистих бесід",
|
||||
"no_people_description": "Ви можете очистити фільтри, щоб побачити інші ваші бесіди",
|
||||
"no_rooms": "Ви ще не входили до кімнат",
|
||||
"no_rooms_description": "Ви можете очистити фільтри, щоб побачити інші ваші бесіди",
|
||||
"no_unread": "Вітаємо! У вас немає непрочитаних повідомлень",
|
||||
"show_chats": "Показати всі бесіди"
|
||||
},
|
||||
"failed_add_tag": "Не вдалось додати до кімнати мітку %(tagName)s",
|
||||
"failed_remove_tag": "Не вдалося прибрати з кімнати мітку %(tagName)s",
|
||||
"failed_set_dm_tag": "Не вдалося встановити мітку особистого повідомлення",
|
||||
@@ -3138,6 +3152,7 @@
|
||||
"view": "Перегляд кімнати з вказаною адресою",
|
||||
"whois": "Показує відомості про користувача"
|
||||
},
|
||||
"sliding_sync_legacy_no_longer_supported": "Застаріла ковзна синхронізація більше не підтримується: вийдіть і ввійдіть знову, щоб увімкнути новий прапорець ковзної синхронізації",
|
||||
"space": {
|
||||
"add_existing_room_space": {
|
||||
"create": "Хочете додати нову кімнату натомість?",
|
||||
|
||||
@@ -8,8 +8,6 @@ Please see LICENSE files in the repository root for full details.
|
||||
*/
|
||||
|
||||
import { logger } from "matrix-js-sdk/src/logger";
|
||||
// XXX: This feels wrong.
|
||||
import { PushProcessor } from "matrix-js-sdk/src/pushprocessor";
|
||||
import { PushRuleActionName } from "matrix-js-sdk/src/matrix";
|
||||
|
||||
import SettingController from "./SettingController";
|
||||
@@ -20,8 +18,7 @@ import { type SettingLevel } from "../SettingLevel";
|
||||
// default action on this rule is dont_notify, but it could be something else
|
||||
export function isPushNotifyDisabled(): boolean {
|
||||
// Return the value of the master push rule as a default
|
||||
const processor = new PushProcessor(MatrixClientPeg.safeGet());
|
||||
const masterRule = processor.getPushRuleById(".m.rule.master");
|
||||
const masterRule = MatrixClientPeg.safeGet().pushProcessor.getPushRuleById(".m.rule.master");
|
||||
|
||||
if (!masterRule) {
|
||||
logger.warn("No master push rule! Notifications are disabled for this user.");
|
||||
|
||||
@@ -8,50 +8,50 @@ SPDX-License-Identifier: AGPL-3.0-only OR GPL-3.0-only OR LicenseRef-Element-Com
|
||||
Please see LICENSE files in the repository root for full details.
|
||||
*/
|
||||
|
||||
import React, { type ReactNode } from "react";
|
||||
import * as utils from "matrix-js-sdk/src/utils";
|
||||
import { MatrixError, JoinRule, type Room, type MatrixEvent } from "matrix-js-sdk/src/matrix";
|
||||
import { KnownMembership } from "matrix-js-sdk/src/types";
|
||||
import { logger } from "matrix-js-sdk/src/logger";
|
||||
import { type ViewRoom as ViewRoomEvent } from "@matrix-org/analytics-events/types/typescript/ViewRoom";
|
||||
import { type JoinedRoom as JoinedRoomEvent } from "@matrix-org/analytics-events/types/typescript/JoinedRoom";
|
||||
import { type Optional } from "matrix-events-sdk";
|
||||
import EventEmitter from "events";
|
||||
import { type ViewRoom as ViewRoomEvent } from "@matrix-org/analytics-events/types/typescript/ViewRoom";
|
||||
import {
|
||||
RoomViewLifecycle,
|
||||
type ViewRoomOpts,
|
||||
} from "@matrix-org/react-sdk-module-api/lib/lifecycles/RoomViewLifecycle";
|
||||
import EventEmitter from "events";
|
||||
import { type Optional } from "matrix-events-sdk";
|
||||
import { logger } from "matrix-js-sdk/src/logger";
|
||||
import { JoinRule, MatrixError, type MatrixEvent, type Room } from "matrix-js-sdk/src/matrix";
|
||||
import { KnownMembership } from "matrix-js-sdk/src/types";
|
||||
import * as utils from "matrix-js-sdk/src/utils";
|
||||
import React, { type ReactNode } from "react";
|
||||
|
||||
import { type MatrixDispatcher } from "../dispatcher/dispatcher";
|
||||
import { MatrixClientPeg } from "../MatrixClientPeg";
|
||||
import Modal from "../Modal";
|
||||
import { _t } from "../languageHandler";
|
||||
import { getCachedRoomIDForAlias, storeRoomAliasInCache } from "../RoomAliasCache";
|
||||
import { Action } from "../dispatcher/actions";
|
||||
import { retry } from "../utils/promise";
|
||||
import ErrorDialog from "../components/views/dialogs/ErrorDialog";
|
||||
import { TimelineRenderingType } from "../contexts/RoomContext";
|
||||
import { type ViewRoomPayload } from "../dispatcher/payloads/ViewRoomPayload";
|
||||
import DMRoomMap from "../utils/DMRoomMap";
|
||||
import { isMetaSpace, MetaSpace } from "./spaces";
|
||||
import { type SdkContextClass } from "../contexts/SDKContext";
|
||||
import { Action } from "../dispatcher/actions";
|
||||
import { type MatrixDispatcher } from "../dispatcher/dispatcher";
|
||||
import { type ActionPayload } from "../dispatcher/payloads";
|
||||
import { type ActiveRoomChangedPayload } from "../dispatcher/payloads/ActiveRoomChangedPayload";
|
||||
import { type CancelAskToJoinPayload } from "../dispatcher/payloads/CancelAskToJoinPayload";
|
||||
import { type JoinRoomErrorPayload } from "../dispatcher/payloads/JoinRoomErrorPayload";
|
||||
import { type JoinRoomPayload } from "../dispatcher/payloads/JoinRoomPayload";
|
||||
import { type JoinRoomReadyPayload } from "../dispatcher/payloads/JoinRoomReadyPayload";
|
||||
import { type JoinRoomErrorPayload } from "../dispatcher/payloads/JoinRoomErrorPayload";
|
||||
import { type ViewRoomErrorPayload } from "../dispatcher/payloads/ViewRoomErrorPayload";
|
||||
import ErrorDialog from "../components/views/dialogs/ErrorDialog";
|
||||
import { type ActiveRoomChangedPayload } from "../dispatcher/payloads/ActiveRoomChangedPayload";
|
||||
import SettingsStore from "../settings/SettingsStore";
|
||||
import { awaitRoomDownSync } from "../utils/RoomUpgrade";
|
||||
import { UPDATE_EVENT } from "./AsyncStore";
|
||||
import { type SdkContextClass } from "../contexts/SDKContext";
|
||||
import { CallStore } from "./CallStore";
|
||||
import { type ThreadPayload } from "../dispatcher/payloads/ThreadPayload";
|
||||
import { type ActionPayload } from "../dispatcher/payloads";
|
||||
import { type CancelAskToJoinPayload } from "../dispatcher/payloads/CancelAskToJoinPayload";
|
||||
import { type SubmitAskToJoinPayload } from "../dispatcher/payloads/SubmitAskToJoinPayload";
|
||||
import { ModuleRunner } from "../modules/ModuleRunner";
|
||||
import { setMarkedUnreadState } from "../utils/notifications";
|
||||
import { type ThreadPayload } from "../dispatcher/payloads/ThreadPayload";
|
||||
import { type ViewRoomErrorPayload } from "../dispatcher/payloads/ViewRoomErrorPayload";
|
||||
import { type ViewRoomPayload } from "../dispatcher/payloads/ViewRoomPayload";
|
||||
import { _t } from "../languageHandler";
|
||||
import { MatrixClientPeg } from "../MatrixClientPeg";
|
||||
import Modal from "../Modal";
|
||||
import { ConnectionState, ElementCall } from "../models/Call";
|
||||
import { ModuleRunner } from "../modules/ModuleRunner";
|
||||
import { getCachedRoomIDForAlias, storeRoomAliasInCache } from "../RoomAliasCache";
|
||||
import SettingsStore from "../settings/SettingsStore";
|
||||
import DMRoomMap from "../utils/DMRoomMap";
|
||||
import { setMarkedUnreadState } from "../utils/notifications";
|
||||
import { retry } from "../utils/promise";
|
||||
import { awaitRoomDownSync } from "../utils/RoomUpgrade";
|
||||
import { isVideoRoom } from "../utils/video-rooms";
|
||||
import { UPDATE_EVENT } from "./AsyncStore";
|
||||
import { CallStore } from "./CallStore";
|
||||
import { isMetaSpace, MetaSpace } from "./spaces";
|
||||
|
||||
const NUM_JOIN_RETRY = 5;
|
||||
|
||||
@@ -265,14 +265,14 @@ export class RoomViewStore extends EventEmitter {
|
||||
numMembers > 1000
|
||||
? "MoreThanAThousand"
|
||||
: numMembers > 100
|
||||
? "OneHundredAndOneToAThousand"
|
||||
: numMembers > 10
|
||||
? "ElevenToOneHundred"
|
||||
: numMembers > 2
|
||||
? "ThreeToTen"
|
||||
: numMembers > 1
|
||||
? "Two"
|
||||
: "One";
|
||||
? "OneHundredAndOneToAThousand"
|
||||
: numMembers > 10
|
||||
? "ElevenToOneHundred"
|
||||
: numMembers > 2
|
||||
? "ThreeToTen"
|
||||
: numMembers > 1
|
||||
? "Two"
|
||||
: "One";
|
||||
|
||||
this.stores.posthogAnalytics.trackEvent<JoinedRoomEvent>({
|
||||
eventName: "JoinedRoom",
|
||||
@@ -537,6 +537,33 @@ export class RoomViewStore extends EventEmitter {
|
||||
metricsTrigger: payload.metricsTrigger,
|
||||
});
|
||||
} catch (err) {
|
||||
if (err instanceof MatrixError) {
|
||||
if (err.errcode === "RE_JKI_JOIN_POLICY_URL") {
|
||||
const redirect_url = [
|
||||
window.location.origin,
|
||||
window.location.pathname.replace(/\/$/, ""), // Remove trailing slash if present
|
||||
"/static/join_room_redirect.html"
|
||||
].join("")
|
||||
|
||||
const url = err.data["re.jki.join_policy_url"] + "?redirect_url=" + redirect_url;
|
||||
|
||||
window.addEventListener("message", (event) => {
|
||||
if (event.data instanceof Object) {
|
||||
if ("action" in event.data && event.data.action == "join") {
|
||||
payload.opts = payload.opts || {};
|
||||
payload.opts.token = event.data.token;
|
||||
|
||||
this.joinRoom(payload);
|
||||
}
|
||||
}
|
||||
})
|
||||
|
||||
window.open(url, "join_window");
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
this.dis?.dispatch({
|
||||
action: Action.JoinRoomError,
|
||||
roomId,
|
||||
|
||||
@@ -11,7 +11,7 @@ import { RoomNotificationStateStore } from "../../../notifications/RoomNotificat
|
||||
|
||||
export class UnreadFilter implements Filter {
|
||||
public matches(room: Room): boolean {
|
||||
return RoomNotificationStateStore.instance.getRoomState(room).isUnread;
|
||||
return RoomNotificationStateStore.instance.getRoomState(room).hasUnreadCount;
|
||||
}
|
||||
|
||||
public get key(): FilterKey.UnreadFilter {
|
||||
|
||||
@@ -7,7 +7,6 @@ Please see LICENSE files in the repository root for full details.
|
||||
*/
|
||||
|
||||
import React, { StrictMode } from "react";
|
||||
import { PushProcessor } from "matrix-js-sdk/src/pushprocessor";
|
||||
import { type MatrixClient, type MatrixEvent, RuleId } from "matrix-js-sdk/src/matrix";
|
||||
import { TooltipProvider } from "@vector-im/compound-web";
|
||||
|
||||
@@ -119,11 +118,10 @@ export function pillifyLinks(
|
||||
}
|
||||
|
||||
if (roomNotifTextNodes.length > 0) {
|
||||
const pushProcessor = new PushProcessor(matrixClient);
|
||||
const atRoomRule = pushProcessor.getPushRuleById(
|
||||
const atRoomRule = matrixClient.pushProcessor.getPushRuleById(
|
||||
mxEvent.getContent()["m.mentions"] !== undefined ? RuleId.IsRoomMention : RuleId.AtRoomNotification,
|
||||
);
|
||||
if (atRoomRule && pushProcessor.ruleMatchesEvent(atRoomRule, mxEvent)) {
|
||||
if (atRoomRule && matrixClient.pushProcessor.ruleMatchesEvent(atRoomRule, mxEvent)) {
|
||||
// Now replace all those nodes with Pills
|
||||
for (const roomNotifTextNode of roomNotifTextNodes) {
|
||||
// Set the next node to be processed to the one after the node
|
||||
|
||||
@@ -12,15 +12,16 @@ import {
|
||||
EventType,
|
||||
type RuleId,
|
||||
type IAnnotatedPushRule,
|
||||
type IPushRule,
|
||||
type PushRuleKind,
|
||||
} from "matrix-js-sdk/src/matrix";
|
||||
import { PushProcessor } from "matrix-js-sdk/src/pushprocessor";
|
||||
import { logger } from "matrix-js-sdk/src/logger";
|
||||
|
||||
import { VectorPushRulesDefinitions, type VectorPushRuleDefinition } from "../../notifications";
|
||||
import { updateExistingPushRulesWithActions } from "./updatePushRuleActions";
|
||||
|
||||
const pushRuleAndKindToAnnotated = (
|
||||
ruleAndKind: ReturnType<PushProcessor["getPushRuleAndKindById"]>,
|
||||
ruleAndKind: { rule: IPushRule; kind: PushRuleKind } | null,
|
||||
): IAnnotatedPushRule | undefined =>
|
||||
ruleAndKind
|
||||
? {
|
||||
@@ -34,23 +35,21 @@ const pushRuleAndKindToAnnotated = (
|
||||
* And updates any that are out of sync
|
||||
* Ignores ruleIds that do not exist for the user
|
||||
* @param matrixClient - cli
|
||||
* @param pushProcessor - processor used to retrieve current state of rules
|
||||
* @param ruleId - primary rule
|
||||
* @param definition - VectorPushRuleDefinition of the primary rule
|
||||
*/
|
||||
const monitorSyncedRule = async (
|
||||
matrixClient: MatrixClient,
|
||||
pushProcessor: PushProcessor,
|
||||
ruleId: RuleId | string,
|
||||
definition: VectorPushRuleDefinition,
|
||||
): Promise<void> => {
|
||||
const primaryRule = pushRuleAndKindToAnnotated(pushProcessor.getPushRuleAndKindById(ruleId));
|
||||
const primaryRule = pushRuleAndKindToAnnotated(matrixClient.pushProcessor.getPushRuleAndKindById(ruleId));
|
||||
|
||||
if (!primaryRule) {
|
||||
return;
|
||||
}
|
||||
const syncedRules: IAnnotatedPushRule[] | undefined = definition.syncedRuleIds
|
||||
?.map((ruleId) => pushRuleAndKindToAnnotated(pushProcessor.getPushRuleAndKindById(ruleId)))
|
||||
?.map((ruleId) => pushRuleAndKindToAnnotated(matrixClient.pushProcessor.getPushRuleAndKindById(ruleId)))
|
||||
.filter((n?: IAnnotatedPushRule): n is IAnnotatedPushRule => Boolean(n));
|
||||
|
||||
// no synced rules to manage
|
||||
@@ -94,11 +93,10 @@ export const monitorSyncedPushRules = async (
|
||||
if (accountDataEvent?.getType() !== EventType.PushRules) {
|
||||
return;
|
||||
}
|
||||
const pushProcessor = new PushProcessor(matrixClient);
|
||||
|
||||
Object.entries(VectorPushRulesDefinitions).forEach(async ([ruleId, definition]) => {
|
||||
try {
|
||||
await monitorSyncedRule(matrixClient, pushProcessor, ruleId, definition);
|
||||
await monitorSyncedRule(matrixClient, ruleId, definition);
|
||||
} catch (error) {
|
||||
logger.error(`Failed to fully synchronise push rules for ${ruleId}`, error);
|
||||
}
|
||||
|
||||
@@ -7,7 +7,6 @@ Please see LICENSE files in the repository root for full details.
|
||||
*/
|
||||
|
||||
import { type MatrixClient, type IPushRule, type PushRuleAction, type PushRuleKind } from "matrix-js-sdk/src/matrix";
|
||||
import { PushProcessor } from "matrix-js-sdk/src/pushprocessor";
|
||||
|
||||
/**
|
||||
* Sets the actions for a given push rule id and kind
|
||||
@@ -51,10 +50,8 @@ export const updateExistingPushRulesWithActions = async (
|
||||
ruleIds?: IPushRule["rule_id"][],
|
||||
actions?: PushRuleAction[],
|
||||
): Promise<void> => {
|
||||
const pushProcessor = new PushProcessor(matrixClient);
|
||||
|
||||
const rules: PushRuleAndKind[] | undefined = ruleIds
|
||||
?.map((ruleId) => pushProcessor.getPushRuleAndKindById(ruleId))
|
||||
?.map((ruleId) => matrixClient.pushProcessor.getPushRuleAndKindById(ruleId))
|
||||
.filter((n: PushRuleAndKind | null): n is PushRuleAndKind => Boolean(n));
|
||||
|
||||
if (!rules?.length) {
|
||||
|
||||
@@ -22,6 +22,7 @@ import { mocked } from "jest-mock";
|
||||
import { CallEventHandlerEvent } from "matrix-js-sdk/src/webrtc/callEventHandler";
|
||||
import fetchMock from "fetch-mock-jest";
|
||||
import { waitFor } from "jest-matrix-react";
|
||||
import { PushProcessor } from "matrix-js-sdk/src/pushprocessor";
|
||||
|
||||
import LegacyCallHandler, {
|
||||
AudioID,
|
||||
@@ -473,6 +474,9 @@ describe("LegacyCallHandler without third party protocols", () => {
|
||||
throw new Error("Endpoint unsupported.");
|
||||
};
|
||||
|
||||
// @ts-expect-error
|
||||
MatrixClientPeg.safeGet().pushProcessor = new PushProcessor(MatrixClientPeg.safeGet());
|
||||
|
||||
audioElement = document.createElement("audio");
|
||||
audioElement.id = "remoteAudio";
|
||||
document.body.appendChild(audioElement);
|
||||
|
||||
@@ -19,6 +19,7 @@ import {
|
||||
import { MediaHandler } from "matrix-js-sdk/src/webrtc/mediaHandler";
|
||||
import { logger } from "matrix-js-sdk/src/logger";
|
||||
import userEvent from "@testing-library/user-event";
|
||||
import { PushProcessor } from "matrix-js-sdk/src/pushprocessor";
|
||||
|
||||
import LoggedInView from "../../../../src/components/structures/LoggedInView";
|
||||
import { SDKContext } from "../../../../src/contexts/SDKContext";
|
||||
@@ -75,6 +76,8 @@ describe("<LoggedInView />", () => {
|
||||
jest.clearAllMocks();
|
||||
mockClient.getMediaHandler.mockReturnValue(mediaHandler);
|
||||
mockClient.setPushRuleActions.mockReset().mockResolvedValue({});
|
||||
// @ts-expect-error
|
||||
mockClient.pushProcessor = new PushProcessor(mockClient);
|
||||
});
|
||||
|
||||
describe("synced push rules", () => {
|
||||
|
||||
@@ -22,7 +22,12 @@ import { logger } from "matrix-js-sdk/src/logger";
|
||||
import { OidcError } from "matrix-js-sdk/src/oidc/error";
|
||||
import { type BearerTokenResponse } from "matrix-js-sdk/src/oidc/validate";
|
||||
import { defer, type IDeferred, sleep } from "matrix-js-sdk/src/utils";
|
||||
import { CryptoEvent, UserVerificationStatus } from "matrix-js-sdk/src/crypto-api";
|
||||
import {
|
||||
CryptoEvent,
|
||||
type DeviceVerificationStatus,
|
||||
UserVerificationStatus,
|
||||
type CryptoApi,
|
||||
} from "matrix-js-sdk/src/crypto-api";
|
||||
|
||||
import MatrixChat from "../../../../src/components/structures/MatrixChat";
|
||||
import * as StorageAccess from "../../../../src/utils/StorageAccess";
|
||||
@@ -62,6 +67,7 @@ import { UIFeature } from "../../../../src/settings/UIFeature";
|
||||
import AutoDiscoveryUtils from "../../../../src/utils/AutoDiscoveryUtils";
|
||||
import { type ValidatedServerConfig } from "../../../../src/utils/ValidatedServerConfig";
|
||||
import Modal from "../../../../src/Modal.tsx";
|
||||
import { SetupEncryptionStore } from "../../../../src/stores/SetupEncryptionStore.ts";
|
||||
|
||||
jest.mock("matrix-js-sdk/src/oidc/authorize", () => ({
|
||||
completeAuthorizationCodeGrant: jest.fn(),
|
||||
@@ -894,13 +900,92 @@ describe("<MatrixChat />", () => {
|
||||
});
|
||||
|
||||
describe("unskippable verification", () => {
|
||||
it("should show the complete security screen if unskippable verification is enabled", async () => {
|
||||
beforeEach(() => {
|
||||
// Force verification is turned on in settings
|
||||
defaultProps.config.force_verification = true;
|
||||
|
||||
// And this device is being force-verified (because it logged in after
|
||||
// enforcement was turned on).
|
||||
localStorage.setItem("must_verify_device", "true");
|
||||
|
||||
// lostKeys returns false, meaning there are other devices to verify against
|
||||
const realStore = SetupEncryptionStore.sharedInstance();
|
||||
jest.spyOn(realStore, "lostKeys").mockReturnValue(false);
|
||||
});
|
||||
|
||||
afterEach(() => {
|
||||
jest.restoreAllMocks();
|
||||
// Reset things back to how they were before we started
|
||||
defaultProps.config.force_verification = false;
|
||||
localStorage.removeItem("must_verify_device");
|
||||
});
|
||||
|
||||
it("should show the complete security screen if unskippable verification is enabled", async () => {
|
||||
// Given we have force verification on, and an existing logged-in session
|
||||
// that is not verified (see beforeEach())
|
||||
|
||||
// When we render MatrixChat
|
||||
getComponent();
|
||||
|
||||
await screen.findByRole("heading", { name: "Unable to verify this device", level: 1 });
|
||||
// Then we are asked to verify our device
|
||||
await screen.findByRole("heading", { name: "Verify this device", level: 1 });
|
||||
|
||||
// Sanity: we are not racing with another screen update, so this heading stays visible
|
||||
await screen.findByRole("heading", { name: "Verify this device", level: 1 });
|
||||
});
|
||||
|
||||
it("should not open app after cancelling device verify if unskippable verification is on", async () => {
|
||||
// See https://github.com/element-hq/element-web/issues/29230
|
||||
// We used to allow bypassing force verification by choosing "Verify with
|
||||
// another device" and not completing the verification.
|
||||
|
||||
// Given we have force verification on, and an existing logged-in session
|
||||
// that is not verified (see beforeEach())
|
||||
|
||||
// And our crypto is set up
|
||||
mockClient.getCrypto.mockReturnValue(createMockCrypto());
|
||||
|
||||
// And MatrixChat is rendered
|
||||
getComponent();
|
||||
|
||||
// When we click "Verify with another device"
|
||||
await screen.findByRole("heading", { name: "Verify this device", level: 1 });
|
||||
const verify = screen.getByRole("button", { name: "Verify with another device" });
|
||||
act(() => verify.click());
|
||||
|
||||
// And close the device verification dialog
|
||||
const closeButton = await screen.findByRole("button", { name: "Close dialog" });
|
||||
act(() => closeButton.click());
|
||||
|
||||
// Then we are not allowed in - we are still being asked to verify
|
||||
await screen.findByRole("heading", { name: "Verify this device", level: 1 });
|
||||
});
|
||||
|
||||
function createMockCrypto(): CryptoApi {
|
||||
return {
|
||||
getVersion: jest.fn().mockReturnValue("Version 0"),
|
||||
getVerificationRequestsToDeviceInProgress: jest.fn().mockReturnValue([]),
|
||||
getUserDeviceInfo: jest.fn().mockReturnValue({
|
||||
get: jest
|
||||
.fn()
|
||||
.mockReturnValue(
|
||||
new Map([
|
||||
["devid", { dehydrated: false, getIdentityKey: jest.fn().mockReturnValue("k") }],
|
||||
]),
|
||||
),
|
||||
}),
|
||||
getUserVerificationStatus: jest
|
||||
.fn()
|
||||
.mockResolvedValue(new UserVerificationStatus(true, true, false)),
|
||||
setDeviceIsolationMode: jest.fn(),
|
||||
isDehydrationSupported: jest.fn().mockReturnValue(false),
|
||||
getDeviceVerificationStatus: jest
|
||||
.fn()
|
||||
.mockResolvedValue({ signedByOwner: true } as DeviceVerificationStatus),
|
||||
isCrossSigningReady: jest.fn().mockReturnValue(false),
|
||||
requestOwnUserVerification: jest.fn().mockResolvedValue({ cancel: jest.fn() }),
|
||||
} as any;
|
||||
}
|
||||
});
|
||||
});
|
||||
|
||||
|
||||
@@ -58,6 +58,7 @@ exports[`SpaceHierarchy <SpaceHierarchy /> renders 1`] = `
|
||||
role="tree"
|
||||
>
|
||||
<li
|
||||
aria-labelledby=":r8:"
|
||||
class="mx_SpaceHierarchy_roomTileWrapper"
|
||||
role="treeitem"
|
||||
>
|
||||
@@ -86,7 +87,11 @@ exports[`SpaceHierarchy <SpaceHierarchy /> renders 1`] = `
|
||||
<div
|
||||
class="mx_SpaceHierarchy_roomTile_name"
|
||||
>
|
||||
Unnamed Room
|
||||
<span
|
||||
id=":r8:"
|
||||
>
|
||||
Unnamed Room
|
||||
</span>
|
||||
</div>
|
||||
<div
|
||||
class="mx_SpaceHierarchy_roomTile_info"
|
||||
@@ -104,30 +109,54 @@ exports[`SpaceHierarchy <SpaceHierarchy /> renders 1`] = `
|
||||
>
|
||||
Join
|
||||
</div>
|
||||
<span
|
||||
class="mx_Checkbox mx_Checkbox_hasKind mx_Checkbox_kind_solid"
|
||||
<form
|
||||
class="_root_19upo_16"
|
||||
>
|
||||
<input
|
||||
id="checkbox_vY7Q4uEh9K"
|
||||
tabindex="0"
|
||||
type="checkbox"
|
||||
/>
|
||||
<label
|
||||
for="checkbox_vY7Q4uEh9K"
|
||||
<div
|
||||
class="_inline-field_19upo_32"
|
||||
>
|
||||
<div
|
||||
class="mx_Checkbox_background"
|
||||
class="_inline-field-control_19upo_44"
|
||||
>
|
||||
<div
|
||||
class="mx_Checkbox_checkmark"
|
||||
/>
|
||||
class="_container_1hel1_10"
|
||||
>
|
||||
<input
|
||||
aria-labelledby=":r8:"
|
||||
class="_input_1hel1_18"
|
||||
id="checkbox_MwbPDmfGtm"
|
||||
role="presentation"
|
||||
tabindex="-1"
|
||||
type="checkbox"
|
||||
/>
|
||||
<div
|
||||
class="_ui_1hel1_19"
|
||||
>
|
||||
<svg
|
||||
aria-hidden="true"
|
||||
fill="currentColor"
|
||||
height="1em"
|
||||
viewBox="0 0 24 24"
|
||||
width="1em"
|
||||
xmlns="http://www.w3.org/2000/svg"
|
||||
>
|
||||
<path
|
||||
d="M9.55 17.575q-.2 0-.375-.062a.9.9 0 0 1-.325-.213L4.55 13q-.274-.274-.262-.713.012-.437.287-.712a.95.95 0 0 1 .7-.275q.425 0 .7.275L9.55 15.15l8.475-8.475q.274-.275.713-.275.437 0 .712.275.275.274.275.713 0 .437-.275.712l-9.2 9.2q-.15.15-.325.212a1.1 1.1 0 0 1-.375.063"
|
||||
/>
|
||||
</svg>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</label>
|
||||
</span>
|
||||
<div
|
||||
class="_inline-field-body_19upo_38"
|
||||
/>
|
||||
</div>
|
||||
</form>
|
||||
</div>
|
||||
</div>
|
||||
</li>
|
||||
<li
|
||||
aria-labelledby=":rc:"
|
||||
class="mx_SpaceHierarchy_roomTileWrapper"
|
||||
role="treeitem"
|
||||
>
|
||||
@@ -156,7 +185,11 @@ exports[`SpaceHierarchy <SpaceHierarchy /> renders 1`] = `
|
||||
<div
|
||||
class="mx_SpaceHierarchy_roomTile_name"
|
||||
>
|
||||
Unnamed Room
|
||||
<span
|
||||
id=":rc:"
|
||||
>
|
||||
Unnamed Room
|
||||
</span>
|
||||
</div>
|
||||
<div
|
||||
class="mx_SpaceHierarchy_roomTile_info"
|
||||
@@ -174,30 +207,54 @@ exports[`SpaceHierarchy <SpaceHierarchy /> renders 1`] = `
|
||||
>
|
||||
Join
|
||||
</div>
|
||||
<span
|
||||
class="mx_Checkbox mx_Checkbox_hasKind mx_Checkbox_kind_solid"
|
||||
<form
|
||||
class="_root_19upo_16"
|
||||
>
|
||||
<input
|
||||
id="checkbox_38QgU2Pomx"
|
||||
tabindex="-1"
|
||||
type="checkbox"
|
||||
/>
|
||||
<label
|
||||
for="checkbox_38QgU2Pomx"
|
||||
<div
|
||||
class="_inline-field_19upo_32"
|
||||
>
|
||||
<div
|
||||
class="mx_Checkbox_background"
|
||||
class="_inline-field-control_19upo_44"
|
||||
>
|
||||
<div
|
||||
class="mx_Checkbox_checkmark"
|
||||
/>
|
||||
class="_container_1hel1_10"
|
||||
>
|
||||
<input
|
||||
aria-labelledby=":rc:"
|
||||
class="_input_1hel1_18"
|
||||
id="checkbox_GQvdMWe954"
|
||||
role="presentation"
|
||||
tabindex="-1"
|
||||
type="checkbox"
|
||||
/>
|
||||
<div
|
||||
class="_ui_1hel1_19"
|
||||
>
|
||||
<svg
|
||||
aria-hidden="true"
|
||||
fill="currentColor"
|
||||
height="1em"
|
||||
viewBox="0 0 24 24"
|
||||
width="1em"
|
||||
xmlns="http://www.w3.org/2000/svg"
|
||||
>
|
||||
<path
|
||||
d="M9.55 17.575q-.2 0-.375-.062a.9.9 0 0 1-.325-.213L4.55 13q-.274-.274-.262-.713.012-.437.287-.712a.95.95 0 0 1 .7-.275q.425 0 .7.275L9.55 15.15l8.475-8.475q.274-.275.713-.275.437 0 .712.275.275.274.275.713 0 .437-.275.712l-9.2 9.2q-.15.15-.325.212a1.1 1.1 0 0 1-.375.063"
|
||||
/>
|
||||
</svg>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</label>
|
||||
</span>
|
||||
<div
|
||||
class="_inline-field-body_19upo_38"
|
||||
/>
|
||||
</div>
|
||||
</form>
|
||||
</div>
|
||||
</div>
|
||||
</li>
|
||||
<li
|
||||
aria-labelledby=":rg:"
|
||||
class="mx_SpaceHierarchy_roomTileWrapper"
|
||||
role="treeitem"
|
||||
>
|
||||
@@ -226,7 +283,11 @@ exports[`SpaceHierarchy <SpaceHierarchy /> renders 1`] = `
|
||||
<div
|
||||
class="mx_SpaceHierarchy_roomTile_name"
|
||||
>
|
||||
Knock room
|
||||
<span
|
||||
id=":rg:"
|
||||
>
|
||||
Knock room
|
||||
</span>
|
||||
</div>
|
||||
<div
|
||||
class="mx_SpaceHierarchy_roomTile_info"
|
||||
@@ -244,31 +305,55 @@ exports[`SpaceHierarchy <SpaceHierarchy /> renders 1`] = `
|
||||
>
|
||||
View
|
||||
</div>
|
||||
<span
|
||||
class="mx_Checkbox mx_Checkbox_hasKind mx_Checkbox_kind_solid"
|
||||
<form
|
||||
class="_root_19upo_16"
|
||||
>
|
||||
<input
|
||||
id="checkbox_wKpa6hpi3Y"
|
||||
tabindex="-1"
|
||||
type="checkbox"
|
||||
/>
|
||||
<label
|
||||
for="checkbox_wKpa6hpi3Y"
|
||||
<div
|
||||
class="_inline-field_19upo_32"
|
||||
>
|
||||
<div
|
||||
class="mx_Checkbox_background"
|
||||
class="_inline-field-control_19upo_44"
|
||||
>
|
||||
<div
|
||||
class="mx_Checkbox_checkmark"
|
||||
/>
|
||||
class="_container_1hel1_10"
|
||||
>
|
||||
<input
|
||||
aria-labelledby=":rg:"
|
||||
class="_input_1hel1_18"
|
||||
id="checkbox_DVIAu5CsiH"
|
||||
role="presentation"
|
||||
tabindex="-1"
|
||||
type="checkbox"
|
||||
/>
|
||||
<div
|
||||
class="_ui_1hel1_19"
|
||||
>
|
||||
<svg
|
||||
aria-hidden="true"
|
||||
fill="currentColor"
|
||||
height="1em"
|
||||
viewBox="0 0 24 24"
|
||||
width="1em"
|
||||
xmlns="http://www.w3.org/2000/svg"
|
||||
>
|
||||
<path
|
||||
d="M9.55 17.575q-.2 0-.375-.062a.9.9 0 0 1-.325-.213L4.55 13q-.274-.274-.262-.713.012-.437.287-.712a.95.95 0 0 1 .7-.275q.425 0 .7.275L9.55 15.15l8.475-8.475q.274-.275.713-.275.437 0 .712.275.275.274.275.713 0 .437-.275.712l-9.2 9.2q-.15.15-.325.212a1.1 1.1 0 0 1-.375.063"
|
||||
/>
|
||||
</svg>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</label>
|
||||
</span>
|
||||
<div
|
||||
class="_inline-field-body_19upo_38"
|
||||
/>
|
||||
</div>
|
||||
</form>
|
||||
</div>
|
||||
</div>
|
||||
</li>
|
||||
<li
|
||||
aria-expanded="true"
|
||||
aria-labelledby=":rk:"
|
||||
class="mx_SpaceHierarchy_roomTileWrapper"
|
||||
role="treeitem"
|
||||
>
|
||||
@@ -297,7 +382,11 @@ exports[`SpaceHierarchy <SpaceHierarchy /> renders 1`] = `
|
||||
<div
|
||||
class="mx_SpaceHierarchy_roomTile_name"
|
||||
>
|
||||
Nested space
|
||||
<span
|
||||
id=":rk:"
|
||||
>
|
||||
Nested space
|
||||
</span>
|
||||
</div>
|
||||
<div
|
||||
class="mx_SpaceHierarchy_roomTile_info"
|
||||
@@ -315,26 +404,49 @@ exports[`SpaceHierarchy <SpaceHierarchy /> renders 1`] = `
|
||||
>
|
||||
Join
|
||||
</div>
|
||||
<span
|
||||
class="mx_Checkbox mx_Checkbox_hasKind mx_Checkbox_kind_solid"
|
||||
<form
|
||||
class="_root_19upo_16"
|
||||
>
|
||||
<input
|
||||
id="checkbox_EetmBG4yVC"
|
||||
tabindex="-1"
|
||||
type="checkbox"
|
||||
/>
|
||||
<label
|
||||
for="checkbox_EetmBG4yVC"
|
||||
<div
|
||||
class="_inline-field_19upo_32"
|
||||
>
|
||||
<div
|
||||
class="mx_Checkbox_background"
|
||||
class="_inline-field-control_19upo_44"
|
||||
>
|
||||
<div
|
||||
class="mx_Checkbox_checkmark"
|
||||
/>
|
||||
class="_container_1hel1_10"
|
||||
>
|
||||
<input
|
||||
aria-labelledby=":rk:"
|
||||
class="_input_1hel1_18"
|
||||
id="checkbox_RD7nyrA2oh"
|
||||
role="presentation"
|
||||
tabindex="-1"
|
||||
type="checkbox"
|
||||
/>
|
||||
<div
|
||||
class="_ui_1hel1_19"
|
||||
>
|
||||
<svg
|
||||
aria-hidden="true"
|
||||
fill="currentColor"
|
||||
height="1em"
|
||||
viewBox="0 0 24 24"
|
||||
width="1em"
|
||||
xmlns="http://www.w3.org/2000/svg"
|
||||
>
|
||||
<path
|
||||
d="M9.55 17.575q-.2 0-.375-.062a.9.9 0 0 1-.325-.213L4.55 13q-.274-.274-.262-.713.012-.437.287-.712a.95.95 0 0 1 .7-.275q.425 0 .7.275L9.55 15.15l8.475-8.475q.274-.275.713-.275.437 0 .712.275.275.274.275.713 0 .437-.275.712l-9.2 9.2q-.15.15-.325.212a1.1 1.1 0 0 1-.375.063"
|
||||
/>
|
||||
</svg>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</label>
|
||||
</span>
|
||||
<div
|
||||
class="_inline-field-body_19upo_38"
|
||||
/>
|
||||
</div>
|
||||
</form>
|
||||
</div>
|
||||
<div
|
||||
class="mx_SpaceHierarchy_subspace_toggle mx_SpaceHierarchy_subspace_toggle_shown"
|
||||
@@ -346,6 +458,7 @@ exports[`SpaceHierarchy <SpaceHierarchy /> renders 1`] = `
|
||||
/>
|
||||
</li>
|
||||
<li
|
||||
aria-labelledby=":ro:"
|
||||
class="mx_SpaceHierarchy_roomTileWrapper"
|
||||
role="treeitem"
|
||||
>
|
||||
@@ -374,7 +487,11 @@ exports[`SpaceHierarchy <SpaceHierarchy /> renders 1`] = `
|
||||
<div
|
||||
class="mx_SpaceHierarchy_roomTile_name"
|
||||
>
|
||||
Nested room
|
||||
<span
|
||||
id=":ro:"
|
||||
>
|
||||
Nested room
|
||||
</span>
|
||||
</div>
|
||||
<div
|
||||
class="mx_SpaceHierarchy_roomTile_info"
|
||||
@@ -393,30 +510,53 @@ exports[`SpaceHierarchy <SpaceHierarchy /> renders 1`] = `
|
||||
Join
|
||||
</div>
|
||||
<span
|
||||
aria-labelledby=":r8:"
|
||||
aria-labelledby=":rp:"
|
||||
tabindex="0"
|
||||
>
|
||||
<span
|
||||
class="mx_Checkbox mx_Checkbox_hasKind mx_Checkbox_kind_solid"
|
||||
<form
|
||||
class="_root_19upo_16"
|
||||
>
|
||||
<input
|
||||
disabled=""
|
||||
id="checkbox_eEefiPqpMR"
|
||||
tabindex="-1"
|
||||
type="checkbox"
|
||||
/>
|
||||
<label
|
||||
for="checkbox_eEefiPqpMR"
|
||||
<div
|
||||
class="_inline-field_19upo_32"
|
||||
>
|
||||
<div
|
||||
class="mx_Checkbox_background"
|
||||
class="_inline-field-control_19upo_44"
|
||||
>
|
||||
<div
|
||||
class="mx_Checkbox_checkmark"
|
||||
/>
|
||||
class="_container_1hel1_10"
|
||||
>
|
||||
<input
|
||||
aria-labelledby=":ro:"
|
||||
class="_input_1hel1_18"
|
||||
disabled=""
|
||||
id="checkbox_jWVJIPauy1"
|
||||
role="presentation"
|
||||
tabindex="-1"
|
||||
type="checkbox"
|
||||
/>
|
||||
<div
|
||||
class="_ui_1hel1_19"
|
||||
>
|
||||
<svg
|
||||
aria-hidden="true"
|
||||
fill="currentColor"
|
||||
height="1em"
|
||||
viewBox="0 0 24 24"
|
||||
width="1em"
|
||||
xmlns="http://www.w3.org/2000/svg"
|
||||
>
|
||||
<path
|
||||
d="M9.55 17.575q-.2 0-.375-.062a.9.9 0 0 1-.325-.213L4.55 13q-.274-.274-.262-.713.012-.437.287-.712a.95.95 0 0 1 .7-.275q.425 0 .7.275L9.55 15.15l8.475-8.475q.274-.275.713-.275.437 0 .712.275.275.274.275.713 0 .437-.275.712l-9.2 9.2q-.15.15-.325.212a1.1 1.1 0 0 1-.375.063"
|
||||
/>
|
||||
</svg>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</label>
|
||||
</span>
|
||||
<div
|
||||
class="_inline-field-body_19upo_38"
|
||||
/>
|
||||
</div>
|
||||
</form>
|
||||
</span>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
@@ -162,6 +162,29 @@ describe("RoomListViewModel", () => {
|
||||
expect(vm.current.activePrimaryFilter).toEqual(vm.current.primaryFilters[i]);
|
||||
});
|
||||
|
||||
it("should remove any active primary filters when secondary filter is changed", async () => {
|
||||
const { fn } = mockAndCreateRooms();
|
||||
const { result: vm } = renderHook(() => useRoomListViewModel());
|
||||
|
||||
// Let's first toggle the People filter
|
||||
const i = vm.current.primaryFilters.findIndex((f) => f.name === "People");
|
||||
act(() => {
|
||||
vm.current.primaryFilters[i].toggle();
|
||||
});
|
||||
expect(vm.current.primaryFilters[i].active).toEqual(true);
|
||||
|
||||
// Let's say we toggle the mentions secondary filter
|
||||
act(() => {
|
||||
vm.current.activateSecondaryFilter(SecondaryFilters.MentionsOnly);
|
||||
});
|
||||
|
||||
// Primary filer should have been unapplied
|
||||
expect(vm.current.primaryFilters[i].active).toEqual(false);
|
||||
|
||||
// RLS call must include only the secondary filter
|
||||
expect(fn).toHaveBeenLastCalledWith(expect.arrayContaining([FilterKey.MentionsFilter]));
|
||||
});
|
||||
|
||||
const testcases: Array<[string, { secondary: SecondaryFilters; filterKey: FilterKey }, string]> = [
|
||||
[
|
||||
"Mentions only",
|
||||
@@ -291,34 +314,51 @@ describe("RoomListViewModel", () => {
|
||||
});
|
||||
});
|
||||
|
||||
describe("active index", () => {
|
||||
it("should recalculate active index when list of rooms change", () => {
|
||||
describe("Sticky room and active index", () => {
|
||||
function expectActiveRoom(vm: ReturnType<typeof useRoomListViewModel>, i: number, roomId: string) {
|
||||
expect(vm.activeIndex).toEqual(i);
|
||||
expect(vm.rooms[i].roomId).toEqual(roomId);
|
||||
}
|
||||
|
||||
it("active room and active index are retained on order change", () => {
|
||||
const { rooms } = mockAndCreateRooms();
|
||||
// Let's say that the first room is the active room initially
|
||||
jest.spyOn(SdkContextClass.instance.roomViewStore, "getRoomId").mockImplementation(() => rooms[0].roomId);
|
||||
|
||||
// Let's say that the room at index 5 is active
|
||||
const roomId = rooms[5].roomId;
|
||||
jest.spyOn(SdkContextClass.instance.roomViewStore, "getRoomId").mockImplementation(() => roomId);
|
||||
|
||||
const { result: vm } = renderHook(() => useRoomListViewModel());
|
||||
expect(vm.current.activeIndex).toEqual(0);
|
||||
expect(vm.current.activeIndex).toEqual(5);
|
||||
|
||||
// Let's say that a new room is added and that becomes active
|
||||
const newRoom = mkStubRoom("bar:matrix.org", "Bar", undefined);
|
||||
jest.spyOn(SdkContextClass.instance.roomViewStore, "getRoomId").mockImplementation(() => newRoom.roomId);
|
||||
rooms.push(newRoom);
|
||||
// Let's say that room at index 9 moves to index 5
|
||||
const room9 = rooms[9];
|
||||
rooms.splice(9, 1);
|
||||
rooms.splice(5, 0, room9);
|
||||
act(() => RoomListStoreV3.instance.emit(LISTS_UPDATE_EVENT));
|
||||
|
||||
// Now the active room should be the last room which we just added
|
||||
expect(vm.current.activeIndex).toEqual(rooms.length - 1);
|
||||
// Active room index should still be 5
|
||||
expectActiveRoom(vm.current, 5, roomId);
|
||||
|
||||
// Let's add 2 new rooms from index 0
|
||||
const newRoom1 = mkStubRoom("bar1:matrix.org", "Bar 1", undefined);
|
||||
const newRoom2 = mkStubRoom("bar2:matrix.org", "Bar 2", undefined);
|
||||
rooms.unshift(newRoom1, newRoom2);
|
||||
act(() => RoomListStoreV3.instance.emit(LISTS_UPDATE_EVENT));
|
||||
|
||||
// Active room index should still be 5
|
||||
expectActiveRoom(vm.current, 5, roomId);
|
||||
});
|
||||
|
||||
it("should recalculate active index when active room changes", () => {
|
||||
it("active room and active index are updated when another room is opened", () => {
|
||||
const { rooms } = mockAndCreateRooms();
|
||||
const roomId = rooms[5].roomId;
|
||||
jest.spyOn(SdkContextClass.instance.roomViewStore, "getRoomId").mockImplementation(() => roomId);
|
||||
|
||||
const { result: vm } = renderHook(() => useRoomListViewModel());
|
||||
expectActiveRoom(vm.current, 5, roomId);
|
||||
|
||||
// No active room yet
|
||||
expect(vm.current.activeIndex).toBeUndefined();
|
||||
|
||||
// Let's say that room at index 5 becomes active
|
||||
const room = rooms[5];
|
||||
// Let's say that room at index 9 becomes active
|
||||
const room = rooms[9];
|
||||
act(() => {
|
||||
dispatcher.dispatch(
|
||||
{
|
||||
@@ -330,8 +370,76 @@ describe("RoomListViewModel", () => {
|
||||
);
|
||||
});
|
||||
|
||||
// We expect index 5 to be active now
|
||||
expect(vm.current.activeIndex).toEqual(5);
|
||||
// Active room index should change to reflect new room
|
||||
expectActiveRoom(vm.current, 9, room.roomId);
|
||||
});
|
||||
|
||||
it("active room and active index are updated when active index spills out of rooms array bounds", () => {
|
||||
const { rooms } = mockAndCreateRooms();
|
||||
// Let's say that the room at index 5 is active
|
||||
const roomId = rooms[5].roomId;
|
||||
jest.spyOn(SdkContextClass.instance.roomViewStore, "getRoomId").mockImplementation(() => roomId);
|
||||
|
||||
const { result: vm } = renderHook(() => useRoomListViewModel());
|
||||
expectActiveRoom(vm.current, 5, roomId);
|
||||
|
||||
// Let's say that we remove rooms from the start of the array
|
||||
for (let i = 0; i < 4; ++i) {
|
||||
// We should be able to do 4 deletions before we run out of rooms
|
||||
rooms.splice(0, 1);
|
||||
act(() => RoomListStoreV3.instance.emit(LISTS_UPDATE_EVENT));
|
||||
expectActiveRoom(vm.current, 5, roomId);
|
||||
}
|
||||
|
||||
// If we remove one more room from the start, there's not going to be enough rooms
|
||||
// to maintain the active index.
|
||||
rooms.splice(0, 1);
|
||||
act(() => RoomListStoreV3.instance.emit(LISTS_UPDATE_EVENT));
|
||||
expectActiveRoom(vm.current, 0, roomId);
|
||||
});
|
||||
|
||||
it("active room and active index are retained when rooms that appear after the active room are deleted", () => {
|
||||
const { rooms } = mockAndCreateRooms();
|
||||
// Let's say that the room at index 5 is active
|
||||
const roomId = rooms[5].roomId;
|
||||
jest.spyOn(SdkContextClass.instance.roomViewStore, "getRoomId").mockImplementation(() => roomId);
|
||||
|
||||
const { result: vm } = renderHook(() => useRoomListViewModel());
|
||||
expectActiveRoom(vm.current, 5, roomId);
|
||||
|
||||
// Let's say that we remove rooms from the start of the array
|
||||
for (let i = 0; i < 4; ++i) {
|
||||
// Deleting rooms after index 5 (active) should not update the active index
|
||||
rooms.splice(6, 1);
|
||||
act(() => RoomListStoreV3.instance.emit(LISTS_UPDATE_EVENT));
|
||||
expectActiveRoom(vm.current, 5, roomId);
|
||||
}
|
||||
});
|
||||
|
||||
it("active room index becomes undefined when active room is deleted", () => {
|
||||
const { rooms } = mockAndCreateRooms();
|
||||
// Let's say that the room at index 5 is active
|
||||
let roomId: string | undefined = rooms[5].roomId;
|
||||
jest.spyOn(SdkContextClass.instance.roomViewStore, "getRoomId").mockImplementation(() => roomId);
|
||||
|
||||
const { result: vm } = renderHook(() => useRoomListViewModel());
|
||||
expectActiveRoom(vm.current, 5, roomId);
|
||||
|
||||
// Let's remove the active room (i.e room at index 5)
|
||||
rooms.splice(5, 1);
|
||||
roomId = undefined;
|
||||
act(() => RoomListStoreV3.instance.emit(LISTS_UPDATE_EVENT));
|
||||
expect(vm.current.activeIndex).toBeUndefined();
|
||||
});
|
||||
|
||||
it("active room index is initially undefined", () => {
|
||||
mockAndCreateRooms();
|
||||
|
||||
// Let's say that there's no active room currently
|
||||
jest.spyOn(SdkContextClass.instance.roomViewStore, "getRoomId").mockImplementation(() => undefined);
|
||||
|
||||
const { result: vm } = renderHook(() => useRoomListViewModel());
|
||||
expect(vm.current.activeIndex).toEqual(undefined);
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
@@ -0,0 +1,66 @@
|
||||
/*
|
||||
Copyright 2025 New Vector Ltd.
|
||||
|
||||
SPDX-License-Identifier: AGPL-3.0-only OR GPL-3.0-only OR LicenseRef-Element-Commercial
|
||||
Please see LICENSE files in the repository root for full details.
|
||||
*/
|
||||
|
||||
import { render } from "jest-matrix-react";
|
||||
import userEvent from "@testing-library/user-event";
|
||||
import React from "react";
|
||||
|
||||
import { ReportRoomDialog } from "../../../../../src/components/views/dialogs/ReportRoomDialog";
|
||||
import SdkConfig from "../../../../../src/SdkConfig";
|
||||
import { stubClient } from "../../../../test-utils";
|
||||
|
||||
const ROOM_ID = "!foo:bar";
|
||||
|
||||
describe("ReportRoomDialog", () => {
|
||||
const onFinished: jest.Mock<any, any> = jest.fn();
|
||||
const reportRoom: jest.Mock<any, any> = jest.fn();
|
||||
beforeEach(() => {
|
||||
jest.resetAllMocks();
|
||||
const client = stubClient();
|
||||
client.reportRoom = reportRoom;
|
||||
|
||||
SdkConfig.put({
|
||||
report_event: {
|
||||
admin_message_md: `
|
||||
# You should know
|
||||
|
||||
This doesn't actually go **anywhere**.`,
|
||||
},
|
||||
});
|
||||
});
|
||||
|
||||
afterEach(() => {
|
||||
SdkConfig.reset();
|
||||
});
|
||||
|
||||
it("can close the dialog", async () => {
|
||||
const { getByTestId } = render(<ReportRoomDialog roomId={ROOM_ID} onFinished={onFinished} />);
|
||||
await userEvent.click(getByTestId("dialog-cancel-button"));
|
||||
expect(onFinished).toHaveBeenCalledWith(false);
|
||||
});
|
||||
|
||||
it("displays admin message", async () => {
|
||||
const { container } = render(<ReportRoomDialog roomId={ROOM_ID} onFinished={onFinished} />);
|
||||
expect(container).toMatchSnapshot();
|
||||
});
|
||||
|
||||
it("can submit a report", async () => {
|
||||
const REASON = "This room is bad!";
|
||||
const { getByLabelText, getByText, getByRole } = render(
|
||||
<ReportRoomDialog roomId={ROOM_ID} onFinished={onFinished} />,
|
||||
);
|
||||
|
||||
await userEvent.type(getByLabelText("Reason"), REASON);
|
||||
await userEvent.click(getByRole("button", { name: "Send report" }));
|
||||
|
||||
expect(reportRoom).toHaveBeenCalledWith(ROOM_ID, REASON);
|
||||
expect(getByText("Your report was sent.")).toBeInTheDocument();
|
||||
|
||||
await userEvent.click(getByRole("button", { name: "Close dialog" }));
|
||||
expect(onFinished).toHaveBeenCalledWith(true);
|
||||
});
|
||||
});
|
||||
@@ -150,28 +150,53 @@ exports[`<ExportDialog /> renders export dialog 1`] = `
|
||||
</span>
|
||||
</span>
|
||||
</div>
|
||||
<span
|
||||
class="mx_Checkbox mx_ExportDialog_attachments-checkbox mx_Checkbox_hasKind mx_Checkbox_kind_solid"
|
||||
<form
|
||||
class="_root_19upo_16"
|
||||
>
|
||||
<input
|
||||
id="include-attachments"
|
||||
type="checkbox"
|
||||
/>
|
||||
<label
|
||||
for="include-attachments"
|
||||
<div
|
||||
class="_inline-field_19upo_32 mx_ExportDialog_attachments-checkbox"
|
||||
>
|
||||
<div
|
||||
class="mx_Checkbox_background"
|
||||
class="_inline-field-control_19upo_44"
|
||||
>
|
||||
<div
|
||||
class="mx_Checkbox_checkmark"
|
||||
/>
|
||||
class="_container_1hel1_10"
|
||||
>
|
||||
<input
|
||||
class="_input_1hel1_18"
|
||||
id="include-attachments"
|
||||
type="checkbox"
|
||||
/>
|
||||
<div
|
||||
class="_ui_1hel1_19"
|
||||
>
|
||||
<svg
|
||||
aria-hidden="true"
|
||||
fill="currentColor"
|
||||
height="1em"
|
||||
viewBox="0 0 24 24"
|
||||
width="1em"
|
||||
xmlns="http://www.w3.org/2000/svg"
|
||||
>
|
||||
<path
|
||||
d="M9.55 17.575q-.2 0-.375-.062a.9.9 0 0 1-.325-.213L4.55 13q-.274-.274-.262-.713.012-.437.287-.712a.95.95 0 0 1 .7-.275q.425 0 .7.275L9.55 15.15l8.475-8.475q.274-.275.713-.275.437 0 .712.275.275.274.275.713 0 .437-.275.712l-9.2 9.2q-.15.15-.325.212a1.1 1.1 0 0 1-.375.063"
|
||||
/>
|
||||
</svg>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div>
|
||||
Include Attachments
|
||||
<div
|
||||
class="_inline-field-body_19upo_38"
|
||||
>
|
||||
<label
|
||||
class="_label_19upo_59"
|
||||
for="include-attachments"
|
||||
>
|
||||
Include Attachments
|
||||
</label>
|
||||
</div>
|
||||
</label>
|
||||
</span>
|
||||
</div>
|
||||
</form>
|
||||
</div>
|
||||
<div
|
||||
class="mx_Dialog_buttons"
|
||||
|
||||
@@ -59,53 +59,80 @@ exports[`<ManageRestrictedJoinRuleDialog /> should list spaces which are not par
|
||||
<h3>
|
||||
Other spaces you know
|
||||
</h3>
|
||||
<label
|
||||
<div
|
||||
class="mx_ManageRestrictedJoinRuleDialog_entry"
|
||||
>
|
||||
<div>
|
||||
<div>
|
||||
<span
|
||||
class="_avatar_1qbcf_8 mx_BaseAvatar _avatar-imageless_1qbcf_52"
|
||||
data-color="1"
|
||||
data-testid="avatar-img"
|
||||
data-type="round"
|
||||
role="presentation"
|
||||
style="--cpd-avatar-size: 20px;"
|
||||
>
|
||||
O
|
||||
</span>
|
||||
<span
|
||||
class="mx_ManageRestrictedJoinRuleDialog_entry_name"
|
||||
>
|
||||
Other Space
|
||||
</span>
|
||||
</div>
|
||||
<div
|
||||
class="mx_ManageRestrictedJoinRuleDialog_entry_description"
|
||||
>
|
||||
0 members
|
||||
</div>
|
||||
</div>
|
||||
<span
|
||||
class="mx_Checkbox mx_Checkbox_hasKind mx_Checkbox_kind_solid"
|
||||
<form
|
||||
class="_root_19upo_16"
|
||||
>
|
||||
<input
|
||||
id="checkbox_vY7Q4uEh9K"
|
||||
type="checkbox"
|
||||
/>
|
||||
<label
|
||||
for="checkbox_vY7Q4uEh9K"
|
||||
<div
|
||||
class="_inline-field_19upo_32"
|
||||
>
|
||||
<div
|
||||
class="mx_Checkbox_background"
|
||||
class="_inline-field-control_19upo_44"
|
||||
>
|
||||
<div
|
||||
class="mx_Checkbox_checkmark"
|
||||
/>
|
||||
class="_container_1hel1_10"
|
||||
>
|
||||
<input
|
||||
aria-describedby=":r5:"
|
||||
class="_input_1hel1_18"
|
||||
id="checkbox_vY7Q4uEh9K"
|
||||
type="checkbox"
|
||||
/>
|
||||
<div
|
||||
class="_ui_1hel1_19"
|
||||
>
|
||||
<svg
|
||||
aria-hidden="true"
|
||||
fill="currentColor"
|
||||
height="1em"
|
||||
viewBox="0 0 24 24"
|
||||
width="1em"
|
||||
xmlns="http://www.w3.org/2000/svg"
|
||||
>
|
||||
<path
|
||||
d="M9.55 17.575q-.2 0-.375-.062a.9.9 0 0 1-.325-.213L4.55 13q-.274-.274-.262-.713.012-.437.287-.712a.95.95 0 0 1 .7-.275q.425 0 .7.275L9.55 15.15l8.475-8.475q.274-.275.713-.275.437 0 .712.275.275.274.275.713 0 .437-.275.712l-9.2 9.2q-.15.15-.325.212a1.1 1.1 0 0 1-.375.063"
|
||||
/>
|
||||
</svg>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</label>
|
||||
</span>
|
||||
</label>
|
||||
<div
|
||||
class="_inline-field-body_19upo_38"
|
||||
>
|
||||
<label
|
||||
class="_label_19upo_59"
|
||||
for="checkbox_vY7Q4uEh9K"
|
||||
>
|
||||
<div>
|
||||
<span
|
||||
class="_avatar_1qbcf_8 mx_BaseAvatar _avatar-imageless_1qbcf_52"
|
||||
data-color="1"
|
||||
data-testid="avatar-img"
|
||||
data-type="round"
|
||||
role="none"
|
||||
style="--cpd-avatar-size: 20px;"
|
||||
>
|
||||
O
|
||||
</span>
|
||||
<span
|
||||
class="mx_ManageRestrictedJoinRuleDialog_entry_name"
|
||||
>
|
||||
Other Space
|
||||
</span>
|
||||
</div>
|
||||
</label>
|
||||
<span
|
||||
class="_message_19upo_85 _help-message_19upo_91"
|
||||
id=":r5:"
|
||||
>
|
||||
0 members
|
||||
</span>
|
||||
</div>
|
||||
</div>
|
||||
</form>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div
|
||||
|
||||
@@ -0,0 +1,100 @@
|
||||
// Jest Snapshot v1, https://goo.gl/fbAQLP
|
||||
|
||||
exports[`ReportRoomDialog displays admin message 1`] = `
|
||||
<div>
|
||||
<div
|
||||
data-focus-guard="true"
|
||||
style="width: 1px; height: 0px; padding: 0px; overflow: hidden; position: fixed; top: 1px; left: 1px;"
|
||||
tabindex="0"
|
||||
/>
|
||||
<div
|
||||
aria-describedby="mx_ReportEventDialog"
|
||||
aria-labelledby="mx_BaseDialog_title"
|
||||
class="mx_ReportRoomDialog mx_Dialog_fixedWidth"
|
||||
data-focus-lock-disabled="false"
|
||||
role="dialog"
|
||||
>
|
||||
<div
|
||||
class="mx_Dialog_header"
|
||||
>
|
||||
<h1
|
||||
class="mx_Heading_h3 mx_Dialog_title"
|
||||
id="mx_BaseDialog_title"
|
||||
>
|
||||
Report Room
|
||||
</h1>
|
||||
</div>
|
||||
<form
|
||||
class="_root_19upo_16"
|
||||
id="mx_ReportEventDialog"
|
||||
>
|
||||
<p>
|
||||
Report this room to your homeserver admin. This will send the room's unique ID, but if messages are encrypted, the administrator won't be able to read them or view shared files.
|
||||
</p>
|
||||
<p>
|
||||
<h1>
|
||||
You should know
|
||||
</h1>
|
||||
|
||||
|
||||
<p>
|
||||
This doesn't actually go
|
||||
<strong>
|
||||
anywhere
|
||||
</strong>
|
||||
.
|
||||
</p>
|
||||
|
||||
|
||||
</p>
|
||||
<div
|
||||
class="_field_19upo_26"
|
||||
>
|
||||
<label
|
||||
class="_label_19upo_59"
|
||||
for="mx_ReportRoomDialog_reason"
|
||||
>
|
||||
Reason
|
||||
</label>
|
||||
<textarea
|
||||
id="mx_ReportRoomDialog_reason"
|
||||
placeholder=" Reason for reporting..."
|
||||
rows="5"
|
||||
/>
|
||||
</div>
|
||||
<div
|
||||
class="mx_Dialog_buttons"
|
||||
>
|
||||
<span
|
||||
class="mx_Dialog_buttons_row"
|
||||
>
|
||||
<button
|
||||
data-testid="dialog-cancel-button"
|
||||
type="button"
|
||||
>
|
||||
Cancel
|
||||
</button>
|
||||
<button
|
||||
class="mx_Dialog_primary"
|
||||
data-testid="dialog-primary-button"
|
||||
type="button"
|
||||
>
|
||||
Send report
|
||||
</button>
|
||||
</span>
|
||||
</div>
|
||||
</form>
|
||||
<div
|
||||
aria-label="Close dialog"
|
||||
class="mx_AccessibleButton mx_Dialog_cancelButton"
|
||||
role="button"
|
||||
tabindex="0"
|
||||
/>
|
||||
</div>
|
||||
<div
|
||||
data-focus-guard="true"
|
||||
style="width: 1px; height: 0px; padding: 0px; overflow: hidden; position: fixed; top: 1px; left: 1px;"
|
||||
tabindex="0"
|
||||
/>
|
||||
</div>
|
||||
`;
|
||||
@@ -2,81 +2,129 @@
|
||||
|
||||
exports[`<LabelledCheckbox /> should render with byline of "this is a byline" 1`] = `
|
||||
<DocumentFragment>
|
||||
<label
|
||||
<div
|
||||
class="mx_LabelledCheckbox"
|
||||
>
|
||||
<span
|
||||
class="mx_Checkbox mx_Checkbox_hasKind mx_Checkbox_kind_solid"
|
||||
<form
|
||||
class="_root_19upo_16"
|
||||
>
|
||||
<input
|
||||
checked=""
|
||||
id="checkbox_vY7Q4uEh9K"
|
||||
type="checkbox"
|
||||
/>
|
||||
<label
|
||||
for="checkbox_vY7Q4uEh9K"
|
||||
<div
|
||||
class="_inline-field_19upo_32"
|
||||
>
|
||||
<div
|
||||
class="mx_Checkbox_background"
|
||||
class="_inline-field-control_19upo_44"
|
||||
>
|
||||
<div
|
||||
class="mx_Checkbox_checkmark"
|
||||
/>
|
||||
class="_container_1hel1_10"
|
||||
>
|
||||
<input
|
||||
aria-describedby=":r4:"
|
||||
checked=""
|
||||
class="_input_1hel1_18"
|
||||
id="checkbox_vY7Q4uEh9K"
|
||||
type="checkbox"
|
||||
/>
|
||||
<div
|
||||
class="_ui_1hel1_19"
|
||||
>
|
||||
<svg
|
||||
aria-hidden="true"
|
||||
fill="currentColor"
|
||||
height="1em"
|
||||
viewBox="0 0 24 24"
|
||||
width="1em"
|
||||
xmlns="http://www.w3.org/2000/svg"
|
||||
>
|
||||
<path
|
||||
d="M9.55 17.575q-.2 0-.375-.062a.9.9 0 0 1-.325-.213L4.55 13q-.274-.274-.262-.713.012-.437.287-.712a.95.95 0 0 1 .7-.275q.425 0 .7.275L9.55 15.15l8.475-8.475q.274-.275.713-.275.437 0 .712.275.275.274.275.713 0 .437-.275.712l-9.2 9.2q-.15.15-.325.212a1.1 1.1 0 0 1-.375.063"
|
||||
/>
|
||||
</svg>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</label>
|
||||
</span>
|
||||
<div
|
||||
class="mx_LabelledCheckbox_labels"
|
||||
>
|
||||
<span
|
||||
class="mx_LabelledCheckbox_label"
|
||||
>
|
||||
Hello world
|
||||
</span>
|
||||
<span
|
||||
class="mx_LabelledCheckbox_byline"
|
||||
>
|
||||
this is a byline
|
||||
</span>
|
||||
</div>
|
||||
</label>
|
||||
<div
|
||||
class="_inline-field-body_19upo_38"
|
||||
>
|
||||
<label
|
||||
class="_label_19upo_59"
|
||||
for="checkbox_vY7Q4uEh9K"
|
||||
>
|
||||
<span
|
||||
class="mx_LabelledCheckbox_label"
|
||||
>
|
||||
Hello world
|
||||
</span>
|
||||
</label>
|
||||
<span
|
||||
class="_message_19upo_85 _help-message_19upo_91"
|
||||
id=":r4:"
|
||||
>
|
||||
this is a byline
|
||||
</span>
|
||||
</div>
|
||||
</div>
|
||||
</form>
|
||||
</div>
|
||||
</DocumentFragment>
|
||||
`;
|
||||
|
||||
exports[`<LabelledCheckbox /> should render with byline of undefined 1`] = `
|
||||
<DocumentFragment>
|
||||
<label
|
||||
<div
|
||||
class="mx_LabelledCheckbox"
|
||||
>
|
||||
<span
|
||||
class="mx_Checkbox mx_Checkbox_hasKind mx_Checkbox_kind_solid"
|
||||
<form
|
||||
class="_root_19upo_16"
|
||||
>
|
||||
<input
|
||||
checked=""
|
||||
id="checkbox_vY7Q4uEh9K"
|
||||
type="checkbox"
|
||||
/>
|
||||
<label
|
||||
for="checkbox_vY7Q4uEh9K"
|
||||
<div
|
||||
class="_inline-field_19upo_32"
|
||||
>
|
||||
<div
|
||||
class="mx_Checkbox_background"
|
||||
class="_inline-field-control_19upo_44"
|
||||
>
|
||||
<div
|
||||
class="mx_Checkbox_checkmark"
|
||||
/>
|
||||
class="_container_1hel1_10"
|
||||
>
|
||||
<input
|
||||
checked=""
|
||||
class="_input_1hel1_18"
|
||||
id="checkbox_vY7Q4uEh9K"
|
||||
type="checkbox"
|
||||
/>
|
||||
<div
|
||||
class="_ui_1hel1_19"
|
||||
>
|
||||
<svg
|
||||
aria-hidden="true"
|
||||
fill="currentColor"
|
||||
height="1em"
|
||||
viewBox="0 0 24 24"
|
||||
width="1em"
|
||||
xmlns="http://www.w3.org/2000/svg"
|
||||
>
|
||||
<path
|
||||
d="M9.55 17.575q-.2 0-.375-.062a.9.9 0 0 1-.325-.213L4.55 13q-.274-.274-.262-.713.012-.437.287-.712a.95.95 0 0 1 .7-.275q.425 0 .7.275L9.55 15.15l8.475-8.475q.274-.275.713-.275.437 0 .712.275.275.274.275.713 0 .437-.275.712l-9.2 9.2q-.15.15-.325.212a1.1 1.1 0 0 1-.375.063"
|
||||
/>
|
||||
</svg>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</label>
|
||||
</span>
|
||||
<div
|
||||
class="mx_LabelledCheckbox_labels"
|
||||
>
|
||||
<span
|
||||
class="mx_LabelledCheckbox_label"
|
||||
>
|
||||
Hello world
|
||||
</span>
|
||||
</div>
|
||||
</label>
|
||||
<div
|
||||
class="_inline-field-body_19upo_38"
|
||||
>
|
||||
<label
|
||||
class="_label_19upo_59"
|
||||
for="checkbox_vY7Q4uEh9K"
|
||||
>
|
||||
<span
|
||||
class="mx_LabelledCheckbox_label"
|
||||
>
|
||||
Hello world
|
||||
</span>
|
||||
</label>
|
||||
</div>
|
||||
</div>
|
||||
</form>
|
||||
</div>
|
||||
</DocumentFragment>
|
||||
`;
|
||||
|
||||
@@ -10,6 +10,7 @@ import React from "react";
|
||||
import { type MatrixClient, type MatrixEvent, PushRuleKind } from "matrix-js-sdk/src/matrix";
|
||||
import { mocked, type MockedObject } from "jest-mock";
|
||||
import { render, waitFor } from "jest-matrix-react";
|
||||
import { PushProcessor } from "matrix-js-sdk/src/pushprocessor";
|
||||
|
||||
import { getMockClientWithEventEmitter, mkEvent, mkMessage, mkStubRoom } from "../../../../test-utils";
|
||||
import { MatrixClientPeg } from "../../../../../src/MatrixClientPeg";
|
||||
@@ -85,6 +86,8 @@ describe("<TextualBody />", () => {
|
||||
throw new Error("MockClient event not found");
|
||||
},
|
||||
});
|
||||
// @ts-expect-error
|
||||
defaultMatrixClient.pushProcessor = new PushProcessor(defaultMatrixClient);
|
||||
|
||||
mocked(defaultRoom).findEventById.mockImplementation((eventId: string) => {
|
||||
if (eventId === defaultEvent.getId()) return defaultEvent;
|
||||
|
||||
@@ -609,46 +609,87 @@ exports[`<RoomSummaryCard /> has button to edit topic 1`] = `
|
||||
data-orientation="horizontal"
|
||||
role="separator"
|
||||
/>
|
||||
<button
|
||||
class="mx_RoomSummaryCard_leave _item_1x5l4_8 _interactive_1x5l4_26"
|
||||
data-kind="critical"
|
||||
role="menuitem"
|
||||
<div
|
||||
class="mx_RoomSummaryCard_bottomOptions"
|
||||
>
|
||||
<svg
|
||||
aria-hidden="true"
|
||||
class="_icon_1x5l4_34"
|
||||
fill="currentColor"
|
||||
height="24"
|
||||
viewBox="0 0 24 24"
|
||||
width="24"
|
||||
xmlns="http://www.w3.org/2000/svg"
|
||||
<button
|
||||
class="mx_RoomSummaryCard_leave _item_1x5l4_8 _interactive_1x5l4_26"
|
||||
data-kind="critical"
|
||||
role="menuitem"
|
||||
>
|
||||
<path
|
||||
d="M14 13q.424 0 .713-.287A.97.97 0 0 0 15 12a.97.97 0 0 0-.287-.713A.97.97 0 0 0 14 11a.97.97 0 0 0-.713.287A.97.97 0 0 0 13 12q0 .424.287.713.288.287.713.287"
|
||||
/>
|
||||
<path
|
||||
d="M10.385 21.788A1 1 0 0 1 10 21V3a1.003 1.003 0 0 1 1.242-.97l8 2A1 1 0 0 1 20 5v14a1 1 0 0 1-.758.97l-8 2a1 1 0 0 1-.857-.182M18 5.781l-6-1.5v15.438l6-1.5zM9 6H7v12h2v2H7a2 2 0 0 1-2-2V6a2 2 0 0 1 2-2h2z"
|
||||
/>
|
||||
</svg>
|
||||
<span
|
||||
class="_typography_6v6n8_153 _font-body-md-medium_6v6n8_60 _label_1x5l4_43"
|
||||
<svg
|
||||
aria-hidden="true"
|
||||
class="_icon_1x5l4_34"
|
||||
fill="currentColor"
|
||||
height="24"
|
||||
viewBox="0 0 24 24"
|
||||
width="24"
|
||||
xmlns="http://www.w3.org/2000/svg"
|
||||
>
|
||||
<path
|
||||
d="M14 13q.424 0 .713-.287A.97.97 0 0 0 15 12a.97.97 0 0 0-.287-.713A.97.97 0 0 0 14 11a.97.97 0 0 0-.713.287A.97.97 0 0 0 13 12q0 .424.287.713.288.287.713.287"
|
||||
/>
|
||||
<path
|
||||
d="M10.385 21.788A1 1 0 0 1 10 21V3a1.003 1.003 0 0 1 1.242-.97l8 2A1 1 0 0 1 20 5v14a1 1 0 0 1-.758.97l-8 2a1 1 0 0 1-.857-.182M18 5.781l-6-1.5v15.438l6-1.5zM9 6H7v12h2v2H7a2 2 0 0 1-2-2V6a2 2 0 0 1 2-2h2z"
|
||||
/>
|
||||
</svg>
|
||||
<span
|
||||
class="_typography_6v6n8_153 _font-body-md-medium_6v6n8_60 _label_1x5l4_43"
|
||||
>
|
||||
Leave room
|
||||
</span>
|
||||
<svg
|
||||
aria-hidden="true"
|
||||
class="_nav-hint_1x5l4_50"
|
||||
fill="currentColor"
|
||||
height="24"
|
||||
viewBox="8 0 8 24"
|
||||
width="8"
|
||||
xmlns="http://www.w3.org/2000/svg"
|
||||
>
|
||||
<path
|
||||
d="M8.7 17.3a.95.95 0 0 1-.275-.7q0-.425.275-.7l3.9-3.9-3.9-3.9a.95.95 0 0 1-.275-.7q0-.425.275-.7a.95.95 0 0 1 .7-.275q.425 0 .7.275l4.6 4.6q.15.15.213.325.062.175.062.375t-.062.375a.9.9 0 0 1-.213.325l-4.6 4.6a.95.95 0 0 1-.7.275.95.95 0 0 1-.7-.275"
|
||||
/>
|
||||
</svg>
|
||||
</button>
|
||||
<button
|
||||
class="_item_1x5l4_8 _interactive_1x5l4_26"
|
||||
data-kind="critical"
|
||||
role="menuitem"
|
||||
>
|
||||
Leave room
|
||||
</span>
|
||||
<svg
|
||||
aria-hidden="true"
|
||||
class="_nav-hint_1x5l4_50"
|
||||
fill="currentColor"
|
||||
height="24"
|
||||
viewBox="8 0 8 24"
|
||||
width="8"
|
||||
xmlns="http://www.w3.org/2000/svg"
|
||||
>
|
||||
<path
|
||||
d="M8.7 17.3a.95.95 0 0 1-.275-.7q0-.425.275-.7l3.9-3.9-3.9-3.9a.95.95 0 0 1-.275-.7q0-.425.275-.7a.95.95 0 0 1 .7-.275q.425 0 .7.275l4.6 4.6q.15.15.213.325.062.175.062.375t-.062.375a.9.9 0 0 1-.213.325l-4.6 4.6a.95.95 0 0 1-.7.275.95.95 0 0 1-.7-.275"
|
||||
/>
|
||||
</svg>
|
||||
</button>
|
||||
<svg
|
||||
aria-hidden="true"
|
||||
class="_icon_1x5l4_34"
|
||||
fill="currentColor"
|
||||
height="24"
|
||||
viewBox="0 0 24 24"
|
||||
width="24"
|
||||
xmlns="http://www.w3.org/2000/svg"
|
||||
>
|
||||
<path
|
||||
d="M12 17q.424 0 .713-.288A.97.97 0 0 0 13 16a.97.97 0 0 0-.287-.713A.97.97 0 0 0 12 15a.97.97 0 0 0-.713.287A.97.97 0 0 0 11 16q0 .424.287.712.288.288.713.288m0-4q.424 0 .713-.287A.97.97 0 0 0 13 12V8a.97.97 0 0 0-.287-.713A.97.97 0 0 0 12 7a.97.97 0 0 0-.713.287A.97.97 0 0 0 11 8v4q0 .424.287.713.288.287.713.287m0 9a9.7 9.7 0 0 1-3.9-.788 10.1 10.1 0 0 1-3.175-2.137q-1.35-1.35-2.137-3.175A9.7 9.7 0 0 1 2 12q0-2.075.788-3.9a10.1 10.1 0 0 1 2.137-3.175q1.35-1.35 3.175-2.137A9.7 9.7 0 0 1 12 2q2.075 0 3.9.788a10.1 10.1 0 0 1 3.175 2.137q1.35 1.35 2.137 3.175A9.7 9.7 0 0 1 22 12a9.7 9.7 0 0 1-.788 3.9 10.1 10.1 0 0 1-2.137 3.175q-1.35 1.35-3.175 2.137A9.7 9.7 0 0 1 12 22"
|
||||
/>
|
||||
</svg>
|
||||
<span
|
||||
class="_typography_6v6n8_153 _font-body-md-medium_6v6n8_60 _label_1x5l4_43"
|
||||
>
|
||||
Report room
|
||||
</span>
|
||||
<svg
|
||||
aria-hidden="true"
|
||||
class="_nav-hint_1x5l4_50"
|
||||
fill="currentColor"
|
||||
height="24"
|
||||
viewBox="8 0 8 24"
|
||||
width="8"
|
||||
xmlns="http://www.w3.org/2000/svg"
|
||||
>
|
||||
<path
|
||||
d="M8.7 17.3a.95.95 0 0 1-.275-.7q0-.425.275-.7l3.9-3.9-3.9-3.9a.95.95 0 0 1-.275-.7q0-.425.275-.7a.95.95 0 0 1 .7-.275q.425 0 .7.275l4.6 4.6q.15.15.213.325.062.175.062.375t-.062.375a.9.9 0 0 1-.213.325l-4.6 4.6a.95.95 0 0 1-.7.275.95.95 0 0 1-.7-.275"
|
||||
/>
|
||||
</svg>
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
@@ -1227,46 +1268,87 @@ exports[`<RoomSummaryCard /> renders the room summary 1`] = `
|
||||
data-orientation="horizontal"
|
||||
role="separator"
|
||||
/>
|
||||
<button
|
||||
class="mx_RoomSummaryCard_leave _item_1x5l4_8 _interactive_1x5l4_26"
|
||||
data-kind="critical"
|
||||
role="menuitem"
|
||||
<div
|
||||
class="mx_RoomSummaryCard_bottomOptions"
|
||||
>
|
||||
<svg
|
||||
aria-hidden="true"
|
||||
class="_icon_1x5l4_34"
|
||||
fill="currentColor"
|
||||
height="24"
|
||||
viewBox="0 0 24 24"
|
||||
width="24"
|
||||
xmlns="http://www.w3.org/2000/svg"
|
||||
<button
|
||||
class="mx_RoomSummaryCard_leave _item_1x5l4_8 _interactive_1x5l4_26"
|
||||
data-kind="critical"
|
||||
role="menuitem"
|
||||
>
|
||||
<path
|
||||
d="M14 13q.424 0 .713-.287A.97.97 0 0 0 15 12a.97.97 0 0 0-.287-.713A.97.97 0 0 0 14 11a.97.97 0 0 0-.713.287A.97.97 0 0 0 13 12q0 .424.287.713.288.287.713.287"
|
||||
/>
|
||||
<path
|
||||
d="M10.385 21.788A1 1 0 0 1 10 21V3a1.003 1.003 0 0 1 1.242-.97l8 2A1 1 0 0 1 20 5v14a1 1 0 0 1-.758.97l-8 2a1 1 0 0 1-.857-.182M18 5.781l-6-1.5v15.438l6-1.5zM9 6H7v12h2v2H7a2 2 0 0 1-2-2V6a2 2 0 0 1 2-2h2z"
|
||||
/>
|
||||
</svg>
|
||||
<span
|
||||
class="_typography_6v6n8_153 _font-body-md-medium_6v6n8_60 _label_1x5l4_43"
|
||||
<svg
|
||||
aria-hidden="true"
|
||||
class="_icon_1x5l4_34"
|
||||
fill="currentColor"
|
||||
height="24"
|
||||
viewBox="0 0 24 24"
|
||||
width="24"
|
||||
xmlns="http://www.w3.org/2000/svg"
|
||||
>
|
||||
<path
|
||||
d="M14 13q.424 0 .713-.287A.97.97 0 0 0 15 12a.97.97 0 0 0-.287-.713A.97.97 0 0 0 14 11a.97.97 0 0 0-.713.287A.97.97 0 0 0 13 12q0 .424.287.713.288.287.713.287"
|
||||
/>
|
||||
<path
|
||||
d="M10.385 21.788A1 1 0 0 1 10 21V3a1.003 1.003 0 0 1 1.242-.97l8 2A1 1 0 0 1 20 5v14a1 1 0 0 1-.758.97l-8 2a1 1 0 0 1-.857-.182M18 5.781l-6-1.5v15.438l6-1.5zM9 6H7v12h2v2H7a2 2 0 0 1-2-2V6a2 2 0 0 1 2-2h2z"
|
||||
/>
|
||||
</svg>
|
||||
<span
|
||||
class="_typography_6v6n8_153 _font-body-md-medium_6v6n8_60 _label_1x5l4_43"
|
||||
>
|
||||
Leave room
|
||||
</span>
|
||||
<svg
|
||||
aria-hidden="true"
|
||||
class="_nav-hint_1x5l4_50"
|
||||
fill="currentColor"
|
||||
height="24"
|
||||
viewBox="8 0 8 24"
|
||||
width="8"
|
||||
xmlns="http://www.w3.org/2000/svg"
|
||||
>
|
||||
<path
|
||||
d="M8.7 17.3a.95.95 0 0 1-.275-.7q0-.425.275-.7l3.9-3.9-3.9-3.9a.95.95 0 0 1-.275-.7q0-.425.275-.7a.95.95 0 0 1 .7-.275q.425 0 .7.275l4.6 4.6q.15.15.213.325.062.175.062.375t-.062.375a.9.9 0 0 1-.213.325l-4.6 4.6a.95.95 0 0 1-.7.275.95.95 0 0 1-.7-.275"
|
||||
/>
|
||||
</svg>
|
||||
</button>
|
||||
<button
|
||||
class="_item_1x5l4_8 _interactive_1x5l4_26"
|
||||
data-kind="critical"
|
||||
role="menuitem"
|
||||
>
|
||||
Leave room
|
||||
</span>
|
||||
<svg
|
||||
aria-hidden="true"
|
||||
class="_nav-hint_1x5l4_50"
|
||||
fill="currentColor"
|
||||
height="24"
|
||||
viewBox="8 0 8 24"
|
||||
width="8"
|
||||
xmlns="http://www.w3.org/2000/svg"
|
||||
>
|
||||
<path
|
||||
d="M8.7 17.3a.95.95 0 0 1-.275-.7q0-.425.275-.7l3.9-3.9-3.9-3.9a.95.95 0 0 1-.275-.7q0-.425.275-.7a.95.95 0 0 1 .7-.275q.425 0 .7.275l4.6 4.6q.15.15.213.325.062.175.062.375t-.062.375a.9.9 0 0 1-.213.325l-4.6 4.6a.95.95 0 0 1-.7.275.95.95 0 0 1-.7-.275"
|
||||
/>
|
||||
</svg>
|
||||
</button>
|
||||
<svg
|
||||
aria-hidden="true"
|
||||
class="_icon_1x5l4_34"
|
||||
fill="currentColor"
|
||||
height="24"
|
||||
viewBox="0 0 24 24"
|
||||
width="24"
|
||||
xmlns="http://www.w3.org/2000/svg"
|
||||
>
|
||||
<path
|
||||
d="M12 17q.424 0 .713-.288A.97.97 0 0 0 13 16a.97.97 0 0 0-.287-.713A.97.97 0 0 0 12 15a.97.97 0 0 0-.713.287A.97.97 0 0 0 11 16q0 .424.287.712.288.288.713.288m0-4q.424 0 .713-.287A.97.97 0 0 0 13 12V8a.97.97 0 0 0-.287-.713A.97.97 0 0 0 12 7a.97.97 0 0 0-.713.287A.97.97 0 0 0 11 8v4q0 .424.287.713.288.287.713.287m0 9a9.7 9.7 0 0 1-3.9-.788 10.1 10.1 0 0 1-3.175-2.137q-1.35-1.35-2.137-3.175A9.7 9.7 0 0 1 2 12q0-2.075.788-3.9a10.1 10.1 0 0 1 2.137-3.175q1.35-1.35 3.175-2.137A9.7 9.7 0 0 1 12 2q2.075 0 3.9.788a10.1 10.1 0 0 1 3.175 2.137q1.35 1.35 2.137 3.175A9.7 9.7 0 0 1 22 12a9.7 9.7 0 0 1-.788 3.9 10.1 10.1 0 0 1-2.137 3.175q-1.35 1.35-3.175 2.137A9.7 9.7 0 0 1 12 22"
|
||||
/>
|
||||
</svg>
|
||||
<span
|
||||
class="_typography_6v6n8_153 _font-body-md-medium_6v6n8_60 _label_1x5l4_43"
|
||||
>
|
||||
Report room
|
||||
</span>
|
||||
<svg
|
||||
aria-hidden="true"
|
||||
class="_nav-hint_1x5l4_50"
|
||||
fill="currentColor"
|
||||
height="24"
|
||||
viewBox="8 0 8 24"
|
||||
width="8"
|
||||
xmlns="http://www.w3.org/2000/svg"
|
||||
>
|
||||
<path
|
||||
d="M8.7 17.3a.95.95 0 0 1-.275-.7q0-.425.275-.7l3.9-3.9-3.9-3.9a.95.95 0 0 1-.275-.7q0-.425.275-.7a.95.95 0 0 1 .7-.275q.425 0 .7.275l4.6 4.6q.15.15.213.325.062.175.062.375t-.062.375a.9.9 0 0 1-.213.325l-4.6 4.6a.95.95 0 0 1-.7.275.95.95 0 0 1-.7-.275"
|
||||
/>
|
||||
</svg>
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
@@ -1882,46 +1964,87 @@ exports[`<RoomSummaryCard /> renders the room topic in the summary 1`] = `
|
||||
data-orientation="horizontal"
|
||||
role="separator"
|
||||
/>
|
||||
<button
|
||||
class="mx_RoomSummaryCard_leave _item_1x5l4_8 _interactive_1x5l4_26"
|
||||
data-kind="critical"
|
||||
role="menuitem"
|
||||
<div
|
||||
class="mx_RoomSummaryCard_bottomOptions"
|
||||
>
|
||||
<svg
|
||||
aria-hidden="true"
|
||||
class="_icon_1x5l4_34"
|
||||
fill="currentColor"
|
||||
height="24"
|
||||
viewBox="0 0 24 24"
|
||||
width="24"
|
||||
xmlns="http://www.w3.org/2000/svg"
|
||||
<button
|
||||
class="mx_RoomSummaryCard_leave _item_1x5l4_8 _interactive_1x5l4_26"
|
||||
data-kind="critical"
|
||||
role="menuitem"
|
||||
>
|
||||
<path
|
||||
d="M14 13q.424 0 .713-.287A.97.97 0 0 0 15 12a.97.97 0 0 0-.287-.713A.97.97 0 0 0 14 11a.97.97 0 0 0-.713.287A.97.97 0 0 0 13 12q0 .424.287.713.288.287.713.287"
|
||||
/>
|
||||
<path
|
||||
d="M10.385 21.788A1 1 0 0 1 10 21V3a1.003 1.003 0 0 1 1.242-.97l8 2A1 1 0 0 1 20 5v14a1 1 0 0 1-.758.97l-8 2a1 1 0 0 1-.857-.182M18 5.781l-6-1.5v15.438l6-1.5zM9 6H7v12h2v2H7a2 2 0 0 1-2-2V6a2 2 0 0 1 2-2h2z"
|
||||
/>
|
||||
</svg>
|
||||
<span
|
||||
class="_typography_6v6n8_153 _font-body-md-medium_6v6n8_60 _label_1x5l4_43"
|
||||
<svg
|
||||
aria-hidden="true"
|
||||
class="_icon_1x5l4_34"
|
||||
fill="currentColor"
|
||||
height="24"
|
||||
viewBox="0 0 24 24"
|
||||
width="24"
|
||||
xmlns="http://www.w3.org/2000/svg"
|
||||
>
|
||||
<path
|
||||
d="M14 13q.424 0 .713-.287A.97.97 0 0 0 15 12a.97.97 0 0 0-.287-.713A.97.97 0 0 0 14 11a.97.97 0 0 0-.713.287A.97.97 0 0 0 13 12q0 .424.287.713.288.287.713.287"
|
||||
/>
|
||||
<path
|
||||
d="M10.385 21.788A1 1 0 0 1 10 21V3a1.003 1.003 0 0 1 1.242-.97l8 2A1 1 0 0 1 20 5v14a1 1 0 0 1-.758.97l-8 2a1 1 0 0 1-.857-.182M18 5.781l-6-1.5v15.438l6-1.5zM9 6H7v12h2v2H7a2 2 0 0 1-2-2V6a2 2 0 0 1 2-2h2z"
|
||||
/>
|
||||
</svg>
|
||||
<span
|
||||
class="_typography_6v6n8_153 _font-body-md-medium_6v6n8_60 _label_1x5l4_43"
|
||||
>
|
||||
Leave room
|
||||
</span>
|
||||
<svg
|
||||
aria-hidden="true"
|
||||
class="_nav-hint_1x5l4_50"
|
||||
fill="currentColor"
|
||||
height="24"
|
||||
viewBox="8 0 8 24"
|
||||
width="8"
|
||||
xmlns="http://www.w3.org/2000/svg"
|
||||
>
|
||||
<path
|
||||
d="M8.7 17.3a.95.95 0 0 1-.275-.7q0-.425.275-.7l3.9-3.9-3.9-3.9a.95.95 0 0 1-.275-.7q0-.425.275-.7a.95.95 0 0 1 .7-.275q.425 0 .7.275l4.6 4.6q.15.15.213.325.062.175.062.375t-.062.375a.9.9 0 0 1-.213.325l-4.6 4.6a.95.95 0 0 1-.7.275.95.95 0 0 1-.7-.275"
|
||||
/>
|
||||
</svg>
|
||||
</button>
|
||||
<button
|
||||
class="_item_1x5l4_8 _interactive_1x5l4_26"
|
||||
data-kind="critical"
|
||||
role="menuitem"
|
||||
>
|
||||
Leave room
|
||||
</span>
|
||||
<svg
|
||||
aria-hidden="true"
|
||||
class="_nav-hint_1x5l4_50"
|
||||
fill="currentColor"
|
||||
height="24"
|
||||
viewBox="8 0 8 24"
|
||||
width="8"
|
||||
xmlns="http://www.w3.org/2000/svg"
|
||||
>
|
||||
<path
|
||||
d="M8.7 17.3a.95.95 0 0 1-.275-.7q0-.425.275-.7l3.9-3.9-3.9-3.9a.95.95 0 0 1-.275-.7q0-.425.275-.7a.95.95 0 0 1 .7-.275q.425 0 .7.275l4.6 4.6q.15.15.213.325.062.175.062.375t-.062.375a.9.9 0 0 1-.213.325l-4.6 4.6a.95.95 0 0 1-.7.275.95.95 0 0 1-.7-.275"
|
||||
/>
|
||||
</svg>
|
||||
</button>
|
||||
<svg
|
||||
aria-hidden="true"
|
||||
class="_icon_1x5l4_34"
|
||||
fill="currentColor"
|
||||
height="24"
|
||||
viewBox="0 0 24 24"
|
||||
width="24"
|
||||
xmlns="http://www.w3.org/2000/svg"
|
||||
>
|
||||
<path
|
||||
d="M12 17q.424 0 .713-.288A.97.97 0 0 0 13 16a.97.97 0 0 0-.287-.713A.97.97 0 0 0 12 15a.97.97 0 0 0-.713.287A.97.97 0 0 0 11 16q0 .424.287.712.288.288.713.288m0-4q.424 0 .713-.287A.97.97 0 0 0 13 12V8a.97.97 0 0 0-.287-.713A.97.97 0 0 0 12 7a.97.97 0 0 0-.713.287A.97.97 0 0 0 11 8v4q0 .424.287.713.288.287.713.287m0 9a9.7 9.7 0 0 1-3.9-.788 10.1 10.1 0 0 1-3.175-2.137q-1.35-1.35-2.137-3.175A9.7 9.7 0 0 1 2 12q0-2.075.788-3.9a10.1 10.1 0 0 1 2.137-3.175q1.35-1.35 3.175-2.137A9.7 9.7 0 0 1 12 2q2.075 0 3.9.788a10.1 10.1 0 0 1 3.175 2.137q1.35 1.35 2.137 3.175A9.7 9.7 0 0 1 22 12a9.7 9.7 0 0 1-.788 3.9 10.1 10.1 0 0 1-2.137 3.175q-1.35 1.35-3.175 2.137A9.7 9.7 0 0 1 12 22"
|
||||
/>
|
||||
</svg>
|
||||
<span
|
||||
class="_typography_6v6n8_153 _font-body-md-medium_6v6n8_60 _label_1x5l4_43"
|
||||
>
|
||||
Report room
|
||||
</span>
|
||||
<svg
|
||||
aria-hidden="true"
|
||||
class="_nav-hint_1x5l4_50"
|
||||
fill="currentColor"
|
||||
height="24"
|
||||
viewBox="8 0 8 24"
|
||||
width="8"
|
||||
xmlns="http://www.w3.org/2000/svg"
|
||||
>
|
||||
<path
|
||||
d="M8.7 17.3a.95.95 0 0 1-.275-.7q0-.425.275-.7l3.9-3.9-3.9-3.9a.95.95 0 0 1-.275-.7q0-.425.275-.7a.95.95 0 0 1 .7-.275q.425 0 .7.275l4.6 4.6q.15.15.213.325.062.175.062.375t-.062.375a.9.9 0 0 1-.213.325l-4.6 4.6a.95.95 0 0 1-.7.275.95.95 0 0 1-.7-.275"
|
||||
/>
|
||||
</svg>
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
@@ -36,6 +36,7 @@ import {
|
||||
} from "jest-matrix-react";
|
||||
import { mocked } from "jest-mock";
|
||||
import userEvent from "@testing-library/user-event";
|
||||
import { PushProcessor } from "matrix-js-sdk/src/pushprocessor";
|
||||
|
||||
import Notifications from "../../../../../src/components/views/settings/Notifications";
|
||||
import SettingsStore from "../../../../../src/settings/SettingsStore";
|
||||
@@ -301,6 +302,8 @@ describe("<Notifications />", () => {
|
||||
mockClient.getPushRules.mockClear().mockResolvedValue(pushRules);
|
||||
mockClient.addPushRule.mockClear();
|
||||
mockClient.deletePushRule.mockClear();
|
||||
// @ts-expect-error
|
||||
mockClient.pushProcessor = new PushProcessor(mockClient);
|
||||
|
||||
userEvent.setup();
|
||||
|
||||
|
||||
@@ -9,29 +9,50 @@ exports[`<FilteredDeviceListHeader /> renders correctly when all devices are sel
|
||||
<span
|
||||
tabindex="0"
|
||||
>
|
||||
<span
|
||||
class="mx_Checkbox mx_Checkbox_hasKind mx_Checkbox_kind_solid"
|
||||
<form
|
||||
class="_root_19upo_16"
|
||||
>
|
||||
<input
|
||||
aria-label="Deselect all"
|
||||
aria-labelledby=":r6:"
|
||||
checked=""
|
||||
data-testid="device-select-all-checkbox"
|
||||
id="device-select-all-checkbox"
|
||||
type="checkbox"
|
||||
/>
|
||||
<label
|
||||
for="device-select-all-checkbox"
|
||||
<div
|
||||
class="_inline-field_19upo_32"
|
||||
>
|
||||
<div
|
||||
class="mx_Checkbox_background"
|
||||
class="_inline-field-control_19upo_44"
|
||||
>
|
||||
<div
|
||||
class="mx_Checkbox_checkmark"
|
||||
/>
|
||||
class="_container_1hel1_10"
|
||||
>
|
||||
<input
|
||||
aria-label="Deselect all"
|
||||
aria-labelledby=":r9:"
|
||||
checked=""
|
||||
class="_input_1hel1_18"
|
||||
data-testid="device-select-all-checkbox"
|
||||
id="device-select-all-checkbox"
|
||||
type="checkbox"
|
||||
/>
|
||||
<div
|
||||
class="_ui_1hel1_19"
|
||||
>
|
||||
<svg
|
||||
aria-hidden="true"
|
||||
fill="currentColor"
|
||||
height="1em"
|
||||
viewBox="0 0 24 24"
|
||||
width="1em"
|
||||
xmlns="http://www.w3.org/2000/svg"
|
||||
>
|
||||
<path
|
||||
d="M9.55 17.575q-.2 0-.375-.062a.9.9 0 0 1-.325-.213L4.55 13q-.274-.274-.262-.713.012-.437.287-.712a.95.95 0 0 1 .7-.275q.425 0 .7.275L9.55 15.15l8.475-8.475q.274-.275.713-.275.437 0 .712.275.275.274.275.713 0 .437-.275.712l-9.2 9.2q-.15.15-.325.212a1.1 1.1 0 0 1-.375.063"
|
||||
/>
|
||||
</svg>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</label>
|
||||
</span>
|
||||
<div
|
||||
class="_inline-field-body_19upo_38"
|
||||
/>
|
||||
</div>
|
||||
</form>
|
||||
</span>
|
||||
<span
|
||||
class="mx_FilteredDeviceListHeader_label"
|
||||
@@ -54,28 +75,49 @@ exports[`<FilteredDeviceListHeader /> renders correctly when no devices are sele
|
||||
<span
|
||||
tabindex="0"
|
||||
>
|
||||
<span
|
||||
class="mx_Checkbox mx_Checkbox_hasKind mx_Checkbox_kind_solid"
|
||||
<form
|
||||
class="_root_19upo_16"
|
||||
>
|
||||
<input
|
||||
aria-label="Select all"
|
||||
aria-labelledby=":r0:"
|
||||
data-testid="device-select-all-checkbox"
|
||||
id="device-select-all-checkbox"
|
||||
type="checkbox"
|
||||
/>
|
||||
<label
|
||||
for="device-select-all-checkbox"
|
||||
<div
|
||||
class="_inline-field_19upo_32"
|
||||
>
|
||||
<div
|
||||
class="mx_Checkbox_background"
|
||||
class="_inline-field-control_19upo_44"
|
||||
>
|
||||
<div
|
||||
class="mx_Checkbox_checkmark"
|
||||
/>
|
||||
class="_container_1hel1_10"
|
||||
>
|
||||
<input
|
||||
aria-label="Select all"
|
||||
aria-labelledby=":r0:"
|
||||
class="_input_1hel1_18"
|
||||
data-testid="device-select-all-checkbox"
|
||||
id="device-select-all-checkbox"
|
||||
type="checkbox"
|
||||
/>
|
||||
<div
|
||||
class="_ui_1hel1_19"
|
||||
>
|
||||
<svg
|
||||
aria-hidden="true"
|
||||
fill="currentColor"
|
||||
height="1em"
|
||||
viewBox="0 0 24 24"
|
||||
width="1em"
|
||||
xmlns="http://www.w3.org/2000/svg"
|
||||
>
|
||||
<path
|
||||
d="M9.55 17.575q-.2 0-.375-.062a.9.9 0 0 1-.325-.213L4.55 13q-.274-.274-.262-.713.012-.437.287-.712a.95.95 0 0 1 .7-.275q.425 0 .7.275L9.55 15.15l8.475-8.475q.274-.275.713-.275.437 0 .712.275.275.274.275.713 0 .437-.275.712l-9.2 9.2q-.15.15-.325.212a1.1 1.1 0 0 1-.375.063"
|
||||
/>
|
||||
</svg>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</label>
|
||||
</span>
|
||||
<div
|
||||
class="_inline-field-body_19upo_38"
|
||||
/>
|
||||
</div>
|
||||
</form>
|
||||
</span>
|
||||
<span
|
||||
class="mx_FilteredDeviceListHeader_label"
|
||||
|
||||
@@ -3,6 +3,7 @@
|
||||
exports[`<SelectableDeviceTile /> renders selected tile 1`] = `
|
||||
<input
|
||||
checked=""
|
||||
class="_input_1hel1_18"
|
||||
data-testid="device-tile-checkbox-my-device"
|
||||
id="device-tile-checkbox-my-device"
|
||||
type="checkbox"
|
||||
@@ -14,88 +15,113 @@ exports[`<SelectableDeviceTile /> renders unselected device tile with checkbox 1
|
||||
<div
|
||||
class="mx_SelectableDeviceTile"
|
||||
>
|
||||
<span
|
||||
class="mx_Checkbox mx_SelectableDeviceTile_checkbox mx_Checkbox_hasKind mx_Checkbox_kind_solid"
|
||||
<form
|
||||
class="_root_19upo_16"
|
||||
>
|
||||
<input
|
||||
data-testid="device-tile-checkbox-my-device"
|
||||
id="device-tile-checkbox-my-device"
|
||||
type="checkbox"
|
||||
/>
|
||||
<label
|
||||
for="device-tile-checkbox-my-device"
|
||||
<div
|
||||
class="_inline-field_19upo_32 mx_SelectableDeviceTile_checkbox"
|
||||
>
|
||||
<div
|
||||
class="mx_Checkbox_background"
|
||||
class="_inline-field-control_19upo_44"
|
||||
>
|
||||
<div
|
||||
class="mx_Checkbox_checkmark"
|
||||
/>
|
||||
class="_container_1hel1_10"
|
||||
>
|
||||
<input
|
||||
class="_input_1hel1_18"
|
||||
data-testid="device-tile-checkbox-my-device"
|
||||
id="device-tile-checkbox-my-device"
|
||||
type="checkbox"
|
||||
/>
|
||||
<div
|
||||
class="_ui_1hel1_19"
|
||||
>
|
||||
<svg
|
||||
aria-hidden="true"
|
||||
fill="currentColor"
|
||||
height="1em"
|
||||
viewBox="0 0 24 24"
|
||||
width="1em"
|
||||
xmlns="http://www.w3.org/2000/svg"
|
||||
>
|
||||
<path
|
||||
d="M9.55 17.575q-.2 0-.375-.062a.9.9 0 0 1-.325-.213L4.55 13q-.274-.274-.262-.713.012-.437.287-.712a.95.95 0 0 1 .7-.275q.425 0 .7.275L9.55 15.15l8.475-8.475q.274-.275.713-.275.437 0 .712.275.275.274.275.713 0 .437-.275.712l-9.2 9.2q-.15.15-.325.212a1.1 1.1 0 0 1-.375.063"
|
||||
/>
|
||||
</svg>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div>
|
||||
<div
|
||||
class="mx_DeviceTile mx_DeviceTile_interactive"
|
||||
data-testid="device-tile-my-device"
|
||||
<div
|
||||
class="_inline-field-body_19upo_38"
|
||||
>
|
||||
<label
|
||||
class="_label_19upo_59"
|
||||
for="device-tile-checkbox-my-device"
|
||||
>
|
||||
<div
|
||||
class="mx_DeviceTypeIcon"
|
||||
class="mx_DeviceTile mx_DeviceTile_interactive"
|
||||
data-testid="device-tile-my-device"
|
||||
>
|
||||
<div
|
||||
class="mx_DeviceTypeIcon_deviceIconWrapper"
|
||||
class="mx_DeviceTypeIcon"
|
||||
>
|
||||
<div
|
||||
aria-label="Unknown session type"
|
||||
class="mx_DeviceTypeIcon_deviceIcon"
|
||||
class="mx_DeviceTypeIcon_deviceIconWrapper"
|
||||
>
|
||||
<div
|
||||
aria-label="Unknown session type"
|
||||
class="mx_DeviceTypeIcon_deviceIcon"
|
||||
role="img"
|
||||
/>
|
||||
</div>
|
||||
<div
|
||||
aria-label="Unverified"
|
||||
class="mx_DeviceTypeIcon_verificationIcon unverified"
|
||||
role="img"
|
||||
/>
|
||||
</div>
|
||||
<div
|
||||
aria-label="Unverified"
|
||||
class="mx_DeviceTypeIcon_verificationIcon unverified"
|
||||
role="img"
|
||||
/>
|
||||
</div>
|
||||
<div
|
||||
class="mx_DeviceTile_info"
|
||||
>
|
||||
<h4
|
||||
class="mx_Heading_h4"
|
||||
class="mx_DeviceTile_info"
|
||||
>
|
||||
My Device
|
||||
</h4>
|
||||
<h4
|
||||
class="mx_Heading_h4"
|
||||
>
|
||||
My Device
|
||||
</h4>
|
||||
<div
|
||||
class="mx_DeviceTile_metadata"
|
||||
>
|
||||
<span
|
||||
data-testid="device-metadata-isVerified"
|
||||
>
|
||||
Unverified
|
||||
</span>
|
||||
·
|
||||
<span
|
||||
data-testid="device-metadata-lastSeenIp"
|
||||
>
|
||||
123.456.789
|
||||
</span>
|
||||
·
|
||||
<span
|
||||
data-testid="device-metadata-deviceId"
|
||||
>
|
||||
my-device
|
||||
</span>
|
||||
</div>
|
||||
</div>
|
||||
<div
|
||||
class="mx_DeviceTile_metadata"
|
||||
class="mx_DeviceTile_actions"
|
||||
>
|
||||
<span
|
||||
data-testid="device-metadata-isVerified"
|
||||
>
|
||||
Unverified
|
||||
</span>
|
||||
·
|
||||
<span
|
||||
data-testid="device-metadata-lastSeenIp"
|
||||
>
|
||||
123.456.789
|
||||
</span>
|
||||
·
|
||||
<span
|
||||
data-testid="device-metadata-deviceId"
|
||||
>
|
||||
my-device
|
||||
</span>
|
||||
<div>
|
||||
test
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div
|
||||
class="mx_DeviceTile_actions"
|
||||
>
|
||||
<div>
|
||||
test
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</label>
|
||||
</div>
|
||||
</label>
|
||||
</span>
|
||||
</div>
|
||||
</form>
|
||||
</div>
|
||||
</div>
|
||||
`;
|
||||
|
||||
@@ -39,8 +39,7 @@ const labelActivityStatus = "New room activity, upgrades and status messages occ
|
||||
const labelActivityBots = "Messages sent by bots";
|
||||
const labelMentionUser = "Notify when someone mentions using @displayname or @mxid";
|
||||
const labelMentionRoom = "Notify when someone mentions using @room";
|
||||
const labelMentionKeyword =
|
||||
"Notify when someone uses a keyword" + "Enter keywords here, or use for spelling variations or nicknames";
|
||||
const labelMentionKeyword = "Notify when someone uses a keyword";
|
||||
const labelResetDefault = "Reset to default settings";
|
||||
|
||||
const keywords = ["justjann3", "justj4nn3", "justj4nne", "Janne", "J4nne", "Jann3", "jann3", "j4nne", "janne"];
|
||||
|
||||
@@ -383,28 +383,49 @@ exports[`<SessionManagerTab /> goes to filtered list from security recommendatio
|
||||
<span
|
||||
tabindex="0"
|
||||
>
|
||||
<span
|
||||
class="mx_Checkbox mx_Checkbox_hasKind mx_Checkbox_kind_solid"
|
||||
<form
|
||||
class="_root_19upo_16"
|
||||
>
|
||||
<input
|
||||
aria-label="Select all"
|
||||
aria-labelledby=":r3s:"
|
||||
data-testid="device-select-all-checkbox"
|
||||
id="device-select-all-checkbox"
|
||||
type="checkbox"
|
||||
/>
|
||||
<label
|
||||
for="device-select-all-checkbox"
|
||||
<div
|
||||
class="_inline-field_19upo_32"
|
||||
>
|
||||
<div
|
||||
class="mx_Checkbox_background"
|
||||
class="_inline-field-control_19upo_44"
|
||||
>
|
||||
<div
|
||||
class="mx_Checkbox_checkmark"
|
||||
/>
|
||||
class="_container_1hel1_10"
|
||||
>
|
||||
<input
|
||||
aria-label="Select all"
|
||||
aria-labelledby=":r4q:"
|
||||
class="_input_1hel1_18"
|
||||
data-testid="device-select-all-checkbox"
|
||||
id="device-select-all-checkbox"
|
||||
type="checkbox"
|
||||
/>
|
||||
<div
|
||||
class="_ui_1hel1_19"
|
||||
>
|
||||
<svg
|
||||
aria-hidden="true"
|
||||
fill="currentColor"
|
||||
height="1em"
|
||||
viewBox="0 0 24 24"
|
||||
width="1em"
|
||||
xmlns="http://www.w3.org/2000/svg"
|
||||
>
|
||||
<path
|
||||
d="M9.55 17.575q-.2 0-.375-.062a.9.9 0 0 1-.325-.213L4.55 13q-.274-.274-.262-.713.012-.437.287-.712a.95.95 0 0 1 .7-.275q.425 0 .7.275L9.55 15.15l8.475-8.475q.274-.275.713-.275.437 0 .712.275.275.274.275.713 0 .437-.275.712l-9.2 9.2q-.15.15-.325.212a1.1 1.1 0 0 1-.375.063"
|
||||
/>
|
||||
</svg>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</label>
|
||||
</span>
|
||||
<div
|
||||
class="_inline-field-body_19upo_38"
|
||||
/>
|
||||
</div>
|
||||
</form>
|
||||
</span>
|
||||
<span
|
||||
class="mx_FilteredDeviceListHeader_label"
|
||||
|
||||
@@ -38,30 +38,53 @@ exports[`<SidebarUserSettingsTab /> renders sidebar settings with guest spa url
|
||||
<div
|
||||
class="mx_SettingsSubsection_content"
|
||||
>
|
||||
<span
|
||||
class="mx_Checkbox mx_SidebarUserSettingsTab_checkbox mx_Checkbox_hasKind mx_Checkbox_kind_solid"
|
||||
<form
|
||||
class="_root_19upo_16"
|
||||
>
|
||||
<input
|
||||
checked=""
|
||||
disabled=""
|
||||
id="checkbox_vY7Q4uEh9K"
|
||||
type="checkbox"
|
||||
/>
|
||||
<label
|
||||
for="checkbox_vY7Q4uEh9K"
|
||||
<div
|
||||
class="_inline-field_19upo_32 mx_SidebarUserSettingsTab_checkbox"
|
||||
>
|
||||
<div
|
||||
class="mx_Checkbox_background"
|
||||
class="_inline-field-control_19upo_44"
|
||||
>
|
||||
<div
|
||||
class="mx_Checkbox_checkmark"
|
||||
/>
|
||||
class="_container_1hel1_10"
|
||||
>
|
||||
<input
|
||||
aria-describedby=":r1:"
|
||||
checked=""
|
||||
class="_input_1hel1_18"
|
||||
disabled=""
|
||||
id="checkbox_vY7Q4uEh9K"
|
||||
type="checkbox"
|
||||
/>
|
||||
<div
|
||||
class="_ui_1hel1_19"
|
||||
>
|
||||
<svg
|
||||
aria-hidden="true"
|
||||
fill="currentColor"
|
||||
height="1em"
|
||||
viewBox="0 0 24 24"
|
||||
width="1em"
|
||||
xmlns="http://www.w3.org/2000/svg"
|
||||
>
|
||||
<path
|
||||
d="M9.55 17.575q-.2 0-.375-.062a.9.9 0 0 1-.325-.213L4.55 13q-.274-.274-.262-.713.012-.437.287-.712a.95.95 0 0 1 .7-.275q.425 0 .7.275L9.55 15.15l8.475-8.475q.274-.275.713-.275.437 0 .712.275.275.274.275.713 0 .437-.275.712l-9.2 9.2q-.15.15-.325.212a1.1 1.1 0 0 1-.375.063"
|
||||
/>
|
||||
</svg>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div>
|
||||
<div
|
||||
class="mx_SettingsSubsection_text"
|
||||
<div
|
||||
class="_inline-field-body_19upo_38"
|
||||
>
|
||||
<label
|
||||
class="_label_19upo_59"
|
||||
for="checkbox_vY7Q4uEh9K"
|
||||
>
|
||||
<svg
|
||||
class="mx_SidebarUserSettingsTab_icon"
|
||||
fill="currentColor"
|
||||
height="1em"
|
||||
viewBox="0 0 24 24"
|
||||
@@ -73,69 +96,116 @@ exports[`<SidebarUserSettingsTab /> renders sidebar settings with guest spa url
|
||||
/>
|
||||
</svg>
|
||||
Home
|
||||
</div>
|
||||
<div
|
||||
class="mx_SettingsSubsection_text"
|
||||
</label>
|
||||
<span
|
||||
class="_message_19upo_85 _help-message_19upo_91"
|
||||
id=":r1:"
|
||||
>
|
||||
Home is useful for getting an overview of everything.
|
||||
</div>
|
||||
</span>
|
||||
</div>
|
||||
</label>
|
||||
</span>
|
||||
<span
|
||||
class="mx_Checkbox mx_SidebarUserSettingsTab_checkbox mx_SidebarUserSettingsTab_homeAllRoomsCheckbox mx_Checkbox_hasKind mx_Checkbox_kind_solid"
|
||||
</div>
|
||||
</form>
|
||||
<form
|
||||
class="_root_19upo_16"
|
||||
>
|
||||
<input
|
||||
data-testid="mx_SidebarUserSettingsTab_homeAllRoomsCheckbox"
|
||||
id="checkbox_38QgU2Pomx"
|
||||
type="checkbox"
|
||||
/>
|
||||
<label
|
||||
for="checkbox_38QgU2Pomx"
|
||||
<div
|
||||
class="_inline-field_19upo_32 mx_SidebarUserSettingsTab_checkbox mx_SidebarUserSettingsTab_homeAllRoomsCheckbox"
|
||||
>
|
||||
<div
|
||||
class="mx_Checkbox_background"
|
||||
class="_inline-field-control_19upo_44"
|
||||
>
|
||||
<div
|
||||
class="mx_Checkbox_checkmark"
|
||||
/>
|
||||
class="_container_1hel1_10"
|
||||
>
|
||||
<input
|
||||
aria-describedby=":r5:"
|
||||
class="_input_1hel1_18"
|
||||
data-testid="mx_SidebarUserSettingsTab_homeAllRoomsCheckbox"
|
||||
id="checkbox_38QgU2Pomx"
|
||||
type="checkbox"
|
||||
/>
|
||||
<div
|
||||
class="_ui_1hel1_19"
|
||||
>
|
||||
<svg
|
||||
aria-hidden="true"
|
||||
fill="currentColor"
|
||||
height="1em"
|
||||
viewBox="0 0 24 24"
|
||||
width="1em"
|
||||
xmlns="http://www.w3.org/2000/svg"
|
||||
>
|
||||
<path
|
||||
d="M9.55 17.575q-.2 0-.375-.062a.9.9 0 0 1-.325-.213L4.55 13q-.274-.274-.262-.713.012-.437.287-.712a.95.95 0 0 1 .7-.275q.425 0 .7.275L9.55 15.15l8.475-8.475q.274-.275.713-.275.437 0 .712.275.275.274.275.713 0 .437-.275.712l-9.2 9.2q-.15.15-.325.212a1.1 1.1 0 0 1-.375.063"
|
||||
/>
|
||||
</svg>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div>
|
||||
<div
|
||||
class="mx_SettingsSubsection_text"
|
||||
<div
|
||||
class="_inline-field-body_19upo_38"
|
||||
>
|
||||
<label
|
||||
class="_label_19upo_59"
|
||||
for="checkbox_38QgU2Pomx"
|
||||
>
|
||||
Show all rooms
|
||||
</div>
|
||||
<div
|
||||
class="mx_SettingsSubsection_text"
|
||||
</label>
|
||||
<span
|
||||
class="_message_19upo_85 _help-message_19upo_91"
|
||||
id=":r5:"
|
||||
>
|
||||
Show all your rooms in Home, even if they're in a space.
|
||||
</div>
|
||||
</span>
|
||||
</div>
|
||||
</label>
|
||||
</span>
|
||||
<span
|
||||
class="mx_Checkbox mx_SidebarUserSettingsTab_checkbox mx_Checkbox_hasKind mx_Checkbox_kind_solid"
|
||||
</div>
|
||||
</form>
|
||||
<form
|
||||
class="_root_19upo_16"
|
||||
>
|
||||
<input
|
||||
id="checkbox_wKpa6hpi3Y"
|
||||
type="checkbox"
|
||||
/>
|
||||
<label
|
||||
for="checkbox_wKpa6hpi3Y"
|
||||
<div
|
||||
class="_inline-field_19upo_32 mx_SidebarUserSettingsTab_checkbox"
|
||||
>
|
||||
<div
|
||||
class="mx_Checkbox_background"
|
||||
class="_inline-field-control_19upo_44"
|
||||
>
|
||||
<div
|
||||
class="mx_Checkbox_checkmark"
|
||||
/>
|
||||
class="_container_1hel1_10"
|
||||
>
|
||||
<input
|
||||
aria-describedby=":r9:"
|
||||
class="_input_1hel1_18"
|
||||
id="checkbox_wKpa6hpi3Y"
|
||||
type="checkbox"
|
||||
/>
|
||||
<div
|
||||
class="_ui_1hel1_19"
|
||||
>
|
||||
<svg
|
||||
aria-hidden="true"
|
||||
fill="currentColor"
|
||||
height="1em"
|
||||
viewBox="0 0 24 24"
|
||||
width="1em"
|
||||
xmlns="http://www.w3.org/2000/svg"
|
||||
>
|
||||
<path
|
||||
d="M9.55 17.575q-.2 0-.375-.062a.9.9 0 0 1-.325-.213L4.55 13q-.274-.274-.262-.713.012-.437.287-.712a.95.95 0 0 1 .7-.275q.425 0 .7.275L9.55 15.15l8.475-8.475q.274-.275.713-.275.437 0 .712.275.275.274.275.713 0 .437-.275.712l-9.2 9.2q-.15.15-.325.212a1.1 1.1 0 0 1-.375.063"
|
||||
/>
|
||||
</svg>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div>
|
||||
<div
|
||||
class="mx_SettingsSubsection_text"
|
||||
<div
|
||||
class="_inline-field-body_19upo_38"
|
||||
>
|
||||
<label
|
||||
class="_label_19upo_59"
|
||||
for="checkbox_wKpa6hpi3Y"
|
||||
>
|
||||
<svg
|
||||
class="mx_SidebarUserSettingsTab_icon"
|
||||
fill="currentColor"
|
||||
height="1em"
|
||||
viewBox="0 0 24 24"
|
||||
@@ -147,37 +217,61 @@ exports[`<SidebarUserSettingsTab /> renders sidebar settings with guest spa url
|
||||
/>
|
||||
</svg>
|
||||
Favourites
|
||||
</div>
|
||||
<div
|
||||
class="mx_SettingsSubsection_text"
|
||||
</label>
|
||||
<span
|
||||
class="_message_19upo_85 _help-message_19upo_91"
|
||||
id=":r9:"
|
||||
>
|
||||
Group all your favourite rooms and people in one place.
|
||||
</div>
|
||||
</span>
|
||||
</div>
|
||||
</label>
|
||||
</span>
|
||||
<span
|
||||
class="mx_Checkbox mx_SidebarUserSettingsTab_checkbox mx_Checkbox_hasKind mx_Checkbox_kind_solid"
|
||||
</div>
|
||||
</form>
|
||||
<form
|
||||
class="_root_19upo_16"
|
||||
>
|
||||
<input
|
||||
id="checkbox_EetmBG4yVC"
|
||||
type="checkbox"
|
||||
/>
|
||||
<label
|
||||
for="checkbox_EetmBG4yVC"
|
||||
<div
|
||||
class="_inline-field_19upo_32 mx_SidebarUserSettingsTab_checkbox"
|
||||
>
|
||||
<div
|
||||
class="mx_Checkbox_background"
|
||||
class="_inline-field-control_19upo_44"
|
||||
>
|
||||
<div
|
||||
class="mx_Checkbox_checkmark"
|
||||
/>
|
||||
class="_container_1hel1_10"
|
||||
>
|
||||
<input
|
||||
aria-describedby=":rd:"
|
||||
class="_input_1hel1_18"
|
||||
id="checkbox_EetmBG4yVC"
|
||||
type="checkbox"
|
||||
/>
|
||||
<div
|
||||
class="_ui_1hel1_19"
|
||||
>
|
||||
<svg
|
||||
aria-hidden="true"
|
||||
fill="currentColor"
|
||||
height="1em"
|
||||
viewBox="0 0 24 24"
|
||||
width="1em"
|
||||
xmlns="http://www.w3.org/2000/svg"
|
||||
>
|
||||
<path
|
||||
d="M9.55 17.575q-.2 0-.375-.062a.9.9 0 0 1-.325-.213L4.55 13q-.274-.274-.262-.713.012-.437.287-.712a.95.95 0 0 1 .7-.275q.425 0 .7.275L9.55 15.15l8.475-8.475q.274-.275.713-.275.437 0 .712.275.275.274.275.713 0 .437-.275.712l-9.2 9.2q-.15.15-.325.212a1.1 1.1 0 0 1-.375.063"
|
||||
/>
|
||||
</svg>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div>
|
||||
<div
|
||||
class="mx_SettingsSubsection_text"
|
||||
<div
|
||||
class="_inline-field-body_19upo_38"
|
||||
>
|
||||
<label
|
||||
class="_label_19upo_59"
|
||||
for="checkbox_EetmBG4yVC"
|
||||
>
|
||||
<svg
|
||||
class="mx_SidebarUserSettingsTab_icon"
|
||||
fill="currentColor"
|
||||
height="1em"
|
||||
viewBox="0 0 24 24"
|
||||
@@ -192,69 +286,115 @@ exports[`<SidebarUserSettingsTab /> renders sidebar settings with guest spa url
|
||||
/>
|
||||
</svg>
|
||||
People
|
||||
</div>
|
||||
<div
|
||||
class="mx_SettingsSubsection_text"
|
||||
</label>
|
||||
<span
|
||||
class="_message_19upo_85 _help-message_19upo_91"
|
||||
id=":rd:"
|
||||
>
|
||||
Group all your people in one place.
|
||||
</div>
|
||||
</span>
|
||||
</div>
|
||||
</label>
|
||||
</span>
|
||||
<span
|
||||
class="mx_Checkbox mx_SidebarUserSettingsTab_checkbox mx_Checkbox_hasKind mx_Checkbox_kind_solid"
|
||||
</div>
|
||||
</form>
|
||||
<form
|
||||
class="_root_19upo_16"
|
||||
>
|
||||
<input
|
||||
id="checkbox_eEefiPqpMR"
|
||||
type="checkbox"
|
||||
/>
|
||||
<label
|
||||
for="checkbox_eEefiPqpMR"
|
||||
<div
|
||||
class="_inline-field_19upo_32 mx_SidebarUserSettingsTab_checkbox"
|
||||
>
|
||||
<div
|
||||
class="mx_Checkbox_background"
|
||||
class="_inline-field-control_19upo_44"
|
||||
>
|
||||
<div
|
||||
class="mx_Checkbox_checkmark"
|
||||
/>
|
||||
</div>
|
||||
<div>
|
||||
<div
|
||||
class="mx_SettingsSubsection_text"
|
||||
class="_container_1hel1_10"
|
||||
>
|
||||
<div />
|
||||
Rooms outside of a space
|
||||
<input
|
||||
aria-describedby=":rh:"
|
||||
class="_input_1hel1_18"
|
||||
id="checkbox_eEefiPqpMR"
|
||||
type="checkbox"
|
||||
/>
|
||||
<div
|
||||
class="_ui_1hel1_19"
|
||||
>
|
||||
<svg
|
||||
aria-hidden="true"
|
||||
fill="currentColor"
|
||||
height="1em"
|
||||
viewBox="0 0 24 24"
|
||||
width="1em"
|
||||
xmlns="http://www.w3.org/2000/svg"
|
||||
>
|
||||
<path
|
||||
d="M9.55 17.575q-.2 0-.375-.062a.9.9 0 0 1-.325-.213L4.55 13q-.274-.274-.262-.713.012-.437.287-.712a.95.95 0 0 1 .7-.275q.425 0 .7.275L9.55 15.15l8.475-8.475q.274-.275.713-.275.437 0 .712.275.275.274.275.713 0 .437-.275.712l-9.2 9.2q-.15.15-.325.212a1.1 1.1 0 0 1-.375.063"
|
||||
/>
|
||||
</svg>
|
||||
</div>
|
||||
</div>
|
||||
<div
|
||||
class="mx_SettingsSubsection_text"
|
||||
</div>
|
||||
<div
|
||||
class="_inline-field-body_19upo_38"
|
||||
>
|
||||
<label
|
||||
class="_label_19upo_59"
|
||||
for="checkbox_eEefiPqpMR"
|
||||
>
|
||||
Rooms outside of a space
|
||||
</label>
|
||||
<span
|
||||
class="_message_19upo_85 _help-message_19upo_91"
|
||||
id=":rh:"
|
||||
>
|
||||
Group all your rooms that aren't part of a space in one place.
|
||||
</div>
|
||||
</span>
|
||||
</div>
|
||||
</label>
|
||||
</span>
|
||||
<span
|
||||
class="mx_Checkbox mx_SidebarUserSettingsTab_checkbox mx_Checkbox_hasKind mx_Checkbox_kind_solid"
|
||||
</div>
|
||||
</form>
|
||||
<form
|
||||
class="_root_19upo_16"
|
||||
>
|
||||
<input
|
||||
id="checkbox_MwbPDmfGtm"
|
||||
type="checkbox"
|
||||
/>
|
||||
<label
|
||||
for="checkbox_MwbPDmfGtm"
|
||||
<div
|
||||
class="_inline-field_19upo_32 mx_SidebarUserSettingsTab_checkbox"
|
||||
>
|
||||
<div
|
||||
class="mx_Checkbox_background"
|
||||
class="_inline-field-control_19upo_44"
|
||||
>
|
||||
<div
|
||||
class="mx_Checkbox_checkmark"
|
||||
/>
|
||||
class="_container_1hel1_10"
|
||||
>
|
||||
<input
|
||||
aria-describedby=":rl:"
|
||||
class="_input_1hel1_18"
|
||||
id="checkbox_MwbPDmfGtm"
|
||||
type="checkbox"
|
||||
/>
|
||||
<div
|
||||
class="_ui_1hel1_19"
|
||||
>
|
||||
<svg
|
||||
aria-hidden="true"
|
||||
fill="currentColor"
|
||||
height="1em"
|
||||
viewBox="0 0 24 24"
|
||||
width="1em"
|
||||
xmlns="http://www.w3.org/2000/svg"
|
||||
>
|
||||
<path
|
||||
d="M9.55 17.575q-.2 0-.375-.062a.9.9 0 0 1-.325-.213L4.55 13q-.274-.274-.262-.713.012-.437.287-.712a.95.95 0 0 1 .7-.275q.425 0 .7.275L9.55 15.15l8.475-8.475q.274-.275.713-.275.437 0 .712.275.275.274.275.713 0 .437-.275.712l-9.2 9.2q-.15.15-.325.212a1.1 1.1 0 0 1-.375.063"
|
||||
/>
|
||||
</svg>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div>
|
||||
<div
|
||||
class="mx_SettingsSubsection_text"
|
||||
<div
|
||||
class="_inline-field-body_19upo_38"
|
||||
>
|
||||
<label
|
||||
class="_label_19upo_59"
|
||||
for="checkbox_MwbPDmfGtm"
|
||||
>
|
||||
<svg
|
||||
class="mx_SidebarUserSettingsTab_icon"
|
||||
fill="currentColor"
|
||||
height="1em"
|
||||
viewBox="0 0 24 24"
|
||||
@@ -266,15 +406,16 @@ exports[`<SidebarUserSettingsTab /> renders sidebar settings with guest spa url
|
||||
/>
|
||||
</svg>
|
||||
Video rooms and conferences
|
||||
</div>
|
||||
<div
|
||||
class="mx_SettingsSubsection_text"
|
||||
</label>
|
||||
<span
|
||||
class="_message_19upo_85 _help-message_19upo_91"
|
||||
id=":rl:"
|
||||
>
|
||||
Group all private video rooms and conferences. In conferences you can invite people outside of matrix.
|
||||
</div>
|
||||
</span>
|
||||
</div>
|
||||
</label>
|
||||
</span>
|
||||
</div>
|
||||
</form>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
@@ -322,30 +463,53 @@ exports[`<SidebarUserSettingsTab /> renders sidebar settings without guest spa u
|
||||
<div
|
||||
class="mx_SettingsSubsection_content"
|
||||
>
|
||||
<span
|
||||
class="mx_Checkbox mx_SidebarUserSettingsTab_checkbox mx_Checkbox_hasKind mx_Checkbox_kind_solid"
|
||||
<form
|
||||
class="_root_19upo_16"
|
||||
>
|
||||
<input
|
||||
checked=""
|
||||
disabled=""
|
||||
id="checkbox_vY7Q4uEh9K"
|
||||
type="checkbox"
|
||||
/>
|
||||
<label
|
||||
for="checkbox_vY7Q4uEh9K"
|
||||
<div
|
||||
class="_inline-field_19upo_32 mx_SidebarUserSettingsTab_checkbox"
|
||||
>
|
||||
<div
|
||||
class="mx_Checkbox_background"
|
||||
class="_inline-field-control_19upo_44"
|
||||
>
|
||||
<div
|
||||
class="mx_Checkbox_checkmark"
|
||||
/>
|
||||
class="_container_1hel1_10"
|
||||
>
|
||||
<input
|
||||
aria-describedby=":rp:"
|
||||
checked=""
|
||||
class="_input_1hel1_18"
|
||||
disabled=""
|
||||
id="checkbox_vY7Q4uEh9K"
|
||||
type="checkbox"
|
||||
/>
|
||||
<div
|
||||
class="_ui_1hel1_19"
|
||||
>
|
||||
<svg
|
||||
aria-hidden="true"
|
||||
fill="currentColor"
|
||||
height="1em"
|
||||
viewBox="0 0 24 24"
|
||||
width="1em"
|
||||
xmlns="http://www.w3.org/2000/svg"
|
||||
>
|
||||
<path
|
||||
d="M9.55 17.575q-.2 0-.375-.062a.9.9 0 0 1-.325-.213L4.55 13q-.274-.274-.262-.713.012-.437.287-.712a.95.95 0 0 1 .7-.275q.425 0 .7.275L9.55 15.15l8.475-8.475q.274-.275.713-.275.437 0 .712.275.275.274.275.713 0 .437-.275.712l-9.2 9.2q-.15.15-.325.212a1.1 1.1 0 0 1-.375.063"
|
||||
/>
|
||||
</svg>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div>
|
||||
<div
|
||||
class="mx_SettingsSubsection_text"
|
||||
<div
|
||||
class="_inline-field-body_19upo_38"
|
||||
>
|
||||
<label
|
||||
class="_label_19upo_59"
|
||||
for="checkbox_vY7Q4uEh9K"
|
||||
>
|
||||
<svg
|
||||
class="mx_SidebarUserSettingsTab_icon"
|
||||
fill="currentColor"
|
||||
height="1em"
|
||||
viewBox="0 0 24 24"
|
||||
@@ -357,69 +521,116 @@ exports[`<SidebarUserSettingsTab /> renders sidebar settings without guest spa u
|
||||
/>
|
||||
</svg>
|
||||
Home
|
||||
</div>
|
||||
<div
|
||||
class="mx_SettingsSubsection_text"
|
||||
</label>
|
||||
<span
|
||||
class="_message_19upo_85 _help-message_19upo_91"
|
||||
id=":rp:"
|
||||
>
|
||||
Home is useful for getting an overview of everything.
|
||||
</div>
|
||||
</span>
|
||||
</div>
|
||||
</label>
|
||||
</span>
|
||||
<span
|
||||
class="mx_Checkbox mx_SidebarUserSettingsTab_checkbox mx_SidebarUserSettingsTab_homeAllRoomsCheckbox mx_Checkbox_hasKind mx_Checkbox_kind_solid"
|
||||
</div>
|
||||
</form>
|
||||
<form
|
||||
class="_root_19upo_16"
|
||||
>
|
||||
<input
|
||||
data-testid="mx_SidebarUserSettingsTab_homeAllRoomsCheckbox"
|
||||
id="checkbox_38QgU2Pomx"
|
||||
type="checkbox"
|
||||
/>
|
||||
<label
|
||||
for="checkbox_38QgU2Pomx"
|
||||
<div
|
||||
class="_inline-field_19upo_32 mx_SidebarUserSettingsTab_checkbox mx_SidebarUserSettingsTab_homeAllRoomsCheckbox"
|
||||
>
|
||||
<div
|
||||
class="mx_Checkbox_background"
|
||||
class="_inline-field-control_19upo_44"
|
||||
>
|
||||
<div
|
||||
class="mx_Checkbox_checkmark"
|
||||
/>
|
||||
class="_container_1hel1_10"
|
||||
>
|
||||
<input
|
||||
aria-describedby=":rt:"
|
||||
class="_input_1hel1_18"
|
||||
data-testid="mx_SidebarUserSettingsTab_homeAllRoomsCheckbox"
|
||||
id="checkbox_38QgU2Pomx"
|
||||
type="checkbox"
|
||||
/>
|
||||
<div
|
||||
class="_ui_1hel1_19"
|
||||
>
|
||||
<svg
|
||||
aria-hidden="true"
|
||||
fill="currentColor"
|
||||
height="1em"
|
||||
viewBox="0 0 24 24"
|
||||
width="1em"
|
||||
xmlns="http://www.w3.org/2000/svg"
|
||||
>
|
||||
<path
|
||||
d="M9.55 17.575q-.2 0-.375-.062a.9.9 0 0 1-.325-.213L4.55 13q-.274-.274-.262-.713.012-.437.287-.712a.95.95 0 0 1 .7-.275q.425 0 .7.275L9.55 15.15l8.475-8.475q.274-.275.713-.275.437 0 .712.275.275.274.275.713 0 .437-.275.712l-9.2 9.2q-.15.15-.325.212a1.1 1.1 0 0 1-.375.063"
|
||||
/>
|
||||
</svg>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div>
|
||||
<div
|
||||
class="mx_SettingsSubsection_text"
|
||||
<div
|
||||
class="_inline-field-body_19upo_38"
|
||||
>
|
||||
<label
|
||||
class="_label_19upo_59"
|
||||
for="checkbox_38QgU2Pomx"
|
||||
>
|
||||
Show all rooms
|
||||
</div>
|
||||
<div
|
||||
class="mx_SettingsSubsection_text"
|
||||
</label>
|
||||
<span
|
||||
class="_message_19upo_85 _help-message_19upo_91"
|
||||
id=":rt:"
|
||||
>
|
||||
Show all your rooms in Home, even if they're in a space.
|
||||
</div>
|
||||
</span>
|
||||
</div>
|
||||
</label>
|
||||
</span>
|
||||
<span
|
||||
class="mx_Checkbox mx_SidebarUserSettingsTab_checkbox mx_Checkbox_hasKind mx_Checkbox_kind_solid"
|
||||
</div>
|
||||
</form>
|
||||
<form
|
||||
class="_root_19upo_16"
|
||||
>
|
||||
<input
|
||||
id="checkbox_wKpa6hpi3Y"
|
||||
type="checkbox"
|
||||
/>
|
||||
<label
|
||||
for="checkbox_wKpa6hpi3Y"
|
||||
<div
|
||||
class="_inline-field_19upo_32 mx_SidebarUserSettingsTab_checkbox"
|
||||
>
|
||||
<div
|
||||
class="mx_Checkbox_background"
|
||||
class="_inline-field-control_19upo_44"
|
||||
>
|
||||
<div
|
||||
class="mx_Checkbox_checkmark"
|
||||
/>
|
||||
class="_container_1hel1_10"
|
||||
>
|
||||
<input
|
||||
aria-describedby=":r11:"
|
||||
class="_input_1hel1_18"
|
||||
id="checkbox_wKpa6hpi3Y"
|
||||
type="checkbox"
|
||||
/>
|
||||
<div
|
||||
class="_ui_1hel1_19"
|
||||
>
|
||||
<svg
|
||||
aria-hidden="true"
|
||||
fill="currentColor"
|
||||
height="1em"
|
||||
viewBox="0 0 24 24"
|
||||
width="1em"
|
||||
xmlns="http://www.w3.org/2000/svg"
|
||||
>
|
||||
<path
|
||||
d="M9.55 17.575q-.2 0-.375-.062a.9.9 0 0 1-.325-.213L4.55 13q-.274-.274-.262-.713.012-.437.287-.712a.95.95 0 0 1 .7-.275q.425 0 .7.275L9.55 15.15l8.475-8.475q.274-.275.713-.275.437 0 .712.275.275.274.275.713 0 .437-.275.712l-9.2 9.2q-.15.15-.325.212a1.1 1.1 0 0 1-.375.063"
|
||||
/>
|
||||
</svg>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div>
|
||||
<div
|
||||
class="mx_SettingsSubsection_text"
|
||||
<div
|
||||
class="_inline-field-body_19upo_38"
|
||||
>
|
||||
<label
|
||||
class="_label_19upo_59"
|
||||
for="checkbox_wKpa6hpi3Y"
|
||||
>
|
||||
<svg
|
||||
class="mx_SidebarUserSettingsTab_icon"
|
||||
fill="currentColor"
|
||||
height="1em"
|
||||
viewBox="0 0 24 24"
|
||||
@@ -431,37 +642,61 @@ exports[`<SidebarUserSettingsTab /> renders sidebar settings without guest spa u
|
||||
/>
|
||||
</svg>
|
||||
Favourites
|
||||
</div>
|
||||
<div
|
||||
class="mx_SettingsSubsection_text"
|
||||
</label>
|
||||
<span
|
||||
class="_message_19upo_85 _help-message_19upo_91"
|
||||
id=":r11:"
|
||||
>
|
||||
Group all your favourite rooms and people in one place.
|
||||
</div>
|
||||
</span>
|
||||
</div>
|
||||
</label>
|
||||
</span>
|
||||
<span
|
||||
class="mx_Checkbox mx_SidebarUserSettingsTab_checkbox mx_Checkbox_hasKind mx_Checkbox_kind_solid"
|
||||
</div>
|
||||
</form>
|
||||
<form
|
||||
class="_root_19upo_16"
|
||||
>
|
||||
<input
|
||||
id="checkbox_EetmBG4yVC"
|
||||
type="checkbox"
|
||||
/>
|
||||
<label
|
||||
for="checkbox_EetmBG4yVC"
|
||||
<div
|
||||
class="_inline-field_19upo_32 mx_SidebarUserSettingsTab_checkbox"
|
||||
>
|
||||
<div
|
||||
class="mx_Checkbox_background"
|
||||
class="_inline-field-control_19upo_44"
|
||||
>
|
||||
<div
|
||||
class="mx_Checkbox_checkmark"
|
||||
/>
|
||||
class="_container_1hel1_10"
|
||||
>
|
||||
<input
|
||||
aria-describedby=":r15:"
|
||||
class="_input_1hel1_18"
|
||||
id="checkbox_EetmBG4yVC"
|
||||
type="checkbox"
|
||||
/>
|
||||
<div
|
||||
class="_ui_1hel1_19"
|
||||
>
|
||||
<svg
|
||||
aria-hidden="true"
|
||||
fill="currentColor"
|
||||
height="1em"
|
||||
viewBox="0 0 24 24"
|
||||
width="1em"
|
||||
xmlns="http://www.w3.org/2000/svg"
|
||||
>
|
||||
<path
|
||||
d="M9.55 17.575q-.2 0-.375-.062a.9.9 0 0 1-.325-.213L4.55 13q-.274-.274-.262-.713.012-.437.287-.712a.95.95 0 0 1 .7-.275q.425 0 .7.275L9.55 15.15l8.475-8.475q.274-.275.713-.275.437 0 .712.275.275.274.275.713 0 .437-.275.712l-9.2 9.2q-.15.15-.325.212a1.1 1.1 0 0 1-.375.063"
|
||||
/>
|
||||
</svg>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div>
|
||||
<div
|
||||
class="mx_SettingsSubsection_text"
|
||||
<div
|
||||
class="_inline-field-body_19upo_38"
|
||||
>
|
||||
<label
|
||||
class="_label_19upo_59"
|
||||
for="checkbox_EetmBG4yVC"
|
||||
>
|
||||
<svg
|
||||
class="mx_SidebarUserSettingsTab_icon"
|
||||
fill="currentColor"
|
||||
height="1em"
|
||||
viewBox="0 0 24 24"
|
||||
@@ -476,69 +711,115 @@ exports[`<SidebarUserSettingsTab /> renders sidebar settings without guest spa u
|
||||
/>
|
||||
</svg>
|
||||
People
|
||||
</div>
|
||||
<div
|
||||
class="mx_SettingsSubsection_text"
|
||||
</label>
|
||||
<span
|
||||
class="_message_19upo_85 _help-message_19upo_91"
|
||||
id=":r15:"
|
||||
>
|
||||
Group all your people in one place.
|
||||
</div>
|
||||
</span>
|
||||
</div>
|
||||
</label>
|
||||
</span>
|
||||
<span
|
||||
class="mx_Checkbox mx_SidebarUserSettingsTab_checkbox mx_Checkbox_hasKind mx_Checkbox_kind_solid"
|
||||
</div>
|
||||
</form>
|
||||
<form
|
||||
class="_root_19upo_16"
|
||||
>
|
||||
<input
|
||||
id="checkbox_eEefiPqpMR"
|
||||
type="checkbox"
|
||||
/>
|
||||
<label
|
||||
for="checkbox_eEefiPqpMR"
|
||||
<div
|
||||
class="_inline-field_19upo_32 mx_SidebarUserSettingsTab_checkbox"
|
||||
>
|
||||
<div
|
||||
class="mx_Checkbox_background"
|
||||
class="_inline-field-control_19upo_44"
|
||||
>
|
||||
<div
|
||||
class="mx_Checkbox_checkmark"
|
||||
/>
|
||||
</div>
|
||||
<div>
|
||||
<div
|
||||
class="mx_SettingsSubsection_text"
|
||||
class="_container_1hel1_10"
|
||||
>
|
||||
<div />
|
||||
Rooms outside of a space
|
||||
<input
|
||||
aria-describedby=":r19:"
|
||||
class="_input_1hel1_18"
|
||||
id="checkbox_eEefiPqpMR"
|
||||
type="checkbox"
|
||||
/>
|
||||
<div
|
||||
class="_ui_1hel1_19"
|
||||
>
|
||||
<svg
|
||||
aria-hidden="true"
|
||||
fill="currentColor"
|
||||
height="1em"
|
||||
viewBox="0 0 24 24"
|
||||
width="1em"
|
||||
xmlns="http://www.w3.org/2000/svg"
|
||||
>
|
||||
<path
|
||||
d="M9.55 17.575q-.2 0-.375-.062a.9.9 0 0 1-.325-.213L4.55 13q-.274-.274-.262-.713.012-.437.287-.712a.95.95 0 0 1 .7-.275q.425 0 .7.275L9.55 15.15l8.475-8.475q.274-.275.713-.275.437 0 .712.275.275.274.275.713 0 .437-.275.712l-9.2 9.2q-.15.15-.325.212a1.1 1.1 0 0 1-.375.063"
|
||||
/>
|
||||
</svg>
|
||||
</div>
|
||||
</div>
|
||||
<div
|
||||
class="mx_SettingsSubsection_text"
|
||||
</div>
|
||||
<div
|
||||
class="_inline-field-body_19upo_38"
|
||||
>
|
||||
<label
|
||||
class="_label_19upo_59"
|
||||
for="checkbox_eEefiPqpMR"
|
||||
>
|
||||
Rooms outside of a space
|
||||
</label>
|
||||
<span
|
||||
class="_message_19upo_85 _help-message_19upo_91"
|
||||
id=":r19:"
|
||||
>
|
||||
Group all your rooms that aren't part of a space in one place.
|
||||
</div>
|
||||
</span>
|
||||
</div>
|
||||
</label>
|
||||
</span>
|
||||
<span
|
||||
class="mx_Checkbox mx_SidebarUserSettingsTab_checkbox mx_Checkbox_hasKind mx_Checkbox_kind_solid"
|
||||
</div>
|
||||
</form>
|
||||
<form
|
||||
class="_root_19upo_16"
|
||||
>
|
||||
<input
|
||||
id="checkbox_MwbPDmfGtm"
|
||||
type="checkbox"
|
||||
/>
|
||||
<label
|
||||
for="checkbox_MwbPDmfGtm"
|
||||
<div
|
||||
class="_inline-field_19upo_32 mx_SidebarUserSettingsTab_checkbox"
|
||||
>
|
||||
<div
|
||||
class="mx_Checkbox_background"
|
||||
class="_inline-field-control_19upo_44"
|
||||
>
|
||||
<div
|
||||
class="mx_Checkbox_checkmark"
|
||||
/>
|
||||
class="_container_1hel1_10"
|
||||
>
|
||||
<input
|
||||
aria-describedby=":r1d:"
|
||||
class="_input_1hel1_18"
|
||||
id="checkbox_MwbPDmfGtm"
|
||||
type="checkbox"
|
||||
/>
|
||||
<div
|
||||
class="_ui_1hel1_19"
|
||||
>
|
||||
<svg
|
||||
aria-hidden="true"
|
||||
fill="currentColor"
|
||||
height="1em"
|
||||
viewBox="0 0 24 24"
|
||||
width="1em"
|
||||
xmlns="http://www.w3.org/2000/svg"
|
||||
>
|
||||
<path
|
||||
d="M9.55 17.575q-.2 0-.375-.062a.9.9 0 0 1-.325-.213L4.55 13q-.274-.274-.262-.713.012-.437.287-.712a.95.95 0 0 1 .7-.275q.425 0 .7.275L9.55 15.15l8.475-8.475q.274-.275.713-.275.437 0 .712.275.275.274.275.713 0 .437-.275.712l-9.2 9.2q-.15.15-.325.212a1.1 1.1 0 0 1-.375.063"
|
||||
/>
|
||||
</svg>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div>
|
||||
<div
|
||||
class="mx_SettingsSubsection_text"
|
||||
<div
|
||||
class="_inline-field-body_19upo_38"
|
||||
>
|
||||
<label
|
||||
class="_label_19upo_59"
|
||||
for="checkbox_MwbPDmfGtm"
|
||||
>
|
||||
<svg
|
||||
class="mx_SidebarUserSettingsTab_icon"
|
||||
fill="currentColor"
|
||||
height="1em"
|
||||
viewBox="0 0 24 24"
|
||||
@@ -550,15 +831,16 @@ exports[`<SidebarUserSettingsTab /> renders sidebar settings without guest spa u
|
||||
/>
|
||||
</svg>
|
||||
Video rooms and conferences
|
||||
</div>
|
||||
<div
|
||||
class="mx_SettingsSubsection_text"
|
||||
</label>
|
||||
<span
|
||||
class="_message_19upo_85 _help-message_19upo_91"
|
||||
id=":r1d:"
|
||||
>
|
||||
Group all private video rooms and conferences.
|
||||
</div>
|
||||
</span>
|
||||
</div>
|
||||
</label>
|
||||
</span>
|
||||
</div>
|
||||
</form>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
@@ -457,7 +457,7 @@ describe("RoomListStoreV3", () => {
|
||||
// Let's say 8, 27 are unread
|
||||
jest.spyOn(RoomNotificationStateStore.instance, "getRoomState").mockImplementation((room) => {
|
||||
const state = {
|
||||
isUnread: [rooms[8], rooms[27]].includes(room),
|
||||
hasUnreadCount: [rooms[8], rooms[27]].includes(room),
|
||||
} as unknown as RoomNotificationState;
|
||||
return state;
|
||||
});
|
||||
@@ -588,7 +588,7 @@ describe("RoomListStoreV3", () => {
|
||||
// Let's say 8, 27 are unread
|
||||
jest.spyOn(RoomNotificationStateStore.instance, "getRoomState").mockImplementation((room) => {
|
||||
const state = {
|
||||
isUnread: [rooms[8], rooms[27]].includes(room),
|
||||
hasUnreadCount: [rooms[8], rooms[27]].includes(room),
|
||||
} as unknown as RoomNotificationState;
|
||||
return state;
|
||||
});
|
||||
|
||||
@@ -10,6 +10,7 @@ import React from "react";
|
||||
import { act, render } from "jest-matrix-react";
|
||||
import { MatrixEvent, ConditionKind, EventType, PushRuleActionName, Room, TweakName } from "matrix-js-sdk/src/matrix";
|
||||
import { mocked } from "jest-mock";
|
||||
import { PushProcessor } from "matrix-js-sdk/src/pushprocessor";
|
||||
|
||||
import { pillifyLinks } from "../../../src/utils/pillify";
|
||||
import { stubClient } from "../../test-utils";
|
||||
@@ -78,6 +79,8 @@ describe("pillify", () => {
|
||||
},
|
||||
],
|
||||
};
|
||||
// @ts-expect-error
|
||||
cli.pushProcessor = new PushProcessor(cli);
|
||||
|
||||
DMRoomMap.makeShared(cli);
|
||||
});
|
||||
|
||||
@@ -176,14 +176,14 @@ module.exports = (env, argv) => {
|
||||
minimize: enableMinification,
|
||||
minimizer: enableMinification
|
||||
? [
|
||||
new TerserPlugin({
|
||||
// Already minified and includes an auto-generated license comment
|
||||
// that the plugin would otherwise pointlessly extract into a separate
|
||||
// file. We add the actual license using CopyWebpackPlugin below.
|
||||
exclude: "jitsi_external_api.min.js",
|
||||
}),
|
||||
new CssMinimizerPlugin(),
|
||||
]
|
||||
new TerserPlugin({
|
||||
// Already minified and includes an auto-generated license comment
|
||||
// that the plugin would otherwise pointlessly extract into a separate
|
||||
// file. We add the actual license using CopyWebpackPlugin below.
|
||||
exclude: "jitsi_external_api.min.js",
|
||||
}),
|
||||
new CssMinimizerPlugin(),
|
||||
]
|
||||
: [],
|
||||
|
||||
// Set the value of `process.env.NODE_ENV` for libraries like React
|
||||
@@ -625,6 +625,12 @@ module.exports = (env, argv) => {
|
||||
minify: false,
|
||||
chunks: [],
|
||||
}),
|
||||
new HtmlWebpackPlugin({
|
||||
template: "./src/vector/static/join_room_redirect.html",
|
||||
filename: "static/join_room_redirect.html",
|
||||
minify: false,
|
||||
chunks: [],
|
||||
}),
|
||||
new HtmlWebpackPlugin({
|
||||
template: "./src/vector/static/incompatible-browser.html",
|
||||
filename: "static/incompatible-browser.html",
|
||||
@@ -648,16 +654,16 @@ module.exports = (env, argv) => {
|
||||
// This plugin throws an error on import on some platforms like ppc64le & s390x even if the plugin isn't called,
|
||||
// so we require it conditionally.
|
||||
process.env.SENTRY_DSN &&
|
||||
require("@sentry/webpack-plugin").sentryWebpackPlugin({
|
||||
release: process.env.VERSION,
|
||||
sourcemaps: {
|
||||
paths: "./webapp/bundles/**",
|
||||
},
|
||||
errorHandler: (err) => {
|
||||
console.warn("Sentry CLI Plugin: " + err.message);
|
||||
console.log(`::warning title=Sentry error::${err.message}`);
|
||||
},
|
||||
}),
|
||||
require("@sentry/webpack-plugin").sentryWebpackPlugin({
|
||||
release: process.env.VERSION,
|
||||
sourcemaps: {
|
||||
paths: "./webapp/bundles/**",
|
||||
},
|
||||
errorHandler: (err) => {
|
||||
console.warn("Sentry CLI Plugin: " + err.message);
|
||||
console.log(`::warning title=Sentry error::${err.message}`);
|
||||
},
|
||||
}),
|
||||
|
||||
new CopyWebpackPlugin({
|
||||
patterns: [
|
||||
|
||||