Emit events from DeviceListenerCurrentDevice instead of delegating to DeviceListener (#32451)

* Emit events from DeviceListenerCurrentDevice instead of delegating to DeviceListener

* Make CurrentDeviceEvents a const enum

* Break out event emitting into its own class so the instance can be persistent
This commit is contained in:
Andy Balaam 2026-02-19 16:43:42 +00:00 committed by GitHub
parent 0b0ac65149
commit 88c7d1ea66
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
5 changed files with 75 additions and 21 deletions

View File

@ -24,7 +24,7 @@ import { RecoveryPanelOutOfSync } from "../../encryption/RecoveryPanelOutOfSync"
import { useTypedEventEmitterState } from "../../../../../hooks/useEventEmitter";
import { KeyStoragePanel } from "../../encryption/KeyStoragePanel";
import { DeleteKeyStoragePanel } from "../../encryption/DeleteKeyStoragePanel";
import { DeviceListener, DeviceListenerEvents, type DeviceState } from "../../../../../device-listener";
import { DeviceListener, CurrentDeviceEvents, type DeviceState } from "../../../../../device-listener";
import { useKeyStoragePanelViewModel } from "../../../../viewmodels/settings/encryption/KeyStoragePanelViewModel";
/**
@ -64,8 +64,8 @@ export function EncryptionUserSettingsTab({ initialState = "main" }: Readonly<Pr
const [state, setState] = useState<State>(initialState);
const deviceState = useTypedEventEmitterState(
DeviceListener.sharedInstance(),
DeviceListenerEvents.DeviceState,
DeviceListener.sharedInstance().currentDeviceChangedEmitter,
CurrentDeviceEvents.DeviceStateChanged,
(state?: DeviceState): DeviceState => {
return state ?? DeviceListener.sharedInstance().getDeviceState();
},

View File

@ -0,0 +1,36 @@
/*
Copyright 2026 Element Creations Ltd.
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 { TypedEventEmitter } from "matrix-js-sdk/src/matrix";
import { type DeviceState } from ".";
/**
* The events emitted when the {@link DeviceState} of the current device
* changes.
*/
export const enum CurrentDeviceEvents {
DeviceStateChanged = "device_state",
}
/**
* You must provide one of these if you listen to {@link CurrentDeviceEvents}
* emitted by {@link DeviceListenerCurrentDevice}. It specifies how to handle
* each type of event.
*/
type EventHandlerMap = {
[CurrentDeviceEvents.DeviceStateChanged]: (state: DeviceState) => void;
};
/**
* Emits events when the current device changes state.
*/
export class CurrentDeviceChangedEmitter extends TypedEventEmitter<CurrentDeviceEvents, EventHandlerMap> {
public onStateChanged(newState: DeviceState): void {
this.emit(CurrentDeviceEvents.DeviceStateChanged, newState);
}
}

View File

@ -7,7 +7,7 @@ SPDX-License-Identifier: AGPL-3.0-only OR GPL-3.0-only OR LicenseRef-Element-Com
Please see LICENSE files in the repository root for full details.
*/
import { type MatrixClient, ClientStoppedError, TypedEventEmitter } from "matrix-js-sdk/src/matrix";
import { type MatrixClient, ClientStoppedError } from "matrix-js-sdk/src/matrix";
import { logger as baseLogger, LogSpan } from "matrix-js-sdk/src/logger";
import { type CryptoSessionStateChange } from "@matrix-org/analytics-events/types/typescript/CryptoSessionStateChange";
import { secureRandomString } from "matrix-js-sdk/src/randomstring";
@ -20,22 +20,16 @@ import SdkConfig from "../SdkConfig";
import PlatformPeg from "../PlatformPeg";
import { recordClientInformation, removeClientInformation } from "../utils/device/clientInformation";
import SettingsStore, { type CallbackFn } from "../settings/SettingsStore";
import { DeviceListenerOtherDevices, DeviceListenerCurrentDevice, type DeviceState } from ".";
import {
DeviceListenerOtherDevices,
DeviceListenerCurrentDevice,
type DeviceState,
CurrentDeviceChangedEmitter,
} from ".";
const logger = baseLogger.getChild("DeviceListener:");
/**
* The events emitted by {@link DeviceListener}
*/
export enum DeviceListenerEvents {
DeviceState = "device_state",
}
type EventHandlerMap = {
[DeviceListenerEvents.DeviceState]: (state: DeviceState) => void;
};
export class DeviceListener extends TypedEventEmitter<DeviceListenerEvents, EventHandlerMap> {
export class DeviceListener {
private dispatcherRef?: string;
/**
@ -44,11 +38,34 @@ export class DeviceListener extends TypedEventEmitter<DeviceListenerEvents, Even
*/
public otherDevices?: DeviceListenerOtherDevices;
/** All the information about whether this device's encrypytion is OK. Only
* set if `running` is true, otherwise undefined.
/**
* All the information about whether this device's encrypytion is OK.
*
* Defined after {@link DeviceListener.start} has been called, before {@link
* DeviceListener.stop} is called.
*/
public currentDevice?: DeviceListenerCurrentDevice;
/**
* Emits `CurrentDeviceEvents` events when the state
* of the current device changes.
*
* @example
* ```ts
* const deviceState = useTypedEventEmitterState(
* DeviceListener.sharedInstance().currentDeviceChangedEmitter,
* CurrentDeviceEvents.DeviceStateChanged,
* (state?: DeviceState): DeviceState => {
* return state ?? DeviceListener.sharedInstance().getDeviceState();
* },
* );
* ```
*
* Now `deviceState` will reflect the state of the current device and
* trigger an update when it changes.
*/
public currentDeviceChangedEmitter = new CurrentDeviceChangedEmitter();
private running = false;
// The client with which the instance is running. Only set if `running` is true, otherwise undefined.
private client?: MatrixClient;

View File

@ -18,7 +18,7 @@ import {
ClientEvent,
} from "matrix-js-sdk/src/matrix";
import { type DeviceListener, type DeviceState, DeviceListenerEvents } from ".";
import { type DeviceListener, type DeviceState } from ".";
import {
hideToast as hideSetupEncryptionToast,
showToast as showSetupEncryptionToast,
@ -263,7 +263,7 @@ export class DeviceListenerCurrentDevice {
private async setDeviceState(newState: DeviceState, logSpan: LogSpan): Promise<void> {
this.deviceState = newState;
this.deviceListener.emit(DeviceListenerEvents.DeviceState, newState);
this.deviceListener.currentDeviceChangedEmitter.onStateChanged(newState);
if (newState === "ok" || this.dismissedThisDeviceToast) {
hideSetupEncryptionToast();

View File

@ -5,6 +5,7 @@ SPDX-License-Identifier: AGPL-3.0-only OR GPL-3.0-only OR LicenseRef-Element-Com
Please see LICENSE files in the repository root for full details.
*/
export * from "./CurrentDeviceChangedEmitter";
export * from "./DeviceListener";
export * from "./DeviceListenerCurrentDevice";
export * from "./DeviceListenerOtherDevices";