mirror of
https://github.com/Novaloop-AG/healthchat-web.git
synced 2026-11-07 23:18:21 +00:00
Extract the "other devices" logic from DeviceListener (#32419)
* Extract the other devices logic from DeviceListener * Remove unneeded async modifier on dismissUnverifiedSessions
This commit is contained in:
+14
-116
@@ -24,18 +24,10 @@ import { secureRandomString } from "matrix-js-sdk/src/randomstring";
|
|||||||
|
|
||||||
import { PosthogAnalytics } from "./PosthogAnalytics";
|
import { PosthogAnalytics } from "./PosthogAnalytics";
|
||||||
import dis from "./dispatcher/dispatcher";
|
import dis from "./dispatcher/dispatcher";
|
||||||
import {
|
|
||||||
hideToast as hideBulkUnverifiedSessionsToast,
|
|
||||||
showToast as showBulkUnverifiedSessionsToast,
|
|
||||||
} from "./toasts/BulkUnverifiedSessionsToast";
|
|
||||||
import {
|
import {
|
||||||
hideToast as hideSetupEncryptionToast,
|
hideToast as hideSetupEncryptionToast,
|
||||||
showToast as showSetupEncryptionToast,
|
showToast as showSetupEncryptionToast,
|
||||||
} from "./toasts/SetupEncryptionToast";
|
} from "./toasts/SetupEncryptionToast";
|
||||||
import {
|
|
||||||
hideToast as hideUnverifiedSessionsToast,
|
|
||||||
showToast as showUnverifiedSessionsToast,
|
|
||||||
} from "./toasts/UnverifiedSessionToast";
|
|
||||||
import { isSecretStorageBeingAccessed } from "./SecurityManager";
|
import { isSecretStorageBeingAccessed } from "./SecurityManager";
|
||||||
import { type ActionPayload } from "./dispatcher/payloads";
|
import { type ActionPayload } from "./dispatcher/payloads";
|
||||||
import { Action } from "./dispatcher/actions";
|
import { Action } from "./dispatcher/actions";
|
||||||
@@ -43,9 +35,8 @@ import SdkConfig from "./SdkConfig";
|
|||||||
import PlatformPeg from "./PlatformPeg";
|
import PlatformPeg from "./PlatformPeg";
|
||||||
import { recordClientInformation, removeClientInformation } from "./utils/device/clientInformation";
|
import { recordClientInformation, removeClientInformation } from "./utils/device/clientInformation";
|
||||||
import SettingsStore, { type CallbackFn } from "./settings/SettingsStore";
|
import SettingsStore, { type CallbackFn } from "./settings/SettingsStore";
|
||||||
import { isBulkUnverifiedDeviceReminderSnoozed } from "./utils/device/snoozeBulkUnverifiedDeviceReminder";
|
|
||||||
import { getUserDeviceIds } from "./utils/crypto/deviceInfo";
|
|
||||||
import { asyncSomeParallel } from "./utils/arrays.ts";
|
import { asyncSomeParallel } from "./utils/arrays.ts";
|
||||||
|
import DeviceListenerOtherDevices from "./device-listener/DeviceListenerOtherDevices.ts";
|
||||||
|
|
||||||
const KEY_BACKUP_POLL_INTERVAL = 5 * 60 * 1000;
|
const KEY_BACKUP_POLL_INTERVAL = 5 * 60 * 1000;
|
||||||
|
|
||||||
@@ -106,20 +97,16 @@ type EventHandlerMap = {
|
|||||||
|
|
||||||
export default class DeviceListener extends TypedEventEmitter<DeviceListenerEvents, EventHandlerMap> {
|
export default class DeviceListener extends TypedEventEmitter<DeviceListenerEvents, EventHandlerMap> {
|
||||||
private dispatcherRef?: string;
|
private dispatcherRef?: string;
|
||||||
// device IDs for which the user has dismissed the verify toast ('Later')
|
|
||||||
private dismissed = new Set<string>();
|
/** All the information about whether other devices are verified. */
|
||||||
|
public otherDevices?: DeviceListenerOtherDevices;
|
||||||
|
|
||||||
// has the user dismissed any of the various nag toasts to setup encryption on this device?
|
// has the user dismissed any of the various nag toasts to setup encryption on this device?
|
||||||
private dismissedThisDeviceToast = false;
|
private dismissedThisDeviceToast = false;
|
||||||
/** Cache of the info about the current key backup on the server. */
|
/** Cache of the info about the current key backup on the server. */
|
||||||
private keyBackupInfo: KeyBackupInfo | null = null;
|
private keyBackupInfo: KeyBackupInfo | null = null;
|
||||||
/** When `keyBackupInfo` was last updated */
|
/** When `keyBackupInfo` was last updated */
|
||||||
private keyBackupFetchedAt: number | null = null;
|
private keyBackupFetchedAt: number | null = null;
|
||||||
// We keep a list of our own device IDs so we can batch ones that were already
|
|
||||||
// there the last time the app launched into a single toast, but display new
|
|
||||||
// ones in their own toasts.
|
|
||||||
private ourDeviceIdsAtStart: Set<string> | null = null;
|
|
||||||
// The set of device IDs we're currently displaying toasts for
|
|
||||||
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.
|
// The client with which the instance is running. Only set if `running` is true, otherwise undefined.
|
||||||
private client?: MatrixClient;
|
private client?: MatrixClient;
|
||||||
@@ -138,8 +125,10 @@ export default class DeviceListener extends TypedEventEmitter<DeviceListenerEven
|
|||||||
|
|
||||||
public start(matrixClient: MatrixClient): void {
|
public start(matrixClient: MatrixClient): void {
|
||||||
this.running = true;
|
this.running = true;
|
||||||
|
|
||||||
|
this.otherDevices = new DeviceListenerOtherDevices(this, matrixClient);
|
||||||
|
|
||||||
this.client = matrixClient;
|
this.client = matrixClient;
|
||||||
this.client.on(CryptoEvent.DevicesUpdated, this.onDevicesUpdated);
|
|
||||||
this.client.on(CryptoEvent.UserTrustStatusChanged, this.onUserTrustStatusChanged);
|
this.client.on(CryptoEvent.UserTrustStatusChanged, this.onUserTrustStatusChanged);
|
||||||
this.client.on(CryptoEvent.KeysChanged, this.onCrossSingingKeysChanged);
|
this.client.on(CryptoEvent.KeysChanged, this.onCrossSingingKeysChanged);
|
||||||
this.client.on(CryptoEvent.KeyBackupStatus, this.onKeyBackupStatusChanged);
|
this.client.on(CryptoEvent.KeyBackupStatus, this.onKeyBackupStatusChanged);
|
||||||
@@ -147,6 +136,7 @@ export default class DeviceListener extends TypedEventEmitter<DeviceListenerEven
|
|||||||
this.client.on(ClientEvent.Sync, this.onSync);
|
this.client.on(ClientEvent.Sync, this.onSync);
|
||||||
this.client.on(RoomStateEvent.Events, this.onRoomStateEvents);
|
this.client.on(RoomStateEvent.Events, this.onRoomStateEvents);
|
||||||
this.client.on(ClientEvent.ToDeviceEvent, this.onToDeviceEvent);
|
this.client.on(ClientEvent.ToDeviceEvent, this.onToDeviceEvent);
|
||||||
|
|
||||||
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.deviceClientInformationSettingWatcherRef = SettingsStore.watchSetting(
|
this.deviceClientInformationSettingWatcherRef = SettingsStore.watchSetting(
|
||||||
@@ -162,7 +152,6 @@ export default class DeviceListener extends TypedEventEmitter<DeviceListenerEven
|
|||||||
public stop(): void {
|
public stop(): void {
|
||||||
this.running = false;
|
this.running = false;
|
||||||
if (this.client) {
|
if (this.client) {
|
||||||
this.client.removeListener(CryptoEvent.DevicesUpdated, this.onDevicesUpdated);
|
|
||||||
this.client.removeListener(CryptoEvent.UserTrustStatusChanged, this.onUserTrustStatusChanged);
|
this.client.removeListener(CryptoEvent.UserTrustStatusChanged, this.onUserTrustStatusChanged);
|
||||||
this.client.removeListener(CryptoEvent.KeysChanged, this.onCrossSingingKeysChanged);
|
this.client.removeListener(CryptoEvent.KeysChanged, this.onCrossSingingKeysChanged);
|
||||||
this.client.removeListener(ClientEvent.AccountData, this.onAccountData);
|
this.client.removeListener(ClientEvent.AccountData, this.onAccountData);
|
||||||
@@ -173,13 +162,11 @@ export default class DeviceListener extends TypedEventEmitter<DeviceListenerEven
|
|||||||
SettingsStore.unwatchSetting(this.deviceClientInformationSettingWatcherRef);
|
SettingsStore.unwatchSetting(this.deviceClientInformationSettingWatcherRef);
|
||||||
dis.unregister(this.dispatcherRef);
|
dis.unregister(this.dispatcherRef);
|
||||||
this.dispatcherRef = undefined;
|
this.dispatcherRef = undefined;
|
||||||
this.dismissed.clear();
|
this.otherDevices?.stop();
|
||||||
this.dismissedThisDeviceToast = false;
|
this.dismissedThisDeviceToast = false;
|
||||||
this.keyBackupInfo = null;
|
this.keyBackupInfo = null;
|
||||||
this.keyBackupFetchedAt = null;
|
this.keyBackupFetchedAt = null;
|
||||||
this.cachedKeyBackupUploadActive = undefined;
|
this.cachedKeyBackupUploadActive = undefined;
|
||||||
this.ourDeviceIdsAtStart = null;
|
|
||||||
this.displayingToastsForDeviceIds = new Set();
|
|
||||||
this.client = undefined;
|
this.client = undefined;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -207,13 +194,10 @@ export default class DeviceListener extends TypedEventEmitter<DeviceListenerEven
|
|||||||
*
|
*
|
||||||
* @param {String[]} deviceIds List of device IDs to dismiss notifications for
|
* @param {String[]} deviceIds List of device IDs to dismiss notifications for
|
||||||
*/
|
*/
|
||||||
public async dismissUnverifiedSessions(deviceIds: Iterable<string>): Promise<void> {
|
public dismissUnverifiedSessions(deviceIds: Iterable<string>): void {
|
||||||
logger.debug("Dismissing unverified sessions: " + Array.from(deviceIds).join(","));
|
logger.debug("Dismissing unverified sessions: " + Array.from(deviceIds).join(","));
|
||||||
for (const d of deviceIds) {
|
|
||||||
this.dismissed.add(d);
|
|
||||||
}
|
|
||||||
|
|
||||||
this.recheck();
|
this.otherDevices?.dismissUnverifiedSessions(deviceIds);
|
||||||
}
|
}
|
||||||
|
|
||||||
public dismissEncryptionSetup(): void {
|
public dismissEncryptionSetup(): void {
|
||||||
@@ -295,35 +279,6 @@ export default class DeviceListener extends TypedEventEmitter<DeviceListenerEven
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private async ensureDeviceIdsAtStartPopulated(): Promise<void> {
|
|
||||||
if (this.ourDeviceIdsAtStart === null) {
|
|
||||||
this.ourDeviceIdsAtStart = await this.getDeviceIds();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/** Get the device list for the current user
|
|
||||||
*
|
|
||||||
* @returns the set of device IDs
|
|
||||||
*/
|
|
||||||
private async getDeviceIds(): Promise<Set<string>> {
|
|
||||||
const cli = this.client;
|
|
||||||
if (!cli) return new Set();
|
|
||||||
return await getUserDeviceIds(cli, cli.getSafeUserId());
|
|
||||||
}
|
|
||||||
|
|
||||||
private onDevicesUpdated = async (users: string[], initialFetch?: boolean): Promise<void> => {
|
|
||||||
if (!this.client) return;
|
|
||||||
// If we didn't know about *any* devices before (ie. it's fresh login),
|
|
||||||
// then they are all pre-existing devices, so ignore this and set the
|
|
||||||
// devicesAtStart list to the devices that we see after the fetch.
|
|
||||||
if (initialFetch) return;
|
|
||||||
|
|
||||||
const myUserId = this.client.getSafeUserId();
|
|
||||||
if (users.includes(myUserId)) await this.ensureDeviceIdsAtStartPopulated();
|
|
||||||
|
|
||||||
this.recheck();
|
|
||||||
};
|
|
||||||
|
|
||||||
private onUserTrustStatusChanged = (userId: string): void => {
|
private onUserTrustStatusChanged = (userId: string): void => {
|
||||||
if (!this.client) return;
|
if (!this.client) return;
|
||||||
if (userId !== this.client.getUserId()) return;
|
if (userId !== this.client.getUserId()) return;
|
||||||
@@ -419,7 +374,7 @@ export default class DeviceListener extends TypedEventEmitter<DeviceListenerEven
|
|||||||
return await asyncSomeParallel(cli.getRooms(), ({ roomId }) => cryptoApi.isEncryptionEnabledInRoom(roomId));
|
return await asyncSomeParallel(cli.getRooms(), ({ roomId }) => cryptoApi.isEncryptionEnabledInRoom(roomId));
|
||||||
}
|
}
|
||||||
|
|
||||||
private recheck(): void {
|
public recheck(): void {
|
||||||
this.doRecheck().catch((e) => {
|
this.doRecheck().catch((e) => {
|
||||||
if (e instanceof ClientStoppedError) {
|
if (e instanceof ClientStoppedError) {
|
||||||
// the client was stopped while recheck() was running. Nothing left to do.
|
// the client was stopped while recheck() was running. Nothing left to do.
|
||||||
@@ -546,64 +501,7 @@ export default class DeviceListener extends TypedEventEmitter<DeviceListenerEven
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// This needs to be done after awaiting on getUserDeviceInfo() above, so
|
await this.otherDevices?.recheck(logSpan);
|
||||||
// we make sure we get the devices after the fetch is done.
|
|
||||||
await this.ensureDeviceIdsAtStartPopulated();
|
|
||||||
|
|
||||||
// Unverified devices that were there last time the app ran
|
|
||||||
// (technically could just be a boolean: we don't actually
|
|
||||||
// need to remember the device IDs, but for the sake of
|
|
||||||
// symmetry...).
|
|
||||||
const oldUnverifiedDeviceIds = new Set<string>();
|
|
||||||
// Unverified devices that have appeared since then
|
|
||||||
const newUnverifiedDeviceIds = new Set<string>();
|
|
||||||
|
|
||||||
// as long as cross-signing isn't ready,
|
|
||||||
// you can't see or dismiss any device toasts
|
|
||||||
if (crossSigningReady) {
|
|
||||||
const devices = await this.getDeviceIds();
|
|
||||||
for (const deviceId of devices) {
|
|
||||||
if (deviceId === cli.deviceId) continue;
|
|
||||||
|
|
||||||
const deviceTrust = await crypto.getDeviceVerificationStatus(cli.getSafeUserId(), deviceId);
|
|
||||||
if (!deviceTrust?.crossSigningVerified && !this.dismissed.has(deviceId)) {
|
|
||||||
if (this.ourDeviceIdsAtStart?.has(deviceId)) {
|
|
||||||
oldUnverifiedDeviceIds.add(deviceId);
|
|
||||||
} else {
|
|
||||||
newUnverifiedDeviceIds.add(deviceId);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
logSpan.debug("Old unverified sessions: " + Array.from(oldUnverifiedDeviceIds).join(","));
|
|
||||||
logSpan.debug("New unverified sessions: " + Array.from(newUnverifiedDeviceIds).join(","));
|
|
||||||
logSpan.debug("Currently showing toasts for: " + Array.from(this.displayingToastsForDeviceIds).join(","));
|
|
||||||
|
|
||||||
const isBulkUnverifiedSessionsReminderSnoozed = isBulkUnverifiedDeviceReminderSnoozed();
|
|
||||||
|
|
||||||
// Display or hide the batch toast for old unverified sessions
|
|
||||||
// don't show the toast if the current device is unverified
|
|
||||||
if (oldUnverifiedDeviceIds.size > 0 && isCurrentDeviceTrusted && !isBulkUnverifiedSessionsReminderSnoozed) {
|
|
||||||
showBulkUnverifiedSessionsToast(oldUnverifiedDeviceIds);
|
|
||||||
} else {
|
|
||||||
hideBulkUnverifiedSessionsToast();
|
|
||||||
}
|
|
||||||
|
|
||||||
// Show toasts for new unverified devices if they aren't already there
|
|
||||||
for (const deviceId of newUnverifiedDeviceIds) {
|
|
||||||
showUnverifiedSessionsToast(deviceId);
|
|
||||||
}
|
|
||||||
|
|
||||||
// ...and hide any we don't need any more
|
|
||||||
for (const deviceId of this.displayingToastsForDeviceIds) {
|
|
||||||
if (!newUnverifiedDeviceIds.has(deviceId)) {
|
|
||||||
logSpan.debug("Hiding unverified session toast for " + deviceId);
|
|
||||||
hideUnverifiedSessionsToast(deviceId);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
this.displayingToastsForDeviceIds = newUnverifiedDeviceIds;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
|||||||
@@ -0,0 +1,203 @@
|
|||||||
|
/*
|
||||||
|
Copyright 2025-2026 Element Creations Ltd.
|
||||||
|
Copyright 2024 New Vector Ltd.
|
||||||
|
Copyright 2020 The Matrix.org Foundation C.I.C.
|
||||||
|
|
||||||
|
SPDX-License-Identifier: AGPL-3.0-only OR GPL-3.0-only OR LicenseRef-Element-Commercial
|
||||||
|
Please see LICENSE files in the repository root for full details.
|
||||||
|
*/
|
||||||
|
|
||||||
|
import { CryptoEvent } from "matrix-js-sdk/src/crypto-api";
|
||||||
|
import { type LogSpan } from "matrix-js-sdk/src/logger";
|
||||||
|
import { type MatrixClient } from "matrix-js-sdk/src/matrix";
|
||||||
|
|
||||||
|
import type DeviceListener from "../DeviceListener";
|
||||||
|
import { getUserDeviceIds } from "../utils/crypto/deviceInfo";
|
||||||
|
import { isBulkUnverifiedDeviceReminderSnoozed } from "../utils/device/snoozeBulkUnverifiedDeviceReminder";
|
||||||
|
import {
|
||||||
|
hideToast as hideBulkUnverifiedSessionsToast,
|
||||||
|
showToast as showBulkUnverifiedSessionsToast,
|
||||||
|
} from "../toasts/BulkUnverifiedSessionsToast";
|
||||||
|
import {
|
||||||
|
hideToast as hideUnverifiedSessionToast,
|
||||||
|
showToast as showUnverifiedSessionToast,
|
||||||
|
} from "../toasts/UnverifiedSessionToast";
|
||||||
|
|
||||||
|
export default class DeviceListenerOtherDevices {
|
||||||
|
/**
|
||||||
|
* The DeviceListener launching this instance.
|
||||||
|
*/
|
||||||
|
private deviceListener: DeviceListener;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* The Matrix client in use by the current user.
|
||||||
|
*/
|
||||||
|
private client: MatrixClient;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Device IDs for which the user has dismissed the verify toast ('Later').
|
||||||
|
*/
|
||||||
|
private dismissed = new Set<string>();
|
||||||
|
|
||||||
|
/**
|
||||||
|
* A list of our own device IDs so we can batch ones that were already
|
||||||
|
* there the last time the app launched into a single toast, but display new
|
||||||
|
* ones in their own toasts.
|
||||||
|
*/
|
||||||
|
private ourDeviceIdsAtStart: Set<string> | null = null;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* The set of device IDs we're currently displaying toasts for.
|
||||||
|
*/
|
||||||
|
private displayingToastsForDeviceIds = new Set<string>();
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Start tracking other devices and call `recheck()` on the supplied
|
||||||
|
* DeviceListener when something changes.
|
||||||
|
*/
|
||||||
|
public constructor(deviceListener: DeviceListener, client: MatrixClient) {
|
||||||
|
this.deviceListener = deviceListener;
|
||||||
|
this.client = client;
|
||||||
|
|
||||||
|
this.client.on(CryptoEvent.DevicesUpdated, this.onDevicesUpdated);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Stop tracking other devices and clear our stored information.
|
||||||
|
*/
|
||||||
|
public stop(): void {
|
||||||
|
this.dismissed.clear();
|
||||||
|
this.ourDeviceIdsAtStart = null;
|
||||||
|
this.displayingToastsForDeviceIds = new Set();
|
||||||
|
|
||||||
|
this.client.removeListener(CryptoEvent.DevicesUpdated, this.onDevicesUpdated);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Dismiss notifications about our own unverified devices.
|
||||||
|
*
|
||||||
|
* @param {String[]} deviceIds List of device IDs to dismiss notifications for
|
||||||
|
*/
|
||||||
|
public dismissUnverifiedSessions(deviceIds: Iterable<string>): void {
|
||||||
|
for (const d of deviceIds) {
|
||||||
|
this.dismissed.add(d);
|
||||||
|
}
|
||||||
|
|
||||||
|
// TODO: maybe we don't need a full DeviceListener check? (Maybe we only
|
||||||
|
// need to call this.recheck().)
|
||||||
|
this.deviceListener.recheck();
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Get the device list for the current user.
|
||||||
|
*
|
||||||
|
* @returns the set of device IDs
|
||||||
|
*/
|
||||||
|
private async getDeviceIds(): Promise<Set<string>> {
|
||||||
|
return await getUserDeviceIds(this.client, this.client.getSafeUserId());
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
*/
|
||||||
|
private async ensureDeviceIdsAtStartPopulated(): Promise<void> {
|
||||||
|
if (this.ourDeviceIdsAtStart === null) {
|
||||||
|
this.ourDeviceIdsAtStart = await this.getDeviceIds();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Called when the user's devices are updated. Refreshes the device
|
||||||
|
* information and then rechecks whether we need to display any toasts.
|
||||||
|
*/
|
||||||
|
private onDevicesUpdated = async (users: string[], initialFetch?: boolean): Promise<void> => {
|
||||||
|
// 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 = this.client.getSafeUserId();
|
||||||
|
if (users.includes(myUserId)) await this.ensureDeviceIdsAtStartPopulated();
|
||||||
|
|
||||||
|
// TODO: maybe we don't need a full DeviceListener check? (Maybe we only
|
||||||
|
// need to call this.recheck().)
|
||||||
|
this.deviceListener.recheck();
|
||||||
|
};
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Display a toast if some new other device is unverified, or if we started
|
||||||
|
* up and some unverified devices have appeared.
|
||||||
|
*/
|
||||||
|
public async recheck(logSpan: LogSpan): Promise<void> {
|
||||||
|
const crypto = this.client.getCrypto();
|
||||||
|
if (!crypto) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
const userId = this.client.getSafeUserId();
|
||||||
|
|
||||||
|
const crossSigningReady = await crypto.isCrossSigningReady();
|
||||||
|
|
||||||
|
const isCurrentDeviceTrusted = Boolean(
|
||||||
|
(await crypto.getDeviceVerificationStatus(userId, this.client.deviceId!))?.crossSigningVerified,
|
||||||
|
);
|
||||||
|
|
||||||
|
// This needs to be done after awaiting on getUserDeviceInfo() above, so
|
||||||
|
// we make sure we get the devices after the fetch is done.
|
||||||
|
await this.ensureDeviceIdsAtStartPopulated();
|
||||||
|
|
||||||
|
// Unverified devices that were there last time the app ran
|
||||||
|
// (technically could just be a boolean: we don't actually
|
||||||
|
// need to remember the device IDs, but for the sake of
|
||||||
|
// symmetry...).
|
||||||
|
const oldUnverifiedDeviceIds = new Set<string>();
|
||||||
|
// Unverified devices that have appeared since then
|
||||||
|
const newUnverifiedDeviceIds = new Set<string>();
|
||||||
|
|
||||||
|
// as long as cross-signing isn't ready,
|
||||||
|
// you can't see or dismiss any device toasts
|
||||||
|
if (crossSigningReady) {
|
||||||
|
const devices = await this.getDeviceIds();
|
||||||
|
for (const deviceId of devices) {
|
||||||
|
if (deviceId === this.client.deviceId) continue;
|
||||||
|
|
||||||
|
const deviceTrust = await crypto.getDeviceVerificationStatus(userId, deviceId);
|
||||||
|
if (!deviceTrust?.crossSigningVerified && !this.dismissed.has(deviceId)) {
|
||||||
|
if (this.ourDeviceIdsAtStart?.has(deviceId)) {
|
||||||
|
oldUnverifiedDeviceIds.add(deviceId);
|
||||||
|
} else {
|
||||||
|
newUnverifiedDeviceIds.add(deviceId);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
logSpan.debug("Old unverified sessions: " + Array.from(oldUnverifiedDeviceIds).join(","));
|
||||||
|
logSpan.debug("New unverified sessions: " + Array.from(newUnverifiedDeviceIds).join(","));
|
||||||
|
logSpan.debug("Currently showing toasts for: " + Array.from(this.displayingToastsForDeviceIds).join(","));
|
||||||
|
|
||||||
|
const isBulkUnverifiedSessionsReminderSnoozed = isBulkUnverifiedDeviceReminderSnoozed();
|
||||||
|
|
||||||
|
// Display or hide the batch toast for old unverified sessions
|
||||||
|
// don't show the toast if the current device is unverified
|
||||||
|
if (oldUnverifiedDeviceIds.size > 0 && isCurrentDeviceTrusted && !isBulkUnverifiedSessionsReminderSnoozed) {
|
||||||
|
showBulkUnverifiedSessionsToast(oldUnverifiedDeviceIds);
|
||||||
|
} else {
|
||||||
|
hideBulkUnverifiedSessionsToast();
|
||||||
|
}
|
||||||
|
|
||||||
|
// Show toasts for new unverified devices if they aren't already there
|
||||||
|
for (const deviceId of newUnverifiedDeviceIds) {
|
||||||
|
showUnverifiedSessionToast(deviceId);
|
||||||
|
}
|
||||||
|
|
||||||
|
// ...and hide any we don't need any more
|
||||||
|
for (const deviceId of this.displayingToastsForDeviceIds) {
|
||||||
|
if (!newUnverifiedDeviceIds.has(deviceId)) {
|
||||||
|
logSpan.debug("Hiding unverified session toast for " + deviceId);
|
||||||
|
hideUnverifiedSessionToast(deviceId);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
this.displayingToastsForDeviceIds = newUnverifiedDeviceIds;
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -734,7 +734,7 @@ describe("DeviceListener", () => {
|
|||||||
new Set<string>([device3.deviceId]),
|
new Set<string>([device3.deviceId]),
|
||||||
);
|
);
|
||||||
|
|
||||||
await instance.dismissUnverifiedSessions([device3.deviceId]);
|
await instance.otherDevices?.dismissUnverifiedSessions([device3.deviceId]);
|
||||||
await flushPromises();
|
await flushPromises();
|
||||||
|
|
||||||
expect(BulkUnverifiedSessionsToast.hideToast).toHaveBeenCalled();
|
expect(BulkUnverifiedSessionsToast.hideToast).toHaveBeenCalled();
|
||||||
|
|||||||
Reference in New Issue
Block a user