mirror of
https://github.com/element-hq/element-web.git
synced 2025-12-05 01:10:40 +00:00
Compare commits
10 Commits
rav/lifecy
...
robin/depr
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
9565d2685e | ||
|
|
7e40e3697f | ||
|
|
beaabd5b44 | ||
|
|
a23a2c03d3 | ||
|
|
c2c040dd42 | ||
|
|
c98358cb26 | ||
|
|
d384a9b71b | ||
|
|
fc04ad26ce | ||
|
|
b5160c47b3 | ||
|
|
3af8273d6b |
@@ -5,7 +5,7 @@ import "../res/css/shared.pcss";
|
||||
import "./preview.css";
|
||||
import React, { useLayoutEffect } from "react";
|
||||
import { FORCE_RE_RENDER } from "storybook/internal/core-events";
|
||||
import { setLanguage } from "../src/shared-components/i18n";
|
||||
import { setLanguage } from "../src/shared-components/utils/i18n";
|
||||
import { TooltipProvider } from "@vector-im/compound-web";
|
||||
|
||||
export const globalTypes = {
|
||||
|
||||
@@ -99,7 +99,7 @@
|
||||
"@types/react-virtualized": "^9.21.30",
|
||||
"@vector-im/compound-design-tokens": "^5.0.0",
|
||||
"@vector-im/compound-web": "^8.1.2",
|
||||
"@vector-im/matrix-wysiwyg": "2.38.4",
|
||||
"@vector-im/matrix-wysiwyg": "2.39.0",
|
||||
"@zxcvbn-ts/core": "^3.0.4",
|
||||
"@zxcvbn-ts/language-common": "^3.0.4",
|
||||
"@zxcvbn-ts/language-en": "^3.0.2",
|
||||
|
||||
@@ -158,6 +158,8 @@ test.describe("Cryptography", function () {
|
||||
await page.getByRole("textbox", { name: "Send a message…" }).press("Enter");
|
||||
await checkDMRoom(page);
|
||||
const bobRoomId = await bobJoin(page, bob);
|
||||
await expect(page.locator(".mx_MessageComposer_e2eIcon")).toMatchScreenshot("composer-e2e-icon-normal.png");
|
||||
|
||||
await testMessages(page, bob, bobRoomId);
|
||||
await verify(app, bob);
|
||||
|
||||
|
||||
Binary file not shown.
|
After Width: | Height: | Size: 268 B |
@@ -52,7 +52,13 @@ Please see LICENSE files in the repository root for full details.
|
||||
|
||||
.mx_E2EIcon_normal::after {
|
||||
mask-image: url("$(res)/img/e2e/normal.svg");
|
||||
background-color: white;
|
||||
background-color: var(--cpd-color-icon-tertiary);
|
||||
}
|
||||
|
||||
.mx_E2EIcon_verified {
|
||||
.mx_E2EIcon_normal::after {
|
||||
background-color: white;
|
||||
}
|
||||
}
|
||||
|
||||
.mx_E2EIcon_verified::after {
|
||||
|
||||
@@ -621,6 +621,9 @@ export async function restoreSessionFromStorage(opts?: { ignoreGuest?: boolean }
|
||||
await getStoredSessionVars();
|
||||
|
||||
if (hasAccessToken && !accessToken) {
|
||||
logger.warn(
|
||||
"restoreSessionFromStorage: storage indicates we should have an access token, but we do not. Displaying StorageEvictedDialog",
|
||||
);
|
||||
await abortLogin();
|
||||
}
|
||||
|
||||
@@ -823,6 +826,7 @@ async function doSetLoggedIn(
|
||||
// crypto store, we'll be generally confused when handling encrypted data.
|
||||
// Show a modal recommending a full reset of storage.
|
||||
if (results.dataInLocalStorage && results.cryptoInited && !results.dataInCryptoStore) {
|
||||
logger.warn("doSetLoggedIn: StorageManager consistency check failed; displaying StorageEvictedDialog.");
|
||||
await abortLogin();
|
||||
}
|
||||
|
||||
|
||||
@@ -9,7 +9,7 @@ Please see LICENSE files in the repository root for full details.
|
||||
*/
|
||||
|
||||
// Import i18n.tsx instead of languageHandler to avoid circular deps
|
||||
import { _td, type TranslationKey } from "../shared-components/i18n";
|
||||
import { _td, type TranslationKey } from "../shared-components/utils/i18n";
|
||||
import { IS_MAC, IS_ELECTRON, Key } from "../Keyboard";
|
||||
import { type IBaseSetting } from "../settings/Settings";
|
||||
import { type KeyCombo } from "../KeyBindingsManager";
|
||||
|
||||
@@ -120,8 +120,8 @@ export const PollHistoryList: React.FC<PollHistoryListProps> = ({
|
||||
value={filter}
|
||||
onFilterChange={onFilterChange}
|
||||
tabs={[
|
||||
{ id: "ACTIVE", label: "Active polls" },
|
||||
{ id: "ENDED", label: "Past polls" },
|
||||
{ id: "ACTIVE", label: _t("right_panel|poll|active_heading") },
|
||||
{ id: "ENDED", label: _t("right_panel|poll|past_heading") },
|
||||
]}
|
||||
/>
|
||||
{!!pollStartEvents.length && (
|
||||
|
||||
@@ -757,6 +757,10 @@ export class UnwrappedEventTile extends React.Component<EventTileProps, IState>
|
||||
shieldReasonMessage = _t("timeline|decryption_failure|sender_identity_previously_verified");
|
||||
break;
|
||||
|
||||
case EventShieldReason.MISMATCHED_SENDER:
|
||||
shieldReasonMessage = _t("encryption|event_shield_reason_mismatched_sender");
|
||||
break;
|
||||
|
||||
default:
|
||||
shieldReasonMessage = _t("error|unknown");
|
||||
break;
|
||||
|
||||
@@ -934,6 +934,7 @@
|
||||
"cross_signing_user_warning": "This user has not verified all of their sessions.",
|
||||
"enter_recovery_key": "Enter recovery key",
|
||||
"event_shield_reason_authenticity_not_guaranteed": "The authenticity of this encrypted message can't be guaranteed on this device.",
|
||||
"event_shield_reason_mismatched_sender": "The sender of the event does not match the owner of the device that sent it.",
|
||||
"event_shield_reason_mismatched_sender_key": "Encrypted by an unverified session",
|
||||
"event_shield_reason_unknown_device": "Encrypted by an unknown or deleted device.",
|
||||
"event_shield_reason_unsigned_device": "Encrypted by a device not verified by its owner.",
|
||||
|
||||
@@ -25,7 +25,7 @@ import {
|
||||
type IVariables,
|
||||
KEY_SEPARATOR,
|
||||
getLangsJson,
|
||||
} from "./shared-components/i18n";
|
||||
} from "./shared-components/utils/i18n";
|
||||
|
||||
export {
|
||||
_t,
|
||||
@@ -40,7 +40,7 @@ export {
|
||||
normalizeLanguageKey,
|
||||
getNormalizedLanguageKeys,
|
||||
substitute,
|
||||
} from "./shared-components/i18n";
|
||||
} from "./shared-components/utils/i18n";
|
||||
|
||||
const i18nFolder = "i18n/";
|
||||
|
||||
|
||||
@@ -669,12 +669,12 @@ export class ElementCall extends Call {
|
||||
|
||||
// Splice together the Element Call URL for this call
|
||||
const params = new URLSearchParams({
|
||||
embed: "true", // We're embedding EC within another application
|
||||
confineToRoom: "true", // Only show the call interface for the configured room
|
||||
// Template variables are used, so that this can be configured using the widget data.
|
||||
skipLobby: "$skipLobby", // Skip the lobby in case we show a lobby component of our own.
|
||||
returnToLobby: "$returnToLobby", // Returns to the lobby (instead of blank screen) when the call ends. (For video rooms)
|
||||
perParticipantE2EE: "$perParticipantE2EE",
|
||||
hideHeader: "true", // Hide the header since our room header is enough
|
||||
header: "none", // Hide the header since our room header is enough
|
||||
userId: client.getUserId()!,
|
||||
deviceId: client.getDeviceId()!,
|
||||
roomId: roomId,
|
||||
|
||||
@@ -12,7 +12,7 @@ import { UNSTABLE_MSC4133_EXTENDED_PROFILES } from "matrix-js-sdk/src/matrix";
|
||||
|
||||
import { type MediaPreviewConfig } from "../@types/media_preview.ts";
|
||||
// Import i18n.tsx instead of languageHandler to avoid circular deps
|
||||
import { _t, _td, type TranslationKey } from "../shared-components/i18n";
|
||||
import { _t, _td, type TranslationKey } from "../shared-components/utils/i18n";
|
||||
import DeviceIsolationModeController from "./controllers/DeviceIsolationModeController.ts";
|
||||
import {
|
||||
NotificationBodyEnabledController,
|
||||
|
||||
@@ -25,7 +25,7 @@ import React from "react";
|
||||
import { type TranslationKey as _TranslationKey, KEY_SEPARATOR } from "matrix-web-i18n";
|
||||
import counterpart from "counterpart";
|
||||
|
||||
import type Translations from "../i18n/strings/en_EN.json";
|
||||
import type Translations from "../../i18n/strings/en_EN.json";
|
||||
|
||||
// @ts-ignore - $webapp is a webpack resolve alias pointing to the output directory, see webpack config
|
||||
import webpackLangJsonUrl from "$webapp/i18n/languages.json";
|
||||
@@ -168,6 +168,7 @@ export class StopGapWidgetDriver extends WidgetDriver {
|
||||
WidgetEventCapability.forStateEvent(EventDirection.Receive, EventType.RoomCreate).raw,
|
||||
);
|
||||
|
||||
const sendRoomEvents = [EventType.CallNotify, EventType.RTCNotification];
|
||||
const sendRecvRoomEvents = [
|
||||
"io.element.call.encryption_keys",
|
||||
"org.matrix.rageshake_request",
|
||||
@@ -175,10 +176,10 @@ export class StopGapWidgetDriver extends WidgetDriver {
|
||||
EventType.RoomRedaction,
|
||||
"io.element.call.reaction",
|
||||
];
|
||||
for (const eventType of sendRecvRoomEvents) {
|
||||
for (const eventType of [...sendRoomEvents, ...sendRecvRoomEvents])
|
||||
this.allowedCapabilities.add(WidgetEventCapability.forRoomEvent(EventDirection.Send, eventType).raw);
|
||||
for (const eventType of sendRecvRoomEvents)
|
||||
this.allowedCapabilities.add(WidgetEventCapability.forRoomEvent(EventDirection.Receive, eventType).raw);
|
||||
}
|
||||
|
||||
const sendRecvToDevice = [
|
||||
EventType.CallInvite,
|
||||
|
||||
@@ -6,12 +6,14 @@ 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 { env } from "process";
|
||||
import "@testing-library/jest-dom";
|
||||
import "blob-polyfill";
|
||||
import { secureRandomString } from "matrix-js-sdk/src/randomstring";
|
||||
import { mocked } from "jest-mock";
|
||||
|
||||
import { PredictableRandom } from "./test-utils/predictableRandom"; // https://github.com/jsdom/jsdom/issues/2555
|
||||
import { PredictableRandom } from "./test-utils/predictableRandom";
|
||||
import * as rageshake from "../src/rageshake/rageshake";
|
||||
|
||||
declare global {
|
||||
// eslint-disable-next-line no-var
|
||||
@@ -37,6 +39,23 @@ beforeEach(() => {
|
||||
});
|
||||
});
|
||||
|
||||
// Somewhat hacky workaround for https://github.com/jestjs/jest/issues/15747: if the GHA reporter is enabled,
|
||||
// capture logs using the rageshake infrastructure, then dump them out after the test.
|
||||
if (env["GITHUB_ACTIONS"] !== undefined) {
|
||||
beforeEach(async () => {
|
||||
await rageshake.init(/* setUpPersistence = */ false);
|
||||
});
|
||||
|
||||
afterEach(async () => {
|
||||
const logs = global.mx_rage_logger.flush(/* keeplogs = */ false);
|
||||
if (logs) {
|
||||
process.stderr.write(`::group::Console logs from test '${expect.getState().currentTestName}'\n\n`);
|
||||
process.stderr.write(logs);
|
||||
process.stderr.write("::endgroup::\n");
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
// Very carefully enable the mocks for everything else in
|
||||
// a specific order. We use this order to ensure we properly
|
||||
// establish an application state that actually works.
|
||||
|
||||
@@ -71,6 +71,8 @@ import { SetupEncryptionStore } from "../../../../src/stores/SetupEncryptionStor
|
||||
import { ShareFormat } from "../../../../src/dispatcher/payloads/SharePayload.ts";
|
||||
import { clearStorage } from "../../../../src/Lifecycle";
|
||||
import RoomListStore from "../../../../src/stores/room-list/RoomListStore.ts";
|
||||
import UserSettingsDialog from "../../../../src/components/views/dialogs/UserSettingsDialog.tsx";
|
||||
import { SdkContextClass } from "../../../../src/contexts/SDKContext.ts";
|
||||
|
||||
jest.mock("matrix-js-sdk/src/oidc/authorize", () => ({
|
||||
completeAuthorizationCodeGrant: jest.fn(),
|
||||
@@ -268,6 +270,10 @@ describe("<MatrixChat />", () => {
|
||||
// (must be sync otherwise the next test will start before it happens)
|
||||
act(() => defaultDispatcher.dispatch({ action: Action.OnLoggedOut }, true));
|
||||
|
||||
// that will cause the Login to kick off an update in the background, which we need to allow to finish within
|
||||
// an `act` to avoid warnings
|
||||
await flushPromises();
|
||||
|
||||
localStorage.clear();
|
||||
});
|
||||
|
||||
@@ -640,22 +646,29 @@ describe("<MatrixChat />", () => {
|
||||
});
|
||||
|
||||
describe("onAction()", () => {
|
||||
beforeEach(() => {
|
||||
jest.spyOn(defaultDispatcher, "dispatch").mockClear();
|
||||
jest.spyOn(defaultDispatcher, "fire").mockClear();
|
||||
afterEach(() => {
|
||||
jest.restoreAllMocks();
|
||||
});
|
||||
it("should open user device settings", async () => {
|
||||
|
||||
it("ViewUserDeviceSettings should open user device settings", async () => {
|
||||
await getComponentAndWaitForReady();
|
||||
|
||||
defaultDispatcher.dispatch({
|
||||
action: Action.ViewUserDeviceSettings,
|
||||
});
|
||||
const createDialog = jest.spyOn(Modal, "createDialog").mockReturnValue({} as any);
|
||||
|
||||
await flushPromises();
|
||||
await act(async () => {
|
||||
defaultDispatcher.dispatch({
|
||||
action: Action.ViewUserDeviceSettings,
|
||||
});
|
||||
|
||||
expect(defaultDispatcher.dispatch).toHaveBeenCalledWith({
|
||||
action: Action.ViewUserSettings,
|
||||
initialTabId: UserTab.SessionManager,
|
||||
await waitFor(() =>
|
||||
expect(createDialog).toHaveBeenCalledWith(
|
||||
UserSettingsDialog,
|
||||
{ initialTabId: UserTab.SessionManager, sdkContext: expect.any(SdkContextClass) },
|
||||
/*className=*/ undefined,
|
||||
/*isPriority=*/ false,
|
||||
/*isStatic=*/ true,
|
||||
),
|
||||
);
|
||||
});
|
||||
});
|
||||
|
||||
@@ -674,10 +687,6 @@ describe("<MatrixChat />", () => {
|
||||
jest.spyOn(ReleaseAnnouncementStore.instance, "getReleaseAnnouncement").mockReturnValue(null);
|
||||
});
|
||||
|
||||
afterEach(() => {
|
||||
jest.restoreAllMocks();
|
||||
});
|
||||
|
||||
describe("forget_room", () => {
|
||||
it("should dispatch after_forget_room action on successful forget", async () => {
|
||||
await clearAllModals();
|
||||
|
||||
@@ -27,6 +27,7 @@ describe("BugReportDialog", () => {
|
||||
return render(<BugReportDialog onFinished={onFinished} />);
|
||||
}
|
||||
|
||||
let prevLogger: ConsoleLogger;
|
||||
beforeEach(() => {
|
||||
jest.resetAllMocks();
|
||||
SdkConfig.put({
|
||||
@@ -48,24 +49,14 @@ describe("BugReportDialog", () => {
|
||||
consume: jest.fn(),
|
||||
warn: jest.fn(),
|
||||
} as unknown as Mocked<ConsoleLogger>;
|
||||
mockConsoleLogger.flush.mockReturnValue("line 1\nline 2\n");
|
||||
|
||||
// @ts-ignore - mock the console logger
|
||||
prevLogger = global.mx_rage_logger;
|
||||
global.mx_rage_logger = mockConsoleLogger;
|
||||
|
||||
// @ts-ignore
|
||||
mockConsoleLogger.flush.mockReturnValue([
|
||||
{
|
||||
id: "instance-0",
|
||||
line: "line 1",
|
||||
},
|
||||
{
|
||||
id: "instance-1",
|
||||
line: "line 2",
|
||||
},
|
||||
]);
|
||||
});
|
||||
|
||||
afterEach(() => {
|
||||
global.mx_rage_logger = prevLogger;
|
||||
jest.restoreAllMocks();
|
||||
SdkConfig.reset();
|
||||
fetchMock.restore();
|
||||
|
||||
@@ -306,6 +306,10 @@ describe("EventTile", () => {
|
||||
[EventShieldReason.MISMATCHED_SENDER_KEY, "Encrypted by an unverified session"],
|
||||
[EventShieldReason.SENT_IN_CLEAR, "Not encrypted"],
|
||||
[EventShieldReason.VERIFICATION_VIOLATION, "Sender's verified identity was reset"],
|
||||
[
|
||||
EventShieldReason.MISMATCHED_SENDER,
|
||||
"The sender of the event does not match the owner of the device that sent it.",
|
||||
],
|
||||
])("shows the correct reason code for %i (%s)", async (reasonCode: EventShieldReason, expectedText: string) => {
|
||||
mxEvent = await mkEncryptedMatrixEvent({
|
||||
plainContent: { msgtype: "m.text", body: "msg1" },
|
||||
|
||||
@@ -92,12 +92,16 @@ describe("StopGapWidgetDriver", () => {
|
||||
"m.always_on_screen",
|
||||
"town.robin.msc3846.turn_servers",
|
||||
"org.matrix.msc2762.timeline:!1:example.org",
|
||||
"org.matrix.msc2762.send.event:org.matrix.msc4075.call.notify",
|
||||
"org.matrix.msc2762.send.event:org.matrix.msc4075.rtc.notification",
|
||||
"org.matrix.msc2762.send.event:org.matrix.rageshake_request",
|
||||
"org.matrix.msc2762.receive.event:org.matrix.rageshake_request",
|
||||
"org.matrix.msc2762.send.event:m.reaction",
|
||||
"org.matrix.msc2762.receive.event:m.reaction",
|
||||
"org.matrix.msc2762.send.event:m.room.redaction",
|
||||
"org.matrix.msc2762.receive.event:m.room.redaction",
|
||||
"org.matrix.msc2762.send.event:io.element.call.reaction",
|
||||
"org.matrix.msc2762.receive.event:io.element.call.reaction",
|
||||
"org.matrix.msc2762.receive.state_event:m.room.create",
|
||||
"org.matrix.msc2762.receive.state_event:m.room.name",
|
||||
"org.matrix.msc2762.receive.state_event:m.room.member",
|
||||
|
||||
@@ -524,25 +524,16 @@ describe("Rageshakes", () => {
|
||||
consume: jest.fn(),
|
||||
warn: jest.fn(),
|
||||
} as unknown as Mocked<ConsoleLogger>;
|
||||
mockConsoleLogger.flush.mockReturnValue("line 1\nline 2\n");
|
||||
|
||||
// @ts-ignore - mock the console logger
|
||||
const prevLogger = global.mx_rage_logger;
|
||||
global.mx_rage_logger = mockConsoleLogger;
|
||||
|
||||
// @ts-ignore
|
||||
mockConsoleLogger.flush.mockReturnValue([
|
||||
{
|
||||
id: "instance-0",
|
||||
line: "line 1",
|
||||
},
|
||||
{
|
||||
id: "instance-1",
|
||||
line: "line 2",
|
||||
},
|
||||
]);
|
||||
|
||||
const formData = await collectBugReport({ sendLogs: true });
|
||||
|
||||
expect(formData.get("compressed-log")).toBeDefined();
|
||||
try {
|
||||
const formData = await collectBugReport({ sendLogs: true });
|
||||
expect(formData.get("compressed-log")).toBeDefined();
|
||||
} finally {
|
||||
global.mx_rage_logger = prevLogger;
|
||||
}
|
||||
});
|
||||
|
||||
describe("A-Element-R label", () => {
|
||||
|
||||
12
yarn.lock
12
yarn.lock
@@ -4543,16 +4543,16 @@
|
||||
classnames "^2.5.1"
|
||||
vaul "^1.0.0"
|
||||
|
||||
"@vector-im/matrix-wysiwyg-wasm@link:../../../../Library/Caches/Yarn/v6/npm-@vector-im-matrix-wysiwyg-2.38.4-fb0001dea01010a1e3ffc7042596e2d001ce9389-integrity/node_modules/bindings/wysiwyg-wasm":
|
||||
"@vector-im/matrix-wysiwyg-wasm@link:../../bindings/wysiwyg-wasm":
|
||||
version "0.0.0"
|
||||
uid ""
|
||||
|
||||
"@vector-im/matrix-wysiwyg@2.38.4":
|
||||
version "2.38.4"
|
||||
resolved "https://registry.yarnpkg.com/@vector-im/matrix-wysiwyg/-/matrix-wysiwyg-2.38.4.tgz#fb0001dea01010a1e3ffc7042596e2d001ce9389"
|
||||
integrity sha512-X6ky+1cf33SPdEVd6iTmOKfZZ2mDJv9cz3sHtDhuclS6uitK3QE8td/pmGqBj4ek2Ia4y0mnU61LfxvMry1SMA==
|
||||
"@vector-im/matrix-wysiwyg@2.39.0":
|
||||
version "2.39.0"
|
||||
resolved "https://registry.yarnpkg.com/@vector-im/matrix-wysiwyg/-/matrix-wysiwyg-2.39.0.tgz#a6238e517f23a2f3025d9c65445914771c63b163"
|
||||
integrity sha512-OROXnzPcQWrCMoUpIrCKEC4FYU+9SsRomUgu+VbJwWtBDkCbfvLD4z6w/mgiADw3iTUpBPgmcWJoGxesFuB20Q==
|
||||
dependencies:
|
||||
"@vector-im/matrix-wysiwyg-wasm" "link:../../Library/Caches/Yarn/v6/npm-@vector-im-matrix-wysiwyg-2.38.4-fb0001dea01010a1e3ffc7042596e2d001ce9389-integrity/node_modules/bindings/wysiwyg-wasm"
|
||||
"@vector-im/matrix-wysiwyg-wasm" "link:../../Library/Caches/Yarn/v6/npm-@vector-im-matrix-wysiwyg-2.39.0-a6238e517f23a2f3025d9c65445914771c63b163-integrity/node_modules/bindings/wysiwyg-wasm"
|
||||
|
||||
"@vitest/expect@3.2.4":
|
||||
version "3.2.4"
|
||||
|
||||
Reference in New Issue
Block a user