mirror of
https://github.com/Novaloop-AG/healthchat-web.git
synced 2026-11-06 06:28:27 +00:00
Merge branch 'develop' into t3chguy/dedup-icons
This commit is contained in:
+1
-1
@@ -74,7 +74,7 @@
|
||||
"@babel/runtime": "^7.12.5",
|
||||
"@matrix-org/analytics-events": "^0.26.0",
|
||||
"@matrix-org/emojibase-bindings": "^1.1.2",
|
||||
"@matrix-org/matrix-wysiwyg": "2.37.9",
|
||||
"@vector-im/matrix-wysiwyg": "2.37.13",
|
||||
"@matrix-org/react-sdk-module-api": "^2.4.0",
|
||||
"@matrix-org/spec": "^1.7.0",
|
||||
"@sentry/browser": "^8.0.0",
|
||||
|
||||
+1
-18
@@ -85,9 +85,7 @@ export default class DeviceListener {
|
||||
public start(matrixClient: MatrixClient): void {
|
||||
this.running = true;
|
||||
this.client = matrixClient;
|
||||
this.client.on(CryptoEvent.WillUpdateDevices, this.onWillUpdateDevices);
|
||||
this.client.on(CryptoEvent.DevicesUpdated, this.onDevicesUpdated);
|
||||
this.client.on(CryptoEvent.DeviceVerificationChanged, this.onDeviceVerificationChanged);
|
||||
this.client.on(CryptoEvent.UserTrustStatusChanged, this.onUserTrustStatusChanged);
|
||||
this.client.on(CryptoEvent.KeysChanged, this.onCrossSingingKeysChanged);
|
||||
this.client.on(ClientEvent.AccountData, this.onAccountData);
|
||||
@@ -109,9 +107,7 @@ export default class DeviceListener {
|
||||
public stop(): void {
|
||||
this.running = false;
|
||||
if (this.client) {
|
||||
this.client.removeListener(CryptoEvent.WillUpdateDevices, this.onWillUpdateDevices);
|
||||
this.client.removeListener(CryptoEvent.DevicesUpdated, this.onDevicesUpdated);
|
||||
this.client.removeListener(CryptoEvent.DeviceVerificationChanged, this.onDeviceVerificationChanged);
|
||||
this.client.removeListener(CryptoEvent.UserTrustStatusChanged, this.onUserTrustStatusChanged);
|
||||
this.client.removeListener(CryptoEvent.KeysChanged, this.onCrossSingingKeysChanged);
|
||||
this.client.removeListener(ClientEvent.AccountData, this.onAccountData);
|
||||
@@ -170,7 +166,7 @@ export default class DeviceListener {
|
||||
return await getUserDeviceIds(cli, cli.getSafeUserId());
|
||||
}
|
||||
|
||||
private onWillUpdateDevices = async (users: string[], initialFetch?: boolean): Promise<void> => {
|
||||
private onDevicesUpdated = async (users: string[], initialFetch?: boolean): Promise<void> => {
|
||||
if (!this.client) return;
|
||||
// If we didn't know about *any* devices before (ie. it's fresh login),
|
||||
// then they are all pre-existing devices, so ignore this and set the
|
||||
@@ -180,19 +176,6 @@ export default class DeviceListener {
|
||||
const myUserId = this.client.getSafeUserId();
|
||||
if (users.includes(myUserId)) await this.ensureDeviceIdsAtStartPopulated();
|
||||
|
||||
// No need to do a recheck here: we just need to get a snapshot of our devices
|
||||
// before we download any new ones.
|
||||
};
|
||||
|
||||
private onDevicesUpdated = (users: string[]): void => {
|
||||
if (!this.client) return;
|
||||
if (!users.includes(this.client.getSafeUserId())) return;
|
||||
this.recheck();
|
||||
};
|
||||
|
||||
private onDeviceVerificationChanged = (userId: string): void => {
|
||||
if (!this.client) return;
|
||||
if (userId !== this.client.getUserId()) return;
|
||||
this.recheck();
|
||||
};
|
||||
|
||||
|
||||
@@ -43,6 +43,7 @@ import { formatList } from "./utils/FormattingUtils";
|
||||
import SdkConfig from "./SdkConfig";
|
||||
import { Features } from "./settings/Settings";
|
||||
import { setDeviceIsolationMode } from "./settings/controllers/DeviceIsolationModeController.ts";
|
||||
import { ReadyWatchingStore } from "./stores/ReadyWatchingStore.ts";
|
||||
|
||||
export interface IMatrixClientCreds {
|
||||
homeserverUrl: string;
|
||||
@@ -309,6 +310,7 @@ class MatrixClientPegClass implements IMatrixClientPeg {
|
||||
MatrixActionCreators.start(this.matrixClient);
|
||||
MatrixClientBackedSettingsHandler.matrixClient = this.matrixClient;
|
||||
MatrixClientBackedController.matrixClient = this.matrixClient;
|
||||
ReadyWatchingStore.matrixClient = this.matrixClient;
|
||||
|
||||
return opts;
|
||||
}
|
||||
|
||||
@@ -82,7 +82,6 @@ import Spinner from "../views/elements/Spinner";
|
||||
import QuestionDialog from "../views/dialogs/QuestionDialog";
|
||||
import UserSettingsDialog from "../views/dialogs/UserSettingsDialog";
|
||||
import CreateRoomDialog from "../views/dialogs/CreateRoomDialog";
|
||||
import KeySignatureUploadFailedDialog from "../views/dialogs/KeySignatureUploadFailedDialog";
|
||||
import IncomingSasDialog from "../views/dialogs/IncomingSasDialog";
|
||||
import CompleteSecurity from "./auth/CompleteSecurity";
|
||||
import Welcome from "../views/auth/Welcome";
|
||||
@@ -1629,18 +1628,6 @@ export default class MatrixChat extends React.PureComponent<IProps, IState> {
|
||||
room.setBlacklistUnverifiedDevices(blacklistEnabled);
|
||||
}
|
||||
});
|
||||
cli.on(CryptoEvent.Warning, (type) => {
|
||||
switch (type) {
|
||||
case "CRYPTO_WARNING_OLD_VERSION_DETECTED":
|
||||
Modal.createDialog(ErrorDialog, {
|
||||
title: _t("encryption|old_version_detected_title"),
|
||||
description: _t("encryption|old_version_detected_description", {
|
||||
brand: SdkConfig.get().brand,
|
||||
}),
|
||||
});
|
||||
break;
|
||||
}
|
||||
});
|
||||
cli.on(CryptoEvent.KeyBackupFailed, async (errcode): Promise<void> => {
|
||||
let haveNewVersion: boolean | undefined;
|
||||
let newVersionInfo: KeyBackupInfo | null = null;
|
||||
@@ -1674,10 +1661,6 @@ export default class MatrixChat extends React.PureComponent<IProps, IState> {
|
||||
}
|
||||
});
|
||||
|
||||
cli.on(CryptoEvent.KeySignatureUploadFailure, (failures, source, continuation) => {
|
||||
Modal.createDialog(KeySignatureUploadFailedDialog, { failures, source, continuation });
|
||||
});
|
||||
|
||||
cli.on(CryptoEvent.VerificationRequestReceived, (request) => {
|
||||
if (request.verifier) {
|
||||
Modal.createDialog(
|
||||
|
||||
@@ -428,7 +428,6 @@ export class RoomView extends React.Component<IRoomProps, IRoomState> {
|
||||
context.client.on(RoomStateEvent.Update, this.onRoomStateUpdate);
|
||||
context.client.on(RoomEvent.MyMembership, this.onMyMembership);
|
||||
context.client.on(CryptoEvent.KeyBackupStatus, this.onKeyBackupStatus);
|
||||
context.client.on(CryptoEvent.DeviceVerificationChanged, this.onDeviceVerificationChanged);
|
||||
context.client.on(CryptoEvent.UserTrustStatusChanged, this.onUserVerificationChanged);
|
||||
context.client.on(CryptoEvent.KeysChanged, this.onCrossSigningKeysChanged);
|
||||
context.client.on(MatrixEventEvent.Decrypted, this.onEventDecrypted);
|
||||
@@ -974,7 +973,6 @@ export class RoomView extends React.Component<IRoomProps, IRoomState> {
|
||||
this.context.client.removeListener(RoomEvent.MyMembership, this.onMyMembership);
|
||||
this.context.client.removeListener(RoomStateEvent.Update, this.onRoomStateUpdate);
|
||||
this.context.client.removeListener(CryptoEvent.KeyBackupStatus, this.onKeyBackupStatus);
|
||||
this.context.client.removeListener(CryptoEvent.DeviceVerificationChanged, this.onDeviceVerificationChanged);
|
||||
this.context.client.removeListener(CryptoEvent.UserTrustStatusChanged, this.onUserVerificationChanged);
|
||||
this.context.client.removeListener(CryptoEvent.KeysChanged, this.onCrossSigningKeysChanged);
|
||||
this.context.client.removeListener(MatrixEventEvent.Decrypted, this.onEventDecrypted);
|
||||
@@ -1438,14 +1436,6 @@ export class RoomView extends React.Component<IRoomProps, IRoomState> {
|
||||
);
|
||||
};
|
||||
|
||||
private onDeviceVerificationChanged = (userId: string): void => {
|
||||
const room = this.state.room;
|
||||
if (!room?.currentState.getMember(userId)) {
|
||||
return;
|
||||
}
|
||||
this.updateE2EStatus(room);
|
||||
};
|
||||
|
||||
private onUserVerificationChanged = (userId: string): void => {
|
||||
const room = this.state.room;
|
||||
if (!room || !room.currentState.getMember(userId)) {
|
||||
|
||||
@@ -1,117 +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
|
||||
Please see LICENSE files in the repository root for full details.
|
||||
*/
|
||||
|
||||
import React, { useState, useCallback, useRef } from "react";
|
||||
|
||||
import { _t } from "../../../languageHandler";
|
||||
import SdkConfig from "../../../SdkConfig";
|
||||
import BaseDialog from "./BaseDialog";
|
||||
import DialogButtons from "../elements/DialogButtons";
|
||||
import Spinner from "../elements/Spinner";
|
||||
|
||||
interface IProps {
|
||||
failures: Record<
|
||||
string,
|
||||
Record<
|
||||
string,
|
||||
{
|
||||
errcode: string;
|
||||
error: string;
|
||||
}
|
||||
>
|
||||
>;
|
||||
source: string;
|
||||
continuation: (opts: { shouldEmit: boolean }) => Promise<void>;
|
||||
onFinished(): void;
|
||||
}
|
||||
|
||||
const KeySignatureUploadFailedDialog: React.FC<IProps> = ({ failures, source, continuation, onFinished }) => {
|
||||
const RETRIES = 2;
|
||||
const [retry, setRetry] = useState(RETRIES);
|
||||
const [cancelled, setCancelled] = useState(false);
|
||||
const [retrying, setRetrying] = useState(false);
|
||||
const [success, setSuccess] = useState(false);
|
||||
const onCancel = useRef(onFinished);
|
||||
|
||||
const causes = new Map([
|
||||
["_afterCrossSigningLocalKeyChange", _t("encryption|key_signature_upload_failed_master_key_signature")],
|
||||
["checkOwnCrossSigningTrust", _t("encryption|key_signature_upload_failed_cross_signing_key_signature")],
|
||||
["setDeviceVerification", _t("encryption|key_signature_upload_failed_device_cross_signing_key_signature")],
|
||||
]);
|
||||
const defaultCause = _t("encryption|key_signature_upload_failed_key_signature");
|
||||
|
||||
const onRetry = useCallback(async (): Promise<void> => {
|
||||
try {
|
||||
setRetrying(true);
|
||||
const cancel = new Promise((resolve, reject) => {
|
||||
onCancel.current = reject;
|
||||
}).finally(() => {
|
||||
setCancelled(true);
|
||||
});
|
||||
await Promise.race([continuation({ shouldEmit: false }), cancel]);
|
||||
setSuccess(true);
|
||||
} catch (e) {
|
||||
setRetry((r) => r - 1);
|
||||
} finally {
|
||||
onCancel.current = onFinished;
|
||||
setRetrying(false);
|
||||
}
|
||||
}, [continuation, onFinished]);
|
||||
|
||||
let body;
|
||||
if (!success && !cancelled && retry > 0) {
|
||||
const reason = causes.get(source) || defaultCause;
|
||||
const brand = SdkConfig.get().brand;
|
||||
|
||||
body = (
|
||||
<div>
|
||||
<p>{_t("encryption|key_signature_upload_failed_body", { brand })}</p>
|
||||
<p>{reason}</p>
|
||||
{retrying && <Spinner />}
|
||||
<pre>{JSON.stringify(failures, null, 2)}</pre>
|
||||
<DialogButtons
|
||||
primaryButton="Retry"
|
||||
hasCancel={true}
|
||||
onPrimaryButtonClick={onRetry}
|
||||
onCancel={onCancel.current}
|
||||
primaryDisabled={retrying}
|
||||
/>
|
||||
</div>
|
||||
);
|
||||
} else {
|
||||
let text = _t("encryption|key_signature_upload_completed");
|
||||
if (!success) {
|
||||
text = cancelled
|
||||
? _t("encryption|key_signature_upload_cancelled")
|
||||
: _t("encryption|key_signature_upload_failed");
|
||||
}
|
||||
|
||||
body = (
|
||||
<div>
|
||||
<span>{text}</span>
|
||||
<DialogButtons primaryButton={_t("action|ok")} hasCancel={false} onPrimaryButtonClick={onFinished} />
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
return (
|
||||
<BaseDialog
|
||||
title={
|
||||
success
|
||||
? _t("encryption|key_signature_upload_success_title")
|
||||
: _t("encryption|key_signature_upload_failed_title")
|
||||
}
|
||||
fixedWidth={false}
|
||||
onFinished={() => {}}
|
||||
>
|
||||
{body}
|
||||
</BaseDialog>
|
||||
);
|
||||
};
|
||||
|
||||
export default KeySignatureUploadFailedDialog;
|
||||
@@ -190,25 +190,42 @@ export default class MessageEvent extends React.Component<IProps> implements IMe
|
||||
}
|
||||
}
|
||||
|
||||
return BodyType ? (
|
||||
<BodyType
|
||||
ref={this.body}
|
||||
mxEvent={this.props.mxEvent}
|
||||
highlights={this.props.highlights}
|
||||
highlightLink={this.props.highlightLink}
|
||||
showUrlPreview={this.props.showUrlPreview}
|
||||
forExport={this.props.forExport}
|
||||
maxImageHeight={this.props.maxImageHeight}
|
||||
replacingEventId={this.props.replacingEventId}
|
||||
editState={this.props.editState}
|
||||
onHeightChanged={this.props.onHeightChanged}
|
||||
onMessageAllowed={this.onTileUpdate}
|
||||
permalinkCreator={this.props.permalinkCreator}
|
||||
mediaEventHelper={this.mediaHelper}
|
||||
getRelationsForEvent={this.props.getRelationsForEvent}
|
||||
isSeeingThroughMessageHiddenForModeration={this.props.isSeeingThroughMessageHiddenForModeration}
|
||||
inhibitInteraction={this.props.inhibitInteraction}
|
||||
/>
|
||||
) : null;
|
||||
const hasCaption =
|
||||
[MsgType.Image, MsgType.File, MsgType.Audio, MsgType.Video].includes(msgtype as MsgType) &&
|
||||
content.filename &&
|
||||
content.filename !== content.body;
|
||||
const bodyProps: IBodyProps = {
|
||||
ref: this.body,
|
||||
mxEvent: this.props.mxEvent,
|
||||
highlights: this.props.highlights,
|
||||
highlightLink: this.props.highlightLink,
|
||||
showUrlPreview: this.props.showUrlPreview,
|
||||
forExport: this.props.forExport,
|
||||
maxImageHeight: this.props.maxImageHeight,
|
||||
replacingEventId: this.props.replacingEventId,
|
||||
editState: this.props.editState,
|
||||
onHeightChanged: this.props.onHeightChanged,
|
||||
onMessageAllowed: this.onTileUpdate,
|
||||
permalinkCreator: this.props.permalinkCreator,
|
||||
mediaEventHelper: this.mediaHelper,
|
||||
getRelationsForEvent: this.props.getRelationsForEvent,
|
||||
isSeeingThroughMessageHiddenForModeration: this.props.isSeeingThroughMessageHiddenForModeration,
|
||||
inhibitInteraction: this.props.inhibitInteraction,
|
||||
};
|
||||
if (hasCaption) {
|
||||
return <CaptionBody {...bodyProps} WrappedBodyType={BodyType} />;
|
||||
}
|
||||
|
||||
return BodyType ? <BodyType {...bodyProps} /> : null;
|
||||
}
|
||||
}
|
||||
|
||||
const CaptionBody: React.FunctionComponent<IBodyProps & { WrappedBodyType: React.ComponentType<IBodyProps> }> = ({
|
||||
WrappedBodyType,
|
||||
...props
|
||||
}) => (
|
||||
<div className="mx_EventTile_content">
|
||||
<WrappedBodyType {...props} />
|
||||
<TextualBody {...{ ...props, ref: undefined }} />
|
||||
</div>
|
||||
);
|
||||
|
||||
@@ -556,6 +556,9 @@ export default class TextualBody extends React.Component<IBodyProps, IState> {
|
||||
const content = mxEvent.getContent();
|
||||
const isNotice = content.msgtype === MsgType.Notice;
|
||||
const isEmote = content.msgtype === MsgType.Emote;
|
||||
const isCaption = [MsgType.Image, MsgType.File, MsgType.Audio, MsgType.Video].includes(
|
||||
content.msgtype as MsgType,
|
||||
);
|
||||
|
||||
const willHaveWrapper =
|
||||
this.props.replacingEventId || this.props.isSeeingThroughMessageHiddenForModeration || isEmote;
|
||||
@@ -635,6 +638,14 @@ export default class TextualBody extends React.Component<IBodyProps, IState> {
|
||||
</div>
|
||||
);
|
||||
}
|
||||
if (isCaption) {
|
||||
return (
|
||||
<div className="mx_MTextBody mx_EventTile_caption" onClick={this.onBodyLinkClick}>
|
||||
{body}
|
||||
{widgets}
|
||||
</div>
|
||||
);
|
||||
}
|
||||
return (
|
||||
<div className="mx_MTextBody mx_EventTile_content" onClick={this.onBodyLinkClick}>
|
||||
{body}
|
||||
|
||||
@@ -1381,22 +1381,16 @@ export const useDevices = (userId: string): IDevice[] | undefined | null => {
|
||||
if (!users.includes(userId)) return;
|
||||
updateDevices();
|
||||
};
|
||||
const onDeviceVerificationChanged = (_userId: string, deviceId: string): void => {
|
||||
if (_userId !== userId) return;
|
||||
updateDevices();
|
||||
};
|
||||
const onUserTrustStatusChanged = (_userId: string, trustLevel: UserVerificationStatus): void => {
|
||||
if (_userId !== userId) return;
|
||||
updateDevices();
|
||||
};
|
||||
cli.on(CryptoEvent.DevicesUpdated, onDevicesUpdated);
|
||||
cli.on(CryptoEvent.DeviceVerificationChanged, onDeviceVerificationChanged);
|
||||
cli.on(CryptoEvent.UserTrustStatusChanged, onUserTrustStatusChanged);
|
||||
// Handle being unmounted
|
||||
return () => {
|
||||
cancel = true;
|
||||
cli.removeListener(CryptoEvent.DevicesUpdated, onDevicesUpdated);
|
||||
cli.removeListener(CryptoEvent.DeviceVerificationChanged, onDeviceVerificationChanged);
|
||||
cli.removeListener(CryptoEvent.UserTrustStatusChanged, onUserTrustStatusChanged);
|
||||
};
|
||||
}, [cli, userId]);
|
||||
|
||||
@@ -385,7 +385,6 @@ export class UnwrappedEventTile extends React.Component<EventTileProps, IState>
|
||||
this.suppressReadReceiptAnimation = false;
|
||||
const client = MatrixClientPeg.safeGet();
|
||||
if (!this.props.forExport) {
|
||||
client.on(CryptoEvent.DeviceVerificationChanged, this.onDeviceVerificationChanged);
|
||||
client.on(CryptoEvent.UserTrustStatusChanged, this.onUserVerificationChanged);
|
||||
this.props.mxEvent.on(MatrixEventEvent.Decrypted, this.onDecrypted);
|
||||
this.props.mxEvent.on(MatrixEventEvent.Replaced, this.onReplaced);
|
||||
@@ -425,7 +424,6 @@ export class UnwrappedEventTile extends React.Component<EventTileProps, IState>
|
||||
public componentWillUnmount(): void {
|
||||
const client = MatrixClientPeg.get();
|
||||
if (client) {
|
||||
client.removeListener(CryptoEvent.DeviceVerificationChanged, this.onDeviceVerificationChanged);
|
||||
client.removeListener(CryptoEvent.UserTrustStatusChanged, this.onUserVerificationChanged);
|
||||
client.removeListener(RoomEvent.Receipt, this.onRoomReceipt);
|
||||
const room = client.getRoom(this.props.mxEvent.getRoomId());
|
||||
@@ -564,12 +562,6 @@ export class UnwrappedEventTile extends React.Component<EventTileProps, IState>
|
||||
this.forceUpdate(this.props.onHeightChanged);
|
||||
};
|
||||
|
||||
private onDeviceVerificationChanged = (userId: string, device: string): void => {
|
||||
if (userId === this.props.mxEvent.getSender()) {
|
||||
this.verifyEvent();
|
||||
}
|
||||
};
|
||||
|
||||
private onUserVerificationChanged = (userId: string, _trustStatus: UserVerificationStatus): void => {
|
||||
if (userId === this.props.mxEvent.getSender()) {
|
||||
this.verifyEvent();
|
||||
|
||||
@@ -9,7 +9,6 @@ Please see LICENSE files in the repository root for full details.
|
||||
|
||||
import React from "react";
|
||||
import { RoomMember, RoomStateEvent, MatrixEvent, EventType } from "matrix-js-sdk/src/matrix";
|
||||
import { DeviceInfo } from "matrix-js-sdk/src/crypto/deviceinfo";
|
||||
import { CryptoEvent } from "matrix-js-sdk/src/crypto";
|
||||
import { UserVerificationStatus } from "matrix-js-sdk/src/crypto-api";
|
||||
|
||||
@@ -62,7 +61,6 @@ export default class MemberTile extends React.Component<IProps, IState> {
|
||||
});
|
||||
if (isRoomEncrypted) {
|
||||
cli.on(CryptoEvent.UserTrustStatusChanged, this.onUserTrustStatusChanged);
|
||||
cli.on(CryptoEvent.DeviceVerificationChanged, this.onDeviceVerificationChanged);
|
||||
this.updateE2EStatus();
|
||||
} else {
|
||||
// Listen for room to become encrypted
|
||||
@@ -77,7 +75,6 @@ export default class MemberTile extends React.Component<IProps, IState> {
|
||||
if (cli) {
|
||||
cli.removeListener(RoomStateEvent.Events, this.onRoomStateEvents);
|
||||
cli.removeListener(CryptoEvent.UserTrustStatusChanged, this.onUserTrustStatusChanged);
|
||||
cli.removeListener(CryptoEvent.DeviceVerificationChanged, this.onDeviceVerificationChanged);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -100,11 +97,6 @@ export default class MemberTile extends React.Component<IProps, IState> {
|
||||
this.updateE2EStatus();
|
||||
};
|
||||
|
||||
private onDeviceVerificationChanged = (userId: string, deviceId: string, deviceInfo: DeviceInfo): void => {
|
||||
if (userId !== this.props.member.userId) return;
|
||||
this.updateE2EStatus();
|
||||
};
|
||||
|
||||
private async updateE2EStatus(): Promise<void> {
|
||||
const cli = MatrixClientPeg.safeGet();
|
||||
const { userId } = this.props.member;
|
||||
|
||||
@@ -10,7 +10,7 @@ import React, { ComponentProps, lazy, Suspense } from "react";
|
||||
import { ISendEventResponse } from "matrix-js-sdk/src/matrix";
|
||||
|
||||
// we need to import the types for TS, but do not import the sendMessage
|
||||
// function to avoid importing from "@matrix-org/matrix-wysiwyg"
|
||||
// function to avoid importing from "@vector-im/matrix-wysiwyg"
|
||||
import { SendMessageParams } from "./utils/message";
|
||||
import { retry } from "../../../../utils/promise";
|
||||
|
||||
@@ -55,7 +55,7 @@ export const dynamicImportConversionFunctions = async (): Promise<{
|
||||
*/
|
||||
plainToRich(plain: string, inMessageFormat: boolean): Promise<string>;
|
||||
}> => {
|
||||
const { richToPlain, plainToRich } = await retry(() => import("@matrix-org/matrix-wysiwyg"), RETRY_COUNT);
|
||||
const { richToPlain, plainToRich } = await retry(() => import("@vector-im/matrix-wysiwyg"), RETRY_COUNT);
|
||||
|
||||
return { richToPlain, plainToRich };
|
||||
};
|
||||
|
||||
@@ -7,7 +7,7 @@ Please see LICENSE files in the repository root for full details.
|
||||
*/
|
||||
|
||||
import React, { MouseEventHandler, ReactNode } from "react";
|
||||
import { FormattingFunctions, AllActionStates, ActionState } from "@matrix-org/matrix-wysiwyg";
|
||||
import { FormattingFunctions, AllActionStates, ActionState } from "@vector-im/matrix-wysiwyg";
|
||||
import classNames from "classnames";
|
||||
import BoldIcon from "@vector-im/compound-design-tokens/assets/web/icons/bold";
|
||||
import BulletedListIcon from "@vector-im/compound-design-tokens/assets/web/icons/list-bulleted";
|
||||
|
||||
@@ -6,7 +6,7 @@ SPDX-License-Identifier: AGPL-3.0-only OR GPL-3.0-only
|
||||
Please see LICENSE files in the repository root for full details.
|
||||
*/
|
||||
|
||||
import { FormattingFunctions } from "@matrix-org/matrix-wysiwyg";
|
||||
import { FormattingFunctions } from "@vector-im/matrix-wysiwyg";
|
||||
import React, { ChangeEvent, useState } from "react";
|
||||
|
||||
import { _t } from "../../../../../languageHandler";
|
||||
|
||||
@@ -7,7 +7,7 @@ Please see LICENSE files in the repository root for full details.
|
||||
*/
|
||||
|
||||
import React, { ForwardedRef, forwardRef, FunctionComponent } from "react";
|
||||
import { FormattingFunctions, MappedSuggestion } from "@matrix-org/matrix-wysiwyg";
|
||||
import { FormattingFunctions, MappedSuggestion } from "@vector-im/matrix-wysiwyg";
|
||||
import { logger } from "matrix-js-sdk/src/logger";
|
||||
|
||||
import { useRoomContext } from "../../../../../contexts/RoomContext";
|
||||
|
||||
@@ -9,7 +9,7 @@ Please see LICENSE files in the repository root for full details.
|
||||
import React, { memo, MutableRefObject, ReactNode, useEffect, useMemo, useRef } from "react";
|
||||
import { IEventRelation } from "matrix-js-sdk/src/matrix";
|
||||
import { EMOTICON_TO_EMOJI } from "@matrix-org/emojibase-bindings";
|
||||
import { useWysiwyg, FormattingFunctions } from "@matrix-org/matrix-wysiwyg";
|
||||
import { useWysiwyg, FormattingFunctions } from "@vector-im/matrix-wysiwyg";
|
||||
import classNames from "classnames";
|
||||
|
||||
import Autocomplete from "../../Autocomplete";
|
||||
|
||||
@@ -6,7 +6,7 @@ SPDX-License-Identifier: AGPL-3.0-only OR GPL-3.0-only
|
||||
Please see LICENSE files in the repository root for full details.
|
||||
*/
|
||||
|
||||
import { Wysiwyg, WysiwygEvent } from "@matrix-org/matrix-wysiwyg";
|
||||
import { Wysiwyg, WysiwygEvent } from "@vector-im/matrix-wysiwyg";
|
||||
import { useCallback } from "react";
|
||||
import { IEventRelation, MatrixClient } from "matrix-js-sdk/src/matrix";
|
||||
|
||||
|
||||
@@ -7,7 +7,7 @@ Please see LICENSE files in the repository root for full details.
|
||||
*/
|
||||
|
||||
import { KeyboardEvent, RefObject, SyntheticEvent, useCallback, useRef, useState } from "react";
|
||||
import { AllowedMentionAttributes, MappedSuggestion } from "@matrix-org/matrix-wysiwyg";
|
||||
import { AllowedMentionAttributes, MappedSuggestion } from "@vector-im/matrix-wysiwyg";
|
||||
import { IEventRelation } from "matrix-js-sdk/src/matrix";
|
||||
|
||||
import { useSettingValue } from "../../../../../hooks/useSettings";
|
||||
|
||||
@@ -7,7 +7,7 @@ Please see LICENSE files in the repository root for full details.
|
||||
*/
|
||||
|
||||
import { EMOTICON_TO_EMOJI } from "@matrix-org/emojibase-bindings";
|
||||
import { AllowedMentionAttributes, MappedSuggestion } from "@matrix-org/matrix-wysiwyg";
|
||||
import { AllowedMentionAttributes, MappedSuggestion } from "@vector-im/matrix-wysiwyg";
|
||||
import { SyntheticEvent, useState, SetStateAction } from "react";
|
||||
import { logger } from "matrix-js-sdk/src/logger";
|
||||
|
||||
|
||||
@@ -8,7 +8,7 @@ Please see LICENSE files in the repository root for full details.
|
||||
|
||||
import { MutableRefObject, RefObject } from "react";
|
||||
import { IEventRelation, MatrixClient } from "matrix-js-sdk/src/matrix";
|
||||
import { WysiwygEvent } from "@matrix-org/matrix-wysiwyg";
|
||||
import { WysiwygEvent } from "@vector-im/matrix-wysiwyg";
|
||||
|
||||
import { TimelineRenderingType } from "../../../../../contexts/RoomContext";
|
||||
import { IRoomState } from "../../../../structures/RoomView";
|
||||
|
||||
@@ -6,7 +6,7 @@ SPDX-License-Identifier: AGPL-3.0-only OR GPL-3.0-only
|
||||
Please see LICENSE files in the repository root for full details.
|
||||
*/
|
||||
|
||||
import { AllowedMentionAttributes, MappedSuggestion } from "@matrix-org/matrix-wysiwyg";
|
||||
import { AllowedMentionAttributes, MappedSuggestion } from "@vector-im/matrix-wysiwyg";
|
||||
import { MatrixClient, Room } from "matrix-js-sdk/src/matrix";
|
||||
|
||||
import { ICompletion } from "../../../../../autocomplete/Autocompleter";
|
||||
|
||||
@@ -6,7 +6,7 @@ SPDX-License-Identifier: AGPL-3.0-only OR GPL-3.0-only
|
||||
Please see LICENSE files in the repository root for full details.
|
||||
*/
|
||||
|
||||
import { richToPlain, plainToRich } from "@matrix-org/matrix-wysiwyg";
|
||||
import { richToPlain, plainToRich } from "@vector-im/matrix-wysiwyg";
|
||||
import { IContent, IEventRelation, MatrixEvent, MsgType } from "matrix-js-sdk/src/matrix";
|
||||
import { ReplacementEvent, RoomMessageEventContent, RoomMessageTextEventContent } from "matrix-js-sdk/src/types";
|
||||
|
||||
|
||||
@@ -904,16 +904,6 @@
|
||||
"incompatible_database_disable": "Continue With Encryption Disabled",
|
||||
"incompatible_database_sign_out_description": "To avoid losing your chat history, you must export your room keys before logging out. You will need to go back to the newer version of %(brand)s to do this",
|
||||
"incompatible_database_title": "Incompatible Database",
|
||||
"key_signature_upload_cancelled": "Cancelled signature upload",
|
||||
"key_signature_upload_completed": "Upload completed",
|
||||
"key_signature_upload_failed": "Unable to upload",
|
||||
"key_signature_upload_failed_body": "%(brand)s encountered an error during upload of:",
|
||||
"key_signature_upload_failed_cross_signing_key_signature": "a new cross-signing key signature",
|
||||
"key_signature_upload_failed_device_cross_signing_key_signature": "a device cross-signing signature",
|
||||
"key_signature_upload_failed_key_signature": "a key signature",
|
||||
"key_signature_upload_failed_master_key_signature": "a new master key signature",
|
||||
"key_signature_upload_failed_title": "Signature upload failed",
|
||||
"key_signature_upload_success_title": "Signature upload success",
|
||||
"messages_not_secure": {
|
||||
"cause_1": "Your homeserver",
|
||||
"cause_2": "The homeserver the user you're verifying is connected to",
|
||||
@@ -929,8 +919,6 @@
|
||||
"warning": "If you didn't set the new recovery method, an attacker may be trying to access your account. Change your account password and set a new recovery method immediately in Settings."
|
||||
},
|
||||
"not_supported": "<not supported>",
|
||||
"old_version_detected_description": "Data from an older version of %(brand)s has been detected. This will have caused end-to-end cryptography to malfunction in the older version. End-to-end encrypted messages exchanged recently whilst using the older version may not be decryptable in this version. This may also cause messages exchanged with this version to fail. If you experience problems, log out and back in again. To retain message history, export and re-import your keys.",
|
||||
"old_version_detected_title": "Old cryptography data detected",
|
||||
"recovery_method_removed": {
|
||||
"description_1": "This session has detected that your Security Phrase and key for Secure Messages have been removed.",
|
||||
"description_2": "If you did this accidentally, you can setup Secure Messages on this session which will re-encrypt this session's message history with a new recovery method.",
|
||||
|
||||
@@ -36,8 +36,13 @@ export abstract class AsyncStoreWithClient<T extends Object> extends AsyncStore<
|
||||
})(dispatcher);
|
||||
}
|
||||
|
||||
public async start(): Promise<void> {
|
||||
await this.readyStore.start();
|
||||
protected async start(matrixClient: MatrixClient | null): Promise<void> {
|
||||
await this.readyStore.start(matrixClient);
|
||||
}
|
||||
|
||||
// XXX: This method is intended only for use in tests.
|
||||
public async useUnitTestClient(cli: MatrixClient): Promise<void> {
|
||||
await this.readyStore.useUnitTestClient(cli);
|
||||
}
|
||||
|
||||
public get matrixClient(): MatrixClient | null {
|
||||
|
||||
@@ -46,9 +46,7 @@ interface IState {
|
||||
*/
|
||||
export default class AutoRageshakeStore extends AsyncStoreWithClient<IState> {
|
||||
private static readonly internalInstance = (() => {
|
||||
const instance = new AutoRageshakeStore();
|
||||
instance.start();
|
||||
return instance;
|
||||
return new AutoRageshakeStore();
|
||||
})();
|
||||
|
||||
private constructor() {
|
||||
|
||||
@@ -30,9 +30,7 @@ interface IState {
|
||||
|
||||
export class BreadcrumbsStore extends AsyncStoreWithClient<IState> {
|
||||
private static readonly internalInstance = (() => {
|
||||
const instance = new BreadcrumbsStore();
|
||||
instance.start();
|
||||
return instance;
|
||||
return new BreadcrumbsStore();
|
||||
})();
|
||||
|
||||
private waitingRooms: { roomId: string; addedTs: number }[] = [];
|
||||
|
||||
@@ -31,7 +31,6 @@ export class CallStore extends AsyncStoreWithClient<{}> {
|
||||
public static get instance(): CallStore {
|
||||
if (!this._instance) {
|
||||
this._instance = new CallStore();
|
||||
this._instance.start();
|
||||
}
|
||||
return this._instance;
|
||||
}
|
||||
|
||||
@@ -24,7 +24,6 @@ interface IState {
|
||||
export class ModalWidgetStore extends AsyncStoreWithClient<IState> {
|
||||
private static readonly internalInstance = (() => {
|
||||
const instance = new ModalWidgetStore();
|
||||
instance.start();
|
||||
return instance;
|
||||
})();
|
||||
private modalInstance: IHandle<typeof ModalWidgetDialog> | null = null;
|
||||
|
||||
@@ -87,7 +87,6 @@ const getLocallyCreatedBeaconEventIds = (): string[] => {
|
||||
export class OwnBeaconStore extends AsyncStoreWithClient<OwnBeaconStoreState> {
|
||||
private static readonly internalInstance = (() => {
|
||||
const instance = new OwnBeaconStore();
|
||||
instance.start();
|
||||
return instance;
|
||||
})();
|
||||
// users beacons, keyed by event type
|
||||
|
||||
@@ -28,7 +28,6 @@ const KEY_AVATAR_URL = "mx_profile_avatar_url";
|
||||
export class OwnProfileStore extends AsyncStoreWithClient<IState> {
|
||||
private static readonly internalInstance = (() => {
|
||||
const instance = new OwnProfileStore();
|
||||
instance.start();
|
||||
return instance;
|
||||
})();
|
||||
|
||||
|
||||
@@ -9,27 +9,40 @@
|
||||
import { MatrixClient, SyncState } from "matrix-js-sdk/src/matrix";
|
||||
import { EventEmitter } from "events";
|
||||
|
||||
import { MatrixClientPeg } from "../MatrixClientPeg";
|
||||
import { ActionPayload } from "../dispatcher/payloads";
|
||||
import { IDestroyable } from "../utils/IDestroyable";
|
||||
import { Action } from "../dispatcher/actions";
|
||||
import { MatrixDispatcher } from "../dispatcher/dispatcher";
|
||||
|
||||
export abstract class ReadyWatchingStore extends EventEmitter implements IDestroyable {
|
||||
protected matrixClient: MatrixClient | null = null;
|
||||
private static instances: ReadyWatchingStore[] = [];
|
||||
protected _matrixClient: MatrixClient | null = null;
|
||||
private dispatcherRef: string | null = null;
|
||||
|
||||
public static set matrixClient(client: MatrixClient) {
|
||||
for (const instance of ReadyWatchingStore.instances) {
|
||||
instance.start(client);
|
||||
}
|
||||
}
|
||||
|
||||
public constructor(protected readonly dispatcher: MatrixDispatcher) {
|
||||
super();
|
||||
|
||||
this.dispatcherRef = this.dispatcher.register(this.onAction);
|
||||
}
|
||||
|
||||
public async start(): Promise<void> {
|
||||
this.dispatcherRef = this.dispatcher.register(this.onAction);
|
||||
public get matrixClient(): MatrixClient | null {
|
||||
return this._matrixClient;
|
||||
}
|
||||
|
||||
// MatrixClientPeg can be undefined in tests because of circular dependencies with other stores
|
||||
const matrixClient = MatrixClientPeg?.get();
|
||||
public async start(matrixClient: MatrixClient | null): Promise<void> {
|
||||
const oldClient = this._matrixClient;
|
||||
this._matrixClient = matrixClient;
|
||||
|
||||
if (oldClient !== matrixClient) {
|
||||
await this.onNotReady();
|
||||
}
|
||||
if (matrixClient) {
|
||||
this.matrixClient = matrixClient;
|
||||
await this.onReady();
|
||||
}
|
||||
}
|
||||
@@ -38,8 +51,10 @@ export abstract class ReadyWatchingStore extends EventEmitter implements IDestro
|
||||
return this.matrixClient; // for external readonly access
|
||||
}
|
||||
|
||||
public useUnitTestClient(cli: MatrixClient): void {
|
||||
this.matrixClient = cli;
|
||||
// XXX: This method is intended only for use in tests.
|
||||
public async useUnitTestClient(cli: MatrixClient): Promise<void> {
|
||||
this._matrixClient = cli;
|
||||
await this.onReady();
|
||||
}
|
||||
|
||||
public destroy(): void {
|
||||
@@ -74,13 +89,13 @@ export abstract class ReadyWatchingStore extends EventEmitter implements IDestro
|
||||
if (this.matrixClient) {
|
||||
await this.onNotReady();
|
||||
}
|
||||
this.matrixClient = payload.matrixClient;
|
||||
this._matrixClient = payload.matrixClient;
|
||||
await this.onReady();
|
||||
}
|
||||
} else if (payload.action === "on_client_not_viable" || payload.action === Action.OnLoggedOut) {
|
||||
if (this.matrixClient) {
|
||||
await this.onNotReady();
|
||||
this.matrixClient = null;
|
||||
this._matrixClient = null;
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
@@ -30,7 +30,6 @@ export class VoiceRecordingStore extends AsyncStoreWithClient<IState> {
|
||||
public static get instance(): VoiceRecordingStore {
|
||||
if (!this.internalInstance) {
|
||||
this.internalInstance = new VoiceRecordingStore();
|
||||
this.internalInstance.start();
|
||||
}
|
||||
return this.internalInstance;
|
||||
}
|
||||
|
||||
@@ -45,7 +45,6 @@ interface IRoomWidgets {
|
||||
export default class WidgetStore extends AsyncStoreWithClient<IState> {
|
||||
private static readonly internalInstance = (() => {
|
||||
const instance = new WidgetStore();
|
||||
instance.start();
|
||||
return instance;
|
||||
})();
|
||||
|
||||
|
||||
@@ -38,7 +38,6 @@ export class EchoStore extends AsyncStoreWithClient<IState> {
|
||||
public static get instance(): EchoStore {
|
||||
if (!this._instance) {
|
||||
this._instance = new EchoStore();
|
||||
this._instance.start();
|
||||
}
|
||||
return this._instance;
|
||||
}
|
||||
|
||||
@@ -26,7 +26,6 @@ export const UPDATE_STATUS_INDICATOR = Symbol("update-status-indicator");
|
||||
export class RoomNotificationStateStore extends AsyncStoreWithClient<IState> {
|
||||
private static readonly internalInstance = (() => {
|
||||
const instance = new RoomNotificationStateStore();
|
||||
instance.start();
|
||||
return instance;
|
||||
})();
|
||||
private roomMap = new Map<Room, RoomNotificationState>();
|
||||
|
||||
@@ -403,7 +403,6 @@ export default class RightPanelStore extends ReadyWatchingStore {
|
||||
public static get instance(): RightPanelStore {
|
||||
if (!this.internalInstance) {
|
||||
this.internalInstance = new RightPanelStore();
|
||||
this.internalInstance.start();
|
||||
}
|
||||
return this.internalInstance;
|
||||
}
|
||||
|
||||
@@ -124,11 +124,7 @@ const mkMessagePreview = (text: string, event: MatrixEvent): MessagePreview => {
|
||||
};
|
||||
|
||||
export class MessagePreviewStore extends AsyncStoreWithClient<IState> {
|
||||
private static readonly internalInstance = (() => {
|
||||
const instance = new MessagePreviewStore();
|
||||
instance.start();
|
||||
return instance;
|
||||
})();
|
||||
private static readonly internalInstance = (() => new MessagePreviewStore())();
|
||||
|
||||
/**
|
||||
* @internal Public for test only
|
||||
|
||||
@@ -28,7 +28,6 @@ export default class RoomListLayoutStore extends AsyncStoreWithClient<IState> {
|
||||
public static get instance(): RoomListLayoutStore {
|
||||
if (!this.internalInstance) {
|
||||
this.internalInstance = new RoomListLayoutStore();
|
||||
this.internalInstance.start();
|
||||
}
|
||||
return RoomListLayoutStore.internalInstance;
|
||||
}
|
||||
|
||||
@@ -643,11 +643,9 @@ export default class RoomListStore {
|
||||
if (SettingsStore.getValue("feature_sliding_sync")) {
|
||||
logger.info("using SlidingRoomListStoreClass");
|
||||
const instance = new SlidingRoomListStoreClass(defaultDispatcher, SdkContextClass.instance);
|
||||
instance.start();
|
||||
RoomListStore.internalInstance = instance;
|
||||
} else {
|
||||
const instance = new RoomListStoreClass(defaultDispatcher);
|
||||
instance.start();
|
||||
RoomListStore.internalInstance = instance;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -17,6 +17,7 @@ import {
|
||||
MatrixEvent,
|
||||
ClientEvent,
|
||||
ISendEventResponse,
|
||||
MatrixClient,
|
||||
} from "matrix-js-sdk/src/matrix";
|
||||
import { KnownMembership } from "matrix-js-sdk/src/types";
|
||||
import { logger } from "matrix-js-sdk/src/logger";
|
||||
@@ -1397,7 +1398,6 @@ export class SpaceStoreClass extends AsyncStoreWithClient<IState> {
|
||||
export default class SpaceStore {
|
||||
private static readonly internalInstance = (() => {
|
||||
const instance = new SpaceStoreClass();
|
||||
instance.start();
|
||||
return instance;
|
||||
})();
|
||||
|
||||
@@ -1408,9 +1408,9 @@ export default class SpaceStore {
|
||||
/**
|
||||
* @internal for test only
|
||||
*/
|
||||
public static testInstance(): SpaceStoreClass {
|
||||
public static testInstance(client: MatrixClient): SpaceStoreClass {
|
||||
const store = new SpaceStoreClass();
|
||||
store.start();
|
||||
store.useUnitTestClient(client);
|
||||
return store;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -60,7 +60,6 @@ export class WidgetLayoutStore extends ReadyWatchingStore {
|
||||
public static get instance(): WidgetLayoutStore {
|
||||
if (!this.internalInstance) {
|
||||
this.internalInstance = new WidgetLayoutStore();
|
||||
this.internalInstance.start();
|
||||
}
|
||||
return this.internalInstance;
|
||||
}
|
||||
|
||||
@@ -27,7 +27,6 @@ export enum WidgetMessagingStoreEvent {
|
||||
export class WidgetMessagingStore extends AsyncStoreWithClient<{}> {
|
||||
private static readonly internalInstance = (() => {
|
||||
const instance = new WidgetMessagingStore();
|
||||
instance.start();
|
||||
return instance;
|
||||
})();
|
||||
|
||||
|
||||
@@ -38,7 +38,9 @@ export function presentableTextForFile(
|
||||
shortened = false,
|
||||
): string {
|
||||
let text = fallbackText;
|
||||
if (content.body?.length) {
|
||||
if (content.filename?.length) {
|
||||
text = content.filename;
|
||||
} else if (content.body?.length) {
|
||||
// The content body should be the name of the file including a
|
||||
// file extension.
|
||||
text = content.body;
|
||||
|
||||
@@ -8,7 +8,10 @@ Please see LICENSE files in the repository root for full details.
|
||||
|
||||
import React from "react";
|
||||
import { render, RenderResult } from "@testing-library/react";
|
||||
import { MatrixClient, MatrixEvent, Room } from "matrix-js-sdk/src/matrix";
|
||||
import { MatrixClient, MatrixEvent, EventType, Room, MsgType } from "matrix-js-sdk/src/matrix";
|
||||
import fetchMock from "fetch-mock-jest";
|
||||
import fs from "fs";
|
||||
import path from "path";
|
||||
|
||||
import SettingsStore from "../../../../src/settings/SettingsStore";
|
||||
import { VoiceBroadcastInfoEventType, VoiceBroadcastInfoState } from "../../../../src/voice-broadcast";
|
||||
@@ -25,6 +28,26 @@ jest.mock("../../../../src/voice-broadcast/components/VoiceBroadcastBody", () =>
|
||||
VoiceBroadcastBody: () => <div data-testid="voice-broadcast-body" />,
|
||||
}));
|
||||
|
||||
jest.mock("../../../../src/components/views/messages/MImageBody", () => ({
|
||||
__esModule: true,
|
||||
default: () => <div data-testid="image-body" />,
|
||||
}));
|
||||
|
||||
jest.mock("../../../../src/components/views/messages/MImageReplyBody", () => ({
|
||||
__esModule: true,
|
||||
default: () => <div data-testid="image-reply-body" />,
|
||||
}));
|
||||
|
||||
jest.mock("../../../../src/components/views/messages/MStickerBody", () => ({
|
||||
__esModule: true,
|
||||
default: () => <div data-testid="sticker-body" />,
|
||||
}));
|
||||
|
||||
jest.mock("../../../../src/components/views/messages/TextualBody.tsx", () => ({
|
||||
__esModule: true,
|
||||
default: () => <div data-testid="textual-body" />,
|
||||
}));
|
||||
|
||||
describe("MessageEvent", () => {
|
||||
let room: Room;
|
||||
let client: MatrixClient;
|
||||
@@ -68,4 +91,42 @@ describe("MessageEvent", () => {
|
||||
result.getByTestId("voice-broadcast-body");
|
||||
});
|
||||
});
|
||||
|
||||
describe("when an image with a caption is sent", () => {
|
||||
let result: RenderResult;
|
||||
|
||||
beforeEach(() => {
|
||||
event = mkEvent({
|
||||
event: true,
|
||||
type: EventType.RoomMessage,
|
||||
user: client.getUserId()!,
|
||||
room: room.roomId,
|
||||
content: {
|
||||
body: "caption for a test image",
|
||||
format: "org.matrix.custom.html",
|
||||
formatted_body: "<strong>caption for a test image</strong>",
|
||||
msgtype: MsgType.Image,
|
||||
filename: "image.webp",
|
||||
info: {
|
||||
w: 40,
|
||||
h: 50,
|
||||
},
|
||||
url: "mxc://server/image",
|
||||
},
|
||||
});
|
||||
result = renderMessageEvent();
|
||||
});
|
||||
|
||||
it("should render a TextualBody and an ImageBody", () => {
|
||||
fetchMock.getOnce(
|
||||
"https://server/_matrix/media/v3/download/server/image",
|
||||
{
|
||||
body: fs.readFileSync(path.resolve(__dirname, "..", "..", "..", "images", "animated-logo.webp")),
|
||||
},
|
||||
{ sendAsJson: false },
|
||||
);
|
||||
result.getByTestId("image-body");
|
||||
result.getByTestId("textual-body");
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
@@ -9,7 +9,7 @@ Please see LICENSE files in the repository root for full details.
|
||||
import React from "react";
|
||||
import { cleanup, render, screen, waitFor } from "@testing-library/react";
|
||||
import userEvent from "@testing-library/user-event";
|
||||
import { ActionState, ActionTypes, AllActionStates, FormattingFunctions } from "@matrix-org/matrix-wysiwyg";
|
||||
import { ActionState, ActionTypes, AllActionStates, FormattingFunctions } from "@vector-im/matrix-wysiwyg";
|
||||
|
||||
import { FormattingButtons } from "../../../../../../src/components/views/rooms/wysiwyg_composer/components/FormattingButtons";
|
||||
import * as LinkModal from "../../../../../../src/components/views/rooms/wysiwyg_composer/components/LinkModal";
|
||||
|
||||
@@ -6,7 +6,7 @@ SPDX-License-Identifier: AGPL-3.0-only OR GPL-3.0-only
|
||||
Please see LICENSE files in the repository root for full details.
|
||||
*/
|
||||
|
||||
import { FormattingFunctions } from "@matrix-org/matrix-wysiwyg";
|
||||
import { FormattingFunctions } from "@vector-im/matrix-wysiwyg";
|
||||
import { render, screen, waitFor } from "@testing-library/react";
|
||||
import React from "react";
|
||||
import userEvent from "@testing-library/user-event";
|
||||
|
||||
@@ -47,7 +47,7 @@ describe("AutoRageshakeStore", () => {
|
||||
|
||||
// @ts-ignore bypass private ctor for tests
|
||||
autoRageshakeStore = new AutoRageshakeStore();
|
||||
autoRageshakeStore.start();
|
||||
autoRageshakeStore.useUnitTestClient(client);
|
||||
|
||||
utdEvent = mkEvent({
|
||||
event: true,
|
||||
|
||||
@@ -40,7 +40,7 @@ describe("OwnProfileStore", () => {
|
||||
displayname: "Display Name",
|
||||
avatar_url: "mxc://example.com/abc123",
|
||||
});
|
||||
await ownProfileStore.start();
|
||||
await ownProfileStore.useUnitTestClient(client);
|
||||
|
||||
expect(onUpdate).toHaveBeenCalled();
|
||||
expect(ownProfileStore.displayName).toBe("Display Name");
|
||||
@@ -54,7 +54,7 @@ describe("OwnProfileStore", () => {
|
||||
errcode: "M_NOT_FOUND",
|
||||
}),
|
||||
);
|
||||
await ownProfileStore.start();
|
||||
await ownProfileStore.useUnitTestClient(client);
|
||||
|
||||
expect(onUpdate).toHaveBeenCalled();
|
||||
expect(ownProfileStore.displayName).toBe(client.getSafeUserId());
|
||||
@@ -69,7 +69,7 @@ describe("OwnProfileStore", () => {
|
||||
}),
|
||||
);
|
||||
try {
|
||||
await ownProfileStore.start();
|
||||
await ownProfileStore.useUnitTestClient(client);
|
||||
} catch (ignore) {}
|
||||
|
||||
expect(onUpdate).not.toHaveBeenCalled();
|
||||
|
||||
@@ -1428,7 +1428,7 @@ describe("SpaceStore", () => {
|
||||
|
||||
it("passes that value in calls to getVisibleRooms during getSpaceFilteredRoomIds", () => {
|
||||
// Given a store
|
||||
const store = SpaceStore.testInstance();
|
||||
const store = SpaceStore.testInstance(client);
|
||||
|
||||
// When we ask for filtered room ids
|
||||
store.getSpaceFilteredRoomIds(MetaSpace.Home);
|
||||
@@ -1478,7 +1478,7 @@ describe("SpaceStore", () => {
|
||||
|
||||
it("passes that value in calls to getVisibleRooms during getSpaceFilteredRoomIds", () => {
|
||||
// Given a store
|
||||
const store = SpaceStore.testInstance();
|
||||
const store = SpaceStore.testInstance(client);
|
||||
// When we ask for filtered room ids
|
||||
store.getSpaceFilteredRoomIds(MetaSpace.Home);
|
||||
|
||||
|
||||
@@ -31,7 +31,7 @@ describe("VoiceRecordingStore", () => {
|
||||
|
||||
const mkStore = (): VoiceRecordingStore => {
|
||||
const store = new VoiceRecordingStore();
|
||||
store.start();
|
||||
store.useUnitTestClient(stubClient);
|
||||
return store;
|
||||
};
|
||||
|
||||
|
||||
@@ -167,7 +167,7 @@ describe("WidgetLayoutStore", () => {
|
||||
|
||||
it("should recalculate all rooms when the client is ready", async () => {
|
||||
mocked(client.getVisibleRooms).mockReturnValue([mockRoom]);
|
||||
await store.start();
|
||||
await store.start(client);
|
||||
|
||||
expect(roomUpdateListener).toHaveBeenCalled();
|
||||
expect(store.getContainerWidgets(mockRoom, Container.Top)).toEqual([]);
|
||||
@@ -243,7 +243,7 @@ describe("WidgetLayoutStore", () => {
|
||||
});
|
||||
|
||||
it("should copy the layout to the room", async () => {
|
||||
await store.start();
|
||||
await store.start(client);
|
||||
store.recalculateRoom(mockRoom);
|
||||
store.moveToContainer(mockRoom, mockApps[0], Container.Top);
|
||||
store.copyLayoutToRoom(mockRoom);
|
||||
@@ -297,7 +297,7 @@ describe("WidgetLayoutStore", () => {
|
||||
mocked(client.getVisibleRooms).mockReturnValue([]);
|
||||
// @ts-ignore bypass private ctor for tests
|
||||
const store = new WidgetLayoutStore();
|
||||
await store.start();
|
||||
await store.start(client);
|
||||
expect(client.getVisibleRooms).toHaveBeenCalledWith(false);
|
||||
});
|
||||
});
|
||||
@@ -314,7 +314,7 @@ describe("WidgetLayoutStore", () => {
|
||||
mocked(client.getVisibleRooms).mockReturnValue([]);
|
||||
// @ts-ignore bypass private ctor for tests
|
||||
const store = new WidgetLayoutStore();
|
||||
await store.start();
|
||||
await store.start(client);
|
||||
expect(client.getVisibleRooms).toHaveBeenCalledWith(true);
|
||||
});
|
||||
});
|
||||
|
||||
@@ -80,7 +80,7 @@ describe("MessagePreviewStore", () => {
|
||||
mocked(client.getRoom).mockReturnValue(room);
|
||||
|
||||
store = MessagePreviewStore.testInstance();
|
||||
await store.start();
|
||||
await store.useUnitTestClient(client);
|
||||
await setupAsyncStoreWithClient(store, client);
|
||||
});
|
||||
|
||||
|
||||
@@ -29,6 +29,9 @@ import DMRoomMap from "../../../src/utils/DMRoomMap";
|
||||
import { flushPromises, stubClient, upsertRoomStateEvents, mkRoom } from "../../test-utils";
|
||||
import { DEFAULT_PUSH_RULES, makePushRule } from "../../test-utils/pushRules";
|
||||
|
||||
// Mock out the SpaceWatcher as it messes with the prefilterConditions
|
||||
jest.mock("../../../src/stores/room-list/SpaceWatcher.ts");
|
||||
|
||||
describe("RoomListStore", () => {
|
||||
const client = stubClient();
|
||||
const newRoomId = "!roomid:example.com";
|
||||
@@ -91,6 +94,10 @@ describe("RoomListStore", () => {
|
||||
await (RoomListStore.instance as RoomListStoreClass).makeReady(client);
|
||||
});
|
||||
|
||||
beforeEach(() => {
|
||||
DMRoomMap.makeShared(client);
|
||||
});
|
||||
|
||||
it.each(OrderedDefaultTagIDs)("defaults to importance ordering for %s=", (tagId) => {
|
||||
expect(RoomListStore.instance.getTagSorting(tagId)).toBe(SortAlgorithm.Recent);
|
||||
});
|
||||
@@ -102,11 +109,11 @@ describe("RoomListStore", () => {
|
||||
function createStore(): { store: RoomListStoreClass; handleRoomUpdate: jest.Mock<any, any> } {
|
||||
const fakeDispatcher = { register: jest.fn() } as unknown as MatrixDispatcher;
|
||||
const store = new RoomListStoreClass(fakeDispatcher);
|
||||
// @ts-ignore accessing private member to set client
|
||||
store.readyStore.matrixClient = client;
|
||||
const handleRoomUpdate = jest.fn();
|
||||
// @ts-ignore accessing private member to mock it
|
||||
store.algorithm.handleRoomUpdate = handleRoomUpdate;
|
||||
// @ts-ignore accessing private member to set client
|
||||
store.readyStore.useUnitTestClient(client);
|
||||
|
||||
return { store, handleRoomUpdate };
|
||||
}
|
||||
@@ -157,7 +164,6 @@ describe("RoomListStore", () => {
|
||||
room1.updateMyMembership(KnownMembership.Join);
|
||||
room2.updateMyMembership(KnownMembership.Join);
|
||||
room3.updateMyMembership(KnownMembership.Join);
|
||||
DMRoomMap.makeShared(client);
|
||||
const { store } = createStore();
|
||||
client.getVisibleRooms = jest.fn().mockReturnValue([room1, room2, room3]);
|
||||
|
||||
@@ -269,7 +275,6 @@ describe("RoomListStore", () => {
|
||||
|
||||
it("Passes the feature flag on to the client when asking for visible rooms", () => {
|
||||
// Given a store that we can ask for a room list
|
||||
DMRoomMap.makeShared(client);
|
||||
const { store } = createStore();
|
||||
client.getVisibleRooms = jest.fn().mockReturnValue([]);
|
||||
|
||||
@@ -285,7 +290,7 @@ describe("RoomListStore", () => {
|
||||
describe("room updates", () => {
|
||||
const makeStore = async () => {
|
||||
const store = new RoomListStoreClass(defaultDispatcher);
|
||||
await store.start();
|
||||
await store.useUnitTestClient(client);
|
||||
return store;
|
||||
};
|
||||
|
||||
|
||||
@@ -65,7 +65,7 @@ describe("SlidingRoomListStore", () => {
|
||||
|
||||
describe("spaces", () => {
|
||||
it("alters 'filters.spaces' on the DefaultTagID.Untagged list when the selected space changes", async () => {
|
||||
await store.start(); // call onReady
|
||||
await store.useUnitTestClient(context.client!); // call onReady
|
||||
const spaceRoomId = "!foo:bar";
|
||||
|
||||
const p = untilEmission(store, LISTS_LOADING_EVENT, (listName, isLoading) => {
|
||||
@@ -92,7 +92,7 @@ describe("SlidingRoomListStore", () => {
|
||||
},
|
||||
);
|
||||
activeSpace = MetaSpace.Home;
|
||||
await store.start(); // call onReady
|
||||
await store.useUnitTestClient(context.client!); // call onReady
|
||||
|
||||
expect(context._SlidingSyncManager!.ensureListRegistered).toHaveBeenCalledWith(DefaultTagID.Untagged, {
|
||||
filters: expect.objectContaining({
|
||||
@@ -108,7 +108,7 @@ describe("SlidingRoomListStore", () => {
|
||||
const p = untilEmission(store, LISTS_LOADING_EVENT, (listName, isLoading) => {
|
||||
return listName === DefaultTagID.Untagged && !isLoading;
|
||||
});
|
||||
await store.start(); // call onReady
|
||||
await store.useUnitTestClient(context.client!); // call onReady
|
||||
await p;
|
||||
expect(context._SlidingSyncManager!.ensureListRegistered).toHaveBeenCalledWith(
|
||||
DefaultTagID.Untagged,
|
||||
@@ -121,7 +121,7 @@ describe("SlidingRoomListStore", () => {
|
||||
});
|
||||
|
||||
it("includes subspaces in 'filters.spaces' when the selected space has subspaces", async () => {
|
||||
await store.start(); // call onReady
|
||||
await store.useUnitTestClient(context.client!); // call onReady
|
||||
const spaceRoomId = "!foo:bar";
|
||||
const subSpace1 = "!ss1:bar";
|
||||
const subSpace2 = "!ss2:bar";
|
||||
@@ -168,7 +168,7 @@ describe("SlidingRoomListStore", () => {
|
||||
});
|
||||
|
||||
it("getTagsForRoom gets the tags for the room", async () => {
|
||||
await store.start();
|
||||
await store.useUnitTestClient(context.client!);
|
||||
const roomA = "!a:localhost";
|
||||
const roomB = "!b:localhost";
|
||||
const keyToListData: Record<string, { joinedCount: number; roomIndexToRoomId: Record<number, string> }> = {
|
||||
@@ -200,7 +200,7 @@ describe("SlidingRoomListStore", () => {
|
||||
});
|
||||
|
||||
it("emits LISTS_UPDATE_EVENT when slidingSync lists update", async () => {
|
||||
await store.start();
|
||||
await store.useUnitTestClient(context.client!);
|
||||
const roomA = "!a:localhost";
|
||||
const roomB = "!b:localhost";
|
||||
const roomC = "!c:localhost";
|
||||
@@ -236,7 +236,7 @@ describe("SlidingRoomListStore", () => {
|
||||
});
|
||||
|
||||
it("sets the sticky room on the basis of the viewed room in RoomViewStore", async () => {
|
||||
await store.start();
|
||||
await store.useUnitTestClient(context.client!);
|
||||
// seed the store with 3 rooms
|
||||
const roomIdA = "!a:localhost";
|
||||
const roomIdB = "!b:localhost";
|
||||
@@ -301,7 +301,7 @@ describe("SlidingRoomListStore", () => {
|
||||
});
|
||||
|
||||
it("gracefully handles unknown room IDs", async () => {
|
||||
await store.start();
|
||||
await store.useUnitTestClient(context.client!);
|
||||
const roomIdA = "!a:localhost";
|
||||
const roomIdB = "!b:localhost"; // does not exist
|
||||
const roomIdC = "!c:localhost";
|
||||
|
||||
@@ -1911,13 +1911,6 @@
|
||||
resolved "https://registry.yarnpkg.com/@matrix-org/matrix-sdk-crypto-wasm/-/matrix-sdk-crypto-wasm-7.0.0.tgz#8d6abdb9ded8656cc9e2a7909913a34bf3fc9b3a"
|
||||
integrity sha512-MOencXiW/gI5MuTtCNsuojjwT5DXCrjMqv9xOslJC9h2tPdLFFFMGr58dY5Lis4DRd9MRWcgrGowUIHOqieWTA==
|
||||
|
||||
"@matrix-org/matrix-wysiwyg@2.37.9":
|
||||
version "2.37.9"
|
||||
resolved "https://registry.yarnpkg.com/@matrix-org/matrix-wysiwyg/-/matrix-wysiwyg-2.37.9.tgz#4d5667df3c74e11fd01c4b5be920caff72bf2f48"
|
||||
integrity sha512-Jn2ug6ySX5es5/SV5BVgSBhVPS7GXggwZ/GGWmnJvhnp/IArit4kgUAsNRhQeKebuVvNphwQykCGwyBOn2PLBA==
|
||||
dependencies:
|
||||
eslint-plugin-unicorn "^54.0.0"
|
||||
|
||||
"@matrix-org/olm@3.2.15":
|
||||
version "3.2.15"
|
||||
resolved "https://registry.yarnpkg.com/@matrix-org/olm/-/olm-3.2.15.tgz#55f3c1b70a21bbee3f9195cecd6846b1083451ec"
|
||||
@@ -3101,6 +3094,13 @@
|
||||
ts-xor "^1.3.0"
|
||||
vaul "^0.7.0"
|
||||
|
||||
"@vector-im/matrix-wysiwyg@2.37.13":
|
||||
version "2.37.13"
|
||||
resolved "https://registry.yarnpkg.com/@vector-im/matrix-wysiwyg/-/matrix-wysiwyg-2.37.13.tgz#7b64e7652a196e811c0470dcbff8d36e2229cd46"
|
||||
integrity sha512-3ilnJBJSzeTfAQXNJJ29EAqa14exrkca3acTPssdoh3WhTKwXyQ0xU8sS50d6gqlAP/Nahw1y2k+PzajXo1SsQ==
|
||||
dependencies:
|
||||
eslint-plugin-unicorn "^54.0.0"
|
||||
|
||||
"@zxcvbn-ts/core@^3.0.4":
|
||||
version "3.0.4"
|
||||
resolved "https://registry.yarnpkg.com/@zxcvbn-ts/core/-/core-3.0.4.tgz#c5bde72235eb6c273cec78b672bb47c0d7045cad"
|
||||
|
||||
Reference in New Issue
Block a user