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:
parent
b6b9ce3c46
commit
21ffc50f1e
23 changed files with 152 additions and 135 deletions
|
@ -17,11 +17,10 @@ limitations under the License.
|
||||||
import { MatrixEvent } from "matrix-js-sdk/src/models/event";
|
import { MatrixEvent } from "matrix-js-sdk/src/models/event";
|
||||||
import { logger } from "matrix-js-sdk/src/logger";
|
import { logger } from "matrix-js-sdk/src/logger";
|
||||||
import { CryptoEvent } from "matrix-js-sdk/src/crypto";
|
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 { SyncState } from "matrix-js-sdk/src/sync";
|
||||||
import { IKeyBackupInfo } from "matrix-js-sdk/src/crypto/keybackup";
|
import { IKeyBackupInfo } from "matrix-js-sdk/src/crypto/keybackup";
|
||||||
|
|
||||||
import { MatrixClientPeg } from "./MatrixClientPeg";
|
|
||||||
import dis from "./dispatcher/dispatcher";
|
import dis from "./dispatcher/dispatcher";
|
||||||
import {
|
import {
|
||||||
hideToast as hideBulkUnverifiedSessionsToast,
|
hideToast as hideBulkUnverifiedSessionsToast,
|
||||||
|
@ -67,6 +66,8 @@ export default class DeviceListener {
|
||||||
// The set of device IDs we're currently displaying toasts for
|
// The set of device IDs we're currently displaying toasts for
|
||||||
private displayingToastsForDeviceIds = new Set<string>();
|
private displayingToastsForDeviceIds = new Set<string>();
|
||||||
private running = false;
|
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 shouldRecordClientInformation = false;
|
||||||
private enableBulkUnverifiedSessionsReminder = true;
|
private enableBulkUnverifiedSessionsReminder = true;
|
||||||
private deviceClientInformationSettingWatcherRef: string | undefined;
|
private deviceClientInformationSettingWatcherRef: string | undefined;
|
||||||
|
@ -76,16 +77,17 @@ export default class DeviceListener {
|
||||||
return window.mxDeviceListener;
|
return window.mxDeviceListener;
|
||||||
}
|
}
|
||||||
|
|
||||||
public start(): void {
|
public start(matrixClient: MatrixClient): void {
|
||||||
this.running = true;
|
this.running = true;
|
||||||
MatrixClientPeg.get().on(CryptoEvent.WillUpdateDevices, this.onWillUpdateDevices);
|
this.client = matrixClient;
|
||||||
MatrixClientPeg.get().on(CryptoEvent.DevicesUpdated, this.onDevicesUpdated);
|
this.client.on(CryptoEvent.WillUpdateDevices, this.onWillUpdateDevices);
|
||||||
MatrixClientPeg.get().on(CryptoEvent.DeviceVerificationChanged, this.onDeviceVerificationChanged);
|
this.client.on(CryptoEvent.DevicesUpdated, this.onDevicesUpdated);
|
||||||
MatrixClientPeg.get().on(CryptoEvent.UserTrustStatusChanged, this.onUserTrustStatusChanged);
|
this.client.on(CryptoEvent.DeviceVerificationChanged, this.onDeviceVerificationChanged);
|
||||||
MatrixClientPeg.get().on(CryptoEvent.KeysChanged, this.onCrossSingingKeysChanged);
|
this.client.on(CryptoEvent.UserTrustStatusChanged, this.onUserTrustStatusChanged);
|
||||||
MatrixClientPeg.get().on(ClientEvent.AccountData, this.onAccountData);
|
this.client.on(CryptoEvent.KeysChanged, this.onCrossSingingKeysChanged);
|
||||||
MatrixClientPeg.get().on(ClientEvent.Sync, this.onSync);
|
this.client.on(ClientEvent.AccountData, this.onAccountData);
|
||||||
MatrixClientPeg.get().on(RoomStateEvent.Events, this.onRoomStateEvents);
|
this.client.on(ClientEvent.Sync, this.onSync);
|
||||||
|
this.client.on(RoomStateEvent.Events, this.onRoomStateEvents);
|
||||||
this.shouldRecordClientInformation = SettingsStore.getValue("deviceClientInformationOptIn");
|
this.shouldRecordClientInformation = SettingsStore.getValue("deviceClientInformationOptIn");
|
||||||
// only configurable in config, so we don't need to watch the value
|
// only configurable in config, so we don't need to watch the value
|
||||||
this.enableBulkUnverifiedSessionsReminder = SettingsStore.getValue(UIFeature.BulkUnverifiedSessionsReminder);
|
this.enableBulkUnverifiedSessionsReminder = SettingsStore.getValue(UIFeature.BulkUnverifiedSessionsReminder);
|
||||||
|
@ -101,18 +103,15 @@ export default class DeviceListener {
|
||||||
|
|
||||||
public stop(): void {
|
public stop(): void {
|
||||||
this.running = false;
|
this.running = false;
|
||||||
if (MatrixClientPeg.get()) {
|
if (this.client) {
|
||||||
MatrixClientPeg.get().removeListener(CryptoEvent.WillUpdateDevices, this.onWillUpdateDevices);
|
this.client.removeListener(CryptoEvent.WillUpdateDevices, this.onWillUpdateDevices);
|
||||||
MatrixClientPeg.get().removeListener(CryptoEvent.DevicesUpdated, this.onDevicesUpdated);
|
this.client.removeListener(CryptoEvent.DevicesUpdated, this.onDevicesUpdated);
|
||||||
MatrixClientPeg.get().removeListener(
|
this.client.removeListener(CryptoEvent.DeviceVerificationChanged, this.onDeviceVerificationChanged);
|
||||||
CryptoEvent.DeviceVerificationChanged,
|
this.client.removeListener(CryptoEvent.UserTrustStatusChanged, this.onUserTrustStatusChanged);
|
||||||
this.onDeviceVerificationChanged,
|
this.client.removeListener(CryptoEvent.KeysChanged, this.onCrossSingingKeysChanged);
|
||||||
);
|
this.client.removeListener(ClientEvent.AccountData, this.onAccountData);
|
||||||
MatrixClientPeg.get().removeListener(CryptoEvent.UserTrustStatusChanged, this.onUserTrustStatusChanged);
|
this.client.removeListener(ClientEvent.Sync, this.onSync);
|
||||||
MatrixClientPeg.get().removeListener(CryptoEvent.KeysChanged, this.onCrossSingingKeysChanged);
|
this.client.removeListener(RoomStateEvent.Events, this.onRoomStateEvents);
|
||||||
MatrixClientPeg.get().removeListener(ClientEvent.AccountData, this.onAccountData);
|
|
||||||
MatrixClientPeg.get().removeListener(ClientEvent.Sync, this.onSync);
|
|
||||||
MatrixClientPeg.get().removeListener(RoomStateEvent.Events, this.onRoomStateEvents);
|
|
||||||
}
|
}
|
||||||
if (this.deviceClientInformationSettingWatcherRef) {
|
if (this.deviceClientInformationSettingWatcherRef) {
|
||||||
SettingsStore.unwatchSetting(this.deviceClientInformationSettingWatcherRef);
|
SettingsStore.unwatchSetting(this.deviceClientInformationSettingWatcherRef);
|
||||||
|
@ -128,6 +127,7 @@ export default class DeviceListener {
|
||||||
this.keyBackupStatusChecked = false;
|
this.keyBackupStatusChecked = false;
|
||||||
this.ourDeviceIdsAtStart = null;
|
this.ourDeviceIdsAtStart = null;
|
||||||
this.displayingToastsForDeviceIds = new Set();
|
this.displayingToastsForDeviceIds = new Set();
|
||||||
|
this.client = undefined;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -160,22 +160,23 @@ export default class DeviceListener {
|
||||||
* @returns the set of device IDs
|
* @returns the set of device IDs
|
||||||
*/
|
*/
|
||||||
private async getDeviceIds(): Promise<Set<string>> {
|
private async getDeviceIds(): Promise<Set<string>> {
|
||||||
const cli = MatrixClientPeg.get();
|
const cli = this.client;
|
||||||
const crypto = cli.getCrypto();
|
const crypto = cli?.getCrypto();
|
||||||
if (crypto === undefined) return new Set();
|
if (crypto === undefined) return new Set();
|
||||||
|
|
||||||
const userId = cli.getSafeUserId();
|
const userId = cli!.getSafeUserId();
|
||||||
const devices = await crypto.getUserDeviceInfo([userId]);
|
const devices = await crypto.getUserDeviceInfo([userId]);
|
||||||
return new Set(devices.get(userId)?.keys() ?? []);
|
return new Set(devices.get(userId)?.keys() ?? []);
|
||||||
}
|
}
|
||||||
|
|
||||||
private onWillUpdateDevices = async (users: string[], initialFetch?: boolean): Promise<void> => {
|
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),
|
// 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
|
// then they are all pre-existing devices, so ignore this and set the
|
||||||
// devicesAtStart list to the devices that we see after the fetch.
|
// devicesAtStart list to the devices that we see after the fetch.
|
||||||
if (initialFetch) return;
|
if (initialFetch) return;
|
||||||
|
|
||||||
const myUserId = MatrixClientPeg.get().getUserId()!;
|
const myUserId = this.client.getSafeUserId();
|
||||||
if (users.includes(myUserId)) await this.ensureDeviceIdsAtStartPopulated();
|
if (users.includes(myUserId)) await this.ensureDeviceIdsAtStartPopulated();
|
||||||
|
|
||||||
// No need to do a recheck here: we just need to get a snapshot of our devices
|
// 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 => {
|
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();
|
this.recheck();
|
||||||
};
|
};
|
||||||
|
|
||||||
private onDeviceVerificationChanged = (userId: string): void => {
|
private onDeviceVerificationChanged = (userId: string): void => {
|
||||||
if (userId !== MatrixClientPeg.get().getUserId()) return;
|
if (!this.client) return;
|
||||||
|
if (userId !== this.client.getUserId()) return;
|
||||||
this.recheck();
|
this.recheck();
|
||||||
};
|
};
|
||||||
|
|
||||||
private onUserTrustStatusChanged = (userId: string): void => {
|
private onUserTrustStatusChanged = (userId: string): void => {
|
||||||
if (userId !== MatrixClientPeg.get().getUserId()) return;
|
if (!this.client) return;
|
||||||
|
if (userId !== this.client.getUserId()) return;
|
||||||
this.recheck();
|
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
|
// The server doesn't tell us when key backup is set up, so we poll
|
||||||
// & cache the result
|
// & cache the result
|
||||||
private async getKeyBackupInfo(): Promise<IKeyBackupInfo | null> {
|
private async getKeyBackupInfo(): Promise<IKeyBackupInfo | null> {
|
||||||
|
if (!this.client) return null;
|
||||||
const now = new Date().getTime();
|
const now = new Date().getTime();
|
||||||
if (
|
if (
|
||||||
!this.keyBackupInfo ||
|
!this.keyBackupInfo ||
|
||||||
!this.keyBackupFetchedAt ||
|
!this.keyBackupFetchedAt ||
|
||||||
this.keyBackupFetchedAt < now - KEY_BACKUP_POLL_INTERVAL
|
this.keyBackupFetchedAt < now - KEY_BACKUP_POLL_INTERVAL
|
||||||
) {
|
) {
|
||||||
this.keyBackupInfo = await MatrixClientPeg.get().getKeyBackupVersion();
|
this.keyBackupInfo = await this.client.getKeyBackupVersion();
|
||||||
this.keyBackupFetchedAt = now;
|
this.keyBackupFetchedAt = now;
|
||||||
}
|
}
|
||||||
return this.keyBackupInfo;
|
return this.keyBackupInfo;
|
||||||
|
@ -256,13 +261,13 @@ export default class DeviceListener {
|
||||||
// modifying the state involved here, so don't add new toasts to setup.
|
// modifying the state involved here, so don't add new toasts to setup.
|
||||||
if (isSecretStorageBeingAccessed()) return false;
|
if (isSecretStorageBeingAccessed()) return false;
|
||||||
// Show setup toasts once the user is in at least one encrypted room.
|
// Show setup toasts once the user is in at least one encrypted room.
|
||||||
const cli = MatrixClientPeg.get();
|
const cli = this.client;
|
||||||
return cli && cli.getRooms().some((r) => cli.isRoomEncrypted(r.roomId));
|
return cli?.getRooms().some((r) => cli.isRoomEncrypted(r.roomId)) ?? false;
|
||||||
}
|
}
|
||||||
|
|
||||||
private async recheck(): Promise<void> {
|
private async recheck(): Promise<void> {
|
||||||
if (!this.running) return; // we have been stopped
|
if (!this.running || !this.client) return; // we have been stopped
|
||||||
const cli = MatrixClientPeg.get();
|
const cli = this.client;
|
||||||
|
|
||||||
// cross-signing support was added to Matrix in MSC1756, which landed in spec v1.1
|
// cross-signing support was added to Matrix in MSC1756, which landed in spec v1.1
|
||||||
if (!(await cli.isVersionSupported("v1.1"))) return;
|
if (!(await cli.isVersionSupported("v1.1"))) return;
|
||||||
|
@ -285,11 +290,11 @@ export default class DeviceListener {
|
||||||
this.checkKeyBackupStatus();
|
this.checkKeyBackupStatus();
|
||||||
} else if (this.shouldShowSetupEncryptionToast()) {
|
} else if (this.shouldShowSetupEncryptionToast()) {
|
||||||
// make sure our keys are finished downloading
|
// 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
|
// cross signing isn't enabled - nag to enable it
|
||||||
// There are 3 different toasts for:
|
// 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)
|
// Cross-signing on account but this device doesn't trust the master key (verify this session)
|
||||||
showSetupEncryptionToast(SetupKind.VERIFY_THIS_SESSION);
|
showSetupEncryptionToast(SetupKind.VERIFY_THIS_SESSION);
|
||||||
this.checkKeyBackupStatus();
|
this.checkKeyBackupStatus();
|
||||||
|
@ -327,7 +332,9 @@ export default class DeviceListener {
|
||||||
|
|
||||||
const isCurrentDeviceTrusted =
|
const isCurrentDeviceTrusted =
|
||||||
crossSigningReady &&
|
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,
|
// as long as cross-signing isn't ready,
|
||||||
// you can't see or dismiss any device toasts
|
// you can't see or dismiss any device toasts
|
||||||
|
@ -336,7 +343,7 @@ export default class DeviceListener {
|
||||||
for (const deviceId of devices) {
|
for (const deviceId of devices) {
|
||||||
if (deviceId === cli.deviceId) continue;
|
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 (!deviceTrust?.crossSigningVerified && !this.dismissed.has(deviceId)) {
|
||||||
if (this.ourDeviceIdsAtStart?.has(deviceId)) {
|
if (this.ourDeviceIdsAtStart?.has(deviceId)) {
|
||||||
oldUnverifiedDeviceIds.add(deviceId);
|
oldUnverifiedDeviceIds.add(deviceId);
|
||||||
|
@ -383,11 +390,11 @@ export default class DeviceListener {
|
||||||
}
|
}
|
||||||
|
|
||||||
private checkKeyBackupStatus = async (): Promise<void> => {
|
private checkKeyBackupStatus = async (): Promise<void> => {
|
||||||
if (this.keyBackupStatusChecked) {
|
if (this.keyBackupStatusChecked || !this.client) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
// returns null when key backup status hasn't finished being checked
|
// 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;
|
this.keyBackupStatusChecked = isKeyBackupEnabled !== null;
|
||||||
|
|
||||||
if (isKeyBackupEnabled === false) {
|
if (isKeyBackupEnabled === false) {
|
||||||
|
@ -412,11 +419,12 @@ export default class DeviceListener {
|
||||||
};
|
};
|
||||||
|
|
||||||
private updateClientInformation = async (): Promise<void> => {
|
private updateClientInformation = async (): Promise<void> => {
|
||||||
|
if (!this.client) return;
|
||||||
try {
|
try {
|
||||||
if (this.shouldRecordClientInformation) {
|
if (this.shouldRecordClientInformation) {
|
||||||
await recordClientInformation(MatrixClientPeg.get(), SdkConfig.get(), PlatformPeg.get() ?? undefined);
|
await recordClientInformation(this.client, SdkConfig.get(), PlatformPeg.get() ?? undefined);
|
||||||
} else {
|
} else {
|
||||||
await removeClientInformation(MatrixClientPeg.get());
|
await removeClientInformation(this.client);
|
||||||
}
|
}
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
// this is a best effort operation
|
// this is a best effort operation
|
||||||
|
|
|
@ -129,7 +129,7 @@ export default class IdentityAuthClient {
|
||||||
} catch (e) {
|
} catch (e) {
|
||||||
if (e instanceof MatrixError && e.errcode === "M_TERMS_NOT_SIGNED") {
|
if (e instanceof MatrixError && e.errcode === "M_TERMS_NOT_SIGNED") {
|
||||||
logger.log("Identity server requires new terms to be agreed to");
|
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;
|
return;
|
||||||
}
|
}
|
||||||
throw e;
|
throw e;
|
||||||
|
|
|
@ -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");
|
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");
|
logger.info("Lifecycle#hydrateSession: Pickle key not provided - trying to get one");
|
||||||
credentials.pickleKey =
|
credentials.pickleKey =
|
||||||
(await PlatformPeg.get()?.getPickleKey(credentials.userId, credentials.deviceId)) ?? undefined;
|
(await PlatformPeg.get()?.getPickleKey(credentials.userId, credentials.deviceId)) ?? undefined;
|
||||||
|
@ -603,14 +603,14 @@ async function doSetLoggedIn(credentials: IMatrixClientCreds, clearStorageEnable
|
||||||
}
|
}
|
||||||
|
|
||||||
MatrixClientPeg.replaceUsingCreds(credentials);
|
MatrixClientPeg.replaceUsingCreds(credentials);
|
||||||
|
const client = MatrixClientPeg.get();
|
||||||
|
|
||||||
setSentryUser(credentials.userId);
|
setSentryUser(credentials.userId);
|
||||||
|
|
||||||
if (PosthogAnalytics.instance.isEnabled()) {
|
if (PosthogAnalytics.instance.isEnabled()) {
|
||||||
PosthogAnalytics.instance.startListeningToSettingsChanges();
|
PosthogAnalytics.instance.startListeningToSettingsChanges(client);
|
||||||
}
|
}
|
||||||
|
|
||||||
const client = MatrixClientPeg.get();
|
|
||||||
if (credentials.freshLogin && SettingsStore.getValue("feature_dehydration")) {
|
if (credentials.freshLogin && SettingsStore.getValue("feature_dehydration")) {
|
||||||
// If we just logged in, try to rehydrate a device instead of using a
|
// 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
|
// 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();
|
SettingsStore.runMigrations();
|
||||||
|
|
||||||
// This needs to be started after crypto is set up
|
// 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
|
// Similarly, don't start sending presence updates until we've started
|
||||||
// the client
|
// the client
|
||||||
if (!SettingsStore.getValue("lowBandwidth")) {
|
if (!SettingsStore.getValue("lowBandwidth")) {
|
||||||
|
|
|
@ -15,8 +15,8 @@ limitations under the License.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
import { ClientWidgetApi } from "matrix-widget-api";
|
import { ClientWidgetApi } from "matrix-widget-api";
|
||||||
|
import { MatrixClient } from "matrix-js-sdk/src/matrix";
|
||||||
|
|
||||||
import { MatrixClientPeg } from "./MatrixClientPeg";
|
|
||||||
import SdkConfig from "./SdkConfig";
|
import SdkConfig from "./SdkConfig";
|
||||||
import { ElementWidgetActions } from "./stores/widgets/ElementWidgetActions";
|
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
|
// Dummy rtmp URL used to signal that we want a special audio-only stream
|
||||||
const AUDIOSTREAM_DUMMY_URL = "rtmp://audiostream.dummy/";
|
const AUDIOSTREAM_DUMMY_URL = "rtmp://audiostream.dummy/";
|
||||||
|
|
||||||
async function createLiveStream(roomId: string): Promise<void> {
|
async function createLiveStream(matrixClient: MatrixClient, roomId: string): Promise<void> {
|
||||||
const openIdToken = await MatrixClientPeg.get().getOpenIdToken();
|
const openIdToken = await matrixClient.getOpenIdToken();
|
||||||
|
|
||||||
const url = getConfigLivestreamUrl() + "/createStream";
|
const url = getConfigLivestreamUrl() + "/createStream";
|
||||||
|
|
||||||
|
@ -47,8 +47,12 @@ async function createLiveStream(roomId: string): Promise<void> {
|
||||||
return respBody["stream_id"];
|
return respBody["stream_id"];
|
||||||
}
|
}
|
||||||
|
|
||||||
export async function startJitsiAudioLivestream(widgetMessaging: ClientWidgetApi, roomId: string): Promise<void> {
|
export async function startJitsiAudioLivestream(
|
||||||
const streamId = await createLiveStream(roomId);
|
matrixClient: MatrixClient,
|
||||||
|
widgetMessaging: ClientWidgetApi,
|
||||||
|
roomId: string,
|
||||||
|
): Promise<void> {
|
||||||
|
const streamId = await createLiveStream(matrixClient, roomId);
|
||||||
|
|
||||||
await widgetMessaging.transport.send(ElementWidgetActions.StartLiveStream, {
|
await widgetMessaging.transport.send(ElementWidgetActions.StartLiveStream, {
|
||||||
rtmpStreamKey: AUDIOSTREAM_DUMMY_URL + streamId,
|
rtmpStreamKey: AUDIOSTREAM_DUMMY_URL + streamId,
|
||||||
|
|
|
@ -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 {
|
private capture(eventName: string, properties: Properties, options?: IPostHogEventOptions): void {
|
||||||
if (!this.enabled) {
|
if (!this.enabled) {
|
||||||
return;
|
return;
|
||||||
|
@ -375,12 +375,12 @@ export class PosthogAnalytics {
|
||||||
this.registerSuperProperties(this.platformSuperProperties);
|
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
|
// Update this.anonymity based on the user's analytics opt-in settings
|
||||||
const anonymity = pseudonymousOptIn ? Anonymity.Pseudonymous : Anonymity.Disabled;
|
const anonymity = pseudonymousOptIn ? Anonymity.Pseudonymous : Anonymity.Disabled;
|
||||||
this.setAnonymity(anonymity);
|
this.setAnonymity(anonymity);
|
||||||
if (anonymity === Anonymity.Pseudonymous) {
|
if (anonymity === Anonymity.Pseudonymous) {
|
||||||
await this.identifyUser(MatrixClientPeg.get(), PosthogAnalytics.getRandomAnalyticsId);
|
await this.identifyUser(client, PosthogAnalytics.getRandomAnalyticsId);
|
||||||
if (MatrixClientPeg.currentUserIsJustRegistered()) {
|
if (MatrixClientPeg.currentUserIsJustRegistered()) {
|
||||||
this.trackNewUserEvent();
|
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.
|
// Listen to account data changes from sync so we can observe changes to relevant flags and update.
|
||||||
// This is called -
|
// This is called -
|
||||||
// * On page load, when the account data is first received by sync
|
// * On page load, when the account data is first received by sync
|
||||||
|
@ -404,7 +404,7 @@ export class PosthogAnalytics {
|
||||||
"pseudonymousAnalyticsOptIn",
|
"pseudonymousAnalyticsOptIn",
|
||||||
null,
|
null,
|
||||||
(originalSettingName, changedInRoomId, atLevel, newValueAtLevel, newValue) => {
|
(originalSettingName, changedInRoomId, atLevel, newValueAtLevel, newValue) => {
|
||||||
this.updateAnonymityFromSettings(!!newValue);
|
this.updateAnonymityFromSettings(client, !!newValue);
|
||||||
},
|
},
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
|
@ -21,7 +21,6 @@ import { ConditionKind, PushRuleActionName, PushRuleKind, TweakName } from "matr
|
||||||
import type { IPushRule } from "matrix-js-sdk/src/@types/PushRules";
|
import type { IPushRule } from "matrix-js-sdk/src/@types/PushRules";
|
||||||
import type { Room } from "matrix-js-sdk/src/models/room";
|
import type { Room } from "matrix-js-sdk/src/models/room";
|
||||||
import type { MatrixClient } from "matrix-js-sdk/src/matrix";
|
import type { MatrixClient } from "matrix-js-sdk/src/matrix";
|
||||||
import { MatrixClientPeg } from "./MatrixClientPeg";
|
|
||||||
import { NotificationColor } from "./stores/notifications/NotificationColor";
|
import { NotificationColor } from "./stores/notifications/NotificationColor";
|
||||||
import { getUnsentMessages } from "./components/structures/RoomStatusBar";
|
import { getUnsentMessages } from "./components/structures/RoomStatusBar";
|
||||||
import { doesRoomHaveUnreadMessages, doesRoomOrThreadHaveUnreadMessages } from "./Unread";
|
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:
|
// look through the override rules for a rule affecting this room:
|
||||||
// if one exists, it will take precedence.
|
// if one exists, it will take precedence.
|
||||||
const muteRule = findOverrideMuteRule(roomId);
|
const muteRule = findOverrideMuteRule(client, roomId);
|
||||||
if (muteRule) {
|
if (muteRule) {
|
||||||
return RoomNotifState.Mute;
|
return RoomNotifState.Mute;
|
||||||
}
|
}
|
||||||
|
@ -70,11 +69,11 @@ export function getRoomNotifsState(client: MatrixClient, roomId: string): RoomNo
|
||||||
return null;
|
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) {
|
if (newState === RoomNotifState.Mute) {
|
||||||
return setRoomNotifsStateMuted(roomId);
|
return setRoomNotifsStateMuted(client, roomId);
|
||||||
} else {
|
} 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
|
// Exclude threadId, as the same thread can't continue over a room upgrade
|
||||||
if (!threadId && predecessor?.roomId) {
|
if (!threadId && predecessor?.roomId) {
|
||||||
const oldRoomId = predecessor.roomId;
|
const oldRoomId = predecessor.roomId;
|
||||||
const oldRoom = MatrixClientPeg.get().getRoom(oldRoomId);
|
const oldRoom = room.client.getRoom(oldRoomId);
|
||||||
if (oldRoom) {
|
if (oldRoom) {
|
||||||
// We only ever care if there's highlights in the old room. No point in
|
// 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
|
// notifying the user for unread messages because they would have extreme
|
||||||
|
@ -104,8 +103,7 @@ export function getUnreadNotificationCount(room: Room, type: NotificationCountTy
|
||||||
return notificationCount;
|
return notificationCount;
|
||||||
}
|
}
|
||||||
|
|
||||||
function setRoomNotifsStateMuted(roomId: string): Promise<any> {
|
function setRoomNotifsStateMuted(cli: MatrixClient, roomId: string): Promise<any> {
|
||||||
const cli = MatrixClientPeg.get();
|
|
||||||
const promises: Promise<unknown>[] = [];
|
const promises: Promise<unknown>[] = [];
|
||||||
|
|
||||||
// delete the room rule
|
// delete the room rule
|
||||||
|
@ -135,11 +133,10 @@ function setRoomNotifsStateMuted(roomId: string): Promise<any> {
|
||||||
return Promise.all(promises);
|
return Promise.all(promises);
|
||||||
}
|
}
|
||||||
|
|
||||||
function setRoomNotifsStateUnmuted(roomId: string, newState: RoomNotifState): Promise<any> {
|
function setRoomNotifsStateUnmuted(cli: MatrixClient, roomId: string, newState: RoomNotifState): Promise<any> {
|
||||||
const cli = MatrixClientPeg.get();
|
|
||||||
const promises: Promise<unknown>[] = [];
|
const promises: Promise<unknown>[] = [];
|
||||||
|
|
||||||
const overrideMuteRule = findOverrideMuteRule(roomId);
|
const overrideMuteRule = findOverrideMuteRule(cli, roomId);
|
||||||
if (overrideMuteRule) {
|
if (overrideMuteRule) {
|
||||||
promises.push(cli.deletePushRule("global", PushRuleKind.Override, overrideMuteRule.rule_id));
|
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);
|
return Promise.all(promises);
|
||||||
}
|
}
|
||||||
|
|
||||||
function findOverrideMuteRule(roomId: string): IPushRule | null {
|
function findOverrideMuteRule(cli: MatrixClient | undefined, roomId: string): IPushRule | null {
|
||||||
const cli = MatrixClientPeg.get();
|
|
||||||
if (!cli?.pushRules?.global?.override) {
|
if (!cli?.pushRules?.global?.override) {
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
|
|
13
src/Rooms.ts
13
src/Rooms.ts
|
@ -17,8 +17,8 @@ limitations under the License.
|
||||||
import { Room } from "matrix-js-sdk/src/models/room";
|
import { Room } from "matrix-js-sdk/src/models/room";
|
||||||
import { EventType } from "matrix-js-sdk/src/@types/event";
|
import { EventType } from "matrix-js-sdk/src/@types/event";
|
||||||
import { RoomMember } from "matrix-js-sdk/src/models/room-member";
|
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";
|
import AliasCustomisations from "./customisations/Alias";
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -52,20 +52,21 @@ export function guessAndSetDMRoom(room: Room, isDirect: boolean): Promise<void>
|
||||||
newTarget = null;
|
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.
|
* 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} roomId The ID of the room to modify
|
||||||
* @param {string | null} userId The user ID of the desired DM room target user or
|
* @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
|
* null to un-mark this room as a DM room
|
||||||
* @returns {object} A promise
|
* @returns {object} A promise
|
||||||
*/
|
*/
|
||||||
export async function setDMRoom(roomId: string, userId: string | null): Promise<void> {
|
export async function setDMRoom(client: MatrixClient, roomId: string, userId: string | null): Promise<void> {
|
||||||
if (MatrixClientPeg.get().isGuest()) return;
|
if (client.isGuest()) return;
|
||||||
|
|
||||||
const mDirectEvent = MatrixClientPeg.get().getAccountData(EventType.Direct);
|
const mDirectEvent = client.getAccountData(EventType.Direct);
|
||||||
const currentContent = mDirectEvent?.getContent() || {};
|
const currentContent = mDirectEvent?.getContent() || {};
|
||||||
|
|
||||||
const dmRoomMap = new Map(Object.entries(currentContent));
|
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
|
// prevent unnecessary calls to setAccountData
|
||||||
if (!modified) return;
|
if (!modified) return;
|
||||||
|
|
||||||
await MatrixClientPeg.get().setAccountData(EventType.Direct, Object.fromEntries(dmRoomMap));
|
await client.setAccountData(EventType.Direct, Object.fromEntries(dmRoomMap));
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
|
|
@ -157,6 +157,7 @@ export default class ScalarAuthClient {
|
||||||
const parsedImRestUrl = parseUrl(this.apiUrl);
|
const parsedImRestUrl = parseUrl(this.apiUrl);
|
||||||
parsedImRestUrl.pathname = "";
|
parsedImRestUrl.pathname = "";
|
||||||
return startTermsFlow(
|
return startTermsFlow(
|
||||||
|
MatrixClientPeg.get(),
|
||||||
[new Service(SERVICE_TYPES.IM, parsedImRestUrl.toString(), token)],
|
[new Service(SERVICE_TYPES.IM, parsedImRestUrl.toString(), token)],
|
||||||
this.termsInteractionCallback,
|
this.termsInteractionCallback,
|
||||||
).then(() => {
|
).then(() => {
|
||||||
|
|
|
@ -876,7 +876,7 @@ const onMessage = function (event: MessageEvent<any>): void {
|
||||||
// No integrations UI URL, ignore silently.
|
// No integrations UI URL, ignore silently.
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
let eventOriginUrl;
|
let eventOriginUrl: URL;
|
||||||
try {
|
try {
|
||||||
eventOriginUrl = new URL(event.origin);
|
eventOriginUrl = new URL(event.origin);
|
||||||
} catch (e) {
|
} catch (e) {
|
||||||
|
|
12
src/Terms.ts
12
src/Terms.ts
|
@ -17,8 +17,8 @@ limitations under the License.
|
||||||
import classNames from "classnames";
|
import classNames from "classnames";
|
||||||
import { SERVICE_TYPES } from "matrix-js-sdk/src/service-types";
|
import { SERVICE_TYPES } from "matrix-js-sdk/src/service-types";
|
||||||
import { logger } from "matrix-js-sdk/src/logger";
|
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 Modal from "./Modal";
|
||||||
import TermsDialog from "./components/views/dialogs/TermsDialog";
|
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
|
* 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 {Service[]} services Object with keys 'serviceType', 'baseUrl', 'accessToken'
|
||||||
* @param {function} interactionCallback Function called with:
|
* @param {function} interactionCallback Function called with:
|
||||||
* * an array of { service: {Service}, policies: {terms response from API} }
|
* * an array of { service: {Service}, policies: {terms response from API} }
|
||||||
|
@ -75,10 +76,11 @@ export type TermsInteractionCallback = (
|
||||||
* if they cancel.
|
* if they cancel.
|
||||||
*/
|
*/
|
||||||
export async function startTermsFlow(
|
export async function startTermsFlow(
|
||||||
|
client: MatrixClient,
|
||||||
services: Service[],
|
services: Service[],
|
||||||
interactionCallback: TermsInteractionCallback = dialogTermsInteractionCallback,
|
interactionCallback: TermsInteractionCallback = dialogTermsInteractionCallback,
|
||||||
): Promise<void> {
|
): 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:
|
* a /terms response looks like:
|
||||||
|
@ -105,7 +107,7 @@ export async function startTermsFlow(
|
||||||
});
|
});
|
||||||
|
|
||||||
// fetch the set of agreed policy URLs from account data
|
// 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>;
|
let agreedUrlSet: Set<string>;
|
||||||
if (!currentAcceptedTerms || !currentAcceptedTerms.getContent() || !currentAcceptedTerms.getContent().accepted) {
|
if (!currentAcceptedTerms || !currentAcceptedTerms.getContent() || !currentAcceptedTerms.getContent().accepted) {
|
||||||
agreedUrlSet = new Set();
|
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
|
// 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) {
|
if (agreedUrlSet.size !== numAcceptedBeforeAgreement) {
|
||||||
const newAcceptedTerms = { accepted: Array.from(agreedUrlSet) };
|
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) => {
|
const agreePromises = policiesAndServicePairs.map((policiesAndService) => {
|
||||||
|
@ -171,7 +173,7 @@ export async function startTermsFlow(
|
||||||
|
|
||||||
if (urlsForService.length === 0) return Promise.resolve();
|
if (urlsForService.length === 0) return Promise.resolve();
|
||||||
|
|
||||||
return MatrixClientPeg.get().agreeToTerms(
|
return client.agreeToTerms(
|
||||||
policiesAndService.service.serviceType,
|
policiesAndService.service.serviceType,
|
||||||
policiesAndService.service.baseUrl,
|
policiesAndService.service.baseUrl,
|
||||||
policiesAndService.service.accessToken,
|
policiesAndService.service.accessToken,
|
||||||
|
|
|
@ -1512,7 +1512,7 @@ export class RoomView extends React.Component<IRoomProps, IRoomState> {
|
||||||
}
|
}
|
||||||
const dmInviter = room.getDMInviter();
|
const dmInviter = room.getDMInviter();
|
||||||
if (dmInviter) {
|
if (dmInviter) {
|
||||||
Rooms.setDMRoom(room.roomId, dmInviter);
|
Rooms.setDMRoom(room.client, room.roomId, dmInviter);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -67,7 +67,7 @@ export const WidgetContextMenu: React.FC<IProps> = ({
|
||||||
if (roomId && getConfigLivestreamUrl() && WidgetType.JITSI.matches(app.type)) {
|
if (roomId && getConfigLivestreamUrl() && WidgetType.JITSI.matches(app.type)) {
|
||||||
const onStreamAudioClick = async (): Promise<void> => {
|
const onStreamAudioClick = async (): Promise<void> => {
|
||||||
try {
|
try {
|
||||||
await startJitsiAudioLivestream(widgetMessaging!, roomId);
|
await startJitsiAudioLivestream(cli, widgetMessaging!, roomId);
|
||||||
} catch (err) {
|
} catch (err) {
|
||||||
logger.error("Failed to start livestream", err);
|
logger.error("Failed to start livestream", err);
|
||||||
// XXX: won't i18n well, but looks like widget api only support 'message'?
|
// XXX: won't i18n well, but looks like widget api only support 'message'?
|
||||||
|
|
|
@ -191,7 +191,7 @@ export function DeviceItem({ userId, device }: { userId: string; device: IDevice
|
||||||
const onDeviceClick = (): void => {
|
const onDeviceClick = (): void => {
|
||||||
const user = cli.getUser(userId);
|
const user = cli.getUser(userId);
|
||||||
if (user) {
|
if (user) {
|
||||||
verifyDevice(user, device);
|
verifyDevice(cli, user, device);
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
|
@ -1446,9 +1446,9 @@ const BasicUserInfo: React.FC<{
|
||||||
className="mx_UserInfo_field mx_UserInfo_verifyButton"
|
className="mx_UserInfo_field mx_UserInfo_verifyButton"
|
||||||
onClick={() => {
|
onClick={() => {
|
||||||
if (hasCrossSigningKeys) {
|
if (hasCrossSigningKeys) {
|
||||||
verifyUser(member as User);
|
verifyUser(cli, member as User);
|
||||||
} else {
|
} else {
|
||||||
legacyVerifyUser(member as User);
|
legacyVerifyUser(cli, member as User);
|
||||||
}
|
}
|
||||||
}}
|
}}
|
||||||
>
|
>
|
||||||
|
|
|
@ -61,6 +61,7 @@ import SettingsSubsection, { SettingsSubsectionText } from "../../shared/Setting
|
||||||
import { SettingsSubsectionHeading } from "../../shared/SettingsSubsectionHeading";
|
import { SettingsSubsectionHeading } from "../../shared/SettingsSubsectionHeading";
|
||||||
import Heading from "../../../typography/Heading";
|
import Heading from "../../../typography/Heading";
|
||||||
import InlineSpinner from "../../../elements/InlineSpinner";
|
import InlineSpinner from "../../../elements/InlineSpinner";
|
||||||
|
import MatrixClientContext from "../../../../../contexts/MatrixClientContext";
|
||||||
|
|
||||||
interface IProps {
|
interface IProps {
|
||||||
closeSettingsFn: () => void;
|
closeSettingsFn: () => void;
|
||||||
|
@ -96,6 +97,9 @@ interface IState {
|
||||||
}
|
}
|
||||||
|
|
||||||
export default class GeneralUserSettingsTab extends React.Component<IProps, IState> {
|
export default class GeneralUserSettingsTab extends React.Component<IProps, IState> {
|
||||||
|
public static contextType = MatrixClientContext;
|
||||||
|
public context!: React.ContextType<typeof MatrixClientContext>;
|
||||||
|
|
||||||
private readonly dispatcherRef: string;
|
private readonly dispatcherRef: string;
|
||||||
|
|
||||||
public constructor(props: IProps) {
|
public constructor(props: IProps) {
|
||||||
|
@ -217,6 +221,7 @@ export default class GeneralUserSettingsTab extends React.Component<IProps, ISta
|
||||||
try {
|
try {
|
||||||
const idAccessToken = await authClient.getAccessToken({ check: false });
|
const idAccessToken = await authClient.getAccessToken({ check: false });
|
||||||
await startTermsFlow(
|
await startTermsFlow(
|
||||||
|
this.context,
|
||||||
[new Service(SERVICE_TYPES.IS, idServerUrl, idAccessToken!)],
|
[new Service(SERVICE_TYPES.IS, idServerUrl, idAccessToken!)],
|
||||||
(policiesAndServices, agreedUrls, extraClassNames) => {
|
(policiesAndServices, agreedUrls, extraClassNames) => {
|
||||||
return new Promise((resolve, reject) => {
|
return new Promise((resolve, reject) => {
|
||||||
|
|
|
@ -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(() => {
|
.then(() => {
|
||||||
if (opts.parentSpace) {
|
if (opts.parentSpace) {
|
||||||
|
|
|
@ -189,7 +189,7 @@ async function collectBugReport(opts: IOpts = {}, gzipLogs = true): Promise<Form
|
||||||
|
|
||||||
// compress
|
// compress
|
||||||
if (gzipLogs) {
|
if (gzipLogs) {
|
||||||
buf = pako.gzip(buf);
|
buf = pako!.gzip(buf);
|
||||||
}
|
}
|
||||||
|
|
||||||
body.append("compressed-log", new Blob([buf]), entry.id);
|
body.append("compressed-log", new Blob([buf]), entry.id);
|
||||||
|
|
|
@ -78,7 +78,7 @@ export class RoomEchoChamber extends GenericEchoChamber<RoomEchoContext, CachedR
|
||||||
CachedRoomKey.NotificationVolume,
|
CachedRoomKey.NotificationVolume,
|
||||||
v,
|
v,
|
||||||
async (): Promise<void> => {
|
async (): Promise<void> => {
|
||||||
return setRoomNotifsState(this.context.room.roomId, v);
|
return setRoomNotifsState(this.context.room.client, this.context.room.roomId, v);
|
||||||
},
|
},
|
||||||
implicitlyReverted,
|
implicitlyReverted,
|
||||||
);
|
);
|
||||||
|
|
|
@ -35,6 +35,7 @@ import { ActionPayload } from "../../dispatcher/payloads";
|
||||||
import { Action } from "../../dispatcher/actions";
|
import { Action } from "../../dispatcher/actions";
|
||||||
import { ActiveRoomChangedPayload } from "../../dispatcher/payloads/ActiveRoomChangedPayload";
|
import { ActiveRoomChangedPayload } from "../../dispatcher/payloads/ActiveRoomChangedPayload";
|
||||||
import { SdkContextClass } from "../../contexts/SDKContext";
|
import { SdkContextClass } from "../../contexts/SDKContext";
|
||||||
|
import { MatrixClientPeg } from "../../MatrixClientPeg";
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* A class for tracking the state of the right panel between layouts and
|
* 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) {
|
if (card.phase === RightPanelPhases.RoomMemberInfo && card.state) {
|
||||||
// RightPanelPhases.RoomMemberInfo -> needs to be changed to RightPanelPhases.EncryptionPanel if there is a pending verification request
|
// RightPanelPhases.RoomMemberInfo -> needs to be changed to RightPanelPhases.EncryptionPanel if there is a pending verification request
|
||||||
const { member } = card.state;
|
const { member } = card.state;
|
||||||
const pendingRequest = member ? pendingVerificationRequestForUser(member) : undefined;
|
const pendingRequest = member
|
||||||
|
? pendingVerificationRequestForUser(MatrixClientPeg.get(), member)
|
||||||
|
: undefined;
|
||||||
if (pendingRequest) {
|
if (pendingRequest) {
|
||||||
return {
|
return {
|
||||||
phase: RightPanelPhases.EncryptionPanel,
|
phase: RightPanelPhases.EncryptionPanel,
|
||||||
|
@ -341,7 +344,7 @@ export default class RightPanelStore extends ReadyWatchingStore {
|
||||||
if (!this.currentCard?.state) return;
|
if (!this.currentCard?.state) return;
|
||||||
const { member } = this.currentCard.state;
|
const { member } = this.currentCard.state;
|
||||||
if (!member) return;
|
if (!member) return;
|
||||||
const pendingRequest = pendingVerificationRequestForUser(member);
|
const pendingRequest = pendingVerificationRequestForUser(MatrixClientPeg.get(), member);
|
||||||
if (pendingRequest) {
|
if (pendingRequest) {
|
||||||
this.currentCard.state.verificationRequest = pendingRequest;
|
this.currentCard.state.verificationRequest = pendingRequest;
|
||||||
this.emitAndUpdateSettings();
|
this.emitAndUpdateSettings();
|
||||||
|
|
|
@ -16,11 +16,10 @@ limitations under the License.
|
||||||
|
|
||||||
import { User } from "matrix-js-sdk/src/models/user";
|
import { User } from "matrix-js-sdk/src/models/user";
|
||||||
import { verificationMethods as VerificationMethods } from "matrix-js-sdk/src/crypto";
|
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 { VerificationRequest } from "matrix-js-sdk/src/crypto/verification/request/VerificationRequest";
|
||||||
import { CrossSigningKey } from "matrix-js-sdk/src/crypto-api";
|
import { CrossSigningKey } from "matrix-js-sdk/src/crypto-api";
|
||||||
|
|
||||||
import { MatrixClientPeg } from "./MatrixClientPeg";
|
|
||||||
import dis from "./dispatcher/dispatcher";
|
import dis from "./dispatcher/dispatcher";
|
||||||
import Modal from "./Modal";
|
import Modal from "./Modal";
|
||||||
import { RightPanelPhases } from "./stores/right-panel/RightPanelStorePhases";
|
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 { IRightPanelCardState } from "./stores/right-panel/RightPanelStoreIPanelState";
|
||||||
import { findDMForUser } from "./utils/dm/findDMForUser";
|
import { findDMForUser } from "./utils/dm/findDMForUser";
|
||||||
|
|
||||||
async function enable4SIfNeeded(): Promise<boolean> {
|
async function enable4SIfNeeded(matrixClient: MatrixClient): Promise<boolean> {
|
||||||
const cli = MatrixClientPeg.get();
|
const crypto = matrixClient.getCrypto();
|
||||||
const crypto = cli.getCrypto();
|
|
||||||
if (!crypto) return false;
|
if (!crypto) return false;
|
||||||
const usk = await crypto.getCrossSigningKeyId(CrossSigningKey.UserSigning);
|
const usk = await crypto.getCrossSigningKeyId(CrossSigningKey.UserSigning);
|
||||||
if (!usk) {
|
if (!usk) {
|
||||||
|
@ -45,15 +43,14 @@ async function enable4SIfNeeded(): Promise<boolean> {
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
export async function verifyDevice(user: User, device: IDevice): Promise<void> {
|
export async function verifyDevice(matrixClient: MatrixClient, user: User, device: IDevice): Promise<void> {
|
||||||
const cli = MatrixClientPeg.get();
|
if (matrixClient.isGuest()) {
|
||||||
if (cli.isGuest()) {
|
|
||||||
dis.dispatch({ action: "require_registration" });
|
dis.dispatch({ action: "require_registration" });
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
// if cross-signing is not explicitly disabled, check if it should be enabled first.
|
// if cross-signing is not explicitly disabled, check if it should be enabled first.
|
||||||
if (cli.getCryptoTrustCrossSignedDevices()) {
|
if (matrixClient.getCryptoTrustCrossSignedDevices()) {
|
||||||
if (!(await enable4SIfNeeded())) {
|
if (!(await enable4SIfNeeded(matrixClient))) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -63,7 +60,7 @@ export async function verifyDevice(user: User, device: IDevice): Promise<void> {
|
||||||
device,
|
device,
|
||||||
onFinished: async (action): Promise<void> => {
|
onFinished: async (action): Promise<void> => {
|
||||||
if (action === "sas") {
|
if (action === "sas") {
|
||||||
const verificationRequestPromise = cli.legacyDeviceVerification(
|
const verificationRequestPromise = matrixClient.legacyDeviceVerification(
|
||||||
user.userId,
|
user.userId,
|
||||||
device.deviceId,
|
device.deviceId,
|
||||||
VerificationMethods.SAS,
|
VerificationMethods.SAS,
|
||||||
|
@ -79,32 +76,30 @@ export async function verifyDevice(user: User, device: IDevice): Promise<void> {
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
export async function legacyVerifyUser(user: User): Promise<void> {
|
export async function legacyVerifyUser(matrixClient: MatrixClient, user: User): Promise<void> {
|
||||||
const cli = MatrixClientPeg.get();
|
if (matrixClient.isGuest()) {
|
||||||
if (cli.isGuest()) {
|
|
||||||
dis.dispatch({ action: "require_registration" });
|
dis.dispatch({ action: "require_registration" });
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
// if cross-signing is not explicitly disabled, check if it should be enabled first.
|
// if cross-signing is not explicitly disabled, check if it should be enabled first.
|
||||||
if (cli.getCryptoTrustCrossSignedDevices()) {
|
if (matrixClient.getCryptoTrustCrossSignedDevices()) {
|
||||||
if (!(await enable4SIfNeeded())) {
|
if (!(await enable4SIfNeeded(matrixClient))) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
const verificationRequestPromise = cli.requestVerification(user.userId);
|
const verificationRequestPromise = matrixClient.requestVerification(user.userId);
|
||||||
setRightPanel({ member: user, verificationRequestPromise });
|
setRightPanel({ member: user, verificationRequestPromise });
|
||||||
}
|
}
|
||||||
|
|
||||||
export async function verifyUser(user: User): Promise<void> {
|
export async function verifyUser(matrixClient: MatrixClient, user: User): Promise<void> {
|
||||||
const cli = MatrixClientPeg.get();
|
if (matrixClient.isGuest()) {
|
||||||
if (cli.isGuest()) {
|
|
||||||
dis.dispatch({ action: "require_registration" });
|
dis.dispatch({ action: "require_registration" });
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
if (!(await enable4SIfNeeded())) {
|
if (!(await enable4SIfNeeded(matrixClient))) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
const existingRequest = pendingVerificationRequestForUser(user);
|
const existingRequest = pendingVerificationRequestForUser(matrixClient, user);
|
||||||
setRightPanel({ member: user, verificationRequest: existingRequest });
|
setRightPanel({ member: user, verificationRequest: existingRequest });
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -120,10 +115,12 @@ function setRightPanel(state: IRightPanelCardState): void {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
export function pendingVerificationRequestForUser(user: User | RoomMember): VerificationRequest | undefined {
|
export function pendingVerificationRequestForUser(
|
||||||
const cli = MatrixClientPeg.get();
|
matrixClient: MatrixClient,
|
||||||
const dmRoom = findDMForUser(cli, user.userId);
|
user: User | RoomMember,
|
||||||
|
): VerificationRequest | undefined {
|
||||||
|
const dmRoom = findDMForUser(matrixClient, user.userId);
|
||||||
if (dmRoom) {
|
if (dmRoom) {
|
||||||
return cli.findVerificationRequestDMInProgress(dmRoom.roomId);
|
return matrixClient.findVerificationRequestDMInProgress(dmRoom.roomId);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -59,8 +59,8 @@ const mockDispatcher = mocked(dis);
|
||||||
const flushPromises = async () => await new Promise(process.nextTick);
|
const flushPromises = async () => await new Promise(process.nextTick);
|
||||||
|
|
||||||
describe("DeviceListener", () => {
|
describe("DeviceListener", () => {
|
||||||
let mockClient: Mocked<MatrixClient> | undefined;
|
let mockClient: Mocked<MatrixClient>;
|
||||||
let mockCrypto: Mocked<CryptoApi> | undefined;
|
let mockCrypto: Mocked<CryptoApi>;
|
||||||
|
|
||||||
// spy on various toasts' hide and show functions
|
// spy on various toasts' hide and show functions
|
||||||
// easier than mocking
|
// easier than mocking
|
||||||
|
@ -111,7 +111,7 @@ describe("DeviceListener", () => {
|
||||||
|
|
||||||
const createAndStart = async (): Promise<DeviceListener> => {
|
const createAndStart = async (): Promise<DeviceListener> => {
|
||||||
const instance = new DeviceListener();
|
const instance = new DeviceListener();
|
||||||
instance.start();
|
instance.start(mockClient);
|
||||||
await flushPromises();
|
await flushPromises();
|
||||||
return instance;
|
return instance;
|
||||||
};
|
};
|
||||||
|
|
|
@ -52,7 +52,7 @@ describe("setDMRoom", () => {
|
||||||
describe("when logged in as a guest and marking a room as DM", () => {
|
describe("when logged in as a guest and marking a room as DM", () => {
|
||||||
beforeEach(() => {
|
beforeEach(() => {
|
||||||
mocked(client.isGuest).mockReturnValue(true);
|
mocked(client.isGuest).mockReturnValue(true);
|
||||||
setDMRoom(roomId1, userId1);
|
setDMRoom(client, roomId1, userId1);
|
||||||
});
|
});
|
||||||
|
|
||||||
it("should not update the account data", () => {
|
it("should not update the account data", () => {
|
||||||
|
@ -62,7 +62,7 @@ describe("setDMRoom", () => {
|
||||||
|
|
||||||
describe("when adding a new room to an existing DM relation", () => {
|
describe("when adding a new room to an existing DM relation", () => {
|
||||||
beforeEach(() => {
|
beforeEach(() => {
|
||||||
setDMRoom(roomId4, userId1);
|
setDMRoom(client, roomId4, userId1);
|
||||||
});
|
});
|
||||||
|
|
||||||
it("should update the account data accordingly", () => {
|
it("should update the account data accordingly", () => {
|
||||||
|
@ -75,7 +75,7 @@ describe("setDMRoom", () => {
|
||||||
|
|
||||||
describe("when adding a new DM room", () => {
|
describe("when adding a new DM room", () => {
|
||||||
beforeEach(() => {
|
beforeEach(() => {
|
||||||
setDMRoom(roomId4, userId3);
|
setDMRoom(client, roomId4, userId3);
|
||||||
});
|
});
|
||||||
|
|
||||||
it("should update the account data accordingly", () => {
|
it("should update the account data accordingly", () => {
|
||||||
|
@ -89,7 +89,7 @@ describe("setDMRoom", () => {
|
||||||
|
|
||||||
describe("when trying to add a DM, that already exists", () => {
|
describe("when trying to add a DM, that already exists", () => {
|
||||||
beforeEach(() => {
|
beforeEach(() => {
|
||||||
setDMRoom(roomId1, userId1);
|
setDMRoom(client, roomId1, userId1);
|
||||||
});
|
});
|
||||||
|
|
||||||
it("should not update the account data", () => {
|
it("should not update the account data", () => {
|
||||||
|
@ -99,7 +99,7 @@ describe("setDMRoom", () => {
|
||||||
|
|
||||||
describe("when removing an existing DM", () => {
|
describe("when removing an existing DM", () => {
|
||||||
beforeEach(() => {
|
beforeEach(() => {
|
||||||
setDMRoom(roomId1, null);
|
setDMRoom(client, roomId1, null);
|
||||||
});
|
});
|
||||||
|
|
||||||
it("should update the account data accordingly", () => {
|
it("should update the account data accordingly", () => {
|
||||||
|
@ -112,7 +112,7 @@ describe("setDMRoom", () => {
|
||||||
|
|
||||||
describe("when removing an unknown room", () => {
|
describe("when removing an unknown room", () => {
|
||||||
beforeEach(() => {
|
beforeEach(() => {
|
||||||
setDMRoom(roomId4, null);
|
setDMRoom(client, roomId4, null);
|
||||||
});
|
});
|
||||||
|
|
||||||
it("should not update the account data", () => {
|
it("should not update the account data", () => {
|
||||||
|
@ -123,7 +123,7 @@ describe("setDMRoom", () => {
|
||||||
describe("when the direct event is undefined", () => {
|
describe("when the direct event is undefined", () => {
|
||||||
beforeEach(() => {
|
beforeEach(() => {
|
||||||
mocked(client.getAccountData).mockReturnValue(undefined);
|
mocked(client.getAccountData).mockReturnValue(undefined);
|
||||||
setDMRoom(roomId1, userId1);
|
setDMRoom(client, roomId1, userId1);
|
||||||
});
|
});
|
||||||
|
|
||||||
it("should update the account data accordingly", () => {
|
it("should update the account data accordingly", () => {
|
||||||
|
@ -139,7 +139,7 @@ describe("setDMRoom", () => {
|
||||||
mocked(client.getAccountData).mockReturnValue({
|
mocked(client.getAccountData).mockReturnValue({
|
||||||
getContent: jest.fn(),
|
getContent: jest.fn(),
|
||||||
});
|
});
|
||||||
setDMRoom(roomId1, userId1);
|
setDMRoom(client, roomId1, userId1);
|
||||||
});
|
});
|
||||||
|
|
||||||
it("should update the account data accordingly", () => {
|
it("should update the account data accordingly", () => {
|
||||||
|
|
|
@ -66,7 +66,7 @@ describe("Terms", function () {
|
||||||
},
|
},
|
||||||
});
|
});
|
||||||
const interactionCallback = jest.fn().mockResolvedValue([]);
|
const interactionCallback = jest.fn().mockResolvedValue([]);
|
||||||
await startTermsFlow([IM_SERVICE_ONE], interactionCallback);
|
await startTermsFlow(mockClient, [IM_SERVICE_ONE], interactionCallback);
|
||||||
|
|
||||||
expect(interactionCallback).toHaveBeenCalledWith(
|
expect(interactionCallback).toHaveBeenCalledWith(
|
||||||
[
|
[
|
||||||
|
@ -97,7 +97,7 @@ describe("Terms", function () {
|
||||||
mockClient.agreeToTerms;
|
mockClient.agreeToTerms;
|
||||||
|
|
||||||
const interactionCallback = jest.fn();
|
const interactionCallback = jest.fn();
|
||||||
await startTermsFlow([IM_SERVICE_ONE], interactionCallback);
|
await startTermsFlow(mockClient, [IM_SERVICE_ONE], interactionCallback);
|
||||||
|
|
||||||
expect(interactionCallback).not.toHaveBeenCalled();
|
expect(interactionCallback).not.toHaveBeenCalled();
|
||||||
expect(mockClient.agreeToTerms).toHaveBeenCalledWith(SERVICE_TYPES.IM, "https://imone.test", "a token token", [
|
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"]);
|
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(
|
expect(interactionCallback).toHaveBeenCalledWith(
|
||||||
[
|
[
|
||||||
|
@ -168,7 +168,7 @@ describe("Terms", function () {
|
||||||
});
|
});
|
||||||
|
|
||||||
const interactionCallback = jest.fn().mockResolvedValue(["http://example.com/one", "http://example.com/two"]);
|
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(
|
expect(interactionCallback).toHaveBeenCalledWith(
|
||||||
[
|
[
|
||||||
|
|
|
@ -500,7 +500,7 @@ describe("<DeviceItem />", () => {
|
||||||
await userEvent.click(button);
|
await userEvent.click(button);
|
||||||
|
|
||||||
expect(mockVerifyDevice).toHaveBeenCalledTimes(1);
|
expect(mockVerifyDevice).toHaveBeenCalledTimes(1);
|
||||||
expect(mockVerifyDevice).toHaveBeenCalledWith(defaultUser, device);
|
expect(mockVerifyDevice).toHaveBeenCalledWith(mockClient, defaultUser, device);
|
||||||
});
|
});
|
||||||
|
|
||||||
it("with display name", async () => {
|
it("with display name", async () => {
|
||||||
|
|
Loading…
Reference in a new issue