Pass around MatrixClients instead of using MatrixClientPeg (#10984)

Co-authored-by: Richard van der Hoff <1389908+richvdh@users.noreply.github.com>
This commit is contained in:
Michael Telatynski 2023-06-01 14:43:24 +01:00 committed by GitHub
parent b6b9ce3c46
commit 21ffc50f1e
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
23 changed files with 152 additions and 135 deletions

View file

@ -17,11 +17,10 @@ limitations under the License.
import { MatrixEvent } from "matrix-js-sdk/src/models/event";
import { logger } from "matrix-js-sdk/src/logger";
import { CryptoEvent } from "matrix-js-sdk/src/crypto";
import { ClientEvent, EventType, RoomStateEvent } from "matrix-js-sdk/src/matrix";
import { ClientEvent, EventType, MatrixClient, RoomStateEvent } from "matrix-js-sdk/src/matrix";
import { SyncState } from "matrix-js-sdk/src/sync";
import { IKeyBackupInfo } from "matrix-js-sdk/src/crypto/keybackup";
import { MatrixClientPeg } from "./MatrixClientPeg";
import dis from "./dispatcher/dispatcher";
import {
hideToast as hideBulkUnverifiedSessionsToast,
@ -67,6 +66,8 @@ export default class DeviceListener {
// The set of device IDs we're currently displaying toasts for
private displayingToastsForDeviceIds = new Set<string>();
private running = false;
// The client with which the instance is running. Only set if `running` is true, otherwise undefined.
private client?: MatrixClient;
private shouldRecordClientInformation = false;
private enableBulkUnverifiedSessionsReminder = true;
private deviceClientInformationSettingWatcherRef: string | undefined;
@ -76,16 +77,17 @@ export default class DeviceListener {
return window.mxDeviceListener;
}
public start(): void {
public start(matrixClient: MatrixClient): void {
this.running = true;
MatrixClientPeg.get().on(CryptoEvent.WillUpdateDevices, this.onWillUpdateDevices);
MatrixClientPeg.get().on(CryptoEvent.DevicesUpdated, this.onDevicesUpdated);
MatrixClientPeg.get().on(CryptoEvent.DeviceVerificationChanged, this.onDeviceVerificationChanged);
MatrixClientPeg.get().on(CryptoEvent.UserTrustStatusChanged, this.onUserTrustStatusChanged);
MatrixClientPeg.get().on(CryptoEvent.KeysChanged, this.onCrossSingingKeysChanged);
MatrixClientPeg.get().on(ClientEvent.AccountData, this.onAccountData);
MatrixClientPeg.get().on(ClientEvent.Sync, this.onSync);
MatrixClientPeg.get().on(RoomStateEvent.Events, this.onRoomStateEvents);
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);
this.client.on(ClientEvent.Sync, this.onSync);
this.client.on(RoomStateEvent.Events, this.onRoomStateEvents);
this.shouldRecordClientInformation = SettingsStore.getValue("deviceClientInformationOptIn");
// only configurable in config, so we don't need to watch the value
this.enableBulkUnverifiedSessionsReminder = SettingsStore.getValue(UIFeature.BulkUnverifiedSessionsReminder);
@ -101,18 +103,15 @@ export default class DeviceListener {
public stop(): void {
this.running = false;
if (MatrixClientPeg.get()) {
MatrixClientPeg.get().removeListener(CryptoEvent.WillUpdateDevices, this.onWillUpdateDevices);
MatrixClientPeg.get().removeListener(CryptoEvent.DevicesUpdated, this.onDevicesUpdated);
MatrixClientPeg.get().removeListener(
CryptoEvent.DeviceVerificationChanged,
this.onDeviceVerificationChanged,
);
MatrixClientPeg.get().removeListener(CryptoEvent.UserTrustStatusChanged, this.onUserTrustStatusChanged);
MatrixClientPeg.get().removeListener(CryptoEvent.KeysChanged, this.onCrossSingingKeysChanged);
MatrixClientPeg.get().removeListener(ClientEvent.AccountData, this.onAccountData);
MatrixClientPeg.get().removeListener(ClientEvent.Sync, this.onSync);
MatrixClientPeg.get().removeListener(RoomStateEvent.Events, this.onRoomStateEvents);
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);
this.client.removeListener(ClientEvent.Sync, this.onSync);
this.client.removeListener(RoomStateEvent.Events, this.onRoomStateEvents);
}
if (this.deviceClientInformationSettingWatcherRef) {
SettingsStore.unwatchSetting(this.deviceClientInformationSettingWatcherRef);
@ -128,6 +127,7 @@ export default class DeviceListener {
this.keyBackupStatusChecked = false;
this.ourDeviceIdsAtStart = null;
this.displayingToastsForDeviceIds = new Set();
this.client = undefined;
}
/**
@ -160,22 +160,23 @@ export default class DeviceListener {
* @returns the set of device IDs
*/
private async getDeviceIds(): Promise<Set<string>> {
const cli = MatrixClientPeg.get();
const crypto = cli.getCrypto();
const cli = this.client;
const crypto = cli?.getCrypto();
if (crypto === undefined) return new Set();
const userId = cli.getSafeUserId();
const userId = cli!.getSafeUserId();
const devices = await crypto.getUserDeviceInfo([userId]);
return new Set(devices.get(userId)?.keys() ?? []);
}
private onWillUpdateDevices = 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
// devicesAtStart list to the devices that we see after the fetch.
if (initialFetch) return;
const myUserId = MatrixClientPeg.get().getUserId()!;
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
@ -183,17 +184,20 @@ export default class DeviceListener {
};
private onDevicesUpdated = (users: string[]): void => {
if (!users.includes(MatrixClientPeg.get().getUserId()!)) return;
if (!this.client) return;
if (!users.includes(this.client.getSafeUserId())) return;
this.recheck();
};
private onDeviceVerificationChanged = (userId: string): void => {
if (userId !== MatrixClientPeg.get().getUserId()) return;
if (!this.client) return;
if (userId !== this.client.getUserId()) return;
this.recheck();
};
private onUserTrustStatusChanged = (userId: string): void => {
if (userId !== MatrixClientPeg.get().getUserId()) return;
if (!this.client) return;
if (userId !== this.client.getUserId()) return;
this.recheck();
};
@ -239,13 +243,14 @@ export default class DeviceListener {
// The server doesn't tell us when key backup is set up, so we poll
// & cache the result
private async getKeyBackupInfo(): Promise<IKeyBackupInfo | null> {
if (!this.client) return null;
const now = new Date().getTime();
if (
!this.keyBackupInfo ||
!this.keyBackupFetchedAt ||
this.keyBackupFetchedAt < now - KEY_BACKUP_POLL_INTERVAL
) {
this.keyBackupInfo = await MatrixClientPeg.get().getKeyBackupVersion();
this.keyBackupInfo = await this.client.getKeyBackupVersion();
this.keyBackupFetchedAt = now;
}
return this.keyBackupInfo;
@ -256,13 +261,13 @@ export default class DeviceListener {
// modifying the state involved here, so don't add new toasts to setup.
if (isSecretStorageBeingAccessed()) return false;
// Show setup toasts once the user is in at least one encrypted room.
const cli = MatrixClientPeg.get();
return cli && cli.getRooms().some((r) => cli.isRoomEncrypted(r.roomId));
const cli = this.client;
return cli?.getRooms().some((r) => cli.isRoomEncrypted(r.roomId)) ?? false;
}
private async recheck(): Promise<void> {
if (!this.running) return; // we have been stopped
const cli = MatrixClientPeg.get();
if (!this.running || !this.client) return; // we have been stopped
const cli = this.client;
// cross-signing support was added to Matrix in MSC1756, which landed in spec v1.1
if (!(await cli.isVersionSupported("v1.1"))) return;
@ -285,11 +290,11 @@ export default class DeviceListener {
this.checkKeyBackupStatus();
} else if (this.shouldShowSetupEncryptionToast()) {
// make sure our keys are finished downloading
await crypto.getUserDeviceInfo([cli.getUserId()!]);
await crypto.getUserDeviceInfo([cli.getSafeUserId()]);
// cross signing isn't enabled - nag to enable it
// There are 3 different toasts for:
if (!(await crypto.getCrossSigningKeyId()) && cli.getStoredCrossSigningForUser(cli.getUserId()!)) {
if (!(await crypto.getCrossSigningKeyId()) && cli.getStoredCrossSigningForUser(cli.getSafeUserId())) {
// Cross-signing on account but this device doesn't trust the master key (verify this session)
showSetupEncryptionToast(SetupKind.VERIFY_THIS_SESSION);
this.checkKeyBackupStatus();
@ -327,7 +332,9 @@ export default class DeviceListener {
const isCurrentDeviceTrusted =
crossSigningReady &&
Boolean((await crypto.getDeviceVerificationStatus(cli.getUserId()!, cli.deviceId!))?.crossSigningVerified);
Boolean(
(await crypto.getDeviceVerificationStatus(cli.getSafeUserId(), cli.deviceId!))?.crossSigningVerified,
);
// as long as cross-signing isn't ready,
// you can't see or dismiss any device toasts
@ -336,7 +343,7 @@ export default class DeviceListener {
for (const deviceId of devices) {
if (deviceId === cli.deviceId) continue;
const deviceTrust = await crypto.getDeviceVerificationStatus(cli.getUserId()!, deviceId);
const deviceTrust = await crypto.getDeviceVerificationStatus(cli.getSafeUserId(), deviceId);
if (!deviceTrust?.crossSigningVerified && !this.dismissed.has(deviceId)) {
if (this.ourDeviceIdsAtStart?.has(deviceId)) {
oldUnverifiedDeviceIds.add(deviceId);
@ -383,11 +390,11 @@ export default class DeviceListener {
}
private checkKeyBackupStatus = async (): Promise<void> => {
if (this.keyBackupStatusChecked) {
if (this.keyBackupStatusChecked || !this.client) {
return;
}
// returns null when key backup status hasn't finished being checked
const isKeyBackupEnabled = MatrixClientPeg.get().getKeyBackupEnabled();
const isKeyBackupEnabled = this.client.getKeyBackupEnabled();
this.keyBackupStatusChecked = isKeyBackupEnabled !== null;
if (isKeyBackupEnabled === false) {
@ -412,11 +419,12 @@ export default class DeviceListener {
};
private updateClientInformation = async (): Promise<void> => {
if (!this.client) return;
try {
if (this.shouldRecordClientInformation) {
await recordClientInformation(MatrixClientPeg.get(), SdkConfig.get(), PlatformPeg.get() ?? undefined);
await recordClientInformation(this.client, SdkConfig.get(), PlatformPeg.get() ?? undefined);
} else {
await removeClientInformation(MatrixClientPeg.get());
await removeClientInformation(this.client);
}
} catch (error) {
// this is a best effort operation

View file

@ -129,7 +129,7 @@ export default class IdentityAuthClient {
} catch (e) {
if (e instanceof MatrixError && e.errcode === "M_TERMS_NOT_SIGNED") {
logger.log("Identity server requires new terms to be agreed to");
await startTermsFlow([new Service(SERVICE_TYPES.IS, identityServerUrl, token)]);
await startTermsFlow(this.matrixClient, [new Service(SERVICE_TYPES.IS, identityServerUrl, token)]);
return;
}
throw e;

View file

@ -553,7 +553,7 @@ export async function hydrateSession(credentials: IMatrixClientCreds): Promise<M
logger.warn("Clearing all data: Old session belongs to a different user/session");
}
if (!credentials.pickleKey) {
if (!credentials.pickleKey && credentials.deviceId !== undefined) {
logger.info("Lifecycle#hydrateSession: Pickle key not provided - trying to get one");
credentials.pickleKey =
(await PlatformPeg.get()?.getPickleKey(credentials.userId, credentials.deviceId)) ?? undefined;
@ -603,14 +603,14 @@ async function doSetLoggedIn(credentials: IMatrixClientCreds, clearStorageEnable
}
MatrixClientPeg.replaceUsingCreds(credentials);
const client = MatrixClientPeg.get();
setSentryUser(credentials.userId);
if (PosthogAnalytics.instance.isEnabled()) {
PosthogAnalytics.instance.startListeningToSettingsChanges();
PosthogAnalytics.instance.startListeningToSettingsChanges(client);
}
const client = MatrixClientPeg.get();
if (credentials.freshLogin && SettingsStore.getValue("feature_dehydration")) {
// If we just logged in, try to rehydrate a device instead of using a
// new device. If it succeeds, we'll get a new device ID, so make sure
@ -825,7 +825,7 @@ async function startMatrixClient(client: MatrixClient, startSyncing = true): Pro
SettingsStore.runMigrations();
// This needs to be started after crypto is set up
DeviceListener.sharedInstance().start();
DeviceListener.sharedInstance().start(client);
// Similarly, don't start sending presence updates until we've started
// the client
if (!SettingsStore.getValue("lowBandwidth")) {

View file

@ -15,8 +15,8 @@ limitations under the License.
*/
import { ClientWidgetApi } from "matrix-widget-api";
import { MatrixClient } from "matrix-js-sdk/src/matrix";
import { MatrixClientPeg } from "./MatrixClientPeg";
import SdkConfig from "./SdkConfig";
import { ElementWidgetActions } from "./stores/widgets/ElementWidgetActions";
@ -27,8 +27,8 @@ export function getConfigLivestreamUrl(): string | undefined {
// Dummy rtmp URL used to signal that we want a special audio-only stream
const AUDIOSTREAM_DUMMY_URL = "rtmp://audiostream.dummy/";
async function createLiveStream(roomId: string): Promise<void> {
const openIdToken = await MatrixClientPeg.get().getOpenIdToken();
async function createLiveStream(matrixClient: MatrixClient, roomId: string): Promise<void> {
const openIdToken = await matrixClient.getOpenIdToken();
const url = getConfigLivestreamUrl() + "/createStream";
@ -47,8 +47,12 @@ async function createLiveStream(roomId: string): Promise<void> {
return respBody["stream_id"];
}
export async function startJitsiAudioLivestream(widgetMessaging: ClientWidgetApi, roomId: string): Promise<void> {
const streamId = await createLiveStream(roomId);
export async function startJitsiAudioLivestream(
matrixClient: MatrixClient,
widgetMessaging: ClientWidgetApi,
roomId: string,
): Promise<void> {
const streamId = await createLiveStream(matrixClient, roomId);
await widgetMessaging.transport.send(ElementWidgetActions.StartLiveStream, {
rtmpStreamKey: AUDIOSTREAM_DUMMY_URL + streamId,

View file

@ -254,7 +254,7 @@ export class PosthogAnalytics {
};
}
// eslint-disable-nextline no-unused-varsx
// eslint-disable-nextline no-unused-vars
private capture(eventName: string, properties: Properties, options?: IPostHogEventOptions): void {
if (!this.enabled) {
return;
@ -375,12 +375,12 @@ export class PosthogAnalytics {
this.registerSuperProperties(this.platformSuperProperties);
}
public async updateAnonymityFromSettings(pseudonymousOptIn: boolean): Promise<void> {
public async updateAnonymityFromSettings(client: MatrixClient, pseudonymousOptIn: boolean): Promise<void> {
// Update this.anonymity based on the user's analytics opt-in settings
const anonymity = pseudonymousOptIn ? Anonymity.Pseudonymous : Anonymity.Disabled;
this.setAnonymity(anonymity);
if (anonymity === Anonymity.Pseudonymous) {
await this.identifyUser(MatrixClientPeg.get(), PosthogAnalytics.getRandomAnalyticsId);
await this.identifyUser(client, PosthogAnalytics.getRandomAnalyticsId);
if (MatrixClientPeg.currentUserIsJustRegistered()) {
this.trackNewUserEvent();
}
@ -391,7 +391,7 @@ export class PosthogAnalytics {
}
}
public startListeningToSettingsChanges(): void {
public startListeningToSettingsChanges(client: MatrixClient): void {
// Listen to account data changes from sync so we can observe changes to relevant flags and update.
// This is called -
// * On page load, when the account data is first received by sync
@ -404,7 +404,7 @@ export class PosthogAnalytics {
"pseudonymousAnalyticsOptIn",
null,
(originalSettingName, changedInRoomId, atLevel, newValueAtLevel, newValue) => {
this.updateAnonymityFromSettings(!!newValue);
this.updateAnonymityFromSettings(client, !!newValue);
},
);
}

View file

@ -21,7 +21,6 @@ import { ConditionKind, PushRuleActionName, PushRuleKind, TweakName } from "matr
import type { IPushRule } from "matrix-js-sdk/src/@types/PushRules";
import type { Room } from "matrix-js-sdk/src/models/room";
import type { MatrixClient } from "matrix-js-sdk/src/matrix";
import { MatrixClientPeg } from "./MatrixClientPeg";
import { NotificationColor } from "./stores/notifications/NotificationColor";
import { getUnsentMessages } from "./components/structures/RoomStatusBar";
import { doesRoomHaveUnreadMessages, doesRoomOrThreadHaveUnreadMessages } from "./Unread";
@ -40,7 +39,7 @@ export function getRoomNotifsState(client: MatrixClient, roomId: string): RoomNo
// look through the override rules for a rule affecting this room:
// if one exists, it will take precedence.
const muteRule = findOverrideMuteRule(roomId);
const muteRule = findOverrideMuteRule(client, roomId);
if (muteRule) {
return RoomNotifState.Mute;
}
@ -70,11 +69,11 @@ export function getRoomNotifsState(client: MatrixClient, roomId: string): RoomNo
return null;
}
export function setRoomNotifsState(roomId: string, newState: RoomNotifState): Promise<void> {
export function setRoomNotifsState(client: MatrixClient, roomId: string, newState: RoomNotifState): Promise<void> {
if (newState === RoomNotifState.Mute) {
return setRoomNotifsStateMuted(roomId);
return setRoomNotifsStateMuted(client, roomId);
} else {
return setRoomNotifsStateUnmuted(roomId, newState);
return setRoomNotifsStateUnmuted(client, roomId, newState);
}
}
@ -91,7 +90,7 @@ export function getUnreadNotificationCount(room: Room, type: NotificationCountTy
// Exclude threadId, as the same thread can't continue over a room upgrade
if (!threadId && predecessor?.roomId) {
const oldRoomId = predecessor.roomId;
const oldRoom = MatrixClientPeg.get().getRoom(oldRoomId);
const oldRoom = room.client.getRoom(oldRoomId);
if (oldRoom) {
// We only ever care if there's highlights in the old room. No point in
// notifying the user for unread messages because they would have extreme
@ -104,8 +103,7 @@ export function getUnreadNotificationCount(room: Room, type: NotificationCountTy
return notificationCount;
}
function setRoomNotifsStateMuted(roomId: string): Promise<any> {
const cli = MatrixClientPeg.get();
function setRoomNotifsStateMuted(cli: MatrixClient, roomId: string): Promise<any> {
const promises: Promise<unknown>[] = [];
// delete the room rule
@ -135,11 +133,10 @@ function setRoomNotifsStateMuted(roomId: string): Promise<any> {
return Promise.all(promises);
}
function setRoomNotifsStateUnmuted(roomId: string, newState: RoomNotifState): Promise<any> {
const cli = MatrixClientPeg.get();
function setRoomNotifsStateUnmuted(cli: MatrixClient, roomId: string, newState: RoomNotifState): Promise<any> {
const promises: Promise<unknown>[] = [];
const overrideMuteRule = findOverrideMuteRule(roomId);
const overrideMuteRule = findOverrideMuteRule(cli, roomId);
if (overrideMuteRule) {
promises.push(cli.deletePushRule("global", PushRuleKind.Override, overrideMuteRule.rule_id));
}
@ -176,8 +173,7 @@ function setRoomNotifsStateUnmuted(roomId: string, newState: RoomNotifState): Pr
return Promise.all(promises);
}
function findOverrideMuteRule(roomId: string): IPushRule | null {
const cli = MatrixClientPeg.get();
function findOverrideMuteRule(cli: MatrixClient | undefined, roomId: string): IPushRule | null {
if (!cli?.pushRules?.global?.override) {
return null;
}

View file

@ -17,8 +17,8 @@ limitations under the License.
import { Room } from "matrix-js-sdk/src/models/room";
import { EventType } from "matrix-js-sdk/src/@types/event";
import { RoomMember } from "matrix-js-sdk/src/models/room-member";
import { MatrixClient } from "matrix-js-sdk/src/matrix";
import { MatrixClientPeg } from "./MatrixClientPeg";
import AliasCustomisations from "./customisations/Alias";
/**
@ -52,20 +52,21 @@ export function guessAndSetDMRoom(room: Room, isDirect: boolean): Promise<void>
newTarget = null;
}
return setDMRoom(room.roomId, newTarget);
return setDMRoom(room.client, room.roomId, newTarget);
}
/**
* Marks or unmarks the given room as being as a DM room.
* @param client the Matrix Client instance of the logged-in user
* @param {string} roomId The ID of the room to modify
* @param {string | null} userId The user ID of the desired DM room target user or
* null to un-mark this room as a DM room
* @returns {object} A promise
*/
export async function setDMRoom(roomId: string, userId: string | null): Promise<void> {
if (MatrixClientPeg.get().isGuest()) return;
export async function setDMRoom(client: MatrixClient, roomId: string, userId: string | null): Promise<void> {
if (client.isGuest()) return;
const mDirectEvent = MatrixClientPeg.get().getAccountData(EventType.Direct);
const mDirectEvent = client.getAccountData(EventType.Direct);
const currentContent = mDirectEvent?.getContent() || {};
const dmRoomMap = new Map(Object.entries(currentContent));
@ -98,7 +99,7 @@ export async function setDMRoom(roomId: string, userId: string | null): Promise<
// prevent unnecessary calls to setAccountData
if (!modified) return;
await MatrixClientPeg.get().setAccountData(EventType.Direct, Object.fromEntries(dmRoomMap));
await client.setAccountData(EventType.Direct, Object.fromEntries(dmRoomMap));
}
/**

View file

@ -157,6 +157,7 @@ export default class ScalarAuthClient {
const parsedImRestUrl = parseUrl(this.apiUrl);
parsedImRestUrl.pathname = "";
return startTermsFlow(
MatrixClientPeg.get(),
[new Service(SERVICE_TYPES.IM, parsedImRestUrl.toString(), token)],
this.termsInteractionCallback,
).then(() => {

View file

@ -876,7 +876,7 @@ const onMessage = function (event: MessageEvent<any>): void {
// No integrations UI URL, ignore silently.
return;
}
let eventOriginUrl;
let eventOriginUrl: URL;
try {
eventOriginUrl = new URL(event.origin);
} catch (e) {

View file

@ -17,8 +17,8 @@ limitations under the License.
import classNames from "classnames";
import { SERVICE_TYPES } from "matrix-js-sdk/src/service-types";
import { logger } from "matrix-js-sdk/src/logger";
import { MatrixClient } from "matrix-js-sdk/src/matrix";
import { MatrixClientPeg } from "./MatrixClientPeg";
import Modal from "./Modal";
import TermsDialog from "./components/views/dialogs/TermsDialog";
@ -66,6 +66,7 @@ export type TermsInteractionCallback = (
/**
* Start a flow where the user is presented with terms & conditions for some services
*
* @param client The Matrix Client instance of the logged-in user
* @param {Service[]} services Object with keys 'serviceType', 'baseUrl', 'accessToken'
* @param {function} interactionCallback Function called with:
* * an array of { service: {Service}, policies: {terms response from API} }
@ -75,10 +76,11 @@ export type TermsInteractionCallback = (
* if they cancel.
*/
export async function startTermsFlow(
client: MatrixClient,
services: Service[],
interactionCallback: TermsInteractionCallback = dialogTermsInteractionCallback,
): Promise<void> {
const termsPromises = services.map((s) => MatrixClientPeg.get().getTerms(s.serviceType, s.baseUrl));
const termsPromises = services.map((s) => client.getTerms(s.serviceType, s.baseUrl));
/*
* a /terms response looks like:
@ -105,7 +107,7 @@ export async function startTermsFlow(
});
// fetch the set of agreed policy URLs from account data
const currentAcceptedTerms = await MatrixClientPeg.get().getAccountData("m.accepted_terms");
const currentAcceptedTerms = await client.getAccountData("m.accepted_terms");
let agreedUrlSet: Set<string>;
if (!currentAcceptedTerms || !currentAcceptedTerms.getContent() || !currentAcceptedTerms.getContent().accepted) {
agreedUrlSet = new Set();
@ -152,7 +154,7 @@ export async function startTermsFlow(
// We only ever add to the set of URLs, so if anything has changed then we'd see a different length
if (agreedUrlSet.size !== numAcceptedBeforeAgreement) {
const newAcceptedTerms = { accepted: Array.from(agreedUrlSet) };
await MatrixClientPeg.get().setAccountData("m.accepted_terms", newAcceptedTerms);
await client.setAccountData("m.accepted_terms", newAcceptedTerms);
}
const agreePromises = policiesAndServicePairs.map((policiesAndService) => {
@ -171,7 +173,7 @@ export async function startTermsFlow(
if (urlsForService.length === 0) return Promise.resolve();
return MatrixClientPeg.get().agreeToTerms(
return client.agreeToTerms(
policiesAndService.service.serviceType,
policiesAndService.service.baseUrl,
policiesAndService.service.accessToken,

View file

@ -1512,7 +1512,7 @@ export class RoomView extends React.Component<IRoomProps, IRoomState> {
}
const dmInviter = room.getDMInviter();
if (dmInviter) {
Rooms.setDMRoom(room.roomId, dmInviter);
Rooms.setDMRoom(room.client, room.roomId, dmInviter);
}
}

View file

@ -67,7 +67,7 @@ export const WidgetContextMenu: React.FC<IProps> = ({
if (roomId && getConfigLivestreamUrl() && WidgetType.JITSI.matches(app.type)) {
const onStreamAudioClick = async (): Promise<void> => {
try {
await startJitsiAudioLivestream(widgetMessaging!, roomId);
await startJitsiAudioLivestream(cli, widgetMessaging!, roomId);
} catch (err) {
logger.error("Failed to start livestream", err);
// XXX: won't i18n well, but looks like widget api only support 'message'?

View file

@ -191,7 +191,7 @@ export function DeviceItem({ userId, device }: { userId: string; device: IDevice
const onDeviceClick = (): void => {
const user = cli.getUser(userId);
if (user) {
verifyDevice(user, device);
verifyDevice(cli, user, device);
}
};
@ -1446,9 +1446,9 @@ const BasicUserInfo: React.FC<{
className="mx_UserInfo_field mx_UserInfo_verifyButton"
onClick={() => {
if (hasCrossSigningKeys) {
verifyUser(member as User);
verifyUser(cli, member as User);
} else {
legacyVerifyUser(member as User);
legacyVerifyUser(cli, member as User);
}
}}
>

View file

@ -61,6 +61,7 @@ import SettingsSubsection, { SettingsSubsectionText } from "../../shared/Setting
import { SettingsSubsectionHeading } from "../../shared/SettingsSubsectionHeading";
import Heading from "../../../typography/Heading";
import InlineSpinner from "../../../elements/InlineSpinner";
import MatrixClientContext from "../../../../../contexts/MatrixClientContext";
interface IProps {
closeSettingsFn: () => void;
@ -96,6 +97,9 @@ interface IState {
}
export default class GeneralUserSettingsTab extends React.Component<IProps, IState> {
public static contextType = MatrixClientContext;
public context!: React.ContextType<typeof MatrixClientContext>;
private readonly dispatcherRef: string;
public constructor(props: IProps) {
@ -217,6 +221,7 @@ export default class GeneralUserSettingsTab extends React.Component<IProps, ISta
try {
const idAccessToken = await authClient.getAccessToken({ check: false });
await startTermsFlow(
this.context,
[new Service(SERVICE_TYPES.IS, idServerUrl, idAccessToken!)],
(policiesAndServices, agreedUrls, extraClassNames) => {
return new Promise((resolve, reject) => {

View file

@ -321,7 +321,7 @@ export default async function createRoom(client: MatrixClient, opts: IOpts): Pro
}
});
if (opts.dmUserId) await Rooms.setDMRoom(roomId, opts.dmUserId);
if (opts.dmUserId) await Rooms.setDMRoom(client, roomId, opts.dmUserId);
})
.then(() => {
if (opts.parentSpace) {

View file

@ -189,7 +189,7 @@ async function collectBugReport(opts: IOpts = {}, gzipLogs = true): Promise<Form
// compress
if (gzipLogs) {
buf = pako.gzip(buf);
buf = pako!.gzip(buf);
}
body.append("compressed-log", new Blob([buf]), entry.id);

View file

@ -78,7 +78,7 @@ export class RoomEchoChamber extends GenericEchoChamber<RoomEchoContext, CachedR
CachedRoomKey.NotificationVolume,
v,
async (): Promise<void> => {
return setRoomNotifsState(this.context.room.roomId, v);
return setRoomNotifsState(this.context.room.client, this.context.room.roomId, v);
},
implicitlyReverted,
);

View file

@ -35,6 +35,7 @@ import { ActionPayload } from "../../dispatcher/payloads";
import { Action } from "../../dispatcher/actions";
import { ActiveRoomChangedPayload } from "../../dispatcher/payloads/ActiveRoomChangedPayload";
import { SdkContextClass } from "../../contexts/SDKContext";
import { MatrixClientPeg } from "../../MatrixClientPeg";
/**
* A class for tracking the state of the right panel between layouts and
@ -308,7 +309,9 @@ export default class RightPanelStore extends ReadyWatchingStore {
if (card.phase === RightPanelPhases.RoomMemberInfo && card.state) {
// RightPanelPhases.RoomMemberInfo -> needs to be changed to RightPanelPhases.EncryptionPanel if there is a pending verification request
const { member } = card.state;
const pendingRequest = member ? pendingVerificationRequestForUser(member) : undefined;
const pendingRequest = member
? pendingVerificationRequestForUser(MatrixClientPeg.get(), member)
: undefined;
if (pendingRequest) {
return {
phase: RightPanelPhases.EncryptionPanel,
@ -341,7 +344,7 @@ export default class RightPanelStore extends ReadyWatchingStore {
if (!this.currentCard?.state) return;
const { member } = this.currentCard.state;
if (!member) return;
const pendingRequest = pendingVerificationRequestForUser(member);
const pendingRequest = pendingVerificationRequestForUser(MatrixClientPeg.get(), member);
if (pendingRequest) {
this.currentCard.state.verificationRequest = pendingRequest;
this.emitAndUpdateSettings();

View file

@ -16,11 +16,10 @@ limitations under the License.
import { User } from "matrix-js-sdk/src/models/user";
import { verificationMethods as VerificationMethods } from "matrix-js-sdk/src/crypto";
import { RoomMember } from "matrix-js-sdk/src/matrix";
import { MatrixClient, RoomMember } from "matrix-js-sdk/src/matrix";
import { VerificationRequest } from "matrix-js-sdk/src/crypto/verification/request/VerificationRequest";
import { CrossSigningKey } from "matrix-js-sdk/src/crypto-api";
import { MatrixClientPeg } from "./MatrixClientPeg";
import dis from "./dispatcher/dispatcher";
import Modal from "./Modal";
import { RightPanelPhases } from "./stores/right-panel/RightPanelStorePhases";
@ -32,9 +31,8 @@ import RightPanelStore from "./stores/right-panel/RightPanelStore";
import { IRightPanelCardState } from "./stores/right-panel/RightPanelStoreIPanelState";
import { findDMForUser } from "./utils/dm/findDMForUser";
async function enable4SIfNeeded(): Promise<boolean> {
const cli = MatrixClientPeg.get();
const crypto = cli.getCrypto();
async function enable4SIfNeeded(matrixClient: MatrixClient): Promise<boolean> {
const crypto = matrixClient.getCrypto();
if (!crypto) return false;
const usk = await crypto.getCrossSigningKeyId(CrossSigningKey.UserSigning);
if (!usk) {
@ -45,15 +43,14 @@ async function enable4SIfNeeded(): Promise<boolean> {
return true;
}
export async function verifyDevice(user: User, device: IDevice): Promise<void> {
const cli = MatrixClientPeg.get();
if (cli.isGuest()) {
export async function verifyDevice(matrixClient: MatrixClient, user: User, device: IDevice): Promise<void> {
if (matrixClient.isGuest()) {
dis.dispatch({ action: "require_registration" });
return;
}
// if cross-signing is not explicitly disabled, check if it should be enabled first.
if (cli.getCryptoTrustCrossSignedDevices()) {
if (!(await enable4SIfNeeded())) {
if (matrixClient.getCryptoTrustCrossSignedDevices()) {
if (!(await enable4SIfNeeded(matrixClient))) {
return;
}
}
@ -63,7 +60,7 @@ export async function verifyDevice(user: User, device: IDevice): Promise<void> {
device,
onFinished: async (action): Promise<void> => {
if (action === "sas") {
const verificationRequestPromise = cli.legacyDeviceVerification(
const verificationRequestPromise = matrixClient.legacyDeviceVerification(
user.userId,
device.deviceId,
VerificationMethods.SAS,
@ -79,32 +76,30 @@ export async function verifyDevice(user: User, device: IDevice): Promise<void> {
});
}
export async function legacyVerifyUser(user: User): Promise<void> {
const cli = MatrixClientPeg.get();
if (cli.isGuest()) {
export async function legacyVerifyUser(matrixClient: MatrixClient, user: User): Promise<void> {
if (matrixClient.isGuest()) {
dis.dispatch({ action: "require_registration" });
return;
}
// if cross-signing is not explicitly disabled, check if it should be enabled first.
if (cli.getCryptoTrustCrossSignedDevices()) {
if (!(await enable4SIfNeeded())) {
if (matrixClient.getCryptoTrustCrossSignedDevices()) {
if (!(await enable4SIfNeeded(matrixClient))) {
return;
}
}
const verificationRequestPromise = cli.requestVerification(user.userId);
const verificationRequestPromise = matrixClient.requestVerification(user.userId);
setRightPanel({ member: user, verificationRequestPromise });
}
export async function verifyUser(user: User): Promise<void> {
const cli = MatrixClientPeg.get();
if (cli.isGuest()) {
export async function verifyUser(matrixClient: MatrixClient, user: User): Promise<void> {
if (matrixClient.isGuest()) {
dis.dispatch({ action: "require_registration" });
return;
}
if (!(await enable4SIfNeeded())) {
if (!(await enable4SIfNeeded(matrixClient))) {
return;
}
const existingRequest = pendingVerificationRequestForUser(user);
const existingRequest = pendingVerificationRequestForUser(matrixClient, user);
setRightPanel({ member: user, verificationRequest: existingRequest });
}
@ -120,10 +115,12 @@ function setRightPanel(state: IRightPanelCardState): void {
}
}
export function pendingVerificationRequestForUser(user: User | RoomMember): VerificationRequest | undefined {
const cli = MatrixClientPeg.get();
const dmRoom = findDMForUser(cli, user.userId);
export function pendingVerificationRequestForUser(
matrixClient: MatrixClient,
user: User | RoomMember,
): VerificationRequest | undefined {
const dmRoom = findDMForUser(matrixClient, user.userId);
if (dmRoom) {
return cli.findVerificationRequestDMInProgress(dmRoom.roomId);
return matrixClient.findVerificationRequestDMInProgress(dmRoom.roomId);
}
}

View file

@ -59,8 +59,8 @@ const mockDispatcher = mocked(dis);
const flushPromises = async () => await new Promise(process.nextTick);
describe("DeviceListener", () => {
let mockClient: Mocked<MatrixClient> | undefined;
let mockCrypto: Mocked<CryptoApi> | undefined;
let mockClient: Mocked<MatrixClient>;
let mockCrypto: Mocked<CryptoApi>;
// spy on various toasts' hide and show functions
// easier than mocking
@ -111,7 +111,7 @@ describe("DeviceListener", () => {
const createAndStart = async (): Promise<DeviceListener> => {
const instance = new DeviceListener();
instance.start();
instance.start(mockClient);
await flushPromises();
return instance;
};

View file

@ -52,7 +52,7 @@ describe("setDMRoom", () => {
describe("when logged in as a guest and marking a room as DM", () => {
beforeEach(() => {
mocked(client.isGuest).mockReturnValue(true);
setDMRoom(roomId1, userId1);
setDMRoom(client, roomId1, userId1);
});
it("should not update the account data", () => {
@ -62,7 +62,7 @@ describe("setDMRoom", () => {
describe("when adding a new room to an existing DM relation", () => {
beforeEach(() => {
setDMRoom(roomId4, userId1);
setDMRoom(client, roomId4, userId1);
});
it("should update the account data accordingly", () => {
@ -75,7 +75,7 @@ describe("setDMRoom", () => {
describe("when adding a new DM room", () => {
beforeEach(() => {
setDMRoom(roomId4, userId3);
setDMRoom(client, roomId4, userId3);
});
it("should update the account data accordingly", () => {
@ -89,7 +89,7 @@ describe("setDMRoom", () => {
describe("when trying to add a DM, that already exists", () => {
beforeEach(() => {
setDMRoom(roomId1, userId1);
setDMRoom(client, roomId1, userId1);
});
it("should not update the account data", () => {
@ -99,7 +99,7 @@ describe("setDMRoom", () => {
describe("when removing an existing DM", () => {
beforeEach(() => {
setDMRoom(roomId1, null);
setDMRoom(client, roomId1, null);
});
it("should update the account data accordingly", () => {
@ -112,7 +112,7 @@ describe("setDMRoom", () => {
describe("when removing an unknown room", () => {
beforeEach(() => {
setDMRoom(roomId4, null);
setDMRoom(client, roomId4, null);
});
it("should not update the account data", () => {
@ -123,7 +123,7 @@ describe("setDMRoom", () => {
describe("when the direct event is undefined", () => {
beforeEach(() => {
mocked(client.getAccountData).mockReturnValue(undefined);
setDMRoom(roomId1, userId1);
setDMRoom(client, roomId1, userId1);
});
it("should update the account data accordingly", () => {
@ -139,7 +139,7 @@ describe("setDMRoom", () => {
mocked(client.getAccountData).mockReturnValue({
getContent: jest.fn(),
});
setDMRoom(roomId1, userId1);
setDMRoom(client, roomId1, userId1);
});
it("should update the account data accordingly", () => {

View file

@ -66,7 +66,7 @@ describe("Terms", function () {
},
});
const interactionCallback = jest.fn().mockResolvedValue([]);
await startTermsFlow([IM_SERVICE_ONE], interactionCallback);
await startTermsFlow(mockClient, [IM_SERVICE_ONE], interactionCallback);
expect(interactionCallback).toHaveBeenCalledWith(
[
@ -97,7 +97,7 @@ describe("Terms", function () {
mockClient.agreeToTerms;
const interactionCallback = jest.fn();
await startTermsFlow([IM_SERVICE_ONE], interactionCallback);
await startTermsFlow(mockClient, [IM_SERVICE_ONE], interactionCallback);
expect(interactionCallback).not.toHaveBeenCalled();
expect(mockClient.agreeToTerms).toHaveBeenCalledWith(SERVICE_TYPES.IM, "https://imone.test", "a token token", [
@ -122,7 +122,7 @@ describe("Terms", function () {
});
const interactionCallback = jest.fn().mockResolvedValue(["http://example.com/one", "http://example.com/two"]);
await startTermsFlow([IM_SERVICE_ONE], interactionCallback);
await startTermsFlow(mockClient, [IM_SERVICE_ONE], interactionCallback);
expect(interactionCallback).toHaveBeenCalledWith(
[
@ -168,7 +168,7 @@ describe("Terms", function () {
});
const interactionCallback = jest.fn().mockResolvedValue(["http://example.com/one", "http://example.com/two"]);
await startTermsFlow([IM_SERVICE_ONE, IM_SERVICE_TWO], interactionCallback);
await startTermsFlow(mockClient, [IM_SERVICE_ONE, IM_SERVICE_TWO], interactionCallback);
expect(interactionCallback).toHaveBeenCalledWith(
[

View file

@ -500,7 +500,7 @@ describe("<DeviceItem />", () => {
await userEvent.click(button);
expect(mockVerifyDevice).toHaveBeenCalledTimes(1);
expect(mockVerifyDevice).toHaveBeenCalledWith(defaultUser, device);
expect(mockVerifyDevice).toHaveBeenCalledWith(mockClient, defaultUser, device);
});
it("with display name", async () => {